fish_lib/models/
specimen.rs1use crate::config::ConfigInterface;
2use crate::game::errors::resource::GameResourceError;
3use crate::game::errors::GameResult;
4use crate::traits::model::Model;
5use crate::utils::random::random_normal_01;
6use chrono::{DateTime, Utc};
7use diesel::{AsChangeset, Insertable, Queryable, Selectable};
8use rand::random;
9use serde::{Deserialize, Serialize};
10use std::sync::Arc;
11
12#[derive(
13 Debug, Default, Serialize, Deserialize, Clone, PartialEq, Queryable, Selectable, AsChangeset,
14)]
15#[diesel(table_name = crate::schema::fish_specimens)]
16#[diesel(check_for_backend(diesel::pg::Pg))]
17pub struct Specimen {
18 pub id: i64,
20 pub user_id: i64,
22 pub species_id: i32,
24 pub created_at: DateTime<Utc>,
26 pub updated_at: DateTime<Utc>,
28 pub size_baby_ratio: f32,
30 pub size_adult_ratio: f32,
32 pub lifespan_days_ratio: f32,
34 pub catch_age: f32,
36}
37
38impl Specimen {
39 pub fn get_age(
40 &self,
41 config: Arc<dyn ConfigInterface>,
42 time_multiplier: f32,
43 ) -> GameResult<f32> {
44 let data = config
45 .get_species_data(self.species_id)
46 .ok_or_else(|| GameResourceError::species_not_found(self.species_id))?;
47 let lifespan_days = data.get_lifespan_days_by_ratio(self.lifespan_days_ratio);
48
49 if self.catch_age >= 1.0 {
50 return Ok(1.0);
51 }
52
53 let now = Utc::now();
54 let seconds_since_catch = (now - self.created_at).num_seconds();
55 let days_since_catch = seconds_since_catch as f32 / (86400f32);
56
57 let remaining_lifespan_days_after_catch = lifespan_days * (1.0 - self.catch_age);
58 let age_progress_since_catch =
59 (days_since_catch * time_multiplier) / remaining_lifespan_days_after_catch;
60
61 let age = (self.catch_age + age_progress_since_catch).clamp(0.0, 1.0);
62 Ok(age)
63 }
64
65 pub fn get_size_mm(
66 &self,
67 config: Arc<dyn ConfigInterface>,
68 time_multiplier: f32,
69 ) -> GameResult<f32> {
70 let data = config
71 .get_species_data(self.species_id)
72 .ok_or_else(|| GameResourceError::species_not_found(self.species_id))?;
73 let size_baby_mm = data.get_baby_size_by_ratio(self.size_baby_ratio);
74 let size_adult_mm = data.get_adult_size_by_ratio(self.size_adult_ratio);
75
76 let current_age = self.get_age(config, time_multiplier)?;
77 let size_mm = size_baby_mm + (size_adult_mm - size_baby_mm) * current_age;
78 Ok(size_mm)
79 }
80
81 pub fn get_weight_g(
82 &self,
83 config: Arc<dyn ConfigInterface>,
84 time_multiplier: f32,
85 ) -> GameResult<f32> {
86 let data = config
87 .get_species_data(self.species_id)
88 .ok_or_else(|| GameResourceError::species_not_found(self.species_id))?;
89 let weight_baby_g = data.get_baby_weight_by_ratio(self.size_baby_ratio);
90 let weight_adult_g = data.get_adult_weight_by_ratio(self.size_adult_ratio);
91
92 let current_age = self.get_age(config, time_multiplier)?;
93 let weight_g = weight_baby_g + (weight_adult_g - weight_baby_g) * current_age;
94 Ok(weight_g)
95 }
96
97 pub fn get_total_size_ratio(
98 &self,
99 config: Arc<dyn ConfigInterface>,
100 time_multiplier: f32,
101 ) -> GameResult<f32> {
102 let data = config
103 .get_species_data(self.species_id)
104 .ok_or_else(|| GameResourceError::species_not_found(self.species_id))?;
105 let min_possible_size = data.min_size_baby_mm as f32;
106 let max_possible_size = data.max_size_adult_mm as f32;
107
108 let current_size = self.get_size_mm(config, time_multiplier)?;
109
110 let ratio = (current_size - min_possible_size) / (max_possible_size - min_possible_size);
111 let total_size_ratio = ratio.clamp(0.0, 1.0);
112 Ok(total_size_ratio)
113 }
114}
115
116impl Model for Specimen {
117 type Table = crate::schema::fish_specimens::table;
118 type PrimaryKeyType = i64;
119 type InsertType = NewSpecimen;
120
121 fn table() -> Self::Table {
122 crate::schema::fish_specimens::table
123 }
124
125 fn id(&self) -> Self::PrimaryKeyType {
126 self.id
127 }
128}
129
130#[derive(Insertable)]
131#[diesel(table_name = crate::schema::fish_specimens)]
132#[diesel(check_for_backend(diesel::pg::Pg))]
133pub struct NewSpecimen {
134 pub user_id: i64,
135 pub species_id: i32,
136 pub size_baby_ratio: f32,
137 pub size_adult_ratio: f32,
138 pub lifespan_days_ratio: f32,
139 pub catch_age: f32,
140}
141
142impl NewSpecimen {
143 pub fn generate(user_id: i64, species_id: i32) -> NewSpecimen {
144 NewSpecimen {
145 user_id,
146 catch_age: random(),
147 species_id,
148 size_baby_ratio: random_normal_01(),
149 size_adult_ratio: random_normal_01(),
150 lifespan_days_ratio: random_normal_01(),
151 }
152 }
153}