Addressed MR comments.

This commit is contained in:
Sam 2022-01-14 12:45:54 -05:00
parent 4d3b0736d0
commit 7ae8ed09f6
16 changed files with 194 additions and 187 deletions

View File

@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Slashing damage now reduces target's energy by an amount equal to damage dealt to target post-mitigation - Slashing damage now reduces target's energy by an amount equal to damage dealt to target post-mitigation
- Crushing damage now does poise damage to a target equal to the amount mitigated by armor - Crushing damage now does poise damage to a target equal to the amount mitigated by armor
- UI to select abilities and assign to hotbar - UI to select abilities and assign to hotbar
- Position of abilities on hotbar is now persisted through the server
### Changed ### Changed

View File

@ -31,7 +31,7 @@ macro_rules! dev_panic {
if cfg!(any(debug_assertions, test)) { if cfg!(any(debug_assertions, test)) {
panic!("{}", $msg); panic!("{}", $msg);
} else { } else {
tracing::warn!("{}", $msg); tracing::error!("{}", $msg);
} }
}; };

View File

@ -209,7 +209,7 @@ impl ActiveAbilities {
}) })
} }
pub fn default_ability_set<'a>( fn default_ability_set<'a>(
inv: Option<&'a Inventory>, inv: Option<&'a Inventory>,
skill_set: Option<&'a SkillSet>, skill_set: Option<&'a SkillSet>,
) -> [AuxiliaryAbility; MAX_ABILITIES] { ) -> [AuxiliaryAbility; MAX_ABILITIES] {
@ -220,15 +220,7 @@ impl ActiveAbilities {
.map(AuxiliaryAbility::OffWeapon), .map(AuxiliaryAbility::OffWeapon),
); );
let mut array = [AuxiliaryAbility::Empty; MAX_ABILITIES]; [(); MAX_ABILITIES].map(|()| iter.next().unwrap_or(AuxiliaryAbility::Empty))
for ability in array.iter_mut() {
if let Some(available_ability) = iter.next() {
*ability = available_ability;
}
}
array
} }
} }

View File

@ -1,4 +1,4 @@
use crate::persistence::character_updater::CharacterUpdater; use crate::persistence::{character_updater::CharacterUpdater, PersistedComponents};
use common::{ use common::{
character::CharacterId, character::CharacterId,
comp::{inventory::loadout_builder::LoadoutBuilder, Body, Inventory, Item, SkillSet, Stats}, comp::{inventory::loadout_builder::LoadoutBuilder, Body, Inventory, Item, SkillSet, Stats},
@ -65,20 +65,15 @@ pub fn create_character(
let waypoint = None; let waypoint = None;
character_updater.create_character( character_updater.create_character(entity, player_uuid, character_alias, PersistedComponents {
entity,
player_uuid,
character_alias,
(
body, body,
stats, stats,
skill_set, skill_set,
inventory, inventory,
waypoint, waypoint,
Vec::new(), pets: Vec::new(),
Default::default(), active_abilities: Default::default(),
), });
);
Ok(()) Ok(())
} }

View File

@ -1,4 +1,4 @@
use crate::{client::Client, sys, Server, StateExt}; use crate::{client::Client, persistence::PersistedComponents, sys, Server, StateExt};
use common::{ use common::{
character::CharacterId, character::CharacterId,
comp::{ comp::{
@ -35,15 +35,7 @@ pub fn handle_initialize_character(
pub fn handle_loaded_character_data( pub fn handle_loaded_character_data(
server: &mut Server, server: &mut Server,
entity: EcsEntity, entity: EcsEntity,
loaded_components: ( loaded_components: PersistedComponents,
comp::Body,
comp::Stats,
comp::SkillSet,
comp::Inventory,
Option<comp::Waypoint>,
Vec<(comp::Pet, comp::Body, comp::Stats)>,
comp::ActiveAbilities,
),
) { ) {
server server
.state .state

View File

@ -1,4 +1,7 @@
use crate::{events::interaction::handle_tame_pet, state_ext::StateExt, Server}; use crate::{
events::interaction::handle_tame_pet, persistence::PersistedComponents, state_ext::StateExt,
Server,
};
use common::event::{EventBus, ServerEvent}; use common::event::{EventBus, ServerEvent};
use common_base::span; use common_base::span;
use entity_creation::{ use entity_creation::{
@ -129,6 +132,17 @@ impl Server {
character_id, character_id,
} => handle_initialize_character(self, entity, character_id), } => handle_initialize_character(self, entity, character_id),
ServerEvent::UpdateCharacterData { entity, components } => { ServerEvent::UpdateCharacterData { entity, components } => {
let (body, stats, skill_set, inventory, waypoint, pets, active_abilities) =
components;
let components = PersistedComponents {
body,
stats,
skill_set,
inventory,
waypoint,
pets,
active_abilities,
};
handle_loaded_character_data(self, entity, components); handle_loaded_character_data(self, entity, components);
}, },
ServerEvent::ExitIngame { entity } => { ServerEvent::ExitIngame { entity } => {

View File

@ -55,6 +55,7 @@ use crate::{
connection_handler::ConnectionHandler, connection_handler::ConnectionHandler,
data_dir::DataDir, data_dir::DataDir,
login_provider::LoginProvider, login_provider::LoginProvider,
persistence::PersistedComponents,
presence::{Presence, RegionSubscription, RepositionOnChunkLoad}, presence::{Presence, RegionSubscription, RepositionOnChunkLoad},
rtsim::RtSim, rtsim::RtSim,
state_ext::StateExt, state_ext::StateExt,
@ -844,9 +845,29 @@ impl Server {
}, },
CharacterLoaderResponseKind::CharacterData(result) => { CharacterLoaderResponseKind::CharacterData(result) => {
let message = match *result { let message = match *result {
Ok(character_data) => ServerEvent::UpdateCharacterData { Ok(character_data) => {
let PersistedComponents {
body,
stats,
skill_set,
inventory,
waypoint,
pets,
active_abilities,
} = character_data;
let character_data = (
body,
stats,
skill_set,
inventory,
waypoint,
pets,
active_abilities,
);
ServerEvent::UpdateCharacterData {
entity: query_result.entity, entity: query_result.entity,
components: character_data, components: character_data,
}
}, },
Err(error) => { Err(error) => {
// We failed to load data for the character from the DB. Notify the // We failed to load data for the character from the DB. Notify the

View File

@ -248,20 +248,20 @@ pub fn load_character_data(
}) })
})?; })?;
Ok(( Ok(PersistedComponents {
convert_body_from_database(&body_data.variant, &body_data.body_data)?, body: convert_body_from_database(&body_data.variant, &body_data.body_data)?,
convert_stats_from_database(character_data.alias), stats: convert_stats_from_database(character_data.alias),
convert_skill_set_from_database(&skill_group_data), skill_set: convert_skill_set_from_database(&skill_group_data),
convert_inventory_from_database_items( inventory: convert_inventory_from_database_items(
character_containers.inventory_container_id, character_containers.inventory_container_id,
&inventory_items, &inventory_items,
character_containers.loadout_container_id, character_containers.loadout_container_id,
&loadout_items, &loadout_items,
)?, )?,
char_waypoint, waypoint: char_waypoint,
pets, pets,
convert_active_abilities_from_database(&ability_set_data), active_abilities: convert_active_abilities_from_database(&ability_set_data),
)) })
} }
/// Loads a list of characters belonging to the player. This data is a small /// Loads a list of characters belonging to the player. This data is a small
@ -346,7 +346,15 @@ pub fn create_character(
) -> CharacterCreationResult { ) -> CharacterCreationResult {
check_character_limit(uuid, transaction)?; check_character_limit(uuid, transaction)?;
let (body, _stats, skill_set, inventory, waypoint, _, active_abilities) = persisted_components; let PersistedComponents {
body,
stats: _,
skill_set,
inventory,
waypoint,
pets: _,
active_abilities,
} = persisted_components;
// Fetch new entity IDs for character, inventory and loadout // Fetch new entity IDs for character, inventory and loadout
let mut new_entity_ids = get_new_entity_ids(transaction, |next_id| next_id + 3)?; let mut new_entity_ids = get_new_entity_ids(transaction, |next_id| next_id + 3)?;

View File

@ -125,36 +125,51 @@ fn aux_ability_to_string(ability: common::comp::ability::AuxiliaryAbility) -> St
} }
} }
fn aux_ability_from_string(ability: String) -> common::comp::ability::AuxiliaryAbility { fn aux_ability_from_string(ability: &str) -> common::comp::ability::AuxiliaryAbility {
use common::comp::ability::AuxiliaryAbility; use common::comp::ability::AuxiliaryAbility;
let parts = ability let mut parts = ability.split(":index:");
.split(":index:") match parts.next() {
.map(String::from) Some("Main Weapon") => match parts
.collect::<Vec<_>>(); .next()
match parts.get(0).map(|s| s.as_str()) { .map(|index| index.parse::<usize>().map_err(|_| index))
Some("Main Weapon") => { {
if let Some(index) = parts.get(1).and_then(|index| index.parse::<usize>().ok()) { Some(Ok(index)) => AuxiliaryAbility::MainWeapon(index),
AuxiliaryAbility::MainWeapon(index) Some(Err(error)) => {
} else {
dev_panic!(format!( dev_panic!(format!(
"Converstion from databse to ability set failed. Unable to parse index for \ "Conversion from database to ability set failed. Unable to parse index for \
mainhand abilities: {:#?}", mainhand abilities: {}",
parts.get(1) error
)); ));
AuxiliaryAbility::Empty AuxiliaryAbility::Empty
}
}, },
Some("Off Weapon") => { None => {
if let Some(index) = parts.get(1).and_then(|index| index.parse::<usize>().ok()) { dev_panic!(String::from(
AuxiliaryAbility::OffWeapon(index) "Conversion from database to ability set failed. Unable to find an index for \
} else { mainhand abilities"
dev_panic!(format!(
"Converstion from databse to ability set failed. Unable to parse index for \
offhand abilities: {:#?}",
parts.get(1)
)); ));
AuxiliaryAbility::Empty AuxiliaryAbility::Empty
} },
},
Some("Off Weapon") => match parts
.next()
.map(|index| index.parse::<usize>().map_err(|_| index))
{
Some(Ok(index)) => AuxiliaryAbility::OffWeapon(index),
Some(Err(error)) => {
dev_panic!(format!(
"Conversion from database to ability set failed. Unable to parse index for \
offhand abilities: {}",
error
));
AuxiliaryAbility::Empty
},
None => {
dev_panic!(String::from(
"Conversion from database to ability set failed. Unable to find an index for \
offhand abilities"
));
AuxiliaryAbility::Empty
},
}, },
Some("Empty") => AuxiliaryAbility::Empty, Some("Empty") => AuxiliaryAbility::Empty,
unknown => { unknown => {
@ -182,6 +197,7 @@ fn tool_kind_to_string(tool: Option<common::comp::item::tool::ToolKind>) -> Stri
Some(Blowgun) => "Blowgun", Some(Blowgun) => "Blowgun",
Some(Pick) => "Pick", Some(Pick) => "Pick",
// Toolkinds that are not anticipated to have many active aiblities (if any at all)
Some(Farming) => "Farming", Some(Farming) => "Farming",
Some(Debug) => "Debug", Some(Debug) => "Debug",
Some(Natural) => "Natural", Some(Natural) => "Natural",
@ -251,7 +267,7 @@ pub fn active_abilities_from_db_model(
let mut auxiliary_abilities = let mut auxiliary_abilities =
[comp::ability::AuxiliaryAbility::Empty; comp::ability::MAX_ABILITIES]; [comp::ability::AuxiliaryAbility::Empty; comp::ability::MAX_ABILITIES];
for (empty, ability) in auxiliary_abilities.iter_mut().zip(abilities.into_iter()) { for (empty, ability) in auxiliary_abilities.iter_mut().zip(abilities.into_iter()) {
*empty = aux_ability_from_string(ability); *empty = aux_ability_from_string(&ability);
} }
( (
( (

View File

@ -21,16 +21,17 @@ use std::{
}; };
use tracing::info; use tracing::info;
/// A tuple of the components that are persisted to the DB for each character /// A struct of the components that are persisted to the DB for each character
pub type PersistedComponents = ( #[derive(Debug)]
comp::Body, pub struct PersistedComponents {
comp::Stats, pub body: comp::Body,
comp::SkillSet, pub stats: comp::Stats,
comp::Inventory, pub skill_set: comp::SkillSet,
Option<comp::Waypoint>, pub inventory: comp::Inventory,
Vec<PetPersistenceData>, pub waypoint: Option<comp::Waypoint>,
comp::ActiveAbilities, pub pets: Vec<PetPersistenceData>,
); pub active_abilities: comp::ActiveAbilities,
}
pub type EditableComponents = (comp::Body,); pub type EditableComponents = (comp::Body,);

View File

@ -238,7 +238,6 @@ impl StateExt for State {
.unwrap_or(0), .unwrap_or(0),
)) ))
.with(stats) .with(stats)
// TODO: Figure out way to have this start with sane defaults
.with(comp::ActiveAbilities::default()) .with(comp::ActiveAbilities::default())
.with(skill_set) .with(skill_set)
.maybe_with(health) .maybe_with(health)
@ -498,7 +497,15 @@ impl StateExt for State {
} }
fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents) { fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents) {
let (body, stats, skill_set, inventory, waypoint, pets, active_abilities) = components; let PersistedComponents {
body,
stats,
skill_set,
inventory,
waypoint,
pets,
active_abilities,
} = components;
if let Some(player_uid) = self.read_component_copied::<Uid>(entity) { if let Some(player_uid) = self.read_component_copied::<Uid>(entity) {
// Notify clients of a player list update // Notify clients of a player list update

View File

@ -48,7 +48,6 @@ use common::{
}, },
consts::{ENERGY_PER_LEVEL, HP_PER_LEVEL}, consts::{ENERGY_PER_LEVEL, HP_PER_LEVEL},
}; };
use inline_tweak::*;
use std::borrow::Cow; use std::borrow::Cow;
const ART_SIZE: [f64; 2] = [320.0, 320.0]; const ART_SIZE: [f64; 2] = [320.0, 320.0];
@ -782,12 +781,12 @@ impl<'a> Widget for Diary<'a> {
// Background Art // Background Art
Image::new(self.imgs.book_bg) Image::new(self.imgs.book_bg)
.w_h(299.0 * 4.0, 184.0 * 4.0) .w_h(299.0 * 4.0, 184.0 * 4.0)
.mid_top_with_margin_on(state.ids.content_align, tweak!(4.0)) .mid_top_with_margin_on(state.ids.content_align, 4.0)
//.graphics_for(state.ids.content_align) //.graphics_for(state.ids.content_align)
.set(state.ids.spellbook_art, ui); .set(state.ids.spellbook_art, ui);
Image::new(self.imgs.skills_bg) Image::new(self.imgs.skills_bg)
.w_h(240.0 * 2.0, 40.0 * 2.0) .w_h(240.0 * 2.0, 40.0 * 2.0)
.mid_bottom_with_margin_on(state.ids.content_align, tweak!(8.0)) .mid_bottom_with_margin_on(state.ids.content_align, 8.0)
.set(state.ids.spellbook_skills_bg, ui); .set(state.ids.spellbook_skills_bg, ui);
Rectangle::fill_with([299.0 * 2.0, 184.0 * 4.0], color::TRANSPARENT) Rectangle::fill_with([299.0 * 2.0, 184.0 * 4.0], color::TRANSPARENT)
@ -816,7 +815,7 @@ impl<'a> Widget for Diary<'a> {
background_color: Some(UI_MAIN), background_color: Some(UI_MAIN),
content_size: ContentSize { content_size: ContentSize {
width_height_ratio: 1.0, width_height_ratio: 1.0,
max_fraction: tweak!(0.9), max_fraction: 0.9,
}, },
selected_content_scale: 1.067, selected_content_scale: 1.067,
amount_font: self.fonts.cyri.conrod_id, amount_font: self.fonts.cyri.conrod_id,
@ -838,11 +837,14 @@ impl<'a> Widget for Diary<'a> {
Some(self.skill_set), Some(self.skill_set),
) )
.ability_id(Some(self.inventory)); .ability_id(Some(self.inventory));
let (ability_title, ability_desc) = let (ability_title, ability_desc) = if let Some(ability_id) = ability_id {
util::ability_description(ability_id.unwrap_or("")); util::ability_description(ability_id)
} else {
("Drag an ability here to use it.", "")
};
let image_size = tweak!(80.0); let image_size = 80.0;
let image_offsets = tweak!(92.0) * i as f64; let image_offsets = 92.0 * i as f64;
let slot = AbilitySlot::Slot(i); let slot = AbilitySlot::Slot(i);
let mut ability_slot = slot_maker.fabricate(slot, [image_size; 2]); let mut ability_slot = slot_maker.fabricate(slot, [image_size; 2]);
@ -850,8 +852,8 @@ impl<'a> Widget for Diary<'a> {
if i == 0 { if i == 0 {
ability_slot = ability_slot.top_left_with_margins_on( ability_slot = ability_slot.top_left_with_margins_on(
state.ids.spellbook_skills_bg, state.ids.spellbook_skills_bg,
tweak!(0.0), 0.0,
tweak!(32.0) + image_offsets, 32.0 + image_offsets,
); );
} else { } else {
ability_slot = ability_slot =
@ -870,52 +872,22 @@ impl<'a> Widget for Diary<'a> {
// Display Slot Keybinding // Display Slot Keybinding
let keys = &self.global_state.settings.controls; let keys = &self.global_state.settings.controls;
let key_layout = &self.global_state.window.key_layout; let key_layout = &self.global_state.window.key_layout;
let ability_key: String = match i { let ability_key = [
0 => { GameInput::Slot1,
if let Some(key) = keys.get_binding(GameInput::Slot1) { GameInput::Slot2,
key.display_string(key_layout) GameInput::Slot3,
} else { GameInput::Slot4,
String::new() GameInput::Slot5,
} ]
}, .get(i)
1 => { .and_then(|input| keys.get_binding(*input))
if let Some(key) = keys.get_binding(GameInput::Slot2) { .map(|key| key.display_string(key_layout))
key.display_string(key_layout) .unwrap_or_default();
} else {
String::new()
}
},
2 => {
if let Some(key) = keys.get_binding(GameInput::Slot3) {
key.display_string(key_layout)
} else {
String::new()
}
},
3 => {
if let Some(key) = keys.get_binding(GameInput::Slot4) {
key.display_string(key_layout)
} else {
String::new()
}
},
4 => {
if let Some(key) = keys.get_binding(GameInput::Slot5) {
key.display_string(key_layout)
} else {
String::new()
}
},
_ => String::new(),
};
Text::new(&ability_key) Text::new(&ability_key)
.top_left_with_margins_on( .top_left_with_margins_on(state.ids.active_abilities[i], 0.0, 4.0)
state.ids.active_abilities[i],
tweak!(0.0),
tweak!(4.0),
)
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(tweak!(20))) .font_size(self.fonts.cyri.scale(20))
.color(TEXT_COLOR) .color(TEXT_COLOR)
.graphics_for(state.ids.active_abilities[i]) .graphics_for(state.ids.active_abilities[i])
.set(state.ids.active_abilities_keys[i], ui); .set(state.ids.active_abilities_keys[i], ui);
@ -964,31 +936,30 @@ impl<'a> Widget for Diary<'a> {
state.update(|s| s.ability_page = 0); state.update(|s| s.ability_page = 0);
} }
let update_length = 12;
state.update(|s| { state.update(|s| {
s.ids s.ids
.abilities .abilities
.resize(update_length, &mut ui.widget_id_generator()) .resize(ABILITIES_PER_PAGE, &mut ui.widget_id_generator())
}); });
state.update(|s| { state.update(|s| {
s.ids s.ids
.abilities_dual .abilities_dual
.resize(update_length, &mut ui.widget_id_generator()) .resize(ABILITIES_PER_PAGE, &mut ui.widget_id_generator())
}); });
state.update(|s| { state.update(|s| {
s.ids s.ids
.ability_titles .ability_titles
.resize(update_length, &mut ui.widget_id_generator()) .resize(ABILITIES_PER_PAGE, &mut ui.widget_id_generator())
}); });
state.update(|s| { state.update(|s| {
s.ids s.ids
.ability_frames .ability_frames
.resize(update_length, &mut ui.widget_id_generator()) .resize(ABILITIES_PER_PAGE, &mut ui.widget_id_generator())
}); });
state.update(|s| { state.update(|s| {
s.ids s.ids
.ability_descs .ability_descs
.resize(update_length, &mut ui.widget_id_generator()) .resize(ABILITIES_PER_PAGE, &mut ui.widget_id_generator())
}); });
// Page button // Page button
@ -998,7 +969,7 @@ impl<'a> Widget for Diary<'a> {
} else { } else {
self.imgs.arrow_l_inactive self.imgs.arrow_l_inactive
}) })
.bottom_left_with_margins_on(state.ids.spellbook_art, tweak!(-83.0), tweak!(10.0)) .bottom_left_with_margins_on(state.ids.spellbook_art, -83.0, 10.0)
.w_h(48.0, 55.0); .w_h(48.0, 55.0);
// Grey out arrows when inactive // Grey out arrows when inactive
if state.ability_page > 0 { if state.ability_page > 0 {
@ -1019,7 +990,7 @@ impl<'a> Widget for Diary<'a> {
} else { } else {
self.imgs.arrow_r_inactive self.imgs.arrow_r_inactive
}) })
.bottom_right_with_margins_on(state.ids.spellbook_art, tweak!(-83.0), tweak!(10.0)) .bottom_right_with_margins_on(state.ids.spellbook_art, -83.0, 10.0)
.w_h(48.0, 55.0); .w_h(48.0, 55.0);
if state.ability_page < page_indices { if state.ability_page < page_indices {
// Only show right button if not on last page // Only show right button if not on last page
@ -1036,7 +1007,6 @@ impl<'a> Widget for Diary<'a> {
} }
let ability_start = state.ability_page * ABILITIES_PER_PAGE; let ability_start = state.ability_page * ABILITIES_PER_PAGE;
let abilities_range = ability_start..(ability_start + ABILITIES_PER_PAGE);
let mut slot_maker = SlotMaker { let mut slot_maker = SlotMaker {
empty_slot: self.imgs.inv_slot, empty_slot: self.imgs.inv_slot,
@ -1045,9 +1015,9 @@ impl<'a> Widget for Diary<'a> {
background_color: Some(UI_MAIN), background_color: Some(UI_MAIN),
content_size: ContentSize { content_size: ContentSize {
width_height_ratio: 1.0, width_height_ratio: 1.0,
max_fraction: tweak!(1.0), max_fraction: 1.0,
}, },
selected_content_scale: tweak!(1.067), selected_content_scale: 1.067,
amount_font: self.fonts.cyri.conrod_id, amount_font: self.fonts.cyri.conrod_id,
amount_margins: Vec2::new(-4.0, 0.0), amount_margins: Vec2::new(-4.0, 0.0),
amount_font_size: self.fonts.cyri.scale(12), amount_font_size: self.fonts.cyri.scale(12),
@ -1060,10 +1030,8 @@ impl<'a> Widget for Diary<'a> {
for (id_index, (ability_id, ability)) in abilities for (id_index, (ability_id, ability)) in abilities
.iter() .iter()
.enumerate() .skip(ability_start)
.filter_map(|(i, ability_info)| { .take(ABILITIES_PER_PAGE)
abilities_range.contains(&i).then_some(ability_info)
})
.enumerate() .enumerate()
{ {
let (ability_title, ability_desc) = let (ability_title, ability_desc) =
@ -1081,13 +1049,8 @@ impl<'a> Widget for Diary<'a> {
self.imgs.ability_frame self.imgs.ability_frame
}) })
.w_h(566.0, 108.0) .w_h(566.0, 108.0)
.top_left_with_margins_on( .top_left_with_margins_on(align_state, 16.0 + image_offsets, 16.0)
align_state,
tweak!(16.0) + image_offsets,
tweak!(16.0),
)
.color(Some(UI_HIGHLIGHT_0)) .color(Some(UI_HIGHLIGHT_0))
//.parent(state.ids.abilities[id_index])
.set(state.ids.ability_frames[id_index], ui); .set(state.ids.ability_frames[id_index], ui);
let slot = AbilitySlot::Ability(*ability); let slot = AbilitySlot::Ability(*ability);
@ -1121,19 +1084,15 @@ impl<'a> Widget for Diary<'a> {
Text::new(ability_title) Text::new(ability_title)
.top_left_with_margins_on(state.ids.abilities[id_index], 5.0, 110.0) .top_left_with_margins_on(state.ids.abilities[id_index], 5.0, 110.0)
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(tweak!(28))) .font_size(self.fonts.cyri.scale(28))
.color(TEXT_COLOR) .color(TEXT_COLOR)
.w(text_width) .w(text_width)
.graphics_for(state.ids.abilities[id_index]) .graphics_for(state.ids.abilities[id_index])
.set(state.ids.ability_titles[id_index], ui); .set(state.ids.ability_titles[id_index], ui);
Text::new(ability_desc) Text::new(ability_desc)
.top_left_with_margins_on( .top_left_with_margins_on(state.ids.abilities[id_index], 40.0, 110.0)
state.ids.abilities[id_index],
tweak!(40.0),
110.0,
)
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(tweak!(18))) .font_size(self.fonts.cyri.scale(18))
.color(TEXT_COLOR) .color(TEXT_COLOR)
.w(text_width) .w(text_width)
.graphics_for(state.ids.abilities[id_index]) .graphics_for(state.ids.abilities[id_index])
@ -1162,7 +1121,7 @@ impl<'a> Widget for Diary<'a> {
// Background Art // Background Art
Image::new(self.imgs.book_bg) Image::new(self.imgs.book_bg)
.w_h(299.0 * 4.0, 184.0 * 4.0) .w_h(299.0 * 4.0, 184.0 * 4.0)
.mid_top_with_margin_on(state.ids.content_align, tweak!(4.0)) .mid_top_with_margin_on(state.ids.content_align, 4.0)
.set(state.ids.spellbook_art, ui); .set(state.ids.spellbook_art, ui);
state.update(|s| { state.update(|s| {
@ -1183,13 +1142,9 @@ impl<'a> Widget for Diary<'a> {
.color(BLACK); .color(BLACK);
if i == 0 { if i == 0 {
txt = txt.top_left_with_margins_on( txt = txt.top_left_with_margins_on(state.ids.spellbook_art, 20.0, 20.0);
state.ids.spellbook_art,
tweak!(20.0),
tweak!(20.0),
);
} else { } else {
txt = txt.down_from(state.ids.stat_names[i - 1], tweak!(10.0)); txt = txt.down_from(state.ids.stat_names[i - 1], 10.0);
}; };
txt.set(state.ids.stat_names[i], ui); txt.set(state.ids.stat_names[i], ui);
@ -1261,7 +1216,7 @@ impl<'a> Widget for Diary<'a> {
(Some(stats), None) | (None, Some(stats)) => { (Some(stats), None) | (None, Some(stats)) => {
format!("{}", stats.power * 10.0) format!("{}", stats.power * 10.0)
}, },
_ => String::new(), (None, None) => String::new(),
}, },
"Weapon Speed" => { "Weapon Speed" => {
let spd_fmt = |sp| (sp - 1.0) * 100.0; let spd_fmt = |sp| (sp - 1.0) * 100.0;
@ -1288,7 +1243,7 @@ impl<'a> Widget for Diary<'a> {
(Some(stats), None) | (None, Some(stats)) => { (Some(stats), None) | (None, Some(stats)) => {
format!("{}", stats.effect_power * 10.0) format!("{}", stats.effect_power * 10.0)
}, },
_ => String::new(), (None, None) => String::new(),
}, },
"Weapon Crit-Chance" => { "Weapon Crit-Chance" => {
let crit_fmt = |cc| cc * 100.0; let crit_fmt = |cc| cc * 100.0;
@ -1301,10 +1256,10 @@ impl<'a> Widget for Diary<'a> {
(Some(stats), None) | (None, Some(stats)) => { (Some(stats), None) | (None, Some(stats)) => {
format!("{:.1}%", crit_fmt(stats.crit_chance)) format!("{:.1}%", crit_fmt(stats.crit_chance))
}, },
_ => String::new(), (None, None) => String::new(),
} }
}, },
_ => String::new(), unknown => unreachable!(unknown),
}; };
let mut number = Text::new(&value) let mut number = Text::new(&value)
@ -1313,9 +1268,9 @@ impl<'a> Widget for Diary<'a> {
.color(BLACK); .color(BLACK);
if i == 0 { if i == 0 {
number = number.right_from(state.ids.stat_names[i], tweak!(265.0)); number = number.right_from(state.ids.stat_names[i], 265.0);
} else { } else {
number = number.down_from(state.ids.stat_values[i - 1], tweak!(10.0)); number = number.down_from(state.ids.stat_values[i - 1], 10.0);
}; };
number.set(state.ids.stat_values[i], ui); number.set(state.ids.stat_values[i], ui);
} }

View File

@ -3392,7 +3392,7 @@ impl Hud {
(AbilitySlot::Slot(index), _) => { (AbilitySlot::Slot(index), _) => {
events.push(Event::ChangeAbility(index, AuxiliaryAbility::Empty)); events.push(Event::ChangeAbility(index, AuxiliaryAbility::Empty));
}, },
(_, _) => {}, (AbilitySlot::Ability(_), AbilitySlot::Ability(_)) => {},
} }
} }
}, },
@ -3494,7 +3494,7 @@ impl Hud {
(AbilitySlot::Slot(index), _) => { (AbilitySlot::Slot(index), _) => {
events.push(Event::ChangeAbility(index, AuxiliaryAbility::Empty)); events.push(Event::ChangeAbility(index, AuxiliaryAbility::Empty));
}, },
(_, _) => {}, (AbilitySlot::Ability(_), AbilitySlot::Ability(_)) => {},
} }
} }
}, },

View File

@ -250,5 +250,10 @@ impl From<AbilitySlot> for SlotKind {
} }
impl SumSlot for SlotKind { impl SumSlot for SlotKind {
fn is_ability(&self) -> bool { matches!(self, Self::Ability(_)) } fn drag_size(&self) -> Option<[f64; 2]> {
Some(match self {
Self::Ability(_) => [80.0; 2],
_ => return None,
})
}
} }

View File

@ -398,8 +398,8 @@ pub fn ability_description(ability_id: &str) -> (&str, &str) {
"Protects you and your group with thorns for a short amount of time.", "Protects you and your group with thorns for a short amount of time.",
), ),
_ => ( _ => (
"Drag an ability here to use it.", "Ability has no title",
"" "Ability has no description."
), ),
} }
} }

View File

@ -20,7 +20,7 @@ pub trait SlotKey<C, I>: Copy {
} }
pub trait SumSlot: Sized + PartialEq + Copy + Send + 'static { pub trait SumSlot: Sized + PartialEq + Copy + Send + 'static {
fn is_ability(&self) -> bool; fn drag_size(&self) -> Option<[f64; 2]>;
} }
pub struct ContentSize { pub struct ContentSize {
@ -219,8 +219,8 @@ where
let content_img = *content_img; let content_img = *content_img;
let drag_amount = *drag_amount; let drag_amount = *drag_amount;
let dragged_size = if slot.is_ability() { let dragged_size = if let Some(dragged_size) = slot.drag_size() {
[inline_tweak::tweak!(80.0); 2] dragged_size
} else { } else {
self.drag_img_size.map(|e| e as f64).into_array() self.drag_img_size.map(|e| e as f64).into_array()
}; };