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
|
def load_appearances
|
||||||
appearance_params = params[:with_appearances_for]
|
appearance_params = params[:with_appearances_for]
|
||||||
return {} if appearance_params.blank?
|
return {} if appearance_params.blank?
|
||||||
raise NotImplementedError if appearance_params[:alt_style_id].present?
|
|
||||||
|
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])
|
||||||
|
end
|
||||||
|
|
||||||
pet_type = Item::Search::Query.load_pet_type_by_color_and_species(
|
target.appearances_for(@items.map(&:id), swf_asset_includes: [:zone])
|
||||||
appearance_params[:color_id], appearance_params[:species_id])
|
|
||||||
pet_type.appearances_for(@items.map(&:id), swf_asset_includes: [:zone])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def search_error(e)
|
def search_error(e)
|
||||||
|
|
|
@ -6,11 +6,31 @@ class AltStyle < ApplicationRecord
|
||||||
has_many :swf_assets, through: :parent_swf_asset_relationships
|
has_many :swf_assets, through: :parent_swf_asset_relationships
|
||||||
has_many :contributions, as: :contributed, inverse_of: :contributed
|
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
|
def name
|
||||||
I18n.translate('pet_types.human_name', color_human_name: color.human_name,
|
I18n.translate('pet_types.human_name', color_human_name: color.human_name,
|
||||||
species_human_name: species.human_name)
|
species_human_name: species.human_name)
|
||||||
end
|
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
|
def adjective_name
|
||||||
"#{series_name} #{color.human_name}"
|
"#{series_name} #{color.human_name}"
|
||||||
end
|
end
|
||||||
|
@ -29,6 +49,11 @@ class AltStyle < ApplicationRecord
|
||||||
swf_asset.image_url
|
swf_asset.image_url
|
||||||
end
|
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)
|
def biology=(biology)
|
||||||
# TODO: This is very similar to what `PetState` does, but like… much much
|
# 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
|
# more compact? Idk if I'm missing something, or if I was just that much
|
||||||
|
|
|
@ -495,6 +495,34 @@ class Item < ApplicationRecord
|
||||||
end
|
end
|
||||||
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)
|
def self.all_by_ids_or_children(ids, swf_assets)
|
||||||
swf_asset_ids = []
|
swf_asset_ids = []
|
||||||
swf_assets_by_id = {}
|
swf_assets_by_id = {}
|
||||||
|
|
|
@ -62,6 +62,8 @@ class Item
|
||||||
when 'restricts'
|
when 'restricts'
|
||||||
is_positive ? Filter.restricts(value) : Filter.not_restricts(value)
|
is_positive ? Filter.restricts(value) : Filter.not_restricts(value)
|
||||||
when 'fits'
|
when 'fits'
|
||||||
|
# First, try the `fits:blue-acara` case.
|
||||||
|
# NOTE: This will also work for `fits:"usuki girl-usul"`!
|
||||||
match = value.match(/^([^-]+)-([^-]+)$/)
|
match = value.match(/^([^-]+)-([^-]+)$/)
|
||||||
if match.present?
|
if match.present?
|
||||||
color_name, species_name = match.captures
|
color_name, species_name = match.captures
|
||||||
|
@ -70,6 +72,33 @@ class Item
|
||||||
Filter.fits_pet_type(pet_type, color_name:, species_name:) :
|
Filter.fits_pet_type(pet_type, color_name:, species_name:) :
|
||||||
Filter.not_fits_pet_type(pet_type, color_name:, species_name:)
|
Filter.not_fits_pet_type(pet_type, color_name:, species_name:)
|
||||||
end
|
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
|
raise_search_error "not_found.fits_target", value: value
|
||||||
when 'species'
|
when 'species'
|
||||||
begin
|
begin
|
||||||
|
@ -176,10 +205,19 @@ class Item
|
||||||
end
|
end
|
||||||
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)
|
def self.load_alt_style_by_id(alt_style_id)
|
||||||
begin
|
begin
|
||||||
AltStyle.find(alt_style_id)
|
AltStyle.find(alt_style_id)
|
||||||
rescue
|
rescue ActiveRecord::RecordNotFound
|
||||||
raise_search_error "not_found.alt_style",
|
raise_search_error "not_found.alt_style",
|
||||||
filter_text: "alt-style-#{alt_style_id}"
|
filter_text: "alt-style-#{alt_style_id}"
|
||||||
end
|
end
|
||||||
|
@ -326,7 +364,17 @@ class Item
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.alt_style_to_filter_text(alt_style)
|
def self.alt_style_to_filter_text(alt_style)
|
||||||
"alt-style-#{alt_style.id}"
|
# 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
|
||||||
end
|
end
|
||||||
|
|
|
@ -169,30 +169,9 @@ class PetType < ApplicationRecord
|
||||||
}.first
|
}.first
|
||||||
end
|
end
|
||||||
|
|
||||||
def appearances_for(item_ids, swf_asset_includes: [])
|
# Given a list of item IDs, return how they look on this pet type.
|
||||||
# First, load all the relationships for these items that also fit this
|
def appearances_for(item_ids, ...)
|
||||||
# body.
|
Item.appearances_for(item_ids, self, ...)
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.all_by_ids_or_children(ids, pet_states)
|
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.
|
# 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|
|
create_table "alt_styles", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
|
||||||
t.integer "species_id", null: false
|
t.integer "species_id", null: false
|
||||||
t.integer "color_id", null: false
|
t.integer "color_id", null: false
|
||||||
t.integer "body_id", null: false
|
t.integer "body_id", null: false
|
||||||
t.datetime "created_at", precision: nil, null: false
|
t.datetime "created_at", precision: nil, null: false
|
||||||
t.datetime "updated_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 ["color_id"], name: "index_alt_styles_on_color_id"
|
||||||
t.index ["species_id"], name: "index_alt_styles_on_species_id"
|
t.index ["species_id"], name: "index_alt_styles_on_species_id"
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue