add a fire charge attack to fire slimes
This commit is contained in:
parent
0b0a667ce0
commit
6e79618784
|
@ -477,10 +477,16 @@ def convert_aseprite_anim_variation(input, output, params):
|
|||
else:
|
||||
raise fxconv.FxconvError(f"{input} lacks a 'palette:' metadata")
|
||||
|
||||
base = os.path.join(os.path.dirname(input), base)
|
||||
palette = os.path.join(os.path.dirname(input), palette)
|
||||
base_path = os.path.join(os.path.dirname(input), base)
|
||||
palette_path = os.path.join(os.path.dirname(input), palette)
|
||||
|
||||
with open(palette, "rb") as fp:
|
||||
base_metadata = os.path.join(os.path.dirname(input),
|
||||
"fxconv-metadata.txt")
|
||||
base_metadata = fxconv.Metadata(base_metadata).rules_for(base)
|
||||
|
||||
params = { **base_metadata, **params }
|
||||
|
||||
with open(palette_path, "rb") as fp:
|
||||
ase = AsepriteFile(fp.read())
|
||||
|
||||
newpalette = None
|
||||
|
@ -490,7 +496,7 @@ def convert_aseprite_anim_variation(input, output, params):
|
|||
if newpalette is None:
|
||||
raise fxconv.FxconvError("f{input} has no palette chunk")
|
||||
|
||||
return convert_aseprite_anim(base, output, params, newpalette)
|
||||
return convert_aseprite_anim(base_path, output, params, newpalette)
|
||||
|
||||
def print_xml_tree(node, indent=0):
|
||||
print(indent*" " + f"<{node.tag}> {node.attrib}")
|
||||
|
|
|
@ -95,7 +95,7 @@ fixed_t (anim_duration)(anim_t const *anim)
|
|||
|
||||
bool anim_in(anim_frame_t const *frame, anim_t const *anim, int index)
|
||||
{
|
||||
if(index >= anim->directions)
|
||||
if(index >= anim->directions || !frame || !anim)
|
||||
return false;
|
||||
|
||||
anim_frame_t *f = anim->start[index];
|
||||
|
|
32
src/aoe.c
32
src/aoe.c
|
@ -86,6 +86,13 @@ entity_t *aoe_make_attack(uint16_t type, entity_t *origin, vec2 dir)
|
|||
distance = fix(0.375);
|
||||
lifetime = fix(999.0);
|
||||
}
|
||||
else if(type == AOE_FIRE_CHARGE) {
|
||||
anim = NULL;
|
||||
hitbox = (rect){ -fix(12)/16, fix(12)/16, -fix(13)/16, fix(2)/16 };
|
||||
distance = fix(0);
|
||||
lifetime = fix(1.1);
|
||||
rotate = false;
|
||||
}
|
||||
|
||||
p->x += fmul(distance, dir.x);
|
||||
p->y += fmul(distance, dir.y);
|
||||
|
@ -94,7 +101,13 @@ entity_t *aoe_make_attack(uint16_t type, entity_t *origin, vec2 dir)
|
|||
|
||||
v->sprite_plane = plane;
|
||||
|
||||
aoe->lifetime = lifetime ? lifetime : anim_duration(anim);
|
||||
if(lifetime)
|
||||
aoe->lifetime = lifetime;
|
||||
else if(anim)
|
||||
aoe->lifetime = anim_duration(anim);
|
||||
else
|
||||
aoe->lifetime = fix(1.0);
|
||||
|
||||
aoe->repeat_delay = 0;
|
||||
aoe->origin = origin;
|
||||
|
||||
|
@ -127,6 +140,12 @@ entity_t *aoe_make_attack(uint16_t type, entity_t *origin, vec2 dir)
|
|||
v->z = fix(0.5);
|
||||
v->shadow_size = 4;
|
||||
}
|
||||
else if(type == AOE_FIRE_CHARGE) {
|
||||
aoe->data.charge.element = 0;
|
||||
aoe->data.charge.power = f->MAG;
|
||||
aoe->data.charge.origin = physical_pos(origin);
|
||||
aoe->repeat_delay = fix(0.2);
|
||||
}
|
||||
|
||||
visible_set_anim(e, anim, 1);
|
||||
return e;
|
||||
|
@ -208,6 +227,14 @@ static bool attack_apply(game_t *game, aoe_t *aoe, entity_t *target)
|
|||
/* TODO: Sideways knockback */
|
||||
damage = aoe->data.bullet.strength * 2;
|
||||
}
|
||||
else if(aoe->type == AOE_FIRE_CHARGE) {
|
||||
dir.x = target_p->x - aoe->data.charge.origin.x;
|
||||
dir.y = target_p->y - aoe->data.charge.origin.y;
|
||||
dir = fnormalize(dir);
|
||||
damage = aoe->data.charge.power * 3 / 2;
|
||||
r /= 4;
|
||||
fighter_stun(target, fix(0.1));
|
||||
}
|
||||
|
||||
/* Inflict damage */
|
||||
damage = fighter_damage(target, damage);
|
||||
|
@ -254,7 +281,8 @@ void aoe_apply(game_t *game, entity_t *entity, entity_t *e)
|
|||
|| aoe->type == AOE_SLASH
|
||||
|| aoe->type == AOE_IMPALE
|
||||
|| aoe->type == AOE_SHOCK
|
||||
|| aoe->type == AOE_BULLET)
|
||||
|| aoe->type == AOE_BULLET
|
||||
|| aoe->type == AOE_FIRE_CHARGE)
|
||||
was_hit = attack_apply(game, aoe, e);
|
||||
|
||||
if(aoe->type == AOE_JUDGEMENT) {
|
||||
|
|
|
@ -23,6 +23,7 @@ enum {
|
|||
AOE_SHOCK,
|
||||
AOE_JUDGEMENT,
|
||||
AOE_BULLET,
|
||||
AOE_FIRE_CHARGE,
|
||||
/* Spawn effect */
|
||||
EFFECT_SPAWN,
|
||||
};
|
||||
|
@ -63,6 +64,8 @@ typedef struct
|
|||
struct { int strength; vec2 origin; } shock;
|
||||
/* AOE_BULLET: magic strengh, speed parameters */
|
||||
struct { int strength; int dir; fixed_t v; fixed_t final_v; } bullet;
|
||||
/* AOE_*_CHARGE: element type, origin and MAG power */
|
||||
struct { int element; int power; vec2 origin; } charge;
|
||||
} data;
|
||||
|
||||
} aoe_t;
|
||||
|
|
|
@ -9,6 +9,11 @@ void visible_set_anim(entity_t *e, anim_t const *anim, int priority)
|
|||
if(priority < v->anim_priority)
|
||||
return;
|
||||
|
||||
if(anim == NULL) {
|
||||
v->anim.frame = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
int anim_index = 0;
|
||||
physical_t *p = getcomp(e, physical);
|
||||
|
||||
|
@ -22,7 +27,7 @@ void visible_set_anim(entity_t *e, anim_t const *anim, int priority)
|
|||
if(anim_in(v->anim.frame, anim, anim_index))
|
||||
return;
|
||||
|
||||
v->anim.frame = anim->start[anim_index];
|
||||
v->anim.frame = anim ? anim->start[anim_index] : NULL;
|
||||
v->anim.elapsed = 0;
|
||||
v->anim_priority = priority;
|
||||
}
|
||||
|
@ -30,6 +35,9 @@ void visible_set_anim(entity_t *e, anim_t const *anim, int priority)
|
|||
void visible_update(entity_t *e, fixed_t dt)
|
||||
{
|
||||
visible_t *v = getcomp(e, visible);
|
||||
if(v->anim.frame == NULL)
|
||||
return;
|
||||
|
||||
anim_state_update(&v->anim, dt);
|
||||
|
||||
if(v->anim.frame == NULL)
|
||||
|
|
|
@ -68,7 +68,7 @@ static enemy_t const bat_2 = {
|
|||
static enemy_t const fire_slime_4 = {
|
||||
.name = "Fire slime",
|
||||
.level = 4,
|
||||
ANIMS(fire_slime, Idle, Walking, Idle, Hit, Death),
|
||||
ANIMS(fire_slime, Idle, Walking, Attack, Hit, Death),
|
||||
.hitbox = (rect){ -fix(3)/16, fix(4)/16, -fix(2)/16, fix(3)/16 },
|
||||
.limits = {
|
||||
.max_speed = fix(1),
|
||||
|
@ -176,6 +176,9 @@ entity_t *enemy_make(int enemy_id)
|
|||
struct bat_ai *ai_data = f->enemy->ai_data;
|
||||
ai_data->retreat_period = 0;
|
||||
}
|
||||
else if(enemy_id == ENEMY_FIRE_SLIME_4) {
|
||||
f->skills[1] = AOE_FIRE_CHARGE;
|
||||
}
|
||||
else if(enemy_id == ENEMY_GUNSLINGER_8) {
|
||||
f->skills[0] = AOE_PROJECTILE;
|
||||
}
|
||||
|
@ -287,8 +290,7 @@ static bool use_skill_towards_player(game_t *g, entity_t *e, int slot)
|
|||
vec2 dir = { player_p->x - p->x, player_p->y - p->y };
|
||||
dir = fnormalize(dir);
|
||||
|
||||
skill_use(g, e, slot, dir);
|
||||
return true;
|
||||
return skill_use(g, e, slot, dir);
|
||||
}
|
||||
|
||||
void enemy_ai(game_t *g, entity_t *e, fixed_t dt)
|
||||
|
@ -318,7 +320,13 @@ void enemy_ai(game_t *g, entity_t *e, fixed_t dt)
|
|||
|
||||
else if(f->enemy->id == &fire_slime_4) {
|
||||
if(move_within_range_of_player(g, e, fix(1.0), dt)) {
|
||||
contact_attack(g, e);
|
||||
/* Use the fire charge attack; if it fails, normal attack */
|
||||
if(use_skill_towards_player(g, e, 1)) {
|
||||
visible_set_anim(e, &anims_fire_slime_Fire, 2);
|
||||
}
|
||||
else {
|
||||
contact_attack(g, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -360,7 +360,7 @@ int main(void)
|
|||
}
|
||||
if(ev.key == KEY_F4 && debug.dev_menu) {
|
||||
player_add_xp(game.player, 1000);
|
||||
fighter_invulnerability(game.player, fix(999.0));
|
||||
// fighter_invulnerability(game.player, fix(999.0));
|
||||
player_f->skills[1] = AOE_SHOCK;
|
||||
player_f->skills[2] = AOE_JUDGEMENT;
|
||||
player_f->skills[3] = SKILL_DASH;
|
||||
|
|
|
@ -279,7 +279,7 @@ static void render_entities(game_t const *g, camera_t const *camera,
|
|||
anim_frame_render(scr.x, elevated_y, v->anim.frame);
|
||||
}
|
||||
/* Show entity hitbox in the map coordinate system */
|
||||
if(v && (!v->anim.frame || show_hitboxes)) {
|
||||
if(v && show_hitboxes) {
|
||||
rect r = p->hitbox;
|
||||
r = rect_scale(r, camera_ppu(camera));
|
||||
r = rect_translate(r, vec_i2f(scr));
|
||||
|
|
29
src/skills.c
29
src/skills.c
|
@ -18,17 +18,19 @@ fixed_t skill_cooldown(int skill)
|
|||
return fix(3.0);
|
||||
else if(skill == AOE_BULLET)
|
||||
return fix(4.0);
|
||||
else if(skill == AOE_FIRE_CHARGE)
|
||||
return fix(3.0);
|
||||
|
||||
return fix(0.0);
|
||||
}
|
||||
|
||||
void skill_use(game_t *game, entity_t *e, int slot, vec2 dir)
|
||||
bool skill_use(game_t *game, entity_t *e, int slot, vec2 dir)
|
||||
{
|
||||
fighter_t *f = getcomp(e, fighter);
|
||||
int skill = f->skills[slot];
|
||||
|
||||
if(skill < 0 || f->actions_cooldown[slot] > 0)
|
||||
return;
|
||||
return false;
|
||||
f->actions_cooldown[slot] = skill_cooldown(skill);
|
||||
|
||||
if(skill == SKILL_DASH) {
|
||||
|
@ -36,7 +38,7 @@ void skill_use(game_t *game, entity_t *e, int slot, vec2 dir)
|
|||
}
|
||||
else if(skill == AOE_PROJECTILE) {
|
||||
if(f->current_attack)
|
||||
return;
|
||||
return false;
|
||||
entity_t *aoe = aoe_make_attack(AOE_PROJECTILE, e, dir);
|
||||
game_add_entity(game, aoe);
|
||||
|
||||
|
@ -47,7 +49,7 @@ void skill_use(game_t *game, entity_t *e, int slot, vec2 dir)
|
|||
}
|
||||
else if(skill == AOE_SHOCK) {
|
||||
if(f->current_attack)
|
||||
return;
|
||||
return false;
|
||||
entity_t *aoe = aoe_make_attack(AOE_SHOCK, e, dir);
|
||||
game_add_entity(game, aoe);
|
||||
|
||||
|
@ -63,7 +65,7 @@ void skill_use(game_t *game, entity_t *e, int slot, vec2 dir)
|
|||
}
|
||||
else if(skill == AOE_JUDGEMENT) {
|
||||
if(f->current_attack)
|
||||
return;
|
||||
return false;
|
||||
entity_t *aoe = aoe_make_attack(AOE_JUDGEMENT, e, dir);
|
||||
game_add_entity(game, aoe);
|
||||
|
||||
|
@ -76,7 +78,7 @@ void skill_use(game_t *game, entity_t *e, int slot, vec2 dir)
|
|||
}
|
||||
else if(skill == AOE_BULLET) {
|
||||
if(f->current_attack)
|
||||
return;
|
||||
return false;
|
||||
entity_t *aoe = aoe_make_attack(AOE_BULLET, e, dir);
|
||||
game_add_entity(game, aoe);
|
||||
|
||||
|
@ -85,6 +87,21 @@ void skill_use(game_t *game, entity_t *e, int slot, vec2 dir)
|
|||
else
|
||||
visible_set_anim(e, f->enemy->id->anim_attack, 2);
|
||||
}
|
||||
else if(skill == AOE_FIRE_CHARGE
|
||||
/* || skill == AOE_WATER_CHARGE */) {
|
||||
if(f->current_attack)
|
||||
return false;
|
||||
entity_t *aoe = aoe_make_attack(skill, e, dir);
|
||||
game_add_entity(game, aoe);
|
||||
|
||||
f->current_attack = aoe;
|
||||
f->attack_follows_movement = true;
|
||||
|
||||
/* This skill is used by enemies, the AI will set the animation
|
||||
themselves on a per-enemy basis */
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void skill_render(int x, int y, int skill, int bg, int color)
|
||||
|
|
|
@ -15,13 +15,14 @@ enum {
|
|||
// AOE_SHOCK,
|
||||
// AOE_JUDGEMENT,
|
||||
// AOE_BULLET,
|
||||
// AOE_FIRE_CHARGE,
|
||||
};
|
||||
|
||||
/* Fixed cooldown for a skill */
|
||||
fixed_t skill_cooldown(int skill);
|
||||
|
||||
/* Have [e] use its skill in the specified slot. */
|
||||
void skill_use(game_t *g, entity_t *e, int slot, vec2 dir);
|
||||
/* Have [e] use its skill in the specified slot. Returns true on success. */
|
||||
bool skill_use(game_t *g, entity_t *e, int slot, vec2 dir);
|
||||
|
||||
/* Render a skill's image */
|
||||
void skill_render(int x, int y, int skill, int bg, int color);
|
||||
|
|
Loading…
Reference in New Issue