Page 4 of 9
Re: createpath + getid + getpos together
Posted: Fri Oct 11, 2013 12:53 pm
by Bill D Cat
I tested this on about a dozen quests in Pioneers Colony and Logar and it worked great. So it has resolved the previous issues and implemented the fly() feature quite well. I think we have a winner here.
(Although, I still count 272 semicolons in the actual createpath.lua file

)
Re: createpath + getid + getpos together
Posted: Fri Oct 11, 2013 1:30 pm
by rock5

Well, I did remove them from the id and comments. I should at least remove them from all the lines it adds to the waypoint file. So when users look at and learn from their new waypoint files, they wont learn the bad habit of using semicolons.
And thanks for giving it a good testing.
Re: createpath + getid + getpos together
Posted: Sat Oct 12, 2013 7:14 pm
by Bill D Cat
Found a quest where the new QuestByName function crashes. This is repeatable for me over three characters on two servers so far.
Zone: Howling Mountains
Area: Pioneers Colony
NPC: Zily Ante
Quest: Fungus Pet
The function getChoice returns nil for variable "index".
Re: createpath + getid + getpos together
Posted: Sun Oct 13, 2013 12:26 am
by rock5
That's because that dialog is not the initial dialog where it lists the quests and options. That dialog is the one it shows when you select a quest. I remember now that some npcs do that, they jump straight into the quest. That's why there is no index, because you are just accepting the quest, not selecting the quest from a list.
I don't think that will interfere with anything else so all we have to do is add "if index ~= nil then" to the "OnClick_QuestListButton" lines.
Here is my latest version. The other main thing I changed is to make the getid/pos code only happen 2 times a second and restoring the main loop to 10ms. The setWindowName function was causing high cpu usage for me for the conhost.exe process.
The language file hasn't changed.
Re: createpath + getid + getpos together
Posted: Sun Oct 13, 2013 2:46 pm
by Bill D Cat
I've successfully used this new version in an effort to re-create my full walk-through of Howling Mountains. So far it has performed perfectly. The only time it crashed was when I was talking to the housemaid and used the QuestByName option to try and sign up for a house instead of using the ChoiceOption key. Overall, I'm feeling really good about how far this has come.
Re: createpath + getid + getpos together
Posted: Sun Oct 13, 2013 10:21 pm
by rock5
Line 708 was missing it's argument. It should be
Code: Select all
hf_type = sprintf("ChoiceOptionByName \'%s\'",name)
The only thing that still bothers me about this version is the different way you choose the option between choiceoption and choiceoptionbyname. Example, if the dialog looks like this
Code: Select all
! Accept quest 1
! Accept quest 2
? Complete quest 1
option 1
option 2
If you want to use the ChoiceOption method to choose the second option, you press 2 to select the second option. But if you want to use ChoiceOptionByName you have to choose 5 to select that option. I think having the 2 different ways to select the option is confusing.
The only solution I can think of at the moment is to make the ChoiceOption part of the '-' option. When you select an option using '-' it can ask you if you want to use ChoiceeOption or ChoiceOptionByName. I think that makes it more complex than it needs to be but I can't think of another way to do it yet.
Re: createpath + getid + getpos together
Posted: Mon Oct 14, 2013 1:44 am
by Bill D Cat
Does it have to be that complex? I mean if the function determines that it is a ChoiceOption rather than an Accept/CompleteQuest option, can't it just record it into the waypoint the same way option 6 does now? You could attach the name as a comment if you wanted. At least that wouldn't make it language dependent.
Re: createpath + getid + getpos together
Posted: Mon Oct 14, 2013 2:01 am
by rock5
The problem is each command has it's place. Neither is preferred to the other. ChoiceOption is good if the options don't change or move and it isn't language dependent. ChoiceOptionByName is good if the options might move or change and you can be sure it picks the right option. It can also handle npc options that don't work with ChoiceOption, such as the in-house maids dialog.
I guess most options don't move so in most cases you would use ChoiceOption. But if you use ChoiceOption because you think the options wont change but then they do, it will end up choosing the wrong option. I wish there was a way we could get the language string for the options so we could make ChoiceOptionByName language independent. I haven't thought of a way yet but I'll think on it some more and see if I can come up with something.
Re: createpath + getid + getpos together
Posted: Mon Oct 14, 2013 7:51 am
by rock5
Well I can quite easily get an options TEXT string from memory but there are 2 main problems with it.
1. Some option texts include substrings. Eg. "I want to be transported to the Varanas Guild Hall". The actual string in memory looks like this "I want to be transported to the [ZONE_VARANAS] Guild Hall". So if I searched in memory for the first string, it wont find it.
2. There can be multiple results for the same text. Of course if the text is the same then it doesn't matter which TEXT string is used but there is always a chance that in some languages they have different text for the different TEXT strings. So it's always best to pick what you think is the right TEXT string. The bot can't automatically do that.
So, even though this has been fun, I'm still not any closer to a solution.
If your interested here is the code I used to get the string from memory. It's just a few commands used with the commandline.
Code: Select all
Command> addressPtrsBase = memoryReadInt(getProc(), addresses.getTEXT)
Command> startloc = memoryReadInt(getProc(), addressPtrsBase + 0x268)
Command> endloc = memoryReadInt(getProc(), addressPtrsBase + 0x26C)
Command> st="Leave conversation" found = findPatternInProcess(getProc(),string.char(0)..st..string.char(0), string.rep("x",#st+2),startloc,endloc) print(memoryReadString(getProc(),found+1)) repeat found = found -1 if memoryReadByte(getProc(), found) == 0 then break end until false print(memoryReadString(getProc(),found+1))
Leave conversation
HOUSE_MAID_LEAVE_TALK
Re: createpath + getid + getpos together
Posted: Tue Oct 15, 2013 5:25 am
by rock5
Well, this has been fun. I managed to write a search function that can handle the substrings but it took 11s maximum which is too long. I've been tweaking it all day and got it down to about 1.2s. That's not too bad. I'm nearly tempted to add the function to the bot as a compliment to getTEXT but I got the time down to 1.2s by limiting the scan to just strings that start with "SC_" or "SO_" which I believe all the options start with. If I make a more general function that can find all strings, it still takes 6s max which is still too long I think. Plus there really isn't any use for a function except for in createpath. So I might just add the function there.
So with this working I think we could do away with the ChoiceOption altogether. We could move the "ByName" option over to key '6' to replace it.
Re: createpath + getid + getpos together
Posted: Tue Oct 15, 2013 2:08 pm
by Bill D Cat
I think I could handle a 2s delay during a createpath session, since I'm usually taking my time while creating the waypoint file anyway. The end result will be a functioning waypoint file that does not have that same delay since the information has already been gathered. And if you think it can effectively combine the two functions into one, then that will free up another key for whatever other option I dream up.

Re: createpath + getid + getpos together
Posted: Tue Oct 15, 2013 2:24 pm
by Bill D Cat
Bill D Cat wrote:AcceptQuestByName() is easy. The issue lies in CompleteQuestByName() where there are multiple quest rewards to pick from.
Have you looking into a way to detect when multiple quest reward options are offered and possibly prompting the user to select one? ie: CompleteQuestByName(123456,2) I know that I've created waypoints in the past where I forgot to add the second option to pick the reward, and the bot would just brute force its way through accepting the first option.
Re: createpath + getid + getpos together
Posted: Tue Oct 15, 2013 2:36 pm
by rock5
Forgot about that. Do we want it to detect the popup and then ask which to select? I think that would work.
Re: createpath + getid + getpos together
Posted: Tue Oct 15, 2013 2:58 pm
by Bill D Cat
I usually end up putting something like this in my waypoint files to pick based on the character's current primary class. I was originally using the class number for the check, but I think using the text makes it easier to debug later.
Code: Select all
if player.Class1 == CLASS_WARRIOR or player.Class1 == CLASS_KNIGHT then
CompleteQuestByName(123456,1) -- Chain Armor Option
elseif player.Class1 == CLASS_SCOUT or player.Class1 == CLASS_ROGUE then
CompleteQuestByName(123456,2) -- Leather Armor Option
elseif player.Class1 == CLASS_MAGE or player.Class1 == CLASS_PRIEST then
CompleteQuestByName(123456,1) -- Cloth Armor Option
end
The only issue with this type of check at higher levels is if there are some armor sets that you want to complete for a specific class. So editing the waypoint file after the fact to add the extra code isn't really a big deal. I was just wondering if the current version of createpath would have trouble with this type of quest turn in.
Re: createpath + getid + getpos together
Posted: Tue Oct 15, 2013 3:09 pm
by rock5
I think this is too much. There really is a lot to consider when selecting rewards; weapons, armor and other rewards. It's really the task for a dedicated userfunction but it's not easy, as previous attempts have shown. Just the ability to select an item should do for now. If a user wants to add code to help select the reward they will have to add it later.
Re: createpath + getid + getpos together
Posted: Tue Oct 15, 2013 5:27 pm
by Bill D Cat
I agree. Detect the pop-up and prompt for which reward to accept is the best option. The user could then even use the insert code option to add a comment about which rewards were offered. Then they could find the IDs of those items and equip them if they want, or just figure out which had the best NPC resale value if they would rather have the gold. I don't think most people will be using the bot like I am to create a full zone walk-through, but it is good to have the ability to do so.
Re: createpath + getid + getpos together
Posted: Tue Oct 15, 2013 10:56 pm
by rock5
Atually, that's a good point. If you want to change it latter you will need to know what the rewards were. Maybe I can add the rewards to the comment for future reference. Of course we are alread adding the quest name to the comment so it could get crowded. eg
Code: Select all
CompleteQuestByName(123456,1) -- questname -- (cloth hat name), leather hat name, chain hat name, plate hat name
Re: createpath + getid + getpos together
Posted: Wed Oct 16, 2013 12:16 am
by Bill D Cat
I'd rather have all the information available like you showed in your example code. It will all get edited anyway before it ever was released to the public. Now if you wanted to truly go overboard, you could include the ID's of the rewards as well

but I have an in-game macro that I use to look it up, so it's not really that important to me. Most of the quest rewards will have sequential IDs, so I can usually tell when I've found the right one if there are more than one item in the game with the same name.
Re: createpath + getid + getpos together
Posted: Wed Oct 16, 2013 12:35 am
by rock5
To tell you the truth, I haven't started that part of the code yet so I don't know what's possible. I'll let you know when I start on it.
About the function, which I'm calling 'getKeyString()', I think I might add it to the bot as a general function. I've been tidying it up today and realized that I could add optional range arguments so it can be used with createpath and still be fast, and it can be used by users to find key strings for themselves using the whole range.
User would use
Which would return a table of matches.
Createpath will use
Code: Select all
getKeyString("the string", true, "SC_", "SP")
Which will return the first match and search from "SC_" to "SP" which will include all "SC_" and "SO_" key strings.
BTW, I'm calling the 2 strings 'key strings' and 'text' to reduce confusion. Eg. "AC_ITEMTYPENAME_0" is the key string and "Weapons" is the text.
Re: createpath + getid + getpos together
Posted: Fri Oct 18, 2013 4:55 am
by rock5
I think this is the best I'm going to get it.
Code: Select all
function getKeyStrings(text, returnfirst, keytextstart, keytextend)
local proc = getProc()
local addressPtrsBase = memoryReadInt(proc, addresses.getTEXT)
local startloc = memoryReadInt(proc, addressPtrsBase + 0x268)
local endloc = memoryReadInt(proc, addressPtrsBase + 0x26C)
local results = {}
-- Returns the key string of the text found at address 'address'. 'address' points to the 0 before the text.
local function getkeystr(address)
repeat
address = address -1
until memoryReadByte(proc, address) == 0
if address < startloc then
address = startloc
else
address = address + 1
end
return memoryReadString(proc, address)
end
-- Returns nil if no results and prints them if there are results. Only applies to result tables.
local function getResults()
if #results == 0 then
return
else
table.print(results)
return results
end
end
--== First search for an exact match with findPatternInProcess because it's so fast.
local rangestart = startloc
local found, tmpkey
repeat
found = findPatternInProcess(proc, string.char(0)..text..string.char(0), string.rep("x",#text+2), rangestart, endloc-rangestart);
if found ~= 0 then
tmpkey = getkeystr(found)
if (keytextstart == nil or tmpkey > keytextstart) and (keytextend == nil or keytextend > tmpkey) then
if returnfirst == true then
return tmpkey
else
table.insert(results, tmpkey)
end
end
rangestart = found + #text+1 -- continue from here to find more matches
if rangestart >= endloc then break end
end
until found == 0
-- If results found, return them.
if #results > 0 then
return getResults()
end
--== Now search for text with substrings that match.
-- Get the start address
local address = startloc
if keytextstart then
address = findPatternInProcess(proc, string.char(0)..keytextstart, string.rep("x",#keytextstart+1), startloc, endloc-startloc)+1;
end
local tmpkey, tmptext, tmpstrpat = "","",""
local translatedSubTEXT
repeat repeat -- Second 'repeat' is break loop
-- Get key string and text
tmpkey = memoryReadString(proc, address) -- key string
address = address + #tmpkey + 1
tmptext = memoryReadString(proc, address) -- text
address = address + #tmptext + 1
-- end of specified key string range
if keytextend and tmpkey > keytextend then
return getResults()
end
-- only search key strings with substrings.
if not string.find(tmptext,"%[") then break end
-- Skip text with variables, eg. [$var], because they wont match anyway.
if string.find(tmptext,"%[%$") then break end
-- Skip text with %s or %d because they wont match anyway.
if string.find(tmptext,"%%[sd]") then break end
-- Create find pattern
tmpstrpat = string.gsub(tmptext,"[%(%)%-%+%*%?]","%%%1") -- prefix special characters with '%'
tmpstrpat = string.gsub(tmpstrpat,"%[.-%]",".*") -- change substrings to '.*'
if string.find(tmpstrpat,"%w") and string.find(text, tmpstrpat) then
for subTEXT in string.gmatch(tmptext,"%[([^%$].-)%]") do
if tonumber(subTEXT) then -- Must be id
translatedSubTEXT = GetIdName(tonumber(subTEXT))
else
translatedSubTEXT = getTEXT(subTEXT)
end
if translatedSubTEXT ~= nil and translatedSubTEXT ~= subTEXT and not string.find(translatedSubTEXT,"%%") then
tmptext = string.gsub(tmptext, "%["..subTEXT.."%]", translatedSubTEXT, 1)
end
end
if tmptext == text then
if returnfirst == true then
return tmpkey
else
table.insert(results, tmpkey)
end
end
end
until true until address >= endloc-1
return getResults()
end
I had to filter the results a bit to get the speed but it should find most text strings. Exact string matches should be almost instantaneous. Texts that use substrings take about 7s maximum. That's on my system. Createpath will filter the results and use the first result so should take about 1s.
I guess I'll get back to createpath now.