225 lines
6.4 KiB
Rust
225 lines
6.4 KiB
Rust
use crate::{
|
|
GameState,
|
|
camera::{CameraArmRotation, CameraTarget},
|
|
cash::{Cash, CashCollectEvent},
|
|
character::{AnimatedCharacter, CharacterAnimations},
|
|
control::controller_common::{CharacterControllerBundle, PlayerMovement},
|
|
global_observer,
|
|
head::ActiveHead,
|
|
heads::{ActiveHeads, HeadChanged, HeadState},
|
|
heads_database::{HeadControls, HeadsDatabase},
|
|
hitpoints::Hitpoints,
|
|
loading_assets::AudioAssets,
|
|
physics_layers::GameLayer,
|
|
sounds::PlaySound,
|
|
tb_entities::SpawnPoint,
|
|
};
|
|
use avian3d::{math::Vector, prelude::*};
|
|
use bevy::{
|
|
input::common_conditions::input_just_pressed,
|
|
prelude::*,
|
|
window::{CursorGrabMode, PrimaryWindow},
|
|
};
|
|
|
|
use std::time::Duration;
|
|
|
|
#[derive(Component, Default)]
|
|
pub struct Player;
|
|
|
|
#[derive(Component, Default)]
|
|
struct PlayerAnimations;
|
|
|
|
#[derive(Component, Default)]
|
|
pub struct PlayerBodyMesh;
|
|
|
|
pub fn plugin(app: &mut App) {
|
|
app.add_systems(Startup, (toggle_cursor_system, cursor_recenter));
|
|
app.add_systems(OnEnter(GameState::Playing), spawn);
|
|
app.add_systems(
|
|
Update,
|
|
(
|
|
collect_cash,
|
|
toggle_animation,
|
|
setup_animations_marker_for_player,
|
|
toggle_cursor_system.run_if(input_just_pressed(KeyCode::Escape)),
|
|
)
|
|
.run_if(in_state(GameState::Playing)),
|
|
);
|
|
|
|
global_observer!(app, on_update_head);
|
|
}
|
|
|
|
fn spawn(
|
|
mut commands: Commands,
|
|
asset_server: Res<AssetServer>,
|
|
query: Query<&Transform, With<SpawnPoint>>,
|
|
heads_db: Res<HeadsDatabase>,
|
|
) {
|
|
let Some(spawn) = query.iter().next() else {
|
|
return;
|
|
};
|
|
|
|
let transform = Transform::from_translation(spawn.translation + Vec3::new(0., 3., 0.));
|
|
|
|
let gravity = Vector::NEG_Y * 40.0;
|
|
let collider = Collider::capsule(0.9, 1.2);
|
|
|
|
commands.spawn((
|
|
Name::from("player"),
|
|
Player,
|
|
ActiveHead(0),
|
|
ActiveHeads::new([
|
|
Some(HeadState::new(0, heads_db.as_ref())),
|
|
Some(HeadState::new(3, heads_db.as_ref())),
|
|
Some(HeadState::new(6, heads_db.as_ref())),
|
|
Some(HeadState::new(10, heads_db.as_ref())),
|
|
Some(HeadState::new(9, heads_db.as_ref())),
|
|
]),
|
|
Hitpoints::new(100),
|
|
CameraTarget,
|
|
transform,
|
|
Visibility::default(),
|
|
// LockedAxes::ROTATION_LOCKED, todo
|
|
CollisionLayers::new(LayerMask(GameLayer::Player.to_bits()), LayerMask::ALL),
|
|
CharacterControllerBundle::new(collider, gravity),
|
|
children![(
|
|
Name::new("player-rig"),
|
|
Transform::default(),
|
|
Visibility::default(),
|
|
PlayerBodyMesh,
|
|
CameraArmRotation,
|
|
children![AnimatedCharacter(0)]
|
|
)],
|
|
));
|
|
|
|
commands.spawn((
|
|
AudioPlayer::new(asset_server.load("sfx/heads/angry demonstrator.ogg")),
|
|
PlaybackSettings::DESPAWN,
|
|
));
|
|
}
|
|
|
|
fn cursor_recenter(q_windows: Single<&mut Window, With<PrimaryWindow>>) {
|
|
let mut primary_window = q_windows;
|
|
let center = Vec2::new(
|
|
primary_window.resolution.width() / 2.,
|
|
primary_window.resolution.height() / 2.,
|
|
);
|
|
primary_window.set_cursor_position(Some(center));
|
|
}
|
|
|
|
fn toggle_grab_cursor(window: &mut Window) {
|
|
match window.cursor_options.grab_mode {
|
|
CursorGrabMode::None => {
|
|
window.cursor_options.grab_mode = CursorGrabMode::Confined;
|
|
window.cursor_options.visible = false;
|
|
}
|
|
_ => {
|
|
window.cursor_options.grab_mode = CursorGrabMode::None;
|
|
window.cursor_options.visible = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn toggle_cursor_system(mut window: Single<&mut Window, With<PrimaryWindow>>) {
|
|
toggle_grab_cursor(&mut window);
|
|
}
|
|
|
|
fn collect_cash(
|
|
mut commands: Commands,
|
|
mut collision_event_reader: EventReader<CollisionStarted>,
|
|
query_player: Query<&Player>,
|
|
query_cash: Query<&Cash>,
|
|
) {
|
|
for CollisionStarted(e1, e2) in collision_event_reader.read() {
|
|
let collect = if query_player.contains(*e1) && query_cash.contains(*e2) {
|
|
Some(*e2)
|
|
} else if query_player.contains(*e2) && query_cash.contains(*e1) {
|
|
Some(*e1)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
if let Some(cash) = collect {
|
|
commands.trigger(CashCollectEvent);
|
|
commands.entity(cash).despawn();
|
|
}
|
|
}
|
|
}
|
|
|
|
fn setup_animations_marker_for_player(
|
|
mut commands: Commands,
|
|
animation_handles: Query<Entity, Added<AnimationGraphHandle>>,
|
|
children: Query<&ChildOf>,
|
|
player: Query<&PlayerBodyMesh>,
|
|
) {
|
|
for entity in animation_handles.iter() {
|
|
for ancestor in children.iter_ancestors(entity) {
|
|
if player.contains(ancestor) {
|
|
commands.entity(entity).insert(PlayerAnimations);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn toggle_animation(
|
|
mut transitions: Query<
|
|
(
|
|
&mut AnimationTransitions,
|
|
&mut AnimationPlayer,
|
|
&CharacterAnimations,
|
|
),
|
|
With<PlayerAnimations>,
|
|
>,
|
|
movement: Res<PlayerMovement>,
|
|
) {
|
|
if movement.is_changed() {
|
|
for (mut transition, mut player, animations) in &mut transitions {
|
|
let index = if movement.any_direction {
|
|
animations.run
|
|
} else {
|
|
animations.idle
|
|
};
|
|
|
|
transition
|
|
.play(&mut player, index, Duration::from_millis(100))
|
|
.repeat();
|
|
}
|
|
}
|
|
}
|
|
|
|
fn on_update_head(
|
|
trigger: Trigger<HeadChanged>,
|
|
mut commands: Commands,
|
|
body_mesh: Single<Entity, With<PlayerBodyMesh>>,
|
|
mut player: Single<&mut ActiveHead, With<Player>>,
|
|
head_db: Res<HeadsDatabase>,
|
|
audio_assets: Res<AudioAssets>,
|
|
) {
|
|
let body_mesh = *body_mesh;
|
|
|
|
player.0 = trigger.0;
|
|
|
|
let head_str = head_db.head_key(trigger.0);
|
|
|
|
commands.trigger(PlaySound::Head(head_str.to_string()));
|
|
|
|
commands.entity(body_mesh).despawn_related::<Children>();
|
|
|
|
commands
|
|
.entity(body_mesh)
|
|
.with_child(AnimatedCharacter(trigger.0));
|
|
|
|
//TODO: make part of full character mesh later
|
|
if head_db.head_stats(trigger.0).controls == HeadControls::Plane {
|
|
commands.entity(body_mesh).with_child((
|
|
Name::new("sfx"),
|
|
AudioPlayer::new(audio_assets.jet.clone()),
|
|
PlaybackSettings {
|
|
mode: bevy::audio::PlaybackMode::Loop,
|
|
..Default::default()
|
|
},
|
|
));
|
|
}
|
|
}
|