1use crate::health::StageLevel;
2use crate::body::{BodyPart, BodyAppliance};
3
4use std::time::{Duration};
5use std::cell::Cell;
6use rand::Rng;
7
8use event::{Dispatcher, Listener};
9use core::ops;
10use std::fmt;
11use std::cmp::Ordering;
12use std::hash::{Hash, Hasher};
13
14pub mod event;
15
16pub struct FrameC<'a, E: Listener + 'static> {
19 pub data: &'a FrameSummaryC,
21 pub events: &'a mut Dispatcher<E>
23}
24
25pub struct FrameSummaryC {
27 pub game_time: GameTimeC,
29 pub player: PlayerStatusC,
31 pub environment: EnvironmentC,
33 pub health: HealthC,
35 pub game_time_delta: f32,
37}
38
39#[derive(Default)]
53pub struct GameTime {
54 pub day : Cell<u64>,
56 pub hour : Cell<u64>,
58 pub minute : Cell<u64>,
60 pub second : Cell<f64>,
62 pub duration: Cell<Duration>
64}
65impl fmt::Display for GameTime {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 write!(f, "{}d {}h {}m {:.1}s", self.day.get(), self.hour.get(), self.minute.get(), self.second.get())
68 }
69}
70impl GameTime {
71 pub fn new() -> Self {
83 GameTime {
84 day: Cell::new(0),
85 hour : Cell::new(0),
86 minute: Cell::new(0),
87 second: Cell::new(0.),
88 duration: Cell::new(Duration::new(0, 0))
89 }
90 }
91
92 pub fn from_duration(d: Duration) -> GameTime {
104 let gt = GameTime::new();
105
106 gt.update_from_duration(d);
107
108 gt
109 }
110
111 pub fn from_contract(gt: GameTimeC) -> Self {
120 GameTime::from_duration(gt.to_duration())
121 }
122
123 pub fn to_contract(&self) -> GameTimeC {
130 GameTimeC {
131 day: self.day.get(),
132 hour: self.hour.get(),
133 minute: self.minute.get(),
134 second: self.second.get()
135 }
136 }
137
138 pub fn add_duration(&self, d: Duration) {
148 let new_values = self.duration.get() + d;
149
150 self.update_from_duration(new_values);
151 }
152
153 pub fn add_seconds(&self, value: f32) {
163 let new_seconds = self.duration.get().as_secs_f64() + value as f64;
164
165 self.update_from_seconds(new_seconds);
166 }
167
168 pub fn update_from(&self, new_values: &GameTime) {
178 self.second.set(new_values.second.get());
179 self.minute.set(new_values.minute.get());
180 self.hour.set(new_values.hour.get());
181 self.day.set(new_values.day.get());
182 }
183
184 pub fn update_from_duration(&self, d: Duration){
194 let secs_passed_f64 = d.as_secs_f64();
195
196 self.update_from_seconds(secs_passed_f64);
197 }
198
199 pub fn update_from_seconds(&self, new_seconds: f64){
209 let second = new_seconds % 60_f64;
210 let secs_passed = new_seconds;
211 let minutes_passed = ((secs_passed / 60_f64) as u64) as u64;
212 let minute = minutes_passed % 60_u64;
213 let hours_passed = ((minutes_passed / 60_u64) as u64) as u64;
214 let hour = hours_passed % 24_u64;
215 let day = ((hours_passed / 24_u64) as u64) as u64;
216
217 self.day.set(day);
218 self.hour.set(hour);
219 self.minute.set(minute);
220 self.second.set(second);
221 self.duration.set(Duration::from_secs_f64(new_seconds));
222 }
223
224}
225
226#[derive(Copy, Clone, Debug, Default)]
231pub struct GameTimeC {
232 pub day: u64,
234 pub hour: u64,
236 pub minute: u64,
238 pub second: f64
240}
241impl Ord for GameTimeC {
242 fn cmp(&self, other: &Self) -> Ordering {
243 self.to_duration().cmp(&other.to_duration())
244 }
245}
246impl Eq for GameTimeC { }
247impl PartialOrd for GameTimeC {
248 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
249 Some(self.cmp(other))
250 }
251}
252impl PartialEq for GameTimeC {
253 fn eq(&self, other: &Self) -> bool {
254 const EPS: f64 = 0.0001;
255
256 self.day == other.day &&
257 self.hour == other.hour &&
258 self.minute == other.minute &&
259 f64::abs(self.second - other.second) < EPS
260 }
261}
262impl fmt::Display for GameTimeC {
263 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264 write!(f, "{}d {}h {}m {:.1}s", self.day, self.hour, self.minute, self.second)
265 }
266}
267impl Hash for GameTimeC {
268 fn hash<H: Hasher>(&self, state: &mut H) {
269 self.day.hash(state);
270 self.hour.hash(state);
271 self.minute.hash(state);
272
273 state.write_u32((self.second*100_f64) as u32);
274 }
275}
276impl GameTimeC {
277 pub fn empty() -> Self {
286 GameTimeC {
287 day: 0,
288 hour: 0,
289 minute: 0,
290 second: 0.
291 }
292 }
293
294 pub fn new(day: u64, hour: u64, minute: u64, second: f64) -> Self {
303 GameTimeC {
304 day,
305 minute,
306 hour,
307 second
308 }
309 }
310
311 pub fn as_secs_f32(&self) -> f32 {
318 self.second as f32+
319 (self.minute as f32)*60_f32+
320 (self.hour as f32)*60_f32*60_f32+
321 (self.day as f32)*24_f32*60_f32*60_f32
322 }
323
324 pub fn add_minutes(&self, amount: u64) -> GameTimeC {
332 let d= self.to_duration() + Duration::from_secs(amount*60);
333
334 GameTimeC::from_duration(d)
335 }
336
337 pub fn to_duration(&self) -> Duration {
344 Duration::from_secs_f64(
345 self.second+((self.minute*60+self.hour*60*60+self.day*24*60*60) as f64))
346 }
347
348 pub fn from_duration(d: Duration) -> Self {
357 GameTime::from_duration(d).to_contract()
358 }
359}
360
361impl ops::Add<GameTimeC> for GameTimeC {
362 type Output = GameTimeC;
363
364 fn add(self, _rhs: GameTimeC) -> GameTimeC {
365 let d = self.to_duration() + _rhs.to_duration();
366
367 GameTime::from_duration(d).to_contract()
368 }
369}
370
371impl ops::Sub<GameTimeC> for GameTimeC {
372 type Output = GameTimeC;
373
374 fn sub(self, _rhs: GameTimeC) -> GameTimeC {
375 let d = self.to_duration() - _rhs.to_duration();
376
377 GameTime::from_duration(d).to_contract()
378 }
379}
380
381#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Default)]
383pub struct ClothesGroupC {
384 pub name: String,
386 pub bonus_cold_resistance: usize,
388 pub bonus_water_resistance: usize
390}
391impl fmt::Display for ClothesGroupC {
392 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
393 write!(f, "Group {} data", self.name)
394 }
395}
396
397pub struct HealthC {
399 pub body_temperature: f32,
401 pub heart_rate: f32,
403 pub top_pressure: f32,
405 pub bottom_pressure: f32,
407 pub blood_level: f32,
409 pub food_level: f32,
411 pub water_level: f32,
413 pub stamina_level: f32,
415 pub fatigue_level: f32,
417 pub oxygen_level: f32,
419 pub diseases: Vec<ActiveDiseaseC>,
421 pub injuries: Vec<ActiveInjuryC>
423}
424impl HealthC {
425 pub fn healthy() -> Self {
435 HealthC {
436 blood_level: 100.,
437 body_temperature: 36.6,
438 top_pressure: 120.,
439 bottom_pressure: 70.,
440 food_level: 100.,
441 water_level: 100.,
442 heart_rate: 64.,
443 stamina_level: 100.,
444 fatigue_level: 0.,
445 oxygen_level: 100.,
446 diseases: Vec::new(),
447 injuries: Vec::new()
448 }
449 }
450}
451
452#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Default)]
454pub struct ActiveDiseaseC {
455 pub name: String,
457 pub scheduled_time: GameTimeC,
459 pub end_time: Option<GameTimeC>,
461 pub current_level: StageLevel,
463 pub current_level_percent: usize,
465 pub is_active: bool,
467 pub is_healing: bool,
469 pub needs_treatment: bool
471}
472impl fmt::Display for ActiveDiseaseC {
473 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
474 write!(f, "{} @{}, active={}", self.name, self.scheduled_time, self.is_active)
475 }
476}
477
478#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Default)]
480pub struct ActiveInjuryC {
481 pub name: String,
483 pub scheduled_time: GameTimeC,
485 pub end_time: Option<GameTimeC>,
487 pub current_level: StageLevel,
489 pub current_level_percent: usize,
491 pub is_active: bool,
493 pub is_healing: bool,
495 pub needs_treatment: bool,
497 pub is_blood_stopped: bool,
499 pub body_part: BodyPart,
501 pub is_fracture: bool
503}
504impl fmt::Display for ActiveInjuryC {
505 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
506 write!(f, "{} on {} @{}, fracture={}, active={}", self.name, self.body_part, self.scheduled_time,
507 self.is_fracture, self.is_active)
508 }
509}
510
511#[derive(Clone, Debug, Default)]
516pub struct EnvironmentC {
517 pub wind_speed: f32,
519 pub temperature : f32,
521 pub rain_intensity : f32
523}
524impl fmt::Display for EnvironmentC {
525 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
526 write!(f, "World: temp {:.1}C, wind {:.1} m/s, rain {:.1}", self.temperature, self.wind_speed, self.rain_intensity)
527 }
528}
529impl Eq for EnvironmentC { }
530impl PartialEq for EnvironmentC {
531 fn eq(&self, other: &Self) -> bool {
532 const EPS: f32 = 0.0001;
533
534 f32::abs(self.wind_speed - other.wind_speed) < EPS &&
535 f32::abs(self.temperature - other.temperature) < EPS &&
536 f32::abs(self.rain_intensity - other.rain_intensity) < EPS
537 }
538}
539impl Hash for EnvironmentC {
540 fn hash<H: Hasher>(&self, state: &mut H) {
541 state.write_i32((self.temperature*10_000_f32) as i32);
542 state.write_u32((self.wind_speed*10_000_f32) as u32);
543 state.write_u32((self.rain_intensity*10_000_f32) as u32);
544 }
545}
546impl EnvironmentC {
547 pub fn new(temperature: f32, wind_speed: f32, rain_intensity: f32) -> EnvironmentC {
569 EnvironmentC {
570 wind_speed,
571 temperature,
572 rain_intensity
573 }
574 }
575
576 pub fn default() -> EnvironmentC { EnvironmentC::new(26., 0., 0.) }
593}
594
595#[derive(Clone, Debug, Default)]
597pub struct PlayerStatusC {
598 pub is_walking: bool,
600 pub is_running: bool,
602 pub is_swimming: bool,
604 pub is_underwater: bool,
606 pub is_sleeping: bool,
608 pub last_slept: Option<GameTimeC>,
610 pub last_slept_duration: f32,
612 pub warmth_level: f32,
614 pub wetness_level: f32,
616 pub clothes: Vec<String>,
618 pub appliances: Vec<BodyAppliance>,
620 pub clothes_group: Option<ClothesGroupC>,
622 pub total_water_resistance: usize,
624 pub total_cold_resistance: usize,
626 pub inventory_weight: f32
628}
629impl fmt::Display for PlayerStatusC {
630 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
631 write!(f, "Player status ({} clothes, {} appliances, inventory {:.0}g)", self.clothes.len(),
632 self.appliances.len(), self.inventory_weight)
633 }
634}
635impl Eq for PlayerStatusC { }
636impl PartialEq for PlayerStatusC {
637 fn eq(&self, other: &Self) -> bool {
638 const EPS: f32 = 0.0001;
639
640 self.is_walking == other.is_walking &&
641 self.is_running == other.is_running &&
642 self.is_swimming == other.is_swimming &&
643 self.is_underwater == other.is_underwater &&
644 self.is_sleeping == other.is_sleeping &&
645 self.last_slept == other.last_slept &&
646 self.clothes == other.clothes &&
647 self.appliances == other.appliances &&
648 self.clothes_group == other.clothes_group &&
649 self.total_water_resistance == other.total_water_resistance &&
650 self.total_cold_resistance == other.total_cold_resistance &&
651 f32::abs(self.last_slept_duration - other.last_slept_duration) < EPS &&
652 f32::abs(self.warmth_level - other.warmth_level) < EPS &&
653 f32::abs(self.wetness_level - other.wetness_level) < EPS &&
654 f32::abs(self.inventory_weight - other.inventory_weight) < EPS
655 }
656}
657impl Hash for PlayerStatusC {
658 fn hash<H: Hasher>(&self, state: &mut H) {
659 self.is_walking.hash(state);
660 self.is_running.hash(state);
661 self.is_swimming.hash(state);
662 self.is_underwater.hash(state);
663 self.is_sleeping.hash(state);
664 self.last_slept.hash(state);
665 self.clothes.hash(state);
666 self.appliances.hash(state);
667 self.clothes_group.hash(state);
668 self.total_water_resistance.hash(state);
669 self.total_cold_resistance.hash(state);
670
671 state.write_u32((self.last_slept_duration*10_000_f32) as u32);
672 state.write_i32((self.warmth_level*10_000_f32) as i32);
673 state.write_u32((self.wetness_level*10_000_f32) as u32);
674 state.write_u32((self.inventory_weight*1_000_f32) as u32);
675 }
676}
677
678pub fn lerp(first: f32, second: f32, by: f32) -> f32 {
685 first * (1. - by) + second * by
686}
687
688pub fn clamp(value: f32, floor: f32, ceiling: f32) -> f32 {
695 if value > ceiling {
696 return ceiling;
697 }
698
699 if value < floor {
700 return floor;
701 }
702
703 value
704}
705
706pub fn clamp_to(value: f32, ceiling: f32) -> f32 {
713 if value > ceiling {
714 return ceiling;
715 }
716
717 value
718}
719
720pub fn clamp_bottom(value: f32, floor: f32) -> f32 {
727 if value < floor {
728 return floor;
729 }
730
731 value
732}
733
734
735pub fn clamp_01(value: f32) -> f32 {
742 if value > 1. {
743 return 1.;
744 }
745 if value < 0. {
746 return 0.;
747 }
748
749 value
750}
751
752pub fn roll_dice(probability: usize) -> bool {
761 if probability == 0 { return false; }
762 if probability >= 100 { return true; }
763
764 let mut rng = rand::thread_rng();
765 let r = rng.gen_range(0..100);
766
767 r < probability
768}
769
770pub fn range(a: f32, b: f32) -> f32 {
777 let mut rng = rand::thread_rng();
778
779 rng.gen_range(a..b)
780}