add judgement skill, improve anim format
|
@ -47,16 +47,17 @@ set(ASSETS
|
|||
assets-cg/player/player_down.aseprite
|
||||
assets-cg/player/player_left.aseprite
|
||||
# Skill animations
|
||||
assets-cg/skills/swing_up.png
|
||||
assets-cg/skills/swing_right.png
|
||||
assets-cg/skills/swing_down.png
|
||||
assets-cg/skills/swing_left.png
|
||||
assets-cg/skills/impale_up.png
|
||||
assets-cg/skills/impale_right.png
|
||||
assets-cg/skills/impale_down.png
|
||||
assets-cg/skills/impale_left.png
|
||||
assets-cg/skills/hit.png
|
||||
assets-cg/skills/teleport.png
|
||||
assets-cg/skills/swing_up.aseprite
|
||||
assets-cg/skills/swing_right.aseprite
|
||||
assets-cg/skills/swing_down.aseprite
|
||||
assets-cg/skills/swing_left.aseprite
|
||||
assets-cg/skills/impale_up.aseprite
|
||||
assets-cg/skills/impale_right.aseprite
|
||||
assets-cg/skills/impale_down.aseprite
|
||||
assets-cg/skills/impale_left.aseprite
|
||||
assets-cg/skills/hit.aseprite
|
||||
assets-cg/skills/judgement.aseprite
|
||||
assets-cg/skills/teleport.aseprite
|
||||
assets-cg/skills/shock.aseprite
|
||||
# Enemies: Slime
|
||||
assets-cg/enemies/slime_idle_left.png
|
||||
|
|
|
@ -215,7 +215,8 @@ def convert_aseprite_anim(input, output, params):
|
|||
print(f"{os.path.basename(input)} ({ase.header.width}x{ase.header.height})",
|
||||
f"has {len(tags)} animations:")
|
||||
for (name, from_, to) in tags:
|
||||
print(f" '{name}': Frames {from_} to {to}", end="")
|
||||
name = f"'{name}'" if name else "(untagged)"
|
||||
print(f" {name}: Frames {from_} to {to}", end="")
|
||||
durations = [ase.frames[i].frame_duration for i in range(from_, to+1)]
|
||||
print(" (" + ", ".join(f"{d} ms" for d in durations) + ")")
|
||||
|
||||
|
@ -232,6 +233,12 @@ def convert_aseprite_anim(input, output, params):
|
|||
if chunk.chunk_type == 0x2005: # Cel
|
||||
# Render only visible layers
|
||||
if ase.layers[chunk.layer_index].flags & 1:
|
||||
# Resolve linked cells
|
||||
if chunk.cel_type == 1: # Linked cel
|
||||
x = ase.frames[chunk.data['link'][0]].chunks
|
||||
x = [c for c in x if c.chunk_type == 0x2005]
|
||||
chunk = x[chunk.layer_index]
|
||||
|
||||
cel = _aseprite_render_cel(chunk)
|
||||
img.paste(cel, (chunk.x_pos, chunk.y_pos))
|
||||
|
||||
|
|
Before Width: | Height: | Size: 707 B After Width: | Height: | Size: 769 B |
|
@ -1,24 +1,34 @@
|
|||
*.png:
|
||||
custom-type: animation
|
||||
name_regex: (.*)\.png anim_\1
|
||||
center: 0, 0
|
||||
profile: p8
|
||||
|
||||
swing_*.png:
|
||||
frame_duration: 30, 60, 30, 30, 30, 60, 30
|
||||
|
||||
impale_*.png:
|
||||
frame_duration: 60, 60, 60, 60, 60
|
||||
|
||||
hit.png:
|
||||
frame_duration: 60, 90, 90, 60, 90
|
||||
|
||||
teleport.png:
|
||||
frame_duration: 90, 90, 90, 60, 60
|
||||
|
||||
*.aseprite:
|
||||
custom-type: aseprite-anim
|
||||
name_regex: (.*)\.aseprite anims_skill_\1
|
||||
center: 0, 0
|
||||
profile: p8
|
||||
profile: p4
|
||||
|
||||
hit.aseprite:
|
||||
center: 3, 3
|
||||
|
||||
impale_up.aseprite:
|
||||
center: 4, 10
|
||||
impale_right.aseprite:
|
||||
center: 0, 4
|
||||
impale_left.aseprite:
|
||||
center: 10, 4
|
||||
impale_down.aseprite:
|
||||
center: 4, 0
|
||||
|
||||
judgement.aseprite:
|
||||
center: 10, 26
|
||||
|
||||
teleport.aseprite:
|
||||
center: 8, 19
|
||||
|
||||
shock.aseprite:
|
||||
center: 18, 18
|
||||
|
||||
swing_up.aseprite:
|
||||
center: 9, 8
|
||||
swing_right.aseprite:
|
||||
center: 0, 9
|
||||
swing_left.aseprite:
|
||||
center: 8, 9
|
||||
swing_down.aseprite:
|
||||
center: 9, 0
|
||||
|
|
Before Width: | Height: | Size: 181 B |
Before Width: | Height: | Size: 213 B |
Before Width: | Height: | Size: 176 B |
Before Width: | Height: | Size: 181 B |
Before Width: | Height: | Size: 217 B |
Before Width: | Height: | Size: 256 B |
Before Width: | Height: | Size: 295 B |
Before Width: | Height: | Size: 274 B |
Before Width: | Height: | Size: 262 B |
Before Width: | Height: | Size: 341 B |
|
@ -24,8 +24,8 @@
|
|||
prefix ## _left_ ## suffix, \
|
||||
};
|
||||
|
||||
DIRECTIONAL_ANIM(anim_swing);
|
||||
DIRECTIONAL_ANIM(anim_impale);
|
||||
DIRECTIONAL_ANIM(anims_skill_swing);
|
||||
DIRECTIONAL_ANIM(anims_skill_impale);
|
||||
|
||||
ANIM_4DIRECTIONAL(anims_player, Idle);
|
||||
ANIM_4DIRECTIONAL(anims_player, Walking);
|
||||
|
|
|
@ -48,9 +48,10 @@ void anim_state_update(anim_state_t *state, fixed_t dt);
|
|||
/* List of animations. */
|
||||
|
||||
/* Basic animations. */
|
||||
extern anim_frame_t anim_hit[];
|
||||
extern anim_frame_t anim_teleport[];
|
||||
extern anim_frame_t anims_skill_hit[];
|
||||
extern anim_frame_t anims_skill_teleport[];
|
||||
extern anim_frame_t anims_skill_shock[];
|
||||
extern anim_frame_t anims_skill_judgement[];
|
||||
|
||||
/* Enemy animations (bidirectional). */
|
||||
|
||||
|
@ -73,5 +74,5 @@ extern anim_frame_t *anims_player_Idle[4];
|
|||
extern anim_frame_t *anims_player_Walking[4];
|
||||
extern anim_frame_t *anims_player_Attack[4];
|
||||
extern anim_frame_t *anims_player_Hit[4];
|
||||
extern anim_frame_t *anim_swing[4];
|
||||
extern anim_frame_t *anim_impale[4];
|
||||
extern anim_frame_t *anims_skill_swing[4];
|
||||
extern anim_frame_t *anims_skill_impale[4];
|
||||
|
|
|
@ -192,30 +192,45 @@ effect_area_t *effect_area_new_attack(uint16_t type, entity_t *e, int facing)
|
|||
if(!area) return NULL;
|
||||
|
||||
frect_t hitbox = { 0 };
|
||||
fixed_t distance = fix(0.75);
|
||||
fixed_t distance = fix(0.625);
|
||||
fpoint_t dir = fdir(facing);
|
||||
fpoint_t anchor = rect_center(entity_sprite(e));
|
||||
anim_frame_t *anim = NULL;
|
||||
bool rotate = true;
|
||||
|
||||
if(type == EFFECT_ATTACK_HIT) {
|
||||
anim = anim_hit;
|
||||
anim = anims_skill_hit;
|
||||
hitbox = (frect_t){ -fix(4)/16, fix(3)/16, -fix(4)/16, fix(3)/16 };
|
||||
}
|
||||
else if(type == EFFECT_ATTACK_SLASH) {
|
||||
anim = anim_swing[facing];
|
||||
hitbox = (frect_t){ -fix(10)/16, fix(10)/16, -fix(4)/16, fix(4)/16 };
|
||||
anim = anims_skill_swing[facing];
|
||||
hitbox = (frect_t){ -fix(10)/16, fix(10)/16, -fix(8)/16, 0 };
|
||||
if(facing == UP || facing == DOWN)
|
||||
anchor = entity_pos(e);
|
||||
}
|
||||
else if(type == EFFECT_ATTACK_IMPALE) {
|
||||
anim = anim_impale[facing];
|
||||
hitbox = (frect_t){ -fix(4)/16, fix(4)/16, -fix(5)/16, fix(5)/16 };
|
||||
anim = anims_skill_impale[facing];
|
||||
hitbox = (frect_t){ -fix(4)/16, fix(4)/16, -fix(10)/16, 0 };
|
||||
if(facing == UP || facing == DOWN)
|
||||
anchor = entity_pos(e);
|
||||
}
|
||||
else if(type == EFFECT_ATTACK_SHOCK) {
|
||||
anim = anims_skill_shock;
|
||||
hitbox = (frect_t){ -fix(18)/16, fix(18)/16, -fix(19)/16, fix(19)/16 };
|
||||
hitbox = (frect_t){ -fix(17)/16, fix(18)/16, -fix(17)/16, fix(18)/16 };
|
||||
anchor = entity_pos(e);
|
||||
distance = fix(0);
|
||||
rotate = false;
|
||||
}
|
||||
else if(type == EFFECT_ATTACK_JUDGEMENT) {
|
||||
anim = anims_skill_judgement;
|
||||
hitbox = (frect_t){ -fix(10)/16, fix(11)/16, -fix(6)/16, fix(6)/16 };
|
||||
anchor = entity_pos(e);
|
||||
distance = fix(1.5);
|
||||
rotate = false;
|
||||
}
|
||||
|
||||
area->sprite = rect_rotate(hitbox, UP, facing);
|
||||
area->anchor = rect_center(entity_sprite(e));
|
||||
area->sprite = rotate ? rect_rotate(hitbox, UP, facing) : hitbox;
|
||||
area->anchor = anchor;
|
||||
area->anchor.x += fmul(distance, dir.x);
|
||||
area->anchor.y += fmul(distance, dir.y);
|
||||
area->lifetime = anim_duration(anim);
|
||||
|
@ -224,9 +239,10 @@ effect_area_t *effect_area_new_attack(uint16_t type, entity_t *e, int facing)
|
|||
|
||||
if(type == EFFECT_ATTACK_HIT
|
||||
|| type == EFFECT_ATTACK_SLASH
|
||||
|| type == EFFECT_ATTACK_IMPALE) {
|
||||
area->data.slash.strength = e->ATK;
|
||||
area->data.slash.dir = facing;
|
||||
|| type == EFFECT_ATTACK_IMPALE
|
||||
|| type == EFFECT_ATTACK_JUDGEMENT) {
|
||||
area->data.generic.strength = e->ATK;
|
||||
area->data.generic.dir = facing;
|
||||
}
|
||||
else if(type == EFFECT_ATTACK_SHOCK) {
|
||||
area->data.shock.strength = e->ATK;
|
||||
|
@ -275,8 +291,8 @@ static bool attack_apply(game_t *game, effect_area_t *ea, entity_t *e)
|
|||
if(ea->type == EFFECT_ATTACK_HIT
|
||||
|| ea->type == EFFECT_ATTACK_SLASH
|
||||
|| ea->type == EFFECT_ATTACK_IMPALE) {
|
||||
dir = fdir(ea->data.slash.dir);
|
||||
damage = ea->data.slash.strength;
|
||||
dir = fdir(ea->data.generic.dir);
|
||||
damage = ea->data.generic.strength;
|
||||
}
|
||||
else if(ea->type == EFFECT_ATTACK_SHOCK) {
|
||||
dir.x = e->movement.x - ea->data.shock.origin.x;
|
||||
|
@ -284,6 +300,10 @@ static bool attack_apply(game_t *game, effect_area_t *ea, entity_t *e)
|
|||
dir = fnormalize(dir);
|
||||
damage = ea->data.shock.strength * 3 / 2;
|
||||
}
|
||||
else if(ea->type == EFFECT_ATTACK_JUDGEMENT) {
|
||||
r = fix(0);
|
||||
damage = ea->data.generic.strength * 5;
|
||||
}
|
||||
|
||||
/* Inflict damage */
|
||||
damage = entity_damage(e, damage);
|
||||
|
@ -316,7 +336,8 @@ void effect_area_apply(game_t *game, effect_area_t *ea, entity_t *e)
|
|||
if(ea->type == EFFECT_ATTACK_HIT
|
||||
|| ea->type == EFFECT_ATTACK_SLASH
|
||||
|| ea->type == EFFECT_ATTACK_IMPALE
|
||||
|| ea->type == EFFECT_ATTACK_SHOCK)
|
||||
|| ea->type == EFFECT_ATTACK_SHOCK
|
||||
|| ea->type == EFFECT_ATTACK_JUDGEMENT)
|
||||
was_hit = attack_apply(game, ea, e);
|
||||
|
||||
if(!was_hit) return;
|
||||
|
|
|
@ -130,8 +130,9 @@ enum {
|
|||
EFFECT_ATTACK_SLASH,
|
||||
/* Impaling attack using a slashing weapon */
|
||||
EFFECT_ATTACK_IMPALE,
|
||||
/* Shock attack */
|
||||
/* Skills */
|
||||
EFFECT_ATTACK_SHOCK,
|
||||
EFFECT_ATTACK_JUDGEMENT,
|
||||
/* Monster spawning */
|
||||
EFFECT_SPAWN,
|
||||
};
|
||||
|
@ -166,10 +167,8 @@ typedef struct effect_area {
|
|||
uint16_t type;
|
||||
/* Effect data (type-dependent) */
|
||||
union {
|
||||
/* EFFECT_ATTACK_HIT: source and ATK strength */
|
||||
struct { int strength; int dir; } hit;
|
||||
/* EFFECT_ATTACK_SLASH: source and ATK strength */
|
||||
struct { int strength; int dir; } slash;
|
||||
/* Generic attacks: source and ATK strength */
|
||||
struct { int strength; int dir; } generic;
|
||||
/* EFFECT_ATTACK_SHOCK: origin and ATK strength */
|
||||
struct { int strength; fpoint_t origin; } shock;
|
||||
} data;
|
||||
|
|
|
@ -112,10 +112,10 @@ void game_spawn_entity(game_t *g, entity_t *e, fpoint_t pos)
|
|||
effect_area_t *area = effect_area_new(EFFECT_SPAWN);
|
||||
area->sprite = hitbox;
|
||||
area->anchor = entity_pos(e);
|
||||
area->lifetime = anim_duration(anim_teleport);
|
||||
area->lifetime = anim_duration(anims_skill_teleport);
|
||||
area->repeat_delay = 0;
|
||||
area->origin = e;
|
||||
effect_area_set_anim(area, anim_teleport);
|
||||
effect_area_set_anim(area, anims_skill_teleport);
|
||||
game_add_effect_area(g, area);
|
||||
|
||||
e->current_attack = area;
|
||||
|
|
|
@ -459,6 +459,15 @@ int main(void)
|
|||
player->current_attack = area;
|
||||
player->attack_follows_movement = true;
|
||||
}
|
||||
if(player->HP > 0 && keydown(KEY_F3) && !player->current_attack) {
|
||||
effect_area_t *area = effect_area_new_attack(
|
||||
EFFECT_ATTACK_JUDGEMENT, player, player->movement.facing);
|
||||
game_add_effect_area(&game, area);
|
||||
|
||||
entity_set_anim(player, anims_player_Attack, 2);
|
||||
player->current_attack = area;
|
||||
player->attack_follows_movement = false;
|
||||
}
|
||||
|
||||
/* Remove dead entities first as it will kill their attack areas */
|
||||
game_remove_dead_entities(&game);
|
||||
|
|
|
@ -145,10 +145,7 @@ static void render_effect_area(effect_area_t const *ea, camera_t const *c,
|
|||
ipoint_t anchor = camera_map2screen(c, ea->anchor);
|
||||
|
||||
if(ea->anim.frame) {
|
||||
fpoint_t top_left = {
|
||||
ea->anchor.x + ea->sprite.l,
|
||||
ea->anchor.y + ea->sprite.t };
|
||||
ipoint_t p = camera_map2screen(c, top_left);
|
||||
ipoint_t p = camera_map2screen(c, ea->anchor);
|
||||
anim_frame_render(p.x, p.y, ea->anim.frame);
|
||||
}
|
||||
if(!ea->anim.frame || show_hitboxes) {
|
||||
|
|