diff --git a/NEWS.rst b/NEWS.rst index dd16cef7d..b7e80cf77 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -72,6 +72,8 @@ Fixes Misc Improvements ----------------- +- Documented all default keybindings (from :file:`dfhack.init-example`) in the + docs for the relevant commands; updates enforced by build system. - `lua` and `gui/gm-editor` now support the same aliases (``scr``, ``unit``, etc.) - `remotefortressreader`: Added support for diff --git a/conf.py b/conf.py index dc033016f..ed62ad5c6 100644 --- a/conf.py +++ b/conf.py @@ -15,15 +15,70 @@ serve to show the default. # pylint:disable=redefined-builtin -import fnmatch from io import open -from itertools import starmap import os import re import shlex # pylint:disable=unused-import import sys +# -- Support :dfhack-keybind:`command` ------------------------------------ +# this is a custom directive that pulls info from dfhack.init-example + +from docutils import nodes +from docutils.parsers.rst import roles + + +def get_keybinds(): + """Get the implemented keybinds, and return a dict of + {tool: [(full_command, keybinding, context), ...]}. + """ + with open('dfhack.init-example') as f: + lines = [l.replace('keybinding add', '').strip() for l in f.readlines() + if l.startswith('keybinding add')] + keybindings = dict() + for k in lines: + first, command = k.split(' ', 1) + bind, context = (first.split('@') + [''])[:2] + if ' ' not in command: + command = command.replace('"', '') + tool = command.split(' ')[0].replace('"', '') + keybindings[tool] = keybindings.get(tool, []) + [ + (command, bind.split('-'), context)] + return keybindings + +KEYBINDS = get_keybinds() + + +# pylint:disable=unused-argument,dangerous-default-value,too-many-arguments +def dfhack_keybind_role_func(role, rawtext, text, lineno, inliner, + options={}, content=[]): + """Custom role parser for DFHack default keybinds.""" + roles.set_classes(options) + if text not in KEYBINDS: + msg = inliner.reporter.error( + 'no keybinding for {} in dfhack.init-example'.format(text), + line=lineno) + prb = inliner.problematic(rawtext, rawtext, msg) + return [prb], [msg] + newnode = nodes.paragraph() + for cmd, key, ctx in KEYBINDS[text]: + n = nodes.paragraph() + newnode += n + n += nodes.strong('Keybinding: ', 'Keybinding: ') + for k in key: + n += nodes.inline(k, k, classes=['kbd']) + if cmd != text: + n += nodes.inline(' -> ', ' -> ') + n += nodes.literal(cmd, cmd, classes=['guilabel']) + if ctx: + n += nodes.inline(' in ', ' in ') + n += nodes.literal(ctx, ctx) + return [newnode], [] + + +roles.register_canonical_role('dfhack-keybind', dfhack_keybind_role_func) + # -- Autodoc for DFhack scripts ------------------------------------------- def doc_dir(dirname, files): @@ -46,28 +101,41 @@ def doc_dir(dirname, files): command = line +def doc_all_dirs(): + """Collect the commands and paths to include in our docs.""" + scripts = [] + for root, _, files in os.walk('scripts'): + scripts.extend(doc_dir(root, files)) + return tuple(scripts) + +DOC_ALL_DIRS = doc_all_dirs() + + def document_scripts(): """Autodoc for files with the magic script documentation marker strings. Returns a dict of script-kinds to lists of .rst include directives. """ - # First, we collect the commands and paths to include in our docs - scripts = [] - for root, _, files in os.walk('scripts'): - scripts.extend(doc_dir(root, files)) # Next we split by type and create include directives sorted by command kinds = {'base': [], 'devel': [], 'fix': [], 'gui': [], 'modtools': []} - for s in scripts: + for s in DOC_ALL_DIRS: k_fname = s[0].split('/', 1) if len(k_fname) == 1: kinds['base'].append(s) else: kinds[k_fname[0]].append(s) - template = '.. _{}:\n\n.. include:: /{}\n' +\ - ' :start-after: {}\n :end-before: {}\n' - return {key: '\n\n'.join(starmap(template.format, sorted(value))) + + def template(arg): + tmp = '.. _{}:\n\n.. include:: /{}\n' +\ + ' :start-after: {}\n :end-before: {}\n' + if arg[0] in KEYBINDS: + tmp += '\n:dfhack-keybind:`{}`\n'.format(arg[0]) + return tmp.format(*arg) + + return {key: '\n\n'.join(map(template, sorted(value))) for key, value in kinds.items()} + def write_script_docs(): """ Creates a file for eack kind of script (base/devel/fix/gui/modtools) @@ -97,10 +165,23 @@ def write_script_docs(): outfile.write(kinds[k]) -# Actually call the docs generator -write_script_docs() +def all_keybinds_documented(): + """Check that all keybindings are documented with the :dfhack-keybind: + directive somewhere.""" + configured_binds = set(KEYBINDS) + script_commands = set(i[0] for i in DOC_ALL_DIRS) + with open('./docs/Plugins.rst') as f: + plugin_binds = set(re.findall(':dfhack-keybind:`(.*?)`', f.read())) + undocumented_binds = configured_binds - script_commands - plugin_binds + if undocumented_binds: + raise ValueError('The following DFHack commands have undocumented' + 'keybindings: {}'.format(sorted(undocumented_binds))) +# Actually call the docs generator and run test +write_script_docs() +all_keybinds_documented() + # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. diff --git a/docs/History.rst b/docs/History.rst index b399fbbdd..e789d48c6 100644 --- a/docs/History.rst +++ b/docs/History.rst @@ -221,7 +221,8 @@ DFHack 0.40.11-r1 ================= Internals -- Plugins on OS X now use ``.plug.dylib` as an extension instead of ``.plug.so`` +--------- +- Plugins on OS X now use ``.plug.dylib`` as an extension instead of ``.plug.so`` Fixes ----- diff --git a/docs/Plugins.rst b/docs/Plugins.rst index 90533aa96..ac73bb58f 100644 --- a/docs/Plugins.rst +++ b/docs/Plugins.rst @@ -353,7 +353,8 @@ This plugin adds an option to the :kbd:`q` menu when `enabled `. command-prompt ============== An in-game DFHack terminal, where you can enter other commands. -Best used from a keybinding; by default :kbd:`Ctrl`:kbd:`Shift`:kbd:`P`. + +:dfhack-keybind:`command-prompt` Usage: ``command-prompt [entry]`` @@ -372,13 +373,11 @@ Otherwise somewhat similar to `gui/quickcmd`. hotkeys ======= Opens an in-game screen showing which DFHack keybindings are -active in the current context. +active in the current context. See also `hotkey-notes`. .. image:: images/hotkeys.png -Type ``hotkeys`` into the DFHack console to open the screen, -or bind the command to a globally active hotkey. The default -keybinding is :kbd:`Ctrl`:kbd:`F1`. See also `hotkey-notes`. +:dfhack-keybind:`hotkeys` .. _rb: @@ -407,7 +406,7 @@ military and social skills. .. image:: images/manipulator2.png -Press :kbd:`t` to toggle between Profession and Squad view. +Press :kbd:`t` to toggle between Profession, Squad, and Job views. .. image:: images/manipulator3.png @@ -442,6 +441,20 @@ The following mouse shortcuts are also available: Pressing :kbd:`Esc` normally returns to the unit screen, but :kbd:`Shift`:kbd:`Esc` would exit directly to the main dwarf mode screen. +Professions +----------- + +The manipulator plugin supports saving Professions: a named set of Labors labors that can be +quickly applied to one or multiple Dwarves. + +To save a Profession highlight a Dwarf and press :kbd:`P`. The Profession will be saved using +the Custom Profession Name of the Dwarf, or the default for that Dwarf if no Custom Profession +Name has been set. + +To apply a Profession either highlight a single Dwarf, or select multiple with :kbd:`x`, and press +:kbd:`p` to select the Profession to apply. All labors for the selected Dwarves will be reset to +the labors of the chosen Profession. + .. comment - the link target "search" is reserved for the Sphinx search page .. _search-plugin: @@ -659,12 +672,16 @@ Unit order examples:: The orderings are defined in ``hack/lua/plugins/sort/*.lua`` +:dfhack-keybind:`sort-units` + .. _stocks: stocks ====== Replaces the DF stocks screen with an improved version. +:dfhack-keybind:`stocks` + .. _stocksettings: .. _stockpiles: @@ -676,6 +693,7 @@ See `gui/stockpiles` for an in-game interface. :copystock: Copies the parameters of the currently highlighted stockpile to the custom stockpile settings and switches to custom stockpile placement mode, effectively allowing you to copy/paste stockpiles easily. + :dfhack-keybind:`copystock` :savestock: Saves the currently highlighted stockpile's settings to a file in your Dwarf Fortress folder. This file can be used to copy settings between game saves or @@ -874,7 +892,7 @@ Invoked as:: job-material -Intended to be used as a keybinding: +:dfhack-keybind:`job-material` * In :kbd:`q` mode, when a job is highlighted within a workshop or furnace, changes the material of the job. Only inorganic materials can be used @@ -887,6 +905,8 @@ job-duplicate In :kbd:`q` mode, when a job is highlighted within a workshop or furnace building, calling ``job-duplicate`` instantly duplicates the job. +:dfhack-keybind:`job-duplicate` + .. _autogems: autogems @@ -1076,6 +1096,8 @@ spotclean Works like ``clean map snow mud``, but only for the tile under the cursor. Ideal if you want to keep that bloody entrance ``clean map`` would clean up. +:dfhack-keybind:`spotclean` + .. _autodump: autodump @@ -1098,10 +1120,16 @@ Options: :destroy-here: As ``destroy``, but only the selected item in the :kbd:`k` list, or inside a container. Alias ``autodump-destroy-here``, for keybindings. + :dfhack-keybind:`autodump-destroy-here` :visible: Only process items that are not hidden. :hidden: Only process hidden items. :forbidden: Only process forbidden items (default: only unforbidden). +``autodump-destroy-item`` destroys the selected item, which may be selected +in the :kbd:`k` list, or inside a container. If called again before the game +is resumed, cancels destruction of the item. +:dfhack-keybind:`autodump-destroy-item` + cleanowned ========== @@ -1139,6 +1167,8 @@ Options: :prefs: Show dwarf preferences summary :reload: Reload configuration file (``dfhack-config/dwarfmonitor.json``) +:dfhack-keybind:`dwarfmonitor` + Widget configuration: The following types of widgets (defined in :file:`hack/lua/plugins/dwarfmonitor.lua`) @@ -1226,7 +1256,16 @@ Options: workNow ======= -Force all dwarves to look for a job immediately, or as soon as the game is unpaused. +Don't allow dwarves to idle if any jobs are available. + +When workNow is active, every time the game pauses, DF will make dwarves +perform any appropriate available jobs. This includes when you one step +through the game using the pause menu. Usage: + +:workNow: print workNow status +:workNow 0: deactivate workNow +:workNow 1: activate workNow (look for jobs on pause, and only then) +:workNow 2: make dwarves look for jobs whenever a job completes .. _seedwatch: @@ -1264,6 +1303,8 @@ zone ==== Helps a bit with managing activity zones (pens, pastures and pits) and cages. +:dfhack-keybind:`zone` + Options: :set: Set zone or cage under cursor as default for future assigns. @@ -1738,6 +1779,8 @@ Basic commands: to remove designations, for if you accidentally set 50 levels at once. :diglx: Also cross z-levels, digging stairs as needed. Alias for ``digl x``. +:dfhack-keybind:`digv` + .. _digexp: digexp @@ -2209,6 +2252,8 @@ Usage: * When viewing unit details, body-swaps into that unit. * In the main adventure mode screen, reverts transient swap. +:dfhack-keybind:`adv-bodyswap` + .. _createitem: createitem diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 0958a6efc..ed5bfdcd2 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -217,18 +217,37 @@ FILE(GLOB PROJECT_PROTOS ${CMAKE_CURRENT_SOURCE_DIR}/proto/*.proto) STRING(REPLACE ".proto" ".pb.cc" PROJECT_PROTO_SRCS "${PROJECT_PROTOS}") STRING(REPLACE ".proto" ".pb.h" PROJECT_PROTO_HDRS "${PROJECT_PROTOS}") +STRING(REPLACE "/proto/" "/proto/tmp/" PROJECT_PROTO_TMP_FILES "${PROJECT_PROTO_SRCS};${PROJECT_PROTO_HDRS}") +SET_SOURCE_FILES_PROPERTIES(${PROJECT_PROTO_SRCS} ${PROJECT_PROTO_HDRS} + PROPERTIES GENERATED TRUE) + +# Force a re-gen if any *.pb.* files are missing +# (only runs when cmake is run, but better than nothing) +FOREACH(file IN LISTS PROJECT_PROTO_SRCS PROJECT_PROTO_HDRS) + IF(NOT EXISTS ${file}) + # MESSAGE("Resetting generate_proto_core because '${file}' is missing") + FILE(REMOVE ${PROJECT_PROTO_TMP_FILES}) + BREAK() + ENDIF() +ENDFOREACH() LIST(APPEND PROJECT_HEADERS ${PROJECT_PROTO_HDRS}) LIST(APPEND PROJECT_SOURCES ${PROJECT_PROTO_SRCS}) ADD_CUSTOM_COMMAND( - OUTPUT ${PROJECT_PROTO_SRCS} ${PROJECT_PROTO_HDRS} + OUTPUT ${PROJECT_PROTO_TMP_FILES} COMMAND protoc-bin -I=${CMAKE_CURRENT_SOURCE_DIR}/proto/ - --cpp_out=dllexport_decl=DFHACK_EXPORT:${CMAKE_CURRENT_SOURCE_DIR}/proto/ + --cpp_out=dllexport_decl=DFHACK_EXPORT:${CMAKE_CURRENT_SOURCE_DIR}/proto/tmp/ ${PROJECT_PROTOS} + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${PROJECT_PROTO_TMP_FILES} + ${CMAKE_CURRENT_SOURCE_DIR}/proto/ + COMMENT "Generating core protobufs" DEPENDS protoc-bin ${PROJECT_PROTOS} ) +ADD_CUSTOM_TARGET(generate_proto_core DEPENDS ${PROJECT_PROTO_TMP_FILES}) + # Merge headers into sources SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HEADERS} PROPERTIES HEADER_FILE_ONLY TRUE ) LIST(APPEND PROJECT_SOURCES ${PROJECT_HEADERS}) @@ -299,7 +318,7 @@ ADD_CUSTOM_TARGET(git-describe ADD_DEPENDENCIES(dfhack-version git-describe) ADD_LIBRARY(dfhack SHARED ${PROJECT_SOURCES}) -ADD_DEPENDENCIES(dfhack generate_headers) +ADD_DEPENDENCIES(dfhack generate_headers generate_proto_core) ADD_LIBRARY(dfhack-client SHARED RemoteClient.cpp ColorText.cpp MiscUtils.cpp ${PROJECT_PROTO_SRCS}) ADD_DEPENDENCIES(dfhack-client dfhack) diff --git a/library/proto/tmp/.gitignore b/library/proto/tmp/.gitignore new file mode 100644 index 000000000..75feca5b1 --- /dev/null +++ b/library/proto/tmp/.gitignore @@ -0,0 +1 @@ +*.pb.* diff --git a/package/linux/dfhack b/package/linux/dfhack index b7ce1d812..7c01fcbd7 100755 --- a/package/linux/dfhack +++ b/package/linux/dfhack @@ -59,7 +59,7 @@ fi # Now run -export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"./hack/libs":"./hack" +export LD_LIBRARY_PATH="./hack/libs:./hack:$LD_LIBRARY_PATH" PRELOAD_LIB="${PRELOAD_LIB:+$PRELOAD_LIB:}./hack/libdfhack.so" diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 9522491dd..612a49715 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -44,16 +44,33 @@ FILE(GLOB PROJECT_PROTOS ${CMAKE_CURRENT_SOURCE_DIR}/proto/*.proto) STRING(REPLACE ".proto" ".pb.cc" PROJECT_PROTO_SRCS "${PROJECT_PROTOS}") STRING(REPLACE ".proto" ".pb.h" PROJECT_PROTO_HDRS "${PROJECT_PROTOS}") +STRING(REPLACE "/proto/" "/proto/tmp/" PROJECT_PROTO_TMP_FILES "${PROJECT_PROTO_SRCS};${PROJECT_PROTO_HDRS}") +SET_SOURCE_FILES_PROPERTIES(${PROJECT_PROTO_SRCS} ${PROJECT_PROTO_HDRS} + PROPERTIES GENERATED TRUE) + +# Force a re-gen if any *.pb.* files are missing +# (only runs when cmake is run, but better than nothing) +FOREACH(file IN LISTS PROJECT_PROTO_SRCS PROJECT_PROTO_HDRS) + IF(NOT EXISTS ${file}) + # MESSAGE("Resetting generate_proto because '${file}' is missing") + FILE(REMOVE ${PROJECT_PROTO_TMP_FILES}) + BREAK() + ENDIF() +ENDFOREACH() ADD_CUSTOM_COMMAND( - OUTPUT ${PROJECT_PROTO_SRCS} ${PROJECT_PROTO_HDRS} + OUTPUT ${PROJECT_PROTO_TMP_FILES} COMMAND protoc-bin -I=${dfhack_SOURCE_DIR}/library/proto/ -I=${CMAKE_CURRENT_SOURCE_DIR}/proto/ - --cpp_out=${CMAKE_CURRENT_SOURCE_DIR}/proto/ + --cpp_out=${CMAKE_CURRENT_SOURCE_DIR}/proto/tmp/ ${PROJECT_PROTOS} + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${PROJECT_PROTO_TMP_FILES} + ${CMAKE_CURRENT_SOURCE_DIR}/proto/ + COMMENT "Generating plugin protobufs" DEPENDS protoc-bin ${PROJECT_PROTOS} ) -add_custom_target(generate_proto DEPENDS ${PROJECT_PROTO_SRCS} ${PROJECT_PROTO_HDRS}) +ADD_CUSTOM_TARGET(generate_proto DEPENDS ${PROJECT_PROTO_TMP_FILES}) SET_SOURCE_FILES_PROPERTIES( Brushes.h PROPERTIES HEADER_FILE_ONLY TRUE ) diff --git a/plugins/devel/memview.cpp b/plugins/devel/memview.cpp index 27dc72861..b93319a07 100644 --- a/plugins/devel/memview.cpp +++ b/plugins/devel/memview.cpp @@ -69,7 +69,7 @@ void outputHex(uint8_t *buf,uint8_t *lbuf,size_t len,size_t start,color_ostream for(size_t i=0;i DFHack::GlobalObjects.new._at(0).world indent_rb { push @lines_rb, "Global = GlobalObjects.new._at(0)"; - for my $obj (@global_objects) + for my $oname (@global_objects) { - push @lines_rb, "def self.$obj ; Global.$obj ; end"; - push @lines_rb, "def self.$obj=(v) ; Global.$obj = v ; end"; + push @lines_rb, "if DFHack.get_global_address('$oname') != 0"; + indent_rb { + push @lines_rb, "def self.$oname ; Global.$oname ; end"; + push @lines_rb, "def self.$oname=(v) ; Global.$oname = v ; end"; + }; + push @lines_rb, "end"; } }; } @@ -743,7 +747,7 @@ sub sizeof { } elsif ($subtype eq 'df-linked-list') { return 3 * $SIZEOF_PTR; } elsif ($subtype eq 'df-flagarray') { - return 4 + $SIZEOF_PTR; + return 2 * $SIZEOF_PTR; # XXX length may be 4 on windows? } elsif ($subtype eq 'df-static-flagarray') { return $field->getAttribute('count'); } elsif ($subtype eq 'df-array') { diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index bb3b7fe30..f9368fdf6 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -214,15 +214,13 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) if (!onupdate_active) return CR_OK; - if (df::global::cur_year && (*df::global::cur_year < onupdate_minyear)) + if (df::global::cur_year && *df::global::cur_year < onupdate_minyear) return CR_OK; if (df::global::cur_year_tick && onupdate_minyeartick >= 0 && - (*df::global::cur_year == onupdate_minyear && - *df::global::cur_year_tick < onupdate_minyeartick)) + *df::global::cur_year_tick < onupdate_minyeartick) return CR_OK; if (df::global::cur_year_tick_advmode && onupdate_minyeartickadv >= 0 && - (*df::global::cur_year == onupdate_minyear && - *df::global::cur_year_tick_advmode < onupdate_minyeartickadv)) + *df::global::cur_year_tick_advmode < onupdate_minyeartickadv) return CR_OK; return plugin_eval_ruby(out, "DFHack.onupdate"); @@ -281,15 +279,13 @@ static command_result df_rubyeval(color_ostream &out, std::vector // - ruby.h with gcc -m32 on linux 64 is broken // so we dynamically load libruby with dlopen/LoadLibrary // lib path is hardcoded here, and by default downloaded by cmake -// this code should work with ruby1.9, but ruby1.9 doesn't like running -// in a dedicated non-main thread, so use ruby1.8 binaries only for now typedef uintptr_t VALUE; typedef uintptr_t ID; -#define Qfalse ((VALUE)0) -#define Qtrue ((VALUE)2) -#define Qnil ((VALUE)4) +static VALUE Qfalse = 0; +static VALUE Qtrue = 2; +static VALUE Qnil = 4; #define INT2FIX(i) ((VALUE)((((intptr_t)i) << 1) | 1)) #define FIX2INT(i) (((intptr_t)i) >> 1) @@ -359,6 +355,7 @@ static int df_loadruby(void) rbloadsym(rb_uint2inum); rbloadsym(rb_num2ulong); #undef rbloadsym + // rb_float_new_in_heap in ruby 2 if (!((rb_float_new = (decltype(rb_float_new))(LookupPlugin(libruby_handle, "rb_float_new"))) || (rb_float_new = (decltype(rb_float_new))(LookupPlugin(libruby_handle, "rb_float_new_in_heap"))))) return 0; @@ -436,6 +433,11 @@ static void df_rubythread(void *p) r_result = CR_OK; r_type = RB_IDLE; + // initialize ruby constants (may depend on libruby compilation flags/version) + Qnil = rb_eval_string_protect("nil", &state); + Qtrue = rb_eval_string_protect("true", &state); + Qfalse = rb_eval_string_protect("false", &state); + // load the default ruby-level definitions in the background state=0; rb_eval_string_protect("require './hack/ruby/ruby'", &state); @@ -1056,8 +1058,8 @@ static VALUE rb_dfmemory_set_clear(VALUE self, VALUE set) /* call an arbitrary object virtual method */ #if defined(_WIN32) && !defined(_WIN64) -__declspec(naked) static int raw_vcall(void *that, void *fptr, unsigned long a0, - unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5) +__declspec(naked) static intptr_t raw_vcall(void *that, void *fptr, uintptr_t a0, + uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5) { // __thiscall requires that the callee cleans up the stack // here we dont know how many arguments it will take, so @@ -1083,10 +1085,11 @@ __declspec(naked) static int raw_vcall(void *that, void *fptr, unsigned long a0, } } #else -static int raw_vcall(void *that, void *fptr, unsigned long a0, - unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5) +static intptr_t raw_vcall(void *that, void *fptr, uintptr_t a0, + uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5) { - int (*t_fptr)(void *me, int, int, int, int, int, int); + intptr_t (*t_fptr)(void *me, uintptr_t, uintptr_t, uintptr_t, + uintptr_t, uintptr_t, uintptr_t); t_fptr = (decltype(t_fptr))fptr; return t_fptr(that, a0, a1, a2, a3, a4, a5); } diff --git a/plugins/ruby/ruby.rb b/plugins/ruby/ruby.rb index 7709276c8..c696c23e5 100644 --- a/plugins/ruby/ruby.rb +++ b/plugins/ruby/ruby.rb @@ -109,13 +109,14 @@ module DFHack def onupdate @onupdate_list ||= [] - y = cur_year + y = yt = 0 + y = cur_year rescue 0 ytmax = TICKS_PER_YEAR if df.gamemode == :ADVENTURE and df.respond_to?(:cur_year_tick_advmode) yt = cur_year_tick_advmode ytmax *= 144 else - yt = cur_year_tick + yt = cur_year_tick rescue 0 end @onupdate_list.each { |o|