Files
8bitMyGoUI/account/charactercreator.go
T
ZacharyZhang-NY c745b082a9 Add account components 386-405; Reduce setting, PinInput Cmd+V
Twenty account components: SettingsList, OptionCycler, KeyBindings,
ControllerMap, ProfileCard, CharacterCreator, LoginForm, PasswordGrid,
SessionList, LanguagePicker, PalettePicker, VolumeMixer,
DifficultySelect, AccessibilityOptions, AccountSwitcher, PlayerSelect,
TwoFactor, About, PatchNotes and FeedbackForm.

The components never check credentials, and no secret reaches the log:
LoginForm reports Submitted and the caller counts Failures; each new
failure clears the password, refocuses it, shakes the form and names no
field. TwoFactor builds on input.PinInput and drains an epoch-aligned
period bar. PalettePicker draws each tile in its own palette on a
pixel.Pad grid.

pixel.Settings.Reduce stills motion like the system preference, the
micro font gains '?', display.Face draws avatar faces anywhere, and
input.PinInput pastes on Cmd+V as native hosts send it.
2026-10-12 08:21:39 -04:00

159 lines
3.9 KiB
Go

package account
import (
"log/slog"
"strings"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
// Look is a character's parts and colors, each an index.
type Look struct {
Hair, Eyes, Outfit, HairColor, OutfitColor int
}
// hues color hair and outfits, as "hues" names them.
var hues = []pixel.Slot{pixel.Ink, pixel.Warn, pixel.Bad, pixel.Magic, pixel.Info}
var (
head = pixel.MustSprite(`
................
................
................
.....eeeeee.....
....elllllle....
....elllllle....
....elllllle....
....elllllle....
.....elllle.....
......eeee......
................
................
................
................
................
................
`)
// hair is SPIKY, BOB, BALD; # takes the hair color.
hair = []*pixel.Sprite{pixel.MustSprite(`
....#..#..#.....
....##.##.##....
....########....
...##########...
...##......##...
...#........#...
`), pixel.MustSprite(`
................
................
.....######.....
....########....
...##########...
...##......##...
...##......##...
...##......##...
...##......##...
`), pixel.MustSprite(`
................
`)}
// eyes are DOTS, WIDE, SLEEPY, from the face's fifth column.
eyes = []*pixel.Sprite{pixel.MustSprite(`
.......
.......
.......
.......
.......
.......
..k..k.
`), pixel.MustSprite(`
.......
.......
.......
.......
.......
..k..k.
..k..k.
`), pixel.MustSprite(`
.......
.......
.......
.......
.......
.......
.kk..kk
`)}
// outfits are TUNIC, ROBE, ARMOR; # takes the outfit color.
outfits = []*pixel.Sprite{pixel.MustSprite(`
.....e####e.....
....e######e....
...e########e...
...l########l...
....########....
....##....##....
`), pixel.MustSprite(`
.....e####e.....
....e######e....
...e########e...
...l########l...
...##########...
..############..
`), pixel.MustSprite(`
.....e####e.....
....e#ssss#e....
...e#ssssss#e...
...l#ssssss#l...
....#ssssss#....
....##....##....
`)}
)
type CharacterCreatorOptions struct {
Disabled bool
}
type CharacterCreatorResult struct {
Element *ui.Element
Changed bool
}
// CharacterCreator cycles hair, eyes, outfit and colors; a live avatar.
func CharacterCreator(c *ui.Context, text string, look *Look, o CharacterCreatorOptions) CharacterCreatorResult {
k := pixel.K(c)
styles, looks, wears, hueNames := words(k, "styles"), words(k, "looks"), words(k, "outfits"), words(k, "hues")
var res CharacterCreatorResult
res.Element = ui.Row(c).Gap(k.D(8)).AlignItems(ui.Center).Role(ui.RoleGroup).Label(text).Children(func() {
shown := *look
say := strings.Join([]string{styles[look.Hair], looks[look.Eyes], wears[look.Outfit], hueNames[look.HairColor], hueNames[look.OutfitColor]}, ", ")
ui.Box(c).Size(k.D(56), k.D(56)).Role(ui.RoleImage).Label(say).Draw(func(p *ui.Painter, r ui.Rect) {
g := k.Canvas(p, r)
g.Frame(0, 0, 56, 56, pixel.Sunken, pixel.Well)
g.Sprite(4, 4, head, 0, pixel.Ink, 3)
g.Sprite(4+3*4, 4, eyes[shown.Eyes], 0, pixel.Ink, 3)
g.Sprite(4, 4, hair[shown.Hair], 0, hues[shown.HairColor], 3)
g.Sprite(4, 4+3*10, outfits[shown.Outfit], 0, hues[shown.OutfitColor], 3)
})
ui.Column(c).Gap(k.D(2)).Children(func() {
for _, row := range []struct {
key string
names []string
at *int
}{
{"hair", styles, &look.Hair}, {"eyes", looks, &look.Eyes}, {"outfit", wears, &look.Outfit},
{"hair_color", hueNames, &look.HairColor}, {"outfit_color", hueNames, &look.OutfitColor},
} {
ui.Row(c).Gap(k.D(4)).AlignItems(ui.Center).Children(func() {
mark(c, k, k.Msg(row.key), pixel.Faint, 64)
if OptionCycler(c, k.Msg(row.key), row.names, row.at, OptionCyclerOptions{Width: 48, Disabled: o.Disabled}).Changed {
res.Changed = true
}
})
}
})
})
if res.Changed {
slog.Debug("8bit: character creator", "label", text, "look", *look)
}
return res
}