Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions game/game.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ import (

// Game container
type Game struct {
Model *app.App
App *app.App
Screen res.Screen
Mouse res.Mouse

canvasHelper *canvasHelper
}

// NewGame returns a new game
func NewGame(mod *app.App) Game {
func NewGame(app *app.App) Game {
return Game{
Model: mod,
App: app,
Screen: res.Screen{Image: nil, Width: 0, Height: 0},
canvasHelper: newCanvasHelper(),
}
Expand Down Expand Up @@ -48,7 +48,7 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
// Update the game.
func (g *Game) Update() error {
g.updateMouse()
g.Model.Update()
g.App.Update()
return nil
}

Expand All @@ -57,7 +57,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
g.Screen.Image = screen
g.Screen.Width = screen.Bounds().Dx()
g.Screen.Height = screen.Bounds().Dy()
g.Model.UpdateUI()
g.App.UpdateUI()
}

func (g *Game) updateMouse() {
Expand Down
130 changes: 66 additions & 64 deletions game/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ func run(g *Game, name string, mapLoc save.MapLocation, load save.LoadType, isEd

func runMenu(g *Game, tab int) {
ebiten.SetVsyncEnabled(true)
g.Model = app.New()
g.App = app.New()

ecs.AddResource(&g.Model.World, &g.Screen)
ecs.AddResource(&g.App.World, &g.Screen)

sprites := res.NewSprites(GameData, "data/gfx", "paper")
ecs.AddResource(&g.Model.World, &sprites)
ecs.AddResource(&g.App.World, &sprites)

achievements := achievements.New(&g.Model.World, GameData, "data/json/achievements.json", "user/achievements.json")
achievements := achievements.New(&g.App.World, GameData, "data/json/achievements.json", "user/achievements.json")

fonts := res.NewFonts(GameData)
ui := menu.NewUI(GameData, saveFolder, mapsFolder, tab, &sprites, &fonts, achievements,
Expand All @@ -66,135 +66,135 @@ func runMenu(g *Game, tab int) {
},
)

ecs.AddResource(&g.Model.World, &ui)
ecs.AddResource(&g.App.World, &ui)

g.Model.AddSystem(&menu.UpdateUI{})
g.Model.AddUISystem(&menu.DrawUI{})
g.App.AddSystem(&menu.UpdateUI{})
g.App.AddUISystem(&menu.DrawUI{})

g.Model.Initialize()
g.App.Initialize()
}

func runGame(g *Game, load save.LoadType, name string, mapLoc save.MapLocation, tileSet string, isEditor bool) error {
ebiten.SetVsyncEnabled(true)

g.Model = app.New()
g.App = app.New()

// Register components for deserialization,
// where it does not happen in systems already.
_ = ecs.ComponentID[comp.CardAnimation](&g.Model.World)
_ = ecs.ComponentID[comp.CardAnimation](&g.App.World)

// =========== Resources ===========

rules := res.NewRules(GameData, "data/json/rules.json")
ecs.AddResource(&g.Model.World, &rules)
ecs.AddResource(&g.App.World, &rules)

gameSpeed := res.GameSpeed{
MinSpeed: -2,
MaxSpeed: 3,
}
ecs.AddResource(&g.Model.World, &gameSpeed)
ecs.AddResource(&g.App.World, &gameSpeed)

gameTick := res.GameTick{}
ecs.AddResource(&g.Model.World, &gameTick)
ecs.AddResource(&g.App.World, &gameTick)

terrain := res.NewTerrain(rules.WorldSize, rules.WorldSize)
ecs.AddResource(&g.Model.World, &terrain)
ecs.AddResource(&g.App.World, &terrain)

terrainEntities := res.TerrainEntities{Grid: res.NewGrid[ecs.Entity](rules.WorldSize, rules.WorldSize)}
ecs.AddResource(&g.Model.World, &terrainEntities)
ecs.AddResource(&g.App.World, &terrainEntities)

landUse := res.NewLandUse(rules.WorldSize, rules.WorldSize)
ecs.AddResource(&g.Model.World, &landUse)
ecs.AddResource(&g.App.World, &landUse)

landUseEntities := res.LandUseEntities{Grid: res.NewGrid[ecs.Entity](rules.WorldSize, rules.WorldSize)}
ecs.AddResource(&g.Model.World, &landUseEntities)
ecs.AddResource(&g.App.World, &landUseEntities)

buildable := res.NewBuildable(rules.WorldSize, rules.WorldSize)
ecs.AddResource(&g.Model.World, &buildable)
ecs.AddResource(&g.App.World, &buildable)

selection := res.Selection{}
ecs.AddResource(&g.Model.World, &selection)
ecs.AddResource(&g.App.World, &selection)

bounds := res.WorldBounds{}
ecs.AddResource(&g.Model.World, &bounds)
ecs.AddResource(&g.App.World, &bounds)

editor := res.EditorMode{IsEditor: isEditor}
ecs.AddResource(&g.Model.World, &editor)
ecs.AddResource(&g.App.World, &editor)

saveTime := res.SaveTime{}
ecs.AddResource(&g.Model.World, &saveTime)
ecs.AddResource(&g.App.World, &saveTime)

randomTerrains := res.RandomTerrains{
TotalAvailable: rules.InitialRandomTerrains,
}
ecs.AddResource(&g.Model.World, &randomTerrains)
ecs.AddResource(&g.App.World, &randomTerrains)

update := res.UpdateInterval{
Interval: TPS,
Countdown: 60,
}
ecs.AddResource(&g.Model.World, &update)
ecs.AddResource(&g.App.World, &update)

sprites := res.NewSprites(GameData, "data/gfx", tileSet)
ecs.AddResource(&g.Model.World, &sprites)
ecs.AddResource(&g.App.World, &sprites)

view := res.NewView(sprites.TileWidth, sprites.TileHeight)
ecs.AddResource(&g.Model.World, &view)
ecs.AddResource(&g.App.World, &view)

production := res.NewProduction()
ecs.AddResource(&g.Model.World, &production)
ecs.AddResource(&g.App.World, &production)

stock := res.NewStock(rules.InitialResources)
ecs.AddResource(&g.Model.World, &stock)
ecs.AddResource(&g.App.World, &stock)

ecs.AddResource(&g.Model.World, &g.Screen)
ecs.AddResource(&g.Model.World, &g.Mouse)
ecs.AddResource(&g.App.World, &g.Screen)
ecs.AddResource(&g.App.World, &g.Mouse)

saveEvent := res.SaveEvent{}
ecs.AddResource(&g.Model.World, &saveEvent)
ecs.AddResource(&g.App.World, &saveEvent)

fonts := res.NewFonts(GameData)
ecs.AddResource(&g.Model.World, &fonts)
ecs.AddResource(&g.App.World, &fonts)

factory := res.NewEntityFactory(&g.Model.World)
ecs.AddResource(&g.Model.World, &factory)
factory := res.NewEntityFactory(&g.App.World)
ecs.AddResource(&g.App.World, &factory)

achievements := achievements.New(&g.Model.World, GameData, "data/json/achievements.json", "user/achievements.json")
ecs.AddResource(&g.Model.World, achievements)
achievements := achievements.New(&g.App.World, GameData, "data/json/achievements.json", "user/achievements.json")
ecs.AddResource(&g.App.World, achievements)

// =========== Systems ===========

if load == save.LoadTypeGame {
g.Model.AddSystem(&sys.InitTerrainLoaded{})
g.App.AddSystem(&sys.InitTerrainLoaded{})
} else if load == save.LoadTypeMap {
g.Model.AddSystem(&sys.InitTerrainMap{
g.App.AddSystem(&sys.InitTerrainMap{
FS: GameData,
MapFolder: mapsFolder,
Map: mapLoc,
})
} else {
g.Model.AddSystem(&sys.InitTerrain{})
g.App.AddSystem(&sys.InitTerrain{})
}
g.Model.AddSystem(&sys.InitUI{})

g.Model.AddSystem(&sys.Tick{})
g.Model.AddSystem(&sys.UpdateProduction{})
g.Model.AddSystem(&sys.UpdatePopulation{})
g.Model.AddSystem(&sys.DoProduction{})
g.Model.AddSystem(&sys.DoConsumption{})
g.Model.AddSystem(&sys.Haul{})
g.Model.AddSystem(&sys.UpdateStats{})
g.Model.AddSystem(&sys.RemoveMarkers{
g.App.AddSystem(&sys.InitUI{})

g.App.AddSystem(&sys.Tick{})
g.App.AddSystem(&sys.UpdateProduction{})
g.App.AddSystem(&sys.UpdatePopulation{})
g.App.AddSystem(&sys.DoProduction{})
g.App.AddSystem(&sys.DoConsumption{})
g.App.AddSystem(&sys.Haul{})
g.App.AddSystem(&sys.UpdateStats{})
g.App.AddSystem(&sys.RemoveMarkers{
MaxTime: TPS,
})

g.Model.AddSystem(&sys.Build{})
g.Model.AddSystem(&sys.AssignHaulers{})
g.Model.AddSystem(&sys.Achievements{
g.App.AddSystem(&sys.Build{})
g.App.AddSystem(&sys.AssignHaulers{})
g.App.AddSystem(&sys.Achievements{
PlayerFile: "user/achievements.json",
})

g.Model.AddSystem(&sys.PanAndZoom{
g.App.AddSystem(&sys.PanAndZoom{
PanButton: ebiten.MouseButton1,
ZoomInKey: '+',
ZoomOutKey: '-',
Expand All @@ -203,15 +203,15 @@ func runGame(g *Game, load save.LoadType, name string, mapLoc save.MapLocation,
MaxZoom: 4,
})

g.Model.AddSystem(&sys.UpdateUI{})
g.Model.AddSystem(&sys.Cheats{})
g.Model.AddSystem(&sys.SaveGame{
g.App.AddSystem(&sys.UpdateUI{})
g.App.AddSystem(&sys.Cheats{})
g.App.AddSystem(&sys.SaveGame{
SaveFolder: "save",
MapFolder: "maps",
Name: name,
MainMenuFunc: func() { runMenu(g, 0) },
})
g.Model.AddSystem(&sys.GameControls{
g.App.AddSystem(&sys.GameControls{
PauseKey: ebiten.KeySpace,
SlowerKey: '[',
FasterKey: ']',
Expand All @@ -220,22 +220,22 @@ func runGame(g *Game, load save.LoadType, name string, mapLoc save.MapLocation,

// =========== UI Systems ===========

g.Model.AddUISystem(&render.CenterView{})
g.Model.AddUISystem(&render.Terrain{})
g.Model.AddUISystem(&render.Markers{
g.App.AddUISystem(&render.CenterView{})
g.App.AddUISystem(&render.Terrain{})
g.App.AddUISystem(&render.Markers{
MinOffset: view.TileHeight * 2,
MaxOffset: view.TileHeight*2 + 30,
Duration: TPS,
})
g.Model.AddUISystem(&render.UI{})
g.Model.AddUISystem(&render.CardAnimation{
g.App.AddUISystem(&render.UI{})
g.App.AddUISystem(&render.CardAnimation{
MaxOffset: 200,
Duration: TPS / 4,
})

// =========== Load game ===========
if load == save.LoadTypeGame {
err := save.LoadWorld(&g.Model.World, saveFolder, name)
err := save.LoadWorld(&g.App.World, saveFolder, name)
if err != nil {
return err
}
Expand All @@ -245,9 +245,11 @@ func runGame(g *Game, load save.LoadType, name string, mapLoc save.MapLocation,
view.TileHeight = sprites.TileHeight
}

addRepl(g.App)

// =========== Run ===========

g.Model.Initialize()
g.App.Initialize()

return nil
}
32 changes: 32 additions & 0 deletions game/run_nowasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
package game

import (
"os"
"strings"

"github.com/hajimehoshi/ebiten/v2"
"github.com/mlange-42/ark-repl/repl"
"github.com/mlange-42/ark-tools/app"
"github.com/mlange-42/ark/ecs"
"github.com/mlange-42/tiny-world/game/res"
)

type canvasHelper struct{}
Expand All @@ -16,3 +23,28 @@ func (c *canvasHelper) isMouseInside(width, height int) bool {
x, y := ebiten.CursorPosition()
return x >= 0 && y >= 0 && x < width && y < height
}

func addRepl(app *app.App) {
startServer := len(os.Args) > 1 && os.Args[1] == "monitor"
if !startServer {
return
}

callbacks := repl.Callbacks{
Pause: func(out *strings.Builder) {
ecs.GetResource[res.GameSpeed](&app.World).Pause = true
},
Resume: func(out *strings.Builder) {
ecs.GetResource[res.GameSpeed](&app.World).Pause = false
},
Ticks: func() int {
return int(ecs.GetResource[res.GameTick](&app.World).Tick)
},
}

repl := repl.NewRepl(&app.World, callbacks)

app.AddUISystem(repl.System())

repl.StartServer(":9000")
}
4 changes: 4 additions & 0 deletions game/run_wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ package game

import (
"syscall/js"

"github.com/mlange-42/ark-tools/app"
)

type canvasHelper struct {
Expand Down Expand Up @@ -41,3 +43,5 @@ func (c *canvasHelper) onMouseLeave(this js.Value, args []js.Value) interface{}
c.mouseInside = false
return nil
}

func addRepl(app *app.App) {}
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/ebitenui/ebitenui v0.7.2
github.com/hajimehoshi/ebiten/v2 v2.9.1
github.com/mlange-42/ark v0.6.1
github.com/mlange-42/ark-repl v0.0.0-20251008145212-e1503a00bccd
github.com/mlange-42/ark-serde v0.3.0
github.com/mlange-42/ark-tools v0.2.1
github.com/spf13/cobra v1.10.1
Expand All @@ -16,15 +17,22 @@ require (
github.com/ebitengine/hideconsole v1.0.0 // indirect
github.com/ebitengine/purego v0.9.0 // indirect
github.com/frustra/bbcode v0.0.0-20201127003707-6ef347fbe1c8 // indirect
github.com/gdamore/encoding v1.0.0 // indirect
github.com/gdamore/tcell/v2 v2.7.4 // indirect
github.com/go-text/typesetting v0.3.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jezek/xgb v1.1.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mum4k/termdash v0.20.0 // indirect
github.com/nsf/termbox-go v1.1.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/spf13/pflag v1.0.9 // indirect
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
golang.org/x/image v0.32.0 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.36.0 // indirect
golang.org/x/term v0.17.0 // indirect
golang.org/x/text v0.30.0 // indirect
)
Loading