implement achievements

This commit is contained in:
yuni 2024-05-14 01:24:57 +02:00
parent e7c533d728
commit aa7734947c
6 changed files with 121 additions and 5 deletions

View file

@ -64,6 +64,7 @@ pub enum DamageType {
pub struct VehicleEnterExitEvent { pub struct VehicleEnterExitEvent {
vehicle: Entity, vehicle: Entity,
driver: Entity, driver: Entity,
name: Option<String>,
is_entering: bool, is_entering: bool,
is_player: bool is_player: bool
} }
@ -293,7 +294,7 @@ pub fn handle_input(
player: Query<Entity, With<actor::Player>>, player: Query<Entity, With<actor::Player>>,
q_camera: Query<&Transform, With<Camera>>, q_camera: Query<&Transform, With<Camera>>,
mut q_flashlight: Query<&mut Visibility, With<PlayersFlashLight>>, mut q_flashlight: Query<&mut Visibility, With<PlayersFlashLight>>,
q_vehicles: Query<(Entity, &Transform), (With<actor::Vehicle>, Without<actor::Player>, Without<Camera>)>, q_vehicles: Query<(Entity, &Actor, &Transform), (With<actor::Vehicle>, Without<actor::Player>, Without<Camera>)>,
mut ew_conv: EventWriter<chat::StartConversationEvent>, mut ew_conv: EventWriter<chat::StartConversationEvent>,
mut ew_vehicle: EventWriter<VehicleEnterExitEvent>, mut ew_vehicle: EventWriter<VehicleEnterExitEvent>,
mut ew_sfx: EventWriter<audio::PlaySfxEvent>, mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
@ -319,16 +320,20 @@ pub fn handle_input(
} }
// Entering Vehicles // Entering Vehicles
if q_player_drives.is_empty() { if q_player_drives.is_empty() {
let objects: Vec<(Entity, &Transform)> = q_vehicles let objects: Vec<((Entity, &Actor), &Transform)> = q_vehicles
.iter() .iter()
.map(|(entity, actor, transform)| ((entity, actor), transform))
.collect(); .collect();
if let (Some(entity), dist) = camera::find_closest_target::<Entity>(objects, camtrans) { if let (Some((entity, actor)), dist) =
camera::find_closest_target::<(Entity, &Actor)>(objects, camtrans)
{
if dist <= MAX_INTERACT_DISTANCE { if dist <= MAX_INTERACT_DISTANCE {
commands.entity(entity).insert(ActorVehicleBeingEntered); commands.entity(entity).insert(ActorVehicleBeingEntered);
commands.entity(player_entity).insert(ActorEnteringVehicle); commands.entity(player_entity).insert(ActorEnteringVehicle);
ew_vehicle.send(VehicleEnterExitEvent{ ew_vehicle.send(VehicleEnterExitEvent{
vehicle: entity, vehicle: entity,
driver: player_entity, driver: player_entity,
name: actor.name.clone(),
is_entering: q_player_drives.is_empty(), is_entering: q_player_drives.is_empty(),
is_player: true, is_player: true,
}); });
@ -344,6 +349,7 @@ pub fn handle_input(
ew_vehicle.send(VehicleEnterExitEvent{ ew_vehicle.send(VehicleEnterExitEvent{
vehicle: vehicle_entity, vehicle: vehicle_entity,
driver: player_entity, driver: player_entity,
name: None,
is_entering: false, is_entering: false,
is_player: true, is_player: true,
}); });
@ -368,6 +374,7 @@ pub fn handle_vehicle_enter_exit(
mut commands: Commands, mut commands: Commands,
mut settings: ResMut<Settings>, mut settings: ResMut<Settings>,
mut er_vehicle: EventReader<VehicleEnterExitEvent>, mut er_vehicle: EventReader<VehicleEnterExitEvent>,
mut ew_achievement: EventWriter<game::AchievementEvent>,
mut q_playerflashlight: Query<&mut Visibility, (With<PlayersFlashLight>, Without<ActorVehicleBeingEntered>, Without<ActorEnteringVehicle>)>, mut q_playerflashlight: Query<&mut Visibility, (With<PlayersFlashLight>, Without<ActorVehicleBeingEntered>, Without<ActorEnteringVehicle>)>,
mut q_drivers: Query<(Entity, &mut Visibility, Option<&Collider>), (Without<ActorVehicleBeingEntered>, With<ActorEnteringVehicle>)>, mut q_drivers: Query<(Entity, &mut Visibility, Option<&Collider>), (Without<ActorVehicleBeingEntered>, With<ActorEnteringVehicle>)>,
mut q_vehicles: Query<(Entity, &mut Vehicle, &mut Visibility), (With<ActorVehicleBeingEntered>, Without<ActorEnteringVehicle>)>, mut q_vehicles: Query<(Entity, &mut Vehicle, &mut Visibility), (With<ActorVehicleBeingEntered>, Without<ActorEnteringVehicle>)>,
@ -398,6 +405,10 @@ pub fn handle_vehicle_enter_exit(
*flashlight_vis = Visibility::Hidden; *flashlight_vis = Visibility::Hidden;
settings.flashlight_active = false; settings.flashlight_active = false;
} }
if let Some(vehicle_name) = &event.name {
ew_achievement.send(game::AchievementEvent
::RideVehicle(vehicle_name.clone()));
}
} }
else { else {
// Exiting Vehicle // Exiting Vehicle

View file

@ -618,6 +618,7 @@ pub fn handle_new_conversations(
mut er_conv: EventReader<StartConversationEvent>, mut er_conv: EventReader<StartConversationEvent>,
mut ew_sfx: EventWriter<audio::PlaySfxEvent>, mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
mut ew_chatevent: EventWriter<ChatEvent>, mut ew_chatevent: EventWriter<ChatEvent>,
mut ew_achievement: EventWriter<game::AchievementEvent>,
chatdb: Res<ChatDB>, chatdb: Res<ChatDB>,
q_chats: Query<&Chat>, q_chats: Query<&Chat>,
time: Res<Time>, time: Res<Time>,
@ -629,6 +630,9 @@ pub fn handle_new_conversations(
} }
match (*chatdb).get_chat_by_id(&event.talker.chat_name) { match (*chatdb).get_chat_by_id(&event.talker.chat_name) {
Ok(chat_id) => { Ok(chat_id) => {
if let Some(name) = &event.talker.name {
ew_achievement.send(game::AchievementEvent::TalkTo(name.clone()));
}
let mut chat = Chat { let mut chat = Chat {
internal_id: chat_id, internal_id: chat_id,
position: vec![0], position: vec![0],
@ -791,6 +795,7 @@ pub fn handle_chat_scripts(
mut q_playercam: Query<(&mut Position, &mut LinearVelocity), With<actor::PlayerCamera>>, mut q_playercam: Query<(&mut Position, &mut LinearVelocity), With<actor::PlayerCamera>>,
mut ew_sfx: EventWriter<audio::PlaySfxEvent>, mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
mut ew_effect: EventWriter<visual::SpawnEffectEvent>, mut ew_effect: EventWriter<visual::SpawnEffectEvent>,
mut ew_achievement: EventWriter<game::AchievementEvent>,
id2pos: Res<game::Id2Pos>, id2pos: Res<game::Id2Pos>,
) { ) {
for script in er_chatscript.read() { for script in er_chatscript.read() {
@ -838,6 +843,7 @@ pub fn handle_chat_scripts(
error!("Invalid parameter for command `{}`: `{}`", name, param1); error!("Invalid parameter for command `{}`: `{}`", name, param1);
} }
"repairsuit" => { "repairsuit" => {
ew_achievement.send(game::AchievementEvent::RepairSuit);
for (_, mut suit, _) in q_player.iter_mut() { for (_, mut suit, _) in q_player.iter_mut() {
suit.integrity = 1.0; suit.integrity = 1.0;
} }
@ -882,6 +888,9 @@ pub fn handle_chat_scripts(
duration: 5.1, duration: 5.1,
}); });
} }
"drinkpizza" => {
ew_achievement.send(game::AchievementEvent::DrinkPizza);
}
_ => { _ => {
error!("Error, undefined chat script {name}"); error!("Error, undefined chat script {name}");
} }

View file

@ -206,21 +206,25 @@
- if: $knows-menu - if: $knows-menu
I'd like a Suspicious Spacefunghi: I'd like a Suspicious Spacefunghi:
- Coming right up your feeding tube! - Coming right up your feeding tube!
- script: drinkpizza
- system: Received Suspicious Spacefunghi pizza smoothie - system: Received Suspicious Spacefunghi pizza smoothie
- goto: served - goto: served
- if: $knows-menu - if: $knows-menu
I'd like a Daring Durian: I'd like a Daring Durian:
- Coming right up your feeding tube! - Coming right up your feeding tube!
- script: drinkpizza
- system: Received Daring Durian pizza smoothie - system: Received Daring Durian pizza smoothie
- goto: served - goto: served
- if: $knows-menu - if: $knows-menu
I'd like an Artichoke Apple Pie pizza: I'd like an Artichoke Apple Pie pizza:
- Coming right up your feeding tube! - Coming right up your feeding tube!
- script: drinkpizza
- system: Received Artichoke Apple Pie pizza smoothie - system: Received Artichoke Apple Pie pizza smoothie
- goto: served - goto: served
- if: $knows-pineapple - if: $knows-pineapple
I'd like a pineapple pizza: I'd like a pineapple pizza:
- Coming right up your feeding tube! - Coming right up your feeding tube!
- script: drinkpizza
- system: Received pineapple pizza smoothie - system: Received pineapple pizza smoothie
- goto: served - goto: served
- if: $knows-coffee - if: $knows-coffee

View file

@ -529,6 +529,7 @@ fn spawn_entities(
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
mut materials_jupiter: ResMut<Assets<load::JupitersRing>>, mut materials_jupiter: ResMut<Assets<load::JupitersRing>>,
mut id2pos: ResMut<game::Id2Pos>, mut id2pos: ResMut<game::Id2Pos>,
mut achievement_tracker: ResMut<var::AchievementTracker>,
settings: Res<var::Settings>, settings: Res<var::Settings>,
) { ) {
for state_wrapper in er_spawn.read() { for state_wrapper in er_spawn.read() {
@ -734,9 +735,15 @@ fn spawn_entities(
pronoun: state.pronoun.clone(), pronoun: state.pronoun.clone(),
talking_speed: 1.0, talking_speed: 1.0,
}); });
if let Some(name) = &state.name {
achievement_tracker.all_people.insert(name.clone());
}
} }
if state.is_vehicle { if state.is_vehicle {
actor.insert(actor::Vehicle::default()); actor.insert(actor::Vehicle::default());
if let Some(name) = &state.name {
achievement_tracker.all_vehicles.insert(name.clone());
}
} }
if state.is_vehicle if state.is_vehicle
|| state.is_suited || state.is_suited

View file

@ -26,15 +26,27 @@ impl Plugin for GamePlugin {
app.add_systems(PostUpdate, handle_game_event); app.add_systems(PostUpdate, handle_game_event);
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.insert_resource(Id2Pos(HashMap::new())); app.insert_resource(Id2Pos(HashMap::new()));
app.insert_resource(var::AchievementTracker::default());
app.add_event::<PlayerDiesEvent>(); app.add_event::<PlayerDiesEvent>();
app.add_event::<GameEvent>(); app.add_event::<GameEvent>();
app.add_event::<AchievementEvent>();
} }
} }
#[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(Event)]
pub enum AchievementEvent {
RepairSuit,
TalkTo(String),
RideVehicle(String),
DrinkPizza,
InJupitersShadow,
}
#[derive(Event)] #[derive(Event)]
pub enum GameEvent { pub enum GameEvent {
SetAR(Turn), SetAR(Turn),
@ -46,6 +58,7 @@ pub enum GameEvent {
SetThirdPerson(Turn), SetThirdPerson(Turn),
SetRotationStabilizer(Turn), SetRotationStabilizer(Turn),
SetShadows(Turn), SetShadows(Turn),
Achievement(String),
} }
pub enum Turn { pub enum Turn {
@ -74,6 +87,7 @@ pub fn handle_game_event(
mut q_window: Query<&mut Window, With<PrimaryWindow>>, mut q_window: Query<&mut Window, With<PrimaryWindow>>,
mut q_light: Query<&mut DirectionalLight>, mut q_light: Query<&mut DirectionalLight>,
mut mapcam: ResMut<camera::MapCam>, mut mapcam: ResMut<camera::MapCam>,
mut log: ResMut<hud::Log>,
opt: Res<var::CommandLineOptions>, opt: Res<var::CommandLineOptions>,
) { ) {
for event in er_game.read() { for event in er_game.read() {
@ -127,6 +141,9 @@ pub fn handle_game_event(
light.shadows_enabled = settings.shadows_sun; light.shadows_enabled = settings.shadows_sun;
} }
} }
GameEvent::Achievement(name) => {
log.info(format!("Achievement accomplished: {name}!"));
}
} }
} }
} }
@ -140,6 +157,7 @@ fn handle_player_death(
mut active_asteroids: ResMut<world::ActiveAsteroids>, mut active_asteroids: ResMut<world::ActiveAsteroids>,
mut ew_effect: EventWriter<visual::SpawnEffectEvent>, mut ew_effect: EventWriter<visual::SpawnEffectEvent>,
mut ew_deathscreen: EventWriter<menu::DeathScreenEvent>, mut ew_deathscreen: EventWriter<menu::DeathScreenEvent>,
mut achievement_tracker: ResMut<var::AchievementTracker>,
mut log: ResMut<hud::Log>, mut log: ResMut<hud::Log>,
mut settings: ResMut<Settings>, mut settings: ResMut<Settings>,
) { ) {
@ -148,6 +166,7 @@ fn handle_player_death(
return; return;
} }
settings.reset_player_settings(); settings.reset_player_settings();
*achievement_tracker = var::AchievementTracker::default();
active_asteroids.0.clear(); active_asteroids.0.clear();
for entity in &q_noscenes { for entity in &q_noscenes {
cmd.entity(entity).despawn(); cmd.entity(entity).despawn();
@ -314,9 +333,10 @@ fn debug(
keyboard_input: Res<ButtonInput<KeyCode>>, keyboard_input: Res<ButtonInput<KeyCode>>,
mut commands: Commands, mut commands: Commands,
mut extended_materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, load::AsteroidSurface>>>, mut extended_materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, load::AsteroidSurface>>>,
achievement_tracker: Res<var::AchievementTracker>,
materials: Query<(Entity, Option<&Name>, &Handle<Mesh>)>, materials: Query<(Entity, Option<&Name>, &Handle<Mesh>)>,
) { ) {
if settings.dev_mode && keyboard_input.pressed(KeyCode::KeyP) { if settings.dev_mode && keyboard_input.just_pressed(KeyCode::KeyP) {
for (entity, _name, mesh) in &materials { for (entity, _name, mesh) in &materials {
dbg!(mesh); dbg!(mesh);
let mut entity = commands.entity(entity); let mut entity = commands.entity(entity);
@ -325,4 +345,54 @@ fn debug(
entity.insert(material); entity.insert(material);
} }
} }
if settings.dev_mode && keyboard_input.just_pressed(KeyCode::KeyN) {
dbg!(achievement_tracker);
}
}
fn handle_achievement_event(
mut er_achievement: EventReader<AchievementEvent>,
mut ew_game: EventWriter<GameEvent>,
mut tracker: ResMut<var::AchievementTracker>,
) {
for event in er_achievement.read() {
match event {
AchievementEvent::RepairSuit => {
if !tracker.repair_suit {
ew_game.send(GameEvent::Achievement("Repair Your Suit".into()));
}
tracker.repair_suit = true;
}
AchievementEvent::InJupitersShadow => {
if !tracker.in_jupiters_shadow {
ew_game.send(GameEvent::Achievement("Enter Jupiter's Shadow".into()));
}
tracker.in_jupiters_shadow = true;
}
AchievementEvent::DrinkPizza => {
if !tracker.drink_a_pizza {
ew_game.send(GameEvent::Achievement("Drink A Pizza".into()));
}
tracker.drink_a_pizza = true;
}
AchievementEvent::RideVehicle(name) => {
tracker.vehicles_ridden.insert(name.clone());
let len = tracker.vehicles_ridden.len();
let total = tracker.all_vehicles.len();
if !tracker.ride_every_vehicle && len == total {
tracker.ride_every_vehicle = true;
ew_game.send(GameEvent::Achievement("Ride Every Vehices".into()));
}
}
AchievementEvent::TalkTo(name) => {
tracker.people_talked_to.insert(name.clone());
let len = tracker.people_talked_to.len();
let total = tracker.all_people.len();
if !tracker.talk_to_everyone && len == total {
tracker.talk_to_everyone = true;
ew_game.send(GameEvent::Achievement("Talk To Everyone".into()));
}
}
}
}
} }

View file

@ -14,7 +14,7 @@
use crate::prelude::*; use crate::prelude::*;
use bevy::window::WindowMode; use bevy::window::WindowMode;
use bevy::prelude::*; use bevy::prelude::*;
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use serde::Deserialize; use serde::Deserialize;
use toml_edit::DocumentMut; use toml_edit::DocumentMut;
use std::env; use std::env;
@ -287,6 +287,21 @@ impl Settings {
} }
} }
#[derive(Resource, Default, Debug)]
pub struct AchievementTracker {
pub repair_suit: bool,
pub drink_a_pizza: bool,
pub in_jupiters_shadow: bool,
pub ride_every_vehicle: bool,
pub vehicles_ridden: HashSet<String>,
pub all_vehicles: HashSet<String>,
pub talk_to_everyone: bool,
pub people_talked_to: HashSet<String>,
pub all_people: HashSet<String>,
}
#[derive(Resource, Deserialize, Debug, Default)] #[derive(Resource, Deserialize, Debug, Default)]
#[serde(default)] #[serde(default)]
pub struct Preferences { pub struct Preferences {