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

+23
View File
@@ -268,3 +268,26 @@ func boolInt(b bool) int {
}
return 0
}
func TestTextVeilKeepsDotsBelowTheLevel(t *testing.T) {
plain := canvas(t, 40, 40, func(g pixel.Canvas) { g.Text(2, 2, "HM", pixel.Ink, 2) })
for _, level := range []int{0, 8, 16} {
veil := canvas(t, 40, 40, func(g pixel.Canvas) { g.TextVeil(2, 2, "HM", pixel.Ink, level, 2) })
kept, all := 0, 0
for y := range 40 {
for x := range 40 {
ink, got := plain.at(x, y) == pixel.Ink, veil.at(x, y) == pixel.Ink
if got && (!ink || pixel.Bayer(x, y) >= level) {
t.Fatalf("level %d: dot %d,%d drawn", level, x, y)
}
if ink && pixel.Bayer(x, y) < level && !got {
t.Fatalf("level %d: dot %d,%d missing", level, x, y)
}
kept, all = kept+boolInt(got), all+boolInt(ink)
}
}
if want := all * level / 16; kept < want-all/8 || kept > want+all/8 {
t.Fatalf("level %d kept %d of %d dots", level, kept, all)
}
}
}
+18
View File
@@ -40,3 +40,21 @@ func checkLevel(level int) {
panic(fmt.Sprintf("pixel: dither level %d outside 0–16", level))
}
}
// TextVeil draws s, keeping glyph dots below the dither level.
func (g Canvas) TextVeil(x, y int, s string, ink Slot, level, scale int) {
checkLevel(level)
checkScale(scale)
for _, r := range s {
gl := glyphOf(r)
gx := x + gl.x*scale
for j := range gl.h * scale {
yy := y + gl.top()*scale + j
row := bayer[(g.gy+yy)&3]
runs(gl.w*scale, func(i int) bool {
return gl.on(i/scale, j/scale) && row[(g.gx+gx+i)&3] < level
}, func(a, b int) { g.Rect(gx+a, yy, b-a, 1, ink) })
}
x += gl.adv * scale
}
}
+10 -2
View File
@@ -24,14 +24,18 @@ var messages = map[Locale]map[string]string{
"just_now": "JUST NOW", "min_ago": "%d MIN AGO", "hours_ago": "%d H AGO", "yesterday": "YESTERDAY", "days_ago": "%d DAYS AGO",
"in_min": "IN %d MIN", "in_hours": "IN %d H", "tomorrow": "TOMORROW", "in_days": "IN %d DAYS",
"spring": "SPRING", "summer": "SUMMER", "autumn": "AUTUMN", "winter": "WINTER", "minutes": "MIN", "seconds": "SEC",
"level": "LV %d", "score": "SCORE", "hi_score": "HI-SCORE", "time": "TIME", "compass": "N E S W",
"head": "HEAD", "body": "BODY", "weapon": "WEAPON", "shield": "SHIELD", "ring": "RING",
"points": "POINTS %d", "learned": "LEARNED", "can_learn": "CAN LEARN", "locked": "LOCKED", "ready": "READY",
"buy": "BUY", "sell": "SELL", "done": "DONE", "tracking": "TRACKING",
},
Zh: {
"ok": "确定", "cancel": "取消", "yes": "是", "no": "否", "close": "关闭",
"continue": "继续?", "game_over": "游戏结束", "loading": "读取中",
"search": "搜索", "empty": "空空如也", "minimize": "最小化", "resize": "调整大小",
"scroll_up": "向上滚动", "scroll_down": "向下滚动",
"scroll_up": "向上翻动", "scroll_down": "向下翻动",
"up": "上", "down": "下", "left": "左", "right": "右", "disabled": "已禁用",
"remove": "移除", "online": "在线", "away": "离开", "busy": "忙碌", "offline": "离线",
"remove": "移除", "online": "在线", "away": "离开", "busy": "繁忙", "offline": "离线",
"gold": "金", "silver": "银", "bronze": "铜",
"common": "普通", "uncommon": "优秀", "rare": "稀有", "epic": "史诗", "legendary": "传说",
"hold": "长按确认", "del": "删", "end": "完", "lives": "命数 +30!",
@@ -46,6 +50,10 @@ var messages = map[Locale]map[string]string{
"just_now": "刚刚", "min_ago": "%d 分钟前", "hours_ago": "%d 小时前", "yesterday": "昨天", "days_ago": "%d 天前",
"in_min": "%d 分钟后", "in_hours": "%d 小时后", "tomorrow": "明天", "in_days": "%d 天后",
"spring": "春", "summer": "夏", "autumn": "秋", "winter": "冬", "minutes": "分", "seconds": "秒",
"level": "等级 %d", "score": "得分", "hi_score": "最高分", "time": "时间", "compass": "北 东 南 西",
"head": "头", "body": "身体", "weapon": "武器", "shield": "盾牌", "ring": "戒指",
"points": "点数 %d", "learned": "已学会", "can_learn": "可学习", "locked": "未解锁", "ready": "就绪",
"buy": "购买", "sell": "出售", "done": "完成", "tracking": "追踪中",
},
}
+12
View File
@@ -26,3 +26,15 @@ func TestMessagesCoverBothLocales(t *testing.T) {
}()
Kit{locale: En}.Msg("no_such_key")
}
func TestMessagesHaveGlyphs(t *testing.T) {
for loc, m := range messages {
for key, s := range m {
for _, r := range s {
if _, ok := font12.find(r); !ok && r != ' ' {
t.Errorf("locale %d message %q: the font has no %q", loc, key, r)
}
}
}
}
}