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:
42 files changed
+3260
-26
No files matched your search
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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 }
|
||||
|
||||
|
||||
Reference in new issue
Block a user