mapping the character data to a viewmodel
that can be rendered easily into the html template
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
package pdfrender
|
||||
|
||||
import (
|
||||
"bamort/models"
|
||||
)
|
||||
|
||||
// MapCharacterToViewModel converts a domain Character to a CharacterSheetViewModel
|
||||
func MapCharacterToViewModel(char *models.Char) (*CharacterSheetViewModel, error) {
|
||||
vm := &CharacterSheetViewModel{
|
||||
Skills: make([]SkillViewModel, 0),
|
||||
Weapons: make([]WeaponViewModel, 0),
|
||||
Spells: make([]SpellViewModel, 0),
|
||||
MagicItems: make([]MagicItemViewModel, 0),
|
||||
Equipment: make([]EquipmentViewModel, 0),
|
||||
GameResults: make([]GameResultViewModel, 0),
|
||||
}
|
||||
|
||||
// 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,
|
||||
Homeland: char.Herkunft,
|
||||
Religion: char.Glaube,
|
||||
Stand: char.SocialClass,
|
||||
IconBase64: "", // Will be set later if image exists
|
||||
}
|
||||
|
||||
// Map attributes
|
||||
vm.Attributes = mapAttributes(char)
|
||||
|
||||
// Map derived values
|
||||
vm.DerivedValues = mapDerivedValues(char)
|
||||
|
||||
// Map skills
|
||||
vm.Skills = mapSkills(char)
|
||||
|
||||
// Map weapons
|
||||
vm.Weapons = mapWeapons(char)
|
||||
|
||||
// Map spells
|
||||
vm.Spells = mapSpells(char)
|
||||
|
||||
// Map equipment
|
||||
vm.Equipment = mapEquipment(char)
|
||||
|
||||
return vm, nil
|
||||
}
|
||||
|
||||
// mapAttributes extracts attribute values from Eigenschaften slice
|
||||
func mapAttributes(char *models.Char) AttributeValues {
|
||||
attrs := AttributeValues{}
|
||||
|
||||
for _, e := range char.Eigenschaften {
|
||||
switch e.Name {
|
||||
case "St":
|
||||
attrs.St = e.Value
|
||||
case "Gs":
|
||||
attrs.Gs = e.Value
|
||||
case "Gw":
|
||||
attrs.Gw = e.Value
|
||||
case "Ko":
|
||||
attrs.Ko = e.Value
|
||||
case "In":
|
||||
attrs.In = e.Value
|
||||
case "Zt":
|
||||
attrs.Zt = e.Value
|
||||
case "Au":
|
||||
attrs.Au = e.Value
|
||||
case "pA":
|
||||
attrs.PA = e.Value
|
||||
case "Wk":
|
||||
attrs.Wk = e.Value
|
||||
}
|
||||
}
|
||||
|
||||
attrs.B = char.B.Value
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
// mapDerivedValues extracts derived values like LP, AP, etc.
|
||||
func mapDerivedValues(char *models.Char) DerivedValueSet {
|
||||
return DerivedValueSet{
|
||||
LPMax: char.Lp.Max,
|
||||
LPAktuell: char.Lp.Value,
|
||||
APMax: char.Ap.Max,
|
||||
APAktuell: char.Ap.Value,
|
||||
}
|
||||
}
|
||||
|
||||
// mapSkills converts character skills to SkillViewModel
|
||||
func mapSkills(char *models.Char) []SkillViewModel {
|
||||
skills := make([]SkillViewModel, 0, len(char.Fertigkeiten))
|
||||
|
||||
for _, skill := range char.Fertigkeiten {
|
||||
skills = append(skills, SkillViewModel{
|
||||
Name: skill.Name,
|
||||
Category: skill.Category,
|
||||
Value: skill.Fertigkeitswert,
|
||||
Bonus: skill.Bonus,
|
||||
PracticePoints: skill.Pp,
|
||||
IsLearned: skill.Fertigkeitswert > 0,
|
||||
})
|
||||
}
|
||||
|
||||
return skills
|
||||
}
|
||||
|
||||
// mapWeapons converts character weapon skills to WeaponViewModel
|
||||
func mapWeapons(char *models.Char) []WeaponViewModel {
|
||||
weapons := make([]WeaponViewModel, 0, len(char.Waffenfertigkeiten))
|
||||
|
||||
for _, weapon := range char.Waffenfertigkeiten {
|
||||
weapons = append(weapons, WeaponViewModel{
|
||||
Name: weapon.Name,
|
||||
Value: weapon.Fertigkeitswert,
|
||||
})
|
||||
}
|
||||
|
||||
return weapons
|
||||
}
|
||||
|
||||
// mapSpells converts character spells to SpellViewModel
|
||||
func mapSpells(char *models.Char) []SpellViewModel {
|
||||
spells := make([]SpellViewModel, 0, len(char.Zauber))
|
||||
|
||||
for _, spell := range char.Zauber {
|
||||
spells = append(spells, SpellViewModel{
|
||||
Name: spell.Name,
|
||||
})
|
||||
}
|
||||
|
||||
return spells
|
||||
}
|
||||
|
||||
// mapEquipment converts character equipment to EquipmentViewModel
|
||||
func mapEquipment(char *models.Char) []EquipmentViewModel {
|
||||
equipment := make([]EquipmentViewModel, 0, len(char.Ausruestung)+len(char.Behaeltnisse))
|
||||
|
||||
// Add regular equipment
|
||||
for _, item := range char.Ausruestung {
|
||||
equipment = append(equipment, EquipmentViewModel{
|
||||
Name: item.Name,
|
||||
Quantity: item.Anzahl,
|
||||
Weight: item.Gewicht,
|
||||
TotalWeight: item.Gewicht * float64(item.Anzahl),
|
||||
Value: int(item.Wert),
|
||||
Location: item.BeinhaltetIn,
|
||||
Container: item.BeinhaltetIn,
|
||||
IsWorn: item.BeinhaltetIn == "Am Körper",
|
||||
IsContainer: false,
|
||||
})
|
||||
}
|
||||
|
||||
// Add containers
|
||||
for _, container := range char.Behaeltnisse {
|
||||
equipment = append(equipment, EquipmentViewModel{
|
||||
Name: container.Name,
|
||||
Quantity: 1,
|
||||
Weight: container.Gewicht,
|
||||
TotalWeight: container.Gewicht,
|
||||
Value: int(container.Wert),
|
||||
IsContainer: true,
|
||||
})
|
||||
}
|
||||
|
||||
return equipment
|
||||
}
|
||||
@@ -0,0 +1,370 @@
|
||||
package pdfrender
|
||||
|
||||
import (
|
||||
"bamort/models"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMapCharacterToViewModel_BasicInfo(t *testing.T) {
|
||||
// Arrange
|
||||
char := &models.Char{
|
||||
BamortBase: models.BamortBase{
|
||||
ID: 1,
|
||||
Name: "Bjarnfinnur Haberdson",
|
||||
},
|
||||
Rasse: "Mensch",
|
||||
Typ: "Krieger",
|
||||
Grad: 5,
|
||||
Alter: 30,
|
||||
Gender: "männlich",
|
||||
Groesse: 180,
|
||||
Gewicht: 85,
|
||||
Herkunft: "Clanngadarn",
|
||||
Glaube: "Druide",
|
||||
SocialClass: "Mittelschicht",
|
||||
}
|
||||
|
||||
// Act
|
||||
vm, err := MapCharacterToViewModel(char)
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
||||
if vm.Character.Name != "Bjarnfinnur Haberdson" {
|
||||
t.Errorf("Expected name 'Bjarnfinnur Haberdson', got '%s'", vm.Character.Name)
|
||||
}
|
||||
|
||||
if vm.Character.Type != "Krieger" {
|
||||
t.Errorf("Expected type 'Krieger', got '%s'", vm.Character.Type)
|
||||
}
|
||||
|
||||
if vm.Character.Grade != 5 {
|
||||
t.Errorf("Expected grade 5, got %d", vm.Character.Grade)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapCharacterToViewModel_Attributes(t *testing.T) {
|
||||
// Arrange
|
||||
char := &models.Char{
|
||||
BamortBase: models.BamortBase{
|
||||
ID: 1,
|
||||
Name: "Test Character",
|
||||
},
|
||||
Eigenschaften: []models.Eigenschaft{
|
||||
{Name: "St", Value: 79},
|
||||
{Name: "Gs", Value: 65},
|
||||
{Name: "Gw", Value: 70},
|
||||
},
|
||||
B: models.B{Value: 10},
|
||||
}
|
||||
|
||||
// Act
|
||||
vm, err := MapCharacterToViewModel(char)
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
||||
if vm.Attributes.St != 79 {
|
||||
t.Errorf("Expected St=79, got %d", vm.Attributes.St)
|
||||
}
|
||||
if vm.Attributes.Gs != 65 {
|
||||
t.Errorf("Expected Gs=65, got %d", vm.Attributes.Gs)
|
||||
}
|
||||
if vm.Attributes.B != 10 {
|
||||
t.Errorf("Expected B=10, got %d", vm.Attributes.B)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapCharacterToViewModel_LPandAP(t *testing.T) {
|
||||
// Arrange
|
||||
char := &models.Char{
|
||||
BamortBase: models.BamortBase{
|
||||
ID: 1,
|
||||
Name: "Test Character",
|
||||
},
|
||||
Lp: models.Lp{Max: 20, Value: 18},
|
||||
Ap: models.Ap{Max: 30, Value: 25},
|
||||
}
|
||||
|
||||
// Act
|
||||
vm, err := MapCharacterToViewModel(char)
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
||||
if vm.DerivedValues.LPMax != 20 {
|
||||
t.Errorf("Expected LP Max=20, got %d", vm.DerivedValues.LPMax)
|
||||
}
|
||||
|
||||
if vm.DerivedValues.LPAktuell != 18 {
|
||||
t.Errorf("Expected LP Aktuell=18, got %d", vm.DerivedValues.LPAktuell)
|
||||
}
|
||||
|
||||
if vm.DerivedValues.APMax != 30 {
|
||||
t.Errorf("Expected AP Max=30, got %d", vm.DerivedValues.APMax)
|
||||
}
|
||||
|
||||
if vm.DerivedValues.APAktuell != 25 {
|
||||
t.Errorf("Expected AP Aktuell=25, got %d", vm.DerivedValues.APAktuell)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapCharacterToViewModel_Skills(t *testing.T) {
|
||||
// Arrange
|
||||
char := &models.Char{
|
||||
BamortBase: models.BamortBase{
|
||||
ID: 1,
|
||||
Name: "Test Character",
|
||||
},
|
||||
Fertigkeiten: []models.SkFertigkeit{
|
||||
{
|
||||
BamortCharTrait: models.BamortCharTrait{
|
||||
BamortBase: models.BamortBase{
|
||||
Name: "Schwimmen",
|
||||
},
|
||||
},
|
||||
Fertigkeitswert: 10,
|
||||
Bonus: 2,
|
||||
Pp: 5,
|
||||
Category: "Körper",
|
||||
},
|
||||
{
|
||||
BamortCharTrait: models.BamortCharTrait{
|
||||
BamortBase: models.BamortBase{
|
||||
Name: "Klettern",
|
||||
},
|
||||
},
|
||||
Fertigkeitswert: 8,
|
||||
Bonus: 1,
|
||||
Pp: 3,
|
||||
Category: "Körper",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Act
|
||||
vm, err := MapCharacterToViewModel(char)
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
||||
if len(vm.Skills) != 2 {
|
||||
t.Fatalf("Expected 2 skills, got %d", len(vm.Skills))
|
||||
}
|
||||
|
||||
skill := vm.Skills[0]
|
||||
if skill.Name != "Schwimmen" {
|
||||
t.Errorf("Expected skill name 'Schwimmen', got '%s'", skill.Name)
|
||||
}
|
||||
if skill.Value != 10 {
|
||||
t.Errorf("Expected skill value 10, got %d", skill.Value)
|
||||
}
|
||||
if skill.Bonus != 2 {
|
||||
t.Errorf("Expected skill bonus 2, got %d", skill.Bonus)
|
||||
}
|
||||
if skill.PracticePoints != 5 {
|
||||
t.Errorf("Expected practice points 5, got %d", skill.PracticePoints)
|
||||
}
|
||||
if skill.Category != "Körper" {
|
||||
t.Errorf("Expected category 'Körper', got '%s'", skill.Category)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapCharacterToViewModel_Weapons(t *testing.T) {
|
||||
// Arrange
|
||||
char := &models.Char{
|
||||
BamortBase: models.BamortBase{
|
||||
ID: 1,
|
||||
Name: "Test Character",
|
||||
},
|
||||
Waffenfertigkeiten: []models.SkWaffenfertigkeit{
|
||||
{
|
||||
SkFertigkeit: models.SkFertigkeit{
|
||||
BamortCharTrait: models.BamortCharTrait{
|
||||
BamortBase: models.BamortBase{
|
||||
Name: "Langschwert",
|
||||
},
|
||||
},
|
||||
Fertigkeitswert: 12,
|
||||
Bonus: 3,
|
||||
Category: "Kampf",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
weapon := vm.Weapons[0]
|
||||
if weapon.Name != "Langschwert" {
|
||||
t.Errorf("Expected weapon name 'Langschwert', got '%s'", weapon.Name)
|
||||
}
|
||||
if weapon.Value != 12 {
|
||||
t.Errorf("Expected weapon value 12, got %d", weapon.Value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapCharacterToViewModel_Spells(t *testing.T) {
|
||||
// Arrange
|
||||
char := &models.Char{
|
||||
BamortBase: models.BamortBase{
|
||||
ID: 1,
|
||||
Name: "Test Character",
|
||||
},
|
||||
Zauber: []models.SkZauber{
|
||||
{
|
||||
BamortCharTrait: models.BamortCharTrait{
|
||||
BamortBase: models.BamortBase{
|
||||
Name: "Macht über die belebte Natur",
|
||||
},
|
||||
},
|
||||
Bonus: 2,
|
||||
Quelle: "Arkanum",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Act
|
||||
vm, err := MapCharacterToViewModel(char)
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
||||
if len(vm.Spells) != 1 {
|
||||
t.Fatalf("Expected 1 spell, got %d", len(vm.Spells))
|
||||
}
|
||||
|
||||
spell := vm.Spells[0]
|
||||
if spell.Name != "Macht über die belebte Natur" {
|
||||
t.Errorf("Expected spell name 'Macht über die belebte Natur', got '%s'", spell.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapCharacterToViewModel_Equipment(t *testing.T) {
|
||||
// Arrange
|
||||
char := &models.Char{
|
||||
BamortBase: models.BamortBase{
|
||||
ID: 1,
|
||||
Name: "Test Character",
|
||||
},
|
||||
Ausruestung: []models.EqAusruestung{
|
||||
{
|
||||
BamortCharTrait: models.BamortCharTrait{
|
||||
BamortBase: models.BamortBase{
|
||||
Name: "Rucksack",
|
||||
},
|
||||
},
|
||||
Anzahl: 1,
|
||||
Gewicht: 2.5,
|
||||
Wert: 10,
|
||||
BeinhaltetIn: "Am Körper",
|
||||
},
|
||||
{
|
||||
BamortCharTrait: models.BamortCharTrait{
|
||||
BamortBase: models.BamortBase{
|
||||
Name: "Seil (20m)",
|
||||
},
|
||||
},
|
||||
Anzahl: 1,
|
||||
Gewicht: 5.0,
|
||||
Wert: 15,
|
||||
BeinhaltetIn: "Rucksack",
|
||||
},
|
||||
},
|
||||
Behaeltnisse: []models.EqContainer{
|
||||
{
|
||||
BamortCharTrait: models.BamortCharTrait{
|
||||
BamortBase: models.BamortBase{
|
||||
Name: "Lederbeutel",
|
||||
},
|
||||
},
|
||||
Gewicht: 0.5,
|
||||
Wert: 5,
|
||||
Tragkraft: 10,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Act
|
||||
vm, err := MapCharacterToViewModel(char)
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
||||
if len(vm.Equipment) != 3 {
|
||||
t.Fatalf("Expected 3 equipment items, got %d", len(vm.Equipment))
|
||||
}
|
||||
|
||||
// Check first item
|
||||
item := vm.Equipment[0]
|
||||
if item.Name != "Rucksack" {
|
||||
t.Errorf("Expected equipment name 'Rucksack', got '%s'", item.Name)
|
||||
}
|
||||
if item.Quantity != 1 {
|
||||
t.Errorf("Expected quantity 1, got %d", item.Quantity)
|
||||
}
|
||||
if item.Weight != 2.5 {
|
||||
t.Errorf("Expected weight 2.5, got %f", item.Weight)
|
||||
}
|
||||
if item.Location != "Am Körper" {
|
||||
t.Errorf("Expected location 'Am Körper', got '%s'", item.Location)
|
||||
}
|
||||
|
||||
// Check container
|
||||
container := vm.Equipment[2]
|
||||
if container.Name != "Lederbeutel" {
|
||||
t.Errorf("Expected container name 'Lederbeutel', got '%s'", container.Name)
|
||||
}
|
||||
if !container.IsContainer {
|
||||
t.Error("Expected IsContainer to be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapCharacterToViewModel_GameResults(t *testing.T) {
|
||||
// Arrange - no game results in domain model yet
|
||||
char := &models.Char{
|
||||
BamortBase: models.BamortBase{
|
||||
ID: 1,
|
||||
Name: "Test Character",
|
||||
},
|
||||
}
|
||||
|
||||
// Act
|
||||
vm, err := MapCharacterToViewModel(char)
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
||||
// Should be empty slice, not nil
|
||||
if vm.GameResults == nil {
|
||||
t.Error("Expected GameResults to be empty slice, got nil")
|
||||
}
|
||||
|
||||
if len(vm.GameResults) != 0 {
|
||||
t.Errorf("Expected 0 game results, got %d", len(vm.GameResults))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package pdfrender
|
||||
|
||||
// SliceList slices a list based on start index and max items
|
||||
// Returns the sliced list and whether there are more items
|
||||
func SliceList[T any](fullList []T, startIndex, maxItems int) ([]T, bool) {
|
||||
totalCount := len(fullList)
|
||||
endIndex := startIndex + maxItems
|
||||
|
||||
if startIndex >= totalCount {
|
||||
return []T{}, false
|
||||
}
|
||||
|
||||
if endIndex > totalCount {
|
||||
endIndex = totalCount
|
||||
}
|
||||
|
||||
return fullList[startIndex:endIndex], endIndex < totalCount
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package pdfrender
|
||||
|
||||
// TemplateMetadata contains information about a template's capacity and requirements
|
||||
type TemplateMetadata struct {
|
||||
Name string // Template name (e.g., "page1_stats.html")
|
||||
PageType string // "stats", "play", "spell", "equip"
|
||||
Description string
|
||||
Blocks []BlockMetadata // List blocks and their capacities
|
||||
}
|
||||
|
||||
// BlockMetadata defines a list block within a template and how many items it can hold
|
||||
type BlockMetadata struct {
|
||||
Name string // Logical name (e.g., "skills_column1", "weapons_main")
|
||||
ListType string // "skills", "weapons", "spells", "equipment", "magicItems", "gameResults"
|
||||
MaxItems int // Maximum number of items this block can display
|
||||
Filter string // Optional filter criteria (e.g., "learned", "unlearned", "languages")
|
||||
Column int // Column number for multi-column layouts (0 if single column)
|
||||
}
|
||||
|
||||
// TemplateSet contains all templates and their metadata for a format
|
||||
type TemplateSet struct {
|
||||
Name string // e.g., "Default_A4_Quer"
|
||||
Description string
|
||||
Templates []TemplateWithMeta
|
||||
}
|
||||
|
||||
// TemplateWithMeta combines a template with its metadata
|
||||
type TemplateWithMeta struct {
|
||||
Metadata TemplateMetadata
|
||||
Path string // File path to the template
|
||||
}
|
||||
|
||||
// DefaultA4QuerTemplateSet returns the template set for A4 Querformat
|
||||
func DefaultA4QuerTemplateSet() TemplateSet {
|
||||
return TemplateSet{
|
||||
Name: "Default_A4_Quer",
|
||||
Description: "Standard A4 Querformat Charakterbogen",
|
||||
Templates: []TemplateWithMeta{
|
||||
{
|
||||
Metadata: TemplateMetadata{
|
||||
Name: "page1_stats.html",
|
||||
PageType: "stats",
|
||||
Description: "Statistikseite mit Grundwerten",
|
||||
Blocks: []BlockMetadata{
|
||||
{
|
||||
Name: "skills_column1",
|
||||
ListType: "skills",
|
||||
MaxItems: 32,
|
||||
Column: 1,
|
||||
},
|
||||
{
|
||||
Name: "skills_column2",
|
||||
ListType: "skills",
|
||||
MaxItems: 32,
|
||||
Column: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
Path: "templates/Default_A4_Quer/page1_stats.html",
|
||||
},
|
||||
{
|
||||
Metadata: TemplateMetadata{
|
||||
Name: "page2_play.html",
|
||||
PageType: "play",
|
||||
Description: "Spielbogen mit gelernten Fertigkeiten und Waffen",
|
||||
Blocks: []BlockMetadata{
|
||||
{
|
||||
Name: "skills_learned",
|
||||
ListType: "skills",
|
||||
MaxItems: 24,
|
||||
Filter: "learned",
|
||||
},
|
||||
{
|
||||
Name: "skills_unlearned",
|
||||
ListType: "skills",
|
||||
MaxItems: 15,
|
||||
Filter: "unlearned",
|
||||
},
|
||||
{
|
||||
Name: "skills_languages",
|
||||
ListType: "skills",
|
||||
MaxItems: 11,
|
||||
Filter: "languages",
|
||||
},
|
||||
{
|
||||
Name: "weapons_main",
|
||||
ListType: "weapons",
|
||||
MaxItems: 30,
|
||||
},
|
||||
},
|
||||
},
|
||||
Path: "templates/Default_A4_Quer/page2_play.html",
|
||||
},
|
||||
{
|
||||
Metadata: TemplateMetadata{
|
||||
Name: "page3_spell.html",
|
||||
PageType: "spell",
|
||||
Description: "Zauberseite mit Zauberliste",
|
||||
Blocks: []BlockMetadata{
|
||||
{
|
||||
Name: "spells_column1",
|
||||
ListType: "spells",
|
||||
MaxItems: 12,
|
||||
Column: 1,
|
||||
},
|
||||
{
|
||||
Name: "spells_column2",
|
||||
ListType: "spells",
|
||||
MaxItems: 12,
|
||||
Column: 2,
|
||||
},
|
||||
{
|
||||
Name: "magic_items",
|
||||
ListType: "magicItems",
|
||||
MaxItems: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
Path: "templates/Default_A4_Quer/page3_spell.html",
|
||||
},
|
||||
{
|
||||
Metadata: TemplateMetadata{
|
||||
Name: "page4_equip.html",
|
||||
PageType: "equip",
|
||||
Description: "Ausrüstungsseite",
|
||||
Blocks: []BlockMetadata{
|
||||
{
|
||||
Name: "equipment_sections",
|
||||
ListType: "equipment",
|
||||
MaxItems: 20,
|
||||
},
|
||||
{
|
||||
Name: "game_results",
|
||||
ListType: "gameResults",
|
||||
MaxItems: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
Path: "templates/Default_A4_Quer/page4_equip.html",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetBlockMetadata returns the metadata for a specific block in a template
|
||||
func (tm *TemplateMetadata) GetBlockMetadata(blockName string) *BlockMetadata {
|
||||
for i := range tm.Blocks {
|
||||
if tm.Blocks[i].Name == blockName {
|
||||
return &tm.Blocks[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMaxItems returns the maximum items for a specific list type in this template
|
||||
func (tm *TemplateMetadata) GetMaxItems(listType string, filter string) int {
|
||||
total := 0
|
||||
for _, block := range tm.Blocks {
|
||||
if block.ListType == listType {
|
||||
if filter == "" || block.Filter == filter {
|
||||
total += block.MaxItems
|
||||
}
|
||||
}
|
||||
}
|
||||
return total
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package pdfrender
|
||||
|
||||
import "time"
|
||||
|
||||
// CharacterSheetViewModel represents all data needed to render a character sheet PDF
|
||||
type CharacterSheetViewModel struct {
|
||||
Character CharacterInfo
|
||||
Attributes AttributeValues
|
||||
DerivedValues DerivedValueSet
|
||||
Skills []SkillViewModel
|
||||
Weapons []WeaponViewModel
|
||||
Spells []SpellViewModel
|
||||
MagicItems []MagicItemViewModel
|
||||
Equipment []EquipmentViewModel
|
||||
GameResults []GameResultViewModel
|
||||
Meta PageMeta
|
||||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
}
|
||||
|
||||
// AttributeValues contains all character attributes
|
||||
type AttributeValues struct {
|
||||
St int // Stärke
|
||||
Gs int // Geschicklichkeit
|
||||
Gw int // Gewandtheit
|
||||
Ko int // Konstitution
|
||||
In int // Intelligenz
|
||||
Zt int // Zaubertaltent
|
||||
Au int // Aussehen
|
||||
PA int // Persönliche Ausstrahlung
|
||||
Wk int // Willenskraft
|
||||
B int // Bewegungsweite
|
||||
}
|
||||
|
||||
// DerivedValueSet contains all derived character values
|
||||
type DerivedValueSet struct {
|
||||
// Lebenspunkte & Ausdauer
|
||||
LP int
|
||||
AP int
|
||||
LPMax int
|
||||
APMax int
|
||||
LPAktuell int
|
||||
APAktuell int
|
||||
AusdauerBonus int
|
||||
|
||||
// Geschwindigkeit
|
||||
GG int // Grundgeschwindigkeit
|
||||
SG int // Schrittgeschwindigkeit
|
||||
|
||||
// Kampfwerte
|
||||
Abwehr int // z.B. "Abwehr+12"
|
||||
SchadenBonus int
|
||||
AngriffBonus int
|
||||
|
||||
// Resistenzen
|
||||
ResistenzGift int
|
||||
ResistenzKorper int
|
||||
ResistenzGeist int
|
||||
|
||||
// Zauberwerte
|
||||
Zaubern int // z.B. "+10/+9"
|
||||
ZaubernBonus int // Erster Zauberbonus
|
||||
|
||||
// Sonstige
|
||||
Sehen int // Sehen-Wert
|
||||
Horen int // Hören-Wert
|
||||
Riechen int // Riechen-Wert
|
||||
Schmecken int // Schmecken-Wert
|
||||
Sechster int // Sechster Sinn
|
||||
}
|
||||
|
||||
// SkillViewModel represents a skill for display
|
||||
type SkillViewModel struct {
|
||||
Name string
|
||||
Category string // z.B. "Kampf", "Körper", "Social"
|
||||
SkillType string // z.B. "Fert", "Waff", "Ungelernte Fertigkeit"
|
||||
Value int // Erfolgswert (EW)
|
||||
BaseValue int // Grundwert (für Statistikseite)
|
||||
Bonus int // Bonus/Malus
|
||||
PracticePoints int // Praxispunkte (PP)
|
||||
Attribute1 string // Leiteigenschaft Attribut für Bonus (z.B. "St")
|
||||
IsLearned bool // Ob die Fertigkeit gelernt wurde
|
||||
}
|
||||
|
||||
// WeaponViewModel represents a weapon for display
|
||||
type WeaponViewModel struct {
|
||||
Name string
|
||||
Value int // Erfolgswert (EW)
|
||||
ParryValue int // Abwehrwert (falls vorhanden)
|
||||
Damage string // Schaden (z.B. "1W6+2")
|
||||
Range string // Reichweite (für Fernkampfwaffen)
|
||||
Notes string // Besondere Eigenschaften
|
||||
IsRanged bool // Fernkampfwaffe ja/nein
|
||||
IsMagical bool // Magische Waffe ja/nein
|
||||
}
|
||||
|
||||
// SpellViewModel represents a spell for display
|
||||
type SpellViewModel struct {
|
||||
Name string
|
||||
AP int // Abenteuerpunkte
|
||||
Category int // Kategorie (z.B. "Beherrschen", "Erkennen")
|
||||
//CastValue int // Zauberwert
|
||||
CastTime string // Zauberdauer (z.B. "1 sec", "10 min")
|
||||
Range string // Reichweite (z.B. "0", "30m")
|
||||
Scope string // Wirkungsbereich (z.B. "1-10 Wesen", "Kegel 5m", "Zauberer", "m²", ...)
|
||||
Duration string // Wirkungsdauer (z.B. "0", "10 min")
|
||||
Objective string // wirkungsziel (z.B. Körper, Geist, Umgebung)
|
||||
CastingType string // Art des Zaubers (z.B. "Geste", "Wort", "Gedanke")
|
||||
Notes string // Notizen/Besonderheiten
|
||||
}
|
||||
|
||||
// MagicItemViewModel represents a magical item
|
||||
type MagicItemViewModel struct {
|
||||
Name string
|
||||
Description string
|
||||
Properties string // Magische Eigenschaften
|
||||
Charges int // Ladungen (falls zutreffend)
|
||||
Notes string
|
||||
}
|
||||
|
||||
// EquipmentViewModel represents an equipment item
|
||||
type EquipmentViewModel struct {
|
||||
Name string
|
||||
Quantity int
|
||||
Weight float64 // Gewicht pro Stück in kg
|
||||
TotalWeight float64 // Gesamtgewicht
|
||||
Location string // z.B. "Am Körper", "Container 1"
|
||||
Container string // Container-Name (z.B. "Becher, Holz")
|
||||
Value int // Wert in Währungseinheiten
|
||||
Notes string
|
||||
IsWorn bool // Am Körper getragen
|
||||
IsContainer bool // Ist selbst ein Container
|
||||
}
|
||||
|
||||
// GameResultViewModel represents a game session result
|
||||
type GameResultViewModel struct {
|
||||
Date time.Time
|
||||
EP int // Erfahrungspunkte
|
||||
Gold int // Gold erhalten
|
||||
Description string // Beschreibung der Sitzung
|
||||
Location string // Ort der Handlung
|
||||
Notes string
|
||||
}
|
||||
|
||||
// PageMeta contains metadata about the current page
|
||||
type PageMeta struct {
|
||||
Date string
|
||||
PageNumber int
|
||||
TotalPages int
|
||||
IsContinuation bool // Ist eine Fortsetzungsseite
|
||||
PageType string // "stats", "play", "spell", "equip"
|
||||
}
|
||||
|
||||
// PageData represents data for a single page (after pagination)
|
||||
type PageData struct {
|
||||
Character CharacterInfo
|
||||
Attributes AttributeValues
|
||||
DerivedValues DerivedValueSet
|
||||
|
||||
// Lists sliced according to template block metadata
|
||||
Skills []SkillViewModel
|
||||
Weapons []WeaponViewModel
|
||||
Spells []SpellViewModel
|
||||
MagicItems []MagicItemViewModel
|
||||
Equipment []EquipmentViewModel
|
||||
GameResults []GameResultViewModel
|
||||
|
||||
Meta PageMeta
|
||||
}
|
||||
Reference in New Issue
Block a user