impress/app/controllers/wardrobe_controller.rb

188 lines
6.6 KiB
Ruby
Raw Normal View History

2025-11-11 17:21:03 -08:00
class WardrobeController < ApplicationController
def show
2026-02-05 20:47:05 -08:00
# Load saved outfit if an ID is provided (e.g. /outfits/:id/v2)
@saved_outfit = Outfit.find(params[:id]) if params[:id].present?
# If visiting a saved outfit with no state params, redirect with the
# outfit's state as query params. This keeps URL-as-source-of-truth simple:
# the rest of the action always reads from params.
if @saved_outfit && !outfit_state_params_present?
redirect_to wardrobe_v2_outfit_path(@saved_outfit, **@saved_outfit.wardrobe_params)
return
end
# Set the form target path for all wardrobe forms
@wardrobe_path = @saved_outfit ? wardrobe_v2_outfit_path(@saved_outfit) : wardrobe_v2_path
2025-11-11 17:21:03 -08:00
# Get selected species and color from params, or default to Blue Acara
@selected_species = params[:species] ? Species.find_by_id(params[:species]) : Species.find_by_name("Acara")
@selected_color = params[:color] ? Color.find_by_id(params[:color]) : Color.find_by_name("Blue")
# Load valid colors for the selected species (colors that have existing pet types)
@species = Species.alphabetical
@colors = @selected_species.compatible_colors
# Find the best pet type for this species+color combo
# If the exact combo doesn't exist, this will fall back to a simple color
@pet_type = PetType.for_species_and_color(
species_id: @selected_species.id,
color_id: @selected_color.id
)
# Use the pet type's actual color as the selected color
# (might differ from requested color if we fell back to a simple color)
@selected_color = @pet_type&.color
2025-11-11 17:41:57 -08:00
# Get the selected pose from params, or default to nil (will use canonical)
@selected_pose = params[:pose]
# Find the pet state for the selected pose, or use canonical
@pet_state = if @pet_type && @selected_pose.present?
@pet_type.pet_states.with_pose(@selected_pose).first || @pet_type.canonical_pet_state
else
@pet_type&.canonical_pet_state
end
# If we found a pet_state, use its actual pose as the selected pose
@selected_pose = @pet_state&.pose
# Load all available poses for this pet type (for the pose picker)
@available_poses = @pet_type ? available_poses_for(@pet_type) : {}
# Preload the layers for all available poses so the thumbnails render efficiently
if @pet_type
pose_pet_states = @available_poses.values.compact
SwfAsset.preload_manifests(pose_pet_states.flat_map(&:swf_assets))
end
2026-02-05 18:04:49 -08:00
# Load alt style from params, scoped to the current species
@alt_style = if params[:style].present? && @selected_species
AltStyle.where(species_id: @selected_species.id).find_by(id: params[:style])
end
# Load all available alt styles for this species (for the style picker)
@available_alt_styles = @selected_species ?
AltStyle.where(species_id: @selected_species.id).by_name_grouped : []
# Load items from the objects[] and closet[] parameters
worn_item_ids = params[:objects] || []
closeted_item_ids = params[:closet] || []
worn_items = Item.where(id: worn_item_ids)
closeted_items = Item.where(id: closeted_item_ids)
2025-11-11 17:21:03 -08:00
# Build the outfit
@outfit = Outfit.new(
name: @saved_outfit ? @saved_outfit.name : (params[:name].presence || "Untitled outfit"),
2025-11-11 17:41:57 -08:00
pet_state: @pet_state,
2026-02-05 18:04:49 -08:00
alt_style: @alt_style,
worn_items: worn_items,
closeted_items: closeted_items,
2025-11-11 17:21:03 -08:00
)
# Preload the manifests for all visible layers, so they load efficiently
# in parallel rather than sequentially when rendering
SwfAsset.preload_manifests(@outfit.visible_layers)
2026-02-05 18:04:49 -08:00
# Also preload alt style layer manifests for the style picker thumbnails
SwfAsset.preload_manifests(@alt_style.swf_assets.to_a) if @alt_style
2026-02-05 20:47:05 -08:00
# Compute saved outfit state for the view
if @saved_outfit
@has_unsaved_changes = !@outfit.same_wardrobe_state_as?(@saved_outfit)
@is_owner = user_signed_in? && current_user.id == @saved_outfit.user_id
else
@has_unsaved_changes = false
end
2025-11-11 17:21:03 -08:00
# Handle search mode
@search_mode = params[:q].present?
if @search_mode
search_filters = build_search_filters(params[:q], @outfit)
query_params = ActionController::Parameters.new(
search_filters.each_with_index.map { |filter, i| [i.to_s, filter] }.to_h
)
@query = Item::Search::Query.from_params(query_params, current_user)
@search_results = @query.results.paginate(page: params.dig(:q, :page), per_page: 30)
end
render layout: false
end
private
2025-11-11 17:41:57 -08:00
# Returns a hash of pose => pet_state for all the main poses,
# indicating which poses are available for this pet type.
# Uses the same logic as the Rainbow Pool to pick the "canonical" pet state
# for each pose when multiple states exist.
def available_poses_for(pet_type)
poses_hash = {}
# Group all pet states by pose, then pick the best one for each pose
# using emotion_order (same logic as Rainbow Pool)
pet_type.pet_states.emotion_order.group_by(&:pose).each do |pose, states|
# Only include the main poses (skip UNKNOWN, UNCONVERTED, etc.)
if PetState::MAIN_POSES.include?(pose)
poses_hash[pose] = states.first
end
end
# Ensure all main poses are in the hash, even if nil
PetState::MAIN_POSES.each do |pose|
poses_hash[pose] ||= nil
end
poses_hash
end
2026-02-05 20:47:05 -08:00
def outfit_state_params_present?
params[:species].present? || params[:color].present? || params[:objects].present? || params[:closet].present?
2026-02-05 20:47:05 -08:00
end
2025-11-11 17:21:03 -08:00
def build_search_filters(query_params, outfit)
filters = []
# Add name filter if present
if query_params[:name].present?
filters << { key: "name", value: query_params[:name] }
end
# Add item kind filter if present
if query_params[:item_kind].present?
case query_params[:item_kind]
when "nc"
filters << { key: "is_nc", value: "true" }
when "np"
filters << { key: "is_np", value: "true" }
when "pb"
filters << { key: "is_pb", value: "true" }
end
end
# Add zone filter if present
if query_params[:zone].present?
filters << { key: "occupied_zone_set_name", value: query_params[:zone] }
end
# Always add auto-filter for items that fit the current pet
pet_type = outfit.pet_type
if pet_type
fit_filter = {
key: "fits",
value: {
species_id: pet_type.species_id.to_s,
color_id: pet_type.color_id.to_s
}
}
# Include alt_style_id if present
if outfit.alt_style_id.present?
fit_filter[:value][:alt_style_id] = outfit.alt_style_id.to_s
end
filters << fit_filter
end
filters
end
end