CalculateStaticFieldsLogic
calculation of Bonus values defence zaubern and other values that depend on base stats
This commit is contained in:
@@ -23,6 +23,7 @@ type CalculateStaticFieldsRequest struct {
|
||||
// Charakterdaten
|
||||
Rasse string `json:"rasse" binding:"required"`
|
||||
Typ string `json:"typ" binding:"required"`
|
||||
Grad int `json:"grad" binding:"omitempty,min=1,max=100"` // Charaktergrad (optional, default 1)
|
||||
}
|
||||
|
||||
// StaticFieldsResponse für berechnete Felder ohne Würfelwürfe
|
||||
@@ -69,17 +70,8 @@ type RolledFieldResponse struct {
|
||||
Details interface{} `json:"details"`
|
||||
}
|
||||
|
||||
// CalculateStaticFields berechnet alle Felder ohne Würfelwürfe
|
||||
func CalculateStaticFields(c *gin.Context) {
|
||||
var req CalculateStaticFieldsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.Error("Fehler beim Parsen der Static Fields Anfrage: %v", err)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Ungültige Anfrage"})
|
||||
return
|
||||
}
|
||||
|
||||
logger.Info("Berechne statische Felder für %s %s", req.Rasse, req.Typ)
|
||||
|
||||
// CalculateStaticFieldsLogic contains the pure business logic for static field calculation
|
||||
func CalculateStaticFieldsLogic(req CalculateStaticFieldsRequest) StaticFieldsResponse {
|
||||
response := StaticFieldsResponse{}
|
||||
|
||||
// Ausdauer Bonus: Ko/10 + St/20
|
||||
@@ -103,20 +95,42 @@ func CalculateStaticFields(c *gin.Context) {
|
||||
// Resistenz Bonus Geist
|
||||
response.ResistenzBonusGeist = calculateResistenzBonusGeist(req.In, req.Rasse, req.Typ)
|
||||
|
||||
// Finale Resistenzwerte
|
||||
response.ResistenzKoerper = 11 + response.ResistenzBonusKoerper
|
||||
response.ResistenzGeist = 11 + response.ResistenzBonusGeist
|
||||
// Grad standardmäßig auf 1 setzen wenn nicht angegeben
|
||||
grad := req.Grad
|
||||
if grad < 1 {
|
||||
grad = 1
|
||||
}
|
||||
|
||||
// Finale Kampfwerte
|
||||
response.Abwehr = 11 + response.AbwehrBonus
|
||||
response.Zaubern = 11 + response.ZauberBonus
|
||||
// Finale Resistenzwerte (mit Gradbonus)
|
||||
response.ResistenzKoerper = getResistenzBaseByGrade(grad) // + response.ResistenzBonusKoerper
|
||||
response.ResistenzGeist = getResistenzBaseByGrade(grad) //+ response.ResistenzBonusGeist
|
||||
|
||||
// Finale Kampfwerte (mit Gradbonus)
|
||||
response.Abwehr = getAbwehrBaseByGrade(grad) //+ response.AbwehrBonus
|
||||
response.Zaubern = getZaubernBaseByGrade(grad) //+ response.ZauberBonus
|
||||
|
||||
// Raufen: (St + GW)/20 + angriffs_bonus + Rassenboni
|
||||
raceBonus := 0
|
||||
if req.Rasse == "Zwerge" {
|
||||
if req.Rasse == "Zwerg" {
|
||||
raceBonus = 1
|
||||
}
|
||||
response.Raufen = (req.St+req.Gw)/20 + response.AngriffsBonus + raceBonus
|
||||
response.Raufen = (req.St+req.Gw)/20 + raceBonus //+ response.AngriffsBonus
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
// CalculateStaticFields berechnet alle Felder ohne Würfelwürfe (HTTP Handler)
|
||||
func CalculateStaticFields(c *gin.Context) {
|
||||
var req CalculateStaticFieldsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.Error("Fehler beim Parsen der Static Fields Anfrage: %v", err)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Ungültige Anfrage"})
|
||||
return
|
||||
}
|
||||
|
||||
logger.Info("Berechne statische Felder für %s %s", req.Rasse, req.Typ)
|
||||
|
||||
response := CalculateStaticFieldsLogic(req)
|
||||
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
@@ -291,19 +305,17 @@ func calculateAttributeBonus(value int) int {
|
||||
|
||||
// calculateResistenzBonusKoerper berechnet den Körper-Resistenzbonus
|
||||
func calculateResistenzBonusKoerper(ko int, rasse string, typ string) int {
|
||||
bonus := 0
|
||||
bonus := calculateAttributeBonus(ko)
|
||||
|
||||
if rasse == "Menschen" {
|
||||
bonus = calculateAttributeBonus(ko)
|
||||
} else {
|
||||
switch rasse {
|
||||
case "Elfen":
|
||||
bonus = 2
|
||||
case "Gnome", "Halblinge":
|
||||
bonus = 4
|
||||
case "Zwerge":
|
||||
bonus = 3
|
||||
}
|
||||
switch rasse {
|
||||
//case "Mensch":
|
||||
// bonus = calculateAttributeBonus(ko)
|
||||
case "Elf":
|
||||
bonus = 2
|
||||
case "Gnom", "Halbling":
|
||||
bonus = 4
|
||||
case "Zwerg":
|
||||
bonus = 3
|
||||
}
|
||||
|
||||
// Klassenmodifikator
|
||||
@@ -318,19 +330,17 @@ func calculateResistenzBonusKoerper(ko int, rasse string, typ string) int {
|
||||
|
||||
// calculateResistenzBonusGeist berechnet den Geist-Resistenzbonus
|
||||
func calculateResistenzBonusGeist(in int, rasse string, typ string) int {
|
||||
bonus := 0
|
||||
bonus := calculateAttributeBonus(in)
|
||||
|
||||
if rasse == "Menschen" {
|
||||
bonus = calculateAttributeBonus(in)
|
||||
} else {
|
||||
switch rasse {
|
||||
case "Elfen":
|
||||
bonus = 2
|
||||
case "Gnome", "Halblinge":
|
||||
bonus = 4
|
||||
case "Zwerge":
|
||||
bonus = 3
|
||||
}
|
||||
switch rasse {
|
||||
//case "Mensch":
|
||||
//bonus = calculateAttributeBonus(in)
|
||||
case "Elf":
|
||||
bonus = 2
|
||||
case "Gnom", "Halbling":
|
||||
bonus = 4
|
||||
case "Zwerg":
|
||||
bonus = 3
|
||||
}
|
||||
|
||||
// Klassenmodifikator (nur Zauberer bekommen Geist-Bonus)
|
||||
@@ -354,7 +364,7 @@ func isKaempfer(typ string) bool {
|
||||
|
||||
// isZauberer prüft ob eine Klasse als Zauberer gilt
|
||||
func isZauberer(typ string) bool {
|
||||
zaubererKlassen := []string{"Magier", "Druide", "Priester", "Schamane"}
|
||||
zaubererKlassen := []string{"Magier", "Druide", "Priester", "Schamane", "Hexer"}
|
||||
for _, z := range zaubererKlassen {
|
||||
if z == typ {
|
||||
return true
|
||||
@@ -400,3 +410,51 @@ func getMovementBaseAndFormula(rasse string) (int, string) {
|
||||
return 16, "4d3 + 16"
|
||||
}
|
||||
}
|
||||
|
||||
// getAbwehrBaseByGrade gibt den Basis-Fertigkeitswert für Abwehr nach Grad zurück
|
||||
// Grad: 1->11, 2->12, 5->13, 10->14, 15->15, 20->16, 25->17, 30->18
|
||||
func getAbwehrBaseByGrade(grad int) int {
|
||||
if grad >= 30 {
|
||||
return 18
|
||||
} else if grad >= 25 {
|
||||
return 17
|
||||
} else if grad >= 20 {
|
||||
return 16
|
||||
} else if grad >= 15 {
|
||||
return 15
|
||||
} else if grad >= 10 {
|
||||
return 14
|
||||
} else if grad >= 5 {
|
||||
return 13
|
||||
} else if grad >= 2 {
|
||||
return 12
|
||||
}
|
||||
return 11
|
||||
}
|
||||
|
||||
// getResistenzBaseByGrade gibt den Basis-Fertigkeitswert für Resistenz nach Grad zurück
|
||||
// Verwendet dieselbe Tabelle wie Abwehr
|
||||
func getResistenzBaseByGrade(grad int) int {
|
||||
return getAbwehrBaseByGrade(grad)
|
||||
}
|
||||
|
||||
// getZaubernBaseByGrade gibt den Basis-Fertigkeitswert für Zaubern nach Grad zurück
|
||||
// Grad: 1->11, 2->12, 4->13, 6->14, 8->15, 10->16, 15->17, 20->18
|
||||
func getZaubernBaseByGrade(grad int) int {
|
||||
if grad >= 20 {
|
||||
return 18
|
||||
} else if grad >= 15 {
|
||||
return 17
|
||||
} else if grad >= 10 {
|
||||
return 16
|
||||
} else if grad >= 8 {
|
||||
return 15
|
||||
} else if grad >= 6 {
|
||||
return 14
|
||||
} else if grad >= 4 {
|
||||
return 13
|
||||
} else if grad >= 2 {
|
||||
return 12
|
||||
}
|
||||
return 11
|
||||
}
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
package character
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetGradeBonus_Abwehr(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
grad int
|
||||
expected int
|
||||
}{
|
||||
{"Grad 1", 1, 11},
|
||||
{"Grad 2", 2, 12},
|
||||
{"Grad 3", 3, 12},
|
||||
{"Grad 4", 4, 12},
|
||||
{"Grad 5", 5, 13},
|
||||
{"Grad 9", 9, 13},
|
||||
{"Grad 10", 10, 14},
|
||||
{"Grad 14", 14, 14},
|
||||
{"Grad 15", 15, 15},
|
||||
{"Grad 19", 19, 15},
|
||||
{"Grad 20", 20, 16},
|
||||
{"Grad 24", 24, 16},
|
||||
{"Grad 25", 25, 17},
|
||||
{"Grad 29", 29, 17},
|
||||
{"Grad 30", 30, 18},
|
||||
{"Grad 35", 35, 18},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := getAbwehrBaseByGrade(tt.grad)
|
||||
if result != tt.expected {
|
||||
t.Errorf("Expected %d for Grad %d, got %d", tt.expected, tt.grad, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetGradeBonus_Resistenz(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
grad int
|
||||
expected int
|
||||
}{
|
||||
{"Grad 1", 1, 11},
|
||||
{"Grad 2", 2, 12},
|
||||
{"Grad 3", 3, 12},
|
||||
{"Grad 4", 4, 12},
|
||||
{"Grad 5", 5, 13},
|
||||
{"Grad 9", 9, 13},
|
||||
{"Grad 10", 10, 14},
|
||||
{"Grad 14", 14, 14},
|
||||
{"Grad 15", 15, 15},
|
||||
{"Grad 19", 19, 15},
|
||||
{"Grad 20", 20, 16},
|
||||
{"Grad 24", 24, 16},
|
||||
{"Grad 25", 25, 17},
|
||||
{"Grad 29", 29, 17},
|
||||
{"Grad 30", 30, 18},
|
||||
{"Grad 35", 35, 18},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := getResistenzBaseByGrade(tt.grad)
|
||||
if result != tt.expected {
|
||||
t.Errorf("Expected %d for Grad %d, got %d", tt.expected, tt.grad, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetGradeBonus_Zaubern(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
grad int
|
||||
expected int
|
||||
}{
|
||||
{"Grad 1", 1, 11},
|
||||
{"Grad 2", 2, 12},
|
||||
{"Grad 3", 3, 12},
|
||||
{"Grad 4", 4, 13},
|
||||
{"Grad 5", 5, 13},
|
||||
{"Grad 6", 6, 14},
|
||||
{"Grad 7", 7, 14},
|
||||
{"Grad 8", 8, 15},
|
||||
{"Grad 9", 9, 15},
|
||||
{"Grad 10", 10, 16},
|
||||
{"Grad 14", 14, 16},
|
||||
{"Grad 15", 15, 17},
|
||||
{"Grad 19", 19, 17},
|
||||
{"Grad 20", 20, 18},
|
||||
{"Grad 25", 25, 18},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := getZaubernBaseByGrade(tt.grad)
|
||||
if result != tt.expected {
|
||||
t.Errorf("Expected %d for Grad %d, got %d", tt.expected, tt.grad, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateStaticFieldsLogic_WithGrade(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
grad int
|
||||
expectedAbwehr int
|
||||
expectedResistenzBody int
|
||||
expectedResistenzMind int
|
||||
expectedZaubern int
|
||||
}{
|
||||
{
|
||||
name: "Grad 1",
|
||||
grad: 1,
|
||||
expectedAbwehr: 11, // base 11 + bonus 0
|
||||
expectedResistenzBody: 12, // base 11 + bonus 1 (Krieger)
|
||||
expectedResistenzMind: 11, // base 11 + bonus 0 (Krieger gets no mind bonus)
|
||||
expectedZaubern: 11, // base 11 + bonus 0
|
||||
},
|
||||
{
|
||||
name: "Grad 5",
|
||||
grad: 5,
|
||||
expectedAbwehr: 13, // base 13 + bonus 0
|
||||
expectedResistenzBody: 14, // base 13 + bonus 1 (Krieger)
|
||||
expectedResistenzMind: 13, // base 13 + bonus 0
|
||||
expectedZaubern: 13, // base 13 + bonus 0
|
||||
},
|
||||
{
|
||||
name: "Grad 10",
|
||||
grad: 10,
|
||||
expectedAbwehr: 14, // base 14 + bonus 0
|
||||
expectedResistenzBody: 15, // base 14 + bonus 1 (Krieger)
|
||||
expectedResistenzMind: 14, // base 14 + bonus 0
|
||||
expectedZaubern: 16, // base 16 + bonus 0
|
||||
},
|
||||
{
|
||||
name: "Grad 20",
|
||||
grad: 20,
|
||||
expectedAbwehr: 16, // base 16 + bonus 0
|
||||
expectedResistenzBody: 17, // base 16 + bonus 1 (Krieger)
|
||||
expectedResistenzMind: 16, // base 16 + bonus 0
|
||||
expectedZaubern: 18, // base 18 + bonus 0
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req := CalculateStaticFieldsRequest{
|
||||
St: 50,
|
||||
Gs: 50,
|
||||
Gw: 50,
|
||||
Ko: 50,
|
||||
In: 50,
|
||||
Zt: 50,
|
||||
Au: 50,
|
||||
Rasse: "Menschen",
|
||||
Typ: "Krieger",
|
||||
Grad: tt.grad,
|
||||
}
|
||||
|
||||
result := CalculateStaticFieldsLogic(req)
|
||||
|
||||
if result.Abwehr != tt.expectedAbwehr {
|
||||
t.Errorf("Expected Abwehr %d for Grad %d, got %d", tt.expectedAbwehr, tt.grad, result.Abwehr)
|
||||
}
|
||||
if result.ResistenzKoerper != tt.expectedResistenzBody {
|
||||
t.Errorf("Expected ResistenzKoerper %d for Grad %d, got %d", tt.expectedResistenzBody, tt.grad, result.ResistenzKoerper)
|
||||
}
|
||||
if result.ResistenzGeist != tt.expectedResistenzMind {
|
||||
t.Errorf("Expected ResistenzGeist %d for Grad %d, got %d", tt.expectedResistenzMind, tt.grad, result.ResistenzGeist)
|
||||
}
|
||||
if result.Zaubern != tt.expectedZaubern {
|
||||
t.Errorf("Expected Zaubern %d for Grad %d, got %d", tt.expectedZaubern, tt.grad, result.Zaubern)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package character
|
||||
|
||||
import (
|
||||
"bamort/database"
|
||||
"bamort/models"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDerivedValuesStorage(t *testing.T) {
|
||||
// Setup test environment
|
||||
original := os.Getenv("ENVIRONMENT")
|
||||
os.Setenv("ENVIRONMENT", "test")
|
||||
t.Cleanup(func() {
|
||||
if original != "" {
|
||||
os.Setenv("ENVIRONMENT", original)
|
||||
} else {
|
||||
os.Unsetenv("ENVIRONMENT")
|
||||
}
|
||||
})
|
||||
|
||||
// Setup test database
|
||||
database.SetupTestDB(true, true)
|
||||
defer database.ResetTestDB()
|
||||
|
||||
err := models.MigrateStructure()
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Run("Derived values are stored in database", func(t *testing.T) {
|
||||
// Create a character with derived values
|
||||
char := models.Char{
|
||||
BamortBase: models.BamortBase{
|
||||
Name: "Test Character",
|
||||
},
|
||||
UserID: 1,
|
||||
Rasse: "Mensch",
|
||||
Typ: "Krieger",
|
||||
Grad: 1,
|
||||
ResistenzKoerper: 12,
|
||||
ResistenzGeist: 11,
|
||||
Abwehr: 13,
|
||||
Zaubern: 10,
|
||||
Raufen: 14,
|
||||
}
|
||||
|
||||
// Save character
|
||||
err := char.Create()
|
||||
assert.NoError(t, err, "Should create character successfully")
|
||||
|
||||
// Reload character from database
|
||||
var loadedChar models.Char
|
||||
err = database.DB.First(&loadedChar, char.ID).Error
|
||||
assert.NoError(t, err, "Should load character from database")
|
||||
|
||||
// Verify derived values are persisted
|
||||
assert.Equal(t, 12, loadedChar.ResistenzKoerper, "ResistenzKoerper should be persisted")
|
||||
assert.Equal(t, 11, loadedChar.ResistenzGeist, "ResistenzGeist should be persisted")
|
||||
assert.Equal(t, 13, loadedChar.Abwehr, "Abwehr should be persisted")
|
||||
assert.Equal(t, 10, loadedChar.Zaubern, "Zaubern should be persisted")
|
||||
assert.Equal(t, 14, loadedChar.Raufen, "Raufen should be persisted")
|
||||
})
|
||||
}
|
||||
|
||||
func TestCalculateBonuses(t *testing.T) {
|
||||
// Setup test environment
|
||||
original := os.Getenv("ENVIRONMENT")
|
||||
os.Setenv("ENVIRONMENT", "test")
|
||||
t.Cleanup(func() {
|
||||
if original != "" {
|
||||
os.Setenv("ENVIRONMENT", original)
|
||||
} else {
|
||||
os.Unsetenv("ENVIRONMENT")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Calculate bonuses from character attributes", func(t *testing.T) {
|
||||
char := models.Char{
|
||||
BamortBase: models.BamortBase{ID: 1},
|
||||
Rasse: "Mensch",
|
||||
Typ: "Krieger",
|
||||
Eigenschaften: []models.Eigenschaft{
|
||||
{CharacterID: 1, Name: "St", Value: 85},
|
||||
{CharacterID: 1, Name: "Gs", Value: 70},
|
||||
{CharacterID: 1, Name: "Gw", Value: 65},
|
||||
{CharacterID: 1, Name: "Ko", Value: 75},
|
||||
{CharacterID: 1, Name: "In", Value: 50},
|
||||
{CharacterID: 1, Name: "Zt", Value: 30},
|
||||
},
|
||||
}
|
||||
|
||||
bonuses := char.CalculateBonuses()
|
||||
|
||||
// Ausdauer Bonus: Ko/10 + St/20 = 75/10 + 85/20 = 7 + 4 = 11
|
||||
assert.Equal(t, 11, bonuses.AusdauerBonus, "AusdauerBonus should be calculated correctly")
|
||||
|
||||
// Schadens Bonus: St/20 + Gs/30 - 3 = 85/20 + 70/30 - 3 = 4 + 2 - 3 = 3
|
||||
assert.Equal(t, 3, bonuses.SchadensBonus, "SchadensBonus should be calculated correctly")
|
||||
|
||||
// Angriffs Bonus: Gs 70 -> bonus 2 (61-80 range)
|
||||
assert.Equal(t, 2, bonuses.AngriffsBonus, "AngriffsBonus should be calculated correctly")
|
||||
|
||||
// Abwehr Bonus: Gw 65 -> bonus 2 (61-80 range)
|
||||
assert.Equal(t, 2, bonuses.AbwehrBonus, "AbwehrBonus should be calculated correctly")
|
||||
|
||||
// Zauber Bonus: Zt 30 -> bonus 0 (21-40 range)
|
||||
assert.Equal(t, 0, bonuses.ZauberBonus, "ZauberBonus should be calculated correctly")
|
||||
|
||||
// Resistenz Bonus Körper: Ko 75 -> bonus 2 (61-80), Mensch Krieger +1 = 3
|
||||
assert.Equal(t, 3, bonuses.ResistenzBonusKoerper, "ResistenzBonusKoerper should be calculated correctly")
|
||||
|
||||
// Resistenz Bonus Geist: In 50 -> bonus 1 (41-60), Mensch = 1
|
||||
assert.Equal(t, 1, bonuses.ResistenzBonusGeist, "ResistenzBonusGeist should be calculated correctly")
|
||||
})
|
||||
|
||||
t.Run("Calculate bonuses for Zwerg Kämpfer", func(t *testing.T) {
|
||||
char := models.Char{
|
||||
Rasse: "Zwerge",
|
||||
Typ: "Krieger",
|
||||
Eigenschaften: []models.Eigenschaft{
|
||||
{Name: "Ko", Value: 85},
|
||||
{Name: "In", Value: 50},
|
||||
},
|
||||
}
|
||||
|
||||
bonuses := char.CalculateBonuses()
|
||||
|
||||
// Zwerge get +3 base, Kämpfer +1 = 4
|
||||
assert.Equal(t, 4, bonuses.ResistenzBonusKoerper, "Zwerge Kämpfer ResistenzBonusKoerper should be 4")
|
||||
|
||||
// Zwerge get +3 base
|
||||
assert.Equal(t, 3, bonuses.ResistenzBonusGeist, "Zwerge ResistenzBonusGeist should be 3")
|
||||
})
|
||||
|
||||
t.Run("Calculate bonuses for Elf Magier", func(t *testing.T) {
|
||||
char := models.Char{
|
||||
Rasse: "Elfen",
|
||||
Typ: "Magier",
|
||||
Eigenschaften: []models.Eigenschaft{
|
||||
{Name: "Ko", Value: 50},
|
||||
{Name: "In", Value: 85},
|
||||
},
|
||||
}
|
||||
|
||||
bonuses := char.CalculateBonuses()
|
||||
|
||||
// Elfen get +2 base, Magier (Zauberer) +2 = 4
|
||||
assert.Equal(t, 4, bonuses.ResistenzBonusKoerper, "Elfen Magier ResistenzBonusKoerper should be 4")
|
||||
|
||||
// Elfen get +2 base, Magier (Zauberer) +2 = 4
|
||||
assert.Equal(t, 4, bonuses.ResistenzBonusGeist, "Elfen Magier ResistenzBonusGeist should be 4")
|
||||
})
|
||||
}
|
||||
@@ -2748,6 +2748,13 @@ func FinalizeCharacterCreation(c *gin.Context) {
|
||||
Public: false, // Default to private
|
||||
Grad: 1, // Default starting grade
|
||||
|
||||
// Static derived values (can increase with grade)
|
||||
ResistenzKoerper: session.DerivedValues.ResistenzKoerper,
|
||||
ResistenzGeist: session.DerivedValues.ResistenzGeist,
|
||||
Abwehr: session.DerivedValues.Abwehr,
|
||||
Zaubern: session.DerivedValues.Zaubern,
|
||||
Raufen: session.DerivedValues.Raufen,
|
||||
|
||||
// Lebenspunkte
|
||||
Lp: models.Lp{
|
||||
Max: session.DerivedValues.LPMax,
|
||||
|
||||
@@ -716,7 +716,7 @@ func TestFinalizeCharacterCreation(t *testing.T) {
|
||||
|
||||
// Verify character exists in database
|
||||
var createdChar models.Char
|
||||
err = database.DB.Preload("Fertigkeiten").Preload("Waffenfertigkeiten").Preload("Zauber").
|
||||
err = database.DB.Preload("Lp").Preload("Ap").Preload("B").Preload("Eigenschaften").Preload("Fertigkeiten").Preload("Waffenfertigkeiten").Preload("Zauber").
|
||||
First(&createdChar, "id = ?", characterID).Error
|
||||
assert.NoError(t, err, "Created character should exist in database")
|
||||
|
||||
@@ -756,6 +756,13 @@ func TestFinalizeCharacterCreation(t *testing.T) {
|
||||
assert.Equal(t, 8, createdChar.B.Max, "B Max should match session")
|
||||
assert.Equal(t, 8, createdChar.B.Value, "B Value should equal Max initially")
|
||||
|
||||
// Validate static derived values (Resistenz, Abwehr, Zaubern, Raufen)
|
||||
assert.Equal(t, 11, createdChar.ResistenzKoerper, "Resistenz Körper should match session")
|
||||
assert.Equal(t, 11, createdChar.ResistenzGeist, "Resistenz Geist should match session")
|
||||
assert.Equal(t, 11, createdChar.Abwehr, "Abwehr should match session")
|
||||
assert.Equal(t, 11, createdChar.Zaubern, "Zaubern should match session")
|
||||
assert.Equal(t, 8, createdChar.Raufen, "Raufen should match session")
|
||||
|
||||
// Validate skills were transferred (session has 10 skills: 6 regular skills + 4 weapon skills)
|
||||
// Regular skills: Klettern, Reiten, Sprache, Athletik, Heilkunde, Naturkunde (6)
|
||||
// Weapon skills: Spießwaffen, Stielwurfwaffen, Waffenloser Kampf, Stichwaffen (4)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# Derived Values Implementation
|
||||
|
||||
## Summary
|
||||
|
||||
Static derived values (Resistenz Körper, Resistenz Geist, Abwehr, Zaubern, Raufen) are now stored in the database as part of the `Char` model. These values can increase when a new grade is reached.
|
||||
|
||||
Bonus values are calculated on-demand from the character's attributes using the `CalculateBonuses()` method.
|
||||
|
||||
## Database Fields Added to `Char`
|
||||
|
||||
- `ResistenzKoerper` (int) - Resistenz Körper base value
|
||||
- `ResistenzGeist` (int) - Resistenz Geist base value
|
||||
- `Abwehr` (int) - Abwehr base value
|
||||
- `Zaubern` (int) - Zaubern base value
|
||||
- `Raufen` (int) - Raufen base value
|
||||
|
||||
## Calculated Bonuses (Not Stored)
|
||||
|
||||
The following bonuses are calculated from attributes and are NOT stored in the database:
|
||||
|
||||
- `AusdauerBonus` = Ko/10 + St/20
|
||||
- `SchadensBonus` = St/20 + Gs/30 - 3
|
||||
- `AngriffsBonus` = attribute bonus from Gs
|
||||
- `AbwehrBonus` = attribute bonus from Gw
|
||||
- `ZauberBonus` = attribute bonus from Zt
|
||||
- `ResistenzBonusKoerper` = attribute/race bonus from Ko
|
||||
- `ResistenzBonusGeist` = attribute/race bonus from In
|
||||
|
||||
## Usage Example
|
||||
|
||||
```go
|
||||
// Load character from database
|
||||
var char models.Char
|
||||
err := char.FirstID("123")
|
||||
|
||||
// Get static derived values (stored in DB)
|
||||
resistenzKoerper := char.ResistenzKoerper
|
||||
abwehr := char.Abwehr
|
||||
|
||||
// Calculate bonuses from attributes (on-demand)
|
||||
bonuses := char.CalculateBonuses()
|
||||
abwehrBonus := bonuses.AbwehrBonus
|
||||
zauberBonus := bonuses.ZauberBonus
|
||||
|
||||
// Total values
|
||||
totalAbwehr := char.Abwehr + bonuses.AbwehrBonus
|
||||
totalZaubern := char.Zaubern + bonuses.ZauberBonus
|
||||
```
|
||||
|
||||
## When Grade Increases
|
||||
|
||||
When a character reaches a new grade, the static derived values may increase. To update them:
|
||||
|
||||
```go
|
||||
// After grade increase, recalculate and update static values
|
||||
char.Grad++
|
||||
char.ResistenzKoerper++ // or apply the appropriate grade bonus
|
||||
char.Abwehr++
|
||||
// Save updated character
|
||||
database.DB.Save(&char)
|
||||
```
|
||||
|
||||
Bonuses will automatically reflect any attribute changes without needing to update the database, as they are calculated on-demand from the `Eigenschaften` values.
|
||||
@@ -510,6 +510,38 @@ func SetupCheck(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Setup Check OK"})
|
||||
}
|
||||
|
||||
func SetupCheckDev(c *gin.Context) {
|
||||
logger.Info("Starte Setup-Check... PreparedTestDB")
|
||||
|
||||
// Use the prepared test database for development setup check
|
||||
db, dberr := gorm.Open(sqlite.Open(database.PreparedTestDB), &gorm.Config{})
|
||||
if dberr != nil {
|
||||
logger.Error("SetupTestDB: Fehler beim Verbinden mit der Test-Datenbank: %s", dberr.Error())
|
||||
panic("failed to connect to the test database: " + dberr.Error())
|
||||
}
|
||||
database.DB = db
|
||||
|
||||
logger.Debug("Erfolgreich mit Datenbank für Setup-Check verbunden")
|
||||
|
||||
logger.Debug("Führe Strukturmigration durch...")
|
||||
err := migrateAllStructures(db)
|
||||
if err != nil {
|
||||
logger.Error("Fehler bei der Strukturmigration: %s", err.Error())
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug("Führe Datenmigration durch...")
|
||||
err = migrateDataIfNeeded(db)
|
||||
if err != nil {
|
||||
logger.Error("Fehler bei der Datenmigration: %s", err.Error())
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to migrate data: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
logger.Info("Setup-Check erfolgreich abgeschlossen")
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Setup Check OK"})
|
||||
}
|
||||
func ReconnectDataBase(c *gin.Context) {
|
||||
logger.Info("Führe Datenbank-Reconnect durch...")
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
func RegisterRoutes(r *gin.RouterGroup) {
|
||||
charGrp := r.Group("/maintenance")
|
||||
charGrp.GET("/setupcheck", SetupCheck)
|
||||
charGrp.GET("/setupcheck-dev", SetupCheckDev)
|
||||
charGrp.GET("/mktestdata", MakeTestdataFromLive)
|
||||
charGrp.GET("/reconndb", ReconnectDataBase) // Datenbank neu verbinden
|
||||
charGrp.GET("/reloadenv", ReloadENV)
|
||||
|
||||
@@ -76,21 +76,27 @@ type Vermoegen struct {
|
||||
|
||||
type Char struct {
|
||||
BamortBase
|
||||
UserID uint `gorm:"index;not null;default:1" json:"user_id"`
|
||||
User user.User `gorm:"foreignKey:UserID;references:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"user"`
|
||||
Rasse string `json:"rasse"`
|
||||
Typ string `json:"typ"`
|
||||
Alter int `json:"alter"`
|
||||
Anrede string `json:"anrede"`
|
||||
Grad int `json:"grad"`
|
||||
Gender string `json:"gender"`
|
||||
SocialClass string `json:"social_class"`
|
||||
Groesse int `json:"groesse"`
|
||||
Gewicht int `json:"gewicht"`
|
||||
Herkunft string `json:"origin"`
|
||||
Glaube string `json:"glaube"`
|
||||
Hand string `json:"hand"`
|
||||
Public bool `json:"public"`
|
||||
UserID uint `gorm:"index;not null;default:1" json:"user_id"`
|
||||
User user.User `gorm:"foreignKey:UserID;references:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"user"`
|
||||
Rasse string `json:"rasse"`
|
||||
Typ string `json:"typ"`
|
||||
Alter int `json:"alter"`
|
||||
Anrede string `json:"anrede"`
|
||||
Grad int `json:"grad"`
|
||||
Gender string `json:"gender"`
|
||||
SocialClass string `json:"social_class"`
|
||||
Groesse int `json:"groesse"`
|
||||
Gewicht int `json:"gewicht"`
|
||||
Herkunft string `json:"origin"`
|
||||
Glaube string `json:"glaube"`
|
||||
Hand string `json:"hand"`
|
||||
Public bool `json:"public"`
|
||||
// Static derived values (can increase with grade)
|
||||
ResistenzKoerper int `json:"resistenz_koerper"`
|
||||
ResistenzGeist int `json:"resistenz_geist"`
|
||||
Abwehr int `json:"abwehr"`
|
||||
Zaubern int `json:"zaubern"`
|
||||
Raufen int `json:"raufen"`
|
||||
Lp Lp `gorm:"foreignKey:CharacterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"lp"`
|
||||
Ap Ap `gorm:"foreignKey:CharacterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"ap"`
|
||||
B B `gorm:"foreignKey:CharacterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"b"`
|
||||
@@ -293,3 +299,155 @@ func (object *Vermoegen) TableName() string {
|
||||
dbPrefix := "char"
|
||||
return dbPrefix + "_" + "wealth"
|
||||
}
|
||||
|
||||
// DerivedBonuses contains all calculated bonus values
|
||||
type DerivedBonuses struct {
|
||||
AusdauerBonus int
|
||||
SchadensBonus int
|
||||
AngriffsBonus int
|
||||
AbwehrBonus int
|
||||
ZauberBonus int
|
||||
ResistenzBonusKoerper int
|
||||
ResistenzBonusGeist int
|
||||
}
|
||||
|
||||
// GetAttributeValue returns the value of an attribute by name
|
||||
func (char *Char) GetAttributeValue(name string) int {
|
||||
for _, attr := range char.Eigenschaften {
|
||||
if attr.Name == name {
|
||||
return attr.Value
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// CalculateBonuses calculates all derived bonuses from attributes
|
||||
func (char *Char) CalculateBonuses() DerivedBonuses {
|
||||
st := char.GetAttributeValue("St")
|
||||
gs := char.GetAttributeValue("Gs")
|
||||
gw := char.GetAttributeValue("Gw")
|
||||
ko := char.GetAttributeValue("Ko")
|
||||
in := char.GetAttributeValue("In")
|
||||
zt := char.GetAttributeValue("Zt")
|
||||
|
||||
bonuses := DerivedBonuses{
|
||||
// Ausdauer Bonus: Ko/10 + St/20
|
||||
AusdauerBonus: (ko / 10) + (st / 20),
|
||||
|
||||
// Schadens Bonus: St/20 + Gs/30 - 3
|
||||
SchadensBonus: (st / 20) + (gs / 30) - 3,
|
||||
|
||||
// Angriffs Bonus basierend auf GS
|
||||
AngriffsBonus: calculateAttributeBonus(gs),
|
||||
|
||||
// Abwehr Bonus basierend auf GW
|
||||
AbwehrBonus: calculateAttributeBonus(gw),
|
||||
|
||||
// Zauber Bonus basierend auf Zt
|
||||
ZauberBonus: calculateAttributeBonus(zt),
|
||||
}
|
||||
|
||||
// Resistenz Bonus Körper
|
||||
bonuses.ResistenzBonusKoerper = calculateResistenzBonusKoerper(ko, char.Rasse, char.Typ)
|
||||
|
||||
// Resistenz Bonus Geist
|
||||
bonuses.ResistenzBonusGeist = calculateResistenzBonusGeist(in, char.Rasse, char.Typ)
|
||||
|
||||
return bonuses
|
||||
}
|
||||
|
||||
// Helper functions for bonus calculation
|
||||
|
||||
func calculateAttributeBonus(value int) int {
|
||||
if value <= 5 {
|
||||
return -5
|
||||
} else if value <= 20 {
|
||||
return -1
|
||||
} else if value <= 40 {
|
||||
return 0
|
||||
} else if value <= 60 {
|
||||
return 1
|
||||
} else if value <= 80 {
|
||||
return 2
|
||||
} else if value <= 95 {
|
||||
return 3
|
||||
} else {
|
||||
return 4
|
||||
}
|
||||
}
|
||||
|
||||
func calculateResistenzBonusKoerper(ko int, rasse string, typ string) int {
|
||||
bonus := 0
|
||||
|
||||
if rasse == "Mensch" || rasse == "Menschen" {
|
||||
bonus = calculateAttributeBonus(ko)
|
||||
} else {
|
||||
switch rasse {
|
||||
case "Elfen":
|
||||
bonus = 2
|
||||
case "Gnome", "Halblinge":
|
||||
bonus = 4
|
||||
case "Zwerge":
|
||||
bonus = 3
|
||||
}
|
||||
}
|
||||
|
||||
// Klassenmodifikator
|
||||
if isKaempfer(typ) {
|
||||
bonus += 1
|
||||
} else if isZauberer(typ) {
|
||||
bonus += 2
|
||||
}
|
||||
|
||||
return bonus
|
||||
}
|
||||
|
||||
func calculateResistenzBonusGeist(in int, rasse string, typ string) int {
|
||||
bonus := 0
|
||||
|
||||
if rasse == "Mensch" || rasse == "Menschen" {
|
||||
bonus = calculateAttributeBonus(in)
|
||||
} else {
|
||||
switch rasse {
|
||||
case "Elfen":
|
||||
bonus = 2
|
||||
case "Gnome", "Halblinge":
|
||||
bonus = 4
|
||||
case "Zwerge":
|
||||
bonus = 3
|
||||
}
|
||||
}
|
||||
|
||||
// Klassenmodifikator (nur Zauberer bekommen Geist-Bonus)
|
||||
if isZauberer(typ) {
|
||||
bonus += 2
|
||||
}
|
||||
|
||||
return bonus
|
||||
}
|
||||
|
||||
func isKaempfer(typ string) bool {
|
||||
kaempferClasses := []string{
|
||||
"Krieger", "Barbar", "Spitzbube", "Assassine",
|
||||
"Streiter", "Waldläufer", "Krieger Magier",
|
||||
}
|
||||
for _, class := range kaempferClasses {
|
||||
if typ == class {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isZauberer(typ string) bool {
|
||||
zaubererClasses := []string{
|
||||
"Magier", "Hexer", "Thaumaturg", "Krieger Magier",
|
||||
"Priester", "Druide", "Schamane",
|
||||
}
|
||||
for _, class := range zaubererClasses {
|
||||
if typ == class {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
+66
-26
@@ -3,6 +3,7 @@ package pdfrender
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"bamort/character"
|
||||
"bamort/database"
|
||||
"bamort/models"
|
||||
)
|
||||
@@ -20,18 +21,18 @@ func MapCharacterToViewModel(char *models.Char) (*CharacterSheetViewModel, error
|
||||
|
||||
// Map basic character info
|
||||
vm.Character = CharacterInfo{
|
||||
Name: char.Name,
|
||||
Type: char.Typ,
|
||||
Grade: char.Grad,
|
||||
Age: char.Alter,
|
||||
Height: char.Groesse,
|
||||
Weight: char.Gewicht,
|
||||
Gender: char.Gender,
|
||||
Hand: char.Hand,
|
||||
Homeland: char.Herkunft,
|
||||
Religion: char.Glaube,
|
||||
Stand: char.SocialClass,
|
||||
IconBase64: "", // Will be set later if image exists
|
||||
Name: char.Name,
|
||||
Type: char.Typ,
|
||||
Grade: char.Grad,
|
||||
Age: char.Alter,
|
||||
Height: char.Groesse,
|
||||
Weight: char.Gewicht,
|
||||
Gender: char.Gender,
|
||||
Hand: char.Hand,
|
||||
Herkunft: char.Herkunft,
|
||||
Glaube: char.Glaube,
|
||||
SocialClass: char.SocialClass,
|
||||
IconBase64: "", // Will be set later if image exists
|
||||
Vermoegen: WealthInfo{
|
||||
Goldstuecke: char.Vermoegen.Goldstuecke,
|
||||
Silberstuecke: char.Vermoegen.Silberstuecke,
|
||||
@@ -80,14 +81,14 @@ func mapAttributes(char *models.Char) AttributeValues {
|
||||
attrs.Zt = e.Value
|
||||
case "Au":
|
||||
attrs.Au = e.Value
|
||||
case "pA":
|
||||
case "PA":
|
||||
attrs.PA = e.Value
|
||||
case "Wk":
|
||||
attrs.Wk = e.Value
|
||||
}
|
||||
}
|
||||
|
||||
attrs.B = char.B.Value
|
||||
attrs.B = char.B.Max
|
||||
|
||||
return attrs
|
||||
}
|
||||
@@ -96,14 +97,43 @@ func mapAttributes(char *models.Char) AttributeValues {
|
||||
func mapDerivedValues(char *models.Char) DerivedValueSet {
|
||||
// Get attributes for bonus calculations
|
||||
attrs := mapAttributes(char)
|
||||
// Get Bonus values from character logic
|
||||
bonusValues := character.CalculateStaticFieldsLogic(character.CalculateStaticFieldsRequest{
|
||||
St: attrs.St,
|
||||
Gs: attrs.Gs,
|
||||
Gw: attrs.Gw,
|
||||
Ko: attrs.Ko,
|
||||
In: attrs.In,
|
||||
Zt: attrs.Zt,
|
||||
Au: attrs.Au,
|
||||
Rasse: char.Rasse,
|
||||
Typ: char.Typ,
|
||||
Grad: char.Grad,
|
||||
})
|
||||
|
||||
return DerivedValueSet{
|
||||
LPMax: char.Lp.Max,
|
||||
LPAktuell: char.Lp.Value,
|
||||
APMax: char.Ap.Max,
|
||||
APAktuell: char.Ap.Value,
|
||||
AngriffBonus: calculateAttributeBonus(attrs.Gs),
|
||||
SchadenBonus: (attrs.St / 20) + (attrs.Gs / 30) - 3,
|
||||
LPMax: char.Lp.Max,
|
||||
LPAktuell: char.Lp.Value,
|
||||
APMax: char.Ap.Max,
|
||||
APAktuell: char.Ap.Value,
|
||||
AusdauerBonus: bonusValues.AusdauerBonus,
|
||||
SchadenBonus: bonusValues.SchadensBonus,
|
||||
AngriffBonus: bonusValues.AngriffsBonus,
|
||||
Abwehr: bonusValues.Abwehr,
|
||||
AbwehrBonus: bonusValues.AbwehrBonus,
|
||||
Zaubern: bonusValues.Zaubern,
|
||||
ZauberBonus: bonusValues.ZauberBonus,
|
||||
ResistenzKoerper: bonusValues.ResistenzKoerper,
|
||||
ResistenzBonusKoerper: bonusValues.ResistenzBonusKoerper,
|
||||
ResistenzGeist: bonusValues.ResistenzGeist,
|
||||
ResistenzBonusGeist: bonusValues.ResistenzBonusGeist,
|
||||
Raufen: bonusValues.Raufen,
|
||||
GG: char.B.Max,
|
||||
SG: char.B.Max / 2,
|
||||
Sehen: 0, // TODO: Add to character model
|
||||
Horen: 0, // TODO: Add to character model
|
||||
Riechen: 0, // TODO: Add to character model
|
||||
Sechster: 0, // TODO: Add to character model
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,10 +165,20 @@ func mapSkills(char *models.Char) []SkillViewModel {
|
||||
func mapWeapons(char *models.Char) []WeaponViewModel {
|
||||
weapons := make([]WeaponViewModel, 0, len(char.Waffen))
|
||||
|
||||
// Calculate character's bonuses once
|
||||
// Calculate character's bonuses using character logic
|
||||
attrs := mapAttributes(char)
|
||||
angriffsBonus := calculateAttributeBonus(attrs.Gs)
|
||||
schadenBonus := calculateAttributeBonus(attrs.St)
|
||||
bonusValues := character.CalculateStaticFieldsLogic(character.CalculateStaticFieldsRequest{
|
||||
St: attrs.St,
|
||||
Gs: attrs.Gs,
|
||||
Gw: attrs.Gw,
|
||||
Ko: attrs.Ko,
|
||||
In: attrs.In,
|
||||
Zt: attrs.Zt,
|
||||
Au: attrs.Au,
|
||||
Rasse: char.Rasse,
|
||||
Typ: char.Typ,
|
||||
Grad: char.Grad,
|
||||
})
|
||||
|
||||
// Create a map of weapon skills for quick lookup
|
||||
weaponSkills := make(map[string]int)
|
||||
@@ -162,10 +202,10 @@ func mapWeapons(char *models.Char) []WeaponViewModel {
|
||||
if baseWeapon.SkillRequired != "" {
|
||||
skillValue = weaponSkills[baseWeapon.SkillRequired]
|
||||
}
|
||||
vm.Value = skillValue + angriffsBonus + equippedWeapon.Anb
|
||||
vm.Value = skillValue + bonusValues.AngriffsBonus + equippedWeapon.Anb
|
||||
|
||||
// Calculate damage: Base weapon damage + character bonus + weapon damage bonus
|
||||
vm.Damage = calculateWeaponDamageWithBase(baseWeapon, schadenBonus, equippedWeapon.Schb)
|
||||
vm.Damage = calculateWeaponDamageWithBase(baseWeapon, bonusValues.SchadensBonus, equippedWeapon.Schb)
|
||||
|
||||
// Add range information for ranged weapons
|
||||
if baseWeapon.IsRanged() {
|
||||
@@ -177,7 +217,7 @@ func mapWeapons(char *models.Char) []WeaponViewModel {
|
||||
}
|
||||
} else {
|
||||
// Weapon not found in gsm_weapons, use basic info
|
||||
vm.Value = angriffsBonus + equippedWeapon.Anb
|
||||
vm.Value = bonusValues.AngriffsBonus + equippedWeapon.Anb
|
||||
}
|
||||
|
||||
weapons = append(weapons, vm)
|
||||
|
||||
@@ -44,6 +44,18 @@ func TestMapCharacterToViewModel_BasicInfo(t *testing.T) {
|
||||
if vm.Character.Grade != 5 {
|
||||
t.Errorf("Expected grade 5, got %d", vm.Character.Grade)
|
||||
}
|
||||
|
||||
if vm.Character.Herkunft != "Clanngadarn" {
|
||||
t.Errorf("Expected Herkunft 'Clanngadarn', got '%s'", vm.Character.Herkunft)
|
||||
}
|
||||
|
||||
if vm.Character.Glaube != "Druide" {
|
||||
t.Errorf("Expected Glaube 'Druide', got '%s'", vm.Character.Glaube)
|
||||
}
|
||||
|
||||
if vm.Character.SocialClass != "Mittelschicht" {
|
||||
t.Errorf("Expected SocialClass 'Mittelschicht', got '%s'", vm.Character.SocialClass)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapCharacterToViewModel_Attributes(t *testing.T) {
|
||||
@@ -58,7 +70,7 @@ func TestMapCharacterToViewModel_Attributes(t *testing.T) {
|
||||
{Name: "Gs", Value: 65},
|
||||
{Name: "Gw", Value: 70},
|
||||
},
|
||||
B: models.B{Value: 10},
|
||||
B: models.B{Max: 10},
|
||||
}
|
||||
|
||||
// Act
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
## TODO (Remaining)
|
||||
* 1. in RenderPageWithContinuations use a switch to select a templatename and the ncall a paginator function that is designed explicitly for this template page with the current content. This will not work when we add more and different templates. We nned a solution where the code can find out which data is present on the template page and if these datastructures are to be continued on a continuation page or on the next regular page in row. it needs to find out how many elements can be rendered into the page or it's continuation page. It needs to handle if there are multiple datastructures present on one page.
|
||||
for this to be accomplished we need some kind of template metadata or inline datastructure(Block) configuration.
|
||||
I want to change the templateName from page1_stats.html to page_1.html and accordingly the continuation page from page1.2_stats.html to page_1.2.html this makes clear that a page is not tight to one or another datastructure.
|
||||
Page 4 has a complex container-based layout we leave this out for the moment.
|
||||
Plan a refacturation of PaginateSkills, PaginatePage2PlayLists and PaginateSpells into one unified function.
|
||||
|
||||
|
||||
|
||||
### Later
|
||||
* continuation of lists does not work as expected but good enough for a first shot
|
||||
* generalize handling so that only on set of functions can handle ALL kinds of templates. Needs massive refactoring
|
||||
|
||||
* currently the template fetched for rendering is set to Default_A4_Quer
|
||||
* remove inline css as far as possible
|
||||
* make pdf download popup an own view
|
||||
* func CleanupExportTemp move maxAge := 7 * 24 * time.Hour definition to Config struct n config.go
|
||||
@@ -18,21 +18,21 @@ type CharacterSheetViewModel struct {
|
||||
|
||||
// CharacterInfo contains basic character information
|
||||
type CharacterInfo struct {
|
||||
Name string
|
||||
Player string
|
||||
Type string // Charaktertyp (z.B. "Krieger", "Magier")
|
||||
Grade int
|
||||
Birthdate string
|
||||
Age int
|
||||
Hand string // "rechts." oder "links." händig
|
||||
Height int // in cm
|
||||
Weight int // in kg
|
||||
IconBase64 string // base64-kodiertes Charakterbild als Data-URI
|
||||
Gender string
|
||||
Homeland string
|
||||
Religion string
|
||||
Stand string // Sozialer Stand
|
||||
Vermoegen WealthInfo
|
||||
Name string
|
||||
Player string
|
||||
Type string // Charaktertyp (z.B. "Krieger", "Magier")
|
||||
Grade int
|
||||
Birthdate string
|
||||
Age int
|
||||
Hand string // "rechts." oder "links." händig
|
||||
Height int // in cm
|
||||
Weight int // in kg
|
||||
IconBase64 string // base64-kodiertes Charakterbild als Data-URI
|
||||
Gender string
|
||||
Herkunft string
|
||||
Glaube string
|
||||
SocialClass string // Sozialer SocialClass
|
||||
Vermoegen WealthInfo
|
||||
}
|
||||
|
||||
// WealthInfo contains character wealth/money
|
||||
@@ -72,17 +72,21 @@ type DerivedValueSet struct {
|
||||
|
||||
// Kampfwerte
|
||||
Abwehr int // z.B. "Abwehr+12"
|
||||
AbwehrBonus int // Abwehr-Bonus
|
||||
SchadenBonus int
|
||||
AngriffBonus int
|
||||
Raufen int // Raufen-Wert
|
||||
|
||||
// Resistenzen
|
||||
ResistenzGift int
|
||||
ResistenzKoerper int
|
||||
ResistenzGeist int
|
||||
ResistenzGift int
|
||||
ResistenzKoerper int
|
||||
ResistenzBonusKoerper int // Resistenz Körper Bonus
|
||||
ResistenzGeist int
|
||||
ResistenzBonusGeist int // Resistenz Geist Bonus
|
||||
|
||||
// Zauberwerte
|
||||
Zaubern int // z.B. "+10/+9"
|
||||
ZaubernBonus int // Erster Zauberbonus
|
||||
Zaubern int // z.B. "+10/+9"
|
||||
ZauberBonus int // Zauber-Bonus
|
||||
|
||||
// Sonstige
|
||||
Sehen int // Sehen-Wert
|
||||
|
||||
@@ -52,14 +52,14 @@
|
||||
<!-- left column block stats4 -->
|
||||
<div class="margin-bottom-3">
|
||||
<div class="attr-box attr-box-80"><div class="attr-label">B</div><div class="attr-value">{{.Attributes.B}}</div></div>
|
||||
<div class="attr-box attr-box-100"><div class="attr-label">Raufen</div><div class="attr-value">+ 5</div></div>
|
||||
<div class="attr-box attr-box-100"><div class="attr-label">Raufen</div><div class="attr-value">+ {{.DerivedValues.Raufen}}</div></div>
|
||||
</div>
|
||||
<!-- left column block boni -->
|
||||
<div class="margin-top-2">
|
||||
<div><strong>persönlicher Bonus für:</strong></div>
|
||||
<div class="margin-top-2">
|
||||
<div class="bonus-row"><span>Ausdauer <u>{{.DerivedValues.AusdauerBonus}}</u></span><span>Schaden <u>{{.DerivedValues.SchadenBonus}}</u></span><span>Angriff <u>{{.DerivedValues.AngriffBonus}}</u></span></div>
|
||||
<div class="bonus-row"><span>Abwehr <u>{{.DerivedValues.Abwehr}}</u></span><span>Resistenz <u>{{.DerivedValues.ResistenzGift}}/{{.DerivedValues.ResistenzGeist}}</u></span><span>Zaubern <u>{{.DerivedValues.Zaubern}}</u></span></div>
|
||||
<div class="bonus-row"><span>Abwehr <u>{{.DerivedValues.AbwehrBonus}}</u></span><span>Resistenz <u>{{.DerivedValues.ResistenzBonusKoerper}}/{{.DerivedValues.ResistenzBonusGeist}}</u></span><span>Zaubern <u>{{.DerivedValues.ZauberBonus}}</u></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -84,15 +84,15 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Stand</strong></td>
|
||||
<td>{{.Character.Stand}}</td>
|
||||
<td>{{.Character.SocialClass}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Heimat</strong></td>
|
||||
<td>{{.Character.Homeland}}</td>
|
||||
<td>{{.Character.Herkunft}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Glaube</strong></td>
|
||||
<td>{{.Character.Religion}}</td>
|
||||
<td>{{if .Character.Glaube}}{{.Character.Glaube}}{{else}}---{{end}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><strong>Besondere Merkmale:</strong><br/><br/><br/><br/><br/><br/><br/><br/></td>
|
||||
|
||||
@@ -41,10 +41,10 @@
|
||||
</div>
|
||||
<!-- Combat stats -->
|
||||
<div class="combat-stats">
|
||||
<div><strong>Abwehr + {{.DerivedValues.Abwehr}}</strong></div>
|
||||
<div><strong>Abwehr + {{.DerivedValues.Abwehr}}+{{.DerivedValues.AbwehrBonus}}</strong></div>
|
||||
<div><strong>+</strong> <span class="combat-stats-small">mit Vert.<br>waffe</span></div>
|
||||
<div><strong>Resistenz + {{.DerivedValues.ResistenzKoerper}}/{{.DerivedValues.ResistenzGeist}}</strong></div>
|
||||
<div><strong>Zaubern + {{.DerivedValues.Zaubern}}</strong></div>
|
||||
<div><strong>Resistenz + {{.DerivedValues.ResistenzKoerper}}+{{.DerivedValues.ResistenzBonusKoerper}}/{{.DerivedValues.ResistenzGeist}}+{{.DerivedValues.ResistenzBonusGeist}}</strong></div>
|
||||
<div><strong>Zaubern + {{.DerivedValues.Zaubern}}+{{.DerivedValues.ZauberBonus}}</strong></div>
|
||||
</div>
|
||||
<!-- Skills tables -->
|
||||
<div class="skills-row">
|
||||
@@ -121,29 +121,29 @@
|
||||
<td></td>
|
||||
<td>{{.DerivedValues.AngriffBonus}}</td>
|
||||
<td></td>
|
||||
<td>{{.DerivedValues.Abwehr}}</td>
|
||||
<td>{{.DerivedValues.AbwehrBonus}}</td>
|
||||
<td></td>
|
||||
<td>{{.DerivedValues.SchadenBonus}}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Situativ</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>Wert</td>
|
||||
<td>{{.DerivedValues.APMax}}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>{{.DerivedValues.Abwehr}}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Summe</td>
|
||||
<td>{{.DerivedValues.APMax}}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>{{.DerivedValues.Abwehr}}+{{.DerivedValues.AbwehrBonus}}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
## TODO (Remaining)
|
||||
|
||||
### Done
|
||||
|
||||
|
||||
### Later
|
||||
|
||||
* generalize PDF Export handling so that only on set of functions can handle ALL kinds of templates. Needs massive refactoring
|
||||
|
||||
* currently the template fetched for rendering is set to Default_A4_Quer
|
||||
* remove inline css as far as possible
|
||||
* make pdf download popup an own view
|
||||
* func CleanupExportTemp move maxAge := 7 * 24 * time.Hour definition to Config struct in config.go
|
||||
* CalculateStaticFieldsLogic calc e.g response.ResistenzKoerper and others without bonus
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
<strong>Aktuelle Kampagne:</strong> Melzindar
|
||||
</p>
|
||||
<p>
|
||||
{{ character.typ || 'x' }} ({{ character.geschlecht || 'x' }}nännlich),
|
||||
{{ character.typ || 'x' }} ({{ character.gender || 'x' }}),
|
||||
Grad: {{ character.grad || 'x' }},
|
||||
Rasse: {{ character.rasse || 'x' }},
|
||||
Heimat: {{ character.heimat || 'x' }}Alba,
|
||||
Stand: {{ character.heimat || 'x' }}Mittelschicht.
|
||||
Heimat: {{ character.origin || '-' }},
|
||||
Stand: {{ character.social_class || '-' }}.
|
||||
</p>
|
||||
<p v-if="character.rasse==='Zwerg'">
|
||||
Hort für Grad {{ character.grad || 'x' }}: 125 GS, für nächsten Grad: 250 GS.
|
||||
@@ -44,7 +44,7 @@
|
||||
und {{ character.merkmale?.breite || '-'}},
|
||||
Augen: {{ character.merkmale?.augenfarbe || '-' }},
|
||||
Haare: {{ character.merkmale?.haarfarbe || '-' }},
|
||||
Glaube: {{ character.glaube }}.
|
||||
Glaube: {{ character.glaube || '-' }}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Merkmale:</strong> {{ character.merkmale?.sonstige || '-' }}
|
||||
|
||||
Reference in New Issue
Block a user