Add basic credits screen to the main menu with some example data loaded from a ron file

This commit is contained in:
Imbris 2021-09-24 01:48:29 -04:00
parent 5da9f93ff7
commit 40240a4005
7 changed files with 297 additions and 0 deletions

23
assets/common/credits.ron Normal file
View File

@ -0,0 +1,23 @@
(
music: [(
name: "Desert jams",
authors: ["AuthorOne", "author two"],
)],
fonts: [(
name: "Wizard",
license: "cc-by-sa 3",
)],
other_art: [(
name: "Voxel shrooms",
authors: ["AuthorOne", "author two"],
)],
contributors: [
(
name: "Example",
contributions: "An example note",
),
(
name: "Example Two",
),
],
)

View File

@ -61,6 +61,14 @@ https://veloren.net/account/."#,
"main.login.client_version": "Client Version",
"main.login.server_version": "Server Version",
"main.servers.select_server": "Select a server",
// Credits screen
"main.credits": "Credits",
"main.credits.music": "Music",
"main.credits.fonts": "Fonts",
"main.credits.other_art": "Other Art",
"main.credits.contributors": "Contributors",
/// End Main screen section
},

43
voxygen/src/credits.rs Normal file
View File

@ -0,0 +1,43 @@
use common::assets;
use serde::Deserialize;
// NOTE: we are free to split the manifest asset format and the format processed
// for display into separate structs but they happen to be identical for now
// TODO: add serde attribs to certain fields
#[derive(Clone, Deserialize)]
pub struct Art {
pub name: String,
// Include asset path as a field?
#[serde(default)]
pub authors: Vec<String>,
#[serde(default)]
pub license: String,
// Include optional license file path and/or web link?
}
#[derive(Clone, Deserialize)]
pub struct Contributor {
pub name: String,
/// Short note or description of the contributions
/// Optional, can be left empty/ommitted
#[serde(default)]
pub contributions: String,
}
/// Credits manifest processed into format for display in the UI
#[derive(Clone, Deserialize)]
pub struct Credits {
pub music: Vec<Art>,
pub fonts: Vec<Art>,
pub other_art: Vec<Art>,
pub contributors: Vec<Contributor>,
// TODO: include credits for dependencies where the license requires attribution?
}
impl assets::Asset for Credits {
type Loader = assets::RonLoader;
const EXTENSION: &'static str = "ron";
}

View File

@ -16,6 +16,7 @@
pub mod ui;
pub mod audio;
pub mod controller;
mod credits;
mod ecs;
pub mod error;
pub mod game_input;

View File

@ -0,0 +1,195 @@
use super::Message;
use crate::{
credits::Credits,
ui::{
fonts::IcedFonts as Fonts,
ice::{component::neat_button, style, Element},
},
};
use i18n::Localization;
use iced::{button, scrollable, Column, Container, Length, Scrollable, Space};
/// Connecting screen for the main menu
pub struct Screen {
back_button: button::State,
scroll: scrollable::State,
}
impl Screen {
pub fn new() -> Self {
Self {
back_button: Default::default(),
scroll: Default::default(),
}
}
pub(super) fn view(
&mut self,
fonts: &Fonts,
i18n: &Localization,
credits: &Credits,
button_style: style::button::Style,
) -> Element<Message> {
use core::fmt::Write;
// TODO: i18n and better formating
let format_art_credit = |credit: &crate::credits::Art| -> Result<String, core::fmt::Error> {
let mut text = String::new();
text.push_str(&credit.name);
let mut authors = credit.authors.iter();
if let Some(author) = authors.next() {
write!(&mut text, " created by {}", author)?;
}
authors.try_for_each(|author| write!(&mut text, ", {}", author))?;
if !credit.license.is_empty() {
write!(&mut text, " ({})", &credit.license)?;
}
Ok::<_, core::fmt::Error>(text)
};
let format_contributor_credit =
|credit: &crate::credits::Contributor| -> Result<String, core::fmt::Error> {
let mut text = String::new();
text.push_str(&credit.name);
if !credit.contributions.is_empty() {
write!(&mut text, ": {}", &credit.contributions)?;
}
Ok(text)
};
let music_header_color = iced::Color::from_rgb8(0xfc, 0x71, 0x76);
let fonts_header_color = iced::Color::from_rgb8(0xf7, 0xd1, 0x81);
let other_art_header_color = iced::Color::from_rgb8(0xc5, 0xe9, 0x80);
let contributors_header_color = iced::Color::from_rgb8(0x4a, 0xa6, 0x7b);
Container::new(
Container::new(
Column::with_children(vec![
iced::Text::new(i18n.get("main.credits"))
.font(fonts.alkhemi.id)
.size(fonts.alkhemi.scale(35))
.into(),
Space::new(Length::Fill, Length::Units(25)).into(),
Scrollable::new(&mut self.scroll)
.push(Column::with_children(
core::iter::once(
iced::Text::new(i18n.get("main.credits.music"))
.font(fonts.cyri.id)
.size(fonts.cyri.scale(30))
.color(music_header_color)
.into(),
)
.chain(credits.music.iter().map(|credit| {
let text = format_art_credit(credit).expect("Formatting failed!!!");
iced::Text::new(text)
.font(fonts.cyri.id)
.size(fonts.cyri.scale(23))
.into()
}))
.chain(core::iter::once(
Space::new(Length::Fill, Length::Units(15)).into(),
))
.collect(),
))
.push(Column::with_children(
core::iter::once(
iced::Text::new(i18n.get("main.credits.fonts"))
.font(fonts.cyri.id)
.size(fonts.cyri.scale(30))
.color(fonts_header_color)
.into(),
)
.chain(credits.fonts.iter().map(|credit| {
let text = format_art_credit(credit).expect("Formatting failed!!!");
iced::Text::new(text)
.font(fonts.cyri.id)
.size(fonts.cyri.scale(23))
.into()
}))
.chain(core::iter::once(
Space::new(Length::Fill, Length::Units(15)).into(),
))
.collect(),
))
.push(Column::with_children(
core::iter::once(
iced::Text::new(i18n.get("main.credits.other_art"))
.font(fonts.cyri.id)
.size(fonts.cyri.scale(30))
.color(other_art_header_color)
.into(),
)
.chain(credits.other_art.iter().map(|credit| {
let text = format_art_credit(credit).expect("Formatting failed!!!");
iced::Text::new(text)
.font(fonts.cyri.id)
.size(fonts.cyri.scale(23))
.into()
}))
.chain(core::iter::once(
Space::new(Length::Fill, Length::Units(15)).into(),
))
.collect(),
))
.push(Column::with_children(
core::iter::once(
iced::Text::new(i18n.get("main.credits.contributors"))
.font(fonts.cyri.id)
.size(fonts.cyri.scale(30))
.color(contributors_header_color)
.into(),
)
.chain(credits.contributors.iter().map(|credit| {
let text = format_contributor_credit(credit)
.expect("Formatting failed!!!");
iced::Text::new(text)
.font(fonts.cyri.id)
.size(fonts.cyri.scale(23))
.into()
}))
.chain(core::iter::once(
Space::new(Length::Fill, Length::Units(15)).into(),
))
.collect(),
))
.height(Length::FillPortion(1))
.into(),
Container::new(
Container::new(neat_button(
&mut self.back_button,
i18n.get("common.back"),
0.7,
button_style,
Some(Message::Back),
))
.height(Length::Units(fonts.cyri.scale(50))),
)
.center_x()
.height(Length::Shrink)
.width(Length::Fill)
.into(),
])
.spacing(5)
.padding(20)
.width(Length::Fill)
.height(Length::Fill),
)
.style(
style::container::Style::color_with_double_cornerless_border(
(22, 19, 17, 255).into(),
(11, 11, 11, 255).into(),
(54, 46, 38, 255).into(),
),
),
)
.center_x()
.center_y()
.padding(70)
.width(Length::Fill)
.height(Length::Fill)
.into()
}
}

View File

@ -26,6 +26,7 @@ pub struct Screen {
quit_button: button::State,
settings_button: button::State,
servers_button: button::State,
credits_button: button::State,
language_select_button: button::State,
error_okay_button: button::State,
@ -38,6 +39,7 @@ impl Screen {
pub fn new() -> Self {
Self {
servers_button: Default::default(),
credits_button: Default::default(),
settings_button: Default::default(),
quit_button: Default::default(),
language_select_button: Default::default(),
@ -85,6 +87,13 @@ impl Screen {
button_style,
Some(Message::OpenLanguageMenu),
),
neat_button(
&mut self.credits_button,
i18n.get("main.credits"),
FILL_FRAC_ONE,
button_style,
Some(Message::ShowCredits),
),
neat_button(
&mut self.quit_button,
i18n.get("common.quit"),

View File

@ -1,10 +1,12 @@
mod connecting;
// Note: Keeping in case we re-add the disclaimer
//mod disclaimer;
mod credits;
mod login;
mod servers;
use crate::{
credits::Credits,
render::UiDrawer,
ui::{
self,
@ -101,6 +103,9 @@ enum Screen {
/*Disclaimer {
screen: disclaimer::Screen,
},*/
Credits {
screen: credits::Screen,
},
Login {
screen: login::Screen,
// Error to display in a box
@ -124,6 +129,7 @@ struct Controls {
version: String,
// Alpha disclaimer
alpha: String,
credits: Credits,
selected_server_index: Option<usize>,
login_info: LoginInfo,
@ -141,6 +147,7 @@ enum Message {
Quit,
Back,
ShowServers,
ShowCredits,
#[cfg(feature = "singleplayer")]
Singleplayer,
Multiplayer,
@ -170,6 +177,8 @@ impl Controls {
let version = common::util::DISPLAY_VERSION_LONG.clone();
let alpha = format!("Veloren {}", common::util::DISPLAY_VERSION.as_str());
let credits = Credits::load_expect_cloned("common.credits");
// Note: Keeping in case we re-add the disclaimer
let screen = /* if settings.show_disclaimer {
Screen::Disclaimer {
@ -205,6 +214,7 @@ impl Controls {
i18n,
version,
alpha,
credits,
selected_server_index,
login_info,
@ -263,6 +273,9 @@ impl Controls {
let content = match &mut self.screen {
// Note: Keeping in case we re-add the disclaimer
//Screen::Disclaimer { screen } => screen.view(&self.fonts, &self.i18n, button_style),
Screen::Credits { screen } => {
screen.view(&self.fonts, &self.i18n.read(), &self.credits, button_style)
},
Screen::Login { screen, error } => screen.view(
&self.fonts,
&self.imgs,
@ -334,6 +347,11 @@ impl Controls {
};
}
},
Message::ShowCredits => {
self.screen = Screen::Credits {
screen: credits::Screen::new(),
};
},
#[cfg(feature = "singleplayer")]
Message::Singleplayer => {
self.screen = Screen::Connecting {