Charakter creation bugfixing

This commit is contained in:
2025-08-10 22:26:07 +02:00
parent 141211edf6
commit 8874084a07
6 changed files with 431 additions and 146 deletions
+173 -12
View File
@@ -2370,14 +2370,26 @@ func calculateSkillLearningCostsOld(skill models.Skill, character models.Char, r
// CreateCharacterSession erstellt eine neue Charakter-Erstellungssession
func CreateCharacterSession(c *gin.Context) {
logger.Debug("CreateCharacterSession aufgerufen")
// Debug: Alle Kontext-Keys anzeigen
keys := make([]string, 0)
for key := range c.Keys {
keys = append(keys, fmt.Sprintf("%s=%v", key, c.Keys[key]))
}
logger.Debug("CreateCharacterSession: Verfügbare Kontext-Keys: [%s]", strings.Join(keys, ", "))
userID := c.GetUint("userID")
logger.Debug("CreateCharacterSession: UserID = %d", userID)
if userID == 0 {
logger.Warn("CreateCharacterSession: Unauthorized - UserID ist 0")
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
sessionID := fmt.Sprintf("char_create_%d_%d", userID, time.Now().Unix())
logger.Debug("CreateCharacterSession: Generierte SessionID = %s", sessionID)
session := models.CharacterCreationSession{
ID: sessionID,
@@ -2389,22 +2401,26 @@ func CreateCharacterSession(c *gin.Context) {
Glaube: "",
Attributes: models.AttributesData{},
DerivedValues: models.DerivedValuesData{},
Skills: []models.CharacterCreationSkill{},
Spells: []models.CharacterCreationSpell{},
Skills: models.CharacterCreationSkills{},
Spells: models.CharacterCreationSpells{},
SkillPoints: models.SkillPointsData{},
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
ExpiresAt: time.Now().AddDate(0, 0, 14), // 14 Tage
CurrentStep: 1,
}
logger.Debug("CreateCharacterSession: Session-Struktur erstellt, ExpiresAt = %s", session.ExpiresAt.Format(time.RFC3339))
// Session in Datenbank speichern
logger.Debug("CreateCharacterSession: Speichere Session in Datenbank...")
err := database.DB.Create(&session).Error
if err != nil {
logger.Error("CreateCharacterSession: Fehler beim Erstellen der Session: %s", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create session"})
return
}
logger.Info("CreateCharacterSession: Session erfolgreich erstellt - SessionID: %s, UserID: %d", sessionID, userID)
c.JSON(http.StatusCreated, gin.H{
"session_id": sessionID,
"expires_at": session.ExpiresAt,
@@ -2413,25 +2429,35 @@ func CreateCharacterSession(c *gin.Context) {
// ListCharacterSessions gibt alle aktiven Sessions für einen Benutzer zurück
func ListCharacterSessions(c *gin.Context) {
logger.Debug("ListCharacterSessions aufgerufen")
userID := c.GetUint("userID")
logger.Debug("ListCharacterSessions: UserID = %d", userID)
if userID == 0 {
logger.Warn("ListCharacterSessions: Unauthorized - UserID ist 0")
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
// Sessions aus Datenbank laden
logger.Debug("ListCharacterSessions: Lade Sessions für UserID %d aus Datenbank...", userID)
sessions, err := models.GetUserSessions(database.DB, userID)
if err != nil {
logger.Error("ListCharacterSessions: Fehler beim Laden der Sessions: %s", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load sessions"})
return
}
logger.Debug("ListCharacterSessions: Gefundene Sessions: %d", len(sessions))
// Sessions für Frontend formatieren
var formattedSessions []gin.H
for _, session := range sessions {
for i, session := range sessions {
// Schritt-Text bestimmen
progressText := getProgressText(session.CurrentStep)
logger.Debug("ListCharacterSessions: Formatiere Session %d - ID: %s, Step: %d, Name: %s",
i+1, session.ID, session.CurrentStep, session.Name)
formattedSessions = append(formattedSessions, gin.H{
"session_id": session.ID,
@@ -2447,6 +2473,7 @@ func ListCharacterSessions(c *gin.Context) {
})
}
logger.Info("ListCharacterSessions: Sessions erfolgreich geladen für UserID %d - Anzahl: %d", userID, len(formattedSessions))
c.JSON(http.StatusOK, gin.H{
"sessions": formattedSessions,
"count": len(formattedSessions),
@@ -2455,48 +2482,72 @@ func ListCharacterSessions(c *gin.Context) {
// getProgressText gibt den Schritt-Text für die Frontend-Anzeige zurück
func getProgressText(step int) string {
logger.Debug("getProgressText: Ermittle Text für Schritt %d", step)
var text string
switch step {
case 1:
return "Grundinformationen"
text = "Grundinformationen"
case 2:
return "Attribute"
text = "Attribute"
case 3:
return "Abgeleitete Werte"
text = "Abgeleitete Werte"
case 4:
return "Fertigkeiten"
text = "Fertigkeiten"
case 5:
return "Zauber"
text = "Zauber"
default:
return "Unbekannt"
text = "Unbekannt"
logger.Warn("getProgressText: Unbekannter Schritt %d", step)
}
logger.Debug("getProgressText: Schritt %d = '%s'", step, text)
return text
}
// GetCharacterSession gibt Session-Daten zurück
func GetCharacterSession(c *gin.Context) {
logger.Debug("GetCharacterSession aufgerufen")
sessionID := c.Param("sessionId")
userID := c.GetUint("userID")
logger.Debug("GetCharacterSession: SessionID = %s, UserID = %d", sessionID, userID)
if userID == 0 {
logger.Warn("GetCharacterSession: Unauthorized - UserID ist 0")
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
// Session aus Datenbank laden
logger.Debug("GetCharacterSession: Lade Session aus Datenbank...")
var session models.CharacterCreationSession
err := database.DB.Where("id = ? AND user_id = ?", sessionID, userID).First(&session).Error
if err != nil {
logger.Error("GetCharacterSession: Session nicht gefunden - SessionID: %s, UserID: %d, Error: %s",
sessionID, userID, err.Error())
c.JSON(http.StatusNotFound, gin.H{"error": "Session not found"})
return
}
logger.Debug("GetCharacterSession: Session gefunden - Name: %s, Step: %d, ExpiresAt: %s",
session.Name, session.CurrentStep, session.ExpiresAt.Format(time.RFC3339))
// Prüfen ob Session noch gültig ist
if session.ExpiresAt.Before(time.Now()) {
logger.Warn("GetCharacterSession: Session abgelaufen - SessionID: %s, ExpiresAt: %s",
sessionID, session.ExpiresAt.Format(time.RFC3339))
// Abgelaufene Session löschen
logger.Debug("GetCharacterSession: Lösche abgelaufene Session...")
database.DB.Delete(&session)
c.JSON(http.StatusGone, gin.H{"error": "Session expired"})
return
}
logger.Info("GetCharacterSession: Session erfolgreich geladen - SessionID: %s, UserID: %d, Step: %d",
sessionID, userID, session.CurrentStep)
c.JSON(http.StatusOK, session)
}
@@ -2511,28 +2562,42 @@ type UpdateBasicInfoRequest struct {
// UpdateCharacterBasicInfo speichert Grundinformationen
func UpdateCharacterBasicInfo(c *gin.Context) {
logger.Debug("UpdateCharacterBasicInfo aufgerufen")
sessionID := c.Param("sessionId")
userID := c.GetUint("userID")
logger.Debug("UpdateCharacterBasicInfo: SessionID = %s, UserID = %d", sessionID, userID)
if userID == 0 {
logger.Warn("UpdateCharacterBasicInfo: Unauthorized - UserID ist 0")
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
var request UpdateBasicInfoRequest
if err := c.ShouldBindJSON(&request); err != nil {
logger.Error("UpdateCharacterBasicInfo: Ungültige Eingabedaten - %s", err.Error())
respondWithError(c, http.StatusBadRequest, "Ungültige Eingabedaten: "+err.Error())
return
}
logger.Debug("UpdateCharacterBasicInfo: Request-Daten - Name: %s, Rasse: %s, Typ: %s, Herkunft: %s, Glaube: %s",
request.Name, request.Rasse, request.Typ, request.Herkunft, request.Glaube)
// Session aus Datenbank laden
logger.Debug("UpdateCharacterBasicInfo: Lade Session aus Datenbank...")
var session models.CharacterCreationSession
err := database.DB.Where("id = ? AND user_id = ?", sessionID, userID).First(&session).Error
if err != nil {
logger.Error("UpdateCharacterBasicInfo: Session nicht gefunden - SessionID: %s, UserID: %d, Error: %s",
sessionID, userID, err.Error())
c.JSON(http.StatusNotFound, gin.H{"error": "Session not found"})
return
}
logger.Debug("UpdateCharacterBasicInfo: Aktueller Session-Status - Step: %d, Name: %s",
session.CurrentStep, session.Name)
// Grundinformationen aktualisieren
session.Name = request.Name
session.Rasse = request.Rasse
@@ -2542,13 +2607,19 @@ func UpdateCharacterBasicInfo(c *gin.Context) {
session.CurrentStep = 2
session.UpdatedAt = time.Now()
logger.Debug("UpdateCharacterBasicInfo: Session aktualisiert, setze CurrentStep auf 2")
// Session in Datenbank aktualisieren
logger.Debug("UpdateCharacterBasicInfo: Speichere Session in Datenbank...")
err = database.DB.Save(&session).Error
if err != nil {
logger.Error("UpdateCharacterBasicInfo: Fehler beim Speichern der Session: %s", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update session"})
return
}
logger.Info("UpdateCharacterBasicInfo: Grundinformationen erfolgreich gespeichert - SessionID: %s, Name: %s",
sessionID, request.Name)
c.JSON(http.StatusOK, gin.H{
"message": "Grundinformationen gespeichert",
"session_id": sessionID,
@@ -2571,28 +2642,41 @@ type UpdateAttributesRequest struct {
// UpdateCharacterAttributes speichert Grundwerte
func UpdateCharacterAttributes(c *gin.Context) {
logger.Debug("UpdateCharacterAttributes aufgerufen")
sessionID := c.Param("sessionId")
userID := c.GetUint("userID")
logger.Debug("UpdateCharacterAttributes: SessionID = %s, UserID = %d", sessionID, userID)
if userID == 0 {
logger.Warn("UpdateCharacterAttributes: Unauthorized - UserID ist 0")
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
var request UpdateAttributesRequest
if err := c.ShouldBindJSON(&request); err != nil {
logger.Error("UpdateCharacterAttributes: Ungültige Attributswerte - %s", err.Error())
respondWithError(c, http.StatusBadRequest, "Ungültige Attributswerte: "+err.Error())
return
}
logger.Debug("UpdateCharacterAttributes: Attribute - ST:%d GS:%d GW:%d KO:%d IN:%d ZT:%d AU:%d PA:%d WK:%d",
request.ST, request.GS, request.GW, request.KO, request.IN, request.ZT, request.AU, request.PA, request.WK)
// Session aus Datenbank laden
logger.Debug("UpdateCharacterAttributes: Lade Session aus Datenbank...")
var session models.CharacterCreationSession
err := database.DB.Where("id = ? AND user_id = ?", sessionID, userID).First(&session).Error
if err != nil {
logger.Error("UpdateCharacterAttributes: Session nicht gefunden - SessionID: %s, UserID: %d, Error: %s",
sessionID, userID, err.Error())
c.JSON(http.StatusNotFound, gin.H{"error": "Session not found"})
return
}
logger.Debug("UpdateCharacterAttributes: Session geladen - CurrentStep: %d", session.CurrentStep)
// Attribute aktualisieren
session.Attributes = models.AttributesData{
ST: request.ST,
@@ -2608,13 +2692,18 @@ func UpdateCharacterAttributes(c *gin.Context) {
session.CurrentStep = 3
session.UpdatedAt = time.Now()
logger.Debug("UpdateCharacterAttributes: Attribute gesetzt, CurrentStep auf 3 aktualisiert")
// Session in Datenbank aktualisieren
logger.Debug("UpdateCharacterAttributes: Speichere Session in Datenbank...")
err = database.DB.Save(&session).Error
if err != nil {
logger.Error("UpdateCharacterAttributes: Fehler beim Speichern der Session: %s", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update session"})
return
}
logger.Info("UpdateCharacterAttributes: Grundwerte erfolgreich gespeichert - SessionID: %s", sessionID)
c.JSON(http.StatusOK, gin.H{
"message": "Grundwerte gespeichert",
"session_id": sessionID,
@@ -2634,28 +2723,41 @@ type UpdateDerivedValuesRequest struct {
// UpdateCharacterDerivedValues speichert abgeleitete Werte
func UpdateCharacterDerivedValues(c *gin.Context) {
logger.Debug("UpdateCharacterDerivedValues aufgerufen")
sessionID := c.Param("sessionId")
userID := c.GetUint("userID")
logger.Debug("UpdateCharacterDerivedValues: SessionID = %s, UserID = %d", sessionID, userID)
if userID == 0 {
logger.Warn("UpdateCharacterDerivedValues: Unauthorized - UserID ist 0")
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
var request UpdateDerivedValuesRequest
if err := c.ShouldBindJSON(&request); err != nil {
logger.Error("UpdateCharacterDerivedValues: Ungültige abgeleitete Werte - %s", err.Error())
respondWithError(c, http.StatusBadRequest, "Ungültige abgeleitete Werte: "+err.Error())
return
}
logger.Debug("UpdateCharacterDerivedValues: Werte - LP_Max:%d AP_Max:%d B_Max:%d SG:%d GG:%d GP:%d",
request.LP_Max, request.AP_Max, request.B_Max, request.SG, request.GG, request.GP)
// Session aus Datenbank laden
logger.Debug("UpdateCharacterDerivedValues: Lade Session aus Datenbank...")
var session models.CharacterCreationSession
err := database.DB.Where("id = ? AND user_id = ?", sessionID, userID).First(&session).Error
if err != nil {
logger.Error("UpdateCharacterDerivedValues: Session nicht gefunden - SessionID: %s, UserID: %d, Error: %s",
sessionID, userID, err.Error())
c.JSON(http.StatusNotFound, gin.H{"error": "Session not found"})
return
}
logger.Debug("UpdateCharacterDerivedValues: Session geladen - CurrentStep: %d", session.CurrentStep)
// Abgeleitete Werte aktualisieren
session.DerivedValues = models.DerivedValuesData{
LPMax: request.LP_Max,
@@ -2668,13 +2770,18 @@ func UpdateCharacterDerivedValues(c *gin.Context) {
session.CurrentStep = 4
session.UpdatedAt = time.Now()
logger.Debug("UpdateCharacterDerivedValues: Abgeleitete Werte gesetzt, CurrentStep auf 4 aktualisiert")
// Session in Datenbank aktualisieren
logger.Debug("UpdateCharacterDerivedValues: Speichere Session in Datenbank...")
err = database.DB.Save(&session).Error
if err != nil {
logger.Error("UpdateCharacterDerivedValues: Fehler beim Speichern der Session: %s", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update session"})
return
}
logger.Info("UpdateCharacterDerivedValues: Abgeleitete Werte erfolgreich gespeichert - SessionID: %s", sessionID)
c.JSON(http.StatusOK, gin.H{
"message": "Abgeleitete Werte gespeichert",
"session_id": sessionID,
@@ -2684,35 +2791,48 @@ func UpdateCharacterDerivedValues(c *gin.Context) {
// UpdateSkillsRequest
type UpdateSkillsRequest struct {
Skills []models.CharacterCreationSkill `json:"skills"`
Spells []models.CharacterCreationSpell `json:"spells"`
SkillPoints models.SkillPointsData `json:"skill_points"` // Verbleibende Punkte pro Kategorie
Skills models.CharacterCreationSkills `json:"skills"`
Spells models.CharacterCreationSpells `json:"spells"`
SkillPoints models.SkillPointsData `json:"skill_points"` // Verbleibende Punkte pro Kategorie
}
// UpdateCharacterSkills speichert Fertigkeiten und Zauber
func UpdateCharacterSkills(c *gin.Context) {
logger.Debug("UpdateCharacterSkills aufgerufen")
sessionID := c.Param("sessionId")
userID := c.GetUint("userID")
logger.Debug("UpdateCharacterSkills: SessionID = %s, UserID = %d", sessionID, userID)
if userID == 0 {
logger.Warn("UpdateCharacterSkills: Unauthorized - UserID ist 0")
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
var request UpdateSkillsRequest
if err := c.ShouldBindJSON(&request); err != nil {
logger.Error("UpdateCharacterSkills: Ungültige Fertigkeitsdaten - %s", err.Error())
respondWithError(c, http.StatusBadRequest, "Ungültige Fertigkeitsdaten: "+err.Error())
return
}
logger.Debug("UpdateCharacterSkills: Skills-Anzahl: %d, Spells-Anzahl: %d",
len(request.Skills), len(request.Spells))
// Session aus Datenbank laden
logger.Debug("UpdateCharacterSkills: Lade Session aus Datenbank...")
var session models.CharacterCreationSession
err := database.DB.Where("id = ? AND user_id = ?", sessionID, userID).First(&session).Error
if err != nil {
logger.Error("UpdateCharacterSkills: Session nicht gefunden - SessionID: %s, UserID: %d, Error: %s",
sessionID, userID, err.Error())
c.JSON(http.StatusNotFound, gin.H{"error": "Session not found"})
return
}
logger.Debug("UpdateCharacterSkills: Session geladen - CurrentStep: %d", session.CurrentStep)
// Fertigkeiten und Zauber aktualisieren
session.Skills = request.Skills
session.Spells = request.Spells
@@ -2720,13 +2840,19 @@ func UpdateCharacterSkills(c *gin.Context) {
session.CurrentStep = 5
session.UpdatedAt = time.Now()
logger.Debug("UpdateCharacterSkills: Skills/Spells gesetzt, CurrentStep auf 5 aktualisiert")
// Session in Datenbank aktualisieren
logger.Debug("UpdateCharacterSkills: Speichere Session in Datenbank...")
err = database.DB.Save(&session).Error
if err != nil {
logger.Error("UpdateCharacterSkills: Fehler beim Speichern der Session: %s", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update session"})
return
}
logger.Info("UpdateCharacterSkills: Fertigkeiten erfolgreich gespeichert - SessionID: %s, Skills: %d, Spells: %d",
sessionID, len(request.Skills), len(request.Spells))
c.JSON(http.StatusOK, gin.H{
"message": "Fertigkeiten gespeichert",
"session_id": sessionID,
@@ -2736,28 +2862,41 @@ func UpdateCharacterSkills(c *gin.Context) {
// FinalizeCharacterCreation schließt die Charakter-Erstellung ab
func FinalizeCharacterCreation(c *gin.Context) {
logger.Debug("FinalizeCharacterCreation aufgerufen")
sessionID := c.Param("sessionId")
userID := c.GetUint("userID")
logger.Debug("FinalizeCharacterCreation: SessionID = %s, UserID = %d", sessionID, userID)
if userID == 0 {
logger.Warn("FinalizeCharacterCreation: Unauthorized - UserID ist 0")
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
// Session laden
logger.Debug("FinalizeCharacterCreation: Lade Session aus Datenbank...")
var session models.CharacterCreationSession
err := database.DB.Where("id = ? AND user_id = ?", sessionID, userID).First(&session).Error
if err != nil {
logger.Error("FinalizeCharacterCreation: Session nicht gefunden - SessionID: %s, UserID: %d, Error: %s",
sessionID, userID, err.Error())
c.JSON(http.StatusNotFound, gin.H{"error": "Session not found"})
return
}
logger.Debug("FinalizeCharacterCreation: Session geladen - Name: %s, CurrentStep: %d",
session.Name, session.CurrentStep)
// Session validieren
if session.CurrentStep < 5 {
logger.Warn("FinalizeCharacterCreation: Charakter-Erstellung unvollständig - CurrentStep: %d (erwartet: 5)",
session.CurrentStep)
c.JSON(http.StatusBadRequest, gin.H{"error": "Character creation not complete"})
return
}
logger.Debug("FinalizeCharacterCreation: Erstelle Charakter-Struktur...")
// Character erstellen
char := models.Char{
BamortBase: models.BamortBase{
@@ -2808,16 +2947,27 @@ func FinalizeCharacterCreation(c *gin.Context) {
{Name: "Wk", Value: session.Attributes.WK},
}
logger.Debug("FinalizeCharacterCreation: Charakter-Struktur erstellt mit %d Eigenschaften",
len(char.Eigenschaften))
// Character in Datenbank speichern
logger.Debug("FinalizeCharacterCreation: Speichere Charakter in Datenbank...")
err = char.Create()
if err != nil {
logger.Error("FinalizeCharacterCreation: Fehler beim Erstellen des Charakters: %s", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create character"})
return
}
logger.Debug("FinalizeCharacterCreation: Charakter erfolgreich erstellt mit ID: %d", char.ID)
// Session löschen
logger.Debug("FinalizeCharacterCreation: Lösche Session aus Datenbank...")
database.DB.Delete(&session)
logger.Info("FinalizeCharacterCreation: Charakter-Erstellung abgeschlossen - CharacterID: %d, SessionID: %s, Name: %s",
char.ID, sessionID, session.Name)
c.JSON(http.StatusCreated, gin.H{
"message": "Charakter erfolgreich erstellt",
"character_id": char.ID,
@@ -2827,26 +2977,37 @@ func FinalizeCharacterCreation(c *gin.Context) {
// DeleteCharacterSession löscht eine Session
func DeleteCharacterSession(c *gin.Context) {
logger.Debug("DeleteCharacterSession aufgerufen")
sessionID := c.Param("sessionId")
userID := c.GetUint("userID")
logger.Debug("DeleteCharacterSession: SessionID = %s, UserID = %d", sessionID, userID)
if userID == 0 {
logger.Warn("DeleteCharacterSession: Unauthorized - UserID ist 0")
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
// Session aus Datenbank löschen (nur eigene Sessions)
logger.Debug("DeleteCharacterSession: Lösche Session aus Datenbank...")
result := database.DB.Where("id = ? AND user_id = ?", sessionID, userID).Delete(&models.CharacterCreationSession{})
if result.Error != nil {
logger.Error("DeleteCharacterSession: Fehler beim Löschen der Session: %s", result.Error.Error())
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete session"})
return
}
if result.RowsAffected == 0 {
logger.Warn("DeleteCharacterSession: Session nicht gefunden oder bereits gelöscht - SessionID: %s, UserID: %d",
sessionID, userID)
c.JSON(http.StatusNotFound, gin.H{"error": "Session not found"})
return
}
logger.Info("DeleteCharacterSession: Session erfolgreich gelöscht - SessionID: %s, UserID: %d, RowsAffected: %d",
sessionID, userID, result.RowsAffected)
c.JSON(http.StatusOK, gin.H{
"message": "Session gelöscht",
"session_id": sessionID,
+20
View File
@@ -0,0 +1,20 @@
package main
import (
"bamort/database"
"bamort/models"
"fmt"
)
func main() {
fmt.Println("Starte Migration...")
database.ConnectDatabase()
err := database.DB.AutoMigrate(&models.CharacterCreationSession{})
if err != nil {
fmt.Printf("Migration Fehler: %v\n", err)
} else {
fmt.Println("Migration erfolgreich!")
}
}
+67 -17
View File
@@ -10,23 +10,23 @@ import (
// CharacterCreationSession speichert den Fortschritt der Charakter-Erstellung
type CharacterCreationSession struct {
ID string `json:"id" gorm:"primaryKey"`
UserID uint `json:"user_id" gorm:"index;not null"`
User user.User `json:"user" gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
Name string `json:"name"`
Rasse string `json:"rasse"`
Typ string `json:"typ"`
Herkunft string `json:"herkunft"`
Glaube string `json:"glaube"`
Attributes AttributesData `json:"attributes" gorm:"type:json"`
DerivedValues DerivedValuesData `json:"derived_values" gorm:"type:json"`
Skills []CharacterCreationSkill `json:"skills" gorm:"type:json"`
Spells []CharacterCreationSpell `json:"spells" gorm:"type:json"`
SkillPoints SkillPointsData `json:"skill_points" gorm:"type:json"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ExpiresAt time.Time `json:"expires_at"`
CurrentStep int `json:"current_step"` // 1=Basic, 2=Attributes, 3=Derived, 4=Skills
ID string `json:"id" gorm:"primaryKey"`
UserID uint `json:"user_id" gorm:"index;not null"`
User user.User `json:"user" gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
Name string `json:"name"`
Rasse string `json:"rasse"`
Typ string `json:"typ"`
Herkunft string `json:"herkunft"`
Glaube string `json:"glaube"`
Attributes AttributesData `json:"attributes" gorm:"type:text;serializer:json"`
DerivedValues DerivedValuesData `json:"derived_values" gorm:"type:text;serializer:json"`
Skills CharacterCreationSkills `json:"skills" gorm:"type:text;serializer:json"`
Spells CharacterCreationSpells `json:"spells" gorm:"type:text;serializer:json"`
SkillPoints SkillPointsData `json:"skill_points" gorm:"type:text;serializer:json"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ExpiresAt time.Time `json:"expires_at"`
CurrentStep int `json:"current_step"` // 1=Basic, 2=Attributes, 3=Derived, 4=Skills
}
// AttributesData speichert die Grundwerte
@@ -69,6 +69,56 @@ type CharacterCreationSpell struct {
Cost int `json:"cost"`
}
// Slice types for GORM JSON handling
type CharacterCreationSkills []CharacterCreationSkill
type CharacterCreationSpells []CharacterCreationSpell
// JSON scanning methods for Skills slice
func (s *CharacterCreationSkills) Scan(value interface{}) error {
if value == nil {
*s = make([]CharacterCreationSkill, 0)
return nil
}
bytes, ok := value.([]byte)
if !ok {
*s = make([]CharacterCreationSkill, 0)
return nil
}
return json.Unmarshal(bytes, s)
}
func (s CharacterCreationSkills) Value() (interface{}, error) {
if len(s) == 0 {
return "[]", nil
}
return json.Marshal(s)
}
// JSON scanning methods for Spells slice
func (sp *CharacterCreationSpells) Scan(value interface{}) error {
if value == nil {
*sp = make([]CharacterCreationSpell, 0)
return nil
}
bytes, ok := value.([]byte)
if !ok {
*sp = make([]CharacterCreationSpell, 0)
return nil
}
return json.Unmarshal(bytes, sp)
}
func (sp CharacterCreationSpells) Value() (interface{}, error) {
if len(sp) == 0 {
return "[]", nil
}
return json.Marshal(sp)
}
func (object *CharacterCreationSession) TableName() string {
dbPrefix := "char"
return dbPrefix + "_" + "char_creation_session"
+7 -2
View File
@@ -139,12 +139,17 @@ func AuthMiddleware() gin.HandlerFunc {
c.Abort()
return
}
if CheckToken(token) == nil {
user := CheckToken(token)
if user == nil {
respondWithError(c, http.StatusUnauthorized, "Unauthorized.")
c.Abort()
return
}
// Add token validation logic here
// Set user information in context
c.Set("userID", user.UserID)
c.Set("username", user.Username)
c.Next()
}
+58 -52
View File
@@ -169,10 +169,64 @@ export default {
}
},
handleNext(data) {
async handleNext(data) {
// Merge the new data
this.sessionData = { ...this.sessionData, ...data }
// Save progress for current step before moving to next
await this.saveProgressForStep(this.currentStep, data)
// Move to next step
this.currentStep++
this.saveProgress()
},
async saveProgressForStep(step, data) {
try {
const token = localStorage.getItem('token')
let endpoint = ''
let payload = {}
switch (step) {
case 1:
endpoint = `/api/characters/create-session/${this.sessionId}/basic`
payload = {
name: data.name || this.sessionData.name,
rasse: data.rasse || this.sessionData.rasse,
typ: data.typ || this.sessionData.typ,
herkunft: data.herkunft || this.sessionData.herkunft,
glaube: data.glaube || this.sessionData.glaube,
}
break
case 2:
endpoint = `/api/characters/create-session/${this.sessionId}/attributes`
payload = data.attributes || data
break
case 3:
endpoint = `/api/characters/create-session/${this.sessionId}/derived`
payload = data.derived_values || data
break
case 4:
endpoint = `/api/characters/create-session/${this.sessionId}/skills`
payload = {
skills: data.skills || this.sessionData.skills,
spells: data.spells || this.sessionData.spells,
skill_points: data.skill_points || this.sessionData.skill_points,
}
break
}
if (endpoint) {
console.log('Saving progress for step', step, 'with payload:', payload)
const response = await API.put(endpoint, payload, {
headers: { Authorization: `Bearer ${token}` },
})
console.log('Save response:', response.data)
}
} catch (error) {
console.error('Error saving progress for step', step, ':', error)
throw error // Re-throw to handle in calling function
}
},
handlePrevious() {
@@ -189,56 +243,8 @@ export default {
},
async saveProgress() {
try {
const token = localStorage.getItem('token')
// Bestimme den korrekten Endpoint basierend auf dem aktuellen Schritt
let endpoint = ''
let payload = {}
switch (this.currentStep) {
case 1:
endpoint = `/api/characters/create-session/${this.sessionId}/basic`
payload = {
name: this.sessionData.name,
rasse: this.sessionData.rasse,
typ: this.sessionData.typ,
herkunft: this.sessionData.herkunft,
glaube: this.sessionData.glaube,
}
break
case 2:
endpoint = `/api/characters/create-session/${this.sessionId}/attributes`
payload = this.sessionData.attributes
break
case 3:
endpoint = `/api/characters/create-session/${this.sessionId}/derived`
payload = this.sessionData.derived_values
break
case 4:
endpoint = `/api/characters/create-session/${this.sessionId}/skills`
payload = {
skills: this.sessionData.skills,
skill_points: this.sessionData.skill_points,
}
break
case 5:
endpoint = `/api/characters/create-session/${this.sessionId}/spells`
payload = {
spells: this.sessionData.spells,
spell_points: this.sessionData.spell_points,
}
break
}
if (endpoint) {
await API.put(endpoint, payload, {
headers: { Authorization: `Bearer ${token}` },
})
}
} catch (error) {
console.error('Error saving progress:', error)
}
// Save progress for current step with current sessionData
await this.saveProgressForStep(this.currentStep, this.sessionData)
},
async handleFinalize() {
@@ -3,80 +3,99 @@
<h2>Skills & Spells</h2>
<p class="instruction">Select skills and spells for your character. Each category has a limited number of learning points.</p>
<!-- Debug Info -->
<div v-if="true" style="background: #f0f0f0; padding: 10px; margin: 10px 0; border-radius: 4px; font-size: 12px;">
<strong>Debug Info:</strong><br>
skillCategories.length: {{ skillCategories.length }}<br>
selectedCategory: {{ selectedCategory }}<br>
availableSkills.length: {{ availableSkills.length }}<br>
selectedSkills.length: {{ selectedSkills.length }}
</div>
<div class="skills-content">
<!-- Skill Categories -->
<div class="categories-section">
<h3>Skill Categories</h3>
<div class="categories-grid">
<div
v-for="category in skillCategories"
:key="category.name"
:class="['category-card', { active: selectedCategory === category.name }]"
@click="selectCategory(category.name)"
v-if="category.name !== 'zauber'"
>
<div class="category-header">
<h4>{{ category.display_name }}</h4>
<div class="points-info">
<span class="remaining">{{ category.points }}</span> /
<span class="total">{{ category.max_points }}</span>
<!-- Left Column: Categories and Skills -->
<div class="left-column">
<!-- Skill Categories -->
<div class="categories-section">
<h3>Skill Categories</h3>
<div v-if="skillCategories.length === 0" class="no-categories">
<p>No skill categories available. Loading...</p>
</div>
<div v-else class="categories-grid">
<div
v-for="category in skillCategories"
:key="category.name"
:class="['category-card', { active: selectedCategory === category.name }]"
@click="selectCategory(category.name)"
v-if="category.name !== 'zauber'"
>
<div class="category-header">
<h4>{{ category.display_name }}</h4>
<div class="points-info">
<span class="remaining">{{ category.points }}</span> /
<span class="total">{{ category.max_points }}</span>
</div>
</div>
<div class="progress-bar">
<div
class="progress-fill"
:style="{ width: ((category.max_points - category.points) / category.max_points * 100) + '%' }"
></div>
</div>
</div>
<div class="progress-bar">
<div
class="progress-fill"
:style="{ width: ((category.max_points - category.points) / category.max_points * 100) + '%' }"
></div>
</div>
</div>
</div>
</div>
<!-- Skills List for Selected Category -->
<div v-if="selectedCategory" class="skills-section">
<h3>{{ getCategoryDisplayName(selectedCategory) }} Skills</h3>
<div class="skills-list">
<div
v-for="skill in availableSkills"
:key="skill.name"
class="skill-item"
>
<div class="skill-info">
<span class="skill-name">{{ skill.name }}</span>
<span class="skill-cost">Cost: {{ skill.cost }} EP</span>
</div>
<button
@click="addSkill(skill)"
:disabled="!canAddSkill(skill)"
class="add-btn"
>
Add
</button>
</div>
</div>
</div>
<!-- Selected Skills -->
<div class="selected-section">
<h3>Selected Skills</h3>
<div v-if="selectedSkills.length > 0" class="selected-skills">
<div class="selected-list">
<!-- Skills List for Selected Category -->
<div v-if="selectedCategory" class="skills-section">
<h3>{{ getCategoryDisplayName(selectedCategory) }} Skills</h3>
<div class="skills-list">
<div
v-for="skill in selectedSkills"
v-for="skill in availableSkills"
:key="skill.name"
class="selected-item"
class="skill-item"
>
<span class="item-name">{{ skill.name }}</span>
<span class="item-category">{{ skill.category }}</span>
<span class="item-cost">{{ skill.cost }} EP</span>
<button @click="removeSkill(skill)" class="remove-btn">×</button>
<div class="skill-info">
<span class="skill-name">{{ skill.name }}</span>
<span class="skill-cost">Cost: {{ skill.cost }} EP</span>
</div>
<button
@click="addSkill(skill)"
:disabled="!canAddSkill(skill)"
class="add-btn"
>
Add
</button>
</div>
</div>
</div>
</div>
<div v-if="selectedSkills.length === 0" class="no-selection">
No skills selected yet. Click on a category above to start selecting skills.
<!-- Right Column: Selected Skills -->
<div class="right-column">
<div class="selected-section">
<h3>Selected Skills</h3>
<div v-if="selectedSkills.length > 0" class="selected-skills">
<div class="selected-list">
<div
v-for="skill in selectedSkills"
:key="skill.name"
class="selected-item"
>
<span class="item-name">{{ skill.name }}</span>
<span class="item-category">{{ skill.category }}</span>
<span class="item-cost">{{ skill.cost }} EP</span>
<button @click="removeSkill(skill)" class="remove-btn">×</button>
</div>
</div>
</div>
<div v-if="selectedSkills.length === 0" class="no-selection">
No skills selected yet. Click on a category above to start selecting skills.
</div>
</div>
</div>
</div>
@@ -116,12 +135,17 @@ export default {
}
},
async created() {
console.log('CharacterSkills created, sessionData:', this.sessionData)
console.log('CharacterSkills created, skillCategories:', this.skillCategories)
// Initialize with session data
if (this.sessionData.skills) {
this.selectedSkills = [...this.sessionData.skills]
console.log('Initialized selectedSkills:', this.selectedSkills)
}
this.updateCategoryPoints()
console.log('Updated skillCategories after points update:', this.skillCategories)
},
methods: {
async selectCategory(categoryName) {
@@ -238,11 +262,23 @@ export default {
.skills-content {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-columns: 2fr 1fr;
gap: 30px;
margin-bottom: 30px;
}
.left-column {
display: flex;
flex-direction: column;
gap: 20px;
}
.right-column {
position: sticky;
top: 20px;
height: fit-content;
}
.categories-section h3, .skills-section h3, .spells-section h3, .selected-section h3 {
margin-bottom: 15px;
color: #333;
@@ -254,6 +290,13 @@ export default {
gap: 15px;
}
.no-categories {
text-align: center;
padding: 20px;
color: #666;
font-style: italic;
}
.category-card {
padding: 15px;
border: 2px solid #ddd;
@@ -386,10 +429,10 @@ export default {
}
.selected-section {
grid-column: span 2;
padding: 20px;
background-color: #f9f9f9;
border-radius: 8px;
height: fit-content;
}
.selected-skills, .selected-spells {