From cb7718860ac6c013a8d120f6bacfa97060cd12cd Mon Sep 17 00:00:00 2001 From: thefish Date: Fri, 8 Nov 2019 03:36:26 +0300 Subject: [PATCH] ui starting, menu screen, ingame help --- cmd/game/main.go | 47 ++++++++---- engine/gamemap/mapgens/default.go | 5 +- engine/screens/game.go | 17 +++-- engine/screens/menu.go | 116 ++++++++++++++++++++++++++++++ engine/screens/sample.go | 11 +++ engine/screens/title.go | 67 +++++++++++++++-- engine/types/rect.go | 22 +++--- ui/mainwindow/layer.go | 16 ++++- ui/mainwindow/mainwindow.go | 2 +- ui/mainwindow/uiwindow.go | 4 +- ui/mainwindow/viewport.go | 4 +- 11 files changed, 271 insertions(+), 40 deletions(-) create mode 100644 engine/screens/menu.go create mode 100644 engine/screens/sample.go diff --git a/cmd/game/main.go b/cmd/game/main.go index 0d546ea..7c4b5f7 100644 --- a/cmd/game/main.go +++ b/cmd/game/main.go @@ -59,7 +59,6 @@ func main() { // set up context mainCtx := util.NewClientContext(config, &logger) - //set up main window mw := mainwindow.Init(mainCtx) defer mw.Close() @@ -69,7 +68,6 @@ func main() { //set up input decoder go decodeInput(mainCtx, mw.GetLayer("base")) - //fixme set up (load / generate) level level, rooms := mapgens.DefaultGen(gamemap.NewLevel(mainCtx, "test", 1)) State.Level = level @@ -78,7 +76,6 @@ func main() { vp := mainwindow.NewViewPort(30, 0, (mw.W - 30), (mw.H - 0), mw.GetLayer("base")) go vp.Listen(State) - //set up controller controller := ecs.NewController() @@ -90,16 +87,39 @@ func main() { moveable := movement.Moveable{ Controller: controller, - Level: level, + Level: level, } - //Set up Screen Manager screenMgr := types.NewScreenManager(mainCtx) - screenMgr.AddScreen("title", &screens.TitleScreen{}) - screenMgr.AddScreen("game", screens.NewGameScreen(mw, &State, vp, controller)) + screenMgr.AddScreen("title", screens.NewTitleScreen(mw, screenMgr)) + screenMgr.AddScreen("game", screens.NewGameScreen(mw, &State, vp, controller, screenMgr)) + screenMgr.AddScreen("help", screens.NewMenuScreen( + mw, + screenMgr, + "Help", + "Keybindings:", + //"[color=yellow]Note[/color]: Many of these are not implemented yet", + "[color=yellow]Note[/color]: Many of these are not implemented yet", + types.NewCenteredRect(mw.Rect, 50, 15), + true, ). + SetBgColor("#ef1d494f"). + SetFgColor("white"). + SetItems([]interface{}{ + "hjklyubn 12346789 or arrow keys - move", + "s or . - pass turn", + "g or , - pick up item", + "i - inventory", + "? - this screen", + "Ctrl+q - exit", + "f or F - fire or throw weapon", + "z or Z - cast a spell", + "p - pray", + "Ctrl+p - message log", + }), + ) - screenMgr.SetScreenByName("game") + screenMgr.SetScreenByName("title") //fixme //player := &mob.Player{ @@ -119,7 +139,6 @@ func main() { //vp.PlayerCoords = player.Coords //vp.Render(&State) - //fixme set up (load / generate) player player := controller.CreateEntity([]ecs.Component{}) @@ -153,9 +172,10 @@ func main() { mainCtx.Logger().Warn().Msg("quitting NOW") exit = true break - // не оставляйте default в бесконесчном select {} - сожрет всё CPU + // не оставляйте default в бесконечном select {} - сожрет всё CPU default: screenMgr.CurrentScreen.Render() + blt.Layer(0) //return to base layer blt.Refresh() } @@ -166,7 +186,8 @@ func main() { func setupLayers(mainwindow *mainwindow.MainWindow) { mainwindow.AddLayer("base", 0, "white") mainwindow.AddLayer("overlay", 1, "white") - mainwindow.AddLayer("menu", 2, "white") + mainwindow.AddLayer("menubg", 2, "white") + mainwindow.AddLayer("menu", 3, "white") } func decodeInput(ctx util.ClientCtx, baseLayer *mainwindow.Layer) { @@ -208,8 +229,8 @@ func decodeInput(ctx util.ClientCtx, baseLayer *mainwindow.Layer) { blt.Set("window: size=100x47; font: ./resources/fonts-ttf/UbuntuMono-R.ttf, size=11;") }) case "Ctrl+q": - fallthrough - case "Escape": + //fallthrough + //case "Escape": ctx.Logger().Info().Msg("exiting on quit command...") State.Exit <- struct{}{} ctx.Logger().Info().Msg("...done") diff --git a/engine/gamemap/mapgens/default.go b/engine/gamemap/mapgens/default.go index 38da59e..c3da87a 100644 --- a/engine/gamemap/mapgens/default.go +++ b/engine/gamemap/mapgens/default.go @@ -24,6 +24,9 @@ func DefaultGen(l *gamemap.Level) (*gamemap.Level, []*gamemap.Room) { rooms := make([]*gamemap.Room, 0) + //one wall around whole level guaranteed + levelBoundary := types.NewRect(l.X + 1, l.Y + 1, l.W - 2, l.H - 2) + for i := 0; i < maxrooms; i++ { newRoom := &gamemap.Room{ Rect: types.NewRect( @@ -36,7 +39,7 @@ func DefaultGen(l *gamemap.Level) (*gamemap.Level, []*gamemap.Room) { failed := false - if !l.InBounds(types.Coords{newRoom.X, newRoom.Y}) { + if !levelBoundary.InBounds(types.Coords{newRoom.X, newRoom.Y}) { failed = true } diff --git a/engine/screens/game.go b/engine/screens/game.go index 8e73ae2..7eabc47 100644 --- a/engine/screens/game.go +++ b/engine/screens/game.go @@ -14,18 +14,24 @@ type GameScreen struct { state *gamestate.GameState vp *mainwindow.ViewPort controller *ecs.Controller + scm *types.ScreenManager } -func NewGameScreen(mw *mainwindow.MainWindow, state *gamestate.GameState, viewPort *mainwindow.ViewPort, controller *ecs.Controller) *GameScreen { - return &GameScreen{mw: mw, state: state, vp: viewPort, controller: controller} +func NewGameScreen(mw *mainwindow.MainWindow, state *gamestate.GameState, viewPort *mainwindow.ViewPort, controller *ecs.Controller, scm *types.ScreenManager) *GameScreen { + ts := &GameScreen{mw: mw, state: state, vp: viewPort, controller: controller, scm: scm} + renderMobs := systems.MobRenderSystem{EntityController: ts.controller} + ts.controller.AddSystem(renderMobs, 1) + return ts } func (ts *GameScreen) UseEcs() bool { return true } func (ts *GameScreen) Enter() { - renderMobs := systems.MobRenderSystem{EntityController: ts.controller} - ts.controller.AddSystem(renderMobs, 1) + ts.mw.GetLayer("overlay").ClearArea(0, ts.mw.H - 3, 30, 3) + ts.mw.GetLayer("overlay").WithColor("#77777777"). + Print(1, ts.mw.H - 2, "Press [color=white]?[/color] for help") } func (ts *GameScreen) Exit() { + ts.mw.GetLayer("base").ClearArea(1, ts.mw.H -2, 30, 1) //remove what we dont need } @@ -56,6 +62,9 @@ func (ts *GameScreen) HandleInput(input string) { case "n", "3": ts.walk(ts.state, 1, 1) break + case "Shift+/": + ts.scm.SetScreenByName("help") + break default: ts.mw.GetLayer("base").ClearArea(0, 3, 40, 1) ts.mw.GetLayer("base").Print(1, 3, "Key: "+input) diff --git a/engine/screens/menu.go b/engine/screens/menu.go new file mode 100644 index 0000000..41c79b5 --- /dev/null +++ b/engine/screens/menu.go @@ -0,0 +1,116 @@ +package screens + +import ( + "lab.zaar.be/thefish/alchemyst-go/engine/types" + "lab.zaar.be/thefish/alchemyst-go/ui/mainwindow" + blt "lab.zaar.be/thefish/bearlibterminal" + "strings" +) + +const runeIndex = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + +type MenuScreen struct { + *types.Rect + + mw *mainwindow.MainWindow + scm *types.ScreenManager + renderParent bool + + items []interface{} + title string + header string + footer string + redraw bool + bgColor string + fgColor string +} + +func NewMenuScreen(mw *mainwindow.MainWindow, scm *types.ScreenManager, title, header, footer string, rect *types.Rect, renderParent bool) *MenuScreen { + return &MenuScreen{ + title: title, + header: header, + footer: footer, + Rect: rect, + mw: mw, + scm: scm, + renderParent: renderParent, + } +} + +func (ms *MenuScreen) SetBgColor(color string) *MenuScreen { + ms.bgColor = color + return ms +} + +func (ms *MenuScreen) SetFgColor(color string) *MenuScreen { + ms.fgColor = color + return ms +} + +//fixme!! + +func (ms *MenuScreen) SetItems(items []interface{}) *MenuScreen { + ms.items = items + return ms +} + +func (ms *MenuScreen) UseEcs() bool { return false } + +func (ms *MenuScreen) Enter() { + ms.redraw = true +} + +func (ms *MenuScreen) HandleInput(input string) { + // + //if input != "" { + // ms.redraw = true + //} + + switch input { + case "Space": + ms.scm.SetScreen(ms.scm.PreviousScreen) + break + } +} + +func (ms *MenuScreen) Exit() { + menuLayer := ms.mw.GetLayer("menu") + menuLayer.ClearRect(ms.Rect) + bgLayer := ms.mw.GetLayer("menubg") + bgLayer.ClearRect(ms.Rect) +} + +func (ms *MenuScreen) Render() { + if ms.renderParent { + ms.scm.PreviousScreen.Render() + } + if (ms.redraw || ms.renderParent) { + ms.redraw = false + menuLayer := ms.mw.GetLayer("menu") + menuLayer.ClearRect(ms.Rect) + bgLayer := ms.mw.GetLayer("menubg") + bgLayer.ClearRect(ms.Rect) + bgLayer.WithColor(ms.bgColor).NewWindow(ms.Rect).Splash() + menuLayer.WithColor(ms.fgColor).NewWindow(ms.Rect).DoubleBordered(ms.title) + menuLayer.Print(ms.X+(ms.W/2)-7, ms.Y+ms.H-1, "╡"+"[color=green]Space[/color] to close"+"╞") + footerHeight := 0 + if ms.footer != "" { + _, footerHeight = menuLayer.PrintInside(ms.Rect, ms.footer, 9) + footerHeight = footerHeight + 2 + } + _, headerHeight := menuLayer.PrintInside(ms.Rect, ms.header, blt.TK_ALIGN_LEFT) + itemField := types.Rect{ms.X, ms.Y + headerHeight + 1, ms.W, ms.H - headerHeight - footerHeight} + _ = itemField + if (len(ms.items) > 0) { + //fixme itemfield object, scroller, inputhandler, current selected item + menuItems := make([]string, 0) + for i, _ := range ms.items { + if string(ms.items[i].(string)) != "" { + menuItems = append(menuItems, ms.items[i].(string)) + } + } + menuLayer.PrintInside(&itemField, strings.Join(menuItems, "\n"), blt.TK_ALIGN_LEFT) + + } + } +} diff --git a/engine/screens/sample.go b/engine/screens/sample.go new file mode 100644 index 0000000..ae26a73 --- /dev/null +++ b/engine/screens/sample.go @@ -0,0 +1,11 @@ +package screens + +type SampleScreen struct { + +} + +func (ss *SampleScreen) UseEcs() bool {return false} +func (ss *SampleScreen) Enter() {} +func (ss *SampleScreen) HandleInput(input string) {} +func (ss *SampleScreen) Exit() {} +func (ss *SampleScreen) Render() {} diff --git a/engine/screens/title.go b/engine/screens/title.go index 1390985..2441a9b 100644 --- a/engine/screens/title.go +++ b/engine/screens/title.go @@ -1,11 +1,66 @@ package screens -type TitleScreen struct { +import ( + "lab.zaar.be/thefish/alchemyst-go/engine/types" + "lab.zaar.be/thefish/alchemyst-go/ui/mainwindow" + blt "lab.zaar.be/thefish/bearlibterminal" +) +type TitleScreen struct { + mw *mainwindow.MainWindow + scm *types.ScreenManager } -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() {} +func NewTitleScreen(mw *mainwindow.MainWindow, scm *types.ScreenManager) *TitleScreen { + return &TitleScreen{mw: mw, scm: scm} +} + +func (ts *TitleScreen) UseEcs() bool { return false } +func (ts *TitleScreen) Enter() { + blt.Clear() +} +func (ts *TitleScreen) HandleInput(input string) { + switch input { + case "n": + ts.scm.SetScreenByName("game") + } +} +func (ts *TitleScreen) Exit() { + blt.Clear() +} +func (ts *TitleScreen) Render() { + blt.PrintExt(0, 0, ts.mw.W, ts.mw.H, 15, logo) +} + +var logo = ` + .ddxo. .c c xlx + .kd. .0o:O, c0uO' + xc 0; x: ko ,, + ;l Y; c, .Yct; + .. .. ....... + :o: :k; .cxloOo x0. kO .olldk; do .;k lO. dO 'xllxk; ocokk:dl + .lKO, .kl 'dl c l0l..;dK lkx..;d: dOx .cOx ,0o c0 kk. .Y .Oo + .l0.k0. .0l lK. l0oxxxOK OK;xxx kKOklOkx ,kO;'do. ;xO: .Oo + :OoooxO .Ol ;Xc , cK' dK xX. , Ox YY xx ,O0. . .oKx. .0o +;k. .Ox Ok..'.,l ;Kk:;dk cKl lX: dKc,;oc kx kk OO dKl;lk: .0O +l; ,o .kK0KKK; :Ok; c; l: .oxc. x: Ol xl .dk: .o + + + +Alchemyst (c) 2011-2014 thefish + + + +[color=green]N[/color]ew dungeon run +[color=green]L[/color]oad saved game +Read [color=green]h[/color]elp file +Highest [color=green]S[/color]cores + + + +Roguebasin Libtcod Tutorial (c) 2010-2011, Jotaf Henriques +Brogue 1.3 (c) 2010 Brian Walker +Madness (c) 2010 hmp +BearLibTerminal (c) Cfyz 2009-2019 +Gogue (c) jcerise +` \ No newline at end of file diff --git a/engine/types/rect.go b/engine/types/rect.go index 64a4212..50fdd02 100644 --- a/engine/types/rect.go +++ b/engine/types/rect.go @@ -12,10 +12,16 @@ func NewRect(x, y, w, h int) *Rect { return &Rect{x, y, w, h} } +func NewCenteredRect(source *Rect, w, h int) *Rect { + targetX := source.X + source.W / 2 - w / 2 + targetY := source.Y + source.H / 2 - h / 2 + return &Rect{targetX, targetY, w, h} +} + func (r *Rect) Blit(fillage RectFill, layer Putable) { if fillage.Body != "" { - for i := r.X + 1; i < r.X+r.W; i++ { + for i := r.X+1; i < r.X+r.W - 1; i++ { for j := r.Y + 1; j < r.Y+r.H; j++ { layer.Put(i, j, fillage.Body) //lii.Put(i, j, "X"); @@ -23,27 +29,27 @@ func (r *Rect) Blit(fillage RectFill, layer Putable) { } } - for i := r.X + 1; i < r.X+r.W; i++ { + for i := r.X; i < r.X+r.W - 1; i++ { layer.Put(i, r.Y, fillage.Top) //lii.Put(i, Y-1, "Q"); - layer.Put(i, r.Y+r.H, fillage.Bottom) + layer.Put(i, r.Y+r.H - 1, fillage.Bottom) //lii.Put(i, Y+H, "H"); } - for j := r.Y + 1; j < r.Y+r.H; j++ { + for j := r.Y; j < r.Y+r.H; j++ { layer.Put(r.X, j, fillage.Left) //lii.Put(X-1, j, "U"); - layer.Put(r.X+r.W, j, fillage.Right) + layer.Put(r.X+r.W - 1, j, fillage.Right) //lii.Put(X+W, j, "M"); } layer.Put(r.X, r.Y, fillage.TopLeft) //lii.Put(X-1, Y-1, "T"); - layer.Put(r.X, r.Y+r.H, fillage.BottomLeft) + layer.Put(r.X, r.Y+r.H - 1, fillage.BottomLeft) //lii.Put(X-1, Y+H, "q"); - layer.Put(r.X+r.W, r.Y, fillage.TopRight) + layer.Put(r.X+r.W - 1, r.Y, fillage.TopRight) //lii.Put(X+W, Y-1, "L"); - layer.Put(r.X+r.W, r.Y+r.H, fillage.BottomRight) + layer.Put(r.X+r.W - 1, r.Y+r.H - 1, fillage.BottomRight) } func (self *Rect) Intersects(other *Rect) bool { diff --git a/ui/mainwindow/layer.go b/ui/mainwindow/layer.go index f7f131e..e7f0543 100644 --- a/ui/mainwindow/layer.go +++ b/ui/mainwindow/layer.go @@ -6,12 +6,12 @@ import ( ) type Layer struct { - idx int + Idx int defaultColor uint32 } func (layer *Layer) before() *Layer { - blt.Layer(layer.idx) + blt.Layer(layer.Idx) return layer } @@ -61,6 +61,7 @@ func (Layer *Layer) PutToBase(x,y int, symbol interface{}, fg uint32, bg uint32) rnes := []rune(symbol.(string)) prevColor := uint32(blt.State(blt.TK_COLOR)) prevBgColor := uint32(blt.State(blt.TK_BKCOLOR)) + blt.Layer(0) blt.BkColor(bg) blt.Color(fg) if (len(rnes)) > 0 { @@ -87,7 +88,13 @@ func (layer Layer) Put(x, y int, symbol interface{}) { } func (layer Layer) Print(x, y int, txt string) (w, h int) { - return blt.Print(x, y, txt) + w, h = blt.Print(x, y, txt) + //blt.Layer(0) + return w, h +} + +func (layer Layer) PrintInside(rect *types.Rect, text string, alignment int) (width int, height int) { + return blt.PrintExt(rect.X + 2, rect.Y + 2, rect.W - 4, rect.H - 4, alignment, text) } func (layer *Layer) Decorate(f func(args ...interface{})) func(args ...interface{}) { @@ -99,7 +106,10 @@ func (layer *Layer) Decorate(f func(args ...interface{})) func(args ...interface } func (layer *Layer) ClearRect(r *types.Rect) { + prevLayer := int(blt.State(blt.TK_LAYER)) + blt.Layer(layer.Idx) blt.ClearArea(r.X, r.Y, r.W, r.H) + blt.Layer(prevLayer) } func (layer *Layer) ClearArea(x,y,w,h int) { diff --git a/ui/mainwindow/mainwindow.go b/ui/mainwindow/mainwindow.go index 12c5ecd..f7c0ce1 100644 --- a/ui/mainwindow/mainwindow.go +++ b/ui/mainwindow/mainwindow.go @@ -24,7 +24,7 @@ func Init(ctx util.ClientCtx) *MainWindow { func (mw *MainWindow) AddLayer(name string, idx int, colorName string) *MainWindow { c := blt.ColorFromName(colorName) - mw.layers[name] = &Layer{idx: idx, defaultColor: c} + mw.layers[name] = &Layer{Idx: idx, defaultColor: c} return mw } diff --git a/ui/mainwindow/uiwindow.go b/ui/mainwindow/uiwindow.go index 6d0a621..7809f66 100644 --- a/ui/mainwindow/uiwindow.go +++ b/ui/mainwindow/uiwindow.go @@ -43,9 +43,9 @@ type UiWindow struct { fillage types.RectFill } -func (layer *Layer) NewWindow(x, y, w, h int) *UiWindow { +func (layer *Layer) NewWindow(rect *types.Rect) *UiWindow { return &UiWindow{ - Rect: types.NewRect(x, y, w, h), + Rect: rect, layer: layer, } } diff --git a/ui/mainwindow/viewport.go b/ui/mainwindow/viewport.go index 5920e20..e0f38c7 100644 --- a/ui/mainwindow/viewport.go +++ b/ui/mainwindow/viewport.go @@ -105,8 +105,8 @@ func (vp *ViewPort) Render(state *gamestate.GameState) { vp.Fov.ComputeFov(state.Level, playerCoords, vp.TorchRadius) } - vp.layer.ClearArea(0, 7, 20, 1) - vp.layer.Print(0,7, fmt.Sprintf("pcds: %v", playerCoords)) + //vp.layer.ClearArea(0, 7, 20, 1) + //vp.layer.Print(0,7, fmt.Sprintf("pcds: %v", playerCoords)) if redraw {