Add win screen
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 744 B After Width: | Height: | Size: 744 B |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 633 B After Width: | Height: | Size: 633 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 157 B After Width: | Height: | Size: 157 B |
Before Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 255 KiB After Width: | Height: | Size: 255 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
11
creep.go
|
@ -50,6 +50,7 @@ func newCreep(creepType int, l *Level, p *gamePlayer) *gameCreep {
|
|||
imageAtlas[ImageVampire3],
|
||||
imageAtlas[ImageVampire2],
|
||||
}
|
||||
startingHealth := 1
|
||||
if creepType == TypeBat {
|
||||
sprites = []*ebiten.Image{
|
||||
batSS.Frame1,
|
||||
|
@ -60,10 +61,12 @@ func newCreep(creepType int, l *Level, p *gamePlayer) *gameCreep {
|
|||
batSS.Frame6,
|
||||
batSS.Frame7,
|
||||
}
|
||||
startingHealth = 2
|
||||
} else if creepType == TypeGhost {
|
||||
sprites = []*ebiten.Image{
|
||||
imageAtlas[ImageGhost1],
|
||||
}
|
||||
startingHealth = 1
|
||||
} else if creepType == TypeTorch {
|
||||
sprites = []*ebiten.Image{
|
||||
sandstoneSS.TorchTop1,
|
||||
|
@ -82,7 +85,11 @@ func newCreep(creepType int, l *Level, p *gamePlayer) *gameCreep {
|
|||
startingFrame = rand.Intn(len(sprites))
|
||||
}
|
||||
|
||||
x, y := l.newSpawnLocation()
|
||||
var x, y float64
|
||||
if creepType != TypeTorch {
|
||||
x, y = l.newSpawnLocation()
|
||||
}
|
||||
|
||||
return &gameCreep{
|
||||
creepType: creepType,
|
||||
x: x,
|
||||
|
@ -92,7 +99,7 @@ func newCreep(creepType int, l *Level, p *gamePlayer) *gameCreep {
|
|||
frame: startingFrame,
|
||||
level: l,
|
||||
player: p,
|
||||
health: 1,
|
||||
health: startingHealth,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
//go:build !js || !wasm
|
||||
// +build !js !wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
)
|
||||
|
||||
func parseFlags(g *game) {
|
||||
flag.BoolVar(&g.godMode, "god", false, "Enable God mode")
|
||||
flag.BoolVar(&g.noclipMode, "noclip", false, "Enable noclip mode")
|
||||
flag.BoolVar(&g.fullBrightMode, "fullbright", false, "Enable fullbright mode")
|
||||
flag.BoolVar(&g.debugMode, "debug", false, "Enable debug mode")
|
||||
flag.BoolVar(&g.muteAudio, "mute", false, "Mute audio")
|
||||
flag.Parse()
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package main
|
||||
|
||||
func parseFlags(g *game) {
|
||||
// Do nothing
|
||||
}
|
263
game.go
|
@ -87,6 +87,7 @@ type game struct {
|
|||
gameStartTime time.Time
|
||||
|
||||
gameOverTime time.Time
|
||||
gameWon bool
|
||||
|
||||
camScale float64
|
||||
camScaleTo float64
|
||||
|
@ -113,8 +114,11 @@ type game struct {
|
|||
flashMessageText string
|
||||
flashMessageUntil time.Time
|
||||
|
||||
forceColorScale float64
|
||||
|
||||
godMode bool
|
||||
noclipMode bool
|
||||
muteAudio bool
|
||||
debugMode bool
|
||||
fullBrightMode bool
|
||||
cpuProfile *os.File
|
||||
|
@ -125,11 +129,12 @@ const sampleRate = 44100
|
|||
// NewGame returns a new isometric demo game.
|
||||
func NewGame() (*game, error) {
|
||||
g := &game{
|
||||
camScale: 2,
|
||||
camScaleTo: 2,
|
||||
mousePanX: math.MinInt32,
|
||||
mousePanY: math.MinInt32,
|
||||
activeGamepad: -1,
|
||||
camScale: 2,
|
||||
camScaleTo: 2,
|
||||
mousePanX: math.MinInt32,
|
||||
mousePanY: math.MinInt32,
|
||||
activeGamepad: -1,
|
||||
forceColorScale: -1,
|
||||
|
||||
op: &ebiten.DrawImageOptions{},
|
||||
}
|
||||
|
@ -166,7 +171,12 @@ func (g *game) flashMessage(message string) {
|
|||
func (g *game) loadAssets() error {
|
||||
var err error
|
||||
// Load SpriteSheets.
|
||||
ojasSS, err = LoadPlayerSpriteSheet()
|
||||
ojasDungeonSS, err = LoadOjasDungeonSpriteSheet()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load embedded spritesheet: %s", err)
|
||||
}
|
||||
|
||||
playerSS, err = LoadPlayerSpriteSheet()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load embedded spritesheet: %s", err)
|
||||
}
|
||||
|
@ -268,6 +278,16 @@ func (g *game) reset() error {
|
|||
|
||||
g.levelNum = 1
|
||||
|
||||
g.gameStartTime = time.Now()
|
||||
|
||||
g.gameOverTime = time.Time{}
|
||||
g.gameWon = false
|
||||
|
||||
g.forceColorScale = -1
|
||||
|
||||
g.player.hasTorch = true
|
||||
g.player.weapon = weaponUzi
|
||||
|
||||
err := g.generateLevel()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -295,7 +315,7 @@ func (g *game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
|||
debugBox := image.NewRGBA(image.Rect(0, 0, g.w, 200))
|
||||
g.overlayImg = ebiten.NewImageFromImage(debugBox)
|
||||
}
|
||||
if g.player.weapon.spriteFlipped == nil {
|
||||
if g.player.weapon != nil && g.player.weapon.spriteFlipped == nil {
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Scale(-1, 1)
|
||||
op.GeoM.Translate(32, 0)
|
||||
|
@ -307,6 +327,14 @@ func (g *game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
|||
return g.w, g.h
|
||||
}
|
||||
|
||||
func (g *game) updateCursor() {
|
||||
if g.activeGamepad == -1 || g.gameWon {
|
||||
ebiten.SetCursorMode(ebiten.CursorModeHidden)
|
||||
return
|
||||
}
|
||||
ebiten.SetCursorMode(ebiten.CursorModeVisible)
|
||||
}
|
||||
|
||||
// Update reads current user input and updates the game state.
|
||||
func (g *game) Update() error {
|
||||
gamepadDeadZone := 0.1
|
||||
|
@ -316,15 +344,16 @@ func (g *game) Update() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
if g.player.health <= 0 && !g.godMode {
|
||||
if !g.gameOverTime.IsZero() {
|
||||
if g.gameWon {
|
||||
return nil
|
||||
}
|
||||
// Game over.
|
||||
if ebiten.IsKeyPressed(ebiten.KeyEnter) || (g.activeGamepad != -1 && ebiten.IsStandardGamepadButtonPressed(g.activeGamepad, ebiten.StandardGamepadButtonCenterRight)) {
|
||||
err := g.reset()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.gameOverTime = time.Time{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -345,7 +374,7 @@ func (g *game) Update() error {
|
|||
if ebiten.IsStandardGamepadButtonPressed(id, button) {
|
||||
log.Printf("gamepad activated: %d", id)
|
||||
g.activeGamepad = id
|
||||
ebiten.SetCursorMode(ebiten.CursorModeHidden)
|
||||
g.updateCursor()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -595,7 +624,7 @@ UPDATEPROJECTILES:
|
|||
}
|
||||
|
||||
// Fire boolets.
|
||||
if fire && time.Since(g.player.weapon.lastFire) >= g.player.weapon.cooldown {
|
||||
if fire && g.player.weapon != nil && time.Since(g.player.weapon.lastFire) >= g.player.weapon.cooldown {
|
||||
p := &projectile{
|
||||
x: g.player.x,
|
||||
y: g.player.y,
|
||||
|
@ -715,17 +744,34 @@ UPDATEPROJECTILES:
|
|||
g.flashMessage("NOCLIP MODE DEACTIVATED")
|
||||
}
|
||||
}
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyH) {
|
||||
g.player.holyWaters++
|
||||
g.flashMessage("+ HOLY WATER")
|
||||
spawnAmount := 13
|
||||
if ebiten.IsKeyPressed(ebiten.KeyControl) && inpututil.IsKeyJustPressed(ebiten.Key1) {
|
||||
for i := 0; i < spawnAmount; i++ {
|
||||
g.level.addCreep(TypeVampire)
|
||||
}
|
||||
g.flashMessage(fmt.Sprintf("SPAWNED %d VAMPIRES", spawnAmount))
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyControl) && inpututil.IsKeyJustPressed(ebiten.Key2) {
|
||||
for i := 0; i < 13; i++ {
|
||||
for i := 0; i < spawnAmount; i++ {
|
||||
g.level.addCreep(TypeBat)
|
||||
}
|
||||
g.flashMessage(fmt.Sprintf("SPAWNED %d BATS", spawnAmount))
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyControl) && inpututil.IsKeyJustPressed(ebiten.Key3) {
|
||||
for i := 0; i < spawnAmount; i++ {
|
||||
g.level.addCreep(TypeGhost)
|
||||
}
|
||||
g.flashMessage("+ GHOST")
|
||||
g.flashMessage(fmt.Sprintf("SPAWNED %d GHOST", spawnAmount))
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyControl) && inpututil.IsKeyJustPressed(ebiten.Key6) {
|
||||
if inpututil.IsKeyJustPressed(ebiten.Key7) {
|
||||
g.player.holyWaters++
|
||||
g.flashMessage("SPAWNED HOLY WATER")
|
||||
}
|
||||
if inpututil.IsKeyJustPressed(ebiten.Key8) {
|
||||
// TODO Add garlic to inventory
|
||||
//g.flashMessage("+ GARLIC")
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyControl) && inpututil.IsKeyJustPressed(ebiten.KeyDigit0) {
|
||||
g.fullBrightMode = !g.fullBrightMode
|
||||
if g.fullBrightMode {
|
||||
g.flashMessage("FULLBRIGHT MODE ACTIVATED")
|
||||
|
@ -733,7 +779,10 @@ UPDATEPROJECTILES:
|
|||
g.flashMessage("FULLBRIGHT MODE DEACTIVATED")
|
||||
}
|
||||
}
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyEqual) {
|
||||
if ebiten.IsKeyPressed(ebiten.KeyControl) && ebiten.IsKeyPressed(ebiten.KeyShift) && inpututil.IsKeyJustPressed(ebiten.KeyEqual) {
|
||||
g.showWinScreen()
|
||||
g.flashMessage("WARPED TO WIN SCREEN")
|
||||
} else if ebiten.IsKeyPressed(ebiten.KeyControl) && inpututil.IsKeyJustPressed(ebiten.KeyEqual) {
|
||||
err := g.nextLevel()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -805,41 +854,51 @@ func (g *game) Draw(screen *ebiten.Image) {
|
|||
return
|
||||
}
|
||||
|
||||
gameOver := g.player.health <= 0 && !g.godMode
|
||||
|
||||
var drawn int
|
||||
if !gameOver {
|
||||
if g.gameOverTime.IsZero() || g.gameWon {
|
||||
drawn = g.renderLevel(screen)
|
||||
} else {
|
||||
// Game over.
|
||||
screen.Fill(colorBlood)
|
||||
// Draw game over screen.
|
||||
img := ebiten.NewImage(g.w, g.h)
|
||||
img.Fill(colorBlood)
|
||||
|
||||
g.drawText(screen, float64(g.h/2)-150, 16, 1.0, "GAME OVER")
|
||||
a := g.forceColorScale
|
||||
if a == -1 {
|
||||
a = 1
|
||||
}
|
||||
|
||||
g.op.GeoM.Reset()
|
||||
g.op.ColorM.Reset()
|
||||
g.op.ColorM.Scale(a, a, a, 1)
|
||||
screen.DrawImage(img, g.op)
|
||||
g.op.ColorM.Reset()
|
||||
|
||||
g.drawText(screen, float64(g.h/2)-150, 16, a, "GAME OVER")
|
||||
|
||||
if time.Since(g.gameOverTime).Milliseconds()%2000 < 1500 {
|
||||
g.drawText(screen, 8, 4, 1.0, "PRESS ENTER OR START TO PLAY AGAIN")
|
||||
g.drawText(screen, 8, 4, a, "PRESS ENTER OR START TO PLAY AGAIN")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if g.gameOverTime.IsZero() {
|
||||
heartSpace := 32
|
||||
heartX := (g.w / 2) - ((heartSpace * g.player.health) / 2) + 8
|
||||
for i := 0; i < g.player.health; i++ {
|
||||
g.op.GeoM.Reset()
|
||||
g.op.GeoM.Translate(float64(heartX+(i*heartSpace)), 32)
|
||||
screen.DrawImage(imageAtlas[ImageHeart], g.op)
|
||||
}
|
||||
|
||||
holyWaterSpace := 16
|
||||
holyWaterX := (g.w / 2) - ((holyWaterSpace * g.player.holyWaters) / 2)
|
||||
for i := 0; i < g.player.holyWaters; i++ {
|
||||
g.op.GeoM.Reset()
|
||||
g.op.GeoM.Translate(float64(holyWaterX+(i*holyWaterSpace)), 76)
|
||||
screen.DrawImage(imageAtlas[ImageHolyWater], g.op)
|
||||
}
|
||||
}
|
||||
|
||||
heartSpace := 32
|
||||
heartX := (g.w / 2) - ((heartSpace * g.player.health) / 2) + 8
|
||||
for i := 0; i < g.player.health; i++ {
|
||||
g.op.GeoM.Reset()
|
||||
g.op.GeoM.Translate(float64(heartX+(i*heartSpace)), 32)
|
||||
screen.DrawImage(imageAtlas[ImageHeart], g.op)
|
||||
}
|
||||
|
||||
holyWaterSpace := 16
|
||||
holyWaterX := (g.w / 2) - ((holyWaterSpace * g.player.holyWaters) / 2)
|
||||
for i := 0; i < g.player.holyWaters; i++ {
|
||||
g.op.GeoM.Reset()
|
||||
g.op.GeoM.Translate(float64(holyWaterX+(i*holyWaterSpace)), 76)
|
||||
screen.DrawImage(imageAtlas[ImageHolyWater], g.op)
|
||||
}
|
||||
|
||||
scoreLabel := numberPrinter.Sprintf("%d", g.player.score)
|
||||
g.drawText(screen, float64(g.h-150), 8, 1.0, scoreLabel)
|
||||
|
||||
flashTime := g.flashMessageUntil.Sub(time.Now())
|
||||
if flashTime > 0 {
|
||||
alpha := flashTime.Seconds() * 4
|
||||
|
@ -849,6 +908,15 @@ func (g *game) Draw(screen *ebiten.Image) {
|
|||
g.drawText(screen, float64(g.h-40), 2, alpha, g.flashMessageText)
|
||||
}
|
||||
|
||||
if !g.gameWon {
|
||||
a := g.forceColorScale
|
||||
if a == -1 {
|
||||
a = 1
|
||||
}
|
||||
scoreLabel := numberPrinter.Sprintf("%d", g.player.score)
|
||||
g.drawText(screen, float64(g.h-150), 8, a, scoreLabel)
|
||||
}
|
||||
|
||||
if !g.debugMode {
|
||||
return
|
||||
}
|
||||
|
@ -870,6 +938,10 @@ func (g *game) tilePosition(x, y float64) (float64, float64) {
|
|||
|
||||
// renderSprite renders a sprite on the screen.
|
||||
func (g *game) renderSprite(x float64, y float64, offsetx float64, offsety float64, angle float64, scale float64, colorScale float64, alpha float64, sprite *ebiten.Image, target *ebiten.Image) int {
|
||||
if g.forceColorScale != -1 {
|
||||
colorScale = g.forceColorScale
|
||||
}
|
||||
|
||||
if alpha < .01 || colorScale < .01 {
|
||||
return 0
|
||||
}
|
||||
|
@ -910,7 +982,7 @@ func (g *game) renderSprite(x float64, y float64, offsetx float64, offsety float
|
|||
|
||||
// Calculate color scale to apply shadows.
|
||||
func (g *game) colorScale(x, y float64) float64 {
|
||||
if g.fullBrightMode {
|
||||
if g.gameWon || g.fullBrightMode {
|
||||
return 1
|
||||
}
|
||||
|
||||
|
@ -927,6 +999,24 @@ func (g *game) colorScale(x, y float64) float64 {
|
|||
func (g *game) renderLevel(screen *ebiten.Image) int {
|
||||
var drawn int
|
||||
|
||||
drawCreeps := func() {
|
||||
for _, c := range g.level.creeps {
|
||||
if c.health == 0 && c.creepType != TypeTorch {
|
||||
continue
|
||||
}
|
||||
|
||||
drawn += g.renderSprite(c.x, c.y, 0, 0, c.angle, 1.0, g.colorScale(c.x, c.y), 1.0, c.sprites[c.frame], screen)
|
||||
if c.frames > 1 && time.Since(c.lastFrame) >= 75*time.Millisecond {
|
||||
c.frame++
|
||||
if c.frame == c.frames {
|
||||
c.frame = 0
|
||||
}
|
||||
c.lastFrame = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var t *Tile
|
||||
for y := 0; y < g.level.h; y++ {
|
||||
for x := 0; x < g.level.w; x++ {
|
||||
|
@ -949,19 +1039,8 @@ func (g *game) renderLevel(screen *ebiten.Image) int {
|
|||
drawn += g.renderSprite(item.x, item.y, 0, 0, 0, 1.0, g.colorScale(item.x, item.y), 1.0, item.sprite, screen)
|
||||
}
|
||||
|
||||
for _, c := range g.level.creeps {
|
||||
if c.health == 0 && c.creepType != TypeTorch {
|
||||
continue
|
||||
}
|
||||
|
||||
drawn += g.renderSprite(c.x, c.y, 0, 0, c.angle, 1.0, g.colorScale(c.x, c.y), 1.0, c.sprites[c.frame], screen)
|
||||
if c.frames > 1 && time.Since(c.lastFrame) >= 75*time.Millisecond {
|
||||
c.frame++
|
||||
if c.frame == c.frames {
|
||||
c.frame = 0
|
||||
}
|
||||
c.lastFrame = time.Now()
|
||||
}
|
||||
if !g.gameWon {
|
||||
drawCreeps()
|
||||
}
|
||||
|
||||
for _, p := range g.projectiles {
|
||||
|
@ -995,27 +1074,39 @@ func (g *game) renderLevel(screen *ebiten.Image) int {
|
|||
drawn += g.renderSprite(g.player.x+0.25, g.player.y+0.25, -offset, -offset, 0, scale, 1.0, alpha, imageAtlas[ImageHolyWater], screen)
|
||||
}
|
||||
|
||||
playerSprite := ojasSS.Frame1
|
||||
var weaponSprite *ebiten.Image
|
||||
|
||||
playerSprite := playerSS.Frame1
|
||||
playerAngle := g.player.angle
|
||||
weaponSprite := g.player.weapon.spriteFlipped
|
||||
mul := float64(1)
|
||||
if g.player.weapon != nil {
|
||||
weaponSprite = g.player.weapon.spriteFlipped
|
||||
}
|
||||
if g.player.angle > math.Pi/2 || g.player.angle < -1*math.Pi/2 {
|
||||
playerSprite = ojasSS.Frame2
|
||||
playerSprite = playerSS.Frame2
|
||||
playerAngle = playerAngle - math.Pi
|
||||
weaponSprite = g.player.weapon.sprite
|
||||
mul = -1
|
||||
if g.player.weapon != nil {
|
||||
weaponSprite = g.player.weapon.sprite
|
||||
}
|
||||
}
|
||||
drawn += g.renderSprite(g.player.x, g.player.y, 0, 0, playerAngle, 1.0, 1.0, 1.0, playerSprite, screen)
|
||||
if g.player.weapon != nil {
|
||||
drawn += g.renderSprite(g.player.x, g.player.y, 11*mul, 9, playerAngle, 1.0, 1.0, 1.0, weaponSprite, screen)
|
||||
}
|
||||
drawn += g.renderSprite(g.player.x, g.player.y, -10*mul, 2, playerAngle, 1.0, 1.0, 1.0, sandstoneSS.TorchMulti, screen)
|
||||
if g.player.hasTorch {
|
||||
drawn += g.renderSprite(g.player.x, g.player.y, -10*mul, 2, playerAngle, 1.0, 1.0, 1.0, sandstoneSS.TorchMulti, screen)
|
||||
}
|
||||
|
||||
flashDuration := 40 * time.Millisecond
|
||||
if time.Since(g.player.weapon.lastFire) < flashDuration {
|
||||
if g.player.weapon != nil && time.Since(g.player.weapon.lastFire) < flashDuration {
|
||||
drawn += g.renderSprite(g.player.x, g.player.y, 39, -1, g.player.angle, 1.0, 1.0, 1.0, imageAtlas[ImageMuzzleFlash], screen)
|
||||
}
|
||||
|
||||
if g.gameWon {
|
||||
drawCreeps()
|
||||
}
|
||||
|
||||
return drawn
|
||||
}
|
||||
|
||||
|
@ -1029,6 +1120,10 @@ func (g *game) resetExpiredTimers() {
|
|||
}
|
||||
|
||||
func (g *game) playSound(sound int, volume float64) error {
|
||||
if g.muteAudio {
|
||||
return nil
|
||||
}
|
||||
|
||||
player := soundAtlas[sound][nextSound[sound]]
|
||||
nextSound[sound]++
|
||||
if nextSound[sound] > 3 {
|
||||
|
@ -1148,6 +1243,44 @@ func (g *game) addBloodSplatter(x, y float64) {
|
|||
}
|
||||
}
|
||||
|
||||
func (g *game) showWinScreen() {
|
||||
if !g.gameOverTime.IsZero() {
|
||||
return
|
||||
}
|
||||
g.gameWon = true
|
||||
g.gameOverTime = time.Now()
|
||||
|
||||
g.updateCursor()
|
||||
|
||||
g.player.health = 0
|
||||
|
||||
g.level = newWinLevel(g.player)
|
||||
|
||||
go func() {
|
||||
time.Sleep(10 * time.Second)
|
||||
|
||||
for i := 1.0; i > 0.001; i *= 0.99 {
|
||||
g.forceColorScale = i
|
||||
time.Sleep(time.Second / 144)
|
||||
}
|
||||
|
||||
g.gameWon = false
|
||||
|
||||
defer func() {
|
||||
g.forceColorScale = -1
|
||||
g.updateCursor()
|
||||
}()
|
||||
|
||||
for i := 0.01; i < 1; i *= 1.02 {
|
||||
if g.player.health > 0 {
|
||||
return
|
||||
}
|
||||
g.forceColorScale = i
|
||||
time.Sleep(time.Second / 144)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (g *game) exit() {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
|
9
go.mod
|
@ -10,12 +10,11 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be // indirect
|
||||
github.com/hajimehoshi/go-mp3 v0.3.2 // indirect
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be // indirect
|
||||
github.com/hajimehoshi/oto/v2 v2.1.0-alpha.2 // indirect
|
||||
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 // indirect
|
||||
golang.org/x/exp v0.0.0-20211012155715-ffe10e552389 // indirect
|
||||
golang.org/x/mobile v0.0.0-20210924032853-1c027f395ef7 // indirect
|
||||
golang.org/x/exp v0.0.0-20211025140241-8418b01e8c3b // indirect
|
||||
golang.org/x/mobile v0.0.0-20211027134744-eb3c0abee20a // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect
|
||||
)
|
||||
|
|
17
go.sum
|
@ -58,8 +58,9 @@ github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2
|
|||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be h1:vEIVIuBApEBQTEJt19GfhoU+zFSV+sNTa9E9FdnRYfk=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be h1:Z28GdQBfKOL8tNHjvaDn3wHDO7AzTRkmAXvHvnopp98=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
||||
|
@ -102,7 +103,6 @@ github.com/hajimehoshi/bitmapfont/v2 v2.1.3/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zE
|
|||
github.com/hajimehoshi/ebiten/v2 v2.2.1 h1:YhITMaBQmnwb4kzAXCCfSkSZmeQX7pfQ7BnC32cnPjc=
|
||||
github.com/hajimehoshi/ebiten/v2 v2.2.1/go.mod h1:olKl/qqhMBBAm2oI7Zy292nCtE+nitlmYKNF3UpbFn0=
|
||||
github.com/hajimehoshi/file2byteslice v0.0.0-20210813153925-5340248a8f41/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.2 h1:xSYNE2F3lxtOu9BRjCWHHceg7S91IHfXfXp5+LYQI7s=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.2/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
|
||||
github.com/hajimehoshi/oto v0.6.1 h1:7cJz/zRQV4aJvMSSRqzN2TImoVVMpE0BCY4nrNJaDOM=
|
||||
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
|
||||
|
@ -290,8 +290,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
|||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/exp v0.0.0-20211012155715-ffe10e552389 h1:qFfBYVpJAdBCk6Nmd7ZbcyhGmKmv8fps+OyoOfpjvu8=
|
||||
golang.org/x/exp v0.0.0-20211012155715-ffe10e552389/go.mod h1:a3o/VtDNHN+dCVLEpzjjUHOzR+Ln3DHX056ZPzoZGGA=
|
||||
golang.org/x/exp v0.0.0-20211025140241-8418b01e8c3b h1:AHIXEGqpPGg/yJCO803Q+UbBiq/vkCe/OPwMSVE7psI=
|
||||
golang.org/x/exp v0.0.0-20211025140241-8418b01e8c3b/go.mod h1:a3o/VtDNHN+dCVLEpzjjUHOzR+Ln3DHX056ZPzoZGGA=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
|
@ -306,8 +306,8 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU
|
|||
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
|
||||
golang.org/x/mobile v0.0.0-20210902104108-5d9a33257ab5/go.mod h1:c4YKU3ZylDmvbw+H/PSvm42vhdWbuxCzbonauEAP9B8=
|
||||
golang.org/x/mobile v0.0.0-20210924032853-1c027f395ef7 h1:CyFUjc175y/mbMjxe+WdqI72jguLyjQChKCDe9mfTvg=
|
||||
golang.org/x/mobile v0.0.0-20210924032853-1c027f395ef7/go.mod h1:c4YKU3ZylDmvbw+H/PSvm42vhdWbuxCzbonauEAP9B8=
|
||||
golang.org/x/mobile v0.0.0-20211027134744-eb3c0abee20a h1:AVqU+D7dTd7kx8xFcaRktB8LbDTIA29vxySTqiZ7A1w=
|
||||
golang.org/x/mobile v0.0.0-20211027134744-eb3c0abee20a/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
|
@ -371,8 +371,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
|
@ -401,6 +401,7 @@ golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapK
|
|||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
4
img.go
|
@ -32,11 +32,11 @@ var imageMap = map[int]string{
|
|||
ImageGhost2: "assets/creeps/ghost/ghost2.png",
|
||||
ImageGhost2R: "assets/creeps/ghost/ghost2r.png",
|
||||
ImageGarlic: "assets/items/garlic.png",
|
||||
ImageHolyWater: "assets/items/holywater.png",
|
||||
ImageHolyWater: "assets/items/holy-water.png",
|
||||
ImageHeart: "assets/ui/heart.png",
|
||||
ImageUzi: "assets/weapons/uzi.png",
|
||||
ImageBullet: "assets/weapons/bullet.png",
|
||||
ImageMuzzleFlash: "assets/weapons/flash.png",
|
||||
ImageMuzzleFlash: "assets/weapons/muzzle-flash.png",
|
||||
}
|
||||
|
||||
var imageAtlas = loadAtlas()
|
||||
|
|
2
main.go
|
@ -28,6 +28,8 @@ func main() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
parseFlags(g)
|
||||
|
||||
sigc := make(chan os.Signal, 1)
|
||||
signal.Notify(sigc,
|
||||
syscall.SIGINT,
|
||||
|
|
15
player.go
|
@ -4,6 +4,11 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
var weaponUzi = &playerWeapon{
|
||||
sprite: imageAtlas[ImageUzi],
|
||||
cooldown: 100 * time.Millisecond,
|
||||
}
|
||||
|
||||
type gamePlayer struct {
|
||||
x, y float64
|
||||
|
||||
|
@ -11,6 +16,8 @@ type gamePlayer struct {
|
|||
|
||||
weapon *playerWeapon
|
||||
|
||||
hasTorch bool
|
||||
|
||||
score int
|
||||
|
||||
soulsRescued int
|
||||
|
@ -25,11 +32,9 @@ type gamePlayer struct {
|
|||
|
||||
func NewPlayer() (*gamePlayer, error) {
|
||||
p := &gamePlayer{
|
||||
weapon: &playerWeapon{
|
||||
sprite: imageAtlas[ImageUzi],
|
||||
cooldown: 100 * time.Millisecond,
|
||||
},
|
||||
health: 3,
|
||||
weapon: weaponUzi,
|
||||
hasTorch: true,
|
||||
health: 3,
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ func LoadEnvironmentSpriteSheet() (*EnvironmentSpriteSheet, error) {
|
|||
s := &EnvironmentSpriteSheet{}
|
||||
|
||||
// Dungeon sprites
|
||||
dungeonFile, err := assetsFS.Open("assets/sandstone-dungeon/Tiles-SandstoneDungeons.png")
|
||||
dungeonFile, err := assetsFS.Open("assets/sandstone-dungeon/Tiles-Sandstone-Dungeons.png")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"image"
|
||||
_ "image/png"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
var ojasDungeonSS *OjasDungeonSpriteSheet
|
||||
|
||||
// OjasDungeonSpriteSheet represents a collection of sprite images.
|
||||
type OjasDungeonSpriteSheet struct {
|
||||
Grass11 *ebiten.Image
|
||||
Grass12 *ebiten.Image
|
||||
Grass13 *ebiten.Image
|
||||
Grass14 *ebiten.Image
|
||||
Grass15 *ebiten.Image
|
||||
Grass16 *ebiten.Image
|
||||
Grass21 *ebiten.Image
|
||||
Grass31 *ebiten.Image
|
||||
Grass41 *ebiten.Image
|
||||
Grass42 *ebiten.Image
|
||||
Grass51 *ebiten.Image
|
||||
Grass61 *ebiten.Image
|
||||
Grass71 *ebiten.Image
|
||||
Grass81 *ebiten.Image
|
||||
Grass82 *ebiten.Image
|
||||
Grass91 *ebiten.Image
|
||||
Wall1 *ebiten.Image
|
||||
Vent1 *ebiten.Image
|
||||
Door11 *ebiten.Image
|
||||
Door12 *ebiten.Image
|
||||
}
|
||||
|
||||
// LoadOjasDungeonSpriteSheet loads the embedded PlayerSpriteSheet.
|
||||
func LoadOjasDungeonSpriteSheet() (*OjasDungeonSpriteSheet, error) {
|
||||
tileSize := 32
|
||||
|
||||
f, err := assetsFS.Open("assets/ojas-dungeon/dungeon-tileset-1.png")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img, _, err := image.Decode(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sheet := ebiten.NewImageFromImage(img)
|
||||
|
||||
// spriteAt returns a sprite at the provided coordinates.
|
||||
spriteAt := func(x, y int) *ebiten.Image {
|
||||
return sheet.SubImage(image.Rect(x*tileSize, (y+1)*tileSize, (x+1)*tileSize, y*tileSize)).(*ebiten.Image)
|
||||
}
|
||||
|
||||
// Populate spritesheet.
|
||||
s := &OjasDungeonSpriteSheet{}
|
||||
s.Grass11 = spriteAt(7, 7)
|
||||
s.Grass12 = spriteAt(8, 7)
|
||||
s.Grass13 = spriteAt(9, 7)
|
||||
s.Grass14 = spriteAt(10, 7)
|
||||
s.Grass15 = spriteAt(11, 7)
|
||||
s.Grass16 = spriteAt(12, 7)
|
||||
s.Grass21 = spriteAt(10, 8)
|
||||
s.Grass31 = spriteAt(10, 9)
|
||||
s.Grass41 = spriteAt(15, 9)
|
||||
s.Grass42 = spriteAt(10, 10)
|
||||
s.Grass51 = spriteAt(10, 11)
|
||||
s.Grass61 = spriteAt(10, 12)
|
||||
s.Grass71 = spriteAt(10, 13)
|
||||
s.Grass81 = spriteAt(14, 11)
|
||||
s.Grass82 = spriteAt(10, 14)
|
||||
s.Grass91 = spriteAt(10, 15)
|
||||
s.Wall1 = spriteAt(7, 5)
|
||||
s.Vent1 = spriteAt(9, 13)
|
||||
s.Door11 = spriteAt(3, 6)
|
||||
s.Door12 = spriteAt(3, 7)
|
||||
|
||||
return s, nil
|
||||
}
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
var ojasSS *PlayerSpriteSheet
|
||||
var playerSS *PlayerSpriteSheet
|
||||
|
||||
// PlayerSpriteSheet represents a collection of sprite images.
|
||||
type PlayerSpriteSheet struct {
|
||||
|
@ -23,7 +23,7 @@ var assetsFS embed.FS
|
|||
func LoadPlayerSpriteSheet() (*PlayerSpriteSheet, error) {
|
||||
tileSize := 32
|
||||
|
||||
f, err := assetsFS.Open("assets/ojas-dungeon/character_run.png")
|
||||
f, err := assetsFS.Open("assets/ojas-dungeon/character-run.png")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,230 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
func newWinLevel(p *gamePlayer) *Level {
|
||||
l := &Level{
|
||||
w: 256,
|
||||
h: 256,
|
||||
tileSize: 32,
|
||||
player: p,
|
||||
}
|
||||
|
||||
startX, startY := 108, 108
|
||||
|
||||
grid := make([][][]*ebiten.Image, l.w)
|
||||
for x := 0; x < l.w; x++ {
|
||||
grid[x] = make([][]*ebiten.Image, l.h)
|
||||
}
|
||||
|
||||
// Add ground.
|
||||
var lastBones int
|
||||
bonesDistance := 7
|
||||
for x := 0; x < l.w; x++ {
|
||||
excludeBones := x-lastBones < bonesDistance
|
||||
|
||||
r := rand.Intn(33)
|
||||
switch r {
|
||||
case 0:
|
||||
grid[x][startY] = []*ebiten.Image{
|
||||
ojasDungeonSS.Grass11,
|
||||
}
|
||||
case 1:
|
||||
grid[x][startY] = []*ebiten.Image{
|
||||
ojasDungeonSS.Grass12,
|
||||
}
|
||||
case 2:
|
||||
grid[x][startY] = []*ebiten.Image{
|
||||
ojasDungeonSS.Grass13,
|
||||
}
|
||||
case 3:
|
||||
grid[x][startY] = []*ebiten.Image{
|
||||
ojasDungeonSS.Grass14,
|
||||
}
|
||||
case 4:
|
||||
grid[x][startY] = []*ebiten.Image{
|
||||
ojasDungeonSS.Grass15,
|
||||
}
|
||||
case 5:
|
||||
grid[x][startY] = []*ebiten.Image{
|
||||
ojasDungeonSS.Grass16,
|
||||
}
|
||||
}
|
||||
grid[x][startY+1] = []*ebiten.Image{
|
||||
ojasDungeonSS.Grass21,
|
||||
}
|
||||
grid[x][startY+2] = []*ebiten.Image{
|
||||
ojasDungeonSS.Grass31,
|
||||
}
|
||||
if rand.Intn(33) != 0 || excludeBones {
|
||||
grid[x][startY+3] = []*ebiten.Image{
|
||||
ojasDungeonSS.Grass41,
|
||||
}
|
||||
} else {
|
||||
grid[x][startY+3] = []*ebiten.Image{
|
||||
ojasDungeonSS.Grass42,
|
||||
}
|
||||
}
|
||||
grid[x][startY+4] = []*ebiten.Image{
|
||||
ojasDungeonSS.Grass51,
|
||||
}
|
||||
grid[x][startY+5] = []*ebiten.Image{
|
||||
ojasDungeonSS.Grass61,
|
||||
}
|
||||
grid[x][startY+6] = []*ebiten.Image{
|
||||
ojasDungeonSS.Grass71,
|
||||
}
|
||||
if rand.Intn(33) != 0 || excludeBones {
|
||||
grid[x][startY+7] = []*ebiten.Image{
|
||||
ojasDungeonSS.Grass81,
|
||||
}
|
||||
} else {
|
||||
grid[x][startY+7] = []*ebiten.Image{
|
||||
ojasDungeonSS.Grass82,
|
||||
}
|
||||
}
|
||||
grid[x][startY+8] = []*ebiten.Image{
|
||||
ojasDungeonSS.Grass91,
|
||||
}
|
||||
}
|
||||
|
||||
// Add dungeon.
|
||||
for x := 0; x < 64; x++ {
|
||||
for y := 0; y < 64; y++ {
|
||||
grid[startX-x-1][startY-y] = []*ebiten.Image{
|
||||
ojasDungeonSS.Wall1,
|
||||
}
|
||||
|
||||
if y == 0 && x%8 == 0 {
|
||||
grid[startX-x-1][startY-y] = append(grid[startX-x-1][startY-y], ojasDungeonSS.Vent1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grid[startX-1][startY-1] = append(grid[startX-1][startY-1], ojasDungeonSS.Door11)
|
||||
grid[startX-1][startY] = append(grid[startX-1][startY], ojasDungeonSS.Door12)
|
||||
|
||||
// Add sprites to tiles.
|
||||
l.tiles = make([][]*Tile, l.h)
|
||||
for y := 0; y < l.h; y++ {
|
||||
l.tiles[y] = make([]*Tile, l.w)
|
||||
for x := 0; x < l.w; x++ {
|
||||
t := &Tile{}
|
||||
for _, sprite := range grid[x][y] {
|
||||
t.AddSprite(sprite)
|
||||
}
|
||||
l.tiles[y][x] = t
|
||||
}
|
||||
}
|
||||
|
||||
l.bakeLightmap()
|
||||
|
||||
p.angle = 0
|
||||
p.x, p.y = float64(startX), float64(startY)
|
||||
|
||||
go func() {
|
||||
// Walk away.
|
||||
for i := 0; i < 144; i++ {
|
||||
p.x += 0.05 * (float64(144-i) / 144)
|
||||
time.Sleep(time.Second / 144)
|
||||
}
|
||||
time.Sleep(time.Second / 2)
|
||||
|
||||
// Turn around.
|
||||
p.angle = math.Pi
|
||||
time.Sleep(time.Second / 2)
|
||||
|
||||
throwEnd := float64(startX) - 0.4
|
||||
|
||||
// Throw torch.
|
||||
torchSprite := newCreep(TypeTorch, l, p)
|
||||
torchSprite.x, torchSprite.y = p.x, p.y
|
||||
torchSprite.frames = 1
|
||||
torchSprite.frame = 0
|
||||
torchSprite.sprites = []*ebiten.Image{
|
||||
sandstoneSS.TorchMulti,
|
||||
}
|
||||
|
||||
p.hasTorch = false
|
||||
l.creeps = append(l.creeps, torchSprite)
|
||||
|
||||
go func() {
|
||||
for i := 0; i < 144*3; i++ {
|
||||
if torchSprite.x < throwEnd {
|
||||
for i, c := range l.creeps {
|
||||
if c == torchSprite {
|
||||
l.creeps = append(l.creeps[:i], l.creeps[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
torchSprite.x -= 0.05
|
||||
torchSprite.angle -= .1
|
||||
time.Sleep(time.Second / 144)
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(time.Second / 2)
|
||||
|
||||
// Throw weapon.
|
||||
weaponSprite := newCreep(TypeTorch, l, p)
|
||||
weaponSprite.x, weaponSprite.y = p.x, p.y
|
||||
weaponSprite.frames = 1
|
||||
weaponSprite.frame = 0
|
||||
weaponSprite.sprites = []*ebiten.Image{
|
||||
imageAtlas[ImageUzi],
|
||||
}
|
||||
|
||||
p.weapon = nil
|
||||
l.creeps = append(l.creeps, weaponSprite)
|
||||
|
||||
go func() {
|
||||
for i := 0; i < 144*3; i++ {
|
||||
if weaponSprite.x < throwEnd {
|
||||
for i, c := range l.creeps {
|
||||
if c == weaponSprite {
|
||||
l.creeps = append(l.creeps[:i], l.creeps[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
weaponSprite.x -= 0.05
|
||||
weaponSprite.angle -= .1
|
||||
time.Sleep(time.Second / 144)
|
||||
}
|
||||
}()
|
||||
|
||||
// Walk away.
|
||||
time.Sleep(time.Second / 2)
|
||||
|
||||
p.angle = 0
|
||||
time.Sleep(time.Second / 2)
|
||||
for i := 0; i < 144; i++ {
|
||||
p.x += 0.05 * (float64(i) / 144)
|
||||
time.Sleep(time.Second / 144)
|
||||
}
|
||||
for i := 0; i < 144*12; i++ {
|
||||
if p.health > 0 {
|
||||
// Game has restarted.
|
||||
return
|
||||
}
|
||||
p.x += 0.05
|
||||
time.Sleep(time.Second / 144)
|
||||
}
|
||||
|
||||
return
|
||||
t := time.NewTicker(time.Second / 144)
|
||||
for range t.C {
|
||||
p.x += 0.05
|
||||
p.angle += 0.1
|
||||
}
|
||||
}()
|
||||
|
||||
return l
|
||||
}
|