Bay 12 Games Forum

Please login or register.

Login with username, password and session length
Advanced search  
Pages: [1] 2

Author Topic: Adventure Mode UI : Quick Quest Gathering Script  (Read 5104 times)

1337G4mer

  • Bay Watcher
    • View Profile
Adventure Mode UI : Quick Quest Gathering Script
« on: January 04, 2016, 05:46:03 pm »

Hi,

Edit 20160106: Updated the post with working code v0.2

Requirement:
Gather all quests (map markers you get by "[you receive a detailed description]" option) from a person. Manual method of Gathering each mapmarker at the minimum requires 15 key presses. A well-travelled person usually has around 40-50 quests on a map with say 150 years of history. I like to have all the markers gathered and then roam the wilderness to slay all the villains and beasts. So, technically the below is what I look to automate.
  • start conversation by pressing k with a well-travelled person(some one who has quests)
  • run the script below, this script will emulate
    • start talking by simulating keypress k and enter
    • find troubles menu and simulate keypress enter
    • find quests menu and simulate keypress enter
    • find directions option and simulate keypress enter

Further TODO:
Implement break infinite looping such that it identifies and stops after the person has given all quests. Currently it does in most cases but not all.
Test for all conversation options, sometimes the options are different or we have extra conversation options. test for them.

How to run below code? See comments in script.

Tested on DFHack version 0.42.03-alpha1 (release)

Code: [Select]
gui = require 'gui'
gs = require 'gui.script'

--[[
Author : 1337G4mer on bay12 and reddit
Version : 0.3
Description : A small utility to gather all quest/sites offered by your conversation partner.

Config:
sleepFrames: Number of frames to skip set low if you want fast processing
slower runs faster but sometimes bugs out. Setting number lower than 10 created
issues for me. Defaulted to 20.

Usage:
To run the script as "gatherQuests.lua" in your hack/scripts directory.
from command propmt type gatherQuests to run it.
Alternatively you can bind to one of your keys with the following command
keybinding add Ctrl-Q@dungeonmode gatherQuests
Before you run it though find a creature who has lot of sites to give.
If the creature doesn't offer any [detailed description] you may run into infinite loop.
In that case kill script as described below.

Troubleshooting:
When script fails or stops because of not able to find the correct menu option.
Just re run the script and it will pick up where it left.

Although this is better than macro because you can switch your screen to your favourite youtube video
and the script still keeps going unlike macro, however, since it progress in game time, if you are stuck in infinite
loop your adventurer can possibly starve to death :D (not happened in testing yet, but possible)

You can kill/stop the script any time by typing cmd in dfhack console.-> kill-lua gatherQuests
Note: You may have to do this sometime as there are some conversation options which run in infinite loop.
]]--
sleepFrames = 20
--Global Variables--
breakNow = 'N'
troublesExist = 'N'
armiesExist = 'N'
beastsExist = 'N'
banditsExist='N'
criminalsExist = 'N'
verminsExist = 'N'
fightingExist = 'N'
ruffiansExist = 'N'
directionsExist = 'N'
invalidMenuOptions = 'N'


local vs = dfhack.gui.getCurViewscreen(true)

function findAndChoose()
invalidMenuOptions = 'N'
local choicesPtr = df.global.ui_advmode.conversation.choices
for i, choice in pairs(choicesPtr) do
choiceTypeNum = choice.choice.type
choiceTitle = choice.title[0].value
choiceTypeStr = 'unst'
--print('chc '..i,choiceTypeNum,choiceTitle)
if string.find(choiceTitle, 'troubles') and choiceTypeNum == 11 then
df.global.ui_advmode.conversation.cursor_choice=i
choiceTypeStr = 'trbl'
troublesExist = 'Y'
break
elseif string.find(choiceTitle, 'beasts') and  choiceTypeNum == 75 then
choiceTypeStr = 'bsts'
beastsExist = 'Y'
df.global.ui_advmode.conversation.cursor_choice=i
break
elseif string.find(choiceTitle, 'directions')  and  choiceTypeNum == 77 then
choiceTypeStr = 'drcn'
directionsExist = 'Y'
df.global.ui_advmode.conversation.cursor_choice=i
break
elseif string.find(choiceTitle, 'criminals') and  choiceTypeNum == 75 then
choiceTypeStr = 'crml'
criminalsExist = 'Y'
df.global.ui_advmode.conversation.cursor_choice=i
break
elseif string.find(choiceTitle, 'bandits') and  choiceTypeNum == 75 then
choiceTypeStr = 'bndt'
banditsExist='Y'
df.global.ui_advmode.conversation.cursor_choice=i
break
elseif string.find(choiceTitle, 'vermin') and  choiceTypeNum == 75 then
choiceTypeStr = 'vrmn'
verminsExist = 'Y'
df.global.ui_advmode.conversation.cursor_choice=i
break
elseif string.find(choiceTitle, 'armies') and  choiceTypeNum == 75 then
choiceTypeStr = 'mrch'
ruffiansExist = 'Y'
df.global.ui_advmode.conversation.cursor_choice=i
break
elseif string.find(choiceTitle, 'ruffians') and  choiceTypeNum == 75 then
choiceTypeStr = 'rffn'
ruffiansExist = 'Y'
df.global.ui_advmode.conversation.cursor_choice=i
break
elseif string.find(choiceTitle, 'fighting') and  choiceTypeNum == 75 then
choiceTypeStr = 'fght'
ruffiansExist = 'Y'
df.global.ui_advmode.conversation.cursor_choice=i
break
end
end
end

gs.start(function()
print('================== Starting Quest Gather ==================')
i=0
repeat
i = i +1

--reset globals.
breakNow = 'N'
troublesExist = 'N'
beastsExist = 'N'
criminalsExist = 'N'
banditsExist='Y'
verminsExist = 'N'
directionsExist = 'N'
invalidMenuOptions = 'N'
choiceTypeNum = ''
choiceTitle = ''

gs.sleep(sleepFrames,'frames')
gui.simulateInput(vs, 'SELECT')
gui.simulateInput(vs, 'SELECT')
gui.simulateInput(vs, 'SELECT')
gs.sleep(sleepFrames,'frames')
--vs = dfhack.gui.getCurViewscreen()
gui.simulateInput(vs, 'A_TALK')
gs.sleep(sleepFrames,'frames')
gui.simulateInput(vs, 'SELECT')
gs.sleep(sleepFrames,'frames')
findAndChoose()
gs.sleep(sleepFrames,'frames')
if (choiceTypeStr ~= 'unst') then
--print('<<>>',choiceTypeStr)
gui.simulateInput(vs,'SELECT')
gs.sleep(sleepFrames,'frames')
else
--print('>><<',choiceTypeStr)
invalidMenuOptions = 'Y'
end
existsStr = troublesExist..armiesExist..beastsExist..criminalsExist..banditsExist..verminsExist..fightingExist..ruffiansExist..directionsExist
if choiceTypeStr == 'drcn' then
dfhack.color(10)
else
dfhack.color(8)
end
print('Lp:'..i,choiceTypeStr,existsStr,invalidMenuOptions,choiceTypeNum,choiceTitle)
if  invalidMenuOptions =='Y' then
print('================== Finished Quest Gather ==================')
breakNow = 'Y'
end

until breakNow == 'Y'
end)

Output Image:




My other scripts:

Rumor System UI improvement Script http://www.bay12forums.com/smf/index.php?topic=155262.0

Edit: v0.3 Fixed location gathering for Armies query
« Last Edit: January 09, 2016, 03:46:12 pm by 1337G4mer »
Logged

Max™

  • Bay Watcher
  • [CULL:SQUARE]
    • View Profile
Re: [WIP] Adventure Mode : Quick Quest Gathering Script
« Reply #1 on: January 04, 2016, 05:57:19 pm »

Yeah, you should definitely make a github, and then make a [yourname]/scripts/ folder with a dfhack fork and see about getting them pulled into the main release once you polish them up. I still gotta go in and figure out what broke names and then restructure some stuff from artifake to make it work right.

I did a sorta "cut to the chase" script like this.
Code: (revealsites.lua) [Select]
local hid = df.global.world.world_data.sites
for k, v in ipairs(hid) do
        hid[k].flags.Undiscovered=false
        hid[k].flags[8]=true
end
Logged
Engraved here is a rendition of an image of the Dwarf Fortress learning curve. All craftsdwarfship is of the highest quality. It depicts an obsidian overhang which menaces with spikes of obsidian and tears. Carved on the overhang is an image of Toady One and the players. The players are curled up in a fetal position. Toady One is laughing. The players are burning.
The VectorCurses+1 tileset strikes the square set and the severed part sails off in an arc!

1337G4mer

  • Bay Watcher
    • View Profile
Re: [WIP] Adventure Mode : Quick Quest Gathering Script
« Reply #2 on: January 04, 2016, 06:05:26 pm »

Max: I slew dragons, megabeasts, vermin and every living entity with the swing of my axe.
Me : It was inevitable.

Damn you Max, that is clever !

But kinda cheaty  :P In my way I only get the quests from the guy who knows about the area around. Not whole world. Help me with my code :) Thanks
Logged

Max™

  • Bay Watcher
  • [CULL:SQUARE]
    • View Profile
Re: [WIP] Adventure Mode : Quick Quest Gathering Script
« Reply #3 on: January 04, 2016, 07:27:08 pm »

Well, I wrote the script because I was on a world with no surviving civs and needed a way to find sites.

I'm currently not Focused or Focused! or I'd be tinkering more with trying to test the scripts and such.
Logged
Engraved here is a rendition of an image of the Dwarf Fortress learning curve. All craftsdwarfship is of the highest quality. It depicts an obsidian overhang which menaces with spikes of obsidian and tears. Carved on the overhang is an image of Toady One and the players. The players are curled up in a fetal position. Toady One is laughing. The players are burning.
The VectorCurses+1 tileset strikes the square set and the severed part sails off in an arc!

1337G4mer

  • Bay Watcher
    • View Profile
Re: [WIP] Adventure Mode : Quick Quest Gathering Script
« Reply #4 on: January 05, 2016, 01:51:00 am »

More research more confusion, I tried another approach , If I just save and run this file anything beyond the commented line doesn't work.

All I am doing is  --> press k, Enter, t, r, o, u, Enter  (nothing after this works) k, Enter, b ,e ,a ,s, Enter, k, d, i, r, Enter
(if you are going to test it, start a conversation with someone before running, meaning press k,  greet them)


I am beginning to get frustrated, looks like I am not understanding the lua api, or dfhack isn't working properly.
Code: [Select]
gui = require 'gui'
local vs1 = dfhack.gui.getCurViewscreen(true)
gui.simulateInput(vs1, 'A_TALK')
gui.simulateInput(vs1, 'SELECT')
gui.simulateInput(vs1, 'STRING_A116')
gui.simulateInput(vs1, 'STRING_A114')
gui.simulateInput(vs1, 'STRING_A111')
gui.simulateInput(vs1, 'STRING_A117')
gui.simulateInput(vs1, 'SELECT')

-- Anything  beyond this line doesn't work...
local vs2 = dfhack.gui.getCurViewscreen(true)
gui.simulateInput(vs2, 'A_TALK')
gui.simulateInput(vs2, 'SELECT')
gui.simulateInput(vs2, 'STRING_A098')
gui.simulateInput(vs2, 'STRING_A101')
gui.simulateInput(vs2, 'STRING_A097')
gui.simulateInput(vs2, 'STRING_A115')
gui.simulateInput(vs2, 'SELECT')

local vs3 = dfhack.gui.getCurViewscreen(true)
gui.simulateInput(vs3, 'A_TALK')
gui.simulateInput(vs3, 'SELECT')
gui.simulateInput(vs3, 'STRING_A100')
gui.simulateInput(vs3, 'STRING_A105')
gui.simulateInput(vs3, 'STRING_A114')
gui.simulateInput(vs3, 'SELECT')
Logged

Warmist

  • Bay Watcher
  • Master of unfinished jobs
    • View Profile
Re: [WIP] Adventure Mode : Quick Quest Gathering Script
« Reply #5 on: January 05, 2016, 07:17:43 am »

I am beginning to get frustrated, looks like I am not understanding the lua api, or dfhack isn't working properly.
I might be wrong, but i think it needs to pass control back to df when pressing "select". For that to work best there is gui.script

e.g.
Code: [Select]
gui= require 'gui'
gs=require 'gui.script'

gs.start(function()
    local vs1 = dfhack.gui.getCurViewscreen(true)
    gui.simulateInput(vs1, 'SELECT')
    gs.sleep(5, 'frames') --one frame would prob. work
    --that sleep returns control to the df and now it can display new viewscreen etc...
    local vs2 = dfhack.gui.getCurViewscreen(true)
    gui.simulateInput(vs2, 'A_TALK')
  end)
Code is not tested but should work :)

1337G4mer

  • Bay Watcher
    • View Profile
Re: [WIP] Adventure Mode : Quick Quest Gathering Script
« Reply #6 on: January 06, 2016, 03:16:50 am »

Thanks, I had suspected that it could be a threading issue. I tried to fix it by os.execute(sleep) kinda function.. but that didn't work of course. This way it works. the lower number of frames however was creating unexpected issues. I was able to get it to work now. The script works almost too perfectly now. Sometimes it goes into infinite loops because of some silly conversation options. but thats okay we can always kill the script with kill-lua command. Let me post a new thread or update OP with the better working version.
Logged

Warmist

  • Bay Watcher
  • Master of unfinished jobs
    • View Profile
Re: [WIP] Adventure Mode : Quick Quest Gathering Script
« Reply #7 on: January 06, 2016, 03:59:00 am »

Thanks, I had suspected that it could be a threading issue. I tried to fix it by os.execute(sleep) kinda function.. but that didn't work of course. This way it works. the lower number of frames however was creating unexpected issues. I was able to get it to work now. The script works almost too perfectly now. Sometimes it goes into infinite loops because of some silly conversation options. but thats okay we can always kill the script with kill-lua command. Let me post a new thread or update OP with the better working version.
It's not exactly threading... It's executed in one thread, but you need to give control back to df for a few frames. Also df you could program in an exact "wait till screen changes" function (by checking each frame if current screen changed).

I also suggest making a function that does all the simulate input (e.g. mash_buttons("A_TALK","SELECT","STRING_A116",... etc) so that it would be more tidy :)

1337G4mer

  • Bay Watcher
    • View Profile
Re: [WIP] Adventure Mode : Quick Quest Gathering Script
« Reply #8 on: January 06, 2016, 04:03:56 am »

Thanks, I had suspected that it could be a threading issue. I tried to fix it by os.execute(sleep) kinda function.. but that didn't work of course. This way it works. the lower number of frames however was creating unexpected issues. I was able to get it to work now. The script works almost too perfectly now. Sometimes it goes into infinite loops because of some silly conversation options. but thats okay we can always kill the script with kill-lua command. Let me post a new thread or update OP with the better working version.
It's not exactly threading... It's executed in one thread, but you need to give control back to df for a few frames. Also df you could program in an exact "wait till screen changes" function (by checking each frame if current screen changed).

I also suggest making a function that does all the simulate input (e.g. mash_buttons("A_TALK","SELECT","STRING_A116",... etc) so that it would be more tidy :)

That was just test code. I have a different way of doing that , I am avoiding mashing_buttons when I can. Just can't get rid of doing that for A_TALK and SELECT. Also, in the current script had to do some sanity check at times as the screen doesn't always change, maybe next I will look into "wait till screen changes". So other than those button mashes, rest of things I am doing via df memory structures. See the new code for fun :) I have updated OP. Appreciate your help :)
Logged

lethosor

  • Bay Watcher
    • View Profile
Re: Adventure Mode UI : Quick Quest Gathering Script
« Reply #9 on: January 06, 2016, 04:12:50 pm »

Instead of string.find(s, substring), you can use s:find(substring). (The same goes for other string methods.) I'd suggest looking at other DFHack scripts if you haven't - you can pick up a number of useful things from them, and they provide good examples of things like creating in-game screens, if that's something you want to try.
Logged
DFHack - Dwarf Manipulator (Lua) - DF Wiki talk

There was a typo in the siegers' campfire code. When the fires went out, so did the game.

Max™

  • Bay Watcher
  • [CULL:SQUARE]
    • View Profile
Re: Adventure Mode UI : Quick Quest Gathering Script
« Reply #10 on: January 06, 2016, 07:36:30 pm »

Ok yeah, this works amazingly well.

Spamming a tavern keeper (they tend to be really good for info) on a 129x129 world filled in over half of the sites of interest.


As lethosor said about the screens, I still gotta figure out where the error is in it, but I used a trick mifki suggested to pull up the adventurer name selection screen and use that to apply names to units.
Code: (names.lua) [Select]
local utils = require 'utils'

validArgs = validArgs or utils.invert({
 'help',
 'item',
 'unit',
 'first'
})

local args = utils.processArgs({...}, validArgs)

if args.help then
 print(
[[names.lua
arguments:
    -help
        print this help message
    -item
if viewing an item
    -unit
if viewing a unit
    -first Somename or "Some Names like This"
if a first name is desired, leave blank to clear current first name
]])
 return
end

if not df.viewscreen_layer_choose_language_namest:is_instance(dfhack.gui.getCurViewscreen()) then
choices = df.new(df.viewscreen_setupadventurest) ; choices.subscreen = 3 ; gui = require 'gui' ; gui.simulateInput(choices, 'A_RANDOM_NAME') ; gui.simulateInput(choices, 'A_CUST_NAME')
end

if args.item then
fact = dfhack.gui.getCurViewscreen().parent.item.general_refs[0].artifact_id
trg = df.artifact_record.find(fact)
end
 
if args.unit then
trg = dfhack.gui.getCurViewscreen().parent.unit
end

if args.first then
trg.name.first_name = args.first
end

function newName()
  newn = dfhack.gui.getCurViewscreen().name
oldn = trg.name
for k = 0,6 do
oldn.words[k] = newn.words[k]
  oldn.parts_of_speech[k] = newn.parts_of_speech[k]
end
  oldn.language = newn.language
  oldn.has_name = newn.has_name
end
newName()
The same sort of syntax for pulling up screens and stuff should work.

I was tinkering with the idea of pulling up the quest screen and having it fill stuff in on there, as an example.
Logged
Engraved here is a rendition of an image of the Dwarf Fortress learning curve. All craftsdwarfship is of the highest quality. It depicts an obsidian overhang which menaces with spikes of obsidian and tears. Carved on the overhang is an image of Toady One and the players. The players are curled up in a fetal position. Toady One is laughing. The players are burning.
The VectorCurses+1 tileset strikes the square set and the severed part sails off in an arc!

lethosor

  • Bay Watcher
    • View Profile
Re: Adventure Mode UI : Quick Quest Gathering Script
« Reply #11 on: January 06, 2016, 07:50:31 pm »

You can use df.viewscreen_setupadventurest:new(), actually. Also, you *need* to call choices:delete() when you're done with it, otherwise that code will leak 864 bytes of memory every time it's run (and ideally set choices to something else afterwards - if it's accessed after it's deleted, it's highly likely that DF will crash).

Edit: Well, delete() is crashing for just that viewscreen type. That's weird.
« Last Edit: January 06, 2016, 07:52:46 pm by lethosor »
Logged
DFHack - Dwarf Manipulator (Lua) - DF Wiki talk

There was a typo in the siegers' campfire code. When the fires went out, so did the game.

Max™

  • Bay Watcher
  • [CULL:SQUARE]
    • View Profile
Re: Adventure Mode UI : Quick Quest Gathering Script
« Reply #12 on: January 06, 2016, 08:03:29 pm »

Yeah, I tried the delete in there before as I recall, along with trying to have it set the name on exit, I mostly just wanted to offer an example of how to pull up specific screens like that for 1337G4mer to play with and see what other clever tricks the pop out.

That convo spam script is very robust btw, sometimes it hangs when the name of the creature rolls it over into the second page I think, but besides that it's smooth as hell.
Logged
Engraved here is a rendition of an image of the Dwarf Fortress learning curve. All craftsdwarfship is of the highest quality. It depicts an obsidian overhang which menaces with spikes of obsidian and tears. Carved on the overhang is an image of Toady One and the players. The players are curled up in a fetal position. Toady One is laughing. The players are burning.
The VectorCurses+1 tileset strikes the square set and the severed part sails off in an arc!

Warmist

  • Bay Watcher
  • Master of unfinished jobs
    • View Profile
Re: Adventure Mode UI : Quick Quest Gathering Script
« Reply #13 on: January 07, 2016, 03:10:25 am »

Also there is the straight way: just add sites to known site list :) (you need to find it first but it shouldn't be too hard). So if you want to know ALL the sites it's easiest but also must cheaty way :D

lethosor

  • Bay Watcher
    • View Profile
Re: Adventure Mode UI : Quick Quest Gathering Script
« Reply #14 on: January 07, 2016, 06:56:43 am »

It turns out the delete() crash is because delete (in this case) calls code in DF, while new() calls code in DFHack, and DFHack has the wrong layout for that screen. Hopefully that'll be fixed soon.
Logged
DFHack - Dwarf Manipulator (Lua) - DF Wiki talk

There was a typo in the siegers' campfire code. When the fires went out, so did the game.
Pages: [1] 2