From 1ac6ae46655d048490e2abe07b38265015e78e33 Mon Sep 17 00:00:00 2001 From: thefish Date: Fri, 1 Nov 2019 17:51:55 +0300 Subject: [PATCH] animation handling, screens, vp changes --- cmd/game/main.go | 39 ++++++-- config.json | 6 +- .../precomputed_shade_test.go | 2 +- engine/gamemap/tile.go | 12 +-- engine/mob/mob.go | 25 ------ engine/screens/character.go | 11 +++ engine/screens/game.go | 60 +++++++++++++ engine/screens/inventory.go | 11 +++ engine/screens/target.go | 11 +++ engine/screens/title.go | 11 +++ engine/types/appearance.go | 17 +++- engine/types/gamestate.go | 1 + engine/types/mob.go | 19 ++++ engine/{mob => types}/player.go | 4 +- engine/types/screen.go | 88 ++++++++++++++++++ ui/mainwindow/viewport.go | 90 ++++++------------- 16 files changed, 297 insertions(+), 110 deletions(-) delete mode 100644 engine/mob/mob.go create mode 100644 engine/screens/character.go create mode 100644 engine/screens/game.go create mode 100644 engine/screens/inventory.go create mode 100644 engine/screens/target.go create mode 100644 engine/screens/title.go create mode 100644 engine/types/mob.go rename engine/{mob => types}/player.go (55%) create mode 100644 engine/types/screen.go diff --git a/cmd/game/main.go b/cmd/game/main.go index 500b934..4169c63 100644 --- a/cmd/game/main.go +++ b/cmd/game/main.go @@ -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/screens" "lab.zaar.be/thefish/alchemyst-go/engine/types" "lab.zaar.be/thefish/alchemyst-go/ui" "lab.zaar.be/thefish/alchemyst-go/ui/mainwindow" @@ -58,11 +59,35 @@ func main() { setupLayers(mw) + //fixme level, rooms := mapgens.DefaultGen(gamemap.NewLevel(mainCtx, "test", 1)) - vp := mainwindow.NewViewPort(40, 0, 60, 47, level, mw.GetLayer("base")) - vp.PlayerCoords = rooms[0].Center - vp.Render(State) + + + screenMgr := types.NewScreenManager(mainCtx) + screenMgr.AddScreen("title", &screens.TitleScreen{}) + screenMgr.AddScreen("game", screens.NewGameScreen(mw, &State, vp)) + + screenMgr.SetScreenByName("game") + + + //fixme + player := &types.Player{ + Mob: types.Mob{ + Appearance: &types.Appearance{ + Glyph: &types.PlainGlyphHolder{"@"}, + ColorSet: &types.TileColorSet{ + Fg: &types.PlainColorHolder{255, 255, 255, 255}, + }, + }, + Coords: rooms[0].Center, + BlocksPass: true, + }, + } + State.Player = player + + vp.PlayerCoords = player.Coords + vp.Render(&State) go decodeInput(mainCtx, mw.GetLayer("base")) go vp.Listen(State) @@ -75,9 +100,7 @@ func main() { case State.RawInput <- ui.ReadKeyCode(): break 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, "█") + screenMgr.CurrentScreen.HandleInput(pressed) break //case f := <-State.mainfunc: // f() @@ -86,9 +109,9 @@ func main() { mainCtx.Logger().Warn().Msg("quitting NOW") exit = true break - // не оставляйте default в бесконесчном select {} - сожрет всё CPU + // не оставляйте default в бесконесчном select {} - сожрет всё CPU default: - vp.Render(State) + screenMgr.CurrentScreen.Render() blt.Refresh() } diff --git a/config.json b/config.json index 9ae5595..7680f2a 100644 --- a/config.json +++ b/config.json @@ -1,10 +1,10 @@ { "version": "0.0.0.1", - "title" : "Test Go+BLT App", + "title" : "Alchemyst", "sizeX": 100, "sizeY": 47, "fpsLimit" : 60, - "font": "./resources/fonts-ttf/UbuntuMono-R.ttf", - "fontSize": "12x16", + "font": "./resources/fonts-ttf/LiberationMono-Bold.ttf", + "fontSize": "8x12", "verbosity": "debug" } \ No newline at end of file diff --git a/engine/fov/precomputed_shade/precomputed_shade_test.go b/engine/fov/precomputed_shade/precomputed_shade_test.go index f703e01..298d7e0 100644 --- a/engine/fov/precomputed_shade/precomputed_shade_test.go +++ b/engine/fov/precomputed_shade/precomputed_shade_test.go @@ -67,7 +67,7 @@ func TestPrecompShade(t *testing.T) { if playerCoords.X == x && playerCoords.Y == y { return "@" } - result := level.GetTileByXY(x, y).Char + result := level.GetTileByXY(x, y).Glyph.GetGlyph() if !level.GetTileByXY(x, y).Visible { result = "?" } diff --git a/engine/gamemap/tile.go b/engine/gamemap/tile.go index ae60388..3a5aae2 100644 --- a/engine/gamemap/tile.go +++ b/engine/gamemap/tile.go @@ -17,7 +17,7 @@ type Tile struct { } func (t *Tile) GetChar() string { - return t.Char + return t.Glyph.GetGlyph() } func (t *Tile) GetRawColor() uint32 { @@ -45,7 +45,7 @@ func NewWall() *Tile { Explored: false, MustDraw: false, Appearance: &Appearance{ - Char: "#", + Glyph: &PlainGlyphHolder{"#"}, ColorSet: &TileColorSet{ Fg: &PlainColorHolder{255, 130, 110, 150}, Bg: &PlainColorHolder{255, 172, 170, 173}, @@ -65,7 +65,7 @@ func NewFloor() *Tile { Explored: false, MustDraw: false, Appearance: &Appearance{ - Char: ".", + Glyph: &PlainGlyphHolder{"."}, ColorSet: &TileColorSet{ Fg: &PlainColorHolder{255, 220, 220, 250}, Bg: &PlainColorHolder{255, 19, 19, 70}, @@ -88,14 +88,14 @@ func NewWaterTile() *Tile { Colordance: true, Appearance: &Appearance{ - Char: " ", + Glyph: &PlainGlyphHolder{" "}, ColorSet: &TileColorSet{ Fg: &PlainColorHolder{255, 220, 220, 250}, Bg: &DanceColorHolder{ 255, SingleColorRing(19), FillColorRing(19, 0, 15, 2), - FillColorRing(70, 120, 220, 12), + FillColorRing(127, 120, 176, 12), }, DarkFg: &PlainColorHolder{255, 30, 20, 50}, DarkBg: &PlainColorHolder{255, 7, 7, 30}, @@ -115,7 +115,7 @@ func NewDeepWaterTile() *Tile { MustDraw: true, //fixme debug Colordance: true, Appearance: &Appearance{ - Char: " ", + Glyph: &PlainGlyphHolder{" "}, ColorSet: &TileColorSet{ Fg: &PlainColorHolder{255, 220, 220, 250}, Bg: &DanceColorHolder{ diff --git a/engine/mob/mob.go b/engine/mob/mob.go deleted file mode 100644 index 1f70c59..0000000 --- a/engine/mob/mob.go +++ /dev/null @@ -1,25 +0,0 @@ -package mob - -import ( - "lab.zaar.be/thefish/alchemyst-go/engine/gamemap" - "lab.zaar.be/thefish/alchemyst-go/engine/types" -) - -type Mob struct { - *gamemap.Appearance - *types.Coords - BlocksPass bool -} - - -func (m *Mob) Walk(dx, dy int) { - -} - -func (m *Mob) Render() { - -} - -func (m *Mob) MoveToCoords(c types.Coords) { - -} \ No newline at end of file diff --git a/engine/screens/character.go b/engine/screens/character.go new file mode 100644 index 0000000..4805e88 --- /dev/null +++ b/engine/screens/character.go @@ -0,0 +1,11 @@ +package screens + +type CharacterScreen struct { + +} + +func (ts *CharacterScreen) UseEcs() bool {return false} +func (ts *CharacterScreen) Enter() {} +func (ts *CharacterScreen) HandleInput(input string) {} +func (ts *CharacterScreen) Exit() {} +func (ts *CharacterScreen) Render() {} diff --git a/engine/screens/game.go b/engine/screens/game.go new file mode 100644 index 0000000..2740ff9 --- /dev/null +++ b/engine/screens/game.go @@ -0,0 +1,60 @@ +package screens + +import ( + "lab.zaar.be/thefish/alchemyst-go/engine/types" + "lab.zaar.be/thefish/alchemyst-go/ui/mainwindow" +) + +type GameScreen struct { + mw *mainwindow.MainWindow + state *types.GameState + vp *mainwindow.ViewPort +} + +func NewGameScreen(mw *mainwindow.MainWindow, state *types.GameState, viewPort *mainwindow.ViewPort) *GameScreen { + return &GameScreen{mw: mw, state: state, vp: viewPort} +} + +func (ts *GameScreen) UseEcs() bool { return true } +func (ts *GameScreen) Enter() {} +func (ts *GameScreen) HandleInput(input string) { + //ts.state.Do(func(){ + switch input { + case "Up", "k", "8": + ts.state.Player.Walk(0, -1) + break + case "Down", "j", "2": + ts.state.Player.Walk(0, 1) + break + case "Left", "h", "4": + ts.state.Player.Walk(-1, 0) + break + case "Right", "l", "6": + ts.state.Player.Walk(1, 0) + break + case "y", "7": + ts.state.Player.Walk(-1, -1) + break + case "u", "9": + ts.state.Player.Walk(1, -1) + break + case "b", "1": + ts.state.Player.Walk(-1, 1) + break + case "n", "3": + ts.state.Player.Walk(-1, 3) + break + + + default: + ts.mw.GetLayer("base").ClearArea(0, 3, 40, 1) + ts.mw.GetLayer("base").Print(1, 3, "Key: "+input) + ts.mw.GetLayer("base").Print(1, 6, "█") + + } + //}) +} +func (ts *GameScreen) Exit() {} +func (ts *GameScreen) Render() { + ts.vp.Render(ts.state) +} diff --git a/engine/screens/inventory.go b/engine/screens/inventory.go new file mode 100644 index 0000000..6b19c5a --- /dev/null +++ b/engine/screens/inventory.go @@ -0,0 +1,11 @@ +package screens + +type InventoryScreen struct { + +} + +func (ts *InventoryScreen) UseEcs() bool {return true} +func (ts *InventoryScreen) Enter() {} +func (ts *InventoryScreen) HandleInput(input string) {} +func (ts *InventoryScreen) Exit() {} +func (ts *InventoryScreen) Render() {} diff --git a/engine/screens/target.go b/engine/screens/target.go new file mode 100644 index 0000000..839efda --- /dev/null +++ b/engine/screens/target.go @@ -0,0 +1,11 @@ +package screens + +type TargetScreen struct { + +} + +func (ts *TargetScreen) UseEcs() bool {return true} +func (ts *TargetScreen) Enter() {} +func (ts *TargetScreen) HandleInput(input string) {} +func (ts *TargetScreen) Exit() {} +func (ts *TargetScreen) Render() {} diff --git a/engine/screens/title.go b/engine/screens/title.go new file mode 100644 index 0000000..1390985 --- /dev/null +++ b/engine/screens/title.go @@ -0,0 +1,11 @@ +package screens + +type TitleScreen struct { + +} + +func (ts *TitleScreen) UseEcs() bool {return false} +func (ts *TitleScreen) Enter() {} +func (ts *TitleScreen) HandleInput(input string) {} +func (ts *TitleScreen) Exit() {} +func (ts *TitleScreen) Render() {} diff --git a/engine/types/appearance.go b/engine/types/appearance.go index d09ded5..d61e397 100644 --- a/engine/types/appearance.go +++ b/engine/types/appearance.go @@ -60,8 +60,21 @@ type TileColorSet struct { DarkBg ColorHolder } + +type GlyphHolder interface { + GetGlyph() string +} + +type PlainGlyphHolder struct { + Glyph string +} + +func (pch *PlainGlyphHolder) GetGlyph() string { + return pch.Glyph +} + type Appearance struct { - Char string `json:"char"` + Glyph GlyphHolder `json:"char"` ColorSet *TileColorSet `json:"colorSet"` } @@ -80,7 +93,7 @@ func FillColorRing(colorValue uint8, minGlow, maxGlow, step int) *cdeque { color = crng.Range(1, step) + color } color = crng.Range(0, step+minGlow) - q = append(q, uint8(color)) + q = append(q, colorValue) //for uint8(color) < uint8(colorValue) { // q = append(q, uint8(color)) // color = crng.Range(1, step+minGlow) diff --git a/engine/types/gamestate.go b/engine/types/gamestate.go index 8ae529e..5478b65 100644 --- a/engine/types/gamestate.go +++ b/engine/types/gamestate.go @@ -7,6 +7,7 @@ type GameState struct { RawInput chan int FovRecompute chan struct{} Redraw chan struct{} + Player *Player } // do runs f on the main thread. diff --git a/engine/types/mob.go b/engine/types/mob.go new file mode 100644 index 0000000..67b7fd5 --- /dev/null +++ b/engine/types/mob.go @@ -0,0 +1,19 @@ +package types + +type Mob struct { + *Appearance + Coords + BlocksPass bool +} + +func (m *Mob) Walk(dx, dy int) { + +} + +func (m *Mob) Render() { + +} + +func (m *Mob) MoveToCoords(c Coords) { + +} \ No newline at end of file diff --git a/engine/mob/player.go b/engine/types/player.go similarity index 55% rename from engine/mob/player.go rename to engine/types/player.go index 1a7d145..052ec2a 100644 --- a/engine/mob/player.go +++ b/engine/types/player.go @@ -1,5 +1,5 @@ -package mob +package types type Player struct { - *Mob + Mob } diff --git a/engine/types/screen.go b/engine/types/screen.go new file mode 100644 index 0000000..8d1dc43 --- /dev/null +++ b/engine/types/screen.go @@ -0,0 +1,88 @@ +package types + +import ( + "lab.zaar.be/thefish/alchemyst-go/util" +) + +type Screen interface { + Enter() + Exit() + Render() + HandleInput(input string) + UseEcs() bool +} + +type ScreenManager struct { + ctx util.ClientCtx + Screens map[string]Screen + CurrentScreen Screen + PreviousScreen Screen +} + +// NewScreenManager is a convenience/constructor method to properly initialize a new ScreenManager +func NewScreenManager(ctx util.ClientCtx) *ScreenManager { + manager := ScreenManager{ + ctx:ctx, + Screens: make(map[string]Screen), + CurrentScreen: nil, + } + return &manager +} + +func (sm *ScreenManager) AddScreen(screenName string, screen Screen) { + // Check to see if a screen with the given screenName has already been added + if _, ok := sm.Screens[screenName]; !ok { + // A screen with the given name does not yet exist on the ScreenManager, go ahead and add it + sm.Screens[screenName] = screen + } else { + sm.ctx.Logger().Warn().Msgf("A screen with name %v was already added to the ScreenManager %v!", screenName, sm) + } +} + +// RemoveScreen will remove a screen from the ScreenManager. This can be useful when a temporary screen needs to be +// created, as it can be quickly added (rather than registering at game creation), and then removed when it is no +// longer needed +func (sm *ScreenManager) RemoveScreen(screenName string, screen Screen) { + // Check if the given screenName exists in the ScreenManager + if _, ok := sm.Screens[screenName]; ok { + delete(sm.Screens, screenName) + } else { + // A screen with the given name does not exist + sm.ctx.Logger().Warn().Msgf("A screen with name %v was not found on ScreenManager %v!", screenName, sm) + } +} + +// SetScreen will set the current screen property of the screen manager to the provided screen +func (sm *ScreenManager) SetScreen(screen Screen) { + // Call the exit function of the currentScreen, and set the currentScreen as the previousScreen + // Only do this if there is a currentScreen + if sm.CurrentScreen != nil { + sm.CurrentScreen.Exit() + sm.PreviousScreen = sm.CurrentScreen + } + + // Set the provided screen as the currentScreen, and call the enter() function of the new currentScreen + sm.CurrentScreen = screen + sm.CurrentScreen.Enter() +} + +// SetScreenByName takes a string representing the screen desired to navigate to. It will then transition the +// ScreenManager to the specified screen, if one is found. +func (sm *ScreenManager) SetScreenByName(screenName string) { + // Check if the given screenName exists in the ScreenManager + if _, ok := sm.Screens[screenName]; ok { + // Call the exit function of the currentScreen, and set the currentScreen as the previousScreen + // Only do this if there is a currentScreen + if sm.CurrentScreen != nil { + sm.CurrentScreen.Exit() + sm.PreviousScreen = sm.CurrentScreen + } + + // Set the provided screen as the currentScreen, and call the enter() function of the new currentScreen + sm.CurrentScreen = sm.Screens[screenName] + sm.CurrentScreen.Enter() + } else { + // A screen with the given name does not exist + sm.ctx.Logger().Warn().Msgf("A screen with name %v was not found on ScreenManager %v!", screenName, sm) + } +} \ No newline at end of file diff --git a/ui/mainwindow/viewport.go b/ui/mainwindow/viewport.go index 96404c8..585ab04 100644 --- a/ui/mainwindow/viewport.go +++ b/ui/mainwindow/viewport.go @@ -7,6 +7,7 @@ import ( "lab.zaar.be/thefish/alchemyst-go/engine/fov/precomputed_shade" "lab.zaar.be/thefish/alchemyst-go/engine/gamemap" "lab.zaar.be/thefish/alchemyst-go/engine/types" + "time" ) var NotInViewError = errors.New("not in ViewPort") @@ -19,26 +20,35 @@ type ViewPort struct { Fov fov.Fov PlayerCoords types.Coords PlayerTorchRadius int + animateTiles *time.Ticker } func NewViewPort(x, y, w, h int, level *gamemap.Level, layer *Layer) *ViewPort { //fixme - fov := precomputed_shade.NewPrecomputedShade(15) - fov.Init() + computedFov := precomputed_shade.NewPrecomputedShade(15) + computedFov.Init() vp := ViewPort{ Rect: &types.Rect{x, y, w, h}, level: level, layer: layer, - Fov: fov, + Fov: computedFov, } - vp.PlayerCoords = types.Coords{10, 10} - vp.PlayerTorchRadius = 10 + vp.PlayerTorchRadius = 14 + vp.animateTiles = time.NewTicker(time.Second / 10) return &vp } -func (vp *ViewPort) Move(c *types.Coords, state types.GameState) { +func (vp *ViewPort) Close() { + vp.animateTiles.Stop() + vp.animateTiles = nil //free pointer to ticker +} + +func (vp *ViewPort) Move(state *types.GameState) { + + c := &state.Player.Coords + x := c.X - vp.Rect.W/2 y := c.Y - vp.Rect.H/2 @@ -71,55 +81,6 @@ func (vp *ViewPort) ToVPCoords(c types.Coords) (newCoords types.Coords, err erro return types.Coords{x, y}, nil } -////call only from main thread -//func (vp *ViewPort) Render() { -// //fixme get these from state chan(s) -// var fpsTicker int -// var fovRecompute bool = true -// redraw := false -// //fixme get player instance -// -// vp.Move(&vp.PlayerCoords) -// //fixme detect fovRecompute -// if fovRecompute { -// vp.layer.ClearRect(vp.Rect) -// fovRecompute = false -// redraw = true -// //fixme -// -// vp.Fov.ComputeFov(vp.level, vp.PlayerCoords, vp.PlayerTorchRadius) -// } -// //increase ticker -// fpsTicker++ -// -// if redraw || fpsTicker%(FPS_LIMIT/10) == 0 { -// fpsTicker = 0 -// -// for y := 0; y < vp.H; y++ { -// for x := 0; x < vp.W; x++ { -// mapCoords := types.Coords{vp.X + x, vp.Y + y} -// tile := vp.level.Tiles[mapCoords.X][mapCoords.Y] -// visible := vp.Fov.IsInFov(mapCoords) -// if !visible { -// if tile.MustDraw { -// //darkened version of landscape -// vp.layer.WithRawColor(tile.ColorSet.DarkFg()). -// PutWithRawBackground(mapCoords.X, mapCoords.Y, tile.Char, tile.ColorSet.DarkBg()) -// } -// } else { -// if redraw == true || tile.Colordance { -// vp.layer.WithRawColor(tile.ColorSet.Fg()). -// PutWithRawBackground(mapCoords.X, mapCoords.Y, tile.Char, tile.ColorSet.Bg()) -// tile.Explored = true -// tile.MustDraw = true -// } -// } -// } -// } -// -// } -//} - var redraw = true var fovRecompute = true @@ -130,19 +91,21 @@ func (vp *ViewPort) Listen(state types.GameState) { fovRecompute = true case <-state.Redraw: redraw = true + case <-vp.animateTiles.C: + redraw = true } } } -func (vp *ViewPort) Render(state types.GameState) { +func (vp *ViewPort) Render(state *types.GameState) { - vp.Move(&vp.PlayerCoords, state) + vp.Move(state) if fovRecompute { vp.layer.ClearRect(vp.Rect) fovRecompute = false redraw = true - vp.Fov.ComputeFov(vp.level, vp.PlayerCoords, vp.PlayerTorchRadius) + vp.Fov.ComputeFov(vp.level, state.Player.Coords, vp.PlayerTorchRadius) } if redraw { @@ -154,24 +117,25 @@ func (vp *ViewPort) Render(state types.GameState) { 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()) + vp.layer.PutToBase(x+vp.X, y+vp.Y, tile.GetChar(), tile.GetRawColor(), tile.GetRawBgColor()) } } } } //mobs - pc,err := vp.ToVPCoords(vp.PlayerCoords) + 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, "@") + 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 + //redraw = true + redraw = false + } }