WIP Char create get skills

und Docker stuff für Production deployment
This commit is contained in:
2025-08-14 23:25:01 +02:00
parent 32375bea76
commit 83286c8627
12 changed files with 1565 additions and 450 deletions
+371 -17
View File
@@ -1996,8 +1996,6 @@ func GetRewardTypesOld(c *gin.Context) {
// GetAvailableSkillsNewSystem gibt alle verfügbaren Fertigkeiten mit Lernkosten zurück (POST mit LernCostRequest)
func GetAvailableSkillsNewSystem(c *gin.Context) {
characterID := c.Param("id")
// Parse LernCostRequest aus POST body
var baseRequest gsmaster.LernCostRequest
if err := c.ShouldBindJSON(&baseRequest); err != nil {
@@ -2005,11 +2003,23 @@ func GetAvailableSkillsNewSystem(c *gin.Context) {
return
}
// For character creation (char_id = 0), we don't need to load an existing character
var character models.Char
if err := database.DB.Preload("Fertigkeiten").Preload("Erfahrungsschatz").Preload("Vermoegen").First(&character, characterID).Error; err != nil {
respondWithError(c, http.StatusNotFound, "Character not found")
return
learnedSkills := make(map[string]bool)
if baseRequest.CharId != 0 {
// Load existing character and their learned skills
if err := database.DB.Preload("Fertigkeiten").Preload("Erfahrungsschatz").Preload("Vermoegen").First(&character, baseRequest.CharId).Error; err != nil {
respondWithError(c, http.StatusNotFound, "Character not found")
return
}
// Create map of learned skills for existing character
for _, skill := range character.Fertigkeiten {
learnedSkills[skill.Name] = true
}
}
// For character creation (char_id = 0), learnedSkills remains empty
// Hole alle verfügbaren Fertigkeiten aus der gsmaster Datenbank, aber filtere Placeholder aus
var allSkills []models.Skill
@@ -2025,12 +2035,6 @@ func GetAvailableSkillsNewSystem(c *gin.Context) {
}
*/
// Erstelle eine Map der bereits gelernten Fertigkeiten
learnedSkills := make(map[string]bool)
for _, skill := range character.Fertigkeiten {
learnedSkills[skill.Name] = true
}
// Organisiere Fertigkeiten nach Kategorien
skillsByCategory := make(map[string][]gin.H)
@@ -2046,7 +2050,7 @@ func GetAvailableSkillsNewSystem(c *gin.Context) {
// Erstelle LernCostRequest für diese Fertigkeit basierend auf der Basis-Anfrage
request := baseRequest
request.CharId = character.ID
request.CharId = baseRequest.CharId // Use the char_id from the request (0 for character creation)
request.Name = skill.Name
request.CurrentLevel = 0 // Nicht gelernt
request.TargetLevel = 1 // Auf Level 1 lernen
@@ -2054,9 +2058,19 @@ func GetAvailableSkillsNewSystem(c *gin.Context) {
request.Action = "learn"
// Erstelle SkillCostResultNew
characterClass := ""
characterID := "0"
if baseRequest.CharId != 0 {
// Use existing character data
characterID = fmt.Sprintf("%d", character.ID)
characterClass = getCharacterClassOld(&character)
}
// For character creation, we don't have a character class yet, use empty string
levelResult := gsmaster.SkillCostResultNew{
CharacterID: fmt.Sprintf("%d", character.ID),
CharacterClass: getCharacterClassOld(&character),
CharacterID: characterID,
CharacterClass: characterClass,
SkillName: skill.Name,
TargetLevel: 1,
}
@@ -2065,7 +2079,7 @@ func GetAvailableSkillsNewSystem(c *gin.Context) {
remainingGold := request.UseGold
// Hole die vollständigen Skill-Informationen für die Kostenberechnung
skillLearningInfo, err := models.GetSkillCategoryAndDifficultyNewSystem(skill.Name, getCharacterClassOld(&character))
skillLearningInfo, err := models.GetSkillCategoryAndDifficultyNewSystem(skill.Name, characterClass)
if err != nil {
// Fallback für unbekannte Skills
skillLearningInfo = &models.SkillLearningInfo{
@@ -3164,11 +3178,351 @@ func GetSkillCategoriesWithPoints(c *gin.Context) {
{Name: "wissen", DisplayName: "Wissen", Points: 100, MaxPoints: 100},
{Name: "kampf", DisplayName: "Kampf", Points: 80, MaxPoints: 80},
{Name: "korper", DisplayName: "Körper", Points: 120, MaxPoints: 120},
{Name: "gesellschaft", DisplayName: "Gesellschaft", Points: 60, MaxPoints: 60},
{Name: "natur", DisplayName: "Natur", Points: 90, MaxPoints: 90},
{Name: "unterwelt", DisplayName: "Unterwelt", Points: 40, MaxPoints: 40},
{Name: "zauber", DisplayName: "Zauber", Points: 200, MaxPoints: 200},
}
c.JSON(http.StatusOK, gin.H{"categories": categories})
}
// LearningPointsData repräsentiert die Lernpunkte und typischen Fertigkeiten einer Charakterklasse
type LearningPointsData struct {
ClassName string `json:"class_name"`
ClassCode string `json:"class_code"`
LearningPoints map[string]int `json:"learning_points"` // Kategorie -> Lernpunkte
WeaponPoints int `json:"weapon_points"` // Waffenlernpunkte
SpellPoints int `json:"spell_points"` // Zauberlerneinheiten (falls vorhanden)
TypicalSkills []TypicalSkill `json:"typical_skills"` // Typische Fertigkeiten
TypicalSpells []string `json:"typical_spells"` // Typische Zauber (falls vorhanden)
}
// TypicalSkill repräsentiert eine typische Fertigkeit mit Bonus
type TypicalSkill struct {
Name string `json:"name"`
Bonus int `json:"bonus"`
Attribute string `json:"attribute"` // Zugehöriges Attribut (z.B. "Gs", "In")
Notes string `json:"notes"` // Zusätzliche Notizen
}
// GetCharacterClassLearningPoints gibt die Lernpunkte und typischen Fertigkeiten für eine Charakterklasse zurück
func GetCharacterClassLearningPoints(c *gin.Context) {
className := c.Query("class")
if className == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Charakterklassen-Name ist erforderlich (Parameter 'class')"})
return
}
stand := c.Query("stand") // Optional: Unfreie, Volk, Mittelschicht, Adel
// Hole die Lernpunkte-Daten für die Klasse
learningData, err := getLearningPointsForClass(className, stand)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Charakterklasse nicht gefunden oder nicht unterstützt: " + err.Error()})
return
}
c.JSON(http.StatusOK, learningData)
}
// 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",
}
classCode := classMapping[className]
if classCode == "" {
classCode = className // Falls der Name bereits ein Code ist
}
// Definiere die Lernpunkte-Daten basierend auf Lerntabelle_Erstellung.md
var data *LearningPointsData
switch classCode {
case "As", "Assassine":
data = &LearningPointsData{
ClassName: "Assassine",
ClassCode: "As",
LearningPoints: map[string]int{
"Alltag": 1,
"Halbwelt": 2,
"Sozial": 4,
"Unterwelt": 8,
},
WeaponPoints: 24,
TypicalSkills: []TypicalSkill{
{Name: "Meucheln", Bonus: 8, Attribute: "Gs", Notes: ""},
},
}
case "Bb", "Barbar":
data = &LearningPointsData{
ClassName: "Barbar",
ClassCode: "Bb",
LearningPoints: map[string]int{
"Alltag": 2,
"Freiland": 4,
"Kampf": 1,
"Körper": 2,
},
WeaponPoints: 24,
TypicalSkills: []TypicalSkill{
{Name: "Spurensuche", Bonus: 8, Attribute: "In", Notes: "in Heimatlandschaft"},
{Name: "Überleben", Bonus: 8, Attribute: "In", Notes: "in Heimatlandschaft"},
},
}
case "Gl", "Glücksritter":
data = &LearningPointsData{
ClassName: "Glücksritter",
ClassCode: "Gl",
LearningPoints: map[string]int{
"Alltag": 2,
"Halbwelt": 3,
"Sozial": 8,
},
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,
},
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,
},
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,
},
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,
},
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,
},
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,
},
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,
},
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,
},
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,
},
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,
},
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,
},
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,
},
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)
}
// Bonus-Lernpunkte basierend auf Stand hinzufügen
if stand != "" && data != nil {
standBonus := getStandBonusPoints(stand)
// Füge die Stand-Bonuspunkte zu den normalen Lernpunkten hinzu
for category, bonus := range standBonus {
if currentPoints, exists := data.LearningPoints[category]; exists {
data.LearningPoints[category] = currentPoints + bonus
} else {
// Falls die Kategorie noch nicht existiert, füge sie hinzu
data.LearningPoints[category] = bonus
}
}
// Speichere die Stand-Bonuspunkte auch separat für Referenz
//data.StandPoints = standBonus
}
return data, nil
}
// getStandBonusPoints gibt die Bonus-Lernpunkte basierend auf dem Stand zurück
func getStandBonusPoints(stand string) map[string]int {
switch stand {
case "Unfreie":
return map[string]int{"Halbwelt": 2}
case "Volk":
return map[string]int{"Alltag": 2}
case "Mittelschicht":
return map[string]int{"Wissen": 2}
case "Adel":
return map[string]int{"Sozial": 2}
default:
return make(map[string]int)
}
}
+373
View File
@@ -0,0 +1,373 @@
package character
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)
func TestGetCharacterClassLearningPoints(t *testing.T) {
// Setup Gin in test mode
gin.SetMode(gin.TestMode)
router := gin.New()
router.GET("/api/characters/classes/learning-points", GetCharacterClassLearningPoints)
tests := []struct {
name string
classParam string
standParam string
expectedStatus int
expectError bool
expectedClass string
checkWeapons bool
expectedWeapons int
checkSpells bool
expectedSpells int
}{
{
name: "Valid Hexer class without stand",
classParam: "Hexer",
standParam: "",
expectedStatus: http.StatusOK,
expectError: false,
expectedClass: "Hexer",
checkWeapons: true,
expectedWeapons: 2,
checkSpells: true,
expectedSpells: 6,
},
{
name: "Valid Hexer class with Volk stand",
classParam: "Hexer",
standParam: "Volk",
expectedStatus: http.StatusOK,
expectError: false,
expectedClass: "Hexer",
checkWeapons: true,
expectedWeapons: 2,
checkSpells: true,
expectedSpells: 6,
},
{
name: "Valid Krieger class with Adel stand",
classParam: "Krieger",
standParam: "Adel",
expectedStatus: http.StatusOK,
expectError: false,
expectedClass: "Krieger",
checkWeapons: true,
expectedWeapons: 36,
checkSpells: false,
},
{
name: "Valid Magier class",
classParam: "Magier",
standParam: "",
expectedStatus: http.StatusOK,
expectError: false,
expectedClass: "Magier",
checkWeapons: true,
expectedWeapons: 2,
checkSpells: true,
expectedSpells: 7,
},
{
name: "Valid Spitzbube class",
classParam: "Spitzbube",
standParam: "",
expectedStatus: http.StatusOK,
expectError: false,
expectedClass: "Spitzbube",
checkWeapons: true,
expectedWeapons: 20,
checkSpells: false,
},
{
name: "Valid Waldläufer class",
classParam: "Waldläufer",
standParam: "",
expectedStatus: http.StatusOK,
expectError: false,
expectedClass: "Waldläufer",
checkWeapons: true,
expectedWeapons: 20,
checkSpells: false,
},
{
name: "Invalid class should return error",
classParam: "InvalidClass",
standParam: "",
expectedStatus: http.StatusNotFound,
expectError: true,
},
{
name: "Missing class parameter should return error",
classParam: "",
standParam: "",
expectedStatus: http.StatusBadRequest,
expectError: true,
},
{
name: "Valid class with invalid stand should still work",
classParam: "Hexer",
standParam: "InvalidStand",
expectedStatus: http.StatusOK,
expectError: false,
expectedClass: "Hexer",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Build request URL
url := "/api/characters/classes/learning-points"
if tt.classParam != "" || tt.standParam != "" {
url += "?"
if tt.classParam != "" {
url += "class=" + tt.classParam
}
if tt.standParam != "" {
if tt.classParam != "" {
url += "&"
}
url += "stand=" + tt.standParam
}
}
// Create request
req, err := http.NewRequest("GET", url, nil)
assert.NoError(t, err)
// Create response recorder
w := httptest.NewRecorder()
// Perform request
router.ServeHTTP(w, req)
// Check status code
assert.Equal(t, tt.expectedStatus, w.Code)
if tt.expectError {
// For error cases, check that we have an error message
var response map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Contains(t, response, "error")
} else {
// For success cases, check the response structure
var response LearningPointsData
err := json.Unmarshal(w.Body.Bytes(), &response)
assert.NoError(t, err)
// Check basic fields
assert.Equal(t, tt.expectedClass, response.ClassName)
assert.NotEmpty(t, response.ClassCode)
assert.NotNil(t, response.LearningPoints)
assert.NotNil(t, response.TypicalSkills)
// Check weapon points if specified
if tt.checkWeapons {
assert.Equal(t, tt.expectedWeapons, response.WeaponPoints)
}
// Check spell points if specified
if tt.checkSpells {
assert.Equal(t, tt.expectedSpells, response.SpellPoints)
}
// Check that learning points are not empty
assert.NotEmpty(t, response.LearningPoints)
// Check that typical skills are not empty
assert.NotEmpty(t, response.TypicalSkills)
}
})
}
}
func TestGetLearningPointsForClass(t *testing.T) {
tests := []struct {
name string
className string
stand string
expectError bool
expectedClass string
expectedCode string
checkPoints map[string]int
checkStand map[string]int
}{
{
name: "Hexer class data",
className: "Hexer",
stand: "",
expectError: false,
expectedClass: "Hexer",
expectedCode: "Hx",
checkPoints: map[string]int{
"Alltag": 3,
"Sozial": 2,
"Wissen": 2,
},
},
{
name: "Hexer with Volk stand",
className: "Hexer",
stand: "Volk",
expectError: false,
expectedClass: "Hexer",
expectedCode: "Hx",
checkPoints: map[string]int{
"Alltag": 5, // Base 3 + Volk bonus 2 = 5
"Sozial": 2,
"Wissen": 2,
},
checkStand: map[string]int{
"Alltag": 2,
},
},
{
name: "Krieger class data",
className: "Krieger",
stand: "",
expectError: false,
expectedClass: "Krieger",
expectedCode: "Kr",
checkPoints: map[string]int{
"Alltag": 2,
"Kampf": 3,
"Körper": 1,
},
},
{
name: "Krieger with Adel stand",
className: "Krieger",
stand: "Adel",
expectError: false,
expectedClass: "Krieger",
expectedCode: "Kr",
checkPoints: map[string]int{
"Alltag": 2,
"Kampf": 3,
"Körper": 1,
"Sozial": 2, // Stand bonus adds this new category
},
checkStand: map[string]int{
"Sozial": 2,
},
},
{
name: "Invalid class should return error",
className: "InvalidClass",
stand: "",
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
data, err := getLearningPointsForClass(tt.className, tt.stand)
if tt.expectError {
assert.Error(t, err)
assert.Nil(t, data)
} else {
assert.NoError(t, err)
assert.NotNil(t, data)
// Check basic properties
assert.Equal(t, tt.expectedClass, data.ClassName)
assert.Equal(t, tt.expectedCode, data.ClassCode)
// Check learning points
if tt.checkPoints != nil {
for category, expectedPoints := range tt.checkPoints {
actualPoints, exists := data.LearningPoints[category]
assert.True(t, exists, "Category %s should exist", category)
assert.Equal(t, expectedPoints, actualPoints, "Points for category %s", category)
}
}
// Check that we have some typical skills
assert.NotEmpty(t, data.TypicalSkills)
// Validate typical skills structure
for _, skill := range data.TypicalSkills {
assert.NotEmpty(t, skill.Name)
assert.NotEmpty(t, skill.Attribute)
assert.GreaterOrEqual(t, skill.Bonus, 0)
}
}
})
}
}
func TestGetStandBonusPoints(t *testing.T) {
tests := []struct {
name string
stand string
expected map[string]int
}{
{
name: "Unfreie stand",
stand: "Unfreie",
expected: map[string]int{"Halbwelt": 2},
},
{
name: "Volk stand",
stand: "Volk",
expected: map[string]int{"Alltag": 2},
},
{
name: "Mittelschicht stand",
stand: "Mittelschicht",
expected: map[string]int{"Wissen": 2},
},
{
name: "Adel stand",
stand: "Adel",
expected: map[string]int{"Sozial": 2},
},
{
name: "Invalid stand",
stand: "Invalid",
expected: map[string]int{},
},
{
name: "Empty stand",
stand: "",
expected: map[string]int{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := getStandBonusPoints(tt.stand)
assert.Equal(t, tt.expected, result)
})
}
}
// Test all character classes to ensure they're properly defined
func TestAllCharacterClassesAreDefined(t *testing.T) {
expectedClasses := []string{
"Assassine", "Barbar", "Glücksritter", "Händler", "Krieger", "Spitzbube", "Waldläufer",
"Barde", "Ordenskrieger", "Druide", "Hexer", "Magier", "Priester Beschützer", "Priester Streiter", "Schamane",
}
for _, className := range expectedClasses {
t.Run("Class_"+className, func(t *testing.T) {
data, err := getLearningPointsForClass(className, "")
assert.NoError(t, err, "Class %s should be defined", className)
assert.NotNil(t, data, "Class %s should return data", className)
assert.Equal(t, className, data.ClassName)
assert.NotEmpty(t, data.ClassCode)
assert.NotEmpty(t, data.LearningPoints)
assert.GreaterOrEqual(t, data.WeaponPoints, 0)
})
}
}
+1
View File
@@ -65,6 +65,7 @@ func RegisterRoutes(r *gin.RouterGroup) {
// Reference Data für Character Creation
charGrp.GET("/races", GetRaces) // Verfügbare Rassen
charGrp.GET("/classes", GetCharacterClasses) // Verfügbare Klassen
charGrp.GET("/classes/learning-points", GetCharacterClassLearningPoints) // Lernpunkte für Charakterklasse
charGrp.GET("/origins", GetOrigins) // Verfügbare Herkünfte
charGrp.GET("/beliefs", SearchBeliefs) // Glaube-Suche
charGrp.GET("/skill-categories-with-points", GetSkillCategoriesWithPoints) // Kategorien mit Lernpunkten
+110
View File
@@ -0,0 +1,110 @@
# Lerneinheiten bei Spielbeginn
Kämpfer
Assassine (As)
Typische Fertigkeit: Meucheln+8 (Gs)
Alltag: 1 LE, Halbwelt: 2 LE, Sozial: 4 LE, Unterwelt:
8 LE
Waffen: 24 LE
Barbar (Bb)
Typische Fertigkeit: Spurensuche+8 (In) und Überleben+8 (In) in Heimatlandschaft*
Alltag: 2 LE, Freiland: 4 LE, Kampf: 1 LE, Körper: 2 LE
Waffen: 24 LE
Glücksritter (Gl)
Typische Fertigkeit: Fechten+5 (Gs) oder beidhändiger
Kampf+5 (Gs)
Alltag: 2 LE, Halbwelt: 3 LE, Sozial: 8 LE
Waffen: 24 LE
Händler (Hä)
Typische Fertigkeit: Geschäftssinn+8 (In)
Alltag: 4 LE, Sozial: 8 LE, Wissen: 4 LE
Waffen: 20 LE
Krieger (Kr)
Typische Fertigkeit: Kampf in Vollrüstung+5 (St)
Alltag: 2 LE, Kampf: 3 LE, Körper: 1 LE
Waffen: 36 LE
Spitzbube (Sp)
Typische Fertigkeit: Fallenmechanik+8 (Gs) oder Geschäftssinn+8 (In)
Alltag: 2 LE, Halbwelt: 6 LE, Unterwelt: 12 LE
Waffen: 20 LE
Waldläufer (Wa)
Typische Fertigkeit: Scharfschießen+5 (Gs)
Alltag: 1 LE, Freiland: 11 LE, Körper: 4 LE
Waffen: 20 LE
Zauberkundige Kämpfer
Barde (Ba)
Typische Fertigkeit: Musizieren+12 (Gs) und Landeskunde+8 (In) für Heimat
Alltag: 2 LE, Sozial: 4 LE, Wissen: 4 LE
Waffen: 16 LE
Zauber: 3 LE (Zauberlieder)
Ordenskrieger (Or)
Typische Fertigkeit: Athletik+8 (St) oder Meditieren+8
(Wk)
Alltag: 2 LE, Kampf: 3 LE, Wissen: 2 LE
Waffen: 18 LE
Zauber: 3 LE (Wundertaten)
Zauberer
Druide (Dr)
Typische Fertigkeit: Pflanzenkunde+8 (In) und Schreiben+12 (In) für Ogam-Zeichen
Alltag: 2 LE, Freiland: 4 LE, Wissen: 2 LE
Waffen: 6 LE
Zauber: 5 LE (Dweomer)
Typischer Zauber: Tiere rufen
Hexer (Hx)
Typische Fertigkeit: Gassenwissen+8 (In) oder Verführen+8 (pA)
Alltag: 3 LE, Sozial: 2 LE, Wissen: 2 LE
Waffen: 2 LE
Zauber: 6 LE (Beherrschen, Verändern)
Typischer Zauber: Verwünschen oder Binden des Vertauten
Magier (Ma)
Typische Fertigkeit: Zauberkunde+8 (In) und Schreiben+12 (In) für Muttersprache**
Alltag: 1 LE, Wissen: 5 LE
Waffen: 2 LE
Zauber: 7 LE (beliebig außer Dweomer, Wundertaten,
Zauberlieder)
Typischer Zauber: Erkennen von Zauberei
Priester, Beschützer (PB)
Typische Fertigkeit: Menschenkenntnis+8 (In) und
Schrei ben+12 (In) für Muttersprache**
Alltag: 2 LE, Sozial: 2 LE, Wissen: 3 LE
Waffen: 6 LE
Zauber: 5 LE (Wundertaten)
Typischer Zauber: Heilen von Wunden
Priester, Streiter (PS)
Typische Fertigkeit: Erste Hilfe+8 (Gs) und Schreiben+12 (In) für Muttersprache**
Alltag: 3 LE, Kampf: 2 LE, Wissen: 2 LE
Waffen: 8 LE
Zauber: 5 LE (Wundertaten)
Typischer Zauber: Bannen von Finsterwerk oder Strahlender Panzer
Schamane (Sc)
Typische Fertigkeit: Tierkunde+8 (In) und Überleben+8 (In) in Heimatlandschaft*
Alltag: 2 LE, Körper: 4 LE, Wissen: 2 LE
Waffen: 6 LE
Zauber: 5 LE (Dweomer, Wundertaten)
Typischer Zauber: Austreibung des Bösen oder Bannen von Gift
*: Gebirge, Steppe oder Wald - je nach Lebensraum seines Stammes
**: abhängig von der Heimat (s. S. 127 für eine Liste der Sprachen)
Standesfertigkeiten
Unfreie: 2 LE für Halbwelt
Volk: 2 LE für Alltag
Mittelschicht: 2 LE für Wissen
Adel: 2 LE für Sozial, leicht
-18
View File
@@ -1,18 +0,0 @@
package main
import (
"bamort/config"
"fmt"
)
func main() {
// Teste, ob die globale Konfigurationsvariable funktioniert
fmt.Printf("Globale Konfiguration:\n")
fmt.Printf("Environment: %s\n", config.Cfg.Environment)
fmt.Printf("DatabaseType: %s\n", config.Cfg.DatabaseType)
fmt.Printf("DatabaseURL: %s\n", config.Cfg.DatabaseURL)
fmt.Printf("ServerPort: %s\n", config.Cfg.ServerPort)
fmt.Printf("DebugMode: %v\n", config.Cfg.DebugMode)
fmt.Printf("LogLevel: %s\n", config.Cfg.LogLevel)
fmt.Printf("Testing: %s\n", config.Cfg.DevTesting)
}