mirror of
https://github.com/mvvasilev/last_light.git
synced 2025-04-19 12:49:52 +03:00
Split logic up a bit
This commit is contained in:
parent
5864ab41ad
commit
b7b269e8b8
19 changed files with 402 additions and 228 deletions
|
@ -31,22 +31,27 @@ func CreateGameContext() *GameContext {
|
|||
}
|
||||
|
||||
func (gc *GameContext) Run() {
|
||||
lastLoop := time.Now()
|
||||
lastTick := time.Now()
|
||||
|
||||
for {
|
||||
deltaTime := 1 + time.Since(lastTick).Microseconds()
|
||||
lastTick = time.Now()
|
||||
deltaTime := 1 + time.Since(lastLoop).Microseconds()
|
||||
lastLoop = time.Now()
|
||||
|
||||
for _, e := range gc.renderContext.CollectInputEvents() {
|
||||
gc.game.Input(e)
|
||||
}
|
||||
|
||||
stop := !gc.game.Tick(deltaTime)
|
||||
if time.Since(lastTick).Milliseconds() >= TICK_RATE {
|
||||
stop := !gc.game.Tick(deltaTime)
|
||||
|
||||
if stop {
|
||||
gc.renderContext.Stop()
|
||||
os.Exit(0)
|
||||
break
|
||||
if stop {
|
||||
gc.renderContext.Stop()
|
||||
os.Exit(0)
|
||||
break
|
||||
}
|
||||
|
||||
lastTick = time.Now()
|
||||
}
|
||||
|
||||
drawables := gc.game.CollectDrawables()
|
||||
|
|
12
game/logic/logic_snippet.go
Normal file
12
game/logic/logic_snippet.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"mvvasilev/last_light/game/model"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
)
|
||||
|
||||
type LogicSnippet[T model.Entity] interface {
|
||||
Input(e *tcell.EventKey)
|
||||
Tick(dt int64, entity T)
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package model
|
||||
|
||||
type Dungeon struct {
|
||||
player *Player
|
||||
|
||||
levels []Map
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package model
|
||||
|
||||
import "mvvasilev/last_light/util"
|
||||
|
||||
type EmptyDungeonLevel struct {
|
||||
level *BasicMap
|
||||
}
|
||||
|
||||
func CreateEmptyDungeonLevel(width, height int) *EmptyDungeonLevel {
|
||||
m := new(EmptyDungeonLevel)
|
||||
|
||||
tiles := make([][]Tile, height)
|
||||
|
||||
for h := range height {
|
||||
tiles[h] = make([]Tile, width)
|
||||
}
|
||||
|
||||
m.level = CreateBasicMap(tiles)
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (edl *EmptyDungeonLevel) Size() util.Size {
|
||||
return edl.level.Size()
|
||||
}
|
||||
|
||||
func (edl *EmptyDungeonLevel) SetTileAt(x int, y int, t Tile) {
|
||||
edl.level.SetTileAt(x, y, t)
|
||||
}
|
||||
|
||||
func (edl *EmptyDungeonLevel) TileAt(x int, y int) Tile {
|
||||
return edl.level.TileAt(x, y)
|
||||
}
|
||||
|
||||
func (edl *EmptyDungeonLevel) Tick() {
|
||||
|
||||
}
|
|
@ -40,5 +40,7 @@ type Entity interface {
|
|||
|
||||
type MovableEntity interface {
|
||||
Position() util.Position
|
||||
Move(dir Direction)
|
||||
MoveTo(newPosition util.Position)
|
||||
|
||||
Entity
|
||||
}
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"mvvasilev/last_light/util"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
)
|
||||
|
||||
type FlatGroundDungeonLevel struct {
|
||||
tiles [][]Tile
|
||||
}
|
||||
|
||||
func CreateFlatGroundDungeonLevel(width, height int) *FlatGroundDungeonLevel {
|
||||
level := new(FlatGroundDungeonLevel)
|
||||
|
||||
level.tiles = make([][]Tile, height)
|
||||
|
||||
for h := range height {
|
||||
level.tiles[h] = make([]Tile, width)
|
||||
|
||||
for w := range width {
|
||||
if w == 0 || h == 0 || w >= width-1 || h >= height-1 {
|
||||
level.tiles[h][w] = CreateStaticTile(w, h, TileTypeRock())
|
||||
continue
|
||||
}
|
||||
|
||||
level.tiles[h][w] = genRandomGroundTile(w, h)
|
||||
}
|
||||
}
|
||||
|
||||
return level
|
||||
}
|
||||
|
||||
func genRandomGroundTile(width, height int) Tile {
|
||||
switch rand.Intn(2) {
|
||||
case 0:
|
||||
return CreateStaticTile(width, height, TileTypeGround())
|
||||
case 1:
|
||||
return CreateStaticTile(width, height, TileTypeGrass())
|
||||
default:
|
||||
return CreateStaticTile(width, height, TileTypeGround())
|
||||
}
|
||||
}
|
||||
|
||||
func (edl *FlatGroundDungeonLevel) Size() util.Size {
|
||||
return util.SizeOf(len(edl.tiles[0]), len(edl.tiles))
|
||||
}
|
||||
|
||||
func (edl *FlatGroundDungeonLevel) SetTileAt(x int, y int, t Tile) {
|
||||
if len(edl.tiles) <= y || len(edl.tiles[0]) <= x {
|
||||
return
|
||||
}
|
||||
|
||||
edl.tiles[y][x] = t
|
||||
}
|
||||
|
||||
func (edl *FlatGroundDungeonLevel) TileAt(x int, y int) Tile {
|
||||
if y < 0 || y >= len(edl.tiles) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if x < 0 || x >= len(edl.tiles[y]) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return edl.tiles[y][x]
|
||||
}
|
||||
|
||||
func (edl *FlatGroundDungeonLevel) Input(e *tcell.EventKey) {
|
||||
|
||||
}
|
||||
|
||||
func (edl *FlatGroundDungeonLevel) Tick() {
|
||||
}
|
|
@ -32,8 +32,8 @@ func (p *Player) Position() util.Position {
|
|||
return p.position
|
||||
}
|
||||
|
||||
func (p *Player) Move(dir Direction) {
|
||||
p.position = p.Position().WithOffset(MovementDirectionOffset(dir))
|
||||
func (p *Player) MoveTo(newPos util.Position) {
|
||||
p.position = newPos
|
||||
}
|
||||
|
||||
func (p *Player) Presentation() (rune, tcell.Style) {
|
||||
|
|
22
game/state/look_state.go
Normal file
22
game/state/look_state.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"mvvasilev/last_light/render"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
)
|
||||
|
||||
type LookState struct {
|
||||
}
|
||||
|
||||
func (ls *LookState) OnInput(e *tcell.EventKey) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (ls *LookState) OnTick(dt int64) GameState {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (ls *LookState) CollectDrawables() []render.Drawable {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
|
@ -3,6 +3,7 @@ package state
|
|||
import (
|
||||
"math/rand"
|
||||
"mvvasilev/last_light/game/model"
|
||||
"mvvasilev/last_light/game/world"
|
||||
"mvvasilev/last_light/render"
|
||||
"mvvasilev/last_light/util"
|
||||
|
||||
|
@ -11,8 +12,9 @@ import (
|
|||
)
|
||||
|
||||
type PlayingState struct {
|
||||
player *model.Player
|
||||
level *model.MultilevelMap
|
||||
player *model.Player
|
||||
entityMap *world.EntityMap
|
||||
level *world.MultilevelMap
|
||||
|
||||
viewport *render.Viewport
|
||||
|
||||
|
@ -27,11 +29,11 @@ func BeginPlayingState() *PlayingState {
|
|||
|
||||
mapSize := util.SizeOf(128, 128)
|
||||
|
||||
dungeonLevel := model.CreateBSPDungeonLevel(mapSize.Width(), mapSize.Height(), 4)
|
||||
dungeonLevel := world.CreateBSPDungeonMap(mapSize.Width(), mapSize.Height(), 4)
|
||||
|
||||
itemTiles := spawnItems(dungeonLevel)
|
||||
|
||||
itemLevel := model.CreateEmptyDungeonLevel(mapSize.Width(), mapSize.Height())
|
||||
itemLevel := world.CreateEmptyDungeonLevel(mapSize.Width(), mapSize.Height())
|
||||
|
||||
for _, it := range itemTiles {
|
||||
itemLevel.SetTileAt(it.Position().X(), it.Position().Y(), it)
|
||||
|
@ -39,13 +41,15 @@ func BeginPlayingState() *PlayingState {
|
|||
|
||||
s.player = model.CreatePlayer(dungeonLevel.PlayerSpawnPoint().XY())
|
||||
|
||||
s.level = model.CreateMultilevelMap(
|
||||
s.entityMap = world.CreateEntityMap(mapSize.WH())
|
||||
|
||||
s.level = world.CreateMultilevelMap(
|
||||
dungeonLevel,
|
||||
itemLevel,
|
||||
model.CreateEmptyDungeonLevel(mapSize.WH()),
|
||||
s.entityMap,
|
||||
)
|
||||
|
||||
s.level.SetTileAtHeight(dungeonLevel.PlayerSpawnPoint().X(), dungeonLevel.PlayerSpawnPoint().Y(), 2, s.player)
|
||||
s.entityMap.AddEntity(s.player, '@', tcell.StyleDefault)
|
||||
|
||||
s.viewport = render.CreateViewport(
|
||||
util.PositionAt(0, 0),
|
||||
|
@ -57,7 +61,7 @@ func BeginPlayingState() *PlayingState {
|
|||
return s
|
||||
}
|
||||
|
||||
func spawnItems(level *model.BSPDungeonLevel) []model.Tile {
|
||||
func spawnItems(level *world.BSPDungeonMap) []world.Tile {
|
||||
rooms := level.Rooms()
|
||||
|
||||
genTable := make(map[float32]*model.ItemType)
|
||||
|
@ -67,7 +71,7 @@ func spawnItems(level *model.BSPDungeonLevel) []model.Tile {
|
|||
genTable[0.051] = model.ItemTypeLongsword()
|
||||
genTable[0.052] = model.ItemTypeKey()
|
||||
|
||||
itemTiles := make([]model.Tile, 0, 10)
|
||||
itemTiles := make([]world.Tile, 0, 10)
|
||||
|
||||
for _, r := range rooms {
|
||||
maxItems := int(0.10 * float64(r.Size().Area()))
|
||||
|
@ -90,7 +94,7 @@ func spawnItems(level *model.BSPDungeonLevel) []model.Tile {
|
|||
util.RandInt(r.Position().Y()+1, r.Position().Y()+r.Size().Height()-1),
|
||||
)
|
||||
|
||||
itemTiles = append(itemTiles, model.CreateItemTile(
|
||||
itemTiles = append(itemTiles, world.CreateItemTile(
|
||||
pos, itemType, 1,
|
||||
))
|
||||
}
|
||||
|
@ -117,14 +121,12 @@ func (ps *PlayingState) MovePlayer() {
|
|||
}
|
||||
|
||||
newPlayerPos := ps.player.Position().WithOffset(model.MovementDirectionOffset(ps.movePlayerDirection))
|
||||
|
||||
tileAtMovePos := ps.level.TileAt(newPlayerPos.XY())
|
||||
|
||||
if tileAtMovePos.Passable() {
|
||||
ps.level.SetTileAtHeight(ps.player.Position().X(), ps.player.Position().Y(), 2, nil)
|
||||
ps.player.Move(ps.movePlayerDirection)
|
||||
dx, dy := model.MovementDirectionOffset(ps.movePlayerDirection)
|
||||
ps.entityMap.MoveEntity(ps.player.UniqueId(), dx, dy)
|
||||
ps.viewport.SetCenter(ps.player.Position())
|
||||
ps.level.SetTileAtHeight(ps.player.Position().X(), ps.player.Position().Y(), 2, ps.player)
|
||||
}
|
||||
|
||||
ps.movePlayerDirection = model.DirectionNone
|
||||
|
@ -134,7 +136,7 @@ func (ps *PlayingState) PickUpItemUnderPlayer() {
|
|||
pos := ps.player.Position()
|
||||
tile := ps.level.TileAtHeight(pos.X(), pos.Y(), 1)
|
||||
|
||||
itemTile, ok := tile.(*model.ItemTile)
|
||||
itemTile, ok := tile.(*world.ItemTile)
|
||||
|
||||
if !ok {
|
||||
return
|
||||
|
@ -178,17 +180,6 @@ func (ps *PlayingState) OnInput(e *tcell.EventKey) {
|
|||
ps.movePlayerDirection = model.DirectionLeft
|
||||
case tcell.KeyRight:
|
||||
ps.movePlayerDirection = model.DirectionRight
|
||||
case tcell.KeyRune:
|
||||
switch e.Rune() {
|
||||
case 'w':
|
||||
ps.movePlayerDirection = model.DirectionUp
|
||||
case 'a':
|
||||
ps.movePlayerDirection = model.DirectionLeft
|
||||
case 's':
|
||||
ps.movePlayerDirection = model.DirectionDown
|
||||
case 'd':
|
||||
ps.movePlayerDirection = model.DirectionRight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
35
game/world/bsp_map.go
Normal file
35
game/world/bsp_map.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package world
|
||||
|
||||
import (
|
||||
"mvvasilev/last_light/util"
|
||||
)
|
||||
|
||||
type BSPDungeonMap struct {
|
||||
level *BasicMap
|
||||
|
||||
playerSpawnPoint util.Position
|
||||
rooms []util.Room
|
||||
}
|
||||
|
||||
func (bsp *BSPDungeonMap) PlayerSpawnPoint() util.Position {
|
||||
return bsp.playerSpawnPoint
|
||||
}
|
||||
|
||||
func (bsp *BSPDungeonMap) Size() util.Size {
|
||||
return bsp.level.Size()
|
||||
}
|
||||
|
||||
func (bsp *BSPDungeonMap) SetTileAt(x int, y int, t Tile) {
|
||||
bsp.level.SetTileAt(x, y, t)
|
||||
}
|
||||
|
||||
func (bsp *BSPDungeonMap) TileAt(x int, y int) Tile {
|
||||
return bsp.level.TileAt(x, y)
|
||||
}
|
||||
|
||||
func (bsp *BSPDungeonMap) Tick(dt int64) {
|
||||
}
|
||||
|
||||
func (bsp *BSPDungeonMap) Rooms() []util.Room {
|
||||
return bsp.rooms
|
||||
}
|
15
game/world/dungeon.go
Normal file
15
game/world/dungeon.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package world
|
||||
|
||||
import "mvvasilev/last_light/game/model"
|
||||
|
||||
type dungeonLevel struct {
|
||||
groundLevel Map
|
||||
entityLevel *EntityMap
|
||||
itemLevel *Map
|
||||
}
|
||||
|
||||
type Dungeon struct {
|
||||
player *model.Player
|
||||
|
||||
levels []*dungeonLevel
|
||||
}
|
23
game/world/empty_map.go
Normal file
23
game/world/empty_map.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package world
|
||||
|
||||
import "mvvasilev/last_light/util"
|
||||
|
||||
type EmptyDungeonMap struct {
|
||||
level *BasicMap
|
||||
}
|
||||
|
||||
func (edl *EmptyDungeonMap) Size() util.Size {
|
||||
return edl.level.Size()
|
||||
}
|
||||
|
||||
func (edl *EmptyDungeonMap) SetTileAt(x int, y int, t Tile) {
|
||||
edl.level.SetTileAt(x, y, t)
|
||||
}
|
||||
|
||||
func (edl *EmptyDungeonMap) TileAt(x int, y int) Tile {
|
||||
return edl.level.TileAt(x, y)
|
||||
}
|
||||
|
||||
func (edl *EmptyDungeonMap) Tick(dt int64) {
|
||||
|
||||
}
|
101
game/world/entity_map.go
Normal file
101
game/world/entity_map.go
Normal file
|
@ -0,0 +1,101 @@
|
|||
package world
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"mvvasilev/last_light/game/model"
|
||||
"mvvasilev/last_light/util"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type EntityMap struct {
|
||||
entities map[int]EntityTile
|
||||
|
||||
util.Sized
|
||||
}
|
||||
|
||||
func CreateEntityMap(width, height int) *EntityMap {
|
||||
return &EntityMap{
|
||||
entities: make(map[int]EntityTile, 0),
|
||||
Sized: util.WithSize(util.SizeOf(width, height)),
|
||||
}
|
||||
}
|
||||
|
||||
func (em *EntityMap) SetTileAt(x int, y int, t Tile) {
|
||||
// if !em.FitsWithin(x, y) {
|
||||
// return
|
||||
// }
|
||||
|
||||
// index := em.Size().AsArrayIndex(x, y)
|
||||
|
||||
// TODO? May not be necessary
|
||||
}
|
||||
|
||||
func (em *EntityMap) FindEntityByUuid(uuid uuid.UUID) (key int, entity EntityTile) {
|
||||
for i, e := range em.entities {
|
||||
if e.Entity().UniqueId() == uuid {
|
||||
return i, e
|
||||
}
|
||||
}
|
||||
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (em *EntityMap) AddEntity(entity model.MovableEntity, presentation rune, style tcell.Style) {
|
||||
if !em.FitsWithin(entity.Position().XY()) {
|
||||
return
|
||||
}
|
||||
|
||||
key := em.Size().AsArrayIndex(entity.Position().XY())
|
||||
et := CreateBasicEntityTile(entity, presentation, style)
|
||||
|
||||
em.entities[key] = et
|
||||
}
|
||||
|
||||
func (em *EntityMap) DropEntity(uuid uuid.UUID) {
|
||||
maps.DeleteFunc(em.entities, func(i int, et EntityTile) bool {
|
||||
if et.Entity().UniqueId() == uuid {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func (em *EntityMap) MoveEntity(uuid uuid.UUID, dx, dy int) {
|
||||
oldKey, e := em.FindEntityByUuid(uuid)
|
||||
|
||||
if e == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !em.FitsWithin(e.Entity().Position().WithOffset(dx, dy).XY()) {
|
||||
return
|
||||
}
|
||||
|
||||
delete(em.entities, oldKey)
|
||||
|
||||
newPos := e.Entity().Position().WithOffset(dx, dy)
|
||||
e.Entity().MoveTo(newPos)
|
||||
|
||||
newKey := em.Size().AsArrayIndex(e.Entity().Position().XY())
|
||||
|
||||
em.entities[newKey] = e
|
||||
}
|
||||
|
||||
func (em *EntityMap) TileAt(x int, y int) Tile {
|
||||
if !em.FitsWithin(x, y) {
|
||||
return CreateStaticTile(x, y, TileTypeVoid())
|
||||
}
|
||||
|
||||
key := em.Size().AsArrayIndex(x, y)
|
||||
|
||||
return em.entities[key]
|
||||
}
|
||||
|
||||
func (em *EntityMap) Tick(dt int64) {
|
||||
for _, e := range em.entities {
|
||||
e.Entity().Tick(dt)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package model
|
||||
package world
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
@ -16,7 +16,7 @@ type bspNode struct {
|
|||
origin util.Position
|
||||
size util.Size
|
||||
|
||||
room Room
|
||||
room util.Room
|
||||
hasRoom bool
|
||||
|
||||
left *bspNode
|
||||
|
@ -25,27 +25,7 @@ type bspNode struct {
|
|||
splitDir splitDirection
|
||||
}
|
||||
|
||||
type Room struct {
|
||||
position util.Position
|
||||
size util.Size
|
||||
}
|
||||
|
||||
func (r Room) Size() util.Size {
|
||||
return r.size
|
||||
}
|
||||
|
||||
func (r Room) Position() util.Position {
|
||||
return r.position
|
||||
}
|
||||
|
||||
type BSPDungeonLevel struct {
|
||||
level *BasicMap
|
||||
|
||||
playerSpawnPoint util.Position
|
||||
rooms []Room
|
||||
}
|
||||
|
||||
func CreateBSPDungeonLevel(width, height int, numSplits int) *BSPDungeonLevel {
|
||||
func CreateBSPDungeonMap(width, height int, numSplits int) *BSPDungeonMap {
|
||||
root := new(bspNode)
|
||||
|
||||
root.origin = util.PositionAt(0, 0)
|
||||
|
@ -59,7 +39,7 @@ func CreateBSPDungeonLevel(width, height int, numSplits int) *BSPDungeonLevel {
|
|||
tiles[h] = make([]Tile, width)
|
||||
}
|
||||
|
||||
rooms := make([]Room, 0, 2^numSplits)
|
||||
rooms := make([]util.Room, 0, 2^numSplits)
|
||||
|
||||
iterateBspLeaves(root, func(leaf *bspNode) {
|
||||
x := util.RandInt(leaf.origin.X(), leaf.origin.X()+leaf.size.Width()/4)
|
||||
|
@ -75,9 +55,9 @@ func CreateBSPDungeonLevel(width, height int, numSplits int) *BSPDungeonLevel {
|
|||
h = h - (y + h - height) - 1
|
||||
}
|
||||
|
||||
room := Room{
|
||||
position: util.PositionAt(x, y),
|
||||
size: util.SizeOf(w, h),
|
||||
room := util.Room{
|
||||
Positioned: util.WithPosition(util.PositionAt(x, y)),
|
||||
Sized: util.WithSize(util.SizeOf(w, h)),
|
||||
}
|
||||
|
||||
rooms = append(rooms, room)
|
||||
|
@ -95,36 +75,32 @@ func CreateBSPDungeonLevel(width, height int, numSplits int) *BSPDungeonLevel {
|
|||
zCorridor(
|
||||
tiles,
|
||||
util.PositionAt(
|
||||
roomLeft.position.X()+roomLeft.size.Width()/2,
|
||||
roomLeft.position.Y()+roomLeft.size.Height()/2,
|
||||
roomLeft.Position().X()+roomLeft.Size().Width()/2,
|
||||
roomLeft.Position().Y()+roomLeft.Size().Height()/2,
|
||||
),
|
||||
util.PositionAt(
|
||||
roomRight.position.X()+roomRight.size.Width()/2,
|
||||
roomRight.position.Y()+roomRight.size.Height()/2,
|
||||
roomRight.Position().X()+roomRight.Size().Width()/2,
|
||||
roomRight.Position().Y()+roomRight.Size().Height()/2,
|
||||
),
|
||||
parent.splitDir,
|
||||
)
|
||||
})
|
||||
|
||||
bsp := new(BSPDungeonLevel)
|
||||
bsp := new(BSPDungeonMap)
|
||||
|
||||
spawnRoom := findRoom(root.left)
|
||||
|
||||
bsp.rooms = rooms
|
||||
bsp.level = CreateBasicMap(tiles)
|
||||
bsp.playerSpawnPoint = util.PositionAt(
|
||||
spawnRoom.position.X()+spawnRoom.size.Width()/2,
|
||||
spawnRoom.position.Y()+spawnRoom.size.Height()/2,
|
||||
spawnRoom.Position().X()+spawnRoom.Size().Width()/2,
|
||||
spawnRoom.Position().Y()+spawnRoom.Size().Height()/2,
|
||||
)
|
||||
|
||||
return bsp
|
||||
}
|
||||
|
||||
func (bsp *BSPDungeonLevel) PlayerSpawnPoint() util.Position {
|
||||
return bsp.playerSpawnPoint
|
||||
}
|
||||
|
||||
func findRoom(parent *bspNode) Room {
|
||||
func findRoom(parent *bspNode) util.Room {
|
||||
if parent.hasRoom {
|
||||
return parent.room
|
||||
}
|
||||
|
@ -280,11 +256,11 @@ func placeWallAtIfNotPassable(tiles [][]Tile, x, y int) {
|
|||
tiles[y][x] = CreateStaticTile(x, y, TileTypeWall())
|
||||
}
|
||||
|
||||
func makeRoom(tiles [][]Tile, room Room) {
|
||||
width := room.size.Width()
|
||||
height := room.size.Height()
|
||||
x := room.position.X()
|
||||
y := room.position.Y()
|
||||
func makeRoom(tiles [][]Tile, room util.Room) {
|
||||
width := room.Size().Width()
|
||||
height := room.Size().Height()
|
||||
x := room.Position().X()
|
||||
y := room.Position().Y()
|
||||
|
||||
for w := x; w < x+width+1; w++ {
|
||||
tiles[y][w] = CreateStaticTile(w, y, TileTypeWall())
|
||||
|
@ -302,22 +278,3 @@ func makeRoom(tiles [][]Tile, room Room) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (bsp *BSPDungeonLevel) Size() util.Size {
|
||||
return bsp.level.Size()
|
||||
}
|
||||
|
||||
func (bsp *BSPDungeonLevel) SetTileAt(x int, y int, t Tile) {
|
||||
bsp.level.SetTileAt(x, y, t)
|
||||
}
|
||||
|
||||
func (bsp *BSPDungeonLevel) TileAt(x int, y int) Tile {
|
||||
return bsp.level.TileAt(x, y)
|
||||
}
|
||||
|
||||
func (bsp *BSPDungeonLevel) Tick() {
|
||||
}
|
||||
|
||||
func (bsp *BSPDungeonLevel) Rooms() []Room {
|
||||
return bsp.rooms
|
||||
}
|
15
game/world/generate_empty_map.go
Normal file
15
game/world/generate_empty_map.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package world
|
||||
|
||||
func CreateEmptyDungeonLevel(width, height int) *EmptyDungeonMap {
|
||||
m := new(EmptyDungeonMap)
|
||||
|
||||
tiles := make([][]Tile, height)
|
||||
|
||||
for h := range height {
|
||||
tiles[h] = make([]Tile, width)
|
||||
}
|
||||
|
||||
m.level = CreateBasicMap(tiles)
|
||||
|
||||
return m
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package model
|
||||
package world
|
||||
|
||||
import (
|
||||
"mvvasilev/last_light/util"
|
||||
|
@ -8,7 +8,17 @@ type Map interface {
|
|||
Size() util.Size
|
||||
SetTileAt(x, y int, t Tile)
|
||||
TileAt(x, y int) Tile
|
||||
Tick()
|
||||
Tick(dt int64)
|
||||
}
|
||||
|
||||
type WithPlayerSpawnPoint interface {
|
||||
PlayerSpawnPoint() util.Position
|
||||
Map
|
||||
}
|
||||
|
||||
type WithRooms interface {
|
||||
Rooms() []util.Room
|
||||
Map
|
||||
}
|
||||
|
||||
type BasicMap struct {
|
|
@ -1,4 +1,4 @@
|
|||
package model
|
||||
package world
|
||||
|
||||
import "mvvasilev/last_light/util"
|
||||
|
||||
|
@ -102,8 +102,8 @@ func (mm *MultilevelMap) TileAtHeight(x, y, height int) Tile {
|
|||
return mm.layers[height].TileAt(x, y)
|
||||
}
|
||||
|
||||
func (mm *MultilevelMap) Tick() {
|
||||
func (mm *MultilevelMap) Tick(dt int64) {
|
||||
for _, l := range mm.layers {
|
||||
l.Tick()
|
||||
l.Tick(dt)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package model
|
||||
package world
|
||||
|
||||
import (
|
||||
"mvvasilev/last_light/game/model"
|
||||
"mvvasilev/last_light/util"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
|
@ -14,6 +15,8 @@ const (
|
|||
MaterialWall
|
||||
MaterialGrass
|
||||
MaterialVoid
|
||||
MaterialClosedDoor
|
||||
MaterialOpenDoor
|
||||
)
|
||||
|
||||
type TileType struct {
|
||||
|
@ -74,6 +77,26 @@ func TileTypeWall() TileType {
|
|||
}
|
||||
}
|
||||
|
||||
func TileTypeClosedDoor() TileType {
|
||||
return TileType{
|
||||
Material: MaterialClosedDoor,
|
||||
Passable: false,
|
||||
Transparent: false,
|
||||
Presentation: '[',
|
||||
Style: tcell.StyleDefault.Foreground(tcell.ColorLightSteelBlue).Background(tcell.ColorSaddleBrown),
|
||||
}
|
||||
}
|
||||
|
||||
func TileTypeOpenDoor() TileType {
|
||||
return TileType{
|
||||
Material: MaterialClosedDoor,
|
||||
Passable: false,
|
||||
Transparent: false,
|
||||
Presentation: '_',
|
||||
Style: tcell.StyleDefault.Foreground(tcell.ColorLightSteelBlue),
|
||||
}
|
||||
}
|
||||
|
||||
type Tile interface {
|
||||
Position() util.Position
|
||||
Presentation() (rune, tcell.Style)
|
||||
|
@ -117,11 +140,11 @@ func (st *StaticTile) Type() TileType {
|
|||
|
||||
type ItemTile struct {
|
||||
position util.Position
|
||||
itemType *ItemType
|
||||
itemType *model.ItemType
|
||||
quantity int
|
||||
}
|
||||
|
||||
func CreateItemTile(position util.Position, itemType *ItemType, quantity int) *ItemTile {
|
||||
func CreateItemTile(position util.Position, itemType *model.ItemType, quantity int) *ItemTile {
|
||||
it := new(ItemTile)
|
||||
|
||||
it.position = position
|
||||
|
@ -131,7 +154,7 @@ func CreateItemTile(position util.Position, itemType *ItemType, quantity int) *I
|
|||
return it
|
||||
}
|
||||
|
||||
func (it *ItemTile) Type() *ItemType {
|
||||
func (it *ItemTile) Type() *model.ItemType {
|
||||
return it.itemType
|
||||
}
|
||||
|
||||
|
@ -144,7 +167,7 @@ func (it *ItemTile) Position() util.Position {
|
|||
}
|
||||
|
||||
func (it *ItemTile) Presentation() (rune, tcell.Style) {
|
||||
return it.itemType.tileIcon, it.itemType.style
|
||||
return it.itemType.TileIcon(), it.itemType.Style()
|
||||
}
|
||||
|
||||
func (it *ItemTile) Passable() bool {
|
||||
|
@ -154,3 +177,43 @@ func (it *ItemTile) Passable() bool {
|
|||
func (it *ItemTile) Transparent() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type EntityTile interface {
|
||||
Entity() model.MovableEntity
|
||||
Tile
|
||||
}
|
||||
|
||||
type BasicEntityTile struct {
|
||||
entity model.MovableEntity
|
||||
|
||||
presentation rune
|
||||
style tcell.Style
|
||||
}
|
||||
|
||||
func CreateBasicEntityTile(entity model.MovableEntity, presentation rune, style tcell.Style) *BasicEntityTile {
|
||||
return &BasicEntityTile{
|
||||
entity: entity,
|
||||
presentation: presentation,
|
||||
style: style,
|
||||
}
|
||||
}
|
||||
|
||||
func (bet *BasicEntityTile) Entity() model.MovableEntity {
|
||||
return bet.entity
|
||||
}
|
||||
|
||||
func (bet *BasicEntityTile) Position() util.Position {
|
||||
return bet.entity.Position()
|
||||
}
|
||||
|
||||
func (bet *BasicEntityTile) Presentation() (rune, tcell.Style) {
|
||||
return bet.presentation, bet.style
|
||||
}
|
||||
|
||||
func (bet *BasicEntityTile) Passable() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (bet *BasicEntityTile) Transparent() bool {
|
||||
return false
|
||||
}
|
42
util/util.go
42
util/util.go
|
@ -2,6 +2,20 @@ package util
|
|||
|
||||
import "math/rand"
|
||||
|
||||
type Positioned struct {
|
||||
pos Position
|
||||
}
|
||||
|
||||
func WithPosition(pos Position) Positioned {
|
||||
return Positioned{
|
||||
pos: pos,
|
||||
}
|
||||
}
|
||||
|
||||
func (wp *Positioned) Position() Position {
|
||||
return wp.pos
|
||||
}
|
||||
|
||||
type Position struct {
|
||||
x int
|
||||
y int
|
||||
|
@ -29,6 +43,25 @@ func (p Position) WithOffset(xOffset int, yOffset int) Position {
|
|||
return p
|
||||
}
|
||||
|
||||
type Sized struct {
|
||||
size Size
|
||||
}
|
||||
|
||||
func WithSize(size Size) Sized {
|
||||
return Sized{
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if the provided coordinates fit within the sized struct, [0, N)
|
||||
func (ws *Sized) FitsWithin(x, y int) bool {
|
||||
return 0 <= x && x < ws.size.width && 0 <= y && y < ws.size.height
|
||||
}
|
||||
|
||||
func (ws *Sized) Size() Size {
|
||||
return ws.size
|
||||
}
|
||||
|
||||
type Size struct {
|
||||
width int
|
||||
height int
|
||||
|
@ -54,6 +87,10 @@ func (s Size) Area() int {
|
|||
return s.width * s.height
|
||||
}
|
||||
|
||||
func (s Size) AsArrayIndex(x, y int) int {
|
||||
return y*s.width + x
|
||||
}
|
||||
|
||||
func LimitIncrement(i int, limit int) int {
|
||||
if (i + 1) > limit {
|
||||
return i
|
||||
|
@ -73,3 +110,8 @@ func LimitDecrement(i int, limit int) int {
|
|||
func RandInt(min, max int) int {
|
||||
return min + rand.Intn(max-min)
|
||||
}
|
||||
|
||||
type Room struct {
|
||||
Positioned
|
||||
Sized
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue