use crate::ZaraController;
use crate::utils::{FrameC, EnvironmentC, HealthC, FrameSummaryC, PlayerStatusC, ActiveDiseaseC, ActiveInjuryC};
use crate::utils::event::{Listener, Event, MessageQueue};
use crate::error::ZaraUpdateErr;
use crate::health::StageLevel;
use std::time::Duration;
use std::collections::BTreeMap;
use std::cell::RefMut;
const UPDATE_INTERVAL: f32 = 1.;
const SLEEPING_UPDATE_INTERVAL: f32 = UPDATE_INTERVAL / 5.;
const MESSAGE_QUEUE_CHECK_PERIOD: f32 = UPDATE_INTERVAL / 3.;
impl<E: Listener + 'static> ZaraController<E> {
pub fn update(&self, frame_time: f32) -> Result<(), ZaraUpdateErr>{
if !self.health.is_alive() { return Err(ZaraUpdateErr::CharacterIsDead); }
if self.is_paused() { return Err(ZaraUpdateErr::InstancePaused); }
let elapsed = self.update_counter.get() + frame_time;
let elapsed_for_queue = self.queue_counter.get() + frame_time;
let mut ceiling = UPDATE_INTERVAL;
let game_time_duration = self.environment.game_time.duration.get();
if elapsed_for_queue >= MESSAGE_QUEUE_CHECK_PERIOD {
self.queue_counter.set(0.);
self.process_health_events();
self.process_inventory_events();
self.process_body_events();
} else {
self.queue_counter.set(elapsed_for_queue);
}
if self.body.is_sleeping() {
ceiling = SLEEPING_UPDATE_INTERVAL;
self.body.sleep_check(
&mut self.dispatcher.borrow_mut(),
&game_time_duration,
(game_time_duration - self.last_frame_game_time.get()).as_secs_f32()
);
}
if elapsed >= ceiling {
let summary = &self.get_summary();
let mut frame_data = &mut FrameC {
events: &mut self.dispatcher.borrow_mut(),
data: summary
};
self.health.update(&mut frame_data);
self.inventory.update(&mut frame_data);
self.body.update(&mut frame_data);
self.last_update_game_time.set(game_time_duration);
self.update_counter.set(0.);
} else {
self.update_counter.set(elapsed);
}
self.last_frame_game_time.set(Duration::from(game_time_duration));
Ok(())
}
fn get_summary(&self) -> FrameSummaryC {
let game_time_duration = self.environment.game_time.duration.get();
let time_delta = game_time_duration - self.last_update_game_time.get();
let mut active_diseases: Vec<ActiveDiseaseC> = Vec::new();
let mut active_injuries: Vec<ActiveInjuryC> = Vec::new();
let game_time_contract = &self.environment.game_time.to_contract();
for (_, disease) in self.health.diseases.borrow().iter() {
match disease.get_active_stage(game_time_contract) {
Some(st) => {
active_diseases.push(ActiveDiseaseC {
name: disease.disease.get_name(),
is_active: true,
scheduled_time: disease.activation_time(),
end_time: disease.end_time(),
current_level: st.info.level,
current_level_percent: st.percent_active(game_time_contract),
is_healing: disease.is_healing(),
needs_treatment: disease.needs_treatment
});
},
None => {
active_diseases.push(ActiveDiseaseC {
name: disease.disease.get_name(),
is_active: false,
scheduled_time: disease.activation_time(),
end_time: disease.end_time(),
current_level: StageLevel::Undefined,
current_level_percent: 0,
is_healing: false,
needs_treatment: disease.needs_treatment
});
}
}
};
for (_, injury) in self.health.injuries.borrow().iter() {
match injury.get_active_stage(game_time_contract) {
Some(st) => {
active_injuries.push(ActiveInjuryC {
name: injury.injury.get_name(),
is_active: true,
scheduled_time: injury.activation_time(),
end_time: injury.end_time(),
current_level: st.info.level,
current_level_percent: st.percent_active(game_time_contract),
is_healing: injury.is_healing(),
needs_treatment: injury.needs_treatment,
is_blood_stopped: injury.is_blood_stopped(),
body_part: injury.body_part,
is_fracture: injury.is_fracture
});
},
None => {
active_injuries.push(ActiveInjuryC {
name: injury.injury.get_name(),
is_active: false,
scheduled_time: injury.activation_time(),
end_time: injury.end_time(),
current_level: StageLevel::Undefined,
current_level_percent: 0,
is_healing: false,
needs_treatment: injury.needs_treatment,
is_blood_stopped: injury.is_blood_stopped(),
body_part: injury.body_part,
is_fracture: injury.is_fracture
});
}
}
};
FrameSummaryC {
game_time: self.environment.game_time.to_contract(),
game_time_delta: time_delta.as_secs_f32(),
player: PlayerStatusC {
is_walking: self.player_state.is_walking.get(),
is_running: self.player_state.is_running.get(),
is_swimming: self.player_state.is_swimming.get(),
is_underwater: self.player_state.is_underwater.get(),
is_sleeping: self.body.is_sleeping(),
last_slept_duration: self.body.last_sleep_duration(),
last_slept: self.body.last_sleep_time().as_ref().map(|x| x.clone()),
warmth_level: self.body.warmth_level(),
wetness_level: self.body.wetness_level(),
clothes: self.body.clothes.borrow().clone(),
clothes_group: self.body.clothes_group(),
appliances: self.body.appliances.borrow().clone(),
total_water_resistance: self.body.total_water_resistance(),
total_cold_resistance: self.body.total_cold_resistance(),
inventory_weight: self.inventory.get_weight()
},
environment: EnvironmentC {
wind_speed: self.environment.wind_speed.get(),
rain_intensity: self.environment.rain_intensity.get(),
temperature: self.environment.temperature.get()
},
health: HealthC {
body_temperature: self.health.body_temperature(),
blood_level: self.health.blood_level(),
heart_rate: self.health.heart_rate(),
water_level: self.health.water_level(),
food_level: self.health.food_level(),
top_pressure: self.health.top_pressure(),
bottom_pressure: self.health.bottom_pressure(),
stamina_level: self.health.stamina_level(),
fatigue_level: self.health.fatigue_level(),
oxygen_level: self.health.oxygen_level(),
diseases: active_diseases,
injuries: active_injuries
}
}
}
fn process_body_events(&self) {
if self.body.has_messages() {
self.process_events(self.body.get_message_queue());
}
}
fn process_inventory_events(&self) {
if self.inventory.has_messages() {
self.process_events(self.inventory.get_message_queue());
}
}
fn process_health_events(&self) {
if self.health.has_messages() {
self.process_events(self.health.get_message_queue());
}
}
fn process_events(&self, mut q: RefMut<BTreeMap<usize, Event>>) {
if q.len() == 0 { return }
let mut dispatcher = self.dispatcher.borrow_mut();
let mut key = 0;
loop {
match q.get(&key) {
Some(event) => {
dispatcher.dispatch(event.clone());
q.remove(&key);
},
None => break
}
key += 1;
}
}
}