go-susapp/renderer.go

398 lines
12 KiB
Go
Raw Permalink Normal View History

2021-09-12 00:25:10 +01:00
package main
import (
"fmt"
2021-09-12 00:25:10 +01:00
"math"
"strconv"
"time"
2021-12-16 20:57:35 +00:00
"tea.melonie54.xyz/sean/go-susapp/src/enum"
"tea.melonie54.xyz/sean/go-susapp/src/innernetobjects"
"tea.melonie54.xyz/sean/go-susapp/src/util"
2021-09-12 00:25:10 +01:00
"github.com/gotk3/gotk3/cairo"
"github.com/gotk3/gotk3/gtk"
"github.com/gotk3/gotk3/pango"
)
type Renderer struct {
state *State
da *gtk.DrawingArea
cr *cairo.Context
w float64
h float64
mouseX float64
mouseY float64
delta int64
}
var defaultRenderFont string = "Noto Sans"
func NewRenderer(state *State) *Renderer {
return &Renderer{state: state}
}
func (r *Renderer) Draw(da *gtk.DrawingArea, cr *cairo.Context) {
r.delta = time.Now().UnixMilli()
r.da = da
r.cr = cr
allocation := da.GetAllocation()
r.w = float64(allocation.GetWidth())
r.h = float64(allocation.GetHeight())
2022-06-25 11:59:37 +01:00
versionText := r.state.Version.Formatted()
2021-09-12 00:25:10 +01:00
// Background
r.DrawFilledRectangle(0, 0, r.w, r.h, 0x14181c)
// Version numbers
r.DrawText(10, 10, versionText, "Noto Sans Display", 16, 0xffffff)
if r.state.Screen == enum.SCREEN_LOBBY || r.state.Screen == enum.SCREEN_INGAME {
r.DrawButton(r.w-60, 20, 40, 40, "⚙", defaultRenderFont, 16)
r.DrawPlayers(r.state.Settings.Debug.PlayerPosition, r.state.Settings.Debug.PlayerCollision)
if r.state.Settings.Debug.MapCollision {
r.DrawCollision()
}
if r.state.IsLobbyMenuOpen {
mw := r.w / 2
mh := r.h / 2
overlayBottom := mh + 200
r.DrawFilledRectangle(0, 0, r.w, r.h, 0x80000000)
r.DrawFilledRoundedRectangle(mw-200, mh-200, 400, 400, 1, 20, 0x000000)
r.DrawButton((r.w-200)/2, overlayBottom-60, 200, 40, "Return to Game", defaultRenderFont, 16)
r.DrawButton((r.w-200)/2, overlayBottom-120, 200, 40, "Leave Game", defaultRenderFont, 16)
}
}
2021-09-12 00:25:10 +01:00
if r.state.Screen == enum.SCREEN_TITLE {
colorLen := enum.GetPlayerTotalColors()
for i := 0; i < colorLen; i++ {
r.DrawFilledRectangle(float64(i)*20+(r.w-float64(colorLen)-float64(colorLen*20))/2, 0, 20, 20, enum.GetPlayerMainColor(i))
r.DrawFilledRectangle(float64(i)*20+(r.w-float64(colorLen)-float64(colorLen*20))/2, 20, 20, 20, enum.GetPlayerShadedColor(i))
}
r.DrawButton(r.w-120, 20, 100, 40, r.state.Settings.PlayerName, defaultRenderFont, 16)
r.DrawButton(r.w-120, 60, 100, 40, r.state.Settings.ServerAddress, defaultRenderFont, 16)
r.DrawButton(r.w-120, 100, 100, 40, strconv.FormatInt(r.state.Settings.ServerPort, 10), defaultRenderFont, 16)
r.DrawTextInput((r.w-200)/2, (r.h-20)/2-60, 200, 40, r.state.TypedGameID, defaultRenderFont, 16, r.state.IsTypingReady)
r.DrawButton((r.w-200)/2, (r.h-20)/2, 200, 40, "Play", defaultRenderFont, 16)
r.DrawButton(r.w-60, r.h-60, 40, 40, "⚙", defaultRenderFont, 16)
} else if r.state.Screen == enum.SCREEN_WAITING {
mw := r.w / 2
mh := r.h / 2
a := float64(r.delta/10) * (math.Pi / 180)
b := a + math.Pi/2
c := a + math.Pi
d := a + math.Pi*3/2
r.DrawFilledRoundedRectangle(mw+50*math.Sin(a)-20, mh+50*math.Cos(a)-20, 40, 40, 1, 20, 0xffffff)
r.DrawFilledRoundedRectangle(mw+50*math.Sin(b)-20, mh+50*math.Cos(b)-20, 40, 40, 1, 20, 0xffffff)
r.DrawFilledRoundedRectangle(mw+50*math.Sin(c)-20, mh+50*math.Cos(c)-20, 40, 40, 1, 20, 0xffffff)
r.DrawFilledRoundedRectangle(mw+50*math.Sin(d)-20, mh+50*math.Cos(d)-20, 40, 40, 1, 20, 0xffffff)
} else if r.state.Screen == enum.SCREEN_LOBBY {
r.DrawButton((r.w-100)/2, r.h-60, 100, 40, util.CodeFromGameID(r.state.CurrentGame.GameID), defaultRenderFont, 16)
if r.state.CurrentGame.GamePrivacy == 1 {
r.DrawButton((r.w-100)/2+120, r.h-60, 100, 40, "Public", defaultRenderFont, 16)
} else if r.state.CurrentGame.GamePrivacy == 0 {
r.DrawButton((r.w-100)/2+120, r.h-60, 100, 40, "Private", defaultRenderFont, 16)
}
r.DrawMiddleText(r.w/2, r.h-80, fmt.Sprint(r.state.CurrentGame.StartGameTimer), defaultRenderFont, 16, 0xffffff)
2021-09-12 00:25:10 +01:00
} else if r.state.Screen == enum.SCREEN_ERROR {
mw := r.w / 2
mh := r.h / 2
r.DrawFilledRoundedRectangle(mw-200, mh-200, 400, 400, 1, 20, 0x000000)
r.DrawText(mw-190, mh-190, "Disconnected", defaultRenderFont, 16, 0xffffff)
r.DrawButton(mw+170, mh-210, 40, 40, "×", defaultRenderFont, 16)
if r.state.DisconnectReason == 0xfe {
r.DrawWrappedText(mw-190, mh-156, 380, r.state.DisconnectStringReason, defaultRenderFont, 16, 0xffffff)
} else {
r.DrawWrappedText(mw-190, mh-156, 380, enum.DisconnectMessageText(r.state.DisconnectReason), defaultRenderFont, 16, 0xffffff)
}
} else if r.state.Screen == enum.SCREEN_INGAME {
switch r.state.CurrentGame.MapType {
case enum.MAP_THE_SKELD:
renderSkeld(r)
case enum.MAP_MIRA_HQ:
renderMira(r)
case enum.MAP_POLUS:
renderPolus(r)
case enum.MAP_THE_AIRSHIP:
renderAirship(r)
}
renderTaskOverlay(r)
2021-09-12 00:25:10 +01:00
}
}
func (r *Renderer) SetRGB(red int, green int, blue int, alpha int) {
r.cr.SetSourceRGBA(float64(red)/0xff, float64(green)/0xff, float64(blue)/0xff, 0xff/float64(alpha))
}
func (r *Renderer) SetHex(hex int) {
r.SetRGB((hex/(256*256))%256, (hex/256)%256, hex%256, (hex/(256*256*256))%256)
}
func (r *Renderer) DrawLine(x1 float64, y1 float64, x2 float64, y2 float64, color int) {
r.cr.Save()
r.SetHex(color)
r.cr.NewPath()
r.cr.MoveTo(x1, y1)
r.cr.LineTo(x2, y2)
r.cr.ClosePath()
r.cr.Stroke()
r.cr.Restore()
}
2021-09-12 00:25:10 +01:00
func (r *Renderer) DrawRectangle(x float64, y float64, w float64, h float64, color int) {
r.cr.Save()
2021-09-12 00:25:10 +01:00
r.SetHex(color)
r.cr.Rectangle(x, y, w, h)
r.cr.Stroke()
r.cr.Restore()
2021-09-12 00:25:10 +01:00
}
func (r *Renderer) DrawFilledRectangle(x float64, y float64, w float64, h float64, color int) {
r.cr.Save()
2021-09-12 00:25:10 +01:00
r.SetHex(color)
r.cr.Rectangle(x, y, w, h)
r.cr.Fill()
r.cr.Restore()
2021-09-12 00:25:10 +01:00
}
func (r *Renderer) DrawBorderedRectangle(x float64, y float64, w float64, h float64, color int, border int) {
r.DrawFilledRectangle(x, y, w, h, color)
r.DrawRectangle(x, y, w, h, border)
}
func (r *Renderer) DrawRoundedRectangle(x float64, y float64, w float64, h float64, aspect float64, corner_radius float64, color int) {
r.cr.Save()
2021-09-12 00:25:10 +01:00
a := corner_radius / aspect
d := math.Pi / 180
r.cr.NewPath()
r.cr.Arc(x+w-a, y+a, a, -90*d, 0*d)
r.cr.Arc(x+w-a, y+h-a, a, 0*d, 90*d)
r.cr.Arc(x+a, y+h-a, a, 90*d, 180*d)
r.cr.Arc(x+a, y+a, a, 180*d, 270*d)
r.cr.ClosePath()
r.SetHex(color)
r.cr.Stroke()
r.cr.Restore()
2021-09-12 00:25:10 +01:00
}
func (r *Renderer) DrawFilledRoundedRectangle(x float64, y float64, w float64, h float64, aspect float64, corner_radius float64, color int) {
r.cr.Save()
2021-09-12 00:25:10 +01:00
a := corner_radius / aspect
d := math.Pi / 180
r.cr.NewPath()
r.cr.Arc(x+w-a, y+a, a, -90*d, 0*d)
r.cr.Arc(x+w-a, y+h-a, a, 0*d, 90*d)
r.cr.Arc(x+a, y+h-a, a, 90*d, 180*d)
r.cr.Arc(x+a, y+a, a, 180*d, 270*d)
r.cr.ClosePath()
r.SetHex(color)
r.cr.FillPreserve()
r.cr.Restore()
2021-09-12 00:25:10 +01:00
}
func (r *Renderer) DrawBorderedRoundedRectangle(x float64, y float64, w float64, h float64, aspect float64, corner_radius float64, color int, border int) {
r.cr.Save()
2021-09-12 00:25:10 +01:00
a := corner_radius / aspect
d := math.Pi / 180
r.cr.NewPath()
r.cr.Arc(x+w-a, y+a, a, -90*d, 0*d)
r.cr.Arc(x+w-a, y+h-a, a, 0*d, 90*d)
r.cr.Arc(x+a, y+h-a, a, 90*d, 180*d)
r.cr.Arc(x+a, y+a, a, 180*d, 270*d)
r.cr.ClosePath()
r.SetHex(color)
r.cr.FillPreserve()
r.SetHex(border)
r.cr.Stroke()
r.cr.Restore()
2021-09-12 00:25:10 +01:00
}
func (r *Renderer) DrawText(x float64, y float64, text string, fontFamily string, fontSize float64, color int) {
layout := pango.CairoCreateLayout(r.cr)
font := pango.FontDescriptionFromString("sans normal 16")
font.SetFamily(fontFamily)
layout.SetFontDescription(font)
layout.SetText(text, len(text))
r.SetHex(color)
r.cr.MoveTo(x, y)
pango.CairoUpdateLayout(r.cr, layout)
pango.CairoShowLayout(r.cr, layout)
}
func (r *Renderer) DrawMiddleText(x float64, y float64, text string, fontFamily string, fontSize float64, color int) {
layout := pango.CairoCreateLayout(r.cr)
font := pango.FontDescriptionFromString("sans normal 16")
font.SetFamily(fontFamily)
layout.SetFontDescription(font)
layout.SetText(text, len(text))
textWidth, textHeight := layout.GetSize()
textWidth /= pango.SCALE
textHeight /= pango.SCALE
r.SetHex(0xffffff)
r.cr.MoveTo(x-float64(textWidth)/2, y-float64(textHeight)/2)
pango.CairoUpdateLayout(r.cr, layout)
pango.CairoShowLayout(r.cr, layout)
}
func (r *Renderer) DrawCircle(x float64, y float64, radius float64, color int) {
r.cr.Save()
r.SetHex(color)
r.cr.NewPath()
r.cr.Arc(x, y, radius, 0, 2*math.Pi)
r.cr.ClosePath()
r.cr.FillPreserve()
r.cr.Restore()
}
func (r *Renderer) DrawBorderedCircle(x float64, y float64, radius float64, color int) {
r.cr.Save()
r.SetHex(color)
r.cr.NewPath()
r.cr.Arc(x, y, radius, 0, 2*math.Pi)
r.cr.ClosePath()
r.cr.Stroke()
r.cr.Restore()
}
2021-09-12 00:25:10 +01:00
func (r *Renderer) DrawWrappedText(x float64, y float64, w float64, text string, fontFamily string, fontSize float64, color int) {
layout := pango.CairoCreateLayout(r.cr)
layout.SetWidth(int(w) * pango.SCALE)
font := pango.FontDescriptionFromString("sans normal 16")
font.SetFamily(fontFamily)
layout.SetFontDescription(font)
layout.SetText(text, len(text))
r.SetHex(color)
r.cr.MoveTo(x, y)
pango.CairoUpdateLayout(r.cr, layout)
pango.CairoShowLayout(r.cr, layout)
}
func (r *Renderer) DrawButton(x float64, y float64, w float64, h float64, text string, fontFamily string, fontSize float64) {
r.DrawFilledRoundedRectangle(x, y, w-4, h-4, w/h, 10, 0x000000)
if r.mouseX > x && r.mouseX < x+w && r.mouseY > y && r.mouseY < y+h {
r.DrawRoundedRectangle(x, y, w-4, h-4, w/h, 10, 0x00ff00)
} else {
r.DrawRoundedRectangle(x, y, w-4, h-4, w/h, 10, 0xffffff)
}
layout := pango.CairoCreateLayout(r.cr)
font := pango.FontDescriptionFromString("sans normal 16")
font.SetFamily(fontFamily)
layout.SetFontDescription(font)
layout.SetText(text, len(text))
textWidth, textHeight := layout.GetSize()
textWidth /= pango.SCALE
textHeight /= pango.SCALE
r.SetHex(0xffffff)
r.cr.MoveTo(x+(w-4-float64(textWidth))/2, y+(h-4-float64(textHeight))/2)
pango.CairoUpdateLayout(r.cr, layout)
pango.CairoShowLayout(r.cr, layout)
}
func (r *Renderer) DrawTextInput(x float64, y float64, w float64, h float64, text string, fontFamily string, fontSize float64, inFocus bool) {
if r.mouseX > x && r.mouseX < x+w && r.mouseY > y && r.mouseY < y+h {
r.DrawFilledRectangle(x, y+h-6, w-4, 2, 0x00ffff)
} else {
r.DrawFilledRectangle(x, y+h-6, w-4, 2, 0xffffff)
}
layout := pango.CairoCreateLayout(r.cr)
font := pango.FontDescriptionFromString("sans normal 16")
font.SetFamily(fontFamily)
layout.SetFontDescription(font)
layout.SetText(text, len(text))
textWidth, textHeight := layout.GetSize()
textWidth /= pango.SCALE
textHeight /= pango.SCALE
r.SetHex(0xffffff)
r.cr.MoveTo(x+(w-float64(textWidth))/2, y+(h-12-float64(textHeight))/2)
pango.CairoUpdateLayout(r.cr, layout)
pango.CairoShowLayout(r.cr, layout)
if inFocus && r.delta%1600 < 800 {
r.DrawFilledRectangle(x+(w+float64(textWidth))/2, y+4, 2, 20, 0xffffff)
}
}
func (r *Renderer) DrawPlayers(debugPosition bool, debugCollision bool) {
meImpostor := r.state.CurrentGame.GetMyPlayer().IsImpostor
2021-09-12 00:25:10 +01:00
for _, item := range r.state.CurrentGame.PlayerNetObjects.ByPlayerClientID {
if item.InVent {
continue
}
var t *innernetobjects.InnerNetObject = r.state.CurrentGame.NetObjects[item.PlayerNetworkTransform]
if t != nil {
a, ok := (*t).(*innernetobjects.PlayerNetworkTransform)
if ok {
b := r.ConvertToDisplayVec2(a.TargetPosition)
if debugCollision {
e := util.NewVec2(a.TargetPosition.X, a.TargetPosition.Y+enum.PLAYER_COLLISION_OFFSET_Y)
f := r.ConvertToDisplayVec2(e)
r.DrawBorderedCircle(float64(f.X), float64(f.Y), enum.PLAYER_COLLISION_RADIUS*50, 0xff0000)
}
r.DrawCircle(float64(b.X), float64(b.Y), 5, enum.GetPlayerMainColor(int(item.PlayerColor)))
fontColor := 0xffffff
if meImpostor && item.IsImpostor {
fontColor = 0x7a0838
}
r.DrawText(float64(b.X), float64(b.Y), item.PlayerName, defaultRenderFont, 10, fontColor)
if debugPosition {
r.DrawText(float64(b.X), float64(b.Y-34), fmt.Sprintf("%v, %v", a.TargetPosition.X, a.TargetPosition.Y), defaultRenderFont, 10, 0xffffff)
}
}
}
}
}
func (r *Renderer) DrawCollision() {
if r.state.Collision != nil {
for _, item := range *r.state.Collision {
lastPoint := util.Vec2{}
for i, point := range item {
if i != 0 {
r.DrawCollisionLine(lastPoint, point, 0x00ff00)
}
lastPoint = point
}
2021-09-12 00:25:10 +01:00
}
}
}
func (r *Renderer) DrawCollisionLine(a util.Vec2, b util.Vec2, color int) {
c := r.ConvertToDisplayVec2(a)
d := r.ConvertToDisplayVec2(b)
r.DrawLine(float64(c.X), float64(c.Y), float64(d.X), float64(d.Y), color)
}
func (r *Renderer) ConvertToDisplayVec2(vec2 util.Vec2) util.Vec2 {
x := r.w/2 - float64(r.state.PosX)*50 + float64(vec2.X)*50
y := r.h/2 + float64(r.state.PosY)*50 - float64(vec2.Y)*50
return util.Vec2{X: float32(x), Y: float32(y)}
}