fixes to viewport, config tuning

This commit is contained in:
thefish 2019-11-01 15:03:52 +03:00
parent a91351d3dc
commit c6c6b6254d
11 changed files with 228 additions and 173 deletions

View File

@ -5,6 +5,7 @@ import (
"github.com/rs/zerolog/log"
"lab.zaar.be/thefish/alchemyst-go/engine/gamemap"
"lab.zaar.be/thefish/alchemyst-go/engine/gamemap/mapgens"
"lab.zaar.be/thefish/alchemyst-go/engine/types"
"lab.zaar.be/thefish/alchemyst-go/ui"
"lab.zaar.be/thefish/alchemyst-go/ui/mainwindow"
"lab.zaar.be/thefish/alchemyst-go/util"
@ -22,23 +23,6 @@ func init() {
runtime.LockOSThread()
}
type GameState struct {
mainfunc chan func()
exit chan struct{}
input chan string
rawInput chan int
}
// do runs f on the main thread.
func (*GameState) Do(f func()) {
done := make(chan struct{}, 1)
State.mainfunc <- func() {
f()
done <- struct{}{}
}
<-done
}
//we can run logic in separate goroutines
//
// go doSometing(State,...)
@ -52,11 +36,13 @@ func (*GameState) Do(f func()) {
// ...
// }
var State = GameState{
mainfunc: make(chan func()),
exit: make(chan struct{}, 1),
input: make(chan string, 1),
rawInput: make(chan int, 1),
var State = types.GameState{
Mainfunc: make(chan func()),
Exit: make(chan struct{}, 1),
Input: make(chan string, 1),
RawInput: make(chan int, 1),
FovRecompute: make(chan struct{},1),
Redraw: make(chan struct{},1),
}
func main() {
@ -72,35 +58,37 @@ func main() {
setupLayers(mw)
level := gamemap.NewLevel(mainCtx, "test", 1)
level = mapgens.DefaultGen(level)
level, rooms := mapgens.DefaultGen(gamemap.NewLevel(mainCtx, "test", 1))
vp := mainwindow.NewViewPort(40, 0, 60, 47, level, mw.GetLayer("base"))
vp.Render()
vp.PlayerCoords = rooms[0].Center
vp.Render(State)
go decodeInput(mainCtx, mw.GetLayer("base"))
go vp.Listen(State)
//but every call to bearlibterminal must be wrapped to closure and passed to mainfunc
var exit = false
for !exit {
select {
case State.rawInput <- ui.ReadKeyCode():
case State.RawInput <- ui.ReadKeyCode():
break
case pressed := <-State.input:
case pressed := <-State.Input:
mw.GetLayer("base").ClearArea(0, 3, 40, 1)
mw.GetLayer("base").Print(1, 3, "Key: "+pressed)
mw.GetLayer("base").Print(1, 6, "█")
break
//case f := <-State.mainfunc:
// f()
// break
case <-State.exit:
//case f := <-State.mainfunc:
// f()
// break
case <-State.Exit:
mainCtx.Logger().Warn().Msg("quitting NOW")
exit = true
break
// не оставляйте default в бесконесчном select {} - сожрет всё CPU
// не оставляйте default в бесконесчном select {} - сожрет всё CPU
default:
vp.Render()
vp.Render(State)
blt.Refresh()
}
@ -117,20 +105,20 @@ func setupLayers(mainwindow *mainwindow.MainWindow) {
func decodeInput(ctx util.ClientCtx, baseLayer *mainwindow.Layer) {
var exit = false
var waitForWCspam = true
for !exit{
for !exit {
select {
case keycode := <-State.rawInput:
case keycode := <-State.RawInput:
if keycode == blt.TK_NONE {
continue
}
if keycode == blt.TK_CLOSE && !waitForWCspam {
ctx.Logger().Warn().Msg("exiting on window close...")
State.exit <- struct{}{}
State.Exit <- struct{}{}
ctx.Logger().Warn().Msg("...done")
return
}
var pressed= ""
var isModifier, _= util.InArray(keycode, modifiers)
var pressed = ""
var isModifier, _ = util.InArray(keycode, modifiers)
if !isModifier {
pressed = ui.Scancodemap[keycode]
@ -147,6 +135,7 @@ func decodeInput(ctx util.ClientCtx, baseLayer *mainwindow.Layer) {
//global hotkeys
switch pressed {
//fixme testing only
case "F10":
State.Do(func() {
blt.Set("window: size=100x47; font: ./resources/fonts-ttf/UbuntuMono-R.ttf, size=11;")
@ -155,13 +144,15 @@ func decodeInput(ctx util.ClientCtx, baseLayer *mainwindow.Layer) {
fallthrough
case "Escape":
ctx.Logger().Info().Msg("exiting on quit command...")
State.exit <- struct{}{}
State.Exit <- struct{}{}
ctx.Logger().Info().Msg("...done")
exit = true
return
default:
waitForWCspam = false;
State.input <- pressed
if pressed != "" {
waitForWCspam = false;
State.Input <- pressed
}
}
}
}

View File

@ -4,6 +4,7 @@
"sizeX": 100,
"sizeY": 47,
"fpsLimit" : 60,
"font": "./resources/fonts-ttf/LiberationMono-Bold.ttf",
"font": "./resources/fonts-ttf/UbuntuMono-R.ttf",
"fontSize": "12x16",
"verbosity": "debug"
}

View File

@ -7,8 +7,8 @@ import (
)
//fixme move to config
var mapWidth = 150
var mapHeight = 100
var mapWidth = 70
var mapHeight = 50
type Level struct {

View File

@ -11,7 +11,7 @@ var maxRoomSize = 22
var maxrooms = 30
//fixme make closure to stack them
func DefaultGen(l *gamemap.Level) *gamemap.Level {
func DefaultGen(l *gamemap.Level) (*gamemap.Level, []*gamemap.Room) {
rng := util.NewRNG()
@ -56,6 +56,9 @@ func DefaultGen(l *gamemap.Level) *gamemap.Level {
if !failed {
rooms = append(rooms, newRoom)
}
//addStairs(rooms)
//itemize(rooms)
}
//fillage := types.RectFill{
@ -89,7 +92,7 @@ func DefaultGen(l *gamemap.Level) *gamemap.Level {
}
}
return l
return l, rooms
}
func connectRooms (l *gamemap.Level, room, otherRoom *gamemap.Room, fillage types.RectFill, toss int) {

View File

@ -1,69 +1,8 @@
package gamemap
import (
"github.com/gammazero/deque"
"lab.zaar.be/thefish/alchemyst-go/util"
. "lab.zaar.be/thefish/alchemyst-go/engine/types"
)
import blt "lab.zaar.be/thefish/bearlibterminal"
type ColorHolder interface {
GetColor() uint32
}
type cdeque struct {
deque.Deque
}
func (c *cdeque) Next() uint8 {
c.Rotate(1)
return c.Front().(uint8)
}
type DanceColorHolder struct {
A uint8
R *cdeque
G *cdeque
B *cdeque
}
func (chd *DanceColorHolder) GetColor() uint32 {
return blt.ColorFromARGB(
chd.A,
chd.R.Next(),
chd.G.Next(),
chd.B.Next(),
)
}
type PlainColorHolder struct {
A uint8
R uint8
G uint8
B uint8
}
func (chb *PlainColorHolder) GetColor() uint32 {
return blt.ColorFromARGB(
chb.A,
chb.R,
chb.G,
chb.B,
)
}
type TileColorSet struct {
Fg ColorHolder
Bg ColorHolder
DarkFg ColorHolder
DarkBg ColorHolder
}
type Appearance struct {
Char string `json:"char"`
ColorSet *TileColorSet `json:"colorSet"`
}
var crng = util.NewRNG()
type Tile struct {
*Appearance `json:"app"`
@ -97,33 +36,6 @@ func (t *Tile) GetRawBgColor() uint32 {
}
}
func singleColorRing(colorValue uint8) *cdeque {
c := &cdeque{}
c.PushBack(colorValue)
return c
}
func fillColorRing(colorValue uint8, minGlow, maxGlow, step int) *cdeque {
q := make([]uint8, 0)
color := int(colorValue)
for color < maxGlow {
q = append(q, uint8(color))
color = crng.Range(1, step) + color
}
color = crng.Range(0, step+minGlow)
q = append(q, uint8(color))
//for uint8(color) < uint8(colorValue) {
// q = append(q, uint8(color))
// color = crng.Range(1, step+minGlow)
//}
c := &cdeque{}
for _, v := range q {
c.PushBack(uint8(v))
}
return c
}
func NewWall() *Tile {
return &Tile{
Name: "Wall",
@ -178,14 +90,14 @@ func NewWaterTile() *Tile {
Appearance: &Appearance{
Char: " ",
ColorSet: &TileColorSet{
Fg: &PlainColorHolder{255, 220, 220, 250},
Fg: &PlainColorHolder{255, 220, 220, 250},
Bg: &DanceColorHolder{
255,
singleColorRing(19),
fillColorRing(19, 0, 15, 2),
fillColorRing(70, 120, 220, 12),
SingleColorRing(19),
FillColorRing(19, 0, 15, 2),
FillColorRing(70, 120, 220, 12),
},
DarkFg: &PlainColorHolder{255, 30, 20, 50 },
DarkFg: &PlainColorHolder{255, 30, 20, 50},
DarkBg: &PlainColorHolder{255, 7, 7, 30},
},
},
@ -205,16 +117,16 @@ func NewDeepWaterTile() *Tile {
Appearance: &Appearance{
Char: " ",
ColorSet: &TileColorSet{
Fg: &PlainColorHolder{255, 220, 220, 250},
Fg: &PlainColorHolder{255, 220, 220, 250},
Bg: &DanceColorHolder{
255,
singleColorRing(5),
fillColorRing(2,2,42,4),
fillColorRing(154, 150, 229, 12),
SingleColorRing(5),
FillColorRing(2, 2, 42, 4),
FillColorRing(154, 150, 229, 12),
},
DarkFg: &PlainColorHolder{255, 30, 20, 50},
DarkBg: &PlainColorHolder{255, 7, 7, 30},
},
},
}
}
}

View File

@ -0,0 +1,94 @@
package types
import (
"github.com/gammazero/deque"
"lab.zaar.be/thefish/alchemyst-go/util"
)
import blt "lab.zaar.be/thefish/bearlibterminal"
var crng = util.NewRNG()
type ColorHolder interface {
GetColor() uint32
}
type cdeque struct {
deque.Deque
}
func (c *cdeque) Next() uint8 {
c.Rotate(1)
return c.Front().(uint8)
}
type DanceColorHolder struct {
A uint8
R *cdeque
G *cdeque
B *cdeque
}
func (chd *DanceColorHolder) GetColor() uint32 {
return blt.ColorFromARGB(
chd.A,
chd.R.Next(),
chd.G.Next(),
chd.B.Next(),
)
}
type PlainColorHolder struct {
A uint8
R uint8
G uint8
B uint8
}
func (chb *PlainColorHolder) GetColor() uint32 {
return blt.ColorFromARGB(
chb.A,
chb.R,
chb.G,
chb.B,
)
}
type TileColorSet struct {
Fg ColorHolder
Bg ColorHolder
DarkFg ColorHolder
DarkBg ColorHolder
}
type Appearance struct {
Char string `json:"char"`
ColorSet *TileColorSet `json:"colorSet"`
}
func SingleColorRing(colorValue uint8) *cdeque {
c := &cdeque{}
c.PushBack(colorValue)
return c
}
func FillColorRing(colorValue uint8, minGlow, maxGlow, step int) *cdeque {
q := make([]uint8, 0)
color := int(colorValue)
for color < maxGlow {
q = append(q, uint8(color))
color = crng.Range(1, step) + color
}
color = crng.Range(0, step+minGlow)
q = append(q, uint8(color))
//for uint8(color) < uint8(colorValue) {
// q = append(q, uint8(color))
// color = crng.Range(1, step+minGlow)
//}
c := &cdeque{}
for _, v := range q {
c.PushBack(uint8(v))
}
return c
}

20
engine/types/gamestate.go Normal file
View File

@ -0,0 +1,20 @@
package types
type GameState struct {
Mainfunc chan func()
Exit chan struct{}
Input chan string
RawInput chan int
FovRecompute chan struct{}
Redraw chan struct{}
}
// do runs f on the main thread.
func (g *GameState) Do(f func()) {
done := make(chan struct{}, 1)
g.Mainfunc <- func() {
f()
done <- struct{}{}
}
<-done
}

View File

@ -98,7 +98,7 @@ func (layer *Layer) Decorate(f func(args ...interface{})) func(args ...interface
}
}
func (layer *Layer) Clear(r *types.Rect) {
func (layer *Layer) ClearRect(r *types.Rect) {
blt.ClearArea(r.X, r.Y, r.W, r.H)
}

View File

@ -42,13 +42,14 @@ func (mw *MainWindow) Open() {
blt.Set(
fmt.Sprintf(
//"window: size=%dx%d, title='%s v%s'; font: ./resources/fonts-bitmap/ibmnew8x12.png, size=8x12;",
"window: size=%dx%d, title='%s v%s'; font: %s, size=8x16;",
"window: size=%dx%d, title='%s v%s'; font: %s, size=%s;",
//"window: size=%dx%d, title='%s v%s'",
config.MainWindowSizeX,
config.MainWindowSizeY,
config.Title,
config.Version,
config.Font,
config.FontSize,
),
)
}

View File

@ -2,6 +2,7 @@ package mainwindow
import (
"errors"
"fmt"
"lab.zaar.be/thefish/alchemyst-go/engine/fov"
"lab.zaar.be/thefish/alchemyst-go/engine/fov/precomputed_shade"
"lab.zaar.be/thefish/alchemyst-go/engine/gamemap"
@ -10,15 +11,14 @@ import (
var NotInViewError = errors.New("not in ViewPort")
const FPS_LIMIT = 60
type ViewPort struct {
*types.Rect
cameraCoords types.Coords
level *gamemap.Level
layer *Layer
Fov fov.Fov
playerCoords types.Coords
playerTorchRadius int
PlayerCoords types.Coords
PlayerTorchRadius int
}
func NewViewPort(x, y, w, h int, level *gamemap.Level, layer *Layer) *ViewPort {
@ -32,13 +32,13 @@ func NewViewPort(x, y, w, h int, level *gamemap.Level, layer *Layer) *ViewPort {
Fov: fov,
}
vp.playerCoords = types.Coords{10, 10}
vp.playerTorchRadius = 10
vp.PlayerCoords = types.Coords{10, 10}
vp.PlayerTorchRadius = 10
return &vp
}
func (vp *ViewPort) Move(c *types.Coords) {
func (vp *ViewPort) Move(c *types.Coords, state types.GameState) {
x := c.X - vp.Rect.W/2
y := c.Y - vp.Rect.H/2
@ -49,22 +49,26 @@ func (vp *ViewPort) Move(c *types.Coords) {
y = 0
}
if x > vp.level.W-vp.W {
x = vp.level.W - vp.W
x = vp.level.W - vp.W - 1
}
if y > vp.level.H-vp.H {
x = vp.level.H - vp.H
x = vp.level.H - vp.H - 1
}
//if x != vp.X || y != vp.Y { State.FovRecompute <- struct{}{}}
vp.X, vp.Y = x, y
if x != vp.cameraCoords.X || y != vp.cameraCoords.Y {
state.FovRecompute <- struct{}{}
}
vp.cameraCoords.X = x
vp.cameraCoords.Y = y
}
func (vp *ViewPort) ToVPCoords(c *types.Coords) (newCoords *types.Coords, err error) {
func (vp *ViewPort) ToVPCoords(c types.Coords) (newCoords types.Coords, err error) {
//coords on map to coords on vp
x, y := c.X-vp.X, c.Y-vp.Y
if x < 0 || y < 0 || x >= vp.W || y >= vp.H {
return &types.Coords{-1, -1}, NotInViewError
x, y := c.X-vp.cameraCoords.X, c.Y-vp.cameraCoords.Y
if x < 0 || y < 0 || x > vp.W || y > vp.H {
return types.Coords{-1, -1}, NotInViewError
}
return &types.Coords{x, y}, nil
return types.Coords{x, y}, nil
}
////call only from main thread
@ -75,15 +79,15 @@ func (vp *ViewPort) ToVPCoords(c *types.Coords) (newCoords *types.Coords, err er
// redraw := false
// //fixme get player instance
//
// vp.Move(&vp.playerCoords)
// vp.Move(&vp.PlayerCoords)
// //fixme detect fovRecompute
// if fovRecompute {
// vp.layer.Clear(vp.Rect)
// vp.layer.ClearRect(vp.Rect)
// fovRecompute = false
// redraw = true
// //fixme
//
// vp.Fov.ComputeFov(vp.level, vp.playerCoords, vp.playerTorchRadius)
// vp.Fov.ComputeFov(vp.level, vp.PlayerCoords, vp.PlayerTorchRadius)
// }
// //increase ticker
// fpsTicker++
@ -119,26 +123,54 @@ func (vp *ViewPort) ToVPCoords(c *types.Coords) (newCoords *types.Coords, err er
var redraw = true
var fovRecompute = true
func (vp *ViewPort) Render() {
func (vp *ViewPort) Listen(state types.GameState) {
for {
select {
case <-state.FovRecompute:
fovRecompute = true
case <-state.Redraw:
redraw = true
}
}
}
func (vp *ViewPort) Render(state types.GameState) {
vp.Move(&vp.PlayerCoords, state)
if fovRecompute {
vp.layer.Clear(vp.Rect)
vp.layer.ClearRect(vp.Rect)
fovRecompute = false
redraw = true
vp.Fov.ComputeFov(vp.level, vp.playerCoords, vp.playerTorchRadius)
vp.Fov.ComputeFov(vp.level, vp.PlayerCoords, vp.PlayerTorchRadius)
}
if redraw {
//terrain
for y := 0; y < vp.H; y++ {
for x := 0; x < vp.W; x++ {
mapCoords := types.Coords{vp.X + x, vp.Y + y}
mapCoords := types.Coords{vp.cameraCoords.X + x, vp.cameraCoords.Y + y}
tile := vp.level.GetTile(mapCoords)
if tile.Explored || tile.MustDraw || tile.Visible {
vp.layer.PutToBase(mapCoords.X, mapCoords.Y, tile.GetChar(), tile.GetRawColor(), tile.GetRawBgColor())
if vp.level.InBounds(mapCoords) {
tile := vp.level.GetTile(mapCoords)
if tile.Explored || tile.MustDraw || tile.Visible {
vp.layer.PutToBase(x + vp.X, y + vp.Y, tile.GetChar(), tile.GetRawColor(), tile.GetRawBgColor())
}
}
}
}
//mobs
pc,err := vp.ToVPCoords(vp.PlayerCoords)
_ = pc
if err != nil {
fmt.Println("error on getting player position")
} else {
vp.layer.WithColor("white").Put(pc.X + vp.X, pc.Y + vp.Y, "@")
//mw.GetLayer("base").WithColor("white").Put(42, 10, "B")
//mw.GetLayer("overlay").WithColor("white").Put(59, 10, "O")
}
redraw = true
//redraw = false
}

View File

@ -14,6 +14,7 @@ type Config struct {
MainWindowSizeX int `json:"sizeX"`
MainWindowSizeY int `json:"sizeY"`
Font string `json:"font"`
FontSize string `json:"fontSize"` //format is "8x12"
Verbosity string `json:"verbosity"`
}