nightshade-api 0.48.0

Procedural high level API for the nightshade game engine
Documentation
//! Forces, raycasts, and collision events for entities spawned with a
//! [`Body`](crate::prelude::Body).

use nightshade::ecs::physics::joints::{
    FixedJoint, JointAxisDirection, JointHandle, RevoluteJoint, RopeJoint, SpringJoint,
    create_fixed_joint, create_revolute_joint, create_rope_joint, create_spring_joint,
};
use nightshade::prelude::*;

/// Applies an instantaneous impulse to a dynamic entity. Good for jumps,
/// knockback, and explosions.
#[inline]
pub fn push(world: &mut World, entity: Entity, impulse: Vec3) {
    physics_world_apply_impulse(&mut world.resources.physics, entity, impulse);
}

/// Sets a dynamic entity's linear velocity directly. Good for launching
/// projectiles.
#[inline]
pub fn set_velocity(world: &mut World, entity: Entity, velocity: Vec3) {
    physics_world_set_linear_velocity(&mut world.resources.physics, entity, velocity);
}

/// Casts a ray against all physics colliders and returns the closest hit.
#[inline]
pub fn raycast(
    world: &World,
    origin: Vec3,
    direction: Vec3,
    max_distance: f32,
) -> Option<RaycastHit> {
    physics_world_cast_ray(
        &world.resources.physics,
        origin,
        direction,
        max_distance,
        None,
    )
}

/// The collision events from the last physics step.
#[inline]
pub fn collisions(world: &World) -> &[CollisionEvent] {
    physics_world_collision_events(&world.resources.physics)
}

/// Welds two bodies together rigidly.
#[inline]
pub fn attach_fixed(world: &mut World, parent: Entity, child: Entity) -> Option<JointHandle> {
    create_fixed_joint(world, parent, child, FixedJoint::new())
}

/// Hinges two bodies around an axis. Doors, levers, wheels.
#[inline]
pub fn attach_hinge(
    world: &mut World,
    parent: Entity,
    child: Entity,
    axis: JointAxisDirection,
) -> Option<JointHandle> {
    create_revolute_joint(world, parent, child, RevoluteJoint::new(axis))
}

/// Connects two bodies with a spring. Suspension, bouncy attachments.
#[inline]
pub fn attach_spring(
    world: &mut World,
    parent: Entity,
    child: Entity,
    rest_length: f32,
    stiffness: f32,
    damping: f32,
) -> Option<JointHandle> {
    create_spring_joint(
        world,
        parent,
        child,
        SpringJoint::new(rest_length, stiffness, damping),
    )
}

/// Tethers two bodies with a maximum separation. Chains, leashes, pendulums.
#[inline]
pub fn attach_rope(
    world: &mut World,
    parent: Entity,
    child: Entity,
    max_distance: f32,
) -> Option<JointHandle> {
    create_rope_joint(world, parent, child, RopeJoint::new(max_distance))
}

/// Applies a continuous force to a dynamic entity for this step. Unlike [`push`]
/// (an instantaneous impulse), this acts over time, for thrust, wind, or custom
/// gravity.
#[inline]
pub fn apply_force(world: &mut World, entity: Entity, force: Vec3) {
    physics_world_apply_force(&mut world.resources.physics, entity, force);
}

/// Applies a continuous torque to a dynamic entity for this step, spinning it up
/// over time.
#[inline]
pub fn apply_torque(world: &mut World, entity: Entity, torque: Vec3) {
    physics_world_apply_torque(&mut world.resources.physics, entity, torque);
}

/// Sets a dynamic entity's angular velocity directly, in radians per second
/// about each axis.
#[inline]
pub fn set_angular_velocity(world: &mut World, entity: Entity, velocity: Vec3) {
    physics_world_set_angular_velocity(&mut world.resources.physics, entity, velocity);
}

/// The entity's current linear velocity, if it has a body.
#[inline]
pub fn velocity(world: &World, entity: Entity) -> Option<Vec3> {
    physics_world_linear_velocity(&world.resources.physics, entity)
}

/// The entity's current angular velocity, if it has a body.
#[inline]
pub fn angular_velocity(world: &World, entity: Entity) -> Option<Vec3> {
    physics_world_angular_velocity(&world.resources.physics, entity)
}

/// Every entity whose collider overlaps a sphere, for proximity tests that do
/// not need a physical contact.
#[inline]
pub fn overlap_sphere(world: &World, center: Vec3, radius: f32) -> Vec<Entity> {
    physics_world_overlap_sphere(&world.resources.physics, center, radius, None)
}

/// Spawns a dynamic cylinder physics body of `half_height` and `radius` at
/// `position`, with `mass` and a linear RGBA `color`. The shapes
/// [`spawn_object`](crate::prelude::spawn_object) does not cover come through
/// here and the collider setters below.
pub fn spawn_cylinder_body(
    world: &mut World,
    position: Vec3,
    half_height: f32,
    radius: f32,
    mass: f32,
    color: [f32; 4],
) -> Entity {
    let material = nightshade::ecs::material::components::Material {
        base_color: color,
        ..Default::default()
    };
    nightshade::ecs::physics::commands::spawn_dynamic_physics_cylinder_with_material(
        world,
        position,
        half_height,
        radius,
        mass,
        material,
    )
}

/// Sets the friction of an entity's collider after spawn, applied live to the
/// running simulation. 0.0 is frictionless ice, 1.0 is grippy.
pub fn set_friction(world: &mut World, entity: Entity, friction: f32) {
    if let Some(collider) = world.core.get_collider_mut(entity) {
        collider.friction = friction;
    }
    physics_world_set_friction(&mut world.resources.physics, entity, friction);
}

/// Sets the restitution (bounciness) of an entity's collider after spawn,
/// applied live. 0.0 is a dead thud, 1.0 is a perfect elastic bounce.
pub fn set_restitution(world: &mut World, entity: Entity, restitution: f32) {
    if let Some(collider) = world.core.get_collider_mut(entity) {
        collider.restitution = restitution;
    }
    physics_world_set_restitution(&mut world.resources.physics, entity, restitution);
}

/// Sets linear damping on a dynamic body after spawn, applied live. Higher
/// values bleed off translational motion over time, like moving through fluid.
pub fn set_linear_damping(world: &mut World, entity: Entity, damping: f32) {
    if let Some(body) = world.core.get_rigid_body_mut(entity) {
        body.linear_damping = damping;
    }
    physics_world_set_linear_damping(&mut world.resources.physics, entity, damping);
}

/// Sets angular damping on a dynamic body after spawn, applied live. Higher
/// values settle spin over time.
pub fn set_angular_damping(world: &mut World, entity: Entity, damping: f32) {
    if let Some(body) = world.core.get_rigid_body_mut(entity) {
        body.angular_damping = damping;
    }
    physics_world_set_angular_damping(&mut world.resources.physics, entity, damping);
}

/// Sets the mass of a dynamic body after spawn, applied live, in kilograms.
pub fn set_mass(world: &mut World, entity: Entity, mass: f32) {
    if let Some(body) = world.core.get_rigid_body_mut(entity) {
        body.mass = mass;
    }
    physics_world_set_additional_mass(&mut world.resources.physics, entity, mass);
}

/// Sets the per-body gravity multiplier after spawn, applied live. 1.0 is
/// normal, 0.0 makes the body float, 2.0 doubles its fall, negative lifts it.
pub fn set_gravity_scale(world: &mut World, entity: Entity, scale: f32) {
    if let Some(body) = world.core.get_rigid_body_mut(entity) {
        body.gravity_scale = scale;
    }
    physics_world_set_gravity_scale(&mut world.resources.physics, entity, scale);
}

/// Turns the entity's collider into a sensor (trigger): it stops blocking
/// movement and instead reports overlaps through [`collisions`], where the
/// event's `is_sensor` field is true.
pub fn make_sensor(world: &mut World, entity: Entity) {
    if let Some(collider) = world.core.get_collider_mut(entity) {
        collider.is_sensor = true;
    }
}

/// Sets the entity collider's collision groups as two bitmasks. `membership` is
/// the set of layers it belongs to, `filter` is the set it collides with. Two
/// colliders interact only when each one's membership intersects the other's
/// filter.
pub fn set_collision_groups(world: &mut World, entity: Entity, membership: u32, filter: u32) {
    if let Some(collider) = world.core.get_collider_mut(entity) {
        collider.collision_groups =
            nightshade::ecs::physics::types::InteractionGroups::new(membership, filter);
    }
}