New test TestCalculateSkillImproveCostNewSystem in chain of learning system
Updated learning_skill_category_difficulties to match rule set removed SkillImprovementCost replaced by SkillImprovementCost2
This commit is contained in:
@@ -368,3 +368,116 @@ func TestCalculateSpellLearnCostNewSystem(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ToDo: add more test cases for other classes dificulties and higher TEs
|
||||
func TestCalculateSkillImproveCostNewSystem(t *testing.T) {
|
||||
// Ensure DB is initialized so GetImprovementCost can run without nil DB
|
||||
database.SetupTestDB()
|
||||
defer database.ResetTestDB()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
request gsmaster.LernCostRequest
|
||||
skillInfo models.SkillLearningInfo
|
||||
targetLevel int
|
||||
requiredTE int
|
||||
requiredEP int
|
||||
requiredGold int
|
||||
availablePP int
|
||||
availableGold int
|
||||
}{
|
||||
{
|
||||
name: "Base cost calculation without rewards",
|
||||
request: gsmaster.LernCostRequest{},
|
||||
skillInfo: models.SkillLearningInfo{
|
||||
SkillName: "Schwimmen",
|
||||
CategoryName: "Körper",
|
||||
DifficultyName: "leicht",
|
||||
ClassCode: "Bb",
|
||||
EPPerTE: 10,
|
||||
LearnCost: 1,
|
||||
},
|
||||
requiredTE: 1,
|
||||
requiredEP: 10,
|
||||
requiredGold: 200,
|
||||
targetLevel: 13,
|
||||
availablePP: 0,
|
||||
availableGold: 0,
|
||||
}, {
|
||||
name: "applies PP reduction",
|
||||
request: gsmaster.LernCostRequest{},
|
||||
skillInfo: models.SkillLearningInfo{
|
||||
SkillName: "Schwimmen",
|
||||
CategoryName: "Körper",
|
||||
DifficultyName: "leicht",
|
||||
ClassCode: "Bb",
|
||||
EPPerTE: 10,
|
||||
LearnCost: 1,
|
||||
},
|
||||
requiredTE: 0,
|
||||
requiredEP: 0,
|
||||
requiredGold: 0,
|
||||
targetLevel: 13,
|
||||
availablePP: 1,
|
||||
availableGold: 0,
|
||||
},
|
||||
|
||||
{
|
||||
name: "converts gold up to half EP",
|
||||
request: gsmaster.LernCostRequest{},
|
||||
skillInfo: models.SkillLearningInfo{
|
||||
SkillName: "Schwimmen",
|
||||
CategoryName: "Körper",
|
||||
DifficultyName: "leicht",
|
||||
ClassCode: "Bb",
|
||||
EPPerTE: 10,
|
||||
},
|
||||
requiredTE: 1,
|
||||
requiredEP: 5,
|
||||
targetLevel: 13,
|
||||
availablePP: 0,
|
||||
availableGold: 50,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
remainingPP := tt.availablePP
|
||||
remainingGold := tt.availableGold
|
||||
|
||||
result := gsmaster.SkillCostResultNew{
|
||||
CharacterClass: tt.skillInfo.ClassCode,
|
||||
SkillName: tt.skillInfo.SkillName,
|
||||
Category: tt.skillInfo.CategoryName,
|
||||
Difficulty: tt.skillInfo.DifficultyName,
|
||||
TargetLevel: tt.targetLevel,
|
||||
}
|
||||
|
||||
err := CalculateSkillImproveCostNewSystem(&tt.request, &result, tt.targetLevel, &remainingPP, &remainingGold, &tt.skillInfo)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Base EP before gold conversion is computed from post-PP LE
|
||||
baseEP := tt.skillInfo.EPPerTE * result.LE
|
||||
originalTrain := result.LE + result.PPUsed
|
||||
|
||||
assert.Greater(t, originalTrain, 0, "should have positive training cost")
|
||||
assert.Equal(t, tt.availablePP-result.PPUsed, remainingPP, "PP tracking should be consistent")
|
||||
assert.Equal(t, tt.availableGold-result.GoldUsed, remainingGold, "Gold tracking should match usage")
|
||||
|
||||
if tt.availablePP > 0 {
|
||||
assert.Equal(t, tt.availablePP-remainingPP, result.PPUsed, "PP used should reduce remaining PP")
|
||||
assert.Equal(t, baseEP, result.EP, "No gold in this case; EP equals base")
|
||||
assert.Equal(t, tt.skillInfo.EPPerTE*result.LE, result.EP)
|
||||
} else {
|
||||
expectedGoldUsed := tt.availableGold
|
||||
halfEPCap := (baseEP / 2) * 10
|
||||
if expectedGoldUsed > halfEPCap {
|
||||
expectedGoldUsed = halfEPCap
|
||||
}
|
||||
assert.Equal(t, expectedGoldUsed, result.GoldUsed)
|
||||
assert.Equal(t, baseEP-expectedGoldUsed/10, result.EP)
|
||||
assert.Equal(t, tt.availableGold-expectedGoldUsed, remainingGold)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1254,30 +1254,37 @@ func ImportSpellLevelLECosts(inputDir string) error {
|
||||
|
||||
// ExportSkillImprovementCosts exports all skill improvement costs to a JSON file
|
||||
func ExportSkillImprovementCosts(outputDir string) error {
|
||||
var costs []models.SkillImprovementCost
|
||||
if err := database.DB.Preload("SkillCategoryDifficulty.Skill").
|
||||
Preload("SkillCategoryDifficulty.SkillCategory").
|
||||
Preload("SkillCategoryDifficulty.SkillDifficulty").
|
||||
Find(&costs).Error; err != nil {
|
||||
var costs []models.SkillImprovementCost2
|
||||
if err := database.DB.Find(&costs).Error; err != nil {
|
||||
return fmt.Errorf("failed to fetch skill improvement costs: %w", err)
|
||||
}
|
||||
|
||||
exportable := make([]ExportableSkillImprovementCost, 0, len(costs))
|
||||
for _, cost := range costs {
|
||||
// Skip records with incomplete relationships
|
||||
if cost.SkillCategoryDifficulty.Skill.Name == "" ||
|
||||
cost.SkillCategoryDifficulty.SkillCategory.Name == "" ||
|
||||
cost.SkillCategoryDifficulty.SkillDifficulty.Name == "" {
|
||||
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 {
|
||||
continue
|
||||
}
|
||||
|
||||
exportable = append(exportable, ExportableSkillImprovementCost{
|
||||
SkillName: cost.SkillCategoryDifficulty.Skill.Name,
|
||||
SkillSystem: cost.SkillCategoryDifficulty.Skill.GameSystem,
|
||||
CategoryName: cost.SkillCategoryDifficulty.SkillCategory.Name,
|
||||
CategorySystem: cost.SkillCategoryDifficulty.SkillCategory.GameSystem,
|
||||
DifficultyName: cost.SkillCategoryDifficulty.SkillDifficulty.Name,
|
||||
DifficultySystem: cost.SkillCategoryDifficulty.SkillDifficulty.GameSystem,
|
||||
SkillName: skill.Name,
|
||||
SkillSystem: skill.GameSystem,
|
||||
CategoryName: lc.Name,
|
||||
CategorySystem: lc.GameSystem,
|
||||
DifficultyName: ld.Name,
|
||||
DifficultySystem: ld.GameSystem,
|
||||
CurrentLevel: cost.CurrentLevel,
|
||||
TERequired: cost.TERequired,
|
||||
})
|
||||
@@ -1325,14 +1332,15 @@ func ImportSkillImprovementCosts(inputDir string) error {
|
||||
exp.SkillName, exp.CategoryName, exp.DifficultyName, err)
|
||||
}
|
||||
|
||||
// Find or create SkillImprovementCost
|
||||
var cost models.SkillImprovementCost
|
||||
result := database.DB.Where("skill_category_difficulty_id = ? AND current_level = ?",
|
||||
scd.ID, exp.CurrentLevel).First(&cost)
|
||||
// Find or create SkillImprovementCost2 using category/difficulty IDs
|
||||
var cost models.SkillImprovementCost2
|
||||
result := database.DB.Where("skill_category_id = ? AND skill_difficulty_id = ? AND current_level = ?",
|
||||
categoryID, difficultyID, exp.CurrentLevel).First(&cost)
|
||||
|
||||
if result.Error == gorm.ErrRecordNotFound {
|
||||
cost = models.SkillImprovementCost{
|
||||
SkillCategoryDifficultyID: scd.ID,
|
||||
cost = models.SkillImprovementCost2{
|
||||
CategoryID: categoryID,
|
||||
DifficultyID: difficultyID,
|
||||
CurrentLevel: exp.CurrentLevel,
|
||||
TERequired: exp.TERequired,
|
||||
}
|
||||
|
||||
@@ -13,30 +13,39 @@ import (
|
||||
|
||||
// ExportSkillImprovementCosts exports all skill improvement costs to a JSON file
|
||||
func ExportSkillImprovementCosts(outputDir string) error {
|
||||
var costs []models.SkillImprovementCost
|
||||
if err := database.DB.Preload("SkillCategoryDifficulty.Skill").
|
||||
Preload("SkillCategoryDifficulty.SkillCategory").
|
||||
Preload("SkillCategoryDifficulty.SkillDifficulty").
|
||||
Find(&costs).Error; err != nil {
|
||||
var costs []models.SkillImprovementCost2
|
||||
if err := database.DB.Find(&costs).Error; err != nil {
|
||||
return fmt.Errorf("failed to fetch skill improvement costs: %w", err)
|
||||
}
|
||||
|
||||
exportable := make([]ExportableSkillImprovementCost, 0, len(costs))
|
||||
for _, cost := range costs {
|
||||
// Skip records with incomplete relationships
|
||||
if cost.SkillCategoryDifficulty.Skill.Name == "" ||
|
||||
cost.SkillCategoryDifficulty.SkillCategory.Name == "" ||
|
||||
cost.SkillCategoryDifficulty.SkillDifficulty.Name == "" {
|
||||
// Resolve category and difficulty names
|
||||
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
|
||||
}
|
||||
// Find skill via learning_skill_category_difficulties
|
||||
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 {
|
||||
continue
|
||||
}
|
||||
|
||||
exportable = append(exportable, ExportableSkillImprovementCost{
|
||||
SkillName: cost.SkillCategoryDifficulty.Skill.Name,
|
||||
SkillSystem: cost.SkillCategoryDifficulty.Skill.GameSystem,
|
||||
CategoryName: cost.SkillCategoryDifficulty.SkillCategory.Name,
|
||||
CategorySystem: cost.SkillCategoryDifficulty.SkillCategory.GameSystem,
|
||||
DifficultyName: cost.SkillCategoryDifficulty.SkillDifficulty.Name,
|
||||
DifficultySystem: cost.SkillCategoryDifficulty.SkillDifficulty.GameSystem,
|
||||
SkillName: skill.Name,
|
||||
SkillSystem: skill.GameSystem,
|
||||
CategoryName: lc.Name,
|
||||
CategorySystem: lc.GameSystem,
|
||||
DifficultyName: ld.Name,
|
||||
DifficultySystem: ld.GameSystem,
|
||||
CurrentLevel: cost.CurrentLevel,
|
||||
TERequired: cost.TERequired,
|
||||
})
|
||||
@@ -84,14 +93,15 @@ func ImportSkillImprovementCosts(inputDir string) error {
|
||||
exp.SkillName, exp.CategoryName, exp.DifficultyName, err)
|
||||
}
|
||||
|
||||
// Find or create SkillImprovementCost
|
||||
var cost models.SkillImprovementCost
|
||||
result := database.DB.Where("skill_category_difficulty_id = ? AND current_level = ?",
|
||||
scd.ID, exp.CurrentLevel).First(&cost)
|
||||
// Find or create SkillImprovementCost2 using category/difficulty IDs
|
||||
var cost models.SkillImprovementCost2
|
||||
result := database.DB.Where("skill_category_id = ? AND skill_difficulty_id = ? AND current_level = ?",
|
||||
categoryID, difficultyID, exp.CurrentLevel).First(&cost)
|
||||
|
||||
if result.Error == gorm.ErrRecordNotFound {
|
||||
cost = models.SkillImprovementCost{
|
||||
SkillCategoryDifficultyID: scd.ID,
|
||||
cost = models.SkillImprovementCost2{
|
||||
CategoryID: categoryID,
|
||||
DifficultyID: difficultyID,
|
||||
CurrentLevel: exp.CurrentLevel,
|
||||
TERequired: exp.TERequired,
|
||||
}
|
||||
|
||||
@@ -893,9 +893,10 @@ func TestExportImportSkillImprovementCosts(t *testing.T) {
|
||||
t.Fatalf("Failed to query SkillCategoryDifficulty: %v", err)
|
||||
}
|
||||
|
||||
// Create SkillImprovementCost
|
||||
improvementCost := models.SkillImprovementCost{
|
||||
SkillCategoryDifficultyID: scd.ID,
|
||||
// Create SkillImprovementCost2 (new model)
|
||||
improvementCost := models.SkillImprovementCost2{
|
||||
CategoryID: category.ID,
|
||||
DifficultyID: difficulty.ID,
|
||||
CurrentLevel: 15, // Use unique level to avoid conflicts
|
||||
TERequired: 5,
|
||||
}
|
||||
@@ -923,10 +924,10 @@ func TestExportImportSkillImprovementCosts(t *testing.T) {
|
||||
t.Fatalf("ImportSkillImprovementCosts failed: %v", err)
|
||||
}
|
||||
|
||||
var imported models.SkillImprovementCost
|
||||
result := database.DB.Where("skill_category_difficulty_id = ? AND current_level = ?", scd.ID, 15).First(&imported)
|
||||
var imported models.SkillImprovementCost2
|
||||
result := database.DB.Where("skill_category_id = ? AND skill_difficulty_id = ? AND current_level = ?", category.ID, difficulty.ID, 15).First(&imported)
|
||||
if result.Error != nil {
|
||||
t.Fatalf("SkillImprovementCost not found after import: %v", result.Error)
|
||||
t.Fatalf("SkillImprovementCost2 not found after import: %v", result.Error)
|
||||
}
|
||||
|
||||
// Should be restored to original value from export
|
||||
|
||||
@@ -291,7 +291,7 @@ func CreateLearningCostsTables() error {
|
||||
&models.ClassSpellSchoolEPCost{},
|
||||
&models.SpellLevelLECost{},
|
||||
&models.SkillCategoryDifficulty{},
|
||||
&models.SkillImprovementCost{},
|
||||
&models.SkillImprovementCost2{},
|
||||
}
|
||||
|
||||
// Erstelle oder migriere alle Tabellen
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"bamort/models"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// MigrateLearningCostsToDatabase überträgt die Daten aus learningCostsData in die Datenbank
|
||||
@@ -473,16 +475,32 @@ func migrateSkillImprovementCosts() error {
|
||||
|
||||
// Für jeden Level in TrainCosts
|
||||
for level, teCost := range data.TrainCosts {
|
||||
improvementCost := models.SkillImprovementCost{
|
||||
SkillCategoryDifficultyID: categoryDifficulty.ID,
|
||||
var improvementCost models.SkillImprovementCost2
|
||||
err := database.DB.Where(
|
||||
"skill_category_id = ? AND skill_difficulty_id = ? AND current_level = ?",
|
||||
skillCategory.ID, skillDifficulty.ID, level,
|
||||
).First(&improvementCost).Error
|
||||
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
improvementCost = models.SkillImprovementCost2{
|
||||
CategoryID: skillCategory.ID,
|
||||
DifficultyID: skillDifficulty.ID,
|
||||
CurrentLevel: level,
|
||||
TERequired: teCost,
|
||||
}
|
||||
|
||||
if err := improvementCost.Create(); err != nil {
|
||||
return fmt.Errorf("failed to create improvement cost for %s level %d: %w", skillName, level, err)
|
||||
}
|
||||
log.Printf("Created improvement cost: %s - %s - %s Level %d = %d TE",
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("failed to query improvement cost for %s level %d: %w", skillName, level, err)
|
||||
} else {
|
||||
improvementCost.TERequired = teCost
|
||||
if err := database.DB.Save(&improvementCost).Error; err != nil {
|
||||
return fmt.Errorf("failed to update improvement cost for %s level %d: %w", skillName, level, err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Upserted improvement cost: %s - %s - %s Level %d = %d TE",
|
||||
skillName, categoryName, difficultyName, level, teCost)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +248,7 @@ func copyMariaDBToSQLite(mariaDB, sqliteDB *gorm.DB) error {
|
||||
&models.SpellLevelLECost{},
|
||||
&models.SkillCategoryDifficulty{},
|
||||
&models.WeaponSkillCategoryDifficulty{},
|
||||
&models.SkillImprovementCost{},
|
||||
&models.SkillImprovementCost2{},
|
||||
&models.ClassCategoryLearningPoints{},
|
||||
&models.ClassSpellPoints{},
|
||||
&models.ClassTypicalSkill{},
|
||||
@@ -368,10 +368,11 @@ func copyTableData(sourceDB, targetDB *gorm.DB, model interface{}) error {
|
||||
}
|
||||
|
||||
// Batch in SQLite einfügen
|
||||
// Use Save() instead of Create() to avoid GORM applying default values to zero values (e.g., false for booleans)
|
||||
// Use Save() with SkipHooks to preserve raw values and avoid callbacks that rely on global DB state
|
||||
db := targetDB.Session(&gorm.Session{SkipHooks: true})
|
||||
for i := 0; i < recordsVal.Len(); i++ {
|
||||
record := recordsVal.Index(i).Addr().Interface()
|
||||
if err := targetDB.Save(record).Error; err != nil {
|
||||
if err := db.Save(record).Error; err != nil {
|
||||
logger.Error("Fehler beim Speichern von Datensatz in Batch %d für %s: %s", batchNum, tableName, err.Error())
|
||||
return err
|
||||
}
|
||||
@@ -770,7 +771,7 @@ func copySQLiteToMariaDB(sqliteDB, mariaDB *gorm.DB) error {
|
||||
&models.SpellLevelLECost{},
|
||||
&models.SkillCategoryDifficulty{}, // Jetzt nach Skills
|
||||
&models.WeaponSkillCategoryDifficulty{},
|
||||
&models.SkillImprovementCost{},
|
||||
&models.SkillImprovementCost2{},
|
||||
&models.ClassCategoryLearningPoints{},
|
||||
&models.ClassSpellPoints{},
|
||||
&models.ClassTypicalSkill{},
|
||||
@@ -923,8 +924,8 @@ func copyTableDataReverse(sourceDB, targetDB *gorm.DB, model interface{}) error
|
||||
return fmt.Errorf("failed to read batch from source: %w", err)
|
||||
}
|
||||
records = batch
|
||||
case *models.SkillImprovementCost:
|
||||
var batch []models.SkillImprovementCost
|
||||
case *models.SkillImprovementCost2:
|
||||
var batch []models.SkillImprovementCost2
|
||||
if err := sourceDB.Limit(batchSize).Offset(offset).Find(&batch).Error; err != nil {
|
||||
return fmt.Errorf("failed to read batch from source: %w", err)
|
||||
}
|
||||
@@ -1162,7 +1163,7 @@ func clearMariaDBData(db *gorm.DB) error {
|
||||
&models.Char{},
|
||||
|
||||
// Learning Costs System - Abhängige Tabellen (vor Skills/Spells löschen)
|
||||
&models.SkillImprovementCost{},
|
||||
&models.SkillImprovementCost2{},
|
||||
&models.WeaponSkillCategoryDifficulty{},
|
||||
&models.SkillCategoryDifficulty{},
|
||||
&models.SpellLevelLECost{},
|
||||
|
||||
@@ -32,7 +32,7 @@ func TestTableListCompleteness(t *testing.T) {
|
||||
"*models.SpellLevelLECost": true,
|
||||
"*models.SkillCategoryDifficulty": true,
|
||||
"*models.WeaponSkillCategoryDifficulty": true,
|
||||
"*models.SkillImprovementCost": true,
|
||||
"*models.SkillImprovementCost2": true,
|
||||
|
||||
// GSMaster Base Data
|
||||
"*models.Skill": true,
|
||||
@@ -88,7 +88,7 @@ func TestTableListCompleteness(t *testing.T) {
|
||||
&models.SpellLevelLECost{},
|
||||
&models.SkillCategoryDifficulty{},
|
||||
&models.WeaponSkillCategoryDifficulty{},
|
||||
&models.SkillImprovementCost{},
|
||||
&models.SkillImprovementCost2{},
|
||||
&models.Skill{},
|
||||
&models.WeaponSkill{},
|
||||
&models.Spell{},
|
||||
@@ -301,8 +301,8 @@ func getModelTypeName(model interface{}) string {
|
||||
return "*models.SkillCategoryDifficulty"
|
||||
case *models.WeaponSkillCategoryDifficulty:
|
||||
return "*models.WeaponSkillCategoryDifficulty"
|
||||
case *models.SkillImprovementCost:
|
||||
return "*models.SkillImprovementCost"
|
||||
case *models.SkillImprovementCost2:
|
||||
return "*models.SkillImprovementCost2"
|
||||
case *models.Skill:
|
||||
return "*models.Skill"
|
||||
case *models.WeaponSkill:
|
||||
|
||||
@@ -180,12 +180,12 @@ func learningMigrateStructure(db ...*gorm.DB) error {
|
||||
&SpellLevelLECost{},
|
||||
&SkillCategoryDifficulty{},
|
||||
&WeaponSkillCategoryDifficulty{},
|
||||
&SkillImprovementCost{},
|
||||
&ClassCategoryLearningPoints{},
|
||||
&ClassSpellPoints{},
|
||||
&ClassTypicalSkill{},
|
||||
&ClassTypicalSpell{},
|
||||
&AuditLogEntry{},
|
||||
&SkillImprovementCost2{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -306,7 +306,7 @@ func TestLearningMigrateStructure_VerifyStructures(t *testing.T) {
|
||||
&ClassSpellSchoolEPCost{},
|
||||
&SpellLevelLECost{},
|
||||
&SkillCategoryDifficulty{},
|
||||
&SkillImprovementCost{},
|
||||
&SkillImprovementCost2{},
|
||||
&AuditLogEntry{},
|
||||
}
|
||||
|
||||
|
||||
@@ -132,6 +132,7 @@ type WeaponSkillCategoryDifficulty struct {
|
||||
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"`
|
||||
@@ -139,6 +140,17 @@ type SkillImprovementCost struct {
|
||||
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"`
|
||||
CategoryID uint `gorm:"column:skill_category_id;not null;index;default:1" json:"skillCategoryId"` // SkillCategoryID
|
||||
DifficultyID uint `gorm:"column:skill_difficulty_id;not null;index;default:1" json:"skillDifficultyId"` // SkillDifficultyID
|
||||
}
|
||||
*/
|
||||
// SkillImprovementCost definiert TE-Kosten für Verbesserungen basierend auf Kategorie, Schwierigkeit und aktuellem Wert
|
||||
type SkillImprovementCost2 struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
CurrentLevel int `gorm:"not null;index" json:"current_level"` // Aktueller Fertigkeitswert
|
||||
TERequired int `gorm:"not null" json:"te_required"` // Benötigte Trainingseinheiten
|
||||
CategoryID uint `gorm:"column:skill_category_id;not null;index;default:1" json:"skillCategoryId"` // SkillCategoryID
|
||||
DifficultyID uint `gorm:"column:skill_difficulty_id;not null;index;default:1" json:"skillDifficultyId"` // SkillDifficultyID
|
||||
}
|
||||
|
||||
// Für komplexere Queries: View oder Helper-Strukturen
|
||||
@@ -151,7 +163,7 @@ type SkillLearningInfo struct {
|
||||
CategoryName string `json:"category_name"`
|
||||
DifficultyID uint `json:"difficulty_id"`
|
||||
DifficultyName string `json:"difficulty_name"`
|
||||
LearnCost int `json:"learn_cost"` // LE-Kosten für das Erlernen
|
||||
LearnCost int `json:"learn_cost"` // LE-Kosten / TE-Kosten für das Erlernen
|
||||
CharacterClassID uint `json:"character_class_id"`
|
||||
ClassCode string `json:"class_code"`
|
||||
ClassName string `json:"class_name"`
|
||||
@@ -233,9 +245,15 @@ func (WeaponSkillCategoryDifficulty) TableName() string {
|
||||
return "learning_weaponskill_category_difficulties"
|
||||
}
|
||||
|
||||
/*
|
||||
func (SkillImprovementCost) TableName() string {
|
||||
return "learning_skill_improvement_costs"
|
||||
}
|
||||
*/
|
||||
|
||||
func (SkillImprovementCost2) TableName() string {
|
||||
return "skill_improvement_cost2"
|
||||
}
|
||||
|
||||
func (sllc *SpellLevelLECost) ensureGameSystem() {
|
||||
gs := GetGameSystem(sllc.GameSystemId, sllc.GameSystem)
|
||||
@@ -476,9 +494,15 @@ func (wscd *WeaponSkillCategoryDifficulty) Create() error {
|
||||
return database.DB.Create(wscd).Error
|
||||
}
|
||||
|
||||
/*
|
||||
func (sic *SkillImprovementCost) Create() error {
|
||||
return database.DB.Create(sic).Error
|
||||
}
|
||||
*/
|
||||
|
||||
func (sic *SkillImprovementCost2) Create() error {
|
||||
return database.DB.Create(sic).Error
|
||||
}
|
||||
|
||||
// Komplexere Query-Methoden
|
||||
|
||||
@@ -630,14 +654,19 @@ func GetSpellLearningInfoNewSystem(spellName string, classCode string) (*SpellLe
|
||||
|
||||
// GetImprovementCost holt die Verbesserungskosten für eine Fertigkeit
|
||||
func GetImprovementCost(skillName string, categoryName string, difficultyName string, currentLevel int) (int, error) {
|
||||
var result SkillImprovementCost
|
||||
var result SkillImprovementCost2
|
||||
|
||||
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
|
||||
FROM skill_improvement_cost2 sic
|
||||
JOIN learning_skill_categories lc ON lc.id = sic.skill_category_id
|
||||
JOIN learning_skill_difficulties ld ON ld.id = sic.skill_difficulty_id
|
||||
JOIN learning_skill_category_difficulties scd ON scd.skill_category = lc.name AND scd.skill_difficulty = ld.name
|
||||
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 = ?
|
||||
WHERE s.name = ?
|
||||
AND lc.name = ?
|
||||
AND ld.name = ?
|
||||
AND sic.current_level = ?
|
||||
`, skillName, categoryName, difficultyName, currentLevel).Scan(&result).Error
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -989,7 +989,7 @@ func TestLearningCostsWorkflow_CompleteDataValidation(t *testing.T) {
|
||||
assert.Equal(t, "leicht", skillInfo.DifficultyName)
|
||||
assert.Equal(t, "Bb", skillInfo.ClassCode)
|
||||
assert.Equal(t, 10, skillInfo.EPPerTE, "EP per TE should match the class/category cost")
|
||||
assert.Equal(t, 5, skillInfo.LearnCost, "Learn cost should be 5 for leicht difficulty")
|
||||
assert.Equal(t, 1, skillInfo.LearnCost, "Learn cost should be 5 for leicht difficulty")
|
||||
|
||||
// 3. Verify active source codes include expected values
|
||||
sourceCodes, err := GetActiveSourceCodes()
|
||||
|
||||
@@ -64,7 +64,7 @@ type DatabaseExport struct {
|
||||
ClassSpellSchoolEPCosts []models.ClassSpellSchoolEPCost `json:"learning_class_spell_school_ep_costs"`
|
||||
SpellLevelLECosts []models.SpellLevelLECost `json:"learning_spell_level_le_costs"`
|
||||
SkillCategoryDifficulties []models.SkillCategoryDifficulty `json:"learning_skill_category_difficulties"`
|
||||
SkillImprovementCosts []models.SkillImprovementCost `json:"learning_skill_improvement_costs"`
|
||||
SkillImprovementCosts []models.SkillImprovementCost2 `json:"learning_skill_improvement_costs"`
|
||||
AuditLogEntries []models.AuditLogEntry `json:"audit_log_entries"`
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ func TestImportDatabaseHandler_Success(t *testing.T) {
|
||||
|
||||
assert.Equal(t, "Database imported successfully", response["message"])
|
||||
assert.Greater(t, response["record_count"], float64(0))
|
||||
assert.Equal(t, response["record_count"], exportResult)
|
||||
assert.Equal(t, float64(exportResult.RecordCount), response["record_count"])
|
||||
}
|
||||
|
||||
func TestImportDatabaseHandler_MissingFilepath(t *testing.T) {
|
||||
|
||||
@@ -31,7 +31,7 @@ type LearningDataExport struct {
|
||||
ClassSpellSchoolEPCosts []models.ClassSpellSchoolEPCost `json:"class_spell_school_ep_costs"`
|
||||
SpellLevelLECosts []models.SpellLevelLECost `json:"spell_level_le_costs"`
|
||||
SkillCategoryDifficulties []models.SkillCategoryDifficulty `json:"skill_category_difficulties"`
|
||||
SkillImprovementCosts []models.SkillImprovementCost `json:"skill_improvement_costs"`
|
||||
SkillImprovementCosts []models.SkillImprovementCost2 `json:"skill_improvement_costs"`
|
||||
}
|
||||
|
||||
// ExportCharacter exports a complete character with all related data
|
||||
@@ -177,7 +177,7 @@ func ExportCharacter(characterID uint) (*CharacterExport, error) {
|
||||
ClassSpellSchoolEPCosts: make([]models.ClassSpellSchoolEPCost, 0),
|
||||
SpellLevelLECosts: make([]models.SpellLevelLECost, 0),
|
||||
SkillCategoryDifficulties: make([]models.SkillCategoryDifficulty, 0),
|
||||
SkillImprovementCosts: make([]models.SkillImprovementCost, 0),
|
||||
SkillImprovementCosts: make([]models.SkillImprovementCost2, 0),
|
||||
}
|
||||
|
||||
database.DB.Find(&export.LearningData.Sources)
|
||||
@@ -189,7 +189,7 @@ func ExportCharacter(characterID uint) (*CharacterExport, error) {
|
||||
database.DB.Preload("CharacterClass").Preload("SpellSchool").Find(&export.LearningData.ClassSpellSchoolEPCosts)
|
||||
database.DB.Find(&export.LearningData.SpellLevelLECosts)
|
||||
database.DB.Preload("Skill").Preload("SkillCategory").Preload("SkillDifficulty").Find(&export.LearningData.SkillCategoryDifficulties)
|
||||
database.DB.Preload("SkillCategoryDifficulty").Find(&export.LearningData.SkillImprovementCosts)
|
||||
database.DB.Find(&export.LearningData.SkillImprovementCosts)
|
||||
|
||||
// Load audit log entries
|
||||
export.AuditLogEntries = make([]models.AuditLogEntry, 0)
|
||||
|
||||
@@ -170,6 +170,11 @@ func importOrUpdateSkill(tx *gorm.DB, skill *models.Skill) error {
|
||||
skill.SourceID = 1
|
||||
}
|
||||
|
||||
// Ensure game system fields are populated so queries match existing records
|
||||
gs := models.GetGameSystem(skill.GameSystemId, skill.GameSystem)
|
||||
skill.GameSystem = gs.Name
|
||||
skill.GameSystemId = gs.ID
|
||||
|
||||
var existing models.Skill
|
||||
err := tx.Where("name = ? AND game_system = ?", skill.Name, skill.GameSystem).First(&existing).Error
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Environment variables for Bamort development environment
|
||||
|
||||
# API Configuration
|
||||
# API_URL=http://localhost:8180
|
||||
|
||||
# Database Configuration (for development)
|
||||
DATABASE_TYPE=mysql
|
||||
DATABASE_URL=bamort:bG4)efozrc@tcp(mariadb-dev:3306)/bamort?charset=utf8mb4&parseTime=True&loc=Local
|
||||
|
||||
# MariaDB Configuration (development)
|
||||
MARIADB_ROOT_PASSWORD=root_password_dev
|
||||
MARIADB_PASSWORD=bG4)efozrc
|
||||
MARIADB_DATABASE=bamort
|
||||
MARIADB_USER=bamort
|
||||
|
||||
# Frontend Configuration
|
||||
API_URL=http://192.168.0.36:8180
|
||||
VITE_API_URL=http://192.168.0.36:8180
|
||||
API_PORT=8180
|
||||
BASE_URL=http://localhost:5173
|
||||
TEMPLATES_DIR=./templates
|
||||
EXPORT_TEMP_DIR=./export_temp
|
||||
GIT_COMMIT=d0c177b
|
||||
LOG_LEVEL=debug
|
||||
COMPOSE_PROJECT_NAME=bamort
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user