pub struct ScalingTransform { /* private fields */ }Expand description
A transform that scales based on another stat.
Adds dependency_value * scale_factor to the input value.
This is commonly used for derived stats (e.g., ATK = base + STR * 2).
§Examples
use zzstat::transform::{StatTransform, ScalingTransform};
use zzstat::{StatId, StatContext};
use std::collections::HashMap;
let str_id = StatId::from_str("STR");
let transform = ScalingTransform::new(str_id.clone(), 2.0);
let mut deps = HashMap::new();
deps.insert(str_id.clone(), 10.0);
let context = StatContext::new();
// 100 (base) + 10 (STR) * 2 = 120
assert_eq!(transform.apply(100.0, &deps, &context).unwrap(), 120.0);Implementations§
Source§impl ScalingTransform
impl ScalingTransform
Sourcepub fn new(dependency: StatId, scale_factor: f64) -> Self
pub fn new(dependency: StatId, scale_factor: f64) -> Self
Create a new scaling transform.
§Arguments
dependency- The stat ID this transform depends onscale_factor- The multiplier to apply to the dependency value
§Examples
use zzstat::transform::ScalingTransform;
use zzstat::StatId;
let str_id = StatId::from_str("STR");
// ATK scales with STR: ATK = base + STR * 2
let transform = ScalingTransform::new(str_id, 2.0);Examples found in repository?
examples/dependencies.rs (line 36)
12fn main() -> Result<(), StatError> {
13 let mut resolver = StatResolver::new();
14
15 // Define stat IDs
16 let str_id = StatId::from_str("STR");
17 let dex_id = StatId::from_str("DEX");
18 let atk_id = StatId::from_str("ATK");
19 let crit_id = StatId::from_str("CRIT");
20
21 println!("=== Setting up base stats ===");
22
23 // Base stats (no dependencies)
24 resolver.register_source(str_id.clone(), Box::new(ConstantSource(10.0)));
25 println!("STR: 10 (base)");
26
27 resolver.register_source(dex_id.clone(), Box::new(ConstantSource(15.0)));
28 println!("DEX: 15 (base)");
29
30 println!("\n=== Setting up derived stats ===");
31
32 // ATK depends on STR
33 resolver.register_source(atk_id.clone(), Box::new(ConstantSource(50.0)));
34 resolver.register_transform(
35 atk_id.clone(),
36 Box::new(ScalingTransform::new(str_id.clone(), 2.0)),
37 );
38 println!("ATK: 50 (base) + STR * 2.0");
39
40 // CRIT depends on DEX
41 resolver.register_source(crit_id.clone(), Box::new(ConstantSource(5.0)));
42 resolver.register_transform(
43 crit_id.clone(),
44 Box::new(ScalingTransform::new(dex_id.clone(), 1.5)),
45 );
46 println!("CRIT: 5 (base) + DEX * 1.5");
47
48 let context = StatContext::new();
49
50 println!("\n=== Resolving all stats ===");
51 let results = resolver.resolve_all(&context)?;
52
53 // Display results
54 println!("\n=== Results ===");
55 for (stat_id, resolved) in &results {
56 println!("\n{}:", stat_id);
57 println!(" Final Value: {:.2}", resolved.value);
58
59 if !resolved.sources.is_empty() {
60 println!(" Sources:");
61 for (desc, value) in &resolved.sources {
62 println!(" {}: {:.2}", desc, value);
63 }
64 }
65
66 if !resolved.transforms.is_empty() {
67 println!(" Transforms:");
68 for (desc, value) in &resolved.transforms {
69 println!(" {}: {:.2}", desc, value);
70 }
71 }
72 }
73
74 println!("\n=== Verification ===");
75 println!("STR: {:.2} (expected: 10.00)", results[&str_id].value);
76 println!("DEX: {:.2} (expected: 15.00)", results[&dex_id].value);
77 println!(
78 "ATK: {:.2} (expected: 70.00 = 50 + 10*2)",
79 results[&atk_id].value
80 );
81 println!(
82 "CRIT: {:.2} (expected: 27.50 = 5 + 15*1.5)",
83 results[&crit_id].value
84 );
85
86 Ok(())
87}More examples
examples/cycle_detection.rs (line 25)
11fn main() {
12 let mut resolver = StatResolver::new();
13
14 // Create stats that depend on each other in a cycle
15 let a_id = StatId::from_str("A");
16 let b_id = StatId::from_str("B");
17 let c_id = StatId::from_str("C");
18
19 println!("=== Setting up circular dependencies ===\n");
20
21 // A depends on B
22 resolver.register_source(a_id.clone(), Box::new(ConstantSource(10.0)));
23 resolver.register_transform(
24 a_id.clone(),
25 Box::new(ScalingTransform::new(b_id.clone(), 1.0)),
26 );
27 println!("A: 10 (base) + B * 1.0");
28
29 // B depends on C
30 resolver.register_source(b_id.clone(), Box::new(ConstantSource(20.0)));
31 resolver.register_transform(
32 b_id.clone(),
33 Box::new(ScalingTransform::new(c_id.clone(), 1.0)),
34 );
35 println!("B: 20 (base) + C * 1.0");
36
37 // C depends on A (creates cycle: A -> B -> C -> A)
38 resolver.register_source(c_id.clone(), Box::new(ConstantSource(30.0)));
39 resolver.register_transform(
40 c_id.clone(),
41 Box::new(ScalingTransform::new(a_id.clone(), 1.0)),
42 );
43 println!("C: 30 (base) + A * 1.0");
44
45 println!("\n=== Attempting to resolve (should detect cycle) ===\n");
46
47 let context = StatContext::new();
48 match resolver.resolve(&a_id, &context) {
49 Err(StatError::CycleDetected(cycle)) => {
50 println!("✓ Cycle detected successfully!");
51 println!("\nCycle path:");
52 for (i, stat_id) in cycle.iter().enumerate() {
53 if i < cycle.len() - 1 {
54 print!("{} -> ", stat_id);
55 } else {
56 println!("{}", stat_id);
57 }
58 }
59 }
60 Err(e) => {
61 println!("✗ Unexpected error: {}", e);
62 }
63 Ok(_) => {
64 println!("✗ ERROR: Cycle was not detected! This should not happen.");
65 }
66 }
67
68 println!("\n=== Valid dependency chain (no cycle) ===\n");
69
70 // Reset resolver
71 let mut resolver2 = StatResolver::new();
72
73 let x_id = StatId::from_str("X");
74 let y_id = StatId::from_str("Y");
75 let z_id = StatId::from_str("Z");
76
77 // Linear chain: X -> Y -> Z (no cycle)
78 resolver2.register_source(x_id.clone(), Box::new(ConstantSource(10.0)));
79 println!("X: 10 (base)");
80
81 resolver2.register_source(y_id.clone(), Box::new(ConstantSource(20.0)));
82 resolver2.register_transform(
83 y_id.clone(),
84 Box::new(ScalingTransform::new(x_id.clone(), 1.0)),
85 );
86 println!("Y: 20 (base) + X * 1.0");
87
88 resolver2.register_source(z_id.clone(), Box::new(ConstantSource(30.0)));
89 resolver2.register_transform(
90 z_id.clone(),
91 Box::new(ScalingTransform::new(y_id.clone(), 1.0)),
92 );
93 println!("Z: 30 (base) + Y * 1.0");
94
95 println!("\n=== Resolving valid chain ===\n");
96
97 let results = resolver2.resolve_all(&context).unwrap();
98
99 println!("Results:");
100 println!(" X: {:.2}", results[&x_id].value);
101 println!(" Y: {:.2} (20 + 10)", results[&y_id].value);
102 println!(" Z: {:.2} (30 + 30)", results[&z_id].value);
103
104 println!("\n✓ Valid dependency chain resolved successfully!");
105}examples/complex.rs (line 49)
13fn main() -> Result<(), StatError> {
14 let mut resolver = StatResolver::new();
15
16 // Define a character's stats
17 let str_id = StatId::from_str("STR");
18 let dex_id = StatId::from_str("DEX");
19 let int_id = StatId::from_str("INT");
20 let hp_id = StatId::from_str("HP");
21 let mp_id = StatId::from_str("MP");
22 let atk_id = StatId::from_str("ATK");
23 let crit_id = StatId::from_str("CRIT");
24 let dps_id = StatId::from_str("DPS");
25
26 println!("=== Character Stat System ===\n");
27
28 // Base attributes
29 println!("Base Attributes:");
30 resolver.register_source(str_id.clone(), Box::new(ConstantSource(20.0)));
31 println!(" STR: 20");
32
33 resolver.register_source(dex_id.clone(), Box::new(ConstantSource(15.0)));
34 println!(" DEX: 15");
35
36 resolver.register_source(int_id.clone(), Box::new(ConstantSource(25.0)));
37 println!(" INT: 25");
38
39 // HP: Base + STR scaling + item bonuses
40 println!("\nHP Calculation:");
41 resolver.register_source(hp_id.clone(), Box::new(ConstantSource(100.0)));
42 println!(" Base: 100");
43
44 resolver.register_source(hp_id.clone(), Box::new(ConstantSource(50.0)));
45 println!(" Item bonus: +50");
46
47 resolver.register_transform(
48 hp_id.clone(),
49 Box::new(ScalingTransform::new(str_id.clone(), 5.0)),
50 );
51 println!(" STR scaling: +STR * 5");
52
53 resolver.register_transform(hp_id.clone(), Box::new(MultiplicativeTransform::new(1.1)));
54 println!(" Passive: +10%");
55
56 // MP: Base + INT scaling
57 println!("\nMP Calculation:");
58 resolver.register_source(mp_id.clone(), Box::new(ConstantSource(50.0)));
59 println!(" Base: 50");
60
61 resolver.register_transform(
62 mp_id.clone(),
63 Box::new(ScalingTransform::new(int_id.clone(), 3.0)),
64 );
65 println!(" INT scaling: +INT * 3");
66
67 // ATK: Base + STR scaling + DEX scaling
68 println!("\nATK Calculation:");
69 resolver.register_source(atk_id.clone(), Box::new(ConstantSource(30.0)));
70 println!(" Base: 30");
71
72 resolver.register_transform(
73 atk_id.clone(),
74 Box::new(ScalingTransform::new(str_id.clone(), 2.0)),
75 );
76 println!(" STR scaling: +STR * 2");
77
78 resolver.register_transform(
79 atk_id.clone(),
80 Box::new(ScalingTransform::new(dex_id.clone(), 1.0)),
81 );
82 println!(" DEX scaling: +DEX * 1");
83
84 resolver.register_transform(atk_id.clone(), Box::new(ClampTransform::new(0.0, 200.0)));
85 println!(" Clamp: [0, 200]");
86
87 // CRIT: Base + DEX scaling
88 println!("\nCRIT Calculation:");
89 resolver.register_source(crit_id.clone(), Box::new(ConstantSource(5.0)));
90 println!(" Base: 5");
91
92 resolver.register_transform(
93 crit_id.clone(),
94 Box::new(ScalingTransform::new(dex_id.clone(), 2.0)),
95 );
96 println!(" DEX scaling: +DEX * 2");
97
98 // DPS: Depends on ATK and CRIT
99 println!("\nDPS Calculation:");
100 resolver.register_source(dps_id.clone(), Box::new(ConstantSource(0.0)));
101 println!(" Base: 0");
102
103 resolver.register_transform(
104 dps_id.clone(),
105 Box::new(ScalingTransform::new(atk_id.clone(), 1.0)),
106 );
107 println!(" ATK contribution: +ATK * 1");
108
109 resolver.register_transform(
110 dps_id.clone(),
111 Box::new(ScalingTransform::new(crit_id.clone(), 0.5)),
112 );
113 println!(" CRIT contribution: +CRIT * 0.5");
114
115 let context = StatContext::new();
116
117 println!("\n=== Resolving All Stats ===\n");
118 let results = resolver.resolve_all(&context)?;
119
120 // Display final stats
121 println!("=== Final Character Stats ===\n");
122
123 let stats = vec![
124 ("STR", &str_id),
125 ("DEX", &dex_id),
126 ("INT", &int_id),
127 ("HP", &hp_id),
128 ("MP", &mp_id),
129 ("ATK", &atk_id),
130 ("CRIT", &crit_id),
131 ("DPS", &dps_id),
132 ];
133
134 for (name, id) in stats {
135 if let Some(resolved) = results.get(id) {
136 println!("{}: {:.2}", name, resolved.value);
137 }
138 }
139
140 println!("\n=== Detailed Breakdown ===\n");
141
142 // Show detailed breakdown for DPS
143 if let Some(dps) = results.get(&dps_id) {
144 println!("DPS Breakdown:");
145 println!(" Final Value: {:.2}", dps.value);
146
147 println!("\n Sources:");
148 for (desc, value) in &dps.sources {
149 println!(" {}: {:.2}", desc, value);
150 }
151
152 println!("\n Transforms:");
153 for (desc, value) in &dps.transforms {
154 println!(" {}: {:.2}", desc, value);
155 }
156 }
157
158 Ok(())
159}Trait Implementations§
Source§impl Clone for ScalingTransform
impl Clone for ScalingTransform
Source§fn clone(&self) -> ScalingTransform
fn clone(&self) -> ScalingTransform
Returns a duplicate of the value. Read more
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreSource§impl Debug for ScalingTransform
impl Debug for ScalingTransform
Source§impl StatTransform for ScalingTransform
impl StatTransform for ScalingTransform
Source§fn depends_on(&self) -> Vec<StatId>
fn depends_on(&self) -> Vec<StatId>
Get the list of stat IDs this transform depends on. Read more
Source§fn apply(
&self,
input: f64,
dependencies: &HashMap<StatId, f64>,
_context: &StatContext,
) -> Result<f64, StatError>
fn apply( &self, input: f64, dependencies: &HashMap<StatId, f64>, _context: &StatContext, ) -> Result<f64, StatError>
Apply the transform to an input value. Read more
Source§fn description(&self) -> String
fn description(&self) -> String
Get a human-readable description of this transform. Read more
Auto Trait Implementations§
impl Freeze for ScalingTransform
impl RefUnwindSafe for ScalingTransform
impl Send for ScalingTransform
impl Sync for ScalingTransform
impl Unpin for ScalingTransform
impl UnwindSafe for ScalingTransform
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more