Skip to content

Commit

Permalink
Scripting first steps
Browse files Browse the repository at this point in the history
  • Loading branch information
sayden committed Jan 2, 2025
1 parent 3203870 commit 6a76540
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 75 deletions.
72 changes: 58 additions & 14 deletions counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"dario.cat/mergo"
"github.com/fogleman/gg"
"github.com/pkg/errors"
"github.com/robertkrimen/otto"

Check failure on line 16 in counter.go

View workflow job for this annotation

GitHub Actions / build

missing go.sum entry for module providing package github.com/robertkrimen/otto (imported by github.com/sayden/counters); to add:
"github.com/thehivecorporation/log"
)

Expand Down Expand Up @@ -54,14 +55,15 @@ type Counters []Counter
type Metadata struct {
// PublicIcon in a FOW counter is the visible icon for the enemy. Imagine an icon for the back
// of a block in a Columbia game
CardImage *Image `json:"card_image,omitempty"`
Cost int `json:"cost,omitempty"`
PublicIcon *Image `json:"public_icon,omitempty"`
Side string `json:"side,omitempty"`
SkipCardGeneration bool `json:"skip_card_generation,omitempty"`
Title string `json:"title,omitempty"`
TitlePosition *int `json:"title_position,omitempty"`
External map[string]interface{} `json:"external,omitempty"`
CardImage *Image `json:"card_image,omitempty"`
Cost int `json:"cost,omitempty"`
PublicIcon *Image `json:"public_icon,omitempty"`
Side string `json:"side,omitempty"`
SkipCardGeneration bool `json:"skip_card_generation,omitempty"`
Title string `json:"title,omitempty"`
TitlePosition *int `json:"title_position,omitempty"`
External map[string]any `json:"external,omitempty"`
Scripts []string `json:"scripts,omitempty"`
}

type ImageExtraData struct {
Expand All @@ -88,6 +90,10 @@ func (c *Counter) GetTextInPosition(i int) string {
// filenumber: CounterTemplate.PositionNumberForFilename. So it will always be fixed number
// position: The position of the text in the counter (0-16)
// suffix: A suffix on the file. Constant
//
// Result:
//
// [sidename_][[Metadata.TitlePosition][_position text][_Metadata.Side][_Metadata.Title]][_PrototypeName][_filenumber][_suffix].png
func (c *Counter) GenerateCounterFilename(sideName string, position int, filenamesInUse *sync.Map) {
if c.Filename != "" {
return
Expand All @@ -102,30 +108,33 @@ func (c *Counter) GenerateCounterFilename(sideName string, position int, filenam
name = c.GetTextInPosition(*c.Metadata.TitlePosition)
}
if name != "" {
b.WriteString(name + " ")
b.WriteString("_")
b.WriteString(name)
}
// This way, the positional based name will always be the first part of the filename
// while the manual title will come later. This is useful when using prototypes so that
// counters with the same positional name are close together in the destination folder
name = ""

if c.Metadata.Side != "" {
b.WriteString("_")
b.WriteString(c.Metadata.Side)
b.WriteString(" ")
}

if c.Metadata.Title != "" {
b.WriteString("_")
b.WriteString(c.Metadata.Title)
b.WriteString(" ")
}
}

if name != "" {
b.WriteString(name + " ")
b.WriteString("_")
b.WriteString(name)
}

if c.PrototypeName != "" {
b.WriteString(c.PrototypeName + " ")
b.WriteString("_")
b.WriteString(c.PrototypeName)
}

res := b.String()
Expand Down Expand Up @@ -155,6 +164,9 @@ func (c *Counter) GenerateCounterFilename(sideName string, position int, filenam
if res == "" {
res = fmt.Sprintf("%04d", filenumber)
}
res = strings.Trim(res, "_")
res = strings.TrimSpace(res)
res = strings.Trim(res, "_")
res = strings.TrimSpace(res)

filenamesInUse.Store(res, true)
Expand Down Expand Up @@ -286,7 +298,7 @@ func (c *Counter) mergeFrontAndBack() (*Counter, error) {

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

c.Back.PrettyName = c.PrettyName + "_back"
Expand All @@ -297,3 +309,35 @@ func (c *Counter) mergeFrontAndBack() (*Counter, error) {

return c.Back, nil
}

func (c *Counter) runCounterScript(script string) (*Counter, error) {
vm := otto.New()

byt, err := json.Marshal(c)
if err != nil {
return nil, fmt.Errorf("could not marshal counter to json: %w", err)
}

if err = vm.Set("counter", string(byt)); err != nil {
return nil, err
}

if _, err = vm.Run(script); err != nil {
return nil, fmt.Errorf("could not run script: %w", err)
}

val, err := vm.Get("output")
if err != nil {
return nil, fmt.Errorf("could not get output from script: %w", err)
}
if byt, err = val.MarshalJSON(); err != nil {
return nil, err
}

newCounter := &Counter{}
if err = json.Unmarshal(byt, newCounter); err != nil {
return nil, err
}

return newCounter, nil
}
14 changes: 10 additions & 4 deletions counter_prototype.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import (

type CounterPrototype struct {
Counter
ImagePrototypes []ImagePrototype `json:"image_prototypes,omitempty"`
TextPrototypes []TextPrototype `json:"text_prototypes,omitempty"`
Back *CounterPrototype `json:"back,omitempty"`
Metadata map[string]interface{} `json:"external,omitempty"`
ImagePrototypes []ImagePrototype `json:"image_prototypes,omitempty"`
TextPrototypes []TextPrototype `json:"text_prototypes,omitempty"`
Back *CounterPrototype `json:"back,omitempty"`
Metadata Metadata `json:"metadata,omitempty"`
}

type ImagePrototype struct {
Expand Down Expand Up @@ -48,6 +48,9 @@ func (p *CounterPrototype) ToCounters(filenamesInUse *sync.Map, sideName, protot
return nil, err
}
newCounter.PrototypeName = prototypeName
newCounter.Metadata = &Metadata{}
newCounter.Metadata.Scripts = make([]string, len(p.Metadata.Scripts))
copy(newCounter.Metadata.Scripts, p.Metadata.Scripts)

if err = p.applyPrototypes(&newCounter, i); err != nil {
return nil, err
Expand Down Expand Up @@ -170,6 +173,9 @@ func mergeFrontAndBack(frontCounter *Counter, backProto *CounterPrototype, index

backCounter.PrettyName = frontCounter.PrettyName + "_back"
backCounter.Filename = strings.TrimSuffix(frontCounter.Filename, path.Ext(frontCounter.Filename)) + "_back.png"
backCounter.Metadata = &Metadata{}
backCounter.Metadata.Scripts = make([]string, len(frontCounter.Metadata.Scripts))
copy(backCounter.Metadata.Scripts, frontCounter.Metadata.Scripts)

images, err := cloneSlice(frontCounter.Images)
if err != nil {
Expand Down
135 changes: 84 additions & 51 deletions counter_template.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package counters

import (
"bytes"
"encoding/json"
"sort"
"sync"
Expand Down Expand Up @@ -30,7 +31,12 @@ type CounterTemplate struct {

Counters []Counter `json:"counters,omitempty"`
Prototypes map[string]CounterPrototype `json:"prototypes,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
Metadata CounterTemplateMetadata `json:"metadata,omitempty"`
}

type CounterTemplateMetadata struct {
External map[string]any `json:"external,omitempty"`
Scripts []string `json:"scripts,omitempty"`
}

type VassalCounterTemplateSettings struct {
Expand All @@ -43,6 +49,10 @@ type VassalCounterTemplateSettings struct {
// ParseCounterTemplate reads a JSON file and parses it into a CounterTemplate after applying it some default settings (if not
// present in the file)
func ParseCounterTemplate(byt []byte, filenamesInUse *sync.Map) (t *CounterTemplate, err error) {
if bytes.Contains(byt, []byte("\n")) {
byt = bytes.ReplaceAll(byt, []byte("\n"), []byte(""))
}

if err = ValidateSchemaBytes[CounterTemplate](byt); err != nil {
return nil, errors.Wrap(err, "JSON file is not valid")
}
Expand Down Expand Up @@ -143,77 +153,100 @@ func (t *CounterTemplate) ParsePrototype() (*CounterTemplate, error) {
return nil, errors.Wrap(err, "could not parse JSON file")
}

// TODO: Scripting
if newTemplate.Metadata.Scripts != nil {
for _, script := range newTemplate.Metadata.Scripts {
err := t.runTemplateScript(script)
if err != nil {
return nil, errors.Wrap(err, "error trying to run script")
}
}
}
for i, counter := range newTemplate.Counters {
if counter.Metadata != nil {
scripts := make([]string, len(counter.Metadata.Scripts))
copy(scripts, counter.Metadata.Scripts)
for _, script := range scripts {
newCounter, err := counter.runCounterScript(script)
if err != nil {
return nil, errors.Wrap(err, "error trying to run script")
}
newTemplate.Counters[i] = *newCounter
}
}
}

return newTemplate, nil
}

func (t *CounterTemplate) ExpandPrototypeCounterTemplate(filenamesInUse *sync.Map) (*CounterTemplate, error) {
if t.Counters != nil {
total := len(t.Counters)
for i := 0; i < total; i++ {
counter := t.Counters[i]
if counter.Filename == "" {
t.Counters[i].GenerateCounterFilename(t.Vassal.SideName, t.PositionNumberForFilename, filenamesInUse)
// t.Counters[i].Filename = counter.Filename
}

if counter.Back != nil {
backCounter, err := t.Counters[i].mergeFrontAndBack()
if err != nil {
return nil, err
}
total := len(t.Counters)
for i := 0; i < total; i++ {
counter := t.Counters[i]
if counter.Filename == "" {
t.Counters[i].GenerateCounterFilename(t.Vassal.SideName, t.PositionNumberForFilename, filenamesInUse)
// t.Counters[i].Filename = counter.Filename
}

t.Counters = append(t.Counters, *backCounter)
// t.Counters[i].Back = nil
if counter.Back != nil {
backCounter, err := t.Counters[i].mergeFrontAndBack()
if err != nil {
return nil, err
}

if t.Vassal.SideName != "" {
err := t.Counters[i].ToVassal(t.Vassal.SideName)
if err != nil {
log.Warn("could not create vassal piece from counter", err)
}
}
t.Counters = append(t.Counters, *backCounter)
// t.Counters[i].Back = nil
}

// JSON counters to Counters, check Prototype in CounterTemplate
if t.Prototypes != nil {
if t.Counters == nil {
t.Counters = make([]Counter, 0)
if t.Vassal.SideName != "" {
err := t.Counters[i].ToVassal(t.Vassal.SideName)
if err != nil {
log.Warn("could not create vassal piece from counter", err)
}
}
}

// sort prototypes by name, to ensure consistent output filenames this is a small
// inconvenience, because iterating over maps in Go returns keys in random order
names := make([]string, 0, len(t.Prototypes))
for name := range t.Prototypes {
names = append(names, name)
}
sort.Strings(names)
if t.Prototypes != nil {
if t.Counters == nil {
t.Counters = make([]Counter, 0)
}

for _, prototypeName := range names {
prototype := t.Prototypes[prototypeName]
// sort prototypes by name, to ensure consistent output filenames this is a small
// inconvenience, because iterating over maps in Go returns keys in random order
names := make([]string, 0, len(t.Prototypes))
for name := range t.Prototypes {
names = append(names, name)
}
sort.Strings(names)

cts, err := prototype.ToCounters(filenamesInUse, t.Vassal.SideName, prototypeName, t.PositionNumberForFilename)
if err != nil {
return nil, err
}
for _, prototypeName := range names {
prototype := t.Prototypes[prototypeName]

t.Counters = append(t.Counters, cts...)
cts, err := prototype.ToCounters(filenamesInUse, t.Vassal.SideName, prototypeName, t.PositionNumberForFilename)
if err != nil {
return nil, err
}

t.Prototypes = nil
t.Counters = append(t.Counters, cts...)
}

if t.Counters != nil {
total := len(t.Counters)
for i := 0; i < total; i++ {
counter := t.Counters[i]
if counter.Filename == "" {
counter.GenerateCounterFilename(t.Vassal.SideName, t.PositionNumberForFilename, filenamesInUse)
t.Counters[i].Filename = counter.Filename
}
t.Prototypes = nil
}

if t.Counters != nil {
total := len(t.Counters)
for i := 0; i < total; i++ {
counter := t.Counters[i]
if counter.Filename == "" {
counter.GenerateCounterFilename(t.Vassal.SideName, t.PositionNumberForFilename, filenamesInUse)
t.Counters[i].Filename = counter.Filename
}
}

}

return t, nil
}

func (t *CounterTemplate) runTemplateScript(script string) error {
return nil
}
Loading

0 comments on commit 6a76540

Please sign in to comment.