# Artifacts Feature Plan ## Overview Artifacts are character equipment items in Granblue Fantasy that provide stat bonuses through a skill system. This document outlines the implementation plan for tracking artifacts in user collections and parties, following the same pattern as the existing weapon/summon collection system. ## Business Requirements ### User Stories 1. **As a user**, I want to record artifacts I own in the game so I can track my collection 2. **As a user**, I want to save artifact skill configurations so I can reference them when team building 3. **As a user**, I want to track multiple copies of the same artifact with different skill configurations 4. **As a user**, I want to equip artifacts from my collection to characters in parties 5. **As a user**, I want to quick-build artifacts directly in parties without adding to my collection 6. **As a user**, I want to see which characters have artifacts equipped in my parties ### Core Concepts #### Collection vs Grid Artifacts - **Collection Artifacts**: Represent artifacts in the user's inventory, independent of any party - **Grid Artifacts**: Represent artifacts equipped to characters within a specific party #### Artifact Types - **Standard Artifacts**: Max level 150, 3 skill slots - **Quirk Artifacts**: Max level 200, 4 skill slots (character-specific) #### Skill System - **Group I Skills**: Attack, HP, critical rate, etc. (max 2 per artifact) - **Group II Skills**: Enmity, stamina, charge bar speed, etc. (max 1 per artifact) - **Group III Skills**: Damage cap increases (max 1 per artifact) Each skill has its own level that users can set when recording their artifacts. #### Item Uniqueness Rules - **Artifacts**: Multiple instances of the same artifact allowed per user, each with unique skill configurations - **Grid Artifacts**: One artifact per character in a party ## Technical Design ### Database Schema #### artifacts (Canonical Game Data) ```sql - id: uuid (primary key) - name_en: string (not null) - name_jp: string (not null) - series: integer (1=Ominous, 2=Saint, 3=Jinyao, etc.) - weapon_specialty: integer (1=sabre, 2=dagger, etc.) - rarity: integer (3=R, 4=SR, 5=SSR) - is_quirk: boolean (default false) - max_level: integer (default 5 for standard, 1 for quirk) - created_at: timestamp - updated_at: timestamp Indexes: - index on weapon_specialty - index on rarity - index on is_quirk ``` #### artifact_skills (Canonical Game Data) ```sql - id: uuid (primary key) - name_en: string (not null) - name_jp: string (not null) - skill_group: integer (1=Group I, 2=Group II, 3=Group III) - effect_type: string (atk, hp, ca_dmg, skill_dmg, etc.) - base_values: jsonb (array of possible starting values) - growth_value: decimal (amount gained per level) - max_level: integer (default 5) - description_en: text - description_jp: text - created_at: timestamp - updated_at: timestamp Indexes: - index on skill_group - index on effect_type ``` #### collection_artifacts (User Collection) ```sql - id: uuid (primary key) - user_id: uuid (foreign key to users, not null) - artifact_id: uuid (foreign key to artifacts, not null) - level: integer (1-200) - skill1_id: uuid (foreign key to artifact_skills) - skill1_level: integer (1-15) - skill2_id: uuid (foreign key to artifact_skills) - skill2_level: integer (1-15) - skill3_id: uuid (foreign key to artifact_skills) - skill3_level: integer (1-15) - skill4_id: uuid (foreign key to artifact_skills, optional for standard artifacts) - skill4_level: integer (1-15, optional) - created_at: timestamp - updated_at: timestamp Indexes: - index on user_id - index on artifact_id - index on [user_id, artifact_id] ``` #### grid_artifacts (Party Equipment) ```sql - id: uuid (primary key) - party_id: uuid (foreign key to parties, not null) - grid_character_id: uuid (foreign key to grid_characters, not null) - collection_artifact_id: uuid (foreign key to collection_artifacts, optional) # Quick-build fields (when not using collection) - artifact_id: uuid (foreign key to artifacts, optional) - level: integer (1-200) - skill1_id: uuid (foreign key to artifact_skills) - skill1_level: integer (1-15) - skill2_id: uuid (foreign key to artifact_skills) - skill2_level: integer (1-15) - skill3_id: uuid (foreign key to artifact_skills) - skill3_level: integer (1-15) - skill4_id: uuid (foreign key to artifact_skills, optional) - skill4_level: integer (1-15, optional) - created_at: timestamp - updated_at: timestamp Indexes: - unique index on grid_character_id (one artifact per character) - index on party_id - index on collection_artifact_id - index on artifact_id ``` ### Model Relationships ```ruby # User model additions has_many :collection_artifacts, dependent: :destroy # Artifact model (canonical game data) class Artifact < ApplicationRecord has_many :collection_artifacts, dependent: :restrict_with_error has_many :grid_artifacts, dependent: :restrict_with_error validates :name_en, :name_jp, presence: true validates :rarity, inclusion: { in: 3..5 } scope :standard, -> { where(is_quirk: false) } scope :quirk, -> { where(is_quirk: true) } def max_level is_quirk ? 200 : 150 end def max_skill_slots is_quirk ? 4 : 3 end end # ArtifactSkill model (canonical game data) class ArtifactSkill < ApplicationRecord validates :name_en, :name_jp, presence: true validates :skill_group, inclusion: { in: 1..3 } validates :max_level, presence: true scope :group_i, -> { where(skill_group: 1) } scope :group_ii, -> { where(skill_group: 2) } scope :group_iii, -> { where(skill_group: 3) } end # CollectionArtifact model class CollectionArtifact < ApplicationRecord belongs_to :user belongs_to :artifact belongs_to :skill1, class_name: 'ArtifactSkill', optional: true belongs_to :skill2, class_name: 'ArtifactSkill', optional: true belongs_to :skill3, class_name: 'ArtifactSkill', optional: true belongs_to :skill4, class_name: 'ArtifactSkill', optional: true has_one :grid_artifact, dependent: :nullify validates :level, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 200 } validates :skill1_level, :skill2_level, :skill3_level, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 15 }, allow_nil: true validates :skill4_level, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 15 }, allow_nil: true # Validate skill4 only exists for quirk artifacts validate :skill4_only_for_quirk def skills [skill1, skill2, skill3, skill4].compact end private def skill4_only_for_quirk if skill4_id.present? && artifact && !artifact.is_quirk errors.add(:skill4_id, "can only be set for quirk artifacts") end end end # GridArtifact model class GridArtifact < ApplicationRecord belongs_to :party belongs_to :grid_character belongs_to :collection_artifact, optional: true belongs_to :artifact, optional: true # For quick-build belongs_to :skill1, class_name: 'ArtifactSkill', optional: true belongs_to :skill2, class_name: 'ArtifactSkill', optional: true belongs_to :skill3, class_name: 'ArtifactSkill', optional: true belongs_to :skill4, class_name: 'ArtifactSkill', optional: true validates :grid_character_id, uniqueness: true validate :validate_artifact_source def from_collection? collection_artifact_id.present? end def artifact_details if from_collection? collection_artifact.artifact else artifact end end def skills if from_collection? collection_artifact.skills else [skill1, skill2, skill3, skill4].compact end end private def validate_artifact_source if collection_artifact_id.blank? && artifact_id.blank? errors.add(:base, "Must specify either collection artifact or quick-build artifact") end if collection_artifact_id.present? && artifact_id.present? && !from_collection? errors.add(:base, "Cannot specify both collection and quick-build artifact") end end end # GridCharacter model additions has_one :grid_artifact, dependent: :destroy # Party model additions has_many :grid_artifacts, dependent: :destroy ``` ### API Design #### Endpoints ##### Collection Artifacts ``` GET /api/v1/collection/artifacts Query params: page, limit, artifact_id (filter) Response: Paginated list of user's artifacts GET /api/v1/collection/artifacts/:id Response: Single collection artifact details POST /api/v1/collection/artifacts Body: artifact_id, level, skill1_id, skill1_level, skill2_id, skill2_level, etc. Response: Created collection artifact PUT /api/v1/collection/artifacts/:id Body: Updated fields Response: Updated collection artifact DELETE /api/v1/collection/artifacts/:id Response: Success/error status ``` ##### Grid Artifacts (Party Equipment) ``` GET /api/v1/parties/:party_id/grid_artifacts Response: List of artifacts equipped in party POST /api/v1/parties/:party_id/grid_artifacts Body: { grid_character_id, collection_artifact_id } OR { grid_character_id, artifact_id, level, skills... } (quick-build) Response: Created grid artifact PUT /api/v1/parties/:party_id/grid_artifacts/:id Body: Updated artifact reference or properties Response: Updated grid artifact DELETE /api/v1/parties/:party_id/grid_artifacts/:id Response: Success/error status ``` ##### Canonical Data Endpoints ``` GET /api/v1/artifacts Query: is_quirk?, page, limit Response: List of all artifacts GET /api/v1/artifacts/:id Response: Artifact details GET /api/v1/artifact_skills Query: skill_group?, page, limit Response: List of all artifact skills GET /api/v1/artifact_skills/:id Response: Skill details ``` ##### Collection Management ``` GET /api/v1/users/:user_id/collection/artifacts Response: View another user's artifact collection (respects privacy settings) GET /api/v1/collection/statistics Response: { total_artifacts: 50, breakdown_by_rarity: {standard: 45, quirk: 5}, breakdown_by_level: {...} } ``` ### Security Considerations 1. **Authorization**: Collection management endpoints require authentication 2. **Ownership**: Users can only modify their own collection 3. **Privacy Controls**: Respect user's collection_privacy settings when viewing collections 4. **Validation**: Strict validation of skill combinations and levels 5. **Rate Limiting**: Standard rate limiting on all collection endpoints ### Performance Considerations 1. **Eager Loading**: Include skills and artifact data in collection queries 2. **Batch Operations**: Support bulk artifact operations for imports 3. **Indexed Queries**: Proper indexes on frequently filtered columns 4. **Pagination**: Mandatory pagination for collection endpoints ## Implementation Phases ### Phase 1: Core Models and Database - Create migrations for artifacts, skills, and collections - Implement Artifact and ArtifactSkill models - Implement CollectionArtifact model - Seed canonical artifact and skill data ### Phase 2: API Controllers and Blueprints - Implement collection artifacts CRUD controller - Create artifact blueprints - Add authentication and authorization - Write controller specs ### Phase 3: Grid Integration - Implement GridArtifact model - Create grid artifacts controller - Add artifact support to party endpoints - Integrate with existing party system ### Phase 4: Frontend Integration - Update frontend models and types - Create artifact management UI - Add artifact selection in party builder - Implement collection views ## Success Metrics 1. **Performance**: All endpoints respond within 200ms for standard operations 2. **Reliability**: 99.9% uptime for artifact services 3. **User Adoption**: 50% of active users use artifact tracking within 3 months 4. **Data Integrity**: Zero data loss incidents