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