everything I got
This commit is contained in:
11
.vscode/launch.json
vendored
Normal file
11
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Package",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
assets/.DS_Store
vendored
Normal file
BIN
assets/.DS_Store
vendored
Normal file
Binary file not shown.
71
assets/audio.go
Normal file
71
assets/audio.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package assets
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/audio"
|
||||
"github.com/hajimehoshi/ebiten/v2/audio/wav"
|
||||
)
|
||||
|
||||
//go:embed *
|
||||
//go:embed */*
|
||||
var embeddedFiles embed.FS
|
||||
var sampleRate = 44100
|
||||
var audioContext = audio.NewContext(sampleRate)
|
||||
var songChoice = 2
|
||||
var SongTracks = loadTracksForSong(songChoice)
|
||||
|
||||
func loadTracksForSong(sng int) map[int]*audio.Player {
|
||||
tempMap := make(map[int]*audio.Player)
|
||||
for i := 1; i <= 10; i++ {
|
||||
tempMap[i] = LoadWavAudioFile(fmt.Sprintf("audio/song0%d/track%02d.wav", sng, i), true)
|
||||
}
|
||||
|
||||
return tempMap
|
||||
}
|
||||
|
||||
var SFXLibrary = map[string]*audio.Player{
|
||||
"newLevel": LoadWavAudioFile("audio/portal2.wav", false),
|
||||
"colourMatch": LoadWavAudioFile("audio/portal.wav", false),
|
||||
"gameOver": LoadWavAudioFile("audio/gameOver.wav", false),
|
||||
}
|
||||
|
||||
func LoadWavAudioFile(filePath string, loop bool) *audio.Player {
|
||||
|
||||
audioData, err := fs.ReadFile(embeddedFiles, filePath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
stream, err := wav.DecodeWithSampleRate(sampleRate, bytes.NewReader(audioData))
|
||||
if err != nil {
|
||||
log.Fatalf("failed to decode audio: %v", err)
|
||||
}
|
||||
|
||||
var player *audio.Player
|
||||
|
||||
if loop {
|
||||
lengthOfAudioData := int64(len(audioData))
|
||||
loopingStream := audio.NewInfiniteLoop(stream, lengthOfAudioData)
|
||||
player, err = audioContext.NewPlayer(loopingStream)
|
||||
|
||||
} else {
|
||||
player, err = audioContext.NewPlayer(stream)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return player
|
||||
|
||||
}
|
||||
|
||||
func SilenceSongTracks() {
|
||||
for _, song := range SongTracks {
|
||||
song.SetVolume(0)
|
||||
}
|
||||
}
|
||||
BIN
assets/audio/.DS_Store
vendored
Normal file
BIN
assets/audio/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
assets/audio/gameOver.wav
Normal file
BIN
assets/audio/gameOver.wav
Normal file
Binary file not shown.
BIN
assets/audio/portal.wav
Normal file
BIN
assets/audio/portal.wav
Normal file
Binary file not shown.
BIN
assets/audio/portal2.wav
Normal file
BIN
assets/audio/portal2.wav
Normal file
Binary file not shown.
BIN
assets/audio/song01/track01.wav
Normal file
BIN
assets/audio/song01/track01.wav
Normal file
Binary file not shown.
BIN
assets/audio/song01/track02.wav
Normal file
BIN
assets/audio/song01/track02.wav
Normal file
Binary file not shown.
BIN
assets/audio/song01/track03.wav
Normal file
BIN
assets/audio/song01/track03.wav
Normal file
Binary file not shown.
BIN
assets/audio/song01/track04.wav
Normal file
BIN
assets/audio/song01/track04.wav
Normal file
Binary file not shown.
BIN
assets/audio/song01/track05.wav
Normal file
BIN
assets/audio/song01/track05.wav
Normal file
Binary file not shown.
BIN
assets/audio/song01/track06.wav
Normal file
BIN
assets/audio/song01/track06.wav
Normal file
Binary file not shown.
BIN
assets/audio/song01/track07.wav
Normal file
BIN
assets/audio/song01/track07.wav
Normal file
Binary file not shown.
BIN
assets/audio/song01/track08.wav
Normal file
BIN
assets/audio/song01/track08.wav
Normal file
Binary file not shown.
BIN
assets/audio/song01/track09.wav
Normal file
BIN
assets/audio/song01/track09.wav
Normal file
Binary file not shown.
BIN
assets/audio/song01/track10.wav
Normal file
BIN
assets/audio/song01/track10.wav
Normal file
Binary file not shown.
BIN
assets/audio/song01/track11.wav
Normal file
BIN
assets/audio/song01/track11.wav
Normal file
Binary file not shown.
BIN
assets/audio/song01/track12.wav
Normal file
BIN
assets/audio/song01/track12.wav
Normal file
Binary file not shown.
BIN
assets/audio/song01/track13.wav
Normal file
BIN
assets/audio/song01/track13.wav
Normal file
Binary file not shown.
BIN
assets/audio/song02/.DS_Store
vendored
Normal file
BIN
assets/audio/song02/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
assets/audio/song02/track01.wav
Normal file
BIN
assets/audio/song02/track01.wav
Normal file
Binary file not shown.
BIN
assets/audio/song02/track02.wav
Normal file
BIN
assets/audio/song02/track02.wav
Normal file
Binary file not shown.
BIN
assets/audio/song02/track03.wav
Normal file
BIN
assets/audio/song02/track03.wav
Normal file
Binary file not shown.
BIN
assets/audio/song02/track04.wav
Normal file
BIN
assets/audio/song02/track04.wav
Normal file
Binary file not shown.
BIN
assets/audio/song02/track05.wav
Normal file
BIN
assets/audio/song02/track05.wav
Normal file
Binary file not shown.
BIN
assets/audio/song02/track06.wav
Normal file
BIN
assets/audio/song02/track06.wav
Normal file
Binary file not shown.
BIN
assets/audio/song02/track07.wav
Normal file
BIN
assets/audio/song02/track07.wav
Normal file
Binary file not shown.
BIN
assets/audio/song02/track08.wav
Normal file
BIN
assets/audio/song02/track08.wav
Normal file
Binary file not shown.
BIN
assets/audio/song02/track09.wav
Normal file
BIN
assets/audio/song02/track09.wav
Normal file
Binary file not shown.
BIN
assets/audio/song02/track10.wav
Normal file
BIN
assets/audio/song02/track10.wav
Normal file
Binary file not shown.
37
assets/fonts.go
Normal file
37
assets/fonts.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package assets
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"log"
|
||||
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/font/opentype"
|
||||
)
|
||||
|
||||
//go:embed *
|
||||
var FontFiles embed.FS
|
||||
var fontFace font.Face
|
||||
|
||||
func LoadFontFace(fileName string, size float64) font.Face {
|
||||
fontBytes, err := FontFiles.ReadFile(fileName)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
tt, err := opentype.Parse(fontBytes)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
const dpi = 144
|
||||
fontFace, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
||||
Size: size,
|
||||
DPI: dpi,
|
||||
Hinting: font.HintingNone,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return fontFace
|
||||
}
|
||||
BIN
assets/fonts/mechanical.otf
Normal file
BIN
assets/fonts/mechanical.otf
Normal file
Binary file not shown.
BIN
assets/fonts/robot.otf
Normal file
BIN
assets/fonts/robot.otf
Normal file
Binary file not shown.
182
geometry/grid.go
Normal file
182
geometry/grid.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package geom
|
||||
|
||||
import (
|
||||
//"fmt"
|
||||
"image/color"
|
||||
"math/rand"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
var defaultSquareSpacing = 5
|
||||
var minBorder = 20
|
||||
var ScoreOffset = 50
|
||||
var HighScoreOffset = 10
|
||||
var GameGrid Grid
|
||||
var MaxGridDimension = 20
|
||||
|
||||
type Grid struct {
|
||||
squareSpacing int
|
||||
squares [][]Square
|
||||
startX int
|
||||
startY int
|
||||
squareSize int
|
||||
coloredSquaresRemaining int
|
||||
}
|
||||
|
||||
// used for comparing two squares positions
|
||||
type Pair struct {
|
||||
rowNum int
|
||||
colNum int
|
||||
}
|
||||
|
||||
func (pr Pair) equal(pr2 Pair) bool {
|
||||
return pr.rowNum == pr2.rowNum && pr.colNum == pr2.colNum
|
||||
|
||||
}
|
||||
|
||||
func pairAlreadyExists(pr Pair, pairs []Pair) bool {
|
||||
result := false
|
||||
for _, nextPair := range pairs {
|
||||
if pr.equal(nextPair) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (grd *Grid) NumSquares() int {
|
||||
size := -1
|
||||
if len(grd.squares[0]) != 0 {
|
||||
size = len(grd.squares) * len(grd.squares[0])
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
// returns num rows by num columns
|
||||
func (grd *Grid) Size() (int, int) {
|
||||
if len(grd.squares[0]) != 0 {
|
||||
return len(grd.squares), len(grd.squares[0])
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
func BuildGrid(numRows int, numColumns int) {
|
||||
grd := Grid{squareSpacing: defaultSquareSpacing}
|
||||
|
||||
winWidth, winHeight := ebiten.WindowSize()
|
||||
sqSize := min((winWidth-2*minBorder-defaultSquareSpacing*numColumns)/numColumns, (winHeight-2*minBorder-ScoreOffset-3*HighScoreOffset-defaultSquareSpacing*numRows)/numRows)
|
||||
sqSize = max(sqSize, 1) //ensure a nonzero, nonnegative square size
|
||||
gridWidth := numColumns*(sqSize+grd.squareSpacing) - grd.squareSpacing
|
||||
gridHeight := numRows*(sqSize+grd.squareSpacing) - grd.squareSpacing
|
||||
|
||||
currentX := (winWidth - gridWidth) / 2
|
||||
currentY := ScoreOffset + (winHeight-gridHeight-ScoreOffset-HighScoreOffset)/2
|
||||
grd.startX = currentX
|
||||
grd.startY = currentY
|
||||
grd.squareSize = sqSize
|
||||
|
||||
table := make([][]Square, numRows)
|
||||
|
||||
for i := 0; i < numRows; i++ {
|
||||
row := make([]Square, numColumns)
|
||||
for j := 0; j < numColumns; j++ {
|
||||
sq := MakeSquare(sqSize)
|
||||
sq.SetPosition(currentX, currentY)
|
||||
row[j] = sq
|
||||
currentX += sqSize + grd.squareSpacing
|
||||
}
|
||||
table[i] = row
|
||||
currentY += sqSize + grd.squareSpacing
|
||||
currentX = grd.startX
|
||||
}
|
||||
|
||||
grd.squares = table
|
||||
|
||||
//assign random colours to a subset of all squares
|
||||
squaresToColour := min(numColumns, numRows)
|
||||
|
||||
usedPositions := make([]Pair, squaresToColour)
|
||||
coloredSoFar := 0
|
||||
for i := 0; i < squaresToColour; i++ {
|
||||
colourIndex := rand.Intn(len(LightRGBMap))
|
||||
row := rand.Intn(numRows) + 1
|
||||
col := rand.Intn(numColumns) + 1
|
||||
//prevent a colour on the starting square
|
||||
if row == numRows && col == 1 {
|
||||
col = 2
|
||||
}
|
||||
|
||||
//make sure we don't colour the same square twice
|
||||
nextPair := Pair{rowNum: row, colNum: col}
|
||||
if coloredSoFar == 0 {
|
||||
usedPositions[0] = nextPair
|
||||
coloredSoFar = 1
|
||||
} else {
|
||||
for pairAlreadyExists(nextPair, usedPositions) {
|
||||
row = rand.Intn(numRows) + 1
|
||||
col = rand.Intn(numColumns) + 1
|
||||
//prevent a colour on the starting square
|
||||
if row == numRows && col == 1 {
|
||||
col = 2
|
||||
}
|
||||
nextPair = Pair{rowNum: row, colNum: col}
|
||||
}
|
||||
usedPositions[coloredSoFar] = nextPair
|
||||
coloredSoFar += 1
|
||||
|
||||
}
|
||||
|
||||
grd.ChangeColour(row, col, LightRGBMap[colourIndex])
|
||||
grd.GetSquareAt(row, col).RGBColourIndex = colourIndex
|
||||
}
|
||||
grd.coloredSquaresRemaining = squaresToColour
|
||||
|
||||
GameGrid = grd
|
||||
}
|
||||
|
||||
func (grd *Grid) Draw(screen *ebiten.Image) {
|
||||
opts := &ebiten.DrawImageOptions{}
|
||||
|
||||
for _, rowOfSquares := range grd.squares {
|
||||
for _, sq := range rowOfSquares {
|
||||
xPos := float64(sq.X())
|
||||
yPos := float64(sq.Y())
|
||||
opts.GeoM.Translate(xPos, yPos)
|
||||
screen.DrawImage(sq.Img, opts)
|
||||
opts.GeoM.Reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (grd *Grid) DrawUpTo(finalSquareCount int, screen *ebiten.Image) {
|
||||
opts := &ebiten.DrawImageOptions{}
|
||||
curr := 1
|
||||
for _, rowOfSquares := range grd.squares {
|
||||
for _, sq := range rowOfSquares {
|
||||
xPos := float64(sq.X())
|
||||
yPos := float64(sq.Y())
|
||||
opts.GeoM.Translate(xPos, yPos)
|
||||
screen.DrawImage(sq.Img, opts)
|
||||
opts.GeoM.Reset()
|
||||
if curr == finalSquareCount {
|
||||
return
|
||||
}
|
||||
curr++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (grd *Grid) ChangeColour(row int, column int, clr color.Color) {
|
||||
sq := grd.squares[row-1][column-1]
|
||||
sq.ChangeColour(clr)
|
||||
}
|
||||
|
||||
func (grd *Grid) GetSquareAt(row int, column int) *Square {
|
||||
return &grd.squares[row-1][column-1]
|
||||
}
|
||||
|
||||
func (grd *Grid) ColouredSquaresRemaining() int {
|
||||
return grd.coloredSquaresRemaining
|
||||
}
|
||||
131
geometry/player.go
Normal file
131
geometry/player.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package geom
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
"src.robn.tv/MrDonuts/RGB/assets"
|
||||
)
|
||||
|
||||
var MainPlayer *Player
|
||||
var totalColors = 3
|
||||
var colorMap = map[int]color.Color{
|
||||
0: RedFill,
|
||||
1: GreenFill,
|
||||
2: BlueFill,
|
||||
}
|
||||
var defaultStartingEnergy = 50
|
||||
var nextLevelEnergyJump = 30
|
||||
var CurrentEnergy = defaultStartingEnergy
|
||||
var CurrentLevel = 1
|
||||
|
||||
type Player struct {
|
||||
square Square
|
||||
row int
|
||||
col int
|
||||
currColorIndex int
|
||||
}
|
||||
|
||||
func SetupPlayer() {
|
||||
rows, _ := GameGrid.Size()
|
||||
|
||||
//build default square
|
||||
sq := MakeSquare(GameGrid.squareSize)
|
||||
|
||||
//adjust to red as starting value
|
||||
sq.ChangeColour(RedFill)
|
||||
|
||||
//position on bottom left
|
||||
gridSq := GameGrid.GetSquareAt(rows, 1)
|
||||
sq.SetPosition(gridSq.x, gridSq.y)
|
||||
|
||||
//setup struct for passing around
|
||||
ply := Player{square: sq, row: rows, col: 1, currColorIndex: 0}
|
||||
MainPlayer = &ply
|
||||
}
|
||||
|
||||
func (p *Player) Update() {
|
||||
|
||||
moveOccurs := false
|
||||
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyArrowDown) {
|
||||
rows, _ := GameGrid.Size()
|
||||
if p.row < rows {
|
||||
p.row += 1
|
||||
p.square.moveDownOnGrid()
|
||||
moveOccurs = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyArrowRight) {
|
||||
_, cols := GameGrid.Size()
|
||||
if p.col < cols {
|
||||
p.col += 1
|
||||
p.square.moveRightOnGrid()
|
||||
moveOccurs = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyArrowLeft) {
|
||||
if p.col > 1 {
|
||||
p.col -= 1
|
||||
p.square.moveLeftOnGrid()
|
||||
moveOccurs = true
|
||||
}
|
||||
}
|
||||
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyArrowUp) {
|
||||
if p.row > 1 {
|
||||
p.row -= 1
|
||||
p.square.moveUpOnGrid()
|
||||
moveOccurs = true
|
||||
}
|
||||
}
|
||||
|
||||
if moveOccurs {
|
||||
moveOccurs = false
|
||||
|
||||
p.NextColor()
|
||||
|
||||
// check if player is on same colour square
|
||||
gridSquare := GameGrid.GetSquareAt(p.row, p.col)
|
||||
if p.currColorIndex == gridSquare.RGBColourIndex {
|
||||
GameGrid.coloredSquaresRemaining -= 1
|
||||
gridSquare.RGBColourIndex = -1
|
||||
gridSquare.ChangeColour(GrayFill)
|
||||
if GameGrid.coloredSquaresRemaining != 0 {
|
||||
assets.SFXLibrary["colourMatch"].Rewind()
|
||||
assets.SFXLibrary["colourMatch"].Play()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// reduce energy
|
||||
CurrentEnergy -= 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (p *Player) Draw(screen *ebiten.Image) {
|
||||
opts := &ebiten.DrawImageOptions{}
|
||||
opts.GeoM.Translate(float64(p.square.x), float64(p.square.y))
|
||||
screen.DrawImage(p.square.Img, opts)
|
||||
}
|
||||
|
||||
func (p *Player) NextColor() {
|
||||
p.currColorIndex = (p.currColorIndex + 1) % totalColors
|
||||
p.square.ChangeColour(colorMap[p.currColorIndex])
|
||||
}
|
||||
|
||||
func ResetPlayerValues() {
|
||||
CurrentEnergy = defaultStartingEnergy
|
||||
CurrentLevel = 1
|
||||
}
|
||||
|
||||
func PrepareForNextLevel() {
|
||||
CurrentEnergy += nextLevelEnergyJump
|
||||
CurrentLevel += 1
|
||||
}
|
||||
93
geometry/square.go
Normal file
93
geometry/square.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package geom
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
var GrayFill = color.RGBA{R: 128, G: 128, B: 128, A: 255}
|
||||
var RedFill = color.RGBA{R: 200, G: 0, B: 0, A: 255}
|
||||
var GreenFill = color.RGBA{R: 0, G: 200, B: 0, A: 255}
|
||||
var BlueFill = color.RGBA{R: 0, G: 0, B: 200, A: 255}
|
||||
var LightRedFill = color.RGBA{R: 255, G: 104, B: 103, A: 255}
|
||||
var LightGreenFill = color.RGBA{R: 144, G: 255, B: 143, A: 255}
|
||||
var LightBlueFill = color.RGBA{R: 150, G: 150, B: 255, A: 255}
|
||||
|
||||
var RGBColourMap = map[int]color.Color{
|
||||
0: RedFill,
|
||||
1: GreenFill,
|
||||
2: BlueFill,
|
||||
}
|
||||
|
||||
var AllColourMap = map[int]color.Color{
|
||||
0: GrayFill,
|
||||
1: RedFill,
|
||||
2: GreenFill,
|
||||
3: BlueFill,
|
||||
4: LightRedFill,
|
||||
5: LightBlueFill,
|
||||
6: LightGreenFill,
|
||||
}
|
||||
|
||||
var LightRGBMap = map[int]color.Color{
|
||||
0: LightRedFill,
|
||||
1: LightGreenFill,
|
||||
2: LightBlueFill,
|
||||
}
|
||||
|
||||
type Square struct {
|
||||
x int
|
||||
y int
|
||||
clr color.Color
|
||||
width int
|
||||
height int
|
||||
Img *ebiten.Image
|
||||
RGBColourIndex int
|
||||
}
|
||||
|
||||
func MakeSquare(size int) Square {
|
||||
image := ebiten.NewImage(size, size)
|
||||
defaultColor := color.RGBA{R: 128, G: 128, B: 128, A: 255}
|
||||
image.Fill(defaultColor)
|
||||
sq := Square{clr: defaultColor, Img: image, width: size, height: size, x: 0, y: 0, RGBColourIndex: -1}
|
||||
return sq
|
||||
}
|
||||
|
||||
func (sq *Square) ChangeColour(newColor color.Color) {
|
||||
sq.clr = newColor
|
||||
sq.Img.Fill(newColor)
|
||||
}
|
||||
|
||||
func (sq *Square) SetPosition(x int, y int) {
|
||||
sq.x = x
|
||||
sq.y = y
|
||||
}
|
||||
|
||||
func (sq *Square) X() int {
|
||||
return sq.x
|
||||
}
|
||||
|
||||
func (sq *Square) Y() int {
|
||||
return sq.y
|
||||
}
|
||||
|
||||
func (sq *Square) moveRightOnGrid() {
|
||||
newX := sq.x + GameGrid.squareSize + GameGrid.squareSpacing
|
||||
sq.x = newX
|
||||
}
|
||||
|
||||
func (sq *Square) moveLeftOnGrid() {
|
||||
newX := sq.x - GameGrid.squareSize - GameGrid.squareSpacing
|
||||
sq.x = newX
|
||||
}
|
||||
|
||||
func (sq *Square) moveDownOnGrid() {
|
||||
newY := sq.y + GameGrid.squareSize + GameGrid.squareSpacing
|
||||
sq.y = newY
|
||||
}
|
||||
|
||||
func (sq *Square) moveUpOnGrid() {
|
||||
newY := sq.y - GameGrid.squareSize - GameGrid.squareSpacing
|
||||
sq.y = newY
|
||||
}
|
||||
17
go.mod
Normal file
17
go.mod
Normal file
@@ -0,0 +1,17 @@
|
||||
module src.robn.tv/MrDonuts/RGB
|
||||
|
||||
go 1.21.3
|
||||
|
||||
require github.com/hajimehoshi/ebiten/v2 v2.6.2
|
||||
|
||||
require (
|
||||
github.com/ebitengine/oto/v3 v3.1.0 // indirect
|
||||
github.com/ebitengine/purego v0.5.0 // indirect
|
||||
github.com/jezek/xgb v1.1.0 // indirect
|
||||
golang.org/x/exp/shiny v0.0.0-20230817173708-d852ddb80c63 // indirect
|
||||
golang.org/x/image v0.14.0
|
||||
golang.org/x/mobile v0.0.0-20230922142353-e2f452493d57 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/sys v0.12.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
)
|
||||
22
go.sum
Normal file
22
go.sum
Normal file
@@ -0,0 +1,22 @@
|
||||
github.com/ebitengine/oto/v3 v3.1.0 h1:9tChG6rizyeR2w3vsygTTTVVJ9QMMyu00m2yBOCch6U=
|
||||
github.com/ebitengine/oto/v3 v3.1.0/go.mod h1:IK1QTnlfZK2GIB6ziyECm433hAdTaPpOsGMLhEyEGTg=
|
||||
github.com/ebitengine/purego v0.5.0 h1:JrMGKfRIAM4/QVKaesIIT7m/UVjTj5GYhRSQYwfVdpo=
|
||||
github.com/ebitengine/purego v0.5.0/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
|
||||
github.com/hajimehoshi/bitmapfont/v3 v3.0.0 h1:r2+6gYK38nfztS/et50gHAswb9hXgxXECYgE8Nczmi4=
|
||||
github.com/hajimehoshi/bitmapfont/v3 v3.0.0/go.mod h1:+CxxG+uMmgU4mI2poq944i3uZ6UYFfAkj9V6WqmuvZA=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.6.2 h1:tVa3ZJbp4Uz/VSjmpgtQIOvwd7aQH290XehHBLr2iWk=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.6.2/go.mod h1:TZtorL713an00UW4LyvMeKD8uXWnuIuCPtlH11b0pgI=
|
||||
github.com/jezek/xgb v1.1.0 h1:wnpxJzP1+rkbGclEkmwpVFQWpuE2PUGNUzP8SbfFobk=
|
||||
github.com/jezek/xgb v1.1.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
|
||||
golang.org/x/exp/shiny v0.0.0-20230817173708-d852ddb80c63 h1:3AGKexOYqL+ztdWdkB1bDwXgPBuTS/S8A4WzuTvJ8Cg=
|
||||
golang.org/x/exp/shiny v0.0.0-20230817173708-d852ddb80c63/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0=
|
||||
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
|
||||
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
||||
golang.org/x/mobile v0.0.0-20230922142353-e2f452493d57 h1:Q6NT8ckDYNcwmi/bmxe+XbiDMXqMRW1xFBtJ+bIpie4=
|
||||
golang.org/x/mobile v0.0.0-20230922142353-e2f452493d57/go.mod h1:wEyOn6VvNW7tcf+bW/wBz1sehi2s2BZ4TimyR7qZen4=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
60
main.go
Normal file
60
main.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
"src.robn.tv/MrDonuts/RGB/assets"
|
||||
geom "src.robn.tv/MrDonuts/RGB/geometry"
|
||||
"src.robn.tv/MrDonuts/RGB/states"
|
||||
score "src.robn.tv/MrDonuts/RGB/tools"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
numRows := 3
|
||||
numColumns := 6
|
||||
geom.BuildGrid(numRows, numColumns)
|
||||
|
||||
states.SetupStateMachine()
|
||||
|
||||
//some audio testing
|
||||
for i, song := range assets.SongTracks {
|
||||
song.Play()
|
||||
if i != 1 {
|
||||
song.SetVolume(0)
|
||||
} else {
|
||||
song.SetVolume(0.7)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type Game struct{}
|
||||
|
||||
func (g *Game) Update() error {
|
||||
|
||||
states.SM.Update()
|
||||
score.ScoreMngr.Update()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Game) Draw(screen *ebiten.Image) {
|
||||
|
||||
states.SM.Draw(screen)
|
||||
|
||||
}
|
||||
|
||||
func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
|
||||
return 640, 480
|
||||
}
|
||||
|
||||
func main() {
|
||||
ebiten.SetWindowSize(640, 480)
|
||||
ebiten.SetWindowTitle("RGB")
|
||||
if err := ebiten.RunGame(&Game{}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
57
states/gameOverState.go
Normal file
57
states/gameOverState.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package states
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
"math/rand"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
"github.com/hajimehoshi/ebiten/v2/text"
|
||||
"golang.org/x/image/font"
|
||||
"src.robn.tv/MrDonuts/RGB/assets"
|
||||
geom "src.robn.tv/MrDonuts/RGB/geometry"
|
||||
)
|
||||
|
||||
type GameOverState struct {
|
||||
}
|
||||
|
||||
func (st *GameOverState) Enter() {
|
||||
fmt.Println("Entering Game Over State")
|
||||
assets.SilenceSongTracks()
|
||||
assets.SFXLibrary["gameOver"].Rewind()
|
||||
assets.SFXLibrary["gameOver"].Play()
|
||||
|
||||
}
|
||||
|
||||
func (st *GameOverState) Exit() {
|
||||
fmt.Println("Exiting Game OverState")
|
||||
|
||||
}
|
||||
|
||||
func (st *GameOverState) Update() {
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyN) {
|
||||
geom.ResetPlayerValues()
|
||||
geom.BuildGrid(rand.Intn(5+geom.CurrentLevel)+2, rand.Intn(5+geom.CurrentLevel)+2)
|
||||
CurrTrack = 1
|
||||
assets.SongTracks[CurrTrack].SetVolume(0.7) //turn up track 1
|
||||
SM.Transition("load")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (st *GameOverState) Draw(screen *ebiten.Image) {
|
||||
w, h := ebiten.WindowSize()
|
||||
|
||||
gameOverStr := "Game Over"
|
||||
face := assets.LoadFontFace("fonts/robot.otf", 30)
|
||||
stringWidth := font.MeasureString(face, gameOverStr).Ceil()
|
||||
text.Draw(screen, gameOverStr, face, w/2-stringWidth/2, h/2, color.White)
|
||||
|
||||
newGameStr := "(press N for new game)"
|
||||
face = assets.LoadFontFace("fonts/robot.otf", 12)
|
||||
stringWidth = font.MeasureString(face, newGameStr).Ceil()
|
||||
text.Draw(screen, newGameStr, face, w/2-stringWidth/2, h-int(0.1*float64(h)), color.White)
|
||||
|
||||
}
|
||||
52
states/loadLevelState.go
Normal file
52
states/loadLevelState.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package states
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
geom "src.robn.tv/MrDonuts/RGB/geometry"
|
||||
score "src.robn.tv/MrDonuts/RGB/tools"
|
||||
)
|
||||
|
||||
var currentEnd = 1 //used for animating squares
|
||||
var secondsPerUpdate = 0.02
|
||||
var ticksPerSecond = 60.0
|
||||
var deltaSeconds = 1.0 / ticksPerSecond
|
||||
var secondsCounter = 0.0
|
||||
|
||||
type LoadLevelState struct {
|
||||
}
|
||||
|
||||
func (s *LoadLevelState) Enter() {
|
||||
fmt.Println("Entering Loading State")
|
||||
currentEnd = 1
|
||||
}
|
||||
|
||||
func (s *LoadLevelState) Exit() {
|
||||
fmt.Println("Exiting Loading State")
|
||||
}
|
||||
|
||||
func (s *LoadLevelState) Update() {
|
||||
secondsCounter += deltaSeconds
|
||||
|
||||
//determine how much to jump by on each draw
|
||||
totalSquares := geom.GameGrid.NumSquares()
|
||||
jumpAmount := math.Ceil(float64(totalSquares) / ticksPerSecond)
|
||||
if secondsCounter >= float64(secondsPerUpdate) {
|
||||
secondsCounter = 0
|
||||
currentEnd += int(jumpAmount)
|
||||
}
|
||||
|
||||
if currentEnd > geom.GameGrid.NumSquares() {
|
||||
SM.Transition("play")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *LoadLevelState) Draw(screen *ebiten.Image) {
|
||||
|
||||
geom.GameGrid.DrawUpTo(currentEnd, screen)
|
||||
score.ScoreMngr.Draw(screen)
|
||||
}
|
||||
71
states/playLevelState.go
Normal file
71
states/playLevelState.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package states
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
"src.robn.tv/MrDonuts/RGB/assets"
|
||||
geom "src.robn.tv/MrDonuts/RGB/geometry"
|
||||
score "src.robn.tv/MrDonuts/RGB/tools"
|
||||
)
|
||||
|
||||
var CurrTrack = 1
|
||||
|
||||
type PlayLevelState struct {
|
||||
}
|
||||
|
||||
func (s *PlayLevelState) Enter() {
|
||||
fmt.Println("Entering Play State")
|
||||
geom.SetupPlayer()
|
||||
fmt.Println(geom.GameGrid.ColouredSquaresRemaining())
|
||||
|
||||
}
|
||||
|
||||
func (s *PlayLevelState) Exit() {
|
||||
fmt.Println("Exiting Play State")
|
||||
|
||||
geom.PrepareForNextLevel()
|
||||
|
||||
if CurrTrack < len(assets.SongTracks) {
|
||||
if geom.CurrentLevel%3 == 0 {
|
||||
CurrTrack++
|
||||
assets.SongTracks[CurrTrack].SetVolume(0.7)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *PlayLevelState) Update() {
|
||||
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyN) {
|
||||
newRowSize := min(rand.Intn(5+geom.CurrentLevel)+2, geom.MaxGridDimension)
|
||||
newColSize := min(rand.Intn(5+geom.CurrentLevel)+2, geom.MaxGridDimension)
|
||||
geom.BuildGrid(newRowSize, newColSize)
|
||||
SM.Transition("load")
|
||||
assets.SFXLibrary["newLevel"].Rewind()
|
||||
assets.SFXLibrary["newLevel"].Play()
|
||||
}
|
||||
|
||||
geom.MainPlayer.Update()
|
||||
if geom.CurrentEnergy == 0 {
|
||||
SM.Transition("game over")
|
||||
}
|
||||
if geom.GameGrid.ColouredSquaresRemaining() == 0 {
|
||||
//go to next level
|
||||
newRowSize := min(rand.Intn(5+geom.CurrentLevel)+2, geom.MaxGridDimension)
|
||||
newColSize := min(rand.Intn(5+geom.CurrentLevel)+2, geom.MaxGridDimension)
|
||||
geom.BuildGrid(newRowSize, newColSize)
|
||||
SM.Transition("load")
|
||||
assets.SFXLibrary["newLevel"].Rewind()
|
||||
assets.SFXLibrary["newLevel"].Play()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *PlayLevelState) Draw(screen *ebiten.Image) {
|
||||
geom.GameGrid.Draw(screen)
|
||||
geom.MainPlayer.Draw(screen)
|
||||
score.ScoreMngr.Draw(screen)
|
||||
}
|
||||
54
states/statemachine.go
Normal file
54
states/statemachine.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package states
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
var SM StateMachine
|
||||
var StateMap map[string]State
|
||||
|
||||
type State interface {
|
||||
Enter()
|
||||
Exit()
|
||||
Update()
|
||||
Draw(screen *ebiten.Image)
|
||||
}
|
||||
|
||||
type StateMachine struct {
|
||||
CurrentState State
|
||||
}
|
||||
|
||||
func NewStateMachine(initialState State) *StateMachine {
|
||||
sm := &StateMachine{CurrentState: initialState}
|
||||
sm.CurrentState.Enter()
|
||||
return sm
|
||||
}
|
||||
|
||||
func (sm *StateMachine) Transition(newState string) {
|
||||
sm.CurrentState.Exit()
|
||||
sm.CurrentState = StateMap[newState]
|
||||
sm.CurrentState.Enter()
|
||||
}
|
||||
|
||||
func (sm *StateMachine) Update() {
|
||||
sm.CurrentState.Update()
|
||||
}
|
||||
|
||||
func (sm *StateMachine) Draw(screen *ebiten.Image) {
|
||||
sm.CurrentState.Draw(screen)
|
||||
}
|
||||
|
||||
func SetupStateMachine() {
|
||||
|
||||
loadState := &LoadLevelState{}
|
||||
playState := &PlayLevelState{}
|
||||
gameOverState := &GameOverState{}
|
||||
|
||||
StateMap = make(map[string]State)
|
||||
StateMap["load"] = loadState
|
||||
StateMap["play"] = playState
|
||||
StateMap["game over"] = gameOverState
|
||||
|
||||
SM = *NewStateMachine(loadState)
|
||||
|
||||
}
|
||||
45
tools/scoreManager.go
Normal file
45
tools/scoreManager.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package score
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/text"
|
||||
"golang.org/x/image/font"
|
||||
|
||||
//"golang.org/x/image/font/gofont/gobold"
|
||||
"src.robn.tv/MrDonuts/RGB/assets"
|
||||
geom "src.robn.tv/MrDonuts/RGB/geometry"
|
||||
)
|
||||
|
||||
var ScoreMngr ScoreManager
|
||||
|
||||
type ScoreManager struct{}
|
||||
|
||||
func (sm *ScoreManager) Update() {
|
||||
}
|
||||
|
||||
func (sm *ScoreManager) Draw(screen *ebiten.Image) {
|
||||
|
||||
w, h := ebiten.WindowSize()
|
||||
|
||||
//highScoreStr := fmt.Sprintf("High Score: %v", highScore)
|
||||
levelStr := fmt.Sprintf("Level: %v", geom.CurrentLevel)
|
||||
face := assets.LoadFontFace("fonts/robot.otf", 12)
|
||||
|
||||
//draw high score (i.e. best level)
|
||||
//stringWidth := font.MeasureString(face, highScoreStr).Ceil()
|
||||
//text.Draw(screen, highScoreStr, face, w-stringWidth-geom.HighScoreOffset, h-geom.HighScoreOffset, color.White)
|
||||
|
||||
//draw current level
|
||||
//stringWidth = font.MeasureString(face, levelStr).Ceil()
|
||||
text.Draw(screen, levelStr, face, geom.HighScoreOffset, h-geom.HighScoreOffset, color.White)
|
||||
|
||||
//draw energy at the top
|
||||
energyStr := fmt.Sprintf("Energy: %v", geom.CurrentEnergy)
|
||||
face = assets.LoadFontFace("fonts/robot.otf", 20)
|
||||
stringWidth := font.MeasureString(face, energyStr).Ceil()
|
||||
text.Draw(screen, energyStr, face, w/2-stringWidth/2, geom.ScoreOffset, color.White)
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user