implement InJupitersShadow achievement

This commit is contained in:
yuni 2024-05-14 04:35:45 +02:00
parent 2a4759abb2
commit 8f796c92fa
2 changed files with 58 additions and 0 deletions

View file

@ -63,3 +63,37 @@ pub fn alive(settings: Res<Settings>) -> bool {
pub fn in_control(settings: Res<Settings>) -> bool { pub fn in_control(settings: Res<Settings>) -> bool {
return settings.in_control(); return settings.in_control();
} }
pub fn in_shadow(
light_source_pos: DVec3,
light_source_r: f64,
shadow_caster_pos: DVec3,
shadow_caster_r: f64,
camera_pos: DVec3
) -> bool {
if light_source_r < shadow_caster_r {
error!("common::in_shadow only works if light_source_r > shadow_caster_r");
return false;
}
let direction = (shadow_caster_pos - light_source_pos).normalize();
let shadow_caster_to_player = camera_pos - shadow_caster_pos;
// Calculate the distance to the shadow caster if the player was projected
// onto the line of the shadow, with positive numbers = in the shadow,
// negative numbers = in between light source and shadow caster.
let projection_length = shadow_caster_to_player.dot(direction);
if projection_length < 0.0 {
return false;
}
let projection = projection_length * direction;
// Calculate the vector to the closest point along the shadow line
let closest_vector = shadow_caster_to_player - projection;
let distance_between_light_and_caster = (shadow_caster_pos - light_source_pos).length();
let max_distance = shadow_caster_r + ((shadow_caster_r - light_source_r) / distance_between_light_and_caster) * projection_length;
return closest_vector.length() < max_distance;
}

View file

@ -27,8 +27,11 @@ impl Plugin for GamePlugin {
app.add_systems(PreUpdate, handle_player_death); app.add_systems(PreUpdate, handle_player_death);
app.add_systems(PostUpdate, update_id2pos); app.add_systems(PostUpdate, update_id2pos);
app.add_systems(Update, handle_achievement_event.run_if(on_event::<AchievementEvent>())); app.add_systems(Update, handle_achievement_event.run_if(on_event::<AchievementEvent>()));
app.add_systems(Update, check_achievements);
app.insert_resource(Id2Pos(HashMap::new())); app.insert_resource(Id2Pos(HashMap::new()));
app.insert_resource(var::AchievementTracker::default()); app.insert_resource(var::AchievementTracker::default());
app.insert_resource(AchievementCheckTimer(
Timer::from_seconds(1.0, TimerMode::Repeating)));
app.add_event::<PlayerDiesEvent>(); app.add_event::<PlayerDiesEvent>();
app.add_event::<GameEvent>(); app.add_event::<GameEvent>();
app.add_event::<AchievementEvent>(); app.add_event::<AchievementEvent>();
@ -37,6 +40,7 @@ impl Plugin for GamePlugin {
#[derive(Event)] pub struct PlayerDiesEvent(pub actor::DamageType); #[derive(Event)] pub struct PlayerDiesEvent(pub actor::DamageType);
#[derive(Resource)] pub struct Id2Pos(pub HashMap<String, DVec3>); #[derive(Resource)] pub struct Id2Pos(pub HashMap<String, DVec3>);
#[derive(Resource)] pub struct AchievementCheckTimer(pub Timer);
#[derive(Event)] #[derive(Event)]
pub enum AchievementEvent { pub enum AchievementEvent {
@ -396,3 +400,23 @@ fn handle_achievement_event(
} }
} }
} }
fn check_achievements(
time: Res<Time>,
q_player: Query<&Position, With<actor::PlayerCamera>>,
id2pos: Res<Id2Pos>,
mut ew_achievement: EventWriter<AchievementEvent>,
mut timer: ResMut<AchievementCheckTimer>,
) {
if !timer.0.tick(time.delta()).just_finished() { return; }
let pos_player = if let Ok(pos) = q_player.get_single() { pos } else { return; };
let pos_sun = if let Some(pos) = id2pos.0.get("sol") { pos } else { return; };
let pos_jupiter = if let Some(pos) = id2pos.0.get("jupiter") { pos } else { return; };
let shadowed = in_shadow(*pos_sun, nature::SOL_RADIUS,
*pos_jupiter, nature::JUPITER_RADIUS, **pos_player);
if shadowed {
ew_achievement.send(AchievementEvent::InJupitersShadow);
}
}