package main import ( "time" "github.com/gen2brain/raylib-go/raylib" ) type ClientAuth struct { } type Entity struct { } type LoginState int const ( LOGIN_STATE_CREDENTIALS LoginState = iota LOGIN_STATE_ATTEMPTING LOGIN_STATE_ERROR ) type ButtonState int const ( BUTTON_STATE_IDLE ButtonState = iota BUTTON_STATE_HOVER BUTTON_STATE_PRESSED ) type Scale struct { window rl.Vector2 ui uint } type ClientState struct { auth *ClientAuth login LoginState login_button ButtonState scale Scale screen rl.Rectangle camera rl.Camera command_queue chan interface{} network_queue chan interface{} context *Entity context_pos rl.Vector2 } const ( WIDTH uint = 1600 HEIGHT uint = 1000 ) func scale(original rl.Rectangle, scale Scale) rl.Rectangle { return rl.Rectangle{ X: original.X, Y: original.Y, Width: original.Width*float32(scale.ui)/scale.window.X, Height: original.Height*float32(scale.ui)/scale.window.Y, } } func offset(original rl.Rectangle, off_x, off_y int, scale Scale) rl.Rectangle { return rl.Rectangle{ X: original.X + float32(off_x)/scale.window.X, Y: original.Y + float32(off_y)/scale.window.Y, Width: original.Width, Height: original.Height, } } func center(container, original rl.Rectangle) rl.Rectangle { return rl.Rectangle{ X: (container.Width - original.Width )/2 + container.X, Y: (container.Height - original.Height)/2 + container.Y, Width: original.Width, Height: original.Height, } } func button(rectangle rl.Rectangle, idle, hover, pressed rl.Color, text string, font_size int32, text_color rl.Color, last_state ButtonState) (action bool, next_state ButtonState) { over := rl.CheckCollisionPointRec(rl.GetMousePosition(), rectangle) action = false next_state = last_state if(rl.IsMouseButtonPressed(rl.MouseButtonLeft) && over) { next_state = BUTTON_STATE_PRESSED } else if(rl.IsMouseButtonReleased(rl.MouseButtonLeft) && last_state == BUTTON_STATE_PRESSED) { next_state = BUTTON_STATE_IDLE if(over) { action = true } } else if(rl.IsMouseButtonUp(rl.MouseButtonLeft)) { if(last_state == BUTTON_STATE_IDLE && over) { next_state = BUTTON_STATE_HOVER } else if (last_state == BUTTON_STATE_HOVER && !over) { next_state = BUTTON_STATE_IDLE } } var color rl.Color switch(next_state) { case BUTTON_STATE_HOVER: color = hover case BUTTON_STATE_PRESSED: color = pressed default: color = idle } rl.DrawRectangleRec(rectangle, color) text_size := rl.MeasureTextEx(rl.GetFontDefault(), text, float32(font_size), 1.0) rl.DrawText(text, int32(rectangle.X + rectangle.Width/2 - text_size.X/2), int32(rectangle.Y + rectangle.Height/2 - text_size.Y/2), font_size, text_color) return action, next_state } func NetworkThread(network_queue chan interface{}) { for(true) { // TODO: remove time.Sleep(time.Millisecond) } } func LogicThread(state *ClientState) { for(true) { select { case _ = <- state.command_queue: case _ = <- state.network_queue: } time.Sleep(time.Millisecond) } } func main() { state := ClientState{ auth: nil, login: LOGIN_STATE_CREDENTIALS, login_button: BUTTON_STATE_IDLE, scale: Scale{ ui: 1, }, camera: rl.NewCamera3D(rl.Vector3{}, rl.Vector3{}, rl.Vector3{}, 90, rl.CameraOrthographic), context: nil, } go LogicThread(&state) rl.SetConfigFlags(rl.FlagWindowHighdpi) rl.InitWindow(0, 0, "roleplay") rl.SetExitKey(0) rl.SetTargetFPS(60) state.scale.window = rl.GetWindowScaleDPI() rl.SetWindowSize(int(float32(WIDTH)/state.scale.window.X), int(float32(HEIGHT)/state.scale.window.Y)) state.screen.Width = float32(WIDTH) /state.scale.window.X state.screen.Height = float32(HEIGHT)/state.scale.window.Y for(rl.WindowShouldClose() == false) { if(state.auth == nil) { // Draw login rl.BeginDrawing() rl.ClearBackground(rl.Black) logo_rect := scale(rl.Rectangle{Width: 1200, Height: 300}, state.scale) logo_rect = center(state.screen, logo_rect) logo_rect = offset(logo_rect, 0, -250, state.scale) rl.DrawRectangleRec(logo_rect, rl.Gray) form_rect := scale(rl.Rectangle{Width: 600, Height: 400}, state.scale) form_rect = center(state.screen, form_rect) form_rect = offset(form_rect, 0, 200, state.scale) rl.DrawRectangleRec(form_rect, rl.Gray) switch(state.login) { case LOGIN_STATE_CREDENTIALS: submit_rect := scale(rl.Rectangle{Width: 100, Height: 50}, state.scale) submit_rect = center(form_rect, submit_rect) submit_rect = offset(submit_rect, 0, 125, state.scale) var submit_action bool submit_action, state.login_button = button(submit_rect, rl.Black, rl.Brown, rl.Red, "Submit", 12, rl.White, state.login_button) if submit_action { // TODO: real auth via network thread state.auth = &ClientAuth{} } case LOGIN_STATE_ATTEMPTING: text := "Logging in..." text_size := rl.MeasureTextEx(rl.GetFontDefault(), text, 20, 1.0) text_rect := center(form_rect, rl.Rectangle{Width: text_size.X, Height: text_size.Y}) rl.DrawText(text, int32(text_rect.X), int32(text_rect.Y), 20, rl.Black) case LOGIN_STATE_ERROR: text := "Error: {TODO}" text_size := rl.MeasureTextEx(rl.GetFontDefault(), text, 20, 1.0) text_rect := center(form_rect, rl.Rectangle{Width: text_size.X, Height: text_size.Y}) rl.DrawText(text, int32(text_rect.X), int32(text_rect.Y), 20, rl.Black) } rl.EndDrawing() } else { rl.BeginDrawing() rl.ClearBackground(rl.Black) rl.BeginMode3D(state.camera) if rl.IsMouseButtonPressed(rl.MouseButtonRight) { state.context_pos = rl.GetMousePosition() // TODO: Cast a ray into the bounding boxes } if state.context != nil { } rl.EndMode3D() rl.EndDrawing() } } }