Merge branch '5008-beta1' into myk_nosdlreal

develop
Myk 2023-04-26 15:48:13 -07:00 committed by GitHub
commit b1ae39764b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 757 additions and 578 deletions

@ -14,7 +14,7 @@ on:
release_channel:
description: Release channel
type: string
required: true
required: false
default: beta
jobs:
@ -36,9 +36,17 @@ jobs:
restore-keys: |
ccache-win64-cross-msvc-develop-${{ github.event.inputs.commit_hash }}
ccache-win64-cross-msvc
- name: Restore steam SDK
uses: actions/cache@v3
with:
path: depends/steam/steamworks_sdk_156.zip
key: steam-sdk-156
enableCrossOsArchive: true
- name: Cross-compile win64 artifacts
env:
CMAKE_EXTRA_ARGS: '-DBUILD_STONESENSE:BOOL=1'
CMAKE_EXTRA_ARGS: '-DBUILD_STONESENSE:BOOL=1 -DBUILD_DFLAUNCH:BOOL=1'
steam_username: ${{ secrets.STEAM_SDK_USERNAME }}
steam_password: ${{ secrets.STEAM_SDK_PASSWORD }}
run: |
cd build
bash -x build-win64-from-linux.sh

3
.gitignore vendored

@ -80,3 +80,6 @@ tags
# external plugins
/plugins/CMakeLists.custom.txt
# steam api
depends/steam

@ -191,9 +191,9 @@ if(NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl
endif()
# set up versioning.
set(DF_VERSION "50.07")
set(DFHACK_RELEASE "r1")
set(DFHACK_PRERELEASE FALSE)
set(DF_VERSION "50.08b1")
set(DFHACK_RELEASE "beta3")
set(DFHACK_PRERELEASE TRUE)
set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}")
@ -315,6 +315,7 @@ if(WIN32)
${SDL_DOWNLOAD_DIR}/SDL.dll
"5a09604daca6b2b5ce049d79af935d6a")
endif()
endif()
if(APPLE)

@ -41,6 +41,8 @@ fi
if ! docker run --rm -i -v "$srcdir":/src -v "$srcdir/build/win64-cross/":/src/build \
-e BUILDER_UID=$builder_uid \
-e CCACHE_DIR=/src/build/ccache \
-e steam_username \
-e steam_password \
--name dfhack-win \
ghcr.io/dfhack/build-env:msvc \
bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output cmake .. -DBUILD_DOCS=1 $CMAKE_EXTRA_ARGS && dfhack-make -j$jobs install" \

@ -0,0 +1,4 @@
IF EXIST DF_PATH.txt SET /P _DF_PATH=<DF_PATH.txt
IF NOT EXIST DF_PATH.txt SET _DF_PATH=%CD%\DF
echo generating a build folder
cmake ..\.. -G"Visual Studio 17 2022" -A x64 -B VC2022 -DCMAKE_INSTALL_PREFIX="%_DF_PATH%" -DBUILD_DEVEL=1 -DBUILD_DEV_PLUGINS=1 -DBUILD_STONESENSE=1 -DBUILD_DFLAUNCH=1

@ -0,0 +1,5 @@
{
"planner": {
"minimized": true
}
}

@ -49,6 +49,9 @@ keybinding add Ctrl-H@dwarfmode autodump-destroy-here
# apply blueprints to the map
keybinding add Ctrl-Shift-Q@dwarfmode gui/quickfort
# toggle keyboard cursor
keybinding add Alt-K@dwarfmode toggle-kbd-cursor
# show information collected by dwarfmonitor
#keybinding add Alt-M@dwarfmode/Default "dwarfmonitor prefs"
#keybinding add Ctrl-F@dwarfmode/Default "dwarfmonitor stats"

@ -144,3 +144,4 @@ enable \
alias add autounsuspend suspendmanager
alias add gui/dig gui/design
alias add version help

@ -1 +1 @@
Subproject commit 6ed8aa46462ea01a1122fc49422840a2facc9757
Subproject commit d5e17c6012e7eefb0cbe3e130a56c24bd11f0094

@ -1,5 +1,5 @@
project(lua CXX)
cmake_minimum_required(VERSION 2.8)
cmake_minimum_required(VERSION 3.21)
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DLUA_USE_APICHECK")

@ -1 +1 @@
Subproject commit 439fdbc259c13f23a3122e68ba35ad5a13bcd97c
Subproject commit 0a994526622c2201756e386ef98b44b193e25f06

@ -162,7 +162,7 @@ You can run them all from the launcher.
First, let's import some useful manager orders to keep your fort stocked with
basic necessities. Run ``orders import library/basic``. If you go to your
mangager orders screen, you can see all the orders that have been created for you.
manager orders screen, you can see all the orders that have been created for you.
Note that you could have imported the orders directly from this screen as well,
using the DFHack `overlay` widget at the bottom of the manager orders panel.

@ -21,7 +21,9 @@ for the tag assignment spreadsheet.
"why" tags
----------
- `armok <armok-tag-index>`: Tools that give you complete control over an aspect of the game or provide access to information that the game intentionally keeps hidden.
- `armok <armok-tag-index>`: Tools which give the player god-like powers of any variety, such as control over game events, creating items from thin air, or viewing information the game intentionally keeps hidden. Players that do not wish to see these tools listed in DFHack command lists can hide them in the ``Preferences`` tab of `gui/control-panel`.
- `auto <auto-tag-index>`: Tools that run in the background and automatically manage routine, toilsome aspects of your fortress.
- `bugfix <bugfix-tag-index>`: Tools that fix specific bugs, either permanently or on-demand.
- `design <design-tag-index>`: Tools that help you design your fort.

@ -12,6 +12,7 @@ Name Github Other
8Z 8Z
Abel abstern
acwatkins acwatkins
Alex Blamey Cubittus
Alexander Collins gearsix
Alexander Gavrilov angavrilov ag
Amber Brown hawkowl
@ -133,6 +134,7 @@ Milo Christiansen milochristiansen
MithrilTuxedo MithrilTuxedo
mizipzor mizipzor
moversti moversti
mrrho mrrho
Murad Beybalaev Erquint
Myk Taylor myk002
napagokc napagokc
@ -158,6 +160,7 @@ Petr Mrázek peterix
Pfhreak Pfhreak
Pierre Lulé plule
Pierre-David Bélanger pierredavidbelanger
PopnROFL PopnROFL
potato
ppaawwll ppaawwll 🐇🐇🐇🐇
Priit Laes plaes
@ -201,6 +204,7 @@ SeerSkye SeerSkye
seishuuu seishuuu
Seth Woodworth sethwoodworth
Shim Panze Shim-Panze
Silver silverflyone
simon
Simon Jackson sizeak
Simon Lees simotek
@ -235,6 +239,7 @@ ViTuRaS ViTuRaS
Vjek vjek
Warmist warmist
Wes Malone wesQ3
Will H TSM-EVO
Will Rogers wjrogers
WoosterUK WoosterUK
XianMaeve XianMaeve

@ -206,6 +206,12 @@ stocksettings
Along with ``copystock``, ``loadstock`` and ``savestock``, replaced with the new
`stockpiles` API.
.. _title-version:
title-version
=============
Replaced with an `overlay`.
.. _warn-stuck-trees:
warn-stuck-trees

@ -36,16 +36,31 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
## New Plugins
## Fixes
- `autoclothing`: eliminate game lag when there are many inventory items in the fort
- `buildingplan`: fixed size limit calculations for rollers
- `dig-now`: properly detect and complete smoothing designations that have been converted into active jobs
## Misc Improvements
- `buildingplan`: planner panel is minimized by default and now remembers minimized state
- `buildingplan`: can now filter by gems (for gem windows) and yarn (for ropes in wells)
- ``toggle-kbd-cursor``: add hotkey for toggling the keyboard cursor (Alt-K)
- ``version``: add alias to display the DFHack help (including the version number) so something happens when players try to run "version"
- `gui/control-panel`: add preference option for hiding the terminal console on startup
- `gui/control-panel`: add preference option for hiding "armok" tools in command lists
- ``Dwarf Therapist``: add a warning to the Labors screen when Dwarf Therapist is active so players know that changes they make to that screen will have no effect. If you're starting a new embark and nobody seems to be doing anything, check your Labors tab for this warning to see if Dwarf Therapist thinks it is in control (even if it's not running).
- `overlay`: add the DFHack version string to the DF title screen
## Documentation
## API
## Lua
- ``widgets.RangeSlider``: new mouse-controlled two-headed slider widget
- ``gui.ZScreenModal``: ZScreen subclass for modal dialogs
- ``widgets.CycleHotkeyLabel``: exposed "key_sep" and "val_gap" attributes for improved stylistic control.
## Removed
- `title-version`: replaced by an `overlay` widget
# 50.07-r1

@ -4323,6 +4323,13 @@ Here is an example skeleton for a ZScreen tool window::
view = view and view:raise() or MyScreen{}:show()
ZScreenModal class
------------------
A ZScreen convenience subclass that sets the attributes to something
appropriate for modal dialogs. The game is force paused, and no input is passed
through to the underlying viewscreens.
FramedScreen class
------------------
@ -4905,12 +4912,17 @@ It has the following attributes:
:key: The hotkey keycode to display, e.g. ``'CUSTOM_A'``.
:key_back: Similar to ``key``, but will cycle backwards (optional)
:key_sep: If specified, will be used to customize how the activation key is
displayed. See ``token.key_sep`` in the ``Label`` documentation.
:label: The string (or a function that returns a string) to display after the
hotkey.
:label_width: The number of spaces to allocate to the ``label`` (for use in
aligning a column of ``CycleHotkeyLabel`` labels).
:label_below: If ``true``, then the option value will apear below the label
instead of to the right of it. Defaults to ``false``.
:val_gap: The size of the gap between the label text and the option value.
Default is ``1``. If set to ``0``, there'll be no gap between the strings.
Note that ``val_gap`` is ignored if ``label_below`` is set to ``true``.
:options: A list of strings or tables of
``{label=string or fn, value=val[, pen=pen]}``. String options use the same
string for the label and value and use the default pen. The optional ``pen``
@ -5130,6 +5142,20 @@ widget does not require direct usage of ``Tab``.
usage of ``Tab`` in ``TabBar:init()`` for an example. See the default value of ``active_tab_pens`` or ``inactive_tab_pens``
in ``TabBar`` for an example of how to construct pens.
RangeSlider class
-----------------
This widget implements a mouse-interactable range-slider. The player can move its two handles to set minimum and maximum values
to define a range, or they can drag the bar itself to move both handles at once.
The parent widget owns the range values, and can control them independently (e.g. with ``CycleHotkeyLabels``). If the range values change, the ``RangeSlider`` appearance will adjust automatically.
:num_stops: Used to specify the number of "notches" in the range slider, the places where handles can stop.
(this should match the parents' number of options)
:get_left_idx_fn: The function used by the RangeSlider to get the notch index on which to display the left handle.
:get_right_idx_fn: The function used by the RangeSlider to get the notch index on which to display the right handle.
:on_left_change: Callback executed when moving the left handle.
:on_right_change: Callback executed when moving the right handle.
.. _lua-plugins:
=======

@ -1,14 +0,0 @@
title-version
=============
.. dfhack-tool::
:summary: Displays the DFHack version on DF's title screen.
:tags: unavailable interface
:no-command:
Usage
-----
::
enable title-version

@ -1,5 +1,5 @@
project(dfapi)
cmake_minimum_required(VERSION 2.8.12)
cmake_minimum_required(VERSION 3.21)
# prevent CMake warnings about INTERFACE_LINK_LIBRARIES vs LINK_INTERFACE_LIBRARIES
cmake_policy(SET CMP0022 NEW)

@ -273,7 +273,7 @@ static std::string dfhack_version_desc()
if (Version::is_release())
s << "(release)";
else
s << "(development build " << Version::git_description() << ")";
s << "(git: " << Version::git_commit(true) << ")";
s << " on " << (sizeof(void*) == 8 ? "x86_64" : "x86");
if (strlen(Version::dfhack_build_id()))
s << " [build ID: " << Version::dfhack_build_id() << "]";
@ -1314,6 +1314,15 @@ static void run_dfhack_init(color_ostream &out, Core *core)
// load user overrides
std::vector<std::string> prefixes(1, "dfhack");
loadScriptFiles(core, out, prefixes, CONFIG_PATH + "init");
// if the option is set, hide the terminal
auto L = Lua::Core::State;
Lua::StackUnwinder top(L);
Lua::CallLuaModuleFunction(out, L, "dfhack", "getHideConsoleOnStartup", 0, 1,
Lua::DEFAULT_LUA_LAMBDA, [&](lua_State* L) {
if (lua_toboolean(L, -1))
core->getConsole().hide();
}, false);
}
// Load dfhack.init in a dedicated thread (non-interactive console mode)
@ -2083,7 +2092,8 @@ void Core::handleLoadAndUnloadScripts(color_ostream& out, state_change_event eve
if (!df::global::world)
return;
std::string rawFolder = "save/" + (df::global::world->cur_savegame.save_dir) + "/init";
std::string rawFolder = !isWorldLoaded() ? "" : "save/" + World::ReadWorldFolder() + "/init";
auto i = table.find(event);
if ( i != table.end() ) {

@ -1,6 +1,8 @@
#define NO_DFHACK_VERSION_MACROS
#include "DFHackVersion.h"
#include "git-describe.h"
#include <string>
namespace DFHack {
namespace Version {
int dfhack_abi_version()
@ -27,9 +29,10 @@ namespace DFHack {
{
return DFHACK_GIT_DESCRIPTION;
}
const char *git_commit()
const char* git_commit(bool short_hash)
{
return DFHACK_GIT_COMMIT;
static std::string shorty(DFHACK_GIT_COMMIT, 0, 7);
return short_hash ? shorty.c_str() : DFHACK_GIT_COMMIT;
}
const char *git_xml_commit()
{

@ -8,7 +8,7 @@ namespace DFHack {
int dfhack_abi_version();
const char *git_description();
const char *git_commit();
const char* git_commit(bool short_hash = false);
const char *git_xml_commit();
const char *git_xml_expected_commit();
bool git_xml_match();

@ -342,26 +342,6 @@ namespace DFHack {namespace Lua {
DFHACK_EXPORT void PushInterfaceKeys(lua_State *L, const std::set<df::interface_key> &keys);
template<class T>
void PushVector(lua_State *state, const T &pvec, bool addn = false)
{
lua_createtable(state,pvec.size(), addn?1:0);
if (addn)
{
lua_pushinteger(state, pvec.size());
lua_setfield(state, -2, "n");
}
for (size_t i = 0; i < pvec.size(); i++)
{
Push(state, pvec[i]);
lua_rawseti(state, -2, i+1);
}
}
DFHACK_EXPORT void GetVector(lua_State *state, std::vector<std::string> &pvec, int idx = 1);
DFHACK_EXPORT int PushPosXYZ(lua_State *state, const df::coord &pos);
DFHACK_EXPORT int PushPosXY(lua_State *state, const df::coord2d &pos);
@ -412,6 +392,26 @@ namespace DFHack {namespace Lua {
lua_settable(state, -3);
}
template<class T>
void PushVector(lua_State *state, const T &pvec, bool addn = false)
{
lua_createtable(state,pvec.size(), addn?1:0);
if (addn)
{
lua_pushinteger(state, pvec.size());
lua_setfield(state, -2, "n");
}
for (size_t i = 0; i < pvec.size(); i++)
{
Push(state, pvec[i]);
lua_rawseti(state, -2, i+1);
}
}
DFHACK_EXPORT void GetVector(lua_State *state, std::vector<std::string> &pvec, int idx = 1);
DFHACK_EXPORT void CheckPen(lua_State *L, Screen::Pen *pen, int index, bool allow_nil = false, bool allow_color = true);
DFHACK_EXPORT bool IsCoreContext(lua_State *state);

@ -51,6 +51,18 @@ if dfhack.is_core_context then
SC_UNPAUSED = 8
end
-- User-changeable options
dfhack.HIDE_CONSOLE_ON_STARTUP = true
function dfhack.getHideConsoleOnStartup()
return dfhack.HIDE_CONSOLE_ON_STARTUP
end
dfhack.HIDE_ARMOK_TOOLS = false
function dfhack.getHideArmokTools()
return dfhack.HIDE_ARMOK_TOOLS
end
-- Error handling
safecall = dfhack.safecall

@ -745,6 +745,7 @@ end
local NO_LOGIC_SCREENS = {
'viewscreen_loadgamest',
'viewscreen_adopt_regionst',
'viewscreen_export_regionst',
'viewscreen_choose_game_typest',
'viewscreen_worldst',
@ -866,8 +867,17 @@ function ZScreen:onGetSelectedPlant()
return zscreen_get_any(self, 'Plant')
end
--------------------------
-- Framed screen object --
-- convenience subclass for modal dialogs
ZScreenModal = defclass(ZScreenModal, ZScreen)
ZScreenModal.ATTRS{
defocusable = false,
force_pause = true,
pass_pause = false,
pass_movement_keys = false,
pass_mouse_clicks = false,
}
-- Framed screen object
--------------------------
-- Plain grey-colored frame.

@ -1488,6 +1488,8 @@ CycleHotkeyLabel = defclass(CycleHotkeyLabel, Label)
CycleHotkeyLabel.ATTRS{
key=DEFAULT_NIL,
key_back=DEFAULT_NIL,
key_sep=': ',
val_gap=1,
label=DEFAULT_NIL,
label_width=DEFAULT_NIL,
label_below=false,
@ -1499,17 +1501,16 @@ CycleHotkeyLabel.ATTRS{
function CycleHotkeyLabel:init()
self:setOption(self.initial_option)
local val_gap = 1
if self.label_below then
val_gap = 0 + (self.key_back and 1 or 0) + (self.key and 3 or 0)
self.val_gap = 0 + (self.key_back and 1 or 0) + (self.key and 3 or 0)
end
self:setText{
self.key_back ~= nil and {key=self.key_back, key_sep='', width=0, on_activate=self:callback('cycle', true)} or {},
{key=self.key, key_sep=': ', text=self.label, width=self.label_width,
{key=self.key, key_sep=self.key_sep, text=self.label, width=self.label_width,
on_activate=self:callback('cycle')},
self.label_below and NEWLINE or '',
{gap=val_gap, text=self:callback('getOptionLabel'),
{gap=self.val_gap, text=self:callback('getOptionLabel'),
pen=self:callback('getOptionPen')},
}
end
@ -2293,4 +2294,141 @@ function TabBar:onInput(keys)
end
end
--------------------------------
-- RangeSlider
--
RangeSlider = defclass(RangeSlider, Widget)
RangeSlider.ATTRS{
num_stops=DEFAULT_NIL,
get_left_idx_fn=DEFAULT_NIL,
get_right_idx_fn=DEFAULT_NIL,
on_left_change=DEFAULT_NIL,
on_right_change=DEFAULT_NIL,
}
function RangeSlider:preinit(init_table)
init_table.frame = init_table.frame or {}
init_table.frame.h = init_table.frame.h or 1
end
function RangeSlider:init()
if self.num_stops < 2 then error('too few RangeSlider stops') end
self.is_dragging_target = nil -- 'left', 'right', or 'both'
self.is_dragging_idx = nil -- offset from leftmost dragged tile
end
local function rangeslider_get_width_per_idx(self)
return math.max(5, (self.frame_body.width-7) // (self.num_stops-1))
end
function RangeSlider:onInput(keys)
if not keys._MOUSE_L_DOWN then return false end
local x = self:getMousePos()
if not x then return false end
local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn()
local width_per_idx = rangeslider_get_width_per_idx(self)
local left_pos = width_per_idx*(left_idx-1)
local right_pos = width_per_idx*(right_idx-1) + 4
if x < left_pos then
self.on_left_change(self.get_left_idx_fn() - 1)
elseif x < left_pos+3 then
self.is_dragging_target = 'left'
self.is_dragging_idx = x - left_pos
elseif x < right_pos then
self.is_dragging_target = 'both'
self.is_dragging_idx = x - left_pos
elseif x < right_pos+3 then
self.is_dragging_target = 'right'
self.is_dragging_idx = x - right_pos
else
self.on_right_change(self.get_right_idx_fn() + 1)
end
return true
end
local function rangeslider_do_drag(self, width_per_idx)
local x = self.frame_body:localXY(dfhack.screen.getMousePos())
local cur_pos = x - self.is_dragging_idx
cur_pos = math.max(0, cur_pos)
cur_pos = math.min(width_per_idx*(self.num_stops-1)+7, cur_pos)
local offset = self.is_dragging_target == 'right' and -2 or 1
local new_idx = math.max(0, cur_pos+offset)//width_per_idx + 1
local new_left_idx, new_right_idx
if self.is_dragging_target == 'right' then
new_right_idx = new_idx
else
new_left_idx = new_idx
if self.is_dragging_target == 'both' then
new_right_idx = new_left_idx + self.get_right_idx_fn() - self.get_left_idx_fn()
if new_right_idx > self.num_stops then
return
end
end
end
if new_left_idx and new_left_idx ~= self.get_left_idx_fn() then
self.on_left_change(new_left_idx)
end
if new_right_idx and new_right_idx ~= self.get_right_idx_fn() then
self.on_right_change(new_right_idx)
end
end
local SLIDER_LEFT_END = to_pen{ch=198, fg=COLOR_GREY, bg=COLOR_BLACK}
local SLIDER_TRACK = to_pen{ch=205, fg=COLOR_GREY, bg=COLOR_BLACK}
local SLIDER_TRACK_SELECTED = to_pen{ch=205, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK}
local SLIDER_TRACK_STOP = to_pen{ch=216, fg=COLOR_GREY, bg=COLOR_BLACK}
local SLIDER_TRACK_STOP_SELECTED = to_pen{ch=216, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK}
local SLIDER_RIGHT_END = to_pen{ch=181, fg=COLOR_GREY, bg=COLOR_BLACK}
local SLIDER_TAB_LEFT = to_pen{ch=60, fg=COLOR_BLACK, bg=COLOR_YELLOW}
local SLIDER_TAB_CENTER = to_pen{ch=9, fg=COLOR_BLACK, bg=COLOR_YELLOW}
local SLIDER_TAB_RIGHT = to_pen{ch=62, fg=COLOR_BLACK, bg=COLOR_YELLOW}
function RangeSlider:onRenderBody(dc, rect)
local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn()
local width_per_idx = rangeslider_get_width_per_idx(self)
-- draw track
dc:seek(1,0)
dc:char(nil, SLIDER_LEFT_END)
dc:char(nil, SLIDER_TRACK)
for stop_idx=1,self.num_stops-1 do
local track_stop_pen = SLIDER_TRACK_STOP_SELECTED
local track_pen = SLIDER_TRACK_SELECTED
if left_idx > stop_idx or right_idx < stop_idx then
track_stop_pen = SLIDER_TRACK_STOP
track_pen = SLIDER_TRACK
elseif right_idx == stop_idx then
track_pen = SLIDER_TRACK
end
dc:char(nil, track_stop_pen)
for i=2,width_per_idx do
dc:char(nil, track_pen)
end
end
if right_idx >= self.num_stops then
dc:char(nil, SLIDER_TRACK_STOP_SELECTED)
else
dc:char(nil, SLIDER_TRACK_STOP)
end
dc:char(nil, SLIDER_TRACK)
dc:char(nil, SLIDER_RIGHT_END)
-- draw tabs
dc:seek(width_per_idx*(left_idx-1))
dc:char(nil, SLIDER_TAB_LEFT)
dc:char(nil, SLIDER_TAB_CENTER)
dc:char(nil, SLIDER_TAB_RIGHT)
dc:seek(width_per_idx*(right_idx-1)+4)
dc:char(nil, SLIDER_TAB_LEFT)
dc:char(nil, SLIDER_TAB_CENTER)
dc:char(nil, SLIDER_TAB_RIGHT)
-- manage dragging
if self.is_dragging_target then
rangeslider_do_drag(self, width_per_idx)
end
if df.global.enabler.mouse_lbut == 0 then
self.is_dragging_target = nil
self.is_dragging_idx = nil
end
end
return _ENV

@ -788,7 +788,11 @@ function ls(filter_str, skip_tags, show_dev_commands, exclude_strs)
table.insert(excludes, {str=argparse.stringList(exclude_strs)})
end
if not show_dev_commands then
table.insert(excludes, {tag='dev'})
local dev_tags = {'dev', 'unavailable'}
if dfhack.getHideArmokTools() then
table.insert(dev_tags, 'armok')
end
table.insert(excludes, {tag=dev_tags})
end
list_entries(skip_tags, include, excludes)
end
@ -813,7 +817,16 @@ function tags(tag)
local skip_tags = true
local include = {entry_type={ENTRY_TYPES.COMMAND}, tag=tag}
list_entries(skip_tags, include)
local excludes = {tag={}}
if tag ~= 'unavailable' then
table.insert(excludes.tag, 'unavailable')
end
if tag ~= 'armok' and dfhack.getHideArmokTools() then
table.insert(excludes.tag, 'armok')
end
list_entries(skip_tags, include, excludes)
end
return _ENV

@ -1115,31 +1115,17 @@ static void createDesign(df::building *bld, bool rough)
static int getMaxStockpileId()
{
auto &vec = world->buildings.other[buildings_other_id::STOCKPILE];
int max_id = 0;
for (size_t i = 0; i < vec.size(); i++)
{
auto bld = strict_virtual_cast<df::building_stockpilest>(vec[i]);
if (bld)
max_id = std::max(max_id, bld->stockpile_number);
}
for (auto bld : world->buildings.other.STOCKPILE)
max_id = std::max(max_id, bld->stockpile_number);
return max_id;
}
static int getMaxCivzoneId()
{
auto &vec = world->buildings.other[buildings_other_id::ANY_ZONE];
int max_id = 0;
for (size_t i = 0; i < vec.size(); i++)
{
auto bld = strict_virtual_cast<df::building_civzonest>(vec[i]);
if (bld)
max_id = std::max(max_id, bld->zone_num);
}
for (auto bld : world->buildings.other.ANY_ZONE)
max_id = std::max(max_id, bld->zone_num);
return max_id;
}

@ -84,6 +84,7 @@ using namespace DFHack;
#include "df/unit.h"
#include "df/unit_inventory_item.h"
#include "df/viewscreen_dwarfmodest.h"
#include "df/viewscreen_titlest.h"
#include "df/world.h"
const size_t MAX_REPORTS_SIZE = 3000; // DF clears old reports to maintain this vector size
@ -144,6 +145,17 @@ static std::map<virtual_identity*, getFocusStringsHandler> getFocusStringsHandle
); \
static void getFocusStrings_##screen_type(std::string &baseFocus, std::vector<std::string> &focusStrings, VIEWSCREEN(screen_type) *screen)
DEFINE_GET_FOCUS_STRING_HANDLER(title)
{
if (screen->managing_mods)
focusStrings.push_back(baseFocus + "/Mods");
else if (game->main_interface.settings.open)
focusStrings.push_back(baseFocus + "/Settings");
if (focusStrings.empty())
focusStrings.push_back(baseFocus + "/Default");
}
DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode)
{
std::string newFocusString;

@ -439,6 +439,7 @@ bool MaterialInfo::matches(const df::dfhack_material_category &cat) const
return true;
if (cat.bits.milk && linear_index(material->reaction_product.id, std::string("CHEESE_MAT")) >= 0)
return true;
TEST(gem, IS_GEM);
return false;
}

@ -1 +1 @@
Subproject commit 43a89a268b825fc05457678b19e551bf632dcd19
Subproject commit 98d5f8a5553690ef71b9650b28d4aababf21ef5e

@ -1,7 +1,38 @@
project(package_windows)
if(WIN32)
add_executable(launchdf WIN32 launchdf.c)
install(TARGETS launchdf
DESTINATION ${DFHACK_DATA_DESTINATION})
if (BUILD_DFLAUNCH)
if ((DEFINED ENV{steam_username}) AND (DEFINED ENV{steam_password}))
# download Steam SDK
set (STEAMAPI_DIR ${dfhack_SOURCE_DIR}/depends/steam)
file(DOWNLOAD "https://partner.steamgames.com/downloads/steamworks_sdk_156.zip"
${STEAMAPI_DIR}/steamworks_sdk_156.zip
EXPECTED_HASH MD5=af5a579990dbe5ae4c1b0689260d001b
USERPWD $ENV{steam_username}:$ENV{steam_password}
STATUS STEAM_SDK_DOWNLOAD_STATUS
SHOW_PROGRESS
)
list(GET STEAM_SDK_DOWNLOAD_STATUS 0 STEAM_SDK_DL_STATUS_CODE)
list(GET STEAM_SDK_DOWNLOAD_STATUS 1 STEAM_SDK_DL_ERROR_MESSAGE)
if (NOT (${STEAM_SDK_DL_STATUS_CODE} EQUAL 0))
message(FATAL_ERROR "Steam SDK download: " ${STEAM_SDK_DL_ERROR_MESSAGE})
else ()
message(STATUS "Steam SDK download: " ${STEAM_SDK_DL_ERROR_MESSAGE})
file(ARCHIVE_EXTRACT
INPUT ${STEAMAPI_DIR}/steamworks_sdk_156.zip
DESTINATION ${STEAMAPI_DIR})
set(STEAMAPI_LIBRARY "${STEAMAPI_DIR}/sdk/redistributable_bin/win64/steam_api64.lib")
set(STEAMAPI_SOURCE_DIR "${STEAMAPI_DIR}/sdk/public/steam")
set(STEAMAPI_SHARED_LIBRARY "${STEAMAPI_DIR}/sdk/redistributable_bin/win64/steam_api64.dll")
endif()
else()
message(SEND_ERROR "Need to set steam_username and steam_password in environment to download Steamworks SDK")
endif()
include_directories(${STEAMAPI_SOURCE_DIR})
link_libraries(${STEAMAPI_LIBRARY})
add_executable(launchdf WIN32 launchdf.cpp)
install(TARGETS launchdf DESTINATION ${DFHACK_DATA_DESTINATION})
install(FILES ${STEAMAPI_SHARED_LIBRARY} DESTINATION ${DFHACK_DATA_DESTINATION})
endif()
endif()

@ -1,72 +0,0 @@
#include <process.h>
#include <windows.h>
static BOOL is_running_on_wine() {
static const char *(CDECL *pwine_get_version)(void);
HMODULE hntdll = GetModuleHandle("ntdll.dll");
if(!hntdll)
return FALSE;
pwine_get_version = (void *)GetProcAddress(hntdll, "wine_get_version");
return !!pwine_get_version;
}
static LPCWSTR launch_via_steam_posix() {
const char* argv[] = { "/bin/sh", "-c", "\"steam -applaunch 975370\"", NULL };
// does not return on success
_execv(argv[0], argv);
return L"Could not launch Dwarf Fortress";
}
static LPCWSTR launch_via_steam_windows() {
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
WCHAR steamPath[1024];
DWORD datasize = 1024;
LONG retCode = RegGetValueW(HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam",
L"SteamExe", RRF_RT_REG_SZ, NULL, &steamPath, &datasize);
if (retCode != ERROR_SUCCESS)
return L"Could not find Steam client executable";
WCHAR commandLine[1024] = L"steam.exe -applaunch 975370";
if (CreateProcessW(steamPath, commandLine,
NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) == 0)
return L"Could not launch Dwarf Fortress";
return NULL;
}
// this method doesn't properly attribute Steam playtime metrics to DF,
// but that's better than not having DF start at all.
static BOOL launch_direct() {
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
return CreateProcessW(L"Dwarf Fortress.exe",
NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
}
int WINAPI wWinMain(HINSTANCE hi, HINSTANCE hpi, PWSTR cmd, int ns) {
LPCWSTR err = is_running_on_wine() ? launch_via_steam_posix() : launch_via_steam_windows();
if (err && !launch_direct()) {
MessageBoxW(NULL, err, NULL, 0);
exit(1);
}
exit(0);
}

@ -0,0 +1,223 @@
#include <process.h>
#include <windows.h>
#include <TlHelp32.h>
#include "steam_api.h"
#include <string>
const uint32 DFHACK_STEAM_APPID = 2346660;
const uint32 DF_STEAM_APPID = 975370;
static BOOL is_running_on_wine() {
typedef const char* (CDECL wine_get_version)(void);
static wine_get_version* pwine_get_version;
HMODULE hntdll = GetModuleHandle("ntdll.dll");
if(!hntdll)
return FALSE;
pwine_get_version = (wine_get_version*) GetProcAddress(hntdll, "wine_get_version");
return !!pwine_get_version;
}
static LPCWSTR launch_via_steam_posix() {
const char* argv[] = { "/bin/sh", "-c", "\"steam -applaunch 975370\"", NULL };
// does not return on success
_execv(argv[0], argv);
return L"Could not launch Dwarf Fortress";
}
static LPCWSTR launch_via_steam_windows() {
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
WCHAR steamPath[1024] = L"";
DWORD datasize = 1024;
LONG retCode = RegGetValueW(HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam",
L"SteamExe", RRF_RT_REG_SZ, NULL, &steamPath, &datasize);
if (retCode != ERROR_SUCCESS)
return L"Could not find Steam client executable";
WCHAR commandLine[1024] = L"steam.exe -applaunch 975370";
BOOL res = CreateProcessW(steamPath, commandLine,
NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
if (res)
{
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return NULL;
}
else
{
return L"Could not launch Dwarf Fortress";
}
}
static LPCWSTR launch_direct() {
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
BOOL res = CreateProcessW(L"Dwarf Fortress.exe",
NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
if (res)
{
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return NULL;
}
return L"Could not launch via non-steam fallback method";
}
DWORD findDwarfFortressProcess()
{
PROCESSENTRY32W entry;
entry.dwSize = sizeof(PROCESSENTRY32W);
const auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (!Process32FirstW(snapshot, &entry))
{
CloseHandle(snapshot);
return -1;
}
do {
std::wstring executableName(entry.szExeFile);
if (executableName == L"Dwarf Fortress.exe")
{
CloseHandle(snapshot);
return entry.th32ProcessID;
}
} while (Process32NextW(snapshot, &entry));
CloseHandle(snapshot);
return -1;
}
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nShowCmd) {
// initialize steam context
if (SteamAPI_RestartAppIfNecessary(DFHACK_STEAM_APPID))
{
exit(0);
}
if (!SteamAPI_Init())
{
// could not initialize steam context, attempt fallback launch
LPCWSTR err = launch_direct();
if (err != NULL)
{
MessageBoxW(NULL, err, NULL, 0);
exit(1);
}
exit(0);
}
bool wine = is_running_on_wine();
if (wine)
{
// attempt launch via steam client
LPCWSTR err = launch_via_steam_posix();
if (err != NULL)
// steam client launch failed, attempt fallback launch
err = launch_direct();
if (err != NULL)
{
MessageBoxW(NULL, err, NULL, 0);
exit(1);
}
exit(0);
}
// steam detected and not running in wine
bool df_installed = SteamApps()->BIsAppInstalled(DF_STEAM_APPID);
if (!df_installed)
{
// Steam DF is not installed. Assume DF is installed in same directory as DFHack and do a fallback launch
LPCWSTR err = launch_direct();
if (err != NULL)
{
MessageBoxW(NULL, err, NULL, 0);
exit(1);
}
exit(0);
}
// obtain DF app path
char buf[2048] = "";
int b1 = SteamApps()->GetAppInstallDir(DFHACK_STEAM_APPID, (char*)&buf, 2048);
std::string dfhack_install_folder = (b1 != -1) ? std::string(buf) : "";
int b2 = SteamApps()->GetAppInstallDir(DF_STEAM_APPID, (char*)&buf, 2048);
std::string df_install_folder = (b2 != -1) ? std::string(buf) : "";
if (df_install_folder != dfhack_install_folder)
{
// DF and DFHack are not installed in the same library
MessageBoxW(NULL, L"DFHack and Dwarf Fortress must be installed in the same Steam library.\nAborting.", NULL, 0);
exit(1);
}
DWORD df_pid = findDwarfFortressProcess();
if (df_pid == -1)
{
LPCWSTR err = launch_via_steam_windows();
if (err != NULL)
{
MessageBoxW(NULL, err, NULL, 0);
exit(1);
}
int counter = 0;
do {
if (counter++ > 60)
{
MessageBoxW(NULL, L"Dwarf Fortress took too long to launch, aborting", NULL, 0);
exit(1);
}
Sleep(1000);
df_pid = findDwarfFortressProcess();
} while (df_pid == -1);
}
HANDLE hDF = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, df_pid);
// in the future open an IPC connection so that we can proxy SteamAPI calls for the DFSteam module
// this will eventuallyh need to become a loop with a WaitForMultipleObjects call
WaitForSingleObject(hDF, INFINITE);
CloseHandle(hDF);
exit(0);
}

@ -162,7 +162,6 @@ dfhack_plugin(strangemood strangemood.cpp)
dfhack_plugin(tailor tailor.cpp LINK_LIBRARIES lua)
dfhack_plugin(tiletypes tiletypes.cpp Brushes.h LINK_LIBRARIES lua)
#dfhack_plugin(title-folder title-folder.cpp)
#dfhack_plugin(title-version title-version.cpp)
#dfhack_plugin(trackstop trackstop.cpp)
#dfhack_plugin(tubefill tubefill.cpp)
#add_subdirectory(tweak)

@ -6,11 +6,6 @@ if(UNIX)
endif()
endif()
include_directories("${dfhack_SOURCE_DIR}/library/include")
include_directories("${dfhack_SOURCE_DIR}/library/proto")
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/proto")
include_directories("${dfhack_SOURCE_DIR}/library/depends/xgetopt")
macro(car var)
set(${var} ${ARGV1})
endmacro()
@ -123,6 +118,11 @@ macro(dfhack_plugin)
add_library(${PLUGIN_NAME} MODULE ${PLUGIN_SOURCES})
ide_folder(${PLUGIN_NAME} "Plugins")
target_include_directories(${PLUGIN_NAME} PRIVATE "${dfhack_SOURCE_DIR}/library/include")
target_include_directories(${PLUGIN_NAME} PRIVATE "${dfhack_SOURCE_DIR}/library/proto")
target_include_directories(${PLUGIN_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/proto")
target_include_directories(${PLUGIN_NAME} PRIVATE "${dfhack_SOURCE_DIR}/library/depends/xgetopt")
if(NUM_PROTO)
add_dependencies(${PLUGIN_NAME} generate_proto_${PLUGIN_NAME})
target_link_libraries(${PLUGIN_NAME} dfhack protobuf-lite dfhack-version ${PLUGIN_LINK_LIBRARIES})

@ -565,7 +565,7 @@ static void find_needed_clothing_items()
if (!item)
{
WARN(cycle).print("autoclothing: Invalid inventory item ID: %d\n", ownedItem);
DEBUG(cycle).print("autoclothing: Invalid inventory item ID: %d\n", ownedItem);
continue;
}
@ -818,7 +818,7 @@ static void generate_report(color_ostream& out)
auto item = Items::findItemByID(itemId);
if (!item)
{
WARN(cycle,out).print("autoclothing: Invalid inventory item ID: %d\n", itemId);
DEBUG(cycle, out).print("autoclothing: Invalid inventory item ID: %d\n", itemId);
continue;
}
if (item->getWear() >= 1)

@ -150,9 +150,11 @@ static const df::dfhack_material_category stone_cat(df::dfhack_material_category
static const df::dfhack_material_category wood_cat(df::dfhack_material_category::mask_wood);
static const df::dfhack_material_category metal_cat(df::dfhack_material_category::mask_metal);
static const df::dfhack_material_category glass_cat(df::dfhack_material_category::mask_glass);
static const df::dfhack_material_category gem_cat(df::dfhack_material_category::mask_gem);
static const df::dfhack_material_category clay_cat(df::dfhack_material_category::mask_clay);
static const df::dfhack_material_category cloth_cat(df::dfhack_material_category::mask_cloth);
static const df::dfhack_material_category silk_cat(df::dfhack_material_category::mask_silk);
static const df::dfhack_material_category yarn_cat(df::dfhack_material_category::mask_yarn);
static void cache_matched(int16_t type, int32_t index) {
MaterialInfo mi;
@ -169,6 +171,9 @@ static void cache_matched(int16_t type, int32_t index) {
} else if (mi.matches(glass_cat)) {
DEBUG(status).print("cached glass material: %s (%d, %d)\n", mi.toString().c_str(), type, index);
mat_cache.emplace(mi.toString(), std::make_pair(mi, "glass"));
} else if (mi.matches(gem_cat)) {
DEBUG(status).print("cached gem material: %s (%d, %d)\n", mi.toString().c_str(), type, index);
mat_cache.emplace(mi.toString(), std::make_pair(mi, "gem"));
} else if (mi.matches(clay_cat)) {
DEBUG(status).print("cached clay material: %s (%d, %d)\n", mi.toString().c_str(), type, index);
mat_cache.emplace(mi.toString(), std::make_pair(mi, "clay"));
@ -178,6 +183,9 @@ static void cache_matched(int16_t type, int32_t index) {
} else if (mi.matches(silk_cat)) {
DEBUG(status).print("cached silk material: %s (%d, %d)\n", mi.toString().c_str(), type, index);
mat_cache.emplace(mi.toString(), std::make_pair(mi, "silk"));
} else if (mi.matches(yarn_cat)) {
DEBUG(status).print("cached yarn material: %s (%d, %d)\n", mi.toString().c_str(), type, index);
mat_cache.emplace(mi.toString(), std::make_pair(mi, "yarn"));
}
else
TRACE(status).print("not matched: %s\n", mi.toString().c_str());
@ -204,6 +212,7 @@ static void load_material_cache() {
load_organic_material_cache(df::organic_mat_category::Wood);
load_organic_material_cache(df::organic_mat_category::PlantFiber);
load_organic_material_cache(df::organic_mat_category::Silk);
load_organic_material_cache(df::organic_mat_category::Yarn);
}
static HeatSafety get_heat_safety_filter(const BuildingTypeKey &key) {
@ -800,12 +809,16 @@ static int setMaterialMaskFilter(lua_State *L) {
mask |= metal_cat.whole;
else if (cat == "glass")
mask |= glass_cat.whole;
else if (cat == "gem")
mask |= gem_cat.whole;
else if (cat == "clay")
mask |= clay_cat.whole;
else if (cat == "cloth")
mask |= cloth_cat.whole;
else if (cat == "silk")
mask |= silk_cat.whole;
else if (cat == "yarn")
mask |= yarn_cat.whole;
}
DEBUG(status,*out).print(
"setting material mask filter for building_type=%d subtype=%d custom=%d index=%d to %x\n",
@ -850,9 +863,11 @@ static int getMaterialMaskFilter(lua_State *L) {
ret.emplace("wood", !bits || bits & wood_cat.whole);
ret.emplace("metal", !bits || bits & metal_cat.whole);
ret.emplace("glass", !bits || bits & glass_cat.whole);
ret.emplace("gem", !bits || bits & gem_cat.whole);
ret.emplace("clay", !bits || bits & clay_cat.whole);
ret.emplace("cloth", !bits || bits & cloth_cat.whole);
ret.emplace("silk", !bits || bits & silk_cat.whole);
ret.emplace("yarn", !bits || bits & yarn_cat.whole);
Lua::Push(L, ret);
return 1;
}
@ -897,12 +912,16 @@ static int setMaterialFilter(lua_State *L) {
mask.whole |= metal_cat.whole;
else if (mat.matches(glass_cat))
mask.whole |= glass_cat.whole;
else if (mat.matches(gem_cat))
mask.whole |= gem_cat.whole;
else if (mat.matches(clay_cat))
mask.whole |= clay_cat.whole;
else if (mat.matches(cloth_cat))
mask.whole |= cloth_cat.whole;
else if (mat.matches(silk_cat))
mask.whole |= silk_cat.whole;
else if (mat.matches(yarn_cat))
mask.whole |= yarn_cat.whole;
}
filter.setMaterialMask(mask.whole);
get_item_filters(*out, key).setItemFilter(*out, filter, index);

@ -113,14 +113,10 @@ public:
case job_type::CarveUpDownStaircase:
td.bits.dig = tile_dig_designation::UpDownStair;
break;
case job_type::DetailWall:
case job_type::DetailFloor: {
df::tiletype tt = map.tiletypeAt(job->pos);
if (tileSpecial(tt) != df::tiletype_special::SMOOTH) {
td.bits.smooth = 1;
}
case job_type::SmoothWall:
case job_type::SmoothFloor:
td.bits.smooth = 1;
break;
}
case job_type::CarveTrack:
to.bits.carve_track_north = (job->item_category.whole >> 18) & 1;
to.bits.carve_track_south = (job->item_category.whole >> 19) & 1;

@ -49,7 +49,22 @@ static int cleanupHotkeys(lua_State *) {
return 0;
}
static void add_binding_if_valid(const string &sym, const string &cmdline, df::viewscreen *screen, bool filtermenu) {
static bool should_hide_armok(color_ostream &out, const string &cmdline) {
bool should_hide = false;
auto L = Lua::Core::State;
Lua::StackUnwinder top(L);
Lua::CallLuaModuleFunction(out, L, "plugins.hotkeys", "should_hide_armok", 1, 1,
[&](lua_State *L){
Lua::Push(L, cmdline);
}, [&](lua_State *L){
should_hide = lua_toboolean(L, -1);
});
return should_hide;
}
static void add_binding_if_valid(color_ostream &out, const string &sym, const string &cmdline, df::viewscreen *screen, bool filtermenu) {
if (!can_invoke(cmdline, screen))
return;
@ -59,6 +74,11 @@ static void add_binding_if_valid(const string &sym, const string &cmdline, df::v
return;
}
if (should_hide_armok(out, cmdline)) {
DEBUG(log).print("filtering out armok keybinding\n");
return;
}
current_bindings[sym] = cmdline;
sorted_keys.push_back(sym);
string keyspec = sym + "@" + MENU_SCREEN_FOCUS_STRING;
@ -67,7 +87,7 @@ static void add_binding_if_valid(const string &sym, const string &cmdline, df::v
Core::getInstance().AddKeyBinding(keyspec, binding);
}
static void find_active_keybindings(df::viewscreen *screen, bool filtermenu) {
static void find_active_keybindings(color_ostream &out, df::viewscreen *screen, bool filtermenu) {
DEBUG(log).print("scanning for active keybindings\n");
if (valid)
cleanupHotkeys(NULL);
@ -103,7 +123,7 @@ static void find_active_keybindings(df::viewscreen *screen, bool filtermenu) {
string::size_type colon_pos = invoke_cmd->find(":");
// colons at location 0 are for commands like ":lua"
if (colon_pos == string::npos || colon_pos == 0) {
add_binding_if_valid(sym, *invoke_cmd, screen, filtermenu);
add_binding_if_valid(out, sym, *invoke_cmd, screen, filtermenu);
}
else {
vector<string> tokens;
@ -111,7 +131,7 @@ static void find_active_keybindings(df::viewscreen *screen, bool filtermenu) {
string focus = tokens[0].substr(1);
if(Gui::matchFocusString(focus)) {
auto cmdline = trim(tokens[1]);
add_binding_if_valid(sym, cmdline, screen, filtermenu);
add_binding_if_valid(out, sym, cmdline, screen, filtermenu);
}
}
}
@ -124,7 +144,10 @@ static void find_active_keybindings(df::viewscreen *screen, bool filtermenu) {
}
static int getHotkeys(lua_State *L) {
find_active_keybindings(Gui::getCurViewscreen(true), true);
color_ostream *out = Lua::GetOutput(L);
if (!out)
out = &Core::getInstance().getConsole();
find_active_keybindings(*out, Gui::getCurViewscreen(true), true);
Lua::PushVector(L, sorted_keys);
Lua::Push(L, current_bindings);
return 2;
@ -140,7 +163,7 @@ static void list(color_ostream &out) {
DEBUG(log).print("listing active hotkeys\n");
bool was_valid = valid;
if (!valid)
find_active_keybindings(Gui::getCurViewscreen(true), false);
find_active_keybindings(out, Gui::getCurViewscreen(true), false);
out.print("Valid keybindings for the current focus:\n %s\n",
join_strings("\n", Gui::getCurFocus(true)).c_str());
@ -176,6 +199,8 @@ static command_result hotkeys_cmd(color_ostream &out, vector <string> & paramete
return Core::getInstance().runCommand(out, INVOKE_MENU_COMMAND );
}
CoreSuspender guard;
if (parameters[0] == "list") {
list(out);
return CR_OK;
@ -185,8 +210,6 @@ static command_result hotkeys_cmd(color_ostream &out, vector <string> & paramete
if (parameters.size() != 2 || parameters[0] != "invoke")
return CR_WRONG_USAGE;
CoreSuspender guard;
int index = string_to_int(parameters[1], -1);
if (index < 0)
return CR_WRONG_USAGE;

@ -10,7 +10,7 @@ AutolaborOverlay.ATTRS{
default_enabled=true,
viewscreens='dwarfmode/Info/LABOR',
frame={w=29, h=5},
frame_style=gui.MEDIUM_FRAME,
frame_style=gui.THIN_FRAME,
frame_background=gui.CLEAR_PEN,
}
@ -18,9 +18,20 @@ function AutolaborOverlay:init()
self:addviews{
widgets.Label{
frame={t=0, l=0},
text_pen=COLOR_RED,
text_pen=COLOR_LIGHTRED,
text='DFHack autolabor is active!',
visible=isEnabled,
},
widgets.Label{
frame={t=0, l=0},
text_pen=COLOR_LIGHTRED,
text='Dwarf Therapist is active!',
visible=function() return not isEnabled() end,
},
widgets.Label{
frame={t=1, l=0},
text_pen=COLOR_WHITE,
text={
'DFHack autolabor is active!', NEWLINE,
'Any changes made on this', NEWLINE,
'screen will have no effect.'
},
@ -29,7 +40,7 @@ function AutolaborOverlay:init()
end
function AutolaborOverlay:render(dc)
if not isEnabled() then return false end
if df.global.game_extra.external_flag ~= 1 then return end
AutolaborOverlay.super.render(self, dc)
end

@ -111,6 +111,8 @@ function get_desc(filter)
desc = 'Ballista part'
elseif desc == 'Catapultpart' then
desc = 'Catapult part'
elseif desc == 'Smallgem' then
desc = 'Small, cut gem'
end
return desc

@ -12,143 +12,6 @@ local function get_cur_filters()
uibs.building_subtype, uibs.custom_type)
end
--------------------------------
-- Slider
--
Slider = defclass(Slider, widgets.Widget)
Slider.ATTRS{
num_stops=DEFAULT_NIL,
get_left_idx_fn=DEFAULT_NIL,
get_right_idx_fn=DEFAULT_NIL,
on_left_change=DEFAULT_NIL,
on_right_change=DEFAULT_NIL,
}
function Slider:preinit(init_table)
init_table.frame = init_table.frame or {}
init_table.frame.h = init_table.frame.h or 1
end
function Slider:init()
if self.num_stops < 2 then error('too few Slider stops') end
self.is_dragging_target = nil -- 'left', 'right', or 'both'
self.is_dragging_idx = nil -- offset from leftmost dragged tile
end
local function slider_get_width_per_idx(self)
return math.max(5, (self.frame_body.width-7) // (self.num_stops-1))
end
function Slider:onInput(keys)
if not keys._MOUSE_L_DOWN then return false end
local x = self:getMousePos()
if not x then return false end
local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn()
local width_per_idx = slider_get_width_per_idx(self)
local left_pos = width_per_idx*(left_idx-1)
local right_pos = width_per_idx*(right_idx-1) + 4
if x < left_pos then
self.on_left_change(self.get_left_idx_fn() - 1)
elseif x < left_pos+3 then
self.is_dragging_target = 'left'
self.is_dragging_idx = x - left_pos
elseif x < right_pos then
self.is_dragging_target = 'both'
self.is_dragging_idx = x - left_pos
elseif x < right_pos+3 then
self.is_dragging_target = 'right'
self.is_dragging_idx = x - right_pos
else
self.on_right_change(self.get_right_idx_fn() + 1)
end
return true
end
local function slider_do_drag(self, width_per_idx)
local x = self.frame_body:localXY(dfhack.screen.getMousePos())
local cur_pos = x - self.is_dragging_idx
cur_pos = math.max(0, cur_pos)
cur_pos = math.min(width_per_idx*(self.num_stops-1)+7, cur_pos)
local offset = self.is_dragging_target == 'right' and -2 or 1
local new_idx = math.max(0, cur_pos+offset)//width_per_idx + 1
local new_left_idx, new_right_idx
if self.is_dragging_target == 'right' then
new_right_idx = new_idx
else
new_left_idx = new_idx
if self.is_dragging_target == 'both' then
new_right_idx = new_left_idx + self.get_right_idx_fn() - self.get_left_idx_fn()
if new_right_idx > self.num_stops then
return
end
end
end
if new_left_idx and new_left_idx ~= self.get_left_idx_fn() then
self.on_left_change(new_left_idx)
end
if new_right_idx and new_right_idx ~= self.get_right_idx_fn() then
self.on_right_change(new_right_idx)
end
end
local SLIDER_LEFT_END = to_pen{ch=198, fg=COLOR_GREY, bg=COLOR_BLACK}
local SLIDER_TRACK = to_pen{ch=205, fg=COLOR_GREY, bg=COLOR_BLACK}
local SLIDER_TRACK_SELECTED = to_pen{ch=205, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK}
local SLIDER_TRACK_STOP = to_pen{ch=216, fg=COLOR_GREY, bg=COLOR_BLACK}
local SLIDER_TRACK_STOP_SELECTED = to_pen{ch=216, fg=COLOR_LIGHTGREEN, bg=COLOR_BLACK}
local SLIDER_RIGHT_END = to_pen{ch=181, fg=COLOR_GREY, bg=COLOR_BLACK}
local SLIDER_TAB_LEFT = to_pen{ch=60, fg=COLOR_BLACK, bg=COLOR_YELLOW}
local SLIDER_TAB_CENTER = to_pen{ch=9, fg=COLOR_BLACK, bg=COLOR_YELLOW}
local SLIDER_TAB_RIGHT = to_pen{ch=62, fg=COLOR_BLACK, bg=COLOR_YELLOW}
function Slider:onRenderBody(dc, rect)
local left_idx, right_idx = self.get_left_idx_fn(), self.get_right_idx_fn()
local width_per_idx = slider_get_width_per_idx(self)
-- draw track
dc:seek(1,0)
dc:char(nil, SLIDER_LEFT_END)
dc:char(nil, SLIDER_TRACK)
for stop_idx=1,self.num_stops-1 do
local track_stop_pen = SLIDER_TRACK_STOP_SELECTED
local track_pen = SLIDER_TRACK_SELECTED
if left_idx > stop_idx or right_idx < stop_idx then
track_stop_pen = SLIDER_TRACK_STOP
track_pen = SLIDER_TRACK
elseif right_idx == stop_idx then
track_pen = SLIDER_TRACK
end
dc:char(nil, track_stop_pen)
for i=2,width_per_idx do
dc:char(nil, track_pen)
end
end
if right_idx >= self.num_stops then
dc:char(nil, SLIDER_TRACK_STOP_SELECTED)
else
dc:char(nil, SLIDER_TRACK_STOP)
end
dc:char(nil, SLIDER_TRACK)
dc:char(nil, SLIDER_RIGHT_END)
-- draw tabs
dc:seek(width_per_idx*(left_idx-1))
dc:char(nil, SLIDER_TAB_LEFT)
dc:char(nil, SLIDER_TAB_CENTER)
dc:char(nil, SLIDER_TAB_RIGHT)
dc:seek(width_per_idx*(right_idx-1)+4)
dc:char(nil, SLIDER_TAB_LEFT)
dc:char(nil, SLIDER_TAB_CENTER)
dc:char(nil, SLIDER_TAB_RIGHT)
-- manage dragging
if self.is_dragging_target then
slider_do_drag(self, width_per_idx)
end
if df.global.enabler.mouse_lbut == 0 then
self.is_dragging_target = nil
self.is_dragging_idx = nil
end
end
--------------------------------
-- QualityAndMaterialsPage
--
@ -328,7 +191,7 @@ function QualityAndMaterialsPage:init()
enabled=enable_item_quality,
on_change=function(val) self:set_max_quality(val+1) end,
},
Slider{
widgets.RangeSlider{
frame={l=0, t=6},
num_stops=7,
get_left_idx_fn=function()
@ -452,9 +315,11 @@ function QualityAndMaterialsPage:refresh()
make_cat_choice('Wood', 'wood', 'CUSTOM_SHIFT_O', cats),
make_cat_choice('Metal', 'metal', 'CUSTOM_SHIFT_M', cats),
make_cat_choice('Glass', 'glass', 'CUSTOM_SHIFT_G', cats),
make_cat_choice('Gem', 'gem', 'CUSTOM_SHIFT_E', cats),
make_cat_choice('Clay', 'clay', 'CUSTOM_SHIFT_C', cats),
make_cat_choice('Cloth', 'cloth', 'CUSTOM_SHIFT_L', cats),
make_cat_choice('Silk', 'silk', 'CUSTOM_SHIFT_K', cats),
make_cat_choice('Yarn', 'yarn', 'CUSTOM_SHIFT_Y', cats),
}
self.subviews.materials_categories:setChoices(category_choices)

@ -4,12 +4,15 @@ local itemselection = require('plugins.buildingplan.itemselection')
local filterselection = require('plugins.buildingplan.filterselection')
local gui = require('gui')
local guidm = require('gui.dwarfmode')
local json = require('json')
local overlay = require('plugins.overlay')
local pens = require('plugins.buildingplan.pens')
local utils = require('utils')
local widgets = require('gui.widgets')
require('dfhack.buildings')
config = config or json.open('dfhack-config/buildingplan.json')
local uibs = df.global.buildreq
reset_counts_flag = false
@ -31,9 +34,10 @@ local function get_selection_size_limits()
or btype == df.building_type.RoadPaved
or btype == df.building_type.RoadDirt then
return {w=31, h=31}
elseif btype == df.building_type.AxleHorizontal
or btype == df.building_type.Rollers then
elseif btype == df.building_type.AxleHorizontal then
return uibs.direction == 1 and {w=1, h=31} or {w=31, h=1}
elseif btype == df.building_type.Rollers then
return (uibs.direction == 1 or uibs.direction == 3) and {w=31, h=1} or {w=1, h=31}
end
end
@ -336,14 +340,14 @@ PlannerOverlay.ATTRS{
function PlannerOverlay:init()
self.selected = 1
self.minimized = false
self.state = ensure_key(config.data, 'planner')
local main_panel = widgets.Panel{
view_id='main',
frame={t=1, l=0, r=0, h=14},
frame_style=gui.INTERIOR_MEDIUM_FRAME,
frame_background=gui.CLEAR_PEN,
visible=function() return not self.minimized end,
visible=self:callback('is_not_minimized'),
}
local minimized_panel = widgets.Panel{
@ -355,8 +359,8 @@ function PlannerOverlay:init()
{text=' show Planner ', pen=pens.MINI_TEXT_PEN, hpen=pens.MINI_TEXT_HPEN},
{text='['..string.char(31)..']', pen=pens.MINI_BUTT_PEN, hpen=pens.MINI_BUTT_HPEN},
},
visible=function() return self.minimized end,
on_click=function() self.minimized = not self.minimized end,
visible=self:callback('is_minimized'),
on_click=self:callback('toggle_minimized'),
},
widgets.Label{
frame={t=0, r=0, h=1},
@ -364,8 +368,8 @@ function PlannerOverlay:init()
{text=' hide Planner ', pen=pens.MINI_TEXT_PEN, hpen=pens.MINI_TEXT_HPEN},
{text='['..string.char(30)..']', pen=pens.MINI_BUTT_PEN, hpen=pens.MINI_BUTT_HPEN},
},
visible=function() return not self.minimized end,
on_click=function() self.minimized = not self.minimized end,
visible=self:callback('is_not_minimized'),
on_click=self:callback('toggle_minimized'),
},
},
}
@ -554,7 +558,7 @@ function PlannerOverlay:init()
view_id='divider',
frame={t=10, l=0, r=0, h=1},
on_render=self:callback('draw_divider_h'),
visible=function() return not self.minimized end,
visible=self:callback('is_not_minimized'),
}
local error_panel = widgets.ResizingPanel{
@ -562,7 +566,7 @@ function PlannerOverlay:init()
frame={t=15, l=0, r=0},
frame_style=gui.BOLD_FRAME,
frame_background=gui.CLEAR_PEN,
visible=function() return not self.minimized end,
visible=self:callback('is_not_minimized'),
}
error_panel:addviews{
@ -609,7 +613,7 @@ function PlannerOverlay:init()
frame={t=0, l=1, w=37, h=1},
frame_inset=0,
frame_background=gui.CLEAR_PEN,
visible=function() return not self.minimized end,
visible=self:callback('is_not_minimized'),
subviews={
prev_next_selector,
},
@ -624,6 +628,19 @@ function PlannerOverlay:init()
}
end
function PlannerOverlay:is_minimized()
return self.state.minimized
end
function PlannerOverlay:is_not_minimized()
return not self.state.minimized
end
function PlannerOverlay:toggle_minimized()
self.state.minimized = not self.state.minimized
config:write()
end
function PlannerOverlay:draw_divider_h(dc)
local x2 = dc.width -1
for x=0,x2 do
@ -729,20 +746,19 @@ function PlannerOverlay:onInput(keys)
return true
end
self.selected = 1
self.minimized = false
self.subviews.hollow:setOption(false)
self:reset()
reset_counts_flag = true
return false
end
if keys.CUSTOM_ALT_M then
self.minimized = not self.minimized
self:toggle_minimized()
return true
end
if PlannerOverlay.super.onInput(self, keys) then
return true
end
if self.minimized then return false end
if self:is_minimized() then return false end
if keys._MOUSE_L_DOWN then
if is_over_options_panel() then return false end
local detect_rect = copyall(self.frame_rect)
@ -838,7 +854,7 @@ function PlannerOverlay:onRenderFrame(dc, rect)
uibs.building_type, uibs.building_subtype, uibs.custom_type))
end
if self.minimized then return end
if self:is_minimized() then return end
local bounds = get_selected_bounds(self.saved_selection_pos, self.saved_pos)
if not bounds then return end

@ -5,6 +5,19 @@ local helpdb = require('helpdb')
local overlay = require('plugins.overlay')
local widgets = require('gui.widgets')
local function get_command(cmdline)
local first_word = cmdline:trim():split(' +')[1]
if first_word:startswith(':') then first_word = first_word:sub(2) end
return first_word
end
function should_hide_armok(cmdline)
local command = get_command(cmdline)
return dfhack.getHideArmokTools() and
helpdb.is_entry(command) and
helpdb.get_entry_tags(command).armok
end
-- ----------------- --
-- HotspotMenuWidget --
-- ----------------- --
@ -26,7 +39,7 @@ HotspotMenuWidget.ATTRS{
-- 'new_region', -- conflicts with vanilla panel layouts
'savegame',
'setupdwarfgame',
'title',
'title/Default',
'update_region',
'world'
},
@ -232,10 +245,9 @@ end
function Menu:onSelect(_, choice)
if not choice or #self.subviews == 0 then return end
local first_word = choice.command:trim():split(' +')[1]
if first_word:startswith(':') then first_word = first_word:sub(2) end
self.subviews.help.text_to_wrap = helpdb.is_entry(first_word) and
helpdb.get_entry_short_help(first_word) or 'Command not found'
local command = get_command(choice.command)
self.subviews.help.text_to_wrap = helpdb.is_entry(command) and
helpdb.get_entry_short_help(command) or 'Command not found'
self.subviews.help_panel:updateLayout()
end

@ -562,4 +562,42 @@ function OverlayWidget:init()
self.frame.h = self.frame.h or 1
end
-- ------------------- --
-- TitleVersionOverlay --
-- ------------------- --
TitleVersionOverlay = defclass(TitleVersionOverlay, OverlayWidget)
TitleVersionOverlay.ATTRS{
default_pos={x=50, y=-2},
default_enabled=true,
viewscreens='title/Default',
frame={w=35, h=3},
}
function TitleVersionOverlay:init()
local text = {}
table.insert(text, 'DFHack ' .. dfhack.getDFHackVersion() ..
(dfhack.isRelease() and '' or (' (git: %s)'):format(dfhack.getGitCommit(true))))
if #dfhack.getDFHackBuildID() > 0 then
table.insert(text, NEWLINE)
table.insert(text, 'Build ID: ' .. dfhack.getDFHackBuildID())
end
if dfhack.isPrerelease() then
table.insert(text, NEWLINE)
table.insert(text, {text='Pre-release build', pen=COLOR_LIGHTRED})
end
self:addviews{
widgets.Label{
frame={t=0, l=0},
text=text,
text_pen=COLOR_WHITE,
},
}
end
OVERLAY_WIDGETS = {
title_version = TitleVersionOverlay,
}
return _ENV

@ -24,23 +24,9 @@ set(PROJECT_PROTO
ui_sidebar_mode
)
set(PLUGIN_PROTOS)
foreach(pbuf ${PROJECT_PROTO})
list(APPEND PLUGIN_PROTOS ${CMAKE_CURRENT_SOURCE_DIR}/../proto/${pbuf}.proto)
endforeach()
string(REPLACE ".proto" ".pb.cc" PLUGIN_PROTO_SRCS "${PLUGIN_PROTOS}")
string(REPLACE ".proto" ".pb.h" PLUGIN_PROTO_HDRS "${PLUGIN_PROTOS}")
set_source_files_properties(${PLUGIN_PROTO_SRCS} ${PLUGIN_PROTO_HDRS} PROPERTIES GENERATED TRUE)
set_source_files_properties( ${PROJECT_HDRS} ${PLUGIN_PROTO_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE)
# mash them together (headers are marked as headers and nothing will try to compile them)
list(APPEND PROJECT_SRCS ${PROJECT_HDRS} ${PLUGIN_PROTOS} ${PLUGIN_PROTO_SRCS} ${PLUGIN_PROTO_HDRS})
if(UNIX AND NOT APPLE)
set(PROJECT_LIBS ${PROJECT_LIBS} SDL)
endif()
# this makes sure all the stuff is put in proper places and linked to dfhack
dfhack_plugin(RemoteFortressReader ${PROJECT_SRCS} LINK_LIBRARIES protobuf-lite ${PROJECT_LIBS} COMPILE_FLAGS_MSVC "/FI\"Export.h\"" COMPILE_FLAGS_GCC "-include Export.h -Wno-misleading-indentation" )
dfhack_plugin(RemoteFortressReader ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS} PROTOBUFS ${PROJECT_PROTO})

@ -0,0 +1,3 @@
*.pb.cc
*.pb.cc.rule
*.pb.h

@ -1,109 +0,0 @@
#include <cmath>
#include <cstdio>
#include <cstring>
#include <stack>
#include <string>
#include <vector>
#include "Core.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h"
#include "modules/Gui.h"
#include "modules/Screen.h"
#include "VTableInterpose.h"
#include "DFHackVersion.h"
#include "df/graphic.h"
#include "df/viewscreen_optionst.h"
#include "df/viewscreen_titlest.h"
#include "uicommon.h"
using std::vector;
using std::string;
using namespace DFHack;
DFHACK_PLUGIN("title-version");
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
REQUIRE_GLOBAL(gps);
void draw_version(int start_x, int start_y) {
int x = start_x,
y = start_y;
OutputString(COLOR_WHITE, x, y, string("DFHack ") + DFHACK_VERSION);
if (!DFHACK_IS_RELEASE)
{
OutputString(COLOR_WHITE, x, y, " (dev)");
x = start_x; y++;
OutputString(COLOR_WHITE, x, y, "Git: ");
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)
{
x = start_x; y++;
OutputString(COLOR_LIGHTRED, x, y, "Pre-release build");
}
}
struct title_version_hook : df::viewscreen_titlest {
typedef df::viewscreen_titlest interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, render, ())
{
INTERPOSE_NEXT(render)();
if (!loading)
draw_version(0, 0);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(title_version_hook, render);
struct options_version_hook : df::viewscreen_optionst {
typedef df::viewscreen_optionst interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, render, ())
{
INTERPOSE_NEXT(render)();
if (!msg_quit && !in_retire_adv && !msg_peasant &&
!in_retire_dwf_abandon_adv && !in_abandon_dwf && !ending_game)
draw_version(2, gps->dimy - 6);
}
};
IMPLEMENT_VMETHOD_INTERPOSE(options_version_hook, render);
DFhackCExport command_result plugin_enable (color_ostream &out, bool enable)
{
if (!gps)
return CR_FAILURE;
if (enable != is_enabled)
{
if (!INTERPOSE_HOOK(title_version_hook, render).apply(enable) ||
!INTERPOSE_HOOK(options_version_hook, render).apply(enable))
return CR_FAILURE;
is_enabled = enable;
}
return CR_OK;
}
DFhackCExport command_result plugin_init (color_ostream &out, vector<PluginCommand> &commands)
{
return CR_OK;
}
DFhackCExport command_result plugin_shutdown (color_ostream &out)
{
INTERPOSE_HOOK(title_version_hook, render).remove();
INTERPOSE_HOOK(options_version_hook, render).remove();
return CR_OK;
}

@ -1 +1 @@
Subproject commit ec1a69788fd6329008672523b622fd8b390fea73
Subproject commit ad1998a0032ce50e90d05429a4178b668c0840ba

@ -1,14 +0,0 @@
#!/usr/bin/env python3
import os
import subprocess
import sys
script_name = os.path.basename(__file__)
new_script_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'ci', script_name)
sys.stderr.write('\nNote: travis/{script_name} is deprecated. Use ci/{script_name} instead.\n\n'.format(script_name=script_name))
sys.stderr.flush()
p = subprocess.run([sys.executable, new_script_path] + sys.argv[1:])
sys.exit(p.returncode)

@ -1,14 +0,0 @@
#!/usr/bin/env python3
import os
import subprocess
import sys
script_name = os.path.basename(__file__)
new_script_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'ci', script_name)
sys.stderr.write('\nNote: travis/{script_name} is deprecated. Use ci/{script_name} instead.\n\n'.format(script_name=script_name))
sys.stderr.flush()
p = subprocess.run([sys.executable, new_script_path] + sys.argv[1:])
sys.exit(p.returncode)

@ -1,14 +0,0 @@
#!/usr/bin/env python3
import os
import subprocess
import sys
script_name = os.path.basename(__file__)
new_script_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'ci', script_name)
sys.stderr.write('\nNote: travis/{script_name} is deprecated. Use ci/{script_name} instead.\n\n'.format(script_name=script_name))
sys.stderr.flush()
p = subprocess.run([sys.executable, new_script_path] + sys.argv[1:])
sys.exit(p.returncode)

@ -1,9 +0,0 @@
#!/bin/sh
script_name="$(basename "$0")"
new_script_path="$(dirname "$0")/../ci/${script_name}"
printf >&2 "\nNote: travis/%s is deprecated. Use ci/%s instead.\n\n" "${script_name}" "${script_name}"
"${new_script_path}" "$@"
exit $?

@ -1,9 +0,0 @@
#!/bin/sh
script_name="$(basename "$0")"
new_script_path="$(dirname "$0")/../ci/${script_name}"
printf >&2 "\nNote: travis/%s is deprecated. Use ci/%s instead.\n\n" "${script_name}" "${script_name}"
"${new_script_path}" "$@"
exit $?

@ -1,14 +0,0 @@
#!/usr/bin/env python3
import os
import subprocess
import sys
script_name = os.path.basename(__file__)
new_script_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'ci', script_name)
sys.stderr.write('\nNote: travis/{script_name} is deprecated. Use ci/{script_name} instead.\n\n'.format(script_name=script_name))
sys.stderr.flush()
p = subprocess.run([sys.executable, new_script_path] + sys.argv[1:])
sys.exit(p.returncode)

@ -1,14 +0,0 @@
#!/usr/bin/env python3
import os
import subprocess
import sys
script_name = os.path.basename(__file__)
new_script_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'ci', script_name)
sys.stderr.write('\nNote: travis/{script_name} is deprecated. Use ci/{script_name} instead.\n\n'.format(script_name=script_name))
sys.stderr.flush()
p = subprocess.run([sys.executable, new_script_path] + sys.argv[1:])
sys.exit(p.returncode)

@ -1,14 +0,0 @@
#!/usr/bin/env python3
import os
import subprocess
import sys
script_name = os.path.basename(__file__)
new_script_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'ci', script_name)
sys.stderr.write('\nNote: travis/{script_name} is deprecated. Use ci/{script_name} instead.\n\n'.format(script_name=script_name))
sys.stderr.flush()
p = subprocess.run([sys.executable, new_script_path] + sys.argv[1:])
sys.exit(p.returncode)

@ -1,14 +0,0 @@
#!/usr/bin/env python3
import os
import subprocess
import sys
script_name = os.path.basename(__file__)
new_script_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'ci', script_name)
sys.stderr.write('\nNote: travis/{script_name} is deprecated. Use ci/{script_name} instead.\n\n'.format(script_name=script_name))
sys.stderr.flush()
p = subprocess.run([sys.executable, new_script_path] + sys.argv[1:])
sys.exit(p.returncode)