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_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_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_falling_spike_small(Object *o) { switch(o->state) { case 0: { o->xmark = o->x; if (pdistlx(12 << CSF)) o->state = 1; } break; case 1: // shaking { if (++o->animtimer >= 12) o->animtimer = 0; o->x = o->xmark; if (o->animtimer >= 6) o->x += (1 << CSF); if (++o->timer > 30) { o->state = 2; // fall o->frame = 1; // slightly brighter frame at top } } break; case 2: // falling { o->yinertia += 0x20; LIMITY(0xC00); if (o->blockd) { if (!player->inputs_locked) // no sound in ending cutscene sound(SND_BLOCK_DESTROY); SmokeClouds(o, 4, 2, 2); effect(o->CenterX(), o->CenterY(), EFFECT_BOOMFLASH); o->Delete(); } } break; } }
void ai_misery_ball(Object *o) { switch (o->state) { case 0: { o->state = 1; o->ymark = o->y; o->xinertia = 0; o->yinertia = -0x200; } case 1: { ANIMATE(2, 0, 1); o->xinertia += (o->x < player->x) ? 0x10 : -0x10; o->yinertia += (o->y < o->ymark) ? 0x20 : -0x20; LIMITX(0x200); LIMITY(0x200); if (pdistlx(8 * CSFI) && player->y > o->y) { o->state = 10; o->timer = 0; } } break; case 10: // black lightning { if (++o->timer > 10) { NXE::Sound::SoundManager::getInstance()->playSfx(NXE::Sound::SFX::SND_LIGHTNING_STRIKE); CreateObject(o->x, o->y, OBJ_BLACK_LIGHTNING); o->Delete(); } o->frame = (o->timer & 2) ? 2 : 1; } break; } }
void ai_press_vert(Object *o) { switch(o->state) { case 0: { o->state = 1; o->y -= (4 << CSF); if (pdistlx((8<<CSF)) && pdistly2((8<<CSF), (128<<CSF))) { o->state = 5; } } break; case 5: { if (o->blockd) { o->state = 10; o->animtimer = 0; o->frame = 1; } } break; case 10: { ANIMATE_FWD(2); if (o->frame > 2) o->frame = 2; if (player->y > o->y) o->flags |= FLAG_SOLID_BRICK; } break; } }
void ai_falling_spike_large(Object *o) { switch(o->state) { case 0: { o->xmark = o->x; if (pdistlx(12 << CSF)) o->state = 1; } break; case 1: // shaking { if (++o->animtimer >= 12) o->animtimer = 0; o->x = o->xmark; if (o->animtimer >= 6) // scuttle:: big spikes shake in the other direction o->x -= (1 << CSF); if (++o->timer > 30) { o->state = 2; // fall o->frame = 1; // slightly brighter frame at top } } break; case 2: // falling { o->yinertia += 0x20; LIMITY(0xC00); if (o->Bottom() < player->Bottom()) { // could fall on player o->flags &= ~FLAG_SOLID_BRICK; o->damage = 127; // ouch! } else { // player could only touch side from this position o->flags |= FLAG_SOLID_BRICK; o->damage = 0; } // damage NPC's as well (it kills that one Dragon Zombie) Object *enemy; FOREACH_OBJECT(enemy) { if ((enemy->flags & FLAG_SHOOTABLE) && \ o->Bottom() >= enemy->CenterY() && hitdetect(o, enemy)) { if (!(enemy->flags & FLAG_INVULNERABLE)) enemy->DealDamage(127); } } if (++o->timer > 8 && o->blockd) { o->flags |= FLAG_SOLID_BRICK; o->damage = 0; o->yinertia = 0; o->state = 3; // fall complete o->timer = 0; sound(SND_BLOCK_DESTROY); SmokeClouds(o, 4, 2, 2); effect(o->CenterX(), o->y + (sprites[o->sprite].block_d[0].y << CSF), EFFECT_STARSOLID); } } break; case 3: // hit ground { if (++o->timer > 4) { // make it destroyable o->flags |= FLAG_SHOOTABLE; o->flags &= ~FLAG_INVULNERABLE; o->state = 4; } } break; } }
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_bat_hang(Object *o) { switch(o->state) { case 0: o->state = 1; case 1: // hanging and waiting if (!random(0, 100)) { o->state = 2; o->timer = 0; o->frame = 1; } if (pdistlx(0x1000) && pdistly2(0x1000, 0x9000)) { o->frame = 0; o->state = 3; } break; case 2: // blinking if (++o->timer > 8) { o->state = 1; o->frame = 0; } break; case 3: // at attention o->frame = 0; if (o->shaketime || pdistlx(0x2800)) { o->frame = 1; o->animtimer = 0; o->state = 4; o->timer = 0; } break; case 4: // falling o->yinertia += 0x20; LIMITY(0x5ff); o->timer++; if (o->timer > 20 || o->blockd) { if (o->blockd || ((player->y - 0x2000) < o->y)) { // start flying o->animtimer = 0; o->frame = 2; o->state = 5; o->ymark = o->y; if (o->blockd) o->yinertia = -0x200; } } break; case 5: // flying ANIMATE(1, 2, 4); FACEPLAYER; o->xinertia += (o->x > player->x) ? -0x20 : 0x20; o->yinertia += (o->y > o->ymark) ? -0x10 : 0x10; LIMITX(0x200); LIMITY(0x200); if (o->blockd) o->yinertia = -0x200; if (o->blockr) o->yinertia = 0x200; 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); } }
// regular NPC curly void ai_curly(Object *o) { switch(o->state) { case 0: // state 0: stand and do nothing o->frame = 0; o->flags |= FLAG_SCRIPTONACTIVATE; // needed for after Almond battle case 1: // important that state 1 does not change look-away frame for Drain cutscene if (o->frame != 12) o->frame = 0; o->xinertia = 0; break; case 3: // state 3: walk forward case 10: // state 10: walk to player and stop { if (o->state == 10) FACEPLAYER; o->state++; o->animtimer = 0; o->frame = 0; } case 4: case 11: { if (o->state == 11 && pdistlx(20<<CSF)) { o->state = 0; break; } ANIMATE(5, 0, 3); if (!o->blockd) o->frame = 3; XMOVE(0x200); } break; // state 5: curly makes a "kaboom", then looks sad. case 5: o->state = 6; SmokeClouds(o, 8, 0, 0); case 6: o->frame = 16; break; case 20: // face away o->xinertia = 0; o->frame = 12; break; case 21: // look up o->xinertia = 0; o->frame = 4; break; case 30: // state 30: curly goes flying through the air and is knocked out { o->state = 31; o->frame = 14; o->timer2 = 0; o->yinertia = -0x400; XMOVE(-0x200); } case 31: { if (o->blockd && o->yinertia >= 0) o->state = 32; else break; } case 32: // state 32: curly is laying knocked out { o->frame = 15; o->xinertia = 0; } break; // walk backwards from collapsing wall during final cutscene case 70: { o->state = 71; o->timer = 0; o->frame = 1; o->animtimer = 0; } case 71: { XMOVE(-0x100); ANIMATE(8, 0, 3); } break; } o->yinertia += 0x40; LIMITY(0x5ff); }
void ai_buyobuyo_base(Object *o) { /*if (o->onscreen) { debug("state: %d", o->state); debug("timer: %d", o->timer); debug("timer2: %d", o->timer2); }*/ if (o->state < 3 && o->hp < (1000 - BUYOBUYO_BASE_HP)) { SmokeClouds(o, objprop[o->type].death_smoke_amt, 8, 8); effect(o->CenterX(), o->CenterY(), EFFECT_BOOMFLASH); o->SpawnPowerups(); o->flags &= ~FLAG_SHOOTABLE; o->damage = 0; o->state = 10; o->frame = 2; } switch(o->state) { case 0: { // ceiling has different bounding box and action point if (o->dir == RIGHT) o->sprite = SPR_BUYOBUYO_BASE_CEILING; o->state = 1; o->timer = 10; } case 1: { if (pdistlx(0x14000)) { if ((o->dir == LEFT && pdistly2(0x14000, 0x2000)) || \ (o->dir == RIGHT && pdistly2(0x2000, 0x14000))) { if (--o->timer < 0) { o->state = 2; o->timer = 0; o->animtimer = 0; } } } } break; case 2: { ANIMATE(3, 0, 1); if (++o->timer > 10) { Object *buyo = SpawnObjectAtActionPoint(o, OBJ_BUYOBUYO); buyo->dir = o->dir; sound(SND_EM_FIRE); o->frame = 0; o->CurlyTargetHere(); // cyclic: three firings then pause o->state = 1; if (++o->timer2 > 2) { o->timer = 100; o->timer2 = 0; } else { o->timer = 20; } } } break; } }
// 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_critter(Object *o) { switch(o->state) { case 0: { if (o->type == OBJ_POWER_CRITTER) { // altered physics for Power Critter o->critter.jumpheight = 0x2800; o->critter.jumpgrav = 0x1C; o->critter.falldmg = 12; } else { o->critter.jumpheight = 0; o->critter.jumpgrav = 0x40; o->critter.falldmg = 3; if (o->type == OBJ_CRITTER_HOPPING_BLUE || // first cave o->type == OBJ_CRITTER_HOPPING_GREEN || // egg 1 o->type == OBJ_CRITTER_HOPPING_AQUA || // egg 2 o->type == OBJ_CRITTER_HOPPING_RED) // last cave { o->critter.canfly = false; // critters in egg1 only 2 dmg if (o->type == OBJ_CRITTER_HOPPING_GREEN) o->critter.falldmg = 2; // critters in First Cave don't jump as high if (o->type != OBJ_CRITTER_HOPPING_BLUE) { o->critter.jumpgrav = 0x2C; } } else { // critters are purple in Maze o->sprite = SPR_CRITTER_FLYING_CYAN; o->critter.canfly = true; } } o->state = 1; } //fall thru case 1: { o->frame = 0; if (o->timer >= 8) { int attack_dist = (o->critter.canfly) ? (96 << CSF) : (64 << CSF); // close enough to attack? if (pdistlx(attack_dist) && pdistly2(96<<CSF, 48<<CSF)) { o->state = 2; o->frame = 0; o->timer = 0; } else if (pdistlx(attack_dist + (32<<CSF)) && pdistly2(128<<CSF, 48<<CSF)) { // no, but close enough to look up at "attention" and watch player FACEPLAYER; o->frame = 1; o->timer = 8; // reset timer to stop watching } else { // once a little time has passed stop watching him if he turns his back if ((o->x > player->x && player->dir==LEFT) || \ (o->x < player->x && player->dir==RIGHT)) { if (++o->timer >= 150) { o->frame = 0; o->timer = 8; } } else o->timer = 8; } } else { o->timer++; } if (o->shaketime) { // attack if shot o->state = 2; o->frame = 0; o->timer = 0; } } break; case 2: // start jump { if (++o->timer > 8) { o->state = 3; o->frame = 2; o->yinertia = -1228; sound(SND_ENEMY_JUMP); FACEPLAYER; XMOVE(0x100); } } break; case 3: // jumping { // enter flying phase as we start to come down or // if we hit the ceiling. if (o->yinertia > 0x100 || o->blocku) { // during flight we will sine-wave oscilliate around this position o->ymark = (o->y - o->critter.jumpheight); o->state = 4; o->frame = 3; o->timer = 0; } else { if (o->blockd && o->yinertia >= 0) { // jumped onto a platform before we got to fly--land immediately goto landed; } break; } } // fall-thru case 4: // flying { FACEPLAYER; // time to come down yet? // (come down immediately if we are not one of the flying critters) if (!o->critter.canfly || \ o->blockl || o->blockr || o->blocku || \ ++o->timer > 100) { o->damage = o->critter.falldmg; // increased damage if falls on player o->state = 5; o->frame = 2; o->yinertia /= 2; } else { // run the propeller ANIMATE(0, 3, 5); if ((o->timer & 3)==1) sound(SND_CRITTER_FLY); if (o->blockd) o->yinertia = -0x200; } } break; case 5: // coming down from flight { if (o->blockd) { // landed landed: ; o->damage = 2; // reset to normal damage o->state = 1; o->frame = 0; o->timer = 0; o->xinertia = 0; sound(SND_THUD); } } break; } if (o->state == 4) // flying { // fly towards player o->xinertia += (o->x > player->x) ? -0x20 : 0x20; // sine-wave oscillate o->yinertia += (o->y > o->ymark) ? -0x10 : 0x10; LIMITX(0x200); LIMITY(0x200); } else if (o->state == 3 && o->yinertia < 0) // jumping up { o->yinertia += o->critter.jumpgrav; } else { o->yinertia += 0x40; } LIMITY(0x5ff); }
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_dragon_zombie(Object *o) { if (o->hp < 950 && o->state < 50) { sound(SND_BIG_CRASH); SmokeClouds(o, 8, 4, 4); o->SpawnPowerups(); o->flags &= ~FLAG_SHOOTABLE; o->damage = 0; o->frame = 5; // dead o->state = 50; // dead } switch(o->state) { case 0: case 1: // ready { ANIMATE(30, 0, 1); if (--o->timer < 0) // can fire again yet? { if (pdistlx(112 << CSF)) // player in range? { o->state = 2; o->timer = 0; } } } break; case 2: // flashing, prepare to fire { FACEPLAYER; o->timer++; o->frame = (o->timer & 2) ? 2 : 3; if (o->timer > 30) o->state = 3; } break; case 3: { o->state = 4; o->timer = 0; o->frame = 4; // save point we'll fire at--these enemies don't update // the position of their target for each shot o->xmark = player->x; o->ymark = player->y; } case 4: { o->timer++; if (o->timer < 40 && (o->timer % 8) == 1) { Object *fire = SpawnObjectAtActionPoint(o, OBJ_DRAGON_ZOMBIE_SHOT); ThrowObject(fire, o->xmark, o->ymark, 6, 0x600); sound(SND_SNAKE_FIRE); } if (o->timer > 60) { o->state = 1; o->frame = 0; o->timer = random(100, 200); // random time till can fire again } } 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_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_bute_sword(Object *o) { if (run_bute_defeated(o, BUTE_HP)) return; switch(o->state) { case 0: { o->flags |= (FLAG_SHOOTABLE | FLAG_INVULNERABLE); o->nxflags |= NXFLAG_FOLLOW_SLOPE; o->damage = 0; o->state = 1; } case 1: // lying in wait { FACEPLAYER; if (pdistlx(128<<CSF) && \ pdistly2(128<<CSF, 16<<CSF)) { o->state = 10; } } break; // wait a moment, then start running at player case 10: { o->flags |= FLAG_INVULNERABLE; o->damage = 0; o->frame = 0; o->state = 11; o->timer = 0; } case 11: { if (++o->timer > 30) { o->state = 20; o->timer = 0; } } break; // run at player and jump case 20: { o->flags &= ~FLAG_INVULNERABLE; o->state = 21; FACEPLAYER; } case 21: { ANIMATE(3, 0, 1); XMOVE(0x400); if (pdistlx(40<<CSF)) { o->xinertia /= 2; o->yinertia = -0x300; o->state = 30; o->frame = 2; // sword back, jumping sound(SND_ENEMY_JUMP); } else if (++o->timer > 50) { // timeout, p got away o->state = 10; o->xinertia = 0; } } break; // jumping up case 30: { if (o->yinertia > -0x80) { o->frame = 3; // sword swipe fwd o->damage = 9; o->state = 31; o->timer = 0; } } break; // swiping sword, in air case 31: { if (++o->timer > 2) { o->timer = 0; o->frame = 4; // sword down, in front } if (o->blockd && o->yinertia > 0) { o->xinertia = 0; o->damage = 3; o->state = 32; o->timer = 0; } } break; case 32: { if (++o->timer > 30) { o->state = 10; o->timer = 0; } } break; } o->yinertia += 0x20; LIMITY(0x5ff); }
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; } }
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_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_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); }