Create player inventory menu

This commit is contained in:
Miroslav Vasilev 2024-05-06 20:43:35 +03:00
parent b7b269e8b8
commit 5d1416bb07
30 changed files with 460 additions and 300 deletions

View file

@ -1,4 +1,4 @@
package render package engine
import ( import (
"github.com/gdamore/tcell/v2/views" "github.com/gdamore/tcell/v2/views"

View file

@ -1,4 +1,4 @@
package render package engine
import ( import (
"mvvasilev/last_light/util" "mvvasilev/last_light/util"
@ -119,6 +119,10 @@ func (g *Grid) Unhighlight() {
g.isHighlighted = false g.isHighlighted = false
} }
func (g *Grid) Position() util.Position {
return g.position
}
// C###T###T###C // C###T###T###C
// # # # # // # # # #
// # # # # // # # # #

View file

@ -1,4 +1,4 @@
package render package engine
import ( import (
"slices" "slices"

View file

@ -1,4 +1,4 @@
package render package engine
import ( import (
"mvvasilev/last_light/util" "mvvasilev/last_light/util"

View file

@ -1,4 +1,4 @@
package render package engine
import ( import (
"mvvasilev/last_light/util" "mvvasilev/last_light/util"

View file

@ -1,4 +1,4 @@
package render package engine
import ( import (
"errors" "errors"

View file

@ -1,4 +1,4 @@
package render package engine
import ( import (
"mvvasilev/last_light/util" "mvvasilev/last_light/util"

View file

@ -1,4 +1,4 @@
package render package engine
import ( import (
"mvvasilev/last_light/util" "mvvasilev/last_light/util"

View file

@ -1,8 +1,8 @@
package game package game
import ( import (
"mvvasilev/last_light/engine"
"mvvasilev/last_light/game/state" "mvvasilev/last_light/game/state"
"mvvasilev/last_light/render"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
) )
@ -46,6 +46,6 @@ func (g *Game) Tick(dt int64) bool {
return true return true
} }
func (g *Game) CollectDrawables() []render.Drawable { func (g *Game) CollectDrawables() []engine.Drawable {
return g.state.CollectDrawables() return g.state.CollectDrawables()
} }

View file

@ -2,7 +2,7 @@ package game
import ( import (
"log" "log"
"mvvasilev/last_light/render" "mvvasilev/last_light/engine"
"os" "os"
"time" "time"
) )
@ -10,7 +10,7 @@ import (
const TICK_RATE int64 = 50 // tick every 50ms ( 20 ticks per second ) const TICK_RATE int64 = 50 // tick every 50ms ( 20 ticks per second )
type GameContext struct { type GameContext struct {
renderContext *render.RenderContext renderContext *engine.RenderContext
game *Game game *Game
} }
@ -18,7 +18,7 @@ type GameContext struct {
func CreateGameContext() *GameContext { func CreateGameContext() *GameContext {
gc := new(GameContext) gc := new(GameContext)
rc, err := render.CreateRenderContext() rc, err := engine.CreateRenderContext()
if err != nil { if err != nil {
log.Fatalf("%~v", err) log.Fatalf("%~v", err)

View file

@ -2,13 +2,21 @@ package model
import "mvvasilev/last_light/util" import "mvvasilev/last_light/util"
type Inventory struct { type Inventory interface {
Items() []*Item
Shape() util.Size
Push(item Item) bool
Drop(x, y int) *Item
ItemAt(x, y int) *Item
}
type BasicInventory struct {
contents []*Item contents []*Item
shape util.Size shape util.Size
} }
func CreateInventory(shape util.Size) *Inventory { func CreateInventory(shape util.Size) *BasicInventory {
inv := new(Inventory) inv := new(BasicInventory)
inv.contents = make([]*Item, 0, shape.Height()*shape.Width()) inv.contents = make([]*Item, 0, shape.Height()*shape.Width())
inv.shape = shape inv.shape = shape
@ -16,15 +24,15 @@ func CreateInventory(shape util.Size) *Inventory {
return inv return inv
} }
func (i *Inventory) Items() (items []*Item) { func (i *BasicInventory) Items() (items []*Item) {
return i.contents return i.contents
} }
func (i *Inventory) Shape() util.Size { func (i *BasicInventory) Shape() util.Size {
return i.shape return i.shape
} }
func (i *Inventory) Push(item Item) (success bool) { func (i *BasicInventory) Push(item Item) (success bool) {
if len(i.contents) == i.shape.Area() { if len(i.contents) == i.shape.Area() {
return false return false
} }
@ -60,17 +68,21 @@ func (i *Inventory) Push(item Item) (success bool) {
return true return true
} }
func (i *Inventory) Drop(x, y int) { func (i *BasicInventory) Drop(x, y int) *Item {
index := y*i.shape.Width() + x index := y*i.shape.Width() + x
if index > len(i.contents)-1 { if index > len(i.contents)-1 {
return return nil
} }
item := i.contents[index]
i.contents[index] = nil i.contents[index] = nil
return item
} }
func (i *Inventory) ItemAt(x, y int) (item *Item) { func (i *BasicInventory) ItemAt(x, y int) (item *Item) {
index := y*i.shape.Width() + x index := y*i.shape.Width() + x
if index > len(i.contents)-1 { if index > len(i.contents)-1 {

View file

@ -1,126 +1,9 @@
package model package model
import ( import (
"math/rand"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
) )
type ItemType struct {
name string
description string
tileIcon rune
itemIcon string
maxStack int
style tcell.Style
}
func (it *ItemType) Name() string {
return it.name
}
func (it *ItemType) Description() string {
return it.description
}
func (it *ItemType) TileIcon() rune {
return it.tileIcon
}
func (it *ItemType) Icon() string {
return it.itemIcon
}
func (it *ItemType) Style() tcell.Style {
return it.style
}
func (it *ItemType) MaxStack() int {
return it.maxStack
}
func ItemTypeFish() *ItemType {
return &ItemType{
name: "Fish",
description: "What's a fish doing down here?",
tileIcon: '>',
itemIcon: "»o>",
style: tcell.StyleDefault.Foreground(tcell.ColorDarkCyan),
maxStack: 16,
}
}
func ItemTypeGold() *ItemType {
return &ItemType{
name: "Gold",
description: "Not all those who wander are lost",
tileIcon: '¤',
itemIcon: " ¤ ",
style: tcell.StyleDefault.Foreground(tcell.ColorGoldenrod),
maxStack: 255,
}
}
func ItemTypeArrow() *ItemType {
return &ItemType{
name: "Arrow",
description: "Ammunition for a bow",
tileIcon: '-',
itemIcon: "»->",
style: tcell.StyleDefault.Foreground(tcell.ColorGoldenrod),
maxStack: 32,
}
}
func ItemTypeBow() *ItemType {
return &ItemType{
name: "Bow",
description: "To shoot arrows with",
tileIcon: ')',
itemIcon: " |)",
style: tcell.StyleDefault.Foreground(tcell.ColorBrown),
maxStack: 1,
}
}
func ItemTypeLongsword() *ItemType {
return &ItemType{
name: "Longsword",
description: "You know nothing.",
tileIcon: '/',
itemIcon: "╪══",
style: tcell.StyleDefault.Foreground(tcell.ColorSilver),
maxStack: 1,
}
}
func ItemTypeKey() *ItemType {
return &ItemType{
name: "Key",
description: "Indispensable for unlocking things",
tileIcon: '¬',
itemIcon: " o╖",
style: tcell.StyleDefault.Foreground(tcell.ColorDarkGoldenrod),
maxStack: 1,
}
}
type ItemTypeGenTable struct {
}
func GenerateItemType(genTable map[float32]*ItemType) *ItemType {
num := rand.Float32()
for k, v := range genTable {
if num > k {
return v
}
}
return nil
}
type Item struct { type Item struct {
name string name string
description string description string

119
game/model/item_type.go Normal file
View file

@ -0,0 +1,119 @@
package model
import (
"math/rand"
"github.com/gdamore/tcell/v2"
)
type ItemType struct {
name string
description string
tileIcon rune
itemIcon string
maxStack int
style tcell.Style
}
func (it *ItemType) Name() string {
return it.name
}
func (it *ItemType) Description() string {
return it.description
}
func (it *ItemType) TileIcon() rune {
return it.tileIcon
}
func (it *ItemType) Icon() string {
return it.itemIcon
}
func (it *ItemType) Style() tcell.Style {
return it.style
}
func (it *ItemType) MaxStack() int {
return it.maxStack
}
func ItemTypeFish() *ItemType {
return &ItemType{
name: "Fish",
description: "What's a fish doing down here?",
tileIcon: '>',
itemIcon: "»o>",
style: tcell.StyleDefault.Foreground(tcell.ColorDarkCyan),
maxStack: 16,
}
}
func ItemTypeGold() *ItemType {
return &ItemType{
name: "Gold",
description: "Not all those who wander are lost",
tileIcon: '¤',
itemIcon: " ¤ ",
style: tcell.StyleDefault.Foreground(tcell.ColorGoldenrod),
maxStack: 255,
}
}
func ItemTypeArrow() *ItemType {
return &ItemType{
name: "Arrow",
description: "Ammunition for a bow",
tileIcon: '-',
itemIcon: "»->",
style: tcell.StyleDefault.Foreground(tcell.ColorGoldenrod),
maxStack: 32,
}
}
func ItemTypeBow() *ItemType {
return &ItemType{
name: "Bow",
description: "To shoot arrows with",
tileIcon: ')',
itemIcon: " |)",
style: tcell.StyleDefault.Foreground(tcell.ColorBrown),
maxStack: 1,
}
}
func ItemTypeLongsword() *ItemType {
return &ItemType{
name: "Longsword",
description: "You know nothing.",
tileIcon: '/',
itemIcon: "╪══",
style: tcell.StyleDefault.Foreground(tcell.ColorSilver),
maxStack: 1,
}
}
func ItemTypeKey() *ItemType {
return &ItemType{
name: "Key",
description: "Indispensable for unlocking things",
tileIcon: '¬',
itemIcon: " o╖",
style: tcell.StyleDefault.Foreground(tcell.ColorDarkGoldenrod),
maxStack: 1,
}
}
func GenerateItemType(genTable map[float32]*ItemType) *ItemType {
num := rand.Float32()
for k, v := range genTable {
if num > k {
return v
}
}
return nil
}

View file

@ -11,7 +11,7 @@ type Player struct {
id uuid.UUID id uuid.UUID
position util.Position position util.Position
inventory *Inventory inventory *EquippedInventory
} }
func CreatePlayer(x, y int) *Player { func CreatePlayer(x, y int) *Player {
@ -19,7 +19,7 @@ func CreatePlayer(x, y int) *Player {
p.id = uuid.New() p.id = uuid.New()
p.position = util.PositionAt(x, y) p.position = util.PositionAt(x, y)
p.inventory = CreateInventory(util.SizeOf(8, 4)) p.inventory = CreatePlayerInventory()
return p return p
} }
@ -48,7 +48,7 @@ func (p *Player) Transparent() bool {
return false return false
} }
func (p *Player) Inventory() *Inventory { func (p *Player) Inventory() *EquippedInventory {
return p.inventory return p.inventory
} }

View file

@ -0,0 +1,72 @@
package model
import "mvvasilev/last_light/util"
type EquippedSlot int
const (
EquippedSlotOffhand EquippedSlot = iota
EquippedSlotDominantHand
EquippedSlotHead
EquippedSlotChestplate
EquippedSlotLeggings
EquippedSlotShoes
)
type EquippedInventory struct {
offHand *Item
dominantHand *Item
head *Item
chestplate *Item
leggings *Item
shoes *Item
*BasicInventory
}
func CreatePlayerInventory() *EquippedInventory {
return &EquippedInventory{
BasicInventory: CreateInventory(util.SizeOf(8, 4)),
}
}
func (ei *EquippedInventory) AtSlot(slot EquippedSlot) *Item {
switch slot {
case EquippedSlotOffhand:
return ei.offHand
case EquippedSlotDominantHand:
return ei.dominantHand
case EquippedSlotHead:
return ei.head
case EquippedSlotChestplate:
return ei.chestplate
case EquippedSlotLeggings:
return ei.leggings
case EquippedSlotShoes:
return ei.shoes
default:
return nil
}
}
func (ei *EquippedInventory) Equip(item Item, slot EquippedSlot) *Item {
ref := &item
switch slot {
case EquippedSlotOffhand:
ei.offHand = ref
case EquippedSlotDominantHand:
ei.dominantHand = ref
case EquippedSlotHead:
ei.head = ref
case EquippedSlotChestplate:
ei.chestplate = ref
case EquippedSlotLeggings:
ei.leggings = ref
case EquippedSlotShoes:
ei.shoes = ref
}
return ref
}

View file

@ -1,7 +1,7 @@
package state package state
import ( import (
"mvvasilev/last_light/render" "mvvasilev/last_light/engine"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
) )
@ -9,7 +9,7 @@ import (
type GameState interface { type GameState interface {
OnInput(e *tcell.EventKey) OnInput(e *tcell.EventKey)
OnTick(dt int64) GameState OnTick(dt int64) GameState
CollectDrawables() []render.Drawable CollectDrawables() []engine.Drawable
} }
type PausableState interface { type PausableState interface {

View file

@ -1,36 +1,24 @@
package state package state
import ( import (
"fmt" "mvvasilev/last_light/engine"
"mvvasilev/last_light/game/model" "mvvasilev/last_light/game/model"
"mvvasilev/last_light/render" "mvvasilev/last_light/game/ui/menu"
"mvvasilev/last_light/ui"
"mvvasilev/last_light/util" "mvvasilev/last_light/util"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
"github.com/gdamore/tcell/v2/views"
) )
type InventoryScreenState struct { type InventoryScreenState struct {
prevState PausableState prevState PausableState
exitMenu bool exitMenu bool
inventoryMenu *ui.UIWindow inventoryMenu *menu.PlayerInventoryMenu
armourLabel *ui.UILabel selectedInventorySlot util.Position
armourGrid *render.Grid
leftHandLabel *ui.UILabel
leftHandBox render.Rectangle
rightHandLabel *ui.UILabel
rightHandBox render.Rectangle
inventoryGrid *render.Grid
playerItems *render.ArbitraryDrawable
selectedItem *render.ArbitraryDrawable
help *ui.UILabel
player *model.Player player *model.Player
moveInventorySlotDirection model.Direction moveInventorySlotDirection model.Direction
selectedInventorySlot util.Position
dropSelectedInventorySlot bool dropSelectedInventorySlot bool
} }
@ -39,88 +27,9 @@ func CreateInventoryScreenState(player *model.Player, prevState PausableState) *
iss.prevState = prevState iss.prevState = prevState
iss.player = player iss.player = player
iss.exitMenu = false
iss.selectedInventorySlot = util.PositionAt(0, 0) iss.selectedInventorySlot = util.PositionAt(0, 0)
iss.exitMenu = false
iss.inventoryMenu = ui.CreateWindow(43, 0, 37, 24, "INVENTORY", tcell.StyleDefault) iss.inventoryMenu = menu.CreatePlayerInventoryMenu(43, 0, player.Inventory(), tcell.StyleDefault, tcell.StyleDefault.Background(tcell.ColorDarkSlateGray))
iss.armourLabel = ui.CreateSingleLineUILabel(58, 1, "ARMOUR", tcell.StyleDefault)
iss.armourGrid = render.CreateGrid(
53, 2, 3, 1, 4, 1, '┌', '─', '┬', '┐', '│', ' ', '│', '│', '├', '─', '┼', '┤', '└', '─', '┴', '┘', tcell.StyleDefault, tcell.StyleDefault.Background(tcell.ColorDarkSlateGray),
)
iss.leftHandLabel = ui.CreateUILabel(
46, 1, 5, 1, "OFF", tcell.StyleDefault,
)
iss.leftHandBox = render.CreateRectangle(
45, 2, 5, 3,
'┌', '─', '┐',
'│', ' ', '│',
'└', '─', '┘',
false, true,
tcell.StyleDefault,
)
iss.rightHandLabel = ui.CreateUILabel(
74, 1, 5, 1, "DOM", tcell.StyleDefault,
)
iss.rightHandBox = render.CreateRectangle(
73, 2, 5, 3,
'┌', '─', '┐',
'│', ' ', '│',
'└', '─', '┘',
false, true,
tcell.StyleDefault,
)
iss.inventoryGrid = render.CreateGrid(
45, 5, 3, 1, 8, 4, '┌', '─', '┬', '┐', '│', ' ', '│', '│', '├', '─', '┼', '┤', '└', '─', '┴', '┘', tcell.StyleDefault, tcell.StyleDefault.Background(tcell.ColorDarkSlateGray),
)
iss.playerItems = render.CreateDrawingInstructions(func(v views.View) {
for y := range player.Inventory().Shape().Height() {
for x := range player.Inventory().Shape().Width() {
item := player.Inventory().ItemAt(x, y)
isHighlighted := x == iss.selectedInventorySlot.X() && y == iss.selectedInventorySlot.Y()
if item == nil {
if isHighlighted {
ui.CreateSingleLineUILabel(45+1+x*4, 5+1+y*2, " ", tcell.StyleDefault.Background(tcell.ColorDarkSlateGray)).Draw(v)
}
continue
}
style := item.Type().Style()
if isHighlighted {
style = style.Background(tcell.ColorDarkSlateGray)
}
ui.CreateSingleLineUILabel(45+1+x*4, 5+y*2, fmt.Sprintf("%03d", item.Quantity()), style).Draw(v)
ui.CreateSingleLineUILabel(45+1+x*4, 5+1+y*2, item.Type().Icon(), style).Draw(v)
}
}
})
iss.selectedItem = render.CreateDrawingInstructions(func(v views.View) {
ui.CreateWindow(45, 14, 33, 8, "ITEM", tcell.StyleDefault).Draw(v)
item := player.Inventory().ItemAt(iss.selectedInventorySlot.XY())
if item == nil {
return
}
ui.CreateSingleLineUILabel(46, 15, fmt.Sprintf("Name: %v", item.Name()), tcell.StyleDefault).Draw(v)
ui.CreateSingleLineUILabel(46, 16, fmt.Sprintf("Desc: %v", item.Description()), tcell.StyleDefault).Draw(v)
})
iss.help = ui.CreateSingleLineUILabel(45, 22, "hjkl - move, x - drop, e - equip", tcell.StyleDefault)
return iss return iss
} }
@ -190,27 +99,17 @@ func (iss *InventoryScreenState) OnTick(dt int64) GameState {
iss.selectedInventorySlot = iss.selectedInventorySlot.WithOffset(+1, 0) iss.selectedInventorySlot = iss.selectedInventorySlot.WithOffset(+1, 0)
} }
iss.inventoryGrid.Highlight(iss.selectedInventorySlot) iss.inventoryMenu.SelectSlot(iss.selectedInventorySlot.XY())
iss.moveInventorySlotDirection = model.DirectionNone iss.moveInventorySlotDirection = model.DirectionNone
} }
return iss return iss
} }
func (iss *InventoryScreenState) CollectDrawables() []render.Drawable { func (iss *InventoryScreenState) CollectDrawables() []engine.Drawable {
drawables := append( drawables := append(
iss.prevState.CollectDrawables(), iss.prevState.CollectDrawables(),
iss.inventoryMenu, iss.inventoryMenu,
iss.armourLabel,
iss.armourGrid,
iss.leftHandLabel,
iss.leftHandBox,
iss.rightHandLabel,
iss.rightHandBox,
iss.inventoryGrid,
iss.playerItems,
iss.selectedItem,
iss.help,
) )
return drawables return drawables

View file

@ -1,7 +1,7 @@
package state package state
import ( import (
"mvvasilev/last_light/render" "mvvasilev/last_light/engine"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
) )
@ -17,6 +17,6 @@ func (ls *LookState) OnTick(dt int64) GameState {
panic("not implemented") // TODO: Implement panic("not implemented") // TODO: Implement
} }
func (ls *LookState) CollectDrawables() []render.Drawable { func (ls *LookState) CollectDrawables() []engine.Drawable {
panic("not implemented") // TODO: Implement panic("not implemented") // TODO: Implement
} }

View file

@ -1,15 +1,15 @@
package state package state
import ( import (
"mvvasilev/last_light/render" "mvvasilev/last_light/engine"
"mvvasilev/last_light/ui" "mvvasilev/last_light/game/ui"
"mvvasilev/last_light/util" "mvvasilev/last_light/util"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
) )
type MainMenuState struct { type MainMenuState struct {
menuTitle *render.Raw menuTitle *engine.Raw
buttons []*ui.UISimpleButton buttons []*ui.UISimpleButton
currButtonSelected int currButtonSelected int
@ -22,7 +22,7 @@ func NewMainMenuState() *MainMenuState {
highlightStyle := tcell.StyleDefault.Attributes(tcell.AttrBold) highlightStyle := tcell.StyleDefault.Attributes(tcell.AttrBold)
state.menuTitle = render.CreateRawDrawable( state.menuTitle = engine.CreateRawDrawable(
11, 1, tcell.StyleDefault.Attributes(tcell.AttrBold).Foreground(tcell.ColorYellow), 11, 1, tcell.StyleDefault.Attributes(tcell.AttrBold).Foreground(tcell.ColorYellow),
" | | | _) | | ", " | | | _) | | ",
" | _` | __| __| | | _` | __ \\ __|", " | _` | __| __| | | _` | __ \\ __|",
@ -77,8 +77,8 @@ func (mms *MainMenuState) OnTick(dt int64) GameState {
return mms return mms
} }
func (mms *MainMenuState) CollectDrawables() []render.Drawable { func (mms *MainMenuState) CollectDrawables() []engine.Drawable {
arr := make([]render.Drawable, 0) arr := make([]engine.Drawable, 0)
arr = append(arr, mms.menuTitle) arr = append(arr, mms.menuTitle)

View file

@ -1,8 +1,8 @@
package state package state
import ( import (
"mvvasilev/last_light/render" "mvvasilev/last_light/engine"
"mvvasilev/last_light/ui" "mvvasilev/last_light/game/ui"
"mvvasilev/last_light/util" "mvvasilev/last_light/util"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
@ -26,7 +26,7 @@ func PauseGame(prevState PausableState) *PauseGameState {
highlightStyle := tcell.StyleDefault.Attributes(tcell.AttrBold) highlightStyle := tcell.StyleDefault.Attributes(tcell.AttrBold)
s.pauseMenuWindow = ui.CreateWindow(int(render.TERMINAL_SIZE_WIDTH)/2-15, int(render.TERMINAL_SIZE_HEIGHT)/2-7, 30, 14, "PAUSED", tcell.StyleDefault) s.pauseMenuWindow = ui.CreateWindow(int(engine.TERMINAL_SIZE_WIDTH)/2-15, int(engine.TERMINAL_SIZE_HEIGHT)/2-7, 30, 14, "PAUSED", tcell.StyleDefault)
s.buttons = make([]*ui.UISimpleButton, 0) s.buttons = make([]*ui.UISimpleButton, 0)
s.buttons = append( s.buttons = append(
s.buttons, s.buttons,
@ -96,8 +96,8 @@ func (pg *PauseGameState) OnTick(dt int64) GameState {
return pg return pg
} }
func (pg *PauseGameState) CollectDrawables() []render.Drawable { func (pg *PauseGameState) CollectDrawables() []engine.Drawable {
arr := make([]render.Drawable, 0) arr := make([]engine.Drawable, 0)
arr = append(arr, pg.prevState.CollectDrawables()...) arr = append(arr, pg.prevState.CollectDrawables()...)

View file

@ -2,9 +2,9 @@ package state
import ( import (
"math/rand" "math/rand"
"mvvasilev/last_light/engine"
"mvvasilev/last_light/game/model" "mvvasilev/last_light/game/model"
"mvvasilev/last_light/game/world" "mvvasilev/last_light/game/world"
"mvvasilev/last_light/render"
"mvvasilev/last_light/util" "mvvasilev/last_light/util"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
@ -16,7 +16,7 @@ type PlayingState struct {
entityMap *world.EntityMap entityMap *world.EntityMap
level *world.MultilevelMap level *world.MultilevelMap
viewport *render.Viewport viewport *engine.Viewport
movePlayerDirection model.Direction movePlayerDirection model.Direction
pauseGame bool pauseGame bool
@ -51,7 +51,7 @@ func BeginPlayingState() *PlayingState {
s.entityMap.AddEntity(s.player, '@', tcell.StyleDefault) s.entityMap.AddEntity(s.player, '@', tcell.StyleDefault)
s.viewport = render.CreateViewport( s.viewport = engine.CreateViewport(
util.PositionAt(0, 0), util.PositionAt(0, 0),
dungeonLevel.PlayerSpawnPoint(), dungeonLevel.PlayerSpawnPoint(),
util.SizeOf(80, 24), util.SizeOf(80, 24),
@ -207,8 +207,8 @@ func (ps *PlayingState) OnTick(dt int64) GameState {
return ps return ps
} }
func (ps *PlayingState) CollectDrawables() []render.Drawable { func (ps *PlayingState) CollectDrawables() []engine.Drawable {
return render.Multidraw(render.CreateDrawingInstructions(func(v views.View) { return engine.Multidraw(engine.CreateDrawingInstructions(func(v views.View) {
ps.viewport.DrawFromProvider(v, func(x, y int) (rune, tcell.Style) { ps.viewport.DrawFromProvider(v, func(x, y int) (rune, tcell.Style) {
tile := ps.level.TileAt(x, y) tile := ps.level.TileAt(x, y)

View file

@ -1,7 +1,7 @@
package state package state
import ( import (
"mvvasilev/last_light/render" "mvvasilev/last_light/engine"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
) )
@ -17,6 +17,6 @@ func (q *QuitState) OnTick(dt int64) GameState {
return q return q
} }
func (q *QuitState) CollectDrawables() []render.Drawable { func (q *QuitState) CollectDrawables() []engine.Drawable {
return render.Multidraw(nil) return engine.Multidraw(nil)
} }

View file

@ -1,7 +1,7 @@
package ui package ui
import ( import (
"mvvasilev/last_light/render" "mvvasilev/last_light/engine"
"mvvasilev/last_light/util" "mvvasilev/last_light/util"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
@ -12,8 +12,8 @@ import (
type UIBorderedButton struct { type UIBorderedButton struct {
id uuid.UUID id uuid.UUID
text render.Text text engine.Text
border render.Rectangle border engine.Rectangle
isSelected bool isSelected bool

View file

@ -1,7 +1,7 @@
package ui package ui
import ( import (
"mvvasilev/last_light/render" "mvvasilev/last_light/engine"
"mvvasilev/last_light/util" "mvvasilev/last_light/util"
"unicode/utf8" "unicode/utf8"
@ -12,14 +12,14 @@ import (
type UILabel struct { type UILabel struct {
id uuid.UUID id uuid.UUID
text *render.Text text *engine.Text
} }
func CreateUILabel(x, y int, width, height int, content string, style tcell.Style) *UILabel { func CreateUILabel(x, y int, width, height int, content string, style tcell.Style) *UILabel {
label := new(UILabel) label := new(UILabel)
label.id = uuid.New() label.id = uuid.New()
label.text = render.CreateText(x, y, width, height, content, style) label.text = engine.CreateText(x, y, width, height, content, style)
return label return label
} }
@ -28,7 +28,7 @@ func CreateSingleLineUILabel(x, y int, content string, style tcell.Style) *UILab
label := new(UILabel) label := new(UILabel)
label.id = uuid.New() label.id = uuid.New()
label.text = render.CreateText(x, y, int(utf8.RuneCountInString(content)), 1, content, style) label.text = engine.CreateText(x, y, int(utf8.RuneCountInString(content)), 1, content, style)
return label return label
} }
@ -38,7 +38,7 @@ func (t *UILabel) UniqueId() uuid.UUID {
} }
func (t *UILabel) MoveTo(x int, y int) { func (t *UILabel) MoveTo(x int, y int) {
t.text = render.CreateText(x, y, int(t.text.Size().Width()), int(t.Size().Height()), t.text.Content(), t.text.Style()) t.text = engine.CreateText(x, y, int(t.text.Size().Width()), int(t.Size().Height()), t.text.Content(), t.text.Style())
} }
func (t *UILabel) Position() util.Position { func (t *UILabel) Position() util.Position {

View file

@ -0,0 +1,175 @@
package menu
import (
"fmt"
"mvvasilev/last_light/engine"
"mvvasilev/last_light/game/model"
"mvvasilev/last_light/game/ui"
"mvvasilev/last_light/util"
"github.com/gdamore/tcell/v2"
"github.com/gdamore/tcell/v2/views"
"github.com/google/uuid"
)
type PlayerInventoryMenu struct {
inventory *model.EquippedInventory
inventoryMenu *ui.UIWindow
armourLabel *ui.UILabel
armourGrid *engine.Grid
leftHandLabel *ui.UILabel
leftHandBox engine.Rectangle
rightHandLabel *ui.UILabel
rightHandBox engine.Rectangle
inventoryGrid *engine.Grid
playerItems *engine.ArbitraryDrawable
selectedItem *engine.ArbitraryDrawable
help *ui.UILabel
selectedInventorySlot util.Position
}
func CreatePlayerInventoryMenu(x, y int, playerInventory *model.EquippedInventory, style tcell.Style, highlightStyle tcell.Style) *PlayerInventoryMenu {
menu := new(PlayerInventoryMenu)
menu.inventory = playerInventory
menu.inventoryMenu = ui.CreateWindow(x, y, 37, 24, "INVENTORY", style)
menu.armourLabel = ui.CreateSingleLineUILabel(x+15, y+1, "ARMOUR", style)
menu.armourGrid = engine.CreateGrid(
x+10, y+2, 3, 1, 4, 1, '┌', '─', '┬', '┐', '│', ' ', '│', '│', '├', '─', '┼', '┤', '└', '─', '┴', '┘', style, highlightStyle, //style.Background(tcell.ColorDarkSlateGray),
)
menu.leftHandLabel = ui.CreateUILabel(
x+3, y+1, 5, 1, "OFF", style,
)
menu.leftHandBox = engine.CreateRectangle(
x+2, y+2, 5, 3,
'┌', '─', '┐',
'│', ' ', '│',
'└', '─', '┘',
false, true,
style,
)
menu.rightHandLabel = ui.CreateUILabel(
x+31, y+1, 5, 1, "DOM", style,
)
menu.rightHandBox = engine.CreateRectangle(
x+30, y+2, 5, 3,
'┌', '─', '┐',
'│', ' ', '│',
'└', '─', '┘',
false, true,
style,
)
menu.inventoryGrid = engine.CreateGrid(
x+2, y+5, 3, 1, 8, 4, '┌', '─', '┬', '┐', '│', ' ', '│', '│', '├', '─', '┼', '┤', '└', '─', '┴', '┘', style, highlightStyle,
)
menu.playerItems = engine.CreateDrawingInstructions(func(v views.View) {
for y := range playerInventory.Shape().Height() {
for x := range playerInventory.Shape().Width() {
item := playerInventory.ItemAt(x, y)
isHighlighted := x == menu.selectedInventorySlot.X() && y == menu.selectedInventorySlot.Y()
if item == nil {
if isHighlighted {
ui.CreateSingleLineUILabel(
menu.inventoryGrid.Position().X()+1+x*4,
menu.inventoryGrid.Position().Y()+1+y*2,
" ",
highlightStyle,
).Draw(v)
}
continue
}
style := item.Type().Style()
if isHighlighted {
style = highlightStyle
}
ui.CreateSingleLineUILabel(
menu.inventoryGrid.Position().X()+1+x*4,
menu.inventoryGrid.Position().Y()+y*2,
fmt.Sprintf("%03d", item.Quantity()),
style,
).Draw(v)
ui.CreateSingleLineUILabel(
menu.inventoryGrid.Position().X()+1+x*4,
menu.inventoryGrid.Position().Y()+1+y*2,
item.Type().Icon(),
style,
).Draw(v)
}
}
})
menu.selectedItem = engine.CreateDrawingInstructions(func(v views.View) {
ui.CreateWindow(x+2, y+14, 33, 8, "ITEM", style).Draw(v)
item := playerInventory.ItemAt(menu.selectedInventorySlot.XY())
if item == nil {
return
}
ui.CreateSingleLineUILabel(x+3, y+15, fmt.Sprintf("Name: %v", item.Name()), style).Draw(v)
ui.CreateSingleLineUILabel(x+3, y+16, fmt.Sprintf("Desc: %v", item.Description()), style).Draw(v)
})
menu.help = ui.CreateSingleLineUILabel(x+2, y+22, "hjkl - move, x - drop, e - equip", style)
return menu
}
func (pim *PlayerInventoryMenu) MoveTo(x int, y int) {
}
func (pim *PlayerInventoryMenu) Position() util.Position {
return pim.inventoryMenu.Position()
}
func (pim *PlayerInventoryMenu) Size() util.Size {
return pim.inventoryMenu.Size()
}
func (pim *PlayerInventoryMenu) Input(e *tcell.EventKey) {
}
func (pim *PlayerInventoryMenu) UniqueId() uuid.UUID {
return pim.inventoryMenu.UniqueId()
}
func (pim *PlayerInventoryMenu) SelectSlot(x, y int) {
pim.inventoryGrid.Unhighlight()
pim.selectedInventorySlot = util.PositionAt(x, y)
pim.inventoryGrid.Highlight(pim.selectedInventorySlot)
}
func (pim *PlayerInventoryMenu) Draw(v views.View) {
pim.inventoryMenu.Draw(v)
pim.armourLabel.Draw(v)
pim.armourGrid.Draw(v)
pim.leftHandLabel.Draw(v)
pim.leftHandBox.Draw(v)
pim.rightHandLabel.Draw(v)
pim.rightHandBox.Draw(v)
pim.inventoryGrid.Draw(v)
pim.playerItems.Draw(v)
pim.selectedItem.Draw(v)
pim.help.Draw(v)
}

View file

@ -1,7 +1,7 @@
package ui package ui
import ( import (
"mvvasilev/last_light/render" "mvvasilev/last_light/engine"
"mvvasilev/last_light/util" "mvvasilev/last_light/util"
"strings" "strings"
"unicode/utf8" "unicode/utf8"
@ -14,7 +14,7 @@ import (
type UISimpleButton struct { type UISimpleButton struct {
id uuid.UUID id uuid.UUID
isHighlighted bool isHighlighted bool
text *render.Text text *engine.Text
selectHandler func() selectHandler func()
unhighlightedStyle tcell.Style unhighlightedStyle tcell.Style
highlightedStyle tcell.Style highlightedStyle tcell.Style
@ -24,7 +24,7 @@ func CreateSimpleButton(x, y int, text string, unhighlightedStyle, highlightedSt
sb := new(UISimpleButton) sb := new(UISimpleButton)
sb.id = uuid.New() sb.id = uuid.New()
sb.text = render.CreateText(x, y, int(utf8.RuneCountInString(text)), 1, text, unhighlightedStyle) sb.text = engine.CreateText(x, y, int(utf8.RuneCountInString(text)), 1, text, unhighlightedStyle)
sb.isHighlighted = false sb.isHighlighted = false
sb.selectHandler = onSelect sb.selectHandler = onSelect
sb.highlightedStyle = highlightedStyle sb.highlightedStyle = highlightedStyle
@ -50,7 +50,7 @@ func (sb *UISimpleButton) Highlight() {
newContent := "[ " + sb.text.Content() + " ]" newContent := "[ " + sb.text.Content() + " ]"
sb.text = render.CreateText( sb.text = engine.CreateText(
int(sb.Position().X()-2), int(sb.Position().Y()), int(sb.Position().X()-2), int(sb.Position().Y()),
int(utf8.RuneCountInString(newContent)), 1, int(utf8.RuneCountInString(newContent)), 1,
newContent, newContent,
@ -65,7 +65,7 @@ func (sb *UISimpleButton) Unhighlight() {
content = strings.Trim(content, "[ ") content = strings.Trim(content, "[ ")
contentLen := utf8.RuneCountInString(content) contentLen := utf8.RuneCountInString(content)
sb.text = render.CreateText( sb.text = engine.CreateText(
int(sb.Position().X()+2), int(sb.Position().Y()), int(sb.Position().X()+2), int(sb.Position().Y()),
int(contentLen), 1, int(contentLen), 1,
content, content,
@ -82,7 +82,7 @@ func (sb *UISimpleButton) UniqueId() uuid.UUID {
} }
func (sb *UISimpleButton) MoveTo(x int, y int) { func (sb *UISimpleButton) MoveTo(x int, y int) {
sb.text = render.CreateText(x, y, int(utf8.RuneCountInString(sb.text.Content())), 1, sb.text.Content(), sb.highlightedStyle) sb.text = engine.CreateText(x, y, int(utf8.RuneCountInString(sb.text.Content())), 1, sb.text.Content(), sb.highlightedStyle)
} }
func (sb *UISimpleButton) Position() util.Position { func (sb *UISimpleButton) Position() util.Position {

View file

@ -1,7 +1,7 @@
package ui package ui
import ( import (
"mvvasilev/last_light/render" "mvvasilev/last_light/engine"
"mvvasilev/last_light/util" "mvvasilev/last_light/util"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
@ -13,7 +13,7 @@ type UIElement interface {
Size() util.Size Size() util.Size
Input(e *tcell.EventKey) Input(e *tcell.EventKey)
render.Drawable engine.Drawable
} }
type UIHighlightableElement interface { type UIHighlightableElement interface {

View file

@ -1,7 +1,7 @@
package ui package ui
import ( import (
"mvvasilev/last_light/render" "mvvasilev/last_light/engine"
"mvvasilev/last_light/util" "mvvasilev/last_light/util"
"unicode/utf8" "unicode/utf8"
@ -13,8 +13,8 @@ import (
type UIWindow struct { type UIWindow struct {
id uuid.UUID id uuid.UUID
title *render.Text title *engine.Text
box render.Rectangle box engine.Rectangle
} }
func CreateWindow(x, y, width, height int, title string, style tcell.Style) *UIWindow { func CreateWindow(x, y, width, height int, title string, style tcell.Style) *UIWindow {
@ -24,9 +24,9 @@ func CreateWindow(x, y, width, height int, title string, style tcell.Style) *UIW
titlePos := (width / 2) - int(titleLen/2) titlePos := (width / 2) - int(titleLen/2)
w.title = render.CreateText(x+titlePos, y, int(titleLen), 1, title, style) w.title = engine.CreateText(x+titlePos, y, int(titleLen), 1, title, style)
w.box = render.CreateRectangle( w.box = engine.CreateRectangle(
x, y, width, height, x, y, width, height,
'┌', '─', '┐', '┌', '─', '┐',
'│', ' ', '│', '│', ' ', '│',

View file

@ -1,15 +1,11 @@
package world package world
import "mvvasilev/last_light/game/model"
type dungeonLevel struct { type dungeonLevel struct {
groundLevel Map groundLevel *Map
entityLevel *EntityMap entityLevel *EntityMap
itemLevel *Map itemLevel *Map
} }
type Dungeon struct { type Dungeon struct {
player *model.Player
levels []*dungeonLevel levels []*dungeonLevel
} }