v0.1.0 initial commit
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"symph/parameter"
|
||||
)
|
||||
|
||||
// Note represents a single musical note
|
||||
type Note struct {
|
||||
Value Value
|
||||
|
||||
// TODO: more properties to be added
|
||||
}
|
||||
|
||||
// IsEmpty reports whether the Note has no content
|
||||
func (n Note) IsEmpty() bool { return n.Value == ValueNone }
|
||||
|
||||
// IsWall reports whether the Note is a Wall obstacle
|
||||
func (n Note) IsWall() bool { return n.Value == ValueWall }
|
||||
|
||||
// Chord represents a 3x3 grid of notes
|
||||
type Chord struct {
|
||||
Notes [parameter.GamePlayIndexYMax][parameter.GamePlayIndexXMax]Note // 3x3 grid of notes
|
||||
}
|
||||
|
||||
type Song struct {
|
||||
Chords []Chord
|
||||
}
|
||||
|
||||
// PlayerState holds the current playing position and temporal displacement state.
|
||||
// Lane (X) position is persistent; row (Y) displacement expires back to BaseIndexY
|
||||
type PlayerState struct {
|
||||
ResetTime time.Time // When the current row displacement expires (Zero = no reset)
|
||||
|
||||
PlayIndexY int // Current Y coordinate
|
||||
PlayIndexX int // Current X coordinate
|
||||
|
||||
BaseIndexY int // Resting row (returns here after jump/slide expiry)
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Value identifies the content of a Note. The enum is taxonomy only: per-kind
|
||||
// semantics live in valueSpecs, engine behavior in game.resolvers, appearance
|
||||
// in render.valueVisuals. No switch keys on a Value
|
||||
type Value int
|
||||
|
||||
const (
|
||||
ValueNone Value = iota
|
||||
// Blocks
|
||||
ValueWall
|
||||
ValueSpike
|
||||
// Force direction
|
||||
ValueLeft
|
||||
ValueDown
|
||||
ValueUp
|
||||
ValueRight
|
||||
// Power up
|
||||
ValueEnergy
|
||||
ValueBoost
|
||||
ValueShield
|
||||
ValueMagnet
|
||||
// Enemies
|
||||
ValueDrain
|
||||
ValueFortifiedLeft
|
||||
ValueFortifiedDown
|
||||
ValueFortifiedUp
|
||||
ValueFortifiedRight
|
||||
ValueFortifiedAll
|
||||
// Count
|
||||
ValueCount
|
||||
)
|
||||
|
||||
// Polarity classifies a Value's effect on the player. Drives the
|
||||
// tile distance row (nearest help / nearest threat)
|
||||
type Polarity uint8
|
||||
|
||||
const (
|
||||
PolarityNeutral Polarity = iota
|
||||
PolarityPositive
|
||||
PolarityNegative
|
||||
)
|
||||
|
||||
// ValueSpec is the static description of a Value — the single source of truth
|
||||
// for kind identity. Adding a kind is a row in valueSpecs, never a switch edit.
|
||||
// Glyph is both the pattern authoring rune and the fallback display glyph for
|
||||
// frontends limited to the 32..126 atlas; ASCII range and uniqueness are asserted
|
||||
type ValueSpec struct {
|
||||
Name string
|
||||
Glyph rune
|
||||
Polarity Polarity
|
||||
}
|
||||
|
||||
// valueSpecs is indexed by Value. Every kind below ValueCount needs a row
|
||||
var valueSpecs = [ValueCount]ValueSpec{
|
||||
ValueNone: {"none", '.', PolarityNeutral},
|
||||
|
||||
ValueWall: {"wall", '#', PolarityNegative},
|
||||
ValueSpike: {"spike", 'x', PolarityNegative},
|
||||
|
||||
ValueLeft: {"left", '<', PolarityNeutral},
|
||||
ValueDown: {"down", 'v', PolarityNeutral},
|
||||
ValueUp: {"up", '^', PolarityNeutral},
|
||||
ValueRight: {"right", '>', PolarityNeutral},
|
||||
|
||||
ValueEnergy: {"energy", '*', PolarityPositive},
|
||||
ValueBoost: {"boost", 'B', PolarityPositive},
|
||||
ValueShield: {"shield", 'S', PolarityPositive},
|
||||
ValueMagnet: {"magnet", 'M', PolarityPositive},
|
||||
|
||||
ValueDrain: {"drain", '~', PolarityNegative},
|
||||
ValueFortifiedLeft: {"fortified-left", 'L', PolarityNegative},
|
||||
ValueFortifiedDown: {"fortified-down", 'J', PolarityNegative},
|
||||
ValueFortifiedUp: {"fortified-up", 'K', PolarityNegative},
|
||||
ValueFortifiedRight: {"fortified-right", 'R', PolarityNegative},
|
||||
ValueFortifiedAll: {"fortified-all", 'O', PolarityNegative},
|
||||
}
|
||||
|
||||
// glyphValue is the reverse index consumed by the pattern parser
|
||||
var glyphValue map[rune]Value
|
||||
|
||||
// Author errors (missing row, non-ASCII or duplicate glyph) are caught at
|
||||
// process start, matching parseChord
|
||||
func init() {
|
||||
glyphValue = make(map[rune]Value, ValueCount)
|
||||
for v := ValueNone; v < ValueCount; v++ {
|
||||
s := valueSpecs[v]
|
||||
switch {
|
||||
case s.Name == "":
|
||||
panic(fmt.Sprintf("types: Value %d has no spec row", int(v)))
|
||||
case s.Glyph < ' ' || s.Glyph > '~':
|
||||
panic(fmt.Sprintf("types: Value %d glyph outside ASCII 32..126", int(v)))
|
||||
}
|
||||
if _, dup := glyphValue[s.Glyph]; dup {
|
||||
panic(fmt.Sprintf("types: glyph %q assigned twice", string(s.Glyph)))
|
||||
}
|
||||
glyphValue[s.Glyph] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Valid reports whether v is a defined kind
|
||||
func (v Value) Valid() bool { return v >= ValueNone && v < ValueCount }
|
||||
|
||||
// Spec returns the description of v; the None row when v is out of range
|
||||
func (v Value) Spec() ValueSpec {
|
||||
if !v.Valid() {
|
||||
return valueSpecs[ValueNone]
|
||||
}
|
||||
return valueSpecs[v]
|
||||
}
|
||||
|
||||
func (v Value) Polarity() Polarity { return v.Spec().Polarity }
|
||||
func (v Value) Glyph() rune { return v.Spec().Glyph }
|
||||
func (v Value) String() string { return v.Spec().Name }
|
||||
|
||||
// ValueByGlyph resolves an authoring rune to its Value
|
||||
func ValueByGlyph(r rune) (Value, bool) {
|
||||
v, ok := glyphValue[r]
|
||||
return v, ok
|
||||
}
|
||||
Reference in New Issue
Block a user