mirror of
https://github.com/mvvasilev/last_light.git
synced 2025-05-12 15:19:53 +03:00
New drawing system, start of inventory menu
This commit is contained in:
parent
c0f80f0e0c
commit
099155c186
32 changed files with 890 additions and 278 deletions
16
game/game.go
16
game/game.go
|
@ -2,13 +2,15 @@ package game
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"mvvasilev/last_light/game/state"
|
"mvvasilev/last_light/game/state"
|
||||||
|
"mvvasilev/last_light/render"
|
||||||
|
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
"github.com/gdamore/tcell/v2/views"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Game struct {
|
type Game struct {
|
||||||
state state.GameState
|
state state.GameState
|
||||||
|
|
||||||
|
quitGame bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateGame() *Game {
|
func CreateGame() *Game {
|
||||||
|
@ -20,10 +22,18 @@ func CreateGame() *Game {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) Input(ev *tcell.EventKey) {
|
func (g *Game) Input(ev *tcell.EventKey) {
|
||||||
|
if ev.Key() == tcell.KeyCtrlC {
|
||||||
|
g.quitGame = true
|
||||||
|
}
|
||||||
|
|
||||||
g.state.OnInput(ev)
|
g.state.OnInput(ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) Tick(dt int64) bool {
|
func (g *Game) Tick(dt int64) bool {
|
||||||
|
if g.quitGame {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
s := g.state.OnTick(dt)
|
s := g.state.OnTick(dt)
|
||||||
|
|
||||||
switch s.(type) {
|
switch s.(type) {
|
||||||
|
@ -36,6 +46,6 @@ func (g *Game) Tick(dt int64) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) Draw(v views.View) {
|
func (g *Game) CollectDrawables() []render.Drawable {
|
||||||
g.state.OnDraw(v)
|
return g.state.CollectDrawables()
|
||||||
}
|
}
|
||||||
|
|
55
game/game_context.go
Normal file
55
game/game_context.go
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"mvvasilev/last_light/render"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const TICK_RATE int64 = 50 // tick every 50ms ( 20 ticks per second )
|
||||||
|
|
||||||
|
type GameContext struct {
|
||||||
|
renderContext *render.RenderContext
|
||||||
|
|
||||||
|
game *Game
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateGameContext() *GameContext {
|
||||||
|
gc := new(GameContext)
|
||||||
|
|
||||||
|
rc, err := render.CreateRenderContext()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("%~v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gc.renderContext = rc
|
||||||
|
gc.game = CreateGame()
|
||||||
|
|
||||||
|
return gc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gc *GameContext) Run() {
|
||||||
|
lastTick := time.Now()
|
||||||
|
|
||||||
|
for {
|
||||||
|
deltaTime := 1 + time.Since(lastTick).Microseconds()
|
||||||
|
lastTick = time.Now()
|
||||||
|
|
||||||
|
for _, e := range gc.renderContext.CollectInputEvents() {
|
||||||
|
gc.game.Input(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
stop := !gc.game.Tick(deltaTime)
|
||||||
|
|
||||||
|
if stop {
|
||||||
|
gc.renderContext.Stop()
|
||||||
|
os.Exit(0)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
drawables := gc.game.CollectDrawables()
|
||||||
|
gc.renderContext.Draw(deltaTime, drawables)
|
||||||
|
}
|
||||||
|
}
|
1
game/model/dungeon.go
Normal file
1
game/model/dungeon.go
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package model
|
47
game/model/empty_dungeon_level.go
Normal file
47
game/model/empty_dungeon_level.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import "mvvasilev/last_light/util"
|
||||||
|
|
||||||
|
type EmptyDungeonLevel struct {
|
||||||
|
tiles [][]Tile
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateEmptyDungeonLevel(width, height int) *EmptyDungeonLevel {
|
||||||
|
m := new(EmptyDungeonLevel)
|
||||||
|
|
||||||
|
m.tiles = make([][]Tile, height)
|
||||||
|
|
||||||
|
for h := range height {
|
||||||
|
m.tiles[h] = make([]Tile, width)
|
||||||
|
}
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (edl *EmptyDungeonLevel) Size() util.Size {
|
||||||
|
return util.SizeOf(len(edl.tiles[0]), len(edl.tiles))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (edl *EmptyDungeonLevel) SetTileAt(x int, y int, t Tile) {
|
||||||
|
if len(edl.tiles) <= y || len(edl.tiles[0]) <= x {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
edl.tiles[y][x] = t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (edl *EmptyDungeonLevel) TileAt(x int, y int) Tile {
|
||||||
|
if y < 0 || y >= len(edl.tiles) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if x < 0 || x >= len(edl.tiles[y]) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return edl.tiles[y][x]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (edl *EmptyDungeonLevel) Tick() {
|
||||||
|
|
||||||
|
}
|
|
@ -4,22 +4,36 @@ import (
|
||||||
"mvvasilev/last_light/util"
|
"mvvasilev/last_light/util"
|
||||||
|
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
"github.com/gdamore/tcell/v2/views"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Direction int
|
type Direction int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Up Direction = iota
|
DirectionNone Direction = iota
|
||||||
Down
|
DirectionUp
|
||||||
Left
|
DirectionDown
|
||||||
Right
|
DirectionLeft
|
||||||
|
DirectionRight
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func MovementDirectionOffset(dir Direction) (int, int) {
|
||||||
|
switch dir {
|
||||||
|
case DirectionUp:
|
||||||
|
return 0, -1
|
||||||
|
case DirectionDown:
|
||||||
|
return 0, 1
|
||||||
|
case DirectionLeft:
|
||||||
|
return -1, 0
|
||||||
|
case DirectionRight:
|
||||||
|
return 1, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
type Entity interface {
|
type Entity interface {
|
||||||
UniqueId() uuid.UUID
|
UniqueId() uuid.UUID
|
||||||
Draw(v views.View)
|
|
||||||
Input(e *tcell.EventKey)
|
Input(e *tcell.EventKey)
|
||||||
Tick(dt int64)
|
Tick(dt int64)
|
||||||
}
|
}
|
||||||
|
|
75
game/model/flat_ground_dungeon_level.go
Normal file
75
game/model/flat_ground_dungeon_level.go
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"mvvasilev/last_light/util"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FlatGroundDungeonLevel struct {
|
||||||
|
tiles [][]Tile
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateFlatGroundDungeonLevel(width, height int) *FlatGroundDungeonLevel {
|
||||||
|
level := new(FlatGroundDungeonLevel)
|
||||||
|
|
||||||
|
level.tiles = make([][]Tile, height)
|
||||||
|
|
||||||
|
for h := range height {
|
||||||
|
level.tiles[h] = make([]Tile, width)
|
||||||
|
|
||||||
|
for w := range width {
|
||||||
|
if w == 0 || h == 0 || w >= width-1 || h >= height-1 {
|
||||||
|
level.tiles[h][w] = CreateStaticTile(w, h, Rock())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
level.tiles[h][w] = genRandomGroundTile(w, h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return level
|
||||||
|
}
|
||||||
|
|
||||||
|
func genRandomGroundTile(width, height int) Tile {
|
||||||
|
switch rand.Intn(2) {
|
||||||
|
case 0:
|
||||||
|
return CreateStaticTile(width, height, Ground())
|
||||||
|
case 1:
|
||||||
|
return CreateStaticTile(width, height, Grass())
|
||||||
|
default:
|
||||||
|
return CreateStaticTile(width, height, Ground())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (edl *FlatGroundDungeonLevel) Size() util.Size {
|
||||||
|
return util.SizeOfInt(len(edl.tiles[0]), len(edl.tiles))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (edl *FlatGroundDungeonLevel) SetTileAt(x int, y int, t Tile) {
|
||||||
|
if len(edl.tiles) <= y || len(edl.tiles[0]) <= x {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
edl.tiles[y][x] = t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (edl *FlatGroundDungeonLevel) TileAt(x int, y int) Tile {
|
||||||
|
if y < 0 || y >= len(edl.tiles) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if x < 0 || x >= len(edl.tiles[y]) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return edl.tiles[y][x]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (edl *FlatGroundDungeonLevel) Input(e *tcell.EventKey) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (edl *FlatGroundDungeonLevel) Tick() {
|
||||||
|
}
|
12
game/model/map.go
Normal file
12
game/model/map.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mvvasilev/last_light/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Map interface {
|
||||||
|
Size() util.Size
|
||||||
|
SetTileAt(x, y int, t Tile)
|
||||||
|
TileAt(x, y int) Tile
|
||||||
|
Tick()
|
||||||
|
}
|
61
game/model/multilevel_map.go
Normal file
61
game/model/multilevel_map.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import "mvvasilev/last_light/util"
|
||||||
|
|
||||||
|
type MultilevelMap struct {
|
||||||
|
layers []Map
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateMultilevelMap(maps ...Map) *MultilevelMap {
|
||||||
|
m := new(MultilevelMap)
|
||||||
|
|
||||||
|
m.layers = maps
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mm *MultilevelMap) Size() util.Size {
|
||||||
|
if len(mm.layers) == 0 {
|
||||||
|
return util.SizeOf(0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mm.layers[0].Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mm *MultilevelMap) SetTileAt(x, y int, t Tile) {
|
||||||
|
mm.layers[0].SetTileAt(x, y, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mm *MultilevelMap) UnsetTileAtHeight(x, y, height int) {
|
||||||
|
if len(mm.layers) < height {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mm.layers[height].SetTileAt(x, y, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mm *MultilevelMap) SetTileAtHeight(x, y, height int, t Tile) {
|
||||||
|
if len(mm.layers) < height {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mm.layers[height].SetTileAt(x, y, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mm *MultilevelMap) TileAt(x int, y int) Tile {
|
||||||
|
for i := len(mm.layers) - 1; i >= 0; i-- {
|
||||||
|
tile := mm.layers[i].TileAt(x, y)
|
||||||
|
|
||||||
|
if tile != nil {
|
||||||
|
return tile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mm *MultilevelMap) Tick() {
|
||||||
|
for _, l := range mm.layers {
|
||||||
|
l.Tick()
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,17 +4,15 @@ import (
|
||||||
"mvvasilev/last_light/util"
|
"mvvasilev/last_light/util"
|
||||||
|
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
"github.com/gdamore/tcell/v2/views"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Player struct {
|
type Player struct {
|
||||||
id uuid.UUID
|
id uuid.UUID
|
||||||
position util.Position
|
position util.Position
|
||||||
style tcell.Style
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreatePlayer(x, y uint16, style tcell.Style) *Player {
|
func CreatePlayer(x, y int) *Player {
|
||||||
p := new(Player)
|
p := new(Player)
|
||||||
|
|
||||||
p.id = uuid.New()
|
p.id = uuid.New()
|
||||||
|
@ -27,48 +25,23 @@ func (p *Player) UniqueId() uuid.UUID {
|
||||||
return p.id
|
return p.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Player) Move(dir Direction) {
|
func (p *Player) Position() util.Position {
|
||||||
x, y := p.position.XYUint16()
|
return p.position
|
||||||
|
|
||||||
switch dir {
|
|
||||||
case Up:
|
|
||||||
p.position = util.PositionAt(x, y-1)
|
|
||||||
case Down:
|
|
||||||
p.position = util.PositionAt(x, y+1)
|
|
||||||
case Left:
|
|
||||||
p.position = util.PositionAt(x-1, y)
|
|
||||||
case Right:
|
|
||||||
p.position = util.PositionAt(x+1, y)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Player) Draw(v views.View) {
|
func (p *Player) Move(dir Direction) {
|
||||||
x, y := p.position.XY()
|
p.position = p.Position().WithOffset(MovementDirectionOffset(dir))
|
||||||
v.SetContent(x, y, '@', nil, p.style)
|
}
|
||||||
|
|
||||||
|
func (p *Player) Presentation() rune {
|
||||||
|
return '@'
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) Passable() bool {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Player) Input(e *tcell.EventKey) {
|
func (p *Player) Input(e *tcell.EventKey) {
|
||||||
switch e.Key() {
|
|
||||||
case tcell.KeyUp:
|
|
||||||
p.Move(Up)
|
|
||||||
case tcell.KeyDown:
|
|
||||||
p.Move(Down)
|
|
||||||
case tcell.KeyLeft:
|
|
||||||
p.Move(Left)
|
|
||||||
case tcell.KeyRight:
|
|
||||||
p.Move(Right)
|
|
||||||
case tcell.KeyRune:
|
|
||||||
switch e.Rune() {
|
|
||||||
case 'w':
|
|
||||||
p.Move(Up)
|
|
||||||
case 'a':
|
|
||||||
p.Move(Left)
|
|
||||||
case 's':
|
|
||||||
p.Move(Down)
|
|
||||||
case 'd':
|
|
||||||
p.Move(Right)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Player) Tick(dt int64) {
|
func (p *Player) Tick(dt int64) {
|
||||||
|
|
86
game/model/tile.go
Normal file
86
game/model/tile.go
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import "mvvasilev/last_light/util"
|
||||||
|
|
||||||
|
type Material uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
MaterialGround Material = iota
|
||||||
|
MaterialRock
|
||||||
|
MaterialGrass
|
||||||
|
MaterialVoid
|
||||||
|
)
|
||||||
|
|
||||||
|
type TileType struct {
|
||||||
|
Material Material
|
||||||
|
Passable bool
|
||||||
|
Presentation rune
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ground() TileType {
|
||||||
|
return TileType{
|
||||||
|
Material: MaterialGround,
|
||||||
|
Passable: true,
|
||||||
|
Presentation: '.',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Rock() TileType {
|
||||||
|
return TileType{
|
||||||
|
Material: MaterialRock,
|
||||||
|
Passable: false,
|
||||||
|
Presentation: '█',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Grass() TileType {
|
||||||
|
return TileType{
|
||||||
|
Material: MaterialGrass,
|
||||||
|
Passable: true,
|
||||||
|
Presentation: ',',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Void() TileType {
|
||||||
|
return TileType{
|
||||||
|
Material: MaterialVoid,
|
||||||
|
Passable: false,
|
||||||
|
Presentation: ' ',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tile interface {
|
||||||
|
Position() util.Position
|
||||||
|
Presentation() rune
|
||||||
|
Passable() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type StaticTile struct {
|
||||||
|
position util.Position
|
||||||
|
t TileType
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateStaticTile(x, y int, t TileType) Tile {
|
||||||
|
st := new(StaticTile)
|
||||||
|
|
||||||
|
st.position = util.PositionAt(x, y)
|
||||||
|
st.t = t
|
||||||
|
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *StaticTile) Position() util.Position {
|
||||||
|
return st.position
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *StaticTile) Presentation() rune {
|
||||||
|
return st.t.Presentation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *StaticTile) Passable() bool {
|
||||||
|
return st.t.Passable
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *StaticTile) Type() TileType {
|
||||||
|
return st.t
|
||||||
|
}
|
|
@ -1,14 +1,15 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"mvvasilev/last_light/render"
|
||||||
|
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
"github.com/gdamore/tcell/v2/views"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type GameState interface {
|
type GameState interface {
|
||||||
OnInput(e *tcell.EventKey)
|
OnInput(e *tcell.EventKey)
|
||||||
OnTick(dt int64) GameState
|
OnTick(dt int64) GameState
|
||||||
OnDraw(c views.View)
|
CollectDrawables() []render.Drawable
|
||||||
}
|
}
|
||||||
|
|
||||||
type PausableState interface {
|
type PausableState interface {
|
51
game/state/inventory_screen_state.go
Normal file
51
game/state/inventory_screen_state.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mvvasilev/last_light/game/model"
|
||||||
|
"mvvasilev/last_light/render"
|
||||||
|
"mvvasilev/last_light/ui"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InventoryScreenState struct {
|
||||||
|
prevState GameState
|
||||||
|
exitMenu bool
|
||||||
|
|
||||||
|
inventoryMenu *ui.UIWindow
|
||||||
|
|
||||||
|
player *model.Player
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateInventoryScreenState(player *model.Player, prevState GameState) *InventoryScreenState {
|
||||||
|
iss := new(InventoryScreenState)
|
||||||
|
|
||||||
|
iss.prevState = prevState
|
||||||
|
iss.player = player
|
||||||
|
iss.exitMenu = false
|
||||||
|
|
||||||
|
iss.inventoryMenu = ui.CreateWindow(40, 0, 40, 24, "INVENTORY", tcell.StyleDefault)
|
||||||
|
|
||||||
|
return iss
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iss *InventoryScreenState) OnInput(e *tcell.EventKey) {
|
||||||
|
if e.Key() == tcell.KeyEsc || (e.Key() == tcell.KeyRune && e.Rune() == 'i') {
|
||||||
|
iss.exitMenu = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iss *InventoryScreenState) OnTick(dt int64) GameState {
|
||||||
|
if iss.exitMenu {
|
||||||
|
return iss.prevState
|
||||||
|
}
|
||||||
|
|
||||||
|
return iss
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iss *InventoryScreenState) CollectDrawables() []render.Drawable {
|
||||||
|
return append(
|
||||||
|
iss.prevState.CollectDrawables(),
|
||||||
|
iss.inventoryMenu,
|
||||||
|
)
|
||||||
|
}
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"mvvasilev/last_light/util"
|
"mvvasilev/last_light/util"
|
||||||
|
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
"github.com/gdamore/tcell/v2/views"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type MainMenuState struct {
|
type MainMenuState struct {
|
||||||
|
@ -78,10 +77,14 @@ func (mms *MainMenuState) OnTick(dt int64) GameState {
|
||||||
return mms
|
return mms
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mms *MainMenuState) OnDraw(c views.View) {
|
func (mms *MainMenuState) CollectDrawables() []render.Drawable {
|
||||||
mms.menuTitle.Draw(c)
|
arr := make([]render.Drawable, 0)
|
||||||
|
|
||||||
|
arr = append(arr, mms.menuTitle)
|
||||||
|
|
||||||
for _, b := range mms.buttons {
|
for _, b := range mms.buttons {
|
||||||
b.Draw(c)
|
arr = append(arr, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return arr
|
||||||
}
|
}
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"mvvasilev/last_light/util"
|
"mvvasilev/last_light/util"
|
||||||
|
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
"github.com/gdamore/tcell/v2/views"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type PauseGameState struct {
|
type PauseGameState struct {
|
||||||
|
@ -27,13 +26,13 @@ func PauseGame(prevState PausableState) *PauseGameState {
|
||||||
|
|
||||||
highlightStyle := tcell.StyleDefault.Attributes(tcell.AttrBold)
|
highlightStyle := tcell.StyleDefault.Attributes(tcell.AttrBold)
|
||||||
|
|
||||||
s.pauseMenuWindow = ui.CreateWindow(uint16(render.TERMINAL_SIZE_WIDTH)/2-15, uint16(render.TERMINAL_SIZE_HEIGHT)/2-7, 30, 14, "PAUSED", tcell.StyleDefault)
|
s.pauseMenuWindow = ui.CreateWindow(int(render.TERMINAL_SIZE_WIDTH)/2-15, int(render.TERMINAL_SIZE_HEIGHT)/2-7, 30, 14, "PAUSED", tcell.StyleDefault)
|
||||||
s.buttons = make([]*ui.UISimpleButton, 0)
|
s.buttons = make([]*ui.UISimpleButton, 0)
|
||||||
s.buttons = append(
|
s.buttons = append(
|
||||||
s.buttons,
|
s.buttons,
|
||||||
ui.CreateSimpleButton(
|
ui.CreateSimpleButton(
|
||||||
uint16(s.pauseMenuWindow.Position().X())+3,
|
int(s.pauseMenuWindow.Position().X())+3,
|
||||||
uint16(s.pauseMenuWindow.Position().Y())+1,
|
int(s.pauseMenuWindow.Position().Y())+1,
|
||||||
"Resume",
|
"Resume",
|
||||||
tcell.StyleDefault,
|
tcell.StyleDefault,
|
||||||
highlightStyle,
|
highlightStyle,
|
||||||
|
@ -45,8 +44,8 @@ func PauseGame(prevState PausableState) *PauseGameState {
|
||||||
s.buttons = append(
|
s.buttons = append(
|
||||||
s.buttons,
|
s.buttons,
|
||||||
ui.CreateSimpleButton(
|
ui.CreateSimpleButton(
|
||||||
uint16(s.pauseMenuWindow.Position().X())+3,
|
int(s.pauseMenuWindow.Position().X())+3,
|
||||||
uint16(s.pauseMenuWindow.Position().Y())+3,
|
int(s.pauseMenuWindow.Position().Y())+3,
|
||||||
"Exit To Main Menu",
|
"Exit To Main Menu",
|
||||||
tcell.StyleDefault,
|
tcell.StyleDefault,
|
||||||
highlightStyle,
|
highlightStyle,
|
||||||
|
@ -97,12 +96,16 @@ func (pg *PauseGameState) OnTick(dt int64) GameState {
|
||||||
return pg
|
return pg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pg *PauseGameState) OnDraw(c views.View) {
|
func (pg *PauseGameState) CollectDrawables() []render.Drawable {
|
||||||
pg.prevState.OnDraw(c)
|
arr := make([]render.Drawable, 0)
|
||||||
|
|
||||||
pg.pauseMenuWindow.Draw(c)
|
arr = append(arr, pg.prevState.CollectDrawables()...)
|
||||||
|
|
||||||
|
arr = append(arr, pg.pauseMenuWindow)
|
||||||
|
|
||||||
for _, b := range pg.buttons {
|
for _, b := range pg.buttons {
|
||||||
b.Draw(c)
|
arr = append(arr, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return arr
|
||||||
}
|
}
|
|
@ -1,56 +0,0 @@
|
||||||
package state
|
|
||||||
|
|
||||||
import (
|
|
||||||
"mvvasilev/last_light/game/model"
|
|
||||||
|
|
||||||
"github.com/gdamore/tcell/v2"
|
|
||||||
"github.com/gdamore/tcell/v2/views"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PlayingState struct {
|
|
||||||
player *model.Player
|
|
||||||
|
|
||||||
pauseGame bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func BeginPlayingState() *PlayingState {
|
|
||||||
s := new(PlayingState)
|
|
||||||
|
|
||||||
s.player = model.CreatePlayer(10, 10, tcell.StyleDefault)
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ps *PlayingState) Pause() {
|
|
||||||
ps.pauseGame = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ps *PlayingState) Unpause() {
|
|
||||||
ps.pauseGame = false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ps *PlayingState) SetPaused(paused bool) {
|
|
||||||
ps.pauseGame = paused
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ps *PlayingState) OnInput(e *tcell.EventKey) {
|
|
||||||
ps.player.Input(e)
|
|
||||||
|
|
||||||
if e.Key() == tcell.KeyEsc {
|
|
||||||
ps.pauseGame = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ps *PlayingState) OnTick(dt int64) GameState {
|
|
||||||
ps.player.Tick(dt)
|
|
||||||
|
|
||||||
if ps.pauseGame {
|
|
||||||
return PauseGame(ps)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ps
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ps *PlayingState) OnDraw(c views.View) {
|
|
||||||
ps.player.Draw(c)
|
|
||||||
}
|
|
144
game/state/playing_state.go
Normal file
144
game/state/playing_state.go
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mvvasilev/last_light/game/model"
|
||||||
|
"mvvasilev/last_light/render"
|
||||||
|
"mvvasilev/last_light/util"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/gdamore/tcell/v2/views"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PlayingState struct {
|
||||||
|
player *model.Player
|
||||||
|
level *model.MultilevelMap
|
||||||
|
|
||||||
|
viewport *render.Viewport
|
||||||
|
|
||||||
|
movePlayerDirection model.Direction
|
||||||
|
pauseGame bool
|
||||||
|
openInventory bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func BeginPlayingState() *PlayingState {
|
||||||
|
s := new(PlayingState)
|
||||||
|
|
||||||
|
mapSize := util.SizeOf(128, 128)
|
||||||
|
|
||||||
|
s.player = model.CreatePlayer(40, 12)
|
||||||
|
|
||||||
|
s.level = model.CreateMultilevelMap(
|
||||||
|
model.CreateFlatGroundDungeonLevel(mapSize.WH()),
|
||||||
|
model.CreateEmptyDungeonLevel(mapSize.WH()),
|
||||||
|
)
|
||||||
|
|
||||||
|
s.level.SetTileAtHeight(40, 12, 1, s.player)
|
||||||
|
|
||||||
|
s.viewport = render.CreateViewport(
|
||||||
|
util.PositionAt(0, 0),
|
||||||
|
util.PositionAt(40, 12),
|
||||||
|
util.SizeOf(80, 24),
|
||||||
|
tcell.StyleDefault,
|
||||||
|
)
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *PlayingState) Pause() {
|
||||||
|
ps.pauseGame = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *PlayingState) Unpause() {
|
||||||
|
ps.pauseGame = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *PlayingState) SetPaused(paused bool) {
|
||||||
|
ps.pauseGame = paused
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *PlayingState) MovePlayer() {
|
||||||
|
if ps.movePlayerDirection == model.DirectionNone {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newPlayerPos := ps.player.Position().WithOffset(model.MovementDirectionOffset(ps.movePlayerDirection))
|
||||||
|
|
||||||
|
tileAtMovePos := ps.level.TileAt(newPlayerPos.XY())
|
||||||
|
|
||||||
|
if tileAtMovePos.Passable() {
|
||||||
|
ps.level.SetTileAtHeight(ps.player.Position().X(), ps.player.Position().Y(), 1, nil)
|
||||||
|
ps.player.Move(ps.movePlayerDirection)
|
||||||
|
ps.viewport.SetCenter(ps.player.Position())
|
||||||
|
ps.level.SetTileAtHeight(ps.player.Position().X(), ps.player.Position().Y(), 1, ps.player)
|
||||||
|
}
|
||||||
|
|
||||||
|
ps.movePlayerDirection = model.DirectionNone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *PlayingState) OnInput(e *tcell.EventKey) {
|
||||||
|
ps.player.Input(e)
|
||||||
|
|
||||||
|
if e.Key() == tcell.KeyEsc {
|
||||||
|
ps.pauseGame = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Key() == tcell.KeyRune && e.Rune() == 'i' {
|
||||||
|
ps.openInventory = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e.Key() {
|
||||||
|
case tcell.KeyUp:
|
||||||
|
ps.movePlayerDirection = model.DirectionUp
|
||||||
|
case tcell.KeyDown:
|
||||||
|
ps.movePlayerDirection = model.DirectionDown
|
||||||
|
case tcell.KeyLeft:
|
||||||
|
ps.movePlayerDirection = model.DirectionLeft
|
||||||
|
case tcell.KeyRight:
|
||||||
|
ps.movePlayerDirection = model.DirectionRight
|
||||||
|
case tcell.KeyRune:
|
||||||
|
switch e.Rune() {
|
||||||
|
case 'w':
|
||||||
|
ps.movePlayerDirection = model.DirectionUp
|
||||||
|
case 'a':
|
||||||
|
ps.movePlayerDirection = model.DirectionLeft
|
||||||
|
case 's':
|
||||||
|
ps.movePlayerDirection = model.DirectionDown
|
||||||
|
case 'd':
|
||||||
|
ps.movePlayerDirection = model.DirectionRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *PlayingState) OnTick(dt int64) GameState {
|
||||||
|
ps.player.Tick(dt)
|
||||||
|
|
||||||
|
if ps.pauseGame {
|
||||||
|
return PauseGame(ps)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ps.openInventory {
|
||||||
|
return CreateInventoryScreenState(ps.player, ps)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ps.movePlayerDirection != model.DirectionNone {
|
||||||
|
ps.MovePlayer()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ps
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *PlayingState) CollectDrawables() []render.Drawable {
|
||||||
|
return render.Multidraw(render.CreateDrawingInstructions(func(v views.View) {
|
||||||
|
ps.viewport.DrawFromProvider(v, func(x, y int) rune {
|
||||||
|
tile := ps.level.TileAt(x, y)
|
||||||
|
|
||||||
|
if tile != nil {
|
||||||
|
return tile.Presentation()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ' '
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}
|
|
@ -1,8 +1,9 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"mvvasilev/last_light/render"
|
||||||
|
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
"github.com/gdamore/tcell/v2/views"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type QuitState struct {
|
type QuitState struct {
|
||||||
|
@ -16,6 +17,6 @@ func (q *QuitState) OnTick(dt int64) GameState {
|
||||||
return q
|
return q
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *QuitState) OnDraw(c views.View) {
|
func (q *QuitState) CollectDrawables() []render.Drawable {
|
||||||
|
return render.Multidraw(nil)
|
||||||
}
|
}
|
51
main.go
51
main.go
|
@ -1,53 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import "mvvasilev/last_light/game"
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"mvvasilev/last_light/game"
|
|
||||||
"mvvasilev/last_light/render"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/gdamore/tcell/v2"
|
|
||||||
"github.com/gdamore/tcell/v2/views"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
gc := game.CreateGameContext()
|
||||||
c, err := render.CreateRenderContext()
|
gc.Run()
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("%~v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
g := game.CreateGame()
|
|
||||||
|
|
||||||
c.HandleInput(func(ev *tcell.EventKey) {
|
|
||||||
if ev.Key() == tcell.KeyCtrlC {
|
|
||||||
c.Stop()
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
g.Input(ev)
|
|
||||||
})
|
|
||||||
|
|
||||||
defStyle := tcell.StyleDefault.Background(tcell.ColorReset).Foreground(tcell.ColorReset)
|
|
||||||
|
|
||||||
c.HandleRender(func(view views.View, deltaTime int64) {
|
|
||||||
fps := 1_000_000 / deltaTime
|
|
||||||
|
|
||||||
fpsText := render.CreateText(0, 0, 16, 1, fmt.Sprintf("%v FPS", fps), defStyle)
|
|
||||||
|
|
||||||
keepGoing := g.Tick(deltaTime)
|
|
||||||
|
|
||||||
if !keepGoing {
|
|
||||||
c.Stop()
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
g.Draw(view)
|
|
||||||
|
|
||||||
fpsText.Draw(view)
|
|
||||||
})
|
|
||||||
|
|
||||||
c.BeginRendering()
|
|
||||||
}
|
}
|
||||||
|
|
28
render/arbitrary_drawable.go
Normal file
28
render/arbitrary_drawable.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package render
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gdamore/tcell/v2/views"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ArbitraryDrawable struct {
|
||||||
|
id uuid.UUID
|
||||||
|
drawingInstructions func(v views.View)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateDrawingInstructions(instructions func(v views.View)) *ArbitraryDrawable {
|
||||||
|
a := new(ArbitraryDrawable)
|
||||||
|
|
||||||
|
a.id = uuid.New()
|
||||||
|
a.drawingInstructions = instructions
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ab *ArbitraryDrawable) UniqueId() uuid.UUID {
|
||||||
|
return ab.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ab *ArbitraryDrawable) Draw(v views.View) {
|
||||||
|
ab.drawingInstructions(v)
|
||||||
|
}
|
|
@ -12,8 +12,8 @@ type Grid struct {
|
||||||
id uuid.UUID
|
id uuid.UUID
|
||||||
|
|
||||||
internalCellSize util.Size
|
internalCellSize util.Size
|
||||||
numCellsHorizontal uint16
|
numCellsHorizontal int
|
||||||
numCellsVertical uint16
|
numCellsVertical int
|
||||||
position util.Position
|
position util.Position
|
||||||
style tcell.Style
|
style tcell.Style
|
||||||
|
|
||||||
|
@ -40,9 +40,9 @@ type Grid struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateSimpleGrid(
|
func CreateSimpleGrid(
|
||||||
x, y uint16,
|
x, y int,
|
||||||
cellWidth, cellHeight uint16,
|
cellWidth, cellHeight int,
|
||||||
numCellsHorizontal, numCellsVertical uint16,
|
numCellsHorizontal, numCellsVertical int,
|
||||||
borderRune, fillRune rune,
|
borderRune, fillRune rune,
|
||||||
style tcell.Style,
|
style tcell.Style,
|
||||||
) Grid {
|
) Grid {
|
||||||
|
@ -61,12 +61,12 @@ func CreateSimpleGrid(
|
||||||
// '├', '─', '┼', '┤',
|
// '├', '─', '┼', '┤',
|
||||||
// '└', '─', '┴', '┘',
|
// '└', '─', '┴', '┘',
|
||||||
func CreateGrid(
|
func CreateGrid(
|
||||||
x uint16,
|
x int,
|
||||||
y uint16,
|
y int,
|
||||||
cellWidth uint16,
|
cellWidth int,
|
||||||
cellHeight uint16,
|
cellHeight int,
|
||||||
numCellsHorizontal uint16,
|
numCellsHorizontal int,
|
||||||
numCellsVertical uint16,
|
numCellsVertical int,
|
||||||
nwCorner, northBorder, verticalDownwardsTJunction, neCorner,
|
nwCorner, northBorder, verticalDownwardsTJunction, neCorner,
|
||||||
westBorder, fillRune, internalVerticalBorder, eastBorder,
|
westBorder, fillRune, internalVerticalBorder, eastBorder,
|
||||||
horizontalRightTJunction, internalHorizontalBorder, crossJunction, horizontalLeftTJunction,
|
horizontalRightTJunction, internalHorizontalBorder, crossJunction, horizontalLeftTJunction,
|
||||||
|
|
|
@ -15,7 +15,7 @@ type Raw struct {
|
||||||
style tcell.Style
|
style tcell.Style
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateRawDrawable(x, y uint16, style tcell.Style, buffer ...string) *Raw {
|
func CreateRawDrawable(x, y int, style tcell.Style, buffer ...string) *Raw {
|
||||||
r := new(Raw)
|
r := new(Raw)
|
||||||
|
|
||||||
r.position = util.PositionAt(x, y)
|
r.position = util.PositionAt(x, y)
|
||||||
|
@ -30,10 +30,43 @@ func CreateRawDrawable(x, y uint16, style tcell.Style, buffer ...string) *Raw {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateRawDrawableFromBuffer(x, y int, style tcell.Style, buffer [][]rune) *Raw {
|
||||||
|
r := new(Raw)
|
||||||
|
|
||||||
|
r.position = util.PositionAt(x, y)
|
||||||
|
r.buffer = buffer
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Raw) UniqueId() uuid.UUID {
|
func (r *Raw) UniqueId() uuid.UUID {
|
||||||
return r.id
|
return r.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Raw) DrawWithin(screenX, screenY, originX, originY, width, height int, v views.View) {
|
||||||
|
for h := originY; h < originY+height; h++ {
|
||||||
|
|
||||||
|
if h < 0 || h >= len(r.buffer) {
|
||||||
|
screenY += 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for w := originX; w < originX+width; w++ {
|
||||||
|
if w < 0 || w >= len(r.buffer[h]) {
|
||||||
|
screenX += 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
v.SetContent(screenX, screenY, r.buffer[h][w], nil, r.style)
|
||||||
|
|
||||||
|
screenX += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
screenX = 0
|
||||||
|
screenY += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Raw) Draw(v views.View) {
|
func (r *Raw) Draw(v views.View) {
|
||||||
x := r.position.X()
|
x := r.position.X()
|
||||||
y := r.position.Y()
|
y := r.position.Y()
|
||||||
|
|
|
@ -31,7 +31,7 @@ type Rectangle struct {
|
||||||
fillRune rune
|
fillRune rune
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateBorderlessRectangle(x, y uint16, width, height uint16, fillRune rune, style tcell.Style) Rectangle {
|
func CreateBorderlessRectangle(x, y int, width, height int, fillRune rune, style tcell.Style) Rectangle {
|
||||||
return CreateRectangle(
|
return CreateRectangle(
|
||||||
x, y, width, height,
|
x, y, width, height,
|
||||||
0, 0, 0,
|
0, 0, 0,
|
||||||
|
@ -41,7 +41,7 @@ func CreateBorderlessRectangle(x, y uint16, width, height uint16, fillRune rune,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateSimpleEmptyRectangle(x, y uint16, width, height uint16, borderRune rune, style tcell.Style) Rectangle {
|
func CreateSimpleEmptyRectangle(x, y int, width, height int, borderRune rune, style tcell.Style) Rectangle {
|
||||||
return CreateRectangle(
|
return CreateRectangle(
|
||||||
x, y, width, height,
|
x, y, width, height,
|
||||||
borderRune, borderRune, borderRune,
|
borderRune, borderRune, borderRune,
|
||||||
|
@ -51,7 +51,7 @@ func CreateSimpleEmptyRectangle(x, y uint16, width, height uint16, borderRune ru
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateSimpleRectangle(x uint16, y uint16, width uint16, height uint16, borderRune rune, fillRune rune, style tcell.Style) Rectangle {
|
func CreateSimpleRectangle(x int, y int, width int, height int, borderRune rune, fillRune rune, style tcell.Style) Rectangle {
|
||||||
return CreateRectangle(
|
return CreateRectangle(
|
||||||
x, y, width, height,
|
x, y, width, height,
|
||||||
borderRune, borderRune, borderRune,
|
borderRune, borderRune, borderRune,
|
||||||
|
@ -62,7 +62,7 @@ func CreateSimpleRectangle(x uint16, y uint16, width uint16, height uint16, bord
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateRectangleV2(
|
func CreateRectangleV2(
|
||||||
x, y uint16, width, height uint16,
|
x, y int, width, height int,
|
||||||
upper, middle, lower string,
|
upper, middle, lower string,
|
||||||
isBorderless, isFilled bool,
|
isBorderless, isFilled bool,
|
||||||
style tcell.Style,
|
style tcell.Style,
|
||||||
|
@ -91,10 +91,10 @@ func CreateRectangleV2(
|
||||||
//
|
//
|
||||||
// )
|
// )
|
||||||
func CreateRectangle(
|
func CreateRectangle(
|
||||||
x uint16,
|
x int,
|
||||||
y uint16,
|
y int,
|
||||||
width uint16,
|
width int,
|
||||||
height uint16,
|
height int,
|
||||||
nwCorner, northBorder, neCorner,
|
nwCorner, northBorder, neCorner,
|
||||||
westBorder, fillRune, eastBorder,
|
westBorder, fillRune, eastBorder,
|
||||||
swCorner, southBorder, seCorner rune,
|
swCorner, southBorder, seCorner rune,
|
||||||
|
|
|
@ -2,8 +2,8 @@ package render
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
"github.com/gdamore/tcell/v2/views"
|
"github.com/gdamore/tcell/v2/views"
|
||||||
|
@ -22,39 +22,51 @@ type Drawable interface {
|
||||||
Draw(v views.View)
|
Draw(v views.View)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Multidraw(drawables ...Drawable) []Drawable {
|
||||||
|
arr := make([]Drawable, 0)
|
||||||
|
|
||||||
|
if drawables == nil {
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, d := range drawables {
|
||||||
|
if d == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
arr = append(arr, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
type RenderContext struct {
|
type RenderContext struct {
|
||||||
screen tcell.Screen
|
screen tcell.Screen
|
||||||
view *views.ViewPort
|
view *views.ViewPort
|
||||||
defaultStyle tcell.Style
|
|
||||||
|
|
||||||
events chan tcell.Event
|
events chan tcell.Event
|
||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
|
drawables chan Drawable
|
||||||
lastRenderTime time.Time
|
|
||||||
|
|
||||||
renderHandler func(view views.View, deltaTime int64)
|
|
||||||
inputHandler func(ev *tcell.EventKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateRenderContext() (*RenderContext, error) {
|
func CreateRenderContext() (*RenderContext, error) {
|
||||||
s, err := tcell.NewScreen()
|
screen, sErr := tcell.NewScreen()
|
||||||
|
|
||||||
if err != nil {
|
if sErr != nil {
|
||||||
log.Fatal(err)
|
log.Fatalf("%~v", sErr)
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stopScreen := func() {
|
stopScreen := func() {
|
||||||
s.Fini()
|
screen.Fini()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.Init(); err != nil {
|
if err := screen.Init(); err != nil {
|
||||||
stopScreen()
|
stopScreen()
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
width, height := s.Size()
|
width, height := screen.Size()
|
||||||
|
|
||||||
if width < TERMINAL_SIZE_WIDTH || height < TERMINAL_SIZE_HEIGHT {
|
if width < TERMINAL_SIZE_WIDTH || height < TERMINAL_SIZE_HEIGHT {
|
||||||
stopScreen()
|
stopScreen()
|
||||||
|
@ -63,27 +75,25 @@ func CreateRenderContext() (*RenderContext, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
view := views.NewViewPort(
|
view := views.NewViewPort(
|
||||||
s,
|
screen,
|
||||||
(width/2)-(TERMINAL_SIZE_WIDTH/2),
|
(width/2)-(TERMINAL_SIZE_WIDTH/2),
|
||||||
(height/2)-(TERMINAL_SIZE_HEIGHT/2),
|
(height/2)-(TERMINAL_SIZE_HEIGHT/2),
|
||||||
TERMINAL_SIZE_WIDTH,
|
TERMINAL_SIZE_WIDTH,
|
||||||
TERMINAL_SIZE_HEIGHT,
|
TERMINAL_SIZE_HEIGHT,
|
||||||
)
|
)
|
||||||
|
|
||||||
defStyle := tcell.StyleDefault.Background(DEFAULT_STYLE_BACKGROUND).Foreground(DEFAULT_STYLE_FOREGROUND)
|
|
||||||
|
|
||||||
events := make(chan tcell.Event)
|
events := make(chan tcell.Event)
|
||||||
quit := make(chan struct{})
|
quit := make(chan struct{})
|
||||||
|
|
||||||
go s.ChannelEvents(events, quit)
|
go screen.ChannelEvents(events, quit)
|
||||||
|
|
||||||
context := new(RenderContext)
|
context := new(RenderContext)
|
||||||
|
|
||||||
context.screen = s
|
context.screen = screen
|
||||||
context.defaultStyle = defStyle
|
|
||||||
context.events = events
|
context.events = events
|
||||||
context.quit = quit
|
context.quit = quit
|
||||||
context.view = view
|
context.view = view
|
||||||
|
context.drawables = make(chan Drawable)
|
||||||
|
|
||||||
return context, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
|
@ -92,12 +102,31 @@ func (c *RenderContext) Stop() {
|
||||||
c.screen.Fini()
|
c.screen.Fini()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RenderContext) HandleRender(renderHandler func(view views.View, deltaTime int64)) {
|
func (c *RenderContext) CollectInputEvents() []*tcell.EventKey {
|
||||||
c.renderHandler = renderHandler
|
events := make([]tcell.Event, len(c.events))
|
||||||
|
|
||||||
|
select {
|
||||||
|
case e := <-c.events:
|
||||||
|
events = append(events, e)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
inputEvents := make([]*tcell.EventKey, 0, len(events))
|
||||||
|
|
||||||
|
for _, e := range events {
|
||||||
|
switch ev := e.(type) {
|
||||||
|
case *tcell.EventKey:
|
||||||
|
inputEvents = append(inputEvents, ev)
|
||||||
|
case *tcell.EventResize:
|
||||||
|
c.onResize(ev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return inputEvents
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RenderContext) HandleInput(inputHandler func(ev *tcell.EventKey)) {
|
func (c *RenderContext) DrawableQueue() chan Drawable {
|
||||||
c.inputHandler = inputHandler
|
return c.drawables
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RenderContext) onResize(ev *tcell.EventResize) {
|
func (c *RenderContext) onResize(ev *tcell.EventResize) {
|
||||||
|
@ -115,33 +144,18 @@ func (c *RenderContext) onResize(ev *tcell.EventResize) {
|
||||||
c.screen.Sync()
|
c.screen.Sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RenderContext) BeginRendering() {
|
func (c *RenderContext) Draw(deltaTime int64, drawables []Drawable) {
|
||||||
c.lastRenderTime = time.Now()
|
fps := 1_000_000 / deltaTime
|
||||||
|
|
||||||
for {
|
c.view.Clear()
|
||||||
deltaTime := 1 + time.Since(c.lastRenderTime).Microseconds()
|
|
||||||
c.lastRenderTime = time.Now()
|
|
||||||
|
|
||||||
c.screen.Clear()
|
fpsText := CreateText(0, 0, 16, 1, fmt.Sprintf("%v FPS", fps), tcell.StyleDefault)
|
||||||
|
|
||||||
c.renderHandler(c.view, deltaTime)
|
for _, d := range drawables {
|
||||||
|
d.Draw(c.view)
|
||||||
|
}
|
||||||
|
|
||||||
|
fpsText.Draw(c.view)
|
||||||
|
|
||||||
c.screen.Show()
|
c.screen.Show()
|
||||||
|
|
||||||
select {
|
|
||||||
case ev, ok := <-c.events:
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ev := ev.(type) {
|
|
||||||
case *tcell.EventResize:
|
|
||||||
c.onResize(ev)
|
|
||||||
case *tcell.EventKey:
|
|
||||||
c.inputHandler(ev)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -19,8 +19,8 @@ type Text struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateText(
|
func CreateText(
|
||||||
x, y uint16,
|
x, y int,
|
||||||
width, height uint16,
|
width, height int,
|
||||||
content string,
|
content string,
|
||||||
style tcell.Style,
|
style tcell.Style,
|
||||||
) *Text {
|
) *Text {
|
||||||
|
|
96
render/viewport.go
Normal file
96
render/viewport.go
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
package render
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mvvasilev/last_light/util"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/gdamore/tcell/v2/views"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Viewport struct {
|
||||||
|
id uuid.UUID
|
||||||
|
screenLocation util.Position
|
||||||
|
|
||||||
|
viewportCenter util.Position
|
||||||
|
viewportSize util.Size
|
||||||
|
|
||||||
|
style tcell.Style
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateViewport(screenLoc, viewportCenter util.Position, size util.Size, style tcell.Style) *Viewport {
|
||||||
|
v := new(Viewport)
|
||||||
|
|
||||||
|
v.id = uuid.New()
|
||||||
|
v.screenLocation = screenLoc
|
||||||
|
v.viewportCenter = viewportCenter
|
||||||
|
v.viewportSize = size
|
||||||
|
v.style = style
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vp *Viewport) UniqueId() uuid.UUID {
|
||||||
|
return vp.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vp *Viewport) Center() util.Position {
|
||||||
|
return vp.viewportCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vp *Viewport) SetCenter(pos util.Position) {
|
||||||
|
vp.viewportCenter = pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vp *Viewport) Size() util.Size {
|
||||||
|
return vp.viewportSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vp *Viewport) ScreenLocation() util.Position {
|
||||||
|
return vp.screenLocation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vp *Viewport) DrawFromProvider(v views.View, provider func(x, y int) rune) {
|
||||||
|
width, height := vp.viewportSize.WH()
|
||||||
|
originX, originY := vp.viewportCenter.WithOffset(-width/2, -height/2).XY()
|
||||||
|
screenX, screenY := vp.screenLocation.XY()
|
||||||
|
|
||||||
|
for h := originY; h < originY+height; h++ {
|
||||||
|
for w := originX; w < originX+width; w++ {
|
||||||
|
v.SetContent(screenX, screenY, provider(w, h), nil, vp.style)
|
||||||
|
|
||||||
|
screenX += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
screenX = 0
|
||||||
|
screenY += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vp *Viewport) Draw(v views.View, buffer [][]rune) {
|
||||||
|
width, height := vp.viewportSize.WH()
|
||||||
|
originX, originY := vp.viewportCenter.WithOffset(-width/2, -height/2).XY()
|
||||||
|
screenX, screenY := vp.screenLocation.XY()
|
||||||
|
|
||||||
|
for h := originY; h < originY+height; h++ {
|
||||||
|
|
||||||
|
if h < 0 || h >= len(buffer) {
|
||||||
|
screenY += 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for w := originX; w < originX+width; w++ {
|
||||||
|
if w < 0 || w >= len(buffer[h]) {
|
||||||
|
screenX += 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
v.SetContent(screenX, screenY, buffer[h][w], nil, vp.style)
|
||||||
|
|
||||||
|
screenX += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
screenX = 0
|
||||||
|
screenY += 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -41,7 +41,7 @@ func (b *UIBorderedButton) UniqueId() uuid.UUID {
|
||||||
return b.id
|
return b.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *UIBorderedButton) MoveTo(x uint16, y uint16) {
|
func (b *UIBorderedButton) MoveTo(x int, y int) {
|
||||||
panic("not implemented") // TODO: Implement
|
panic("not implemented") // TODO: Implement
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ type UIContainer struct {
|
||||||
elements []UIElement
|
elements []UIElement
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateUIContainer(x, y uint16, width, height uint16, layout UIContainerLayout) *UIContainer {
|
func CreateUIContainer(x, y int, width, height int, layout UIContainerLayout) *UIContainer {
|
||||||
container := new(UIContainer)
|
container := new(UIContainer)
|
||||||
|
|
||||||
container.id = uuid.New()
|
container.id = uuid.New()
|
||||||
|
@ -57,7 +57,7 @@ func (uic *UIContainer) UniqueId() uuid.UUID {
|
||||||
return uic.id
|
return uic.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uic *UIContainer) MoveTo(x, y uint16) {
|
func (uic *UIContainer) MoveTo(x, y int) {
|
||||||
uic.position = util.PositionAt(x, y)
|
uic.position = util.PositionAt(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
ui/label.go
10
ui/label.go
|
@ -15,7 +15,7 @@ type UILabel struct {
|
||||||
text *render.Text
|
text *render.Text
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateUILabel(x, y uint16, width, height uint16, content string, style tcell.Style) *UILabel {
|
func CreateUILabel(x, y int, width, height int, content string, style tcell.Style) *UILabel {
|
||||||
label := new(UILabel)
|
label := new(UILabel)
|
||||||
|
|
||||||
label.id = uuid.New()
|
label.id = uuid.New()
|
||||||
|
@ -24,11 +24,11 @@ func CreateUILabel(x, y uint16, width, height uint16, content string, style tcel
|
||||||
return label
|
return label
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateSingleLineUILabel(x, y uint16, content string, style tcell.Style) *UILabel {
|
func CreateSingleLineUILabel(x, y int, content string, style tcell.Style) *UILabel {
|
||||||
label := new(UILabel)
|
label := new(UILabel)
|
||||||
|
|
||||||
label.id = uuid.New()
|
label.id = uuid.New()
|
||||||
label.text = render.CreateText(x, y, uint16(utf8.RuneCountInString(content)), 1, content, style)
|
label.text = render.CreateText(x, y, int(utf8.RuneCountInString(content)), 1, content, style)
|
||||||
|
|
||||||
return label
|
return label
|
||||||
}
|
}
|
||||||
|
@ -37,8 +37,8 @@ func (t *UILabel) UniqueId() uuid.UUID {
|
||||||
return t.id
|
return t.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *UILabel) MoveTo(x uint16, y uint16) {
|
func (t *UILabel) MoveTo(x int, y int) {
|
||||||
t.text = render.CreateText(x, y, uint16(t.text.Size().Width()), uint16(t.Size().Height()), t.text.Content(), t.text.Style())
|
t.text = render.CreateText(x, y, int(t.text.Size().Width()), int(t.Size().Height()), t.text.Content(), t.text.Style())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *UILabel) Position() util.Position {
|
func (t *UILabel) Position() util.Position {
|
||||||
|
|
|
@ -20,11 +20,11 @@ type UISimpleButton struct {
|
||||||
highlightedStyle tcell.Style
|
highlightedStyle tcell.Style
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateSimpleButton(x, y uint16, text string, unhighlightedStyle, highlightedStyle tcell.Style, onSelect func()) *UISimpleButton {
|
func CreateSimpleButton(x, y int, text string, unhighlightedStyle, highlightedStyle tcell.Style, onSelect func()) *UISimpleButton {
|
||||||
sb := new(UISimpleButton)
|
sb := new(UISimpleButton)
|
||||||
|
|
||||||
sb.id = uuid.New()
|
sb.id = uuid.New()
|
||||||
sb.text = render.CreateText(x, y, uint16(utf8.RuneCountInString(text)), 1, text, unhighlightedStyle)
|
sb.text = render.CreateText(x, y, int(utf8.RuneCountInString(text)), 1, text, unhighlightedStyle)
|
||||||
sb.isHighlighted = false
|
sb.isHighlighted = false
|
||||||
sb.selectHandler = onSelect
|
sb.selectHandler = onSelect
|
||||||
sb.highlightedStyle = highlightedStyle
|
sb.highlightedStyle = highlightedStyle
|
||||||
|
@ -51,8 +51,8 @@ func (sb *UISimpleButton) Highlight() {
|
||||||
newContent := "[ " + sb.text.Content() + " ]"
|
newContent := "[ " + sb.text.Content() + " ]"
|
||||||
|
|
||||||
sb.text = render.CreateText(
|
sb.text = render.CreateText(
|
||||||
uint16(sb.Position().X()-2), uint16(sb.Position().Y()),
|
int(sb.Position().X()-2), int(sb.Position().Y()),
|
||||||
uint16(utf8.RuneCountInString(newContent)), 1,
|
int(utf8.RuneCountInString(newContent)), 1,
|
||||||
newContent,
|
newContent,
|
||||||
sb.highlightedStyle,
|
sb.highlightedStyle,
|
||||||
)
|
)
|
||||||
|
@ -66,8 +66,8 @@ func (sb *UISimpleButton) Unhighlight() {
|
||||||
contentLen := utf8.RuneCountInString(content)
|
contentLen := utf8.RuneCountInString(content)
|
||||||
|
|
||||||
sb.text = render.CreateText(
|
sb.text = render.CreateText(
|
||||||
uint16(sb.Position().X()+2), uint16(sb.Position().Y()),
|
int(sb.Position().X()+2), int(sb.Position().Y()),
|
||||||
uint16(contentLen), 1,
|
int(contentLen), 1,
|
||||||
content,
|
content,
|
||||||
sb.unhighlightedStyle,
|
sb.unhighlightedStyle,
|
||||||
)
|
)
|
||||||
|
@ -81,8 +81,8 @@ func (sb *UISimpleButton) UniqueId() uuid.UUID {
|
||||||
return sb.id
|
return sb.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sb *UISimpleButton) MoveTo(x uint16, y uint16) {
|
func (sb *UISimpleButton) MoveTo(x int, y int) {
|
||||||
sb.text = render.CreateText(x, y, uint16(utf8.RuneCountInString(sb.text.Content())), 1, sb.text.Content(), sb.highlightedStyle)
|
sb.text = render.CreateText(x, y, int(utf8.RuneCountInString(sb.text.Content())), 1, sb.text.Content(), sb.highlightedStyle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sb *UISimpleButton) Position() util.Position {
|
func (sb *UISimpleButton) Position() util.Position {
|
9
ui/ui.go
9
ui/ui.go
|
@ -1,20 +1,19 @@
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"mvvasilev/last_light/render"
|
||||||
"mvvasilev/last_light/util"
|
"mvvasilev/last_light/util"
|
||||||
|
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
"github.com/gdamore/tcell/v2/views"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type UIElement interface {
|
type UIElement interface {
|
||||||
UniqueId() uuid.UUID
|
MoveTo(x, y int)
|
||||||
MoveTo(x, y uint16)
|
|
||||||
Position() util.Position
|
Position() util.Position
|
||||||
Size() util.Size
|
Size() util.Size
|
||||||
Draw(v views.View)
|
|
||||||
Input(e *tcell.EventKey)
|
Input(e *tcell.EventKey)
|
||||||
|
|
||||||
|
render.Drawable
|
||||||
}
|
}
|
||||||
|
|
||||||
type UIHighlightableElement interface {
|
type UIHighlightableElement interface {
|
||||||
|
|
|
@ -17,14 +17,14 @@ type UIWindow struct {
|
||||||
box render.Rectangle
|
box render.Rectangle
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateWindow(x, y, width, height uint16, title string, style tcell.Style) *UIWindow {
|
func CreateWindow(x, y, width, height int, title string, style tcell.Style) *UIWindow {
|
||||||
w := new(UIWindow)
|
w := new(UIWindow)
|
||||||
|
|
||||||
titleLen := utf8.RuneCountInString(title)
|
titleLen := utf8.RuneCountInString(title)
|
||||||
|
|
||||||
titlePos := (width / 2) - uint16(titleLen/2)
|
titlePos := (width / 2) - int(titleLen/2)
|
||||||
|
|
||||||
w.title = render.CreateText(x+titlePos, y, uint16(titleLen), 1, title, style)
|
w.title = render.CreateText(x+titlePos, y, int(titleLen), 1, title, style)
|
||||||
|
|
||||||
w.box = render.CreateRectangle(
|
w.box = render.CreateRectangle(
|
||||||
x, y, width, height,
|
x, y, width, height,
|
||||||
|
@ -41,7 +41,7 @@ func (w *UIWindow) UniqueId() uuid.UUID {
|
||||||
return w.id
|
return w.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *UIWindow) MoveTo(x uint16, y uint16) {
|
func (w *UIWindow) MoveTo(x int, y int) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
18
util/util.go
18
util/util.go
|
@ -5,7 +5,7 @@ type Position struct {
|
||||||
y int
|
y int
|
||||||
}
|
}
|
||||||
|
|
||||||
func PositionAt(x uint16, y uint16) Position {
|
func PositionAt(x int, y int) Position {
|
||||||
return Position{int(x), int(y)}
|
return Position{int(x), int(y)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,8 +21,10 @@ func (p Position) XY() (int, int) {
|
||||||
return p.x, p.y
|
return p.x, p.y
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Position) XYUint16() (uint16, uint16) {
|
func (p Position) WithOffset(xOffset int, yOffset int) Position {
|
||||||
return uint16(p.x), uint16(p.y)
|
p.x = p.x + xOffset
|
||||||
|
p.y = p.y + yOffset
|
||||||
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
type Size struct {
|
type Size struct {
|
||||||
|
@ -30,10 +32,14 @@ type Size struct {
|
||||||
height int
|
height int
|
||||||
}
|
}
|
||||||
|
|
||||||
func SizeOf(width uint16, height uint16) Size {
|
func SizeOf(width int, height int) Size {
|
||||||
return Size{int(width), int(height)}
|
return Size{int(width), int(height)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SizeOfInt(width int, height int) Size {
|
||||||
|
return Size{width, height}
|
||||||
|
}
|
||||||
|
|
||||||
func (s Size) Width() int {
|
func (s Size) Width() int {
|
||||||
return s.width
|
return s.width
|
||||||
}
|
}
|
||||||
|
@ -42,8 +48,8 @@ func (s Size) Height() int {
|
||||||
return s.height
|
return s.height
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Size) WHUint16() (uint16, uint16) {
|
func (s Size) WH() (int, int) {
|
||||||
return uint16(s.width), uint16(s.height)
|
return s.width, s.height
|
||||||
}
|
}
|
||||||
|
|
||||||
func LimitIncrement(i int, limit int) int {
|
func LimitIncrement(i int, limit int) int {
|
||||||
|
|
Loading…
Add table
Reference in a new issue