diff --git a/scripts/eloot.lic b/scripts/eloot.lic index e670d866a..1fc55cfa2 100644 --- a/scripts/eloot.lic +++ b/scripts/eloot.lic @@ -15,9 +15,29 @@ game: Gemstone tags: loot required: Lich >= 5.12.9 - version: 2.7.0 + version: 2.8.0 Improvements: Major_change.feature_addition.bugfix + v2.8.0 (2026-01-27) + - replaced overflow_container and secondary_overflow with single overflow_containers setting (comma-separated) + - added configurable "Trash to Dump" section, instead of hardcoded herb/food/junk + - bugfix inbetween script(s) added erroneous commas between script parameters + - allow custom bank withdraw amount + - bugfix when trying to store box in non-group disk + - bugfix in box_loot_ground locked box message + - bugfix in single_drag_box cycling thru all group disks even when use_disk_group was false + - bugfix for eonake gauntlet missing XML to properly match + - bugfix for in_region check if already in destination room + - reset sacks full when using --sellable/type/sell CLI + - locksmith pool remove silver withdraw + - fix to process boxes from default/overflows first, then box/disk container to prevent sacks_full emptying loot + - add skin bounty creatures only + - bugfix in keep scrolls option to skip if adding non-number to entries + - expand F2P silver withdraw to support Teras + - bugfix in process_boxes and always check pool + - bugfix for frozen bramble looting + - bugfix for manual deposit hoard when items in left hand + - bugfix for hoarding when valid hoard items have same name as none valid items v2.7.0 (2026-01-19) - added ground looting of boxes - added support for group disks when looting boxes @@ -245,7 +265,7 @@ module ELoot # Data # inventory type @sacks_closed = [] - @sacks_full = [] + @sacks_full = { "last cleared" => [Time.now.strftime('%Y-%m-%d %H:%M:%S'), Room.current.id, Script.current.vars[0]] } @checked_bags = [] @sell_containers = [] @ready_lines = [] @@ -622,8 +642,7 @@ module ELoot # UI Setup loot_phase: { default: false }, use_disk: { default: true }, loot_defensive: { default: false }, - overflow_container: { default: '' }, - secondary_overflow: { default: '' }, + overflow_containers: { default: '' }, coin_hand_name: { default: '' }, charm_name: { default: '' }, sigil_determination_on_fail: { default: false }, @@ -724,6 +743,7 @@ module ELoot # UI Setup next if setting.include?(text) spell_no = text[/^\d+/] + next if spell_no.nil? next if Spell[spell_no].nil? setting.push(text) @@ -743,12 +763,14 @@ module ELoot # UI Setup sell_aspect: { default: false }, sell_keep_silver: { default: 0 }, sell_deposit_coinhand: { default: false }, + trash_dump_types: { default: ["herb", "junk", "food"] }, }, skin: { skin_enable: { default: false }, skin_kneel: { default: false }, skin_604: { default: false }, skin_resolve: { default: false }, + skin_bounty_only: { default: false }, skin_sheath: { default: '' }, skin_weapon: { default: '' }, skin_sheath_blunt: { default: '' }, @@ -802,7 +824,8 @@ module ELoot # UI Setup gem_everything_list: { default: false }, gem_only_list: { default: false }, gem_horde_container: { default: '' }, - gem_horde_containers: { default: ['default', 'gem', 'overflow1', 'overflow2'] }, + gem_horde_containers: { default: ['default', 'gem'] }, + gem_horde_use_overflow: { default: false }, gem_locker: { default: '' }, gem_locker_name: { default: '' }, gem_horde_locker_che: { default: false }, @@ -855,7 +878,8 @@ module ELoot # UI Setup alchemy_everything_list: { default: false }, alchemy_only_list: { default: false }, alchemy_horde_container: { default: '' }, - alchemy_horde_containers: { default: ['default', 'reagent', 'overflow1', 'overflow2'] }, + alchemy_horde_containers: { default: ['default', 'reagent'] }, + alchemy_horde_use_overflow: { default: false }, alchemy_locker: { default: '' }, alchemy_locker_name: { default: '' }, alchemy_horde_locker_che: { default: false }, @@ -948,11 +972,9 @@ module ELoot # UI Setup 00TrueTrue1515TrueOriginally from Mushroom Spore Giftbox 07/29/202301 TrueFalse2024Use Bloodtooth Bands(?)TrueTrueFalsestartTrue 04Use Group Disks(?)TrueTrueTruestartTrue11 - TrueFalseOther SettingsFalseTrue1TrueFalseTrueTrueFalse100TrueFalseTrueFalsestart55Primary Overflow:00 - 00TrueTrueEnter the name of your overflow container. This container will be used when the targeted STOW container is full.5155True - 01TrueFalsestart555Secondary Overflow:02 - TrueTrueEnter the name of your secondary overflow container. This container will be used when the primary overflow container is full.51510TrueFalse - 03TrueFalseOverflow Containers00TrueFalse100TrueFalseTrueFalsestart555Coin Bag / Coin Hand / Gambling Kit + TrueFalseOther SettingsFalseTrue1TrueFalseTrueTrueFalse100TrueFalseTrueFalsestart55(comma-separated):00 + 0065TrueTrue51510TrueinTrueTrueEnter comma-separated names of your overflow containers (e.g., "rucksack, cloak, orange pack"). These containers will be used in order when the targeted STOW container is full.wordFalse + 01TrueFalseOverflow Containers00TrueFalse100TrueFalseTrueFalsestart555Coin Bag / Coin Hand / Gambling Kit 00TrueTrue5155TrueName of item01 TrueFalsestart555Gambling Kit Silvers (?)02130TrueTruestart5100gambling_adjustment 03TrueFalseWeightless Coin Containers10FalseTrue2 @@ -977,7 +999,7 @@ module ELoot # UI Setup 00Use town locksmithTrueTrueFalsestart510True 01Display box contentsTrueTrueFalsestart510True 11Always check pool (?)TrueTrueFalsestart510True - 10TrueFalsestart1010Locksmith Withdraw Amount03 + 10TrueFalsestart1010Bank Withdraw Amount03 TrueTrueHow much to withdraw from the bank before going to the town locksmith.start10105locksmith_withdraw_amount_adjustment 04Default to using locksmith when gem bounty activeTrueTrueFalse510True 022TrueFalseLocksmithing00 @@ -1061,7 +1083,7 @@ module ELoot # UI Setup Share silversTrueTrueFalsestart55True20 TrueTruestart305300sell_keep_silver_adjustmentTrue312 TrueFalseOther Settings002FalseTrue3 - 3TrueFalseSelling3FalseTrueTrueinTrueFalseTrueFalse10150150TrueFalse50TrueFalsecenter5525Add80TrueTrueTruestart5 + TrueFalse100TrueFalse5525TrueAlchemyTrueTrueFalsestart5True00FoodTrueTrueFalsestartTrue10Locksmith TrapsTrueTrueFalsestartTrue20BoxTrueTrueFalsestartTrue30HerbTrueTrueFalsestart5True01ReagentTrueTrueFalsestartTrue11WandTrueTrueFalsestartTrue21BreakableTrueTrueFalsestartTrue31ClothingTrueTrueFalsestart5True02JunkTrueTrueFalsestartTrue12LockpickTrueTrueFalsestartTrue22TrueFalseTrash to Dump (?)Will attempt to TRASH type after selling is done, requires type to be enabled for selling to dumpFalseTrue43TrueFalseSelling3FalseTrueTrueinTrueFalseTrueFalse10150150TrueFalse50TrueFalsecenter5525Add80TrueTrueTruestart5 00Delete80TrueTrueTrueend520 TrueTrueTrueEnter exclusion (e.g. uncut diamond)10200TrueTrue5555TrueinTrueFalseTrueTruesell_exclude_storeFalse0True fixedExclusion0013TrueFalseExclusions (?) @@ -1071,11 +1093,11 @@ module ELoot # UI Setup Spell Number0Spell Name1Vibrant2 013TrueFalseKeep Scrolls (?) 104TrueFalseSelling Grid - 4FalseTrueTrueinTrueFalsestartTrueFalse10verticalTrueFalse50TrueFalse55True1025TrueTrueFalse + 4FalseTrueTrueinTrueFalsestartTrueFalse10verticalTrueFalse50TrueFalse5510210TrueFalse 30Enable skinningTrueTrueFalsestartTrue00 Kneel to skinTrueTrueFalsestartTrue10Use Sigil of ResolveTrueTrueFalsestartTrue 20Use 604TrueTrueFalsestartTrue30 - TrueFalseSettingsFalseTrue0820TrueFalsestartTrueTrueFalsestart50TrueFalsecenterTrueTrueFalsestart1055Skin Sheath (blunt) + Skin Bounty OnlyTrueTrueFalsestartTrue40TrueFalseSettingsFalseTrue0820TrueFalsestartTrueTrueFalsestart50TrueFalsecenterTrueTrueFalsestart1055Skin Sheath (blunt) 00TrueTrue10102001TrueFalsestart2055Skin Weapon (blunt) 10TrueTrue2010102011 TrueFalseBlunt Skinning01TrueFalsestart50TrueFalsecenter55TrueTrueTrueFalsestart1055Skin Sheath (edged) @@ -1099,9 +1121,8 @@ module ELoot # UI Setup 00TrueFalseend10Entry Verb01TrueFalseend10Exit Verb 0214TrueFalsestartstart1520 04TrueFalseGeneral SettingsFalseTrue0TrueFalse50TrueFalseTrueDefaultTrueTrueFalsestart101010True - 00First OverflowTrueTrueFalsestart55True - 20Second OverflowTrueTrueFalsestart55True - 30GemTrueTrueFalsestart55True + 00Use All Overflow ContainersTrueTrueFalsestart55True + 20GemTrueTrueFalsestart55True 10TrueFalseOnly Hoard Gems from these ContainersFalseTrue1 TrueFalse50TrueFalseHoard all gems except those excluded belowTrueTrueFalsestart1010True 00ONLY hoard gems listed belowTrueTrueFalsestart1010True @@ -1128,9 +1149,8 @@ module ELoot # UI Setup 0214Hoard reagents in a house locker?TrueTrueFalsestartend201010True 13TrueFalsestartstart152004 TrueFalseGeneral SettingsFalseTrue0TrueFalse50TrueFalseTrueDefaultTrueTrueFalsestart101010True - 00First OverflowTrueTrueFalsestart55True - 20Second OverflowTrueTrueFalsestart55True - 30ReagentTrueTrueFalsestart55True + 00Use All Overflow ContainersTrueTrueFalsestart55True + 20ReagentTrueTrueFalsestart55True 10TrueFalseOnly Hoard Alchemy Ingredients from these ContainersFalseTrue1 TrueFalse50TrueFalseHoard all alchemy ingredients except those excluded belowTrueTrueFalsestart1010True 00ONLY hoard alchemy ingredients listed belowTrueTrueFalsestart1010True @@ -1302,6 +1322,9 @@ module ELoot # UI Setup elsif obj.class == Gtk::Entry obj.text = @settings[key].strip obj.signal_connect('changed') { on_update(obj) } + elsif obj.class == Gtk::TextView + obj.buffer.text = @settings[key].to_s.strip + obj.buffer.signal_connect('changed') { on_update(obj) } elsif obj.class == Gtk::SpinButton obj.value = @settings[key] obj.adjustment.value = @settings[key] @@ -1501,6 +1524,8 @@ module ELoot # UI Setup end elsif obj.class == Gtk::Entry @settings[key] = obj.text.strip + elsif obj.class == Gtk::TextView + @settings[key] = obj.buffer.text.strip elsif obj.class == Gtk::SpinButton @settings[key] = obj.buffer.text end @@ -1755,10 +1780,68 @@ module ELoot # Profile loading/saving and settings def self.load(settings) @@data = Data.new(settings) + # Migrate old overflow settings to new format + ELoot.migrate_overflow_settings(ELoot.data.settings) end - def self.load_defaults() - default_hash = { + def self.migrate_overflow_settings(settings) + migrated_settings = ELoot.seed_missing_defaults(settings) + # Migration helper: Convert old overflow_container and secondary_overflow to new overflow_containers format + if settings.key?(:overflow_container) || settings.key?(:secondary_overflow) + containers = [] + containers << settings[:overflow_container].to_s.strip unless settings[:overflow_container].to_s.strip.empty? + containers << settings[:secondary_overflow].to_s.strip unless settings[:secondary_overflow].to_s.strip.empty? + + unless containers.empty? + settings[:overflow_containers] = containers.join(', ') + ELoot.msg(text: " Migrated old overflow container settings to new format: #{settings[:overflow_containers]}") + end + + # Remove old keys + settings.delete(:overflow_container) + settings.delete(:secondary_overflow) + migrated_settings = true + end + + # Migration: Convert old gem/alchemy hoarding overflow checkboxes to new use_overflow boolean + ['gem', 'alchemy'].each do |type| + containers_key = "#{type}_horde_containers".to_sym + use_overflow_key = "#{type}_horde_use_overflow".to_sym + + # Check if the old format with overflow1/overflow2 is present in the containers array + if settings[containers_key] && settings[containers_key].is_a?(Array) + old_containers = settings[containers_key] + + # Check if overflow1 or overflow2 are in the array + has_overflow = old_containers.include?('overflow1') || old_containers.include?('overflow2') + + if has_overflow + # Set the new use_overflow boolean to true + settings[use_overflow_key] = true + + # Remove overflow1 and overflow2 from the array + settings[containers_key] = old_containers.reject { |c| c == 'overflow1' || c == 'overflow2' } + + ELoot.msg(text: " Migrated #{type} hoarding overflow settings: enabled 'Use All Overflow Containers'") + migrated_settings = true + elsif !settings.key?(use_overflow_key) + # If the key doesn't exist and no old overflows were found, set to false + settings[use_overflow_key] = false + migrated_settings = true + end + end + end + + if migrated_settings + ELoot.save_profile() + end + end + + # Returns the canonical defaults hash with no side-effects. + # Used by load_defaults (which also persists it to disk) and by + # seed_missing_defaults (which backfills missing keys at load time). + def self.defaults_hash + { :loot_types => ["alchemy", "armor", "box", "breakable", "clothing", "collectible", "food", "gem", "jewelry", "lockpick", "lm trap", "magic", "reagent", "scroll", "skin", "uncommon", "valuable", "wand"], :loot_exclude => ["black ora", "urglaes"], :loot_phase => false, @@ -1767,8 +1850,7 @@ module ELoot # Profile loading/saving and settings :coin_hand_name => "", :sigil_determination_on_fail => false, :charm_name => "", - :overflow_container => "", - :secondary_overflow => "", + :overflow_containers => "", :sell_loot_types => ["alchemy", "armor", "breakable", "clothing", "food", "gem", "jewelry", "lockpick", "magic", "reagent", "scroll", "skin", "uncommon", "valuable", "wand", "box", "lm trap"], :sell_container => ["default", "overflow", "box", "collectible", "forageable", "gem", "herb", "lockpick", "potion", "reagent", "scroll", "skin", "treasure", "trinket", "wand"], :sell_exclude => [], @@ -1791,10 +1873,12 @@ module ELoot # Profile loading/saving and settings :sell_shroud => false, :sell_aspect => false, :sell_keep_silver => 0, + :trash_dump_types => ["herb", "junk", "food"], :skin_enable => false, :skin_kneel => false, :skin_604 => false, :skin_resolve => false, + :skin_bounty_only => false, :skin_sheath => "", :skin_weapon => "", :skin_sheath_blunt => "", @@ -1810,6 +1894,23 @@ module ELoot # Profile loading/saving and settings :favor_left => false, :log_unlootables => false } + end + + # Backfill any keys present in defaults_hash but absent from +settings+. + # Returns true if at least one key was added, so the caller knows to + # persist the profile. Safe to call without GTK. + def self.seed_missing_defaults(settings) + added = false + defaults_hash.each do |key, default_value| + next if settings.key?(key) + settings[key] = default_value + added = true + end + added + end + + def self.load_defaults() + default_hash = defaults_hash Dir.mkdir(File.join(DATA_DIR, XMLData.game)) unless File.exist?(File.join(DATA_DIR, XMLData.game)) Dir.mkdir(File.join(DATA_DIR, XMLData.game, Char.name)) unless File.exist?(File.join(DATA_DIR, XMLData.game, Char.name)) @@ -1914,7 +2015,7 @@ module ELoot # Sets Inventory end end - if list[key].nil? && caller_locations(1, 1)[0].label != 'set_inventory' + if list[key].nil? && !caller_locations(1, 1)[0].label.include?('set_inventory') ELoot.msg(text: " Can't find #{key}: #{item_name}. Please check ;eloot setup.", space: true) end end @@ -2026,11 +2127,27 @@ module ELoot # Sets Inventory # Remove any extra keys and repopulate them ReadyList.ready_list.delete_if { |k, _| [:skin_weapon, :skin_weapon_blunt, :skin_sheath, :skin_sheath_blunt].include?(k) } - StowList.stow_list.delete_if { |k, _| [:overflow_container, :secondary_overflow, :appraisal_container].include?(k) } + # Remove old overflow container keys (they may use sequential numbering now) + StowList.stow_list.delete_if { |k, _| k.to_s.start_with?('overflow_container') || k == :secondary_overflow || k == :appraisal_container } + StowList.stow_list.delete(:secondary_overflow) + + # Find the stow containers we need: overflow, appraisal + # Parse comma-separated overflow containers and add them dynamically + overflow_list = ELoot.data.settings[:overflow_containers].to_s.split(',').map(&:strip).reject(&:empty?) + overflow_list.each_with_index do |container_name, index| + # Create a unique key for each overflow container + key = index == 0 ? :overflow_container : "overflow_container_#{index + 1}".to_sym + # Temporarily set the setting for this specific container so ensure_items can find it + temp_settings_key = "overflow_temp_#{index}".to_sym + ELoot.data.settings[temp_settings_key] = container_name + ELoot.ensure_items(key: temp_settings_key, list: StowList.stow_list) + # Move the found item to the proper key + if StowList.stow_list[temp_settings_key] + StowList.stow_list[key] = StowList.stow_list.delete(temp_settings_key) + end + ELoot.data.settings.delete(temp_settings_key) + end - # Find the stow containers we need: stow, overflow, appraisal - ELoot.ensure_items(key: 'overflow_container', list: StowList.stow_list) - ELoot.ensure_items(key: 'secondary_overflow', list: StowList.stow_list) ELoot.ensure_items(key: 'appraisal_container', list: StowList.stow_list) # Find skin sheaths @@ -2079,26 +2196,32 @@ module ELoot # Sets Inventory container_array = [] - # Add the specific type container if it's in the sell list - if type && ELoot.data.settings[:sell_container].include?(type) - container = StowList.stow_list[type.to_sym] - container_array << container if container - end - # Add the default container if ELoot.data.settings[:sell_container].include?("default") container_array << StowList.default end - # Handle overflow containers + # Handle overflow containers - dynamically find all overflow container keys if ELoot.data.settings[:sell_container].include?("overflow") - [:overflow_container, :secondary_overflow].each do |key| - ELoot.ensure_items(key: key, list: StowList.stow_list) + overflow_keys = StowList.stow_list.keys.select { |k| k.to_s.start_with?('overflow_container') } + overflow_keys.each do |key| container = StowList.stow_list[key] container_array << container if container end end + # Add the specific type container if it's in the sell list + if type && ELoot.data.settings[:sell_container].include?(type) + container = StowList.stow_list[type.to_sym] + if container + unless type.eql?('box') + container_array.unshift(container) + else + container_array << container + end + end + end + # Remove duplicates and ensure an array is always returned container_array.compact.uniq end @@ -2107,8 +2230,6 @@ module ELoot # Sets Inventory need_exit = false checks = [ - [:overflow_container, StowList.stow_list[:overflow_container], "primary overflow container"], - [:secondary_overflow, StowList.stow_list[:secondary_overflow], "secondary overflow container"], [:appraisal_container, StowList.stow_list[:appraisal_container], "appraisal container"], [:skin_sheath, ReadyList.ready_list[:skin_sheath], "bladed skinning sheath", :skin_enable], [:skin_sheath_blunt, ReadyList.ready_list[:skin_sheath_blunt], "blunt skinning sheath", :skin_enable], @@ -2118,13 +2239,26 @@ module ELoot # Sets Inventory [:charm_name, ELoot.data.charm, "fossil charm"], ] - checks.each do |setting_key, found_obj, label, conditional_key| + # Dynamically add overflow container checks + overflow_list = ELoot.data.settings[:overflow_containers].to_s.split(',').map(&:strip).reject(&:empty?) + overflow_list.each_with_index do |container_name, index| + key = index == 0 ? :overflow_container : "overflow_container_#{index + 1}".to_sym + label = index == 0 ? "primary overflow container" : "overflow container #{index + 1}" + # For validation, we check against the overflow_containers setting (comma-separated list) + # but we look up the actual found object in StowList + if !container_name.empty? + checks << [:overflow_containers, StowList.stow_list[key], label, nil, container_name] + end + end + + checks.each do |setting_key, found_obj, label, conditional_key, configured_name| # Only check if setting is non-empty and optional condition (like :skin_enable) is satisfied - next if ELoot.data.settings[setting_key].to_s.empty? + next if setting_key != :overflow_containers && ELoot.data.settings[setting_key].to_s.empty? next if conditional_key && !ELoot.data.settings[conditional_key] if found_obj.nil? - ELoot.msg(text: " Not able to find the #{label}: #{ELoot.data.settings[setting_key]}") + container_name = configured_name || ELoot.data.settings[setting_key] + ELoot.msg(text: " Not able to find the #{label}: #{container_name}") need_exit = true end end @@ -2181,12 +2315,29 @@ module ELoot # Regional bounty Selling def self.tag_for_town(town, tag) ELoot.msg(type: "debug", text: "tag: #{tag} | town: #{town}") - self.by_town(tag).find { |k, _v| k.downcase.include?(town.downcase) }.last + match = self.by_town(tag).find { |k, _v| k.downcase.include?(town.downcase) } + match&.last end - def self.in_region(place) # Determine if the bounty town is within the characters region + # Determine if the bounty town is within the characters region + # vaalor ferry, west cart, east cart, RR boot + def self.in_region(place) ELoot.msg(type: "debug", text: "place: #{place}") - # vaalor ferry, west cart, east cart, RR boot + + bounty_town = Bounty.town.eql?("Cold River") ? "Hinterwilds" : Bounty.town + destination = self.tag_for_town(bounty_town, place) + return nil unless destination + + place = destination.id + + # Early return if already at destination + return place if Room.current.eql?(Room[place]) + + # Calculate path once + path = Room.current.path_to(Room[place]) + return nil unless path + + # Lazy evaluation - only compute boundaries if we need to check them boundaries = [ Map.ids_from_uid(14001002).first, # Ta'Vaalor Ferry Map.ids_from_uid(13002021).first, # Western Spine Mine Cart @@ -2204,12 +2355,8 @@ module ELoot # Regional bounty Selling Map.ids_from_uid(7133026).first, # Portmaster - Icemule Trace ] - bounty_town = Bounty.town == "Cold River" ? "Hinterwilds" : Bounty.town - - place = self.tag_for_town(bounty_town, place).id - path = Room.current.path_to(Room[place]) - - return place if path && boundaries.none? { |fence| path.include?(fence) } + # Use array intersection instead of iterating through each boundary + (path & boundaries).empty? ? place : nil end def self.furrier @@ -2453,7 +2600,9 @@ module ELoot # Script utility type methods rows << [{ value: " *** Full Disk/Sack Check ***", colspan: 7 }] rows << :separator rows << [{ value: "Disk Full: #{ELoot.data.disk_full.inspect}", colspan: 7 }] - rows << [{ value: "Sacks Full: #{ELoot.data.sacks_full.map(&:name)}", colspan: 7 }] + ELoot.data.sacks_full.each do |container, information| + rows << [{ value: "Sacks Full[#{container}]: #{information}", colspan: 7 }] + end rows << :separator rows << [{ value: " *** Contents ***", colspan: 7 }] @@ -2637,7 +2786,7 @@ module ELoot # Game utility type methods end unless ELoot.data.gauntlet.nil? - lines = ELoot.get_command("look ##{ELoot.data.gauntlet.id}", /You are currently wearing the eonake gauntlet/, silent: true, quiet: true) + lines = ELoot.get_command("look ##{ELoot.data.gauntlet.id}", /You are currently wearing the eonake gauntlet<\/a>/, silent: true, quiet: true) if lines.any? { |l| l =~ /(right|left) hand/ } gauntlet_hand = Regexp.last_match(1) end @@ -3022,7 +3171,7 @@ module ELoot # Game utility type methods balance = 0 lines = ELoot.get_command("check balance", /(?:The|A prim) teller/, silent: true, quiet: true) - if lines.find { |line| line.match(/Your balance is currently at (?[\d,]+)/) } + if lines.find { |line| line.match(/(?:Your balance is currently at |his temple for a moment. ")(?[\d,]+)/) } balance = Regexp.last_match[:silver].gsub(',', '').to_i end @@ -3045,8 +3194,8 @@ module ELoot # Game utility type methods # deposit the note and then attempt to withdraw the amount of silver needed Inventory.drag(note) - lines = ELoot.get_command("deposit ##{note.id}", /You (?:deposit|hand your)/, silent: true, quiet: true) - break if lines.find { |line| line.match(/worth (?[\d,]+)/) }.nil? + lines = ELoot.get_command("deposit ##{note.id}", /You (?:deposit|hand your)|takes your/, silent: true, quiet: true) + break if lines.find { |line| line.match(/(?:worth|for) (?[\d,]+)/) }.nil? note_amount = Regexp.last_match[:silver].gsub(',', '').to_i withdraw_amount = note_amount >= amount ? amount : note_amount dothistimeout("withdraw #{withdraw_amount} silver", 2, /The teller/) @@ -3345,8 +3494,12 @@ module ELoot # Inventory methods return unless ELoot.data.settings[:keep_closed] Inventory.open_single_container(StowList.stow_list[:default]) - Inventory.open_single_container(StowList.stow_list[:overflow_container]) - Inventory.open_single_container(StowList.stow_list[:secondary_overflow]) + + # Dynamically open all overflow containers + overflow_keys = StowList.stow_list.keys.select { |k| k.to_s.start_with?('overflow_container') } + overflow_keys.each do |key| + Inventory.open_single_container(StowList.stow_list[key]) + end containers = Array.new item.each { |loot| @@ -3433,32 +3586,39 @@ module ELoot # Inventory methods return if item&.name == "Empty" return if Inventory.single_drag_box(item) - # Try sacks in order of priority: item-specific, default, overflow, secondary overflow + # Try sacks in order of priority: item-specific, default, overflow containers (in order) containers = [ StowList.stow_list[item.type.to_sym], - StowList.stow_list[:default], - StowList.stow_list[:overflow_container], - StowList.stow_list[:secondary_overflow] + StowList.stow_list[:default] ] + # Dynamically add all overflow containers in order + overflow_keys = StowList.stow_list.keys.select { |k| k.to_s.start_with?('overflow_container') }.sort_by do |k| + # Sort by extracting number from key (overflow_container = 0, overflow_container_2 = 2, etc.) + k.to_s.match(/overflow_container_?(\d*)$/)[1].to_i + end + overflow_keys.each { |key| containers << StowList.stow_list[key] } + begin containers.each_with_index do |bag, index| if stunned? ELoot.wait_rt raise end - next if ELoot.data.sacks_full.include?(bag) + next if bag && ELoot.data.sacks_full.keys.include?(bag.name) if bag.nil? case index + when 0 + # Skip if item-specific container not set (this is normal) when 1 ELoot.msg(type: "yellow", text: " No default container identified. This shouldn't happen.") ELoot.msg(type: "yellow", text: " Check your STOW settings. Exiting") exit - when 2 - ELoot.msg(type: "info", text: " Skipping primary overflow. No container identified.") - when 3 - ELoot.msg(type: "info", text: " Skipping secondary overflow. No container identified.") + else + # Overflow container not identified (this is normal if not all overflow containers are set) + overflow_num = index - 1 + ELoot.msg(type: "info", text: " Skipping overflow container #{overflow_num}. No container identified.") end else result = Inventory.store_item(bag, item) @@ -3478,6 +3638,11 @@ module ELoot # Inventory methods if Room.current.tags.any?(/locksmith|locksmith pool/i) && (gold_ingot = [GameObj.right_hand, GameObj.left_hand].find { |obj| obj.name =~ /gold ingot/ }) Sell.handle_ingot(gold_ingot) else + if ELoot.data.sacks_full.keys.count > 1 + ELoot.data.sacks_full.each do |container, information| + ELoot.msg(type: "info", text: " Sacks Full[#{container}]: #{information}") + end + end ELoot.msg(type: "info", text: " Failed to store the #{item.name}.") ELoot.msg(type: "info", text: " Pausing the script to handle it yourself") ELoot.msg(type: "info", text: " ;unpause #{Script.current.name} after addressing to continue!") @@ -3501,7 +3666,9 @@ module ELoot # Inventory methods ELoot.wait_for_disk unless ELoot.data.disk.nil? # sort to prioritize characters disk - disks = Group.disks.sort_by { |d| d.name == Char.name ? 0 : 1 } + disks = Group.disks + disks = disks.select { |d| d.name == Char.name } unless ELoot.data.settings[:use_disk_group] + disks.sort_by! { |d| d.name == Char.name ? 0 : 1 } disks.each do |disk| next if ELoot.data.disk_full[disk.name] # make sure its not full next unless Disk.find_by_name(disk.name) # skip the disk unless its present @@ -3524,9 +3691,9 @@ module ELoot # Inventory methods # loot item will fallback to the default sack but if both are full we need to loot manually bag = StowList.stow_list[item.type.to_sym] - bag = nil if bag.nil? || ELoot.data.sacks_full.include?(bag) + bag = nil if bag.nil? || ELoot.data.sacks_full.keys.include?(bag.name) bag ||= StowList.stow_list[:default] - bag = nil if bag.nil? || ELoot.data.sacks_full.include?(bag) + bag = nil if bag.nil? || ELoot.data.sacks_full.keys.include?(bag.name) unless bag Inventory.single_drag(item) @@ -3580,7 +3747,10 @@ module ELoot # Inventory methods lines = ELoot.get_command("_drag ##{item.id} ##{bag.id}", ELoot.data.put_regex) end - return true if lines.any? { |l| l =~ /You are unable to handle|That is not yours|Hey, that belongs to|Get what|I could not find what you were referring/ } + lines.each do |line| + return false if line.match?(/Hey, that belongs to/) + return true if line.match?(/You are unable to handle|That is not yours|Get what|I could not find what you were referring/) + end if lines.any? { |l| l =~ /put something that you can't hold/ } ELoot.unlootable(item) @@ -3591,7 +3761,7 @@ module ELoot # Inventory methods if lines.any? { |l| l =~ /won't fit/i } unless item.name =~ /gold ingot/ ELoot.msg(type: "debug", text: "sacks_full(#{bag.name}) - item: #{item.inspect}") - ELoot.data.sacks_full.push(bag) + ELoot.data.sacks_full[bag.name] = [Time.now.strftime('%Y-%m-%d %H:%M:%S'), Room.current.id, "#{item.name}(#{item.id})"] end return false end @@ -4245,8 +4415,17 @@ module ELoot # Gem and Reagent hoarding ELoot.data.items_to_hoard = [] obj_type = ELoot.data.hoard_type == 'alchemy' ? 'reagent' : ELoot.data.hoard_type + # Get containers from the container_settings array item_containers = ELoot.data.container_settings.filter_map { |key| StowList.stow_list[key.to_sym] } + # If use_overflow is enabled, dynamically add all overflow containers + use_overflow_key = "#{ELoot.data.hoard_type}_horde_use_overflow".to_sym + if ELoot.data.settings[use_overflow_key] + overflow_keys = StowList.stow_list.keys.select { |k| k.to_s.start_with?('overflow_container') } + overflow_containers = overflow_keys.filter_map { |key| StowList.stow_list[key] } + item_containers.concat(overflow_containers) + end + item_containers.each do |container| next unless container @@ -4254,7 +4433,7 @@ module ELoot # Gem and Reagent hoarding ELoot.data.items_to_hoard.concat(container.contents.to_a) end - return ELoot.data.items_to_hoard.select { |item| item.name =~ /#{single}/ } if single + return ELoot.data.items_to_hoard.select { |item| item.name =~ /#{single}/ && item.type =~ /#{obj_type}/ } if single ELoot.data.items_to_hoard.reject! { |item| item.type !~ /#{obj_type}/ } @@ -4334,7 +4513,7 @@ module ELoot # Gem and Reagent hoarding if bottle 3.times do - Inventory.drag(bottle) + Inventory.drag(bottle, 'left') break if ELoot.in_hand?(bottle) # assuming if we are still here and its a locker it reset on us if ELoot.data.locker || ELoot.data.use_house_locker @@ -4345,7 +4524,7 @@ module ELoot # Gem and Reagent hoarding jar = [GameObj.right_hand, GameObj.left_hand].find { |i| i.noun =~ /^(?:jar|bottle|beaker)$/ } elsif empty 3.times do - Inventory.drag(empty) + Inventory.drag(empty, 'left') break if ELoot.in_hand?(empty) # assuming if we are still here and its a locker it reset on us if ELoot.data.locker || ELoot.data.use_house_locker @@ -4376,7 +4555,7 @@ module ELoot # Gem and Reagent hoarding items_to_hoard.each do |thing| thing_name = Hoard.normalize_name(thing.name) - Inventory.drag(thing) + Inventory.drag(thing, 'right') result = ELoot.get_res("_drag ##{thing.id} ##{jar.id}", /You (add|put)|The.*?is full/) @@ -4563,7 +4742,7 @@ module ELoot # Room looting box_list.each do |box| line = ELoot.get_res("open ##{box.id}", /open|locked/) - next if line&.match?(/That is locked/) + next if line&.match?(/It appears to be locked/) quiet_msg = ELoot.data.settings[:display_box_contents] ? false : true ELoot.get_command("look in ##{box.id}", ELoot.data.look_regex, silent: quiet_msg, quiet: quiet_msg) @@ -4646,7 +4825,7 @@ module ELoot # Room looting items_opened = Array.new items.each { |item| item_type = item.type.to_sym - if StowList.stow_list[item_type] && !ELoot.data.sacks_full.include?(StowList.stow_list[item_type]) + if StowList.stow_list[item_type] && !ELoot.data.sacks_full.keys.include?(StowList.stow_list[item_type].name) bag = StowList.stow_list[item_type] else bag = StowList.stow_list[:default] @@ -4909,7 +5088,7 @@ module ELoot # Room looting # Loot it 3.times do waitrt? - results = ELoot.get_command("loot ##{thing.id}", /You (search|plunge|break)|not in any condition|see well enough to search|You can only loot creatures|Geez! It's still alive! Not a good time for that\./, silent: false, quiet: false) + results = ELoot.get_command("loot ##{thing.id}", /You (search|plunge|break|tentatively)|not in any condition|see well enough to search|You can only loot creatures|Geez! It's still alive! Not a good time for that\.|As you move to search/, silent: false, quiet: false) ELoot.msg(type: "debug", text: "Thing: #{thing.id}-#{thing.name}, Results: #{results.first}") if results.any? { |line| line =~ /not in any condition/ } && @@ -4930,7 +5109,7 @@ module ELoot # Room looting end end - break if results.any? { |line| line =~ /You (search|plunge|break)|not in any condition|see well enough to search|You can only loot creatures|Geez! It's still alive! Not a good time for that\./ } || + break if results.any? { |line| line =~ /You (search|plunge|break|tentatively)|not in any condition|see well enough to search|You can only loot creatures|Geez! It's still alive! Not a good time for that\./ } || thing.nil? || thing.status =~ /gone/ end @@ -5067,6 +5246,12 @@ module ELoot # Room looting ELoot.data.settings[:critter_exclude].length.positive? && obj.name =~ Regexp.union(ELoot.data.settings[:critter_exclude]) end + if ELoot.data.settings[:skin_bounty_only] + return unless Bounty.task.skin? + bounty_creature = Bounty.task.creature.to_s.downcase + objs = objs.select { |obj| obj.name.to_s.downcase.include?(bounty_creature) } + end + return if objs.empty? blunts = objs.find_all { |obj| obj.name =~ /krynch|stone mastiff|krag dweller|cavern urchin/i } @@ -5733,14 +5918,15 @@ module ELoot # Sells the loot end def self.dump_herbs_junk - # Determine which types of loot we are supposed to dump - dump_stuff = ["herb", "junk", "food"].select { |type| ELoot.data.settings[:sell_loot_types].include?(type) } + # Determine which types of loot we are supposed to dump based on trash_dump_types setting + dump_stuff = ELoot.data.settings[:trash_dump_types].select { |type| ELoot.data.settings[:sell_loot_types].include?(type) } alchemy_regex = /^(some ground|flask of pure water|some powdered|some mashed|handful of sea salt|spirit shard|tincture of)/ # Collect items to dump dump_items = ELoot.set_selling_containers.flat_map(&:contents).select do |item| next false unless dump_stuff.any? { |type| item.type =~ /#{type}/ } || (ELoot.data.alchemy_mode && item.name =~ alchemy_regex) next false if ELoot.data.alchemy_mode && Vars.needed_reagents.any? { |r| item.name =~ /#{r}/ } + next false if !ELoot.data.settings[:sell_exclude].empty? && item.name =~ /#{ELoot.data.settings[:sell_exclude].join('|')}/ true end @@ -6134,7 +6320,7 @@ module ELoot # Sells the loot ELoot.data.settings[:between].each do |i| tokens = i.split(/\s+/) if (tokens.size > 1) - Script.run(tokens[0], tokens[1..-1].join(", ")) + Script.run(tokens[0], tokens[1..-1].join(" ")) else Script.run(tokens[0]) end @@ -6235,13 +6421,7 @@ module ELoot # Sells the loot # if we're here, assume we will empty out the disk ELoot.reset_disk_full - if boxes.length.positive? && ELoot.data.settings[:use_standard_tipping] - amount = ELoot.data.settings[:locksmith_withdraw_amount] - ELoot.silver_withdraw(amount) - end - ELoot.go2('locksmith pool') - original_pool = Room.current.id worker = ELoot.find_worker # if using incremental tipping, need to find out how many boxes are in the pool @@ -6250,11 +6430,6 @@ module ELoot # Sells the loot if pool_count > 99 pool_count = handle_full_pool(worker) { return true if deposit } return if pool_count > 99 - else - total_tips = Sell.locksmith_determine_tip(pool_count + 1, boxes.length) + 15_000 - ELoot.silver_withdraw(total_tips) - ELoot.go2(original_pool) - worker = ELoot.find_worker end end @@ -6294,9 +6469,7 @@ module ELoot # Sells the loot pool_count = handle_full_pool(worker) { return true if deposit } end when /You don't have that much/ - ELoot.silver_withdraw(ELoot.data.settings[:locksmith_withdraw_amount]) - ELoot.go2(original_pool) - redo_needed = true + redo_needed = false when /already holding as many boxes/ Inventory.single_drag(box) unless box_in_hand pool_count = handle_full_pool(worker) { return true if deposit } @@ -6553,7 +6726,7 @@ module ELoot # Sells the loot def self.process_boxes boxes = ELoot.find_boxes - return unless boxes.any? + return unless boxes.any? || ELoot.data.settings[:always_check_pool] ELoot.msg(type: "debug", text: "length: #{boxes.length}") @@ -6714,14 +6887,14 @@ module ELoot # Starts the script # Initialize/Load settings unless ELoot.data && ELoot.data.respond_to?(:version) && ELoot.data.version == ELoot.get_script_version waitrt? - ELoot.load(ELoot.load_profile()) + ELoot.load(ELoot.load_profile) ELoot.set_inventory ELoot.disk_usage exit if Script.current.vars[0] =~ /load/ end if !ELoot.data.settings[:track_full_sacks] - ELoot.data.sacks_full = [] + ELoot.data.sacks_full = { "last cleared" => [Time.now.strftime('%Y-%m-%d %H:%M:%S'), Room.current.id, Script.current.vars[0]] } ELoot.reset_disk_full end @@ -6799,7 +6972,7 @@ module ELoot # Starts the script ELoot.wait_rt # Set bags as empty to avoid false full status - ELoot.data.sacks_full = [] + ELoot.data.sacks_full = { "last cleared" => [Time.now.strftime('%Y-%m-%d %H:%M:%S'), Room.current.id, Script.current.vars[0]] } if Script.current.vars[2] == "alchemy_mode" ELoot.data.alchemy_mode = true @@ -6809,7 +6982,7 @@ module ELoot # Starts the script ELoot.sell # Reset bags to empty after all the selling is done - ELoot.data.sacks_full = [] + ELoot.data.sacks_full = { "last cleared" => [Time.now.strftime('%Y-%m-%d %H:%M:%S'), Room.current.id, Script.current.vars[0]] } ELoot.go2(ELoot.data.start_room) ELoot::Sell.breakdown @@ -6818,6 +6991,8 @@ module ELoot # Starts the script ELoot.data.debug_logger = ELoot::DebugLogger.new if ELoot.data.settings[:debug_file] arg = Script.current.vars[2] + ELoot.data.sacks_full = { "last cleared" => [Time.now.strftime('%Y-%m-%d %H:%M:%S'), Room.current.id, Script.current.vars[0]] } + if arg.nil? check = true deposit = true @@ -6829,16 +7004,23 @@ module ELoot # Starts the script ELoot.disk_usage ELoot::Sell.pool(deposit: deposit, check: check) + ELoot.data.sacks_full = { "last cleared" => [Time.now.strftime('%Y-%m-%d %H:%M:%S'), Room.current.id, Script.current.vars[0]] } + ELoot::Sell.breakdown when /--(sellable|type|sell)\b\s*=?\s*(\/?[a-zA-Z,\s|]+\/?)/ ELoot.data.debug_logger = ELoot::DebugLogger.new if ELoot.data.settings[:debug_file] type = Regexp.last_match(1) things = Regexp.last_match(2) + ELoot.data.sacks_full = { "last cleared" => [Time.now.strftime('%Y-%m-%d %H:%M:%S'), Room.current.id, Script.current.vars[0]] } + ELoot::Sell.box_in_hand(false) + ELoot::Sell.custom_sellable(things) if type == 'sellable' ELoot::Sell.custom_type(things) if type == 'type' ELoot::Sell.custom_list(things) if type == 'sell' + ELoot.data.sacks_full = { "last cleared" => [Time.now.strftime('%Y-%m-%d %H:%M:%S'), Room.current.id, Script.current.vars[0]] } + ELoot.go2(ELoot.data.start_room) ELoot::Sell.breakdown when /settings|setup/i