Skip to content

Commit

Permalink
Added option for child projectiles
Browse files Browse the repository at this point in the history
  • Loading branch information
ramirezmike committed Jul 28, 2024
1 parent 1b90141 commit 8fa3ab1
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 6 deletions.
40 changes: 37 additions & 3 deletions src/game/actor/attack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use serde::Deserialize;
use serde::Serialize;

use crate::core::UpdateSet;
use crate::game::actor::facing::Facing;
use crate::game::actor::faction::Faction;
use crate::game::combat::projectile::projectile;
use crate::util::prelude::*;
Expand All @@ -34,9 +35,11 @@ pub struct Attack {
/// The key of the projectile to attack with.
#[serde(rename = "projectile")]
pub projectile_key: Option<String>,
/// Optional list of facing offsets for multiple shots
/// Optional list of facing offsets for multiple shots.
#[serde(default)]
pub multi_shot: Option<MultiShot>,
#[serde(default)]
pub child_projectile: Option<ChildProjectile>,
}

impl Configure for Attack {
Expand All @@ -55,14 +58,26 @@ impl Default for Attack {
offset: 5.0,
projectile_key: None,
multi_shot: None,
child_projectile: None,
}
}
}

// Way to specify a projectile fires multiple shots, one for each offset
// Way to specify a projectile fires multiple shots, one for each offset.
#[derive(Reflect, Serialize, Deserialize, Clone, Debug)]
pub struct MultiShot(pub Vec<f32>);

#[derive(Reflect, Serialize, Deserialize, Clone, Debug)]
pub struct ChildProjectile {
// The direction child projectiles will go. The first entry is relative to the parent projectile's velocity and any additional
// entries are treated as multishots of the first entry, so their directions are relative to the first projectile.
// i.e., [0.25, 0.5] would fire one shot to the left of the parent and one shot to the right because 0.5 is "behind" relative to the first child.
pub offsets: Vec<f32>,
/// The key of the child projectiles.
#[serde(rename = "projectile")]
pub projectile_key: Option<String>,
}

fn apply_attack(
mut commands: Commands,
attack_query: Query<(
Expand All @@ -85,6 +100,7 @@ fn apply_attack(
// Render projectile above attacker.
let translation = pos.extend(translation.z + 2.0);

// Handle creating multiple shots based off controller's aim
let mut shots = vec![controller.aim];
if let Some(additional_shots) = &attack.multi_shot {
for offset in additional_shots.0.iter() {
Expand All @@ -93,6 +109,23 @@ fn apply_attack(
}
}

// Setup any child projectiles that the current attack's projectile will create
let child_projectiles = if let Some(child) = &attack.child_projectile {
if let Some((primary, multi_shots)) = child.offsets.split_first() {
let facing_direction = controller.aim.rotate(Vec2::from_angle(primary * TAU));
let mut child_attack = attack.clone();
child_attack.child_projectile = None;
child_attack.multi_shot = Some(MultiShot((*multi_shots).into()));
child_attack.projectile_key = child.projectile_key.clone();

Some((child_attack, Facing(c!(Dir2::new(facing_direction)))))
} else {
None
}
} else {
None
};

for shot in shots.iter() {
// Projectiles get a boost if the actor is moving in the same direction.
let aligned_speed = velocity
Expand All @@ -110,13 +143,14 @@ fn apply_attack(
attack.power,
attack.force * *shot * speed_force,
attack.color,
child_projectiles.clone(),
))
.insert(Transform::from_translation(translation));
}
}
}

#[derive(Component, Reflect, Default)]
#[derive(Component, Reflect, Default, Clone)]
#[reflect(Component)]
pub struct AttackController {
pub aim: Vec2,
Expand Down
2 changes: 1 addition & 1 deletion src/game/actor/facing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub(super) fn plugin(app: &mut App) {
app.configure::<(Facing, FacePlayer, FaceCursor, FacingIndicator)>();
}

#[derive(Component, Reflect)]
#[derive(Component, Reflect, Clone)]
#[reflect(Component)]
pub struct Facing(pub Dir2);

Expand Down
1 change: 1 addition & 0 deletions src/game/card/attack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ fn attack_on_beat(mut attack_query: Query<(&mut Attack, &mut AttackController, &
attack.force = attack_on_beat.0.force;
attack.projectile_key = attack_on_beat.0.projectile_key.clone();
attack.multi_shot = attack_on_beat.0.multi_shot.clone();
attack.child_projectile = attack_on_beat.0.child_projectile.clone();

controller.fire = true;
}
Expand Down
21 changes: 19 additions & 2 deletions src/game/combat/projectile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ use bevy_tweening::*;
use serde::Deserialize;
use serde::Serialize;

use crate::game::actor::attack::Attack;
use crate::game::actor::attack::AttackController;
use crate::game::actor::facing::Facing;
use crate::game::actor::faction::Faction;
use crate::game::card::attack::AimTowardsFacing;
use crate::game::card::attack::AttackOnBeat;
use crate::game::cleanup::DespawnOnHit;
use crate::game::cleanup::DespawnOnTimer;
use crate::game::cleanup::DespawnRadiusSq;
Expand Down Expand Up @@ -97,6 +102,7 @@ pub fn projectile(
power: f32,
force: Vec2,
color: impl Into<Color>,
child_projectiles: Option<(Attack, Facing)>,
) -> impl EntityCommand {
let key = key.into();
let color = color.into();
Expand All @@ -123,8 +129,8 @@ pub fn projectile(
.with_volume(projectile.spawn_sfx_volume);
}

world
.entity_mut(entity)
let mut entity = world.entity_mut(entity);
entity
.insert((
Name::new(projectile.name.replace(' ', "")),
// Appearance:
Expand Down Expand Up @@ -171,5 +177,16 @@ pub fn projectile(
),
))
.set_parent(parent);

if let Some((attack, facing)) = child_projectiles {
entity.insert((
AttackOnBeat(attack.clone()),
AimTowardsFacing,
AttackController::default(),
facing,
faction,
attack,
));
}
}
}

0 comments on commit 8fa3ab1

Please sign in to comment.