use crate::{ GameState, cash::Cash, loading_assets::GameAssets, physics_layers::GameLayer, protocol::{SkipReplicateColliders, TbMapIdCounter}, utils::global_observer, }; use avian3d::{ parry::{na::SVector, shape::SharedShape}, prelude::*, }; use bevy::{ ecs::{lifecycle::HookContext, world::DeferredWorld}, math::*, prelude::*, }; use bevy_trenchbroom::prelude::*; use serde::{Deserialize, Serialize}; #[point_class(base(Transform), model({ "path": "models/spawn.glb" }))] #[derive(Default)] #[component(on_add = Self::on_add)] pub struct SpawnPoint {} impl SpawnPoint { fn on_add(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) { let Some(assets) = world.get_resource::() else { return; }; let mesh = assets.mesh_spawn.clone(); world.commands().entity(entity).insert(( Name::new("spawn"), SceneRoot(mesh), RigidBody::Static, ColliderConstructorHierarchy::new(ColliderConstructor::ConvexHullFromMesh), )); } } #[solid_class( hooks(SpawnHooks::new().convex_collider()) )] #[derive(Default)] pub struct Worldspawn; #[solid_class(base(Transform), hooks(SpawnHooks::new()))] #[derive(Default)] pub struct Water; #[solid_class(base(Transform), hooks(SpawnHooks::new().convex_collider()))] #[derive(Default)] pub struct Crates; #[solid_class(base(Transform), hooks(SpawnHooks::new().convex_collider()))] #[derive(Default)] pub struct NamedEntity { pub name: String, } #[solid_class(base(Transform, Target), hooks(SpawnHooks::new().convex_collider()))] #[derive(Default)] #[require(RigidBody = RigidBody::Kinematic)] pub struct Platform; #[point_class(base(Transform))] #[derive(Default)] pub struct PlatformTarget { pub targetname: String, } #[solid_class(base(Transform, Target), hooks(SpawnHooks::new().convex_collider()))] #[derive(Default, Serialize, Deserialize, PartialEq)] #[require(RigidBody = RigidBody::Kinematic)] pub struct Movable { pub name: String, } #[point_class(base(Transform))] #[derive(Default)] pub struct MoveTarget { pub targetname: String, } #[point_class(base(Transform))] #[derive(Default)] pub struct CameraTarget { pub targetname: String, } #[point_class(base(Transform, Target))] #[derive(Default)] pub struct CutsceneCamera { pub name: String, pub targetname: String, } #[point_class(base(Transform, Target))] #[derive(Default)] pub struct CutsceneCameraMovementEnd; #[point_class(base(Transform), model({ "path": "models/alien_naked.glb" }))] #[derive(Default)] #[component(on_add = Self::on_add)] pub struct EnemySpawn { pub head: String, pub key: String, pub disable_ai: bool, pub spawn_order: Option, } impl EnemySpawn { fn on_add(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) { //TODO: figure out why this crashes if removed let Some(_assets) = world.get_resource::() else { return; }; let this = world.get_entity(entity).unwrap().get::().unwrap(); let this_transform = world .get_entity(entity) .unwrap() .get::() .unwrap(); let mut this_transform = *this_transform; this_transform.translation += Vec3::new(0., 1.5, 0.); let position = Position::new(this_transform.translation); let rotation = Rotation(this_transform.rotation); let head = this.head.clone(); world.commands().entity(entity).insert(( this_transform, position, rotation, Name::from(format!("enemy [{head}]")), Visibility::default(), RigidBody::Kinematic, Collider::capsule(0.6, 2.), CollisionLayers::new(LayerMask(GameLayer::Npc.to_bits()), LayerMask::ALL), LockedAxes::new().lock_rotation_z().lock_rotation_x(), )); } } #[point_class(base(Transform), model({ "path": "models/cash.glb" }))] #[derive(Default)] #[component(on_add = Self::on_add)] pub struct CashSpawn {} impl CashSpawn { fn on_add(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) { let Some(assets) = world.get_resource::() else { return; }; let mesh = assets.mesh_cash.clone(); world.commands().entity(entity).insert(( Name::new("cash"), SceneRoot(mesh), Cash, Collider::cuboid(2., 3.0, 2.), CollisionLayers::new(GameLayer::CollectibleSensors, LayerMask::ALL), RigidBody::Static, CollisionEventsEnabled, Sensor, )); } } #[point_class(base(Transform), model({ "path": "models/head_drop.glb" }))] #[derive(Default)] pub struct SecretHead { pub head_id: usize, } fn fix_target_tb_entities( mut commands: Commands, mut entities: Query<(Entity, &Transform, &Collider), With>, ) { for (entity, tf, coll) in entities.iter_mut() { if let Some(shape) = coll.shape().as_compound() { let mut shapes: Vec<_> = shape.shapes().to_vec(); for shape in shapes.iter_mut() { shape.0.translation.vector -= SVector::::from(tf.translation.to_array()); } commands .entity(entity) .insert(Collider::from(SharedShape::compound(shapes))); } } } pub fn plugin(app: &mut App) { app.register_type::(); app.override_class::(); app.register_type::(); app.register_type::(); app.register_type::(); app.register_type::(); app.register_type::(); app.register_type::(); app.register_type::(); app.register_type::(); app.register_type::(); app.register_type::(); app.register_type::(); app.register_type::(); app.register_type::(); app.add_systems(OnExit(GameState::MapLoading), fix_target_tb_entities); global_observer!(app, tb_component_setup::); global_observer!(app, tb_component_setup::); global_observer!(app, tb_component_setup::); global_observer!(app, tb_component_setup::); } fn tb_component_setup( trigger: On, mut commands: Commands, mut world: DeferredWorld, ) { let id = world.resource_mut::().alloc(); commands .entity(trigger.event().entity) .insert_if_new(id) .insert(SkipReplicateColliders); }