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