forked from OpenNeo/impress
Matchu
2501cb5667
TNT has started serving half-removed Corridor of Chance effects: it has the asset ID and URL and all, but the zone ID is blank. RocketAMF has patched the empty key bug, and now we ignore assets associated with empty keys.
212 lines
7.3 KiB
Ruby
212 lines
7.3 KiB
Ruby
class PetState < ActiveRecord::Base
|
|
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
|
|
|
|
alias_method :swf_asset_ids_from_association, :swf_asset_ids
|
|
|
|
attr_writer :parent_swf_asset_relationships_to_update
|
|
|
|
# Our ideal order is: happy, sad, sick, UC, any+effects, glitched, with male
|
|
# before female within those groups for consistency. We therefore order as
|
|
# follows, listed in order of priority:
|
|
# * Send glitched states to the back
|
|
# * Bring known happy states to the front (we don't want to sort by mood_id
|
|
# DESC first because then labeled sad will appear before unlabeled happy)
|
|
# * Send states with effect assets to the back
|
|
# * Bring state with more assets forward (that is, send UC near the back)
|
|
# * Bring males forward
|
|
# * Bring states with a lower asset ID sum forward (the idea being that
|
|
# sad/female states are usually created after a happy/male base, but that's
|
|
# becoming increasingly untrue over time - this is a very last resort)
|
|
#
|
|
# Maybe someday, when most states are labeled, we can depend exclusively on
|
|
# their labels - or at least use more than is-happy and is-female. For now,
|
|
# though, this strikes a good balance of bringing default to the front for
|
|
# many pet types (the highest priority!) and otherwise doing decent sorting.
|
|
bio_effect_zone_id = 4
|
|
scope :emotion_order, joins(:parent_swf_asset_relationships).
|
|
joins("LEFT JOIN swf_assets effect_assets ON effect_assets.id = parents_swf_assets.swf_asset_id AND effect_assets.zone_id = #{bio_effect_zone_id}").
|
|
group("pet_states.id").
|
|
order("glitched ASC, (mood_id = 1) DESC, COUNT(effect_assets.remote_id) ASC, COUNT(parents_swf_assets.swf_asset_id) DESC, female ASC, SUM(parents_swf_assets.swf_asset_id) ASC")
|
|
|
|
def as_json(options={})
|
|
serializable_hash :only => [:id], :methods => [:gender_mood_description]
|
|
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.split(',').map(&:to_i).sort.join(',')
|
|
end
|
|
|
|
def swf_asset_ids
|
|
self['swf_asset_ids']
|
|
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 label_by_pet(pet, username)
|
|
# If this pet is already labeled with a gender/mood, or it's unconverted
|
|
# and therefore has none, skip.
|
|
return false if self.labeled? || self.unconverted?
|
|
|
|
# Find this pet on the owner's userlookup, where we can get both its gender
|
|
# and its mood.
|
|
begin
|
|
user_pet = Neopets::User.new(username).pets.
|
|
find { |user_pet| user_pet.name.downcase == pet.name.strip.downcase }
|
|
self.female = user_pet.female?
|
|
self.mood_id = user_pet.mood.id
|
|
self.labeled = true
|
|
rescue Neopets::User::Error
|
|
# If there's an error loading the userlookup data (e.g. account is
|
|
# frozen), no big deal; we just won't label the pet right now. Proceed
|
|
# as usual.
|
|
end
|
|
|
|
true
|
|
end
|
|
|
|
def mood
|
|
Neopets::Pet::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 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_and_swf_asset_ids(
|
|
pet_type.id,
|
|
swf_asset_ids_str
|
|
)
|
|
end
|
|
existing_swf_assets = SwfAsset.biology_assets.includes(:zone).
|
|
find_all_by_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
|
|
|
|
def self.repair_all!
|
|
self.transaction do
|
|
self.all.each do |pet_state|
|
|
pet_state.sort_swf_asset_ids!
|
|
pet_state.save
|
|
end
|
|
|
|
self.
|
|
select('pet_states.pet_type_id, pet_states.swf_asset_ids, GROUP_CONCAT(DISTINCT pet_states.id) AS duplicate_ids').
|
|
joins('INNER JOIN pet_states ps2 ON pet_states.pet_type_id = ps2.pet_type_id AND pet_states.swf_asset_ids = ps2.swf_asset_ids').
|
|
group('pet_states.pet_type_id, pet_states.swf_asset_ids').
|
|
having('count(*) > 1').
|
|
all.
|
|
each do |pet_state|
|
|
pet_state.reassign_duplicates!
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|