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
for i, _ := range m.raw.Characters {
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
}
}

View File

@@ -7,6 +7,7 @@ import (
"strings"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/inpututil"
"code.ur.gs/lupine/ordoor/internal/assetstore"
"code.ur.gs/lupine/ordoor/internal/config"
@@ -118,6 +119,14 @@ func (f *Flow) Update(screenX, screenY int) error {
if ebiten.IsKeyPressed(ebiten.KeyDown) {
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 {

View File

@@ -1,5 +1,9 @@
package flow
import (
"code.ur.gs/lupine/ordoor/internal/maps"
)
// TODO: There are Chaos and Ultramarine versions of MainGame. Do we really want
// 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.Briefing)
for i, chr := range out.Characters {
for i, _ := range out.Characters {
chr := &out.Characters[i]
nullTerminate(&chr.Name)
fmt.Printf("Character %v: %s\n", i, chr.String())
}

View File

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

View File

@@ -36,7 +36,7 @@ func (s *Scenario) Update(screenX, screenY int) error {
}
// FIXME: adjust for Z level
s.selectedCell = screenPos.ToISO()
s.highlightedCell = screenPos.ToISO()
return nil
}
@@ -112,7 +112,7 @@ func (s *Scenario) Draw(screen *ebiten.Image) error {
}
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.Translate(-209, -332)
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)
ebitenutil.DebugPrintAt(
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(y1),
)

View File

@@ -1,6 +1,8 @@
package scenario
import (
"log"
"code.ur.gs/lupine/ordoor/internal/maps"
)
@@ -10,8 +12,23 @@ type CellPoint struct {
}
func (s *Scenario) CellAtCursor() (*maps.Cell, CellPoint) {
cell := s.area.Cell(int(s.selectedCell.X), int(s.selectedCell.Y), 0)
return cell, CellPoint{IsoPt: s.selectedCell, Z: 0}
cell := s.area.Cell(int(s.highlightedCell.X), int(s.highlightedCell.Y), 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) {

View File

@@ -5,15 +5,18 @@ import (
"image"
"code.ur.gs/lupine/ordoor/internal/assetstore"
"code.ur.gs/lupine/ordoor/internal/maps"
)
type Scenario struct {
area *assetstore.Map
specials *assetstore.Object
tick int
turn int
selectedCell IsoPt
tick int
turn int
highlightedCell IsoPt
selectedCharacter *maps.Character
// All these must be modified by user actions 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 {
case menus.SubTypeSimpleButton, menus.SubTypeInvokeButton:
_, widget, err = d.buildButton(r.Props())
case menus.SubTypeDoorHotspot1, menus.SubTypeDoorHotspot2, menus.SubTypeDoorHotspot3:
case menus.SubTypeDoorHotspot1, menus.SubTypeDoorHotspot2:
_, widget, err = d.buildDoorHotspot(r.Props())
case menus.SubTypeClickText:
_, widget, err = d.buildClickText(r.Props())
case menus.SubTypeOverlay:
_, widget, err = d.buildOverlay(r.Props())
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
// generate textImg on demand instead of once at start.
if ni.label != nil {
ni.label.text = ""
ni.label.str = ""
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
}
// Paint some text to screen
// Paint some text to screen, possibly settable
type label struct {
align AlignMode
rect image.Rectangle
text string
font *assetstore.Font
locator string
align AlignMode
rect image.Rectangle
font *assetstore.Font
valueImpl
}
// 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
}
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
func (d *Driver) buildOverlay(p *menus.Properties) (*noninteractive, *Widget, error) {
ni, err := d.buildNoninteractive(p)
@@ -127,9 +155,9 @@ func (d *Driver) buildOverlay(p *menus.Properties) (*noninteractive, *Widget, er
fnt := d.menu.Font(p.FontType/10 - 1)
ni.label = &label{
font: fnt,
rect: ni.rect, // We will be centered by default
text: p.Text,
font: fnt,
rect: ni.rect, // We will be centered by default
valueImpl: valueImpl{str: p.Text},
}
} else {
log.Printf("Overlay without text detected in %v", p.Locator)
@@ -253,6 +281,10 @@ func (a *animationHover) setHoverState(value bool) {
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
// the centre of the rect.
//
@@ -260,7 +292,7 @@ func (a *animationHover) setHoverState(value bool) {
func (l *label) pos() image.Point {
pos := l.rect.Min
textRect := l.font.CalculateBounds(l.text)
textRect := l.font.CalculateBounds(l.str)
// Centre the text horizontally
if l.align == AlignModeCentre {
@@ -287,7 +319,7 @@ func (l *label) regions(tick int) []region {
pt := l.pos()
for _, r := range l.text {
for _, r := range l.str {
glyph, err := l.font.Glyph(r)
if err != nil {
log.Printf("FIXME: ignoring misssing glyph %v", r)