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
|
||||
InitialCamPos []float64
|
||||
DelayedStart bool // if true game, we wait. like pause but program induced
|
||||
Theme string
|
||||
ThemeManager ThemeManager
|
||||
|
||||
// for internal profiling
|
||||
ProfileFile string
|
||||
@@ -50,6 +52,7 @@ const (
|
||||
DEFAULT_CELLSIZE = 4
|
||||
DEFAULT_ZOOMFACTOR = 150
|
||||
DEFAULT_GEOM = "640x384"
|
||||
DEFAULT_THEME = "light" // inverse => "dark"
|
||||
)
|
||||
|
||||
const KEYBINDINGS string = `
|
||||
@@ -224,7 +227,11 @@ func ParseCommandline() (*Config, error) {
|
||||
pflag.BoolVarP(&config.Debug, "debug", "d", false, "show debug info")
|
||||
pflag.BoolVarP(&config.ShowGrid, "show-grid", "g", false, "draw grid lines")
|
||||
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.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.Wrap, "wrap-around", "w", false, "wrap around grid mode")
|
||||
pflag.BoolVarP(&config.UseShader, "use-shader", "k", false, "use shader for cell rendering")
|
||||
@@ -253,6 +260,12 @@ func ParseCommandline() (*Config, error) {
|
||||
|
||||
config.SetupCamera()
|
||||
|
||||
if config.Theme == "light" && config.Invert {
|
||||
config.Theme = "dark"
|
||||
}
|
||||
|
||||
config.ThemeManager = NewThemeManager(config.Theme, config.Cellsize)
|
||||
|
||||
//repr.Println(config)
|
||||
return &config, nil
|
||||
}
|
||||
@@ -267,9 +280,23 @@ func (config *Config) ToggleDebugging() {
|
||||
|
||||
func (config *Config) ToggleInvert() {
|
||||
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
|
||||
}
|
||||
|
||||
func (config *Config) SwitchTheme(theme string) {
|
||||
config.ThemeManager.SetCurrentTheme(theme)
|
||||
}
|
||||
|
||||
func (config *Config) ToggleGridlines() {
|
||||
config.ShowGrid = !config.ShowGrid
|
||||
config.RestartCache = true
|
||||
|
||||
@@ -10,3 +10,10 @@ func Contains[E comparable](s []E, v E) bool {
|
||||
|
||||
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
|
||||
Index int // points to current grid
|
||||
Generations int64 // Stats
|
||||
Black, White, Grey, Old color.RGBA
|
||||
AgeColor1, AgeColor2, AgeColor3, AgeColor4 color.RGBA
|
||||
TicksElapsed int // tick counter for game speed
|
||||
Tiles Images // pre-computed tiles for dead and alife cells
|
||||
Camera Camera // for zoom+move
|
||||
World, Cache *ebiten.Image // actual image we render to
|
||||
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
|
||||
RunOneStep bool // mutable flags from config
|
||||
TPG int // current game speed (ticks per game)
|
||||
Theme Theme
|
||||
}
|
||||
|
||||
func NewPlayScene(game *Game, config *Config) Scene {
|
||||
@@ -417,7 +415,7 @@ func (scene *ScenePlay) Update() error {
|
||||
|
||||
if scene.Config.RestartCache {
|
||||
scene.Config.RestartCache = false
|
||||
scene.InitTiles()
|
||||
scene.Theme = scene.Config.ThemeManager.GetCurrentTheme()
|
||||
scene.InitCache()
|
||||
return nil
|
||||
}
|
||||
@@ -473,7 +471,7 @@ func (scene *ScenePlay) Draw(screen *ebiten.Image) {
|
||||
scene.DrawEvolution(screen, x, y, op)
|
||||
} else {
|
||||
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] {
|
||||
case Alive:
|
||||
if age > 50 && scene.Config.ShowEvolution {
|
||||
scene.World.DrawImage(scene.Tiles.Old, op)
|
||||
scene.World.DrawImage(scene.Theme.Tile(ColOld), op)
|
||||
} else {
|
||||
scene.World.DrawImage(scene.Tiles.Black, op)
|
||||
scene.World.DrawImage(scene.Theme.Tile(ColLife), op)
|
||||
}
|
||||
case Dead:
|
||||
// only draw dead cells in case evolution trace is enabled
|
||||
if scene.History[y][x] > 1 && scene.Config.ShowEvolution {
|
||||
switch {
|
||||
case age < 10:
|
||||
scene.World.DrawImage(scene.Tiles.Age1, op)
|
||||
scene.World.DrawImage(scene.Theme.Tile(ColAge1), op)
|
||||
case age < 20:
|
||||
scene.World.DrawImage(scene.Tiles.Age2, op)
|
||||
scene.World.DrawImage(scene.Theme.Tile(ColAge2), op)
|
||||
case age < 30:
|
||||
scene.World.DrawImage(scene.Tiles.Age3, op)
|
||||
scene.World.DrawImage(scene.Theme.Tile(ColAge3), op)
|
||||
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,
|
||||
x+1, y+1,
|
||||
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.SetTarget(screen)
|
||||
|
||||
FontRenderer.Renderer.SetColor(scene.Black)
|
||||
FontRenderer.Renderer.SetColor(scene.Theme.Color(ColLife))
|
||||
FontRenderer.Renderer.Draw(debug, 31, 31)
|
||||
|
||||
FontRenderer.Renderer.SetColor(scene.Old)
|
||||
FontRenderer.Renderer.SetColor(scene.Theme.Color(ColOld))
|
||||
FontRenderer.Renderer.Draw(debug, 30, 30)
|
||||
|
||||
fmt.Println(debug)
|
||||
@@ -582,9 +580,9 @@ func (scene *ScenePlay) InitCache() {
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
|
||||
if scene.Config.ShowGrid {
|
||||
scene.Cache.Fill(scene.Grey)
|
||||
scene.Cache.Fill(scene.Theme.Color(ColGrid))
|
||||
} else {
|
||||
scene.Cache.Fill(scene.White)
|
||||
scene.Cache.Fill(scene.Theme.Color(ColDead))
|
||||
}
|
||||
|
||||
for y := 0; y < scene.Config.Height; y++ {
|
||||
@@ -595,7 +593,7 @@ func (scene *ScenePlay) InitCache() {
|
||||
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() {
|
||||
// setup the scene
|
||||
scene.Camera = Camera{
|
||||
@@ -685,7 +642,7 @@ func (scene *ScenePlay) Init() {
|
||||
scene.Config.Height*scene.Config.Cellsize,
|
||||
)
|
||||
|
||||
scene.InitTiles()
|
||||
scene.Theme = scene.Config.ThemeManager.GetCurrentTheme()
|
||||
scene.InitCache()
|
||||
|
||||
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