Begin creating character editing

This commit is contained in:
Dr. Dystopia 2021-12-13 01:13:33 +01:00
parent de5ca67615
commit 408fe1e6b6
14 changed files with 341 additions and 31 deletions

View File

@ -738,6 +738,7 @@ impl Client {
let stream = match msg { let stream = match msg {
ClientGeneral::RequestCharacterList ClientGeneral::RequestCharacterList
| ClientGeneral::CreateCharacter { .. } | ClientGeneral::CreateCharacter { .. }
| ClientGeneral::EditCharacter { .. }
| ClientGeneral::DeleteCharacter(_) | ClientGeneral::DeleteCharacter(_)
| ClientGeneral::Character(_) | ClientGeneral::Character(_)
| ClientGeneral::Spectate => &mut self.character_screen_stream, | ClientGeneral::Spectate => &mut self.character_screen_stream,
@ -842,6 +843,11 @@ impl Client {
}); });
} }
pub fn edit_character(&mut self, alias: String, id: CharacterId, body: comp::Body) {
self.character_list.loading = true;
self.send_msg(ClientGeneral::EditCharacter { alias, id, body });
}
/// Character deletion /// Character deletion
pub fn delete_character(&mut self, character_id: CharacterId) { pub fn delete_character(&mut self, character_id: CharacterId) {
self.character_list.loading = true; self.character_list.loading = true;

View File

@ -55,6 +55,11 @@ pub enum ClientGeneral {
body: comp::Body, body: comp::Body,
}, },
DeleteCharacter(CharacterId), DeleteCharacter(CharacterId),
EditCharacter {
id: CharacterId,
alias: String,
body: comp::Body,
},
Character(CharacterId), Character(CharacterId),
Spectate, Spectate,
//Only in game //Only in game
@ -105,6 +110,7 @@ impl ClientMsg {
&& match g { && match g {
ClientGeneral::RequestCharacterList ClientGeneral::RequestCharacterList
| ClientGeneral::CreateCharacter { .. } | ClientGeneral::CreateCharacter { .. }
| ClientGeneral::EditCharacter { .. }
| ClientGeneral::DeleteCharacter(_) => { | ClientGeneral::DeleteCharacter(_) => {
c_type != ClientType::ChatOnly && presence.is_none() c_type != ClientType::ChatOnly && presence.is_none()
}, },

View File

@ -133,6 +133,7 @@ pub enum ServerGeneral {
CharacterActionError(String), CharacterActionError(String),
/// A new character was created /// A new character was created
CharacterCreated(character::CharacterId), CharacterCreated(character::CharacterId),
CharacterEdited(character::CharacterId),
CharacterSuccess, CharacterSuccess,
//Ingame related //Ingame related
GroupUpdate(comp::group::ChangeNotification<sync::Uid>), GroupUpdate(comp::group::ChangeNotification<sync::Uid>),
@ -275,6 +276,7 @@ impl ServerMsg {
ServerGeneral::CharacterDataLoadError(_) ServerGeneral::CharacterDataLoadError(_)
| ServerGeneral::CharacterListUpdate(_) | ServerGeneral::CharacterListUpdate(_)
| ServerGeneral::CharacterActionError(_) | ServerGeneral::CharacterActionError(_)
| ServerGeneral::CharacterEdited(_)
| ServerGeneral::CharacterCreated(_) => { | ServerGeneral::CharacterCreated(_) => {
c_type != ClientType::ChatOnly && presence.is_none() c_type != ClientType::ChatOnly && presence.is_none()
}, },

View File

@ -1,6 +1,7 @@
use crate::persistence::character_updater::CharacterUpdater; use crate::persistence::character_updater::CharacterUpdater;
use common::comp::{ use common::{
inventory::loadout_builder::LoadoutBuilder, Body, Inventory, Item, SkillSet, Stats, character::CharacterId,
comp::{inventory::loadout_builder::LoadoutBuilder, Body, Inventory, Item, SkillSet, Stats},
}; };
use specs::{Entity, WriteExpect}; use specs::{Entity, WriteExpect};
@ -73,6 +74,26 @@ pub fn create_character(
Ok(()) Ok(())
} }
pub fn edit_character(
entity: Entity,
player_uuid: String,
id: CharacterId,
character_alias: String,
body: Body,
character_updater: &mut WriteExpect<'_, CharacterUpdater>,
) -> Result<(), CreationError> {
// quick fix whitelist validation for now; eventually replace the
// `Option<String>` with an index into a server-provided list of starter
// items, and replace `comp::body::Body` with `comp::body::humanoid::Body`
// throughout the messages involved
if !matches!(body, Body::Humanoid(_)) {
return Err(CreationError::InvalidBody);
}
character_updater.edit_character(entity, player_uuid, id, character_alias, (body,));
Ok(())
}
// Error handling // Error handling
impl core::fmt::Display for CreationError { impl core::fmt::Display for CreationError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {

View File

@ -94,6 +94,7 @@ impl Client {
| ServerGeneral::CharacterListUpdate(_) | ServerGeneral::CharacterListUpdate(_)
| ServerGeneral::CharacterActionError(_) | ServerGeneral::CharacterActionError(_)
| ServerGeneral::CharacterCreated(_) | ServerGeneral::CharacterCreated(_)
| ServerGeneral::CharacterEdited(_)
| ServerGeneral::CharacterSuccess => { | ServerGeneral::CharacterSuccess => {
self.character_screen_stream.lock().unwrap().send(g) self.character_screen_stream.lock().unwrap().send(g)
}, },
@ -165,6 +166,7 @@ impl Client {
| ServerGeneral::CharacterListUpdate(_) | ServerGeneral::CharacterListUpdate(_)
| ServerGeneral::CharacterActionError(_) | ServerGeneral::CharacterActionError(_)
| ServerGeneral::CharacterCreated(_) | ServerGeneral::CharacterCreated(_)
| ServerGeneral::CharacterEdited(_)
| ServerGeneral::CharacterSuccess => { | ServerGeneral::CharacterSuccess => {
PreparedMsg::new(1, &g, &self.character_screen_stream_params) PreparedMsg::new(1, &g, &self.character_screen_stream_params)
}, },

View File

@ -813,6 +813,22 @@ impl Server {
ServerGeneral::CharacterActionError(error.to_string()), ServerGeneral::CharacterActionError(error.to_string()),
), ),
}, },
CharacterLoaderResponseKind::CharacterEdit(result) => match result {
Ok((character_id, list)) => {
self.notify_client(
query_result.entity,
ServerGeneral::CharacterListUpdate(list),
);
self.notify_client(
query_result.entity,
ServerGeneral::CharacterEdited(character_id),
);
},
Err(error) => self.notify_client(
query_result.entity,
ServerGeneral::CharacterActionError(error.to_string()),
),
},
CharacterLoaderResponseKind::CharacterData(result) => { CharacterLoaderResponseKind::CharacterData(result) => {
let message = match *result { let message = match *result {
Ok(character_data) => ServerEvent::UpdateCharacterData { Ok(character_data) => ServerEvent::UpdateCharacterData {

View File

@ -22,7 +22,7 @@ use crate::{
character_loader::{CharacterCreationResult, CharacterDataResult, CharacterListResult}, character_loader::{CharacterCreationResult, CharacterDataResult, CharacterListResult},
character_updater::PetPersistenceData, character_updater::PetPersistenceData,
error::PersistenceError::DatabaseError, error::PersistenceError::DatabaseError,
PersistedComponents, EditableComponents, PersistedComponents,
}, },
}; };
use common::character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER}; use common::character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER};
@ -497,6 +497,35 @@ pub fn create_character(
load_character_list(uuid, transactionn).map(|list| (character_id, list)) load_character_list(uuid, transactionn).map(|list| (character_id, list))
} }
pub fn edit_character(
editable_components: EditableComponents,
transaction: &mut Transaction,
character_id: CharacterId,
uuid: &str,
character_alias: &str,
) -> CharacterCreationResult {
let (body,) = editable_components;
let mut stmt = transaction
.prepare_cached("UPDATE body SET variant = ?1, body_data = ?2 WHERE character_id = ?3")?;
let (body_variant, body_data) = convert_body_to_database_json(&body)?;
stmt.execute(&[
&body_variant.to_string(),
&body_data,
&character_id as &dyn ToSql,
])?;
drop(stmt);
let mut stmt =
transaction.prepare_cached("UPDATE character SET alias = ?1 WHERE character_id = ?2")?;
stmt.execute(&[&character_alias, &character_id as &dyn ToSql])?;
drop(stmt);
load_character_list(uuid, transaction).map(|list| (character_id, list))
}
/// Delete a character. Returns the updated character list. /// Delete a character. Returns the updated character list.
pub fn delete_character( pub fn delete_character(
requesting_player_uuid: &str, requesting_player_uuid: &str,

View File

@ -12,6 +12,7 @@ use tracing::error;
pub(crate) type CharacterListResult = Result<Vec<CharacterItem>, PersistenceError>; pub(crate) type CharacterListResult = Result<Vec<CharacterItem>, PersistenceError>;
pub(crate) type CharacterCreationResult = pub(crate) type CharacterCreationResult =
Result<(CharacterId, Vec<CharacterItem>), PersistenceError>; Result<(CharacterId, Vec<CharacterItem>), PersistenceError>;
pub(crate) type CharacterEditResult = Result<(CharacterId, Vec<CharacterItem>), PersistenceError>;
pub(crate) type CharacterDataResult = Result<PersistedComponents, PersistenceError>; pub(crate) type CharacterDataResult = Result<PersistedComponents, PersistenceError>;
type CharacterLoaderRequest = (specs::Entity, CharacterLoaderRequestKind); type CharacterLoaderRequest = (specs::Entity, CharacterLoaderRequestKind);
@ -33,6 +34,7 @@ pub enum CharacterLoaderResponseKind {
CharacterList(CharacterListResult), CharacterList(CharacterListResult),
CharacterData(Box<CharacterDataResult>), CharacterData(Box<CharacterDataResult>),
CharacterCreation(CharacterCreationResult), CharacterCreation(CharacterCreationResult),
CharacterEdit(CharacterEditResult),
} }
/// Common message format dispatched in response to an update request /// Common message format dispatched in response to an update request

View File

@ -4,7 +4,8 @@ use common::character::CharacterId;
use crate::persistence::{ use crate::persistence::{
character_loader::{CharacterLoaderResponse, CharacterLoaderResponseKind}, character_loader::{CharacterLoaderResponse, CharacterLoaderResponseKind},
error::PersistenceError, error::PersistenceError,
establish_connection, ConnectionMode, DatabaseSettings, PersistedComponents, VelorenConnection, establish_connection, ConnectionMode, DatabaseSettings, EditableComponents,
PersistedComponents, VelorenConnection,
}; };
use crossbeam_channel::TryIter; use crossbeam_channel::TryIter;
use rusqlite::DropBehavior; use rusqlite::DropBehavior;
@ -36,6 +37,13 @@ pub enum CharacterUpdaterEvent {
character_alias: String, character_alias: String,
persisted_components: PersistedComponents, persisted_components: PersistedComponents,
}, },
EditCharacter {
entity: Entity,
player_uuid: String,
character_id: CharacterId,
character_alias: String,
editable_components: EditableComponents,
},
DeleteCharacter { DeleteCharacter {
entity: Entity, entity: Entity,
requesting_player_uuid: String, requesting_player_uuid: String,
@ -124,6 +132,37 @@ impl CharacterUpdater {
), ),
} }
}, },
CharacterUpdaterEvent::EditCharacter {
entity,
character_id,
character_alias,
player_uuid,
editable_components,
} => {
match execute_character_edit(
entity,
character_id,
character_alias,
&player_uuid,
editable_components,
&mut conn,
) {
Ok(response) => {
if let Err(e) = response_tx.send(response) {
error!(?e, "Could not send character edit response");
} else {
debug!(
"Processed character create for player {}",
player_uuid
);
}
},
Err(e) => error!(
"Error editing character for player {}, error: {:?}",
player_uuid, e
),
}
},
CharacterUpdaterEvent::DeleteCharacter { CharacterUpdaterEvent::DeleteCharacter {
entity, entity,
requesting_player_uuid, requesting_player_uuid,
@ -231,6 +270,30 @@ impl CharacterUpdater {
} }
} }
pub fn edit_character(
&mut self,
entity: Entity,
requesting_player_uuid: String,
character_id: CharacterId,
alias: String,
editable_components: EditableComponents,
) {
if let Err(e) =
self.update_tx
.as_ref()
.unwrap()
.send(CharacterUpdaterEvent::EditCharacter {
entity,
player_uuid: requesting_player_uuid,
character_id,
character_alias: alias,
editable_components,
})
{
error!(?e, "Could not send character edit request");
}
}
pub fn delete_character( pub fn delete_character(
&mut self, &mut self,
entity: Entity, entity: Entity,
@ -363,6 +426,34 @@ fn execute_character_create(
Ok(response) Ok(response)
} }
fn execute_character_edit(
entity: Entity,
character_id: CharacterId,
alias: String,
requesting_player_uuid: &str,
editable_components: EditableComponents,
connection: &mut VelorenConnection,
) -> Result<CharacterLoaderResponse, PersistenceError> {
let mut transaction = connection.connection.transaction()?;
let response = CharacterLoaderResponse {
entity,
result: CharacterLoaderResponseKind::CharacterEdit(super::character::edit_character(
editable_components,
&mut transaction,
character_id,
requesting_player_uuid,
&alias,
)),
};
if !response.is_err() {
transaction.commit()?;
};
Ok(response)
}
fn execute_character_delete( fn execute_character_delete(
entity: Entity, entity: Entity,
requesting_player_uuid: &str, requesting_player_uuid: &str,

View File

@ -31,6 +31,8 @@ pub type PersistedComponents = (
Vec<PetPersistenceData>, Vec<PetPersistenceData>,
); );
pub type EditableComponents = (comp::Body,);
// See: https://docs.rs/refinery/0.5.0/refinery/macro.embed_migrations.html // See: https://docs.rs/refinery/0.5.0/refinery/macro.embed_migrations.html
// This macro is called at build-time, and produces the necessary migration info // This macro is called at build-time, and produces the necessary migration info
// for the `run_migrations` call below. // for the `run_migrations` call below.

View File

@ -145,6 +145,28 @@ impl Sys {
} }
} }
}, },
ClientGeneral::EditCharacter { id, alias, body } => {
if let Err(error) = alias_validator.validate(&alias) {
debug!(?error, ?alias, "denied alias as it contained a banned word");
client.send(ServerGeneral::CharacterActionError(error.to_string()))?;
} else if let Some(player) = players.get(entity) {
if let Err(error) = character_creator::edit_character(
entity,
player.uuid().to_string(),
id,
alias,
body,
character_updater,
) {
debug!(
?error,
?body,
"Denied creating character because of invalid input."
);
client.send(ServerGeneral::CharacterActionError(error.to_string()))?;
}
}
},
ClientGeneral::DeleteCharacter(character_id) => { ClientGeneral::DeleteCharacter(character_id) => {
if let Some(player) = players.get(entity) { if let Some(player) = players.get(entity) {
character_updater.delete_character( character_updater.delete_character(

View File

@ -283,6 +283,7 @@ impl Sys {
}, },
ClientGeneral::RequestCharacterList ClientGeneral::RequestCharacterList
| ClientGeneral::CreateCharacter { .. } | ClientGeneral::CreateCharacter { .. }
| ClientGeneral::EditCharacter { .. }
| ClientGeneral::DeleteCharacter(_) | ClientGeneral::DeleteCharacter(_)
| ClientGeneral::Character(_) | ClientGeneral::Character(_)
| ClientGeneral::Spectate | ClientGeneral::Spectate

View File

@ -119,6 +119,16 @@ impl PlayState for CharSelectionState {
.borrow_mut() .borrow_mut()
.create_character(alias, mainhand, offhand, body); .create_character(alias, mainhand, offhand, body);
}, },
ui::Event::EditCharacter {
alias,
character_id,
body,
} => {
self.client
.borrow_mut()
.edit_character(alias, character_id, body);
println!("Event::EditCharacter");
},
ui::Event::DeleteCharacter(character_id) => { ui::Event::DeleteCharacter(character_id) => {
self.client.borrow_mut().delete_character(character_id); self.client.borrow_mut().delete_character(character_id);
}, },

View File

@ -76,6 +76,10 @@ image_ids_ice! {
delete_button_hover: "voxygen.element.ui.char_select.icons.bin_hover", delete_button_hover: "voxygen.element.ui.char_select.icons.bin_hover",
delete_button_press: "voxygen.element.ui.char_select.icons.bin_press", delete_button_press: "voxygen.element.ui.char_select.icons.bin_press",
edit_button: "voxygen.element.ui.char_select.icons.bin",
edit_button_hover: "voxygen.element.ui.char_select.icons.bin_hover",
edit_button_press: "voxygen.element.ui.char_select.icons.bin_press",
name_input: "voxygen.element.ui.generic.textbox", name_input: "voxygen.element.ui.generic.textbox",
// Tool Icons // Tool Icons
@ -129,6 +133,11 @@ pub enum Event {
offhand: Option<String>, offhand: Option<String>,
body: comp::Body, body: comp::Body,
}, },
EditCharacter {
alias: String,
character_id: CharacterId,
body: comp::Body,
},
DeleteCharacter(CharacterId), DeleteCharacter(CharacterId),
ClearCharacterListError, ClearCharacterListError,
SelectCharacter(Option<CharacterId>), SelectCharacter(Option<CharacterId>),
@ -146,7 +155,7 @@ enum Mode {
yes_button: button::State, yes_button: button::State,
no_button: button::State, no_button: button::State,
}, },
Create { CreateOrEdit {
name: String, name: String,
body: humanoid::Body, body: humanoid::Body,
inventory: Box<comp::inventory::Inventory>, inventory: Box<comp::inventory::Inventory>,
@ -163,6 +172,7 @@ enum Mode {
create_button: button::State, create_button: button::State,
rand_character_button: button::State, rand_character_button: button::State,
rand_name_button: button::State, rand_name_button: button::State,
character_id: Option<CharacterId>,
}, },
} }
@ -194,7 +204,7 @@ impl Mode {
let inventory = Box::new(Inventory::new_with_loadout(loadout)); let inventory = Box::new(Inventory::new_with_loadout(loadout));
Self::Create { Self::CreateOrEdit {
name, name,
body: humanoid::Body::random(), body: humanoid::Body::random(),
inventory, inventory,
@ -210,6 +220,41 @@ impl Mode {
create_button: Default::default(), create_button: Default::default(),
rand_character_button: Default::default(), rand_character_button: Default::default(),
rand_name_button: Default::default(), rand_name_button: Default::default(),
character_id: None,
}
}
pub fn edit(name: String, character_id: CharacterId, body: humanoid::Body) -> Self {
// TODO: Load these from the server (presumably from a .ron) to allow for easier
// modification of custom starting weapons
let mainhand = Some(STARTER_SWORD);
let offhand = None;
let loadout = LoadoutBuilder::empty()
.defaults()
.active_mainhand(mainhand.map(Item::new_from_asset_expect))
.active_offhand(offhand.map(Item::new_from_asset_expect))
.build();
let inventory = Box::new(Inventory::new_with_loadout(loadout));
Self::CreateOrEdit {
name,
body,
inventory,
mainhand,
offhand,
body_type_buttons: Default::default(),
species_buttons: Default::default(),
tool_buttons: Default::default(),
sliders: Default::default(),
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),
} }
} }
} }
@ -247,6 +292,8 @@ enum Message {
EnterWorld, EnterWorld,
Select(CharacterId), Select(CharacterId),
Delete(usize), Delete(usize),
Edit(usize),
ConfirmEdit(CharacterId),
NewCharacter, NewCharacter,
CreateCharacter, CreateCharacter,
Name(String), Name(String),
@ -421,12 +468,13 @@ impl Controls {
let characters = &client.character_list().characters; let characters = &client.character_list().characters;
let num = characters.len(); let num = characters.len();
// Ensure we have enough button states // Ensure we have enough button states
character_buttons.resize_with(num * 2, Default::default); const CHAR_BUTTONS: usize = 3;
character_buttons.resize_with(num * CHAR_BUTTONS, Default::default);
// Character Selection List // Character Selection List
let mut characters = characters let mut characters = characters
.iter() .iter()
.zip(character_buttons.chunks_exact_mut(2)) .zip(character_buttons.chunks_exact_mut(CHAR_BUTTONS))
.filter_map(|(character, buttons)| { .filter_map(|(character, buttons)| {
let mut buttons = buttons.iter_mut(); let mut buttons = buttons.iter_mut();
// TODO: eliminate option in character id? // TODO: eliminate option in character id?
@ -434,20 +482,43 @@ impl Controls {
( (
id, id,
character, character,
(buttons.next().unwrap(), buttons.next().unwrap()), (
buttons.next().unwrap(),
buttons.next().unwrap(),
buttons.next().unwrap(),
),
) )
}) })
}) })
.enumerate() .enumerate()
.map( .map(
|(i, (character_id, character, (select_button, delete_button)))| { |(
i,
(
character_id,
character,
(select_button, edit_button, delete_button),
),
)| {
let select_col = if Some(i) == selected { let select_col = if Some(i) == selected {
(255, 208, 69) (255, 208, 69)
} else { } else {
(255, 255, 255) (255, 255, 255)
}; };
Overlay::new( Overlay::new(
Container::new( Container::new(Row::with_children(vec![
// Edit button
Button::new(
edit_button,
Space::new(Length::Units(16), Length::Units(16)),
)
.style(
style::button::Style::new(imgs.edit_button)
.hover_image(imgs.edit_button_hover)
.press_image(imgs.edit_button_press),
)
.on_press(Message::Edit(i))
.into(),
// Delete button // Delete button
Button::new( Button::new(
delete_button, delete_button,
@ -458,8 +529,9 @@ impl Controls {
.hover_image(imgs.delete_button_hover) .hover_image(imgs.delete_button_hover)
.press_image(imgs.delete_button_press), .press_image(imgs.delete_button_press),
) )
.on_press(Message::Delete(i)), .on_press(Message::Delete(i))
) .into(),
]))
.padding(4), .padding(4),
// Select Button // Select Button
AspectRatioContainer::new( AspectRatioContainer::new(
@ -716,7 +788,7 @@ impl Controls {
content.into() content.into()
} }
}, },
Mode::Create { Mode::CreateOrEdit {
name, name,
body, body,
inventory: _, inventory: _,
@ -732,6 +804,7 @@ impl Controls {
ref mut create_button, ref mut create_button,
ref mut rand_character_button, ref mut rand_character_button,
ref mut rand_name_button, ref mut rand_name_button,
character_id,
} => { } => {
let unselected_style = style::button::Style::new(imgs.icon_border) let unselected_style = style::button::Style::new(imgs.icon_border)
.hover_image(imgs.icon_border_mo) .hover_image(imgs.icon_border_mo)
@ -1185,7 +1258,11 @@ impl Controls {
Message::Name, Message::Name,
) )
.size(25) .size(25)
.on_submit(Message::CreateCharacter), .on_submit(if let Some(character_id) = character_id {
Message::ConfirmEdit(*character_id)
} else {
Message::CreateCharacter
}),
) )
.padding(Padding::new().horizontal(7).top(5)); .padding(Padding::new().horizontal(7).top(5));
@ -1293,7 +1370,7 @@ impl Controls {
fn update(&mut self, message: Message, events: &mut Vec<Event>, characters: &[CharacterItem]) { fn update(&mut self, message: Message, events: &mut Vec<Event>, characters: &[CharacterItem]) {
match message { match message {
Message::Back => { Message::Back => {
if matches!(&self.mode, Mode::Create { .. }) { if matches!(&self.mode, Mode::CreateOrEdit { .. }) {
self.mode = Mode::select(None); self.mode = Mode::select(None);
} }
}, },
@ -1316,13 +1393,25 @@ impl Controls {
*info_content = Some(InfoContent::Deletion(idx)); *info_content = Some(InfoContent::Deletion(idx));
} }
}, },
Message::Edit(idx) => {
if matches!(&self.mode, Mode::Select { .. }) {
if let Some(character) = characters.get(idx) {
if let comp::Body::Humanoid(body) = character.body {
if let Some(id) = character.character.id {
self.mode = Mode::edit(character.character.alias.clone(), id, body);
println!("Message::Edit");
}
}
}
}
},
Message::NewCharacter => { Message::NewCharacter => {
if matches!(&self.mode, Mode::Select { .. }) { if matches!(&self.mode, Mode::Select { .. }) {
self.mode = Mode::create(self.default_name.clone()); self.mode = Mode::create(self.default_name.clone());
} }
}, },
Message::CreateCharacter => { Message::CreateCharacter => {
if let Mode::Create { if let Mode::CreateOrEdit {
name, name,
body, body,
mainhand, mainhand,
@ -1339,25 +1428,36 @@ impl Controls {
self.mode = Mode::select(Some(InfoContent::CreatingCharacter)); self.mode = Mode::select(Some(InfoContent::CreatingCharacter));
} }
}, },
Message::ConfirmEdit(character_id) => {
if let Mode::CreateOrEdit { name, body, .. } = &self.mode {
events.push(Event::EditCharacter {
alias: name.clone(),
character_id,
body: comp::Body::Humanoid(*body),
});
self.mode = Mode::select(Some(InfoContent::CreatingCharacter));
println!("Message::ConfirmEdit");
}
},
Message::Name(value) => { Message::Name(value) => {
if let Mode::Create { name, .. } = &mut self.mode { if let Mode::CreateOrEdit { name, .. } = &mut self.mode {
*name = value.chars().take(MAX_NAME_LENGTH).collect(); *name = value.chars().take(MAX_NAME_LENGTH).collect();
} }
}, },
Message::BodyType(value) => { Message::BodyType(value) => {
if let Mode::Create { body, .. } = &mut self.mode { if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
body.body_type = value; body.body_type = value;
body.validate(); body.validate();
} }
}, },
Message::Species(value) => { Message::Species(value) => {
if let Mode::Create { body, .. } = &mut self.mode { if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
body.species = value; body.species = value;
body.validate(); body.validate();
} }
}, },
Message::Tool(value) => { Message::Tool(value) => {
if let Mode::Create { if let Mode::CreateOrEdit {
mainhand, mainhand,
offhand, offhand,
inventory, inventory,
@ -1378,7 +1478,7 @@ impl Controls {
}, },
//Todo: Add species and body type to randomization. //Todo: Add species and body type to randomization.
Message::RandomizeCharacter => { Message::RandomizeCharacter => {
if let Mode::Create { body, .. } = &mut self.mode { if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
use rand::Rng; use rand::Rng;
let body_type = body.body_type; let body_type = body.body_type;
let species = body.species; let species = body.species;
@ -1394,7 +1494,7 @@ impl Controls {
}, },
Message::RandomizeName => { Message::RandomizeName => {
if let Mode::Create { name, body, .. } = &mut self.mode { if let Mode::CreateOrEdit { name, body, .. } = &mut self.mode {
use common::npc; use common::npc;
*name = npc::get_npc_name( *name = npc::get_npc_name(
npc::NpcKind::Humanoid, npc::NpcKind::Humanoid,
@ -1429,43 +1529,43 @@ impl Controls {
events.push(Event::ClearCharacterListError); events.push(Event::ClearCharacterListError);
}, },
Message::HairStyle(value) => { Message::HairStyle(value) => {
if let Mode::Create { body, .. } = &mut self.mode { if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
body.hair_style = value; body.hair_style = value;
body.validate(); body.validate();
} }
}, },
Message::HairColor(value) => { Message::HairColor(value) => {
if let Mode::Create { body, .. } = &mut self.mode { if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
body.hair_color = value; body.hair_color = value;
body.validate(); body.validate();
} }
}, },
Message::Skin(value) => { Message::Skin(value) => {
if let Mode::Create { body, .. } = &mut self.mode { if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
body.skin = value; body.skin = value;
body.validate(); body.validate();
} }
}, },
Message::Eyes(value) => { Message::Eyes(value) => {
if let Mode::Create { body, .. } = &mut self.mode { if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
body.eyes = value; body.eyes = value;
body.validate(); body.validate();
} }
}, },
Message::EyeColor(value) => { Message::EyeColor(value) => {
if let Mode::Create { body, .. } = &mut self.mode { if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
body.eye_color = value; body.eye_color = value;
body.validate(); body.validate();
} }
}, },
Message::Accessory(value) => { Message::Accessory(value) => {
if let Mode::Create { body, .. } = &mut self.mode { if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
body.accessory = value; body.accessory = value;
body.validate(); body.validate();
} }
}, },
Message::Beard(value) => { Message::Beard(value) => {
if let Mode::Create { body, .. } = &mut self.mode { if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
body.beard = value; body.beard = value;
body.validate(); body.validate();
} }
@ -1484,7 +1584,7 @@ impl Controls {
.selected .selected
.and_then(|id| characters.iter().find(|i| i.character.id == Some(id))) .and_then(|id| characters.iter().find(|i| i.character.id == Some(id)))
.map(|i| (i.body, &i.inventory)), .map(|i| (i.body, &i.inventory)),
Mode::Create { Mode::CreateOrEdit {
inventory, body, .. inventory, body, ..
} => Some((comp::Body::Humanoid(*body), inventory)), } => Some((comp::Body::Humanoid(*body), inventory)),
} }