Merge remote-tracking branch 'origin/master'
# Conflicts: # cmd/game/main.go # delaunay_test.go # engine/screens/devmenu.go # go.mod
This commit is contained in:
commit
3886a05ce2
18
TODO
18
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
|
||||
|
||||
|
||||
|
||||
|
||||
|
460
cmd/game/main.go
460
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "v0.0.1.7",
|
||||
"version": "v0.0.1.7-16-gbf13c9c",
|
||||
"title": "Alchemyst",
|
||||
"sizeX": 100,
|
||||
"sizeY": 47,
|
||||
|
@ -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] {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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":
|
||||
|
@ -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
|
||||
|
@ -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 <humpolec@gmail.com>
|
||||
BearLibTerminal (c) Cfyz 2009-2019 <http://foo.wyrd.name/en:bearlibterminal>
|
||||
Gogue (c) jcerise
|
||||
Gogue (c) 2019 jcerise
|
||||
`
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
1 Почему Go?
|
||||
---
|
||||
|
||||
потому что круто. (тут бла-бла про управление памятью, многопоточность, сборку и либы)
|
||||
|
||||
2 Почему Libbearterminal?
|
||||
---
|
||||
|
||||
Дьявол предложил расчесать манту.
|
||||
|
||||
3 Почему нет звука?
|
||||
---
|
||||
|
||||
Бля, да лень возиться. И ни к чему это тут.
|
@ -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
|
||||
и прочих нагруженных местах - да будет предан анафеме.
|
||||
|
||||
|
||||
|
@ -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). Как правильно готовить
|
||||
предметы - чтобы потом не было мучительно больно.
|
||||
|
||||
Дополнения
|
||||
---
|
||||
|
16
story/item_objecttypes_and_blueprints.md
Normal file
16
story/item_objecttypes_and_blueprints.md
Normal file
@ -0,0 +1,16 @@
|
||||
Blueprints, паттерн Object Type и сериализация
|
||||
==
|
||||
|
||||
Посмотрите внимательно вот это видео, [чувак дело говорит](https://www.youtube.com/watch?v=JxI3Eu5DPwE).
|
||||
|
||||
|
||||
Итого:
|
||||
|
||||
- Всё что может делать предмет - в типы - или компоненты, если решились на ECS.
|
||||
- Суперкласс/архетип для предмета
|
||||
- Всё данные предмета - в человекочитаемый формат, json например
|
||||
- Код для сериализации данных в экземпляр а памяти и обратно - **тщательно тестируем**! (TODO: примеры смешных багов)
|
||||
- Названия типов - тоже в json, ни грамма данных врагу (т.е. коду). Позволит быстро менять/модифицировать игру чуть ли
|
||||
не текстовым редактором.
|
||||
|
||||
И кстати. Чертежи не только на предметы работают, но об этом с следующей главе.
|
@ -5,7 +5,7 @@
|
||||
|
||||
Почему это важно
|
||||
---
|
||||
Про сборку под разные ОС я даже уюеждать не буду - аудитория рогаликов мало того что крохотная, так еще и сильно
|
||||
Про сборку под разные ОС я даже убеждать не буду - аудитория рогаликов мало того что крохотная, так еще и сильно
|
||||
сегментирована по осям. Go почти бесплатно дает вам возможность сборки под все мажорные оси, пользуйтесь этим - и
|
||||
потенциально в разы больше народа ознакомится с вашим творением.
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
Система типов в Go
|
||||
Система типов и 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()
|
||||
}
|
||||
|
||||
|
@ -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"))
|
||||
|
78
util/wu/line.go
Normal file
78
util/wu/line.go
Normal file
@ -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
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user