void ai_bat_circle(Object *o) { switch(o->state) { case 0: { uint8_t angle; o->state = 1; // set up initial direction and target x,y angle = random(0, 255); o->xinertia = sin_table[angle]; angle += 64; o->xmark = (o->x + (sin_table[angle] * 8)); angle = random(0, 255); o->yinertia = sin_table[angle]; angle += 64; o->ymark = (o->y + (sin_table[angle] * 8)); } case 1: // circle around our target point ANIMATE(1, 2, 4); FACEPLAYER; o->xinertia += (o->x > o->xmark) ? -0x10 : 0x10; o->yinertia += (o->y > o->ymark) ? -0x10 : 0x10; LIMITX(0x200); LIMITY(0x200); if (!o->timer2) { if (pdistlx(0x1000) && (player->y > o->y) && pdistly(0xC000)) { // dive attack o->xinertia /= 2; o->yinertia = 0; o->state = 2; o->frame = 5; // mouth showing teeth } } else o->timer2--; break; case 2: // dive attack o->yinertia += 0x40; LIMITY(0x5ff); if (o->blockd) { o->yinertia = 0; o->xinertia *= 2; o->timer2 = 120; // delay before can dive again o->state = 1; } break; } }
void ai_firewhirr(Object *o) { Object *shot; FACEPLAYER; switch(o->state) { case 0: o->state = 1; o->timer = random(0, 50); o->ymark = o->y; //o->yinertia = -0x200; case 1: if (!o->timer) { o->state = 10; o->yinertia = -0x200; } else o->timer--; case 10: o->frame ^= 1; o->yinertia += (o->y < o->ymark) ? 0x10 : -0x10; LIMITY(0x200); // inc time-to-fire while player near if (pdistly(80 << CSF)) { if (o->dir==LEFT && player->x < o->x && pdistlx(160 << CSF)) o->timer2++; if (o->dir==RIGHT && player->x > o->x && pdistlx(160 << CSF)) o->timer2++; } // if time to fire, spawn a shot if (o->timer2 > 20) { shot = SpawnObjectAtActionPoint(o, OBJ_FIREWHIRR_SHOT); o->timer2 = -100 + random(0, 20); // tell Curly to acquire us as a target if (o->onscreen) { o->CurlyTargetHere(); } } 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_bute_archer_red(Object *o) { //DebugCrosshair(o->x, o->y, 0, 255, 255); switch(o->state) { case 0: { o->state = 1; o->xmark = o->x; o->ymark = o->y; if (o->dir == LEFT) o->xmark -= (128<<CSF); else o->xmark += (128<<CSF); o->xinertia = random(-0x400, 0x400); o->yinertia = random(-0x400, 0x400); } case 1: // come on screen { ANIMATE(1, 0, 1); if ((o->dir == LEFT && o->x < o->xmark) || \ (o->dir == RIGHT && o->x > o->xmark)) { o->state = 20; } } break; case 20: // aiming { o->state = 21; o->timer = random(0, 150); o->frame = 2; o->animtimer = 0; } case 21: { ANIMATE(2, 2, 3); if (++o->timer > 300 || \ (pdistlx(112<<CSF) && pdistly(16<<CSF))) { o->state = 30; } } break; case 30: // flashing to fire { o->state = 31; o->timer = 0; o->animtimer = 0; o->frame = 3; } case 31: { ANIMATE(1, 3, 4); if (++o->timer > 30) { o->state = 40; o->frame = 5; Object *arrow = CreateObject(o->x, o->y, OBJ_BUTE_ARROW); arrow->dir = o->dir; arrow->xinertia = (o->dir == RIGHT) ? 0x800 : -0x800; } } break; case 40: // fired { o->state = 41; o->timer = 0; o->animtimer = 0; } case 41: { ANIMATE(2, 5, 6); if (++o->timer > 40) { o->state = 50; o->timer = 0; o->xinertia = 0; o->yinertia = 0; } } break; case 50: // retreat offscreen { ANIMATE(1, 0, 1); XACCEL(-0x20); if (o->Right() < 0 || o->Left() > ((map.xsize * TILE_W) << CSF)) o->Delete(); } break; } // sinusoidal hover around set point if (o->state != 50) { o->xinertia += (o->x < o->xmark) ? 0x2A : -0x2A; o->yinertia += (o->y < o->ymark) ? 0x2A : -0x2A; LIMITX(0x400); LIMITY(0x400); } }
void ai_mesa(Object *o) { if (run_bute_defeated(o, MESA_HP)) return; switch(o->state) { case 0: { o->y += (4<<CSF); o->state = 1; } case 1: { ANIMATE(40, 0, 1); FACEPLAYER; if (pdistlx(320<<CSF) && pdistly(160<<CSF)) { if (++o->timer > 50) o->state = 10; } } break; case 10: { o->state = 11; o->timer = 0; o->frame = 2; // hand down int x = o->x + ((o->dir == LEFT) ? (7<<CSF) : -(7<<CSF)); int y = o->y + (10<<CSF); o->linkedobject = CreateObject(x, y, OBJ_MESA_BLOCK); o->linkedobject->linkedobject = o; } case 11: { if (++o->timer > 50) { o->state = 20; o->timer = 0; o->frame = 3; // hand up, throwing if (o->linkedobject) { Object *&block = o->linkedobject; block->y = (o->y - (4<<CSF)); block->xinertia = (o->dir == RIGHT) ? 0x400 : -0x400; block->yinertia = -0x400; block->state = 1; sound(SND_EM_FIRE); block->linkedobject = NULL; o->linkedobject = NULL; } } } break; case 20: { if (++o->timer > 20) { // throw again, if player still near o->state = 1; o->timer = 0; } } break; } o->yinertia += 0x55; LIMITY(0x5ff); }
void ai_bute_archer(Object *o) { if (run_bute_defeated(o, BUTE_HP)) return; switch(o->state) { case 0: // waiting for player (when haven't seen him yet) { if ((o->dir == LEFT && player->CenterX() < o->CenterX()) || \ (o->dir == RIGHT && player->CenterX() > o->CenterX())) { if (pdistlx(320<<CSF) && pdistly(160<<CSF)) { o->state = 10; } } } break; // aiming--can track player here for a brief period case 10: { FACEPLAYER; if (!pdistlx(224<<CSF) || player->y <= (o->y - (8<<CSF))) { o->frame = 4; // shooting up o->timer2 = 1; } else { o->frame = 1; // shooting straight o->timer2 = 0; } if (++o->timer > 10) { o->state = 20; o->timer = 0; } } break; // flashing to fire case 20: { if (o->timer2 == 0) ANIMATE(0, 1, 2) else ANIMATE(0, 4, 5) if (++o->timer > 30) o->state = 30; } break; // fire case 30: { o->state = 31; o->timer = 0; Object *arrow = CreateObject(o->CenterX(), o->CenterY(), OBJ_BUTE_ARROW); arrow->xinertia = (o->dir == RIGHT) ? 0x600 : -0x600; if (o->timer2 == 1) // shooting up arrow->yinertia = -0x600; // frame: arrow away o->frame = (o->timer2 == 1) ? 6 : 3; } case 31: { if (++o->timer > 30) { o->state = 40; o->frame = 0; o->timer = random(50, 150); } } break; // after fire, and the "woken up" waiting-for-player state case 40: { if (pdistlx(352<<CSF) && pdistly(240<<CSF)) { if (--o->timer < 0) o->state = 10; // fire again } else { // player got away, do nothing until he returns o->timer = 150; } } break; } }
void ai_critter_hopping_red(Object *o) { switch(o->state) { case 0: { FACEPLAYER; o->frame = 0; if (o->shaketime) { o->state = 2; o->timer = 0; } else if (++o->timer >= 8) { if (pdistly((5 * TILE_H) << CSF)) { if (pdistlx((6 * TILE_W) << CSF)) { o->state = 1; o->timer = 0; } else if (pdistlx((9 * TILE_W) << CSF)) { o->frame = 1; } } } } break; case 1: // prepare to jump { if (++o->timer > 8) { o->state = 2; o->frame = 2; sound(SND_ENEMY_JUMP); o->yinertia = -0x5ff; o->xinertia = (o->dir == RIGHT) ? 0x200 : -0x200; } } break; case 2: // in air { if (o->blockd && o->yinertia > 0) { sound(SND_THUD); o->xinertia = 0; o->state = 0; o->timer = 0; o->frame = 1; } } break; } o->yinertia += 0x55; LIMITY(0x5ff); }
void ai_buyobuyo(Object *o) { bool deleteme = false; ANIMATE(6, 0, 1); switch(o->state) { case 0: { // shoot up down at player... o->yinertia = (o->dir == LEFT) ? -0x600 : 0x600; o->state = 1; o->timer = 0; } case 1: { o->timer++; // inc fly time // reached height of player yet? if (pdistly(0x2000)) { o->state = 2; ai_buyobuyo(o); return; } } break; case 2: { // this slight "minimum fly time" keeps the underwater ones from // smacking into the floor if the player is underwater with them if (++o->timer > 3) { FACEPLAYER; o->xmark = o->x; o->ymark = o->y; o->xinertia = random(0, 1) ? 0x200 : -0x200; o->yinertia = random(0, 1) ? 0x200 : -0x200; o->state = 3; } } break; case 3: { if (o->x > o->xmark) o->xinertia -= 0x20; if (o->x < o->xmark) o->xinertia += 0x20; if (o->y > o->ymark) o->yinertia -= 0x20; if (o->y < o->ymark) o->yinertia += 0x20; LIMITX(0x400); LIMITY(0x400); // move the point we are bobbling around o->xmark += (o->dir == LEFT) ? -(1 << CSF) : (1 << CSF); //debugVline(o->xmark, 0, 0xff, 0); if (++o->timer > 300) deleteme = true; } break; } if ((o->blockl && o->xinertia < 0) || \ (o->blockr && o->xinertia > 0) || \ (o->blocku && o->yinertia < 0) || \ (o->blockd && o->yinertia > 0)) { deleteme = true; } if (deleteme) { effect(o->CenterX(), o->CenterY(), EFFECT_STARPOOF); o->Delete(); return; } }
// 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; } }
// there is an apparent bug on the sprite sheet for this monster: // right-facing frame 1 is a duplicate of frame 0, // so the mouth-twitch animation does not work when // the frog is facing right. void ai_frog(Object *o) { switch(o->state) { case 0: { o->timer = 0; o->xinertia = 0; o->yinertia = 0; // non-normal dirs are used to indicate that this frog was // spawned by balfrog: we are falling out of ceiling during fight if (o->dir != LEFT && o->dir != RIGHT) { o->dir = random(0, 1) ? LEFT : RIGHT; o->flags |= FLAG_IGNORE_SOLID; o->state = 3; o->frame = 2; } else { o->flags &= ~FLAG_IGNORE_SOLID; o->state = 1; } } case 1: // standing { o->timer++; if (!random(0, 50)) { // mouth-flitter animation o->state = 2; o->frame = 0; o->timer = 0; o->animtimer = 0; } } break; case 2: // mouth flitter { ANIMATE(2, 0, 1); if (++o->timer > 18) o->state = 1; } break; case 3: // falling out of ceiling during balfrog fight { if (++o->timer > 40) { o->flags &= ~FLAG_IGNORE_SOLID; if (o->blockd) { o->state = 0; o->frame = 0; o->timer = 0; } } } break; case 10: // jumping case 11: { if (o->blockl && o->xinertia < 0) { o->dir = RIGHT; o->xinertia = -o->xinertia; } if (o->blockr && o->xinertia > 0) { o->dir = LEFT; o->xinertia = -o->xinertia; } if (o->blockd) { o->state = 0; o->frame = 0; o->timer = 0; } } break; } // random jumping, and jump when shot if (o->state < 3 && o->timer > 10) { bool dojump = false; if (o->shaketime) { dojump = true; } else if (pdistlx(0x14000) && pdistly(0x8000)) { if (!random(0, 50)) { dojump = true; } } if (dojump) { FACEPLAYER; o->state = 10; o->frame = 2; o->yinertia = -0x5ff; // no jumping sound in cutscenes at ending if (!player->inputs_locked && !player->disabled) sound(SND_ENEMY_JUMP); XMOVE(0x200); } } o->yinertia += 0x80; LIMITY(0x5ff); }
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); }