// set the x&y inertia of object o so that it travels towards [destx,desty]. // rand_variance is a random amount of inaccuracy, in 0-255 degrees, to introduce // into the toss. // speed is how quickly to throw the object, in CSF'd coordinates. void ThrowObject(Object *o, int destx, int desty, int rand_variance, int speed) { uint8_t angle = GetAngle(o->x, o->y, destx, desty); if (rand_variance) angle += random(-rand_variance, rand_variance); ThrowObjectAtAngle(o, angle, speed); }
// boss-fight igor void ai_boss_igor(Object *o) { enum { STATE_INIT = 0, STATE_STAND, STATE_BEGIN_ATTACK, STATE_WALK, STATE_JUMPING, STATE_LANDED, STATE_PUNCH, STATE_PUNCH_2, STATE_PUNCH_3, STATE_MOUTH_BLAST, STATE_MOUTH_BLAST_2 }; switch(o->state) { case STATE_INIT: { o->damage = 0; o->xinertia = 0; o->state = STATE_STAND; o->frame = 0; o->animtimer = 0; } case STATE_STAND: { ANIMATE(5, 0, 1); if (++o->timer > 50) { o->state = STATE_BEGIN_ATTACK; } } break; case STATE_BEGIN_ATTACK: { o->state = STATE_WALK; o->frame = 2; o->animtimer = 0; o->timer = 0; FACEPLAYER; o->igor.fireattack = false; // when health is less than halfway, then use // the mouth blast attack every third time. if (++o->timer2 >= 3 && \ o->hp <= (objprop[o->type].initial_hp / 2)) { o->timer2 = 0; o->igor.fireattack = true; o->dir ^= 1; // walk away from player } } // fall thru case STATE_WALK: { ANIMATE(3, 2, 5); XMOVE(0x200); if (o->igor.fireattack) { // begin mouth-blast attack if (++o->timer > 16) { o->state = STATE_MOUTH_BLAST; o->xinertia = 0; o->frame = 10; } } else { if (o->dir == LEFT) { if (o->x <= player->x + player->Width()) o->state = STATE_PUNCH; } else { if (o->x + o->Width() >= player->x) o->state = STATE_PUNCH; } // if we don't reach him after a while, do a jump if (++o->timer > 50) { o->frame = 10; o->yinertia = -0x400; o->state = STATE_JUMPING; o->timer = 0; o->xinertia *= 2; o->xinertia /= 3; o->damage = 2; } } } break; case STATE_PUNCH: { o->xinertia = 0; o->state = STATE_PUNCH_2; o->frame = 6; o->timer = 0; } case STATE_PUNCH_2: { if (++o->timer > 12) { sound(SND_EXPL_SMALL); // sprite appears identical, but has a wider bounding box. o->sprite = SPR_IGOR_PUNCHING; o->damage = 5; o->state = STATE_PUNCH_3; o->frame = 7; o->timer = 0; } } break; case STATE_PUNCH_3: { if (++o->timer > 10) { o->state = STATE_INIT; o->frame = 0; o->damage = 0; // return to normal-size bounding box o->sprite = SPR_IGOR; } } break; case STATE_JUMPING: { if (o->blockd) { sound(SND_QUAKE); SmokeSide(o, 4, DOWN); o->state = STATE_LANDED; o->frame = 11; o->timer = 0; } } break; case STATE_LANDED: { o->xinertia = 0; if (++o->timer > 10) { o->state = STATE_INIT; o->frame = 0; o->damage = 0; } } break; case STATE_MOUTH_BLAST: { FACEPLAYER; o->timer = 0; o->state++; } case STATE_MOUTH_BLAST_2: { o->timer++; // flash mouth o->frame = 8; if (o->timer > 50 && (o->timer & 2)) o->frame = 9; // fire shots if (o->timer > 100) { if ((o->timer % 6) == 1) { sound(SND_BLOCK_DESTROY); Object *shot = SpawnObjectAtActionPoint(o, OBJ_IGOR_SHOT); int angle = (o->dir == LEFT) ? 136 : 248; angle += random(-16, 16); ThrowObjectAtAngle(shot, angle, 0x580); } if (o->timer > 132) // fires 6 shots { o->state = STATE_INIT; o->timer = 0; } } } break; } o->yinertia += 0x40; LIMITY(0x5ff); }
void ai_boss_doctor(Object *o) { //AIDEBUG; /*if (o->state > 2 && o->state < 500) { o->state = 937; StartScript(410); return; }*/ switch(o->state) { case 0: { o->y += (8 << CSF); o->frame = 3; o->state = 1; o->BringToFront(); // make sure in front of doctor_crowned crystal_tofront = true; // make sure front crystal is in front of us } break; case 2: // transforming (script) { o->timer++; o->frame = (o->timer & 2) ? 0 : 3; if (o->timer > 50) o->state = 10; } break; case 10: // base state/falling (script) { o->yinertia += 0x80; o->flags |= FLAG_SHOOTABLE; o->damage = 3; if (o->blockd) { o->state = 20; o->timer = 0; o->frame = 0; o->savedhp = o->hp; FACEPLAYER; } } break; // fire wave shot case 20: { o->timer++; if (o->timer < 50) { if ((o->hp - o->savedhp) > 20) o->timer = 50; } if (o->timer == 50) { // arm across chest FACEPLAYER; o->frame = 4; } if (o->timer == 80) { Object *shot; o->frame = 5; // arm cast out shot = SpawnObjectAtActionPoint(o, OBJ_DOCTOR_SHOT); shot->dir = o->dir; shot->angle = 0; shot = SpawnObjectAtActionPoint(o, OBJ_DOCTOR_SHOT); shot->dir = o->dir; shot->angle = 0x80; sound(SND_FUNNY_EXPLODE); } if (o->timer == 120) o->frame = 0; // arm down if (o->timer > 130) { if ((o->hp - o->savedhp) > 50) { o->state = 100; o->timer = 0; } if (o->timer > 160) { o->state = 100; o->timer = 0; } } } break; // big "explosion" blast case 30: { o->state = 31; o->timer = 0; o->frame = 6; o->xmark = o->x; o->flags |= FLAG_SHOOTABLE; } case 31: { o->x = o->xmark; if (++o->timer & 2) o->x += (1 << CSF); if (o->timer > 50) { o->state = 32; o->timer = 0; o->frame = 7; sound(SND_LIGHTNING_STRIKE); for(int angle=8;angle<256;angle+=16) { Object *shot = SpawnObjectAtActionPoint(o, OBJ_DOCTOR_BLAST); ThrowObjectAtAngle(shot, angle, 0x400); } } } break; case 32: // after blast { if (++o->timer > 50) o->state = 100; } break; // teleport away case 100: { o->state = 101; o->flags &= ~FLAG_SHOOTABLE; o->damage = 0; dr_tp_out_init(o); } case 101: { if (dr_tp_out(o)) { o->state = 102; o->timer = 0; o->invisible = true; // decide where we're going to go now, so the red crystal // can start moving towards it. But, it's important not to // actually move until the last possible second, or we could // drag our floattext along with us (and give away our position). o->xmark = (random(5, 35) * TILE_W) << CSF; o->ymark = (random(5, 7) * TILE_H) << CSF; } } break; case 102: // invisible: waiting to reappear { if (++o->timer > 40) { o->state = 103; o->timer = 16; o->frame = 2; o->yinertia = 0; o->x = o->xmark; o->y = o->ymark; FACEPLAYER; } } break; // tp back in case 103: { o->state++; dr_tp_in_init(o); } case 104: { if (dr_tp_in(o)) { o->flags |= FLAG_SHOOTABLE; o->damage = 3; if (++o->timer2 >= 4) { // big explode o->timer2 = 0; o->state = 30; } else { // another wave shot o->state = 10; } } } break; // defeated! case 500: { o->flags &= ~FLAG_SHOOTABLE; o->frame = 6; // fall to earth o->yinertia += 0x10; if (o->blockd && o->yinertia >= 0) { o->state = 501; o->timer = 0; o->xmark = o->x; FACEPLAYER; } } break; case 501: // flashing (transforming into Doctor 2) { FACEPLAYER; o->frame = 8; o->x = o->xmark; if (!(++o->timer & 2)) o->x += (1 << CSF); } break; } // enable per-frame bbox COPY_PFBOX; // set crystal follow position if (o->state >= 10) { if (o->invisible) // teleporting { crystal_xmark = o->xmark; crystal_ymark = o->ymark; } else { crystal_xmark = o->x; crystal_ymark = o->y; } } LIMITY(0x5ff); }