weapons are displayed fine
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
// =============================================================================
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}} {{end}}</td>
|
||||
<td>{{.Damage}}</td>
|
||||
<td> </td>
|
||||
<td>{{if .Damage}}{{.Damage}}{{else}} {{end}}</td>
|
||||
<td>{{if .Range}}{{.Range}}{{else}} {{end}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
|
||||
Reference in New Issue
Block a user