From acec45b756f5de5cf28862b23bc8391e379fc5b1 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 31 Mar 2023 14:24:14 +0100 Subject: [PATCH] Initial implementation of starting site choice --- assets/voxygen/i18n/en/char_selection.ftl | 3 + client/src/bin/bot/main.rs | 1 + client/src/bin/swarm/main.rs | 1 + client/src/lib.rs | 2 + common/net/src/msg/client.rs | 1 + server/src/character_creator.rs | 7 +- server/src/sys/msg/character_screen.rs | 40 +++- voxygen/src/hud/mod.rs | 18 +- voxygen/src/menu/char_selection/mod.rs | 3 +- voxygen/src/menu/char_selection/ui/mod.rs | 239 +++++++++++++++------- voxygen/src/ui/img_ids.rs | 11 + voxygen/src/ui/mod.rs | 3 + world/src/lib.rs | 5 + 13 files changed, 247 insertions(+), 87 deletions(-) diff --git a/assets/voxygen/i18n/en/char_selection.ftl b/assets/voxygen/i18n/en/char_selection.ftl index 29d4006e41..0aa81b7b18 100644 --- a/assets/voxygen/i18n/en/char_selection.ftl +++ b/assets/voxygen/i18n/en/char_selection.ftl @@ -19,5 +19,8 @@ char_selection-eye_color = Eye Color char_selection-skin = Skin char_selection-eyeshape = Eye Details char_selection-accessories = Accessories +char_selection-starting_site = Starting Site +char_selection-starting_site_name = Name: { $name } +char_selection-starting_site_kind = Kind: { $kind } char_selection-create_info_name = Your Character needs a name! char_selection-version_mismatch = WARNING! This server is running a different, possibly incompatible game version. Please update your game. diff --git a/client/src/bin/bot/main.rs b/client/src/bin/bot/main.rs index 9fcdade262..f1c31e449b 100644 --- a/client/src/bin/bot/main.rs +++ b/client/src/bin/bot/main.rs @@ -189,6 +189,7 @@ impl BotClient { Some("common.items.weapons.sword.starter".to_string()), None, body.into(), + None, ); client.load_character_list(); } diff --git a/client/src/bin/swarm/main.rs b/client/src/bin/swarm/main.rs index 735ad30b0c..0f90b170d3 100644 --- a/client/src/bin/swarm/main.rs +++ b/client/src/bin/swarm/main.rs @@ -148,6 +148,7 @@ fn run_client( Some("common.items.weapons.sword.starter".into()), None, body(), + None, ); client.load_character_list(); diff --git a/client/src/lib.rs b/client/src/lib.rs index ee8a800a59..6cd8c04800 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -951,6 +951,7 @@ impl Client { mainhand: Option, offhand: Option, body: comp::Body, + start_site: Option, ) { self.character_list.loading = true; self.send_msg(ClientGeneral::CreateCharacter { @@ -958,6 +959,7 @@ impl Client { mainhand, offhand, body, + start_site, }); } diff --git a/common/net/src/msg/client.rs b/common/net/src/msg/client.rs index e91f728d02..a599b9117a 100644 --- a/common/net/src/msg/client.rs +++ b/common/net/src/msg/client.rs @@ -48,6 +48,7 @@ pub enum ClientGeneral { mainhand: Option, offhand: Option, body: comp::Body, + start_site: Option, }, DeleteCharacter(CharacterId), EditCharacter { diff --git a/server/src/character_creator.rs b/server/src/character_creator.rs index 9e15bc3510..f1366def6c 100644 --- a/server/src/character_creator.rs +++ b/server/src/character_creator.rs @@ -1,7 +1,10 @@ use crate::persistence::{character_updater::CharacterUpdater, PersistedComponents}; use common::{ character::CharacterId, - comp::{inventory::loadout_builder::LoadoutBuilder, Body, Inventory, Item, SkillSet, Stats}, + comp::{ + inventory::loadout_builder::LoadoutBuilder, Body, Inventory, Item, SkillSet, Stats, + Waypoint, + }, }; use specs::{Entity, WriteExpect}; @@ -32,6 +35,7 @@ pub fn create_character( character_offhand: Option, body: Body, character_updater: &mut WriteExpect<'_, CharacterUpdater>, + waypoint: Option, ) -> Result<(), CreationError> { // quick fix whitelist validation for now; eventually replace the // `Option` with an index into a server-provided list of starter @@ -63,7 +67,6 @@ pub fn create_character( .push(Item::new_from_asset_expect("common.items.food.cheese")) .expect("Inventory has at least 1 slot left!"); - let waypoint = None; let map_marker = None; character_updater.create_character(entity, player_uuid, character_alias, PersistedComponents { diff --git a/server/src/sys/msg/character_screen.rs b/server/src/sys/msg/character_screen.rs index 6a05a5a8fb..2f29e1d541 100644 --- a/server/src/sys/msg/character_screen.rs +++ b/server/src/sys/msg/character_screen.rs @@ -1,3 +1,8 @@ +#[cfg(not(feature = "worldgen"))] +use crate::test_world::{IndexOwned, World}; +#[cfg(feature = "worldgen")] +use world::{IndexOwned, World}; + use crate::{ automod::AutoMod, character_creator, @@ -7,15 +12,17 @@ use crate::{ EditableSettings, }; use common::{ - comp::{Admin, AdminRole, ChatType, Player, UnresolvedChatMsg}, + comp::{Admin, AdminRole, ChatType, Player, UnresolvedChatMsg, Waypoint}, event::{EventBus, ServerEvent}, + resources::Time, + terrain::TerrainChunkSize, uid::Uid, }; use common_ecs::{Job, Origin, Phase, System}; use common_net::msg::{ClientGeneral, ServerGeneral}; use specs::{Entities, Join, Read, ReadExpect, ReadStorage, WriteExpect, WriteStorage}; use std::sync::{atomic::Ordering, Arc}; -use tracing::debug; +use tracing::{debug, error}; impl Sys { fn handle_client_character_screen_msg( @@ -32,6 +39,9 @@ impl Sys { censor: &ReadExpect<'_, Arc>, automod: &AutoMod, msg: ClientGeneral, + time: Time, + index: &ReadExpect<'_, IndexOwned>, + world: &ReadExpect<'_, Arc>, ) -> Result<(), crate::error::Error> { let mut send_join_messages = || -> Result<(), crate::error::Error> { // Give the player a welcome message @@ -135,6 +145,7 @@ impl Sys { mainhand, offhand, body, + start_site, } => { if censor.check(&alias) { debug!(?alias, "denied alias as it contained a banned word"); @@ -151,6 +162,22 @@ impl Sys { offhand.clone(), body, character_updater, + start_site.and_then(|site_idx| { + // TODO: This corresponds to the ID generation logic in `world/src/lib.rs` + // Really, we should have a way to consistently refer to sites, but that's a job for rtsim2 + // and the site changes that it will require. Until then, this code is very hacky. + world.civs().sites.iter() + .find(|(_, site)| site.site_tmp.map(|i| i.id()) == Some(site_idx)) + .map(Some) + .unwrap_or_else(|| { + error!("Tried to create character with starting site index {}, but such a site does not exist", site_idx); + None + }) + .map(|(_, site)| { + let wpos2d = TerrainChunkSize::center_wpos(site.center); + Waypoint::new(world.find_accessible_pos(index.as_index_ref(), wpos2d, true), time) + }) + }), ) { debug!( ?error, @@ -226,6 +253,9 @@ impl<'a> System<'a> for Sys { ReadExpect<'a, EditableSettings>, ReadExpect<'a, Arc>, ReadExpect<'a, AutoMod>, + ReadExpect<'a, Time>, + ReadExpect<'a, IndexOwned>, + ReadExpect<'a, Arc>, ); const NAME: &'static str = "msg::character_screen"; @@ -247,6 +277,9 @@ impl<'a> System<'a> for Sys { editable_settings, censor, automod, + time, + index, + world, ): Self::SystemData, ) { let mut server_emitter = server_event_bus.emitter(); @@ -267,6 +300,9 @@ impl<'a> System<'a> for Sys { &censor, &automod, msg, + *time, + &index, + &world, ) }); } diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index fecadcd7c2..2ec4def172 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -74,8 +74,8 @@ use crate::{ }, settings::chat::ChatFilter, ui::{ - self, fonts::Fonts, img_ids::Rotations, slot, slot::SlotKey, Graphic, Ingameable, - ScaleMode, Ui, + self, default_water_color, fonts::Fonts, img_ids::Rotations, slot, slot::SlotKey, Graphic, + Ingameable, ScaleMode, Ui, }, window::Event as WinEvent, GlobalState, @@ -106,7 +106,7 @@ use common::{ terrain::{SpriteKind, TerrainChunk, UnlockKind}, trade::{ReducedInventory, TradeAction}, uid::Uid, - util::{srgba_to_linear, Dir}, + util::Dir, vol::RectRasterableVol, }; use common_base::{prof_span, span}; @@ -1303,15 +1303,15 @@ impl Hud { ui.set_scaling_mode(settings.interface.ui_scale); // Generate ids. let ids = Ids::new(ui.id_generator()); - // NOTE: Use a border the same color as the LOD ocean color (but with a - // translucent alpha since UI have transparency and LOD doesn't). - let water_color = srgba_to_linear(Rgba::new(0.0, 0.18, 0.37, 1.0)); // Load world map let mut layers = Vec::new(); for layer in client.world_data().map_layers() { - layers.push( - ui.add_graphic_with_rotations(Graphic::Image(Arc::clone(layer), Some(water_color))), - ); + // NOTE: Use a border the same color as the LOD ocean color (but with a + // translucent alpha since UI have transparency and LOD doesn't). + layers.push(ui.add_graphic_with_rotations(Graphic::Image( + Arc::clone(layer), + Some(default_water_color()), + ))); } let world_map = (layers, client.world_data().chunk_size().map(|e| e as u32)); // Load images. diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index a024fa8f5c..777965066c 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -117,10 +117,11 @@ impl PlayState for CharSelectionState { mainhand, offhand, body, + start_site, } => { self.client .borrow_mut() - .create_character(alias, mainhand, offhand, body); + .create_character(alias, mainhand, offhand, body, start_site); }, ui::Event::EditCharacter { alias, diff --git a/voxygen/src/menu/char_selection/ui/mod.rs b/voxygen/src/menu/char_selection/ui/mod.rs index 63afdf5273..3f7518dd30 100644 --- a/voxygen/src/menu/char_selection/ui/mod.rs +++ b/voxygen/src/menu/char_selection/ui/mod.rs @@ -1,7 +1,7 @@ use crate::{ render::UiDrawer, ui::{ - self, + self, default_water_color, fonts::IcedFonts as Fonts, ice::{ component::{ @@ -15,7 +15,8 @@ use crate::{ }, Element, IcedRenderer, IcedUi as Ui, }, - img_ids::ImageGraphic, + img_ids::{GraphicCreator, ImageGraphic, PixelGraphic}, + GraphicId, }, window, GlobalState, }; @@ -25,6 +26,7 @@ use common::{ comp::{self, humanoid, inventory::slot::EquipSlot, Inventory, Item}, LoadoutBuilder, }; +use common_net::msg::world_msg::{SiteId, SiteInfo, SiteKind}; use i18n::{Localization, LocalizationHandle}; //ImageFrame, Tooltip, use crate::settings::Settings; @@ -133,6 +135,7 @@ pub enum Event { mainhand: Option, offhand: Option, body: comp::Body, + start_site: Option, }, EditCharacter { alias: String, @@ -168,13 +171,15 @@ enum Mode { species_buttons: [button::State; 6], tool_buttons: [button::State; 6], sliders: Sliders, - scroll: scrollable::State, + left_scroll: scrollable::State, + right_scroll: scrollable::State, name_input: text_input::State, back_button: button::State, create_button: button::State, rand_character_button: button::State, rand_name_button: button::State, character_id: Option, + start_site_idx: usize, }, } @@ -217,13 +222,15 @@ impl Mode { species_buttons: Default::default(), tool_buttons: Default::default(), sliders: Default::default(), - scroll: Default::default(), + left_scroll: Default::default(), + right_scroll: Default::default(), name_input: Default::default(), back_button: Default::default(), create_button: Default::default(), rand_character_button: Default::default(), rand_name_button: Default::default(), character_id: None, + start_site_idx: 0, } } @@ -243,13 +250,15 @@ impl Mode { species_buttons: Default::default(), tool_buttons: Default::default(), sliders: Default::default(), - scroll: Default::default(), + left_scroll: Default::default(), + right_scroll: Default::default(), name_input: Default::default(), back_button: Default::default(), create_button: Default::default(), rand_character_button: Default::default(), rand_name_button: Default::default(), character_id: Some(character_id), + start_site_idx: 0, } } } @@ -279,6 +288,8 @@ struct Controls { // Id of the selected character selected: Option, default_name: String, + map_img: GraphicId, + possible_starting_sites: Vec, } #[derive(Clone)] @@ -309,6 +320,7 @@ enum Message { EyeColor(u8), Accessory(u8), Beard(u8), + StartSite(usize), // Workaround for widgets that require a message but we don't want them to actually do // anything DoNothing, @@ -321,6 +333,8 @@ impl Controls { selected: Option, default_name: String, server_info: &ServerInfo, + map_img: GraphicId, + possible_starting_sites: Vec, ) -> Self { let version = common::util::DISPLAY_VERSION_LONG.clone(); let alpha = format!("Veloren {}", common::util::DISPLAY_VERSION.as_str()); @@ -339,6 +353,8 @@ impl Controls { mode: Mode::select(Some(InfoContent::LoadingCharacters)), selected, default_name, + map_img, + possible_starting_sites, } } @@ -831,7 +847,8 @@ impl Controls { inventory: _, mainhand, offhand: _, - ref mut scroll, + ref mut left_scroll, + ref mut right_scroll, ref mut body_type_buttons, ref mut species_buttons, ref mut tool_buttons, @@ -842,6 +859,7 @@ impl Controls { ref mut rand_character_button, ref mut rand_name_button, character_id, + start_site_idx, } => { let unselected_style = style::button::Style::new(imgs.icon_border) .hover_image(imgs.icon_border_mo) @@ -1070,19 +1088,19 @@ impl Controls { // Height of interactable area const SLIDER_HEIGHT: u16 = 30; - fn char_slider<'a>( + fn char_slider<'a, T: Copy + Into>( text: String, state: &'a mut slider::State, - max: u8, - selected_val: u8, - on_change: impl 'static + Fn(u8) -> Message, + max: T, + selected_val: T, + on_change: impl 'static + Fn(u32) -> Message, (fonts, imgs): (&Fonts, &Imgs), ) -> Element<'a, Message> { Column::with_children(vec![ Text::new(text) .size(fonts.cyri.scale(SLIDER_TEXT_SIZE)) .into(), - Slider::new(state, 0..=max, selected_val, on_change) + Slider::new(state, 0..=max.into(), selected_val.into(), on_change) .height(SLIDER_HEIGHT) .style(style::slider::Style::images( imgs.slider_indicator, @@ -1096,13 +1114,13 @@ impl Controls { .align_items(Align::Center) .into() } - fn char_slider_greyable<'a>( + fn char_slider_greyable<'a, T: Copy + Into>( active: bool, text: String, state: &'a mut slider::State, - max: u8, - selected_val: u8, - on_change: impl 'static + Fn(u8) -> Message, + max: T, + selected_val: T, + on_change: impl 'static + Fn(u32) -> Message, (fonts, imgs): (&Fonts, &Imgs), ) -> Element<'a, Message> { if active { @@ -1115,19 +1133,21 @@ impl Controls { .into(), // "Disabled" slider // TODO: add iced support for disabled sliders (like buttons) - Slider::new(state, 0..=max, selected_val, |_| Message::DoNothing) - .height(SLIDER_HEIGHT) - .style(style::slider::Style { - cursor: style::slider::Cursor::Color(Rgba::zero()), - bar: style::slider::Bar::Image( - imgs.slider_range, - Rgba::from_translucent(255, 51), - SLIDER_BAR_PAD, - ), - labels: false, - ..Default::default() - }) - .into(), + Slider::new(state, 0..=max.into(), selected_val.into(), |_| { + Message::DoNothing + }) + .height(SLIDER_HEIGHT) + .style(style::slider::Style { + cursor: style::slider::Cursor::Color(Rgba::zero()), + bar: style::slider::Bar::Image( + imgs.slider_range, + Rgba::from_translucent(255, 51), + SLIDER_BAR_PAD, + ), + labels: false, + ..Default::default() + }) + .into(), ]) .align_items(Align::Center) .into() @@ -1140,7 +1160,7 @@ impl Controls { &mut sliders.hair_style, body.species.num_hair_styles(body.body_type) - 1, body.hair_style, - Message::HairStyle, + |x| Message::HairStyle(x as u8), (fonts, imgs), ), char_slider( @@ -1148,7 +1168,7 @@ impl Controls { &mut sliders.hair_color, body.species.num_hair_colors() - 1, body.hair_color, - Message::HairColor, + |x| Message::HairColor(x as u8), (fonts, imgs), ), char_slider( @@ -1156,7 +1176,7 @@ impl Controls { &mut sliders.skin, body.species.num_skin_colors() - 1, body.skin, - Message::Skin, + |x| Message::Skin(x as u8), (fonts, imgs), ), char_slider( @@ -1164,7 +1184,7 @@ impl Controls { &mut sliders.eyes, body.species.num_eyes(body.body_type) - 1, body.eyes, - Message::Eyes, + |x| Message::Eyes(x as u8), (fonts, imgs), ), char_slider( @@ -1172,7 +1192,7 @@ impl Controls { &mut sliders.eye_color, body.species.num_eye_colors() - 1, body.eye_color, - Message::EyeColor, + |x| Message::EyeColor(x as u8), (fonts, imgs), ), char_slider_greyable( @@ -1181,7 +1201,7 @@ impl Controls { &mut sliders.accessory, body.species.num_accessories(body.body_type) - 1, body.accessory, - Message::Accessory, + |x| Message::Accessory(x as u8), (fonts, imgs), ), char_slider_greyable( @@ -1190,7 +1210,7 @@ impl Controls { &mut sliders.beard, body.species.num_beards(body.body_type) - 1, body.beard, - Message::Beard, + |x| Message::Beard(x as u8), (fonts, imgs), ), ]) @@ -1213,7 +1233,7 @@ impl Controls { tooltip::text(&tooltip_text, tooltip_style) }); - let column_content = vec![ + let left_column_content = vec![ body_type.into(), tool.into(), species.into(), @@ -1221,47 +1241,96 @@ impl Controls { rand_character.into(), ]; - let left_column = Container::new( - Scrollable::new(scroll) - .push( - Column::with_children(column_content) - .align_items(Align::Center) - .width(Length::Fill) - .spacing(5), - ) - .padding(5) - .width(Length::Fill) - .align_items(Align::Center) - .style(style::scrollable::Style { - track: None, - scroller: style::scrollable::Scroller::Color(UI_MAIN), - }), - ) - .width(Length::Units(320)) // TODO: see if we can get iced to work with settings below - //.max_width(360) - //.width(Length::Fill) - .height(Length::Fill); + let right_column_content = vec![ + Image::new(self.map_img) + .height(Length::Units(300)) + .width(Length::Units(300)) + .into(), + Column::with_children(if self.possible_starting_sites.is_empty() { + Vec::new() + } else { + let site_slider = char_slider( + i18n.get_msg("char_selection-starting_site").into_owned(), + &mut sliders.starting_site, + self.possible_starting_sites.len() as u32 - 1, + *start_site_idx as u32, + |x| Message::StartSite(x as usize), + (fonts, imgs), + ); - let left_column = Column::with_children(vec![ - Container::new(left_column) - .style(style::container::Style::color(Rgba::from_translucent( - 0, - BANNER_ALPHA, - ))) - .width(Length::Units(320)) - .center_x() - .into(), - Image::new(imgs.frame_bottom) - .height(Length::Units(40)) - .width(Length::Units(320)) - .color(Rgba::from_translucent(0, BANNER_ALPHA)) - .into(), - ]) - .height(Length::Fill); + let site_name = Text::new(i18n + .get_msg_ctx("char_selection-starting_site_name", &i18n::fluent_args! { + "name" => self.possible_starting_sites[*start_site_idx].name.as_deref() + .unwrap_or("Unknown"), + }) + .into_owned()) + .size(fonts.cyri.scale(SLIDER_TEXT_SIZE)) + .into(); + + let site_kind = Text::new(i18n + .get_msg_ctx("char_selection-starting_site_kind", &i18n::fluent_args! { + "kind" => match self.possible_starting_sites[*start_site_idx].kind { + SiteKind::Town => i18n.get_msg("hud-map-town").into_owned(), + SiteKind::Castle => i18n.get_msg("hud-map-castle").into_owned(), + SiteKind::Bridge => i18n.get_msg("hud-map-bridge").into_owned(), + _ => "Unknown".to_string(), + }, + }) + .into_owned()) + .size(fonts.cyri.scale(SLIDER_TEXT_SIZE)) + .into(); + + vec![site_slider, site_name, site_kind] + }) + .max_width(200) + .padding(5) + .into(), + ]; + + let column = |column_content, scroll| { + let column = Container::new( + Scrollable::new(scroll) + .push( + Column::with_children(column_content) + .align_items(Align::Center) + .width(Length::Fill) + .spacing(5), + ) + .padding(5) + .width(Length::Fill) + .align_items(Align::Center) + .style(style::scrollable::Style { + track: None, + scroller: style::scrollable::Scroller::Color(UI_MAIN), + }), + ) + .width(Length::Units(320)) // TODO: see if we can get iced to work with settings below + //.max_width(360) + //.width(Length::Fill) + .height(Length::Fill); + + Column::with_children(vec![ + Container::new(column) + .style(style::container::Style::color(Rgba::from_translucent( + 0, + BANNER_ALPHA, + ))) + .width(Length::Units(320)) + .center_x() + .into(), + Image::new(imgs.frame_bottom) + .height(Length::Units(40)) + .width(Length::Units(320)) + .color(Rgba::from_translucent(0, BANNER_ALPHA)) + .into(), + ]) + .height(Length::Fill) + }; let top = Row::with_children(vec![ - left_column.into(), + column(left_column_content, left_scroll).into(), MouseDetector::new(&mut self.mouse_detector, Length::Fill, Length::Fill).into(), + column(right_column_content, right_scroll).into(), ]) .padding(10) .width(Length::Fill) @@ -1516,6 +1585,7 @@ impl Controls { body, mainhand, offhand, + start_site_idx, .. } = &self.mode { @@ -1524,6 +1594,10 @@ impl Controls { mainhand: mainhand.map(String::from), offhand: offhand.map(String::from), body: comp::Body::Humanoid(*body), + start_site: self + .possible_starting_sites + .get(*start_site_idx) + .map(|info| info.id), }); self.mode = Mode::select(Some(InfoContent::CreatingCharacter)); } @@ -1643,6 +1717,11 @@ impl Controls { body.validate(); } }, + Message::StartSite(idx) => { + if let Mode::CreateOrEdit { start_site_idx, .. } = &mut self.mode { + *start_site_idx = idx; + } + }, } } @@ -1707,6 +1786,19 @@ impl CharSelectionUi { selected_character, default_name, client.server_info(), + ui.add_graphic( + PixelGraphic::new_graphic(( + client.world_data().map_image().clone(), + Some(default_water_color()), + )) + .expect("Failed to generate map image"), + ), + client.sites() + .values() + // TODO: Enforce this server-side and add some way to customise it? + .filter(|info| matches!(&info.site.kind, SiteKind::Town | SiteKind::Castle | SiteKind::Bridge)) + .map(|info| info.site.clone()) + .collect(), ); Self { @@ -1814,4 +1906,5 @@ struct Sliders { eye_color: slider::State, accessory: slider::State, beard: slider::State, + starting_site: slider::State, } diff --git a/voxygen/src/ui/img_ids.rs b/voxygen/src/ui/img_ids.rs index dd515ba514..1da1de3152 100644 --- a/voxygen/src/ui/img_ids.rs +++ b/voxygen/src/ui/img_ids.rs @@ -3,6 +3,7 @@ use common::{ assets::{self, AssetExt, DotVoxAsset, Error}, figure::Segment, }; +use image::DynamicImage; use std::sync::Arc; use vek::*; @@ -112,6 +113,16 @@ impl<'a> GraphicCreator<'a> for VoxelPixArtGraphic { } } +pub enum PixelGraphic {} + +impl<'a> GraphicCreator<'a> for PixelGraphic { + type Specifier = (Arc, Option>); + + fn new_graphic((img, col): Self::Specifier) -> Result { + Ok(Graphic::Image(img, col)) + } +} + pub struct Rotations { pub none: conrod_core::image::Id, pub cw90: conrod_core::image::Id, diff --git a/voxygen/src/ui/mod.rs b/voxygen/src/ui/mod.rs index 535ec2e3ad..bc570c5dbe 100644 --- a/voxygen/src/ui/mod.rs +++ b/voxygen/src/ui/mod.rs @@ -1090,3 +1090,6 @@ fn default_scissor(physical_resolution: Vec2) -> Aabr { }, } } + +// TODO: Don't hard code this +pub fn default_water_color() -> Rgba { srgba_to_linear(Rgba::new(0.0, 0.18, 0.37, 1.0)) } diff --git a/world/src/lib.rs b/world/src/lib.rs index baede46498..5b187fcae3 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -219,6 +219,11 @@ impl World { pub fn sample_blocks(&self) -> BlockGen { BlockGen::new(ColumnGen::new(&self.sim)) } + /// Find a position that's accessible to a player at the given world + /// position by searching blocks vertically. + /// + /// If `ascending` is `true`, we try to find the highest accessible position + /// instead of the lowest. pub fn find_accessible_pos( &self, index: IndexRef,