move classmapping to DB

LearningPointsData transfered to DataBase
This commit is contained in:
2026-01-12 16:36:35 +01:00
parent 1a7d8af65c
commit f83faad432
7 changed files with 673 additions and 276 deletions
+60 -273
View File
@@ -3080,289 +3080,76 @@ func GetCharacterClassLearningPoints(c *gin.Context) {
// getLearningPointsForClass gibt die Lernpunkte-Daten für eine bestimmte Charakterklasse zurück
func getLearningPointsForClass(className string, stand string) (*LearningPointsData, error) {
// Mapping der Klassennamen zu Codes (falls notwendig)
classMapping := map[string]string{
"Assassine": "As",
"Barbar": "Bb",
"Glücksritter": "Gl",
"Händler": "Hä",
"Krieger": "Kr",
"Spitzbube": "Sp",
"Waldläufer": "Wa",
"Barde": "Ba",
"Ordenskrieger": "Or",
"Druide": "Dr",
"Hexer": "Hx",
"Magier": "Ma",
"Priester Beschützer": "PB",
"Priester Streiter": "PS",
"Schamane": "Sc",
// Get character class from database by name or code
var charClass models.CharacterClass
result := database.DB.Where("name = ? OR code = ?", className, className).First(&charClass)
if result.Error != nil {
return nil, fmt.Errorf("character class not found: %s", className)
}
classCode := classMapping[className]
if classCode == "" {
classCode = className // Falls der Name bereits ein Code ist
// Get learning points from database
learningPoints, err := models.GetLearningPointsForClass(charClass.ID)
if err != nil {
return nil, fmt.Errorf("failed to get learning points: %w", err)
}
// Definiere die Lernpunkte-Daten basierend auf Lerntabelle_Erstellung.md
var data *LearningPointsData
// Convert to map format
learningPointsMap := make(map[string]int)
for _, lp := range learningPoints {
learningPointsMap[lp.SkillCategory.Name] = lp.Points
}
switch classCode {
case "As", "Assassine":
data = &LearningPointsData{
ClassName: "Assassine",
ClassCode: "As",
LearningPoints: map[string]int{
"Alltag": 1,
"Halbwelt": 2,
"Sozial": 4,
"Unterwelt": 8,
"Waffen": 24,
},
//WeaponPoints: 24,
TypicalSkills: []TypicalSkill{
{Name: "Meucheln", Bonus: 8, Attribute: "Gs", Notes: ""},
},
// Get spell points if applicable
spellPoints, err := models.GetSpellPointsForClass(charClass.ID)
if err != nil {
return nil, fmt.Errorf("failed to get spell points: %w", err)
}
// Get typical skills
typicalSkillsDB, err := models.GetTypicalSkillsForClass(charClass.ID)
if err != nil {
return nil, fmt.Errorf("failed to get typical skills: %w", err)
}
// Convert typical skills
typicalSkills := make([]TypicalSkill, len(typicalSkillsDB))
for i, ts := range typicalSkillsDB {
typicalSkills[i] = TypicalSkill{
Name: ts.Skill.Name,
Bonus: ts.Bonus,
Attribute: ts.Attribute,
Notes: ts.Notes,
}
case "Bb", "Barbar":
data = &LearningPointsData{
ClassName: "Barbar",
ClassCode: "Bb",
LearningPoints: map[string]int{
"Alltag": 2,
"Freiland": 4,
"Kampf": 1,
"Körper": 2,
"Waffen": 24,
},
//WeaponPoints: 24,
TypicalSkills: []TypicalSkill{
{Name: "Spurensuche", Bonus: 8, Attribute: "In", Notes: "in Heimatlandschaft"},
{Name: "Überleben", Bonus: 8, Attribute: "In", Notes: "in Heimatlandschaft"},
},
}
// Get typical spells
typicalSpellsDB, err := models.GetTypicalSpellsForClass(charClass.ID)
if err != nil {
return nil, fmt.Errorf("failed to get typical spells: %w", err)
}
// Convert typical spells
typicalSpells := make([]string, len(typicalSpellsDB))
for i, ts := range typicalSpellsDB {
spellName := ts.Spell.Name
if ts.Notes != "" {
spellName = ts.Notes // Use notes for special cases like "beliebig außer..."
}
case "Gl", "Glücksritter":
data = &LearningPointsData{
ClassName: "Glücksritter",
ClassCode: "Gl",
LearningPoints: map[string]int{
"Alltag": 2,
"Halbwelt": 3,
"Sozial": 8,
"Waffen": 24,
},
//WeaponPoints: 24,
TypicalSkills: []TypicalSkill{
{Name: "Fechten", Bonus: 5, Attribute: "Gs", Notes: "oder beidhändiger Kampf+5 (Gs)"},
},
}
case "Hä", "Händler":
data = &LearningPointsData{
ClassName: "Händler",
ClassCode: "Hä",
LearningPoints: map[string]int{
"Alltag": 4,
"Sozial": 8,
"Wissen": 4,
"Waffen": 20,
},
//WeaponPoints: 20,
TypicalSkills: []TypicalSkill{
{Name: "Geschäftssinn", Bonus: 8, Attribute: "In", Notes: ""},
},
}
case "Kr", "Krieger":
data = &LearningPointsData{
ClassName: "Krieger",
ClassCode: "Kr",
LearningPoints: map[string]int{
"Alltag": 2,
"Kampf": 3,
"Körper": 1,
"Waffen": 36,
},
//WeaponPoints: 36,
TypicalSkills: []TypicalSkill{
{Name: "Kampf in Vollrüstung", Bonus: 5, Attribute: "St", Notes: ""},
},
}
case "Sp", "Spitzbube":
data = &LearningPointsData{
ClassName: "Spitzbube",
ClassCode: "Sp",
LearningPoints: map[string]int{
"Alltag": 2,
"Halbwelt": 6,
"Unterwelt": 12,
"Waffen": 20,
},
//WeaponPoints: 20,
TypicalSkills: []TypicalSkill{
{Name: "Fallenmechanik", Bonus: 8, Attribute: "Gs", Notes: "oder Geschäftssinn+8 (In)"},
},
}
case "Wa", "Waldläufer":
data = &LearningPointsData{
ClassName: "Waldläufer",
ClassCode: "Wa",
LearningPoints: map[string]int{
"Alltag": 1,
"Freiland": 11,
"Körper": 4,
"Waffen": 20,
},
//WeaponPoints: 20,
TypicalSkills: []TypicalSkill{
{Name: "Scharfschießen", Bonus: 5, Attribute: "Gs", Notes: ""},
},
}
case "Ba", "Barde":
data = &LearningPointsData{
ClassName: "Barde",
ClassCode: "Ba",
LearningPoints: map[string]int{
"Alltag": 2,
"Sozial": 4,
"Wissen": 4,
"Waffen": 16,
},
//WeaponPoints: 16,
SpellPoints: 3,
TypicalSkills: []TypicalSkill{
{Name: "Musizieren", Bonus: 12, Attribute: "Gs", Notes: ""},
{Name: "Landeskunde", Bonus: 8, Attribute: "In", Notes: "für Heimat"},
},
TypicalSpells: []string{"Zauberlieder"},
}
case "Or", "Ordenskrieger":
data = &LearningPointsData{
ClassName: "Ordenskrieger",
ClassCode: "Or",
LearningPoints: map[string]int{
"Alltag": 2,
"Kampf": 3,
"Wissen": 2,
"Waffen": 18,
},
//WeaponPoints: 18,
SpellPoints: 3,
TypicalSkills: []TypicalSkill{
{Name: "Athletik", Bonus: 8, Attribute: "St", Notes: "oder Meditieren+8 (Wk)"},
},
TypicalSpells: []string{"Wundertaten"},
}
case "Dr", "Druide":
data = &LearningPointsData{
ClassName: "Druide",
ClassCode: "Dr",
LearningPoints: map[string]int{
"Alltag": 2,
"Freiland": 4,
"Wissen": 2,
"Waffen": 6,
},
//WeaponPoints: 6,
SpellPoints: 5,
TypicalSkills: []TypicalSkill{
{Name: "Pflanzenkunde", Bonus: 8, Attribute: "In", Notes: ""},
{Name: "Schreiben", Bonus: 12, Attribute: "In", Notes: "für Ogam-Zeichen"},
},
TypicalSpells: []string{"Dweomer", "Tiere rufen"},
}
case "Hx", "Hexer":
data = &LearningPointsData{
ClassName: "Hexer",
ClassCode: "Hx",
LearningPoints: map[string]int{
"Alltag": 3,
"Sozial": 2,
"Wissen": 2,
"Waffen": 2,
},
//WeaponPoints: 2,
SpellPoints: 6,
TypicalSkills: []TypicalSkill{
{Name: "Gassenwissen", Bonus: 8, Attribute: "In", Notes: "oder Verführen+8 (pA)"},
},
TypicalSpells: []string{"Beherrschen", "Verändern", "Verwünschen", "Binden des Vertrauten"},
}
case "Ma", "Magier":
data = &LearningPointsData{
ClassName: "Magier",
ClassCode: "Ma",
LearningPoints: map[string]int{
"Alltag": 1,
"Wissen": 5,
"Waffen": 2,
},
//WeaponPoints: 2,
SpellPoints: 7,
TypicalSkills: []TypicalSkill{
{Name: "Zauberkunde", Bonus: 8, Attribute: "In", Notes: ""},
{Name: "Schreiben", Bonus: 12, Attribute: "In", Notes: "für Muttersprache"},
},
TypicalSpells: []string{"beliebig außer Dweomer, Wundertaten, Zauberlieder", "Erkennen von Zauberei"},
}
case "PB", "Priester Beschützer":
data = &LearningPointsData{
ClassName: "Priester Beschützer",
ClassCode: "PB",
LearningPoints: map[string]int{
"Alltag": 2,
"Sozial": 2,
"Wissen": 3,
"Waffen": 6,
},
//WeaponPoints: 6,
SpellPoints: 5,
TypicalSkills: []TypicalSkill{
{Name: "Menschenkenntnis", Bonus: 8, Attribute: "In", Notes: ""},
{Name: "Schreiben", Bonus: 12, Attribute: "In", Notes: "für Muttersprache"},
},
TypicalSpells: []string{"Wundertaten", "Heilen von Wunden"},
}
case "PS", "Priester Streiter":
data = &LearningPointsData{
ClassName: "Priester Streiter",
ClassCode: "PS",
LearningPoints: map[string]int{
"Alltag": 3,
"Kampf": 2,
"Wissen": 2,
"Waffen": 8,
},
//WeaponPoints: 8,
SpellPoints: 5,
TypicalSkills: []TypicalSkill{
{Name: "Erste Hilfe", Bonus: 8, Attribute: "Gs", Notes: ""},
{Name: "Schreiben", Bonus: 12, Attribute: "In", Notes: "für Muttersprache"},
},
TypicalSpells: []string{"Wundertaten", "Bannen von Finsterwerk", "Strahlender Panzer"},
}
case "Sc", "Schamane":
data = &LearningPointsData{
ClassName: "Schamane",
ClassCode: "Sc",
LearningPoints: map[string]int{
"Alltag": 2,
"Körper": 4,
"Wissen": 2,
"Waffen": 6,
},
//WeaponPoints: 6,
SpellPoints: 5,
TypicalSkills: []TypicalSkill{
{Name: "Tierkunde", Bonus: 8, Attribute: "In", Notes: ""},
{Name: "Überleben", Bonus: 8, Attribute: "In", Notes: "in Heimatlandschaft"},
},
TypicalSpells: []string{"Dweomer", "Wundertaten", "Austreibung des Bösen", "Bannen von Gift"},
}
default:
return nil, fmt.Errorf("unbekannte Charakterklasse: %s", className)
typicalSpells[i] = spellName
}
// Build the response data
data := &LearningPointsData{
ClassName: charClass.Name,
ClassCode: charClass.Code,
LearningPoints: learningPointsMap,
SpellPoints: spellPoints.SpellPoints,
TypicalSkills: typicalSkills,
TypicalSpells: typicalSpells,
}
// Bonus-Lernpunkte basierend auf Stand hinzufügen
if stand != "" && data != nil {
if stand != "" {
standBonus := getStandBonusPoints(stand)
// Füge die Stand-Bonuspunkte zu den normalen Lernpunkten hinzu
for category, bonus := range standBonus {
+44 -2
View File
@@ -1,6 +1,8 @@
package character
import (
"bamort/database"
"bamort/models"
"encoding/json"
"net/http"
"net/http/httptest"
@@ -11,6 +13,20 @@ import (
)
func TestGetCharacterClassLearningPoints(t *testing.T) {
// Setup test database
database.SetupTestDB()
// Migrate the new structures
if err := models.MigrateStructure(database.DB); err != nil {
t.Fatalf("Failed to migrate structures: %v", err)
}
// Populate test data (character classes and learning points)
if err := models.PopulateClassLearningPointsData(); err != nil {
t.Logf("Warning: Failed to populate learning points data: %v", err)
// Continue anyway - some tests may still work
}
// Setup Gin in test mode
gin.SetMode(gin.TestMode)
router := gin.New()
@@ -169,9 +185,9 @@ func TestGetCharacterClassLearningPoints(t *testing.T) {
assert.NotNil(t, response.LearningPoints)
assert.NotNil(t, response.TypicalSkills)
// Check weapon points if specified
// Check weapon points if specified (now in LearningPoints["Waffen"])
if tt.checkWeapons {
assert.Equal(t, tt.expectedWeapons, response.WeaponPoints)
assert.Equal(t, tt.expectedWeapons, response.LearningPoints["Waffen"], "Weapon learning points should match")
}
// Check spell points if specified
@@ -191,6 +207,19 @@ func TestGetCharacterClassLearningPoints(t *testing.T) {
}
func TestGetLearningPointsForClass(t *testing.T) {
// Setup test database
database.SetupTestDB()
// Migrate the new structures
if err := models.MigrateStructure(database.DB); err != nil {
t.Fatalf("Failed to migrate structures: %v", err)
}
// Populate test data
if err := models.PopulateClassLearningPointsData(); err != nil {
t.Logf("Warning: Failed to populate learning points data: %v", err)
}
tests := []struct {
name string
className string
@@ -354,6 +383,19 @@ func TestGetStandBonusPoints(t *testing.T) {
// Test all character classes to ensure they're properly defined
func TestAllCharacterClassesAreDefined(t *testing.T) {
// Setup test database
database.SetupTestDB()
// Migrate the new structures
if err := models.MigrateStructure(database.DB); err != nil {
t.Fatalf("Failed to migrate structures: %v", err)
}
// Populate test data
if err := models.PopulateClassLearningPointsData(); err != nil {
t.Logf("Warning: Failed to populate learning points data: %v", err)
}
expectedClasses := []string{
"Assassine", "Barbar", "Glücksritter", "Händler", "Krieger", "Spitzbube", "Waldläufer",
"Barde", "Ordenskrieger", "Druide", "Hexer", "Magier", "Priester Beschützer", "Priester Streiter", "Schamane",
+52
View File
@@ -219,6 +219,10 @@ func copyMariaDBToSQLite(mariaDB, sqliteDB *gorm.DB) error {
&models.SkillCategoryDifficulty{},
&models.WeaponSkillCategoryDifficulty{},
&models.SkillImprovementCost{},
&models.ClassLearningPoints{},
&models.ClassSpellPoints{},
&models.ClassTypicalSkill{},
&models.ClassTypicalSpell{},
// GSMaster Basis-Daten
//&models.LookupList{}, // Basis für Skills, Spells, Equipment
@@ -561,6 +565,22 @@ func SetupCheckDev(c *gin.Context) {
logger.Info("Setup-Check erfolgreich abgeschlossen")
c.JSON(http.StatusOK, gin.H{"message": "Setup Check OK"})
}
// PopulateClassLearningPoints populates the class learning points tables from hardcoded data
func PopulateClassLearningPoints(c *gin.Context) {
logger.Info("Starte Population der Class Learning Points Daten...")
err := models.PopulateClassLearningPointsData()
if err != nil {
logger.Error("Fehler beim Populieren der Class Learning Points: %s", err.Error())
respondWithError(c, http.StatusInternalServerError, "Failed to populate class learning points: "+err.Error())
return
}
logger.Info("Class Learning Points erfolgreich populiert")
c.JSON(http.StatusOK, gin.H{"message": "Class learning points data populated successfully"})
}
func ReconnectDataBase(c *gin.Context) {
logger.Info("Führe Datenbank-Reconnect durch...")
@@ -735,6 +755,10 @@ func copySQLiteToMariaDB(sqliteDB, mariaDB *gorm.DB) error {
&models.SkillCategoryDifficulty{}, // Jetzt nach Skills
&models.WeaponSkillCategoryDifficulty{},
&models.SkillImprovementCost{},
&models.ClassLearningPoints{},
&models.ClassSpellPoints{},
&models.ClassTypicalSkill{},
&models.ClassTypicalSpell{},
// Charaktere (Basis)
&models.Char{},
@@ -889,6 +913,30 @@ func copyTableDataReverse(sourceDB, targetDB *gorm.DB, model interface{}) error
return fmt.Errorf("failed to read batch from source: %w", err)
}
records = batch
case *models.ClassLearningPoints:
var batch []models.ClassLearningPoints
if err := sourceDB.Limit(batchSize).Offset(offset).Find(&batch).Error; err != nil {
return fmt.Errorf("failed to read batch from source: %w", err)
}
records = batch
case *models.ClassSpellPoints:
var batch []models.ClassSpellPoints
if err := sourceDB.Limit(batchSize).Offset(offset).Find(&batch).Error; err != nil {
return fmt.Errorf("failed to read batch from source: %w", err)
}
records = batch
case *models.ClassTypicalSkill:
var batch []models.ClassTypicalSkill
if err := sourceDB.Limit(batchSize).Offset(offset).Find(&batch).Error; err != nil {
return fmt.Errorf("failed to read batch from source: %w", err)
}
records = batch
case *models.ClassTypicalSpell:
var batch []models.ClassTypicalSpell
if err := sourceDB.Limit(batchSize).Offset(offset).Find(&batch).Error; err != nil {
return fmt.Errorf("failed to read batch from source: %w", err)
}
records = batch
case *models.Skill:
var batch []models.Skill
if err := sourceDB.Limit(batchSize).Offset(offset).Find(&batch).Error; err != nil {
@@ -1104,6 +1152,10 @@ func clearMariaDBData(db *gorm.DB) error {
&models.SpellLevelLECost{},
&models.ClassSpellSchoolEPCost{},
&models.ClassCategoryEPCost{},
&models.ClassTypicalSpell{},
&models.ClassTypicalSkill{},
&models.ClassSpellPoints{},
&models.ClassLearningPoints{},
// GSMaster Basis-Daten
&models.Believe{},
+2 -1
View File
@@ -15,7 +15,8 @@ func RegisterRoutes(r *gin.RouterGroup) {
charGrp.GET("/mktestdata", MakeTestdataFromLive)
charGrp.GET("/reconndb", ReconnectDataBase) // Datenbank neu verbinden
charGrp.GET("/reloadenv", ReloadENV)
charGrp.POST("/transfer-sqlite-to-mariadb", TransferSQLiteToMariaDB) // Transfer data from SQLite to MariaDB
charGrp.POST("/transfer-sqlite-to-mariadb", TransferSQLiteToMariaDB) // Transfer data from SQLite to MariaDB
charGrp.POST("/populate-class-learning-points", PopulateClassLearningPoints) // Populate class learning points from hardcoded data
/*
//nur zur einmaligen Ausführung, um das Lernkosten-System zu initialisieren
charGrp.POST("/initialize-learning-costs", InitializeLearningCosts)
+4
View File
@@ -157,6 +157,10 @@ func learningMigrateStructure(db ...*gorm.DB) error {
&SkillCategoryDifficulty{},
&WeaponSkillCategoryDifficulty{},
&SkillImprovementCost{},
&ClassLearningPoints{},
&ClassSpellPoints{},
&ClassTypicalSkill{},
&ClassTypicalSpell{},
&AuditLogEntry{},
)
if err != nil {
@@ -0,0 +1,101 @@
package models
import (
"bamort/database"
"gorm.io/gorm"
)
// ClassLearningPoints stores the learning points distribution for a character class
type ClassLearningPoints struct {
ID uint `gorm:"primaryKey" json:"id"`
CharacterClassID uint `gorm:"uniqueIndex:idx_class_category;not null" json:"character_class_id"`
SkillCategoryID uint `gorm:"uniqueIndex:idx_class_category;not null" json:"skill_category_id"`
Points int `gorm:"not null;default:0" json:"points"`
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"`
}
// ClassSpellPoints stores the spell learning points for a character class
type ClassSpellPoints struct {
ID uint `gorm:"primaryKey" json:"id"`
CharacterClassID uint `gorm:"uniqueIndex;not null" json:"character_class_id"`
SpellPoints int `gorm:"not null;default:0" json:"spell_points"` // Zauberlerneinheiten
CharacterClass CharacterClass `gorm:"foreignKey:CharacterClassID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"character_class"`
}
// ClassTypicalSkill stores typical skills for a character class with bonuses
type ClassTypicalSkill struct {
ID uint `gorm:"primaryKey" json:"id"`
CharacterClassID uint `gorm:"not null;index" json:"character_class_id"`
SkillID uint `gorm:"not null;index" json:"skill_id"`
Bonus int `gorm:"not null;default:0" json:"bonus"`
Attribute string `gorm:"size:10" json:"attribute,omitempty"` // z.B. "Gs", "In", "St"
Notes string `json:"notes,omitempty"` // Zusätzliche Notizen
CharacterClass CharacterClass `gorm:"foreignKey:CharacterClassID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"character_class"`
Skill Skill `gorm:"foreignKey:SkillID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"skill"`
}
// ClassTypicalSpell stores typical spells for a character class
type ClassTypicalSpell struct {
ID uint `gorm:"primaryKey" json:"id"`
CharacterClassID uint `gorm:"not null;index" json:"character_class_id"`
SpellID uint `gorm:"not null;index" json:"spell_id"`
Notes string `json:"notes,omitempty"` // z.B. "beliebig außer Dweomer"
CharacterClass CharacterClass `gorm:"foreignKey:CharacterClassID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"character_class"`
Spell Spell `gorm:"foreignKey:SpellID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"spell"`
}
// TableName specifies the table name for ClassLearningPoints
func (ClassLearningPoints) TableName() string {
return "class_learning_points"
}
// TableName specifies the table name for ClassSpellPoints
func (ClassSpellPoints) TableName() string {
return "class_spell_points"
}
// TableName specifies the table name for ClassTypicalSkill
func (ClassTypicalSkill) TableName() string {
return "class_typical_skills"
}
// TableName specifies the table name for ClassTypicalSpell
func (ClassTypicalSpell) TableName() string {
return "class_typical_spells"
}
// GetLearningPointsForClass retrieves all learning points for a character class
func GetLearningPointsForClass(classID uint) ([]ClassLearningPoints, error) {
var points []ClassLearningPoints
err := database.DB.Preload("SkillCategory").Where("character_class_id = ?", classID).Find(&points).Error
return points, err
}
// GetSpellPointsForClass retrieves spell points for a character class
func GetSpellPointsForClass(classID uint) (*ClassSpellPoints, error) {
var points ClassSpellPoints
err := database.DB.Where("character_class_id = ?", classID).First(&points).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return &ClassSpellPoints{SpellPoints: 0}, nil
}
return nil, err
}
return &points, nil
}
// GetTypicalSkillsForClass retrieves typical skills for a character class
func GetTypicalSkillsForClass(classID uint) ([]ClassTypicalSkill, error) {
var skills []ClassTypicalSkill
err := database.DB.Preload("Skill").Where("character_class_id = ?", classID).Find(&skills).Error
return skills, err
}
// GetTypicalSpellsForClass retrieves typical spells for a character class
func GetTypicalSpellsForClass(classID uint) ([]ClassTypicalSpell, error) {
var spells []ClassTypicalSpell
err := database.DB.Preload("Spell").Where("character_class_id = ?", classID).Find(&spells).Error
return spells, err
}
@@ -0,0 +1,410 @@
package models
import (
"bamort/database"
"fmt"
)
// PopulateClassLearningPointsData inserts all hardcoded learning points data into the database
func PopulateClassLearningPointsData() error {
// Define all character class learning data
classData := []struct {
ClassCode string
ClassName string
LearningPoints map[string]int
SpellPoints int
TypicalSkills []struct {
Name string
Bonus int
Attribute string
Notes string
}
TypicalSpells []string
}{
{
ClassCode: "As",
ClassName: "Assassine",
LearningPoints: map[string]int{
"Alltag": 1,
"Halbwelt": 2,
"Sozial": 4,
"Unterwelt": 8,
"Waffen": 24,
},
TypicalSkills: []struct {
Name string
Bonus int
Attribute string
Notes string
}{
{Name: "Meucheln", Bonus: 8, Attribute: "Gs", Notes: ""},
},
},
{
ClassCode: "Bb",
ClassName: "Barbar",
LearningPoints: map[string]int{
"Alltag": 2,
"Freiland": 4,
"Kampf": 1,
"Körper": 2,
"Waffen": 24,
},
TypicalSkills: []struct {
Name string
Bonus int
Attribute string
Notes string
}{
{Name: "Spurensuche", Bonus: 8, Attribute: "In", Notes: "in Heimatlandschaft"},
{Name: "Überleben", Bonus: 8, Attribute: "In", Notes: "in Heimatlandschaft"},
},
},
{
ClassCode: "Gl",
ClassName: "Glücksritter",
LearningPoints: map[string]int{
"Alltag": 2,
"Halbwelt": 3,
"Sozial": 8,
"Waffen": 24,
},
TypicalSkills: []struct {
Name string
Bonus int
Attribute string
Notes string
}{
{Name: "Fechten", Bonus: 5, Attribute: "Gs", Notes: "oder beidhändiger Kampf+5 (Gs)"},
},
},
{
ClassCode: "Hä",
ClassName: "Händler",
LearningPoints: map[string]int{
"Alltag": 4,
"Sozial": 8,
"Wissen": 4,
"Waffen": 20,
},
TypicalSkills: []struct {
Name string
Bonus int
Attribute string
Notes string
}{
{Name: "Geschäftssinn", Bonus: 8, Attribute: "In", Notes: ""},
},
},
{
ClassCode: "Kr",
ClassName: "Krieger",
LearningPoints: map[string]int{
"Alltag": 2,
"Kampf": 3,
"Körper": 1,
"Waffen": 36,
},
TypicalSkills: []struct {
Name string
Bonus int
Attribute string
Notes string
}{
{Name: "Kampf in Vollrüstung", Bonus: 5, Attribute: "St", Notes: ""},
},
},
{
ClassCode: "Sp",
ClassName: "Spitzbube",
LearningPoints: map[string]int{
"Alltag": 2,
"Halbwelt": 6,
"Unterwelt": 12,
"Waffen": 20,
},
TypicalSkills: []struct {
Name string
Bonus int
Attribute string
Notes string
}{
{Name: "Fallenmechanik", Bonus: 8, Attribute: "Gs", Notes: "oder Geschäftssinn+8 (In)"},
},
},
{
ClassCode: "Wa",
ClassName: "Waldläufer",
LearningPoints: map[string]int{
"Alltag": 1,
"Freiland": 11,
"Körper": 4,
"Waffen": 20,
},
TypicalSkills: []struct {
Name string
Bonus int
Attribute string
Notes string
}{
{Name: "Scharfschießen", Bonus: 5, Attribute: "Gs", Notes: ""},
},
},
{
ClassCode: "Ba",
ClassName: "Barde",
SpellPoints: 3,
LearningPoints: map[string]int{
"Alltag": 2,
"Sozial": 4,
"Wissen": 4,
"Waffen": 16,
},
TypicalSkills: []struct {
Name string
Bonus int
Attribute string
Notes string
}{
{Name: "Musizieren", Bonus: 12, Attribute: "Gs", Notes: ""},
{Name: "Landeskunde", Bonus: 8, Attribute: "In", Notes: "für Heimat"},
},
TypicalSpells: []string{"Zauberlieder"},
},
{
ClassCode: "Or",
ClassName: "Ordenskrieger",
SpellPoints: 3,
LearningPoints: map[string]int{
"Alltag": 2,
"Kampf": 3,
"Wissen": 2,
"Waffen": 18,
},
TypicalSkills: []struct {
Name string
Bonus int
Attribute string
Notes string
}{
{Name: "Athletik", Bonus: 8, Attribute: "St", Notes: "oder Meditieren+8 (Wk)"},
},
TypicalSpells: []string{"Wundertaten"},
},
{
ClassCode: "Dr",
ClassName: "Druide",
SpellPoints: 5,
LearningPoints: map[string]int{
"Alltag": 2,
"Freiland": 4,
"Wissen": 2,
"Waffen": 6,
},
TypicalSkills: []struct {
Name string
Bonus int
Attribute string
Notes string
}{
{Name: "Pflanzenkunde", Bonus: 8, Attribute: "In", Notes: ""},
{Name: "Schreiben", Bonus: 12, Attribute: "In", Notes: "für Ogam-Zeichen"},
},
TypicalSpells: []string{"Dweomer", "Tiere rufen"},
},
{
ClassCode: "Hx",
ClassName: "Hexer",
SpellPoints: 6,
LearningPoints: map[string]int{
"Alltag": 3,
"Sozial": 2,
"Wissen": 2,
"Waffen": 2,
},
TypicalSkills: []struct {
Name string
Bonus int
Attribute string
Notes string
}{
{Name: "Gassenwissen", Bonus: 8, Attribute: "In", Notes: "oder Verführen+8 (pA)"},
},
TypicalSpells: []string{"Beherrschen", "Verändern", "Verwünschen", "Binden des Vertrauten"},
},
{
ClassCode: "Ma",
ClassName: "Magier",
SpellPoints: 7,
LearningPoints: map[string]int{
"Alltag": 1,
"Wissen": 5,
"Waffen": 2,
},
TypicalSkills: []struct {
Name string
Bonus int
Attribute string
Notes string
}{
{Name: "Zauberkunde", Bonus: 8, Attribute: "In", Notes: ""},
{Name: "Schreiben", Bonus: 12, Attribute: "In", Notes: "für Muttersprache"},
},
TypicalSpells: []string{"beliebig außer Dweomer, Wundertaten, Zauberlieder", "Erkennen von Zauberei"},
},
{
ClassCode: "PB",
ClassName: "Priester Beschützer",
SpellPoints: 5,
LearningPoints: map[string]int{
"Alltag": 2,
"Sozial": 2,
"Wissen": 3,
"Waffen": 6,
},
TypicalSkills: []struct {
Name string
Bonus int
Attribute string
Notes string
}{
{Name: "Menschenkenntnis", Bonus: 8, Attribute: "In", Notes: ""},
{Name: "Schreiben", Bonus: 12, Attribute: "In", Notes: "für Muttersprache"},
},
TypicalSpells: []string{"Wundertaten", "Heilen von Wunden"},
},
{
ClassCode: "PS",
ClassName: "Priester Streiter",
SpellPoints: 5,
LearningPoints: map[string]int{
"Alltag": 3,
"Kampf": 2,
"Wissen": 2,
"Waffen": 8,
},
TypicalSkills: []struct {
Name string
Bonus int
Attribute string
Notes string
}{
{Name: "Erste Hilfe", Bonus: 8, Attribute: "Gs", Notes: ""},
{Name: "Schreiben", Bonus: 12, Attribute: "In", Notes: "für Muttersprache"},
},
TypicalSpells: []string{"Wundertaten", "Bannen von Finsterwerk", "Strahlender Panzer"},
},
{
ClassCode: "Sc",
ClassName: "Schamane",
SpellPoints: 5,
LearningPoints: map[string]int{
"Alltag": 2,
"Körper": 4,
"Wissen": 2,
"Waffen": 6,
},
TypicalSkills: []struct {
Name string
Bonus int
Attribute string
Notes string
}{
{Name: "Tierkunde", Bonus: 8, Attribute: "In", Notes: ""},
{Name: "Überleben", Bonus: 8, Attribute: "In", Notes: "in Heimatlandschaft"},
},
TypicalSpells: []string{"Dweomer", "Wundertaten", "Austreibung des Bösen", "Bannen von Gift"},
},
}
// Process each character class
for _, cd := range classData {
// Find or create character class
var charClass CharacterClass
result := database.DB.Where("code = ?", cd.ClassCode).First(&charClass)
if result.Error != nil {
fmt.Printf("Warning: Character class %s (%s) not found in database, skipping\n", cd.ClassName, cd.ClassCode)
continue
}
// Insert learning points for each category
for categoryName, points := range cd.LearningPoints {
var category SkillCategory
if err := database.DB.Where("name = ?", categoryName).First(&category).Error; err != nil {
fmt.Printf("Warning: Skill category %s not found, skipping\n", categoryName)
continue
}
learningPoints := ClassLearningPoints{
CharacterClassID: charClass.ID,
SkillCategoryID: category.ID,
Points: points,
}
if err := database.DB.Where("character_class_id = ? AND skill_category_id = ?",
charClass.ID, category.ID).FirstOrCreate(&learningPoints).Error; err != nil {
return fmt.Errorf("failed to insert learning points for %s/%s: %w", cd.ClassName, categoryName, err)
}
}
// Insert spell points if applicable
if cd.SpellPoints > 0 {
spellPoints := ClassSpellPoints{
CharacterClassID: charClass.ID,
SpellPoints: cd.SpellPoints,
}
if err := database.DB.Where("character_class_id = ?", charClass.ID).
FirstOrCreate(&spellPoints).Error; err != nil {
return fmt.Errorf("failed to insert spell points for %s: %w", cd.ClassName, err)
}
}
// Insert typical skills
for _, ts := range cd.TypicalSkills {
var skill Skill
if err := database.DB.Where("name = ?", ts.Name).First(&skill).Error; err != nil {
fmt.Printf("Warning: Skill %s not found for class %s, skipping\n", ts.Name, cd.ClassName)
continue
}
typicalSkill := ClassTypicalSkill{
CharacterClassID: charClass.ID,
SkillID: skill.ID,
Bonus: ts.Bonus,
Attribute: ts.Attribute,
Notes: ts.Notes,
}
if err := database.DB.Where("character_class_id = ? AND skill_id = ?",
charClass.ID, skill.ID).FirstOrCreate(&typicalSkill).Error; err != nil {
return fmt.Errorf("failed to insert typical skill %s for %s: %w", ts.Name, cd.ClassName, err)
}
}
// Insert typical spells
for _, spellName := range cd.TypicalSpells {
var spell Spell
if err := database.DB.Where("name = ?", spellName).First(&spell).Error; err != nil {
fmt.Printf("Warning: Spell %s not found for class %s, creating reference with notes\n", spellName, cd.ClassName)
// For special cases like "beliebig außer...", we can skip or create a placeholder
continue
}
typicalSpell := ClassTypicalSpell{
CharacterClassID: charClass.ID,
SpellID: spell.ID,
Notes: "",
}
if err := database.DB.Where("character_class_id = ? AND spell_id = ?",
charClass.ID, spell.ID).FirstOrCreate(&typicalSpell).Error; err != nil {
return fmt.Errorf("failed to insert typical spell %s for %s: %w", spellName, cd.ClassName, err)
}
}
fmt.Printf("Populated learning points data for %s (%s)\n", cd.ClassName, cd.ClassCode)
}
return nil
}