Skip to content

Commit

Permalink
Merge pull request #152 from cdsupina/abilities
Browse files Browse the repository at this point in the history
Add Spread Pattern Attribute to WeaponProjectileData and add Random Spread Pattern
  • Loading branch information
varoonp123 authored Jan 15, 2024
2 parents 74a33f2 + 78abd30 commit dccfca1
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 59 deletions.
21 changes: 15 additions & 6 deletions assets/data/characters.ron
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@
direction: 1.57080,
despawn_time: 1.0,
count: 1,
spread_weights: (0.5, 1.0),
max_spread_arc: 1.57080,
projectile_gap: 3.14159,
spread_pattern: Arc((
spread_weights: (0.5, 1.0),
max_spread: 1.57080,
projectile_gap: 3.14159,
)),
size: 1.0,
sound: PlayerFireBlast,
),
Expand Down Expand Up @@ -66,9 +68,16 @@
direction: 1.57080,
despawn_time: 0.4,
count: 2,
spread_weights: (0.8, 1.0),
max_spread_arc: 1.57080,
projectile_gap: 3.14159,
spread_pattern: Random((
speed_range: (
start: 0.75,
end: 1.25,
),
angle_range: (
start: 1.37445,
end: 1.76715,
),
)),
size: 1.0,
sound: PlayerFireBlast,
)
Expand Down
32 changes: 20 additions & 12 deletions assets/data/mobs.ron
Original file line number Diff line number Diff line change
Expand Up @@ -407,9 +407,11 @@
direction: 4.71239,
despawn_time: 2.5,
count: 2,
spread_weights: (0.5, 1.0),
max_spread_arc: 1.57080,
projectile_gap: 3.14159,
spread_pattern: Arc((
spread_weights: (0.5, 1.0),
max_spread: 1.57080,
projectile_gap: 3.14159,
)),
size: 1.0,
sound: EnemyFireBlast,
),
Expand Down Expand Up @@ -455,9 +457,11 @@
direction: 4.71239,
despawn_time: 2.0,
count: 1,
spread_weights: (0.5, 1.0),
max_spread_arc: 1.57080,
projectile_gap: 3.14159,
spread_pattern: Arc((
spread_weights: (0.5, 1.0),
max_spread: 1.57080,
projectile_gap: 3.14159,
)),
size: 1.0,
sound: EnemyFireBlast,
),
Expand Down Expand Up @@ -703,9 +707,11 @@
direction: 4.71239,
despawn_time: 1.0,
count: 1,
spread_weights: (0.5, 1.0),
max_spread_arc: 1.57080,
projectile_gap: 3.14159,
spread_pattern: Arc((
spread_weights: (0.5, 1.0),
max_spread: 1.57080,
projectile_gap: 3.14159,
)),
size: 1.0,
sound: EnemyFireBlast,
),
Expand Down Expand Up @@ -758,9 +764,11 @@
direction: 4.71239,
despawn_time: 1.0,
count: 1,
spread_weights: (0.5, 1.0),
max_spread_arc: 1.57080,
projectile_gap: 3.14159,
spread_pattern: Arc((
spread_weights: (0.5, 1.0),
max_spread: 1.57080,
projectile_gap: 3.14159,
)),
size: 1.0,
sound: EnemyFireBlast,
),
Expand Down
32 changes: 25 additions & 7 deletions crates/thetawave_interface/src/weapon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,36 @@ use crate::{
spawnable::{ProjectileType, SpawnPosition},
};

use std::time::Duration;
use std::{ops::Range, time::Duration};

#[derive(Deserialize, Clone)]
pub enum FireMode {
Automatic,
Manual,
}

#[derive(Deserialize, Clone)]
pub enum SpreadPattern {
Arc(ArcPatternData),
Random(RandomPatternData),
}

#[derive(Deserialize, Clone)]
pub struct ArcPatternData {
/// Determines the shape of the arc using (x, y) velocity multipliers
pub spread_weights: Vec2,
/// Maximum spead angle of fired projectiles
pub max_spread: f32,
/// Target gap between fired projectiles
pub projectile_gap: f32,
}

#[derive(Deserialize, Clone)]
pub struct RandomPatternData {
pub speed_range: Range<f32>,
pub angle_range: Range<f32>,
}

/// Stores data about about a Weapon using minimal defining characteristics
#[derive(Deserialize, Clone)]
pub struct WeaponData {
Expand Down Expand Up @@ -47,12 +69,8 @@ pub struct WeaponProjectileData {
pub despawn_time: f32,
/// Number of projectiles spawned at once
pub count: usize,
/// Determines the shape of the arc using (x, y) velocity multipliers
pub spread_weights: Vec2,
/// Maximum spead angle of fired projectiles
pub max_spread_arc: f32,
/// Target gap between fired projectiles
pub projectile_gap: f32,
/// How projectiles are organized when they spawn
pub spread_pattern: SpreadPattern,
/// Size multiplier of the projectile
pub size: f32,
/// Sound that the weapon makes when fired
Expand Down
10 changes: 6 additions & 4 deletions src/game/counters/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ mod test {
EnemyMobType, Faction, MobDestroyedEvent, MobType, ProjectileType, SpawnPosition,
};
use thetawave_interface::states::{AppStates, GameStates};
use thetawave_interface::weapon::WeaponProjectileData;
use thetawave_interface::weapon::{ArcPatternData, SpreadPattern, WeaponProjectileData};

fn base_app_required_for_counting_metrics() -> App {
let mut app = App::new();
Expand Down Expand Up @@ -275,9 +275,11 @@ mod test {
direction: FRAC_PI_2,
despawn_time: 0.0,
count: 1,
spread_weights: Vec2::new(0.5, 1.0),
max_spread_arc: FRAC_PI_2,
projectile_gap: PI,
spread_pattern: SpreadPattern::Arc(ArcPatternData {
spread_weights: Vec2::new(0.5, 1.0),
max_spread: FRAC_PI_2,
projectile_gap: PI,
}),
size: 1.0,
sound: SoundEffectType::PlayerFireBlast,
},
Expand Down
2 changes: 1 addition & 1 deletion src/game/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub struct GameParametersResource {
pub camera_zoom_out_scale: f32,
/// Maximum possible projectiles for 1 of the player/mobs shots. Mainly kept low for perf and as
/// a hard cap (along with fire rate) on how much of a "bullet hell" each mob/player creates.
pub max_player_projectiles: f32,
pub max_player_projectiles: u16,
/// Maximum possible speed of an entity
pub max_speed: f32,
/// Maximum angle between the first and last projectile
Expand Down
38 changes: 12 additions & 26 deletions src/spawnable/projectile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::{
assets::ProjectileAssets,
game::GameParametersResource,
spawnable::{SpawnableBehavior, SpawnableComponent},
weapon::WeaponProjectileInitialVelocitiesExt,
};

mod behavior;
Expand Down Expand Up @@ -98,16 +99,7 @@ pub fn spawn_projectile_system(
);
}
}
/// The angle in between each fired projectile for the shot. Evenly space `weapon.count` projectiles in an arc
fn get_spread_angle_segment(weapon: &WeaponProjectileData, max_projectiles: f32) -> f32 {
let total_projectiles_percent = (weapon.count as f32 - 1.) / (max_projectiles - 1.);
// indicates the angle between the first and last projectile
let spread_arc = weapon
.max_spread_arc
.min(total_projectiles_percent * weapon.projectile_gap);
// indicates the angle between each projectile
spread_arc / (weapon.count as f32 - 1.).max(1.)
}

pub fn spawn_projectile_from_weapon(
commands: &mut Commands,
sound_effect_event_writer: &mut EventWriter<PlaySoundEffectEvent>,
Expand All @@ -119,17 +111,21 @@ pub fn spawn_projectile_from_weapon(
projectile_assets: &ProjectileAssets,
game_parameters: &GameParametersResource,
) {
// Play the sound effect for the projectiles firing
sound_effect_event_writer.send(PlaySoundEffectEvent {
sound_effect_type: weapon_projectile_data.sound.clone(),
});

// Get data for the type of ammunition being spawned
let projectile_data = &projectile_resource.projectiles[&weapon_projectile_data.ammunition];

// Get the behaviors for the type of ammunition being spawned and add a despawn behavior
let mut projectile_behaviors = projectile_data.projectile_behaviors.clone();
projectile_behaviors.push(ProjectileBehavior::TimedDespawn {
despawn_time: weapon_projectile_data.despawn_time,
});

// Create the transform for spawned projectiles
let projectile_transform = Transform {
translation: match weapon_projectile_data.position {
thetawave_interface::spawnable::SpawnPosition::Global(pos) => pos,
Expand All @@ -142,28 +138,18 @@ pub fn spawn_projectile_from_weapon(
rotation: Quat::from_rotation_z(weapon_projectile_data.direction),
};

let spread_angle_segment = get_spread_angle_segment(
&weapon_projectile_data,
game_parameters.max_player_projectiles,
);

// Set the correct collider group for the ammunition based on the faction
let projectile_colider_group =
get_projectile_collider_group(weapon_projectile_data.ammunition.get_faction());

for p in 0..weapon_projectile_data.count {
// Get a vec of linvels to create the spread pattern
let spread_linvels = weapon_projectile_data.get_linvels(game_parameters.max_player_projectiles);

for linvel in spread_linvels {
let new_initial_motion =
if let Some(mut initial_motion_linvel) = initial_motion.clone().linvel {
// Calculate the angle for the current projectile.
// The first projectile is spread_angle_segment/2 radians to the left of the direction,
// and the last projectile is spread_angle_segment/2 radians to the right.
let angle_offset = (p as f32 - (weapon_projectile_data.count as f32 - 1.) / 2.)
* spread_angle_segment;
let projectile_angle = weapon_projectile_data.direction + angle_offset;

// Convert the angle to a velocity vector
initial_motion_linvel += Vec2::from_angle(projectile_angle)
* weapon_projectile_data.speed
* weapon_projectile_data.spread_weights;
initial_motion_linvel += linvel;

InitialMotion {
linvel: Some(initial_motion_linvel),
Expand Down
68 changes: 65 additions & 3 deletions src/weapon/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@ use bevy::{
schedule::{common_conditions::in_state, IntoSystemConfigs},
system::{Query, Res},
},
math::Vec2,
time::Time,
transform::components::Transform,
};
use bevy_rapier2d::dynamics::Velocity;
use std::time::Duration;
use thetawave_interface::{
states::{AppStates, GameStates},
weapon::{FireMode, WeaponComponent, WeaponProjectileData},
weapon::{FireMode, SpreadPattern, WeaponComponent, WeaponProjectileData},
};

use crate::spawnable::{FireWeaponEvent, InitialMotion};
use rand::{thread_rng, Rng};

pub struct WeaponPlugin;

Expand All @@ -31,11 +33,10 @@ impl Plugin for WeaponPlugin {
}
}
trait WeaponExt {
/// Updates the weapon's timers. Returns Some iff the weapon can be fired
fn update(&mut self, delta_time: Duration) -> Option<WeaponProjectileData>;
}
impl WeaponExt for WeaponComponent {
/// Updates the weapon's timers
/// Returns true if the weapon can be fired
fn update(&mut self, delta_time: Duration) -> Option<WeaponProjectileData> {
if self.is_enabled {
// tick the initial timer if there is still time remaining
Expand Down Expand Up @@ -82,3 +83,64 @@ pub fn update_weapon_system(
}
}
}

pub(crate) trait WeaponProjectileInitialVelocitiesExt {
/// The initial velocities of `n` projectiles using existing/'partially evaluated' params.
/// Could be evenly spaced, or something else based on the struct params. max_projectiles
/// should be greater than 0.
fn get_linvels(&self, max_projectiles: u16) -> Vec<Vec2>;
}
impl WeaponProjectileInitialVelocitiesExt for WeaponProjectileData {
fn get_linvels(&self, max_projectiles: u16) -> Vec<Vec2> {
match &self.spread_pattern {
SpreadPattern::Arc(arc_pattern) => {
// Get the segment of a spread angle
let spread_angle_segment = {
// percentage of the game's maximum amount of projectiles being spawned
let total_projectiles_percent =
(self.count as f32 - 1.) / (max_projectiles as f32 - 1.);
// indicates the angle between the first and last projectile
let spread_arc = arc_pattern
.max_spread
.min(total_projectiles_percent * arc_pattern.projectile_gap);
// indicates the angle between each projectile
spread_arc / (self.count as f32 - 1.).max(1.)
};

let mut linvels = vec![];

for p in 0..self.count {
// Calculate the angle for the current projectile.
// The first projectile is spread_angle_segment/2 radians to the left of the direction,
// and the last projectile is spread_angle_segment/2 radians to the right.
let angle_offset =
(p as f32 - (self.count as f32 - 1.) / 2.) * spread_angle_segment;
let projectile_angle = self.direction + angle_offset;

linvels.push(
Vec2::from_angle(projectile_angle)
* self.speed
* arc_pattern.spread_weights,
);
}

linvels
}
SpreadPattern::Random(random_pattern) => {
let mut linvels = vec![];

for _ in 0..self.count {
linvels.push(
// multiply the speed the projectile by a random angle and velocity multiplier
Vec2::from_angle(
thread_rng().gen_range(random_pattern.angle_range.clone()),
) * self.speed
* thread_rng().gen_range(random_pattern.speed_range.clone()),
);
}

linvels
}
}
}
}

0 comments on commit dccfca1

Please sign in to comment.