Make character model more variable

Former-commit-id: 331baad40c79d846290c4617ea8e359b2786c7ef
This commit is contained in:
timokoesters 2019-04-19 09:35:23 +02:00
parent 836e6245ee
commit 802c484a38
10 changed files with 360 additions and 208 deletions

View File

@ -18,7 +18,7 @@ fn main() {
let mut clock = Clock::new(); let mut clock = Clock::new();
// Create client // Create client
let mut client = Client::new(([127, 0, 0, 1], 59003), comp::Player::new("test".to_string()), None, 300) let mut client = Client::new(([127, 0, 0, 1], 59003), comp::Player::new("test".to_string()), 300)
.expect("Failed to create client instance"); .expect("Failed to create client instance");
client.send_chat("Hello!".to_string()); client.send_chat("Hello!".to_string());

View File

@ -52,7 +52,6 @@ impl Client {
pub fn new<A: Into<SocketAddr>>( pub fn new<A: Into<SocketAddr>>(
addr: A, addr: A,
player: comp::Player, player: comp::Player,
character: Option<comp::Character>,
view_distance: u64, view_distance: u64,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
@ -61,7 +60,6 @@ impl Client {
// Send connection request // Send connection request
postbox.send_message(ClientMsg::Connect { postbox.send_message(ClientMsg::Connect {
player, player,
character,
}); });
// Wait for handshake from server // Wait for handshake from server

View File

@ -1,7 +1,8 @@
use specs::{Component, VecStorage, FlaggedStorage}; use specs::{Component, VecStorage, FlaggedStorage};
use vek::*; use vek::*;
use rand::prelude::*;
#[derive(Copy, Clone, Debug, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Race { pub enum Race {
Danari, Danari,
Dwarf, Dwarf,
@ -11,13 +12,103 @@ pub enum Race {
Undead, Undead,
} }
#[derive(Copy, Clone, Debug, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Gender { pub enum Gender {
Female, Female,
Male, Male,
Unspecified, Unspecified,
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Head {
DefaultHead,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Chest {
DefaultChest,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Belt {
DefaultBelt,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Pants {
DefaultPants,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Hand {
DefaultHand,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Foot {
DefaultFoot,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Weapon {
Daggers,
SwordShield,
Sword,
Axe,
Hammer,
Bow,
Staff,
}
use Race::*;
use Gender::*;
use Head::*;
use Chest::*;
use Belt::*;
use Pants::*;
use Hand::*;
use Foot::*;
use Weapon::*;
const ALL_RACES: [Race; 6] = [Danari, Dwarf, Elf, Human, Orc, Undead];
const ALL_GENDERS: [Gender; 3] = [Female, Male, Unspecified];
const ALL_HEADS: [Head; 1] = [DefaultHead];
const ALL_CHESTS: [Chest; 1] = [DefaultChest];
const ALL_BELTS: [Belt; 1] = [DefaultBelt];
const ALL_PANTS: [Pants; 1] = [DefaultPants];
const ALL_HANDS: [Hand; 1] = [DefaultHand];
const ALL_FEET: [Foot; 1] = [DefaultFoot];
const ALL_WEAPONS: [Weapon; 7] = [Daggers, SwordShield, Sword, Axe, Hammer, Bow, Staff];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Character {
pub race: Race,
pub gender: Gender,
pub head: Head,
pub chest: Chest,
pub belt: Belt,
pub pants: Pants,
pub hand: Hand,
pub foot: Foot,
pub weapon: Weapon,
}
impl Character {
pub fn random() -> Self {
Self {
race: *thread_rng().choose(&ALL_RACES).unwrap(),
gender: *thread_rng().choose(&ALL_GENDERS).unwrap(),
head: *thread_rng().choose(&ALL_HEADS).unwrap(),
chest: *thread_rng().choose(&ALL_CHESTS).unwrap(),
belt: *thread_rng().choose(&ALL_BELTS).unwrap(),
pants: *thread_rng().choose(&ALL_PANTS).unwrap(),
hand: *thread_rng().choose(&ALL_HANDS).unwrap(),
foot: *thread_rng().choose(&ALL_FEET).unwrap(),
weapon: *thread_rng().choose(&ALL_WEAPONS).unwrap(),
}
}
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct AnimationHistory { pub struct AnimationHistory {
pub last: Option<Animation>, pub last: Option<Animation>,
@ -30,33 +121,6 @@ pub enum Animation {
Run, Run,
} }
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct Character {
race: Race,
gender: Gender,
head: (),
chest: (),
belt: (),
arms: (),
feet: (),
}
impl Character {
// TODO: Remove this
pub fn test() -> Self {
Self {
race: Race::Human,
gender: Gender::Unspecified,
head: (),
chest: (),
belt: (),
arms: (),
feet: (),
}
}
}
impl Component for Character { impl Component for Character {
type Storage = FlaggedStorage<Self, VecStorage<Self>>; type Storage = FlaggedStorage<Self, VecStorage<Self>>;
} }

View File

@ -5,9 +5,11 @@ use crate::comp;
pub enum ClientMsg { pub enum ClientMsg {
Connect { Connect {
player: comp::Player, player: comp::Player,
character: Option<comp::Character>,
}, },
Character {
character: comp::Character,
},
Ping, Ping,
Pong, Pong,
Chat(String), Chat(String),

View File

@ -72,7 +72,7 @@ impl Server {
}; };
for i in 0..4 { for i in 0..4 {
this.create_character(comp::Character::test()) this.create_npc(comp::Character::random())
.with(comp::Agent::Wanderer(Vec2::zero())) .with(comp::Agent::Wanderer(Vec2::zero()))
.with(comp::Control::default()) .with(comp::Control::default())
.build(); .build();
@ -105,7 +105,7 @@ impl Server {
/// Build a non-player character /// Build a non-player character
#[allow(dead_code)] #[allow(dead_code)]
pub fn create_character(&mut self, character: comp::Character) -> EcsEntityBuilder { pub fn create_npc(&mut self, character: comp::Character) -> EcsEntityBuilder {
self.state self.state
.ecs_mut() .ecs_mut()
.create_entity_synced() .create_entity_synced()
@ -115,6 +115,21 @@ impl Server {
.with(character) .with(character)
} }
pub fn create_player_character(state: &mut State, entity: EcsEntity, character: comp::Character) {
state.write_component(entity, character);
state.write_component(entity, comp::phys::Pos(Vec3::zero()));
state.write_component(entity, comp::phys::Vel(Vec3::zero()));
state.write_component(entity, comp::phys::Dir(Vec3::unit_y()));
// Make sure everything is accepted
state.write_component(entity, comp::phys::ForceUpdate);
// Set initial animation
state.write_component(entity, comp::AnimationHistory {
last: None,
current: Animation::Idle
});
}
/// Execute a single server tick, handle input and update the game state by the given duration /// Execute a single server tick, handle input and update the game state by the given duration
#[allow(dead_code)] #[allow(dead_code)]
pub fn tick(&mut self, input: Input, dt: Duration) -> Result<Vec<Event>, Error> { pub fn tick(&mut self, input: Input, dt: Duration) -> Result<Vec<Event>, Error> {
@ -234,14 +249,15 @@ impl Server {
for msg in new_msgs { for msg in new_msgs {
match client.state { match client.state {
ClientState::Connecting => match msg { ClientState::Connecting => match msg {
ClientMsg::Connect { player, character } => { ClientMsg::Connect { player } => {
Self::initialize_client(state, entity, client, player, character); Self::initialize_client(state, entity, client, player);
} }
_ => disconnect = true, _ => disconnect = true,
}, },
ClientState::Connected => match msg { ClientState::Connected => match msg {
ClientMsg::Connect { .. } => disconnect = true, // Not allowed when already connected ClientMsg::Connect { .. } => disconnect = true, // Not allowed when already connected
ClientMsg::Disconnect => disconnect = true, ClientMsg::Disconnect => disconnect = true,
ClientMsg::Character { character } => Self::create_player_character(state, entity, character),
ClientMsg::Ping => client.postbox.send_message(ServerMsg::Pong), ClientMsg::Ping => client.postbox.send_message(ServerMsg::Pong),
ClientMsg::Pong => {} ClientMsg::Pong => {}
ClientMsg::Chat(msg) => new_chat_msgs.push((entity, msg)), ClientMsg::Chat(msg) => new_chat_msgs.push((entity, msg)),
@ -325,30 +341,10 @@ impl Server {
entity: specs::Entity, entity: specs::Entity,
client: &mut Client, client: &mut Client,
player: comp::Player, player: comp::Player,
character: Option<comp::Character>,
) { ) {
// Save player metadata (for example the username) // Save player metadata (for example the username)
state.write_component(entity, player); state.write_component(entity, player);
// Give the player it's character if he wants one
// (Chat only clients don't need one for example)
if let Some(character) = character {
state.write_component(entity, character);
// Every character has to have these components
state.write_component(entity, comp::phys::Pos(Vec3::zero()));
state.write_component(entity, comp::phys::Vel(Vec3::zero()));
state.write_component(entity, comp::phys::Dir(Vec3::unit_y()));
// Make sure everything is accepted
state.write_component(entity, comp::phys::ForceUpdate);
// Set initial animation
state.write_component(entity, comp::AnimationHistory {
last: None,
current: Animation::Idle
});
}
client.state = ClientState::Connected; client.state = ClientState::Connected;
// Return a handshake with the state of the current world // Return a handshake with the state of the current world

View File

@ -3,7 +3,21 @@ use crate::{
ui::{self, ScaleMode, Ui}, ui::{self, ScaleMode, Ui},
window::Window, window::Window,
}; };
use common::assets; use common::{
assets,
comp::character::{
self,
Race,
Gender,
Head,
Chest,
Belt,
Pants,
Hand,
Foot,
Weapon,
}
};
use conrod_core::{ use conrod_core::{
color, color,
color::TRANSPARENT, color::TRANSPARENT,
@ -65,8 +79,8 @@ widget_ids! {
race_4, race_4,
race_5, race_5,
race_6, race_6,
sex_1, gender_1,
sex_2, gender_2,
weapon_1, weapon_1,
weapon_2, weapon_2,
weapon_3, weapon_3,
@ -315,34 +329,13 @@ enum CreationState {
Weapon, Weapon,
Body(BodyPart), Body(BodyPart),
} }
enum Races {
Human,
Orc,
Elf,
Dwarf,
Undead,
Danari,
}
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
enum BodyPart { enum BodyPart {
SkinEyes, SkinEyes,
Hair, Hair,
Accessories, Accessories,
} }
enum Sex {
Male,
Female,
Undefined,
}
enum Weapons {
Daggers,
SwordShield,
Sword,
Axe,
Hammer,
Bow,
Staff,
}
pub enum Event { pub enum Event {
Logout, Logout,
@ -360,9 +353,15 @@ pub struct CharSelectionUi {
font_opensans: FontId, font_opensans: FontId,
character_creation: bool, character_creation: bool,
selected_char_no: Option<i32>, selected_char_no: Option<i32>,
race: Races, race: Race,
sex: Sex, gender: Gender,
weapon: Weapons, head: Head,
chest: Chest,
belt: Belt,
pants: Pants,
hand: Hand,
foot: Foot,
weapon: Weapon,
creation_state: CreationState, creation_state: CreationState,
character_name: String, character_name: String,
} }
@ -386,7 +385,8 @@ impl CharSelectionUi {
}; };
let font_opensans = load_font("/OpenSans-Regular.ttf", &mut ui); let font_opensans = load_font("/OpenSans-Regular.ttf", &mut ui);
let font_metamorph = load_font("/Metamorphous-Regular.ttf", &mut ui); let font_metamorph = load_font("/Metamorphous-Regular.ttf", &mut ui);
// TODO: Randomize initial values
Self { Self {
ui, ui,
imgs, imgs,
@ -396,9 +396,15 @@ impl CharSelectionUi {
character_creation: false, character_creation: false,
selected_char_no: None, selected_char_no: None,
character_name: "Character Name".to_string(), character_name: "Character Name".to_string(),
race: Races::Human, race: Race::Human,
sex: Sex::Male, gender: Gender::Male,
weapon: Weapons::Sword, head: Head::DefaultHead,
chest: Chest::DefaultChest,
belt: Belt::DefaultBelt,
pants: Pants::DefaultPants,
hand: Hand::DefaultHand,
foot: Foot::DefaultFoot,
weapon: Weapon::Sword,
creation_state: CreationState::Race, creation_state: CreationState::Race,
} }
} }
@ -581,6 +587,7 @@ impl CharSelectionUi {
.set(self.ids.create_button, ui_widgets) .set(self.ids.create_button, ui_widgets)
.was_clicked() .was_clicked()
{ {
// TODO: Save character
self.character_creation = false; self.character_creation = false;
} }
// Character Name Input // Character Name Input
@ -704,7 +711,7 @@ impl CharSelectionUi {
.w_h(68.0, 68.0) .w_h(68.0, 68.0)
.mid_left_of(self.ids.gender_bg) .mid_left_of(self.ids.gender_bg)
.set(self.ids.male, ui_widgets); .set(self.ids.male, ui_widgets);
if Button::image(if let Sex::Male = self.sex { if Button::image(if let Gender::Male = self.gender {
self.imgs.icon_border_pressed self.imgs.icon_border_pressed
} else { } else {
self.imgs.icon_border self.imgs.icon_border
@ -712,17 +719,17 @@ impl CharSelectionUi {
.middle_of(self.ids.male) .middle_of(self.ids.male)
.hover_image(self.imgs.icon_border_mo) .hover_image(self.imgs.icon_border_mo)
.press_image(self.imgs.icon_border_press) .press_image(self.imgs.icon_border_press)
.set(self.ids.sex_1, ui_widgets) .set(self.ids.gender_1, ui_widgets)
.was_clicked() .was_clicked()
{ {
self.sex = Sex::Male; self.gender = Gender::Male;
} }
// Female // Female
Image::new(self.imgs.female) Image::new(self.imgs.female)
.w_h(68.0, 68.0) .w_h(68.0, 68.0)
.right_from(self.ids.male, 16.0) .right_from(self.ids.male, 16.0)
.set(self.ids.female, ui_widgets); .set(self.ids.female, ui_widgets);
if Button::image(if let Sex::Female = self.sex { if Button::image(if let Gender::Female = self.gender {
self.imgs.icon_border_pressed self.imgs.icon_border_pressed
} else { } else {
self.imgs.icon_border self.imgs.icon_border
@ -730,10 +737,10 @@ impl CharSelectionUi {
.middle_of(self.ids.female) .middle_of(self.ids.female)
.hover_image(self.imgs.icon_border_mo) .hover_image(self.imgs.icon_border_mo)
.press_image(self.imgs.icon_border_press) .press_image(self.imgs.icon_border_press)
.set(self.ids.sex_2, ui_widgets) .set(self.ids.gender_2, ui_widgets)
.was_clicked() .was_clicked()
{ {
self.sex = Sex::Female; self.gender = Gender::Female;
} }
// for alignment // for alignment
Rectangle::fill_with([458.0, 68.0], color::TRANSPARENT) Rectangle::fill_with([458.0, 68.0], color::TRANSPARENT)
@ -741,7 +748,7 @@ impl CharSelectionUi {
.set(self.ids.races_bg, ui_widgets); .set(self.ids.races_bg, ui_widgets);
// TODO: If races where in some sort of array format we could do this in a loop // TODO: If races where in some sort of array format we could do this in a loop
// Human // Human
Image::new(if let Sex::Male = self.sex { Image::new(if let Gender::Male = self.gender {
self.imgs.human_m self.imgs.human_m
} else { } else {
self.imgs.human_f self.imgs.human_f
@ -749,7 +756,7 @@ impl CharSelectionUi {
.w_h(68.0, 68.0) .w_h(68.0, 68.0)
.mid_left_of(self.ids.races_bg) .mid_left_of(self.ids.races_bg)
.set(self.ids.human, ui_widgets); .set(self.ids.human, ui_widgets);
if Button::image(if let Races::Human = self.race { if Button::image(if let Race::Human = self.race {
self.imgs.icon_border_pressed self.imgs.icon_border_pressed
} else { } else {
self.imgs.icon_border self.imgs.icon_border
@ -760,11 +767,11 @@ impl CharSelectionUi {
.set(self.ids.race_1, ui_widgets) .set(self.ids.race_1, ui_widgets)
.was_clicked() .was_clicked()
{ {
self.race = Races::Human; self.race = Race::Human;
} }
// Orc // Orc
Image::new(if let Sex::Male = self.sex { Image::new(if let Gender::Male = self.gender {
self.imgs.orc_m self.imgs.orc_m
} else { } else {
self.imgs.orc_f self.imgs.orc_f
@ -772,7 +779,7 @@ impl CharSelectionUi {
.w_h(68.0, 68.0) .w_h(68.0, 68.0)
.right_from(self.ids.human, 10.0) .right_from(self.ids.human, 10.0)
.set(self.ids.orc, ui_widgets); .set(self.ids.orc, ui_widgets);
if Button::image(if let Races::Orc = self.race { if Button::image(if let Race::Orc = self.race {
self.imgs.icon_border_pressed self.imgs.icon_border_pressed
} else { } else {
self.imgs.icon_border self.imgs.icon_border
@ -783,10 +790,10 @@ impl CharSelectionUi {
.set(self.ids.race_2, ui_widgets) .set(self.ids.race_2, ui_widgets)
.was_clicked() .was_clicked()
{ {
self.race = Races::Orc; self.race = Race::Orc;
} }
// Dwarf // Dwarf
Image::new(if let Sex::Male = self.sex { Image::new(if let Gender::Male = self.gender {
self.imgs.dwarf_m self.imgs.dwarf_m
} else { } else {
self.imgs.dwarf_f self.imgs.dwarf_f
@ -794,7 +801,7 @@ impl CharSelectionUi {
.w_h(68.0, 68.0) .w_h(68.0, 68.0)
.right_from(self.ids.human, 10.0 * 2.0 + 68.0) .right_from(self.ids.human, 10.0 * 2.0 + 68.0)
.set(self.ids.dwarf, ui_widgets); .set(self.ids.dwarf, ui_widgets);
if Button::image(if let Races::Dwarf = self.race { if Button::image(if let Race::Dwarf = self.race {
self.imgs.icon_border_pressed self.imgs.icon_border_pressed
} else { } else {
self.imgs.icon_border self.imgs.icon_border
@ -805,10 +812,10 @@ impl CharSelectionUi {
.set(self.ids.race_3, ui_widgets) .set(self.ids.race_3, ui_widgets)
.was_clicked() .was_clicked()
{ {
self.race = Races::Dwarf; self.race = Race::Dwarf;
} }
// Elf // Elf
Image::new(if let Sex::Male = self.sex { Image::new(if let Gender::Male = self.gender {
self.imgs.elf_m self.imgs.elf_m
} else { } else {
self.imgs.elf_f self.imgs.elf_f
@ -816,7 +823,7 @@ impl CharSelectionUi {
.w_h(68.0, 68.0) .w_h(68.0, 68.0)
.right_from(self.ids.human, 10.0 * 3.0 + 68.0 * 2.0) .right_from(self.ids.human, 10.0 * 3.0 + 68.0 * 2.0)
.set(self.ids.elf, ui_widgets); .set(self.ids.elf, ui_widgets);
if Button::image(if let Races::Elf = self.race { if Button::image(if let Race::Elf = self.race {
self.imgs.icon_border_pressed self.imgs.icon_border_pressed
} else { } else {
self.imgs.icon_border self.imgs.icon_border
@ -827,10 +834,10 @@ impl CharSelectionUi {
.set(self.ids.race_4, ui_widgets) .set(self.ids.race_4, ui_widgets)
.was_clicked() .was_clicked()
{ {
self.race = Races::Elf; self.race = Race::Elf;
} }
// Undead // Undead
Image::new(if let Sex::Male = self.sex { Image::new(if let Gender::Male = self.gender {
self.imgs.undead_m self.imgs.undead_m
} else { } else {
self.imgs.undead_f self.imgs.undead_f
@ -838,7 +845,7 @@ impl CharSelectionUi {
.w_h(68.0, 68.0) .w_h(68.0, 68.0)
.right_from(self.ids.human, 10.0 * 4.0 + 68.0 * 3.0) .right_from(self.ids.human, 10.0 * 4.0 + 68.0 * 3.0)
.set(self.ids.undead, ui_widgets); .set(self.ids.undead, ui_widgets);
if Button::image(if let Races::Undead = self.race { if Button::image(if let Race::Undead = self.race {
self.imgs.icon_border_pressed self.imgs.icon_border_pressed
} else { } else {
self.imgs.icon_border self.imgs.icon_border
@ -849,17 +856,17 @@ impl CharSelectionUi {
.set(self.ids.race_5, ui_widgets) .set(self.ids.race_5, ui_widgets)
.was_clicked() .was_clicked()
{ {
self.race = Races::Undead; self.race = Race::Undead;
} }
// Danari // Danari
Image::new(if let Sex::Male = self.sex { Image::new(if let Gender::Male = self.gender {
self.imgs.danari_m self.imgs.danari_m
} else { } else {
self.imgs.danari_f self.imgs.danari_f
}) })
.right_from(self.ids.human, 10.0 * 5.0 + 68.0 * 4.0) .right_from(self.ids.human, 10.0 * 5.0 + 68.0 * 4.0)
.set(self.ids.danari, ui_widgets); .set(self.ids.danari, ui_widgets);
if Button::image(if let Races::Danari = self.race { if Button::image(if let Race::Danari = self.race {
self.imgs.icon_border_pressed self.imgs.icon_border_pressed
} else { } else {
self.imgs.icon_border self.imgs.icon_border
@ -871,7 +878,7 @@ impl CharSelectionUi {
.set(self.ids.race_6, ui_widgets) .set(self.ids.race_6, ui_widgets)
.was_clicked() .was_clicked()
{ {
self.race = Races::Danari; self.race = Race::Danari;
} }
// Description Headline and Text // Description Headline and Text
@ -933,12 +940,12 @@ impl CharSelectionUi {
Outcast communities consisting of these Blessed Danari have formed all over the land."; Outcast communities consisting of these Blessed Danari have formed all over the land.";
let (race_str, race_desc) = match self.race { let (race_str, race_desc) = match self.race {
Races::Human => ("Humans", HUMAN_DESC), Race::Human => ("Humans", HUMAN_DESC),
Races::Orc => ("Orcs", ORC_DESC), Race::Orc => ("Orcs", ORC_DESC),
Races::Dwarf => ("Dwarves", DWARF_DESC), Race::Dwarf => ("Dwarves", DWARF_DESC),
Races::Undead => ("Undead", UNDEAD_DESC), Race::Undead => ("Undead", UNDEAD_DESC),
Races::Elf => ("Elves", ELF_DESC), Race::Elf => ("Elves", ELF_DESC),
Races::Danari => ("Danari", DANARI_DESC), Race::Danari => ("Danari", DANARI_DESC),
}; };
Text::new(race_str) Text::new(race_str)
.mid_top_with_margin_on(self.ids.creation_window, 370.0) .mid_top_with_margin_on(self.ids.creation_window, 370.0)
@ -972,7 +979,7 @@ impl CharSelectionUi {
.w_h(60.0, 60.0) .w_h(60.0, 60.0)
.mid_left_of(self.ids.weapon_bg) .mid_left_of(self.ids.weapon_bg)
.set(self.ids.sword_shield, ui_widgets); .set(self.ids.sword_shield, ui_widgets);
if Button::image(if let Weapons::SwordShield = self.weapon { if Button::image(if let Weapon::SwordShield = self.weapon {
self.imgs.icon_border_pressed self.imgs.icon_border_pressed
} else { } else {
self.imgs.icon_border self.imgs.icon_border
@ -983,7 +990,7 @@ impl CharSelectionUi {
.set(self.ids.weapon_1, ui_widgets) .set(self.ids.weapon_1, ui_widgets)
.was_clicked() .was_clicked()
{ {
self.weapon = Weapons::SwordShield; self.weapon = Weapon::SwordShield;
} }
// Daggers // Daggers
@ -991,7 +998,7 @@ impl CharSelectionUi {
.w_h(60.0, 60.0) .w_h(60.0, 60.0)
.right_from(self.ids.sword_shield, 8.0) .right_from(self.ids.sword_shield, 8.0)
.set(self.ids.daggers, ui_widgets); .set(self.ids.daggers, ui_widgets);
if Button::image(if let Weapons::Daggers = self.weapon { if Button::image(if let Weapon::Daggers = self.weapon {
self.imgs.icon_border_pressed self.imgs.icon_border_pressed
} else { } else {
self.imgs.icon_border self.imgs.icon_border
@ -1002,7 +1009,7 @@ impl CharSelectionUi {
.set(self.ids.weapon_2, ui_widgets) .set(self.ids.weapon_2, ui_widgets)
.was_clicked() .was_clicked()
{ {
self.weapon = Weapons::Daggers; self.weapon = Weapon::Daggers;
} }
// Sword // Sword
@ -1010,7 +1017,7 @@ impl CharSelectionUi {
.w_h(60.0, 60.0) .w_h(60.0, 60.0)
.right_from(self.ids.sword_shield, 8.0 * 2.0 + 60.0 * 1.0) .right_from(self.ids.sword_shield, 8.0 * 2.0 + 60.0 * 1.0)
.set(self.ids.sword, ui_widgets); .set(self.ids.sword, ui_widgets);
if Button::image(if let Weapons::Sword = self.weapon { if Button::image(if let Weapon::Sword = self.weapon {
self.imgs.icon_border_pressed self.imgs.icon_border_pressed
} else { } else {
self.imgs.icon_border self.imgs.icon_border
@ -1021,14 +1028,14 @@ impl CharSelectionUi {
.set(self.ids.weapon_3, ui_widgets) .set(self.ids.weapon_3, ui_widgets)
.was_clicked() .was_clicked()
{ {
self.weapon = Weapons::Sword; self.weapon = Weapon::Sword;
} }
// Axe // Axe
Image::new(self.imgs.axe) Image::new(self.imgs.axe)
.w_h(60.0, 60.0) .w_h(60.0, 60.0)
.right_from(self.ids.sword_shield, 8.0 * 3.0 + 60.0 * 2.0) .right_from(self.ids.sword_shield, 8.0 * 3.0 + 60.0 * 2.0)
.set(self.ids.axe, ui_widgets); .set(self.ids.axe, ui_widgets);
if Button::image(if let Weapons::Axe = self.weapon { if Button::image(if let Weapon::Axe = self.weapon {
self.imgs.icon_border_pressed self.imgs.icon_border_pressed
} else { } else {
self.imgs.icon_border self.imgs.icon_border
@ -1039,14 +1046,14 @@ impl CharSelectionUi {
.set(self.ids.weapon_4, ui_widgets) .set(self.ids.weapon_4, ui_widgets)
.was_clicked() .was_clicked()
{ {
self.weapon = Weapons::Axe; self.weapon = Weapon::Axe;
} }
// Hammer // Hammer
Image::new(self.imgs.hammer) Image::new(self.imgs.hammer)
.w_h(60.0, 60.0) .w_h(60.0, 60.0)
.right_from(self.ids.sword_shield, 8.0 * 4.0 + 60.0 * 3.0) .right_from(self.ids.sword_shield, 8.0 * 4.0 + 60.0 * 3.0)
.set(self.ids.hammer, ui_widgets); .set(self.ids.hammer, ui_widgets);
if Button::image(if let Weapons::Hammer = self.weapon { if Button::image(if let Weapon::Hammer = self.weapon {
self.imgs.icon_border_pressed self.imgs.icon_border_pressed
} else { } else {
self.imgs.icon_border self.imgs.icon_border
@ -1057,14 +1064,14 @@ impl CharSelectionUi {
.set(self.ids.weapon_5, ui_widgets) .set(self.ids.weapon_5, ui_widgets)
.was_clicked() .was_clicked()
{ {
self.weapon = Weapons::Hammer; self.weapon = Weapon::Hammer;
} }
// Bow // Bow
Image::new(self.imgs.bow) Image::new(self.imgs.bow)
.w_h(60.0, 60.0) .w_h(60.0, 60.0)
.right_from(self.ids.sword_shield, 8.0 * 5.0 + 60.0 * 4.0) .right_from(self.ids.sword_shield, 8.0 * 5.0 + 60.0 * 4.0)
.set(self.ids.bow, ui_widgets); .set(self.ids.bow, ui_widgets);
if Button::image(if let Weapons::Bow = self.weapon { if Button::image(if let Weapon::Bow = self.weapon {
self.imgs.icon_border_pressed self.imgs.icon_border_pressed
} else { } else {
self.imgs.icon_border self.imgs.icon_border
@ -1075,14 +1082,14 @@ impl CharSelectionUi {
.set(self.ids.weapon_6, ui_widgets) .set(self.ids.weapon_6, ui_widgets)
.was_clicked() .was_clicked()
{ {
self.weapon = Weapons::Bow; self.weapon = Weapon::Bow;
} }
// Staff // Staff
Image::new(self.imgs.staff) Image::new(self.imgs.staff)
.w_h(60.0, 60.0) .w_h(60.0, 60.0)
.right_from(self.ids.sword_shield, 8.0 * 6.0 + 60.0 * 5.0) .right_from(self.ids.sword_shield, 8.0 * 6.0 + 60.0 * 5.0)
.set(self.ids.staff, ui_widgets); .set(self.ids.staff, ui_widgets);
if Button::image(if let Weapons::Staff = self.weapon { if Button::image(if let Weapon::Staff = self.weapon {
self.imgs.icon_border_pressed self.imgs.icon_border_pressed
} else { } else {
self.imgs.icon_border self.imgs.icon_border
@ -1093,7 +1100,7 @@ impl CharSelectionUi {
.set(self.ids.weapon_7, ui_widgets) .set(self.ids.weapon_7, ui_widgets)
.was_clicked() .was_clicked()
{ {
self.weapon = Weapons::Staff; self.weapon = Weapon::Staff;
} }
// TODO: Load these from files (or from the server???) // TODO: Load these from files (or from the server???)
@ -1106,13 +1113,13 @@ impl CharSelectionUi {
const STAFF_DESC: &str = " MISSING "; const STAFF_DESC: &str = " MISSING ";
let (weapon_str, weapon_desc) = match self.weapon { let (weapon_str, weapon_desc) = match self.weapon {
Weapons::SwordShield => ("Sword and Shield", SWORDSHIELD_DESC), Weapon::SwordShield => ("Sword and Shield", SWORDSHIELD_DESC),
Weapons::Daggers => ("Daggers", DAGGERS_DESC), Weapon::Daggers => ("Daggers", DAGGERS_DESC),
Weapons::Sword => ("Sword", SWORD_DESC), Weapon::Sword => ("Sword", SWORD_DESC),
Weapons::Axe => ("Axe", AXE_DESC), Weapon::Axe => ("Axe", AXE_DESC),
Weapons::Hammer => ("Hammer", HAMMER_DESC), Weapon::Hammer => ("Hammer", HAMMER_DESC),
Weapons::Bow => ("Bow", BOW_DESC), Weapon::Bow => ("Bow", BOW_DESC),
Weapons::Staff => ("Staff", STAFF_DESC), Weapon::Staff => ("Staff", STAFF_DESC),
}; };
Text::new(weapon_str) Text::new(weapon_str)
.mid_top_with_margin_on(self.ids.creation_window, 370.0) .mid_top_with_margin_on(self.ids.creation_window, 370.0)
@ -1452,7 +1459,7 @@ impl CharSelectionUi {
.was_clicked() .was_clicked()
{}; {};
// Beard -> Only active when "male" was chosen // Beard -> Only active when "male" was chosen
if let Sex::Male = self.sex { if let Gender::Male = self.gender {
Text::new("Beard Style") Text::new("Beard Style")
.mid_top_with_margin_on(self.ids.hair_window, 340.0) .mid_top_with_margin_on(self.ids.hair_window, 340.0)
.color(TEXT_COLOR) .color(TEXT_COLOR)
@ -1484,7 +1491,7 @@ impl CharSelectionUi {
// Brightness -> Slider // Brightness -> Slider
BodyPart::Accessories => { BodyPart::Accessories => {
match self.race { match self.race {
Races::Human => { Race::Human => {
Text::new("Head Band") Text::new("Head Band")
.mid_top_with_margin_on(self.ids.accessories_window, 60.0) .mid_top_with_margin_on(self.ids.accessories_window, 60.0)
.color(TEXT_COLOR) .color(TEXT_COLOR)
@ -1550,7 +1557,7 @@ impl CharSelectionUi {
.font_size(14) .font_size(14)
.set(self.ids.warpaint_slider_text, ui_widgets); .set(self.ids.warpaint_slider_text, ui_widgets);
} // Human } // Human
Races::Orc => { Race::Orc => {
Text::new("Head Band") Text::new("Head Band")
.mid_top_with_margin_on(self.ids.accessories_window, 60.0) .mid_top_with_margin_on(self.ids.accessories_window, 60.0)
.color(TEXT_COLOR) .color(TEXT_COLOR)
@ -1616,7 +1623,7 @@ impl CharSelectionUi {
.font_size(14) .font_size(14)
.set(self.ids.warpaint_slider_text, ui_widgets); .set(self.ids.warpaint_slider_text, ui_widgets);
} // Orc } // Orc
Races::Elf => { Race::Elf => {
Text::new("Tribe Markings") Text::new("Tribe Markings")
.mid_top_with_margin_on(self.ids.accessories_window, 60.0) .mid_top_with_margin_on(self.ids.accessories_window, 60.0)
.color(TEXT_COLOR) .color(TEXT_COLOR)
@ -1682,7 +1689,7 @@ impl CharSelectionUi {
.font_size(14) .font_size(14)
.set(self.ids.warpaint_slider_text, ui_widgets); .set(self.ids.warpaint_slider_text, ui_widgets);
} // Elf } // Elf
Races::Dwarf => { Race::Dwarf => {
Text::new("War Paint") Text::new("War Paint")
.mid_top_with_margin_on(self.ids.accessories_window, 60.0) .mid_top_with_margin_on(self.ids.accessories_window, 60.0)
.color(TEXT_COLOR) .color(TEXT_COLOR)
@ -1748,7 +1755,7 @@ impl CharSelectionUi {
.font_size(14) .font_size(14)
.set(self.ids.warpaint_slider_text, ui_widgets); .set(self.ids.warpaint_slider_text, ui_widgets);
} // Dwarf } // Dwarf
Races::Undead => { Race::Undead => {
Text::new("Teeth") Text::new("Teeth")
.mid_top_with_margin_on(self.ids.accessories_window, 60.0) .mid_top_with_margin_on(self.ids.accessories_window, 60.0)
.color(TEXT_COLOR) .color(TEXT_COLOR)
@ -1814,7 +1821,7 @@ impl CharSelectionUi {
.font_size(14) .font_size(14)
.set(self.ids.warpaint_slider_text, ui_widgets); .set(self.ids.warpaint_slider_text, ui_widgets);
} // Undead } // Undead
Races::Danari => { Race::Danari => {
Text::new("Horns") Text::new("Horns")
.mid_top_with_margin_on(self.ids.accessories_window, 60.0) .mid_top_with_margin_on(self.ids.accessories_window, 60.0)
.color(TEXT_COLOR) .color(TEXT_COLOR)

View File

@ -22,10 +22,10 @@ pub struct ClientInit {
impl ClientInit { impl ClientInit {
pub fn new( pub fn new(
connection_args: (String, u16, bool), connection_args: (String, u16, bool),
client_args: (comp::Player, Option<comp::Character>, u64), client_args: (comp::Player, u64),
) -> Self { ) -> Self {
let (server_address, default_port, prefer_ipv6) = connection_args; let (server_address, default_port, prefer_ipv6) = connection_args;
let (player, character, view_distance) = client_args; let (player, view_distance) = client_args;
let (tx, rx) = channel(); let (tx, rx) = channel();
@ -44,7 +44,7 @@ impl ClientInit {
let mut last_err = None; let mut last_err = None;
for socket_addr in first_addrs.into_iter().chain(second_addrs) { for socket_addr in first_addrs.into_iter().chain(second_addrs) {
match Client::new(socket_addr, player.clone(), character, view_distance) { match Client::new(socket_addr, player.clone(), view_distance) {
Ok(client) => { Ok(client) => {
let _ = tx.send(Ok(client)); let _ = tx.send(Ok(client));
return; return;

View File

@ -98,7 +98,6 @@ impl PlayState for MainMenuState {
(server_address, DEFAULT_PORT, false), (server_address, DEFAULT_PORT, false),
( (
comp::Player::new(username.clone()), comp::Player::new(username.clone()),
Some(comp::Character::test()),
300, 300,
), ),
))); )));

View File

@ -6,7 +6,19 @@ use specs::{Entity as EcsEntity, Component, VecStorage, Join};
use vek::*; use vek::*;
use client::Client; use client::Client;
use common::{ use common::{
comp, comp::{
self,
character::{
Character,
Head,
Chest,
Belt,
Pants,
Hand,
Foot,
Weapon,
}
},
figure::Segment, figure::Segment,
msg, msg,
assets, assets,
@ -35,58 +47,123 @@ use crate::{
mesh::Meshable, mesh::Meshable,
}; };
pub struct Figures { pub struct FigureCache {
test_model: Model<FigurePipeline>, models: HashMap<Character, Model<FigurePipeline>>,
states: HashMap<EcsEntity, FigureState<CharacterSkeleton>>, states: HashMap<EcsEntity, FigureState<CharacterSkeleton>>,
} }
impl Figures { impl FigureCache {
pub fn new(renderer: &mut Renderer) -> Self { pub fn new() -> Self {
// TODO: Make a proper asset loading system
fn load_segment(filename: &'static str) -> Segment {
let fullpath: String = ["/voxygen/voxel/", filename].concat();
Segment::from(dot_vox::load_bytes(
assets::load(fullpath.as_str())
.expect("Error loading file")
.as_slice(),
).unwrap())
}
let bone_meshes = [
Some(load_segment("head.vox").generate_mesh(Vec3::new(-3.5, -7.0, -6.0))),
Some(load_segment("chest.vox").generate_mesh(Vec3::new(-3.0, -6.0, 0.0))),
Some(load_segment("belt.vox").generate_mesh(Vec3::new(-3.0, -5.0, 0.0))),
Some(load_segment("pants.vox").generate_mesh(Vec3::new(-3.0, -5.0, 0.0))),
Some(load_segment("hand.vox").generate_mesh(Vec3::new(0.0, -2.0, -6.0))),
Some(load_segment("hand.vox").generate_mesh(Vec3::new(0.0, -2.0, -6.0))),
Some(load_segment("foot.vox").generate_mesh(Vec3::new(-4.0, -2.5, -6.0))),
Some(load_segment("foot.vox").generate_mesh(Vec3::new(-4.0, -2.5, -6.0))),
Some(load_segment("sword.vox").generate_mesh(Vec3::new(0.0, -0.0, 0.0))),
None,
None,
None,
None,
None,
None,
None,
];
let mut mesh = Mesh::new();
bone_meshes
.iter()
.enumerate()
.filter_map(|(i, bm)| bm.as_ref().map(|bm| (i, bm)))
.for_each(|(i, bone_mesh)| {
mesh.push_mesh_map(bone_mesh, |vert| vert.with_bone_idx(i as u8))
});
Self { Self {
test_model: renderer.create_model(&mesh).unwrap(), models: HashMap::new(),
states: HashMap::new(), states: HashMap::new(),
} }
} }
pub fn get_or_create_model<'a>(
models: &'a mut HashMap<Character, Model<FigurePipeline>>,
renderer: &mut Renderer,
character: Character)
-> &'a Model<FigurePipeline> {
models.entry(character).or_insert_with(|| {
let bone_meshes = [
Some(Self::load_head(character.head)),
Some(Self::load_chest(character.chest)),
Some(Self::load_belt(character.belt)),
Some(Self::load_pants(character.pants)),
Some(Self::load_left_hand(character.hand)),
Some(Self::load_right_hand(character.hand)),
Some(Self::load_left_foot(character.foot)),
Some(Self::load_right_foot(character.foot)),
Some(Self::load_weapon(character.weapon)),
None,
None,
None,
None,
None,
None,
None,
];
let mut mesh = Mesh::new();
bone_meshes
.iter()
.enumerate()
.filter_map(|(i, bm)| bm.as_ref().map(|bm| (i, bm)))
.for_each(|(i, bone_mesh)| {
mesh.push_mesh_map(bone_mesh, |vert| vert.with_bone_idx(i as u8))
});
renderer.create_model(&mesh).unwrap()
})
}
fn load_mesh(filename: &'static str, position: Vec3<f32>) -> Mesh<FigurePipeline> {
let fullpath: String = ["/voxygen/voxel/", filename].concat();
Segment::from(dot_vox::load_bytes(
assets::load(fullpath.as_str())
.expect("Error loading file")
.as_slice(),
).unwrap())
.generate_mesh(position)
}
fn load_head(head: Head) -> Mesh<FigurePipeline> {
Self::load_mesh(match head {
Head::DefaultHead => "head.vox",
}, Vec3::new(-3.5, -7.0, -6.0))
}
fn load_chest(chest: Chest) -> Mesh<FigurePipeline> {
Self::load_mesh(match chest {
Chest::DefaultChest => "chest.vox",
}, Vec3::new(-3.0, -6.0, 0.0))
}
fn load_belt(belt: Belt) -> Mesh<FigurePipeline> {
Self::load_mesh(match belt {
Belt::DefaultBelt => "belt.vox",
}, Vec3::new(-3.0, -5.0, 0.0))
}
fn load_pants(pants: Pants) -> Mesh<FigurePipeline> {
Self::load_mesh(match pants {
Pants::DefaultPants => "pants.vox",
}, Vec3::new(-3.0, -5.0, 0.0))
}
fn load_left_hand(hand: Hand) -> Mesh<FigurePipeline> {
Self::load_mesh(match hand {
Hand::DefaultHand => "hand.vox",
}, Vec3::new(0.0, -2.0, -6.0))
}
fn load_right_hand(hand: Hand) -> Mesh<FigurePipeline> {
Self::load_mesh(match hand {
Hand::DefaultHand => "hand.vox",
}, Vec3::new(0.0, -2.0, -6.0))
}
fn load_left_foot(foot: Foot) -> Mesh<FigurePipeline> {
Self::load_mesh(match foot {
Foot::DefaultFoot => "foot.vox",
}, Vec3::new(-4.0, -2.5, -6.0))
}
fn load_right_foot(foot: Foot) -> Mesh<FigurePipeline> {
Self::load_mesh(match foot {
Foot::DefaultFoot => "foot.vox",
}, Vec3::new(-4.0, -2.5, -6.0))
}
fn load_weapon(weapon: Weapon) -> Mesh<FigurePipeline> {
Self::load_mesh(match weapon {
Weapon::Sword => "sword.vox",
// TODO actually match against other weapons and set the right model
_ => "sword.vox",
}, Vec3::new(0.0, 0.0, 0.0))
}
pub fn maintain(&mut self, renderer: &mut Renderer, client: &mut Client) { pub fn maintain(&mut self, renderer: &mut Renderer, client: &mut Client) {
let time = client.state().get_time(); let time = client.state().get_time();
let ecs = client.state_mut().ecs_mut(); let ecs = client.state_mut().ecs_mut();
@ -114,10 +191,17 @@ impl Figures {
self.states.retain(|entity, _| ecs.entities().is_alive(*entity)); self.states.retain(|entity, _| ecs.entities().is_alive(*entity));
} }
pub fn render(&self, renderer: &mut Renderer, client: &Client, globals: &Consts<Globals>) { pub fn render(&mut self, renderer: &mut Renderer, client: &Client, globals: &Consts<Globals>) {
for state in self.states.values() { let ecs = client.state().ecs().internal();
let models = &mut self.models;
for (entity, &character) in (
&ecs.entities(),
&ecs.read_storage::<comp::Character>(),
).join() {
let model = Self::get_or_create_model(models, renderer, character);
let state = self.states.get(&entity).unwrap();
renderer.render_figure( renderer.render_figure(
&self.test_model, model,
globals, globals,
&state.locals, &state.locals,
&state.bone_consts, &state.bone_consts,

View File

@ -21,15 +21,14 @@ use crate::{
create_skybox_mesh, create_skybox_mesh,
}, },
window::Event, window::Event,
mesh::Meshable, mesh::Meshable, anim::{
anim::{
Animation, Animation,
character::{CharacterSkeleton, RunAnimation}, character::{CharacterSkeleton, RunAnimation},
}, },
}; };
use self::{ use self::{
camera::Camera, camera::Camera,
figure::Figures, figure::FigureCache,
terrain::Terrain, terrain::Terrain,
}; };
@ -47,7 +46,8 @@ pub struct Scene {
skybox: Skybox, skybox: Skybox,
terrain: Terrain, terrain: Terrain,
figures: Figures,
figure_cache: FigureCache,
} }
impl Scene { impl Scene {
@ -70,7 +70,7 @@ impl Scene {
.unwrap(), .unwrap(),
}, },
terrain: Terrain::new(), terrain: Terrain::new(),
figures: Figures::new(renderer), figure_cache: FigureCache::new(),
} }
} }
@ -134,13 +134,15 @@ impl Scene {
)]) )])
.expect("Failed to update global constants"); .expect("Failed to update global constants");
// Maintain the terrain and figures // Maintain the terrain
self.terrain.maintain(renderer, client); self.terrain.maintain(renderer, client);
self.figures.maintain(renderer, client);
// Maintain the figures
self.figure_cache.maintain(renderer, client);
} }
/// Render the scene using the provided `Renderer` /// Render the scene using the provided `Renderer`
pub fn render(&self, renderer: &mut Renderer, client: &Client) { pub fn render(&mut self, renderer: &mut Renderer, client: &Client) {
// Render the skybox first (it appears over everything else so must be rendered first) // Render the skybox first (it appears over everything else so must be rendered first)
renderer.render_skybox( renderer.render_skybox(
&self.skybox.model, &self.skybox.model,
@ -150,6 +152,6 @@ impl Scene {
// Render terrain and figures // Render terrain and figures
self.terrain.render(renderer, &self.globals); self.terrain.render(renderer, &self.globals);
self.figures.render(renderer, client, &self.globals); self.figure_cache.render(renderer, client, &self.globals);
} }
} }