UO Unchained: ITEM_filter_junk_salvager.py

Created: about 23 hours ago on 08/06/2025, 06:54:35 PM
Size: 52488
Category: ITEM
Hotkey: O

Description: Item filtering by Type and Affix Tier , moves unfavored equipment into a red dyed "junk" backpack for salvaging

""" ITEM Filter Junk Salvager - a Razor Enhanced Python Script for Ultima Online A Strict Item Filter , configurable to save items by type and by tier of their properties - Moves low tier items to a junk backpack based on Tier configuration - Salvages items in the "junk" (red) backpack ( unchained ) - Configurable Tier reservations by their properties ( Vanquishing , Greater Slaying , Invulnerable ) Adjust the settings to your liking , default setting = - only save leather , plate , and maces , of tier 1 highest ( invulnable , vanquishing ) Requirements: - Any configured salvage tool in player's backpack ( tinker tools , or sewing kit ) - A red dyed backpack that will hold the salvagable items # TODO: - have equip_slot be apart of the item dicts properties - implement single item strictness HOTKEY:: O VERSION::20250806 """ #//======================================================================================== # SETTINGS DEBUG_MODE = False # Set to True to enable debug/info messages AUTO_SALVAGE = True # Set to False to skip auto-salvaging (for debugging) # ITEM TYPE-based filtering , SAVE itmes you favor , set to False types unfavored SAVE_ITEM_WEAPON_AXE = False SAVE_ITEM_WEAPON_SWORD = False SAVE_ITEM_WEAPON_MACE = True # defaulting only maces SAVE_ITEM_WEAPON_FENCING = False SAVE_ITEM_WEAPON_ARCHERY = False SAVE_ITEM_WEAPON_UNKNOWN = False # Default is only saving leather and plate armor SAVE_ITEM_ARMOR_LEATHER = True # defaulting leather armor for mages SAVE_ITEM_ARMOR_PLATE = True # defaulting plate armor for warriors SAVE_ITEM_ARMOR_CHAINMAIL = False SAVE_ITEM_ARMOR_RINGMAIL = False SAVE_ITEM_ARMOR_STUDDED = False SAVE_ITEM_ARMOR_BONE = False SAVE_ITEM_ARMOR_SHIELD = True # defaulting save shields # STRICTEST FILTERING - SINGLE ITEM ID SAVE_ONLY_THIS_ONE_WEAPON = False # sometimes we only favor a single weapon id SAVE_ONLY_THIS_ONE_WEAPON_ID = 0x143D # Hammer Pick SAVE_ONLY_THIS_ONE_ARMOR = False # sometimes we only favor a single armor id SAVE_ONLY_THIS_ONE_ARMOR_ID = 0x0F5C # Leather female plate SAVE_ONLY_THIS_ONE_SHIELD = False # sometimes we only favor a single shield id SAVE_ONLY_THIS_ONE_SHIELD_ID = 0x1BC4 # Order shield # AFFIX TIER FILTERING RESERVE_TIERS = { 'TIER1': True, # Set to True to reserve Tier 1 items 'TIER2': True, # Set to True to reserve Tier 2 items 'TIER3': True, # Set to True to reserve Tier 3 items 'TIER4': True, # Set to True to reserve Tier 4 items 'MAGICAL': False # Set to True to reserve other magical items } # TIER AFFIX DEFINITIONS (GLOBAL) TIER1_AFFIXES = [ # these are the best "Vanquishing", # +25 damage "Greater", # Slaying +25% "Invulnerable" # +100% armor ] TIER2_AFFIXES = [ "Power", # +20 damage "Supremely Accurate", # +25% Hit Chance "Fortification" # +80% armor ] TIER3_AFFIXES = [ "Force", # +15 damage "Massive" # +60% armor ] TIER4_AFFIXES = [ "Might", # +10 damage "Substantial" # +40% armor ] # Junk Backpack Configuration JUNK_BACKPACK_ID = 0x0E75 # a backpack JUNK_BACKPACK_HUES = [0x0021, 0x0026, 0x002B] # Range of red hues that are acceptable JUNK_BACKPACK_SERIAL = 0 # Will be auto-set by find_junk_backpack() # Arcane Dust ARCANE_DUST_ITEMID = 0x5745 ARCANE_DUST_COLOR = 0x07ad # Item Type Configuration #//======================================================================================== # Weapon Category Info Mapping Split by Sub Type WEAPON_AXE_INFO = { 0x0F49: { 'name': 'Axe', 'id': '0x0F49', 'subtype': 'Axe', 'category': 'Weapon' }, 0x0F47: { 'name': 'Battle Axe', 'id': '0x0F47', 'subtype': 'Axe', 'category': 'Weapon' }, 0x0F4B: { 'name': 'Double Axe', 'id': '0x0F4B', 'subtype': 'Axe', 'category': 'Weapon' }, 0x0F45: { 'name': "Executioner's Axe", 'id': '0x0F45', 'subtype': 'Axe', 'category': 'Weapon' }, 0x0F43: { 'name': 'Hatchet', 'id': '0x0F43', 'subtype': 'Axe', 'category': 'Weapon' }, 0x13FB: { 'name': 'Large Battle Axe', 'id': '0x13FB', 'subtype': 'Axe', 'category': 'Weapon' }, 0x1443: { 'name': 'Two Handed Axe', 'id': '0x1443', 'subtype': 'Axe', 'category': 'Weapon' }, 0x13B0: { 'name': 'War Axe', 'id': '0x13B0', 'subtype': 'Axe', 'category': 'Weapon' }, } WEAPON_SWORD_INFO = { 0x0F5E: { 'name': 'Broadsword', 'id': '0x0F5E', 'subtype': 'Sword', 'category': 'Weapon' }, 0x1441: { 'name': 'Cutlass', 'id': '0x1441', 'subtype': 'Sword', 'category': 'Weapon' }, 0x13FF: { 'name': 'Katana', 'id': '0x13FF', 'subtype': 'Sword', 'category': 'Weapon' }, 0x0F61: { 'name': 'Longsword', 'id': '0x0F61', 'subtype': 'Sword', 'category': 'Weapon' }, 0x13B6: { 'name': 'Scimitar', 'id': '0x13B6', 'subtype': 'Sword', 'category': 'Weapon' }, 0x13B9: { 'name': 'Viking Sword', 'id': '0x13B9', 'subtype': 'Sword', 'category': 'Weapon' }, } WEAPON_MACE_INFO = { 0x13B4: { 'name': 'Club', 'id': '0x13B4', 'subtype': 'Mace', 'category': 'Weapon' }, 0x143D: { 'name': 'Hammer Pick', 'id': '0x143D', 'subtype': 'Mace', 'category': 'Weapon' }, 0x0F5C: { 'name': 'Mace', 'id': '0x0F5C', 'subtype': 'Mace', 'category': 'Weapon' }, 0x143B: { 'name': 'Maul', 'id': '0x143B', 'subtype': 'Mace', 'category': 'Weapon' }, 0x1439: { 'name': 'War Hammer', 'id': '0x1439', 'subtype': 'Mace', 'category': 'Weapon' }, 0x1407: { 'name': 'War Mace', 'id': '0x1407', 'subtype': 'Mace', 'category': 'Weapon' }, 0x0DF0: { 'name': 'Black Staff', 'id': '0x0DF0', 'subtype': 'Mace', 'category': 'Weapon' }, 0x13F8: { 'name': 'Gnarled Staff', 'id': '0x13F8', 'subtype': 'Mace', 'category': 'Weapon' }, 0x0E89: { 'name': 'Quarter Staff', 'id': '0x0E89', 'subtype': 'Mace', 'category': 'Weapon' }, } WEAPON_FENCING_INFO = { 0x0EC3: { 'name': 'Cleaver', 'id': '0x0EC3', 'subtype': 'Fencing', 'category': 'Weapon' }, 0x0EC4: { 'name': 'Skinning Knife', 'id': '0x0EC4', 'subtype': 'Fencing', 'category': 'Weapon' }, 0x13F6: { 'name': 'Butcher Knife', 'id': '0x13F6', 'subtype': 'Fencing', 'category': 'Weapon' }, 0x0F52: { 'name': 'Dagger', 'id': '0x0F52', 'subtype': 'Fencing', 'category': 'Weapon' }, 0x0F62: { 'name': 'Spear', 'id': '0x0F62', 'subtype': 'Fencing', 'category': 'Weapon' }, 0x1403: { 'name': 'Short Spear', 'id': '0x1403', 'subtype': 'Fencing', 'category': 'Weapon' }, 0x1405: { 'name': 'War Fork', 'id': '0x1405', 'subtype': 'Fencing', 'category': 'Weapon' }, 0x1401: { 'name': 'Kryss', 'id': '0x1401', 'subtype': 'Fencing', 'category': 'Weapon' }, } WEAPON_ARCHERY_INFO = { 0x13B2: { 'name': 'Bow', 'id': '0x13B2', 'subtype': 'Archery', 'category': 'Weapon' }, 0x13B1: { 'name': 'Bow (Alternate)', 'id': '0x13B1', 'subtype': 'Archery', 'category': 'Weapon' }, 0x26C2: { 'name': 'Composite Bow', 'id': '0x26C2', 'subtype': 'Archery', 'category': 'Weapon' }, 0x0F50: { 'name': 'Crossbow', 'id': '0x0F50', 'subtype': 'Archery', 'category': 'Weapon' }, 0x13FD: { 'name': 'Heavy Crossbow', 'id': '0x13FD', 'subtype': 'Archery', 'category': 'Weapon' }, 0x26C3: { 'name': 'Repeating Crossbow', 'id': '0x26C3', 'subtype': 'Archery', 'category': 'Weapon' }, 0x2D1F: { 'name': 'Magical Shortbow', 'id': '0x2D1F', 'subtype': 'Archery', 'category': 'Weapon' }, } WEAPON_UNKNOWN_INFO = { 0x0E86: { 'name': 'Pickaxe', 'id': '0x0E86', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Axe 0x0DF2: { 'name': 'Magic Wand', 'id': '0x0DF2', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Mace 0x26BC: { 'name': 'Scepter', 'id': '0x26BC', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Mace 0x1439: { 'name': 'War Hammer', 'id': '0x1439', 'subtype': 'Unknown', 'category': 'Weapon' }, # confirmed 0x0F4D: { 'name': 'Bardiche', 'id': '0x0F4D', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Axe 0x143E: { 'name': 'Halberd', 'id': '0x143E', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Axe 0x26BA: { 'name': 'Scythe', 'id': '0x26BA', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Axe 0x26BD: { 'name': 'Bladed Staff', 'id': '0x26BD', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Polearm 0x26BF: { 'name': 'Double Bladed Staff', 'id': '0x26BF', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Polearm 0x26BE: { 'name': 'Pike', 'id': '0x26BE', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Polearm 0x0E87: { 'name': 'Pitchfork', 'id': '0x0E87', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Fencing 0x0E81: { 'name': "Shepherd's Crook", 'id': '0x0E81', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Mace 0x26BB: { 'name': 'Bone Harvester', 'id': '0x26BB', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Axe 0x26C5: { 'name': 'Bone Harvester (Alternate)', 'id': '0x26C5', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Axe 0x26C1: { 'name': 'Crescent Blade', 'id': '0x26C1', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Sword 0x1400: { 'name': 'Kryss (Alternate)', 'id': '0x1400', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Fencing 0x26C0: { 'name': 'Lance', 'id': '0x26C0', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Polearm 0x27A8: { 'name': 'Bokuto', 'id': '0x27A8', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Mace 0x27A9: { 'name': 'Daisho', 'id': '0x27A9', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Sword 0x27AD: { 'name': 'Kama', 'id': '0x27AD', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Fencing 0x27A7: { 'name': 'Lajatang', 'id': '0x27A7', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Polearm 0x27A2: { 'name': 'No-Dachi', 'id': '0x27A2', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Sword 0x27AE: { 'name': 'Nunchaku', 'id': '0x27AE', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Mace 0x27AF: { 'name': 'Sai', 'id': '0x27AF', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Fencing 0x27AB: { 'name': 'Tekagi', 'id': '0x27AB', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Fencing 0x27A3: { 'name': 'Tessen', 'id': '0x27A3', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Fencing 0x27A6: { 'name': 'Tetsubo', 'id': '0x27A6', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Mace 0x27A4: { 'name': 'Wakizashi', 'id': '0x27A4', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Sword 0x27A5: { 'name': 'Yumi', 'id': '0x27A5', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Archery 0x2D21: { 'name': 'Assassin Spike', 'id': '0x2D21', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Fencing 0x2D24: { 'name': 'Diamond Mace', 'id': '0x2D24', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Mace 0x2D1E: { 'name': 'Elven Composite Longbow', 'id': '0x2D1E', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Archery 0x2D35: { 'name': 'Elven Machete', 'id': '0x2D35', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Sword 0x2D20: { 'name': 'Elven Spellblade', 'id': '0x2D20', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Sword 0x2D22: { 'name': 'Leafblade', 'id': '0x2D22', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Fencing 0x2D2B: { 'name': 'Magical Shortbow (Alternate)', 'id': '0x2D2B', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Archery 0x2D28: { 'name': 'Ornate Axe', 'id': '0x2D28', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Axe 0x2D33: { 'name': 'Radiant Scimitar', 'id': '0x2D33', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Sword 0x2D32: { 'name': 'Rune Blade', 'id': '0x2D32', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Sword 0x2D2F: { 'name': 'War Cleaver', 'id': '0x2D2F', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Axe 0x2D25: { 'name': 'Wild Staff', 'id': '0x2D25', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Mace 0x406B: { 'name': 'Soul Glaive', 'id': '0x406B', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Throwing 0x406C: { 'name': 'Cyclone', 'id': '0x406C', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Throwing 0x4067: { 'name': 'Boomerang', 'id': '0x4067', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Throwing 0x08FE: { 'name': 'Bloodblade', 'id': '0x08FE', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Sword 0x0903: { 'name': 'Disc Mace', 'id': '0x0903', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Mace 0x090B: { 'name': 'Dread Sword', 'id': '0x090B', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Sword 0x0904: { 'name': 'Dual Pointed Spear', 'id': '0x0904', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Fencing 0x08FD: { 'name': 'Dual Short Axes', 'id': '0x08FD', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Axe 0x48B2: { 'name': 'Gargish Axe', 'id': '0x48B2', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Axe 0x48B4: { 'name': 'Gargish Bardiche', 'id': '0x48B4', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Axe 0x48B0: { 'name': 'Gargish Battle Axe', 'id': '0x48B0', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Axe 0x48C6: { 'name': 'Gargish Bone Harvester', 'id': '0x48C6', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Axe 0x48B6: { 'name': 'Gargish Butcher Knife', 'id': '0x48B6', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Fencing 0x48AE: { 'name': 'Gargish Cleaver', 'id': '0x48AE', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Fencing 0x0902: { 'name': 'Gargish Dagger', 'id': '0x0902', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Fencing 0x48D0: { 'name': 'Gargish Daisho', 'id': '0x48D0', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Sword 0x48B8: { 'name': 'Gargish Gnarled Staff', 'id': '0x48B8', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Mace 0x48BA: { 'name': 'Gargish Katana', 'id': '0x48BA', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Sword 0x48BC: { 'name': 'Gargish Kryss', 'id': '0x48BC', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Fencing 0x48CA: { 'name': 'Gargish Lance', 'id': '0x48CA', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Polearm 0x48C2: { 'name': 'Gargish Maul', 'id': '0x48C2', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Mace 0x48C8: { 'name': 'Gargish Pike', 'id': '0x48C8', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Polearm 0x48C4: { 'name': 'Gargish Scythe', 'id': '0x48C4', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Axe 0x0908: { 'name': 'Gargish Talwar', 'id': '0x0908', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Sword 0x48CE: { 'name': 'Gargish Tekagi', 'id': '0x48CE', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Fencing 0x48CC: { 'name': 'Gargish Tessen', 'id': '0x48CC', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Fencing 0x48C0: { 'name': 'Gargish War Hammer', 'id': '0x48C0', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Mace 0x0905: { 'name': 'Glass Staff', 'id': '0x0905', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Mace 0x090C: { 'name': 'Glass Sword', 'id': '0x090C', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Sword 0x0906: { 'name': 'Serpentstone Staff', 'id': '0x0906', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Mace 0x0907: { 'name': 'Shortblade', 'id': '0x0907', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Fencing 0x0900: { 'name': 'Stone War Sword', 'id': '0x0900', 'subtype': 'Unknown', 'category': 'Weapon' }, # guess: Sword } # Merge all to get the full WEAPON_INFO : WEAPON_INFO = {**WEAPON_AXE_INFO, **WEAPON_SWORD_INFO, **WEAPON_MACE_INFO, **WEAPON_FENCING_INFO, **WEAPON_ARCHERY_INFO, **WEAPON_UNKNOWN_INFO} # Shield Info Mapping # Each entry: itemID: { 'name': ..., 'id': ..., 'subtype': ..., 'category': ... } SHIELD_BASE_INFO = { 0x1B72: { 'name': 'Bronze Shield', 'id': '0x1B72', 'subtype': 'Shield', 'category': 'Shield' }, 0x1B73: { 'name': 'Buckler', 'id': '0x1B73', 'subtype': 'Shield', 'category': 'Shield' }, 0x1B74: { 'name': 'Metal Kite Shield', 'id': '0x1B74', 'subtype': 'Shield', 'category': 'Shield' }, 0x1B75: { 'name': 'Shield', 'id': '0x1B75', 'subtype': 'Shield', 'category': 'Shield' }, 0x1B76: { 'name': 'Heater Shield', 'id': '0x1B76', 'subtype': 'Shield', 'category': 'Shield' }, 0x1B77: { 'name': 'Shield', 'id': '0x1B77', 'subtype': 'Shield', 'category': 'Shield' }, 0x1B78: { 'name': 'Wooden Shield', 'id': '0x1B78', 'subtype': 'Shield', 'category': 'Shield' }, 0x1B79: { 'name': 'Shield', 'id': '0x1B79', 'subtype': 'Shield', 'category': 'Shield' }, 0x1B7A: { 'name': 'Wooden Kite Shield', 'id': '0x1B7A', 'subtype': 'Shield', 'category': 'Shield' }, 0x1B7B: { 'name': 'Metal Shield', 'id': '0x1B7B', 'subtype': 'Shield', 'category': 'Shield' }, 0x1BC3: { 'name': 'Chaos Shield', 'id': '0x1BC3', 'subtype': 'Shield', 'category': 'Shield' }, 0x1BC4: { 'name': 'Order Shield', 'id': '0x1BC4', 'subtype': 'Shield', 'category': 'Shield' }, } SHIELD_UNKNOWN_INFO = { 0x1BC5: { 'name': 'Shield', 'id': '0x1BC5', 'subtype': 'Unknown', 'category': 'Shield' }, 0x4201: { 'name': 'Shield', 'id': '0x4201', 'subtype': 'Unknown', 'category': 'Shield' }, # guess: Shield 0x4200: { 'name': 'Shield', 'id': '0x4200', 'subtype': 'Unknown', 'category': 'Shield' }, # guess: Shield 0x4202: { 'name': 'Shield', 'id': '0x4202', 'subtype': 'Unknown', 'category': 'Shield' }, # guess: Shield 0x4203: { 'name': 'Shield', 'id': '0x4203', 'subtype': 'Unknown', 'category': 'Shield' }, # guess: Shield 0x4204: { 'name': 'Shield', 'id': '0x4204', 'subtype': 'Unknown', 'category': 'Shield' }, # guess: Shield 0x4205: { 'name': 'Shield', 'id': '0x4205', 'subtype': 'Unknown', 'category': 'Shield' }, # guess: Shield 0x4206: { 'name': 'Shield', 'id': '0x4206', 'subtype': 'Unknown', 'category': 'Shield' }, # guess: Shield 0x4207: { 'name': 'Shield', 'id': '0x4207', 'subtype': 'Unknown', 'category': 'Shield' }, # guess: Shield 0x4208: { 'name': 'Shield', 'id': '0x4208', 'subtype': 'Unknown', 'category': 'Shield' }, # guess: Shield 0x4228: { 'name': 'Shield', 'id': '0x4228', 'subtype': 'Unknown', 'category': 'Shield' }, # guess: Shield 0x4229: { 'name': 'Shield', 'id': '0x4229', 'subtype': 'Unknown', 'category': 'Shield' }, # guess: Shield 0x422A: { 'name': 'Shield', 'id': '0x422A', 'subtype': 'Unknown', 'category': 'Shield' }, # guess: Shield 0x422C: { 'name': 'Shield', 'id': '0x422C', 'subtype': 'Unknown', 'category': 'Shield' }, # guess: Shield 0x7817: { 'name': 'Shield', 'id': '0x7817', 'subtype': 'Unknown', 'category': 'Shield' }, # guess: Shield 0x7818: { 'name': 'Shield', 'id': '0x7818', 'subtype': 'Unknown', 'category': 'Shield' }, # guess: Shield 0xA649: { 'name': 'Shield', 'id': '0xA649', 'subtype': 'Unknown', 'category': 'Shield' }, # guess: Shield } SHIELD_INFO = {**SHIELD_BASE_INFO, **SHIELD_UNKNOWN_INFO} # Armor Info Mapping # Each entry: itemID: { 'name': ..., 'id': ..., 'subtype': ..., 'category': ... } ARMOR_LEATHER_INFO = { 0x13CD: { 'name': 'Leather Sleeves', 'id': '0x13CD', 'subtype': 'Leather', 'category': 'Armor' }, 0x13CB: { 'name': 'Leather Leggings', 'id': '0x13CB', 'subtype': 'Leather', 'category': 'Armor' }, 0x13CC: { 'name': 'Leather Tunic', 'id': '0x13CC', 'subtype': 'Leather', 'category': 'Armor' }, 0x13C6: { 'name': 'Leather Gloves', 'id': '0x13C6', 'subtype': 'Leather', 'category': 'Armor' }, 0x1DB9: { 'name': 'Leather Cap', 'id': '0x1DB9', 'subtype': 'Leather', 'category': 'Armor' }, 0x1C08: { 'name': 'Leather Skirt', 'id': '0x1C08', 'subtype': 'Leather', 'category': 'Armor' }, 0x1C06: { 'name': 'Leather Armor (Female)', 'id': '0x1C06', 'subtype': 'Leather', 'category': 'Armor' }, 0x277A: { 'name': 'Leather Mempo', 'id': '0x277A', 'subtype': 'Leather', 'category': 'Armor' }, 0x1C00: { 'name': 'Leather Shorts', 'id': '0x1C00', 'subtype': 'Leather', 'category': 'Armor' }, 0x1C0A: { 'name': 'Leather Bustier', 'id': '0x1C0A', 'subtype': 'Leather', 'category': 'Armor' }, } ARMOR_PLATEMAIL_INFO = { 0x13EC: { 'name': 'Plate Mail Arms', 'id': '0x13EC', 'subtype': 'Platemail', 'category': 'Armor' }, 0x1415: { 'name': 'Plate Mail Chest', 'id': '0x1415', 'subtype': 'Platemail', 'category': 'Armor' }, 0x1411: { 'name': 'Plate Mail Legs', 'id': '0x1411', 'subtype': 'Platemail', 'category': 'Armor' }, 0x1414: { 'name': 'Plate Mail Gorget', 'id': '0x1414', 'subtype': 'Platemail', 'category': 'Armor' }, 0x1413: { 'name': 'Plate Mail Gloves', 'id': '0x1413', 'subtype': 'Platemail', 'category': 'Armor' }, 0x2779: { 'name': 'Platemail Mempo', 'id': '0x2779', 'subtype': 'Platemail', 'category': 'Armor' }, 0x140A: { 'name': 'Helmet', 'id': '0x140A', 'subtype': 'Platemail', 'category': 'Armor' }, 0x140C: { 'name': 'Bascinet', 'id': '0x140C', 'subtype': 'Platemail', 'category': 'Armor' }, 0x140E: { 'name': 'Norse Helm', 'id': '0x140E', 'subtype': 'Platemail', 'category': 'Armor' }, 0x1408: { 'name': 'Close Helm', 'id': '0x1408', 'subtype': 'Platemail', 'category': 'Armor' }, 0x1412: { 'name': 'Plate Helm', 'id': '0x1412', 'subtype': 'Platemail', 'category': 'Armor' }, 0x1C04: { 'name': 'Platemail Female', 'id': '0x1C04', 'subtype': 'Platemail', 'category': 'Armor' }, 0x1410: { 'name': 'Platemail Arms', 'id': '0x1410', 'subtype': 'Platemail', 'category': 'Armor' }, } ARMOR_CHAINMAIL_INFO = { 0x13BF: { 'name': 'Chainmail Tunic', 'id': '0x13BF', 'subtype': 'Chainmail', 'category': 'Armor' }, 0x13BE: { 'name': 'Chainmail Leggings', 'id': '0x13BE', 'subtype': 'Chainmail', 'category': 'Armor' }, 0x13BB: { 'name': 'Chainmail Coif', 'id': '0x13BB', 'subtype': 'Chainmail', 'category': 'Armor' }, 0x13C0: { 'name': 'Chainmail Tunic (Alt)', 'id': '0x13C0', 'subtype': 'Chainmail', 'category': 'Armor' }, 0x13C3: { 'name': 'Chainmail Sleeves', 'id': '0x13C3', 'subtype': 'Chainmail', 'category': 'Armor' }, 0x13C4: { 'name': 'Chainmail Gloves', 'id': '0x13C4', 'subtype': 'Chainmail', 'category': 'Armor' }, 0x13F0: { 'name': 'Chainmail Leggings (Alt)', 'id': '0x13F0', 'subtype': 'Chainmail', 'category': 'Armor' }, } ARMOR_BONE_INFO = { 0x1B72: { 'name': 'Bone Armor', 'id': '0x1B72', 'subtype': 'Bone', 'category': 'Armor' }, 0x1B7B: { 'name': 'Bone Legs', 'id': '0x1B7B', 'subtype': 'Bone', 'category': 'Armor' }, 0x1B73: { 'name': 'Bone Arms', 'id': '0x1B73', 'subtype': 'Bone', 'category': 'Armor' }, 0x144E: { 'name': 'Bone Arms', 'id': '0x144E', 'subtype': 'Bone', 'category': 'Armor' }, 0x1B7A: { 'name': 'Bone Gloves', 'id': '0x1B7A', 'subtype': 'Bone', 'category': 'Armor' }, 0x1B79: { 'name': 'Bone Helmet', 'id': '0x1B79', 'subtype': 'Bone', 'category': 'Armor' }, 0x1450: { 'name': 'Bone Gloves (Alternate)', 'id': '0x1450', 'subtype': 'Bone', 'category': 'Armor' }, 0x1452: { 'name': 'Bone Leggings', 'id': '0x1452', 'subtype': 'Bone', 'category': 'Armor' }, 0x144F: { 'name': 'Bone Armor Chest', 'id': '0x144F', 'subtype': 'Bone', 'category': 'Armor' }, 0x1451: { 'name': 'Bone Helmet', 'id': '0x1451', 'subtype': 'Bone', 'category': 'Armor' }, } ARMOR_RINGMAIL_INFO = { 0x13C0: { 'name': 'Ring Mail Tunic', 'id': '0x13C0', 'subtype': 'Ringmail', 'category': 'Armor' }, 0x13C3: { 'name': 'Ring Mail Sleeves', 'id': '0x13C3', 'subtype': 'Ringmail', 'category': 'Armor' }, 0x13C4: { 'name': 'Ring Mail Gloves', 'id': '0x13C4', 'subtype': 'Ringmail', 'category': 'Armor' }, 0x13EE: { 'name': 'Ringmail Sleeves', 'id': '0x13EE', 'subtype': 'Ringmail', 'category': 'Armor' }, 0x13F0: { 'name': 'Ringmail Leggings', 'id': '0x13F0', 'subtype': 'Ringmail', 'category': 'Armor' }, 0x13EB: { 'name': 'Ringmail Gloves', 'id': '0x13EB', 'subtype': 'Ringmail', 'category': 'Armor' }, } ARMOR_STUDDED_INFO = { 0x13C7: { 'name': 'Studded Leather Gorget', 'id': '0x13C7', 'subtype': 'Studded', 'category': 'Armor' }, 0x13DB: { 'name': 'Studded Leather Tunic', 'id': '0x13DB', 'subtype': 'Studded', 'category': 'Armor' }, 0x13D4: { 'name': 'Studded Leather Sleeves', 'id': '0x13D4', 'subtype': 'Studded', 'category': 'Armor' }, 0x13DA: { 'name': 'Studded Leather Gloves', 'id': '0x13DA', 'subtype': 'Studded', 'category': 'Armor' }, 0x13D6: { 'name': 'Studded Leather Leggings', 'id': '0x13D6', 'subtype': 'Studded', 'category': 'Armor' }, 0x279D: { 'name': 'Studded Mempo', 'id': '0x279D', 'subtype': 'Studded', 'category': 'Armor' }, 0x13D5: { 'name': 'Studded Gloves', 'id': '0x13D5', 'subtype': 'Studded', 'category': 'Armor' }, 0x13DC: { 'name': 'Studded Sleeves', 'id': '0x13DC', 'subtype': 'Studded', 'category': 'Armor' }, 0x1C0C: { 'name': 'Studded Bustier', 'id': '0x1C0C', 'subtype': 'Studded', 'category': 'Armor' }, 0x1C02: { 'name': 'Studded Armor (Female)', 'id': '0x1C02', 'subtype': 'Studded', 'category': 'Armor' }, } ARMOR_INFO = {**ARMOR_LEATHER_INFO, **ARMOR_PLATEMAIL_INFO, **ARMOR_BONE_INFO, **ARMOR_CHAINMAIL_INFO, **ARMOR_RINGMAIL_INFO, **ARMOR_STUDDED_INFO} # ITEM SLOT filters , ultima online armor slot ( no feet ) , useful to distinguish 1handed and 2handed weapons ITEM_SLOTS = ['head', 'neck', 'body', 'legs', 'arms', 'hand', 'weapon1h', 'weapon2h', 'shield'] # TODO: have equip_slot be apart of the item dicts properties # Timing Configuration MOVE_DELAY = 1000 # Delay moving items (in milliseconds) , depends on server and object delay , 600 works but we are being safe SCAN_DELAY = 100 # Delay scanning items (in milliseconds) , this is done to not freeze the rendering # Salvage Tool Configuration 0x1EBC SALVAGE_TOOLS = { "Tinker's Tools": {'id': 0x1EB8, 'color': -1, 'gump_id': 949095101, 'salvage_action': 63, 'priority': 1}, "Tinker's ToolsB": {'id': 0x1EBC, 'color': -1, 'gump_id': 949095101, 'salvage_action': 63, 'priority': 1}, "Sewing Kit": {'id': 0x0F9D, 'color': -1, 'gump_id': 949095101, 'salvage_action': 63, 'priority': 2} } #//======================================================================================== class JunkSalvager: def __init__(self): # Debug colors self.colors = { 'info': 68, # Blue 'warning': 33, # Red 'success': 63, # Green 'important': 53, # Yellow 'found': 43, # Purple 'config': 90 # Gray for config info } # Initialize stats self.stats = { 'tier1_items': 0, 'tier2_items': 0, 'tier3_items': 0, 'tier4_items': 0, 'other_items': 0, 'items_moved': 0, 'items_checked': 0, 'subtype_junked': 0, 'subtype_saved': 0 } # Show current configuration self.show_config() def show_config(self): """Display current configuration settings""" self.debug_message("=== Configuration ===", self.colors['config']) self.debug_message(f"Reserve Tier 1 items: {'Yes' if RESERVE_TIERS['TIER1'] else 'No'}", self.colors['config']) self.debug_message(f"Reserve Tier 2 items: {'Yes' if RESERVE_TIERS['TIER2'] else 'No'}", self.colors['config']) self.debug_message(f"Reserve Tier 3 items: {'Yes' if RESERVE_TIERS['TIER3'] else 'No'}", self.colors['config']) self.debug_message(f"Reserve Tier 4 items: {'Yes' if RESERVE_TIERS['TIER4'] else 'No'}", self.colors['config']) self.debug_message(f"Reserve magical items: {'Yes' if RESERVE_TIERS['MAGICAL'] else 'No'}", self.colors['config']) self.debug_message(f"Move delay: {str(MOVE_DELAY)}ms", self.colors['config']) self.debug_message(f"Auto-salvage: {'Yes' if AUTO_SALVAGE else 'No'}", self.colors['config']) self.debug_message(f"Junk backpack serial: 0x{JUNK_BACKPACK_SERIAL:X}", self.colors['config']) self.debug_message("==================", self.colors['config']) def debug_message(self, message, color='info'): """Send debug message to client (deprecated, use debug_message instead)""" if DEBUG_MODE: Misc.SendMessage(f"[JunkSalvager] {message}", self.colors[color] if isinstance(color, str) else color) def get_item_properties(self, item): """Get item properties using multiple methods""" properties = [] try: Items.WaitForProps(int(item.Serial), 1000) prop_list = Items.GetPropStringList(int(item.Serial)) if prop_list: properties.extend(prop_list) index = 0 while True: prop = Items.GetPropStringByIndex(int(item.Serial), index) if not prop: break if prop not in properties: properties.append(prop) index += 1 return properties except Exception as e: self.debug_message(f"Error getting properties: {str(e)}", 'warning') return properties def has_affix(self, properties, affixes): """Check if item has any of the specified affixes""" if not properties: return False for prop in properties: prop_lower = str(prop).lower() for affix in affixes: if affix.lower() in prop_lower: return True return False def has_any_affix(self, properties): """Check if item has any magical properties""" if not properties: return False magic_indicators = [ "damage increase", "defense chance increase", "faster casting", "faster cast recovery", "hit chance increase", "lower mana cost", "mage armor", "spell channeling", "strength bonus", "swing speed increase" ] for prop in properties: prop_lower = str(prop).lower() if any(x in prop_lower for x in ["+", "increase", "bonus", "lower"]): return True if any(x in prop_lower for x in magic_indicators): return True return False def is_salvageable_item(self, item): """Check if item is a weapon, armor, or shield that can be salvaged""" return item.ItemID in WEAPON_INFO or item.ItemID in ARMOR_INFO or item.ItemID in SHIELD_INFO def should_move_to_junk(self, item): """Determine if an item should be moved to junk backpack, using subtype AND tier/magical filtering""" # First check if it's a weapon, armor, or shield if not self.is_salvageable_item(item): return False # Get item info dict and subtype item_id = item.ItemID item_info = WEAPON_INFO.get(item_id) or ARMOR_INFO.get(item_id) or SHIELD_INFO.get(item_id) subtype = item_info['subtype'] if item_info and 'subtype' in item_info else None category = item_info['category'] if item_info and 'category' in item_info else None name = item_info['name'] if item_info and 'name' in item_info else str(item_id) # Map subtype to config flag save_flag = True if item_info: if item_info['category'] == 'Weapon': save_flag = { 'Axe': SAVE_ITEM_WEAPON_AXE, 'Sword': SAVE_ITEM_WEAPON_SWORD, 'Mace': SAVE_ITEM_WEAPON_MACE, 'Fencing': SAVE_ITEM_WEAPON_FENCING, 'Archery': SAVE_ITEM_WEAPON_ARCHERY, 'Unknown': SAVE_ITEM_WEAPON_UNKNOWN }.get(subtype, False) elif item_info['category'] == 'Armor': save_flag = { 'Leather': SAVE_ITEM_ARMOR_LEATHER, 'Platemail': SAVE_ITEM_ARMOR_PLATE, 'Chainmail': SAVE_ITEM_ARMOR_CHAINMAIL, 'Ringmail': SAVE_ITEM_ARMOR_RINGMAIL, 'Studded': SAVE_ITEM_ARMOR_STUDDED, 'Bone': SAVE_ITEM_ARMOR_BONE }.get(subtype, False) elif item_info['category'] == 'Shield': save_flag = SAVE_ITEM_ARMOR_SHIELD properties = self.get_item_properties(item) affix_str = ', '.join([str(p) for p in properties]) if properties else 'None' tier = 'None' if self.has_affix(properties, TIER1_AFFIXES): tier = 'T1' elif self.has_affix(properties, TIER2_AFFIXES): tier = 'T2' elif self.has_affix(properties, TIER3_AFFIXES): tier = 'T3' elif self.has_affix(properties, TIER4_AFFIXES): tier = 'T4' elif self.has_any_affix(properties): tier = 'Magical' # Debug: show full filtering decision self.debug_message(f"[Filter] {name} (ID: {hex(item_id)}) | Cat: {category} | Subtype: {subtype} | Tier: {tier} | Affixes: {affix_str} | SaveFlag: {save_flag}", self.colors['config']) # If the subtype is not configured to save, always junk if not save_flag: self.stats['subtype_junked'] += 1 self.debug_message(f"JUNKED by subtype filter: {name} (ID: {hex(item_id)})", self.colors['important']) return True # Passed subtype filter self.stats['subtype_saved'] += 1 # Check tier 1 if tier == 'T1': self.stats['tier1_items'] += 1 return not RESERVE_TIERS['TIER1'] # Check tier 2 if tier == 'T2': self.stats['tier2_items'] += 1 return not RESERVE_TIERS['TIER2'] # Check tier 3 if tier == 'T3': self.stats['tier3_items'] += 1 return not RESERVE_TIERS['TIER3'] # Check tier 4 if tier == 'T4': self.stats['tier4_items'] += 1 return not RESERVE_TIERS['TIER4'] # Check other magical items if tier == 'Magical': self.stats['other_items'] += 1 return not RESERVE_TIERS['MAGICAL'] # Non-magical weapons/armor go to junk return True def find_salvage_tool(self): """Find any available salvage tool in player's backpack""" for tool_name, tool_config in SALVAGE_TOOLS.items(): tool = Items.FindByID(tool_config['id'], tool_config['color'], Player.Backpack.Serial) if tool: return tool_name, tool, tool_config return None, None, None def salvage_junk_backpack(self): """Salvage items in the junk backpack""" if JUNK_BACKPACK_SERIAL == 0: self.debug_message("No junk backpack serial configured!", 'warning') return False tool_name, tool, tool_config = self.find_salvage_tool() if not tool: self.debug_message("No salvage tool found!", 'warning') return False self.debug_message(f"Using {tool_name} to salvage junk backpack", 'info') # Use the tool Items.UseItem(tool) Misc.Pause(500) # Select salvage option Gumps.WaitForGump(tool_config['gump_id'], 1000) Gumps.SendAction(tool_config['gump_id'], tool_config['salvage_action']) Misc.Pause(1000) # Target the junk backpack Target.WaitForTarget(1000) Target.TargetExecute(JUNK_BACKPACK_SERIAL) Misc.Pause(1000) # Use any resulting arcane dust dust = Items.FindByID(ARCANE_DUST_ITEMID, ARCANE_DUST_COLOR, Player.Backpack.Serial) if dust: self.debug_message(f"Using Arcane Dust (ID: 0x{ARCANE_DUST_ITEMID:X}, Color: 0x{ARCANE_DUST_COLOR:X})", 'info') Items.UseItem(dust) Target.WaitForTarget(1000) Target.TargetExecute(JUNK_BACKPACK_SERIAL) return True else: self.debug_message(f"No Arcane Dust (ID: 0x{ARCANE_DUST_ITEMID:X}, Color: 0x{ARCANE_DUST_COLOR:X}) found after salvaging.", 'warning') return False def process_inventory(self): """Process inventory and move unwanted items to junk backpack""" if JUNK_BACKPACK_SERIAL == 0: self.debug_message("No junk backpack serial configured!", 'warning') return False junk_backpack = Items.FindBySerial(JUNK_BACKPACK_SERIAL) if not junk_backpack: self.debug_message("Junk backpack not found!", 'warning') return False self.debug_message("Processing inventory for junk items...", 'info') items = Items.FindBySerial(Player.Backpack.Serial).Contains for item in items: self.stats['items_checked'] += 1 if self.should_move_to_junk(item): Items.Move(item, junk_backpack, 0) self.stats['items_moved'] += 1 Misc.Pause(MOVE_DELAY) self.show_stats() return True def show_stats(self): """Display item processing statistics""" self.debug_message("=== Processing Stats ===", 'important') self.debug_message(f"Items checked: {self.stats['items_checked']}", 'info') self.debug_message(f"Tier 1 items found: {self.stats['tier1_items']}", 'info') self.debug_message(f"Tier 2 items found: {self.stats['tier2_items']}", 'info') self.debug_message(f"Other magical items: {self.stats['other_items']}", 'info') self.debug_message(f"Items moved to junk: {self.stats['items_moved']}", 'info') self.debug_message(f"Subtype-junked: {self.stats['subtype_junked']} | Subtype-saved: {self.stats['subtype_saved']}", 'config') def find_junk_backpack(self): """Find the red junk backpack in player's backpack""" if not Player.Backpack: self.debug_message("No backpack found!", 33) return None # Try each hue in our acceptable range for hue in JUNK_BACKPACK_HUES: junk = Items.FindByID(JUNK_BACKPACK_ID, hue, Player.Backpack.Serial) if junk: self.debug_message(f"Found junk backpack (hue: 0x{hue:X}): 0x{junk.Serial:X}", 68) return junk.Serial self.debug_message(f"No junk backpack found with hues {[f'0x{h:X}' for h in JUNK_BACKPACK_HUES]}! Place a red backpack in your backpack.", 33) return None # find junk backpack on script load JUNK_BACKPACK_SERIAL = JunkSalvager().find_junk_backpack() or 0 def main(): """Main function to run the junk salvager""" try: salvager = JunkSalvager() if salvager.process_inventory(): if AUTO_SALVAGE: salvager.salvage_junk_backpack() except Exception as e: Misc.SendMessage(f"Error: {str(e)}", 33) if __name__ == "__main__": main()

Version History

Original Version Saved - 8/6/2025, 6:54:35 PM - about 23 hours ago

ITEM_filter_junk_salvager.py

No changes to display
View list of scripts
Disclaimer: This is a fan made site and is not directly associated with Ultima Online or UO staff.