Theme

alex/beta-minecraft-rust

Minecraft Beta 1.7.3 Rewrite

BRANCH main
main
SNAPSHOT Draw terrain GPU-driven from packed per-section buffers with GPU occlusion culling, add a render distance slider (2-128) with a quad/disk shape, cut chunk bookkeeping at high render distances, and fix a light underflow at y=0
DOWNLOAD
Download file

root / crates / client / src / menu / layout.rs

use super::{Action, Menu, Page};
use crate::settings::{Binding, Options, Setting, key_name};

pub struct Button {
    pub rect: [f32; 4],
    pub label: String,
    pub action: Action,
    pub enabled: bool,
    pub slider: Option<f32>,
}

pub fn contains([x, y, w, h]: [f32; 4], point: [f32; 2]) -> bool {
    point[0] >= x && point[0] < x + w && point[1] >= y && point[1] < y + h
}

impl Menu {
    pub fn field_rects(&self, [w, h]: [f32; 2]) -> [Option<[f32; 4]>; 2] {
        let x = (w / 2.0).floor() - 100.0;
        match self.page {
            Some(Page::Create) => [Some([x, 60.0, 200.0, 20.0]), Some([x, 116.0, 200.0, 20.0])],
            Some(Page::Rename) => [Some([x, 60.0, 200.0, 20.0]), None],
            Some(Page::Multiplayer) => [Some([x, (h / 4.0).floor() + 58.0, 200.0, 20.0]), None],
            Some(Page::Chat | Page::Sleep) => [Some([2.0, h - 14.0, w - 4.0, 12.0]), None],
            _ => [None; 2],
        }
    }

    pub fn buttons(&self, [w, h]: [f32; 2], options: &Options) -> Vec<Button> {
        let mut buttons = Vec::with_capacity(12);
        let center = (w / 2.0).floor();
        let quarter = (h / 4.0).floor();
        let sixth = (h / 6.0).floor();
        let mut add = |rect, label: &str, action| {
            buttons.push(Button {
                rect,
                label: label.into(),
                action,
                enabled: self.enabled(action, options),
                slider: None,
            })
        };
        let grid = |index: usize, width| {
            let x = center - 155.0 + (index % 2) as f32 * 160.0;
            [x, sixth + (index / 2) as f32 * 24.0, width, 20.0]
        };
        match self.page {
            Some(Page::Title) => {
                for (index, label, page) in [
                    (0, "Singleplayer", Page::Worlds),
                    (1, "Multiplayer", Page::Multiplayer),
                    (2, "Mods and Texture Packs", Page::Packs),
                ] {
                    let y = quarter + 48.0 + index as f32 * 24.0;
                    add([center - 100.0, y, 200.0, 20.0], label, Action::Open(page));
                }
                let options = Action::Open(Page::Options);
                add(
                    [center - 100.0, quarter + 132.0, 98.0, 20.0],
                    "Options...",
                    options,
                );
                add(
                    [center + 2.0, quarter + 132.0, 98.0, 20.0],
                    "Quit Game",
                    Action::Quit,
                );
            }
            Some(Page::Worlds) => {
                let play = "Play Selected World";
                add([center - 154.0, h - 52.0, 150.0, 20.0], play, Action::Play);
                let rename = Action::Open(Page::Rename);
                add([center - 154.0, h - 28.0, 70.0, 20.0], "Rename", rename);
                let delete = Action::Open(Page::Delete);
                add([center - 74.0, h - 28.0, 70.0, 20.0], "Delete", delete);
                let create = Action::Open(Page::Create);
                add(
                    [center + 4.0, h - 52.0, 150.0, 20.0],
                    "Create New World",
                    create,
                );
                add(
                    [center + 4.0, h - 28.0, 150.0, 20.0],
                    "Cancel",
                    Action::Back,
                );
            }
            Some(page @ (Page::Create | Page::Rename | Page::Multiplayer)) => {
                let (label, action) = match page {
                    Page::Create => ("Create New World", Action::Create),
                    Page::Rename => ("Rename", Action::Rename),
                    _ => ("Connect", Action::Connect),
                };
                add(
                    [center - 100.0, quarter + 108.0, 200.0, 20.0],
                    label,
                    action,
                );
                add(
                    [center - 100.0, quarter + 132.0, 200.0, 20.0],
                    "Cancel",
                    Action::Back,
                );
            }
            Some(Page::Delete) => {
                add(
                    [center - 155.0, sixth + 96.0, 150.0, 20.0],
                    "Delete",
                    Action::Delete,
                );
                add(
                    [center + 5.0, sixth + 96.0, 150.0, 20.0],
                    "Cancel",
                    Action::Back,
                );
            }
            Some(page @ (Page::Options | Page::Video)) => {
                let settings: &[Setting] = if page == Page::Options {
                    &Setting::GENERAL
                } else {
                    &Setting::VIDEO
                };
                for (index, &setting) in settings.iter().enumerate() {
                    add(
                        grid(index, 150.0),
                        &setting.label(options),
                        Action::Setting(setting),
                    );
                }
                if page == Page::Options {
                    let video = Action::Open(Page::Video);
                    add(
                        [center - 100.0, sixth + 108.0, 200.0, 20.0],
                        "Video Settings...",
                        video,
                    );
                    let controls = Action::Open(Page::Controls);
                    add(
                        [center - 100.0, sixth + 132.0, 200.0, 20.0],
                        "Controls...",
                        controls,
                    );
                }
                add(
                    [center - 100.0, sixth + 168.0, 200.0, 20.0],
                    "Done",
                    Action::Back,
                );
            }
            Some(Page::Controls) => {
                for (index, binding) in Binding::ALL.into_iter().enumerate() {
                    let name = key_name(options.keys[index]);
                    let label = if self.binding == Some(binding) {
                        format!("> {name} <")
                    } else {
                        name
                    };
                    add(grid(index, 70.0), &label, Action::Bind(binding));
                }
                add(
                    [center - 100.0, sixth + 168.0, 200.0, 20.0],
                    "Done",
                    Action::Back,
                );
            }
            Some(Page::Achievements) => {
                let y = (h / 2.0).floor() + 74.0;
                add([center + 24.0, y, 80.0, 20.0], "Done", Action::Resume);
            }
            Some(Page::Statistics) => {
                add([center + 4.0, h - 28.0, 150.0, 20.0], "Done", Action::Back);
                for (tab, label) in ["General", "Blocks", "Items"].into_iter().enumerate() {
                    let x = center - 154.0 + tab as f32 * 108.0;
                    add(
                        [x, h - 52.0, 100.0, 20.0],
                        label,
                        Action::StatsTab(tab as u8),
                    );
                }
            }
            Some(Page::Pause) => {
                add(
                    [center - 100.0, quarter + 8.0, 200.0, 20.0],
                    "Back to game",
                    Action::Resume,
                );
                let achievements = Action::Open(Page::Achievements);
                add(
                    [center - 100.0, quarter + 32.0, 98.0, 20.0],
                    "Achievements",
                    achievements,
                );
                let statistics = Action::Open(Page::Statistics);
                add(
                    [center + 2.0, quarter + 32.0, 98.0, 20.0],
                    "Statistics",
                    statistics,
                );
                let options = Action::Open(Page::Options);
                add(
                    [center - 100.0, quarter + 80.0, 200.0, 20.0],
                    "Options...",
                    options,
                );
                let leave = if self.remote {
                    "Disconnect"
                } else {
                    "Save and quit to title"
                };
                add(
                    [center - 100.0, quarter + 104.0, 200.0, 20.0],
                    leave,
                    Action::Leave,
                );
            }
            Some(Page::Packs) => {
                let folder = "Open texture pack folder";
                add(
                    [center - 154.0, h - 48.0, 150.0, 20.0],
                    folder,
                    Action::OpenPackFolder,
                );
                add([center + 4.0, h - 48.0, 150.0, 20.0], "Done", Action::Back);
            }
            Some(Page::Loading) if self.remote => {
                add(
                    [center - 100.0, quarter + 132.0, 200.0, 20.0],
                    "Cancel",
                    Action::Leave,
                );
            }
            Some(Page::Error) => {
                let back = if self.error_parent == Page::Title {
                    "Back to title screen"
                } else {
                    "Back"
                };
                add(
                    [center - 100.0, h / 2.0 + 64.0, 200.0, 20.0],
                    back,
                    Action::Back,
                );
            }
            Some(Page::Chat) => add([0.0; 4], "", Action::SendChat),
            Some(Page::Sign) => {
                add(
                    [center - 100.0, quarter + 120.0, 200.0, 20.0],
                    "Done",
                    Action::FinishSign,
                );
            }
            Some(Page::Sleep) => {
                add([0.0; 4], "", Action::SendChat);
                add(
                    [center - 100.0, h - 40.0, 200.0, 20.0],
                    "Leave Bed",
                    Action::LeaveBed,
                );
            }
            _ => {}
        }
        for button in &mut buttons {
            if let Action::Setting(setting) = button.action {
                button.slider = setting.slider(options);
            }
        }
        buttons
    }

    fn enabled(&self, action: Action, options: &Options) -> bool {
        match action {
            Action::Play | Action::Delete | Action::Open(Page::Rename | Page::Delete) => {
                self.selected_world().is_some()
            }
            Action::Create | Action::Rename | Action::Connect => {
                !self.fields[0].text.trim().is_empty()
            }
            Action::StatsTab(tab) => tab == 0 || !self.stat_items_for(tab).is_empty(),
            Action::Setting(Setting::DescriptorHeaps) => self.descriptor_heaps,
            Action::Setting(Setting::PresentMode) => self.present_modes.len() > 1,
            Action::Setting(Setting::FramePacing) => {
                self.frame_pacing && options.present_mode.is_fifo()
            }
            _ => true,
        }
    }
}