Add transformers

Transformers take raw data from Granblue Fantasy and transforms them into hensei-compatible JSON. Transformers heavily borrow from vazkii/hensei-transfer.
This commit is contained in:
Justin Edmund 2025-01-17 11:56:15 -08:00
parent 86a16113cc
commit f83a41bf7c
6 changed files with 437 additions and 0 deletions

View file

@ -0,0 +1,91 @@
# frozen_string_literal: true
module Granblue
module Transformers
class BaseDeckTransformer < BaseTransformer
def transform
Rails.logger.info "[TRANSFORM] Starting BaseDeckTransformer#transform"
Rails.logger.info "[TRANSFORM] Data class: #{data.class}"
# Handle already transformed parameters
if data.is_a?(ActionController::Parameters) && data.key?(:name)
Rails.logger.info "[TRANSFORM] Found existing parameters, returning as is"
return data.to_h.symbolize_keys
end
# Handle raw game data
Rails.logger.info "[TRANSFORM] Processing raw game data"
input_data = data['import'] if data.is_a?(Hash)
unless input_data
Rails.logger.error "[TRANSFORM] No import data found"
return {}
end
Rails.logger.info "[TRANSFORM] Found import data"
deck = input_data['deck']
pc = deck['pc'] if deck
unless deck && pc
Rails.logger.error "[TRANSFORM] Missing deck or pc data"
Rails.logger.error "[TRANSFORM] deck present: #{!!deck}"
Rails.logger.error "[TRANSFORM] pc present: #{!!pc}"
return {}
end
Rails.logger.info "[TRANSFORM] Building deck data structure"
result = {
lang: language,
name: deck['name'] || 'Untitled',
class: pc.dig('job', 'master', 'name'),
extra: pc['isExtraDeck'] || false,
subskills: transform_subskills(pc['set_action']),
characters: transform_characters(deck['npc']),
weapons: transform_weapons(pc['weapons']),
summons: transform_summons(pc['summons'], pc['quick_user_summon_id']),
sub_summons: transform_summons(pc['sub_summons']),
friend_summon: pc.dig('damage_info', 'summon_name')
}
Rails.logger.info "[TRANSFORM] Completed transformation"
Rails.logger.debug "[TRANSFORM] Result: #{result}"
result
end
private
def transform_subskills(set_action)
Rails.logger.info "[TRANSFORM] Processing subskills"
unless set_action.is_a?(Array) && !set_action.empty?
Rails.logger.info "[TRANSFORM] No valid set_action data"
return []
end
skills = set_action[0]
unless skills.is_a?(Array)
Rails.logger.info "[TRANSFORM] Invalid skills array"
return []
end
results = skills.map { |skill| skill['name'] if skill.is_a?(Hash) }.compact
Rails.logger.info "[TRANSFORM] Found #{results.length} subskills"
results
end
def transform_characters(npc_data)
Rails.logger.info "[TRANSFORM] Processing characters"
CharacterTransformer.new(npc_data, options).transform
end
def transform_weapons(weapons_data)
Rails.logger.info "[TRANSFORM] Processing weapons"
WeaponTransformer.new(weapons_data, options).transform
end
def transform_summons(summons_data, quick_summon_id = nil)
Rails.logger.info "[TRANSFORM] Processing summons"
SummonTransformer.new(summons_data, quick_summon_id, options).transform
end
end
end
end

View file

@ -0,0 +1,74 @@
# frozen_string_literal: true
module Granblue
module Transformers
class TransformerError < StandardError
attr_reader :details
def initialize(message, details = nil)
@details = details
super(message)
end
end
class BaseTransformer
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
def initialize(data, options = {})
@data = data
@options = options
@language = options[:language] || 'en'
Rails.logger.info "[TRANSFORM] Initializing #{self.class.name} with data: #{data.class}"
validate_data
end
def transform
raise NotImplementedError, "#{self.class} must implement #transform"
end
protected
attr_reader :data, :options, :language
def validate_data
Rails.logger.info "[TRANSFORM] Validating data: #{data.inspect[0..100]}..."
if data.nil?
Rails.logger.info "[TRANSFORM] Data is nil"
return true
end
if data.empty?
Rails.logger.info "[TRANSFORM] Data is empty"
return true
end
# Data validation successful
true
end
def get_master_param(obj)
return [nil, nil] unless obj.is_a?(Hash)
master = obj['master']
param = obj['param']
Rails.logger.debug "[TRANSFORM] Extracted master: #{!!master}, param: #{!!param}"
[master, param]
end
def log_debug(message)
return unless options[:debug]
Rails.logger.debug "[TRANSFORM-DEBUG] #{self.class.name}: #{message}"
end
end
end
end

View file

@ -0,0 +1,51 @@
module Granblue
module Transformers
class CharacterTransformer < BaseTransformer
def transform
Rails.logger.info "[TRANSFORM] Starting CharacterTransformer#transform"
unless data.is_a?(Hash)
Rails.logger.error "[TRANSFORM] Invalid character data structure"
return []
end
characters = []
data.each_value do |char_data|
next unless char_data['master'] && char_data['param']
master = char_data['master']
param = char_data['param']
Rails.logger.debug "[TRANSFORM] Processing character: #{master['name']}"
character = {
name: master['name'],
id: master['id'],
uncap: param['evolution'].to_i
}
Rails.logger.debug "[TRANSFORM] Base character data: #{character}"
# Add perpetuity (rings) if present
if param['has_npcaugment_constant']
character[:ringed] = true
Rails.logger.debug "[TRANSFORM] Character is ringed"
end
# Add transcendence if present
phase = param['phase'].to_i
if phase && phase.positive?
character[:transcend] = phase
Rails.logger.debug "[TRANSFORM] Character has transcendence: #{phase}"
end
characters << character unless master['id'].nil?
Rails.logger.info "[TRANSFORM] Successfully processed character #{character[:name]}"
end
Rails.logger.info "[TRANSFORM] Completed processing #{characters.length} characters"
characters
end
end
end
end

View file

@ -0,0 +1,73 @@
# frozen_string_literal: true
module Granblue
module Transformers
class SummonTransformer < BaseTransformer
TRANSCENDENCE_LEVELS = [210, 220, 230, 240].freeze
def initialize(data, quick_summon_id = nil, options = {})
super(data, options)
@quick_summon_id = quick_summon_id
Rails.logger.info "[TRANSFORM] Initializing SummonTransformer with quick_summon_id: #{quick_summon_id}"
end
def transform
Rails.logger.info "[TRANSFORM] Starting SummonTransformer#transform"
unless data.is_a?(Hash)
Rails.logger.error "[TRANSFORM] Invalid summon data structure"
Rails.logger.error "[TRANSFORM] Data class: #{data.class}"
return []
end
summons = []
data.each_value do |summon_data|
Rails.logger.debug "[TRANSFORM] Processing summon: #{summon_data['master']['name'] if summon_data['master']}"
master, param = get_master_param(summon_data)
unless master && param
Rails.logger.debug "[TRANSFORM] Skipping summon - missing master or param data"
next
end
summon = {
name: master['name'],
id: master['id'],
uncap: param['evolution'].to_i
}
Rails.logger.debug "[TRANSFORM] Base summon data: #{summon}"
# Add transcendence if applicable
if summon[:uncap] > 5
level = param['level'].to_i
trans = calculate_transcendence_level(level)
summon[:transcend] = trans
Rails.logger.debug "[TRANSFORM] Added transcendence level: #{trans}"
end
# Mark quick summon if applicable
if @quick_summon_id && param['id'].to_s == @quick_summon_id.to_s
summon[:qs] = true
Rails.logger.debug "[TRANSFORM] Marked as quick summon"
end
summons << summon
Rails.logger.info "[TRANSFORM] Successfully processed summon #{summon[:name]}"
end
Rails.logger.info "[TRANSFORM] Completed processing #{summons.length} summons"
summons
end
private
def calculate_transcendence_level(level)
return 1 unless level
level = 1 + TRANSCENDENCE_LEVELS.count { |cutoff| level > cutoff }
Rails.logger.debug "[TRANSFORM] Calculated transcendence level: #{level}"
level
end
end
end
end

View file

@ -0,0 +1,12 @@
module Granblue
module Transformers
class TransformerError < StandardError
attr_reader :details
def initialize(message, details = nil)
@details = details
super(message)
end
end
end
end

View file

@ -0,0 +1,136 @@
module Granblue
module Transformers
class WeaponTransformer < BaseTransformer
UNCAP_LEVELS = [40, 60, 80, 100, 150, 200].freeze
TRANSCENDENCE_LEVELS = [210, 220, 230, 240].freeze
MULTIELEMENT_SERIES = [13, 17, 19].freeze
def transform
Rails.logger.info "[TRANSFORM] Starting WeaponTransformer#transform"
unless data.is_a?(Hash)
Rails.logger.error "[TRANSFORM] Invalid weapon data structure"
return []
end
weapons = []
data.each_value do |weapon_data|
next unless weapon_data['master'] && weapon_data['param']
master = weapon_data['master']
param = weapon_data['param']
Rails.logger.debug "[TRANSFORM] Processing weapon: #{master['name']}"
weapon = transform_base_attributes(master, param)
Rails.logger.debug "[TRANSFORM] Base weapon attributes: #{weapon}"
weapon.merge!(transform_awakening(param))
Rails.logger.debug "[TRANSFORM] After awakening: #{weapon[:awakening] if weapon[:awakening]}"
weapon.merge!(transform_ax_skills(param))
Rails.logger.debug "[TRANSFORM] After AX skills: #{weapon[:ax] if weapon[:ax]}"
weapon.merge!(transform_weapon_keys(weapon_data))
Rails.logger.debug "[TRANSFORM] After weapon keys: #{weapon[:keys] if weapon[:keys]}"
weapons << weapon unless master['id'].nil?
Rails.logger.info "[TRANSFORM] Successfully processed weapon #{weapon[:name]}"
end
Rails.logger.info "[TRANSFORM] Completed processing #{weapons.length} weapons"
weapons
end
private
def transform_base_attributes(master, param)
Rails.logger.debug "[TRANSFORM] Processing base attributes for weapon"
series = master['series_id'].to_i
weapon = {
name: master['name'],
id: master['id']
}
# Handle multi-element weapons
if MULTIELEMENT_SERIES.include?(series)
element = master['attribute'].to_i - 1
weapon[:attr] = element
weapon[:id] = (master['id'].to_i - (element * 100)).to_s
Rails.logger.debug "[TRANSFORM] Multi-element weapon adjustments made"
end
# Calculate uncap level
level = param['level'].to_i
uncap = calculate_uncap_level(level)
weapon[:uncap] = uncap
Rails.logger.debug "[TRANSFORM] Calculated uncap level: #{uncap}"
# Add transcendence if applicable
if uncap > 5
trans = calculate_transcendence_level(level)
weapon[:transcend] = trans
Rails.logger.debug "[TRANSFORM] Added transcendence level: #{trans}"
end
weapon
end
def transform_awakening(param)
return {} unless param['arousal']&.[]('is_arousal_weapon')
Rails.logger.debug "[TRANSFORM] Processing weapon awakening"
{
awakening: {
type: param['arousal']['form_name'],
lvl: param['arousal']['level']
}
}
end
def transform_ax_skills(param)
augments = param['augment_skill_info']
return {} unless augments&.first&.any?
Rails.logger.debug "[TRANSFORM] Processing AX skills"
ax = []
augments.first.each_value do |augment|
ax_skill = {
id: augment['skill_id'].to_s,
val: augment['show_value']
}
ax << ax_skill
Rails.logger.debug "[TRANSFORM] Added AX skill: #{ax_skill}"
end
{ ax: ax }
end
def transform_weapon_keys(weapon_data)
Rails.logger.debug "[TRANSFORM] Processing weapon keys"
keys = []
# Add weapon keys if they exist
['skill1', 'skill2', 'skill3'].each do |skill_key|
if weapon_data[skill_key]&.[]('id')
keys << weapon_data[skill_key]['id']
Rails.logger.debug "[TRANSFORM] Added weapon key: #{weapon_data[skill_key]['id']}"
end
end
keys.any? ? { keys: keys } : {}
end
def calculate_uncap_level(level)
return 0 unless level
UNCAP_LEVELS.count { |cutoff| level.to_i > cutoff }
end
def calculate_transcendence_level(level)
return 1 unless level
1 + TRANSCENDENCE_LEVELS.count { |cutoff| level.to_i > cutoff }
end
end
end
end