mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Begin creating character editing
This commit is contained in:
parent
de5ca67615
commit
408fe1e6b6
@ -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;
|
||||||
|
@ -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()
|
||||||
},
|
},
|
||||||
|
@ -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()
|
||||||
},
|
},
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
},
|
},
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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.
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
},
|
},
|
||||||
|
@ -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)),
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user