Skip to content

Commit

Permalink
Fix issue with some back counter situations
Browse files Browse the repository at this point in the history
  • Loading branch information
sayden committed Nov 23, 2024
1 parent 9ecd529 commit be111da
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 134 deletions.
31 changes: 26 additions & 5 deletions counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package counters

import (
"bytes"
"encoding/json"
"fmt"
"io"
"path"
"strings"
"sync"
"text/template"

"dario.cat/mergo"
"github.com/fogleman/gg"
"github.com/pkg/errors"
"github.com/thehivecorporation/log"
Expand Down Expand Up @@ -244,7 +246,7 @@ func (c *Counter) ToVassal(sideName string) error {
return errors.New("vassal: side name is empty")
}

backFilename := strings.TrimRight(c.Filename, path.Ext(c.Filename)) + "_back.png"
backFilename := strings.TrimSuffix(c.Filename, path.Ext(c.Filename)) + "_back.png"
buf := bytes.NewBufferString("")
pieceTemp := PieceTemplateData{
FrontFilename: c.Filename,
Expand All @@ -263,13 +265,32 @@ func (c *Counter) ToVassal(sideName string) error {
data = strings.ReplaceAll(data, "	", "\t")
piece := PieceSlot{
EntryName: c.PrettyName,
// Gpid: c.Extra.Title,
Height: c.Height,
Width: c.Width,
Data: data,
Height: c.Height,
Width: c.Width,
Data: data,
}

c.VassalPiece = &piece

return nil
}

func (c *Counter) mergeFrontAndBack() (*Counter, error) {
if err := mergo.Merge(c.Back, c); err != nil {
return nil, fmt.Errorf("could not merge back and front counter: %w", err)
}
c.Back.Back = nil

if c.PrettyName == "" {
byt, _ := json.MarshalIndent(c, "", " ")
return nil, fmt.Errorf("PrettyName was empty for counter:\n%s\n", string(byt))
}

c.Back.PrettyName = c.PrettyName + "_back"
c.Back.Filename = strings.TrimSuffix(c.Filename, path.Ext(c.Filename)) + "_back.png"

c.Back.Images = mergeImagesOrTexts(c.Images, c.Back.Images)
c.Back.Texts = mergeImagesOrTexts(c.Texts, c.Back.Texts)

return c.Back, nil
}
214 changes: 114 additions & 100 deletions counter_prototype.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
"strings"
"sync"

"dario.cat/mergo"
"github.com/charmbracelet/log"
"github.com/pkg/errors"
"github.com/qdm12/reprint"
deepcopy "github.com/qdm12/reprint"
)

Expand Down Expand Up @@ -56,12 +56,7 @@ func (p *CounterPrototype) ToCounters(filenamesInUse *sync.Map, sideName, protot
newCounter.GenerateCounterFilename(sideName, positionNumberForFilename, filenamesInUse)

if p.Back != nil {
var tempBackCounter Counter
if err := deepcopy.FromTo(newCounter, &tempBackCounter); err != nil {
return nil, err
}

backCounter, err := mergeFrontAndBack(&tempBackCounter, p.Back, i)
backCounter, err := mergeFrontAndBack(&newCounter, p.Back, i)
if err != nil {
return nil, err
}
Expand All @@ -72,7 +67,7 @@ func (p *CounterPrototype) ToCounters(filenamesInUse *sync.Map, sideName, protot
if sideName != "" {
err = newCounter.ToVassal(sideName)
if err != nil {
log.Warn("could not create vassal piece", err)
log.Warn("could not create vassal piece from prototype", err)
}
}

Expand All @@ -91,7 +86,7 @@ func (p *CounterPrototype) applyPrototypes(newCounter *Counter, index int) error
if p.TextPrototypes != nil {
for _, textPrototype := range p.TextPrototypes {
originalText := Text{}
if err := deepcopy.FromTo(textPrototype.Text, &originalText); err != nil {
if err := reprint.FromTo(textPrototype.Text, &originalText); err != nil {
return err
}
originalText.String = textPrototype.StringList[index]
Expand All @@ -102,7 +97,7 @@ func (p *CounterPrototype) applyPrototypes(newCounter *Counter, index int) error
if p.ImagePrototypes != nil {
for _, imagePrototype := range p.ImagePrototypes {
originalImage := Image{}
if err := deepcopy.FromTo(imagePrototype.Image, &originalImage); err != nil {
if err := reprint.FromTo(imagePrototype.Image, &originalImage); err != nil {
return err
}
originalImage.Path = imagePrototype.PathList[index]
Expand All @@ -113,44 +108,99 @@ func (p *CounterPrototype) applyPrototypes(newCounter *Counter, index int) error
return nil
}

// isLengthConsistent checks if the lengths of text and image prototypes are consistent.
func (p *CounterPrototype) isLengthConsistent() (int, error) {
// find a reference length
length := p.getTextLength(p.TextPrototypes)

// if no text prototypes found to use for reference, try with image prototypes
if length == 0 {
length = p.getImageLength(p.ImagePrototypes)
}

if length == 0 {
return 0, errors.New("no prototypes found in the counter template")
}

lengths := map[string]int{
"Text prototypes": p.getTextLength(p.TextPrototypes),
"Image prototypes": p.getImageLength(p.ImagePrototypes),
}
if p.Back != nil {
lengths["Back text prototypes"] = p.getTextLength(p.Back.TextPrototypes)
lengths["Back image prototypes"] = p.getImageLength(p.Back.ImagePrototypes)
}

for s, l := range lengths {
if l > 0 && l != length {
return 0, fmt.Errorf("the number of images and texts prototypes must be the same than "+
"the reference '%d' in '%s', found %d != %d", length, s, l, length)
}
}

return length, nil
}

func (p *CounterPrototype) getImageLength(ts []ImagePrototype) int {
if len(ts) > 0 {
return len(ts[0].PathList)
}

return 0
}

func (p *CounterPrototype) getTextLength(ts []TextPrototype) int {
if len(ts) > 0 {
return len(ts[0].StringList)
}

return 0
}

/*
mergeFrontAndBack merges the images and texts from both counters. If the back prototype exists and
it has its own image or text prototypes, they are applied to the back counter, replacing or adding
to the existing images and texts.
*/
func mergeFrontAndBack(frontCounter *Counter, backPrototype *CounterPrototype, index int) (Counter, error) {
backCounter := backPrototype.Counter
if err := mergo.Merge(&backCounter, frontCounter); err != nil {
return Counter{}, err
func mergeFrontAndBack(frontCounter *Counter, backProto *CounterPrototype, index int) (c Counter, err error) {
var backCounter Counter
if err = reprint.FromTo(backProto.Counter, &backCounter); err != nil {
return
}

backCounter.PrettyName = frontCounter.PrettyName + "_back"
backCounter.Filename = strings.TrimRight(frontCounter.Filename, path.Ext(frontCounter.Filename)) + "_back.png"
backCounter.Filename = strings.TrimSuffix(frontCounter.Filename, path.Ext(frontCounter.Filename)) + "_back.png"

backCounter.Images = mergeImagesOrTexts(frontCounter.Images, backCounter.Images)
backCounter.Texts = mergeImagesOrTexts(frontCounter.Texts, backCounter.Texts)
images, err := cloneSlice(frontCounter.Images)
if err != nil {
return c, err
}
texts, err := cloneSlice(frontCounter.Texts)
if err != nil {
return c, err
}

if backPrototype.ImagePrototypes != nil {
for _, imagePrototype := range backPrototype.ImagePrototypes {
originalImage := Image{}
if err := deepcopy.FromTo(imagePrototype.Image, &originalImage); err != nil {
return Counter{}, err
}
originalImage.Path = imagePrototype.PathList[index]
backCounter.Images = mergeImagesOrTexts(images, backCounter.Images)
backCounter.Texts = mergeImagesOrTexts(texts, backCounter.Texts)

backCounter.Images = replaceOrAddPrototypes(backCounter.Images, originalImage)
for _, imageProto := range backProto.ImagePrototypes {
newImage := Image{}
if err = reprint.FromTo(imageProto.Image, &newImage); err != nil {
return
}
}

if backPrototype.TextPrototypes != nil {
for _, textPrototype := range backPrototype.TextPrototypes {
originalText := Text{}
if err := deepcopy.FromTo(textPrototype.Text, &originalText); err != nil {
return Counter{}, err
}
originalText.String = textPrototype.StringList[index]
newImage.Path = imageProto.PathList[index]
replaceOrAddPrototypes(backCounter.Images, newImage)
}

backCounter.Texts = replaceOrAddPrototypes(backCounter.Texts, originalText)
for _, textProto := range backProto.TextPrototypes {
newText := Text{}
if err = reprint.FromTo(textProto.Text, &newText); err != nil {
return
}

newText.String = textProto.StringList[index]
replaceOrAddPrototypes(backCounter.Texts, newText)
}

return backCounter, nil
Expand All @@ -161,95 +211,59 @@ mergeImagesOrTexts is used to merge texts or images slices from the front and ba
skips items sharing the same position unless the field BackPersistent is set to true, in which case
both items are kept with the front will be on top of the image
*/
func mergeImagesOrTexts[T SettingsGetter](frontPrototypes, backPrototypes []T) []T {
for _, frontPrototype := range frontPrototypes {
found := false
for _, backPrototype := range backPrototypes {
if frontPrototype.GetSettings().Position == backPrototype.GetSettings().Position {
found = true
if frontPrototype.GetSettings().BackPersistent {
backPrototypes = append(backPrototypes, frontPrototype)
func mergeImagesOrTexts[T SettingsGetter](fronts, backs []T) []T {
newAr := make([]T, 0, len(fronts))

if len(fronts) == 0 {
return backs
}

newAr = append(newAr, backs...)

frontLoop:
for _, front := range fronts {
for _, back := range backs {
if front.GetSettings().Position == back.GetSettings().Position {
if front.GetSettings().BackPersistent {
newAr = append(newAr, front)
}
break
continue frontLoop
}
}

if !found {
backPrototypes = append(backPrototypes, frontPrototype)
}
newAr = append(newAr, front)
}

if len(frontPrototypes) == 0 {
return backPrototypes
}

return backPrototypes
return newAr
}

// replaceOrAddPrototypes is used to override texts or images in the back of the counter. It checks
// if a prototype with the same position as newPrototype already exists and replaces it if it does
// or adds it if it doesn't
func replaceOrAddPrototypes[T SettingsGetter](originals []T, newPrototype T) []T {
func replaceOrAddPrototypes[T SettingsGetter](originals []T, newProto T) {
found := false
for j, original := range originals {
if original.GetSettings().Position == newPrototype.GetSettings().Position {
originals[j] = newPrototype
if original.GetSettings().Position == newProto.GetSettings().Position {
originals[j] = newProto
found = true
break
}
}
if !found {
originals = append(originals, newPrototype)
originals = append(originals, newProto)
}

return originals
}

// isLengthConsistent checks if the lengths of text and image prototypes are consistent.
func (p *CounterPrototype) isLengthConsistent() (int, error) {
// find a reference length
length := p.getTextLength(p.TextPrototypes)

// if no text prototypes found to use for reference, try with image prototypes
if length == 0 {
length = p.getImageLength(p.ImagePrototypes)
}

if length == 0 {
return 0, errors.New("no prototypes found in the counter template")
}

lengths := map[string]int{
"Text prototypes": p.getTextLength(p.TextPrototypes),
"Image prototypes": p.getImageLength(p.ImagePrototypes),
}
if p.Back != nil {
lengths["Back text prototypes"] = p.getTextLength(p.Back.TextPrototypes)
lengths["Back image prototypes"] = p.getImageLength(p.Back.ImagePrototypes)
}
func cloneSlice[T any](ts []T) (res []T, err error) {
res = make([]T, 0, len(ts))

for s, l := range lengths {
if l > 0 && l != length {
return 0, fmt.Errorf("the number of images and texts prototypes must be the same than "+
"the reference '%d' in '%s', found %d != %d", length, s, l, length)
for i := range ts {
var newT T
if err = reprint.FromTo(ts[i], &newT); err != nil {
return nil, fmt.Errorf("error cloning slice %w", err)
}
res = append(res, newT)
}

return length, nil
}

func (p *CounterPrototype) getImageLength(ts []ImagePrototype) int {
if len(ts) > 0 {
return len(ts[0].PathList)
}

return 0
}

func (p *CounterPrototype) getTextLength(ts []TextPrototype) int {
if len(ts) > 0 {
return len(ts[0].StringList)
}

return 0
return
}
Loading

0 comments on commit be111da

Please sign in to comment.