void ai_black_lightning(Object *o) { ANIMATE(0, 0, 1); o->yinertia = 0x1000; if (o->blockd) { effect(o->CenterX(), o->Bottom(), EFFECT_BOOMFLASH); SmokeXY(o->CenterX(), o->Bottom(), 3, o->Width() / CSFI, 4); o->Delete(); } }
void ai_counter_bomb(Object *o) { /*debug("state: %d", o->state); debug("timer: %d", o->timer); debug("timer2: %d", o->timer2);*/ switch(o->state) { case 0: { o->state = 1; o->ymark = o->y; o->timer = random(0, 50); o->timer2 = 0; } case 1: { // desync if multiple enemies if (--o->timer < 0) { o->timer = 0; o->state = 2; o->yinertia = 0x300; } } break; case 2: // ready { if (pdistlx(80 << CSF) || o->shaketime) { o->state = 3; o->timer = 0; } } break; case 3: // counting down... { if (--o->timer < 0) { if (o->timer2 < 5) { Object *number = CreateObject(o->CenterX() + (8 << CSF), \ o->y + (16 << CSF), \ OBJ_COUNTER_BOMB_NUMBER); number->frame = o->timer2++; o->timer = 60; } else { // expand bounding box to cover explosion area o->x = o->CenterX(); o->y = o->CenterY(); o->invisible = true; o->sprite = SPR_BBOX_PUPPET_1; sprites[o->sprite].bbox.x1 = -128; sprites[o->sprite].bbox.y1 = -100; sprites[o->sprite].bbox.x2 = 128; sprites[o->sprite].bbox.y2 = 100; o->damage = 30; o->yinertia = 0; o->state = 4; // make kaboom sound(SND_EXPLOSION1); quake(20); SmokeXY(o->CenterX(), o->CenterY(), 100, 128, 100); return; } } } break; case 4: // exploding (one frame only to give time for bbox to damage player) o->Delete(); return; } ANIMATE(4, 0, 2); if (o->state == 2 || o->state == 3) { o->yinertia += (o->y > o->ymark) ? -0x10 : 0x10; LIMITY(0x100); } }
void ai_deleet(Object *o) { // trigger counter if (o->hp < (1000 - DELEET_HP) && o->state < 2) { o->state = 2; o->timer = 0; o->frame = 2; o->flags |= FLAG_INVULNERABLE; sound(SND_CHEST_OPEN); } switch(o->state) { case 0: { o->state = 1; o->x += (TILE_W / 2) << CSF; o->y += (TILE_H / 2) << CSF; if (o->dir == LEFT) o->y += (8<<CSF); else o->x += (8<<CSF); } case 1: { if (o->shaketime) o->timer2++; else o->timer2 = 0; o->frame = (o->timer2 & 2) ? 1 : 0; } break; case 2: { int counter = -1; switch(o->timer) { case 0: counter = 0; break; // 5 case 50: counter = 1; break; // 4 case 100: counter = 2; break; // 3 case 150: counter = 3; break; // 2 case 200: counter = 4; break; // 1 case 250: { o->state = 3; o->sprite = SPR_BBOX_PUPPET_1; o->invisible = true; sprites[o->sprite].bbox.x1 = -48; sprites[o->sprite].bbox.x2 = 48; sprites[o->sprite].bbox.y1 = -48; sprites[o->sprite].bbox.y2 = 48; o->damage = 12; quake(10); SmokeXY(o->x, o->y, 40, 48, 48); o->flags &= ~FLAG_SHOOTABLE; o->flags &= ~FLAG_INVULNERABLE; if (o->dir == LEFT) { int x = (o->x >> CSF) / TILE_W; int y = ((o->y >> CSF) - 8) / TILE_H; map.tiles[x][y] = 0; map.tiles[x][y+1] = 0; } else { int x = ((o->x >> CSF) - 8) / TILE_W; int y = (o->y >> CSF) / TILE_H; map.tiles[x][y] = 0; map.tiles[x+1][y] = 0; } } break; } if (counter != -1) { CreateObject(o->x, o->y - (8<<CSF), \ OBJ_COUNTER_BOMB_NUMBER)->frame = counter; } o->timer++; }
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); }
// spawn a cloud of smoke centered around object o and starting within "range" distance. void SmokeClouds(Object *o, int nclouds, int rangex, int rangey, Object *push_behind) { SmokeXY(o->CenterX(), o->CenterY(), nclouds, rangex, rangey, push_behind); }
// handles his "looping" flight/rush attacks static void run_flight(Object *o) { switch(o->state) { // flying left or right case BP_FLY_LR: { o->state++; o->animtimer = 0; o->frame = 6; // flying horizontally o->yinertia = 0; o->damage = DMG_RUSH; FACEPLAYER; XMOVE(RUSH_SPEED); } case BP_FLY_LR+1: { ANIMATE(1, 6, 7); // smacked into wall? if ((o->blockl && o->dir == LEFT) || \ (o->blockr && o->dir == RIGHT)) { o->xinertia = 0; o->state = BP_HIT_WALL; o->damage = DMG_NORMAL; o->timer = 0; megaquake(10); } // reached player? // this has to be AFTER smacked-into-wall check for proper behavior // if player stands in spikes at far left/right of arena. if (pdistlx(RUSH_DIST)) o->state = BP_PREPARE_FLY_UD; } break; // smacked into wall while flying L/R case BP_HIT_WALL: { o->frame = 6; if (++o->timer > 30) { if (o->timer2 <= 3) o->state = BP_PREPARE_FLY_LR; else o->state = BP_RETURN_TO_GROUND; } } break; // flying up case BP_FLY_UP: { o->state++; o->timer = 0; o->animtimer = 0; o->frame = 8; // vertical flight o->dir = LEFT; // up-facing frame o->yinertia = -RUSH_SPEED; o->xinertia = 0; o->damage = DMG_RUSH; } case BP_FLY_UP+1: { ANIMATE(1, 8, 9); // hit ceiling? (to make this happen, break his loop and jump ABOVE him // while he is in the air, at the part where he would normally be // coming back down at you). if (o->blocku) { o->state = BP_HIT_CEILING; o->damage = DMG_NORMAL; o->timer = 0; SmokeXY(o->CenterX(), o->Top(), 8); megaquake(10); spawn_bones(o, UP); } // reached player? (this check here isn't exactly the same as pdistly; // it's important that it checks the player's top and not his center). if ((abs(player->y - o->y) < RUSH_DIST) && o->timer2 < 4) o->state = BP_PREPARE_FLY_LR; } break; case BP_HIT_CEILING: // hit ceiling { o->frame = 8; if (++o->timer > 30) { if (o->timer2 <= 3) o->state = BP_PREPARE_FLY_LR; else o->state = BP_RETURN_TO_GROUND; } } break; // flying down case BP_FLY_DOWN: { o->state++; o->timer = 0; o->animtimer = 0; o->frame = 8; // vertical flight o->dir = RIGHT; // down-facing frame o->yinertia = RUSH_SPEED; o->xinertia = 0; o->damage = DMG_RUSH; } case BP_FLY_DOWN+1: { ANIMATE(1, 8, 9); if (o->blockd) { o->state = BP_HIT_FLOOR; o->damage = DMG_NORMAL; o->timer = 0; SmokeXY(o->CenterX(), o->Bottom(), 8); megaquake(10); spawn_bones(o, DOWN); FACEPLAYER; } if (pdistly(RUSH_DIST) && o->timer2 < 4) o->state = BP_PREPARE_FLY_LR; } break; case BP_HIT_FLOOR: // hit floor { o->frame = 3; if (++o->timer > 30) { o->state = BP_FIGHTING_STANCE; o->timer = 120; } } break; // come back to ground while facing head on case BP_RETURN_TO_GROUND: { o->frame = 4; // face screen frame o->dir = LEFT; // non-flashing version o->state++; } case BP_RETURN_TO_GROUND+1: { ANIMATE(1, 4, 5); o->yinertia += 0x40; LIMITY(0x5ff); if (o->blockd && o->yinertia >= 0) { o->state++; o->timer = 0; o->frame = 3; // landed FACEPLAYER; } } break; case BP_RETURN_TO_GROUND+2: { o->xinertia *= 3; o->xinertia /= 4; if (++o->timer > 10) { o->state = BP_FIGHTING_STANCE; o->timer = 140; } } break; } }