Skip to content

Commit

Permalink
Add production animation (#11)
Browse files Browse the repository at this point in the history
* prevent duplicate sprite names
* add production animation
  • Loading branch information
mlange-42 authored Feb 24, 2024
1 parent d76b314 commit d88a579
Show file tree
Hide file tree
Showing 14 changed files with 225 additions and 21 deletions.
Binary file added artwork/sprites/flat_48x24/food/food.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added artwork/sprites/flat_48x24/food/food.xcf
Binary file not shown.
Binary file added artwork/sprites/flat_48x24/stones/stones.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added artwork/sprites/flat_48x24/stones/stones.xcf
Binary file not shown.
Binary file added artwork/sprites/flat_48x24/wood/wood.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added artwork/sprites/flat_48x24/wood/wood.xcf
Binary file not shown.
53 changes: 37 additions & 16 deletions assets/sprites/flat_48x24.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,113 +3,134 @@
"SpriteHeight": 24,
"Sprites": [
{
"Name": "path",
"Name": "food",
"Index": 0,
"Height": 0,
"YOffset": 0,
"MultiTile": false
},
{
"Name": "path",
"Index": 1,
"Height": 0,
"YOffset": 0,
"MultiTile": true
},
{
"Name": "path_1",
"Index": 1,
"Index": 2,
"Height": 0,
"YOffset": 0,
"MultiTile": false
},
{
"Name": "path_2",
"Index": 2,
"Index": 3,
"Height": 0,
"YOffset": 0,
"MultiTile": false
},
{
"Name": "path_3",
"Index": 3,
"Index": 4,
"Height": 0,
"YOffset": 0,
"MultiTile": false
},
{
"Name": "path_4",
"Index": 4,
"Index": 5,
"Height": 0,
"YOffset": 0,
"MultiTile": false
},
{
"Name": "path_5",
"Index": 5,
"Index": 6,
"Height": 0,
"YOffset": 0,
"MultiTile": false
},
{
"Name": "path_6",
"Index": 6,
"Index": 7,
"Height": 0,
"YOffset": 0,
"MultiTile": false
},
{
"Name": "path_7",
"Index": 7,
"Index": 8,
"Height": 0,
"YOffset": 0,
"MultiTile": false
},
{
"Name": "path_8",
"Index": 8,
"Index": 9,
"Height": 0,
"YOffset": 0,
"MultiTile": false
},
{
"Name": "path_9",
"Index": 9,
"Index": 10,
"Height": 0,
"YOffset": 0,
"MultiTile": false
},
{
"Name": "path_10",
"Index": 10,
"Index": 11,
"Height": 0,
"YOffset": 0,
"MultiTile": false
},
{
"Name": "path_11",
"Index": 11,
"Index": 12,
"Height": 0,
"YOffset": 0,
"MultiTile": false
},
{
"Name": "path_12",
"Index": 12,
"Index": 13,
"Height": 0,
"YOffset": 0,
"MultiTile": false
},
{
"Name": "path_13",
"Index": 13,
"Index": 14,
"Height": 0,
"YOffset": 0,
"MultiTile": false
},
{
"Name": "path_14",
"Index": 14,
"Index": 15,
"Height": 0,
"YOffset": 0,
"MultiTile": false
},
{
"Name": "path_15",
"Index": 15,
"Index": 16,
"Height": 0,
"YOffset": 0,
"MultiTile": false
},
{
"Name": "stones",
"Index": 17,
"Height": 0,
"YOffset": 0,
"MultiTile": false
},
{
"Name": "wood",
"Index": 18,
"Height": 0,
"YOffset": 0,
"MultiTile": false
Expand Down
Binary file modified assets/sprites/flat_48x24.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 12 additions & 2 deletions cmd/sprites/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ var multiTileOrder = [16]int{
func main() {
dirs := extractFiles()

allNames := map[string]bool{}
for _, dir := range dirs {
processDirectory(dir)
processDirectory(dir, allNames)
}
}

Expand All @@ -53,7 +54,7 @@ type ImagePair struct {
Base image.Image
}

func processDirectory(info dirInfo) {
func processDirectory(info dirInfo, names map[string]bool) {
fmt.Printf("Processing %s (%d images)\n", info.Directory, len(info.Files))

if len(info.Files) == 0 {
Expand Down Expand Up @@ -115,6 +116,11 @@ func processDirectory(info dirInfo) {
if i > 0 {
name = fmt.Sprintf("%s_%d", name, i)
}
if _, ok := names[name]; ok {
log.Fatalf("duplicate sprite name: %s", name)
}
names[name] = true

subInfo := util.SpriteInfo{
Name: name,
Index: index,
Expand All @@ -131,6 +137,10 @@ func processDirectory(info dirInfo) {
if sprite.Bounds().Dx() != info.Width || sprite.Bounds().Dy() != info.Height {
log.Fatalf("unexpected tile size in %s: got %dx%d", file, sprite.Bounds().Dx(), sprite.Bounds().Dy())
}
if _, ok := names[baseName]; ok {
log.Fatalf("duplicate sprite name: %s", baseName)
}
names[baseName] = true

images = append(images, ImagePair{sprite, nil})
infos = append(infos, spriteInfo)
Expand Down
5 changes: 5 additions & 0 deletions game/comp/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@ type Consumption struct {
Amount int
Countdown int
}

type ProductionMarker struct {
StartTick int64
Resource resource.Resource
}
96 changes: 96 additions & 0 deletions game/render/markers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package render

import (
"image"

"github.com/hajimehoshi/ebiten/v2"
ares "github.com/mlange-42/arche-model/resource"
"github.com/mlange-42/arche/ecs"
"github.com/mlange-42/arche/generic"
"github.com/mlange-42/tiny-world/game/comp"
"github.com/mlange-42/tiny-world/game/res"
"github.com/mlange-42/tiny-world/game/resource"
)

// Markers is a system to render production markers.
type Markers struct {
MinOffset int
MaxOffset int
Duration int

time generic.Resource[ares.Tick]
screen generic.Resource[res.EbitenImage]
sprites generic.Resource[res.Sprites]
view generic.Resource[res.View]

filter generic.Filter2[comp.Tile, comp.ProductionMarker]

resources [resource.EndResources]int
}

// InitializeUI the system
func (s *Markers) InitializeUI(world *ecs.World) {
s.time = generic.NewResource[ares.Tick](world)
s.screen = generic.NewResource[res.EbitenImage](world)
s.sprites = generic.NewResource[res.Sprites](world)
s.view = generic.NewResource[res.View](world)

s.filter = *generic.NewFilter2[comp.Tile, comp.ProductionMarker]()

sprites := s.sprites.Get()
for i := resource.Resource(0); i < resource.EndResources; i++ {
s.resources[i] = sprites.GetIndex(resource.Properties[i].Name)
}
}

// UpdateUI the system
func (s *Markers) UpdateUI(world *ecs.World) {
tick := s.time.Get().Tick
sprites := s.sprites.Get()
view := s.view.Get()
canvas := s.screen.Get()
img := canvas.Image

off := view.Offset()
bounds := view.Bounds(canvas.Width, canvas.Height)

op := ebiten.DrawImageOptions{}
op.Blend = ebiten.BlendSourceOver
if view.Zoom < 1 {
op.Filter = ebiten.FilterLinear
}

halfWidth := view.TileWidth / 2

drawCursor := func(point *image.Point, cursor int) {
sp, info := sprites.Get(cursor)
h := sp.Bounds().Dy() - view.TileHeight

op.GeoM.Reset()
op.GeoM.Scale(view.Zoom, view.Zoom)
op.GeoM.Translate(
float64(point.X-halfWidth)*view.Zoom-float64(off.X),
float64(point.Y-h-info.YOffset)*view.Zoom-float64(off.Y),
)
img.DrawImage(sp, &op)
}

query := s.filter.Query(world)
for query.Next() {
tile, mark := query.Get()
point := view.TileToGlobal(tile.X, tile.Y)
if !point.In(bounds) {
continue
}
passed := tick - mark.StartTick
off := s.MinOffset + (s.MaxOffset-s.MinOffset)*int(passed)/s.Duration
point.Y -= off
drawCursor(&point, s.resources[mark.Resource])
}
}

// PostUpdateUI the system
func (s *Markers) PostUpdateUI(world *ecs.World) {}

// FinalizeUI the system
func (s *Markers) FinalizeUI(world *ecs.World) {}
24 changes: 21 additions & 3 deletions game/sys/do_production.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/mlange-42/arche/generic"
"github.com/mlange-42/tiny-world/game/comp"
"github.com/mlange-42/tiny-world/game/res"
"github.com/mlange-42/tiny-world/game/resource"
)

// DoProduction system.
Expand All @@ -14,7 +15,9 @@ type DoProduction struct {
update generic.Resource[res.UpdateInterval]
stock generic.Resource[res.Stock]

filter generic.Filter2[comp.UpdateTick, comp.Production]
filter generic.Filter3[comp.Tile, comp.UpdateTick, comp.Production]
markerBuilder generic.Map2[comp.Tile, comp.ProductionMarker]
toCreate []markerEntry
}

// Initialize the system
Expand All @@ -23,7 +26,8 @@ func (s *DoProduction) Initialize(world *ecs.World) {
s.update = generic.NewResource[res.UpdateInterval](world)
s.stock = generic.NewResource[res.Stock](world)

s.filter = *generic.NewFilter2[comp.UpdateTick, comp.Production]()
s.filter = *generic.NewFilter3[comp.Tile, comp.UpdateTick, comp.Production]()
s.markerBuilder = generic.NewMap2[comp.Tile, comp.ProductionMarker](world)
}

// Update the system
Expand All @@ -35,7 +39,7 @@ func (s *DoProduction) Update(world *ecs.World) {

query := s.filter.Query(world)
for query.Next() {
up, pr := query.Get()
tile, up, pr := query.Get()

if up.Tick != tickMod {
continue
Expand All @@ -44,9 +48,23 @@ func (s *DoProduction) Update(world *ecs.World) {
if pr.Countdown < 0 {
pr.Countdown += update.Countdown
stock.Res[pr.Type]++
s.toCreate = append(s.toCreate, markerEntry{Tile: *tile, Resource: pr.Type})
}
}

for _, entry := range s.toCreate {
s.markerBuilder.NewWith(
&entry.Tile,
&comp.ProductionMarker{StartTick: tick, Resource: entry.Resource},
)
}
s.toCreate = s.toCreate[:0]
}

// Finalize the system
func (s *DoProduction) Finalize(world *ecs.World) {}

type markerEntry struct {
Tile comp.Tile
Resource resource.Resource
}
Loading

0 comments on commit d88a579

Please sign in to comment.