mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'Character-editting' into 'master'
Character editing See merge request veloren/veloren!3059
This commit is contained in:
commit
9a4e6b81d3
BIN
assets/voxygen/element/ui/char_select/icons/pen.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/ui/char_select/icons/pen.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/ui/char_select/icons/pen_hover.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/ui/char_select/icons/pen_hover.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/ui/char_select/icons/pen_press.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/ui/char_select/icons/pen_press.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -37,6 +37,7 @@
|
||||
"common.automatic": "Auto",
|
||||
"common.random": "Random",
|
||||
"common.empty": "Empty",
|
||||
"common.confirm": "Confirm",
|
||||
|
||||
// Settings Window title
|
||||
"common.interface_settings": "Interface Settings",
|
||||
|
@ -108,6 +108,7 @@ pub enum Event {
|
||||
SetViewDistance(u32),
|
||||
Outcome(Outcome),
|
||||
CharacterCreated(CharacterId),
|
||||
CharacterEdited(CharacterId),
|
||||
CharacterError(String),
|
||||
}
|
||||
|
||||
@ -738,6 +739,7 @@ impl Client {
|
||||
let stream = match msg {
|
||||
ClientGeneral::RequestCharacterList
|
||||
| ClientGeneral::CreateCharacter { .. }
|
||||
| ClientGeneral::EditCharacter { .. }
|
||||
| ClientGeneral::DeleteCharacter(_)
|
||||
| ClientGeneral::Character(_)
|
||||
| ClientGeneral::Spectate => &mut self.character_screen_stream,
|
||||
@ -842,6 +844,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
|
||||
pub fn delete_character(&mut self, character_id: CharacterId) {
|
||||
self.character_list.loading = true;
|
||||
@ -2037,6 +2044,9 @@ impl Client {
|
||||
ServerGeneral::CharacterCreated(character_id) => {
|
||||
events.push(Event::CharacterCreated(character_id));
|
||||
},
|
||||
ServerGeneral::CharacterEdited(character_id) => {
|
||||
events.push(Event::CharacterEdited(character_id));
|
||||
},
|
||||
ServerGeneral::CharacterSuccess => {
|
||||
debug!("client is now in ingame state on server");
|
||||
if let Some(vd) = self.view_distance {
|
||||
|
@ -55,6 +55,11 @@ pub enum ClientGeneral {
|
||||
body: comp::Body,
|
||||
},
|
||||
DeleteCharacter(CharacterId),
|
||||
EditCharacter {
|
||||
id: CharacterId,
|
||||
alias: String,
|
||||
body: comp::Body,
|
||||
},
|
||||
Character(CharacterId),
|
||||
Spectate,
|
||||
//Only in game
|
||||
@ -105,6 +110,7 @@ impl ClientMsg {
|
||||
&& match g {
|
||||
ClientGeneral::RequestCharacterList
|
||||
| ClientGeneral::CreateCharacter { .. }
|
||||
| ClientGeneral::EditCharacter { .. }
|
||||
| ClientGeneral::DeleteCharacter(_) => {
|
||||
c_type != ClientType::ChatOnly && presence.is_none()
|
||||
},
|
||||
|
@ -133,6 +133,7 @@ pub enum ServerGeneral {
|
||||
CharacterActionError(String),
|
||||
/// A new character was created
|
||||
CharacterCreated(character::CharacterId),
|
||||
CharacterEdited(character::CharacterId),
|
||||
CharacterSuccess,
|
||||
//Ingame related
|
||||
GroupUpdate(comp::group::ChangeNotification<sync::Uid>),
|
||||
@ -275,6 +276,7 @@ impl ServerMsg {
|
||||
ServerGeneral::CharacterDataLoadError(_)
|
||||
| ServerGeneral::CharacterListUpdate(_)
|
||||
| ServerGeneral::CharacterActionError(_)
|
||||
| ServerGeneral::CharacterEdited(_)
|
||||
| ServerGeneral::CharacterCreated(_) => {
|
||||
c_type != ClientType::ChatOnly && presence.is_none()
|
||||
},
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::persistence::character_updater::CharacterUpdater;
|
||||
use common::comp::{
|
||||
inventory::loadout_builder::LoadoutBuilder, Body, Inventory, Item, SkillSet, Stats,
|
||||
use common::{
|
||||
character::CharacterId,
|
||||
comp::{inventory::loadout_builder::LoadoutBuilder, Body, Inventory, Item, SkillSet, Stats},
|
||||
};
|
||||
use specs::{Entity, WriteExpect};
|
||||
|
||||
@ -73,6 +74,22 @@ pub fn create_character(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn edit_character(
|
||||
entity: Entity,
|
||||
player_uuid: String,
|
||||
id: CharacterId,
|
||||
character_alias: String,
|
||||
body: Body,
|
||||
character_updater: &mut WriteExpect<'_, CharacterUpdater>,
|
||||
) -> Result<(), CreationError> {
|
||||
if !matches!(body, Body::Humanoid(_)) {
|
||||
return Err(CreationError::InvalidBody);
|
||||
}
|
||||
|
||||
character_updater.edit_character(entity, player_uuid, id, character_alias, (body,));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Error handling
|
||||
impl core::fmt::Display for CreationError {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
|
@ -94,6 +94,7 @@ impl Client {
|
||||
| ServerGeneral::CharacterListUpdate(_)
|
||||
| ServerGeneral::CharacterActionError(_)
|
||||
| ServerGeneral::CharacterCreated(_)
|
||||
| ServerGeneral::CharacterEdited(_)
|
||||
| ServerGeneral::CharacterSuccess => {
|
||||
self.character_screen_stream.lock().unwrap().send(g)
|
||||
},
|
||||
@ -165,6 +166,7 @@ impl Client {
|
||||
| ServerGeneral::CharacterListUpdate(_)
|
||||
| ServerGeneral::CharacterActionError(_)
|
||||
| ServerGeneral::CharacterCreated(_)
|
||||
| ServerGeneral::CharacterEdited(_)
|
||||
| ServerGeneral::CharacterSuccess => {
|
||||
PreparedMsg::new(1, &g, &self.character_screen_stream_params)
|
||||
},
|
||||
|
@ -813,6 +813,22 @@ impl Server {
|
||||
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) => {
|
||||
let message = match *result {
|
||||
Ok(character_data) => ServerEvent::UpdateCharacterData {
|
||||
|
@ -22,7 +22,7 @@ use crate::{
|
||||
character_loader::{CharacterCreationResult, CharacterDataResult, CharacterListResult},
|
||||
character_updater::PetPersistenceData,
|
||||
error::PersistenceError::DatabaseError,
|
||||
PersistedComponents,
|
||||
EditableComponents, PersistedComponents,
|
||||
},
|
||||
};
|
||||
use common::character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER};
|
||||
@ -277,8 +277,8 @@ pub fn load_character_list(player_uuid_: &str, connection: &Connection) -> Chara
|
||||
let mut stmt = connection.prepare_cached(
|
||||
"
|
||||
SELECT character_id,
|
||||
alias
|
||||
FROM character
|
||||
alias
|
||||
FROM character
|
||||
WHERE player_uuid = ?1
|
||||
ORDER BY character_id",
|
||||
)?;
|
||||
@ -497,6 +497,58 @@ pub fn create_character(
|
||||
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 char_list = load_character_list(uuid, transaction);
|
||||
|
||||
if let Ok(char_list) = &mut char_list {
|
||||
if let Some(char) = char_list
|
||||
.iter_mut()
|
||||
.find(|c| c.character.id == Some(character_id))
|
||||
{
|
||||
if let (crate::comp::Body::Humanoid(new), crate::comp::Body::Humanoid(old)) =
|
||||
(body, char.body)
|
||||
{
|
||||
if new.species != old.species || new.body_type != old.body_type {
|
||||
warn!(
|
||||
"Character edit rejected due to failed validation - Character ID: {} \
|
||||
Alias: {}",
|
||||
character_id, character_alias
|
||||
);
|
||||
return Err(PersistenceError::CharacterDataError);
|
||||
} else {
|
||||
char.body = body;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut stmt = transaction
|
||||
.prepare_cached("UPDATE body SET variant = ?1, body_data = ?2 WHERE body_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);
|
||||
|
||||
char_list.map(|list| (character_id, list))
|
||||
}
|
||||
|
||||
/// Delete a character. Returns the updated character list.
|
||||
pub fn delete_character(
|
||||
requesting_player_uuid: &str,
|
||||
@ -846,7 +898,7 @@ fn delete_pets(
|
||||
|
||||
#[rustfmt::skip]
|
||||
let mut stmt = transaction.prepare_cached("
|
||||
DELETE
|
||||
DELETE
|
||||
FROM body
|
||||
WHERE body_id IN rarray(?1)"
|
||||
)?;
|
||||
@ -1009,7 +1061,7 @@ pub fn update(
|
||||
|
||||
let mut stmt = transaction.prepare_cached(
|
||||
"
|
||||
REPLACE
|
||||
REPLACE
|
||||
INTO skill (entity_id,
|
||||
skill,
|
||||
level)
|
||||
|
@ -12,6 +12,7 @@ use tracing::error;
|
||||
pub(crate) type CharacterListResult = Result<Vec<CharacterItem>, PersistenceError>;
|
||||
pub(crate) type CharacterCreationResult =
|
||||
Result<(CharacterId, Vec<CharacterItem>), PersistenceError>;
|
||||
pub(crate) type CharacterEditResult = Result<(CharacterId, Vec<CharacterItem>), PersistenceError>;
|
||||
pub(crate) type CharacterDataResult = Result<PersistedComponents, PersistenceError>;
|
||||
type CharacterLoaderRequest = (specs::Entity, CharacterLoaderRequestKind);
|
||||
|
||||
@ -33,6 +34,7 @@ pub enum CharacterLoaderResponseKind {
|
||||
CharacterList(CharacterListResult),
|
||||
CharacterData(Box<CharacterDataResult>),
|
||||
CharacterCreation(CharacterCreationResult),
|
||||
CharacterEdit(CharacterEditResult),
|
||||
}
|
||||
|
||||
/// Common message format dispatched in response to an update request
|
||||
|
@ -4,10 +4,11 @@ use common::character::CharacterId;
|
||||
use crate::persistence::{
|
||||
character_loader::{CharacterLoaderResponse, CharacterLoaderResponseKind},
|
||||
error::PersistenceError,
|
||||
establish_connection, ConnectionMode, DatabaseSettings, PersistedComponents, VelorenConnection,
|
||||
establish_connection, ConnectionMode, DatabaseSettings, EditableComponents,
|
||||
PersistedComponents, VelorenConnection,
|
||||
};
|
||||
use crossbeam_channel::TryIter;
|
||||
use rusqlite::DropBehavior;
|
||||
use rusqlite::{DropBehavior, Transaction};
|
||||
use specs::Entity;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
@ -36,6 +37,13 @@ pub enum CharacterUpdaterEvent {
|
||||
character_alias: String,
|
||||
persisted_components: PersistedComponents,
|
||||
},
|
||||
EditCharacter {
|
||||
entity: Entity,
|
||||
player_uuid: String,
|
||||
character_id: CharacterId,
|
||||
character_alias: String,
|
||||
editable_components: EditableComponents,
|
||||
},
|
||||
DeleteCharacter {
|
||||
entity: Entity,
|
||||
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 edit for player {}",
|
||||
player_uuid
|
||||
);
|
||||
}
|
||||
},
|
||||
Err(e) => error!(
|
||||
"Error editing character for player {}, error: {:?}",
|
||||
player_uuid, e
|
||||
),
|
||||
}
|
||||
},
|
||||
CharacterUpdaterEvent::DeleteCharacter {
|
||||
entity,
|
||||
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(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
@ -345,22 +408,33 @@ fn execute_character_create(
|
||||
connection: &mut VelorenConnection,
|
||||
) -> Result<CharacterLoaderResponse, PersistenceError> {
|
||||
let mut transaction = connection.connection.transaction()?;
|
||||
|
||||
let response = CharacterLoaderResponse {
|
||||
entity,
|
||||
result: CharacterLoaderResponseKind::CharacterCreation(super::character::create_character(
|
||||
let result =
|
||||
CharacterLoaderResponseKind::CharacterCreation(super::character::create_character(
|
||||
requesting_player_uuid,
|
||||
&alias,
|
||||
persisted_components,
|
||||
&mut transaction,
|
||||
)),
|
||||
};
|
||||
));
|
||||
check_response(entity, transaction, result)
|
||||
}
|
||||
|
||||
if !response.is_err() {
|
||||
transaction.commit()?;
|
||||
};
|
||||
|
||||
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 result = CharacterLoaderResponseKind::CharacterEdit(super::character::edit_character(
|
||||
editable_components,
|
||||
&mut transaction,
|
||||
character_id,
|
||||
requesting_player_uuid,
|
||||
&alias,
|
||||
));
|
||||
check_response(entity, transaction, result)
|
||||
}
|
||||
|
||||
fn execute_character_delete(
|
||||
@ -370,15 +444,20 @@ fn execute_character_delete(
|
||||
connection: &mut VelorenConnection,
|
||||
) -> Result<CharacterLoaderResponse, PersistenceError> {
|
||||
let mut transaction = connection.connection.transaction()?;
|
||||
let result = CharacterLoaderResponseKind::CharacterList(super::character::delete_character(
|
||||
requesting_player_uuid,
|
||||
character_id,
|
||||
&mut transaction,
|
||||
));
|
||||
check_response(entity, transaction, result)
|
||||
}
|
||||
|
||||
let response = CharacterLoaderResponse {
|
||||
entity,
|
||||
result: CharacterLoaderResponseKind::CharacterList(super::character::delete_character(
|
||||
requesting_player_uuid,
|
||||
character_id,
|
||||
&mut transaction,
|
||||
)),
|
||||
};
|
||||
fn check_response(
|
||||
entity: Entity,
|
||||
transaction: Transaction,
|
||||
result: CharacterLoaderResponseKind,
|
||||
) -> Result<CharacterLoaderResponse, PersistenceError> {
|
||||
let response = CharacterLoaderResponse { entity, result };
|
||||
|
||||
if !response.is_err() {
|
||||
transaction.commit()?;
|
||||
|
@ -31,6 +31,8 @@ pub type PersistedComponents = (
|
||||
Vec<PetPersistenceData>,
|
||||
);
|
||||
|
||||
pub type EditableComponents = (comp::Body,);
|
||||
|
||||
// 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
|
||||
// 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 editing character because of invalid input."
|
||||
);
|
||||
client.send(ServerGeneral::CharacterActionError(error.to_string()))?;
|
||||
}
|
||||
}
|
||||
},
|
||||
ClientGeneral::DeleteCharacter(character_id) => {
|
||||
if let Some(player) = players.get(entity) {
|
||||
character_updater.delete_character(
|
||||
|
@ -283,6 +283,7 @@ impl Sys {
|
||||
},
|
||||
ClientGeneral::RequestCharacterList
|
||||
| ClientGeneral::CreateCharacter { .. }
|
||||
| ClientGeneral::EditCharacter { .. }
|
||||
| ClientGeneral::DeleteCharacter(_)
|
||||
| ClientGeneral::Character(_)
|
||||
| ClientGeneral::Spectate
|
||||
|
@ -119,6 +119,15 @@ impl PlayState for CharSelectionState {
|
||||
.borrow_mut()
|
||||
.create_character(alias, mainhand, offhand, body);
|
||||
},
|
||||
ui::Event::EditCharacter {
|
||||
alias,
|
||||
character_id,
|
||||
body,
|
||||
} => {
|
||||
self.client
|
||||
.borrow_mut()
|
||||
.edit_character(alias, character_id, body);
|
||||
},
|
||||
ui::Event::DeleteCharacter(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_press: "voxygen.element.ui.char_select.icons.bin_press",
|
||||
|
||||
edit_button: "voxygen.element.ui.char_select.icons.pen",
|
||||
edit_button_hover: "voxygen.element.ui.char_select.icons.pen_hover",
|
||||
edit_button_press: "voxygen.element.ui.char_select.icons.pen_press",
|
||||
|
||||
name_input: "voxygen.element.ui.generic.textbox",
|
||||
|
||||
// Tool Icons
|
||||
@ -129,6 +133,11 @@ pub enum Event {
|
||||
offhand: Option<String>,
|
||||
body: comp::Body,
|
||||
},
|
||||
EditCharacter {
|
||||
alias: String,
|
||||
character_id: CharacterId,
|
||||
body: comp::Body,
|
||||
},
|
||||
DeleteCharacter(CharacterId),
|
||||
ClearCharacterListError,
|
||||
SelectCharacter(Option<CharacterId>),
|
||||
@ -146,7 +155,7 @@ enum Mode {
|
||||
yes_button: button::State,
|
||||
no_button: button::State,
|
||||
},
|
||||
Create {
|
||||
CreateOrEdit {
|
||||
name: String,
|
||||
body: humanoid::Body,
|
||||
inventory: Box<comp::inventory::Inventory>,
|
||||
@ -163,6 +172,7 @@ enum Mode {
|
||||
create_button: button::State,
|
||||
rand_character_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));
|
||||
|
||||
Self::Create {
|
||||
Self::CreateOrEdit {
|
||||
name,
|
||||
body: humanoid::Body::random(),
|
||||
inventory,
|
||||
@ -210,6 +220,33 @@ impl Mode {
|
||||
create_button: Default::default(),
|
||||
rand_character_button: Default::default(),
|
||||
rand_name_button: Default::default(),
|
||||
character_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn edit(
|
||||
name: String,
|
||||
character_id: CharacterId,
|
||||
body: humanoid::Body,
|
||||
inventory: &Inventory,
|
||||
) -> Self {
|
||||
Self::CreateOrEdit {
|
||||
name,
|
||||
body,
|
||||
inventory: Box::new(inventory.clone()),
|
||||
mainhand: None,
|
||||
offhand: None,
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -219,6 +256,7 @@ enum InfoContent {
|
||||
Deletion(usize),
|
||||
LoadingCharacters,
|
||||
CreatingCharacter,
|
||||
EditingCharacter,
|
||||
DeletingCharacter,
|
||||
CharacterError(String),
|
||||
}
|
||||
@ -247,6 +285,8 @@ enum Message {
|
||||
EnterWorld,
|
||||
Select(CharacterId),
|
||||
Delete(usize),
|
||||
Edit(usize),
|
||||
ConfirmEdit(CharacterId),
|
||||
NewCharacter,
|
||||
CreateCharacter,
|
||||
Name(String),
|
||||
@ -394,6 +434,7 @@ impl Controls {
|
||||
info_content,
|
||||
Some(InfoContent::LoadingCharacters)
|
||||
| Some(InfoContent::CreatingCharacter)
|
||||
| Some(InfoContent::EditingCharacter)
|
||||
| Some(InfoContent::DeletingCharacter)
|
||||
) && !client.character_list().loading
|
||||
{
|
||||
@ -421,12 +462,13 @@ impl Controls {
|
||||
let characters = &client.character_list().characters;
|
||||
let num = characters.len();
|
||||
// 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
|
||||
let mut characters = characters
|
||||
.iter()
|
||||
.zip(character_buttons.chunks_exact_mut(2))
|
||||
.zip(character_buttons.chunks_exact_mut(CHAR_BUTTONS))
|
||||
.filter_map(|(character, buttons)| {
|
||||
let mut buttons = buttons.iter_mut();
|
||||
// TODO: eliminate option in character id?
|
||||
@ -434,13 +476,24 @@ impl Controls {
|
||||
(
|
||||
id,
|
||||
character,
|
||||
(buttons.next().unwrap(), buttons.next().unwrap()),
|
||||
(
|
||||
buttons.next().unwrap(),
|
||||
buttons.next().unwrap(),
|
||||
buttons.next().unwrap(),
|
||||
),
|
||||
)
|
||||
})
|
||||
})
|
||||
.enumerate()
|
||||
.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 {
|
||||
(255, 208, 69)
|
||||
} else {
|
||||
@ -448,17 +501,33 @@ impl Controls {
|
||||
};
|
||||
Overlay::new(
|
||||
Container::new(
|
||||
// Delete button
|
||||
Button::new(
|
||||
delete_button,
|
||||
Space::new(Length::Units(16), Length::Units(16)),
|
||||
)
|
||||
.style(
|
||||
style::button::Style::new(imgs.delete_button)
|
||||
.hover_image(imgs.delete_button_hover)
|
||||
.press_image(imgs.delete_button_press),
|
||||
)
|
||||
.on_press(Message::Delete(i)),
|
||||
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
|
||||
Button::new(
|
||||
delete_button,
|
||||
Space::new(Length::Units(16), Length::Units(16)),
|
||||
)
|
||||
.style(
|
||||
style::button::Style::new(imgs.delete_button)
|
||||
.hover_image(imgs.delete_button_hover)
|
||||
.press_image(imgs.delete_button_press),
|
||||
)
|
||||
.on_press(Message::Delete(i))
|
||||
.into(),
|
||||
])
|
||||
.spacing(5),
|
||||
)
|
||||
.padding(4),
|
||||
// Select Button
|
||||
@ -668,6 +737,11 @@ impl Controls {
|
||||
.size(fonts.cyri.scale(24))
|
||||
.into()
|
||||
},
|
||||
InfoContent::EditingCharacter => {
|
||||
Text::new(i18n.get("char_selection.editing_character"))
|
||||
.size(fonts.cyri.scale(24))
|
||||
.into()
|
||||
},
|
||||
InfoContent::DeletingCharacter => {
|
||||
Text::new(i18n.get("char_selection.deleting_character"))
|
||||
.size(fonts.cyri.scale(24))
|
||||
@ -716,7 +790,7 @@ impl Controls {
|
||||
content.into()
|
||||
}
|
||||
},
|
||||
Mode::Create {
|
||||
Mode::CreateOrEdit {
|
||||
name,
|
||||
body,
|
||||
inventory: _,
|
||||
@ -732,6 +806,7 @@ impl Controls {
|
||||
ref mut create_button,
|
||||
ref mut rand_character_button,
|
||||
ref mut rand_name_button,
|
||||
character_id,
|
||||
} => {
|
||||
let unselected_style = style::button::Style::new(imgs.icon_border)
|
||||
.hover_image(imgs.icon_border_mo)
|
||||
@ -763,190 +838,192 @@ impl Controls {
|
||||
})
|
||||
};
|
||||
|
||||
let (body_m_ico, body_f_ico) = match body.species {
|
||||
humanoid::Species::Human => (imgs.human_m, imgs.human_f),
|
||||
humanoid::Species::Orc => (imgs.orc_m, imgs.orc_f),
|
||||
humanoid::Species::Dwarf => (imgs.dwarf_m, imgs.dwarf_f),
|
||||
humanoid::Species::Elf => (imgs.elf_m, imgs.elf_f),
|
||||
humanoid::Species::Undead => (imgs.undead_m, imgs.undead_f),
|
||||
humanoid::Species::Danari => (imgs.danari_m, imgs.danari_f),
|
||||
};
|
||||
|
||||
let [ref mut body_m_button, ref mut body_f_button] = body_type_buttons;
|
||||
let body_type = Row::with_children(vec![
|
||||
icon_button(
|
||||
body_m_button,
|
||||
matches!(body.body_type, humanoid::BodyType::Male),
|
||||
Message::BodyType(humanoid::BodyType::Male),
|
||||
body_m_ico,
|
||||
)
|
||||
.into(),
|
||||
icon_button(
|
||||
body_f_button,
|
||||
matches!(body.body_type, humanoid::BodyType::Female),
|
||||
Message::BodyType(humanoid::BodyType::Female),
|
||||
body_f_ico,
|
||||
)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1);
|
||||
|
||||
let (human_icon, orc_icon, dwarf_icon, elf_icon, undead_icon, danari_icon) =
|
||||
match body.body_type {
|
||||
humanoid::BodyType::Male => (
|
||||
self.imgs.human_m,
|
||||
self.imgs.orc_m,
|
||||
self.imgs.dwarf_m,
|
||||
self.imgs.elf_m,
|
||||
self.imgs.undead_m,
|
||||
self.imgs.danari_m,
|
||||
),
|
||||
humanoid::BodyType::Female => (
|
||||
self.imgs.human_f,
|
||||
self.imgs.orc_f,
|
||||
self.imgs.dwarf_f,
|
||||
self.imgs.elf_f,
|
||||
self.imgs.undead_f,
|
||||
self.imgs.danari_f,
|
||||
),
|
||||
};
|
||||
|
||||
// TODO: tooltips
|
||||
let [
|
||||
ref mut human_button,
|
||||
ref mut orc_button,
|
||||
ref mut dwarf_button,
|
||||
ref mut elf_button,
|
||||
ref mut undead_button,
|
||||
ref mut danari_button,
|
||||
] = species_buttons;
|
||||
let species = Column::with_children(vec![
|
||||
Row::with_children(vec![
|
||||
icon_button_tooltip(
|
||||
human_button,
|
||||
matches!(body.species, humanoid::Species::Human),
|
||||
Message::Species(humanoid::Species::Human),
|
||||
human_icon,
|
||||
"common.species.human",
|
||||
let (tool, species, body_type) = if character_id.is_some() {
|
||||
(Column::new(), Column::new(), Row::new())
|
||||
} else {
|
||||
let (body_m_ico, body_f_ico) = match body.species {
|
||||
humanoid::Species::Human => (imgs.human_m, imgs.human_f),
|
||||
humanoid::Species::Orc => (imgs.orc_m, imgs.orc_f),
|
||||
humanoid::Species::Dwarf => (imgs.dwarf_m, imgs.dwarf_f),
|
||||
humanoid::Species::Elf => (imgs.elf_m, imgs.elf_f),
|
||||
humanoid::Species::Undead => (imgs.undead_m, imgs.undead_f),
|
||||
humanoid::Species::Danari => (imgs.danari_m, imgs.danari_f),
|
||||
};
|
||||
let [ref mut body_m_button, ref mut body_f_button] = body_type_buttons;
|
||||
let body_type = Row::with_children(vec![
|
||||
icon_button(
|
||||
body_m_button,
|
||||
matches!(body.body_type, humanoid::BodyType::Male),
|
||||
Message::BodyType(humanoid::BodyType::Male),
|
||||
body_m_ico,
|
||||
)
|
||||
.into(),
|
||||
icon_button_tooltip(
|
||||
orc_button,
|
||||
matches!(body.species, humanoid::Species::Orc),
|
||||
Message::Species(humanoid::Species::Orc),
|
||||
orc_icon,
|
||||
"common.species.orc",
|
||||
)
|
||||
.into(),
|
||||
icon_button_tooltip(
|
||||
dwarf_button,
|
||||
matches!(body.species, humanoid::Species::Dwarf),
|
||||
Message::Species(humanoid::Species::Dwarf),
|
||||
dwarf_icon,
|
||||
"common.species.dwarf",
|
||||
icon_button(
|
||||
body_f_button,
|
||||
matches!(body.body_type, humanoid::BodyType::Female),
|
||||
Message::BodyType(humanoid::BodyType::Female),
|
||||
body_f_ico,
|
||||
)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1)
|
||||
.into(),
|
||||
Row::with_children(vec![
|
||||
icon_button_tooltip(
|
||||
elf_button,
|
||||
matches!(body.species, humanoid::Species::Elf),
|
||||
Message::Species(humanoid::Species::Elf),
|
||||
elf_icon,
|
||||
"common.species.elf",
|
||||
)
|
||||
.spacing(1);
|
||||
let (human_icon, orc_icon, dwarf_icon, elf_icon, undead_icon, danari_icon) =
|
||||
match body.body_type {
|
||||
humanoid::BodyType::Male => (
|
||||
self.imgs.human_m,
|
||||
self.imgs.orc_m,
|
||||
self.imgs.dwarf_m,
|
||||
self.imgs.elf_m,
|
||||
self.imgs.undead_m,
|
||||
self.imgs.danari_m,
|
||||
),
|
||||
humanoid::BodyType::Female => (
|
||||
self.imgs.human_f,
|
||||
self.imgs.orc_f,
|
||||
self.imgs.dwarf_f,
|
||||
self.imgs.elf_f,
|
||||
self.imgs.undead_f,
|
||||
self.imgs.danari_f,
|
||||
),
|
||||
};
|
||||
let [
|
||||
ref mut human_button,
|
||||
ref mut orc_button,
|
||||
ref mut dwarf_button,
|
||||
ref mut elf_button,
|
||||
ref mut undead_button,
|
||||
ref mut danari_button,
|
||||
] = species_buttons;
|
||||
let species = Column::with_children(vec![
|
||||
Row::with_children(vec![
|
||||
icon_button_tooltip(
|
||||
human_button,
|
||||
matches!(body.species, humanoid::Species::Human),
|
||||
Message::Species(humanoid::Species::Human),
|
||||
human_icon,
|
||||
"common.species.human",
|
||||
)
|
||||
.into(),
|
||||
icon_button_tooltip(
|
||||
orc_button,
|
||||
matches!(body.species, humanoid::Species::Orc),
|
||||
Message::Species(humanoid::Species::Orc),
|
||||
orc_icon,
|
||||
"common.species.orc",
|
||||
)
|
||||
.into(),
|
||||
icon_button_tooltip(
|
||||
dwarf_button,
|
||||
matches!(body.species, humanoid::Species::Dwarf),
|
||||
Message::Species(humanoid::Species::Dwarf),
|
||||
dwarf_icon,
|
||||
"common.species.dwarf",
|
||||
)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1)
|
||||
.into(),
|
||||
icon_button_tooltip(
|
||||
undead_button,
|
||||
matches!(body.species, humanoid::Species::Undead),
|
||||
Message::Species(humanoid::Species::Undead),
|
||||
undead_icon,
|
||||
"common.species.undead",
|
||||
)
|
||||
.into(),
|
||||
icon_button_tooltip(
|
||||
danari_button,
|
||||
matches!(body.species, humanoid::Species::Danari),
|
||||
Message::Species(humanoid::Species::Danari),
|
||||
danari_icon,
|
||||
"common.species.danari",
|
||||
)
|
||||
Row::with_children(vec![
|
||||
icon_button_tooltip(
|
||||
elf_button,
|
||||
matches!(body.species, humanoid::Species::Elf),
|
||||
Message::Species(humanoid::Species::Elf),
|
||||
elf_icon,
|
||||
"common.species.elf",
|
||||
)
|
||||
.into(),
|
||||
icon_button_tooltip(
|
||||
undead_button,
|
||||
matches!(body.species, humanoid::Species::Undead),
|
||||
Message::Species(humanoid::Species::Undead),
|
||||
undead_icon,
|
||||
"common.species.undead",
|
||||
)
|
||||
.into(),
|
||||
icon_button_tooltip(
|
||||
danari_button,
|
||||
matches!(body.species, humanoid::Species::Danari),
|
||||
Message::Species(humanoid::Species::Danari),
|
||||
danari_icon,
|
||||
"common.species.danari",
|
||||
)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1);
|
||||
.spacing(1);
|
||||
let [
|
||||
ref mut sword_button,
|
||||
ref mut swords_button,
|
||||
ref mut axe_button,
|
||||
ref mut hammer_button,
|
||||
ref mut bow_button,
|
||||
ref mut staff_button,
|
||||
] = tool_buttons;
|
||||
let tool = Column::with_children(vec![
|
||||
Row::with_children(vec![
|
||||
icon_button_tooltip(
|
||||
sword_button,
|
||||
*mainhand == Some(STARTER_SWORD),
|
||||
Message::Tool((Some(STARTER_SWORD), None)),
|
||||
imgs.sword,
|
||||
"common.weapons.greatsword",
|
||||
)
|
||||
.into(),
|
||||
icon_button_tooltip(
|
||||
hammer_button,
|
||||
*mainhand == Some(STARTER_HAMMER),
|
||||
Message::Tool((Some(STARTER_HAMMER), None)),
|
||||
imgs.hammer,
|
||||
"common.weapons.hammer",
|
||||
)
|
||||
.into(),
|
||||
icon_button_tooltip(
|
||||
axe_button,
|
||||
*mainhand == Some(STARTER_AXE),
|
||||
Message::Tool((Some(STARTER_AXE), None)),
|
||||
imgs.axe,
|
||||
"common.weapons.axe",
|
||||
)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1)
|
||||
.into(),
|
||||
Row::with_children(vec![
|
||||
icon_button_tooltip(
|
||||
swords_button,
|
||||
*mainhand == Some(STARTER_SWORDS),
|
||||
Message::Tool((Some(STARTER_SWORDS), Some(STARTER_SWORDS))),
|
||||
imgs.swords,
|
||||
"common.weapons.shortswords",
|
||||
)
|
||||
.into(),
|
||||
icon_button_tooltip(
|
||||
bow_button,
|
||||
*mainhand == Some(STARTER_BOW),
|
||||
Message::Tool((Some(STARTER_BOW), None)),
|
||||
imgs.bow,
|
||||
"common.weapons.bow",
|
||||
)
|
||||
.into(),
|
||||
icon_button_tooltip(
|
||||
staff_button,
|
||||
*mainhand == Some(STARTER_STAFF),
|
||||
Message::Tool((Some(STARTER_STAFF), None)),
|
||||
imgs.staff,
|
||||
"common.weapons.staff",
|
||||
)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1);
|
||||
|
||||
let [
|
||||
ref mut sword_button,
|
||||
ref mut swords_button,
|
||||
ref mut axe_button,
|
||||
ref mut hammer_button,
|
||||
ref mut bow_button,
|
||||
ref mut staff_button,
|
||||
] = tool_buttons;
|
||||
let tool = Column::with_children(vec![
|
||||
Row::with_children(vec![
|
||||
icon_button_tooltip(
|
||||
sword_button,
|
||||
*mainhand == Some(STARTER_SWORD),
|
||||
Message::Tool((Some(STARTER_SWORD), None)),
|
||||
imgs.sword,
|
||||
"common.weapons.greatsword",
|
||||
)
|
||||
.into(),
|
||||
icon_button_tooltip(
|
||||
hammer_button,
|
||||
*mainhand == Some(STARTER_HAMMER),
|
||||
Message::Tool((Some(STARTER_HAMMER), None)),
|
||||
imgs.hammer,
|
||||
"common.weapons.hammer",
|
||||
)
|
||||
.into(),
|
||||
icon_button_tooltip(
|
||||
axe_button,
|
||||
*mainhand == Some(STARTER_AXE),
|
||||
Message::Tool((Some(STARTER_AXE), None)),
|
||||
imgs.axe,
|
||||
"common.weapons.axe",
|
||||
)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1)
|
||||
.into(),
|
||||
Row::with_children(vec![
|
||||
icon_button_tooltip(
|
||||
swords_button,
|
||||
*mainhand == Some(STARTER_SWORDS),
|
||||
Message::Tool((Some(STARTER_SWORDS), Some(STARTER_SWORDS))),
|
||||
imgs.swords,
|
||||
"common.weapons.shortswords",
|
||||
)
|
||||
.into(),
|
||||
icon_button_tooltip(
|
||||
bow_button,
|
||||
*mainhand == Some(STARTER_BOW),
|
||||
Message::Tool((Some(STARTER_BOW), None)),
|
||||
imgs.bow,
|
||||
"common.weapons.bow",
|
||||
)
|
||||
.into(),
|
||||
icon_button_tooltip(
|
||||
staff_button,
|
||||
*mainhand == Some(STARTER_STAFF),
|
||||
Message::Tool((Some(STARTER_STAFF), None)),
|
||||
imgs.staff,
|
||||
"common.weapons.staff",
|
||||
)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1);
|
||||
(tool, species, body_type)
|
||||
};
|
||||
|
||||
const SLIDER_TEXT_SIZE: u16 = 20;
|
||||
const SLIDER_CURSOR_SIZE: (u16, u16) = (9, 21);
|
||||
@ -1174,6 +1251,12 @@ impl Controls {
|
||||
tooltip::text(i18n.get("common.rand_name"), tooltip_style)
|
||||
});
|
||||
|
||||
let confirm_msg = if let Some(character_id) = character_id {
|
||||
Message::ConfirmEdit(*character_id)
|
||||
} else {
|
||||
Message::CreateCharacter
|
||||
};
|
||||
|
||||
let name_input = BackgroundContainer::new(
|
||||
Image::new(imgs.name_input)
|
||||
.height(Length::Units(40))
|
||||
@ -1185,7 +1268,7 @@ impl Controls {
|
||||
Message::Name,
|
||||
)
|
||||
.size(25)
|
||||
.on_submit(Message::CreateCharacter),
|
||||
.on_submit(confirm_msg.clone()),
|
||||
)
|
||||
.padding(Padding::new().horizontal(7).top(5));
|
||||
|
||||
@ -1204,10 +1287,14 @@ impl Controls {
|
||||
|
||||
let create = neat_button(
|
||||
create_button,
|
||||
i18n.get("common.create"),
|
||||
i18n.get(if character_id.is_some() {
|
||||
"common.confirm"
|
||||
} else {
|
||||
"common.create"
|
||||
}),
|
||||
FILL_FRAC_ONE,
|
||||
button_style,
|
||||
(!name.is_empty()).then_some(Message::CreateCharacter),
|
||||
(!name.is_empty()).then_some(confirm_msg),
|
||||
);
|
||||
|
||||
let create: Element<Message> = if name.is_empty() {
|
||||
@ -1293,7 +1380,7 @@ impl Controls {
|
||||
fn update(&mut self, message: Message, events: &mut Vec<Event>, characters: &[CharacterItem]) {
|
||||
match message {
|
||||
Message::Back => {
|
||||
if matches!(&self.mode, Mode::Create { .. }) {
|
||||
if matches!(&self.mode, Mode::CreateOrEdit { .. }) {
|
||||
self.mode = Mode::select(None);
|
||||
}
|
||||
},
|
||||
@ -1316,13 +1403,29 @@ impl Controls {
|
||||
*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,
|
||||
&character.inventory,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Message::NewCharacter => {
|
||||
if matches!(&self.mode, Mode::Select { .. }) {
|
||||
self.mode = Mode::create(self.default_name.clone());
|
||||
}
|
||||
},
|
||||
Message::CreateCharacter => {
|
||||
if let Mode::Create {
|
||||
if let Mode::CreateOrEdit {
|
||||
name,
|
||||
body,
|
||||
mainhand,
|
||||
@ -1339,25 +1442,35 @@ impl Controls {
|
||||
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::EditingCharacter));
|
||||
}
|
||||
},
|
||||
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();
|
||||
}
|
||||
},
|
||||
Message::BodyType(value) => {
|
||||
if let Mode::Create { body, .. } = &mut self.mode {
|
||||
if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
|
||||
body.body_type = value;
|
||||
body.validate();
|
||||
}
|
||||
},
|
||||
Message::Species(value) => {
|
||||
if let Mode::Create { body, .. } = &mut self.mode {
|
||||
if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
|
||||
body.species = value;
|
||||
body.validate();
|
||||
}
|
||||
},
|
||||
Message::Tool(value) => {
|
||||
if let Mode::Create {
|
||||
if let Mode::CreateOrEdit {
|
||||
mainhand,
|
||||
offhand,
|
||||
inventory,
|
||||
@ -1378,7 +1491,7 @@ impl Controls {
|
||||
},
|
||||
//Todo: Add species and body type to randomization.
|
||||
Message::RandomizeCharacter => {
|
||||
if let Mode::Create { body, .. } = &mut self.mode {
|
||||
if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
|
||||
use rand::Rng;
|
||||
let body_type = body.body_type;
|
||||
let species = body.species;
|
||||
@ -1394,7 +1507,7 @@ impl Controls {
|
||||
},
|
||||
|
||||
Message::RandomizeName => {
|
||||
if let Mode::Create { name, body, .. } = &mut self.mode {
|
||||
if let Mode::CreateOrEdit { name, body, .. } = &mut self.mode {
|
||||
use common::npc;
|
||||
*name = npc::get_npc_name(
|
||||
npc::NpcKind::Humanoid,
|
||||
@ -1429,43 +1542,43 @@ impl Controls {
|
||||
events.push(Event::ClearCharacterListError);
|
||||
},
|
||||
Message::HairStyle(value) => {
|
||||
if let Mode::Create { body, .. } = &mut self.mode {
|
||||
if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
|
||||
body.hair_style = value;
|
||||
body.validate();
|
||||
}
|
||||
},
|
||||
Message::HairColor(value) => {
|
||||
if let Mode::Create { body, .. } = &mut self.mode {
|
||||
if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
|
||||
body.hair_color = value;
|
||||
body.validate();
|
||||
}
|
||||
},
|
||||
Message::Skin(value) => {
|
||||
if let Mode::Create { body, .. } = &mut self.mode {
|
||||
if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
|
||||
body.skin = value;
|
||||
body.validate();
|
||||
}
|
||||
},
|
||||
Message::Eyes(value) => {
|
||||
if let Mode::Create { body, .. } = &mut self.mode {
|
||||
if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
|
||||
body.eyes = value;
|
||||
body.validate();
|
||||
}
|
||||
},
|
||||
Message::EyeColor(value) => {
|
||||
if let Mode::Create { body, .. } = &mut self.mode {
|
||||
if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
|
||||
body.eye_color = value;
|
||||
body.validate();
|
||||
}
|
||||
},
|
||||
Message::Accessory(value) => {
|
||||
if let Mode::Create { body, .. } = &mut self.mode {
|
||||
if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
|
||||
body.accessory = value;
|
||||
body.validate();
|
||||
}
|
||||
},
|
||||
Message::Beard(value) => {
|
||||
if let Mode::Create { body, .. } = &mut self.mode {
|
||||
if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
|
||||
body.beard = value;
|
||||
body.validate();
|
||||
}
|
||||
@ -1484,7 +1597,7 @@ impl Controls {
|
||||
.selected
|
||||
.and_then(|id| characters.iter().find(|i| i.character.id == Some(id)))
|
||||
.map(|i| (i.body, &i.inventory)),
|
||||
Mode::Create {
|
||||
Mode::CreateOrEdit {
|
||||
inventory, body, ..
|
||||
} => Some((comp::Body::Humanoid(*body), inventory)),
|
||||
}
|
||||
|
@ -301,6 +301,7 @@ impl SessionState {
|
||||
},
|
||||
client::Event::Outcome(outcome) => outcomes.push(outcome),
|
||||
client::Event::CharacterCreated(_) => {},
|
||||
client::Event::CharacterEdited(_) => {},
|
||||
client::Event::CharacterError(error) => {
|
||||
global_state.client_error = Some(error);
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user