Merge branch 'master' of lab.zaar.be:thefish/alchemyst-go

This commit is contained in:
anton.gurov 2019-11-11 11:33:38 +03:00
commit d304ff7837
38 changed files with 858 additions and 223 deletions

View File

@ -30,7 +30,7 @@ build.game.linux64:
php update-config-version.php
cp $(CWD)/vendor/lab.zaar.be/thefish/bearlibterminal/libBearLibTerminal.so $(DISTFOLDER) && \
cp $(CWD)/config.json $(DISTFOLDER) && \
cp -r $(CWD)/resources $(DISTFOLDER) && \
cp -r $(CWD)/assets $(DISTFOLDER) && \
$(GO) build -ldflags $(LDFLAGS) -o $(DISTFOLDER)/game $(CWD)/cmd/game/main.go && \
strip $(DISTFOLDER)/game && \
chmod +x $(DISTFOLDER)/game && \
@ -40,7 +40,7 @@ build.game.win64:
php update-config-version.php
cp $(CWD)/lib/win64/BearLibTerminal.dll $(DISTFOLDER) && \
cp $(CWD)/config.json $(DISTFOLDER) && \
cp -r $(CWD)/resources $(DISTFOLDER) && \
cp -r $(CWD)/assets $(DISTFOLDER) && \
GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ $(GO) build -o $(DISTFOLDER)/game.exe -ldflags $(LDFLAGS) $(CWD)/cmd/game/main.go && \
/usr/bin/x86_64-w64-mingw32-strip $(DISTFOLDER)/game.exe && \
cd $(DISTFOLDER) && zip -r ../$(PROJECT_NAME)-$(OS)-${PKG_VER}.zip . -x *.git*
@ -48,7 +48,7 @@ build.game.win64:
build.game.mac:
cp $(CWD)/lib/mac/libBearLibTerminal.dylib $(DISTFOLDER) && \
cp $(CWD)/config.json $(DISTFOLDER) && \
cp -r $(CWD)/resources $(DISTFOLDER) && \
cp -r $(CWD)/assets $(DISTFOLDER) && \
OSXCROSS_NO_INCLUDE_PATH_WARNINGS=1 MACOSX_DEPLOYMENT_TARGET=10.6 CC=o64-clang CXX=o64-clang++ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 $(GO) build -ldflags $(LDFLAGS) -o $(DISTFOLDER)/game $(CWD)/cmd/game/main.go && \
strip $(DISTFOLDER)/game.exe && \
cd $(DISTFOLDER) && zip -r ../$(PROJECT_NAME)-$(OS)-${PKG_VER}.zip . -x *.git*

49
TODO Normal file
View File

@ -0,0 +1,49 @@
Assets and i18n:
- move tile settings to json, generate from there (part of prefabs)
- prepare all interface entries for i18n
- all texts (terrain, mobs, quests) also in at least 2 languages
ECS & engine:
- implement time queue (how to deal with closures?) (?) github.com/thefish/sheduleq - get rid od time.Now inside
- move all rendering to systems
- try to move input handling to systems
Dungeon and branches:
General:
- river! (dig_bezier)
- erosion (?)
- global map of valley
Prefabs:
+ load prefabs
- compose from gens and prefabs
- editor for prefabs
Mapgen
- use delaunay -> minimum spanning tree for room connection (Краскал в gonum)
github.com/algds/kruskals - MST
github.com/esimov/triangle - delaunay
- или граф относительных окрестностей
Combat:
- generate skeleton / intesines / muscle / eyes&ears & fingers from templates
- serializable
- config in outer files
- mass
- damage from skill / mass / speed / material density
- no hitpoints! blood is the life source
Items:
- pickup
- drop
- use
Mobs:
basic:
- place mobs
- move mobs
advanced:
- ai
- dijkstra maps
Quest engine:
- look at parsers like URQL etc
- distorted Aschenputtel story / partisans / rapist prince / Grey Mountains

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

39
assets/prefabs/test.json Normal file
View File

@ -0,0 +1,39 @@
{
"default_tile_legend": {
"?": "any",
"#": "wall",
".": "floor",
"+": "connector"
},
"default_mobs_legend": {},
"default_item_legend": {},
"prefabs": [
{"name": "test_room_1",
"tile_legend": {
"D": "decorated_wall",
"w": "water",
"W": "deep_water"
},
"mobs_legend": {},
"item_legend": {},
"size": {"x":16, "y":13},
"body": [
"???????#+#??????",
"???DDDD#.#DDDD??",
"###D.........DD?",
"+....wwwwwww..D?",
"###..wWWWWWw..D?",
"??#..wW...Ww..##",
"??D..wW.D.Ww...+",
"??D..wW...Ww..##",
"??D..wWWWWWw..#?",
"??D..wwwwwww..D?",
"??DD.........DD?",
"???DDDD#.#DDDD??",
"???????#+#??????"
]
}
]
}

View File

@ -69,21 +69,25 @@ func main() {
go decodeInput(mainCtx, mw.GetLayer("base"))
//fixme set up (load / generate) level
level, rooms := mapgens.DefaultGen(gamemap.NewLevel(mainCtx, "test", 1))
level, rooms := mapgens.DefaultGen(mainCtx, gamemap.NewLevel(mainCtx, "test", 1))
State.Level = level
sidebarWidth := 0
//Set up viewport
vp := mainwindow.NewViewPort(30, 0, (mw.W - 30), (mw.H - 0), mw.GetLayer("base"))
vp := mainwindow.NewViewPort(sidebarWidth, 0, (mw.W - sidebarWidth), (mw.H - 0), mw.GetLayer("base"))
go vp.Listen(State)
//set up controller
controller := ecs.NewController()
controller.MapComponentClass("coords", types.Coords{})
controller.MapComponentClass("appearance", types.Appearance{})
controller.MapComponentClass("mob", mob.Mob{})
controller.MapComponentClass("moveable", movement.Moveable{})
controller.MapComponentClass(ecs.CoordsComponent, types.Coords{})
controller.MapComponentClass(ecs.AppearanceComponent, types.Appearance{})
controller.MapComponentClass(ecs.MobComponent, mob.Mob{})
controller.MapComponentClass(ecs.MoveableComponent, movement.Moveable{})
controller.MapComponentClass(ecs.CarriedComponent, movement.Moveable{})
controller.MapComponentClass(ecs.UsableComponent, movement.Moveable{})
moveable := movement.Moveable{
Controller: controller,
@ -106,7 +110,7 @@ func main() {
SetBgColor("#ef1d494f").
SetFgColor("white").
SetItems([]interface{}{
"hjklyubn 12346789 or arrow keys - move",
"hjklyubn, NumPad 12346789, arrow keys - move",
"s or . - pass turn",
"g or , - pick up item",
"i - inventory",
@ -116,9 +120,35 @@ func main() {
"z or Z - cast a spell",
"p - pray",
"Ctrl+p - message log",
}),
}).MakeList(),
)
screenMgr.AddScreen("inventory", screens.NewMenuScreen(
mw,
screenMgr,
"Inventory",
"Items in your backpack:",
//"[color=yellow]Note[/color]: Many of these are not implemented yet",
"",
types.NewCenteredRect(mw.Rect, 70, 25),
true, ).
SetBgColor("#ef305c70").
SetFgColor("white").
SetItems([]interface{}{
"hjklyubn, NumPad 12346789, arrow keys - move",
"s or . - pass turn",
"g or , - pick up item",
"i - inventory",
"? - this screen",
"Ctrl+q - exit",
"f or F - fire or throw weapon",
"z or Z - cast a spell",
"p - pray",
"Ctrl+p - message log",
}).MakeList(),
)
screenMgr.SetScreenByName("title")
//fixme
@ -144,7 +174,7 @@ func main() {
controller.AddComponent(player, &types.Appearance{
Glyph: &types.PlainGlyphHolder{"@"},
ColorSet: &types.TileColorSet{
ColorSet: types.TileColorSet{
Fg: &types.PlainColorHolder{255, 255, 255, 255},
},
})
@ -206,7 +236,7 @@ func decodeInput(ctx util.ClientCtx, baseLayer *mainwindow.Layer) {
return
}
var pressed = ""
var isModifier, _ = util.InArray(keycode, modifiers)
var isModifier, _ = util.IntInSlice(keycode, modifiers)
if !isModifier {
pressed = ui.Scancodemap[keycode]
@ -226,7 +256,7 @@ func decodeInput(ctx util.ClientCtx, baseLayer *mainwindow.Layer) {
//fixme testing only
case "F10":
State.Do(func() {
blt.Set("window: size=100x47; font: ./resources/fonts-ttf/UbuntuMono-R.ttf, size=11;")
blt.Set("window: size=100x47; font: ./assets/ttf/UbuntuMono-R.ttf, size=11;")
})
case "Ctrl+q":
//fallthrough

View File

@ -1 +1,7 @@
package main
import "lab.zaar.be/thefish/alchemyst-go/engine/gamemap"
func main() {
_ = gamemap.PrefabRoomsList()
}

View File

@ -0,0 +1,18 @@
package alchemyst_go
import (
"encoding/json"
"fmt"
"lab.zaar.be/thefish/alchemyst-go/engine/gamemap"
"testing"
)
func TestSerializeTile (t *testing.T) {
wt := gamemap.NewWaterTile()
txt, err := json.Marshal(wt)
if err != nil {
t.Log(err)
t.Fail()
}
fmt.Printf("%s", txt)
}

View File

@ -4,7 +4,7 @@
"sizeX": 100,
"sizeY": 47,
"fpsLimit": 60,
"font": "./resources/fonts-ttf/LiberationMono-Bold.ttf",
"font": "./assets/fonts/ttf/LiberationMono-Bold.ttf",
"fontSize": "9x14",
"verbosity": "debug"
}

View File

@ -2,8 +2,13 @@ package ecs
// ECS system by jcerise, github.com/jcerise/gogue
import "reflect"
const AppearanceComponent = "appearance"
const CoordsComponent = "coords"
const MobComponent = "mob"
const MoveableComponent = "movable"
const CarriedComponent = "carried"
const UsableComponent = "usable"
type Component interface {
TypeOf() reflect.Type
Type() string
}

View File

@ -4,17 +4,16 @@ package ecs
import (
"fmt"
"reflect"
"sort"
)
type Controller struct {
systems map[reflect.Type]System
systems map[string]System
sortedSystems map[int][]System
priorityKeys []int
nextEntityID Entity
components map[reflect.Type][]Entity
entities map[Entity]map[reflect.Type]Component
components map[string][]Entity
entities map[Entity]map[string]Component
deadEntities []int
// The component map will keep track of what components are available
@ -24,12 +23,12 @@ type Controller struct {
// NewController is a convenience/constructor method to properly initialize a new processor
func NewController() *Controller {
controller := Controller{}
controller.systems = make(map[reflect.Type]System)
controller.systems = make(map[string]System)
controller.sortedSystems = make(map[int][]System)
controller.priorityKeys = []int{}
controller.nextEntityID = 0
controller.components = make(map[reflect.Type][]Entity)
controller.entities = make(map[Entity]map[reflect.Type]Component)
controller.components = make(map[string][]Entity)
controller.entities = make(map[Entity]map[string]Component)
controller.deadEntities = []int{}
controller.componentMap = make(map[string]Component)
@ -47,7 +46,7 @@ func (c *Controller) CreateEntity(components []Component) Entity {
}
}
c.entities[c.nextEntityID] = make(map[reflect.Type]Component)
c.entities[c.nextEntityID] = make(map[string]Component)
return c.nextEntityID
}
@ -89,7 +88,7 @@ func (c *Controller) GetMappedComponentClass(componentName string) Component {
// as you can check which entites are associated with a component, and vice versa.
func (c *Controller) AddComponent(entity Entity, component Component) {
// First, get the type of the component
componentType := reflect.TypeOf(component)
componentType := component.Type()
// Record that the component type is associated with the entity.
c.components[componentType] = append(c.components[componentType], entity)
@ -97,14 +96,14 @@ func (c *Controller) AddComponent(entity Entity, component Component) {
// Now, check to see if the entity is already tracked in the controller entity list. If it is not, add it, and
// associate the component with it
if _, ok := c.entities[entity]; !ok {
c.entities[entity] = make(map[reflect.Type]Component)
c.entities[entity] = make(map[string]Component)
}
c.entities[entity][componentType] = component
}
// HasComponent checks a given entity to see if it has a given component associated with it
func (c *Controller) HasComponent(entity Entity, componentType reflect.Type) bool {
func (c *Controller) HasComponent(entity Entity, componentType string) bool {
if _, ok := c.entities[entity][componentType]; ok {
return true
} else {
@ -113,7 +112,7 @@ func (c *Controller) HasComponent(entity Entity, componentType reflect.Type) boo
}
// GetComponent returns the component instance for a component type, if one exists for the provided entity
func (c *Controller) GetComponent(entity Entity, componentType reflect.Type) Component {
func (c *Controller) GetComponent(entity Entity, componentType string) Component {
// Check the given entity has the provided component
if c.HasComponent(entity, componentType) {
return c.entities[entity][componentType]
@ -123,7 +122,7 @@ func (c *Controller) GetComponent(entity Entity, componentType reflect.Type) Com
}
// GetEntity gets a specific entity, and all of its component instances
func (c *Controller) GetEntity(entity Entity) map[reflect.Type]Component {
func (c *Controller) GetEntity(entity Entity) map[string]Component {
for i, _ := range c.entities {
if i == entity {
return c.entities[entity]
@ -134,13 +133,13 @@ func (c *Controller) GetEntity(entity Entity) map[reflect.Type]Component {
}
// GetEntities returns a map of all entities and their component instances
func (c *Controller) GetEntities() map[Entity]map[reflect.Type]Component {
func (c *Controller) GetEntities() map[Entity]map[string]Component {
return c.entities
}
// 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 reflect.Type) []Entity {
func (c *Controller) GetEntitiesWithComponent(componentType string) []Entity {
entitiesWithComponent := make([]Entity, 0)
for entity := range c.entities {
if c.HasComponent(entity, componentType) {
@ -152,7 +151,7 @@ func (c *Controller) GetEntitiesWithComponent(componentType reflect.Type) []Enti
}
// UpdateComponent updates a component on an entity with a new version of the same component
func (c *Controller) UpdateComponent(entity Entity, componentType reflect.Type, newComponent Component) Entity {
func (c *Controller) UpdateComponent(entity Entity, componentType string, newComponent Component) Entity {
// First, remove the component in question (Don't actually update things, but rather remove and replace)
c.RemoveComponent(entity, componentType)
@ -165,7 +164,7 @@ func (c *Controller) UpdateComponent(entity Entity, componentType reflect.Type,
// DeleteComponent will delete a component instance from an entity, based on component type. It will also remove the
// association between the component and the entity, and remove the component from the processor completely if no
// other entities are using it.
func (c *Controller) RemoveComponent(entity Entity, componentType reflect.Type) Entity {
func (c *Controller) RemoveComponent(entity Entity, componentType string) Entity {
// Find the index of the entity to operate on in the components slice
index := -1
for i, v := range c.components[componentType] {
@ -194,7 +193,7 @@ func (c *Controller) RemoveComponent(entity Entity, componentType reflect.Type)
// numeric order, low to high. If multiple systems are registered as the same priority, they will be randomly run within
// that priority group.
func (c *Controller) AddSystem(system System, priority int) {
systemType := reflect.TypeOf(system)
systemType := system.SystemType()
if _, ok := c.systems[systemType]; !ok {
// A system of this type has not been added yet, so add it to the systems list
@ -214,10 +213,10 @@ func (c *Controller) AddSystem(system System, priority int) {
// Process kicks off system processing for all systems attached to the controller. Systems will be processed in the
// order they are found, or if they have a priority, in priority order. If there is a mix of systems with priority and
// without, systems with priority will be processed first (in order).
func (c *Controller) Process(excludedSystems []reflect.Type) {
func (c *Controller) Process(excludedSystems []string) {
for _, key := range c.priorityKeys {
for _, system := range c.sortedSystems[key] {
systemType := reflect.TypeOf(system)
systemType := system.SystemType()
// Check if the current system type was marked as excluded on this call. If it was, not process it.
if !TypeInSlice(systemType, excludedSystems) {
@ -228,7 +227,7 @@ func (c *Controller) Process(excludedSystems []reflect.Type) {
}
// HasSystem checks the controller to see if it has a given system associated with it
func (c *Controller) HasSystem(systemType reflect.Type) bool {
func (c *Controller) HasSystem(systemType string) bool {
if _, ok := c.systems[systemType]; ok {
return true
} else {
@ -237,7 +236,7 @@ func (c *Controller) HasSystem(systemType reflect.Type) bool {
}
// ProcessSystem allows for on demand processing of individual systems, rather than processing all at once via Process
func (c *Controller) ProcessSystem(systemType reflect.Type) {
func (c *Controller) ProcessSystem(systemType string) {
if c.HasSystem(systemType) {
system := c.systems[systemType]
system.Process()

View File

@ -2,6 +2,9 @@ package ecs
// ECS system by jcerise, github.com/jcerise/gogue
const MobRenderSystem = "mobrender"
type System interface {
Process()
SystemType() string
}

View File

@ -13,11 +13,11 @@ type MobRenderSystem struct {
func (mrs MobRenderSystem) Process(){
for e := range mrs.EntityController.GetEntities() {
if mrs.EntityController.HasComponent(e, types.Coords{}.TypeOf()) &&
mrs.EntityController.HasComponent(e, types.Appearance{}.TypeOf()) {
if mrs.EntityController.HasComponent(e, ecs.CoordsComponent) &&
mrs.EntityController.HasComponent(e, ecs.AppearanceComponent) {
pos := mrs.EntityController.GetComponent(e, types.Coords{}.TypeOf()).(types.Coords)
appearance := mrs.EntityController.GetComponent(e, types.Appearance{}.TypeOf()).(types.Appearance)
pos := mrs.EntityController.GetComponent(e, ecs.CoordsComponent).(types.Coords)
appearance := mrs.EntityController.GetComponent(e, ecs.AppearanceComponent).(types.Appearance)
//fixme
// Clear the cell this entity occupies, so it is the only glyph drawn there
@ -30,4 +30,8 @@ func (mrs MobRenderSystem) Process(){
//gogue.PrintGlyph(pos.X, pos.Y, appearance.Glyph, "", appearance.Layer)
}
}
}
func (mrs MobRenderSystem) SystemType() string {
return ecs.MobRenderSystem
}

View File

@ -2,8 +2,6 @@ package ecs
// ECS system by jcerise, github.com/jcerise/gogue
import "reflect"
// IntInSlice will return true if the integer value provided is present in the slice provided, false otherwise.
func IntInSlice(a int, list []int) bool {
for _, b := range list {
@ -15,7 +13,7 @@ func IntInSlice(a int, list []int) bool {
}
// TypeInSlice will return true if the reflect.Type provided is present in the slice provided, false otherwise.
func TypeInSlice(a reflect.Type, list []reflect.Type) bool {
func TypeInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
return true

View File

@ -1,3 +0,0 @@
- load prefabs
- compose from gens and prefabs
- editor for prefabs

View File

@ -82,7 +82,3 @@ func (l *Level) SetAllInvisible() {
}
}
type Room struct {
*types.Rect
Center types.Coords
}

View File

@ -1,20 +1,52 @@
package mapgens
import (
"fmt"
"lab.zaar.be/thefish/alchemyst-go/engine/gamemap"
"lab.zaar.be/thefish/alchemyst-go/engine/types"
"lab.zaar.be/thefish/alchemyst-go/util"
)
//fixme move to config
var minRoomSize = 3
var maxRoomSize = 22
var maxrooms = 30
var maxrooms = 50
//fixme make closure to stack them
func DefaultGen(l *gamemap.Level) (*gamemap.Level, []*gamemap.Room) {
var fges = map[int]types.RectFill{
1: types.RectFill{
Top: func() *gamemap.Tile { return gamemap.NewWall() },
Bottom: func() *gamemap.Tile { return gamemap.NewWall() },
Left: func() *gamemap.Tile { return gamemap.NewWall() },
Right: func() *gamemap.Tile { return gamemap.NewWall() },
BottomLeft: func() *gamemap.Tile { return gamemap.NewWall() },
BottomRight: func() *gamemap.Tile { return gamemap.NewWall() },
TopLeft: func() *gamemap.Tile { return gamemap.NewWall() },
TopRight: func() *gamemap.Tile { return gamemap.NewWall() },
Body: func() *gamemap.Tile { return gamemap.NewFloor() },
},
2: types.RectFill{
Top: func() *gamemap.Tile { return gamemap.NewWaterTile() },
Bottom: func() *gamemap.Tile { return gamemap.NewWaterTile() },
Left: func() *gamemap.Tile { return gamemap.NewWaterTile() },
Right: func() *gamemap.Tile { return gamemap.NewWaterTile() },
BottomLeft: func() *gamemap.Tile { return gamemap.NewWaterTile() },
BottomRight: func() *gamemap.Tile { return gamemap.NewWaterTile() },
TopLeft: func() *gamemap.Tile { return gamemap.NewWaterTile() },
TopRight: func() *gamemap.Tile { return gamemap.NewWaterTile() },
Body: func() *gamemap.Tile { return gamemap.NewDeepWaterTile() },
},
}
func DefaultGen(ctx util.ClientCtx,l *gamemap.Level) (*gamemap.Level, []*gamemap.Room) {
rng := util.NewRNG()
//load prefabs
//pfLoader := gamemap.NewPrefabLoader(ctx)
//pfRooms := pfLoader.PrefabRoomsList()
//fill with walls
for i := 0; i < l.W; i ++ {
for j := 0; j < l.H; j++ {
@ -24,35 +56,33 @@ func DefaultGen(l *gamemap.Level) (*gamemap.Level, []*gamemap.Room) {
rooms := make([]*gamemap.Room, 0)
//one wall around whole level guaranteed
levelBoundary := types.NewRect(l.X + 1, l.Y + 1, l.W - 2, l.H - 2)
for i := 0; i < maxrooms; i++ {
newRoom := &gamemap.Room{
Rect: types.NewRect(
rng.Range(l.X, l.W),
rng.Range(l.Y, l.H),
rng.Range(minRoomSize, maxRoomSize),
rng.Range(minRoomSize, maxRoomSize),
)}
newRoom.Center = types.Coords{newRoom.X + newRoom.W / 2, newRoom.Y + newRoom.H / 2}
failed := false
if !levelBoundary.InBounds(types.Coords{newRoom.X, newRoom.Y}) {
failed = true
var fillage types.RectFill
fillage = fges[rng.GetWeightedEntity(map[int]int{1: 10, 2: 1})]
var newRoom *gamemap.Room
//if rng.Range(0, 5) > 3 {
newRoom = gamemap.NewRandomRectRoom(
rng,
rng.Range(minRoomSize, maxRoomSize),
rng.Range(minRoomSize, maxRoomSize),
fillage,
)
//} else {
// prefab
// newRoom = &pfRooms[0]
//}
where := types.Coords{
rng.Range(1, l.W -2 - newRoom.W),
rng.Range(1, l.H - 2 - newRoom.H),
}
if !failed && !l.InBounds(types.Coords{newRoom.X + newRoom.W, newRoom.Y + newRoom.H}) {
failed = true
}
newRoom.MoveToCoords(where)
if !failed {
for _, otherRoom := range rooms {
if otherRoom.Intersects(newRoom.Rect) {
failed = true
break
}
for _, otherRoom := range rooms {
if otherRoom.Intersects(newRoom.Rect) {
failed = true
break
}
}
@ -63,58 +93,39 @@ func DefaultGen(l *gamemap.Level) (*gamemap.Level, []*gamemap.Room) {
//addStairs(rooms)
//itemize(rooms)
}
fges := map[int]types.RectFill{
1: types.RectFill{
Top: func() *gamemap.Tile { return gamemap.NewWall() },
Bottom: func() *gamemap.Tile { return gamemap.NewWall() },
Left: func() *gamemap.Tile { return gamemap.NewWall() },
Right: func() *gamemap.Tile { return gamemap.NewWall() },
BottomLeft: func() *gamemap.Tile { return gamemap.NewWall() },
BottomRight: func() *gamemap.Tile { return gamemap.NewWall() },
TopLeft: func() *gamemap.Tile { return gamemap.NewWall() },
TopRight: func() *gamemap.Tile { return gamemap.NewWall() },
Body: func() *gamemap.Tile { return gamemap.NewFloor() },
},
2: types.RectFill{
Top: func() *gamemap.Tile { return gamemap.NewWaterTile() },
Bottom: func() *gamemap.Tile { return gamemap.NewWaterTile() },
Left: func() *gamemap.Tile { return gamemap.NewWaterTile() },
Right: func() *gamemap.Tile { return gamemap.NewWaterTile() },
BottomLeft: func() *gamemap.Tile { return gamemap.NewWaterTile() },
BottomRight: func() *gamemap.Tile { return gamemap.NewWaterTile() },
TopLeft: func() *gamemap.Tile { return gamemap.NewWaterTile() },
TopRight: func() *gamemap.Tile { return gamemap.NewWaterTile() },
Body: func() *gamemap.Tile { return gamemap.NewDeepWaterTile() },
},
}
//build delannay graph from room center
var fillage types.RectFill
//refine it to minimum spanning tree
//connect accordingly
for _, room := range rooms {
fillage = fges[rng.GetWeightedEntity(map[int]int{1:10, 2:1})]
room.Blit(fillage, l)
err := room.BlitToLevel(l)
if err != nil {
fmt.Printf("err: %v", err)
}
}
for idx, room := range rooms {
if idx > 0 {
connectRooms(l, room, rooms[idx-1], fillage, rng.Range(0,1))
connectRooms(l, room, rooms[idx-1], rng.Range(0, 1))
}
}
return l, rooms
}
func connectRooms (l *gamemap.Level, room, otherRoom *gamemap.Room, fillage types.RectFill, toss int) {
func connectRooms(l *gamemap.Level, room, otherRoom *gamemap.Room, toss int) {
if toss == 0 {
digHTunnel(l, room.Center.X,otherRoom.Center.X,room.Center.Y, fillage)
digVTunnel(l, room.Center.Y,otherRoom.Center.Y,otherRoom.Center.X, fillage)
digHTunnel(l, room.Center.X, otherRoom.Center.X, room.Center.Y)
digVTunnel(l, room.Center.Y, otherRoom.Center.Y, otherRoom.Center.X)
} else {
digVTunnel(l, room.Center.Y, otherRoom.Center.Y, room.Center.Y, fillage)
digHTunnel(l, room.Center.X, otherRoom.Center.X, otherRoom.Center.Y, fillage)
digVTunnel(l, room.Center.Y, otherRoom.Center.Y, room.Center.Y)
digHTunnel(l, room.Center.X, otherRoom.Center.X, otherRoom.Center.Y)
}
}
func digHTunnel(l *gamemap.Level, x1,x2,y int, fillage types.RectFill) {
func digHTunnel(l *gamemap.Level, x1, x2, y int) {
var start, finish int
if x1 < x2 {
start = x1
@ -125,13 +136,13 @@ func digHTunnel(l *gamemap.Level, x1,x2,y int, fillage types.RectFill) {
}
for i := start; i <= finish; i++ {
if l.InBounds(types.Coords{i, y}) {
l.SetTileByXY(i, y, fillage.Body.(func() *gamemap.Tile)())
l.SetTileByXY(i, y, gamemap.NewFloor())
//l.Tiles[i][y] = gamemap.NewFloor()
}
}
}
func digVTunnel(l *gamemap.Level, y1,y2,x int, fillage types.RectFill) {
func digVTunnel(l *gamemap.Level, y1, y2, x int) {
var start, finish int
if y1 < y2 {
start = y1
@ -142,7 +153,7 @@ func digVTunnel(l *gamemap.Level, y1,y2,x int, fillage types.RectFill) {
}
for i := start; i <= finish; i++ {
if l.InBounds(types.Coords{x, i}) {
l.SetTileByXY(x, i, fillage.Body.(func() *gamemap.Tile)())
l.SetTileByXY(x, i, gamemap.NewFloor())
}
}
}

124
engine/gamemap/prefab.go Normal file
View File

@ -0,0 +1,124 @@
package gamemap
import (
"encoding/json"
"fmt"
"io/ioutil"
"lab.zaar.be/thefish/alchemyst-go/engine/items"
"lab.zaar.be/thefish/alchemyst-go/engine/mob"
"lab.zaar.be/thefish/alchemyst-go/engine/types"
"lab.zaar.be/thefish/alchemyst-go/util"
)
type PrefabFile struct {
DefaultTileLegend map[string]string `json:"default_tile_legend"`
DefaultMobsLegend map[string]string `json:"default_mobs_legend"`
DefaultItemLegend map[string]string `json:"default_item_legend"`
Prefabs []PrefabRecord
}
type PrefabRecord struct {
name string
Size struct {
X int `json:"x"`
Y int `json:"y"`
} `json:"Size"`
TileLegend map[string]string `json:"default_tile_legend"`
MobsLegend map[string]string `json:"default_mobs_legend"`
ItemLegend map[string]string `json:"default_item_legend"`
Body []string `json:"body"`
}
func LoadPrefabFile(filename string) (*PrefabFile, error) {
data, err := ioutil.ReadFile(filename)
if err!= nil {
return nil, err
}
instance := &PrefabFile{}
err = json.Unmarshal(data, instance)
if err != nil {
return nil, err
}
return instance, nil
}
type PrefabLoader struct {
ctx util.ClientCtx
}
func NewPrefabLoader(ctx util.ClientCtx) PrefabLoader {
return PrefabLoader{ctx: ctx}
}
func (pfbl PrefabLoader) PrefabRoomsList() []Room {
rooms := make([]Room, 0)
file, err := LoadPrefabFile("./assets/prefabs/test.json")
if err !=nil {
panic(err)
}
for _, rawPrefab := range file.Prefabs {
//prepare actual legends
currentTileLegend := file.DefaultTileLegend
currentMobsLegend := file.DefaultMobsLegend
currentItemLegend := file.DefaultItemLegend
fmt.Printf("%v",rawPrefab)
for k,v := range rawPrefab.TileLegend {
currentTileLegend[k] = v
}
for k,v := range rawPrefab.MobsLegend {
currentMobsLegend[k] = v
}
for k,v := range rawPrefab.ItemLegend {
currentItemLegend[k] = v
}
room := Room{
Rect:&types.Rect{0, 0, rawPrefab.Size.X, rawPrefab.Size.Y},
Center: types.Coords{rawPrefab.Size.X / 2, rawPrefab.Size.Y / 2}, //fixme
Geometry: make([]func()*Tile, rawPrefab.Size.X*rawPrefab.Size.Y),
Mobs: make([]mob.Mob, rawPrefab.Size.X*rawPrefab.Size.Y),
Items: make([]items.Carried, rawPrefab.Size.X*rawPrefab.Size.Y),
Connectors: make([]types.Coords, rawPrefab.Size.X*rawPrefab.Size.Y),
}
//make geometry
var f func() *Tile
for j := 0; j < room.H; j++ {
str := rawPrefab.Body[j]
if len(str) != room.W {
continue;
}
for i:=0; i < room.W; i++ {
ok := false
shortName := currentTileLegend[string(str[i])]
if shortName == "any" {
continue
}
if shortName == "connector" {
f = NewFloor
room.Connectors = append(room.Connectors, types.Coords{i,j})
} else {
f, ok = TileTypeMap[shortName]
}
if (!ok) {
pfbl.ctx.Logger().Warn().Msgf("Unknown tile: %s", shortName)
}
room.Geometry[i+ j*room.W] = f
}
}
//add room to list
rooms = append(rooms, room)
}
return rooms
}
var TileTypeMap = map[string]func()*Tile{
"wall": NewWall,
"floor": NewFloor,
"decorated_wall": NewDecoratedWall,
"water": NewWaterTile,
"deep_water": NewDeepWaterTile,
}

97
engine/gamemap/room.go Normal file
View File

@ -0,0 +1,97 @@
package gamemap
import (
"errors"
"fmt"
"lab.zaar.be/thefish/alchemyst-go/engine/items"
"lab.zaar.be/thefish/alchemyst-go/engine/mob"
"lab.zaar.be/thefish/alchemyst-go/engine/types"
"lab.zaar.be/thefish/alchemyst-go/util"
)
var invalidBlit = errors.New("trying to blit on existing good tile")
type Room struct {
*types.Rect
Center types.Coords
Geometry []func() *Tile
Mobs []mob.Mob
Items []items.Carried
Connectors []types.Coords
}
func (r *Room) Put (x, y int, tileFunc interface{}) {
tf := tileFunc.(func() *Tile)
if tf == nil {
return //fixme error
}
if r.InBounds(types.Coords{x, y}) {
r.Geometry[x+y*r.W] = tf
}
}
func (room *Room) BlitToLevel(l *Level) error {
//copy tiles like this:
//https://stackoverflow.com/questions/21011023/copy-pointer-values-a-b-in-golang
for j := 0; j < room.H; j++ {
for i := 0; i < room.W; i++ {
mapCoords := types.Coords{room.X + i, room.Y + j}
underlyingTile := l.GetTile(mapCoords)
tileFunc := room.Geometry[i+j*room.W]
if tileFunc == nil {
continue
}
//check underlying tile
if underlyingTile == nil ||
underlyingTile.Name != "Wall" {
fmt.Println("Invalid blit!")
return invalidBlit
}
l.Put(mapCoords.X, mapCoords.Y, tileFunc)
}
}
return nil
}
func (room *Room) MoveToCoords(where types.Coords) *Room {
//update room coords?
room.X = where.X
room.Y = where.Y
//update centers!
room.Center.X += where.X
room.Center.Y += where.Y
//update connector?
for i, _ := range room.Connectors {
room.Connectors[i].X += where.X
room.Connectors[i].Y += where.Y
}
return room
}
func NewRandomRectRoom(rng *util.RNG, w, h int, fillage types.RectFill) *Room {
newRoom := &Room{
Rect: types.NewRect(
0,
0,
w,
h,
),
Center: types.Coords{w / 2, h /2 },
Geometry: make([]func()*Tile, w*h),
}
newRoom.Blit(fillage, newRoom)
//add connectors
newRoom.Connectors = append(
newRoom.Connectors,
types.Coords{rng.Range(1, w - 2), 0},
types.Coords{rng.Range(1, w - 2), h -1},
types.Coords{0, rng.Range(1, h - 2)},
types.Coords{w - 1, rng.Range(1, h - 2)},
)
return newRoom
}

View File

@ -10,7 +10,6 @@ type Tile struct {
Description string `json:"desc"`
BlocksPass bool `json:"blocksPass"`
BlocksSight bool `json:"blocksSight"`
Colordance bool `json:"colordance"`
Explored bool
MustDraw bool
Visible bool
@ -37,6 +36,26 @@ func (t *Tile) GetRawBgColor() uint32 {
}
func NewWall() *Tile {
return &Tile{
Name: "Wall",
Description: "A dull rock wall",
BlocksPass: false,
BlocksSight: true,
Explored: false,
MustDraw: false,
Appearance: &Appearance{
Glyph: &PlainGlyphHolder{"#"},
ColorSet: TileColorSet{
Fg: &PlainColorHolder{255, 130, 110, 150},
Bg: &PlainColorHolder{255, 172, 170, 173},
DarkFg: &PlainColorHolder{255, 20, 20, 68},
DarkBg: &PlainColorHolder{255, 7, 7, 30},
},
},
}
}
func NewDecoratedWall() *Tile {
return &Tile{
Name: "Wall",
Description: "A dull rock wall",
@ -46,9 +65,15 @@ func NewWall() *Tile {
MustDraw: false,
Appearance: &Appearance{
Glyph: &PlainGlyphHolder{"#"},
ColorSet: &TileColorSet{
ColorSet: TileColorSet{
Fg: &PlainColorHolder{255, 130, 110, 150},
Bg: &PlainColorHolder{255, 172, 170, 173},
//Bg: &PlainColorHolder{255, 172, 170, 173},
Bg: &DanceColorHolder{
255,
DeviatedColorRing(172, -15, 10),
DeviatedColorRing(170, -5, 15),
DeviatedColorRing(173, -10, 10),
},
DarkFg: &PlainColorHolder{255, 20, 20, 68},
DarkBg: &PlainColorHolder{255, 7, 7, 30},
},
@ -66,7 +91,7 @@ func NewFloor() *Tile {
MustDraw: false,
Appearance: &Appearance{
Glyph: &PlainGlyphHolder{"."},
ColorSet: &TileColorSet{
ColorSet: TileColorSet{
Fg: &PlainColorHolder{255, 220, 220, 250},
Bg: &PlainColorHolder{255, 19, 19, 70},
DarkFg: &PlainColorHolder{255, 30, 20, 50},
@ -85,11 +110,10 @@ func NewWaterTile() *Tile {
BlocksSight: false,
Explored: false,
MustDraw: true, //fixme debug
Colordance: true,
Appearance: &Appearance{
Glyph: &PlainGlyphHolder{" "},
ColorSet: &TileColorSet{
ColorSet: TileColorSet{
Fg: &PlainColorHolder{255, 220, 220, 250},
Bg: &DanceColorHolder{
255,
@ -113,10 +137,9 @@ func NewDeepWaterTile() *Tile {
BlocksSight: false,
Explored: false,
MustDraw: true, //fixme debug
Colordance: true,
Appearance: &Appearance{
Glyph: &PlainGlyphHolder{" "},
ColorSet: &TileColorSet{
ColorSet: TileColorSet{
Fg: &PlainColorHolder{255, 220, 220, 250},
Bg: &DanceColorHolder{
255,

9
engine/items/carried.go Normal file
View File

@ -0,0 +1,9 @@
package items
import "lab.zaar.be/thefish/alchemyst-go/engine/ecs"
type Carried struct {}
func (c *Carried) Type() string {
return ecs.CarriedComponent
}

10
engine/items/useable.go Normal file
View File

@ -0,0 +1,10 @@
package items
import "lab.zaar.be/thefish/alchemyst-go/engine/ecs"
type Useable struct {}
func (u *Useable) Type() string {
return ecs.UsableComponent
}

View File

@ -1,8 +1,8 @@
package mob
import (
"lab.zaar.be/thefish/alchemyst-go/engine/ecs"
"lab.zaar.be/thefish/alchemyst-go/engine/types"
"reflect"
)
type Mob struct {
@ -15,6 +15,6 @@ func (m *Mob) Render() {
}
func (mob Mob) TypeOf() reflect.Type {
return reflect.TypeOf(mob)
func (mob Mob) Type() string {
return ecs.MobComponent
}

View File

@ -5,7 +5,6 @@ import (
"lab.zaar.be/thefish/alchemyst-go/engine/gamemap"
"lab.zaar.be/thefish/alchemyst-go/engine/mob"
"lab.zaar.be/thefish/alchemyst-go/engine/types"
"reflect"
)
type Moveable struct {
@ -13,6 +12,9 @@ type Moveable struct {
Level *gamemap.Level
}
func (mov Moveable) Type() string {
return ecs.MoveableComponent
}
func (mov Moveable) Walk() {
@ -23,9 +25,9 @@ func (mov Moveable) IsBlocked(c types.Coords) bool {
if mov.Level.GetTile(c).BlocksPass == true {
return true
}
list := mov.Controller.GetEntitiesWithComponent(mob.Mob{}.TypeOf())
list := mov.Controller.GetEntitiesWithComponent(ecs.MobComponent)
for idx, _ := range list {
coords := mov.Controller.GetComponent(list[idx], types.Coords{}.TypeOf())
coords := mov.Controller.GetComponent(list[idx], ecs.CoordsComponent)
if coords == nil {
continue
}
@ -33,7 +35,7 @@ func (mov Moveable) IsBlocked(c types.Coords) bool {
if coords != c {
continue
}
m := mov.Controller.GetComponent(list[idx], mob.Mob{}.TypeOf())
m := mov.Controller.GetComponent(list[idx], ecs.MobComponent)
if m == nil {
continue
}
@ -43,7 +45,3 @@ func (mov Moveable) IsBlocked(c types.Coords) bool {
}
return false
}
func (mov Moveable) TypeOf() reflect.Type {
return reflect.TypeOf(mov)
}

View File

@ -65,6 +65,9 @@ func (ts *GameScreen) HandleInput(input string) {
case "Shift+/":
ts.scm.SetScreenByName("help")
break
case "i":
ts.scm.SetScreenByName("inventory")
break
default:
ts.mw.GetLayer("base").ClearArea(0, 3, 40, 1)
ts.mw.GetLayer("base").Print(1, 3, "Key: "+input)
@ -80,12 +83,12 @@ func (ts *GameScreen) Render() {
func (ts *GameScreen) walk(state *gamestate.GameState, dx, dy int) {
controller := state.Controller
coords := controller.GetComponent(state.Player, types.Coords{}.TypeOf()).(types.Coords)
coords := controller.GetComponent(state.Player, ecs.CoordsComponent).(types.Coords)
newCoords := types.Coords{coords.X + dx, coords.Y + dy}
movable := controller.GetComponent(state.Player, movement.Moveable{}.TypeOf()).(movement.Moveable)
movable := controller.GetComponent(state.Player, ecs.MoveableComponent).(movement.Moveable)
if !movable.IsBlocked(newCoords) {
controller.UpdateComponent(state.Player, types.Coords{}.TypeOf(), newCoords)
controller.UpdateComponent(state.Player, ecs.CoordsComponent, newCoords)
}
state.Redraw <- struct{}{}

View File

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

View File

@ -29,7 +29,9 @@ func (ts *TitleScreen) Exit() {
blt.Clear()
}
func (ts *TitleScreen) Render() {
blt.PrintExt(0, 0, ts.mw.W, ts.mw.H, 15, logo)
blt.PrintExt(0, 2, ts.mw.W, ts.mw.H, blt.TK_ALIGN_CENTER, logo)
blt.PrintExt(0, 19, ts.mw.W, ts.mw.H, blt.TK_ALIGN_CENTER, menu)
blt.PrintExt(0, 35, ts.mw.W, ts.mw.H, 3, credits)
}
var logo = `
@ -45,8 +47,9 @@ var logo = `
;k. .Ox Ok..'.,l ;Kk:;dk cKl lX: dKc,;oc kx kk OO dKl;lk: .0O
l; ,o .kK0KKK; :Ok; c; l: .oxc. x: Ol xl .dk: .o
`
var menu = `
Alchemyst (c) 2011-2014 thefish <thefish@zaar.be>
@ -55,9 +58,9 @@ Alchemyst (c) 2011-2014 thefish <thefish@zaar.be>
[color=green]L[/color]oad saved game
Read [color=green]h[/color]elp file
Highest [color=green]S[/color]cores
`
var credits = `
Roguebasin Libtcod Tutorial (c) 2010-2011, Jotaf Henriques
Brogue 1.3 (c) 2010 Brian Walker
Madness (c) 2010 hmp <humpolec@gmail.com>

View File

@ -1,9 +1,11 @@
package types
import (
"bytes"
"fmt"
"github.com/gammazero/deque"
"lab.zaar.be/thefish/alchemyst-go/engine/ecs"
"lab.zaar.be/thefish/alchemyst-go/util"
"reflect"
)
import blt "lab.zaar.be/thefish/bearlibterminal"
@ -29,12 +31,12 @@ type DanceColorHolder struct {
B *cdeque
}
func (chd *DanceColorHolder) GetColor() uint32 {
func (dch DanceColorHolder) GetColor() uint32 {
return blt.ColorFromARGB(
chd.A,
chd.R.Next(),
chd.G.Next(),
chd.B.Next(),
dch.A,
dch.R.Next(),
dch.G.Next(),
dch.B.Next(),
)
}
@ -45,7 +47,7 @@ type PlainColorHolder struct {
B uint8
}
func (chb *PlainColorHolder) GetColor() uint32 {
func (chb PlainColorHolder) GetColor() uint32 {
return blt.ColorFromARGB(
chb.A,
chb.R,
@ -55,10 +57,10 @@ func (chb *PlainColorHolder) GetColor() uint32 {
}
type TileColorSet struct {
Fg ColorHolder
Bg ColorHolder
DarkFg ColorHolder
DarkBg ColorHolder
Fg ColorHolder `json:"fg"`
Bg ColorHolder `json:"bg"`
DarkFg ColorHolder `json:"darkfg"`
DarkBg ColorHolder `json:"darkbg"`
}
@ -70,13 +72,13 @@ type PlainGlyphHolder struct {
Glyph string
}
func (pch *PlainGlyphHolder) GetGlyph() string {
return pch.Glyph
func (pgh PlainGlyphHolder) GetGlyph() string {
return pgh.Glyph
}
type Appearance struct {
Glyph GlyphHolder `json:"char"`
ColorSet *TileColorSet `json:"colorSet"`
Glyph GlyphHolder `json:"glyph"`
ColorSet TileColorSet `json:"colorSet"`
}
func SingleColorRing(colorValue uint8) *cdeque {
@ -106,6 +108,90 @@ func FillColorRing(colorValue uint8, minGlow, maxGlow, step int) *cdeque {
return c
}
func (app Appearance) TypeOf() reflect.Type {
return reflect.TypeOf(app)
func DeviatedColorRing(colorValue uint8, minGlow, maxGlow int) *cdeque {
q := make([]uint8, 0)
color := int(colorValue)
color = crng.Range(minGlow, maxGlow) + color
q = append(q, colorValue)
c := &cdeque{}
c.PushBack(uint8(color))
return c
}
func (app Appearance) Type() string {
return ecs.AppearanceComponent
}
func (app *Appearance) MarshalJSON() ([]byte, error) {
buffer := bytes.NewBufferString("{")
//glyph
buffer.WriteString(`"glyph":{`)
if _, ok := app.Glyph.(*PlainGlyphHolder); ok {
buffer.WriteString(fmt.Sprintf(`"type":"plain", "chars":"%s"`, app.Glyph.GetGlyph()))
}
buffer.WriteString("},")
//color
buffer.WriteString(`"color":{`)
buffer.WriteString(getColorJson("fg", app.ColorSet.Fg) + ",")
buffer.WriteString(getColorJson("bg", app.ColorSet.Bg) + ",")
buffer.WriteString(getColorJson("darkfg", app.ColorSet.DarkFg) + ",")
buffer.WriteString(getColorJson("darkbg", app.ColorSet.DarkBg))
buffer.WriteString("}")
buffer.WriteString("}")
fmt.Printf("\n\nbuffer: %s\n\n", buffer.String())
return buffer.Bytes(), nil
}
func (app *Appearance) UnmarshalJSON(buffer []byte) error {
return nil
}
func getColorJson(field string, holder ColorHolder) string {
result := ""
if _, dch := holder.(*DanceColorHolder); dch {
result = fmt.Sprintf(`{"dance":{`)
result = result + detectRing("a", holder.(*DanceColorHolder).A) + ","
result = result + detectRing("r", holder.(*DanceColorHolder).R) + ","
result = result + detectRing("g", holder.(*DanceColorHolder).G) + ","
result = result + detectRing("b", holder.(*DanceColorHolder).B)
result = result + "}}"
}
if _, pch := holder.(*PlainColorHolder); pch {
result = fmt.Sprintf(`{"plain":[%d,%d,%d,%d]}`,
holder.(*PlainColorHolder).A,
holder.(*PlainColorHolder).R,
holder.(*PlainColorHolder).G,
holder.(*PlainColorHolder).B,
)
}
return fmt.Sprintf(`"%s":%s`, field, result)
}
func detectRing(channel string, something interface{}) string {
result := ""
switch something.(type) {
case (int),(uint8):
result += fmt.Sprintf(`"%s":{"plain":%d}`, channel, something)
case (*cdeque):
fmt.Printf("%v", something)
if something.(*cdeque).Len() == 1 {
//fixme right now we can not distinct plain and deviated
result += fmt.Sprintf(`"%s":{"single":[%v]}`, channel, something.(*cdeque).Front())
} else {
result += fmt.Sprintf(`"%s":{"fill":[%v, %v, %v, %v]}`,
channel,
something.(*cdeque).Front(),
something.(*cdeque).Next(),
something.(*cdeque).Next(),
something.(*cdeque).Next(),
)
}
}
return result
}

View File

@ -1,16 +1,16 @@
package types
import (
"lab.zaar.be/thefish/alchemyst-go/engine/ecs"
"math"
"reflect"
)
type Coords struct {
X, Y int
}
func (сс Coords) TypeOf() reflect.Type {
return reflect.TypeOf(сс)
func (сс Coords) Type() string {
return ecs.CoordsComponent
}
func (c *Coords) Get() (int, int) {

7
go.mod
View File

@ -3,7 +3,14 @@ module lab.zaar.be/thefish/alchemyst-go
go 1.12
require (
github.com/esimov/triangle v1.0.4 // indirect
github.com/fogleman/astar v0.0.0-20160904014929-93992825fbf3 // indirect
github.com/fogleman/gg v1.3.0 // indirect
github.com/gammazero/deque v0.0.0-20190521012701-46e4ffb7a622
github.com/rs/zerolog v1.15.0
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 // indirect
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect
golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101
gonum.org/v1/gonum v0.6.0 // indirect
lab.zaar.be/thefish/bearlibterminal v0.0.0-20191018101635-dd37bbc90d77
)

46
go.sum
View File

@ -1,16 +1,62 @@
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/esimov/triangle v1.0.4 h1:vmEwL5zBPRbN5+GmJE5Dk38IV/v8Zkdp34+yaGr4zrY=
github.com/esimov/triangle v1.0.4/go.mod h1:EKT/GJbKyDWK5IdEgx+wKjcj6zI0Wd27+p2zRgjfdKo=
github.com/fogleman/astar v0.0.0-20160904014929-93992825fbf3 h1:H1UdXUq3kFKSHmoISZ+B3EWukjHvAg8y9VFYO3bXHB8=
github.com/fogleman/astar v0.0.0-20160904014929-93992825fbf3/go.mod h1:oaWu1Maoxg5V3KjA0zGlhwwLAalGLjOFGFblaiHLwMc=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
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/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.15.0 h1:uPRuwkWF4J6fGsJ2R0Gn2jB1EQiav9k3S6CSdygQJXY=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2 h1:y102fOLFqhV41b+4GPiJoa0k/x+pJcEi2/HB1Y5T6fU=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
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-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101 h1:LCmXVkvpQCDj724eX6irUTPCJP5GelFHxqGSWL2D1R0=
golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.6.0 h1:DJy6UzXbahnGUf1ujUNkh/NEtK14qMo2nvlBPs4U5yw=
gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
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=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

17
prefab_test.go Normal file
View File

@ -0,0 +1,17 @@
package alchemyst_go
import (
"lab.zaar.be/thefish/alchemyst-go/engine/gamemap"
"testing"
)
func TestPrefabLoad(t *testing.T) {
testFile, err := gamemap.LoadPrefabFile("./assets/prefabs/test.json")
if err!= nil {
t.Log(err)
t.Fail()
}
t.Log(testFile)
}

View File

@ -13,7 +13,7 @@ func ReadKey() (string, int) {
}
var key = blt.Read()
var pressed = ""
var isModifier, _ = util.InArray(key, modifiers)
var isModifier, _ = util.IntInSlice(key, modifiers)
if !isModifier {
pressed = Scancodemap[key]
@ -38,5 +38,4 @@ func ReadKeyCode() int {
return blt.TK_NONE
}
return blt.Read()
}

View File

@ -42,7 +42,7 @@ func (mw *MainWindow) Open() {
//blt.Set("window: size=80x25, title="+config.Title+" v"+string(version)+"; font: ./fonts/Monaco-Linux.ttf, size=10")
blt.Set(
fmt.Sprintf(
//"window: size=%dx%d, title='%s v%s'; font: ./resources/fonts-bitmap/ibmnew8x12.png, size=8x12;",
//"window: size=%dx%d, title='%s v%s'; font: ./assets/fonts/bitmap/ibmnew8x12.png, size=8x12;",
"window: size=%dx%d, title='%s %s'; font: %s, size=%s;",
//"window: size=%dx%d, title='%s v%s'",
config.MainWindowSizeX,

View File

@ -3,6 +3,7 @@ package mainwindow
import (
"errors"
"fmt"
"lab.zaar.be/thefish/alchemyst-go/engine/ecs"
"lab.zaar.be/thefish/alchemyst-go/engine/fov"
"lab.zaar.be/thefish/alchemyst-go/engine/fov/precomputed_shade"
"lab.zaar.be/thefish/alchemyst-go/engine/gamestate"
@ -94,7 +95,7 @@ func (vp *ViewPort) Listen(state gamestate.GameState) {
func (vp *ViewPort) Render(state *gamestate.GameState) {
playerCoords := state.Controller.GetComponent(state.Player, types.Coords{}.TypeOf()).(types.Coords)
playerCoords := state.Controller.GetComponent(state.Player, ecs.CoordsComponent).(types.Coords)
vp.Move(state, playerCoords)

1
util/delenay.go Normal file
View File

@ -0,0 +1 @@
package util

View File

@ -1,24 +1,32 @@
package util
import (
"reflect"
)
func InArray(val interface{}, array interface{}) (exists bool, index int) {
func IntInSlice(needle int, haystack[]int) (exists bool, index int) {
exists = false
index = -1
switch reflect.TypeOf(array).Kind() {
case reflect.Slice:
s := reflect.ValueOf(array)
for i := 0; i < s.Len(); i++ {
if reflect.DeepEqual(val, s.Index(i).Interface()) == true {
index = i
exists = true
return
}
for i := 0; i < len(haystack); i++ {
if haystack[i] == needle {
return true, i
}
}
return
return exists, index
}
//left here for historical reasons
//func InArray(val interface{}, array interface{}) (exists bool, index int) {
// exists = false
// index = -1
//
// switch reflect.TypeOf(array).Kind() {
// case reflect.Slice:
// s := reflect.ValueOf(array)
//
// for i := 0; i < s.Len(); i++ {
// if reflect.DeepEqual(val, s.Index(i).Interface()) == true {
// index = i
// exists = true
// return
// }
// }
// }
// return
//}