Add initial tutorial
This commit is contained in:
parent
46c9a1e300
commit
5dd5219b5a
8
flags.go
8
flags.go
|
@ -12,12 +12,18 @@ import (
|
|||
func parseFlags() {
|
||||
var (
|
||||
skipIntro bool
|
||||
skipName bool
|
||||
)
|
||||
flag.BoolVar(&world.Fullscreen, "fullscreen", true, "run in fullscreen mode")
|
||||
flag.BoolVar(&skipIntro, "skip-intro", false, "skip intro screen")
|
||||
flag.BoolVar(&skipName, "skip-name", false, "skip name input screen")
|
||||
flag.Parse()
|
||||
|
||||
if skipIntro {
|
||||
if skipIntro || skipName {
|
||||
world.StartGame()
|
||||
if skipName {
|
||||
world.Name = world.NameInput
|
||||
world.NameInput = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,6 +61,8 @@ func NewGame() *Game {
|
|||
entity.NewOnceEntity()
|
||||
gohan.AddSystem(&system.NewEmployee{})
|
||||
gohan.AddSystem(&system.RenderBackground{})
|
||||
gohan.AddSystem(&system.Customer{})
|
||||
gohan.AddSystem(&system.Tutorial{})
|
||||
gohan.AddSystem(&system.RenderUI{})
|
||||
|
||||
s, err := ebiten.NewShader([]byte(shaderSrc))
|
||||
|
@ -128,7 +130,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// NewEmployee target on screen using dither shader.
|
||||
// Customer target on screen using dither shader.
|
||||
op := &ebiten.DrawRectShaderOptions{}
|
||||
op.Images[0] = g.target
|
||||
screen.DrawRectShader(world.ScreenWidth, world.ScreenHeight, g.shader, op)
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"code.rocketnine.space/tslocum/gas-station-sim/asset"
|
||||
|
||||
"code.rocketnine.space/tslocum/gas-station-sim/world"
|
||||
"code.rocketnine.space/tslocum/gohan"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
type Customer struct {
|
||||
}
|
||||
|
||||
func (r *Customer) Update(_ gohan.Entity) error {
|
||||
if !world.TutorialIntroFinished {
|
||||
return nil
|
||||
}
|
||||
|
||||
if world.CustomerTicks == 0 {
|
||||
world.CustomerGenderMale = world.Rand.Intn(2) == 0
|
||||
|
||||
asset.SoundChime.Rewind()
|
||||
asset.SoundChime.Play()
|
||||
}
|
||||
world.CustomerTicks++
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Customer) Draw(_ gohan.Entity, screen *ebiten.Image) error {
|
||||
if !world.TutorialIntroFinished {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -2,24 +2,16 @@ package system
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
"code.rocketnine.space/tslocum/gas-station-sim/world"
|
||||
"code.rocketnine.space/tslocum/gohan"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||
)
|
||||
|
||||
type RenderUI struct {
|
||||
img *ebiten.Image
|
||||
}
|
||||
|
||||
func (r *RenderUI) Update(_ gohan.Entity) error {
|
||||
if r.img == nil {
|
||||
r.img = ebiten.NewImage(world.ScreenWidth, world.ScreenHeight)
|
||||
}
|
||||
|
||||
if !world.GameStarted {
|
||||
return nil
|
||||
}
|
||||
|
@ -37,33 +29,11 @@ func (r *RenderUI) Draw(_ gohan.Entity, screen *ebiten.Image) error {
|
|||
|
||||
if world.Name == "" {
|
||||
if world.Ticks > 6 {
|
||||
r.drawText(screen, 2, 32, 64, true, "What is your name?")
|
||||
world.DrawText(screen, 2, 32, 64, true, "What is your name?")
|
||||
if world.Ticks > 8 {
|
||||
r.drawText(screen, 2, 64, 128, true, fmt.Sprintf("%s_", world.NameInput))
|
||||
world.DrawText(screen, 2, 64, 128, true, fmt.Sprintf("%s_", world.NameInput))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RenderUI) drawText(screen *ebiten.Image, scale int, x int, y int, fill bool, text string) {
|
||||
const (
|
||||
paddingW = 6
|
||||
paddingH = 4
|
||||
)
|
||||
|
||||
l := len(text)
|
||||
textW, textH := 6*l+paddingW, 12+paddingH
|
||||
|
||||
rect := image.Rect(x, y, x+textW*scale, y+textH*scale)
|
||||
screen.SubImage(rect).(*ebiten.Image).Fill(color.White)
|
||||
screen.SubImage(rect.Inset(1)).(*ebiten.Image).Fill(color.Black)
|
||||
|
||||
r.img.Clear()
|
||||
ebitenutil.DebugPrintAt(r.img, text, 2+x/scale, y/scale)
|
||||
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Scale(float64(scale), float64(scale))
|
||||
//op.ColorM.ChangeHSV(0, 0, 0.8)
|
||||
screen.DrawImage(r.img, op)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"code.rocketnine.space/tslocum/gas-station-sim/world"
|
||||
"code.rocketnine.space/tslocum/gohan"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
)
|
||||
|
||||
var hireReasons = []string{
|
||||
"brought a pen",
|
||||
"didn't bring a pen",
|
||||
"laughed at our jokes",
|
||||
"didn't laugh at our jokes",
|
||||
"wore a plain, boring tie",
|
||||
"wore an oversized tie",
|
||||
"have the same uncle as me",
|
||||
"got married to my sister",
|
||||
"still have most of your fingers",
|
||||
"played the harmonica",
|
||||
}
|
||||
|
||||
const tutorialText1 = `Welcome, %s! We are glad to have you here.
|
||||
|
||||
We care deeply about our valued customers.
|
||||
And we know what our customers need.
|
||||
|
||||
They need gas. Gallons and gallons and gallons...
|
||||
And snacks. And sometimes... the restroom.
|
||||
|
||||
(Click to continue)`
|
||||
|
||||
const tutorialText2 = `We know you will serve our customers with a smile.
|
||||
While interviewing you, we saw something special.
|
||||
|
||||
We loved the way you %s.
|
||||
That's when we knew our search was over.`
|
||||
|
||||
const tutorialText3 = `Oh, here comes your first customer now!
|
||||
We know you will do great. Break a leg.
|
||||
|
||||
Actually, don't break a leg.
|
||||
We don't want to have to pay for that.`
|
||||
|
||||
type Tutorial struct {
|
||||
ticks int
|
||||
|
||||
page int
|
||||
|
||||
hireReason string
|
||||
}
|
||||
|
||||
func (r *Tutorial) Update(_ gohan.Entity) error {
|
||||
if !world.GameStarted || world.Name == "" || world.TutorialIntroFinished {
|
||||
return nil
|
||||
}
|
||||
r.ticks++
|
||||
|
||||
if r.page == 3 {
|
||||
if r.ticks == world.TPS {
|
||||
world.TutorialIntroFinished = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
|
||||
r.page++
|
||||
r.ticks = 0
|
||||
|
||||
if r.page == 1 {
|
||||
r.hireReason = hireReasons[world.Rand.Intn(len(hireReasons))]
|
||||
} else if r.page == 3 {
|
||||
r.ticks = 7
|
||||
}
|
||||
}
|
||||
|
||||
if r.page == 1 && inpututil.IsKeyJustPressed(ebiten.KeySpace) {
|
||||
r.hireReason = hireReasons[world.Rand.Intn(len(hireReasons))]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Tutorial) Draw(_ gohan.Entity, screen *ebiten.Image) error {
|
||||
if !world.GameStarted || world.Name == "" || world.TutorialIntroFinished {
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.ticks > 6 {
|
||||
var text string
|
||||
switch r.page {
|
||||
case 0:
|
||||
text = fmt.Sprintf(tutorialText1, world.Name)
|
||||
case 1:
|
||||
text = fmt.Sprintf(tutorialText2, r.hireReason)
|
||||
case 2:
|
||||
text = tutorialText3
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
world.DrawText(screen, 1, 16, 48, true, text)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,8 +1,14 @@
|
|||
package world
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -22,9 +28,16 @@ var (
|
|||
|
||||
Fullscreen = true
|
||||
|
||||
TutorialIntroFinished bool
|
||||
|
||||
DisableEsc bool
|
||||
)
|
||||
|
||||
var (
|
||||
CustomerTicks int
|
||||
CustomerGenderMale bool
|
||||
)
|
||||
|
||||
var Rand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
func RandomName() string {
|
||||
|
@ -36,3 +49,34 @@ func StartGame() {
|
|||
NameInput = RandomName()
|
||||
GameStarted = true
|
||||
}
|
||||
|
||||
var img = ebiten.NewImage(ScreenWidth, ScreenHeight)
|
||||
|
||||
func DrawText(screen *ebiten.Image, scale int, x int, y int, fill bool, text string) {
|
||||
const (
|
||||
paddingW = 6
|
||||
paddingH = 2
|
||||
)
|
||||
|
||||
var maxLen int
|
||||
lines := strings.Split(text, "\n")
|
||||
for _, line := range lines {
|
||||
l := len(line)
|
||||
if l > maxLen {
|
||||
maxLen = l
|
||||
}
|
||||
}
|
||||
textW, textH := maxLen*6+paddingW, len(lines)*16+paddingH
|
||||
|
||||
rect := image.Rect(x, y, x+textW*scale, y+textH*scale)
|
||||
screen.SubImage(rect).(*ebiten.Image).Fill(color.White)
|
||||
screen.SubImage(rect.Inset(1)).(*ebiten.Image).Fill(color.Black)
|
||||
|
||||
img.Clear()
|
||||
ebitenutil.DebugPrintAt(img, text, 2+x/scale, y/scale)
|
||||
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Scale(float64(scale), float64(scale))
|
||||
//op.ColorM.ChangeHSV(0, 0, 0.8)
|
||||
screen.DrawImage(img, op)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue