mirror of
https://github.com/mvvasilev/last_light.git
synced 2025-04-19 12:49:52 +03:00
Simulated arrows. Why...
This commit is contained in:
parent
e8f3c6ca9e
commit
855fa8dfc1
12 changed files with 246 additions and 85 deletions
|
@ -38,3 +38,20 @@ func (p *Path) Next() (current Position, hasNext bool) {
|
||||||
|
|
||||||
return p.CurrentPosition(), true
|
return p.CurrentPosition(), true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LinePath(from, to Position) *Path {
|
||||||
|
points := make([]Position, 0)
|
||||||
|
n := float64(from.Distance(to))
|
||||||
|
|
||||||
|
for step := 0.0; step <= n; step += 1.0 {
|
||||||
|
t := 0.0
|
||||||
|
|
||||||
|
if n != 0 {
|
||||||
|
t = step / n
|
||||||
|
}
|
||||||
|
|
||||||
|
points = append(points, LerpPositions(from, to, t))
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreatePath(from, to, points)
|
||||||
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ func FindPath(from Position, to Position, maxDistance int, isPassable func(x, y
|
||||||
iteration++
|
iteration++
|
||||||
|
|
||||||
if iteration >= maxDistance {
|
if iteration >= maxDistance {
|
||||||
return nil
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(openList) == 0 {
|
if len(openList) == 0 {
|
||||||
|
|
|
@ -173,3 +173,14 @@ func AbsInt(val int) int {
|
||||||
}
|
}
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Lerp(start, end float64, t float64) float64 {
|
||||||
|
return start*(1.0-t) + end*t
|
||||||
|
}
|
||||||
|
|
||||||
|
func LerpPositions(p0, p1 Position, t float64) Position {
|
||||||
|
return PositionAt(
|
||||||
|
int(math.Round(Lerp(float64(p0.x), float64(p1.x), t))),
|
||||||
|
int(math.Round(Lerp(float64(p0.y), float64(p1.y), t))),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -87,6 +87,11 @@ type Entity_DropTableComponent struct {
|
||||||
DropTable *LootTable
|
DropTable *LootTable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Entity_ProjectileComponent struct {
|
||||||
|
Source Entity
|
||||||
|
Path *engine.Path
|
||||||
|
}
|
||||||
|
|
||||||
type Entity interface {
|
type Entity interface {
|
||||||
UniqueId() uuid.UUID
|
UniqueId() uuid.UUID
|
||||||
|
|
||||||
|
@ -99,6 +104,7 @@ type Entity interface {
|
||||||
Stats() *Entity_StatsHolderComponent
|
Stats() *Entity_StatsHolderComponent
|
||||||
HealthData() *Entity_HealthComponent
|
HealthData() *Entity_HealthComponent
|
||||||
DropTable() *Entity_DropTableComponent
|
DropTable() *Entity_DropTableComponent
|
||||||
|
Projectile() *Entity_ProjectileComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseEntity struct {
|
type BaseEntity struct {
|
||||||
|
@ -113,6 +119,7 @@ type BaseEntity struct {
|
||||||
stats *Entity_StatsHolderComponent
|
stats *Entity_StatsHolderComponent
|
||||||
damageable *Entity_HealthComponent
|
damageable *Entity_HealthComponent
|
||||||
dropTable *Entity_DropTableComponent
|
dropTable *Entity_DropTableComponent
|
||||||
|
projectile *Entity_ProjectileComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
func (be *BaseEntity) UniqueId() uuid.UUID {
|
func (be *BaseEntity) UniqueId() uuid.UUID {
|
||||||
|
@ -155,6 +162,10 @@ func (be *BaseEntity) DropTable() *Entity_DropTableComponent {
|
||||||
return be.dropTable
|
return be.dropTable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (be *BaseEntity) Projectile() *Entity_ProjectileComponent {
|
||||||
|
return be.projectile
|
||||||
|
}
|
||||||
|
|
||||||
func CreateEntity(components ...func(*BaseEntity)) *BaseEntity {
|
func CreateEntity(components ...func(*BaseEntity)) *BaseEntity {
|
||||||
e := &BaseEntity{
|
e := &BaseEntity{
|
||||||
id: uuid.New(),
|
id: uuid.New(),
|
||||||
|
@ -250,3 +261,12 @@ func WithDropTable(table map[int]ItemSupplier) func(e *BaseEntity) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithProjectileData(source Entity, path *engine.Path) func(e *BaseEntity) {
|
||||||
|
return func(e *BaseEntity) {
|
||||||
|
e.projectile = &Entity_ProjectileComponent{
|
||||||
|
Source: source,
|
||||||
|
Path: path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,11 +5,86 @@ import (
|
||||||
"mvvasilev/last_light/engine"
|
"mvvasilev/last_light/engine"
|
||||||
)
|
)
|
||||||
|
|
||||||
// func ProjectileBehavior() func(npc Entity) (complete bool, requeue bool) {
|
type ProjectileSprite rune
|
||||||
// return func(npc Entity) (complete bool, requeue bool) {
|
|
||||||
|
|
||||||
// }
|
//
|
||||||
// }
|
// \ | /
|
||||||
|
//
|
||||||
|
// ─ + ─
|
||||||
|
//
|
||||||
|
// / | \
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProjectileSprite_NorthSouth ProjectileSprite = '|'
|
||||||
|
ProjectileSprite_EastWest = '─'
|
||||||
|
ProjectileSprite_NorthEastSouthWest = '/'
|
||||||
|
ProjectileSprite_NorthWestSouthEast = '\\'
|
||||||
|
)
|
||||||
|
|
||||||
|
func ProjectileBehavior(eventLog *engine.GameEventLog, dungeon *Dungeon) func(npc Entity) (complete bool, requeue bool) {
|
||||||
|
return func(npc Entity) (complete bool, requeue bool) {
|
||||||
|
hasNext := ProjectileFollowPathNext(npc, eventLog, dungeon)
|
||||||
|
|
||||||
|
return !hasNext, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProjectileFollowPathNext(npc Entity, eventLog *engine.GameEventLog, dungeon *Dungeon) (hasNext bool) {
|
||||||
|
projectileData := npc.Projectile()
|
||||||
|
positionData := npc.Positioned()
|
||||||
|
|
||||||
|
if projectileData == nil || positionData == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
path := projectileData.Path
|
||||||
|
next, hasNext := path.Next()
|
||||||
|
|
||||||
|
nextTile := dungeon.CurrentLevel().TileAt(next.XY())
|
||||||
|
|
||||||
|
nextTileEntityData := nextTile.Entity()
|
||||||
|
|
||||||
|
dungeon.CurrentLevel().DropEntity(npc.UniqueId())
|
||||||
|
|
||||||
|
positionData.Position = next
|
||||||
|
|
||||||
|
// The next tile is impassable ( wall, void, etc. ) and contains no entity to damage
|
||||||
|
// This is the end of the path
|
||||||
|
if nextTileEntityData == nil && !nextTile.Passable() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, if the tile is passible, but also the end of the path, stop here and despawn the projectile
|
||||||
|
if nextTileEntityData == nil && next == projectileData.Path.To() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// The next tile contains an entity, do damage to it if we have damage data
|
||||||
|
if nextTileEntityData != nil {
|
||||||
|
|
||||||
|
// The arrow strikes against its master, but to no avail, for I decree it to be illegal
|
||||||
|
if nextTileEntityData.Entity == projectileData.Source {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Futher I decree, that should the arrow striketh at thyself, it shall be blocked from doing so
|
||||||
|
if nextTileEntityData.Entity == npc {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if projectileData.Source == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecuteAttack(eventLog, projectileData.Source, nextTileEntityData.Entity)
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
dungeon.CurrentLevel().AddEntity(npc)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func HostileNPCBehavior(eventLog *engine.GameEventLog, dungeon *Dungeon, player *Player) func(npc Entity) (complete bool, requeue bool) {
|
func HostileNPCBehavior(eventLog *engine.GameEventLog, dungeon *Dungeon, player *Player) func(npc Entity) (complete bool, requeue bool) {
|
||||||
return func(npc Entity) (complete bool, requeue bool) {
|
return func(npc Entity) (complete bool, requeue bool) {
|
||||||
|
@ -121,6 +196,10 @@ func WithinHitRange(pos engine.Position, otherPos engine.Position) bool {
|
||||||
func ExecuteAttack(eventLog *engine.GameEventLog, attacker, victim Entity) {
|
func ExecuteAttack(eventLog *engine.GameEventLog, attacker, victim Entity) {
|
||||||
hit, precision, evasion, dmg, dmgType := CalculateAttack(attacker, victim)
|
hit, precision, evasion, dmg, dmgType := CalculateAttack(attacker, victim)
|
||||||
|
|
||||||
|
if attacker.Projectile() != nil {
|
||||||
|
attacker = attacker.Projectile().Source
|
||||||
|
}
|
||||||
|
|
||||||
attackerName := "Unknown"
|
attackerName := "Unknown"
|
||||||
|
|
||||||
if attacker.Named() != nil {
|
if attacker.Named() != nil {
|
||||||
|
@ -138,6 +217,10 @@ func ExecuteAttack(eventLog *engine.GameEventLog, attacker, victim Entity) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if victim.HealthData() == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
victim.HealthData().Health -= dmg
|
victim.HealthData().Health -= dmg
|
||||||
|
|
||||||
if victim.HealthData().Health <= 0 {
|
if victim.HealthData().Health <= 0 {
|
||||||
|
|
|
@ -12,10 +12,13 @@ const (
|
||||||
ImpClaws specialItemType = 100_000 + iota
|
ImpClaws specialItemType = 100_000 + iota
|
||||||
)
|
)
|
||||||
|
|
||||||
func Entity_ArrowProjectile(startX, startY int, targetX, targetY int) Entity {
|
func Entity_ArrowProjectile(source Entity, path *engine.Path, eventLog *engine.GameEventLog, dungeon *Dungeon) Entity {
|
||||||
return CreateEntity(
|
return CreateEntity(
|
||||||
WithName("Arrow"),
|
WithName("Arrow"),
|
||||||
WithPosition(engine.PositionAt(startX, startY)),
|
WithPosition(path.From()),
|
||||||
|
WithPresentation('?', tcell.StyleDefault),
|
||||||
|
WithProjectileData(source, path),
|
||||||
|
WithBehavior(1, ProjectileBehavior(eventLog, dungeon)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
type Player struct {
|
type Player struct {
|
||||||
Entity
|
Entity
|
||||||
|
|
||||||
|
inLookState bool
|
||||||
skipNextTurn bool
|
skipNextTurn bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,3 +65,11 @@ func (p *Player) SkipNextTurn(skip bool) {
|
||||||
func (p *Player) IsNextTurnSkipped() bool {
|
func (p *Player) IsNextTurnSkipped() bool {
|
||||||
return p.skipNextTurn
|
return p.skipNextTurn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Player) IsInLookState() bool {
|
||||||
|
return p.inLookState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetInLookState(lookState bool) {
|
||||||
|
p.inLookState = lookState
|
||||||
|
}
|
||||||
|
|
|
@ -90,7 +90,7 @@ func (d *Dungeon) HasNextLevel() bool {
|
||||||
|
|
||||||
type DungeonLevel struct {
|
type DungeonLevel struct {
|
||||||
ground Map
|
ground Map
|
||||||
entitiesByPosition map[engine.Position][]Entity
|
entitiesByPosition map[engine.Position]Entity
|
||||||
entities map[uuid.UUID]Entity
|
entities map[uuid.UUID]Entity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ func CreateDungeonLevel(width, height int, dungeonType DungeonType) (dLevel *Dun
|
||||||
dLevel = &DungeonLevel{
|
dLevel = &DungeonLevel{
|
||||||
ground: groundLevel,
|
ground: groundLevel,
|
||||||
entities: map[uuid.UUID]Entity{},
|
entities: map[uuid.UUID]Entity{},
|
||||||
entitiesByPosition: map[engine.Position][]Entity{},
|
entitiesByPosition: map[engine.Position]Entity{},
|
||||||
}
|
}
|
||||||
|
|
||||||
if groundLevel.Rooms() == nil {
|
if groundLevel.Rooms() == nil {
|
||||||
|
@ -238,11 +238,7 @@ func (d *DungeonLevel) AddEntity(entity Entity) {
|
||||||
d.entities[entity.UniqueId()] = entity
|
d.entities[entity.UniqueId()] = entity
|
||||||
|
|
||||||
if entity.Positioned() != nil {
|
if entity.Positioned() != nil {
|
||||||
if d.entitiesByPosition[entity.Positioned().Position] == nil {
|
d.entitiesByPosition[entity.Positioned().Position] = entity
|
||||||
d.entitiesByPosition[entity.Positioned().Position] = []Entity{entity}
|
|
||||||
} else {
|
|
||||||
d.entitiesByPosition[entity.Positioned().Position] = append(d.entitiesByPosition[entity.Positioned().Position], entity)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,9 +254,7 @@ func (d *DungeonLevel) MoveEntityTo(uuid uuid.UUID, x, y int) {
|
||||||
ent.Positioned().Position = engine.PositionAt(x, y)
|
ent.Positioned().Position = engine.PositionAt(x, y)
|
||||||
|
|
||||||
if d.entitiesByPosition[ent.Positioned().Position] == nil {
|
if d.entitiesByPosition[ent.Positioned().Position] == nil {
|
||||||
d.entitiesByPosition[ent.Positioned().Position] = []Entity{ent}
|
d.entitiesByPosition[ent.Positioned().Position] = ent
|
||||||
} else {
|
|
||||||
d.entitiesByPosition[ent.Positioned().Position] = append(d.entitiesByPosition[ent.Positioned().Position], ent)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +297,7 @@ func (d *DungeonLevel) TileAt(x, y int) Tile {
|
||||||
tile := Map_TileAt(d.ground, x, y)
|
tile := Map_TileAt(d.ground, x, y)
|
||||||
|
|
||||||
if entity != nil {
|
if entity != nil {
|
||||||
return CreateTileFromPrototype(tile, Tile_WithEntities(entity))
|
return CreateTileFromPrototype(tile, Tile_WithEntity(entity))
|
||||||
}
|
}
|
||||||
|
|
||||||
return tile
|
return tile
|
||||||
|
@ -316,14 +310,14 @@ func (d *DungeonLevel) IsTilePassable(x, y int) bool {
|
||||||
|
|
||||||
tile := d.TileAt(x, y)
|
tile := d.TileAt(x, y)
|
||||||
|
|
||||||
if tile.Entities() != nil {
|
if tile.Entity() != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return tile.Passable()
|
return tile.Passable()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DungeonLevel) EntitiesAt(x, y int) (e []Entity) {
|
func (d *DungeonLevel) EntityAt(x, y int) (e Entity) {
|
||||||
return d.entitiesByPosition[engine.PositionAt(x, y)]
|
return d.entitiesByPosition[engine.PositionAt(x, y)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"slices"
|
|
||||||
|
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Material uint
|
type Material uint
|
||||||
|
@ -26,7 +23,7 @@ type Tile_ItemComponent struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tile_EntityComponent struct {
|
type Tile_EntityComponent struct {
|
||||||
Entities []Entity
|
Entity Entity
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tile interface {
|
type Tile interface {
|
||||||
|
@ -40,9 +37,9 @@ type Tile interface {
|
||||||
RemoveItem()
|
RemoveItem()
|
||||||
WithItem(item Item)
|
WithItem(item Item)
|
||||||
|
|
||||||
Entities() *Tile_EntityComponent
|
Entity() *Tile_EntityComponent
|
||||||
RemoveEntity(uuid uuid.UUID)
|
RemoveEntity()
|
||||||
AddEntity(entity Entity)
|
WithEntity(entity Entity)
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseTile struct {
|
type BaseTile struct {
|
||||||
|
@ -53,7 +50,7 @@ type BaseTile struct {
|
||||||
passable, opaque, transparent bool
|
passable, opaque, transparent bool
|
||||||
|
|
||||||
item *Tile_ItemComponent
|
item *Tile_ItemComponent
|
||||||
entities *Tile_EntityComponent
|
entity *Tile_EntityComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateTileFromPrototype(prototype Tile, components ...func(*BaseTile)) Tile {
|
func CreateTileFromPrototype(prototype Tile, components ...func(*BaseTile)) Tile {
|
||||||
|
@ -121,46 +118,24 @@ func (t *BaseTile) WithItem(item Item) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *BaseTile) Entities() *Tile_EntityComponent {
|
func (t *BaseTile) Entity() *Tile_EntityComponent {
|
||||||
return t.entities
|
return t.entity
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *BaseTile) RemoveEntity(uuid uuid.UUID) {
|
func (t *BaseTile) WithEntity(entity Entity) {
|
||||||
if t.entities == nil {
|
t.entity = &Tile_EntityComponent{
|
||||||
return
|
Entity: entity,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.entities.Entities = slices.DeleteFunc(t.entities.Entities, func(e Entity) bool { return e.UniqueId() == uuid })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *BaseTile) AddEntity(entity Entity) {
|
func (t *BaseTile) RemoveEntity() {
|
||||||
if t.entities == nil {
|
t.entity = nil
|
||||||
t.entities = &Tile_EntityComponent{
|
|
||||||
Entities: []Entity{
|
|
||||||
entity,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
t.entities.Entities = append(t.entities.Entities, entity)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Tile_WithEntity(entity Entity) func(*BaseTile) {
|
func Tile_WithEntity(entity Entity) func(*BaseTile) {
|
||||||
return func(bt *BaseTile) {
|
return func(bt *BaseTile) {
|
||||||
bt.entities = &Tile_EntityComponent{
|
bt.entity = &Tile_EntityComponent{
|
||||||
Entities: []Entity{
|
Entity: entity,
|
||||||
entity,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Tile_WithEntities(entities []Entity) func(*BaseTile) {
|
|
||||||
return func(bt *BaseTile) {
|
|
||||||
bt.entities = &Tile_EntityComponent{
|
|
||||||
Entities: entities,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"mvvasilev/last_light/engine"
|
"mvvasilev/last_light/engine"
|
||||||
"mvvasilev/last_light/game/model"
|
"mvvasilev/last_light/game/model"
|
||||||
"mvvasilev/last_light/game/systems"
|
"mvvasilev/last_light/game/systems"
|
||||||
|
"mvvasilev/last_light/game/ui"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
|
@ -23,9 +24,14 @@ type LookState struct {
|
||||||
player *model.Player
|
player *model.Player
|
||||||
dungeon *model.Dungeon
|
dungeon *model.Dungeon
|
||||||
|
|
||||||
|
showLog bool
|
||||||
|
uiEventLog *ui.UIEventLog
|
||||||
|
|
||||||
showCursor bool
|
showCursor bool
|
||||||
cursorPos engine.Position
|
cursorPos engine.Position
|
||||||
lastCursorBlinkTime time.Time
|
lastCursorBlinkTime time.Time
|
||||||
|
|
||||||
|
nextGameState GameState
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateLookState(prevState GameState, eventLog *engine.GameEventLog, dungeon *model.Dungeon, inputSystem *systems.InputSystem, turnSystem *systems.TurnSystem, player *model.Player) *LookState {
|
func CreateLookState(prevState GameState, eventLog *engine.GameEventLog, dungeon *model.Dungeon, inputSystem *systems.InputSystem, turnSystem *systems.TurnSystem, player *model.Player) *LookState {
|
||||||
|
@ -38,6 +44,8 @@ func CreateLookState(prevState GameState, eventLog *engine.GameEventLog, dungeon
|
||||||
eventLog: eventLog,
|
eventLog: eventLog,
|
||||||
cursorPos: engine.PositionAt(0, 0),
|
cursorPos: engine.PositionAt(0, 0),
|
||||||
lastCursorBlinkTime: time.Now(),
|
lastCursorBlinkTime: time.Now(),
|
||||||
|
showLog: true,
|
||||||
|
uiEventLog: ui.CreateUIEventLog(0, 17, 80, 7, eventLog, tcell.StyleDefault),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +54,8 @@ func (ls *LookState) InputContext() systems.InputContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *LookState) OnTick(dt int64) GameState {
|
func (ls *LookState) OnTick(dt int64) GameState {
|
||||||
|
ls.nextGameState = ls
|
||||||
|
|
||||||
switch ls.inputSystem.NextAction() {
|
switch ls.inputSystem.NextAction() {
|
||||||
case systems.InputAction_Move_North:
|
case systems.InputAction_Move_North:
|
||||||
ls.cursorPos = ls.cursorPos.WithOffset(model.MovementDirectionOffset(model.North))
|
ls.cursorPos = ls.cursorPos.WithOffset(model.MovementDirectionOffset(model.North))
|
||||||
|
@ -55,15 +65,17 @@ func (ls *LookState) OnTick(dt int64) GameState {
|
||||||
ls.cursorPos = ls.cursorPos.WithOffset(model.MovementDirectionOffset(model.East))
|
ls.cursorPos = ls.cursorPos.WithOffset(model.MovementDirectionOffset(model.East))
|
||||||
case systems.InputAction_Move_West:
|
case systems.InputAction_Move_West:
|
||||||
ls.cursorPos = ls.cursorPos.WithOffset(model.MovementDirectionOffset(model.West))
|
ls.cursorPos = ls.cursorPos.WithOffset(model.MovementDirectionOffset(model.West))
|
||||||
|
case systems.InputAction_OpenLogs:
|
||||||
|
ls.showLog = !ls.showLog
|
||||||
case systems.InputAction_Describe:
|
case systems.InputAction_Describe:
|
||||||
ls.Describe()
|
ls.Describe()
|
||||||
case systems.InputAction_Shoot:
|
case systems.InputAction_Shoot:
|
||||||
ls.ShootEquippedWeapon()
|
ls.ShootEquippedWeapon()
|
||||||
case systems.InputAction_Menu_Exit:
|
case systems.InputAction_Menu_Exit:
|
||||||
return ls.prevState
|
ls.nextGameState = ls.prevState
|
||||||
}
|
}
|
||||||
|
|
||||||
return ls
|
return ls.nextGameState
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *LookState) ShootEquippedWeapon() {
|
func (ls *LookState) ShootEquippedWeapon() {
|
||||||
|
@ -90,15 +102,49 @@ func (ls *LookState) ShootEquippedWeapon() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Projectiles
|
// TODO: Projectiles
|
||||||
|
dX, dY := ls.lookCursorCoordsToDungeonCoords()
|
||||||
|
|
||||||
|
distance := engine.PositionAt(dX, dY).Distance(ls.player.Position())
|
||||||
|
|
||||||
|
if distance >= 12 {
|
||||||
|
ls.eventLog.Log("Can't see in the dark that far")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
path := engine.LinePath(
|
||||||
|
ls.player.Position(),
|
||||||
|
engine.PositionAt(dX, dY),
|
||||||
|
)
|
||||||
|
|
||||||
|
if path == nil {
|
||||||
|
ls.eventLog.Log("Can't shoot there, something is in the way")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
projectile := model.Entity_ArrowProjectile(ls.player, path, ls.eventLog, ls.dungeon)
|
||||||
|
|
||||||
|
ls.turnSystem.Schedule(
|
||||||
|
projectile.Behavior().Speed,
|
||||||
|
projectile.Behavior().Behavior,
|
||||||
|
)
|
||||||
|
|
||||||
ls.player.SkipNextTurn(true)
|
ls.player.SkipNextTurn(true)
|
||||||
|
|
||||||
ls.turnSystem.NextTurn()
|
ls.nextGameState = ls.prevState
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *LookState) Describe() {
|
func (ls *LookState) Describe() {
|
||||||
dX, dY := ls.lookCursorCoordsToDungeonCoords()
|
dX, dY := ls.lookCursorCoordsToDungeonCoords()
|
||||||
|
|
||||||
|
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))
|
isVisibleFromPlayer, lastTile := model.HasLineOfSight(ls.dungeon, ls.player.Position(), engine.PositionAt(dX, dY))
|
||||||
|
|
||||||
if !isVisibleFromPlayer {
|
if !isVisibleFromPlayer {
|
||||||
|
@ -110,10 +156,10 @@ func (ls *LookState) Describe() {
|
||||||
|
|
||||||
tile := ls.dungeon.CurrentLevel().TileAt(dX, dY)
|
tile := ls.dungeon.CurrentLevel().TileAt(dX, dY)
|
||||||
|
|
||||||
entities := tile.Entities()
|
entity := tile.Entity()
|
||||||
|
|
||||||
if entities != nil {
|
if entity != nil {
|
||||||
ls.DescribeEntities(entities.Entities)
|
ls.DescribeEntity(entity.Entity)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -131,20 +177,19 @@ func (ls *LookState) Describe() {
|
||||||
ls.eventLog.Log(fmt.Sprintf("%s: %s", materialName, materialDesc))
|
ls.eventLog.Log(fmt.Sprintf("%s: %s", materialName, materialDesc))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *LookState) DescribeEntities(entities []model.Entity) {
|
func (ls *LookState) DescribeEntity(entity model.Entity) {
|
||||||
if entities == nil {
|
if entity == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, entity := range entities {
|
|
||||||
if entity == ls.player {
|
if entity == ls.player {
|
||||||
ls.eventLog.Log("You")
|
ls.eventLog.Log("You")
|
||||||
|
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if entity.Named() == nil {
|
if entity.Named() == nil {
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if entity.Described() != nil {
|
if entity.Described() != nil {
|
||||||
|
@ -152,7 +197,6 @@ func (ls *LookState) DescribeEntities(entities []model.Entity) {
|
||||||
} else {
|
} else {
|
||||||
ls.eventLog.Log(entity.Named().Name)
|
ls.eventLog.Log(entity.Named().Name)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *LookState) DescribeItem(item model.Item) {
|
func (ls *LookState) DescribeItem(item model.Item) {
|
||||||
|
@ -209,5 +253,9 @@ func (ls *LookState) CollectDrawables() []engine.Drawable {
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
if ls.showLog {
|
||||||
|
drawables = append(drawables, ls.uiEventLog)
|
||||||
|
}
|
||||||
|
|
||||||
return drawables
|
return drawables
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ func CreatePlayingState(turnSystem *systems.TurnSystem, inputSystem *systems.Inp
|
||||||
case systems.InputAction_OpenInventory:
|
case systems.InputAction_OpenInventory:
|
||||||
s.nextGameState = CreateInventoryScreenState(s.eventLog, s.dungeon, s.inputSystem, s.turnSystem, s.player, s)
|
s.nextGameState = CreateInventoryScreenState(s.eventLog, s.dungeon, s.inputSystem, s.turnSystem, s.player, s)
|
||||||
case systems.InputAction_EnterLookMode:
|
case systems.InputAction_EnterLookMode:
|
||||||
s.viewShortLogs = !s.viewShortLogs
|
s.viewShortLogs = false
|
||||||
s.nextGameState = CreateLookState(s, s.eventLog, s.dungeon, s.inputSystem, s.turnSystem, s.player)
|
s.nextGameState = CreateLookState(s, s.eventLog, s.dungeon, s.inputSystem, s.turnSystem, s.player)
|
||||||
case systems.InputAction_PickUpItem:
|
case systems.InputAction_PickUpItem:
|
||||||
complete = PickUpItemUnderPlayer(s.eventLog, s.dungeon, s.player)
|
complete = PickUpItemUnderPlayer(s.eventLog, s.dungeon, s.player)
|
||||||
|
@ -162,7 +162,7 @@ func (ps *PlayingState) MovePlayer(direction model.Direction) (success bool) {
|
||||||
|
|
||||||
newPlayerPos := ps.player.Position().WithOffset(model.MovementDirectionOffset(direction))
|
newPlayerPos := ps.player.Position().WithOffset(model.MovementDirectionOffset(direction))
|
||||||
|
|
||||||
ent := ps.dungeon.CurrentLevel().EntitiesAt(newPlayerPos.XY())[0]
|
ent := ps.dungeon.CurrentLevel().EntityAt(newPlayerPos.XY())
|
||||||
|
|
||||||
// We are moving into an entity with health data. Attack it.
|
// We are moving into an entity with health data. Attack it.
|
||||||
if ent != nil && ent.HealthData() != nil {
|
if ent != nil && ent.HealthData() != nil {
|
||||||
|
@ -334,8 +334,8 @@ func (ps *PlayingState) CollectDrawables() []engine.Drawable {
|
||||||
|
|
||||||
if tile != nil {
|
if tile != nil {
|
||||||
|
|
||||||
if tile.Entities() != nil {
|
if tile.Entity() != nil {
|
||||||
return tile.Entities().Entities[0].Presentable().Rune, tile.Entities().Entities[0].Presentable().Style
|
return tile.Entity().Entity.Presentable().Rune, tile.Entity().Entity.Presentable().Style
|
||||||
}
|
}
|
||||||
|
|
||||||
if tile.Item() != nil {
|
if tile.Item() != nil {
|
||||||
|
|
|
@ -93,6 +93,7 @@ func CreateInputSystemWithDefaultBindings() *InputSystem {
|
||||||
InputKeyOf(InputContext_Look, 0, tcell.KeyRight, 0): InputAction_Move_East,
|
InputKeyOf(InputContext_Look, 0, tcell.KeyRight, 0): InputAction_Move_East,
|
||||||
InputKeyOf(InputContext_Look, 0, tcell.KeyRune, 'd'): InputAction_Describe,
|
InputKeyOf(InputContext_Look, 0, tcell.KeyRune, 'd'): InputAction_Describe,
|
||||||
InputKeyOf(InputContext_Look, 0, tcell.KeyRune, 'a'): InputAction_Shoot,
|
InputKeyOf(InputContext_Look, 0, tcell.KeyRune, 'a'): InputAction_Shoot,
|
||||||
|
InputKeyOf(InputContext_Look, 0, tcell.KeyRune, 'l'): InputAction_OpenLogs,
|
||||||
InputKeyOf(InputContext_Look, 0, tcell.KeyESC, 0): InputAction_Menu_Exit,
|
InputKeyOf(InputContext_Look, 0, tcell.KeyESC, 0): InputAction_Menu_Exit,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue