impress/app/models/pet_state.rb
2023-11-10 18:04:24 -08:00

253 lines
6.9 KiB
Ruby

class PetState < ApplicationRecord
SwfAssetType = 'biology'
has_many :contributions, :as => :contributed,
:inverse_of => :contributed # in case of duplicates being merged
has_many :outfits
has_many :parent_swf_asset_relationships, :as => :parent,
:autosave => false
has_many :swf_assets, :through => :parent_swf_asset_relationships
belongs_to :pet_type
delegate :color, to: :pet_type
alias_method :swf_asset_ids_from_association, :swf_asset_ids
attr_writer :parent_swf_asset_relationships_to_update
# A simple ordering that tries to bring reliable pet states to the front.
scope :emotion_order, -> {
order(Arel.sql(
"(mood_id IS NULL) ASC, mood_id ASC, female DESC, unconverted DESC, " +
"glitched ASC, id DESC"
))
}
# Filter pet states using the "pose" concept we use in the editor.
scope :with_pose, ->(pose) {
case pose
when "UNCONVERTED"
where(unconverted: true)
when "HAPPY_MASC"
where(mood_id: 1, female: false)
when "HAPPY_FEM"
where(mood_id: 1, female: true)
when "SAD_MASC"
where(mood_id: 2, female: false)
when "SAD_FEM"
where(mood_id: 2, female: true)
when "SICK_MASC"
where(mood_id: 4, female: false)
when "SICK_FEM"
where(mood_id: 4, female: true)
when "UNKNOWN"
where(mood_id: nil).or(where(female: nil))
else
raise ArgumentError, "unexpected pose value #{pose}"
end
}
def pose
if unconverted?
"UNCONVERTED"
elsif mood_id.nil? || female.nil?
"UNKNOWN"
elsif mood_id == 1 && !female?
"HAPPY_MASC"
elsif mood_id == 1 && female?
"HAPPY_FEM"
elsif mood_id == 2 && !female?
"SAD_MASC"
elsif mood_id == 2 && female?
"SAD_FEM"
elsif mood_id == 4 && !female?
"SICK_MASC"
elsif mood_id == 4 && female?
"SICK_FEM"
else
raise "could not identify pose: moodId=#{mood_id}, female=#{female}, " +
"unconverted=#{unconverted}"
end
end
def as_json(options={})
{
id: id,
gender_mood_description: gender_mood_description,
swf_asset_ids: swf_asset_ids_array,
artist_name: artist_name,
artist_url: artist_url
}
end
def reassign_children_to!(main_pet_state)
self.contributions.each do |contribution|
contribution.contributed = main_pet_state
contribution.save
end
self.outfits.each do |outfit|
outfit.pet_state = main_pet_state
outfit.save
end
ParentSwfAssetRelationship.where(ParentSwfAssetRelationship.arel_table[:parent_id].eq(self.id)).delete_all
end
def reassign_duplicates!
raise "This may only be applied to pet states that represent many duplicate entries" unless duplicate_ids
pet_states = duplicate_ids.split(',').map do |id|
PetState.find(id.to_i)
end
main_pet_state = pet_states.shift
pet_states.each do |pet_state|
pet_state.reassign_children_to!(main_pet_state)
pet_state.destroy
end
end
def sort_swf_asset_ids!
self.swf_asset_ids = swf_asset_ids_array.sort.join(',')
end
def swf_asset_ids
self['swf_asset_ids']
end
def swf_asset_ids_array
swf_asset_ids.split(',').map(&:to_i)
end
def swf_asset_ids=(ids)
self['swf_asset_ids'] = ids
end
def handle_assets!
@parent_swf_asset_relationships_to_update.each do |rel|
rel.swf_asset.save!
rel.save!
end
end
def mood
Mood.find(self.mood_id)
end
def gender_name
if female?
I18n.translate("pet_states.description.gender.female")
else
I18n.translate("pet_states.description.gender.male")
end
end
def mood_name
I18n.translate("pet_states.description.mood.#{mood.name}")
end
def gender_mood_description
if glitched?
I18n.translate('pet_states.description.glitched')
elsif unconverted?
I18n.translate('pet_states.description.unconverted')
elsif labeled?
I18n.translate('pet_states.description.main', :gender => gender_name,
:mood => mood_name)
else
I18n.translate('pet_states.description.unlabeled')
end
end
def artist_name
artist_neopets_username || I18n.translate("pet_states.default_artist_name")
end
def artist_url
if artist_neopets_username
"https://www.neopets.com/userlookup.phtml?user=#{artist_neopets_username}"
else
nil
end
end
def self.from_pet_type_and_biology_info(pet_type, info)
swf_asset_ids = []
info.each do |zone_id, asset_info|
if zone_id.present? && asset_info
swf_asset_ids << asset_info[:part_id].to_i
end
end
swf_asset_ids_str = swf_asset_ids.sort.join(',')
if pet_type.new_record?
pet_state = self.new :swf_asset_ids => swf_asset_ids_str
else
pet_state = self.find_or_initialize_by(
pet_type_id: pet_type.id,
swf_asset_ids: swf_asset_ids_str
)
end
existing_swf_assets = SwfAsset.biology_assets.includes(:zone).
where(remote_id: swf_asset_ids)
existing_swf_assets_by_id = {}
existing_swf_assets.each do |swf_asset|
existing_swf_assets_by_id[swf_asset.remote_id] = swf_asset
end
existing_relationships_by_swf_asset_id = {}
unless pet_state.new_record?
pet_state.parent_swf_asset_relationships.each do |relationship|
existing_relationships_by_swf_asset_id[relationship.swf_asset_id] = relationship
end
end
pet_state.pet_type = pet_type # save the second case from having to look it up by ID
relationships = []
info.each do |zone_id, asset_info|
if zone_id.present? && asset_info
swf_asset_id = asset_info[:part_id].to_i
swf_asset = existing_swf_assets_by_id[swf_asset_id]
unless swf_asset
swf_asset = SwfAsset.new
swf_asset.remote_id = swf_asset_id
end
swf_asset.origin_biology_data = asset_info
swf_asset.origin_pet_type = pet_type
relationship = existing_relationships_by_swf_asset_id[swf_asset.id]
unless relationship
relationship ||= ParentSwfAssetRelationship.new
relationship.parent = pet_state
relationship.swf_asset_id = swf_asset.id
end
relationship.swf_asset = swf_asset
relationships << relationship
end
end
pet_state.parent_swf_asset_relationships_to_update = relationships
pet_state.unconverted = (relationships.size == 1)
pet_state
end
# Copied from https://github.com/matchu/neopets/blob/5d13a720b616ba57fbbd54541f3e5daf02b3fedc/lib/neopets/pet/mood.rb
class Mood
attr_reader :id, :name
def initialize(options)
@id = options[:id]
@name = options[:name]
end
def self.find(id)
self.all_by_id[id.to_i]
end
def self.all
@all ||= [
Mood.new(:id => 1, :name => :happy),
Mood.new(:id => 2, :name => :sad),
Mood.new(:id => 4, :name => :sick)
]
end
def self.all_by_id
@all_by_id ||= self.all.inject({}) { |h, m| h[m.id] = m; h }
end
end
end