Branch data Line data Source code
1 : : use anyhow::{anyhow, Result};
2 : : use indexmap::IndexMap;
3 : : use std::{path::Path, vec};
4 : :
5 : : use serde::{Deserialize, Serialize};
6 : :
7 : : use crate::{
8 : : common::{
9 : : all_target_const::{TARGET_ALLY, TARGET_ENNEMY},
10 : : reach_const::INDIVIDUAL,
11 : : stats_const::HP,
12 : : },
13 : : effect::EffectParam,
14 : : utils,
15 : : };
16 : :
17 : : #[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
18 : : pub struct AtksInfo {
19 : : pub atk_name: String,
20 : : pub nb_use: i64,
21 : : pub all_damages_by_target: IndexMap<String, i64>, // key target, i64 dmg or heal accumulated
22 : : }
23 : :
24 : : /// Defines the parameters of an attack.
25 : : #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
26 : : #[serde(default)]
27 : : pub struct AttackType {
28 : : /// Name of the attack
29 : : #[serde(rename = "Nom")]
30 : : pub name: String,
31 : : #[serde(rename = "Niveau")]
32 : : pub level: u8,
33 : : #[serde(rename = "Coût de mana")]
34 : : pub mana_cost: u64,
35 : : #[serde(rename = "Coût de vigueur")]
36 : : pub vigor_cost: u64,
37 : : #[serde(rename = "Coût de rage")]
38 : : pub berseck_cost: u64,
39 : : /// TODO is there any sense for target and reach ? those are defined for each effect of that attack
40 : : #[serde(rename = "Cible")]
41 : : pub target: String,
42 : : #[serde(rename = "Portée")]
43 : : pub reach: String,
44 : : #[serde(rename = "Photo")]
45 : : pub name_photo: String,
46 : : #[serde(rename = "Effet")]
47 : : pub all_effects: Vec<EffectParam>,
48 : : #[serde(rename = "Forme")]
49 : : pub form: String,
50 : : #[serde(rename = "Aggro")]
51 : : pub aggro: i64,
52 : : #[serde(rename = "Durée")]
53 : : pub turns_duration: i64,
54 : : }
55 : :
56 : : impl Default for AttackType {
57 : 442 : fn default() -> Self {
58 : 442 : AttackType {
59 : 442 : name: "".to_owned(),
60 : 442 : level: 0,
61 : 442 : mana_cost: 0,
62 : 442 : vigor_cost: 0,
63 : 442 : berseck_cost: 0,
64 : 442 : target: TARGET_ALLY.to_owned(),
65 : 442 : reach: INDIVIDUAL.to_owned(),
66 : 442 : name_photo: "".to_owned(),
67 : 442 : all_effects: vec![],
68 : 442 : form: "".to_owned(),
69 : 442 : aggro: 0,
70 : 442 : turns_duration: 0,
71 : 442 : }
72 : 442 : }
73 : : }
74 : :
75 : : impl AttackType {
76 : 399 : pub fn try_new_from_json<P: AsRef<Path>>(path: P) -> Result<AttackType> {
77 : 399 : utils::read_from_json::<_, AttackType>(&path)
78 : 399 : .map_err(|_| anyhow!("Unknown file: {:?}", path.as_ref()))
79 : 399 : }
80 : :
81 : 3 : pub fn has_only_heal_effect(&self) -> bool {
82 : 3 : let mut is_only_heal_effect = false;
83 : 5 : for e in &self.all_effects {
84 : 3 : if e.stats_name == HP && e.value < 0 {
85 : 1 : return false;
86 : 2 : }
87 : 2 : if e.stats_name == HP && e.value > 0 {
88 : 2 : is_only_heal_effect = true;
89 : 2 : }
90 : : }
91 : 2 : if self.target != TARGET_ENNEMY && is_only_heal_effect {
92 : 1 : return true;
93 : 1 : }
94 : 1 : false
95 : 3 : }
96 : : }
97 : :
98 : : #[cfg(test)]
99 : : mod tests {
100 : : use crate::{
101 : : attack_type::AttackType,
102 : : common::{
103 : : all_target_const::TARGET_ENNEMY, character_json_key::STANDARD_CLASS,
104 : : effect_const::EFFECT_VALUE_CHANGE, reach_const::INDIVIDUAL, stats_const::HP,
105 : : },
106 : : testing_atk::{build_atk_damage_indiv, build_atk_heal1_indiv},
107 : : };
108 : :
109 : : #[test]
110 : 1 : fn unit_try_new_from_json() {
111 : : // existence
112 : 1 : let file_path = "./tests/offlines/attack/test/hehe.json"; // Path to the JSON file
113 : 1 : let atk_type = AttackType::try_new_from_json(file_path);
114 : 1 : assert!(atk_type.is_err());
115 : :
116 : 1 : let file_path = "./tests/offlines/attack/test/SimpleAtk.json"; // Path to the JSON file
117 : 1 : let atk_type = AttackType::try_new_from_json(file_path);
118 : 1 : assert!(atk_type.is_ok());
119 : 1 : let atk_type: AttackType = atk_type.unwrap();
120 : 1 : assert_eq!(atk_type.name, "SimpleAtk");
121 : 1 : assert_eq!(atk_type.level, 1);
122 : 1 : assert_eq!(atk_type.mana_cost, 9);
123 : 1 : assert_eq!(atk_type.vigor_cost, 0);
124 : 1 : assert_eq!(atk_type.berseck_cost, 0);
125 : 1 : assert_eq!(atk_type.target, TARGET_ENNEMY);
126 : 1 : assert_eq!(atk_type.reach, INDIVIDUAL);
127 : 1 : assert_eq!(atk_type.name_photo, "SimpleAtk.png");
128 : 1 : assert_eq!(atk_type.form, STANDARD_CLASS);
129 : 1 : assert_eq!(atk_type.aggro, 0);
130 : : // decode the effect
131 : 1 : assert_eq!(atk_type.all_effects.len(), 1);
132 : 1 : assert_eq!(atk_type.all_effects[0].stats_name, HP);
133 : 1 : assert_eq!(atk_type.all_effects[0].value, -35);
134 : 1 : assert_eq!(atk_type.all_effects[0].target, TARGET_ENNEMY);
135 : 1 : assert_eq!(atk_type.all_effects[0].reach, INDIVIDUAL);
136 : 1 : assert_eq!(atk_type.all_effects[0].effect_type, EFFECT_VALUE_CHANGE);
137 : 1 : assert_eq!(atk_type.all_effects[0].sub_value_effect, 0);
138 : 1 : }
139 : :
140 : : #[test]
141 : 1 : fn unit_has_only_heal_effect() {
142 : 1 : let atk_dmg = build_atk_damage_indiv();
143 : 1 : assert_eq!(false, atk_dmg.has_only_heal_effect());
144 : :
145 : 1 : let mut atk_heal = build_atk_heal1_indiv();
146 : 1 : assert_eq!(true, atk_heal.has_only_heal_effect());
147 : :
148 : 1 : atk_heal.target = TARGET_ENNEMY.to_owned();
149 : 1 : assert_eq!(false, atk_heal.has_only_heal_effect());
150 : 1 : }
151 : : }
|