1use crate::establish_connection;
2use crate::interactions::Interaction;
3use crate::player::Player;
4use crate::player::PlayerState;
5use crate::schema::players;
6use crate::time::CustomDateTime;
7use chrono::Datelike;
8use chrono::NaiveDateTime;
9use chrono::Utc;
10use diesel::ExpressionMethods;
11use diesel::QueryDsl;
12use diesel::RunQueryDsl;
13use log::{info, trace};
14use std::time::Duration;
15use tokio::time::interval;
16
17pub type Zombie = Player;
19
20impl Zombie {
21 pub async fn get_next_feeding(&self) -> Result<Option<CustomDateTime>, String> {
28 Ok(self.next_feeding)
29 }
30
31 pub async fn starvation(&self) -> Result<Vec<(String, Option<CustomDateTime>)>, String> {
39 let connection = &mut establish_connection();
40
41 let starvation_all = players::table
42 .select((players::alias, players::state, players::next_feeding))
43 .filter(players::state.eq(PlayerState::Zombie))
44 .or_filter(players::state.eq(PlayerState::Zombie0))
45 .order(players::next_feeding.asc())
46 .load::<(String, PlayerState, Option<CustomDateTime>)>(connection)
47 .map_err(|e| format!("Failed to load starvation: {}", e))?;
48
49 let mut seen_z0 = false;
50 let mut starvation = Vec::new();
51 for (alias, state, next_feeding) in starvation_all {
52 if state == PlayerState::Zombie0 {
53 if !seen_z0 {
54 starvation.push(("Z0".to_owned(), next_feeding));
55 seen_z0 = true;
56 }
57 } else {
58 starvation.push((alias, next_feeding));
59 }
60 }
61
62 Ok(starvation)
63 }
64
65 pub async fn hunt(
75 &self,
76 target: &Player,
77 feed1: Option<&Player>,
78 feed2: Option<&Player>,
79 ) -> Result<(), String> {
80 target.zombify().await?;
81 self.feed(None).await?;
82 target.feed(None).await?;
83 if let Some(zombie_feed1) = feed1 {
84 zombie_feed1.feed(None).await?;
85 }
86 if let Some(zombie_feed2) = feed2 {
87 zombie_feed2.feed(None).await?;
88 }
89
90 Interaction::save_hunt_interaction(self, target, feed1, feed2).await?;
91
92 Ok(())
93 }
94
95 pub async fn zombify(&self) -> Result<(), String> {
103 let connection = &mut establish_connection();
104
105 diesel::update(players::table.filter(players::id.eq(self.id.clone())))
106 .set(players::state.eq(PlayerState::Zombie))
107 .execute(connection)
108 .expect("Failed to zombify player.");
109
110 info!("Player {:?} hunted", self.name);
111
112 Ok(())
113 }
114
115 pub async fn feed(&self, amount: Option<i64>) -> Result<Option<CustomDateTime>, String> {
123 let connection = &mut establish_connection();
124
125 let mut new_feeding_time: CustomDateTime = Self::feeding_time_calc()?;
126 if let Some(amount) = amount {
127 new_feeding_time = CustomDateTime::now() + chrono::Duration::hours(amount);
128 let weekday = new_feeding_time.weekday().num_days_from_monday();
129 if weekday == 5 || weekday == 6 {
130 new_feeding_time += chrono::Duration::days(2);
131 }
132 }
133
134 diesel::update(players::table.filter(players::id.eq(self.id.clone())))
135 .set(players::next_feeding.eq(new_feeding_time))
136 .execute(connection)
137 .expect("Failed to update player next feeding");
138 return Ok(Some(new_feeding_time));
139 }
140
141 pub fn feeding_time_calc() -> Result<CustomDateTime, String> {
146 let day_of_the_week = chrono::Local::now().weekday().num_days_from_monday();
147
148 let next_feeding_time = match day_of_the_week {
149 0 | 1 => CustomDateTime::now() + chrono::Duration::days(3),
150 2 | 3 | 4 => CustomDateTime::now() + chrono::Duration::days(5),
151 5 => CustomDateTime::now() + chrono::Duration::days(4),
152 6 => CustomDateTime::now() + chrono::Duration::days(3),
153 _ => return Err("Invalid day of the week".to_string()),
154 };
155
156 Ok(next_feeding_time)
157 }
158
159 pub async fn monitor_zombie_death() -> ! {
167 let connection = &mut establish_connection();
168 let mut interval = interval(Duration::from_secs(60)); let date_none: Option<NaiveDateTime> = None;
170 loop {
171 interval.tick().await;
172 trace!("Checking zombie death... Current time: {}", Utc::now());
173 diesel::update(players::table.filter(players::next_feeding.lt(CustomDateTime::now())))
174 .set((
175 players::state.eq(PlayerState::Corpse),
176 players::next_feeding.eq(date_none),
177 ))
178 .execute(connection)
179 .expect("Failed to update player state");
180 }
181 }
182}