initial commit
This commit is contained in:
commit
86f9333aec
21 changed files with 6449 additions and 0 deletions
284
src/armour/mod.rs
Normal file
284
src/armour/mod.rs
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
use bevy_ecs::prelude::*;
|
||||
use rand::distributions::Distribution;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::{
|
||||
hierarchy::HierarchyBuilder,
|
||||
player::{
|
||||
status_effect::{
|
||||
ConcussionGrenade, FlashGrenade, PepperSpray, Sand, TearGas, TempDebuffImmunity,
|
||||
},
|
||||
BodyPart,
|
||||
},
|
||||
Name, Stages,
|
||||
};
|
||||
|
||||
#[derive(Component, Default)]
|
||||
pub struct Armour;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BodyPartCoverage {
|
||||
pub armour: Entity,
|
||||
pub coverage: f32,
|
||||
pub armour_value: f32,
|
||||
}
|
||||
|
||||
#[derive(Component, Default)]
|
||||
pub struct ArmourCoverage(ArmourVec<f32>);
|
||||
|
||||
#[derive(Component, Default)]
|
||||
pub struct ArmourValue(pub f32);
|
||||
|
||||
#[derive(Component)]
|
||||
#[cfg_attr(feature = "json", derive(serde::Deserialize))]
|
||||
#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))]
|
||||
pub enum Set {
|
||||
Riot,
|
||||
Assault,
|
||||
Dune,
|
||||
Delta,
|
||||
Vanguard,
|
||||
Sentinel,
|
||||
Marauder,
|
||||
Eod,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "json", derive(serde::Deserialize))]
|
||||
#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))]
|
||||
pub enum Immunity {
|
||||
Radiation,
|
||||
TearGas,
|
||||
PepperSpray,
|
||||
NerveGas,
|
||||
Sand,
|
||||
ConcussionGrenades,
|
||||
FlashGrenades,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Immunities(pub Vec<Immunity>);
|
||||
|
||||
#[derive(Bundle, Default)]
|
||||
pub struct ArmourBundle {
|
||||
pub name: Name,
|
||||
pub armour: Armour,
|
||||
pub coverage: ArmourCoverage,
|
||||
pub armour_value: ArmourValue,
|
||||
}
|
||||
|
||||
impl ArmourBundle {
|
||||
pub fn new(name: String, coverage: [f32; 10], armour_value: f32) -> Self {
|
||||
Self {
|
||||
name: Name(name),
|
||||
armour: Armour,
|
||||
coverage: ArmourCoverage(ArmourVec(coverage)),
|
||||
armour_value: ArmourValue(armour_value / 100.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Default, Debug)]
|
||||
pub struct ArmourBodyPart {
|
||||
pub armour_pieces: Vec<BodyPartCoverage>,
|
||||
}
|
||||
|
||||
#[derive(Component, Default)]
|
||||
pub struct EquippedArmour {
|
||||
pub head: Option<Entity>,
|
||||
pub body: Option<Entity>,
|
||||
pub legs: Option<Entity>,
|
||||
pub feet: Option<Entity>,
|
||||
pub hands: Option<Entity>,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
pub struct ArmourBodyParts(pub ArmourVec<Entity>);
|
||||
|
||||
enum ArmourIterState {
|
||||
Head,
|
||||
Body,
|
||||
Legs,
|
||||
Feet,
|
||||
Hands,
|
||||
}
|
||||
|
||||
pub struct ArmourIter<'a> {
|
||||
state: ArmourIterState,
|
||||
equipped_armour: &'a EquippedArmour,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ArmourIter<'a> {
|
||||
type Item = Entity;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
let (next, piece) = match self.state {
|
||||
ArmourIterState::Head => (ArmourIterState::Body, self.equipped_armour.head),
|
||||
ArmourIterState::Body => (ArmourIterState::Legs, self.equipped_armour.body),
|
||||
ArmourIterState::Legs => (ArmourIterState::Feet, self.equipped_armour.legs),
|
||||
ArmourIterState::Feet => (ArmourIterState::Hands, self.equipped_armour.feet),
|
||||
ArmourIterState::Hands => return self.equipped_armour.hands,
|
||||
};
|
||||
|
||||
self.state = next;
|
||||
if piece.is_some() {
|
||||
return piece;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a EquippedArmour {
|
||||
type Item = Entity;
|
||||
|
||||
type IntoIter = ArmourIter<'a>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
ArmourIter {
|
||||
state: ArmourIterState::Head,
|
||||
equipped_armour: self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(usize)]
|
||||
#[derive(Clone, Copy, strum::EnumIter)]
|
||||
pub enum ArmourBodyPartSlot {
|
||||
Arms,
|
||||
Stomach,
|
||||
Heart,
|
||||
Chest,
|
||||
Throat,
|
||||
Hands,
|
||||
Groin,
|
||||
Legs,
|
||||
Head,
|
||||
Feet,
|
||||
}
|
||||
|
||||
impl From<BodyPart> for ArmourBodyPartSlot {
|
||||
fn from(value: BodyPart) -> Self {
|
||||
match value {
|
||||
BodyPart::LeftArm | BodyPart::RightArm => Self::Arms,
|
||||
BodyPart::Stomach => Self::Stomach,
|
||||
BodyPart::Heart => Self::Heart,
|
||||
BodyPart::Chest => Self::Chest,
|
||||
BodyPart::Throat => Self::Throat,
|
||||
BodyPart::LeftHand | BodyPart::RightHand => Self::Hands,
|
||||
BodyPart::Groin => Self::Groin,
|
||||
BodyPart::LeftLeg | BodyPart::RightLeg => Self::Legs,
|
||||
BodyPart::Head => Self::Head,
|
||||
BodyPart::LeftFoot | BodyPart::RightFoot => Self::Feet,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct ArmourVec<T>([T; 10]);
|
||||
|
||||
impl<T> std::ops::Index<ArmourBodyPartSlot> for ArmourVec<T> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, index: ArmourBodyPartSlot) -> &Self::Output {
|
||||
&self.0[index as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::IndexMut<ArmourBodyPartSlot> for ArmourVec<T> {
|
||||
fn index_mut(&mut self, index: ArmourBodyPartSlot) -> &mut Self::Output {
|
||||
&mut self.0[index as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoIterator for ArmourVec<T> {
|
||||
type Item = T;
|
||||
|
||||
type IntoIter = std::array::IntoIter<T, 10>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Distribution<Option<&'a BodyPartCoverage>> for &'a ArmourBodyPart {
|
||||
fn sample<R: rand::prelude::Rng + ?Sized>(&self, rng: &mut R) -> Option<&'a BodyPartCoverage> {
|
||||
let mut current = None;
|
||||
for piece in &self.armour_pieces {
|
||||
// NOTE: This is not strictly speaking correct, but the edge cases where this applies
|
||||
// should be very rare, and it should be a decent enough heuristic to barely make a
|
||||
// difference from the actual pixel comparisons that torn seems to be using for this.
|
||||
if current
|
||||
.map(|c: &BodyPartCoverage| c.armour_value)
|
||||
.unwrap_or_default()
|
||||
< piece.armour_value
|
||||
&& rng.gen_bool(piece.coverage as f64)
|
||||
{
|
||||
current = Some(piece);
|
||||
}
|
||||
}
|
||||
current
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_body_parts(
|
||||
equip_q: Query<(Entity, &EquippedArmour)>,
|
||||
armour_q: Query<(Entity, &ArmourCoverage, &ArmourValue, Option<&Immunities>)>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
for (player, equipped_armour) in equip_q.iter() {
|
||||
let mut parts = ArmourVec::<ArmourBodyPart>::default();
|
||||
|
||||
for (armour, coverage, armour_value, immunities) in armour_q.iter_many(equipped_armour) {
|
||||
commands.entity(armour).set_parent(player);
|
||||
|
||||
if let Some(immunities) = immunities {
|
||||
let mut player = commands.entity(player);
|
||||
for immunity in &immunities.0 {
|
||||
match immunity {
|
||||
// TODO: Going to need this if irradiate is ever added
|
||||
Immunity::Radiation => (),
|
||||
// NOTE: It's an unreleased DOT temp, so the exact effect is currently
|
||||
// unknwown
|
||||
Immunity::NerveGas => (),
|
||||
Immunity::TearGas => {
|
||||
player.insert(TempDebuffImmunity::<TearGas>::default());
|
||||
}
|
||||
Immunity::PepperSpray => {
|
||||
player.insert(TempDebuffImmunity::<PepperSpray>::default());
|
||||
}
|
||||
Immunity::FlashGrenades => {
|
||||
player.insert(TempDebuffImmunity::<FlashGrenade>::default());
|
||||
}
|
||||
Immunity::Sand => {
|
||||
player.insert(TempDebuffImmunity::<Sand>::default());
|
||||
}
|
||||
Immunity::ConcussionGrenades => {
|
||||
player.insert(TempDebuffImmunity::<ConcussionGrenade>::default());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for slot in ArmourBodyPartSlot::iter() {
|
||||
if coverage.0[slot] > 0.0 {
|
||||
parts[slot].armour_pieces.push(BodyPartCoverage {
|
||||
armour,
|
||||
coverage: coverage.0[slot] / 100.0,
|
||||
armour_value: armour_value.0,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let parts = parts.0.map(|p| commands.spawn(p).id());
|
||||
|
||||
commands
|
||||
.entity(player)
|
||||
.add_children(parts)
|
||||
.insert(ArmourBodyParts(ArmourVec(parts)));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn configure(stages: &mut Stages) {
|
||||
stages.equip.add_systems(generate_body_parts);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue