Merge remote-tracking branch 'dfhack/develop' into remote_reader

develop
JapaMala 2018-07-31 19:46:31 +05:30
commit 5e95bd9cf2
73 changed files with 1727 additions and 400 deletions

4
.gitignore vendored

@ -56,3 +56,7 @@ tags
#VS is annoying about this one. #VS is annoying about this one.
/build/win64/DF_PATH.txt /build/win64/DF_PATH.txt
/build/win32/DF_PATH.txt /build/win32/DF_PATH.txt
/.vs
# custom plugins
/plugins/CMakeLists.custom.txt

@ -13,6 +13,7 @@ addons:
- libsdl1.2-dev - libsdl1.2-dev
- libxml-libxml-perl - libxml-libxml-perl
- libxml-libxslt-perl - libxml-libxslt-perl
- ninja-build
- zlib1g-dev - zlib1g-dev
matrix: matrix:
include: include:
@ -32,6 +33,7 @@ before_install:
- sh travis/build-lua.sh - sh travis/build-lua.sh
- sh travis/download-df.sh - sh travis/download-df.sh
- echo "export DFHACK_HEADLESS=1" >> "$HOME/.dfhackrc" - echo "export DFHACK_HEADLESS=1" >> "$HOME/.dfhackrc"
- echo "export DFHACK_DISABLE_CONSOLE=1" >> "$HOME/.dfhackrc"
script: script:
- export PATH="$PATH:$HOME/lua53/bin" - export PATH="$PATH:$HOME/lua53/bin"
- git tag tmp-travis-build - git tag tmp-travis-build
@ -45,12 +47,18 @@ script:
- python travis/script-syntax.py --ext=rb --cmd="ruby -c" - python travis/script-syntax.py --ext=rb --cmd="ruby -c"
- mkdir build-travis - mkdir build-travis
- cd build-travis - cd build-travis
- cmake .. -DCMAKE_C_COMPILER=gcc-$GCC_VERSION -DCMAKE_CXX_COMPILER=g++-$GCC_VERSION -DDFHACK_BUILD_ARCH=64 -DBUILD_DOCS:BOOL=ON -DCMAKE_INSTALL_PREFIX="$DF_FOLDER" - cmake .. -G Ninja -DCMAKE_C_COMPILER=gcc-$GCC_VERSION -DCMAKE_CXX_COMPILER=g++-$GCC_VERSION -DDFHACK_BUILD_ARCH=64 -DBUILD_DOCS:BOOL=ON -DCMAKE_INSTALL_PREFIX="$DF_FOLDER"
- make -j3 install - ninja -j3 install
- mv "$DF_FOLDER"/dfhack.init-example "$DF_FOLDER"/dfhack.init - mv "$DF_FOLDER"/dfhack.init-example "$DF_FOLDER"/dfhack.init
- cd .. - cd ..
- cp travis/dfhack_travis.init "$DF_FOLDER"/ - cp travis/dfhack_travis.init "$DF_FOLDER"/
- python travis/run-tests.py "$DF_FOLDER" - python travis/run-tests.py "$DF_FOLDER"
- python travis/check-rpc.py "$DF_FOLDER/dfhack-rpc.txt"
before_cache:
- cat "$DF_FOLDER/stderr.log"
- rm -rf "$DF_FOLDER/hack"
- rm -rf "$DF_FOLDER/dfhack-config"
- rm -f "$DF_FOLDER"/*.log
notifications: notifications:
email: false email: false
irc: irc:

@ -2,12 +2,16 @@
# Set up build types # Set up build types
if(CMAKE_CONFIGURATION_TYPES) if(CMAKE_CONFIGURATION_TYPES)
SET(CMAKE_CONFIGURATION_TYPES Release RelWithDebInfo) SET(CMAKE_CONFIGURATION_TYPES "Release;RelWithDebInfo" CACHE STRING "List of supported configuration types" FORCE)
SET(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" CACHE STRING "List of supported configuration types" FORCE)
else(CMAKE_CONFIGURATION_TYPES) else(CMAKE_CONFIGURATION_TYPES)
set(DFHACK_TYPE_HELP "Choose the type of build, options are: Release and RelWithDebInfo")
# Prevent cmake C module attempts to overwrite our help string
if (NOT CMAKE_BUILD_TYPE) if (NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Release RelWithDebInfo.") SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "${DFHACK_TYPE_HELP}")
else (NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "${DFHACK_TYPE_HELP}")
endif (NOT CMAKE_BUILD_TYPE) endif (NOT CMAKE_BUILD_TYPE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Release;RelWithDebInfo")
endif(CMAKE_CONFIGURATION_TYPES) endif(CMAKE_CONFIGURATION_TYPES)
OPTION(BUILD_DOCS "Choose whether to build the documentation (requires python and Sphinx)." OFF) OPTION(BUILD_DOCS "Choose whether to build the documentation (requires python and Sphinx)." OFF)
@ -44,8 +48,8 @@ if(UNIX)
endif() endif()
if(WIN32) if(WIN32)
if((NOT MSVC) OR (NOT MSVC_VERSION STREQUAL 1900)) if((NOT MSVC) OR (MSVC_VERSION LESS 1900) OR (MSVC_VERSION GREATER 1919))
message(SEND_ERROR "MSVC 2015 is required") message(SEND_ERROR "MSVC 2015 or 2017 is required")
endif() endif()
endif() endif()
@ -83,7 +87,7 @@ IF(MSVC AND NOT DEFINED DFHACK_BUILD_ARCH)
SET(DFHACK_BUILD_ARCH "32") SET(DFHACK_BUILD_ARCH "32")
ENDIF() ENDIF()
ELSE() ELSE()
SET(DFHACK_BUILD_ARCH "32" CACHE STRING "Architecture to build ('32' or '64')") SET(DFHACK_BUILD_ARCH "64" CACHE STRING "Architecture to build ('32' or '64')")
ENDIF() ENDIF()
IF("${DFHACK_BUILD_ARCH}" STREQUAL "32") IF("${DFHACK_BUILD_ARCH}" STREQUAL "32")
@ -164,13 +168,15 @@ endif()
# set up versioning. # set up versioning.
set(DF_VERSION "0.44.12") set(DF_VERSION "0.44.12")
set(DFHACK_RELEASE "alpha0") set(DFHACK_RELEASE "r1")
set(DFHACK_PRERELEASE TRUE) set(DFHACK_PRERELEASE FALSE)
set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}")
set(DFHACK_ABI_VERSION 1) set(DFHACK_ABI_VERSION 1)
set(DFHACK_BUILD_ID "" CACHE STRING "Build ID (should be specified on command line)")
## where to install things (after the build is done, classic 'make install' or package structure) ## where to install things (after the build is done, classic 'make install' or package structure)
# the dfhack libraries will be installed here: # the dfhack libraries will be installed here:
IF(UNIX) IF(UNIX)
@ -400,6 +406,7 @@ file(WRITE "${CMAKE_BINARY_DIR}/dfhack_setarch.txt" ${DFHACK_SETARCH})
install(FILES "${CMAKE_BINARY_DIR}/dfhack_setarch.txt" DESTINATION "${DFHACK_DATA_DESTINATION}") install(FILES "${CMAKE_BINARY_DIR}/dfhack_setarch.txt" DESTINATION "${DFHACK_DATA_DESTINATION}")
install(DIRECTORY dfhack-config/ DESTINATION dfhack-config/default) install(DIRECTORY dfhack-config/ DESTINATION dfhack-config/default)
install(DIRECTORY test DESTINATION "${DFHACK_DATA_DESTINATION}")
#build the plugins #build the plugins
IF(BUILD_PLUGINS) IF(BUILD_PLUGINS)
@ -477,9 +484,54 @@ IF(APPLE)
ELSE() ELSE()
set(DFHACK_PACKAGE_PLATFORM_NAME ${CMAKE_SYSTEM_NAME}) set(DFHACK_PACKAGE_PLATFORM_NAME ${CMAKE_SYSTEM_NAME})
ENDIF() ENDIF()
set(CPACK_PACKAGE_FILE_NAME "dfhack-${DFHACK_VERSION}-${DFHACK_PACKAGE_PLATFORM_NAME}-${DFHACK_BUILD_ARCH}${DFHACK_PACKAGE_SUFFIX}") # set on command line
if(DFHACK_BUILD_ID STREQUAL "")
set(DFHACK_BUILD_ID_PACKAGE "")
else()
set(DFHACK_BUILD_ID_PACKAGE "${DFHACK_BUILD_ID}-")
endif()
set(CPACK_PACKAGE_FILE_NAME "dfhack-${DFHACK_VERSION}-${DFHACK_BUILD_ID_PACKAGE}${DFHACK_PACKAGE_PLATFORM_NAME}-${DFHACK_BUILD_ARCH}${DFHACK_PACKAGE_SUFFIX}")
INCLUDE(CPack) INCLUDE(CPack)
OPTION(DFHACK_INCLUDE_CORE "Download and include Dwarf Fortress core files in DFHack. Useful for local testing, but should not be used in releases." OFF)
IF(DFHACK_INCLUDE_CORE)
STRING(REPLACE "." "_" DF_CORE_FILENAME "${DF_VERSION}")
STRING(REGEX REPLACE "^0_" "df_" DF_CORE_FILENAME "${DF_CORE_FILENAME}")
IF(UNIX)
IF(APPLE)
STRING(APPEND DF_CORE_FILENAME "_osx")
ELSE()
STRING(APPEND DF_CORE_FILENAME "_linux")
ENDIF()
IF(DFHACK_BUILD_32)
STRING(APPEND DF_CORE_FILENAME "32")
ENDIF()
STRING(APPEND DF_CORE_FILENAME ".tar.bz2")
ELSE()
STRING(APPEND DF_CORE_FILENAME "_win")
IF(DFHACK_BUILD_32)
STRING(APPEND DF_CORE_FILENAME "32")
ENDIF()
STRING(APPEND DF_CORE_FILENAME ".zip")
ENDIF()
SET(DF_CORE_URL "http://bay12games.com/dwarves/${DF_CORE_FILENAME}")
IF (NOT EXISTS "${CMAKE_BINARY_DIR}/${DF_CORE_FILENAME}")
FILE(DOWNLOAD "${DF_CORE_URL}" "${CMAKE_BINARY_DIR}/${DF_CORE_FILENAME}" SHOW_PROGRESS)
ENDIF()
FILE(REMOVE_RECURSE "${CMAKE_BINARY_DIR}/df-core")
FILE(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/df-core")
IF(UNIX)
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xjf "../${DF_CORE_FILENAME}" --strip-components=1
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/df-core")
ELSE()
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "../${DF_CORE_FILENAME}" --format=zip
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/df-core")
FILE(REMOVE "${CMAKE_BINARY_DIR}/df-core/SDL.dll")
ENDIF()
INSTALL(DIRECTORY "${CMAKE_BINARY_DIR}/df-core/"
DESTINATION .)
ENDIF()
#INCLUDE(FindSphinx.cmake) #INCLUDE(FindSphinx.cmake)
# Store old build arch # Store old build arch

@ -0,0 +1,350 @@
{
"environments": [
{
"environment": "msvc_2015_x86",
"PATH": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\BIN\\amd64_x86;${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\BIN\\amd64;${env.ProgramFiles(x86)}\\Windows Kits\\10\\bin\\x86;${env.ProgramFiles(x86)}\\Windows Kits\\8.1\\bin\\x86;${env.PATH}",
"VS140COMNTOOLS": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\Common7\\Tools\\",
"VCINSTALLDIR": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\",
"WindowsSdkDir": "${env.ProgramFiles(x86)}\\Windows Kits\\10\\",
"UCRTVersion": "10.0.10240.0",
"UniversalCRTSdkDir": "${env.ProgramFiles(x86)}\\Windows Kits\\10\\",
"LIB": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\LIB;${env.ProgramFiles(x86)}\\Windows Kits\\10\\lib\\10.0.10240.0\\ucrt\\x86;${env.ProgramFiles(x86)}\\Windows Kits\\10\\lib\\um\\x86;${env.ProgramFiles(x86)}\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\x86",
"INCLUDE": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\INCLUDE;${env.ProgramFiles(x86)}\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt;${env.ProgramFiles(x86)}\\Windows Kits\\10\\include\\shared;${env.ProgramFiles(x86)}\\Windows Kits\\10\\include\\um;${env.ProgramFiles(x86)}\\Windows Kits\\10\\include\\winrt;${env.ProgramFiles(x86)}\\Windows Kits\\8.1\\Include\\um;${env.ProgramFiles(x86)}\\Windows Kits\\8.1\\Include\\shared",
"LIBPATH": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\LIB"
},
{
"environment": "msvc_2015_x64",
"PATH": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\BIN\\amd64;${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\BIN;${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\BIN\\1033;${env.ProgramFiles(x86)}\\Windows Kits\\bin\\x64;${env.ProgramFiles(x86)}\\Windows Kits\\8.1\\bin\\x64;${env.PATH}",
"VS140COMNTOOLS": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\Common7\\Tools\\",
"VCINSTALLDIR": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\",
"WindowsSdkDir": "${env.ProgramFiles(x86)}\\Windows Kits\\10\\",
"UCRTVersion": "10.0.10240.0",
"UniversalCRTSdkDir": "${env.ProgramFiles(x86)}\\Windows Kits\\10\\",
"LIB": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\LIB\\amd64;${env.ProgramFiles(x86)}\\Windows Kits\\10\\lib\\10.0.10240.0\\ucrt\\x64;${env.ProgramFiles(x86)}\\Windows Kits\\10\\lib\\um\\x64;${env.ProgramFiles(x86)}\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\x64",
"INCLUDE": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\INCLUDE;${env.ProgramFiles(x86)}\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt;${env.ProgramFiles(x86)}\\Windows Kits\\10\\include\\shared;${env.ProgramFiles(x86)}\\Windows Kits\\10\\include\\um;${env.ProgramFiles(x86)}\\Windows Kits\\10\\include\\winrt;${env.ProgramFiles(x86)}\\Windows Kits\\8.1\\Include\\um;${env.ProgramFiles(x86)}\\Windows Kits\\8.1\\Include\\shared",
"LIBPATH": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\LIB\\amd64"
}
],
"configurations": [
{
"name": "MSVC 32 Debug",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"inheritEnvironments": [ "msvc_x86_x64", "msvc_2015_x86" ],
"variables": [
{
"name": "DFHACK_BUILD_ARCH",
"value": "32"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
},
{
"name": "REMOVE_SYMBOLS_FROM_DF_STUBS",
"value": "0"
},
{
"name": "DFHACK_INCLUDE_CORE",
"value": "1"
}
]
},
{
"name": "MSVC 32 Release",
"generator": "Ninja",
"configurationType": "Release",
"inheritEnvironments": [ "msvc_x86_x64", "msvc_2015_x86" ],
"variables": [
{
"name": "DFHACK_BUILD_ARCH",
"value": "32"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
}
]
},
{
"name": "MSVC 64 Debug",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"inheritEnvironments": [ "msvc_x64_x64", "msvc_2015_x64" ],
"variables": [
{
"name": "DFHACK_BUILD_ARCH",
"value": "64"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
},
{
"name": "REMOVE_SYMBOLS_FROM_DF_STUBS",
"value": "0"
},
{
"name": "DFHACK_INCLUDE_CORE",
"value": "1"
}
]
},
{
"name": "MSVC 64 Release",
"generator": "Ninja",
"configurationType": "Release",
"inheritEnvironments": [ "msvc_x64_x64", "msvc_2015_x64" ],
"variables": [
{
"name": "DFHACK_BUILD_ARCH",
"value": "64"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
}
]
},
{
"name": "GCC 32 Debug",
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/tmp/dfhack-${workspaceHash}/src",
"remoteBuildRoot": "/tmp/dfhack-${workspaceHash}/debug-32",
"rsyncCommandArgs": "--exclude=build --include=.git",
"remoteCopySources": true,
"cmakeExecutable": "$(which cmake)/..",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"intelliSenseMode": "linux-gcc-x86",
"variables": [
{
"name": "DFHACK_BUILD_ARCH",
"value": "32"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
},
{
"name": "REMOVE_SYMBOLS_FROM_DF_STUBS",
"value": "0"
},
{
"name": "DFHACK_INCLUDE_CORE",
"value": "1"
}
]
},
{
"name": "GCC 32 Release",
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/tmp/dfhack-${workspaceHash}/src",
"remoteBuildRoot": "/tmp/dfhack-${workspaceHash}/release-32",
"rsyncCommandArgs": "--exclude=build --include=.git",
"remoteCopySources": true,
"cmakeExecutable": "$(which cmake)/..",
"generator": "Ninja",
"configurationType": "Release",
"intelliSenseMode": "linux-gcc-x86",
"variables": [
{
"name": "DFHACK_BUILD_ARCH",
"value": "32"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
}
]
},
{
"name": "GCC 64 Debug",
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/tmp/dfhack-${workspaceHash}/src",
"remoteBuildRoot": "/tmp/dfhack-${workspaceHash}/debug-64",
"rsyncCommandArgs": "--exclude=build --include=.git",
"remoteCopySources": true,
"cmakeExecutable": "$(which cmake)/..",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"intelliSenseMode": "linux-gcc-x64",
"variables": [
{
"name": "DFHACK_BUILD_ARCH",
"value": "64"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
},
{
"name": "REMOVE_SYMBOLS_FROM_DF_STUBS",
"value": "0"
},
{
"name": "DFHACK_INCLUDE_CORE",
"value": "1"
}
]
},
{
"name": "GCC 64 Release",
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/tmp/dfhack-${workspaceHash}/src",
"remoteBuildRoot": "/tmp/dfhack-${workspaceHash}/release-64",
"rsyncCommandArgs": "--exclude=build --include=.git",
"remoteCopySources": true,
"cmakeExecutable": "$(which cmake)/..",
"generator": "Ninja",
"configurationType": "Release",
"intelliSenseMode": "linux-gcc-x64",
"variables": [
{
"name": "DFHACK_BUILD_ARCH",
"value": "64"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
}
]
},
{
"name": "GCC 4.8 32 Debug",
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/tmp/dfhack-${workspaceHash}/src",
"remoteBuildRoot": "/tmp/dfhack-${workspaceHash}/debug-32-48",
"rsyncCommandArgs": "--exclude=build --include=.git",
"remoteCopySources": true,
"cmakeExecutable": "$(which cmake)/..",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"intelliSenseMode": "linux-gcc-x86",
"variables": [
{
"name": "CMAKE_C_COMPILER",
"value": "gcc-4.8"
},
{
"name": "CMAKE_CXX_COMPILER",
"value": "g++-4.8"
},
{
"name": "DFHACK_BUILD_ARCH",
"value": "32"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
},
{
"name": "REMOVE_SYMBOLS_FROM_DF_STUBS",
"value": "0"
},
{
"name": "DFHACK_INCLUDE_CORE",
"value": "1"
}
]
},
{
"name": "GCC 4.8 32 Release",
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/tmp/dfhack-${workspaceHash}/src",
"remoteBuildRoot": "/tmp/dfhack-${workspaceHash}/release-32-48",
"rsyncCommandArgs": "--exclude=build --include=.git",
"remoteCopySources": true,
"cmakeExecutable": "$(which cmake)/..",
"generator": "Ninja",
"configurationType": "Release",
"intelliSenseMode": "linux-gcc-x86",
"variables": [
{
"name": "CMAKE_C_COMPILER",
"value": "gcc-4.8"
},
{
"name": "CMAKE_CXX_COMPILER",
"value": "g++-4.8"
},
{
"name": "DFHACK_BUILD_ARCH",
"value": "32"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
}
]
},
{
"name": "GCC 4.8 64 Debug",
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/tmp/dfhack-${workspaceHash}/src",
"remoteBuildRoot": "/tmp/dfhack-${workspaceHash}/debug-64-48",
"rsyncCommandArgs": "--exclude=build --include=.git",
"remoteCopySources": true,
"cmakeExecutable": "$(which cmake)/..",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"intelliSenseMode": "linux-gcc-x64",
"variables": [
{
"name": "CMAKE_C_COMPILER",
"value": "gcc-4.8"
},
{
"name": "CMAKE_CXX_COMPILER",
"value": "g++-4.8"
},
{
"name": "DFHACK_BUILD_ARCH",
"value": "64"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
},
{
"name": "REMOVE_SYMBOLS_FROM_DF_STUBS",
"value": "0"
},
{
"name": "DFHACK_INCLUDE_CORE",
"value": "1"
}
]
},
{
"name": "GCC 4.8 64 Release",
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/tmp/dfhack-${workspaceHash}/src",
"remoteBuildRoot": "/tmp/dfhack-${workspaceHash}/release-64-48",
"rsyncCommandArgs": "--exclude=build --include=.git",
"remoteCopySources": true,
"cmakeExecutable": "$(which cmake)/..",
"generator": "Ninja",
"configurationType": "Release",
"intelliSenseMode": "linux-gcc-x64",
"variables": [
{
"name": "CMAKE_C_COMPILER",
"value": "gcc-4.8"
},
{
"name": "CMAKE_CXX_COMPILER",
"value": "g++-4.8"
},
{
"name": "DFHACK_BUILD_ARCH",
"value": "64"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
}
]
}
]
}

@ -6,7 +6,7 @@ Licenses
DFHack is distributed under the Zlib license, with some MIT- DFHack is distributed under the Zlib license, with some MIT-
and BSD-licensed components. These licenses protect your right and BSD-licensed components. These licenses protect your right
to use DFhack for any purpose, distribute copies, and so on. to use DFHack for any purpose, distribute copies, and so on.
The core, plugins, scripts, and other DFHack code all use the The core, plugins, scripts, and other DFHack code all use the
ZLib license unless noted otherwise. By contributing to DFHack, ZLib license unless noted otherwise. By contributing to DFHack,
@ -26,11 +26,11 @@ jsoncpp_ MIT \(c\) 2007-2010, Baptiste Lepilleur
linenoise_ BSD 2-clause \(c\) 2010, Salvatore Sanfilippo & Pieter Noordhuis linenoise_ BSD 2-clause \(c\) 2010, Salvatore Sanfilippo & Pieter Noordhuis
lua_ MIT \(c\) 1994-2008, Lua.org, PUC-Rio. lua_ MIT \(c\) 1994-2008, Lua.org, PUC-Rio.
luafilesystem_ MIT \(c\) 2003-2014, Kepler Project luafilesystem_ MIT \(c\) 2003-2014, Kepler Project
lua-profiler_ MIT \(c\) 2002,2003,2004 Pepperfish
protobuf_ BSD 3-clause \(c\) 2008, Google Inc. protobuf_ BSD 3-clause \(c\) 2008, Google Inc.
tinythread_ Zlib \(c\) 2010, Marcus Geelnard tinythread_ Zlib \(c\) 2010, Marcus Geelnard
tinyxml_ Zlib \(c\) 2000-2006, Lee Thomason tinyxml_ Zlib \(c\) 2000-2006, Lee Thomason
UTF-8-decoder_ MIT \(c\) 2008-2010, Bjoern Hoehrmann UTF-8-decoder_ MIT \(c\) 2008-2010, Bjoern Hoehrmann
lua-profiler_ MIT \(c\) 2002,2003,2004 Pepperfish
=============== ============= ================================================= =============== ============= =================================================
.. _DFHack: https://github.com/DFHack/dfhack .. _DFHack: https://github.com/DFHack/dfhack

@ -11,7 +11,7 @@ endif()
add_subdirectory(tthread) add_subdirectory(tthread)
OPTION(JSONCPP_WITH_TESTS "Compile and (for jsoncpp_check) run JsonCpp test executables" OFF) OPTION(JSONCPP_WITH_TESTS "Compile and (for jsoncpp_check) run JsonCpp test executables" OFF)
OPTION(JSONCPP_WITH_POST_BUILD_UNITTEST "Automatically run unit-tests as a post build step" OFF) OPTION(JSONCPP_WITH_POST_BUILD_UNITTEST "Automatically run unit-tests as a post build step" OFF)
add_subdirectory(jsoncpp-sub) add_subdirectory(jsoncpp-sub EXCLUDE_FROM_ALL)
# build clsocket static and only as a dependency. Setting those options here overrides its own default settings. # build clsocket static and only as a dependency. Setting those options here overrides its own default settings.
OPTION(CLSOCKET_SHARED "Build clsocket lib as shared." OFF) OPTION(CLSOCKET_SHARED "Build clsocket lib as shared." OFF)
OPTION(CLSOCKET_DEP_ONLY "Build for use inside other CMake projects as dependency." ON) OPTION(CLSOCKET_DEP_ONLY "Build for use inside other CMake projects as dependency." ON)

@ -62,6 +62,7 @@ Jonas Ask
Josh Cooper cppcooper coope Josh Cooper cppcooper coope
kane-t kane-t kane-t kane-t
Kelly Kinkade ab9rf Kelly Kinkade ab9rf
KlonZK KlonZK
Kris Parker kaypy Kris Parker kaypy
Kromtec Kromtec Kromtec Kromtec
Kurik Amudnil Kurik Amudnil
@ -125,6 +126,7 @@ Seth Woodworth sethwoodworth
simon simon
Simon Jackson sizeak Simon Jackson sizeak
stolencatkarma stolencatkarma
Stoyan Gaydarov sgayda2
suokko suokko shrieker suokko suokko shrieker
sv-esk sv-esk sv-esk sv-esk
Tacomagic Tacomagic

@ -33,13 +33,16 @@ To get the latest development code (develop branch), clone as above and then::
Generally, you should only need to clone DFHack once. Generally, you should only need to clone DFHack once.
**Important note regarding submodule update and changing branches**: **Important note regarding submodule update after pulling or changing branches**:
You must run ``git submodule update`` every time you change branches, You must run ``git submodule update`` every time you change branches, such as
such as when switching between the master and develop branches or vice versa. when switching between the master and develop branches or vice versa. You also
If a submodule only exists on the newer branch, you also need to run must run it after pulling any changes to submodules from the DFHack repo. If a
``git submodule update --init``. Failure to do this may result in strange submodule only exists on the newer branch, or if a commit you just pulled
build errors or "not a known DF version" errors. contains a new submodule, you need to run ``git submodule update --init``.
Failure to do this may result in a variety of errors, including ``fatal: <path>
does not exist`` when using Git, errors when building DFHack, and ``not a known
DF version`` when starting DF.
**More notes**: **More notes**:
@ -65,6 +68,24 @@ and more, please see `contributing-code`.
Build settings Build settings
============== ==============
Generator
---------
The ``Ninja`` CMake build generator is the prefered build method on Linux and
macOS, instead of ``Unix Makefiles``, which is the default. You can select Ninja
by passing ``-G Ninja`` to CMake. Incremental builds using Unix Makefiles can be
much slower than Ninja builds.
::
cmake .. -G Ninja
.. warning::
Most other CMake settings can be changed by running ``cmake`` again, but the
generator cannot be changed after ``cmake`` has been run without creating a
new build folder. Do not forget to specify this option.
Build type Build type
---------- ----------
@ -72,19 +93,16 @@ Build type
cmake .. -DCMAKE_BUILD_TYPE:string=BUILD_TYPE cmake .. -DCMAKE_BUILD_TYPE:string=BUILD_TYPE
Without specifying a build type or 'None', cmake uses the Valid and useful build types include 'Release' and 'RelWithDebInfo'. The default
``CMAKE_CXX_FLAGS`` variable for building. build type is 'Release'.
Valid and useful build types include 'Release', 'Debug' and
'RelWithDebInfo'.
'Debug' is not available on Windows; use 'RelWithDebInfo' instead.
Target architecture (32-bit vs. 64-bit) Target architecture (32-bit vs. 64-bit)
--------------------------------------- ---------------------------------------
Set DFHACK_BUILD_ARCH to either ``32`` or ``64`` to build a 32-bit or 64-bit Set DFHACK_BUILD_ARCH to either ``32`` or ``64`` to build a 32-bit or 64-bit
version of DFHack (respectively). The default is currently ``32``, but this may version of DFHack (respectively). The default is currently ``64``, so you will
change, so specifying it explicitly is a good idea. need to specify this explicitly for 32-bit builds. Specifying it is a good idea
in any case.
:: ::
@ -138,7 +156,7 @@ Here are some package install commands for various platforms:
* On Ubuntu:: * On Ubuntu::
apt-get install gcc cmake git zlib1g-dev libsdl1.2-dev libxml-libxml-perl libxml-libxslt-perl apt-get install gcc cmake ninja-build git zlib1g-dev libsdl1.2-dev libxml-libxml-perl libxml-libxslt-perl
* Debian and derived distros should have similar requirements to Ubuntu. * Debian and derived distros should have similar requirements to Ubuntu.
@ -171,8 +189,8 @@ Building is fairly straightforward. Enter the ``build`` folder (or create an
empty folder in the DFHack directory to use instead) and start the build like this:: empty folder in the DFHack directory to use instead) and start the build like this::
cd build cd build
cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=<path to DF> cmake .. -G Ninja -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=<path to DF>
make install # or make -jX install on multi-core systems to compile with X parallel processes ninja install # or ninja -jX install to specify the number of cores (X) to use
<path to DF> should be a path to a copy of Dwarf Fortress, of the appropriate <path to DF> should be a path to a copy of Dwarf Fortress, of the appropriate
version for the DFHack you are building. This will build the library along version for the DFHack you are building. This will build the library along
@ -181,8 +199,8 @@ with the normal set of plugins and install them into your DF folder.
Alternatively, you can use ccmake instead of cmake:: Alternatively, you can use ccmake instead of cmake::
cd build cd build
ccmake .. ccmake .. -G Ninja
make install ninja install
This will show a curses-based interface that lets you set all of the This will show a curses-based interface that lets you set all of the
extra options. You can also use a cmake-friendly IDE like KDevelop 4 extra options. You can also use a cmake-friendly IDE like KDevelop 4
@ -215,7 +233,7 @@ compilation-for-distribution with a GCC newer than 4.8.
Mac OS X Mac OS X
======== ========
DFHack functions similarly on OS X and Linux, and the majority of the DFHack functions similarly on OS X and Linux, and the majority of the
information above regarding the build process (cmake and make) applies here information above regarding the build process (cmake and ninja) applies here
as well. as well.
DFHack can officially be built on OS X with GCC 4.8 or 7. Anything newer than 7 DFHack can officially be built on OS X with GCC 4.8 or 7. Anything newer than 7
@ -278,11 +296,12 @@ Dependencies and system set-up
brew tap homebrew/versions brew tap homebrew/versions
brew install git brew install git
brew install cmake brew install cmake
brew install ninja
brew install gcc@7 brew install gcc@7
Using `MacPorts <https://www.macports.org>`_:: Using `MacPorts <https://www.macports.org>`_::
sudo port install gcc7 +universal cmake +universal git-core +universal sudo port install gcc7 +universal cmake +universal git-core +universal ninja +universal
Macports will take some time - maybe hours. At some point it may ask Macports will take some time - maybe hours. At some point it may ask
you to install a Java environment; let it do so. you to install a Java environment; let it do so.
@ -345,8 +364,8 @@ Building
mkdir build-osx mkdir build-osx
cd build-osx cd build-osx
cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=<path to DF> cmake .. -G Ninja -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=<path to DF>
make install # or make -j X install on multi-core systems to compile with X parallel processes ninja install # or ninja -jX install to specify the number of cores (X) to use
<path to DF> should be a path to a copy of Dwarf Fortress, of the appropriate <path to DF> should be a path to a copy of Dwarf Fortress, of the appropriate
version for the DFHack you are building. version for the DFHack you are building.
@ -361,7 +380,7 @@ Dependencies
------------ ------------
You will need the following: You will need the following:
* Microsoft Visual Studio 2015, with the C++ language * Microsoft Visual C++ 2015 or 2017
* Git * Git
* CMake * CMake
* Perl with XML::LibXML and XML::LibXSLT * Perl with XML::LibXML and XML::LibXSLT
@ -372,12 +391,16 @@ You will need the following:
Microsoft Visual Studio 2015 Microsoft Visual Studio 2015
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DFHack has to be compiled with the Microsoft Visual C++ 2015 toolchain; other DFHack has to be compiled with the Microsoft Visual C++ 2015 or 2017 toolchain on Windows;
versions won't work against Dwarf Fortress due to ABI and STL incompatibilities. other versions won't work against Dwarf Fortress due to ABI and STL incompatibilities.
You can install Visual Studio 2015_ or 2017_ Community edition for free, which
include all the features needed by DFHack. You can also download just the
`build tools`_ if you aren't going to use Visual Studio to edit code.
At present, the only way to obtain the MSVC C++ 2015 toolchain is to install a .. _2015: https://visualstudio.microsoft.com/vs/older-downloads/#visual-studio-2015-and-other-products
full copy of Microsoft Visual Studio 2015. The free Community version is .. _2017: https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15
sufficient. .. _build tools: https://visualstudio.microsoft.com/vs/older-downloads/#microsoft-build-tools-2015-update-3
Additional dependencies: installing with the Chocolatey Package Manager Additional dependencies: installing with the Chocolatey Package Manager
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -724,7 +747,7 @@ files alphabetically, so all the files you need should be next to each other.
It is recommended that you create a build folder and run CMake to verify that It is recommended that you create a build folder and run CMake to verify that
you have downloaded everything at this point, assuming your download machine has you have downloaded everything at this point, assuming your download machine has
CMake installed. This involves running a "generate" batch script on Windows, or CMake installed. This involves running a "generate" batch script on Windows, or
a command starting with ``cmake ..`` on Linux and OS X, following the a command starting with ``cmake .. -G Ninja`` on Linux and OS X, following the
instructions in the sections above. CMake should automatically locate files that instructions in the sections above. CMake should automatically locate files that
you placed in ``CMake/downloads``, and use them instead of attempting to you placed in ``CMake/downloads``, and use them instead of attempting to
download them. download them.

@ -822,6 +822,7 @@ can be omitted.
* ``dfhack.getDFHackVersion()`` * ``dfhack.getDFHackVersion()``
* ``dfhack.getDFHackRelease()`` * ``dfhack.getDFHackRelease()``
* ``dfhack.getDFHackBuildID()``
* ``dfhack.getCompiledDFVersion()`` * ``dfhack.getCompiledDFVersion()``
* ``dfhack.getGitDescription()`` * ``dfhack.getGitDescription()``
* ``dfhack.getGitCommit()`` * ``dfhack.getGitCommit()``
@ -1271,6 +1272,18 @@ Units module
Retrieves the profession color for the given race/caste using raws. Retrieves the profession color for the given race/caste using raws.
* ``dfhack.units.getStressCategory(unit)``
Returns a number from 0-6 indicating stress. 0 is most stressed; 6 is least.
Note that 0 is guaranteed to remain the most stressed but 6 could change in the future.
* ``dfhack.units.getStressCategoryRaw(stress_level)``
Identical to ``getStressCategory`` but takes a raw stress level instead of a unit.
* ``dfhack.units.getStressCutoffs()``
Returns a table of the cutoffs used by the above stress level functions.
Items module Items module
------------ ------------

@ -539,7 +539,7 @@ This plugin provides embark site selection help. It has to be run with the
extended (and correct(?)) resource information for the embark rectangle as well extended (and correct(?)) resource information for the embark rectangle as well
as normally undisplayed sites in the current embark region. It also has a site as normally undisplayed sites in the current embark region. It also has a site
selection tool with more options than DF's vanilla search tool. For detailed selection tool with more options than DF's vanilla search tool. For detailed
help invoke the in game info screen. Requires 46 lines to display properly. help invoke the in game info screen.
.. _embark-tools: .. _embark-tools:
@ -1933,6 +1933,10 @@ all 'down ramps' that can remain after a cave-in (you don't have to designate
anything for that to happen). anything for that to happen).
.. _dig: .. _dig:
.. _digv:
.. _digvx:
.. _digl:
.. _diglx:
dig dig
=== ===

@ -38,11 +38,102 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
# Future # Future
## Fixes ## Fixes
- `building-hacks`: fixed error when dealing with custom animation tables
- `embark-assistant`: fixed crash when entering finder with a 16x16 embark selected, and added 16 to dimension choices
- `prospector`: (also affected `embark-tools`) - fixed a crash when prospecting an unusable site (ocean, mountains, etc.) with a large default embark size in d_init.txt (e.g. 16x16)
## Misc Improvements
- `devel/export-dt-ini`: added viewscreen offsets for DT 40.1.2
## Internals
- Linux/macOS: changed recommended build backend from Make to Ninja (Make builds will be significantly slower now)
- Added a usable unit test framework for basic tests, and a few basic tests
- Core: various thread safety and memory management improvements
- Fixed cmake build dependencies for generated header files
- Fixed custom ``CMAKE_CXX_FLAGS`` not being passed to plugins
- Changed ``plugins/CMakeLists.custom.txt`` to be ignored by git and created (if needed) at build time instead
## Lua
- ``utils``: new ``OrderedTable`` class
================================================================================
# 0.44.12-r1
## Fixes
-@ Console: fixed crash when entering long commands on Linux/macOS
-@ Removed jsoncpp's ``include`` and ``lib`` folders from DFHack builds/packages
- Fixed special characters in `command-prompt` and other non-console in-game outputs on Linux/macOS (in tools using ``df2console``)
- `die`: fixed Windows crash in exit handling
- `dwarfmonitor`, `manipulator`: fixed stress cutoffs
- `modtools/force`: fixed a bug where the help text would always be displayed and nothing useful would happen
- `ruby`: fixed calling conventions for vmethods that return strings (currently ``enabler.GetKeyDisplay()``)
- `startdwarf`: fixed on 64-bit Linux
## Misc Improvements
- Reduced time for designation jobs from tools like `digv` to be assigned workers
- `embark-assistant`:
- Switched to standard scrolling keys, improved spacing slightly
- Introduced scrolling of Finder search criteria, removing requirement for 46 lines to work properly (Help/Info still formatted for 46 lines).
- Added Freezing search criterion, allowing searches for NA/Frozen/At_Least_Partial/Partial/At_Most_Partial/Never Freezing embarks.
- `rejuvenate`:
- Added ``-all`` argument to apply to all citizens
- Added ``-force`` to include units under 20 years old
- Clarified documentation
## API
- Added to ``Units`` module:
- ``getStressCategory(unit)``
- ``getStressCategoryRaw(level)``
- ``stress_cutoffs`` (Lua: ``getStressCutoffs()``)
## Internals
- Changed default build architecture to 64-bit
- Added documentation for all RPC functions and a build-time check
- Added support for build IDs to development builds
- Use ``dlsym(3)`` to find vtables from libgraphics.so
## Structures
- Added ``start_dwarf_count`` on 64-bit Linux again and fixed scanning script
- ``army_controller``: added new vector from 0.44.11
-@ ``viewscreen_civlistst``: split ``unk_20`` into 3 pointers
- ``belief_system``: new type, few fields identified
- ``mental_picture``: new type, some fields identified
- ``mission``: new type (used in ``viewscreen_civlistst``)
- ``mission_report``:
- new type (renamed, was ``mission`` before)
- identified some fields
- ``spoils_report``: new type, most fields identified
- ``viewscreen_civlistst``:
- identified new pages
- identified new messenger-related fields
- ``viewscreen_image_creatorst``:
- fixed layout
- identified many fields
- ``viewscreen_reportlistst``:
- added new mission and spoils report-related fields (fixed layout)
- ``world``:
- ``belief_systems``: identified
- ``world.languages``: identified (minimal information; whole languages stored elsewhere)
- ``world.status``:
- ``mission_reports``: renamed, was ``missions``
- ``spoils_reports``: identified
- ``world.unk_131ec0``, ``world.unk_131ef0``: researched layout
- ``world.worldgen_status``: identified many fields
================================================================================
# 0.44.12-alpha1
## Fixes
-@ macOS: fixed ``renderer`` vtable address on x64 (fixes `rendermax`)
- `stonesense`: fixed ``PLANT:DESERT_LIME:LEAF`` typo - `stonesense`: fixed ``PLANT:DESERT_LIME:LEAF`` typo
## API ## API
- Added C++-style linked list interface for DF linked lists - Added C++-style linked list interface for DF linked lists
## Structures
- Dropped 0.44.11 support
- ``ui.squads``: Added fields new in 0.44.12
================================================================================ ================================================================================
# 0.44.11-beta2.1 # 0.44.11-beta2.1

@ -0,0 +1 @@
git-describe.cmake

@ -258,15 +258,22 @@ LIST(APPEND PROJECT_SOURCES ${GENERATED_HDRS})
FILE(GLOB GENERATE_INPUT_SCRIPTS ${dfapi_SOURCE_DIR}/xml/*.pm ${dfapi_SOURCE_DIR}/xml/*.xslt) FILE(GLOB GENERATE_INPUT_SCRIPTS ${dfapi_SOURCE_DIR}/xml/*.pm ${dfapi_SOURCE_DIR}/xml/*.xslt)
FILE(GLOB GENERATE_INPUT_XMLS ${dfapi_SOURCE_DIR}/xml/df.*.xml) FILE(GLOB GENERATE_INPUT_XMLS ${dfapi_SOURCE_DIR}/xml/df.*.xml)
set(CODEGEN_OUT ${dfapi_SOURCE_DIR}/include/df/codegen.out.xml)
LIST(APPEND CODEGEN_OUT ${GENERATED_HDRS})
ADD_CUSTOM_COMMAND( ADD_CUSTOM_COMMAND(
OUTPUT ${dfapi_SOURCE_DIR}/include/df/codegen.out.xml OUTPUT ${CODEGEN_OUT}
COMMAND ${PERL_EXECUTABLE} xml/codegen.pl xml include/df COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/xml/codegen.pl
WORKING_DIRECTORY ${dfapi_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/xml
${CMAKE_CURRENT_SOURCE_DIR}/include/df
MAIN_DEPENDENCY ${dfapi_SOURCE_DIR}/xml/codegen.pl MAIN_DEPENDENCY ${dfapi_SOURCE_DIR}/xml/codegen.pl
COMMENT "Generating codegen.out.xml and df/headers"
DEPENDS ${GENERATE_INPUT_XMLS} ${GENERATE_INPUT_SCRIPTS} DEPENDS ${GENERATE_INPUT_XMLS} ${GENERATE_INPUT_SCRIPTS}
) )
ADD_CUSTOM_TARGET(generate_headers DEPENDS ${dfapi_SOURCE_DIR}/include/df/codegen.out.xml) ADD_CUSTOM_TARGET(generate_headers
DEPENDS ${dfapi_SOURCE_DIR}/include/df/codegen.out.xml)
IF(REMOVE_SYMBOLS_FROM_DF_STUBS) IF(REMOVE_SYMBOLS_FROM_DF_STUBS)
IF(UNIX) IF(UNIX)
# Don't produce debug info for generated stubs # Don't produce debug info for generated stubs
@ -276,6 +283,11 @@ IF(REMOVE_SYMBOLS_FROM_DF_STUBS)
SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES} SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES}
PROPERTIES COMPILE_FLAGS "/O1 /bigobj") PROPERTIES COMPILE_FLAGS "/O1 /bigobj")
ENDIF() ENDIF()
ELSE()
IF(WIN32)
SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES}
PROPERTIES COMPILE_FLAGS "/Od /bigobj")
ENDIF()
ENDIF() ENDIF()
# Compilation # Compilation
@ -297,7 +309,14 @@ ELSE(WIN32)
SET(PROJECT_LIBS psapi dfhack-md5 ${DFHACK_TINYXML} dfhack-tinythread) SET(PROJECT_LIBS psapi dfhack-md5 ${DFHACK_TINYXML} dfhack-tinythread)
ENDIF() ENDIF()
ADD_LIBRARY(dfhack-version STATIC DFHackVersion.cpp) set(VERSION_SRCS DFHackVersion.cpp)
set(VERSION_HDRS include/git-describe.h)
SET_SOURCE_FILES_PROPERTIES(${VERSION_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE)
LIST(APPEND VERSION_SRCS ${VERSION_HDRS})
ADD_LIBRARY(dfhack-version STATIC ${VERSION_SRCS})
SET_PROPERTY(TARGET dfhack-version APPEND PROPERTY COMPILE_DEFINITIONS SET_PROPERTY(TARGET dfhack-version APPEND PROPERTY COMPILE_DEFINITIONS
DFHACK_VERSION="${DFHACK_VERSION}" DFHACK_VERSION="${DFHACK_VERSION}"
DF_VERSION="${DF_VERSION}" DF_VERSION="${DF_VERSION}"
@ -310,17 +329,24 @@ IF(DFHACK_PRERELEASE)
) )
ENDIF() ENDIF()
ADD_CUSTOM_TARGET(git-describe configure_file(git-describe.cmake.in ${CMAKE_CURRENT_SOURCE_DIR}/git-describe.cmake @ONLY)
COMMAND ${CMAKE_COMMAND} if (EXISTS ${CMAKE_SOURCE_DIR}/.git/index AND EXISTS ${CMAKE_SOURCE_DIR}/.git/modules/library/xml/index)
-D dfhack_SOURCE_DIR:STRING=${dfhack_SOURCE_DIR} ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/include/git-describe.h
-D GIT_EXECUTABLE:STRING=${GIT_EXECUTABLE} COMMAND ${CMAKE_COMMAND}
-P ${CMAKE_CURRENT_SOURCE_DIR}/git-describe.cmake -D dfhack_SOURCE_DIR:STRING=${dfhack_SOURCE_DIR}
COMMENT "Obtaining git commit information" -D GIT_EXECUTABLE:STRING=${GIT_EXECUTABLE}
) -P ${CMAKE_CURRENT_SOURCE_DIR}/git-describe.cmake
ADD_DEPENDENCIES(dfhack-version git-describe) COMMENT "Obtaining git commit information"
DEPENDS ${CMAKE_SOURCE_DIR}/.git/index
${CMAKE_SOURCE_DIR}/.git/modules/library/xml/index
${CMAKE_CURRENT_SOURCE_DIR}/git-describe.cmake
include/git-describe.h.in
)
endif ()
ADD_LIBRARY(dfhack SHARED ${PROJECT_SOURCES}) ADD_LIBRARY(dfhack SHARED ${PROJECT_SOURCES})
ADD_DEPENDENCIES(dfhack generate_headers generate_proto_core) ADD_DEPENDENCIES(dfhack generate_proto_core)
ADD_DEPENDENCIES(dfhack generate_headers)
ADD_LIBRARY(dfhack-client SHARED RemoteClient.cpp ColorText.cpp MiscUtils.cpp Error.cpp ${PROJECT_PROTO_SRCS}) ADD_LIBRARY(dfhack-client SHARED RemoteClient.cpp ColorText.cpp MiscUtils.cpp Error.cpp ${PROJECT_PROTO_SRCS})
ADD_DEPENDENCIES(dfhack-client dfhack) ADD_DEPENDENCIES(dfhack-client dfhack)

@ -486,9 +486,10 @@ namespace DFHack
int cooked_cursor = raw_cursor; int cooked_cursor = raw_cursor;
if ((plen+cooked_cursor) >= cols) if ((plen+cooked_cursor) >= cols)
{ {
begin = plen+cooked_cursor-cols-1; const int text_over = plen + cooked_cursor + 1 - cols;
len -= plen+cooked_cursor-cols-1; begin = text_over;
cooked_cursor -= plen+cooked_cursor-cols-1; len -= text_over;
cooked_cursor -= text_over;
} }
if (plen+len > cols) if (plen+len > cols)
len -= plen+len - cols; len -= plen+len - cols;

@ -54,6 +54,7 @@ using namespace std;
#include "modules/Graphic.h" #include "modules/Graphic.h"
#include "modules/Windows.h" #include "modules/Windows.h"
#include "RemoteServer.h" #include "RemoteServer.h"
#include "RemoteTools.h"
#include "LuaTools.h" #include "LuaTools.h"
#include "DFHackVersion.h" #include "DFHackVersion.h"
@ -98,7 +99,8 @@ static bool parseKeySpec(std::string keyspec, int *psym, int *pmod, std::string
size_t loadScriptFiles(Core* core, color_ostream& out, const vector<std::string>& prefix, const std::string& folder); size_t loadScriptFiles(Core* core, color_ostream& out, const vector<std::string>& prefix, const std::string& folder);
namespace DFHack { namespace DFHack {
struct MainThread { class MainThread {
public:
//! MainThread::suspend keeps the main DF thread suspended from Core::Init to //! MainThread::suspend keeps the main DF thread suspended from Core::Init to
//! thread exit. //! thread exit.
static CoreSuspenderBase& suspend() { static CoreSuspenderBase& suspend() {
@ -261,6 +263,8 @@ static string dfhack_version_desc()
else else
s << "(development build " << Version::git_description() << ")"; s << "(development build " << Version::git_description() << ")";
s << " on " << (sizeof(void*) == 8 ? "x86_64" : "x86"); s << " on " << (sizeof(void*) == 8 ? "x86_64" : "x86");
if (strlen(Version::dfhack_build_id()))
s << " [build ID: " << Version::dfhack_build_id() << "]";
return s.str(); return s.str();
} }
@ -655,6 +659,9 @@ string getBuiltinCommand(std::string cmd)
else if (cmd == "clear") else if (cmd == "clear")
builtin = "cls"; builtin = "cls";
else if (cmd == "devel/dump-rpc")
builtin = "devel/dump-rpc";
return builtin; return builtin;
} }
@ -1163,7 +1170,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
} }
else if (builtin == "die") else if (builtin == "die")
{ {
_exit(666); std::_Exit(666);
} }
else if (builtin == "kill-lua") else if (builtin == "kill-lua")
{ {
@ -1301,6 +1308,34 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
} }
} }
else if (builtin == "devel/dump-rpc")
{
if (parts.size() == 1)
{
std::ofstream file(parts[0]);
CoreService core;
core.dumpMethods(file);
for (auto & it : *plug_mgr)
{
Plugin * plug = it.second;
if (!plug)
continue;
std::unique_ptr<RPCService> svc(plug->rpc_connect(con));
if (!svc)
continue;
file << "// Plugin: " << plug->getName() << endl;
svc->dumpMethods(file);
}
}
else
{
con << "Usage: devel/dump-rpc \"filename\"" << endl;
return CR_WRONG_USAGE;
}
}
else if (RunAlias(con, first, parts, res)) else if (RunAlias(con, first, parts, res))
{ {
return res; return res;
@ -1441,6 +1476,10 @@ void fIOthread(void * iodata)
dfhack_version_desc().c_str()); dfhack_version_desc().c_str());
int clueless_counter = 0; int clueless_counter = 0;
if (getenv("DFHACK_DISABLE_CONSOLE"))
return;
while (true) while (true)
{ {
string command = ""; string command = "";
@ -1479,17 +1518,7 @@ void fIOthread(void * iodata)
Core::~Core() Core::~Core()
{ {
if (MainThread::suspend().owns_lock()) // we leak the memory in case ~Core is called after _exit
MainThread::suspend().unlock();
if (d->hotkeythread.joinable()) {
std::lock_guard<std::mutex> lock(HotkeyMutex);
hotkey_set = SHUTDOWN;
HotkeyCond.notify_one();
}
if (d->iothread.joinable())
con.shutdown();
delete d;
} }
Core::Core() : Core::Core() :
@ -1558,8 +1587,6 @@ std::string Core::getHackPath()
#endif #endif
} }
void init_screen_module(Core *);
bool Core::Init() bool Core::Init()
{ {
if(started) if(started)
@ -1682,7 +1709,7 @@ bool Core::Init()
cerr << "Headless mode not supported on Windows" << endl; cerr << "Headless mode not supported on Windows" << endl;
#endif #endif
} }
if ((is_text_mode && !is_headless) || getenv("DFHACK_DISABLE_CONSOLE")) if (is_text_mode && !is_headless)
{ {
con.init(true); con.init(true);
cerr << "Console is not available. Use dfhack-run to send commands.\n"; cerr << "Console is not available. Use dfhack-run to send commands.\n";
@ -1706,7 +1733,6 @@ bool Core::Init()
*/ */
// initialize data defs // initialize data defs
virtual_identity::Init(this); virtual_identity::Init(this);
init_screen_module(this);
// copy over default config files if necessary // copy over default config files if necessary
std::vector<std::string> config_files; std::vector<std::string> config_files;
@ -2299,6 +2325,8 @@ int Core::Shutdown ( void )
} }
allModules.clear(); allModules.clear();
memset(&(s_mods), 0, sizeof(s_mods)); memset(&(s_mods), 0, sizeof(s_mods));
delete d;
d = nullptr;
return -1; return -1;
} }

@ -19,6 +19,10 @@ namespace DFHack {
{ {
return DFHACK_RELEASE; return DFHACK_RELEASE;
} }
const char *dfhack_build_id()
{
return DFHACK_BUILD_ID;
}
const char *git_description() const char *git_description()
{ {
return DFHACK_GIT_DESCRIPTION; return DFHACK_GIT_DESCRIPTION;

@ -26,6 +26,7 @@ distribution.
#include <windows.h> #include <windows.h>
#include <stdint.h> #include <stdint.h>
#include <mutex>
#include <vector> #include <vector>
#include <string> #include <string>
#include "Core.h" #include "Core.h"
@ -40,8 +41,8 @@ distribution.
// we don't know which of the SDL functions will be called first... so we // we don't know which of the SDL functions will be called first... so we
// just catch the first one and init all our function pointers at that time // just catch the first one and init all our function pointers at that time
bool FirstCall(void); static void InitSDLPointers(void);
bool inited = false; static std::once_flag inited;
/// wrappers for SDL 1.2 functions used in 40d16 /// wrappers for SDL 1.2 functions used in 40d16
/***** Condition variables /***** Condition variables
@ -612,8 +613,7 @@ DFhackCExport int SDL_NumJoysticks(void)
static void (*_SDL_GL_SwapBuffers)(void) = 0; static void (*_SDL_GL_SwapBuffers)(void) = 0;
DFhackCExport void SDL_GL_SwapBuffers(void) DFhackCExport void SDL_GL_SwapBuffers(void)
{ {
if(!inited) InitSDLPointers();
FirstCall();
_SDL_GL_SwapBuffers(); _SDL_GL_SwapBuffers();
} }
@ -621,16 +621,14 @@ DFhackCExport void SDL_GL_SwapBuffers(void)
static int (*_SDL_Flip)(void * some_ptr) = 0; static int (*_SDL_Flip)(void * some_ptr) = 0;
DFhackCExport int SDL_Flip(void * some_ptr) DFhackCExport int SDL_Flip(void * some_ptr)
{ {
if(!inited) InitSDLPointers();
FirstCall();
return _SDL_Flip(some_ptr); return _SDL_Flip(some_ptr);
} }
static int (*_SDL_Init)(uint32_t flags) = 0; static int (*_SDL_Init)(uint32_t flags) = 0;
DFhackCExport int SDL_Init(uint32_t flags) DFhackCExport int SDL_Init(uint32_t flags)
{ {
if(!inited) InitSDLPointers();
FirstCall();
return _SDL_Init(flags); return _SDL_Init(flags);
} }
@ -640,16 +638,14 @@ MORE CRAP
static void * (*_SDL_CreateSemaphore)(uint32_t initial_value) = 0; static void * (*_SDL_CreateSemaphore)(uint32_t initial_value) = 0;
DFhackCExport void *SDL_CreateSemaphore(uint32_t initial_value) DFhackCExport void *SDL_CreateSemaphore(uint32_t initial_value)
{ {
if(!inited) InitSDLPointers();
FirstCall();
return _SDL_CreateSemaphore(initial_value); return _SDL_CreateSemaphore(initial_value);
} }
static vPtr (*_SDL_CreateThread)(int (*fn)(void *), void *data) = 0; static vPtr (*_SDL_CreateThread)(int (*fn)(void *), void *data) = 0;
DFhackCExport vPtr SDL_CreateThread(int (*fn)(void *), void *data) DFhackCExport vPtr SDL_CreateThread(int (*fn)(void *), void *data)
{ {
if(!inited) InitSDLPointers();
FirstCall();
return _SDL_CreateThread(fn,data); return _SDL_CreateThread(fn,data);
} }
@ -657,93 +653,81 @@ DFhackCExport vPtr SDL_CreateThread(int (*fn)(void *), void *data)
static void (*_SDL_Delay)(uint32_t ms) = 0; static void (*_SDL_Delay)(uint32_t ms) = 0;
DFhackCExport void SDL_Delay(uint32_t ms) DFhackCExport void SDL_Delay(uint32_t ms)
{ {
if(!inited) InitSDLPointers();
FirstCall();
_SDL_Delay(ms); _SDL_Delay(ms);
} }
static void (*_SDL_DestroySemaphore)(void *sem) = 0; static void (*_SDL_DestroySemaphore)(void *sem) = 0;
DFhackCExport void SDL_DestroySemaphore(void *sem) DFhackCExport void SDL_DestroySemaphore(void *sem)
{ {
if(!inited) InitSDLPointers();
FirstCall();
_SDL_DestroySemaphore(sem); _SDL_DestroySemaphore(sem);
} }
static uint8_t (*_SDL_GetAppState)(void) = 0; static uint8_t (*_SDL_GetAppState)(void) = 0;
DFhackCExport uint8_t SDL_GetAppState(void) DFhackCExport uint8_t SDL_GetAppState(void)
{ {
if(!inited) InitSDLPointers();
FirstCall();
return _SDL_GetAppState(); return _SDL_GetAppState();
} }
static uint8_t (*_SDL_GetMouseState)(int *, int *) = 0; static uint8_t (*_SDL_GetMouseState)(int *, int *) = 0;
DFhackCExport uint8_t SDL_GetMouseState(int *x, int *y) DFhackCExport uint8_t SDL_GetMouseState(int *x, int *y)
{ {
if(!inited) InitSDLPointers();
FirstCall();
return _SDL_GetMouseState(x,y); return _SDL_GetMouseState(x,y);
} }
static int (*_SDL_InitSubSystem)(uint32_t flags) = 0; static int (*_SDL_InitSubSystem)(uint32_t flags) = 0;
DFhackCExport int SDL_InitSubSystem(uint32_t flags) DFhackCExport int SDL_InitSubSystem(uint32_t flags)
{ {
if(!inited) InitSDLPointers();
FirstCall();
return _SDL_InitSubSystem(flags); return _SDL_InitSubSystem(flags);
} }
static int (*_SDL_SemPost)(void *sem) = 0; static int (*_SDL_SemPost)(void *sem) = 0;
DFhackCExport int SDL_SemPost(void *sem) DFhackCExport int SDL_SemPost(void *sem)
{ {
if(!inited) InitSDLPointers();
FirstCall();
return _SDL_SemPost(sem); return _SDL_SemPost(sem);
} }
static int (*_SDL_SemTryWait)(void *sem) = 0; static int (*_SDL_SemTryWait)(void *sem) = 0;
DFhackCExport int SDL_SemTryWait(void *sem) DFhackCExport int SDL_SemTryWait(void *sem)
{ {
if(!inited) InitSDLPointers();
FirstCall();
return _SDL_SemTryWait(sem); return _SDL_SemTryWait(sem);
} }
static int (*_SDL_SemWait)(void *sem) = 0; static int (*_SDL_SemWait)(void *sem) = 0;
DFhackCExport int SDL_SemWait(void *sem) DFhackCExport int SDL_SemWait(void *sem)
{ {
if(!inited) InitSDLPointers();
FirstCall();
return _SDL_SemWait(sem); return _SDL_SemWait(sem);
} }
static uint32_t (*_SDL_ThreadID)(void) = 0; static uint32_t (*_SDL_ThreadID)(void) = 0;
DFhackCExport uint32_t SDL_ThreadID(void) DFhackCExport uint32_t SDL_ThreadID(void)
{ {
if(!inited) InitSDLPointers();
FirstCall();
return _SDL_ThreadID(); return _SDL_ThreadID();
} }
static char* (*_SDL_getenv)(const char *name) = 0; static char* (*_SDL_getenv)(const char *name) = 0;
DFhackCExport char* SDL_getenv(const char *name) DFhackCExport char* SDL_getenv(const char *name)
{ {
if(!inited) InitSDLPointers();
FirstCall();
return _SDL_getenv(name); return _SDL_getenv(name);
} }
static size_t (*_SDL_strlcat)(char *dst, const char *src, size_t maxlen) = 0; static size_t (*_SDL_strlcat)(char *dst, const char *src, size_t maxlen) = 0;
DFhackCExport size_t SDL_strlcat(char *dst, const char *src, size_t maxlen) DFhackCExport size_t SDL_strlcat(char *dst, const char *src, size_t maxlen)
{ {
if(!inited) InitSDLPointers();
FirstCall();
return _SDL_strlcat(dst, src, maxlen); return _SDL_strlcat(dst, src, maxlen);
} }
// FIXME: this has to be thread-safe. void FirstCall()
bool FirstCall()
{ {
// reroute stdout and stderr // reroute stdout and stderr
freopen("stdout.log", "w", stdout); freopen("stdout.log", "w", stdout);
@ -753,7 +737,7 @@ bool FirstCall()
{ {
MessageBox(0,"Can't load SDLreal.dll\n","Error", MB_OK); MessageBox(0,"Can't load SDLreal.dll\n","Error", MB_OK);
fprintf(stderr, "Can't load SDLreal.dll\n"); fprintf(stderr, "Can't load SDLreal.dll\n");
return 0; return;
} }
fprintf(stderr, "FirstCall()\n"); fprintf(stderr, "FirstCall()\n");
// stuff for DF // stuff for DF
@ -836,6 +820,9 @@ bool FirstCall()
_SDL_EnableUNICODE(1); _SDL_EnableUNICODE(1);
fprintf(stderr,"Initized HOOKS!\n"); fprintf(stderr,"Initized HOOKS!\n");
inited = true; }
return 1;
void InitSDLPointers()
{
std::call_once(inited, [](){ FirstCall(); });
} }

@ -1416,7 +1416,7 @@ static bool isMapLoaded() { return Core::getInstance().isMapLoaded(); }
static std::string df2utf(std::string s) { return DF2UTF(s); } static std::string df2utf(std::string s) { return DF2UTF(s); }
static std::string utf2df(std::string s) { return UTF2DF(s); } static std::string utf2df(std::string s) { return UTF2DF(s); }
static std::string df2console(std::string s) { return DF2CONSOLE(s); } static std::string df2console(color_ostream &out, std::string s) { return DF2CONSOLE(out, s); }
#define WRAP_VERSION_FUNC(name, function) WRAPN(name, DFHack::Version::function) #define WRAP_VERSION_FUNC(name, function) WRAPN(name, DFHack::Version::function)
@ -1436,6 +1436,7 @@ static const LuaWrapper::FunctionReg dfhack_module[] = {
WRAP(df2console), WRAP(df2console),
WRAP_VERSION_FUNC(getDFHackVersion, dfhack_version), WRAP_VERSION_FUNC(getDFHackVersion, dfhack_version),
WRAP_VERSION_FUNC(getDFHackRelease, dfhack_release), WRAP_VERSION_FUNC(getDFHackRelease, dfhack_release),
WRAP_VERSION_FUNC(getDFHackBuildID, dfhack_build_id),
WRAP_VERSION_FUNC(getCompiledDFVersion, df_version), WRAP_VERSION_FUNC(getCompiledDFVersion, df_version),
WRAP_VERSION_FUNC(getGitDescription, git_description), WRAP_VERSION_FUNC(getGitDescription, git_description),
WRAP_VERSION_FUNC(getGitCommit, git_commit), WRAP_VERSION_FUNC(getGitCommit, git_commit),
@ -1641,6 +1642,8 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, isDomesticated), WRAPM(Units, isDomesticated),
WRAPM(Units, getMainSocialActivity), WRAPM(Units, getMainSocialActivity),
WRAPM(Units, getMainSocialEvent), WRAPM(Units, getMainSocialEvent),
WRAPM(Units, getStressCategory),
WRAPM(Units, getStressCategoryRaw),
{ NULL, NULL } { NULL, NULL }
}; };
@ -1691,10 +1694,19 @@ static int units_getUnitsInBox(lua_State *state)
return 2; return 2;
} }
static int units_getStressCutoffs(lua_State *L)
{
lua_newtable(L);
for (size_t i = 0; i < Units::stress_cutoffs.size(); i++)
Lua::TableInsert(L, i, Units::stress_cutoffs[i]);
return 1;
}
static const luaL_Reg dfhack_units_funcs[] = { static const luaL_Reg dfhack_units_funcs[] = {
{ "getPosition", units_getPosition }, { "getPosition", units_getPosition },
{ "getNoblePositions", units_getNoblePositions }, { "getNoblePositions", units_getNoblePositions },
{ "getUnitsInBox", units_getUnitsInBox }, { "getUnitsInBox", units_getUnitsInBox },
{ "getStressCutoffs", units_getStressCutoffs },
{ NULL, NULL } { NULL, NULL }
}; };

@ -25,6 +25,7 @@ distribution.
#include "Internal.h" #include "Internal.h"
#include "Export.h" #include "Export.h"
#include "MiscUtils.h" #include "MiscUtils.h"
#include "ColorText.h"
#ifndef LINUX_BUILD #ifndef LINUX_BUILD
// We don't want min and max macros // We don't want min and max macros
@ -394,3 +395,8 @@ DFHACK_EXPORT std::string DF2CONSOLE(const std::string &in)
#endif #endif
return is_utf ? DF2UTF(in) : in; return is_utf ? DF2UTF(in) : in;
} }
DFHACK_EXPORT std::string DF2CONSOLE(DFHack::color_ostream &out, const std::string &in)
{
return out.is_console() ? DF2CONSOLE(in) : in;
}

@ -23,6 +23,7 @@
*/ */
namespace DFHack namespace DFHack
{ {
DFLibrary* GLOBAL_NAMES = (DFLibrary*)RTLD_DEFAULT;
DFLibrary * OpenPlugin (const char * filename) DFLibrary * OpenPlugin (const char * filename)
{ {
dlerror(); dlerror();

@ -41,6 +41,7 @@ distribution.
*/ */
namespace DFHack namespace DFHack
{ {
DFLibrary* GLOBAL_NAMES = (DFLibrary*)GetModuleHandle(nullptr);
DFLibrary * OpenPlugin (const char * filename) DFLibrary * OpenPlugin (const char * filename)
{ {
return (DFLibrary *) LoadLibrary(filename); return (DFLibrary *) LoadLibrary(filename);

@ -236,7 +236,7 @@ void RemoteClient::disconnect()
} }
bool RemoteClient::bind(color_ostream &out, RemoteFunctionBase *function, bool RemoteClient::bind(color_ostream &out, RemoteFunctionBase *function,
const std::string &name, const std::string &proto) const std::string &name, const std::string &plugin)
{ {
if (!active || !socket->IsSocketValid()) if (!active || !socket->IsSocketValid())
return false; return false;
@ -247,8 +247,8 @@ bool RemoteClient::bind(color_ostream &out, RemoteFunctionBase *function,
auto in = bind_call.in(); auto in = bind_call.in();
in->set_method(name); in->set_method(name);
if (!proto.empty()) if (!plugin.empty())
in->set_plugin(proto); in->set_plugin(plugin);
in->set_input_msg(function->p_in_template->GetTypeName()); in->set_input_msg(function->p_in_template->GetTypeName());
in->set_output_msg(function->p_out_template->GetTypeName()); in->set_output_msg(function->p_out_template->GetTypeName());
} }
@ -326,23 +326,23 @@ void RPCFunctionBase::reset(bool free)
} }
bool RemoteFunctionBase::bind(color_ostream &out, RemoteClient *client, bool RemoteFunctionBase::bind(color_ostream &out, RemoteClient *client,
const std::string &name, const std::string &proto) const std::string &name, const std::string &plugin)
{ {
if (isValid()) if (isValid())
{ {
if (p_client == client && this->name == name && this->proto == proto) if (p_client == client && this->name == name && this->plugin == plugin)
return true; return true;
out.printerr("Function already bound to %s::%s\n", out.printerr("Function already bound to %s::%s\n",
this->proto.c_str(), this->name.c_str()); this->plugin.c_str(), this->name.c_str());
return false; return false;
} }
this->name = name; this->name = name;
this->proto = proto; this->plugin = plugin;
this->p_client = client; this->p_client = client;
return client->bind(out, this, name, proto); return client->bind(out, this, name, plugin);
} }
bool sendRemoteMessage(CSimpleSocket *socket, int16_t id, const MessageLite *msg, bool size_ready) bool sendRemoteMessage(CSimpleSocket *socket, int16_t id, const MessageLite *msg, bool size_ready)
@ -371,14 +371,14 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
if (!isValid()) if (!isValid())
{ {
out.printerr("Calling an unbound RPC function %s::%s.\n", out.printerr("Calling an unbound RPC function %s::%s.\n",
this->proto.c_str(), this->name.c_str()); this->plugin.c_str(), this->name.c_str());
return CR_NOT_IMPLEMENTED; return CR_NOT_IMPLEMENTED;
} }
if (!p_client->socket->IsSocketValid()) if (!p_client->socket->IsSocketValid())
{ {
out.printerr("In call to %s::%s: invalid socket.\n", out.printerr("In call to %s::%s: invalid socket.\n",
this->proto.c_str(), this->name.c_str()); this->plugin.c_str(), this->name.c_str());
return CR_LINK_FAILURE; return CR_LINK_FAILURE;
} }
@ -387,14 +387,14 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
if (send_size > RPCMessageHeader::MAX_MESSAGE_SIZE) if (send_size > RPCMessageHeader::MAX_MESSAGE_SIZE)
{ {
out.printerr("In call to %s::%s: message too large: %d.\n", out.printerr("In call to %s::%s: message too large: %d.\n",
this->proto.c_str(), this->name.c_str(), send_size); this->plugin.c_str(), this->name.c_str(), send_size);
return CR_LINK_FAILURE; return CR_LINK_FAILURE;
} }
if (!sendRemoteMessage(p_client->socket, id, input, true)) if (!sendRemoteMessage(p_client->socket, id, input, true))
{ {
out.printerr("In call to %s::%s: I/O error in send.\n", out.printerr("In call to %s::%s: I/O error in send.\n",
this->proto.c_str(), this->name.c_str()); this->plugin.c_str(), this->name.c_str());
return CR_LINK_FAILURE; return CR_LINK_FAILURE;
} }
@ -409,7 +409,7 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
if (!readFullBuffer(p_client->socket, &header, sizeof(header))) if (!readFullBuffer(p_client->socket, &header, sizeof(header)))
{ {
out.printerr("In call to %s::%s: I/O error in receive header.\n", out.printerr("In call to %s::%s: I/O error in receive header.\n",
this->proto.c_str(), this->name.c_str()); this->plugin.c_str(), this->name.c_str());
return CR_LINK_FAILURE; return CR_LINK_FAILURE;
} }
@ -421,7 +421,7 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
if (header.size < 0 || header.size > RPCMessageHeader::MAX_MESSAGE_SIZE) if (header.size < 0 || header.size > RPCMessageHeader::MAX_MESSAGE_SIZE)
{ {
out.printerr("In call to %s::%s: invalid received size %d.\n", out.printerr("In call to %s::%s: invalid received size %d.\n",
this->proto.c_str(), this->name.c_str(), header.size); this->plugin.c_str(), this->name.c_str(), header.size);
return CR_LINK_FAILURE; return CR_LINK_FAILURE;
} }
@ -430,7 +430,7 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
if (!readFullBuffer(p_client->socket, buf, header.size)) if (!readFullBuffer(p_client->socket, buf, header.size))
{ {
out.printerr("In call to %s::%s: I/O error in receive %d bytes of data.\n", out.printerr("In call to %s::%s: I/O error in receive %d bytes of data.\n",
this->proto.c_str(), this->name.c_str(), header.size); this->plugin.c_str(), this->name.c_str(), header.size);
return CR_LINK_FAILURE; return CR_LINK_FAILURE;
} }
@ -439,7 +439,7 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
if (!output->ParseFromArray(buf, header.size)) if (!output->ParseFromArray(buf, header.size))
{ {
out.printerr("In call to %s::%s: error parsing received result.\n", out.printerr("In call to %s::%s: error parsing received result.\n",
this->proto.c_str(), this->name.c_str()); this->plugin.c_str(), this->name.c_str());
delete[] buf; delete[] buf;
return CR_LINK_FAILURE; return CR_LINK_FAILURE;
} }
@ -453,7 +453,7 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
text_decoder.decode(&text_data); text_decoder.decode(&text_data);
else else
out.printerr("In call to %s::%s: received invalid text data.\n", out.printerr("In call to %s::%s: received invalid text data.\n",
this->proto.c_str(), this->name.c_str()); this->plugin.c_str(), this->name.c_str());
break; break;
default: default:

@ -109,6 +109,24 @@ void RPCService::finalize(ServerConnection *owner, std::vector<ServerFunctionBas
} }
} }
void RPCService::dumpMethods(std::ostream & out) const
{
for (auto fn : functions)
{
std::string in_name = fn->p_in_template->GetTypeName();
size_t last_dot = in_name.rfind('.');
if (last_dot != std::string::npos)
in_name = in_name.substr(last_dot + 1);
std::string out_name = fn->p_out_template->GetTypeName();
last_dot = out_name.rfind('.');
if (last_dot != std::string::npos)
out_name = out_name.substr(last_dot + 1);
out << "// RPC " << fn->name << " : " << in_name << " -> " << out_name << endl;
}
}
ServerConnection::ServerConnection(CActiveSocket *socket) ServerConnection::ServerConnection(CActiveSocket *socket)
: socket(socket), stream(this) : socket(socket), stream(this)
{ {

@ -35,6 +35,7 @@ using namespace std;
#include "VersionInfo.h" #include "VersionInfo.h"
#include "Error.h" #include "Error.h"
#include "Memory.h" #include "Memory.h"
#include "PluginManager.h"
using namespace DFHack; using namespace DFHack;
#include <tinyxml.h> #include <tinyxml.h>
@ -133,18 +134,30 @@ void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem)
if(!cstr_key) if(!cstr_key)
throw Error::SymbolsXmlUnderspecifiedEntry(cstr_name); throw Error::SymbolsXmlUnderspecifiedEntry(cstr_name);
const char *cstr_value = pMemEntry->Attribute("value"); const char *cstr_value = pMemEntry->Attribute("value");
if(!cstr_value) const char *cstr_mangled = pMemEntry->Attribute("mangled");
if(!cstr_value && !cstr_mangled)
{ {
cerr << "Dummy symbol table entry: " << cstr_key << endl; cerr << "Dummy symbol table entry: " << cstr_key << endl;
continue; continue;
} }
if ((is_vtable && no_vtables) || (!is_vtable && no_globals)) if ((is_vtable && no_vtables) || (!is_vtable && no_globals))
continue; continue;
#ifdef DFHACK64 uintptr_t addr;
uintptr_t addr = strtoull(cstr_value, 0, 0); if (cstr_value) {
#else if (sizeof(addr) == sizeof(unsigned long))
uintptr_t addr = strtol(cstr_value, 0, 0); addr = strtoul(cstr_value, 0, 0);
#endif else
addr = strtoull(cstr_value, 0, 0);
} else {
addr = (uintptr_t)DFHack::LookupPlugin(DFHack::GLOBAL_NAMES, cstr_mangled);
if (!addr)
continue;
const char *cstr_offset = pMemEntry->Attribute("offset");
if (cstr_offset) {
unsigned long offset = strtoul(cstr_offset, 0, 0);
addr += offset;
}
}
if (is_vtable) if (is_vtable)
mem->setVTable(cstr_key, addr); mem->setVTable(cstr_key, addr);
else else

@ -1,52 +0,0 @@
if(NOT EXISTS ${dfhack_SOURCE_DIR}/.git/index OR NOT EXISTS ${dfhack_SOURCE_DIR}/.git/modules/library/xml/index)
MESSAGE(FATAL_ERROR "Could not find git index file(s)")
endif()
set(git_describe_tmp_h ${dfhack_SOURCE_DIR}/library/include/git-describe.tmp.h)
set(git_describe_h ${dfhack_SOURCE_DIR}/library/include/git-describe.h)
if(EXISTS ${git_describe_tmp_h} AND
NOT(${dfhack_SOURCE_DIR}/.git/index IS_NEWER_THAN ${git_describe_tmp_h}) AND
NOT(${dfhack_SOURCE_DIR}/.git/modules/library/xml/index IS_NEWER_THAN ${git_describe_tmp_h}) AND
NOT(${dfhack_SOURCE_DIR}/library/git-describe.cmake IS_NEWER_THAN ${git_describe_tmp_h}))
return()
endif()
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=8 --long
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
OUTPUT_VARIABLE DFHACK_GIT_DESCRIPTION)
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
OUTPUT_VARIABLE DFHACK_GIT_COMMIT)
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=8 --exact-match
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
RESULT_VARIABLE DFHACK_GIT_TAGGED_RESULT
OUTPUT_QUIET ERROR_QUIET)
# Check library/xml for matching commits
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD:library/xml
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
OUTPUT_VARIABLE DFHACK_GIT_XML_EXPECTED_COMMIT)
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}/library/xml"
OUTPUT_VARIABLE DFHACK_GIT_XML_COMMIT)
file(WRITE ${git_describe_tmp_h} "")
macro(git_describe_definition var)
string(STRIP "${${var}}" GIT_DEF_VALUE)
file(APPEND ${git_describe_tmp_h} "#define ${var} \"${GIT_DEF_VALUE}\"\n")
endmacro()
git_describe_definition(DFHACK_GIT_DESCRIPTION)
git_describe_definition(DFHACK_GIT_COMMIT)
git_describe_definition(DFHACK_GIT_XML_EXPECTED_COMMIT)
git_describe_definition(DFHACK_GIT_XML_COMMIT)
if(${DFHACK_GIT_TAGGED_RESULT} EQUAL 0)
file(APPEND ${git_describe_tmp_h} "#define DFHACK_GIT_TAGGED\n")
endif()
if(${DFHACK_GIT_XML_COMMIT} STREQUAL ${DFHACK_GIT_XML_EXPECTED_COMMIT})
file(APPEND ${git_describe_tmp_h} "#define DFHACK_GIT_XML_MATCH\n")
endif()
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
${git_describe_tmp_h} ${git_describe_h})

@ -0,0 +1,39 @@
# Generated from git-describe.cmake.in
set(DFHACK_BUILD_ID "@DFHACK_BUILD_ID@")
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=8 --long
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
OUTPUT_VARIABLE DFHACK_GIT_DESCRIPTION
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
OUTPUT_VARIABLE DFHACK_GIT_COMMIT
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=8 --exact-match
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
RESULT_VARIABLE DFHACK_GIT_TAGGED_RESULT
OUTPUT_QUIET ERROR_QUIET)
# Check library/xml for matching commits
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD:library/xml
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
OUTPUT_VARIABLE DFHACK_GIT_XML_EXPECTED_COMMIT
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}/library/xml"
OUTPUT_VARIABLE DFHACK_GIT_XML_COMMIT
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(${DFHACK_GIT_TAGGED_RESULT} EQUAL 0)
set(DFHACK_GIT_TAGGED TRUE)
else()
set(DFHACK_GIT_TAGGED FALSE)
endif()
if(${DFHACK_GIT_XML_COMMIT} STREQUAL ${DFHACK_GIT_XML_EXPECTED_COMMIT})
set(DFHACK_GIT_XML_MATCH TRUE)
else()
set(DFHACK_GIT_XML_MATCH FALSE)
endif()
configure_file(${dfhack_SOURCE_DIR}/library/include/git-describe.h.in ${dfhack_SOURCE_DIR}/library/include/git-describe.h)

@ -1 +1 @@
git-describe.* git-describe.h

@ -1,15 +1,18 @@
#pragma once #pragma once
namespace DFHack { namespace DFHack {
namespace Version { namespace Version {
const char *dfhack_version();
const char *df_version(); const char *df_version();
const char *dfhack_version();
const char *dfhack_release(); const char *dfhack_release();
const char *dfhack_build_id();
int dfhack_abi_version(); int dfhack_abi_version();
const char *git_description(); const char *git_description();
const char *git_commit(); const char *git_commit();
const char *git_xml_commit(); const char *git_xml_commit();
const char *git_xml_expected_commit(); const char *git_xml_expected_commit();
bool git_xml_match(); bool git_xml_match();
bool is_release(); bool is_release();
bool is_prerelease(); bool is_prerelease();
} }
@ -17,14 +20,17 @@ namespace DFHack {
#ifndef NO_DFHACK_VERSION_MACROS #ifndef NO_DFHACK_VERSION_MACROS
#define DF_VERSION (DFHack::Version::df_version()) #define DF_VERSION (DFHack::Version::df_version())
#define DFHACK_RELEASE (DFHack::Version::dfhack_release())
#define DFHACK_VERSION (DFHack::Version::dfhack_version()) #define DFHACK_VERSION (DFHack::Version::dfhack_version())
#define DFHACK_RELEASE (DFHack::Version::dfhack_release())
#define DFHACK_BUILD_ID (DFHack::Version::dfhack_build_id())
#define DFHACK_ABI_VERSION (DFHack::Version::dfhack_abi_version()) #define DFHACK_ABI_VERSION (DFHack::Version::dfhack_abi_version())
#define DFHACK_GIT_DESCRIPTION (DFHack::Version::git_description()) #define DFHACK_GIT_DESCRIPTION (DFHack::Version::git_description())
#define DFHACK_GIT_COMMIT (DFHack::Version::git_commit()) #define DFHACK_GIT_COMMIT (DFHack::Version::git_commit())
#define DFHACK_GIT_XML_COMMIT (DFHack::Version::git_xml_commit()) #define DFHACK_GIT_XML_COMMIT (DFHack::Version::git_xml_commit())
#define DFHACK_GIT_XML_EXPECTED_COMMIT (DFHack::Version::git_xml_expected_commit()) #define DFHACK_GIT_XML_EXPECTED_COMMIT (DFHack::Version::git_xml_expected_commit())
#define DFHACK_GIT_XML_MATCH (DFHack::Version::git_xml_match()) #define DFHACK_GIT_XML_MATCH (DFHack::Version::git_xml_match())
#define DFHACK_IS_RELEASE (DFHack::Version::is_release()) #define DFHACK_IS_RELEASE (DFHack::Version::is_release())
#define DFHACK_IS_PRERELEASE (DFHack::Version::is_prerelease()) #define DFHACK_IS_PRERELEASE (DFHack::Version::is_prerelease())
#endif #endif

@ -45,6 +45,10 @@ using std::endl;
#define DFHACK_FUNCTION_SIG __func__ #define DFHACK_FUNCTION_SIG __func__
#endif #endif
namespace DFHack {
class color_ostream;
}
/*! \namespace dts /*! \namespace dts
* std.reverse() == dts, The namespace that include forward compatible helpers * std.reverse() == dts, The namespace that include forward compatible helpers
* which can be used from newer standards. The preprocessor check prefers * which can be used from newer standards. The preprocessor check prefers
@ -384,3 +388,4 @@ DFHACK_EXPORT std::string stl_vsprintf(const char *fmt, va_list args) Wformat(pr
DFHACK_EXPORT std::string UTF2DF(const std::string &in); DFHACK_EXPORT std::string UTF2DF(const std::string &in);
DFHACK_EXPORT std::string DF2UTF(const std::string &in); DFHACK_EXPORT std::string DF2UTF(const std::string &in);
DFHACK_EXPORT std::string DF2CONSOLE(const std::string &in); DFHACK_EXPORT std::string DF2CONSOLE(const std::string &in);
DFHACK_EXPORT std::string DF2CONSOLE(DFHack::color_ostream &out, const std::string &in);

@ -67,6 +67,8 @@ namespace DFHack
// anon type, pretty much // anon type, pretty much
struct DFLibrary; struct DFLibrary;
// DFLibrary* that can be used to resolve global names
extern DFLibrary* GLOBAL_NAMES;
// Open a plugin library // Open a plugin library
DFHACK_EXPORT DFLibrary * OpenPlugin (const char * filename); DFHACK_EXPORT DFLibrary * OpenPlugin (const char * filename);
// find a symbol inside plugin // find a symbol inside plugin

@ -150,10 +150,10 @@ namespace DFHack
class DFHACK_EXPORT RemoteFunctionBase : public RPCFunctionBase { class DFHACK_EXPORT RemoteFunctionBase : public RPCFunctionBase {
public: public:
bool bind(RemoteClient *client, const std::string &name, bool bind(RemoteClient *client, const std::string &name,
const std::string &proto = std::string()); const std::string &plugin = std::string());
bool bind(color_ostream &out, bool bind(color_ostream &out,
RemoteClient *client, const std::string &name, RemoteClient *client, const std::string &name,
const std::string &proto = std::string()); const std::string &plugin = std::string());
bool isValid() { return (id >= 0); } bool isValid() { return (id >= 0); }
@ -167,7 +167,7 @@ namespace DFHack
inline color_ostream &default_ostream(); inline color_ostream &default_ostream();
command_result execute(color_ostream &out, const message_type *input, message_type *output); command_result execute(color_ostream &out, const message_type *input, message_type *output);
std::string name, proto; std::string name, plugin;
RemoteClient *p_client; RemoteClient *p_client;
int16_t id; int16_t id;
}; };
@ -227,7 +227,7 @@ namespace DFHack
friend class RemoteFunctionBase; friend class RemoteFunctionBase;
bool bind(color_ostream &out, RemoteFunctionBase *function, bool bind(color_ostream &out, RemoteFunctionBase *function,
const std::string &name, const std::string &proto); const std::string &name, const std::string &plugin);
public: public:
RemoteClient(color_ostream *default_output = NULL); RemoteClient(color_ostream *default_output = NULL);
@ -269,8 +269,8 @@ namespace DFHack
} }
inline bool RemoteFunctionBase::bind(RemoteClient *client, const std::string &name, inline bool RemoteFunctionBase::bind(RemoteClient *client, const std::string &name,
const std::string &proto) { const std::string &plugin) {
return bind(client->default_output(), client, name, proto); return bind(client->default_output(), client, name, plugin);
} }
class RemoteSuspender { class RemoteSuspender {

@ -151,6 +151,7 @@ namespace DFHack
class DFHACK_EXPORT RPCService { class DFHACK_EXPORT RPCService {
friend class ServerConnection; friend class ServerConnection;
friend class Plugin; friend class Plugin;
friend class Core;
std::vector<ServerFunctionBase*> functions; std::vector<ServerFunctionBase*> functions;
std::map<std::string, ServerFunctionBase*> lookup; std::map<std::string, ServerFunctionBase*> lookup;
@ -208,6 +209,8 @@ namespace DFHack
assert(!owner); assert(!owner);
functions.push_back(new VoidServerMethod<Svc,In>(this, name, flags, fptr)); functions.push_back(new VoidServerMethod<Svc,In>(this, name, flags, fptr));
} }
void dumpMethods(std::ostream & out) const;
}; };
class ServerConnection { class ServerConnection {

@ -0,0 +1,7 @@
#cmakedefine DFHACK_GIT_DESCRIPTION "@DFHACK_GIT_DESCRIPTION@"
#cmakedefine DFHACK_GIT_COMMIT "@DFHACK_GIT_COMMIT@"
#cmakedefine DFHACK_GIT_XML_EXPECTED_COMMIT "@DFHACK_GIT_XML_EXPECTED_COMMIT@"
#cmakedefine DFHACK_GIT_XML_COMMIT "@DFHACK_GIT_XML_COMMIT@"
#define DFHACK_BUILD_ID "@DFHACK_BUILD_ID@"
#cmakedefine DFHACK_GIT_TAGGED
#cmakedefine DFHACK_GIT_XML_MATCH

@ -55,7 +55,7 @@ class Block;
struct BiomeInfo { struct BiomeInfo {
// Determined by the 4-bit index in the designation bitfield // Determined by the 4-bit index in the designation bitfield
static const unsigned MAX_LAYERS = 16; static constexpr unsigned MAX_LAYERS = 16;
df::coord2d pos; df::coord2d pos;
int default_soil, default_stone, lava_stone; int default_soil, default_stone, lava_stone;

@ -347,6 +347,8 @@ namespace DFHack
virtual df::job *getSelectedJob() { return nullptr; } virtual df::job *getSelectedJob() { return nullptr; }
virtual df::building *getSelectedBuilding() { return nullptr; } virtual df::building *getSelectedBuilding() { return nullptr; }
virtual df::plant *getSelectedPlant() { return nullptr; } virtual df::plant *getSelectedPlant() { return nullptr; }
static virtual_identity _identity;
}; };
class DFHACK_EXPORT dfhack_lua_viewscreen : public dfhack_viewscreen { class DFHACK_EXPORT dfhack_lua_viewscreen : public dfhack_viewscreen {
@ -387,6 +389,8 @@ namespace DFHack
virtual df::job *getSelectedJob(); virtual df::job *getSelectedJob();
virtual df::building *getSelectedBuilding(); virtual df::building *getSelectedBuilding();
virtual df::plant *getSelectedPlant(); virtual df::plant *getSelectedPlant();
static virtual_identity _identity;
}; };
} }

@ -184,5 +184,10 @@ DFHACK_EXPORT std::string getSquadName(df::unit *unit);
DFHACK_EXPORT df::activity_entry *getMainSocialActivity(df::unit *unit); DFHACK_EXPORT df::activity_entry *getMainSocialActivity(df::unit *unit);
DFHACK_EXPORT df::activity_event *getMainSocialEvent(df::unit *unit); DFHACK_EXPORT df::activity_event *getMainSocialEvent(df::unit *unit);
// stress categories - 0 is highest stress
DFHACK_EXPORT extern const std::vector<int32_t> stress_cutoffs;
DFHACK_EXPORT int getStressCategory(df::unit *unit);
DFHACK_EXPORT int getStressCategoryRaw(int32_t stress_level);
} }
} }

@ -682,4 +682,40 @@ function addressof(obj)
return select(2, obj:sizeof()) return select(2, obj:sizeof())
end end
function OrderedTable()
-- store values in a separate table to ensure that __index and __newindex
-- run on every table index operation
local t = {}
local key_to_index = {}
local index_to_key = {}
local mt = {}
function mt:__index(k)
return t[k]
end
function mt:__newindex(k, v)
if not key_to_index[k] then
table.insert(index_to_key, k)
key_to_index[k] = #index_to_key
end
t[k] = v
end
function mt:__pairs()
return function(_, k)
if k then
k = index_to_key[key_to_index[k] + 1]
else
k = index_to_key[1]
end
if k then
return k, t[k]
end
end, nil, nil
end
local self = {}
setmetatable(self, mt)
return self
end
return _ENV return _ENV

@ -79,10 +79,6 @@ using df::global::world;
extern bool GetLocalFeature(t_feature &feature, df::coord2d rgn_pos, int32_t index); extern bool GetLocalFeature(t_feature &feature, df::coord2d rgn_pos, int32_t index);
#ifdef LINUX_BUILD
const unsigned MapExtras::BiomeInfo::MAX_LAYERS;
#endif
const BiomeInfo MapCache::biome_stub = { const BiomeInfo MapCache::biome_stub = {
df::coord2d(), df::coord2d(),
-1, -1, -1, -1, -1, -1, -1, -1,
@ -742,6 +738,7 @@ bool MapExtras::Block::Write ()
{ {
COPY(block->designation, designation); COPY(block->designation, designation);
block->flags.bits.designated = true; block->flags.bits.designated = true;
block->dsgn_check_cooldown = 0;
dirty_designations = false; dirty_designations = false;
} }
if(dirty_tiles || dirty_veins) if(dirty_tiles || dirty_veins)

@ -412,58 +412,6 @@ void Hide::merge(df::viewscreen* a)
} }
} } } }
#ifdef _LINUX
class DFHACK_EXPORT renderer {
unsigned char *screen;
long *screentexpos;
char *screentexpos_addcolor;
unsigned char *screentexpos_grayscale;
unsigned char *screentexpos_cf;
unsigned char *screentexpos_cbr;
// For partial printing:
unsigned char *screen_old;
long *screentexpos_old;
char *screentexpos_addcolor_old;
unsigned char *screentexpos_grayscale_old;
unsigned char *screentexpos_cf_old;
unsigned char *screentexpos_cbr_old;
public:
virtual void update_tile(int x, int y) {};
virtual void update_all() {};
virtual void render() {};
virtual void set_fullscreen();
virtual void zoom(df::zoom_commands cmd);
virtual void resize(int w, int h) {};
virtual void grid_resize(int w, int h) {};
renderer() {
screen = NULL;
screentexpos = NULL;
screentexpos_addcolor = NULL;
screentexpos_grayscale = NULL;
screentexpos_cf = NULL;
screentexpos_cbr = NULL;
screen_old = NULL;
screentexpos_old = NULL;
screentexpos_addcolor_old = NULL;
screentexpos_grayscale_old = NULL;
screentexpos_cf_old = NULL;
screentexpos_cbr_old = NULL;
}
virtual ~renderer();
virtual bool get_mouse_coords(int &x, int &y) { return false; }
virtual bool uses_opengl();
};
#endif
void init_screen_module(Core *core)
{
#ifdef _LINUX
renderer tmp;
if (!strict_virtual_cast<df::renderer>((virtual_ptr)&tmp))
cerr << "Could not fetch the renderer vtable." << std::endl;
#endif
}
string Screen::getKeyDisplay(df::interface_key key) string Screen::getKeyDisplay(df::interface_key key)
{ {
if (enabler) if (enabler)
@ -970,3 +918,31 @@ df::plant *dfhack_lua_viewscreen::getSelectedPlant()
safe_call_lua(do_notify, 1, 1); safe_call_lua(do_notify, 1, 1);
return Lua::GetDFObject<df::plant>(Lua::Core::State, -1); return Lua::GetDFObject<df::plant>(Lua::Core::State, -1);
} }
#define STATIC_FIELDS_GROUP
#include "../DataStaticsFields.cpp"
using df::identity_traits;
#define CUR_STRUCT dfhack_viewscreen
static const struct_field_info dfhack_viewscreen_fields[] = {
{ METHOD(OBJ_METHOD, is_lua_screen), 0, 0 },
{ METHOD(OBJ_METHOD, getFocusString), 0, 0 },
{ METHOD(OBJ_METHOD, onShow), 0, 0 },
{ METHOD(OBJ_METHOD, onDismiss), 0, 0 },
{ METHOD(OBJ_METHOD, getSelectedUnit), 0, 0 },
{ METHOD(OBJ_METHOD, getSelectedItem), 0, 0 },
{ METHOD(OBJ_METHOD, getSelectedJob), 0, 0 },
{ METHOD(OBJ_METHOD, getSelectedBuilding), 0, 0 },
{ METHOD(OBJ_METHOD, getSelectedPlant), 0, 0 },
{ FLD_END }
};
#undef CUR_STRUCT
virtual_identity dfhack_viewscreen::_identity(sizeof(dfhack_viewscreen), nullptr, "dfhack_viewscreen", nullptr, &df::viewscreen::_identity, dfhack_viewscreen_fields);
#define CUR_STRUCT dfhack_lua_viewscreen
static const struct_field_info dfhack_lua_viewscreen_fields[] = {
{ FLD_END }
};
#undef CUR_STRUCT
virtual_identity dfhack_lua_viewscreen::_identity(sizeof(dfhack_lua_viewscreen), nullptr, "dfhack_lua_viewscreen", nullptr, &dfhack_viewscreen::_identity, dfhack_lua_viewscreen_fields);

@ -1626,3 +1626,30 @@ bool Units::isDomesticated(df::unit* unit)
} }
return tame; return tame;
} }
// 50000 and up is level 0, 25000 and up is level 1, etc.
const vector<int32_t> Units::stress_cutoffs {50000, 25000, 10000, -10000, -25000, -50000, -100000};
int Units::getStressCategory(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
if (!unit->status.current_soul)
return int(stress_cutoffs.size()) / 2;
return getStressCategoryRaw(unit->status.current_soul->personality.stress_level);
}
int Units::getStressCategoryRaw(int32_t stress_level)
{
int max_level = int(stress_cutoffs.size()) - 1;
int level = max_level;
for (int i = max_level; i >= 0; i--)
{
if (stress_level >= stress_cutoffs[i])
{
level = i;
}
}
return level;
}

@ -1 +1 @@
Subproject commit 372993c58851153320f3390119cdd2d97caef9db Subproject commit 57928311390bb67fe0cd5deec14e2d4621b13a58

@ -1,3 +0,0 @@
# You can add custom plugins here to avoid touching plugins/CMakeLists.txt,
# This can be useful if you've made modifications to that file and try to
# switch between branches that have also made modifications to it.

@ -178,4 +178,12 @@ if(BUILD_SKELETON)
add_subdirectory(skeleton) add_subdirectory(skeleton)
endif() endif()
if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.custom.txt")
FILE(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.custom.txt" "# You can add custom plugins here to avoid touching plugins/CMakeLists.txt,
# This can be useful if you've made modifications to that file and try to
# switch between branches that have also made modifications to it.
")
endif()
INCLUDE(CMakeLists.custom.txt) INCLUDE(CMakeLists.custom.txt)

@ -1,15 +1,4 @@
IF(UNIX) IF(UNIX)
add_definitions(-DLINUX_BUILD)
SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wall")
SET(CMAKE_CXX_FLAGS "-fvisibility=hidden")
SET(CMAKE_C_FLAGS "-fvisibility=hidden")
IF(DFHACK_BUILD_64)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m64 -mno-avx")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m64 -mno-avx")
ELSE()
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32")
ENDIF()
IF(NOT APPLE) IF(NOT APPLE)
# Linux: Check for unresolved symbols at link time # Linux: Check for unresolved symbols at link time
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,-z,defs") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,-z,defs")

@ -101,23 +101,10 @@ static color_value monitor_colors[] =
static int get_happiness_cat(df::unit *unit) static int get_happiness_cat(df::unit *unit)
{ {
if (!unit || !unit->status.current_soul) int level = Units::getStressCategory(unit);
return 3; if (level < 0) level = 0;
int stress = unit->status.current_soul->personality.stress_level; if (level > 6) level = 6;
if (stress >= 500000) return level;
return 0;
else if (stress >= 250000)
return 1;
else if (stress >= 100000)
return 2;
else if (stress >= 60000)
return 3;
else if (stress >= 30000)
return 4;
else if (stress >= 0)
return 5;
else
return 6;
} }
static int get_max_history() static int get_max_history()

@ -58,6 +58,8 @@ namespace embark_assist {
int16_t biome_index[10]; // Indexed through biome_offset; -1 = null, Index of region, [0] not used int16_t biome_index[10]; // Indexed through biome_offset; -1 = null, Index of region, [0] not used
int16_t biome[10]; // Indexed through biome_offset; -1 = null, df::biome_type, [0] not used int16_t biome[10]; // Indexed through biome_offset; -1 = null, df::biome_type, [0] not used
uint8_t biome_count; uint8_t biome_count;
int16_t min_temperature[10]; // Indexed through biome_offset; -30000 = null, Urists - 10000, [0] not used
int16_t max_temperature[10]; // Indexed through biome_offset; -30000 = null, Urists - 10000, [0] not used
bool blood_rain[10]; bool blood_rain[10];
bool blood_rain_possible; bool blood_rain_possible;
bool blood_rain_full; bool blood_rain_full;
@ -229,16 +231,14 @@ namespace embark_assist {
None None
}; };
/* // Future possible enhancement
enum class freezing_ranges : int8_t { enum class freezing_ranges : int8_t {
NA = -1, NA = -1,
Permanent, Permanent,
At_Least_Partial, At_Least_Partial,
Partial, Partial,
At_Most_Partial, At_Most_Partial,
Never Never
}; };
*/
struct finders { struct finders {
uint16_t x_dim; uint16_t x_dim;
@ -256,7 +256,7 @@ namespace embark_assist {
soil_ranges soil_min; soil_ranges soil_min;
all_present_ranges soil_min_everywhere; all_present_ranges soil_min_everywhere;
soil_ranges soil_max; soil_ranges soil_max;
/*freezing_ranges freezing;*/ freezing_ranges freezing;
yes_no_ranges blood_rain; // Will probably blow up with the magic release arcs... yes_no_ranges blood_rain; // Will probably blow up with the magic release arcs...
syndrome_rain_ranges syndrome_rain; syndrome_rain_ranges syndrome_rain;
reanimation_ranges reanimation; reanimation_ranges reanimation;

@ -12,6 +12,7 @@
#include "df/material_flags.h" #include "df/material_flags.h"
#include "df/viewscreen_choose_start_sitest.h" #include "df/viewscreen_choose_start_sitest.h"
#include "df/world.h" #include "df/world.h"
#include "df/world_data.h"
#include "df/world_region_type.h" #include "df/world_region_type.h"
#include "df/world_raws.h" #include "df/world_raws.h"
@ -46,6 +47,7 @@ namespace embark_assist {
soil_min, soil_min,
soil_min_everywhere, soil_min_everywhere,
soil_max, soil_max,
freezing,
blood_rain, blood_rain,
syndrome_rain, syndrome_rain,
reanimation, reanimation,
@ -314,14 +316,14 @@ namespace embark_assist {
switch (i) { switch (i) {
case fields::x_dim: case fields::x_dim:
for (int16_t k = 1; k < 16; k++) { for (int16_t k = 1; k <= 16; k++) {
element->list.push_back({ std::to_string(k), k }); element->list.push_back({ std::to_string(k), k });
} }
break; break;
case fields::y_dim: case fields::y_dim:
for (int16_t k = 1; k < 16; k++) { for (int16_t k = 1; k <= 16; k++) {
element->list.push_back({ std::to_string(k), k }); element->list.push_back({ std::to_string(k), k });
} }
@ -574,6 +576,52 @@ namespace embark_assist {
break; break;
case fields::freezing:
{
embark_assist::defs::freezing_ranges k = embark_assist::defs::freezing_ranges::NA;
while (true) {
switch (k) {
case embark_assist::defs::freezing_ranges::NA:
element->list.push_back({ "N/A", static_cast<int8_t>(k) });
break;
case embark_assist::defs::freezing_ranges::Permanent:
element->list.push_back({ "Permanent", static_cast<int8_t>(k) });
break;
case embark_assist::defs::freezing_ranges::At_Least_Partial:
element->list.push_back({ "At Least Partially Frozen", static_cast<int8_t>(k) });
break;
case embark_assist::defs::freezing_ranges::Partial:
element->list.push_back({ "Partially Frozen", static_cast<int8_t>(k) });
break;
case embark_assist::defs::freezing_ranges::At_Most_Partial:
element->list.push_back({ "At Most Partially Frozen", static_cast<int8_t>(k) });
break;
case embark_assist::defs::freezing_ranges::Never:
element->list.push_back({ "Never Frozen", static_cast<int8_t>(k) });
break;
}
if (k == embark_assist::defs::freezing_ranges::Never ||
(world->world_data->world_height != 17 && // Can't handle temperature in non standard height worlds.
world->world_data->world_height != 33 &&
world->world_data->world_height != 65 &&
world->world_data->world_height != 129 &&
world->world_data->world_height != 257)) {
break;
}
k = static_cast <embark_assist::defs::freezing_ranges>(static_cast<int8_t>(k) + 1);
}
}
break;
case fields::syndrome_rain: case fields::syndrome_rain:
{ {
embark_assist::defs::syndrome_rain_ranges k = embark_assist::defs::syndrome_rain_ranges::NA; embark_assist::defs::syndrome_rain_ranges k = embark_assist::defs::syndrome_rain_ranges::NA;
@ -917,6 +965,10 @@ namespace embark_assist {
state->finder_list.push_back({ "Min Soil Everywhere", static_cast<int8_t>(i) }); state->finder_list.push_back({ "Min Soil Everywhere", static_cast<int8_t>(i) });
break; break;
case fields::freezing:
state->finder_list.push_back({ "Freezing", static_cast<int8_t>(i) });
break;
case fields::blood_rain: case fields::blood_rain:
state->finder_list.push_back({ "Blood Rain", static_cast<int8_t>(i) }); state->finder_list.push_back({ "Blood Rain", static_cast<int8_t>(i) });
break; break;
@ -1141,6 +1193,11 @@ namespace embark_assist {
static_cast<embark_assist::defs::all_present_ranges>(state->ui[static_cast<uint8_t>(i)]->current_value); static_cast<embark_assist::defs::all_present_ranges>(state->ui[static_cast<uint8_t>(i)]->current_value);
break; break;
case fields::freezing:
finder.freezing =
static_cast<embark_assist::defs::freezing_ranges>(state->ui[static_cast<uint8_t>(i)]->current_value);
break;
case fields::blood_rain: case fields::blood_rain:
finder.blood_rain = finder.blood_rain =
static_cast<embark_assist::defs::yes_no_ranges>(state->ui[static_cast<uint8_t>(i)]->current_value); static_cast<embark_assist::defs::yes_no_ranges>(state->ui[static_cast<uint8_t>(i)]->current_value);
@ -1303,11 +1360,11 @@ namespace embark_assist {
Screen::dismiss(this); Screen::dismiss(this);
return; return;
} else if (input->count(df::interface_key::CURSOR_LEFT) || } else if (input->count(df::interface_key::STANDARDSCROLL_LEFT) ||
input->count(df::interface_key::CURSOR_RIGHT)) { input->count(df::interface_key::STANDARDSCROLL_RIGHT)) {
state->finder_list_active = !state->finder_list_active; state->finder_list_active = !state->finder_list_active;
} else if (input->count(df::interface_key::CURSOR_UP)) { } else if (input->count(df::interface_key::STANDARDSCROLL_UP)) {
if (state->finder_list_active) { if (state->finder_list_active) {
if (state->finder_list_focus > 0) { if (state->finder_list_focus > 0) {
state->finder_list_focus--; state->finder_list_focus--;
@ -1324,7 +1381,7 @@ namespace embark_assist {
} }
} }
} else if (input->count(df::interface_key::CURSOR_DOWN)) { } else if (input->count(df::interface_key::STANDARDSCROLL_DOWN)) {
if (state->finder_list_active) { if (state->finder_list_active) {
if (state->finder_list_focus < static_cast<uint16_t>(last_fields)) { if (state->finder_list_focus < static_cast<uint16_t>(last_fields)) {
state->finder_list_focus++; state->finder_list_focus++;
@ -1366,59 +1423,76 @@ namespace embark_assist {
void ViewscreenFindUi::render() { void ViewscreenFindUi::render() {
// color_ostream_proxy out(Core::getInstance().getConsole()); // color_ostream_proxy out(Core::getInstance().getConsole());
auto screen_size = DFHack::Screen::getWindowSize(); auto screen_size = DFHack::Screen::getWindowSize();
const int list_column = 53; uint16_t top_row = 2;
uint16_t list_column = 53;
uint16_t offset = 0; uint16_t offset = 0;
Screen::clear(); Screen::clear();
Screen::drawBorder("Embark Assistant Site Finder"); Screen::drawBorder(" Embark Assistant Site Finder ");
embark_assist::screen::paintString(lr_pen, 1, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_LEFT).c_str()); embark_assist::screen::paintString(lr_pen, 1, 1, DFHack::Screen::getKeyDisplay(df::interface_key::STANDARDSCROLL_LEFT).c_str());
embark_assist::screen::paintString(white_pen, 2, 1, "/"); embark_assist::screen::paintString(white_pen, 2, 1, "/");
embark_assist::screen::paintString(lr_pen, 3, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_RIGHT).c_str()); embark_assist::screen::paintString(lr_pen, 3, 1, DFHack::Screen::getKeyDisplay(df::interface_key::STANDARDSCROLL_RIGHT).c_str());
embark_assist::screen::paintString(white_pen, 4, 1, ":\x1b/\x1a"); embark_assist::screen::paintString(white_pen, 4, 1, ": \x1b/\x1a");
embark_assist::screen::paintString(lr_pen, 9, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_UP).c_str()); embark_assist::screen::paintString(lr_pen, 10, 1, DFHack::Screen::getKeyDisplay(df::interface_key::STANDARDSCROLL_UP).c_str());
embark_assist::screen::paintString(white_pen, 10, 1, "/"); embark_assist::screen::paintString(white_pen, 11, 1, "/");
embark_assist::screen::paintString(lr_pen, 11, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_DOWN).c_str()); embark_assist::screen::paintString(lr_pen, 12, 1, DFHack::Screen::getKeyDisplay(df::interface_key::STANDARDSCROLL_DOWN).c_str());
embark_assist::screen::paintString(white_pen, 12, 1, ":Up/Down"); embark_assist::screen::paintString(white_pen, 13, 1, ": \x18/\x19");
embark_assist::screen::paintString(lr_pen, 21, 1, DFHack::Screen::getKeyDisplay(df::interface_key::SELECT).c_str()); embark_assist::screen::paintString(lr_pen, 19, 1, DFHack::Screen::getKeyDisplay(df::interface_key::SELECT).c_str());
embark_assist::screen::paintString(white_pen, 26, 1, ":Select"); embark_assist::screen::paintString(white_pen, 24, 1, ": Select");
embark_assist::screen::paintString(lr_pen, 34, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_F).c_str()); embark_assist::screen::paintString(lr_pen, 33, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_F).c_str());
embark_assist::screen::paintString(white_pen, 35, 1, ":Find"); embark_assist::screen::paintString(white_pen, 34, 1, ": Find");
embark_assist::screen::paintString(lr_pen, 41, 1, DFHack::Screen::getKeyDisplay(df::interface_key::LEAVESCREEN).c_str()); embark_assist::screen::paintString(lr_pen, 41, 1, DFHack::Screen::getKeyDisplay(df::interface_key::LEAVESCREEN).c_str());
embark_assist::screen::paintString(white_pen, 44, 1, ":Abort"); embark_assist::screen::paintString(white_pen, 44, 1, ": Abort");
embark_assist::screen::paintString(lr_pen, 51, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_S).c_str()); embark_assist::screen::paintString(lr_pen, 52, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_S).c_str());
embark_assist::screen::paintString(white_pen, 52, 1, ":Save"); embark_assist::screen::paintString(white_pen, 53, 1, ": Save");
embark_assist::screen::paintString(lr_pen, 58, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_L).c_str()); embark_assist::screen::paintString(lr_pen, 60, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_L).c_str());
embark_assist::screen::paintString(white_pen, 59, 1, ":Load"); embark_assist::screen::paintString(white_pen, 61, 1, ": Load");
// Implement scrolling lists if they don't fit on the screen.
if (int32_t(state->finder_list.size()) > screen_size.y - top_row - 1) {
offset = (screen_size.y - top_row - 1) / 2;
if (state->finder_list_focus < offset) {
offset = 0;
}
else {
offset = state->finder_list_focus - offset;
}
for (uint16_t i = 0; i < state->finder_list.size(); i++) { if (int32_t(state->finder_list.size() - offset) < screen_size.y - top_row - 1) {
offset = static_cast<uint16_t>(state->finder_list.size()) - (screen_size.y - top_row - 1);
}
}
for (uint16_t i = offset; i < state->finder_list.size(); i++) {
if (i == state->finder_list_focus) { if (i == state->finder_list_focus) {
if (state->finder_list_active) { if (state->finder_list_active) {
embark_assist::screen::paintString(active_pen, 1, 2 + i, state->finder_list[i].text); embark_assist::screen::paintString(active_pen, 1, top_row + i - offset, state->finder_list[i].text);
} }
else { else {
embark_assist::screen::paintString(passive_pen, 1, 2 + i, state->finder_list[i].text); embark_assist::screen::paintString(passive_pen, 1, top_row + i - offset, state->finder_list[i].text);
} }
embark_assist::screen::paintString(active_pen, embark_assist::screen::paintString(active_pen,
21, 21,
2 + i, top_row + i - offset,
state->ui[i]->list[state->ui[i]->current_display_value].text); state->ui[i]->list[state->ui[i]->current_display_value].text);
} }
else { else {
embark_assist::screen::paintString(normal_pen, 1, 2 + i, state->finder_list[i].text); embark_assist::screen::paintString(normal_pen, 1, top_row + i - offset, state->finder_list[i].text);
embark_assist::screen::paintString(white_pen, embark_assist::screen::paintString(white_pen,
21, 21,
2 + i, top_row + i - offset,
state->ui[i]->list[state->ui[i]->current_display_value].text); state->ui[i]->list[state->ui[i]->current_display_value].text);
} }
} }
// Implement scrolling lists if they don't fit on the screen. // Implement scrolling lists if they don't fit on the screen.
if (int32_t(state->ui[state->finder_list_focus]->list.size()) > screen_size.y - 3) { offset = 0;
offset = (screen_size.y - 3) / 2;
if (int32_t(state->ui[state->finder_list_focus]->list.size()) > screen_size.y - top_row - 1) {
offset = (screen_size.y - top_row - 1) / 2;
if (state->ui[state->finder_list_focus]->current_index < offset) { if (state->ui[state->finder_list_focus]->current_index < offset) {
offset = 0; offset = 0;
} }
@ -1426,22 +1500,22 @@ namespace embark_assist {
offset = state->ui[state->finder_list_focus]->current_index - offset; offset = state->ui[state->finder_list_focus]->current_index - offset;
} }
if (int32_t(state->ui[state->finder_list_focus]->list.size() - offset) < screen_size.y - 3) { if (int32_t(state->ui[state->finder_list_focus]->list.size() - offset) < screen_size.y - top_row - 1) {
offset = static_cast<uint16_t>(state->ui[state->finder_list_focus]->list.size()) - (screen_size.y - 3); offset = static_cast<uint16_t>(state->ui[state->finder_list_focus]->list.size()) - (screen_size.y - top_row - 1);
} }
} }
for (uint16_t i = 0; i < state->ui[state->finder_list_focus]->list.size(); i++) { for (uint16_t i = offset; i < state->ui[state->finder_list_focus]->list.size(); i++) {
if (i == state->ui[state->finder_list_focus]->current_index) { if (i == state->ui[state->finder_list_focus]->current_index) {
if (!state->finder_list_active) { // Negated expression to get the display lines in the same order as above. if (!state->finder_list_active) { // Negated expression to get the display lines in the same order as above.
embark_assist::screen::paintString(active_pen, list_column, 2 + i - offset, state->ui[state->finder_list_focus]->list[i].text); embark_assist::screen::paintString(active_pen, list_column, top_row + i - offset, state->ui[state->finder_list_focus]->list[i].text);
} }
else { else {
embark_assist::screen::paintString(passive_pen, list_column, 2 + i - offset, state->ui[state->finder_list_focus]->list[i].text); embark_assist::screen::paintString(passive_pen, list_column, top_row + i - offset, state->ui[state->finder_list_focus]->list[i].text);
} }
} }
else { else {
embark_assist::screen::paintString(normal_pen, list_column, 2 + i - offset, state->ui[state->finder_list_focus]->list[i].text); embark_assist::screen::paintString(normal_pen, list_column, top_row + i - offset, state->ui[state->finder_list_focus]->list[i].text);
} }
} }

@ -99,7 +99,7 @@ namespace embark_assist{
switch (current_page) { switch (current_page) {
case pages::Intro: case pages::Intro:
Screen::drawBorder("Embark Assistant Help/Info Introduction Page"); Screen::drawBorder(" Embark Assistant Help/Info Introduction Page ");
help_text.push_back("Embark Assistant is used on the embark selection screen to provide information"); help_text.push_back("Embark Assistant is used on the embark selection screen to provide information");
help_text.push_back("to help selecting a suitable embark site. It provides three services:"); help_text.push_back("to help selecting a suitable embark site. It provides three services:");
@ -108,10 +108,10 @@ namespace embark_assist{
help_text.push_back("- Site find search. Richer set of selection criteria than the vanilla"); help_text.push_back("- Site find search. Richer set of selection criteria than the vanilla");
help_text.push_back(" DF Find that Embark Assistant suppresses (by using the same key)."); help_text.push_back(" DF Find that Embark Assistant suppresses (by using the same key).");
help_text.push_back(""); help_text.push_back("");
help_text.push_back("The functionality requires a screen height of at least 46 lines to display"); help_text.push_back("The functionality requires a screen height larger than the default 80*25,");
help_text.push_back("correctly (that's the height of the Finder screen), as fitting everything"); help_text.push_back("and while the Finder screen provides for scrolling, the embark resources");
help_text.push_back("onto a standard 80*25 screen would be too challenging. The help is adjusted"); help_text.push_back("list will spill over the bottom if many resources are present and the");
help_text.push_back("to fit into onto an 80*46 screen as well."); help_text.push_back("screen isn't deep enough. This help info is adapted to fit onto 80*46.");
help_text.push_back("This help/info is split over several screens, and you can move between them"); help_text.push_back("This help/info is split over several screens, and you can move between them");
help_text.push_back("using the TAB/Shift-TAB keys, and leave the help from any screen using ESC."); help_text.push_back("using the TAB/Shift-TAB keys, and leave the help from any screen using ESC.");
help_text.push_back(""); help_text.push_back("");
@ -139,7 +139,7 @@ namespace embark_assist{
break; break;
case pages::General: case pages::General:
Screen::drawBorder("Embark Assistant Help/Info General Page"); Screen::drawBorder(" Embark Assistant Help/Info General Page ");
help_text.push_back("The Embark Assistant overlays the region map with characters indicating sites"); help_text.push_back("The Embark Assistant overlays the region map with characters indicating sites");
help_text.push_back("normally not displayed by DF. The following key is used:"); help_text.push_back("normally not displayed by DF. The following key is used:");
@ -178,7 +178,7 @@ namespace embark_assist{
break; break;
case pages::Finder: case pages::Finder:
Screen::drawBorder("Embark Assistant Help/Info Find Page"); Screen::drawBorder(" Embark Assistant Help/Info Find Page ");
help_text.push_back("The Embark Assist Finder page is brought up with the f command key."); help_text.push_back("The Embark Assist Finder page is brought up with the f command key.");
help_text.push_back("The top of the Finder page lists the command keys available on the page:"); help_text.push_back("The top of the Finder page lists the command keys available on the page:");
@ -209,6 +209,9 @@ namespace embark_assist{
help_text.push_back("Min/Max soil uses the same terminology as DF for 1-4. The Min Soil"); help_text.push_back("Min/Max soil uses the same terminology as DF for 1-4. The Min Soil");
help_text.push_back("Everywhere toggles the Min Soil parameter between acting as All and"); help_text.push_back("Everywhere toggles the Min Soil parameter between acting as All and");
help_text.push_back("and Present."); help_text.push_back("and Present.");
help_text.push_back("Freezing allows you to select embarks to select/avoid various freezing");
help_text.push_back("conditions. Note that the minimum temperature is held for only 10 ticks");
help_text.push_back("in many embarks.");
help_text.push_back("Syndrome Rain allows you to search for Permanent and Temporary syndromes,"); help_text.push_back("Syndrome Rain allows you to search for Permanent and Temporary syndromes,");
help_text.push_back("where Permanent allows for Temporary ones as well, but not the reverse, as"); help_text.push_back("where Permanent allows for Temporary ones as well, but not the reverse, as");
help_text.push_back("Not Permanent matches everything except Permanent syndromes."); help_text.push_back("Not Permanent matches everything except Permanent syndromes.");
@ -222,7 +225,7 @@ namespace embark_assist{
break; break;
case pages::Caveats: case pages::Caveats:
Screen::drawBorder("Embark Assistant Help/Info Caveats Page"); Screen::drawBorder(" Embark Assistant Help/Info Caveats Page ");
help_text.push_back("Find searching first does a sanity check (e.g. max < min) and then a rough"); help_text.push_back("Find searching first does a sanity check (e.g. max < min) and then a rough");
help_text.push_back("world tile match to find tiles that may have matching embarks. This results"); help_text.push_back("world tile match to find tiles that may have matching embarks. This results");
@ -261,7 +264,7 @@ namespace embark_assist{
help_text.push_back(" reaching caverns that have been removed at world gen to fail to be"); help_text.push_back(" reaching caverns that have been removed at world gen to fail to be");
help_text.push_back(" generated at all. It's likely this bug also affects magma pools."); help_text.push_back(" generated at all. It's likely this bug also affects magma pools.");
help_text.push_back(" This plugin does not address this but scripts can correct it."); help_text.push_back(" This plugin does not address this but scripts can correct it.");
help_text.push_back("Version 0.4 2018-06-21"); help_text.push_back("Version 0.5 2018-07-13");
break; break;
} }
@ -270,9 +273,9 @@ namespace embark_assist{
embark_assist::screen::paintString(pen_lr, 1, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CHANGETAB).c_str()); embark_assist::screen::paintString(pen_lr, 1, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CHANGETAB).c_str());
embark_assist::screen::paintString(pen, 4, 1, "/"); embark_assist::screen::paintString(pen, 4, 1, "/");
embark_assist::screen::paintString(pen_lr, 5, 1, DFHack::Screen::getKeyDisplay(df::interface_key::SEC_CHANGETAB).c_str()); embark_assist::screen::paintString(pen_lr, 5, 1, DFHack::Screen::getKeyDisplay(df::interface_key::SEC_CHANGETAB).c_str());
embark_assist::screen::paintString(pen, 14, 1, ":Next/Previous Page"); embark_assist::screen::paintString(pen, 14, 1, ": Next/Previous Page");
embark_assist::screen::paintString(pen_lr, 34, 1, DFHack::Screen::getKeyDisplay(df::interface_key::LEAVESCREEN).c_str()); embark_assist::screen::paintString(pen_lr, 35, 1, DFHack::Screen::getKeyDisplay(df::interface_key::LEAVESCREEN).c_str());
embark_assist::screen::paintString(pen, 37, 1, ":Leave Info/Help"); embark_assist::screen::paintString(pen, 38, 1, ": Leave Info/Help");
for (uint16_t i = 0; i < help_text.size(); i++) { for (uint16_t i = 0; i < help_text.size(); i++) {
embark_assist::screen::paintString(pen, 1, 2 + i, help_text[i]); embark_assist::screen::paintString(pen, 1, 2 + i, help_text[i]);
@ -298,10 +301,10 @@ namespace embark_assist{
break; break;
case pages::Finder: case pages::Finder:
embark_assist::screen::paintString(pen_lr, 1, 4, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_LEFT).c_str()); embark_assist::screen::paintString(pen_lr, 1, 4, DFHack::Screen::getKeyDisplay(df::interface_key::STANDARDSCROLL_LEFT).c_str());
embark_assist::screen::paintString(pen_lr, 3, 4, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_RIGHT).c_str()); embark_assist::screen::paintString(pen_lr, 3, 4, DFHack::Screen::getKeyDisplay(df::interface_key::STANDARDSCROLL_RIGHT).c_str());
embark_assist::screen::paintString(pen_lr, 1, 5, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_UP).c_str()); embark_assist::screen::paintString(pen_lr, 1, 5, DFHack::Screen::getKeyDisplay(df::interface_key::STANDARDSCROLL_UP).c_str());
embark_assist::screen::paintString(pen_lr, 3, 5, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_DOWN).c_str()); embark_assist::screen::paintString(pen_lr, 3, 5, DFHack::Screen::getKeyDisplay(df::interface_key::STANDARDSCROLL_DOWN).c_str());
embark_assist::screen::paintString(pen_lr, 1, 6, DFHack::Screen::getKeyDisplay(df::interface_key::SELECT).c_str()); embark_assist::screen::paintString(pen_lr, 1, 6, DFHack::Screen::getKeyDisplay(df::interface_key::SELECT).c_str());
embark_assist::screen::paintString(pen_lr, 1, 7, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_F).c_str()); embark_assist::screen::paintString(pen_lr, 1, 7, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_F).c_str());
embark_assist::screen::paintString(pen_lr, 1, 8, DFHack::Screen::getKeyDisplay(df::interface_key::LEAVESCREEN).c_str()); embark_assist::screen::paintString(pen_lr, 1, 8, DFHack::Screen::getKeyDisplay(df::interface_key::LEAVESCREEN).c_str());

@ -48,6 +48,8 @@ namespace embark_assist {
bool flux_found = false; bool flux_found = false;
uint8_t max_soil = 0; uint8_t max_soil = 0;
bool uneven = false; bool uneven = false;
int16_t min_temperature = survey_results->at(x).at(y).min_temperature[mlt->at(start_x).at(start_y).biome_offset];
int16_t max_temperature = survey_results->at(x).at(y).max_temperature[mlt->at(start_x).at(start_y).biome_offset];
bool blood_rain_found = false; bool blood_rain_found = false;
bool permanent_syndrome_rain_found = false; bool permanent_syndrome_rain_found = false;
bool temporary_syndrome_rain_found = false; bool temporary_syndrome_rain_found = false;
@ -185,6 +187,21 @@ namespace embark_assist {
if (finder->soil_max != embark_assist::defs::soil_ranges::NA && if (finder->soil_max != embark_assist::defs::soil_ranges::NA &&
mlt->at(i).at(k).soil_depth > static_cast<uint16_t>(finder->soil_max)) return false; mlt->at(i).at(k).soil_depth > static_cast<uint16_t>(finder->soil_max)) return false;
// Freezing
if (min_temperature > survey_results->at(x).at(y).min_temperature[mlt->at(i).at(k).biome_offset]) {
min_temperature = survey_results->at(x).at(y).min_temperature[mlt->at(i).at(k).biome_offset];
}
if (max_temperature < survey_results->at(x).at(y).max_temperature[mlt->at(i).at(k).biome_offset]) {
max_temperature = survey_results->at(x).at(y).max_temperature[mlt->at(i).at(k).biome_offset];
}
if (min_temperature <= 0 &&
finder->freezing == embark_assist::defs::freezing_ranges::Never) return false;
if (max_temperature > 0 &&
finder->freezing == embark_assist::defs::freezing_ranges::Permanent) return false;
// Blood Rain // Blood Rain
if (survey_results->at(x).at(y).blood_rain[mlt->at(i).at(k).biome_offset]) { if (survey_results->at(x).at(y).blood_rain[mlt->at(i).at(k).biome_offset]) {
if (finder->blood_rain == embark_assist::defs::yes_no_ranges::No) return false; if (finder->blood_rain == embark_assist::defs::yes_no_ranges::No) return false;
@ -323,6 +340,17 @@ namespace embark_assist {
finder->soil_min_everywhere == embark_assist::defs::all_present_ranges::Present && finder->soil_min_everywhere == embark_assist::defs::all_present_ranges::Present &&
max_soil < static_cast<uint8_t>(finder->soil_min)) return false; max_soil < static_cast<uint8_t>(finder->soil_min)) return false;
// Freezing
if (finder->freezing == embark_assist::defs::freezing_ranges::At_Least_Partial &&
min_temperature > 0) return false;
if (finder->freezing == embark_assist::defs::freezing_ranges::Partial &&
(min_temperature > 0 ||
max_temperature <= 0)) return false;
if (finder->freezing == embark_assist::defs::freezing_ranges::At_Most_Partial &&
max_temperature <= 0) return false;
// Blood Rain // Blood Rain
if (finder->blood_rain == embark_assist::defs::yes_no_ranges::Yes && !blood_rain_found) return false; if (finder->blood_rain == embark_assist::defs::yes_no_ranges::Yes && !blood_rain_found) return false;
@ -415,7 +443,7 @@ namespace embark_assist {
uint16_t y, uint16_t y,
embark_assist::defs::finders *finder) { embark_assist::defs::finders *finder) {
// color_ostream_proxy out(Core::getInstance().getConsole()); color_ostream_proxy out(Core::getInstance().getConsole());
df::world_data *world_data = world->world_data; df::world_data *world_data = world->world_data;
embark_assist::defs::region_tile_datum *tile = &survey_results->at(x).at(y); embark_assist::defs::region_tile_datum *tile = &survey_results->at(x).at(y);
const uint16_t embark_size = finder->x_dim * finder->y_dim; const uint16_t embark_size = finder->x_dim * finder->y_dim;
@ -622,6 +650,61 @@ namespace embark_assist {
break; break;
} }
// Freezing
if (finder->freezing != embark_assist::defs::freezing_ranges::NA)
{
int16_t max_max_temperature = tile->max_temperature[5];
int16_t min_max_temperature = tile->max_temperature[5];
int16_t max_min_temperature = tile->min_temperature[5];
int16_t min_min_temperature = tile->min_temperature[5];
for (uint8_t i = 1; i < 10; i++) {
if (tile->max_temperature[i] > max_max_temperature) {
max_max_temperature = tile->max_temperature[i];
}
if (tile->max_temperature[i] != - 30000 &&
tile->max_temperature[i] < min_max_temperature) {
min_max_temperature = tile->max_temperature[i];
}
if (tile->min_temperature[i] != -30000 &&
tile->min_temperature[i] < min_min_temperature) {
min_min_temperature = tile->min_temperature[i];
}
if (tile->min_temperature[i] > max_min_temperature) {
max_min_temperature = tile->min_temperature[i];
}
}
switch (finder->freezing) {
case embark_assist::defs::freezing_ranges::NA:
break; // Excluded above, but the Travis complains if it's not here.
case embark_assist::defs::freezing_ranges::Permanent:
if (min_max_temperature > 0) return false;
break;
case embark_assist::defs::freezing_ranges::At_Least_Partial:
if (min_min_temperature > 0) return false;
break;
case embark_assist::defs::freezing_ranges::Partial:
if (min_min_temperature > 0 ||
max_max_temperature <= 0) return false;
break;
case embark_assist::defs::freezing_ranges::At_Most_Partial:
if (max_max_temperature <= 0) return false;
break;
case embark_assist::defs::freezing_ranges::Never:
if (max_min_temperature <= 0) return false;
break;
}
}
// Blood Rain // Blood Rain
switch (finder->blood_rain) { switch (finder->blood_rain) {
case embark_assist::defs::yes_no_ranges::NA: case embark_assist::defs::yes_no_ranges::NA:

@ -151,21 +151,21 @@ namespace embark_assist {
if (state->matching) state->show = true; if (state->matching) state->show = true;
Screen::drawBorder("Embark Assistant"); Screen::drawBorder(" Embark Assistant ");
Screen::Pen pen_lr(' ', COLOR_LIGHTRED); Screen::Pen pen_lr(' ', COLOR_LIGHTRED);
Screen::Pen pen_w(' ', COLOR_WHITE); Screen::Pen pen_w(' ', COLOR_WHITE);
Screen::paintString(pen_lr, width - 28, 20, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_I).c_str(), false); Screen::paintString(pen_lr, width - 28, 20, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_I).c_str(), false);
Screen::paintString(pen_w, width - 27, 20, ":Embark Assistant Info", false); Screen::paintString(pen_w, width - 27, 20, ": Embark Assistant Info", false);
Screen::paintString(pen_lr, width - 28, 21, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_F).c_str(), false); Screen::paintString(pen_lr, width - 28, 21, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_F).c_str(), false);
Screen::paintString(pen_w, width - 27, 21, ":Find Embark ", false); Screen::paintString(pen_w, width - 27, 21, ": Find Embark ", false);
Screen::paintString(pen_lr, width - 28, 22, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_C).c_str(), false); Screen::paintString(pen_lr, width - 28, 22, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_C).c_str(), false);
Screen::paintString(pen_w, width - 27, 22, ":Cancel/Clear Find", false); Screen::paintString(pen_w, width - 27, 22, ": Cancel/Clear Find", false);
Screen::paintString(pen_lr, width - 28, 23, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_Q).c_str(), false); Screen::paintString(pen_lr, width - 28, 23, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_Q).c_str(), false);
Screen::paintString(pen_w, width - 27, 23, ":Quit Embark Assistant", false); Screen::paintString(pen_w, width - 27, 23, ": Quit Embark Assistant", false);
Screen::paintString(pen_w, width - 28, 25, "Matching World Tiles", false); Screen::paintString(pen_w, width - 28, 25, "Matching World Tiles:", false);
Screen::paintString(empty_pen, width - 7, 25, to_string(state->match_count), false); Screen::paintString(empty_pen, width - 6, 25, to_string(state->match_count), false);
if (height > 25) { // Mask the vanilla DF find help as it's overridden. if (height > 25) { // Mask the vanilla DF find help as it's overridden.
Screen::paintString(pen_w, 50, height - 2, " ", false); Screen::paintString(pen_w, 50, height - 2, " ", false);

@ -1,3 +1,4 @@
#include <math.h>
#include <vector> #include <vector>
#include "Core.h" #include "Core.h"
@ -399,6 +400,67 @@ namespace embark_assist {
} }
} }
} }
//=================================================================================
int16_t min_temperature(int16_t max_temperature, uint16_t latitude) {
uint16_t divisor;
uint16_t steps;
uint16_t lat;
if (world->world_data->flip_latitude == df::world_data::T_flip_latitude::None) {
return max_temperature;
}
else if (world->world_data->flip_latitude == df::world_data::T_flip_latitude::North ||
world->world_data->flip_latitude == df::world_data::T_flip_latitude::South) {
steps = world->world_data->world_height / 2;
if (latitude > steps) {
lat = world->world_data->world_height - 1 - latitude;
}
else
{
lat = latitude;
}
}
else { // Both
steps = world->world_data->world_height / 4;
if (latitude < steps) {
lat = latitude;
}
else if (latitude <= steps * 2) {
lat = steps * 2 - latitude;
}
else if (latitude <= steps * 3) {
lat = latitude - steps * 2;
}
else {
lat = world->world_data->world_height - latitude;
}
}
if (world->world_data->world_height == 17) {
divisor = (57 / steps * lat + 0.4);
}
else if (world->world_data->world_height == 33) {
divisor = (61 / steps * lat + 0.1);
}
else if (world->world_data->world_height == 65) {
divisor = (63 / steps * lat);
}
else if (world->world_data->world_height == 129 ||
world->world_data->world_height == 257) {
divisor = (64 / steps * lat);
}
else {
return max_temperature; // Not any standard world height. No formula available
}
return max_temperature - ceil(divisor * 3 / 4);
}
} }
} }
@ -450,7 +512,10 @@ void embark_assist::survey::clear_results(embark_assist::defs::match_results *ma
void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_data *geo_summary, void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_data *geo_summary,
embark_assist::defs::world_tile_data *survey_results) { embark_assist::defs::world_tile_data *survey_results) {
// color_ostream_proxy out(Core::getInstance().getConsole()); // color_ostream_proxy out(Core::getInstance().getConsole());
int16_t temperature;
bool negative;
embark_assist::survey::geo_survey(geo_summary); embark_assist::survey::geo_survey(geo_summary);
for (uint16_t i = 0; i < world->worldgen.worldgen_parms.dim_x; i++) { for (uint16_t i = 0; i < world->worldgen.worldgen_parms.dim_x; i++) {
@ -488,6 +553,23 @@ void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_dat
results.biome_index[l] = world_data->region_map[adjusted.x][adjusted.y].region_id; results.biome_index[l] = world_data->region_map[adjusted.x][adjusted.y].region_id;
results.biome[l] = get_biome_type(adjusted.x, adjusted.y, k); results.biome[l] = get_biome_type(adjusted.x, adjusted.y, k);
temperature = world_data->region_map[adjusted.x][adjusted.y].temperature;
negative = temperature < 0;
if (negative) {
temperature = -temperature;
}
results.max_temperature[l] = (temperature / 4) * 3;
if (temperature % 4 > 1) {
results.max_temperature[l] = results.max_temperature[l] + temperature % 4 - 1;
}
if (negative) {
results.max_temperature[l] = -results.max_temperature[l];
}
results.min_temperature[l] = min_temperature(results.max_temperature[l], adjusted.y);
geo_index = world_data->region_map[adjusted.x][adjusted.y].geo_index; geo_index = world_data->region_map[adjusted.x][adjusted.y].geo_index;
if (!geo_summary->at(geo_index).aquifer_absent) results.aquifer_count++; if (!geo_summary->at(geo_index).aquifer_absent) results.aquifer_count++;
@ -518,6 +600,8 @@ void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_dat
else { else {
results.biome_index[l] = -1; results.biome_index[l] = -1;
results.biome[l] = -1; results.biome[l] = -1;
results.max_temperature[l] = -30000;
results.min_temperature[l] = -30000;
} }
} }
@ -1109,7 +1193,6 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles *
if (i == state->local_min_x && k == state->local_min_y) { if (i == state->local_min_x && k == state->local_min_y) {
elevation = mlt->at(i).at(k).elevation; elevation = mlt->at(i).at(k).elevation;
} }
else if (elevation != mlt->at(i).at(k).elevation) { else if (elevation != mlt->at(i).at(k).elevation) {
site_info->flat = false; site_info->flat = false;

@ -147,13 +147,13 @@ function registerBuilding(args)
end end
--animations and connections next: --animations and connections next:
local gears local gears
local frames
local frameLength local frameLength
local animate=args.animate local animate=args.animate
if not auto_gears then if not auto_gears then
gears=args.gears or {} gears=args.gears or {}
frameLength=1 frameLength=1
local frames
if animate~=nil then if animate~=nil then
frameLength=animate.frameLength frameLength=animate.frameLength
if animate.isMechanical then if animate.isMechanical then

@ -1932,20 +1932,20 @@ void viewscreen_unitlaborsst::render()
int8_t fg = 15, bg = 0; int8_t fg = 15, bg = 0;
int stress_lvl = unit->status.current_soul ? unit->status.current_soul->personality.stress_level : 0; int stress_lvl = unit->status.current_soul ? unit->status.current_soul->personality.stress_level : 0;
const vector<UIColor> stress_colors {
13, // 5:1
12, // 4:1
14, // 6:1
2, // 2:0
10 // 2:1 (default)
};
fg = vector_get(stress_colors, Units::getStressCategoryRaw(stress_lvl), stress_colors.back());
// cap at 6 digits // cap at 6 digits
if (stress_lvl < -99999) stress_lvl = -99999; if (stress_lvl < -99999) stress_lvl = -99999;
if (stress_lvl > 999999) stress_lvl = 999999; if (stress_lvl > 999999) stress_lvl = 999999;
string stress = stl_sprintf("%6i", stress_lvl); string stress = stl_sprintf("%6i", stress_lvl);
if (stress_lvl >= 500000)
fg = 13; // 5:1
else if (stress_lvl >= 250000)
fg = 12; // 4:1
else if (stress_lvl >= 100000)
fg = 14; // 6:1
else if (stress_lvl >= 0)
fg = 2; // 2:0
else
fg = 10; // 2:1
Screen::paintString(Screen::Pen(' ', fg, bg), col_offsets[DISP_COLUMN_STRESS], 4 + row, stress); Screen::paintString(Screen::Pen(' ', fg, bg), col_offsets[DISP_COLUMN_STRESS], 4 + row, stress);
Screen::paintTile( Screen::paintTile(

@ -255,6 +255,10 @@ static df::world_region_details *get_details(df::world_data *data, df::coord2d p
bool estimate_underground(color_ostream &out, EmbarkTileLayout &tile, df::world_region_details *details, int x, int y) bool estimate_underground(color_ostream &out, EmbarkTileLayout &tile, df::world_region_details *details, int x, int y)
{ {
if (x < 0 || y < 0 || x > 15 || y > 15) {
out.printerr("Invalid embark coordinates: x=%i, y=%i\n", x, y);
return false;
}
// Find actual biome // Find actual biome
int bv = clip_range(details->biome[x][y] & 15, 1, 9); int bv = clip_range(details->biome[x][y] & 15, 1, 9);
tile.biome_off = biome_delta[bv-1]; tile.biome_off = biome_delta[bv-1];
@ -519,9 +523,9 @@ static command_result embark_prospector(color_ostream &out, df::viewscreen_choos
biomes[screen->biome_rgn[screen->biome_idx]]++; biomes[screen->biome_rgn[screen->biome_idx]]++;
}*/ }*/
for (int x = screen->location.embark_pos_min.x; x <= screen->location.embark_pos_max.x; x++) for (int x = screen->location.embark_pos_min.x; x <= 15 && x <= screen->location.embark_pos_max.x; x++)
{ {
for (int y = screen->location.embark_pos_min.y; y <= screen->location.embark_pos_max.y; y++) for (int y = screen->location.embark_pos_min.y; y <= 15 && y <= screen->location.embark_pos_max.y; y++)
{ {
EmbarkTileLayout tile; EmbarkTileLayout tile;
if (!estimate_underground(out, tile, cur_details, x, y) || if (!estimate_underground(out, tile, cur_details, x, y) ||

@ -5,6 +5,8 @@ option optimize_for = LITE_RUNTIME;
import "RemoteFortressReader.proto"; import "RemoteFortressReader.proto";
// Plugin: RemoteFortressReader
enum AdvmodeMenu enum AdvmodeMenu
{ {
Default = 0; Default = 0;

@ -3,6 +3,8 @@ package ItemdefInstrument;
//Attempts to provide a complete framework for reading everything from a fortress needed for vizualization //Attempts to provide a complete framework for reading everything from a fortress needed for vizualization
option optimize_for = LITE_RUNTIME; option optimize_for = LITE_RUNTIME;
// Plugin: RemoteFortressReader
message InstrumentFlags message InstrumentFlags
{ {
optional bool indefinite_pitch = 1; optional bool indefinite_pitch = 1;

@ -3,8 +3,46 @@ package RemoteFortressReader;
//Attempts to provide a complete framework for reading everything from a fortress needed for vizualization //Attempts to provide a complete framework for reading everything from a fortress needed for vizualization
option optimize_for = LITE_RUNTIME; option optimize_for = LITE_RUNTIME;
// Plugin: RemoteFortressReader
import "ItemdefInstrument.proto"; import "ItemdefInstrument.proto";
// RPC GetMaterialList : EmptyMessage -> MaterialList
// RPC GetGrowthList : EmptyMessage -> MaterialList
// RPC GetBlockList : BlockRequest -> BlockList
// RPC CheckHashes : EmptyMessage -> EmptyMessage
// RPC GetTiletypeList : EmptyMessage -> TiletypeList
// RPC GetPlantList : BlockRequest -> PlantList
// RPC GetUnitList : EmptyMessage -> UnitList
// RPC GetUnitListInside : BlockRequest -> UnitList
// RPC GetViewInfo : EmptyMessage -> ViewInfo
// RPC GetMapInfo : EmptyMessage -> MapInfo
// RPC ResetMapHashes : EmptyMessage -> EmptyMessage
// RPC GetItemList : EmptyMessage -> MaterialList
// RPC GetBuildingDefList : EmptyMessage -> BuildingList
// RPC GetWorldMap : EmptyMessage -> WorldMap
// RPC GetWorldMapNew : EmptyMessage -> WorldMap
// RPC GetRegionMaps : EmptyMessage -> RegionMaps
// RPC GetRegionMapsNew : EmptyMessage -> RegionMaps
// RPC GetCreatureRaws : EmptyMessage -> CreatureRawList
// RPC GetPartialCreatureRaws : ListRequest -> CreatureRawList
// RPC GetWorldMapCenter : EmptyMessage -> WorldMap
// RPC GetPlantRaws : EmptyMessage -> PlantRawList
// RPC GetPartialPlantRaws : ListRequest -> PlantRawList
// RPC CopyScreen : EmptyMessage -> ScreenCapture
// RPC PassKeyboardEvent : KeyboardEvent -> EmptyMessage
// RPC SendDigCommand : DigCommand -> EmptyMessage
// RPC SetPauseState : SingleBool -> EmptyMessage
// RPC GetPauseState : EmptyMessage -> SingleBool
// RPC GetVersionInfo : EmptyMessage -> VersionInfo
// RPC GetReports : EmptyMessage -> Status
// RPC MoveCommand : MoveCommandParams -> EmptyMessage
// RPC JumpCommand : MoveCommandParams -> EmptyMessage
// RPC MenuQuery : EmptyMessage -> MenuContents
// RPC MovementSelectCommand : IntMessage -> EmptyMessage
// RPC MiscMoveCommand : MiscMoveParams -> EmptyMessage
// RPC GetLanguage : EmptyMessage -> Language
//We use shapes, etc, because the actual tiletypes may differ between DF versions. //We use shapes, etc, because the actual tiletypes may differ between DF versions.
enum TiletypeShape enum TiletypeShape
{ {

@ -3,6 +3,8 @@ package isoworldremote;
//Describes a very basic material structure for the map embark //Describes a very basic material structure for the map embark
option optimize_for = LITE_RUNTIME; option optimize_for = LITE_RUNTIME;
// Plugin: isoworldremote
enum BasicMaterial { enum BasicMaterial {
AIR = 0; AIR = 0;
OTHER = 1; OTHER = 1;
@ -50,6 +52,7 @@ message EmbarkTile {
optional bool is_valid = 7; optional bool is_valid = 7;
} }
// RPC GetEmbarkTile : TileRequest -> EmbarkTile
message TileRequest { message TileRequest {
optional int32 want_x = 1; optional int32 want_x = 1;
optional int32 want_y = 2; optional int32 want_y = 2;
@ -59,6 +62,7 @@ message MapRequest {
optional string save_folder = 1; optional string save_folder = 1;
} }
// RPC GetEmbarkInfo : MapRequest -> MapReply
message MapReply { message MapReply {
required bool available = 1; required bool available = 1;
optional int32 region_x = 2; optional int32 region_x = 2;
@ -69,6 +73,7 @@ message MapReply {
optional int32 current_season = 7; optional int32 current_season = 7;
} }
// RPC GetRawNames : MapRequest -> RawNames
message RawNames { message RawNames {
required bool available = 1; required bool available = 1;
repeated string inorganic = 2; repeated string inorganic = 2;

@ -2,6 +2,8 @@ package dfproto;
option optimize_for = LITE_RUNTIME; option optimize_for = LITE_RUNTIME;
// Plugin: rename
// RPC RenameSquad : RenameSquadIn -> EmptyMessage // RPC RenameSquad : RenameSquadIn -> EmptyMessage
message RenameSquadIn { message RenameSquadIn {
required int32 squad_id = 1; required int32 squad_id = 1;

@ -462,8 +462,13 @@ sub render_class_vmethods {
push @lines_rb, "def $name(" . join(', ', @argnames) . ')'; push @lines_rb, "def $name(" . join(', ', @argnames) . ')';
indent_rb { indent_rb {
my $args = join('', map { ", $_" } @argargs); my $args = join('', map { ", $_" } @argargs);
my $call = "DFHack.vmethod_call(self, $voff$args)";
my $ret = $meth->findnodes('child::ret-type')->[0]; my $ret = $meth->findnodes('child::ret-type')->[0];
my $call;
if (!$ret or $ret->getAttribute('ld:meta') ne 'primitive') {
$call = "DFHack.vmethod_call(self, $voff$args)";
} else {
$call = "DFHack.vmethod_call_mem_return(self, $voff, rv$args)";
}
render_class_vmethod_ret($call, $ret); render_class_vmethod_ret($call, $ret);
}; };
push @lines_rb, 'end'; push @lines_rb, 'end';
@ -534,6 +539,18 @@ sub render_class_vmethod_ret {
}; };
push @lines_rb, "end._at(ptr) if ptr != 0"; push @lines_rb, "end._at(ptr) if ptr != 0";
} }
elsif ($retmeta eq 'primitive')
{
my $subtype = $ret->getAttribute('ld:subtype');
if ($subtype eq 'stl-string') {
push @lines_rb, "rv = DFHack::StlString.cpp_new";
push @lines_rb, $call;
push @lines_rb, "rv";
} else {
print "Unknown return subtype for $call\n";
push @lines_rb, "nil";
}
}
else else
{ {
print "vmethod unkret $call\n"; print "vmethod unkret $call\n";

@ -994,6 +994,14 @@ module DFHack
vmethod_arg(a3), vmethod_arg(a4), vmethod_arg(a5)) vmethod_arg(a3), vmethod_arg(a4), vmethod_arg(a5))
end end
def self.vmethod_call_mem_return(obj, voff, r0=0, a0=0, a1=0, a2=0, a3=0, a4=0)
this = obj._memaddr
vt = df.get_vtable_ptr(this)
fptr = df.memory_read_ptr(vt + voff)
vmethod_do_call(vmethod_arg(r0), fptr, this, vmethod_arg(a0), vmethod_arg(a1), vmethod_arg(a2),
vmethod_arg(a3), vmethod_arg(a4))
end
def self.vmethod_arg(arg) def self.vmethod_arg(arg)
case arg case arg
when nil, false; 0 when nil, false; 0

@ -69,7 +69,7 @@ command_result df_showmood (color_ostream &out, vector <string> & parameters)
out.printerr("Dwarf with strange mood does not have a mood type!\n"); out.printerr("Dwarf with strange mood does not have a mood type!\n");
continue; continue;
} }
out.print("%s is currently ", DF2CONSOLE(Translation::TranslateName(&unit->name, false)).c_str()); out.print("%s is currently ", DF2CONSOLE(out, Translation::TranslateName(&unit->name, false)).c_str());
switch (unit->mood) switch (unit->mood)
{ {
case mood_type::Macabre: case mood_type::Macabre:

@ -1,8 +1,9 @@
#include <vector> #include <cmath>
#include <cstdio> #include <cstdio>
#include <cstring>
#include <stack> #include <stack>
#include <string> #include <string>
#include <cmath> #include <vector>
#include "Core.h" #include "Core.h"
#include "Console.h" #include "Console.h"
@ -38,6 +39,12 @@ void draw_version(int start_x, int start_y) {
OutputString(COLOR_WHITE, x, y, "Git: "); OutputString(COLOR_WHITE, x, y, "Git: ");
OutputString(COLOR_WHITE, x, y, DFHACK_GIT_DESCRIPTION); OutputString(COLOR_WHITE, x, y, DFHACK_GIT_DESCRIPTION);
} }
if (strlen(DFHACK_BUILD_ID))
{
x = start_x; y++;
OutputString(COLOR_WHITE, x, y, "Build ID: ");
OutputString(COLOR_WHITE, x, y, DFHACK_BUILD_ID);
}
if (DFHACK_IS_PRERELEASE) if (DFHACK_IS_PRERELEASE)
{ {
x = start_x; y++; x = start_x; y++;
@ -66,7 +73,7 @@ struct options_version_hook : df::viewscreen_optionst {
INTERPOSE_NEXT(render)(); INTERPOSE_NEXT(render)();
if (!msg_quit && !in_retire_adv && !msg_peasant && if (!msg_quit && !in_retire_adv && !msg_peasant &&
!in_retire_dwf_abandon_adv && !in_abandon_dwf && !ending_game) !in_retire_dwf_abandon_adv && !in_abandon_dwf && !ending_game)
draw_version(2, gps->dimy - 5); draw_version(2, gps->dimy - 6);
} }
}; };

@ -1 +1 @@
Subproject commit 6849148e8694f5da415cd6920dd9094c352110de Subproject commit 66b39d888e5f2d4f5c194ece08ba62ac7f110478

@ -0,0 +1,25 @@
function test.getCurViewscreen()
local scr = dfhack.gui.getCurViewscreen()
local scr2 = df.global.gview.view
for i = 1, 100 do
if scr2.child then
scr2 = scr2.child
else
break
end
end
expect.eq(scr, scr2)
end
function test.getViewscreenByType()
local scr = dfhack.gui.getCurViewscreen()
local scr2 = dfhack.gui.getViewscreenByType(scr._type)
expect.eq(scr, scr2)
local bad_type = df.viewscreen_titlest
if scr._type == bad_type then
bad_type = df.viewscreen_optionst
end
local scr_bad = dfhack.gui.getViewscreenByType(bad_type)
expect.eq(scr_bad, nil)
end

@ -0,0 +1,15 @@
local utils = require 'utils'
function test.OrderedTable()
local t = utils.OrderedTable()
local keys = {'a', 'c', 'e', 'd', 'b', 'q', 58, -1.2}
for i = 1, #keys do
t[keys[i]] = i
end
local i = 1
for k, v in pairs(t) do
expect.eq(k, keys[i], 'key order')
expect.eq(v, i, 'correct value')
i = i + 1
end
end

@ -1,10 +1,172 @@
local script = require 'gui.script'
local utils = require 'utils'
local args = {...}
local done_command = args[1]
expect = {}
function expect.true_(value, comment)
return not not value, comment, 'expected true'
end
function expect.false_(value, comment)
return not value, comment, 'expected false'
end
function expect.eq(a, b, comment)
return a == b, comment, ('%s ~= %s'):format(a, b)
end
function expect.ne(a, b, comment)
return a ~= b, comment, ('%s == %s'):format(a, b)
end
function expect.error(func, ...)
local ok, ret = pcall(func, ...)
if ok then
return false, 'no error raised by function call'
else
return true
end
end
function delay(frames)
frames = frames or 1
script.sleep(frames, 'frames')
end
function build_test_env()
local env = {
test = utils.OrderedTable(),
expect = {},
delay = delay,
}
local private = {
checks = 0,
checks_ok = 0,
}
for name, func in pairs(expect) do
env.expect[name] = function(...)
private.checks = private.checks + 1
local ret = {func(...)}
local ok = table.remove(ret, 1)
local msg = ''
for _, part in pairs(ret) do
if part then
msg = msg .. ': ' .. tostring(part)
end
end
msg = msg:sub(3) -- strip leading ': '
if ok then
private.checks_ok = private.checks_ok + 1
else
dfhack.printerr('Check failed! ' .. (msg or '(no message)'))
local info = debug.getinfo(2)
dfhack.printerr((' at %s:%d'):format(info.short_src, info.currentline))
print('')
end
end
end
setmetatable(env, {__index = _G})
return env, private
end
function get_test_files()
local files = {}
for _, entry in ipairs(dfhack.filesystem.listdir_recursive(dfhack.getHackPath() .. 'test')) do
if not entry.isdir and not entry.path:match('main.lua') then
table.insert(files, entry.path)
end
end
table.sort(files)
return files
end
function set_test_stage(stage) function set_test_stage(stage)
local f = io.open('test_stage.txt', 'w') local f = io.open('test_stage.txt', 'w')
f:write(stage) f:write(stage)
f:close() f:close()
end end
print('running tests') function finish_tests(ok)
if ok then
print('Tests finished')
else
dfhack.printerr('Tests failed!')
end
if done_command then
dfhack.run_command(done_command)
end
end
function main()
local files = get_test_files()
local counts = {
tests = 0,
tests_ok = 0,
checks = 0,
checks_ok = 0,
file_errors = 0,
}
local passed = true
print('Looking for title screen...')
for i = 0, 100 do
local scr = dfhack.gui.getCurViewscreen()
if df.viewscreen_titlest:is_instance(scr) then
print('Found title screen')
break
else
scr:feed_key(df.interface_key.LEAVESCREEN)
delay(10)
end
end
if not df.viewscreen_titlest:is_instance(dfhack.gui.getCurViewscreen()) then
qerror('Could not find title screen')
end
print('Running tests')
for _, file in ipairs(files) do
print('Running file: ' .. file:sub(file:find('test'), -1))
local env, env_private = build_test_env()
local code, err = loadfile(file, 't', env)
if not code then
passed = false
counts.file_errors = counts.file_errors + 1
dfhack.printerr('Failed to load file: ' .. tostring(err))
else
local ok, err = pcall(code)
if not ok then
passed = false
counts.file_errors = counts.file_errors + 1
dfhack.printerr('Error when running file: ' .. tostring(err))
else
for name, test in pairs(env.test) do
env_private.checks = 0
env_private.checks_ok = 0
counts.tests = counts.tests + 1
local ok, err = pcall(test)
if not ok then
dfhack.printerr('test errored: ' .. name .. ': ' .. tostring(err))
passed = false
elseif env_private.checks ~= env_private.checks_ok then
dfhack.printerr('test failed: ' .. name)
passed = false
else
print('test passed: ' .. name)
counts.tests_ok = counts.tests_ok + 1
end
counts.checks = counts.checks + (tonumber(env_private.checks) or 0)
counts.checks_ok = counts.checks_ok + (tonumber(env_private.checks_ok) or 0)
end
end
end
end
print('\nTest summary:')
print(('%d/%d tests passed'):format(counts.tests_ok, counts.tests))
print(('%d/%d checks passed'):format(counts.checks_ok, counts.checks))
print(('%d test files failed to load'):format(counts.file_errors))
set_test_stage(passed and 'done' or 'fail')
finish_tests(passed)
end
set_test_stage('done') script.start(main)
dfhack.run_command('die')

@ -0,0 +1,109 @@
import glob
import sys
actual = {'': {}}
with open(sys.argv[1]) as f:
plugin_name = ''
for line in f:
line = line.rstrip()
if line.startswith('// Plugin: '):
plugin_name = line.split(' ')[2]
if plugin_name not in actual:
actual[plugin_name] = {}
elif line.startswith('// RPC '):
parts = line.split(' ')
actual[plugin_name][parts[2]] = (parts[4], parts[6])
expected = {'': {}}
for p in glob.iglob('library/proto/*.proto'):
with open(p) as f:
for line in f:
line = line.rstrip()
if line.startswith('// RPC '):
parts = line.split(' ')
expected[''][parts[2]] = (parts[4], parts[6])
for p in glob.iglob('plugins/proto/*.proto'):
plugin_name = ''
with open(p) as f:
for line in f:
line = line.rstrip()
if line.startswith('// Plugin: '):
plugin_name = line.split(' ')[2]
if plugin_name not in expected:
expected[plugin_name] = {}
break
if plugin_name == '':
continue
with open(p) as f:
for line in f:
line = line.rstrip()
if line.startswith('// RPC '):
parts = line.split(' ')
expected[plugin_name][parts[2]] = (parts[4], parts[6])
error_count = 0
for plugin_name in actual:
methods = actual[plugin_name]
if plugin_name not in expected:
print('Missing documentation for plugin proto files: ' + plugin_name)
print('Add the following lines:')
print('// Plugin: ' + plugin_name)
error_count += 1
for m in methods:
io = methods[m]
print('// RPC ' + m + ' : ' + io[0] + ' -> ' + io[1])
error_count += 1
else:
missing = []
wrong = []
for m in methods:
io = methods[m]
if m in expected[plugin_name]:
if expected[plugin_name][m] != io:
wrong.add('// RPC ' + m + ' : ' + io[0] + ' -> ' + io[1])
else:
missing.add('// RPC ' + m + ' : ' + io[0] + ' -> ' + io[1])
if len(missing) > 0:
print('Incomplete documentation for ' + ('core' if plugin_name == '' else 'plugin "' + plugin_name + '"') + ' proto files. Add the following lines:')
for m in missing:
print(m)
error_count += 1
if len(wrong) > 0:
print('Incorrect documentation for ' + ('core' if plugin_name == '' else 'plugin "' + plugin_name + '"') + ' proto files. Replace the following comments:')
for m in wrong:
print(m)
error_count += 1
for plugin_name in expected:
methods = expected[plugin_name]
if plugin_name not in actual:
print('Incorrect documentation for plugin proto files: ' + plugin_name)
print('The following methods are documented, but the plugin does not provide any RPC methods:')
for m in methods:
io = methods[m]
print('// RPC ' + m + ' : ' + io[0] + ' -> ' + io[1])
error_count += 1
else:
missing = []
for m in methods:
io = methods[m]
if m not in actual[plugin_name]:
missing.add('// RPC ' + m + ' : ' + io[0] + ' -> ' + io[1])
if len(missing) > 0:
print('Incorrect documentation for ' + ('core' if plugin_name == '' else 'plugin "' + plugin_name + '"') + ' proto files. Remove the following lines:')
for m in missing:
print(m)
error_count += 1
sys.exit(min(100, error_count))

@ -1,2 +1,4 @@
:lua dfhack.internal.addScriptPath(os.getenv('TRAVIS_BUILD_DIR')) devel/dump-rpc dfhack-rpc.txt
test/main
:lua dfhack.internal.addScriptPath(dfhack.getHackPath())
test/main die

@ -23,8 +23,13 @@ while True:
print('Done!') print('Done!')
os.remove(test_stage) os.remove(test_stage)
sys.exit(0) sys.exit(0)
elif stage == 'fail':
print('Failed!')
os.remove(test_stage)
sys.exit(1)
if tries > MAX_TRIES: if tries > MAX_TRIES:
print('Too many tries - aborting') print('Too many tries - aborting')
sys.exit(1) sys.exit(1)
os.system(dfhack) process = subprocess.Popen([dfhack], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
process.communicate()