simple rotate to player ai while engaging
This commit is contained in:
@@ -1116,6 +1116,7 @@
|
|||||||
"angles" "0 -90 0"
|
"angles" "0 -90 0"
|
||||||
"head" "field medic"
|
"head" "field medic"
|
||||||
"key" "fence_gate"
|
"key" "fence_gate"
|
||||||
|
"disable_ai" "true"
|
||||||
}
|
}
|
||||||
// entity 3
|
// entity 3
|
||||||
{
|
{
|
||||||
@@ -1128,6 +1129,7 @@
|
|||||||
"origin" "3256 248 -232"
|
"origin" "3256 248 -232"
|
||||||
"head" "field medic"
|
"head" "field medic"
|
||||||
"angles" "0 0 0"
|
"angles" "0 0 0"
|
||||||
|
"disable_ai" "true"
|
||||||
}
|
}
|
||||||
// entity 5
|
// entity 5
|
||||||
{
|
{
|
||||||
|
|||||||
128
src/ai/mod.rs
128
src/ai/mod.rs
@@ -6,19 +6,141 @@ use crate::{
|
|||||||
aim::AimTarget,
|
aim::AimTarget,
|
||||||
heads::ActiveHeads,
|
heads::ActiveHeads,
|
||||||
heads_database::HeadsDatabase,
|
heads_database::HeadsDatabase,
|
||||||
|
player::Player,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Component, Reflect)]
|
#[derive(Component, Reflect)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct Ai;
|
pub struct Ai;
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Clone)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
struct WaitForAnyPlayer;
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Clone)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
struct Engage(Entity);
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Clone)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
struct Reload;
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.add_systems(Update, update.run_if(in_state(GameState::Playing)));
|
app.add_systems(
|
||||||
|
Update,
|
||||||
|
(
|
||||||
|
engage_and_throw,
|
||||||
|
wait_for_player,
|
||||||
|
out_of_range,
|
||||||
|
detect_reload,
|
||||||
|
detect_reload_done,
|
||||||
|
rotate,
|
||||||
|
)
|
||||||
|
.run_if(in_state(GameState::Playing)),
|
||||||
|
);
|
||||||
|
app.add_systems(Update, on_ai_added);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(
|
fn on_ai_added(mut commands: Commands, query: Query<Entity, Added<Ai>>) {
|
||||||
|
for entity in query.iter() {
|
||||||
|
commands.entity(entity).insert(WaitForAnyPlayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait_for_player(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut query: Query<(&mut ActiveHeads, &AimTarget, &Transform), With<Ai>>,
|
agents: Query<Entity, With<WaitForAnyPlayer>>,
|
||||||
|
transform: Query<&Transform>,
|
||||||
|
players: Query<Entity, With<Player>>,
|
||||||
|
) {
|
||||||
|
for agent in agents.iter() {
|
||||||
|
if let Some(player) = in_range(50., agent, &players, &transform) {
|
||||||
|
info!("[{agent}] Engage: {player}");
|
||||||
|
commands
|
||||||
|
.entity(agent)
|
||||||
|
.remove::<WaitForAnyPlayer>()
|
||||||
|
.insert(Engage(player));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn out_of_range(
|
||||||
|
mut commands: Commands,
|
||||||
|
agents: Query<Entity, With<Engage>>,
|
||||||
|
transform: Query<&Transform>,
|
||||||
|
players: Query<Entity, With<Player>>,
|
||||||
|
) {
|
||||||
|
for agent in agents.iter() {
|
||||||
|
if in_range(100., agent, &players, &transform).is_none() {
|
||||||
|
info!("[{agent}] Player out of range");
|
||||||
|
commands
|
||||||
|
.entity(agent)
|
||||||
|
.remove::<Engage>()
|
||||||
|
.insert(WaitForAnyPlayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detect_reload(mut commands: Commands, agents: Query<(Entity, &ActiveHeads), With<Engage>>) {
|
||||||
|
for (e, head) in agents.iter() {
|
||||||
|
if head.reloading() {
|
||||||
|
info!("[{e}] Reload started");
|
||||||
|
commands.entity(e).remove::<Engage>().insert(Reload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detect_reload_done(mut commands: Commands, agents: Query<(Entity, &ActiveHeads), With<Reload>>) {
|
||||||
|
for (e, head) in agents.iter() {
|
||||||
|
if !head.reloading() {
|
||||||
|
info!("[{e}] Reload done");
|
||||||
|
commands
|
||||||
|
.entity(e)
|
||||||
|
.remove::<Reload>()
|
||||||
|
.insert(WaitForAnyPlayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn in_range(
|
||||||
|
range: f32,
|
||||||
|
entity: Entity,
|
||||||
|
players: &Query<'_, '_, Entity, With<Player>>,
|
||||||
|
transform: &Query<'_, '_, &Transform>,
|
||||||
|
) -> Option<Entity> {
|
||||||
|
let Ok(pos) = transform.get(entity).map(|t| t.translation) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
players
|
||||||
|
.iter()
|
||||||
|
.filter_map(|p| transform.get(p).ok().map(|t| (p, *t)))
|
||||||
|
.find(|(_, t)| t.translation.distance(pos) < range)
|
||||||
|
.map(|(e, _)| e)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rotate(agent: Query<(Entity, &Engage)>, mut transform: Query<&mut Transform>) {
|
||||||
|
for (agent, Engage(target)) in agent.iter() {
|
||||||
|
let Ok(target_pos) = transform.get(*target).map(|t| t.translation) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Ok(mut agent_transform) = transform.get_mut(agent) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the direction vector from the current position to the target
|
||||||
|
let direction = (target_pos - agent_transform.translation).normalize();
|
||||||
|
|
||||||
|
// Project the direction onto the XZ plane by zeroing out the Y component
|
||||||
|
let xz_direction = Vec3::new(direction.x, 0.0, direction.z).normalize();
|
||||||
|
|
||||||
|
agent_transform.rotation = Quat::from_rotation_arc(Vec3::Z, xz_direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn engage_and_throw(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut query: Query<(&mut ActiveHeads, &AimTarget, &Transform), With<Engage>>,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
heads_db: Res<HeadsDatabase>,
|
heads_db: Res<HeadsDatabase>,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ fn init(mut commands: Commands, query: Query<(Entity, &EnemySpawn)>, heads_db: R
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
]),
|
]),
|
||||||
Ai,
|
|
||||||
))
|
))
|
||||||
|
.insert_if(Ai, || !spawn.disable_ai)
|
||||||
.with_child((Name::from("body-rig"), AnimatedCharacter(id)))
|
.with_child((Name::from("body-rig"), AnimatedCharacter(id)))
|
||||||
.observe(on_kill);
|
.observe(on_kill);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ pub struct CutsceneCameraMovementEnd;
|
|||||||
pub struct EnemySpawn {
|
pub struct EnemySpawn {
|
||||||
pub head: String,
|
pub head: String,
|
||||||
pub key: String,
|
pub key: String,
|
||||||
|
pub disable_ai: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnemySpawn {
|
impl EnemySpawn {
|
||||||
|
|||||||
@@ -83,6 +83,11 @@
|
|||||||
[
|
[
|
||||||
head(string) : "head" : "" : ""
|
head(string) : "head" : "" : ""
|
||||||
key(string) : "key" : "" : ""
|
key(string) : "key" : "" : ""
|
||||||
|
disable_ai(choices) : "disable_ai" : "false" : "" =
|
||||||
|
[
|
||||||
|
"true" : "true"
|
||||||
|
"false" : "false"
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
@SolidClass base(transform, target) = movable
|
@SolidClass base(transform, target) = movable
|
||||||
|
|||||||
Reference in New Issue
Block a user