Merge branch 'develop' of https://github.com/DFHack/dfhack into develop

develop
Japa 2016-04-03 11:09:42 +05:30
commit 869a4af50e
10 changed files with 265 additions and 56 deletions

@ -42,3 +42,8 @@ script:
- make -j3 - make -j3
notifications: notifications:
email: false email: false
irc:
channels:
- "chat.freenode.net#dfhack"
on_success: change
on_failure: always

@ -42,6 +42,7 @@ Internals
"Dwarf Fortress.exe" +devel/print-args example "Dwarf Fortress.exe" +devel/print-args example
- Prevented plugins with active viewscreens from being unloaded and causing a crash - Prevented plugins with active viewscreens from being unloaded and causing a crash
- Additional script search paths can be specified in dfhack-config/script-paths.txt
Lua Lua
--- ---
@ -55,6 +56,7 @@ New Plugins
New Scripts New Scripts
----------- -----------
- `devel/save-version`: Displays DF version information about the current save - `devel/save-version`: Displays DF version information about the current save
- `modtools/extra-gamelog`: replaces ``log-region``, ``soundsense-season``, and ``soundsense``
New Features New Features
------------ ------------
@ -102,11 +104,17 @@ Fixes
Misc Improvements Misc Improvements
----------------- -----------------
- `autolabor`, `autohauler`, `manipulator`: Added support for new jobs/labors/skills - `autolabor`, `autohauler`, `manipulator`: Added support for new jobs/labors/skills
- `colonies`: now implemented by a script
- `devel/export-dt-ini`: Updated for 0.42.06 - `devel/export-dt-ini`: Updated for 0.42.06
- `devel/find-offsets`: Automated several more scans
- `lua`: Now supports some built-in variables like `gui/gm-editor`, e.g. ``unit``, ``screen`` - `lua`: Now supports some built-in variables like `gui/gm-editor`, e.g. ``unit``, ``screen``
- `remotefortressreader`: Can now trigger keyboard events
- `stockflow`: Now offers better control over individual craft jobs - `stockflow`: Now offers better control over individual craft jobs
- `weather`: now implemented by a script - `weather`: now implemented by a script
- `colonies`: now implemented by a script
Removed
-------
- DFusion: legacy script system, obsolete or replaced by better alternatives
DFHack 0.40.24-r5 DFHack 0.40.24-r5
@ -139,14 +147,6 @@ Misc Improvements
- `fix/dry-buckets`: replaces the ``drybuckets`` plugin - `fix/dry-buckets`: replaces the ``drybuckets`` plugin
- `feature`: now implemented by a script - `feature`: now implemented by a script
Removed
-------
- DFusion: legacy script system, obsolete or replaced by better alternatives
New Scripts
-----------
- `modtools/extra-gamelog`: replaces ``log-region``, ``soundsense-season``, and ``soundsense``
DFHack 0.40.24-r4 DFHack 0.40.24-r4
================= =================

@ -0,0 +1,4 @@
# Add additional script search paths here
# Blank lines and lines that start with "#" will be ignored
# Paths preceded by "+" will be searched first
# Paths preceded by "-" will be searched after the default paths

@ -165,6 +165,26 @@ modules/Once.cpp
modules/Filesystem.cpp modules/Filesystem.cpp
) )
SET(STATIC_FIELDS_FILES)
FOREACH(GROUP other a b c d e f g h i j k l m n o p q r s t u v w x y z)
SET(STATIC_FIELDS_FILENAME ${dfhack_SOURCE_DIR}/library/DataStaticsFields/${GROUP}.cpp)
IF(${GROUP} STREQUAL "other")
SET(STATIC_FIELDS_INC_FILENAME "df/static.fields.inc")
ELSE()
SET(STATIC_FIELDS_INC_FILENAME "df/static.fields-${GROUP}.inc")
ENDIF()
FILE(WRITE ${STATIC_FIELDS_FILENAME}.tmp
"#define STATIC_FIELDS_GROUP\n"
"#include \"../DataStaticsFields.cpp\"\n"
"#include \"${STATIC_FIELDS_INC_FILENAME}\"\n"
)
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E copy_if_different
${STATIC_FIELDS_FILENAME}.tmp ${STATIC_FIELDS_FILENAME})
FILE(REMOVE ${STATIC_FIELDS_FILENAME}.tmp)
LIST(APPEND STATIC_FIELDS_FILES ${STATIC_FIELDS_FILENAME})
ENDFOREACH()
LIST(APPEND MAIN_SOURCES ${STATIC_FIELDS_FILES})
IF(WIN32) IF(WIN32)
SOURCE_GROUP("Modules\\Headers" FILES ${MODULE_HEADERS}) SOURCE_GROUP("Modules\\Headers" FILES ${MODULE_HEADERS})
SOURCE_GROUP("Modules\\Sources" FILES ${MODULE_SOURCES}) SOURCE_GROUP("Modules\\Sources" FILES ${MODULE_SOURCES})
@ -231,10 +251,10 @@ ADD_CUSTOM_TARGET(generate_headers DEPENDS ${dfapi_SOURCE_DIR}/include/df/codege
IF(UNIX) IF(UNIX)
# Don't produce debug info for generated stubs # Don't produce debug info for generated stubs
SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES}
PROPERTIES COMPILE_FLAGS "-g0 -O1") PROPERTIES COMPILE_FLAGS "-g0 -O1")
ELSE(WIN32) ELSE(WIN32)
SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp 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()

@ -511,6 +511,41 @@ string Core::findScript(string name)
return ""; return "";
} }
bool loadScriptPaths(color_ostream &out, bool silent = false)
{
using namespace std;
string filename("dfhack-config/script-paths.txt");
ifstream file(filename);
if (!file)
{
if (!silent)
out.printerr("Could not load %s\n", filename.c_str());
return false;
}
string raw;
int line = 0;
while (getline(file, raw))
{
++line;
istringstream ss(raw);
char ch;
ss >> skipws;
if (!(ss >> ch) || ch == '#')
continue;
ss >> ws; // discard whitespace
string path;
getline(ss, path);
if (ch == '+' || ch == '-')
{
if (!Core::getInstance().addScriptPath(path, ch == '+') && !silent)
out.printerr("%s:%i: Failed to add path: %s\n", filename.c_str(), line, path.c_str());
}
else if (!silent)
out.printerr("%s:%i: Illegal character: %c\n", filename.c_str(), line, ch);
}
return true;
}
static std::map<std::string, state_change_event> state_change_event_map; static std::map<std::string, state_change_event> state_change_event_map;
static void sc_event_map_init() { static void sc_event_map_init() {
if (!state_change_event_map.size()) if (!state_change_event_map.size())
@ -1505,6 +1540,8 @@ bool Core::Init()
} }
} }
loadScriptPaths(con);
// initialize common lua context // initialize common lua context
if (!Lua::Core::Init(con)) if (!Lua::Core::Init(con))
{ {

@ -3,9 +3,11 @@
#include "MiscUtils.h" #include "MiscUtils.h"
#include "VersionInfo.h" #include "VersionInfo.h"
#ifndef STATIC_FIELDS_GROUP
#include "df/world.h" #include "df/world.h"
#include "df/world_data.h" #include "df/world_data.h"
#include "df/ui.h" #include "df/ui.h"
#endif
#include "DataIdentity.h" #include "DataIdentity.h"
#include "DataFuncs.h" #include "DataFuncs.h"
@ -18,6 +20,7 @@ namespace df {
#define NUMBER_IDENTITY_TRAITS(type) \ #define NUMBER_IDENTITY_TRAITS(type) \
number_identity<type> identity_traits<type>::identity(#type); number_identity<type> identity_traits<type>::identity(#type);
#ifndef STATIC_FIELDS_GROUP
NUMBER_IDENTITY_TRAITS(char); NUMBER_IDENTITY_TRAITS(char);
NUMBER_IDENTITY_TRAITS(int8_t); NUMBER_IDENTITY_TRAITS(int8_t);
NUMBER_IDENTITY_TRAITS(uint8_t); NUMBER_IDENTITY_TRAITS(uint8_t);
@ -48,7 +51,7 @@ namespace df {
sizeof(std::fstream), fstream_allocator_fn, "fstream"); sizeof(std::fstream), fstream_allocator_fn, "fstream");
buffer_container_identity buffer_container_identity::base_instance; buffer_container_identity buffer_container_identity::base_instance;
#endif
#undef NUMBER_IDENTITY_TRAITS #undef NUMBER_IDENTITY_TRAITS
} }
@ -58,6 +61,3 @@ namespace df {
#define GFLD(mode, name) struct_field_info::mode, #name, (size_t)&df::global::name #define GFLD(mode, name) struct_field_info::mode, #name, (size_t)&df::global::name
#define METHOD(mode, name) struct_field_info::mode, #name, 0, wrap_function(&CUR_STRUCT::name) #define METHOD(mode, name) struct_field_info::mode, #name, 0, wrap_function(&CUR_STRUCT::name)
#define FLD_END struct_field_info::END #define FLD_END struct_field_info::END
// Field definitions
#include "df/static.fields.inc"

@ -0,0 +1,2 @@
*.cpp
*.tmp

@ -296,9 +296,14 @@ function field_ref(handle,...)
end end
function field_offset(type,...) function field_offset(type,...)
local handle = df.reinterpret_cast(type,1) local handle = df.new(type)
local _,haddr = df.sizeof(handle)
local _,addr = df.sizeof(field_ref(handle,...)) local _,addr = df.sizeof(field_ref(handle,...))
return addr-1 -- to aid in diagnosis of bad virtual dtors
io.stderr:write('memscan: deleting instance of '..tostring(type) .. '\n'):flush()
df.delete(handle)
io.stderr:write('successfully deleted\n'):flush()
return addr-haddr
end end
function MemoryArea:object_by_field(addr,type,...) function MemoryArea:object_by_field(addr,type,...)

@ -1 +1 @@
Subproject commit 90645ebd8e8433196d695a478e3d6c9a32add21d Subproject commit baba4f54464a303bab6bc4f1b7d90cc9e4ff107b

@ -46,6 +46,11 @@ end
collectgarbage() collectgarbage()
function prompt_proceed(indent)
if not indent then indent = 0 end
return utils.prompt_yes_no(string.rep(' ', indent) .. 'Proceed?', true)
end
print[[ print[[
WARNING: THIS SCRIPT IS STRICTLY FOR DFHACK DEVELOPERS. WARNING: THIS SCRIPT IS STRICTLY FOR DFHACK DEVELOPERS.
@ -88,7 +93,7 @@ local searcher = ms.DiffSearcher.new(data)
local function get_screen(class, prompt) local function get_screen(class, prompt)
if not is_known('gview') then if not is_known('gview') then
print('Please navigate to '..prompt) print('Please navigate to '..prompt)
if not utils.prompt_yes_no('Proceed?', true) then if not prompt_proceed() then
return nil return nil
end end
return true return true
@ -98,7 +103,7 @@ local function get_screen(class, prompt)
local cs = dfhack.gui.getCurViewscreen(true) local cs = dfhack.gui.getCurViewscreen(true)
if not df.is_instance(class, cs) then if not df.is_instance(class, cs) then
print('Please navigate to '..prompt) print('Please navigate to '..prompt)
if not utils.prompt_yes_no('Proceed?', true) then if not prompt_proceed() then
return nil return nil
end end
else else
@ -172,8 +177,9 @@ local function exec_finder(finder, names, validators)
end end
end end
if search then if search then
if not dfhack.safecall(finder) then local ok, err = dfhack.safecall(finder)
if not utils.prompt_yes_no('Proceed with the rest of the script?') then if not ok then
if tostring(err):find('abort') or not utils.prompt_yes_no('Proceed with the rest of the script?') then
searcher:reset() searcher:reset()
qerror('Quit') qerror('Quit')
end end
@ -217,6 +223,7 @@ local function dwarfmode_feed_input(...)
if not df.isvalid(screen) then if not df.isvalid(screen) then
qerror('could not retrieve dwarfmode screen') qerror('could not retrieve dwarfmode screen')
end end
try_save_cursor()
for _,v in ipairs({...}) do for _,v in ipairs({...}) do
gui.simulateInput(screen, v) gui.simulateInput(screen, v)
end end
@ -259,9 +266,29 @@ local function dwarfmode_to_top()
return true return true
end end
local prev_cursor = df.global.T_cursor:new()
prev_cursor.x = -30000
function try_save_cursor()
if not dfhack.internal.getAddress('cursor') then return end
for _, v in pairs(df.global.cursor) do
if v < 0 then
return
end
end
prev_cursor:assign(df.global.cursor)
end
function try_restore_cursor()
if not dfhack.internal.getAddress('cursor') then return end
if prev_cursor.x >= 0 then
df.global.cursor:assign(prev_cursor)
dwarfmode_feed_input('CURSOR_DOWN_Z', 'CURSOR_UP_Z')
end
end
local function feed_menu_choice(catnames,catkeys,enum,enter_seq,exit_seq,prompt) local function feed_menu_choice(catnames,catkeys,enum,enter_seq,exit_seq,prompt)
return function (idx) return function (idx)
if idx == 0 and prompt and not utils.prompt_yes_no(' Proceed?', true) then if idx == 0 and prompt and not prompt_proceed(2) then
return false return false
end end
if idx > 0 then if idx > 0 then
@ -305,7 +332,7 @@ local function feed_list_choice(count,upkey,downkey)
end end
else else
print(' Please select the first list item.') print(' Please select the first list item.')
if not utils.prompt_yes_no(' Proceed?', true) then if not prompt_proceed(2) then
return false return false
end end
return true, 0 return true, 0
@ -316,14 +343,14 @@ end
local function feed_menu_bool(enter_seq, exit_seq) local function feed_menu_bool(enter_seq, exit_seq)
return function(idx) return function(idx)
if idx == 0 then if idx == 0 then
if not utils.prompt_yes_no(' Proceed?', true) then if not prompt_proceed(2) then
return false return false
end end
return true, 0 return true, 0
end end
if idx == 5 then if idx == 5 then
print(' Please resize the game window.') print(' Please resize the game window.')
if not utils.prompt_yes_no(' Proceed?', true) then if not prompt_proceed(2) then
return false return false
end end
end end
@ -539,7 +566,7 @@ end
local function find_gps() local function find_gps()
print('\nPlease ensure the mouse cursor is not over the game window.') print('\nPlease ensure the mouse cursor is not over the game window.')
if not utils.prompt_yes_no('Proceed?', true) then if not prompt_proceed() then
return return
end end
@ -555,7 +582,7 @@ local function find_gps()
local idx, addr = zone.area.int32_t:find_one{w, h, -1, -1} local idx, addr = zone.area.int32_t:find_one{w, h, -1, -1}
if not idx then if not idx then
idx, addr = data.int32_t.find_one{w, h, -1, -1} idx, addr = data.int32_t:find_one{w, h, -1, -1}
end end
if idx then if idx then
validate_offset('gps', is_valid_gps, addr, df.graphic, 'dimx') validate_offset('gps', is_valid_gps, addr, df.graphic, 'dimx')
@ -709,11 +736,18 @@ local function find_ui_sidebar_menus()
dwarfmode_feed_input('D_BUILDJOB') dwarfmode_feed_input('D_BUILDJOB')
addr = searcher:find_interactive([[ addr = searcher:find_interactive([[
Auto-searching for ui_sidebar_menus. Please select a Mason, Auto-searching for ui_sidebar_menus. Please select a Mason's,
Craftsdwarfs, or Carpenters workshop, open the Add Job Craftsdwarf's, or Carpenter's workshop:]],
menu, and move the cursor within:]],
'int32_t', 'int32_t',
feed_list_choice(7), function(idx)
if idx == 0 then
prompt_proceed(2)
-- ensure that the job list isn't full
dwarfmode_feed_input('BUILDJOB_CANCEL', 'BUILDJOB_ADD')
return true, 0
end
return feed_list_choice(7)(idx)
end,
20 20
) )
end end
@ -875,19 +909,41 @@ end
-- --
local function find_ui_menu_width() local function find_ui_menu_width()
local addr = searcher:find_menu_cursor([[ local addr
if dwarfmode_to_top() then
addr = searcher:find_interactive('Auto-searching for ui_menu_width', 'int8_t', function(idx)
local val = (idx % 3) + 1
if idx == 0 then
print('Switch to the default [map][menu][map] layout (with Tab)')
if not prompt_proceed(2) then return false end
else
dwarfmode_feed_input('CHANGETAB', val ~= 3 and 'CHANGETAB')
end
return true, val
end)
end
if not addr then
addr = searcher:find_menu_cursor([[
Searching for ui_menu_width. Please exit to the main Searching for ui_menu_width. Please exit to the main
dwarfmode menu, then use Tab to do as instructed below:]], dwarfmode menu, then use Tab to do as instructed below:]],
'int8_t', 'int8_t',
{ 2, 3, 1 }, { 2, 3, 1 },
{ [2] = 'switch to the most usual [mapmap][menu] layout', { [2] = 'switch to the most usual [mapmap][menu] layout',
[3] = 'hide the menu completely', [3] = 'hide the menu completely',
[1] = 'switch to the default [map][menu][map] layout' } [1] = 'switch to the default [map][menu][map] layout' }
) )
end
ms.found_offset('ui_menu_width', addr) ms.found_offset('ui_menu_width', addr)
-- NOTE: Assume that the vars are adjacent, as always -- NOTE: Assume that the vars are adjacent, as always
ms.found_offset('ui_area_map_width', addr+1) ms.found_offset('ui_area_map_width', addr+1)
-- reset to make sure view is small enough for window_x/y scan on small maps
df.global.ui_menu_width = 2
df.global.ui_area_map_width = 3
end end
-- --
@ -1087,12 +1143,20 @@ local function find_ui_workshop_job_cursor()
if dwarfmode_to_top() then if dwarfmode_to_top() then
dwarfmode_feed_input('D_BUILDJOB') dwarfmode_feed_input('D_BUILDJOB')
addr = searcher:find_interactive([[ addr = searcher:find_interactive([[
Auto-searching for ui_workshop_job_cursor. Please highlight a Auto-searching for ui_workshop_job_cursor. Please highlight a
workshop with at least 5 contained jobs, and select as instructed:]], Mason's or Carpenter's workshop, or any building with a job
selection interface navigable with just "Enter":]],
'int32_t', 'int32_t',
feed_list_choice(workshop_job_list_count), function(idx)
if idx == 0 then prompt_proceed(2) end
for i = 1, 10 - workshop_job_list_count() do
dwarfmode_feed_input('BUILDJOB_ADD', 'SELECT', 'SELECT', 'SELECT', 'SELECT', 'SELECT')
end
dwarfmode_feed_input('SECONDSCROLL_DOWN')
-- adding jobs resets the cursor position, so it is difficult to determine here
return true
end,
20 20
) )
end end
@ -1119,6 +1183,7 @@ local function find_ui_building_in_assign()
if dwarfmode_to_top() then if dwarfmode_to_top() then
dwarfmode_feed_input('D_BUILDJOB') dwarfmode_feed_input('D_BUILDJOB')
try_restore_cursor()
addr = searcher:find_interactive([[ addr = searcher:find_interactive([[
Auto-searching for ui_building_in_assign. Please select a room, Auto-searching for ui_building_in_assign. Please select a room,
@ -1160,6 +1225,7 @@ local function find_ui_building_in_resize()
if dwarfmode_to_top() then if dwarfmode_to_top() then
dwarfmode_feed_input('D_BUILDJOB') dwarfmode_feed_input('D_BUILDJOB')
try_restore_cursor()
addr = searcher:find_interactive([[ addr = searcher:find_interactive([[
Auto-searching for ui_building_in_resize. Please select a room, Auto-searching for ui_building_in_resize. Please select a room,
@ -1396,7 +1462,7 @@ function step_n_frames(cnt, feed)
while world.frame_counter-ctick < cnt do while world.frame_counter-ctick < cnt do
print(" Please step the game "..(cnt-world.frame_counter+ctick)..more.." frames.") print(" Please step the game "..(cnt-world.frame_counter+ctick)..more.." frames.")
more = ' more' more = ' more'
if not utils.prompt_yes_no(' Proceed?', true) then if not prompt_proceed(2) then
return nil return nil
end end
end end
@ -1531,16 +1597,53 @@ end
local function find_process_jobs() local function find_process_jobs()
local zone = get_process_zone() or searcher local zone = get_process_zone() or searcher
local addr
stop_autosave() stop_autosave()
local addr = zone:find_menu_cursor([[ if dwarfmode_to_top() and dfhack.internal.getAddress('cursor') then
local cursor = df.global.T_cursor:new()
addr = zone:find_interactive([[
Searching for process_jobs. Please position the cursor to the right
of at least 10 vacant natural floor tiles.]],
'int8_t',
function(idx)
if idx == 0 then
dwarfmode_feed_input('D_LOOK')
if not prompt_proceed(2) then return false end
cursor:assign(df.global.cursor)
elseif idx == 6 then
print(' Please resize the game window.')
if not prompt_proceed(2) then return false end
end
dwarfmode_to_top()
dwarfmode_step_frames(1)
if idx % 2 == 0 then
dwarfmode_feed_input(
'D_BUILDING',
'HOTKEY_BUILDING_CONSTRUCTION',
'HOTKEY_BUILDING_CONSTRUCTION_WALL'
)
df.global.cursor:assign(cursor)
df.global.cursor.x = df.global.cursor.x + (idx / 2)
dwarfmode_feed_input('CURSOR_RIGHT', 'CURSOR_LEFT', 'SELECT', 'SELECT')
return true, 1
else
return true, 0
end
end,
20)
end
if not addr then
local addr = zone:find_menu_cursor([[
Searching for process_jobs. Please do as instructed below:]], Searching for process_jobs. Please do as instructed below:]],
'int8_t', 'int8_t',
{ 1, 0 }, { 1, 0 },
{ [1] = 'designate a building to be constructed, e.g a bed or a wall', { [1] = 'designate a building to be constructed, e.g a bed or a wall',
[0] = 'step or unpause the game to reset the flag' } [0] = 'step or unpause the game to reset the flag' }
) )
end
ms.found_offset('process_jobs', addr) ms.found_offset('process_jobs', addr)
end end
@ -1550,16 +1653,49 @@ end
local function find_process_dig() local function find_process_dig()
local zone = get_process_zone() or searcher local zone = get_process_zone() or searcher
local addr
stop_autosave() stop_autosave()
local addr = zone:find_menu_cursor([[ if dwarfmode_to_top() and dfhack.internal.getAddress('cursor') then
local cursor = df.global.T_cursor:new()
addr = zone:find_interactive([[
Searching for process_dig. Please position the cursor to the right
of at least 10 unmined, unrevealed tiles.]],
'int8_t',
function(idx)
if idx == 0 then
dwarfmode_feed_input('D_LOOK')
if not prompt_proceed(2) then return false end
cursor:assign(df.global.cursor)
elseif idx == 6 then
print(' Please resize the game window.')
if not prompt_proceed(2) then return false end
end
dwarfmode_to_top()
dwarfmode_step_frames(1)
if idx % 2 == 0 then
dwarfmode_feed_input('D_DESIGNATE', 'DESIGNATE_DIG')
df.global.cursor:assign(cursor)
df.global.cursor.x = df.global.cursor.x + (idx / 2)
dwarfmode_feed_input('SELECT', 'SELECT')
return true, 1
else
return true, 0
end
end,
20)
end
if not addr then
addr = zone:find_menu_cursor([[
Searching for process_dig. Please do as instructed below:]], Searching for process_dig. Please do as instructed below:]],
'int8_t', 'int8_t',
{ 1, 0 }, { 1, 0 },
{ [1] = 'designate a tile to be mined out', { [1] = 'designate a tile to be mined out',
[0] = 'step or unpause the game to reset the flag' } [0] = 'step or unpause the game to reset the flag' }
) )
end
ms.found_offset('process_dig', addr) ms.found_offset('process_dig', addr)
end end
@ -1671,7 +1807,7 @@ exec_finder(find_gps, 'gps', is_valid_gps)
print('\nCompound globals (need loaded world):\n') print('\nCompound globals (need loaded world):\n')
print('\nPlease load the save previously processed with prepare-save.') print('\nPlease load the save previously processed with prepare-save.')
if not utils.prompt_yes_no('Proceed?', true) then if not prompt_proceed() then
searcher:reset() searcher:reset()
return return
end end