// standard library includes
#include <queue>
+#include <tuple>
// third party includes
#include <raylib.h>
ElectricityEffect::ElectricityEffect(Vector3 center, float radius,
int line_count, float lifetime)
- : cylinders(), lifetime(lifetime), timer(0.0F) {
+ : cylinders(),
+ center(center),
+ radius(radius),
+ lifetime(lifetime),
+ timer(0.0F) {
cylinders.reserve(line_count);
+ const float line_max_length = radius * CYLINDER_LINE_MAX_LENGTH_RATIO;
+
// Generate cylinders.
- std::queue<Vector3> positions;
- Vector3 next, dir;
- for (unsigned int idx = 0; idx < CYLINDER_SPLIT_COUNT; ++idx) {
- positions.push(center);
+ std::queue<std::tuple<Vector3, int>> positions;
+ std::tuple<Vector3, int> next;
+ Vector3 next_pos, dir;
+ for (unsigned int idx = 0; idx < CYLINDER_SPLIT_COUNT && line_count > 0;
+ ++idx, --line_count) {
+ cylinders.push_back(Cylinder{.next_idx = -1,
+ .point = center,
+ .mdir = Vector3Normalize(Vector3{
+ call_js_get_random() * 2.0F - 1.0F,
+ call_js_get_random() * 2.0F - 1.0F,
+ call_js_get_random() * 2.0F - 1.0F,
+ })});
+ positions.push({center, cylinders.size() - 1});
}
while (line_count-- > 0 && !positions.empty()) {
next = positions.front();
+ next_pos = std::get<Vector3>(next);
positions.pop();
- dir = Vector3Normalize(center - next);
+ dir = Vector3Normalize(center - next_pos);
dir = Vector3Normalize(Vector3{call_js_get_random() * 2.0F - 1.0F,
call_js_get_random() * 2.0F - 1.0F,
call_js_get_random() * 2.0F - 1.0F} +
dir);
- auto coll = GetRayCollisionSphere(Ray{.position = next, .direction = dir},
- center, radius);
+ auto coll = GetRayCollisionSphere(
+ Ray{.position = next_pos, .direction = dir}, center, radius);
- if (coll.distance > CYLINDER_LINE_MAX_LENGTH) {
+ if (coll.distance > line_max_length) {
coll.point =
- next + Vector3Normalize(coll.point - next) * CYLINDER_LINE_MAX_LENGTH;
+ next_pos + Vector3Normalize(coll.point - next_pos) * line_max_length;
}
- cylinders.push_back(Cylinder{.start = next, .end = coll.point});
+ cylinders.push_back(Cylinder{
+ .next_idx = std::get<int>(next),
+ .point = coll.point,
+ .mdir = Vector3Normalize(Vector3{call_js_get_random() * 2.0F - 1.0F,
+ call_js_get_random() * 2.0F - 1.0F,
+ call_js_get_random() * 2.0F - 1.0F})});
dir = Vector3Normalize(center - coll.point);
coll.point = coll.point + dir * (radius * CYLINDER_EDGE_OFFSET);
for (unsigned int idx = 0; idx < CYLINDER_SPLIT_COUNT; ++idx) {
- positions.push(coll.point);
+ positions.push({coll.point, cylinders.size() - 1});
}
}
}
bool ElectricityEffect::update(float dt) {
timer += dt;
+ for (auto &cylinder : cylinders) {
+ cylinder.point = cylinder.point + cylinder.mdir * (dt * CYLINDER_MOVE_RATE);
+ if (Vector3Distance(cylinder.point, center) > radius) {
+ cylinder.point =
+ cylinder.point - cylinder.mdir * (dt * CYLINDER_MOVE_RATE);
+ Vector3 to_center = center - cylinder.point;
+ Vector3 perpendicular = Vector3Normalize(Vector3Perpendicular(to_center));
+ cylinder.mdir = Vector3Normalize(
+ to_center + Vector3RotateByAxisAngle(perpendicular,
+ Vector3Normalize(to_center),
+ call_js_get_random() * PI * 2) *
+ (call_js_get_random() * radius));
+ }
+ }
+
return timer >= lifetime;
}
float ratio = timer < lifetime ? (1.0F - timer / lifetime) : 0.0F;
for (const auto &cylinder : cylinders) {
- DrawCylinderEx(cylinder.start, cylinder.end, CYLINDER_MAX_RADIUS * ratio,
- CYLINDER_MAX_RADIUS * ratio, CYLINDER_SIDES, color);
+ if (cylinder.next_idx >= 0) {
+ DrawCylinderEx(cylinder.point, cylinders.at(cylinder.next_idx).point,
+ CYLINDER_MAX_RADIUS * ratio, CYLINDER_MAX_RADIUS * ratio,
+ CYLINDER_SIDES, color);
+ }
}
}