Get character stats (kind of) displaying in-scenario
This commit is contained in:
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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
|
||||||
|
}
|
||||||
|
@@ -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())
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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),
|
||||||
)
|
)
|
||||||
|
@@ -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) {
|
||||||
|
@@ -5,15 +5,18 @@ 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 {
|
||||||
area *assetstore.Map
|
area *assetstore.Map
|
||||||
specials *assetstore.Object
|
specials *assetstore.Object
|
||||||
|
|
||||||
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?
|
||||||
|
@@ -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:
|
||||||
|
@@ -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]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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 {
|
||||||
align AlignMode
|
locator string
|
||||||
rect image.Rectangle
|
align AlignMode
|
||||||
text string
|
rect image.Rectangle
|
||||||
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)
|
||||||
@@ -127,9 +155,9 @@ func (d *Driver) buildOverlay(p *menus.Properties) (*noninteractive, *Widget, er
|
|||||||
fnt := d.menu.Font(p.FontType/10 - 1)
|
fnt := d.menu.Font(p.FontType/10 - 1)
|
||||||
|
|
||||||
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)
|
||||||
|
Reference in New Issue
Block a user