diff --git a/main.go b/main.go index 2a4aa47..85584b4 100644 --- a/main.go +++ b/main.go @@ -85,7 +85,7 @@ func mainLoop(style style) { if event.Key == termbox.KeyCtrlC { break } - newScreenIndex = displayScreen.handleKeyEvent(event) + newScreenIndex = displayScreen.handleKeyPress(event) if newScreenIndex < len(screens) { displayScreen = screens[newScreenIndex] } else if newScreenIndex == exitScreenIndex { diff --git a/screen.go b/screen.go index 577f3d5..dc173b4 100644 --- a/screen.go +++ b/screen.go @@ -7,7 +7,7 @@ import ( // Screen TODO: Comment type Screen interface { - handleKeyEvent(event termbox.Event) int + handleKeyPress(event termbox.Event) int performLayout(style style) drawScreen(style style) update() diff --git a/screen_main.go b/screen_main.go index 87b2cef..9ce0e2e 100644 --- a/screen_main.go +++ b/screen_main.go @@ -14,53 +14,90 @@ const ( modeInit = iota modeRun modeRunInput + modePause ) -type player struct { - x, y int - projSpeed int - projAngle int - screen *mainScreen -} - -func (p *player) draw() { - drawGopher(p.x, p.y, termbox.ColorCyan) -} - -type building struct { - width, height int - color termbox.Attribute - windowsOn []int -} - -func (b *building) draw(x int) { - // Fill - for i := 0; i < b.height; i++ { - for j := 0; j < b.width; j++ { - c := b.color - /* - if i > 0 && i < b.height && j > 0 && j < b.width { - if i%2 == 1 && j%2 == 1 { - c = termbox.ColorBlack - } - } - */ - termbox.SetCell(x+j, ScreenHeight-i, ' ', c, c) - } - } -} - type mainScreen struct { GameMode int PlayerTurn int - tabIdx int - Player1 player - Player2 player + Player1 *player + Player2 *player + Bullet *projectile Buildings []building + PauseMenu *termboxUtil.Menu + r *rand.Rand + animating bool + gravity float32 } -func (screen *mainScreen) handleKeyEvent(event termbox.Event) int { +func (screen *mainScreen) handleKeyPress(event termbox.Event) int { + if event.Key == termbox.KeyEsc { + if screen.GameMode != modePause { + screen.GameMode = modePause + } else { + screen.GameMode = modeRun + } + } + if screen.GameMode == modePause { + screen.PauseMenu.HandleKeyPress(event) + if screen.PauseMenu.IsDone() { + selOpt := screen.PauseMenu.GetSelectedOption() + if selOpt.GetText() == "Exit" { + return exitScreenIndex + } else if selOpt.GetText() == "Resume" { + screen.GameMode = modeRun + } else if selOpt.GetText() == "Restart" { + //screen.GameMode = modeInit + //return titleScreenIndex + } + } + } if screen.GameMode == modeRunInput { + if event.Key == termbox.KeySpace { + if screen.PlayerTurn == 1 { + screen.Bullet.angle = screen.Player1.projAngle + screen.Bullet.speed = screen.Player1.projSpeed + screen.Bullet.x, screen.Bullet.y = screen.Player1.GetBulletHandPos() + } else { + screen.Bullet.angle = 180 - screen.Player2.projAngle + screen.Bullet.speed = screen.Player2.projSpeed + screen.Bullet.x, screen.Bullet.y = screen.Player2.GetBulletHandPos() + } + screen.animating = true + screen.GameMode = modeRun + } else if event.Key == termbox.KeyArrowLeft || event.Ch == 'a' { + if screen.PlayerTurn == 1 { + if screen.Player1.projAngle < 180 { + screen.Player1.projAngle++ + } + } else { + if screen.Player2.projAngle > 0 { + screen.Player2.projAngle-- + } + } + } else if event.Key == termbox.KeyArrowRight || event.Ch == 'd' { + if screen.PlayerTurn == 1 { + if screen.Player1.projAngle > 0 { + screen.Player1.projAngle-- + } + } else { + if screen.Player2.projAngle > 180 { + screen.Player2.projAngle++ + } + } + } else if event.Key == termbox.KeyArrowUp || event.Ch == 'w' { + if screen.PlayerTurn == 1 { + screen.Player1.projSpeed++ + } else { + screen.Player2.projSpeed++ + } + } else if event.Key == termbox.KeyArrowDown || event.Ch == 's' { + if screen.PlayerTurn == 1 { + screen.Player1.projSpeed-- + } else { + screen.Player2.projSpeed-- + } + } } return mainScreenIndex } @@ -68,14 +105,19 @@ func (screen *mainScreen) handleKeyEvent(event termbox.Event) int { func (screen *mainScreen) performLayout(style style) { var bldCity int if screen.GameMode == modeInit { + b := projectile{} + screen.Bullet = &b + // TODO: Reset Buildings + //screen.Buildings = []building{} // Create a random seed (from time) seed := fmt.Sprintf("%d", time.Now().UnixNano()) - r := rand.New(rand.NewSource(getSeedFromString(seed + "-world"))) + screen.r = rand.New(rand.NewSource(getSeedFromString(seed + "-world"))) + screen.PlayerTurn = screen.r.Intn(1) + 1 for bldCity < ScreenWidth { - w := r.Intn(8) + 5 - h := r.Intn(ScreenHeight-10) + 2 + w := screen.r.Intn(8) + 6 + h := screen.r.Intn(ScreenHeight-10) + 2 var c termbox.Attribute - switch r.Intn(3) { + switch screen.r.Intn(3) { case 0: c = termbox.ColorRed case 1: @@ -83,52 +125,65 @@ func (screen *mainScreen) performLayout(style style) { case 2: c = termbox.ColorBlue } + if ScreenWidth-(bldCity+w) < 5 { + w += ScreenWidth - (bldCity + w) + } b := building{width: w, height: h, color: c} screen.Buildings = append(screen.Buildings, b) bldCity += w } - // Player 1 should be on the first 1/3 of the screen - p1x := r.Intn(ScreenWidth / 3) - p1y := 0 - // Make sure that p1x is fully on a building - // Find the building for the player's x - var calcX int - for i := range screen.Buildings { - calcX += screen.Buildings[i].width - if calcX > p1x { - if calcX > p1x+5 { - mv := (p1x + 5) - calcX - p1x -= mv - } - p1y = ScreenHeight - screen.Buildings[i].height - break - } + // Player 1 should be on the first 1/3 of the buildings + bld := screen.r.Intn(len(screen.Buildings) / 3) + p1x, p1y := screen.getRndPosForBuilding(bld) + screen.Player1 = createPlayer(p1x, p1y) + + // Player 2 should be on the third 1/3 of the buildings + bld = screen.r.Intn(len(screen.Buildings)/3) + ((len(screen.Buildings) * 2) / 3) + p2x, p2y := screen.getRndPosForBuilding(bld) + if ScreenWidth-p2x < 5 { + p2x, p2y = screen.getRndPosForBuilding(bld - 1) } - screen.Player1 = player{x: p1x, y: p1y, screen: screen} - // Player 2 should be on the third 1/3 of the screen - p2x := r.Intn(ScreenWidth/3) + ((ScreenWidth * 2) / 3) - p2y := 0 - for i := range screen.Buildings { - calcX += screen.Buildings[i].width - if calcX > p1x { - if calcX > p2x+5 { - mv := (p2x + 5) - calcX - p2x -= mv - } - p2y = ScreenHeight - screen.Buildings[i].height - break - } - } - // Make sure that p2x is fully on a building - screen.Player2 = player{x: p2x, y: p2y, screen: screen} + screen.Player2 = createPlayer(p2x, p2y) + + screen.PauseMenu = termboxUtil.CreateMenu("** GOPHER BATTLE **", + []string{"Resume", "Restart", "Exit"}, + (ScreenWidth/2)-11, (ScreenHeight/2)-3, 22, 6, + style.defaultFg, style.defaultBg) + screen.PauseMenu.SetBordered(true) + screen.GameMode = modeRun } - screen.GameMode = modeRun +} + +func createPlayer(px, py int) *player { + p := player{x: px, y: py, projSpeed: 10, projAngle: 45, baseColor: termbox.ColorCyan} + return &p } // Trigger all updates func (screen *mainScreen) update() { if screen.GameMode == modeRun { - + if !screen.animating { + screen.GameMode = modeRunInput + } else { + if screen.Bullet.x > 0 && screen.Bullet.x < ScreenWidth { + // Figure out where the bullet should move to based + // on its angle and speed + if screen.Bullet.angle < 35 { + screen.Bullet.x++ + } else if screen.Bullet.angle < 90 { + screen.Bullet.x++ + screen.Bullet.y++ + } else if screen.Bullet.angle < 125 { + screen.Bullet.x-- + screen.Bullet.y-- + } else { + screen.Bullet.x-- + } + } else { + // Bullet went off the sides of the screen + screen.animating = false + } + } } } @@ -145,25 +200,44 @@ func (screen *mainScreen) drawScreen(style style) { } if screen.GameMode == modeRun { // Draw Projectile(s) + if screen.Bullet != nil { + screen.Bullet.draw() + } } else if screen.GameMode == modeRunInput { // Draw inputs if screen.PlayerTurn == 1 { - termboxUtil.DrawStringAtPoint("Angle:", 0, 0, style.defaultFg, style.defaultBg) - termboxUtil.DrawStringAtPoint("Speed:", 0, 1, style.defaultFg, style.defaultBg) + angleText := fmt.Sprintf("Angle: %d", screen.Player1.projAngle) + speedText := fmt.Sprintf("Speed: %d", screen.Player1.projSpeed) + termboxUtil.DrawStringAtPoint(angleText, 0, 0, style.defaultFg, style.defaultBg) + termboxUtil.DrawStringAtPoint(speedText, 0, 1, style.defaultFg, style.defaultBg) + termboxUtil.DrawStringAtPoint("'space' to fire", 0, 2, style.defaultFg, style.defaultBg) } else { - termboxUtil.DrawStringAtPoint("Angle:", ScreenWidth-11, 0, style.defaultFg, style.defaultBg) - termboxUtil.DrawStringAtPoint("Speed:", ScreenWidth-11, 1, style.defaultFg, style.defaultBg) + angleText := fmt.Sprintf("Angle: %d", screen.Player2.projAngle) + speedText := fmt.Sprintf("Speed: %d", screen.Player2.projSpeed) + termboxUtil.DrawStringAtPoint(angleText, ScreenWidth-11, 0, style.defaultFg, style.defaultBg) + termboxUtil.DrawStringAtPoint(speedText, ScreenWidth-11, 1, style.defaultFg, style.defaultBg) + termboxUtil.DrawStringAtPoint("'space' to fire", ScreenWidth-15, 2, style.defaultFg, style.defaultBg) } } + if screen.GameMode == modePause { + // Draw the pause menu + screen.PauseMenu.Draw() + } } -func getNowTime() int { - return int(time.Now().UnixNano()) +func (screen *mainScreen) getRndPosForBuilding(i int) (int, int) { + retY := ScreenHeight - screen.Buildings[i].height + bldStX := 0 + for idx := 0; idx < i; idx++ { + bldStX += screen.Buildings[idx].width + } + retX := screen.r.Intn(screen.Buildings[i].width-5) + bldStX + return retX, retY } func getSeedFromString(seed string) int64 { - // How does this algorithm work for string->seed generation? - // ~\_(o.o)_/~ + // Does this algorithm work for string->seed generation? + // ~\_(o.o)_/~ - seems to. var ret int64 for i, k := range seed { ret += int64(k) * int64(i) @@ -171,34 +245,105 @@ func getSeedFromString(seed string) int64 { return ret } -func drawGopher(x, y int, c termbox.Attribute) { - termbox.SetCell(x, y-4, ' ', c, c) - termbox.SetCell(x+1, y-4, ' ', c, c) - termbox.SetCell(x+2, y-4, ' ', c, c) - termbox.SetCell(x+3, y-4, ' ', c, c) - termbox.SetCell(x+4, y-4, ' ', c, c) - - termbox.SetCell(x, y-3, ' ', c, c) - termbox.SetCell(x+1, y-3, '@', termbox.ColorBlack, termbox.ColorWhite) - termbox.SetCell(x+2, y-3, ' ', c, c) - termbox.SetCell(x+3, y-3, '@', termbox.ColorBlack, termbox.ColorWhite) - termbox.SetCell(x+4, y-3, ' ', c, c) - - termbox.SetCell(x, y-2, ' ', c, c) - termbox.SetCell(x+1, y-2, ' ', c, c) - termbox.SetCell(x+2, y-2, 'w', termbox.ColorWhite, c) - termbox.SetCell(x+3, y-2, ' ', c, c) - termbox.SetCell(x+4, y-2, ' ', c, c) - - termbox.SetCell(x, y-1, ' ', c, c) - termbox.SetCell(x+1, y-1, ' ', c, c) - termbox.SetCell(x+2, y-1, ' ', c, c) - termbox.SetCell(x+3, y-1, ' ', c, c) - termbox.SetCell(x+4, y-1, ' ', c, c) - - termbox.SetCell(x, y, ' ', c, c) - termbox.SetCell(x+1, y, ' ', c, c) - termbox.SetCell(x+2, y, ' ', c, c) - termbox.SetCell(x+3, y, ' ', c, c) - termbox.SetCell(x+4, y, ' ', c, c) +type projectile struct { + x, y int + speed, angle int +} + +func (p *projectile) draw() { + termbox.SetCell(p.x, p.y, '0', termbox.ColorYellow, termbox.ColorBlack) +} + +type player struct { + x, y int + projSpeed int + projAngle int + baseColor termbox.Attribute +} + +func (p *player) draw() { + termbox.SetCell(p.x, p.y-4, ' ', p.baseColor, p.baseColor) + termbox.SetCell(p.x+1, p.y-4, ' ', p.baseColor, p.baseColor) + termbox.SetCell(p.x+2, p.y-4, ' ', p.baseColor, p.baseColor) + termbox.SetCell(p.x+3, p.y-4, ' ', p.baseColor, p.baseColor) + termbox.SetCell(p.x+4, p.y-4, ' ', p.baseColor, p.baseColor) + + termbox.SetCell(p.x, p.y-3, ' ', p.baseColor, p.baseColor) + termbox.SetCell(p.x+1, p.y-3, '@', termbox.ColorBlack, termbox.ColorWhite) + termbox.SetCell(p.x+2, p.y-3, ' ', p.baseColor, p.baseColor) + termbox.SetCell(p.x+3, p.y-3, '@', termbox.ColorBlack, termbox.ColorWhite) + termbox.SetCell(p.x+4, p.y-3, ' ', p.baseColor, p.baseColor) + + termbox.SetCell(p.x, p.y-2, ' ', p.baseColor, p.baseColor) + termbox.SetCell(p.x+1, p.y-2, ' ', p.baseColor, p.baseColor) + termbox.SetCell(p.x+2, p.y-2, 'w', termbox.ColorWhite, p.baseColor) + termbox.SetCell(p.x+3, p.y-2, ' ', p.baseColor, p.baseColor) + termbox.SetCell(p.x+4, p.y-2, ' ', p.baseColor, p.baseColor) + + termbox.SetCell(p.x, p.y-1, ' ', p.baseColor, p.baseColor) + termbox.SetCell(p.x+1, p.y-1, ' ', p.baseColor, p.baseColor) + termbox.SetCell(p.x+2, p.y-1, ' ', p.baseColor, p.baseColor) + termbox.SetCell(p.x+3, p.y-1, ' ', p.baseColor, p.baseColor) + termbox.SetCell(p.x+4, p.y-1, ' ', p.baseColor, p.baseColor) + + termbox.SetCell(p.x, p.y, ' ', p.baseColor, p.baseColor) + termbox.SetCell(p.x+1, p.y, ' ', p.baseColor, p.baseColor) + termbox.SetCell(p.x+2, p.y, ' ', p.baseColor, p.baseColor) + termbox.SetCell(p.x+3, p.y, ' ', p.baseColor, p.baseColor) + termbox.SetCell(p.x+4, p.y, ' ', p.baseColor, p.baseColor) + + // Now draw the arms + bulletHandX, bulletHandY := p.GetBulletHandPos() + termbox.SetCell(bulletHandX, bulletHandY, 'O', termbox.ColorBlack, p.baseColor) +} + +func (p *player) GetBulletHandPos() (int, int) { + if p.projAngle <= 11 { + return p.x + 5, p.y - 2 + } else if p.projAngle <= 22 { + return p.x + 5, p.y - 3 + } else if p.projAngle <= 41 { + return p.x + 5, p.y - 4 + } else if p.projAngle <= 50 { + return p.x + 5, p.y - 5 + } else if p.projAngle <= 77 { + return p.x + 4, p.y - 5 + } else if p.projAngle <= 86 { + return p.x + 3, p.y - 5 + } else if p.projAngle <= 95 { + return p.x + 2, p.y - 5 + } else if p.projAngle <= 119 { + return p.x + 1, p.y - 5 + } else if p.projAngle <= 130 { + return p.x, p.y - 5 + } else if p.projAngle <= 141 { + return p.x - 1, p.y - 5 + } else if p.projAngle <= 152 { + return p.x - 1, p.y - 4 + } else if p.projAngle <= 165 { + return p.x - 1, p.y - 3 + } + return p.x - 1, p.y - 2 +} + +type building struct { + width, height int + color termbox.Attribute + windowsOn []int +} + +func (b *building) draw(x int) { + for i := 0; i < b.height; i++ { + for j := 0; j < b.width; j++ { + c := b.color + /* TODO: Windows + if i > 0 && i < b.height && j > 0 && j < b.width { + if i%2 == 1 && j%2 == 1 { + c = termbox.ColorBlack + } + } + */ + termbox.SetCell(x+j, ScreenHeight-i, ' ', c, c) + } + } } diff --git a/screen_title.go b/screen_title.go index f2b0f8d..6d0baf1 100644 --- a/screen_title.go +++ b/screen_title.go @@ -16,7 +16,7 @@ type titleScreen struct { initialized bool } -func (screen *titleScreen) handleKeyEvent(event termbox.Event) int { +func (screen *titleScreen) handleKeyPress(event termbox.Event) int { if screen.menu.HandleKeyPress(event) { if screen.menu.IsDone() { selOpt := screen.menu.GetSelectedOption()