diff --git a/app/models/character.rb b/app/models/character.rb index a8a610e..b1333ad 100644 --- a/app/models/character.rb +++ b/app/models/character.rb @@ -41,6 +41,30 @@ class Character < ApplicationRecord { slug: 'character-multi', name_en: 'Multiattack', name_jp: '連続攻撃', order: 3 } ].freeze + # Non-gachable series (characters that must be recruited, not pulled) + NON_GACHABLE_SERIES = [ + GranblueEnums::CHARACTER_SERIES[:Eternal], + GranblueEnums::CHARACTER_SERIES[:Evoker], + GranblueEnums::CHARACTER_SERIES[:Saint], + GranblueEnums::CHARACTER_SERIES[:Event], + GranblueEnums::CHARACTER_SERIES[:Collab] + ].freeze + + # Validations + validates :season, + numericality: { only_integer: true }, + inclusion: { in: GranblueEnums::CHARACTER_SEASONS.values }, + allow_nil: true + + validate :validate_series_values + + # Scopes + scope :by_season, ->(season) { where(season: season) } + scope :by_series, ->(series) { where('? = ANY(series)', series) } + scope :gachable, -> { where(gacha_available: true) } + scope :recruitable, -> { where(gacha_available: false) } + scope :seasonal, -> { where.not(season: [nil, GranblueEnums::CHARACTER_SEASONS[:Standard]]) } + def blueprint CharacterBlueprint end @@ -48,4 +72,36 @@ class Character < ApplicationRecord def display_resource(character) character.name_en end + + # Helper methods + def seasonal? + season.present? && season != GranblueEnums::CHARACTER_SEASONS[:Standard] + end + + def gachable? + gacha_available + end + + def season_name + return nil if season.nil? + + GranblueEnums::CHARACTER_SEASONS.key(season)&.to_s + end + + def series_names + return [] if series.blank? + + series.filter_map { |s| GranblueEnums::CHARACTER_SERIES.key(s)&.to_s } + end + + private + + def validate_series_values + return if series.blank? + + invalid_values = series.reject { |s| GranblueEnums::CHARACTER_SERIES.values.include?(s) } + return if invalid_values.empty? + + errors.add(:series, "contains invalid values: #{invalid_values.join(', ')}") + end end