Compare commits

..

16 Commits

Author SHA1 Message Date
4913b0a567 merge master 2024-04-22 13:55:13 +03:00
dc2e6ea2b5 diagnostic tool fixes 2024-04-22 13:52:17 +03:00
5613744d6e more cleanup 2023-08-02 00:09:54 +03:00
4c0117b6a7 fix name calls on coords 2023-08-01 21:49:27 +03:00
thefish
4f18b6db18 fix indirection 2022-10-18 08:34:53 +03:00
thefish
f52e235799 fix flags loading 2022-10-18 06:37:11 +03:00
def43265de tests, load materials start 2022-10-15 01:25:10 +03:00
09bfa44c78 physics test 2022-10-14 13:38:24 +03:00
thefish
31d5be5d07 merged 2022-10-12 16:25:50 +03:00
thefish
3886a05ce2 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	cmd/game/main.go
#	delaunay_test.go
#	engine/screens/devmenu.go
#	go.mod
2022-10-12 16:07:35 +03:00
d9a8529a20 merge master 2020-09-25 01:40:19 +03:00
76780d7936 fix GetEntitiesWuthComponent, pickup logic fixed accordingly 2020-09-25 01:13:26 +03:00
9f3eaafa3f fix logger 2020-09-25 00:45:45 +03:00
d2b22f4760 pick up in adjacent tiles, fix bulk check 2020-09-24 23:47:36 +03:00
anton.gurov
2352ec4f0a wu algo 2020-02-27 17:40:49 +03:00
anton.gurov
e738568c14 test fixes 2020-01-28 15:25:32 +03:00
53 changed files with 1312 additions and 679 deletions

1
.envrc Normal file
View File

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

18
TODO
View File

@ -27,7 +27,7 @@ Assets and i18n:
ECS & engine:
- implement time queue (how to deal with closures?) (?) github.com/thefish/scheduleq - get rid od time.Now inside
- move all rendering to systems
- try to move input handling to systems
+ try to move input handling to systems
Dungeon and branches:
General:
@ -53,7 +53,7 @@ Combat:
- mass
- damage calculated from:
kinetic energy is calculated by mass / speed / material density (p = mv) масса на скорость = кинетическая энергия
next you determine target and its subpart (accuracy appied)
next you determine target and its subpart (accuracy applied)
next we calculate the area, on which kinetic energy is applied (determined by piercing, hacking, crushing damage profile) находим площадь
next we calculate
@ -81,5 +81,17 @@ Combat:
Quest engine:
- look at parsers like URQL etc
- distorted Aschenputtel story / partisans / rapist prince / Grey Mountains
- distorted Aschenputtel story / partisans / rapist prince / Grey Mountains / No gold
No Gold in Gery Mountains / Kim Newman
- Drakenfells castle //location
- Greteschele // char / rogue / charred zombie
- Yom Lamprecht // char / rogue /
- Tiley manor(?) // location / княжество
- Melissa d'Acu // char / small girl

View File

@ -0,0 +1,26 @@
{
"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

@ -0,0 +1,28 @@
{
"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

@ -0,0 +1,15 @@
{
"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

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

View File

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

View File

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

View File

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

View File

@ -46,7 +46,7 @@ func NewLevelRenderSystem(
return trs
}
//fixme add to screens/game Exit()
// fixme add to screens/game Exit()
func (trs LevelRenderSystem) Close() {
trs.animateTiles.Stop()
trs.animateTiles = nil //zero pointer to ticker
@ -93,7 +93,7 @@ func (trs LevelRenderSystem) Process() {
//terrain
for y := 0; y < trs.Viewport.H; y++ {
for x := 0; x < trs.Viewport.W; x++ {
mapCoords := types.Coords{trs.Viewport.CameraCoords.X + x, trs.Viewport.CameraCoords.Y + y}
mapCoords := types.Coords{X: trs.Viewport.CameraCoords.X + x, Y: trs.Viewport.CameraCoords.Y + y}
if trs.state.Level.InBounds(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() {
max := pp.MaxTorchRadius
minusMax := (-1) * max
zeroCoords := types.Coords{0, 0}
zeroCoords := types.Coords{X: 0, Y: 0}
var x, y int
//fill list
for x = minusMax; x < max+1; x++ {
@ -70,7 +70,7 @@ func (pp *precomputedPermissive) PrecomputeFovMap() {
if x == 0 && y == 0 {
continue;
}
iterCoords := types.Coords{x, y}
iterCoords := types.Coords{X: x, Y: y}
distance := zeroCoords.DistanceTo(iterCoords)
if distance <= float64(max) {
pp.CellList = append(pp.CellList, &Cell{iterCoords, distance, nil})
@ -101,7 +101,7 @@ func (pp *precomputedPermissive) PrecomputeFovMap() {
roundedX := int(basic.Round(lineX))
roundedY := int(basic.Round(lineY))
idx, cell, err := pp.FindByCoords(types.Coords{roundedX, roundedY})
idx, cell, err := pp.FindByCoords(types.Coords{X: roundedX, Y: roundedY})
if err != nil {
//inexistent coord found
break;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,215 @@
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,35 +3,54 @@ package itemprops
import "github.com/shopspring/decimal"
type ItemPhysics struct {
Material Material
Rigidity DimensionItemRigidity
Size DimensionItemSize
Temperature DimensionItemTemperature
Material `json:"material"`
//DimensionItemRigidity жёсткость (самого предмета а не материала), мера прочности,
//уменьшается со временем, может зависеть от тепмературы
DimensionItemRigidity `json:"rigidity"`
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 {
Width decimal.Decimal
Height decimal.Decimal
// if item is solid - depth in m (1mm = 1/1000 of m)
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 decimal.NullDecimal
Width decimal.Decimal `json:"width"`
Height decimal.Decimal `json:"height"`
// if item is solid - depth in m (1mm = 1/1000 of m)
Depth decimal.NullDecimal `json:"depth,omitempty"`
//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"`
}
//Area is frontal area
// Area is frontal area
func (d *DimensionItemSize) Area() decimal.Decimal {
return d.Width.Mul(d.Height)
return d.Width.Mul(d.Height)
}
//DimensionItemDensity density in kg/m3
type DimensionItemDensity decimal.Decimal
func (ip *ItemPhysics) Weight() decimal.Decimal {
return ip.Material.Density.Mul(ip.Volume())
}
//DimensionItemRigidity rigidity жёсткость, способность твёрдого тела, конструкции или её элементов сопротивляться деформации in N/m
type DimensionItemRigidity decimal.Decimal
//NotchFractureToughness ударная вязкость по Шарпи, Дж (надо ли?)
type NotchFractureToughness decimal.Decimal
//DimensionItemTemperature in celsius, -273 to 10000
type DimensionItemTemperature decimal.Decimal
func (d *ItemPhysics) Volume() decimal.Decimal {
v := d.Width.Mul(d.Height)
//есть глубина
if !d.Depth.Decimal.IsZero() {
v = v.Mul(d.Depth.Decimal)
//пустотелый
if !d.Thickness.Decimal.IsZero() {
surfaceArea := d.Width.Mul(d.Height).Mul(decimal.NewFromInt(2)).
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

@ -0,0 +1,35 @@
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,20 +1,117 @@
package itemprops
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/rs/zerolog"
)
type Material struct {
Name string
Flags MaterialFlags
Density DimensionItemDensity
FractureToughness NotchFractureToughness
MeltingPoint DimensionItemTemperature
BoilingPoint DimensionItemTemperature
Id string `json:"id"`
Name string `json:"name"`
Flags MaterialFlags `json:"material_flags"`
Density DimensionItemDensity `json:"density"`
FractureToughness DimensionFractureToughness `json:"fracture_toughness"`
MeltingPoint DimensionItemNullTemperature `json:"melting_point,omitempty"`
BoilingPoint DimensionItemNullTemperature `json:"boiling_point,omitempty"`
}
func (m Material) Unmarshal() {}
type MaterialFlags struct {
ConductsElictricity bool
BlocksLiquid bool
AcidResistant bool
BlocksGas bool
Flammable bool
ConductsHeat bool
Radiates bool
ConductsElictricity bool `json:"conducts_elictricity"`
BlocksLiquid bool `json:"blocks_liquid"`
AcidResistant bool `json:"acid_resistant"`
BlocksGas bool `json:"blocks_gas"`
Flammable bool `json:"flammable"`
ConductsHeat bool `json:"conducts_heat"`
Radiates bool `json:"radiates"`
}
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
}(transitions[from], to)
if !newStateFound {
log.Warn().Msgf("Transition %s -> %s is impossible", from, to)
log.Warn().Msgf(`Transition %d -> %d is impossible`, from, to)
return false
}
// check temperatures/conditions, see template
@ -79,4 +79,4 @@ func (ms MatterState) Change(from MatterState, to MatterState) bool {
return true
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

1
go.mod
View File

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

16
go.sum
View File

@ -1,19 +1,33 @@
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/go.mod h1:D90+MBHVc9Sk1lJAbEVgws0eYEurY4mv2TDso3Nxh3w=
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/zerolog v1.15.0 h1:uPRuwkWF4J6fGsJ2R0Gn2jB1EQiav9k3S6CSdygQJXY=
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/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=
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/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/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=
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/go.mod h1:tV7Vxx6vf9dPgj9B+RPeSrmtRl8nTSH07HIyBSSnEc4=

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,7 +13,7 @@ type ViewPort struct {
func NewViewPort(x, y, w, h int) *ViewPort {
vp := ViewPort{
Rect: types.Rect{x, y, w, h},
Rect: types.Rect{X: x, Y: y, W: w, H: h},
}
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
x, y := c.X-vp.CameraCoords.X, c.Y-vp.CameraCoords.Y
if x < 0 || y < 0 || x > vp.W || y > vp.H {
return types.Coords{-1, -1}, fmt.Errorf("Not in viewport: {%d, %d}", x, y)
return types.Coords{X: -1, Y: -1}, fmt.Errorf("Not in viewport: {%d, %d}", x, y)
}
return types.Coords{x, y}, nil
return types.Coords{X: x, Y: y}, nil
}

View File

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

View File

@ -6,6 +6,7 @@ import "lab.zaar.be/thefish/alchemyst-go/engine/types"
var nodeId = 0
var nodeList = make(map[types.Coords]Node, 0)
// Node defines a struct having as components the node X and Y coordinate position.
type Node struct {
Id int
@ -129,17 +130,27 @@ func (d *Delaunay) Init(width, height int) *Delaunay {
}
var supertriangle1, supertriangle2 Triangle
// clear method clears the delaunay triangles slice.
func (d *Delaunay) clear() {
p0 := newNode(types.Coords{0,0})
p1 := newNode(types.Coords{d.width, 0})
p2 := newNode(types.Coords{d.width, d.height})
p3 := newNode(types.Coords{0, d.height})
p0 := newNode(types.Coords{X: 0, Y: 0})
p1 := newNode(types.Coords{X: d.width, Y: 0})
p2 := func() Node {
var coords types.Coords = types.Coords{X: d.width, Y: 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.
// 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)
supertriangle2 = t.newTriangle(p0,p2,p3)
supertriangle1 = t.newTriangle(p0, p1, p2)
supertriangle2 = t.newTriangle(p0, p2, p3)
d.triangles = []Triangle{supertriangle1, supertriangle2}
}
@ -199,7 +210,7 @@ func (d *Delaunay) Insert(points []types.Coords) *Delaunay {
}
for i = 0; i < len(polygon); i++ {
edge := polygon[i]
temps = append(temps, t.newTriangle(edge.Nodes[0], edge.Nodes[1], newNode(types.Coords{x, y})))
temps = append(temps, t.newTriangle(edge.Nodes[0], edge.Nodes[1], newNode(types.Coords{X: x, Y: y})))
}
d.triangles = temps
}
@ -245,12 +256,9 @@ func (d *Delaunay) GetTriangles() []Triangle {
}
func (d *Delaunay) GetEdges() []Edge {
edges := make([]Edge, 0)
for _, trs := range d.triangles {
for _, e := range trs.Edges {
edges = append(edges, e)
}
}
return edges
edges := make([]Edge, 0)
for _, trs := range d.triangles {
edges = append(edges, trs.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()
output := make([]types.Edge, 0)
for _, e := range edges{
output = append(output, types.Edge{e.Nodes[0].Coords, e.Nodes[1].Coords})
output = append(output, types.Edge{From: e.Nodes[0].Coords, To: e.Nodes[1].Coords})
}
return output
}
@ -46,16 +46,16 @@ func GetMst(coords []types.Coords, w, h, negativeWeight int) []types.Edge {
graph = append(
graph,
kruskals.SimpleWeightedEdge{
nodeMap[e.Nodes[0].Id],
nodeMap[e.Nodes[1].Id],
negativeWeight - int(e.Nodes[0].Coords.DistanceTo(e.Nodes[1].Coords))},
F: nodeMap[e.Nodes[0].Id],
T: nodeMap[e.Nodes[1].Id],
W: negativeWeight - int(e.Nodes[0].Coords.DistanceTo(e.Nodes[1].Coords))},
)
}
result := kruskals.MinimumSpanningTree(graph)
output := make([]types.Edge, 0)
for _, we := range result{
output = append(output, types.Edge{nodeList[we.From()].Coords, nodeList[we.To()].Coords})
output = append(output, types.Edge{From: nodeList[we.From()].Coords, To: nodeList[we.To()].Coords})
}
return output
}

78
util/wu/line.go Normal file
View File

@ -0,0 +1,78 @@
package wu
import (
"lab.zaar.be/thefish/alchemyst-go/engine/types"
"math"
)
func ipart(x float64) float64 {
return math.Floor(x)
}
func round(x float64) float64 {
return ipart(x + .5)
}
func fpart(x float64) float64 {
return x - ipart(x)
}
func rfpart(x float64) float64 {
return 1 - fpart(x)
}
func (Layer types.Putable) WuLine(x1, y1, x2, y2 float64, w int) {
dx := x2 - x1
dy := y2 - y1
ax := dx
if ax < 0 {
ax = -ax
}
ay := dy
if ay < 0 {
ay = -ay
}
var plot func(int, int, float64)
if ax < ay {
x1, y1 = y1, x1
x2, y2 = y2, x2
dx, dy = dy, dx
plot = func(x, y int, c float64) {
Layer.Put(y, x, uint8(255 * c))
}
} else {
plot = func(x, y int, c float64) {
Layer.Put(x, y, uint8(255 * c))
}
}
if x2 < x1 {
x1, x2 = x2, x1
y1, y2 = y2, y1
}
gradient := dy / dx
xend := round(x1)
yend := y1 + gradient*(xend-x1)
xgap := rfpart(x1 + .5)
xpxl1 := int(xend)
ypxl1 := int(ipart(yend))
plot(xpxl1, ypxl1, rfpart(yend)*xgap)
plot(xpxl1, ypxl1+1, fpart(yend)*xgap)
intery := yend + gradient
xend = round(x2)
yend = y2 + gradient*(xend-x2)
xgap = fpart(x2 + 0.5)
xpxl2 := int(xend)
ypxl2 := int(ipart(yend))
plot(xpxl2, ypxl2, rfpart(yend)*xgap)
plot(xpxl2, ypxl2+1, fpart(yend)*xgap)
for x := xpxl1 + 1; x <= xpxl2-1; x++ {
plot(x, int(ipart(intery)), rfpart(intery))
plot(x, int(ipart(intery))+1, fpart(intery))
intery = intery + gradient
}
}