Compare commits
5 commits
9f74e6020e
...
87782767f8
Author | SHA1 | Date | |
---|---|---|---|
87782767f8 | |||
421f2ce39f | |||
18c7a34b8f | |||
9243193bc8 | |||
d983a20989 |
7 changed files with 123 additions and 32 deletions
|
@ -121,11 +121,16 @@ class ItemsController < ApplicationController
|
|||
def load_appearances
|
||||
appearance_params = params[:with_appearances_for]
|
||||
return {} if appearance_params.blank?
|
||||
raise NotImplementedError if appearance_params[:alt_style_id].present?
|
||||
|
||||
pet_type = Item::Search::Query.load_pet_type_by_color_and_species(
|
||||
if appearance_params[:alt_style_id].present?
|
||||
target = Item::Search::Query.load_alt_style_by_id(
|
||||
appearance_params[:alt_style_id])
|
||||
else
|
||||
target = Item::Search::Query.load_pet_type_by_color_and_species(
|
||||
appearance_params[:color_id], appearance_params[:species_id])
|
||||
pet_type.appearances_for(@items.map(&:id), swf_asset_includes: [:zone])
|
||||
end
|
||||
|
||||
target.appearances_for(@items.map(&:id), swf_asset_includes: [:zone])
|
||||
end
|
||||
|
||||
def search_error(e)
|
||||
|
|
|
@ -6,11 +6,31 @@ class AltStyle < ApplicationRecord
|
|||
has_many :swf_assets, through: :parent_swf_asset_relationships
|
||||
has_many :contributions, as: :contributed, inverse_of: :contributed
|
||||
|
||||
scope :matching_name, ->(series_name, color_name, species_name) {
|
||||
color = Color.find_by_name!(color_name)
|
||||
species = Species.find_by_name!(species_name)
|
||||
where(series_name:, color_id: color.id, species_id: species.id)
|
||||
}
|
||||
|
||||
def name
|
||||
I18n.translate('pet_types.human_name', color_human_name: color.human_name,
|
||||
species_human_name: species.human_name)
|
||||
end
|
||||
|
||||
# If the series_name hasn't yet been set manually by support staff, show the
|
||||
# string "<New?>" instead. But it won't be searchable by that string—that is,
|
||||
# `fits:<New?>-faerie-draik` intentionally will not work, and the canonical
|
||||
# filter name will be `fits:alt-style-IDNUMBER`, instead.
|
||||
def series_name
|
||||
self[:series_name] || "<New?>"
|
||||
end
|
||||
|
||||
# You can use this to check whether `series_name` is returning the actual
|
||||
# value or its placeholder value.
|
||||
def has_real_series_name?
|
||||
self[:series_name].present?
|
||||
end
|
||||
|
||||
def adjective_name
|
||||
"#{series_name} #{color.human_name}"
|
||||
end
|
||||
|
@ -29,6 +49,11 @@ class AltStyle < ApplicationRecord
|
|||
swf_asset.image_url
|
||||
end
|
||||
|
||||
# Given a list of item IDs, return how they look on this alt style.
|
||||
def appearances_for(item_ids, ...)
|
||||
Item.appearances_for(item_ids, self, ...)
|
||||
end
|
||||
|
||||
def biology=(biology)
|
||||
# TODO: This is very similar to what `PetState` does, but like… much much
|
||||
# more compact? Idk if I'm missing something, or if I was just that much
|
||||
|
|
|
@ -495,6 +495,34 @@ class Item < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
# Given a list of item IDs, return how they look on the given target (either
|
||||
# a pet type or an alt style).
|
||||
def self.appearances_for(item_ids, target, swf_asset_includes: [])
|
||||
# First, load all the relationships for these items that also fit this
|
||||
# body.
|
||||
relationships = ParentSwfAssetRelationship.
|
||||
includes(swf_asset: swf_asset_includes).
|
||||
where(parent_type: "Item", parent_id: item_ids).
|
||||
where(swf_asset: {body_id: [target.body_id, 0]})
|
||||
|
||||
pet_type_body = Appearance::Body.new(target.body_id, target.species)
|
||||
all_pets_body = Appearance::Body.new(0, nil)
|
||||
|
||||
# Then, convert this into a hash from item ID to SWF assets.
|
||||
assets_by_item_id = relationships.group_by(&:parent_id).
|
||||
transform_values { |rels| rels.map(&:swf_asset) }
|
||||
|
||||
# Finally, for each item, return an appearance—even if it's empty!
|
||||
item_ids.to_h do |item_id|
|
||||
assets = assets_by_item_id.fetch(item_id, [])
|
||||
|
||||
fits_all_pets = assets.present? && assets.all? { |a| a.body_id == 0 }
|
||||
body = fits_all_pets ? all_pets_body : pet_type_body
|
||||
|
||||
[item_id, Appearance.new(body, assets)]
|
||||
end
|
||||
end
|
||||
|
||||
def self.all_by_ids_or_children(ids, swf_assets)
|
||||
swf_asset_ids = []
|
||||
swf_assets_by_id = {}
|
||||
|
|
|
@ -62,6 +62,8 @@ class Item
|
|||
when 'restricts'
|
||||
is_positive ? Filter.restricts(value) : Filter.not_restricts(value)
|
||||
when 'fits'
|
||||
# First, try the `fits:blue-acara` case.
|
||||
# NOTE: This will also work for `fits:"usuki girl-usul"`!
|
||||
match = value.match(/^([^-]+)-([^-]+)$/)
|
||||
if match.present?
|
||||
color_name, species_name = match.captures
|
||||
|
@ -70,6 +72,33 @@ class Item
|
|||
Filter.fits_pet_type(pet_type, color_name:, species_name:) :
|
||||
Filter.not_fits_pet_type(pet_type, color_name:, species_name:)
|
||||
end
|
||||
|
||||
# Next, try the `fits:alt-style-87305` case.
|
||||
match = value.match(/^alt-style-([0-9]+)$/)
|
||||
if match.present?
|
||||
alt_style_id, = match.captures
|
||||
alt_style = load_alt_style_by_id(alt_style_id)
|
||||
return is_positive ?
|
||||
Filter.fits_alt_style(alt_style) :
|
||||
Filter.not_fits_alt_style(alt_style)
|
||||
end
|
||||
|
||||
# Next, try the `fits:nostalgic-faerie-draik` case.
|
||||
# NOTE: This will also work for `fits:"nostalgic-usuki girl-usul"`!
|
||||
match = value.match(/^([^-]+)-([^-]+)-([^-]+)$/)
|
||||
if match.present?
|
||||
series_name, color_name, species_name = match.captures
|
||||
alt_style = load_alt_style_by_name(
|
||||
series_name, color_name, species_name)
|
||||
return is_positive ?
|
||||
Filter.fits_alt_style(alt_style) :
|
||||
Filter.not_fits_alt_style(alt_style)
|
||||
end
|
||||
|
||||
# TODO: We could make `fits:acara` an alias for `species:acara`, or
|
||||
# even the primary syntax?
|
||||
|
||||
# If none of these cases work, raise an error.
|
||||
raise_search_error "not_found.fits_target", value: value
|
||||
when 'species'
|
||||
begin
|
||||
|
@ -176,10 +205,19 @@ class Item
|
|||
end
|
||||
end
|
||||
|
||||
def self.load_alt_style_by_name(series_name, color_name, species_name)
|
||||
begin
|
||||
AltStyle.matching_name(series_name, color_name, species_name).first!
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
raise_search_error "not_found.alt_style",
|
||||
filter_text: "#{series_name}-#{color_name}-#{species_name}"
|
||||
end
|
||||
end
|
||||
|
||||
def self.load_alt_style_by_id(alt_style_id)
|
||||
begin
|
||||
AltStyle.find(alt_style_id)
|
||||
rescue
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
raise_search_error "not_found.alt_style",
|
||||
filter_text: "alt-style-#{alt_style_id}"
|
||||
end
|
||||
|
@ -326,8 +364,18 @@ class Item
|
|||
end
|
||||
|
||||
def self.alt_style_to_filter_text(alt_style)
|
||||
# If the real series name has been set in the database by support
|
||||
# staff, use that for the canonical filter text for this alt style.
|
||||
# Otherwise, represent this alt style by ID.
|
||||
if alt_style.has_real_series_name?
|
||||
series_name = alt_style.series_name.downcase
|
||||
color_name = alt_style.color.name.downcase
|
||||
species_name = alt_style.species.name.downcase
|
||||
"#{series_name}-#{color_name}-#{species_name}"
|
||||
else
|
||||
"alt-style-#{alt_style.id}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -169,30 +169,9 @@ class PetType < ApplicationRecord
|
|||
}.first
|
||||
end
|
||||
|
||||
def appearances_for(item_ids, swf_asset_includes: [])
|
||||
# First, load all the relationships for these items that also fit this
|
||||
# body.
|
||||
relationships = ParentSwfAssetRelationship.
|
||||
includes(swf_asset: swf_asset_includes).
|
||||
where(parent_type: "Item", parent_id: item_ids).
|
||||
where(swf_asset: {body_id: [body_id, 0]})
|
||||
|
||||
pet_type_body = Item::Appearance::Body.new(body_id, species)
|
||||
all_pets_body = Item::Appearance::Body.new(0, nil)
|
||||
|
||||
# Then, convert this into a hash from item ID to SWF assets.
|
||||
assets_by_item_id = relationships.group_by(&:parent_id).
|
||||
transform_values { |rels| rels.map(&:swf_asset) }
|
||||
|
||||
# Finally, for each item, return an appearance—even if it's empty!
|
||||
item_ids.to_h do |item_id|
|
||||
assets = assets_by_item_id.fetch(item_id, [])
|
||||
|
||||
fits_all_pets = assets.present? && assets.all? { |a| a.body_id == 0 }
|
||||
body = fits_all_pets ? all_pets_body : pet_type_body
|
||||
|
||||
[item_id, Item::Appearance.new(body, assets)]
|
||||
end
|
||||
# Given a list of item IDs, return how they look on this pet type.
|
||||
def appearances_for(item_ids, ...)
|
||||
Item.appearances_for(item_ids, self, ...)
|
||||
end
|
||||
|
||||
def self.all_by_ids_or_children(ids, pet_states)
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
class ChangeDefaultForAltStylesSeriesName < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
change_column_null :alt_styles, :series_name, true
|
||||
change_column_default :alt_styles, :series_name, from: "<New?>", to: nil
|
||||
end
|
||||
end
|
|
@ -10,14 +10,14 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.1].define(version: 2024_02_27_231815) do
|
||||
ActiveRecord::Schema[7.1].define(version: 2024_02_27_233743) do
|
||||
create_table "alt_styles", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
|
||||
t.integer "species_id", null: false
|
||||
t.integer "color_id", null: false
|
||||
t.integer "body_id", null: false
|
||||
t.datetime "created_at", precision: nil, null: false
|
||||
t.datetime "updated_at", precision: nil, null: false
|
||||
t.string "series_name", default: "<New?>", null: false
|
||||
t.string "series_name"
|
||||
t.index ["color_id"], name: "index_alt_styles_on_color_id"
|
||||
t.index ["species_id"], name: "index_alt_styles_on_species_id"
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue