package models import ( "bamort/database" "fmt" "time" "gorm.io/gorm" ) // Source repräsentiert ein Regelwerk/Quellenbuch type Source struct { ID uint `gorm:"primaryKey" json:"id"` Code string `gorm:"uniqueIndex;size:10;not null" json:"code"` // z.B. "KOD", "ARK", "MYS" Name string `gorm:"unique;not null" json:"name"` // z.B. "Kodex", "Arkanum", "Mysterium" FullName string `json:"full_name,omitempty"` // z.B. "Midgard Regelwerk - Kodex" Edition string `json:"edition,omitempty"` // z.B. "5. Edition" Publisher string `json:"publisher,omitempty"` // z.B. "Pegasus Spiele" PublishYear int `json:"publish_year,omitempty"` // Erscheinungsjahr Description string `json:"description,omitempty"` // Beschreibung des Werks IsCore bool `gorm:"default:false" json:"is_core"` // Ist es ein Grundregelwerk? IsActive bool `gorm:"default:true" json:"is_active"` // Ist das Werk aktiv/verfügbar? GameSystem string `gorm:"index;default:midgard" json:"game_system"` GameSystemId uint `json:"game_system_id,omitempty"` } // CharacterClass repräsentiert eine Charakterklasse mit Code und vollständigem Namen type CharacterClass struct { ID uint `gorm:"primaryKey" json:"id"` Code string `gorm:"uniqueIndex;size:3" json:"code"` // z.B. "Hx", "Ma", "Kr" Name string `gorm:"unique;not null" json:"name"` // z.B. "Hexer", "Magier", "Krieger" Description string `json:"description,omitempty"` // Optional: Beschreibung der Klasse SourceID uint `gorm:"not null;index" json:"source_id"` // Verweis auf das Quellenbuch GameSystem string `gorm:"index;default:midgard" json:"game_system"` GameSystemId uint `json:"game_system_id,omitempty"` Source Source `gorm:"foreignKey:SourceID;constraint:OnUpdate:CASCADE,OnDelete:RESTRICT" json:"source"` Quelle string `gorm:"index;size:3;default:KOD" json:"quelle,omitempty"` // Optional: Quelle der Kategorie, falls abweichend } // SkillCategory repräsentiert eine Fertigkeitskategorie type SkillCategory struct { ID uint `gorm:"primaryKey" json:"id"` Name string `gorm:"unique;not null" json:"name"` // z.B. "Alltag", "Kampf", "Waffen" SourceID uint `gorm:"not null;index" json:"source_id"` // Verweis auf das Quellenbuch GameSystem string `gorm:"index;default:midgard" json:"game_system"` GameSystemId uint `json:"game_system_id,omitempty"` Source Source `gorm:"foreignKey:SourceID;constraint:OnUpdate:CASCADE,OnDelete:RESTRICT" json:"source"` Quelle string `gorm:"index;size:3;default:KOD" json:"quelle,omitempty"` // Optional: Quelle der Kategorie, falls abweichend } // SkillDifficulty repräsentiert Schwierigkeitsgrade type SkillDifficulty struct { ID uint `gorm:"primaryKey" json:"id"` Name string `gorm:"unique;not null" json:"name"` // z.B. "leicht", "normal", "schwer", "sehr schwer" Description string `json:"description,omitempty"` // Optional: Beschreibung GameSystem string `gorm:"index;default:midgard" json:"game_system"` GameSystemId uint `json:"game_system_id,omitempty"` } // SpellSchool repräsentiert Zauberschulen type SpellSchool struct { ID uint `gorm:"primaryKey" json:"id"` Name string `gorm:"unique;not null" json:"name"` // z.B. "Beherrschen", "Bewegen", "Erkennen" Description string `json:"description,omitempty"` // Optional: Beschreibung SourceID uint `gorm:"not null;index" json:"source_id"` // Verweis auf das Quellenbuch GameSystem string `gorm:"index;default:midgard" json:"game_system"` GameSystemId uint `json:"game_system_id,omitempty"` Source Source `gorm:"foreignKey:SourceID;constraint:OnUpdate:CASCADE,OnDelete:RESTRICT" json:"source"` Quelle string `gorm:"index;size:3;default:KOD" json:"quelle,omitempty"` // Optional: Quelle der Kategorie, falls abweichend } // ClassCategoryEPCost definiert EP-Kosten für 1 Trainingseinheit (TE) pro Charakterklasse und Fertigkeitskategorie type ClassCategoryEPCost struct { ID uint `gorm:"primaryKey" json:"id"` CharacterClassID uint `gorm:"not null;index" json:"character_class_id"` SkillCategoryID uint `gorm:"not null;index" json:"skill_category_id"` EPPerTE int `gorm:"not null" json:"ep_per_te"` // EP-Kosten für 1 Trainingseinheit CharacterClass CharacterClass `gorm:"foreignKey:CharacterClassID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"character_class"` SkillCategory SkillCategory `gorm:"foreignKey:SkillCategoryID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"skill_category"` CCLass string `gorm:"column:character_class;size:3;not null;index;default:Or" json:"characterClass"` // Abkürzung der Charakterklasse SCategory string `gorm:"column:skill_category;size:15;not null;index;default:Alltag" json:"skillCategory"` // SkillCategory } // ClassSpellSchoolEPCost definiert EP-Kosten für 1 Lerneinheit (LE) für Zauber pro Charakterklasse und Zauberschule type ClassSpellSchoolEPCost struct { ID uint `gorm:"primaryKey" json:"id"` CharacterClassID uint `gorm:"not null;index" json:"character_class_id"` SpellSchoolID uint `gorm:"not null;index" json:"spell_school_id"` EPPerLE int `gorm:"not null" json:"ep_per_le"` // EP-Kosten für 1 Lerneinheit CharacterClass CharacterClass `gorm:"foreignKey:CharacterClassID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"character_class"` SpellSchool SpellSchool `gorm:"foreignKey:SpellSchoolID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"spell_school"` CCLass string `gorm:"column:character_class;size:3;not null;index;default:Or" json:"characterClass"` // Abkürzung der Charakterklasse SCategory string `gorm:"column:spell_school;size:15;not null;index;default:Dweomer" json:"spellSchool"` // Spell School Name } // SpellLevelLECost definiert LE-Kosten pro Zauber-Stufe type SpellLevelLECost struct { ID uint `gorm:"primaryKey" json:"id"` Level int `gorm:"uniqueIndex;not null" json:"level"` // Zauber-Stufe (1-12) LERequired int `gorm:"not null" json:"le_required"` // Benötigte Lerneinheiten GameSystem string `gorm:"index;default:midgard" json:"game_system"` GameSystemId uint `json:"game_system_id,omitempty"` } // SkillCategoryDifficulty definiert die Schwierigkeit einer Fertigkeit in einer bestimmten Kategorie // Eine Fertigkeit kann in mehreren Kategorien mit unterschiedlichen Schwierigkeiten existieren type SkillCategoryDifficulty struct { ID uint `gorm:"primaryKey" json:"id"` SkillID uint `gorm:"not null;index" json:"skill_id"` SkillCategoryID uint `gorm:"not null;index" json:"skill_category_id"` SkillDifficultyID uint `gorm:"not null;index" json:"skill_difficulty_id"` LearnCost int `gorm:"not null" json:"learn_cost"` // LE-Kosten für das Erlernen Skill Skill `gorm:"foreignKey:SkillID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"skill"` SkillCategory SkillCategory `gorm:"foreignKey:SkillCategoryID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"skill_category"` SkillDifficulty SkillDifficulty `gorm:"foreignKey:SkillDifficultyID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"skill_difficulty"` SDifficulty string `gorm:"column:skill_difficulty;size:15;not null;index;default:normal" json:"skillDifficulty"` // Abkürzung der Charakterklasse SCategory string `gorm:"column:skill_category;size:25;not null;index;default:Alltag" json:"skillCategory"` // SkillCategory } // WeaponSkillCategoryDifficulty definiert die Schwierigkeit einer Waffenfertigkeit in einer bestimmten Kategorie // Separate Tabelle für Waffenfertigkeiten, da diese in gsm_weaponskills gespeichert sind type WeaponSkillCategoryDifficulty struct { ID uint `gorm:"primaryKey" json:"id"` WeaponSkillID uint `gorm:"not null;index" json:"weapon_skill_id"` SkillCategoryID uint `gorm:"not null;index" json:"skill_category_id"` SkillDifficultyID uint `gorm:"not null;index" json:"skill_difficulty_id"` LearnCost int `gorm:"not null" json:"learn_cost"` // LE-Kosten für das Erlernen WeaponSkill WeaponSkill `gorm:"foreignKey:WeaponSkillID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"weapon_skill"` SkillCategory SkillCategory `gorm:"foreignKey:SkillCategoryID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"skill_category"` SkillDifficulty SkillDifficulty `gorm:"foreignKey:SkillDifficultyID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"skill_difficulty"` SDifficulty string `gorm:"column:skill_difficulty;size:15;not null;index;default:normal" json:"skillDifficulty"` SCategory string `gorm:"column:skill_category;size:25;not null;index;default:Waffen" json:"skillCategory"` } // SkillImprovementCost definiert TE-Kosten für Verbesserungen basierend auf Kategorie, Schwierigkeit und aktuellem Wert type SkillImprovementCost struct { ID uint `gorm:"primaryKey" json:"id"` SkillCategoryDifficultyID uint `gorm:"not null;index" json:"skill_category_difficulty_id"` CurrentLevel int `gorm:"not null;index" json:"current_level"` // Aktueller Fertigkeitswert TERequired int `gorm:"not null" json:"te_required"` // Benötigte Trainingseinheiten SkillCategoryDifficulty SkillCategoryDifficulty `gorm:"foreignKey:SkillCategoryDifficultyID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"skill_category_difficulty"` } // Für komplexere Queries: View oder Helper-Strukturen // SkillLearningInfo kombiniert alle Informationen für eine Fertigkeit in einer bestimmten Kategorie type SkillLearningInfo struct { SkillID uint `json:"skill_id"` SkillName string `json:"skill_name"` CategoryID uint `json:"category_id"` CategoryName string `json:"category_name"` DifficultyID uint `json:"difficulty_id"` DifficultyName string `json:"difficulty_name"` LearnCost int `json:"learn_cost"` CharacterClassID uint `json:"character_class_id"` ClassCode string `json:"class_code"` ClassName string `json:"class_name"` EPPerTE int `json:"ep_per_te"` GameSystem string `json:"game_system"` GameSystemId uint `json:"game_system_id,omitempty"` } // SpellLearningInfo kombiniert alle Informationen für einen Zauber type SpellLearningInfo struct { SpellID uint `json:"spell_id"` SpellName string `json:"spell_name"` SpellLevel int `json:"spell_level"` SchoolID uint `json:"school_id"` SchoolName string `json:"school_name"` CharacterClassID uint `json:"character_class_id"` ClassCode string `json:"class_code"` ClassName string `json:"class_name"` EPPerLE int `json:"ep_per_le"` LERequired int `json:"le_required"` GameSystem string `json:"game_system"` GameSystemId uint `json:"game_system_id,omitempty"` } // AuditLogEntry repräsentiert einen Eintrag im Audit-Log type AuditLogEntry struct { ID uint `gorm:"primaryKey" json:"id"` CharacterID uint `json:"character_id" gorm:"not null"` FieldName string `json:"field_name" gorm:"not null"` // "experience_points", "gold", "silver", "copper" OldValue int `json:"old_value"` NewValue int `json:"new_value"` Difference int `json:"difference"` // NewValue - OldValue Reason string `json:"reason"` // "manual", "skill_learning", "skill_improvement", "spell_learning", etc. UserID uint `json:"user_id,omitempty"` Timestamp time.Time `json:"timestamp" gorm:"autoCreateTime"` Notes string `json:"notes,omitempty"` // Zusätzliche Informationen } // TableName-Methoden für GORM func (Source) TableName() string { //learning_sources return "gsm_lit_sources" } func (CharacterClass) TableName() string { //learning_character_classes return "gsm_character_classes" } func (SkillCategory) TableName() string { return "learning_skill_categories" } func (SkillDifficulty) TableName() string { return "learning_skill_difficulties" } func (SpellSchool) TableName() string { return "learning_spell_schools" } func (ClassCategoryEPCost) TableName() string { return "learning_class_category_ep_costs" } func (ClassSpellSchoolEPCost) TableName() string { return "learning_class_spell_school_ep_costs" } func (SpellLevelLECost) TableName() string { return "learning_spell_level_le_costs" } func (SkillCategoryDifficulty) TableName() string { return "learning_skill_category_difficulties" } func (WeaponSkillCategoryDifficulty) TableName() string { return "learning_weaponskill_category_difficulties" } func (SkillImprovementCost) TableName() string { return "learning_skill_improvement_costs" } func (sllc *SpellLevelLECost) ensureGameSystem() { gs := GetGameSystem(sllc.GameSystemId, sllc.GameSystem) sllc.GameSystemId = gs.ID sllc.GameSystem = gs.Name } func (sllc *SpellLevelLECost) BeforeCreate(tx *gorm.DB) error { sllc.ensureGameSystem() return nil } func (sllc *SpellLevelLECost) BeforeSave(tx *gorm.DB) error { sllc.ensureGameSystem() return nil } func (sllc *SpellLevelLECost) Save() error { sllc.ensureGameSystem() return database.DB.Save(sllc).Error } func (s *Source) ensureGameSystem() { gs := GetGameSystem(s.GameSystemId, s.GameSystem) s.GameSystemId = gs.ID s.GameSystem = gs.Name } func (s *Source) BeforeCreate(tx *gorm.DB) error { s.ensureGameSystem() return nil } func (s *Source) BeforeSave(tx *gorm.DB) error { s.ensureGameSystem() return nil } func (s *Source) Save() error { s.ensureGameSystem() return database.DB.Save(s).Error } func (cc *CharacterClass) ensureGameSystem() { gs := GetGameSystem(cc.GameSystemId, cc.GameSystem) cc.GameSystemId = gs.ID cc.GameSystem = gs.Name } func (cc *CharacterClass) BeforeCreate(tx *gorm.DB) error { cc.ensureGameSystem() return nil } func (cc *CharacterClass) BeforeSave(tx *gorm.DB) error { cc.ensureGameSystem() return nil } func (cc *CharacterClass) Save() error { cc.ensureGameSystem() return database.DB.Save(cc).Error } func (sc *SkillCategory) ensureGameSystem() { gs := GetGameSystem(sc.GameSystemId, sc.GameSystem) sc.GameSystemId = gs.ID sc.GameSystem = gs.Name } func (sc *SkillCategory) BeforeCreate(tx *gorm.DB) error { sc.ensureGameSystem() return nil } func (sc *SkillCategory) BeforeSave(tx *gorm.DB) error { sc.ensureGameSystem() return nil } func (sc *SkillCategory) Save() error { sc.ensureGameSystem() return database.DB.Save(sc).Error } func (sd *SkillDifficulty) ensureGameSystem() { gs := GetGameSystem(sd.GameSystemId, sd.GameSystem) sd.GameSystemId = gs.ID sd.GameSystem = gs.Name } func (sd *SkillDifficulty) BeforeCreate(tx *gorm.DB) error { sd.ensureGameSystem() return nil } func (sd *SkillDifficulty) BeforeSave(tx *gorm.DB) error { sd.ensureGameSystem() return nil } func (sd *SkillDifficulty) Save() error { sd.ensureGameSystem() return database.DB.Save(sd).Error } func (ss *SpellSchool) ensureGameSystem() { gs := GetGameSystem(ss.GameSystemId, ss.GameSystem) ss.GameSystemId = gs.ID ss.GameSystem = gs.Name } func (ss *SpellSchool) BeforeCreate(tx *gorm.DB) error { ss.ensureGameSystem() return nil } func (ss *SpellSchool) BeforeSave(tx *gorm.DB) error { ss.ensureGameSystem() return nil } func (ss *SpellSchool) Save() error { ss.ensureGameSystem() return database.DB.Save(ss).Error } // CRUD-Methoden func (s *Source) Create() error { s.ensureGameSystem() return database.DB.Create(s).Error } func (s *Source) FirstByCode(code string) error { gs := GetGameSystem(s.GameSystemId, s.GameSystem) return database.DB.Where("(game_system = ? OR game_system_id = ?) AND code = ?", gs.Name, gs.ID, code).First(s).Error } func (s *Source) FirstByName(name string) error { gs := GetGameSystem(s.GameSystemId, s.GameSystem) return database.DB.Where("(game_system = ? OR game_system_id = ?) AND name = ?", gs.Name, gs.ID, name).First(s).Error } func (cc *CharacterClass) Create() error { cc.ensureGameSystem() return database.DB.Create(cc).Error } func (cc *CharacterClass) FirstByCode(code string) error { gs := GetGameSystem(cc.GameSystemId, cc.GameSystem) return database.DB.Where("(game_system = ? OR game_system_id = ?) AND code = ?", gs.Name, gs.ID, code).First(cc).Error } func (cc *CharacterClass) FirstByName(code string) error { gs := GetGameSystem(cc.GameSystemId, cc.GameSystem) return database.DB.Where("(game_system = ? OR game_system_id = ?) AND name = ?", gs.Name, gs.ID, code).First(cc).Error } func (cc *CharacterClass) FirstByNameOrCode(value string) error { gs := GetGameSystem(cc.GameSystemId, cc.GameSystem) return database.DB.Where("(game_system = ? OR game_system_id = ?) AND (name = ? OR code = ?)", gs.Name, gs.ID, value, value).First(cc).Error } func (sc *SkillCategory) Create() error { sc.ensureGameSystem() return database.DB.Create(sc).Error } func (sc *SkillCategory) FirstByName(name string) error { gs := GetGameSystem(sc.GameSystemId, sc.GameSystem) return database.DB.Where("(game_system = ? OR game_system_id = ?) AND name = ?", gs.Name, gs.ID, name).First(sc).Error } func (sd *SkillDifficulty) Create() error { sd.ensureGameSystem() return database.DB.Create(sd).Error } func (sd *SkillDifficulty) FirstByName(name string) error { gs := GetGameSystem(sd.GameSystemId, sd.GameSystem) return database.DB.Where("(game_system = ? OR game_system_id = ?) AND name = ?", gs.Name, gs.ID, name).First(sd).Error } func (ss *SpellSchool) Create() error { ss.ensureGameSystem() return database.DB.Create(ss).Error } func (ss *SpellSchool) FirstByName(name string) error { gs := GetGameSystem(ss.GameSystemId, ss.GameSystem) return database.DB.Where("(game_system = ? OR game_system_id = ?) AND name = ?", gs.Name, gs.ID, name).First(ss).Error } func (ccec *ClassCategoryEPCost) Create() error { return database.DB.Create(ccec).Error } func (cssec *ClassSpellSchoolEPCost) Create() error { return database.DB.Create(cssec).Error } func (sllc *SpellLevelLECost) Create() error { sllc.ensureGameSystem() return database.DB.Create(sllc).Error } func (scd *SkillCategoryDifficulty) Create() error { return database.DB.Create(scd).Error } func (wscd *WeaponSkillCategoryDifficulty) Create() error { return database.DB.Create(wscd).Error } func (sic *SkillImprovementCost) Create() error { return database.DB.Create(sic).Error } // Komplexere Query-Methoden // GetEPPerTEForClassAndCategory holt die EP-Kosten für eine Klasse und Kategorie func GetEPPerTEForClassAndCategory(classCode string, categoryName string) (int, error) { var result ClassCategoryEPCost err := database.DB. Where("character_class = ? AND skill_category = ?", classCode, categoryName). First(&result).Error if err != nil { return 0, err } return result.EPPerTE, nil } // GetEPPerLEForClassAndSpellSchool holt die EP-Kosten für eine Klasse und Zauberschule func GetEPPerLEForClassAndSpellSchool(classCode string, schoolName string) (int, error) { var result ClassSpellSchoolEPCost err := database.DB. Where("character_class = ? AND spell_school = ?", classCode, schoolName). First(&result).Error if err != nil { return 0, err } return result.EPPerLE, nil } // GetSkillCategoryAndDifficultyNewSystem findet die beste Kategorie für eine Fertigkeit basierend auf niedrigsten EP-Kosten func GetSkillCategoryAndDifficultyNewSystem(skillName string, classCode string) (*SkillLearningInfo, error) { var results []SkillLearningInfo gs := GetGameSystem(0, "midgard") err := database.DB.Raw(` SELECT scd.skill_id, s.name as skill_name, s.game_system as game_system, s.game_system_id as game_system_id, scd.skill_category as category_name, scd.skill_difficulty as difficulty_name, scd.learn_cost, ccec.character_class as class_code, ccec.character_class as class_name, ccec.ep_per_te, (scd.learn_cost * ccec.ep_per_te) as total_cost FROM learning_skill_category_difficulties scd JOIN learning_class_category_ep_costs ccec ON scd.skill_category = ccec.skill_category JOIN gsm_skills s ON scd.skill_id = s.id WHERE s.name = ? AND ccec.character_class = ? AND (s.game_system = ? OR s.game_system_id = ?) ORDER BY total_cost ASC `, skillName, classCode, gs.Name, gs.ID).Scan(&results).Error if err != nil { return nil, err } for i := range results { gs := GetGameSystem(results[i].GameSystemId, results[i].GameSystem) results[i].GameSystemId = gs.ID results[i].GameSystem = gs.Name } if len(results) == 0 { return nil, gorm.ErrRecordNotFound } return &results[0], nil } // GetSkillInfoCategoryAndDifficultyNewSystem holt die Informationen für eine spezifische Kategorie/Schwierigkeit func GetSkillInfoCategoryAndDifficultyNewSystem(skillName, category, difficulty, classCode string) (*SkillLearningInfo, error) { var result SkillLearningInfo gs := GetGameSystem(0, "midgard") err := database.DB.Raw(` SELECT scd.skill_id, s.name as skill_name, s.game_system as game_system, s.game_system_id as game_system_id, scd.skill_category as category_name, scd.skill_difficulty as difficulty_name, scd.learn_cost, ccec.character_class as class_code, ccec.character_class as class_name, ccec.ep_per_te, (scd.learn_cost * ccec.ep_per_te) as total_cost FROM learning_skill_category_difficulties scd JOIN learning_class_category_ep_costs ccec ON scd.skill_category = ccec.skill_category JOIN gsm_skills s ON scd.skill_id = s.id WHERE s.name = ? AND scd.skill_category = ? AND scd.skill_difficulty = ? AND ccec.character_class = ? AND (s.game_system = ? OR s.game_system_id = ?) `, skillName, category, difficulty, classCode, gs.Name, gs.ID).Scan(&result).Error if err != nil { return nil, err } gs = GetGameSystem(result.GameSystemId, result.GameSystem) result.GameSystemId = gs.ID result.GameSystem = gs.Name return &result, nil } // GetSpellLearningInfoNewSystem holt alle Informationen für das Erlernen eines Zaubers func GetSpellLearningInfoNewSystem(spellName string, classCode string) (*SpellLearningInfo, error) { var result SpellLearningInfo err := database.DB.Raw(` SELECT s.id as spell_id, s.name as spell_name, s.game_system as game_system, s.game_system_id as game_system_id, s.stufe as spell_level, s.learning_category as school_name, cssec.character_class as class_code, cssec.character_class as class_name, cssec.ep_per_le, COALESCE(sllc.le_required, 0) as le_required FROM gsm_spells s JOIN learning_class_spell_school_ep_costs cssec ON s.learning_category = cssec.spell_school LEFT JOIN learning_spell_level_le_costs sllc ON s.stufe = sllc.level AND (sllc.game_system = s.game_system OR sllc.game_system_id = s.game_system_id OR sllc.game_system_id IS NULL) WHERE s.name = ? AND cssec.character_class = ? `, spellName, classCode).Scan(&result).Error if err != nil { return nil, err } // Validate that we found a spell (spell_id should be > 0) if result.SpellID == 0 { return nil, gorm.ErrRecordNotFound } // Validate spell level - level 0 is not a valid spell level if result.SpellLevel <= 0 { return nil, fmt.Errorf("invalid spell level %d for spell '%s' - spell levels must be 1 or higher", result.SpellLevel, spellName) } gs := GetGameSystem(result.GameSystemId, result.GameSystem) result.GameSystemId = gs.ID result.GameSystem = gs.Name return &result, nil } // GetImprovementCost holt die Verbesserungskosten für eine Fertigkeit func GetImprovementCost(skillName string, categoryName string, difficultyName string, currentLevel int) (int, error) { var result SkillImprovementCost err := database.DB.Raw(` SELECT sic.te_required FROM learning_skill_improvement_costs sic JOIN learning_skill_category_difficulties scd ON sic.skill_category_difficulty_id = scd.id JOIN gsm_skills s ON scd.skill_id = s.id WHERE s.name = ? AND scd.skill_category = ? AND scd.skill_difficulty = ? AND sic.current_level = ? `, skillName, categoryName, difficultyName, currentLevel).Scan(&result).Error if err != nil { return 0, err } return result.TERequired, nil } // GetSkillLearnCost holt die Lernkosten (LE) für eine Fertigkeit basierend auf Kategorie und Schwierigkeit func GetSkillLearnCost(categoryName string, difficultyName string) (int, error) { var result SkillCategoryDifficulty err := database.DB. Where("skill_category = ? AND skill_difficulty = ?", categoryName, difficultyName). First(&result).Error if err != nil { return 0, err } return result.LearnCost, nil } // Quellenmanagement-Funktionen // GetActiveSourceCodes gibt alle aktiven Quellencodes zurück func GetActiveSourceCodes() ([]string, error) { var codes []string err := database.DB.Model(&Source{}).Where("is_active = ?", true).Pluck("code", &codes).Error return codes, err } // GetSourcesByGameSystem gibt alle Quellen für ein bestimmtes Spielsystem zurück func GetSourcesByGameSystem(gameSystem string) ([]Source, error) { var sources []Source err := database.DB.Where("game_system = ?", gameSystem).Order("is_core DESC, code ASC").Find(&sources).Error return sources, err } // GetSkillsByActiveSources gibt alle Fertigkeiten zurück, die in aktiven Quellen definiert sind func GetSkillsByActiveSources(gameSystem string) ([]Skill, error) { var skills []Skill err := database.DB. Joins("LEFT JOIN gsm_lit_sources ON gsm_skills.source_id = gsm_lit_sources.id"). Where("gsm_skills.game_system = ? AND (gsm_lit_sourcesis_active = ? OR gsm_skills.source_id IS NULL)", gameSystem, true). Find(&skills).Error return skills, err } // GetSpellsByActiveSources gibt alle Zauber zurück, die in aktiven Quellen definiert sind func GetSpellsByActiveSources(gameSystem string) ([]Spell, error) { var spells []Spell err := database.DB. Joins("LEFT JOIN gsm_lit_sources ON gsm_spells.source_id = gsm_lit_sources.id"). Where("gsm_spells.game_system = ? AND (gsm_lit_sources.is_active = ? OR gsm_spells.source_id IS NULL)", gameSystem, true). Find(&spells).Error return spells, err } // GetCharacterClassesByActiveSources gibt alle Charakterklassen zurück, die in aktiven Quellen definiert sind func GetCharacterClassesByActiveSources(gameSystem string) ([]CharacterClass, error) { var classes []CharacterClass err := database.DB. Joins("LEFT JOIN gsm_lit_sources ON gsm_character_classes.source_id = gsm_lit_sources.id"). Where("gsm_character_classes.game_system = ? AND (gsm_lit_sources.is_active = ? OR gsm_character_classes.source_id IS NULL)", gameSystem, true). Find(&classes).Error return classes, err } // SetSourceActive aktiviert oder deaktiviert eine Quelle func SetSourceActive(sourceCode string, active bool) error { return database.DB.Model(&Source{}).Where("code = ?", sourceCode).Update("is_active", active).Error } // GetDefaultSourceForContentType gibt die Standardquelle für einen Inhaltstyp zurück func GetDefaultSourceForContentType(contentType string) (string, error) { switch contentType { case "skill", "character_class", "skill_category": return "KOD", nil case "spell", "spell_school": return "ARK", nil case "unknown": return "UNB", nil default: return "KOD", nil // Fallback auf Kodex } } // GetContentTypeDefaultSources gibt eine Übersicht aller Standardquellen-Zuordnungen zurück func GetContentTypeDefaultSources() map[string]string { return map[string]string{ "character_class": "KOD", // Charakterklassen -> Kodex "skill": "KOD", // Fertigkeiten -> Kodex "skill_category": "KOD", // Fertigkeitskategorien -> Kodex "spell": "ARK", // Zauber -> Arkanum "spell_school": "ARK", // Zauberschulen -> Arkanum (erweiterte), KOD (Basis) "unknown": "UNB", // Unbekannte Inhalte -> Unbekannt } }