void XBoss::run_target(int index) { Object *o = targets[index]; // has this target been destroyed? // (we don't really kill the object until the battle is over, // to avoid having to deal with dangling pointers). if (o->invisible) return; switch(o->state) { case 0: o->flags &= ~FLAG_SHOOTABLE; o->frame &= 3; o->state = 1; break; case STATE_TARGET_FIRE: { o->timer = 40 + (index * 10); o->flags |= FLAG_SHOOTABLE; o->state++; } case STATE_TARGET_FIRE+1: { if (--o->timer <= 16) { // flash shortly before firing if (o->timer & 2) o->frame |= 4; else o->frame &= 3; if (o->timer <= 0) { o->timer = 40; EmFireAngledShot(o, OBJ_GAUDI_FLYING_SHOT, 2, 0x500); sound(SND_EM_FIRE); } } } break; } // keep appropriate position on internals // UL UR LL LR static const int xoffs[] = { -22 <<CSF, 28 <<CSF, -15 <<CSF, 17 <<CSF }; static const int yoffs[] = { -16 <<CSF, -16 <<CSF, 14 <<CSF, 14 <<CSF }; o->x = internals->x + xoffs[index]; o->y = internals->y + yoffs[index]; }
// her 3 attacks: black shots, black balls, and summon falling block. static void run_spells(Object *o) { switch (o->state) { // flashes for spell... // then either fires shots or casts the falling-block spell case STATE_FLASH_FOR_SPELL: { o->flags &= ~FLAG_SHOOTABLE; o->xinertia = 0; o->yinertia = 0; o->timer = 0; o->state++; } case STATE_FLASH_FOR_SPELL + 1: { o->timer++; o->frame = 5 + (o->timer & 1); if (o->timer > 30) { o->timer = 0; o->frame = 4; if (++o->timer2 >= 3) { o->state = STATE_SUMMON_BLOCK; o->timer2 = 0; } else { o->state = STATE_FIRE_SHOTS; } } } break; // fire black shots at player case STATE_FIRE_SHOTS: { if ((++o->timer % 6) == 0) { EmFireAngledShot(o, OBJ_MISERY_SHOT, 4, 0x800); NXE::Sound::SoundManager::getInstance()->playSfx(NXE::Sound::SFX::SND_FIREBALL); } if (o->timer > 30) { o->timer = 0; o->state = STATE_TP_AWAY; } } break; // summon falling block case STATE_SUMMON_BLOCK: { if (++o->timer == 10) { int x = player->x - (8 * CSFI); int y = player->y - (64 * CSFI); Object *block = CreateObject(x, y, OBJ_FALLING_BLOCK); block->sprite = SPR_BALCONY_BLOCK_LARGE; block->dir = DOWN; // tell block it was spawned by Misery } if (o->timer > 30) { o->state = STATE_TP_AWAY; o->timer = 0; } } break; // summon black balls case STATE_SUMMON_BALLS: { FACEPLAYER; o->frame = 4; o->timer = 0; o->state++; } case STATE_SUMMON_BALLS + 1: { o->yinertia += (o->y < o->ymark) ? 0x20 : -0x20; LIMITY(0x200); if ((++o->timer % 24) == 0) { CreateObject(o->x, o->y + (4 * CSFI), OBJ_MISERY_BALL); NXE::Sound::SoundManager::getInstance()->playSfx(NXE::Sound::SFX::SND_FIREBALL); } if (o->timer > 72) { o->state = 100; o->timer = 0; } } break; } }
// used only for purple ones in maze void ai_critter_shooting_purple(Object *o) { switch(o->state) { case 0: o->state = STATE_IDLE; o->damage = CRITTER_DAMAGE; case STATE_IDLE: { o->frame = 0; // assume not at attention if (o->timer >= 8) { if (pdistlx(96<<CSF) && pdistly2(96<<CSF, 32<<CSF)) { FACEPLAYER; // close enough to attack? if (pdistlx(48<<CSF)) { o->state = STATE_PREPARE_JUMP; o->frame = 0; o->timer = 0; } else { // no, but stand at "attention" o->frame = 1; } } } else { o->timer++; } // also attack if shot if (o->shaketime) { o->state = STATE_PREPARE_JUMP; o->frame = 0; o->timer = 0; } } break; case STATE_PREPARE_JUMP: { o->frame = 1; if (++o->timer > 8) { FACEPLAYER; o->state = STATE_JUMP; o->timer = 0; o->frame = 2; sound(SND_ENEMY_JUMP); o->yinertia = -0x5ff; } } break; case STATE_JUMP: { if (o->yinertia > 0x100 || \ (o->blockd && ++o->timer > 16)) // failsafe { o->ymark = o->y; o->state = STATE_HOVER; o->frame = 3; o->timer = 0; o->CurlyTargetHere(60, 100); } } break; case STATE_HOVER: { // sinusoidal hover o->yinertia += (o->y > o->ymark) ? -0x10 : 0x10; LIMITY(0x200); FACEPLAYER; ANIMATE(0, 3, 5); // time to end flight? if (++o->timer > 60 || o->blocku) { o->damage = CRITTER_FALL_DAMAGE; o->state = STATE_END_JUMP; o->frame = 2; break; } if ((o->timer % 4) == 1) sound(SND_CRITTER_FLY); if ((o->timer % 30) == 6) { EmFireAngledShot(o, OBJ_CRITTER_SHOT, 6, 0x600); sound(SND_EM_FIRE); } if (o->blockd) o->yinertia = -0x200; } break; case STATE_END_JUMP: { if (o->blockd) { o->damage = 2; o->xinertia = 0; o->timer = 0; o->frame = 0; o->state = 0; sound(SND_THUD); } } break; } if (o->state != STATE_HOVER) { o->yinertia += 0x20; LIMITY(0x5ff); } }
void ai_minicore(Object *o) { Object *core = o->linkedobject; if (!core) { o->Delete(); return; } switch(o->state) { case MC_SLEEP: // idle & mouth closed o->frame = 2; o->xmark = o->x; o->ymark = o->y; break; case MC_THRUST: // thrust (move to random new pos) o->state = MC_THRUST+1; o->frame = 2; o->timer = 0; o->xmark = core->x + (random(-128, 32) << CSF); o->ymark = core->y + (random(-64, 64) << CSF); case MC_THRUST+1: if (++o->timer > 50) { o->frame = 0; } break; case MC_CHARGE_FIRE: // charging for fire o->state = MC_CHARGE_FIRE+1; o->timer = 0; case MC_CHARGE_FIRE+1: // flash blue o->timer++; o->frame = ((o->timer >> 1) & 1); if (o->timer > 20) { o->state = MC_FIRE; } break; case MC_FIRE: // firing o->state = MC_FIRE+1; o->frame = 2; // close mouth again o->timer = 0; o->xmark = o->x + (random(24, 48) << CSF); o->ymark = o->y + (random(-4, 4) << CSF); case MC_FIRE+1: if (++o->timer > 50) { o->state = MC_FIRED; o->frame = 0; } else if (o->timer==1 || o->timer==3) { // fire at player at speed (2<<CSF) with 2 degrees of variance EmFireAngledShot(o, OBJ_MINICORE_SHOT, 2, 2<<CSF); sound(SND_EM_FIRE); } break; case MC_RETREAT: // defeated! o->state = MC_RETREAT+1; o->frame = 2; o->xinertia = o->yinertia = 0; case MC_RETREAT+1: // retreat back into the abyss o->xinertia += 0x20; if (o->x > ((map.xsize*TILE_W)<<CSF) + 0x4000) { o->Delete(); } break; } if (o->state < MC_RETREAT) { // jump back when shot if (o->shaketime) { o->xmark += 0x400; } o->x += (o->xmark - o->x) / 16; o->y += (o->ymark - o->y) / 16; } // don't let them kill us o->hp = 1000; // invincible when mouth is closed if (o->frame != 2) o->flags &= ~FLAG_INVULNERABLE; else o->flags |= FLAG_INVULNERABLE; }
void CoreBoss::Run() { bool do_thrust = false; int i; if (!o) return; //stat("state = %d", o->state); switch(o->state) { case CORE_SLEEP: break; // core is asleep // Core's mouth is closed. // Core targets player point but does not update it during the state. // This is also the state set via BOA to awaken the core. case CORE_CLOSED: { o->state = CORE_CLOSED+1; o->timer = 0; StopWaterStream(); o->xmark = player->x; o->ymark = player->y; } case CORE_CLOSED+1: { // open mouth after 400 ticks if (o->timer > 400) { if (++o->timer2 > 3) { // every 3rd time do gusting left and big core blasts o->timer2 = 0; o->state = CORE_GUST; } else { o->state = CORE_OPEN; } do_thrust = true; } } break; // Core's mouth is open. // Core moves towards player, and updates the position throughout // the state (is "aggressive" about seeking him). // Core fires ghosties, and curly targets it. case CORE_OPEN: { o->state = CORE_OPEN+1; o->timer = 0; // gonna open mouth, so save the current HP so we'll // know how much damage we've taken this time. o->savedhp = o->hp; } case CORE_OPEN+1: { o->xmark = player->x; o->ymark = player->y; // must call constantly for red-flashing when hit OPEN_MOUTH; // hint curly to target us if ((o->timer % 64) == 1) { o->CurlyTargetHere(); } // spawn ghosties if (o->timer < 200) { if ((o->timer % 20)==0) { CreateObject(o->x + (random(-48, -16) << CSF), \ o->y + (random(-64, 64) << CSF), \ OBJ_CORE_GHOSTIE); } } // close mouth when 400 ticks have passed or we've taken more than 200 damage if (o->timer > 400 || (o->savedhp - o->hp) >= 200) { o->state = CORE_CLOSED; CLOSE_MOUTH; do_thrust = true; } } break; case CORE_GUST: { o->state = CORE_GUST+1; o->timer = 0; StartWaterStream(); } case CORE_GUST+1: { // spawn water droplet effects and push player Object *droplet = CreateObject(player->x + ((random(-50, 150)<<CSF)*2), \ player->y + (random(-160, 160)<<CSF), OBJ_FAN_DROPLET); droplet->dir = LEFT; player->xinertia -= 0x20; OPEN_MOUTH; // spawn the big white blasts if (o->timer==300 || o->timer==350 || o->timer==400) { EmFireAngledShot(pieces[CFRONT], OBJ_CORE_BLAST, 0, 3<<CSF); sound(SND_LIGHTNING_STRIKE); } if (o->timer > 400) { o->state = CORE_CLOSED; CLOSE_MOUTH; do_thrust = true; } } break; case 500: // defeated!! { StopWaterStream(); map.wlforcestate = WL_CALM; o->state = 501; o->timer = 0; o->xinertia = o->yinertia = 0; game.curlytarget.timeleft = 0; CLOSE_MOUTH; game.quaketime = 20; SmokeXY(pieces[CBACK]->x, pieces[CBACK]->CenterY(), 20, 128, 64); // tell all the MC's to retreat for(i=0;i<5;i++) { pieces[i]->flags &= ~(FLAG_SHOOTABLE & FLAG_INVULNERABLE); pieces[i]->state = MC_RETREAT; } } case 501: { o->timer++; if ((o->timer & 0x0f) != 0) { SmokeXY(pieces[CBACK]->x, pieces[CBACK]->CenterY(), 1, 64, 32); } if (o->timer & 2) o->x -= (1 << CSF); else o->x += (1 << CSF); #define CORE_DEATH_TARGET_X 0x7a000 #define CORE_DEATH_TARGET_Y 0x16000 o->xinertia += (o->x > CORE_DEATH_TARGET_X) ? -0x80 : 0x80; o->yinertia += (o->y > CORE_DEATH_TARGET_Y) ? -0x80 : 0x80; } break; case 600: // teleported away by Misery { o->xinertia = 0; o->yinertia = 0; o->state++; //sound(SND_TELEPORT); pieces[CFRONT]->clip_enable = pieces[CBACK]->clip_enable = 1; o->timer = sprites[pieces[CFRONT]->sprite].h; } case 601: { pieces[CFRONT]->display_xoff = pieces[CBACK]->display_xoff = random(-8, 8); pieces[CFRONT]->clipy2 = o->timer; pieces[CBACK]->clipy2 = o->timer; if (--o->timer < 0) { pieces[CFRONT]->invisible = true; pieces[CBACK]->invisible = true; // restore status bars game.stageboss.object = NULL; game.bossbar.object = NULL; o->Delete(); o = NULL; return; } } break; } if (do_thrust) { // tell all the minicores to jump to a new position for(i=0;i<5;i++) { pieces[i]->state = MC_THRUST; } quake(20); sound(SND_CORE_THRUST); } // fire the minicores in any awake non-dead state if (o->state >= CORE_CLOSED && o->state < 500) { o->timer++; // fire off each minicore sequentially... switch(o->timer) { case 80+0: pieces[0]->state = MC_CHARGE_FIRE; break; case 80+30: pieces[1]->state = MC_CHARGE_FIRE; break; case 80+60: pieces[2]->state = MC_CHARGE_FIRE; break; case 80+90: pieces[3]->state = MC_CHARGE_FIRE; break; case 80+120: pieces[4]->state = MC_CHARGE_FIRE; break; } // move main core towards a spot in front of target o->xinertia += (o->x > (o->xmark + (160<<CSF))) ? -4 : 4; o->yinertia += (o->y > o->ymark - (o->Height() / 2)) ? -4 : 4; } // set up our shootable status--you never actually hit the core (CFRONT), // but if it's mouth is open, make us, the invisible controller object, shootable. if (pieces[CFRONT]->frame==2) { o->flags &= ~FLAG_SHOOTABLE; pieces[CFRONT]->flags |= FLAG_INVULNERABLE; } else { o->flags |= FLAG_SHOOTABLE; pieces[CFRONT]->flags &= ~FLAG_INVULNERABLE; } LIMITX(0x80); LIMITY(0x80); }
void ai_red_demon(Object *o) { switch(o->state) { case 0: { o->xmark = o->x; o->xinertia = 0; o->frame = 0; o->state = 1; } case 1: { ANIMATE(20, 0, 1); FACEPLAYER; } break; case 10: // prepare to jump { o->flags |= FLAG_SHOOTABLE; o->state = 11; o->frame = 3; o->timer = 0; } case 11: { switch(++o->timer) { case 30: case 40: case 50: { o->frame = 4; EmFireAngledShot(o, OBJ_RED_DEMON_SHOT, 0, 0x800); sound(SND_EM_FIRE); } break; case 34: case 44: case 54: { o->frame = 3; } break; case 61: { o->state = 20; o->timer = 0; o->frame = 2; } break; } } break; case 20: // pause before jump { if (++o->timer > 20) { o->state = 21; o->timer = 0; o->frame = 5; o->yinertia = -0x5ff; o->xinertia = (o->CenterX() < player->CenterX()) ? 0x100 : -0x100; } } break; case 21: // in air { switch(++o->timer) { case 30: case 40: case 50: { o->frame = 6; EmFireAngledShot(o, OBJ_RED_DEMON_SHOT, 0, 0x800); sound(SND_EM_FIRE); } break; case 34: case 44: { o->frame = 5; } break; case 54: { o->frame = 7; } break; } if (o->blockd && o->yinertia >= 0) { quake(10); o->state = 22; o->timer = 0; o->frame = 2; } } break; case 22: // landed { o->xinertia /= 2; if (++o->timer > 22) { o->state = 10; } } break; // defeated/turned to stone (set by script) case 50: { o->flags &= ~FLAG_SHOOTABLE; o->damage = 0; if (o->blockd) { o->state = 51; o->frame = 2; game.quaketime = 10; SmokeClouds(o, 12, 4, 4); o->SpawnXP(19); sound(SND_BIG_CRASH); // needed to prevent status bars from not disappearing game.bossbar.object = NULL; } } break; case 51: { o->xinertia *= 7; o->xinertia /= 8; o->frame = 8; } break; } if (o->state < 50) { FACEPLAYER; } o->yinertia += 0x20; LIMITY(0x5ff); }