2025-11-13 21:30:44 +01:00
|
|
|
package cmd
|
2024-06-07 17:27:08 +02:00
|
|
|
|
|
|
|
|
import (
|
2024-06-08 16:19:54 +02:00
|
|
|
"fmt"
|
2024-06-07 17:27:08 +02:00
|
|
|
"image/color"
|
2024-06-08 16:19:54 +02:00
|
|
|
"log"
|
2024-06-07 17:27:08 +02:00
|
|
|
|
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
2024-06-08 16:19:54 +02:00
|
|
|
"github.com/hajimehoshi/ebiten/v2/vector"
|
2024-06-07 17:27:08 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 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 {
|
2024-06-15 18:21:54 +02:00
|
|
|
Tiles map[int]*ebiten.Image
|
|
|
|
|
GridTiles map[int]*ebiten.Image
|
|
|
|
|
Colors map[int]color.RGBA
|
|
|
|
|
Name string
|
|
|
|
|
ShowGrid bool
|
2024-06-07 17:27:08 +02:00
|
|
|
}
|
|
|
|
|
|
2024-06-09 18:00:06 +02:00
|
|
|
type ThemeDef struct {
|
|
|
|
|
life, dead, grid, old, age1, age2, age3, age4 string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var THEMES = map[string]ThemeDef{
|
|
|
|
|
"standard": {
|
|
|
|
|
life: "e15f0b",
|
|
|
|
|
dead: "5a5a5a",
|
2024-06-11 19:43:30 +02:00
|
|
|
old: "ff1e1e",
|
2024-06-09 18:33:09 +02:00
|
|
|
grid: "808080",
|
2024-10-20 12:39:37 +02:00
|
|
|
age3: "6c6059",
|
|
|
|
|
age2: "735f52",
|
|
|
|
|
age1: "7b5e4b",
|
|
|
|
|
age4: "635d59",
|
2024-06-09 18:00:06 +02:00
|
|
|
},
|
|
|
|
|
"dark": {
|
|
|
|
|
life: "c8c8c8",
|
|
|
|
|
dead: "000000",
|
2024-06-09 18:33:09 +02:00
|
|
|
old: "ff1e1e",
|
2024-06-11 19:43:30 +02:00
|
|
|
grid: "808080",
|
2024-06-09 18:33:09 +02:00
|
|
|
age1: "522600",
|
|
|
|
|
age2: "422300",
|
|
|
|
|
age3: "2b1b00",
|
|
|
|
|
age4: "191100",
|
2024-06-09 18:00:06 +02:00
|
|
|
},
|
|
|
|
|
"light": {
|
|
|
|
|
life: "000000",
|
|
|
|
|
dead: "c8c8c8",
|
2024-06-09 18:33:09 +02:00
|
|
|
old: "ff1e1e",
|
2024-06-11 19:43:30 +02:00
|
|
|
grid: "808080",
|
2024-06-09 18:33:09 +02:00
|
|
|
age1: "ffc361",
|
|
|
|
|
age2: "ffd38c",
|
|
|
|
|
age3: "ffe3b5",
|
|
|
|
|
age4: "fff0e0",
|
2024-06-09 18:00:06 +02:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-07 17:27:08 +02:00
|
|
|
// create a new theme
|
2024-06-09 18:00:06 +02:00
|
|
|
func NewTheme(def ThemeDef, cellsize int, name string) Theme {
|
2024-06-07 17:27:08 +02:00
|
|
|
theme := Theme{
|
|
|
|
|
Name: name,
|
|
|
|
|
Colors: map[int]color.RGBA{
|
2024-06-09 18:00:06 +02:00
|
|
|
ColLife: HexColor2RGBA(def.life),
|
|
|
|
|
ColDead: HexColor2RGBA(def.dead),
|
|
|
|
|
ColGrid: HexColor2RGBA(def.grid),
|
|
|
|
|
ColAge1: HexColor2RGBA(def.age1),
|
|
|
|
|
ColAge2: HexColor2RGBA(def.age2),
|
|
|
|
|
ColAge3: HexColor2RGBA(def.age3),
|
|
|
|
|
ColAge4: HexColor2RGBA(def.age4),
|
|
|
|
|
ColOld: HexColor2RGBA(def.old),
|
2024-06-07 17:27:08 +02:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
theme.Tiles = make(map[int]*ebiten.Image, 6)
|
2024-06-15 18:21:54 +02:00
|
|
|
theme.GridTiles = make(map[int]*ebiten.Image, 6)
|
2024-06-07 17:27:08 +02:00
|
|
|
|
|
|
|
|
for cid, col := range theme.Colors {
|
|
|
|
|
theme.Tiles[cid] = ebiten.NewImage(cellsize, cellsize)
|
2024-06-15 18:21:54 +02:00
|
|
|
FillCell(theme.Tiles[cid], cellsize, col, 0)
|
|
|
|
|
|
|
|
|
|
theme.GridTiles[cid] = ebiten.NewImage(cellsize, cellsize)
|
|
|
|
|
FillCell(theme.GridTiles[cid], cellsize, col, 1)
|
2024-06-07 17:27:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 {
|
2024-06-15 18:21:54 +02:00
|
|
|
if theme.ShowGrid {
|
|
|
|
|
return theme.GridTiles[col]
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-07 17:27:08 +02:00
|
|
|
return theme.Tiles[col]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (theme *Theme) Color(col int) color.RGBA {
|
|
|
|
|
return theme.Colors[col]
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-15 18:21:54 +02:00
|
|
|
func (theme *Theme) SetGrid(showgrid bool) {
|
|
|
|
|
theme.ShowGrid = showgrid
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-07 17:27:08 +02:00
|
|
|
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 {
|
|
|
|
|
manager := ThemeManager{
|
|
|
|
|
Theme: initial,
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 18:00:06 +02:00
|
|
|
manager.Themes = make(map[string]Theme, len(THEMES))
|
|
|
|
|
|
|
|
|
|
for name, def := range THEMES {
|
|
|
|
|
manager.Themes[name] = NewTheme(def, cellsize, name)
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-07 17:27:08 +02:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-08 16:19:54 +02:00
|
|
|
|
2024-06-09 18:41:27 +02:00
|
|
|
// Fill a cell with the given color.
|
|
|
|
|
//
|
|
|
|
|
// We do not draw the cell at 0,0 of it's position but at 1,1. This
|
|
|
|
|
// creates a top and lef transparent. By using a different background
|
|
|
|
|
// for the whole grid we can then decide wether to show grid lines or
|
|
|
|
|
// not.
|
|
|
|
|
//
|
|
|
|
|
// If no gridlines are selected the background will just be filled
|
|
|
|
|
// with the DEAD color. However, IF we are to show the gridlines, we
|
|
|
|
|
// fill it with a lighter color. The transparent edges of all tiles
|
|
|
|
|
// then create the grid.
|
|
|
|
|
//
|
|
|
|
|
// So we don't draw a grid, we just left a grid behind, which saves us
|
|
|
|
|
// from a lot of drawing operations.
|
2024-06-15 18:21:54 +02:00
|
|
|
func FillCell(tile *ebiten.Image, cellsize int, col color.RGBA, x int) {
|
2024-06-08 16:19:54 +02:00
|
|
|
vector.DrawFilledRect(
|
|
|
|
|
tile,
|
2024-06-15 18:21:54 +02:00
|
|
|
float32(x),
|
|
|
|
|
float32(x),
|
2024-06-08 16:19:54 +02:00
|
|
|
float32(cellsize),
|
|
|
|
|
float32(cellsize),
|
|
|
|
|
col, false,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func HexColor2RGBA(hex string) color.RGBA {
|
|
|
|
|
var r, g, b uint8
|
|
|
|
|
|
|
|
|
|
_, err := fmt.Sscanf(hex, "%02x%02x%02x", &r, &g, &b)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("failed to parse hex color: %s", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return color.RGBA{r, g, b, 255}
|
|
|
|
|
}
|