· 9 years ago · Nov 01, 2016, 06:24 PM
1##quiet
2## vim: set ft=ruby:
3=begin
4
5 SBounty by SpiffyJr (spiffyjr@gmail.com)
6
7 Does all bounties except gem, escorts, and bandits. Gems should
8 be handled via your loot with stockpiling.
9
10 Feel free to send donations or Gift's of Adventure.
11
12 author: spiffyjr
13 email: theman@spiffyjr.me
14 name: SpiffyBounty
15 tags: bounty
16 version: 1.0
17
18=end
19
20CharSettings[:enable_cull] = CharSettings[:enable_cull].nil? ? true : CharSettings[:enable_cull]
21CharSettings[:enable_dangerous] = CharSettings[:enable_dangerous].nil? ? true : CharSettings[:enable_dangerous]
22CharSettings[:enable_forage] = CharSettings[:enable_forage].nil? ? true : CharSettings[:enable_forage]
23CharSettings[:enable_loot] = CharSettings[:enable_loot].nil? ? true : CharSettings[:enable_loot]
24CharSettings[:enable_rescue] = CharSettings[:enable_rescue].nil? ? true : CharSettings[:enable_rescue]
25CharSettings[:enable_search] = CharSettings[:enable_search].nil? ? true : CharSettings[:enable_search]
26CharSettings[:enable_bandit] = CharSettings[:enable_bandit].nil? ? false : CharSettings[:enable_bandit]
27CharSettings[:enable_skin] = CharSettings[:enable_skin].nil? ? true : CharSettings[:enable_skin]
28CharSettings[:enable_expedite] = CharSettings[:enable_expedite].nil? ? true : CharSettings[:enable_expedite]
29
30CharSettings[:enable_hunt_complete] = CharSettings[:enable_hunt_complete].nil? ? true : CharSettings[:enable_hunt_complete]
31
32CharSettings[:pre_search_commands] ||= ['store all']
33CharSettings[:post_search_commands] ||= ['gird']
34CharSettings[:forage_retry_delay] ||= 300
35CharSettings[:loot_script] ||= 'sloot'
36CharSettings[:turn_in_percent] ||= 95
37
38CharSettings[:should_hunt_mind] ||= 75
39CharSettings[:should_hunt_mana] ||= 0
40CharSettings[:should_hunt_spirit] ||= 7
41CharSettings[:hunt_pre_commands] ||= ['gird']
42CharSettings[:should_rest_mind] ||= 100
43CharSettings[:should_rest_mana] ||= 0
44CharSettings[:should_rest_encum] ||= 20
45CharSettings[:should_rest_wounded] ||= StringProc.new 'bleeding? || percenthealth <= 50 || ([Wounds.head, Scars.head].max >= 2) || ([Wounds.nsys, Scars.nsys].max >= 2) || ([Wounds.leftArm, Wounds.leftHand, Wounds.rightArm, Wounds.rightHand, Scars.leftArm, Scars.leftHand, Scars.rightArm, Scars.rightHand].max >= 3) || ( ([Wounds.leftArm, Wounds.leftHand, Scars.leftArm, Scars.leftHand].max >= 2) or ([Wounds.rightArm, Wounds.rightHand, Scars.leftArm, Scars.leftHand].max >= 2) )'
46
47CharSettings[:rest_in_commands] ||= ['go table, sit']
48CharSettings[:rest_out_commands] ||= ['stand', 'out']
49CharSettings[:rest_pre_commands] ||= ['store all']
50CharSettings[:rest_scripts] ||= ['useherbs --buy-missing=on', 'sloot sell', 'waggle']
51CharSettings[:rest_sleep_interval] ||= 30
52
53CharSettings[:locations] ||= {}
54
55$sbounty_rest = false
56$sbounty_rest_reason = nil
57in_rest_area = false
58rest_reason = nil
59hunt_reason = nil
60can_do_bounty_cache = nil
61expedite_left = true
62last_forage_attempt = 0
63last_forage_delay = 300
64
65if UserVars.lootsack.nil? or UserVars.lootsack.empty?
66 echo '** lootsack has not been set, set it with ;set change lootsack [container]'
67 exit
68end
69
70if CharSettings[:enable_skin] and (UserVars.skinsack.nil? or UserVars.skinsack.empty?)
71 echo '** skinsack has not been set, set it with ;set change skinsack [container]'
72 exit
73end
74
75lootsack = GameObj.inv.find { |i| i.noun =~ /#{UserVars.lootsack}/ } or GameObj.inv.find { |i| i.name =~ /#{UserVars.lootsack}/ }
76skinsack = GameObj.inv.find { |i| i.noun =~ /#{UserVars.skinsack}/ } or GameObj.inv.find { |i| i.name =~ /#{UserVars.lootsack}/ }
77
78if lootsack.nil?
79 echo '** failed to find your lootsack, set it with ;set change lootsack [container]'
80 exit
81end
82
83if CharSettings[:enable_skin] and skinsack.nil?
84 echo '** failed to find your skinsack, set it with ;set change skinsack [container]'
85 exit
86end
87
88# You have been tasked to recover a peridot-inset pewter ring that an unfortunate citizen lost after being attacked by a storm giant in the Upper Trollfang near Wehnimer's Landing. The heirloom can be identified by the initials MG engraved upon it. Hunt down the creature and LOOT the item from its corpse.
89
90
91bounty_patterns = {
92 'none' => '^You are not currently assigned a task\.',
93
94 # help
95 'help_bandit' => 'It appears they have a bandit problem',
96 'help_creature' => 'It appears they have a creature problem they\'d like you to solve\.',
97 'help_resident' => 'It appears that a local resident urgently needs our help in some matter\.',
98 'help_heirloom' => 'It appears they need your help in tracking down some kind of lost heirloom\.',
99 'help_gemdealer' => 'The local gem dealer, .*, has an order to fill and wants our help\.',
100 'help_herbalist' => 'local herbalist|local healer|local alchemist',
101 'help_furrier' => 'The local furrier',
102
103 # in progress
104 'task_bandit' => '^You have been tasked to suppress bandit activity (?:in|on|near) (?:the )?(.*)\s(?:near|between|under|\.)',
105 'task_escort' => '^You have made contact with the child',
106 'task_dangerous' => 'You have been tasked to hunt down and kill a particularly dangerous (.*) that has established a territory (?:in|on|near) (?:the )?(.*)(?:\s(?:near|between|under)|\.)\s+You can',
107 'task_provoked' => 'You have been tasked to hunt down and kill a particularly dangerous (.*) that has established a territory (?:in|on|near) (?:the )?([\w\'\s-]+)(?:\s(?:in|on|near|\.).*)\s+ You have provoked',
108 'task_dealer' => '^The(?: local)? gem dealer',
109 'task_forage' => 'concoction that requires (?:a|an|some) (.*) found (?:in|on|near) (?:the )?(.*?)(?: near| between| under|\.).*These samples must be in pristine condition\.\s+You have been tasked to retrieve (\d+) samples\.',
110 'task_cull' => 'You have been tasked to suppress (.*) activity (?:in|on) (?:the )?(.*)(?:\s(?:near|between|under)|\.)',
111 'task_search' => 'unfortunate citizen lost after being attacked by (?:a|an) (.*) (?:in|on|near) (?:the )?(.*)\s(?:near|between|under|\.).*\. SEARCH',
112 'task_heirloom' => 'unfortunate citizen lost after being attacked by (?:a|an) (.*) (?:in|on|near) (?:the )?(.*)\s(?:near|between|under|\.).*LOOT',
113 'task_found' => 'You have located the heirloom',
114 'task_skin' => '^You have been tasked to retrieve (\d+|\w+)s? (.*) of at least .*\. You can SKIN them off the corpse of (?:a|an) (.*) or ',
115 'task_rescue' => 'A local divinist has had visions of the child fleeing from (?:a|an) (.*) (?:in|on) (?:the )?(.*) (?:near|between|under)',
116
117 # fail
118 'fail_child' => 'The child you were tasked to rescue is gone and your task is failed\.',
119
120 # success
121 'success' => '^You have succeeded in your task and can return',
122 'success_guard' => '^You succeeded in your task and should report back to',
123 'success_heirloom' => '^You have located the heirloom and should bring it back .*\.$',
124}
125
126setup = proc {
127 script_name = script.name
128
129 action = nil
130 window = nil
131 notebook = nil
132
133 locations = CharSettings[:locations].dup
134 location = nil
135
136 error = proc { |msg|
137 dlg = Gtk::Dialog.new(
138 'Error!',
139 window,
140 Gtk::Dialog::DESTROY_WITH_PARENT,
141 [ Gtk::Stock::OK, Gtk::Dialog::RESPONSE_NONE ]
142 )
143 dlg.resize 200,100
144 dlg.signal_connect 'response' do dlg.destroy end
145 dlg.vbox.add(Gtk::Label.new(msg))
146 dlg.show_all
147 }
148
149 widgets = {
150 :gui_widgets => {
151 # buttons
152 :close => Gtk::Button.new('Close'),
153 :create => Gtk::Button.new('Create'),
154 :delete => Gtk::Button.new('Delete'),
155 :save => Gtk::Button.new('Save & Close'),
156
157 # combos
158 :locations => Gtk::ComboBox.new,
159
160 # entries
161 :new_name => Gtk::Entry.new,
162 },
163
164 :location_widgets => {
165 # check buttons
166 :enable_bounty_only => Gtk::CheckButton.new('Only attack bouny critters'),
167 :enable_hunting_rotation => Gtk::CheckButton.new('In hunting rotation'),
168 :enable_search_only => Gtk::CheckButton.new('Only search here (no hunting)'),
169
170 #entries
171 :location => Gtk::Entry.new,
172 :skins => Gtk::Entry.new,
173 :targets => Gtk::Entry.new,
174 :room => Gtk::Entry.new,
175 :boundaries => Gtk::Entry.new,
176 },
177
178 # check buttons
179 :enable_cull => Gtk::CheckButton.new('Cull critters').set_active(CharSettings[:enable_cull]),
180 :enable_dangerous => Gtk::CheckButton.new('Dangerous critter').set_active(CharSettings[:enable_dangerous]),
181 :enable_forage => Gtk::CheckButton.new('Forage herbs').set_active(CharSettings[:enable_forage]),
182 :enable_loot => Gtk::CheckButton.new('Loot heirloom').set_active(CharSettings[:enable_loot]),
183 :enable_rescue => Gtk::CheckButton.new('Rescue child').set_active(CharSettings[:enable_rescue]),
184 :enable_search => Gtk::CheckButton.new('Search heirloom').set_active(CharSettings[:enable_search]),
185 :enable_bandit => Gtk::CheckButton.new('Bandits').set_active(CharSettings[:enable_bandit]),
186 :enable_skin => Gtk::CheckButton.new('Skin critters').set_active(CharSettings[:enable_skin]),
187 :enable_expedite => Gtk::CheckButton.new('Expedite bounties').set_active(CharSettings[:enable_expedite]),
188
189 :enable_hunt_complete => Gtk::CheckButton.new('Hunt until complete?').set_active(CharSettings[:enable_hunt_complete]),
190
191 # entries
192 :pre_search_commands => Gtk::Entry.new.set_text(CharSettings[:pre_search_commands].collect { |a| a.strip }.join(',')),
193 :post_search_commands => Gtk::Entry.new.set_text(CharSettings[:post_search_commands].collect { |a| a.strip }.join(',')),
194 :forage_retry_delay => Gtk::Entry.new.set_text(CharSettings[:forage_retry_delay].to_s),
195 :loot_script => Gtk::Entry.new.set_text(CharSettings[:loot_script].to_s),
196 :turn_in_percent => Gtk::Entry.new.set_text(CharSettings[:turn_in_percent].to_s),
197
198 :should_hunt_mind => Gtk::Entry.new.set_text(CharSettings[:should_hunt_mind].to_s),
199 :should_hunt_mana => Gtk::Entry.new.set_text(CharSettings[:should_hunt_mana].to_s),
200 :should_hunt_spirit => Gtk::Entry.new.set_text(CharSettings[:should_hunt_spirit].to_s),
201
202 :hunt_pre_commands => Gtk::Entry.new.set_text(CharSettings[:hunt_pre_commands].collect { |a| a.strip }.join(',')),
203 :hunt_commands_a => Gtk::Entry.new.set_text(CharSettings[:hunt_commands_a].collect { |a| a.strip }.join(',')),
204 :hunt_commands_b => Gtk::Entry.new.set_text(CharSettings[:hunt_commands_b].collect { |a| a.strip }.join(',')),
205 :hunt_commands_c => Gtk::Entry.new.set_text(CharSettings[:hunt_commands_c].collect { |a| a.strip }.join(',')),
206
207 :should_rest_mana => Gtk::Entry.new.set_text(CharSettings[:should_rest_mana].to_s),
208 :should_rest_mind => Gtk::Entry.new.set_text(CharSettings[:should_rest_mind].to_s),
209 :should_rest_encum => Gtk::Entry.new.set_text(CharSettings[:should_rest_encum].to_s),
210 :should_rest_wounded => Gtk::Entry.new.set_text(CharSettings[:should_rest_wounded]._dump),
211
212 :rest_in_commands => Gtk::Entry.new.set_text(CharSettings[:rest_in_commands].collect { |a| a.strip }.join(',')),
213 :rest_out_commands => Gtk::Entry.new.set_text(CharSettings[:rest_out_commands].collect { |a| a.strip }.join(',')),
214 :rest_pre_commands => Gtk::Entry.new.set_text(CharSettings[:rest_pre_commands].collect { |a| a.strip }.join(',')),
215 :rest_room => Gtk::Entry.new.set_text(CharSettings[:rest_room]),
216 :rest_scripts => Gtk::Entry.new.set_text(CharSettings[:rest_scripts].collect { |a| a.strip }.join(','))
217 }
218
219 Gtk.queue {
220 update_disabled = proc {
221 widgets[:location_widgets].each { |key, widget|
222 if location.nil?
223 widget.set_sensitive(false)
224 else
225 widget.set_sensitive(true)
226 end
227 }
228 }
229
230 populate_locations = proc {
231 loop {
232 break if not widgets[:gui_widgets][:locations].model.iter_first
233 widgets[:gui_widgets][:locations].remove_text(0)
234 }
235
236 locations.keys.sort.each { |text|
237 widgets[:gui_widgets][:locations].append_text(text)
238 }
239 }
240
241 save_location = proc {
242 widgets[:location_widgets].each { |key, widget|
243 if widget.class == Gtk::CheckButton
244 value = widget.active?
245 elsif [:skins, :targets, :boundaries].include?(key)
246 value = widget.text.strip.split(',').collect { |item| item.strip }
247 else
248 value = widget.text.strip
249 end
250
251 locations[location][key] = value
252 }
253 }
254
255 attach_signals = proc {
256 widgets[:gui_widgets][:close].signal_connect 'clicked' do action = :close end
257 widgets[:gui_widgets][:save].signal_connect 'clicked' do action = :save; save_location.call end
258
259 widgets[:gui_widgets][:create].signal_connect 'clicked' do
260 name = widgets[:gui_widgets][:new_name].text.strip
261
262 if name.nil? or name.empty? or name.length < 3
263 error.call 'You need to enter a name!'
264 elsif locations.keys.find { |n| n.downcase.strip == name.downcase.strip }
265 error.call 'A location with that name already exists!'
266 else
267 locations[name] = {}
268
269 widgets[:gui_widgets][:new_name].set_text ""
270
271 populate_locations.call
272 update_disabled.call
273 end
274 end
275
276 widgets[:gui_widgets][:delete].signal_connect 'clicked' do
277 Gtk.queue {
278 if widgets[:gui_widgets][:locations].active == -1
279 error.call 'You do not have a location selected!'
280 else
281 locations.delete(widgets[:gui_widgets][:locations].active_text)
282 widgets[:gui_widgets][:locations].remove_text(widgets[:gui_widgets][:locations].active)
283
284 widgets[:location_widgets].each { |key, widget|
285 if widget.class == Gtk::CheckButton
286 widget.set_active(false)
287 else
288 widget.set_text("")
289 end
290 }
291 end
292 }
293 end
294
295 widgets[:gui_widgets][:locations].signal_connect 'changed' do
296 Gtk.queue {
297 save_location.call
298
299 location = widgets[:gui_widgets][:locations].active_text
300
301 if locations[location]
302 widgets[:location_widgets].each { |key, widget|
303 if widget.class == Gtk::CheckButton
304 widget.set_active(!!locations[location][key])
305 elsif [:skins, :targets, :boundaries].include?(key)
306 widget.set_text(locations[location][key].join(','))
307 else
308 widget.set_text(locations[location][key].strip)
309 end
310 }
311 end
312
313 update_disabled.call
314 }
315 end
316 }
317
318 # main window
319 window = Gtk::Window.new
320 window.border_width = 5
321 window.keep_above = true
322 window.resizable = true
323 window.resize 500, 300
324 window.set_window_position Gtk::Window::POS_CENTER
325
326 # main notebook (tabs)
327 notebook = Gtk::Notebook.new
328 notebook.show_border = true
329
330 vbox_main = Gtk::VBox.new
331 vbox_main.pack_start notebook
332
333 # sbounty options
334 vbox = Gtk::VBox.new
335
336 frm = Gtk::Frame.new('Locations').set_border_width(3)
337 tbl = Gtk::Table.new(7, 4, true).set_border_width(3)
338
339 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('New name: ')), 0, 1, 0, 1
340 tbl.attach widgets[:gui_widgets][:new_name], 1, 3, 0, 1
341 tbl.attach widgets[:gui_widgets][:create], 3, 4, 0, 1
342
343 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('Locations: ')), 0, 1, 1, 2
344 tbl.attach widgets[:gui_widgets][:locations], 1, 3, 1, 2
345 tbl.attach widgets[:gui_widgets][:delete], 3, 4, 1, 2
346
347 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('Location: ')), 0, 1, 3, 4
348 tbl.attach widgets[:location_widgets][:location], 1, 2, 3, 4
349 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('Skins: ')), 2, 3, 3, 4
350 tbl.attach widgets[:location_widgets][:skins], 3, 4, 3, 4
351
352 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('Targets: ')), 0, 1, 4, 5
353 tbl.attach widgets[:location_widgets][:targets], 1, 2, 4, 5
354
355 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('Room #: ')), 0, 1, 5, 6
356 tbl.attach widgets[:location_widgets][:room], 1, 2, 5, 6
357 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('Boundaries: ')), 2, 3, 5, 6
358 tbl.attach widgets[:location_widgets][:boundaries], 3, 4, 5, 6
359
360 tbl.attach widgets[:location_widgets][:enable_hunting_rotation], 0, 1, 6, 7
361 tbl.attach widgets[:location_widgets][:enable_bounty_only], 1, 2, 6, 7
362 tbl.attach widgets[:location_widgets][:enable_search_only], 2, 3, 6, 7
363
364 frm.add(tbl)
365 vbox.pack_start(frm)
366
367 frm = Gtk::Frame.new('Bounties').set_border_width(3)
368 tbl = Gtk::Table.new(5, 4, true).set_border_width(3)
369
370 tbl.attach widgets[:enable_cull], 0, 1, 0, 1
371 tbl.attach widgets[:enable_dangerous], 1, 2, 0, 1
372 tbl.attach widgets[:enable_rescue], 2, 3, 0, 1
373 tbl.attach widgets[:enable_skin], 3, 4, 0, 1
374
375 tbl.attach widgets[:enable_loot], 0, 1, 1, 2
376 tbl.attach widgets[:enable_search], 1, 2, 1, 2
377 tbl.attach widgets[:enable_forage], 2, 3, 1, 2
378 tbl.attach widgets[:enable_bandit], 3, 4, 1, 2
379
380 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('turn in when mind >= ')), 0, 1, 2, 3
381 tbl.attach widgets[:turn_in_percent], 1, 2, 2, 3
382 tbl.attach widgets[:enable_expedite], 2, 3, 2, 3
383 tbl.attach widgets[:enable_hunt_complete], 3, 4, 2, 3
384
385 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('pre-search commands: ')), 0, 1, 3, 4
386 tbl.attach widgets[:pre_search_commands], 1, 2, 3, 4
387 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('post-search commands: ')), 2, 3, 3, 4
388 tbl.attach widgets[:post_search_commands], 3, 4, 3, 4
389
390 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('forage retry delay: ')), 0, 1, 4, 5
391 tbl.attach widgets[:forage_retry_delay], 1, 2, 4, 5
392 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('loot script: ')), 2, 3, 4, 5
393 tbl.attach widgets[:loot_script], 3, 4, 4, 5
394
395 frm.add(tbl)
396 vbox.pack_start(frm)
397
398 notebook.append_page vbox, Gtk::Label.new('Locations / Options')
399
400 # default options
401 vbox = Gtk::VBox.new
402
403 frm = Gtk::Frame.new('Should Rest').set_border_width(3)
404 tbl = Gtk::Table.new(3, 4, true).set_border_width(3)
405
406 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('when mind % >= ')), 0, 1, 0, 1
407 tbl.attach widgets[:should_rest_mind], 1, 2, 0, 1
408 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('or mana <= ')), 2, 3, 0, 1
409 tbl.attach widgets[:should_rest_mana], 3, 4, 0, 1
410
411 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('or encumbrance % >= ')), 0, 1, 1, 2
412 tbl.attach widgets[:should_rest_encum], 1, 2, 1, 2
413
414 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('or wounded eval: ')), 0, 1, 2, 3
415 tbl.attach widgets[:should_rest_wounded], 1, 4, 2, 3
416
417 frm.add(tbl)
418 vbox.pack_start(frm)
419
420 frm = Gtk::Frame.new('Resting').set_border_width(3)
421 tbl = Gtk::Table.new(2, 4, true).set_border_width(3)
422
423 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('room #: ')), 0, 1, 0, 1
424 tbl.attach widgets[:rest_room], 1, 2, 0, 1
425 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('pre-rest commands: ')), 2, 3, 0, 1
426 tbl.attach widgets[:rest_pre_commands], 3, 4, 0, 1
427
428 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('enter commands: ')), 0, 1, 1, 2
429 tbl.attach widgets[:rest_in_commands], 1, 2, 1, 2
430 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('exit commands: ')), 2, 3, 1, 2
431 tbl.attach widgets[:rest_out_commands], 3, 4, 1, 2
432
433 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('scripts: ')), 0, 1, 2, 3
434 tbl.attach widgets[:rest_scripts], 1, 4, 2, 3
435
436 frm.add(tbl)
437 vbox.pack_start(frm)
438
439 frm = Gtk::Frame.new('Should Hunt').set_border_width(3)
440 tbl = Gtk::Table.new(2, 4, true).set_border_width(3)
441
442 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('when mind % <= ')), 0, 1, 0, 1
443 tbl.attach widgets[:should_hunt_mind], 1, 2, 0, 1
444 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('and mana >= ')), 2, 3, 0, 1
445 tbl.attach widgets[:should_hunt_mana], 3, 4, 0, 1
446
447 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('and spirit >= ')), 0, 1, 1, 2
448 tbl.attach widgets[:should_hunt_spirit], 1, 2, 1, 2
449
450 frm.add(tbl)
451 vbox.pack_start(frm)
452
453 frm = Gtk::Frame.new('Hunting').set_border_width(3)
454 tbl = Gtk::Table.new(2, 4, true).set_border_width(3)
455
456 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('Pre-hunt commands: ')), 0, 1, 0, 1
457 tbl.attach widgets[:hunt_pre_commands], 1, 2, 0, 1
458 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('Commands (a): ')), 2, 3, 0, 1
459 tbl.attach widgets[:hunt_commands_a], 3, 4, 0, 1
460
461 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('Commands (b): ')), 0, 1, 1, 2
462 tbl.attach widgets[:hunt_commands_b], 1, 2, 1, 2
463 tbl.attach Gtk::Alignment.new(1, 0, 0, 1).add(Gtk::Label.new('Commands (c): ')), 2, 3, 1, 2
464 tbl.attach widgets[:hunt_commands_c], 3, 4, 1, 2
465
466 frm.add(tbl)
467 vbox.pack_start(frm)
468
469 notebook.append_page vbox, Gtk::Label.new('Resting / Hunting')
470
471 # main window widgets
472 vbox = Gtk::VBox.new
473 vbox.pack_start Gtk::Alignment.new(0, 1, 0, 0), false
474
475 hbox = Gtk::HBox.new
476 hbox.add widgets[:gui_widgets][:save]
477 hbox.add widgets[:gui_widgets][:close]
478
479 halign = Gtk::Alignment.new 1, 0, 0, 0
480 halign.add hbox
481
482 vbox.pack_start halign
483 vbox_main.pack_start vbox
484
485 # create the window
486 attach_signals.call
487 update_disabled.call
488 populate_locations.call
489
490 window.add vbox_main
491 window.show_all
492 }
493
494 before_dying { Gtk.queue { window.destroy unless window.nil? } }
495
496 sleep 0.10 while action.nil?
497
498 case action
499 when :save
500 CharSettings[:locations] = locations
501
502 widgets.each { |key, widget|
503 next if [:location_widgets, :gui_widgets].include?(key)
504
505 if widget.class == Gtk::CheckButton
506 value = widget.active?
507 elsif [:pre_search_commands, :post_search_commands, :hunt_pre_commands, :hunt_commands_a, :hunt_commands_b, :hunt_commands_c, :rest_in_commands, :rest_out_commands, :rest_pre_commands, :rest_scripts].include?(key)
508 value = widget.text.strip.split(',').collect { |item| item.strip }
509 elsif key == :should_rest_wounded
510 value = StringProc.new widget.text.strip
511 else
512 value = widget.text.strip
513 end
514
515 CharSettings[key] = value
516 }
517 end
518}
519
520error = proc { |msg|
521 echo "** #{msg}"
522 exit
523}
524
525print = proc { |msg|
526 echo "-- #{msg}"
527}
528
529clean_skin = proc { |name|
530 name.strip.downcase.gsub(/s$/, '').gsub(/teeth/, 'tooth').gsub(/hooves?/, 'hoof')
531}
532
533has_skins = proc {
534 if bounty? =~ /#{bounty_patterns['task_skin']}/
535 count = $1.to_i
536 skin = $2.downcase
537
538 skinsack.contents.find_all { |item| item.name =~ /#{clean_skin.call(skin)}/i }.size >= (count + 3)
539 else
540 false
541 end
542}
543
544is_bounty = proc { |types|
545 result = []
546 if types.class != Array
547 types = [types]
548 end
549 types.each { |type| result.push(bounty_patterns[type]) if not bounty_patterns[type].nil? }
550
551 !(bounty? =~ /#{result.join('|')}/).nil?
552}
553
554get_bounty_location = proc {
555 location = target = nil
556
557 # remove search only locations for hunting areas
558 locations = CharSettings[:locations].dup
559 if is_bounty.call ['task_skin', 'task_heirloom', 'task_dangerous', 'task_cull', 'task_rescue']
560 locations.delete_if { |name, data| data[:enable_search_only] }
561 end
562
563 if bounty? =~ /#{bounty_patterns['task_skin']}/
564 # remember: don't swap the order or the global $ vars are lost!
565 target = $3.strip.downcase
566 skin = clean_skin.call($2)
567 location = locations.find { |name, data| $SBOUNTY_CLEAN_TARGETS.call(data[:targets]).find { |t| target =~ /#{t}/i } and data[:skins].find { |s| skin =~ /#{s.strip.downcase}/i }}
568 else
569 bounty_patterns.each { |key,value|
570 if bounty? =~ /#{value}/i
571 target = $1
572 location = $2
573
574 break
575 end
576 }
577
578 location = location.downcase.strip
579 location = locations.find { |name, data|
580 location =~ /#{data[:location].strip}/i and $SBOUNTY_CLEAN_TARGETS.call(data[:targets]).find {
581 |t| target =~ /#{t}/i
582 }
583 }
584 end
585
586 if location
587 name = location[0].dup
588 data = location[1].to_hash.dup
589
590 if target
591 targets = data[:targets].dup
592 cleaned = $SBOUNTY_CLEAN_TARGETS.call(data[:targets])
593 index = cleaned.index(cleaned.find { |t| target =~ /#{t}/i })
594 target = targets[index]
595
596 if is_bounty.call 'task_provoked' and target !~ /ancient|grizzled/i
597 new_target = "(?:ancient|grizzled).*#{cleaned[index].split(' ').last}".downcase
598 targets[target.index(target)] = new_target
599 target = new_target
600 end
601 end
602
603 if data[:enable_bounty_only]
604 targets = [ target ]
605 end
606
607 data[:targets] = targets
608
609 [name, data]
610 else
611 print.call 'could not find bounty location'
612 nil
613 end
614}
615
616get_herb_rooms = proc { |location, herb|
617 rooms = Room.list.find_all { |room| room.location =~ /^(?:the)?\s*#{location.strip}/i and room.tags.find { |tag| tag =~ /#{herb}/i } }
618 rooms = rooms.collect { |r| r.id }
619
620 previous, shortest_distances = Map.dijkstra(CharSettings[:rest_room])
621 rooms.delete_if { |room| shortest_distances[room].nil? or shortest_distances[room] > 60 }
622}
623
624can_turn_in = proc {
625 is_bounty.call ['success', 'success_guard', 'success_heirloom'] and (CharSettings[:turn_in_percent].nil? or percentmind >= CharSettings[:turn_in_percent].to_i and checkmind !~ /saturated/)
626}
627
628can_do_bounty = proc {
629 if not can_do_bounty_cache.nil?
630 ; # intentionally left blank
631 elsif is_bounty.call ['success', 'success_heirloom', 'success_guard']
632 can_do_bounty_cache = true
633 elsif is_bounty.call ['task_bandit'] and CharSettings[:enable_bandit]
634 can_do_bounty_cache = true
635 elsif ((is_bounty.call 'task_search' and CharSettings[:enable_search] and get_bounty_location.call) or (is_bounty.call 'task_heirloom' and CharSettings[:enable_loot] and get_bounty_location.call))
636 can_do_bounty_cache = true
637 elsif bounty? =~ /#{bounty_patterns['task_forage']}/ and CharSettings[:enable_forage] and not get_herb_rooms.call($2, $1).empty?
638 can_do_bounty_cache = true
639 elsif is_bounty.call 'task_skin' and CharSettings[:enable_skin] and get_bounty_location.call
640 can_do_bounty_cache = true
641 elsif is_bounty.call ['task_provoked', 'task_dangerous'] and CharSettings[:enable_dangerous] and get_bounty_location.call
642 can_do_bounty_cache = true
643 elsif is_bounty.call 'task_cull' and CharSettings[:enable_cull] and get_bounty_location.call
644 can_do_bounty_cache = true
645 elsif (is_bounty.call 'task_escort' and CharSettings[:enable_rescue]) or (is_bounty.call 'task_rescue' and CharSettings[:enable_rescue] and get_bounty_location.call)
646 can_do_bounty_cache = true
647 else
648 can_do_bounty_cache = false
649 end
650
651 can_do_bounty_cache
652}
653
654should_hunt = proc {
655 if (not can_do_bounty.call or is_bounty.call ['success', 'success_heirloom', 'success_guard'] or not CharSettings[:enable_hunt_complete]) and percentmind > CharSettings[:should_hunt_mind].to_i
656 hunt_reason = 'mind not clear enough'
657 false
658 elsif not checkmana(CharSettings[:should_hunt_mana].to_i)
659 hunt_reason = 'out of mana'
660 false
661 elsif not checkspirit(CharSettings[:should_hunt_spirit].to_i)
662 hunt_reason = 'low spirit'
663 false
664 else
665 true
666 end
667}
668
669should_rest = proc {
670 if CharSettings[:should_rest_wounded].call
671 rest_reason = 'wounded'
672 true
673 elsif is_bounty.call 'task_forage' and can_do_bounty.call and Time.now.to_i < last_forage_attempt + last_forage_delay and percentmind >= CharSettings[:should_rest_mind].to_i
674 rest_reason = 'mind is full (waiting on foraging cooldown)'
675 true
676 elsif ((not can_turn_in.call and is_bounty.call ['success', 'success_guard', 'success_heirloom']) or not can_do_bounty.call or not CharSettings[:enable_hunt_complete]) and percentmind >= CharSettings[:should_rest_mind].to_i
677 rest_reason = 'mind is full'
678 true
679 elsif not checkmana(CharSettings[:should_rest_mana].to_i)
680 rest_reason = 'out of mana'
681 true
682 elsif checkencumbrance(CharSettings[:should_rest_encum].to_i)
683 rest_reason = 'encumbered'
684 true
685 elsif $sbounty_rest
686 if $sbounty_rest_reason
687 rest_reason = $sbounty_rest_reason
688 else
689 rest_reason = '$sbounty_rest was set'
690 end
691
692 $sbounty_rest = false
693 $sbounty_rest_reason = nil
694
695 true
696 else
697 rest_reason = nil
698 false
699 end
700}
701
702wander = proc { |boundaries|
703 room = Room.current
704 next_room_options = room.wayto.keys - boundaries
705 next_room_options.delete_if { |room_id| (room.timeto[room_id].class == Proc) and room.timeto[room_id].call.nil? }
706 next_room = next_room_options.find_all { |r| not $sbounty_wander_rooms.include?(r) }
707 if next_room.empty?
708 next_room = $sbounty_wander_rooms.find { |r| next_room_options.include?(r) }
709 else
710 next_room = next_room[rand(next_room.length)]
711 end
712 $sbounty_wander_rooms.delete(next_room)
713 $sbounty_wander_rooms.push(next_room)
714 way = room.wayto[next_room]
715 if way.class == String
716 move(way)
717 else
718 way.call
719 end
720}
721
722go2 = proc { |room|
723 next if Room.current.id.to_s == room.to_s
724
725 if checkarea =~ /Table/
726 if room.to_s == CharSettings[:rest_room].to_s
727 next
728 end
729
730 fput 'stand' until standing?
731 move 'out'
732 end
733
734 wait_while { running? 'go2' }
735 start_script('go2', [ room.to_s, '_disable_confirm_' ]);
736 wait_while { running? 'go2' }
737}
738
739go2_nearest = proc { |list|
740 room = Room[CharSettings[:rest_room]].find_nearest(list)
741 if room.nil?
742 error.call "failed to find nearest room"
743 end
744
745 go2.call(room)
746}
747
748go2_nearest_tag = proc { |tag|
749 if checkarea =~ /Table/i
750 fput 'stand' until standing?
751 fput 'out'
752 end
753
754 town = Room[CharSettings[:rest_room]].find_nearest_by_tag('town')
755 room = Room[town].find_nearest_by_tag(tag)
756 if room.nil?
757 error.call "failed to find room by tag: #{tag}"
758 end
759
760 go2.call(room)
761}
762
763run_commands = proc { |commands|
764 next if commands.empty?
765
766 if commands.class != Array
767 error.call "run_commands expects an input Array"
768 end
769
770 commands.each { |command| fput command }
771}
772
773run_scripts = proc { |scripts|
774 scripts.each { |script|
775 args = script.split(' ')
776 script = args.shift
777
778 start_script(script, args, true)
779 wait_while { running? script }
780 }
781}
782
783run_loot_script = proc {
784 wait_while { running? CharSettings[:loot_script] }
785 start_script(CharSettings[:loot_script], [], true)
786 wait_while { running? CharSettings[:loot_script] }
787}
788
789hunt_prepare = proc {
790 run_commands.call(CharSettings[:hunt_pre_commands])
791}
792
793rest_goto = proc {
794 next if in_rest_area
795 go2.call(CharSettings[:rest_room])
796}
797
798rest_exit = proc {
799 next unless in_rest_area
800 if Room.current.id != CharSettings[:rest_room] and Room.current.path_to(CharSettings[:rest_room]).nil?
801 run_commands.call(CharSettings[:rest_out_commands])
802 end
803 in_rest_area = false
804}
805
806rest_enter = proc {
807 next if in_rest_area
808 if Room.current.id != CharSettings[:rest_room] and Room.current.path_to(CharSettings[:rest_room]).nil?
809 run_commands.call(CharSettings[:rest_in_commands])
810 end
811 in_rest_area = true
812}
813
814rest_run_scripts = proc {
815 rest_exit.call
816 run_scripts.call(CharSettings[:rest_scripts])
817}
818
819reload_hunter = proc {
820 $SBOUNTY_HUNTER_SETUP.call(CharSettings.to_hash.dup, get_bounty_location.call)
821 $SBOUNTY_HUNTER_RELOAD.call
822}
823
824start_hunter = proc { |location|
825 $SBOUNTY_HUNTER_SETUP.call(CharSettings.to_hash.dup, location)
826 start_script($SBOUNTY_HUNTER_NAME, [], false)
827}
828
829finish_hunt = proc {
830 if running? $SBOUNTY_HUNTER_NAME
831 stop_script($SBOUNTY_HUNTER_NAME)
832 wait_while { running? $SBOUNTY_HUNTER_NAME }
833 end
834
835 fput 'stance defensive' until checkstance('defensive') or checkstance('guarded')
836
837 # pause to make sure looter isn't running
838 wait_while { running? CharSettings[:loot_script] }
839
840 # run loot script in case the hunter wasn't able to clean up
841 if GameObj.npcs.find { |npc| npc.status =~ /dead/ } or is_bounty.call 'success_heirloom'
842 run_loot_script.call
843 end
844}
845
846remove_bounty = proc {
847 next if (not CharSettings[:enable_expedite] and Spell['Next Bounty'].active?) or is_bounty.call 'none'
848
849 rest_exit.call
850
851 kill_script 'go2' if running? 'go2'
852
853 go2_nearest_tag.call('advguild')
854
855 print.call 'removing bounty, you have five seconds to kill me'
856 sleep 5.00
857
858 npc = GameObj.npcs.find { |t| t.name =~ /taskmaster/i }
859 if npc.nil?
860 error.call 'failed to find taskmaster'
861 end
862
863 res = dothistimeout "ask #{npc} about remove", 5, /^#{npc}.*?want to be removed|Trying to sneak/
864 if res =~ /Trying to sneak/
865 fput "ask #{npc} about bounty"
866 else
867 dothistimeout "ask #{npc} about remove", 5, /^#{npc}.*?have removed you/
868 end
869
870 can_do_bounty_cache = nil
871}
872
873expedite_bounty = proc {
874 print.call 'expediting bounty, you have five seconds to kill me'
875 sleep 5.00
876
877 remove_bounty.call
878
879 rest_exit.call
880 go2_nearest_tag.call('advguild')
881
882 npc = GameObj.npcs.find { |t| t.name =~ /taskmaster/i }
883 if npc.nil?
884 error.call 'failed to find taskmaster'
885 end
886
887 res = dothistimeout "ask #{npc} about expedite", 5, /I'll expedite|You don't seem to have any expedited task reassignment|I still need to complete/
888 if res =~ /expedited/
889 expedite_left = false
890 end
891
892 can_do_bounty_cache = nil
893}
894
895get_bounty = proc {
896 rest_exit.call
897
898 go2_nearest_tag.call('advguild')
899
900 npc = GameObj.npcs.find { |t| t.name =~ /taskmaster/i }
901 res = dothistimeout "ask ##{npc.id} for bounty", 2, /(?:protective escort|creature problem|local gem dealer|local furrier|lost heirloom|local healer|local herbalist|local resident|bandit problem|I'm kind of busy right now)/
902
903 if res.nil?
904 error.call 'invalid response from taskmaster'
905 elsif res =~ /Come back in about (\d+) minutes if you want another task\./
906 Spell['Next Bounty'].putup
907 Spell['Next Bounty'].timeleft = $1.to_i
908 elsif res =~ /bandit/
909 remove_bounty.call
910 end
911
912 can_do_bounty_cache = nil
913}
914
915talk_to_herbalist = proc {
916 go2_nearest.call([ 3824, 1851, 10396, 640, 5722, 2406, 11002, 9505 ])
917
918 if Room.current.id == 10396
919 npc = 'maraene'
920 else
921 npc = GameObj.npcs.find { |t| t.name =~ /brother Barnstel|scarred Agarnil kris|healer|herbalist|merchant Kelph|famed baker Leaftoe|Akrash|old Mistress Lomara/i }
922 end
923
924 if is_bounty.call 'help_herbalist'
925 res = dothistimeout "ask #{npc} about bounty", 5, /Yes, I do have a task for you/
926 if res =~ /I've recently received an order for ([0-9]+) (.*?)\./
927 print.call "received bounty from herbalist [#{$1} #{$2}]"
928 end
929 elsif bounty? =~ /#{bounty_patterns['task_forage']}/
930 herb_name = $1.gsub(/s?$/,'')
931 herbs = lootsack.contents.find_all { |item| item.name =~ /#{herb_name}/ }
932
933 if herbs.empty?
934 error.call 'no herbs to turn in, why are you here?'
935 end
936
937 empty_hands
938 herbs.each { |herb|
939 fput "get ##{herb.id} from ##{lootsack.id}"
940 result = dothistimeout "give ##{herb.id} to #{npc}", 3, /This looks perfect|That looks like it has been partially used up/
941
942 if result !~ /perfect/
943 fput "drop ##{herb.id}"
944 end
945 }
946 fill_hands
947
948 if (checkleft == herb_name and GameObj.left.type =~ /herb/) or (checkright == herb_name and GameObj.right_hand.type =~ /herb/)
949 fput 'drop ' + herb_name
950 end
951 else
952 error.call 'why are you at the herbalist?'
953 end
954}
955
956talk_to_gemdealer = proc {
957 go2_nearest_tag.call('gemshop')
958
959 if Room.current.id == 10327
960 npc = 'areacne'
961 else
962 npc = GameObj.npcs.find { |t| t.name =~ /dwarven clerk|gem dealer|jeweler|Zirconia/i }
963 end
964
965 res = dothistimeout "ask #{npc} about bounty", 5, /Yes, I do have a task for you/
966 if res =~ /interested in purchasing an? (.*?)\. .* go round up ([0-9]+) of them/
967 name = $1
968 count = $2.to_i
969
970 print.call "-- received bounty from gem dealer [#{count} #{name}]"
971 end
972}
973
974talk_to_furrier = proc {
975 go2_nearest_tag.call('furrier')
976
977 if Room.current.id == 10327
978 npc 'areacne'
979 else
980 npc = GameObj.npcs.find { |t| t.name =~ /dwarven clerk|furrier/i }
981 end
982
983 res = dothistimeout "ask #{npc} about bounty", 5, /Yes, I do have a task for you/
984 if res =~ /I've recently received an order for ([0-9]+) (.*?)\./
985 print.call "received bounty from furrier [#{$1} #{$2}]"
986 end
987}
988
989get_guard_npc = proc {
990 if Room.current.id == 10915
991 'purser'
992 else
993 GameObj.npcs.find { |npc| npc.name =~ /guard|sergeant|guardsman|purser/i }
994 end
995}
996
997find_guard = proc {
998 go2_nearest_tag.call('advguard')
999 npc = get_guard_npc.call
1000
1001 if npc.nil?
1002 go2_nearest_tag.call('advguard2')
1003 npc = get_guard_npc.call
1004 end
1005
1006 if npc.nil?
1007 error.call "failed to locate guard"
1008 end
1009
1010 npc
1011}
1012
1013talk_to_guard = proc {
1014 npc = find_guard.call
1015
1016 res = dothistimeout "ask #{npc} about bounty", 5, /Yes, we do have a task for you|Ah, so you have returned/
1017 if res.nil?
1018 print.call "unknown response from guard: #{res}"
1019 end
1020}
1021
1022get_random_location = proc {
1023 keys = []
1024 CharSettings[:locations].each { |key, data|
1025 if data[:enable_hunting_rotation]
1026 keys.push(key)
1027 end
1028 }
1029 name = keys[rand(keys.size)]
1030
1031 if name.nil?
1032 error.call 'failed to find a hunting area'
1033 end
1034
1035 [ name, CharSettings[:locations][name] ]
1036}
1037
1038task_escort = proc { |target|
1039 print.call "escorting child to #{target}"
1040
1041 path = []
1042 step2 = proc { |destination_room|
1043 unless path and (s = path.index(Room.current.id)) and (d = path.index(destination_room)) and (s < d)
1044 previous, shortest_distances = Map.dijkstra(Room.current.id, destination_room)
1045 unless previous[destination_room]
1046 echo "error: failed to find a path between your current room (#{Room.current.id}) and destination room (#{destination_room})"
1047 exit
1048 end
1049 path = [ destination_room ]
1050 path.push(previous[path[-1]]) until previous[path[-1]].nil?
1051 path.reverse!
1052 previous = shortest_distances = nil
1053 end
1054
1055 way = Room.current.wayto[path[path.index(Room.current.id)+1].to_s]
1056
1057 if way.class == String
1058 move way
1059 elsif way.class == Proc
1060 way.call
1061 end
1062 }
1063
1064 waitrt?
1065 fput 'stance defensive' unless checkstance('guarded')
1066
1067 destination = Room[CharSettings[:rest_room]].find_nearest_by_tag(target)
1068 while Room.current.id != destination and is_bounty.call 'task_escort'
1069 if GameObj.npcs.find { |n| n.name =~ /child/i }
1070 step2.call(destination)
1071 end
1072 sleep 0.25
1073 end
1074
1075 if is_bounty.call 'fail_child'
1076 print.call 'failed to escort child or child was killed'
1077 elsif npc = get_guard_npc.call
1078 print.call 'waiting for child to arrive'
1079 wait_until { GameObj.npcs.find { |n| n.name =~ /child/ } }
1080
1081 fput "ask ##{npc.id} for bounty"
1082 elsif target == 'advguard'
1083 task_escort.call 'advguard2'
1084 else
1085 error.call 'failed to find guard for escort'
1086 end
1087}
1088
1089task_search = proc {
1090 if bounty? !~ /#{bounty_patterns['task_search']}/
1091 error.call 'you are not on a search bounty'
1092 end
1093
1094 print.call 'searching for heirloom'
1095
1096 name, location = get_bounty_location.call
1097 song_of_peace = false
1098 invalid_rooms = []
1099 last_room = nil
1100
1101 hunt_prepare.call
1102 rest_exit.call
1103
1104 go2.call location[:room]
1105
1106 while is_bounty.call 'task_search' and not CharSettings[:should_rest_wounded].call
1107 if Spell[1011].known? and Spell[1011].affordable? and not song_of_peace
1108 Spell[1011].cast
1109 song_of_peace = true
1110 elsif Spell[506].known? and Spell[506].affordable? and not Spell[506].active?
1111 Spell[506].cast
1112 end
1113
1114 wander.call location[:boundaries]
1115
1116 fput 'stand' until standing?
1117 fput 'stance defensive' until checkstance 'defensive' or checkstance 'guarded'
1118
1119 if (GameObj.npcs.empty? or GameObj.npcs.nil?) and Room.current.location =~ /#{location[:location].downcase.strip}/i and not invalid_rooms.include? Room.current.id and Room.current.id != last_room
1120 run_commands.call CharSettings[:pre_search_commands]
1121
1122 fput 'kneel' until kneeling?
1123 res = dothistimeout 'search', 1, /You intently search the area|You put your head to the ground and peer intently/
1124 if res =~ /You intently search the area|You put your head to the ground and peer intently/
1125 last_room = Room.current.id
1126 else
1127 print.call 'invalid room, skipping in the future'
1128 invalid_rooms.push Room.current.id
1129 end
1130
1131 waitrt?
1132
1133 run_commands.call CharSettings[:post_search_commands]
1134
1135 while not checkstance 'offensive' and (song_of_peace or GameObj.npcs.find_all { |npc| npc.status !~ /dead/ }.nil?)
1136 fput 'stance offensive'
1137 end
1138
1139 fput 'stand' until standing?
1140
1141 fput 'stance defensive' until checkstance 'defensive' or checkstance 'guarded'
1142
1143 if is_bounty.call 'task_found'
1144 run_loot_script.call
1145 break
1146 end
1147 end
1148 end
1149
1150 waitrt?
1151
1152 if song_of_peace
1153 fput "stop 1011"
1154 end
1155}
1156
1157task_bandits = proc {
1158 if bounty? !~ /#{bounty_patterns['task_bandit']}/
1159 error.call 'you are not on a bandits bounty'
1160 end
1161
1162 print.call 'culling bandits'
1163
1164 my_ambush = false
1165 ambush_room_count = XMLData.room_count
1166
1167 location = $1.strip.downcase
1168 rooms = Room.list.find_all { |r| r.location =~ /#{location}/i }.collect { |r| r.id }
1169 attack_script = 'shunt-attack'
1170
1171 while not CharSettings[:should_rest_wounded].call
1172 rooms.each { |room|
1173 waitrt?
1174 waitcastrt?
1175
1176 print.call 'moving to room ' + room.to_s
1177
1178 fput 'stance defensive' unless checkstance == 'defensive'
1179
1180 go2.call(room)
1181 sleep 3
1182
1183 while not CharSettings[:should_rest_wounded].call
1184 npcs = GameObj.npcs.to_a.find_all { |npc| npc.type =~ /bandit/ and npc.status !~ /dead/ }
1185 dead = GameObj.npcs.to_a.find_all { |npc| npc.type =~ /bandit/ and npc.status =~ /dead/ }
1186
1187 if npcs.empty?
1188 break
1189 else
1190 waitrt?
1191 waitcastrt?
1192
1193 start_script(attack_script, npcs.collect { |n| n.id }, h={:quiet=>true})
1194 wait_while { running?(attack_script) }
1195 end
1196
1197 sleep 0.1
1198 end
1199
1200 break if CharSettings[:should_rest_wounded].call
1201 }
1202
1203 sleep 0.1
1204 end
1205}
1206
1207task_forage = proc {
1208 if bounty? !~ /#{bounty_patterns['task_forage']}/
1209 error.call 'you are not on a forage bounty'
1210 end
1211
1212 print.call 'foraging for herbs'
1213
1214 herb = $1.downcase
1215 location = $2
1216 count = $3.to_i
1217
1218 refresh_spells = proc {
1219 [506, 603, 9704].each { |spell|
1220 if Spell[spell].known? and not Spell[spell].active? and Spell[spell].affordable?
1221 waitrt?
1222 waitcastrt?
1223
1224 Spell[spell].cast
1225 end
1226 }
1227
1228 yierka = Spell['AA: Yierka']
1229 yierka_cooldown = Spell['AA: Yierka Cooldown']
1230
1231 if yierka.known? and yierka.affordable? and not yierka_cooldown.active?
1232 yierka.cast
1233 end
1234 }
1235
1236 get_herb_count = proc {
1237 lootsack.contents.find_all { |item| item.name =~ /#{herb.gsub(/s?$/,'')}/i }.size
1238 }
1239
1240 last_forage_attempt = Time.now.to_i
1241
1242 if get_herb_count.call < count
1243 hunt_prepare.call
1244 rest_exit.call
1245
1246 rooms = get_herb_rooms.call(location, herb)
1247
1248 cur_room = 0
1249 num_tries = 0
1250
1251 song_of_peace = false
1252
1253 while get_herb_count.call < count and not CharSettings[:should_rest_wounded].call and num_tries < 3
1254 go2.call(rooms[cur_room])
1255 cur_room = cur_room + 1
1256
1257 if cur_room >= rooms.length
1258 cur_room = 0
1259 num_tries = num_tries + 1
1260 end
1261
1262 while get_herb_count.call < count and not CharSettings[:should_rest_wounded].call
1263 if Spell[1011].known? and Spell[1011].affordable? and not song_of_peace
1264 Spell[1011].cast
1265 song_of_peace = true
1266 end
1267
1268 waitrt?
1269 fput 'stance defensive' until ['defensive', 'guarded'].include?(checkstance)
1270
1271 refresh_spells.call
1272
1273 if not song_of_peace and not GameObj.npcs.find_all { |npc| npc.status !~ /dead|gone/ }.nil?
1274 break
1275 end
1276
1277 if not kneeling?
1278 run_commands.call(CharSettings[:pre_search_commands])
1279 fput "kneel" until kneeling?
1280
1281 if Spell[213].known? and Spell[213].affordable?
1282 fput "incant 213"
1283 end
1284
1285 if Spell[205].known? and Spell[205].affordable?
1286 fput "incant 205"
1287 end
1288 end
1289
1290 res = dothistimeout "forage #{herb.gsub(/(?:(?:handful|bunch|sprig) of)\s+/, '') }", 1, /not even positive|it could be|it could even be found|and manage to find|Roundtime|In order to forage|foraging here recently/
1291
1292 waitrt?
1293
1294 if res =~ /it could be|it could even be found|not even positive/
1295 rooms.delete(cur_room)
1296 break
1297 elsif res =~ /and manage to find/
1298 if checkleft =~ /#{herb}/
1299 fput "put #{checkleft} in ##{lootsack.id}"
1300 else
1301 fput "put #{checkright} in ##{lootsack.id}"
1302 end
1303
1304 print.call "-- success, found #{get_herb_count.call} of #{count} #{herb}"
1305 elsif res =~ /In order to forage/
1306 fput "store all"
1307 elsif res =~ /foraging here recently/
1308 rooms.delete(cur_room)
1309 break
1310 else
1311 print.call "-- failure, found #{get_herb_count.call} of #{count} #{herb}"
1312 end
1313 end
1314
1315 # Sanity check, cleanup any remaining herbs
1316 if checkleft =~ /#{herb}/
1317 fput "put #{checkleft} in ##{lootsack.id}"
1318 elsif checkright =~ /#{herb}/
1319 fput "put #{checkright} in ##{lootsack.id}"
1320 end
1321
1322 if not standing?
1323 run_commands.call(CharSettings[:post_search_commands])
1324
1325 while not checkstance 'offensive' and (song_of_peace or GameObj.npcs.find_all { |npc| npc.status !~ /dead/ }.nil?)
1326 fput 'stance offensive'
1327 end
1328
1329 fput 'stance defensive' until checkstance 'defensive' or checkstance 'guarded'
1330
1331 fput "stand" until standing?
1332 end
1333 end
1334 end
1335
1336 if song_of_peace
1337 fput "stop 1011"
1338 end
1339
1340 if get_herb_count.call >= count
1341 talk_to_herbalist.call
1342 last_forage_attempt = 0
1343 end
1344}
1345
1346success_heirloom = proc {
1347 print.call 'turning in heirloom'
1348
1349 npc = find_guard.call
1350
1351 empty_hands
1352
1353 close = false
1354 if lootsack.contents.nil?
1355 open_result = dothistimeout "open ##{lootsack.id}", 5, /^You open|^That is already open\.$/
1356 if open_result =~ /^You open/
1357 close = true
1358 else
1359 dothistimeout "look in ##{lootsack.id}", 5, /In .*? you see/
1360 end
1361 end
1362
1363 found = false
1364 lootsack.contents.each { |item|
1365 res = dothistimeout "look ##{item.id}", 2, /^Engraved .* initials|^You see nothing unusual\.$|^The ring appears|^It takes you a moment|^It is difficult to see/
1366 if res =~ /^Engraved .* initials/
1367 fput "get ##{item.id}"
1368 fput "give ##{item.id} to #{npc}"
1369 found = true
1370
1371 break
1372 end
1373 }
1374
1375 fput "close ##{lootsack.id}" if close
1376
1377 if not found
1378 error.call 'failed to find heirloom for guard'
1379 end
1380
1381 fill_hands
1382}
1383
1384turn_in = proc {
1385 rest_exit.call
1386
1387 if is_bounty.call 'success_guard'
1388 talk_to_guard.call
1389 elsif is_bounty.call 'success_heirloom'
1390 success_heirloom.call
1391 end
1392
1393 go2_nearest_tag.call('advguild')
1394
1395 npc = GameObj.npcs.find { |t| t.name =~ /taskmaster/i }
1396 res = dothistimeout "ask #{npc} about bounty", 5, /^#{npc}.*?done with that assignment/
1397 10.times {
1398 if line = get? and line =~ /You have earned ([0-9]+) bounty points?, ([0-9]+) experience points, and ([0-9]+) silver\./
1399 print.call "finished task (#{$1} points, #{$2} exp, #{$3} silver)"
1400 break
1401 end
1402
1403 sleep 0.1
1404 }
1405
1406 run_loot_script.call
1407
1408 can_do_bounty_cache = nil
1409}
1410
1411talk_to_npc = proc {
1412 if is_bounty.call ['help_creature', 'help_resident', 'help_heirloom', 'success_guard']
1413 talk_to_guard.call
1414 # separate bandits so we don't trigger them on accident while walking
1415 elsif is_bounty.call ['help_bandit'] and CharSettings[:enable_bandit]
1416 talk_to_guard.call
1417 elsif is_bounty.call 'help_furrier'
1418 talk_to_furrier.call
1419 elsif is_bounty.call 'help_herbalist'
1420 talk_to_herbalist.call
1421 elsif is_bounty.call 'help_gemdealer'
1422 talk_to_gemdealer.call
1423 end
1424}
1425
1426help = proc {
1427 respond
1428 respond 'SpiffyBounty by SpiffyJr (theman@spiffyjr.me)'
1429 respond 'usage: ;sbounty [hunter=bigshot]'
1430 respond
1431}
1432
1433hunter = 'sbounty-bigshot'
1434
1435if script.vars[1] =~ /^setup$/i
1436 setup.call
1437 exit
1438elsif script.vars[1] =~ /^help$/i
1439 help.call
1440 exit
1441elsif script.vars[1] =~ /^forage$/i
1442 task_forage.call
1443 exit
1444elsif script.vars[1] =~ /^npc$/i
1445 talk_to_npc.call
1446 exit
1447elsif script.vars[1]
1448 hunter = "sbounty-#{script.vars[1]}"
1449end
1450
1451task_accepted = false
1452resting = false
1453
1454start_script(hunter, nil, true)
1455wait_while { running? hunter }
1456
1457if not defined? $SBOUNTY_HUNTER_SETUP
1458 error.call 'hunter setup was not defined'
1459elsif not defined? $SBOUNTY_HUNTER_NAME
1460 error.call 'hunter name was not defined'
1461elsif not defined? $SBOUNTY_HUNTER_RELOAD
1462 error.call 'hunter reload was not defined'
1463end
1464
1465before_dying { kill_script $SBOUNTY_HUNTER_NAME if running? $SBOUNTY_HUNTER_NAME }
1466
1467while true
1468 talk_to_npc.call
1469
1470 if can_do_bounty.call and not CharSettings[:should_rest_wounded].call
1471 if is_bounty.call 'task_search'
1472 task_search.call
1473 elsif is_bounty.call 'task_forage' and Time.now.to_i >= last_forage_attempt + last_forage_delay
1474 task_forage.call
1475 end
1476 elsif Spell['Next Bounty'].active? and (expedite_left and not is_bounty.call 'none' and not can_do_bounty.call and CharSettings[:enable_expedite])
1477 expedite_bounty.call
1478 next
1479 end
1480
1481 if is_bounty.call 'task_escort' and GameObj.npcs.find { |npc| npc.name =~ /child/ }
1482 task_escort.call 'advguard'
1483 elsif is_bounty.call 'task_fail'
1484 can_do_bounty_cache = nil
1485 elsif is_bounty.call 'success_heirloom'
1486 success_heirloom.call
1487 end
1488
1489 exit if dead?
1490
1491 if can_turn_in.call
1492 turn_in.call
1493 resting = false
1494 else
1495 if not can_do_bounty.call and not Spell['Next Bounty'].active?
1496 remove_bounty.call
1497 get_bounty.call
1498 elsif should_hunt.call and not should_rest.call and not has_skins.call
1499 resting = false
1500 provoked = false
1501 success = false
1502 bounty = bounty?
1503
1504 rest_exit.call
1505 hunt_prepare.call
1506
1507 # default hunter location
1508 if can_do_bounty.call
1509 # order is important here - bandits and regular culling look similar
1510 if is_bounty.call ['task_bandit']
1511 task_bandits.call
1512 elsif is_bounty.call ['task_cull', 'task_dangerous', 'task_heirloom', 'task_rescue', 'task_skin']
1513 start_hunter.call(get_bounty_location.call)
1514 else
1515 start_hunter.call(get_random_location.call)
1516 end
1517 else
1518 start_hunter.call(get_random_location.call)
1519 end
1520
1521 while not should_rest.call and running? $SBOUNTY_HUNTER_NAME
1522 if bounty? =~ /#{bounty_patterns['task_provoked']}/ and not provoked
1523 reload_hunter.call
1524 provoked = true
1525 elsif is_bounty.call 'task_escort'
1526 break
1527 elsif has_skins.call
1528 break
1529 elsif not success and (bounty? != bounty and (is_bounty.call [ 'success', 'success_heirloom' ] or (provoked and is_bounty.call 'success_guard')))
1530 finish_hunt.call
1531 start_hunter.call(get_random_location.call)
1532
1533 success = true
1534 elsif can_turn_in.call
1535 break
1536 elsif not can_do_bounty.call and not Spell['Next Bounty'].active?
1537 break
1538 end
1539
1540 sleep 0.10
1541 end
1542
1543 finish_hunt.call
1544 elsif not can_turn_in.call
1545 rest_goto.call
1546
1547 if not resting or CharSettings[:should_rest_wounded].call
1548 rest_run_scripts.call
1549 rest_goto.call
1550
1551 sleep 1
1552 end
1553
1554 rest_enter.call
1555
1556 while should_rest.call or not should_hunt.call
1557 if can_turn_in.call
1558 break
1559 elsif CharSettings[:should_rest_wounded].call
1560 break
1561 elsif not can_do_bounty.call and not Spell['Next Bounty'].active?
1562 break
1563 end
1564
1565 fput 'exp'
1566
1567 if rest_reason.nil?
1568 rest_reason = hunt_reason
1569 end
1570 print.call "still resting because: #{rest_reason}"
1571
1572 sleep CharSettings[:rest_sleep_interval]
1573 end
1574
1575 resting = true
1576 rest_exit.call
1577 end
1578 end
1579
1580 sleep 0.10
1581end