Compare commits

...

16 commits

Author SHA1 Message Date
ed66784c1b Process element for changeable weapons 2025-02-17 23:46:31 -08:00
5b8fcdcfba ImportController should use processors 2025-02-17 23:38:53 -08:00
ebb3218c29 Update SummonProcessor to work like the others 2025-02-17 23:15:56 -08:00
e5ecfbd95f Implement SummonProcessor
Process summon data from deck
2025-02-17 22:54:21 -08:00
1dd8ae6159 Implement JobProcessor
Process job, job skill, and job accessory data from deck
2025-02-17 22:42:13 -08:00
e208e7daa9 Implement WeaponProcessor
Process weapon data from deck
2025-02-17 22:08:39 -08:00
3378e7114f Implement CharacterProcessor
Process character data from deck
2025-02-17 22:08:18 -08:00
d6ca8e8e90 Implement BaseProcessor
Processors are in charge of processing deck data straight from Granblue.
2025-02-17 22:07:24 -08:00
487c01bd07 Migrations
- Adds series of migrations for changing the weapon's series to the values used by Cygames
- Shuffled around some foreign keys
2025-02-17 20:35:55 -08:00
38f40d9269 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
2025-02-17 20:34:52 -08:00
15729ec7a8 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
2025-02-17 20:33:48 -08:00
d8eee21f99 Make party association optional in Job 2025-02-17 20:32:48 -08:00
699e467ad6 +tests/-debug logs 2025-02-13 22:27:20 -08:00
958d1f7ce4 Update party_querying_concern.rb 2025-02-13 22:27:06 -08:00
86287ec10a Fix count filters and refactor apply_filters 2025-02-13 22:27:01 -08:00
82fa291398 Update test csvs 2025-02-13 22:26:48 -08:00
38 changed files with 7634 additions and 321 deletions

View file

@ -2,6 +2,20 @@
module Api
module V1
##
# ImportController is responsible for importing game data (e.g. deck data)
# and creating a new Party along with associated records (job, characters, weapons, summons, etc.).
#
# The controller expects a JSON payload whose top-level key is "import". If not wrapped,
# the controller will wrap the raw data automatically.
#
# @example Valid payload structure
# {
# "import": {
# "deck": { "name": "My Party", ... },
# "pc": { "job": { "master": { "name": "Warrior" } }, ... }
# }
# }
class ImportController < Api::V1::ApiController
ELEMENT_MAPPING = {
0 => nil,
@ -13,263 +27,84 @@ module Api
6 => 5
}.freeze
##
# Processes an import request.
#
# It reads and parses the raw JSON, wraps the data under the "import" key if necessary,
# transforms the deck data using BaseDeckTransformer, validates that the transformed data
# contains required fields, and then creates a new Party record (and its associated objects)
# inside a transaction.
#
# @return [void] Renders JSON response with a party shortcode or an error message.
def create
Rails.logger.info "[IMPORT] Starting import..."
Rails.logger.info '[IMPORT] Checking input...'
# Parse JSON request body
raw_body = request.raw_post
begin
raw_params = JSON.parse(raw_body) if raw_body.present?
Rails.logger.info "[IMPORT] Raw game data: #{raw_params.inspect}"
rescue JSON::ParserError => e
Rails.logger.error "[IMPORT] Invalid JSON in request body: #{e.message}"
render json: { error: 'Invalid JSON data' }, status: :bad_request
return
body = parse_request_body
return unless body
raw_params = body['import']
unless raw_params.is_a?(Hash)
Rails.logger.error "[IMPORT] 'import' key is missing or not a hash."
return render json: { error: 'Invalid JSON data' }, status: :unprocessable_content
end
if raw_params.nil? || !raw_params.is_a?(Hash)
Rails.logger.error "[IMPORT] Missing or invalid game data"
render json: { error: 'Missing or invalid game data' }, status: :bad_request
return
unless raw_params['deck'].is_a?(Hash) &&
raw_params['deck'].key?('pc') &&
raw_params['deck'].key?('npc')
Rails.logger.error "[IMPORT] Deck data incomplete or missing."
return render json: { error: 'Invalid deck data' }, status: :unprocessable_content
end
# Transform game data
transformer = ::Granblue::Transformers::BaseDeckTransformer.new(raw_params)
transformed_data = transformer.transform
Rails.logger.info "[IMPORT] Transformed data: #{transformed_data.inspect}"
Rails.logger.info '[IMPORT] Starting import...'
# Validate transformed data
unless transformed_data[:name].present? && transformed_data[:lang].present?
Rails.logger.error "[IMPORT] Missing required fields in transformed data"
render json: { error: 'Missing required fields name or lang' }, status: :unprocessable_entity
return
end
return if performed? # Rendered an error response already
# Create party
party = Party.new(user: current_user)
party = Party.create(user: current_user)
deck_data = raw_params['import']
process_data(party, deck_data)
ActiveRecord::Base.transaction do
# Basic party data
party.name = transformed_data[:name]
party.extra = transformed_data[:extra]
party.save!
# Process job and skills
if transformed_data[:class].present?
process_job(party, transformed_data[:class], transformed_data[:subskills])
end
# Process characters
if transformed_data[:characters].present?
process_characters(party, transformed_data[:characters])
end
# Process weapons
if transformed_data[:weapons].present?
process_weapons(party, transformed_data[:weapons])
end
# Process summons
if transformed_data[:summons].present?
process_summons(party, transformed_data[:summons], transformed_data[:friend_summon])
end
# Process sub summons
if transformed_data[:sub_summons].present?
process_sub_summons(party, transformed_data[:sub_summons])
end
end
# Return shortcode for redirection
render json: { shortcode: party.shortcode }, status: :created
rescue StandardError => e
Rails.logger.error "[IMPORT] Error processing import: #{e.message}"
Rails.logger.error "[IMPORT] Backtrace: #{e.backtrace.join("\n")}"
render json: { error: 'Error processing import' }, status: :unprocessable_entity
render json: { error: e.message }, status: :unprocessable_content
end
private
def process_job(party, job_name, subskills)
return unless job_name
job = Job.find_by("name_en = ? OR name_jp = ?", job_name, job_name)
unless job
Rails.logger.warn "[IMPORT] Could not find job: #{job_name}"
return
end
party.job = job
party.save!
Rails.logger.info "[IMPORT] Assigned job=#{job_name} to party_id=#{party.id}"
return unless subskills&.any?
subskills.each_with_index do |skill_name, idx|
next if skill_name.blank?
skill = JobSkill.find_by("(name_en = ? OR name_jp = ?) AND job_id = ?", skill_name, skill_name, job.id)
unless skill
Rails.logger.warn "[IMPORT] Could not find skill=#{skill_name} for job_id=#{job.id}"
next
end
party["skill#{idx + 1}_id"] = skill.id
Rails.logger.info "[IMPORT] Assigned skill=#{skill_name} at position #{idx + 1}"
end
##
# Reads and parses the raw JSON request body.
#
# @return [Hash] Parsed JSON data.
# @raise [JSON::ParserError] If the JSON is invalid.
def parse_request_body
raw_body = request.raw_post
JSON.parse(raw_body)
rescue JSON::ParserError => e
Rails.logger.error "[IMPORT] Invalid JSON: #{e.message}"
render json: { error: 'Invalid JSON data' }, status: :bad_request and return
end
def process_characters(party, characters)
return unless characters&.any?
Rails.logger.info "[IMPORT] Processing #{characters.length} characters"
characters.each_with_index do |char_data, idx|
character = Character.find_by(granblue_id: char_data[:id])
unless character
Rails.logger.warn "[IMPORT] Character not found: #{char_data[:id]}"
next
end
GridCharacter.create!(
party: party,
character_id: character.id,
position: idx,
uncap_level: char_data[:uncap],
perpetuity: char_data[:ringed] || false,
transcendence_step: char_data[:transcend] || 0
)
Rails.logger.info "[IMPORT] Added character: #{character.name_en} at position #{idx}"
end
##
# Ensures that the provided data is wrapped under an "import" key.
#
# @param data [Hash] The parsed JSON data.
# @return [Hash] Data wrapped under the "import" key.
def wrap_import_data(data)
data.key?('import') ? data : { 'import' => data }
end
def process_weapons(party, weapons)
return unless weapons&.any?
Rails.logger.info "[IMPORT] Processing #{weapons.length} weapons"
##
# Processes the deck data using processors.
#
# @param party [Party] The party to insert data into
# @param data [Hash] The wrapped data.
# @return [Hash] The transformed deck data.
def process_data(party, data)
Rails.logger.info '[IMPORT] Transforming deck data'
weapons.each_with_index do |weapon_data, idx|
weapon = Weapon.find_by(granblue_id: weapon_data[:id])
unless weapon
Rails.logger.warn "[IMPORT] Weapon not found: #{weapon_data[:id]}"
next
end
grid_weapon = GridWeapon.create!(
party: party,
weapon_id: weapon.id,
position: idx - 1,
mainhand: idx.zero?,
uncap_level: weapon_data[:uncap],
transcendence_step: weapon_data[:transcend] || 0,
element: weapon_data[:attr] ? ELEMENT_MAPPING[weapon_data[:attr]] : nil
)
process_weapon_keys(grid_weapon, weapon_data[:keys]) if weapon_data[:keys]
process_weapon_ax(grid_weapon, weapon_data[:ax]) if weapon_data[:ax]
Rails.logger.info "[IMPORT] Added weapon: #{weapon.name_en} at position #{idx - 1}"
end
end
def process_weapon_keys(grid_weapon, keys)
keys.each_with_index do |key_id, idx|
key = WeaponKey.find_by(granblue_id: key_id)
unless key
Rails.logger.warn "[IMPORT] WeaponKey not found: #{key_id}"
next
end
grid_weapon["weapon_key#{idx + 1}_id"] = key.id
grid_weapon.save!
end
end
def process_weapon_ax(grid_weapon, ax_skills)
ax_skills.each_with_index do |ax, idx|
grid_weapon["ax_modifier#{idx + 1}"] = ax[:id].to_i
grid_weapon["ax_strength#{idx + 1}"] = ax[:val].to_s.gsub(/[+%]/, '').to_i
end
grid_weapon.save!
end
def process_summons(party, summons, friend_summon = nil)
return unless summons&.any?
Rails.logger.info "[IMPORT] Processing #{summons.length} summons"
# Main and sub summons
summons.each_with_index do |summon_data, idx|
summon = Summon.find_by(granblue_id: summon_data[:id])
unless summon
Rails.logger.warn "[IMPORT] Summon not found: #{summon_data[:id]}"
next
end
grid_summon = GridSummon.new(
party: party,
summon_id: summon.id,
position: idx,
main: idx.zero?,
friend: false,
uncap_level: summon_data[:uncap],
transcendence_step: summon_data[:transcend] || 0,
quick_summon: summon_data[:qs] || false
)
if grid_summon.save
Rails.logger.info "[IMPORT] Added summon: #{summon.name_en} at position #{idx}"
else
Rails.logger.error "[IMPORT] Failed to save summon: #{grid_summon.errors.full_messages}"
end
end
# Friend summon if provided
process_friend_summon(party, friend_summon) if friend_summon.present?
end
def process_friend_summon(party, friend_summon)
friend = Summon.find_by("name_en = ? OR name_jp = ?", friend_summon, friend_summon)
unless friend
Rails.logger.warn "[IMPORT] Friend summon not found: #{friend_summon}"
return
end
grid_summon = GridSummon.new(
party: party,
summon_id: friend.id,
position: 6,
main: false,
friend: true,
uncap_level: friend.ulb ? 5 : (friend.flb ? 4 : 3)
)
if grid_summon.save
Rails.logger.info "[IMPORT] Added friend summon: #{friend.name_en}"
else
Rails.logger.error "[IMPORT] Failed to save friend summon: #{grid_summon.errors.full_messages}"
end
end
def process_sub_summons(party, sub_summons)
return unless sub_summons&.any?
Rails.logger.info "[IMPORT] Processing #{sub_summons.length} sub summons"
sub_summons.each_with_index do |summon_data, idx|
summon = Summon.find_by(granblue_id: summon_data[:id])
unless summon
Rails.logger.warn "[IMPORT] Sub summon not found: #{summon_data[:id]}"
next
end
grid_summon = GridSummon.new(
party: party,
summon_id: summon.id,
position: idx + 5,
main: false,
friend: false,
uncap_level: summon_data[:uncap],
transcendence_step: summon_data[:transcend] || 0
)
if grid_summon.save
Rails.logger.info "[IMPORT] Added sub summon: #{summon.name_en} at position #{idx + 5}"
else
Rails.logger.error "[IMPORT] Failed to save sub summon: #{grid_summon.errors.full_messages}"
end
end
Processors::JobProcessor.new(party, data).process
Processors::CharacterProcessor.new(party, data).process
Processors::SummonProcessor.new(party, data).process
Processors::WeaponProcessor.new(party, data).process
end
end
end

View file

@ -25,7 +25,10 @@ module PartyQueryingConcern
# Uses PartyQueryBuilder to apply additional filters (includes, excludes, date ranges, etc.)
def build_filtered_query(base_query)
PartyQueryBuilder.new(base_query, params: params, current_user: current_user).build
PartyQueryBuilder.new(base_query,
params: params,
current_user: current_user,
options: { apply_defaults: true }).build
end
# Renders paginated parties using PartyBlueprint.

View file

@ -3,7 +3,7 @@
class Job < ApplicationRecord
include PgSearch::Model
belongs_to :party
belongs_to :party, optional: true
has_many :skills, class_name: 'JobSkill'
multisearchable against: %i[name_en name_jp],

View file

@ -37,6 +37,54 @@ class Weapon < ApplicationRecord
has_many :weapon_awakenings
has_many :awakenings, through: :weapon_awakenings
SERIES_SLUGS = {
1 => 'seraphic',
2 => 'grand',
3 => 'dark-opus',
4 => 'revenant',
5 => 'primal',
6 => 'beast',
7 => 'regalia',
8 => 'omega',
9 => 'olden-primal',
10 => 'hollowsky',
11 => 'xeno',
12 => 'rose',
13 => 'ultima',
14 => 'bahamut',
15 => 'epic',
16 => 'cosmos',
17 => 'superlative',
18 => 'vintage',
19 => 'class-champion',
20 => 'replica',
21 => 'relic',
22 => 'rusted',
23 => 'sephira',
24 => 'vyrmament',
25 => 'upgrader',
26 => 'astral',
27 => 'draconic',
28 => 'eternal-splendor',
29 => 'ancestral',
30 => 'new-world-foundation',
31 => 'ennead',
32 => 'militis',
33 => 'malice',
34 => 'menace',
35 => 'illustrious',
36 => 'proven',
37 => 'revans',
38 => 'world',
39 => 'exo',
40 => 'draconic-providence',
41 => 'celestial',
42 => 'omega-rebirth',
43 => 'collab',
98 => 'event',
99 => 'gacha'
}.freeze
def blueprint
WeaponBlueprint
end
@ -51,11 +99,23 @@ class Weapon < ApplicationRecord
# Returns whether the weapon is included in the Draconic or Dark Opus series
def opus_or_draconic?
[2, 3].include?(series)
[3, 27].include?(series)
end
# Returns whether the weapon belongs to the Draconic Weapon series or the Draconic Weapon Providence series
def draconic_or_providence?
[3, 34].include?(series)
[27, 40].include?(series)
end
def self.element_changeable?(series)
[4, 13, 17, 19].include?(series.to_i)
end
private
def series_slug
# Assuming series is an array, take the first value
series_number = series.first
SERIES_SLUGS[series_number]
end
end

View file

@ -46,15 +46,11 @@ class PartyQueryBuilder
# Example edge case: If the request does not specify 'characters_count',
# then the default (e.g. 3) will be used, with the upper bound coming from a constant.
def apply_filters(query)
conditions = build_filters
query = query.where(conditions)
# If name_quality filtering is enabled via params, apply it.
query = query.where(name_quality) if @params[:name_quality].present?
query.where(
weapons_count: build_count(@params[:weapons_count], default_weapons_count)..max_weapons,
characters_count: build_count(@params[:characters_count], default_characters_count)..max_characters,
summons_count: build_count(@params[:summons_count], default_summons_count)..max_summons
)
query = apply_base_filters(query)
query = apply_name_quality_filter(query)
query = apply_count_filters(query)
query
end
# Example callback method: if no explicit status filter is provided, we may want
@ -148,6 +144,63 @@ class PartyQueryBuilder
query
end
# Applies base filtering conditions from build_filters to the query.
# @param query [ActiveRecord::QueryMethods::WhereChain] The current query.
# @return [ActiveRecord::Relation] The query with base filters applied.
def apply_base_filters(query)
query.where(build_filters)
end
# Applies the name quality filter to the query if the parameter is present.
# @param query [ActiveRecord::QueryMethods::WhereChain] The current query.
# @return [ActiveRecord::Relation] The query with the name quality filter applied.
def apply_name_quality_filter(query)
@params[:name_quality].present? ? query.where(name_quality) : query
end
# Applies count filters to the query based on provided parameters or default options.
# If apply_defaults is set in options, default ranges are applied.
# Otherwise, count ranges are built from provided parameters.
# @param query [ | ActiveRecord::QueryMethods::WhereChain] The current query.
# @return [ActiveRecord::Relation] The query with count filters applied.
def apply_count_filters(query)
if @options[:apply_defaults]
query.where(
weapons_count: default_weapons_count..max_weapons,
characters_count: default_characters_count..max_characters,
summons_count: default_summons_count..max_summons
)
elsif count_filter_provided?
query.where(build_count_conditions)
else
query
end
end
# Determines if any count filter parameters have been provided.
# @return [Boolean] True if any count filters are provided, false otherwise.
def count_filter_provided?
@params.key?(:weapons_count) || @params.key?(:characters_count) || @params.key?(:summons_count)
end
# Builds a hash of count conditions based on the count filter parameters.
# @return [Hash] A hash with keys :weapons_count, :characters_count, and :summons_count.
def build_count_conditions
{
weapons_count: build_range(@params[:weapons_count], max_weapons),
characters_count: build_range(@params[:characters_count], max_characters),
summons_count: build_range(@params[:summons_count], max_summons)
}
end
# Constructs a range for a given count parameter.
# @param param_value [String, nil] The count filter parameter value.
# @param max_value [Integer] The maximum allowed value for the count.
# @return [Range] A range from the provided count (or 0 if blank) to the max_value.
def build_range(param_value, max_value)
param_value.present? ? param_value.to_i..max_value : 0..max_value
end
# Maps an IDs first character to the corresponding grid table and object table names.
#
# For example:
@ -170,27 +223,27 @@ class PartyQueryBuilder
# Default values and maximum limits for counts.
def default_weapons_count
@options[:default_weapons_count] || 5;
@options[:default_weapons_count] || 5
end
def default_characters_count
@options[:default_characters_count] || 3;
@options[:default_characters_count] || 3
end
def default_summons_count
@options[:default_summons_count] || 2;
@options[:default_summons_count] || 2
end
def max_weapons
@options[:max_weapons] || 13;
@options[:max_weapons] || 13
end
def max_characters
@options[:max_characters] || 5;
@options[:max_characters] || 5
end
def max_summons
@options[:max_summons] || 8;
@options[:max_summons] || 8
end
# Stub method for name quality filtering.

View file

@ -0,0 +1,44 @@
# frozen_string_literal: true
module Processors
##
# BaseProcessor provides shared functionality for processing transformed deck data
# into new party records. Subclasses must implement the +process+ method.
#
# @abstract
class BaseProcessor
##
# Initializes the processor.
#
# @param party [Party] the Party record to which the component will be added.
# @param data [Object] the transformed data for this component.
# @param options [Hash] optional additional options.
def initialize(party, data, options = {})
@party = party
@data = data
@options = options
end
##
# Process the given data and create associated records.
#
# @abstract Subclasses must implement this method.
# @return [void]
def process
raise NotImplementedError, "#{self.class} must implement the process method"
end
protected
attr_reader :party, :data, :options
##
# Logs a message to Rails.logger.
#
# @param message [String] the message to log.
# @return [void]
def log(message)
Rails.logger.info "[PROCESSOR][#{self.class.name}] #{message}"
end
end
end

View file

@ -0,0 +1,89 @@
# frozen_string_literal: true
module Processors
##
# CharacterProcessor processes an array of character data and creates GridCharacter records.
#
# @example
# processor = Processors::CharacterProcessor.new(party, transformed_characters_array)
# processor.process
class CharacterProcessor < BaseProcessor
def initialize(party, data, type = :normal, options = {})
super(party, data, options)
@party = party
@data = data
end
##
# Processes character data.
#
# Iterates over each character hash in +data+ and creates a new GridCharacter record.
# Expects each character hash to include keys such as :id, :position, :uncap, etc.
#
# @return [void]
def process
unless @data.is_a?(Hash)
Rails.logger.error "[CHARACTER] Invalid data format: expected a Hash, got #{@data.class}"
return
end
unless @data.key?('deck') && @data['deck'].key?('npc')
Rails.logger.error '[CHARACTER] Missing npc data in deck JSON'
return
end
@data = @data.with_indifferent_access
characters_data = @data.dig('deck', 'npc')
grid_characters = process_characters(characters_data)
grid_characters.each do |grid_character|
begin
grid_character.save!
rescue ActiveRecord::RecordInvalid => e
Rails.logger.error "[CHARACTER] Failed to create GridCharacter: #{e.record.errors.full_messages.join(', ')}"
end
end
rescue StandardError => e
raise e
end
private
def process_characters(characters_data)
characters_data.map do |key, raw_character|
next if raw_character.nil? || raw_character['param'].nil? || raw_character['master'].nil?
position = key.to_i - 1
# Find the Character record by its granblue_id.
character_id = raw_character.dig('master', 'id')
character = Character.find_by(granblue_id: character_id)
unless character
Rails.logger.error "[CHARACTER] Character not found with id #{character_id}"
next
end
# The deck doesn't have Awakening data, so use the default
awakening = Awakening.where(slug: 'character-balanced').first
grid_character = GridCharacter.create(
party_id: @party.id,
character_id: character.id,
uncap_level: raw_character.dig('param', 'evolution').to_i,
transcendence_step: raw_character.dig('param', 'phase').to_i,
position: position,
perpetuity: raw_character.dig('param', 'has_npcaugment_constant'),
awakening: awakening
)
grid_character
end.compact
end
# Converts a value to a boolean.
def parse_boolean(val)
val.to_s.downcase == 'true'
end
end
end

View file

@ -0,0 +1,127 @@
# frozen_string_literal: true
module Processors
##
# JobProcessor is responsible for processing job data from the transformed deck data.
# It finds a Job record by the masters id and assigns it (and its job skills) to the Party.
#
# @example
# raw_data = { 'job' => { "master": { "id": '130401', ... }, ... }, 'set_action': [ ... ] }
# processor = Processors::JobProcessor.new(party, raw_data, language: 'en')
# processor.process
class JobProcessor < BaseProcessor
##
# Initializes a new JobProcessor.
#
# @param party [Party] the Party record.
# @param data [Hash] the raw JSON data.
# @param options [Hash] options hash; e.g. expects :language.
def initialize(party, data, options = {})
super(party, options)
@party = party
@data = data
@language = options[:language] || 'en'
end
##
# Processes job data.
#
# Finds a Job record using a caseinsensitive search on +name_en+ or +name_jp+.
# If found, it assigns the job to the party and (if provided) assigns subskills.
#
# @return [void]
def process
if @data.is_a?(Hash)
@data = @data.with_indifferent_access
else
Rails.logger.error "[JOB] Invalid data format: expected a Hash, got #{@data.class}"
return
end
unless @data.key?('deck') && @data['deck'].key?('pc') && @data['deck']['pc'].key?('job')
Rails.logger.error '[JOB] Missing job data in deck JSON'
return
end
# Extract job data
job_data = @data.dig('deck', 'pc', 'job', 'master')
job_skills = @data.dig('deck', 'pc', 'set_action')
job_accessory_id = @data.dig('deck', 'pc', 'familiar_id') || @data.dig('deck', 'pc', 'shield_id')
# Look up and set the Job and its main skill
process_core_job(job_data)
# Look up and set the job skills.
if job_skills.present?
skills = process_job_skills(job_skills)
party.update(skill1: skills[0], skill2: skills[1], skill3: skills[2])
end
# Look up and set the job accessory.
accessory = process_job_accessory(job_accessory_id)
party.update(accessory: accessory)
rescue StandardError => e
Rails.logger.error "[JOB] Exception during job processing: #{e.message}"
raise e
end
private
##
# Updates the party with the corresponding job and its main skill.
#
# This method attempts to locate a Job using the provided job_data's 'id' (which represents
# the granblue_id). If the job is found, it retrieves the job's main
# skill (i.e. the JobSkill record where `main` is true) and updates the party with the job
# and its main skill. If no job is found, the method returns without updating.
#
# @param [Hash] job_data A hash containing job information.
# It must include the key 'id', which holds the granblue_id for the job.
# @return [void]
#
# @example
# job_data = { 'id' => 42 }
# process_core_job(job_data)
def process_core_job(job_data)
# Look up the Job by granblue_id (the job master id).
job = Job.find_by(granblue_id: job_data['id'])
return unless job
main_skill = JobSkill.find_by(job_id: job.id, main: true)
party.update(job: job, skill0: main_skill)
end
##
# Processes and associates job skills with a given job.
#
# This method first removes any existing skills from the job. It then iterates over the provided
# array of skill names, attempting to find a matching JobSkill record by comparing the provided
# name against both the English and Japanese name fields. Any found JobSkill records are then
# associated with the job. Finally, the method logs the processed job skill names.
#
# @param job_skills [Array<String>] an array of job skill names.
# @return [Array<JobSkill>] an array of JobSkill records that were associated with the job.
def process_job_skills(job_skills)
job_skills.map do |skill|
name = skill['name']
JobSkill.find_by(name_en: name)
end
end
##
# Processes raw data to find the currently set job accessory
#
# Searches JobAccessories for the given `granblue_id`
#
# @param accessory_id [String] the granblue_id of the accessory
def process_job_accessory(accessory_id)
JobAccessory.find_by(granblue_id: accessory_id)
end
# Converts a value (string or boolean) to a boolean.
def to_boolean(val)
val.to_s.downcase == 'true'
end
end
end

View file

@ -0,0 +1,201 @@
# 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

View file

@ -0,0 +1,395 @@
# frozen_string_literal: true
module Processors
##
# WeaponProcessor processes weapon data from a deck JSON and creates GridWeapon records.
# It follows a similar errorhandling and implementation strategy as SummonProcessor.
#
# Expected data format (excerpt):
# {
# "deck": {
# "pc": {
# "weapons": {
# "1": {
# "param": {
# "uncap": 3,
# "level": "150",
# "augment_skill_info": [ [ { "skill_id": 1588, "effect_value": "3", "show_value": "3%" }, ... ] ],
# "arousal": {
# "is_arousal_weapon": true,
# "level": 4,
# "skill": [ { "skill_id": 1896, ... }, ... ]
# },
# ...
# },
# "master": {
# "id": "1040215100",
# "name": "Wamdus's Cnidocyte",
# "attribute": "2",
# ...
# },
# "keys": [ "..." ] // optional
# },
# "2": { ... },
# ...
# }
# }
# }
# }
#
# The processor also uses an AX_MAPPING to convert ingame AX skill IDs to our stored values.
class WeaponProcessor < BaseProcessor
TRANSCENDENCE_LEVELS = [200, 210, 220, 230, 240, 250].freeze
# Mapping from ingame AX skill IDs (as strings) to our internal modifier values.
AX_MAPPING = {
'1588' => 2,
'1589' => 0,
'1590' => 1,
'1591' => 3,
'1592' => 4,
'1593' => 9,
'1594' => 13,
'1595' => 10,
'1596' => 5,
'1597' => 6,
'1599' => 8,
'1600' => 12,
'1601' => 11,
'1719' => 15,
'1720' => 16,
'1721' => 17,
'1722' => 14
}.freeze
# KEY_MAPPING maps the raw key value (as a string) to a canonical range or value.
# For example, in our test we want a raw key "10001" to be interpreted as any key whose
# canonical granblue_id is between 697 and 706.
KEY_MAPPING = {
'10001' => %w[697 698 699 700 701 702 703 704 705 706],
'10002' => %w[707 708 709 710 711 712 713 714 715 716],
'10003' => %w[717 718 719 720 721 722 723 724 725 726],
'10004' => %w[727 728 729 730 731 732 733 734 735 736],
'10005' => %w[737 738 739 740 741 742 743 744 745 746],
'10006' => %w[747 748 749 750 751 752 753 754 755 756],
'11001' => '758',
'11002' => '759',
'11003' => '760',
'11004' => '760',
'13001' => %w[1240 2204 2208], # α Pendulum
'13002' => %w[1241 2205 2209], # β Pendulum
'13003' => %w[1242 2206 2210], # γ Pendulum
'13004' => %w[1243 2207 2211], # Δ Pendulum
'14001' => %w[502 503 504 505 506 507 1213 1214 1215 1216 1217 1218], # Pendulum of Strength
'14002' => %w[130 131 132 133 134 135 71 72 73 74 75 76], # Pendulum of Zeal
'14003' => %w[1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271], # Pendulum of Strife
'14004' => %w[1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210], # Pendulum of Prosperity
'14005' => %w[2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223], # Pendulum of Extremity
'14006' => %w[2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235], # Pendulum of Sagacity
'14007' => %w[2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247], # Pendulum of Supremacy
'14011' => %w[322 323 324 325 326 327 1310 1311 1312 1313 1314 1315], # Chain of Temperament
'14012' => %w[764 765 766 767 768 769 1731 1732 1733 1734 1735 948], # Chain of Restoration
'14013' => %w[1171 1172 1173 1174 1175 1176 1736 1737 1738 1739 1740 1741], # Chain of Glorification
'14014' => '1723', # Chain of Temptation
'14015' => '1724', # Chain of Forbiddance
'14016' => '1725', # Chain of Depravity
'14017' => '1726', # Chain of Falsehood
'15001' => '1446',
'15002' => '1447',
'15003' => '1448', # Abyss Teluma
'15004' => '1449', # Crag Teluma
'15005' => '1450', # Tempest Teluma
'15006' => '1451',
'15007' => '1452', # Malice Teluma
'15008' => %w[2043 2044 2045 2046 2047 2048],
'15009' => %w[2049 2050 2051 2052 2053 2054], # Oblivion Teluma
'16001' => %w[1228 1229 1230 1231 1232 1233], # Optimus Teluma
'16002' => %w[1234 1235 1236 1237 1238 1239], # Omega Teluma
'17001' => '1807',
'17002' => '1808',
'17003' => '1809',
'17004' => '1810',
# Emblems (series {24})
'3' => '3',
'2' => '2',
'1' => '1'
}.freeze
AWAKENING_MAPPING = {
'1' => 'weapon-atk',
'2' => 'weapon-def',
'3' => 'weapon-special',
'4' => 'weapon-ca',
'5' => 'weapon-skill',
'6' => 'weapon-heal',
'7' => 'weapon-multi'
}.freeze
ELEMENTAL_WEAPON_MAPPING = %w[1040914600 1040810100 1040506800 1040312000 1040513800 1040810900 1040910300
1040114200 1040027000 1040807600 1040120300 1040318500 1040710000 1040608100
1040812100 1040307200 1040410200 1040510600 1040018100 1040113400 1040017300
1040011900 1040412200 1040508000 1040512600 1040609100 1040411600 1040208800
1040906900 1040909300 1040509700 1040014400 1040308400 1040613100 1040013200
1040011300 1040413400 1040607500 1040504400 1040703600 1040406000 1040601700
1040904300 1040109700 1040900300 1040002000 1040807200 1040102900 1040203000
1040402800 1040507400 1040200900 1040307800 1040501600 1040706900 1040604200
1040103000 1040003500 1040300100 1040907500 1040105500 1040106600 1040503500
1040801300 1040410800 1040702700 1040006200 1040302300 1040803700 1040900400
1040406900 1040109100 1040111600 1040706300 1040806400 1040209700 1040707500
1040208200 1040214000 1040021100 1040417200 1040012600 1040317500 1040402900].freeze
ELEMENTAL_WEAPON_MAPPING_INT = ELEMENTAL_WEAPON_MAPPING.map(&:to_i).sort.freeze
ELEMENT_MAPPING = {
0 => nil,
1 => 4, # Wind -> Earth
2 => 2, # Fire -> Fire
3 => 3, # Water -> Water
4 => 1, # Earth -> Wind
5 => 6, # Dark -> Light
6 => 5 # Light -> Dark
}.freeze
##
# Initializes a new WeaponProcessor.
#
# @param party [Party] the Party record.
# @param data [Hash] the full deck JSON.
# @param type [Symbol] (optional) processing type.
# @param options [Hash] additional options.
def initialize(party, data, type = :normal, options = {})
super(party, data, options)
@party = party
@data = data
end
##
# Processes the decks weapon data and creates GridWeapon records.
#
# It expects the incoming data to be a Hash that contains:
# "deck" → "pc" → "weapons"
#
# @return [void]
def process
unless @data.is_a?(Hash)
Rails.logger.error "[WEAPON] Invalid data format: expected a Hash, got #{@data.class}"
return
end
unless @data.key?('deck') && @data['deck'].key?('pc') && @data['deck']['pc'].key?('weapons')
Rails.logger.error '[WEAPON] Missing weapons data in deck JSON'
return
end
@data = @data.with_indifferent_access
weapons_data = @data['deck']['pc']['weapons']
grid_weapons = process_weapons(weapons_data)
grid_weapons.each do |grid_weapon|
begin
grid_weapon.save!
rescue ActiveRecord::RecordInvalid => e
Rails.logger.error "[WEAPON] Failed to create GridWeapon: #{e.record.errors.full_messages.join(', ')}"
end
end
end
private
##
# Processes a hash of raw weapon data and returns an array of GridWeapon records.
#
# @param weapons_data [Hash] the raw weapons data (keyed by slot number).
# @return [Array<GridWeapon>]
def process_weapons(weapons_data)
weapons_data.map do |key, raw_weapon|
next if raw_weapon.nil? || raw_weapon['param'].nil? || raw_weapon['master'].nil?
position = key.to_i == 1 ? -1 : key.to_i - 2
mainhand = (position == -1)
uncap_level = raw_weapon.dig('param', 'uncap').to_i
level = raw_weapon.dig('param', 'level').to_i
transcendence_step = level_to_transcendence(level)
series = raw_weapon.dig('master', 'series_id')
weapon_id = raw_weapon.dig('master', 'id')
processed_weapon_id = if Weapon.element_changeable?(series)
process_elemental_weapon(weapon_id)
else
weapon_id
end
processed_element = if Weapon.element_changeable?(series)
ELEMENT_MAPPING[raw_weapon.dig('master', 'attribute')]
end
weapon = Weapon.find_by(granblue_id: processed_weapon_id)
unless weapon
Rails.logger.error "[WEAPON] Weapon not found with id #{processed_weapon_id}"
next
end
grid_weapon = GridWeapon.new(
party: @party,
weapon: weapon,
position: position,
mainhand: mainhand,
uncap_level: uncap_level,
transcendence_step: transcendence_step,
element: processed_element
)
arousal_data = raw_weapon.dig('param', 'arousal')
if arousal_data && arousal_data['is_arousal_weapon']
grid_weapon.awakening_id = map_arousal_to_awakening(arousal_data)
grid_weapon.awakening_level = arousal_data['level'].to_i.positive? ? arousal_data['level'].to_i : 1
end
# Extract skill IDs and convert into weapon keys
skill_ids = [raw_weapon['skill1'], raw_weapon['skill2'], raw_weapon['skill3']].compact.map { |s| s['id'] }
process_weapon_keys(grid_weapon, skill_ids) if skill_ids.length.positive?
if raw_weapon.dig('param', 'augment_skill_info').present?
process_weapon_ax(grid_weapon, raw_weapon.dig('param', 'augment_skill_info'))
end
grid_weapon
end.compact
end
##
# Converts a given weapon level to a transcendence step.
#
# If the level is less than 200, returns 0; otherwise, floors the level
# to the nearest 10 and returns its index in TRANSCENDENCE_LEVELS.
#
# @param level [Integer] the weapons 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) || 0
end
##
# Processes weapon key data and assigns them to the grid_weapon.
#
# @param grid_weapon [GridWeapon] the grid weapon record being built.
# @param skill_ids [Array<String>] an array of key identifiers.
# @return [void]
def process_weapon_keys(grid_weapon, skill_ids)
series = grid_weapon.weapon.series.to_i
skill_ids.each_with_index do |skill_id, idx|
# Go to the next iteration unless the key under which `skill_id` exists
mapping_pair = KEY_MAPPING.find { |key, value| Array(value).include?(skill_id) }
next unless mapping_pair
# Fetch the key from the mapping_pair and find the weapon key based on the weapon series
mapping_value = mapping_pair.first
candidate = WeaponKey.where('granblue_id = ? AND ? = ANY(series)', mapping_value, series).first
if candidate
grid_weapon["weapon_key#{idx + 1}_id"] = candidate.id
else
Rails.logger.warn "[WEAPON] No matching WeaponKey found for raw key #{skill_id} using mapping #{mapping_value}"
end
end
end
##
# Returns true if the candidate key (a string) matches the mapping entry.
#
# If mapping_entry includes a dash, it is interpreted as a range (e.g. "697-706").
# Otherwise, it must match exactly.
#
# @param candidate_key [String] the candidate WeaponKey.granblue_id.
# @param mapping_entry [String] the mapping entry.
# @return [Boolean]
def matches_key?(candidate_key, mapping_entry)
if mapping_entry.include?('-')
left, right = mapping_entry.split('-').map(&:to_i)
candidate_key.to_i >= left && candidate_key.to_i <= right
else
candidate_key == mapping_entry
end
end
##
# Processes AX (augment) skill data.
#
# The deck stores AX skills in an array of arrays under "augment_skill_info".
# This method flattens the data and assigns each skills modifier and strength.
#
# @param grid_weapon [GridWeapon] the grid weapon record being built.
# @param ax_skill_info [Array] the raw AX skill info.
# @return [void]
def process_weapon_ax(grid_weapon, ax_skill_info)
# Flatten the nested array structure.
ax_skills = ax_skill_info.flatten
ax_skills.each_with_index do |ax, idx|
ax_id = ax['skill_id'].to_s
ax_mod = AX_MAPPING[ax_id] || ax_id.to_i
strength = ax['effect_value'].to_s.gsub(/[+%]/, '').to_i
grid_weapon["ax_modifier#{idx + 1}"] = ax_mod
grid_weapon["ax_strength#{idx + 1}"] = strength
end
end
##
# Maps the ingame awakening data (stored under "arousal") to our Awakening record.
#
# This method looks at the "skill" array inside the arousal data and uses the first
# awakenings skill_id to find the corresponding Awakening record.
#
# @param arousal_data [Hash] the raw arousal (awakening) data.
# @return [String, nil] the database awakening id or nil if not found.
def map_arousal_to_awakening(arousal_data)
raw_data = arousal_data.with_indifferent_access
return nil if raw_data.nil?
return nil unless raw_data.is_a?(Hash)
return nil unless raw_data.has_key?('form')
id = (raw_data['form']).to_s
return unless AWAKENING_MAPPING.key?(id)
slug = AWAKENING_MAPPING[id]
awakening = Awakening.find_by(slug: slug)
awakening&.id
end
def process_elemental_weapon(granblue_id)
granblue_int = granblue_id.to_i
# Find the index of the first element that is >= granblue_int.
idx = ELEMENTAL_WEAPON_MAPPING_INT.bsearch_index { |x| x >= granblue_int }
# We'll check the candidate at idx and the one immediately before it.
candidates = []
if idx
candidate = ELEMENTAL_WEAPON_MAPPING_INT[idx]
candidates << candidate if (granblue_int - candidate).abs <= 500
# Check the candidate just before, if it exists.
if idx > 0
candidate_prev = ELEMENTAL_WEAPON_MAPPING_INT[idx - 1]
candidates << candidate_prev if (granblue_int - candidate_prev).abs <= 500
end
else
# If idx is nil, then granblue_int is greater than all mapped values.
candidate = ELEMENTAL_WEAPON_MAPPING_INT.last
candidates << candidate if (granblue_int - candidate).abs <= 500
end
# If no candidate is close enough, return the original input.
return granblue_id if candidates.empty?
# Choose the candidate with the smallest difference.
best_match = candidates.min_by { |x| (granblue_int - x).abs }
best_match.to_s
end
end
end

View file

@ -0,0 +1,134 @@
# frozen_string_literal: true
class MigrateWeaponSeries < ActiveRecord::Migration[8.0]
def up
Weapon.transaction do
puts 'Starting weapon series migration...'
puts 'Updating Seraphic Weapons (0 -> 1)...'
Weapon.find_by(series: 0).update!(new_series: 1)
puts 'Updating Grand Weapons (1 -> 2)...'
Weapon.find_by(series: 1).update!(new_series: 2)
puts 'Updating Dark Opus Weapons (2 -> 3)...'
Weapon.find_by(series: 2).update!(new_series: 3)
puts 'Updating Revenant Weapons (4 -> 4)...'
Weapon.find_by(series: 4).update!(new_series: 4)
puts 'Updating Primal Weapons (6 -> 5)...'
Weapon.find_by(series: 6).update!(new_series: 5)
puts 'Updating Beast Weapons (5, 7 -> 6)...'
Weapon.find_by(series: 5).update!(new_series: 6)
Weapon.find_by(series: 7).update!(new_series: 6)
puts 'Updating Regalia Weapons (8 -> 7)...'
Weapon.find_by(series: 8).update!(new_series: 7)
puts 'Updating Omega Weapons (9 -> 8)...'
Weapon.find_by(series: 9).update!(new_series: 8)
puts 'Updating Olden Primal Weapons (10 -> 9)...'
Weapon.find_by(series: 10).update!(new_series: 9)
puts 'Updating Hollowsky Weapons (12 -> 10)...'
Weapon.find_by(series: 12).update!(new_series: 10)
puts 'Updating Xeno Weapons (13 -> 11)...'
Weapon.find_by(series: 13).update!(new_series: 11)
puts 'Updating Rose Weapons (15 -> 12)...'
Weapon.find_by(series: 15).update!(new_series: 12)
puts 'Updating Ultima Weapons (17 -> 13)...'
Weapon.find_by(series: 17).update!(new_series: 13)
puts 'Updating Bahamut Weapons (16 -> 14)...'
Weapon.find_by(series: 16).update!(new_series: 14)
puts 'Updating Epic Weapons (18 -> 15)...'
Weapon.find_by(series: 18).update!(new_series: 15)
puts 'Updating Cosmos Weapons (20 -> 16)...'
Weapon.find_by(series: 20).update!(new_series: 16)
puts 'Updating Superlative Weapons (22 -> 17)...'
Weapon.find_by(series: 22).update!(new_series: 17)
puts 'Updating Vintage Weapons (23 -> 18)...'
Weapon.find_by(series: 23).update!(new_series: 18)
puts 'Updating Class Champion Weapons (24 -> 19)...'
Weapon.find_by(series: 24).update!(new_series: 19)
puts 'Updating Sephira Weapons (28 -> 23)...'
Weapon.find_by(series: 28).update!(new_series: 23)
puts 'Updating Astral Weapons (14 -> 26)...'
Weapon.find_by(series: 14).update!(new_series: 26)
puts 'Updating Draconic Weapons (3 -> 27)...'
Weapon.find_by(series: 3).update!(new_series: 27)
puts 'Updating Ancestral Weapons (21 -> 29)...'
Weapon.find_by(series: 21).update!(new_series: 29)
puts 'Updating New World Foundation (29 -> 30)...'
Weapon.find_by(series: 29).update!(new_series: 30)
puts 'Updating Ennead Weapons (19 -> 31)...'
Weapon.find_by(series: 19).update!(new_series: 31)
puts 'Updating Militis Weapons (11 -> 32)...'
Weapon.find_by(series: 11).update!(new_series: 32)
puts 'Updating Malice Weapons (26 -> 33)...'
Weapon.find_by(series: 26).update!(new_series: 33)
puts 'Updating Menace Weapons (26 -> 34)...'
Weapon.find_by(series: 26).update!(new_series: 34)
puts 'Updating Illustrious Weapons (31 -> 35)...'
Weapon.find_by(series: 31).update!(new_series: 35)
puts 'Updating Proven Weapons (25 -> 36)...'
Weapon.find_by(series: 25).update!(new_series: 36)
puts 'Updating Revans Weapons (30 -> 37)...'
Weapon.find_by(series: 30).update!(new_series: 37)
puts 'Updating World Weapons (32 -> 38)...'
Weapon.find_by(series: 32).update!(new_series: 38)
puts 'Updating Exo Weapons (33 -> 39)...'
Weapon.find_by(series: 33).update!(new_series: 39)
puts 'Updating Draconic Weapons Providence (34 -> 40)...'
Weapon.find_by(series: 34).update!(new_series: 40)
puts 'Updating Celestial Weapons (37 -> 41)...'
Weapon.find_by(series: 37).update!(new_series: 41)
puts 'Updating Omega Rebirth Weapons (38 -> 42)...'
Weapon.find_by(series: 38).update!(new_series: 42)
puts 'Updating Event Weapons (34 -> 98)...'
Weapon.find_by(series: 34).update!(new_series: 98) # Event
puts 'Updating Gacha Weapons (36 -> 99)...'
Weapon.find_by(series: 36).update!(new_series: 99) # Gacha
puts 'Migration completed successfully!'
rescue StandardError => e
puts "Error occurred during migration: #{e.message}"
puts "Backtrace: #{e.backtrace}"
raise e
end
end
def down
raise ActiveRecord::IrreversibleMigration
end
end

View file

@ -0,0 +1,37 @@
# frozen_string_literal: true
class MigrateSeriesOnWeaponKey < ActiveRecord::Migration[8.0]
def up
WeaponKey.transaction do
puts 'Starting weapon key series migration...'
puts 'Updating Telumas (3 -> 27)...'
WeaponKey.where('? = ANY(series)', 3).update_all('series = array_replace(series, 3, 27)')
puts 'Updating Providence Telumas (34 -> 40)...'
WeaponKey.where('? = ANY(series)', 34).update_all('series = array_replace(series, 34, 40)')
puts 'Updating Gauph Keys (17 -> 13)...'
WeaponKey.where('? = ANY(series)', 17).update_all('series = array_replace(series, 17, 13)')
puts 'Updating Pendulums (2 -> 3)...'
WeaponKey.where('? = ANY(series)', 2).update_all('series = array_replace(series, 2, 3)')
puts 'Updating Chains (2 -> 3)...'
WeaponKey.where('? = ANY(series)', 2).update_all('series = array_replace(series, 2, 3)')
puts 'Updating Emblems (24 -> 19)...'
WeaponKey.where('? = ANY(series)', 24).update_all('series = array_replace(series, 24, 19)')
puts 'Migration completed successfully!'
rescue StandardError => e
puts "Error occurred during migration: #{e.message}"
puts "Backtrace: #{e.backtrace}"
raise e
end
end
def down
raise ActiveRecord::IrreversibleMigration
end
end

View file

@ -1 +1 @@
DataMigrate::Data.define(version: 20250115094623)
DataMigrate::Data.define(version: 20250218025755)

View file

@ -0,0 +1,6 @@
class MakeJobForeignKeyDeferrable < ActiveRecord::Migration[8.0]
def change
remove_foreign_key :jobs, column: :base_job_id
add_foreign_key :jobs, :jobs, column: :base_job_id, deferrable: :deferred, initially_deferred: true
end
end

View file

@ -0,0 +1,13 @@
class RemoveForeignKeyConstraintOnJobsBaseJobId < ActiveRecord::Migration[8.0]
# Removes the self-referential foreign key constraint on jobs.base_job_id.
# This constraint was causing issues when seeding job records via CSV.
def change
# Check if the foreign key exists before removing it
if foreign_key_exists?(:jobs, column: :base_job_id)
remove_foreign_key :jobs, column: :base_job_id
Rails.logger.info 'Removed foreign key constraint on jobs.base_job_id'
else
Rails.logger.info 'No foreign key on jobs.base_job_id found'
end
end
end

View file

@ -0,0 +1,5 @@
class AddNewSeriesToWeapons < ActiveRecord::Migration[8.0]
def change
add_column :weapons, :new_series, :integer
end
end

View file

@ -0,0 +1,6 @@
class MoveNewSeriesToSeries < ActiveRecord::Migration[8.0]
def change
remove_column :weapons, :series
rename_column :weapons, :new_series, :series
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2025_02_01_170037) do
ActiveRecord::Schema[8.0].define(version: 2025_02_18_025315) do
# These are extensions that must be enabled in order to support this database
enable_extension "btree_gin"
enable_extension "pg_catalog.plpgsql"
@ -465,7 +465,6 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_01_170037) do
t.integer "rarity"
t.integer "element"
t.integer "proficiency"
t.integer "series", default: -1, null: false
t.boolean "flb", default: false, null: false
t.boolean "ulb", default: false, null: false
t.integer "max_level", default: 100, null: false
@ -495,6 +494,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_01_170037) do
t.boolean "transcendence", default: false
t.date "transcendence_date"
t.string "recruits"
t.integer "series"
t.index ["granblue_id"], name: "index_weapons_on_granblue_id"
t.index ["name_en"], name: "index_weapons_on_name_en", opclass: :gin_trgm_ops, using: :gin
t.index ["recruits"], name: "index_weapons_on_recruits"
@ -511,7 +511,6 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_01_170037) do
add_foreign_key "grid_weapons", "parties"
add_foreign_key "grid_weapons", "weapon_keys", column: "weapon_key3_id"
add_foreign_key "grid_weapons", "weapons"
add_foreign_key "jobs", "jobs", column: "base_job_id"
add_foreign_key "oauth_access_grants", "oauth_applications", column: "application_id"
add_foreign_key "oauth_access_tokens", "oauth_applications", column: "application_id"
add_foreign_key "parties", "guidebooks", column: "guidebook1_id"

View file

@ -1,41 +1,98 @@
# frozen_string_literal: true
# canonical.rb - Loads canonical seed data from CSV files into the database.
#
# This file is used to load canonical data for various models from CSV files
# located in db/seed/test. For models that reference other models by fixed IDs
# (e.g. Job, Guidebook, etc.), use the `use_id: true` option to preserve the CSV
# provided IDs (so that inter-model references remain correct).
#
# @example
# load_csv_for(Character, 'characters_test.csv', :granblue_id)
#
# # For objects that need to preserve the CSV "id" column:
# load_csv_for(Job, 'jobs_test.csv', :granblue_id, use_id: true)
#
require 'csv'
# Helper: Process boolean columns.
##
# Processes specified columns in an attributes hash to booleans.
#
# @param attrs [Hash] The attributes hash.
# @param columns [Array<Symbol>] The list of columns to cast to boolean.
def process_booleans(attrs, columns)
columns.each do |col|
next unless attrs.key?(col) && attrs[col].present?
# Use ActiveModel::Type::Boolean to cast the value.
attrs[col] = ActiveModel::Type::Boolean.new.cast(attrs[col])
end
end
# Helper: Process date columns.
##
# Processes specified columns in an attributes hash to dates.
#
# @param attrs [Hash] The attributes hash.
# @param columns [Array<Symbol>] The list of columns to parse as dates.
def process_dates(attrs, columns)
columns.each do |col|
next unless attrs.key?(col) && attrs[col].present?
# Parse the date, or assign nil if parsing fails.
attrs[col] = Date.parse(attrs[col]) rescue nil
end
end
# Simplified CSV loader for a given model.
def load_csv_for(model_class, csv_filename, unique_key = :granblue_id)
##
# Loads CSV data for the given model class.
#
# Reads a CSV file from the db/seed/test directory and uses the given unique_key
# to determine whether a record already exists. If the record exists, its attributes
# are not overwritten; otherwise, a new record is created.
#
# @param model_class [Class] The ActiveRecord model class to load data for.
# @param csv_filename [String] The CSV filename (located in db/seed/test).
# @param unique_key [Symbol] The attribute used to uniquely identify a record (default: :granblue_id).
# @param use_id [Boolean] If true, preserves the CSV id field instead of removing it (default: false).
#
# @return [void]
def load_csv_for(model_class, csv_filename, unique_key = :granblue_id, use_id: false)
csv_file = Rails.root.join('db', 'seed', 'test', csv_filename)
puts "Loading #{model_class.name} data from #{csv_file}..."
# puts "Loading #{model_class.name} data from #{csv_file}..."
CSV.foreach(csv_file, headers: true) do |row|
# Convert CSV row to a hash with symbolized keys.
attrs = row.to_hash.symbolize_keys
# Process known boolean columns.
process_booleans(attrs, %i[flb ulb subaura limit transcendence])
process_dates(attrs, %i[release_date flb_date ulb_date transcendence_date])
# Process known date columns. Extend this list as needed.
process_dates(attrs, %i[release_date flb_date ulb_date transcendence_date created_at])
# Clean up attribute values: trim whitespace and convert empty strings to nil.
attrs.each { |k, v| attrs[k] = nil if v.is_a?(String) && v.strip.empty? }
attrs.except!(:id)
model_class.find_or_create_by!(unique_key => attrs[unique_key]) do |r|
# Remove the :id attribute unless we want to preserve it (for fixed canonical IDs).
attrs.except!(:id) unless use_id
# Find or create the record based on the unique key.
record = model_class.find_or_create_by!(unique_key => attrs[unique_key]) do |r|
# Assign all attributes except the unique_key.
r.assign_attributes(attrs.except(unique_key))
end
# puts "Loaded #{model_class.name}: #{record.public_send(unique_key)}"
end
end
# Load canonical data for each model.
load_csv_for(Awakening, 'awakenings_test.csv', :slug)
load_csv_for(Summon, 'summons_test.csv', :granblue_id)
load_csv_for(Weapon, 'weapons_test.csv', :granblue_id)
load_csv_for(Character, 'characters_test.csv', :granblue_id)
# Load canonical data for core models.
load_csv_for(Awakening, 'awakenings_test.csv', :id, use_id: true)
load_csv_for(Summon, 'summons_test.csv', :id, use_id: true)
load_csv_for(Weapon, 'weapons_test.csv', :id, use_id: true)
load_csv_for(Character, 'characters_test.csv', :id, use_id: true)
# Load additional canonical data that require preserving the provided IDs.
load_csv_for(Job, 'jobs_test.csv', :id, use_id: true)
load_csv_for(Guidebook, 'guidebooks_test.csv', :id, use_id: true)
load_csv_for(JobAccessory, 'job_accessories_test.csv', :id, use_id: true)
load_csv_for(JobSkill, 'job_skills_test.csv', :id, use_id: true)
load_csv_for(WeaponAwakening, 'weapon_awakenings_test.csv', :id, use_id: true)
load_csv_for(WeaponKey, 'weapon_keys_test.csv', :id, use_id: true)

View file

@ -4,3 +4,14 @@
"d5fb1b79-483f-44cf-8437-92ce31f5f2b2","Rosamia (SR)","ロザミア(SR)","3030049000","2014-12-31","Rosamia (SR)","%A5%ED%A5%B6%A5%DF%A5%A2%20%28SR%29",2,6,1,,2,1,,FALSE,260,1300,,260,1300,,7,3,3.5,,FALSE,FALSE,,,"{1018}","{}","{}",,,"21090","SR/ロザミア"
"24bf1c09-509f-4db1-b953-b95ebcc69fb9","Seofon","シエテ","3040036000","2015-04-16","Seofon","%A5%B7%A5%A8%A5%C6%20%28SSR%29",3,1,1,,1,1,,TRUE,237,1477,1777,1777,10777,12777,10,5,4.5,5,FALSE,TRUE,14777,1977,"{4007}","{siete}","{}","2017-03-20","2021-06-29","21117","SSR/シエテ"
"b1eae4fe-e35c-44da-aa4f-7ca1c3e5863f","Zeta","ゼタ","3040028000","2014-12-31","Zeta","%A5%BC%A5%BF%20%28SSR%29",3,2,4,,2,1,,TRUE,240,1280,1520,240,1280,1520,10,5,5,,FALSE,FALSE,,,"{3024}","{}","{}","2020-01-28",,"21290","SSR/ゼタ"
"03b0bee9-e75b-4d5f-9ffc-0ad8cc2df7f0","Percival (Grand)","パーシヴァル(リミテッドver)","3040425000","2022-09-30","Percival (Grand)","%A5%D1%A1%BC%A5%B7%A5%F4%A5%A1%A5%EB%20%28SSR%29%A5%EA%A5%DF%A5%C6%A5%C3%A5%C9%A5%D0%A1%BC%A5%B8%A5%E7%A5%F3",3,2,1,,1,1,,FALSE,230,1340,,2010,10120,,,,,,FALSE,FALSE,,,"{3042}","{}","{}",,,"366171","SSR/リミテッドパーシヴァル"
"c96d4ba1-8a99-49fd-acde-d8eec45f6161","Fenie (Grand)","フェニー(リミテッドver)","3040519000","2024-03-15","Fenie (Grand)","",3,2,6,,2,0,,FALSE,294,1724,,1380,6580,,,,,,FALSE,FALSE,,,"{3246}","{}","{}",,,"",""
"3e7c163c-c92f-404e-8ec1-fe73bce6d6c3","Alanaan","アラナン","3040167000","2019-03-10","Alanaan","%A5%A2%A5%E9%A5%CA%A5%F3%20%28SSR%29",3,2,6,,1,2,,TRUE,219,1319,1519,1605,9705,11305,10,5,4.5,,FALSE,FALSE,,,"{3106}","{}","{}",,,"144742","SSR/アラナン"
"427f3e8a-8148-4b76-8982-f6a625a0f1e6","Zeta (Grand)","ゼタ(リミテッドver)","3040499000","2023-12-28","Zeta (Grand)","",3,2,4,,2,1,,FALSE,,1100,,,10500,,,,,,FALSE,FALSE,,,"{3024}","{}","{}",,,"",""
"437ddfde-7c39-469f-b75e-102f30595880","Fraux","フラウ","3040161000","2019-03-10","Fraux","%A5%D5%A5%E9%A5%A6%20%28SSR%29",3,2,7,,2,2,,TRUE,215,1315,,1608,9808,,10,5,4.5,,FALSE,FALSE,,,"{3100}","{}","{}","2023-08-16",,"144749","SSR/フラウ"
"76fe3ab2-e192-42f5-b063-920a2e406fb4","Michael","ミカエル","3040440000","2022-12-31","Michael","%A5%DF%A5%AB%A5%A8%A5%EB%20%28SSR%29",3,2,1,,2,5,,FALSE,240,1256,,2200,11320,,,,,,FALSE,FALSE,,,"{3217}","{}","{}",,,"381021","SSR/ミカエル"
"336f11a7-35b7-4a69-8041-c747a0c10b53","Fediel","フェディエル(リミテッドver)","3040376000","2021-12-31","Fediel","%A5%D5%A5%A7%A5%C7%A5%A3%A5%A8%A5%EB%20%28SSR%29",3,5,1,7,0,3,,FALSE,224,1200,,2015,10720,,,,,,FALSE,FALSE,,,"{3191}","{}","{}",,,"311659","SSR/フェディエル"
"180527e3-58ad-4e90-91ed-c70fa91798f7","Tikoh","ティコ","3040337000","2021-05-18","Tikoh","%A5%C6%A5%A3%A5%B3%20%28SSR%29",3,6,6,,2,2,,FALSE,367,1794,,730,4710,,,,,,FALSE,FALSE,,,"{3179}","{}","{}",,,"277461","SSR/ティコ"
"83ef5ef3-5180-465b-981e-6a121894aaec","Halluel and Malluel","ハールート・マールート(リミテッドver)","3040443000","2023-01-19","Halluel and Malluel","%A5%CF%A1%BC%A5%EB%A1%BC%A5%C8%A1%A6%A5%DE%A1%BC%A5%EB%A1%BC%A5%C8%20%28SSR%29",3,5,2,,2,5,,FALSE,290,800,,1500,4400,,,,,,FALSE,FALSE,,,"{3138}","{}","{}",,,"384939","SSR/リミテッドハールートマールート"
"8dbebe0d-12ed-4334-b3d7-8f516b8b2e23","Lich","リッチリミテッドver","3040357000","2021-09-15","Lich","%A5%EA%A5%C3%A5%C1%20%28SSR%29",3,5,6,,2,5,,FALSE,260,1300,,1550,8600,,,,,,FALSE,FALSE,,,"{3184}","{}","{}",,,"294327","SSR/リッチ"
"e9bb4639-d4f2-4299-b3ed-d396760a30eb","Nier","ニーア","3040169000","2019-03-10","Nier","%A5%CB%A1%BC%A5%A2%20%28SSR%29",3,5,3,2,2,2,,TRUE,200,1313,1513,1476,8906,10306,7,3,4.5,,FALSE,FALSE,,,"{3108}","{}","{}","2023-06-07",,"144747","SSR/ニーア"

1 id name_en name_jp granblue_id release_date wiki_en wiki_ja rarity element proficiency1 proficiency2 gender race1 race2 flb min_hp max_hp max_hp_flb min_atk max_atk max_atk_flb base_da base_ta ougi_ratio ougi_ratio_flb special ulb max_hp_ulb max_atk_ulb character_id nicknames_en nicknames_jp flb_date ulb_date gamewith kamigame
4 d5fb1b79-483f-44cf-8437-92ce31f5f2b2 Rosamia (SR) ロザミア(SR) 3030049000 2014-12-31 Rosamia (SR) %A5%ED%A5%B6%A5%DF%A5%A2%20%28SR%29 2 6 1 2 1 FALSE 260 1300 260 1300 7 3 3.5 FALSE FALSE {1018} {} {} 21090 SR/ロザミア
5 24bf1c09-509f-4db1-b953-b95ebcc69fb9 Seofon シエテ 3040036000 2015-04-16 Seofon %A5%B7%A5%A8%A5%C6%20%28SSR%29 3 1 1 1 1 TRUE 237 1477 1777 1777 10777 12777 10 5 4.5 5 FALSE TRUE 14777 1977 {4007} {siete} {} 2017-03-20 2021-06-29 21117 SSR/シエテ
6 b1eae4fe-e35c-44da-aa4f-7ca1c3e5863f Zeta ゼタ 3040028000 2014-12-31 Zeta %A5%BC%A5%BF%20%28SSR%29 3 2 4 2 1 TRUE 240 1280 1520 240 1280 1520 10 5 5 FALSE FALSE {3024} {} {} 2020-01-28 21290 SSR/ゼタ
7 03b0bee9-e75b-4d5f-9ffc-0ad8cc2df7f0 Percival (Grand) パーシヴァル(リミテッドver) 3040425000 2022-09-30 Percival (Grand) %A5%D1%A1%BC%A5%B7%A5%F4%A5%A1%A5%EB%20%28SSR%29%A5%EA%A5%DF%A5%C6%A5%C3%A5%C9%A5%D0%A1%BC%A5%B8%A5%E7%A5%F3 3 2 1 1 1 FALSE 230 1340 2010 10120 FALSE FALSE {3042} {} {} 366171 SSR/リミテッドパーシヴァル
8 c96d4ba1-8a99-49fd-acde-d8eec45f6161 Fenie (Grand) フェニー(リミテッドver) 3040519000 2024-03-15 Fenie (Grand) 3 2 6 2 0 FALSE 294 1724 1380 6580 FALSE FALSE {3246} {} {}
9 3e7c163c-c92f-404e-8ec1-fe73bce6d6c3 Alanaan アラナン 3040167000 2019-03-10 Alanaan %A5%A2%A5%E9%A5%CA%A5%F3%20%28SSR%29 3 2 6 1 2 TRUE 219 1319 1519 1605 9705 11305 10 5 4.5 FALSE FALSE {3106} {} {} 144742 SSR/アラナン
10 427f3e8a-8148-4b76-8982-f6a625a0f1e6 Zeta (Grand) ゼタ(リミテッドver) 3040499000 2023-12-28 Zeta (Grand) 3 2 4 2 1 FALSE 1100 10500 FALSE FALSE {3024} {} {}
11 437ddfde-7c39-469f-b75e-102f30595880 Fraux フラウ 3040161000 2019-03-10 Fraux %A5%D5%A5%E9%A5%A6%20%28SSR%29 3 2 7 2 2 TRUE 215 1315 1608 9808 10 5 4.5 FALSE FALSE {3100} {} {} 2023-08-16 144749 SSR/フラウ
12 76fe3ab2-e192-42f5-b063-920a2e406fb4 Michael ミカエル 3040440000 2022-12-31 Michael %A5%DF%A5%AB%A5%A8%A5%EB%20%28SSR%29 3 2 1 2 5 FALSE 240 1256 2200 11320 FALSE FALSE {3217} {} {} 381021 SSR/ミカエル
13 336f11a7-35b7-4a69-8041-c747a0c10b53 Fediel フェディエル(リミテッドver) 3040376000 2021-12-31 Fediel %A5%D5%A5%A7%A5%C7%A5%A3%A5%A8%A5%EB%20%28SSR%29 3 5 1 7 0 3 FALSE 224 1200 2015 10720 FALSE FALSE {3191} {} {} 311659 SSR/フェディエル
14 180527e3-58ad-4e90-91ed-c70fa91798f7 Tikoh ティコ 3040337000 2021-05-18 Tikoh %A5%C6%A5%A3%A5%B3%20%28SSR%29 3 6 6 2 2 FALSE 367 1794 730 4710 FALSE FALSE {3179} {} {} 277461 SSR/ティコ
15 83ef5ef3-5180-465b-981e-6a121894aaec Halluel and Malluel ハールート・マールート(リミテッドver) 3040443000 2023-01-19 Halluel and Malluel %A5%CF%A1%BC%A5%EB%A1%BC%A5%C8%A1%A6%A5%DE%A1%BC%A5%EB%A1%BC%A5%C8%20%28SSR%29 3 5 2 2 5 FALSE 290 800 1500 4400 FALSE FALSE {3138} {} {} 384939 SSR/リミテッドハールートマールート
16 8dbebe0d-12ed-4334-b3d7-8f516b8b2e23 Lich リッチ(リミテッドver) 3040357000 2021-09-15 Lich %A5%EA%A5%C3%A5%C1%20%28SSR%29 3 5 6 2 5 FALSE 260 1300 1550 8600 FALSE FALSE {3184} {} {} 294327 SSR/リッチ
17 e9bb4639-d4f2-4299-b3ed-d396760a30eb Nier ニーア 3040169000 2019-03-10 Nier %A5%CB%A1%BC%A5%A2%20%28SSR%29 3 5 3 2 2 2 TRUE 200 1313 1513 1476 8906 10306 7 3 4.5 FALSE FALSE {3108} {} {} 2023-06-07 144747 SSR/ニーア

View file

@ -0,0 +1,9 @@
"id","granblue_id","name_en","name_jp","description_en","description_jp","created_at"
"3905ccba-fc56-44ef-890d-94b858ded339","6","Acuity's Guidebook","鋭撃の導本","Grants Stamina and more","渾身効果などを得られる","2023-04-17 23:19:19.425728"
"4285b593-31ff-45e3-bd96-55c972199753","8","Insight's Guidebook","啓示の導本","Improves debuff resistance and success rate","弱体効果に強くなる","2023-04-17 23:19:19.425728"
"794b2e5f-9eec-4d27-93ee-c7971eb25862","16","Shockwave's Guidebook","激震の導本","Greatly improves normal attacks","通常攻撃を大幅に強化する効果が得られる","2023-04-17 23:19:19.425728"
"8453e4e8-1c86-4a92-a164-41762e5f5e49","5","Tenebrosity's Guidebook","暗夜の導本","Improves multiattack rate and more","連続攻撃確率アップなどの効果が得られる","2023-04-17 23:19:19.425728"
"a35af3f7-3e37-46f5-9fef-615819b8492b","4","Valor's Guidebook","勇気の導本","Grant Bonus DMG effect and more","追撃などの効果が得られる","2023-04-17 23:19:19.425728"
"a9313de5-092c-4f72-a5bb-e7f09f550961","7","Fortitude's Guidebook","守護者の導本","Greatly improves survivability","耐久効果を多く得られる","2023-04-17 23:19:19.425728"
"af73e2ad-aae4-47dc-8f4e-c9c0d4225a84","15","Sanctum's Guidebook","聖域の導本","Greatly improves battle longevity","継戦能力が大きく高まる効果を得られる","2023-04-17 23:19:19.425728"
"bbd6368d-567c-4d23-aa75-c2fe2c6818ff","10","Adept's Guidebook","魔刃の導本","Improves skills","アビリティを強化する効果が得られる","2023-04-17 23:19:19.425728"
1 id granblue_id name_en name_jp description_en description_jp created_at
2 3905ccba-fc56-44ef-890d-94b858ded339 6 Acuity's Guidebook 鋭撃の導本 Grants Stamina and more 渾身効果などを得られる 2023-04-17 23:19:19.425728
3 4285b593-31ff-45e3-bd96-55c972199753 8 Insight's Guidebook 啓示の導本 Improves debuff resistance and success rate 弱体効果に強くなる 2023-04-17 23:19:19.425728
4 794b2e5f-9eec-4d27-93ee-c7971eb25862 16 Shockwave's Guidebook 激震の導本 Greatly improves normal attacks 通常攻撃を大幅に強化する効果が得られる 2023-04-17 23:19:19.425728
5 8453e4e8-1c86-4a92-a164-41762e5f5e49 5 Tenebrosity's Guidebook 暗夜の導本 Improves multiattack rate and more 連続攻撃確率アップなどの効果が得られる 2023-04-17 23:19:19.425728
6 a35af3f7-3e37-46f5-9fef-615819b8492b 4 Valor's Guidebook 勇気の導本 Grant Bonus DMG effect and more 追撃などの効果が得られる 2023-04-17 23:19:19.425728
7 a9313de5-092c-4f72-a5bb-e7f09f550961 7 Fortitude's Guidebook 守護者の導本 Greatly improves survivability 耐久効果を多く得られる 2023-04-17 23:19:19.425728
8 af73e2ad-aae4-47dc-8f4e-c9c0d4225a84 15 Sanctum's Guidebook 聖域の導本 Greatly improves battle longevity 継戦能力が大きく高まる効果を得られる 2023-04-17 23:19:19.425728
9 bbd6368d-567c-4d23-aa75-c2fe2c6818ff 10 Adept's Guidebook 魔刃の導本 Improves skills アビリティを強化する効果が得られる 2023-04-17 23:19:19.425728

View file

@ -0,0 +1,9 @@
"id","job_id","name_en","name_jp","granblue_id","rarity","release_date","accessory_type"
"32295cc2-c1ed-4e1b-9273-baa79262bf66","a5d6fca3-5649-4e12-a6db-5fcec49150ee","Bahamut Mino","バハムート・ミニステル","5",0,"2022-01-25",2
"32786311-6d8f-4b3b-99f7-7dd53343a0f3","a5d6fca3-5649-4e12-a6db-5fcec49150ee","Yggdrasil Mino","ユグドラシル・ミニステル","4",0,"2022-01-25",2
"824c06c8-0d4c-485a-9cc6-3e72e58a5588","a5d6fca3-5649-4e12-a6db-5fcec49150ee","Mini Mimic","ミニック","7",0,"2022-01-25",2
"8490d389-3f41-47f5-9ae5-c5bcf7f39965","a5d6fca3-5649-4e12-a6db-5fcec49150ee","Lu Woh Mino","ル・オー・ミニステル","6",0,"2022-01-25",2
"a2cf6934-deab-4082-8eb8-6ec3c9c0d53e","a5d6fca3-5649-4e12-a6db-5fcec49150ee","Ouroboros Mino","ウロボロス・ミニステル","8",3,"2022-09-06",2
"aee2ee5b-7847-45af-aab4-ba210a199bcb","a5d6fca3-5649-4e12-a6db-5fcec49150ee","Leviathan Mino","リヴァイアサン・ミニステル","3",0,"2022-01-25",2
"af013d1b-cc40-43ec-9d34-3a0ea0592e52","a5d6fca3-5649-4e12-a6db-5fcec49150ee","Burlona","ブルロネ","1",0,"2022-01-25",2
"dce5f041-b709-4cf4-aa71-bec44727d6ce","a5d6fca3-5649-4e12-a6db-5fcec49150ee","Schalk","シャルク","2",0,"2022-01-25",2
1 id job_id name_en name_jp granblue_id rarity release_date accessory_type
2 32295cc2-c1ed-4e1b-9273-baa79262bf66 a5d6fca3-5649-4e12-a6db-5fcec49150ee Bahamut Mino バハムート・ミニステル 5 0 2022-01-25 2
3 32786311-6d8f-4b3b-99f7-7dd53343a0f3 a5d6fca3-5649-4e12-a6db-5fcec49150ee Yggdrasil Mino ユグドラシル・ミニステル 4 0 2022-01-25 2
4 824c06c8-0d4c-485a-9cc6-3e72e58a5588 a5d6fca3-5649-4e12-a6db-5fcec49150ee Mini Mimic ミニック 7 0 2022-01-25 2
5 8490d389-3f41-47f5-9ae5-c5bcf7f39965 a5d6fca3-5649-4e12-a6db-5fcec49150ee Lu Woh Mino ル・オー・ミニステル 6 0 2022-01-25 2
6 a2cf6934-deab-4082-8eb8-6ec3c9c0d53e a5d6fca3-5649-4e12-a6db-5fcec49150ee Ouroboros Mino ウロボロス・ミニステル 8 3 2022-09-06 2
7 aee2ee5b-7847-45af-aab4-ba210a199bcb a5d6fca3-5649-4e12-a6db-5fcec49150ee Leviathan Mino リヴァイアサン・ミニステル 3 0 2022-01-25 2
8 af013d1b-cc40-43ec-9d34-3a0ea0592e52 a5d6fca3-5649-4e12-a6db-5fcec49150ee Burlona ブルロネ 1 0 2022-01-25 2
9 dce5f041-b709-4cf4-aa71-bec44727d6ce a5d6fca3-5649-4e12-a6db-5fcec49150ee Schalk シャルク 2 0 2022-01-25 2

View file

@ -0,0 +1,18 @@
"id","job_id","name_en","name_jp","slug","color","main","sub","emp","order","base"
"589d1718-887f-4837-9a7b-93e9ce33bbf3","a5d6fca3-5649-4e12-a6db-5fcec49150ee","Aether Siphon","エーテルサクション","aether-siphon",2,TRUE,FALSE,FALSE,0,FALSE
"b0fa1cbd-1761-49f7-b250-d601a98fddac","a5d6fca3-5649-4e12-a6db-5fcec49150ee","Wild Magica","ワイルドマギカ","wild-magica",2,FALSE,FALSE,TRUE,1,FALSE
"0cdd20ec-5869-4bff-8016-35a4a48e897a","a5d6fca3-5649-4e12-a6db-5fcec49150ee","Secret Triad","シークレットトライアド","secret-triad",0,FALSE,FALSE,TRUE,2,FALSE
"a42211a5-e7fd-4cdb-80a9-f2fb3ccce7f2","a5d6fca3-5649-4e12-a6db-5fcec49150ee","Overtrance","オーバートランス","overtrance",0,FALSE,FALSE,TRUE,3,FALSE
"b0a50aec-6f88-4a0f-900a-c26e84fd09c6","1eb55dd3-3278-4da1-8940-c4fc50c1a0f5","Full Arsenal III","ウェポンバーストIII","full-arsenal-iii",0,TRUE,FALSE,FALSE,0,FALSE
"fdfdee6d-6ead-4504-9add-a04776546b15","1eb55dd3-3278-4da1-8940-c4fc50c1a0f5","Armor Break II","レイジIV","armor-break-ii",2,FALSE,FALSE,TRUE,1,FALSE
"4df00bf2-aab1-4bd4-a399-fcad942c7daf","1eb55dd3-3278-4da1-8940-c4fc50c1a0f5","Rage IV","アーマーブレイクII","rage-iv",0,FALSE,FALSE,TRUE,2,FALSE
"e705ef94-4d70-4e24-b505-4a1e8b0038f0","1eb55dd3-3278-4da1-8940-c4fc50c1a0f5","Rampage II","ランページII","rampage-ii",0,FALSE,FALSE,TRUE,3,FALSE
"30df2315-457a-414c-9eef-3980b72b17c2","1eb55dd3-3278-4da1-8940-c4fc50c1a0f5","Ulfhedinn","ウールヴヘジン","ulfhedinn",0,FALSE,FALSE,TRUE,4,FALSE
"3b862283-c2b0-42ab-abf8-83f7b71d5fb5","1eb55dd3-3278-4da1-8940-c4fc50c1a0f5","Ferocious Roar","フェロシティロアー","ferocious-roar",0,FALSE,FALSE,TRUE,5,FALSE
"a1491902-838f-4e7d-8a4a-572a5653537f","1eb55dd3-3278-4da1-8940-c4fc50c1a0f5","Beast Fang","ビーストファング","beast-fang",2,FALSE,FALSE,TRUE,6,FALSE
"0d2987b1-2322-48b6-a071-e6b60699889b","1eb55dd3-3278-4da1-8940-c4fc50c1a0f5","Bloodzerker","狂戦の血","bloodzerker",0,FALSE,FALSE,TRUE,7,FALSE
"e76fbe2a-4dc7-4c29-9db7-7feee06559fb","43652444-64bb-4938-85d7-aafdfc503d66","Miserable Mist","ミゼラブルミスト","miserable-mist",1,TRUE,TRUE,FALSE,1,FALSE
"37218a55-3335-4457-98c3-4d8367af3d7c","d27a4f29-5f0b-4bc6-b75a-1bd187e1a529","Splitting Spirit","他心陣","splitting-spirit",0,TRUE,TRUE,FALSE,2,FALSE
"61e9a862-41f1-477a-9131-72b366c359be","2abbab55-5bf7-49f8-9ed6-1fe8a3991cca","Clarity","クリアオール","clarity",3,TRUE,TRUE,FALSE,1,FALSE
"4a00259a-9e2b-4239-bca2-2afdc2f52be7","c128944b-cc79-45b4-bfed-17c8b68db612","Dispel","ディスペル","dispel",1,TRUE,TRUE,FALSE,1,FALSE
"67a126d1-5515-492f-aeaf-7f88b25e2e26","667bf041-61c9-4568-bdd3-ce6e43f40603","Dark Haze","ブラックヘイズ","dark-haze",1,FALSE,FALSE,TRUE,1,FALSE
1 id job_id name_en name_jp slug color main sub emp order base
2 589d1718-887f-4837-9a7b-93e9ce33bbf3 a5d6fca3-5649-4e12-a6db-5fcec49150ee Aether Siphon エーテルサクション aether-siphon 2 TRUE FALSE FALSE 0 FALSE
3 b0fa1cbd-1761-49f7-b250-d601a98fddac a5d6fca3-5649-4e12-a6db-5fcec49150ee Wild Magica ワイルドマギカ wild-magica 2 FALSE FALSE TRUE 1 FALSE
4 0cdd20ec-5869-4bff-8016-35a4a48e897a a5d6fca3-5649-4e12-a6db-5fcec49150ee Secret Triad シークレットトライアド secret-triad 0 FALSE FALSE TRUE 2 FALSE
5 a42211a5-e7fd-4cdb-80a9-f2fb3ccce7f2 a5d6fca3-5649-4e12-a6db-5fcec49150ee Overtrance オーバートランス overtrance 0 FALSE FALSE TRUE 3 FALSE
6 b0a50aec-6f88-4a0f-900a-c26e84fd09c6 1eb55dd3-3278-4da1-8940-c4fc50c1a0f5 Full Arsenal III ウェポンバーストIII full-arsenal-iii 0 TRUE FALSE FALSE 0 FALSE
7 fdfdee6d-6ead-4504-9add-a04776546b15 1eb55dd3-3278-4da1-8940-c4fc50c1a0f5 Armor Break II レイジIV armor-break-ii 2 FALSE FALSE TRUE 1 FALSE
8 4df00bf2-aab1-4bd4-a399-fcad942c7daf 1eb55dd3-3278-4da1-8940-c4fc50c1a0f5 Rage IV アーマーブレイクII rage-iv 0 FALSE FALSE TRUE 2 FALSE
9 e705ef94-4d70-4e24-b505-4a1e8b0038f0 1eb55dd3-3278-4da1-8940-c4fc50c1a0f5 Rampage II ランページII rampage-ii 0 FALSE FALSE TRUE 3 FALSE
10 30df2315-457a-414c-9eef-3980b72b17c2 1eb55dd3-3278-4da1-8940-c4fc50c1a0f5 Ulfhedinn ウールヴヘジン ulfhedinn 0 FALSE FALSE TRUE 4 FALSE
11 3b862283-c2b0-42ab-abf8-83f7b71d5fb5 1eb55dd3-3278-4da1-8940-c4fc50c1a0f5 Ferocious Roar フェロシティロアー ferocious-roar 0 FALSE FALSE TRUE 5 FALSE
12 a1491902-838f-4e7d-8a4a-572a5653537f 1eb55dd3-3278-4da1-8940-c4fc50c1a0f5 Beast Fang ビーストファング beast-fang 2 FALSE FALSE TRUE 6 FALSE
13 0d2987b1-2322-48b6-a071-e6b60699889b 1eb55dd3-3278-4da1-8940-c4fc50c1a0f5 Bloodzerker 狂戦の血 bloodzerker 0 FALSE FALSE TRUE 7 FALSE
14 e76fbe2a-4dc7-4c29-9db7-7feee06559fb 43652444-64bb-4938-85d7-aafdfc503d66 Miserable Mist ミゼラブルミスト miserable-mist 1 TRUE TRUE FALSE 1 FALSE
15 37218a55-3335-4457-98c3-4d8367af3d7c d27a4f29-5f0b-4bc6-b75a-1bd187e1a529 Splitting Spirit 他心陣 splitting-spirit 0 TRUE TRUE FALSE 2 FALSE
16 61e9a862-41f1-477a-9131-72b366c359be 2abbab55-5bf7-49f8-9ed6-1fe8a3991cca Clarity クリアオール clarity 3 TRUE TRUE FALSE 1 FALSE
17 4a00259a-9e2b-4239-bca2-2afdc2f52be7 c128944b-cc79-45b4-bfed-17c8b68db612 Dispel ディスペル dispel 1 TRUE TRUE FALSE 1 FALSE
18 67a126d1-5515-492f-aeaf-7f88b25e2e26 667bf041-61c9-4568-bdd3-ce6e43f40603 Dark Haze ブラックヘイズ dark-haze 1 FALSE FALSE TRUE 1 FALSE

View file

@ -0,0 +1,15 @@
"id","name_en","name_jp","proficiency1","proficiency2","row","master_level","order","base_job_id","granblue_id","accessory","accessory_type","ultimate_mastery"
"67899c05-e73a-43ee-a83b-30fcd6e8ccf8","Fighter","ファイター",1,3,"1",FALSE,1,"67899c05-e73a-43ee-a83b-30fcd6e8ccf8","100001",FALSE,0,FALSE
"56aa0f3e-5cc1-49e7-a12d-a4d506064c9a","Warrior","ウオーリア",1,3,"2",FALSE,1,"67899c05-e73a-43ee-a83b-30fcd6e8ccf8","100001",FALSE,0,FALSE
"6283eb60-234f-4851-8cc7-7ea36e42def2","Weapon Master","ウェポンマスター",1,3,"3",FALSE,1,"67899c05-e73a-43ee-a83b-30fcd6e8ccf8","100201",FALSE,0,FALSE
"0e0c149d-8021-4f1e-a9d4-e2c77fd9e59a","Viking","ヴァイキング",1,3,"5",FALSE,1,"67899c05-e73a-43ee-a83b-30fcd6e8ccf8","100401",FALSE,0,FALSE
"2b0cfead-50b3-4acd-8cb0-18aab243fdd1","Wizard","ウィザード",6,2,"1",FALSE,4,"2b0cfead-50b3-4acd-8cb0-18aab243fdd1","130001",FALSE,0,FALSE
"0b536736-669c-48d2-9b3c-a450f5183951","Sorcerer","ソーサラー",6,2,"2",FALSE,4,"2b0cfead-50b3-4acd-8cb0-18aab243fdd1","130001",FALSE,0,FALSE
"667bf041-61c9-4568-bdd3-ce6e43f40603","Warlock","ウォーロック",6,2,"4",TRUE,4,"2b0cfead-50b3-4acd-8cb0-18aab243fdd1","130301",FALSE,0,TRUE
"1eb55dd3-3278-4da1-8940-c4fc50c1a0f5","Berserker","ベルセルク",1,3,"4",TRUE,1,"67899c05-e73a-43ee-a83b-30fcd6e8ccf8","100301",FALSE,0,TRUE
"a5d6fca3-5649-4e12-a6db-5fcec49150ee","Manadiver","マナダイバー",6,2,"5",FALSE,4,"2b0cfead-50b3-4acd-8cb0-18aab243fdd1","130401",TRUE,2,FALSE
"43652444-64bb-4938-85d7-aafdfc503d66","Dark Fencer","ダークフェンサー",1,2,"3",FALSE,6,"21dff3a3-22bc-4863-9861-af0a1b41a5f0","150201",FALSE,0,FALSE
"d27a4f29-5f0b-4bc6-b75a-1bd187e1a529","Mystic","賢者",6,6,"ex1",FALSE,6,"d27a4f29-5f0b-4bc6-b75a-1bd187e1a529","250201",FALSE,0,FALSE
"2abbab55-5bf7-49f8-9ed6-1fe8a3991cca","Cleric","クレリック",6,4,"2",FALSE,3,"950f659b-0521-4a79-b578-7f3b05cb3102","120001",FALSE,0,FALSE
"667bf041-61c9-4568-bdd3-ce6e43f40603","Warlock","ウォーロック",6,2,"4",TRUE,4,"2b0cfead-50b3-4acd-8cb0-18aab243fdd1","130301",FALSE,0,TRUE
"c128944b-cc79-45b4-bfed-17c8b68db612","Bishop","ビショップ",6,4,"3",FALSE,3,"950f659b-0521-4a79-b578-7f3b05cb3102","120201",FALSE,0,FALSE
1 id name_en name_jp proficiency1 proficiency2 row master_level order base_job_id granblue_id accessory accessory_type ultimate_mastery
2 67899c05-e73a-43ee-a83b-30fcd6e8ccf8 Fighter ファイター 1 3 1 FALSE 1 67899c05-e73a-43ee-a83b-30fcd6e8ccf8 100001 FALSE 0 FALSE
3 56aa0f3e-5cc1-49e7-a12d-a4d506064c9a Warrior ウオーリア 1 3 2 FALSE 1 67899c05-e73a-43ee-a83b-30fcd6e8ccf8 100001 FALSE 0 FALSE
4 6283eb60-234f-4851-8cc7-7ea36e42def2 Weapon Master ウェポンマスター 1 3 3 FALSE 1 67899c05-e73a-43ee-a83b-30fcd6e8ccf8 100201 FALSE 0 FALSE
5 0e0c149d-8021-4f1e-a9d4-e2c77fd9e59a Viking ヴァイキング 1 3 5 FALSE 1 67899c05-e73a-43ee-a83b-30fcd6e8ccf8 100401 FALSE 0 FALSE
6 2b0cfead-50b3-4acd-8cb0-18aab243fdd1 Wizard ウィザード 6 2 1 FALSE 4 2b0cfead-50b3-4acd-8cb0-18aab243fdd1 130001 FALSE 0 FALSE
7 0b536736-669c-48d2-9b3c-a450f5183951 Sorcerer ソーサラー 6 2 2 FALSE 4 2b0cfead-50b3-4acd-8cb0-18aab243fdd1 130001 FALSE 0 FALSE
8 667bf041-61c9-4568-bdd3-ce6e43f40603 Warlock ウォーロック 6 2 4 TRUE 4 2b0cfead-50b3-4acd-8cb0-18aab243fdd1 130301 FALSE 0 TRUE
9 1eb55dd3-3278-4da1-8940-c4fc50c1a0f5 Berserker ベルセルク 1 3 4 TRUE 1 67899c05-e73a-43ee-a83b-30fcd6e8ccf8 100301 FALSE 0 TRUE
10 a5d6fca3-5649-4e12-a6db-5fcec49150ee Manadiver マナダイバー 6 2 5 FALSE 4 2b0cfead-50b3-4acd-8cb0-18aab243fdd1 130401 TRUE 2 FALSE
11 43652444-64bb-4938-85d7-aafdfc503d66 Dark Fencer ダークフェンサー 1 2 3 FALSE 6 21dff3a3-22bc-4863-9861-af0a1b41a5f0 150201 FALSE 0 FALSE
12 d27a4f29-5f0b-4bc6-b75a-1bd187e1a529 Mystic 賢者 6 6 ex1 FALSE 6 d27a4f29-5f0b-4bc6-b75a-1bd187e1a529 250201 FALSE 0 FALSE
13 2abbab55-5bf7-49f8-9ed6-1fe8a3991cca Cleric クレリック 6 4 2 FALSE 3 950f659b-0521-4a79-b578-7f3b05cb3102 120001 FALSE 0 FALSE
14 667bf041-61c9-4568-bdd3-ce6e43f40603 Warlock ウォーロック 6 2 4 TRUE 4 2b0cfead-50b3-4acd-8cb0-18aab243fdd1 130301 FALSE 0 TRUE
15 c128944b-cc79-45b4-bfed-17c8b68db612 Bishop ビショップ 6 4 3 FALSE 3 950f659b-0521-4a79-b578-7f3b05cb3102 120201 FALSE 0 FALSE

View file

@ -8,3 +8,16 @@
"0156d098-f5e7-4d72-8d14-34e6adff9280","The Moon","ザ・ムーン","2040243000",3,3,"6",TRUE,TRUE,200,110,668,952,1094,110,668,952,1094,TRUE,FALSE,FALSE,,,,"2017-11-29","2018-03-22","2019-03-10","The Moon (SSR)","%BE%A4%B4%AD%C0%D0%2F%A5%B6%A1%A6%A5%E0%A1%BC%A5%F3%20%28SSR%29","81835","SSR/ザ・ムーン",,"{}","{}"
"05214d59-2765-40c3-9a1d-6c29c6bdc6d6","Colossus Omega","コロッサス・マグナ","2040034000",3,2,"2",TRUE,TRUE,200,103,648,778,921,275,1635,1965,2315,TRUE,FALSE,TRUE,2665,1064,2001,"2014-10-08","2018-03-10","2020-08-08","Colossus Omega","%BE%A4%B4%AD%C0%D0%2F%A5%B3%A5%ED%A5%C3%A5%B5%A5%B9%A1%A6%A5%DE%A5%B0%A5%CA%20%28SSR%29","21736","SSR/コロッサス・マグナ","2024-05-02","{}","{}"
"d27d0d9a-3b38-4dcd-89a5-4016c2906249","Bahamut","バハムート","2040003000",3,5,"0",TRUE,TRUE,200,140,850,1210,1390,140,850,1210,1390,TRUE,FALSE,TRUE,,,,"2014-04-30","2017-03-10","2019-03-22","Bahamut","%BE%A4%B4%AD%C0%D0%2F%A5%D0%A5%CF%A5%E0%A1%BC%A5%C8%20%28SSR%29","21612","SSR/バハムート",,"{}","{}"
"fbeaa551-7ea1-48f3-982f-f4569d14fb45","Agni","アグニス","2040094000",3,2,"3",TRUE,TRUE,250,127,770,1092,1253,127,770,1092,3685,TRUE,FALSE,TRUE,,,,"2015-09-30","2019-08-27","2021-03-22","Agni","%BE%A4%B4%AD%C0%D0%2F%A5%A2%A5%B0%A5%CB%A5%B9%20%28SSR%29","147663","SSR/アグニス",,"{}","{}"
"04302038-a8dc-4860-a09a-257c0a6ac2a9","Lucifer","ルシフェル","2040056000",3,6,"0",TRUE,TRUE,250,136,900,1280,1470,136,900,1280,1470,FALSE,FALSE,TRUE,,,,"2014-12-31","2017-03-10","2019-03-22","Lucifer","%BE%A4%B4%AD%C0%D0%2F%A5%EB%A5%B7%A5%D5%A5%A7%A5%EB%20%28SSR%29","21599",,,"{}","{}"
"49bd4739-486a-4eca-990d-88431279ac3a","Qilin","黒麒麟","2040158000",3,5,,FALSE,FALSE,100,107,649,,,107,649,,,FALSE,FALSE,FALSE,,,,"2016-06-23",,,"Qilin",,,,,"{}","{}"
"4f6b3ccd-c906-43d6-9720-8328317cf6b2","The Sun","ザ・サン","2040244000",3,2,"6",TRUE,TRUE,200,106,664,948,1090,106,664,948,1090,TRUE,FALSE,FALSE,,,,"2017-11-29","2018-03-22","2019-03-10","The Sun (SSR)","%BE%A4%B4%AD%C0%D0%2F%A5%B6%A1%A6%A5%B5%A5%F3%20%28SSR%29","81834","SSR/ザ・サン",,"{}","{}"
"5c007586-588b-4eea-9bc5-d099f94af737","Wilnas","ウィルナス","2040398000",3,2,"12",TRUE,FALSE,100,127,771,1093,,399,2349,3324,,TRUE,FALSE,FALSE,,,,"2022-01-31",,,"Wilnas","%A5%A6%A5%A3%A5%EB%A5%CA%A5%B9%20%28SSR%29","317298","SSR/ウィルナス",,"{}","{}"
"597d6c56-73e3-424f-9971-8a237700fe08","Michael","ミカエル","2040306000",3,2,"5",TRUE,FALSE,150,129,832,1184,,359,2240,3181,,TRUE,FALSE,FALSE,,,,"2022-12-31",,,"Michael","%A5%DF%A5%AB%A5%A8%A5%EB%20%28SSR%29","381021","SSR/ミカエル",,"{}","{}"
"ad21f1d7-2b0a-4cd6-beb7-ce624d381f36","Triple Zero","トリプルゼロ","2040425000",3,6,"0",TRUE,FALSE,150,130,1140,,,367,2947,,,TRUE,TRUE,FALSE,,,,"2023-12-31","2023-12-31",,"Triple Zero","","","",,"{}","{}"
"1e3be2f2-803c-4cff-802d-785f3b682cfb","Hades","ハデス","2040090000",3,5,"3",TRUE,TRUE,250,123,755,1071,1229,123,755,1071,3798,TRUE,FALSE,TRUE,,,,"2015-08-31","2019-08-27","2021-03-22","Hades","%BE%A4%B4%AD%C0%D0%2F%A5%CF%A5%C7%A5%B9%20%28SSR%29","147575","SSR/ハデス",,"{}","{}"
"d27d0d9a-3b38-4dcd-89a5-4016c2906249","Bahamut","バハムート","2040003000",3,5,"0",TRUE,TRUE,200,140,850,1210,1390,140,850,1210,1390,TRUE,FALSE,TRUE,,,,"2014-04-30","2017-03-10","2019-03-22","Bahamut","%BE%A4%B4%AD%C0%D0%2F%A5%D0%A5%CF%A5%E0%A1%BC%A5%C8%20%28SSR%29","21612","SSR/バハムート",,"{}","{}"
"0fcfa02d-879f-4166-bc70-1f86a99a45ca","Sariel","サリエル","2040327000",3,5,"5",TRUE,FALSE,150,132,790,1119,,357,2155,3054,,TRUE,FALSE,FALSE,,,,"2019-04-30","2022-03-24",,"Sariel","%BE%A4%B4%AD%C0%D0%2F%A5%B5%A5%EA%A5%A8%A5%EB%20%28SSR%29","149228","SSR/サリエル",,"{}","{}"
"b203a9bc-9453-4090-91cc-84532b709d58","Zirnitra","ジルニトラ","2040385000",3,5,,FALSE,FALSE,100,128,806,,,128,806,,,TRUE,FALSE,FALSE,,,,"2020-09-30",,,"Zirnitra",,"230823",,,"{}","{}"
"83a6dfcb-0b74-4354-948c-2cff49b5b2b9","Fediel","フェディエル","2040418000",3,5,"12",TRUE,FALSE,150,132,810,1149,,374,2154,3044,,TRUE,FALSE,FALSE,,,,"2021-12-31",,,"Fediel","%A5%D5%A5%A7%A5%C7%A5%A3%A5%A8%A5%EB%20%28SSR%29","311659","SSR/フェディエル",,"{}","{}"
"fbde7c76-be0c-4a42-8479-046ed9715db9","Death","デス","2040238000",3,5,"6",TRUE,TRUE,200,109,695,984,1128,109,695,984,1128,TRUE,FALSE,FALSE,,,,"2017-11-29","2018-03-22","2019-03-10","Death (SSR)","%BE%A4%B4%AD%C0%D0%2F%A5%C7%A5%B9%20%28SSR%29","81843","SSR/デス",,"{}","{}"

1 id name_en name_jp granblue_id rarity element series flb ulb max_level min_hp max_hp max_hp_flb max_hp_ulb min_atk max_atk max_atk_flb max_atk_ulb subaura limit transcendence max_atk_xlb max_hp_xlb summon_id release_date flb_date ulb_date wiki_en wiki_ja gamewith kamigame transcendence_date nicknames_en nicknames_jp
8 0156d098-f5e7-4d72-8d14-34e6adff9280 The Moon ザ・ムーン 2040243000 3 3 6 TRUE TRUE 200 110 668 952 1094 110 668 952 1094 TRUE FALSE FALSE 2017-11-29 2018-03-22 2019-03-10 The Moon (SSR) %BE%A4%B4%AD%C0%D0%2F%A5%B6%A1%A6%A5%E0%A1%BC%A5%F3%20%28SSR%29 81835 SSR/ザ・ムーン {} {}
9 05214d59-2765-40c3-9a1d-6c29c6bdc6d6 Colossus Omega コロッサス・マグナ 2040034000 3 2 2 TRUE TRUE 200 103 648 778 921 275 1635 1965 2315 TRUE FALSE TRUE 2665 1064 2001 2014-10-08 2018-03-10 2020-08-08 Colossus Omega %BE%A4%B4%AD%C0%D0%2F%A5%B3%A5%ED%A5%C3%A5%B5%A5%B9%A1%A6%A5%DE%A5%B0%A5%CA%20%28SSR%29 21736 SSR/コロッサス・マグナ 2024-05-02 {} {}
10 d27d0d9a-3b38-4dcd-89a5-4016c2906249 Bahamut バハムート 2040003000 3 5 0 TRUE TRUE 200 140 850 1210 1390 140 850 1210 1390 TRUE FALSE TRUE 2014-04-30 2017-03-10 2019-03-22 Bahamut %BE%A4%B4%AD%C0%D0%2F%A5%D0%A5%CF%A5%E0%A1%BC%A5%C8%20%28SSR%29 21612 SSR/バハムート {} {}
11 fbeaa551-7ea1-48f3-982f-f4569d14fb45 Agni アグニス 2040094000 3 2 3 TRUE TRUE 250 127 770 1092 1253 127 770 1092 3685 TRUE FALSE TRUE 2015-09-30 2019-08-27 2021-03-22 Agni %BE%A4%B4%AD%C0%D0%2F%A5%A2%A5%B0%A5%CB%A5%B9%20%28SSR%29 147663 SSR/アグニス {} {}
12 04302038-a8dc-4860-a09a-257c0a6ac2a9 Lucifer ルシフェル 2040056000 3 6 0 TRUE TRUE 250 136 900 1280 1470 136 900 1280 1470 FALSE FALSE TRUE 2014-12-31 2017-03-10 2019-03-22 Lucifer %BE%A4%B4%AD%C0%D0%2F%A5%EB%A5%B7%A5%D5%A5%A7%A5%EB%20%28SSR%29 21599 {} {}
13 49bd4739-486a-4eca-990d-88431279ac3a Qilin 黒麒麟 2040158000 3 5 FALSE FALSE 100 107 649 107 649 FALSE FALSE FALSE 2016-06-23 Qilin {} {}
14 4f6b3ccd-c906-43d6-9720-8328317cf6b2 The Sun ザ・サン 2040244000 3 2 6 TRUE TRUE 200 106 664 948 1090 106 664 948 1090 TRUE FALSE FALSE 2017-11-29 2018-03-22 2019-03-10 The Sun (SSR) %BE%A4%B4%AD%C0%D0%2F%A5%B6%A1%A6%A5%B5%A5%F3%20%28SSR%29 81834 SSR/ザ・サン {} {}
15 5c007586-588b-4eea-9bc5-d099f94af737 Wilnas ウィルナス 2040398000 3 2 12 TRUE FALSE 100 127 771 1093 399 2349 3324 TRUE FALSE FALSE 2022-01-31 Wilnas %A5%A6%A5%A3%A5%EB%A5%CA%A5%B9%20%28SSR%29 317298 SSR/ウィルナス {} {}
16 597d6c56-73e3-424f-9971-8a237700fe08 Michael ミカエル 2040306000 3 2 5 TRUE FALSE 150 129 832 1184 359 2240 3181 TRUE FALSE FALSE 2022-12-31 Michael %A5%DF%A5%AB%A5%A8%A5%EB%20%28SSR%29 381021 SSR/ミカエル {} {}
17 ad21f1d7-2b0a-4cd6-beb7-ce624d381f36 Triple Zero トリプルゼロ 2040425000 3 6 0 TRUE FALSE 150 130 1140 367 2947 TRUE TRUE FALSE 2023-12-31 2023-12-31 Triple Zero {} {}
18 1e3be2f2-803c-4cff-802d-785f3b682cfb Hades ハデス 2040090000 3 5 3 TRUE TRUE 250 123 755 1071 1229 123 755 1071 3798 TRUE FALSE TRUE 2015-08-31 2019-08-27 2021-03-22 Hades %BE%A4%B4%AD%C0%D0%2F%A5%CF%A5%C7%A5%B9%20%28SSR%29 147575 SSR/ハデス {} {}
19 d27d0d9a-3b38-4dcd-89a5-4016c2906249 Bahamut バハムート 2040003000 3 5 0 TRUE TRUE 200 140 850 1210 1390 140 850 1210 1390 TRUE FALSE TRUE 2014-04-30 2017-03-10 2019-03-22 Bahamut %BE%A4%B4%AD%C0%D0%2F%A5%D0%A5%CF%A5%E0%A1%BC%A5%C8%20%28SSR%29 21612 SSR/バハムート {} {}
20 0fcfa02d-879f-4166-bc70-1f86a99a45ca Sariel サリエル 2040327000 3 5 5 TRUE FALSE 150 132 790 1119 357 2155 3054 TRUE FALSE FALSE 2019-04-30 2022-03-24 Sariel %BE%A4%B4%AD%C0%D0%2F%A5%B5%A5%EA%A5%A8%A5%EB%20%28SSR%29 149228 SSR/サリエル {} {}
21 b203a9bc-9453-4090-91cc-84532b709d58 Zirnitra ジルニトラ 2040385000 3 5 FALSE FALSE 100 128 806 128 806 TRUE FALSE FALSE 2020-09-30 Zirnitra 230823 {} {}
22 83a6dfcb-0b74-4354-948c-2cff49b5b2b9 Fediel フェディエル 2040418000 3 5 12 TRUE FALSE 150 132 810 1149 374 2154 3044 TRUE FALSE FALSE 2021-12-31 Fediel %A5%D5%A5%A7%A5%C7%A5%A3%A5%A8%A5%EB%20%28SSR%29 311659 SSR/フェディエル {} {}
23 fbde7c76-be0c-4a42-8479-046ed9715db9 Death デス 2040238000 3 5 6 TRUE TRUE 200 109 695 984 1128 109 695 984 1128 TRUE FALSE FALSE 2017-11-29 2018-03-22 2019-03-10 Death (SSR) %BE%A4%B4%AD%C0%D0%2F%A5%C7%A5%B9%20%28SSR%29 81843 SSR/デス {} {}

View file

@ -0,0 +1,14 @@
"id","weapon_id","awakening_id"
"3f8be70e-db9f-41c0-91a7-b07cca9ed263","706438c4-a5eb-4f7c-a145-0c2a3e7e6fbe","18ab5007-3fcb-4f83-a7a0-879a9a4a7ad7"
"59af97e7-8828-432e-9ff7-b2c792d08d70","ba7af3b3-c62f-4f85-a420-0321c776ba00","275c9de5-db1e-4c66-8210-660505fd1af4"
"97c4b396-597f-4622-9f6d-ee9536a6629b","ba7af3b3-c62f-4f85-a420-0321c776ba00","d691a61c-dc7e-4d92-a8e6-98c04608353c"
"b6b911bb-ee89-435f-b325-9df53a1ce6ea","ba7af3b3-c62f-4f85-a420-0321c776ba00","969d37db-5f14-4d1a-bef4-59ba5a016674"
"1dbff135-b401-4619-973d-740f4504ee3a","a2f0db22-baf1-4640-8c2e-6d283375744f","d691a61c-dc7e-4d92-a8e6-98c04608353c"
"d48fd874-484d-41c5-bff0-709cb714f7b0","a2f0db22-baf1-4640-8c2e-6d283375744f","275c9de5-db1e-4c66-8210-660505fd1af4"
"e793cc76-025d-4b6d-975a-58c56ff19141","47208685-e87a-4e07-b328-fb9ac3888718","d691a61c-dc7e-4d92-a8e6-98c04608353c"
"42ba1467-971e-40bd-b701-07538678cc95","e7a05d2e-a3ec-4620-98a5-d8472d474971","d691a61c-dc7e-4d92-a8e6-98c04608353c"
"6e94080f-1bbf-4171-8d77-40328c1daf1f","e7a05d2e-a3ec-4620-98a5-d8472d474971","969d37db-5f14-4d1a-bef4-59ba5a016674"
"714e3575-d536-4a77-870b-b5e2d8b31b68","e7a05d2e-a3ec-4620-98a5-d8472d474971","275c9de5-db1e-4c66-8210-660505fd1af4"
"5daffb43-f456-41db-8e04-dadc42bea788","8137294e-6bf1-4bac-a1e0-38cdc542622b","d691a61c-dc7e-4d92-a8e6-98c04608353c"
"ab83344b-b4ee-4aad-8e9b-1b7a8169575b","8137294e-6bf1-4bac-a1e0-38cdc542622b","275c9de5-db1e-4c66-8210-660505fd1af4"
"e26dbd37-b4d1-49f2-a5f2-36525a57b998","8137294e-6bf1-4bac-a1e0-38cdc542622b","969d37db-5f14-4d1a-bef4-59ba5a016674"
1 id weapon_id awakening_id
2 3f8be70e-db9f-41c0-91a7-b07cca9ed263 706438c4-a5eb-4f7c-a145-0c2a3e7e6fbe 18ab5007-3fcb-4f83-a7a0-879a9a4a7ad7
3 59af97e7-8828-432e-9ff7-b2c792d08d70 ba7af3b3-c62f-4f85-a420-0321c776ba00 275c9de5-db1e-4c66-8210-660505fd1af4
4 97c4b396-597f-4622-9f6d-ee9536a6629b ba7af3b3-c62f-4f85-a420-0321c776ba00 d691a61c-dc7e-4d92-a8e6-98c04608353c
5 b6b911bb-ee89-435f-b325-9df53a1ce6ea ba7af3b3-c62f-4f85-a420-0321c776ba00 969d37db-5f14-4d1a-bef4-59ba5a016674
6 1dbff135-b401-4619-973d-740f4504ee3a a2f0db22-baf1-4640-8c2e-6d283375744f d691a61c-dc7e-4d92-a8e6-98c04608353c
7 d48fd874-484d-41c5-bff0-709cb714f7b0 a2f0db22-baf1-4640-8c2e-6d283375744f 275c9de5-db1e-4c66-8210-660505fd1af4
8 e793cc76-025d-4b6d-975a-58c56ff19141 47208685-e87a-4e07-b328-fb9ac3888718 d691a61c-dc7e-4d92-a8e6-98c04608353c
9 42ba1467-971e-40bd-b701-07538678cc95 e7a05d2e-a3ec-4620-98a5-d8472d474971 d691a61c-dc7e-4d92-a8e6-98c04608353c
10 6e94080f-1bbf-4171-8d77-40328c1daf1f e7a05d2e-a3ec-4620-98a5-d8472d474971 969d37db-5f14-4d1a-bef4-59ba5a016674
11 714e3575-d536-4a77-870b-b5e2d8b31b68 e7a05d2e-a3ec-4620-98a5-d8472d474971 275c9de5-db1e-4c66-8210-660505fd1af4
12 5daffb43-f456-41db-8e04-dadc42bea788 8137294e-6bf1-4bac-a1e0-38cdc542622b d691a61c-dc7e-4d92-a8e6-98c04608353c
13 ab83344b-b4ee-4aad-8e9b-1b7a8169575b 8137294e-6bf1-4bac-a1e0-38cdc542622b 275c9de5-db1e-4c66-8210-660505fd1af4
14 e26dbd37-b4d1-49f2-a5f2-36525a57b998 8137294e-6bf1-4bac-a1e0-38cdc542622b 969d37db-5f14-4d1a-bef4-59ba5a016674

View file

@ -0,0 +1,47 @@
"id","name_en","name_jp","slot","group","order","slug","granblue_id","series"
"02b40c48-b0d4-4df6-a27f-da2bc58fdd0f","Pendulum of Strife","闘争のペンデュラム",1,0,2,"pendulum-strife",14003,"{3}"
"0946e421-db65-403b-946f-5e2285e963f5","Pendulum of Sagacity","窮理のペンデュラム",1,2,2,"pendulum-sagacity",14006,"{3}"
"14534be3-defa-44cd-9096-09bae07565c8","Chain of Temperament","技錬のチェイン",1,1,0,"chain-temperament",14011,"{3}"
"1e2a1e5b-75f4-4e00-85d5-e5ef474dd6d7","Chain of Depravity","邪罪のチェイン",1,1,5,"chain-depravity",14016,"{3}"
"3faafaf1-5fc5-4aa8-8c65-894bbe1c615f","α Pendulum","アルファ・ペンデュラム",0,0,0,"pendulum-alpha",13001,"{3}"
"562c89bd-68cf-4a33-8609-d82e017130d6","Chain of Restoration","賦活のチェイン",1,1,1,"chain-restoration",14012,"{3}"
"5936e870-61a1-40a4-8c52-b85b9ab96967","Pendulum of Prosperity","隆盛のペンデュラム",1,0,3,"pendulum-prosperity",14004,"{3}"
"653477b7-5321-4ea4-8b6f-42218e67a090","Pendulum of Zeal","激情のペンデュラム",1,0,1,"pendulum-zeal",14002,"{3}"
"6ded911e-81d6-4fae-a3e7-682a5d18f2fc","Chain of Glorification","謳歌のチェイン",1,1,2,"chain-glorification",14013,"{3}"
"b3d8d4d8-8bf6-4e03-9f21-547653bf7574","Pendulum of Strength","強壮のペンデュラム",1,0,0,"pendulum-strength",14001,"{3}"
"c7a65d1f-c6a5-4c12-a90e-f3a31dc9d8f9","Pendulum of Extremity","絶涯のペンデュラム",1,2,1,"pendulum-extremity",14005,"{3}"
"d5b81056-fd58-45b6-b6ef-a43b45a15194","Chain of Temptation","誘惑のチェイン",1,1,3,"chain-temptation",14014,"{3}"
"d5ed9765-263e-4e28-b46a-a1f6bf8c6615","Pendulum of Supremacy","天髄のペンデュラム",1,2,3,"pendulum-supremacy",14007,"{3}"
"e719de37-500e-44cd-98a4-2d9af71e0809","Δ Pendulum","デルタ・ペンデュラム",0,0,4,"pendulum-delta",13004,"{3}"
"ebe424a0-7370-4b07-bd37-7eeee9b8425c","Chain of Falsehood","虚詐のチェイン",1,1,6,"chain-falsehood",14017,"{3}"
"ed19dcef-8579-4125-8607-5a43922d0999","β Pendulum","ベータ・ペンデュラム",0,0,1,"pendulum-beta",13002,"{3}"
"f5d711d8-f2f8-4909-9a64-ce6dc3584e03","γ Pendulum","ガンマ・ペンデュラム",0,0,2,"pendulum-gamma",13003,"{3}"
"f81ec8e8-acc8-4ad3-8460-b628e90cd29d","Chain of Forbiddance","禁忌のチェイン",1,1,4,"chain-forbiddance",14015,"{3}"
"0b696acb-baf4-4ad8-9caa-4255b338b13b","Gauph Key of Vitality","ガフスキー【生命】",0,3,2,"gauph-vitality",10003,"{13}"
"148e3323-395f-417c-b18a-96fd9421cfe6","Gauph Key of Strife","ガフスキー【闘争】",0,3,1,"gauph-strife",10002,"{13}"
"2ebe966e-0339-4464-acb9-0db138c3e2e7","Gauph Key of Will","ガフスキー【戦意】",0,3,0,"gauph-will",10001,"{13}"
"3ca1a71c-66bf-464a-8ad2-254c52169e8e","Gauph Key γ","ガフスキー【γ】",1,3,2,"gauph-gamma",11003,"{13}"
"3d5d610a-3734-444d-8818-fce2024a190b","Gauph Key Tria","ガフスキー【トリア】",2,3,2,"gauph-tria",17003,"{13}"
"4d6fefb6-09e6-4c92-98b0-a48b35ddd738","Gauph Key β","ガフスキー【β】",1,3,1,"gauph-beta",11002,"{13}"
"606632e3-3391-4223-8147-07060fe6f2e4","Gauph Key of Courage","ガフスキー【勇気】",0,3,5,"gauph-courage",10006,"{13}"
"6d03b9c2-08d8-49ea-8522-5507e9243ccc","Gauph Key α","ガフスキー【α】",1,3,0,"gauph-alpha",11001,"{13}"
"98a358bc-d123-40c9-8c0e-7953467c9a27","Gauph Key Δ","ガフスキー【Δ】",1,3,3,"gauph-delta",11004,"{13}"
"a1613dcd-dcc1-4290-95e7-3f9dfc28dd06","Gauph Key Tessera","ガフスキー【テーセラ】",2,3,3,"gauph-tessera",17004,"{13}"
"abd48244-8398-4159-ada6-9062803189f1","Gauph Key of Strength","ガフスキー【強壮】",0,3,3,"gauph-strength",10004,"{13}"
"cdd87f62-2d29-4698-b09d-8eef3f7b4406","Gauph Key Ena","ガフスキー【エナ】",2,3,0,"gauph-ena",17001,"{13}"
"d0dd2b46-cb55-4c2f-beb6-e2ee380bdb5e","Gauph Key Dio","ガフスキー【ディオ】",2,3,1,"gauph-dio",17002,"{13}"
"d6c0afdb-f6f3-4473-ada3-d505228ee348","Gauph Key of Zeal","ガフスキー【激情】",0,3,4,"gauph-zeal",10005,"{13}"
"44c2b0ba-642e-4edc-9680-1a34abe20418","Emblem of Devilry","魔獄のエンブレム",0,4,2,"emblem-devilry",3,"{19}"
"5ac2ad0a-f8da-403a-b098-7831d354f8e0","Emblem of Divinity","天聖のエンブレム",0,4,1,"emblem-divinity",2,"{19}"
"c2f1e5bc-9f8b-4af1-821c-2b32a9fb5f1f","Emblem of Humanity","英勇のエンブレム",0,4,0,"emblem-humanity",1,"{19}"
"0c6ce91c-864c-4c62-8c9b-be61e8fae47f","Optimus Teluma","オプティマス・テルマ",1,2,0,"teluma-optimus",16001,"{27,40}"
"1929bfa8-6bbd-4918-9ad7-594525b5e2c6","Crag Teluma","巨岩のテルマ",0,2,3,"teluma-crag",15004,"{27,40}"
"3fa65774-1ed1-4a16-86cd-9133adca2232","Omega Teluma","マグナ・テルマ",1,2,1,"teluma-omega",16002,"{27,40}"
"49f46e22-1796-435e-bce2-d9fdfe76d6c5","Tempest Teluma","暴風のテルマ",0,2,4,"teluma-tempest",15005,"{27,40}"
"81950efb-a4e1-4d45-8572-ddb604246212","Malice Teluma","闇禍のテルマ",0,2,6,"teluma-malice",15007,"{27,40}"
"d14e933e-630d-4cd6-9d61-dbdfd6e9332e","Abyss Teluma","深海のテルマ",0,2,2,"teluma-abyss",15003,"{27,40}"
"dc96edb7-8bee-4721-94c2-daa6508aaed8","Inferno Teluma","炎獄のテルマ",0,2,1,"teluma-inferno",15002,"{27,40}"
"e36950be-1ea9-4642-af94-164187e38e6c","Aureole Teluma","後光のテルマ",0,2,5,"teluma-aureole",15006,"{27,40}"
"ee80ff09-71c0-48bb-90ff-45e138df7481","Endurance Teluma","剛堅のテルマ",0,2,0,"teluma-endurance",15001,"{27,40}"
"b0b6d3be-7203-437e-8acd-2a59c2b5506a","Oblivion Teluma","冥烈のテルマ",0,2,8,"teluma-oblivion",15009,"{40}"
"d79558df-53fb-4c24-963b-e0b67040afc7","Salvation Teluma","燦護のテルマ",0,2,7,"teluma-salvation",15008,"{40}"
1 id name_en name_jp slot group order slug granblue_id series
2 02b40c48-b0d4-4df6-a27f-da2bc58fdd0f Pendulum of Strife 闘争のペンデュラム 1 0 2 pendulum-strife 14003 {3}
3 0946e421-db65-403b-946f-5e2285e963f5 Pendulum of Sagacity 窮理のペンデュラム 1 2 2 pendulum-sagacity 14006 {3}
4 14534be3-defa-44cd-9096-09bae07565c8 Chain of Temperament 技錬のチェイン 1 1 0 chain-temperament 14011 {3}
5 1e2a1e5b-75f4-4e00-85d5-e5ef474dd6d7 Chain of Depravity 邪罪のチェイン 1 1 5 chain-depravity 14016 {3}
6 3faafaf1-5fc5-4aa8-8c65-894bbe1c615f α Pendulum アルファ・ペンデュラム 0 0 0 pendulum-alpha 13001 {3}
7 562c89bd-68cf-4a33-8609-d82e017130d6 Chain of Restoration 賦活のチェイン 1 1 1 chain-restoration 14012 {3}
8 5936e870-61a1-40a4-8c52-b85b9ab96967 Pendulum of Prosperity 隆盛のペンデュラム 1 0 3 pendulum-prosperity 14004 {3}
9 653477b7-5321-4ea4-8b6f-42218e67a090 Pendulum of Zeal 激情のペンデュラム 1 0 1 pendulum-zeal 14002 {3}
10 6ded911e-81d6-4fae-a3e7-682a5d18f2fc Chain of Glorification 謳歌のチェイン 1 1 2 chain-glorification 14013 {3}
11 b3d8d4d8-8bf6-4e03-9f21-547653bf7574 Pendulum of Strength 強壮のペンデュラム 1 0 0 pendulum-strength 14001 {3}
12 c7a65d1f-c6a5-4c12-a90e-f3a31dc9d8f9 Pendulum of Extremity 絶涯のペンデュラム 1 2 1 pendulum-extremity 14005 {3}
13 d5b81056-fd58-45b6-b6ef-a43b45a15194 Chain of Temptation 誘惑のチェイン 1 1 3 chain-temptation 14014 {3}
14 d5ed9765-263e-4e28-b46a-a1f6bf8c6615 Pendulum of Supremacy 天髄のペンデュラム 1 2 3 pendulum-supremacy 14007 {3}
15 e719de37-500e-44cd-98a4-2d9af71e0809 Δ Pendulum デルタ・ペンデュラム 0 0 4 pendulum-delta 13004 {3}
16 ebe424a0-7370-4b07-bd37-7eeee9b8425c Chain of Falsehood 虚詐のチェイン 1 1 6 chain-falsehood 14017 {3}
17 ed19dcef-8579-4125-8607-5a43922d0999 β Pendulum ベータ・ペンデュラム 0 0 1 pendulum-beta 13002 {3}
18 f5d711d8-f2f8-4909-9a64-ce6dc3584e03 γ Pendulum ガンマ・ペンデュラム 0 0 2 pendulum-gamma 13003 {3}
19 f81ec8e8-acc8-4ad3-8460-b628e90cd29d Chain of Forbiddance 禁忌のチェイン 1 1 4 chain-forbiddance 14015 {3}
20 0b696acb-baf4-4ad8-9caa-4255b338b13b Gauph Key of Vitality ガフスキー【生命】 0 3 2 gauph-vitality 10003 {13}
21 148e3323-395f-417c-b18a-96fd9421cfe6 Gauph Key of Strife ガフスキー【闘争】 0 3 1 gauph-strife 10002 {13}
22 2ebe966e-0339-4464-acb9-0db138c3e2e7 Gauph Key of Will ガフスキー【戦意】 0 3 0 gauph-will 10001 {13}
23 3ca1a71c-66bf-464a-8ad2-254c52169e8e Gauph Key γ ガフスキー【γ】 1 3 2 gauph-gamma 11003 {13}
24 3d5d610a-3734-444d-8818-fce2024a190b Gauph Key Tria ガフスキー【トリア】 2 3 2 gauph-tria 17003 {13}
25 4d6fefb6-09e6-4c92-98b0-a48b35ddd738 Gauph Key β ガフスキー【β】 1 3 1 gauph-beta 11002 {13}
26 606632e3-3391-4223-8147-07060fe6f2e4 Gauph Key of Courage ガフスキー【勇気】 0 3 5 gauph-courage 10006 {13}
27 6d03b9c2-08d8-49ea-8522-5507e9243ccc Gauph Key α ガフスキー【α】 1 3 0 gauph-alpha 11001 {13}
28 98a358bc-d123-40c9-8c0e-7953467c9a27 Gauph Key Δ ガフスキー【Δ】 1 3 3 gauph-delta 11004 {13}
29 a1613dcd-dcc1-4290-95e7-3f9dfc28dd06 Gauph Key Tessera ガフスキー【テーセラ】 2 3 3 gauph-tessera 17004 {13}
30 abd48244-8398-4159-ada6-9062803189f1 Gauph Key of Strength ガフスキー【強壮】 0 3 3 gauph-strength 10004 {13}
31 cdd87f62-2d29-4698-b09d-8eef3f7b4406 Gauph Key Ena ガフスキー【エナ】 2 3 0 gauph-ena 17001 {13}
32 d0dd2b46-cb55-4c2f-beb6-e2ee380bdb5e Gauph Key Dio ガフスキー【ディオ】 2 3 1 gauph-dio 17002 {13}
33 d6c0afdb-f6f3-4473-ada3-d505228ee348 Gauph Key of Zeal ガフスキー【激情】 0 3 4 gauph-zeal 10005 {13}
34 44c2b0ba-642e-4edc-9680-1a34abe20418 Emblem of Devilry 魔獄のエンブレム 0 4 2 emblem-devilry 3 {19}
35 5ac2ad0a-f8da-403a-b098-7831d354f8e0 Emblem of Divinity 天聖のエンブレム 0 4 1 emblem-divinity 2 {19}
36 c2f1e5bc-9f8b-4af1-821c-2b32a9fb5f1f Emblem of Humanity 英勇のエンブレム 0 4 0 emblem-humanity 1 {19}
37 0c6ce91c-864c-4c62-8c9b-be61e8fae47f Optimus Teluma オプティマス・テルマ 1 2 0 teluma-optimus 16001 {27,40}
38 1929bfa8-6bbd-4918-9ad7-594525b5e2c6 Crag Teluma 巨岩のテルマ 0 2 3 teluma-crag 15004 {27,40}
39 3fa65774-1ed1-4a16-86cd-9133adca2232 Omega Teluma マグナ・テルマ 1 2 1 teluma-omega 16002 {27,40}
40 49f46e22-1796-435e-bce2-d9fdfe76d6c5 Tempest Teluma 暴風のテルマ 0 2 4 teluma-tempest 15005 {27,40}
41 81950efb-a4e1-4d45-8572-ddb604246212 Malice Teluma 闇禍のテルマ 0 2 6 teluma-malice 15007 {27,40}
42 d14e933e-630d-4cd6-9d61-dbdfd6e9332e Abyss Teluma 深海のテルマ 0 2 2 teluma-abyss 15003 {27,40}
43 dc96edb7-8bee-4721-94c2-daa6508aaed8 Inferno Teluma 炎獄のテルマ 0 2 1 teluma-inferno 15002 {27,40}
44 e36950be-1ea9-4642-af94-164187e38e6c Aureole Teluma 後光のテルマ 0 2 5 teluma-aureole 15006 {27,40}
45 ee80ff09-71c0-48bb-90ff-45e138df7481 Endurance Teluma 剛堅のテルマ 0 2 0 teluma-endurance 15001 {27,40}
46 b0b6d3be-7203-437e-8acd-2a59c2b5506a Oblivion Teluma 冥烈のテルマ 0 2 8 teluma-oblivion 15009 {40}
47 d79558df-53fb-4c24-963b-e0b67040afc7 Salvation Teluma 燦護のテルマ 0 2 7 teluma-salvation 15008 {40}

View file

@ -1,42 +1,24 @@
"id","name_en","name_jp","granblue_id","rarity","element","proficiency","series","flb","ulb","max_level","max_skill_level","min_hp","max_hp","max_hp_flb","max_hp_ulb","min_atk","max_atk","max_atk_flb","max_atk_ulb","extra","ax_type","limit","ax","nicknames_en","nicknames_jp","max_awakening_level","release_date","flb_date","ulb_date","wiki_en","wiki_ja","gamewith","kamigame","transcendence","transcendence_date","recruits"
"6c4f29c8-f43b-43f1-9fc5-967fb85c816e","Gauntlet of Proudearth","揺るがぬ大地の拳","1040611300",3,4,7,0,TRUE,FALSE,150,15,35,240,290,,390,2300,2780,,FALSE,0,FALSE,FALSE,"{}","{}",,"2019-04-24","2019-04-24",,"Gauntlet of Proudearth",,,,FALSE,,
"302ded88-b5c9-4570-b422-66fc40277c4f","Ixaba","イクサバ","1040906400",3,2,10,1,TRUE,FALSE,150,15,30,195,236,,502,3000,3620,,FALSE,0,FALSE,FALSE,"{}","{}",4,"2017-03-31","2018-05-21",,"Ixaba",,"72189","イクサバ",FALSE,,"3040115000"
"b540fbaf-48c9-41c0-981f-05953319b409","Skeletal Eclipse","呪蝕の骸槍","1040216900",3,5,4,1,TRUE,FALSE,150,15,43,280,339,,441,2547,3074,,FALSE,0,FALSE,FALSE,"{}","{}",,"2021-12-31","2021-12-31",,"Skeletal Eclipse","%C9%F0%B4%EF%2F%BC%F6%BF%AA%A4%CE%B3%BC%C1%E4%20%28SSR%29","314295","呪蝕の骸槍",FALSE,,"3040376000"
"aa6f8b9b-ed78-4b1a-8693-acefd5b455fc","Scythe of Repudiation","絶対否定の大鎌","1040310600",3,2,3,2,TRUE,TRUE,200,20,30,195,236,277,450,2730,3300,3870,FALSE,0,TRUE,FALSE,"{}","{}",,"2019-04-11","2019-04-11","2019-04-11","Scythe of Repudiation","{{{link_jpwiki|%C9%F0%B4%EF%B3%B5%CD%D7%2F%BD%AA%CB%F6%A4%CE%BF%C0%B4%EF%A5%B7%A5%EA%A1%BC%A5%BA}}}","{{{link_gamewith|146896}}}","{{{link_kamigame|{{{jpname|}}}}}}",TRUE,"2024-01-15",
"c6e4eeaa-bd19-466e-81ea-58310ed5cf25","Draconic Blade","ドラゴニックブレイド","1040912100",3,6,10,3,TRUE,TRUE,200,20,32,193,233,273,445,2744,3319,3894,FALSE,0,TRUE,FALSE,"{}","{}",,"2020-03-10","2020-03-10","2020-03-10","Draconic Blade","%C9%F0%B4%EF%2F%A5%C9%A5%E9%A5%B4%A5%CB%A5%C3%A5%AF%A5%D6%A5%EC%A5%A4%A5%C9%20%28SSR%29","190367",,FALSE,,
"6c4f29c8-f43b-43f1-9fc5-967fb85c816e","Gauntlet of Proudearth","揺るがぬ大地の拳","1040611300",3,4,7,,TRUE,FALSE,150,15,35,240,290,,390,2300,2780,,FALSE,0,FALSE,FALSE,"{}","{}",,"2019-04-24","2019-04-24",,"Gauntlet of Proudearth",,,,FALSE,,
"302ded88-b5c9-4570-b422-66fc40277c4f","Ixaba","イクサバ","1040906400",3,2,10,,TRUE,FALSE,150,15,30,195,236,,502,3000,3620,,FALSE,0,FALSE,FALSE,"{}","{}",4,"2017-03-31","2018-05-21",,"Ixaba",,"72189","イクサバ",FALSE,,"3040115000"
"b540fbaf-48c9-41c0-981f-05953319b409","Skeletal Eclipse","呪蝕の骸槍","1040216900",3,5,4,,TRUE,FALSE,150,15,43,280,339,,441,2547,3074,,FALSE,0,FALSE,FALSE,"{}","{}",,"2021-12-31","2021-12-31",,"Skeletal Eclipse","%C9%F0%B4%EF%2F%BC%F6%BF%AA%A4%CE%B3%BC%C1%E4%20%28SSR%29","314295","呪蝕の骸槍",FALSE,,"3040376000"
"a2025b78-5c72-4efa-9fbf-c9fdc2aa2364","Katana of Repudiation","絶対否定の太刀","1040911000",3,5,10,,TRUE,TRUE,200,20,28,189,229,269,465,2765,3340,3915,FALSE,0,TRUE,FALSE,"{}","{}",,"2019-04-11","2019-04-11","2019-04-11","Katana of Repudiation","{{{link_jpwiki|%C9%F0%B4%EF%B3%B5%CD%D7%2F%BD%AA%CB%F6%A4%CE%BF%C0%B4%EF%A5%B7%A5%EA%A1%BC%A5%BA}}}","{{{link_gamewith|146896}}}","{{{link_kamigame|{{{jpname|}}}}}}",TRUE,"2024-01-15",
"aa6f8b9b-ed78-4b1a-8693-acefd5b455fc","Scythe of Repudiation","絶対否定の大鎌","1040310600",3,2,3,,TRUE,TRUE,200,20,30,195,236,277,450,2730,3300,3870,FALSE,0,TRUE,FALSE,"{}","{}",,"2019-04-11","2019-04-11","2019-04-11","Scythe of Repudiation","{{{link_jpwiki|%C9%F0%B4%EF%B3%B5%CD%D7%2F%BD%AA%CB%F6%A4%CE%BF%C0%B4%EF%A5%B7%A5%EA%A1%BC%A5%BA}}}","{{{link_gamewith|146896}}}","{{{link_kamigame|{{{jpname|}}}}}}",TRUE,"2024-01-15",
"c6e4eeaa-bd19-466e-81ea-58310ed5cf25","Draconic Blade","ドラゴニックブレイド","1040912100",3,6,10,,TRUE,TRUE,200,20,32,193,233,273,445,2744,3319,3894,FALSE,0,TRUE,FALSE,"{}","{}",,"2020-03-10","2020-03-10","2020-03-10","Draconic Blade","%C9%F0%B4%EF%2F%A5%C9%A5%E9%A5%B4%A5%CB%A5%C3%A5%AF%A5%D6%A5%EC%A5%A4%A5%C9%20%28SSR%29","190367",,FALSE,,
"1cedbb93-79ef-41ef-915f-94961ef9eba8","Nine-Realm Harp (Awakened)","九界琴・覚醒","1040801400",3,0,8,4,FALSE,FALSE,100,10,75,275,,,380,2470,,,FALSE,0,FALSE,FALSE,"{}","{}",,"2014-03-10",,,"Nine-Realm Harp (Awakened)",,,,FALSE,,
"a5d72b41-6dea-4179-9996-36c01d2dad32","Winter's Frostnettle","冬ノ霜柱","1040111300",3,3,2,5,TRUE,FALSE,150,15,21,189,228,,290,1857,2249,,FALSE,0,FALSE,FALSE,"{}","{}",,"2019-07-12","2019-07-12",,"Winter%27s Frostnettle","%C9%F0%B4%EF%2F%C5%DF%A5%CE%C1%FA%C3%EC%20%28SSR%29","158278","冬ノ霜柱",FALSE,,
"620fbcd5-7c2e-4949-8cad-bbfb0908b00f","Ecke Sachs","エッケザックス","1040007100",3,2,1,6,TRUE,FALSE,150,15,106,664,800,,278,1677,2030,,FALSE,3,FALSE,TRUE,"{}","{}",,"2014-03-10","2020-04-07",,"Ecke Sachs","%C9%F0%B4%EF%2F%A5%A8%A5%C3%A5%B1%A5%B6%A5%C3%A5%AF%A5%B9%20%28SSR%29","71702","エッケザックス",FALSE,,
"8cebe3c3-be12-4985-b45d-3e9db8204e6e","Ray of Zhuque Malus","朱雀光剣・邪","1040906700",3,2,10,7,TRUE,TRUE,200,20,22,145,175,205,345,2090,2530,2970,FALSE,0,TRUE,FALSE,"{}","{}",,"2017-04-10","2017-04-10","2022-04-07","Ray of Zhuque Malus",,"75564","朱雀光剣・邪",FALSE,,
"4380828f-1acc-46cd-b7eb-1cb8d34ca9ec","Last Storm Harp","ラストストームハープ","1040808300",3,1,8,8,TRUE,FALSE,150,15,62,223,260,,337,2059,2400,,FALSE,0,FALSE,FALSE,"{}","{}",,"2018-03-10","2018-03-10",,"Last Storm Harp",,,,FALSE,,
"ec3ba18a-9417-4ebe-a898-a74d5f15385f","Pillar of Flame","炎の柱","1040215200",3,6,4,8,TRUE,FALSE,150,15,37,213,250,,341,2250,2630,,FALSE,0,FALSE,FALSE,"{}","{}",,"2020-08-31","2020-08-31",,"Pillar of Flame","%C9%F0%B4%EF%2F%B1%EA%A4%CE%C3%EC%20%28SSR%29","225789","炎の柱",FALSE,,
"d61ee84f-4520-4064-8ff9-42a899273316","Luminiera Sword Omega","シュヴァリエソード・マグナ","1040007200",3,6,1,9,TRUE,TRUE,200,20,31,195,228,244,370,2275,2660,2850,FALSE,1,FALSE,TRUE,"{}","{}",,"2014-03-10",,"2018-03-10","Luminiera Sword Omega",,,,FALSE,,
"9f94d1e5-a117-432f-9da4-f3a5022b666d","Bow of Sigurd","シグルズの弓","1040705100",3,3,5,10,TRUE,FALSE,150,15,36,214,250,,365,2311,2701,,FALSE,3,FALSE,TRUE,"{}","{}",,"2014-03-10","2018-07-15",,"Bow of Sigurd",,,,FALSE,,
"82deb08e-8f92-44eb-8671-22426f89564e","Sword of Pallas Militis","パラスソード・ミーレス","1040022600",3,2,1,11,FALSE,FALSE,100,10,28,182,,,355,2153,,,TRUE,0,FALSE,FALSE,"{}","{}",,"2022-02-21",,,"Sword of Pallas Militis","%C9%F0%B4%EF%2F%A5%D1%A5%E9%A5%B9%A5%BD%A1%BC%A5%C9%A1%A6%A5%DF%A1%BC%A5%EC%A5%B9%20%28SSR%29","319816",,FALSE,,
"88492bc9-8085-4651-8a9d-305ab03d0710","Hollowsky Bow","虚空の歪弦","1040708900",3,6,5,12,TRUE,FALSE,150,15,37,234,280,,420,2580,3120,,FALSE,0,TRUE,FALSE,"{}","{}",,"2018-12-18","2018-12-18",,"Hollowsky Bow",,"134591","虚空の歪弦",FALSE,,
"9adb22c7-eb09-47e6-b100-783e0cefaf95","Last Sahrivar","ラスト・シャフレワル","1040015800",3,4,1,13,TRUE,TRUE,200,20,39,200,232,264,391,2240,2611,2982,FALSE,2,FALSE,TRUE,"{}","{}",,"2018-05-15","2018-05-15","2022-06-07","Last Sahrivar",,"105147",,FALSE,,
"6d4b41c6-2807-4aa6-9f69-14f5c2c68f37","Claws of Terror","黒銀の滅爪","1040612500",3,5,7,14,TRUE,TRUE,200,20,33,227,266,305,372,2196,2561,2926,FALSE,0,FALSE,FALSE,"{}","{}",,"2020-03-03","2020-03-03","2020-05-25","Claws of Terror",,"187437",,FALSE,,
"874eaf0b-5561-49d4-8983-ded494642a84","Rose Crystal Sword","ローズクリスタルソード","1040009700",3,3,1,15,FALSE,FALSE,100,10,34,204,,,365,2320,,,FALSE,0,FALSE,FALSE,"{}","{}",,"2017-03-10",,,"Rose Crystal Sword",,,,FALSE,,
"e65ddc21-b8e9-45ee-8c0f-06013b4187a1","Spear of Bahamut Coda","バハムートスピア・フツルス","1040205400",3,5,4,16,TRUE,TRUE,200,20,37,248,290,332,380,2260,2640,3020,TRUE,0,TRUE,FALSE,"{}","{}",,"2014-03-10",,"2021-12-03","Spear of Bahamut Coda",,,,FALSE,,
"07dd062a-640c-4f00-9943-614b9f031271","Ultima Claw","オメガクロー","1040608100",3,0,7,17,TRUE,TRUE,200,20,35,277,313,349,393,2717,3066,3415,TRUE,0,TRUE,FALSE,"{}","{}",,"2014-03-10","2021-12-03","2021-12-03","Ultima Claw",,,,FALSE,,
"0c21542c-ce18-471e-ac80-1378fc97bec8","Scales of Dominion","支配の天秤","1040415800",3,5,6,18,TRUE,FALSE,150,15,38,261,320,,345,2020,2440,,FALSE,0,FALSE,FALSE,"{}","{}",,"2017-07-24","2019-08-06",,"Scales of Dominion",,"161169",,FALSE,,
"54c220d4-9cee-4f42-b184-5057cb1cb24a","Esna","エスナ","1040420600",3,3,6,19,TRUE,FALSE,150,15,50,294,355,,288,1859,2252,,FALSE,0,FALSE,FALSE,"{}","{}",15,"2022-07-20","2022-07-20",,"Esna","%C9%F0%B4%EF%2F%A5%A8%A5%B9%A5%CA%20%28SSR%29","352615",,FALSE,,
"742f29a3-2fa0-40f9-9275-126d892501b3","Cosmic Blade","ブレイド・オブ・コスモス","1040911800",3,6,10,20,TRUE,FALSE,150,15,31,184,222,,423,2610,3157,,FALSE,0,FALSE,FALSE,"{}","{}",,"2016-03-10","2019-12-19",,"Cosmic Blade",,,,FALSE,,
"c250d5c7-0208-49b5-9c88-fc51117dd7d3","Ewiyar's Beak","イーウィヤピーク","1040912400",3,1,10,21,TRUE,FALSE,150,15,31,192,224,,450,2749,3209,,FALSE,1,FALSE,TRUE,"{}","{}",,"2020-07-27","2020-07-27",,"Ewiyar%27s Beak",,"218570",,FALSE,,
"52d41363-16b1-42af-b185-ed1ba1308891","Ameno Habakiri","天羽々斬","1040904300",3,0,10,22,TRUE,TRUE,200,20,37,213,250,287,504,3024,3530,4036,FALSE,0,FALSE,FALSE,"{}","{}",,"2014-03-10","2018-04-17","2020-05-11","Ameno Habakiri","%C9%F0%B4%EF%C9%BE%B2%C1%2F%A5%B9%A5%DA%A5%EA%A5%AA%A5%EB%A5%B7%A5%EA%A1%BC%A5%BA","75523","天羽々斬",FALSE,,
"a273dcbf-4d85-4898-89ac-41cc80c262d7","Gisla","グラーシーザー","1040200700",3,5,4,23,TRUE,FALSE,150,15,39,255,309,,404,2325,2810,,FALSE,0,FALSE,FALSE,"{}","{}",,"2014-05-14","2015-07-27",,"Gisla",,,,FALSE,,
"eeb5882d-63a1-4852-a753-32166b4b9b7f","Wasserspeier","ヴァッサーシュパイア","1040018100",3,0,1,24,TRUE,TRUE,200,20,32,205,240,275,390,2410,2814,3218,FALSE,0,FALSE,FALSE,"{}","{}",,"2019-05-10","2019-05-10","2019-05-10","Wasserspeier","%C9%F0%B4%EF%2F%A5%F4%A5%A1%A5%C3%A5%B5%A1%BC%A5%B7%A5%E5%A5%D1%A5%A4%A5%A2%A1%BC%20%28SSR%29","150388","ヴァッサーシュパイアー",FALSE,,
"8137294e-6bf1-4bac-a1e0-38cdc542622b","Clarion","クラリオン","1040511200",3,3,9,25,TRUE,TRUE,200,20,27,163,200,237,385,2425,2940,3455,FALSE,0,FALSE,FALSE,"{}","{}",15,"2019-06-04","2019-06-04","2022-08-19","Clarion","%C9%F0%B4%EF%2F%A5%AF%A5%E9%A5%EA%A5%AA%A5%F3%20%28SSR%29","153291",,FALSE,,
"63a066c7-9f23-4c12-a921-ec56f584b0ed","Kaladanda","カラダンダ","1040416300",3,2,6,26,TRUE,FALSE,150,15,41,264,320,,333,2009,2428,,FALSE,0,FALSE,FALSE,"{}","{}",,"2019-12-19","2019-12-19",,"Kaladanda","%C9%F0%B4%EF%2F%A5%AB%A5%E9%A5%C0%A5%F3%A5%C0%20%28SSR%29","180277","カラダンダ",FALSE,,
"ac8da736-4041-45e2-b413-f859e6fae828","Magma Rush","マグマストリーム","1040408100",3,2,6,27,FALSE,FALSE,150,15,36,232,,,295,1770,,,FALSE,0,FALSE,FALSE,"{}","{}",,"2016-06-09","2023-07-12",,"Magma Rush",,"72005",,FALSE,,
"aec45e41-9874-465b-b668-9129a49d40c5","Sephira Emerald Duke","セフィラの翠甲","1040610000",3,4,7,28,TRUE,FALSE,150,15,38,232,271,,350,2175,2540,,TRUE,0,FALSE,FALSE,"{}","{}",,"2018-03-22","2020-12-04",,"Sephira Emerald Duke",,,,FALSE,,
"7f70e52a-d18c-4353-a135-1a841d3b7bf2","Rise of Justice","ライズ・オブ・ジャスティス","1040020800",3,3,1,29,TRUE,TRUE,200,20,34,202,244,286,367,2322,2811,3300,TRUE,0,FALSE,FALSE,"{}","{}",,"2020-12-04","2022-02-21","2022-12-26","Rise of Justice","%C9%F0%B4%EF%2F%A5%E9%A5%A4%A5%BA%A1%A6%A5%AA%A5%D6%A1%A6%A5%B8%A5%E3%A5%B9%A5%C6%A5%A3%A5%B9%20%28SSR%29","{{{link_gamewith|220273}}}",,FALSE,,
"e7a05d2e-a3ec-4620-98a5-d8472d474971","Fang of the Dragonslayer Mk II","竜伐の剛牙・再誕","1040117700",3,4,2,30,TRUE,FALSE,150,15,41,247,325,,394,2456,3230,,FALSE,,FALSE,FALSE,"{}","{}",20,"2023-11-09","2023-11-09",,"Fang of the Dragonslayer Mk II","","","",FALSE,,
"af83ceee-3a24-48c7-8cae-9f83276ced81","Hraesvelgr","フレズヴェルク","1040515200",3,3,9,31,TRUE,FALSE,150,15,37,211,246,,514,3131,3654,,FALSE,0,FALSE,FALSE,"{}","{}",,"2022-12-26","2022-12-26",,"Hraesvelgr","%C9%F0%B4%EF%2F%A5%D5%A5%EC%A5%BA%A5%F4%A5%A7%A5%EB%A5%AF%20%28SSR%29","366906",,FALSE,,
"47208685-e87a-4e07-b328-fb9ac3888718","Worldscathing Leon","レオン・オブ・ワールド","1040815100",3,2,8,32,TRUE,FALSE,150,15,44,281,350,,379,2286,2763,,TRUE,,FALSE,FALSE,"{}","{}",10,"2023-03-30","2023-12-19",,"Worldscathing Leon",,"393901",,FALSE,,
"a2f0db22-baf1-4640-8c2e-6d283375744f","Exo Antaeus","神銃エクス・アンタイオス","1040516300",3,3,9,33,TRUE,FALSE,150,15,29,169,204,,394,2488,3012,,FALSE,,FALSE,FALSE,"{}","{}",10,"2023-09-07","2023-09-07",,"Exo Antaeus","","","",FALSE,,
"b9522d2d-1d29-4a2b-b58c-d3b7c781feb6","Prayer of Grand Gales","狂飆と至高の祈り","1040422200",3,1,6,34,TRUE,TRUE,200,20,39,236,285,439,412,2566,3105,3302,TRUE,,TRUE,FALSE,"{draconic}","{ドラポン}",,"2023-10-23",,,"Prayer of Grand Gales","","","",FALSE,,
"81b9845a-a6d5-4aec-bbcf-1678277c1d79","Albacore Body","アルバコアボディ","1040423500",3,5,6,35,FALSE,FALSE,100,10,32,224,,,303,1759,,,FALSE,,FALSE,FALSE,"{}","{}",,"2024-07-29",,,"Albacore Body","アルバコアボディ (SSR)","458282","アルバコアボディ",FALSE,,
"f0d13eb4-f462-48d8-8705-16f91c351cb2","Syringe or Treat","シリンジ・オア・トリート","1040516400",3,5,9,36,FALSE,FALSE,100,10,19,130,,,504,2930,,,FALSE,,FALSE,FALSE,"{}","{}",,"2023-10-18",,,"Syringe or Treat","","","",FALSE,,"3040487000"
"3d9fad4c-a34f-4133-9d5e-c382a747eeec","Demolition-Tiger Axe","絶壊・威寅斧","1040319100",3,4,3,37,TRUE,FALSE,150,15,35,207,250,,456,2834,3429,,FALSE,,FALSE,FALSE,"{}","{}",,"2024-11-07","2024-11-07",,"Demolition-Tiger_Axe","絶壊・威寅斧 (SSR)","471097","絶壊・威寅斧",FALSE,,
"4110e59e-5b4c-40f8-ad83-2e62f5d60fc2","Yggdrasil Crystal Blade Arbos","世界樹の晶剣・アルボス","1040026300",3,4,1,38,TRUE,FALSE,150,15,32,200,242,,377,2332,2821,,FALSE,,FALSE,FALSE,"{}","{}",,"2024-06-03","2024-06-03",,"Yggdrasil Crystal Blade Arbos","","","",FALSE,,
"a5d72b41-6dea-4179-9996-36c01d2dad32","Winter's Frostnettle","冬ノ霜柱","1040111300",3,3,2,,TRUE,FALSE,150,15,21,189,228,,290,1857,2249,,FALSE,0,FALSE,FALSE,"{}","{}",,"2019-07-12","2019-07-12",,"Winter%27s Frostnettle","%C9%F0%B4%EF%2F%C5%DF%A5%CE%C1%FA%C3%EC%20%28SSR%29","158278","冬ノ霜柱",FALSE,,
"620fbcd5-7c2e-4949-8cad-bbfb0908b00f","Ecke Sachs","エッケザックス","1040007100",3,2,1,,TRUE,FALSE,150,15,106,664,800,,278,1677,2030,,FALSE,3,FALSE,TRUE,"{}","{}",,"2014-03-10","2020-04-07",,"Ecke Sachs","%C9%F0%B4%EF%2F%A5%A8%A5%C3%A5%B1%A5%B6%A5%C3%A5%AF%A5%B9%20%28SSR%29","71702","エッケザックス",FALSE,,
"8cebe3c3-be12-4985-b45d-3e9db8204e6e","Ray of Zhuque Malus","朱雀光剣・邪","1040906700",3,2,10,,TRUE,TRUE,200,20,22,145,175,205,345,2090,2530,2970,FALSE,0,TRUE,FALSE,"{}","{}",,"2017-04-10","2017-04-10","2022-04-07","Ray of Zhuque Malus",,"75564","朱雀光剣・邪",FALSE,,
"4380828f-1acc-46cd-b7eb-1cb8d34ca9ec","Last Storm Harp","ラストストームハープ","1040808300",3,1,8,,TRUE,FALSE,150,15,62,223,260,,337,2059,2400,,FALSE,0,FALSE,FALSE,"{}","{}",,"2018-03-10","2018-03-10",,"Last Storm Harp",,,,FALSE,,
"ec3ba18a-9417-4ebe-a898-a74d5f15385f","Pillar of Flame","炎の柱","1040215200",3,6,4,,TRUE,FALSE,150,15,37,213,250,,341,2250,2630,,FALSE,0,FALSE,FALSE,"{}","{}",,"2020-08-31","2020-08-31",,"Pillar of Flame","%C9%F0%B4%EF%2F%B1%EA%A4%CE%C3%EC%20%28SSR%29","225789","炎の柱",FALSE,,
"d61ee84f-4520-4064-8ff9-42a899273316","Luminiera Sword Omega","シュヴァリエソード・マグナ","1040007200",3,6,1,,TRUE,TRUE,200,20,31,195,228,244,370,2275,2660,2850,FALSE,1,FALSE,TRUE,"{}","{}",,"2014-03-10",,"2018-03-10","Luminiera Sword Omega",,,,FALSE,,
"9f94d1e5-a117-432f-9da4-f3a5022b666d","Bow of Sigurd","シグルズの弓","1040705100",3,3,5,,TRUE,FALSE,150,15,36,214,250,,365,2311,2701,,FALSE,3,FALSE,TRUE,"{}","{}",,"2014-03-10","2018-07-15",,"Bow of Sigurd",,,,FALSE,,
"a4441a22-4704-4fbc-a543-77d3b952e921","Pain and Suffering","ペイン・アンド・ストレイン","1040314300",3,5,3,,TRUE,FALSE,150,15,50,410,500,,410,1890,2260,,FALSE,0,FALSE,FALSE,"{pns}","{}",,"2021-09-15","2021-09-15",,"Pain and Suffering","%C9%F0%B4%EF%2F%A5%DA%A5%A4%A5%F3%A1%A6%A5%A2%A5%F3%A5%C9%A1%A6%A5%B9%A5%C8%A5%EC%A5%A4%A5%F3%20%28SSR%29","294337","ペイン・アンド・ストレイン",FALSE,,"3040357000"
"f4460b37-ab5b-4252-bd79-009a8819ee25","Eternal Signature","永遠の落款","1040116600",3,5,2,,TRUE,FALSE,150,15,40,259,,,459,2562,,,FALSE,,FALSE,FALSE,"{es,""halmal dagger""}","{}",,"2023-01-19","2023-01-19",,"Eternal Signature","%C9%F0%B4%EF%2F%B1%CA%B1%F3%A4%CE%CD%EE%B4%BE%20%28SSR%29","384946",,FALSE,,"3040443000"
"07dd062a-640c-4f00-9943-614b9f031271","Ultima Claw","オメガクロー","1040608100",3,0,7,,TRUE,TRUE,200,20,35,277,313,349,393,2717,3066,3415,TRUE,0,TRUE,FALSE,"{}","{}",,"2014-03-10","2021-12-03","2021-12-03","Ultima Claw",,,,FALSE,,
"33d75927-70e9-49ba-8494-fb67b4567540","Blutgang","ブルトガング","1040008700",3,5,1,,TRUE,FALSE,150,15,36,234,280,,480,2790,3370,,FALSE,0,FALSE,FALSE,"{}","{}",4,"2016-04-28","2017-11-17",,"Blutgang","%C9%F0%B4%EF%2F%A5%D6%A5%EB%A5%C8%A5%AC%A5%F3%A5%B0%20%28SSR%29","71711",,FALSE,,"3040082000"
"1b3b84fd-eefa-4845-8fd0-b4452482e716","Bab-el-Mandeb","バブ・エル・マンデブ","1040311600",3,5,3,,TRUE,FALSE,150,15,31,207,251,,503,2915,3518,,FALSE,0,FALSE,FALSE,"{}","{}",,"2019-12-28","2019-12-28",,"Bab-el-Mandeb",,,,FALSE,,"3040251000"
"dd199867-ec7b-4067-8886-1fa02e1095b4","Celeste Zaghnal Omega","セレストザグナル・マグナ","1040301400",3,5,3,,TRUE,TRUE,200,20,24,169,198,213,405,2405,2810,3010,FALSE,1,FALSE,TRUE,"{}","{}",,"2014-03-10",,"2018-03-10","Celeste Zaghnal Omega",,"71937",,FALSE,,
"cddf9de4-ee8f-4978-9901-0ec7f2601927","Pain of Death","ペイン・オブ・デス","1040113200",3,5,2,,TRUE,TRUE,200,20,32,218,265,312,379,2241,2707,3173,TRUE,0,FALSE,FALSE,"{}","{}",,"2020-12-04","2022-02-21","2022-12-26","Pain of Death","%C9%F0%B4%EF%2F%A5%DA%A5%A4%A5%F3%A1%A6%A5%AA%A5%D6%A1%A6%A5%C7%A5%B9%20%28SSR%29","{{{link_gamewith|220273}}}",,FALSE,,
"38df4067-db48-4dbc-b1cf-c26e019137d8","Parazonium","パラゾニウム","1040108700",3,5,2,,TRUE,FALSE,150,15,40,259,310,,459,2652,3200,,FALSE,0,FALSE,FALSE,"{}","{}",4,"2017-02-28","2018-02-14",,"Parazonium","%C9%F0%B4%EF%2F%A5%D1%A5%E9%A5%BE%A5%CB%A5%A6%A5%E0%20%28SSR%29","71768","パラゾニウム",FALSE,,"3040111000"
"36959849-1ff6-4317-992e-2287b31138eb","Dagger of Bahamut Coda","バハムートダガー・フツルス","1040106700",3,5,2,,TRUE,TRUE,200,20,34,229,268,307,395,2355,2750,3145,TRUE,0,TRUE,FALSE,"{}","{}",,"2014-03-10",,"2021-12-03","Dagger of Bahamut Coda",,,,FALSE,,

1 id name_en name_jp granblue_id rarity element proficiency series flb ulb max_level max_skill_level min_hp max_hp max_hp_flb max_hp_ulb min_atk max_atk max_atk_flb max_atk_ulb extra ax_type limit ax nicknames_en nicknames_jp max_awakening_level release_date flb_date ulb_date wiki_en wiki_ja gamewith kamigame transcendence transcendence_date recruits
2 6c4f29c8-f43b-43f1-9fc5-967fb85c816e Gauntlet of Proudearth 揺るがぬ大地の拳 1040611300 3 4 7 0 TRUE FALSE 150 15 35 240 290 390 2300 2780 FALSE 0 FALSE FALSE {} {} 2019-04-24 2019-04-24 Gauntlet of Proudearth FALSE
3 302ded88-b5c9-4570-b422-66fc40277c4f Ixaba イクサバ 1040906400 3 2 10 1 TRUE FALSE 150 15 30 195 236 502 3000 3620 FALSE 0 FALSE FALSE {} {} 4 2017-03-31 2018-05-21 Ixaba 72189 イクサバ FALSE 3040115000
4 b540fbaf-48c9-41c0-981f-05953319b409 Skeletal Eclipse 呪蝕の骸槍 1040216900 3 5 4 1 TRUE FALSE 150 15 43 280 339 441 2547 3074 FALSE 0 FALSE FALSE {} {} 2021-12-31 2021-12-31 Skeletal Eclipse %C9%F0%B4%EF%2F%BC%F6%BF%AA%A4%CE%B3%BC%C1%E4%20%28SSR%29 314295 呪蝕の骸槍 FALSE 3040376000
5 aa6f8b9b-ed78-4b1a-8693-acefd5b455fc a2025b78-5c72-4efa-9fbf-c9fdc2aa2364 Scythe of Repudiation Katana of Repudiation 絶対否定の大鎌 絶対否定の太刀 1040310600 1040911000 3 2 5 3 10 2 TRUE TRUE 200 20 30 28 195 189 236 229 277 269 450 465 2730 2765 3300 3340 3870 3915 FALSE 0 TRUE FALSE {} {} 2019-04-11 2019-04-11 2019-04-11 Scythe of Repudiation Katana of Repudiation {{{link_jpwiki|%C9%F0%B4%EF%B3%B5%CD%D7%2F%BD%AA%CB%F6%A4%CE%BF%C0%B4%EF%A5%B7%A5%EA%A1%BC%A5%BA}}} {{{link_gamewith|146896}}} {{{link_kamigame|{{{jpname|}}}}}} TRUE 2024-01-15
6 c6e4eeaa-bd19-466e-81ea-58310ed5cf25 aa6f8b9b-ed78-4b1a-8693-acefd5b455fc Draconic Blade Scythe of Repudiation ドラゴニックブレイド 絶対否定の大鎌 1040912100 1040310600 3 6 2 10 3 3 TRUE TRUE 200 20 32 30 193 195 233 236 273 277 445 450 2744 2730 3319 3300 3894 3870 FALSE 0 TRUE FALSE {} {} 2020-03-10 2019-04-11 2020-03-10 2019-04-11 2020-03-10 2019-04-11 Draconic Blade Scythe of Repudiation %C9%F0%B4%EF%2F%A5%C9%A5%E9%A5%B4%A5%CB%A5%C3%A5%AF%A5%D6%A5%EC%A5%A4%A5%C9%20%28SSR%29 {{{link_jpwiki|%C9%F0%B4%EF%B3%B5%CD%D7%2F%BD%AA%CB%F6%A4%CE%BF%C0%B4%EF%A5%B7%A5%EA%A1%BC%A5%BA}}} 190367 {{{link_gamewith|146896}}} {{{link_kamigame|{{{jpname|}}}}}} FALSE TRUE 2024-01-15
7 c6e4eeaa-bd19-466e-81ea-58310ed5cf25 Draconic Blade ドラゴニックブレイド 1040912100 3 6 10 TRUE TRUE 200 20 32 193 233 273 445 2744 3319 3894 FALSE 0 TRUE FALSE {} {} 2020-03-10 2020-03-10 2020-03-10 Draconic Blade %C9%F0%B4%EF%2F%A5%C9%A5%E9%A5%B4%A5%CB%A5%C3%A5%AF%A5%D6%A5%EC%A5%A4%A5%C9%20%28SSR%29 190367 FALSE
8 1cedbb93-79ef-41ef-915f-94961ef9eba8 Nine-Realm Harp (Awakened) 九界琴・覚醒 1040801400 3 0 8 4 FALSE FALSE 100 10 75 275 380 2470 FALSE 0 FALSE FALSE {} {} 2014-03-10 Nine-Realm Harp (Awakened) FALSE
9 a5d72b41-6dea-4179-9996-36c01d2dad32 Winter's Frostnettle 冬ノ霜柱 1040111300 3 3 2 5 TRUE FALSE 150 15 21 189 228 290 1857 2249 FALSE 0 FALSE FALSE {} {} 2019-07-12 2019-07-12 Winter%27s Frostnettle %C9%F0%B4%EF%2F%C5%DF%A5%CE%C1%FA%C3%EC%20%28SSR%29 158278 冬ノ霜柱 FALSE
10 620fbcd5-7c2e-4949-8cad-bbfb0908b00f Ecke Sachs エッケザックス 1040007100 3 2 1 6 TRUE FALSE 150 15 106 664 800 278 1677 2030 FALSE 3 FALSE TRUE {} {} 2014-03-10 2020-04-07 Ecke Sachs %C9%F0%B4%EF%2F%A5%A8%A5%C3%A5%B1%A5%B6%A5%C3%A5%AF%A5%B9%20%28SSR%29 71702 エッケザックス FALSE
11 8cebe3c3-be12-4985-b45d-3e9db8204e6e Ray of Zhuque Malus 朱雀光剣・邪 1040906700 3 2 10 7 TRUE TRUE 200 20 22 145 175 205 345 2090 2530 2970 FALSE 0 TRUE FALSE {} {} 2017-04-10 2017-04-10 2022-04-07 Ray of Zhuque Malus 75564 朱雀光剣・邪 FALSE
12 4380828f-1acc-46cd-b7eb-1cb8d34ca9ec Last Storm Harp ラストストームハープ 1040808300 3 1 8 8 TRUE FALSE 150 15 62 223 260 337 2059 2400 FALSE 0 FALSE FALSE {} {} 2018-03-10 2018-03-10 Last Storm Harp FALSE
13 ec3ba18a-9417-4ebe-a898-a74d5f15385f Pillar of Flame 炎の柱 1040215200 3 6 4 8 TRUE FALSE 150 15 37 213 250 341 2250 2630 FALSE 0 FALSE FALSE {} {} 2020-08-31 2020-08-31 Pillar of Flame %C9%F0%B4%EF%2F%B1%EA%A4%CE%C3%EC%20%28SSR%29 225789 炎の柱 FALSE
14 d61ee84f-4520-4064-8ff9-42a899273316 Luminiera Sword Omega シュヴァリエソード・マグナ 1040007200 3 6 1 9 TRUE TRUE 200 20 31 195 228 244 370 2275 2660 2850 FALSE 1 FALSE TRUE {} {} 2014-03-10 2018-03-10 Luminiera Sword Omega FALSE
15 9f94d1e5-a117-432f-9da4-f3a5022b666d Bow of Sigurd シグルズの弓 1040705100 3 3 5 10 TRUE FALSE 150 15 36 214 250 365 2311 2701 FALSE 3 FALSE TRUE {} {} 2014-03-10 2018-07-15 Bow of Sigurd FALSE
16 82deb08e-8f92-44eb-8671-22426f89564e a4441a22-4704-4fbc-a543-77d3b952e921 Sword of Pallas Militis Pain and Suffering パラスソード・ミーレス ペイン・アンド・ストレイン 1040022600 1040314300 3 2 5 1 3 11 FALSE TRUE FALSE 100 150 10 15 28 50 182 410 500 355 410 2153 1890 2260 TRUE FALSE 0 FALSE FALSE {} {pns} {} 2022-02-21 2021-09-15 2021-09-15 Sword of Pallas Militis Pain and Suffering %C9%F0%B4%EF%2F%A5%D1%A5%E9%A5%B9%A5%BD%A1%BC%A5%C9%A1%A6%A5%DF%A1%BC%A5%EC%A5%B9%20%28SSR%29 %C9%F0%B4%EF%2F%A5%DA%A5%A4%A5%F3%A1%A6%A5%A2%A5%F3%A5%C9%A1%A6%A5%B9%A5%C8%A5%EC%A5%A4%A5%F3%20%28SSR%29 319816 294337 ペイン・アンド・ストレイン FALSE 3040357000
17 88492bc9-8085-4651-8a9d-305ab03d0710 f4460b37-ab5b-4252-bd79-009a8819ee25 Hollowsky Bow Eternal Signature 虚空の歪弦 永遠の落款 1040708900 1040116600 3 6 5 5 2 12 TRUE FALSE 150 15 37 40 234 259 280 420 459 2580 2562 3120 FALSE 0 TRUE FALSE FALSE {} {es,"halmal dagger"} {} 2018-12-18 2023-01-19 2018-12-18 2023-01-19 Hollowsky Bow Eternal Signature %C9%F0%B4%EF%2F%B1%CA%B1%F3%A4%CE%CD%EE%B4%BE%20%28SSR%29 134591 384946 虚空の歪弦 FALSE 3040443000
18 9adb22c7-eb09-47e6-b100-783e0cefaf95 07dd062a-640c-4f00-9943-614b9f031271 Last Sahrivar Ultima Claw ラスト・シャフレワル オメガクロー 1040015800 1040608100 3 4 0 1 7 13 TRUE TRUE 200 20 39 35 200 277 232 313 264 349 391 393 2240 2717 2611 3066 2982 3415 FALSE TRUE 2 0 FALSE TRUE TRUE FALSE {} {} 2018-05-15 2014-03-10 2018-05-15 2021-12-03 2022-06-07 2021-12-03 Last Sahrivar Ultima Claw 105147 FALSE
19 6d4b41c6-2807-4aa6-9f69-14f5c2c68f37 33d75927-70e9-49ba-8494-fb67b4567540 Claws of Terror Blutgang 黒銀の滅爪 ブルトガング 1040612500 1040008700 3 5 7 1 14 TRUE TRUE FALSE 200 150 20 15 33 36 227 234 266 280 305 372 480 2196 2790 2561 3370 2926 FALSE 0 FALSE FALSE {} {} 4 2020-03-03 2016-04-28 2020-03-03 2017-11-17 2020-05-25 Claws of Terror Blutgang %C9%F0%B4%EF%2F%A5%D6%A5%EB%A5%C8%A5%AC%A5%F3%A5%B0%20%28SSR%29 187437 71711 FALSE 3040082000
20 874eaf0b-5561-49d4-8983-ded494642a84 1b3b84fd-eefa-4845-8fd0-b4452482e716 Rose Crystal Sword Bab-el-Mandeb ローズクリスタルソード バブ・エル・マンデブ 1040009700 1040311600 3 3 5 1 3 15 FALSE TRUE FALSE 100 150 10 15 34 31 204 207 251 365 503 2320 2915 3518 FALSE 0 FALSE FALSE {} {} 2017-03-10 2019-12-28 2019-12-28 Rose Crystal Sword Bab-el-Mandeb FALSE 3040251000
21 e65ddc21-b8e9-45ee-8c0f-06013b4187a1 dd199867-ec7b-4067-8886-1fa02e1095b4 Spear of Bahamut Coda Celeste Zaghnal Omega バハムートスピア・フツルス セレストザグナル・マグナ 1040205400 1040301400 3 5 4 3 16 TRUE TRUE 200 20 37 24 248 169 290 198 332 213 380 405 2260 2405 2640 2810 3020 3010 TRUE FALSE 0 1 TRUE FALSE FALSE TRUE {} {} 2014-03-10 2021-12-03 2018-03-10 Spear of Bahamut Coda Celeste Zaghnal Omega 71937 FALSE
22 07dd062a-640c-4f00-9943-614b9f031271 cddf9de4-ee8f-4978-9901-0ec7f2601927 Ultima Claw Pain of Death オメガクロー ペイン・オブ・デス 1040608100 1040113200 3 0 5 7 2 17 TRUE TRUE 200 20 35 32 277 218 313 265 349 312 393 379 2717 2241 3066 2707 3415 3173 TRUE 0 TRUE FALSE FALSE {} {} 2014-03-10 2020-12-04 2021-12-03 2022-02-21 2021-12-03 2022-12-26 Ultima Claw Pain of Death %C9%F0%B4%EF%2F%A5%DA%A5%A4%A5%F3%A1%A6%A5%AA%A5%D6%A1%A6%A5%C7%A5%B9%20%28SSR%29 {{{link_gamewith|220273}}} FALSE
23 0c21542c-ce18-471e-ac80-1378fc97bec8 38df4067-db48-4dbc-b1cf-c26e019137d8 Scales of Dominion Parazonium 支配の天秤 パラゾニウム 1040415800 1040108700 3 5 6 2 18 TRUE FALSE 150 15 38 40 261 259 320 310 345 459 2020 2652 2440 3200 FALSE 0 FALSE FALSE {} {} 4 2017-07-24 2017-02-28 2019-08-06 2018-02-14 Scales of Dominion Parazonium %C9%F0%B4%EF%2F%A5%D1%A5%E9%A5%BE%A5%CB%A5%A6%A5%E0%20%28SSR%29 161169 71768 パラゾニウム FALSE 3040111000
24 54c220d4-9cee-4f42-b184-5057cb1cb24a 36959849-1ff6-4317-992e-2287b31138eb Esna Dagger of Bahamut Coda エスナ バハムートダガー・フツルス 1040420600 1040106700 3 3 5 6 2 19 TRUE FALSE TRUE 150 200 15 20 50 34 294 229 355 268 307 288 395 1859 2355 2252 2750 3145 FALSE TRUE 0 FALSE TRUE FALSE {} {} 15 2022-07-20 2014-03-10 2022-07-20 2021-12-03 Esna Dagger of Bahamut Coda %C9%F0%B4%EF%2F%A5%A8%A5%B9%A5%CA%20%28SSR%29 352615 FALSE
742f29a3-2fa0-40f9-9275-126d892501b3 Cosmic Blade ブレイド・オブ・コスモス 1040911800 3 6 10 20 TRUE FALSE 150 15 31 184 222 423 2610 3157 FALSE 0 FALSE FALSE {} {} 2016-03-10 2019-12-19 Cosmic Blade FALSE
c250d5c7-0208-49b5-9c88-fc51117dd7d3 Ewiyar's Beak イーウィヤピーク 1040912400 3 1 10 21 TRUE FALSE 150 15 31 192 224 450 2749 3209 FALSE 1 FALSE TRUE {} {} 2020-07-27 2020-07-27 Ewiyar%27s Beak 218570 FALSE
52d41363-16b1-42af-b185-ed1ba1308891 Ameno Habakiri 天羽々斬 1040904300 3 0 10 22 TRUE TRUE 200 20 37 213 250 287 504 3024 3530 4036 FALSE 0 FALSE FALSE {} {} 2014-03-10 2018-04-17 2020-05-11 Ameno Habakiri %C9%F0%B4%EF%C9%BE%B2%C1%2F%A5%B9%A5%DA%A5%EA%A5%AA%A5%EB%A5%B7%A5%EA%A1%BC%A5%BA 75523 天羽々斬 FALSE
a273dcbf-4d85-4898-89ac-41cc80c262d7 Gisla グラーシーザー 1040200700 3 5 4 23 TRUE FALSE 150 15 39 255 309 404 2325 2810 FALSE 0 FALSE FALSE {} {} 2014-05-14 2015-07-27 Gisla FALSE
eeb5882d-63a1-4852-a753-32166b4b9b7f Wasserspeier ヴァッサーシュパイア 1040018100 3 0 1 24 TRUE TRUE 200 20 32 205 240 275 390 2410 2814 3218 FALSE 0 FALSE FALSE {} {} 2019-05-10 2019-05-10 2019-05-10 Wasserspeier %C9%F0%B4%EF%2F%A5%F4%A5%A1%A5%C3%A5%B5%A1%BC%A5%B7%A5%E5%A5%D1%A5%A4%A5%A2%A1%BC%20%28SSR%29 150388 ヴァッサーシュパイアー FALSE
8137294e-6bf1-4bac-a1e0-38cdc542622b Clarion クラリオン 1040511200 3 3 9 25 TRUE TRUE 200 20 27 163 200 237 385 2425 2940 3455 FALSE 0 FALSE FALSE {} {} 15 2019-06-04 2019-06-04 2022-08-19 Clarion %C9%F0%B4%EF%2F%A5%AF%A5%E9%A5%EA%A5%AA%A5%F3%20%28SSR%29 153291 FALSE
63a066c7-9f23-4c12-a921-ec56f584b0ed Kaladanda カラダンダ 1040416300 3 2 6 26 TRUE FALSE 150 15 41 264 320 333 2009 2428 FALSE 0 FALSE FALSE {} {} 2019-12-19 2019-12-19 Kaladanda %C9%F0%B4%EF%2F%A5%AB%A5%E9%A5%C0%A5%F3%A5%C0%20%28SSR%29 180277 カラダンダ FALSE
ac8da736-4041-45e2-b413-f859e6fae828 Magma Rush マグマストリーム 1040408100 3 2 6 27 FALSE FALSE 150 15 36 232 295 1770 FALSE 0 FALSE FALSE {} {} 2016-06-09 2023-07-12 Magma Rush 72005 FALSE
aec45e41-9874-465b-b668-9129a49d40c5 Sephira Emerald Duke セフィラの翠甲 1040610000 3 4 7 28 TRUE FALSE 150 15 38 232 271 350 2175 2540 TRUE 0 FALSE FALSE {} {} 2018-03-22 2020-12-04 Sephira Emerald Duke FALSE
7f70e52a-d18c-4353-a135-1a841d3b7bf2 Rise of Justice ライズ・オブ・ジャスティス 1040020800 3 3 1 29 TRUE TRUE 200 20 34 202 244 286 367 2322 2811 3300 TRUE 0 FALSE FALSE {} {} 2020-12-04 2022-02-21 2022-12-26 Rise of Justice %C9%F0%B4%EF%2F%A5%E9%A5%A4%A5%BA%A1%A6%A5%AA%A5%D6%A1%A6%A5%B8%A5%E3%A5%B9%A5%C6%A5%A3%A5%B9%20%28SSR%29 {{{link_gamewith|220273}}} FALSE
e7a05d2e-a3ec-4620-98a5-d8472d474971 Fang of the Dragonslayer Mk II 竜伐の剛牙・再誕 1040117700 3 4 2 30 TRUE FALSE 150 15 41 247 325 394 2456 3230 FALSE FALSE FALSE {} {} 20 2023-11-09 2023-11-09 Fang of the Dragonslayer Mk II FALSE
af83ceee-3a24-48c7-8cae-9f83276ced81 Hraesvelgr フレズヴェルク 1040515200 3 3 9 31 TRUE FALSE 150 15 37 211 246 514 3131 3654 FALSE 0 FALSE FALSE {} {} 2022-12-26 2022-12-26 Hraesvelgr %C9%F0%B4%EF%2F%A5%D5%A5%EC%A5%BA%A5%F4%A5%A7%A5%EB%A5%AF%20%28SSR%29 366906 FALSE
47208685-e87a-4e07-b328-fb9ac3888718 Worldscathing Leon レオン・オブ・ワールド 1040815100 3 2 8 32 TRUE FALSE 150 15 44 281 350 379 2286 2763 TRUE FALSE FALSE {} {} 10 2023-03-30 2023-12-19 Worldscathing Leon 393901 FALSE
a2f0db22-baf1-4640-8c2e-6d283375744f Exo Antaeus 神銃エクス・アンタイオス 1040516300 3 3 9 33 TRUE FALSE 150 15 29 169 204 394 2488 3012 FALSE FALSE FALSE {} {} 10 2023-09-07 2023-09-07 Exo Antaeus FALSE
b9522d2d-1d29-4a2b-b58c-d3b7c781feb6 Prayer of Grand Gales 狂飆と至高の祈り 1040422200 3 1 6 34 TRUE TRUE 200 20 39 236 285 439 412 2566 3105 3302 TRUE TRUE FALSE {draconic} {ドラポン} 2023-10-23 Prayer of Grand Gales FALSE
81b9845a-a6d5-4aec-bbcf-1678277c1d79 Albacore Body アルバコアボディ 1040423500 3 5 6 35 FALSE FALSE 100 10 32 224 303 1759 FALSE FALSE FALSE {} {} 2024-07-29 Albacore Body アルバコアボディ (SSR) 458282 アルバコアボディ FALSE
f0d13eb4-f462-48d8-8705-16f91c351cb2 Syringe or Treat シリンジ・オア・トリート 1040516400 3 5 9 36 FALSE FALSE 100 10 19 130 504 2930 FALSE FALSE FALSE {} {} 2023-10-18 Syringe or Treat FALSE 3040487000
3d9fad4c-a34f-4133-9d5e-c382a747eeec Demolition-Tiger Axe 絶壊・威寅斧 1040319100 3 4 3 37 TRUE FALSE 150 15 35 207 250 456 2834 3429 FALSE FALSE FALSE {} {} 2024-11-07 2024-11-07 Demolition-Tiger_Axe 絶壊・威寅斧 (SSR) 471097 絶壊・威寅斧 FALSE
4110e59e-5b4c-40f8-ad83-2e62f5d60fc2 Yggdrasil Crystal Blade Arbos 世界樹の晶剣・アルボス 1040026300 3 4 1 38 TRUE FALSE 150 15 32 200 242 377 2332 2821 FALSE FALSE FALSE {} {} 2024-06-03 2024-06-03 Yggdrasil Crystal Blade Arbos FALSE

3066
spec/fixtures/deck_sample.json vendored Normal file

File diff suppressed because it is too large Load diff

2564
spec/fixtures/deck_sample2.json vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,72 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'ImportController', type: :request do
let(:user) { create(:user) }
let(:access_token) do
Doorkeeper::AccessToken.create!(resource_owner_id: user.id, expires_in: 30.days, scopes: 'public')
end
let(:headers) do
{ 'Authorization' => "Bearer #{access_token.token}", 'Content-Type' => 'application/json' }
end
# Load raw deck JSON from fixture and wrap it under the "import" key.
let(:raw_deck_data) do
file_path = Rails.root.join('spec', 'fixtures', 'deck_sample.json')
JSON.parse(File.read(file_path))
end
let(:valid_deck_json) { { 'import' => raw_deck_data }.to_json }
describe 'POST /api/v1/import' do
context 'with valid deck data' do
it 'creates a new party and returns a shortcode' do
expect {
post '/api/v1/import', params: valid_deck_json, headers: headers
}.to change(Party, :count).by(1)
expect(response).to have_http_status(:created)
json_response = JSON.parse(response.body)
expect(json_response).to have_key('shortcode')
end
end
context 'with invalid JSON' do
it 'returns a bad request error' do
post '/api/v1/import', params: 'this is not json', headers: headers
expect(response).to have_http_status(:bad_request)
json_response = JSON.parse(response.body)
expect(json_response['error']).to eq('Invalid JSON data')
end
end
context 'with missing required fields in transformed data' do
it 'returns unprocessable entity error' do
# Here we simulate missing required fields by sending an import hash
# where the 'deck' key is present but missing required subkeys.
invalid_data = { 'import' => { 'deck' => { 'name' => '', 'pc' => nil } } }.to_json
post '/api/v1/import', params: invalid_data, headers: headers
expect(response).to have_http_status(:unprocessable_entity)
json_response = JSON.parse(response.body)
expect(json_response['error']).to eq('Invalid deck data')
end
end
context 'when an error occurs during processing' do
it 'returns unprocessable entity status with error details' do
# Stub the transformer to raise an error when transform is called.
allow_any_instance_of(Processors::CharacterProcessor)
.to receive(:process).and_raise(StandardError.new('Error processing import'))
allow_any_instance_of(Processors::WeaponProcessor)
.to receive(:process).and_raise(StandardError.new('Error processing import'))
allow_any_instance_of(Processors::SummonProcessor)
.to receive(:process).and_raise(StandardError.new('Error processing import'))
allow_any_instance_of(Processors::JobProcessor)
.to receive(:process).and_raise(StandardError.new('Error processing import'))
post '/api/v1/import', params: valid_deck_json, headers: headers
expect(response).to have_http_status(:unprocessable_entity)
json_response = JSON.parse(response.body)
expect(json_response['error']).to eq('Error processing import')
end
end
end
end

View file

@ -107,6 +107,31 @@ RSpec.describe 'Parties API', type: :request do
expect(json['results']).to be_an(Array)
expect(json['meta']).to have_key('count')
end
before do
# For index, assume the controller builds the query with defaults turned on.
# Create one party that meets the default thresholds and one that does not.
# Defaults: weapons_count >= 5, characters_count >= 3, summons_count >= 2.
@good_party = create(:party, user: user,
weapons_count: 5,
characters_count: 4,
summons_count: 2,
visibility: 1)
@bad_party = create(:party, user: user,
weapons_count: 2, # below default threshold
characters_count: 2,
summons_count: 1,
visibility: 1)
end
it 'returns only parties meeting the default filters' do
get '/api/v1/parties', headers: headers
expect(response).to have_http_status(:ok)
json = JSON.parse(response.body)
party_ids = json['results'].map { |p| p['id'] }
expect(party_ids).to include(@good_party.id)
expect(party_ids).not_to include(@bad_party.id)
end
end
describe 'GET /api/v1/parties/favorites' do
@ -123,19 +148,12 @@ RSpec.describe 'Parties API', type: :request do
create_list(:grid_summon, 2, party: party)
party.reload # Reload to update counter caches.
ap "DEBUG: Party counts - characters: #{party.characters_count}, weapons: #{party.weapons_count}, summons: #{party.summons_count}"
create(:favorite, user: user, party: party)
end
before { create(:favorite, user: user, party: party) }
it 'lists parties favorited by the current user' do
# Debug: print IDs returned by the join query (this code can be removed later)
favorite_ids = Party.joins(:favorites).where(favorites: { user_id: user.id }).distinct.pluck(:id)
ap "DEBUG: Created party id: #{party.id}"
ap "DEBUG: Favorite party ids: #{favorite_ids.inspect}"
get '/api/v1/parties/favorites', headers: headers
expect(response).to have_http_status(:ok)
json = JSON.parse(response.body)

View file

@ -34,7 +34,6 @@ RSpec.describe PartyQueryBuilder, type: :model do
it 'returns an ActiveRecord::Relation with filters applied' do
query = subject.build
sql = query.to_sql
# Expect the element filter to be applied (converted to integer)
expect(sql).to include('"parties"."element" = 3')
# Expect the raid filter to be applied
@ -192,6 +191,61 @@ RSpec.describe PartyQueryBuilder, type: :model do
expect(sql).not_to include('NOT EXISTS (')
end
end
context 'when apply_defaults option is true' do
subject do
described_class.new(
base_query,
params: params,
current_user: current_user,
options: { apply_defaults: true }
)
end
it 'adds count filters to the query' do
query = subject.build
sql = query.to_sql
expect(sql).to include('"weapons_count" BETWEEN')
expect(sql).to include('"characters_count" BETWEEN')
expect(sql).to include('"summons_count" BETWEEN')
end
end
context 'when apply_defaults option is false (or not provided)' do
let(:blanked_params) do
{
element: '3',
raid: '123e4567-e89b-12d3-a456-426614174000',
recency: '3600',
full_auto: '1',
auto_guard: '0',
charge_attack: '1',
weapons_count: '', # blank => should use default
characters_count: '',
summons_count: '',
includes: '300001,200002',
excludes: '100003',
name_quality: '1' # dummy flag for testing name_quality clause
}
end
subject do
described_class.new(
base_query,
params: blanked_params,
current_user: current_user,
options: {}
)
end
it 'does not add count filters to the query' do
query = subject.build
sql = query.to_sql
expect(sql).not_to include('weapons_count BETWEEN')
expect(sql).not_to include('characters_count BETWEEN')
expect(sql).not_to include('summons_count BETWEEN')
end
end
end
end
end

View file

@ -0,0 +1,45 @@
# frozen_string_literal: true
require 'rails_helper'
require 'rails_helper'
module Processors
class DummyBaseProcessor < BaseProcessor
# A dummy implementation of process.
def process
"processed"
end
# Expose the protected log method as public for testing.
def public_log(message)
log(message)
end
end
end
RSpec.describe Processors::DummyBaseProcessor, type: :model do
# Note: BaseProcessor.new expects (party, data, options = {})
let(:dummy_party) { nil }
let(:dummy_data) { {} }
let(:processor) { described_class.new(dummy_party, dummy_data) }
describe '#process' do
it 'returns the dummy processed value' do
expect(processor.process).to eq("processed")
end
end
describe '#public_log' do
it 'logs a message containing the processor class name' do
message = "Test log message"
expect(Rails.logger).to receive(:info).with(a_string_including("DummyBaseProcessor", message))
processor.public_log(message)
end
end
after(:each) do |example|
if example.exception
puts "\nDEBUG [DummyBaseProcessor]: #{example.full_description} failed with error: #{example.exception.message}"
end
end
end

View file

@ -0,0 +1,52 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Processors::CharacterProcessor, type: :model do
before(:each) do
@party = create(:party)
end
# Use canonical data loaded via canonical.rb.
let(:deck_data) do
file_path = Rails.root.join('spec', 'fixtures', 'deck_sample2.json')
JSON.parse(File.read(file_path))
end
subject! { described_class.new(@party, deck_data, language: 'en') }
context 'with valid character data' do
it 'creates the correct number of GridCharacter records' do
expect { subject.process }.to change(GridCharacter, :count).by(5)
end
it 'creates GridCharacters with the correct attributes' do
subject.process
grid_chars = GridCharacter.where(party_id: @party.id).order(:position)
# We assume the processor uses the character id from raw_data.
expect(grid_chars[0].character.granblue_id).to eq(deck_data.dig('deck', 'npc', '1', 'master', 'id'))
expect(grid_chars[3].uncap_level).to eq(deck_data.dig('deck', 'npc', '4', 'param', 'evolution').to_i)
expect(grid_chars[4].position).to eq(4)
end
end
context 'with invalid character data' do
let(:deck_data) { 'invalid data' }
it 'does not create any GridCharacter and logs an error containing "CHARACTER"' do
expect { subject.process }.not_to change(GridCharacter, :count)
begin
subject.process
rescue StandardError
nil
end
end
end
after(:each) do |example|
if example.exception
puts "\nDEBUG [CharacterProcessor]: #{example.full_description} failed with error: #{example.exception.message}"
end
end
end

View file

@ -0,0 +1,86 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Processors::JobProcessor, type: :model do
let(:party) { create(:party) }
# Use a job that has associated job skills.
# In our seed/canonical data this job (by its ID) has several associated skills.
let!(:job_record) { Job.find_by!(granblue_id: '130401') }
# Build the raw data hash that mimics the transformed structure.
# The master section includes the job's basic information.
# The param section includes level data and the subskills derived from the job's associated job skills.
let(:deck_data) do
file_path = Rails.root.join('spec', 'fixtures', 'deck_sample2.json')
JSON.parse(File.read(file_path))
end
subject { described_class.new(party, deck_data, language: 'en') }
context 'with valid job data' do
it 'assigns the job to the party' do
# Before processing, the party should not have a job.
expect(party.job).to be_nil
# Process the job data.
subject.process
party.reload
# The party's job should now be set to the job_record.
expect(party.job).to eq(job_record)
end
it 'assigns the correct main skill to the party' do
# Before processing, the party should not have a job.
expect(party.job).to be_nil
# Process the job data.
subject.process
party.reload
main_skill = party.job.skills.where(main: true).first
expect(party.skill0.id).to eq(main_skill.id)
end
it 'associates the correct job skills' do
# Before processing, the party should not have a job.
expect(party.job).to be_nil
# Process the job data.
subject.process
party.reload
# We assume that the processor assigns up to four subskills to party attributes,
# for example, party.skill0, party.skill1, etc.
# Get the expected subskills (using order and taking the first four).
data = deck_data.with_indifferent_access
expected_subskills = data.dig('deck', 'pc', 'set_action').pluck(:name)
actual_subskills = [party.skill1.name_en, party.skill2.name_en, party.skill3.name_en]
expect(actual_subskills).to eq(expected_subskills)
end
it 'assigns the correct accessory to the party' do
# Process the job data.
subject.process
party.reload
expect(party.accessory.granblue_id).to eq(1.to_s)
end
end
context 'with invalid job data' do
let(:invalid_data) { 'invalid data' }
subject { described_class.new(party, invalid_data, language: 'en') }
it 'logs an error and does not assign a job' do
expect { subject.process }.not_to(change { party.reload.job })
end
end
after(:each) do |example|
if example.exception
puts "\nDEBUG [JobProcessor]: #{example.full_description} failed with error: #{example.exception.message}"
end
end
end

View file

@ -0,0 +1,37 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Processors::SummonProcessor, type: :model do
let(:party) { create(:party) }
let(:deck_data) do
file_path = Rails.root.join('spec', 'fixtures', 'deck_sample.json')
JSON.parse(File.read(file_path))
end
subject { described_class.new(party, deck_data, language: 'en') }
context 'with valid summon data' do
it 'creates the correct number of GridSummon records' do
expect { subject.process }.to change(GridSummon, :count).by(7)
end
end
context 'with invalid summon data' do
let(:deck_data) { "invalid data" }
it 'does not create any GridSummon and logs an error containing "SUMMON"' do
expect { subject.process }.not_to change(GridSummon, :count)
begin
subject.process
rescue StandardError
nil
end
end
end
after(:each) do |example|
if example.exception
puts "\nDEBUG [SummonProcessor]: #{example.full_description} failed with error: #{example.exception.message}"
end
end
end

View file

@ -0,0 +1,137 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Processors::WeaponProcessor, type: :model do
let(:party) { create(:party) }
# Minimal deck data for testing private methods.
let(:dummy_deck_data) { { 'deck' => { 'pc' => { 'weapons' => {} } } } }
let(:processor) { described_class.new(party, dummy_deck_data) }
describe '#level_to_transcendence' do
it 'returns 0 for levels less than 200' do
expect(processor.send(:level_to_transcendence, 150)).to eq(0)
end
it 'returns the correct transcendence step for levels >= 200' do
expect(processor.send(:level_to_transcendence, 200)).to eq(0)
expect(processor.send(:level_to_transcendence, 215)).to eq(1)
expect(processor.send(:level_to_transcendence, 250)).to eq(5)
end
end
describe '#matches_key?' do
it 'returns true if candidate key falls within a range' do
expect(processor.send(:matches_key?, '700', '697-706')).to be true
end
it 'returns false if candidate key is below the range' do
expect(processor.send(:matches_key?, '696', '697-706')).to be false
end
it 'returns false if candidate key is above the range' do
expect(processor.send(:matches_key?, '707', '697-706')).to be false
end
it 'returns true if candidate key exactly matches the mapping' do
expect(processor.send(:matches_key?, '700', '700')).to be true
end
end
describe '#process_weapon_ax' do
let(:grid_weapon) { build(:grid_weapon, party: party) }
it 'flattens nested augment_skill_info and assigns ax_modifier and ax_strength' do
ax_skill_info = [
[
{ 'skill_id' => '1588', 'effect_value' => '3', 'show_value' => '3%' },
{ 'skill_id' => '1591', 'effect_value' => '5', 'show_value' => '5%' }
]
]
processor.send(:process_weapon_ax, grid_weapon, ax_skill_info)
expect(grid_weapon.ax_modifier1).to eq(2) # from 1588 → 2
expect(grid_weapon.ax_strength1).to eq(3)
expect(grid_weapon.ax_modifier2).to eq(3) # from 1591 → 3
expect(grid_weapon.ax_strength2).to eq(5)
end
end
describe '#map_arousal_to_awakening' do
it 'returns nil if there is no form key' do
arousal_data = {}
expect(processor.send(:map_arousal_to_awakening, arousal_data)).to be_nil
end
it 'returns the awakening id if found' do
arousal_data = {
"is_arousal_weapon": true,
"level": 4,
"form": 2,
"form_name": 'Defense',
"remain_for_next_level": 0,
"width": 100,
"is_complete_condition": true,
"max_level": 4,
}
awakening = Awakening.find_by(slug: 'weapon-def')
expect(processor.send(:map_arousal_to_awakening, arousal_data)).to eq(awakening.id)
end
end
describe '#process_weapon_keys' do
let(:deck_data) do
file_path = Rails.root.join('spec', 'fixtures', 'deck_sample2.json')
JSON.parse(File.read(file_path))
end
let(:deck_weapon) do
deck_data['deck']['pc']['weapons']['7']
end
let(:canonical_weapon) do
Weapon.find_by(granblue_id: deck_weapon['master']['id'])
end
let(:grid_weapon) do
create(:grid_weapon, weapon: canonical_weapon, party: party)
end
context 'when the raw key is provided via KEY_MAPPING' do
it 'assigns the mapped WeaponKey' do
skill_ids = [deck_weapon['skill1'], deck_weapon['skill2'], deck_weapon['skill3']].compact.map { |s| s['id'] }
processor.send(:process_weapon_keys, grid_weapon, skill_ids)
expect(grid_weapon.weapon_key1_id).to be_nil
expect(grid_weapon.weapon_key2_id).to eq(WeaponKey.find_by(slug: 'pendulum-beta').id)
expect(grid_weapon.weapon_key3_id).to eq(WeaponKey.find_by(slug: 'pendulum-extremity').id)
end
end
context 'when no matching WeaponKey is found' do
it 'logs a warning and does not assign the key' do
processor.send(:process_weapon_keys, grid_weapon, ['unknown'])
expect(grid_weapon.weapon_key1_id).to be_nil
end
end
end
describe 'processing a complete canonical deck' do
let(:deck_data) do
file_path = Rails.root.join('spec', 'fixtures', 'deck_sample2.json')
JSON.parse(File.read(file_path))
end
subject { described_class.new(party, deck_data) }
it 'processes the deck and creates the expected number of GridWeapon records' do
# Assume the canonical records are already loaded (via canonical.rb).
expect { subject.process }.to change(GridWeapon, :count).by(13)
end
it 'creates the correct main weapon' do
# In this canonical deck, the main weapon (slot 1) should be Parazonium.
main_weapon = GridWeapon.find_by(position: -1)
expect(main_weapon).not_to be_nil
expect(main_weapon.weapon.granblue_id).to eq('1040108700')
end
end
end