[WV2] Group items by zone
This commit is contained in:
parent
dad185150c
commit
e8d768961b
2 changed files with 139 additions and 10 deletions
|
|
@ -75,8 +75,133 @@ module OutfitsHelper
|
||||||
locals: parse_outfit_viewer_options(...)
|
locals: parse_outfit_viewer_options(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Group outfit items by zone, applying smart multi-zone simplification.
|
||||||
|
# Returns an array of hashes: {zone:, items:}
|
||||||
|
# This matches the logic from wardrobe-2020's getZonesAndItems function.
|
||||||
|
def outfit_items_by_zone(outfit)
|
||||||
|
return [] if outfit.pet_type.nil?
|
||||||
|
|
||||||
|
# Get item appearances for this outfit
|
||||||
|
item_appearances = Item.appearances_for(
|
||||||
|
outfit.worn_items,
|
||||||
|
outfit.pet_type,
|
||||||
|
swf_asset_includes: [:zone]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Separate incompatible items (no layers for this pet)
|
||||||
|
compatible_items = []
|
||||||
|
incompatible_items = []
|
||||||
|
|
||||||
|
outfit.worn_items.each do |item|
|
||||||
|
appearance = item_appearances[item.id]
|
||||||
|
if appearance&.present?
|
||||||
|
compatible_items << {item: item, appearance: appearance}
|
||||||
|
else
|
||||||
|
incompatible_items << item
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Group items by zone - multi-zone items appear in each zone
|
||||||
|
items_by_zone = Hash.new { |h, k| h[k] = [] }
|
||||||
|
zones_by_id = {}
|
||||||
|
|
||||||
|
compatible_items.each do |item_with_appearance|
|
||||||
|
item = item_with_appearance[:item]
|
||||||
|
appearance = item_with_appearance[:appearance]
|
||||||
|
|
||||||
|
# Get unique zones for this item (an item may have multiple assets per zone)
|
||||||
|
appearance.swf_assets.map(&:zone).uniq.each do |zone|
|
||||||
|
zones_by_id[zone.id] = zone
|
||||||
|
items_by_zone[zone.id] << item
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create zone groups with sorted items
|
||||||
|
zones_and_items = items_by_zone.map do |zone_id, items|
|
||||||
|
{
|
||||||
|
zone_id: zone_id,
|
||||||
|
zone_label: zones_by_id[zone_id].label,
|
||||||
|
items: items.sort_by { |item| item.name.downcase }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sort zone groups alphabetically by label, then by ID for tiebreaking
|
||||||
|
zones_and_items.sort_by! do |group|
|
||||||
|
[group[:zone_label].downcase, group[:zone_id]]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Apply multi-zone simplification: remove redundant single-item groups
|
||||||
|
zones_and_items = simplify_multi_zone_groups(zones_and_items)
|
||||||
|
|
||||||
|
# Add zone ID disambiguation for duplicate labels
|
||||||
|
zones_and_items = disambiguate_zone_labels(zones_and_items)
|
||||||
|
|
||||||
|
# Add incompatible items section if any
|
||||||
|
if incompatible_items.any?
|
||||||
|
zones_and_items << {
|
||||||
|
zone_id: nil,
|
||||||
|
zone_label: "Incompatible",
|
||||||
|
items: incompatible_items.sort_by { |item| item.name.downcase }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
zones_and_items
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
# Simplify zone groups by removing redundant single-item groups.
|
||||||
|
# Keep groups with multiple items (conflicts). For single-item groups,
|
||||||
|
# only keep them if the item doesn't appear in a multi-item group.
|
||||||
|
def simplify_multi_zone_groups(zones_and_items)
|
||||||
|
# Find groups with conflicts (multiple items)
|
||||||
|
groups_with_conflicts = zones_and_items.select { |g| g[:items].length > 1 }
|
||||||
|
|
||||||
|
# Track which items appear in conflict groups
|
||||||
|
items_with_conflicts = Set.new(
|
||||||
|
groups_with_conflicts.flat_map { |g| g[:items].map(&:id) }
|
||||||
|
)
|
||||||
|
|
||||||
|
# Track which items we've already shown
|
||||||
|
items_we_have_seen = Set.new
|
||||||
|
|
||||||
|
# Filter groups
|
||||||
|
zones_and_items.select do |group|
|
||||||
|
# Always keep groups with multiple items
|
||||||
|
if group[:items].length > 1
|
||||||
|
group[:items].each { |item| items_we_have_seen.add(item.id) }
|
||||||
|
true
|
||||||
|
else
|
||||||
|
# For single-item groups, only keep if:
|
||||||
|
# - Item hasn't been seen yet AND
|
||||||
|
# - Item won't appear in a conflict group
|
||||||
|
item = group[:items].first
|
||||||
|
item_id = item.id
|
||||||
|
|
||||||
|
if items_we_have_seen.include?(item_id) || items_with_conflicts.include?(item_id)
|
||||||
|
false
|
||||||
|
else
|
||||||
|
items_we_have_seen.add(item_id)
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add zone IDs to labels when there are duplicates
|
||||||
|
def disambiguate_zone_labels(zones_and_items)
|
||||||
|
label_counts = zones_and_items.group_by { |g| g[:zone_label] }
|
||||||
|
.transform_values(&:count)
|
||||||
|
|
||||||
|
zones_and_items.each do |group|
|
||||||
|
if label_counts[group[:zone_label]] > 1
|
||||||
|
group[:zone_label] = "#{group[:zone_label]} (##{group[:zone_id]})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
zones_and_items
|
||||||
|
end
|
||||||
|
|
||||||
def parse_outfit_viewer_options(
|
def parse_outfit_viewer_options(
|
||||||
outfit=nil, pet_state: nil, preferred_image_format: :png, **html_options
|
outfit=nil, pet_state: nil, preferred_image_format: :png, **html_options
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -51,13 +51,17 @@
|
||||||
- if @outfit.worn_items.any?
|
- if @outfit.worn_items.any?
|
||||||
.worn-items
|
.worn-items
|
||||||
%h2 Items (#{@outfit.worn_items.size})
|
%h2 Items (#{@outfit.worn_items.size})
|
||||||
%ul.items-list
|
|
||||||
- @outfit.worn_items.each do |item|
|
- outfit_items_by_zone(@outfit).each do |zone_group|
|
||||||
%li.item-card
|
.zone-group
|
||||||
.item-thumbnail
|
%h3.zone-label= zone_group[:zone_label]
|
||||||
= image_tag item.thumbnail_url, alt: item.name, loading: "lazy"
|
%ul.items-list
|
||||||
.item-info
|
- zone_group[:items].each do |item|
|
||||||
.item-name= item.name
|
%li.item-card
|
||||||
.item-badges
|
.item-thumbnail
|
||||||
= render "items/badges/kind", item: item
|
= image_tag item.thumbnail_url, alt: item.name, loading: "lazy"
|
||||||
= render "items/badges/first_seen", item: item
|
.item-info
|
||||||
|
.item-name= item.name
|
||||||
|
.item-badges
|
||||||
|
= render "items/badges/kind", item: item
|
||||||
|
= render "items/badges/first_seen", item: item
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue