Compare commits

..

No commits in common. "4913b0a567041081d4f9a290509f1f00bde28262" and "20ba03c758feed14f3c9cf5c5bb095bbffa0d491" have entirely different histories.

53 changed files with 679 additions and 1312 deletions

1
.envrc
View File

@ -1 +0,0 @@
GOPROXY="direct"

18
TODO
View File

@ -27,7 +27,7 @@ Assets and i18n:
ECS & engine: ECS & engine:
- implement time queue (how to deal with closures?) (?) github.com/thefish/scheduleq - get rid od time.Now inside - implement time queue (how to deal with closures?) (?) github.com/thefish/scheduleq - get rid od time.Now inside
- move all rendering to systems - move all rendering to systems
+ try to move input handling to systems - try to move input handling to systems
Dungeon and branches: Dungeon and branches:
General: General:
@ -53,7 +53,7 @@ Combat:
- mass - mass
- damage calculated from: - damage calculated from:
kinetic energy is calculated by mass / speed / material density (p = mv) масса на скорость = кинетическая энергия kinetic energy is calculated by mass / speed / material density (p = mv) масса на скорость = кинетическая энергия
next you determine target and its subpart (accuracy applied) next you determine target and its subpart (accuracy appied)
next we calculate the area, on which kinetic energy is applied (determined by piercing, hacking, crushing damage profile) находим площадь next we calculate the area, on which kinetic energy is applied (determined by piercing, hacking, crushing damage profile) находим площадь
next we calculate next we calculate
@ -81,17 +81,5 @@ Combat:
Quest engine: Quest engine:
- look at parsers like URQL etc - look at parsers like URQL etc
- distorted Aschenputtel story / partisans / rapist prince / Grey Mountains / No gold - distorted Aschenputtel story / partisans / rapist prince / Grey Mountains
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

View File

@ -1,26 +0,0 @@
{
"material_flags": [
{
"metal": {
"conducts_elictricity": true,
"blocks_liquid": true,
"acid_resistant": true,
"blocks_gas": true,
"flammable": false,
"conducts_heat": true,
"radiates": true
}
},
{
"wood": {
"conducts_elictricity": false,
"blocks_liquid": true,
"acid_resistant": false,
"blocks_gas": true,
"flammable": true,
"conducts_heat": false,
"radiates": false
}
}
]
}

View File

@ -1,28 +0,0 @@
{
"materials": [
{
"steel": {
"name": "steel",
"material_flags": {
"$ref": "#/material_flags/metal"
},
"density": "7800",
"fracture_toughness": "30",
"melting_point": "1400",
"boiling_point": "3200"
}
},
{
"iron": {
"name": "iron",
"material_flags": {
"$ref": "#/material_flags/metal"
},
"density": "7800",
"fracture_toughness": "12",
"melting_point": "1400",
"boiling_point": "3200"
}
}
]
}

View File

@ -1,15 +0,0 @@
{
"material_flags": [
{
"acme-fiber": {
"conducts_elictricity": false,
"blocks_liquid": true,
"acid_resistant": true,
"blocks_gas": true,
"flammable": false,
"conducts_heat": false,
"radiates": false
}
}
]
}

View File

@ -1,16 +0,0 @@
{
"materials": [
{
"oakwood": {
"name": "testoakwood",
"material_flags": {
"$ref": "#/material_flags/wood"
},
"density": "700",
"fracture_toughness": "4.5",
"melting_point": "600",
"boiling_point": null
}
}
]
}

View File

@ -1,26 +1,26 @@
package main package main
import ( import (
"context" "context"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"lab.zaar.be/thefish/alchemyst-go/engine/ecs" "lab.zaar.be/thefish/alchemyst-go/engine/ecs"
"lab.zaar.be/thefish/alchemyst-go/engine/gamemap" "lab.zaar.be/thefish/alchemyst-go/engine/gamemap"
"lab.zaar.be/thefish/alchemyst-go/engine/gamemap/mapgens" "lab.zaar.be/thefish/alchemyst-go/engine/gamemap/mapgens"
"lab.zaar.be/thefish/alchemyst-go/engine/gamestate" "lab.zaar.be/thefish/alchemyst-go/engine/gamestate"
"lab.zaar.be/thefish/alchemyst-go/engine/items" "lab.zaar.be/thefish/alchemyst-go/engine/items"
"lab.zaar.be/thefish/alchemyst-go/engine/mob" "lab.zaar.be/thefish/alchemyst-go/engine/mob"
"lab.zaar.be/thefish/alchemyst-go/engine/mob/movement" "lab.zaar.be/thefish/alchemyst-go/engine/mob/movement"
"lab.zaar.be/thefish/alchemyst-go/engine/screens" "lab.zaar.be/thefish/alchemyst-go/engine/screens"
"lab.zaar.be/thefish/alchemyst-go/engine/types" "lab.zaar.be/thefish/alchemyst-go/engine/types"
"lab.zaar.be/thefish/alchemyst-go/ui" "lab.zaar.be/thefish/alchemyst-go/ui"
"lab.zaar.be/thefish/alchemyst-go/ui/mainwindow" "lab.zaar.be/thefish/alchemyst-go/ui/mainwindow"
"lab.zaar.be/thefish/alchemyst-go/util" "lab.zaar.be/thefish/alchemyst-go/util"
"lab.zaar.be/thefish/alchemyst-go/util/appctx" "lab.zaar.be/thefish/alchemyst-go/util/appctx"
blt "lab.zaar.be/thefish/bearlibterminal" blt "lab.zaar.be/thefish/bearlibterminal"
"os" "os"
"runtime" "runtime"
"time" "time"
) )
var modifiers = []int{blt.TK_SHIFT, blt.TK_ALT, blt.TK_CONTROL} 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 // Рецепт чтобы убежать от [fatal] 'refresh' was not called from the main thread
// https://github.com/golang/go/wiki/LockOSThread // https://github.com/golang/go/wiki/LockOSThread
func init() { func init() {
runtime.LockOSThread() runtime.LockOSThread()
} }
//we can run logic in separate goroutines //we can run logic in separate goroutines
// //
// go doSomething(State,...) // go doSometing(State,...)
// //
//and there we go like this: //and there we go like this:
// func doSomething(State main.GameState, args...) { // func doSomething(State main.GameState, args...) {
@ -45,254 +45,252 @@ func init() {
// } // }
var State = gamestate.GameState{ var State = gamestate.GameState{
Mainfunc: make(chan func()), Mainfunc: make(chan func()),
Exit: make(chan struct{}, 1), Exit: make(chan struct{}, 1),
Input: make(chan string, 1), Input: make(chan string, 1),
RawInput: make(chan int, 1), RawInput: make(chan int, 1),
FovRecompute: make(chan struct{}, 1), FovRecompute: make(chan struct{}, 1),
Redraw: make(chan struct{}, 1), Redraw: make(chan struct{}, 1),
} }
func main() { func main() {
config := util.LoadConfig() config := util.LoadConfig()
var logLevels = map[string]zerolog.Level{"debug": zerolog.DebugLevel, "info": zerolog.InfoLevel, "warn": zerolog.WarnLevel} 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]) var logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}).Level(logLevels[config.Verbosity])
// set up context // set up context
appctx.NewClientContext(config, &logger) mainCtx := appctx.NewClientContext(config, &logger)
mainCtx := appctx.ClientState
//set up main window //set up main window
mw := mainwindow.Init(mainCtx) mw := mainwindow.Init(mainCtx)
defer mw.Close() defer mw.Close()
setupLayers(mw) setupLayers(mw)
//set up input decoder //set up input decoder
go decodeInput(mainCtx, mw.GetLayer("base")) go decodeInput(mainCtx, mw.GetLayer("base"))
//fixme set up (load / generate) level - move to game / enter or title / exit //fixme set up (load / generate) level - move to game / enter or title / exit
level, rooms := mapgens.DefaultGen(gamemap.NewLevel("test", 1)) //level, rooms := _default.DefaultGen(mainCtx, gamemap.NewLevel(mainCtx, "test", 1))
//level, rooms := mapgens.DelaunayMstGen(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.DelaunayMstExtGen(mainCtx, gamemap.NewLevel(mainCtx, "test", 1))
//level, rooms := mapgens.DelaunayPureGen(mainCtx, gamemap.NewLevel(mainCtx, "test", 1)) //level, rooms := mapgens.DelaunayPureGen(mainCtx, gamemap.NewLevel(mainCtx, "test", 1))
State.Level = level State.Level = level
sidebarWidth := 0 sidebarWidth := 0
//Set up viewport //Set up viewport
vp := mainwindow.NewViewPort(sidebarWidth, 0, (mw.W - sidebarWidth), (mw.H - 0)) vp := mainwindow.NewViewPort(sidebarWidth, 0, (mw.W - sidebarWidth), (mw.H - 0))
//set up controller //set up controller
controller := ecs.NewController() controller := ecs.NewController(mainCtx)
controller.MapComponentClass(ecs.CoordsComponent, types.Coords{}) controller.MapComponentClass(ecs.CoordsComponent, types.Coords{})
controller.MapComponentClass(ecs.AppearanceComponent, types.Appearance{}) controller.MapComponentClass(ecs.AppearanceComponent, types.Appearance{})
controller.MapComponentClass(ecs.MobComponent, mob.Mob{}) controller.MapComponentClass(ecs.MobComponent, mob.Mob{})
controller.MapComponentClass(ecs.MoveableComponent, movement.Moveable{}) controller.MapComponentClass(ecs.MoveableComponent, movement.Moveable{})
controller.MapComponentClass(ecs.CarriedComponent, movement.Moveable{}) controller.MapComponentClass(ecs.CarriedComponent, movement.Moveable{})
controller.MapComponentClass(ecs.UsableComponent, movement.Moveable{}) controller.MapComponentClass(ecs.UsableComponent, movement.Moveable{})
controller.MapComponentClass(ecs.BackpackComponent, items.Backpack{}) controller.MapComponentClass(ecs.BackpackComponent, items.Backpack{})
moveable := movement.Moveable{ moveable := movement.Moveable{
Controller: controller, Controller: controller,
Level: level, 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 //Set up Screen Manager
screenMgr := types.NewScreenManager() screenMgr := types.NewScreenManager(mainCtx)
screenMgr.AddScreen("title", screens.NewTitleScreen(mw, screenMgr)) screenMgr.AddScreen("title", screens.NewTitleScreen(mw, screenMgr))
screenMgr.AddScreen("game", screens.NewGameScreen(mw, &State, vp, controller, screenMgr)) screenMgr.AddScreen("game", screens.NewGameScreen(mainCtx, mw, &State, vp, controller, screenMgr))
screenMgr.AddScreen("help", screens.NewMenuScreen( screenMgr.AddScreen("help", screens.NewMenuScreen(
mw, mw,
screenMgr, screenMgr,
"Help", "Help",
"Keybindings:", "Keybindings:",
//"[color=yellow]Note[/color]: Many of these are not implemented yet", //"[color=yellow]Note[/color]: Many of these are not implemented yet",
"[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), types.NewCenteredRect(mw.Rect, 50, 15),
true, ). true).
SetBgColor("#ef1d494f"). SetBgColor("#ef1d494f").
SetFgColor("white"). SetFgColor("white").
SetItems([]interface{}{ SetItems([]interface{}{
"hjklyubn, NumPad 12346789, arrow keys - move", "hjklyubn, NumPad 12346789, arrow keys - move",
"s or . - pass turn", "s or . - pass turn",
"g or , - pick up item", "g or , - pick up item",
"i - inventory", "i - inventory",
"? - this screen", "? - this screen",
"Ctrl+q - exit", "Ctrl+q - exit",
"f or F - fire weapon or throw item", "f or F - fire or throw weapon",
"z or Z - cast a spell", "z or Z - cast a spell",
"p - pray", "p - pray",
"Ctrl+p - message log", "Ctrl+p - message log",
}).MakeList(), }).MakeList(),
) )
inv := screens.InventoryScreen{ inv := screens.InventoryScreen{
MenuScreen: screens.NewMenuScreen( MenuScreen: screens.NewMenuScreen(
mw, mw,
screenMgr, screenMgr,
"Inventory", "Inventory",
"Items in your backpack:", "Items in your backpack:",
//"[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, 70, 25), types.NewCenteredRect(mw.Rect, 70, 25),
true, ). true).
SetBgColor("#ef305c70"). SetBgColor("#ef305c70").
SetFgColor("white"), SetFgColor("white"),
} }
screenMgr.AddScreen("devmenu", screens.NewDevmenuScreen( screenMgr.AddScreen("devmenu", screens.NewDevmenuScreen(
mainCtx, mainCtx,
mw, mw,
controller, controller,
screenMgr, screenMgr,
&State, &State,
types.NewCenteredRect(mw.Rect, 70, 25), types.NewCenteredRect(mw.Rect, 70, 25),
true, true,
).SetBgColor("#ef6d559d"). ).SetBgColor("#ef6d559d").
SetFgColor("white"), SetFgColor("white"),
) )
screenMgr.SetScreenByName("title") screenMgr.SetScreenByName("title")
//fixme set up (load / generate) player - move to game / enter or title / exit //fixme set up (load / generate) player - move to game / enter or title / exit
player := controller.CreateEntity([]ecs.Component{}) player := controller.CreateEntity([]ecs.Component{})
controller.AddComponent(player, types.Appearance{ controller.AddComponent(player, types.Appearance{
Glyph: types.PlainGlyphHolder{"@"}, Glyph: types.PlainGlyphHolder{"@"},
ColorSet: types.TileColorSet{ ColorSet: types.TileColorSet{
Fg: types.PlainColorHolder{255, 255, 255, 255}, Fg: types.PlainColorHolder{255, 255, 255, 255},
}, },
}) })
controller.AddComponent(player, rooms[0].Center) //implicit Coords controller.AddComponent(player, rooms[0].Center) //implicit Coords
controller.AddComponent(player, moveable) controller.AddComponent(player, moveable)
controller.AddComponent(player, bp) controller.AddComponent(player, bp)
//fixme adding items //fixme adding items
potion := controller.CreateEntity([]ecs.Component{}) potion := controller.CreateEntity([]ecs.Component{})
controller.AddComponent(potion, types.Appearance{ controller.AddComponent(potion, types.Appearance{
Glyph: types.PlainGlyphHolder{"!"}, Glyph: types.PlainGlyphHolder{"!"},
ColorSet: types.TileColorSet{ ColorSet: types.TileColorSet{
Fg: types.PlainColorHolder{255, 55, 255, 222}, Fg: types.PlainColorHolder{255, 55, 255, 222},
}, },
}) })
controller.AddComponent(potion, rooms[0].Center) //implicit Coords 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.Carried{Mass: 5, Bulk: 3})
controller.AddComponent(potion, items.Usable{}) controller.AddComponent(potion, items.Usable{})
controller.AddComponent(potion, items.Consumable{}) controller.AddComponent(potion, items.Consumable{})
controller.AddComponent(potion, ecs.Named{Name:"first potion"}) controller.AddComponent(potion, ecs.Named{Name: "first potion"})
potion2 := controller.CreateEntity([]ecs.Component{}) potion2 := controller.CreateEntity([]ecs.Component{})
controller.AddComponent(potion2, types.Appearance{ controller.AddComponent(potion2, types.Appearance{
Glyph: types.PlainGlyphHolder{Glyph: "!"}, Glyph: types.PlainGlyphHolder{"!"},
ColorSet: types.TileColorSet{ ColorSet: types.TileColorSet{
Fg: types.PlainColorHolder{A: 255, R: 222, G: 255, B: 55}, Fg: types.PlainColorHolder{255, 222, 255, 55},
}, },
}) })
controller.AddComponent(potion2, rooms[1].Center) //implicit Coords controller.AddComponent(potion2, rooms[1].Center) //implicit Coords
controller.AddComponent(potion2, items.Carried{Mass:5, Bulk:3}) controller.AddComponent(potion2, items.Carried{Mass: 5, Bulk: 3})
controller.AddComponent(potion2, items.Usable{}) controller.AddComponent(potion2, items.Usable{})
controller.AddComponent(potion2, items.Consumable{}) controller.AddComponent(potion2, items.Consumable{})
controller.AddComponent(potion2, ecs.Named{Name:"second potion"}) controller.AddComponent(potion2, ecs.Named{Name: "second potion"})
//fixme end setting up items //fixme end setting up items
State.Player = player State.Player = player
State.Controller = controller 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 {
//but every call to bearlibterminal must be wrapped to closure and passed to mainfunc select {
var exit = false case State.RawInput <- ui.ReadKeyCode():
for !exit { 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()
}
select { }
case State.RawInput <- ui.ReadKeyCode(): appctx.Logger(mainCtx).Info().Msg("pre-shutdown sequence")
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) { func setupLayers(mainwindow *mainwindow.MainWindow) {
mainwindow.AddLayer("base", 0, "white") mainwindow.AddLayer("base", 0, "white")
mainwindow.AddLayer("overlay", 1, "white") mainwindow.AddLayer("overlay", 1, "white")
mainwindow.AddLayer("menubg", 2, "white") mainwindow.AddLayer("menubg", 2, "white")
mainwindow.AddLayer("menu", 3, "white") mainwindow.AddLayer("menu", 3, "white")
} }
func decodeInput(ctx context.Context, baseLayer *mainwindow.Layer) { func decodeInput(ctx context.Context, baseLayer *mainwindow.Layer) {
var exit = false var exit = false
var waitForWCspam = true var waitForWCspam = true
for !exit { for !exit {
select { select {
case keycode := <-State.RawInput: case keycode := <-State.RawInput:
if keycode == blt.TK_NONE { if keycode == blt.TK_NONE {
continue continue
} }
if keycode == blt.TK_CLOSE && !waitForWCspam { if keycode == blt.TK_CLOSE && !waitForWCspam {
appctx.Logger().Warn().Msg("exiting on window close...") appctx.Logger(ctx).Warn().Msg("exiting on window close...")
State.Exit <- struct{}{} State.Exit <- struct{}{}
appctx.Logger().Warn().Msg("...done") appctx.Logger(ctx).Warn().Msg("...done")
return return
} }
var pressed = "" var pressed = ""
var isModifier, _ = util.IntInSlice(keycode, modifiers) var isModifier, _ = util.IntInSlice(keycode, modifiers)
if !isModifier { if !isModifier {
pressed = ui.Scancodemap[keycode] pressed = ui.Scancodemap[keycode]
if blt.Check(blt.TK_SHIFT) != 0 { if blt.Check(blt.TK_SHIFT) != 0 {
pressed = "Shift+" + pressed pressed = "Shift+" + pressed
} }
if blt.Check(blt.TK_ALT) != 0 { if blt.Check(blt.TK_ALT) != 0 {
pressed = "Alt+" + pressed pressed = "Alt+" + pressed
} }
if blt.Check(blt.TK_CONTROL) != 0 { if blt.Check(blt.TK_CONTROL) != 0 {
pressed = "Ctrl+" + pressed pressed = "Ctrl+" + pressed
} }
//global hotkeys //global hotkeys
switch pressed { switch pressed {
case "Ctrl+q": case "Ctrl+q":
//fallthrough //fallthrough
//case "Escape": //case "Escape":
appctx.Logger().Info().Msg("exiting on quit command...") appctx.Logger(ctx).Info().Msg("exiting on quit command...")
State.Exit <- struct{}{} State.Exit <- struct{}{}
appctx.Logger().Info().Msg("...done") appctx.Logger(ctx).Info().Msg("...done")
exit = true exit = true
return return
default: default:
if pressed != "" { if pressed != "" {
waitForWCspam = false; waitForWCspam = false
State.Input <- pressed State.Input <- pressed
} }
} }
} }
} }
} }
} }

View File

@ -1,5 +1,5 @@
{ {
"version": "v0.0.1.7-29-g4f18b6d", "version": "v0.0.1.7",
"title": "Alchemyst", "title": "Alchemyst",
"sizeX": 100, "sizeX": 100,
"sizeY": 47, "sizeY": 47,

View File

@ -25,7 +25,7 @@ func TestDelaunay(t *testing.T) {
{types.Coords{10, 10}, types.Coords{30, 10}}, {types.Coords{10, 10}, types.Coords{30, 10}},
} }
result := delaunay.GetMst(coords, 100, 100, 100 ) result := delaunay.GetMst(coords, 100, 100, 0)
for idx, _ := range result { for idx, _ := range result {
if result[idx] != expected[idx] { if result[idx] != expected[idx] {

View File

@ -3,11 +3,13 @@ package ecs
// ECS system by jcerise, github.com/jcerise/gogue // ECS system by jcerise, github.com/jcerise/gogue
import ( import (
"context"
"lab.zaar.be/thefish/alchemyst-go/util/appctx" "lab.zaar.be/thefish/alchemyst-go/util/appctx"
"sort" "sort"
) )
type Controller struct { type Controller struct {
ctx context.Context
systems map[string]System systems map[string]System
sortedSystems map[int][]System sortedSystems map[int][]System
priorityKeys []int priorityKeys []int
@ -21,8 +23,8 @@ type Controller struct {
} }
// NewController is a convenience/constructor method to properly initialize a new processor // NewController is a convenience/constructor method to properly initialize a new processor
func NewController() *Controller { func NewController(ctx context.Context) *Controller {
controller := Controller{} controller := Controller{ctx: ctx}
controller.systems = make(map[string]System) controller.systems = make(map[string]System)
controller.sortedSystems = make(map[int][]System) controller.sortedSystems = make(map[int][]System)
controller.priorityKeys = []int{} controller.priorityKeys = []int{}
@ -78,7 +80,7 @@ func (c *Controller) GetMappedComponentClass(componentName string) Component {
return c.componentMap[componentName] return c.componentMap[componentName]
} else { } else {
// TODO: Add better (read: actual) error handling here // TODO: Add better (read: actual) error handling here
appctx.Logger().Warn().Msgf("Component[%s] not registered on Controller.\n", componentName) appctx.Logger(c.ctx).Warn().Msgf("Component[%s] not registered on Controller.\n", componentName)
return nil return nil
} }
} }
@ -138,20 +140,15 @@ func (c *Controller) GetEntities() map[Entity]map[string]Component {
} }
// GetEntitiesWithComponent returns a list of all entities with a given component attached // GetEntitiesWithComponent returns a list of all entities with a given component attached
func (c *Controller) GetEntitiesWithComponent(componentTypes... string) []Entity { // TODO: Allow for passing a list of components
func (c *Controller) GetEntitiesWithComponent(componentType string) []Entity {
entitiesWithComponent := make([]Entity, 0) entitiesWithComponent := make([]Entity, 0)
for entity := range c.entities { for entity := range c.entities {
mustAddThis := true if c.HasComponent(entity, componentType) {
for _, componentType := range componentTypes {
if !c.HasComponent(entity, componentType) {
mustAddThis = false
break
}
}
if mustAddThis {
entitiesWithComponent = append(entitiesWithComponent, entity) entitiesWithComponent = append(entitiesWithComponent, entity)
} }
} }
return entitiesWithComponent return entitiesWithComponent
} }
@ -211,7 +208,7 @@ func (c *Controller) AddSystem(system System, priority int) {
c.sortedSystems[priority] = append(c.sortedSystems[priority], system) c.sortedSystems[priority] = append(c.sortedSystems[priority], system)
sort.Ints(c.priorityKeys) sort.Ints(c.priorityKeys)
} else { } else {
appctx.Logger().Warn().Msgf("A system of type %v was already added to the controller %v!", systemType, c) appctx.Logger(c.ctx).Warn().Msgf("A system of type %v was already added to the controller %v!", systemType, c)
} }
} }

View File

@ -46,7 +46,7 @@ func NewLevelRenderSystem(
return trs return trs
} }
// fixme add to screens/game Exit() //fixme add to screens/game Exit()
func (trs LevelRenderSystem) Close() { func (trs LevelRenderSystem) Close() {
trs.animateTiles.Stop() trs.animateTiles.Stop()
trs.animateTiles = nil //zero pointer to ticker trs.animateTiles = nil //zero pointer to ticker
@ -93,7 +93,7 @@ func (trs LevelRenderSystem) Process() {
//terrain //terrain
for y := 0; y < trs.Viewport.H; y++ { for y := 0; y < trs.Viewport.H; y++ {
for x := 0; x < trs.Viewport.W; x++ { for x := 0; x < trs.Viewport.W; x++ {
mapCoords := types.Coords{X: trs.Viewport.CameraCoords.X + x, Y: trs.Viewport.CameraCoords.Y + y} mapCoords := types.Coords{trs.Viewport.CameraCoords.X + x, trs.Viewport.CameraCoords.Y + y}
if trs.state.Level.InBounds(mapCoords) { if trs.state.Level.InBounds(mapCoords) {
tile := trs.state.Level.GetTile(mapCoords) tile := trs.state.Level.GetTile(mapCoords)

View File

@ -62,7 +62,7 @@ func (pp *precomputedPermissive) ComputeFov(coords types.Coords, radius int) {
func (pp *precomputedPermissive) PrecomputeFovMap() { func (pp *precomputedPermissive) PrecomputeFovMap() {
max := pp.MaxTorchRadius max := pp.MaxTorchRadius
minusMax := (-1) * max minusMax := (-1) * max
zeroCoords := types.Coords{X: 0, Y: 0} zeroCoords := types.Coords{0, 0}
var x, y int var x, y int
//fill list //fill list
for x = minusMax; x < max+1; x++ { for x = minusMax; x < max+1; x++ {
@ -70,7 +70,7 @@ func (pp *precomputedPermissive) PrecomputeFovMap() {
if x == 0 && y == 0 { if x == 0 && y == 0 {
continue; continue;
} }
iterCoords := types.Coords{X: x, Y: y} iterCoords := types.Coords{x, y}
distance := zeroCoords.DistanceTo(iterCoords) distance := zeroCoords.DistanceTo(iterCoords)
if distance <= float64(max) { if distance <= float64(max) {
pp.CellList = append(pp.CellList, &Cell{iterCoords, distance, nil}) pp.CellList = append(pp.CellList, &Cell{iterCoords, distance, nil})
@ -101,7 +101,7 @@ func (pp *precomputedPermissive) PrecomputeFovMap() {
roundedX := int(basic.Round(lineX)) roundedX := int(basic.Round(lineX))
roundedY := int(basic.Round(lineY)) roundedY := int(basic.Round(lineY))
idx, cell, err := pp.FindByCoords(types.Coords{X: roundedX, Y: roundedY}) idx, cell, err := pp.FindByCoords(types.Coords{roundedX, roundedY})
if err != nil { if err != nil {
//inexistent coord found //inexistent coord found
break; break;

View File

@ -124,9 +124,7 @@ func (ps *precomputedShade) FindByCoords(c types.Coords) (int, *Cell, error) {
func (ps *precomputedShade) IsInFov(coords types.Coords) bool { func (ps *precomputedShade) IsInFov(coords types.Coords) bool {
rc := ps.fromLevelCoords(coords) rc := ps.fromLevelCoords(coords)
if rc.X == 0 && rc.Y == 0 { if rc.X == 0 && rc.Y ==0 {return true}
return true
}
_, cell, err := ps.FindByCoords(rc) _, cell, err := ps.FindByCoords(rc)
if err != nil { if err != nil {
return false return false
@ -145,15 +143,15 @@ func (ps *precomputedShade) Init() {
func (ps *precomputedShade) PrecomputeFovMap() { func (ps *precomputedShade) PrecomputeFovMap() {
max := ps.MaxTorchRadius max := ps.MaxTorchRadius
minusMax := (-1) * max minusMax := (-1) * max
zeroCoords := types.Coords{X: 0, Y: 0} zeroCoords := types.Coords{0, 0}
var x, y int var x, y int
//fill list //fill list
for x = minusMax; x < max+1; x++ { for x = minusMax; x < max+1; x++ {
for y = minusMax; y < max+1; y++ { for y = minusMax; y < max+1; y++ {
if x == 0 && y == 0 { if x == 0 && y == 0 {
continue continue;
} }
iterCoords := types.Coords{X: x, Y: y} iterCoords := types.Coords{x, y}
distance := zeroCoords.DistanceTo(iterCoords) distance := zeroCoords.DistanceTo(iterCoords)
if distance <= float64(max) { if distance <= float64(max) {
ps.CellList = append(ps.CellList, &Cell{iterCoords, distance, nil, 0}) ps.CellList = append(ps.CellList, &Cell{iterCoords, distance, nil, 0})
@ -182,11 +180,11 @@ func (ps *precomputedShade) PrecomputeFovMap() {
roundedX := int(basic.Round(lineX)) roundedX := int(basic.Round(lineX))
roundedY := int(basic.Round(lineY)) roundedY := int(basic.Round(lineY))
_, cell, err := ps.FindByCoords(types.Coords{X: roundedX, Y: roundedY}) _, cell, err := ps.FindByCoords(types.Coords{roundedX, roundedY})
if err != nil { if err != nil {
//inexistent coord found //inexistent coord found
break break;
} }
cell.occludedAngles = unique(append(cell.occludedAngles, i)) cell.occludedAngles = unique(append(cell.occludedAngles, i))
} }
@ -221,7 +219,7 @@ func (ps *precomputedShade) recalc(level *gamemap.Level, initCoords types.Coords
i := 0 i := 0
prevDistance := 0.0 prevDistance := 0.0
for !bytes.Equal(currentShade, fullShade) { for !bytes.Equal(currentShade, fullShade) {
if i == len(ps.CellList)-1 { if (i == len(ps.CellList)-1) {
break break
} }
cell := ps.CellList[i] cell := ps.CellList[i]
@ -244,7 +242,7 @@ func (ps *precomputedShade) recalc(level *gamemap.Level, initCoords types.Coords
if level.GetTile(lc).BlocksSight && ps.LightWalls { if level.GetTile(lc).BlocksSight && ps.LightWalls {
//if (nextShade[angle] == 0 && currentShade[angle] == 0) { //if (nextShade[angle] == 0 && currentShade[angle] == 0) {
if nextShade[angle] == 0 { if (nextShade[angle] == 0) {
level.GetTile(lc).Visible = true level.GetTile(lc).Visible = true
level.GetTile(lc).Explored = true level.GetTile(lc).Explored = true
} }
@ -268,7 +266,7 @@ func (ps *precomputedShade) ComputeFov(level *gamemap.Level, initCoords types.Co
//fmt.Printf("\n coords: %v, distance: %f, lit: %d", cell.Coords, cell.distance, cell.lit) //fmt.Printf("\n coords: %v, distance: %f, lit: %d", cell.Coords, cell.distance, cell.lit)
cs, err := ps.toLevelCoords(level, initCoords, cell.Coords) cs, err := ps.toLevelCoords(level, initCoords, cell.Coords)
if cell.lit > 0 && cell.lit > MIN_LIT_TO_BE_VISIBLE { if cell.lit > 0 && cell.lit > MIN_LIT_TO_BE_VISIBLE {
//if cell.lit > 0 && cell.lit / (ps.MaxTorchRadius - int(cell.distance - 0.4) - 1) > MIN_LIT_TO_BE_VISIBLE { //if cell.lit > 0 && cell.lit / (ps.MaxTorchRadius - int(cell.distance - 0.4) - 1) > MIN_LIT_TO_BE_VISIBLE {
if err != nil { if err != nil {
continue continue
} }
@ -300,7 +298,7 @@ func (ps *precomputedShade) ComputeFov(level *gamemap.Level, initCoords types.Co
} }
func (ps *precomputedShade) toLevelCoords(level *gamemap.Level, initCoords, relativeCoords types.Coords) (types.Coords, error) { func (ps *precomputedShade) toLevelCoords(level *gamemap.Level, initCoords, relativeCoords types.Coords) (types.Coords, error) {
realCoords := types.Coords{X: initCoords.X + relativeCoords.X, Y: initCoords.Y + relativeCoords.Y} realCoords := types.Coords{initCoords.X + relativeCoords.X, initCoords.Y + relativeCoords.Y}
if !level.InBounds(realCoords) { if !level.InBounds(realCoords) {
return types.Coords{}, errOutOfBounds return types.Coords{}, errOutOfBounds
} }
@ -308,7 +306,7 @@ func (ps *precomputedShade) toLevelCoords(level *gamemap.Level, initCoords, rela
} }
func (ps *precomputedShade) fromLevelCoords(lc types.Coords) types.Coords { func (ps *precomputedShade) fromLevelCoords(lc types.Coords) types.Coords {
relativeCoords := types.Coords{X: lc.X - ps.originCoords.X, Y: lc.Y - ps.originCoords.Y} relativeCoords := types.Coords{lc.X - ps.originCoords.X, lc.Y - ps.originCoords.Y}
return relativeCoords return relativeCoords
} }

View File

@ -8,12 +8,12 @@ import (
) )
func TestPsDistance(t *testing.T) { func TestPsDistance(t *testing.T) {
iterCoords := types.Coords{X: 0, Y: 0} iterCoords := types.Coords{0, 0}
fmt.Printf("\n dto: \t %v", iterCoords.DistanceTo(types.Coords{X: 0, Y: 1})) fmt.Printf("\n dto: \t %v", iterCoords.DistanceTo(types.Coords{0, 1}))
fmt.Printf("\n dto: \t %v", iterCoords.DistanceTo(types.Coords{X: 0, Y: 5})) fmt.Printf("\n dto: \t %v", iterCoords.DistanceTo(types.Coords{0, 5}))
fmt.Printf("\n dto: \t %v", iterCoords.DistanceTo(types.Coords{X: 3, Y: 3})) fmt.Printf("\n dto: \t %v", iterCoords.DistanceTo(types.Coords{3, 3}))
fmt.Printf("\n dto: \t %v", iterCoords.DistanceTo(types.Coords{X: 100, Y: 0})) fmt.Printf("\n dto: \t %v", iterCoords.DistanceTo(types.Coords{100, 0}))
} }
func TestPrecompShade(t *testing.T) { func TestPrecompShade(t *testing.T) {
@ -44,7 +44,7 @@ func TestPrecompShade(t *testing.T) {
} }
} }
playerCoords := types.Coords{X: 10, Y: 10} playerCoords := types.Coords{10, 10}
level.SetTileByXY(8, 12, gamemap.NewWall()) level.SetTileByXY(8, 12, gamemap.NewWall())
level.SetTileByXY(10, 8, gamemap.NewWall()) level.SetTileByXY(10, 8, gamemap.NewWall())

View File

@ -1,8 +1,7 @@
package gamemap package gamemap
import ( import (
"fmt" "context"
"lab.zaar.be/thefish/alchemyst-go/engine/ecs" "lab.zaar.be/thefish/alchemyst-go/engine/ecs"
"lab.zaar.be/thefish/alchemyst-go/engine/types" "lab.zaar.be/thefish/alchemyst-go/engine/types"
"lab.zaar.be/thefish/alchemyst-go/util/appctx" "lab.zaar.be/thefish/alchemyst-go/util/appctx"
@ -15,6 +14,7 @@ var mapHeight = 90
type Level struct { type Level struct {
types.Rect types.Rect
ctx context.Context
Name string Name string
Branch string Branch string
Depth int Depth int
@ -30,7 +30,7 @@ func (l *Level) GetTileNbs (coords types.Coords) []*Tile {
result := make([]*Tile,0) result := make([]*Tile,0)
for i := coords.X-1; i < coords.X+1; i++ { for i := coords.X-1; i < coords.X+1; i++ {
for j := coords.Y-1; j < coords.Y+1; j++ { for j := coords.Y-1; j < coords.Y+1; j++ {
nbc := types.Coords{X: i,Y: j} nbc := types.Coords{i,j}
if l.InBounds(nbc){ if l.InBounds(nbc){
if nbc == coords { if nbc == coords {
continue continue
@ -66,33 +66,34 @@ func (l *Level) MakePassByXY (x,y int, tile *Tile) {
func (l *Level) Put (x, y int, tileFunc interface{}) { func (l *Level) Put (x, y int, tileFunc interface{}) {
tile := tileFunc.(func() *Tile)() tile := tileFunc.(func() *Tile)()
if tile == nil { if tile == nil {
appctx.Logger().Fatal().Msgf("Got non-tile type to put into level: %v", tile) appctx.Logger(l.ctx).Fatal().Msgf("Got non-tile type to put into level: %v", tile)
} }
if l.InBounds(types.Coords{X: x, Y: y}) { if l.InBounds(types.Coords{x, y}) {
l.Tiles[y*l.W+x] = tile l.Tiles[y*l.W+x] = tile
} }
} }
func NewLevel(branch string, depth int) *Level { func NewLevel(ctx context.Context, branch string, depth int) *Level {
l := &Level{ l := &Level{
Name: fmt.Sprintf(branch, depth), ctx: ctx,
Name: branch + string(depth),
Depth: depth, Depth: depth,
Rect: types.NewRect(0,0, mapWidth, mapHeight), Rect: types.NewRect(0,0, mapWidth, mapHeight),
} }
l.Tiles = make([]*Tile, l.W*l.H) l.Tiles = make([]*Tile, l.W*l.H)
appctx.Logger().Debug().Msgf("Generating level of branch %s depth %d", branch, depth) appctx.Logger(ctx).Debug().Msgf("Generating level of branch %s depth %d", branch, depth)
return l return l
} }
func (l *Level) SetAllInvisible() { func (l *Level) SetAllInvisible() {
for idx := range l.Tiles { for idx, _ := range l.Tiles {
l.Tiles[idx].Visible = false l.Tiles[idx].Visible = false
} }
} }
func (l *Level) SetAllVisible() { func (l *Level) SetAllVisible() {
for idx := range l.Tiles { for idx, _ := range l.Tiles {
l.Tiles[idx].Visible = true l.Tiles[idx].Visible = true
} }
} }

View File

@ -1,6 +1,7 @@
package mapgens package mapgens
import ( import (
"context"
"lab.zaar.be/thefish/alchemyst-go/engine/gamemap" "lab.zaar.be/thefish/alchemyst-go/engine/gamemap"
"lab.zaar.be/thefish/alchemyst-go/engine/types" "lab.zaar.be/thefish/alchemyst-go/engine/types"
"lab.zaar.be/thefish/alchemyst-go/util" "lab.zaar.be/thefish/alchemyst-go/util"
@ -13,7 +14,7 @@ var maxRoomSize = 22
var maxrooms = 100 var maxrooms = 100
var fges = map[int]types.RectFill{ var fges = map[int]types.RectFill{
1: { 1: types.RectFill{
Top: gamemap.NewWall, Top: gamemap.NewWall,
Bottom: gamemap.NewWall, Bottom: gamemap.NewWall,
Left: gamemap.NewWall, Left: gamemap.NewWall,
@ -25,7 +26,7 @@ var fges = map[int]types.RectFill{
Body: gamemap.NewFloor, Body: gamemap.NewFloor,
}, },
2: { 2: types.RectFill{
Top: gamemap.NewWaterTile, Top: gamemap.NewWaterTile,
Bottom: gamemap.NewWaterTile, Bottom: gamemap.NewWaterTile,
Left: gamemap.NewWaterTile, Left: gamemap.NewWaterTile,
@ -38,9 +39,9 @@ var fges = map[int]types.RectFill{
}, },
} }
func GetRandomRoomList(rng *util.RNG, l *gamemap.Level, maxRooms, minRoomSize, maxRoomSize int, ) []gamemap.Room{ func GetRandomRoomList(ctx context.Context, rng *util.RNG, l *gamemap.Level, maxRooms, minRoomSize, maxRoomSize int, ) []gamemap.Room{
rooms := make([]gamemap.Room, 0) rooms := make([]gamemap.Room, 0)
pfLoader := gamemap.NewPrefabLoader() pfLoader := gamemap.NewPrefabLoader(ctx)
pfRooms := pfLoader.PrefabRoomsList() pfRooms := pfLoader.PrefabRoomsList()
var fillage types.RectFill var fillage types.RectFill
@ -53,14 +54,7 @@ func GetRandomRoomList(rng *util.RNG, l *gamemap.Level, maxRooms, minRoomSize, m
var newRoom = gamemap.Room{} var newRoom = gamemap.Room{}
if prefabUsed && rng.Range(0, 5) <= 3 { if !prefabUsed || rng.Range(0, 5) > 3 {
newRoom = gamemap.NewRandomRectRoom(
rng,
rng.Range(minRoomSize, maxRoomSize),
rng.Range(minRoomSize, maxRoomSize),
fillage,
)
} else {
//if prefabUsed { //if prefabUsed {
//prefab //prefab
prefabUsed = true prefabUsed = true
@ -74,14 +68,20 @@ func GetRandomRoomList(rng *util.RNG, l *gamemap.Level, maxRooms, minRoomSize, m
Mobs: r.Mobs, Mobs: r.Mobs,
Connectors: make([]types.Coords,0), Connectors: make([]types.Coords,0),
} }
newRoom.Connectors = append(newRoom.Connectors, r.Connectors...) for _, coord := range r.Connectors {
// for _, coord := range r.Connectors { newRoom.Connectors = append(newRoom.Connectors, coord)
// newRoom.Connectors = append(newRoom.Connectors, coord) }
// } } else {
newRoom = gamemap.NewRandomRectRoom(
rng,
rng.Range(minRoomSize, maxRoomSize),
rng.Range(minRoomSize, maxRoomSize),
fillage,
)
} }
where := types.Coords{ where := types.Coords{
X: rng.Range(1, l.W-2-newRoom.W), rng.Range(1, l.W-2-newRoom.W),
Y: rng.Range(1, l.H-2-newRoom.H), rng.Range(1, l.H-2-newRoom.H),
} }
newRoom.MoveToCoords(where) newRoom.MoveToCoords(where)
@ -102,12 +102,11 @@ func GetRandomRoomList(rng *util.RNG, l *gamemap.Level, maxRooms, minRoomSize, m
return rooms return rooms
} }
//fixme overlapping rooms func BlitToLevel (ctx context.Context, l *gamemap.Level, rooms[]gamemap.Room) {
func BlitToLevel (l *gamemap.Level, rooms[]gamemap.Room) {
for _, room := range rooms { for _, room := range rooms {
err := room.BlitToLevel(l) err := room.BlitToLevel(ctx, l)
if err != nil { if err != nil {
appctx.Logger().Err(err) appctx.Logger(ctx).Err(err)
} }
} }
} }
@ -173,7 +172,7 @@ func DigHTunnel(l *gamemap.Level, x1, x2, y int) {
finish = x1 finish = x1
} }
for i := start; i <= finish; i++ { for i := start; i <= finish; i++ {
if l.InBounds(types.Coords{X: i, Y: y}) { if l.InBounds(types.Coords{i, y}) {
l.MakePassByXY(i, y, gamemap.NewFloor()) l.MakePassByXY(i, y, gamemap.NewFloor())
//l.Tiles[i][y] = gamemap.NewFloor() //l.Tiles[i][y] = gamemap.NewFloor()
} }
@ -190,7 +189,7 @@ func DigVTunnel(l *gamemap.Level, y1, y2, x int) {
finish = y1 finish = y1
} }
for i := start; i <= finish; i++ { for i := start; i <= finish; i++ {
if l.InBounds(types.Coords{X: x, Y: i}) { if l.InBounds(types.Coords{x, i}) {
l.MakePassByXY(x, i, gamemap.NewFloor()) l.MakePassByXY(x, i, gamemap.NewFloor())
} }
} }

View File

@ -3,9 +3,10 @@ package mapgens
import ( import (
"lab.zaar.be/thefish/alchemyst-go/engine/gamemap" "lab.zaar.be/thefish/alchemyst-go/engine/gamemap"
"lab.zaar.be/thefish/alchemyst-go/util" "lab.zaar.be/thefish/alchemyst-go/util"
"lab.zaar.be/thefish/alchemyst-go/util/appctx"
) )
func DefaultGen(l *gamemap.Level) (*gamemap.Level, []gamemap.Room) { func DefaultGen(ctx appctx.ClientCtx,l *gamemap.Level) (*gamemap.Level, []gamemap.Room) {
rng := util.NewRNG() rng := util.NewRNG()
@ -16,9 +17,9 @@ func DefaultGen(l *gamemap.Level) (*gamemap.Level, []gamemap.Room) {
} }
} }
rooms := GetRandomRoomList(rng, l, maxrooms, minRoomSize, maxRoomSize) rooms := GetRandomRoomList(ctx, rng, l, maxrooms, minRoomSize, maxRoomSize)
BlitToLevel(l, rooms) BlitToLevel(ctx, l, rooms)
for idx, room := range rooms { for idx, room := range rooms {
if idx > 0 { if idx > 0 {

View File

@ -18,10 +18,10 @@ func DelaunayMstGen(ctx context.Context, l *gamemap.Level) (*gamemap.Level, []ga
l.SetTileByXY(i, j, gamemap.NewWall()) l.SetTileByXY(i, j, gamemap.NewWall())
} }
} }
rooms := GetRandomRoomList(rng, l, maxrooms, minRoomSize, maxRoomSize) rooms := GetRandomRoomList(ctx, rng, l, maxrooms, minRoomSize, maxRoomSize)
BlitToLevel(l, rooms) BlitToLevel(ctx, l, rooms)
centers := make([]types.Coords, 0) centers := make([]types.Coords, 0)
for _, room := range rooms { for _, room := range rooms {

View File

@ -4,10 +4,11 @@ import (
"lab.zaar.be/thefish/alchemyst-go/engine/gamemap" "lab.zaar.be/thefish/alchemyst-go/engine/gamemap"
"lab.zaar.be/thefish/alchemyst-go/engine/types" "lab.zaar.be/thefish/alchemyst-go/engine/types"
"lab.zaar.be/thefish/alchemyst-go/util" "lab.zaar.be/thefish/alchemyst-go/util"
"lab.zaar.be/thefish/alchemyst-go/util/appctx"
"lab.zaar.be/thefish/alchemyst-go/util/delaunay" "lab.zaar.be/thefish/alchemyst-go/util/delaunay"
) )
func DelaunayMstExtGen(l *gamemap.Level) (*gamemap.Level, []gamemap.Room) { func DelaunayMstExtGen(ctx appctx.ClientCtx, l *gamemap.Level) (*gamemap.Level, []gamemap.Room) {
rng := util.NewRNG() rng := util.NewRNG()
@ -17,9 +18,9 @@ func DelaunayMstExtGen(l *gamemap.Level) (*gamemap.Level, []gamemap.Room) {
l.SetTileByXY(i, j, gamemap.NewWall()) l.SetTileByXY(i, j, gamemap.NewWall())
} }
} }
rooms := GetRandomRoomList(rng, l, maxrooms, minRoomSize, maxRoomSize) rooms := GetRandomRoomList(ctx, rng, l, maxrooms, minRoomSize, maxRoomSize)
BlitToLevel(l, rooms) BlitToLevel(ctx, l, rooms)
centers := make([]types.Coords, 0) centers := make([]types.Coords, 0)
for _, room := range rooms { for _, room := range rooms {

View File

@ -18,9 +18,9 @@ func DelaunayPureGen(ctx context.Context, l *gamemap.Level) (*gamemap.Level, []g
l.SetTileByXY(i, j, gamemap.NewWall()) l.SetTileByXY(i, j, gamemap.NewWall())
} }
} }
rooms := GetRandomRoomList(rng, l, maxrooms, minRoomSize, maxRoomSize) rooms := GetRandomRoomList(ctx, rng, l, maxrooms, minRoomSize, maxRoomSize)
BlitToLevel(l, rooms) BlitToLevel(ctx, l, rooms)
centers := make([]types.Coords, 0) centers := make([]types.Coords, 0)
for _, room := range rooms { for _, room := range rooms {

View File

@ -1,9 +1,9 @@
package gamemap package gamemap
import ( import (
"context"
"encoding/json" "encoding/json"
"os" "io/ioutil"
"lab.zaar.be/thefish/alchemyst-go/engine/items" "lab.zaar.be/thefish/alchemyst-go/engine/items"
"lab.zaar.be/thefish/alchemyst-go/engine/mob" "lab.zaar.be/thefish/alchemyst-go/engine/mob"
"lab.zaar.be/thefish/alchemyst-go/engine/types" "lab.zaar.be/thefish/alchemyst-go/engine/types"
@ -30,7 +30,7 @@ type PrefabRecord struct {
} }
func LoadPrefabFile(filename string) (*PrefabFile, error) { func LoadPrefabFile(filename string) (*PrefabFile, error) {
data, err := os.ReadFile(filename) data, err := ioutil.ReadFile(filename)
if err!= nil { if err!= nil {
return nil, err return nil, err
} }
@ -42,10 +42,12 @@ func LoadPrefabFile(filename string) (*PrefabFile, error) {
return instance, nil return instance, nil
} }
type PrefabLoader struct {} type PrefabLoader struct {
ctx context.Context
}
func NewPrefabLoader() PrefabLoader { func NewPrefabLoader(ctx context.Context) PrefabLoader {
return PrefabLoader{} return PrefabLoader{ctx: ctx}
} }
func (pfbl PrefabLoader) PrefabRoomsList() []Room { func (pfbl PrefabLoader) PrefabRoomsList() []Room {
@ -63,8 +65,6 @@ func (pfbl PrefabLoader) PrefabRoomsList() []Room {
for _, rawPrefab := range file.Prefabs { for _, rawPrefab := range file.Prefabs {
appctx.Logger().Debug().Msgf("adding %s", rawPrefab.name)
for k,v := range rawPrefab.TileLegend { for k,v := range rawPrefab.TileLegend {
currentTileLegend[k] = v currentTileLegend[k] = v
} }
@ -76,8 +76,8 @@ func (pfbl PrefabLoader) PrefabRoomsList() []Room {
} }
room := Room{ room := Room{
Rect:types.Rect{X: 0, Y: 0, W: rawPrefab.Size.X, H: rawPrefab.Size.Y}, Rect:types.Rect{0, 0, rawPrefab.Size.X, rawPrefab.Size.Y},
Center: types.Coords{X: rawPrefab.Size.X / 2, Y: rawPrefab.Size.Y / 2}, //fixme Center: types.Coords{rawPrefab.Size.X / 2, rawPrefab.Size.Y / 2}, //fixme
Geometry: make([]func()*Tile, rawPrefab.Size.X*rawPrefab.Size.Y), Geometry: make([]func()*Tile, rawPrefab.Size.X*rawPrefab.Size.Y),
Mobs: make([]mob.Mob, rawPrefab.Size.X*rawPrefab.Size.Y), Mobs: make([]mob.Mob, rawPrefab.Size.X*rawPrefab.Size.Y),
Items: make([]items.Carried, rawPrefab.Size.X*rawPrefab.Size.Y), Items: make([]items.Carried, rawPrefab.Size.X*rawPrefab.Size.Y),
@ -102,11 +102,11 @@ func (pfbl PrefabLoader) PrefabRoomsList() []Room {
} }
if shortName == "connector" { if shortName == "connector" {
f = NewWall f = NewWall
room.Connectors = append(room.Connectors, types.Coords{X: i,Y: j}) room.Connectors = append(room.Connectors, types.Coords{i,j})
} else { } else {
f, ok = TileTypeMap[shortName] f, ok = TileTypeMap[shortName]
if (!ok) { if (!ok) {
appctx.Logger().Warn().Msgf("Unknown tile: %s", shortName) appctx.Logger(pfbl.ctx).Warn().Msgf("Unknown tile: %s", shortName)
} }
} }
room.Geometry[i+ j*room.W] = f room.Geometry[i+ j*room.W] = f

View File

@ -1,15 +1,15 @@
package gamemap package gamemap
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"strings"
"lab.zaar.be/thefish/alchemyst-go/engine/items" "lab.zaar.be/thefish/alchemyst-go/engine/items"
"lab.zaar.be/thefish/alchemyst-go/engine/mob" "lab.zaar.be/thefish/alchemyst-go/engine/mob"
"lab.zaar.be/thefish/alchemyst-go/engine/types" "lab.zaar.be/thefish/alchemyst-go/engine/types"
"lab.zaar.be/thefish/alchemyst-go/util" "lab.zaar.be/thefish/alchemyst-go/util"
"lab.zaar.be/thefish/alchemyst-go/util/appctx" "lab.zaar.be/thefish/alchemyst-go/util/appctx"
"strings"
) )
var invalidBlit = errors.New("trying to blit on existing good tile") var invalidBlit = errors.New("trying to blit on existing good tile")
@ -28,19 +28,19 @@ func (r *Room) Put (x, y int, tileFunc interface{}) {
if tf == nil { if tf == nil {
return //fixme error return //fixme error
} }
if r.InBounds(types.Coords{X: x, Y: y}) { if r.InBounds(types.Coords{x, y}) {
r.Geometry[x+y*r.W] = tf r.Geometry[x+y*r.W] = tf
} }
} }
func (room *Room) BlitToLevel(l *Level) error { func (room *Room) BlitToLevel(ctx context.Context, l *Level) error {
//copy tiles like this: //copy tiles like this:
//https://stackoverflow.com/questions/21011023/copy-pointer-values-a-b-in-golang //https://stackoverflow.com/questions/21011023/copy-pointer-values-a-b-in-golang
for j := 0; j < room.H; j++ { for j := 0; j < room.H; j++ {
for i := 0; i < room.W; i++ { for i := 0; i < room.W; i++ {
mapCoords := types.Coords{X: room.X + i, Y: room.Y + j} mapCoords := types.Coords{room.X + i, room.Y + j}
underlyingTile := l.GetTile(mapCoords) underlyingTile := l.GetTile(mapCoords)
tileFunc := room.Geometry[i+j*room.W] tileFunc := room.Geometry[i+j*room.W]
@ -51,7 +51,7 @@ func (room *Room) BlitToLevel(l *Level) error {
//check underlying tile //check underlying tile
if underlyingTile == nil || if underlyingTile == nil ||
underlyingTile.Name != "Wall" { underlyingTile.Name != "Wall" {
appctx.Logger().Warn().Msg("Invalid blit!") appctx.Logger(ctx).Warn().Msg("Invalid blit!")
return invalidBlit return invalidBlit
} }
l.Put(mapCoords.X, mapCoords.Y, tileFunc) l.Put(mapCoords.X, mapCoords.Y, tileFunc)
@ -86,17 +86,17 @@ func NewRandomRectRoom(rng *util.RNG, w, h int, fillage types.RectFill) Room {
w, w,
h, h,
), ),
Center: types.Coords{X: w / 2, Y: h /2 }, Center: types.Coords{w / 2, h /2 },
Geometry: make([]func()*Tile, w*h), Geometry: make([]func()*Tile, w*h),
} }
newRoom.Blit(fillage, &newRoom) newRoom.Blit(fillage, &newRoom)
//add connectors //add connectors
newRoom.Connectors = append( newRoom.Connectors = append(
newRoom.Connectors, newRoom.Connectors,
types.Coords{X: rng.Range(1, w - 2), Y: 1}, types.Coords{rng.Range(1, w - 2), 1},
types.Coords{X: rng.Range(1, w - 2), Y: h -2}, types.Coords{rng.Range(1, w - 2), h -2},
types.Coords{X: 1, Y: rng.Range(1, h - 2)}, types.Coords{1, rng.Range(1, h - 2)},
types.Coords{X: w - 2, Y: rng.Range(1, h - 2)}, types.Coords{w - 2, rng.Range(1, h - 2)},
) )
return newRoom return newRoom
} }
@ -105,7 +105,7 @@ func (r *Room) String() string {
return strings.Join([]string{ return strings.Join([]string{
"room: ", "room: ",
"\t" + fmt.Sprintf(" rect: X: %d, Y: %d, maxX: %d, maxY: %d", r.Rect.X, r.Rect.Y, r.Rect.W + r.X - 1, r.Rect.H + r.Y - 1), "\t" + fmt.Sprintf(" rect: X: %d, Y: %d, maxX: %d, maxY: %d", r.Rect.X, r.Rect.Y, r.Rect.W + r.X - 1, r.Rect.H + r.Y - 1),
"\t" + fmt.Sprintf(" center: %d, %d", r.Center.X, r.Center.Y), "\t" + fmt.Sprintf(" center:", r.Center.X, r.Center.Y),
"\t" + fmt.Sprintf(" Connectors: %v", r.Connectors), "\t" + fmt.Sprintf(" Connectors: %v", r.Connectors),
},"\n") + "\n" },"\n") + "\n"
} }

View File

@ -37,22 +37,21 @@ func (t *Tile) GetRawBgColor() uint32 {
func NewWall() *Tile { func NewWall() *Tile {
return &Tile{ return &Tile{
Appearance: &Appearance{
Glyph: &PlainGlyphHolder{Glyph: "#"},
ColorSet: TileColorSet{
Fg: &PlainColorHolder{A: 255, R: 130, G: 110, B: 150},
Bg: &PlainColorHolder{A: 255, R: 172, G: 170, B: 173},
DarkFg: &PlainColorHolder{A: 255, R: 20, G: 20, B: 68},
DarkBg: &PlainColorHolder{A: 255, R: 7, G: 7, B: 30},
},
},
Name: "Wall", Name: "Wall",
Description: "A dull rock wall", Description: "A dull rock wall",
BlocksPass: true, BlocksPass: true,
BlocksSight: true, BlocksSight: true,
Explored: false, Explored: false,
MustDraw: false, MustDraw: false,
Visible: false, Appearance: &Appearance{
Glyph: &PlainGlyphHolder{"#"},
ColorSet: TileColorSet{
Fg: &PlainColorHolder{255, 130, 110, 150},
Bg: &PlainColorHolder{255, 172, 170, 173},
DarkFg: &PlainColorHolder{255, 20, 20, 68},
DarkBg: &PlainColorHolder{255, 7, 7, 30},
},
},
} }
} }
@ -65,18 +64,18 @@ func NewDecoratedWall() *Tile {
Explored: false, Explored: false,
MustDraw: false, MustDraw: false,
Appearance: &Appearance{ Appearance: &Appearance{
Glyph: &PlainGlyphHolder{Glyph: "#"}, Glyph: &PlainGlyphHolder{"#"},
ColorSet: TileColorSet{ ColorSet: TileColorSet{
Fg: &PlainColorHolder{A: 255, R: 130, G: 110, B: 150}, Fg: &PlainColorHolder{255, 130, 110, 150},
//Bg: &PlainColorHolder{255, 172, 170, 173}, //Bg: &PlainColorHolder{255, 172, 170, 173},
Bg: &DanceColorHolder{ Bg: &DanceColorHolder{
A: 255, 255,
R: DeviatedColorRing(172, -15, 10), DeviatedColorRing(172, -15, 10),
G: DeviatedColorRing(170, -5, 15), DeviatedColorRing(170, -5, 15),
B: DeviatedColorRing(173, -10, 10), DeviatedColorRing(173, -10, 10),
}, },
DarkFg: &PlainColorHolder{A: 255, R: 20, G: 20, B: 68}, DarkFg: &PlainColorHolder{255, 20, 20, 68},
DarkBg: &PlainColorHolder{A: 255, R: 7, G: 7, B: 30}, DarkBg: &PlainColorHolder{255, 7, 7, 30},
}, },
}, },
} }
@ -91,12 +90,12 @@ func NewFloor() *Tile {
Explored: false, Explored: false,
MustDraw: false, MustDraw: false,
Appearance: &Appearance{ Appearance: &Appearance{
Glyph: &PlainGlyphHolder{Glyph: "."}, Glyph: &PlainGlyphHolder{"."},
ColorSet: TileColorSet{ ColorSet: TileColorSet{
Fg: &PlainColorHolder{A: 255, R: 220, G: 220, B: 250}, Fg: &PlainColorHolder{255, 220, 220, 250},
Bg: &PlainColorHolder{A: 255, R: 19, G: 19, B: 70}, Bg: &PlainColorHolder{255, 19, 19, 70},
DarkFg: &PlainColorHolder{A: 255, R: 30, G: 20, B: 50}, DarkFg: &PlainColorHolder{255, 30, 20, 50},
DarkBg: &PlainColorHolder{A: 255, R: 7, G: 7, B: 30}, DarkBg: &PlainColorHolder{255, 7, 7, 30},
}, },
}, },
} }
@ -112,17 +111,17 @@ func NewWaterTile() *Tile {
Explored: false, Explored: false,
MustDraw: true, //fixme debug MustDraw: true, //fixme debug
Appearance: &Appearance{ Appearance: &Appearance{
Glyph: &PlainGlyphHolder{Glyph: " "}, Glyph: &PlainGlyphHolder{" "},
ColorSet: TileColorSet{ ColorSet: TileColorSet{
Fg: &PlainColorHolder{A: 255, R: 220, G: 220, B: 250}, Fg: &PlainColorHolder{255, 220, 220, 250},
Bg: &DanceColorHolder{ Bg: &DanceColorHolder{
A: 255, 255,
R: SingleColorRing(5), SingleColorRing(5),
G: FillColorRing(2, 2, 42, 4), FillColorRing(2, 2, 42, 4),
B: FillColorRing(154, 150, 229, 12), FillColorRing(154, 150, 229, 12),
}, },
DarkFg: &PlainColorHolder{A: 255, R: 30, G: 20, B: 50}, DarkFg: &PlainColorHolder{255, 30, 20, 50},
DarkBg: &PlainColorHolder{A: 255, R: 7, G: 7, B: 30}, DarkBg: &PlainColorHolder{255, 7, 7, 30},
}, },
}, },
@ -132,21 +131,27 @@ func NewWaterTile() *Tile {
func NewDeepWaterTile() *Tile { func NewDeepWaterTile() *Tile {
//ch := &ColorHolder{5, 2, 154} //ch := &ColorHolder{5, 2, 154}
return &Tile{ return &Tile{
Appearance: &Appearance{
Glyph: &PlainGlyphHolder{Glyph: " "},
ColorSet: TileColorSet{
Fg: &PlainColorHolder{A: 255, R: 220, G: 220, B: 250},
Bg: &DanceColorHolder{A: 255, R: SingleColorRing(19), G: FillColorRing(19, 0, 15, 2), B: FillColorRing(127, 120, 176, 12)},
DarkFg: &PlainColorHolder{A: 255, R: 30, G: 20, B: 50},
DarkBg: &PlainColorHolder{A: 255, R: 7, G: 7, B: 30},
},
},
Name: "Deep Water", Name: "Deep Water",
Description: "Deep water", Description: "Deep water",
BlocksPass: false, BlocksPass: false,
BlocksSight: false, BlocksSight: false,
Explored: false, Explored: false,
MustDraw: true, MustDraw: true, //fixme debug
Visible: false,
Appearance: &Appearance{
Glyph: &PlainGlyphHolder{" "},
ColorSet: TileColorSet{
Fg: &PlainColorHolder{255, 220, 220, 250},
Bg: &DanceColorHolder{
255,
SingleColorRing(19),
FillColorRing(19, 0, 15, 2),
FillColorRing(127, 120, 176, 12),
},
DarkFg: &PlainColorHolder{255, 30, 20, 50},
DarkBg: &PlainColorHolder{255, 7, 7, 30},
},
},
} }
} }

View File

@ -1,13 +1,7 @@
package items package items
import ( import "lab.zaar.be/thefish/alchemyst-go/engine/ecs"
"fmt"
"lab.zaar.be/thefish/alchemyst-go/engine/ecs"
)
var (
ErrorInvTooHeavy = fmt.Errorf("too heavy")
ErrorInvTooBulky = fmt.Errorf("too bulky")
)
type Backpack struct { type Backpack struct {
MaxNumber int MaxNumber int
MaxBulk int MaxBulk int
@ -19,7 +13,7 @@ func (b Backpack) Type() string {
return ecs.BackpackComponent return ecs.BackpackComponent
} }
func (b *Backpack) HasFreeSpace(Bulk, Mass int) error { func (b *Backpack) HasFreeSpace(Bulk, Mass int) bool {
totalBulk, totalMass := 0, 0 totalBulk, totalMass := 0, 0
for i, _ := range b.items { for i, _ := range b.items {
tmp := Controller.GetComponent(b.items[i], Carried{}.Type()).(Carried) tmp := Controller.GetComponent(b.items[i], Carried{}.Type()).(Carried)
@ -28,12 +22,14 @@ func (b *Backpack) HasFreeSpace(Bulk, Mass int) error {
totalMass += carried.Mass totalMass += carried.Mass
} }
if totalMass >= b.MaxMass { if totalMass >= b.MaxMass {
return ErrorInvTooHeavy //fixme return message along - 'too heavy'
return false
} }
if totalBulk >= b.MaxBulk { if totalBulk >= b.MaxMass {
return ErrorInvTooBulky //fixme return message along - 'doesnt fit to your backpack'
return false
} }
return nil return true
} }
func (b *Backpack) GetItems() []ecs.Entity { func (b *Backpack) GetItems() []ecs.Entity {

View File

@ -1,7 +1,6 @@
package items package items
import ( import (
"fmt"
"lab.zaar.be/thefish/alchemyst-go/engine/ecs" "lab.zaar.be/thefish/alchemyst-go/engine/ecs"
"lab.zaar.be/thefish/alchemyst-go/engine/types" "lab.zaar.be/thefish/alchemyst-go/engine/types"
) )
@ -20,33 +19,32 @@ func (c Carried) Type() string {
return ecs.CarriedComponent return ecs.CarriedComponent
} }
func (c Carried) Pickup(who, what ecs.Entity) error { func (c Carried) Pickup(who, what ecs.Entity) {
// check if im lying on ground // check if im lying on ground
if !Controller.HasComponent(what, ecs.CoordsComponent) { if !Controller.HasComponent(what, ecs.CoordsComponent) {
return fmt.Errorf("bug! item with no coords?!") return
} }
// something inexistent on map trying to pickup an item?! // something inexistent on map trying to pickup an item?!
if !Controller.HasComponent(who, ecs.CoordsComponent) { if !Controller.HasComponent(who, ecs.CoordsComponent) {
//todo log error - investigate this situation //todo log error - investigate this situation
return fmt.Errorf("bug! actor with no coords?!") return
} }
//check if who and what are in adjacent tiles //check if who and what are on the same tile
whoCoords := Controller.GetComponent(who, ecs.CoordsComponent).(types.Coords) whoCoords := Controller.GetComponent(who, ecs.CoordsComponent).(types.Coords)
whatCoords := Controller.GetComponent(what, ecs.CoordsComponent).(types.Coords) whatCoords := Controller.GetComponent(what, ecs.CoordsComponent).(types.Coords)
if !whoCoords.IsAdjacentTo(&whatCoords) { if whoCoords != whatCoords {
//todo log error - something strange happened //todo log error - something strange happened
return fmt.Errorf("bug! actor and item in inadjacent coords?!") return
} }
//does not have inventory? //does not have inventory?
if !Controller.HasComponent(who, ecs.BackpackComponent) { if !Controller.HasComponent(who, ecs.BackpackComponent) {
//todo send message - you cant carry items //todo send message - you cant carry items
return fmt.Errorf("bug! actor cannot carry items") return
} }
bp := Controller.GetComponent(who, Backpack{}.Type()).(Backpack) bp := Controller.GetComponent(who, Backpack{}.Type()).(Backpack)
err := bp.HasFreeSpace(c.Bulk, c.Mass) if !bp.HasFreeSpace(c.Bulk, c.Mass) {
if err != nil {
//todo send message - does not fit to your inventory //todo send message - does not fit to your inventory
return err return
} }
//do not remove appearance //do not remove appearance
//remove coords instead (does not exist on map anymore) //remove coords instead (does not exist on map anymore)
@ -54,7 +52,6 @@ func (c Carried) Pickup(who, what ecs.Entity) error {
bp.items = append(bp.items, what) bp.items = append(bp.items, what)
//fuck that, we need to update constantly //fuck that, we need to update constantly
Controller.UpdateComponent(who, ecs.BackpackComponent, bp) Controller.UpdateComponent(who, ecs.BackpackComponent, bp)
return nil
} }
func (c Carried) Drop(who, what ecs.Entity) { func (c Carried) Drop(who, what ecs.Entity) {
@ -93,14 +90,13 @@ func (c *Carried) GetBulk(what ecs.Entity) int {
} }
func FindCarriedUnder(who ecs.Entity) []ecs.Entity { func FindCarriedUnder(who ecs.Entity) []ecs.Entity {
pickerCoords := Controller.GetComponent(who, ecs.CoordsComponent).(types.Coords) coords := Controller.GetComponent(who, ecs.CoordsComponent).(types.Coords)
// _И_ носимые _И_ имеющие координаты, т.е. где-то лежащие carrieds := Controller.GetEntitiesWithComponent(ecs.CarriedComponent)
carrieds := Controller.GetEntitiesWithComponent(ecs.CarriedComponent, ecs.CoordsComponent)
result := make([]ecs.Entity, 0) result := make([]ecs.Entity, 0)
for _, carried := range carrieds { for _, ent := range carrieds {
carriedCoords := Controller.GetComponent(carried, ecs.CoordsComponent).(types.Coords) car := Controller.GetComponent(ent, ecs.CoordsComponent)
if pickerCoords.IsAdjacentTo(&carriedCoords) { if car == coords {
result = append(result, carried) result = append(result, ent)
} }
} }
return result return result

View File

@ -1,7 +1,5 @@
package itemprops package itemprops
import "github.com/shopspring/decimal"
//MedicalSystem организм //MedicalSystem организм
// Humanoid // Humanoid
// Circuits // Circuits
@ -57,95 +55,94 @@ import "github.com/shopspring/decimal"
// -> [right] joint -> leg -> joint -> hip -> joint -> foot -> 5 x finger // -> [right] joint -> leg -> joint -> hip -> joint -> foot -> 5 x finger
// -> head -> joint -> jaw // -> head -> joint -> jaw
// MedicalSystem Организм //MedicalSystem Организм
type MedicalSystem struct { type MedicalSystem struct {
BasePart BodyPart BasePart BodyPart
} }
// MedicalCircuit Система обращения //MedicalCircuit Система обращения
type MedicalCircuit struct { type MedicalCircuit struct {
Provides MedicalAbility Provides MedicalAbility
DependsOn Organ DependsOn Organ
Vessel MedicalVessel Vessel MedicalVessel
Contains []Organ Contains []Organ
} }
// MedicalVessel кровь, желчь, пульпа, воздух, еда //MedicalVessel кровь, желчь, пульпа, воздух, еда
type MedicalVessel struct { type MedicalVessel struct {
Name string Name string
Material Material
Pressure decimal.Decimal //Pressure давление, kg / m3 Pressure DimensionItemDensity
} }
// BodyPart часть тела //BodyPart часть тела
type BodyPart struct { type BodyPart struct {
LayerExtra MedicalMaterial LayerExtra MedicalMaterial
LayerOuter MedicalMaterial LayerOuter MedicalMaterial
LayerMiddle MedicalMaterial LayerMiddle MedicalMaterial
LayerInner MedicalMaterial LayerInner MedicalMaterial
Size DimensionItemSize Joints []Joint
Joints []Joint Contains []InnerOrgan
Contains []InnerOrgan Exposes []OuterOrgan
Exposes []OuterOrgan
} }
// Joint суставы, к чему и что крепится //Joint суставы, к чему и что крепится
type Joint struct { type Joint struct {
Name string Name string
ConnectsFrom BodyPart ConnectsFrom BodyPart
ConnectsTo BodyPart ConnectsTo BodyPart
} }
type Organ struct { type Organ struct {
Name string Name string
Material Material
} }
// InnerOrgan ливер, селезёнка, сердце, кишки итп //InnerOrgan ливер, селезёнка, сердце, кишки итп
type InnerOrgan struct { type InnerOrgan struct {
Organ Organ
DependsOn MedicalCircuit DependsOn MedicalCircuit
BelongsTo MedicalCircuit BelongsTo MedicalCircuit
} }
// OuterOrgan глаза, уши, волосы, когти итп //OuterOrgan глаза, уши, волосы, когти итп
type OuterOrgan struct { type OuterOrgan struct {
Organ Organ
DependsOn MedicalCircuit DependsOn MedicalCircuit
BelongsTo MedicalCircuit BelongsTo MedicalCircuit
} }
// слой части тела - кожа/чешуя/роговые пластины/хитиновый панцирь, жир, мускулы, кости //слой части тела - кожа/чешуя/роговые пластины/хитиновый панцирь, жир, мускулы, кости
type MedicalMaterial struct { type MedicalMaterial struct {
Name string Name string
Material Material
MedicalSystemFlags MedicalSystemFlags
} }
// @todo заменить на Medical Circuit //@todo заменить на Medical Circuit
type MedicalSystemFlags struct { type MedicalSystemFlags struct {
//Structural является ли опорным аппаратом //Structural является ли опорным аппаратом
Structural bool Structural bool
//Содежит ли кровь/ихор/ //Содежит ли кровь/ихор/
MajorVeins bool //вход на мотор, сломаешь - быстро выйдет из строя если будет двигаться MajorVeins bool //вход на мотор, сломаешь - быстро выйдет из строя если будет двигаться
MajorArteria bool //выход, то же самое + высокое давление MajorArteria bool //выход, то же самое + высокое давление
Veins bool //вход на мотор Veins bool //вход на мотор
Arteria bool //выход из мотора, высокое давление Arteria bool //выход из мотора, высокое давление
MajorNerve bool //повредишь - ниже по суставам не работает MajorNerve bool //повредишь - ниже по суставам не работает
NerveTissue bool //повредишь - ниже по суставамс болит NerveTissue bool //повредишь - ниже по суставамс болит
OxygenTube bool //трахея OxygenTube bool //трахея
OxygenPump bool //лёгкое OxygenPump bool //лёгкое
BloodPump bool //мотор BloodPump bool //мотор
ContainsCongestionLiquid bool ContainsCongestionLiquid bool
IsMainCongestionPump bool IsMainCongestionPump bool
} }
// MedicalAbility спсобность есть, стоять, не терять равновесие, дышать, выздоравливать, лечить свои органы, видеть итп //MedicalAbility спсобность есть, стоять, не терять равновесие, дышать, выздоравливать, лечить свои органы, видеть итп
type MedicalAbility string type MedicalAbility string

View File

@ -1,215 +0,0 @@
package itemprops
import (
"encoding/json"
"os"
"testing"
"time"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/shopspring/decimal"
"github.com/stretchr/testify/suite"
)
type IpTestSuite struct {
suite.Suite
}
// метод для установки тестовых данных для всего набора, запускается до всего прочего
func (suite *IpTestSuite) SetupSuite() {
}
// метод для сноса наделанного тестом до идеально чистого состояния, запускается после всего прочего
func (suite *IpTestSuite) TearDownSuite() {
}
// запускается перед каждым тестом
func (suite *IpTestSuite) SetupTest() {}
// запускается после каждого теста
func (suite *IpTestSuite) TearDownTest() {}
func TestApi(t *testing.T) {
tests := new(IpTestSuite)
suite.Run(t, tests)
}
func (suite *IpTestSuite) TestMaterialWeightAndVolume() {
// плотность https://tekkos.ru/katalog/poleznaya-informatsiya/tablica-plotnosti-stali-kg-m3.html
// ударная вязкость https://nposanef.ru/DOCUMENTS/PB-03-605-03/PB-03-605-03_Tab-2.6.pdf
// температура плавления http://zaozmi.ru/polezno/temperatura_plavleniya_metallov.html
// температура кипения http://temperatures.ru/pages/temperatura_plavleniya_i_kipeniya
// Пределы прочности некоторых материалов https://sevparitet.ru/raznoe/koefficient-uprugosti-tablica.html
metalMaterialFlags := MaterialFlags{
ConductsElictricity: true,
BlocksLiquid: true,
AcidResistant: true,
BlocksGas: true,
Flammable: false,
ConductsHeat: true,
Radiates: true,
}
woodMaterialFlags := MaterialFlags{
ConductsElictricity: false,
BlocksLiquid: true,
AcidResistant: false,
BlocksGas: true,
Flammable: true,
ConductsHeat: false,
Radiates: false,
}
teststeel := Material{
Id: "teststeel",
Name: "steel",
Flags: metalMaterialFlags,
Density: DimensionItemDensity{decimal.NewFromInt(7800)},
FractureToughness: DimensionFractureToughness{decimal.NewFromInt(30)},
MeltingPoint: DimensionItemNullTemperature{decimal.NullDecimal{Decimal: decimal.NewFromInt(1400), Valid: true}},
BoilingPoint: DimensionItemNullTemperature{decimal.NullDecimal{Decimal: decimal.NewFromInt(3200), Valid: true}},
}
testOakWood := Material{
Id: "testoakwood",
Name: "oakwood",
Flags: woodMaterialFlags,
Density: DimensionItemDensity{decimal.NewFromInt(700)},
FractureToughness: DimensionFractureToughness{decimal.NewFromFloat(4.5)},
MeltingPoint: DimensionItemNullTemperature{decimal.NullDecimal{Decimal: decimal.NewFromInt(600), Valid: true}}, //загорается при 600 град Цельсия
}
testCube := ItemPhysics{
Material: teststeel,
DimensionItemRigidity: DimensionItemRigidity{decimal.NewFromInt(65)},
DimensionItemSize: DimensionItemSize{
Width: decimal.NewFromFloat(0.1),
Height: decimal.NewFromFloat(0.1),
Depth: decimal.NewNullDecimal(decimal.NewFromFloat(0.1)),
Thickness: decimal.NullDecimal{},
},
DimensionItemTemperature: DimensionItemTemperature{decimal.NewFromInt(20)},
}
suite.Equal(decimal.NewFromFloat(7.8).String(), testCube.Weight().String())
suite.Equal(decimal.NewFromFloat(0.001).String(), testCube.Volume().String())
testOakCube := ItemPhysics{
Material: testOakWood,
DimensionItemRigidity: DimensionItemRigidity{decimal.NewFromInt(4)},
DimensionItemSize: DimensionItemSize{
Width: decimal.NewFromFloat(0.1),
Height: decimal.NewFromFloat(0.1),
Depth: decimal.NewNullDecimal(decimal.NewFromFloat(0.1)),
Thickness: decimal.NullDecimal{},
},
DimensionItemTemperature: DimensionItemTemperature{decimal.NewFromInt(20)},
}
//oakwood is ~10 times lighter than steel
suite.Equal(decimal.NewFromFloat(0.7).String(), testOakCube.Weight().String())
suite.Equal(decimal.NewFromFloat(0.001).String(), testOakCube.Volume().String())
testCuirass := ItemPhysics{
Material: teststeel,
DimensionItemRigidity: DimensionItemRigidity{decimal.NewFromInt(55)},
DimensionItemSize: DimensionItemSize{
Width: decimal.NewFromFloat(0.5), //60 cm wide
Height: decimal.NewFromFloat(0.8), //80 cm high
Depth: decimal.NewNullDecimal(decimal.NewFromFloat(0.4)), //50 cm deep
Thickness: decimal.NewNullDecimal(decimal.NewFromFloat(0.001)), // 1mm thick
},
DimensionItemTemperature: DimensionItemTemperature{},
}
//12.1992 kg HEAVY ARMOR IS HEAVY
suite.Equal(decimal.NewFromFloat(12.1992).String(), testCuirass.Weight().String())
//0.001564 m3 of steel
suite.Equal(decimal.NewFromFloat(0.001564).String(), testCuirass.Volume().String())
}
func (suite *IpTestSuite) TestMaterialSerialization() {
metalMaterialFlags := MaterialFlags{
ConductsElictricity: true,
BlocksLiquid: true,
AcidResistant: true,
BlocksGas: true,
Flammable: false,
ConductsHeat: true,
Radiates: true,
}
woodMaterialFlags := MaterialFlags{
ConductsElictricity: false,
BlocksLiquid: true,
AcidResistant: false,
BlocksGas: true,
Flammable: true,
ConductsHeat: false,
Radiates: false,
}
teststeel := Material{
Id: "teststeel",
Name: "steel",
Flags: metalMaterialFlags,
Density: DimensionItemDensity{decimal.NewFromInt(7800)},
FractureToughness: DimensionFractureToughness{decimal.NewFromInt(30)},
MeltingPoint: DimensionItemNullTemperature{decimal.NullDecimal{Decimal: decimal.NewFromInt(1400), Valid: true}},
BoilingPoint: DimensionItemNullTemperature{decimal.NullDecimal{Decimal: decimal.NewFromInt(3200), Valid: true}},
}
testOakWood := Material{
Id: "testoakwood",
Name: "oakwood",
Flags: woodMaterialFlags,
Density: DimensionItemDensity{decimal.NewFromInt(700)},
FractureToughness: DimensionFractureToughness{decimal.NewFromFloat(4.5)},
MeltingPoint: DimensionItemNullTemperature{decimal.NullDecimal{Decimal: decimal.NewFromInt(600), Valid: true}}, //загорается при 600 град Цельсия
}
bytes, err := json.Marshal(teststeel)
suite.NoError(err)
suite.Equal(
`{"id":"teststeel","name":"steel","material_flags":{"conducts_elictricity":true,"blocks_liquid":true,"acid_resistant":true,"blocks_gas":true,"flammable":false,"conducts_heat":true,"radiates":true},"density":"7800","fracture_toughness":"30","melting_point":"1400","boiling_point":"3200"}`,
string(bytes),
)
bytes, err = json.Marshal(testOakWood)
suite.NoError(err)
suite.Equal(`{"id":"testoakwood","name":"oakwood","material_flags":{"conducts_elictricity":false,"blocks_liquid":true,"acid_resistant":false,"blocks_gas":true,"flammable":true,"conducts_heat":false,"radiates":false},"density":"700","fracture_toughness":"4.5","melting_point":"600","boiling_point":null}`,
string(bytes),
)
}
func (suite *IpTestSuite) TestMaterialDeserialization() {
logger := log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339})
mm, err := NewMaterialMap("../../..", logger)
suite.NoError(err)
metalMaterialFlags := MaterialFlags{
ConductsElictricity: true,
BlocksLiquid: true,
AcidResistant: true,
BlocksGas: true,
Flammable: false,
ConductsHeat: true,
Radiates: true,
}
teststeel := Material{
Id: "steel",
Name: "steel",
Flags: metalMaterialFlags,
Density: DimensionItemDensity{decimal.NewFromInt(7800)},
FractureToughness: DimensionFractureToughness{decimal.NewFromInt(30)},
MeltingPoint: DimensionItemNullTemperature{decimal.NullDecimal{Decimal: decimal.NewFromInt(1400), Valid: true}},
BoilingPoint: DimensionItemNullTemperature{decimal.NullDecimal{Decimal: decimal.NewFromInt(3200), Valid: true}},
}
loadedsteel := mm["steel"]
suite.Equalf(teststeel, *loadedsteel, "error: %s")
_ = mm
}

View File

@ -3,54 +3,35 @@ package itemprops
import "github.com/shopspring/decimal" import "github.com/shopspring/decimal"
type ItemPhysics struct { type ItemPhysics struct {
Material `json:"material"` Material Material
//DimensionItemRigidity жёсткость (самого предмета а не материала), мера прочности, Rigidity DimensionItemRigidity
//уменьшается со временем, может зависеть от тепмературы Size DimensionItemSize
DimensionItemRigidity `json:"rigidity"` Temperature DimensionItemTemperature
DimensionItemSize `json:"size"`
DimensionItemTemperature `json:"temperature"`
//CubeFactor фактор близости куб <-> сфера по площади поверхности, чем ближе к 0, тем ближе к сфере,
//100 - куб, >100 - для суперсложных (шипастых например) поверхностей
CubeFactor decimal.NullDecimal `json:"cube_factor,omitempty"`
} }
// DimensionItemSize length in m (1 mm = 1/1000 of 1m) //DimensionItemSize length in m (1 mm = 1/1000 of 1m)
type DimensionItemSize struct { type DimensionItemSize struct {
Width decimal.Decimal `json:"width"` Width decimal.Decimal
Height decimal.Decimal `json:"height"` Height decimal.Decimal
// if item is solid - depth in m (1mm = 1/1000 of m) // if item is solid - depth in m (1mm = 1/1000 of m)
Depth decimal.NullDecimal `json:"depth,omitempty"` Depth decimal.NullDecimal
//Thickness if item is hollow - thickness of outer item shell, ie for armor, in m (1 mm = 1/1000 of m) //Thickness if item is hollow - thickness of outer item shell, ie for armor, in m (1 mm = 1/1000 of m)
Thickness decimal.NullDecimal `json:"thickness,omitempty"` Thickness decimal.NullDecimal
} }
// Area is frontal area //Area is frontal area
func (d *DimensionItemSize) Area() decimal.Decimal { func (d *DimensionItemSize) Area() decimal.Decimal {
return d.Width.Mul(d.Height) return d.Width.Mul(d.Height)
} }
func (ip *ItemPhysics) Weight() decimal.Decimal { //DimensionItemDensity density in kg/m3
return ip.Material.Density.Mul(ip.Volume()) type DimensionItemDensity decimal.Decimal
}
func (d *ItemPhysics) Volume() decimal.Decimal { //DimensionItemRigidity rigidity жёсткость, способность твёрдого тела, конструкции или её элементов сопротивляться деформации in N/m
v := d.Width.Mul(d.Height) type DimensionItemRigidity decimal.Decimal
//есть глубина
if !d.Depth.Decimal.IsZero() { //NotchFractureToughness ударная вязкость по Шарпи, Дж (надо ли?)
v = v.Mul(d.Depth.Decimal) type NotchFractureToughness decimal.Decimal
//пустотелый
if !d.Thickness.Decimal.IsZero() { //DimensionItemTemperature in celsius, -273 to 10000
surfaceArea := d.Width.Mul(d.Height).Mul(decimal.NewFromInt(2)). type DimensionItemTemperature decimal.Decimal
Add(d.Height.Mul(d.Depth.Decimal).Mul(decimal.NewFromInt(2))).
Add(d.Width.Mul(d.Depth.Decimal).Mul(decimal.NewFromInt(2)))
surfaceAreaCoeff := 0.85
if !d.CubeFactor.Decimal.IsZero() {
surfaceAreaCoeff = surfaceAreaCoeff * float64(int(d.CubeFactor.Decimal.IntPart())/100)
}
v = surfaceArea.Mul(d.Thickness.Decimal).
//волюнтаристский коэффт отличия поверхности от куба, см https/en.wikipedia.org/wiki/Volume-to-surface_area_ratio
Mul(decimal.NewFromFloat(surfaceAreaCoeff))
}
}
return v
}

View File

@ -1,35 +0,0 @@
package itemprops
import "github.com/shopspring/decimal"
// плотность https://tekkos.ru/katalog/poleznaya-informatsiya/tablica-plotnosti-stali-kg-m3.html
// ударная вязкость https://nposanef.ru/DOCUMENTS/PB-03-605-03/PB-03-605-03_Tab-2.6.pdf
// температура плавления http://zaozmi.ru/polezno/temperatura_plavleniya_metallov.html
// температура кипения http://temperatures.ru/pages/temperatura_plavleniya_i_kipeniya
// Пределы прочности некоторых материалов https://sevparitet.ru/raznoe/koefficient-uprugosti-tablica.html
// DimensionItemDensity density in kg/m3
type DimensionItemDensity struct {
decimal.Decimal
}
// DimensionItemRigidity rigidity жёсткость, способность твёрдого тела, конструкции или её элементов сопротивляться деформации in N/m
type DimensionItemRigidity struct {
decimal.Decimal
}
// DimensionFractureToughness ударная вязкость по Шарпи, Дж (надо ли?)
// Ударная вязкость, мера скорости поглощения энергии без деформаций, джоули на квадратный метр в секунду
type DimensionFractureToughness struct {
decimal.Decimal
}
// DimensionItemTemperature in celsius, -273 to 10000
type DimensionItemTemperature struct {
decimal.Decimal
}
// DimensionItemTemperature in celsius, -273 to 10000
type DimensionItemNullTemperature struct {
decimal.NullDecimal
}

View File

@ -1,117 +1,20 @@
package itemprops package itemprops
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/rs/zerolog"
)
type Material struct { type Material struct {
Id string `json:"id"` Name string
Name string `json:"name"` Flags MaterialFlags
Flags MaterialFlags `json:"material_flags"` Density DimensionItemDensity
Density DimensionItemDensity `json:"density"` FractureToughness NotchFractureToughness
FractureToughness DimensionFractureToughness `json:"fracture_toughness"` MeltingPoint DimensionItemTemperature
MeltingPoint DimensionItemNullTemperature `json:"melting_point,omitempty"` BoilingPoint DimensionItemTemperature
BoilingPoint DimensionItemNullTemperature `json:"boiling_point,omitempty"`
} }
func (m Material) Unmarshal() {}
type MaterialFlags struct { type MaterialFlags struct {
ConductsElictricity bool `json:"conducts_elictricity"` ConductsElictricity bool
BlocksLiquid bool `json:"blocks_liquid"` BlocksLiquid bool
AcidResistant bool `json:"acid_resistant"` AcidResistant bool
BlocksGas bool `json:"blocks_gas"` BlocksGas bool
Flammable bool `json:"flammable"` Flammable bool
ConductsHeat bool `json:"conducts_heat"` ConductsHeat bool
Radiates bool `json:"radiates"` Radiates bool
}
type MaterialMap map[string]*Material
type tt map[string]MaterialMap
func NewMaterialMap(path string, logger zerolog.Logger) (MaterialMap, error) {
mm := make(MaterialMap)
tmp := make(map[string][]interface{})
flags := make(map[string]*MaterialFlags)
err := filepath.Walk(path+"/assets/materials",
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if strings.HasSuffix(path, ".json") {
splt := strings.Split(path, "/")
logger.Info().Msgf("loading %s %d", splt[len(splt)-1], info.Size())
bytes, err := os.ReadFile(path)
if err != nil {
return err
}
ttmp := make(map[string][]interface{})
err = json.Unmarshal(bytes, &ttmp)
if err != nil {
return err
}
for idx := range ttmp {
tmp[idx] = append(tmp[idx], ttmp[idx]...)
}
}
return nil
})
_ = flags
if lst, ok := tmp["material_flags"]; ok {
for _, item := range lst {
ttt := item.(map[string]interface{})
_ = ttt
for clause, item2 := range ttt {
bts, err := json.Marshal(item2)
if err != nil {
return nil, fmt.Errorf("Could not marshal back:%w", err)
}
flags[clause] = &MaterialFlags{}
err = json.Unmarshal(bts, flags[clause])
if err != nil {
return nil, fmt.Errorf("Could not unmarshal to material_flags:%w", err)
}
}
}
}
logger.Info().Msgf("loaded %d material flag sets", len(flags))
if lst, ok := tmp["materials"]; ok {
for _, item := range lst {
ttt := item.(map[string]interface{})
_ = ttt
for clause, item2 := range ttt {
toReplace := item2.(map[string]interface{})["material_flags"]
//todo generalize
if ref, ok := toReplace.(map[string]interface{})["$ref"]; ok {
rfs := strings.Split(ref.(string), "/")
referredFlag := rfs[len(rfs)-1]
item2.(map[string]interface{})["material_flags"] = flags[referredFlag]
}
bts, err := json.Marshal(item2)
if err != nil {
return nil, fmt.Errorf("Could not marshal back:%w", err)
}
mm[clause] = &Material{Id: clause}
err = json.Unmarshal(bts, mm[clause])
if err != nil {
return nil, fmt.Errorf("Could not unmarshal to material_flags:%w", err)
}
}
}
}
return mm, err
} }

View File

@ -50,7 +50,7 @@ func (ms MatterState) Change(from MatterState, to MatterState) bool {
return false return false
}(transitions[from], to) }(transitions[from], to)
if !newStateFound { if !newStateFound {
log.Warn().Msgf(`Transition %d -> %d is impossible`, from, to) log.Warn().Msgf("Transition %s -> %s is impossible", from, to)
return false return false
} }
// check temperatures/conditions, see template // check temperatures/conditions, see template
@ -79,4 +79,4 @@ func (ms MatterState) Change(from MatterState, to MatterState) bool {
return true return true
} }

View File

@ -23,11 +23,11 @@ func (mov Moveable) Walk() {
//fixme change it to WhatsOnTile //fixme change it to WhatsOnTile
func (mov Moveable) IsBlocked(c types.Coords) bool { func (mov Moveable) IsBlocked(c types.Coords) bool {
if mov.Level.GetTile(c).BlocksPass { if mov.Level.GetTile(c).BlocksPass == true {
return true return true
} }
list := mov.Controller.GetEntitiesWithComponent(ecs.MobComponent) list := mov.Controller.GetEntitiesWithComponent(ecs.MobComponent)
for idx := range list { for idx, _ := range list {
coords := mov.Controller.GetComponent(list[idx], ecs.CoordsComponent) coords := mov.Controller.GetComponent(list[idx], ecs.CoordsComponent)
if coords == nil { if coords == nil {
continue continue
@ -50,7 +50,7 @@ func (mov Moveable) IsBlocked(c types.Coords) bool {
func Walk(entity ecs.Entity, state *gamestate.GameState, dx, dy int) { func Walk(entity ecs.Entity, state *gamestate.GameState, dx, dy int) {
controller := state.Controller controller := state.Controller
coords := controller.GetComponent(state.Player, ecs.CoordsComponent).(types.Coords) coords := controller.GetComponent(state.Player, ecs.CoordsComponent).(types.Coords)
newCoords := types.Coords{X: coords.X + dx, Y: coords.Y + dy} newCoords := types.Coords{coords.X + dx, coords.Y + dy}
if !state.Level.InBounds(newCoords) { if !state.Level.InBounds(newCoords) {
return return
} }

View File

@ -1,16 +1,15 @@
package screens package screens
import ( import (
"context" "context"
"fmt" "fmt"
"strings" "lab.zaar.be/thefish/alchemyst-go/effects"
"lab.zaar.be/thefish/alchemyst-go/engine/ecs"
"lab.zaar.be/thefish/alchemyst-go/effects" "lab.zaar.be/thefish/alchemyst-go/engine/gamestate"
"lab.zaar.be/thefish/alchemyst-go/engine/ecs" "lab.zaar.be/thefish/alchemyst-go/engine/types"
"lab.zaar.be/thefish/alchemyst-go/engine/gamestate" "lab.zaar.be/thefish/alchemyst-go/ui/mainwindow"
"lab.zaar.be/thefish/alchemyst-go/engine/types" "lab.zaar.be/thefish/alchemyst-go/util/appctx"
"lab.zaar.be/thefish/alchemyst-go/ui/mainwindow" "strings"
"lab.zaar.be/thefish/alchemyst-go/util/appctx"
) )
type DevmenuScreen struct { type DevmenuScreen struct {
@ -70,7 +69,7 @@ func (devm *DevmenuScreen) HandleInput(input string) {
level.Tiles[idx].Visible = true level.Tiles[idx].Visible = true
level.Tiles[idx].Explored = true level.Tiles[idx].Explored = true
} }
appctx.Logger().Warn().Msg("making everything visible!") appctx.Logger(devm.ctx).Warn().Msg("making everything visible!")
devm.scm.SetScreen(devm.scm.PreviousScreen) devm.scm.SetScreen(devm.scm.PreviousScreen)
break break
case "p": case "p":

View File

@ -1,6 +1,7 @@
package screens package screens
import ( import (
"context"
"lab.zaar.be/thefish/alchemyst-go/engine/ecs" "lab.zaar.be/thefish/alchemyst-go/engine/ecs"
"lab.zaar.be/thefish/alchemyst-go/engine/ecs/systems" "lab.zaar.be/thefish/alchemyst-go/engine/ecs/systems"
"lab.zaar.be/thefish/alchemyst-go/engine/fov" "lab.zaar.be/thefish/alchemyst-go/engine/fov"
@ -14,6 +15,7 @@ import (
) )
type GameScreen struct { type GameScreen struct {
ctx context.Context
mw *mainwindow.MainWindow mw *mainwindow.MainWindow
state *gamestate.GameState state *gamestate.GameState
vp *mainwindow.ViewPort vp *mainwindow.ViewPort
@ -22,8 +24,9 @@ type GameScreen struct {
fov fov.Fov fov fov.Fov
} }
func NewGameScreen(mw *mainwindow.MainWindow, state *gamestate.GameState, viewPort *mainwindow.ViewPort, controller *ecs.Controller, scm *types.ScreenManager) *GameScreen { func NewGameScreen(ctx context.Context, mw *mainwindow.MainWindow, state *gamestate.GameState, viewPort *mainwindow.ViewPort, controller *ecs.Controller, scm *types.ScreenManager) *GameScreen {
ts := &GameScreen{ ts := &GameScreen{
ctx: ctx,
mw: mw, mw: mw,
state: state, state: state,
vp: viewPort, vp: viewPort,
@ -46,7 +49,7 @@ func (ts *GameScreen) UseEcs() bool { return true }
func (ts *GameScreen) Enter() { func (ts *GameScreen) Enter() {
ts.mw.GetLayer("overlay").ClearArea(0, ts.mw.H-3, 30, 3) ts.mw.GetLayer("overlay").ClearArea(0, ts.mw.H-3, 30, 3)
ts.mw.GetLayer("overlay").WithColor("#77777777"). ts.mw.GetLayer("overlay").WithColor("#77777777").
Print(ts.mw.W - 17 , 1, "Press [color=white]?[/color] for help") Print(1, ts.mw.H-2, "Press [color=white]?[/color] for help")
} }
func (ts *GameScreen) Exit() { func (ts *GameScreen) Exit() {
//trs := ts.controller.GetSystem(ecs.LevelRenderSystem) //trs := ts.controller.GetSystem(ecs.LevelRenderSystem)
@ -55,7 +58,6 @@ func (ts *GameScreen) Exit() {
//remove what we dont need //remove what we dont need
} }
//fixme kry names to action constants!
func (ts *GameScreen) HandleInput(input string) { func (ts *GameScreen) HandleInput(input string) {
//ts.state.Do(func(){ //ts.state.Do(func(){
switch input { switch input {
@ -98,22 +100,13 @@ func (ts *GameScreen) HandleInput(input string) {
} //do nothing } //do nothing
//select if there is more than 1 //select if there is more than 1
if len(carrieds) > 1 { if len(carrieds) > 1 {
appctx.Logger().Warn().Msg("Passing item list to inventory not implemented yet") appctx.Logger(ts.ctx).Warn().Msg("Passing item list to inventory not implemented yet")
} else { } else {
//call pickup in selected //call pickup in selected
cc := items.Controller.GetComponent(carrieds[0], ecs.CarriedComponent).(items.Carried) cc := items.Controller.GetComponent(carrieds[0], ecs.CarriedComponent).(items.Carried)
err := items.Carried.Pickup(cc, ts.state.Player, carrieds[0]) 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; break;
case "i": case "i":

View File

@ -63,8 +63,6 @@ func (is *InventoryScreen) Enter() {
is.prepared.Prepare(is) is.prepared.Prepare(is)
} }
//fixme key names to action constants!
//fixme unify scrolling controls!
func (is *InventoryScreen) HandleInput(input string) { func (is *InventoryScreen) HandleInput(input string) {
if strings.Contains(string(runeIndex), strings.Replace(input, "Shift+", "", -1)) { if strings.Contains(string(runeIndex), strings.Replace(input, "Shift+", "", -1)) {
if strings.Contains("Shift+", input) { if strings.Contains("Shift+", input) {
@ -78,7 +76,7 @@ func (is *InventoryScreen) HandleInput(input string) {
return return
} }
switch input { switch input {
case "Up", "k": case "Up":
is.cursor = is.cursor - 1 is.cursor = is.cursor - 1
if is.cursor < 0 { if is.cursor < 0 {
is.cursor = 0 is.cursor = 0
@ -90,7 +88,7 @@ func (is *InventoryScreen) HandleInput(input string) {
} }
} }
break break
case "Down", "j": case "Down":
is.cursor = is.cursor + 1 is.cursor = is.cursor + 1
if is.cursor >= len(is.prepared) { if is.cursor >= len(is.prepared) {
is.cursor = len(is.prepared) - 1 is.cursor = len(is.prepared) - 1
@ -121,8 +119,7 @@ func (is *InventoryScreen) HandleInput(input string) {
} }
break break
case "enter": case "enter":
//show actions menu for item under cursor //select current under cursor
//fixme implement
break; break;
case "Escape": case "Escape":
fallthrough fallthrough
@ -146,12 +143,7 @@ func (is *InventoryScreen) InventoryRender() {
footerHeight = footerHeight + 2 footerHeight = footerHeight + 2
} }
_, headerHeight := menuLayer.PrintInside(is.Rect, is.header, blt.TK_ALIGN_LEFT) _, headerHeight := menuLayer.PrintInside(is.Rect, is.header, blt.TK_ALIGN_LEFT)
itemField := types.Rect{ itemField := types.Rect{is.X, is.Y + headerHeight + 1, is.W, is.H - headerHeight - footerHeight}
X: is.X,
Y: is.Y + headerHeight + 1,
W: is.W,
H: is.H - headerHeight - footerHeight,
}
_ = itemField _ = itemField
is.pageSize = itemField.H - 2 is.pageSize = itemField.H - 2

View File

@ -1,166 +1,166 @@
package screens package screens
import ( import (
"fmt" "fmt"
"lab.zaar.be/thefish/alchemyst-go/engine/types" "lab.zaar.be/thefish/alchemyst-go/engine/types"
"lab.zaar.be/thefish/alchemyst-go/ui/mainwindow" "lab.zaar.be/thefish/alchemyst-go/ui/mainwindow"
blt "lab.zaar.be/thefish/bearlibterminal" blt "lab.zaar.be/thefish/bearlibterminal"
"strings" "strings"
) )
const runeIndex = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" const runeIndex = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
type MenuScreen struct { type MenuScreen struct {
types.Rect types.Rect
mw *mainwindow.MainWindow mw *mainwindow.MainWindow
scm *types.ScreenManager scm *types.ScreenManager
renderParent bool renderParent bool
items []interface{} items []interface{}
offset int offset int
drawFunc func() drawFunc func()
inputFunc func(string) inputFunc func(string)
title string title string
header string header string
footer string footer string
redraw bool redraw bool
bgColor string bgColor string
fgColor string fgColor string
} }
func NewMenuScreen(mw *mainwindow.MainWindow, scm *types.ScreenManager, title, header, footer string, rect types.Rect, renderParent bool) *MenuScreen { func NewMenuScreen(mw *mainwindow.MainWindow, scm *types.ScreenManager, title, header, footer string, rect types.Rect, renderParent bool) *MenuScreen {
return &MenuScreen{ return &MenuScreen{
title: title, title: title,
header: header, header: header,
footer: footer, footer: footer,
Rect: rect, Rect: rect,
mw: mw, mw: mw,
scm: scm, scm: scm,
renderParent: renderParent, renderParent: renderParent,
} }
} }
func (ms *MenuScreen) MakeList() *MenuScreen { func (ms *MenuScreen) MakeList() *MenuScreen {
ms.drawFunc = ms.ListRender ms.drawFunc = ms.ListRender
ms.inputFunc = ms.ListHandleInput ms.inputFunc = ms.ListHandleInput
return ms return ms
} }
func (ms *MenuScreen) SetBgColor(color string) *MenuScreen { func (ms *MenuScreen) SetBgColor(color string) *MenuScreen {
ms.bgColor = color ms.bgColor = color
return ms return ms
} }
func (ms *MenuScreen) SetFgColor(color string) *MenuScreen { func (ms *MenuScreen) SetFgColor(color string) *MenuScreen {
ms.fgColor = color ms.fgColor = color
return ms return ms
} }
//fixme!! //fixme!!
func (ms *MenuScreen) SetItems(items []interface{}) *MenuScreen { func (ms *MenuScreen) SetItems(items []interface{}) *MenuScreen {
ms.items = items ms.items = items
return ms return ms
} }
func (ms *MenuScreen) UseEcs() bool { return false } func (ms *MenuScreen) UseEcs() bool { return false }
func (ms *MenuScreen) Enter() { func (ms *MenuScreen) Enter() {
ms.redraw = true ms.redraw = true
ms.offset = 0 ms.offset = 0
} }
func (ms *MenuScreen) HandleInput(input string) { func (ms *MenuScreen) HandleInput(input string) {
ms.inputFunc(input) ms.inputFunc(input)
} }
func (ms *MenuScreen) Exit() { func (ms *MenuScreen) Exit() {
menuLayer := ms.mw.GetLayer("menu") menuLayer := ms.mw.GetLayer("menu")
menuLayer.ClearRect(ms.Rect) menuLayer.ClearRect(ms.Rect)
bgLayer := ms.mw.GetLayer("menubg") bgLayer := ms.mw.GetLayer("menubg")
bgLayer.ClearRect(ms.Rect) bgLayer.ClearRect(ms.Rect)
} }
func (ms *MenuScreen) Render() { func (ms *MenuScreen) Render() {
if ms.renderParent { if ms.renderParent {
ms.scm.PreviousScreen.Render() ms.scm.PreviousScreen.Render()
} }
if ms.redraw || ms.renderParent { if (ms.redraw || ms.renderParent) {
ms.redraw = false ms.redraw = false
ms.drawFunc() ms.drawFunc()
} }
} }
func (ms *MenuScreen) ListHandleInput(input string) { func (ms *MenuScreen) ListHandleInput(input string) {
switch input { switch input {
case "Up", "k": case "Up":
ms.offset = ms.offset - 1 ms.offset = ms.offset - 1
if ms.offset < 0 { if ms.offset < 0 {
ms.offset = 0 ms.offset = 0
} }
break break
case "Down", "j": case "Down":
ms.offset = ms.offset + 1 ms.offset = ms.offset + 1
if ms.offset > len(ms.items)-1 { if ms.offset > len(ms.items)-1 {
ms.offset = len(ms.items) - 1 ms.offset = len(ms.items) - 1
} }
break break
case "Escape": case "Escape":
fallthrough fallthrough
case "Space": case "Space":
ms.scm.SetScreen(ms.scm.PreviousScreen) ms.scm.SetScreen(ms.scm.PreviousScreen)
break break
} }
} }
func (ms *MenuScreen) ListRender() { func (ms *MenuScreen) ListRender() {
menuLayer := ms.mw.GetLayer("menu") menuLayer := ms.mw.GetLayer("menu")
menuLayer.ClearRect(ms.Rect) menuLayer.ClearRect(ms.Rect)
bgLayer := ms.mw.GetLayer("menubg") bgLayer := ms.mw.GetLayer("menubg")
bgLayer.ClearRect(ms.Rect) bgLayer.ClearRect(ms.Rect)
bgLayer.WithColor(ms.bgColor).NewWindow(ms.Rect).Splash() bgLayer.WithColor(ms.bgColor).NewWindow(ms.Rect).Splash()
menuLayer.WithColor(ms.fgColor).NewWindow(ms.Rect).DoubleBordered(ms.title) menuLayer.WithColor(ms.fgColor).NewWindow(ms.Rect).DoubleBordered(ms.title)
menuLayer.Print(ms.X+(ms.W/2)-7, ms.Y+ms.H-1, "╡"+"[color=green]Space[/color] to close"+"╞") menuLayer.Print(ms.X+(ms.W/2)-7, ms.Y+ms.H-1, "╡"+"[color=green]Space[/color] to close"+"╞")
footerHeight := 0 footerHeight := 0
if ms.footer != "" { if ms.footer != "" {
_, footerHeight = menuLayer.PrintInside(ms.Rect, ms.footer, 9) _, footerHeight = menuLayer.PrintInside(ms.Rect, ms.footer, 9)
footerHeight = footerHeight + 2 footerHeight = footerHeight + 2
} }
_, headerHeight := menuLayer.PrintInside(ms.Rect, ms.header, blt.TK_ALIGN_LEFT) _, headerHeight := menuLayer.PrintInside(ms.Rect, ms.header, blt.TK_ALIGN_LEFT)
itemField := types.Rect{ms.X, ms.Y + headerHeight + 1, ms.W, ms.H - headerHeight - footerHeight} itemField := types.Rect{ms.X, ms.Y + headerHeight + 1, ms.W, ms.H - headerHeight - footerHeight}
_ = itemField _ = itemField
var ilw, ilh int var ilw, ilh int
if len(ms.items) > 0 { if (len(ms.items) > 0) {
//fixme itemfield object, scroller, inputhandler, current selected item //fixme itemfield object, scroller, inputhandler, current selected item
menuItems := make([]string, 0) menuItems := make([]string, 0)
for i := ms.offset; i < len(ms.items); i++ { for i := ms.offset; i < len(ms.items); i++ {
if string(ms.items[i].(string)) != "" { if string(ms.items[i].(string)) != "" {
menuItems = append(menuItems, ms.items[i].(string)) menuItems = append(menuItems, ms.items[i].(string))
} }
} }
ilw, ilh = menuLayer.PrintInside(itemField, strings.Join(menuItems, "\n"), blt.TK_ALIGN_LEFT) ilw, ilh = menuLayer.PrintInside(itemField, strings.Join(menuItems, "\n"), blt.TK_ALIGN_LEFT)
} }
if ilh < len(ms.items) { if ilh < len(ms.items) {
ms.drawScrollBar(menuLayer, itemField) ms.drawScrollBar(menuLayer, itemField)
} }
if ilw > itemField.W-4 { if ilw > itemField.W-4 {
fmt.Printf("Excess width of item names found! Need h-scroll of certain names") fmt.Printf("Excess width of item names found! Need h-scroll of certain names")
} }
} }
func (ms *MenuScreen) drawScrollBar(menuLayer *mainwindow.Layer, itemField types.Rect) { func (ms *MenuScreen) drawScrollBar(menuLayer *mainwindow.Layer, itemField types.Rect) {
scrollbarBg := types.NewRect(itemField.X+itemField.W-2, itemField.Y+1, 1, itemField.H-4) scrollbarBg := types.NewRect(itemField.X+itemField.W-2, itemField.Y + 1, 1, itemField.H - 4)
menuLayer.WithColor("#77000000").NewWindow(scrollbarBg).Splash() menuLayer.WithColor("#77000000").NewWindow(scrollbarBg).Splash()
//tick //tick
menuLayer.WithColor(ms.fgColor).Put( menuLayer.WithColor(ms.fgColor).Put(
scrollbarBg.X, scrollbarBg.X,
scrollbarBg.Y+int(float64(ms.offset)/float64(len(ms.items))*float64(scrollbarBg.H)), scrollbarBg.Y + int(float64(ms.offset) / float64(len(ms.items)) * float64(scrollbarBg.H)),
"⏹", "⏹",
) )
menuLayer.WithColor(ms.fgColor).Put(itemField.X+itemField.W-2, itemField.Y+scrollbarBg.H+1, "↓") menuLayer.WithColor(ms.fgColor).Put(itemField.X+itemField.W-2, itemField.Y+scrollbarBg.H + 1, "↓")
menuLayer.WithColor(ms.fgColor).Put(itemField.X+itemField.W-2, itemField.Y, "↑") menuLayer.WithColor(ms.fgColor).Put(itemField.X+itemField.W-2, itemField.Y, "↑")
} }

View File

@ -19,8 +19,6 @@ func (ts *TitleScreen) UseEcs() bool { return false }
func (ts *TitleScreen) Enter() { func (ts *TitleScreen) Enter() {
blt.Clear() blt.Clear()
} }
//fixme key names to action constants!
func (ts *TitleScreen) HandleInput(input string) { func (ts *TitleScreen) HandleInput(input string) {
switch input { switch input {
case "n": case "n":
@ -57,5 +55,5 @@ Roguebasin Libtcod Tutorial (c) 2010-2011, Jotaf Henriques
Brogue 1.3 (c) 2010 Brian Walker Brogue 1.3 (c) 2010 Brian Walker
Madness (c) 2010 hmp <humpolec@gmail.com> Madness (c) 2010 hmp <humpolec@gmail.com>
BearLibTerminal (c) Cfyz 2009-2019 <http://foo.wyrd.name/en:bearlibterminal> BearLibTerminal (c) Cfyz 2009-2019 <http://foo.wyrd.name/en:bearlibterminal>
Gogue (c) 2019 jcerise Gogue (c) jcerise
` `

View File

@ -1,6 +1,7 @@
package types package types
import ( import (
"context"
"lab.zaar.be/thefish/alchemyst-go/util/appctx" "lab.zaar.be/thefish/alchemyst-go/util/appctx"
) )
@ -13,14 +14,16 @@ type Screen interface {
} }
type ScreenManager struct { type ScreenManager struct {
ctx context.Context
Screens map[string]Screen Screens map[string]Screen
CurrentScreen Screen CurrentScreen Screen
PreviousScreen Screen PreviousScreen Screen
} }
// NewScreenManager is a convenience/constructor method to properly initialize a new ScreenManager // NewScreenManager is a convenience/constructor method to properly initialize a new ScreenManager
func NewScreenManager() *ScreenManager { func NewScreenManager(ctx context.Context) *ScreenManager {
manager := ScreenManager{ manager := ScreenManager{
ctx:ctx,
Screens: make(map[string]Screen), Screens: make(map[string]Screen),
CurrentScreen: nil, CurrentScreen: nil,
} }
@ -33,7 +36,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 // A screen with the given name does not yet exist on the ScreenManager, go ahead and add it
sm.Screens[screenName] = screen sm.Screens[screenName] = screen
} else { } else {
appctx.Logger().Warn().Msgf("A screen with name %v was already added to the ScreenManager %v!", screenName, sm) appctx.Logger(sm.ctx).Warn().Msgf("A screen with name %v was already added to the ScreenManager %v!", screenName, sm)
} }
} }
@ -46,7 +49,7 @@ func (sm *ScreenManager) RemoveScreen(screenName string, screen Screen) {
delete(sm.Screens, screenName) delete(sm.Screens, screenName)
} else { } else {
// A screen with the given name does not exist // A screen with the given name does not exist
appctx.Logger().Warn().Msgf("A screen with name %v was not found on ScreenManager %v!", screenName, sm) appctx.Logger(sm.ctx).Warn().Msgf("A screen with name %v was not found on ScreenManager %v!", screenName, sm)
} }
} }
@ -81,6 +84,6 @@ func (sm *ScreenManager) SetScreenByName(screenName string) {
sm.CurrentScreen.Enter() sm.CurrentScreen.Enter()
} else { } else {
// A screen with the given name does not exist // A screen with the given name does not exist
appctx.Logger().Warn().Msgf("A screen with name %v was not found on ScreenManager %v!", screenName, sm) appctx.Logger(sm.ctx).Warn().Msgf("A screen with name %v was not found on ScreenManager %v!", screenName, sm)
} }
} }

1
go.mod
View File

@ -6,6 +6,5 @@ require (
github.com/gammazero/deque v0.0.0-20190521012701-46e4ffb7a622 github.com/gammazero/deque v0.0.0-20190521012701-46e4ffb7a622
github.com/rs/zerolog v1.15.0 github.com/rs/zerolog v1.15.0
github.com/shopspring/decimal v1.3.1 github.com/shopspring/decimal v1.3.1
github.com/stretchr/testify v1.8.0
lab.zaar.be/thefish/bearlibterminal v0.0.0-20191018101635-dd37bbc90d77 lab.zaar.be/thefish/bearlibterminal v0.0.0-20191018101635-dd37bbc90d77
) )

16
go.sum
View File

@ -1,33 +1,19 @@
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gammazero/deque v0.0.0-20190521012701-46e4ffb7a622 h1:lxbhOGZ9pU3Kf8P6lFluUcE82yVZn2EqEf4+mWRNPV0= github.com/gammazero/deque v0.0.0-20190521012701-46e4ffb7a622 h1:lxbhOGZ9pU3Kf8P6lFluUcE82yVZn2EqEf4+mWRNPV0=
github.com/gammazero/deque v0.0.0-20190521012701-46e4ffb7a622/go.mod h1:D90+MBHVc9Sk1lJAbEVgws0eYEurY4mv2TDso3Nxh3w= github.com/gammazero/deque v0.0.0-20190521012701-46e4ffb7a622/go.mod h1:D90+MBHVc9Sk1lJAbEVgws0eYEurY4mv2TDso3Nxh3w=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.15.0 h1:uPRuwkWF4J6fGsJ2R0Gn2jB1EQiav9k3S6CSdygQJXY= github.com/rs/zerolog v1.15.0 h1:uPRuwkWF4J6fGsJ2R0Gn2jB1EQiav9k3S6CSdygQJXY=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc h1:N3zlSgxkefUH/ecsl37RWTkESTB026kmXzNly8TuZCI=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
lab.zaar.be/thefish/bearlibterminal v0.0.0-20191018101635-dd37bbc90d77 h1:ElfFSOSxp1PViWH7+iKZ8sZvEhaKN9o3vt13+hX2yaE= lab.zaar.be/thefish/bearlibterminal v0.0.0-20191018101635-dd37bbc90d77 h1:ElfFSOSxp1PViWH7+iKZ8sZvEhaKN9o3vt13+hX2yaE=
lab.zaar.be/thefish/bearlibterminal v0.0.0-20191018101635-dd37bbc90d77/go.mod h1:tV7Vxx6vf9dPgj9B+RPeSrmtRl8nTSH07HIyBSSnEc4= lab.zaar.be/thefish/bearlibterminal v0.0.0-20191018101635-dd37bbc90d77/go.mod h1:tV7Vxx6vf9dPgj9B+RPeSrmtRl8nTSH07HIyBSSnEc4=

View File

@ -1,14 +0,0 @@
1 Почему Go?
---
потому что круто. (тут бла-бла про управление памятью, многопоточность, сборку и либы)
2 Почему Libbearterminal?
---
Дьявол предложил расчесать манту.
3 Почему нет звука?
---
Бля, да лень возиться. И ни к чему это тут.

View File

@ -155,9 +155,6 @@ func (tr *TerrainRender) Render() {
... ...
} }
``` ```
TODO: троттлинг
#### Каналы состояний и их Listеner-ы #### Каналы состояний и их Listеner-ы
@ -170,7 +167,7 @@ TODO: троттлинг
- reflect в main loop. Лишь **только** выкинув рефлкесию и больше ничего не делая - я снизил потребление CPU приложением - reflect в main loop. Лишь **только** выкинув рефлкесию и больше ничего не делая - я снизил потребление CPU приложением
**вдвое**. Это удобная штука, не спорю, но пользоваться ей надо при загрузке ресурсов, при сохранении/загрузке состояния **вдвое**. Это удобная штука, не спорю, но пользоваться ей надо при загрузке ресурсов, при сохранении/загрузке состояния
приложения - т.е. при разовых операциях. Как оказалось, она _очень_ дорогая по CPU. Кто пользуется ей в main loop, ORM приложения - т.е. при разовых операциях. Как оказалось, она _очень_ дорогая по CPU. Кто пользоуется ей в main loop, ORM
и прочих нагруженных местах - да будет предан анафеме. и прочих нагруженных местах - да будет предан анафеме.

View File

@ -4,8 +4,6 @@ RLG и Golang - некоторые полезные советы
1. [Установка и некоторые особенности работы](linux_go_blt_install_quickstart.md) связки BLT + Go на Linux 1. [Установка и некоторые особенности работы](linux_go_blt_install_quickstart.md) связки BLT + Go на Linux
2. Что [стоит и НЕ стоит](go_game_dos_and_donts.md) делать с возможностями Go - +chans, +tickers, +throttling, -closures 2. Что [стоит и НЕ стоит](go_game_dos_and_donts.md) делать с возможностями Go - +chans, +tickers, +throttling, -closures
3. [Система типов](./static_types_vs_ecs.md) - нативная или ECS? На самом деле и то, и то 3. [Система типов](./static_types_vs_ecs.md) - нативная или ECS? На самом деле и то, и то
4. Немножко конкретики: [предметы и обращение с ними](./item_objecttypes_and_blueprints.md). Как правильно готовить
предметы - чтобы потом не было мучительно больно.
Дополнения Дополнения
--- ---

View File

@ -1,16 +0,0 @@
Blueprints, паттерн Object Type и сериализация
==
Посмотрите внимательно вот это видео, [чувак дело говорит](https://www.youtube.com/watch?v=JxI3Eu5DPwE).
Итого:
- Всё что может делать предмет - в типы - или компоненты, если решились на ECS.
- Суперкласс/архетип для предмета
- Всё данные предмета - в человекочитаемый формат, json например
- Код для сериализации данных в экземпляр а памяти и обратно - **тщательно тестируем**! (TODO: примеры смешных багов)
- Названия типов - тоже в json, ни грамма данных врагу (т.е. коду). Позволит быстро менять/модифицировать игру чуть ли
не текстовым редактором.
И кстати. Чертежи не только на предметы работают, но об этом с следующей главе.

View File

@ -150,4 +150,4 @@ func renderSuperEffect() {
выполняется в main loop. В целом картина именно такая, но больше подробностей можно выполняется в main loop. В целом картина именно такая, но больше подробностей можно
найти по ссылкам в комментариях. найти по ссылкам в комментариях.
[1]: Если такой контейнер аккуратно сериализовать (рекурсивно вместе со всем содержимым) и записать на диск... То потом можно его прочитать и десериализовать. Получив тем самым почти бесплатно Save / Load. [1]: Если такой контейнер аккуратно сериализовать (рекурсивно вместе со всем содержимым) и записать на диск... То потом можно его прочитать и десериализовать. Получив тем самым почти бесплатно Save / Load.

View File

@ -5,7 +5,7 @@
Почему это важно Почему это важно
--- ---
Про сборку под разные ОС я даже убеждать не буду - аудитория рогаликов мало того что крохотная, так еще и сильно Про сборку под разные ОС я даже уюеждать не буду - аудитория рогаликов мало того что крохотная, так еще и сильно
сегментирована по осям. Go почти бесплатно дает вам возможность сборки под все мажорные оси, пользуйтесь этим - и сегментирована по осям. Go почти бесплатно дает вам возможность сборки под все мажорные оси, пользуйтесь этим - и
потенциально в разы больше народа ознакомится с вашим творением. потенциально в разы больше народа ознакомится с вашим творением.

View File

@ -1,5 +1,4 @@
Система типов и Go Система типов в Go
---
Плюсы использования нативной системы типов Плюсы использования нативной системы типов

View File

@ -15,7 +15,7 @@ type MainWindow struct {
} }
func Init(ctx context.Context) *MainWindow { func Init(ctx context.Context) *MainWindow {
appctx.Logger().Info().Msgf("Opening main window...") appctx.Logger(ctx).Info().Msgf("Opening main window...")
mw := MainWindow{ctx: ctx, layers: make(map[string]types.Renderable, 0)} mw := MainWindow{ctx: ctx, layers: make(map[string]types.Renderable, 0)}
mw.Open() mw.Open()
return &mw return &mw
@ -33,7 +33,7 @@ func (mw *MainWindow) GetLayer(name string) *Layer {
if layer, ok := mw.layers[name]; ok { if layer, ok := mw.layers[name]; ok {
return layer.(*Layer) return layer.(*Layer)
} }
appctx.Logger().Fatal().Msgf("No layer with such name %s", name) appctx.Logger(mw.ctx).Fatal().Msgf("No layer with such name %s", name)
return nil return nil
} }
@ -58,7 +58,7 @@ func (mw *MainWindow) Open() {
} }
func (mw *MainWindow) Close() { func (mw *MainWindow) Close() {
appctx.Logger().Info().Msg("Closing main window...") appctx.Logger(mw.ctx).Info().Msg("Closing main window...")
blt.Close() blt.Close()
} }

View File

@ -13,7 +13,7 @@ type ViewPort struct {
func NewViewPort(x, y, w, h int) *ViewPort { func NewViewPort(x, y, w, h int) *ViewPort {
vp := ViewPort{ vp := ViewPort{
Rect: types.Rect{X: x, Y: y, W: w, H: h}, Rect: types.Rect{x, y, w, h},
} }
return &vp return &vp
} }
@ -47,8 +47,8 @@ func (vp *ViewPort) ToVPCoords(c types.Coords) (newCoords types.Coords, err erro
//coords on map to coords on vp //coords on map to coords on vp
x, y := c.X-vp.CameraCoords.X, c.Y-vp.CameraCoords.Y x, y := c.X-vp.CameraCoords.X, c.Y-vp.CameraCoords.Y
if x < 0 || y < 0 || x > vp.W || y > vp.H { if x < 0 || y < 0 || x > vp.W || y > vp.H {
return types.Coords{X: -1, Y: -1}, fmt.Errorf("Not in viewport: {%d, %d}", x, y) return types.Coords{-1, -1}, fmt.Errorf("Not in viewport: {%d, %d}", x, y)
} }
return types.Coords{X: x, Y: y}, nil return types.Coords{x, y}, nil
} }

View File

@ -12,17 +12,15 @@ const (
loggerKey = "logger" loggerKey = "logger"
) )
type clientCtx struct { type ClientCtx struct {
context.Context context.Context
} }
var ClientState clientCtx func NewClientContext(config *util.Config, logger *zerolog.Logger) ClientCtx {
func NewClientContext(config *util.Config, logger *zerolog.Logger) {
ctx := context.Context(context.TODO()) ctx := context.Context(context.TODO())
ctx = context.WithValue(ctx, configKey, config) ctx = context.WithValue(ctx, configKey, config)
ctx = context.WithValue(ctx, loggerKey, logger) ctx = context.WithValue(ctx, loggerKey, logger)
ClientState = clientCtx{ctx} return ClientCtx{ ctx}
} }
func Config(c context.Context) *util.Config { func Config(c context.Context) *util.Config {
@ -33,11 +31,7 @@ func Config(c context.Context) *util.Config {
return cfg return cfg
} }
func Logger() *zerolog.Logger { func Logger(c context.Context) *zerolog.Logger {
return getLogger(ClientState.Context)
}
func getLogger(c context.Context) *zerolog.Logger {
logger, ok := c.Value(loggerKey).(*zerolog.Logger) logger, ok := c.Value(loggerKey).(*zerolog.Logger)
if !ok { if !ok {
panic(fmt.Errorf("no access to logger from context")) panic(fmt.Errorf("no access to logger from context"))

View File

@ -6,7 +6,6 @@ import "lab.zaar.be/thefish/alchemyst-go/engine/types"
var nodeId = 0 var nodeId = 0
var nodeList = make(map[types.Coords]Node, 0) var nodeList = make(map[types.Coords]Node, 0)
// Node defines a struct having as components the node X and Y coordinate position. // Node defines a struct having as components the node X and Y coordinate position.
type Node struct { type Node struct {
Id int Id int
@ -130,27 +129,17 @@ func (d *Delaunay) Init(width, height int) *Delaunay {
} }
var supertriangle1, supertriangle2 Triangle var supertriangle1, supertriangle2 Triangle
// clear method clears the delaunay triangles slice. // clear method clears the delaunay triangles slice.
func (d *Delaunay) clear() { func (d *Delaunay) clear() {
p0 := newNode(types.Coords{X: 0, Y: 0}) p0 := newNode(types.Coords{0,0})
p1 := newNode(types.Coords{X: d.width, Y: 0}) p1 := newNode(types.Coords{d.width, 0})
p2 := func() Node { p2 := newNode(types.Coords{d.width, d.height})
var coords types.Coords = types.Coords{X: d.width, Y: d.height} p3 := newNode(types.Coords{0, d.height})
if n, ok := nodeList[coords]; ok {
return n
}
neue := Node{Id: nodeId, Coords: coords}
nodeList[coords] = neue
nodeId++
return neue
}()
p3 := newNode(types.Coords{X: 0, Y: d.height})
// Create the supertriangle, an artificial triangle which encompasses all the points. // Create the supertriangle, an artificial triangle which encompasses all the points.
// At the end of the triangulation process any triangles which share Edges with the supertriangle are deleted from the triangle list. // At the end of the triangulation process any triangles which share Edges with the supertriangle are deleted from the triangle list.
supertriangle1 = t.newTriangle(p0, p1, p2) supertriangle1 = t.newTriangle(p0,p1,p2)
supertriangle2 = t.newTriangle(p0, p2, p3) supertriangle2 = t.newTriangle(p0,p2,p3)
d.triangles = []Triangle{supertriangle1, supertriangle2} d.triangles = []Triangle{supertriangle1, supertriangle2}
} }
@ -210,7 +199,7 @@ func (d *Delaunay) Insert(points []types.Coords) *Delaunay {
} }
for i = 0; i < len(polygon); i++ { for i = 0; i < len(polygon); i++ {
edge := polygon[i] edge := polygon[i]
temps = append(temps, t.newTriangle(edge.Nodes[0], edge.Nodes[1], newNode(types.Coords{X: x, Y: y}))) temps = append(temps, t.newTriangle(edge.Nodes[0], edge.Nodes[1], newNode(types.Coords{x, y})))
} }
d.triangles = temps d.triangles = temps
} }
@ -256,9 +245,12 @@ func (d *Delaunay) GetTriangles() []Triangle {
} }
func (d *Delaunay) GetEdges() []Edge { func (d *Delaunay) GetEdges() []Edge {
edges := make([]Edge, 0) edges := make([]Edge, 0)
for _, trs := range d.triangles { for _, trs := range d.triangles {
edges = append(edges, trs.Edges...) for _, e := range trs.Edges {
} edges = append(edges, e)
return edges }
}
return edges
} }

View File

@ -11,7 +11,7 @@ func GetTriangles(coords []types.Coords, w, h int) []types.Edge {
edges := d.Init(100, 100).Insert(coords).GetEdges() edges := d.Init(100, 100).Insert(coords).GetEdges()
output := make([]types.Edge, 0) output := make([]types.Edge, 0)
for _, e := range edges{ for _, e := range edges{
output = append(output, types.Edge{From: e.Nodes[0].Coords, To: e.Nodes[1].Coords}) output = append(output, types.Edge{e.Nodes[0].Coords, e.Nodes[1].Coords})
} }
return output return output
} }
@ -46,16 +46,16 @@ func GetMst(coords []types.Coords, w, h, negativeWeight int) []types.Edge {
graph = append( graph = append(
graph, graph,
kruskals.SimpleWeightedEdge{ kruskals.SimpleWeightedEdge{
F: nodeMap[e.Nodes[0].Id], nodeMap[e.Nodes[0].Id],
T: nodeMap[e.Nodes[1].Id], nodeMap[e.Nodes[1].Id],
W: negativeWeight - int(e.Nodes[0].Coords.DistanceTo(e.Nodes[1].Coords))}, negativeWeight - int(e.Nodes[0].Coords.DistanceTo(e.Nodes[1].Coords))},
) )
} }
result := kruskals.MinimumSpanningTree(graph) result := kruskals.MinimumSpanningTree(graph)
output := make([]types.Edge, 0) output := make([]types.Edge, 0)
for _, we := range result{ for _, we := range result{
output = append(output, types.Edge{From: nodeList[we.From()].Coords, To: nodeList[we.To()].Coords}) output = append(output, types.Edge{nodeList[we.From()].Coords, nodeList[we.To()].Coords})
} }
return output return output
} }

View File

@ -1,78 +0,0 @@
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
}
}