mirror of
https://github.com/mvvasilev/last_light.git
synced 2025-04-19 12:49:52 +03:00
Character stats menu start, key bindings menu start, add tophat item
This commit is contained in:
parent
9445958338
commit
8e6309c824
13 changed files with 289 additions and 92 deletions
|
@ -3,22 +3,9 @@ package model
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"mvvasilev/last_light/engine"
|
"mvvasilev/last_light/engine"
|
||||||
)
|
"mvvasilev/last_light/game/systems"
|
||||||
|
|
||||||
type ArrowSprite rune
|
"github.com/gdamore/tcell/v2"
|
||||||
|
|
||||||
//
|
|
||||||
// \ | /
|
|
||||||
//
|
|
||||||
// ─ + ─
|
|
||||||
//
|
|
||||||
// / | \
|
|
||||||
|
|
||||||
const (
|
|
||||||
ProjectileSprite_NorthSouth ArrowSprite = '|'
|
|
||||||
ProjectileSprite_EastWest ArrowSprite = '─'
|
|
||||||
ProjectileSprite_NorthEastSouthWest ArrowSprite = '/'
|
|
||||||
ProjectileSprite_NorthWestSouthEast ArrowSprite = '\\'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ProjectileBehavior(eventLog *engine.GameEventLog, dungeon *Dungeon) func(npc Entity) (complete bool, requeue bool) {
|
func ProjectileBehavior(eventLog *engine.GameEventLog, dungeon *Dungeon) func(npc Entity) (complete bool, requeue bool) {
|
||||||
|
@ -76,7 +63,8 @@ func ProjectileFollowPathNext(npc Entity, eventLog *engine.GameEventLog, dungeon
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecuteAttack(eventLog, projectileData.Source, nextTileEntityData.Entity)
|
// TODO: calculate additional projectile damage
|
||||||
|
ExecuteAttack(eventLog, projectileData.Source, nextTileEntityData.Entity, true)
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -86,7 +74,7 @@ func ProjectileFollowPathNext(npc Entity, eventLog *engine.GameEventLog, dungeon
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func HostileNPCBehavior(eventLog *engine.GameEventLog, dungeon *Dungeon, player *Player) func(npc Entity) (complete bool, requeue bool) {
|
func HostileMeleeNPCBehavior(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) {
|
||||||
CalcPathToPlayerAndMove(25, eventLog, dungeon, npc, player)
|
CalcPathToPlayerAndMove(25, eventLog, dungeon, npc, player)
|
||||||
|
|
||||||
|
@ -94,6 +82,16 @@ func HostileNPCBehavior(eventLog *engine.GameEventLog, dungeon *Dungeon, player
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func HostileRangedNPCBehavior(eventLog *engine.GameEventLog, dungeon *Dungeon, player *Player) func(npc Entity) (complete bool, requeue bool) {
|
||||||
|
return func(npc Entity) (complete bool, requeue bool) {
|
||||||
|
return true, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CalcPathToPlayerAndKeepDistance(simulationDistance int, eventLog *engine.GameEventLog, dungeon *Dungeon, npc Entity, player *Player) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func CalcPathToPlayerAndMove(simulationDistance int, eventLog *engine.GameEventLog, dungeon *Dungeon, npc Entity, player *Player) {
|
func CalcPathToPlayerAndMove(simulationDistance int, eventLog *engine.GameEventLog, dungeon *Dungeon, npc Entity, player *Player) {
|
||||||
if npc.Positioned().Position.DistanceSquared(player.Position()) > simulationDistance*simulationDistance {
|
if npc.Positioned().Position.DistanceSquared(player.Position()) > simulationDistance*simulationDistance {
|
||||||
return
|
return
|
||||||
|
@ -141,7 +139,7 @@ func CalcPathToPlayerAndMove(simulationDistance int, eventLog *engine.GameEventL
|
||||||
}
|
}
|
||||||
|
|
||||||
if WithinHitRange(npc.Positioned().Position, player.Position()) {
|
if WithinHitRange(npc.Positioned().Position, player.Position()) {
|
||||||
ExecuteAttack(eventLog, npc, player)
|
ExecuteAttack(eventLog, npc, player, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pathToPlayer := engine.FindPath(
|
pathToPlayer := engine.FindPath(
|
||||||
|
@ -193,8 +191,8 @@ func WithinHitRange(pos engine.Position, otherPos engine.Position) bool {
|
||||||
return pos.WithOffset(-1, 0) == otherPos || pos.WithOffset(+1, 0) == otherPos || pos.WithOffset(0, -1) == otherPos || pos.WithOffset(0, +1) == otherPos
|
return pos.WithOffset(-1, 0) == otherPos || pos.WithOffset(+1, 0) == otherPos || pos.WithOffset(0, -1) == otherPos || pos.WithOffset(0, +1) == otherPos
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExecuteAttack(eventLog *engine.GameEventLog, attacker, victim Entity) {
|
func ExecuteAttack(eventLog *engine.GameEventLog, attacker, victim Entity, isRanged bool) {
|
||||||
hit, precision, evasion, dmg, dmgType := CalculateAttack(attacker, victim)
|
hit, precision, evasion, dmg, dmgType := CalculateAttack(attacker, victim, isRanged)
|
||||||
|
|
||||||
if attacker.Projectile() != nil {
|
if attacker.Projectile() != nil {
|
||||||
attacker = attacker.Projectile().Source
|
attacker = attacker.Projectile().Source
|
||||||
|
@ -232,12 +230,140 @@ func ExecuteAttack(eventLog *engine.GameEventLog, attacker, victim Entity) {
|
||||||
eventLog.Log(fmt.Sprintf("%s attacked %s, and hit for %v %v damage", attackerName, victimName, dmg, DamageTypeName(dmgType)))
|
eventLog.Log(fmt.Sprintf("%s attacked %s, and hit for %v %v damage", attackerName, victimName, dmg, DamageTypeName(dmgType)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func CalculateAttack(attacker, victim Entity) (hit bool, precisionRoll, evasionRoll int, damage int, damageType DamageType) {
|
func CalculateAttack(attacker, victim Entity, isRanged bool) (hit bool, precisionRoll, evasionRoll int, damage int, damageType DamageType) {
|
||||||
if attacker.Equipped() != nil && attacker.Equipped().Inventory.AtSlot(EquippedSlotDominantHand) != nil {
|
if attacker.Equipped() != nil && attacker.Equipped().Inventory.AtSlot(EquippedSlotDominantHand) != nil {
|
||||||
weapon := attacker.Equipped().Inventory.AtSlot(EquippedSlotDominantHand)
|
weapon := attacker.Equipped().Inventory.AtSlot(EquippedSlotDominantHand)
|
||||||
|
|
||||||
|
if weapon.Damaging() != nil {
|
||||||
|
// If the weapon is ranged, but the combat isn't, do unarmed
|
||||||
|
if isRanged && !weapon.Damaging().IsRanged {
|
||||||
|
return UnarmedAttack(attacker, victim)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doing melee damage from ranged? Don't think so.
|
||||||
|
if !isRanged && weapon.Damaging().IsRanged {
|
||||||
|
return false, 0, 0, 0, DamageType_Physical_Unarmed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return PhysicalWeaponAttack(attacker, weapon, victim)
|
return PhysicalWeaponAttack(attacker, weapon, victim)
|
||||||
} else {
|
} else {
|
||||||
return UnarmedAttack(attacker, victim)
|
return UnarmedAttack(attacker, victim)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ShootProjectile(shooter Entity, target engine.Position, eventLog *engine.GameEventLog, dungeon *Dungeon, turnSystem *systems.TurnSystem) (success bool) {
|
||||||
|
success = false
|
||||||
|
|
||||||
|
logMessage := func(msg string) {
|
||||||
|
if eventLog != nil {
|
||||||
|
eventLog.Log(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if shooter.Equipped() == nil || shooter.Positioned() == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
shooterName := "Unknown"
|
||||||
|
|
||||||
|
if shooter.Named() != nil {
|
||||||
|
shooterName = shooter.Named().Name
|
||||||
|
}
|
||||||
|
|
||||||
|
weapon := shooter.Equipped().Inventory.AtSlot(EquippedSlotDominantHand)
|
||||||
|
|
||||||
|
if weapon == nil {
|
||||||
|
logMessage(fmt.Sprintf("%s wants to shoot, but doesn't have anything equipped!", shooterName))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if weapon.Damaging() == nil || !weapon.Damaging().IsRanged {
|
||||||
|
itemName := "dominant hand"
|
||||||
|
|
||||||
|
if weapon.Named() != nil {
|
||||||
|
itemName = weapon.Named().Name
|
||||||
|
}
|
||||||
|
|
||||||
|
logMessage(fmt.Sprintf("%s wants to use %s for this, but can't!", shooterName, itemName))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
projectileItem := shooter.Equipped().Inventory.AtSlot(EquippedSlotOffhand)
|
||||||
|
|
||||||
|
if projectileItem == nil {
|
||||||
|
logMessage(fmt.Sprintf("%s doesn't have any projectiles equipped!", shooterName))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if projectileItem.ProjectileData() == nil {
|
||||||
|
projectileItemName := "off hand"
|
||||||
|
|
||||||
|
if projectileItem.Named() != nil {
|
||||||
|
projectileItemName = projectileItem.Named().Name
|
||||||
|
}
|
||||||
|
|
||||||
|
logMessage(fmt.Sprintf("%s can't use %s as ammo", shooterName, projectileItemName))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
distance := target.Distance(shooter.Positioned().Position)
|
||||||
|
|
||||||
|
if distance > 12 {
|
||||||
|
// logMessage("Can't see in the dark that far")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
path := engine.LinePath(
|
||||||
|
shooter.Positioned().Position,
|
||||||
|
target,
|
||||||
|
)
|
||||||
|
|
||||||
|
if path == nil {
|
||||||
|
// logMessage("Can't shoot there, something is in the way")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
direction := map[engine.Position]ProjectileDirection{
|
||||||
|
engine.PositionAt(-1, -1): ProjectileDirection_NorthWestSouthEast,
|
||||||
|
engine.PositionAt(+1, +1): ProjectileDirection_NorthWestSouthEast,
|
||||||
|
engine.PositionAt(-1, +1): ProjectileDirection_NorthEastSouthWest,
|
||||||
|
engine.PositionAt(+1, -1): ProjectileDirection_NorthEastSouthWest,
|
||||||
|
engine.PositionAt(0, +1): ProjectileDirection_NorthSouth,
|
||||||
|
engine.PositionAt(0, -1): ProjectileDirection_NorthSouth,
|
||||||
|
engine.PositionAt(-1, 0): ProjectileDirection_EastWest,
|
||||||
|
engine.PositionAt(+1, 0): ProjectileDirection_EastWest,
|
||||||
|
}[shooter.Positioned().Position.Diff(target).Sign()]
|
||||||
|
|
||||||
|
projectile := Entity_Projectile(
|
||||||
|
"Projectile",
|
||||||
|
projectileItem.ProjectileData().Sprites[direction],
|
||||||
|
tcell.StyleDefault,
|
||||||
|
shooter,
|
||||||
|
path,
|
||||||
|
eventLog,
|
||||||
|
dungeon,
|
||||||
|
)
|
||||||
|
|
||||||
|
turnSystem.Schedule(
|
||||||
|
projectile.Behavior().Speed,
|
||||||
|
projectile.Behavior().Behavior,
|
||||||
|
)
|
||||||
|
|
||||||
|
if projectileItem.Quantifiable() == nil {
|
||||||
|
shooter.Equipped().Inventory.Equip(nil, EquippedSlotOffhand)
|
||||||
|
} else {
|
||||||
|
projectileItem.Quantifiable().CurrentQuantity--
|
||||||
|
|
||||||
|
if projectileItem.Quantifiable().CurrentQuantity <= 0 {
|
||||||
|
shooter.Equipped().Inventory.Equip(nil, EquippedSlotOffhand)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ func CreatePlayer(x, y int, playerBaseStats map[Stat]int) *Player {
|
||||||
}
|
}
|
||||||
|
|
||||||
p.Inventory().Push(Item_Bow())
|
p.Inventory().Push(Item_Bow())
|
||||||
|
p.Inventory().Push(Item_Arrow(8))
|
||||||
|
|
||||||
p.HealthData().MaxHealth = BaseMaxHealth(p)
|
p.HealthData().MaxHealth = BaseMaxHealth(p)
|
||||||
p.HealthData().Health = p.HealthData().MaxHealth
|
p.HealthData().Health = p.HealthData().MaxHealth
|
||||||
|
|
|
@ -144,3 +144,13 @@ func (i *BasicInventory) ItemAt(x, y int) (item Item) {
|
||||||
|
|
||||||
return i.contents[index]
|
return i.contents[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *BasicInventory) Find(filter func(i Item) bool) Item {
|
||||||
|
for _, c := range i.contents {
|
||||||
|
if filter(c) {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -16,9 +16,19 @@ const (
|
||||||
MetaItemType_Magic_Armour
|
MetaItemType_Magic_Armour
|
||||||
MetaItemType_Armour
|
MetaItemType_Armour
|
||||||
MetaItemType_Consumable
|
MetaItemType_Consumable
|
||||||
|
MetaItemType_Projectile
|
||||||
MetaItemType_Potion
|
MetaItemType_Potion
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ProjectileDirection int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProjectileDirection_NorthSouth ProjectileDirection = iota
|
||||||
|
ProjectileDirection_EastWest
|
||||||
|
ProjectileDirection_NorthEastSouthWest
|
||||||
|
ProjectileDirection_NorthWestSouthEast
|
||||||
|
)
|
||||||
|
|
||||||
type Item interface {
|
type Item interface {
|
||||||
TileIcon() rune
|
TileIcon() rune
|
||||||
Icon() string
|
Icon() string
|
||||||
|
@ -33,6 +43,12 @@ type Item interface {
|
||||||
Damaging() *Item_DamagingComponent
|
Damaging() *Item_DamagingComponent
|
||||||
StatModifier() *Item_StatModifierComponent
|
StatModifier() *Item_StatModifierComponent
|
||||||
MetaTypes() *Item_MetaTypesComponent
|
MetaTypes() *Item_MetaTypesComponent
|
||||||
|
ProjectileData() *Item_ProjectileComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
type Item_ProjectileComponent struct {
|
||||||
|
Sprites map[ProjectileDirection]rune
|
||||||
|
AdditionalDamageRoll func() (dmg int, dmgType DamageType)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Item_QuantifiableComponent struct {
|
type Item_QuantifiableComponent struct {
|
||||||
|
@ -78,14 +94,15 @@ type BaseItem struct {
|
||||||
style tcell.Style
|
style tcell.Style
|
||||||
itemType ItemType
|
itemType ItemType
|
||||||
|
|
||||||
quantifiable *Item_QuantifiableComponent
|
quantifiable *Item_QuantifiableComponent
|
||||||
usable *Item_UsableComponent
|
usable *Item_UsableComponent
|
||||||
equippable *Item_EquippableComponent
|
equippable *Item_EquippableComponent
|
||||||
named *Item_NamedComponent
|
named *Item_NamedComponent
|
||||||
described *Item_DescribedComponent
|
described *Item_DescribedComponent
|
||||||
damaging *Item_DamagingComponent
|
damaging *Item_DamagingComponent
|
||||||
statModifier *Item_StatModifierComponent
|
statModifier *Item_StatModifierComponent
|
||||||
metaTypes *Item_MetaTypesComponent
|
metaTypes *Item_MetaTypesComponent
|
||||||
|
projectileData *Item_ProjectileComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *BaseItem) TileIcon() rune {
|
func (i *BaseItem) TileIcon() rune {
|
||||||
|
@ -136,6 +153,10 @@ func (i *BaseItem) MetaTypes() *Item_MetaTypesComponent {
|
||||||
return i.metaTypes
|
return i.metaTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *BaseItem) ProjectileData() *Item_ProjectileComponent {
|
||||||
|
return i.projectileData
|
||||||
|
}
|
||||||
|
|
||||||
func createBaseItem(itemType ItemType, tileIcon rune, icon string, style tcell.Style, components ...func(*BaseItem)) *BaseItem {
|
func createBaseItem(itemType ItemType, tileIcon rune, icon string, style tcell.Style, components ...func(*BaseItem)) *BaseItem {
|
||||||
i := &BaseItem{
|
i := &BaseItem{
|
||||||
itemType: itemType,
|
itemType: itemType,
|
||||||
|
@ -219,3 +240,12 @@ func item_WithMetaTypes(metaTypes []ItemMetaType) func(*BaseItem) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func item_WithProjectileData(sprites map[ProjectileDirection]rune, additionalDamageRoll func() (dmg int, dmgType DamageType)) func(*BaseItem) {
|
||||||
|
return func(bi *BaseItem) {
|
||||||
|
bi.projectileData = &Item_ProjectileComponent{
|
||||||
|
Sprites: sprites,
|
||||||
|
AdditionalDamageRoll: additionalDamageRoll,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ const (
|
||||||
ItemType_SmallHealthPotion
|
ItemType_SmallHealthPotion
|
||||||
ItemType_HealthPotion
|
ItemType_HealthPotion
|
||||||
ItemType_LargeHealthPotion
|
ItemType_LargeHealthPotion
|
||||||
|
ItemType_Arrow
|
||||||
|
|
||||||
// Weapons
|
// Weapons
|
||||||
ItemType_Bow
|
ItemType_Bow
|
||||||
|
@ -30,6 +31,7 @@ const (
|
||||||
ItemType_Quarterstaff
|
ItemType_Quarterstaff
|
||||||
|
|
||||||
// Armour
|
// Armour
|
||||||
|
ItemType_TopHat
|
||||||
|
|
||||||
// Special
|
// Special
|
||||||
)
|
)
|
||||||
|
@ -353,6 +355,41 @@ func Item_Spear() Item {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Item_Arrow(amount int) Item {
|
||||||
|
return createBaseItem(
|
||||||
|
ItemType_Arrow,
|
||||||
|
'-',
|
||||||
|
"»->",
|
||||||
|
tcell.StyleDefault.Foreground(tcell.ColorBurlyWood),
|
||||||
|
item_WithQuantity(amount, 32),
|
||||||
|
item_WithName("Arrow", tcell.StyleDefault),
|
||||||
|
item_WithProjectileData(
|
||||||
|
map[ProjectileDirection]rune{
|
||||||
|
ProjectileDirection_NorthSouth: '|',
|
||||||
|
ProjectileDirection_EastWest: '─',
|
||||||
|
ProjectileDirection_NorthEastSouthWest: '/',
|
||||||
|
ProjectileDirection_NorthWestSouthEast: '\\',
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
item_WithMetaTypes([]ItemMetaType{MetaItemType_Consumable, MetaItemType_Projectile}),
|
||||||
|
item_WithEquippable(EquippedSlotOffhand),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Item_TopHat() Item {
|
||||||
|
return createBaseItem(
|
||||||
|
ItemType_TopHat,
|
||||||
|
'■',
|
||||||
|
"_█_",
|
||||||
|
tcell.StyleDefault.Foreground(tcell.ColorLightYellow),
|
||||||
|
item_WithName("Top Hat", tcell.StyleDefault),
|
||||||
|
item_WithDescription("Smells fishy...", tcell.StyleDefault),
|
||||||
|
item_WithMetaTypes([]ItemMetaType{MetaItemType_Physical_Armour, MetaItemType_Armour}),
|
||||||
|
item_WithEquippable(EquippedSlotHead),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// func ItemTypeGold() ItemType {
|
// func ItemTypeGold() ItemType {
|
||||||
// return &BasicItemType{
|
// return &BasicItemType{
|
||||||
// id: 1,
|
// id: 1,
|
||||||
|
|
|
@ -102,6 +102,10 @@ func CreateDungeonLevel(width, height int, dungeonType DungeonType) (dLevel *Dun
|
||||||
return Item_HealthPotion()
|
return Item_HealthPotion()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
genTable.Add(3, func() Item {
|
||||||
|
return Item_Arrow(engine.RandInt(1, 5))
|
||||||
|
})
|
||||||
|
|
||||||
itemPool := []Item{
|
itemPool := []Item{
|
||||||
Item_Bow(),
|
Item_Bow(),
|
||||||
Item_Longsword(),
|
Item_Longsword(),
|
||||||
|
|
26
game/state/character_stats_state.go
Normal file
26
game/state/character_stats_state.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mvvasilev/last_light/engine"
|
||||||
|
"mvvasilev/last_light/game/model"
|
||||||
|
"mvvasilev/last_light/game/systems"
|
||||||
|
"mvvasilev/last_light/game/ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CharacterStatsState struct {
|
||||||
|
window *ui.UIWindow
|
||||||
|
|
||||||
|
player *model.Player
|
||||||
|
}
|
||||||
|
|
||||||
|
func (css *CharacterStatsState) InputContext() systems.InputContext {
|
||||||
|
return systems.InputContext_Menu
|
||||||
|
}
|
||||||
|
|
||||||
|
func (css *CharacterStatsState) OnTick(dt int64) GameState {
|
||||||
|
return css
|
||||||
|
}
|
||||||
|
|
||||||
|
func (css *CharacterStatsState) CollectDrawables() []engine.Drawable {
|
||||||
|
return []engine.Drawable{}
|
||||||
|
}
|
|
@ -60,6 +60,9 @@ func (iss *InventoryScreenState) OnTick(dt int64) (nextState GameState) {
|
||||||
|
|
||||||
if item.Usable() != nil {
|
if item.Usable() != nil {
|
||||||
item.Usable().Use(iss.eventLog, iss.dungeon, iss.player)
|
item.Usable().Use(iss.eventLog, iss.dungeon, iss.player)
|
||||||
|
|
||||||
|
iss.player.Inventory().ReduceQuantityAt(iss.selectedInventorySlot.X(), iss.selectedInventorySlot.Y(), 1)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.Equippable() != nil {
|
if item.Equippable() != nil {
|
||||||
|
@ -70,7 +73,7 @@ func (iss *InventoryScreenState) OnTick(dt int64) (nextState GameState) {
|
||||||
iss.player.Inventory().Equip(item, item.Equippable().Slot)
|
iss.player.Inventory().Equip(item, item.Equippable().Slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
iss.player.Inventory().ReduceQuantityAt(iss.selectedInventorySlot.X(), iss.selectedInventorySlot.Y(), 1)
|
iss.player.Inventory().Drop(iss.selectedInventorySlot.X(), iss.selectedInventorySlot.Y())
|
||||||
|
|
||||||
case systems.InputAction_DropItem:
|
case systems.InputAction_DropItem:
|
||||||
iss.player.Inventory().Drop(iss.selectedInventorySlot.XY())
|
iss.player.Inventory().Drop(iss.selectedInventorySlot.XY())
|
||||||
|
|
|
@ -79,72 +79,21 @@ func (ls *LookState) OnTick(dt int64) GameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *LookState) ShootEquippedWeapon() {
|
func (ls *LookState) ShootEquippedWeapon() {
|
||||||
weapon := ls.player.Inventory().AtSlot(model.EquippedSlotDominantHand)
|
|
||||||
|
|
||||||
if weapon == nil {
|
|
||||||
ls.eventLog.Log("You don't have anything equipped!")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if weapon.Damaging() == nil {
|
|
||||||
ls.eventLog.Log("Item unusable")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
damaging := weapon.Damaging()
|
|
||||||
|
|
||||||
if !damaging.IsRanged {
|
|
||||||
ls.eventLog.Log("Equipped weapon is not ranged!")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Projectiles
|
|
||||||
dX, dY := ls.lookCursorCoordsToDungeonCoords()
|
dX, dY := ls.lookCursorCoordsToDungeonCoords()
|
||||||
cursorPos := engine.PositionAt(dX, dY)
|
cursorPos := engine.PositionAt(dX, dY)
|
||||||
|
|
||||||
distance := cursorPos.Distance(ls.player.Position())
|
success := model.ShootProjectile(
|
||||||
|
ls.player,
|
||||||
if distance > 12 {
|
cursorPos,
|
||||||
ls.eventLog.Log("Can't see in the dark that far")
|
ls.eventLog,
|
||||||
|
ls.dungeon,
|
||||||
return
|
ls.turnSystem,
|
||||||
}
|
|
||||||
|
|
||||||
path := engine.LinePath(
|
|
||||||
ls.player.Position(),
|
|
||||||
engine.PositionAt(dX, dY),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if path == nil {
|
if !success {
|
||||||
ls.eventLog.Log("Can't shoot there, something is in the way")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
direction := ls.player.Position().Diff(cursorPos).Sign()
|
|
||||||
|
|
||||||
sprites := map[engine.Position]model.ArrowSprite{
|
|
||||||
engine.PositionAt(-1, -1): model.ProjectileSprite_NorthWestSouthEast,
|
|
||||||
engine.PositionAt(+1, -1): model.ProjectileSprite_NorthWestSouthEast,
|
|
||||||
engine.PositionAt(-1, +1): model.ProjectileSprite_NorthEastSouthWest,
|
|
||||||
engine.PositionAt(+1, +1): model.ProjectileSprite_NorthEastSouthWest,
|
|
||||||
engine.PositionAt(0, +1): model.ProjectileSprite_NorthSouth,
|
|
||||||
engine.PositionAt(0, -1): model.ProjectileSprite_NorthSouth,
|
|
||||||
engine.PositionAt(-1, 0): model.ProjectileSprite_EastWest,
|
|
||||||
engine.PositionAt(+1, 0): model.ProjectileSprite_EastWest,
|
|
||||||
}
|
|
||||||
|
|
||||||
sprite := sprites[direction]
|
|
||||||
|
|
||||||
projectile := model.Entity_Projectile("Arrow", rune(sprite), tcell.StyleDefault, 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.nextGameState = ls.prevState
|
ls.nextGameState = ls.prevState
|
||||||
|
|
|
@ -101,13 +101,13 @@ func CreatePlayingState(turnSystem *systems.TurnSystem, inputSystem *systems.Inp
|
||||||
entityTable := model.CreateEntityTable()
|
entityTable := model.CreateEntityTable()
|
||||||
|
|
||||||
entityTable.Add(1, func(x, y int) model.Entity {
|
entityTable.Add(1, func(x, y int) model.Entity {
|
||||||
return model.Entity_Imp(x, y, model.HostileNPCBehavior(s.eventLog, s.dungeon, s.player))
|
return model.Entity_Imp(x, y, model.HostileMeleeNPCBehavior(s.eventLog, s.dungeon, s.player))
|
||||||
})
|
})
|
||||||
entityTable.Add(1, func(x, y int) model.Entity {
|
entityTable.Add(1, func(x, y int) model.Entity {
|
||||||
return model.Entity_SkeletalKnight(x, y, model.HostileNPCBehavior(s.eventLog, s.dungeon, s.player))
|
return model.Entity_SkeletalKnight(x, y, model.HostileMeleeNPCBehavior(s.eventLog, s.dungeon, s.player))
|
||||||
})
|
})
|
||||||
entityTable.Add(1, func(x, y int) model.Entity {
|
entityTable.Add(1, func(x, y int) model.Entity {
|
||||||
return model.Entity_SkeletalWarrior(x, y, model.HostileNPCBehavior(s.eventLog, s.dungeon, s.player))
|
return model.Entity_SkeletalWarrior(x, y, model.HostileMeleeNPCBehavior(s.eventLog, s.dungeon, s.player))
|
||||||
})
|
})
|
||||||
|
|
||||||
s.npcs = SpawnNPCs(s.dungeon, 7, entityTable)
|
s.npcs = SpawnNPCs(s.dungeon, 7, entityTable)
|
||||||
|
@ -171,7 +171,7 @@ func (ps *PlayingState) MovePlayer(direction model.Direction) (success bool) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
model.ExecuteAttack(ps.eventLog, ps.player, ent)
|
model.ExecuteAttack(ps.eventLog, ps.player, ent, false)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
1
game/ui/menu/menu_character_stats.go
Normal file
1
game/ui/menu/menu_character_stats.go
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package menu
|
1
game/ui/menu/menu_key_bindings.go
Normal file
1
game/ui/menu/menu_key_bindings.go
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package menu
|
|
@ -148,6 +148,15 @@ func drawEquipmentSlot(screenX, screenY int, item model.Item, highlighted bool,
|
||||||
style = highlightStyle
|
style = highlightStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if item.Quantifiable() != nil {
|
||||||
|
ui.CreateSingleLineUILabel(
|
||||||
|
screenX,
|
||||||
|
screenY,
|
||||||
|
fmt.Sprintf("%03d", item.Quantifiable().CurrentQuantity),
|
||||||
|
style,
|
||||||
|
).Draw(v)
|
||||||
|
}
|
||||||
|
|
||||||
ui.CreateSingleLineUILabel(
|
ui.CreateSingleLineUILabel(
|
||||||
screenX,
|
screenX,
|
||||||
screenY+1,
|
screenY+1,
|
||||||
|
|
Loading…
Add table
Reference in a new issue