added GameSystemID to SpellLevelLECost, SkillLearningInfo, SpellLearningInfo

This commit is contained in:
2026-01-28 22:40:44 +01:00
parent 6af44fa557
commit 0424f2da00
3 changed files with 162 additions and 25 deletions
+18 -12
View File
@@ -140,9 +140,10 @@ type ExportableClassSpellSchoolEPCost struct {
// ExportableSpellLevelLECost represents spell level LE costs for export // ExportableSpellLevelLECost represents spell level LE costs for export
type ExportableSpellLevelLECost struct { type ExportableSpellLevelLECost struct {
Level int `json:"level"` Level int `json:"level"`
LERequired int `json:"le_required"` LERequired int `json:"le_required"`
GameSystem string `json:"game_system"` GameSystem string `json:"game_system"`
GameSystemId uint `json:"game_system_id"`
} }
// ExportableSkillImprovementCost represents skill improvement costs for export // ExportableSkillImprovementCost represents skill improvement costs for export
@@ -1204,9 +1205,10 @@ func ExportSpellLevelLECosts(outputDir string) error {
exportable := make([]ExportableSpellLevelLECost, len(costs)) exportable := make([]ExportableSpellLevelLECost, len(costs))
for i, cost := range costs { for i, cost := range costs {
exportable[i] = ExportableSpellLevelLECost{ exportable[i] = ExportableSpellLevelLECost{
Level: cost.Level, Level: cost.Level,
LERequired: cost.LERequired, LERequired: cost.LERequired,
GameSystem: cost.GameSystem, GameSystem: cost.GameSystem,
GameSystemId: cost.GameSystemId,
} }
} }
@@ -1221,23 +1223,27 @@ func ImportSpellLevelLECosts(inputDir string) error {
} }
for _, exp := range exportable { for _, exp := range exportable {
gs := models.GetGameSystem(exp.GameSystemId, exp.GameSystem)
var cost models.SpellLevelLECost var cost models.SpellLevelLECost
result := database.DB.Where("level = ? AND game_system = ?", exp.Level, exp.GameSystem).First(&cost) result := database.DB.Where("level = ? AND (game_system = ? OR game_system_id = ?)", exp.Level, gs.Name, gs.ID).First(&cost)
if result.Error == gorm.ErrRecordNotFound { if result.Error == gorm.ErrRecordNotFound {
cost = models.SpellLevelLECost{ cost = models.SpellLevelLECost{
Level: exp.Level, Level: exp.Level,
LERequired: exp.LERequired, LERequired: exp.LERequired,
GameSystem: exp.GameSystem, GameSystem: gs.Name,
GameSystemId: gs.ID,
} }
if err := database.DB.Create(&cost).Error; err != nil { if err := cost.Create(); err != nil {
return fmt.Errorf("failed to create spell level LE cost: %w", err) return fmt.Errorf("failed to create spell level LE cost: %w", err)
} }
} else if result.Error != nil { } else if result.Error != nil {
return fmt.Errorf("failed to query spell level LE cost: %w", result.Error) return fmt.Errorf("failed to query spell level LE cost: %w", result.Error)
} else { } else {
cost.LERequired = exp.LERequired cost.LERequired = exp.LERequired
if err := database.DB.Save(&cost).Error; err != nil { cost.GameSystem = gs.Name
cost.GameSystemId = gs.ID
if err := cost.Save(); err != nil {
return fmt.Errorf("failed to update spell level LE cost: %w", err) return fmt.Errorf("failed to update spell level LE cost: %w", err)
} }
} }
+62 -13
View File
@@ -95,10 +95,11 @@ type ClassSpellSchoolEPCost struct {
// SpellLevelLECost definiert LE-Kosten pro Zauber-Stufe // SpellLevelLECost definiert LE-Kosten pro Zauber-Stufe
type SpellLevelLECost struct { type SpellLevelLECost struct {
ID uint `gorm:"primaryKey" json:"id"` ID uint `gorm:"primaryKey" json:"id"`
Level int `gorm:"uniqueIndex;not null" json:"level"` // Zauber-Stufe (1-12) Level int `gorm:"uniqueIndex;not null" json:"level"` // Zauber-Stufe (1-12)
LERequired int `gorm:"not null" json:"le_required"` // Benötigte Lerneinheiten LERequired int `gorm:"not null" json:"le_required"` // Benötigte Lerneinheiten
GameSystem string `gorm:"index;default:midgard" json:"game_system"` 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 // SkillCategoryDifficulty definiert die Schwierigkeit einer Fertigkeit in einer bestimmten Kategorie
@@ -155,6 +156,8 @@ type SkillLearningInfo struct {
ClassCode string `json:"class_code"` ClassCode string `json:"class_code"`
ClassName string `json:"class_name"` ClassName string `json:"class_name"`
EPPerTE int `json:"ep_per_te"` 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 // SpellLearningInfo kombiniert alle Informationen für einen Zauber
@@ -169,6 +172,8 @@ type SpellLearningInfo struct {
ClassName string `json:"class_name"` ClassName string `json:"class_name"`
EPPerLE int `json:"ep_per_le"` EPPerLE int `json:"ep_per_le"`
LERequired int `json:"le_required"` 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 // AuditLogEntry repräsentiert einen Eintrag im Audit-Log
@@ -232,6 +237,27 @@ func (SkillImprovementCost) TableName() string {
return "learning_skill_improvement_costs" 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() { func (s *Source) ensureGameSystem() {
gs := GetGameSystem(s.GameSystemId, s.GameSystem) gs := GetGameSystem(s.GameSystemId, s.GameSystem)
s.GameSystemId = gs.ID s.GameSystemId = gs.ID
@@ -411,6 +437,7 @@ func (cssec *ClassSpellSchoolEPCost) Create() error {
} }
func (sllc *SpellLevelLECost) Create() error { func (sllc *SpellLevelLECost) Create() error {
sllc.ensureGameSystem()
return database.DB.Create(sllc).Error return database.DB.Create(sllc).Error
} }
@@ -457,11 +484,14 @@ func GetEPPerLEForClassAndSpellSchool(classCode string, schoolName string) (int,
// GetSkillCategoryAndDifficultyNewSystem findet die beste Kategorie für eine Fertigkeit basierend auf niedrigsten EP-Kosten // GetSkillCategoryAndDifficultyNewSystem findet die beste Kategorie für eine Fertigkeit basierend auf niedrigsten EP-Kosten
func GetSkillCategoryAndDifficultyNewSystem(skillName string, classCode string) (*SkillLearningInfo, error) { func GetSkillCategoryAndDifficultyNewSystem(skillName string, classCode string) (*SkillLearningInfo, error) {
var results []SkillLearningInfo var results []SkillLearningInfo
gs := GetGameSystem(0, "midgard")
err := database.DB.Raw(` err := database.DB.Raw(`
SELECT SELECT
scd.skill_id, scd.skill_id,
s.name as skill_name, 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_category as category_name,
scd.skill_difficulty as difficulty_name, scd.skill_difficulty as difficulty_name,
scd.learn_cost, scd.learn_cost,
@@ -472,14 +502,20 @@ func GetSkillCategoryAndDifficultyNewSystem(skillName string, classCode string)
FROM learning_skill_category_difficulties scd FROM learning_skill_category_difficulties scd
JOIN learning_class_category_ep_costs ccec ON scd.skill_category = ccec.skill_category JOIN learning_class_category_ep_costs ccec ON scd.skill_category = ccec.skill_category
JOIN gsm_skills s ON scd.skill_id = s.id JOIN gsm_skills s ON scd.skill_id = s.id
WHERE s.name = ? AND ccec.character_class = ? WHERE s.name = ? AND ccec.character_class = ? AND (s.game_system = ? OR s.game_system_id = ?)
ORDER BY total_cost ASC ORDER BY total_cost ASC
`, skillName, classCode).Scan(&results).Error `, skillName, classCode, gs.Name, gs.ID).Scan(&results).Error
if err != nil { if err != nil {
return nil, err 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 { if len(results) == 0 {
return nil, gorm.ErrRecordNotFound return nil, gorm.ErrRecordNotFound
} }
@@ -490,11 +526,14 @@ func GetSkillCategoryAndDifficultyNewSystem(skillName string, classCode string)
// GetSkillInfoCategoryAndDifficultyNewSystem holt die Informationen für eine spezifische Kategorie/Schwierigkeit // GetSkillInfoCategoryAndDifficultyNewSystem holt die Informationen für eine spezifische Kategorie/Schwierigkeit
func GetSkillInfoCategoryAndDifficultyNewSystem(skillName, category, difficulty, classCode string) (*SkillLearningInfo, error) { func GetSkillInfoCategoryAndDifficultyNewSystem(skillName, category, difficulty, classCode string) (*SkillLearningInfo, error) {
var result SkillLearningInfo var result SkillLearningInfo
gs := GetGameSystem(0, "midgard")
err := database.DB.Raw(` err := database.DB.Raw(`
SELECT SELECT
scd.skill_id, scd.skill_id,
s.name as skill_name, 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_category as category_name,
scd.skill_difficulty as difficulty_name, scd.skill_difficulty as difficulty_name,
scd.learn_cost, scd.learn_cost,
@@ -505,13 +544,17 @@ func GetSkillInfoCategoryAndDifficultyNewSystem(skillName, category, difficulty,
FROM learning_skill_category_difficulties scd FROM learning_skill_category_difficulties scd
JOIN learning_class_category_ep_costs ccec ON scd.skill_category = ccec.skill_category JOIN learning_class_category_ep_costs ccec ON scd.skill_category = ccec.skill_category
JOIN gsm_skills s ON scd.skill_id = s.id 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 = ? 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).Scan(&result).Error `, skillName, category, difficulty, classCode, gs.Name, gs.ID).Scan(&result).Error
if err != nil { if err != nil {
return nil, err return nil, err
} }
gs = GetGameSystem(result.GameSystemId, result.GameSystem)
result.GameSystemId = gs.ID
result.GameSystem = gs.Name
return &result, nil return &result, nil
} }
@@ -523,6 +566,8 @@ func GetSpellLearningInfoNewSystem(spellName string, classCode string) (*SpellLe
SELECT SELECT
s.id as spell_id, s.id as spell_id,
s.name as spell_name, 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.stufe as spell_level,
s.learning_category as school_name, s.learning_category as school_name,
cssec.character_class as class_code, cssec.character_class as class_code,
@@ -531,7 +576,7 @@ func GetSpellLearningInfoNewSystem(spellName string, classCode string) (*SpellLe
COALESCE(sllc.le_required, 0) as le_required COALESCE(sllc.le_required, 0) as le_required
FROM gsm_spells s FROM gsm_spells s
JOIN learning_class_spell_school_ep_costs cssec ON s.learning_category = cssec.spell_school 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 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 = ? WHERE s.name = ? AND cssec.character_class = ?
`, spellName, classCode).Scan(&result).Error `, spellName, classCode).Scan(&result).Error
@@ -539,15 +584,19 @@ func GetSpellLearningInfoNewSystem(spellName string, classCode string) (*SpellLe
return nil, err 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 // Validate spell level - level 0 is not a valid spell level
if result.SpellLevel <= 0 { 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) return nil, fmt.Errorf("invalid spell level %d for spell '%s' - spell levels must be 1 or higher", result.SpellLevel, spellName)
} }
// Validate that we found a spell (spell_id should be > 0) gs := GetGameSystem(result.GameSystemId, result.GameSystem)
if result.SpellID == 0 { result.GameSystemId = gs.ID
return nil, gorm.ErrRecordNotFound result.GameSystem = gs.Name
}
return &result, nil return &result, nil
} }
@@ -225,6 +225,28 @@ func TestSkillDifficulty_Create_SetsGameSystem(t *testing.T) {
assert.NotZero(t, difficulty.GameSystemId) assert.NotZero(t, difficulty.GameSystemId)
} }
// =============================================================================
// Tests for SpellLevelLECost struct and methods
// =============================================================================
func TestSpellLevelLECost_Create_SetsGameSystem(t *testing.T) {
setupLearningCostsTestDB(t)
gs := GetGameSystem(0, "midgard")
cost := SpellLevelLECost{
Level: 99,
LERequired: 1,
}
err := cost.Create()
require.NoError(t, err)
assert.NotZero(t, cost.ID)
assert.Equal(t, gs.Name, cost.GameSystem)
assert.Equal(t, gs.ID, cost.GameSystemId)
}
// ============================================================================= // =============================================================================
// Tests for SpellSchool struct and methods // Tests for SpellSchool struct and methods
// ============================================================================= // =============================================================================
@@ -336,6 +358,19 @@ func TestGetSkillCategoryAndDifficultyNewSystem_Success(t *testing.T) {
} }
} }
func TestGetSkillCategoryAndDifficultyNewSystem_IncludesGameSystem(t *testing.T) {
setupLearningCostsTestDB(t)
gs := GetGameSystem(0, "midgard")
skillInfo, err := GetSkillCategoryAndDifficultyNewSystem("Schwimmen", "Bb")
require.NoError(t, err)
require.NotNil(t, skillInfo)
assert.Equal(t, gs.Name, skillInfo.GameSystem)
assert.Equal(t, gs.ID, skillInfo.GameSystemId)
}
func TestGetSkillCategoryAndDifficultyNewSystem_InvalidSkill(t *testing.T) { func TestGetSkillCategoryAndDifficultyNewSystem_InvalidSkill(t *testing.T) {
setupLearningCostsTestDB(t) setupLearningCostsTestDB(t)
@@ -373,6 +408,53 @@ func TestGetSpellLearningInfoNewSystem_Success(t *testing.T) {
} }
} }
func TestGetSpellLearningInfoNewSystem_IncludesGameSystem(t *testing.T) {
setupLearningCostsTestDB(t)
gs := GetGameSystem(0, "midgard")
require.NotNil(t, gs)
var class CharacterClass
require.NoError(t, class.FirstByCode("Ma"))
var school SpellSchool
require.NoError(t, school.FirstByName("Dweomer"))
cost := ClassSpellSchoolEPCost{
CharacterClassID: class.ID,
SpellSchoolID: school.ID,
EPPerLE: 3,
}
require.NoError(t, cost.Create())
var spellLevelCost SpellLevelLECost
err := database.DB.Where("level = ?", 1).First(&spellLevelCost).Error
if err != nil {
spellLevelCost = SpellLevelLECost{Level: 1, LERequired: 1, GameSystem: gs.Name, GameSystemId: gs.ID}
require.NoError(t, spellLevelCost.Create())
} else {
spellLevelCost.GameSystem = gs.Name
spellLevelCost.GameSystemId = gs.ID
require.NoError(t, spellLevelCost.Save())
}
spell := Spell{
Name: "TestSpellGS",
GameSystem: gs.Name,
GameSystemId: gs.ID,
Stufe: spellLevelCost.Level,
LearningCategory: school.Name,
}
require.NoError(t, spell.Create())
spellInfo, err := GetSpellLearningInfoNewSystem(spell.Name, class.Code)
require.NoError(t, err)
require.NotNil(t, spellInfo)
assert.Equal(t, gs.Name, spellInfo.GameSystem)
assert.Equal(t, gs.ID, spellInfo.GameSystemId)
}
func TestGetSpellLearningInfoNewSystem_InvalidSpell(t *testing.T) { func TestGetSpellLearningInfoNewSystem_InvalidSpell(t *testing.T) {
setupLearningCostsTestDB(t) setupLearningCostsTestDB(t)