* Install Rspec * Create .aidigestignore * Update rails_helper - Added sections and comments - Add support for loading via canonical.rb - Add FactoryBot syntax methods - Disable SQL logging in test environment * Move gems around * Add canonical.rb and test env CSVs We load these CSVs via canonical.rb when we run tests as a data source for canonical objects. * Remove RBS for now This is too much and we need to find the right solution * Refactor GridSummonsController and add tests * Create GridSummon factory * Refactor GridSummon and add documentation and tests * Create have_error_on.rb * Update .aidigestignore * Fix warnings * Add GridWeapons and Parties factories * Refactor GridWeapon and add documentation and tests * Create .rubocop.yml * Create no_weapon_provided_error.rb * Refactor GridWeaponsController - Refactors controller - Adds YARD documentation - Adds Rspec tests * Refactor GridSummonsController - Refactors controller - Adds YARD documentation - Adds Rspec tests * Enable shoulda/matchers * Update User factory * Update party.rb We moved updating the party's element and extra flag to inside the party. We use an after_commit hook to minimize the amount of queries we're running to do this. * Update party.rb We change setting the edit key to use the conditional assignment operator so that it doesn't get overridden when we're running tests. This shouldn't have an effect in production. * Update api_controller.rb Change render_unprocessable_entity_response to render the errors hash instead of the exception so that we get more helpful errors. * Add new errors Added NoCharacterProvidedError and NoSummonProvidedError * Add tests and docs to GridCharacter We added a factory, spec and documentation to the GridCharacter model * Ensure numericality * Move enums into GranblueEnums We don't use these yet, but it gives us a structured place to pull them from. * Refactor GridCharactersController - Refactors controller - Adds YARD documentation - Adds Rspec tests * Add debug hook and other small changes * Update grid_characters_controller.rb Removes logs * Update .gitignore * Update .aidigestignore * Refactored PartiesController - Split PartiesController into three concerns - Implemented testing for PartiesController and two concerns - Implemented fixes across other files to ensure PartiesController tests pass - Added Favorites factory * Implement SimpleCov * Refactor Party model - Refactors Party model - Adds tests - Adds documentation * Update granblue_enums.rb Remove included block
154 lines
5.5 KiB
Ruby
154 lines
5.5 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
##
|
|
# Model representing a grid weapon within a party.
|
|
#
|
|
# This model associates a weapon with a party and manages validations for weapon compatibility,
|
|
# conflict detection, and attribute adjustments such as determining if a weapon is mainhand.
|
|
#
|
|
# @!attribute [r] weapon
|
|
# @return [Weapon] the associated weapon.
|
|
# @!attribute [r] party
|
|
# @return [Party] the party to which the grid weapon belongs.
|
|
# @!attribute [r] weapon_key1
|
|
# @return [WeaponKey, nil] the primary weapon key, if assigned.
|
|
# @!attribute [r] weapon_key2
|
|
# @return [WeaponKey, nil] the secondary weapon key, if assigned.
|
|
# @!attribute [r] weapon_key3
|
|
# @return [WeaponKey, nil] the tertiary weapon key, if assigned.
|
|
# @!attribute [r] weapon_key4
|
|
# @return [WeaponKey, nil] the quaternary weapon key, if assigned.
|
|
# @!attribute [r] awakening
|
|
# @return [Awakening, nil] the associated awakening, if any.
|
|
class GridWeapon < ApplicationRecord
|
|
# Allowed extra positions and allowed weapon series when in an extra position.
|
|
EXTRA_POSITIONS = [9, 10, 11].freeze
|
|
ALLOWED_EXTRA_SERIES = [11, 16, 17, 28, 29, 32, 34].freeze
|
|
|
|
belongs_to :weapon, foreign_key: :weapon_id, primary_key: :id
|
|
|
|
belongs_to :party,
|
|
counter_cache: :weapons_count,
|
|
inverse_of: :weapons
|
|
validates_presence_of :party
|
|
|
|
belongs_to :weapon_key1, class_name: 'WeaponKey', foreign_key: :weapon_key1_id, optional: true
|
|
belongs_to :weapon_key2, class_name: 'WeaponKey', foreign_key: :weapon_key2_id, optional: true
|
|
belongs_to :weapon_key3, class_name: 'WeaponKey', foreign_key: :weapon_key3_id, optional: true
|
|
belongs_to :weapon_key4, class_name: 'WeaponKey', foreign_key: :weapon_key4_id, optional: true
|
|
|
|
belongs_to :awakening, optional: true
|
|
|
|
# Validate that uncap_level and transcendence_step are present and numeric.
|
|
validates :uncap_level, presence: true, numericality: { only_integer: true }
|
|
validates :transcendence_step, presence: true, numericality: { only_integer: true }
|
|
|
|
validate :compatible_with_position, on: :create
|
|
validate :no_conflicts, on: :create
|
|
|
|
before_save :assign_mainhand
|
|
|
|
##### Amoeba configuration
|
|
amoeba do
|
|
nullify :ax_modifier1
|
|
nullify :ax_modifier2
|
|
nullify :ax_strength1
|
|
nullify :ax_strength2
|
|
end
|
|
|
|
##
|
|
# Returns the blueprint for rendering the grid weapon.
|
|
#
|
|
# @return [GridWeaponBlueprint] the blueprint class for grid weapons.
|
|
def blueprint
|
|
GridWeaponBlueprint
|
|
end
|
|
|
|
##
|
|
# Returns an array of assigned weapon keys.
|
|
#
|
|
# This method returns an array containing weapon_key1, weapon_key2, and weapon_key3,
|
|
# omitting any nil values.
|
|
#
|
|
# @return [Array<WeaponKey>] the non-nil weapon keys.
|
|
def weapon_keys
|
|
[weapon_key1, weapon_key2, weapon_key3].compact
|
|
end
|
|
|
|
##
|
|
# Returns conflicting grid weapons within a given party.
|
|
#
|
|
# Checks if the associated weapon is present, responds to a :limit method, and is limited.
|
|
# It then iterates over the party's grid weapons and selects those that conflict with this one,
|
|
# based on series matching or specific conditions related to opus or draconic status.
|
|
#
|
|
# @param party [Party] the party in which to check for conflicts.
|
|
# @return [ActiveRecord::Relation<GridWeapon>] an array of conflicting grid weapons (empty if none are found).
|
|
def conflicts(party)
|
|
return [] unless weapon.present? && weapon.respond_to?(:limit) && weapon.limit
|
|
|
|
party.weapons.select do |party_weapon|
|
|
# Skip if the record is not persisted.
|
|
next false unless party_weapon.id.present?
|
|
|
|
id_match = weapon.id == party_weapon.id
|
|
series_match = weapon.series == party_weapon.weapon.series
|
|
both_opus_or_draconic = weapon.opus_or_draconic? && party_weapon.weapon.opus_or_draconic?
|
|
both_draconic = weapon.draconic_or_providence? && party_weapon.weapon.draconic_or_providence?
|
|
|
|
(series_match || both_opus_or_draconic || both_draconic) && !id_match
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
##
|
|
# Validates whether the grid weapon is compatible with the desired position.
|
|
#
|
|
# For positions 9, 10, or 11 (considered extra positions), the weapon's series must belong to the allowed set.
|
|
# If the weapon is in an extra position but does not match an allowed series, an error is added.
|
|
#
|
|
# @return [void]
|
|
def compatible_with_position
|
|
return unless weapon.present?
|
|
|
|
if EXTRA_POSITIONS.include?(position.to_i) && !ALLOWED_EXTRA_SERIES.include?(weapon.series.to_i)
|
|
errors.add(:series, 'must be compatible with position')
|
|
end
|
|
end
|
|
|
|
##
|
|
# Validates that the assigned weapon keys are compatible with the weapon.
|
|
#
|
|
# Iterates over each non-nil weapon key and checks compatibility using the weapon's
|
|
# `compatible_with_key?` method. An error is added for any key that is not compatible.
|
|
#
|
|
# @return [void]
|
|
def compatible_with_key
|
|
weapon_keys.each do |key|
|
|
errors.add(:weapon_keys, 'must be compatible with weapon') unless weapon.compatible_with_key?(key)
|
|
end
|
|
end
|
|
|
|
##
|
|
# Validates that there are no conflicting grid weapons in the party.
|
|
#
|
|
# Checks if the current grid weapon conflicts with any other grid weapons within the party.
|
|
# If conflicting weapons are found, an error is added.
|
|
#
|
|
# @return [void]
|
|
def no_conflicts
|
|
conflicting = conflicts(party)
|
|
errors.add(:series, 'must not conflict with existing weapons') if conflicting.any?
|
|
end
|
|
|
|
##
|
|
# Determines if the grid weapon should be marked as mainhand based on its position.
|
|
#
|
|
# If the grid weapon's position is -1, sets the `mainhand` attribute to true.
|
|
#
|
|
# @return [void]
|
|
def assign_mainhand
|
|
self.mainhand = (position == -1)
|
|
end
|
|
end
|