Add game components 246-268; data.DrawSlot, Canvas.TextVeil

Health, mana and experience bars, quarter hearts, lives, a spinning
coin counter, score board, stage timer, minimap with fog, compass,
hotbar, paper-doll equipment, skill tree, skill buttons with cooldown
wipes, status effects, a boss bar, damage numbers, quest log and
tracker, party frames, character sheet, level-up screen and shop.
data exports DrawSlot so every slot looks alike; pixel adds TextVeil
for dissolving text. Messages the font cannot draw are fixed and
TestMessagesHaveGlyphs holds every message to the font.
This commit is contained in:
ZacharyZhang-NY committed 2026-10-12 01:53:20 -04:00
1 parent 596c62e026
commit f48075230f
42 files changed
+3260 -26

No files matched your search

+276
View File
@@ -0,0 +1,276 @@
package gallery
import (
"fmt"
"math/rand/v2"
"time"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/data"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/display"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/game"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/icon"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/input"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
var gameEntries = []Entry{
{246, "HealthBar", "生命条", "game", "HIT and HEAL. The pink trail lags; under a quarter the bar blinks.", demoHealthBar},
{247, "ManaBar", "魔力条", "game", "A spell costs 12 MP. Ticks mark every 10.", demoManaBar},
{248, "ExpBar", "经验条", "game", "Slay slimes for XP. A full bar flashes and the level goes up.", demoExpBar},
{249, "Hearts", "心形血量", "game", "Damage in quarter hearts. A potion mends a whole one.", demoHearts},
{250, "Lives", "剩余命数", "game", "Your face, times lives. 1UP!", demoLives},
{251, "CoinCounter", "金币", "game", "The coin spins; the count ticks up to the new total.", demoCoinCounter},
{252, "ScoreBoard", "计分板", "game", "Beat the HI-SCORE and its label lights up.", demoScoreBoard},
{253, "TimerHUD", "关卡计时", "game", "TIME runs out. Under 30 it turns red and blinks. Hurry!", demoTimerHUD},
{254, "Minimap", "小地图", "game", "Walk with the D-pad; the fog lifts around you.", demoMinimap},
{255, "Compass", "指南针", "game", "Turn the heading; the quest and town markers slide along.", demoCompass},
{256, "Hotbar", "快捷栏", "game", "Press 1–9, scroll, or click. Enter uses the slot.", demoHotbar},
{257, "Equipment", "装备栏", "game", "A paper doll. Arrows walk the slots; Enter opens one.", demoEquipment},
}
type gameDemo struct {
hp, mp, level, xp, lives, coins, score, slot, gear int
hearts, heading float64
deadline time.Time
pos game.Spot
seen [][]bool
dice *rand.Rand
}
func gameState(c *ui.Context) *gameDemo {
now := pixel.K(c).Now()
return ui.Local(c.Root(), demoKey("game"), func() gameDemo {
s := gameDemo{hp: 100, mp: 60, level: 1, lives: 3, coins: 120, hearts: 5, heading: 30, gear: game.Weapon,
deadline: now.Add(100 * time.Second), pos: game.Spot{X: 2, Y: 2}, dice: rand.New(rand.NewPCG(3, 4))}
for range dungeon {
s.seen = append(s.seen, make([]bool, len(dungeon[0])))
}
s.look()
return s
})
}
// small is a small button.
func small(c *ui.Context, text string, disabled bool) bool {
return input.Button(c, text, input.ButtonOptions{Size: input.Small, Disabled: disabled}).Clicked
}
func demoHealthBar(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := gameState(c)
ui.Column(c).Gap(k.D(6)).Children(func() {
game.HealthBar(c, "HP", s.hp, 100, game.HealthBarOptions{Width: 140})
ui.Row(c).Gap(k.D(4)).Children(func() {
if small(c, "HIT", s.hp == 0) {
s.hp = max(0, s.hp-18)
log(fmt.Sprintf("ouch! %d HP", s.hp))
}
if small(c, "HEAL", s.hp == 100) {
s.hp = min(100, s.hp+30)
log(fmt.Sprintf("healed to %d HP", s.hp))
}
})
})
}
func demoManaBar(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := gameState(c)
ui.Column(c).Gap(k.D(6)).Children(func() {
game.ManaBar(c, "MP", s.mp, 60, game.ManaBarOptions{Width: 140})
ui.Row(c).Gap(k.D(4)).Children(func() {
if small(c, "FIREBALL", s.mp < 12) {
s.mp -= 12
log(fmt.Sprintf("fwoosh! %d MP left", s.mp))
}
if small(c, "REST", s.mp == 60) {
s.mp = min(60, s.mp+20)
log("zzz")
}
})
})
}
func demoExpBar(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := gameState(c)
next := s.level * 100
ui.Column(c).Gap(k.D(6)).Children(func() {
game.ExpBar(c, "EXP", s.level, s.xp, next, game.ExpBarOptions{Width: 140})
if small(c, "SLAY A SLIME", false) {
log("+45 XP")
if s.xp += 45; s.xp >= next {
s.level, s.xp = s.level+1, s.xp-next
log(fmt.Sprintf("level up! LV %d", s.level))
}
}
})
}
func demoHearts(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := gameState(c)
ui.Column(c).Gap(k.D(6)).Children(func() {
game.Hearts(c, "Life", s.hearts, 5, game.HeartsOptions{})
ui.Row(c).Gap(k.D(4)).Children(func() {
if small(c, "OUCH", s.hearts == 0) {
s.hearts = max(0, s.hearts-0.75)
log(fmt.Sprintf("%.2f hearts", s.hearts))
}
if small(c, "POTION", s.hearts == 5) {
s.hearts = min(5, s.hearts+1)
log("glug glug")
}
})
})
}
func demoLives(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := gameState(c)
ui.Row(c).Gap(k.D(8)).AlignItems(ui.Center).Children(func() {
game.Lives(c, "Lives", s.lives, game.LivesOptions{})
if small(c, "1UP", false) {
s.lives++
log("1UP!")
}
if small(c, "FALL IN A PIT", s.lives == 0) {
s.lives--
log("aaaaa...")
}
})
}
func demoCoinCounter(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := gameState(c)
ui.Row(c).Gap(k.D(8)).AlignItems(ui.Center).Children(func() {
game.CoinCounter(c, "Gold", s.coins, game.CoinCounterOptions{Scale: 2})
if small(c, "OPEN CHEST", false) {
s.coins += 250
log("+250 gold")
}
})
}
func demoScoreBoard(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := gameState(c)
ui.Column(c).Gap(k.D(6)).Children(func() {
game.ScoreBoard(c, s.score, 9900, game.ScoreBoardOptions{})
ui.Row(c).Gap(k.D(4)).Children(func() {
if small(c, "STOMP", false) {
s.score += 1500
log(fmt.Sprintf("score %d", s.score))
}
if small(c, "RESET", s.score == 0) {
s.score = 0
log("score reset")
}
})
})
}
func demoTimerHUD(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := gameState(c)
left := max(0, s.deadline.Sub(k.Now()))
if left > 0 {
k.Tick(c, 2)
}
ui.Row(c).Gap(k.D(12)).AlignItems(ui.Center).Children(func() {
game.TimerHUD(c, left, game.TimerHUDOptions{})
if small(c, "RESTART", false) {
s.deadline = k.Now().Add(100 * time.Second)
log("go go go!")
}
if small(c, "SKIP TO 33", left <= 33*time.Second) {
s.deadline = k.Now().Add(33 * time.Second)
log("33 seconds left")
}
})
}
// dungeon is the minimap demo's map.
var dungeon = []string{
`############`,
`#....~~....#`,
`#.""...~...#`,
`#.""..###..#`,
`#.....#^^..#`,
`############`,
}
// look lifts the fog two tiles around the player.
func (s *gameDemo) look() {
for y := range dungeon {
for x := range dungeon[y] {
if max(x-s.pos.X, s.pos.X-x, y-s.pos.Y, s.pos.Y-y) <= 2 {
s.seen[y][x] = true
}
}
}
}
func demoMinimap(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := gameState(c)
ui.Row(c).Gap(k.D(10)).AlignItems(ui.Center).Children(func() {
game.Minimap(c, "Dungeon", dungeon, s.seen, s.pos, []game.Marker{{Spot: game.Spot{X: 9, Y: 4}, Slot: pixel.Warn}}, game.MinimapOptions{Cell: 6})
step := map[input.Direction]game.Spot{input.Up: {Y: -1}, input.Down: {Y: 1}, input.Left: {X: -1}, input.Right: {X: 1}}
if d, ok := step[input.DPad(c, "Walk", input.DPadOptions{}).Pressed]; ok {
to := game.Spot{X: s.pos.X + d.X, Y: s.pos.Y + d.Y}
if dungeon[to.Y][to.X] != '#' {
s.pos = to
s.look()
log(fmt.Sprintf("at %d, %d", to.X, to.Y))
}
}
})
}
func demoCompass(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := gameState(c)
ui.Column(c).Gap(k.D(6)).Children(func() {
game.Compass(c, "Heading", s.heading, []game.Bearing{{Degrees: 120, Slot: pixel.Warn}, {Degrees: 300, Slot: pixel.Info}}, game.CompassOptions{Width: 160})
if input.Slider(c, "Heading", &s.heading, input.SliderOptions{Max: 360, Width: 160}).Changed {
log(fmt.Sprintf("%.0f°", s.heading))
}
})
}
// pack is the hotbar demo's items.
var pack = []data.Item{
{Sprite: icon.Sword, Name: "Sword", Rarity: display.Rare}, {Sprite: icon.Shield, Name: "Shield"}, {Sprite: icon.Potion, Name: "Potion", Count: 5},
{Sprite: icon.Bolt, Name: "Bolt scroll", Rarity: display.Epic}, {Sprite: icon.Key, Name: "Key"}, {}, {Sprite: icon.Map, Name: "Map"}, {}, {Sprite: icon.Gem, Name: "Gem", Count: 3},
}
func demoHotbar(c *ui.Context, log func(string)) {
s := gameState(c)
r := game.Hotbar(c, "Belt", pack, &s.slot, game.HotbarOptions{})
if r.Changed {
log(fmt.Sprintf("slot %d", s.slot+1))
}
if r.Used >= 0 {
name := pack[r.Used].Name
if name == "" {
name = "an empty hand"
}
log("used " + name)
}
}
func demoEquipment(c *ui.Context, log func(string)) {
s := gameState(c)
gear := []data.Item{{Sprite: icon.Crown, Name: "Old crown", Rarity: display.Legendary}, {}, {Sprite: icon.Sword, Name: "Iron sword"}, {Sprite: icon.Shield, Name: "Pot lid"}, {}}
r := game.Equipment(c, "Gear", gear, &s.gear, game.EquipmentOptions{})
if r.Changed {
log(fmt.Sprintf("slot %d", s.gear))
}
if r.Picked >= 0 {
log(fmt.Sprintf("open slot %d", r.Picked))
}
}
+229
View File
@@ -0,0 +1,229 @@
package gallery
import (
"fmt"
"time"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/data"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/game"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/icon"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/layout"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
var rpgEntries = []Entry{
{258, "SkillTree", "技能树", "game", "Spend points on open skills: click once to choose, twice to learn.", demoSkillTree},
{259, "SkillButton", "技能按钮", "game", "Cast, then wait out the clockwise cooldown wipe.", demoSkillButton},
{260, "StatusEffects", "状态效果", "game", "Buffs edged green, debuffs red, turns left in the corner.", demoStatusEffects},
{261, "BossBar", "首领血条", "game", "A boss in phases. Hit it until it gets angry.", demoBossBar},
{262, "DamageNumbers", "伤害数字", "game", "Numbers float up and dissolve. Criticals in gold!", demoDamageNumbers},
{263, "QuestLog", "任务日志", "game", "Choose a quest to track; its objectives show beside it.", demoQuestLog},
{264, "QuestTracker", "任务追踪", "game", "Compact objectives with counts. Go slay one.", demoQuestTracker},
{265, "PartyFrames", "队伍面板", "game", "Cards with HP and MP. Brom needs a phoenix down.", demoPartyFrames},
{266, "CharacterSheet", "角色面板", "game", "Who you are: class, level and stats.", demoCharacterSheet},
{267, "LevelUpScreen", "升级分配", "game", "LEVEL UP! Spend points with ◀ ▶, then DONE.", demoLevelUpScreen},
{268, "Shop", "商店", "game", "Buy at the price, sell at half. Too dear? It goes grey.", demoShop},
}
type rpgDemo struct {
skills []game.Skill
cursor, points, boss, quest int
slain int
cast time.Time
effects []game.Effect
hits []game.Hit
attrs []game.Attribute
wares []game.Ware
gold, raise int
}
func rpgState(c *ui.Context) *rpgDemo {
return ui.Local(c.Root(), demoKey("rpg"), func() rpgDemo {
return rpgDemo{skills: talents(), points: 2, boss: 1000, gold: 300, raise: 3,
effects: []game.Effect{{Sprite: icon.Bolt, Name: "Haste", Turns: 3}, {Sprite: icon.Shield, Name: "Guard", Turns: 2}, {Sprite: icon.Skull, Name: "Poison", Turns: 4, Debuff: true}},
attrs: []game.Attribute{{Name: "STR", Base: 12}, {Name: "DEX", Base: 9}, {Name: "INT", Base: 7}, {Name: "LUK", Base: 5}},
wares: []game.Ware{{Item: data.Item{Sprite: icon.Potion, Name: "Potion"}, Price: 30, Owned: 2}, {Item: data.Item{Sprite: icon.Key, Name: "Skeleton key"}, Price: 120},
{Item: data.Item{Sprite: icon.Sword, Name: "Flame sword"}, Price: 900}}}
})
}
// talents is a root, two branches and a capstone.
func talents() []game.Skill {
return []game.Skill{
{Name: "Slash", Sprite: icon.Sword, At: game.Spot{X: 1}, Learned: true},
{Name: "Guard", Sprite: icon.Shield, At: game.Spot{Y: 1}, Needs: []int{0}},
{Name: "Fireball", Sprite: icon.Flame, At: game.Spot{X: 2, Y: 1}, Needs: []int{0}},
{Name: "Bulwark", Sprite: icon.Crown, At: game.Spot{Y: 2}, Needs: []int{1}},
{Name: "Meteor", Sprite: icon.Star, At: game.Spot{X: 2, Y: 2}, Needs: []int{2}},
{Name: "Starfall", Sprite: icon.Sparkle, At: game.Spot{X: 1, Y: 3}, Needs: []int{3, 4}},
}
}
func demoSkillTree(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := rpgState(c)
ui.Column(c).Gap(k.D(6)).Children(func() {
r := game.SkillTree(c, "Talents", s.skills, &s.cursor, &s.points, game.SkillTreeOptions{})
if r.Changed {
log(s.skills[s.cursor].Name)
}
if r.Learned >= 0 {
log("learned " + s.skills[r.Learned].Name)
}
if small(c, "RESPEC", s.points == 2) {
s.skills, s.points = talents(), 2
log("a clean slate")
}
})
}
func demoSkillButton(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := rpgState(c)
// The spell cools for three seconds, in tenths.
cool := 1 - float64(k.Since(c, s.cast, 10, 30))/30
ui.Row(c).Gap(k.D(6)).Children(func() {
if game.SkillButton(c, "Fireball", icon.Flame, "Q", cool, game.SkillButtonOptions{}).Clicked {
s.cast = k.Now()
log("fireball!")
}
if game.SkillButton(c, "Heal", icon.Heart, "E", 0, game.SkillButtonOptions{}).Clicked {
log("you feel better")
}
game.SkillButton(c, "Meteor", icon.Star, "R", 0, game.SkillButtonOptions{Disabled: true})
})
}
func demoStatusEffects(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := rpgState(c)
ui.Row(c).Gap(k.D(8)).AlignItems(ui.Center).Children(func() {
game.StatusEffects(c, "Effects", s.effects, game.StatusEffectsOptions{})
if small(c, "END TURN", len(s.effects) == 0) {
left := s.effects[:0]
for _, ef := range s.effects {
if ef.Turns--; ef.Turns > 0 {
left = append(left, ef)
}
}
s.effects = left
log(fmt.Sprintf("%d effects left", len(s.effects)))
}
})
}
func demoBossBar(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := rpgState(c)
ui.Column(c).Gap(k.D(6)).Children(func() {
game.BossBar(c, "SLIME KING", s.boss, 1000, game.BossBarOptions{Width: 220, Phases: []float64{0.66, 0.33}})
if small(c, "ATTACK", s.boss == 0) {
was := s.boss
s.boss = max(0, s.boss-90)
log(fmt.Sprintf("hit! %d HP left", s.boss))
for _, mark := range []int{660, 330} {
if was > mark && s.boss <= mark {
log("the Slime King wobbles angrily!")
}
}
if s.boss == 0 {
log("victory!")
}
}
})
}
func demoDamageNumbers(c *ui.Context, log func(string)) {
k := pixel.K(c)
s, dice := rpgState(c), gameState(c).dice
ui.Column(c).Gap(k.D(4)).Children(func() {
// The numbers float over a slime having a bad day.
layout.Layers(c, func() {
ui.Box(c).Fill().Draw(func(p *ui.Painter, r ui.Rect) {
g := k.Canvas(p, r)
g.Sprite((g.W-3*slime.W)/2, g.H-3*slime.H, slime, 0, pixel.Ink, 3)
})
}, func() {
game.DamageNumbers(c, "Damage", &s.hits, game.DamageNumbersOptions{Width: 180, Height: 60})
})
if small(c, "ATTACK", false) {
h := game.Hit{Value: 5 + dice.IntN(30), X: 20 + dice.IntN(140), Y: 25 + dice.IntN(25)}
// One hit in five is critical, for double.
if h.Crit = dice.IntN(5) == 0; h.Crit {
h.Value *= 2
}
s.hits = append(s.hits, h)
log(fmt.Sprintf("hit for %d", h.Value))
}
})
}
// adventures are the quest demos' quests.
func adventures(slain int) []game.Quest {
return []game.Quest{
{Title: "Slime trouble", Objectives: []game.Objective{{Text: "Slay slimes", Have: slain, Need: 5}, {Text: "Tell the mayor", Have: slain / 5, Need: 1}}},
{Title: "Lost cat", Objectives: []game.Objective{{Text: "Find Mittens", Have: 1, Need: 1}, {Text: "Bring her home", Need: 1}}},
{Title: "Herb run", Objectives: []game.Objective{{Text: "Pick moonleaf", Have: 2, Need: 6}}},
}
}
func demoQuestLog(c *ui.Context, log func(string)) {
s := rpgState(c)
quests := adventures(s.slain)
if game.QuestLog(c, "Quests", quests, &s.quest, game.QuestLogOptions{}).Changed {
log("tracking " + quests[s.quest].Title)
}
}
func demoQuestTracker(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := rpgState(c)
ui.Column(c).Gap(k.D(6)).Children(func() {
game.QuestTracker(c, adventures(s.slain)[0], game.QuestTrackerOptions{})
if small(c, "SLAY A SLIME", s.slain == 5) {
s.slain++
log(fmt.Sprintf("%d of 5", s.slain))
}
})
}
func demoPartyFrames(c *ui.Context, log func(string)) {
game.PartyFrames(c, "Party", []game.Member{
{Name: "Aria", Portrait: game.Hero, HP: 34, MaxHP: 40, MP: 18, MaxMP: 20},
{Name: "Cyra", Portrait: game.Hero, HP: 9, MaxHP: 30, MP: 31, MaxMP: 40},
{Name: "Brom", Portrait: game.Hero, MaxHP: 50, MP: 4, MaxMP: 10},
}, game.PartyFramesOptions{})
}
func demoCharacterSheet(c *ui.Context, log func(string)) {
game.CharacterSheet(c, game.Character{Name: "Aria", Class: "Knight", Level: 7, Portrait: game.Hero,
Stats: []game.Stat{{Name: "STR", Value: 14}, {Name: "DEX", Value: 9}, {Name: "INT", Value: 7}, {Name: "LUK", Value: 11}}}, game.CharacterSheetOptions{})
}
func demoLevelUpScreen(c *ui.Context, log func(string)) {
s := rpgState(c)
r := game.LevelUpScreen(c, "Level up", s.attrs, &s.raise, game.LevelUpScreenOptions{})
if r.Changed {
log(fmt.Sprintf("%d points left", s.raise))
}
if r.Done {
for i := range s.attrs {
s.attrs[i].Base, s.attrs[i].Added = s.attrs[i].Base+s.attrs[i].Added, 0
}
s.raise = 3
log("stronger! three more points, for the demo")
}
}
func demoShop(c *ui.Context, log func(string)) {
s := rpgState(c)
r := game.Shop(c, "Item shop", s.wares, &s.gold, game.ShopOptions{})
if r.Bought >= 0 {
log("bought " + s.wares[r.Bought].Item.Name)
}
if r.Sold >= 0 {
log("sold " + s.wares[r.Sold].Item.Name)
}
}
+44
View File
@@ -0,0 +1,44 @@
package gallery
import (
"testing"
"github.com/egoist/mygo/ui"
)
func TestGameDemosLogTheirActions(t *testing.T) {
for _, tc := range []struct {
path, label string
key ui.Key
}{
{"/c/248-expbar", "SLAY A SLIME", 0}, {"/c/252-scoreboard", "RESET", 0}, {"/c/253-timerhud", "SKIP TO 33", 0},
{"/c/257-equipment", "Gear", ui.KeyRight}, {"/c/258-skilltree", "Talents", ui.KeyDown}, {"/c/261-bossbar", "ATTACK", 0},
} {
a, tt := open(tc.path)
slug := tc.path[3:]
if tc.label == "RESET" {
click(t, tt, "STOMP")
}
before := len(a.logs[slug])
click(t, tt, tc.label)
// A key moves a control the click has focused.
if tc.key != 0 {
before = len(a.logs[slug])
tt.Key(0, tc.key)
}
if len(a.logs[slug]) <= before {
t.Errorf("%s: %s logged nothing: %q", slug, tc.label, a.logs[slug])
}
}
}
func TestHotbarDemoLogsAnEmptySlotUsed(t *testing.T) {
a, tt := open("/c/256-hotbar")
click(t, tt, "Belt")
tt.Key(0, ui.Key6)
before := len(a.logs["256-hotbar"])
tt.Key(0, ui.KeyEnter)
if len(a.logs["256-hotbar"]) <= before {
t.Fatalf("using empty slot 6 logged nothing: %q", a.logs["256-hotbar"])
}
}
+1 -1
View File
@@ -56,7 +56,7 @@ var Categories = []Category{
{"tabletop", "Tabletop", "桌游", icon.Crown},
}
var entries = slices.Concat(foundationEntries, layoutEntries, textEntries, displayEntries, inputEntries, controlEntries, feedbackEntries, navigationEntries, dataEntries, recordEntries, overlayEntries, popupEntries, datetimeEntries, timeEntries, chartEntries, plotEntries)
var entries = slices.Concat(foundationEntries, layoutEntries, textEntries, displayEntries, inputEntries, controlEntries, feedbackEntries, navigationEntries, dataEntries, recordEntries, overlayEntries, popupEntries, datetimeEntries, timeEntries, chartEntries, plotEntries, gameEntries, rpgEntries)
func Entries() []Entry { return entries }