- preview_sync endpoint shows what'll get deleted before you commit - import services handle reconciliation (find missing items, delete them) - grid items get flagged as orphaned when their collection source is gone - party exposes has_orphaned_items - blueprints include orphaned field
144 lines
No EOL
4.8 KiB
Ruby
144 lines
No EOL
4.8 KiB
Ruby
class CollectionWeapon < ApplicationRecord
|
|
belongs_to :user
|
|
belongs_to :weapon
|
|
belongs_to :awakening, optional: true
|
|
|
|
belongs_to :weapon_key1, class_name: 'WeaponKey', optional: true
|
|
belongs_to :weapon_key2, class_name: 'WeaponKey', optional: true
|
|
belongs_to :weapon_key3, class_name: 'WeaponKey', optional: true
|
|
belongs_to :weapon_key4, class_name: 'WeaponKey', optional: true
|
|
|
|
has_many :grid_weapons, dependent: :nullify
|
|
|
|
before_destroy :orphan_grid_items
|
|
|
|
# Set defaults before validation so database defaults don't cause validation failures
|
|
attribute :awakening_level, :integer, default: 1
|
|
|
|
validates :uncap_level, inclusion: { in: 0..5 }
|
|
validates :transcendence_step, inclusion: { in: 0..10 }
|
|
validates :awakening_level, inclusion: { in: 1..20 }
|
|
|
|
validate :validate_weapon_keys
|
|
validate :validate_ax_skills
|
|
validate :validate_element_change
|
|
validate :validate_awakening_compatibility
|
|
validate :validate_awakening_level
|
|
validate :validate_transcendence_requirements
|
|
|
|
scope :by_weapon, ->(weapon_id) { where(weapon_id: weapon_id) }
|
|
scope :by_series, ->(series_id) { joins(:weapon).where(weapons: { weapon_series_id: series_id }) }
|
|
scope :with_keys, -> { where.not(weapon_key1_id: nil) }
|
|
scope :with_ax, -> { where.not(ax_modifier1: nil) }
|
|
scope :by_element, ->(element) { joins(:weapon).where(weapons: { element: element }) }
|
|
scope :by_rarity, ->(rarity) { joins(:weapon).where(weapons: { rarity: rarity }) }
|
|
scope :by_proficiency, ->(proficiency) { joins(:weapon).where(weapons: { proficiency: proficiency }) }
|
|
scope :transcended, -> { where('transcendence_step > 0') }
|
|
scope :with_awakening, -> { where.not(awakening_id: nil) }
|
|
|
|
scope :sorted_by, ->(sort_key) {
|
|
case sort_key
|
|
when 'name_asc'
|
|
joins(:weapon).order('weapons.name_en ASC NULLS LAST')
|
|
when 'name_desc'
|
|
joins(:weapon).order('weapons.name_en DESC NULLS LAST')
|
|
when 'element_asc'
|
|
joins(:weapon).order('weapons.element ASC')
|
|
when 'element_desc'
|
|
joins(:weapon).order('weapons.element DESC')
|
|
when 'proficiency_asc'
|
|
joins(:weapon).order('weapons.proficiency ASC')
|
|
when 'proficiency_desc'
|
|
joins(:weapon).order('weapons.proficiency DESC')
|
|
else
|
|
order(created_at: :desc)
|
|
end
|
|
}
|
|
|
|
def blueprint
|
|
Api::V1::CollectionWeaponBlueprint
|
|
end
|
|
|
|
def weapon_keys
|
|
[weapon_key1, weapon_key2, weapon_key3, weapon_key4].compact
|
|
end
|
|
|
|
private
|
|
|
|
def validate_weapon_keys
|
|
return unless weapon.present?
|
|
|
|
# Validate weapon_key4 is only on Opus/Draconic weapons
|
|
if weapon_key4.present? && !weapon.opus_or_draconic?
|
|
errors.add(:weapon_key4, "can only be set on Opus or Draconic weapons")
|
|
end
|
|
|
|
weapon_keys.each do |key|
|
|
unless weapon.compatible_with_key?(key)
|
|
errors.add(:weapon_keys, "#{key.name_en} is not compatible with this weapon")
|
|
end
|
|
end
|
|
|
|
# Check for duplicate keys
|
|
key_ids = [weapon_key1_id, weapon_key2_id, weapon_key3_id, weapon_key4_id].compact
|
|
if key_ids.length != key_ids.uniq.length
|
|
errors.add(:weapon_keys, "cannot have duplicate keys")
|
|
end
|
|
end
|
|
|
|
def validate_ax_skills
|
|
# Check for incomplete AX skills regardless of weapon.ax
|
|
if (ax_modifier1.present? && ax_strength1.blank?) ||
|
|
(ax_modifier1.blank? && ax_strength1.present?)
|
|
errors.add(:base, "AX skill 1 must have both modifier and strength")
|
|
end
|
|
|
|
if (ax_modifier2.present? && ax_strength2.blank?) ||
|
|
(ax_modifier2.blank? && ax_strength2.present?)
|
|
errors.add(:base, "AX skill 2 must have both modifier and strength")
|
|
end
|
|
end
|
|
|
|
def validate_element_change
|
|
return unless element.present? && weapon.present?
|
|
|
|
unless Weapon.element_changeable?(weapon)
|
|
errors.add(:element, "can only be set on element-changeable weapons")
|
|
end
|
|
end
|
|
|
|
def validate_awakening_compatibility
|
|
return unless awakening.present?
|
|
|
|
unless awakening.object_type == 'Weapon'
|
|
errors.add(:awakening, "must be a weapon awakening")
|
|
end
|
|
end
|
|
|
|
def validate_awakening_level
|
|
if awakening_level.present? && awakening_level > 1 && awakening_id.blank?
|
|
errors.add(:awakening_level, "cannot be set without an awakening")
|
|
end
|
|
end
|
|
|
|
def validate_transcendence_requirements
|
|
return unless transcendence_step.present? && transcendence_step > 0
|
|
|
|
if uncap_level < 5
|
|
errors.add(:transcendence_step, "requires uncap level 5 (current: #{uncap_level})")
|
|
end
|
|
|
|
# Some weapons might not support transcendence
|
|
if weapon.present? && !weapon.transcendence
|
|
errors.add(:transcendence_step, "not available for this weapon") if transcendence_step > 0
|
|
end
|
|
end
|
|
|
|
##
|
|
# Marks all linked grid weapons as orphaned before destroying this collection weapon.
|
|
#
|
|
# @return [void]
|
|
def orphan_grid_items
|
|
grid_weapons.update_all(orphaned: true, collection_weapon_id: nil)
|
|
end
|
|
end |