From e613fe3bbdde50121ea4a73cbe9960c845e6e7f9 Mon Sep 17 00:00:00 2001 From: Edgaru089 Date: Tue, 16 Apr 2024 21:08:21 +0800 Subject: [PATCH] Prettify particles & put them on different layers Also hazards now emit black particles --- app_debug.c | 2 +- mapper_misc.c | 35 +++++++++++++++++++++ particle.c | 71 +++++++++++++++++++++++++----------------- particle.h | 8 +++-- particle_render.cpp | 18 +++++------ player.c | 4 +++ render_bundle.h | 3 +- render_bundle_draw.cpp | 3 -- render_bundle_file.c | 18 +++++------ render_util.cpp | 17 ++++++++-- render_util.h | 1 + 11 files changed, 119 insertions(+), 61 deletions(-) diff --git a/app_debug.c b/app_debug.c index d8683ae..0605011 100644 --- a/app_debug.c +++ b/app_debug.c @@ -46,7 +46,7 @@ void app_DebugText(App *app, vector_Vector *vec_string) { PUSH_STRING(buf); snprintf( buf, sizeof(buf) - 1, - "Particle count: %d\n", tree_Count(app->particle->parts)); + "Particle count[0]: %d\n", tree_Count(app->particle->parts[0])); PUSH_STRING(buf); char zero = '\0'; diff --git a/mapper_misc.c b/mapper_misc.c index 0d11a4c..c9eab97 100644 --- a/mapper_misc.c +++ b/mapper_misc.c @@ -6,6 +6,7 @@ #include "player.h" #include "types.h" #include "util/assert.h" +#include "util/rand.h" #include @@ -52,6 +53,40 @@ void misc_thinker_Hazard(App *app, Entity *e, Duration deltaTime) { e->thinker = NULL; return; } + + // Emit some black particles + // BTW, very bad (but reliable) way to store a number here! + static const FillMode mode = { + .rop2 = 13, // R2_COPYPEN + .style = 0, // BS_SOLID + .hatch = 0, + .rotate = {.microseconds = 0}, + .dissolve = {.microseconds = 0}, + .fadein = false, + .bg = 0xffffff, + .fg = 0}; + uint64_t emitCooldown = 400.0 * 40000.0 / e->misc->trigger.size.x; // 40 msec across 400px + TimePoint lastEmit = {.microseconds = (uint64_t)e->thinkerData}; + if (time_Since(lastEmit).microseconds > emitCooldown) { + lastEmit = time_Now(); + lastEmit.microseconds -= 10000 * rand_Double01(); + Box2 worldbox = ABSOLUTE_BOX(e, e->misc->trigger); + particle_Emit( + app->particle, + 0, + vec2_Random( + worldbox.lefttop.x + 20, + worldbox.lefttop.x + worldbox.size.x - 20, + worldbox.lefttop.y + 20.0, + worldbox.lefttop.y + 40.0), + vec2(0.0, rand_DoubleRange(-200.0, -280.0)), + rand_DoubleRange(1, 1.5), + rand_DoubleRange(18, 30), + 20, duration_FromSeconds(0), &mode); + e->thinkerData = (void *)lastEmit.microseconds; + } + + if (app->player->player == NULL) // No player return; Component_Player *p = app->player->player; diff --git a/particle.c b/particle.c index 14b8415..acbecc1 100644 --- a/particle.c +++ b/particle.c @@ -7,28 +7,40 @@ #include +typedef struct { + uint64_t layer; + uint64_t id; +} _particle_DeleteRecord; + +static inline void particle_Delete(System_Particle *sys, int layer, uint64_t id) { + uint64_t a[2] = {layer, id}; + vector_Push(sys->deleted_ids, a); +} + System_Particle *particle_NewSystem(App *super) { System_Particle *sys = zero_malloc(sizeof(System_Particle)); sys->super = super; - sys->parts = tree_Create(sizeof(Particle)); - sys->deleted_ids = vector_Create(sizeof(uintptr_t)); + sys->deleted_ids = vector_Create(sizeof(_particle_DeleteRecord)); sys->maxID = 0; + for (int i = 0; i < PARTICLE_LAYER_COUNT; i++) + sys->parts[i] = tree_Create(sizeof(Particle)); return sys; } void particle_DeleteSystem(System_Particle *sys) { - tree_Destroy(sys->parts); + for (int i = 0; i < PARTICLE_LAYER_COUNT; i++) + tree_Destroy(sys->parts[i]); vector_Destroy(sys->deleted_ids); free(sys); } -static void _particle_AdvancePart(System_Particle *sys, Particle *p, Duration deltaTime) { +static void _particle_AdvancePart(System_Particle *sys, int layer, Particle *p, Duration deltaTime) { p->size -= p->sizedec * duration_Seconds(deltaTime); if ((p->tolive.microseconds != 0 && time_Since(p->addtime).microseconds > p->tolive.microseconds) || p->size < EPS) { // vanished - particle_Delete(sys, p->id); + particle_Delete(sys, layer, p->id); return; } @@ -41,33 +53,33 @@ static void _particle_AdvancePart(System_Particle *sys, Particle *p, Duration de } void particle_Advance(System_Particle *sys, Duration deltaTime) { - // Clear particles marked as deleted - for (int i = 0; i < vector_Size(sys->deleted_ids); i++) { - uintptr_t id = *(uintptr_t *)vector_At(sys->deleted_ids, i); - tree_Node *n = tree_FindNode(sys->parts, id); - if (n != NULL) - tree_Delete(sys->parts, n); + // Delete particles + int len = vector_Size(sys->deleted_ids); + for (int i = 0; i < len; i++) { + _particle_DeleteRecord rec = *(_particle_DeleteRecord *)vector_At(sys->deleted_ids, i); + tree_Node *node = tree_FindNode(sys->parts[rec.layer], rec.id); + if (!node) + WARN("particle id %llu in layer %llu not found", rec.id, rec.layer); else - fprintf(stderr, "[particle_Delete][WARN] Missing particle ID [%d]\n", id); + tree_Delete(sys->parts[rec.layer], node); } vector_Clear(sys->deleted_ids); - for (tree_Node *i = tree_FirstNode(sys->parts); - i != NULL; - i = tree_Node_Next(i)) { - Particle *p = (Particle *)i->data; - _particle_AdvancePart(sys, p, deltaTime); + for (int layer = 0; layer < PARTICLE_LAYER_COUNT; layer++) { + for (tree_Node *i = tree_FirstNode(sys->parts[layer]); + i != NULL; + i = tree_Node_Next(i)) { + Particle *p = (Particle *)i->data; + _particle_AdvancePart(sys, layer, p, deltaTime); + } } } -void particle_Delete(System_Particle *sys, uintptr_t id) { - vector_Push(sys->deleted_ids, &id); -} - -void particle_Emit(System_Particle *sys, Vec2 pos, Vec2 vec, double vec_friction, double size, double sizedec, Duration tolive, const FillMode *fill) { +void particle_Emit(System_Particle *sys, int layer, Vec2 pos, Vec2 vec, double vec_friction, double size, double sizedec, Duration tolive, const FillMode *fill) { + ASSERT(layer >= 0 && layer <= (PARTICLE_LAYER_COUNT - 1)); uintptr_t id = ++sys->maxID; - Particle *p = tree_Insert(sys->parts, id, NULL); + Particle *p = tree_Insert(sys->parts[layer], id, NULL); memset(p, 0, sizeof(Particle)); p->id = id; p->addtime = time_Now(); @@ -83,10 +95,11 @@ void particle_Emit(System_Particle *sys, Vec2 pos, Vec2 vec, double vec_friction void particle_Clear(System_Particle *sys) { vector_Clear(sys->deleted_ids); - // Delete every first node - int count = tree_Count(sys->parts); - while (count--) - tree_Delete(sys->parts, tree_FirstNode(sys->parts)); - - ASSERT(tree_Count(sys->parts) == 0); + for (int i = 0; i < PARTICLE_LAYER_COUNT; i++) { + // Delete every first node + int count = tree_Count(sys->parts[i]); + while (count--) + tree_Delete(sys->parts[i], tree_FirstNode(sys->parts[i])); + ASSERT(tree_Count(sys->parts[i]) == 0); + } } diff --git a/particle.h b/particle.h index 9ad2234..917a047 100644 --- a/particle.h +++ b/particle.h @@ -11,6 +11,9 @@ extern "C" { #endif +#define PARTICLE_LAYER_COUNT 4 + + // A particle instance. typedef struct { uintptr_t id; // ID. @@ -30,7 +33,7 @@ typedef struct _App App; // Particle system. typedef struct { App *super; - tree_Tree *parts; // uintptr_t -> struct Particle + tree_Tree *parts[PARTICLE_LAYER_COUNT]; // uintptr_t -> struct Particle vector_Vector *deleted_ids; uintptr_t maxID; } System_Particle; @@ -43,8 +46,7 @@ void particle_Render(System_Particle *sys); // Emit a particle here. -void particle_Emit(System_Particle *sys, Vec2 pos, Vec2 vec, double vec_friction, double size, double sizedec, Duration tolive, const FillMode *fill); -void particle_Delete(System_Particle *sys, uintptr_t id); +void particle_Emit(System_Particle *sys, int layer, Vec2 pos, Vec2 vec, double vec_friction, double size, double sizedec, Duration tolive, const FillMode *fill); // Clear all particles. void particle_Clear(System_Particle *sys); diff --git a/particle_render.cpp b/particle_render.cpp index 9787e4d..fc0c3da 100644 --- a/particle_render.cpp +++ b/particle_render.cpp @@ -10,16 +10,14 @@ extern "C" { void particle_Render(System_Particle *sys) { - ASSERT(sys->super->camera && "particle_Render called without a Camera system"); - System_Camera *cam = sys->super->camera; + for (int l = 0; l < PARTICLE_LAYER_COUNT; l++) + for (tree_Node *i = tree_FirstNode(sys->parts[l]); + i != NULL; + i = tree_Node_Next(i)) { + Particle *p = (Particle *)i->data; - for (tree_Node *i = tree_FirstNode(sys->parts); - i != NULL; - i = tree_Node_Next(i)) { - Particle *p = (Particle *)i->data; - - render_SetModes(*p->mode, p->addtime); - render_FillCircleW(sys->super, p->pos, p->size); - } + render_SetModes(*p->mode, p->addtime); + render_FillCircleW(sys->super, p->pos, p->size); + } } } diff --git a/player.c b/player.c index 9ce2257..f48efd7 100644 --- a/player.c +++ b/player.c @@ -83,6 +83,7 @@ void player_Advance(System_Player *sys, Duration deltaTime) { Vec2 to_pos = vec2_Add(p->super->position->position, vec2(0, -p->super->hitbox->box.size.y / 2.0)); particle_Emit( sys->super->particle, + 3, vec2_Add(vec2_Random(-5, 5, -30, 30), to_pos), vec2(rand_DoubleRange(650, 700) * -p->faceDirection, rand_DoubleRange(-100, 100)), 7, rand_DoubleRange(14, 20), rand_DoubleRange(32, 40), @@ -98,6 +99,7 @@ void player_Advance(System_Player *sys, Duration deltaTime) { Vec2 to_pos = vec2_Add(p->super->position->position, vec2(0, -p->super->hitbox->box.size.y)); particle_Emit( sys->super->particle, + 3, vec2_Add(vec2_Random(-10, 10, -10, 10), to_pos), vec2_Add( vec2(0, rand_DoubleRange(-200, -240)), @@ -134,6 +136,7 @@ void player_Advance(System_Player *sys, Duration deltaTime) { for (int i = 0; i < airjumpParticleCount; i++) particle_Emit( sys->super->particle, + 3, vec2_Add(vec2_Random(-20, 20, -5, 5), p->super->position->position), (p->onGround) ? vec2(rand_DoubleRange(-50, 50), -100) : vec2(0, 200), 2, rand_DoubleRange(8, 12), 20, duration_FromSeconds(0), &render_ModeRotate); @@ -188,6 +191,7 @@ void player_HazardHarm(System_Player *sys) { double angle = rand_Double01() * 2.0 * M_PI; particle_Emit( sys->super->particle, + 3, sys->player->super->position->position, vec2(speed_linear * cos(angle), speed_linear * sin(angle)), rand_DoubleRange(2, 3), diff --git a/render_bundle.h b/render_bundle.h index a65cbd6..d8f27be 100644 --- a/render_bundle.h +++ b/render_bundle.h @@ -18,8 +18,7 @@ typedef enum { // render_Primitive describes one render operation. typedef struct { - FillMode mode; // fill mode - uint32_t fg, bg; // foreground & background colors + FillMode mode; // fill mode render_PrimitiveType type; vector_Vector *points; // Vector of Vec2 diff --git a/render_bundle_draw.cpp b/render_bundle_draw.cpp index 88f21ad..afc3ec4 100644 --- a/render_bundle_draw.cpp +++ b/render_bundle_draw.cpp @@ -47,9 +47,6 @@ extern "C" void render_DrawPrimitiveW(App *app, render_Primitive *p, Vec2 offset // See if any of the points are in the camera box if (needDraw) { // Set the colors - setlinecolor(p->fg); - setfillcolor(p->fg); - setbkcolor(p->bg); render_SetModes(p->mode, time_Now()); // Draw the converted primitive diff --git a/render_bundle_file.c b/render_bundle_file.c index 3ef075e..a26c255 100644 --- a/render_bundle_file.c +++ b/render_bundle_file.c @@ -51,8 +51,6 @@ static void _render_BundleCommand(char *cmd) { } _tmpp = malloc(sizeof(render_Primitive)); _tmpp->points = vector_Create(sizeof(Vec2)); - _tmpp->fg = 0xffffff; - _tmpp->bg = 0; _tmpp->mode = render_ModeDefault; // parse the type @@ -88,19 +86,19 @@ static void _render_BundleCommand(char *cmd) { } else if (CMD("FG")) { // Set Foreground color if (_tmpp) { - int r = atoi(strtok(NULL, " ")); - int g = atoi(strtok(NULL, " ")); - int b = atoi(strtok(NULL, " ")); - _tmpp->fg = RGB(r, g, b); + int r = atoi(strtok(NULL, " ")); + int g = atoi(strtok(NULL, " ")); + int b = atoi(strtok(NULL, " ")); + _tmpp->mode.fg = RGB(r, g, b); } else WARN("FG without PRIM first", 0); } else if (CMD("BG")) { // Set Background color if (_tmpp) { - int r = atoi(strtok(NULL, " ")); - int g = atoi(strtok(NULL, " ")); - int b = atoi(strtok(NULL, " ")); - _tmpp->bg = RGB(r, g, b); + int r = atoi(strtok(NULL, " ")); + int g = atoi(strtok(NULL, " ")); + int b = atoi(strtok(NULL, " ")); + _tmpp->mode.bg = RGB(r, g, b); } else WARN("BG without PRIM first", 0); } else { diff --git a/render_util.cpp b/render_util.cpp index 08878d9..d7abddc 100644 --- a/render_util.cpp +++ b/render_util.cpp @@ -61,21 +61,27 @@ const FillMode render_ModeDefault = { .hatch = 0, .rotate = {.microseconds = 0}, .dissolve = {.microseconds = 0}, - .fadein = false}; + .fadein = false, + .bg = 0, + .fg = 0xffffff}; const FillMode render_ModeInverse = { .rop2 = R2_NOT, .style = BS_SOLID, .hatch = 0, .rotate = {.microseconds = 0}, .dissolve = {.microseconds = 0}, - .fadein = false}; + .fadein = false, + .bg = 0, + .fg = 0xffffff}; extern const FillMode render_ModeRotate = { .rop2 = R2_COPYPEN, .style = BS_SOLID, .hatch = 0, .rotate = {.microseconds = 100000}, .dissolve = {.microseconds = 0}, - .fadein = false}; + .fadein = false, + .bg = 0, + .fg = 0xffffff}; void render_SetModes(FillMode mode, TimePoint since) { @@ -97,12 +103,17 @@ void render_SetModes(FillMode mode, TimePoint since) { static const long hatches[] = {HS_HORIZONTAL, HS_FDIAGONAL, HS_VERTICAL, HS_BDIAGONAL}; setfillstyle(BS_HATCHED, hatches[steps % 4], NULL); setrop2(R2_COPYPEN); + setfillcolor(0xffffff); + setbkcolor(0); return; } // Normal mode setfillstyle(mode.style, mode.hatch, NULL); setrop2(mode.rop2); + setlinecolor(mode.fg); + setfillcolor(mode.fg); + setbkcolor(mode.bg); } void render_FillScreen() { diff --git a/render_util.h b/render_util.h index 135ac4a..077cf18 100644 --- a/render_util.h +++ b/render_util.h @@ -43,6 +43,7 @@ typedef struct { // amount of time. Overrides rotate, style & hatch Duration dissolve; bool fadein; // Fade Into being full for true, fade out of otherwise + uint32_t bg, fg; // Background & foreground colors } FillMode; // Default fill mode.