func main() { if err := glfw.Init(); err != nil { fmt.Println("glfw.Init():", err) return } defer glfw.Terminate() glfw.WindowHint(glfw.Decorated, glfw.True) glfw.WindowHint(glfw.ContextVersionMajor, 1) glfw.WindowHint(glfw.ContextVersionMinor, 0) glfw.WindowHint(glfw.Resizable, glfw.True) window, err := glfw.CreateWindow(10, 10, "Settlers", nil, nil) if err != nil { fmt.Println("glfw.CreateWindow():", err) return } window.MakeContextCurrent() if err := gl.Init(); err != nil { fmt.Println("gl.Init():", err) return } stash := fontstash.New(512, 512) fontID, err := stash.AddFont(resourcePath("MorrisRoman-Black.ttf")) if err != nil { fmt.Println(err) return } stash.SetYInverted(true) font := &font{stash, fontID, 35} gl.Enable(gl.BLEND) gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) gl.MatrixMode(gl.MODELVIEW) gl.LoadIdentity() rand.Seed(time.Now().UnixNano()) g := game.New([]game.Color{game.Red, game.White, game.Blue}, rand.Int()) var lines []string window.SetCharCallback(func(_ *glfw.Window, r rune) { if len(lines) == 0 { lines = []string{""} } lines[len(lines)-1] += string(r) }) window.SetCharModsCallback(func(_ *glfw.Window, r rune, _ glfw.ModifierKey) { }) window.SetKeyCallback(func(_ *glfw.Window, key glfw.Key, _ int, action glfw.Action, _ glfw.ModifierKey) { if action == glfw.Release { return } if key == glfw.KeyEscape { window.SetShouldClose(true) } if key == glfw.Key1 { g.CurrentPlayer = 0 } if key == glfw.Key2 { g.CurrentPlayer = 1 } if key == glfw.Key3 { g.CurrentPlayer = 2 } if key == glfw.Key4 { g.CurrentPlayer = 3 } if key == glfw.KeyR { state = buildingRoad } if key == glfw.KeyS { state = buildingSettlement } if key == glfw.KeyKP2 { g.DealResources(2) } if key == glfw.KeyKP3 { g.DealResources(3) } if key == glfw.KeyKP4 { g.DealResources(4) } if key == glfw.KeyKP5 { g.DealResources(5) } if key == glfw.KeyKP6 { g.DealResources(6) } if key == glfw.KeyKP8 { g.DealResources(8) } if key == glfw.KeyKP9 { g.DealResources(9) } if key == glfw.KeyKP0 { g.DealResources(10) } if key == glfw.KeyKP1 { g.DealResources(11) } if key == glfw.KeyKP7 { g.DealResources(12) } if len(lines) > 0 && key == glfw.KeyBackspace && (action == glfw.Press || action == glfw.Repeat) { if len(lines[len(lines)-1]) == 0 { lines = lines[:len(lines)-1] } else { lines[len(lines)-1] = removeLastRune(lines[len(lines)-1]) } } if (action == glfw.Press || action == glfw.Repeat) && (key == glfw.KeyEnter || key == glfw.KeyKPEnter) { lines = append(lines, "") } }) window.SetInputMode(glfw.CursorMode, glfw.CursorHidden) window.SetInputMode(glfw.CursorMode, glfw.CursorNormal) window.SetSizeCallback(func(_ *glfw.Window, w, h int) { windowW, windowH = w, h cam.WindowWidth, cam.WindowHeight = w, h gl.Viewport(0, 0, int32(w), int32(h)) }) if err := loadImages(); err != nil { fmt.Println(err) return } var background image.Image var backImg *glImage go func() { gameW, gameH := 7*200, 7*tileYOffset+tileSlopeHeight back := image.NewRGBA(image.Rect(0, 0, gameW, gameH)) clearToTransparent(back) drawGameBackgroundIntoImage(back, g) background = back }() gameColorToString := func(c game.Color) string { switch c { case game.Red: return "red" case game.Orange: return "orange" case game.Blue: return "blue" default: return "white" } } roadImage := func(pos game.TileEdge, c game.Color) *glImage { color := gameColorToString(c) dir := "up" if isEdgeVertical(pos) { dir = "vertical" } if isEdgeGoingDown(pos) { dir = "down" } return glImages["road_"+color+"_"+dir] } settlementImage := func(c game.Color) *glImage { return glImages["settlement_"+gameColorToString(c)] } cityImage := func(c game.Color) *glImage { return glImages["city_"+gameColorToString(c)] } // TODO this is for showing the road/settlement being built currently var movingSettlementCorner game.TileCorner var movingSettlementVisible bool var movingRoadEdge game.TileEdge var movingRoadVisible bool window.SetCursorPosCallback(func(_ *glfw.Window, x, y float64) { mouseX, mouseY = x, y gameX, gameY := cam.screenToGame(x, y) movingSettlementCorner, movingSettlementVisible = screenToCorner(gameX, gameY) movingRoadEdge, movingRoadVisible = screenToEdge(gameX, gameY) }) window.SetCursorEnterCallback(func(_ *glfw.Window, entered bool) { mouseIn = entered if entered == false { movingSettlementVisible = false movingRoadVisible = true } }) ////////////////////////////////////////////////////////////////// drawGame := func() { if backImg == nil && background != nil { backImg, _ = NewGLImageFromImage(background) } if backImg != nil { backImg.DrawAtXY(0, 0) for _, p := range g.GetPlayers() { for _, r := range p.GetBuiltRoads() { x, y := edgeToScreen(r.Position) img := roadImage(r.Position, p.Color) img.DrawAtXY(x-img.Width/2, y-img.Height/2) } for _, s := range p.GetBuiltSettlements() { x, y := cornerToScreen(s.Position) img := settlementImage(p.Color) img.DrawAtXY(x-img.Width/2, y-(5*img.Height/8)) } for _, c := range p.GetBuiltCities() { x, y := cornerToScreen(c.Position) img := cityImage(p.Color) img.DrawAtXY(x-img.Width/2, y-(5*img.Height/8)) } } if state == buildingSettlement && movingSettlementVisible && mouseIn { color := [4]float32{1, 1, 1, 1} x, y := cornerToScreen(movingSettlementCorner) if !movingSettlementVisible || !g.CanBuildSettlementAt(movingSettlementCorner) { color = [4]float32{0.7, 0.7, 0.7, 0.7} x, y = cam.screenToGame(mouseX, mouseY) } img := settlementImage(g.GetCurrentPlayer().Color) // TODO duplicate code: img.DrawColoredAtXY(x-img.Width/2, y-(5*img.Height/8), color) } if state == buildingRoad && movingRoadVisible && mouseIn { color := [4]float32{1, 1, 1, 1} x, y := edgeToScreen(movingRoadEdge) if !(!movingRoadVisible || !g.CanBuildRoadAt(movingRoadEdge)) { img := roadImage(movingRoadEdge, g.GetCurrentPlayer().Color) // TODO duplicate code: img.DrawColoredAtXY(x-img.Width/2, y-(5*img.Height/8), color) } } x, y, w, h := tileToScreen(g.Robber.Position) robber := glImages["robber"] robber.DrawAtXY(x+(w-robber.Width)/2, y+(h-robber.Height)/2) } } buildMenu := &menu{color{0.5, 0.4, 0.8, 0.9}, rect{0, 500, 800, 250}} showCursor := 0 start := time.Now() frames := 0 window.SetSize(windowW, windowH) for !window.ShouldClose() { glfw.PollEvents() gl.MatrixMode(gl.PROJECTION) gl.LoadIdentity() const controlsHeight = 0 // TODO reserve are for stats and menus gameW, gameH := 7.0*200, 7.0*tileYOffset+tileSlopeHeight+controlsHeight gameRatio := gameW / gameH windowRatio := float64(windowW) / float64(windowH) var left, right, bottom, top float64 if windowRatio > gameRatio { // window is wider than game => borders left and right border := (windowRatio*gameH - gameW) / 2 left, right = -border, border+gameW bottom, top = gameH, 0 } else { // window is higher than game => borders on top and bottom border := (gameW/windowRatio - gameH) / 2 left, right = 0, gameW bottom, top = gameH+border, -border } gl.Ortho(left, right, bottom, top, -1, 1) cam.Left, cam.Right, cam.Bottom, cam.Top = left, right, bottom, top gl.ClearColor(0, 0, 1, 1) gl.Clear(gl.COLOR_BUFFER_BIT) drawGame() lines = make([]string, g.PlayerCount*6) for i, player := range g.GetPlayers() { lines[i*6+0] = fmt.Sprintf("player %v has %v ore", i+1, player.Resources[game.Ore]) lines[i*6+1] = fmt.Sprintf("player %v has %v grain", i+1, player.Resources[game.Grain]) lines[i*6+2] = fmt.Sprintf("player %v has %v lumber", i+1, player.Resources[game.Lumber]) lines[i*6+3] = fmt.Sprintf("player %v has %v wool", i+1, player.Resources[game.Wool]) lines[i*6+4] = fmt.Sprintf("player %v has %v brick", i+1, player.Resources[game.Brick]) } if len(lines) > 0 { stash.BeginDraw() const fontSize = 35 const cursorText = "" //"_" cursor := "" if showCursor > 60 { cursor = cursorText } for i, line := range lines { output := line if i == len(lines)-1 { output += cursor } font.Write(output, 0, float64(i+1)*fontSize) } if len(lines) == 0 { font.Write(cursor, 0, fontSize) } stash.EndDraw() } buildMenu.draw() window.SwapBuffers() showCursor = (showCursor + 1) % 120 frames++ if time.Now().Sub(start).Seconds() >= 1.0 { fmt.Println(frames, "fps") frames = 0 start = time.Now() } } }
func NewGameUI(win Window) (*gameUI, error) { if err := settings.Settings.Load(); err != nil { fmt.Println("cannot load last settings:", err) } graphics, err := newGraphics() if err != nil { return nil, err } g := game.New([]game.Color{game.Red, game.Blue, game.White}, 1) // main menu size := func(w, h int) rect { return rect{w: w, h: h} } newGame := newButton(lang.NewGame, size(500, 80), NewGameOption) joinRemoteGame := newButton(lang.JoinRemoteGame, size(500, 80), JoinRemoteGameOption) chooseLanguage := newButton(lang.LanguageWord, size(500, 80), ChooseLanguageOption) quit := newButton(lang.Quit, size(500, 80), QuitOption) mainMenu := newWindow( rect{0, 0, gameW, gameH}, newVerticalFlowLayout(20), newGame, joinRemoteGame, chooseLanguage, quit, ) // language menu var langBoxes []*checkBox for language := lang.Language(0); language < lang.LastLanguage; language++ { action := LanguageOptionOffset + int(language) cb := newCheckBox(lang.Item(language), size(300, 80), action) cb.setChecked(settings.Settings.Language == int(language)) langBoxes = append(langBoxes, cb) } languageMenu := newWindow( rect{0, 0, gameW, gameH}, newVerticalFlowLayout(0), newCheckBoxGroup(langBoxes...), newSpacer(size(0, 20)), newButton(lang.OK, size(300, 80), LanguageOKOption), ) languageMenu.setVisible(false) // new game menu threePlayers := newCheckBox(lang.ThreePlayers, size(350, 80), ThreePlayersOption) threePlayers.checked = settings.Settings.PlayerCount == 3 fourPlayers := newCheckBox(lang.FourPlayers, size(350, 80), FourPlayersOption) fourPlayers.checked = settings.Settings.PlayerCount == 4 var playerMenus [4]*window for i := range playerMenus { playerIndex := i // need to copy this for use in closures nameText := newTextBox(lang.Name, rect{0, 0, 500, 80}, graphics.font) nameText.text = settings.Settings.PlayerNames[i] nameText.onTextChange(func(text string) { settings.Settings.PlayerNames[playerIndex] = text }) playHere := newCheckBox(lang.PlayHere, rect{0, 0, 500, 80}, -1) playHere.onCheckChange(func(checked bool) { if checked { settings.Settings.PlayerTypes[playerIndex] = settings.Human } }) playHere.checked = settings.Settings.PlayerTypes[i] == settings.Human playAI := newCheckBox(lang.AIPlayer, rect{0, 0, 500, 80}, -1) playAI.onCheckChange(func(checked bool) { if checked { settings.Settings.PlayerTypes[playerIndex] = settings.AI } }) playAI.checked = settings.Settings.PlayerTypes[i] == settings.AI ipText := newTextBox(lang.IP, rect{0, 0, 500, 80}, graphics.font) ipText.text = settings.Settings.IPs[i] ipText.onTextChange(func(text string) { settings.Settings.IPs[playerIndex] = text }) portText := newTextBox(lang.Port, rect{0, 0, 500, 80}, graphics.font) portText.text = settings.Settings.Ports[i] portText.onTextChange(func(text string) { settings.Settings.Ports[playerIndex] = text }) connectButton := newButton(lang.Connect, size(500, 80), -1) playNetwork := newCheckBox(lang.NetworkPlayer, rect{0, 0, 500, 80}, -1) playNetwork.onCheckChange(func(checked bool) { ipText.setEnabled(checked) portText.setEnabled(checked) connectButton.setEnabled(checked) if checked { settings.Settings.PlayerTypes[playerIndex] = settings.NetworkPlayer } }) playNetwork.setChecked(settings.Settings.PlayerTypes[i] == settings.NetworkPlayer) playerMenus[i] = newWindow( rect{}, newVerticalFlowLayout(0), newSpacer(rect{0, 0, 620, 30}), nameText, newCheckBoxGroup( playHere, playAI, playNetwork, ), ipText, portText, connectButton, newSpacer(rect{0, 0, 0, 30}), ) } var playerTabs [4]*tab for i := range playerTabs { col := game.Color(i) playerTabs[i] = newTab(fullPlayerColor(col), playerMenus[i], i < settings.Settings.PlayerCount) } playersSheet := newTabSheet(60, playerTabs[:]...) newGameMenu := newWindow( rect{0, 0, gameW, gameH}, newVerticalFlowLayout(20), newCheckBoxGroup(threePlayers, fourPlayers), playersSheet, newButton(lang.StartGame, rect{0, 0, 400, 80}, StartGameOption), newButton(lang.Back, rect{0, 0, 400, 80}, NewGameBackOption), ) newGameMenu.setVisible(false) ui := &gameUI{ game: g, window: win, camera: newCamera(), graphics: graphics, mainMenu: mainMenu, newGameMenu: newGameMenu, languageMenu: languageMenu, playerTabSheet: playersSheet, lastPlayerTab: playerTabs[3], } ui.buyMenu = newBuyMenu(graphics, ui) ui.gui = newComposite(ui.mainMenu, ui.newGameMenu, ui.languageMenu) if err := ui.init(); err != nil { return nil, err } ui.setLanguage(lang.Language(settings.Settings.Language)) //ui.DEBUG_initGame() return ui, nil }
func main() { g := game.New([]game.Color{game.Red, game.Blue, game.White}, rand.Int) state := buildingSettlement g.Players[0].Roads[0].Position = game.TileEdge{9, 2} draw.RunWindow("Settlers", 1400, 1000, draw.Resizable, func(window draw.Window) { if window.WasKeyPressed("escape") { window.Close() } if window.WasKeyPressed("s") { state = buildingSettlement } if window.WasKeyPressed("r") { state = buildingRoad } if window.WasKeyPressed("c") { state = buildingCity } for i := 1; i <= 4; i++ { if window.WasKeyPressed(strconv.Itoa(i)) { fmt.Println(g.CurrentPlayer, "->", i) g.CurrentPlayer = i - 1 } } if state == buildingSettlement || state == buildingCity || state == buildingRoad { canBuild := g.CanPlayerBuildSettlement build := g.BuildSettlement screenToTile := screenToCorner if state == buildingCity { canBuild = g.CanPlayerBuildCity build = g.BuildCity } if state == buildingRoad { canBuild = g.CanPlayerBuildRoad build = g.BuildRoad screenToTile = screenToEdge } if canBuild() { for _, click := range window.Clicks() { if click.Button == draw.LeftButton { corner, ok := screenToTile(click.X, click.Y, 40) if ok { build(corner) } else { fmt.Println("miss") } } } } } for i := 2; i <= 12; i++ { // TODO what about 7? if window.WasKeyPressed(strconv.Itoa(i)) { g.DealResources(i) } } drawGame(g, window) if state == buildingSettlement || state == buildingCity { mx, my := window.MousePosition() corner, hit := screenToCorner(mx, my, 40) if hit { x, y := cornerToScreen(corner) color := currentPlayerColor(g) color.A = 0.75 window.FillEllipse(x-40, y-40, 80, 80, color) if state == buildingSettlement && !g.CanBuildSettlementAt(game.TileCorner(corner)) { color.A = 1 window.DrawLine(x-50, y-50, x+50, y+50, color) window.DrawLine(x-50, y+50, x+50, y-50, color) } } } if state == buildingRoad { mx, my := window.MousePosition() edge, hit := screenToEdge(mx, my, 40) if hit { x, y := edgeToScreen(game.TileEdge(edge)) color := currentPlayerColor(g) color.A = 0.75 window.FillEllipse(x-40, y-40, 80, 80, color) if !g.CanBuildRoadAt(game.TileEdge(edge)) { color.A = 1 window.DrawLine(x-50, y-50, x+50, y+50, color) window.DrawLine(x-50, y+50, x+50, y-50, color) } } } }) }
func (ui *gameUI) MouseButtonDown(button glfw.MouseButton) { if button != glfw.MouseButtonLeft { return // TODO handle right click when building to undo both buying and build } gameX, gameY := ui.camera.windowToGame(ui.mouseX, ui.mouseY) if ui.game.State == game.NotStarted { if action := ui.gui.click(gameX, gameY); action != -1 { switch action { case NewGameOption: ui.mainMenu.visible = false ui.newGameMenu.visible = true case ChooseLanguageOption: ui.mainMenu.visible = false ui.languageMenu.visible = true case QuitOption: ui.window.Close() case LanguageOKOption: ui.languageMenu.visible = false ui.mainMenu.visible = true case NewGameBackOption: ui.newGameMenu.visible = false ui.mainMenu.visible = true case StartGameOption: ui.game = game.New([]game.Color{game.Red, game.Blue, game.White}, rand.Int()) ui.game = game.New([]game.Color{game.Red, game.Blue, game.White}, 0) // TODO remove ui.init() ui.game.Start() case ThreePlayersOption: ui.lastPlayerTab.visible = false ui.playerTabSheet.relayout() settings.Settings.PlayerCount = 3 case FourPlayersOption: ui.lastPlayerTab.visible = true ui.playerTabSheet.relayout() settings.Settings.PlayerCount = 4 } if action >= LanguageOptionOffset { language := lang.Language(action - LanguageOptionOffset) ui.setLanguage(language) } } } else if ui.game.State == game.ChoosingNextAction { ui.buyMenu.click(gameX, gameY) } else if ui.game.State == game.BuildingFirstSettlement || ui.game.State == game.BuildingSecondSettlement || ui.game.State == game.BuildingNewSettlement { corner, hit := screenToCorner(gameX, gameY) if hit && ui.game.CanBuildSettlementAt(corner) { ui.game.BuildSettlement(corner) } } else if ui.game.State == game.BuildingFirstRoad || ui.game.State == game.BuildingSecondRoad || ui.game.State == game.BuildingNewRoad { edge, hit := screenToEdge(gameX, gameY) if hit && ui.game.CanBuildRoadAt(edge) { ui.game.BuildRoad(edge) } } else if ui.game.State == game.BuildingNewCity { corner, hit := screenToCorner(gameX, gameY) if hit && ui.game.CanBuildCityAt(corner) { ui.game.BuildCity(corner) } } else if ui.game.State == game.RollingDice { center := rect{gameW/2 - 100, gameH/2 - 50, 200, 100} if center.contains(gameX, gameY) { ui.game.RollTheDice() } } }