package main import ( "fmt" "math/rand" "time" "gogs.bullercodeworks.com/brian/termbox-util" "github.com/nsf/termbox-go" ) const ( modeInit = iota modeRun modeRunInput modePause ) type mainScreen struct { GameMode int PlayerTurn int Player1 *player Player2 *player Bullet *projectile Buildings []building PauseMenu *termboxUtil.Menu r *rand.Rand animating bool gravity float32 } 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 } 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()) screen.r = rand.New(rand.NewSource(getSeedFromString(seed + "-world"))) screen.PlayerTurn = screen.r.Intn(1) + 1 for bldCity < ScreenWidth { w := screen.r.Intn(8) + 6 h := screen.r.Intn(ScreenHeight-10) + 2 var c termbox.Attribute switch screen.r.Intn(3) { case 0: c = termbox.ColorRed case 1: c = termbox.ColorGreen 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 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.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 } } 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 } } } } func (screen *mainScreen) drawScreen(style style) { // Draw Player 1 screen.Player1.draw() // Draw Player 2 screen.Player2.draw() // Draw Landscape var x int for i := range screen.Buildings { screen.Buildings[i].draw(x) x += screen.Buildings[i].width } 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 { 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 { 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 (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 { // 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) } return ret } 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) } } }