diff options
author | Alexander Foremny <aforemny@posteo.de> | 2024-03-21 15:36:05 +0100 |
---|---|---|
committer | Alexander Foremny <aforemny@posteo.de> | 2024-03-22 03:53:08 +0100 |
commit | 91419fb01ef5dcdc06d9f6774d16d3ccca1e4b57 (patch) | |
tree | a302f2ca0d110579f468b2144b81e24f3aa113a3 /src/shared | |
parent | 445a51c344ecea346051cf59d03b95c98bb28e75 (diff) |
feat: towers
Diffstat (limited to 'src/shared')
-rw-r--r-- | src/shared/ability.rs | 301 | ||||
-rw-r--r-- | src/shared/champion.rs | 21 | ||||
-rw-r--r-- | src/shared/immovable.rs | 4 | ||||
-rw-r--r-- | src/shared/player.rs | 3 | ||||
-rw-r--r-- | src/shared/shape.rs | 16 | ||||
-rw-r--r-- | src/shared/tower.rs | 65 |
6 files changed, 272 insertions, 138 deletions
diff --git a/src/shared/ability.rs b/src/shared/ability.rs index b7b3664..4723e59 100644 --- a/src/shared/ability.rs +++ b/src/shared/ability.rs @@ -3,6 +3,7 @@ use crate::shared::area_of_effect::*; use crate::shared::buffs::*; use crate::shared::player::*; use crate::shared::projectile::*; +use crate::shared::shape::*; use crate::shared::*; use bevy::ecs::system::*; use bevy::utils::Duration; @@ -248,79 +249,61 @@ fn dash_activation(dash: Dash) -> DirectionalAbilityActivation { move |commands: &mut Commands, source_player: PlayerId, direction: Vec2| { commands.add(move |world: &mut World| { world.run_system_once( - move |players: Query<(Entity, &PlayerId)>, + move |entity_map: Res<EntityMap>, mut imperatives: Query<&mut Imperative>, mut set: ParamSet<( Query<&mut PlayerPosition>, - Query<(&PlayerId, &PlayerPosition)>, + Query<(Entity, &PlayerId, &PlayerPosition, &Shape)>, )>, mut commands: Commands| { - let Some(source_entity) = ({ - let mut source_entity = None; - for (entity, player_id) in players.iter() { - if *player_id != source_player { - continue; - } - source_entity = Some(entity); - break; - } - source_entity - }) else { + let Some(source_entity) = entity_map.0.get(&source_player.0) else { return; }; - let Some(source_position) = ({ - let positions = set.p0(); - if let Ok(position) = positions.get(source_entity) { - Some(*position) - } else { - None - } - }) else { - return; - }; - - let DashCollisionResult { + let Some(DashCollisionResult { dash_end, dash_collision, - } = { + }) = ({ let dash_targets = set.p1(); dash_collision( - source_player, - source_position.0, + *source_entity, direction.normalize_or_zero(), dash.max_distance, &dash_targets, ) + }) + else { + return; }; let mut positions = set.p0(); - if let Ok(mut position) = positions.get_mut(source_entity) { + if let Ok(mut position) = positions.get_mut(*source_entity) { position.0 = dash_end; } if let Some(DashCollision { - collision_id, - collision_position, + collision_player_id, + collision_player_position, + .. }) = dash_collision { commands.spawn(AreaOfEffectBundle::new(AreaOfEffect { - position: collision_position, - radius: 1.5 * PLAYER_RADIUS, + position: collision_player_position.0, + radius: 1.5 * Shape::player().radius, duration: None, source_player, area_of_effect_type: AreaOfEffectType::Slow, })); commands.spawn(ProjectileBundle::new(Projectile { type_: ProjectileType::Instant(InstantProjectile { - target_player: collision_id, + target_player: collision_player_id, }), source_player, damage: dash.damage, })); - if let Ok(mut imperative) = imperatives.get_mut(source_entity) { + if let Ok(mut imperative) = imperatives.get_mut(*source_entity) { *imperative = - Imperative::AttackTarget(AbilitySlot::A, collision_id); + Imperative::AttackTarget(AbilitySlot::A, collision_player_id); } } }, @@ -335,61 +318,52 @@ pub struct DashCollisionResult { pub dash_collision: Option<DashCollision>, } +impl DashCollisionResult { + fn from_player_player_collision_result( + player_player_collision_result: PlayerPlayerCollisionResult, + ) -> Self { + DashCollisionResult { + dash_end: player_player_collision_result.final_position, + dash_collision: player_player_collision_result.collision.map( + |player_player_collision| { + DashCollision::from_player_player_collision(player_player_collision) + }, + ), + } + } +} + pub struct DashCollision { - pub collision_id: PlayerId, - pub collision_position: Vec2, + pub collision_entity: Entity, + pub collision_player_id: PlayerId, + pub collision_player_position: PlayerPosition, + pub collision_shape: Shape, +} + +impl DashCollision { + fn from_player_player_collision(player_player_collision: PlayerPlayerCollision) -> Self { + DashCollision { + collision_entity: player_player_collision.collision_entity, + collision_player_id: player_player_collision.collision_player_id, + collision_player_position: player_player_collision.collision_player_position, + collision_shape: player_player_collision.collision_shape, + } + } } pub fn dash_collision( - source_id: PlayerId, - dash_start: Vec2, + source_entity: Entity, dash_direction: Vec2, dash_max_distance: f32, - player_positions: &Query<(&PlayerId, &PlayerPosition)>, -) -> DashCollisionResult { - let mut dash_collision = dash_max_distance * dash_direction; - let mut collision_id = None; - let mut collision_position = None; - for (player_id, position) in player_positions.iter() { - if *player_id == source_id { - continue; - } - - let player_position = position.0 - dash_start; - let player_projection = player_position.project_onto(dash_collision); - let player_rejection = player_position - player_projection; - let scalar_factor = player_projection.dot(dash_collision).signum() - * player_projection.length() - / dash_collision.length(); - - if scalar_factor < 0. || scalar_factor > 1.0 { - continue; - } - - if player_rejection.length() < 2. * PLAYER_RADIUS { - dash_collision = player_projection; - collision_id = Some(*player_id); - collision_position = Some(position.0); - } - } - - if let (Some(collision_id), Some(collision_position)) = (collision_id, collision_position) { - let dash_end = dash_start - + (dash_collision.length() - 2. * PLAYER_RADIUS) * dash_collision.normalize_or_zero(); - DashCollisionResult { - dash_end, - dash_collision: Some(DashCollision { - collision_id, - collision_position, - }), - } - } else { - let dash_end = dash_start + dash_max_distance * dash_direction; - DashCollisionResult { - dash_end, - dash_collision: None, - } - } + dash_targets: &Query<(Entity, &PlayerId, &PlayerPosition, &Shape)>, +) -> Option<DashCollisionResult> { + player_player_collision( + source_entity, + dash_direction, + dash_max_distance, + dash_targets, + ) + .map(DashCollisionResult::from_player_player_collision_result) } fn pull_activation(pull: Pull) -> DirectionalAbilityActivation { @@ -401,7 +375,7 @@ fn pull_activation(pull: Pull) -> DirectionalAbilityActivation { mut imperatives: Query<&mut Imperative>, mut set: ParamSet<( Query<&mut PlayerPosition>, - Query<(&PlayerId, &PlayerPosition)>, + Query<(Entity, &PlayerId, &PlayerPosition, &Shape)>, )>, mut commands: Commands| { let Some(source_entity) = ({ @@ -418,26 +392,14 @@ fn pull_activation(pull: Pull) -> DirectionalAbilityActivation { return; }; - let Some(source_position) = ({ - let positions = set.p0(); - if let Ok(position) = positions.get(source_entity) { - Some(*position) - } else { - None - } - }) else { - return; - }; - - let Some(PullCollision { + let Some(PullCollisionResult { pull_end, - collision_id, + collision_player_id, .. }) = ({ let pull_targets = set.p1(); pull_collision( - source_player, - source_position.0, + source_entity, direction.normalize_or_zero(), pull.max_distance, &pull_targets, @@ -450,7 +412,7 @@ fn pull_activation(pull: Pull) -> DirectionalAbilityActivation { let Some(target_entity) = ({ let mut target_entity = None; for (entity, player_id) in players.iter() { - if *player_id != collision_id { + if *player_id != collision_player_id { continue; } target_entity = Some(entity); @@ -468,13 +430,14 @@ fn pull_activation(pull: Pull) -> DirectionalAbilityActivation { commands.spawn(ProjectileBundle::new(Projectile { type_: ProjectileType::Instant(InstantProjectile { - target_player: collision_id, + target_player: collision_player_id, }), source_player, damage: pull.damage, })); if let Ok(mut imperative) = imperatives.get_mut(source_entity) { - *imperative = Imperative::AttackTarget(AbilitySlot::A, collision_id); + *imperative = + Imperative::AttackTarget(AbilitySlot::A, collision_player_id); } }, ) @@ -483,56 +446,122 @@ fn pull_activation(pull: Pull) -> DirectionalAbilityActivation { ) } -pub struct PullCollision { +#[derive(Clone, Copy)] +pub struct PullCollisionResult { pub pull_end: Vec2, - pub collision_id: PlayerId, - pub collision_position: Vec2, + pub collision_entity: Entity, + pub collision_player_id: PlayerId, + pub collision_player_position: PlayerPosition, + pub collision_shape: Shape, +} + +impl PullCollisionResult { + fn from_player_player_collision_result( + player_player_collision_result: PlayerPlayerCollisionResult, + ) -> Option<Self> { + player_player_collision_result + .collision + .map(|player_player_collision| PullCollisionResult { + pull_end: player_player_collision.collision_player_position.0 + - (player_player_collision_result.final_position + - player_player_collision_result.source_player_position.0), + collision_entity: player_player_collision.collision_entity, + collision_player_id: player_player_collision.collision_player_id, + collision_player_position: player_player_collision.collision_player_position, + collision_shape: player_player_collision.collision_shape, + }) + } } pub fn pull_collision( - source_id: PlayerId, - pull_start: Vec2, + source_entity: Entity, pull_direction: Vec2, pull_max_distance: f32, - player_positions: &Query<(&PlayerId, &PlayerPosition)>, -) -> Option<PullCollision> { - let mut pull_collision = pull_max_distance * pull_direction; - let mut pull_player_id = None; - let mut pull_player_position = None; - for (player_id, position) in player_positions.iter() { - if *player_id == source_id { + pull_targets: &Query<(Entity, &PlayerId, &PlayerPosition, &Shape)>, +) -> Option<PullCollisionResult> { + player_player_collision( + source_entity, + pull_direction, + pull_max_distance, + pull_targets, + ) + .and_then(PullCollisionResult::from_player_player_collision_result) +} + +pub struct PlayerPlayerCollisionResult { + pub final_position: Vec2, + pub source_entity: Entity, + pub source_player_id: PlayerId, + pub source_player_position: PlayerPosition, + pub source_shape: Shape, + pub collision: Option<PlayerPlayerCollision>, +} + +pub struct PlayerPlayerCollision { + pub collision_entity: Entity, + pub collision_player_id: PlayerId, + pub collision_player_position: PlayerPosition, + pub collision_shape: Shape, +} + +pub fn player_player_collision( + source_entity: Entity, + direction: Vec2, + max_distance: f32, + targets: &Query<(Entity, &PlayerId, &PlayerPosition, &Shape)>, +) -> Option<PlayerPlayerCollisionResult> { + let Ok((_, source_player_id, source_player_position, source_shape)) = + targets.get(source_entity) + else { + return None; + }; + let start = source_player_position.0; + + let mut result = PlayerPlayerCollisionResult { + final_position: max_distance * direction, + source_entity, + source_player_id: *source_player_id, + source_player_position: *source_player_position, + source_shape: *source_shape, + collision: None, + }; + for (entity, player_id, position, shape) in targets.iter() { + if entity == source_entity { continue; } - let player_position = position.0 - pull_start; - let player_projection = player_position.project_onto(pull_collision); - let player_rejection = player_position - player_projection; - let scalar_factor = player_projection.dot(pull_collision).signum() - * player_projection.length() - / pull_collision.length(); + let target_position = position.0 - start; + let target_projection = target_position.project_onto(result.final_position); + let target_rejection = target_position - target_projection; + let scalar_factor = target_projection.dot(result.final_position).signum() + * target_projection.length() + / result.final_position.length(); if scalar_factor < 0. || scalar_factor > 1.0 { continue; } - if player_rejection.length() < 2. * PLAYER_RADIUS { - pull_player_id = Some(player_id); - pull_player_position = Some(position.0); - pull_collision = player_projection; + if target_rejection.length() < source_shape.radius + shape.radius { + result.final_position = target_projection; + result.collision = Some(PlayerPlayerCollision { + collision_entity: entity, + collision_player_id: *player_id, + collision_player_position: *position, + collision_shape: *shape, + }); } } - if let (Some(target_id), Some(target_position)) = (pull_player_id, pull_player_position) { - let pull_direction = pull_start - target_position; - let pull_end = target_position - + (pull_direction.length() - 2. * PLAYER_RADIUS) * pull_direction.normalize_or_zero(); - Some(PullCollision { - pull_end, - collision_id: *target_id, - collision_position: target_position, - }) + if let Some(ref mut collision) = result.collision { + result.final_position = start + + (result.final_position.length() + - source_shape.radius + - collision.collision_shape.radius) + * result.final_position.normalize_or_zero(); + Some(result) } else { - None + result.final_position += start; + Some(result) } } diff --git a/src/shared/champion.rs b/src/shared/champion.rs index f48faa1..1667672 100644 --- a/src/shared/champion.rs +++ b/src/shared/champion.rs @@ -1,4 +1,5 @@ use crate::shared::ability::*; +use crate::shared::shape::*; use crate::shared::stats::*; use crate::shared::*; use bevy::utils::*; @@ -8,6 +9,7 @@ use std::str::FromStr; pub enum Champion { Meele, Ranged, + Tower, } impl Default for Champion { @@ -23,6 +25,7 @@ impl FromStr for Champion { match s { "ranged" => Ok(Champion::Ranged), "meele" => Ok(Champion::Meele), + "tower" => Ok(Champion::Tower), _ => Err(format!("unknown champion: {}", s)), } } @@ -32,17 +35,23 @@ impl Champion { pub fn base_stats(self) -> BaseStats { match self { Champion::Meele => BaseStats(Stats { - attack_range: 25., + attack_range: Shape::player().radius, attack_speed: 0.75, max_health: 150., movement_speed: 75., }), Champion::Ranged => BaseStats(Stats { - attack_range: 60., + attack_range: 50., attack_speed: 1.5, max_health: 100., movement_speed: 85., }), + Champion::Tower => BaseStats(Stats { + attack_range: 100., + attack_speed: 4.5, + max_health: 500., + movement_speed: 0., + }), } } @@ -88,6 +97,11 @@ impl Champion { } _ => Ability::Targeted(TargetedAbility::RangedAttack(RangedAttack { damage: 8. })), }, + Champion::Tower => match ability_slot { + _ => { + Ability::Targeted(TargetedAbility::RangedAttack(RangedAttack { damage: 100. })) + } + }, } } @@ -99,6 +113,9 @@ impl Champion { Champion::Ranged => { BaseCooldown([0., 10., 10., 15., 35., 50., 50.].map(Duration::from_secs_f32)) } + Champion::Tower => { + BaseCooldown([10., 10., 10., 10., 10., 10., 10.].map(Duration::from_secs_f32)) + } } } } diff --git a/src/shared/immovable.rs b/src/shared/immovable.rs new file mode 100644 index 0000000..ef4b3f0 --- /dev/null +++ b/src/shared/immovable.rs @@ -0,0 +1,4 @@ +use crate::shared::*; + +#[derive(Component)] +pub struct Immovable; diff --git a/src/shared/player.rs b/src/shared/player.rs index 3805969..59e8853 100644 --- a/src/shared/player.rs +++ b/src/shared/player.rs @@ -1,5 +1,6 @@ use crate::shared::activation::*; use crate::shared::buffs::*; +use crate::shared::shape::*; use crate::shared::stats::*; use crate::shared::*; @@ -15,6 +16,7 @@ pub struct PlayerBundle { effective_stats: EffectiveStats, buffs: Buffs, activation: Activation, + shape: Shape, replicate: Replicate, } @@ -45,6 +47,7 @@ impl PlayerBundle { effective_stats, buffs: Buffs::default(), activation: Activation::default(), + shape: Shape::player(), replicate, } } diff --git a/src/shared/shape.rs b/src/shared/shape.rs new file mode 100644 index 0000000..7423375 --- /dev/null +++ b/src/shared/shape.rs @@ -0,0 +1,16 @@ +use crate::shared::*; + +#[derive(Component, Message, Clone, Copy, Serialize, Deserialize, Debug, PartialEq)] +pub struct Shape { + pub radius: f32, +} + +impl Shape { + pub fn player() -> Self { + Shape { radius: 10. } + } + + pub fn tower() -> Self { + Shape { radius: 25. } + } +} diff --git a/src/shared/tower.rs b/src/shared/tower.rs new file mode 100644 index 0000000..5b62b7a --- /dev/null +++ b/src/shared/tower.rs @@ -0,0 +1,65 @@ +use crate::shared::activation::*; +use crate::shared::buffs::*; +use crate::shared::immovable::*; +use crate::shared::player::*; +use crate::shared::shape::*; +use crate::shared::stats::*; +use crate::shared::*; + +#[derive(Bundle)] +pub struct TowerBundle { + id: PlayerId, + position: PlayerPosition, + color: PlayerColor, + imperative: Imperative, + cooldown: Cooldown, + health: Health, + champion: Champion, + effective_stats: EffectiveStats, + buffs: Buffs, + activation: Activation, + shape: Shape, + tower: Tower, + immovable: Immovable, + replicate: Replicate, +} + +impl TowerBundle { + pub fn new(id: ClientId, position: Vec2, color: Color) -> Self { + let mut replicate = Replicate { + replication_group: ReplicationGroup::default().set_priority(10.), + ..Default::default() + }; + replicate.enable_replicate_once::<PlayerId>(); + replicate.enable_replicate_once::<PlayerColor>(); + replicate.target::<Champion>(NetworkTarget::Single(id)); + replicate.target::<Cooldown>(NetworkTarget::Single(id)); + replicate.target::<EffectiveStats>(NetworkTarget::Single(id)); + let champion = Champion::Tower; + let effective_stats = EffectiveStats(champion.base_stats().0); + TowerBundle { + id: PlayerId(id), + position: PlayerPosition(position), + color: PlayerColor(color), + imperative: Imperative::Idle, + cooldown: Cooldown::default(), + health: Health { + health: effective_stats.0.max_health, + shield: 0., + }, + champion, + effective_stats, + buffs: Buffs::default(), + activation: Activation::default(), + shape: Shape::tower(), + tower: Tower::default(), + immovable: Immovable, + replicate, + } + } +} + +#[derive(Component, Default)] +pub struct Tower { + pub last_target_player_id: Option<PlayerId>, +} |