diff --git a/README.md b/README.md index 7b2e48f..42e1e95 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # Ambition ## About -Ambtion is a fast-paced, text-based multiplayer role-playing game. +Ambition is a fast-paced, text-based multiplayer role-playing game. ## Credits - [Muhammad Nauman Raza](https://github.com/devraza) - Project Manager/Founder, Producer, and Programmer @@ -11,4 +11,4 @@ Ambtion is a fast-paced, text-based multiplayer role-playing game. - [Adam Khan](https://github.com/NightmaresStuff) - Concept/Game Artist and Level Designer ## License -See [LICENSE](LICENSE) for a copy of the Apache License 2.0 +See the [LICENSE](LICENSE) for a copy of the Apache License 2.0 diff --git a/frontend/assets/fonts/embed.go b/frontend/assets/fonts/embed.go new file mode 100644 index 0000000..43a48bc --- /dev/null +++ b/frontend/assets/fonts/embed.go @@ -0,0 +1,13 @@ +package fonts + +import ( + _ "embed" +) + +var ( + //go:embed iosevka-bold.ttf + IosevkaBold_ttf []byte + + //go:embed iosevka-regular.ttf + IosevkaRegular_ttf []byte +) diff --git a/frontend/assets/fonts/iosevka-bold.ttf b/frontend/assets/fonts/iosevka-bold.ttf new file mode 100644 index 0000000..0138ef2 Binary files /dev/null and b/frontend/assets/fonts/iosevka-bold.ttf differ diff --git a/frontend/assets/fonts/iosevka-regular.ttf b/frontend/assets/fonts/iosevka-regular.ttf new file mode 100644 index 0000000..75e83ea Binary files /dev/null and b/frontend/assets/fonts/iosevka-regular.ttf differ diff --git a/frontend/go.mod b/frontend/go.mod index 2f5958c..22fbfc9 100644 --- a/frontend/go.mod +++ b/frontend/go.mod @@ -4,7 +4,9 @@ go 1.20 require ( github.com/ebitenui/ebitenui v0.5.4 + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/hajimehoshi/ebiten/v2 v2.5.5 + golang.org/x/image v0.7.0 ) require ( @@ -12,7 +14,6 @@ require ( github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b // indirect github.com/jezek/xgb v1.1.0 // indirect golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect - golang.org/x/image v0.7.0 // indirect golang.org/x/mobile v0.0.0-20230427221453-e8d11dd0ba41 // indirect golang.org/x/sync v0.2.0 // indirect golang.org/x/sys v0.8.0 // indirect diff --git a/frontend/go.sum b/frontend/go.sum index 59a39b7..1364e7e 100644 --- a/frontend/go.sum +++ b/frontend/go.sum @@ -7,6 +7,7 @@ github.com/ebitenui/ebitenui v0.5.4/go.mod h1:2iyyjninLWNQLZ+pdV/eJ9vRnSs+I+HrzE github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b h1:GgabKamyOYguHqHjSkDACcgoPIz3w0Dis/zJ1wyHHHU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/hajimehoshi/bitmapfont/v2 v2.2.3 h1:jmq/TMNj352V062Tr5e3hAoipkoxCbY1JWTzor0zNps= github.com/hajimehoshi/ebiten/v2 v2.5.5 h1:TJNoZsYJYUyFucwE56QRSgmZ+/cklUt1YrwpQVC5vjs= github.com/hajimehoshi/ebiten/v2 v2.5.5/go.mod h1:mnHSOVysTr/nUZrN1lBTRqhK4NG+T9NR3JsJP2rCppk= diff --git a/frontend/main.go b/frontend/main.go index 520df0c..3ee29e3 100644 --- a/frontend/main.go +++ b/frontend/main.go @@ -11,18 +11,22 @@ import ( // Ebitengine "github.com/hajimehoshi/ebiten/v2" - "github.com/hajimehoshi/ebiten/v2/ebitenutil" + // "github.com/hajimehoshi/ebiten/v2/ebitenutil" ) +// Initialise the test player +var testPlayer = initPlayer() + // Create the `Game` struct type Game struct { - ui UI + ui UI activePlayer Player } +// Define the window width/height const ( - window_width = 640 - window_height = 480 + window_width = 1440 + window_height = 960 ) // Update implements Game @@ -36,11 +40,6 @@ func (g *Game) Update() error { func (g *Game) Draw(screen *ebiten.Image) { // Draw the UI onto the screen g.ui.base.Draw(screen) - - ebitenutil.DebugPrintAt(screen, "health: " + strconv.Itoa(g.activePlayer.health), 0, 0) - ebitenutil.DebugPrintAt(screen, "level: " + strconv.Itoa(g.activePlayer.level), 0, 10) - ebitenutil.DebugPrintAt(screen, "exp: " + strconv.Itoa(int(g.activePlayer.exp)), 0, 20) - ebitenutil.DebugPrintAt(screen, "ambittion: " + strconv.Itoa(int(g.activePlayer.ambition)), 0, 30) } // Layout implements Game @@ -69,8 +68,8 @@ func main() { // Initialise the game game := Game{ // Initialise the UI - ui: uiInit(window_width, window_height), activePlayer: testPlayer, + ui: uiInit(window_width, window_height), } // Log and exit on error diff --git a/frontend/player.go b/frontend/player.go index ea12fd0..8ce9528 100644 --- a/frontend/player.go +++ b/frontend/player.go @@ -1,43 +1,79 @@ package main -import ( - "math/rand" -) +import () +// The player struct type Player struct { - health int - level int - exp float32 - ambition float32 + health int + health_max int + defence int + level int + exp float32 + ambition float32 + ambition_max float32 } -// NOTE(midnadimple): These gates are temporary. We'll decide on real values later -var level_gates = map[int]float32{ - 1: 100.0, - 2: 150.0, - 3: 300.0, -} +// Create the maps for the level/(max) ambition gates +var level_gates = make(map[int]float32) +var level_ambition = make(map[int]float32) // TODO(midnadimple): Move player initialization to server upon login func initPlayer() Player { return Player{ - health: 100, - level: 1, - exp: 0.0, - ambition: (rand.Float32() * 10), // NOTE(midnadimple): In the future this will be affected by player activity + health: 100, + health_max: 100, + defence: 0, + level: 1, + exp: 0.0, + ambition: 0.0, // NOTE(midnadimple): In the future this will be affected by player activity + ambition_max: level_ambition[1], // NOTE(midnadimple): In the future this will be affected by player activity } } +// Formula for XP gain - extremely simple +func gain(basexp float32, modifier float32) float32 { + gain := basexp * modifier + return gain +} + +// Update the player func (p *Player) update() { // TODO(midnadimple): update health upon damage - + + // Auto-generate the level gates + level_gates[1] = 500 + for i := 0; i <= 1000; i++ { + if i >= 2 { + switch { + case i <= 10: + level_gates[i] = level_gates[i-1] * 1.2 + case (i <= 100) && (i > 10): + level_gates[i] = level_gates[i-1] * 1.1 + case (i <= 1000) && (i > 100): + level_gates[i] = level_gates[i-1] * 1.005 + } + } + } + + // Auto-generate maximum ambition gates + level_ambition[1] = 10 + for i := 0; i <= 1000; i++ { + if i >= 2 { + switch { + case i <= 10: + level_ambition[i] = level_ambition[i-1] * 1.1 + case (i <= 100) && (i > 10): + level_ambition[i] = level_ambition[i-1] * 1.05 + case (i <= 1000) && (i > 100): + level_ambition[i] = level_ambition[i-1] * 1.00005 + } + } + } + + // Set the XP to 0 and increase both the level and max ambition on level up if p.exp >= level_gates[p.level] { p.exp = 0.0 p.level += 1 + p.ambition_max = level_ambition[p.level] } - - // NOTE(midnadimple): This formula for exp gain is pretty simple, maybe devraza can think of - // a more practical one - p.exp += 10.0 * (1.0/60.0) * p.ambition - -} \ No newline at end of file +} diff --git a/frontend/ui.go b/frontend/ui.go index b1c7789..8a41142 100644 --- a/frontend/ui.go +++ b/frontend/ui.go @@ -5,45 +5,58 @@ import ( img "image" "image/color" + // Misc. + // "fmt" + // EbitenUI "github.com/ebitenui/ebitenui" "github.com/ebitenui/ebitenui/image" "github.com/ebitenui/ebitenui/widget" + + // Fonts + "github.com/devraza/ambition/assets/fonts" + "github.com/golang/freetype/truetype" + "golang.org/x/image/font" ) +// The UI struct type UI struct { - base ebitenui.UI - colors map[string]color.RGBA + base ebitenui.UI + colors map[string]color.RGBA width, height int } +// The `hazakura` colorscheme (default) +var hazakura = map[string]color.RGBA{ + // The monotone colors + "dark_black": color.RGBA{0x0f, 0x0f, 0x0d, 0xff}, + "black": color.RGBA{0x15, 0x15, 0x17, 0xff}, + "dark_gray": color.RGBA{0x24, 0x24, 0x26, 0xff}, + "gray": color.RGBA{0x27, 0x27, 0x2b, 0xff}, + "light_gray": color.RGBA{0x45, 0x44, 0x49, 0xff}, + "overlay": color.RGBA{0x5c, 0x5c, 0x61, 0xff}, + "subwhite": color.RGBA{0xd9, 0x0, 0xd7, 0xff}, + "white": color.RGBA{0xec, 0xe5, 0xea, 0xff}, + + // Actual* colors + "red": color.RGBA{0xf0, 0x69, 0x69, 0xff}, + "magenta": color.RGBA{0xe8, 0x87, 0xbb, 0xff}, + "purple": color.RGBA{0xa2, 0x92, 0xe8, 0xff}, + "blue": color.RGBA{0x78, 0xb9, 0xc4, 0xff}, + "cyan": color.RGBA{0x7e, 0xe6, 0xae, 0xff}, + "green": color.RGBA{0x91, 0xd6, 0x5c, 0xff}, + "yellow": color.RGBA{0xd9, 0xd5, 0x64, 0xff}, +} + // Function for UI initialization func uiInit(width, height int) UI { var ui UI - // The `hazakura` colorscheme - { - hazakura := make(map[string]color.RGBA) - // The monotone colors - hazakura["dark_black"] = color.RGBA{0x0f, 0x0f, 0x0d, 0xff} - hazakura["black"] = color.RGBA{0x15, 0x15, 0x17, 0xff} - hazakura["dark_gray"] = color.RGBA{0x24, 0x24, 0x26, 0xff} - hazakura["gray"] = color.RGBA{0x27, 0x27, 0x2b, 0xff} - hazakura["light_gray"] = color.RGBA{0x45, 0x44, 0x49, 0xff} - hazakura["overlay"] = color.RGBA{0x5c, 0x5c, 0x61, 0xff} - hazakura["highlight"] = color.RGBA{0xd9, 0x0, 0xd7, 0xff} - hazakura["subwhite"] = color.RGBA{0xec, 0xe5, 0xea, 0xff} - // Actual* colors - hazakura["red"] = color.RGBA{0xf0, 0x69, 0x69, 0xff} - hazakura["magenta"] = color.RGBA{0xe8, 0x87, 0xbb, 0xff} - hazakura["purple"] = color.RGBA{0xa2, 0x92, 0xe8, 0xff} - hazakura["blue"] = color.RGBA{0x78, 0xb9, 0xc4, 0xff} - hazakura["cyan"] = color.RGBA{0x7e, 0xe6, 0xae, 0xff} - hazakura["green"] = color.RGBA{0x91, 0xd6, 0x5c, 0xff} - hazakura["yellow"] = color.RGBA{0xd9, 0xd5, 0x64, 0xff} + // Define the UI colors + ui.colors = hazakura - ui.colors = hazakura - } + // Load the images for the button states + buttonImage, _ := loadButtonImage() // Get the window width/height ui.width = width @@ -55,12 +68,136 @@ func uiInit(width, height int) UI { widget.ContainerOpts.BackgroundImage(image.NewNineSliceColor(ui.colors["dark_black"])), ) + // Make the different faces + headingFace, _ := makeFace(18, fonts.IosevkaBold_ttf) + defaultFace, _ := makeFace(14, fonts.IosevkaRegular_ttf) + + // Create the 'Profile' tab + tabProfile := widget.NewTabBookTab("Profile", + widget.ContainerOpts.BackgroundImage(image.NewNineSliceColor(ui.colors["gray"])), + widget.ContainerOpts.Layout(widget.NewGridLayout( + //Define number of columns in the grid + widget.GridLayoutOpts.Columns(2), + // Specify the Stretch for each row and column. + widget.GridLayoutOpts.Stretch([]bool{false, true}, nil), + // Define the spacing between items in the grid + widget.GridLayoutOpts.Spacing(20, 15), + // Define the padding in the grid + widget.GridLayoutOpts.Padding(widget.NewInsetsSimple(20)), + )), + ) + // Add the player stats as content for the 'profile' tab + makeStatsBars(tabProfile, ui, defaultFace) + + // Create the 'Inventory' tab + tabInventory := widget.NewTabBookTab("Inventory", + widget.ContainerOpts.BackgroundImage(image.NewNineSliceColor(ui.colors["gray"])), + widget.ContainerOpts.Layout(widget.NewAnchorLayout()), + ) + inventoryButton := widget.NewText( + widget.TextOpts.Text("Placeholder", headingFace, ui.colors["white"]), + widget.TextOpts.Position(widget.TextPositionCenter, widget.TextPositionCenter), + widget.TextOpts.WidgetOpts(widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{ + HorizontalPosition: widget.AnchorLayoutPositionCenter, + VerticalPosition: widget.AnchorLayoutPositionCenter, + })), + ) + tabInventory.AddChild(inventoryButton) + + // Create the 'Other' tab + tabOther := widget.NewTabBookTab("Other", + widget.ContainerOpts.BackgroundImage(image.NewNineSliceColor(ui.colors["gray"])), + widget.ContainerOpts.Layout(widget.NewAnchorLayout()), + ) + otherButton := widget.NewText( + widget.TextOpts.Text("Placeholder", headingFace, ui.colors["white"]), + widget.TextOpts.Position(widget.TextPositionCenter, widget.TextPositionCenter), + widget.TextOpts.WidgetOpts(widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{ + HorizontalPosition: widget.AnchorLayoutPositionCenter, + VerticalPosition: widget.AnchorLayoutPositionCenter, + })), + ) + tabOther.AddChild(otherButton) + + // Create the tabbook widget + leftTabs := widget.NewTabBook( + widget.TabBookOpts.TabButtonImage(buttonImage), + widget.TabBookOpts.TabButtonText(headingFace, &widget.ButtonTextColor{Idle: color.White}), + widget.TabBookOpts.TabButtonSpacing(0), + widget.TabBookOpts.ContainerOpts( + widget.ContainerOpts.WidgetOpts(widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{ + StretchHorizontal: true, + StretchVertical: true, + HorizontalPosition: widget.AnchorLayoutPositionCenter, + VerticalPosition: widget.AnchorLayoutPositionCenter, + }), + ), + ), + widget.TabBookOpts.TabButtonOpts( + widget.ButtonOpts.TextPadding(widget.NewInsetsSimple(5)), + widget.ButtonOpts.WidgetOpts(widget.WidgetOpts.MinSize(int(float32(width)/(3.5*3)), 0)), + ), + widget.TabBookOpts.Tabs(tabProfile, tabInventory, tabOther), + ) // Define the left bar container leftBar := widget.NewContainer( widget.ContainerOpts.BackgroundImage(image.NewNineSliceColor(ui.colors["gray"])), + widget.ContainerOpts.Layout(widget.NewAnchorLayout()), ) + // Add the tabbook to the left bar + leftBar.AddChild(leftTabs) + + // Define the contents of the chat window + chatContainer := widget.NewContainer( + widget.ContainerOpts.BackgroundImage(image.NewNineSliceColor(ui.colors["black"])), + widget.ContainerOpts.Layout(widget.NewAnchorLayout()), + ) + chatContainer.AddChild(widget.NewText( + widget.TextOpts.Text("Placeholder", defaultFace, ui.colors["white"]), + widget.TextOpts.WidgetOpts(widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{ + HorizontalPosition: widget.AnchorLayoutPositionCenter, + VerticalPosition: widget.AnchorLayoutPositionCenter, + })), + )) + // Define the titlebar for the window + chatTitleContainer := widget.NewContainer( + // Set the background color of the titlebar + widget.ContainerOpts.BackgroundImage(image.NewNineSliceColor(ui.colors["overlay"])), + widget.ContainerOpts.Layout(widget.NewAnchorLayout()), + ) + chatTitleContainer.AddChild(widget.NewText( + widget.TextOpts.Text("Chat", headingFace, ui.colors["white"]), + widget.TextOpts.WidgetOpts(widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{ + HorizontalPosition: widget.AnchorLayoutPositionCenter, + VerticalPosition: widget.AnchorLayoutPositionCenter, + })), + )) + + // Define the chat window + chat := widget.NewWindow( + // Set the contents of the window + widget.WindowOpts.Contents(chatContainer), + // Set the titlebar for the window + widget.WindowOpts.TitleBar(chatTitleContainer, 25), + //Set the window above everything else and block input elsewhere + widget.WindowOpts.Modal(), + // Set how to close the window. CLICK_OUT will close the window when clicking anywhere + widget.WindowOpts.CloseMode(widget.CLICK_OUT), + // Make the window draggable + widget.WindowOpts.Draggable(), + // Make the window resizeable + widget.WindowOpts.Resizeable(), + // Set the minimum size of the window + widget.WindowOpts.MinSize(int(float32(width)/3.6), int(float32(height)/3.5)), + // Set the maximum size of the window + widget.WindowOpts.MaxSize(width/2, height/2), + ) + // Place the window and add the window to the UI + showWindow(chat, ui, float32(width)-float32(width)/3.6, float32(height)-float32(height)/3.5) + ui.base.AddWindow(chat) + // Set the position and size of the left bar - leftBar.SetLocation(img.Rect(0, 0, int(float64(width)/3.5), height)) + leftBar.SetLocation(img.Rect(0, 0, int(float32(width)/3.5), height)) // Add the left bar to the root container root.AddChild(leftBar) @@ -68,3 +205,190 @@ func uiInit(width, height int) UI { return ui } + +// Set a window's location and open the window +func showWindow(window *widget.Window, ui UI, v float32, h float32) { + // Get the preferred size of the content + x, y := window.Contents.PreferredSize() + // Create a rect with the preferred size of the content + r := img.Rect(0, 0, x, y) + // Use the Add method to move the window to the specified point + r = r.Add(img.Point{int(v), int(h)}) + // Set the windows location to the rect + window.SetLocation(r) + +} + +// Create progressbars for all the player stats +func makeStatsBars(parent *widget.TabBookTab, ui UI, face font.Face) { + // Health + health := widget.NewText( + widget.TextOpts.Text("Health", face, ui.colors["white"]), + widget.TextOpts.WidgetOpts(widget.WidgetOpts.LayoutData(widget.GridLayoutData{ + HorizontalPosition: widget.GridLayoutPositionStart, + })), + ) + health_progressbar := widget.NewProgressBar( + widget.ProgressBarOpts.WidgetOpts( + // Set the required anchor layout data to determine where in the container to place the progressbar + widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{ + HorizontalPosition: widget.AnchorLayoutPositionCenter, + VerticalPosition: widget.AnchorLayoutPositionCenter, + }), + // Set the minimum size for the progressbar. + widget.WidgetOpts.MinSize(200, 20), + ), + widget.ProgressBarOpts.Images( + // Set the track colors + &widget.ProgressBarImage{ + Idle: image.NewNineSliceColor(ui.colors["black"]), + Hover: image.NewNineSliceColor(ui.colors["black"]), + }, + // Set the progress colors + &widget.ProgressBarImage{ + Idle: image.NewNineSliceColor(ui.colors["red"]), + Hover: image.NewNineSliceColor(ui.colors["red"]), + }, + ), + // Set the min, max, and current values for each progressbar + widget.ProgressBarOpts.Values(0, testPlayer.health, testPlayer.health), + ) + parent.AddChild(health) + parent.AddChild(health_progressbar) + + // Defence + defence := widget.NewText( + widget.TextOpts.Text("Defence", face, ui.colors["white"]), + widget.TextOpts.WidgetOpts(widget.WidgetOpts.LayoutData(widget.GridLayoutData{ + HorizontalPosition: widget.GridLayoutPositionStart, + })), + ) + defence_progressbar := widget.NewProgressBar( + widget.ProgressBarOpts.WidgetOpts( + // Set the required anchor layout data to determine where in the container to place the progressbar + widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{ + HorizontalPosition: widget.AnchorLayoutPositionCenter, + VerticalPosition: widget.AnchorLayoutPositionCenter, + }), + // Set the minimum size for the progressbar. + widget.WidgetOpts.MinSize(200, 20), + ), + widget.ProgressBarOpts.Images( + // Set the track colors + &widget.ProgressBarImage{ + Idle: image.NewNineSliceColor(ui.colors["black"]), + Hover: image.NewNineSliceColor(ui.colors["black"]), + }, + // Set the progress colors + &widget.ProgressBarImage{ + Idle: image.NewNineSliceColor(ui.colors["yellow"]), + Hover: image.NewNineSliceColor(ui.colors["yellow"]), + }, + ), + // Set the min, max, and current values for each progressbar + widget.ProgressBarOpts.Values(0, testPlayer.health, testPlayer.health), + ) + parent.AddChild(defence) + parent.AddChild(defence_progressbar) + + // XP/Level + level := widget.NewText( + widget.TextOpts.Text("Level", face, ui.colors["white"]), + widget.TextOpts.WidgetOpts(widget.WidgetOpts.LayoutData(widget.GridLayoutData{ + HorizontalPosition: widget.GridLayoutPositionStart, + })), + ) + level_progressbar := widget.NewProgressBar( + widget.ProgressBarOpts.WidgetOpts( + // Set the required anchor layout data to determine where in the container to place the progressbar + widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{ + HorizontalPosition: widget.AnchorLayoutPositionCenter, + VerticalPosition: widget.AnchorLayoutPositionCenter, + }), + // Set the minimum size for the progressbar. + widget.WidgetOpts.MinSize(200, 20), + ), + widget.ProgressBarOpts.Images( + // Set the track colors + &widget.ProgressBarImage{ + Idle: image.NewNineSliceColor(ui.colors["black"]), + Hover: image.NewNineSliceColor(ui.colors["black"]), + }, + // Set the progress colors + &widget.ProgressBarImage{ + Idle: image.NewNineSliceColor(ui.colors["cyan"]), + Hover: image.NewNineSliceColor(ui.colors["cyan"]), + }, + ), + // Set the min, max, and current values for each progressbar + widget.ProgressBarOpts.Values(0, testPlayer.health, testPlayer.health_max), + ) + parent.AddChild(level) + parent.AddChild(level_progressbar) + + // Ambition + ambition := widget.NewText( + widget.TextOpts.Text("Ambition", face, ui.colors["white"]), + widget.TextOpts.WidgetOpts(widget.WidgetOpts.LayoutData(widget.GridLayoutData{ + HorizontalPosition: widget.GridLayoutPositionStart, + })), + ) + ambition_progressbar := widget.NewProgressBar( + widget.ProgressBarOpts.WidgetOpts( + // Set the required anchor layout data to determine where in the container to place the progressbar + widget.WidgetOpts.LayoutData(widget.AnchorLayoutData{ + HorizontalPosition: widget.AnchorLayoutPositionCenter, + VerticalPosition: widget.AnchorLayoutPositionCenter, + }), + // Set the minimum size for the progressbar. + widget.WidgetOpts.MinSize(200, 20), + ), + widget.ProgressBarOpts.Images( + // Set the track colors + &widget.ProgressBarImage{ + Idle: image.NewNineSliceColor(ui.colors["black"]), + Hover: image.NewNineSliceColor(ui.colors["black"]), + }, + // Set the progress colors + &widget.ProgressBarImage{ + Idle: image.NewNineSliceColor(ui.colors["purple"]), + Hover: image.NewNineSliceColor(ui.colors["purple"]), + }, + ), + // Set the min, max, and current values for each progressbar + widget.ProgressBarOpts.Values(0, testPlayer.health, testPlayer.health), + ) + parent.AddChild(ambition) + parent.AddChild(ambition_progressbar) +} + +// Load a button image +func loadButtonImage() (*widget.ButtonImage, error) { + idle := image.NewNineSliceColor(hazakura["black"]) + hover := image.NewNineSliceColor(hazakura["light_gray"]) + pressed := image.NewNineSliceColor(hazakura["gray"]) + pressedHover := image.NewNineSliceColor(hazakura["gray"]) + disabled := image.NewNineSliceColor(hazakura["overlay"]) + + return &widget.ButtonImage{ + Idle: idle, + Hover: hover, + Pressed: pressed, + PressedHover: pressedHover, + Disabled: disabled, + }, nil +} + +// Function to create a face providing font size and file (from assets) +func makeFace(size float64, fontfile []byte) (font.Face, error) { + ttfFont, err := truetype.Parse(fontfile) + if err != nil { + return nil, err + } + + return truetype.NewFace(ttfFont, &truetype.Options{ + Size: size, + DPI: 72, + Hinting: font.HintingFull, + }), nil +}