mirror of
https://codeberg.org/scip/golsky.git
synced 2025-12-16 20:20:57 +01:00
added theme system, makes it easier to add more color schemes
This commit is contained in:
@@ -33,6 +33,8 @@ type Config struct {
|
|||||||
ZoomOutFactor int
|
ZoomOutFactor int
|
||||||
InitialCamPos []float64
|
InitialCamPos []float64
|
||||||
DelayedStart bool // if true game, we wait. like pause but program induced
|
DelayedStart bool // if true game, we wait. like pause but program induced
|
||||||
|
Theme string
|
||||||
|
ThemeManager ThemeManager
|
||||||
|
|
||||||
// for internal profiling
|
// for internal profiling
|
||||||
ProfileFile string
|
ProfileFile string
|
||||||
@@ -50,6 +52,7 @@ const (
|
|||||||
DEFAULT_CELLSIZE = 4
|
DEFAULT_CELLSIZE = 4
|
||||||
DEFAULT_ZOOMFACTOR = 150
|
DEFAULT_ZOOMFACTOR = 150
|
||||||
DEFAULT_GEOM = "640x384"
|
DEFAULT_GEOM = "640x384"
|
||||||
|
DEFAULT_THEME = "light" // inverse => "dark"
|
||||||
)
|
)
|
||||||
|
|
||||||
const KEYBINDINGS string = `
|
const KEYBINDINGS string = `
|
||||||
@@ -224,7 +227,11 @@ func ParseCommandline() (*Config, error) {
|
|||||||
pflag.BoolVarP(&config.Debug, "debug", "d", false, "show debug info")
|
pflag.BoolVarP(&config.Debug, "debug", "d", false, "show debug info")
|
||||||
pflag.BoolVarP(&config.ShowGrid, "show-grid", "g", false, "draw grid lines")
|
pflag.BoolVarP(&config.ShowGrid, "show-grid", "g", false, "draw grid lines")
|
||||||
pflag.BoolVarP(&config.Empty, "empty", "e", false, "start with an empty screen")
|
pflag.BoolVarP(&config.Empty, "empty", "e", false, "start with an empty screen")
|
||||||
|
|
||||||
|
// style
|
||||||
pflag.BoolVarP(&config.Invert, "invert", "i", false, "invert colors (dead cell: black)")
|
pflag.BoolVarP(&config.Invert, "invert", "i", false, "invert colors (dead cell: black)")
|
||||||
|
pflag.StringVarP(&config.Theme, "theme", "T", "light", "color theme: dark, light (default: light)")
|
||||||
|
|
||||||
pflag.BoolVarP(&config.ShowEvolution, "show-evolution", "s", false, "show evolution traces")
|
pflag.BoolVarP(&config.ShowEvolution, "show-evolution", "s", false, "show evolution traces")
|
||||||
pflag.BoolVarP(&config.Wrap, "wrap-around", "w", false, "wrap around grid mode")
|
pflag.BoolVarP(&config.Wrap, "wrap-around", "w", false, "wrap around grid mode")
|
||||||
pflag.BoolVarP(&config.UseShader, "use-shader", "k", false, "use shader for cell rendering")
|
pflag.BoolVarP(&config.UseShader, "use-shader", "k", false, "use shader for cell rendering")
|
||||||
@@ -253,6 +260,12 @@ func ParseCommandline() (*Config, error) {
|
|||||||
|
|
||||||
config.SetupCamera()
|
config.SetupCamera()
|
||||||
|
|
||||||
|
if config.Theme == "light" && config.Invert {
|
||||||
|
config.Theme = "dark"
|
||||||
|
}
|
||||||
|
|
||||||
|
config.ThemeManager = NewThemeManager(config.Theme, config.Cellsize)
|
||||||
|
|
||||||
//repr.Println(config)
|
//repr.Println(config)
|
||||||
return &config, nil
|
return &config, nil
|
||||||
}
|
}
|
||||||
@@ -267,9 +280,23 @@ func (config *Config) ToggleDebugging() {
|
|||||||
|
|
||||||
func (config *Config) ToggleInvert() {
|
func (config *Config) ToggleInvert() {
|
||||||
config.Invert = !config.Invert
|
config.Invert = !config.Invert
|
||||||
|
|
||||||
|
switch config.ThemeManager.GetCurrentThemeName() {
|
||||||
|
case "dark":
|
||||||
|
config.ThemeManager.SetCurrentTheme("light")
|
||||||
|
case "light":
|
||||||
|
config.ThemeManager.SetCurrentTheme("dark")
|
||||||
|
default:
|
||||||
|
fmt.Println("unable to invert custom theme, only possible with dark and light themes.")
|
||||||
|
}
|
||||||
|
|
||||||
config.RestartCache = true
|
config.RestartCache = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (config *Config) SwitchTheme(theme string) {
|
||||||
|
config.ThemeManager.SetCurrentTheme(theme)
|
||||||
|
}
|
||||||
|
|
||||||
func (config *Config) ToggleGridlines() {
|
func (config *Config) ToggleGridlines() {
|
||||||
config.ShowGrid = !config.ShowGrid
|
config.ShowGrid = !config.ShowGrid
|
||||||
config.RestartCache = true
|
config.RestartCache = true
|
||||||
|
|||||||
@@ -10,3 +10,10 @@ func Contains[E comparable](s []E, v E) bool {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Exists[K comparable, V any](m map[K]V, v K) bool {
|
||||||
|
if _, ok := m[v]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
75
src/play.go
75
src/play.go
@@ -35,10 +35,7 @@ type ScenePlay struct {
|
|||||||
History [][]int64 // holds state of past dead cells for evolution traces
|
History [][]int64 // holds state of past dead cells for evolution traces
|
||||||
Index int // points to current grid
|
Index int // points to current grid
|
||||||
Generations int64 // Stats
|
Generations int64 // Stats
|
||||||
Black, White, Grey, Old color.RGBA
|
|
||||||
AgeColor1, AgeColor2, AgeColor3, AgeColor4 color.RGBA
|
|
||||||
TicksElapsed int // tick counter for game speed
|
TicksElapsed int // tick counter for game speed
|
||||||
Tiles Images // pre-computed tiles for dead and alife cells
|
|
||||||
Camera Camera // for zoom+move
|
Camera Camera // for zoom+move
|
||||||
World, Cache *ebiten.Image // actual image we render to
|
World, Cache *ebiten.Image // actual image we render to
|
||||||
WheelTurned bool // when user turns wheel multiple times, zoom faster
|
WheelTurned bool // when user turns wheel multiple times, zoom faster
|
||||||
@@ -49,6 +46,7 @@ type ScenePlay struct {
|
|||||||
Mark, Point image.Point // area to marks+save
|
Mark, Point image.Point // area to marks+save
|
||||||
RunOneStep bool // mutable flags from config
|
RunOneStep bool // mutable flags from config
|
||||||
TPG int // current game speed (ticks per game)
|
TPG int // current game speed (ticks per game)
|
||||||
|
Theme Theme
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPlayScene(game *Game, config *Config) Scene {
|
func NewPlayScene(game *Game, config *Config) Scene {
|
||||||
@@ -417,7 +415,7 @@ func (scene *ScenePlay) Update() error {
|
|||||||
|
|
||||||
if scene.Config.RestartCache {
|
if scene.Config.RestartCache {
|
||||||
scene.Config.RestartCache = false
|
scene.Config.RestartCache = false
|
||||||
scene.InitTiles()
|
scene.Theme = scene.Config.ThemeManager.GetCurrentTheme()
|
||||||
scene.InitCache()
|
scene.InitCache()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -473,7 +471,7 @@ func (scene *ScenePlay) Draw(screen *ebiten.Image) {
|
|||||||
scene.DrawEvolution(screen, x, y, op)
|
scene.DrawEvolution(screen, x, y, op)
|
||||||
} else {
|
} else {
|
||||||
if scene.Grids[scene.Index].Data[y][x] {
|
if scene.Grids[scene.Index].Data[y][x] {
|
||||||
scene.World.DrawImage(scene.Tiles.Black, op)
|
scene.World.DrawImage(scene.Theme.Tile(ColLife), op)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -497,22 +495,22 @@ func (scene *ScenePlay) DrawEvolution(screen *ebiten.Image, x, y int, op *ebiten
|
|||||||
switch scene.Grids[scene.Index].Data[y][x] {
|
switch scene.Grids[scene.Index].Data[y][x] {
|
||||||
case Alive:
|
case Alive:
|
||||||
if age > 50 && scene.Config.ShowEvolution {
|
if age > 50 && scene.Config.ShowEvolution {
|
||||||
scene.World.DrawImage(scene.Tiles.Old, op)
|
scene.World.DrawImage(scene.Theme.Tile(ColOld), op)
|
||||||
} else {
|
} else {
|
||||||
scene.World.DrawImage(scene.Tiles.Black, op)
|
scene.World.DrawImage(scene.Theme.Tile(ColLife), op)
|
||||||
}
|
}
|
||||||
case Dead:
|
case Dead:
|
||||||
// only draw dead cells in case evolution trace is enabled
|
// only draw dead cells in case evolution trace is enabled
|
||||||
if scene.History[y][x] > 1 && scene.Config.ShowEvolution {
|
if scene.History[y][x] > 1 && scene.Config.ShowEvolution {
|
||||||
switch {
|
switch {
|
||||||
case age < 10:
|
case age < 10:
|
||||||
scene.World.DrawImage(scene.Tiles.Age1, op)
|
scene.World.DrawImage(scene.Theme.Tile(ColAge1), op)
|
||||||
case age < 20:
|
case age < 20:
|
||||||
scene.World.DrawImage(scene.Tiles.Age2, op)
|
scene.World.DrawImage(scene.Theme.Tile(ColAge2), op)
|
||||||
case age < 30:
|
case age < 30:
|
||||||
scene.World.DrawImage(scene.Tiles.Age3, op)
|
scene.World.DrawImage(scene.Theme.Tile(ColAge3), op)
|
||||||
default:
|
default:
|
||||||
scene.World.DrawImage(scene.Tiles.Age4, op)
|
scene.World.DrawImage(scene.Theme.Tile(ColAge4), op)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -529,7 +527,7 @@ func (scene *ScenePlay) DrawMark(screen *ebiten.Image) {
|
|||||||
scene.World,
|
scene.World,
|
||||||
x+1, y+1,
|
x+1, y+1,
|
||||||
w, h,
|
w, h,
|
||||||
1.0, scene.Old, false,
|
1.0, scene.Theme.Color(ColOld), false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -561,10 +559,10 @@ func (scene *ScenePlay) DrawDebug(screen *ebiten.Image) {
|
|||||||
FontRenderer.Renderer.SetSizePx(10 + int(scene.Game.Scale*10))
|
FontRenderer.Renderer.SetSizePx(10 + int(scene.Game.Scale*10))
|
||||||
FontRenderer.Renderer.SetTarget(screen)
|
FontRenderer.Renderer.SetTarget(screen)
|
||||||
|
|
||||||
FontRenderer.Renderer.SetColor(scene.Black)
|
FontRenderer.Renderer.SetColor(scene.Theme.Color(ColLife))
|
||||||
FontRenderer.Renderer.Draw(debug, 31, 31)
|
FontRenderer.Renderer.Draw(debug, 31, 31)
|
||||||
|
|
||||||
FontRenderer.Renderer.SetColor(scene.Old)
|
FontRenderer.Renderer.SetColor(scene.Theme.Color(ColOld))
|
||||||
FontRenderer.Renderer.Draw(debug, 30, 30)
|
FontRenderer.Renderer.Draw(debug, 30, 30)
|
||||||
|
|
||||||
fmt.Println(debug)
|
fmt.Println(debug)
|
||||||
@@ -582,9 +580,9 @@ func (scene *ScenePlay) InitCache() {
|
|||||||
op := &ebiten.DrawImageOptions{}
|
op := &ebiten.DrawImageOptions{}
|
||||||
|
|
||||||
if scene.Config.ShowGrid {
|
if scene.Config.ShowGrid {
|
||||||
scene.Cache.Fill(scene.Grey)
|
scene.Cache.Fill(scene.Theme.Color(ColGrid))
|
||||||
} else {
|
} else {
|
||||||
scene.Cache.Fill(scene.White)
|
scene.Cache.Fill(scene.Theme.Color(ColDead))
|
||||||
}
|
}
|
||||||
|
|
||||||
for y := 0; y < scene.Config.Height; y++ {
|
for y := 0; y < scene.Config.Height; y++ {
|
||||||
@@ -595,7 +593,7 @@ func (scene *ScenePlay) InitCache() {
|
|||||||
float64(y*scene.Config.Cellsize),
|
float64(y*scene.Config.Cellsize),
|
||||||
)
|
)
|
||||||
|
|
||||||
scene.Cache.DrawImage(scene.Tiles.White, op)
|
scene.Cache.DrawImage(scene.Theme.Tile(ColDead), op)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -619,47 +617,6 @@ func (scene *ScenePlay) InitGrid() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare tile images
|
|
||||||
func (scene *ScenePlay) InitTiles() {
|
|
||||||
scene.Grey = color.RGBA{128, 128, 128, 0xff}
|
|
||||||
scene.Old = color.RGBA{255, 30, 30, 0xff}
|
|
||||||
|
|
||||||
scene.Black = color.RGBA{0, 0, 0, 0xff}
|
|
||||||
scene.White = color.RGBA{200, 200, 200, 0xff}
|
|
||||||
scene.AgeColor1 = color.RGBA{255, 195, 97, 0xff} // FIXME: use slice!
|
|
||||||
scene.AgeColor2 = color.RGBA{255, 211, 140, 0xff}
|
|
||||||
scene.AgeColor3 = color.RGBA{255, 227, 181, 0xff}
|
|
||||||
scene.AgeColor4 = color.RGBA{255, 240, 224, 0xff}
|
|
||||||
|
|
||||||
if scene.Config.Invert {
|
|
||||||
scene.White = color.RGBA{0, 0, 0, 0xff}
|
|
||||||
scene.Black = color.RGBA{200, 200, 200, 0xff}
|
|
||||||
|
|
||||||
scene.AgeColor1 = color.RGBA{82, 38, 0, 0xff}
|
|
||||||
scene.AgeColor2 = color.RGBA{66, 35, 0, 0xff}
|
|
||||||
scene.AgeColor3 = color.RGBA{43, 27, 0, 0xff}
|
|
||||||
scene.AgeColor4 = color.RGBA{25, 17, 0, 0xff}
|
|
||||||
}
|
|
||||||
|
|
||||||
scene.Tiles.Black = ebiten.NewImage(scene.Config.Cellsize, scene.Config.Cellsize)
|
|
||||||
scene.Tiles.White = ebiten.NewImage(scene.Config.Cellsize, scene.Config.Cellsize)
|
|
||||||
scene.Tiles.Old = ebiten.NewImage(scene.Config.Cellsize, scene.Config.Cellsize)
|
|
||||||
scene.Tiles.Age1 = ebiten.NewImage(scene.Config.Cellsize, scene.Config.Cellsize)
|
|
||||||
scene.Tiles.Age2 = ebiten.NewImage(scene.Config.Cellsize, scene.Config.Cellsize)
|
|
||||||
scene.Tiles.Age3 = ebiten.NewImage(scene.Config.Cellsize, scene.Config.Cellsize)
|
|
||||||
scene.Tiles.Age4 = ebiten.NewImage(scene.Config.Cellsize, scene.Config.Cellsize)
|
|
||||||
|
|
||||||
cellsize := scene.Config.ScreenWidth / scene.Config.Cellsize
|
|
||||||
|
|
||||||
FillCell(scene.Tiles.Black, cellsize, scene.Black)
|
|
||||||
FillCell(scene.Tiles.White, cellsize, scene.White)
|
|
||||||
FillCell(scene.Tiles.Old, cellsize, scene.Old)
|
|
||||||
FillCell(scene.Tiles.Age1, cellsize, scene.AgeColor1)
|
|
||||||
FillCell(scene.Tiles.Age2, cellsize, scene.AgeColor2)
|
|
||||||
FillCell(scene.Tiles.Age3, cellsize, scene.AgeColor3)
|
|
||||||
FillCell(scene.Tiles.Age4, cellsize, scene.AgeColor4)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (scene *ScenePlay) Init() {
|
func (scene *ScenePlay) Init() {
|
||||||
// setup the scene
|
// setup the scene
|
||||||
scene.Camera = Camera{
|
scene.Camera = Camera{
|
||||||
@@ -685,7 +642,7 @@ func (scene *ScenePlay) Init() {
|
|||||||
scene.Config.Height*scene.Config.Cellsize,
|
scene.Config.Height*scene.Config.Cellsize,
|
||||||
)
|
)
|
||||||
|
|
||||||
scene.InitTiles()
|
scene.Theme = scene.Config.ThemeManager.GetCurrentTheme()
|
||||||
scene.InitCache()
|
scene.InitCache()
|
||||||
|
|
||||||
if scene.Config.DelayedStart && !scene.Config.Empty {
|
if scene.Config.DelayedStart && !scene.Config.Empty {
|
||||||
|
|||||||
123
src/theme.go
Normal file
123
src/theme.go
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Color definitions. ColLife could be black or white depending on theme
|
||||||
|
const (
|
||||||
|
ColLife = iota
|
||||||
|
ColDead
|
||||||
|
ColOld
|
||||||
|
ColAge1
|
||||||
|
ColAge2
|
||||||
|
ColAge3
|
||||||
|
ColAge4
|
||||||
|
ColGrid
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Theme defines how the grid and the cells are colored. We define
|
||||||
|
// the colors and the actual tile images here, so that they are
|
||||||
|
// readily available from play.go
|
||||||
|
type Theme struct {
|
||||||
|
Tiles map[int]*ebiten.Image
|
||||||
|
Colors map[int]color.RGBA
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new theme
|
||||||
|
func NewTheme(life, dead, old, age1, age2, age3, age4, grid color.RGBA, cellsize int, name string) Theme {
|
||||||
|
theme := Theme{
|
||||||
|
Name: name,
|
||||||
|
Colors: map[int]color.RGBA{
|
||||||
|
ColLife: life,
|
||||||
|
ColDead: dead,
|
||||||
|
ColGrid: grid,
|
||||||
|
ColAge1: age1,
|
||||||
|
ColAge2: age2,
|
||||||
|
ColAge3: age3,
|
||||||
|
ColAge4: age4,
|
||||||
|
ColOld: old,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
theme.Tiles = make(map[int]*ebiten.Image, 6)
|
||||||
|
|
||||||
|
for cid, col := range theme.Colors {
|
||||||
|
theme.Tiles[cid] = ebiten.NewImage(cellsize, cellsize)
|
||||||
|
FillCell(theme.Tiles[cid], cellsize, col)
|
||||||
|
}
|
||||||
|
|
||||||
|
return theme
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the tile image for the requested color type. panic if
|
||||||
|
// unknown type is being used, which is ok, since the code is the only
|
||||||
|
// user anyway
|
||||||
|
func (theme *Theme) Tile(col int) *ebiten.Image {
|
||||||
|
return theme.Tiles[col]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (theme *Theme) Color(col int) color.RGBA {
|
||||||
|
return theme.Colors[col]
|
||||||
|
}
|
||||||
|
|
||||||
|
type ThemeManager struct {
|
||||||
|
Theme string
|
||||||
|
Themes map[string]Theme
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manager is used to easily switch themes from cli or menu
|
||||||
|
func NewThemeManager(initial string, cellsize int) ThemeManager {
|
||||||
|
light := NewTheme(
|
||||||
|
color.RGBA{0, 0, 0, 0xff}, // life
|
||||||
|
color.RGBA{200, 200, 200, 0xff}, // dead
|
||||||
|
color.RGBA{255, 30, 30, 0xff}, // old
|
||||||
|
color.RGBA{255, 195, 97, 0xff}, // age 1..4
|
||||||
|
color.RGBA{255, 211, 140, 0xff},
|
||||||
|
color.RGBA{255, 227, 181, 0xff},
|
||||||
|
color.RGBA{255, 240, 224, 0xff},
|
||||||
|
color.RGBA{128, 128, 128, 0xff}, // grid
|
||||||
|
cellsize,
|
||||||
|
"light",
|
||||||
|
)
|
||||||
|
|
||||||
|
dark := NewTheme(
|
||||||
|
color.RGBA{200, 200, 200, 0xff}, // life
|
||||||
|
color.RGBA{0, 0, 0, 0xff}, // dead
|
||||||
|
color.RGBA{255, 30, 30, 0xff}, // old
|
||||||
|
color.RGBA{82, 38, 0, 0xff}, // age 1..4
|
||||||
|
color.RGBA{66, 35, 0, 0xff},
|
||||||
|
color.RGBA{43, 27, 0, 0xff},
|
||||||
|
color.RGBA{25, 17, 0, 0xff},
|
||||||
|
color.RGBA{128, 128, 128, 0xff}, // grid
|
||||||
|
cellsize,
|
||||||
|
"dark",
|
||||||
|
)
|
||||||
|
|
||||||
|
manager := ThemeManager{
|
||||||
|
Themes: map[string]Theme{
|
||||||
|
"dark": dark,
|
||||||
|
"light": light,
|
||||||
|
},
|
||||||
|
Theme: initial,
|
||||||
|
}
|
||||||
|
|
||||||
|
return manager
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *ThemeManager) GetCurrentTheme() Theme {
|
||||||
|
return manager.Themes[manager.Theme]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *ThemeManager) GetCurrentThemeName() string {
|
||||||
|
return manager.Theme
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *ThemeManager) SetCurrentTheme(theme string) {
|
||||||
|
if Exists(manager.Themes, theme) {
|
||||||
|
manager.Theme = theme
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user