1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
use std::ops::Sub;

use macroquad::{
    color,
    experimental::scene::{Handle, Node, RefMut},
    prelude::*,
};

use crate::ability::Effect;
use crate::physics::get_beam_end;
use crate::{nodes::Actor, physics::beam_collision_check};

pub struct ContinuousBeam {
    pub actor_id: String,
    pub actor: Handle<Actor>,
    pub factions: Vec<String>,
    pub effects: Vec<Effect>,
    pub color: Color,
    pub width: f32,
    pub origin: Vec2,
    pub end: Vec2,
}

#[derive(Default)]
pub struct ContinuousBeams {
    active: Vec<ContinuousBeam>,
}

impl ContinuousBeams {
    const DEFAULT_COLOR: Color = color::RED;
    const DEFAULT_WIDTH: f32 = 4.0;

    const WIDTH_TOLERANCE_FACTOR: f32 = 350.0;

    pub fn new() -> Self {
        ContinuousBeams { active: Vec::new() }
    }

    pub fn add_node() -> Handle<Self> {
        scene::add_node(Self::new())
    }

    #[allow(clippy::too_many_arguments)]
    pub fn spawn(
        &mut self,
        actor_id: &str,
        actor: Handle<Actor>,
        factions: &[String],
        effects: &[Effect],
        color_override: Option<Color>,
        width_override: Option<f32>,
        origin: Vec2,
        end: Vec2,
    ) {
        let beam = ContinuousBeam {
            actor_id: actor_id.to_string(),
            actor,
            factions: factions.to_vec(),
            effects: effects.to_vec(),
            color: color_override.unwrap_or(Self::DEFAULT_COLOR),
            width: width_override.unwrap_or(Self::DEFAULT_WIDTH),
            origin,
            end,
        };
        self.active.push(beam);
    }
}

impl Node for ContinuousBeams {
    fn fixed_update(mut node: RefMut<Self>) {
        for mut beam in &mut node.active {
            let mut cutoff = get_beam_end(
                beam.origin,
                beam.end,
                beam.width,
                Self::WIDTH_TOLERANCE_FACTOR,
            );
            'outer: for mut other_actor in scene::find_nodes_by_type::<Actor>() {
                let position = match other_actor.body.get_offset_collider() {
                    Some(collider) => collider.get_position(),
                    None => other_actor.body.position,
                };
                if beam_collision_check(
                    position,
                    beam.origin,
                    beam.end,
                    beam.width,
                    Self::WIDTH_TOLERANCE_FACTOR,
                ) {
                    for effect in beam.effects.clone() {
                        if other_actor.apply_effect(
                            &beam.actor_id,
                            beam.actor,
                            &beam.factions,
                            effect,
                        ) {
                            if beam.origin.distance(position) < beam.origin.distance(cutoff) {
                                cutoff = position;
                            }
                        } else {
                            continue 'outer;
                        }
                    }
                }
            }
            beam.end = beam.origin
                + beam
                    .end
                    .sub(beam.origin)
                    .clamp_length(0.0, beam.origin.distance(cutoff));
        }
    }

    fn draw(mut node: RefMut<Self>) {
        node.active.retain(|beam| {
            let mut highlight = color::WHITE;
            highlight.a = 0.5;
            draw_circle(beam.end.x, beam.end.y, beam.width / 2.0, beam.color);
            draw_line(
                beam.origin.x,
                beam.origin.y,
                beam.end.x,
                beam.end.y,
                beam.width,
                beam.color,
            );
            draw_circle(
                beam.end.x,
                beam.end.y,
                ((beam.width / 2.0) * 0.8) / 2.0,
                highlight,
            );
            draw_line(
                beam.origin.x,
                beam.origin.y,
                beam.end.x,
                beam.end.y,
                (beam.width - 4.0) * 0.8,
                highlight,
            );
            false
        });
    }
}