* Update test csvs * Fix count filters and refactor apply_filters * Update party_querying_concern.rb * +tests/-debug logs * Make party association optional in Job * Updates for weapon series - Change to new series numbers - Add static method for querying whether the weapon's element is changeable - Add a new method to return a text slug for the weapon's series * Add and update test data - Updates canonical.rb for loading multiple types of data with multiple types of associations - Adds test data for Guidebooks, Job Accessories, Job Skills, and Jobs - Updates test data for Weapons and Summons * Migrations - Adds series of migrations for changing the weapon's series to the values used by Cygames - Shuffled around some foreign keys * Implement BaseProcessor Processors are in charge of processing deck data straight from Granblue. * Implement CharacterProcessor Process character data from deck * Implement WeaponProcessor Process weapon data from deck * Implement JobProcessor Process job, job skill, and job accessory data from deck * Implement SummonProcessor Process summon data from deck * Update SummonProcessor to work like the others * ImportController should use processors * Process element for changeable weapons
201 lines
6.3 KiB
Ruby
201 lines
6.3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Processors
|
|
##
|
|
# SummonProcessor processes an array of summon data and creates GridSummon records.
|
|
# It handles different summon types based on the +type+ parameter:
|
|
# - :normal => standard summons
|
|
# - :friend => friend summon (fixed position and uncap logic)
|
|
# - :sub => sub summons (position based on order)
|
|
#
|
|
# @example
|
|
# normal_processor = SummonProcessor.new(party, summons_array, :normal, quick_summon_id)
|
|
# normal_processor.process
|
|
#
|
|
# friend_processor = SummonProcessor.new(party, [friend_summon_name], :friend)
|
|
# friend_processor.process
|
|
class SummonProcessor < BaseProcessor
|
|
TRANSCENDENCE_LEVELS = [200, 210, 220, 230, 240, 250].freeze
|
|
|
|
##
|
|
# Initializes a new SummonProcessor.
|
|
#
|
|
# @param party [Party] the Party record.
|
|
# @param data [Hash] the deck hash.
|
|
# @param type [Symbol] the type of summon (:normal, :friend, or :sub).
|
|
# @param quick_summon_id [String, nil] (optional) the quick summon identifier.
|
|
# @param options [Hash] additional options.
|
|
def initialize(party, data, type = :normal, options = {})
|
|
super(party, data, options)
|
|
@party = party
|
|
@data = data
|
|
@type = type
|
|
end
|
|
|
|
##
|
|
# Processes summon data and creates GridSummon records.
|
|
#
|
|
# @return [void]
|
|
def process
|
|
unless @data.is_a?(Hash)
|
|
Rails.logger.error "[SUMMON] Invalid data format: expected a Hash, got #{@data.class}"
|
|
return
|
|
end
|
|
|
|
unless @data.key?('deck') && @data['deck'].key?('pc')
|
|
Rails.logger.error '[SUMMON] Missing npc data in deck JSON'
|
|
return
|
|
end
|
|
|
|
@data = @data.with_indifferent_access
|
|
summons_data = @data.dig('deck', 'pc', 'summons')
|
|
sub_summons_data = @data.dig('deck', 'pc', 'sub_summons')
|
|
|
|
grid_summons = process_summons(summons_data, sub: false)
|
|
friend_summon = process_friend_summon
|
|
sub_summons = process_summons(sub_summons_data, sub: true)
|
|
|
|
summons = [*grid_summons, friend_summon, *sub_summons]
|
|
|
|
summons.each do |summon|
|
|
summon.save!
|
|
rescue ActiveRecord::RecordInvalid => e
|
|
Rails.logger.error "[SUMMON] Failed to create GridSummon: #{e.record.errors.full_messages.join(', ')}"
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
attr_reader :type
|
|
|
|
##
|
|
# Processes a set of summon data and creates GridSummon records.
|
|
#
|
|
# @param summons [Hash] the summon data
|
|
# @param sub [Boolean] true if we are polling sub summons
|
|
# @return [Array<GridSummon>]
|
|
def process_summons(summons, sub: false)
|
|
internal_quick_summon_id = @data['quick_user_summon_id'].to_i if sub
|
|
|
|
summons.map do |key, raw_summon|
|
|
summon_params = raw_summon['param']
|
|
summon_id = raw_summon['master']['id']
|
|
summon = Summon.find_by(granblue_id: transform_id(summon_id))
|
|
|
|
position = if sub
|
|
key.to_i + 4
|
|
else
|
|
key.to_i == 1 ? -1 : key.to_i - 2
|
|
end
|
|
|
|
GridSummon.new({
|
|
party: @party,
|
|
summon: summon,
|
|
position: position,
|
|
main: key.to_i == 1,
|
|
friend: false,
|
|
quick_summon: summon_params['id'].to_i == internal_quick_summon_id,
|
|
uncap_level: summon_params['evolution'].to_i,
|
|
transcendence_step: level_to_transcendence(summon_params['level'].to_i),
|
|
created_at: Time.now,
|
|
updated_at: Time.now
|
|
})
|
|
end
|
|
end
|
|
|
|
##
|
|
# Processes friend summon data and creates a GridSummon record.
|
|
#
|
|
# @return [GridSummon]
|
|
def process_friend_summon
|
|
summon_name = @data.dig('deck', 'pc', 'damage_info', 'summon_name')
|
|
summon = Summon.find_by('name_en = ? OR name_jp = ?', summon_name, summon_name)
|
|
|
|
GridSummon.new({
|
|
party: @party,
|
|
summon: summon,
|
|
position: 4,
|
|
main: false,
|
|
friend: true,
|
|
quick_summon: false,
|
|
uncap_level: determine_uncap_level(summon),
|
|
transcendence_step: summon.transcendence ? 5 : 0,
|
|
created_at: Time.now,
|
|
updated_at: Time.now
|
|
})
|
|
end
|
|
|
|
##
|
|
# Determines the numeric uncap level of a given Summon
|
|
#
|
|
# @param summon [Summon] the canonical summon
|
|
# @return [Integer]
|
|
def determine_uncap_level(summon)
|
|
if summon.transcendence
|
|
6
|
|
elsif summon.ulb
|
|
5
|
|
elsif summon.flb
|
|
4
|
|
else
|
|
3
|
|
end
|
|
end
|
|
|
|
##
|
|
# Determines the uncap level for a friend summon based on its ULb and FLb flags.
|
|
#
|
|
# @param summon_data [Hash] the summon data.
|
|
# @return [Integer] the computed uncap level.
|
|
def determine_friend_uncap(summon_data)
|
|
if summon_data[:ulb]
|
|
5
|
|
elsif summon_data[:flb]
|
|
4
|
|
else
|
|
3
|
|
end
|
|
end
|
|
|
|
##
|
|
# Converts a given level, rounded down to the nearest 10,
|
|
# to its corresponding transcendence step.
|
|
#
|
|
# If level is 200, returns 0; if level is 250, returns 5.
|
|
#
|
|
# @param level [Integer] the summon's level
|
|
# @return [Integer] the transcendence step
|
|
def level_to_transcendence(level)
|
|
return 0 if level < 200
|
|
|
|
floored_level = (level / 10).floor * 10
|
|
TRANSCENDENCE_LEVELS.index(floored_level)
|
|
end
|
|
|
|
##
|
|
# Transforms 5★ Arcarum-series summon IDs into their 4★ variants,
|
|
# as that's what is stored in the database.
|
|
#
|
|
# If an unrelated ID, or the 4★ ID is passed, then returns the input.
|
|
#
|
|
# @param id [String] the ID to match
|
|
# @return [String] the resulting ID
|
|
def transform_id(id)
|
|
mapping = {
|
|
'2040315000' => '2040238000',
|
|
'2040316000' => '2040239000',
|
|
'2040314000' => '2040237000',
|
|
'2040313000' => '2040236000',
|
|
'2040321000' => '2040244000',
|
|
'2040319000' => '2040242000',
|
|
'2040317000' => '2040240000',
|
|
'2040322000' => '2040245000',
|
|
'2040318000' => '2040241000',
|
|
'2040320000' => '2040243000'
|
|
}
|
|
|
|
# If the id is a key, return the mapped value; otherwise, return the id.
|
|
mapping[id] || id
|
|
end
|
|
end
|
|
end
|