last_light/game/state/look_state.go

227 lines
5.4 KiB
Go
Raw Permalink Normal View History

2024-05-06 18:59:14 +03:00
package state
import (
"fmt"
2024-05-06 20:43:35 +03:00
"mvvasilev/last_light/engine"
"mvvasilev/last_light/game/model"
"mvvasilev/last_light/game/systems"
2024-06-10 23:20:38 +03:00
"mvvasilev/last_light/game/ui"
"time"
2024-05-06 18:59:14 +03:00
"github.com/gdamore/tcell/v2"
"github.com/gdamore/tcell/v2/views"
2024-05-06 18:59:14 +03:00
)
const CursorRune = '+'
const CursorBlinkTime = 200 // Blink cursor every 200ms, showing what's under it
2024-05-06 18:59:14 +03:00
type LookState struct {
prevState GameState
inputSystem *systems.InputSystem
turnSystem *systems.TurnSystem
eventLog *engine.GameEventLog
player *model.Player
dungeon *model.Dungeon
2024-06-10 23:20:38 +03:00
showLog bool
uiEventLog *ui.UIEventLog
showCursor bool
cursorPos engine.Position
lastCursorBlinkTime time.Time
2024-06-10 23:20:38 +03:00
nextGameState GameState
2024-05-06 18:59:14 +03:00
}
func CreateLookState(prevState GameState, eventLog *engine.GameEventLog, dungeon *model.Dungeon, inputSystem *systems.InputSystem, turnSystem *systems.TurnSystem, player *model.Player) *LookState {
return &LookState{
prevState: prevState,
inputSystem: inputSystem,
turnSystem: turnSystem,
dungeon: dungeon,
player: player,
eventLog: eventLog,
cursorPos: engine.PositionAt(0, 0),
lastCursorBlinkTime: time.Now(),
2024-06-10 23:20:38 +03:00
showLog: true,
uiEventLog: ui.CreateUIEventLog(0, 17, 80, 7, eventLog, tcell.StyleDefault),
}
}
func (ls *LookState) InputContext() systems.InputContext {
return systems.InputContext_Look
2024-05-06 18:59:14 +03:00
}
func (ls *LookState) OnTick(dt int64) GameState {
2024-06-10 23:20:38 +03:00
ls.nextGameState = ls
switch ls.inputSystem.NextAction() {
case systems.InputAction_Move_North:
ls.cursorPos = ls.cursorPos.WithOffset(model.MovementDirectionOffset(model.North))
case systems.InputAction_Move_South:
ls.cursorPos = ls.cursorPos.WithOffset(model.MovementDirectionOffset(model.South))
case systems.InputAction_Move_East:
ls.cursorPos = ls.cursorPos.WithOffset(model.MovementDirectionOffset(model.East))
case systems.InputAction_Move_West:
ls.cursorPos = ls.cursorPos.WithOffset(model.MovementDirectionOffset(model.West))
2024-06-10 23:20:38 +03:00
case systems.InputAction_OpenLogs:
ls.showLog = !ls.showLog
case systems.InputAction_Describe:
ls.Describe()
case systems.InputAction_Shoot:
ls.ShootEquippedWeapon()
case systems.InputAction_Menu_Exit:
2024-06-10 23:20:38 +03:00
ls.nextGameState = ls.prevState
}
2024-06-10 23:20:38 +03:00
return ls.nextGameState
}
func (ls *LookState) ShootEquippedWeapon() {
2024-06-10 23:20:38 +03:00
dX, dY := ls.lookCursorCoordsToDungeonCoords()
2024-06-11 11:04:22 +03:00
cursorPos := engine.PositionAt(dX, dY)
2024-06-10 23:20:38 +03:00
success := model.ShootProjectile(
ls.player,
cursorPos,
ls.eventLog,
ls.dungeon,
ls.turnSystem,
2024-06-10 23:20:38 +03:00
)
if !success {
2024-06-10 23:20:38 +03:00
return
}
ls.player.SkipNextTurn(true)
2024-06-10 23:20:38 +03:00
ls.nextGameState = ls.prevState
}
func (ls *LookState) Describe() {
dX, dY := ls.lookCursorCoordsToDungeonCoords()
2024-06-10 23:20:38 +03:00
distance := engine.PositionAt(dX, dY).Distance(ls.player.Position())
if distance >= 12 {
ls.eventLog.Log("Can't see in the dark that far")
return
}
isVisibleFromPlayer, lastTile := model.HasLineOfSight(ls.dungeon, ls.player.Position(), engine.PositionAt(dX, dY))
if !isVisibleFromPlayer {
materialName, _ := materialToDescription(lastTile.Material())
ls.eventLog.Log(fmt.Sprintf("%s obscures your view", materialName))
return
}
tile := ls.dungeon.CurrentLevel().TileAt(dX, dY)
2024-06-10 23:20:38 +03:00
entity := tile.Entity()
2024-06-10 23:20:38 +03:00
if entity != nil {
ls.DescribeEntity(entity.Entity)
return
}
item := tile.Item()
if item != nil {
ls.DescribeItem(item.Item)
return
}
materialName, materialDesc := materialToDescription(tile.Material())
ls.eventLog.Log(fmt.Sprintf("%s: %s", materialName, materialDesc))
}
2024-06-10 23:20:38 +03:00
func (ls *LookState) DescribeEntity(entity model.Entity) {
if entity == nil {
return
}
2024-06-10 23:20:38 +03:00
if entity == ls.player {
ls.eventLog.Log("You")
2024-06-10 23:20:38 +03:00
return
}
2024-06-10 23:20:38 +03:00
if entity.Named() == nil {
return
}
2024-06-10 23:20:38 +03:00
if entity.Described() != nil {
ls.eventLog.Log(fmt.Sprintf("%s: %s", entity.Named().Name, entity.Described().Description))
} else {
ls.eventLog.Log(entity.Named().Name)
}
}
func (ls *LookState) DescribeItem(item model.Item) {
if item == nil {
return
}
if item.Named() == nil {
return
}
if item.Described() != nil {
ls.eventLog.Log(fmt.Sprintf("%s: %s", item.Named().Name, item.Described().Description))
} else {
ls.eventLog.Log(item.Named().Name)
}
}
func materialToDescription(material model.Material) (name, description string) {
switch material {
case model.MaterialVoid:
return "Void", "Who knows what lurks here..."
case model.MaterialWall:
return "Wall", "Mediocre masonry"
case model.MaterialGround:
return "Ground", "Try not to trip"
}
return "Void", "Who knows what lurks here..."
}
func (ls *LookState) lookCursorCoordsToScreenCoords() (sX, xY int) {
x, y := ls.cursorPos.XY()
middleOfScreenX, middleOfScreenY := engine.TERMINAL_SIZE_WIDTH/2, engine.TERMINAL_SIZE_HEIGHT/2
return middleOfScreenX + x, middleOfScreenY + y
}
func (ls *LookState) lookCursorCoordsToDungeonCoords() (sX, xY int) {
x, y := ls.cursorPos.XY()
playerX, playerY := ls.player.Position().XY()
return playerX + x, playerY + y
2024-05-06 18:59:14 +03:00
}
2024-05-06 20:43:35 +03:00
func (ls *LookState) CollectDrawables() []engine.Drawable {
drawables := append(ls.prevState.CollectDrawables(), engine.CreateDrawingInstructions(func(v views.View) {
if time.Since(ls.lastCursorBlinkTime).Milliseconds() >= CursorBlinkTime {
ls.showCursor = !ls.showCursor
ls.lastCursorBlinkTime = time.Now()
}
if ls.showCursor {
x, y := ls.lookCursorCoordsToScreenCoords()
v.SetContent(x, y, CursorRune, nil, tcell.StyleDefault)
}
}))
2024-06-10 23:20:38 +03:00
if ls.showLog {
drawables = append(drawables, ls.uiEventLog)
}
return drawables
2024-05-06 18:59:14 +03:00
}