void ai_green_devil(Object *o) { switch(o->state) { case 0: { o->flags |= FLAG_SHOOTABLE; o->ymark = o->y; o->yinertia = random(-5<<CSF, 5<<CSF); o->damage = 3; o->state = 1; } case 1: { ANIMATE(2, 0, 1); o->yinertia += (o->y < o->ymark) ? 0x80 : -0x80; XACCEL(0x20); LIMITX(0x400); if (o->dir == LEFT) { if (o->x < -o->Width()) o->Delete(); } else { if (o->x > ((map.xsize * TILE_W) << CSF) + o->Width()) o->Delete(); } } break; } }
void ai_fuzz(Object *o) { FACEPLAYER; switch(o->state) { case 0: { o->angle += 4; if (!o->linkedobject) { o->xinertia = random(-0x200, 0x200); o->yinertia = random(-0x200, 0x200); o->state = 1; } } break; // base destroyed, simple sinusoidal player-seek case 1: { o->xinertia += (o->x > player->x) ? -0x20 : 0x20; o->yinertia += (o->y > player->y) ? -0x20 : 0x20; LIMITX(0x800); LIMITY(0x200); } break; } }
// red energy for doctor. In a completely different role, // it's also used for the dripping blood from Ballos's final form. void ai_red_energy(Object *o) { switch(o->angle) { case UP: { o->yinertia -= 0x40; if (o->blocku && o->yinertia < 0) o->Delete(); } break; case DOWN: { o->yinertia += 0x40; if (o->blockd && o->yinertia > 0) o->Delete(); if (++o->timer > 50) o->Delete(); if (o->yinertia > 0x5ff) o->yinertia = 0x5ff; } break; case RIGHT: { if (!o->linkedobject) { o->Delete(); return; } if (o->state == 0) { o->state = 1; o->flags |= FLAG_IGNORE_SOLID; o->xinertia = random(-0x600, 0x600); o->yinertia = random(-0x600, 0x600); // accel speed o->speed = (512 / random(16, 51)); // x/y limit int limit = random(0x80, 0x100); o->timer2 = (limit * 2); // x limit o->timer3 = (limit * 3); // y limit (form elongated sphere) } int tgtx = o->linkedobject->x + (4<<CSF); if (o->x < tgtx) o->xinertia += o->speed; else if (o->x > tgtx) o->xinertia -= o->speed; if (o->y < o->linkedobject->y) o->yinertia += o->speed; else if (o->y > o->linkedobject->y) o->yinertia -= o->speed; LIMITX(o->timer2); LIMITY(o->timer3); } } o->frame = random(0, 1); }
void ai_bute_flying(Object *o) { //AIDEBUG; if (run_bute_defeated(o, BUTE_HP)) return; switch(o->state) { case 0: { o->invisible = true; o->state = 1; } case 1: { if (o->dir == LEFT) { if (player->x > (o->x - (288<<CSF)) && \ player->x < (o->x - (272<<CSF))) { o->state = 10; } } else { if (player->x < (o->x + (288<<CSF)) && \ player->x > (o->x + (272<<CSF))) { o->state = 10; } } } break; case 10: { o->state = 11; o->invisible = false; o->flags |= FLAG_SHOOTABLE; o->damage = 5; } case 11: { FACEPLAYER; ANIMATE(1, 0, 1); XACCEL(0x10); o->yinertia += (o->y > player->y) ? -0x10 : 0x10; LIMITX(0x5ff); LIMITY(0x5ff); if ((o->blockl && o->xinertia < 0) || \ (o->blockr && o->xinertia > 0)) { o->xinertia = -o->xinertia; } if ((o->blockd && o->yinertia > 0) || \ (o->blocku && o->yinertia < 0)) { o->yinertia = -o->yinertia; } } break; } }
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_miserys_bubble(Object *o) { Object *target; switch(o->state) { case 0: { // find the Toroko object we are to home in on target = mbubble_find_target(); if (!target) { o->state = 9999; return; } o->xmark = target->x - (6 << CSF); o->ymark = target->y - (6 << CSF); ThrowObject(o, o->xmark, o->ymark, 0, (2 << CSF)); o->state = 1; // correct values: 0x3F0, 0xAE NX_LOG("Computed toss values xi: 0x%x, 0x%x\n", o->xinertia, o->yinertia); NX_LOG("Target x/y: 0x%x, 0x%x\n", target->x, target->y); } case 1: ANIMATE(1, 0, 1); if (abs(o->x - o->xmark) <= (3 << CSF) && \ abs(o->y - o->ymark) <= (3 << CSF)) { o->state = 2; o->frame = 2; sound(SND_BUBBLE); if ((target = mbubble_find_target())) target->invisible = true; } break; case 2: { ANIMATE(1, 2, 3); o->xinertia -= 0x20; o->yinertia -= 0x20; LIMITX(0x5FF); LIMITY(0x5FF); if (o->y < -1000) o->Delete(); } break; } }
void ai_boss_misery(Object *o) { /*debug("state: %d", o->state); debug("timer: %d", o->timer); debug("timer2: %d", o->timer2);*/ switch (o->state) { // fight begin and default/base state case STATE_FIGHTING: { o->flags |= FLAG_SHOOTABLE; o->savedhp = o->hp; o->timer = 0; o->frame = 0; o->xinertia = 0; o->state++; } case STATE_FIGHTING + 1: { FACEPLAYER; o->yinertia += (o->y < o->ymark) ? 0x20 : -0x20; LIMITY(0x200); if (++o->timer > 200 || (o->savedhp - o->hp) >= 80) { o->state = STATE_FLASH_FOR_SPELL; o->timer = 0; } } break; } run_spells(o); run_teleport(o); run_intro(o); run_defeated(o); LIMITX(0x200); LIMITY(0x400); }
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; } }
// The Doctor's red crystal. // There are actually two, one is behind him and one is in front // and they alternate visibility as they spin around him so it looks 3D. // // This function has to be an aftermove, otherwise, because one is in front // and the other behind, one will be checking crystal_xmark before the Doctor // updates it, and the other afterwards, and they will get out of sync. void aftermove_red_crystal(Object *o) { ANIMATE(3, 0, 1); switch(o->state) { case 0: { if (crystal_xmark != 0) { o->state = 1; crystal_tofront = true; } } break; case 1: { o->xinertia += (o->x < crystal_xmark) ? 0x55 : -0x55; o->yinertia += (o->y < crystal_ymark) ? 0x55 : -0x55; LIMITX(0x400); LIMITY(0x400); if ((o->dir == LEFT && o->xinertia > 0) || \ (o->dir == RIGHT && o->xinertia < 0)) { o->invisible = true; } else { o->invisible = false; } } break; } if (crystal_tofront && o->dir == LEFT) { o->BringToFront(); crystal_tofront = false; } }
void ai_core_ghostie(Object *o) { char hit = 0; if (o->xinertia > 0 && o->blockr) hit = 1; if (o->xinertia < 0 && o->blockl) hit = 1; if (o->yinertia > 0 && o->blockd) hit = 1; if (o->yinertia < 0 && o->blocku) hit = 1; o->xinertia -= 0x20; LIMITX(0x400); if (hit) { effect(o->CenterX(), o->CenterY(), EFFECT_FISHY); o->Delete(); } ai_animate2(o); }
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_red(Object *o) { switch(o->state) { case 0: { o->state = 1; o->sprite = SPR_BUTE_SWORD_RED_FALLING; o->MoveAtDir(o->dir, 0x600); o->dir = 0; } case 1: { ANIMATE(2, 0, 3); if (++o->timer == 8) o->flags &= ~FLAG_IGNORE_SOLID; if (o->timer >= 16) { o->state = 10; o->sprite = SPR_BUTE_SWORD_RED; o->frame = 0; o->flags |= FLAG_SHOOTABLE; o->damage = 5; } } break; case 10: { ANIMATE(1, 0, 1); FACEPLAYER; // when player is below them, they come towards him, // when player is above, they sweep away. if (player->CenterY() > (o->y + (24 << CSF))) { XACCEL(0x10); } else { XACCEL(-0x10); } o->yinertia += (o->y <= player->y) ? 0x10 : -0x10; if ((o->blockl && o->xinertia < 0) || \ (o->blockr && o->xinertia > 0)) { o->xinertia = -o->xinertia; } if ((o->blocku && o->yinertia <= 0) || \ (o->blockd && o->yinertia >= 0)) { o->yinertia = -o->yinertia; } LIMITX(0x5ff); LIMITY(0x5ff); } break; } }
void ai_jelly(Object *o) { switch(o->state) { case 0: { o->nxflags |= NXFLAG_SLOW_WHEN_HURT; o->timer = random(0, 20); o->xmark = o->x; o->ymark = o->y; o->xinertia = (o->dir == LEFT) ? 0x200 : -0x200; o->state = 1; } case 1: { if (--o->timer <= 0) { o->state = 10; } else break; } case 10: { if (++o->timer > 10) { o->timer = o->frame = 0; o->state = 11; } } break; case 11: { if (++o->animtimer > 5) { o->animtimer = 0; o->frame++; } if (o->frame == 2) { o->xinertia += (o->dir == LEFT) ? -0x100 : +0x100; o->yinertia -= 0x200; } else if (o->frame > 2) { o->state = 12; o->frame = 3; } } break; case 12: { o->timer++; if (o->y > o->ymark && o->timer > 10) { o->timer = 0; o->state = 10; o->frame = 0; } } break; } o->dir = (o->x > o->xmark) ? LEFT : RIGHT; if (o->blockl) o->dir = RIGHT; if (o->blockr) o->dir = LEFT; if (o->blockd) o->yinertia = -0x200; o->yinertia += 0x20; LIMITX(0x100); LIMITY(0x200); }
void ai_npc_sue(Object *o) { switch(o->state) { case 0: // stand and blink o->timer = 0; o->frame = 0; o->xinertia = 0; o->sue.carried_by = NULL; randblink(o, 1, 4); break; case 3: // walking case 4: // walking case 5: // face away ai_generic_npc(o); break; // got punched by Igor case 6: o->state = 7; o->frame = 7; o->timer = 0; sound(SND_ENEMY_SQUEAK); case 7: if (++o->timer > 10) o->state = 0; break; // got punched extra hard by Igor // flys through air backwards and crashes case 8: o->state = 9; o->frame = 7; o->timer = 0; sound(SND_ENEMY_SQUEAK); o->yinertia = -0x200; XMOVE(-0x400); case 9: if (++o->timer > 3 && o->blockd) { o->state = 10; o->dir ^= 1; } break; case 10: o->xinertia = 0; o->frame = 8; break; // punching the air (when she tells Igor "I'm not afraid of you!") case 11: o->state = 12; o->timer = 0; o->animframe = 0; o->animtimer = 0; case 12: { const static int punchframes[] = { 10, 0 }; o->animate_seq(8, punchframes, 2); } break; // picked up & carried away by Igor case 13: o->frame = 11; o->xinertia = 0; o->yinertia = 0; o->state = 14; // find Igor o->sue.carried_by = FindObjectByID2(501); if (!o->sue.carried_by) NX_ERR("-- Could not find entity carrying Sue (ID 501)\n"); case 14: // being carried--see aftermove routine o->frame = 9; break; // spawn red crystal and call it to us (Undead Core intro) case 15: { o->PushBehind(dr_create_red_crystal(o->x+(128<<CSF), o->y)); o->state = 16; o->xinertia = 0; o->frame = 0; } case 16: { crystal_xmark = o->x - (18<<CSF); crystal_ymark = o->y - (8<<CSF); } break; case 17: // look up (still followed by red crystal) { o->xinertia = 0; o->frame = 12; crystal_xmark = o->x; crystal_ymark = o->y - (8<<CSF); } break; // run away from DOCTOR_GHOST and hide behind player case 20: { o->state = 21; o->frame = 2; o->animtimer = 0; } case 21: { ANIMATE(2, 2, 5); XMOVE(0x400); if (o->x < player->x - (8<<CSF)) { o->dir = RIGHT; o->state = 0; } } break; // run, during "we've got to get out of here" post-undead core cutscene. case 30: { o->state = 31; o->frame = 2; o->animtimer = 0; } case 31: { ANIMATE(2, 2, 5); XMOVE(0x400); } break; case 40: // she jumps off the island { o->state = 41; o->frame = 9; o->yinertia = -0x400; } break; /*default: NX_ERR("-- Sue entered unhandled state %d (0x%02x)\n", o->state, o->state); exit(1);*/ } o->yinertia += 0x40; LIMITX(0x400); LIMITY(0x5ff); }
void ai_toroko(Object *o) { switch(o->state) { case 0: // stand and blink o->frame = 0; o->xinertia = 0; randblink(o, 1, 4); break; case 3: // run away!! o->state = 4; o->frame = 1; o->animtimer = 0; case 4: ANIMATE(2, 1, 4); if (o->blockl) { o->dir = RIGHT; o->xinertia = 0x200; } if (o->blockr) { o->dir = LEFT; o->xinertia = -0x200; } XMOVE(0x400); break; case 6: // hop and run away!! o->state = 7; o->frame = 1; o->animtimer = 0; o->yinertia = -0x400; o->toro.left_ground = false; case 7: ANIMATE(2, 1, 4); XMOVE(0x100); if (!o->toro.left_ground) { if (!o->blockd) o->toro.left_ground = true; } else { if (o->blockd) { o->toro.left_ground = false; o->state = 3; } } break; // small hop straight up/down from Balrog // shaking the ground, used in Shack case 8: o->frame = 1; o->timer = 0; o->state = 9; o->yinertia = -0x200; o->toro.left_ground = false; case 9: { if (!o->toro.left_ground) { if (!o->blockd) o->toro.left_ground = true; } else { if (o->blockd) { o->toro.left_ground = false; o->state = 0; } } } break; case 10: // eeks and falls down o->state = 11; o->frame = 5; o->yinertia = -(2 << CSF); sound(SND_ENEMY_SQUEAK); XMOVE(0x100); break; case 11: // falling down if (o->blockd) { o->state = 12; o->frame = 6; o->flags |= FLAG_SCRIPTONACTIVATE; o->xinertia = 0; } break; } o->yinertia += 0x40; LIMITX(0x400); 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_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; } }
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_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; } }
// his lightning-strike attack static void run_lightning(Object *o) { switch(o->state) { // lightning strikes (targeting player) case BP_LIGHTNING_STRIKE: { o->xmark = player->x; o->yinertia = -0x600; o->timer = 0; o->timer2 = 0; o->animtimer = 0; o->frame = 4; // facing screen o->dir = LEFT; // not flashing o->state++; } case BP_LIGHTNING_STRIKE+1: { ANIMATE(1, 4, 5); o->xinertia += (o->x < o->xmark) ? 0x40 : -0x40; o->yinertia += (o->y < FLOAT_Y) ? 0x40 : -0x40; LIMITX(0x400); LIMITY(0x400); // run firing if (++o->timer > 200) { int pos = (o->timer % 40); if (pos == 1) { // spawn lightning target CreateObject(player->CenterX(), LIGHTNING_Y, OBJ_BALLOS_TARGET)->dir = LEFT; o->dir = RIGHT; // switch to flashing frames o->animtimer = 0; // after 8 attacks, switch to even-spaced strikes if (++o->timer2 >= 8) { o->xinertia = 0; o->yinertia = 0; o->dir = RIGHT; // flashing o->frame = 5; // flash red then white during screen flash o->animtimer = 1; // desync animation from screen flashes so it's visible o->state++; o->timer = 0; o->timer2 = 0; } } else if (pos == 20) { o->dir = LEFT; // stop flashing } } } break; // lightning strikes (evenly-spaced everywhere) case BP_LIGHTNING_STRIKE+2: { ANIMATE(1, 4, 5); o->timer++; if (o->timer == 40) flashscreen.Start(); if (o->timer > 50) { if ((o->timer % 10) == 1) { CreateObject((o->timer2 * TILE_W) << CSF, \ LIGHTNING_Y, OBJ_BALLOS_TARGET)->dir = LEFT; o->timer2 += 4; if (o->timer2 >= 40) o->state = BP_RETURN_TO_GROUND; } } } break; } }
void XBoss::run_tread(int index) { Object *o = treads[index]; switch(o->state) { case 0: { o->flags |= (FLAG_SOLID_BRICK | FLAG_INVULNERABLE | FLAG_NOREARTOPATTACK); o->state = STATE_TREAD_STOPPED; } case STATE_TREAD_STOPPED: { o->frame = 0; o->damage = 0; o->flags &= ~FLAG_BOUNCY; } break; case STATE_TREAD_RUN: { o->flags |= FLAG_BOUNCY; o->timer = 0; o->frame = 2; o->animtimer = 0; o->state++; } case STATE_TREAD_RUN+1: { ANIMATE(0, 2, 3); XACCEL(0x20); if (++o->timer > 30) { o->flags &= ~FLAG_BOUNCY; o->frame = 0; o->animtimer = 0; o->state++; } } break; case STATE_TREAD_RUN+2: { ANIMATE(1, 0, 1); XACCEL(0x20); o->timer++; } break; case STATE_TREAD_BRAKE: { o->frame = 2; o->animtimer = 0; o->flags |= FLAG_BOUNCY; o->state++; } case STATE_TREAD_BRAKE+1: { ANIMATE(0, 2, 3); XACCEL(0x20); if ((o->dir == RIGHT && o->xinertia > 0) || \ (o->dir == LEFT && o->xinertia < 0)) { o->xinertia = 0; o->state = STATE_TREAD_STOPPED; } } break; } // make motor noise switch(o->state) { case STATE_TREAD_RUN+1: case STATE_TREAD_BRAKE+1: { if (o->timer & 1) sound(SND_MOTOR_SKIP); } break; case STATE_TREAD_RUN+2: { if ((o->timer % 4) == 1) sound(SND_MOTOR_RUN); } break; } // determine if player is in a position where he could get run over. if (o->state > STATE_TREAD_STOPPED && o->xinertia != 0) { if (abs(player->y - o->CenterY()) <= (5 << CSF)) o->damage = 10; else o->damage = 0; } else { o->damage = 0; } LIMITX(0x400); }
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_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_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); }