Merge branch 'staging' into fix-staging-20230122

This commit is contained in:
Justin Edmund 2023-01-22 23:48:11 -08:00 committed by GitHub
commit ec0549cd18
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 822 additions and 254 deletions

View file

@ -45,6 +45,9 @@ gem 'will_paginate', '~> 3.3'
# Migrate and update data alongside your database structure. # Migrate and update data alongside your database structure.
gem 'data_migrate' gem 'data_migrate'
# A ruby gem to allow the copying of ActiveRecord objects and their associated children, configurable with a DSL on the model
gem 'amoeba'
gem 'bootsnap' gem 'bootsnap'
group :doc do group :doc do

View file

@ -67,6 +67,8 @@ GEM
minitest (>= 5.1) minitest (>= 5.1)
tzinfo (~> 2.0) tzinfo (~> 2.0)
amazing_print (1.4.0) amazing_print (1.4.0)
amoeba (3.2.0)
activerecord (>= 4.2.0)
api_matchers (0.6.2) api_matchers (0.6.2)
activesupport (>= 3.2.5) activesupport (>= 3.2.5)
nokogiri (>= 1.5.2) nokogiri (>= 1.5.2)
@ -305,6 +307,7 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
amazing_print amazing_print
amoeba
api_matchers api_matchers
apipie-rails apipie-rails
awesome_nested_set awesome_nested_set

View file

@ -14,6 +14,10 @@ module Api
field :errors, if: ->(_field_name, _error, options) { options.key?(:exception) } do |_, options| field :errors, if: ->(_field_name, _error, options) { options.key?(:exception) } do |_, options|
options[:exception] options[:exception]
end end
field :errors, if: ->(_field_name, object, options) { options.key?(:errors) } do |_, options|
options[:errors]
end
end end
end end
end end

View file

@ -11,11 +11,33 @@ module Api
view :nested do view :nested do
fields :position, :uncap_level, :perpetuity fields :position, :uncap_level, :perpetuity
field :transcendence_step, if: lambda { |_fn, obj, _opt|
obj.character.ulb
} do |c|
c.transcendence_step
end
field :awakening do |c| field :awakening do |c|
{ c.awakening
type: c.awakening_type, end
level: c.awakening_level
} field :over_mastery, if: lambda { |_fn, obj, _opt|
!obj.ring1['modifier'].nil? && !obj.ring2['modifier'].nil?
} do |c|
rings = []
rings.push(c.ring1) unless c.ring1['modifier'].nil?
rings.push(c.ring2) unless c.ring2['modifier'].nil?
rings.push(c.ring3) unless c.ring3['modifier'].nil?
rings.push(c.ring4) unless c.ring4['modifier'].nil?
rings
end
field :aetherial_mastery, if: lambda { |_fn, obj, _opt|
!obj.earring['modifier'].nil?
} do |c|
c.earring
end end
association :character, name: :object, blueprint: CharacterBlueprint association :character, name: :object, blueprint: CharacterBlueprint
@ -25,6 +47,10 @@ module Api
include_view :nested include_view :nested
association :party, blueprint: PartyBlueprint, view: :minimal association :party, blueprint: PartyBlueprint, view: :minimal
end end
view :destroyed do
fields :position, :created_at, :updated_at
end
end end
end end
end end

View file

@ -5,11 +5,11 @@ module Api
class GridSummonBlueprint < ApiBlueprint class GridSummonBlueprint < ApiBlueprint
view :uncap do view :uncap do
association :party, blueprint: PartyBlueprint, view: :minimal association :party, blueprint: PartyBlueprint, view: :minimal
fields :position, :uncap_level fields :position, :uncap_level, :transcendence_step
end end
view :nested do view :nested do
fields :main, :friend, :position, :uncap_level fields :main, :friend, :position, :uncap_level, :transcendence_step
association :summon, name: :object, blueprint: SummonBlueprint association :summon, name: :object, blueprint: SummonBlueprint
end end
@ -17,6 +17,10 @@ module Api
include_view :nested include_view :nested
association :party, blueprint: PartyBlueprint, view: :minimal association :party, blueprint: PartyBlueprint, view: :minimal
end end
view :destroyed do
fields :main, :friend, :position, :created_at, :updated_at
end
end end
end end
end end

View file

@ -43,6 +43,10 @@ module Api
include_view :nested include_view :nested
association :party, blueprint: PartyBlueprint, view: :minimal association :party, blueprint: PartyBlueprint, view: :minimal
end end
view :destroyed do
fields :mainhand, :position, :created_at, :updated_at
end
end end
end end
end end

View file

@ -17,7 +17,7 @@ module Api
] ]
end end
fields :row, :ml, :order fields :granblue_id, :row, :ml, :order
end end
end end
end end

View file

@ -15,7 +15,8 @@ module Api
field :uncap do |w| field :uncap do |w|
{ {
flb: w.flb, flb: w.flb,
ulb: w.ulb ulb: w.ulb,
xlb: w.xlb
} }
end end
@ -24,7 +25,8 @@ module Api
min_hp: w.min_hp, min_hp: w.min_hp,
max_hp: w.max_hp, max_hp: w.max_hp,
max_hp_flb: w.max_hp_flb, max_hp_flb: w.max_hp_flb,
max_hp_ulb: w.max_hp_ulb max_hp_ulb: w.max_hp_ulb,
max_hp_xlb: w.max_hp_xlb
} }
end end
@ -33,7 +35,8 @@ module Api
min_atk: w.min_atk, min_atk: w.min_atk,
max_atk: w.max_atk, max_atk: w.max_atk,
max_atk_flb: w.max_atk_flb, max_atk_flb: w.max_atk_flb,
max_atk_ulb: w.max_atk_ulb max_atk_ulb: w.max_atk_ulb,
max_atk_xlb: w.max_atk_xlb
} }
end end
end end

View file

@ -6,6 +6,8 @@ module Api
attr_reader :party, :incoming_character, :current_characters attr_reader :party, :incoming_character, :current_characters
before_action :find_party, only: :create before_action :find_party, only: :create
before_action :set, only: %i[update destroy]
before_action :check_authorization, only: %i[update destroy]
before_action :find_incoming_character, only: :create before_action :find_incoming_character, only: :create
before_action :find_current_characters, only: :create before_action :find_current_characters, only: :create
@ -19,17 +21,16 @@ module Api
conflict_view = render_conflict_view(conflict_characters, incoming_character, character_params[:position]) conflict_view = render_conflict_view(conflict_characters, incoming_character, character_params[:position])
render json: conflict_view render json: conflict_view
else else
# Replace the grid character in the position if it is already filled # Destroy the grid character in the position if it is already filled
if GridCharacter.where(party_id: party.id, position: character_params[:position]).exists? if GridCharacter.where(party_id: party.id, position: character_params[:position]).exists?
character = GridCharacter.where(party_id: party.id, position: character_params[:position]).limit(1)[0] character = GridCharacter.where(party_id: party.id, position: character_params[:position]).limit(1)[0]
character.character_id = incoming_character.id character.destroy
# Otherwise, create a new grid character
else
character = GridCharacter.create!(character_params.merge(party_id: party.id,
character_id: incoming_character.id))
end end
# Then, create a new grid character
character = GridCharacter.create!(character_params.merge(party_id: party.id,
character_id: incoming_character.id))
if character.save! if character.save!
grid_character_view = render_grid_character_view(character) grid_character_view = render_grid_character_view(character)
render json: grid_character_view, status: :created render json: grid_character_view, status: :created
@ -37,6 +38,20 @@ module Api
end end
end end
def update
mastery = {}
%i[ring1 ring2 ring3 ring4 earring awakening].each do |key|
value = character_params.to_h[key]
mastery[key] = value unless value.nil?
end
@character.attributes = character_params.merge(mastery)
return render json: GridCharacterBlueprint.render(@character, view: :full) if @character.save
render_validation_error_response(@character)
end
def resolve def resolve
incoming = Character.find(resolve_params[:incoming]) incoming = Character.find(resolve_params[:incoming])
conflicting = resolve_params[:conflicting].map { |id| GridCharacter.find(id) } conflicting = resolve_params[:conflicting].map { |id| GridCharacter.find(id) }
@ -70,13 +85,17 @@ module Api
render_unauthorized_response if current_user && (character.party.user != current_user) render_unauthorized_response if current_user && (character.party.user != current_user)
character.uncap_level = character_params[:uncap_level] character.uncap_level = character_params[:uncap_level]
character.transcendence_step = character_params[:transcendence_step]
return unless character.save! return unless character.save!
render json: GridCharacterBlueprint.render(character, view: :nested, root: :grid_character) render json: GridCharacterBlueprint.render(character, view: :nested, root: :grid_character)
end end
# TODO: Implement removing characters # TODO: Implement removing characters
def destroy; end def destroy
render_unauthorized_response if @character.party.user != current_user
return render json: GridCharacterBlueprint.render(@character, view: :destroyed) if @character.destroy
end
private private
@ -103,6 +122,10 @@ module Api
end.flatten end.flatten
end end
def set
@character = GridCharacter.find(params[:id])
end
def find_incoming_character def find_incoming_character
@incoming_character = Character.find(character_params[:character_id]) @incoming_character = Character.find(character_params[:character_id])
end end
@ -112,10 +135,21 @@ module Api
render_unauthorized_response if current_user && (party.user != current_user) render_unauthorized_response if current_user && (party.user != current_user)
end end
def check_authorization
render_unauthorized_response if @character.party.user != current_user
end
# Specify whitelisted properties that can be modified. # Specify whitelisted properties that can be modified.
def character_params def character_params
params.require(:character).permit(:id, :party_id, :character_id, :position, :uncap_level, :conflicting, params.require(:character).permit(:id, :party_id, :character_id, :position,
:incoming) :uncap_level, :transcendence_step, :perpetuity,
ring1: %i[modifier strength], ring2: %i[modifier strength],
ring3: %i[modifier strength], ring4: %i[modifier strength],
earring: %i[modifier strength], awakening: %i[type level])
end
def resolve_params
params.require(:resolve).permit(:position, :incoming, conflicting: [])
end end
def render_conflict_view(conflict_characters, incoming_character, incoming_position) def render_conflict_view(conflict_characters, incoming_character, incoming_position)
@ -129,10 +163,6 @@ module Api
def render_grid_character_view(grid_character) def render_grid_character_view(grid_character)
GridCharacterBlueprint.render(grid_character, view: :nested) GridCharacterBlueprint.render(grid_character, view: :nested)
end end
def resolve_params
params.require(:resolve).permit(:position, :incoming, conflicting: [])
end
end end
end end
end end

View file

@ -3,12 +3,36 @@
module Api module Api
module V1 module V1
class GridSummonsController < Api::V1::ApiController class GridSummonsController < Api::V1::ApiController
before_action :set, only: %w[update destroy]
attr_reader :party, :incoming_summon
before_action :find_party, only: :create
before_action :find_incoming_summon, only: :create
def create def create
party = Party.find(summon_params[:party_id]) # Create the GridSummon with the desired parameters
canonical_summon = Summon.find(summon_params[:summon_id]) summon = GridSummon.new
summon.attributes = summon_params.merge(party_id: party.id, summon_id: incoming_summon.id)
render_unauthorized_response if current_user && (party.user != current_user) if summon.validate
ap 'Validating'
save_summon(summon)
else
ap 'Handling conflict'
handle_conflict(summon)
end
end
def update
@summon.attributes = summon_params
return render json: GridSummonBlueprint.render(@summon, view: :nested, root: :grid_summon) if @summon.save
render_validation_error_response(@character)
end
def save_summon(summon)
if (grid_summon = GridSummon.where( if (grid_summon = GridSummon.where(
party_id: party.id, party_id: party.id,
position: summon_params[:position] position: summon_params[:position]
@ -16,8 +40,23 @@ module Api
GridSummon.destroy(grid_summon.id) GridSummon.destroy(grid_summon.id)
end end
summon = GridSummon.create!(summon_params.merge(party_id: party.id, summon_id: canonical_summon.id)) return unless summon.save
render json: GridSummonBlueprint.render(summon, view: :nested), status: :created if summon.save!
output = render_grid_summon_view(summon)
render json: output, status: :created
end
def handle_conflict(summon)
conflict_summon = summon.conflicts(party)
return unless conflict_summon.summon.id == incoming_summon.id
old_position = conflict_summon.position
conflict_summon.position = summon_params[:position]
return unless conflict_summon.save
output = render_grid_summon_view(conflict_summon, old_position)
render json: output
end end
def update_uncap_level def update_uncap_level
@ -26,19 +65,44 @@ module Api
render_unauthorized_response if current_user && (summon.party.user != current_user) render_unauthorized_response if current_user && (summon.party.user != current_user)
summon.uncap_level = summon_params[:uncap_level] summon.uncap_level = summon_params[:uncap_level]
summon.transcendence_step = 0
return unless summon.save! return unless summon.save!
render json: GridSummonBlueprint.render(summon, view: :nested, root: :grid_summon) render json: GridSummonBlueprint.render(summon, view: :nested, root: :grid_summon)
end end
# TODO: Implement removing summons def destroy
def destroy; end render_unauthorized_response if @summon.party.user != current_user
return render json: GridSummonBlueprint.render(@summon, view: :destroyed) if @summon.destroy
end
private private
def find_incoming_summon
@incoming_summon = Summon.find_by(id: summon_params[:summon_id])
end
def find_party
# BUG: I can create grid weapons even when I'm not logged in on an authenticated party
@party = Party.find(summon_params[:party_id])
render_unauthorized_response if current_user && (party.user != current_user)
end
def render_grid_summon_view(grid_summon, conflict_position = nil)
GridSummonBlueprint.render(grid_summon, view: :nested,
root: :grid_summon,
meta: { replaced: conflict_position })
end
def set
@summon = GridSummon.where('id = ?', params[:id]).first
end
# Specify whitelisted properties that can be modified. # Specify whitelisted properties that can be modified.
def summon_params def summon_params
params.require(:summon).permit(:id, :party_id, :summon_id, :position, :main, :friend, :uncap_level) params.require(:summon).permit(:id, :party_id, :summon_id, :position, :main, :friend, :uncap_level,
:transcendence_step)
end end
end end
end end

View file

@ -3,7 +3,7 @@
module Api module Api
module V1 module V1
class GridWeaponsController < Api::V1::ApiController class GridWeaponsController < Api::V1::ApiController
before_action :set, except: %w[create update_uncap_level destroy] before_action :set, except: %w[create update_uncap_level]
attr_reader :party, :incoming_weapon attr_reader :party, :incoming_weapon
@ -55,8 +55,10 @@ module Api
render json: GridWeaponBlueprint.render(@weapon, view: :nested) if @weapon.update(weapon_params) render json: GridWeaponBlueprint.render(@weapon, view: :nested) if @weapon.update(weapon_params)
end end
# TODO: Implement removing characters def destroy
def destroy; end render_unauthorized_response if @weapon.party.user != current_user
return render json: GridCharacterBlueprint.render(@weapon, view: :destroyed) if @weapon.destroy
end
def update_uncap_level def update_uncap_level
weapon = GridWeapon.find(weapon_params[:id]) weapon = GridWeapon.find(weapon_params[:id])
@ -115,15 +117,15 @@ module Api
# Render the conflict view as a string # Render the conflict view as a string
def render_conflict_view(conflict_weapon, incoming_weapon, incoming_position) def render_conflict_view(conflict_weapon, incoming_weapon, incoming_position)
ConflictBlueprint.render(nil, view: :weapons, ConflictBlueprint.render(nil, view: :weapons,
conflict_weapon: conflict_weapon, conflict_weapon: conflict_weapon,
incoming_weapon: incoming_weapon, incoming_weapon: incoming_weapon,
incoming_position: incoming_position) incoming_position: incoming_position)
end end
def render_grid_weapon_view(grid_weapon, conflict_position) def render_grid_weapon_view(grid_weapon, conflict_position)
GridWeaponBlueprint.render(grid_weapon, view: :full, GridWeaponBlueprint.render(grid_weapon, view: :full,
root: :grid_weapon, root: :grid_weapon,
meta: { replaced: conflict_position }) meta: { replaced: conflict_position })
end end
def save_weapon(weapon) def save_weapon(weapon)

View file

@ -8,12 +8,9 @@ module Api
before_action :set, only: %w[update destroy] before_action :set, only: %w[update destroy]
def create def create
party = Party.new(shortcode: random_string) party = Party.new
party.user = current_user if current_user party.user = current_user if current_user
party.attributes = party_params if party_params
if party_params
party.attributes = party_params
end
# unless party_params.empty? # unless party_params.empty?
# party.attributes = party_params # party.attributes = party_params
@ -57,6 +54,22 @@ module Api
return render json: PartyBlueprint.render(@party, view: :destroyed, root: :checkin) if @party.destroy return render json: PartyBlueprint.render(@party, view: :destroyed, root: :checkin) if @party.destroy
end end
def remix
new_party = @party.amoeba_dup
new_party.attributes = {
user: current_user,
name: remixed_name(@party.name),
source_party: @party
}
if new_party.save
render json: PartyBlueprint.render(new_party, view: :full, root: :party,
meta: { remix: true })
else
render_validation_error_response(new_party)
end
end
def index def index
conditions = build_conditions(request.params) conditions = build_conditions(request.params)
@ -111,7 +124,7 @@ module Api
def build_conditions(params) def build_conditions(params)
unless params['recency'].blank? unless params['recency'].blank?
start_time = (DateTime.current - params['recency'].to_i.seconds) start_time = (DateTime.current - params['recency'].to_i.seconds)
.to_datetime.beginning_of_day .to_datetime.beginning_of_day
end end
{}.tap do |hash| {}.tap do |hash|
@ -122,15 +135,31 @@ module Api
end end
end end
def random_string def remixed_name(name)
num_chars = 6 blanked_name = {
o = [('a'..'z'), ('A'..'Z'), (0..9)].map(&:to_a).flatten en: name.blank? ? 'Untitled team' : name,
(0...num_chars).map { o[rand(o.length)] }.join ja: name.blank? ? '無名の編成' : name
}
if current_user
case current_user.language
when 'en'
"Remix of #{blanked_name[:en]}"
when 'ja'
"#{blanked_name[:ja]}のリミックス"
end
else
"Remix of #{blanked_name[:en]}"
end
end end
def set_from_slug def set_from_slug
@party = Party.where('shortcode = ?', params[:id]).first @party = Party.where('shortcode = ?', params[:id]).first
@party.favorited = current_user && @party ? @party.is_favorited(current_user) : false if @party
@party.favorited = current_user && @party ? @party.is_favorited(current_user) : false
else
render_not_found_response('party')
end
end end
def set def set
@ -138,6 +167,8 @@ module Api
end end
def party_params def party_params
return unless params[:party].present?
params.require(:party).permit( params.require(:party).permit(
:user_id, :user_id,
:extra, :extra,
@ -156,7 +187,7 @@ module Api
:button_count, :button_count,
:turn_count, :turn_count,
:chain_count :chain_count
) if params[:party].present? )
end end
end end
end end

View file

@ -1,7 +1,78 @@
# frozen_string_literal: true # frozen_string_literal: true
class GridCharacter < ApplicationRecord class GridCharacter < ApplicationRecord
belongs_to :party belongs_to :party,
counter_cache: :weapons_count,
inverse_of: :characters
validates_presence_of :party
validate :awakening_level, on: :update
validate :transcendence, on: :update
validate :validate_over_mastery_values, on: :update
validate :validate_aetherial_mastery_value, on: :update
validate :over_mastery_attack_matches_hp, on: :update
##### Amoeba configuration
amoeba do
set ring1: { modifier: nil, strength: nil }
set ring2: { modifier: nil, strength: nil }
set ring3: { modifier: nil, strength: nil }
set ring4: { modifier: nil, strength: nil }
set earring: { modifier: nil, strength: nil }
set perpetuity: false
end
def awakening_level
return if awakening.nil?
errors.add(:awakening, 'awakening level too low') if awakening['level'] < 1
errors.add(:awakening, 'awakening level too high') if awakening['level'] > 9
end
def transcendence
errors.add(:transcendence_step, 'character has no transcendence') if transcendence_step.positive? && !character.ulb
errors.add(:transcendence_step, 'transcendence step too high') if transcendence_step > 5 && character.ulb
errors.add(:transcendence_step, 'transcendence step too low') if transcendence_step.negative? && character.ulb
end
def over_mastery_attack
errors.add(:ring1, 'invalid value') unless ring1['modifier'].nil? || atk_values.include?(ring1['strength'])
end
def over_mastery_hp
return if ring2['modifier'].nil?
errors.add(:ring2, 'invalid value') unless hp_values.include?(ring2['strength'])
end
def over_mastery_attack_matches_hp
return if ring1[:modifier].nil? && ring2[:modifier].nil?
return if ring2[:strength] == (ring1[:strength] / 2)
errors.add(:over_mastery,
'over mastery attack and hp values do not match')
end
def validate_over_mastery_values
[ring1, ring2, ring3, ring4].each_with_index do |ring, index|
next if ring['modifier'].nil?
modifier = over_mastery_modifiers[ring['modifier']]
check_value({ "ring#{index}": { ring[modifier] => ring['strength'] } },
'over_mastery')
end
end
def validate_aetherial_mastery_value
return if earring['modifier'].nil?
return unless earring['modifier'].positive?
modifier = aetherial_mastery_modifiers[earring['modifier']].to_sym
check_value({ "earring": { modifier => earring['strength'] } },
'aetherial_mastery')
end
def character def character
Character.find(character_id) Character.find(character_id)
@ -10,4 +81,132 @@ class GridCharacter < ApplicationRecord
def blueprint def blueprint
GridCharacterBlueprint GridCharacterBlueprint
end end
private
def check_value(property, type)
# Input format
# { ring1: { atk: 300 } }
key = property.keys.first
modifier = property[key].keys.first
return if modifier.nil?
case type
when 'over_mastery'
errors.add(key, 'invalid value') unless over_mastery_values.include?(key['strength'])
when 'aetherial_mastery'
errors.add(key, 'value too low') if aetherial_mastery_values[modifier][:min] > self[key]['strength']
errors.add(key, 'value too high') if aetherial_mastery_values[modifier][:max] < self[key]['strength']
end
end
def over_mastery_modifiers
{
1 => 'atk',
2 => 'hp',
3 => 'debuff_success',
4 => 'skill_cap',
5 => 'ca_dmg',
6 => 'ca_cap',
7 => 'stamina',
8 => 'enmity',
9 => 'crit',
10 => 'da',
11 => 'ta',
12 => 'def',
13 => 'heal',
14 => 'debuff_resist',
15 => 'dodge'
}
end
def over_mastery_values
{
atk: [300, 600, 900, 1200, 1500, 1800, 2100, 2400, 2700, 3000],
hp: [150, 300, 450, 600, 750, 900, 1050, 1200, 1350, 1500],
debuff_success: [6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
skill_cap: [6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
ca_dmg: [10, 12, 14, 16, 18, 20, 22, 24, 27, 30],
ca_cap: [6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
crit: [10, 12, 14, 16, 18, 20, 22, 24, 27, 30],
enmity: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
stamina: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
def: [6, 7, 8, 9, 10, 12, 14, 16, 18, 20],
heal: [3, 6, 9, 12, 15, 18, 21, 24, 27, 30],
debuff_resist: [6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
dodge: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
da: [6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
ta: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
end
def aetherial_mastery_modifiers
{
1 => 'da',
2 => 'ta',
3 => 'ele_atk',
4 => 'ele_resist',
5 => 'stamina',
6 => 'enmity',
7 => 'supplemental',
8 => 'crit',
9 => 'counter_dodge',
10 => 'counter_dmg'
}
end
def aetherial_mastery_values
{
da: {
min: 10,
max: 17
},
ta: {
min: 5,
max: 12
},
ele_atk: {
min: 15,
max: 22
},
ele_resist: {
min: 5,
max: 12
},
stamina: {
min: 5,
max: 12
},
enmity: {
min: 5,
max: 12
},
supplemental: {
min: 5,
max: 12
},
crit: {
min: 18,
max: 35
},
counter_dodge: {
min: 5,
max: 12
},
counter_dmg: {
min: 10,
max: 17
}
}
end
def atk_values
[300, 600, 900, 1200, 1500, 1800, 2100, 2400, 2700, 3000]
end
def hp_values
[150, 300, 450, 600, 750, 900, 1050, 1200, 1350, 1500]
end
end end

View file

@ -1,7 +1,13 @@
# frozen_string_literal: true # frozen_string_literal: true
class GridSummon < ApplicationRecord class GridSummon < ApplicationRecord
belongs_to :party belongs_to :party,
counter_cache: :weapons_count,
inverse_of: :summons
validates_presence_of :party
validate :compatible_with_position, on: :create
validate :no_conflicts, on: :create
def summon def summon
Summon.find(summon_id) Summon.find(summon_id)
@ -10,4 +16,36 @@ class GridSummon < ApplicationRecord
def blueprint def blueprint
GridSummonBlueprint GridSummonBlueprint
end end
# Returns conflicting summons if they exist
def conflicts(party)
return unless summon.limit
party.summons.find do |party_summon|
ap 'Normal summon:'
ap summon
ap 'Party summon:'
ap party_summon
summon if summon.id == party_summon.summon.id
end
end
private
# Validates whether there is a conflict with the party
def no_conflicts
ap conflicts(party)
# Check if the grid weapon conflicts with any of the other grid weapons in the party
errors.add(:series, 'must not conflict with existing summons') unless conflicts(party).nil?
end
# Validates whether the weapon can be added to the desired position
def compatible_with_position
ap [4, 5].include?(position.to_i) && !summon.subaura
return unless [4, 5].include?(position.to_i) && !summon.subaura
errors.add(:position, 'must have subaura for position')
end
end end

View file

@ -2,7 +2,9 @@
class GridWeapon < ApplicationRecord class GridWeapon < ApplicationRecord
belongs_to :party, belongs_to :party,
counter_cache: :weapons_count counter_cache: :weapons_count,
inverse_of: :weapons
validates_presence_of :party
belongs_to :weapon_key1, class_name: 'WeaponKey', foreign_key: :weapon_key1_id, optional: true belongs_to :weapon_key1, class_name: 'WeaponKey', foreign_key: :weapon_key1_id, optional: true
belongs_to :weapon_key2, class_name: 'WeaponKey', foreign_key: :weapon_key2_id, optional: true belongs_to :weapon_key2, class_name: 'WeaponKey', foreign_key: :weapon_key2_id, optional: true
@ -11,6 +13,14 @@ class GridWeapon < ApplicationRecord
validate :compatible_with_position, on: :create validate :compatible_with_position, on: :create
validate :no_conflicts, on: :create validate :no_conflicts, on: :create
##### Amoeba configuration
amoeba do
nullify :ax_modifier1
nullify :ax_modifier2
nullify :ax_strength1
nullify :ax_strength2
end
# Helper methods # Helper methods
def blueprint def blueprint
GridWeaponBlueprint GridWeaponBlueprint
@ -29,6 +39,8 @@ class GridWeapon < ApplicationRecord
return unless weapon.limit return unless weapon.limit
party.weapons.find do |party_weapon| party.weapons.find do |party_weapon|
return unless party_weapon.id
id_match = weapon.id == party_weapon.id id_match = weapon.id == party_weapon.id
series_match = weapon.series == party_weapon.weapon.series series_match = weapon.series == party_weapon.weapon.series
both_opus_or_draconic = weapon.opus_or_draconic? && party_weapon.weapon.opus_or_draconic? both_opus_or_draconic = weapon.opus_or_draconic? && party_weapon.weapon.opus_or_draconic?

View file

@ -2,6 +2,16 @@
class Party < ApplicationRecord class Party < ApplicationRecord
##### ActiveRecord Associations ##### ActiveRecord Associations
belongs_to :source_party,
class_name: 'Party',
foreign_key: :source_party_id,
optional: true
has_many :derivative_parties,
class_name: 'Party',
foreign_key: :source_party_id,
inverse_of: :source_party
belongs_to :user, optional: true belongs_to :user, optional: true
belongs_to :raid, optional: true belongs_to :raid, optional: true
belongs_to :job, optional: true belongs_to :job, optional: true
@ -29,20 +39,35 @@ class Party < ApplicationRecord
has_many :characters, has_many :characters,
foreign_key: 'party_id', foreign_key: 'party_id',
class_name: 'GridCharacter', class_name: 'GridCharacter',
dependent: :destroy dependent: :destroy,
inverse_of: :party
has_many :weapons, has_many :weapons,
foreign_key: 'party_id', foreign_key: 'party_id',
class_name: 'GridWeapon', class_name: 'GridWeapon',
dependent: :destroy dependent: :destroy,
inverse_of: :party
has_many :summons, has_many :summons,
foreign_key: 'party_id', foreign_key: 'party_id',
class_name: 'GridSummon', class_name: 'GridSummon',
dependent: :destroy dependent: :destroy,
inverse_of: :party
has_many :favorites has_many :favorites
before_create :set_shortcode
##### Amoeba configuration
amoeba do
nullify :description
nullify :shortcode
include_association :characters
include_association :weapons
include_association :summons
end
##### ActiveRecord Validations ##### ActiveRecord Validations
validate :skills_are_unique validate :skills_are_unique
@ -58,6 +83,16 @@ class Party < ApplicationRecord
private private
def set_shortcode
self.shortcode = random_string
end
def random_string
num_chars = 6
o = [('a'..'z'), ('A'..'Z'), (0..9)].map(&:to_a).flatten
(0...num_chars).map { o[rand(o.length)] }.join
end
def skills_are_unique def skills_are_unique
skills = [skill0, skill1, skill2, skill3].compact skills = [skill0, skill1, skill2, skill3].compact

View file

@ -8,13 +8,16 @@ Rails.application.routes.draw do
namespace :v1 do namespace :v1 do
resources :parties, only: %i[index create update destroy] resources :parties, only: %i[index create update destroy]
resources :users, only: %i[create update show] resources :users, only: %i[create update show]
resources :grid_weapons, only: [:update] resources :grid_weapons, only: %i[update destroy]
resources :grid_characters, only: %i[update destroy]
resources :grid_summons, only: %i[update destroy]
resources :favorites, only: [:create] resources :favorites, only: [:create]
get 'users/info/:id', to: 'users#info' get 'users/info/:id', to: 'users#info'
get 'parties/favorites', to: 'parties#favorites' get 'parties/favorites', to: 'parties#favorites'
get 'parties/:id', to: 'parties#show' get 'parties/:id', to: 'parties#show'
post 'parties/:id/remix', to: 'parties#remix'
put 'parties/:id/jobs', to: 'jobs#update_job' put 'parties/:id/jobs', to: 'jobs#update_job'
put 'parties/:id/job_skills', to: 'jobs#update_job_skills' put 'parties/:id/job_skills', to: 'jobs#update_job_skills'

View file

@ -0,0 +1,5 @@
class ChangeAwakeningTypeDefaultValue < ActiveRecord::Migration[7.0]
def change
change_column :grid_characters, :awakening_type, :integer, null: false, default: 1
end
end

View file

@ -0,0 +1,22 @@
class ChangeMasteryColumnsToJsonb < ActiveRecord::Migration[7.0]
def change
# Remove old columns
remove_column :grid_characters, :ring_modifier1, :integer
remove_column :grid_characters, :ring_modifier2, :integer
remove_column :grid_characters, :ring_modifier3, :integer
remove_column :grid_characters, :ring_modifier4, :integer
remove_column :grid_characters, :ring_strength1, :integer
remove_column :grid_characters, :ring_strength2, :integer
remove_column :grid_characters, :ring_strength3, :integer
remove_column :grid_characters, :ring_strength4, :integer
remove_column :grid_characters, :earring_modifier, :integer
remove_column :grid_characters, :earring_strength, :integer
# Add new columns
add_column :grid_characters, :ring1, :jsonb, default: { modifier: nil, strength: nil }
add_column :grid_characters, :ring2, :jsonb, default: { modifier: nil, strength: nil }
add_column :grid_characters, :ring3, :jsonb, default: { modifier: nil, strength: nil }
add_column :grid_characters, :ring4, :jsonb, default: { modifier: nil, strength: nil }
add_column :grid_characters, :earring, :jsonb, default: { modifier: nil, strength: nil }
end
end

View file

@ -0,0 +1,10 @@
class ChangeAwakeningColumnsToJsonb < ActiveRecord::Migration[7.0]
def change
# Remove old columns
remove_column :grid_characters, :awakening_type, :integer
remove_column :grid_characters, :awakening_level, :integer
# Add new column
add_column :grid_characters, :awakening, :jsonb, default: { type: 1, level: 1 }
end
end

View file

@ -0,0 +1,10 @@
class MakeMasteryColumnsNotNullable < ActiveRecord::Migration[7.0]
def change
change_column :grid_characters, :ring1, :jsonb, null: false
change_column :grid_characters, :ring2, :jsonb, null: false
change_column :grid_characters, :ring3, :jsonb, null: false
change_column :grid_characters, :ring4, :jsonb, null: false
change_column :grid_characters, :earring, :jsonb, null: false
change_column :grid_characters, :awakening, :jsonb, null: false
end
end

View file

@ -0,0 +1,7 @@
class AddSourcePartyToParties < ActiveRecord::Migration[7.0]
def change
change_table(:parties) do |t|
t.references :source_party, type: :uuid, foreign_key: { to_table: 'parties' }
end
end
end

View file

@ -0,0 +1,6 @@
class AddMaxHpatkxlbToSummon < ActiveRecord::Migration[7.0]
def change
add_column :summons, :max_atk_xlb, :integer
add_column :summons, :max_hp_xlb, :integer
end
end

View file

@ -0,0 +1,5 @@
class AddGranblueIdToJobs < ActiveRecord::Migration[7.0]
def change
add_column :jobs, :granblue_id, :string
end
end

View file

@ -10,128 +10,122 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_01_03_180458) do ActiveRecord::Schema[7.0].define(version: 2023_01_23_055508) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "btree_gin" enable_extension 'btree_gin'
enable_extension "pg_trgm" enable_extension 'pg_trgm'
enable_extension "pgcrypto" enable_extension 'pgcrypto'
enable_extension "plpgsql" enable_extension 'plpgsql'
create_table "characters", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| create_table 'characters', id: :uuid, default: -> { 'gen_random_uuid()' }, force: :cascade do |t|
t.string "name_en" t.string 'name_en'
t.string "name_jp" t.string 'name_jp'
t.string "granblue_id" t.string 'granblue_id'
t.integer "rarity" t.integer 'rarity'
t.integer "element" t.integer 'element'
t.integer "proficiency1" t.integer 'proficiency1'
t.integer "proficiency2" t.integer 'proficiency2'
t.integer "gender" t.integer 'gender'
t.integer "race1" t.integer 'race1'
t.integer "race2" t.integer 'race2'
t.boolean "flb", default: false, null: false t.boolean 'flb', default: false, null: false
t.integer "min_hp" t.integer 'min_hp'
t.integer "max_hp" t.integer 'max_hp'
t.integer "max_hp_flb" t.integer 'max_hp_flb'
t.integer "min_atk" t.integer 'min_atk'
t.integer "max_atk" t.integer 'max_atk'
t.integer "max_atk_flb" t.integer 'max_atk_flb'
t.integer "base_da" t.integer 'base_da'
t.integer "base_ta" t.integer 'base_ta'
t.float "ougi_ratio" t.float 'ougi_ratio'
t.float "ougi_ratio_flb" t.float 'ougi_ratio_flb'
t.boolean "special", default: false, null: false t.boolean 'special', default: false, null: false
t.boolean "ulb", default: false, null: false t.boolean 'ulb', default: false, null: false
t.integer "max_hp_ulb" t.integer 'max_hp_ulb'
t.integer "max_atk_ulb" t.integer 'max_atk_ulb'
t.integer "character_id", default: [], null: false, array: true t.integer 'character_id', default: [], null: false, array: true
t.index ["name_en"], name: "index_characters_on_name_en", opclass: :gin_trgm_ops, using: :gin t.index ['name_en'], name: 'index_characters_on_name_en', opclass: :gin_trgm_ops, using: :gin
end end
create_table "data_migrations", primary_key: "version", id: :string, force: :cascade do |t| create_table 'data_migrations', primary_key: 'version', id: :string, force: :cascade do |t|
end end
create_table "favorites", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| create_table 'favorites', id: :uuid, default: -> { 'gen_random_uuid()' }, force: :cascade do |t|
t.uuid "user_id" t.uuid 'user_id'
t.uuid "party_id" t.uuid 'party_id'
t.datetime "created_at", null: false t.datetime 'created_at', null: false
t.datetime "updated_at", null: false t.datetime 'updated_at', null: false
t.index ["party_id"], name: "index_favorites_on_party_id" t.index ['party_id'], name: 'index_favorites_on_party_id'
t.index ["user_id"], name: "index_favorites_on_user_id" t.index ['user_id'], name: 'index_favorites_on_user_id'
end end
create_table "grid_characters", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| create_table 'grid_characters', id: :uuid, default: -> { 'gen_random_uuid()' }, force: :cascade do |t|
t.uuid "party_id" t.uuid 'party_id'
t.uuid "character_id" t.uuid 'character_id'
t.integer "uncap_level" t.integer 'uncap_level'
t.integer "position" t.integer 'position'
t.datetime "created_at", null: false t.datetime 'created_at', null: false
t.datetime "updated_at", null: false t.datetime 'updated_at', null: false
t.boolean "perpetuity", default: false, null: false t.boolean 'perpetuity', default: false, null: false
t.integer "awakening_type", default: 0, null: false t.integer 'transcendence_step', default: 0, null: false
t.integer "awakening_level", default: 1, null: false t.jsonb 'ring1', default: { 'modifier' => nil, 'strength' => nil }, null: false
t.integer "transcendence_step", default: 0, null: false t.jsonb 'ring2', default: { 'modifier' => nil, 'strength' => nil }, null: false
t.integer "ring_modifier1" t.jsonb 'ring3', default: { 'modifier' => nil, 'strength' => nil }, null: false
t.float "ring_strength1" t.jsonb 'ring4', default: { 'modifier' => nil, 'strength' => nil }, null: false
t.integer "ring_modifier2" t.jsonb 'earring', default: { 'modifier' => nil, 'strength' => nil }, null: false
t.float "ring_strength2" t.jsonb 'awakening', default: { 'type' => 1, 'level' => 1 }, null: false
t.integer "ring_modifier3" t.index ['character_id'], name: 'index_grid_characters_on_character_id'
t.float "ring_strength3" t.index ['party_id'], name: 'index_grid_characters_on_party_id'
t.integer "ring_modifier4"
t.float "ring_strength4"
t.integer "earring_modifier"
t.float "earring_strength"
t.index ["character_id"], name: "index_grid_characters_on_character_id"
t.index ["party_id"], name: "index_grid_characters_on_party_id"
end end
create_table "grid_summons", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| create_table 'grid_summons', id: :uuid, default: -> { 'gen_random_uuid()' }, force: :cascade do |t|
t.uuid "party_id" t.uuid 'party_id'
t.uuid "summon_id" t.uuid 'summon_id'
t.integer "uncap_level" t.integer 'uncap_level'
t.boolean "main" t.boolean 'main'
t.boolean "friend" t.boolean 'friend'
t.integer "position" t.integer 'position'
t.datetime "created_at", null: false t.datetime 'created_at', null: false
t.datetime "updated_at", null: false t.datetime 'updated_at', null: false
t.integer "transcendence_step", default: 0, null: false t.integer 'transcendence_step', default: 0, null: false
t.index ["party_id"], name: "index_grid_summons_on_party_id" t.index ['party_id'], name: 'index_grid_summons_on_party_id'
t.index ["summon_id"], name: "index_grid_summons_on_summon_id" t.index ['summon_id'], name: 'index_grid_summons_on_summon_id'
end end
create_table "grid_weapons", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| create_table 'grid_weapons', id: :uuid, default: -> { 'gen_random_uuid()' }, force: :cascade do |t|
t.uuid "party_id" t.uuid 'party_id'
t.uuid "weapon_id" t.uuid 'weapon_id'
t.uuid "weapon_key1_id" t.uuid 'weapon_key1_id'
t.uuid "weapon_key2_id" t.uuid 'weapon_key2_id'
t.integer "uncap_level" t.integer 'uncap_level'
t.boolean "mainhand" t.boolean 'mainhand'
t.integer "position" t.integer 'position'
t.datetime "created_at", null: false t.datetime 'created_at', null: false
t.datetime "updated_at", null: false t.datetime 'updated_at', null: false
t.uuid "weapon_key3_id" t.uuid 'weapon_key3_id'
t.integer "ax_modifier1" t.integer 'ax_modifier1'
t.float "ax_strength1" t.float 'ax_strength1'
t.integer "ax_modifier2" t.integer 'ax_modifier2'
t.float "ax_strength2" t.float 'ax_strength2'
t.integer "element" t.integer 'element'
t.integer "awakening_type" t.integer 'awakening_type'
t.integer "awakening_level", default: 1, null: false t.integer 'awakening_level', default: 1, null: false
t.index ["party_id"], name: "index_grid_weapons_on_party_id" t.index ['party_id'], name: 'index_grid_weapons_on_party_id'
t.index ["weapon_id"], name: "index_grid_weapons_on_weapon_id" t.index ['weapon_id'], name: 'index_grid_weapons_on_weapon_id'
end end
create_table "job_skills", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| create_table 'job_skills', id: :uuid, default: -> { 'gen_random_uuid()' }, force: :cascade do |t|
t.uuid "job_id" t.uuid 'job_id'
t.string "name_en", null: false t.string 'name_en', null: false
t.string "name_jp", null: false t.string 'name_jp', null: false
t.string "slug", null: false t.string 'slug', null: false
t.integer "color", null: false t.integer 'color', null: false
t.boolean "main", default: false t.boolean 'main', default: false
t.boolean "sub", default: false t.boolean 'sub', default: false
t.boolean "emp", default: false t.boolean 'emp', default: false
t.integer "order" t.integer 'order'
t.boolean "base", default: false t.boolean 'base', default: false
t.index ["job_id"], name: "index_job_skills_on_job_id" t.index ['job_id'], name: 'index_job_skills_on_job_id'
end end
create_table "jobs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| create_table "jobs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
@ -143,45 +137,46 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_03_180458) do
t.boolean "ml", default: false t.boolean "ml", default: false
t.integer "order" t.integer "order"
t.uuid "base_job_id" t.uuid "base_job_id"
t.string "granblue_id"
t.index ["base_job_id"], name: "index_jobs_on_base_job_id" t.index ["base_job_id"], name: "index_jobs_on_base_job_id"
end end
create_table "oauth_access_grants", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| create_table 'oauth_access_grants', id: :uuid, default: -> { 'gen_random_uuid()' }, force: :cascade do |t|
t.uuid "resource_owner_id", null: false t.uuid 'resource_owner_id', null: false
t.uuid "application_id", null: false t.uuid 'application_id', null: false
t.string "token", null: false t.string 'token', null: false
t.integer "expires_in", null: false t.integer 'expires_in', null: false
t.text "redirect_uri", null: false t.text 'redirect_uri', null: false
t.datetime "created_at", precision: nil, null: false t.datetime 'created_at', precision: nil, null: false
t.datetime "revoked_at", precision: nil t.datetime 'revoked_at', precision: nil
t.string "scopes" t.string 'scopes'
t.index ["token"], name: "index_oauth_access_grants_on_token", unique: true t.index ['token'], name: 'index_oauth_access_grants_on_token', unique: true
end end
create_table "oauth_access_tokens", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| create_table 'oauth_access_tokens', id: :uuid, default: -> { 'gen_random_uuid()' }, force: :cascade do |t|
t.uuid "resource_owner_id" t.uuid 'resource_owner_id'
t.uuid "application_id" t.uuid 'application_id'
t.string "token", null: false t.string 'token', null: false
t.string "refresh_token" t.string 'refresh_token'
t.integer "expires_in" t.integer 'expires_in'
t.datetime "revoked_at", precision: nil t.datetime 'revoked_at', precision: nil
t.datetime "created_at", precision: nil, null: false t.datetime 'created_at', precision: nil, null: false
t.string "scopes" t.string 'scopes'
t.string "previous_refresh_token", default: "", null: false t.string 'previous_refresh_token', default: '', null: false
t.index ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true t.index ['refresh_token'], name: 'index_oauth_access_tokens_on_refresh_token', unique: true
t.index ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id" t.index ['resource_owner_id'], name: 'index_oauth_access_tokens_on_resource_owner_id'
t.index ["token"], name: "index_oauth_access_tokens_on_token", unique: true t.index ['token'], name: 'index_oauth_access_tokens_on_token', unique: true
end end
create_table "oauth_applications", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| create_table 'oauth_applications', id: :uuid, default: -> { 'gen_random_uuid()' }, force: :cascade do |t|
t.string "name", null: false t.string 'name', null: false
t.string "uid", null: false t.string 'uid', null: false
t.string "secret", null: false t.string 'secret', null: false
t.text "redirect_uri", null: false t.text 'redirect_uri', null: false
t.string "scopes", default: "", null: false t.string 'scopes', default: '', null: false
t.datetime "created_at", null: false t.datetime 'created_at', null: false
t.datetime "updated_at", null: false t.datetime 'updated_at', null: false
t.index ["uid"], name: "index_oauth_applications_on_uid", unique: true t.index ['uid'], name: 'index_oauth_applications_on_uid', unique: true
end end
create_table "parties", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| create_table "parties", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
@ -208,21 +203,23 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_03_180458) do
t.integer "button_count" t.integer "button_count"
t.integer "chain_count" t.integer "chain_count"
t.integer "turn_count" t.integer "turn_count"
t.uuid "source_party_id"
t.index ["job_id"], name: "index_parties_on_job_id" t.index ["job_id"], name: "index_parties_on_job_id"
t.index ["skill0_id"], name: "index_parties_on_skill0_id" t.index ["skill0_id"], name: "index_parties_on_skill0_id"
t.index ["skill1_id"], name: "index_parties_on_skill1_id" t.index ["skill1_id"], name: "index_parties_on_skill1_id"
t.index ["skill2_id"], name: "index_parties_on_skill2_id" t.index ["skill2_id"], name: "index_parties_on_skill2_id"
t.index ["skill3_id"], name: "index_parties_on_skill3_id" t.index ["skill3_id"], name: "index_parties_on_skill3_id"
t.index ["source_party_id"], name: "index_parties_on_source_party_id"
t.index ["user_id"], name: "index_parties_on_user_id" t.index ["user_id"], name: "index_parties_on_user_id"
end end
create_table "raids", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| create_table 'raids', id: :uuid, default: -> { 'gen_random_uuid()' }, force: :cascade do |t|
t.string "name_en" t.string 'name_en'
t.string "name_jp" t.string 'name_jp'
t.integer "level" t.integer 'level'
t.integer "group" t.integer 'group'
t.integer "element" t.integer 'element'
t.string "slug" t.string 'slug'
end end
create_table "summons", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| create_table "summons", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
@ -246,60 +243,62 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_03_180458) do
t.boolean "subaura", default: false, null: false t.boolean "subaura", default: false, null: false
t.boolean "limit", default: false, null: false t.boolean "limit", default: false, null: false
t.boolean "xlb", default: false, null: false t.boolean "xlb", default: false, null: false
t.integer "max_atk_xlb"
t.integer "max_hp_xlb"
t.index ["name_en"], name: "index_summons_on_name_en", opclass: :gin_trgm_ops, using: :gin t.index ["name_en"], name: "index_summons_on_name_en", opclass: :gin_trgm_ops, using: :gin
end end
create_table "users", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| create_table 'users', id: :uuid, default: -> { 'gen_random_uuid()' }, force: :cascade do |t|
t.string "email" t.string 'email'
t.string "password_digest" t.string 'password_digest'
t.string "username" t.string 'username'
t.integer "granblue_id" t.integer 'granblue_id'
t.datetime "created_at", null: false t.datetime 'created_at', null: false
t.datetime "updated_at", null: false t.datetime 'updated_at', null: false
t.string "picture", default: "gran" t.string 'picture', default: 'gran'
t.string "language", default: "en", null: false t.string 'language', default: 'en', null: false
t.boolean "private", default: false, null: false t.boolean 'private', default: false, null: false
t.string "element", default: "water", null: false t.string 'element', default: 'water', null: false
t.integer "gender", default: 0, null: false t.integer 'gender', default: 0, null: false
t.string "theme", default: "system", null: false t.string 'theme', default: 'system', null: false
end end
create_table "weapon_keys", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| create_table 'weapon_keys', id: :uuid, default: -> { 'gen_random_uuid()' }, force: :cascade do |t|
t.string "name_en" t.string 'name_en'
t.string "name_jp" t.string 'name_jp'
t.integer "series" t.integer 'series'
t.integer "slot" t.integer 'slot'
t.integer "group" t.integer 'group'
t.integer "order" t.integer 'order'
t.string "slug" t.string 'slug'
end end
create_table "weapons", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| create_table 'weapons', id: :uuid, default: -> { 'gen_random_uuid()' }, force: :cascade do |t|
t.string "name_en" t.string 'name_en'
t.string "name_jp" t.string 'name_jp'
t.string "granblue_id" t.string 'granblue_id'
t.integer "rarity" t.integer 'rarity'
t.integer "element" t.integer 'element'
t.integer "proficiency" t.integer 'proficiency'
t.integer "series", default: -1, null: false t.integer 'series', default: -1, null: false
t.boolean "flb", default: false, null: false t.boolean 'flb', default: false, null: false
t.boolean "ulb", default: false, null: false t.boolean 'ulb', default: false, null: false
t.integer "max_level", default: 100, null: false t.integer 'max_level', default: 100, null: false
t.integer "max_skill_level", default: 10, null: false t.integer 'max_skill_level', default: 10, null: false
t.integer "min_hp" t.integer 'min_hp'
t.integer "max_hp" t.integer 'max_hp'
t.integer "max_hp_flb" t.integer 'max_hp_flb'
t.integer "max_hp_ulb" t.integer 'max_hp_ulb'
t.integer "min_atk" t.integer 'min_atk'
t.integer "max_atk" t.integer 'max_atk'
t.integer "max_atk_flb" t.integer 'max_atk_flb'
t.integer "max_atk_ulb" t.integer 'max_atk_ulb'
t.boolean "extra", default: false, null: false t.boolean 'extra', default: false, null: false
t.integer "ax_type" t.integer 'ax_type'
t.boolean "awakening", default: true, null: false t.boolean 'awakening', default: true, null: false
t.boolean "limit", default: false, null: false t.boolean 'limit', default: false, null: false
t.boolean "ax", default: false, null: false t.boolean 'ax', default: false, null: false
t.index ["name_en"], name: "index_weapons_on_name_en", opclass: :gin_trgm_ops, using: :gin t.index ['name_en'], name: 'index_weapons_on_name_en', opclass: :gin_trgm_ops, using: :gin
end end
add_foreign_key "favorites", "parties" add_foreign_key "favorites", "parties"
@ -319,6 +318,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_03_180458) do
add_foreign_key "parties", "job_skills", column: "skill2_id" add_foreign_key "parties", "job_skills", column: "skill2_id"
add_foreign_key "parties", "job_skills", column: "skill3_id" add_foreign_key "parties", "job_skills", column: "skill3_id"
add_foreign_key "parties", "jobs" add_foreign_key "parties", "jobs"
add_foreign_key "parties", "parties", column: "source_party_id"
add_foreign_key "parties", "raids" add_foreign_key "parties", "raids"
add_foreign_key "parties", "users" add_foreign_key "parties", "users"
end end

View file

@ -32,7 +32,11 @@ namespace :granblue do
Rake::Task['granblue:export:summon'].invoke('square') Rake::Task['granblue:export:summon'].invoke('square')
Rake::Task['granblue:export:summon'].reenable Rake::Task['granblue:export:summon'].reenable
puts 'Exported 9 files' # Run job tasks
Rake::Task['granblue:export:job'].invoke
Rake::Task['granblue:export:job'].reenable
puts 'Exported 10 files'
end end
end end
end end

33
lib/tasks/export_job.rake Normal file
View file

@ -0,0 +1,33 @@
namespace :granblue do
namespace :export do
def build_job_icon_url(id)
# Set up URL
base_url = 'https://prd-game-a-granbluefantasy.akamaized.net/assets_en/img/sp/ui/icon/job'
extension = '.png'
"#{base_url}/#{id}#{extension}"
end
desc 'Exports a list of weapon URLs for a given size'
task :job do |_t, _args|
# Include weapon model
Dir.glob("#{Rails.root}/app/models/job.rb").each { |file| require file }
# Set up filepath
dir = "#{Rails.root}/export/"
filename = "#{dir}/job-icon.txt"
FileUtils.mkdir(dir) unless Dir.exist?(dir)
# Write to file
File.open(filename, 'w') do |f|
Job.all.each do |w|
f.write("#{build_job_icon_url(w.granblue_id.to_s)} \n")
end
end
# CLI output
count = `wc -l #{filename}`.split.first.to_i
puts "Wrote #{count} job URLs for icon size"
end
end
end

View file

@ -36,6 +36,11 @@ namespace :granblue do
f.write("#{build_summon_url("#{s.granblue_id}_02", f.write("#{build_summon_url("#{s.granblue_id}_02",
size)} \n") size)} \n")
end end
if s.xlb
f.write("#{build_summon_url("#{s.granblue_id}_03",
size)} \n")
end
end end
end end