last_light/game/state/playing_state.go
2024-05-03 13:46:32 +03:00

231 lines
4.9 KiB
Go

package state
import (
"math/rand"
"mvvasilev/last_light/game/model"
"mvvasilev/last_light/render"
"mvvasilev/last_light/util"
"github.com/gdamore/tcell/v2"
"github.com/gdamore/tcell/v2/views"
)
type PlayingState struct {
player *model.Player
level *model.MultilevelMap
viewport *render.Viewport
movePlayerDirection model.Direction
pauseGame bool
openInventory bool
pickUpUnderPlayer bool
}
func BeginPlayingState() *PlayingState {
s := new(PlayingState)
mapSize := util.SizeOf(128, 128)
dungeonLevel := model.CreateBSPDungeonLevel(mapSize.Width(), mapSize.Height(), 4)
itemTiles := spawnItems(dungeonLevel)
itemLevel := model.CreateEmptyDungeonLevel(mapSize.Width(), mapSize.Height())
for _, it := range itemTiles {
itemLevel.SetTileAt(it.Position().X(), it.Position().Y(), it)
}
s.player = model.CreatePlayer(dungeonLevel.PlayerSpawnPoint().XY())
s.level = model.CreateMultilevelMap(
dungeonLevel,
itemLevel,
model.CreateEmptyDungeonLevel(mapSize.WH()),
)
s.level.SetTileAtHeight(dungeonLevel.PlayerSpawnPoint().X(), dungeonLevel.PlayerSpawnPoint().Y(), 2, s.player)
s.viewport = render.CreateViewport(
util.PositionAt(0, 0),
dungeonLevel.PlayerSpawnPoint(),
util.SizeOf(80, 24),
tcell.StyleDefault,
)
return s
}
func spawnItems(level *model.BSPDungeonLevel) []model.Tile {
rooms := level.Rooms()
genTable := make(map[float32]*model.ItemType)
genTable[0.2] = model.ItemTypeFish()
genTable[0.05] = model.ItemTypeBow()
genTable[0.051] = model.ItemTypeLongsword()
genTable[0.052] = model.ItemTypeKey()
itemTiles := make([]model.Tile, 0, 10)
for _, r := range rooms {
maxItems := int(0.10 * float64(r.Size().Area()))
if maxItems < 1 {
continue
}
numItems := rand.Intn(maxItems)
for range numItems {
itemType := model.GenerateItemType(genTable)
if itemType == nil {
continue
}
pos := util.PositionAt(
util.RandInt(r.Position().X()+1, r.Position().X()+r.Size().Width()-1),
util.RandInt(r.Position().Y()+1, r.Position().Y()+r.Size().Height()-1),
)
itemTiles = append(itemTiles, model.CreateItemTile(
pos, itemType, 1,
))
}
}
return itemTiles
}
func (ps *PlayingState) Pause() {
ps.pauseGame = true
}
func (ps *PlayingState) Unpause() {
ps.pauseGame = false
}
func (ps *PlayingState) SetPaused(paused bool) {
ps.pauseGame = paused
}
func (ps *PlayingState) MovePlayer() {
if ps.movePlayerDirection == model.DirectionNone {
return
}
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)
ps.viewport.SetCenter(ps.player.Position())
ps.level.SetTileAtHeight(ps.player.Position().X(), ps.player.Position().Y(), 2, ps.player)
}
ps.movePlayerDirection = model.DirectionNone
}
func (ps *PlayingState) PickUpItemUnderPlayer() {
pos := ps.player.Position()
tile := ps.level.TileAtHeight(pos.X(), pos.Y(), 1)
itemTile, ok := tile.(*model.ItemTile)
if !ok {
return
}
item := model.CreateItem(itemTile.Type(), itemTile.Quantity())
success := ps.player.Inventory().Push(item)
if !success {
return
}
ps.level.SetTileAtHeight(pos.X(), pos.Y(), 1, nil)
}
func (ps *PlayingState) OnInput(e *tcell.EventKey) {
ps.player.Input(e)
if e.Key() == tcell.KeyEsc {
ps.pauseGame = true
return
}
if e.Key() == tcell.KeyRune && e.Rune() == 'i' {
ps.openInventory = true
return
}
if e.Key() == tcell.KeyRune && e.Rune() == 'p' {
ps.pickUpUnderPlayer = true
return
}
switch e.Key() {
case tcell.KeyUp:
ps.movePlayerDirection = model.DirectionUp
case tcell.KeyDown:
ps.movePlayerDirection = model.DirectionDown
case tcell.KeyLeft:
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
}
}
}
func (ps *PlayingState) OnTick(dt int64) GameState {
ps.player.Tick(dt)
if ps.pauseGame {
return PauseGame(ps)
}
if ps.openInventory {
ps.openInventory = false
return CreateInventoryScreenState(ps.player, ps)
}
if ps.movePlayerDirection != model.DirectionNone {
ps.MovePlayer()
}
if ps.pickUpUnderPlayer {
ps.pickUpUnderPlayer = false
ps.PickUpItemUnderPlayer()
}
return ps
}
func (ps *PlayingState) CollectDrawables() []render.Drawable {
return render.Multidraw(render.CreateDrawingInstructions(func(v views.View) {
ps.viewport.DrawFromProvider(v, func(x, y int) (rune, tcell.Style) {
tile := ps.level.TileAt(x, y)
if tile != nil {
return tile.Presentation()
}
return ' ', tcell.StyleDefault
})
}))
}