diff --git a/CMakeLists.txt b/CMakeLists.txt index 5670453..c762bf2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/assets-cg/items/buff.aseprite b/assets-cg/items/buff.aseprite new file mode 100644 index 0000000..d7ecfc9 Binary files /dev/null and b/assets-cg/items/buff.aseprite differ diff --git a/assets-cg/items/fxconv-metadata.txt b/assets-cg/items/fxconv-metadata.txt index 0c8bce0..20781f8 100644 --- a/assets-cg/items/fxconv-metadata.txt +++ b/assets-cg/items/fxconv-metadata.txt @@ -3,3 +3,6 @@ name_regex: (.*)\.aseprite frames_item_\1 next: Item=Item profile: p4 + +buff.aseprite: + section: .data diff --git a/src/anim.c b/src/anim.c index 0ccede4..d35bda9 100644 --- a/src/anim.c +++ b/src/anim.c @@ -82,6 +82,7 @@ ANIM1(item_stick2); ANIM1(item_sword1); ANIM1(item_sword2); ANIM1(item_armor1); +ANIM1(item_buff); /* Animation functions. */ diff --git a/src/anim.h b/src/anim.h index 0884048..58b4357 100644 --- a/src/anim.h +++ b/src/anim.h @@ -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 */ diff --git a/src/comp/particle.c b/src/comp/particle.c index 3a25462..988471f 100644 --- a/src/comp/particle.c +++ b/src/comp/particle.c @@ -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); } diff --git a/src/comp/particle.h b/src/comp/particle.h index aae210a..dd44455 100644 --- a/src/comp/particle.h +++ b/src/comp/particle.h @@ -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); diff --git a/src/game.c b/src/game.c index 59d4f38..de620e2 100644 --- a/src/game.c +++ b/src/game.c @@ -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) diff --git a/src/item.c b/src/item.c index 48f92fb..5e9c960 100644 --- a/src/item.c +++ b/src/item.c @@ -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) diff --git a/src/item.h b/src/item.h index 3f7de81..a1c0e8c 100644 --- a/src/item.h +++ b/src/item.h @@ -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); diff --git a/src/main.c b/src/main.c index 0178904..039c3e3 100644 --- a/src/main.c +++ b/src/main.c @@ -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); + } } } } diff --git a/src/render.c b/src/render.c index f559402..5f20222 100644 --- a/src/render.c +++ b/src/render.c @@ -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); } }