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 SmokeXY(int x, int y, int nclouds, int rangex, int rangey, Object *push_behind) { Object *s; for(int i=0;i<nclouds;i++) { s = SmokePuff(x + (random(-rangex, rangex) << CSF), \ y + (random(-rangey, rangey) << CSF)); } }
void OmegaBoss::Run(void) { Object *&o = game.stageboss.object; if (omg.defeated) return; switch(o->state) { case 0: break; // waiting for trigger by script case OMG_WAIT: // waits for a moment then go to omg.nextstate { o->state++; omg.timer = 0; } case OMG_WAIT+1: { if (++omg.timer >= OMEGA_WAIT_TIME) { omg.timer = 0; o->state = omg.nextstate; } } break; case OMG_APPEAR: { omg.timer = 0; o->frame = 0; o->state = OMG_MOVE; omg.movedir = -OMEGA_SPEED; o->flags |= FLAG_SOLID_MUSHY; } case OMG_MOVE: // rising up/going back into ground { o->frame = 0; o->y += omg.movedir; game.quaketime = 2; omg.timer++; if ((omg.timer & 3) == 0) sound(SND_QUAKE); if (omg.timer >= omg.movetime) { if (omg.movedir < 0) { // was rising out of ground omg.nextstate = OMG_JAWS_OPEN; o->state = OMG_WAIT; } else { // was going back into ground omg.timer = 0; o->state = OMG_UNDERGROUND; o->flags &= ~(FLAG_SOLID_MUSHY | FLAG_SOLID_BRICK); } } } break; case OMG_JAWS_OPEN: // jaws opening { o->state++; omg.animtimer = 0; sound(SND_JAWS); o->sprite = SPR_OMG_OPENED; // select "open" bounding box } case OMG_JAWS_OPEN+1: { omg.animtimer++; if (omg.animtimer > 2) { omg.animtimer = 0; o->frame++; if (o->frame==3) { o->state = OMG_FIRE; omg.firecounter = 0; o->flags |= FLAG_SHOOTABLE; } } } break; case OMG_FIRE: // throwing out red stuff { omg.firecounter++; if (omg.firecounter > omg.startfiring && omg.firecounter < omg.stopfiring) { if ((omg.firecounter % omg.firefreq)==0) { Object *shot; sound(SND_EM_FIRE); shot = SpawnObjectAtActionPoint(o, OBJ_OMEGA_SHOT); shot->xinertia = random(-omg.shotxspd, omg.shotxspd); shot->yinertia = -0x333; if (omg.form==2 || random(0, 9) < 8) { shot->sprite = SPR_OMG_BULLET_NORMAL; shot->flags = FLAG_SHOOTABLE; } else { shot->sprite = SPR_OMG_BULLET_HARD; shot->flags = (FLAG_SHOOTABLE | FLAG_INVULNERABLE); } shot->timer = (random(0, 7) >= 4) ? random(300, 400):0; shot->damage = 4; } } else if (omg.firecounter >= omg.endfirestate || sound_is_playing(SND_MISSILE_HIT)) { // snap jaws shut omg.animtimer = 0; o->state = OMG_JAWS_CLOSE; sound(SND_JAWS); } } break; case OMG_JAWS_CLOSE: // jaws closing { omg.animtimer++; if (omg.animtimer > 2) { omg.animtimer = 0; o->frame--; if (o->frame == 0) { sound_stop(SND_JAWS); sound(SND_BLOCK_DESTROY); o->sprite = SPR_OMG_CLOSED; // select "closed" bounding box o->flags &= ~FLAG_SHOOTABLE; o->damage = 0; if (omg.form == 1) { // form 1: return to sand o->state = OMG_WAIT; omg.nextstate = OMG_MOVE; omg.movedir = OMEGA_SPEED; omg.movetime = OMEGA_SINK_DEPTH; } else { // form 2: jump sound(SND_FUNNY_EXPLODE); if (o->x < player->x) o->xinertia = 0xC0; else o->xinertia = -0xC0; o->state = OMG_JUMP; o->yinertia = -0x5FF; omg.orgy = o->y; } } } // hurt player if he was standing in the middle when the jaws shut if (player->riding == o) { hurtplayer(OMEGA_DAMAGE); } } break; case OMG_UNDERGROUND: // underground waiting to reappear { if (++omg.timer >= 120) { omg.timer = 0; o->state = OMG_APPEAR; o->x = omg.orgx + (random(-64, 64) << CSF); o->y = omg.orgy; omg.movetime = OMEGA_RISE_HEIGHT; // switch to jumping out of ground when we get low on life if (omg.form==1 && o->hp <= HP_TRIGGER_POINT) { o->flags |= FLAG_SOLID_MUSHY; omg.form = 2; omg.firefreq = 5; omg.shotxspd = 0x155; omg.startfiring = 0; omg.stopfiring = 30; omg.endfirestate = 50; omg.movetime = OMEGA_RISE_HEIGHT+3; } } } break; case OMG_JUMP: // init for jump { omg.orgy = o->y; o->state++; omg.timer = 0; } case OMG_JUMP+1: // jumping { o->yinertia += 0x24; if (o->yinertia > 0x5ff) o->yinertia = 0x5ff; if (o->yinertia > 0) { // coming down pieces[LEFTLEG]->sprite = pieces[RIGHTLEG]->sprite = SPR_OMG_LEG_ONGROUND; // retract legs a little when we hit the ground if (pieces[LEFTLEG]->blockd || pieces[RIGHTLEG]->blockd) { o->xinertia = 0; omg.leg_descend -= o->yinertia; if (++omg.timer >= 3) { o->yinertia = 0; o->state = OMG_JAWS_OPEN; } } // --- squash player if we land on him ------------- // if top of player is higher than bottom of our bounding box // but bottom of player's bounding box is not... if (player->blockd) { int omg_bottom = o->y + (sprites[o->sprite].solidbox.y2 << CSF); if (player->y <= omg_bottom) { if (player->y + (sprites[player->sprite].solidbox.y2 << CSF) >= omg_bottom) { if (hitdetect(o, player)) // easy way to verify the X's are lined up { // SQUISH! hurtplayer(OMEGA_DAMAGE); } } } } } else { // jumping up; extend legs omg.leg_descend = (omg.orgy - o->y) + LEGD_MIN; if (omg.leg_descend > LEGD_MAX) omg.leg_descend = LEGD_MAX; pieces[LEFTLEG]->sprite = pieces[RIGHTLEG]->sprite = SPR_OMG_LEG_INAIR; } } break; /// victory case OMG_EXPLODING: { omg.timer = 0; o->state++; } case OMG_EXPLODING+1: { int x, y; o->xinertia = o->yinertia = 0; x = o->CenterX() + (random(-48, 48)<<CSF); y = o->CenterY() + (random(-48, 24)<<CSF); SmokePuff(x, y); effect(x, y, EFFECT_BOOMFLASH); game.quaketime = 2; if ((omg.timer % 12)==0) sound(SND_ENEMY_HURT_BIG); if (++omg.timer > 100) { omg.timer = 0; starflash.Start(o->CenterX(), o->CenterY()); o->state = OMG_EXPLODED; } else if (omg.timer==24) { StartScript(210); } } break; case OMG_EXPLODED: { game.quaketime = 40; if (++omg.timer > 50) { o->Delete(); for(int i=0;i<NUM_PIECES;i++) pieces[i]->Delete(); omg.defeated = true; return; } } break; } // implement shaking when shot // we do it manually instead of used the usual shared code // because we want all the pieces to shake at once if (o->hp != omg.lasthp && !omg.shaketimer) { omg.shaketimer = 3; // why did I write this? anyway, I'm sure it's important if (o->x > player->x) o->display_xoff = -1; else o->display_xoff = 1; omg.lasthp = o->hp; } if (omg.shaketimer) { int xoff = -o->display_xoff; if (!--omg.shaketimer) xoff = 0; o->display_xoff = xoff; pieces[LEFTLEG]->display_xoff = xoff; pieces[RIGHTLEG]->display_xoff = xoff; pieces[LEFTSTRUT]->display_xoff = xoff; pieces[RIGHTSTRUT]->display_xoff = xoff; } if (o->state) { o->blockl |= pieces[LEFTLEG]->blockl; o->blockr |= pieces[RIGHTLEG]->blockr; pieces[LEFTLEG]->x = o->x - (4 << CSF); pieces[LEFTLEG]->y = o->y + omg.leg_descend; pieces[RIGHTLEG]->x = o->x + (38 << CSF); pieces[RIGHTLEG]->y = o->y + omg.leg_descend; pieces[LEFTSTRUT]->x = o->x + (9 << CSF); pieces[LEFTSTRUT]->y = o->y + (27 << CSF); pieces[RIGHTSTRUT]->x = o->x + (43 << CSF); pieces[RIGHTSTRUT]->y = o->y + (27 << CSF); } }
void XBoss::Run() { Object *o = mainobject; int i; if (!mainobject) return; if (o->state == 0 || (!X.initilized && o->state != STATE_X_APPEAR)) { o->hp = 1; o->x = -(Graphics::SCREEN_WIDTH << CSF); return; } switch(o->state) { // script triggered us to initilize/appear // (there is a hvtrigger, right before player first walks by us // and sees us inactive, which sends us this ANP). case STATE_X_APPEAR: { if (!X.initilized) { Init(); X.initilized = true; } } break; // script has triggered the fight to begin case STATE_X_FIGHT_BEGIN: { o->timer = 0; o->state++; } case STATE_X_FIGHT_BEGIN+1: { if (++o->timer > 100) { FACEPLAYER; o->timer = 0; o->state = STATE_X_TRAVEL; } } break; // starts the treads and moves us in the currently-facing direction case STATE_X_TRAVEL: { // count number of times we've traveled, we brake // and attack every third time. o->timer2++; o->timer = 0; o->state++; } case STATE_X_TRAVEL+1: { o->timer++; // trigger the treads to start moving, // and put them slightly out of sync with each-other. for(int i=0;i<4;i++) { if (o->timer == tread_turnon_times[i]) { treads[i]->state = STATE_TREAD_RUN; treads[i]->dir = o->dir; } } if (o->timer > 120) { // time to attack? we attack every 3rd travel // if so skid to a stop, that's the first step. if (o->timer2 >= 3) { o->timer2 = 0; o->dir ^= 1; o->state = STATE_X_BRAKE; o->timer = 0; } else { // passed player? skid and turn around. if ((o->dir == RIGHT && o->x > player->x) || \ (o->dir == LEFT && o->x < player->x)) { o->dir ^= 1; o->state = STATE_X_TRAVEL; } } } } break; // skidding to a stop in preparation to attack case STATE_X_BRAKE: { o->timer = 0; o->state++; } case STATE_X_BRAKE+1: { o->timer++; // trigger the treads to start braking, // and put them slightly out of sync with each-other. for(int i=0;i<4;i++) { if (o->timer == tread_turnon_times[i]) { treads[i]->state = STATE_TREAD_BRAKE; treads[i]->dir = o->dir; } } if (o->timer > 50) { o->state = STATE_X_OPEN_DOORS; o->timer = 0; } } break; // doors opening to attack case STATE_X_OPEN_DOORS: { o->timer = 0; o->savedhp = o->hp; // select type of attack depending on where we are in the battle if (!AllTargetsDestroyed()) { SetStates(doors, 2, STATE_DOOR_OPENING); o->state = STATE_X_FIRE_TARGETS; } else { SetStates(doors, 2, STATE_DOOR_OPENING_PARTIAL); o->state = STATE_X_FIRE_FISHIES; } } break; // firing targets (early battle) case STATE_X_FIRE_TARGETS: { if (doors[0]->state == STATE_DOOR_FINISHED) { doors[0]->state = 0; SetStates(targets, 4, STATE_TARGET_FIRE); } if (++o->timer > 300 || AllTargetsDestroyed()) { o->state = STATE_X_CLOSE_DOORS; o->timer = 0; } } break; // firing fishy missiles (late battle) case STATE_X_FIRE_FISHIES: { if (doors[0]->state == STATE_DOOR_FINISHED) { doors[0]->state = 0; SetStates(fishspawners, 4, STATE_FISHSPAWNER_FIRE); internals->flags |= FLAG_SHOOTABLE; } if (++o->timer > 300 || (o->savedhp - o->hp) > 200) { o->state = STATE_X_CLOSE_DOORS; o->timer = 0; } } break; // doors closing after attack case STATE_X_CLOSE_DOORS: { o->timer = 0; o->state++; SetStates(doors, 2, STATE_DOOR_CLOSING); } case STATE_X_CLOSE_DOORS+1: { if (doors[0]->state == STATE_DOOR_FINISHED) { doors[0]->state = 0; // just turn off everything for both types of attacks; // turning off the attack type that wasn't enabled isn't harmful. SetStates(targets, 4, 0); SetStates(fishspawners, 4, 0); internals->flags &= ~FLAG_SHOOTABLE; } if (++o->timer > 50) { FACEPLAYER; o->state = STATE_X_TRAVEL; o->timer = 0; } } break; // exploding case STATE_X_EXPLODING: { SetStates(fishspawners, 4, 0); KillObjectsOfType(OBJ_X_FISHY_MISSILE); StartScript(1000); o->timer = 0; o->state++; } case STATE_X_EXPLODING+1: { game.quaketime = 2; o->timer++; if ((o->timer % 8) == 0) sound(SND_ENEMY_HURT_BIG); SmokePuff(o->CenterX() + (random(-72, 72) << CSF), o->CenterY() + (random(-64, 64) << CSF)); if (o->timer > 100) { starflash.Start(o->CenterX(), o->CenterY()); sound(SND_EXPLOSION1); o->timer = 0; o->state++; } } break; case STATE_X_EXPLODING+2: { game.quaketime = 40; if (++o->timer > 50) { CreateObject(o->x, o->y - (24 << CSF), OBJ_X_DEFEATED); DeleteMonster(); return; } } break; } // call AI for all tread pieces for(i=0;i<4;i++) { run_tread(i); run_fishy_spawner(i); } }