diff --git a/TODO b/TODO index 6d046bf..e23ff37 100644 --- a/TODO +++ b/TODO @@ -27,7 +27,7 @@ Assets and i18n: ECS & engine: - implement time queue (how to deal with closures?) (?) github.com/thefish/scheduleq - get rid od time.Now inside - move all rendering to systems - - try to move input handling to systems + + try to move input handling to systems Dungeon and branches: General: @@ -53,7 +53,7 @@ Combat: - mass - damage calculated from: kinetic energy is calculated by mass / speed / material density (p = mv) масса на скорость = кинетическая энергия - next you determine target and its subpart (accuracy appied) + next you determine target and its subpart (accuracy applied) next we calculate the area, on which kinetic energy is applied (determined by piercing, hacking, crushing damage profile) находим площадь next we calculate @@ -81,5 +81,17 @@ Combat: Quest engine: - look at parsers like URQL etc - - distorted Aschenputtel story / partisans / rapist prince / Grey Mountains + - distorted Aschenputtel story / partisans / rapist prince / Grey Mountains / No gold + + +No Gold in Gery Mountains / Kim Newman + +- Drakenfells castle //location +- Greteschele // char / rogue / charred zombie +- Yom Lamprecht // char / rogue / +- Tiley manor(?) // location / княжество +- Melissa d'Acu // char / small girl + + + diff --git a/cmd/game/main.go b/cmd/game/main.go index 9642f4a..ab82123 100644 --- a/cmd/game/main.go +++ b/cmd/game/main.go @@ -1,26 +1,26 @@ package main import ( - "context" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" - "lab.zaar.be/thefish/alchemyst-go/engine/ecs" - "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" - "lab.zaar.be/thefish/alchemyst-go/engine/items" - "lab.zaar.be/thefish/alchemyst-go/engine/mob" - "lab.zaar.be/thefish/alchemyst-go/engine/mob/movement" - "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" - "lab.zaar.be/thefish/alchemyst-go/util" - "lab.zaar.be/thefish/alchemyst-go/util/appctx" - blt "lab.zaar.be/thefish/bearlibterminal" - "os" - "runtime" - "time" + "context" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "lab.zaar.be/thefish/alchemyst-go/engine/ecs" + "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" + "lab.zaar.be/thefish/alchemyst-go/engine/items" + "lab.zaar.be/thefish/alchemyst-go/engine/mob" + "lab.zaar.be/thefish/alchemyst-go/engine/mob/movement" + "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" + "lab.zaar.be/thefish/alchemyst-go/util" + "lab.zaar.be/thefish/alchemyst-go/util/appctx" + blt "lab.zaar.be/thefish/bearlibterminal" + "os" + "runtime" + "time" ) var modifiers = []int{blt.TK_SHIFT, blt.TK_ALT, blt.TK_CONTROL} @@ -28,12 +28,12 @@ var modifiers = []int{blt.TK_SHIFT, blt.TK_ALT, blt.TK_CONTROL} // Рецепт чтобы убежать от [fatal] 'refresh' was not called from the main thread // https://github.com/golang/go/wiki/LockOSThread func init() { - runtime.LockOSThread() + runtime.LockOSThread() } //we can run logic in separate goroutines // -// go doSometing(State,...) +// go doSomething(State,...) // //and there we go like this: // func doSomething(State main.GameState, args...) { @@ -45,252 +45,254 @@ func init() { // } var State = gamestate.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), + 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() { - config := util.LoadConfig() - var logLevels = map[string]zerolog.Level{"debug": zerolog.DebugLevel, "info": zerolog.InfoLevel, "warn": zerolog.WarnLevel} - var logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}).Level(logLevels[config.Verbosity]) + config := util.LoadConfig() + var logLevels = map[string]zerolog.Level{"debug": zerolog.DebugLevel, "info": zerolog.InfoLevel, "warn": zerolog.WarnLevel} + var logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}).Level(logLevels[config.Verbosity]) - // set up context - mainCtx := appctx.NewClientContext(config, &logger) + // set up context + appctx.NewClientContext(config, &logger) + mainCtx := appctx.ClientState - //set up main window - mw := mainwindow.Init(mainCtx) - defer mw.Close() + //set up main window + mw := mainwindow.Init(mainCtx) + defer mw.Close() - setupLayers(mw) + setupLayers(mw) - //set up input decoder - go decodeInput(mainCtx, mw.GetLayer("base")) + //set up input decoder + go decodeInput(mainCtx, mw.GetLayer("base")) - //fixme set up (load / generate) level - move to game / enter or title / exit - //level, rooms := _default.DefaultGen(mainCtx, gamemap.NewLevel(mainCtx, "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)) - State.Level = level + //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)) + State.Level = level - sidebarWidth := 0 + sidebarWidth := 0 - //Set up viewport - vp := mainwindow.NewViewPort(sidebarWidth, 0, (mw.W - sidebarWidth), (mw.H - 0)) + //Set up viewport + vp := mainwindow.NewViewPort(sidebarWidth, 0, (mw.W - sidebarWidth), (mw.H - 0)) - //set up controller + //set up controller - controller := ecs.NewController(mainCtx) + controller := ecs.NewController() - controller.MapComponentClass(ecs.CoordsComponent, types.Coords{}) - controller.MapComponentClass(ecs.AppearanceComponent, types.Appearance{}) - controller.MapComponentClass(ecs.MobComponent, mob.Mob{}) - controller.MapComponentClass(ecs.MoveableComponent, movement.Moveable{}) - controller.MapComponentClass(ecs.CarriedComponent, movement.Moveable{}) - controller.MapComponentClass(ecs.UsableComponent, movement.Moveable{}) - controller.MapComponentClass(ecs.BackpackComponent, items.Backpack{}) + controller.MapComponentClass(ecs.CoordsComponent, types.Coords{}) + controller.MapComponentClass(ecs.AppearanceComponent, types.Appearance{}) + controller.MapComponentClass(ecs.MobComponent, mob.Mob{}) + controller.MapComponentClass(ecs.MoveableComponent, movement.Moveable{}) + controller.MapComponentClass(ecs.CarriedComponent, movement.Moveable{}) + controller.MapComponentClass(ecs.UsableComponent, movement.Moveable{}) + controller.MapComponentClass(ecs.BackpackComponent, items.Backpack{}) - moveable := movement.Moveable{ - Controller: controller, - Level: level, - } + moveable := movement.Moveable{ + Controller: controller, + Level: level, + } - items.Init(controller) + items.Init(controller) - bp := items.Backpack{MaxMass: 100, MaxBulk: 100} + bp := items.Backpack{MaxMass:100, MaxBulk:100} - //Set up Screen Manager - screenMgr := types.NewScreenManager(mainCtx) - screenMgr.AddScreen("title", screens.NewTitleScreen(mw, screenMgr)) - screenMgr.AddScreen("game", screens.NewGameScreen(mainCtx, 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, NumPad 12346789, 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", - }).MakeList(), - ) + //Set up Screen Manager + screenMgr := types.NewScreenManager() + 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, NumPad 12346789, arrow keys - move", + "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", + }).MakeList(), + ) - inv := screens.InventoryScreen{ - MenuScreen: screens.NewMenuScreen( - mw, - screenMgr, - "Inventory", - "Items in your backpack:", - //"[color=yellow]Note[/color]: Many of these are not implemented yet", - "", - types.NewCenteredRect(mw.Rect, 70, 25), - true). - SetBgColor("#ef305c70"). - SetFgColor("white"), - } + inv := screens.InventoryScreen{ + MenuScreen: screens.NewMenuScreen( + mw, + screenMgr, + "Inventory", + "Items in your backpack:", + //"[color=yellow]Note[/color]: Many of these are not implemented yet", + "", + types.NewCenteredRect(mw.Rect, 70, 25), + true, ). + SetBgColor("#ef305c70"). + SetFgColor("white"), + } - screenMgr.AddScreen("devmenu", screens.NewDevmenuScreen( - mainCtx, - mw, - controller, - screenMgr, - &State, - types.NewCenteredRect(mw.Rect, 70, 25), - true, - ).SetBgColor("#ef6d559d"). - SetFgColor("white"), - ) + screenMgr.AddScreen("devmenu", screens.NewDevmenuScreen( + mainCtx, + mw, + controller, + screenMgr, + &State, + types.NewCenteredRect(mw.Rect, 70, 25), + true, + ).SetBgColor("#ef6d559d"). + 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{}) + //fixme set up (load / generate) player - move to game / enter or title / exit + player := controller.CreateEntity([]ecs.Component{}) - controller.AddComponent(player, types.Appearance{ - Glyph: types.PlainGlyphHolder{"@"}, - ColorSet: types.TileColorSet{ - Fg: types.PlainColorHolder{255, 255, 255, 255}, - }, - }) + controller.AddComponent(player, types.Appearance{ + Glyph: types.PlainGlyphHolder{"@"}, + ColorSet: types.TileColorSet{ + Fg: types.PlainColorHolder{255, 255, 255, 255}, + }, + }) - controller.AddComponent(player, rooms[0].Center) //implicit Coords - controller.AddComponent(player, moveable) - controller.AddComponent(player, bp) + controller.AddComponent(player, rooms[0].Center) //implicit Coords + controller.AddComponent(player, moveable) + controller.AddComponent(player, bp) - //fixme adding items - potion := controller.CreateEntity([]ecs.Component{}) - controller.AddComponent(potion, types.Appearance{ - Glyph: types.PlainGlyphHolder{"!"}, - ColorSet: types.TileColorSet{ - Fg: types.PlainColorHolder{255, 55, 255, 222}, - }, - }) - controller.AddComponent(potion, rooms[0].Center) //implicit Coords - controller.AddComponent(potion, items.Carried{Mass: 5, Bulk: 3}) - controller.AddComponent(potion, items.Usable{}) - controller.AddComponent(potion, items.Consumable{}) - controller.AddComponent(potion, ecs.Named{Name: "first potion"}) + //fixme adding items + potion := controller.CreateEntity([]ecs.Component{}) + controller.AddComponent(potion, types.Appearance{ + Glyph: types.PlainGlyphHolder{"!"}, + ColorSet: types.TileColorSet{ + 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, items.Usable{}) + controller.AddComponent(potion, items.Consumable{}) + controller.AddComponent(potion, ecs.Named{Name:"first potion"}) - potion2 := controller.CreateEntity([]ecs.Component{}) - controller.AddComponent(potion2, types.Appearance{ - Glyph: types.PlainGlyphHolder{"!"}, - ColorSet: types.TileColorSet{ - Fg: types.PlainColorHolder{255, 222, 255, 55}, - }, - }) - controller.AddComponent(potion2, rooms[1].Center) //implicit Coords - 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"}) - //fixme end setting up items + potion2 := controller.CreateEntity([]ecs.Component{}) + controller.AddComponent(potion2, types.Appearance{ + Glyph: types.PlainGlyphHolder{"!"}, + ColorSet: types.TileColorSet{ + Fg: types.PlainColorHolder{255, 222, 255, 55}, + }, + }) + controller.AddComponent(potion2, rooms[1].Center) //implicit Coords + 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"}) + //fixme end setting up items - State.Player = player - State.Controller = controller + State.Player = player + State.Controller = controller - screenMgr.AddScreen("inventory", inv.MakeInverntory(player)) + screenMgr.AddScreen("inventory", inv.MakeInverntory(player)) - //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(): - break - case pressed := <-State.Input: - screenMgr.CurrentScreen.HandleInput(pressed) - break - //case f := <-State.mainfunc: - // f() - // break - case <-State.Exit: - appctx.Logger(mainCtx).Warn().Msg("quitting NOW") - exit = true - break - // не оставляйте default в бесконечном select {} - сожрет всё CPU - default: - screenMgr.CurrentScreen.Render() - blt.Layer(0) //return to base layer - blt.Refresh() - } + //but every call to bearlibterminal must be wrapped to closure and passed to mainfunc + var exit = false + for !exit { - } - appctx.Logger(mainCtx).Info().Msg("pre-shutdown sequence") + select { + case State.RawInput <- ui.ReadKeyCode(): + break + case pressed := <-State.Input: + screenMgr.CurrentScreen.HandleInput(pressed) + break + //case f := <-State.mainfunc: + // f() + // break + case <-State.Exit: + appctx.Logger().Warn().Msg("quitting NOW") + exit = true + break + // не оставляйте default в бесконечном select {} - сожрет всё CPU + default: + screenMgr.CurrentScreen.Render() + blt.Layer(0) //return to base layer + blt.Refresh() + } + + } + appctx.Logger().Info().Msg("pre-shutdown sequence") } func setupLayers(mainwindow *mainwindow.MainWindow) { - mainwindow.AddLayer("base", 0, "white") - mainwindow.AddLayer("overlay", 1, "white") - mainwindow.AddLayer("menubg", 2, "white") - mainwindow.AddLayer("menu", 3, "white") + mainwindow.AddLayer("base", 0, "white") + mainwindow.AddLayer("overlay", 1, "white") + mainwindow.AddLayer("menubg", 2, "white") + mainwindow.AddLayer("menu", 3, "white") } func decodeInput(ctx context.Context, baseLayer *mainwindow.Layer) { - var exit = false - var waitForWCspam = true - for !exit { - select { - case keycode := <-State.RawInput: - if keycode == blt.TK_NONE { - continue - } - if keycode == blt.TK_CLOSE && !waitForWCspam { - appctx.Logger(ctx).Warn().Msg("exiting on window close...") - State.Exit <- struct{}{} - appctx.Logger(ctx).Warn().Msg("...done") - return - } - var pressed = "" - var isModifier, _ = util.IntInSlice(keycode, modifiers) - if !isModifier { + var exit = false + var waitForWCspam = true + for !exit { + select { + case keycode := <-State.RawInput: + if keycode == blt.TK_NONE { + continue + } + if keycode == blt.TK_CLOSE && !waitForWCspam { + appctx.Logger().Warn().Msg("exiting on window close...") + State.Exit <- struct{}{} + appctx.Logger().Warn().Msg("...done") + return + } + var pressed = "" + var isModifier, _ = util.IntInSlice(keycode, modifiers) + if !isModifier { - pressed = ui.Scancodemap[keycode] + pressed = ui.Scancodemap[keycode] - if blt.Check(blt.TK_SHIFT) != 0 { - pressed = "Shift+" + pressed - } - if blt.Check(blt.TK_ALT) != 0 { - pressed = "Alt+" + pressed - } - if blt.Check(blt.TK_CONTROL) != 0 { - pressed = "Ctrl+" + pressed - } + if blt.Check(blt.TK_SHIFT) != 0 { + pressed = "Shift+" + pressed + } + if blt.Check(blt.TK_ALT) != 0 { + pressed = "Alt+" + pressed + } + if blt.Check(blt.TK_CONTROL) != 0 { + pressed = "Ctrl+" + pressed + } - //global hotkeys - switch pressed { - case "Ctrl+q": - //fallthrough - //case "Escape": - appctx.Logger(ctx).Info().Msg("exiting on quit command...") - State.Exit <- struct{}{} - appctx.Logger(ctx).Info().Msg("...done") - exit = true - return - default: - if pressed != "" { - waitForWCspam = false + //global hotkeys + switch pressed { + case "Ctrl+q": + //fallthrough + //case "Escape": + appctx.Logger().Info().Msg("exiting on quit command...") + State.Exit <- struct{}{} + appctx.Logger().Info().Msg("...done") + exit = true + return + default: + if pressed != "" { + waitForWCspam = false; State.Input <- pressed - } - } - } - } - } + } + } + } + } + } } diff --git a/config.json b/config.json index 8be09b4..1b77443 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,5 @@ { - "version": "v0.0.1.7", + "version": "v0.0.1.7-16-gbf13c9c", "title": "Alchemyst", "sizeX": 100, "sizeY": 47, diff --git a/delaunay_test.go b/delaunay_test.go index 094de35..28d9a15 100644 --- a/delaunay_test.go +++ b/delaunay_test.go @@ -25,7 +25,7 @@ func TestDelaunay(t *testing.T) { {types.Coords{10, 10}, types.Coords{30, 10}}, } - result := delaunay.GetMst(coords, 100, 100, 0) + result := delaunay.GetMst(coords, 100, 100, 100 ) for idx, _ := range result { if result[idx] != expected[idx] { diff --git a/engine/ecs/controller.go b/engine/ecs/controller.go index 68eeb8e..8383668 100644 --- a/engine/ecs/controller.go +++ b/engine/ecs/controller.go @@ -3,13 +3,11 @@ package ecs // ECS system by jcerise, github.com/jcerise/gogue import ( - "context" "lab.zaar.be/thefish/alchemyst-go/util/appctx" "sort" ) type Controller struct { - ctx context.Context systems map[string]System sortedSystems map[int][]System priorityKeys []int @@ -23,8 +21,8 @@ type Controller struct { } // NewController is a convenience/constructor method to properly initialize a new processor -func NewController(ctx context.Context) *Controller { - controller := Controller{ctx: ctx} +func NewController() *Controller { + controller := Controller{} controller.systems = make(map[string]System) controller.sortedSystems = make(map[int][]System) controller.priorityKeys = []int{} @@ -80,7 +78,7 @@ func (c *Controller) GetMappedComponentClass(componentName string) Component { return c.componentMap[componentName] } else { // TODO: Add better (read: actual) error handling here - appctx.Logger(c.ctx).Warn().Msgf("Component[%s] not registered on Controller.\n", componentName) + appctx.Logger().Warn().Msgf("Component[%s] not registered on Controller.\n", componentName) return nil } } @@ -140,15 +138,20 @@ func (c *Controller) GetEntities() map[Entity]map[string]Component { } // GetEntitiesWithComponent returns a list of all entities with a given component attached -// TODO: Allow for passing a list of components -func (c *Controller) GetEntitiesWithComponent(componentType string) []Entity { +func (c *Controller) GetEntitiesWithComponent(componentTypes... string) []Entity { entitiesWithComponent := make([]Entity, 0) for entity := range c.entities { - if c.HasComponent(entity, componentType) { + mustAddThis := true + for _, componentType := range componentTypes { + if !c.HasComponent(entity, componentType) { + mustAddThis = false + break + } + } + if mustAddThis { entitiesWithComponent = append(entitiesWithComponent, entity) } } - return entitiesWithComponent } @@ -208,7 +211,7 @@ func (c *Controller) AddSystem(system System, priority int) { c.sortedSystems[priority] = append(c.sortedSystems[priority], system) sort.Ints(c.priorityKeys) } else { - appctx.Logger(c.ctx).Warn().Msgf("A system of type %v was already added to the controller %v!", systemType, c) + appctx.Logger().Warn().Msgf("A system of type %v was already added to the controller %v!", systemType, c) } } diff --git a/engine/fov/precomputed_shade/precomputed_shade.go b/engine/fov/precomputed_shade/precomputed_shade.go index 4123033..1bf15fe 100644 --- a/engine/fov/precomputed_shade/precomputed_shade.go +++ b/engine/fov/precomputed_shade/precomputed_shade.go @@ -124,7 +124,7 @@ func (ps *precomputedShade) FindByCoords(c types.Coords) (int, *Cell, error) { func (ps *precomputedShade) IsInFov(coords types.Coords) bool { rc := ps.fromLevelCoords(coords) - if rc.X == 0 && rc.Y ==0 {return true} + if rc.X == 0 && rc.Y ==0 {return true} _, cell, err := ps.FindByCoords(rc) if err != nil { return false diff --git a/engine/gamemap/level.go b/engine/gamemap/level.go index 472fc67..8c82464 100644 --- a/engine/gamemap/level.go +++ b/engine/gamemap/level.go @@ -1,7 +1,6 @@ package gamemap import ( - "context" "lab.zaar.be/thefish/alchemyst-go/engine/ecs" "lab.zaar.be/thefish/alchemyst-go/engine/types" "lab.zaar.be/thefish/alchemyst-go/util/appctx" @@ -14,7 +13,6 @@ var mapHeight = 90 type Level struct { types.Rect - ctx context.Context Name string Branch string Depth int @@ -66,23 +64,22 @@ func (l *Level) MakePassByXY (x,y int, tile *Tile) { func (l *Level) Put (x, y int, tileFunc interface{}) { tile := tileFunc.(func() *Tile)() if tile == nil { - appctx.Logger(l.ctx).Fatal().Msgf("Got non-tile type to put into level: %v", tile) + appctx.Logger().Fatal().Msgf("Got non-tile type to put into level: %v", tile) } if l.InBounds(types.Coords{x, y}) { l.Tiles[y*l.W+x] = tile } } -func NewLevel(ctx context.Context, branch string, depth int) *Level { +func NewLevel(branch string, depth int) *Level { l := &Level{ - ctx: ctx, Name: branch + string(depth), Depth: depth, Rect: types.NewRect(0,0, mapWidth, mapHeight), } l.Tiles = make([]*Tile, l.W*l.H) - appctx.Logger(ctx).Debug().Msgf("Generating level of branch %s depth %d", branch, depth) + appctx.Logger().Debug().Msgf("Generating level of branch %s depth %d", branch, depth) return l } diff --git a/engine/gamemap/mapgens/common.go b/engine/gamemap/mapgens/common.go index 6410aca..c32e30d 100644 --- a/engine/gamemap/mapgens/common.go +++ b/engine/gamemap/mapgens/common.go @@ -1,7 +1,6 @@ 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" @@ -39,9 +38,9 @@ var fges = map[int]types.RectFill{ }, } -func GetRandomRoomList(ctx context.Context, rng *util.RNG, l *gamemap.Level, maxRooms, minRoomSize, maxRoomSize int, ) []gamemap.Room{ +func GetRandomRoomList(rng *util.RNG, l *gamemap.Level, maxRooms, minRoomSize, maxRoomSize int, ) []gamemap.Room{ rooms := make([]gamemap.Room, 0) - pfLoader := gamemap.NewPrefabLoader(ctx) + pfLoader := gamemap.NewPrefabLoader() pfRooms := pfLoader.PrefabRoomsList() var fillage types.RectFill @@ -102,11 +101,12 @@ func GetRandomRoomList(ctx context.Context, rng *util.RNG, l *gamemap.Level, max return rooms } -func BlitToLevel (ctx context.Context, l *gamemap.Level, rooms[]gamemap.Room) { +//fixme overlapping rooms +func BlitToLevel (l *gamemap.Level, rooms[]gamemap.Room) { for _, room := range rooms { - err := room.BlitToLevel(ctx, l) + err := room.BlitToLevel(l) if err != nil { - appctx.Logger(ctx).Err(err) + appctx.Logger().Err(err) } } } diff --git a/engine/gamemap/mapgens/default.go b/engine/gamemap/mapgens/default.go index 9845486..db6bbb0 100644 --- a/engine/gamemap/mapgens/default.go +++ b/engine/gamemap/mapgens/default.go @@ -3,10 +3,9 @@ package mapgens import ( "lab.zaar.be/thefish/alchemyst-go/engine/gamemap" "lab.zaar.be/thefish/alchemyst-go/util" - "lab.zaar.be/thefish/alchemyst-go/util/appctx" ) -func DefaultGen(ctx appctx.ClientCtx,l *gamemap.Level) (*gamemap.Level, []gamemap.Room) { +func DefaultGen(l *gamemap.Level) (*gamemap.Level, []gamemap.Room) { rng := util.NewRNG() @@ -17,9 +16,9 @@ func DefaultGen(ctx appctx.ClientCtx,l *gamemap.Level) (*gamemap.Level, []gamema } } - rooms := GetRandomRoomList(ctx, rng, l, maxrooms, minRoomSize, maxRoomSize) + rooms := GetRandomRoomList(rng, l, maxrooms, minRoomSize, maxRoomSize) - BlitToLevel(ctx, l, rooms) + BlitToLevel(l, rooms) for idx, room := range rooms { if idx > 0 { diff --git a/engine/gamemap/mapgens/delaunay_mst.go b/engine/gamemap/mapgens/delaunay_mst.go index 624687c..c6cceb8 100644 --- a/engine/gamemap/mapgens/delaunay_mst.go +++ b/engine/gamemap/mapgens/delaunay_mst.go @@ -18,10 +18,10 @@ func DelaunayMstGen(ctx context.Context, l *gamemap.Level) (*gamemap.Level, []ga l.SetTileByXY(i, j, gamemap.NewWall()) } } - rooms := GetRandomRoomList(ctx, rng, l, maxrooms, minRoomSize, maxRoomSize) + rooms := GetRandomRoomList(rng, l, maxrooms, minRoomSize, maxRoomSize) - BlitToLevel(ctx, l, rooms) + BlitToLevel(l, rooms) centers := make([]types.Coords, 0) for _, room := range rooms { diff --git a/engine/gamemap/mapgens/delaunay_mst_ext.go b/engine/gamemap/mapgens/delaunay_mst_ext.go index fd7fe0c..255d7eb 100644 --- a/engine/gamemap/mapgens/delaunay_mst_ext.go +++ b/engine/gamemap/mapgens/delaunay_mst_ext.go @@ -4,11 +4,10 @@ import ( "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/appctx" "lab.zaar.be/thefish/alchemyst-go/util/delaunay" ) -func DelaunayMstExtGen(ctx appctx.ClientCtx, l *gamemap.Level) (*gamemap.Level, []gamemap.Room) { +func DelaunayMstExtGen(l *gamemap.Level) (*gamemap.Level, []gamemap.Room) { rng := util.NewRNG() @@ -18,9 +17,9 @@ func DelaunayMstExtGen(ctx appctx.ClientCtx, l *gamemap.Level) (*gamemap.Level, l.SetTileByXY(i, j, gamemap.NewWall()) } } - rooms := GetRandomRoomList(ctx, rng, l, maxrooms, minRoomSize, maxRoomSize) + rooms := GetRandomRoomList(rng, l, maxrooms, minRoomSize, maxRoomSize) - BlitToLevel(ctx, l, rooms) + BlitToLevel(l, rooms) centers := make([]types.Coords, 0) for _, room := range rooms { diff --git a/engine/gamemap/mapgens/delaunay_pure.go b/engine/gamemap/mapgens/delaunay_pure.go index df1ee84..af5c14c 100644 --- a/engine/gamemap/mapgens/delaunay_pure.go +++ b/engine/gamemap/mapgens/delaunay_pure.go @@ -18,9 +18,9 @@ func DelaunayPureGen(ctx context.Context, l *gamemap.Level) (*gamemap.Level, []g l.SetTileByXY(i, j, gamemap.NewWall()) } } - rooms := GetRandomRoomList(ctx, rng, l, maxrooms, minRoomSize, maxRoomSize) + rooms := GetRandomRoomList(rng, l, maxrooms, minRoomSize, maxRoomSize) - BlitToLevel(ctx, l, rooms) + BlitToLevel(l, rooms) centers := make([]types.Coords, 0) for _, room := range rooms { diff --git a/engine/gamemap/prefab.go b/engine/gamemap/prefab.go index 4bdf750..fcdba80 100644 --- a/engine/gamemap/prefab.go +++ b/engine/gamemap/prefab.go @@ -1,7 +1,6 @@ package gamemap import ( - "context" "encoding/json" "io/ioutil" "lab.zaar.be/thefish/alchemyst-go/engine/items" @@ -42,12 +41,10 @@ func LoadPrefabFile(filename string) (*PrefabFile, error) { return instance, nil } -type PrefabLoader struct { - ctx context.Context -} +type PrefabLoader struct {} -func NewPrefabLoader(ctx context.Context) PrefabLoader { - return PrefabLoader{ctx: ctx} +func NewPrefabLoader() PrefabLoader { + return PrefabLoader{} } func (pfbl PrefabLoader) PrefabRoomsList() []Room { @@ -106,7 +103,7 @@ func (pfbl PrefabLoader) PrefabRoomsList() []Room { } else { f, ok = TileTypeMap[shortName] if (!ok) { - appctx.Logger(pfbl.ctx).Warn().Msgf("Unknown tile: %s", shortName) + appctx.Logger().Warn().Msgf("Unknown tile: %s", shortName) } } room.Geometry[i+ j*room.W] = f diff --git a/engine/gamemap/room.go b/engine/gamemap/room.go index 9111455..4c6edbe 100644 --- a/engine/gamemap/room.go +++ b/engine/gamemap/room.go @@ -1,7 +1,6 @@ package gamemap import ( - "context" "errors" "fmt" "lab.zaar.be/thefish/alchemyst-go/engine/items" @@ -33,7 +32,7 @@ func (r *Room) Put (x, y int, tileFunc interface{}) { } } -func (room *Room) BlitToLevel(ctx context.Context, l *Level) error { +func (room *Room) BlitToLevel(l *Level) error { //copy tiles like this: //https://stackoverflow.com/questions/21011023/copy-pointer-values-a-b-in-golang @@ -51,7 +50,7 @@ func (room *Room) BlitToLevel(ctx context.Context, l *Level) error { //check underlying tile if underlyingTile == nil || underlyingTile.Name != "Wall" { - appctx.Logger(ctx).Warn().Msg("Invalid blit!") + appctx.Logger().Warn().Msg("Invalid blit!") return invalidBlit } l.Put(mapCoords.X, mapCoords.Y, tileFunc) diff --git a/engine/items/backpack.go b/engine/items/backpack.go index c3e2a0f..bacc0f7 100644 --- a/engine/items/backpack.go +++ b/engine/items/backpack.go @@ -1,7 +1,13 @@ package items -import "lab.zaar.be/thefish/alchemyst-go/engine/ecs" - +import ( + "fmt" + "lab.zaar.be/thefish/alchemyst-go/engine/ecs" +) +var ( + ErrorInvTooHeavy = fmt.Errorf("too heavy") + ErrorInvTooBulky = fmt.Errorf("too bulky") +) type Backpack struct { MaxNumber int MaxBulk int @@ -13,7 +19,7 @@ func (b Backpack) Type() string { return ecs.BackpackComponent } -func (b *Backpack) HasFreeSpace(Bulk, Mass int) bool { +func (b *Backpack) HasFreeSpace(Bulk, Mass int) error { totalBulk, totalMass := 0, 0 for i, _ := range b.items { tmp := Controller.GetComponent(b.items[i], Carried{}.Type()).(Carried) @@ -22,14 +28,12 @@ func (b *Backpack) HasFreeSpace(Bulk, Mass int) bool { totalMass += carried.Mass } if totalMass >= b.MaxMass { - //fixme return message along - 'too heavy' - return false + return ErrorInvTooHeavy } - if totalBulk >= b.MaxMass { - //fixme return message along - 'doesnt fit to your backpack' - return false + if totalBulk >= b.MaxBulk { + return ErrorInvTooBulky } - return true + return nil } func (b *Backpack) GetItems() []ecs.Entity { diff --git a/engine/items/carried.go b/engine/items/carried.go index 9df2123..a190823 100644 --- a/engine/items/carried.go +++ b/engine/items/carried.go @@ -1,6 +1,7 @@ package items import ( + "fmt" "lab.zaar.be/thefish/alchemyst-go/engine/ecs" "lab.zaar.be/thefish/alchemyst-go/engine/types" ) @@ -19,32 +20,33 @@ func (c Carried) Type() string { return ecs.CarriedComponent } -func (c Carried) Pickup(who, what ecs.Entity) { +func (c Carried) Pickup(who, what ecs.Entity) error { // check if im lying on ground if !Controller.HasComponent(what, ecs.CoordsComponent) { - return + return fmt.Errorf("bug! item with no coords?!") } // something inexistent on map trying to pickup an item?! if !Controller.HasComponent(who, ecs.CoordsComponent) { //todo log error - investigate this situation - return + return fmt.Errorf("bug! actor with no coords?!") } - //check if who and what are on the same tile + //check if who and what are in adjacent tiles whoCoords := Controller.GetComponent(who, ecs.CoordsComponent).(types.Coords) whatCoords := Controller.GetComponent(what, ecs.CoordsComponent).(types.Coords) - if whoCoords != whatCoords { + if !whoCoords.IsAdjacentTo(&whatCoords) { //todo log error - something strange happened - return + return fmt.Errorf("bug! actor and item in inadjacent coords?!") } //does not have inventory? if !Controller.HasComponent(who, ecs.BackpackComponent) { //todo send message - you cant carry items - return + return fmt.Errorf("bug! actor cannot carry items") } bp := Controller.GetComponent(who, Backpack{}.Type()).(Backpack) - if !bp.HasFreeSpace(c.Bulk, c.Mass) { + err := bp.HasFreeSpace(c.Bulk, c.Mass) + if err != nil { //todo send message - does not fit to your inventory - return + return err } //do not remove appearance //remove coords instead (does not exist on map anymore) @@ -52,6 +54,7 @@ func (c Carried) Pickup(who, what ecs.Entity) { bp.items = append(bp.items, what) //fuck that, we need to update constantly Controller.UpdateComponent(who, ecs.BackpackComponent, bp) + return nil } func (c Carried) Drop(who, what ecs.Entity) { @@ -90,13 +93,14 @@ func (c *Carried) GetBulk(what ecs.Entity) int { } func FindCarriedUnder(who ecs.Entity) []ecs.Entity { - coords := Controller.GetComponent(who, ecs.CoordsComponent).(types.Coords) - carrieds := Controller.GetEntitiesWithComponent(ecs.CarriedComponent) + pickerCoords := Controller.GetComponent(who, ecs.CoordsComponent).(types.Coords) + // _И_ носимые _И_ имеющие координаты, т.е. где-то лежащие + carrieds := Controller.GetEntitiesWithComponent(ecs.CarriedComponent, ecs.CoordsComponent) result := make([]ecs.Entity, 0) - for _, ent := range carrieds { - car := Controller.GetComponent(ent, ecs.CoordsComponent) - if car == coords { - result = append(result, ent) + for _, carried := range carrieds { + carriedCoords := Controller.GetComponent(carried, ecs.CoordsComponent).(types.Coords) + if pickerCoords.IsAdjacentTo(&carriedCoords) { + result = append(result, carried) } } return result diff --git a/engine/screens/game.go b/engine/screens/game.go index d46393e..b97268c 100644 --- a/engine/screens/game.go +++ b/engine/screens/game.go @@ -1,7 +1,6 @@ package screens import ( - "context" "lab.zaar.be/thefish/alchemyst-go/engine/ecs" "lab.zaar.be/thefish/alchemyst-go/engine/ecs/systems" "lab.zaar.be/thefish/alchemyst-go/engine/fov" @@ -15,7 +14,6 @@ import ( ) type GameScreen struct { - ctx context.Context mw *mainwindow.MainWindow state *gamestate.GameState vp *mainwindow.ViewPort @@ -24,9 +22,8 @@ type GameScreen struct { fov fov.Fov } -func NewGameScreen(ctx context.Context, mw *mainwindow.MainWindow, state *gamestate.GameState, viewPort *mainwindow.ViewPort, controller *ecs.Controller, scm *types.ScreenManager) *GameScreen { +func NewGameScreen(mw *mainwindow.MainWindow, state *gamestate.GameState, viewPort *mainwindow.ViewPort, controller *ecs.Controller, scm *types.ScreenManager) *GameScreen { ts := &GameScreen{ - ctx: ctx, mw: mw, state: state, vp: viewPort, @@ -49,7 +46,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(1, ts.mw.H-2, "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) @@ -58,6 +55,7 @@ func (ts *GameScreen) Exit() { //remove what we dont need } +//fixme kry names to action constants! func (ts *GameScreen) HandleInput(input string) { //ts.state.Do(func(){ switch input { @@ -100,13 +98,22 @@ func (ts *GameScreen) HandleInput(input string) { } //do nothing //select if there is more than 1 if len(carrieds) > 1 { - appctx.Logger(ts.ctx).Warn().Msg("Passing item list to inventory not implemented yet") + appctx.Logger().Warn().Msg("Passing item list to inventory not implemented yet") } else { //call pickup in selected cc := items.Controller.GetComponent(carrieds[0], ecs.CarriedComponent).(items.Carried) - items.Carried.Pickup(cc, ts.state.Player, carrieds[0]) - } + err := items.Carried.Pickup(cc, ts.state.Player, carrieds[0]) + if err != nil { + // Message with error + //gameLog.Log.Error(err) + //@fixme! + appctx.Logger().Warn().Err(err) + break; + } + } + //log picked up + //gameLog.Log.Message(err) break; case "i": diff --git a/engine/screens/inventory.go b/engine/screens/inventory.go index b5e4685..e3eece5 100644 --- a/engine/screens/inventory.go +++ b/engine/screens/inventory.go @@ -63,6 +63,8 @@ func (is *InventoryScreen) Enter() { is.prepared.Prepare(is) } +//fixme key names to action constants! +//fixme unify scrolling controls! func (is *InventoryScreen) HandleInput(input string) { if strings.Contains(string(runeIndex), strings.Replace(input, "Shift+", "", -1)) { if strings.Contains("Shift+", input) { @@ -76,7 +78,7 @@ func (is *InventoryScreen) HandleInput(input string) { return } switch input { - case "Up": + case "Up", "k": is.cursor = is.cursor - 1 if is.cursor < 0 { is.cursor = 0 @@ -88,7 +90,7 @@ func (is *InventoryScreen) HandleInput(input string) { } } break - case "Down": + case "Down", "j": is.cursor = is.cursor + 1 if is.cursor >= len(is.prepared) { is.cursor = len(is.prepared) - 1 @@ -119,7 +121,8 @@ func (is *InventoryScreen) HandleInput(input string) { } break case "enter": - //select current under cursor + //show actions menu for item under cursor + //fixme implement break; case "Escape": fallthrough diff --git a/engine/screens/title.go b/engine/screens/title.go index 693c2cc..bfce193 100644 --- a/engine/screens/title.go +++ b/engine/screens/title.go @@ -19,6 +19,8 @@ func (ts *TitleScreen) UseEcs() bool { return false } func (ts *TitleScreen) Enter() { blt.Clear() } + +//fixme key names to action constants! func (ts *TitleScreen) HandleInput(input string) { switch input { case "n": @@ -55,5 +57,5 @@ 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 +Gogue (c) 2019 jcerise ` diff --git a/engine/types/screen.go b/engine/types/screen.go index 2ea0292..285b2a3 100644 --- a/engine/types/screen.go +++ b/engine/types/screen.go @@ -1,7 +1,6 @@ package types import ( - "context" "lab.zaar.be/thefish/alchemyst-go/util/appctx" ) @@ -14,16 +13,14 @@ type Screen interface { } type ScreenManager struct { - ctx context.Context Screens map[string]Screen CurrentScreen Screen PreviousScreen Screen } // NewScreenManager is a convenience/constructor method to properly initialize a new ScreenManager -func NewScreenManager(ctx context.Context) *ScreenManager { +func NewScreenManager() *ScreenManager { manager := ScreenManager{ - ctx:ctx, Screens: make(map[string]Screen), CurrentScreen: nil, } @@ -36,7 +33,7 @@ func (sm *ScreenManager) AddScreen(screenName string, screen Screen) { // A screen with the given name does not yet exist on the ScreenManager, go ahead and add it sm.Screens[screenName] = screen } else { - appctx.Logger(sm.ctx).Warn().Msgf("A screen with name %v was already added to the ScreenManager %v!", screenName, sm) + appctx.Logger().Warn().Msgf("A screen with name %v was already added to the ScreenManager %v!", screenName, sm) } } @@ -49,7 +46,7 @@ func (sm *ScreenManager) RemoveScreen(screenName string, screen Screen) { delete(sm.Screens, screenName) } else { // A screen with the given name does not exist - appctx.Logger(sm.ctx).Warn().Msgf("A screen with name %v was not found on ScreenManager %v!", screenName, sm) + appctx.Logger().Warn().Msgf("A screen with name %v was not found on ScreenManager %v!", screenName, sm) } } @@ -84,6 +81,6 @@ func (sm *ScreenManager) SetScreenByName(screenName string) { sm.CurrentScreen.Enter() } else { // A screen with the given name does not exist - appctx.Logger(sm.ctx).Warn().Msgf("A screen with name %v was not found on ScreenManager %v!", screenName, sm) + appctx.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/story/choose_your_pill.md b/story/choose_your_pill.md index e69de29..ff92a2b 100644 --- a/story/choose_your_pill.md +++ b/story/choose_your_pill.md @@ -0,0 +1,14 @@ +1 Почему Go? +--- + +потому что круто. (тут бла-бла про управление памятью, многопоточность, сборку и либы) + +2 Почему Libbearterminal? +--- + +Дьявол предложил расчесать манту. + +3 Почему нет звука? +--- + +Бля, да лень возиться. И ни к чему это тут. \ No newline at end of file diff --git a/story/go_game_dos_and_donts.md b/story/go_game_dos_and_donts.md index e98a51c..c601f9c 100644 --- a/story/go_game_dos_and_donts.md +++ b/story/go_game_dos_and_donts.md @@ -155,6 +155,9 @@ func (tr *TerrainRender) Render() { ... } ``` +TODO: троттлинг + + #### Каналы состояний и их Listеner-ы @@ -167,7 +170,7 @@ func (tr *TerrainRender) Render() { - reflect в main loop. Лишь **только** выкинув рефлкесию и больше ничего не делая - я снизил потребление CPU приложением **вдвое**. Это удобная штука, не спорю, но пользоваться ей надо при загрузке ресурсов, при сохранении/загрузке состояния -приложения - т.е. при разовых операциях. Как оказалось, она _очень_ дорогая по CPU. Кто пользоуется ей в main loop, ORM +приложения - т.е. при разовых операциях. Как оказалось, она _очень_ дорогая по CPU. Кто пользуется ей в main loop, ORM и прочих нагруженных местах - да будет предан анафеме. diff --git a/story/index.md b/story/index.md index f97494c..2bede94 100644 --- a/story/index.md +++ b/story/index.md @@ -4,6 +4,8 @@ RLG и Golang - некоторые полезные советы 1. [Установка и некоторые особенности работы](linux_go_blt_install_quickstart.md) связки BLT + Go на Linux 2. Что [стоит и НЕ стоит](go_game_dos_and_donts.md) делать с возможностями Go - +chans, +tickers, +throttling, -closures 3. [Система типов](./static_types_vs_ecs.md) - нативная или ECS? На самом деле и то, и то +4. Немножко конкретики: [предметы и обращение с ними](./item_objecttypes_and_blueprints.md). Как правильно готовить +предметы - чтобы потом не было мучительно больно. Дополнения --- diff --git a/story/item_objecttypes_and_blueprints.md b/story/item_objecttypes_and_blueprints.md new file mode 100644 index 0000000..455277a --- /dev/null +++ b/story/item_objecttypes_and_blueprints.md @@ -0,0 +1,16 @@ +Blueprints, паттерн Object Type и сериализация +== + +Посмотрите внимательно вот это видео, [чувак дело говорит](https://www.youtube.com/watch?v=JxI3Eu5DPwE). + + +Итого: + + - Всё что может делать предмет - в типы - или компоненты, если решились на ECS. + - Суперкласс/архетип для предмета + - Всё данные предмета - в человекочитаемый формат, json например + - Код для сериализации данных в экземпляр а памяти и обратно - **тщательно тестируем**! (TODO: примеры смешных багов) + - Названия типов - тоже в json, ни грамма данных врагу (т.е. коду). Позволит быстро менять/модифицировать игру чуть ли + не текстовым редактором. + + И кстати. Чертежи не только на предметы работают, но об этом с следующей главе. \ No newline at end of file diff --git a/story/makefile_and_crosscompiling.md b/story/makefile_and_crosscompiling.md index 72cd100..438ea0c 100644 --- a/story/makefile_and_crosscompiling.md +++ b/story/makefile_and_crosscompiling.md @@ -5,7 +5,7 @@ Почему это важно --- -Про сборку под разные ОС я даже уюеждать не буду - аудитория рогаликов мало того что крохотная, так еще и сильно +Про сборку под разные ОС я даже убеждать не буду - аудитория рогаликов мало того что крохотная, так еще и сильно сегментирована по осям. Go почти бесплатно дает вам возможность сборки под все мажорные оси, пользуйтесь этим - и потенциально в разы больше народа ознакомится с вашим творением. diff --git a/story/static_types_vs_ecs.md b/story/static_types_vs_ecs.md index cd94f5a..0420094 100644 --- a/story/static_types_vs_ecs.md +++ b/story/static_types_vs_ecs.md @@ -1,4 +1,5 @@ -Система типов в Go +Система типов и Go +--- Плюсы использования нативной системы типов diff --git a/ui/mainwindow/mainwindow.go b/ui/mainwindow/mainwindow.go index ac0a3f2..8772ebe 100644 --- a/ui/mainwindow/mainwindow.go +++ b/ui/mainwindow/mainwindow.go @@ -15,7 +15,7 @@ type MainWindow struct { } func Init(ctx context.Context) *MainWindow { - appctx.Logger(ctx).Info().Msgf("Opening main window...") + appctx.Logger().Info().Msgf("Opening main window...") mw := MainWindow{ctx: ctx, layers: make(map[string]types.Renderable, 0)} mw.Open() return &mw @@ -33,7 +33,7 @@ func (mw *MainWindow) GetLayer(name string) *Layer { if layer, ok := mw.layers[name]; ok { return layer.(*Layer) } - appctx.Logger(mw.ctx).Fatal().Msgf("No layer with such name %s", name) + appctx.Logger().Fatal().Msgf("No layer with such name %s", name) return nil } @@ -58,7 +58,7 @@ func (mw *MainWindow) Open() { } func (mw *MainWindow) Close() { - appctx.Logger(mw.ctx).Info().Msg("Closing main window...") + appctx.Logger().Info().Msg("Closing main window...") blt.Close() } diff --git a/util/appctx/context.go b/util/appctx/context.go index f51bcda..b3c3c46 100644 --- a/util/appctx/context.go +++ b/util/appctx/context.go @@ -12,15 +12,17 @@ const ( loggerKey = "logger" ) -type ClientCtx struct { +type clientCtx struct { context.Context } -func NewClientContext(config *util.Config, logger *zerolog.Logger) ClientCtx { +var ClientState clientCtx + +func NewClientContext(config *util.Config, logger *zerolog.Logger) { ctx := context.Context(context.TODO()) ctx = context.WithValue(ctx, configKey, config) ctx = context.WithValue(ctx, loggerKey, logger) - return ClientCtx{ ctx} + ClientState = clientCtx{ctx} } func Config(c context.Context) *util.Config { @@ -31,7 +33,11 @@ func Config(c context.Context) *util.Config { return cfg } -func Logger(c context.Context) *zerolog.Logger { +func Logger() *zerolog.Logger { + return getLogger(ClientState.Context) +} + +func getLogger(c context.Context) *zerolog.Logger { logger, ok := c.Value(loggerKey).(*zerolog.Logger) if !ok { panic(fmt.Errorf("no access to logger from context")) diff --git a/util/wu/line.go b/util/wu/line.go new file mode 100644 index 0000000..47926ef --- /dev/null +++ b/util/wu/line.go @@ -0,0 +1,78 @@ +package wu + +import ( + "lab.zaar.be/thefish/alchemyst-go/engine/types" + "math" +) + +func ipart(x float64) float64 { + return math.Floor(x) +} + +func round(x float64) float64 { + return ipart(x + .5) +} + +func fpart(x float64) float64 { + return x - ipart(x) +} + +func rfpart(x float64) float64 { + return 1 - fpart(x) +} + +func (Layer types.Putable) WuLine(x1, y1, x2, y2 float64, w int) { + dx := x2 - x1 + dy := y2 - y1 + ax := dx + if ax < 0 { + ax = -ax + } + ay := dy + if ay < 0 { + ay = -ay + } + + var plot func(int, int, float64) + + if ax < ay { + x1, y1 = y1, x1 + x2, y2 = y2, x2 + dx, dy = dy, dx + plot = func(x, y int, c float64) { + Layer.Put(y, x, uint8(255 * c)) + } + } else { + plot = func(x, y int, c float64) { + Layer.Put(x, y, uint8(255 * c)) + } + } + if x2 < x1 { + x1, x2 = x2, x1 + y1, y2 = y2, y1 + } + gradient := dy / dx + + xend := round(x1) + yend := y1 + gradient*(xend-x1) + xgap := rfpart(x1 + .5) + xpxl1 := int(xend) + ypxl1 := int(ipart(yend)) + plot(xpxl1, ypxl1, rfpart(yend)*xgap) + plot(xpxl1, ypxl1+1, fpart(yend)*xgap) + intery := yend + gradient + + xend = round(x2) + yend = y2 + gradient*(xend-x2) + xgap = fpart(x2 + 0.5) + xpxl2 := int(xend) + ypxl2 := int(ipart(yend)) + plot(xpxl2, ypxl2, rfpart(yend)*xgap) + plot(xpxl2, ypxl2+1, fpart(yend)*xgap) + + for x := xpxl1 + 1; x <= xpxl2-1; x++ { + plot(x, int(ipart(intery)), rfpart(intery)) + plot(x, int(ipart(intery))+1, fpart(intery)) + intery = intery + gradient + } +}