Implement a more automated search mode based on keys for some globals.

develop
Alexander Gavrilov 2012-10-02 19:53:16 +04:00
parent 7962f24cce
commit 33aead34b4
2 changed files with 279 additions and 34 deletions

@ -358,7 +358,7 @@ end
-- Interactive search utility
function DiffSearcher:find_interactive(prompt,data_type,condition_cb)
function DiffSearcher:find_interactive(prompt,data_type,condition_cb,iter_limit)
enum = enum or {}
-- Loop for restarting search from scratch
@ -374,6 +374,11 @@ function DiffSearcher:find_interactive(prompt,data_type,condition_cb)
while true do
print('')
if iter_limit and ccursor >= iter_limit then
dfhack.printerr(' Iteration limit reached without a solution.')
break
end
local ok, value, delta = condition_cb(ccursor)
ccursor = ccursor + 1

@ -2,6 +2,7 @@
local utils = require 'utils'
local ms = require 'memscan'
local gui = require 'gui'
local is_known = dfhack.internal.getAddress
@ -53,6 +54,35 @@ end
local searcher = ms.DiffSearcher.new(data)
local function get_screen(class, prompt)
if not is_known('gview') then
print('Please navigate to '..prompt)
if not utils.prompt_yes_no('Proceed?', true) then
return nil
end
return true
end
while true do
local cs = dfhack.gui.getCurViewscreen(true)
if not df.is_instance(class, cs) then
print('Please navigate to '..prompt)
if not utils.prompt_yes_no('Proceed?', true) then
return nil
end
else
return cs
end
end
end
local function screen_title()
return get_screen(df.viewscreen_titlest, 'the title screen')
end
local function screen_dwarfmode()
return get_screen(df.viewscreen_dwarfmodest, 'the main dwarf mode screen')
end
local function validate_offset(name,validator,addr,tname,...)
local obj = data:object_by_field(addr,tname,...)
if obj and not validator(obj) then
@ -136,13 +166,95 @@ local function list_index_choices(length_func)
end
end
local function can_feed()
return not force_scan['nofeed'] and is_known 'gview'
end
local function dwarfmode_feed_input(...)
local screen = screen_dwarfmode()
if not df.isvalid(screen) then
qerror('could not retrieve dwarfmode screen')
end
for _,v in ipairs({...}) do
gui.simulateInput(screen, v)
end
end
local function dwarfmode_to_top()
if not can_feed() then
return false
end
local screen = screen_dwarfmode()
if not df.isvalid(screen) then
return false
end
for i=0,10 do
if is_known 'ui' and df.global.ui.main.mode == df.ui_sidebar_mode.Default then
break
end
gui.simulateInput(screen, 'LEAVESCREEN')
end
-- force pause just in case
screen.keyRepeat = 1
return true
end
local function feed_menu_choice(catnames,catkeys,enum)
return function (idx)
idx = idx % #catnames + 1
dwarfmode_feed_input(catkeys[idx])
if enum then
return true, enum[catnames[idx]]
else
return true, catnames[idx]
end
end
end
local function feed_list_choice(count,upkey,downkey)
return function(idx)
if idx > 0 then
local ok, len
if type(count) == 'number' then
ok, len = true, count
else
ok, len = pcall(count)
end
if not ok then
len = 5
elseif len > 10 then
len = 10
end
local hcnt = len-1
local rix = 1 + (idx-1) % (hcnt*2)
if rix >= hcnt then
dwarfmode_feed_input(upkey or 'SECONDSCROLL_UP')
return true, hcnt*2 - rix
else
dwarfmode_feed_input(donwkey or 'SECONDSCROLL_DOWN')
return true, rix
end
else
print(' Please select the first list item.')
if not utils.prompt_yes_no(' Proceed?') then
return false
end
return true, 0
end
end
end
--
-- Cursor group
--
local function find_cursor()
print('\nPlease navigate to the title screen to find cursor.')
if not utils.prompt_yes_no('Proceed?', true) then
if not screen_title() then
return false
end
@ -354,13 +466,35 @@ local function is_valid_world(world)
end
local function find_world()
local addr = searcher:find_menu_cursor([[
local catnames = {
'Corpses', 'Refuse', 'Stone', 'Wood', 'Gems', 'Bars', 'Cloth', 'Leather', 'Ammo', 'Coins'
}
local catkeys = {
'STOCKPILE_GRAVEYARD', 'STOCKPILE_REFUSE', 'STOCKPILE_STONE', 'STOCKPILE_WOOD',
'STOCKPILE_GEM', 'STOCKPILE_BARBLOCK', 'STOCKPILE_CLOTH', 'STOCKPILE_LEATHER',
'STOCKPILE_AMMO', 'STOCKPILE_COINS'
}
local addr
if dwarfmode_to_top() then
dwarfmode_feed_input('D_STOCKPILES')
addr = searcher:find_interactive(
'Auto-searching for world.',
'int32_t',
feed_menu_choice(catnames, catkeys, df.stockpile_category),
20
)
end
if not addr then
addr = searcher:find_menu_cursor([[
Searching for world. Please open the stockpile creation
menu, and select different types as instructed below:]],
'int32_t',
{ 'Corpses', 'Refuse', 'Stone', 'Wood', 'Gems', 'Bars', 'Cloth', 'Leather', 'Ammo', 'Coins' },
df.stockpile_category
)
'int32_t', catnames, df.stockpile_category
)
end
validate_offset('world', is_valid_world, addr, df.world, 'selected_stockpile_type')
end
@ -385,14 +519,37 @@ local function is_valid_ui(ui)
end
local function find_ui()
local addr = searcher:find_menu_cursor([[
local catnames = {
'DesignateMine', 'DesignateChannel', 'DesignateRemoveRamps',
'DesignateUpStair', 'DesignateDownStair', 'DesignateUpDownStair',
'DesignateUpRamp', 'DesignateChopTrees'
}
local catkeys = {
'DESIGNATE_DIG', 'DESIGNATE_CHANNEL', 'DESIGNATE_DIG_REMOVE_STAIRS_RAMPS',
'DESIGNATE_STAIR_UP', 'DESIGNATE_STAIR_DOWN', 'DESIGNATE_STAIR_UPDOWN',
'DESIGNATE_RAMP', 'DESIGNATE_CHOP'
}
local addr
if dwarfmode_to_top() then
dwarfmode_feed_input('D_DESIGNATE')
addr = searcher:find_interactive(
'Auto-searching for ui.',
'int16_t',
feed_menu_choice(catnames, catkeys, df.ui_sidebar_mode),
20
)
end
if not addr then
addr = searcher:find_menu_cursor([[
Searching for ui. Please open the designation
menu, and switch modes as instructed below:]],
'int16_t',
{ 'DesignateMine', 'DesignateChannel', 'DesignateRemoveRamps', 'DesignateUpStair',
'DesignateDownStair', 'DesignateUpDownStair', 'DesignateUpRamp', 'DesignateChopTrees' },
df.ui_sidebar_mode
)
'int16_t', catnames, df.ui_sidebar_mode
)
end
validate_offset('ui', is_valid_ui, addr, df.ui, 'main', 'mode')
end
@ -421,14 +578,32 @@ local function is_valid_ui_sidebar_menus(usm)
end
local function find_ui_sidebar_menus()
local addr = searcher:find_menu_cursor([[
local addr
if dwarfmode_to_top() then
dwarfmode_feed_input('D_BUILDJOB')
addr = searcher:find_interactive([[
Auto-searching for ui_sidebar_menus. Please select a Mason,
Craftsdwarfs, or Carpenters workshop, open the Add Job
menu, and move the cursor within:]],
'int32_t',
feed_list_choice(7),
20
)
end
if not addr then
addr = searcher:find_menu_cursor([[
Searching for ui_sidebar_menus. Please switch to 'q' mode,
select a Mason, Craftsdwarfs, or Carpenters workshop, open
the Add Job menu, and move the cursor within:]],
'int32_t',
{ 0, 1, 2, 3, 4, 5, 6 },
ordinal_names
)
'int32_t',
{ 0, 1, 2, 3, 4, 5, 6 },
ordinal_names
)
end
validate_offset('ui_sidebar_menus', is_valid_ui_sidebar_menus,
addr, df.ui_sidebar_menus, 'workshop_job', 'cursor')
end
@ -455,14 +630,41 @@ local function is_valid_ui_build_selector(ubs)
end
local function find_ui_build_selector()
local addr = searcher:find_menu_cursor([[
local addr
if dwarfmode_to_top() then
addr = searcher:find_interactive([[
Auto-searching for ui_build_selector. This requires mechanisms.]],
'int32_t',
function(idx)
if idx == 0 then
dwarfmode_to_top()
dwarfmode_feed_input(
'D_BUILDING',
'HOTKEY_BUILDING_TRAP',
'HOTKEY_BUILDING_TRAP_TRIGGER',
'BUILDING_TRIGGER_ENABLE_CREATURE'
)
else
dwarfmode_feed_input('BUILDING_TRIGGER_MIN_SIZE_DOWN')
end
return true, 50000 - 1000*idx
end,
20
)
end
if not addr then
addr = searcher:find_menu_cursor([[
Searching for ui_build_selector. Please start constructing
a pressure plate, and enable creatures. Then change the min
weight as requested, remembering that the ui truncates the
number, so when it shows "Min (5000df", it means 50000:]],
'int32_t',
{ 50000, 49000, 48000, 47000, 46000, 45000, 44000 }
)
'int32_t',
{ 50000, 49000, 48000, 47000, 46000, 45000, 44000 }
)
end
validate_offset('ui_build_selector', is_valid_ui_build_selector,
addr, df.ui_build_selector, 'plate_info', 'unit_min')
end
@ -601,13 +803,33 @@ end
--
local function find_ui_unit_view_mode()
local addr = searcher:find_menu_cursor([[
local catnames = { 'General', 'Inventory', 'Preferences', 'Wounds' }
local catkeys = { 'UNITVIEW_GEN', 'UNITVIEW_INV', 'UNITVIEW_PRF', 'UNITVIEW_WND' }
local addr
if dwarfmode_to_top() and is_known('ui_selected_unit') then
dwarfmode_feed_input('D_VIEWUNIT')
if df.global.ui_selected_unit < 0 then
df.global.ui_selected_unit = 0
end
addr = searcher:find_interactive(
'Auto-searching for ui_unit_view_mode.',
'int32_t',
feed_menu_choice(catnames, catkeys, df.ui_unit_view_mode.T_value),
10
)
end
if not addr then
addr = searcher:find_menu_cursor([[
Searching for ui_unit_view_mode. Having selected a unit
with 'v', switch the pages as requested:]],
'int32_t',
{ 'General', 'Inventory', 'Preferences', 'Wounds' },
df.ui_unit_view_mode.T_value
)
'int32_t', catnames, df.ui_unit_view_mode.T_value
)
end
ms.found_offset('ui_unit_view_mode', addr)
end
@ -620,14 +842,32 @@ local function look_item_list_count()
end
local function find_ui_look_cursor()
local addr = searcher:find_menu_cursor([[
local addr
if dwarfmode_to_top() then
dwarfmode_feed_input('D_LOOK')
addr = searcher:find_interactive([[
Auto-searching for ui_look_cursor. Please select a tile
with at least 5 items or units on the ground, and move
the cursor as instructed:]],
'int32_t',
feed_list_choice(look_item_list_count),
20
)
end
if not addr then
addr = searcher:find_menu_cursor([[
Searching for ui_look_cursor. Please activate the 'k'
mode, find a tile with many items or units on the ground,
and select list entries as instructed:]],
'int32_t',
list_index_choices(look_item_list_count),
ordinal_names
)
'int32_t',
list_index_choices(look_item_list_count),
ordinal_names
)
end
ms.found_offset('ui_look_cursor', addr)
end
@ -989,10 +1229,10 @@ end
print('\nInitial globals (need title screen):\n')
exec_finder(find_gview, 'gview')
exec_finder(find_cursor, { 'cursor', 'selection_rect', 'gamemode', 'gametype' })
exec_finder(find_announcements, 'announcements')
exec_finder(find_d_init, 'd_init')
exec_finder(find_gview, 'gview')
exec_finder(find_enabler, 'enabler')
exec_finder(find_gps, 'gps')