weapons are displayed fine

This commit is contained in:
2025-12-20 00:19:20 +01:00
parent c47e00457f
commit de6c8c1c1b
10 changed files with 535 additions and 163 deletions
+8
View File
@@ -102,6 +102,9 @@ type Weapon struct {
Equipment
SkillRequired string `json:"skill_required"`
Damage string `json:"damage"`
RangeNear int `json:"range_near"` // Range in meters for "Nah" (near)
RangeMiddle int `json:"range_middle"` // Range in meters for "Mittel" (medium)
RangeFar int `json:"range_far"` // Range in meters for "Fern" (far)
}
type Container struct {
@@ -483,6 +486,11 @@ func (object *Weapon) TableName() string {
return dbPrefix + "_" + "weapons"
}
// IsRanged returns true if the weapon has at least one range value > 0
func (w *Weapon) IsRanged() bool {
return w.RangeNear > 0 || w.RangeMiddle > 0 || w.RangeFar > 0
}
func (stamm *Weapon) Create() error {
gameSystem := "midgard"
stamm.GameSystem = gameSystem
+69
View File
@@ -757,6 +757,75 @@ func TestWeapon_Save(t *testing.T) {
assert.Equal(t, "2W6+3", foundWeapon.Damage)
}
func TestWeapon_RangedWeaponRanges(t *testing.T) {
setupGSMasterTestDB(t)
// Create a ranged weapon with ranges
weapon := &Weapon{
Equipment: Equipment{
GameSystem: "midgard",
Name: "TestBogen",
Beschreibung: "Test ranged weapon",
Gewicht: 1.5,
Wert: 100.0,
},
SkillRequired: "Bogen",
Damage: "1W6",
RangeNear: 10,
RangeMiddle: 30,
RangeFar: 100,
}
err := weapon.Create()
require.NoError(t, err)
// Verify the weapon was created with ranges
foundWeapon := &Weapon{}
err = foundWeapon.FirstId(weapon.ID)
require.NoError(t, err)
assert.Equal(t, 10, foundWeapon.RangeNear)
assert.Equal(t, 30, foundWeapon.RangeMiddle)
assert.Equal(t, 100, foundWeapon.RangeFar)
}
func TestWeapon_IsRanged(t *testing.T) {
setupGSMasterTestDB(t)
// Test ranged weapon (has at least one range > 0)
rangedWeapon := &Weapon{
Equipment: Equipment{
GameSystem: "midgard",
Name: "TestArmbrust",
},
SkillRequired: "Armbrust",
Damage: "2W6",
RangeNear: 15,
RangeMiddle: 50,
RangeFar: 150,
}
err := rangedWeapon.Create()
require.NoError(t, err)
assert.True(t, rangedWeapon.IsRanged(), "Weapon with ranges should be ranged")
// Test melee weapon (all ranges are 0)
meleeWeapon := &Weapon{
Equipment: Equipment{
GameSystem: "midgard",
Name: "TestSchwert",
},
SkillRequired: "Einhandschwerter",
Damage: "1W6+2",
RangeNear: 0,
RangeMiddle: 0,
RangeFar: 0,
}
err = meleeWeapon.Create()
require.NoError(t, err)
assert.False(t, meleeWeapon.IsRanged(), "Weapon with no ranges should not be ranged")
}
// =============================================================================
// Tests for Container struct
// =============================================================================
+10 -104
View File
@@ -506,113 +506,19 @@ func TestIntegration_CompleteWorkflow(t *testing.T) {
// Run with: go test -v ./pdfrender/... -run TestVisualInspection
func TestVisualInspection_AllPages(t *testing.T) {
database.SetupTestDB()
// Create a rich character with data for all page types
char := &models.Char{
BamortBase: models.BamortBase{
Name: "Integration Test",
},
Typ: "Krieger",
Grad: 8,
Alter: 42,
Groesse: 185,
Gewicht: 92,
Gender: "m",
SocialClass: "Frei",
Glaube: "Apshai",
Herkunft: "Erainn",
Eigenschaften: []models.Eigenschaft{
{Name: "St", Value: 95},
{Name: "Gs", Value: 85},
{Name: "Gw", Value: 80},
{Name: "Ko", Value: 90},
{Name: "In", Value: 75},
{Name: "Zt", Value: 70},
{Name: "Au", Value: 80},
{Name: "pA", Value: 85},
{Name: "Wk", Value: 70},
},
Lp: models.Lp{Value: 42, Max: 48},
Ap: models.Ap{Value: 28, Max: 32},
B: models.B{Value: 18},
Vermoegen: models.Vermoegen{
Goldstuecke: 100,
Silberstuecke: 50,
Kupferstuecke: 2,
},
// Load character Fanjo Vetrani with ID 18 from test database
char := &models.Char{}
err := char.FirstID("18")
if err != nil {
t.Fatalf("Failed to load character with ID 18 (Fanjo Vetrani): %v", err)
}
// Add skills
skillNames := []string{
"Schwimmen", "Klettern", "Reiten", "Laufen", "Springen",
"Balancieren", "Schleichen", "Sich Verstecken", "Singen",
"Tanzen", "Musizieren", "Malen", "Kochen", "Erste Hilfe",
"Himmelskunde", "Pflanzenkunde", "Tierkunde", "Heilkunde",
"Geschichte", "Lesen/Schreiben", "Rechnen", "Schätzen",
"Heilkunde", "Giftmischen", "Alchimie", "Schlösser öffnen",
}
char.Fertigkeiten = make([]models.SkFertigkeit, len(skillNames))
for i, name := range skillNames {
char.Fertigkeiten[i] = models.SkFertigkeit{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{Name: name},
},
Fertigkeitswert: 8 + i%12,
Pp: i % 6,
}
}
// Add weapons
weaponNames := []string{
"Langschwert", "Kurzschwert", "Kriegshammer", "Streitaxt",
"Speer", "Langbogen", "Armbrust", "Dolch", "Schild",
}
char.Waffenfertigkeiten = make([]models.SkWaffenfertigkeit, len(weaponNames))
for i, name := range weaponNames {
char.Waffenfertigkeiten[i] = models.SkWaffenfertigkeit{
SkFertigkeit: models.SkFertigkeit{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{Name: name},
},
Fertigkeitswert: 12 + i*2,
Pp: i,
},
}
}
// Add spells
spellNames := []string{
"Macht über die belebte Natur", "Macht über das Selbst",
"Erkennen von Gift", "Heilen von Wunden", "Bannen von Zauberwerk",
"Schutz vor Dämonen", "Macht über Unbelebtes", "Angst",
"Unsichtbarkeit", "Feuerlanze",
}
char.Zauber = make([]models.SkZauber, len(spellNames))
for i, name := range spellNames {
char.Zauber[i] = models.SkZauber{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{Name: name},
},
Bonus: i % 3,
}
}
// Add equipment
equipmentNames := []string{
"Rüstung (Leder)", "Helm", "Stiefel", "Umhang", "Rucksack",
"Seil (20m)", "Fackel (5x)", "Öllampe", "Zunderbüchse",
"Wasserschlauch", "Proviant (7 Tage)", "Schlafsack",
"Zelt", "Kochgeschirr", "Werkzeug",
}
char.Ausruestung = make([]models.EqAusruestung, len(equipmentNames))
for i, name := range equipmentNames {
char.Ausruestung[i] = models.EqAusruestung{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{Name: name},
},
Anzahl: 1 + i%3,
Gewicht: 0.5 + float64(i%10)*0.5,
}
// Verify we loaded the correct character
if char.Name == "" {
t.Fatalf("Character loaded but has empty name")
}
t.Logf("Loaded character: %s (ID: %d)", char.Name, char.ID)
// Map to view model
viewModel, err := MapCharacterToViewModel(char)
+100 -20
View File
@@ -27,6 +27,7 @@ func MapCharacterToViewModel(char *models.Char) (*CharacterSheetViewModel, error
Height: char.Groesse,
Weight: char.Gewicht,
Gender: char.Gender,
Hand: char.Hand,
Homeland: char.Herkunft,
Religion: char.Glaube,
Stand: char.SocialClass,
@@ -124,39 +125,54 @@ func mapSkills(char *models.Char) []SkillViewModel {
return skills
}
// mapWeapons converts character weapon skills to WeaponViewModel
// mapWeapons converts equipped weapons to WeaponViewModel
// EW = Waffenfertigkeit.Fertigkeitswert + Character.AngriffBonus + Weapon.Anb
func mapWeapons(char *models.Char) []WeaponViewModel {
weapons := make([]WeaponViewModel, 0, len(char.Waffenfertigkeiten))
weapons := make([]WeaponViewModel, 0, len(char.Waffen))
// Calculate character's attack bonus once
// Calculate character's bonuses once
attrs := mapAttributes(char)
angriffsBonus := calculateAttributeBonus(attrs.Gs)
// schadenBonus will be used later for damage calculation
// schadenBonus := (attrs.St / 20) + (attrs.Gs / 30) - 3
schadenBonus := calculateAttributeBonus(attrs.St)
// Create a map of equipped weapons for quick lookup
equippedWeapons := make(map[string]*models.EqWaffe)
for i := range char.Waffen {
equippedWeapons[char.Waffen[i].Name] = &char.Waffen[i]
// Create a map of weapon skills for quick lookup
weaponSkills := make(map[string]int)
for _, skill := range char.Waffenfertigkeiten {
weaponSkills[skill.Name] = skill.Fertigkeitswert
}
for _, weaponSkill := range char.Waffenfertigkeiten {
// Iterate over equipped weapons
for _, equippedWeapon := range char.Waffen {
vm := WeaponViewModel{
Name: weaponSkill.Name,
Value: weaponSkill.Fertigkeitswert, // Base skill value
Name: equippedWeapon.Name,
}
// If character has this weapon equipped, add weapon bonuses
if equippedWeapon, exists := equippedWeapons[weaponSkill.Name]; exists {
// EW = skill + character attack bonus + weapon attack bonus
vm.Value += angriffsBonus + equippedWeapon.Anb
// Load weapon from gsm_weapons to get base stats and required skill
baseWeapon := &models.Weapon{}
err := baseWeapon.First(equippedWeapon.Name)
// TODO: Calculate damage including weapon and character bonuses
// TODO: Add range information for ranged weapons
if err == nil && baseWeapon.ID > 0 {
// Calculate attack value: skill + character bonus + weapon bonus
skillValue := 0
if baseWeapon.SkillRequired != "" {
skillValue = weaponSkills[baseWeapon.SkillRequired]
}
vm.Value = skillValue + angriffsBonus + equippedWeapon.Anb
// Calculate damage: Base weapon damage + character bonus + weapon damage bonus
vm.Damage = calculateWeaponDamageWithBase(baseWeapon, schadenBonus, equippedWeapon.Schb)
// Add range information for ranged weapons
if baseWeapon.IsRanged() {
vm.Range = fmt.Sprintf("%d/%d/%d",
baseWeapon.RangeNear,
baseWeapon.RangeMiddle,
baseWeapon.RangeFar)
vm.IsRanged = true
}
} else {
// No equipped weapon, just use skill + character bonus
vm.Value += angriffsBonus
// Weapon not found in gsm_weapons, use basic info
vm.Value = angriffsBonus + equippedWeapon.Anb
}
weapons = append(weapons, vm)
@@ -263,3 +279,67 @@ func calculateAttributeBonus(value int) int {
}
return 0
}
// calculateWeaponDamageWithBase calculates the total damage string using an already-loaded weapon
// Format: BaseDamage+TotalBonus, e.g., "1W6+3"
// TotalBonus = Character's SchadenBonus + Weapon's Schadensbonus (Schb)
func calculateWeaponDamageWithBase(baseWeapon *models.Weapon, schadenBonus int, weaponSchb int) string {
baseDamage := baseWeapon.Damage
if baseDamage == "" {
return ""
}
// Calculate total damage bonus
totalBonus := schadenBonus + weaponSchb
// Format the damage string
if totalBonus > 0 {
return fmt.Sprintf("%s+%d", baseDamage, totalBonus)
} else if totalBonus < 0 {
return fmt.Sprintf("%s%d", baseDamage, totalBonus)
}
return baseDamage
}
// calculateWeaponDamage calculates the total damage string for a weapon
// Format: BaseDamage+TotalBonus, e.g., "1W6+3"
// TotalBonus = Character's SchadenBonus + Weapon's Schadensbonus (Schb)
func calculateWeaponDamage(weaponName string, schadenBonus int, weaponSchb int) string {
// Try to load weapon details from gsmaster to get base damage
var baseDamage string
if database.DB != nil {
masterWeapon := &models.Weapon{}
err := masterWeapon.First(weaponName)
if err == nil && masterWeapon.ID > 0 && masterWeapon.Damage != "" {
return calculateWeaponDamageWithBase(masterWeapon, schadenBonus, weaponSchb)
}
}
return baseDamage
}
// calculateWeaponRange returns the range string for a weapon and whether it's ranged
// Format: "Nah/Mittel/Fern", e.g., "10/30/100"
// Returns (rangeString, isRanged)
func calculateWeaponRange(weaponName string) (string, bool) {
// Try to load weapon details from gsmaster to get ranges
if database.DB != nil {
masterWeapon := &models.Weapon{}
err := masterWeapon.First(weaponName)
if err == nil && masterWeapon.ID > 0 {
// Check if weapon is ranged (at least one range value > 0)
if masterWeapon.IsRanged() {
// Format as "Nah/Mittel/Fern"
rangeStr := fmt.Sprintf("%d/%d/%d",
masterWeapon.RangeNear,
masterWeapon.RangeMiddle,
masterWeapon.RangeFar)
return rangeStr, true
}
}
}
return "", false
}
+29 -2
View File
@@ -1,6 +1,7 @@
package pdfrender
import (
"bamort/database"
"bamort/models"
"testing"
)
@@ -179,6 +180,21 @@ func TestMapCharacterToViewModel_Skills(t *testing.T) {
}
func TestMapCharacterToViewModel_Weapons(t *testing.T) {
// Setup test database for weapon lookup
database.SetupTestDB()
// Create test weapon in gsm_weapons
database.DB.Where("name = ?", "Langschwert").Delete(&models.Weapon{})
testWeapon := &models.Weapon{
Equipment: models.Equipment{
GameSystem: "midgard",
Name: "Langschwert",
},
SkillRequired: "Schwerter",
Damage: "1W6",
}
_ = testWeapon.Create()
// Arrange
char := &models.Char{
BamortBase: models.BamortBase{
@@ -190,7 +206,7 @@ func TestMapCharacterToViewModel_Weapons(t *testing.T) {
SkFertigkeit: models.SkFertigkeit{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{
Name: "Langschwert",
Name: "Schwerter",
},
},
Fertigkeitswert: 12,
@@ -199,6 +215,17 @@ func TestMapCharacterToViewModel_Weapons(t *testing.T) {
},
},
},
Waffen: []models.EqWaffe{
{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{
Name: "Langschwert",
},
},
Anb: 0,
Schb: 0,
},
},
}
// Act
@@ -218,7 +245,7 @@ func TestMapCharacterToViewModel_Weapons(t *testing.T) {
t.Errorf("Expected weapon name 'Langschwert', got '%s'", weapon.Name)
}
if weapon.Value != 12 {
t.Errorf("Expected weapon value 12, got %d", weapon.Value)
t.Errorf("Expected weapon value 12 (skill value), got %d", weapon.Value)
}
}
+22 -19
View File
@@ -23,25 +23,28 @@
- Updated TestPaginationUsesTemplateMetadata to read from template files
- Tests will automatically adapt when template capacities change
* ✅ Weapon model enhancements:
- ✅ Added RangeNear, RangeMiddle, RangeFar integer fields to gsm_weapons table
- ✅ Added IsRanged() method that returns true if at least one range value > 0
- ✅ Damage field already exists in gsm_weapons table
- ✅ EqWaffe already contains bonus values: Anb (Attack), Schb (Damage), Abwb (Defense)
- ✅ All tests pass with new fields
* ✅ Page 2 Weapons Table - Complete implementation:
- ✅ Changed TestVisualInspection_AllPages to load character Fanjo Vetrani (ID 18) from test database
- ✅ Damage calculation implemented:
- Calculates total damage: Base Weapon Damage + Character SchadenBonus + Weapon Schb
- Format: "1W6+3" where +3 = SchadenBonus + Schb
- Implemented calculateWeaponDamage() function
- Added TestMapWeapons_WithDamageCalculation test
- ✅ Ranged weapon ranges implemented:
- Shows ranges for ranged weapons (Bogen, Armbrust, etc.)
- Format: "Nah/Mittel/Fern" (e.g., "10/30/100")
- Implemented calculateWeaponRange() function
- Added TestMapWeapons_WithRangedWeaponRanges test
- Marks weapons as ranged using IsRanged field
- ✅ All tests pass
## TODO (Remaining)
* Page 2 Weapons - Damage calculation:
- Calculate and display weapon damage including:
- Base weapon damage (from models.Weapon)
- Character's SchadenBonus
- Weapon's Schadensbonus (Schb from EqWaffe)
- Format: e.g., "1W6+3" where +3 = SchadenBonus + Schb
* Page 2 Weapons - Ranged weapon ranges:
- For ranged weapons (Bogen, Armbrust, Wurfwaffe), show:
- Range for "Nah" (near)
- Range for "Mittel" (medium)
- Range for "Fern" (far)
- This data should come from models.Weapon.Range or similar field
.
* add 3 field to gsm_weapons that holds the ranges near, middle, far all values are measured in meters so integer seems to be a good datatype. If at least 1 of 3 values is > 0 the weapon is treated as a ranged weapon
* add a field to gsm_weapons that holds the damage the weapon creates Damage is notated as 2W6+3
* EqWaffe already contains the values for the bunus values we need Attack (Anb), Damage (Schb) und Defence (Abwb)
* currently the template fetched for rendering is set to Default_A4_Quer
+32 -5
View File
@@ -1,6 +1,7 @@
package pdfrender
import (
"bamort/database"
"bamort/models"
"os"
"testing"
@@ -139,7 +140,22 @@ func TestPage3MagicItemsCapacity(t *testing.T) {
}
func TestWeaponsWithEW(t *testing.T) {
// Test that weapons use Waffenfertigkeiten with correct EW
// Setup test database for weapon lookup
database.SetupTestDB()
// Create test weapon in gsm_weapons
database.DB.Where("name = ?", "Schwert").Delete(&models.Weapon{})
testWeapon := &models.Weapon{
Equipment: models.Equipment{
GameSystem: "midgard",
Name: "Schwert",
},
SkillRequired: "Schwerter",
Damage: "1W6",
}
_ = testWeapon.Create()
// Test that equipped weapons use Waffenfertigkeiten with correct EW
char := &models.Char{
BamortBase: models.BamortBase{
ID: 1,
@@ -150,7 +166,7 @@ func TestWeaponsWithEW(t *testing.T) {
SkFertigkeit: models.SkFertigkeit{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{
Name: "Schwert",
Name: "Schwerter",
},
},
Fertigkeitswert: 15,
@@ -158,6 +174,17 @@ func TestWeaponsWithEW(t *testing.T) {
},
},
},
Waffen: []models.EqWaffe{
{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{
Name: "Schwert",
},
},
Anb: 0,
Schb: 0,
},
},
}
viewModel, err := MapCharacterToViewModel(char)
@@ -165,9 +192,9 @@ func TestWeaponsWithEW(t *testing.T) {
t.Fatalf("Failed to map character: %v", err)
}
// Weapons should contain weapon skills with EW
// Weapons should contain equipped weapons with EW from skill
if len(viewModel.Weapons) == 0 {
t.Fatal("Expected weapons from Waffenfertigkeiten, got none")
t.Fatal("Expected weapons from equipped Waffen, got none")
}
weapon := viewModel.Weapons[0]
@@ -175,6 +202,6 @@ func TestWeaponsWithEW(t *testing.T) {
t.Errorf("Expected weapon name 'Schwert', got '%s'", weapon.Name)
}
if weapon.Value != 15 {
t.Errorf("Expected weapon EW 15, got %d", weapon.Value)
t.Errorf("Expected weapon EW 15 (from skill), got %d", weapon.Value)
}
}
+1
View File
@@ -24,6 +24,7 @@ type CharacterInfo struct {
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
+261 -10
View File
@@ -1,6 +1,7 @@
package pdfrender
import (
"bamort/database"
"bamort/models"
"testing"
)
@@ -8,6 +9,30 @@ import (
// TestMapWeapons_WithEWCalculation tests that weapon EW is correctly calculated
// EW = Waffenfertigkeit.Fertigkeitswert + Character.AngriffBonus + Weapon.Anb
func TestMapWeapons_WithEWCalculation(t *testing.T) {
// Setup test database
database.SetupTestDB()
// Create weapons in gsmaster with skill requirements
testSword := &models.Weapon{
Equipment: models.Equipment{
GameSystem: "midgard",
Name: "Langschwert",
},
SkillRequired: "Schwerter",
Damage: "1W6",
}
_ = testSword.Create()
testBow := &models.Weapon{
Equipment: models.Equipment{
GameSystem: "midgard",
Name: "Langbogen",
},
SkillRequired: "Bogen",
Damage: "1W6",
}
_ = testBow.Create()
// Arrange - Create a character with weapon skills and equipped weapons
char := &models.Char{
BamortBase: models.BamortBase{
@@ -20,7 +45,7 @@ func TestMapWeapons_WithEWCalculation(t *testing.T) {
SkFertigkeit: models.SkFertigkeit{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{
Name: "Langschwert",
Name: "Schwerter",
},
},
Fertigkeitswert: 12, // Base skill value
@@ -51,7 +76,7 @@ func TestMapWeapons_WithEWCalculation(t *testing.T) {
{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{
Name: "Bogen",
Name: "Langbogen",
},
},
Anb: 0,
@@ -90,10 +115,10 @@ func TestMapWeapons_WithEWCalculation(t *testing.T) {
t.Errorf("Expected sword EW >= 14 (skill 12 + weapon bonus 2), got %d", sword.Value)
}
// Test Bogen
// Test Langbogen
bow := vm.Weapons[1]
if bow.Name != "Bogen" {
t.Errorf("Expected weapon name 'Bogen', got '%s'", bow.Name)
if bow.Name != "Langbogen" {
t.Errorf("Expected weapon name 'Langbogen', got '%s'", bow.Name)
}
if bow.Value < 10 {
@@ -102,14 +127,240 @@ func TestMapWeapons_WithEWCalculation(t *testing.T) {
}
// TestMapWeapons_WithDamageCalculation tests that weapon damage is correctly calculated
// Damage = Base Weapon Damage + Character SchadenBonus + Weapon Schb
func TestMapWeapons_WithDamageCalculation(t *testing.T) {
// This test will be implemented to verify damage calculation
// For now, we mark it as a placeholder for TDD
t.Skip("TODO: Implement damage calculation test")
// Setup test database to load weapon data
database.SetupTestDB()
// Create test weapon in gsmaster if it doesn't exist
testWeapon := &models.Weapon{
Equipment: models.Equipment{
GameSystem: "midgard",
Name: "Langschwert",
},
SkillRequired: "Schwerter",
Damage: "1W6",
}
_ = testWeapon.Create() // Ignore error if already exists
// Arrange - Create a character with weapons that have damage values
char := &models.Char{
BamortBase: models.BamortBase{
ID: 1,
Name: "Test Fighter",
},
// Weapon skills
Waffenfertigkeiten: []models.SkWaffenfertigkeit{
{
SkFertigkeit: models.SkFertigkeit{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{
Name: "Schwerter",
},
},
Fertigkeitswert: 12,
},
},
},
// Equipped weapons with damage bonuses
Waffen: []models.EqWaffe{
{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{
Name: "Langschwert",
},
},
Schb: 2, // Weapon's damage bonus
},
},
// Character attributes that influence damage bonus
Eigenschaften: []models.Eigenschaft{
{Name: "St", Value: 95}, // High strength gives SchadenBonus = +1
{Name: "Gw", Value: 80},
},
}
// Act
vm, err := MapCharacterToViewModel(char)
// Assert
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(vm.Weapons) != 1 {
t.Fatalf("Expected 1 weapon, got %d", len(vm.Weapons))
}
sword := vm.Weapons[0]
// Damage should be in format like "1W6+3" where the bonus combines
// Character's SchadenBonus (from St=95 -> +1) + Weapon's Schb (2) = +3
if sword.Damage == "" {
t.Error("Expected weapon to have damage value, got empty string")
}
// The damage string should be "1W6+3"
// Base damage "1W6" + total bonus (+3)
expectedDamage := "1W6+3"
if sword.Damage != expectedDamage {
t.Errorf("Expected damage '%s', got '%s'", expectedDamage, sword.Damage)
}
t.Logf("✓ Weapon damage correctly calculated: %s (SchadenBonus %d + Weapon Schb 2 = +3)",
sword.Damage, vm.DerivedValues.SchadenBonus)
}
// TestMapWeapons_WithRangedWeaponRanges tests that ranged weapons show their ranges
func TestMapWeapons_WithRangedWeaponRanges(t *testing.T) {
// This test will verify that ranged weapons (Bogen, Armbrust) show ranges
t.Skip("TODO: Implement ranged weapon ranges test")
// Setup test database
database.SetupTestDB()
// Delete existing test weapons to ensure clean state
database.DB.Where("name IN (?, ?)", "Langbogen", "Kurzschwert").Delete(&models.Weapon{})
// Create a ranged weapon in gsmaster with range values
testBow := &models.Weapon{
Equipment: models.Equipment{
GameSystem: "midgard",
Name: "Langbogen",
},
SkillRequired: "Bogen",
Damage: "1W6",
RangeNear: 10,
RangeMiddle: 30,
RangeFar: 100,
}
err := testBow.Create()
if err != nil {
t.Fatalf("Failed to create Langbogen: %v", err)
}
// Verify the weapon was created
verifyWeapon := &models.Weapon{}
verifyErr := verifyWeapon.First("Langbogen")
if verifyErr != nil || verifyWeapon.ID == 0 {
t.Fatalf("Failed to create/find Langbogen in database: %v", verifyErr)
}
if verifyWeapon.RangeNear != 10 || verifyWeapon.RangeMiddle != 30 || verifyWeapon.RangeFar != 100 {
t.Fatalf("Langbogen ranges not set correctly: got %d/%d/%d, expected 10/30/100",
verifyWeapon.RangeNear, verifyWeapon.RangeMiddle, verifyWeapon.RangeFar)
}
// Create a melee weapon without ranges
testSword := &models.Weapon{
Equipment: models.Equipment{
GameSystem: "midgard",
Name: "Kurzschwert",
},
SkillRequired: "Schwerter",
Damage: "1W6+1",
RangeNear: 0,
RangeMiddle: 0,
RangeFar: 0,
}
_ = testSword.Create()
// Arrange - Character with both ranged and melee weapons
char := &models.Char{
BamortBase: models.BamortBase{
ID: 1,
Name: "Test Archer",
},
Waffenfertigkeiten: []models.SkWaffenfertigkeit{
{
SkFertigkeit: models.SkFertigkeit{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{
Name: "Bogen",
},
},
Fertigkeitswert: 14,
},
},
{
SkFertigkeit: models.SkFertigkeit{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{
Name: "Schwerter",
},
},
Fertigkeitswert: 10,
},
},
},
Waffen: []models.EqWaffe{
{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{
Name: "Langbogen",
},
},
},
{
BamortCharTrait: models.BamortCharTrait{
BamortBase: models.BamortBase{
Name: "Kurzschwert",
},
},
},
},
Eigenschaften: []models.Eigenschaft{
{Name: "St", Value: 75},
{Name: "Gs", Value: 85},
},
}
// Act
vm, err := MapCharacterToViewModel(char)
// Assert
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(vm.Weapons) != 2 {
t.Fatalf("Expected 2 weapons, got %d", len(vm.Weapons))
}
// Find the bow
var bow *WeaponViewModel
var sword *WeaponViewModel
for i := range vm.Weapons {
if vm.Weapons[i].Name == "Langbogen" {
bow = &vm.Weapons[i]
} else if vm.Weapons[i].Name == "Kurzschwert" {
sword = &vm.Weapons[i]
}
}
// Test ranged weapon
if bow == nil {
t.Fatal("Langbogen not found in weapons")
}
if !bow.IsRanged {
t.Error("Expected Langbogen to be marked as ranged weapon")
}
// Range should be formatted as "10/30/100" (Nah/Mittel/Fern)
expectedRange := "10/30/100"
if bow.Range != expectedRange {
t.Errorf("Expected bow range '%s', got '%s'", expectedRange, bow.Range)
}
t.Logf("✓ Ranged weapon ranges: %s", bow.Range)
// Test melee weapon
if sword == nil {
t.Fatal("Kurzschwert not found in weapons")
}
if sword.IsRanged {
t.Error("Expected Kurzschwert not to be marked as ranged weapon")
}
if sword.Range != "" {
t.Errorf("Expected melee weapon to have empty range, got '%s'", sword.Range)
}
}
@@ -159,14 +159,14 @@
<th>Waffe</th>
<th>Fert.<br>wert</th>
<th>Schaden</th>
<th>Nah</th>
<th>Reichweite<br/>nah/mitte/fern</th>
</tr>
{{range .Weapons}}
<tr>
<td>{{.Name}}</td>
<td>{{if .Value}}{{.Value}}{{else}}&nbsp;{{end}}</td>
<td>{{.Damage}}</td>
<td>&nbsp;</td>
<td>{{if .Damage}}{{.Damage}}{{else}}&nbsp;{{end}}</td>
<td>{{if .Range}}{{.Range}}{{else}}&nbsp;{{end}}</td>
</tr>
{{end}}
</table>