Browse Source

Update win screen

master
Trevor Slocum 1 month ago
parent
commit
5400251f83
  1. 36
      creep.go
  2. 396
      game.go
  3. 8
      go.mod
  4. 24
      go.sum
  5. 41
      level.go
  6. 8
      main.go
  7. 4
      ss_environment.go
  8. 2
      ss_ojas_dungeon.go
  9. 95
      win.go

36
creep.go

@ -14,6 +14,7 @@ const (
TypeVampire = iota
TypeBat
TypeGhost
TypeSoul
TypeTorch
)
@ -67,6 +68,10 @@ func newCreep(creepType int, l *Level, p *gamePlayer) *gameCreep {
imageAtlas[ImageGhost1],
}
startingHealth = 1
} else if creepType == TypeSoul {
sprites = []*ebiten.Image{
ojasDungeonSS.Soul1,
}
} else if creepType == TypeTorch {
sprites = []*ebiten.Image{
sandstoneSS.TorchTop1,
@ -114,8 +119,8 @@ func (c *gameCreep) queueNextAction() {
func (c *gameCreep) runAway() {
c.queueNextAction()
randMovementA := (rand.Float64() - 0.5) / 8
randMovementB := (rand.Float64() - 0.5) / 8
randMovementA := ((rand.Float64() - 0.5) * c.moveSpeed()) / 8
randMovementB := ((rand.Float64() - 0.5) * c.moveSpeed()) / 8
c.moveX = c.x - c.player.x
if c.moveX < 0 {
@ -131,9 +136,20 @@ func (c *gameCreep) runAway() {
}
}
func (c *gameCreep) moveSpeed() float64 {
if c.creepType == TypeSoul {
return 0.5 / 4
}
return 0.4
}
func (c *gameCreep) seekPlayer() {
maxSpeed := 0.5 / 9
minSpeed := 0.1 / 9
maxSpeed := c.moveSpeed() / 9
minSpeed := c.moveSpeed() / 5 / 9
if c.creepType == TypeSoul {
maxSpeed *= 5
}
a := angle(c.x, c.y, c.player.x, c.player.y)
c.moveX = -math.Cos(a)
@ -155,14 +171,15 @@ func (c *gameCreep) seekPlayer() {
c.moveY *= 0.9
}
c.tick = 0
c.nextAction = 1440
}
func (c *gameCreep) doNextAction() {
c.queueNextAction()
randMovementA := (rand.Float64() - 0.5) / 12
randMovementB := (rand.Float64() - 0.5) / 12
randMovementA := ((rand.Float64() - 0.5) * c.moveSpeed()) / 12
randMovementB := ((rand.Float64() - 0.5) * c.moveSpeed()) / 12
if c.creepType == TypeGhost {
c.angle = angle(c.x, c.y, c.player.x, c.player.y)
@ -181,7 +198,7 @@ func (c *gameCreep) doNextAction() {
}
repelled := c.repelled()
if !repelled && rand.Intn(13) == 0 {
if !repelled && rand.Intn(13) == 0 && c.creepType != TypeSoul {
c.seekPlayer()
} else {
c.moveX = randMovementA
@ -201,6 +218,9 @@ func (c *gameCreep) doNextAction() {
}
func (c *gameCreep) repelled() bool {
if c.creepType == TypeSoul {
return false
}
repelled := !c.player.garlicUntil.IsZero() || !c.player.holyWaterUntil.IsZero()
return repelled
}
@ -274,7 +294,7 @@ func (c *gameCreep) Update() {
}
dx, dy := deltaXY(c.x, c.y, c.player.x, c.player.y)
seekDistance := 2.0
seekDistance := 3.5
if !repelled && dx < seekDistance && dy < seekDistance {
c.queueNextAction()
c.seekPlayer()

396
game.go

@ -81,14 +81,16 @@ type game struct {
player *gamePlayer
requiredSouls int
spawnedPortal bool
gameStartTime time.Time
gameOverTime time.Time
gameWon bool
winScreenBackground *ebiten.Image
winScreenSun *ebiten.Image
winScreenSunY float64
winScreenColorScale float64
camScale float64
camScaleTo float64
@ -135,6 +137,7 @@ func NewGame() (*game, error) {
mousePanY: math.MinInt32,
activeGamepad: -1,
forceColorScale: -1,
levelNum: 1,
op: &ebiten.DrawImageOptions{},
}
@ -151,11 +154,6 @@ func NewGame() (*game, error) {
return nil, err
}
err = g.reset()
if err != nil {
return nil, err
}
return g, nil
}
@ -207,9 +205,12 @@ func (g *game) newItem(itemType int) *gameItem {
}
func (g *game) nextLevel() error {
g.player.soulsRescued = 0
g.levelNum++
if g.levelNum > 13 {
log.Fatal("YOU WIN")
if g.levelNum > 3 {
g.showWinScreen()
return nil
}
return g.generateLevel()
}
@ -230,12 +231,14 @@ func (g *game) generateLevel() error {
}
// Position player.
for {
g.player.x = float64(rand.Intn(g.level.w))
g.player.y = float64(rand.Intn(g.level.h))
if g.level.isFloor(g.player.x, g.player.y) {
break
if g.levelNum > 1 {
g.player.x, g.player.y = float64(g.level.enterX), float64(g.level.enterY)+1
} else {
for {
g.player.x, g.player.y = float64(rand.Intn(g.level.w)), float64(rand.Intn(g.level.h))
if g.level.isFloor(g.player.x, g.player.y) {
break
}
}
}
@ -336,6 +339,39 @@ func (g *game) updateCursor() {
ebiten.SetCursorShape(ebiten.CursorShapeCrosshair)
}
func (g *game) handlePlayerDeath() {
if g.player.health > 0 {
return
}
g.player.holyWaters = 0
g.gameOverTime = time.Now()
// Play die sound.
err := g.playSound(SoundPlayerDie, playerDieVolume)
if err != nil {
// TODO return err
panic(err)
}
g.updateCursor()
}
func (g *game) checkLevelComplete() {
if g.player.soulsRescued < g.level.requiredSouls || g.level.exitOpen {
return
}
g.level.exitOpen = true
g.level.tiles[g.level.exitY][g.level.exitX].sprites = nil
g.level.tiles[g.level.exitY][g.level.exitX].AddSprite(sandstoneSS.FloorA)
g.level.tiles[g.level.exitY][g.level.exitX].AddSprite(sandstoneSS.DoorOpen)
// TODO widen doorway
// TODO add trigger entity or hardcode check
}
// Update reads current user input and updates the game state.
func (g *game) Update() error {
gamepadDeadZone := 0.1
@ -393,7 +429,6 @@ func (g *game) Update() error {
g.resetExpiredTimers()
biteThreshold := 0.75
liveCreeps := 0
for _, c := range g.level.creeps {
if c.health == 0 {
@ -406,11 +441,24 @@ func (g *game) Update() error {
continue
}
biteThreshold := 0.75
if c.creepType == TypeSoul {
biteThreshold = 0.25
}
// TODO can this move into creep?
cx, cy := c.Position()
dx, dy := deltaXY(g.player.x, g.player.y, cx, cy)
if dx <= biteThreshold && dy <= biteThreshold {
if !g.godMode && !c.repelled() {
if c.creepType == TypeSoul {
g.player.soulsRescued++
err := g.hurtCreep(c, -1)
if err != nil {
// TODO
panic(err)
}
g.checkLevelComplete()
} else if !g.godMode && !c.repelled() {
if g.player.holyWaters > 0 {
// TODO g.playSound(SoundItemUseHolyWater, useholyWaterVolume)
g.player.holyWaterUntil = time.Now().Add(holyWaterActiveTime)
@ -431,21 +479,8 @@ func (g *game) Update() error {
}
g.addBloodSplatter(g.player.x, g.player.y)
}
}
if g.player.health == 0 {
ebiten.SetCursorShape(ebiten.CursorShapeDefault)
g.player.holyWaters = 0
g.gameOverTime = time.Now()
// Play die sound.
err := g.playSound(SoundPlayerDie, playerDieVolume)
if err != nil {
// TODO return err
panic(err)
g.handlePlayerDeath()
}
}
} else if c.creepType == TypeBat && (dx <= 12 && dy <= 7) && rand.Intn(166) == 6 && time.Since(g.lastBatSound) >= batSoundDelay {
@ -575,6 +610,7 @@ func (g *game) Update() error {
// Update boolets.
bulletHitThreshold := 0.501
bulletSeekThreshold := 2.0
removed := 0
UPDATEPROJECTILES:
for i, p := range g.projectiles {
@ -601,13 +637,16 @@ UPDATEPROJECTILES:
}
for _, c := range g.level.creeps {
if c.health == 0 {
if c.health == 0 || c.creepType == TypeSoul {
continue
}
cx, cy := c.Position()
dx, dy := deltaXY(p.x, p.y, cx, cy)
if dx > bulletHitThreshold || dy > bulletHitThreshold {
if dx < bulletSeekThreshold && dy < bulletSeekThreshold {
c.seekPlayer()
}
continue
}
@ -660,7 +699,7 @@ UPDATEPROJECTILES:
}
// Spawn garlic.
if g.tick%(144*45) == 0 {
if g.tick%(144*45) == 0 || rand.Intn(666) == 0 {
item := g.newItem(itemTypeGarlic)
g.level.items = append(g.level.items, item)
@ -670,7 +709,7 @@ UPDATEPROJECTILES:
}
// Spawn holy water.
if g.tick%(144*120) == 0 || rand.Intn(660) == 0 { // TODO
if g.tick%(144*120) == 0 || rand.Intn(666) == 0 {
item := g.newItem(itemTypeHolyWater)
g.level.items = append(g.level.items, item)
@ -729,14 +768,7 @@ UPDATEPROJECTILES:
}
}
if inpututil.IsKeyJustPressed(ebiten.KeyG) {
g.godMode = !g.godMode
if g.godMode {
g.flashMessage("GOD MODE ACTIVATED")
} else {
g.flashMessage("GOD MODE DEACTIVATED")
}
}
// Read user input.
if inpututil.IsKeyJustPressed(ebiten.KeyM) {
g.muteAudio = !g.muteAudio
if g.muteAudio {
@ -745,88 +777,92 @@ UPDATEPROJECTILES:
g.flashMessage("AUDIO UNMUTED")
}
}
if inpututil.IsKeyJustPressed(ebiten.KeyN) {
g.noclipMode = !g.noclipMode
if g.noclipMode {
g.flashMessage("NOCLIP MODE ACTIVATED")
} else {
g.flashMessage("NOCLIP MODE DEACTIVATED")
}
}
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 < 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(fmt.Sprintf("SPAWNED %d GHOST", spawnAmount))
}
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")
} else {
g.flashMessage("FULLBRIGHT MODE DEACTIVATED")
}
}
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
}
g.flashMessage(fmt.Sprintf("WARPED TO LEVEL %d", g.levelNum))
}
if inpututil.IsKeyJustPressed(ebiten.KeyV) {
g.debugMode = !g.debugMode
if g.debugMode {
g.flashMessage("DEBUG MODE ACTIVATED")
} else {
g.flashMessage("DEBUG MODE DEACTIVATED")
}
}
if inpututil.IsKeyJustPressed(ebiten.KeyP) {
if g.cpuProfile == nil {
g.flashMessage("CPU PROFILING STARTED")
if ebiten.IsKeyPressed(ebiten.KeyControl) {
spawnAmount := 13
switch {
case inpututil.IsKeyJustPressed(ebiten.KeyE):
g.player.x, g.player.y = float64(g.level.exitX), float64(g.level.exitY+1)
g.flashMessage("WARPED TO EXIT")
case inpututil.IsKeyJustPressed(ebiten.KeyF):
g.fullBrightMode = !g.fullBrightMode
if g.fullBrightMode {
g.flashMessage("FULLBRIGHT MODE ACTIVATED")
} else {
g.flashMessage("FULLBRIGHT MODE DEACTIVATED")
}
case inpututil.IsKeyJustPressed(ebiten.KeyG):
g.godMode = !g.godMode
if g.godMode {
g.flashMessage("GOD MODE ACTIVATED")
} else {
g.flashMessage("GOD MODE DEACTIVATED")
}
case inpututil.IsKeyJustPressed(ebiten.KeyN):
g.noclipMode = !g.noclipMode
if g.noclipMode {
g.flashMessage("NOCLIP MODE ACTIVATED")
} else {
g.flashMessage("NOCLIP MODE DEACTIVATED")
}
case inpututil.IsKeyJustPressed(ebiten.KeyV):
g.debugMode = !g.debugMode
if g.debugMode {
g.flashMessage("DEBUG MODE ACTIVATED")
} else {
g.flashMessage("DEBUG MODE DEACTIVATED")
}
case inpututil.IsKeyJustPressed(ebiten.KeyP):
if g.cpuProfile == nil {
g.flashMessage("CPU PROFILING STARTED")
homeDir, err := os.UserHomeDir()
if err != nil {
return err
homeDir, err := os.UserHomeDir()
if err != nil {
return err
}
g.cpuProfile, err = os.Create(path.Join(homeDir, "cartillery.prof"))
if err != nil {
return err
}
if err := pprof.StartCPUProfile(g.cpuProfile); err != nil {
return err
}
} else {
g.flashMessage("CPU PROFILING STOPPED")
pprof.StopCPUProfile()
g.cpuProfile.Close()
g.cpuProfile = nil
}
g.cpuProfile, err = os.Create(path.Join(homeDir, "cartillery.prof"))
if err != nil {
return err
case inpututil.IsKeyJustPressed(ebiten.Key1):
for i := 0; i < spawnAmount; i++ {
g.level.addCreep(TypeVampire)
}
g.flashMessage(fmt.Sprintf("SPAWNED %d VAMPIRES", spawnAmount))
case inpututil.IsKeyJustPressed(ebiten.Key2):
for i := 0; i < spawnAmount; i++ {
g.level.addCreep(TypeBat)
}
g.flashMessage(fmt.Sprintf("SPAWNED %d BATS", spawnAmount))
case inpututil.IsKeyJustPressed(ebiten.Key3):
for i := 0; i < spawnAmount; i++ {
g.level.addCreep(TypeGhost)
}
if err := pprof.StartCPUProfile(g.cpuProfile); err != nil {
g.flashMessage(fmt.Sprintf("SPAWNED %d GHOSTS", spawnAmount))
case inpututil.IsKeyJustPressed(ebiten.Key7):
g.player.holyWaters++
g.flashMessage("SPAWNED HOLY WATER")
case inpututil.IsKeyJustPressed(ebiten.Key8):
// TODO Add garlic to inventory
//g.flashMessage("+ GARLIC")
case ebiten.IsKeyPressed(ebiten.KeyShift) && inpututil.IsKeyJustPressed(ebiten.KeyEqual):
g.showWinScreen()
g.flashMessage("WARPED TO WIN SCREEN")
case inpututil.IsKeyJustPressed(ebiten.KeyEqual):
err := g.nextLevel()
if err != nil {
return err
}
} else {
g.flashMessage("CPU PROFILING STOPPED")
pprof.StopCPUProfile()
g.cpuProfile.Close()
g.cpuProfile = nil
g.flashMessage(fmt.Sprintf("WARPED TO LEVEL %d", g.levelNum))
}
}
@ -834,12 +870,12 @@ UPDATEPROJECTILES:
return nil
}
func (g *game) drawText(target *ebiten.Image, y float64, scale float64, alpha float64, text string) {
func (g *game) drawText(target *ebiten.Image, offsetX float64, y float64, scale float64, alpha float64, text string) {
g.overlayImg.Clear()
ebitenutil.DebugPrint(g.overlayImg, text)
g.op.GeoM.Reset()
g.op.GeoM.Scale(scale, scale)
g.op.GeoM.Translate(float64(g.w/2)-(float64(len(text))*3*scale), y)
g.op.GeoM.Translate(float64(g.w/2)-(float64(len(text))*3*scale)+offsetX, y)
g.op.ColorM.Scale(1, 1, 1, alpha)
target.DrawImage(g.overlayImg, g.op)
g.op.ColorM.Reset()
@ -850,14 +886,14 @@ func (g *game) Draw(screen *ebiten.Image) {
if g.gameStartTime.IsZero() {
screen.Fill(colorBlood)
g.drawText(screen, float64(g.h/2)-350, 16, 1.0, "CAROTID")
g.drawText(screen, float64(g.h/2)-100, 16, 1.0, "ARTILLERY")
g.drawText(screen, 0, float64(g.h/2)-350, 16, 1.0, "CAROTID")
g.drawText(screen, 0, float64(g.h/2)-100, 16, 1.0, "ARTILLERY")
g.drawText(screen, float64(g.h-210), 4, 1.0, "WASD + MOUSE = OK")
g.drawText(screen, float64(g.h-145), 4, 1.0, "FULLSCREEN + GAMEPAD = BEST")
g.drawText(screen, 0, float64(g.h-210), 4, 1.0, "WASD + MOUSE = OK")
g.drawText(screen, 0, float64(g.h-145), 4, 1.0, "FULLSCREEN + GAMEPAD = BEST")
if time.Now().UnixMilli()%2000 < 1500 {
g.drawText(screen, float64(g.h-80), 4, 1.0, "PRESS ANY KEY OR BUTTON TO START")
g.drawText(screen, 0, float64(g.h-80), 4, 1.0, "PRESS ANY KEY OR BUTTON TO START")
}
return
@ -865,6 +901,18 @@ func (g *game) Draw(screen *ebiten.Image) {
var drawn int
if g.gameOverTime.IsZero() || g.gameWon {
if g.gameWon {
g.op.GeoM.Reset()
g.op.ColorM.Reset()
g.op.ColorM.Scale(g.winScreenColorScale, g.winScreenColorScale, g.winScreenColorScale, 1)
screen.DrawImage(g.winScreenBackground, g.op)
g.op.GeoM.Reset()
g.op.GeoM.Translate(float64(g.w)*0.75, g.winScreenSunY)
g.op.ColorM.Reset()
g.op.ColorM.Scale(g.winScreenColorScale, g.winScreenColorScale, g.winScreenColorScale, 1)
screen.DrawImage(g.winScreenSun, g.op)
g.op.ColorM.Reset()
}
drawn = g.renderLevel(screen)
} else {
// Draw game over screen.
@ -882,30 +930,49 @@ func (g *game) Draw(screen *ebiten.Image) {
screen.DrawImage(img, g.op)
g.op.ColorM.Reset()
g.drawText(screen, float64(g.h/2)-150, 16, a, "GAME OVER")
g.drawText(screen, 0, float64(g.h/2)-150, 16, a, "GAME OVER")
if time.Since(g.gameOverTime).Milliseconds()%2000 < 1500 {
g.drawText(screen, 8, 4, a, "PRESS ENTER OR START TO PLAY AGAIN")
g.drawText(screen, 0, 8, 4, a, "PRESS ENTER OR START TO PLAY AGAIN")
}
}
if g.gameOverTime.IsZero() {
// Draw health.
heartSpace := 32
heartX := (g.w / 2) - ((heartSpace * g.player.health) / 2) + 8
heartX := (g.w / 2) - ((heartSpace * g.player.health) / 2)
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)
}
// Draw holy waters.
holyWaterSpace := 16
holyWaterX := (g.w / 2) - ((holyWaterSpace * g.player.holyWaters) / 2)
holyWaterX := (g.w / 2) - ((holyWaterSpace * g.player.holyWaters) / 2) - 8
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)
}
scale := 3.0
soulsY := 104.0
if !g.level.exitOpen {
// Draw souls.
soulsLabel := fmt.Sprintf("%d", g.level.requiredSouls-g.player.soulsRescued)
g.op.GeoM.Reset()
g.op.GeoM.Translate(float64(g.w/2)-(float64(len(soulsLabel))*3*scale)-40, soulsY+8)
screen.DrawImage(ojasDungeonSS.Soul1, g.op)
g.drawText(screen, 0, soulsY, scale, 1.0, soulsLabel)
} else {
// Draw exit message.
// TODO flash text
g.drawText(screen, 0, soulsY, scale, 1.0, "EXIT OPEN")
}
}
flashTime := g.flashMessageUntil.Sub(time.Now())
@ -914,7 +981,7 @@ func (g *game) Draw(screen *ebiten.Image) {
if alpha > 1 {
alpha = 1
}
g.drawText(screen, float64(g.h-40), 2, alpha, g.flashMessageText)
g.drawText(screen, 0, float64(g.h-40), 2, alpha, g.flashMessageText)
}
if !g.gameWon {
@ -923,7 +990,7 @@ func (g *game) Draw(screen *ebiten.Image) {
a = 1
}
scoreLabel := numberPrinter.Sprintf("%d", g.player.score)
g.drawText(screen, float64(g.h-150), 8, a, scoreLabel)
g.drawText(screen, 0, float64(g.h-150), 8, a, scoreLabel)
}
if !g.debugMode {
@ -1014,7 +1081,12 @@ func (g *game) renderLevel(screen *ebiten.Image) int {
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)
a := 1.0
if c.creepType == TypeSoul {
a = 0.35
}
drawn += g.renderSprite(c.x, c.y, 0, 0, c.angle, 1.0, g.colorScale(c.x, c.y), a, c.sprites[c.frame], screen)
if c.frames > 1 && time.Since(c.lastFrame) >= 75*time.Millisecond {
c.frame++
if c.frame == c.frames {
@ -1211,6 +1283,11 @@ func (g *game) hurtCreep(c *gameCreep, damage int) error {
g.addBloodSplatter(c.x, c.y)
soul := g.level.addCreep(TypeSoul)
soul.x, soul.y = c.x, c.y
soul.moveX, soul.moveY = c.moveX/4, c.moveY/4
soul.tick, soul.nextAction = c.tick, c.nextAction
return nil
}
@ -1256,6 +1333,11 @@ func (g *game) showWinScreen() {
if !g.gameOverTime.IsZero() {
return
}
// TODO make it a sunrise instead
g.forceColorScale = 0.4
g.gameWon = true
g.gameOverTime = time.Now()
@ -1267,14 +1349,54 @@ func (g *game) showWinScreen() {
g.level = newWinLevel(g.player)
// TODO move win screen background upward
g.winScreenBackground = ebiten.NewImage(g.w, g.h)
g.winScreenBackground.Fill(colornames.Deepskyblue)
sunSize := 66
g.winScreenSun = ebiten.NewImage(sunSize, sunSize)
g.winScreenSun.Fill(color.RGBA{254, 231, 108, 255})
g.winScreenSunY = float64(g.h/2) + float64(sunSize/2)
go func() {
time.Sleep(10 * time.Second)
// Animate sunrise.
go func() {
time.Sleep(5 * time.Second)
for i := 0; i < 144*15; i++ {
g.winScreenSunY -= 0.035
if i > int(144*3.5) {
g.forceColorScale += 0.0005
if g.forceColorScale > 1 {
g.forceColorScale = 1
}
}
for i := 1.0; i > 0.001; i *= 0.99 {
g.forceColorScale = i
time.Sleep(time.Second / 144)
}
}()
// Fade in sky.
for i := 0.0001; i < 1; i *= 1.005 {
g.winScreenColorScale = i
time.Sleep(time.Second / 144)
}
time.Sleep(5 * time.Second)
// Fade out win screen.
for i := 0; i < 144; i++ {
g.winScreenColorScale -= 0.01
g.forceColorScale -= 0.01
time.Sleep(time.Second / 144)
}
// Fade in game over screen.
g.gameWon = false
defer func() {

8
go.mod

@ -5,16 +5,16 @@ go 1.17
require (
github.com/Meshiest/go-dungeon v0.0.0-20160809210039-1d1d1e7596b8
github.com/hajimehoshi/ebiten/v2 v2.2.1
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
golang.org/x/image v0.0.0-20211028202545-6944b10bf410
golang.org/x/text v0.3.7
)
require (
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/hajimehoshi/oto/v2 v2.1.0-alpha.3 // indirect
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 // indirect
golang.org/x/exp v0.0.0-20211025140241-8418b01e8c3b // indirect
golang.org/x/mobile v0.0.0-20211027134744-eb3c0abee20a // indirect
golang.org/x/exp v0.0.0-20211029153326-98a2daa683e3 // indirect
golang.org/x/mobile v0.0.0-20211029123140-7b8bfe09930c // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect
)

24
go.sum

@ -106,8 +106,9 @@ github.com/hajimehoshi/file2byteslice v0.0.0-20210813153925-5340248a8f41/go.mod
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=
github.com/hajimehoshi/oto/v2 v2.1.0-alpha.2 h1:DV2DcbY3YLuLB9gI9R1GT9TPOo92lUeWveV8ci1sBLk=
github.com/hajimehoshi/oto/v2 v2.1.0-alpha.2/go.mod h1:rUKQmwMkqmRxe+IAof9+tuYA2ofm8cAWXFmSfzDN8vQ=
github.com/hajimehoshi/oto/v2 v2.1.0-alpha.3 h1:vXHLbXL0GpLE9RRUzTeTashvto4mjRF/AAT+0TIHW1M=
github.com/hajimehoshi/oto/v2 v2.1.0-alpha.3/go.mod h1:rUKQmwMkqmRxe+IAof9+tuYA2ofm8cAWXFmSfzDN8vQ=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@ -258,6 +259,7 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
@ -287,16 +289,18 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
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-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/exp v0.0.0-20211029153326-98a2daa683e3 h1:EThhdleNHAYpuHkwXUPv0bjZuNhSMmNrIvCandX6jSU=
golang.org/x/exp v0.0.0-20211029153326-98a2daa683e3/go.mod h1:OyI624f2tQ/aU3IMa7GB16Hk54CHURAfHfj6tMqtyhA=
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=
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs=
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -306,15 +310,16 @@ 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-20211027134744-eb3c0abee20a h1:AVqU+D7dTd7kx8xFcaRktB8LbDTIA29vxySTqiZ7A1w=
golang.org/x/mobile v0.0.0-20211027134744-eb3c0abee20a/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
golang.org/x/mobile v0.0.0-20211029123140-7b8bfe09930c h1:K3U/yO0T6tX3GbBJxRdmhVD/JAQBtDXynUQW38GUujM=
golang.org/x/mobile v0.0.0-20211029123140-7b8bfe09930c/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=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -332,8 +337,10 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -369,8 +376,10 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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/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=
@ -402,6 +411,7 @@ 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/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
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=

41
level.go

@ -25,6 +25,13 @@ type Level struct {
player *gamePlayer
torches []*gameCreep
enterX, enterY int
exitX, exitY int
exitOpen bool
requiredSouls int
}
// Tile returns the tile at the provided coordinates, or nil.
@ -99,6 +106,13 @@ func NewLevel(levelNum int, p *gamePlayer) (*Level, error) {
player: p,
}
l.requiredSouls = 66
if levelNum == 2 {
l.requiredSouls = 666
} else if levelNum == 3 {
l.requiredSouls = 6666
}
var err error
sandstoneSS, err = LoadEnvironmentSpriteSheet()
if err != nil {
@ -149,6 +163,8 @@ func NewLevel(levelNum int, p *gamePlayer) (*Level, error) {
return t.floor
}
var topWalls [][2]int
// Add walls.
for x := 0; x < l.w; x++ {
for y := 0; y < l.h; y++ {
@ -196,6 +212,7 @@ func NewLevel(levelNum int, p *gamePlayer) (*Level, error) {
l.torches = append(l.torches, c)
} else {
neighbor.AddSprite(sandstoneSS.WallTop)
topWalls = append(topWalls, [2]int{nx, ny})
}
case spriteLeft:
if spriteBottom {
@ -218,6 +235,27 @@ func NewLevel(levelNum int, p *gamePlayer) (*Level, error) {
}
}
entrance := topWalls[rand.Intn(len(topWalls))]
exit := entrance
for exit == entrance {
exit = topWalls[rand.Intn(len(topWalls))]
}
l.enterX, l.enterY = entrance[0], entrance[1]
l.exitX, l.exitY = exit[0], exit[1]
if levelNum > 1 {
l.Tile(l.enterX, l.enterY).sprites = nil
l.Tile(l.enterX, l.enterY).AddSprite(sandstoneSS.FloorA)
l.Tile(l.enterX, l.enterY).AddSprite(sandstoneSS.DoorClosed)
}
l.Tile(l.exitX, l.exitY).sprites = nil
l.Tile(l.exitX, l.exitY).AddSprite(sandstoneSS.FloorA)
l.Tile(l.exitX, l.exitY).AddSprite(sandstoneSS.DoorClosed)
// TODO special door for final exit
l.bakeLightmap()
return l, nil
@ -261,9 +299,10 @@ func (l *Level) bakePartialLightmap(lx, ly int) {
}
}
func (l *Level) addCreep(creepType int) {
func (l *Level) addCreep(creepType int) *gameCreep {
c := newCreep(creepType, l, l.player)
l.creeps = append(l.creeps, c)
return c
}
func angle(x1, y1, x2, y2 float64) float64 {

8
main.go

@ -28,7 +28,15 @@ func main() {
log.Fatal(err)
}
err = g.reset()
if err != nil {
panic(err)
}
parseFlags(g)
if !g.debugMode {
g.gameStartTime = time.Time{}
}
sigc := make(chan os.Signal, 1)
signal.Notify(sigc,

4
ss_environment.go

@ -33,6 +33,8 @@ type EnvironmentSpriteSheet struct {
TorchTop8 *ebiten.Image
TorchTop9 *ebiten.Image
TorchMulti *ebiten.Image
DoorOpen *ebiten.Image
DoorClosed *ebiten.Image
}
// LoadEnvironmentSpriteSheet loads the embedded EnvironmentSpriteSheet.
@ -69,6 +71,8 @@ func LoadEnvironmentSpriteSheet() (*EnvironmentSpriteSheet, error) {
s.WallTopLeft = dungeonSpriteAt(2, 3)
s.WallTopRight = dungeonSpriteAt(0, 3)
s.WallPillar = dungeonSpriteAt(8, 4)
s.DoorOpen = dungeonSpriteAt(3, 3)
s.DoorClosed = dungeonSpriteAt(3, 2)
// Prop sprites
propFile, err := assetsFS.Open("assets/sandstone-dungeon/Tiles-Props-pack.png")

2
ss_ojas_dungeon.go

@ -27,6 +27,7 @@ type OjasDungeonSpriteSheet struct {
Grass81 *ebiten.Image
Grass82 *ebiten.Image
Grass91 *ebiten.Image
Soul1 *ebiten.Image
Wall1 *ebiten.Image
Vent1 *ebiten.Image
Door11 *ebiten.Image
@ -71,6 +72,7 @@ func LoadOjasDungeonSpriteSheet() (*OjasDungeonSpriteSheet, error) {
s.Grass81 = spriteAt(14, 11)
s.Grass82 = spriteAt(10, 14)
s.Grass91 = spriteAt(10, 15)
s.Soul1 = spriteAt(8, 11)
s.Wall1 = spriteAt(7, 5)
s.Vent1 = spriteAt(9, 13)
s.Door11 = spriteAt(3, 6)

95
win.go

@ -125,91 +125,105 @@ func newWinLevel(p *gamePlayer) *Level {
l.bakeLightmap()
doorX := float64(startX) - 0.4
p.angle = 0
p.x, p.y = float64(startX), float64(startY)
p.x, p.y = doorX, float64(startY)
go func() {
// Walk away.
for i := 0; i < 144; i++ {
p.x += 0.05 * (float64(144-i) / 144)
for i := 0; i < 36; i++ {
p.x += 0.05
time.Sleep(time.Second / 144)
}
for i := 0; i < 288; i++ {
p.x += 0.05 * (float64(288-i) / 288)
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
time.Sleep(time.Millisecond * 1750)
// 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,
// Throw weapon.
weaponSprite := newCreep(TypeTorch, l, p)
weaponSprite.x, weaponSprite.y = p.x, p.y-0.25
weaponSprite.frames = 1
weaponSprite.frame = 0
weaponSprite.sprites = []*ebiten.Image{
imageAtlas[ImageUzi],
}
p.hasTorch = false
l.creeps = append(l.creeps, torchSprite)
p.weapon = nil
l.creeps = append(l.creeps, weaponSprite)
go func() {
for i := 0; i < 144*3; i++ {
if torchSprite.x < throwEnd {
for i := 0; i < 144*2; i++ {
if weaponSprite.x < doorX {
for i, c := range l.creeps {
if c == torchSprite {
if c == weaponSprite {
l.creeps = append(l.creeps[:i], l.creeps[i+1:]...)
}
}
return
}
torchSprite.x -= 0.05
torchSprite.angle -= .1
weaponSprite.x -= 0.05
if i < 100 {
weaponSprite.y -= 0.005 * (float64(144-i) / 144)
} else {
weaponSprite.y += 0.01 * (float64(288-i) / 288)
}
weaponSprite.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],
// Throw torch.
torchSprite := newCreep(TypeTorch, l, p)
torchSprite.x, torchSprite.y = p.x, p.y-0.25
torchSprite.frames = 1
torchSprite.frame = 0
torchSprite.sprites = []*ebiten.Image{
sandstoneSS.TorchMulti,
}
p.weapon = nil
l.creeps = append(l.creeps, weaponSprite)
p.hasTorch = false
l.creeps = append(l.creeps, torchSprite)
go func() {
for i := 0; i < 144*3; i++ {
if weaponSprite.x < throwEnd {
if torchSprite.x < doorX {
for i, c := range l.creeps {
if c == weaponSprite {
if c == torchSprite {
l.creeps = append(l.creeps[:i], l.creeps[i+1:]...)
}
}
}
weaponSprite.x -= 0.05
weaponSprite.angle -= .1
torchSprite.x -= 0.05
if i < 100 {
torchSprite.y -= 0.005 * (float64(144-i) / 144)
} else {
torchSprite.y += 0.01 * (float64(288-i) / 288)
}
torchSprite.angle -= .1
time.Sleep(time.Second / 144)
}
}()
// Walk away.
time.Sleep(time.Second / 2)
time.Sleep(time.Second)
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++ {
for i := 0; i < 144*15; i++ {
if p.health > 0 {
// Game has restarted.
return
@ -217,13 +231,6 @@ func newWinLevel(p *gamePlayer) *Level {
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

Loading…
Cancel
Save