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

@ -33,7 +33,7 @@ func init() {
//we can run logic in separate goroutines //we can run logic in separate goroutines
// //
// go doSometing(State,...) // go doSomething(State,...)
// //
//and there we go like this: //and there we go like this:
// func doSomething(State main.GameState, args...) { // func doSomething(State main.GameState, args...) {
@ -60,7 +60,8 @@ func main() {
var logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}).Level(logLevels[config.Verbosity]) var logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}).Level(logLevels[config.Verbosity])
// set up context // set up context
mainCtx := appctx.NewClientContext(config, &logger) appctx.NewClientContext(config, &logger)
mainCtx := appctx.ClientState
//set up main window //set up main window
mw := mainwindow.Init(mainCtx) mw := mainwindow.Init(mainCtx)
@ -72,9 +73,9 @@ func main() {
go decodeInput(mainCtx, mw.GetLayer("base")) go decodeInput(mainCtx, mw.GetLayer("base"))
//fixme set up (load / generate) level - move to game / enter or title / exit //fixme set up (load / generate) level - move to game / enter or title / exit
//level, rooms := _default.DefaultGen(mainCtx, gamemap.NewLevel(mainCtx, "test", 1)) level, rooms := mapgens.DefaultGen(gamemap.NewLevel("test", 1))
//level, rooms := mapgens.DelaunayMstGen(mainCtx, gamemap.NewLevel(mainCtx, "test", 1)) //level, rooms := mapgens.DelaunayMstGen(mainCtx, gamemap.NewLevel(mainCtx, "test", 1))
level, rooms := mapgens.DelaunayMstExtGen(mainCtx, gamemap.NewLevel(mainCtx, "test", 1)) //level, rooms := mapgens.DelaunayMstExtGen(mainCtx, gamemap.NewLevel(mainCtx, "test", 1))
//level, rooms := mapgens.DelaunayPureGen(mainCtx, gamemap.NewLevel(mainCtx, "test", 1)) //level, rooms := mapgens.DelaunayPureGen(mainCtx, gamemap.NewLevel(mainCtx, "test", 1))
State.Level = level State.Level = level
@ -85,7 +86,7 @@ func main() {
//set up controller //set up controller
controller := ecs.NewController(mainCtx) controller := ecs.NewController()
controller.MapComponentClass(ecs.CoordsComponent, types.Coords{}) controller.MapComponentClass(ecs.CoordsComponent, types.Coords{})
controller.MapComponentClass(ecs.AppearanceComponent, types.Appearance{}) controller.MapComponentClass(ecs.AppearanceComponent, types.Appearance{})
@ -105,9 +106,9 @@ func main() {
bp := items.Backpack{MaxMass:100, MaxBulk:100} bp := items.Backpack{MaxMass:100, MaxBulk:100}
//Set up Screen Manager //Set up Screen Manager
screenMgr := types.NewScreenManager(mainCtx) screenMgr := types.NewScreenManager()
screenMgr.AddScreen("title", screens.NewTitleScreen(mw, screenMgr)) screenMgr.AddScreen("title", screens.NewTitleScreen(mw, screenMgr))
screenMgr.AddScreen("game", screens.NewGameScreen(mainCtx, mw, &State, vp, controller, screenMgr)) screenMgr.AddScreen("game", screens.NewGameScreen(mw, &State, vp, controller, screenMgr))
screenMgr.AddScreen("help", screens.NewMenuScreen( screenMgr.AddScreen("help", screens.NewMenuScreen(
mw, mw,
screenMgr, screenMgr,
@ -116,7 +117,7 @@ func main() {
//"[color=yellow]Note[/color]: Many of these are not implemented yet", //"[color=yellow]Note[/color]: Many of these are not implemented yet",
"[color=yellow]Note[/color]: Many of these are not implemented yet", "[color=yellow]Note[/color]: Many of these are not implemented yet",
types.NewCenteredRect(mw.Rect, 50, 15), types.NewCenteredRect(mw.Rect, 50, 15),
true). true, ).
SetBgColor("#ef1d494f"). SetBgColor("#ef1d494f").
SetFgColor("white"). SetFgColor("white").
SetItems([]interface{}{ SetItems([]interface{}{
@ -126,7 +127,7 @@ func main() {
"i - inventory", "i - inventory",
"? - this screen", "? - this screen",
"Ctrl+q - exit", "Ctrl+q - exit",
"f or F - fire or throw weapon", "f or F - fire weapon or throw item",
"z or Z - cast a spell", "z or Z - cast a spell",
"p - pray", "p - pray",
"Ctrl+p - message log", "Ctrl+p - message log",
@ -142,7 +143,7 @@ func main() {
//"[color=yellow]Note[/color]: Many of these are not implemented yet", //"[color=yellow]Note[/color]: Many of these are not implemented yet",
"", "",
types.NewCenteredRect(mw.Rect, 70, 25), types.NewCenteredRect(mw.Rect, 70, 25),
true). true, ).
SetBgColor("#ef305c70"). SetBgColor("#ef305c70").
SetFgColor("white"), SetFgColor("white"),
} }
@ -184,16 +185,16 @@ func main() {
}, },
}) })
controller.AddComponent(potion, rooms[0].Center) //implicit Coords controller.AddComponent(potion, rooms[0].Center) //implicit Coords
controller.AddComponent(potion, items.Carried{Mass: 5, Bulk: 3}) controller.AddComponent(potion, items.Carried{Mass:5, Bulk:3}) //fixme generate from blueprint!
controller.AddComponent(potion, items.Usable{}) controller.AddComponent(potion, items.Usable{})
controller.AddComponent(potion, items.Consumable{}) controller.AddComponent(potion, items.Consumable{})
controller.AddComponent(potion, ecs.Named{Name:"first potion"}) controller.AddComponent(potion, ecs.Named{Name:"first potion"})
potion2 := controller.CreateEntity([]ecs.Component{}) potion2 := controller.CreateEntity([]ecs.Component{})
controller.AddComponent(potion2, types.Appearance{ controller.AddComponent(potion2, types.Appearance{
Glyph: types.PlainGlyphHolder{"!"}, Glyph: types.PlainGlyphHolder{Glyph: "!"},
ColorSet: types.TileColorSet{ ColorSet: types.TileColorSet{
Fg: types.PlainColorHolder{255, 222, 255, 55}, Fg: types.PlainColorHolder{A: 255, R: 222, G: 255, B: 55},
}, },
}) })
controller.AddComponent(potion2, rooms[1].Center) //implicit Coords controller.AddComponent(potion2, rooms[1].Center) //implicit Coords
@ -208,6 +209,7 @@ func main() {
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 //but every call to bearlibterminal must be wrapped to closure and passed to mainfunc
var exit = false var exit = false
for !exit { for !exit {
@ -222,7 +224,7 @@ func main() {
// f() // f()
// break // break
case <-State.Exit: case <-State.Exit:
appctx.Logger(mainCtx).Warn().Msg("quitting NOW") appctx.Logger().Warn().Msg("quitting NOW")
exit = true exit = true
break break
// не оставляйте default в бесконечном select {} - сожрет всё CPU // не оставляйте default в бесконечном select {} - сожрет всё CPU
@ -233,7 +235,7 @@ func main() {
} }
} }
appctx.Logger(mainCtx).Info().Msg("pre-shutdown sequence") appctx.Logger().Info().Msg("pre-shutdown sequence")
} }
func setupLayers(mainwindow *mainwindow.MainWindow) { func setupLayers(mainwindow *mainwindow.MainWindow) {
@ -253,9 +255,9 @@ func decodeInput(ctx context.Context, baseLayer *mainwindow.Layer) {
continue continue
} }
if keycode == blt.TK_CLOSE && !waitForWCspam { if keycode == blt.TK_CLOSE && !waitForWCspam {
appctx.Logger(ctx).Warn().Msg("exiting on window close...") appctx.Logger().Warn().Msg("exiting on window close...")
State.Exit <- struct{}{} State.Exit <- struct{}{}
appctx.Logger(ctx).Warn().Msg("...done") appctx.Logger().Warn().Msg("...done")
return return
} }
var pressed = "" var pressed = ""
@ -279,14 +281,14 @@ func decodeInput(ctx context.Context, baseLayer *mainwindow.Layer) {
case "Ctrl+q": case "Ctrl+q":
//fallthrough //fallthrough
//case "Escape": //case "Escape":
appctx.Logger(ctx).Info().Msg("exiting on quit command...") appctx.Logger().Info().Msg("exiting on quit command...")
State.Exit <- struct{}{} State.Exit <- struct{}{}
appctx.Logger(ctx).Info().Msg("...done") appctx.Logger().Info().Msg("...done")
exit = true exit = true
return return
default: default:
if pressed != "" { if pressed != "" {
waitForWCspam = false waitForWCspam = false;
State.Input <- pressed State.Input <- pressed
} }
} }

View File

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

View File

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

View File

@ -3,13 +3,11 @@ package ecs
// ECS system by jcerise, github.com/jcerise/gogue // ECS system by jcerise, github.com/jcerise/gogue
import ( import (
"context"
"lab.zaar.be/thefish/alchemyst-go/util/appctx" "lab.zaar.be/thefish/alchemyst-go/util/appctx"
"sort" "sort"
) )
type Controller struct { type Controller struct {
ctx context.Context
systems map[string]System systems map[string]System
sortedSystems map[int][]System sortedSystems map[int][]System
priorityKeys []int priorityKeys []int
@ -23,8 +21,8 @@ type Controller struct {
} }
// NewController is a convenience/constructor method to properly initialize a new processor // NewController is a convenience/constructor method to properly initialize a new processor
func NewController(ctx context.Context) *Controller { func NewController() *Controller {
controller := Controller{ctx: ctx} controller := Controller{}
controller.systems = make(map[string]System) controller.systems = make(map[string]System)
controller.sortedSystems = make(map[int][]System) controller.sortedSystems = make(map[int][]System)
controller.priorityKeys = []int{} controller.priorityKeys = []int{}
@ -80,7 +78,7 @@ func (c *Controller) GetMappedComponentClass(componentName string) Component {
return c.componentMap[componentName] return c.componentMap[componentName]
} else { } else {
// TODO: Add better (read: actual) error handling here // TODO: Add better (read: actual) error handling here
appctx.Logger(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 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 // GetEntitiesWithComponent returns a list of all entities with a given component attached
// TODO: Allow for passing a list of components func (c *Controller) GetEntitiesWithComponent(componentTypes... string) []Entity {
func (c *Controller) GetEntitiesWithComponent(componentType string) []Entity {
entitiesWithComponent := make([]Entity, 0) entitiesWithComponent := make([]Entity, 0)
for entity := range c.entities { for entity := range c.entities {
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) entitiesWithComponent = append(entitiesWithComponent, entity)
} }
} }
return entitiesWithComponent return entitiesWithComponent
} }
@ -208,7 +211,7 @@ func (c *Controller) AddSystem(system System, priority int) {
c.sortedSystems[priority] = append(c.sortedSystems[priority], system) c.sortedSystems[priority] = append(c.sortedSystems[priority], system)
sort.Ints(c.priorityKeys) sort.Ints(c.priorityKeys)
} else { } else {
appctx.Logger(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

@ -93,7 +93,7 @@ func (trs LevelRenderSystem) Process() {
//terrain //terrain
for y := 0; y < trs.Viewport.H; y++ { for y := 0; y < trs.Viewport.H; y++ {
for x := 0; x < trs.Viewport.W; x++ { for x := 0; x < trs.Viewport.W; x++ {
mapCoords := types.Coords{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) { if trs.state.Level.InBounds(mapCoords) {
tile := trs.state.Level.GetTile(mapCoords) tile := trs.state.Level.GetTile(mapCoords)

View File

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

View File

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

View File

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

View File

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

View File

@ -3,10 +3,9 @@ package mapgens
import ( import (
"lab.zaar.be/thefish/alchemyst-go/engine/gamemap" "lab.zaar.be/thefish/alchemyst-go/engine/gamemap"
"lab.zaar.be/thefish/alchemyst-go/util" "lab.zaar.be/thefish/alchemyst-go/util"
"lab.zaar.be/thefish/alchemyst-go/util/appctx"
) )
func DefaultGen(ctx appctx.ClientCtx,l *gamemap.Level) (*gamemap.Level, []gamemap.Room) { func DefaultGen(l *gamemap.Level) (*gamemap.Level, []gamemap.Room) {
rng := util.NewRNG() 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 { for idx, room := range rooms {
if idx > 0 { if idx > 0 {

View File

@ -18,10 +18,10 @@ func DelaunayMstGen(ctx context.Context, l *gamemap.Level) (*gamemap.Level, []ga
l.SetTileByXY(i, j, gamemap.NewWall()) l.SetTileByXY(i, j, gamemap.NewWall())
} }
} }
rooms := GetRandomRoomList(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) centers := make([]types.Coords, 0)
for _, room := range rooms { 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/gamemap"
"lab.zaar.be/thefish/alchemyst-go/engine/types" "lab.zaar.be/thefish/alchemyst-go/engine/types"
"lab.zaar.be/thefish/alchemyst-go/util" "lab.zaar.be/thefish/alchemyst-go/util"
"lab.zaar.be/thefish/alchemyst-go/util/appctx"
"lab.zaar.be/thefish/alchemyst-go/util/delaunay" "lab.zaar.be/thefish/alchemyst-go/util/delaunay"
) )
func DelaunayMstExtGen(ctx appctx.ClientCtx, l *gamemap.Level) (*gamemap.Level, []gamemap.Room) { func DelaunayMstExtGen(l *gamemap.Level) (*gamemap.Level, []gamemap.Room) {
rng := util.NewRNG() rng := util.NewRNG()
@ -18,9 +17,9 @@ func DelaunayMstExtGen(ctx appctx.ClientCtx, l *gamemap.Level) (*gamemap.Level,
l.SetTileByXY(i, j, gamemap.NewWall()) 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) centers := make([]types.Coords, 0)
for _, room := range rooms { for _, room := range rooms {

View File

@ -18,9 +18,9 @@ func DelaunayPureGen(ctx context.Context, l *gamemap.Level) (*gamemap.Level, []g
l.SetTileByXY(i, j, gamemap.NewWall()) l.SetTileByXY(i, j, gamemap.NewWall())
} }
} }
rooms := GetRandomRoomList(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) centers := make([]types.Coords, 0)
for _, room := range rooms { for _, room := range rooms {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,7 @@
package itemprops package itemprops
import "github.com/shopspring/decimal"
//MedicalSystem организм //MedicalSystem организм
// Humanoid // Humanoid
// Circuits // Circuits
@ -72,7 +74,7 @@ type MedicalCircuit struct {
type MedicalVessel struct { type MedicalVessel struct {
Name string Name string
Material Material
Pressure DimensionItemDensity Pressure decimal.Decimal //Pressure давление, kg / m3
} }
// BodyPart часть тела // BodyPart часть тела
@ -82,6 +84,7 @@ type BodyPart struct {
LayerMiddle MedicalMaterial LayerMiddle MedicalMaterial
LayerInner MedicalMaterial LayerInner MedicalMaterial
Size DimensionItemSize
Joints []Joint Joints []Joint
Contains []InnerOrgan Contains []InnerOrgan
Exposes []OuterOrgan Exposes []OuterOrgan

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,20 +3,25 @@ package itemprops
import "github.com/shopspring/decimal" import "github.com/shopspring/decimal"
type ItemPhysics struct { type ItemPhysics struct {
Material Material Material `json:"material"`
Rigidity DimensionItemRigidity //DimensionItemRigidity жёсткость (самого предмета а не материала), мера прочности,
Size DimensionItemSize //уменьшается со временем, может зависеть от тепмературы
Temperature DimensionItemTemperature 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 { type DimensionItemSize struct {
Width decimal.Decimal Width decimal.Decimal `json:"width"`
Height decimal.Decimal Height decimal.Decimal `json:"height"`
// if item is solid - depth in m (1mm = 1/1000 of m) // if item is solid - depth in m (1mm = 1/1000 of m)
Depth decimal.NullDecimal 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 if item is hollow - thickness of outer item shell, ie for armor, in m (1 mm = 1/1000 of m)
Thickness decimal.NullDecimal Thickness decimal.NullDecimal `json:"thickness,omitempty"`
} }
// Area is frontal area // Area is frontal area
@ -24,14 +29,28 @@ func (d *DimensionItemSize) Area() decimal.Decimal {
return d.Width.Mul(d.Height) return d.Width.Mul(d.Height)
} }
//DimensionItemDensity density in kg/m3 func (ip *ItemPhysics) Weight() decimal.Decimal {
type DimensionItemDensity decimal.Decimal return ip.Material.Density.Mul(ip.Volume())
}
//DimensionItemRigidity rigidity жёсткость, способность твёрдого тела, конструкции или её элементов сопротивляться деформации in N/m func (d *ItemPhysics) Volume() decimal.Decimal {
type DimensionItemRigidity decimal.Decimal v := d.Width.Mul(d.Height)
//есть глубина
//NotchFractureToughness ударная вязкость по Шарпи, Дж (надо ли?) if !d.Depth.Decimal.IsZero() {
type NotchFractureToughness decimal.Decimal v = v.Mul(d.Depth.Decimal)
//пустотелый
//DimensionItemTemperature in celsius, -273 to 10000 if !d.Thickness.Decimal.IsZero() {
type DimensionItemTemperature decimal.Decimal 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 package itemprops
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/rs/zerolog"
)
type Material struct { type Material struct {
Name string Id string `json:"id"`
Flags MaterialFlags Name string `json:"name"`
Density DimensionItemDensity Flags MaterialFlags `json:"material_flags"`
FractureToughness NotchFractureToughness Density DimensionItemDensity `json:"density"`
MeltingPoint DimensionItemTemperature FractureToughness DimensionFractureToughness `json:"fracture_toughness"`
BoilingPoint DimensionItemTemperature MeltingPoint DimensionItemNullTemperature `json:"melting_point,omitempty"`
BoilingPoint DimensionItemNullTemperature `json:"boiling_point,omitempty"`
} }
func (m Material) Unmarshal() {}
type MaterialFlags struct { type MaterialFlags struct {
ConductsElictricity bool ConductsElictricity bool `json:"conducts_elictricity"`
BlocksLiquid bool BlocksLiquid bool `json:"blocks_liquid"`
AcidResistant bool AcidResistant bool `json:"acid_resistant"`
BlocksGas bool BlocksGas bool `json:"blocks_gas"`
Flammable bool Flammable bool `json:"flammable"`
ConductsHeat bool ConductsHeat bool `json:"conducts_heat"`
Radiates bool 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 return false
}(transitions[from], to) }(transitions[from], to)
if !newStateFound { if !newStateFound {
log.Warn().Msgf("Transition %s -> %s is impossible", from, to) log.Warn().Msgf(`Transition %d -> %d is impossible`, from, to)
return false return false
} }
// check temperatures/conditions, see template // check temperatures/conditions, see template

View File

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

View File

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

View File

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

View File

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

View File

@ -88,7 +88,7 @@ func (ms *MenuScreen) Render() {
if ms.renderParent { if ms.renderParent {
ms.scm.PreviousScreen.Render() ms.scm.PreviousScreen.Render()
} }
if (ms.redraw || ms.renderParent) { if ms.redraw || ms.renderParent {
ms.redraw = false ms.redraw = false
ms.drawFunc() ms.drawFunc()
} }
@ -96,13 +96,13 @@ func (ms *MenuScreen) Render() {
func (ms *MenuScreen) ListHandleInput(input string) { func (ms *MenuScreen) ListHandleInput(input string) {
switch input { switch input {
case "Up": case "Up", "k":
ms.offset = ms.offset - 1 ms.offset = ms.offset - 1
if ms.offset < 0 { if ms.offset < 0 {
ms.offset = 0 ms.offset = 0
} }
break break
case "Down": case "Down", "j":
ms.offset = ms.offset + 1 ms.offset = ms.offset + 1
if ms.offset > len(ms.items)-1 { if ms.offset > len(ms.items)-1 {
ms.offset = len(ms.items) - 1 ms.offset = len(ms.items) - 1
@ -133,7 +133,7 @@ func (ms *MenuScreen) ListRender() {
itemField := types.Rect{ms.X, ms.Y + headerHeight + 1, ms.W, ms.H - headerHeight - footerHeight} itemField := types.Rect{ms.X, ms.Y + headerHeight + 1, ms.W, ms.H - headerHeight - footerHeight}
_ = itemField _ = itemField
var ilw, ilh int var ilw, ilh int
if (len(ms.items) > 0) { if len(ms.items) > 0 {
//fixme itemfield object, scroller, inputhandler, current selected item //fixme itemfield object, scroller, inputhandler, current selected item
menuItems := make([]string, 0) menuItems := make([]string, 0)
for i := ms.offset; i < len(ms.items); i++ { for i := ms.offset; i < len(ms.items); i++ {

View File

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

View File

@ -1,7 +1,6 @@
package types package types
import ( import (
"context"
"lab.zaar.be/thefish/alchemyst-go/util/appctx" "lab.zaar.be/thefish/alchemyst-go/util/appctx"
) )
@ -14,16 +13,14 @@ type Screen interface {
} }
type ScreenManager struct { type ScreenManager struct {
ctx context.Context
Screens map[string]Screen Screens map[string]Screen
CurrentScreen Screen CurrentScreen Screen
PreviousScreen Screen PreviousScreen Screen
} }
// NewScreenManager is a convenience/constructor method to properly initialize a new ScreenManager // NewScreenManager is a convenience/constructor method to properly initialize a new ScreenManager
func NewScreenManager(ctx context.Context) *ScreenManager { func NewScreenManager() *ScreenManager {
manager := ScreenManager{ manager := ScreenManager{
ctx:ctx,
Screens: make(map[string]Screen), Screens: make(map[string]Screen),
CurrentScreen: nil, 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 // A screen with the given name does not yet exist on the ScreenManager, go ahead and add it
sm.Screens[screenName] = screen sm.Screens[screenName] = screen
} else { } else {
appctx.Logger(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) delete(sm.Screens, screenName)
} else { } else {
// A screen with the given name does not exist // A screen with the given name does not exist
appctx.Logger(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() sm.CurrentScreen.Enter()
} else { } else {
// A screen with the given name does not exist // A screen with the given name does not exist
appctx.Logger(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/gammazero/deque v0.0.0-20190521012701-46e4ffb7a622
github.com/rs/zerolog v1.15.0 github.com/rs/zerolog v1.15.0
github.com/shopspring/decimal v1.3.1 github.com/shopspring/decimal v1.3.1
github.com/stretchr/testify v1.8.0
lab.zaar.be/thefish/bearlibterminal v0.0.0-20191018101635-dd37bbc90d77 lab.zaar.be/thefish/bearlibterminal v0.0.0-20191018101635-dd37bbc90d77
) )

16
go.sum
View File

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

View File

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

View File

@ -155,6 +155,9 @@ func (tr *TerrainRender) Render() {
... ...
} }
``` ```
TODO: троттлинг
#### Каналы состояний и их Listеner-ы #### Каналы состояний и их Listеner-ы
@ -167,7 +170,7 @@ func (tr *TerrainRender) Render() {
- reflect в main loop. Лишь **только** выкинув рефлкесию и больше ничего не делая - я снизил потребление CPU приложением - 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 1. [Установка и некоторые особенности работы](linux_go_blt_install_quickstart.md) связки BLT + Go на Linux
2. Что [стоит и НЕ стоит](go_game_dos_and_donts.md) делать с возможностями Go - +chans, +tickers, +throttling, -closures 2. Что [стоит и НЕ стоит](go_game_dos_and_donts.md) делать с возможностями Go - +chans, +tickers, +throttling, -closures
3. [Система типов](./static_types_vs_ecs.md) - нативная или ECS? На самом деле и то, и то 3. [Система типов](./static_types_vs_ecs.md) - нативная или ECS? На самом деле и то, и то
4. Немножко конкретики: [предметы и обращение с ними](./item_objecttypes_and_blueprints.md). Как правильно готовить
предметы - чтобы потом не было мучительно больно.
Дополнения Дополнения
--- ---

View File

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

View File

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

View File

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

View File

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

View File

@ -13,7 +13,7 @@ type ViewPort struct {
func NewViewPort(x, y, w, h int) *ViewPort { func NewViewPort(x, y, w, h int) *ViewPort {
vp := ViewPort{ vp := ViewPort{
Rect: types.Rect{x, y, w, h}, Rect: types.Rect{X: x, Y: y, W: w, H: h},
} }
return &vp return &vp
} }
@ -47,8 +47,8 @@ func (vp *ViewPort) ToVPCoords(c types.Coords) (newCoords types.Coords, err erro
//coords on map to coords on vp //coords on map to coords on vp
x, y := c.X-vp.CameraCoords.X, c.Y-vp.CameraCoords.Y x, y := c.X-vp.CameraCoords.X, c.Y-vp.CameraCoords.Y
if x < 0 || y < 0 || x > vp.W || y > vp.H { if x < 0 || y < 0 || x > vp.W || y > vp.H {
return types.Coords{-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" loggerKey = "logger"
) )
type ClientCtx struct { type clientCtx struct {
context.Context 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.Context(context.TODO())
ctx = context.WithValue(ctx, configKey, config) ctx = context.WithValue(ctx, configKey, config)
ctx = context.WithValue(ctx, loggerKey, logger) ctx = context.WithValue(ctx, loggerKey, logger)
return ClientCtx{ ctx} ClientState = clientCtx{ctx}
} }
func Config(c context.Context) *util.Config { func Config(c context.Context) *util.Config {
@ -31,7 +33,11 @@ func Config(c context.Context) *util.Config {
return cfg 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) logger, ok := c.Value(loggerKey).(*zerolog.Logger)
if !ok { if !ok {
panic(fmt.Errorf("no access to logger from context")) panic(fmt.Errorf("no access to logger from context"))

View File

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

View File

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