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 }