src/ems.cc \
src/walker.cc \
src/surface_triangle.cc \
- src/screen_walker_hack.cc
+ src/screen_walker_hack.cc \
+ src/electricity_effect.cc
HEADERS = \
src/game.h \
src/3d_helpers.h \
src/ems.h \
src/walker.h \
- src/surface_triangle.h\
- src/screen_walker_hack.h
+ src/surface_triangle.h \
+ src/screen_walker_hack.h \
+ src/electricity_effect.h
OBJECTS = $(addprefix ${OBJDIR}/,$(subst .cc,.cc.o,${SOURCES}))
--- /dev/null
+#include "electricity_effect.h"
+
+// third party includes
+#include <raylib.h>
+#include <raymath.h>
+
+// local includes
+#include "3d_helpers.h"
+#include "ems.h"
+
+ElectricityEffect::ElectricityEffect(Vector3 center, float radius,
+ int line_count, float lifetime)
+ : cylinders(),
+ center(center),
+ cylinder_radius(CYLINDER_MAX_RADIUS),
+ lifetime(lifetime),
+ timer(0.0F) {
+ cylinders.reserve(line_count);
+
+ // Generate cylinders.
+ int sub_count = 0;
+ Vector3 dir, pos;
+ for (; line_count > 0; --line_count) {
+ if (sub_count == 0) {
+ dir.x = call_js_get_random() * 2.0F - 1.0F;
+ dir.y = call_js_get_random() * 2.0F - 1.0F;
+ dir.z = call_js_get_random() * 2.0F - 1.0F;
+
+ dir = Vector3Normalize(dir);
+
+ pos = center + dir * (radius * 0.7F);
+
+ dir.x = call_js_get_random() * 2.0F - 1.0F;
+ dir.y = call_js_get_random() * 2.0F - 1.0F;
+ dir.z = call_js_get_random() * 2.0F - 1.0F;
+
+ dir = Vector3Normalize(dir);
+
+ auto coll = GetRayCollisionSphere(
+ Ray{.position = center, .direction = dir}, center, radius);
+
+ cylinders.push_back(Cylinder{.start = pos, .end = coll.point});
+
+ pos = coll.point;
+ } else {
+ dir = Vector3Normalize(center - pos);
+
+ pos = pos + dir * (radius * CYLINDER_EDGE_OFFSET);
+
+ for (unsigned int idx = 0; idx < CYLINDER_SPLIT_COUNT; ++idx) {
+ dir.x = call_js_get_random() * 2.0F - 1.0F;
+ dir.y = call_js_get_random() * 2.0F - 1.0F;
+ dir.z = call_js_get_random() * 2.0F - 1.0F;
+
+ dir = Vector3Normalize(dir);
+
+ auto coll = GetRayCollisionSphere(
+ Ray{.position = pos, .direction = dir}, center, radius);
+
+ cylinders.push_back(Cylinder{.start = pos, .end = coll.point});
+
+ if (idx == CYLINDER_SPLIT_COUNT - 1) {
+ pos = coll.point;
+ }
+ }
+ }
+ if (++sub_count >= CYLINDER_SUB_COUNT_MAX) {
+ sub_count = 0;
+ }
+ }
+}
+
+bool ElectricityEffect::update(float dt) {
+ timer += dt;
+
+ return timer >= lifetime;
+}
+
+void ElectricityEffect::draw(Color color) {
+ 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, 8, color);
+ }
+}
--- /dev/null
+#ifndef JUMPARTIFACT_DOT_COM_DEMO_0_ELECTRICITY_EFFECT_H_
+#define JUMPARTIFACT_DOT_COM_DEMO_0_ELECTRICITY_EFFECT_H_
+
+// standard library includes
+#include <vector>
+
+// third party includes
+#include <raylib.h>
+
+constexpr int CYLINDER_SUB_COUNT_MAX = 3;
+constexpr int CYLINDER_SPLIT_COUNT = 3;
+constexpr int CYLINDER_SIDES = 8;
+constexpr float CYLINDER_MAX_RADIUS = 0.03F;
+constexpr float CYLINDER_EDGE_OFFSET = 0.01F;
+
+class ElectricityEffect {
+ public:
+ ElectricityEffect(Vector3 center, float radius, int line_count,
+ float lifetime);
+
+ /// Returns true if lifetime ended.
+ bool update(float dt);
+ /// Assumes draw mode is active.
+ void draw(Color color);
+
+ private:
+ struct Cylinder {
+ Vector3 start, end;
+ };
+
+ std::vector<Cylinder> cylinders;
+ Vector3 center;
+ float cylinder_radius;
+ float lifetime;
+ float timer;
+};
+
+#endif
camera_target{0.0F, 0.0F, 0.0F},
mouse_hit{0.0F, 0.0F, 0.0F},
surface_triangles(),
+ electricityEffects(),
idx_hit(SURFACE_UNIT_WIDTH / 2 +
(SURFACE_UNIT_HEIGHT / 2) * SURFACE_UNIT_WIDTH),
controlled_walker_idx(std::nullopt),
if (flags.test(1)) {
if (walker_hack_success && controlled_walker_idx.has_value()) {
walkers[controlled_walker_idx.value()].set_player_controlled(true);
+ electricityEffects.push_back(ElectricityEffect(
+ walkers[controlled_walker_idx.value()].get_body_pos(), 1.7F, 15,
+ 1.0F));
+
} else {
controlled_walker_idx.reset();
}
SURFACE_UNIT_HEIGHT);
}
+ std::vector<decltype(electricityEffects.size())> to_remove;
+ for (decltype(electricityEffects.size()) idx = 0;
+ idx < electricityEffects.size(); ++idx) {
+ if (electricityEffects[idx].update(dt)) {
+ to_remove.push_back(idx);
+ }
+ }
+
+ for (auto iter = to_remove.rbegin(); iter != to_remove.rend(); ++iter) {
+ if (*iter != electricityEffects.size() - 1) {
+ electricityEffects[*iter] = *electricityEffects.rbegin();
+ }
+ electricityEffects.pop_back();
+ }
+
return false;
}
walker.draw(TEMP_cube_model);
}
+ for (auto &ee : electricityEffects) {
+ ee.draw(GREEN);
+ }
+
// TODO DEBUG
if (!controlled_walker_idx.has_value() && !flags.test(0)) {
DrawLine3D(Vector3{0.0F, 3.0F, 0.0F}, mouse_hit, BLACK);
// local includes
#include "common_constants.h"
+#include "electricity_effect.h"
#include "surface_triangle.h"
#include "walker.h"
std::unique_ptr<std::array<SurfaceTriangle,
SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT * 2> >
surface_triangles;
+ std::vector<ElectricityEffect> electricityEffects;
unsigned int idx_hit;
std::optional<unsigned int> controlled_walker_idx;
const int left_text_width;
../src/raymath.cc \
../src/walker.cc \
../src/surface_triangle.cc \
- ../src/screen_walker_hack.cc
+ ../src/screen_walker_hack.cc \
+ ../src/electricity_effect.cc
HEADERS = \
../src/ems.h \
../src/3d_helpers.h \
../src/walker.h \
../src/surface_triangle.h \
- ../src/screen_walker_hack.h
+ ../src/screen_walker_hack.h \
+ ../src/electricity_effect.h
OBJECTS = $(addprefix ${OBJDIR}/,$(subst ..,PREVDIR,$(subst .cc,.cc.o,${SOURCES})))