Add final touches

main v1.0.0
Trevor Slocum 2023-06-30 00:27:38 -07:00
parent 3098eb9867
commit 3d5baa3e89
5 changed files with 263 additions and 24 deletions

View File

@ -8,8 +8,6 @@ game jam.
It is a parody of the 1973 business simulation game [Lemonade Stand](https://en.wikipedia.org/wiki/Lemonade_Stand)
by Bob Jamison.
**Note:** This game has not yet been released. The following links are placeholders.
## Play
### Browser

View File

@ -37,6 +37,8 @@ type Game struct {
inputLetters bool // Whether to allow the user to input letters.
sim *Simulation
gameOver bool
}
func loadFont() font.Face {
@ -115,14 +117,13 @@ func (g *Game) inputActive() bool {
func (g *Game) acceptInput(text string) (handled bool) {
if text == "" {
log.Println("blank", g.currentView)
switch g.currentView {
case world.ViewStartDayProduction1:
if g.sim.MakePretzels == -1 {
if g.sim.MakePretzels == -1 || g.sim.MakePretzels*PretzelCost > g.sim.Money {
return false
}
case world.ViewStartDayProduction2:
if g.sim.MakeSigns == -1 {
if g.sim.MakeSigns == -1 || g.sim.MakeSigns*SignCost > (g.sim.Money-(g.sim.MakePretzels*PretzelCost)) {
return false
}
case world.ViewStartDayProduction3:
@ -138,16 +139,20 @@ func (g *Game) acceptInput(text string) (handled bool) {
}
switch g.currentView {
case world.ViewStartDayProduction1:
if i < 1 || i*PretzelCost > g.sim.Money {
return false
}
g.sim.MakePretzels = i
case world.ViewStartDayProduction2:
if i*SignCost > (g.sim.Money - (g.sim.MakePretzels * PretzelCost)) {
return false
}
g.sim.MakeSigns = i
case world.ViewStartDayProduction3:
g.sim.PretzelPrice = i
}
}
log.Println("accept input:" + text)
g.currentView++
partialTransition := g.currentView == world.ViewStartDayProduction2 || g.currentView == world.ViewStartDayProduction3
if partialTransition {
@ -158,9 +163,7 @@ func (g *Game) acceptInput(text string) (handled bool) {
g.viewTicks = 0
}
log.Println(g.currentView)
if g.currentView == world.ViewDay {
log.Println("start day")
g.sim.StartDay()
}
@ -220,6 +223,18 @@ func (g *Game) drawDay() error {
g.setDayCell(a.X+1, a.Y, 'o')
}
// Draw labels.
drawText(17, 0, fmt.Sprintf("DAY %d", g.sim.Day))
drawText(8, 2, fmt.Sprintf("STOCK %d", g.sim.MakePretzels-g.sim.PretzelsSold))
drawText(24, 2, fmt.Sprintf("SOLD %d", g.sim.PretzelsSold))
if g.sim.Disaster != 0 {
label := disasterLabel(g.sim.Disaster)
drawText(20-len(label)/2, 12, label)
drawText(11, 14, "NO CUSTOMERS TODAY")
drawText(7, 17, "PRESS ENTER TO CONTINUE...")
}
g.textBuffer.Clear()
for y := range g.dayBuffer {
if y != 0 {
@ -234,6 +249,29 @@ func (g *Game) refreshBuffer() error {
// TODO only do this when the view buffer or input buffer changes
// TODO fix trailing newline causing scroll bar to appear
if g.gameOver {
g.textBuffer.Clear()
g.textBuffer.Write([]byte(fmt.Sprintf(` DAY %d
YOU HAVE NO MONEY!
YOU ARE NOW HOMELESS!
THERE IS NO HOPE LEFT
IN YOUR EYES... ONLY PAIN!
GAME OVER`, g.sim.Day)))
return nil
}
if g.currentView == world.ViewDay {
return g.drawDay()
}
@ -241,10 +279,11 @@ func (g *Game) refreshBuffer() error {
income := g.sim.PretzelsSold * g.sim.PretzelPrice
totalIncome := fmt.Sprintf("$%d.%02d", income/100, income%100)
totalExpenses := "$1.45"
expenses := (g.sim.MakePretzels * PretzelCost) + (g.sim.MakeSigns * SignCost)
totalExpenses := formatMoney(expenses)
profit := "$3.55"
assets := "$6.60"
profit := formatMoney(income - expenses)
assets := formatMoney(g.sim.Money)
viewBytes := viewText[g.currentView]
writeLines := g.viewTicks + 1
@ -272,13 +311,13 @@ func (g *Game) refreshBuffer() error {
var lines [][]byte
switch g.currentView {
case world.ViewStartDayProduction1:
viewBytes = []byte(fmt.Sprintf(string(viewBytes), g.sim.Day))
viewBytes = []byte(fmt.Sprintf(string(viewBytes), g.sim.Day, formatMoney(g.sim.Money)))
lines = bytes.Split(viewBytes, []byte("\n"))
case world.ViewStartDayProduction2:
viewBytes = []byte(fmt.Sprintf(string(viewBytes), g.sim.Day, g.sim.MakePretzels))
viewBytes = []byte(fmt.Sprintf(string(viewBytes), g.sim.Day, formatMoney(g.sim.Money), g.sim.MakePretzels))
lines = bytes.Split(viewBytes, []byte("\n"))
case world.ViewStartDayProduction3:
viewBytes = []byte(fmt.Sprintf(string(viewBytes), g.sim.Day, g.sim.MakePretzels, g.sim.MakeSigns))
viewBytes = []byte(fmt.Sprintf(string(viewBytes), g.sim.Day, formatMoney(g.sim.Money), g.sim.MakePretzels, g.sim.MakeSigns))
lines = bytes.Split(viewBytes, []byte("\n"))
case world.ViewFinancialReport:
viewBytes = []byte(fmt.Sprintf(string(viewBytes), g.sim.Day, g.sim.PretzelsSold, formatCents(g.sim.PretzelPrice), totalIncome, g.sim.MakePretzels, g.sim.MakeSigns, totalExpenses, profit, assets))
@ -331,6 +370,10 @@ func (g *Game) Update() error {
}
}
if g.gameOver {
return nil
}
// Handle user input.
if g.inputActive() {
err := etk.Update()
@ -362,10 +405,21 @@ func (g *Game) Update() error {
return err
}
}
g.sim.DisasterAcknowledged = true
if g.sim.Money < PretzelCost {
g.gameOver = true
}
}
g.currentView++
}
g.viewTicks = 0
err := g.refreshBuffer()
if err != nil {
return err
}
return nil
}
if g.currentView == world.ViewDay {
@ -380,9 +434,13 @@ func (g *Game) Update() error {
}
}
if g.sim.DayFinished {
if g.sim.DayFinished && (g.sim.Disaster == 0 || g.sim.DisasterAcknowledged) {
g.currentView++
g.viewTicks = 0
if g.sim.Money < PretzelCost {
g.gameOver = true
}
}
}
@ -409,3 +467,12 @@ func (g *Game) Draw(screen *ebiten.Image) {
func (g *Game) Exit() {
os.Exit(0)
}
func formatMoney(v int) string {
var prefix string
if v < 0 {
prefix = "-"
v *= -1
}
return fmt.Sprintf("%s$%d.%02d", prefix, v/100, v%100)
}

View File

@ -4,6 +4,69 @@ import (
"math/rand"
)
const (
PretzelCost = 15
SignCost = 125
)
const (
DisasterWeather = iota + 1
DisasterParentsLostJobs
DisasterMomMedical
DisasterDadMedical
DisasterPlayerMedical
DisasterCompetitor
DisasterChildLabor
DisasterBankAccount
DisasterCreditCard
DisasterInflation
DisasterDeflation
DisasterStockMarket
DisasterThugs
DisasterDebt
DisasterSued
DisasterWar
)
func disasterLabel(d int) string {
switch d {
case DisasterWeather:
return "RAIN, RAIN, GO AWAY!"
case DisasterParentsLostJobs:
return "YOUR PARENTS LOST BOTH OF THEIR JOBS!"
case DisasterMomMedical:
return "YOUR MOM NEEDS EXPENSIVE MEDICAL WORK!"
case DisasterDadMedical:
return "YOUR DAD NEEDS EXPENSIVE MEDICAL WORK!"
case DisasterPlayerMedical:
return "YOU NEED EXPENSIVE MEDICAL WORK!"
case DisasterCompetitor:
return "A MEGA-CORP COMPETITOR OPENS NEARBY!"
case DisasterChildLabor:
return "YOUR ARE ACCUSED OF USING CHILD LABOR!"
case DisasterBankAccount:
return "YOUR BANK ACCOUNT IS FROZEN!"
case DisasterCreditCard:
return "YOUR CREDIT CARD IS FROZEN!"
case DisasterInflation:
return "MASSIVE INFLATION PLAGUES THE ECONOMY!"
case DisasterDeflation:
return "MASSIVE DEFLATION PLAGUES THE ECONOMY!"
case DisasterStockMarket:
return "THE STOCK MARKET HAS CRASHED!"
case DisasterThugs:
return "THUGS KNOCK OVER YOUR PRETZEL STAND!"
case DisasterDebt:
return "YOUR CAR IS REPOSSESSED DUE TO DEBT!"
case DisasterSued:
return "A CUSTOMER IS SUING YOU FOR FRAUD!"
case DisasterWar:
return "WAR HAS BEEN DECLARED!"
default:
return ""
}
}
type SimulationActor struct {
X, Y int
TargetX, TargetY int
@ -26,11 +89,20 @@ type Simulation struct {
MakeSigns int
PretzelPrice int // In cents.
NoSaleNoStock int
Disaster int
DisasterDay int // Last disaster day.
Disasters []int
DisasterAcknowledged bool
}
func NewSimulation() *Simulation {
return &Simulation{
Day: 1,
Day: 1,
Money: 420,
MakePretzels: -1,
MakeSigns: -1,
@ -38,8 +110,39 @@ func NewSimulation() *Simulation {
}
}
func (s *Simulation) nextDisaster() int {
if s.Day == 1 || len(s.Disasters) == 16 {
return 0
}
if s.Day-s.DisasterDay < 3 && rand.Intn(2) == 0 {
return 0
}
DISASTERS:
for {
disaster := rand.Intn(16) + 1
for _, d := range s.Disasters {
if d == disaster {
continue DISASTERS
}
}
s.Disasters = append(s.Disasters, disaster)
s.DisasterDay = s.Day
return disaster
}
}
func (s *Simulation) generateActors() {
for i := 0; i < 7; i++ {
if s.Disaster != 0 {
return
}
numActors := 3 + rand.Intn(7)
for i := 0; i < s.MakeSigns; i++ {
numActors += 2 + rand.Intn(10)
}
for i := 0; i < numActors; i++ {
x := -2 + rand.Intn(40)
tx := 44 - x
y := 18
@ -62,6 +165,11 @@ func (s *Simulation) StartDay() {
s.DayFinishedTicks = 25
s.PretzelsSold = 0
s.NoSaleNoStock = 0
s.Disaster = s.nextDisaster()
s.DisasterAcknowledged = false
s.generateActors()
}
@ -72,6 +180,17 @@ func (s *Simulation) Tick() error {
standX, standY := 19, 10
spaceAvailable := func(x, y int, thisActor *SimulationActor) bool {
for _, a := range s.Actors {
if a == thisActor || a.Inactive {
continue
} else if a.X == x && a.Y == y && (a.TargetActive == thisActor.TargetActive || (x == standX && y == standY)) {
return false
}
}
return true
}
var active int
var tx, ty int
for _, a := range s.Actors {
@ -90,16 +209,18 @@ func (s *Simulation) Tick() error {
} else {
tx, ty = standX, standY
}
moveX, moveY := 0, 0
if a.X < tx {
a.X++
moveX++
} else if a.X > tx {
a.X--
moveX--
}
if a.Y < ty {
a.Y++
moveY++
} else if a.Y > ty {
a.Y--
moveY--
}
a.X, a.Y = a.X+moveX, a.Y+moveY
if a.TargetActive && (a.X < -2 || a.X > 40 || a.Y > 17) {
a.Inactive = true
@ -108,10 +229,15 @@ func (s *Simulation) Tick() error {
if a.X == tx && a.Y == ty {
if !a.TargetActive {
s.PretzelsSold++
if s.MakePretzels > s.PretzelsSold {
s.PretzelsSold++
a.WaitTicks = 100 + rand.Intn(150)
} else {
s.NoSaleNoStock++
a.WaitTicks = 25 + rand.Intn(25)
}
a.TargetActive = true
a.WaitTicks = 100 + rand.Intn(150)
continue
} else {
a.Inactive = true
@ -119,10 +245,17 @@ func (s *Simulation) Tick() error {
}
}
a.WaitTicks = a.WalkTicks
if !spaceAvailable(a.X+moveX, a.Y+moveY, a) || !spaceAvailable(a.X+moveX-1, a.Y+moveY, a) || !spaceAvailable(a.X+moveX+1, a.Y+moveY, a) {
a.WaitTicks = 50 + rand.Intn(100)
}
}
if active == 0 {
s.DayFinishedTicks--
if s.DayFinishedTicks == 0 {
income := s.PretzelsSold * s.PretzelPrice
expenses := (s.MakePretzels * PretzelCost) + (s.MakeSigns * SignCost)
s.Money = s.Money + income - expenses
s.DayFinished = true
}
}

View File

@ -49,12 +49,20 @@ YOU WILL BEGIN WITH $4.20 CASH (ASSETS).
// viewStartDayProduction1
[]byte(` DAY %d
ASSETS %s
HOW MANY PRETZELS ($0.15 EACH) DO YOU
WISH TO MAKE ?`),
// viewStartDayProduction2
[]byte(` DAY %d
ASSETS %s
HOW MANY PRETZELS ($0.15 EACH) DO YOU
WISH TO MAKE ?%d
@ -64,6 +72,10 @@ DO YOU WISH TO MAKE ?`),
// viewStartDayProduction3
[]byte(` DAY %d
ASSETS %s
HOW MANY PRETZELS ($0.15 EACH) DO YOU
WISH TO MAKE ?%d

29
goreleaser.yml Normal file
View File

@ -0,0 +1,29 @@
project_name: pretzel-tycoon
builds:
-
id: pretzel-tycoon
goos:
- js
- linux
- windows
goarch:
- amd64
- wasm
archives:
-
id: pretzel-tycoon
builds:
- pretzel-tycoon
replacements:
386: i386
format_overrides:
- goos: js
format: zip
- goos: windows
format: zip
files:
- ./*.md
- LICENSE
checksum:
name_template: 'checksums.txt'