support special colors in the infinite closet

This commit is contained in:
Emi Matchu 2011-05-02 18:07:56 -04:00
parent d565fd6587
commit 648649f5cc
8 changed files with 322 additions and 131 deletions

View file

@ -1,11 +1,16 @@
class SwfAssetsController < ApplicationController class SwfAssetsController < ApplicationController
def index def index
if params[:item_id] if params[:item_id]
@swf_assets = Item.find(params[:item_id]).swf_assets item = Item.find(params[:item_id])
@swf_assets = item.swf_assets
if params[:body_id] if params[:body_id]
@swf_assets = @swf_assets.fitting_body_id(params[:body_id]) @swf_assets = @swf_assets.fitting_body_id(params[:body_id])
else else
@swf_assets = @swf_assets.fitting_standard_body_ids if item.special_color
@swf_assets = @swf_assets.fitting_color(item.special_color)
else
@swf_assets = @swf_assets.fitting_standard_body_ids
end
json = @swf_assets.all.group_by(&:body_id) json = @swf_assets.all.group_by(&:body_id)
end end
elsif params[:body_id] && params[:item_ids] elsif params[:body_id] && params[:item_ids]
@ -26,3 +31,4 @@ class SwfAssetsController < ApplicationController
render :json => json render :json => json
end end
end end

View file

@ -2,14 +2,14 @@ module ItemsHelper
NeoitemsURLFormat = 'http://neoitems.net/search2.php?Name=%s&AndOr=and&Category=All&Special=0&Status=Active&Sort=ItemID&results=15&SearchType=8' NeoitemsURLFormat = 'http://neoitems.net/search2.php?Name=%s&AndOr=and&Category=All&Special=0&Status=Active&Sort=ItemID&results=15&SearchType=8'
module PetTypeImage module PetTypeImage
Format = 'http://pets.neopets.com/cp/%s/%i/%i.png' Format = 'http://pets.neopets.com/cp/%s/%i/%i.png'
Emotions = { Emotions = {
:happy => 1, :happy => 1,
:sad => 2, :sad => 2,
:angry => 3, :angry => 3,
:ill => 4 :ill => 4
} }
Sizes = { Sizes = {
:face => 1, :face => 1,
:thumb => 2, :thumb => 2,
@ -17,17 +17,17 @@ module ItemsHelper
:full => 4 :full => 4
} }
end end
def standard_species_search_links def standard_species_search_links
build_on_random_standard_pet_types(Species.all) do |pet_type| build_on_pet_types(Species.all) do |pet_type|
image = pet_type_image(pet_type, :happy, :zoom) image = pet_type_image(pet_type, :happy, :zoom)
query = "species:#{pet_type.species.name}" query = "species:#{pet_type.species.name}"
link_to(image, items_path(:q => query)) link_to(image, items_path(:q => query))
end end
end end
def standard_species_images(species) def standard_species_images_for(item)
build_on_random_standard_pet_types(species) do |pet_type| build_on_pet_types(item.supported_species, item.special_color) do |pet_type|
image = pet_type_image(pet_type, :happy, :face) image = pet_type_image(pet_type, :happy, :face)
attributes = { attributes = {
'data-id' => pet_type.id, 'data-id' => pet_type.id,
@ -47,25 +47,29 @@ module ItemsHelper
) )
end end
end end
def list_zones(zones, method=:label) def list_zones(zones, method=:label)
zones.sort { |x,y| x.label <=> y.label }.map(&method).join(', ') zones.sort { |x,y| x.label <=> y.label }.map(&method).join(', ')
end end
def nc_icon_for(item) def nc_icon_for(item)
image_tag 'nc.png', :title => 'NC Mall Item', :alt => 'NC', :class => 'nc-icon' if item.nc? image_tag 'nc.png', :title => 'NC Mall Item', :alt => 'NC', :class => 'nc-icon' if item.nc?
end end
def neoitems_url_for(item) def neoitems_url_for(item)
sprintf(NeoitemsURLFormat, CGI::escape(item.name)) sprintf(NeoitemsURLFormat, CGI::escape(item.name))
end end
private private
def build_on_random_standard_pet_types(species, &block) def build_on_pet_types(species, special_color=nil, &block)
raw(PetType.random_basic_per_species(species.map(&:id)).map(&block).join) species_ids = species.map(&:id)
pet_types = special_color ?
PetType.where(:color_id => special_color.id, :species_id => species_ids).order(:species_id) :
PetType.random_basic_per_species(species.map(&:id))
pet_types.map(&block).join.html_safe
end end
def pet_type_image(pet_type, emotion, size) def pet_type_image(pet_type, emotion, size)
emotion_id = PetTypeImage::Emotions[emotion] emotion_id = PetTypeImage::Emotions[emotion]
size_id = PetTypeImage::Sizes[size] size_id = PetTypeImage::Sizes[size]
@ -74,3 +78,4 @@ module ItemsHelper
image_tag(src, :alt => human_name, :title => human_name) image_tag(src, :alt => human_name, :title => human_name)
end end
end end

View file

@ -2,46 +2,50 @@
class Item < ActiveRecord::Base class Item < ActiveRecord::Base
SwfAssetType = 'object' SwfAssetType = 'object'
has_one :contribution, :as => :contributed has_one :contribution, :as => :contributed
has_many :parent_swf_asset_relationships, :foreign_key => 'parent_id', has_many :parent_swf_asset_relationships, :foreign_key => 'parent_id',
:conditions => {:swf_asset_type => SwfAssetType} :conditions => {:swf_asset_type => SwfAssetType}
has_many :swf_assets, :through => :parent_swf_asset_relationships, :source => :object_asset, has_many :swf_assets, :through => :parent_swf_asset_relationships, :source => :object_asset,
:conditions => {:type => SwfAssetType} :conditions => {:type => SwfAssetType}
attr_writer :current_body_id attr_writer :current_body_id
NCRarities = [0, 500] NCRarities = [0, 500]
PaintbrushSetDescription = 'This item is part of a deluxe paint brush set!' PAINTBRUSH_SET_DESCRIPTION = 'This item is part of a deluxe paint brush set!'
SPECIAL_COLOR_DESCRIPTION_REGEX = /This item is only wearable by Neopets painted ([a-zA-Z]+)\./
SPECIAL_PAINTBRUSH_COLORS_PATH = Rails.root.join('config', 'colors_with_unique_bodies.txt')
SPECIAL_PAINTBRUSH_COLORS = File.read(SPECIAL_PAINTBRUSH_COLORS_PATH).split("\n").map { |name| Color.find_by_name(name) }
set_table_name 'objects' # Neo & PHP Impress call them objects, but the class name is a conflict (duh!) set_table_name 'objects' # Neo & PHP Impress call them objects, but the class name is a conflict (duh!)
set_inheritance_column 'inheritance_type' # PHP Impress used "type" to describe category set_inheritance_column 'inheritance_type' # PHP Impress used "type" to describe category
cattr_reader :per_page cattr_reader :per_page
@@per_page = 30 @@per_page = 30
scope :alphabetize, order('name ASC') scope :alphabetize, order('name ASC')
scope :join_swf_assets, joins("INNER JOIN #{ParentSwfAssetRelationship.table_name} psa ON psa.swf_asset_type = 'object' AND psa.parent_id = objects.id"). scope :join_swf_assets, joins("INNER JOIN #{ParentSwfAssetRelationship.table_name} psa ON psa.swf_asset_type = 'object' AND psa.parent_id = objects.id").
joins("INNER JOIN #{SwfAsset.table_name} swf_assets ON swf_assets.id = psa.swf_asset_id"). joins("INNER JOIN #{SwfAsset.table_name} swf_assets ON swf_assets.id = psa.swf_asset_id").
group('objects.id') group('objects.id')
scope :without_swf_assets, joins( scope :without_swf_assets, joins(
"LEFT JOIN #{ParentSwfAssetRelationship.table_name} psa ON psa.swf_asset_type = 'object' AND psa.parent_id = #{table_name}.id " + "LEFT JOIN #{ParentSwfAssetRelationship.table_name} psa ON psa.swf_asset_type = 'object' AND psa.parent_id = #{table_name}.id " +
"LEFT JOIN #{SwfAsset.table_name} sa ON sa.type = 'object' AND sa.id = psa.swf_asset_id" "LEFT JOIN #{SwfAsset.table_name} sa ON sa.type = 'object' AND sa.id = psa.swf_asset_id"
).where('sa.id IS NULL') ).where('sa.id IS NULL')
scope :spidered_longest_ago, order(["(#{Item.arel_table[:last_spidered].eq(nil).to_sql}) DESC", arel_table[:last_spidered].desc]) scope :spidered_longest_ago, order(["(#{Item.arel_table[:last_spidered].eq(nil).to_sql}) DESC", arel_table[:last_spidered].desc])
scope :sold_in_mall, where(:sold_in_mall => true) scope :sold_in_mall, where(:sold_in_mall => true)
scope :not_sold_in_mall, where(:sold_in_mall => false) scope :not_sold_in_mall, where(:sold_in_mall => false)
# Not defining validations, since this app is currently read-only # Not defining validations, since this app is currently read-only
def nc? def nc?
NCRarities.include?(rarity_index) NCRarities.include?(rarity_index)
end end
def restricted_zones def restricted_zones
unless @restricted_zones unless @restricted_zones
@restricted_zones = [] @restricted_zones = []
@ -51,7 +55,7 @@ class Item < ActiveRecord::Base
end end
@restricted_zones @restricted_zones
end end
def occupied_zones def occupied_zones
all_body_ids = [] all_body_ids = []
zone_body_ids = {} zone_body_ids = {}
@ -70,25 +74,45 @@ class Item < ActiveRecord::Base
end end
zones zones
end end
def affected_zones def affected_zones
restricted_zones + occupied_zones restricted_zones + occupied_zones
end end
def special_color
@special_color ||= determine_special_color
end
protected
def determine_special_color
if description.include?(PAINTBRUSH_SET_DESCRIPTION)
downcased_name = name.downcase
SPECIAL_PAINTBRUSH_COLORS.each do |color|
return color if downcased_name.include?(color.name)
end
end
match = description.match(SPECIAL_COLOR_DESCRIPTION_REGEX)
if match
return Color.find_by_name(match[1].downcase)
end
end
public
def species_support_ids def species_support_ids
@species_support_ids_array ||= read_attribute('species_support_ids').split(',').map(&:to_i) rescue nil @species_support_ids_array ||= read_attribute('species_support_ids').split(',').map(&:to_i) rescue nil
end end
def species_support_ids=(replacement) def species_support_ids=(replacement)
@species_support_ids_array = nil @species_support_ids_array = nil
replacement = replacement.join(',') if replacement.is_a?(Array) replacement = replacement.join(',') if replacement.is_a?(Array)
write_attribute('species_support_ids', replacement) write_attribute('species_support_ids', replacement)
end end
def supported_species def supported_species
@supported_species ||= species_support_ids.blank? ? Species.all : species_support_ids.sort.map { |id| Species.find(id) } @supported_species ||= species_support_ids.blank? ? Species.all : species_support_ids.sort.map { |id| Species.find(id) }
end end
def self.search(query) def self.search(query)
raise SearchError, "Please provide a search query" unless query raise SearchError, "Please provide a search query" unless query
query = query.strip query = query.strip
@ -120,7 +144,7 @@ class Item < ActiveRecord::Base
condition.narrow(scope) condition.narrow(scope)
end end
end end
def as_json(options = {}) def as_json(options = {})
{ {
:description => description, :description => description,
@ -131,12 +155,12 @@ class Item < ActiveRecord::Base
:rarity_index => rarity_index :rarity_index => rarity_index
} }
end end
before_create do before_create do
self.sold_in_mall ||= false self.sold_in_mall ||= false
true true
end end
def handle_assets! def handle_assets!
if @parent_swf_asset_relationships_to_update && @current_body_id if @parent_swf_asset_relationships_to_update && @current_body_id
new_swf_asset_ids = @parent_swf_asset_relationships_to_update.map(&:swf_asset_id) new_swf_asset_ids = @parent_swf_asset_relationships_to_update.map(&:swf_asset_id)
@ -158,7 +182,7 @@ class Item < ActiveRecord::Base
self.parent_swf_asset_relationships += @parent_swf_asset_relationships_to_update self.parent_swf_asset_relationships += @parent_swf_asset_relationships_to_update
end end
end end
def origin_registry_info=(info) def origin_registry_info=(info)
# bear in mind that numbers from registries are floats # bear in mind that numbers from registries are floats
self.species_support_ids = info[:species_support].map(&:to_i) self.species_support_ids = info[:species_support].map(&:to_i)
@ -170,17 +194,17 @@ class Item < ActiveRecord::Base
end end
end end
end end
def pending_swf_assets def pending_swf_assets
@parent_swf_asset_relationships_to_update.inject([]) do |all_swf_assets, relationship| @parent_swf_asset_relationships_to_update.inject([]) do |all_swf_assets, relationship|
all_swf_assets << relationship.swf_asset all_swf_assets << relationship.swf_asset
end end
end end
def parent_swf_asset_relationships_to_update=(rels) def parent_swf_asset_relationships_to_update=(rels)
@parent_swf_asset_relationships_to_update = rels @parent_swf_asset_relationships_to_update = rels
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 = {}
@ -208,7 +232,7 @@ class Item < ActiveRecord::Base
end end
end end
end end
def self.collection_from_pet_type_and_registries(pet_type, info_registry, asset_registry) def self.collection_from_pet_type_and_registries(pet_type, info_registry, asset_registry)
# bear in mind that registries are arrays with many nil elements, # bear in mind that registries are arrays with many nil elements,
# due to how the parser works # due to how the parser works
@ -277,7 +301,7 @@ class Item < ActiveRecord::Base
end end
items.values items.values
end end
class << self class << self
MALL_HOST = 'ncmall.neopets.com' MALL_HOST = 'ncmall.neopets.com'
MALL_MAIN_PATH = '/mall/shop.phtml' MALL_MAIN_PATH = '/mall/shop.phtml'
@ -286,14 +310,14 @@ class Item < ActiveRecord::Base
MALL_CATEGORY_TRIGGER = /load_items_pane\("browse", ([0-9]+)\);/ MALL_CATEGORY_TRIGGER = /load_items_pane\("browse", ([0-9]+)\);/
MALL_JSON_ITEM_DATA_KEY = 'object_data' MALL_JSON_ITEM_DATA_KEY = 'object_data'
MALL_ITEM_URL_TEMPLATE = 'http://images.neopets.com/items/%s.gif' MALL_ITEM_URL_TEMPLATE = 'http://images.neopets.com/items/%s.gif'
MALL_MAIN_URI = Addressable::URI.new :scheme => 'http', MALL_MAIN_URI = Addressable::URI.new :scheme => 'http',
:host => MALL_HOST, :path => MALL_MAIN_PATH :host => MALL_HOST, :path => MALL_MAIN_PATH
MALL_CATEGORY_URI = Addressable::URI.new :scheme => 'http', MALL_CATEGORY_URI = Addressable::URI.new :scheme => 'http',
:host => MALL_HOST, :path => MALL_CATEGORY_PATH, :host => MALL_HOST, :path => MALL_CATEGORY_PATH,
:query => MALL_CATEGORY_QUERY :query => MALL_CATEGORY_QUERY
MALL_CATEGORY_TEMPLATE = Addressable::Template.new MALL_CATEGORY_URI MALL_CATEGORY_TEMPLATE = Addressable::Template.new MALL_CATEGORY_URI
def spider_mall! def spider_mall!
# Load the mall HTML, scan it for category onclicks # Load the mall HTML, scan it for category onclicks
items = {} items = {}
@ -340,7 +364,7 @@ class Item < ActiveRecord::Base
end end
items items
end end
def spider_mall_assets!(limit) def spider_mall_assets!(limit)
items = self.select([arel_table[:id], arel_table[:name]]).sold_in_mall.spidered_longest_ago.limit(limit).all items = self.select([arel_table[:id], arel_table[:name]]).sold_in_mall.spidered_longest_ago.limit(limit).all
puts "- #{items.size} items need asset spidering" puts "- #{items.size} items need asset spidering"
@ -349,7 +373,7 @@ class Item < ActiveRecord::Base
AssetStrategy.spider item AssetStrategy.spider item
end end
end end
def spider_request(uri) def spider_request(uri)
begin begin
response = Net::HTTP.get_response uri response = Net::HTTP.get_response uri
@ -361,26 +385,26 @@ class Item < ActiveRecord::Base
end end
response.body response.body
end end
private private
class AssetStrategy class AssetStrategy
Strategies = {} Strategies = {}
MALL_ASSET_PATH = '/mall/ajax/get_item_assets.phtml' MALL_ASSET_PATH = '/mall/ajax/get_item_assets.phtml'
MALL_ASSET_QUERY = 'pet={pet_name}&oii={item_id}' MALL_ASSET_QUERY = 'pet={pet_name}&oii={item_id}'
MALL_ASSET_URI = Addressable::URI.new :scheme => 'http', MALL_ASSET_URI = Addressable::URI.new :scheme => 'http',
:host => MALL_HOST, :path => MALL_ASSET_PATH, :host => MALL_HOST, :path => MALL_ASSET_PATH,
:query => MALL_ASSET_QUERY :query => MALL_ASSET_QUERY
MALL_ASSET_TEMPLATE = Addressable::Template.new MALL_ASSET_URI MALL_ASSET_TEMPLATE = Addressable::Template.new MALL_ASSET_URI
def initialize(name, options) def initialize(name, options)
@name = name @name = name
@pass = options[:pass] @pass = options[:pass]
@complete = options[:complete] @complete = options[:complete]
@pet_types = options[:pet_types] @pet_types = options[:pet_types]
end end
def spider(item) def spider(item)
puts " - Using #{@name} strategy" puts " - Using #{@name} strategy"
exit = false exit = false
@ -412,9 +436,9 @@ class Item < ActiveRecord::Base
Strategies[@complete].spider(item) Strategies[@complete].spider(item)
end end
end end
private private
def load_for_pet_type(item, pet_type, banned_pet_ids=[]) def load_for_pet_type(item, pet_type, banned_pet_ids=[])
pet_id = pet_type.pet_id pet_id = pet_type.pet_id
pet_name = pet_type.pet_name pet_name = pet_type.pet_name
@ -442,7 +466,7 @@ class Item < ActiveRecord::Base
end end
end end
end end
def load_for_pet_name(item, pet_type, pet_name) def load_for_pet_name(item, pet_type, pet_name)
uri = MALL_ASSET_TEMPLATE. uri = MALL_ASSET_TEMPLATE.
expand( expand(
@ -468,12 +492,12 @@ class Item < ActiveRecord::Base
nil nil
end end
end end
class << self class << self
def add_strategy(name, options) def add_strategy(name, options)
Strategies[name] = new(name, options) Strategies[name] = new(name, options)
end end
def add_cascading_strategy(name, options) def add_cascading_strategy(name, options)
pet_type_groups = options[:pet_types] pet_type_groups = options[:pet_types]
pet_type_group_names = pet_type_groups.keys pet_type_group_names = pet_type_groups.keys
@ -494,7 +518,7 @@ class Item < ActiveRecord::Base
name = next_name name = next_name
end end
end end
def spider(item) def spider(item)
puts "- Spidering for #{item.name}" puts "- Spidering for #{item.name}"
Strategies[:start].spider(item) Strategies[:start].spider(item)
@ -502,7 +526,7 @@ class Item < ActiveRecord::Base
item.save item.save
puts "- #{item.name} done spidering, saved last spidered timestamp" puts "- #{item.name} done spidering, saved last spidered timestamp"
end end
def build_strategies def build_strategies
if Strategies.empty? if Strategies.empty?
pet_type_t = PetType.arel_table pet_type_t = PetType.arel_table
@ -512,20 +536,20 @@ class Item < ActiveRecord::Base
joins(:pets).group(pet_type_t[:id]) joins(:pets).group(pet_type_t[:id])
remaining_standard_pet_types = pet_types.single_standard_color.order(:species_id) remaining_standard_pet_types = pet_types.single_standard_color.order(:species_id)
first_standard_pet_type = [remaining_standard_pet_types.slice!(0)] first_standard_pet_type = [remaining_standard_pet_types.slice!(0)]
add_strategy :start, :pass => :remaining_standard, :complete => :first_nonstandard_color, add_strategy :start, :pass => :remaining_standard, :complete => :first_nonstandard_color,
:pet_types => first_standard_pet_type :pet_types => first_standard_pet_type
add_strategy :remaining_standard, :complete => :exit, add_strategy :remaining_standard, :complete => :exit,
:pet_types => remaining_standard_pet_types :pet_types => remaining_standard_pet_types
add_cascading_strategy :first_nonstandard_color, :complete => :remaining_standard, add_cascading_strategy :first_nonstandard_color, :complete => :remaining_standard,
:pet_types => pet_types.select(pet_type_t[:color_id]).nonstandard_colors.all.group_by(&:color_id) :pet_types => pet_types.select(pet_type_t[:color_id]).nonstandard_colors.all.group_by(&:color_id)
end end
end end
end end
end end
def spider_mall_category(json) def spider_mall_category(json)
begin begin
items_data = JSON.parse(json)[MALL_JSON_ITEM_DATA_KEY] items_data = JSON.parse(json)[MALL_JSON_ITEM_DATA_KEY]
@ -549,17 +573,17 @@ class Item < ActiveRecord::Base
end end
items items
end end
class SpiderError < RuntimeError;end class SpiderError < RuntimeError;end
class SpiderHTTPError < SpiderError;end class SpiderHTTPError < SpiderError;end
class SpiderJSONError < SpiderError;end class SpiderJSONError < SpiderError;end
end end
private private
SearchFilterScopes = [] SearchFilterScopes = []
LimitedSearchFilters = [] LimitedSearchFilters = []
def self.search_filter(name, options={}, &block) def self.search_filter(name, options={}, &block)
assume_complement = options.delete(:assume_complement) || true assume_complement = options.delete(:assume_complement) || true
name = name.to_s name = name.to_s
@ -576,12 +600,12 @@ class Item < ActiveRecord::Base
end end
end end
end end
def self.single_search_filter(name, options={}, &block) def self.single_search_filter(name, options={}, &block)
options[:assume_complement] = false options[:assume_complement] = false
search_filter name, options, &block search_filter name, options, &block
end end
def self.search_filter_block(options, positive) def self.search_filter_block(options, positive)
Proc.new { |str, scope| Proc.new { |str, scope|
condition = yield(str) condition = yield(str)
@ -590,18 +614,18 @@ class Item < ActiveRecord::Base
scope.where(condition) scope.where(condition)
} }
end end
search_filter :name do |name| search_filter :name do |name|
arel_table[:name].matches("%#{name}%") arel_table[:name].matches("%#{name}%")
end end
search_filter :description do |description| search_filter :description do |description|
arel_table[:description].matches("%#{description}%") arel_table[:description].matches("%#{description}%")
end end
ADJECTIVE_FILTERS = { ADJECTIVE_FILTERS = {
'nc' => arel_table[:rarity_index].in(NCRarities), 'nc' => arel_table[:rarity_index].in(NCRarities),
'pb' => arel_table[:description].eq(PaintbrushSetDescription) 'pb' => arel_table[:description].eq(PAINTBRUSH_SET_DESCRIPTION)
} }
search_filter :is do |adjective| search_filter :is do |adjective|
filter = ADJECTIVE_FILTERS[adjective] filter = ADJECTIVE_FILTERS[adjective]
@ -612,7 +636,7 @@ class Item < ActiveRecord::Base
end end
filter filter
end end
search_filter :only do |species_name| search_filter :only do |species_name|
begin begin
id = Species.require_by_name(species_name).id id = Species.require_by_name(species_name).id
@ -621,7 +645,7 @@ class Item < ActiveRecord::Base
end end
arel_table[:species_support_ids].eq(id.to_s) arel_table[:species_support_ids].eq(id.to_s)
end end
search_filter :species do |species_name| search_filter :species do |species_name|
begin begin
id = Species.require_by_name(species_name).id id = Species.require_by_name(species_name).id
@ -636,13 +660,13 @@ class Item < ActiveRecord::Base
"%,#{id}" "%,#{id}"
])) ]))
end end
single_search_filter :type, {:limit => true, :scope => :join_swf_assets} do |zone_set_name| single_search_filter :type, {:limit => true, :scope => :join_swf_assets} do |zone_set_name|
zone_set = Zone::ItemZoneSets[zone_set_name] zone_set = Zone::ItemZoneSets[zone_set_name]
raise SearchError, "Type \"#{zone_set_name}\" does not exist" unless zone_set raise SearchError, "Type \"#{zone_set_name}\" does not exist" unless zone_set
SwfAsset.arel_table[:zone_id].in(zone_set.map(&:id)) SwfAsset.arel_table[:zone_id].in(zone_set.map(&:id))
end end
single_search_filter :not_type, :full => lambda { |zone_set_name, scope| single_search_filter :not_type, :full => lambda { |zone_set_name, scope|
zone_set = Zone::ItemZoneSets[zone_set_name] zone_set = Zone::ItemZoneSets[zone_set_name]
raise SearchError, "Type \"#{zone_set_name}\" does not exist" unless zone_set raise SearchError, "Type \"#{zone_set_name}\" does not exist" unless zone_set
@ -670,27 +694,27 @@ class Item < ActiveRecord::Base
map(&:id) map(&:id)
scope.where(arel_table[:id].in(item_ids)) scope.where(arel_table[:id].in(item_ids))
} }
class Condition < String class Condition < String
attr_accessor :filter attr_accessor :filter
def initialize def initialize
@positive = true @positive = true
end end
def filter? def filter?
!@filter.nil? !@filter.nil?
end end
def to_filter! def to_filter!
@filter = self.clone @filter = self.clone
self.replace '' self.replace ''
end end
def negate! def negate!
@positive = !@positive @positive = !@positive
end end
def narrow(scope) def narrow(scope)
if SearchFilterScopes.include?(filter) if SearchFilterScopes.include?(filter)
polarized_filter = @positive ? filter : "not_#{filter}" polarized_filter = @positive ? filter : "not_#{filter}"
@ -699,17 +723,18 @@ class Item < ActiveRecord::Base
raise SearchError, "Filter #{filter} does not exist" raise SearchError, "Filter #{filter} does not exist"
end end
end end
def filter def filter
@filter || 'name' @filter || 'name'
end end
def inspect def inspect
@filter ? "#{@filter}:#{super}" : super @filter ? "#{@filter}:#{super}" : super
end end
end end
class SearchError < ArgumentError;end class SearchError < ArgumentError;end
end end
require 'item_sweeper' require 'item_sweeper'

View file

@ -2,27 +2,27 @@ class PetType < ActiveRecord::Base
IMAGE_CPN_FORMAT = 'http://pets.neopets.com/cpn/%s/1/1.png'; IMAGE_CPN_FORMAT = 'http://pets.neopets.com/cpn/%s/1/1.png';
IMAGE_CP_LOCATION_REGEX = %r{^/cp/(.+?)/1/1\.png$}; IMAGE_CP_LOCATION_REGEX = %r{^/cp/(.+?)/1/1\.png$};
IMAGE_CPN_ACCEPTABLE_NAME = /^[a-z0-9_]+$/ IMAGE_CPN_ACCEPTABLE_NAME = /^[a-z0-9_]+$/
has_one :contribution, :as => :contributed has_one :contribution, :as => :contributed
has_many :pet_states has_many :pet_states
has_many :pets has_many :pets
attr_writer :origin_pet attr_writer :origin_pet
BasicHashes = YAML::load_file(Rails.root.join('config', 'basic_type_hashes.yml')) BasicHashes = YAML::load_file(Rails.root.join('config', 'basic_type_hashes.yml'))
StandardPetTypesBySpeciesId = PetType.where(arel_table[:color_id].in(Color::BasicIds)).group_by(&:species_id) StandardPetTypesBySpeciesId = PetType.where(arel_table[:color_id].in(Color::BasicIds)).group_by(&:species_id)
StandardBodyIds = [] StandardBodyIds = []
StandardPetTypesBySpeciesId.each do |species_id, pet_types| StandardPetTypesBySpeciesId.each do |species_id, pet_types|
StandardBodyIds += pet_types.map(&:body_id) StandardBodyIds += pet_types.map(&:body_id)
end end
# Returns all pet types of a single standard color. The caller shouldn't care # Returns all pet types of a single standard color. The caller shouldn't care
# which, though, in this implemention, it's always Blue. Don't depend on that. # which, though, in this implemention, it's always Blue. Don't depend on that.
scope :single_standard_color, where(:color_id => Color::BasicIds[0]) scope :single_standard_color, where(:color_id => Color::BasicIds[0])
scope :nonstandard_colors, where(:color_id => Color.nonstandard_ids) scope :nonstandard_colors, where(:color_id => Color.nonstandard_ids)
def self.random_basic_per_species(species_ids) def self.random_basic_per_species(species_ids)
random_pet_types = [] random_pet_types = []
species_ids.each do |species_id| species_ids.each do |species_id|
@ -31,7 +31,7 @@ class PetType < ActiveRecord::Base
end end
random_pet_types random_pet_types
end end
def as_json(options={}) def as_json(options={})
if options[:for] == 'wardrobe' if options[:for] == 'wardrobe'
{:id => id, :body_id => body_id, :pet_state_ids => pet_states.select([:id]).emotion_order.map(&:id)} {:id => id, :body_id => body_id, :pet_state_ids => pet_states.select([:id]).emotion_order.map(&:id)}
@ -39,47 +39,47 @@ class PetType < ActiveRecord::Base
{:image_hash => image_hash} {:image_hash => image_hash}
end end
end end
def color_id=(new_color_id) def color_id=(new_color_id)
@color = nil @color = nil
write_attribute('color_id', new_color_id) write_attribute('color_id', new_color_id)
end end
def color=(new_color) def color=(new_color)
@color = new_color @color = new_color
write_attribute('color_id', @color.id) write_attribute('color_id', @color.id)
end end
def color def color
@color ||= Color.find(color_id) @color ||= Color.find(color_id)
end end
def species_id=(new_species_id) def species_id=(new_species_id)
@species = nil @species = nil
write_attribute('species_id', new_species_id) write_attribute('species_id', new_species_id)
end end
def species=(new_species) def species=(new_species)
@species = new_species @species = new_species
write_attribute('species_id', @species.id) write_attribute('species_id', @species.id)
end end
def species def species
@species ||= Species.find(species_id) @species ||= Species.find(species_id)
end end
def image_hash def image_hash
self['image_hash'] || basic_image_hash self['image_hash'] || basic_image_hash
end end
def basic_image_hash def basic_image_hash
BasicHashes[species.name][color.name] BasicHashes[species.name][color.name]
end end
def human_name def human_name
self.color.human_name + ' ' + self.species.human_name self.color.human_name + ' ' + self.species.human_name
end end
def needed_items def needed_items
items = Item.arel_table items = Item.arel_table
species_matchers = [ species_matchers = [
@ -101,12 +101,12 @@ class PetType < ActiveRecord::Base
Item.where(items[:id].not_in(unneeded_item_ids)). Item.where(items[:id].not_in(unneeded_item_ids)).
where(species_condition) where(species_condition)
end end
def add_pet_state_from_biology!(biology) def add_pet_state_from_biology!(biology)
pet_state = PetState.from_pet_type_and_biology_info(self, biology) pet_state = PetState.from_pet_type_and_biology_info(self, biology)
pet_state pet_state
end end
before_save do before_save do
if @origin_pet && @origin_pet.name =~ IMAGE_CPN_ACCEPTABLE_NAME if @origin_pet && @origin_pet.name =~ IMAGE_CPN_ACCEPTABLE_NAME
cpn_uri = URI.parse sprintf(IMAGE_CPN_FORMAT, CGI.escape(@origin_pet.name)); cpn_uri = URI.parse sprintf(IMAGE_CPN_FORMAT, CGI.escape(@origin_pet.name));
@ -134,7 +134,7 @@ class PetType < ActiveRecord::Base
end end
end end
end end
def self.all_by_ids_or_children(ids, pet_states) def self.all_by_ids_or_children(ids, pet_states)
pet_states_by_pet_type_id = {} pet_states_by_pet_type_id = {}
pet_states.each do |pet_state| pet_states.each do |pet_state|
@ -154,6 +154,7 @@ class PetType < ActiveRecord::Base
end end
end end
end end
class DownloadError < Exception;end class DownloadError < Exception;end
end end

View file

@ -3,35 +3,40 @@ class SwfAsset < ActiveRecord::Base
LOCAL_ASSET_DIR = Rails.root.join('public', PUBLIC_ASSET_DIR) LOCAL_ASSET_DIR = Rails.root.join('public', PUBLIC_ASSET_DIR)
NEOPETS_ASSET_SERVER = 'http://images.neopets.com' NEOPETS_ASSET_SERVER = 'http://images.neopets.com'
set_inheritance_column 'inheritance_type' set_inheritance_column 'inheritance_type'
attr_accessor :item attr_accessor :item
has_one :contribution, :as => :contributed has_one :contribution, :as => :contributed
has_many :object_asset_relationships, :class_name => 'ParentSwfAssetRelationship', has_many :object_asset_relationships, :class_name => 'ParentSwfAssetRelationship',
:conditions => {:swf_asset_type => 'object'} :conditions => {:swf_asset_type => 'object'}
delegate :depth, :to => :zone delegate :depth, :to => :zone
scope :fitting_body_id, lambda { |body_id| scope :fitting_body_id, lambda { |body_id|
where(arel_table[:body_id].in([body_id, 0])) where(arel_table[:body_id].in([body_id, 0]))
} }
BodyIdsFittingStandard = PetType::StandardBodyIds + [0] BodyIdsFittingStandard = PetType::StandardBodyIds + [0]
scope :fitting_standard_body_ids, lambda { scope :fitting_standard_body_ids, lambda {
where(arel_table[:body_id].in(BodyIdsFittingStandard)) where(arel_table[:body_id].in(BodyIdsFittingStandard))
} }
scope :fitting_color, lambda { |color|
body_ids = PetType.select(:body_id).where(:color_id => color.id).map(&:body_id)
where(arel_table[:body_id].in(body_ids))
}
scope :biology_assets, where(arel_table[:type].eq(PetState::SwfAssetType)) scope :biology_assets, where(arel_table[:type].eq(PetState::SwfAssetType))
scope :object_assets, where(arel_table[:type].eq(Item::SwfAssetType)) scope :object_assets, where(arel_table[:type].eq(Item::SwfAssetType))
scope :for_item_ids, lambda { |item_ids| scope :for_item_ids, lambda { |item_ids|
joins(:object_asset_relationships). joins(:object_asset_relationships).
where(ParentSwfAssetRelationship.arel_table[:parent_id].in(item_ids)) where(ParentSwfAssetRelationship.arel_table[:parent_id].in(item_ids))
} }
def local_url def local_url
'/' + File.join(PUBLIC_ASSET_DIR, local_path_within_outfit_swfs) '/' + File.join(PUBLIC_ASSET_DIR, local_path_within_outfit_swfs)
end end
def as_json(options={}) def as_json(options={})
json = { json = {
:id => id, :id => id,
@ -49,37 +54,37 @@ class SwfAsset < ActiveRecord::Base
json[:parent_id] = options[:parent_id] if options[:parent_id] json[:parent_id] = options[:parent_id] if options[:parent_id]
json json
end end
def body_specific? def body_specific?
self.zone.type_id < 3 self.zone.type_id < 3
end end
def zone def zone
Zone.find(zone_id) Zone.find(zone_id)
end end
def origin_pet_type=(pet_type) def origin_pet_type=(pet_type)
self.body_id = pet_type.body_id self.body_id = pet_type.body_id
end end
def origin_biology_data=(data) def origin_biology_data=(data)
self.type = 'biology' self.type = 'biology'
self.zone_id = data[:zone_id].to_i self.zone_id = data[:zone_id].to_i
self.url = data[:asset_url] self.url = data[:asset_url]
self.zones_restrict = data[:zones_restrict] self.zones_restrict = data[:zones_restrict]
end end
def origin_object_data=(data) def origin_object_data=(data)
self.type = 'object' self.type = 'object'
self.zone_id = data[:zone_id].to_i self.zone_id = data[:zone_id].to_i
self.url = data[:asset_url] self.url = data[:asset_url]
end end
def mall_data=(data) def mall_data=(data)
self.zone_id = data['zone'].to_i self.zone_id = data['zone'].to_i
self.url = "#{NEOPETS_ASSET_SERVER}/#{data['url']}" self.url = "#{NEOPETS_ASSET_SERVER}/#{data['url']}"
end end
before_create do before_create do
uri = URI.parse url uri = URI.parse url
begin begin
@ -105,17 +110,17 @@ class SwfAsset < ActiveRecord::Base
end end
end end
end end
before_save do before_save do
# If an asset body ID changes, that means more than one body ID has been # If an asset body ID changes, that means more than one body ID has been
# linked to it, meaning that it's probably wearable by all bodies. # linked to it, meaning that it's probably wearable by all bodies.
self.body_id = 0 if !self.body_specific? || (!self.new_record? && self.body_id_changed?) self.body_id = 0 if !self.body_specific? || (!self.new_record? && self.body_id_changed?)
end end
class DownloadError < Exception;end class DownloadError < Exception;end
private private
def local_path_within_outfit_swfs def local_path_within_outfit_swfs
uri = URI.parse(url) uri = URI.parse(url)
pieces = uri.path.split('/') pieces = uri.path.split('/')
@ -124,3 +129,4 @@ class SwfAsset < ActiveRecord::Base
File.join(relevant_pieces) File.join(relevant_pieces)
end end
end end

View file

@ -25,7 +25,7 @@
%a#customize-more.button{:href => '/'} Customize more %a#customize-more.button{:href => '/'} Customize more
#item-preview #item-preview
#item-preview-species= standard_species_images(@item.supported_species) #item-preview-species= standard_species_images_for(@item)
#item-preview-error #item-preview-error
#item-preview-swf #item-preview-swf
Javascript and Flash are required to preview wearables. Sorry! Javascript and Flash are required to preview wearables. Sorry!
@ -36,3 +36,4 @@
= include_javascript_libraries :jquery, :swfobject = include_javascript_libraries :jquery, :swfobject
= javascript_include_tag 'items/show' = javascript_include_tag 'items/show'

View file

@ -1,270 +1,412 @@
---
acara: acara:
blue: mnbztxxn blue: mnbztxxn
green: obxdjm88 green: obxdjm88
red: 7njwqlq8 red: 7njwqlq8
yellow: o3wj6s77 yellow: o3wj6s77
baby: sk5z9r4n
maraquan: ntvxx7xc
mutant: zmofvhdx
aisha: aisha:
blue: n9ozx4z5 blue: n9ozx4z5
green: vw3j5rnt green: vw3j5rnt
red: r27dkxdc red: r27dkxdc
yellow: t4osnjxq yellow: t4osnjxq
baby: bon8m2o4
maraquan: 37wgjb6s
mutant: c4wrb4vd
blumaroo: blumaroo:
blue: 8dng4bvh blue: 8dng4bvh
green: xmqmg3m9 green: xmqmg3m9
red: j77lzlmx red: j77lzlmx
yellow: kfonqhdc yellow: kfonqhdc
baby: md7bjrss
maraquan: 82fzq4kh
mutant: jcxf8fwx
bori: bori:
blue: cdtgf7jo blue: cdtgf7jo
green: 6c8dvw76 green: 6c8dvw76
red: tg7hx55j red: tg7hx55j
yellow: sc2hhvhn yellow: sc2hhvhn
baby: fofhnsqj
maraquan: rln8zot7
mutant: 7lqvol7n
bruce: bruce:
blue: w3hljbx7 blue: w3hljbx7
green: gwgc67tt green: gwgc67tt
red: 72d26x2b red: 72d26x2b
yellow: wqz8xn4t yellow: wqz8xn4t
baby: kkkqd9hd
maraquan: oddhtbgs
mutant: b3jrdol9
buzz: buzz:
blue: rkvgtzv3 blue: rkvgtzv3
green: schfx5c4 green: schfx5c4
red: gddkkb5j red: gddkkb5j
yellow: jc9klfxm yellow: jc9klfxm
baby: kvxjk22f
maraquan: cm66xwqk
mutant: j9brxg4n
chia: chia:
blue: d65mc9gf blue: d65mc9gf
green: 8d4949sr green: 8d4949sr
red: 4lrb4n3f red: 4lrb4n3f
yellow: oqs3kzmf yellow: oqs3kzmf
baby: kvm8jr9k
mutant: mnf38rsr
chomby: chomby:
blue: h3qctoxg blue: h3qctoxg
green: 552mzx7r green: 552mzx7r
red: 9swsd8ln red: 9swsd8ln
yellow: bdml26md yellow: bdml26md
baby: v2l4d2nf
8-bit: 96lgkwnj
maraquan: v5x466sl
cybunny: cybunny:
blue: v8shwcoq blue: v8shwcoq
green: xl6msllv green: xl6msllv
red: lfkscvgd red: lfkscvgd
yellow: cwlv6zz9 yellow: cwlv6zz9
baby: h3lx4sw2
maraquan: 6zlzft6t
mutant: zzsonrg8
draik: draik:
blue: 2g4jtrfh blue: 2g4jtrfh
green: 438o4dv5 green: 438o4dv5
red: vd2joxxq red: vd2joxxq
yellow: bob39shq yellow: bob39shq
baby: h7g2lzvw
maraquan: mtxqzrjz
mutant: gq3dmvcf
elephante: elephante:
blue: 4ckkjxjj blue: 4ckkjxjj
green: r22hl9oh green: r22hl9oh
red: jhhhbrww red: jhhhbrww
yellow: jf9ww4om yellow: jf9ww4om
baby: gvj8c7rn
maraquan: gh3bofrd
mutant: fsvsz87k
eyrie: eyrie:
blue: m5mvxv23 blue: m5mvxv23
green: s42o6oon green: s42o6oon
red: 6kngmhvs red: 6kngmhvs
yellow: d6b8fqnm yellow: d6b8fqnm
baby: gqxnnsc4
maraquan: 4qxob8gs
mutant: dvxtocr7
flotsam: flotsam:
blue: hwdo7rlb blue: hwdo7rlb
green: 47vt32x2 green: 47vt32x2
red: xhob45wn red: xhob45wn
yellow: r8r7jmvr yellow: r8r7jmvr
baby: 84jvz79s
mutant: wj83k9kh
gelert: gelert:
blue: 4g7n9mh5 blue: 4g7n9mh5
green: jfmwlmk8 green: jfmwlmk8
red: l95vq4w2 red: l95vq4w2
yellow: 5nrd2lvd yellow: 5nrd2lvd
baby: c473f4o7
maraquan: 3dzw2l8c
mutant: 9ldnhtw5
gnorbu: gnorbu:
blue: 6c275jcg blue: 6c275jcg
green: ghfgo5tn green: ghfgo5tn
red: qozzjgmg red: qozzjgmg
yellow: 5lrvftfb yellow: 5lrvftfb
baby: fzjt34mc
mutant: bqgwjvq2
grarrl: grarrl:
blue: j7q65fv4 blue: j7q65fv4
green: t3mstkl6 green: t3mstkl6
red: r946sq53 red: r946sq53
yellow: jtg67z98 yellow: jtg67z98
baby: g66gs7m7
maraquan: 3dldqlj2
mutant: 8jtbfojx
grundo: grundo:
blue: nwx8v2rb blue: nwx8v2rb
green: 5xn4kjf8 green: 5xn4kjf8
red: qjcb6t8x red: qjcb6t8x
yellow: n2g9d94f yellow: n2g9d94f
baby: 4sqhoksn
maraquan: 8coxbn7v
mutant: 9tfsvdk7
hissi: hissi:
blue: 7ls55f33 blue: 7ls55f33
green: dz5dwsbx green: dz5dwsbx
red: jsfvcqwt red: jsfvcqwt
yellow: z24m7h7l yellow: z24m7h7l
baby: fwd63758
maraquan: z5dw7ln7
mutant: xx8g8jjb
ixi: ixi:
blue: ro3qcd6s blue: ro3qcd6s
green: w32r74vo green: w32r74vo
red: zs2m6862 red: zs2m6862
yellow: oggkzvq7 yellow: oggkzvq7
baby: r6djxvxw
maraquan: xd73hdoz
mutant: scd2f73q
jetsam: jetsam:
blue: w3rl2k9n blue: w3rl2k9n
green: cwss4w3s green: cwss4w3s
red: ghddf93g red: ghddf93g
yellow: kz43rnld yellow: kz43rnld
baby: zhz7wlxg
mutant: m8n64bsr
jubjub: jubjub:
blue: 8mf5gzov blue: 8mf5gzov
green: m267j935 green: m267j935
red: szckt2tj red: szckt2tj
yellow: 78hdsnmw yellow: 78hdsnmw
baby: 92x2bdtq
maraquan: gj7dmvsd
mutant: 783vj5nt
kacheek: kacheek:
blue: tvnq2l5s blue: tvnq2l5s
green: fkzfcb47 green: fkzfcb47
red: 9hbtocjh red: 9hbtocjh
yellow: 4gsrb59g yellow: 4gsrb59g
baby: nw424vgn
mutant: fq6nnvwh
kau: kau:
blue: ktlxmrtr blue: ktlxmrtr
green: x9ww69qo green: x9ww69qo
red: mrdtwkto red: mrdtwkto
yellow: 78w49w7x yellow: 78w49w7x
baby: qtxmt64l
maraquan: r5fzvmtd
mutant: 39thxh84
kiko: kiko:
blue: 2qlfqqnd blue: 2qlfqqnd
green: 42j5q3zx green: 42j5q3zx
red: mcodrwlt red: mcodrwlt
yellow: 86b5xdn6 yellow: 86b5xdn6
baby: d54hfwc7
mutant: wdqnjz2z
koi: koi:
blue: tjvv5cq8 blue: tjvv5cq8
green: ncfn87wk green: ncfn87wk
red: f3wvzz6r red: f3wvzz6r
yellow: 4f6cvzgh yellow: 4f6cvzgh
baby: 59lh68wg
mutant: zoc4ntrg
korbat: korbat:
blue: 4qxwv7w7 blue: 4qxwv7w7
green: d2ow9co8 green: d2ow9co8
red: omx9c876 red: omx9c876
yellow: nsxodd8z yellow: nsxodd8z
baby: kgmnvlx8
maraquan: 6g73dr7l
mutant: lf98r4sb
kougra: kougra:
blue: rfsbh59t blue: rfsbh59t
green: otvq569n green: otvq569n
red: x8hsckbh red: x8hsckbh
yellow: x7bbg59h yellow: x7bbg59h
baby: 45dwtg2f
8-bit: 5g6fj4g8
maraquan: 4snffcf5
mutant: 5qt72kjo
krawk: krawk:
blue: hxgsm5d4 blue: hxgsm5d4
green: 7ngw8z4f green: 7ngw8z4f
red: kzwxb3dn red: kzwxb3dn
yellow: m2gq59r5 yellow: m2gq59r5
baby: zgscmthd
maraquan: kbmht6zh
mutant: 8m94bk5w
kyrii: kyrii:
blue: ojmo7qgg blue: ojmo7qgg
green: t3b8s9nz green: t3b8s9nz
red: 6fvg9ngs red: 6fvg9ngs
yellow: blxmjgbk yellow: blxmjgbk
baby: 42fokwff
maraquan: s2do4jhq
mutant: x25ml223
lenny: lenny:
blue: jfc75n44 blue: jfc75n44
green: 242k4kdq green: 242k4kdq
red: kc4w238d red: kc4w238d
yellow: 8r94jhfq yellow: 8r94jhfq
baby: k2qnttxo
mutant: 89zgj87k
lupe: lupe:
blue: r552fqj8 blue: r552fqj8
green: ohmx4lc9 green: ohmx4lc9
red: 6jwlghsg red: 6jwlghsg
yellow: z42535zh yellow: z42535zh
baby: b4xfn6xv
maraquan: fhlqw4bf
mutant: 7ggn4k6x
lutari: lutari:
blue: qgg6z8s7 blue: qgg6z8s7
green: wtjv7hw4 green: wtjv7hw4
red: lgws33oo red: lgws33oo
yellow: 8x37fgjf yellow: 8x37fgjf
baby: jnbjxvf5
meerca: meerca:
blue: wwwhjf88 blue: wwwhjf88
green: lg9bb6tf green: lg9bb6tf
red: v7g8w8w6 red: v7g8w8w6
yellow: kk2nn2jr yellow: kk2nn2jr
baby: 446k2fln
maraquan: kgw5wv42
mutant: n4nd3c4v
moehog: moehog:
blue: moot839l blue: moot839l
green: jgkoro5z green: jgkoro5z
red: dk47mfk8 red: dk47mfk8
yellow: g84ovct7 yellow: g84ovct7
baby: 5r3oq2k4
maraquan: 5cgov2km
mutant: wdc9gwtq
mynci: mynci:
blue: xwlo9657 blue: xwlo9657
green: s33qzokw green: s33qzokw
red: f828tkrc red: f828tkrc
yellow: tdfhsndr yellow: tdfhsndr
baby: 9d354534
maraquan: 7d2ll99r
mutant: bqtb9hsz
nimmo: nimmo:
blue: bx7fho8x blue: bx7fho8x
green: mb4mcwj5 green: mb4mcwj5
red: mok2fcl4 red: mok2fcl4
yellow: 9559onds yellow: 9559onds
baby: nwq55xx5
ogrin: ogrin:
blue: 26v2sx49 blue: 26v2sx49
green: lg5bzqkq green: lg5bzqkq
red: 3xgoz429 red: 3xgoz429
yellow: rjzmx24v yellow: rjzmx24v
baby: bfrh323n
mutant: t6s8hrzx
peophin: peophin:
blue: 996t7zfg blue: 996t7zfg
green: c9qo7zzt green: c9qo7zzt
red: kokc52kh red: kokc52kh
yellow: 4khvfnwl yellow: 4khvfnwl
baby: 4jbvhghx
mutant: bjdggzwd
poogle: poogle:
blue: 5n2vx7v2 blue: 5n2vx7v2
green: fw6lvf3c green: fw6lvf3c
red: xhcwbdd5 red: xhcwbdd5
yellow: xozlhd68 yellow: xozlhd68
baby: b2467jkz
maraquan: z44rk3xg
pteri: pteri:
blue: 82d369sm blue: 82d369sm
green: slqmqr78 green: slqmqr78
red: tjhwbro3 red: tjhwbro3
yellow: 4gkkb57f yellow: 4gkkb57f
baby: snd77sqq
maraquan: sktvv8t7
mutant: 57tg8jb8
quiggle: quiggle:
blue: 4zs6b32m blue: 4zs6b32m
green: vbrsgqrl green: vbrsgqrl
red: xrmt72wr red: xrmt72wr
yellow: jdto7mj4 yellow: jdto7mj4
baby: 2k7wm6dx
mutant: bv9htk44
ruki: ruki:
blue: qsgbm5f6 blue: qsgbm5f6
green: vgml65cz green: vgml65cz
red: sm55zs3q red: sm55zs3q
yellow: n43szv42 yellow: n43szv42
baby: grbwvh54
maraquan: kjx2jjcg
mutant: hsls3rd8
scorchio: scorchio:
blue: ohb3b486 blue: ohb3b486
green: kmb82xw8 green: kmb82xw8
red: hkjoncsx red: hkjoncsx
yellow: hgfofbl2 yellow: hgfofbl2
baby: n443j7hn
maraquan: bm9b2b73
mutant: 3mfckmq4
shoyru: shoyru:
blue: dj7n2zk8 blue: dj7n2zk8
green: 3qwwn4n2 green: 3qwwn4n2
red: 3zb2s2zw red: 3zb2s2zw
yellow: mmvn4tkg yellow: mmvn4tkg
baby: xvvxvvjt
maraquan: f57glqj6
mutant: 49tjr4vf
skeith: skeith:
blue: 7c847jkn blue: 7c847jkn
green: qdgwwz6c green: qdgwwz6c
red: fc4cxk3t red: fc4cxk3t
yellow: 533nc7rj yellow: 533nc7rj
baby: tjjsn83l
maraquan: o65f8j4f
mutant: jb8jhqqr
techo: techo:
blue: vl8qro4r blue: vl8qro4r
green: 2zc3vd47 green: 2zc3vd47
red: zgl2gjjn red: zgl2gjjn
yellow: 84gvowmj yellow: 84gvowmj
baby: ng83vkg7
maraquan: ln8lcbvh
mutant: tkqgzsv3
tonu: tonu:
blue: jd433863 blue: jd433863
green: 6mcv27mn green: 6mcv27mn
red: oskw3hqd red: oskw3hqd
yellow: 5bhqs7sv yellow: 5bhqs7sv
baby: h6jd7kw8
mutant: 59kr9c96
tuskaninny: tuskaninny:
blue: jj6t6cqd blue: jj6t6cqd
green: stl4mm78 green: stl4mm78
red: 48z4o75j red: 48z4o75j
yellow: q39wn6vq yellow: q39wn6vq
baby: c3vhmr2m
mutant: j2hob2sj
uni: uni:
blue: 74xvb24d blue: 74xvb24d
green: njzvoflw green: njzvoflw
red: jzfbdomk red: jzfbdomk
yellow: 5qdfrtcq yellow: 5qdfrtcq
baby: st84z8xf
maraquan: 3f3nzsjf
mutant: qw3gsfbb
usul: usul:
blue: xff976n8 blue: xff976n8
green: 5245lzmh green: 5245lzmh
red: rox4mgh5 red: rox4mgh5
yellow: vxn8b4vr yellow: vxn8b4vr
baby: 39vzk6nc
maraquan: 8sfjr77d
mutant: 3dgowzld
wocky: wocky:
blue: xonntr5f blue: xonntr5f
green: 4ssorlcs green: 4ssorlcs
red: 743smd5v red: 743smd5v
yellow: dnr2kj4b yellow: dnr2kj4b
baby: s7tld393
xweetok: xweetok:
blue: ddhdrt2o blue: ddhdrt2o
green: thzwbqsq green: thzwbqsq
red: tdkqr2b6 red: tdkqr2b6
yellow: o36btnbf yellow: o36btnbf
baby: wwh2wvkn
mutant: qs53ohzt
yurble: yurble:
blue: nhn5lxb4 blue: nhn5lxb4
green: mjgbr7vb green: mjgbr7vb
red: h95cs547 red: h95cs547
yellow: vfqkbvv6 yellow: vfqkbvv6
baby: 8b8fvxo2
mutant: 5o5t2j3s
zafara: zafara:
blue: x8c57g2l blue: x8c57g2l
green: tq56zj3x green: tq56zj3x
red: n32sgrvm red: n32sgrvm
yellow: onxr95x6 yellow: onxr95x6
baby: 93c22wgn
maraquan: b2gxlo5h
mutant: 9c9frk9g

View file

@ -0,0 +1,5 @@
8-Bit
Baby
Maraquan
Mutant