diff --git a/CMakeLists.txt b/CMakeLists.txt index c762bf2..a8283a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,6 +156,12 @@ set(ASSETS # Enemies: Washing machine assets-cg/enemies/washing_machine_left.aseprite assets-cg/enemies/washing_machine_right.aseprite + # Enemies: Water slime + assets-cg/enemies/water_slime_left.txt + assets-cg/enemies/water_slime_right.txt + # Enemies: Chemical slime + assets-cg/enemies/chemical_slime_left.txt + assets-cg/enemies/chemical_slime_right.txt # Misc assets-cg/font_damage_red.png assets-cg/font_damage_white.png diff --git a/TODO b/TODO index 54e95f4..2e2528b 100644 --- a/TODO +++ b/TODO @@ -39,6 +39,8 @@ Core mechanics: Content: * Additional skills +* Give the chemical slime a magic attack +* Jump attacks for elemental slimes Infrastructure: * Better end screen diff --git a/assets-cg/converters.py b/assets-cg/converters.py index 3f295e5..bc8a6d1 100644 --- a/assets-cg/converters.py +++ b/assets-cg/converters.py @@ -35,6 +35,8 @@ MONSTER_IDS = { "fire_slime/4": 3, "albinos_bat/6": 4, "gunslinger/8": 5, + "water_slime/8": 6, + "chemical_slime/10": 7, } ITEM_IDS = { "life": 0, diff --git a/src/aoe.c b/src/aoe.c index bb5dcec..05b4a91 100644 --- a/src/aoe.c +++ b/src/aoe.c @@ -87,7 +87,9 @@ 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) { + else if(type == AOE_FIRE_CHARGE + || type == AOE_WATER_CHARGE + || type == AOE_CHEMICAL_CHARGE) { anim = NULL; hitbox = (rect){ -fix(12)/16, fix(12)/16, -fix(13)/16, fix(2)/16 }; distance = fix(0); @@ -147,7 +149,9 @@ 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) { + else if(type == AOE_FIRE_CHARGE + || type == AOE_WATER_CHARGE + || type == AOE_CHEMICAL_CHARGE) { aoe->data.charge.element = 0; aoe->data.charge.power = f->MAG; aoe->data.charge.origin = physical_pos(origin); @@ -234,7 +238,9 @@ static bool attack_apply(game_t *game, aoe_t *aoe, entity_t *target) /* TODO: Sideways knockback */ damage = aoe->data.bullet.strength * 3 / 2; } - else if(aoe->type == AOE_FIRE_CHARGE) { + else if(aoe->type == AOE_FIRE_CHARGE + || aoe->type == AOE_WATER_CHARGE + || aoe->type == AOE_CHEMICAL_CHARGE) { dir.x = target_p->x - aoe->data.charge.origin.x; dir.y = target_p->y - aoe->data.charge.origin.y; dir = fnormalize(dir); @@ -292,7 +298,9 @@ void aoe_apply(game_t *game, entity_t *entity, entity_t *e) || aoe->type == AOE_IMPALE || aoe->type == AOE_SHOCK || aoe->type == AOE_BULLET - || aoe->type == AOE_FIRE_CHARGE) + || aoe->type == AOE_FIRE_CHARGE + || aoe->type == AOE_WATER_CHARGE + || aoe->type == AOE_CHEMICAL_CHARGE) was_hit = attack_apply(game, aoe, e); if(aoe->type == AOE_JUDGEMENT) { diff --git a/src/aoe.h b/src/aoe.h index 61a0d70..8674102 100644 --- a/src/aoe.h +++ b/src/aoe.h @@ -25,6 +25,8 @@ enum { AOE_JUDGEMENT, AOE_BULLET, AOE_FIRE_CHARGE, + AOE_WATER_CHARGE, + AOE_CHEMICAL_CHARGE, /* Spawn effect */ EFFECT_SPAWN, }; diff --git a/src/enemies.c b/src/enemies.c index 5c5348d..849fe12 100644 --- a/src/enemies.c +++ b/src/enemies.c @@ -118,12 +118,54 @@ static enemy_t const gunslinger_8 = { .ai_data_size = 0, }; +static enemy_t const water_slime_8 = { + .name = "Water slime", + .level = 8, + ANIMS(water_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.2), + .friction = fix(0.4), + .max_disruption_speed = fix(999.0), + }, + /* Same as slime/1 */ + .stats_base = { .HP=10, .ATK=8, .MAG=5, .DEF=5 }, + .stats_growth = { .HP=8, .ATK=4, .MAG=4, .DEF=2 }, + + .shadow_size = 4, + .xp = 24, + .z = 0, + .ai_data_size = 0, +}; + +static enemy_t const chemical_slime_10 = { + .name = "Chemical slime", + .level = 10, + ANIMS(chemical_slime, Idle, Walking, Attack, Hit, Death), + .hitbox = (rect){ -fix(3)/16, fix(4)/16, -fix(2)/16, fix(3)/16 }, + .limits = { + .max_speed = fix(0.6), + .friction = fix(0.8), + .max_disruption_speed = fix(999.0), + }, + /* Like as slime/1 but boosted MAG over ATK */ + .stats_base = { .HP=10, .ATK=8, .MAG=5, .DEF=5 }, + .stats_growth = { .HP=8, .ATK=2, .MAG=6, .DEF=2 }, + + .shadow_size = 4, + .xp = 41, + .z = 0, + .ai_data_size = 0, +}; + static enemy_t const * const enemies[] = { - [ENEMY_SLIME_1] = &slime_1, - [ENEMY_BAT_2] = &bat_2, - [ENEMY_FIRE_SLIME_4] = &fire_slime_4, - [ENEMY_ALBINOS_BAT_6] = &albinos_bat_6, - [ENEMY_GUNSLINGER_8] = &gunslinger_8, + [ENEMY_SLIME_1] = &slime_1, + [ENEMY_BAT_2] = &bat_2, + [ENEMY_FIRE_SLIME_4] = &fire_slime_4, + [ENEMY_ALBINOS_BAT_6] = &albinos_bat_6, + [ENEMY_GUNSLINGER_8] = &gunslinger_8, + [ENEMY_WATER_SLIME_8] = &water_slime_8, + [ENEMY_CHEMICAL_SLIME_10] = &chemical_slime_10, }; enemy_t const *enemy_data(int enemy_id) @@ -195,6 +237,12 @@ entity_t *enemy_make(int enemy_id) else if(enemy_id == ENEMY_GUNSLINGER_8) { f->skills[0] = AOE_PROJECTILE; } + else if(enemy_id == ENEMY_WATER_SLIME_8) { + f->skills[1] = AOE_WATER_CHARGE; + } + else if(enemy_id == ENEMY_CHEMICAL_SLIME_10) { + f->skills[1] = AOE_CHEMICAL_CHARGE; + } return e; } @@ -338,11 +386,17 @@ void enemy_ai(game_t *g, entity_t *e, fixed_t dt) } } - else if(f->enemy->id == &fire_slime_4) { + else if(f->enemy->id == &fire_slime_4 || f->enemy->id == &water_slime_8 + || f->enemy->id == &chemical_slime_10) { if(move_within_range_of_player(g, e, fix(0.75), dt)) { /* 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); + if(f->enemy->id == &fire_slime_4) + visible_set_anim(e, &anims_fire_slime_Fire, 2); + if(f->enemy->id == &water_slime_8) + visible_set_anim(e, &anims_water_slime_Fire, 2); + if(f->enemy->id == &chemical_slime_10) + visible_set_anim(e, &anims_chemical_slime_Fire, 2); } else { contact_attack(g, e); diff --git a/src/enemies.h b/src/enemies.h index a024d0c..7c114cf 100644 --- a/src/enemies.h +++ b/src/enemies.h @@ -40,11 +40,13 @@ typedef struct /* List of enemies */ enum { /* ID 0 is used for the player! */ - ENEMY_SLIME_1 = 1, - ENEMY_BAT_2 = 2, - ENEMY_FIRE_SLIME_4 = 3, - ENEMY_ALBINOS_BAT_6 = 4, - ENEMY_GUNSLINGER_8 = 5, + ENEMY_SLIME_1 = 1, + ENEMY_BAT_2 = 2, + ENEMY_FIRE_SLIME_4 = 3, + ENEMY_ALBINOS_BAT_6 = 4, + ENEMY_GUNSLINGER_8 = 5, + ENEMY_WATER_SLIME_8 = 6, + ENEMY_CHEMICAL_SLIME_10 = 7, }; /* Dynamic enemy information. */ diff --git a/src/skills.c b/src/skills.c index 474ef65..c77e4bb 100644 --- a/src/skills.c +++ b/src/skills.c @@ -22,7 +22,9 @@ 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) + else if(skill == AOE_FIRE_CHARGE + || skill == AOE_WATER_CHARGE + || skill == AOE_CHEMICAL_CHARGE) return fix(3.0); return fix(0.0); @@ -108,7 +110,8 @@ bool skill_use(game_t *game, entity_t *e, int slot, vec2 dir) visible_set_anim(e, f->enemy->id->anim_attack, 2); } else if(skill == AOE_FIRE_CHARGE - /* || skill == AOE_WATER_CHARGE */) { + || skill == AOE_WATER_CHARGE + || skill == AOE_CHEMICAL_CHARGE) { if(f->current_attack) return false; entity_t *aoe = aoe_make_attack(skill, e, dir); diff --git a/src/skills.h b/src/skills.h index ca49c13..f5f7836 100644 --- a/src/skills.h +++ b/src/skills.h @@ -18,6 +18,8 @@ enum { // AOE_JUDGEMENT, // AOE_BULLET, // AOE_FIRE_CHARGE, + // AOE_WATER_CHARGE, + // AOE_CHEMICAL_CHARGE, }; /* Fixed cooldown for a skill */