void ai_ballos_skull(Object *o) { ANIMATE(8, 0, 3); switch(o->state) { case 0: { o->state = 100; o->frame = random(0, 16) & 3; } case 100: { o->yinertia += 0x40; LIMITY(0x700); if (o->timer++ & 2) { (SmokePuff(o->x, o->y))->PushBehind(o); } if (o->y > 0x10000) { o->flags &= ~FLAG_IGNORE_SOLID; if (o->blockd) { o->yinertia = -0x200; o->state = 110; o->flags |= FLAG_IGNORE_SOLID; quake(10, SND_BLOCK_DESTROY); for(int i=0;i<4;i++) { Object *s = SmokePuff(o->x + random(-12<<CSF, 12<<CSF), \ o->y + 0x2000); s->xinertia = random(-0x155, 0x155); s->yinertia = random(-0x600, 0); s->PushBehind(o); } } } } break; case 110: { o->yinertia += 0x40; if (o->Top() >= (map.ysize * TILE_H) << CSF) { o->Delete(); } } break; } }
void ai_proximity_press_vert(Object *o) { switch(o->state) { case 0: { if (pdistlx(8<<CSF) && pdistly2(8<<CSF, 128<<CSF) && \ !o->blockd) { o->state = 10; o->animtimer = 0; o->frame = 1; } } break; case 10: { if (o->frame < 2) ANIMATE_FWD(2); if (o->blockd) { if (o->frame >= 2) // make sure eye fully open { SmokeSide(o, 4, DOWN); quake(10); } o->flags |= FLAG_SOLID_BRICK; o->damage = 0; o->state = 11; o->frame = 0; } else { if (player->Top() > o->CenterY()) { o->flags &= ~FLAG_SOLID_BRICK; o->damage = 127; } else { o->flags |= FLAG_SOLID_BRICK; o->damage = 0; } } } break; } if (o->state >= 5) { o->yinertia += 0x80; LIMITY(0x5ff); } }
void ai_block_movev(Object *o) { int py = player->CenterY(); int objy = o->CenterY(); switch(o->state) { case 0: NX_LOG("ai_block_movev - state 0.\n"); o->flags |= FLAG_SOLID_BRICK; o->smushdamage = 100; o->dir = (o->dir == LEFT) ? UP : DOWN; o->state = (o->dir == DOWN) ? 10 : 20; break; case 10: // at top edge, ready to travel down NX_LOG("ai_block_movev - state 10.\n"); if (((py > objy) && (py - objy) < 0x32000) || \ ((py < objy) && (objy - py) < 0x3200)) { if (pdistlx(0x3200)) { o->state = 30; o->timer = 0; } } break; case 20: // at bottom edge, ready to travel up NX_LOG("ai_block_movev - state 20.\n"); if (((py > objy) && (py - objy) < 0x3200) || \ ((py < objy) && (objy - py) < 0x32000)) { if (pdistlx(0x3200)) { o->state = 30; o->timer = 0; } } break; case 30: // traveling NX_LOG("ai_block_movev - state 30.\n"); { YACCEL(0x20); LIMITY(0x200); // hit edge if ((o->dir == DOWN && o->blockd) || (o->dir == UP && o->blocku)) { SmokeSide(o, 4, o->dir); quake(10); o->yinertia = 0; o->dir ^= 1; o->state = (o->dir==DOWN) ? 10 : 20; } if ((++o->timer % 10) == 6) sound(SND_BLOCK_MOVE); } break; } }
void ai_block_moveh(Object *o) { int px = player->CenterX(); int objx = o->CenterX(); switch(o->state) { case 0: NX_LOG("ai_block_moveh - state 0.\n"); o->flags |= FLAG_SOLID_BRICK; o->smushdamage = 100; o->state = (o->dir == LEFT) ? 10:20; break; case 10: // at right edge, ready to travel left NX_LOG("ai_block_moveh - state 10.\n"); if (((px > objx) && (px - objx) < 0x3200) || \ ((px < objx) && (objx - px) < 0x32000)) { if (pdistly(0x3200)) { o->state = 30; o->timer = 0; } } break; case 20: // at left edge, ready to travel right NX_LOG("ai_block_moveh - state 20.\n"); if (((px > objx) && (px - objx) < 0x32000) || \ ((px < objx) && (objx - px) < 0x3200)) { if (pdistly(0x3200)) { o->state = 30; o->timer = 0; } } break; case 30: // traveling { NX_LOG("ai_block_moveh - state 30.\n"); XACCEL(0x20); LIMITX(0x200); // hit edge if ((o->dir == RIGHT && o->blockr) || (o->dir == LEFT && o->blockl)) { SmokeSide(o, 4, o->dir); quake(10); o->xinertia = 0; o->dir ^= 1; o->state = (o->dir==LEFT) ? 10 : 20; } if ((++o->timer % 10) == 6) sound(SND_BLOCK_MOVE); } break; } }
void ai_ma_pignon(Object *o) { /*debug("state: %d", o->state); debug("timer: %d", o->timer); debug("timer2: %d", o->timer2); debug("timer3: %d", o->timer3); debug("xinertia: %d", o->xinertia); debug("yinertia: %d", o->yinertia); debug("frame: %d", o->frame); */ switch(o->state) { case 0: { o->SnapToGround(); o->state = 1; } case 1: { FACEPLAYER; o->frame = 0; randblink(o); } break; case MP_Fight_Start: // set by script { o->state = MP_BaseState; o->timer = 0; o->timer2 = 0; o->flags |= FLAG_SHOOTABLE; } case MP_BaseState: { FACEPLAYER; o->damage = 1; o->frame = 0; if (++o->timer > 4) { o->timer = 0; o->state = MP_Jump; if (++o->timer3 > 12) { o->timer3 = 0; o->state = MP_CloneAttack; } } } break; case MP_Jump: // pause a moment and jump { o->frame = 2; if (++o->timer > 4) { o->state = MP_In_Air; o->frame = 3; o->xinertia = random(-0x400, 0x400); o->yinertia = -0x800; sound(SND_ENEMY_JUMP); o->timer2++; } } break; case MP_In_Air: // jumping or falling after clone attack { o->yinertia += 0x80; // for when falling back onscreen after clone attack if (o->y > (8 * TILE_H) << CSF) o->flags &= ~FLAG_IGNORE_SOLID; else o->blockd = false; // bounce off walls if ((o->blockl && o->xinertia < 0) || \ (o->blockr && o->xinertia > 0)) { o->xinertia = -o->xinertia; } FACEPLAYER; // select frame if (o->yinertia < -0x200) { o->frame = 3; } else if (o->yinertia > 0x200) { o->frame = 4; } else { o->frame = 0; } if (o->blockd && o->yinertia > 0) { o->state = MP_Landed; o->timer = 0; o->frame = 2; o->xinertia = 0; } if (o->timer2 > 4) { if (player->y < (o->y + 0x800)) { o->state = MP_ChargeAttack; o->timer = 0; o->xinertia = 0; o->yinertia = 0; } } } break; case MP_Landed: { o->frame = 2; if (++o->timer > 4) { o->state = MP_BaseState; } } break; case MP_ChargeAttack: // charge attack { o->frame = 5; if (++o->timer > 10) { o->state = MP_ChargeAttack+1; o->frame = 6; XMOVE(0x5ff); sound(SND_FUNNY_EXPLODE); o->flags &= ~FLAG_SHOOTABLE; o->flags |= FLAG_INVULNERABLE; o->damage = 10; } } break; case MP_ChargeAttack+1: // in-air during charge attack { ANIMATE(0, 6, 7); if ((o->xinertia < 0 && o->blockl) || \ (o->xinertia > 0 && o->blockr)) { o->state = MP_Hit_Wall; } } break; case MP_Hit_Wall: // hit wall { o->state++; o->timer = 0; quake(16); } case MP_Hit_Wall+1: { o->damage = 4; ANIMATE(0, 6, 7); if ((++o->timer % 6) == 0) { int x = (random(4, 16) * TILE_W) << CSF; CreateObject(x, (16 << CSF), OBJ_MA_PIGNON_ROCK); } if (o->timer > 30) { o->timer2 = 0; o->state = MP_In_Air; o->flags |= FLAG_SHOOTABLE; o->flags &= ~FLAG_INVULNERABLE; o->damage = 3; } } break; case MP_CloneAttack: // begin clone-attack sequence { o->state++; o->frame = 9; FACEPLAYER; } case MP_CloneAttack+1: // walk at player before attack { ANIMATE(0, 9, 11); XMOVE(0x400); if (pdistlx(3 << CSF)) { o->state = MP_Fly_Up; o->timer = 0; o->frame = 2; o->xinertia = 0; } } break; case MP_Fly_Up: // jump and fly up for clone attack { o->frame = 2; if (++o->timer > 4) { o->state++; o->frame = 12; o->yinertia = -0x800; sound(SND_FUNNY_EXPLODE); o->flags |= FLAG_IGNORE_SOLID; o->flags &= ~FLAG_SHOOTABLE; o->flags |= FLAG_INVULNERABLE; o->damage = 10; } } break; case MP_Fly_Up+1: // flying up { ANIMATE(0, 12, 13); if (o->y < (16<<CSF)) o->state = MP_Spawn_Clones; } break; case MP_Spawn_Clones: // offscreen, spawning clones { o->yinertia = 0; o->state++; o->timer = 0; quake(10); } case MP_Spawn_Clones+1: { ANIMATE(0, 12, 13); if ((++o->timer % 6) == 0) { int x = (random(4, 16) * TILE_W) << CSF; CreateObject(x, (16 << CSF), OBJ_MA_PIGNON_CLONE); } if (o->timer > 30) { o->timer2 = 0; o->state = MP_In_Air; // fall back down to ground o->flags |= FLAG_SHOOTABLE; o->flags &= ~FLAG_INVULNERABLE; } } break; case MP_Defeated: // defeated -- set by script { KillObjectsOfType(OBJ_MA_PIGNON_CLONE); o->flags &= ~FLAG_SHOOTABLE; o->state++; o->timer = 0; o->frame = 8; o->damage = 0; } case MP_Defeated+1: { o->yinertia += 0x20; if (o->blockd) { o->xinertia *= 7; o->xinertia /= 8; } o->display_xoff = (++o->timer & 1); } break; } // ma pignon is invulnerable to missiles and Blade. if (o->state >= MP_Fight_Start && o->state < MP_Defeated) { // ....he's invulnerable anyway during these two states so don't mess with that. if (o->state != MP_ChargeAttack+1 && o->state != MP_Fly_Up+1) { bool found_weapons = false; if (o->type != OBJ_MA_PIGNON_CLONE) { Object *c; FOREACH_OBJECT(c) { if (c->type == OBJ_MISSILE_SHOT || \ c->type == OBJ_SUPERMISSILE_SHOT || \ c->type == OBJ_MISSILE_BOOM_SPAWNER || \ c->type == OBJ_BLADE12_SHOT || \ c->type == OBJ_BLADE3_SHOT || \ c->type == OBJ_BLADE_SLASH) { found_weapons = true; break; } } } if (found_weapons) { o->flags &= ~FLAG_SHOOTABLE; o->flags |= FLAG_INVULNERABLE; } else { o->flags |= FLAG_SHOOTABLE; o->flags &= ~FLAG_INVULNERABLE; } }
// This object is responsible for collapsing the walls in the final best-ending sequence. // All the original object does is collapse one tile further every 101 frames. // However, since it's triggered at the beginning of the cinematic and then is let to run // through almost the entire thing it needs to be sync'd really-really perfect with a // number of other systems; the textboxes, etc. // // I spent several hours trying to get my events to run in perfect frame-by-frame // exactness with the original engine, and found several things that were slightly off. // However, I've decided that even if I got it absolutely perfect, it's too liable to // get broken by some minor innocent change in the future, and requires too much of // the engine to be tuned just so. // // So, I've added some event-based triggers to the object, that are NOT technically supposed // to be there. These will make extra sure that nothing embarrassing happens during this great // finale, such as the walls being one tile too far at one point, or even worse, having // them collapse onto Balrog before he makes it to the exit. Because there are no triggers // in the script and I can't change the script, I had to do a bit of sneaky spying on program // state to implement them. void ai_wall_collapser(Object *o) { int y; switch(o->state) { case 0: { o->invisible = true; o->timer = 0; o->state = 1; } break; case 10: // trigger { if (++o->timer > 100) { o->timer2++; o->timer = 0; int xa = (o->x >> CSF) / TILE_W; int ya = (o->y >> CSF) / TILE_H; for(y=0;y<20;y++) { // pushing the smoke behind all objects prevents it from covering // up the NPC's on the collapse just before takeoff. map_ChangeTileWithSmoke(xa, ya+y, 109, 4, false, lowestobject); } sound(SND_BLOCK_DESTROY); quake(20); if (o->dir == LEFT) o->x -= (TILE_W << CSF); else o->x += (TILE_W << CSF); // reached the solid tile in the center of the throne. // it isn't supposed to cover this tile until after Curly // says we're gonna get crushed. if (o->timer2 == 6) o->state = 20; // balrog is about to take off/rescue you. if (o->timer2 == 9) o->state = 30; } } break; // "gonna get crushed" event case 20: { // wait for text to come up if (textbox.IsVisible()) o->state = 21; } break; case 21: { // wait for text to dismiss, then tile immediately collapses if (!textbox.IsVisible()) { o->state = 10; o->timer = 1000; } } break; // balrog is about to take off. the video I took shows that // the walls are supposed to collapse into your space on the // exact same frame that he breaks the first ceiling tile. case 30: { o->linkedobject = Objects::FindByType(OBJ_BALROG_DROP_IN); if (o->linkedobject) o->state = 31; } break; case 31: { //debug("%x", o->linkedobject->y); if (o->linkedobject && o->linkedobject->y <= 0x45800) { o->state = 10; o->timer = 1000; } } break; }
void ai_balrog(Object *o) { bool fall = true; // he is greenish when he first appears in Gum Room if (DoesCurrentStageUseSpriteset(NPCSET_FROG)) o->sprite = SPR_BALROG_GREEN; switch(o->state) { case 0: { o->flags &= ~FLAG_IGNORE_SOLID; o->xinertia = 0; o->balrog.smoking = false; o->frame = 0; randblink(o, 4, 8); } break; case 10: // he jumps and flys away o->xinertia = 0; o->frame = 2; o->timer = 0; o->state++; case 11: { if (++o->timer <= 20) break; o->state++; o->yinertia = -0x800; o->flags |= FLAG_IGNORE_SOLID; } case 12: { fall = false; o->frame = 3; o->yinertia -= 0x10; if (o->y < 0) { o->Delete(); sound(SND_QUAKE); game.quaketime = 30; } } break; // he looks shocked and shakes, then flys away // used when he is "hit by something" case 20: { o->state = 21; o->frame = 5; o->xinertia = 0; o->timer = o->timer2 = 0; SmokeClouds(o, 4, 8, 8); sound(SND_BIG_CRASH); o->balrog.smoking = 1; } case 21: { o->timer2++; o->x += ((o->timer2 >> 1) & 1) ? (1<<CSF) : -(1<<CSF); if (++o->timer > 100) o->state = 10; o->yinertia += 0x20; LIMITY(0x5ff); } break; case 30: // he smiles for a moment o->frame = 6; o->timer = 0; o->state = 31; case 31: if (++o->timer > 100) o->state = o->frame = 0; break; // flashing white (spell casted on him) // this only works in Gum Room before balfrog fight, as the normal // non-greenish spritesheet doesn't include the required frame. case 40: o->state = 41; o->animtimer = 0; o->animframe = 0; case 41: { static const int flash_seq[] = { 5, 7 }; o->animate_seq(1, flash_seq, 2); } break; case 42: o->timer = 0; o->state = 43; case 43: // flashing visibility // (transforming into Balfrog stage boss; // our flashing is interlaced with his) o->timer++; o->invisible = (o->timer & 2) ? false : true; break; case 50: // he faces away o->frame = 8; o->xinertia = 0; break; case 60: // he walks o->state = 61; balrog_walk_init(o); case 61: { balrog_walk_animation(o); XMOVE(0x200); } break; // he is teleported away (looking distressed) // this is when he is sent to Labyrinth at end of Sand Zone case 70: o->xinertia = 0; o->timer = 0; o->frame = 7; o->state++; case 71: if (DoTeleportOut(o, 2)) o->Delete(); break; case 80: // hands up and shakes o->frame = 5; o->state = 81; case 81: { if (++o->timer & 2) o->x += (1 << CSF); else o->x -= (1 << CSF); } break; // fly up and lift Curly & PNPC // (post-Ballos ending scene) case 100: { o->state = 101; o->timer = 0; o->frame = 2; // prepare for jump } case 101: { if (++o->timer > 20) { o->state = 102; o->timer = 0; o->frame = 3; // fly up DeleteObjectsOfType(OBJ_NPC_PLAYER); DeleteObjectsOfType(OBJ_CURLY); CreateObject(0, 0, OBJ_BALROG_PASSENGER, 0, 0, LEFT)->linkedobject = o; CreateObject(0, 0, OBJ_BALROG_PASSENGER, 0, 0, RIGHT)->linkedobject = o; o->yinertia = -0x800; o->flags |= FLAG_IGNORE_SOLID; // so can fly through ceiling fall = false; } } break; case 102: // flying up during escape seq { fall = false; // bust through ceiling int y = ((o->y + (4<<CSF)) >> CSF) / TILE_H; if (y < 35 && y >= 0) { int x = (o->CenterX() >> CSF) / TILE_W; if (map.tiles[x][y] != 0) { // smoke needs to go at the bottom of z-order or you can't // see any of the characters through all the smoke. map_ChangeTileWithSmoke(x, y, 0, 4, false, lowestobject); map_ChangeTileWithSmoke(x-1, y, 0, 4, false, lowestobject); map_ChangeTileWithSmoke(x+1, y, 0, 4, false, lowestobject); megaquake(10, 0); sound(SND_MISSILE_HIT); } } if (o->Bottom() < -(20<<CSF)) { quake(30, 0); o->Delete(); } } break; case 500: // used during Balfrog death scene { fall = false; } break; }
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); }
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); }
// Kulala void ai_giant_jelly(Object *o) { switch(o->state) { case 0: // frozen/in stasis. waiting for player to shoot. o->frame = 4; if (o->shaketime) { quake(30); o->state = 10; o->frame = 0; o->timer = 0; } break; case 10: // falling { o->flags |= FLAG_SHOOTABLE; o->flags &= ~FLAG_INVULNERABLE; if (++o->timer > 40) { o->timer = 0; o->animtimer = 0; o->state = 11; } } break; case 11: // animate thrust { ANIMATE_FWD(5); if (o->frame >= 3) { o->frame = 3; o->state = 12; } } break; case 12: // thrusting upwards { o->yinertia = -0x155; if (++o->timer > 20) { o->state = 10; o->frame = 0; o->timer = 0; } } break; case 20: // shot/freeze over/go invulnerable { o->frame = 4; o->xinertia >>= 1; o->yinertia += 0x20; if (!o->shaketime) { o->state = 10; o->frame = 0; o->timer = 30; } } break; } if (o->shaketime) { if (++o->timer3 > 12) { o->state = 20; o->frame = 4; o->flags &= ~FLAG_SHOOTABLE; o->flags |= FLAG_INVULNERABLE; } } else { o->timer3 = 0; } if (o->state >= 10) { if (o->blockl) { o->timer2 = 50; o->dir = RIGHT; } if (o->blockr) { o->timer2 = 50; o->dir = LEFT; } if (o->timer2 > 0) { o->timer2--; XACCEL(0x80); } else { o->timer2 = 50; FACEPLAYER; } o->yinertia += 0x10; if (o->blockd) o->yinertia = -0x300; } LIMITX(0x100); LIMITY(0x300); }
void ai_balrog_boss_running(Object *o) { // try to catch player if (o->state == STATE_CHARGE+1 || o->state == STATE_JUMP) { if ((pdistlx(12 << CSF) && pdistly(8 << CSF)) && o->timer > 8) { hurtplayer(2); balrog_grab_player(o); o->state = STATE_CAUGHT_PLAYER; } } switch(o->state) { case 0: { FACEPLAYER; o->flags |= FLAG_SHOOTABLE; o->frame = 0; o->state = 1; } case 1: { if (++o->timer > 30) { o->state = STATE_CHARGE; o->timer2++; } } break; // running towards player case STATE_CHARGE: { o->state++; o->timer = 0; o->frame = 9; o->animtimer = 0; } case STATE_CHARGE+1: { XACCEL(0x10); walking_animation(o); if (++o->timer > 75 || \ (o->dir == LEFT && o->blockl) || (o->dir == RIGHT && o->blockr)) { o->frame = 0; o->state = STATE_SLOW_DOWN; break; } // can jump every 3rd time, but if he catches the player // before he gets a chance to he does NOT jump on the next charge. if ((o->timer2 & 3) == 0) { if (o->timer > 25) { // initiate jump o->frame = 3; o->yinertia = -0x400; o->state = STATE_JUMP; } } } break; // jumping case STATE_JUMP: { if (o->blockd && o->yinertia >= 0) { o->frame = 2; // <-- Landed frame. quake(30); o->state = STATE_SLOW_DOWN; } } break; // slowing down after charging or jumping case STATE_SLOW_DOWN: { o->xinertia *= 4; o->xinertia /= 5; if (o->xinertia == 0) o->state = 0; } break; // caught player case STATE_CAUGHT_PLAYER: { if (balrog_toss_player_away(o)) o->state = 0; } break; } o->yinertia += 0x20; LIMITX(0x400); LIMITY(0x5FF); }