Metroidvania video game
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

156 lines
3.2 KiB

package game
import (
"fmt"
"os"
"strings"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"code.rocketnine.space/tslocum/doctorlectro/asset"
"code.rocketnine.space/tslocum/doctorlectro/entity"
"code.rocketnine.space/tslocum/doctorlectro/level"
"code.rocketnine.space/tslocum/doctorlectro/system"
"code.rocketnine.space/tslocum/doctorlectro/world"
"code.rocketnine.space/tslocum/gohan"
"github.com/hajimehoshi/ebiten/v2"
"github.com/jakecoffman/cp"
)
const introMessage = `
DOCTOR LECTRO, THE MAD SCIENTIST, HAS TAKEN
CHARGE OF EARTH'S MAGNETIC POLES!
ANIMALS, ELECTRONICS, AND HAIRSTYLES ACROSS
THE GLOBE ARE IN GRAVE DANGER!
STOP DOCTOR LECTRO BEFORE IT IS TOO LATE!
`
type Game struct {
shownIntro bool
}
func NewGame() (*Game, error) {
g := &Game{}
g.reset()
g.addSystems()
return g, nil
}
// addSystems adds all systems to the game. Systems run in the order they are
// added in. Systems do not run in parallel.
func (g *Game) addSystems() {
gohan.AddSystem(system.NewPhysicsSystem())
gohan.AddSystem(system.NewPlayerMoveSystem())
gohan.AddSystem(system.NewMovementSystem())
for layer := world.LayerPlatform; layer <= world.LayerDefault; layer++ {
gohan.AddSystem(system.NewRenderSystem(layer))
}
gohan.AddSystem(system.NewRenderMessageSystem())
gohan.AddSystem(system.NewWinSystem())
gohan.AddSystem(system.NewRenderDebugTextSystem())
}
func (g *Game) reset() {
for _, e := range gohan.AllEntities() {
e.Remove()
}
world.GameOver = false
world.Tick = 0
world.CanJump = false
world.CanMagnetize = false
world.CanFire = false
world.CanDoubleJump = false
world.LastFire = 0
world.MetalRects = nil
world.HazardRects = nil
world.Clinging = false
world.LastWalkDirL = true
world.LastWalkDirU = true
world.Space = cp.NewSpace()
world.Space.Iterations = 1
world.Space.SetGravity(cp.Vector{0, world.Gravity})
world.Player = entity.NewPlayer()
level.LoadMap()
if world.Give != "" {
for _, ability := range strings.Split(world.Give, ",") {
switch strings.ToLower(ability) {
case "jump":
world.CanJump = true
case "magnetize":
world.CanMagnetize = true
case "fire":
world.CanFire = true
case "switch":
world.CanDoubleJump = true
default:
panic(fmt.Sprintf("unknown ability to give player: %s", ability))
}
}
}
}
func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
return world.ScreenWidth, world.ScreenHeight
}
func (g *Game) Update() error {
if ebiten.IsWindowBeingClosed() {
g.Exit()
return nil
}
if world.ResetGame {
g.reset()
world.ResetGame = false
}
if inpututil.IsKeyJustPressed(ebiten.KeyM) {
if asset.SoundMusic.IsPlaying() {
asset.SoundMusic.Pause()
} else {
asset.SoundMusic.Play()
}
}
if world.MessageText != "" {
if ebiten.IsKeyPressed(ebiten.KeyEnter) {
world.MessageText = ""
}
return nil
}
if !world.ShownIntro && world.Tick == 0 {
world.SetMessage(strings.TrimSpace(introMessage))
world.ShownIntro = true
if !world.StartMuted {
asset.SoundMusic.Play()
}
}
return gohan.Update()
}
func (g *Game) Draw(screen *ebiten.Image) {
err := gohan.Draw(screen)
if err != nil {
panic(err)
}
}
func (g *Game) Exit() {
os.Exit(0)
}