Get character stats (kind of) displaying in-scenario

This commit is contained in:
2020-06-13 18:11:45 +01:00
parent 3b7cfb6ecc
commit 4d336b9189
11 changed files with 137 additions and 24 deletions

View File

@@ -119,7 +119,7 @@ func (m *Map) CharacterAt(x, y, z int) *maps.Character {
// FIXME: don't iterate // FIXME: don't iterate
for i, _ := range m.raw.Characters { for i, _ := range m.raw.Characters {
chr := &m.raw.Characters[i] chr := &m.raw.Characters[i]
if chr.XPos == x && chr.YPos == y && z == 1 { // FIXME: sort out ZPos if chr.XPos == x && chr.YPos == y && z == 0 { // FIXME: sort out ZPos
return chr return chr
} }
} }

View File

@@ -7,6 +7,7 @@ import (
"strings" "strings"
"github.com/hajimehoshi/ebiten" "github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/inpututil"
"code.ur.gs/lupine/ordoor/internal/assetstore" "code.ur.gs/lupine/ordoor/internal/assetstore"
"code.ur.gs/lupine/ordoor/internal/config" "code.ur.gs/lupine/ordoor/internal/config"
@@ -118,6 +119,14 @@ func (f *Flow) Update(screenX, screenY int) error {
if ebiten.IsKeyPressed(ebiten.KeyDown) { if ebiten.IsKeyPressed(ebiten.KeyDown) {
f.scenario.Viewpoint.Y += step f.scenario.Viewpoint.Y += step
} }
if inpututil.IsMouseButtonJustReleased(ebiten.MouseButtonLeft) {
f.scenario.SelectHighlightedCharacter()
// Now we need to update the info screens with data about the
// selected character. FIXME: oh, for data binding
f.selectedMainGameCharacter(f.scenario.SelectedCharacter())
}
} }
if f.scenario != nil { if f.scenario != nil {

View File

@@ -1,5 +1,9 @@
package flow package flow
import (
"code.ur.gs/lupine/ordoor/internal/maps"
)
// TODO: There are Chaos and Ultramarine versions of MainGame. Do we really want // TODO: There are Chaos and Ultramarine versions of MainGame. Do we really want
// to duplicate everything for both? // to duplicate everything for both?
@@ -154,3 +158,48 @@ func (f *Flow) linkMainGameViewMenu() {
})) }))
} }
func (f *Flow) maybeSetErr(next func() error) {
if f.exit != nil {
return
}
f.exit = next()
}
func (f *Flow) selectedMainGameCharacter(chr *maps.Character) {
if chr == nil {
chr = &maps.Character{}
}
d := f.drivers[mainGame]
// 7.1 Portrait
f.maybeSetErr(func() error { return d.SetValue("7.2", chr.Name) }) // Name
// 7.3 doesn't exit
// 7.4 more button (ignore)
// 7.5 AP icon
// f.maybeSetErr(func() error { return d.SetValueInt("7.6", chr.ActionPoints)}) // AP meter
f.maybeSetErr(func() error { return d.SetValueInt("7.7", chr.ActionPoints) }) // AP value
// 7.8 armor icon
// 7.9 armor meter
f.maybeSetErr(func() error { return d.SetValueInt("7.10", chr.Armor) }) // armor value
// 7.11 health icon
// 7.12 health meter
f.maybeSetErr(func() error { return d.SetValueInt("7.13", chr.Health) }) // health value
// 7.14 action points status bar
// 7.15 armor status bar
// 7.16 health status bar
// 8.1 to 8.10 are hot spots
f.maybeSetErr(func() error { return d.SetValueInt("8.11", chr.ActionPoints) }) // AP
f.maybeSetErr(func() error { return d.SetValueInt("8.12", chr.Health) }) // Health
f.maybeSetErr(func() error { return d.SetValueInt("8.13", chr.Armor) }) // Armor
f.maybeSetErr(func() error { return d.SetValueInt("8.14", chr.BallisticSkill) }) // Ballistic Skill
f.maybeSetErr(func() error { return d.SetValueInt("8.15", chr.WeaponSkill) }) // Weapon Skill
f.maybeSetErr(func() error { return d.SetValueInt("8.16", chr.Strength) }) // Strength
f.maybeSetErr(func() error { return d.SetValueInt("8.17", chr.Toughness) }) // Toughness
// 8.18 Initiative
// 8.19 Attacks
f.maybeSetErr(func() error { return d.SetValueInt("8.20", chr.Leadership) }) // Leadership
}

View File

@@ -319,7 +319,8 @@ func loadMapFile(filename string) (*GameMap, error) {
nullTerminate(&out.Title) nullTerminate(&out.Title)
nullTerminate(&out.Briefing) nullTerminate(&out.Briefing)
for i, chr := range out.Characters { for i, _ := range out.Characters {
chr := &out.Characters[i]
nullTerminate(&chr.Name) nullTerminate(&chr.Name)
fmt.Printf("Character %v: %s\n", i, chr.String()) fmt.Printf("Character %v: %s\n", i, chr.String())
} }

View File

@@ -33,7 +33,7 @@ const (
SubTypeLineBriefing SubMenuType = 41 SubTypeLineBriefing SubMenuType = 41
SubTypeThumb SubMenuType = 45 // A "thumb" appears to be a vertical slider SubTypeThumb SubMenuType = 45 // A "thumb" appears to be a vertical slider
SubTypeInvokeButton SubMenuType = 50 SubTypeInvokeButton SubMenuType = 50
SubTypeDoorHotspot3 SubMenuType = 60 // Maybe? Appears in Arrange.mnu SubTypeClickText SubMenuType = 60
SubTypeOverlay SubMenuType = 61 SubTypeOverlay SubMenuType = 61
SubTypeHypertext SubMenuType = 70 SubTypeHypertext SubMenuType = 70
SubTypeCheckbox SubMenuType = 91 SubTypeCheckbox SubMenuType = 91

View File

@@ -36,7 +36,7 @@ func (s *Scenario) Update(screenX, screenY int) error {
} }
// FIXME: adjust for Z level // FIXME: adjust for Z level
s.selectedCell = screenPos.ToISO() s.highlightedCell = screenPos.ToISO()
return nil return nil
} }
@@ -112,7 +112,7 @@ func (s *Scenario) Draw(screen *ebiten.Image) error {
} }
op := ebiten.DrawImageOptions{} op := ebiten.DrawImageOptions{}
geo := s.geoForCoords(int(s.selectedCell.X), int(s.selectedCell.Y), 0) geo := s.geoForCoords(int(s.highlightedCell.X), int(s.highlightedCell.Y), 0)
op.GeoM = geo op.GeoM = geo
op.GeoM.Translate(-209, -332) op.GeoM.Translate(-209, -332)
op.GeoM.Translate(float64(spr.Rect.Min.X), float64(spr.Rect.Min.Y)) op.GeoM.Translate(float64(spr.Rect.Min.X), float64(spr.Rect.Min.Y))
@@ -125,7 +125,7 @@ func (s *Scenario) Draw(screen *ebiten.Image) error {
x1, y1 := geo.Apply(0, 0) x1, y1 := geo.Apply(0, 0)
ebitenutil.DebugPrintAt( ebitenutil.DebugPrintAt(
screen, screen,
fmt.Sprintf("(%d,%d)", int(s.selectedCell.X), int(s.selectedCell.Y)), fmt.Sprintf("(%d,%d)", int(s.highlightedCell.X), int(s.highlightedCell.Y)),
int(x1), int(x1),
int(y1), int(y1),
) )

View File

@@ -1,6 +1,8 @@
package scenario package scenario
import ( import (
"log"
"code.ur.gs/lupine/ordoor/internal/maps" "code.ur.gs/lupine/ordoor/internal/maps"
) )
@@ -10,8 +12,23 @@ type CellPoint struct {
} }
func (s *Scenario) CellAtCursor() (*maps.Cell, CellPoint) { func (s *Scenario) CellAtCursor() (*maps.Cell, CellPoint) {
cell := s.area.Cell(int(s.selectedCell.X), int(s.selectedCell.Y), 0) cell := s.area.Cell(int(s.highlightedCell.X), int(s.highlightedCell.Y), 0)
return cell, CellPoint{IsoPt: s.selectedCell, Z: 0} return cell, CellPoint{IsoPt: s.highlightedCell, Z: 0}
}
func (s *Scenario) HighlightedCharacter() *maps.Character {
// FIXME: characters are always at zIdx 0 right now
return s.area.CharacterAt(int(s.highlightedCell.X), int(s.highlightedCell.Y), 0)
}
func (s *Scenario) SelectedCharacter() *maps.Character {
return s.selectedCharacter
}
func (s *Scenario) SelectHighlightedCharacter() {
chr := s.HighlightedCharacter()
log.Printf("Selected character %s", chr)
s.selectedCharacter = chr
} }
func (s *Scenario) ChangeZIdx(by int) { func (s *Scenario) ChangeZIdx(by int) {

View File

@@ -5,6 +5,7 @@ import (
"image" "image"
"code.ur.gs/lupine/ordoor/internal/assetstore" "code.ur.gs/lupine/ordoor/internal/assetstore"
"code.ur.gs/lupine/ordoor/internal/maps"
) )
type Scenario struct { type Scenario struct {
@@ -13,7 +14,9 @@ type Scenario struct {
tick int tick int
turn int turn int
selectedCell IsoPt
highlightedCell IsoPt
selectedCharacter *maps.Character
// All these must be modified by user actions somehow. // All these must be modified by user actions somehow.
// TODO: extract into the idea of a viewport passed to Update / Draw somehow? // TODO: extract into the idea of a viewport passed to Update / Draw somehow?

View File

@@ -78,8 +78,10 @@ func (d *Driver) buildRecord(r *menus.Record) (*Widget, error) {
switch r.Type { switch r.Type {
case menus.SubTypeSimpleButton, menus.SubTypeInvokeButton: case menus.SubTypeSimpleButton, menus.SubTypeInvokeButton:
_, widget, err = d.buildButton(r.Props()) _, widget, err = d.buildButton(r.Props())
case menus.SubTypeDoorHotspot1, menus.SubTypeDoorHotspot2, menus.SubTypeDoorHotspot3: case menus.SubTypeDoorHotspot1, menus.SubTypeDoorHotspot2:
_, widget, err = d.buildDoorHotspot(r.Props()) _, widget, err = d.buildDoorHotspot(r.Props())
case menus.SubTypeClickText:
_, widget, err = d.buildClickText(r.Props())
case menus.SubTypeOverlay: case menus.SubTypeOverlay:
_, widget, err = d.buildOverlay(r.Props()) _, widget, err = d.buildOverlay(r.Props())
case menus.SubTypeHypertext: case menus.SubTypeHypertext:

View File

@@ -157,9 +157,9 @@ func (l *listBox) refresh() {
// FIXME: noninteractive isn't set up for dynamic text yet. Need to // FIXME: noninteractive isn't set up for dynamic text yet. Need to
// generate textImg on demand instead of once at start. // generate textImg on demand instead of once at start.
if ni.label != nil { if ni.label != nil {
ni.label.text = "" ni.label.str = ""
if len(l.strings) > l.offset+i { if len(l.strings) > l.offset+i {
ni.label.text = l.strings[l.offset+i] ni.label.str = l.strings[l.offset+i]
} }
} }
} }

View File

@@ -32,12 +32,13 @@ type noninteractive struct {
hoverImpl hoverImpl
} }
// Paint some text to screen // Paint some text to screen, possibly settable
type label struct { type label struct {
locator string
align AlignMode align AlignMode
rect image.Rectangle rect image.Rectangle
text string
font *assetstore.Font font *assetstore.Font
valueImpl
} }
// This particular animation has entry and exit sequences, which are invoked // This particular animation has entry and exit sequences, which are invoked
@@ -109,6 +110,33 @@ func (d *Driver) buildHypertext(p *menus.Properties) (*noninteractive, *Widget,
return ni, widget, nil return ni, widget, nil
} }
func (d *Driver) buildClickText(p *menus.Properties) (*noninteractive, *Widget, error) {
ni, err := d.buildNoninteractive(p)
if err != nil {
return nil, nil, err
}
fnt := d.menu.Font(p.FontType/10 - 1)
// FIXME: is this always right? Seems to make sense for Main.mnu
ni.label = &label{
locator: ni.locator,
font: fnt,
rect: ni.rect, // We will be centered by default
// Starts with no text. The text specified in the menu is hovertext
}
widget := &Widget{
Locator: ni.locator,
Active: p.Active,
ownClickables: []clickable{ni},
ownPaintables: []paintable{ni},
ownValueables: []valueable{ni.label},
}
return ni, widget, nil
}
// An overlay is a static image + some text that needs to be rendered // An overlay is a static image + some text that needs to be rendered
func (d *Driver) buildOverlay(p *menus.Properties) (*noninteractive, *Widget, error) { func (d *Driver) buildOverlay(p *menus.Properties) (*noninteractive, *Widget, error) {
ni, err := d.buildNoninteractive(p) ni, err := d.buildNoninteractive(p)
@@ -129,7 +157,7 @@ func (d *Driver) buildOverlay(p *menus.Properties) (*noninteractive, *Widget, er
ni.label = &label{ ni.label = &label{
font: fnt, font: fnt,
rect: ni.rect, // We will be centered by default rect: ni.rect, // We will be centered by default
text: p.Text, valueImpl: valueImpl{str: p.Text},
} }
} else { } else {
log.Printf("Overlay without text detected in %v", p.Locator) log.Printf("Overlay without text detected in %v", p.Locator)
@@ -253,6 +281,10 @@ func (a *animationHover) setHoverState(value bool) {
a.hoverImpl.setHoverState(value) a.hoverImpl.setHoverState(value)
} }
func (l *label) id() string {
return l.locator
}
// Top-left of where to start drawing the text. We want it to appear to be in // Top-left of where to start drawing the text. We want it to appear to be in
// the centre of the rect. // the centre of the rect.
// //
@@ -260,7 +292,7 @@ func (a *animationHover) setHoverState(value bool) {
func (l *label) pos() image.Point { func (l *label) pos() image.Point {
pos := l.rect.Min pos := l.rect.Min
textRect := l.font.CalculateBounds(l.text) textRect := l.font.CalculateBounds(l.str)
// Centre the text horizontally // Centre the text horizontally
if l.align == AlignModeCentre { if l.align == AlignModeCentre {
@@ -287,7 +319,7 @@ func (l *label) regions(tick int) []region {
pt := l.pos() pt := l.pos()
for _, r := range l.text { for _, r := range l.str {
glyph, err := l.font.Glyph(r) glyph, err := l.font.Glyph(r)
if err != nil { if err != nil {
log.Printf("FIXME: ignoring misssing glyph %v", r) log.Printf("FIXME: ignoring misssing glyph %v", r)