Merge remote-tracking branch 'origin/master'

# Conflicts:
#	cmd/game/main.go
#	delaunay_test.go
#	engine/screens/devmenu.go
#	go.mod
This commit is contained in:
thefish
2022-10-12 16:07:35 +03:00
29 changed files with 477 additions and 332 deletions

View File

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

View File

@ -124,7 +124,7 @@ func (ps *precomputedShade) FindByCoords(c types.Coords) (int, *Cell, error) {
func (ps *precomputedShade) IsInFov(coords types.Coords) bool {
rc := ps.fromLevelCoords(coords)
if rc.X == 0 && rc.Y ==0 {return true}
if rc.X == 0 && rc.Y ==0 {return true}
_, cell, err := ps.FindByCoords(rc)
if err != nil {
return false

View File

@ -1,7 +1,6 @@
package gamemap
import (
"context"
"lab.zaar.be/thefish/alchemyst-go/engine/ecs"
"lab.zaar.be/thefish/alchemyst-go/engine/types"
"lab.zaar.be/thefish/alchemyst-go/util/appctx"
@ -14,7 +13,6 @@ var mapHeight = 90
type Level struct {
types.Rect
ctx context.Context
Name string
Branch string
Depth int
@ -66,23 +64,22 @@ func (l *Level) MakePassByXY (x,y int, tile *Tile) {
func (l *Level) Put (x, y int, tileFunc interface{}) {
tile := tileFunc.(func() *Tile)()
if tile == nil {
appctx.Logger(l.ctx).Fatal().Msgf("Got non-tile type to put into level: %v", tile)
appctx.Logger().Fatal().Msgf("Got non-tile type to put into level: %v", tile)
}
if l.InBounds(types.Coords{x, y}) {
l.Tiles[y*l.W+x] = tile
}
}
func NewLevel(ctx context.Context, branch string, depth int) *Level {
func NewLevel(branch string, depth int) *Level {
l := &Level{
ctx: ctx,
Name: branch + string(depth),
Depth: depth,
Rect: types.NewRect(0,0, mapWidth, mapHeight),
}
l.Tiles = make([]*Tile, l.W*l.H)
appctx.Logger(ctx).Debug().Msgf("Generating level of branch %s depth %d", branch, depth)
appctx.Logger().Debug().Msgf("Generating level of branch %s depth %d", branch, depth)
return l
}

View File

@ -1,7 +1,6 @@
package mapgens
import (
"context"
"lab.zaar.be/thefish/alchemyst-go/engine/gamemap"
"lab.zaar.be/thefish/alchemyst-go/engine/types"
"lab.zaar.be/thefish/alchemyst-go/util"
@ -39,9 +38,9 @@ var fges = map[int]types.RectFill{
},
}
func GetRandomRoomList(ctx context.Context, rng *util.RNG, l *gamemap.Level, maxRooms, minRoomSize, maxRoomSize int, ) []gamemap.Room{
func GetRandomRoomList(rng *util.RNG, l *gamemap.Level, maxRooms, minRoomSize, maxRoomSize int, ) []gamemap.Room{
rooms := make([]gamemap.Room, 0)
pfLoader := gamemap.NewPrefabLoader(ctx)
pfLoader := gamemap.NewPrefabLoader()
pfRooms := pfLoader.PrefabRoomsList()
var fillage types.RectFill
@ -102,11 +101,12 @@ func GetRandomRoomList(ctx context.Context, rng *util.RNG, l *gamemap.Level, max
return rooms
}
func BlitToLevel (ctx context.Context, l *gamemap.Level, rooms[]gamemap.Room) {
//fixme overlapping rooms
func BlitToLevel (l *gamemap.Level, rooms[]gamemap.Room) {
for _, room := range rooms {
err := room.BlitToLevel(ctx, l)
err := room.BlitToLevel(l)
if err != nil {
appctx.Logger(ctx).Err(err)
appctx.Logger().Err(err)
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,6 @@
package gamemap
import (
"context"
"encoding/json"
"io/ioutil"
"lab.zaar.be/thefish/alchemyst-go/engine/items"
@ -42,12 +41,10 @@ func LoadPrefabFile(filename string) (*PrefabFile, error) {
return instance, nil
}
type PrefabLoader struct {
ctx context.Context
}
type PrefabLoader struct {}
func NewPrefabLoader(ctx context.Context) PrefabLoader {
return PrefabLoader{ctx: ctx}
func NewPrefabLoader() PrefabLoader {
return PrefabLoader{}
}
func (pfbl PrefabLoader) PrefabRoomsList() []Room {
@ -106,7 +103,7 @@ func (pfbl PrefabLoader) PrefabRoomsList() []Room {
} else {
f, ok = TileTypeMap[shortName]
if (!ok) {
appctx.Logger(pfbl.ctx).Warn().Msgf("Unknown tile: %s", shortName)
appctx.Logger().Warn().Msgf("Unknown tile: %s", shortName)
}
}
room.Geometry[i+ j*room.W] = f

View File

@ -1,7 +1,6 @@
package gamemap
import (
"context"
"errors"
"fmt"
"lab.zaar.be/thefish/alchemyst-go/engine/items"
@ -33,7 +32,7 @@ func (r *Room) Put (x, y int, tileFunc interface{}) {
}
}
func (room *Room) BlitToLevel(ctx context.Context, l *Level) error {
func (room *Room) BlitToLevel(l *Level) error {
//copy tiles like this:
//https://stackoverflow.com/questions/21011023/copy-pointer-values-a-b-in-golang
@ -51,7 +50,7 @@ func (room *Room) BlitToLevel(ctx context.Context, l *Level) error {
//check underlying tile
if underlyingTile == nil ||
underlyingTile.Name != "Wall" {
appctx.Logger(ctx).Warn().Msg("Invalid blit!")
appctx.Logger().Warn().Msg("Invalid blit!")
return invalidBlit
}
l.Put(mapCoords.X, mapCoords.Y, tileFunc)

View File

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

View File

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

View File

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

View File

@ -63,6 +63,8 @@ func (is *InventoryScreen) Enter() {
is.prepared.Prepare(is)
}
//fixme key names to action constants!
//fixme unify scrolling controls!
func (is *InventoryScreen) HandleInput(input string) {
if strings.Contains(string(runeIndex), strings.Replace(input, "Shift+", "", -1)) {
if strings.Contains("Shift+", input) {
@ -76,7 +78,7 @@ func (is *InventoryScreen) HandleInput(input string) {
return
}
switch input {
case "Up":
case "Up", "k":
is.cursor = is.cursor - 1
if is.cursor < 0 {
is.cursor = 0
@ -88,7 +90,7 @@ func (is *InventoryScreen) HandleInput(input string) {
}
}
break
case "Down":
case "Down", "j":
is.cursor = is.cursor + 1
if is.cursor >= len(is.prepared) {
is.cursor = len(is.prepared) - 1
@ -119,7 +121,8 @@ func (is *InventoryScreen) HandleInput(input string) {
}
break
case "enter":
//select current under cursor
//show actions menu for item under cursor
//fixme implement
break;
case "Escape":
fallthrough

View File

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

View File

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