1use super::{
2 BLACK, CRITICAL_HP_COLOR, HP_COLOR, Position, PositionSpecifier, Show, TEXT_COLOR,
3 UI_HIGHLIGHT_0, UI_MAIN, XP_COLOR,
4 img_ids::{Imgs, ImgsRot},
5 item_imgs::{ItemImgs, animate_by_pulse},
6};
7use crate::{
8 GlobalState,
9 game_input::GameInput,
10 hud::{
11 self,
12 slots::{AbilitySlot, SlotManager},
13 util,
14 },
15 ui::{
16 ImageFrame, Tooltip, TooltipManager, Tooltipable,
17 fonts::Fonts,
18 slot::{ContentSize, SlotMaker},
19 },
20};
21use client::{self, Client};
22use common::{
23 combat,
24 comp::{
25 self, Body, CharacterState, Energy, Health, Inventory, Poise, Stats,
26 ability::{Ability, ActiveAbilities, AuxiliaryAbility, BASE_ABILITY_LIMIT},
27 inventory::{
28 item::{
29 ItemI18n, ItemKind, MaterialStatManifest,
30 item_key::ItemKey,
31 tool::{AbilityContext, ToolKind},
32 },
33 slot::EquipSlot,
34 },
35 skills::{
36 self, AxeSkill, BowSkill, ClimbSkill, HammerSkill, MiningSkill, SKILL_MODIFIERS,
37 SceptreSkill, Skill, StaffSkill, SwimSkill, SwordSkill,
38 },
39 skillset::{SkillGroupKind, SkillSet},
40 },
41 resources::BattleMode,
42 uid::Uid,
43};
44use conrod_core::{
45 Color, Colorable, Labelable, Positionable, Sizeable, UiCell, Widget, WidgetCommon, color,
46 image,
47 widget::{self, Button, Image, Rectangle, State, Text},
48 widget_ids,
49};
50use i18n::Localization;
51use std::borrow::Cow;
52use strum::{EnumIter, IntoEnumIterator};
53use vek::*;
54const ART_SIZE: [f64; 2] = [320.0, 320.0];
55
56widget_ids! {
57 pub struct Ids {
58 frame,
59 bg,
60 icon,
61 close,
62 title,
63 content_align,
64 section_imgs[],
65 section_btns[],
66 exp_bar_bg,
68 exp_bar_frame,
69 exp_bar_content_align,
70 exp_bar_content,
71 exp_bar_rank,
72 exp_bar_txt,
73 active_bar_checkbox,
74 active_bar_checkbox_label,
75 tree_title_txt,
76 lock_imgs[],
77 available_pts_txt,
78 weapon_imgs[],
79 weapon_btns[],
80 skills_top_l_align,
81 skills_top_r_align,
82 skills_bot_l_align,
83 skills_bot_r_align,
84 skills_top_l[],
85 skills_top_r[],
86 skills_bot_l[],
87 skills_bot_r[],
88 skills[],
89 skill_lock_imgs[],
90 sword_bg,
91 axe_bg,
92 hammer_bg,
93 bow_render,
94 skill_bow_charged_0,
95 skill_bow_charged_1,
96 skill_bow_charged_2,
97 skill_bow_charged_3,
98 skill_bow_charged_4,
99 skill_bow_charged_5,
100 skill_bow_repeater_0,
101 skill_bow_repeater_1,
102 skill_bow_repeater_2,
103 skill_bow_repeater_3,
104 skill_bow_shotgun_0,
105 skill_bow_shotgun_1,
106 skill_bow_shotgun_2,
107 skill_bow_shotgun_3,
108 skill_bow_shotgun_4,
109 skill_bow_passive_0,
110 staff_render,
111 skill_staff_basic_0,
112 skill_staff_basic_1,
113 skill_staff_basic_2,
114 skill_staff_basic_3,
115 skill_staff_basic_4,
116 skill_staff_beam_0,
117 skill_staff_beam_1,
118 skill_staff_beam_2,
119 skill_staff_beam_3,
120 skill_staff_beam_4,
121 skill_staff_shockwave_0,
122 skill_staff_shockwave_1,
123 skill_staff_shockwave_2,
124 skill_staff_shockwave_3,
125 skill_staff_shockwave_4,
126 sceptre_render,
127 skill_sceptre_lifesteal_0,
128 skill_sceptre_lifesteal_1,
129 skill_sceptre_lifesteal_2,
130 skill_sceptre_lifesteal_3,
131 skill_sceptre_lifesteal_4,
132 skill_sceptre_heal_0,
133 skill_sceptre_heal_1,
134 skill_sceptre_heal_2,
135 skill_sceptre_heal_3,
136 skill_sceptre_heal_4,
137 skill_sceptre_aura_0,
138 skill_sceptre_aura_1,
139 skill_sceptre_aura_2,
140 skill_sceptre_aura_3,
141 skill_sceptre_aura_4,
142 pick_render,
143 skill_pick_m1,
144 skill_pick_m1_0,
145 skill_pick_m1_1,
146 skill_pick_m1_2,
147 general_combat_render_0,
148 general_combat_render_1,
149 skill_general_stat_0,
150 skill_general_stat_1,
151 skill_general_tree_0,
152 skill_general_tree_1,
153 skill_general_tree_2,
154 skill_general_tree_3,
155 skill_general_tree_4,
156 skill_general_tree_5,
157 skill_general_roll_0,
158 skill_general_roll_1,
159 skill_general_roll_2,
160 skill_general_roll_3,
161 skill_general_climb_0,
162 skill_general_climb_1,
163 skill_general_climb_2,
164 skill_general_swim_0,
165 skill_general_swim_1,
166 sword_path_overlay,
167 spellbook_art,
169 sb_page_left_align,
170 sb_page_right_align,
171 spellbook_skills_bg,
172 spellbook_btn,
173 spellbook_btn_bg,
174 ability_select_title,
175 ability_page_left,
176 ability_page_right,
177 active_abilities[],
178 active_abilities_keys[],
179 main_weap_select,
180 off_weap_select,
181 abilities[],
182 ability_frames[],
183 abilities_dual[],
184 ability_titles[],
185 ability_descs[],
186 dragged_ability,
187 stat_names[],
189 stat_values[],
190 recipe_groups[],
192 }
193}
194
195#[derive(WidgetCommon)]
196pub struct Diary<'a> {
197 show: &'a Show,
198 client: &'a Client,
199 global_state: &'a GlobalState,
200 skill_set: &'a SkillSet,
201 active_abilities: &'a ActiveAbilities,
202 inventory: &'a Inventory,
203 char_state: &'a CharacterState,
204 health: &'a Health,
205 energy: &'a Energy,
206 poise: &'a Poise,
207 body: &'a Body,
208 uid: &'a Uid,
209 msm: &'a MaterialStatManifest,
210 imgs: &'a Imgs,
211 item_imgs: &'a ItemImgs,
212 fonts: &'a Fonts,
213 localized_strings: &'a Localization,
214 item_i18n: &'a ItemI18n,
215 rot_imgs: &'a ImgsRot,
216 tooltip_manager: &'a mut TooltipManager,
217 slot_manager: &'a mut SlotManager,
218 pulse: f32,
219 context: &'a AbilityContext,
220 stats: Option<&'a Stats>,
221
222 #[conrod(common_builder)]
223 common: widget::CommonBuilder,
224 created_btns_top_l: usize,
225 created_btns_top_r: usize,
226 created_btns_bot_l: usize,
227 created_btns_bot_r: usize,
228}
229
230pub struct DiaryShow {
231 pub skilltreetab: SelectedSkillTree,
232 pub section: DiarySection,
233}
234
235impl Default for DiaryShow {
236 fn default() -> Self {
237 Self {
238 skilltreetab: SelectedSkillTree::General,
239 section: DiarySection::SkillTrees,
240 }
241 }
242}
243
244#[expect(clippy::too_many_arguments)]
245impl<'a> Diary<'a> {
246 pub fn new(
247 show: &'a Show,
248 client: &'a Client,
249 global_state: &'a GlobalState,
250 skill_set: &'a SkillSet,
251 active_abilities: &'a ActiveAbilities,
252 inventory: &'a Inventory,
253 char_state: &'a CharacterState,
254 health: &'a Health,
255 energy: &'a Energy,
256 poise: &'a Poise,
257 body: &'a Body,
258 uid: &'a Uid,
259 msm: &'a MaterialStatManifest,
260 imgs: &'a Imgs,
261 item_imgs: &'a ItemImgs,
262 fonts: &'a Fonts,
263 localized_strings: &'a Localization,
264 item_i18n: &'a ItemI18n,
265 rot_imgs: &'a ImgsRot,
266 tooltip_manager: &'a mut TooltipManager,
267 slot_manager: &'a mut SlotManager,
268 pulse: f32,
269 context: &'a AbilityContext,
270 stats: Option<&'a Stats>,
271 ) -> Self {
272 Self {
273 show,
274 client,
275 global_state,
276 skill_set,
277 active_abilities,
278 inventory,
279 char_state,
280 health,
281 energy,
282 poise,
283 body,
284 uid,
285 msm,
286 imgs,
287 item_imgs,
288 fonts,
289 localized_strings,
290 item_i18n,
291 rot_imgs,
292 tooltip_manager,
293 slot_manager,
294 pulse,
295 context,
296 stats,
297 common: widget::CommonBuilder::default(),
298 created_btns_top_l: 0,
299 created_btns_top_r: 0,
300 created_btns_bot_l: 0,
301 created_btns_bot_r: 0,
302 }
303 }
304}
305
306pub type SelectedSkillTree = SkillGroupKind;
307
308pub enum Event {
309 Close,
310 ChangeSkillTree(SelectedSkillTree),
311 UnlockSkill(Skill),
312 ChangeSection(DiarySection),
313 SelectExpBar(Option<SkillGroupKind>),
314}
315
316#[derive(EnumIter, PartialEq, Eq)]
319pub enum DiarySection {
320 SkillTrees,
321 AbilitySelection,
322 Character,
323 Recipes,
324}
325
326impl DiarySection {
327 fn title_key(&self) -> &'static str {
328 match self {
329 DiarySection::SkillTrees => "hud-diary-sections-skill_trees-title",
330 DiarySection::AbilitySelection => "hud-diary-sections-abilities-title",
331 DiarySection::Character => "hud-diary-sections-character-title",
332 DiarySection::Recipes => "hud-diary-sections-recipes-title",
333 }
334 }
335}
336
337#[derive(EnumIter, PartialEq, Eq)]
340pub enum DiarySkillTree {
341 General,
342 Sword,
343 Axe,
344 Hammer,
345 Bow,
346 Staff,
347 Sceptre,
348 Pick,
349}
350
351impl DiarySkillTree {
352 fn title_key(&self) -> &'static str {
353 match self {
354 DiarySkillTree::General => "hud-skill_tree-general",
355 DiarySkillTree::Sword => "hud-skill_tree-sword",
356 DiarySkillTree::Axe => "hud-skill_tree-axe",
357 DiarySkillTree::Hammer => "hud-skill_tree-hammer",
358 DiarySkillTree::Bow => "hud-skill_tree-bow",
359 DiarySkillTree::Staff => "hud-skill_tree-staff",
360 DiarySkillTree::Sceptre => "hud-skill_tree-sceptre",
361 DiarySkillTree::Pick => "hud-skill_tree-mining",
362 }
363 }
364
365 fn to_skill_group(&self) -> SkillGroupKind {
366 match self {
367 DiarySkillTree::General => SkillGroupKind::General,
368 DiarySkillTree::Sword => SkillGroupKind::Weapon(ToolKind::Sword),
369 DiarySkillTree::Axe => SkillGroupKind::Weapon(ToolKind::Axe),
370 DiarySkillTree::Hammer => SkillGroupKind::Weapon(ToolKind::Hammer),
371 DiarySkillTree::Bow => SkillGroupKind::Weapon(ToolKind::Bow),
372 DiarySkillTree::Staff => SkillGroupKind::Weapon(ToolKind::Staff),
373 DiarySkillTree::Sceptre => SkillGroupKind::Weapon(ToolKind::Sceptre),
374 DiarySkillTree::Pick => SkillGroupKind::Weapon(ToolKind::Pick),
375 }
376 }
377}
378
379pub struct DiaryState {
380 ids: Ids,
381 ability_page: usize,
382 recipe_page: usize,
383}
384
385impl Widget for Diary<'_> {
386 type Event = Vec<Event>;
387 type State = DiaryState;
388 type Style = ();
389
390 fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
391 DiaryState {
392 ids: Ids::new(id_gen),
393 ability_page: 0,
394 recipe_page: 0,
395 }
396 }
397
398 fn style(&self) -> Self::Style {}
399
400 fn update(mut self, args: widget::UpdateArgs<Self>) -> Self::Event {
401 common_base::prof_span!("Diary::update");
402 let widget::UpdateArgs { state, ui, .. } = args;
403 let mut events = Vec::new();
404
405 let diary_tooltip = Tooltip::new({
407 let edge = &self.rot_imgs.tt_side;
410 let corner = &self.rot_imgs.tt_corner;
411 ImageFrame::new(
412 [edge.cw180, edge.none, edge.cw270, edge.cw90],
413 [corner.none, corner.cw270, corner.cw90, corner.cw180],
414 Color::Rgba(0.08, 0.07, 0.04, 1.0),
415 5.0,
416 )
417 })
418 .title_font_size(self.fonts.cyri.scale(15))
419 .parent(ui.window)
420 .desc_font_size(self.fonts.cyri.scale(12))
421 .font_id(self.fonts.cyri.conrod_id)
422 .desc_text_color(TEXT_COLOR);
423
424 let frame_ani = (self.pulse * 4.0).cos() * 0.5 + 0.8;
426
427 Image::new(self.imgs.diary_bg)
428 .w_h(1202.0, 886.0)
429 .mid_top_with_margin_on(ui.window, 5.0)
430 .color(Some(UI_MAIN))
431 .set(state.ids.bg, ui);
432
433 Image::new(self.imgs.diary_frame)
434 .w_h(1202.0, 886.0)
435 .middle_of(state.ids.bg)
436 .color(Some(UI_HIGHLIGHT_0))
437 .set(state.ids.frame, ui);
438
439 Image::new(self.imgs.spellbook_button)
441 .w_h(30.0, 27.0)
442 .top_left_with_margins_on(state.ids.frame, 8.0, 8.0)
443 .set(state.ids.icon, ui);
444
445 if Button::image(self.imgs.close_button)
447 .w_h(24.0, 25.0)
448 .hover_image(self.imgs.close_btn_hover)
449 .press_image(self.imgs.close_btn_press)
450 .top_right_with_margins_on(state.ids.frame, 0.0, 0.0)
451 .set(state.ids.close, ui)
452 .was_clicked()
453 {
454 events.push(Event::Close);
455 }
456
457 Text::new(&self.localized_strings.get_msg("hud-diary"))
459 .mid_top_with_margin_on(state.ids.frame, 3.0)
460 .font_id(self.fonts.cyri.conrod_id)
461 .font_size(self.fonts.cyri.scale(29))
462 .color(TEXT_COLOR)
463 .set(state.ids.title, ui);
464
465 Rectangle::fill_with([599.0 * 2.0, 419.0 * 2.0], color::TRANSPARENT)
467 .mid_top_with_margin_on(state.ids.frame, 46.0)
468 .set(state.ids.content_align, ui);
469
470 let sel_section = &self.show.diary_fields.section;
473
474 let sections_len = DiarySection::iter().enumerate().len();
475
476 state.update(|s| {
478 s.ids
479 .section_imgs
480 .resize(sections_len, &mut ui.widget_id_generator())
481 });
482 state.update(|s| {
483 s.ids
484 .section_btns
485 .resize(sections_len, &mut ui.widget_id_generator())
486 });
487
488 for (i, section) in DiarySection::iter().enumerate() {
489 let section_name = self.localized_strings.get_msg(section.title_key());
490
491 let btn_img = {
492 let img = match section {
493 DiarySection::AbilitySelection => self.imgs.spellbook_ico,
494 DiarySection::SkillTrees => self.imgs.skilltree_ico,
495 DiarySection::Character => self.imgs.stats_ico,
496 DiarySection::Recipes => self.imgs.crafting_icon,
497 };
498
499 if i == 0 {
500 Image::new(img).top_left_with_margins_on(state.ids.content_align, 0.0, -50.0)
501 } else {
502 Image::new(img).down_from(state.ids.section_btns[i - 1], 5.0)
503 }
504 };
505 btn_img.w_h(50.0, 50.0).set(state.ids.section_imgs[i], ui);
506 let border_image = if section == *sel_section {
508 self.imgs.wpn_icon_border_pressed
509 } else {
510 self.imgs.wpn_icon_border
511 };
512
513 let hover_image = if section == *sel_section {
514 self.imgs.wpn_icon_border_pressed
515 } else {
516 self.imgs.wpn_icon_border_mo
517 };
518
519 let press_image = if section == *sel_section {
520 self.imgs.wpn_icon_border_pressed
521 } else {
522 self.imgs.wpn_icon_border_press
523 };
524 let section_buttons = Button::image(border_image)
525 .w_h(50.0, 50.0)
526 .hover_image(hover_image)
527 .press_image(press_image)
528 .middle_of(state.ids.section_imgs[i])
529 .with_tooltip(
530 self.tooltip_manager,
531 §ion_name,
532 "",
533 &diary_tooltip,
534 TEXT_COLOR,
535 )
536 .set(state.ids.section_btns[i], ui);
537 if section_buttons.was_clicked() {
538 events.push(Event::ChangeSection(section))
539 }
540 }
541 match self.show.diary_fields.section {
542 DiarySection::SkillTrees => {
543 let sel_tab = &self.show.diary_fields.skilltreetab;
545
546 let skill_trees_len = DiarySkillTree::iter().enumerate().len();
547
548 state.update(|s| {
550 s.ids
551 .weapon_btns
552 .resize(skill_trees_len, &mut ui.widget_id_generator())
553 });
554 state.update(|s| {
555 s.ids
556 .weapon_imgs
557 .resize(skill_trees_len, &mut ui.widget_id_generator())
558 });
559 state.update(|s| {
560 s.ids
561 .lock_imgs
562 .resize(skill_trees_len, &mut ui.widget_id_generator())
563 });
564
565 for (i, skill_tree) in DiarySkillTree::iter().enumerate() {
567 let skill_tree_name = self.localized_strings.get_msg(skill_tree.title_key());
568 let skill_group = skill_tree.to_skill_group();
569
570 let locked = !self.skill_set.skill_group_accessible(skill_group);
572
573 let btn_img = {
575 let img = match skill_tree {
576 DiarySkillTree::General => self.imgs.swords_crossed,
577 DiarySkillTree::Sword => self.imgs.sword,
578 DiarySkillTree::Axe => self.imgs.axe,
579 DiarySkillTree::Hammer => self.imgs.hammer,
580 DiarySkillTree::Bow => self.imgs.bow,
581 DiarySkillTree::Staff => self.imgs.staff,
582 DiarySkillTree::Sceptre => self.imgs.sceptre,
583 DiarySkillTree::Pick => self.imgs.mining,
584 };
585
586 if i == 0 {
587 Image::new(img).top_left_with_margins_on(
588 state.ids.content_align,
589 10.0,
590 5.0,
591 )
592 } else {
593 Image::new(img).down_from(state.ids.weapon_btns[i - 1], 5.0)
594 }
595 };
596 btn_img.w_h(50.0, 50.0).set(state.ids.weapon_imgs[i], ui);
597
598 if locked {
600 Image::new(self.imgs.lock)
601 .w_h(50.0, 50.0)
602 .middle_of(state.ids.weapon_imgs[i])
603 .graphics_for(state.ids.weapon_imgs[i])
604 .color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.8)))
605 .set(state.ids.lock_imgs[i], ui);
606 }
607
608 let have_points = {
610 let available = self.skill_set.available_sp(skill_group);
611
612 let earned = self.skill_set.earned_sp(skill_group);
613 let total_cost = skill_group.total_skill_point_cost();
614
615 available > 0 && (earned - available) < total_cost
616 };
617
618 let border_image = if skill_group == *sel_tab || have_points {
619 self.imgs.wpn_icon_border_pressed
620 } else {
621 self.imgs.wpn_icon_border
622 };
623
624 let hover_image = if skill_group == *sel_tab {
625 self.imgs.wpn_icon_border_pressed
626 } else {
627 self.imgs.wpn_icon_border_mo
628 };
629
630 let press_image = if skill_group == *sel_tab {
631 self.imgs.wpn_icon_border_pressed
632 } else {
633 self.imgs.wpn_icon_border_press
634 };
635
636 let color = if skill_group != *sel_tab && have_points {
637 Color::Rgba(0.92, 0.76, 0.0, frame_ani)
638 } else {
639 TEXT_COLOR
640 };
641
642 let tooltip_txt = if locked {
643 self.localized_strings.get_msg("hud-skill-not_unlocked")
644 } else {
645 Cow::Borrowed("")
646 };
647
648 let wpn_button = Button::image(border_image)
649 .w_h(50.0, 50.0)
650 .hover_image(hover_image)
651 .press_image(press_image)
652 .middle_of(state.ids.weapon_imgs[i])
653 .image_color(color)
654 .with_tooltip(
655 self.tooltip_manager,
656 &skill_tree_name,
657 &tooltip_txt,
658 &diary_tooltip,
659 TEXT_COLOR,
660 )
661 .set(state.ids.weapon_btns[i], ui);
662 if wpn_button.was_clicked() {
663 events.push(Event::ChangeSkillTree(skill_group))
664 }
665 }
666
667 let current_exp = self.skill_set.available_experience(*sel_tab) as f64;
669 let max_exp = self.skill_set.skill_point_cost(*sel_tab) as f64;
670 let exp_percentage = current_exp / max_exp;
671 let rank = self.skill_set.earned_sp(*sel_tab);
672 let rank_txt = format!("{}", rank);
673 let exp_txt = format!("{}/{}", current_exp, max_exp);
674 let available_pts = self.skill_set.available_sp(*sel_tab);
675 Image::new(self.imgs.diary_exp_bg)
676 .w_h(480.0, 76.0)
677 .mid_bottom_with_margin_on(state.ids.content_align, 10.0)
678 .set(state.ids.exp_bar_bg, ui);
679 Rectangle::fill_with([400.0, 40.0], color::TRANSPARENT)
680 .top_left_with_margins_on(state.ids.exp_bar_bg, 32.0, 40.0)
681 .set(state.ids.exp_bar_content_align, ui);
682 Image::new(self.imgs.bar_content)
683 .w_h(400.0 * exp_percentage, 40.0)
684 .top_left_with_margins_on(state.ids.exp_bar_content_align, 0.0, 0.0)
685 .color(Some(XP_COLOR))
686 .set(state.ids.exp_bar_content, ui);
687 Image::new(self.imgs.diary_exp_frame)
688 .w_h(480.0, 76.0)
689 .color(Some(UI_HIGHLIGHT_0))
690 .middle_of(state.ids.exp_bar_bg)
691 .set(state.ids.exp_bar_frame, ui);
692 let exp_selected =
694 self.global_state.settings.interface.xp_bar_skillgroup == Some(*sel_tab);
695 if Button::image(if !exp_selected {
696 self.imgs.checkbox
697 } else {
698 self.imgs.checkbox_checked
699 })
700 .w_h(18.0, 18.0)
701 .hover_image(if !exp_selected {
702 self.imgs.checkbox_mo
703 } else {
704 self.imgs.checkbox_checked_mo
705 })
706 .press_image(if !exp_selected {
707 self.imgs.checkbox_press
708 } else {
709 self.imgs.checkbox_checked
710 })
711 .top_right_with_margins_on(state.ids.exp_bar_frame, 50.0, -30.0)
712 .set(state.ids.active_bar_checkbox, ui)
713 .was_clicked()
714 {
715 if self.global_state.settings.interface.xp_bar_skillgroup != Some(*sel_tab) {
716 events.push(Event::SelectExpBar(Some(*sel_tab)));
717 } else {
718 events.push(Event::SelectExpBar(None));
719 }
720 }
721
722 Text::new(&self.localized_strings.get_msg("hud-skill-set_as_exp_bar"))
723 .right_from(state.ids.active_bar_checkbox, 10.0)
724 .font_size(self.fonts.cyri.scale(14))
725 .font_id(self.fonts.cyri.conrod_id)
726 .graphics_for(state.ids.active_bar_checkbox)
727 .color(TEXT_COLOR)
728 .set(state.ids.active_bar_checkbox_label, ui);
729
730 if ui
732 .widget_input(state.ids.exp_bar_frame)
733 .mouse()
734 .is_some_and(|m| m.is_over())
735 {
736 Text::new(&exp_txt)
737 .mid_top_with_margin_on(state.ids.exp_bar_frame, 47.0)
738 .font_id(self.fonts.cyri.conrod_id)
739 .font_size(self.fonts.cyri.scale(14))
740 .color(TEXT_COLOR)
741 .graphics_for(state.ids.exp_bar_frame)
742 .set(state.ids.exp_bar_txt, ui);
743 }
744 Text::new(&rank_txt)
745 .mid_top_with_margin_on(state.ids.exp_bar_frame, match rank {
746 0..=99 => 5.0,
747 100..=999 => 8.0,
748 _ => 10.0,
749 })
750 .font_id(self.fonts.cyri.conrod_id)
751 .font_size(self.fonts.cyri.scale(match rank {
752 0..=99 => 28,
753 100..=999 => 21,
754 _ => 15,
755 }))
756 .color(TEXT_COLOR)
757 .set(state.ids.exp_bar_rank, ui);
758
759 Text::new(&self.localized_strings.get_msg_ctx(
760 "hud-skill-sp_available",
761 &i18n::fluent_args! {
762 "number" => available_pts,
763 },
764 ))
765 .mid_top_with_margin_on(state.ids.content_align, 700.0)
766 .font_id(self.fonts.cyri.conrod_id)
767 .font_size(self.fonts.cyri.scale(28))
768 .color(if available_pts > 0 {
769 Color::Rgba(0.92, 0.76, 0.0, frame_ani)
770 } else {
771 TEXT_COLOR
772 })
773 .set(state.ids.available_pts_txt, ui);
774 let x = 200.0;
777 let y = 100.0;
778 Rectangle::fill_with([124.0 * 2.0, 124.0 * 2.0], color::TRANSPARENT)
780 .top_left_with_margins_on(state.ids.content_align, y, x)
781 .set(state.ids.skills_top_l_align, ui);
782 Rectangle::fill_with([124.0 * 2.0, 124.0 * 2.0], color::TRANSPARENT)
783 .top_right_with_margins_on(state.ids.content_align, y, x)
784 .set(state.ids.skills_top_r_align, ui);
785 Rectangle::fill_with([124.0 * 2.0, 124.0 * 2.0], color::TRANSPARENT)
786 .bottom_left_with_margins_on(state.ids.content_align, y, x)
787 .set(state.ids.skills_bot_l_align, ui);
788 Rectangle::fill_with([124.0 * 2.0, 124.0 * 2.0], color::TRANSPARENT)
789 .bottom_right_with_margins_on(state.ids.content_align, y, x)
790 .set(state.ids.skills_bot_r_align, ui);
791
792 match sel_tab {
793 SelectedSkillTree::General => {
794 self.handle_general_skills_window(&diary_tooltip, state, ui, events)
795 },
796 SelectedSkillTree::Weapon(ToolKind::Sword) => {
797 self.handle_sword_skills_window(&diary_tooltip, state, ui, events)
798 },
799 SelectedSkillTree::Weapon(ToolKind::Axe) => {
800 self.handle_axe_skills_window(&diary_tooltip, state, ui, events)
801 },
802 SelectedSkillTree::Weapon(ToolKind::Hammer) => {
803 self.handle_hammer_skills_window(&diary_tooltip, state, ui, events)
804 },
805 SelectedSkillTree::Weapon(ToolKind::Bow) => {
806 self.handle_bow_skills_window(&diary_tooltip, state, ui, events)
807 },
808 SelectedSkillTree::Weapon(ToolKind::Staff) => {
809 self.handle_staff_skills_window(&diary_tooltip, state, ui, events)
810 },
811 SelectedSkillTree::Weapon(ToolKind::Sceptre) => {
812 self.handle_sceptre_skills_window(&diary_tooltip, state, ui, events)
813 },
814 SelectedSkillTree::Weapon(ToolKind::Pick) => {
815 self.handle_mining_skills_window(&diary_tooltip, state, ui, events)
816 },
817 _ => events,
818 }
819 },
820 DiarySection::AbilitySelection => {
821 use comp::ability::AbilityInput;
822
823 Image::new(self.imgs.book_bg)
825 .w_h(299.0 * 4.0, 184.0 * 4.0)
826 .mid_top_with_margin_on(state.ids.content_align, 4.0)
827 .set(state.ids.spellbook_art, ui);
829 Image::new(self.imgs.skills_bg)
830 .w_h(240.0 * 2.0, 40.0 * 2.0)
831 .mid_bottom_with_margin_on(state.ids.content_align, 8.0)
832 .set(state.ids.spellbook_skills_bg, ui);
833
834 Rectangle::fill_with([299.0 * 2.0, 184.0 * 4.0], color::TRANSPARENT)
835 .top_left_with_margins_on(state.ids.spellbook_art, 0.0, 0.0)
836 .set(state.ids.sb_page_left_align, ui);
837 Rectangle::fill_with([299.0 * 2.0, 184.0 * 4.0], color::TRANSPARENT)
838 .top_right_with_margins_on(state.ids.spellbook_art, 0.0, 0.0)
839 .set(state.ids.sb_page_right_align, ui);
840
841 state.update(|s| {
843 s.ids
844 .active_abilities
845 .resize(BASE_ABILITY_LIMIT, &mut ui.widget_id_generator())
846 });
847 state.update(|s| {
848 s.ids
849 .active_abilities_keys
850 .resize(BASE_ABILITY_LIMIT, &mut ui.widget_id_generator())
851 });
852
853 let mut slot_maker = SlotMaker {
854 empty_slot: self.imgs.inv_slot,
855 filled_slot: self.imgs.inv_slot,
856 selected_slot: self.imgs.inv_slot_sel,
857 background_color: Some(UI_MAIN),
858 content_size: ContentSize {
859 width_height_ratio: 1.0,
860 max_fraction: 0.9,
861 },
862 selected_content_scale: 1.067,
863 amount_font: self.fonts.cyri.conrod_id,
864 amount_margins: Vec2::new(-4.0, 0.0),
865 amount_font_size: self.fonts.cyri.scale(12),
866 amount_text_color: TEXT_COLOR,
867 content_source: &(
868 self.active_abilities,
869 self.inventory,
870 self.skill_set,
871 self.context,
872 Some(self.char_state),
873 self.stats,
874 ),
875 image_source: self.imgs,
876 slot_manager: Some(self.slot_manager),
877 pulse: 0.0,
878 };
879
880 for i in 0..BASE_ABILITY_LIMIT {
881 let ability_id = self
882 .active_abilities
883 .get_ability(
884 AbilityInput::Auxiliary(i),
885 Some(self.inventory),
886 Some(self.skill_set),
887 self.stats,
888 )
889 .ability_id(
890 Some(self.char_state),
891 Some(self.inventory),
892 Some(self.skill_set),
893 self.context,
894 );
895 let (ability_title, ability_desc) = if let Some(ability_id) = ability_id {
896 util::ability_description(ability_id, self.localized_strings)
897 } else {
898 (
899 Cow::Borrowed("Drag an ability here to use it."),
900 Cow::Borrowed(""),
901 )
902 };
903
904 let image_size = 80.0;
905 let image_offsets = 92.0 * i as f64;
906
907 let slot = AbilitySlot::Slot(i);
908 let mut ability_slot = slot_maker.fabricate(slot, [image_size; 2]);
909
910 if i == 0 {
911 ability_slot = ability_slot.top_left_with_margins_on(
912 state.ids.spellbook_skills_bg,
913 0.0,
914 32.0 + image_offsets,
915 );
916 } else {
917 ability_slot =
918 ability_slot.right_from(state.ids.active_abilities[i - 1], 4.0)
919 }
920 ability_slot
921 .with_tooltip(
922 self.tooltip_manager,
923 &ability_title,
924 &ability_desc,
925 &diary_tooltip,
926 TEXT_COLOR,
927 )
928 .set(state.ids.active_abilities[i], ui);
929
930 let keys = &self.global_state.settings.controls;
932 let ability_key = [
933 GameInput::Slot1,
934 GameInput::Slot2,
935 GameInput::Slot3,
936 GameInput::Slot4,
937 GameInput::Slot5,
938 ]
939 .get(i)
940 .and_then(|input| keys.get_binding(*input))
941 .map(|key| key.display_shortest())
942 .unwrap_or_default();
943
944 Text::new(&ability_key)
945 .top_left_with_margins_on(state.ids.active_abilities[i], 0.0, 4.0)
946 .font_id(self.fonts.cyri.conrod_id)
947 .font_size(self.fonts.cyri.scale(20))
948 .color(TEXT_COLOR)
949 .graphics_for(state.ids.active_abilities[i])
950 .set(state.ids.active_abilities_keys[i], ui);
951 }
952
953 let abilities: Vec<_> = ActiveAbilities::all_available_abilities(
954 Some(self.inventory),
955 Some(self.skill_set),
956 )
957 .into_iter()
958 .map(|a| {
959 (
960 Ability::from(a).ability_id(
961 Some(self.char_state),
962 Some(self.inventory),
963 Some(self.skill_set),
964 self.context,
965 ),
966 a,
967 )
968 })
969 .collect();
970
971 const ABILITIES_PER_PAGE: usize = 12;
972
973 let page_indices = (abilities.len().saturating_sub(1)) / ABILITIES_PER_PAGE;
974
975 if state.ability_page > page_indices {
976 state.update(|s| s.ability_page = 0);
977 }
978
979 state.update(|s| {
980 s.ids
981 .abilities
982 .resize(ABILITIES_PER_PAGE, &mut ui.widget_id_generator())
983 });
984 state.update(|s| {
985 s.ids
986 .abilities_dual
987 .resize(ABILITIES_PER_PAGE, &mut ui.widget_id_generator())
988 });
989 state.update(|s| {
990 s.ids
991 .ability_titles
992 .resize(ABILITIES_PER_PAGE, &mut ui.widget_id_generator())
993 });
994 state.update(|s| {
995 s.ids
996 .ability_frames
997 .resize(ABILITIES_PER_PAGE, &mut ui.widget_id_generator())
998 });
999 state.update(|s| {
1000 s.ids
1001 .ability_descs
1002 .resize(ABILITIES_PER_PAGE, &mut ui.widget_id_generator())
1003 });
1004
1005 let left_arrow = Button::image(if state.ability_page > 0 {
1008 self.imgs.arrow_l
1009 } else {
1010 self.imgs.arrow_l_inactive
1011 })
1012 .bottom_left_with_margins_on(state.ids.spellbook_art, -83.0, 10.0)
1013 .w_h(48.0, 55.0);
1014 if state.ability_page > 0 {
1016 if left_arrow
1017 .hover_image(self.imgs.arrow_l_click)
1018 .press_image(self.imgs.arrow_l)
1019 .set(state.ids.ability_page_left, ui)
1020 .was_clicked()
1021 {
1022 state.update(|s| s.ability_page -= 1);
1023 }
1024 } else {
1025 left_arrow.set(state.ids.ability_page_left, ui);
1026 }
1027 let right_arrow = Button::image(if state.ability_page < page_indices {
1029 self.imgs.arrow_r
1030 } else {
1031 self.imgs.arrow_r_inactive
1032 })
1033 .bottom_right_with_margins_on(state.ids.spellbook_art, -83.0, 10.0)
1034 .w_h(48.0, 55.0);
1035 if state.ability_page < page_indices {
1036 if right_arrow
1038 .hover_image(self.imgs.arrow_r_click)
1039 .press_image(self.imgs.arrow_r)
1040 .set(state.ids.ability_page_right, ui)
1041 .was_clicked()
1042 {
1043 state.update(|s| s.ability_page += 1);
1044 };
1045 } else {
1046 right_arrow.set(state.ids.ability_page_right, ui);
1047 }
1048
1049 let ability_start = state.ability_page * ABILITIES_PER_PAGE;
1050
1051 let mut slot_maker = SlotMaker {
1052 empty_slot: self.imgs.inv_slot,
1053 filled_slot: self.imgs.inv_slot,
1054 selected_slot: self.imgs.inv_slot_sel,
1055 background_color: Some(UI_MAIN),
1056 content_size: ContentSize {
1057 width_height_ratio: 1.0,
1058 max_fraction: 1.0,
1059 },
1060 selected_content_scale: 1.067,
1061 amount_font: self.fonts.cyri.conrod_id,
1062 amount_margins: Vec2::new(-4.0, 0.0),
1063 amount_font_size: self.fonts.cyri.scale(12),
1064 amount_text_color: TEXT_COLOR,
1065 content_source: &(
1066 self.active_abilities,
1067 self.inventory,
1068 self.skill_set,
1069 self.context,
1070 Some(self.char_state),
1071 self.stats,
1072 ),
1073 image_source: self.imgs,
1074 slot_manager: Some(self.slot_manager),
1075 pulse: 0.0,
1076 };
1077
1078 let same_weap_kinds = self
1079 .inventory
1080 .equipped(EquipSlot::ActiveMainhand)
1081 .zip(self.inventory.equipped(EquipSlot::ActiveOffhand))
1082 .is_some_and(|(a, b)| {
1083 if let (ItemKind::Tool(tool_a), ItemKind::Tool(tool_b)) =
1084 (&*a.kind(), &*b.kind())
1085 {
1086 (a.ability_spec(), tool_a.kind) == (b.ability_spec(), tool_b.kind)
1087 } else {
1088 false
1089 }
1090 });
1091
1092 for (id_index, (ability_id, ability)) in abilities
1093 .iter()
1094 .skip(ability_start)
1095 .take(ABILITIES_PER_PAGE)
1096 .enumerate()
1097 {
1098 let (ability_title, ability_desc) =
1099 util::ability_description(ability_id.unwrap_or(""), self.localized_strings);
1100
1101 let (align_state, image_offsets) = if id_index < 6 {
1102 (state.ids.sb_page_left_align, 120.0 * id_index as f64)
1103 } else {
1104 (state.ids.sb_page_right_align, 120.0 * (id_index - 6) as f64)
1105 };
1106
1107 Image::new(if same_weap_kinds {
1108 self.imgs.ability_frame_dual
1109 } else {
1110 self.imgs.ability_frame
1111 })
1112 .w_h(566.0, 108.0)
1113 .top_left_with_margins_on(align_state, 16.0 + image_offsets, 16.0)
1114 .color(Some(UI_HIGHLIGHT_0))
1115 .set(state.ids.ability_frames[id_index], ui);
1116
1117 let slot = AbilitySlot::Ability(*ability);
1118 slot_maker
1119 .fabricate(slot, [100.0; 2])
1120 .top_left_with_margins_on(align_state, 20.0 + image_offsets, 20.0)
1121 .set(state.ids.abilities[id_index], ui);
1122
1123 if same_weap_kinds && let AuxiliaryAbility::MainWeapon(slot) = ability {
1124 let ability = AuxiliaryAbility::OffWeapon(*slot);
1125
1126 let slot = AbilitySlot::Ability(ability);
1127 slot_maker
1128 .fabricate(slot, [100.0; 2])
1129 .top_right_with_margins_on(align_state, 20.0 + image_offsets, 20.0)
1130 .set(state.ids.abilities_dual[id_index], ui);
1131 }
1132 let text_width = 299.0 * 2.0
1134 - if same_weap_kinds && matches!(ability, AuxiliaryAbility::MainWeapon(_)) {
1135 (20.0 + 100.0 + 10.0) * 2.0
1138 } else {
1139 20.0 * 2.0 + 100.0
1142 };
1143 Text::new(&ability_title)
1144 .top_left_with_margins_on(state.ids.abilities[id_index], 5.0, 110.0)
1145 .font_id(self.fonts.cyri.conrod_id)
1146 .font_size(self.fonts.cyri.scale(28))
1147 .color(TEXT_COLOR)
1148 .w(text_width)
1149 .graphics_for(state.ids.abilities[id_index])
1150 .set(state.ids.ability_titles[id_index], ui);
1151 Text::new(&ability_desc)
1152 .top_left_with_margins_on(state.ids.abilities[id_index], 40.0, 110.0)
1153 .font_id(self.fonts.cyri.conrod_id)
1154 .font_size(self.fonts.cyri.scale(13))
1155 .color(TEXT_COLOR)
1156 .w(text_width)
1157 .graphics_for(state.ids.abilities[id_index])
1158 .set(state.ids.ability_descs[id_index], ui);
1159 }
1160
1161 events
1162 },
1163 DiarySection::Character => {
1164 Image::new(self.imgs.book_bg)
1166 .w_h(299.0 * 4.0, 184.0 * 4.0)
1167 .mid_top_with_margin_on(state.ids.content_align, 4.0)
1168 .set(state.ids.spellbook_art, ui);
1169
1170 if state.ids.stat_names.len() < STAT_COUNT {
1171 state.update(|s| {
1172 s.ids
1173 .stat_names
1174 .resize(STAT_COUNT, &mut ui.widget_id_generator());
1175 s.ids
1176 .stat_values
1177 .resize(STAT_COUNT, &mut ui.widget_id_generator());
1178 });
1179 }
1180
1181 for (i, stat) in CharacterStat::iter().enumerate() {
1182 let localized_name = stat.localized_str(self.localized_strings);
1184 let mut txt = Text::new(&localized_name)
1185 .font_id(self.fonts.cyri.conrod_id)
1186 .font_size(self.fonts.cyri.scale(29))
1187 .color(BLACK);
1188
1189 if i == 0 {
1190 txt = txt.top_left_with_margins_on(state.ids.spellbook_art, 20.0, 20.0);
1191 } else {
1192 txt = txt.down_from(state.ids.stat_names[i - 1], 10.0);
1193 };
1194 txt.set(state.ids.stat_names[i], ui);
1195
1196 let main_weap_stats = self
1197 .inventory
1198 .equipped(EquipSlot::ActiveMainhand)
1199 .and_then(|item| match &*item.kind() {
1200 ItemKind::Tool(tool) => {
1201 Some(tool.stats(item.stats_durability_multiplier()))
1202 },
1203 _ => None,
1204 });
1205
1206 let off_weap_stats = self
1207 .inventory
1208 .equipped(EquipSlot::ActiveOffhand)
1209 .and_then(|item| match &*item.kind() {
1210 ItemKind::Tool(tool) => {
1211 Some(tool.stats(item.stats_durability_multiplier()))
1212 },
1213 _ => None,
1214 });
1215
1216 let (name, _gender, battle_mode) = self
1217 .client
1218 .player_list()
1219 .get(self.uid)
1220 .and_then(|info| info.character.as_ref())
1221 .map_or_else(
1222 || ("Unknown".to_string(), None, BattleMode::PvP),
1223 |character_info| {
1224 (
1225 self.localized_strings.get_content(&character_info.name),
1226 character_info.gender,
1227 character_info.battle_mode,
1228 )
1229 },
1230 );
1231
1232 let value = match stat {
1234 CharacterStat::Name => name,
1235 CharacterStat::BattleMode => match battle_mode {
1236 BattleMode::PvP => "PvP".to_string(),
1237 BattleMode::PvE => "PvE".to_string(),
1238 },
1239 CharacterStat::Waypoint => self
1240 .client
1241 .waypoint()
1242 .as_ref()
1243 .cloned()
1244 .unwrap_or_else(|| "Unknown".to_string()),
1245 CharacterStat::Hitpoints => format!("{}", self.health.base_max() as u32),
1246 CharacterStat::Energy => format!("{}", self.energy.base_max() as u32),
1247 CharacterStat::Poise => format!("{}", self.poise.base_max() as u32),
1248 CharacterStat::CombatRating => {
1249 let cr = combat::combat_rating(
1250 self.inventory,
1251 self.health,
1252 self.energy,
1253 self.poise,
1254 self.skill_set,
1255 *self.body,
1256 self.msm,
1257 );
1258 format!("{:.2}", cr * 10.0)
1259 },
1260 CharacterStat::Protection => {
1261 let protection =
1262 combat::compute_protection(Some(self.inventory), self.msm);
1263 match protection {
1264 Some(prot) => format!("{}", prot),
1265 None => String::from("Invincible"),
1266 }
1267 },
1268 CharacterStat::StunResistance => {
1269 let stun_res = Poise::compute_poise_damage_reduction(
1270 Some(self.inventory),
1271 self.msm,
1272 None,
1273 self.stats,
1274 );
1275 format!("{:.2}%", stun_res * 100.0)
1276 },
1277 CharacterStat::PrecisionPower => {
1278 let precision_power =
1279 combat::compute_precision_mult(Some(self.inventory), self.msm);
1280 format!("x{:.2}", precision_power)
1281 },
1282 CharacterStat::EnergyReward => {
1283 let energy_rew =
1284 combat::compute_energy_reward_mod(Some(self.inventory), self.msm);
1285 format!("{:+.0}%", (energy_rew - 1.0) * 100.0)
1286 },
1287 CharacterStat::Stealth => {
1288 let stealth_perception_multiplier =
1289 combat::perception_dist_multiplier_from_stealth(
1290 Some(self.inventory),
1291 None,
1292 self.msm,
1293 );
1294 let txt =
1295 format!("{:+.1}%", (1.0 - stealth_perception_multiplier) * 100.0);
1296
1297 txt
1298 },
1299 CharacterStat::WeaponPower => match (main_weap_stats, off_weap_stats) {
1300 (Some(m_stats), Some(o_stats)) => {
1301 format!("{} {}", m_stats.power * 10.0, o_stats.power * 10.0)
1302 },
1303 (Some(stats), None) | (None, Some(stats)) => {
1304 format!("{}", stats.power * 10.0)
1305 },
1306 (None, None) => String::new(),
1307 },
1308 CharacterStat::WeaponSpeed => {
1309 let spd_fmt = |sp| (sp - 1.0) * 100.0;
1310 match (main_weap_stats, off_weap_stats) {
1311 (Some(m_stats), Some(o_stats)) => format!(
1312 "{:+.0}% {:+.0}%",
1313 spd_fmt(m_stats.speed),
1314 spd_fmt(o_stats.speed)
1315 ),
1316 (Some(stats), None) | (None, Some(stats)) => {
1317 format!("{:+.0}%", spd_fmt(stats.speed))
1318 },
1319 _ => String::new(),
1320 }
1321 },
1322 CharacterStat::WeaponEffectPower => match (main_weap_stats, off_weap_stats)
1323 {
1324 (Some(m_stats), Some(o_stats)) => {
1325 format!(
1326 "{} {}",
1327 m_stats.effect_power * 10.0,
1328 o_stats.effect_power * 10.0
1329 )
1330 },
1331 (Some(stats), None) | (None, Some(stats)) => {
1332 format!("{}", stats.effect_power * 10.0)
1333 },
1334 (None, None) => String::new(),
1335 },
1336 };
1337
1338 let mut number = Text::new(&value)
1339 .font_id(self.fonts.cyri.conrod_id)
1340 .font_size(self.fonts.cyri.scale(29))
1341 .color(BLACK);
1342
1343 if i == 0 {
1344 number = number.right_from(state.ids.stat_names[i], 165.0);
1345 } else {
1346 number = number.down_from(state.ids.stat_values[i - 1], 10.0);
1347 };
1348 number.set(state.ids.stat_values[i], ui);
1349 }
1350
1351 events
1352 },
1353 DiarySection::Recipes => {
1354 Image::new(self.imgs.book_bg)
1356 .w_h(299.0 * 4.0, 184.0 * 4.0)
1357 .mid_top_with_margin_on(state.ids.content_align, 4.0)
1358 .set(state.ids.spellbook_art, ui);
1359
1360 Rectangle::fill_with([299.0 * 2.0, 184.0 * 4.0], color::TRANSPARENT)
1361 .top_left_with_margins_on(state.ids.spellbook_art, 0.0, 0.0)
1362 .set(state.ids.sb_page_left_align, ui);
1363 Rectangle::fill_with([299.0 * 2.0, 184.0 * 4.0], color::TRANSPARENT)
1364 .top_right_with_margins_on(state.ids.spellbook_art, 0.0, 0.0)
1365 .set(state.ids.sb_page_right_align, ui);
1366
1367 const RECIPES_PER_PAGE: usize = 36;
1368
1369 let page_index_max =
1370 self.inventory.recipe_groups_iter().len().saturating_sub(1) / RECIPES_PER_PAGE;
1371
1372 if state.recipe_page > page_index_max {
1373 state.update(|s| s.recipe_page = 0);
1374 }
1375
1376 let left_arrow = Button::image(if state.recipe_page > 0 {
1379 self.imgs.arrow_l
1380 } else {
1381 self.imgs.arrow_l_inactive
1382 })
1383 .bottom_left_with_margins_on(state.ids.spellbook_art, -83.0, 10.0)
1384 .w_h(48.0, 55.0);
1385 if state.recipe_page > 0 {
1387 if left_arrow
1388 .hover_image(self.imgs.arrow_l_click)
1389 .press_image(self.imgs.arrow_l)
1390 .set(state.ids.ability_page_left, ui)
1391 .was_clicked()
1392 {
1393 state.update(|s| s.recipe_page -= 1);
1394 }
1395 } else {
1396 left_arrow.set(state.ids.ability_page_left, ui);
1397 }
1398 let right_arrow = Button::image(if state.recipe_page < page_index_max {
1400 self.imgs.arrow_r
1401 } else {
1402 self.imgs.arrow_r_inactive
1403 })
1404 .bottom_right_with_margins_on(state.ids.spellbook_art, -83.0, 10.0)
1405 .w_h(48.0, 55.0);
1406 if state.recipe_page < page_index_max {
1407 if right_arrow
1409 .hover_image(self.imgs.arrow_r_click)
1410 .press_image(self.imgs.arrow_r)
1411 .set(state.ids.ability_page_right, ui)
1412 .was_clicked()
1413 {
1414 state.update(|s| s.recipe_page += 1);
1415 };
1416 } else {
1417 right_arrow.set(state.ids.ability_page_right, ui);
1418 }
1419
1420 state.update(|s| {
1421 s.ids
1422 .recipe_groups
1423 .resize(RECIPES_PER_PAGE, &mut ui.widget_id_generator())
1424 });
1425
1426 for (i, rg) in self
1427 .inventory
1428 .recipe_groups_iter()
1429 .skip(state.recipe_page * RECIPES_PER_PAGE)
1430 .take(RECIPES_PER_PAGE)
1431 .enumerate()
1432 {
1433 let (title, _desc) =
1434 util::item_text(rg, self.localized_strings, self.item_i18n);
1435
1436 let mut text = Text::new(&title)
1437 .font_id(self.fonts.cyri.conrod_id)
1438 .font_size(self.fonts.cyri.scale(29))
1439 .color(BLACK);
1440
1441 if i == 0 {
1442 text =
1443 text.top_left_with_margins_on(state.ids.sb_page_left_align, 20.0, 20.0);
1444 } else if i == 18 {
1445 text = text.top_left_with_margins_on(
1446 state.ids.sb_page_right_align,
1447 20.0,
1448 20.0,
1449 );
1450 } else {
1451 text = text.down_from(state.ids.recipe_groups[i - 1], 10.0);
1452 }
1453 text.set(state.ids.recipe_groups[i], ui);
1454 }
1455
1456 events
1457 },
1458 }
1459 }
1460}
1461
1462enum SkillIcon<'a> {
1463 Unlockable {
1464 skill: Skill,
1465 image: image::Id,
1466 position: PositionSpecifier,
1467 id: widget::Id,
1468 },
1469 Descriptive {
1470 title: &'a str,
1471 desc: &'a str,
1472 image: image::Id,
1473 position: PositionSpecifier,
1474 id: widget::Id,
1475 },
1476 Ability {
1477 skill: Skill,
1478 ability_id: &'a str,
1479 position: PositionSpecifier,
1480 },
1481}
1482
1483impl Diary<'_> {
1484 fn handle_general_skills_window(
1485 &mut self,
1486 diary_tooltip: &Tooltip,
1487 state: &mut State<DiaryState>,
1488 ui: &mut UiCell,
1489 mut events: Vec<Event>,
1490 ) -> Vec<Event> {
1491 let tree_title = &self.localized_strings.get_msg("common-weapons-general");
1492 Text::new(tree_title)
1493 .mid_top_with_margin_on(state.ids.content_align, 2.0)
1494 .font_id(self.fonts.cyri.conrod_id)
1495 .font_size(self.fonts.cyri.scale(34))
1496 .color(TEXT_COLOR)
1497 .set(state.ids.tree_title_txt, ui);
1498
1499 let skills_top_l = 6;
1502 let skills_top_r = 0;
1503 let skills_bot_l = 0;
1504 let skills_bot_r = 5;
1505
1506 self.setup_state_for_skill_icons(
1507 state,
1508 ui,
1509 skills_top_l,
1510 skills_top_r,
1511 skills_bot_l,
1512 skills_bot_r,
1513 );
1514
1515 use SkillGroupKind::*;
1516 use ToolKind::*;
1517 Image::new(animate_by_pulse(
1519 &self.item_imgs.img_ids_or_not_found_img(ItemKey::Simple(
1520 "example_general_combat_left".to_string(),
1521 )),
1522 self.pulse,
1523 ))
1524 .wh(ART_SIZE)
1525 .middle_of(state.ids.content_align)
1526 .color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
1527 .set(state.ids.general_combat_render_0, ui);
1528
1529 Image::new(animate_by_pulse(
1530 &self.item_imgs.img_ids_or_not_found_img(ItemKey::Simple(
1531 "example_general_combat_right".to_string(),
1532 )),
1533 self.pulse,
1534 ))
1535 .wh(ART_SIZE)
1536 .middle_of(state.ids.general_combat_render_0)
1537 .color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
1538 .set(state.ids.general_combat_render_1, ui);
1539
1540 use PositionSpecifier::MidTopWithMarginOn;
1541 let skill_buttons = &[
1542 SkillIcon::Unlockable {
1548 skill: Skill::UnlockGroup(Weapon(Sword)),
1549 image: self.imgs.unlock_sword_skill,
1550 position: MidTopWithMarginOn(state.ids.skills_top_l[0], 3.0),
1551 id: state.ids.skill_general_tree_0,
1552 },
1553 SkillIcon::Unlockable {
1554 skill: Skill::UnlockGroup(Weapon(Axe)),
1555 image: self.imgs.unlock_axe_skill,
1556 position: MidTopWithMarginOn(state.ids.skills_top_l[1], 3.0),
1557 id: state.ids.skill_general_tree_1,
1558 },
1559 SkillIcon::Unlockable {
1560 skill: Skill::UnlockGroup(Weapon(Hammer)),
1561 image: self.imgs.unlock_hammer_skill,
1562 position: MidTopWithMarginOn(state.ids.skills_top_l[2], 3.0),
1563 id: state.ids.skill_general_tree_2,
1564 },
1565 SkillIcon::Unlockable {
1566 skill: Skill::UnlockGroup(Weapon(Bow)),
1567 image: self.imgs.unlock_bow_skill,
1568 position: MidTopWithMarginOn(state.ids.skills_top_l[3], 3.0),
1569 id: state.ids.skill_general_tree_3,
1570 },
1571 SkillIcon::Unlockable {
1572 skill: Skill::UnlockGroup(Weapon(Staff)),
1573 image: self.imgs.unlock_staff_skill0,
1574 position: MidTopWithMarginOn(state.ids.skills_top_l[4], 3.0),
1575 id: state.ids.skill_general_tree_4,
1576 },
1577 SkillIcon::Unlockable {
1578 skill: Skill::UnlockGroup(Weapon(Sceptre)),
1579 image: self.imgs.unlock_sceptre_skill,
1580 position: MidTopWithMarginOn(state.ids.skills_top_l[5], 3.0),
1581 id: state.ids.skill_general_tree_5,
1582 },
1583 SkillIcon::Descriptive {
1585 title: "hud-skill-climbing_title",
1586 desc: "hud-skill-climbing",
1587 image: self.imgs.skill_climbing_skill,
1588 position: MidTopWithMarginOn(state.ids.skills_bot_r[0], 3.0),
1589 id: state.ids.skill_general_climb_0,
1590 },
1591 SkillIcon::Unlockable {
1592 skill: Skill::Climb(ClimbSkill::Cost),
1593 image: self.imgs.utility_cost_skill,
1594 position: MidTopWithMarginOn(state.ids.skills_bot_r[1], 3.0),
1595 id: state.ids.skill_general_climb_1,
1596 },
1597 SkillIcon::Unlockable {
1598 skill: Skill::Climb(ClimbSkill::Speed),
1599 image: self.imgs.utility_speed_skill,
1600 position: MidTopWithMarginOn(state.ids.skills_bot_r[2], 3.0),
1601 id: state.ids.skill_general_climb_2,
1602 },
1603 SkillIcon::Descriptive {
1604 title: "hud-skill-swim_title",
1605 desc: "hud-skill-swim",
1606 image: self.imgs.skill_swim_skill,
1607 position: MidTopWithMarginOn(state.ids.skills_bot_r[3], 3.0),
1608 id: state.ids.skill_general_swim_0,
1609 },
1610 SkillIcon::Unlockable {
1611 skill: Skill::Swim(SwimSkill::Speed),
1612 image: self.imgs.utility_speed_skill,
1613 position: MidTopWithMarginOn(state.ids.skills_bot_r[4], 3.0),
1614 id: state.ids.skill_general_swim_1,
1615 },
1616 ];
1617
1618 self.handle_skill_buttons(skill_buttons, ui, &mut events, diary_tooltip, state);
1619 events
1620 }
1621
1622 fn handle_sword_skills_window(
1623 &mut self,
1624 diary_tooltip: &Tooltip,
1625 state: &mut State<DiaryState>,
1626 ui: &mut UiCell,
1627 mut events: Vec<Event>,
1628 ) -> Vec<Event> {
1629 Image::new(self.imgs.sword_tree_paths)
1630 .wh([1042.0, 636.0])
1631 .mid_top_with_margin_on(state.ids.content_align, 55.0)
1632 .graphics_for(state.ids.content_align)
1633 .color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
1634 .set(state.ids.sword_path_overlay, ui);
1635
1636 Image::new(self.imgs.sword_bg)
1638 .wh([933.0, 615.0])
1639 .mid_top_with_margin_on(state.ids.content_align, 65.0)
1640 .color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
1641 .set(state.ids.sword_bg, ui);
1642
1643 use PositionSpecifier::TopLeftWithMarginsOn;
1644 let skill_buttons = &[
1645 SkillIcon::Ability {
1646 skill: Skill::Sword(SwordSkill::CrescentSlash),
1647 ability_id: "veloren.core.pseudo_abilities.sword.crescent_slash",
1648 position: TopLeftWithMarginsOn(state.ids.sword_bg, 537.0, 429.0),
1649 },
1650 SkillIcon::Ability {
1651 skill: Skill::Sword(SwordSkill::FellStrike),
1652 ability_id: "veloren.core.pseudo_abilities.sword.fell_strike",
1653 position: TopLeftWithMarginsOn(state.ids.sword_bg, 457.0, 527.0),
1654 },
1655 SkillIcon::Ability {
1656 skill: Skill::Sword(SwordSkill::Skewer),
1657 ability_id: "veloren.core.pseudo_abilities.sword.skewer",
1658 position: TopLeftWithMarginsOn(state.ids.sword_bg, 368.0, 527.0),
1659 },
1660 SkillIcon::Ability {
1661 skill: Skill::Sword(SwordSkill::Cascade),
1662 ability_id: "veloren.core.pseudo_abilities.sword.cascade",
1663 position: TopLeftWithMarginsOn(state.ids.sword_bg, 457.0, 332.0),
1664 },
1665 SkillIcon::Ability {
1666 skill: Skill::Sword(SwordSkill::CrossCut),
1667 ability_id: "veloren.core.pseudo_abilities.sword.cross_cut",
1668 position: TopLeftWithMarginsOn(state.ids.sword_bg, 368.0, 332.0),
1669 },
1670 SkillIcon::Ability {
1671 skill: Skill::Sword(SwordSkill::Finisher),
1672 ability_id: "veloren.core.pseudo_abilities.sword.finisher",
1673 position: TopLeftWithMarginsOn(state.ids.sword_bg, 263.0, 429.0),
1674 },
1675 SkillIcon::Ability {
1676 skill: Skill::Sword(SwordSkill::HeavySweep),
1677 ability_id: "common.abilities.sword.heavy_sweep",
1678 position: TopLeftWithMarginsOn(state.ids.sword_bg, 457.0, 2.0),
1679 },
1680 SkillIcon::Ability {
1681 skill: Skill::Sword(SwordSkill::HeavyPommelStrike),
1682 ability_id: "common.abilities.sword.heavy_pommel_strike",
1683 position: TopLeftWithMarginsOn(state.ids.sword_bg, 457.0, 91.0),
1684 },
1685 SkillIcon::Ability {
1686 skill: Skill::Sword(SwordSkill::AgileQuickDraw),
1687 ability_id: "common.abilities.sword.agile_quick_draw",
1688 position: TopLeftWithMarginsOn(state.ids.sword_bg, 142.0, 384.0),
1689 },
1690 SkillIcon::Ability {
1691 skill: Skill::Sword(SwordSkill::AgileFeint),
1692 ability_id: "common.abilities.sword.agile_feint",
1693 position: TopLeftWithMarginsOn(state.ids.sword_bg, 142.0, 472.0),
1694 },
1695 SkillIcon::Ability {
1696 skill: Skill::Sword(SwordSkill::DefensiveRiposte),
1697 ability_id: "common.abilities.sword.defensive_riposte",
1698 position: TopLeftWithMarginsOn(state.ids.sword_bg, 457.0, 766.0),
1699 },
1700 SkillIcon::Ability {
1701 skill: Skill::Sword(SwordSkill::DefensiveDisengage),
1702 ability_id: "common.abilities.sword.defensive_disengage",
1703 position: TopLeftWithMarginsOn(state.ids.sword_bg, 457.0, 855.0),
1704 },
1705 SkillIcon::Ability {
1706 skill: Skill::Sword(SwordSkill::CripplingGouge),
1707 ability_id: "common.abilities.sword.crippling_gouge",
1708 position: TopLeftWithMarginsOn(state.ids.sword_bg, 53.0, 766.0),
1709 },
1710 SkillIcon::Ability {
1711 skill: Skill::Sword(SwordSkill::CripplingHamstring),
1712 ability_id: "common.abilities.sword.crippling_hamstring",
1713 position: TopLeftWithMarginsOn(state.ids.sword_bg, 142.0, 766.0),
1714 },
1715 SkillIcon::Ability {
1716 skill: Skill::Sword(SwordSkill::CleavingWhirlwindSlice),
1717 ability_id: "common.abilities.sword.cleaving_whirlwind_slice",
1718 position: TopLeftWithMarginsOn(state.ids.sword_bg, 53.0, 91.0),
1719 },
1720 SkillIcon::Ability {
1721 skill: Skill::Sword(SwordSkill::CleavingEarthSplitter),
1722 ability_id: "common.abilities.sword.cleaving_earth_splitter",
1723 position: TopLeftWithMarginsOn(state.ids.sword_bg, 142.0, 91.0),
1724 },
1725 SkillIcon::Ability {
1726 skill: Skill::Sword(SwordSkill::HeavyFortitude),
1727 ability_id: "common.abilities.sword.heavy_fortitude",
1728 position: TopLeftWithMarginsOn(state.ids.sword_bg, 368.0, 2.0),
1729 },
1730 SkillIcon::Ability {
1731 skill: Skill::Sword(SwordSkill::HeavyPillarThrust),
1732 ability_id: "common.abilities.sword.heavy_pillar_thrust",
1733 position: TopLeftWithMarginsOn(state.ids.sword_bg, 368.0, 91.0),
1734 },
1735 SkillIcon::Ability {
1736 skill: Skill::Sword(SwordSkill::AgileDancingEdge),
1737 ability_id: "common.abilities.sword.agile_dancing_edge",
1738 position: TopLeftWithMarginsOn(state.ids.sword_bg, 53.0, 385.0),
1739 },
1740 SkillIcon::Ability {
1741 skill: Skill::Sword(SwordSkill::AgileFlurry),
1742 ability_id: "common.abilities.sword.agile_flurry",
1743 position: TopLeftWithMarginsOn(state.ids.sword_bg, 53.0, 473.0),
1744 },
1745 SkillIcon::Ability {
1746 skill: Skill::Sword(SwordSkill::DefensiveStalwartSword),
1747 ability_id: "common.abilities.sword.defensive_stalwart_sword",
1748 position: TopLeftWithMarginsOn(state.ids.sword_bg, 368.0, 766.0),
1749 },
1750 SkillIcon::Ability {
1751 skill: Skill::Sword(SwordSkill::DefensiveDeflect),
1752 ability_id: "common.abilities.sword.defensive_deflect",
1753 position: TopLeftWithMarginsOn(state.ids.sword_bg, 368.0, 855.0),
1754 },
1755 SkillIcon::Ability {
1756 skill: Skill::Sword(SwordSkill::CripplingEviscerate),
1757 ability_id: "common.abilities.sword.crippling_eviscerate",
1758 position: TopLeftWithMarginsOn(state.ids.sword_bg, 142.0, 855.0),
1759 },
1760 SkillIcon::Ability {
1761 skill: Skill::Sword(SwordSkill::CripplingBloodyGash),
1762 ability_id: "common.abilities.sword.crippling_bloody_gash",
1763 position: TopLeftWithMarginsOn(state.ids.sword_bg, 53.0, 855.0),
1764 },
1765 SkillIcon::Ability {
1766 skill: Skill::Sword(SwordSkill::CleavingBladeFever),
1767 ability_id: "common.abilities.sword.cleaving_blade_fever",
1768 position: TopLeftWithMarginsOn(state.ids.sword_bg, 53.0, 2.0),
1769 },
1770 SkillIcon::Ability {
1771 skill: Skill::Sword(SwordSkill::CleavingSkySplitter),
1772 ability_id: "common.abilities.sword.cleaving_sky_splitter",
1773 position: TopLeftWithMarginsOn(state.ids.sword_bg, 142.0, 2.0),
1774 },
1775 ];
1776
1777 state.update(|s| {
1778 s.ids
1779 .skills
1780 .resize(skill_buttons.len(), &mut ui.widget_id_generator())
1781 });
1782 state.update(|s| {
1783 s.ids
1784 .skill_lock_imgs
1785 .resize(skill_buttons.len(), &mut ui.widget_id_generator())
1786 });
1787
1788 self.handle_skill_buttons(skill_buttons, ui, &mut events, diary_tooltip, state);
1789 events
1790 }
1791
1792 fn handle_axe_skills_window(
1793 &mut self,
1794 diary_tooltip: &Tooltip,
1795 state: &mut State<DiaryState>,
1796 ui: &mut UiCell,
1797 mut events: Vec<Event>,
1798 ) -> Vec<Event> {
1799 Image::new(self.imgs.axe_bg)
1801 .wh([924.0, 619.0])
1802 .mid_top_with_margin_on(state.ids.content_align, 65.0)
1803 .color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
1804 .set(state.ids.axe_bg, ui);
1805
1806 use PositionSpecifier::TopLeftWithMarginsOn;
1807 let skill_buttons = &[
1808 SkillIcon::Ability {
1809 skill: Skill::Axe(AxeSkill::BrutalSwing),
1810 ability_id: "common.abilities.axe.brutal_swing",
1811 position: TopLeftWithMarginsOn(state.ids.axe_bg, 387.0, 424.0),
1812 },
1813 SkillIcon::Ability {
1814 skill: Skill::Axe(AxeSkill::Berserk),
1815 ability_id: "common.abilities.axe.berserk",
1816 position: TopLeftWithMarginsOn(state.ids.axe_bg, 287.0, 374.0),
1817 },
1818 SkillIcon::Ability {
1819 skill: Skill::Axe(AxeSkill::RisingTide),
1820 ability_id: "common.abilities.axe.rising_tide",
1821 position: TopLeftWithMarginsOn(state.ids.axe_bg, 287.0, 474.0),
1822 },
1823 SkillIcon::Ability {
1824 skill: Skill::Axe(AxeSkill::SavageSense),
1825 ability_id: "common.abilities.axe.savage_sense",
1826 position: TopLeftWithMarginsOn(state.ids.axe_bg, 187.0, 324.0),
1827 },
1828 SkillIcon::Ability {
1829 skill: Skill::Axe(AxeSkill::AdrenalineRush),
1830 ability_id: "common.abilities.axe.adrenaline_rush",
1831 position: TopLeftWithMarginsOn(state.ids.axe_bg, 187.0, 524.0),
1832 },
1833 SkillIcon::Ability {
1834 skill: Skill::Axe(AxeSkill::Execute),
1835 ability_id: "common.abilities.axe.execute",
1836 position: TopLeftWithMarginsOn(state.ids.axe_bg, 187.0, 424.0),
1837 },
1838 SkillIcon::Ability {
1839 skill: Skill::Axe(AxeSkill::Maelstrom),
1840 ability_id: "common.abilities.axe.maelstrom",
1841 position: TopLeftWithMarginsOn(state.ids.axe_bg, 4.0, 424.0),
1842 },
1843 SkillIcon::Ability {
1844 skill: Skill::Axe(AxeSkill::Rake),
1845 ability_id: "common.abilities.axe.rake",
1846 position: TopLeftWithMarginsOn(state.ids.axe_bg, 507.0, 325.0),
1847 },
1848 SkillIcon::Ability {
1849 skill: Skill::Axe(AxeSkill::Bloodfeast),
1850 ability_id: "common.abilities.axe.bloodfeast",
1851 position: TopLeftWithMarginsOn(state.ids.axe_bg, 387.0, 74.0),
1852 },
1853 SkillIcon::Ability {
1854 skill: Skill::Axe(AxeSkill::FierceRaze),
1855 ability_id: "common.abilities.axe.fierce_raze",
1856 position: TopLeftWithMarginsOn(state.ids.axe_bg, 387.0, 174.0),
1857 },
1858 SkillIcon::Ability {
1859 skill: Skill::Axe(AxeSkill::Furor),
1860 ability_id: "common.abilities.axe.furor",
1861 position: TopLeftWithMarginsOn(state.ids.axe_bg, 287.0, 24.0),
1862 },
1863 SkillIcon::Ability {
1864 skill: Skill::Axe(AxeSkill::Fracture),
1865 ability_id: "common.abilities.axe.fracture",
1866 position: TopLeftWithMarginsOn(state.ids.axe_bg, 287.0, 224.0),
1867 },
1868 SkillIcon::Ability {
1869 skill: Skill::Axe(AxeSkill::Lacerate),
1870 ability_id: "common.abilities.axe.lacerate",
1871 position: TopLeftWithMarginsOn(state.ids.axe_bg, 287.0, 124.0),
1872 },
1873 SkillIcon::Ability {
1874 skill: Skill::Axe(AxeSkill::Riptide),
1875 ability_id: "common.abilities.axe.riptide",
1876 position: TopLeftWithMarginsOn(state.ids.axe_bg, 104.0, 124.0),
1877 },
1878 SkillIcon::Ability {
1879 skill: Skill::Axe(AxeSkill::SkullBash),
1880 ability_id: "common.abilities.axe.skull_bash",
1881 position: TopLeftWithMarginsOn(state.ids.axe_bg, 507.0, 523.0),
1882 },
1883 SkillIcon::Ability {
1884 skill: Skill::Axe(AxeSkill::Sunder),
1885 ability_id: "common.abilities.axe.sunder",
1886 position: TopLeftWithMarginsOn(state.ids.axe_bg, 387.0, 674.0),
1887 },
1888 SkillIcon::Ability {
1889 skill: Skill::Axe(AxeSkill::Plunder),
1890 ability_id: "common.abilities.axe.plunder",
1891 position: TopLeftWithMarginsOn(state.ids.axe_bg, 387.0, 774.0),
1892 },
1893 SkillIcon::Ability {
1894 skill: Skill::Axe(AxeSkill::Defiance),
1895 ability_id: "common.abilities.axe.defiance",
1896 position: TopLeftWithMarginsOn(state.ids.axe_bg, 287.0, 624.0),
1897 },
1898 SkillIcon::Ability {
1899 skill: Skill::Axe(AxeSkill::Keelhaul),
1900 ability_id: "common.abilities.axe.keelhaul",
1901 position: TopLeftWithMarginsOn(state.ids.axe_bg, 287.0, 824.0),
1902 },
1903 SkillIcon::Ability {
1904 skill: Skill::Axe(AxeSkill::Bulkhead),
1905 ability_id: "common.abilities.axe.bulkhead",
1906 position: TopLeftWithMarginsOn(state.ids.axe_bg, 287.0, 724.0),
1907 },
1908 SkillIcon::Ability {
1909 skill: Skill::Axe(AxeSkill::Capsize),
1910 ability_id: "common.abilities.axe.capsize",
1911 position: TopLeftWithMarginsOn(state.ids.axe_bg, 104.0, 724.0),
1912 },
1913 ];
1914
1915 state.update(|s| {
1916 s.ids
1917 .skills
1918 .resize(skill_buttons.len(), &mut ui.widget_id_generator())
1919 });
1920 state.update(|s| {
1921 s.ids
1922 .skill_lock_imgs
1923 .resize(skill_buttons.len(), &mut ui.widget_id_generator())
1924 });
1925
1926 self.handle_skill_buttons(skill_buttons, ui, &mut events, diary_tooltip, state);
1927 events
1928 }
1929
1930 fn handle_hammer_skills_window(
1931 &mut self,
1932 diary_tooltip: &Tooltip,
1933 state: &mut State<DiaryState>,
1934 ui: &mut UiCell,
1935 mut events: Vec<Event>,
1936 ) -> Vec<Event> {
1937 Image::new(self.imgs.hammer_bg)
1939 .wh([924.0, 619.0])
1940 .mid_top_with_margin_on(state.ids.content_align, 65.0)
1941 .color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
1942 .set(state.ids.hammer_bg, ui);
1943
1944 use PositionSpecifier::TopLeftWithMarginsOn;
1945 let skill_buttons = &[
1946 SkillIcon::Ability {
1947 skill: Skill::Hammer(HammerSkill::ScornfulSwipe),
1948 ability_id: "common.abilities.hammer.scornful_swipe",
1949 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 455.0, 424.0),
1950 },
1951 SkillIcon::Ability {
1952 skill: Skill::Hammer(HammerSkill::Tremor),
1953 ability_id: "common.abilities.hammer.tremor",
1954 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 398.0, 172.0),
1955 },
1956 SkillIcon::Ability {
1957 skill: Skill::Hammer(HammerSkill::VigorousBash),
1958 ability_id: "common.abilities.hammer.vigorous_bash",
1959 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 398.0, 272.0),
1960 },
1961 SkillIcon::Ability {
1962 skill: Skill::Hammer(HammerSkill::Retaliate),
1963 ability_id: "common.abilities.hammer.retaliate",
1964 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 284.0, 122.0),
1965 },
1966 SkillIcon::Ability {
1967 skill: Skill::Hammer(HammerSkill::SpineCracker),
1968 ability_id: "common.abilities.hammer.spine_cracker",
1969 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 284.0, 222.0),
1970 },
1971 SkillIcon::Ability {
1972 skill: Skill::Hammer(HammerSkill::Breach),
1973 ability_id: "common.abilities.hammer.breach",
1974 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 284.0, 322.0),
1975 },
1976 SkillIcon::Ability {
1977 skill: Skill::Hammer(HammerSkill::IronTempest),
1978 ability_id: "common.abilities.hammer.iron_tempest",
1979 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 170.0, 172.0),
1980 },
1981 SkillIcon::Ability {
1982 skill: Skill::Hammer(HammerSkill::Upheaval),
1983 ability_id: "common.abilities.hammer.upheaval",
1984 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 170.0, 272.0),
1985 },
1986 SkillIcon::Ability {
1987 skill: Skill::Hammer(HammerSkill::Thunderclap),
1988 ability_id: "common.abilities.hammer.thunderclap",
1989 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 56.0, 172.0),
1990 },
1991 SkillIcon::Ability {
1992 skill: Skill::Hammer(HammerSkill::SeismicShock),
1993 ability_id: "common.abilities.hammer.seismic_shock",
1994 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 56.0, 272.0),
1995 },
1996 SkillIcon::Ability {
1997 skill: Skill::Hammer(HammerSkill::HeavyWhorl),
1998 ability_id: "common.abilities.hammer.heavy_whorl",
1999 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 398.0, 576.0),
2000 },
2001 SkillIcon::Ability {
2002 skill: Skill::Hammer(HammerSkill::Intercept),
2003 ability_id: "common.abilities.hammer.intercept",
2004 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 398.0, 676.0),
2005 },
2006 SkillIcon::Ability {
2007 skill: Skill::Hammer(HammerSkill::PileDriver),
2008 ability_id: "common.abilities.hammer.pile_driver",
2009 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 284.0, 526.0),
2010 },
2011 SkillIcon::Ability {
2012 skill: Skill::Hammer(HammerSkill::LungPummel),
2013 ability_id: "common.abilities.hammer.lung_pummel",
2014 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 284.0, 626.0),
2015 },
2016 SkillIcon::Ability {
2017 skill: Skill::Hammer(HammerSkill::HelmCrusher),
2018 ability_id: "common.abilities.hammer.helm_crusher",
2019 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 284.0, 726.0),
2020 },
2021 SkillIcon::Ability {
2022 skill: Skill::Hammer(HammerSkill::Rampart),
2023 ability_id: "common.abilities.hammer.rampart",
2024 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 170.0, 576.0),
2025 },
2026 SkillIcon::Ability {
2027 skill: Skill::Hammer(HammerSkill::Tenacity),
2028 ability_id: "common.abilities.hammer.tenacity",
2029 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 170.0, 676.0),
2030 },
2031 SkillIcon::Ability {
2032 skill: Skill::Hammer(HammerSkill::Earthshaker),
2033 ability_id: "common.abilities.hammer.earthshaker",
2034 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 56.0, 576.0),
2035 },
2036 SkillIcon::Ability {
2037 skill: Skill::Hammer(HammerSkill::Judgement),
2038 ability_id: "common.abilities.hammer.judgement",
2039 position: TopLeftWithMarginsOn(state.ids.hammer_bg, 56.0, 676.0),
2040 },
2041 ];
2042
2043 state.update(|s| {
2044 s.ids
2045 .skills
2046 .resize(skill_buttons.len(), &mut ui.widget_id_generator())
2047 });
2048 state.update(|s| {
2049 s.ids
2050 .skill_lock_imgs
2051 .resize(skill_buttons.len(), &mut ui.widget_id_generator())
2052 });
2053
2054 self.handle_skill_buttons(skill_buttons, ui, &mut events, diary_tooltip, state);
2055 events
2056 }
2057
2058 fn handle_sceptre_skills_window(
2059 &mut self,
2060 diary_tooltip: &Tooltip,
2061 state: &mut State<DiaryState>,
2062 ui: &mut UiCell,
2063 mut events: Vec<Event>,
2064 ) -> Vec<Event> {
2065 let tree_title = &self.localized_strings.get_msg("common-weapons-sceptre");
2067
2068 Text::new(tree_title)
2069 .mid_top_with_margin_on(state.ids.content_align, 2.0)
2070 .font_id(self.fonts.cyri.conrod_id)
2071 .font_size(self.fonts.cyri.scale(34))
2072 .color(TEXT_COLOR)
2073 .set(state.ids.tree_title_txt, ui);
2074
2075 let skills_top_l = 5;
2078 let skills_top_r = 5;
2079 let skills_bot_l = 5;
2080 let skills_bot_r = 0;
2081
2082 self.setup_state_for_skill_icons(
2083 state,
2084 ui,
2085 skills_top_l,
2086 skills_top_r,
2087 skills_bot_l,
2088 skills_bot_r,
2089 );
2090
2091 use skills::SceptreSkill::*;
2093 Image::new(animate_by_pulse(
2095 &self
2096 .item_imgs
2097 .img_ids_or_not_found_img(ItemKey::Simple("example_sceptre".to_string())),
2098 self.pulse,
2099 ))
2100 .wh(ART_SIZE)
2101 .middle_of(state.ids.content_align)
2102 .color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
2103 .set(state.ids.sceptre_render, ui);
2104 use PositionSpecifier::MidTopWithMarginOn;
2105 let skill_buttons = &[
2106 SkillIcon::Descriptive {
2111 title: "hud-skill-sc_lifesteal_title",
2112 desc: "hud-skill-sc_lifesteal",
2113 image: self.imgs.skill_sceptre_lifesteal,
2114 position: MidTopWithMarginOn(state.ids.skills_top_l[0], 3.0),
2115 id: state.ids.skill_sceptre_lifesteal_0,
2116 },
2117 SkillIcon::Unlockable {
2118 skill: Skill::Sceptre(LDamage),
2119 image: self.imgs.magic_damage_skill,
2120 position: MidTopWithMarginOn(state.ids.skills_top_l[1], 3.0),
2121 id: state.ids.skill_sceptre_lifesteal_1,
2122 },
2123 SkillIcon::Unlockable {
2124 skill: Skill::Sceptre(LRange),
2125 image: self.imgs.magic_distance_skill,
2126 position: MidTopWithMarginOn(state.ids.skills_top_l[2], 3.0),
2127 id: state.ids.skill_sceptre_lifesteal_2,
2128 },
2129 SkillIcon::Unlockable {
2130 skill: Skill::Sceptre(LLifesteal),
2131 image: self.imgs.magic_lifesteal_skill,
2132 position: MidTopWithMarginOn(state.ids.skills_top_l[3], 3.0),
2133 id: state.ids.skill_sceptre_lifesteal_3,
2134 },
2135 SkillIcon::Unlockable {
2136 skill: Skill::Sceptre(LRegen),
2137 image: self.imgs.magic_energy_regen_skill,
2138 position: MidTopWithMarginOn(state.ids.skills_top_l[4], 3.0),
2139 id: state.ids.skill_sceptre_lifesteal_4,
2140 },
2141 SkillIcon::Descriptive {
2143 title: "hud-skill-sc_heal_title",
2144 desc: "hud-skill-sc_heal",
2145 image: self.imgs.skill_sceptre_heal,
2146 position: MidTopWithMarginOn(state.ids.skills_top_r[0], 3.0),
2147 id: state.ids.skill_sceptre_heal_0,
2148 },
2149 SkillIcon::Unlockable {
2150 skill: Skill::Sceptre(HHeal),
2151 image: self.imgs.heal_heal_skill,
2152 position: MidTopWithMarginOn(state.ids.skills_top_r[1], 3.0),
2153 id: state.ids.skill_sceptre_heal_1,
2154 },
2155 SkillIcon::Unlockable {
2156 skill: Skill::Sceptre(HDuration),
2157 image: self.imgs.heal_duration_skill,
2158 position: MidTopWithMarginOn(state.ids.skills_top_r[2], 3.0),
2159 id: state.ids.skill_sceptre_heal_2,
2160 },
2161 SkillIcon::Unlockable {
2162 skill: Skill::Sceptre(HRange),
2163 image: self.imgs.heal_radius_skill,
2164 position: MidTopWithMarginOn(state.ids.skills_top_r[3], 3.0),
2165 id: state.ids.skill_sceptre_heal_3,
2166 },
2167 SkillIcon::Unlockable {
2168 skill: Skill::Sceptre(HCost),
2169 image: self.imgs.heal_cost_skill,
2170 position: MidTopWithMarginOn(state.ids.skills_top_r[4], 3.0),
2171 id: state.ids.skill_sceptre_heal_4,
2172 },
2173 SkillIcon::Unlockable {
2175 skill: Skill::Sceptre(UnlockAura),
2176 image: self.imgs.skill_sceptre_aura,
2177 position: MidTopWithMarginOn(state.ids.skills_bot_l[0], 3.0),
2178 id: state.ids.skill_sceptre_aura_0,
2179 },
2180 SkillIcon::Unlockable {
2181 skill: Skill::Sceptre(AStrength),
2182 image: self.imgs.buff_damage_skill,
2183 position: MidTopWithMarginOn(state.ids.skills_bot_l[1], 3.0),
2184 id: state.ids.skill_sceptre_aura_1,
2185 },
2186 SkillIcon::Unlockable {
2187 skill: Skill::Sceptre(ADuration),
2188 image: self.imgs.buff_duration_skill,
2189 position: MidTopWithMarginOn(state.ids.skills_bot_l[2], 3.0),
2190 id: state.ids.skill_sceptre_aura_2,
2191 },
2192 SkillIcon::Unlockable {
2193 skill: Skill::Sceptre(ARange),
2194 image: self.imgs.buff_radius_skill,
2195 position: MidTopWithMarginOn(state.ids.skills_bot_l[3], 3.0),
2196 id: state.ids.skill_sceptre_aura_3,
2197 },
2198 SkillIcon::Unlockable {
2199 skill: Skill::Sceptre(ACost),
2200 image: self.imgs.buff_cost_skill,
2201 position: MidTopWithMarginOn(state.ids.skills_bot_l[4], 3.0),
2202 id: state.ids.skill_sceptre_aura_4,
2203 },
2204 ];
2205
2206 self.handle_skill_buttons(skill_buttons, ui, &mut events, diary_tooltip, state);
2207 events
2208 }
2209
2210 fn handle_bow_skills_window(
2211 &mut self,
2212 diary_tooltip: &Tooltip,
2213 state: &mut State<DiaryState>,
2214 ui: &mut UiCell,
2215 mut events: Vec<Event>,
2216 ) -> Vec<Event> {
2217 let tree_title = &self.localized_strings.get_msg("common-weapons-bow");
2219
2220 Text::new(tree_title)
2221 .mid_top_with_margin_on(state.ids.content_align, 2.0)
2222 .font_id(self.fonts.cyri.conrod_id)
2223 .font_size(self.fonts.cyri.scale(34))
2224 .color(TEXT_COLOR)
2225 .set(state.ids.tree_title_txt, ui);
2226
2227 let skills_top_l = 6;
2230 let skills_top_r = 4;
2231 let skills_bot_l = 5;
2232 let skills_bot_r = 1;
2233
2234 self.setup_state_for_skill_icons(
2235 state,
2236 ui,
2237 skills_top_l,
2238 skills_top_r,
2239 skills_bot_l,
2240 skills_bot_r,
2241 );
2242
2243 use skills::BowSkill::*;
2245 Image::new(animate_by_pulse(
2247 &self
2248 .item_imgs
2249 .img_ids_or_not_found_img(ItemKey::Simple("example_bow".to_string())),
2250 self.pulse,
2251 ))
2252 .wh(ART_SIZE)
2253 .middle_of(state.ids.content_align)
2254 .set(state.ids.bow_render, ui);
2255 use PositionSpecifier::MidTopWithMarginOn;
2256 let skill_buttons = &[
2257 SkillIcon::Descriptive {
2262 title: "hud-skill-bow_charged_title",
2263 desc: "hud-skill-bow_charged",
2264 image: self.imgs.bow_m1,
2265 position: MidTopWithMarginOn(state.ids.skills_top_l[0], 3.0),
2266 id: state.ids.skill_bow_charged_0,
2267 },
2268 SkillIcon::Unlockable {
2269 skill: Skill::Bow(CDamage),
2270 image: self.imgs.physical_damage_skill,
2271 position: MidTopWithMarginOn(state.ids.skills_top_l[1], 3.0),
2272 id: state.ids.skill_bow_charged_1,
2273 },
2274 SkillIcon::Unlockable {
2275 skill: Skill::Bow(CRegen),
2276 image: self.imgs.physical_energy_regen_skill,
2277 position: MidTopWithMarginOn(state.ids.skills_top_l[2], 3.0),
2278 id: state.ids.skill_bow_charged_2,
2279 },
2280 SkillIcon::Unlockable {
2281 skill: Skill::Bow(CKnockback),
2282 image: self.imgs.physical_knockback_skill,
2283 position: MidTopWithMarginOn(state.ids.skills_top_l[3], 3.0),
2284 id: state.ids.skill_bow_charged_3,
2285 },
2286 SkillIcon::Unlockable {
2287 skill: Skill::Bow(CSpeed),
2288 image: self.imgs.physical_speed_skill,
2289 position: MidTopWithMarginOn(state.ids.skills_top_l[4], 3.0),
2290 id: state.ids.skill_bow_charged_4,
2291 },
2292 SkillIcon::Unlockable {
2293 skill: Skill::Bow(CMove),
2294 image: self.imgs.physical_speed_skill,
2295 position: MidTopWithMarginOn(state.ids.skills_top_l[5], 3.0),
2296 id: state.ids.skill_bow_charged_5,
2297 },
2298 SkillIcon::Descriptive {
2300 title: "hud-skill-bow_repeater_title",
2301 desc: "hud-skill-bow_repeater",
2302 image: self.imgs.bow_m2,
2303 position: MidTopWithMarginOn(state.ids.skills_top_r[0], 3.0),
2304 id: state.ids.skill_bow_repeater_0,
2305 },
2306 SkillIcon::Unlockable {
2307 skill: Skill::Bow(RDamage),
2308 image: self.imgs.physical_damage_skill,
2309 position: MidTopWithMarginOn(state.ids.skills_top_r[1], 3.0),
2310 id: state.ids.skill_bow_repeater_1,
2311 },
2312 SkillIcon::Unlockable {
2313 skill: Skill::Bow(RCost),
2314 image: self.imgs.physical_cost_skill,
2315 position: MidTopWithMarginOn(state.ids.skills_top_r[2], 3.0),
2316 id: state.ids.skill_bow_repeater_2,
2317 },
2318 SkillIcon::Unlockable {
2319 skill: Skill::Bow(RSpeed),
2320 image: self.imgs.physical_speed_skill,
2321 position: MidTopWithMarginOn(state.ids.skills_top_r[3], 3.0),
2322 id: state.ids.skill_bow_repeater_3,
2323 },
2324 SkillIcon::Unlockable {
2326 skill: Skill::Bow(UnlockShotgun),
2327 image: self.imgs.skill_bow_jump_burst,
2328 position: MidTopWithMarginOn(state.ids.skills_bot_l[0], 3.0),
2329 id: state.ids.skill_bow_shotgun_0,
2330 },
2331 SkillIcon::Unlockable {
2332 skill: Skill::Bow(SDamage),
2333 image: self.imgs.physical_damage_skill,
2334 position: MidTopWithMarginOn(state.ids.skills_bot_l[1], 3.0),
2335 id: state.ids.skill_bow_shotgun_1,
2336 },
2337 SkillIcon::Unlockable {
2338 skill: Skill::Bow(SCost),
2339 image: self.imgs.physical_cost_skill,
2340 position: MidTopWithMarginOn(state.ids.skills_bot_l[2], 3.0),
2341 id: state.ids.skill_bow_shotgun_2,
2342 },
2343 SkillIcon::Unlockable {
2344 skill: Skill::Bow(SArrows),
2345 image: self.imgs.physical_amount_skill,
2346 position: MidTopWithMarginOn(state.ids.skills_bot_l[3], 3.0),
2347 id: state.ids.skill_bow_shotgun_3,
2348 },
2349 SkillIcon::Unlockable {
2350 skill: Skill::Bow(SSpread),
2351 image: self.imgs.physical_explosion_skill,
2352 position: MidTopWithMarginOn(state.ids.skills_bot_l[4], 3.0),
2353 id: state.ids.skill_bow_shotgun_4,
2354 },
2355 SkillIcon::Unlockable {
2357 skill: Skill::Bow(ProjSpeed),
2358 image: self.imgs.physical_projectile_speed_skill,
2359 position: MidTopWithMarginOn(state.ids.skills_bot_r[0], 3.0),
2360 id: state.ids.skill_bow_passive_0,
2361 },
2362 ];
2363
2364 self.handle_skill_buttons(skill_buttons, ui, &mut events, diary_tooltip, state);
2365 events
2366 }
2367
2368 fn handle_staff_skills_window(
2369 &mut self,
2370 diary_tooltip: &Tooltip,
2371 state: &mut State<DiaryState>,
2372 ui: &mut UiCell,
2373 mut events: Vec<Event>,
2374 ) -> Vec<Event> {
2375 let tree_title = &self.localized_strings.get_msg("common-weapons-staff");
2377
2378 Text::new(tree_title)
2379 .mid_top_with_margin_on(state.ids.content_align, 2.0)
2380 .font_id(self.fonts.cyri.conrod_id)
2381 .font_size(self.fonts.cyri.scale(34))
2382 .color(TEXT_COLOR)
2383 .set(state.ids.tree_title_txt, ui);
2384
2385 let skills_top_l = 4;
2388 let skills_top_r = 5;
2389 let skills_bot_l = 5;
2390 let skills_bot_r = 0;
2391
2392 self.setup_state_for_skill_icons(
2393 state,
2394 ui,
2395 skills_top_l,
2396 skills_top_r,
2397 skills_bot_l,
2398 skills_bot_r,
2399 );
2400
2401 use skills::StaffSkill::*;
2403 Image::new(animate_by_pulse(
2405 &self
2406 .item_imgs
2407 .img_ids_or_not_found_img(ItemKey::Simple("example_staff_fire".to_string())),
2408 self.pulse,
2409 ))
2410 .wh(ART_SIZE)
2411 .middle_of(state.ids.content_align)
2412 .color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
2413 .set(state.ids.staff_render, ui);
2414
2415 use PositionSpecifier::MidTopWithMarginOn;
2416 let skill_buttons = &[
2417 SkillIcon::Descriptive {
2422 title: "hud-skill-st_fireball_title",
2423 desc: "hud-skill-st_fireball",
2424 image: self.imgs.fireball,
2425 position: MidTopWithMarginOn(state.ids.skills_top_l[0], 3.0),
2426 id: state.ids.skill_staff_basic_0,
2427 },
2428 SkillIcon::Unlockable {
2429 skill: Skill::Staff(BDamage),
2430 image: self.imgs.magic_damage_skill,
2431 position: MidTopWithMarginOn(state.ids.skills_top_l[1], 3.0),
2432 id: state.ids.skill_staff_basic_1,
2433 },
2434 SkillIcon::Unlockable {
2435 skill: Skill::Staff(BRegen),
2436 image: self.imgs.magic_energy_regen_skill,
2437 position: MidTopWithMarginOn(state.ids.skills_top_l[2], 3.0),
2438 id: state.ids.skill_staff_basic_2,
2439 },
2440 SkillIcon::Unlockable {
2441 skill: Skill::Staff(BRadius),
2442 image: self.imgs.magic_radius_skill,
2443 position: MidTopWithMarginOn(state.ids.skills_top_l[3], 3.0),
2444 id: state.ids.skill_staff_basic_3,
2445 },
2446 SkillIcon::Descriptive {
2448 title: "hud-skill-st_flamethrower_title",
2449 desc: "hud-skill-st_flamethrower",
2450 image: self.imgs.flamethrower,
2451 position: MidTopWithMarginOn(state.ids.skills_top_r[0], 3.0),
2452 id: state.ids.skill_staff_beam_0,
2453 },
2454 SkillIcon::Unlockable {
2455 skill: Skill::Staff(FDamage),
2456 image: self.imgs.magic_damage_skill,
2457 position: MidTopWithMarginOn(state.ids.skills_top_r[1], 3.0),
2458 id: state.ids.skill_staff_beam_1,
2459 },
2460 SkillIcon::Unlockable {
2461 skill: Skill::Staff(FDrain),
2462 image: self.imgs.magic_energy_drain_skill,
2463 position: MidTopWithMarginOn(state.ids.skills_top_r[2], 3.0),
2464 id: state.ids.skill_staff_beam_2,
2465 },
2466 SkillIcon::Unlockable {
2467 skill: Skill::Staff(FRange),
2468 image: self.imgs.magic_radius_skill,
2469 position: MidTopWithMarginOn(state.ids.skills_top_r[3], 3.0),
2470 id: state.ids.skill_staff_beam_3,
2471 },
2472 SkillIcon::Unlockable {
2473 skill: Skill::Staff(FVelocity),
2474 image: self.imgs.magic_projectile_speed_skill,
2475 position: MidTopWithMarginOn(state.ids.skills_top_r[4], 3.0),
2476 id: state.ids.skill_staff_beam_4,
2477 },
2478 SkillIcon::Unlockable {
2480 skill: Skill::Staff(UnlockShockwave),
2481 image: self.imgs.fire_aoe,
2482 position: MidTopWithMarginOn(state.ids.skills_bot_l[0], 3.0),
2483 id: state.ids.skill_staff_shockwave_0,
2484 },
2485 SkillIcon::Unlockable {
2486 skill: Skill::Staff(SDamage),
2487 image: self.imgs.magic_damage_skill,
2488 position: MidTopWithMarginOn(state.ids.skills_bot_l[1], 3.0),
2489 id: state.ids.skill_staff_shockwave_1,
2490 },
2491 SkillIcon::Unlockable {
2492 skill: Skill::Staff(SKnockback),
2493 image: self.imgs.magic_knockback_skill,
2494 position: MidTopWithMarginOn(state.ids.skills_bot_l[2], 3.0),
2495 id: state.ids.skill_staff_shockwave_2,
2496 },
2497 SkillIcon::Unlockable {
2498 skill: Skill::Staff(SCost),
2499 image: self.imgs.magic_cost_skill,
2500 position: MidTopWithMarginOn(state.ids.skills_bot_l[3], 3.0),
2501 id: state.ids.skill_staff_shockwave_3,
2502 },
2503 SkillIcon::Unlockable {
2504 skill: Skill::Staff(SRange),
2505 image: self.imgs.magic_radius_skill,
2506 position: MidTopWithMarginOn(state.ids.skills_bot_l[4], 3.0),
2507 id: state.ids.skill_staff_shockwave_4,
2508 },
2509 ];
2510
2511 self.handle_skill_buttons(skill_buttons, ui, &mut events, diary_tooltip, state);
2512 events
2513 }
2514
2515 fn handle_mining_skills_window(
2516 &mut self,
2517 diary_tooltip: &Tooltip,
2518 state: &mut State<DiaryState>,
2519 ui: &mut UiCell,
2520 mut events: Vec<Event>,
2521 ) -> Vec<Event> {
2522 let tree_title = &self.localized_strings.get_msg("common-tool-mining");
2524
2525 Text::new(tree_title)
2526 .mid_top_with_margin_on(state.ids.content_align, 2.0)
2527 .font_id(self.fonts.cyri.conrod_id)
2528 .font_size(self.fonts.cyri.scale(34))
2529 .color(TEXT_COLOR)
2530 .set(state.ids.tree_title_txt, ui);
2531
2532 let skills_top_l = 4;
2535 let skills_top_r = 0;
2536 let skills_bot_l = 0;
2537 let skills_bot_r = 0;
2538
2539 self.setup_state_for_skill_icons(
2540 state,
2541 ui,
2542 skills_top_l,
2543 skills_top_r,
2544 skills_bot_l,
2545 skills_bot_r,
2546 );
2547
2548 use skills::MiningSkill::*;
2550 Image::new(animate_by_pulse(
2552 &self
2553 .item_imgs
2554 .img_ids_or_not_found_img(ItemKey::Simple("example_pick".to_string())),
2555 self.pulse,
2556 ))
2557 .wh(ART_SIZE)
2558 .middle_of(state.ids.content_align)
2559 .color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
2560 .set(state.ids.pick_render, ui);
2561
2562 use PositionSpecifier::MidTopWithMarginOn;
2563 let skill_buttons = &[
2564 SkillIcon::Descriptive {
2569 title: "hud-skill-pick_strike_title",
2570 desc: "hud-skill-pick_strike",
2571 image: self.imgs.pickaxe,
2572 position: MidTopWithMarginOn(state.ids.skills_top_l[0], 3.0),
2573 id: state.ids.skill_pick_m1,
2574 },
2575 SkillIcon::Unlockable {
2576 skill: Skill::Pick(Speed),
2577 image: self.imgs.pickaxe_speed_skill,
2578 position: MidTopWithMarginOn(state.ids.skills_top_l[1], 3.0),
2579 id: state.ids.skill_pick_m1_0,
2580 },
2581 SkillIcon::Unlockable {
2582 skill: Skill::Pick(OreGain),
2583 image: self.imgs.pickaxe_oregain_skill,
2584 position: MidTopWithMarginOn(state.ids.skills_top_l[2], 3.0),
2585 id: state.ids.skill_pick_m1_1,
2586 },
2587 SkillIcon::Unlockable {
2588 skill: Skill::Pick(GemGain),
2589 image: self.imgs.pickaxe_gemgain_skill,
2590 position: MidTopWithMarginOn(state.ids.skills_top_l[3], 3.0),
2591 id: state.ids.skill_pick_m1_2,
2592 },
2593 ];
2594
2595 self.handle_skill_buttons(skill_buttons, ui, &mut events, diary_tooltip, state);
2596 events
2597 }
2598
2599 fn handle_skill_buttons(
2600 &mut self,
2601 icons: &[SkillIcon],
2602 ui: &mut UiCell,
2603 events: &mut Vec<Event>,
2604 diary_tooltip: &Tooltip,
2605 state: &mut State<DiaryState>,
2606 ) {
2607 for (i, icon) in icons.iter().enumerate() {
2608 match icon {
2609 SkillIcon::Descriptive {
2610 title,
2611 desc,
2612 image,
2613 position,
2614 id,
2615 } => {
2616 Button::image(*image)
2618 .w_h(74.0, 74.0)
2619 .position(*position)
2620 .with_tooltip(
2621 self.tooltip_manager,
2622 &self.localized_strings.get_msg(title),
2623 &self.localized_strings.get_msg(desc),
2624 diary_tooltip,
2625 TEXT_COLOR,
2626 )
2627 .set(*id, ui);
2628 },
2629 SkillIcon::Unlockable {
2630 skill,
2631 image,
2632 position,
2633 id,
2634 } => self.create_unlock_skill_button(
2635 *skill,
2636 *image,
2637 *position,
2638 *id,
2639 ui,
2640 events,
2641 diary_tooltip,
2642 ),
2643 SkillIcon::Ability {
2644 skill,
2645 ability_id,
2646 position,
2647 } => self.create_unlock_ability_button(
2648 *skill,
2649 ability_id,
2650 *position,
2651 i,
2652 ui,
2653 events,
2654 diary_tooltip,
2655 state,
2656 ),
2657 }
2658 }
2659 }
2660
2661 fn setup_state_for_skill_icons(
2662 &mut self,
2663 state: &mut State<DiaryState>,
2664 ui: &mut UiCell,
2665 skills_top_l: usize,
2666 skills_top_r: usize,
2667 skills_bot_l: usize,
2668 skills_bot_r: usize,
2669 ) {
2670 state.update(|s| {
2672 s.ids
2673 .skills_top_l
2674 .resize(skills_top_l, &mut ui.widget_id_generator())
2675 });
2676 state.update(|s| {
2677 s.ids
2678 .skills_top_r
2679 .resize(skills_top_r, &mut ui.widget_id_generator())
2680 });
2681 state.update(|s| {
2682 s.ids
2683 .skills_bot_l
2684 .resize(skills_bot_l, &mut ui.widget_id_generator())
2685 });
2686 state.update(|s| {
2687 s.ids
2688 .skills_bot_r
2689 .resize(skills_bot_r, &mut ui.widget_id_generator())
2690 });
2691
2692 let offset_0 = 22.0;
2701 let offset_1 = -122.0;
2702 let offset_2 = offset_1 - -20.0;
2703
2704 let skill_pos = |idx, align, central_skill| {
2705 use PositionSpecifier::*;
2706 match idx {
2707 0 => MiddleOf(align),
2709 1 => UpFrom(central_skill, offset_0),
2711 2 => DownFrom(central_skill, offset_0),
2713 3 => LeftFrom(central_skill, offset_0),
2715 4 => RightFrom(central_skill, offset_0),
2717 5 => TopLeftWithMarginsOn(central_skill, offset_1, offset_2),
2719 6 => TopRightWithMarginsOn(central_skill, offset_1, offset_2),
2721 7 => BottomLeftWithMarginsOn(central_skill, offset_1, offset_2),
2723 8 => BottomRightWithMarginsOn(central_skill, offset_1, offset_2),
2725 buttons => {
2726 panic!("{} > 8 position number", buttons);
2727 },
2728 }
2729 };
2730
2731 while self.created_btns_top_l < skills_top_l {
2736 let pos = skill_pos(
2737 self.created_btns_top_l,
2738 state.ids.skills_top_l_align,
2739 state.ids.skills_top_l[0],
2740 );
2741 Button::image(self.imgs.wpn_icon_border_skills)
2742 .w_h(80.0, 100.0)
2743 .position(pos)
2744 .set(state.ids.skills_top_l[self.created_btns_top_l], ui);
2745 self.created_btns_top_l += 1;
2746 }
2747 while self.created_btns_top_r < skills_top_r {
2749 let pos = skill_pos(
2750 self.created_btns_top_r,
2751 state.ids.skills_top_r_align,
2752 state.ids.skills_top_r[0],
2753 );
2754 Button::image(self.imgs.wpn_icon_border_skills)
2755 .w_h(80.0, 100.0)
2756 .position(pos)
2757 .set(state.ids.skills_top_r[self.created_btns_top_r], ui);
2758 self.created_btns_top_r += 1;
2759 }
2760 while self.created_btns_bot_l < skills_bot_l {
2762 let pos = skill_pos(
2763 self.created_btns_bot_l,
2764 state.ids.skills_bot_l_align,
2765 state.ids.skills_bot_l[0],
2766 );
2767 Button::image(self.imgs.wpn_icon_border_skills)
2768 .w_h(80.0, 100.0)
2769 .position(pos)
2770 .set(state.ids.skills_bot_l[self.created_btns_bot_l], ui);
2771 self.created_btns_bot_l += 1;
2772 }
2773 while self.created_btns_bot_r < skills_bot_r {
2775 let pos = skill_pos(
2776 self.created_btns_bot_r,
2777 state.ids.skills_bot_r_align,
2778 state.ids.skills_bot_r[0],
2779 );
2780 Button::image(self.imgs.wpn_icon_border_skills)
2781 .w_h(80.0, 100.0)
2782 .position(pos)
2783 .set(state.ids.skills_bot_r[self.created_btns_bot_r], ui);
2784 self.created_btns_bot_r += 1;
2785 }
2786 }
2787
2788 fn create_unlock_skill_button(
2789 &mut self,
2790 skill: Skill,
2791 skill_image: image::Id,
2792 position: PositionSpecifier,
2793 widget_id: widget::Id,
2794 ui: &mut UiCell,
2795 events: &mut Vec<Event>,
2796 diary_tooltip: &Tooltip,
2797 ) {
2798 let label = if self.skill_set.prerequisites_met(skill) {
2799 let current = self.skill_set.skill_level(skill).unwrap_or(0);
2800 let max = skill.max_level();
2801 format!("{}/{}", current, max)
2802 } else {
2803 "".to_owned()
2804 };
2805
2806 let label_color = if self.skill_set.is_at_max_level(skill) {
2807 TEXT_COLOR
2808 } else if self.skill_set.sufficient_skill_points(skill) {
2809 HP_COLOR
2810 } else {
2811 CRITICAL_HP_COLOR
2812 };
2813
2814 let image_color = if self.skill_set.prerequisites_met(skill) {
2815 TEXT_COLOR
2816 } else {
2817 Color::Rgba(0.41, 0.41, 0.41, 0.7)
2818 };
2819
2820 let skill_strings = skill_strings(skill);
2821 let (title, description) =
2822 skill_strings.localize(self.localized_strings, self.skill_set, skill);
2823
2824 let button = Button::image(skill_image)
2825 .w_h(74.0, 74.0)
2826 .position(position)
2827 .label(&label)
2828 .label_y(conrod_core::position::Relative::Scalar(-47.0))
2829 .label_x(conrod_core::position::Relative::Scalar(0.0))
2830 .label_color(label_color)
2831 .label_font_size(self.fonts.cyri.scale(15))
2832 .label_font_id(self.fonts.cyri.conrod_id)
2833 .image_color(image_color)
2834 .with_tooltip(
2835 self.tooltip_manager,
2836 &title,
2837 &description,
2838 diary_tooltip,
2839 TEXT_COLOR,
2840 )
2841 .set(widget_id, ui);
2842
2843 if button.was_clicked() {
2844 events.push(Event::UnlockSkill(skill));
2845 };
2846 }
2847
2848 fn create_unlock_ability_button(
2849 &mut self,
2850 skill: Skill,
2851 ability_id: &str,
2852 position: PositionSpecifier,
2853 widget_index: usize,
2854 ui: &mut UiCell,
2855 events: &mut Vec<Event>,
2856 diary_tooltip: &Tooltip,
2857 state: &mut State<DiaryState>,
2858 ) {
2859 let locked = !self.skill_set.prerequisites_met(skill);
2860 let owned = self.skill_set.has_skill(skill);
2861 let image_color = if owned {
2862 TEXT_COLOR
2863 } else {
2864 Color::Rgba(0.41, 0.41, 0.41, 0.7)
2865 };
2866
2867 let (title, description) = util::ability_description(ability_id, self.localized_strings);
2868
2869 let sp_cost = sp(self.localized_strings, self.skill_set, skill);
2870
2871 let description = format!("{description}\n{sp_cost}");
2872
2873 let button = Button::image(util::ability_image(self.imgs, ability_id))
2874 .w_h(76.0, 76.0)
2875 .position(position)
2876 .image_color(image_color)
2877 .with_tooltip(
2878 self.tooltip_manager,
2879 &title,
2880 &description,
2881 diary_tooltip,
2882 TEXT_COLOR,
2883 )
2884 .set(state.ids.skills[widget_index], ui);
2885
2886 if locked {
2888 Image::new(self.imgs.lock)
2889 .w_h(76.0, 76.0)
2890 .middle_of(state.ids.skills[widget_index])
2891 .graphics_for(state.ids.skills[widget_index])
2892 .color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.8)))
2893 .set(state.ids.skill_lock_imgs[widget_index], ui);
2894 }
2895
2896 if button.was_clicked() {
2897 events.push(Event::UnlockSkill(skill));
2898 };
2899 }
2900}
2901
2902fn skill_strings(skill: Skill) -> SkillStrings<'static> {
2906 match skill {
2907 Skill::UnlockGroup(s) => unlock_skill_strings(s),
2909 Skill::Bow(s) => bow_skill_strings(s),
2911 Skill::Staff(s) => staff_skill_strings(s),
2912 Skill::Sceptre(s) => sceptre_skill_strings(s),
2913 Skill::Climb(s) => climb_skill_strings(s),
2915 Skill::Swim(s) => swim_skill_strings(s),
2916 Skill::Pick(s) => mining_skill_strings(s),
2918 _ => SkillStrings::plain("", ""),
2919 }
2920}
2921
2922fn unlock_skill_strings(group: SkillGroupKind) -> SkillStrings<'static> {
2923 match group {
2924 SkillGroupKind::Weapon(ToolKind::Sword) => {
2925 SkillStrings::plain("hud-skill-unlck_sword_title", "hud-skill-unlck_sword")
2926 },
2927 SkillGroupKind::Weapon(ToolKind::Axe) => {
2928 SkillStrings::plain("hud-skill-unlck_axe_title", "hud-skill-unlck_axe")
2929 },
2930 SkillGroupKind::Weapon(ToolKind::Hammer) => {
2931 SkillStrings::plain("hud-skill-unlck_hammer_title", "hud-skill-unlck_hammer")
2932 },
2933 SkillGroupKind::Weapon(ToolKind::Bow) => {
2934 SkillStrings::plain("hud-skill-unlck_bow_title", "hud-skill-unlck_bow")
2935 },
2936 SkillGroupKind::Weapon(ToolKind::Staff) => {
2937 SkillStrings::plain("hud-skill-unlck_staff_title", "hud-skill-unlck_staff")
2938 },
2939 SkillGroupKind::Weapon(ToolKind::Sceptre) => {
2940 SkillStrings::plain("hud-skill-unlck_sceptre_title", "hud-skill-unlck_sceptre")
2941 },
2942 SkillGroupKind::General
2943 | SkillGroupKind::Weapon(
2944 ToolKind::Dagger
2945 | ToolKind::Shield
2946 | ToolKind::Spear
2947 | ToolKind::Blowgun
2948 | ToolKind::Debug
2949 | ToolKind::Farming
2950 | ToolKind::Instrument
2951 | ToolKind::Throwable
2952 | ToolKind::Pick
2953 | ToolKind::Shovel
2954 | ToolKind::Natural
2955 | ToolKind::Empty,
2956 ) => {
2957 tracing::warn!("Requesting title for unlocking unexpected skill group");
2958 SkillStrings::Empty
2959 },
2960 }
2961}
2962
2963fn bow_skill_strings(skill: BowSkill) -> SkillStrings<'static> {
2964 let modifiers = SKILL_MODIFIERS.bow_tree;
2965 match skill {
2966 BowSkill::ProjSpeed => SkillStrings::with_mult(
2968 "hud-skill-bow_projectile_speed_title",
2969 "hud-skill-bow_projectile_speed",
2970 modifiers.universal.projectile_speed,
2971 ),
2972 BowSkill::CDamage => SkillStrings::with_mult(
2974 "hud-skill-bow_charged_damage_title",
2975 "hud-skill-bow_charged_damage",
2976 modifiers.charged.damage_scaling,
2977 ),
2978 BowSkill::CRegen => SkillStrings::with_mult(
2979 "hud-skill-bow_charged_energy_regen_title",
2980 "hud-skill-bow_charged_energy_regen",
2981 modifiers.charged.regen_scaling,
2982 ),
2983 BowSkill::CKnockback => SkillStrings::with_mult(
2984 "hud-skill-bow_charged_knockback_title",
2985 "hud-skill-bow_charged_knockback",
2986 modifiers.charged.knockback_scaling,
2987 ),
2988 BowSkill::CSpeed => SkillStrings::with_mult(
2989 "hud-skill-bow_charged_speed_title",
2990 "hud-skill-bow_charged_speed",
2991 modifiers.charged.charge_rate,
2992 ),
2993 BowSkill::CMove => SkillStrings::with_mult(
2994 "hud-skill-bow_charged_move_title",
2995 "hud-skill-bow_charged_move",
2996 modifiers.charged.move_speed,
2997 ),
2998 BowSkill::RDamage => SkillStrings::with_mult(
3000 "hud-skill-bow_repeater_damage_title",
3001 "hud-skill-bow_repeater_damage",
3002 modifiers.repeater.power,
3003 ),
3004 BowSkill::RCost => SkillStrings::with_mult(
3005 "hud-skill-bow_repeater_cost_title",
3006 "hud-skill-bow_repeater_cost",
3007 modifiers.repeater.energy_cost,
3008 ),
3009 BowSkill::RSpeed => SkillStrings::with_mult(
3010 "hud-skill-bow_repeater_speed_title",
3011 "hud-skill-bow_repeater_speed",
3012 modifiers.repeater.max_speed,
3013 ),
3014 BowSkill::UnlockShotgun => SkillStrings::plain(
3016 "hud-skill-bow_shotgun_unlock_title",
3017 "hud-skill-bow_shotgun_unlock",
3018 ),
3019 BowSkill::SDamage => SkillStrings::with_mult(
3020 "hud-skill-bow_shotgun_damage_title",
3021 "hud-skill-bow_shotgun_damage",
3022 modifiers.shotgun.power,
3023 ),
3024 BowSkill::SCost => SkillStrings::with_mult(
3025 "hud-skill-bow_shotgun_cost_title",
3026 "hud-skill-bow_shotgun_cost",
3027 modifiers.shotgun.energy_cost,
3028 ),
3029 BowSkill::SArrows => SkillStrings::with_const(
3030 "hud-skill-bow_shotgun_arrow_count_title",
3031 "hud-skill-bow_shotgun_arrow_count",
3032 modifiers.shotgun.num_projectiles,
3033 ),
3034 BowSkill::SSpread => SkillStrings::with_mult(
3035 "hud-skill-bow_shotgun_spread_title",
3036 "hud-skill-bow_shotgun_spread",
3037 modifiers.shotgun.spread,
3038 ),
3039 }
3040}
3041
3042fn staff_skill_strings(skill: StaffSkill) -> SkillStrings<'static> {
3043 let modifiers = SKILL_MODIFIERS.staff_tree;
3044 match skill {
3045 StaffSkill::BDamage => SkillStrings::with_mult(
3047 "hud-skill-st_damage_title",
3048 "hud-skill-st_damage",
3049 modifiers.fireball.power,
3050 ),
3051 StaffSkill::BRegen => SkillStrings::with_mult(
3052 "hud-skill-st_energy_regen_title",
3053 "hud-skill-st_energy_regen",
3054 modifiers.fireball.regen,
3055 ),
3056 StaffSkill::BRadius => SkillStrings::with_mult(
3057 "hud-skill-st_explosion_radius_title",
3058 "hud-skill-st_explosion_radius",
3059 modifiers.fireball.range,
3060 ),
3061 StaffSkill::FDamage => SkillStrings::with_mult(
3063 "hud-skill-st_flamethrower_damage_title",
3064 "hud-skill-st_flamethrower_damage",
3065 modifiers.flamethrower.damage,
3066 ),
3067 StaffSkill::FRange => SkillStrings::with_mult(
3068 "hud-skill-st_flamethrower_range_title",
3069 "hud-skill-st_flamethrower_range",
3070 modifiers.flamethrower.range,
3071 ),
3072 StaffSkill::FDrain => SkillStrings::with_mult(
3073 "hud-skill-st_energy_drain_title",
3074 "hud-skill-st_energy_drain",
3075 modifiers.flamethrower.energy_drain,
3076 ),
3077 StaffSkill::FVelocity => SkillStrings::with_mult(
3078 "hud-skill-st_flame_velocity_title",
3079 "hud-skill-st_flame_velocity",
3080 modifiers.flamethrower.velocity,
3081 ),
3082 StaffSkill::UnlockShockwave => SkillStrings::plain(
3084 "hud-skill-st_shockwave_unlock_title",
3085 "hud-skill-st_shockwave_unlock",
3086 ),
3087 StaffSkill::SDamage => SkillStrings::with_mult(
3088 "hud-skill-st_shockwave_damage_title",
3089 "hud-skill-st_shockwave_damage",
3090 modifiers.shockwave.damage,
3091 ),
3092 StaffSkill::SKnockback => SkillStrings::with_mult(
3093 "hud-skill-st_shockwave_knockback_title",
3094 "hud-skill-st_shockwave_knockback",
3095 modifiers.shockwave.knockback,
3096 ),
3097 StaffSkill::SRange => SkillStrings::with_mult(
3098 "hud-skill-st_shockwave_range_title",
3099 "hud-skill-st_shockwave_range",
3100 modifiers.shockwave.duration,
3101 ),
3102 StaffSkill::SCost => SkillStrings::with_mult(
3103 "hud-skill-st_shockwave_cost_title",
3104 "hud-skill-st_shockwave_cost",
3105 modifiers.shockwave.energy_cost,
3106 ),
3107 }
3108}
3109
3110fn sceptre_skill_strings(skill: SceptreSkill) -> SkillStrings<'static> {
3111 let modifiers = SKILL_MODIFIERS.sceptre_tree;
3112 match skill {
3113 SceptreSkill::LDamage => SkillStrings::with_mult(
3115 "hud-skill-sc_lifesteal_damage_title",
3116 "hud-skill-sc_lifesteal_damage",
3117 modifiers.beam.damage,
3118 ),
3119 SceptreSkill::LRange => SkillStrings::with_mult(
3120 "hud-skill-sc_lifesteal_range_title",
3121 "hud-skill-sc_lifesteal_range",
3122 modifiers.beam.range,
3123 ),
3124 SceptreSkill::LLifesteal => SkillStrings::with_mult(
3125 "hud-skill-sc_lifesteal_lifesteal_title",
3126 "hud-skill-sc_lifesteal_lifesteal",
3127 modifiers.beam.lifesteal,
3128 ),
3129 SceptreSkill::LRegen => SkillStrings::with_mult(
3130 "hud-skill-sc_lifesteal_regen_title",
3131 "hud-skill-sc_lifesteal_regen",
3132 modifiers.beam.energy_regen,
3133 ),
3134 SceptreSkill::HHeal => SkillStrings::with_mult(
3136 "hud-skill-sc_heal_heal_title",
3137 "hud-skill-sc_heal_heal",
3138 modifiers.healing_aura.strength,
3139 ),
3140 SceptreSkill::HRange => SkillStrings::with_mult(
3141 "hud-skill-sc_heal_range_title",
3142 "hud-skill-sc_heal_range",
3143 modifiers.healing_aura.range,
3144 ),
3145 SceptreSkill::HDuration => SkillStrings::with_mult(
3146 "hud-skill-sc_heal_duration_title",
3147 "hud-skill-sc_heal_duration",
3148 modifiers.healing_aura.duration,
3149 ),
3150 SceptreSkill::HCost => SkillStrings::with_mult(
3151 "hud-skill-sc_heal_cost_title",
3152 "hud-skill-sc_heal_cost",
3153 modifiers.healing_aura.energy_cost,
3154 ),
3155 SceptreSkill::UnlockAura => SkillStrings::plain(
3157 "hud-skill-sc_wardaura_unlock_title",
3158 "hud-skill-sc_wardaura_unlock",
3159 ),
3160 SceptreSkill::AStrength => SkillStrings::with_mult(
3161 "hud-skill-sc_wardaura_strength_title",
3162 "hud-skill-sc_wardaura_strength",
3163 modifiers.warding_aura.strength,
3164 ),
3165 SceptreSkill::ADuration => SkillStrings::with_mult(
3166 "hud-skill-sc_wardaura_duration_title",
3167 "hud-skill-sc_wardaura_duration",
3168 modifiers.warding_aura.duration,
3169 ),
3170 SceptreSkill::ARange => SkillStrings::with_mult(
3171 "hud-skill-sc_wardaura_range_title",
3172 "hud-skill-sc_wardaura_range",
3173 modifiers.warding_aura.range,
3174 ),
3175 SceptreSkill::ACost => SkillStrings::with_mult(
3176 "hud-skill-sc_wardaura_cost_title",
3177 "hud-skill-sc_wardaura_cost",
3178 modifiers.warding_aura.energy_cost,
3179 ),
3180 }
3181}
3182
3183fn climb_skill_strings(skill: ClimbSkill) -> SkillStrings<'static> {
3184 let modifiers = SKILL_MODIFIERS.general_tree.climb;
3185 match skill {
3186 ClimbSkill::Cost => SkillStrings::with_mult(
3187 "hud-skill-climbing_cost_title",
3188 "hud-skill-climbing_cost",
3189 modifiers.energy_cost,
3190 ),
3191 ClimbSkill::Speed => SkillStrings::with_mult(
3192 "hud-skill-climbing_speed_title",
3193 "hud-skill-climbing_speed",
3194 modifiers.speed,
3195 ),
3196 }
3197}
3198
3199fn swim_skill_strings(skill: SwimSkill) -> SkillStrings<'static> {
3200 let modifiers = SKILL_MODIFIERS.general_tree.swim;
3201 match skill {
3202 SwimSkill::Speed => SkillStrings::with_mult(
3203 "hud-skill-swim_speed_title",
3204 "hud-skill-swim_speed",
3205 modifiers.speed,
3206 ),
3207 }
3208}
3209
3210fn mining_skill_strings(skill: MiningSkill) -> SkillStrings<'static> {
3211 let modifiers = SKILL_MODIFIERS.mining_tree;
3212 match skill {
3213 MiningSkill::Speed => SkillStrings::with_mult(
3214 "hud-skill-pick_strike_speed_title",
3215 "hud-skill-pick_strike_speed",
3216 modifiers.speed,
3217 ),
3218 MiningSkill::OreGain => SkillStrings::with_const(
3219 "hud-skill-pick_strike_oregain_title",
3220 "hud-skill-pick_strike_oregain",
3221 (modifiers.ore_gain * 100.0).round() as u32,
3222 ),
3223 MiningSkill::GemGain => SkillStrings::with_const(
3224 "hud-skill-pick_strike_gemgain_title",
3225 "hud-skill-pick_strike_gemgain",
3226 (modifiers.gem_gain * 100.0).round() as u32,
3227 ),
3228 }
3229}
3230
3231enum SkillStrings<'a> {
3234 Plain {
3235 title: &'a str,
3236 desc: &'a str,
3237 },
3238 WithConst {
3239 title: &'a str,
3240 desc: &'a str,
3241 constant: u32,
3242 },
3243 WithMult {
3244 title: &'a str,
3245 desc: &'a str,
3246 multiplier: f32,
3247 },
3248 Empty,
3249}
3250
3251impl<'a> SkillStrings<'a> {
3252 fn plain(title: &'a str, desc: &'a str) -> Self { Self::Plain { title, desc } }
3253
3254 fn with_const(title: &'a str, desc: &'a str, constant: u32) -> Self {
3255 Self::WithConst {
3256 title,
3257 desc,
3258 constant,
3259 }
3260 }
3261
3262 fn with_mult(title: &'a str, desc: &'a str, multiplier: f32) -> Self {
3263 Self::WithMult {
3264 title,
3265 desc,
3266 multiplier,
3267 }
3268 }
3269
3270 fn localize<'loc>(
3271 &self,
3272 i18n: &'loc Localization,
3273 skill_set: &SkillSet,
3274 skill: Skill,
3275 ) -> (Cow<'loc, str>, Cow<'loc, str>) {
3276 match self {
3277 Self::Plain { title, desc } => {
3278 let title = i18n.get_msg(title);
3279
3280 let args = i18n::fluent_args! {
3281 "SP" => sp(i18n, skill_set, skill),
3282 };
3283 let desc = i18n.get_msg_ctx(desc, &args);
3284
3285 (title, desc)
3286 },
3287 Self::WithConst {
3288 title,
3289 desc,
3290 constant,
3291 } => {
3292 let title = i18n.get_msg(title);
3293 let args = i18n::fluent_args! {
3294 "boost" => constant,
3295 "SP" => sp(i18n, skill_set, skill),
3296 };
3297 let desc = i18n.get_msg_ctx(desc, &args);
3298
3299 (title, desc)
3300 },
3301 Self::WithMult {
3302 title,
3303 desc,
3304 multiplier,
3305 } => {
3306 let percentage = hud::multiplier_to_percentage(*multiplier).abs();
3307
3308 let title = i18n.get_msg(title);
3309
3310 let args = i18n::fluent_args! {
3311 "boost" => format!("{percentage:.0}"),
3312 "SP" => sp(i18n, skill_set, skill),
3313 };
3314 let desc = i18n.get_msg_ctx(desc, &args);
3315
3316 (title, desc)
3317 },
3318 Self::Empty => (Cow::Borrowed(""), Cow::Borrowed("")),
3319 }
3320 }
3321}
3322
3323const STAT_COUNT: usize = 15;
3325
3326#[derive(EnumIter)]
3327enum CharacterStat {
3328 Name,
3329 BattleMode,
3330 Waypoint,
3331 Hitpoints,
3332 Energy,
3333 Poise,
3334 CombatRating,
3335 Protection,
3336 StunResistance,
3337 PrecisionPower,
3338 EnergyReward,
3339 Stealth,
3340 WeaponPower,
3341 WeaponSpeed,
3342 WeaponEffectPower,
3343}
3344
3345impl CharacterStat {
3346 fn localized_str<'a>(&self, i18n: &'a Localization) -> Cow<'a, str> {
3347 use CharacterStat::*;
3348
3349 match self {
3350 Name => i18n.get_msg("character_window-character_name"),
3351 BattleMode => i18n.get_msg("hud-battle-mode"),
3352 Waypoint => i18n.get_msg("hud-waypoint"),
3353 Hitpoints => i18n.get_msg("hud-bag-health"),
3354 Energy => i18n.get_msg("hud-bag-energy"),
3355 CombatRating => i18n.get_msg("hud-bag-combat_rating"),
3356 Protection => i18n.get_msg("hud-bag-protection"),
3357 StunResistance => i18n.get_msg("hud-bag-stun_res"),
3358 Poise => i18n.get_msg("common-stats-poise_res"),
3359 PrecisionPower => i18n.get_msg("common-stats-precision_power"),
3360 EnergyReward => i18n.get_msg("common-stats-energy_reward"),
3361 Stealth => i18n.get_msg("common-stats-stealth"),
3362 WeaponPower => i18n.get_msg("common-stats-power"),
3363 WeaponSpeed => i18n.get_msg("common-stats-speed"),
3364 WeaponEffectPower => i18n.get_msg("common-stats-effect-power"),
3365 }
3366 }
3367}
3368
3369fn sp<'loc>(i18n: &'loc Localization, skill_set: &SkillSet, skill: Skill) -> Cow<'loc, str> {
3370 let current_level = skill_set.skill_level(skill);
3371 if matches!(current_level, Ok(level) if level == skill.max_level()) {
3372 Cow::Borrowed("")
3373 } else {
3374 i18n.get_msg_ctx("hud-skill-req_sp", &i18n::fluent_args! {
3375 "number" => skill_set.skill_cost(skill),
3376 })
3377 }
3378}