aboutsummaryrefslogtreecommitdiffstats
path: root/src/shared
diff options
context:
space:
mode:
authorLibravatar Alexander Foremny <aforemny@posteo.de>2024-03-21 15:36:05 +0100
committerLibravatar Alexander Foremny <aforemny@posteo.de>2024-03-22 03:53:08 +0100
commit91419fb01ef5dcdc06d9f6774d16d3ccca1e4b57 (patch)
treea302f2ca0d110579f468b2144b81e24f3aa113a3 /src/shared
parent445a51c344ecea346051cf59d03b95c98bb28e75 (diff)
feat: towers
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/ability.rs301
-rw-r--r--src/shared/champion.rs21
-rw-r--r--src/shared/immovable.rs4
-rw-r--r--src/shared/player.rs3
-rw-r--r--src/shared/shape.rs16
-rw-r--r--src/shared/tower.rs65
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>,
+}