Lerne Zauber

This commit is contained in:
2025-07-24 19:18:32 +02:00
parent 109a97e489
commit d220f2fd34
3 changed files with 377 additions and 9 deletions
+69 -4
View File
@@ -1,6 +1,8 @@
package gsmaster
import "fmt"
import (
"fmt"
)
// DifficultyData enthält Skills und Trainingskosten für eine Schwierigkeitsstufe
type DifficultyData struct {
@@ -15,7 +17,8 @@ type LearningCostsTable2 struct {
EPPerTE map[string]map[string]int
// EP-Kosten für 1 Lerneinheit (LE) für Zauber pro Charakterklasse und Zauberschule
SpellEPPerLE map[string]map[string]int
SpellEPPerLE map[string]map[string]int
SpellLEPerLevel map[int]int
// LE-Kosten für Fertigkeiten basierend auf Schwierigkeit
BaseLearnCost map[string]map[string]int
@@ -282,6 +285,9 @@ var learningCostsData = &LearningCostsTable2{
"Lied": 30,
},
},
// Lernen von Zaubern
// LE pro Stufe des Zaubers
SpellLEPerLevel: map[int]int{1: 1, 2: 1, 3: 2, 4: 3, 5: 5, 6: 10, 7: 15, 8: 20, 9: 30, 10: 40, 11: 60, 12: 90},
// TE-Kosten für Verbesserungen basierend auf Kategorie, Schwierigkeit und aktuellem Wert
ImprovementCost: map[string]map[string]DifficultyData{
@@ -518,6 +524,28 @@ func contains(slice []string, item string) bool {
//### End of Helper functions ###
// GetSpellInfo returns the school and level of a spell from the database
func GetSpellInfo(spellName string) (string, int, error) {
// Create a Spell instance to search in the database
var spell Spell
// Search for the spell in the database
err := spell.First(spellName)
if err != nil {
return "", 0, fmt.Errorf("spell '%s' not found in database: %w", spellName, err)
}
return spell.Category, spell.Stufe, nil
}
// GetSpecialization returns the specialization school for a character (placeholder)
// This should be implemented to get the actual specialization from character data
func GetSpecialization(characterID string) string {
// TODO: Implement actual character specialization lookup
// For now, return a default specialization
return "Beherrschen"
}
// findBestCategoryForSkillImprovement findet die Kategorie mit den niedrigsten EP-Kosten für eine Fertigkeit
func findBestCategoryForSkillImprovement(skillName, characterClass string, level int) (string, string, error) {
classKey := characterClass
@@ -713,6 +741,43 @@ func CalcSkillImproveCost(costResult *SkillCostResultNew, currentLevel int, rewa
func CalcSpellLernCost(costResult *SkillCostResultNew, reward *string) error {
// Für Zauber verwenden wir eine ähnliche Logik wie für Skills
// TODO: Implementiere spezifische Zauber-Kostenlogik wenn verfügbar
// Für jetzt verwenden wir die gleiche Logik wie für Skills
return CalcSkillLernCost(costResult, reward)
classKey := costResult.CharacterClass
spellCategory, spellLevel, err := GetSpellInfo(costResult.SkillName)
if err != nil {
return fmt.Errorf("failed to get spell info: %w", err)
}
SpellEPPerLE, ok := learningCostsData.SpellEPPerLE[classKey][spellCategory]
if !ok {
return fmt.Errorf("EP-Kosten für Zauber '%s' und Klasse '%s' nicht gefunden", costResult.SkillName, classKey)
}
if classKey == "Ma" {
spezialgebiet := GetSpecialization(costResult.CharacterID)
if spellCategory == spezialgebiet {
SpellEPPerLE = 30 // Spezialgebiet für Magier
}
}
trainCost := learningCostsData.SpellLEPerLevel[spellLevel] // LE pro Stufe des Zaubers
if costResult.PPUsed > 0 {
trainCost -= costResult.PPUsed // Wenn PP verwendet werden, setze die Kosten auf die PP
}
costResult.EP = trainCost * SpellEPPerLE // EP-Kosten für das Lernen des Zaubers
costResult.GoldCost = trainCost * 100 // Beispiel: 200 Gold pro LE
costResult.Category = spellCategory
costResult.Difficulty = fmt.Sprintf("Stufe %d", spellLevel) // Zauber haben keine Schwierigkeit, sondern eine Stufe
if reward != nil && *reward == "spruchrolle" {
costResult.GoldCost = 20 // 20 Gold für Jeden Versuch
costResult.EP = costResult.EP / 3 // 1/3 EP-Kosten bei Erfolg
} else {
if reward != nil && *reward == "halveep" {
costResult.EP = costResult.EP / 2 // Halbiere die EP-Kosten für diese Belohnung
}
if reward != nil && *reward == "halveepnoGold" {
costResult.EP = costResult.EP / 2 // Halbiere die EP-Kosten für diese Belohnung
costResult.GoldCost = 0 // Keine Goldkosten für diese Belohnung
}
}
return nil
}
+218 -5
View File
@@ -1,9 +1,15 @@
package gsmaster
import (
"bamort/database"
"testing"
)
// Helper function to create string pointers
func stringPtr(s string) *string {
return &s
}
// TestGetSkillCategory tests the GetSkillCategory function
func TestGetSkillCategory(t *testing.T) {
tests := []struct {
@@ -606,11 +612,6 @@ func TestCalcSkillLernCostWithRewards(t *testing.T) {
}
}
// Helper function to create string pointers
func stringPtr(s string) *string {
return &s
}
// TestCalcSpellLernCostWithRewards tests the reward logic in CalcSpellLernCost
/*
func TestCalcSpellLernCostWithRewards(t *testing.T) {
@@ -735,3 +736,215 @@ func TestCalcSkillImproveCostWithRewards(t *testing.T) {
})
}
}
// TestGetSpellInfo tests the GetSpellInfo function
func TestGetSpellInfo(t *testing.T) {
// Initialize test database with migration (but no test data since we don't have the preparedTestDB file)
database.SetupTestDB(true, false) // Use in-memory SQLite, no test data loading
defer database.ResetTestDB()
MigrateStructure()
// Create minimal test spell data for our test
testSpells := []Spell{
{
LookupList: LookupList{
GameSystem: "midgard",
Name: "Schlummer",
Beschreibung: "Test spell for GetSpellInfo",
Quelle: "Test",
},
Stufe: 1,
Category: "Beherrschen",
},
{
LookupList: LookupList{
GameSystem: "midgard",
Name: "Erkennen von Krankheit",
Beschreibung: "Test spell for GetSpellInfo",
Quelle: "Test",
},
Stufe: 2,
Category: "Dweomerzauber",
},
{
LookupList: LookupList{
GameSystem: "midgard",
Name: "Das Loblied",
Beschreibung: "Test spell for GetSpellInfo",
Quelle: "Test",
},
Stufe: 3,
Category: "Zauberlied",
},
}
// Insert test data directly
for _, spell := range testSpells {
if err := database.DB.Create(&spell).Error; err != nil {
t.Fatalf("Failed to create test spell: %v", err)
}
}
tests := []struct {
spellName string
expectedSchool string
expectedLevel int
expectError bool
}{
{
spellName: "Schlummer",
expectedSchool: "Beherrschen",
expectedLevel: 1,
expectError: false,
},
{
spellName: "Erkennen von Krankheit",
expectedSchool: "Dweomerzauber",
expectedLevel: 2,
expectError: false,
},
{
spellName: "Das Loblied",
expectedSchool: "Zauberlied",
expectedLevel: 3,
expectError: false,
},
{
spellName: "Unknown Spell",
expectedSchool: "", // Should error for unknown spell
expectedLevel: 0, // Should error for unknown spell
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.spellName, func(t *testing.T) {
school, level, err := GetSpellInfo(tt.spellName)
if tt.expectError {
if err == nil {
t.Errorf("Expected error for unknown spell, but got none")
}
return
}
if err != nil {
t.Fatalf("Failed to get spell info: %v", err)
}
if school != tt.expectedSchool {
t.Errorf("Expected school %s, got %s", tt.expectedSchool, school)
}
if level != tt.expectedLevel {
t.Errorf("Expected level %d, got %d", tt.expectedLevel, level)
}
})
}
}
// TestCalcSpellLernCostWithRewards tests the reward logic in CalcSpellLernCost
func TestCalcSpellLernCostWithRewards(t *testing.T) {
// Initialize test database with migration (but no test data since we don't have the preparedTestDB file)
database.SetupTestDB(true, false) // Use in-memory SQLite, no test data loading
defer database.ResetTestDB()
MigrateStructure()
// Create minimal test spell data for our test
testSpells := []Spell{
{
LookupList: LookupList{
GameSystem: "midgard",
Name: "Schlummer",
Beschreibung: "Test spell for GetSpellInfo",
Quelle: "Test",
},
Stufe: 1,
Category: "Beherrschen",
},
{
LookupList: LookupList{
GameSystem: "midgard",
Name: "Erkennen von Krankheit",
Beschreibung: "Test spell for GetSpellInfo",
Quelle: "Test",
},
Stufe: 2,
Category: "Dweomer",
},
{
LookupList: LookupList{
GameSystem: "midgard",
Name: "Das Loblied",
Beschreibung: "Test spell for GetSpellInfo",
Quelle: "Test",
},
Stufe: 3,
Category: "Zauberlied",
},
}
// Insert test data directly
for _, spell := range testSpells {
if err := database.DB.Create(&spell).Error; err != nil {
t.Fatalf("Failed to create test spell: %v", err)
}
}
tests := []struct {
name string
spellName string
characterClass string
reward *string
expectedEP int
expectedGold int
}{
{
name: "Simple spell for Magier without but specialized",
spellName: "Schlummer",
characterClass: "Ma",
reward: nil,
expectedEP: 30, // Ma has 60 EP/LE for Beherrschen, Furcht is level 1 = 1 LE, so 1*60=60
expectedGold: 100, // 1 LE * 100 Gold per LE
},
{
name: "Spell with spruchrolle no reward",
spellName: "Erkennen von Krankheit",
characterClass: "Ma",
reward: nil,
expectedEP: 120, // 60/3 for spruchrolle
expectedGold: 100, // Fixed 20 Gold for spruchrolle
},
{
name: "Spell with spruchrolle reward",
spellName: "Erkennen von Krankheit",
characterClass: "Ma",
reward: stringPtr("spruchrolle"),
expectedEP: 40, // 60/3 for spruchrolle
expectedGold: 20, // Fixed 20 Gold for spruchrolle
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
costResult := &SkillCostResultNew{
CharacterClass: tt.characterClass,
SkillName: tt.spellName,
CharacterID: "test-character",
}
err := CalcSpellLernCost(costResult, tt.reward)
if err != nil {
t.Fatalf("Failed to calculate spell costs: %v", err)
}
if costResult.EP != tt.expectedEP {
t.Errorf("Expected EP %d, got %d", tt.expectedEP, costResult.EP)
}
if costResult.GoldCost != tt.expectedGold {
t.Errorf("Expected gold cost %d, got %d", tt.expectedGold, costResult.GoldCost)
}
})
}
}
@@ -0,0 +1,90 @@
package integration_test
import (
"bamort/database"
"bamort/gsmaster"
_ "bamort/maintenance" // Import for init() function that sets up test data callbacks
"testing"
)
// TestGetSpellInfoIntegration tests the GetSpellInfo function with full database integration
func TestGetSpellInfoIntegration(t *testing.T) {
// The maintenance package init() already sets up:
// database.SetTestDataLoader(LoadPredefinedTestDataFromFile)
// database.SetMigrationCallback(migrateAllStructures)
// Initialize test database with test data loading
database.SetupTestDB(true, true) // Use in-memory SQLite with test data
defer database.ResetTestDB()
// Test with real spell names that should exist in the prepared test database
// If the prepared test database doesn't exist, these tests will fail gracefully
tests := []struct {
spellName string
expectedSchool string
expectedLevel int
expectError bool
description string
}{
{
spellName: "Licht",
expectedSchool: "Erschaffen", // Common light spell in Midgard
expectedLevel: 1,
expectError: false,
description: "Basic light spell should exist in prepared database",
},
{
spellName: "Erkennen von Krankheit",
expectedSchool: "Erkennen",
expectedLevel: 2,
expectError: false,
description: "Common diagnostic spell should exist in prepared database",
},
{
spellName: "Furcht",
expectedSchool: "Beherrschen",
expectedLevel: 1,
expectError: false,
description: "Fear spell should exist in prepared database",
},
{
spellName: "Unknown Spell That Should Not Exist",
expectedSchool: "", // Should error for unknown spell
expectedLevel: 0, // Should error for unknown spell
expectError: true,
description: "Non-existent spell should return error",
},
}
for _, tt := range tests {
t.Run(tt.spellName, func(t *testing.T) {
school, level, err := gsmaster.GetSpellInfo(tt.spellName)
if tt.expectError {
if err == nil {
t.Errorf("Expected error for %s, but got none. %s", tt.spellName, tt.description)
}
return
}
if err != nil {
// If we get an error for a spell we expect to exist, it might mean
// the prepared test database doesn't exist or doesn't contain this spell
t.Logf("Warning: Failed to get spell info for %s: %v. %s", tt.spellName, err, tt.description)
t.Logf("This might indicate that the prepared test database is missing or incomplete.")
t.Skip("Skipping test due to missing prepared test data")
return
}
if school != tt.expectedSchool {
t.Errorf("Expected school %s, got %s for spell %s", tt.expectedSchool, school, tt.spellName)
}
if level != tt.expectedLevel {
t.Errorf("Expected level %d, got %d for spell %s", tt.expectedLevel, level, tt.spellName)
}
t.Logf("✅ Successfully found spell %s: school=%s, level=%d", tt.spellName, school, level)
})
}
}