alex/beta-minecraft-rust
Minecraft Beta 1.7.3 Rewrite
BRANCH 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
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,
}
}
}