add dynamically-colored buff effect (seen on healing potions)

This commit is contained in:
Lephenixnoir 2023-01-15 17:17:45 +01:00
parent 80fda55e98
commit 861457fdaf
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
12 changed files with 148 additions and 9 deletions

View File

@ -90,6 +90,7 @@ set(ASSETS
assets-cg/items/sword1.aseprite
assets-cg/items/sword2.aseprite
assets-cg/items/armor1.aseprite
assets-cg/items/buff.aseprite
# Player animations
assets-cg/player/player_up.aseprite
assets-cg/player/player_right.aseprite

Binary file not shown.

View File

@ -3,3 +3,6 @@
name_regex: (.*)\.aseprite frames_item_\1
next: Item=Item
profile: p4
buff.aseprite:
section: .data

View File

@ -82,6 +82,7 @@ ANIM1(item_stick2);
ANIM1(item_sword1);
ANIM1(item_sword2);
ANIM1(item_armor1);
ANIM1(item_buff);
/* Animation functions. */

View File

@ -126,6 +126,7 @@ extern anim_t anims_item_stick2;
extern anim_t anims_item_sword1;
extern anim_t anims_item_sword2;
extern anim_t anims_item_armor1;
extern anim_t anims_item_buff;
/* Expand definitions for enemies */

View File

@ -1,6 +1,7 @@
#include "comp/entity.h"
#include "comp/physical.h"
#include "comp/particle.h"
#include "anim.h"
#include "geometry.h"
#include "util.h"
@ -17,9 +18,10 @@ entity_t *particle_make_damage(entity_t *target, int damage, int color)
p->type = PARTICLE_DAMAGE;
p->plane = CEILING;
p->bound_to_entity = false;
p->x = target_p->x;
p->y = target_p->y - fix(0.5);
p->z = 0;
p->y = target_p->y;
p->z = fix(0.5);
p->age = 0;
p->DAMAGE.damage = damage;
@ -37,6 +39,7 @@ entity_t *particle_make_dash(entity_t *target)
p->type = PARTICLE_DASH;
p->plane = HORIZONTAL;
p->bound_to_entity = false;
p->x = target_p->x;
p->y = target_p->y;
p->z = 0;
@ -45,6 +48,23 @@ entity_t *particle_make_dash(entity_t *target)
return e;
}
entity_t *particle_make_buff(entity_t *target, int color)
{
entity_t *e = entity_make(particle);
particle_t *p = getcomp(e, particle);
p->type = PARTICLE_BUFF;
p->plane = CEILING;
p->bound_to_entity = true;
p->bound_entity = target;
p->z = fix(0.5);
p->age = 0;
p->BUFF.color = color;
return e;
}
static bool damage_update(particle_t *p, GUNUSED fixed_t dt)
{
return p->age >= 300;
@ -75,6 +95,50 @@ static void dash_render(int x, int y, particle_t const *p)
}
}
static bool buff_update(particle_t *p, GUNUSED fixed_t dt)
{
int total_ms = 0;
anim_frame_t const *frame = anims_item_buff.start[0];
while(frame) {
total_ms += frame->duration;
frame = frame->next;
}
return p->age >= total_ms;
}
static void buff_render(int x, int y, particle_t const *p)
{
int ms = p->age;
anim_frame_t const *frame = anims_item_buff.start[0];
while(frame && ms > frame->duration) {
ms -= frame->duration;
frame = frame->next;
}
if(!frame)
return;
/* Find black and white in the palette */
int black_index = -1;
int white_index = -1;
for(int i = 0; i < frame->sheet->color_count; i++) {
if(black_index == -1 && frame->sheet->palette[i] == 0x0000)
black_index = i;
if(white_index == -1 && frame->sheet->palette[i] == 0xffff)
white_index = i;
}
/* Render while swapping black for p->BUFF.color */
frame->sheet->palette[black_index] = p->BUFF.color;
frame->sheet->palette[white_index] = ~((~p->BUFF.color & 0xf7de) >> 1);
dsubimage_p4(x - frame->cx, y - frame->cy, frame->sheet,
frame->x, frame->y, frame->w, frame->h, DIMAGE_NONE);
frame->sheet->palette[black_index] = 0x0000;
frame->sheet->palette[white_index] = 0xffff;
}
//---
// Generic functions
//---
@ -87,6 +151,8 @@ bool particle_update(particle_t *p, fixed_t dt)
return damage_update(p, dt);
if(p->type == PARTICLE_DASH)
return dash_update(p, dt);
if(p->type == PARTICLE_BUFF)
return buff_update(p, dt);
return true;
}
@ -97,4 +163,6 @@ void particle_render(int x, int y, particle_t const *p)
return damage_render(x, y, p);
if(p->type == PARTICLE_DASH)
return dash_render(x, y, p);
if(p->type == PARTICLE_BUFF)
return buff_render(x, y, p);
}

View File

@ -17,6 +17,7 @@ enum {
PARTICLE_DAMAGE,
PARTICLE_DASH,
PARTICLE_HPBAR,
PARTICLE_BUFF,
};
typedef struct
@ -25,10 +26,18 @@ typedef struct
uint8_t type;
/* Plane of rendering */
uint8_t plane;
/* Whether the particle's position is bound to an entity, or absolute */
bool bound_to_entity;
/* Location on screen and in the air */
fixed_t x, y, z;
/* Age and lifetime */
fixed_t age;
union {
struct { fixed_t x, y; };
entity_t *bound_entity;
};
fixed_t z;
/* Age (milliseconds) */
int age;
/* 8 bytes of data */
union {
/* Generic particles or with a lot of extra data */
@ -39,6 +48,8 @@ typedef struct
/* PARTICLE_HPBAR: Pointer to entity */
struct { entity_t *entity; } HPBAR;
/* PARTICLE_BUFF: Buff animation */
struct { uint16_t color; } BUFF;
};
} particle_t;
@ -49,6 +60,9 @@ entity_t *particle_make_damage(entity_t *target, int damage, int color);
/* Make a dashing particle at the specified target's position */
entity_t *particle_make_dash(entity_t *target);
/* Make a buff particle of the specified color bound to the specified target */
entity_t *particle_make_buff(entity_t *target, int color);
/* Update time and layout for a particle (type-generic). */
bool particle_update(particle_t *p, fixed_t dt);

View File

@ -414,6 +414,14 @@ void game_remove_dead_entities(game_t *g)
}
}
/* Remove particles bound to dead fighters */
for(int i = 0; i < g->entity_count; i++) {
entity_t *e = g->entities[i];
particle_t *p = getcomp(e, particle);
if(p && p->bound_to_entity && p->bound_entity->deleted)
entity_mark_to_delete(e);
}
int i = 0;
while(i < g->entity_count) {
if(g->entities[i]->deleted)

View File

@ -65,6 +65,25 @@ char const *item_name(int item)
return "???";
}
int item_buff_color(int item)
{
if(item == ITEM_LIFE)
return C_RGB(31, 0, 31);
else if(item == ITEM_POTION_ATK)
return C_RGB(31, 16, 16);
else if(item == ITEM_POTION_COOLDOWN)
return C_BLUE;
else if(item == ITEM_POTION_DEF)
return C_RGB(0, 15, 31);
else if(item == ITEM_POTION_FRZ)
return 0x5555;
else if(item == ITEM_POTION_HP)
return C_RGB(0, 31, 0);
else if(item == ITEM_POTION_SPD)
return C_RGB(31, 31, 0);
return -1;
}
char const *item_description(int item)
{
if(item == ITEM_LIFE)

View File

@ -33,6 +33,9 @@ anim_t const *item_anim(int item);
/* Item name (fancy huh?). */
char const *item_name(int item);
/* Buff effect color (-1 for no buff). */
int item_buff_color(int item);
/* Item description, can be multi-line. NULL if none specified. */
char const *item_description(int item);

View File

@ -494,6 +494,12 @@ static int menu_select_play_repeat(void)
}
else if(item >= 0 && item_use(item, player)) {
player_data.inventory[game.menu_cursor] = -1;
int color = item_buff_color(item);
if(color > 0) {
entity_t *part = particle_make_buff(player, color);
game_add_entity(&game, part);
}
}
}
}

View File

@ -182,6 +182,15 @@ void render_map_layer(map_t const *map, camera_t const *c, int ss_x, int ss_y,
static int depth_measure(entity_t const *e, int direction)
{
particle_t *p = getcomp(e, particle);
if(p) {
if(p->plane != direction)
return -1;
if(!p->bound_to_entity)
return p->y;
physical_t *bound_p = getcomp(p->bound_entity, physical);
return bound_p ? bound_p->y : -1;
}
if(p) {
return (p->plane != direction) ? -1 : p->y;
}
@ -253,9 +262,15 @@ static void render_entities(game_t const *g, camera_t const *camera,
visible_t *v = getcomp(e, visible);
particle_t *pt = getcomp(e, particle);
vec2 xy;
fixed_t z;
if(pt) {
vec2 xy = { 0, 0 };
fixed_t z = 0;
if(pt && pt->bound_to_entity) {
physical_t *bound_p = getcomp(pt->bound_entity, physical);
if(bound_p)
xy = (vec2) { bound_p->x, bound_p->y };
z = pt->z;
}
else if(pt) {
xy = (vec2) { pt->x, pt->y };
z = pt->z;
}
@ -292,7 +307,7 @@ static void render_entities(game_t const *g, camera_t const *camera,
/* Show particle */
if(pt) {
particle_render(scr.x, scr.y, pt);
particle_render(scr.x, elevated_y, pt);
}
}