diff --git a/scripts/eloot.lic b/scripts/eloot.lic
index ebb516503..951cc1c1f 100644
--- a/scripts/eloot.lic
+++ b/scripts/eloot.lic
@@ -14,10 +14,12 @@
wiki: https://gswiki.play.net/Lich:Script_Eloot
game: Gemstone
tags: loot
- required: Lich >= 5.12.9
- version: 2.8.2
+ required: Lich >= 5.15.0
+ version: 2.9.0
Improvements:
Major_change.feature_addition.bugfix
+ v2.9.0 (2026-05-05)
+ - add new feature to keep transmogs
v2.8.2 (2026-05-01)
- bugfix: validate_hoarding_settings no longer errors when container_settings
is empty but use_overflow is enabled with resolved overflow containers
@@ -257,7 +259,8 @@ module ELoot # Data
:only_list, :only, :inventory, :gem_inventory, :alchemy_inventory, :hoard_type, :cache, :locker_city, :use_hoarding, :stash,
:items_to_hoard, :hoard_deposit, :inv_save, :deposit_regex, :withdraw_regex, :disk_nouns_regex, :sigil_determination_on_fail, :start_room, :last_called,
:urchin_msg, :gemshop_first, :reject_loot_names, :reject_loot_nouns, :version, :debug_logger, :details_check, :coin_bag_full,
- :use_house_locker, :che_rooms, :che_entry, :che_exit, :towns, :gambling_kit, :gambling_kit_full, :ready_lines, :weapon_inv, :original_readylist, :blood_band
+ :use_house_locker, :che_rooms, :che_entry, :che_exit, :towns, :gambling_kit, :gambling_kit_full, :ready_lines, :weapon_inv, :original_readylist, :blood_band,
+ :transmog_cache
def initialize(settings)
@settings = settings
@@ -338,6 +341,9 @@ module ELoot # Data
$sell_ignore = []
@gemshop_first = false
+ # session-scoped cache of ANALYZE results for transmog detection: { [id, name] => true/false }
+ @transmog_cache = {}
+
default_crumbly = [
# Kraken Fall
"gnarled dark wooden crook",
@@ -768,6 +774,7 @@ module ELoot # UI Setup
sell_aspect: { default: false },
sell_keep_silver: { default: 0 },
sell_deposit_coinhand: { default: false },
+ keep_transmogs: { default: false },
trash_dump_types: { default: ["herb", "junk", "food"] },
},
skin: {
@@ -1087,7 +1094,7 @@ module ELoot # UI Setup
21
20
312
- 002FalseTrue3
+ 12002FalseTrue3
FalseTrue43TrueFalseSelling3FalseTrueTrueinTrueFalseTrueFalse10150150TrueFalse50TrueFalsecenter5525Add80TrueTrueTruestart5
00Delete80TrueTrueTrueend520
TrueTrueTrueEnter exclusion (e.g. uncut diamond)10200TrueTrue5555TrueinTrueFalseTrueTruesell_exclude_storeFalse0True
@@ -1878,6 +1885,7 @@ module ELoot # Profile loading/saving and settings
:sell_shroud => false,
:sell_aspect => false,
:sell_keep_silver => 0,
+ :keep_transmogs => false,
:trash_dump_types => ["herb", "junk", "food"],
:skin_enable => false,
:skin_kneel => false,
@@ -3337,6 +3345,30 @@ module ELoot # Inventory methods
return false
end
+ # Determines if an item is a transmog (allows transferring its appearance onto another worn item).
+ # Only jewelry/clothing items with an after_name are candidates; for those we look at ANALYZE
+ # output for the transmog descriptor line. Callers that already have ANALYZE output for the
+ # item should pass it via `analyze_lines:` to avoid issuing a redundant ANALYZE command.
+ # Results are cached by a composite key of [item.id, item.name] for the
+ # lifetime of the script process. The composite key defends against game-id reuse over long
+ # sessions: if the item at a given id changes name, the prior result is ignored.
+ # @param item [GameObj] item to check
+ # @param analyze_lines [Array, nil] pre-fetched ANALYZE output, or nil to fetch
+ # @return [Boolean] true if ANALYZE output confirms this is a transmog
+ def self.is_transmog?(item, analyze_lines: nil)
+ return false unless item.type =~ /jewelry|clothing/
+ return false if item.after_name.nil? || item.after_name.empty?
+
+ cache = ELoot.data.transmog_cache
+ key = [item.id, item.name]
+ return cache[key] if cache.key?(key)
+
+ lines = analyze_lines || ELoot.get_command("analyze ##{item.id}", /You analyze/, silent: true, quiet: true)
+ result = lines.any?(/This item allows you to transfer its appearance onto another compatible worn item/)
+ cache[key] = result
+ result
+ end
+
def self.check_auto_closer
return if ELoot.data.settings[:auto_close].empty?
@@ -5801,6 +5833,11 @@ module ELoot # Sells the loot
items_to_sell.each do |item|
Inventory.drag(item)
+ if ELoot.data.settings[:keep_transmogs] && Inventory.is_transmog?(item)
+ Inventory.single_drag(item)
+ next
+ end
+
if item.type =~ /#{ELoot.data.settings[:sell_appraise_types].join('|')}/ && !ELoot.data.settings[:sell_appraise_types].empty?
Sell.appraise(item, place.capitalize, ELoot.data.silver_breakdown)
else
@@ -5936,6 +5973,7 @@ module ELoot # Sells the loot
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('|')}/
+ next false if ELoot.data.settings[:keep_transmogs] && Inventory.is_transmog?(item)
true
end
@@ -6131,6 +6169,11 @@ module ELoot # Sells the loot
Inventory.drag(item)
+ if ELoot.data.settings[:keep_transmogs] && Inventory.is_transmog?(item)
+ Inventory.single_drag(item)
+ next
+ end
+
if item.type =~ /#{ELoot.data.settings[:sell_appraise_types].join('|')}/ && !ELoot.data.settings[:sell_appraise_types].empty?
Sell.appraise(item, "Gemshop", ELoot.data.silver_breakdown)
else
@@ -6652,6 +6695,12 @@ module ELoot # Sells the loot
Inventory.drag(thing)
lines = ELoot.get_command("analyze ##{thing.id}", /You analyze/, silent: true, quiet: true)
+
+ if ELoot.data.settings[:keep_transmogs] && Inventory.is_transmog?(thing, analyze_lines: lines)
+ Inventory.single_drag(thing)
+ next
+ end
+
if lines.any?(/ALTER 41/)
ELoot.msg(type: "info", text: "** This analyzes as Alter 41. Keeping it **", space: true)
Inventory.single_drag(thing)