PP, EP und Gold werden nun korrekt verbraucht
This commit is contained in:
@@ -592,6 +592,74 @@ func getValueOrDefault(value *int, defaultValue int) int {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// updateOrCreateSkill aktualisiert eine vorhandene Fertigkeit oder erstellt eine neue
|
||||
func updateOrCreateSkill(character *Char, skillName string, newLevel int) error {
|
||||
// Suche erst in normalen Fertigkeiten
|
||||
for i := range character.Fertigkeiten {
|
||||
if character.Fertigkeiten[i].Name == skillName {
|
||||
character.Fertigkeiten[i].Fertigkeitswert = newLevel
|
||||
return database.DB.Save(&character.Fertigkeiten[i]).Error
|
||||
}
|
||||
}
|
||||
|
||||
// Suche in Waffenfertigkeiten
|
||||
for i := range character.Waffenfertigkeiten {
|
||||
if character.Waffenfertigkeiten[i].Name == skillName {
|
||||
character.Waffenfertigkeiten[i].Fertigkeitswert = newLevel
|
||||
return database.DB.Save(&character.Waffenfertigkeiten[i]).Error
|
||||
}
|
||||
}
|
||||
|
||||
// Fertigkeit nicht gefunden - erstelle neue normale Fertigkeit
|
||||
newSkill := skills.Fertigkeit{
|
||||
BamortCharTrait: models.BamortCharTrait{
|
||||
BamortBase: models.BamortBase{
|
||||
Name: skillName,
|
||||
},
|
||||
CharacterID: character.ID,
|
||||
},
|
||||
Fertigkeitswert: newLevel,
|
||||
Improvable: true,
|
||||
}
|
||||
|
||||
if err := database.DB.Create(&newSkill).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Füge zur Charakter-Liste hinzu
|
||||
character.Fertigkeiten = append(character.Fertigkeiten, newSkill)
|
||||
return nil
|
||||
}
|
||||
|
||||
// addSpellToCharacter fügt einen neuen Zauber zum Charakter hinzu
|
||||
func addSpellToCharacter(character *Char, spellName string) error {
|
||||
// Prüfe, ob Zauber bereits existiert
|
||||
for _, spell := range character.Zauber {
|
||||
if spell.Name == spellName {
|
||||
// Zauber bereits vorhanden, nichts zu tun
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Erstelle neuen Zauber
|
||||
newSpell := skills.Zauber{
|
||||
BamortCharTrait: models.BamortCharTrait{
|
||||
BamortBase: models.BamortBase{
|
||||
Name: spellName,
|
||||
},
|
||||
CharacterID: character.ID,
|
||||
},
|
||||
}
|
||||
|
||||
if err := database.DB.Create(&newSpell).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Füge zur Charakter-Liste hinzu
|
||||
character.Zauber = append(character.Zauber, newSpell)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Learn and Improve handlers with automatic audit logging
|
||||
|
||||
// LearnSpellRequest definiert die Struktur für das Lernen eines Zaubers
|
||||
@@ -728,7 +796,7 @@ func LearnSkill(c *gin.Context) {
|
||||
}
|
||||
|
||||
// Für Learning müssen wir von Level 0 (nicht gelernt) auf finalLevel lernen
|
||||
var totalEP, totalGold int
|
||||
var totalEP, totalGold, totalPP int
|
||||
var err error
|
||||
|
||||
// Loop für jeden Level von 0 bis finalLevel (für neue Fertigkeiten)
|
||||
@@ -762,6 +830,7 @@ func LearnSkill(c *gin.Context) {
|
||||
// Addiere die Kosten
|
||||
totalEP += costResult.EP
|
||||
totalGold += costResult.GoldCost
|
||||
totalPP += costResult.PPUsed
|
||||
}
|
||||
|
||||
// Prüfe, ob genügend EP vorhanden sind
|
||||
@@ -778,6 +847,28 @@ func LearnSkill(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Prüfe, ob genügend PP vorhanden sind (PP der jeweiligen Fertigkeit) - für neue Fertigkeiten normalerweise 0
|
||||
currentPP := 0
|
||||
for _, skill := range character.Fertigkeiten {
|
||||
if skill.Name == request.Name {
|
||||
currentPP = skill.Pp
|
||||
break
|
||||
}
|
||||
}
|
||||
// Falls nicht in normalen Fertigkeiten gefunden, prüfe Waffenfertigkeiten
|
||||
if currentPP == 0 {
|
||||
for _, skill := range character.Waffenfertigkeiten {
|
||||
if skill.Name == request.Name {
|
||||
currentPP = skill.Pp
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if totalPP > 0 && currentPP < totalPP {
|
||||
respondWithError(c, http.StatusBadRequest, "Nicht genügend Praxispunkte vorhanden")
|
||||
return
|
||||
}
|
||||
|
||||
// EP abziehen und Audit-Log erstellen
|
||||
newEP := currentEP - totalEP
|
||||
if totalEP > 0 {
|
||||
@@ -794,6 +885,10 @@ func LearnSkill(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
character.Erfahrungsschatz.EP = newEP
|
||||
if err := database.DB.Save(&character.Erfahrungsschatz).Error; err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, "Fehler beim Speichern der Erfahrungspunkte")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Gold abziehen und Audit-Log erstellen
|
||||
@@ -807,10 +902,35 @@ func LearnSkill(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
character.Vermoegen.Goldstücke = newGold
|
||||
if err := database.DB.Save(&character.Vermoegen).Error; err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, "Fehler beim Speichern des Vermögens")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Hier sollte die Fertigkeit dem Charakter hinzugefügt werden
|
||||
// Das hängt davon ab, wie Fertigkeiten in der Datenbank gespeichert werden
|
||||
// PP abziehen (falls vorhanden und erforderlich)
|
||||
if totalPP > 0 {
|
||||
// Suche die richtige Fertigkeit und ziehe PP ab
|
||||
for i, skill := range character.Fertigkeiten {
|
||||
if skill.Name == request.Name {
|
||||
character.Fertigkeiten[i].Pp -= totalPP
|
||||
break
|
||||
}
|
||||
}
|
||||
// Falls nicht in normalen Fertigkeiten gefunden, prüfe Waffenfertigkeiten
|
||||
for i, skill := range character.Waffenfertigkeiten {
|
||||
if skill.Name == request.Name {
|
||||
character.Waffenfertigkeiten[i].Pp -= totalPP
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Erstelle die neue Fertigkeit mit dem finalen Level
|
||||
if err := updateOrCreateSkill(&character, request.Name, finalLevel); err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, "Fehler beim Hinzufügen der Fertigkeit: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Charakter speichern
|
||||
if err := database.DB.Save(&character).Error; err != nil {
|
||||
@@ -855,7 +975,13 @@ func ImproveSkill(c *gin.Context) {
|
||||
|
||||
// Hole Charakter über die ID aus dem Request
|
||||
var character Char
|
||||
if err := character.FirstID(fmt.Sprintf("%d", request.CharId)); err != nil {
|
||||
err := database.DB.
|
||||
Preload("Fertigkeiten").
|
||||
Preload("Waffenfertigkeiten").
|
||||
Preload("Erfahrungsschatz").
|
||||
Preload("Vermoegen").
|
||||
First(&character, request.CharId).Error
|
||||
if err != nil {
|
||||
respondWithError(c, http.StatusNotFound, "Charakter nicht gefunden")
|
||||
return
|
||||
}
|
||||
@@ -886,8 +1012,7 @@ func ImproveSkill(c *gin.Context) {
|
||||
}
|
||||
|
||||
// Initialisiere Gesamtkosten
|
||||
var totalEP, totalGold int
|
||||
var err error
|
||||
var totalEP, totalGold, totalPP int
|
||||
|
||||
// Loop für jeden Level von currentLevel bis finalLevel
|
||||
tempLevel := currentLevel
|
||||
@@ -914,6 +1039,7 @@ func ImproveSkill(c *gin.Context) {
|
||||
// Addiere die Kosten
|
||||
totalEP += costResult.EP
|
||||
totalGold += costResult.GoldCost
|
||||
totalPP += costResult.PPUsed
|
||||
|
||||
tempLevel++
|
||||
}
|
||||
@@ -932,6 +1058,28 @@ func ImproveSkill(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Prüfe, ob genügend PP vorhanden sind (PP der jeweiligen Fertigkeit)
|
||||
currentPP := 0
|
||||
for _, skill := range character.Fertigkeiten {
|
||||
if skill.Name == request.Name {
|
||||
currentPP = skill.Pp
|
||||
break
|
||||
}
|
||||
}
|
||||
// Falls nicht in normalen Fertigkeiten gefunden, prüfe Waffenfertigkeiten
|
||||
if currentPP == 0 {
|
||||
for _, skill := range character.Waffenfertigkeiten {
|
||||
if skill.Name == request.Name {
|
||||
currentPP = skill.Pp
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if totalPP > 0 && currentPP < totalPP {
|
||||
respondWithError(c, http.StatusBadRequest, "Nicht genügend Praxispunkte vorhanden")
|
||||
return
|
||||
}
|
||||
|
||||
// EP abziehen und Audit-Log erstellen
|
||||
newEP := currentEP - totalEP
|
||||
if totalEP > 0 {
|
||||
@@ -950,6 +1098,10 @@ func ImproveSkill(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
character.Erfahrungsschatz.EP = newEP
|
||||
if err := database.DB.Save(&character.Erfahrungsschatz).Error; err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, "Fehler beim Speichern der Erfahrungspunkte")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Gold abziehen und Audit-Log erstellen
|
||||
@@ -963,10 +1115,43 @@ func ImproveSkill(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
character.Vermoegen.Goldstücke = newGold
|
||||
if err := database.DB.Save(&character.Vermoegen).Error; err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, "Fehler beim Speichern des Vermögens")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Hier sollte die Fertigkeit des Charakters verbessert werden
|
||||
// Das hängt davon ab, wie Fertigkeiten in der Datenbank gespeichert werden
|
||||
// PP abziehen wenn verwendet (PP der jeweiligen Fertigkeit)
|
||||
if totalPP > 0 {
|
||||
// Finde die richtige Fertigkeit und ziehe PP ab
|
||||
for i := range character.Fertigkeiten {
|
||||
if character.Fertigkeiten[i].Name == request.Name {
|
||||
character.Fertigkeiten[i].Pp -= totalPP
|
||||
if err := database.DB.Save(&character.Fertigkeiten[i]).Error; err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, "Fehler beim Aktualisieren der Praxispunkte")
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
// Falls nicht in normalen Fertigkeiten gefunden, prüfe Waffenfertigkeiten
|
||||
for i := range character.Waffenfertigkeiten {
|
||||
if character.Waffenfertigkeiten[i].Name == request.Name {
|
||||
character.Waffenfertigkeiten[i].Pp -= totalPP
|
||||
if err := database.DB.Save(&character.Waffenfertigkeiten[i]).Error; err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, "Fehler beim Aktualisieren der Praxispunkte")
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Aktualisiere die Fertigkeit mit dem neuen Level
|
||||
if err := updateOrCreateSkill(&character, request.Name, finalLevel); err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, "Fehler beim Aktualisieren der Fertigkeit: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Charakter speichern
|
||||
if err := database.DB.Save(&character).Error; err != nil {
|
||||
@@ -1054,7 +1239,11 @@ func LearnSpell(c *gin.Context) {
|
||||
character.Erfahrungsschatz.EP = newEP
|
||||
}
|
||||
|
||||
// TODO: Hier sollte der Zauber dem Charakter hinzugefügt werden
|
||||
// Füge den Zauber zum Charakter hinzu
|
||||
if err := addSpellToCharacter(&character, request.Name); err != nil {
|
||||
respondWithError(c, http.StatusInternalServerError, "Fehler beim Hinzufügen des Zaubers: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Charakter speichern
|
||||
if err := database.DB.Save(&character).Error; err != nil {
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
package character
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"bamort/database"
|
||||
"bamort/gsmaster"
|
||||
"bamort/models"
|
||||
"bamort/skills"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestImproveSkillUpdatesLevel(t *testing.T) {
|
||||
// Setup test database
|
||||
database.SetupTestDB()
|
||||
defer database.ResetTestDB()
|
||||
|
||||
// Migrate the schema
|
||||
err := MigrateStructure()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Also migrate skills and equipment to avoid preload errors
|
||||
err = skills.MigrateStructure()
|
||||
assert.NoError(t, err)
|
||||
err = gsmaster.MigrateStructure()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Try to migrate equipment if it exists
|
||||
if equipmentDB := database.DB.Exec("CREATE TABLE IF NOT EXISTS equi_equipments (id INTEGER PRIMARY KEY, character_id INTEGER, name TEXT)"); equipmentDB.Error != nil {
|
||||
t.Logf("Warning: Could not create equipment table: %v", equipmentDB.Error)
|
||||
}
|
||||
|
||||
// Create audit log table for tests
|
||||
database.DB.Exec(`CREATE TABLE IF NOT EXISTS audit_log_entries (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
character_id INTEGER,
|
||||
field_name TEXT,
|
||||
old_value INTEGER,
|
||||
new_value INTEGER,
|
||||
difference INTEGER,
|
||||
reason TEXT,
|
||||
user_id INTEGER,
|
||||
timestamp DATETIME,
|
||||
notes TEXT
|
||||
)`)
|
||||
|
||||
// Create container table to avoid preload errors
|
||||
database.DB.Exec("CREATE TABLE IF NOT EXISTS equi_containers (id INTEGER PRIMARY KEY, character_id INTEGER)")
|
||||
database.DB.Exec("CREATE TABLE IF NOT EXISTS equi_weapons (id INTEGER PRIMARY KEY, character_id INTEGER)")
|
||||
database.DB.Exec("CREATE TABLE IF NOT EXISTS equi_transportmittel (id INTEGER PRIMARY KEY, character_id INTEGER)")
|
||||
database.DB.Exec("CREATE TABLE IF NOT EXISTS equi_equipments (id INTEGER PRIMARY KEY, character_id INTEGER)")
|
||||
|
||||
// Create test character with ID 20
|
||||
testChar := Char{
|
||||
BamortBase: models.BamortBase{
|
||||
ID: 20,
|
||||
Name: "Test Krieger",
|
||||
},
|
||||
Typ: "Krieger",
|
||||
Rasse: "Mensch",
|
||||
Grad: 1,
|
||||
Erfahrungsschatz: Erfahrungsschatz{
|
||||
BamortCharTrait: models.BamortCharTrait{
|
||||
CharacterID: 20,
|
||||
},
|
||||
EP: 326, // Starting EP
|
||||
},
|
||||
Vermoegen: Vermoegen{
|
||||
BamortCharTrait: models.BamortCharTrait{
|
||||
CharacterID: 20,
|
||||
},
|
||||
Goldstücke: 390, // Starting Gold
|
||||
},
|
||||
}
|
||||
|
||||
// Add Athletik skill at level 9
|
||||
athletikSkill := skills.Fertigkeit{
|
||||
BamortCharTrait: models.BamortCharTrait{
|
||||
BamortBase: models.BamortBase{
|
||||
Name: "Athletik",
|
||||
},
|
||||
CharacterID: 20,
|
||||
},
|
||||
Fertigkeitswert: 9,
|
||||
Improvable: true,
|
||||
}
|
||||
testChar.Fertigkeiten = append(testChar.Fertigkeiten, athletikSkill)
|
||||
|
||||
err = testChar.Create()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify character was created correctly
|
||||
var verifyChar Char
|
||||
err = database.DB.Preload("Fertigkeiten").Preload("Erfahrungsschatz").Preload("Vermoegen").First(&verifyChar, 20).Error
|
||||
assert.NoError(t, err)
|
||||
t.Logf("Character created with ID: %d, EP: %d, Skills: %d", verifyChar.ID, verifyChar.Erfahrungsschatz.EP, len(verifyChar.Fertigkeiten))
|
||||
|
||||
// Setup Gin in test mode
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
t.Run("ImproveSkill updates skill level from 9 to 10", func(t *testing.T) {
|
||||
// Create request body
|
||||
requestData := map[string]interface{}{
|
||||
"char_id": 20,
|
||||
"name": "Athletik",
|
||||
"current_level": 9,
|
||||
"target_level": 10,
|
||||
"type": "skill",
|
||||
"action": "improve",
|
||||
"reward": "default",
|
||||
"use_pp": 1,
|
||||
"use_gold": 0,
|
||||
"notes": "Fertigkeit Athletik von 9 auf 10 verbessert (1 Level)",
|
||||
}
|
||||
requestBody, _ := json.Marshal(requestData)
|
||||
|
||||
// Create HTTP request
|
||||
req, _ := http.NewRequest("POST", "/api/characters/improve-skill", bytes.NewBuffer(requestBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
// Create response recorder
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// Create Gin context
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = req
|
||||
|
||||
// Call the actual handler function
|
||||
ImproveSkill(c)
|
||||
|
||||
// Print the actual response to see what we get
|
||||
t.Logf("Response Status: %d", w.Code)
|
||||
t.Logf("Response Body: %s", w.Body.String())
|
||||
|
||||
// Check if we got a successful response
|
||||
assert.Equal(t, http.StatusOK, w.Code, "Status code should be 200 OK")
|
||||
|
||||
// Verify that the skill level was actually updated in the database
|
||||
var updatedChar Char
|
||||
err = database.DB.Preload("Fertigkeiten").First(&updatedChar, 20).Error
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Find the Athletik skill and check its level
|
||||
skillFound := false
|
||||
for _, skill := range updatedChar.Fertigkeiten {
|
||||
if skill.Name == "Athletik" {
|
||||
assert.Equal(t, 10, skill.Fertigkeitswert, "Athletik skill should be level 10 after improvement")
|
||||
skillFound = true
|
||||
t.Logf("Found Athletik skill with level: %d", skill.Fertigkeitswert)
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, skillFound, "Athletik skill should be found in character's skills")
|
||||
|
||||
t.Logf("Test completed successfully!")
|
||||
t.Logf("Athletik skill successfully updated from level 9 to level 10")
|
||||
})
|
||||
|
||||
t.Run("LearnSkill creates new skill at level 1", func(t *testing.T) {
|
||||
// Create request body for learning a new skill
|
||||
requestData := map[string]interface{}{
|
||||
"name": "Schwimmen",
|
||||
"current_level": 0,
|
||||
"target_level": 1,
|
||||
"type": "skill",
|
||||
"action": "learn",
|
||||
"reward": "default",
|
||||
"notes": "Neue Fertigkeit Schwimmen gelernt",
|
||||
}
|
||||
requestBody, _ := json.Marshal(requestData)
|
||||
|
||||
// Create HTTP request
|
||||
req, _ := http.NewRequest("POST", "/api/characters/20/learn-skill", bytes.NewBuffer(requestBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
// Create response recorder
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// Create Gin context with character ID parameter
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = req
|
||||
c.Params = gin.Params{gin.Param{Key: "id", Value: "20"}}
|
||||
|
||||
// Call the actual handler function
|
||||
LearnSkill(c)
|
||||
|
||||
// Check if we got a successful response
|
||||
assert.Equal(t, http.StatusOK, w.Code, "Status code should be 200 OK")
|
||||
|
||||
// Verify that the new skill was created in the database
|
||||
var updatedChar Char
|
||||
err = updatedChar.FirstID("20")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Find the Schwimmen skill and check its level
|
||||
skillFound := false
|
||||
for _, skill := range updatedChar.Fertigkeiten {
|
||||
if skill.Name == "Schwimmen" {
|
||||
assert.Equal(t, 1, skill.Fertigkeitswert, "Schwimmen skill should be level 1 after learning")
|
||||
skillFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, skillFound, "Schwimmen skill should be found in character's skills")
|
||||
|
||||
t.Logf("New skill 'Schwimmen' successfully created at level 1")
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user