2026-01-04 23:06:40 +01:00
|
|
|
package gsmaster
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bamort/database"
|
2026-05-01 18:15:31 +02:00
|
|
|
"bamort/bmrt/models"
|
2026-01-04 23:06:40 +01:00
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
)
|
|
|
|
|
|
2026-01-05 09:29:31 +01:00
|
|
|
// ExportableCategoryDifficulty represents a category/difficulty combination for a skill
|
|
|
|
|
type ExportableCategoryDifficulty struct {
|
|
|
|
|
Category string `json:"category"`
|
|
|
|
|
Difficulty string `json:"difficulty"`
|
|
|
|
|
LearnCost int `json:"learn_cost"`
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-04 23:06:40 +01:00
|
|
|
// ExportableSkill represents a skill without database IDs for export
|
|
|
|
|
type ExportableSkill struct {
|
2026-01-05 09:29:31 +01:00
|
|
|
Name string `json:"name"`
|
|
|
|
|
GameSystem string `json:"game_system"`
|
2026-01-28 09:20:55 +01:00
|
|
|
GameSystemId uint `json:"game_system_id"`
|
2026-01-05 09:29:31 +01:00
|
|
|
Beschreibung string `json:"beschreibung"`
|
|
|
|
|
SourceCode string `json:"source_code"` // Instead of SourceID
|
|
|
|
|
PageNumber int `json:"page_number"`
|
|
|
|
|
Initialwert int `json:"initialwert"`
|
|
|
|
|
BasisWert int `json:"basiswert"`
|
|
|
|
|
Bonuseigenschaft string `json:"bonuseigenschaft"`
|
|
|
|
|
Improvable bool `json:"improvable"`
|
|
|
|
|
InnateSkill bool `json:"innate_skill"`
|
|
|
|
|
Category string `json:"category"` // Deprecated: use CategoriesDifficulties
|
|
|
|
|
Difficulty string `json:"difficulty"` // Deprecated: use CategoriesDifficulties
|
|
|
|
|
CategoriesDifficulties []ExportableCategoryDifficulty `json:"categories_difficulties"` // All category/difficulty combinations
|
2026-01-04 23:06:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableSource represents a source for export
|
|
|
|
|
type ExportableSource struct {
|
|
|
|
|
Code string `json:"code"`
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
FullName string `json:"full_name"`
|
|
|
|
|
Edition string `json:"edition"`
|
|
|
|
|
Publisher string `json:"publisher"`
|
|
|
|
|
PublishYear int `json:"publish_year"`
|
|
|
|
|
Description string `json:"description"`
|
|
|
|
|
IsCore bool `json:"is_core"`
|
|
|
|
|
IsActive bool `json:"is_active"`
|
|
|
|
|
GameSystem string `json:"game_system"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableSkillCategory represents a skill category for export
|
|
|
|
|
type ExportableSkillCategory struct {
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
GameSystem string `json:"game_system"`
|
|
|
|
|
SourceCode string `json:"source_code"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableSkillDifficulty represents a skill difficulty for export
|
|
|
|
|
type ExportableSkillDifficulty struct {
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
GameSystem string `json:"game_system"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableSkillCategoryDifficulty represents the relationship for export
|
|
|
|
|
type ExportableSkillCategoryDifficulty struct {
|
|
|
|
|
SkillName string `json:"skill_name"`
|
|
|
|
|
SkillSystem string `json:"skill_system"`
|
|
|
|
|
CategoryName string `json:"category_name"`
|
|
|
|
|
CategorySystem string `json:"category_system"`
|
|
|
|
|
DifficultyName string `json:"difficulty_name"`
|
|
|
|
|
DifficultySystem string `json:"difficulty_system"`
|
|
|
|
|
LearnCost int `json:"learn_cost"`
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-12 16:36:35 +01:00
|
|
|
// ExportableWeaponSkillCategoryDifficulty represents the weapon skill relationship for export
|
|
|
|
|
type ExportableWeaponSkillCategoryDifficulty struct {
|
|
|
|
|
WeaponSkillName string `json:"weapon_skill_name"`
|
|
|
|
|
SkillSystem string `json:"skill_system"`
|
|
|
|
|
CategoryName string `json:"category_name"`
|
|
|
|
|
CategorySystem string `json:"category_system"`
|
|
|
|
|
DifficultyName string `json:"difficulty_name"`
|
|
|
|
|
DifficultySystem string `json:"difficulty_system"`
|
|
|
|
|
LearnCost int `json:"learn_cost"`
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-04 23:06:40 +01:00
|
|
|
// ExportableSpell represents a spell for export
|
|
|
|
|
type ExportableSpell struct {
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
GameSystem string `json:"game_system"`
|
2026-01-28 08:51:35 +01:00
|
|
|
GameSystemId uint `json:"game_system_id"`
|
2026-01-04 23:06:40 +01:00
|
|
|
Beschreibung string `json:"beschreibung"`
|
|
|
|
|
SourceCode string `json:"source_code"`
|
|
|
|
|
PageNumber int `json:"page_number"`
|
|
|
|
|
Bonus int `json:"bonus"`
|
|
|
|
|
Stufe int `json:"level"`
|
|
|
|
|
AP string `json:"ap"`
|
|
|
|
|
Art string `json:"art"`
|
|
|
|
|
Zauberdauer string `json:"zauberdauer"`
|
|
|
|
|
Reichweite string `json:"reichweite"`
|
|
|
|
|
Wirkungsziel string `json:"wirkungsziel"`
|
|
|
|
|
Wirkungsbereich string `json:"wirkungsbereich"`
|
|
|
|
|
Wirkungsdauer string `json:"wirkungsdauer"`
|
|
|
|
|
Ursprung string `json:"ursprung"`
|
|
|
|
|
Category string `json:"category"`
|
|
|
|
|
LearningCategory string `json:"learning_category"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableCharacterClass represents a character class for export
|
|
|
|
|
type ExportableCharacterClass struct {
|
|
|
|
|
Code string `json:"code"`
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
Description string `json:"description"`
|
|
|
|
|
SourceCode string `json:"source_code"`
|
|
|
|
|
GameSystem string `json:"game_system"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableSpellSchool represents a spell school for export
|
|
|
|
|
type ExportableSpellSchool struct {
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
Description string `json:"description"`
|
|
|
|
|
SourceCode string `json:"source_code"`
|
|
|
|
|
GameSystem string `json:"game_system"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableClassCategoryEPCost represents class-category EP costs for export
|
|
|
|
|
type ExportableClassCategoryEPCost struct {
|
|
|
|
|
CharacterClassCode string `json:"character_class_code"`
|
|
|
|
|
SkillCategoryName string `json:"skill_category_name"`
|
|
|
|
|
EPPerTE int `json:"ep_per_te"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableClassSpellSchoolEPCost represents class-spell school EP costs for export
|
|
|
|
|
type ExportableClassSpellSchoolEPCost struct {
|
|
|
|
|
CharacterClassCode string `json:"character_class_code"`
|
|
|
|
|
SpellSchoolName string `json:"spell_school_name"`
|
|
|
|
|
EPPerLE int `json:"ep_per_le"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableSpellLevelLECost represents spell level LE costs for export
|
|
|
|
|
type ExportableSpellLevelLECost struct {
|
2026-01-28 22:40:44 +01:00
|
|
|
Level int `json:"level"`
|
|
|
|
|
LERequired int `json:"le_required"`
|
|
|
|
|
GameSystem string `json:"game_system"`
|
|
|
|
|
GameSystemId uint `json:"game_system_id"`
|
2026-01-04 23:06:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableSkillImprovementCost represents skill improvement costs for export
|
|
|
|
|
type ExportableSkillImprovementCost struct {
|
|
|
|
|
SkillName string `json:"skill_name"`
|
|
|
|
|
SkillSystem string `json:"skill_system"`
|
|
|
|
|
CategoryName string `json:"category_name"`
|
|
|
|
|
CategorySystem string `json:"category_system"`
|
|
|
|
|
DifficultyName string `json:"difficulty_name"`
|
|
|
|
|
DifficultySystem string `json:"difficulty_system"`
|
|
|
|
|
CurrentLevel int `json:"current_level"`
|
|
|
|
|
TERequired int `json:"te_required"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableWeaponSkill represents a weapon skill for export
|
|
|
|
|
type ExportableWeaponSkill struct {
|
2026-01-12 16:36:35 +01:00
|
|
|
Name string `json:"name"`
|
|
|
|
|
GameSystem string `json:"game_system"`
|
2026-01-28 09:20:55 +01:00
|
|
|
GameSystemId uint `json:"game_system_id"`
|
2026-01-12 16:36:35 +01:00
|
|
|
Beschreibung string `json:"beschreibung"`
|
|
|
|
|
SourceCode string `json:"source_code"`
|
|
|
|
|
PageNumber int `json:"page_number"`
|
|
|
|
|
Initialwert int `json:"initialwert"`
|
|
|
|
|
BasisWert int `json:"basiswert"`
|
|
|
|
|
Bonuseigenschaft string `json:"bonuseigenschaft"`
|
|
|
|
|
Improvable bool `json:"improvable"`
|
|
|
|
|
InnateSkill bool `json:"innate_skill"`
|
|
|
|
|
Category string `json:"category"` // Deprecated: use CategoriesDifficulties
|
|
|
|
|
Difficulty string `json:"difficulty"` // Deprecated: use CategoriesDifficulties
|
|
|
|
|
CategoriesDifficulties []ExportableCategoryDifficulty `json:"categories_difficulties"` // All category/difficulty combinations
|
2026-01-04 23:06:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableEquipment represents equipment for export
|
|
|
|
|
type ExportableEquipment struct {
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
GameSystem string `json:"game_system"`
|
|
|
|
|
Beschreibung string `json:"beschreibung"`
|
|
|
|
|
SourceCode string `json:"source_code"`
|
|
|
|
|
PageNumber int `json:"page_number"`
|
|
|
|
|
Gewicht float64 `json:"gewicht"`
|
|
|
|
|
Wert float64 `json:"wert"`
|
|
|
|
|
PersonalItem bool `json:"personal_item"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableWeapon represents a weapon for export
|
|
|
|
|
type ExportableWeapon struct {
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
GameSystem string `json:"game_system"`
|
|
|
|
|
Beschreibung string `json:"beschreibung"`
|
|
|
|
|
SourceCode string `json:"source_code"`
|
|
|
|
|
PageNumber int `json:"page_number"`
|
|
|
|
|
Gewicht float64 `json:"gewicht"`
|
|
|
|
|
Wert float64 `json:"wert"`
|
|
|
|
|
PersonalItem bool `json:"personal_item"`
|
|
|
|
|
SkillRequired string `json:"skill_required"`
|
|
|
|
|
Damage string `json:"damage"`
|
|
|
|
|
RangeNear int `json:"range_near"`
|
|
|
|
|
RangeMiddle int `json:"range_middle"`
|
|
|
|
|
RangeFar int `json:"range_far"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableContainer represents a container for export
|
|
|
|
|
type ExportableContainer struct {
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
GameSystem string `json:"game_system"`
|
|
|
|
|
Beschreibung string `json:"beschreibung"`
|
|
|
|
|
SourceCode string `json:"source_code"`
|
|
|
|
|
PageNumber int `json:"page_number"`
|
|
|
|
|
Gewicht float64 `json:"gewicht"`
|
|
|
|
|
Wert float64 `json:"wert"`
|
|
|
|
|
PersonalItem bool `json:"personal_item"`
|
|
|
|
|
Tragkraft float64 `json:"tragkraft"`
|
|
|
|
|
Volumen float64 `json:"volumen"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableTransportation represents transportation for export
|
|
|
|
|
type ExportableTransportation struct {
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
GameSystem string `json:"game_system"`
|
|
|
|
|
Beschreibung string `json:"beschreibung"`
|
|
|
|
|
SourceCode string `json:"source_code"`
|
|
|
|
|
PageNumber int `json:"page_number"`
|
|
|
|
|
Gewicht float64 `json:"gewicht"`
|
|
|
|
|
Wert float64 `json:"wert"`
|
|
|
|
|
PersonalItem bool `json:"personal_item"`
|
|
|
|
|
Tragkraft float64 `json:"tragkraft"`
|
|
|
|
|
Volumen float64 `json:"volumen"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableBelieve represents a belief system for export
|
|
|
|
|
type ExportableBelieve struct {
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
GameSystem string `json:"game_system"`
|
|
|
|
|
Beschreibung string `json:"beschreibung"`
|
|
|
|
|
SourceCode string `json:"source_code"`
|
|
|
|
|
PageNumber int `json:"page_number"`
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-12 16:36:35 +01:00
|
|
|
// ExportableClassCategoryLearningPoints represents learning points allocation for export
|
|
|
|
|
type ExportableClassCategoryLearningPoints struct {
|
|
|
|
|
ClassName string `json:"class_name"`
|
|
|
|
|
ClassSystem string `json:"class_system"`
|
|
|
|
|
CategoryName string `json:"category_name"`
|
|
|
|
|
CategorySystem string `json:"category_system"`
|
|
|
|
|
Points int `json:"points"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableClassSpellPoints represents spell points for export
|
|
|
|
|
type ExportableClassSpellPoints struct {
|
|
|
|
|
ClassName string `json:"class_name"`
|
|
|
|
|
ClassSystem string `json:"class_system"`
|
|
|
|
|
SpellPoints int `json:"spell_points"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableClassTypicalSkill represents typical skills for export
|
|
|
|
|
type ExportableClassTypicalSkill struct {
|
|
|
|
|
ClassName string `json:"class_name"`
|
|
|
|
|
ClassSystem string `json:"class_system"`
|
|
|
|
|
SkillName string `json:"skill_name"`
|
|
|
|
|
SkillSystem string `json:"skill_system"`
|
|
|
|
|
Bonus int `json:"bonus"`
|
|
|
|
|
Attribute string `json:"attribute,omitempty"`
|
|
|
|
|
Notes string `json:"notes,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportableClassTypicalSpell represents typical spells for export
|
|
|
|
|
type ExportableClassTypicalSpell struct {
|
|
|
|
|
ClassName string `json:"class_name"`
|
|
|
|
|
ClassSystem string `json:"class_system"`
|
|
|
|
|
SpellName string `json:"spell_name"`
|
|
|
|
|
SpellSystem string `json:"spell_system"`
|
|
|
|
|
Notes string `json:"notes,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 22:14:23 +01:00
|
|
|
// ExportableMiscLookup represents miscellaneous lookup values for export
|
|
|
|
|
type ExportableMiscLookup struct {
|
2026-01-28 07:44:17 +01:00
|
|
|
Key string `json:"key"`
|
|
|
|
|
Value string `json:"value"`
|
|
|
|
|
SourceCode string `json:"source_code"`
|
|
|
|
|
PageNumber int `json:"page_number"`
|
|
|
|
|
GameSystemId uint `json:"game_system_id"`
|
2026-01-14 22:14:23 +01:00
|
|
|
}
|
|
|
|
|
|
2026-01-04 23:06:40 +01:00
|
|
|
// ExportSkills exports all skills to a JSON file
|
|
|
|
|
func ExportSkills(outputDir string) error {
|
|
|
|
|
var skills []models.Skill
|
|
|
|
|
if err := database.DB.Find(&skills).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch skills: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
sourceMap := buildSourceMap()
|
2026-01-04 23:06:40 +01:00
|
|
|
|
2026-01-05 09:29:31 +01:00
|
|
|
// Get all skill category difficulties
|
|
|
|
|
var scds []models.SkillCategoryDifficulty
|
|
|
|
|
database.DB.Preload("SkillCategory").Preload("SkillDifficulty").Find(&scds)
|
|
|
|
|
|
|
|
|
|
// Build map of skill_id -> []category/difficulty combinations
|
|
|
|
|
scdMap := make(map[uint][]ExportableCategoryDifficulty)
|
|
|
|
|
for _, scd := range scds {
|
|
|
|
|
scdMap[scd.SkillID] = append(scdMap[scd.SkillID], ExportableCategoryDifficulty{
|
|
|
|
|
Category: scd.SkillCategory.Name,
|
|
|
|
|
Difficulty: scd.SkillDifficulty.Name,
|
|
|
|
|
LearnCost: scd.LearnCost,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-04 23:06:40 +01:00
|
|
|
// Convert to exportable format
|
|
|
|
|
exportable := make([]ExportableSkill, len(skills))
|
|
|
|
|
for i, skill := range skills {
|
|
|
|
|
exportable[i] = ExportableSkill{
|
2026-01-05 09:29:31 +01:00
|
|
|
Name: skill.Name,
|
|
|
|
|
GameSystem: skill.GameSystem,
|
2026-01-28 09:20:55 +01:00
|
|
|
GameSystemId: skill.GameSystemId,
|
2026-01-05 09:29:31 +01:00
|
|
|
Beschreibung: skill.Beschreibung,
|
|
|
|
|
SourceCode: sourceMap[skill.SourceID],
|
|
|
|
|
PageNumber: skill.PageNumber,
|
|
|
|
|
Initialwert: skill.Initialwert,
|
|
|
|
|
BasisWert: skill.BasisWert,
|
|
|
|
|
Bonuseigenschaft: skill.Bonuseigenschaft,
|
|
|
|
|
Improvable: skill.Improvable,
|
|
|
|
|
InnateSkill: skill.InnateSkill,
|
|
|
|
|
Category: skill.Category,
|
|
|
|
|
Difficulty: skill.Difficulty,
|
|
|
|
|
CategoriesDifficulties: scdMap[skill.ID],
|
2026-01-04 23:06:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "skills.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportSkills imports skills from a JSON file
|
|
|
|
|
func ImportSkills(inputDir string) error {
|
|
|
|
|
var exportable []ExportableSkill
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "skills.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-28 09:20:55 +01:00
|
|
|
if err := database.DB.AutoMigrate(&models.Skill{}); err != nil {
|
|
|
|
|
return fmt.Errorf("failed to migrate skills table: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
sourceMap := buildSourceMapReverse()
|
|
|
|
|
categoryMap := buildCategoryMap()
|
|
|
|
|
difficultyMap := buildDifficultyMap()
|
2026-01-05 09:29:31 +01:00
|
|
|
|
2026-01-04 23:06:40 +01:00
|
|
|
for _, exp := range exportable {
|
|
|
|
|
var skill models.Skill
|
2026-01-28 09:20:55 +01:00
|
|
|
gs := models.GetGameSystem(exp.GameSystemId, exp.GameSystem)
|
|
|
|
|
effectiveName := exp.GameSystem
|
|
|
|
|
if effectiveName == "" {
|
|
|
|
|
effectiveName = gs.Name
|
|
|
|
|
}
|
|
|
|
|
effectiveID := gs.ID
|
|
|
|
|
if effectiveID == 0 {
|
|
|
|
|
effectiveID = models.GetGameSystem(0, "").ID
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result := database.DB.Where("name = ? AND (game_system = ? OR game_system_id = ?)", exp.Name, effectiveName, effectiveID).First(&skill)
|
2026-01-04 23:06:40 +01:00
|
|
|
|
|
|
|
|
sourceID := sourceMap[exp.SourceCode]
|
|
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
// Create new skill
|
|
|
|
|
skill = models.Skill{
|
|
|
|
|
Name: exp.Name,
|
2026-01-28 09:20:55 +01:00
|
|
|
GameSystem: effectiveName,
|
|
|
|
|
GameSystemId: effectiveID,
|
2026-01-04 23:06:40 +01:00
|
|
|
Beschreibung: exp.Beschreibung,
|
|
|
|
|
SourceID: sourceID,
|
|
|
|
|
PageNumber: exp.PageNumber,
|
|
|
|
|
Initialwert: exp.Initialwert,
|
|
|
|
|
BasisWert: exp.BasisWert,
|
|
|
|
|
Bonuseigenschaft: exp.Bonuseigenschaft,
|
|
|
|
|
Improvable: exp.Improvable,
|
|
|
|
|
InnateSkill: exp.InnateSkill,
|
|
|
|
|
Category: exp.Category,
|
|
|
|
|
Difficulty: exp.Difficulty,
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&skill).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create skill %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query skill %s: %w", exp.Name, result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
// Update existing skill
|
|
|
|
|
skill.Beschreibung = exp.Beschreibung
|
|
|
|
|
skill.SourceID = sourceID
|
|
|
|
|
skill.PageNumber = exp.PageNumber
|
|
|
|
|
skill.Initialwert = exp.Initialwert
|
|
|
|
|
skill.BasisWert = exp.BasisWert
|
|
|
|
|
skill.Bonuseigenschaft = exp.Bonuseigenschaft
|
|
|
|
|
skill.Improvable = exp.Improvable
|
|
|
|
|
skill.InnateSkill = exp.InnateSkill
|
|
|
|
|
skill.Category = exp.Category
|
|
|
|
|
skill.Difficulty = exp.Difficulty
|
2026-01-28 09:20:55 +01:00
|
|
|
skill.GameSystemId = effectiveID
|
|
|
|
|
skill.GameSystem = effectiveName
|
2026-01-04 23:06:40 +01:00
|
|
|
|
|
|
|
|
if err := database.DB.Save(&skill).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update skill %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-05 09:29:31 +01:00
|
|
|
|
|
|
|
|
// Import category/difficulty combinations if present
|
|
|
|
|
if len(exp.CategoriesDifficulties) > 0 {
|
|
|
|
|
// Delete existing relationships for this skill
|
|
|
|
|
database.DB.Where("skill_id = ?", skill.ID).Delete(&models.SkillCategoryDifficulty{})
|
|
|
|
|
|
|
|
|
|
// Create new relationships
|
|
|
|
|
for _, cd := range exp.CategoriesDifficulties {
|
2026-01-28 09:20:55 +01:00
|
|
|
categoryID := categoryMap[effectiveName][cd.Category]
|
|
|
|
|
difficultyID := difficultyMap[effectiveName][cd.Difficulty]
|
2026-01-05 09:29:31 +01:00
|
|
|
|
|
|
|
|
if categoryID == 0 || difficultyID == 0 {
|
|
|
|
|
continue // Skip if category or difficulty not found
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scd := models.SkillCategoryDifficulty{
|
|
|
|
|
SkillID: skill.ID,
|
|
|
|
|
SkillCategoryID: categoryID,
|
|
|
|
|
SkillDifficultyID: difficultyID,
|
|
|
|
|
LearnCost: cd.LearnCost,
|
|
|
|
|
SCategory: cd.Category,
|
|
|
|
|
SDifficulty: cd.Difficulty,
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&scd).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create skill category difficulty for %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-04 23:06:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportSources exports all sources to a JSON file
|
|
|
|
|
func ExportSources(outputDir string) error {
|
|
|
|
|
var sources []models.Source
|
|
|
|
|
if err := database.DB.Find(&sources).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch sources: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exportable := make([]ExportableSource, len(sources))
|
|
|
|
|
for i, s := range sources {
|
|
|
|
|
exportable[i] = ExportableSource{
|
|
|
|
|
Code: s.Code,
|
|
|
|
|
Name: s.Name,
|
|
|
|
|
FullName: s.FullName,
|
|
|
|
|
Edition: s.Edition,
|
|
|
|
|
Publisher: s.Publisher,
|
|
|
|
|
PublishYear: s.PublishYear,
|
|
|
|
|
Description: s.Description,
|
|
|
|
|
IsCore: s.IsCore,
|
|
|
|
|
IsActive: s.IsActive,
|
|
|
|
|
GameSystem: s.GameSystem,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "sources.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportSources imports sources from a JSON file
|
|
|
|
|
func ImportSources(inputDir string) error {
|
|
|
|
|
var exportable []ExportableSource
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "sources.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
2026-01-05 09:56:12 +01:00
|
|
|
source := models.Source{
|
|
|
|
|
Code: exp.Code,
|
|
|
|
|
Name: exp.Name,
|
|
|
|
|
FullName: exp.FullName,
|
|
|
|
|
Edition: exp.Edition,
|
|
|
|
|
Publisher: exp.Publisher,
|
|
|
|
|
PublishYear: exp.PublishYear,
|
|
|
|
|
Description: exp.Description,
|
|
|
|
|
IsCore: exp.IsCore,
|
|
|
|
|
IsActive: exp.IsActive,
|
|
|
|
|
GameSystem: exp.GameSystem,
|
|
|
|
|
}
|
2026-01-04 23:06:40 +01:00
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
if err := findOrCreateByCode(exp.Code, &source, "source"); err != nil {
|
|
|
|
|
return err
|
2026-01-04 23:06:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportSkillCategoryDifficulties exports skill-category-difficulty relationships
|
|
|
|
|
func ExportSkillCategoryDifficulties(outputDir string) error {
|
|
|
|
|
var scds []models.SkillCategoryDifficulty
|
|
|
|
|
if err := database.DB.Find(&scds).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch skill category difficulties: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exportable := make([]ExportableSkillCategoryDifficulty, len(scds))
|
|
|
|
|
for i, scd := range scds {
|
|
|
|
|
var skill models.Skill
|
|
|
|
|
var category models.SkillCategory
|
|
|
|
|
var difficulty models.SkillDifficulty
|
|
|
|
|
|
|
|
|
|
database.DB.First(&skill, scd.SkillID)
|
|
|
|
|
database.DB.First(&category, scd.SkillCategoryID)
|
|
|
|
|
database.DB.First(&difficulty, scd.SkillDifficultyID)
|
|
|
|
|
|
|
|
|
|
exportable[i] = ExportableSkillCategoryDifficulty{
|
|
|
|
|
SkillName: skill.Name,
|
|
|
|
|
SkillSystem: skill.GameSystem,
|
|
|
|
|
CategoryName: category.Name,
|
|
|
|
|
CategorySystem: category.GameSystem,
|
|
|
|
|
DifficultyName: difficulty.Name,
|
|
|
|
|
DifficultySystem: difficulty.GameSystem,
|
|
|
|
|
LearnCost: scd.LearnCost,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "skill_category_difficulties.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportSkillCategoryDifficulties imports skill-category-difficulty relationships
|
|
|
|
|
func ImportSkillCategoryDifficulties(inputDir string) error {
|
|
|
|
|
var exportable []ExportableSkillCategoryDifficulty
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "skill_category_difficulties.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
|
|
|
|
// Find the skill
|
|
|
|
|
var skill models.Skill
|
|
|
|
|
if err := database.DB.Where("name = ? AND game_system = ?", exp.SkillName, exp.SkillSystem).First(&skill).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("skill not found: %s/%s", exp.SkillName, exp.SkillSystem)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the category
|
|
|
|
|
var category models.SkillCategory
|
|
|
|
|
if err := database.DB.Where("name = ? AND game_system = ?", exp.CategoryName, exp.CategorySystem).First(&category).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("category not found: %s/%s", exp.CategoryName, exp.CategorySystem)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the difficulty
|
|
|
|
|
var difficulty models.SkillDifficulty
|
|
|
|
|
if err := database.DB.Where("name = ? AND game_system = ?", exp.DifficultyName, exp.DifficultySystem).First(&difficulty).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("difficulty not found: %s/%s", exp.DifficultyName, exp.DifficultySystem)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if relationship exists
|
|
|
|
|
var scd models.SkillCategoryDifficulty
|
|
|
|
|
result := database.DB.Where("skill_id = ? AND skill_category_id = ? AND skill_difficulty_id = ?",
|
|
|
|
|
skill.ID, category.ID, difficulty.ID).First(&scd)
|
|
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
// Create new relationship
|
|
|
|
|
scd = models.SkillCategoryDifficulty{
|
|
|
|
|
SkillID: skill.ID,
|
|
|
|
|
SkillCategoryID: category.ID,
|
|
|
|
|
SkillDifficultyID: difficulty.ID,
|
|
|
|
|
LearnCost: exp.LearnCost,
|
|
|
|
|
SCategory: category.Name,
|
|
|
|
|
SDifficulty: difficulty.Name,
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&scd).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create relationship: %w", err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query relationship: %w", result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
// Update existing relationship
|
|
|
|
|
scd.LearnCost = exp.LearnCost
|
|
|
|
|
scd.SCategory = category.Name
|
|
|
|
|
scd.SDifficulty = difficulty.Name
|
|
|
|
|
|
|
|
|
|
if err := database.DB.Save(&scd).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update relationship: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-12 16:36:35 +01:00
|
|
|
// ExportWeaponSkillCategoryDifficulties exports weapon skill-category-difficulty relationships
|
|
|
|
|
func ExportWeaponSkillCategoryDifficulties(outputDir string) error {
|
|
|
|
|
var wscds []models.WeaponSkillCategoryDifficulty
|
|
|
|
|
if err := database.DB.Find(&wscds).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch weapon skill category difficulties: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exportable := make([]ExportableWeaponSkillCategoryDifficulty, len(wscds))
|
|
|
|
|
for i, wscd := range wscds {
|
|
|
|
|
var weaponSkill models.WeaponSkill
|
|
|
|
|
var category models.SkillCategory
|
|
|
|
|
var difficulty models.SkillDifficulty
|
|
|
|
|
|
|
|
|
|
database.DB.First(&weaponSkill, wscd.WeaponSkillID)
|
|
|
|
|
database.DB.First(&category, wscd.SkillCategoryID)
|
|
|
|
|
database.DB.First(&difficulty, wscd.SkillDifficultyID)
|
|
|
|
|
|
|
|
|
|
exportable[i] = ExportableWeaponSkillCategoryDifficulty{
|
|
|
|
|
WeaponSkillName: weaponSkill.Name,
|
|
|
|
|
SkillSystem: weaponSkill.GameSystem,
|
|
|
|
|
CategoryName: category.Name,
|
|
|
|
|
CategorySystem: category.GameSystem,
|
|
|
|
|
DifficultyName: difficulty.Name,
|
|
|
|
|
DifficultySystem: difficulty.GameSystem,
|
|
|
|
|
LearnCost: wscd.LearnCost,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "weaponskill_category_difficulties.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportWeaponSkillCategoryDifficulties imports weapon skill-category-difficulty relationships
|
|
|
|
|
func ImportWeaponSkillCategoryDifficulties(inputDir string) error {
|
|
|
|
|
var exportable []ExportableWeaponSkillCategoryDifficulty
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "weaponskill_category_difficulties.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
|
|
|
|
// Find the weapon skill
|
|
|
|
|
var weaponSkill models.WeaponSkill
|
|
|
|
|
if err := database.DB.Where("name = ? AND game_system = ?", exp.WeaponSkillName, exp.SkillSystem).First(&weaponSkill).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("weapon skill not found: %s/%s", exp.WeaponSkillName, exp.SkillSystem)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the category
|
|
|
|
|
var category models.SkillCategory
|
|
|
|
|
if err := database.DB.Where("name = ? AND game_system = ?", exp.CategoryName, exp.CategorySystem).First(&category).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("category not found: %s/%s", exp.CategoryName, exp.CategorySystem)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the difficulty
|
|
|
|
|
var difficulty models.SkillDifficulty
|
|
|
|
|
if err := database.DB.Where("name = ? AND game_system = ?", exp.DifficultyName, exp.DifficultySystem).First(&difficulty).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("difficulty not found: %s/%s", exp.DifficultyName, exp.DifficultySystem)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if relationship exists
|
|
|
|
|
var wscd models.WeaponSkillCategoryDifficulty
|
|
|
|
|
result := database.DB.Where("weapon_skill_id = ? AND skill_category_id = ? AND skill_difficulty_id = ?",
|
|
|
|
|
weaponSkill.ID, category.ID, difficulty.ID).First(&wscd)
|
|
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
// Create new relationship
|
|
|
|
|
wscd = models.WeaponSkillCategoryDifficulty{
|
|
|
|
|
WeaponSkillID: weaponSkill.ID,
|
|
|
|
|
SkillCategoryID: category.ID,
|
|
|
|
|
SkillDifficultyID: difficulty.ID,
|
|
|
|
|
LearnCost: exp.LearnCost,
|
|
|
|
|
SCategory: category.Name,
|
|
|
|
|
SDifficulty: difficulty.Name,
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&wscd).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create relationship: %w", err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query relationship: %w", result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
// Update existing relationship
|
|
|
|
|
wscd.LearnCost = exp.LearnCost
|
|
|
|
|
wscd.SCategory = category.Name
|
|
|
|
|
wscd.SDifficulty = difficulty.Name
|
|
|
|
|
|
|
|
|
|
if err := database.DB.Save(&wscd).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update relationship: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-04 23:06:40 +01:00
|
|
|
// Helper functions for JSON I/O
|
|
|
|
|
func writeJSON(filename string, data interface{}) error {
|
|
|
|
|
file, err := os.Create(filename)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create file %s: %w", filename, err)
|
|
|
|
|
}
|
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
|
|
encoder := json.NewEncoder(file)
|
|
|
|
|
encoder.SetIndent("", " ")
|
|
|
|
|
if err := encoder.Encode(data); err != nil {
|
|
|
|
|
return fmt.Errorf("failed to encode JSON to %s: %w", filename, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func readJSON(filename string, data interface{}) error {
|
|
|
|
|
file, err := os.Open(filename)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to open file %s: %w", filename, err)
|
|
|
|
|
}
|
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
|
|
decoder := json.NewDecoder(file)
|
|
|
|
|
if err := decoder.Decode(data); err != nil {
|
|
|
|
|
return fmt.Errorf("failed to decode JSON from %s: %w", filename, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportSkillCategories exports all skill categories to a JSON file
|
|
|
|
|
func ExportSkillCategories(outputDir string) error {
|
|
|
|
|
var categories []models.SkillCategory
|
|
|
|
|
if err := database.DB.Find(&categories).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch skill categories: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
sourceMap := buildSourceMap()
|
2026-01-04 23:06:40 +01:00
|
|
|
|
|
|
|
|
exportable := make([]ExportableSkillCategory, len(categories))
|
|
|
|
|
for i, cat := range categories {
|
|
|
|
|
exportable[i] = ExportableSkillCategory{
|
|
|
|
|
Name: cat.Name,
|
|
|
|
|
GameSystem: cat.GameSystem,
|
|
|
|
|
SourceCode: sourceMap[cat.SourceID],
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "skill_categories.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportSkillCategories imports skill categories from a JSON file
|
|
|
|
|
func ImportSkillCategories(inputDir string) error {
|
|
|
|
|
var exportable []ExportableSkillCategory
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "skill_categories.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
sourceMap := buildSourceMapReverse()
|
2026-01-04 23:06:40 +01:00
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
2026-01-05 09:56:12 +01:00
|
|
|
category := models.SkillCategory{
|
|
|
|
|
Name: exp.Name,
|
|
|
|
|
GameSystem: exp.GameSystem,
|
|
|
|
|
SourceID: sourceMap[exp.SourceCode],
|
|
|
|
|
}
|
2026-01-04 23:06:40 +01:00
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
if err := findOrCreateByNameAndSystem(exp.Name, exp.GameSystem, &category, "skill category"); err != nil {
|
|
|
|
|
return err
|
2026-01-04 23:06:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportSkillDifficulties exports all skill difficulties to a JSON file
|
|
|
|
|
func ExportSkillDifficulties(outputDir string) error {
|
|
|
|
|
var difficulties []models.SkillDifficulty
|
|
|
|
|
if err := database.DB.Find(&difficulties).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch skill difficulties: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exportable := make([]ExportableSkillDifficulty, len(difficulties))
|
|
|
|
|
for i, diff := range difficulties {
|
|
|
|
|
exportable[i] = ExportableSkillDifficulty{
|
|
|
|
|
Name: diff.Name,
|
|
|
|
|
GameSystem: diff.GameSystem,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "skill_difficulties.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportSkillDifficulties imports skill difficulties from a JSON file
|
|
|
|
|
func ImportSkillDifficulties(inputDir string) error {
|
|
|
|
|
var exportable []ExportableSkillDifficulty
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "skill_difficulties.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
2026-01-05 09:56:12 +01:00
|
|
|
difficulty := models.SkillDifficulty{
|
|
|
|
|
Name: exp.Name,
|
|
|
|
|
GameSystem: exp.GameSystem,
|
|
|
|
|
}
|
2026-01-04 23:06:40 +01:00
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
if err := findOrCreateByNameAndSystem(exp.Name, exp.GameSystem, &difficulty, "skill difficulty"); err != nil {
|
|
|
|
|
return err
|
2026-01-04 23:06:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportSpells exports all spells to a JSON file
|
|
|
|
|
func ExportSpells(outputDir string) error {
|
|
|
|
|
var spells []models.Spell
|
|
|
|
|
if err := database.DB.Find(&spells).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch spells: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
sourceMap := buildSourceMap()
|
2026-01-04 23:06:40 +01:00
|
|
|
|
|
|
|
|
exportable := make([]ExportableSpell, len(spells))
|
|
|
|
|
for i, spell := range spells {
|
|
|
|
|
exportable[i] = ExportableSpell{
|
|
|
|
|
Name: spell.Name,
|
|
|
|
|
GameSystem: spell.GameSystem,
|
2026-01-28 08:51:35 +01:00
|
|
|
GameSystemId: spell.GameSystemId,
|
2026-01-04 23:06:40 +01:00
|
|
|
Beschreibung: spell.Beschreibung,
|
|
|
|
|
SourceCode: sourceMap[spell.SourceID],
|
|
|
|
|
PageNumber: spell.PageNumber,
|
|
|
|
|
Bonus: spell.Bonus,
|
|
|
|
|
Stufe: spell.Stufe,
|
|
|
|
|
AP: spell.AP,
|
|
|
|
|
Art: spell.Art,
|
|
|
|
|
Zauberdauer: spell.Zauberdauer,
|
|
|
|
|
Reichweite: spell.Reichweite,
|
|
|
|
|
Wirkungsziel: spell.Wirkungsziel,
|
|
|
|
|
Wirkungsbereich: spell.Wirkungsbereich,
|
|
|
|
|
Wirkungsdauer: spell.Wirkungsdauer,
|
|
|
|
|
Ursprung: spell.Ursprung,
|
|
|
|
|
Category: spell.Category,
|
|
|
|
|
LearningCategory: spell.LearningCategory,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "spells.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportSpells imports spells from a JSON file
|
|
|
|
|
func ImportSpells(inputDir string) error {
|
|
|
|
|
var exportable []ExportableSpell
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "spells.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
sourceMap := buildSourceMapReverse()
|
2026-01-04 23:06:40 +01:00
|
|
|
|
2026-01-28 08:51:35 +01:00
|
|
|
if err := database.DB.AutoMigrate(&models.Spell{}); err != nil {
|
|
|
|
|
return fmt.Errorf("failed to migrate spells table: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gs := models.GetGameSystem(exportable[0].GameSystemId, exportable[0].GameSystem)
|
2026-01-04 23:06:40 +01:00
|
|
|
for _, exp := range exportable {
|
|
|
|
|
var spell models.Spell
|
2026-01-28 08:51:35 +01:00
|
|
|
|
|
|
|
|
result := database.DB.Where("name = ? AND (game_system = ? OR game_system_id = ?)", exp.Name, gs.Name, gs.ID).First(&spell)
|
2026-01-04 23:06:40 +01:00
|
|
|
|
|
|
|
|
sourceID := sourceMap[exp.SourceCode]
|
|
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
spell = models.Spell{
|
|
|
|
|
Name: exp.Name,
|
2026-01-28 08:51:35 +01:00
|
|
|
GameSystem: gs.Name,
|
|
|
|
|
GameSystemId: gs.ID,
|
2026-01-04 23:06:40 +01:00
|
|
|
Beschreibung: exp.Beschreibung,
|
|
|
|
|
SourceID: sourceID,
|
|
|
|
|
PageNumber: exp.PageNumber,
|
|
|
|
|
Bonus: exp.Bonus,
|
|
|
|
|
Stufe: exp.Stufe,
|
|
|
|
|
AP: exp.AP,
|
|
|
|
|
Art: exp.Art,
|
|
|
|
|
Zauberdauer: exp.Zauberdauer,
|
|
|
|
|
Reichweite: exp.Reichweite,
|
|
|
|
|
Wirkungsziel: exp.Wirkungsziel,
|
|
|
|
|
Wirkungsbereich: exp.Wirkungsbereich,
|
|
|
|
|
Wirkungsdauer: exp.Wirkungsdauer,
|
|
|
|
|
Ursprung: exp.Ursprung,
|
|
|
|
|
Category: exp.Category,
|
|
|
|
|
LearningCategory: exp.LearningCategory,
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&spell).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create spell %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query spell %s: %w", exp.Name, result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
// Update existing spell
|
|
|
|
|
spell.Beschreibung = exp.Beschreibung
|
|
|
|
|
spell.SourceID = sourceID
|
|
|
|
|
spell.PageNumber = exp.PageNumber
|
|
|
|
|
spell.Bonus = exp.Bonus
|
|
|
|
|
spell.Stufe = exp.Stufe
|
|
|
|
|
spell.AP = exp.AP
|
|
|
|
|
spell.Art = exp.Art
|
|
|
|
|
spell.Zauberdauer = exp.Zauberdauer
|
|
|
|
|
spell.Reichweite = exp.Reichweite
|
|
|
|
|
spell.Wirkungsziel = exp.Wirkungsziel
|
|
|
|
|
spell.Wirkungsbereich = exp.Wirkungsbereich
|
|
|
|
|
spell.Wirkungsdauer = exp.Wirkungsdauer
|
|
|
|
|
spell.Ursprung = exp.Ursprung
|
|
|
|
|
spell.Category = exp.Category
|
|
|
|
|
spell.LearningCategory = exp.LearningCategory
|
2026-01-28 08:51:35 +01:00
|
|
|
spell.GameSystemId = gs.ID
|
|
|
|
|
spell.GameSystem = gs.Name
|
2026-01-04 23:06:40 +01:00
|
|
|
|
|
|
|
|
if err := database.DB.Save(&spell).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update spell %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportCharacterClasses exports all character classes to a JSON file
|
|
|
|
|
func ExportCharacterClasses(outputDir string) error {
|
|
|
|
|
var classes []models.CharacterClass
|
|
|
|
|
if err := database.DB.Find(&classes).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch character classes: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
sourceMap := buildSourceMap()
|
2026-01-04 23:06:40 +01:00
|
|
|
|
|
|
|
|
exportable := make([]ExportableCharacterClass, len(classes))
|
|
|
|
|
for i, class := range classes {
|
|
|
|
|
exportable[i] = ExportableCharacterClass{
|
|
|
|
|
Code: class.Code,
|
|
|
|
|
Name: class.Name,
|
|
|
|
|
Description: class.Description,
|
|
|
|
|
SourceCode: sourceMap[class.SourceID],
|
|
|
|
|
GameSystem: class.GameSystem,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "character_classes.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportCharacterClasses imports character classes from a JSON file
|
|
|
|
|
func ImportCharacterClasses(inputDir string) error {
|
|
|
|
|
var exportable []ExportableCharacterClass
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "character_classes.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
sourceMap := buildSourceMapReverse()
|
2026-01-04 23:06:40 +01:00
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
2026-01-05 09:56:12 +01:00
|
|
|
class := models.CharacterClass{
|
|
|
|
|
Code: exp.Code,
|
|
|
|
|
Name: exp.Name,
|
|
|
|
|
Description: exp.Description,
|
|
|
|
|
SourceID: sourceMap[exp.SourceCode],
|
|
|
|
|
GameSystem: exp.GameSystem,
|
|
|
|
|
}
|
2026-01-04 23:06:40 +01:00
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
if err := findOrCreateByCode(exp.Code, &class, "character class"); err != nil {
|
|
|
|
|
return err
|
2026-01-04 23:06:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportSpellSchools exports all spell schools to a JSON file
|
|
|
|
|
func ExportSpellSchools(outputDir string) error {
|
|
|
|
|
var schools []models.SpellSchool
|
|
|
|
|
if err := database.DB.Find(&schools).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch spell schools: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
sourceMap := buildSourceMap()
|
2026-01-04 23:06:40 +01:00
|
|
|
|
|
|
|
|
exportable := make([]ExportableSpellSchool, len(schools))
|
|
|
|
|
for i, school := range schools {
|
|
|
|
|
exportable[i] = ExportableSpellSchool{
|
|
|
|
|
Name: school.Name,
|
|
|
|
|
Description: school.Description,
|
|
|
|
|
SourceCode: sourceMap[school.SourceID],
|
|
|
|
|
GameSystem: school.GameSystem,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "spell_schools.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportSpellSchools imports spell schools from a JSON file
|
|
|
|
|
func ImportSpellSchools(inputDir string) error {
|
|
|
|
|
var exportable []ExportableSpellSchool
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "spell_schools.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get source map
|
|
|
|
|
var sources []models.Source
|
|
|
|
|
database.DB.Find(&sources)
|
|
|
|
|
sourceMap := make(map[string]uint)
|
|
|
|
|
for _, s := range sources {
|
|
|
|
|
sourceMap[s.Code] = s.ID
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
|
|
|
|
var school models.SpellSchool
|
|
|
|
|
result := database.DB.Where("name = ? AND game_system = ?", exp.Name, exp.GameSystem).First(&school)
|
|
|
|
|
|
|
|
|
|
sourceID := sourceMap[exp.SourceCode]
|
|
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
school = models.SpellSchool{
|
|
|
|
|
Name: exp.Name,
|
|
|
|
|
Description: exp.Description,
|
|
|
|
|
SourceID: sourceID,
|
|
|
|
|
GameSystem: exp.GameSystem,
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&school).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create spell school %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query spell school %s: %w", exp.Name, result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
school.Description = exp.Description
|
|
|
|
|
school.SourceID = sourceID
|
|
|
|
|
school.GameSystem = exp.GameSystem
|
|
|
|
|
|
|
|
|
|
if err := database.DB.Save(&school).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update spell school %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Export/Import functions for ClassCategoryEPCost, ClassSpellSchoolEPCost, SpellLevelLECost
|
|
|
|
|
// SkillImprovementCost, WeaponSkill, Equipment, Weapon, Container, Transportation, Believe
|
|
|
|
|
|
|
|
|
|
// ExportClassCategoryEPCosts exports class-category EP costs
|
|
|
|
|
func ExportClassCategoryEPCosts(outputDir string) error {
|
|
|
|
|
var costs []models.ClassCategoryEPCost
|
|
|
|
|
if err := database.DB.Find(&costs).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch class category EP costs: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
// Build reverse maps for export (ID -> Code/Name)
|
2026-01-04 23:06:40 +01:00
|
|
|
var classes []models.CharacterClass
|
|
|
|
|
database.DB.Find(&classes)
|
|
|
|
|
classMap := make(map[uint]string)
|
|
|
|
|
for _, c := range classes {
|
|
|
|
|
classMap[c.ID] = c.Code
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var categories []models.SkillCategory
|
|
|
|
|
database.DB.Find(&categories)
|
|
|
|
|
categoryMap := make(map[uint]string)
|
|
|
|
|
for _, cat := range categories {
|
|
|
|
|
categoryMap[cat.ID] = cat.Name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exportable := make([]ExportableClassCategoryEPCost, len(costs))
|
|
|
|
|
for i, cost := range costs {
|
|
|
|
|
exportable[i] = ExportableClassCategoryEPCost{
|
|
|
|
|
CharacterClassCode: classMap[cost.CharacterClassID],
|
|
|
|
|
SkillCategoryName: categoryMap[cost.SkillCategoryID],
|
|
|
|
|
EPPerTE: cost.EPPerTE,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "class_category_ep_costs.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportClassCategoryEPCosts imports class-category EP costs
|
|
|
|
|
func ImportClassCategoryEPCosts(inputDir string) error {
|
|
|
|
|
var exportable []ExportableClassCategoryEPCost
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "class_category_ep_costs.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
classMap := buildCharacterClassMap()
|
2026-01-04 23:06:40 +01:00
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
// Build category map (name -> id) - need to aggregate by name since we don't have game_system in the exportable
|
2026-01-04 23:06:40 +01:00
|
|
|
var categories []models.SkillCategory
|
|
|
|
|
database.DB.Find(&categories)
|
|
|
|
|
categoryMap := make(map[string]uint)
|
|
|
|
|
for _, cat := range categories {
|
|
|
|
|
categoryMap[cat.Name] = cat.ID
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
|
|
|
|
classID := classMap[exp.CharacterClassCode]
|
|
|
|
|
categoryID := categoryMap[exp.SkillCategoryName]
|
|
|
|
|
|
|
|
|
|
var cost models.ClassCategoryEPCost
|
|
|
|
|
result := database.DB.Where("character_class_id = ? AND skill_category_id = ?", classID, categoryID).First(&cost)
|
|
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
cost = models.ClassCategoryEPCost{
|
|
|
|
|
CharacterClassID: classID,
|
|
|
|
|
SkillCategoryID: categoryID,
|
|
|
|
|
EPPerTE: exp.EPPerTE,
|
|
|
|
|
CCLass: exp.CharacterClassCode,
|
|
|
|
|
SCategory: exp.SkillCategoryName,
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&cost).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create class category EP cost: %w", err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query class category EP cost: %w", result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
cost.EPPerTE = exp.EPPerTE
|
|
|
|
|
cost.CCLass = exp.CharacterClassCode
|
|
|
|
|
cost.SCategory = exp.SkillCategoryName
|
|
|
|
|
if err := database.DB.Save(&cost).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update class category EP cost: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportClassSpellSchoolEPCosts exports class-spell school EP costs
|
|
|
|
|
func ExportClassSpellSchoolEPCosts(outputDir string) error {
|
|
|
|
|
var costs []models.ClassSpellSchoolEPCost
|
|
|
|
|
if err := database.DB.Find(&costs).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch class spell school EP costs: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
// Build reverse maps for export
|
2026-01-04 23:06:40 +01:00
|
|
|
var classes []models.CharacterClass
|
|
|
|
|
database.DB.Find(&classes)
|
|
|
|
|
classMap := make(map[uint]string)
|
|
|
|
|
for _, c := range classes {
|
|
|
|
|
classMap[c.ID] = c.Code
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var schools []models.SpellSchool
|
|
|
|
|
database.DB.Find(&schools)
|
|
|
|
|
schoolMap := make(map[uint]string)
|
|
|
|
|
for _, s := range schools {
|
|
|
|
|
schoolMap[s.ID] = s.Name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exportable := make([]ExportableClassSpellSchoolEPCost, len(costs))
|
|
|
|
|
for i, cost := range costs {
|
|
|
|
|
exportable[i] = ExportableClassSpellSchoolEPCost{
|
|
|
|
|
CharacterClassCode: classMap[cost.CharacterClassID],
|
|
|
|
|
SpellSchoolName: schoolMap[cost.SpellSchoolID],
|
|
|
|
|
EPPerLE: cost.EPPerLE,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "class_spell_school_ep_costs.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportClassSpellSchoolEPCosts imports class-spell school EP costs
|
|
|
|
|
func ImportClassSpellSchoolEPCosts(inputDir string) error {
|
|
|
|
|
var exportable []ExportableClassSpellSchoolEPCost
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "class_spell_school_ep_costs.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
classMap := buildCharacterClassMap()
|
2026-01-04 23:06:40 +01:00
|
|
|
|
2026-01-05 09:56:12 +01:00
|
|
|
// Build spell school map (name -> id)
|
2026-01-04 23:06:40 +01:00
|
|
|
var schools []models.SpellSchool
|
|
|
|
|
database.DB.Find(&schools)
|
|
|
|
|
schoolMap := make(map[string]uint)
|
|
|
|
|
for _, s := range schools {
|
|
|
|
|
schoolMap[s.Name] = s.ID
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
|
|
|
|
classID := classMap[exp.CharacterClassCode]
|
|
|
|
|
schoolID := schoolMap[exp.SpellSchoolName]
|
|
|
|
|
|
|
|
|
|
var cost models.ClassSpellSchoolEPCost
|
|
|
|
|
result := database.DB.Where("character_class_id = ? AND spell_school_id = ?", classID, schoolID).First(&cost)
|
|
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
cost = models.ClassSpellSchoolEPCost{
|
|
|
|
|
CharacterClassID: classID,
|
|
|
|
|
SpellSchoolID: schoolID,
|
|
|
|
|
EPPerLE: exp.EPPerLE,
|
|
|
|
|
CCLass: exp.CharacterClassCode,
|
|
|
|
|
SCategory: exp.SpellSchoolName,
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&cost).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create class spell school EP cost: %w", err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query class spell school EP cost: %w", result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
cost.EPPerLE = exp.EPPerLE
|
|
|
|
|
cost.CCLass = exp.CharacterClassCode
|
|
|
|
|
cost.SCategory = exp.SpellSchoolName
|
|
|
|
|
if err := database.DB.Save(&cost).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update class spell school EP cost: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportSpellLevelLECosts exports spell level LE costs
|
|
|
|
|
func ExportSpellLevelLECosts(outputDir string) error {
|
|
|
|
|
var costs []models.SpellLevelLECost
|
|
|
|
|
if err := database.DB.Find(&costs).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch spell level LE costs: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exportable := make([]ExportableSpellLevelLECost, len(costs))
|
|
|
|
|
for i, cost := range costs {
|
|
|
|
|
exportable[i] = ExportableSpellLevelLECost{
|
2026-01-28 22:40:44 +01:00
|
|
|
Level: cost.Level,
|
|
|
|
|
LERequired: cost.LERequired,
|
|
|
|
|
GameSystem: cost.GameSystem,
|
|
|
|
|
GameSystemId: cost.GameSystemId,
|
2026-01-04 23:06:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "spell_level_le_costs.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportSpellLevelLECosts imports spell level LE costs
|
|
|
|
|
func ImportSpellLevelLECosts(inputDir string) error {
|
|
|
|
|
var exportable []ExportableSpellLevelLECost
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "spell_level_le_costs.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
2026-01-28 22:40:44 +01:00
|
|
|
gs := models.GetGameSystem(exp.GameSystemId, exp.GameSystem)
|
2026-01-04 23:06:40 +01:00
|
|
|
var cost models.SpellLevelLECost
|
2026-01-28 22:40:44 +01:00
|
|
|
result := database.DB.Where("level = ? AND (game_system = ? OR game_system_id = ?)", exp.Level, gs.Name, gs.ID).First(&cost)
|
2026-01-04 23:06:40 +01:00
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
cost = models.SpellLevelLECost{
|
2026-01-28 22:40:44 +01:00
|
|
|
Level: exp.Level,
|
|
|
|
|
LERequired: exp.LERequired,
|
|
|
|
|
GameSystem: gs.Name,
|
|
|
|
|
GameSystemId: gs.ID,
|
2026-01-04 23:06:40 +01:00
|
|
|
}
|
2026-01-28 22:40:44 +01:00
|
|
|
if err := cost.Create(); err != nil {
|
2026-01-04 23:06:40 +01:00
|
|
|
return fmt.Errorf("failed to create spell level LE cost: %w", err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query spell level LE cost: %w", result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
cost.LERequired = exp.LERequired
|
2026-01-28 22:40:44 +01:00
|
|
|
cost.GameSystem = gs.Name
|
|
|
|
|
cost.GameSystemId = gs.ID
|
|
|
|
|
if err := cost.Save(); err != nil {
|
2026-01-04 23:06:40 +01:00
|
|
|
return fmt.Errorf("failed to update spell level LE cost: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-12 16:36:35 +01:00
|
|
|
// ExportSkillImprovementCosts exports all skill improvement costs to a JSON file
|
|
|
|
|
func ExportSkillImprovementCosts(outputDir string) error {
|
2026-02-01 13:33:04 +01:00
|
|
|
var costs []models.SkillImprovementCost
|
2026-02-01 13:21:16 +01:00
|
|
|
if err := database.DB.Find(&costs).Error; err != nil {
|
2026-01-12 16:36:35 +01:00
|
|
|
return fmt.Errorf("failed to fetch skill improvement costs: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exportable := make([]ExportableSkillImprovementCost, 0, len(costs))
|
|
|
|
|
for _, cost := range costs {
|
2026-02-01 13:21:16 +01:00
|
|
|
var lc models.SkillCategory
|
|
|
|
|
if err := database.DB.First(&lc, cost.CategoryID).Error; err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
var ld models.SkillDifficulty
|
|
|
|
|
if err := database.DB.First(&ld, cost.DifficultyID).Error; err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
var scd models.SkillCategoryDifficulty
|
|
|
|
|
if err := database.DB.Where("skill_category = ? AND skill_difficulty = ?", lc.Name, ld.Name).First(&scd).Error; err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
var skill models.Skill
|
|
|
|
|
if err := database.DB.First(&skill, scd.SkillID).Error; err != nil {
|
2026-01-12 16:36:35 +01:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exportable = append(exportable, ExportableSkillImprovementCost{
|
2026-02-01 13:21:16 +01:00
|
|
|
SkillName: skill.Name,
|
|
|
|
|
SkillSystem: skill.GameSystem,
|
|
|
|
|
CategoryName: lc.Name,
|
|
|
|
|
CategorySystem: lc.GameSystem,
|
|
|
|
|
DifficultyName: ld.Name,
|
|
|
|
|
DifficultySystem: ld.GameSystem,
|
2026-01-12 16:36:35 +01:00
|
|
|
CurrentLevel: cost.CurrentLevel,
|
|
|
|
|
TERequired: cost.TERequired,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "skill_improvement_costs.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportSkillImprovementCosts imports skill improvement costs from a JSON file
|
|
|
|
|
func ImportSkillImprovementCosts(inputDir string) error {
|
|
|
|
|
var exportable []ExportableSkillImprovementCost
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "skill_improvement_costs.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build lookup maps using helpers
|
|
|
|
|
skillMap := buildSkillMap()
|
|
|
|
|
categoryMap := buildCategoryMap()
|
|
|
|
|
difficultyMap := buildDifficultyMap()
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
|
|
|
|
// Find skill ID
|
|
|
|
|
skillID, ok := skillMap[exp.SkillSystem][exp.SkillName]
|
|
|
|
|
if !ok {
|
|
|
|
|
return fmt.Errorf("skill not found: %s (%s)", exp.SkillName, exp.SkillSystem)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find category ID
|
|
|
|
|
categoryID, ok := categoryMap[exp.CategorySystem][exp.CategoryName]
|
|
|
|
|
if !ok {
|
|
|
|
|
return fmt.Errorf("category not found: %s (%s)", exp.CategoryName, exp.CategorySystem)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find difficulty ID
|
|
|
|
|
difficultyID, ok := difficultyMap[exp.DifficultySystem][exp.DifficultyName]
|
|
|
|
|
if !ok {
|
|
|
|
|
return fmt.Errorf("difficulty not found: %s (%s)", exp.DifficultyName, exp.DifficultySystem)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find SkillCategoryDifficulty
|
|
|
|
|
var scd models.SkillCategoryDifficulty
|
|
|
|
|
if err := database.DB.Where("skill_id = ? AND skill_category_id = ? AND skill_difficulty_id = ?",
|
|
|
|
|
skillID, categoryID, difficultyID).First(&scd).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("skill category difficulty not found for %s/%s/%s: %w",
|
|
|
|
|
exp.SkillName, exp.CategoryName, exp.DifficultyName, err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-01 13:33:04 +01:00
|
|
|
// Find or create SkillImprovementCost using category/difficulty IDs
|
|
|
|
|
var cost models.SkillImprovementCost
|
2026-02-01 13:21:16 +01:00
|
|
|
result := database.DB.Where("skill_category_id = ? AND skill_difficulty_id = ? AND current_level = ?",
|
|
|
|
|
categoryID, difficultyID, exp.CurrentLevel).First(&cost)
|
2026-01-12 16:36:35 +01:00
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
2026-02-01 13:33:04 +01:00
|
|
|
cost = models.SkillImprovementCost{
|
2026-02-01 13:21:16 +01:00
|
|
|
CategoryID: categoryID,
|
|
|
|
|
DifficultyID: difficultyID,
|
|
|
|
|
CurrentLevel: exp.CurrentLevel,
|
|
|
|
|
TERequired: exp.TERequired,
|
2026-01-12 16:36:35 +01:00
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&cost).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create skill improvement cost: %w", err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query skill improvement cost: %w", result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
cost.TERequired = exp.TERequired
|
|
|
|
|
if err := database.DB.Save(&cost).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update skill improvement cost: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportWeaponSkills exports all weapon skills to a JSON file
|
|
|
|
|
func ExportWeaponSkills(outputDir string) error {
|
|
|
|
|
var skills []models.WeaponSkill
|
|
|
|
|
if err := database.DB.Find(&skills).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch weapon skills: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sourceMap := buildSourceMap()
|
|
|
|
|
|
2026-01-12 16:36:35 +01:00
|
|
|
// Get all weapon skill category difficulties
|
|
|
|
|
var wscds []models.WeaponSkillCategoryDifficulty
|
|
|
|
|
database.DB.Preload("SkillCategory").Preload("SkillDifficulty").Find(&wscds)
|
|
|
|
|
|
|
|
|
|
// Build map of weapon_skill_id -> []category/difficulty combinations
|
|
|
|
|
wscdMap := make(map[uint][]ExportableCategoryDifficulty)
|
|
|
|
|
for _, wscd := range wscds {
|
|
|
|
|
wscdMap[wscd.WeaponSkillID] = append(wscdMap[wscd.WeaponSkillID], ExportableCategoryDifficulty{
|
|
|
|
|
Category: wscd.SkillCategory.Name,
|
|
|
|
|
Difficulty: wscd.SkillDifficulty.Name,
|
|
|
|
|
LearnCost: wscd.LearnCost,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-12 16:36:35 +01:00
|
|
|
exportable := make([]ExportableWeaponSkill, len(skills))
|
|
|
|
|
for i, skill := range skills {
|
|
|
|
|
exportable[i] = ExportableWeaponSkill{
|
2026-01-12 16:36:35 +01:00
|
|
|
Name: skill.Name,
|
|
|
|
|
GameSystem: skill.GameSystem,
|
2026-01-28 09:20:55 +01:00
|
|
|
GameSystemId: skill.GameSystemId,
|
2026-01-12 16:36:35 +01:00
|
|
|
Beschreibung: skill.Beschreibung,
|
|
|
|
|
SourceCode: sourceMap[skill.SourceID],
|
|
|
|
|
PageNumber: skill.PageNumber,
|
|
|
|
|
Initialwert: skill.Initialwert,
|
|
|
|
|
BasisWert: skill.BasisWert,
|
|
|
|
|
Bonuseigenschaft: skill.Bonuseigenschaft,
|
|
|
|
|
Improvable: skill.Improvable,
|
|
|
|
|
InnateSkill: skill.InnateSkill,
|
|
|
|
|
Category: skill.Category,
|
|
|
|
|
Difficulty: skill.Difficulty,
|
|
|
|
|
CategoriesDifficulties: wscdMap[skill.ID],
|
2026-01-12 16:36:35 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "weapon_skills.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportWeaponSkills imports weapon skills from a JSON file
|
|
|
|
|
func ImportWeaponSkills(inputDir string) error {
|
|
|
|
|
var exportable []ExportableWeaponSkill
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "weapon_skills.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-28 09:20:55 +01:00
|
|
|
if err := database.DB.AutoMigrate(&models.Skill{}, &models.WeaponSkill{}); err != nil {
|
|
|
|
|
return fmt.Errorf("failed to migrate weapon skills: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-12 16:36:35 +01:00
|
|
|
sourceMap := buildSourceMapReverse()
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
|
|
|
|
var skill models.WeaponSkill
|
2026-01-28 09:20:55 +01:00
|
|
|
gs := models.GetGameSystem(exp.GameSystemId, exp.GameSystem)
|
|
|
|
|
effectiveName := exp.GameSystem
|
|
|
|
|
if effectiveName == "" {
|
|
|
|
|
effectiveName = gs.Name
|
|
|
|
|
}
|
|
|
|
|
effectiveID := gs.ID
|
|
|
|
|
if effectiveID == 0 {
|
|
|
|
|
effectiveID = models.GetGameSystem(0, "").ID
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result := database.DB.Where("name = ? AND (game_system = ? OR game_system_id = ?)", exp.Name, effectiveName, effectiveID).First(&skill)
|
2026-01-12 16:36:35 +01:00
|
|
|
|
|
|
|
|
sourceID := sourceMap[exp.SourceCode]
|
|
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
skill = models.WeaponSkill{
|
|
|
|
|
Skill: models.Skill{
|
|
|
|
|
Name: exp.Name,
|
2026-01-28 09:20:55 +01:00
|
|
|
GameSystem: effectiveName,
|
|
|
|
|
GameSystemId: effectiveID,
|
2026-01-12 16:36:35 +01:00
|
|
|
Beschreibung: exp.Beschreibung,
|
|
|
|
|
SourceID: sourceID,
|
|
|
|
|
PageNumber: exp.PageNumber,
|
|
|
|
|
Initialwert: exp.Initialwert,
|
|
|
|
|
BasisWert: exp.BasisWert,
|
|
|
|
|
Bonuseigenschaft: exp.Bonuseigenschaft,
|
|
|
|
|
Improvable: exp.Improvable,
|
|
|
|
|
InnateSkill: exp.InnateSkill,
|
|
|
|
|
Category: exp.Category,
|
|
|
|
|
Difficulty: exp.Difficulty,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&skill).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create weapon skill %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query weapon skill %s: %w", exp.Name, result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
skill.Beschreibung = exp.Beschreibung
|
|
|
|
|
skill.SourceID = sourceID
|
|
|
|
|
skill.PageNumber = exp.PageNumber
|
|
|
|
|
skill.Initialwert = exp.Initialwert
|
|
|
|
|
skill.BasisWert = exp.BasisWert
|
|
|
|
|
skill.Bonuseigenschaft = exp.Bonuseigenschaft
|
|
|
|
|
skill.Improvable = exp.Improvable
|
|
|
|
|
skill.InnateSkill = exp.InnateSkill
|
|
|
|
|
skill.Category = exp.Category
|
|
|
|
|
skill.Difficulty = exp.Difficulty
|
2026-01-28 09:20:55 +01:00
|
|
|
skill.GameSystemId = effectiveID
|
|
|
|
|
skill.GameSystem = effectiveName
|
2026-01-12 16:36:35 +01:00
|
|
|
|
|
|
|
|
if err := database.DB.Save(&skill).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update weapon skill %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-12 16:36:35 +01:00
|
|
|
|
|
|
|
|
// Handle category/difficulty relationships if present
|
|
|
|
|
if len(exp.CategoriesDifficulties) > 0 {
|
|
|
|
|
// Delete existing relationships
|
|
|
|
|
database.DB.Where("weapon_skill_id = ?", skill.ID).Delete(&models.WeaponSkillCategoryDifficulty{})
|
|
|
|
|
|
|
|
|
|
// Create new relationships
|
|
|
|
|
for _, cd := range exp.CategoriesDifficulties {
|
|
|
|
|
var category models.SkillCategory
|
|
|
|
|
var difficulty models.SkillDifficulty
|
|
|
|
|
|
|
|
|
|
if err := database.DB.Where("name = ?", cd.Category).First(&category).Error; err != nil {
|
|
|
|
|
continue // Skip if category not found
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Where("name = ?", cd.Difficulty).First(&difficulty).Error; err != nil {
|
|
|
|
|
continue // Skip if difficulty not found
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wscd := models.WeaponSkillCategoryDifficulty{
|
|
|
|
|
WeaponSkillID: skill.ID,
|
|
|
|
|
SkillCategoryID: category.ID,
|
|
|
|
|
SkillDifficultyID: difficulty.ID,
|
|
|
|
|
LearnCost: cd.LearnCost,
|
|
|
|
|
SCategory: category.Name,
|
|
|
|
|
SDifficulty: difficulty.Name,
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&wscd).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create weapon skill category difficulty: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-12 16:36:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportEquipment exports all equipment to a JSON file
|
|
|
|
|
func ExportEquipment(outputDir string) error {
|
|
|
|
|
var equipment []models.Equipment
|
|
|
|
|
if err := database.DB.Find(&equipment).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch equipment: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sourceMap := buildSourceMap()
|
|
|
|
|
|
|
|
|
|
exportable := make([]ExportableEquipment, len(equipment))
|
|
|
|
|
for i, eq := range equipment {
|
|
|
|
|
exportable[i] = ExportableEquipment{
|
|
|
|
|
Name: eq.Name,
|
|
|
|
|
GameSystem: eq.GameSystem,
|
|
|
|
|
Beschreibung: eq.Beschreibung,
|
|
|
|
|
SourceCode: sourceMap[eq.SourceID],
|
|
|
|
|
PageNumber: eq.PageNumber,
|
|
|
|
|
Gewicht: eq.Gewicht,
|
|
|
|
|
Wert: eq.Wert,
|
|
|
|
|
PersonalItem: eq.PersonalItem,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "equipment.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportEquipment imports equipment from a JSON file
|
|
|
|
|
func ImportEquipment(inputDir string) error {
|
|
|
|
|
var exportable []ExportableEquipment
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "equipment.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sourceMap := buildSourceMapReverse()
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
|
|
|
|
var eq models.Equipment
|
|
|
|
|
result := database.DB.Where("name = ? AND game_system = ?", exp.Name, exp.GameSystem).First(&eq)
|
|
|
|
|
|
|
|
|
|
sourceID := sourceMap[exp.SourceCode]
|
|
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
eq = models.Equipment{
|
|
|
|
|
Name: exp.Name,
|
|
|
|
|
GameSystem: exp.GameSystem,
|
|
|
|
|
Beschreibung: exp.Beschreibung,
|
|
|
|
|
SourceID: sourceID,
|
|
|
|
|
PageNumber: exp.PageNumber,
|
|
|
|
|
Gewicht: exp.Gewicht,
|
|
|
|
|
Wert: exp.Wert,
|
|
|
|
|
PersonalItem: exp.PersonalItem,
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&eq).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create equipment %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query equipment %s: %w", exp.Name, result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
eq.Beschreibung = exp.Beschreibung
|
|
|
|
|
eq.SourceID = sourceID
|
|
|
|
|
eq.PageNumber = exp.PageNumber
|
|
|
|
|
eq.Gewicht = exp.Gewicht
|
|
|
|
|
eq.Wert = exp.Wert
|
|
|
|
|
eq.PersonalItem = exp.PersonalItem
|
|
|
|
|
|
|
|
|
|
if err := database.DB.Save(&eq).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update equipment %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportWeapons exports all weapons to a JSON file
|
|
|
|
|
func ExportWeapons(outputDir string) error {
|
|
|
|
|
var weapons []models.Weapon
|
|
|
|
|
if err := database.DB.Find(&weapons).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch weapons: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sourceMap := buildSourceMap()
|
|
|
|
|
|
|
|
|
|
exportable := make([]ExportableWeapon, len(weapons))
|
|
|
|
|
for i, weapon := range weapons {
|
|
|
|
|
exportable[i] = ExportableWeapon{
|
|
|
|
|
Name: weapon.Name,
|
|
|
|
|
GameSystem: weapon.GameSystem,
|
|
|
|
|
Beschreibung: weapon.Beschreibung,
|
|
|
|
|
SourceCode: sourceMap[weapon.SourceID],
|
|
|
|
|
PageNumber: weapon.PageNumber,
|
|
|
|
|
Gewicht: weapon.Gewicht,
|
|
|
|
|
Wert: weapon.Wert,
|
|
|
|
|
PersonalItem: weapon.PersonalItem,
|
|
|
|
|
SkillRequired: weapon.SkillRequired,
|
|
|
|
|
Damage: weapon.Damage,
|
|
|
|
|
RangeNear: weapon.RangeNear,
|
|
|
|
|
RangeMiddle: weapon.RangeMiddle,
|
|
|
|
|
RangeFar: weapon.RangeFar,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "weapons.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportWeapons imports weapons from a JSON file
|
|
|
|
|
func ImportWeapons(inputDir string) error {
|
|
|
|
|
var exportable []ExportableWeapon
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "weapons.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sourceMap := buildSourceMapReverse()
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
|
|
|
|
var weapon models.Weapon
|
|
|
|
|
result := database.DB.Where("name = ? AND game_system = ?", exp.Name, exp.GameSystem).First(&weapon)
|
|
|
|
|
|
|
|
|
|
sourceID := sourceMap[exp.SourceCode]
|
|
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
weapon = models.Weapon{
|
|
|
|
|
Equipment: models.Equipment{
|
|
|
|
|
Name: exp.Name,
|
|
|
|
|
GameSystem: exp.GameSystem,
|
|
|
|
|
Beschreibung: exp.Beschreibung,
|
|
|
|
|
SourceID: sourceID,
|
|
|
|
|
PageNumber: exp.PageNumber,
|
|
|
|
|
Gewicht: exp.Gewicht,
|
|
|
|
|
Wert: exp.Wert,
|
|
|
|
|
PersonalItem: exp.PersonalItem,
|
|
|
|
|
},
|
|
|
|
|
SkillRequired: exp.SkillRequired,
|
|
|
|
|
Damage: exp.Damage,
|
|
|
|
|
RangeNear: exp.RangeNear,
|
|
|
|
|
RangeMiddle: exp.RangeMiddle,
|
|
|
|
|
RangeFar: exp.RangeFar,
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&weapon).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create weapon %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query weapon %s: %w", exp.Name, result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
weapon.Beschreibung = exp.Beschreibung
|
|
|
|
|
weapon.SourceID = sourceID
|
|
|
|
|
weapon.PageNumber = exp.PageNumber
|
|
|
|
|
weapon.Gewicht = exp.Gewicht
|
|
|
|
|
weapon.Wert = exp.Wert
|
|
|
|
|
weapon.PersonalItem = exp.PersonalItem
|
|
|
|
|
weapon.SkillRequired = exp.SkillRequired
|
|
|
|
|
weapon.Damage = exp.Damage
|
|
|
|
|
weapon.RangeNear = exp.RangeNear
|
|
|
|
|
weapon.RangeMiddle = exp.RangeMiddle
|
|
|
|
|
weapon.RangeFar = exp.RangeFar
|
|
|
|
|
|
|
|
|
|
if err := database.DB.Save(&weapon).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update weapon %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportContainers exports all containers to a JSON file
|
|
|
|
|
func ExportContainers(outputDir string) error {
|
|
|
|
|
var containers []models.Container
|
|
|
|
|
if err := database.DB.Find(&containers).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch containers: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sourceMap := buildSourceMap()
|
|
|
|
|
|
|
|
|
|
exportable := make([]ExportableContainer, len(containers))
|
|
|
|
|
for i, container := range containers {
|
|
|
|
|
exportable[i] = ExportableContainer{
|
|
|
|
|
Name: container.Name,
|
|
|
|
|
GameSystem: container.GameSystem,
|
|
|
|
|
Beschreibung: container.Beschreibung,
|
|
|
|
|
SourceCode: sourceMap[container.SourceID],
|
|
|
|
|
PageNumber: container.PageNumber,
|
|
|
|
|
Gewicht: container.Gewicht,
|
|
|
|
|
Wert: container.Wert,
|
|
|
|
|
PersonalItem: container.PersonalItem,
|
|
|
|
|
Tragkraft: container.Tragkraft,
|
|
|
|
|
Volumen: container.Volumen,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "containers.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportContainers imports containers from a JSON file
|
|
|
|
|
func ImportContainers(inputDir string) error {
|
|
|
|
|
var exportable []ExportableContainer
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "containers.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sourceMap := buildSourceMapReverse()
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
|
|
|
|
var container models.Container
|
|
|
|
|
result := database.DB.Where("name = ? AND game_system = ?", exp.Name, exp.GameSystem).First(&container)
|
|
|
|
|
|
|
|
|
|
sourceID := sourceMap[exp.SourceCode]
|
|
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
container = models.Container{
|
|
|
|
|
Equipment: models.Equipment{
|
|
|
|
|
Name: exp.Name,
|
|
|
|
|
GameSystem: exp.GameSystem,
|
|
|
|
|
Beschreibung: exp.Beschreibung,
|
|
|
|
|
SourceID: sourceID,
|
|
|
|
|
PageNumber: exp.PageNumber,
|
|
|
|
|
Gewicht: exp.Gewicht,
|
|
|
|
|
Wert: exp.Wert,
|
|
|
|
|
PersonalItem: exp.PersonalItem,
|
|
|
|
|
},
|
|
|
|
|
Tragkraft: exp.Tragkraft,
|
|
|
|
|
Volumen: exp.Volumen,
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&container).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create container %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query container %s: %w", exp.Name, result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
container.Beschreibung = exp.Beschreibung
|
|
|
|
|
container.SourceID = sourceID
|
|
|
|
|
container.PageNumber = exp.PageNumber
|
|
|
|
|
container.Gewicht = exp.Gewicht
|
|
|
|
|
container.Wert = exp.Wert
|
|
|
|
|
container.PersonalItem = exp.PersonalItem
|
|
|
|
|
container.Tragkraft = exp.Tragkraft
|
|
|
|
|
container.Volumen = exp.Volumen
|
|
|
|
|
|
|
|
|
|
if err := database.DB.Save(&container).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update container %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportTransportation exports all transportation to a JSON file
|
|
|
|
|
func ExportTransportation(outputDir string) error {
|
|
|
|
|
var transportation []models.Transportation
|
|
|
|
|
if err := database.DB.Find(&transportation).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch transportation: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sourceMap := buildSourceMap()
|
|
|
|
|
|
|
|
|
|
exportable := make([]ExportableTransportation, len(transportation))
|
|
|
|
|
for i, trans := range transportation {
|
|
|
|
|
exportable[i] = ExportableTransportation{
|
|
|
|
|
Name: trans.Name,
|
|
|
|
|
GameSystem: trans.GameSystem,
|
|
|
|
|
Beschreibung: trans.Beschreibung,
|
|
|
|
|
SourceCode: sourceMap[trans.SourceID],
|
|
|
|
|
PageNumber: trans.PageNumber,
|
|
|
|
|
Gewicht: trans.Gewicht,
|
|
|
|
|
Wert: trans.Wert,
|
|
|
|
|
PersonalItem: trans.PersonalItem,
|
|
|
|
|
Tragkraft: trans.Tragkraft,
|
|
|
|
|
Volumen: trans.Volumen,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "transportation.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportTransportation imports transportation from a JSON file
|
|
|
|
|
func ImportTransportation(inputDir string) error {
|
|
|
|
|
var exportable []ExportableTransportation
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "transportation.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sourceMap := buildSourceMapReverse()
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
|
|
|
|
var trans models.Transportation
|
|
|
|
|
result := database.DB.Where("name = ? AND game_system = ?", exp.Name, exp.GameSystem).First(&trans)
|
|
|
|
|
|
|
|
|
|
sourceID := sourceMap[exp.SourceCode]
|
|
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
trans = models.Transportation{
|
|
|
|
|
Container: models.Container{
|
|
|
|
|
Equipment: models.Equipment{
|
|
|
|
|
Name: exp.Name,
|
|
|
|
|
GameSystem: exp.GameSystem,
|
|
|
|
|
Beschreibung: exp.Beschreibung,
|
|
|
|
|
SourceID: sourceID,
|
|
|
|
|
PageNumber: exp.PageNumber,
|
|
|
|
|
Gewicht: exp.Gewicht,
|
|
|
|
|
Wert: exp.Wert,
|
|
|
|
|
PersonalItem: exp.PersonalItem,
|
|
|
|
|
},
|
|
|
|
|
Tragkraft: exp.Tragkraft,
|
|
|
|
|
Volumen: exp.Volumen,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&trans).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create transportation %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query transportation %s: %w", exp.Name, result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
trans.Beschreibung = exp.Beschreibung
|
|
|
|
|
trans.SourceID = sourceID
|
|
|
|
|
trans.PageNumber = exp.PageNumber
|
|
|
|
|
trans.Gewicht = exp.Gewicht
|
|
|
|
|
trans.Wert = exp.Wert
|
|
|
|
|
trans.PersonalItem = exp.PersonalItem
|
|
|
|
|
trans.Tragkraft = exp.Tragkraft
|
|
|
|
|
trans.Volumen = exp.Volumen
|
|
|
|
|
|
|
|
|
|
if err := database.DB.Save(&trans).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update transportation %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportBelieves exports all beliefs to a JSON file
|
|
|
|
|
func ExportBelieves(outputDir string) error {
|
|
|
|
|
var believes []models.Believe
|
|
|
|
|
if err := database.DB.Find(&believes).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch believes: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sourceMap := buildSourceMap()
|
|
|
|
|
|
|
|
|
|
exportable := make([]ExportableBelieve, len(believes))
|
|
|
|
|
for i, believe := range believes {
|
|
|
|
|
exportable[i] = ExportableBelieve{
|
|
|
|
|
Name: believe.Name,
|
|
|
|
|
GameSystem: believe.GameSystem,
|
|
|
|
|
Beschreibung: believe.Beschreibung,
|
|
|
|
|
SourceCode: sourceMap[believe.SourceID],
|
|
|
|
|
PageNumber: believe.PageNumber,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "believes.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportBelieves imports believes from a JSON file
|
|
|
|
|
func ImportBelieves(inputDir string) error {
|
|
|
|
|
var exportable []ExportableBelieve
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "believes.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sourceMap := buildSourceMapReverse()
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
|
|
|
|
var believe models.Believe
|
|
|
|
|
result := database.DB.Where("name = ? AND game_system = ?", exp.Name, exp.GameSystem).First(&believe)
|
|
|
|
|
|
|
|
|
|
sourceID := sourceMap[exp.SourceCode]
|
|
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
believe = models.Believe{
|
|
|
|
|
Name: exp.Name,
|
|
|
|
|
GameSystem: exp.GameSystem,
|
|
|
|
|
Beschreibung: exp.Beschreibung,
|
|
|
|
|
SourceID: sourceID,
|
|
|
|
|
PageNumber: exp.PageNumber,
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&believe).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create believe %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query believe %s: %w", exp.Name, result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
believe.Beschreibung = exp.Beschreibung
|
|
|
|
|
believe.SourceID = sourceID
|
|
|
|
|
believe.PageNumber = exp.PageNumber
|
|
|
|
|
|
|
|
|
|
if err := database.DB.Save(&believe).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update believe %s: %w", exp.Name, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-12 16:36:35 +01:00
|
|
|
// ExportClassCategoryLearningPoints exports class category learning points to a JSON file
|
|
|
|
|
func ExportClassCategoryLearningPoints(outputDir string) error {
|
|
|
|
|
var records []models.ClassCategoryLearningPoints
|
|
|
|
|
if err := database.DB.Preload("CharacterClass").Preload("SkillCategory").Find(&records).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch class category learning points: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exportable := make([]ExportableClassCategoryLearningPoints, len(records))
|
|
|
|
|
for i, record := range records {
|
|
|
|
|
exportable[i] = ExportableClassCategoryLearningPoints{
|
|
|
|
|
ClassName: record.CharacterClass.Name,
|
|
|
|
|
ClassSystem: record.CharacterClass.GameSystem,
|
|
|
|
|
CategoryName: record.SkillCategory.Name,
|
|
|
|
|
CategorySystem: record.SkillCategory.GameSystem,
|
|
|
|
|
Points: record.Points,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "class_category_learning_points.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportClassCategoryLearningPoints imports class category learning points from a JSON file
|
|
|
|
|
func ImportClassCategoryLearningPoints(inputDir string) error {
|
|
|
|
|
var exportable []ExportableClassCategoryLearningPoints
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "class_category_learning_points.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
|
|
|
|
// Find class
|
|
|
|
|
var class models.CharacterClass
|
|
|
|
|
if err := database.DB.Where("name = ? AND game_system = ?", exp.ClassName, exp.ClassSystem).First(&class).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to find class %s: %w", exp.ClassName, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find category
|
|
|
|
|
var category models.SkillCategory
|
|
|
|
|
if err := database.DB.Where("name = ? AND game_system = ?", exp.CategoryName, exp.CategorySystem).First(&category).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to find category %s: %w", exp.CategoryName, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if record exists
|
|
|
|
|
var record models.ClassCategoryLearningPoints
|
|
|
|
|
result := database.DB.Where("character_class_id = ? AND skill_category_id = ?", class.ID, category.ID).First(&record)
|
|
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
record = models.ClassCategoryLearningPoints{
|
|
|
|
|
CharacterClassID: class.ID,
|
|
|
|
|
SkillCategoryID: category.ID,
|
|
|
|
|
Points: exp.Points,
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&record).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create class category learning points: %w", err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query class category learning points: %w", result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
record.Points = exp.Points
|
|
|
|
|
if err := database.DB.Save(&record).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update class category learning points: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportClassSpellPoints exports class spell points to a JSON file
|
|
|
|
|
func ExportClassSpellPoints(outputDir string) error {
|
|
|
|
|
var records []models.ClassSpellPoints
|
|
|
|
|
if err := database.DB.Preload("CharacterClass").Find(&records).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch class spell points: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exportable := make([]ExportableClassSpellPoints, len(records))
|
|
|
|
|
for i, record := range records {
|
|
|
|
|
exportable[i] = ExportableClassSpellPoints{
|
|
|
|
|
ClassName: record.CharacterClass.Name,
|
|
|
|
|
ClassSystem: record.CharacterClass.GameSystem,
|
|
|
|
|
SpellPoints: record.SpellPoints,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "class_spell_points.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportClassSpellPoints imports class spell points from a JSON file
|
|
|
|
|
func ImportClassSpellPoints(inputDir string) error {
|
|
|
|
|
var exportable []ExportableClassSpellPoints
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "class_spell_points.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
|
|
|
|
// Find class
|
|
|
|
|
var class models.CharacterClass
|
|
|
|
|
if err := database.DB.Where("name = ? AND game_system = ?", exp.ClassName, exp.ClassSystem).First(&class).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to find class %s: %w", exp.ClassName, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if record exists
|
|
|
|
|
var record models.ClassSpellPoints
|
|
|
|
|
result := database.DB.Where("character_class_id = ?", class.ID).First(&record)
|
|
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
record = models.ClassSpellPoints{
|
|
|
|
|
CharacterClassID: class.ID,
|
|
|
|
|
SpellPoints: exp.SpellPoints,
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&record).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create class spell points: %w", err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query class spell points: %w", result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
record.SpellPoints = exp.SpellPoints
|
|
|
|
|
if err := database.DB.Save(&record).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update class spell points: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportClassTypicalSkills exports class typical skills to a JSON file
|
|
|
|
|
func ExportClassTypicalSkills(outputDir string) error {
|
|
|
|
|
var records []models.ClassTypicalSkill
|
|
|
|
|
if err := database.DB.Preload("CharacterClass").Preload("Skill").Find(&records).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch class typical skills: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exportable := make([]ExportableClassTypicalSkill, len(records))
|
|
|
|
|
for i, record := range records {
|
|
|
|
|
exportable[i] = ExportableClassTypicalSkill{
|
|
|
|
|
ClassName: record.CharacterClass.Name,
|
|
|
|
|
ClassSystem: record.CharacterClass.GameSystem,
|
|
|
|
|
SkillName: record.Skill.Name,
|
|
|
|
|
SkillSystem: record.Skill.GameSystem,
|
|
|
|
|
Bonus: record.Bonus,
|
|
|
|
|
Attribute: record.Attribute,
|
|
|
|
|
Notes: record.Notes,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "class_typical_skills.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportClassTypicalSkills imports class typical skills from a JSON file
|
|
|
|
|
func ImportClassTypicalSkills(inputDir string) error {
|
|
|
|
|
var exportable []ExportableClassTypicalSkill
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "class_typical_skills.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
|
|
|
|
// Find class
|
|
|
|
|
var class models.CharacterClass
|
|
|
|
|
if err := database.DB.Where("name = ? AND game_system = ?", exp.ClassName, exp.ClassSystem).First(&class).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to find class %s: %w", exp.ClassName, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find skill
|
|
|
|
|
var skill models.Skill
|
|
|
|
|
if err := database.DB.Where("name = ? AND game_system = ?", exp.SkillName, exp.SkillSystem).First(&skill).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to find skill %s: %w", exp.SkillName, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if record exists
|
|
|
|
|
var record models.ClassTypicalSkill
|
|
|
|
|
result := database.DB.Where("character_class_id = ? AND skill_id = ?", class.ID, skill.ID).First(&record)
|
|
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
record = models.ClassTypicalSkill{
|
|
|
|
|
CharacterClassID: class.ID,
|
|
|
|
|
SkillID: skill.ID,
|
|
|
|
|
Bonus: exp.Bonus,
|
|
|
|
|
Attribute: exp.Attribute,
|
|
|
|
|
Notes: exp.Notes,
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&record).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create class typical skill: %w", err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query class typical skill: %w", result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
record.Bonus = exp.Bonus
|
|
|
|
|
record.Attribute = exp.Attribute
|
|
|
|
|
record.Notes = exp.Notes
|
|
|
|
|
if err := database.DB.Save(&record).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update class typical skill: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExportClassTypicalSpells exports class typical spells to a JSON file
|
|
|
|
|
func ExportClassTypicalSpells(outputDir string) error {
|
|
|
|
|
var records []models.ClassTypicalSpell
|
|
|
|
|
if err := database.DB.Preload("CharacterClass").Preload("Spell").Find(&records).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch class typical spells: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exportable := make([]ExportableClassTypicalSpell, len(records))
|
|
|
|
|
for i, record := range records {
|
|
|
|
|
exportable[i] = ExportableClassTypicalSpell{
|
|
|
|
|
ClassName: record.CharacterClass.Name,
|
|
|
|
|
ClassSystem: record.CharacterClass.GameSystem,
|
|
|
|
|
SpellName: record.Spell.Name,
|
|
|
|
|
SpellSystem: record.Spell.GameSystem,
|
|
|
|
|
Notes: record.Notes,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "class_typical_spells.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportClassTypicalSpells imports class typical spells from a JSON file
|
|
|
|
|
func ImportClassTypicalSpells(inputDir string) error {
|
|
|
|
|
var exportable []ExportableClassTypicalSpell
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "class_typical_spells.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, exp := range exportable {
|
|
|
|
|
// Find class
|
|
|
|
|
var class models.CharacterClass
|
|
|
|
|
if err := database.DB.Where("name = ? AND game_system = ?", exp.ClassName, exp.ClassSystem).First(&class).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to find class %s: %w", exp.ClassName, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find spell
|
|
|
|
|
var spell models.Spell
|
|
|
|
|
if err := database.DB.Where("name = ? AND game_system = ?", exp.SpellName, exp.SpellSystem).First(&spell).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to find spell %s: %w", exp.SpellName, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if record exists
|
|
|
|
|
var record models.ClassTypicalSpell
|
|
|
|
|
result := database.DB.Where("character_class_id = ? AND spell_id = ?", class.ID, spell.ID).First(&record)
|
|
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
record = models.ClassTypicalSpell{
|
|
|
|
|
CharacterClassID: class.ID,
|
|
|
|
|
SpellID: spell.ID,
|
|
|
|
|
Notes: exp.Notes,
|
|
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&record).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create class typical spell: %w", err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query class typical spell: %w", result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
record.Notes = exp.Notes
|
|
|
|
|
if err := database.DB.Save(&record).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update class typical spell: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 22:14:23 +01:00
|
|
|
// ExportMiscLookups exports miscellaneous lookup values to a JSON file
|
|
|
|
|
func ExportMiscLookups(outputDir string) error {
|
|
|
|
|
var lookups []models.MiscLookup
|
|
|
|
|
if err := database.DB.Find(&lookups).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to fetch misc lookups: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sourceMap := buildSourceMap()
|
|
|
|
|
|
|
|
|
|
exportable := make([]ExportableMiscLookup, len(lookups))
|
|
|
|
|
for i, lookup := range lookups {
|
|
|
|
|
exportable[i] = ExportableMiscLookup{
|
2026-01-28 07:44:17 +01:00
|
|
|
Key: lookup.Key,
|
|
|
|
|
Value: lookup.Value,
|
|
|
|
|
SourceCode: sourceMap[lookup.SourceID],
|
|
|
|
|
PageNumber: lookup.PageNumber,
|
|
|
|
|
GameSystemId: lookup.GameSystemId,
|
2026-01-14 22:14:23 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return writeJSON(filepath.Join(outputDir, "misc_lookups.json"), exportable)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportMiscLookups imports miscellaneous lookup values from a JSON file
|
|
|
|
|
func ImportMiscLookups(inputDir string) error {
|
|
|
|
|
var exportable []ExportableMiscLookup
|
|
|
|
|
if err := readJSON(filepath.Join(inputDir, "misc_lookups.json"), &exportable); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sourceMap := buildSourceMapReverse()
|
|
|
|
|
|
2026-01-28 07:44:17 +01:00
|
|
|
// ensure new columns exist when importing older exports
|
|
|
|
|
if err := database.DB.AutoMigrate(&models.MiscLookup{}); err != nil {
|
|
|
|
|
return fmt.Errorf("failed to migrate misc lookup table: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gs := models.GetGameSystem(exportable[0].GameSystemId, "")
|
2026-01-14 22:14:23 +01:00
|
|
|
for _, exp := range exportable {
|
|
|
|
|
var lookup models.MiscLookup
|
2026-01-28 07:44:17 +01:00
|
|
|
result := database.DB.Where("key = ? AND (game_system_id = ? OR game_system_id = 0)", exp.Key, gs.ID).First(&lookup)
|
2026-01-14 22:14:23 +01:00
|
|
|
|
|
|
|
|
sourceID := sourceMap[exp.SourceCode]
|
|
|
|
|
|
|
|
|
|
if result.Error == gorm.ErrRecordNotFound {
|
|
|
|
|
lookup = models.MiscLookup{
|
2026-01-28 07:44:17 +01:00
|
|
|
Key: exp.Key,
|
|
|
|
|
Value: exp.Value,
|
|
|
|
|
SourceID: sourceID,
|
|
|
|
|
PageNumber: exp.PageNumber,
|
|
|
|
|
GameSystemId: gs.ID,
|
2026-01-14 22:14:23 +01:00
|
|
|
}
|
|
|
|
|
if err := database.DB.Create(&lookup).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create misc lookup %s: %w", exp.Key, err)
|
|
|
|
|
}
|
|
|
|
|
} else if result.Error != nil {
|
|
|
|
|
return fmt.Errorf("failed to query misc lookup %s: %w", exp.Key, result.Error)
|
|
|
|
|
} else {
|
|
|
|
|
lookup.Value = exp.Value
|
|
|
|
|
lookup.SourceID = sourceID
|
|
|
|
|
lookup.PageNumber = exp.PageNumber
|
2026-01-28 07:44:17 +01:00
|
|
|
lookup.GameSystemId = gs.ID
|
2026-01-14 22:14:23 +01:00
|
|
|
|
|
|
|
|
if err := database.DB.Save(&lookup).Error; err != nil {
|
|
|
|
|
return fmt.Errorf("failed to update misc lookup %s: %w", exp.Key, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-04 23:06:40 +01:00
|
|
|
// Note: SkillImprovementCost, WeaponSkill, Equipment, Weapon, Container, Transportation, Believe
|
|
|
|
|
// export/import functions follow similar patterns - implement as needed
|
|
|
|
|
|
|
|
|
|
// ExportAll exports all master data to the specified directory
|
|
|
|
|
func ExportAll(outputDir string) error {
|
|
|
|
|
// Export in dependency order
|
|
|
|
|
if err := ExportSources(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportCharacterClasses(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportSkillCategories(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportSkillDifficulties(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportSpellSchools(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportSkills(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportSkillCategoryDifficulties(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportSpells(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportClassCategoryEPCosts(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportClassSpellSchoolEPCosts(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportSpellLevelLECosts(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportSkillImprovementCosts(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportWeaponSkills(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-01-12 16:36:35 +01:00
|
|
|
if err := ExportWeaponSkillCategoryDifficulties(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-01-04 23:06:40 +01:00
|
|
|
if err := ExportEquipment(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportWeapons(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportContainers(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportTransportation(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportBelieves(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-01-12 16:36:35 +01:00
|
|
|
if err := ExportClassCategoryLearningPoints(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportClassSpellPoints(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportClassTypicalSkills(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ExportClassTypicalSpells(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-01-14 22:14:23 +01:00
|
|
|
if err := ExportMiscLookups(outputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-01-04 23:06:40 +01:00
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImportAll imports all master data from the specified directory
|
|
|
|
|
func ImportAll(inputDir string) error {
|
|
|
|
|
// Import in dependency order
|
|
|
|
|
if err := ImportSources(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportCharacterClasses(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportSkillCategories(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportSkillDifficulties(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportSpellSchools(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportSkills(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportSkillCategoryDifficulties(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportSpells(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportClassCategoryEPCosts(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportClassSpellSchoolEPCosts(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportSpellLevelLECosts(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportSkillImprovementCosts(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportWeaponSkills(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-01-12 16:36:35 +01:00
|
|
|
if err := ImportWeaponSkillCategoryDifficulties(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-01-04 23:06:40 +01:00
|
|
|
if err := ImportEquipment(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportWeapons(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportContainers(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportTransportation(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportBelieves(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-01-12 16:36:35 +01:00
|
|
|
if err := ImportClassCategoryLearningPoints(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportClassSpellPoints(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportClassTypicalSkills(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := ImportClassTypicalSpells(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-01-14 22:14:23 +01:00
|
|
|
if err := ImportMiscLookups(inputDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-01-04 23:06:40 +01:00
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|