Char Window, Mana, Health, Exp-Bar

Former-commit-id: 546d29aae7441232892c7b7438708e93fb440128
This commit is contained in:
Pfauenauge 2019-04-06 00:36:04 +02:00
parent c1af42522d
commit 8ecb849970
6 changed files with 635 additions and 60 deletions

2
.gitignore vendored
View File

@ -22,3 +22,5 @@
assets/voxygen assets/voxygen
UI3.rar UI3.rar
assets.rar assets.rar
*.rar
assets/voxygen

74
common/src/net/error.rs Normal file
View File

@ -0,0 +1,74 @@
#[derive(Debug)]
pub enum PostError {
InvalidMessage,
InternalError,
Disconnected,
}
#[derive(Debug)]
pub enum PostErrorInternal {
Io(std::io::Error),
Serde(bincode::Error),
ChannelRecv(std::sync::mpsc::TryRecvError),
ChannelSend, // Empty because I couldn't figure out how to handle generic type in mpsc::TrySendError properly
MsgSizeLimitExceeded,
MioError,
}
impl<'a, T: Into<&'a PostErrorInternal>> From<T> for PostError {
fn from(err: T) -> Self {
match err.into() {
// TODO: Are I/O errors always disconnect errors?
PostErrorInternal::Io(_) => PostError::Disconnected,
PostErrorInternal::Serde(_) => PostError::InvalidMessage,
PostErrorInternal::MsgSizeLimitExceeded => PostError::InvalidMessage,
PostErrorInternal::MioError => PostError::InternalError,
PostErrorInternal::ChannelRecv(_) => PostError::InternalError,
PostErrorInternal::ChannelSend => PostError::InternalError,
}
}
}
impl From<PostErrorInternal> for PostError {
fn from(err: PostErrorInternal) -> Self {
(&err).into()
}
}
impl From<std::io::Error> for PostErrorInternal {
fn from(err: std::io::Error) -> Self {
PostErrorInternal::Io(err)
}
}
impl From<bincode::Error> for PostErrorInternal {
fn from(err: bincode::Error) -> Self {
PostErrorInternal::Serde(err)
}
}
impl From<std::sync::mpsc::TryRecvError> for PostErrorInternal {
fn from(err: std::sync::mpsc::TryRecvError) -> Self {
PostErrorInternal::ChannelRecv(err)
}
}
impl From<std::io::Error> for PostError {
fn from(err: std::io::Error) -> Self {
(&PostErrorInternal::from(err)).into()
}
}
impl From<bincode::Error> for PostError {
fn from(err: bincode::Error) -> Self {
(&PostErrorInternal::from(err)).into()
}
}
impl From<std::sync::mpsc::TryRecvError> for PostError {
fn from(err: std::sync::mpsc::TryRecvError) -> Self {
(&PostErrorInternal::from(err)).into()
}
}

270
common/src/net/postbox.rs Normal file
View File

@ -0,0 +1,270 @@
// Standard
use std::{
collections::VecDeque,
convert::TryFrom,
io::{
ErrorKind,
Read,
},
net::SocketAddr,
thread,
time::Duration,
sync::mpsc::TryRecvError,
};
// External
use bincode;
use mio::{net::TcpStream, Events, Poll, PollOpt, Ready, Token};
use mio_extras::channel::{channel, Receiver, Sender};
// Crate
use super::{
data::ControlMsg,
error::{
PostError,
PostErrorInternal,
},
PostRecv,
PostSend,
};
// Constants
const CTRL_TOKEN: Token = Token(0); // Token for thread control messages
const DATA_TOKEN: Token = Token(1); // Token for thread data exchange
const CONN_TOKEN: Token = Token(2); // Token for TcpStream for the PostBox child thread
const MESSAGE_SIZE_CAP: u64 = 1 << 20; // Maximum accepted length of a packet
/// A high-level wrapper of [`TcpStream`](mio::net::TcpStream).
/// [`PostBox`] takes care of serializing sent packets and deserializing received packets in the background, providing a simple API for sending and receiving objects over network.
pub struct PostBox<S, R>
where
S: PostSend,
R: PostRecv,
{
handle: Option<thread::JoinHandle<()>>,
ctrl: Sender<ControlMsg>,
recv: Receiver<Result<R, PostErrorInternal>>,
send: Sender<S>,
poll: Poll,
err: Option<PostErrorInternal>,
}
impl<S, R> PostBox<S, R>
where
S: PostSend,
R: PostRecv,
{
/// Creates a new [`PostBox`] connected to specified address, meant to be used by the client
pub fn to_server<A: Into<SocketAddr>>(addr: A) -> Result<PostBox<S, R>, PostError> {
let connection = TcpStream::connect(&addr.into())?;
Self::from_tcpstream(connection)
}
/// Creates a new [`PostBox`] from an existing connection, meant to be used by [`PostOffice`](super::PostOffice) on the server
pub fn from_tcpstream(connection: TcpStream) -> Result<PostBox<S, R>, PostError> {
let (ctrl_tx, ctrl_rx) = channel(); // Control messages
let (send_tx, send_rx) = channel(); // main thread -[data to be serialized and sent]> worker thread
let (recv_tx, recv_rx) = channel(); // main thread <[received and deserialized data]- worker thread
let thread_poll = Poll::new().unwrap();
let postbox_poll = Poll::new().unwrap();
thread_poll
.register(&connection, CONN_TOKEN, Ready::readable(), PollOpt::edge())
.unwrap();
thread_poll
.register(&ctrl_rx, CTRL_TOKEN, Ready::readable(), PollOpt::edge())
.unwrap();
thread_poll
.register(&send_rx, DATA_TOKEN, Ready::readable(), PollOpt::edge())
.unwrap();
postbox_poll
.register(&recv_rx, DATA_TOKEN, Ready::readable(), PollOpt::edge())
.unwrap();
let handle = thread::Builder::new()
.name("postbox_worker".into())
.spawn(move || postbox_thread(connection, ctrl_rx, send_rx, recv_tx, thread_poll))?;
Ok(PostBox {
handle: Some(handle),
ctrl: ctrl_tx,
recv: recv_rx,
send: send_tx,
poll: postbox_poll,
err: None,
})
}
/// Return an `Option<PostError>` indicating the current status of the `PostBox`.
pub fn status(&self) -> Option<PostError> {
self.err.as_ref().map(|err| err.into())
}
/// Non-blocking sender method
pub fn send(&mut self, data: S) -> Result<(), PostError> {
match &mut self.err {
err @ None => if let Err(_) = self.send.send(data) {
*err = Some(PostErrorInternal::MioError);
Err(err.as_ref().unwrap().into())
} else {
Ok(())
},
err => Err(err.as_ref().unwrap().into()),
}
}
/// Non-blocking receiver method returning an iterator over already received and deserialized objects
/// # Errors
/// If the other side disconnects PostBox won't realize that until you try to send something
pub fn new_messages(&mut self) -> impl ExactSizeIterator<Item = R> {
let mut events = Events::with_capacity(4096);
let mut items = VecDeque::new();
// If an error occured, or previously occured, just give up
if let Some(_) = self.err {
return items.into_iter();
} else if let Err(err) = self.poll.poll(&mut events, Some(Duration::new(0, 0))) {
self.err = Some(err.into());
return items.into_iter();
}
for event in events {
match event.token() {
DATA_TOKEN => loop {
match self.recv.try_recv() {
Ok(Ok(item)) => items.push_back(item),
Err(TryRecvError::Empty) => break,
Err(err) => self.err = Some(err.into()),
Ok(Err(err)) => self.err = Some(err.into()),
}
},
_ => (),
}
}
items.into_iter()
}
}
fn postbox_thread<S, R>(
mut connection: TcpStream,
ctrl_rx: Receiver<ControlMsg>,
send_rx: Receiver<S>,
recv_tx: Sender<Result<R, PostErrorInternal>>,
poll: Poll,
) where
S: PostSend,
R: PostRecv,
{
// Receiving related variables
let mut events = Events::with_capacity(64);
let mut recv_buff = Vec::new();
let mut recv_nextlen: u64 = 0;
loop {
let mut disconnected = false;
poll.poll(&mut events, Some(Duration::from_millis(20)))
.expect("Failed to execute poll(), most likely fault of the OS");
println!("FINISHED POLL!");
for event in events.iter() {
println!("EVENT!");
match event.token() {
CTRL_TOKEN => match ctrl_rx.try_recv().unwrap() {
ControlMsg::Shutdown => return,
},
CONN_TOKEN => match connection.read_to_end(&mut recv_buff) {
Ok(_) => {}
// Returned when all the data has been read
Err(ref e) if e.kind() == ErrorKind::WouldBlock => {}
Err(e) => recv_tx.send(Err(e.into())).unwrap(),
},
DATA_TOKEN => {
let msg = send_rx.try_recv().unwrap();
println!("Send: {:?}", msg);
let mut packet = bincode::serialize(&msg).unwrap();
packet.splice(0..0, (packet.len() as u64).to_be_bytes().iter().cloned());
match connection.write_bufs(&[packet.as_slice().into()]) {
Ok(_) => { println!("Sent!"); }
Err(e) => {
println!("Send error!");
recv_tx.send(Err(e.into())).unwrap();
}
};
}
_ => {}
}
}
loop {
if recv_nextlen == 0 && recv_buff.len() >= 8 {
println!("Read nextlen");
recv_nextlen = u64::from_be_bytes(
<[u8; 8]>::try_from(recv_buff.drain(0..8).collect::<Vec<u8>>().as_slice()).unwrap(),
);
if recv_nextlen > MESSAGE_SIZE_CAP {
recv_tx.send(Err(PostErrorInternal::MsgSizeLimitExceeded)).unwrap();
connection.shutdown(std::net::Shutdown::Both).unwrap();
recv_buff.drain(..);
recv_nextlen = 0;
break;
}
}
if recv_buff.len() as u64 >= recv_nextlen && recv_nextlen != 0 {
match bincode::deserialize(recv_buff
.drain(
0..usize::try_from(recv_nextlen)
.expect("Message size was larger than usize (insane message size and 32 bit OS)"),
)
.collect::<Vec<u8>>()
.as_slice()) {
Ok(msg) => {
println!("Recv: {:?}", msg);
recv_tx
.send(Ok(msg))
.unwrap();
recv_nextlen = 0;
}
Err(e) => {
println!("Recv error: {:?}", e);
recv_tx.send(Err(e.into())).unwrap();
recv_nextlen = 0;
}
}
} else {
break;
}
}
match connection.take_error().unwrap() {
Some(e) => {
if e.kind() == ErrorKind::BrokenPipe {
disconnected = true;
}
recv_tx.send(Err(e.into())).unwrap();
}
None => {}
}
if disconnected == true {
break;
}
}
// Loop after disconnected
loop {
poll.poll(&mut events, None)
.expect("Failed to execute poll(), most likely fault of the OS");
for event in events.iter() {
match event.token() {
CTRL_TOKEN => match ctrl_rx.try_recv().unwrap() {
ControlMsg::Shutdown => return,
},
_ => {}
}
}
}
}
impl<S, R> Drop for PostBox<S, R>
where
S: PostSend,
R: PostRecv,
{
fn drop(&mut self) {
self.ctrl.send(ControlMsg::Shutdown).unwrap_or(());
self.handle.take().map(|handle| handle.join());
}
}

View File

@ -0,0 +1,150 @@
// Standard
use core::time::Duration;
use std::{
collections::VecDeque,
net::SocketAddr,
thread,
sync::mpsc::TryRecvError,
};
// External
use mio::{net::TcpListener, Events, Poll, PollOpt, Ready, Token};
use mio_extras::channel::{channel, Receiver, Sender};
// Crate
use super::{
data::ControlMsg,
error::{
PostError,
PostErrorInternal,
},
postbox::PostBox,
PostRecv,
PostSend,
};
// Constants
const CTRL_TOKEN: Token = Token(0); // Token for thread control messages
const DATA_TOKEN: Token = Token(1); // Token for thread data exchange
const CONN_TOKEN: Token = Token(2); // Token for TcpStream for the PostBox child thread
/// A high-level wrapper of [`TcpListener`](mio::net::TcpListener).
/// [`PostOffice`] listens for incoming connections in the background and wraps them into [`PostBox`]es, providing a simple non-blocking API for receiving them.
pub struct PostOffice<S, R>
where
S: PostSend,
R: PostRecv,
{
handle: Option<thread::JoinHandle<()>>,
ctrl: Sender<ControlMsg>,
recv: Receiver<Result<PostBox<S, R>, PostErrorInternal>>,
poll: Poll,
err: Option<PostErrorInternal>,
}
impl<S, R> PostOffice<S, R>
where
S: PostSend,
R: PostRecv,
{
/// Creates a new [`PostOffice`] listening on specified address
pub fn new<A: Into<SocketAddr>>(addr: A) -> Result<Self, PostError> {
let listener = TcpListener::bind(&addr.into())?;
let (ctrl_tx, ctrl_rx) = channel();
let (recv_tx, recv_rx) = channel();
let thread_poll = Poll::new()?;
let postbox_poll = Poll::new()?;
thread_poll.register(&listener, CONN_TOKEN, Ready::readable(), PollOpt::edge())?;
thread_poll.register(&ctrl_rx, CTRL_TOKEN, Ready::readable(), PollOpt::edge())?;
postbox_poll.register(&recv_rx, DATA_TOKEN, Ready::readable(), PollOpt::edge())?;
let handle = thread::Builder::new()
.name("postoffice_worker".into())
.spawn(move || postoffice_thread(listener, ctrl_rx, recv_tx, thread_poll))?;
Ok(PostOffice {
handle: Some(handle),
ctrl: ctrl_tx,
recv: recv_rx,
poll: postbox_poll,
err: None,
})
}
/// Return an `Option<PostError>` indicating the current status of the `PostOffice`.
pub fn status(&self) -> Option<PostError> {
self.err.as_ref().map(|err| err.into())
}
/// Non-blocking method returning an iterator over new connections wrapped in [`PostBox`]es
pub fn new_connections(
&mut self,
) -> impl ExactSizeIterator<Item = PostBox<S, R>> {
let mut events = Events::with_capacity(256);
let mut conns = VecDeque::new();
// If an error occured, or previously occured, just give up
if let Some(_) = self.err {
return conns.into_iter();
} else if let Err(err) = self.poll.poll(&mut events, Some(Duration::new(0, 0))) {
self.err = Some(err.into());
return conns.into_iter();
}
for event in events {
match event.token() {
DATA_TOKEN => loop {
match self.recv.try_recv() {
Ok(Ok(conn)) => conns.push_back(conn),
Err(TryRecvError::Empty) => break,
Err(err) => self.err = Some(err.into()),
Ok(Err(err)) => self.err = Some(err.into()),
}
},
_ => (),
}
}
conns.into_iter()
}
}
fn postoffice_thread<S, R>(
listener: TcpListener,
ctrl_rx: Receiver<ControlMsg>,
recv_tx: Sender<Result<PostBox<S, R>, PostErrorInternal>>,
poll: Poll,
) where
S: PostSend,
R: PostRecv,
{
let mut events = Events::with_capacity(256);
loop {
poll.poll(&mut events, None).expect("Failed to execute recv_poll.poll() in PostOffce receiver thread, most likely fault of the OS.");
for event in events.iter() {
match event.token() {
CTRL_TOKEN => match ctrl_rx.try_recv().unwrap() {
ControlMsg::Shutdown => return,
},
CONN_TOKEN => {
let (conn, _addr) = listener.accept().unwrap();
recv_tx.send(PostBox::from_tcpstream(conn)
// TODO: Is it okay to count a failure to create a postbox here as an 'internal error'?
.map_err(|_| PostErrorInternal::MioError)).unwrap();
}
_ => (),
}
}
}
}
impl<S, R> Drop for PostOffice<S, R>
where
S: PostSend,
R: PostRecv,
{
fn drop(&mut self) {
self.ctrl.send(ControlMsg::Shutdown).unwrap_or(()); // If this fails the thread is dead already
self.handle.take().map(|handle| handle.join());
}
}

View File

@ -10,7 +10,7 @@ use conrod_core::{
image::Id as ImgId, image::Id as ImgId,
text::font::Id as FontId, text::font::Id as FontId,
widget::{Button, Image, Rectangle, Scrollbar, Text}, widget::{Button, Image, Rectangle, Scrollbar, Text},
widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget,
}; };
widget_ids! { widget_ids! {
@ -62,6 +62,9 @@ widget_ids! {
sb_grid_r, sb_grid_r,
sb_grid_bg_l, sb_grid_bg_l,
sb_grid_bg_r, sb_grid_bg_r,
xp_bar_progress,
health_bar_color,
mana_bar_color,
// Level Display // Level Display
level_text, level_text,
next_level_text, next_level_text,
@ -108,6 +111,7 @@ widget_ids! {
spellbook_title, spellbook_title,
//4 Charwindow //4 Charwindow
charwindow_frame, charwindow_frame,
charwindow,
charwindow_bg, charwindow_bg,
charwindow_icon, charwindow_icon,
charwindow_close, charwindow_close,
@ -118,9 +122,12 @@ widget_ids! {
charwindow_tab1_level, charwindow_tab1_level,
charwindow_tab1_exp, charwindow_tab1_exp,
charwindow_tab1_stats, charwindow_tab1_stats,
charwindow_tab1_statnames,
charwindow_tab1_stats_numbers, charwindow_tab1_stats_numbers,
charwindow_tab1_expbar, charwindow_tab1_expbar,
charwindow_tab1_expbar_progress, charwindow_rectangle,
charwindow_exp_rectangle,
charwindow_exp_progress_rectangle,
//5 Quest-Log //5 Quest-Log
questlog_frame, questlog_frame,
questlog_bg, questlog_bg,
@ -199,6 +206,7 @@ pub(self) struct Imgs {
button_blank: ImgId, button_blank: ImgId,
button_blue_mo: ImgId, button_blue_mo: ImgId,
button_blue_press: ImgId, button_blue_press: ImgId,
window_bg: ImgId,
// Social-Window // Social-Window
social_bg: ImgId, social_bg: ImgId,
social_icon: ImgId, social_icon: ImgId,
@ -210,12 +218,12 @@ pub(self) struct Imgs {
spellbook_bg: ImgId, spellbook_bg: ImgId,
spellbook_icon: ImgId, spellbook_icon: ImgId,
// Char Window // Char Window
charwindow_bg: ImgId, charwindow: ImgId,
charwindow_icon: ImgId, charwindow_icon: ImgId,
charwindow_tab_bg: ImgId, charwindow_tab_bg: ImgId,
charwindow_tab: ImgId, charwindow_tab: ImgId,
charwindow_expbar: ImgId, charwindow_expbar: ImgId,
progress_bar: ImgId, progress_frame: ImgId,
progress: ImgId, progress: ImgId,
// Quest-Log Window // Quest-Log Window
@ -312,6 +320,9 @@ impl Imgs {
button_blue_mo: load("element/buttons/blue_mo.png"), button_blue_mo: load("element/buttons/blue_mo.png"),
button_blue_press: load("element/buttons/blue_press.png"), button_blue_press: load("element/buttons/blue_press.png"),
// Window BG
window_bg: load("element/misc_backgrounds/window_bg.png"),
//Social Window //Social Window
social_bg: load("element/misc_backgrounds/small_bg.png"), social_bg: load("element/misc_backgrounds/small_bg.png"),
social_icon: load("element/icons/social.png"), social_icon: load("element/icons/social.png"),
@ -325,17 +336,15 @@ impl Imgs {
spellbook_bg: load("element/misc_backgrounds/small_bg.png"), spellbook_bg: load("element/misc_backgrounds/small_bg.png"),
spellbook_icon: load("element/icons/spellbook.png"), spellbook_icon: load("element/icons/spellbook.png"),
//Char Window //Char Window
charwindow_bg: load("element/misc_backgrounds/char_bg.png"), charwindow: load("element/misc_backgrounds/charwindow.png"),
charwindow_icon: load("element/icons/charwindow.png"), charwindow_icon: load("element/icons/charwindow.png"),
charwindow_tab_bg: load("element/frames/tab.png"), charwindow_tab_bg: load("element/frames/tab.png"),
charwindow_tab: load("element/buttons/tab.png"), charwindow_tab: load("element/buttons/tab.png"),
charwindow_expbar: load("element/misc_backgrounds/small_bg.png"), charwindow_expbar: load("element/misc_backgrounds/small_bg.png"),
progress_bar: load("element/frames/progress_bar.png"), progress_frame: load("element/frames/progress_bar.png"),
progress: load("element/misc_backgrounds/progress.png"), progress: load("element/misc_backgrounds/progress.png"),
//Quest-Log Window //Quest-Log Window
questlog_bg: load("element/misc_backgrounds/small_bg.png"), questlog_bg: load("element/misc_backgrounds/small_bg.png"),
questlog_icon: load("element/icons/questlog.png"), questlog_icon: load("element/icons/questlog.png"),
@ -400,6 +409,9 @@ pub struct Hud {
map_open: bool, map_open: bool,
show_ui: bool, show_ui: bool,
inventory_space: u32, inventory_space: u32,
xp_percentage: f64,
hp_percentage: f64,
mana_percentage: f64,
inventorytest_button: bool, inventorytest_button: bool,
settings_tab: SettingsTab, settings_tab: SettingsTab,
} }
@ -448,6 +460,9 @@ impl Hud {
open_windows: Windows::None, open_windows: Windows::None,
font_metamorph, font_metamorph,
font_opensans, font_opensans,
xp_percentage: 0.8,
hp_percentage: 0.8,
mana_percentage: 0.8,
} }
} }
@ -538,7 +553,7 @@ impl Hud {
.align_bottom_of(self.ids.mmap_frame) .align_bottom_of(self.ids.mmap_frame)
.set(self.ids.mmap_icons, ui_widgets); .set(self.ids.mmap_icons, ui_widgets);
// Title // Title
// TODO Make it display the actual Location // Make it display the actual location
Text::new("Uncanny Valley") Text::new("Uncanny Valley")
.mid_top_with_margin_on(self.ids.mmap_frame, 5.0) .mid_top_with_margin_on(self.ids.mmap_frame, 5.0)
.font_size(14) .font_size(14)
@ -599,7 +614,7 @@ impl Hud {
Some(Small::Social) => Windows::CharacterAnd(None), Some(Small::Social) => Windows::CharacterAnd(None),
_ => Windows::CharacterAnd(Some(Small::Social)), _ => Windows::CharacterAnd(Some(Small::Social)),
}, },
Windows::Settings => unreachable!(), Windows::Settings => Windows::Settings,
}; };
} }
@ -619,7 +634,7 @@ impl Hud {
Some(Small::Spellbook) => Windows::CharacterAnd(None), Some(Small::Spellbook) => Windows::CharacterAnd(None),
_ => Windows::CharacterAnd(Some(Small::Spellbook)), _ => Windows::CharacterAnd(Some(Small::Spellbook)),
}, },
Windows::Settings => unreachable!(), Windows::Settings => Windows::Settings,
}; };
} }
//4 Char-Window //4 Char-Window
@ -638,7 +653,7 @@ impl Hud {
}, },
Windows::Small(small) => Windows::CharacterAnd(Some(small)), Windows::Small(small) => Windows::CharacterAnd(Some(small)),
Windows::None => Windows::CharacterAnd(None), Windows::None => Windows::CharacterAnd(None),
Windows::Settings => unreachable!(), Windows::Settings => Windows::Settings,
} }
} }
//5 Quest-Log //5 Quest-Log
@ -657,7 +672,7 @@ impl Hud {
Some(Small::Questlog) => Windows::CharacterAnd(None), Some(Small::Questlog) => Windows::CharacterAnd(None),
_ => Windows::CharacterAnd(Some(Small::Questlog)), _ => Windows::CharacterAnd(Some(Small::Questlog)),
}, },
Windows::Settings => unreachable!(), Windows::Settings => Windows::Settings,
}; };
} }
} }
@ -670,6 +685,10 @@ impl Hud {
.mid_bottom_of(ui_widgets.window) .mid_bottom_of(ui_widgets.window)
.set(self.ids.xp_bar, ui_widgets); .set(self.ids.xp_bar, ui_widgets);
Rectangle::fill_with([406.0 * (self.xp_percentage), 5.0], color::rgb(0.31, 0.14, 0.4)) // "W=406*[Exp. %]"
.top_left_with_margins_on(self.ids.xp_bar, 5.0, 21.0)
.set(self.ids.xp_bar_progress, ui_widgets);
// Left Grid // Left Grid
Image::new(self.imgs.sb_grid) Image::new(self.imgs.sb_grid)
.w_h(2240.0 / 12.0, 448.0 / 12.0) .w_h(2240.0 / 12.0, 448.0 / 12.0)
@ -707,19 +726,32 @@ impl Hud {
.align_bottom_of(self.ids.sb_grid_bg_r) .align_bottom_of(self.ids.sb_grid_bg_r)
.set(self.ids.r_click, ui_widgets); .set(self.ids.r_click, ui_widgets);
// Health and mana bars // Health Bar
Image::new(self.imgs.health_bar) Image::new(self.imgs.health_bar)
.w_h(1120.0 / 6.0, 96.0 / 6.0) .w_h(1120.0 / 6.0, 96.0 / 6.0)
.left_from(self.ids.l_click, 0.0) .left_from(self.ids.l_click, 0.0)
.align_top_of(self.ids.l_click) .align_top_of(self.ids.l_click)
.set(self.ids.health_bar, ui_widgets); .set(self.ids.health_bar, ui_widgets);
// Filling
Rectangle::fill_with([182.0 * (self.hp_percentage), 6.0], color::rgb(0.09, 0.36, 0.0)) // "W=182.0 * [Health. %]"
.top_right_with_margins_on(self.ids.health_bar, 5.0, 0.0)
.set(self.ids.health_bar_color, ui_widgets);
// Mana Bar
Image::new(self.imgs.mana_bar) Image::new(self.imgs.mana_bar)
.w_h(1120.0 / 6.0, 96.0 / 6.0) .w_h(1120.0 / 6.0, 96.0 / 6.0)
.right_from(self.ids.r_click, 0.0) .right_from(self.ids.r_click, 0.0)
.align_top_of(self.ids.r_click) .align_top_of(self.ids.r_click)
.set(self.ids.mana_bar, ui_widgets); .set(self.ids.mana_bar, ui_widgets);
// Filling
Rectangle::fill_with([182.0 * (self.hp_percentage), 6.0], color::rgb(0.15, 0.14, 0.39)) // "W=182.0 * [Mana. %]"
.top_left_with_margins_on(self.ids.mana_bar, 5.0, 0.0)
.set(self.ids.mana_bar_color, ui_widgets);
// Buffs/Debuffs // Buffs/Debuffs
// Buffs // Buffs
@ -730,14 +762,14 @@ impl Hud {
// Insert actual Level here // Insert actual Level here
Text::new("1") Text::new("1")
.left_from(self.ids.xp_bar, -20.0) .left_from(self.ids.xp_bar, -15.0)
.font_size(14) .font_size(14)
.rgba(220.0, 220.0, 220.0, 0.8) .rgba(220.0, 220.0, 220.0, 0.8)
.set(self.ids.level_text, ui_widgets); .set(self.ids.level_text, ui_widgets);
// Insert next Level here // Insert next Level here
Text::new("2") Text::new("2")
.right_from(self.ids.xp_bar, -20.0) .right_from(self.ids.xp_bar, -15.0)
.font_size(14) .font_size(14)
.rgba(220.0, 220.0, 220.0, 0.8) .rgba(220.0, 220.0, 220.0, 0.8)
.set(self.ids.next_level_text, ui_widgets); .set(self.ids.next_level_text, ui_widgets);
@ -1038,7 +1070,7 @@ impl Hud {
self.open_windows = match self.open_windows { self.open_windows = match self.open_windows {
Windows::Small(_) => Windows::None, Windows::Small(_) => Windows::None,
Windows::CharacterAnd(_) => Windows::CharacterAnd(None), Windows::CharacterAnd(_) => Windows::CharacterAnd(None),
_ => unreachable!(), _ => Windows::Settings,
} }
} }
// Title // Title
@ -1085,7 +1117,7 @@ impl Hud {
self.open_windows = match self.open_windows { self.open_windows = match self.open_windows {
Windows::Small(_) => Windows::None, Windows::Small(_) => Windows::None,
Windows::CharacterAnd(_) => Windows::CharacterAnd(None), Windows::CharacterAnd(_) => Windows::CharacterAnd(None),
_ => unreachable!(), _ => Windows::Settings,
} }
} }
// Title // Title
@ -1132,7 +1164,7 @@ impl Hud {
self.open_windows = match self.open_windows { self.open_windows = match self.open_windows {
Windows::Small(_) => Windows::None, Windows::Small(_) => Windows::None,
Windows::CharacterAnd(_) => Windows::CharacterAnd(None), Windows::CharacterAnd(_) => Windows::CharacterAnd(None),
_ => unreachable!(), _ => Windows::Settings,
} }
} }
// Title // Title
@ -1153,11 +1185,16 @@ impl Hud {
.set(self.ids.charwindow_frame, ui_widgets); .set(self.ids.charwindow_frame, ui_widgets);
// BG // BG
Image::new(self.imgs.charwindow_bg) Image::new(self.imgs.window_bg)
.w_h(348.0, 404.0) .w_h(348.0, 404.0)
.mid_top_with_margin_on(self.ids.charwindow_frame, 48.0) .mid_top_with_margin_on(self.ids.charwindow_frame, 48.0)
.set(self.ids.charwindow_bg, ui_widgets); .set(self.ids.charwindow_bg, ui_widgets);
// Overlay
Image::new(self.imgs.charwindow)
.middle_of(self.ids.charwindow_bg)
.set(self.ids.charwindow, ui_widgets);
//Icon //Icon
//Image::new(self.imgs.charwindow_icon) //Image::new(self.imgs.charwindow_icon)
//.w_h(224.0 / 3.0, 224.0 / 3.0) //.w_h(224.0 / 3.0, 224.0 / 3.0)
@ -1178,6 +1215,7 @@ impl Hud {
None => Windows::None, None => Windows::None,
} }
} }
// Title // Title
Text::new("Character Name") //Add in actual Character Name Text::new("Character Name") //Add in actual Character Name
.mid_top_with_margin_on(self.ids.charwindow_frame, 7.0) .mid_top_with_margin_on(self.ids.charwindow_frame, 7.0)
@ -1188,6 +1226,10 @@ impl Hud {
.w_h(205.0, 412.0) .w_h(205.0, 412.0)
.mid_left_with_margin_on(self.ids.charwindow_frame, -205.0) .mid_left_with_margin_on(self.ids.charwindow_frame, -205.0)
.set(self.ids.charwindow_tab_bg, ui_widgets); .set(self.ids.charwindow_tab_bg, ui_widgets);
// Tab Rectangle
Rectangle::fill_with([192.0, 371.0], color::rgba(0.0, 0.0, 0.0, 0.8))
.top_right_with_margins_on(self.ids.charwindow_tab_bg, 20.0, 0.0)
.set(self.ids.charwindow_rectangle, ui_widgets);
// Tab Button // Tab Button
Button::image(self.imgs.charwindow_tab) Button::image(self.imgs.charwindow_tab)
.w_h(65.0, 23.0) .w_h(65.0, 23.0)
@ -1197,25 +1239,63 @@ impl Hud {
.label_font_id(self.font_opensans) .label_font_id(self.font_opensans)
.label_font_size(14) .label_font_size(14)
.set(self.ids.charwindow_tab1, ui_widgets); .set(self.ids.charwindow_tab1, ui_widgets);
Text::new("1") //Add in actual Character Level later Text::new("1") //Add in actual Character Level
.mid_top_with_margin_on(self.ids.charwindow_tab_bg, 14.0) .mid_top_with_margin_on(self.ids.charwindow_rectangle, 10.0)
.font_id(self.font_opensans) .font_id(self.font_opensans)
.font_size(30) .font_size(30)
.rgba(220.0, 220.0, 220.0, 0.8) .rgba(220.0, 220.0, 220.0, 0.8)
.set(self.ids.charwindow_tab1_level, ui_widgets); .set(self.ids.charwindow_tab1_level, ui_widgets);
// Stats // Exp-Bar Background
Text::new("Stat 1") Rectangle::fill_with([170.0, 10.0], color::BLACK)
.top_left_with_margins_on(self.ids.charwindow_tab_bg, 40.0, 20.0) .mid_top_with_margin_on(self.ids.charwindow_rectangle, 50.0)
.set(self.ids.charwindow_exp_rectangle, ui_widgets);
// Exp-Bar Progress
Rectangle::fill_with([170.0 * (self.xp_percentage), 6.0], color::rgb(0.31, 0.14, 0.40)) // 0.8 = Experience percantage
.mid_left_with_margin_on(self.ids.charwindow_tab1_expbar, 1.0)
.set(self.ids.charwindow_exp_progress_rectangle, ui_widgets);
// Exp-Bar Foreground Frame
Image::new(self.imgs.progress_frame)
.w_h(170.0, 10.0)
.middle_of(self.ids.charwindow_exp_rectangle)
.set(self.ids.charwindow_tab1_expbar, ui_widgets);
// Exp-Text
Text::new("120/170") // Shows the Exp / Exp to reach the next level
.mid_top_with_margin_on(self.ids.charwindow_tab1_expbar, 10.0)
.font_id(self.font_opensans) .font_id(self.font_opensans)
.font_size(20) .font_size(15)
.rgba(220.0, 220.0, 220.0, 0.8)
.set(self.ids.charwindow_tab1_exp, ui_widgets);
// Stats
Text::new(
"Stamina\n\
\n\
Strength\n\
\n\
Dexterity\n\
\n\
Intelligence",
)
.top_left_with_margins_on(self.ids.charwindow_rectangle, 100.0, 20.0)
.font_id(self.font_opensans)
.font_size(16)
.rgba(220.0, 220.0, 220.0, 0.8)
.set(self.ids.charwindow_tab1_statnames, ui_widgets);
Text::new(
"1234\n\
\n\
12312\n\
\n\
12414\n\
\n\
124124",
)
.right_from(self.ids.charwindow_tab1_statnames, 10.0)
.font_id(self.font_opensans)
.font_size(16)
.rgba(220.0, 220.0, 220.0, 0.8) .rgba(220.0, 220.0, 220.0, 0.8)
.set(self.ids.charwindow_tab1_stats, ui_widgets); .set(self.ids.charwindow_tab1_stats, ui_widgets);
} }
//2 Map //2 Map
@ -1372,7 +1452,7 @@ impl Hud {
Some(Small::Questlog) => Windows::CharacterAnd(None), Some(Small::Questlog) => Windows::CharacterAnd(None),
_ => Windows::CharacterAnd(Some(Small::Questlog)), _ => Windows::CharacterAnd(Some(Small::Questlog)),
}, },
Windows::Settings => unreachable!(), Windows::Settings => Windows::Settings,
}; };
} }
pub fn toggle_map(&mut self) { pub fn toggle_map(&mut self) {
@ -1387,7 +1467,7 @@ impl Hud {
}, },
Windows::Small(small) => Windows::CharacterAnd(Some(small)), Windows::Small(small) => Windows::CharacterAnd(Some(small)),
Windows::None => Windows::CharacterAnd(None), Windows::None => Windows::CharacterAnd(None),
Windows::Settings => unreachable!(), Windows::Settings => Windows::Settings,
} }
} }
pub fn toggle_social(&mut self) { pub fn toggle_social(&mut self) {
@ -1398,7 +1478,7 @@ impl Hud {
Some(Small::Social) => Windows::CharacterAnd(None), Some(Small::Social) => Windows::CharacterAnd(None),
_ => Windows::CharacterAnd(Some(Small::Social)), _ => Windows::CharacterAnd(Some(Small::Social)),
}, },
Windows::Settings => unreachable!(), Windows::Settings => Windows::Settings,
}; };
} }
pub fn toggle_spellbook(&mut self) { pub fn toggle_spellbook(&mut self) {
@ -1409,7 +1489,7 @@ impl Hud {
Some(Small::Spellbook) => Windows::CharacterAnd(None), Some(Small::Spellbook) => Windows::CharacterAnd(None),
_ => Windows::CharacterAnd(Some(Small::Spellbook)), _ => Windows::CharacterAnd(Some(Small::Spellbook)),
}, },
Windows::Settings => unreachable!(), Windows::Settings => Windows::Settings,
}; };
} }
pub fn toggle_settings(&mut self) { pub fn toggle_settings(&mut self) {

View File

@ -461,7 +461,6 @@ impl CharSelectionUi {
.was_clicked() .was_clicked()
{ {
self.selected_char_no = Some(1); self.selected_char_no = Some(1);
self.creation_state = CreationState::Race;
} }
// Veloren Logo and Alpha Version // Veloren Logo and Alpha Version