class Roulette attr_accessor :pet_type, :pet_state, :item_ids def initialize # Choose a random pet type, and get a random state @pet_type = PetType.offset(rand(PetType.count)).first # random pet type states = @pet_type.pet_states @pet_state = states.offset(rand(states.count)).first fitting_swf_assets = SwfAsset.object_assets.fitting_body_id(@pet_type.body_id) # Keep going until we have all zones occupied. Find a random SWF asset for # a given unoccupied zone, then look up its item to see if it affects or # restricts zones we've already used. If so, try again. If not, add it to # the list. unoccupied_zone_ids = Zone.all.map(&:id) swf_asset_count_by_zone_id = fitting_swf_assets.count(:group => :zone_id) @item_ids = [] while !unoccupied_zone_ids.empty? zone_id = unoccupied_zone_ids.slice!(rand(unoccupied_zone_ids.count)) if swf_asset_count = swf_asset_count_by_zone_id[zone_id] used_swf_asset_ids = [] first_asset = true found_item = false while !found_item && used_swf_asset_ids.size < swf_asset_count base = fitting_swf_assets unless first_asset first_asset = false base = base.where(fitting_swf_assets.arel_table[:id].not_in(used_swf_asset_ids)) end swf_asset = base. where(:zone_id => zone_id). offset(rand(swf_asset_count)). includes(:object_asset_relationships => :item). first used_swf_asset_ids.push(swf_asset.id) swf_asset.object_asset_relationships.each do |rel| item = rel.item pass = true item.affected_zones.each do |zone| checked_zone_id = zone.id next if checked_zone_id == zone_id if i = unoccupied_zone_ids.find_index(zone_id) unoccupied_zone_ids.delete zone_id else # This zone is already occupied, so this item is no good. pass = false end end if pass found_item = true @item_ids << item.id break end end break if found_item end end end end def to_query { :color => @pet_type.color_id, :species => @pet_type.species_id, :state => @pet_state.id, :objects => @item_ids }.to_query end end