forked from OpenNeo/impress
This clocks in a bit bigger than what Impress 2020 does in terms of binary encoding (with gzip it's at 11K instead of 4K), but I'm okay with that for the simplicity win. Gonna try to swap this in for where we're still using Impress 2020 for the species/color picker in the outfit editor!
231 lines
6.5 KiB
Ruby
231 lines
6.5 KiB
Ruby
class PetState < ApplicationRecord
|
|
SwfAssetType = 'biology'
|
|
|
|
MAIN_POSES = %w(HAPPY_FEM HAPPY_MASC SAD_FEM SAD_MASC SICK_FEM SICK_MASC)
|
|
|
|
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 :species_id, :species, :color_id, :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
|
|
|
|
# TODO: More and more, wanting to refactor poses…
|
|
def pose=(pose)
|
|
case pose
|
|
when "UNKNOWN"
|
|
label_pose nil, nil, unconverted: nil, labeled: false
|
|
when "HAPPY_MASC"
|
|
label_pose 1, false
|
|
when "HAPPY_FEM"
|
|
label_pose 1, true
|
|
when "SAD_MASC"
|
|
label_pose 2, false
|
|
when "SAD_FEM"
|
|
label_pose 2, true
|
|
when "SICK_MASC"
|
|
label_pose 4, false
|
|
when "SICK_FEM"
|
|
label_pose 4, true
|
|
when "UNCONVERTED"
|
|
label_pose nil, nil, unconverted: true
|
|
end
|
|
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 to_param
|
|
"#{id}-#{pose.split('_').map(&:capitalize).join('-')}"
|
|
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
|
|
end
|
|
|
|
private
|
|
|
|
# A helper for the `pose=` method.
|
|
def label_pose(mood_id, female, unconverted: false, labeled: true)
|
|
self.labeled = labeled
|
|
self.mood_id = mood_id
|
|
self.female = female
|
|
self.unconverted = unconverted
|
|
end
|
|
|
|
def self.last_updated_key
|
|
PetState.maximum(:id)
|
|
end
|
|
|
|
def self.all_supported_poses
|
|
Rails.cache.fetch("PetState.all_supported_poses #{last_updated_key}") do
|
|
{}.tap do |h|
|
|
includes(:pet_type).find_each do |pet_state|
|
|
h[pet_state.species_id] ||= {}
|
|
h[pet_state.species_id][pet_state.color_id] ||= []
|
|
h[pet_state.species_id][pet_state.color_id] << pet_state.pose
|
|
end
|
|
|
|
h.values.map(&:values).flatten(1).each(&:uniq!).each(&:sort!)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|