diff --git a/cmd/game/main.go b/cmd/game/main.go index ec6b64a..ff48cc3 100644 --- a/cmd/game/main.go +++ b/cmd/game/main.go @@ -5,6 +5,7 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" "lab.zaar.be/thefish/alchemyst-go/engine/ecs" + "lab.zaar.be/thefish/alchemyst-go/engine/gamelog" "lab.zaar.be/thefish/alchemyst-go/engine/gamemap" "lab.zaar.be/thefish/alchemyst-go/engine/gamemap/mapgens" "lab.zaar.be/thefish/alchemyst-go/engine/gamestate" @@ -74,9 +75,9 @@ func main() { //fixme set up (load / generate) level - move to game / enter or title / exit level, rooms := mapgens.DefaultGen(gamemap.NewLevel("test", 1)) - //level, rooms := mapgens.DelaunayMstGen(mainCtx, gamemap.NewLevel(mainCtx, "test", 1)) - //level, rooms := mapgens.DelaunayMstExtGen(mainCtx, gamemap.NewLevel(mainCtx, "test", 1)) - //level, rooms := mapgens.DelaunayPureGen(mainCtx, gamemap.NewLevel(mainCtx, "test", 1)) + //level, rooms := mapgens.DelaunayMstGen(mainCtx, gamemap.NewLevel("test", 1)) + //level, rooms := mapgens.DelaunayMstExtGen(gamemap.NewLevel("test", 1)) + //level, rooms := mapgens.DelaunayPureGen(gamemap.NewLevel("test", 1)) State.Level = level sidebarWidth := 0 @@ -103,7 +104,7 @@ func main() { items.Init(controller) - bp := items.Backpack{MaxMass:100, MaxBulk:100} + bp := items.Backpack{MaxMass: 100, MaxBulk: 100} //Set up Screen Manager screenMgr := types.NewScreenManager() @@ -117,7 +118,7 @@ func main() { //"[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, ). + true). SetBgColor("#ef1d494f"). SetFgColor("white"). SetItems([]interface{}{ @@ -125,12 +126,12 @@ func main() { "s or . - pass turn", "g or , - pick up item", "i - inventory", - "? - this screen", - "Ctrl+q - exit", - "f or F - fire weapon or throw item", - "z or Z - cast a spell", - "p - pray", - "Ctrl+p - message log", + "? - this screen", + "Ctrl+q - exit", + "f or F - fire weapon or throw item", + "z or Z - cast a spell", + "p - pray", + "Ctrl+p - message log", }).MakeList(), ) @@ -143,7 +144,7 @@ func main() { //"[color=yellow]Note[/color]: Many of these are not implemented yet", "", types.NewCenteredRect(mw.Rect, 70, 25), - true, ). + true). SetBgColor("#ef305c70"). SetFgColor("white"), } @@ -160,7 +161,7 @@ func main() { SetFgColor("white"), ) - screenMgr.SetScreenByName("title") + screenMgr.SetScreenByName("title") //fixme set up (load / generate) player - move to game / enter or title / exit player := controller.CreateEntity([]ecs.Component{}) @@ -184,11 +185,11 @@ func main() { Fg: types.PlainColorHolder{255, 55, 255, 222}, }, }) - controller.AddComponent(potion, rooms[0].Center) //implicit Coords - controller.AddComponent(potion, items.Carried{Mass:5, Bulk:3}) //fixme generate from blueprint! + controller.AddComponent(potion, rooms[0].Center) //implicit Coords + controller.AddComponent(potion, items.Carried{Mass: 5, Bulk: 3}) //fixme generate from blueprint! controller.AddComponent(potion, items.Usable{}) controller.AddComponent(potion, items.Consumable{}) - controller.AddComponent(potion, ecs.Named{Name:"first potion"}) + controller.AddComponent(potion, ecs.Named{Name: "first potion"}) potion2 := controller.CreateEntity([]ecs.Component{}) controller.AddComponent(potion2, types.Appearance{ @@ -198,10 +199,10 @@ func main() { }, }) controller.AddComponent(potion2, rooms[1].Center) //implicit Coords - controller.AddComponent(potion2, items.Carried{Mass:5, Bulk:3}) + controller.AddComponent(potion2, items.Carried{Mass: 5, Bulk: 3}) controller.AddComponent(potion2, items.Usable{}) controller.AddComponent(potion2, items.Consumable{}) - controller.AddComponent(potion2, ecs.Named{Name:"second potion"}) + controller.AddComponent(potion2, ecs.Named{Name: "second potion"}) //fixme end setting up items State.Player = player @@ -209,6 +210,13 @@ func main() { screenMgr.AddScreen("inventory", inv.MakeInverntory(player)) + gamelog.Log.SetMaxHeight(5) + gamelog.Log.Msg("2025-08-03T03:37:44+03:00 DBG Generating level of branch test depth 1") + gamelog.Log.Msg("2025-08-03T03:37:44+03:00 DBG adding") + gamelog.Log.Msg("2025-08-03T03:37:44+03:00 DBG adding") + gamelog.Log.Msg("2025-08-03T03:37:44+03:00 DBG adding") + gamelog.Log.Msg("TrOlolo") + gamelog.Log.Msg("[color=red]Game[/color] starting...") //but every call to bearlibterminal must be wrapped to closure and passed to mainfunc var exit = false @@ -224,7 +232,7 @@ func main() { // f() // break case <-State.Exit: - appctx.Logger().Warn().Msg("quitting NOW") + log.Warn().Msg("quitting NOW") exit = true break // не оставляйте default в бесконечном select {} - сожрет всё CPU @@ -235,7 +243,7 @@ func main() { } } - appctx.Logger().Info().Msg("pre-shutdown sequence") + log.Info().Msg("pre-shutdown sequence") } func setupLayers(mainwindow *mainwindow.MainWindow) { @@ -280,7 +288,7 @@ func decodeInput(ctx context.Context, baseLayer *mainwindow.Layer) { switch pressed { case "Ctrl+q": //fallthrough - //case "Escape": + //case "Escape": appctx.Logger().Info().Msg("exiting on quit command...") State.Exit <- struct{}{} appctx.Logger().Info().Msg("...done") @@ -288,7 +296,7 @@ func decodeInput(ctx context.Context, baseLayer *mainwindow.Layer) { return default: if pressed != "" { - waitForWCspam = false; + waitForWCspam = false State.Input <- pressed } } diff --git a/config.json b/config.json index 9023718..0e45e10 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,5 @@ { - "version": "v0.0.1.7-29-g4f18b6d", + "version": "v0.0.1.7-35-g2be7717", "title": "Alchemyst", "sizeX": 100, "sizeY": 47, diff --git a/engine/ecs/system.go b/engine/ecs/system.go index 773418f..ec0a722 100644 --- a/engine/ecs/system.go +++ b/engine/ecs/system.go @@ -3,6 +3,7 @@ package ecs // ECS system by jcerise, github.com/jcerise/gogue const TerrainRenderSystem = "levelrender" +const UiRenderSystem = "uirender" type System interface { Process() diff --git a/engine/gamelog/messages.go b/engine/gamelog/messages.go new file mode 100644 index 0000000..ebd2d55 --- /dev/null +++ b/engine/gamelog/messages.go @@ -0,0 +1,36 @@ +package gamelog + +import ( + "time" +) + +var Log = &GameLog{RedrawLogs: true} + +type GameLog struct { + Messages []Message + MaxHeight int + RedrawLogs bool +} + +type Message struct { + GameTS uint64 + RealTS time.Time + Message string +} + +func (gl *GameLog) GetMaxHeight() int { + return gl.MaxHeight +} + +func (gl *GameLog) SetMaxHeight(i int) { + gl.MaxHeight = i +} + +func (gl *GameLog) Msg(msg string) { + gl.Messages = append(gl.Messages, Message{ + GameTS: 0, //fixme + RealTS: time.Now(), + Message: msg, + }) + gl.RedrawLogs = true +} diff --git a/engine/gamemap/mapgens/delaunay_pure.go b/engine/gamemap/mapgens/delaunay_pure.go index af5c14c..19390ac 100644 --- a/engine/gamemap/mapgens/delaunay_pure.go +++ b/engine/gamemap/mapgens/delaunay_pure.go @@ -1,19 +1,18 @@ package mapgens import ( - "context" "lab.zaar.be/thefish/alchemyst-go/engine/gamemap" "lab.zaar.be/thefish/alchemyst-go/engine/types" "lab.zaar.be/thefish/alchemyst-go/util" "lab.zaar.be/thefish/alchemyst-go/util/delaunay" ) -func DelaunayPureGen(ctx context.Context, l *gamemap.Level) (*gamemap.Level, []gamemap.Room) { +func DelaunayPureGen(l *gamemap.Level) (*gamemap.Level, []gamemap.Room) { rng := util.NewRNG() //fill with walls - for i := 0; i < l.W; i ++ { + for i := 0; i < l.W; i++ { for j := 0; j < l.H; j++ { l.SetTileByXY(i, j, gamemap.NewWall()) } @@ -33,6 +32,3 @@ func DelaunayPureGen(ctx context.Context, l *gamemap.Level) (*gamemap.Level, []g return l, rooms } - - - diff --git a/engine/items/carried.go b/engine/items/carried.go index a190823..58e5731 100644 --- a/engine/items/carried.go +++ b/engine/items/carried.go @@ -3,6 +3,7 @@ package items import ( "fmt" "lab.zaar.be/thefish/alchemyst-go/engine/ecs" + "lab.zaar.be/thefish/alchemyst-go/engine/gamelog" "lab.zaar.be/thefish/alchemyst-go/engine/types" ) @@ -52,6 +53,7 @@ func (c Carried) Pickup(who, what ecs.Entity) error { //remove coords instead (does not exist on map anymore) Controller.RemoveComponent(what, ecs.CoordsComponent) bp.items = append(bp.items, what) + gamelog.Log.Msg(fmt.Sprintf("Picked up %s.", Controller.GetComponent(what, ecs.NamedComponent).(Named).GetName())) //fuck that, we need to update constantly Controller.UpdateComponent(who, ecs.BackpackComponent, bp) return nil diff --git a/engine/mob/movement/movement.go b/engine/mob/movement/movement.go index 836119e..40cf566 100644 --- a/engine/mob/movement/movement.go +++ b/engine/mob/movement/movement.go @@ -1,6 +1,7 @@ package movement import ( + "lab.zaar.be/thefish/alchemyst-go/effects" "lab.zaar.be/thefish/alchemyst-go/engine/ecs" "lab.zaar.be/thefish/alchemyst-go/engine/gamemap" "lab.zaar.be/thefish/alchemyst-go/engine/gamestate" @@ -10,7 +11,7 @@ import ( type Moveable struct { Controller *ecs.Controller - Level *gamemap.Level + Level *gamemap.Level } func (mov Moveable) Type() string { @@ -21,12 +22,12 @@ func (mov Moveable) Walk() { } -//fixme change it to WhatsOnTile +// fixme change it to WhatsOnTile func (mov Moveable) IsBlocked(c types.Coords) bool { if mov.Level.GetTile(c).BlocksPass { return true } - list := mov.Controller.GetEntitiesWithComponent(ecs.MobComponent) + list := mov.Controller.GetEntitiesWithComponent(ecs.MobComponent) for idx := range list { coords := mov.Controller.GetComponent(list[idx], ecs.CoordsComponent) if coords == nil { @@ -59,7 +60,7 @@ func Walk(entity ecs.Entity, state *gamestate.GameState, dx, dy int) { return } if !movable.(Moveable).IsBlocked(newCoords) || - controller.GetComponent(entity, "pass_wall") != nil { + controller.GetComponent(entity, effects.BuffPassWall) != nil { controller.UpdateComponent(state.Player, ecs.CoordsComponent, newCoords) } diff --git a/engine/screens/game.go b/engine/screens/game.go index b97268c..f730aa7 100644 --- a/engine/screens/game.go +++ b/engine/screens/game.go @@ -5,6 +5,7 @@ import ( "lab.zaar.be/thefish/alchemyst-go/engine/ecs/systems" "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/gamelog" "lab.zaar.be/thefish/alchemyst-go/engine/gamestate" "lab.zaar.be/thefish/alchemyst-go/engine/items" "lab.zaar.be/thefish/alchemyst-go/engine/mob/movement" @@ -24,11 +25,11 @@ type GameScreen struct { 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, + mw: mw, + state: state, + vp: viewPort, controller: controller, - scm: scm, + scm: scm, } //fixme move this to fov system @@ -38,6 +39,7 @@ func NewGameScreen(mw *mainwindow.MainWindow, state *gamestate.GameState, viewPo renderLevel := systems.NewLevelRenderSystem(state, ts.controller, viewPort, ts.mw.GetLayer("base"), ts.fov) go renderLevel.Listen() + ts.controller.AddSystem(renderLevel, 50) return ts } @@ -46,7 +48,7 @@ func (ts *GameScreen) UseEcs() bool { return true } func (ts *GameScreen) Enter() { ts.mw.GetLayer("overlay").ClearArea(0, ts.mw.H-3, 30, 3) ts.mw.GetLayer("overlay").WithColor("#77777777"). - Print(ts.mw.W - 17 , 1, "Press [color=white]?[/color] for help") + Print(ts.mw.W-17, 1, "Press [color=white]?[/color] for help") } func (ts *GameScreen) Exit() { //trs := ts.controller.GetSystem(ecs.LevelRenderSystem) @@ -55,7 +57,7 @@ func (ts *GameScreen) Exit() { //remove what we dont need } -//fixme kry names to action constants! +// fixme kry names to action constants! func (ts *GameScreen) HandleInput(input string) { //ts.state.Do(func(){ switch input { @@ -105,16 +107,16 @@ func (ts *GameScreen) HandleInput(input string) { err := items.Carried.Pickup(cc, ts.state.Player, carrieds[0]) if err != nil { // Message with error - //gameLog.Log.Error(err) + //gameLog.Msg.Error(err) //@fixme! appctx.Logger().Warn().Err(err) - break; + break } } //log picked up - //gameLog.Log.Message(err) - break; + //gameLog.Msg.Message(err) + break case "i": ts.scm.SetScreenByName("inventory") @@ -123,11 +125,21 @@ func (ts *GameScreen) HandleInput(input string) { 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, "█") + + } + if gamelog.Log.RedrawLogs { + logRect := types.NewRect(1, ts.mw.H-gamelog.Log.MaxHeight-1, ts.mw.W-1, gamelog.Log.MaxHeight) + ts.mw.GetLayer("overlay").ClearRect(logRect) + count := 0 + for i := len(gamelog.Log.Messages) - 1; i >= len(gamelog.Log.Messages)-gamelog.Log.MaxHeight; i-- { + //fmt.Println(gamelog.Log.Messages[i].Message) + ts.mw.GetLayer("overlay").PutStringInto(logRect, count-2, gamelog.Log.Messages[i].Message, 0) + count++ + } + gamelog.Log.RedrawLogs = false } - //}) } func (ts *GameScreen) Render() { - //ts.vp.Render(ts.state) - ts.controller.Process([]string{}) + ts.controller.Process(nil) } diff --git a/go.mod b/go.mod index eb4c5c0..c23e559 100644 --- a/go.mod +++ b/go.mod @@ -7,5 +7,5 @@ require ( github.com/rs/zerolog v1.15.0 github.com/shopspring/decimal v1.3.1 github.com/stretchr/testify v1.8.0 - lab.zaar.be/thefish/bearlibterminal.git v0.0.0-20191018101635-dd37bbc90d77 + lab.zaar.be/thefish/bearlibterminal v0.0.0-20250802054216-21f184e55ce3 ) diff --git a/util/wu/line.go b/util/wu/line.go index 47926ef..2173a3f 100644 --- a/util/wu/line.go +++ b/util/wu/line.go @@ -1,8 +1,9 @@ package wu import ( - "lab.zaar.be/thefish/alchemyst-go/engine/types" "math" + + "lab.zaar.be/thefish/alchemyst-go/engine/types" ) func ipart(x float64) float64 { @@ -21,7 +22,7 @@ func rfpart(x float64) float64 { return 1 - fpart(x) } -func (Layer types.Putable) WuLine(x1, y1, x2, y2 float64, w int) { +func WuLine(l types.Putable, x1, y1, x2, y2 float64, w int) { dx := x2 - x1 dy := y2 - y1 ax := dx @@ -40,11 +41,11 @@ func (Layer types.Putable) WuLine(x1, y1, x2, y2 float64, w int) { x2, y2 = y2, x2 dx, dy = dy, dx plot = func(x, y int, c float64) { - Layer.Put(y, x, uint8(255 * c)) + l.Put(y, x, uint8(255*c)) } } else { plot = func(x, y int, c float64) { - Layer.Put(x, y, uint8(255 * c)) + l.Put(x, y, uint8(255*c)) } } if x2 < x1 {