#include "../newobjects.h" #include "../../Game/items.h" #include "../../Game/effect2.h" #include "../../Game/lot.h" #include "../../Game/Box.h" #include "../../Game/sphere.h" #include "../../Game/people.h" #include "../../Game/draw.h" #include "../../Game/effects.h" #include "../../Game/misc.h" #define MAX_TRIGGER_RANGE 0x4000 #define SMALL_FLASH 10 #define BIG_FLASH 16 #define BOLT_SPEED 384 enum { NOTHING, SUMMONING, KNOCKBACK }; enum { NORMAL_BOLT, LARGE_BOLT, SUMMON_BOLT }; enum { RIGHT_PRONG, ICONPOS, LEFT_PRONG }; enum { ATTACK_HEAD, ATTACK_HAND1, ATTACK_HAND2 }; enum londonboss_anims { LONDONBOSS_EMPTY, LONDONBOSS_STAND, LONDONBOSS_WALK, LONDONBOSS_RUN, LONDONBOSS_SUMMON, LONDONBOSS_BIGZAP, LONDONBOSS_DEATH, LONDONBOSS_LAUGH, LONDONBOSS_LILZAP, LONDONBOSS_VAULT2, LONDONBOSS_VAULT3, LONDONBOSS_VAULT4, LONDONBOSS_GODOWN }; #define LONDONBOSS_VAULT2_ANIM 9 #define LONDONBOSS_VAULT3_ANIM 18 #define LONDONBOSS_VAULT4_ANIM 15 #define LONDONBOSS_GODOWN_ANIM 21 #define LONDONBOSS_STND2SUM_ANIM 1 #define LONDONBOSS_SUMMON_ANIM 2 #define LONDONBOSS_GODOWN_ANIM 21 #define LONDONBOSS_VAULT_SHIFT 96 #define LONDONBOSS_AWARE_DISTANCE SQUARE(WALL_SIZE) #define LONDONBOSS_WALK_TURN ANGLE(4) #define LONDONBOSS_RUN_TURN ANGLE(7) #define LONDONBOSS_WALK_RANGE SQUARE(WALL_SIZE) #define LONDONBOSS_WALK_CHANCE 0x100 #define LONDONBOSS_LAUGH_CHANCE 0x100 #define LONDONBOSS_TURN ANGLE(2) #define LONDONBOSS_DIE_ANIM 17 #define LONDONBOSS_FINAL_HEIGHT -11776 #define BIGZAP_TIMER 600 static long death_radii[5]; static long death_heights[5]; static long radii[5] = { 200,400,500,500,475 }; static long heights[5] = { -1536,-1280,-832,-384,0 }; static long dradii[5] = { 100 << 4,350 << 4,400 << 4,350 << 4,100 << 4 }; static long dheights1[5] = { -1536 - (768 << 3),-1152 - (384 << 3),-768,-384 + (384 << 3),0 + (768 << 3) }; static long dheights2[5] = { -1536,-1152,-768,-384,0 }; static BITE_INFO londonboss_points[3] = { {16,56,356, 10}, // Right prong. {-28,48,304, 10}, // Icon. {-72,48,356, 10}, // Left prong. }; static SHIELD_POINTS LondonBossShield[40]; static EXPLOSION_RING ExpRings[6]; static EXPLOSION_RING KBRings[3]; static BOSS_STRUCT bossdata; static char links[20][4] = { { 0,0,1,2 }, { 0,0,2,3, }, { 0,0,3,4, }, { 0,0,4,1, }, { 1,2,5,6, }, { 2,3,6,7, }, { 3,4,7,8, }, { 4,1,8,5, }, { 1 + 4,2 + 4,5 + 4,6 + 4, }, { 2 + 4,3 + 4,6 + 4,7 + 4, }, { 3 + 4,4 + 4,7 + 4,8 + 4, }, { 4 + 4,1 + 4,8 + 4,5 + 4, }, { 1 + 8,2 + 8,5 + 8,6 + 8, }, { 2 + 8,3 + 8,6 + 8,7 + 8, }, { 3 + 8,4 + 8,7 + 8,8 + 8, }, { 4 + 8,1 + 8,8 + 8,5 + 8, }, { 1 + 12,2 + 12,5 + 12,5 + 12, }, { 2 + 12,3 + 12,5 + 12,5 + 12, }, { 3 + 12,4 + 12,5 + 12,5 + 12, }, { 4 + 12,1 + 12,5 + 12,5 + 12, } }; static char sumlinks[8][4] = { { 0,0,1,2 }, { 0,0,2,3 }, { 0,0,3,4 }, { 0,0,4,1 }, { 1,2,5,5 }, { 2,3,5,5 }, { 3,4,5,5 }, { 4,1,5,5 } }; static void DrawLondonBossShield(ITEM_INFO* item) { // PSX } static void DrawExplosionRings() { // PSX } static void DrawSummonRings() { // PSX } static void DrawKnockBackRings() { // PSX } static void TriggerLaserBolt(PHD_VECTOR* pos, ITEM_INFO* item, long type, short yang) { ITEM_INFO* bolt_item; short item_number; short angles[2]; /* Create a laser bolt and launch it on its way */ item_number = CreateItem(); if (item_number != NO_ITEM) { bolt_item = &Items[item_number]; bolt_item->objectNumber = ID_LASER_BOLT; bolt_item->roomNumber = item->roomNumber; bolt_item->pos.xPos = pos->x; bolt_item->pos.yPos = pos->y; bolt_item->pos.zPos = pos->z; InitialiseItem(item_number); if (type == SUMMON_BOLT) { bolt_item->pos.yPos += item->pos.yPos - 384; bolt_item->pos.xRot = -pos->y << 5; bolt_item->pos.yRot = GetRandomControl() << 1; } else { phd_GetVectorAngles(LaraItem->pos.xPos - pos->x, LaraItem->pos.yPos - STEP_SIZE - pos->y, LaraItem->pos.zPos - pos->z, angles); bolt_item->pos.xRot = angles[1]; bolt_item->pos.yRot = yang; bolt_item->pos.zRot = 0; } if (type != LARGE_BOLT) { bolt_item->speed = 16; bolt_item->itemFlags[0] = -24; bolt_item->itemFlags[1] = 4; if (type == SUMMON_BOLT) bolt_item->itemFlags[2] = 1; // Set the 'just draw front and back' flag. } else { bolt_item->speed = 24; bolt_item->itemFlags[0] = 31; bolt_item->itemFlags[1] = 16; } AddActiveItem(item_number); } } static void TriggerPlasmaBallFlame(short fx_number, long type, long xv, long yv, long zv) { SPARKS* sptr; long size; long dx, dz; dx = LaraItem->pos.xPos - Effects[fx_number].pos.xPos; dz = LaraItem->pos.zPos - Effects[fx_number].pos.zPos; if (dx < -MAX_TRIGGER_RANGE || dx > MAX_TRIGGER_RANGE || dz < -MAX_TRIGGER_RANGE || dz > MAX_TRIGGER_RANGE) return; sptr = &Sparks[GetFreeSpark()]; sptr->on = 1; sptr->sG = 255; sptr->sB = 48 + (GetRandomControl() & 31); sptr->sR = 48; sptr->dG = 192 + (GetRandomControl() & 63); sptr->dB = 128 + (GetRandomControl() & 63); sptr->dR = 32; sptr->colFadeSpeed = 12 + (GetRandomControl() & 3); sptr->fadeToBlack = 8; sptr->sLife = sptr->life = (GetRandomControl() & 7) + 24; sptr->transType = 2; sptr->extras = 0; sptr->dynamic = -1; sptr->x = ((GetRandomControl() & 15) - 8); sptr->y = 0; sptr->z = ((GetRandomControl() & 15) - 8); sptr->xVel = xv + (GetRandomControl() & 255) - 128; sptr->yVel = yv; sptr->zVel = zv + (GetRandomControl() & 255) - 128; sptr->friction = 5; if (GetRandomControl() & 1) { sptr->flags = SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF | SP_FX; sptr->rotAng = GetRandomControl() & 4095; if (GetRandomControl() & 1) sptr->rotAdd = -(GetRandomControl() & 15) - 16; else sptr->rotAdd = (GetRandomControl() & 15) + 16; } else { sptr->flags = SP_SCALE | SP_DEF | SP_EXPDEF | SP_FX; } sptr->fxObj = fx_number; sptr->scalar = 1; size = (GetRandomControl() & 31) + 64; sptr->size = sptr->sSize = size; sptr->dSize = size >> 2; sptr->gravity = sptr->maxYvel = 0; } static void TriggerPlasmaBall(ITEM_INFO* item, long type, PHD_VECTOR* pos1, short room_number, short angle) { FX_INFO* fx; PHD_VECTOR pos; short fx_number, angles[2]; long speed; pos.x = pos1->x; pos.y = pos1->y; pos.z = pos1->z; speed = (GetRandomControl() & 31) + 64; angles[0] = angle + GetRandomControl() + 0x4000; angles[1] = 0x2000; fx_number = CreateNewEffect(room_number); if (fx_number != NO_ITEM) { fx = &Effects[fx_number]; fx->pos.xPos = pos.x; fx->pos.yPos = pos.y; fx->pos.zPos = pos.z; fx->pos.yRot = angles[0]; fx->pos.xRot = angles[1]; fx->objectNumber = ID_LASER_BOLT; fx->speed = speed; fx->fallspeed = 0; fx->flag1 = 1; if (type == 2) fx->flag2 = 1; else fx->flag2 = 0; } } static long KnockBackCollision(EXPLOSION_RING* erptr) { int radius, x, z, dist; short angle, diff; radius = erptr->radius; x = LaraItem->pos.xPos - erptr->x; z = LaraItem->pos.zPos - erptr->z; if (x > 16000 || x < -16000 || z > 16000 || z < -16000) dist = 0x7FFF; else dist = SQUARE(x) + SQUARE(z); if (dist < SQUARE(radius)) { /* Whump! Been hit by sphere - throw Lara through air */ LaraItem->hitPoints -= 200; LaraItem->hitStatus = true; angle = ATAN(z, x); diff = LaraItem->pos.yRot - angle; if (abs(diff) < 0x4000) { /* Facing away from blast */ LaraItem->speed = 75; LaraItem->pos.yRot = angle; } else { /* Facing towards blast */ LaraItem->pos.yRot = short(angle - 0x8000); LaraItem->speed = -75; } LaraItem->gravityStatus = true; LaraItem->fallspeed = -50; LaraItem->pos.xRot = LaraItem->pos.zRot = 0; LaraItem->animNumber = 34; LaraItem->frameNumber = GF(34, 0); LaraItem->currentAnimState = 3; LaraItem->goalAnimState = 3; TriggerExplosionSparks(LaraItem->pos.xPos, LaraItem->pos.yPos, LaraItem->pos.zPos, 3, -2, 2, LaraItem->roomNumber); // -2 = Set off a dynamic light controller. for (x = 0; x < 3; x++) TriggerPlasmaBall(LaraItem, 2, (PHD_VECTOR*)&LaraItem->pos, LaraItem->roomNumber, GetRandomControl() << 1); return 1; } return 0; } static void ExplodeLondonBoss(ITEM_INFO* item) { SHIELD_POINTS* shptr; long lp, lp1, angle, y, r, g, b; if (bossdata.explode_count == 1 || bossdata.explode_count == 15 || bossdata.explode_count == 25 || bossdata.explode_count == 35 || bossdata.explode_count == 45 || bossdata.explode_count == 55) // Trigger explosion & ring ? { long x, y, z; x = item->pos.xPos + ((GetRandomControl() & 1023) - 512); y = item->pos.yPos - (GetRandomControl() & 1023) - 256; z = item->pos.zPos + ((GetRandomControl() & 1023) - 512); ExpRings[bossdata.ring_count].x = x; ExpRings[bossdata.ring_count].y = y; ExpRings[bossdata.ring_count].z = z; ExpRings[bossdata.ring_count].on = true; bossdata.ring_count++; TriggerExplosionSparks(x, y, z, 3, -2, 2, 0); // -2 = Set off a dynamic light controller. for (lp = 0; lp < 2; lp++) TriggerExplosionSparks(x, y, z, 3, -1, 2, 0); SoundEffect(SFX_TR3_BOSS_EXPLODE_ID76, &item->pos, PITCH_SHIFT | 0x800000); } // Adjust shield coordinates. for (lp = 0; lp < 5; lp++) { if (bossdata.explode_count < 128) // Expand. { death_radii[lp] = (dradii[lp] >> 4) + (((dradii[lp]) * bossdata.explode_count) >> 7); death_heights[lp] = dheights2[lp] + (((dheights1[lp] - dheights2[lp]) * bossdata.explode_count) >> 7); } } // Setup shield coordinates. shptr = &LondonBossShield[0]; // x,y,z,rgb. for (lp = 0; lp < 5; lp++) { y = death_heights[lp]; angle = (Wibble << 3) & 511; for (lp1 = 0; lp1 < 8; lp1++) { shptr->x = (rcossin_tbl[angle << 1] * death_radii[lp]) >> 11; shptr->y = y; shptr->z = (rcossin_tbl[(angle << 1) + 1] * death_radii[lp]) >> 11; if (lp != 0 && lp != 4 && bossdata.explode_count < 64) { g = (GetRandomControl() & 31) + 224; b = (g >> 2) + (GetRandomControl() & 63); r = (GetRandomControl() & 63); r = (r * (64 - bossdata.explode_count)) >> 6; g = (g * (64 - bossdata.explode_count)) >> 6; b = (b * (64 - bossdata.explode_count)) >> 6; shptr->rgb = r | (g << 8) | (b << 16); } else shptr->rgb = 0; angle += 512; angle &= 4095; shptr++; } } } static void LondonBossDie(short item_number) { ITEM_INFO* item; item = &Items[item_number]; item->collidable = false; item->hitPoints = -16384; KillItem(item_number); DisableBaddieAI(item_number); item->flags |= ONESHOT; // Don't want bad guys triggered ever again } void ControlLaserBolts(short item_number) { FLOOR_INFO* floor; ITEM_INFO* item; PHD_VECTOR oldpos; long g, b, rad; short room_number, oldroom, hitlara; int speed; item = &Items[item_number]; oldpos.x = item->pos.xPos; oldpos.y = item->pos.yPos; oldpos.z = item->pos.zPos; oldroom = item->roomNumber; speed = (item->speed * COS(item->pos.xRot)) >> W2V_SHIFT; item->pos.zPos += (speed * COS(item->pos.yRot)) >> W2V_SHIFT; item->pos.xPos += (speed * SIN(item->pos.yRot)) >> W2V_SHIFT; item->pos.yPos += -((item->speed * SIN(item->pos.xRot)) >> W2V_SHIFT); if (item->speed < BOLT_SPEED) item->speed += (item->speed >> 3) + 2; if (item->itemFlags[2] && item->speed > (BOLT_SPEED >> 1)) { item->itemFlags[3]++; if (item->itemFlags[3] >= 16) { KillItem(item_number); return; } } room_number = item->roomNumber; floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &room_number); if (item->roomNumber != room_number) ItemNewRoom(item_number, room_number); if (!item->itemFlags[2]) { if (ItemNearLara(&item->pos, 400)) hitlara = 1; else hitlara = 0; item->floor = GetFloorHeight(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos); if (hitlara || item->pos.yPos >= item->floor || item->pos.yPos <= GetCeiling(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos)) { SoundEffect(SFX_EXPLOSION1, &item->pos, 0); if (item->itemFlags[0] < 0) rad = 2; else rad = 3; TriggerExplosionSparks(oldpos.x, oldpos.y, oldpos.z, rad, -2, 2, item->roomNumber); // -2 = Set off a dynamic light controller. for (g = 0; g < rad; g++) TriggerExplosionSparks(oldpos.x, oldpos.y, oldpos.z, 2, -1, 2, item->roomNumber); rad++; for (g = 0; g < rad; g++) TriggerPlasmaBall(item, 1, &oldpos, oldroom, item->pos.yRot); if (hitlara) { LaraItem->hitPoints -= 30 + ((item->itemFlags[0] >= 0) << 9); LaraItem->hitStatus = true; } else { long dx, dy, dz; dx = SQUARE(LaraItem->pos.xPos - item->pos.xPos); dy = SQUARE(LaraItem->pos.yPos - 256 - item->pos.yPos); dz = SQUARE(LaraItem->pos.zPos - item->pos.zPos); dx = SQRT_ASM(dx + dy + dz); if (dx < 0x400) { LaraItem->hitPoints -= (0x400 - dx) >> (6 - ((item->itemFlags[0] >= 0) << 1)); LaraItem->hitStatus = true; } } KillItem(item_number); return; } } g = 31 - (GetRandomControl() & 7); b = g >> 1; if (item->itemFlags[0] < 0) // Small bolt? { if (item->itemFlags[2]) { g = (g * (16 - item->itemFlags[3])) >> 4; b = (b * (16 - item->itemFlags[3])) >> 4; } rad = -item->itemFlags[0]; if (rad > SMALL_FLASH) { item->itemFlags[0]++; item->itemFlags[1] += 2; } } else { rad = item->itemFlags[0]; if (rad > BIG_FLASH) { item->itemFlags[0]--; item->itemFlags[1] += 4; } } TriggerDynamicLight(item->pos.xPos, item->pos.yPos, item->pos.zPos, rad, 0, g, b); } void ControlLondBossPlasmaBall(short fx_number) { FX_INFO* fx; FLOOR_INFO* floor; long speed; long rnd, r, g, b, old_y; byte radtab[2] = { 13,7 }; short room_number; fx = &Effects[fx_number]; old_y = fx->pos.yPos; fx->fallspeed++; if (fx->speed > 8) fx->speed -= 2; if (fx->pos.xRot > -0x3c00) fx->pos.xRot -= 0x100; speed = (fx->speed * COS(fx->pos.xRot)) >> W2V_SHIFT; fx->pos.zPos += (speed * COS(fx->pos.yRot)) >> W2V_SHIFT; fx->pos.xPos += (speed * SIN(fx->pos.yRot)) >> W2V_SHIFT; fx->pos.yPos += -((fx->speed * SIN(fx->pos.xRot)) >> W2V_SHIFT) + fx->fallspeed; if ((Wibble & 15) == 0) TriggerPlasmaBallFlame(fx_number, 0, 0, abs(old_y - fx->pos.yPos) << 3, 0); room_number = fx->roomNumber; floor = GetFloor(fx->pos.xPos, fx->pos.yPos, fx->pos.zPos, &room_number); if (fx->pos.yPos >= GetFloorHeight(floor, fx->pos.xPos, fx->pos.yPos, fx->pos.zPos) || fx->pos.yPos < GetCeiling(floor, fx->pos.xPos, fx->pos.yPos, fx->pos.zPos)) { KillEffect(fx_number); return; } if (Rooms[room_number].flags & ENV_FLAG_WATER) { KillEffect(fx_number); return; } if (!fx->flag2 && ItemNearLara(&fx->pos, 200)) { LaraItem->hitPoints -= 25; LaraItem->hitStatus = true; KillEffect(fx_number); return; } if (room_number != fx->roomNumber) EffectNewRoom(fx_number, LaraItem->roomNumber); rnd = GetRandomControl(); g = 31 - ((rnd >> 4) & 3); b = 24 - ((rnd >> 6) & 3); r = rnd & 7; TriggerDynamicLight(fx->pos.xPos, fx->pos.yPos, fx->pos.zPos, radtab[fx->flag1], r, g, b); } void InitialiseLondonBoss(short item_number) { SHIELD_POINTS* shptr; long lp, lp1, angle, y; bossdata.explode_count = 0; bossdata.ring_count = 0; bossdata.dead = 0; bossdata.dropped_icon = 0; shptr = &LondonBossShield[0]; for (lp = 0; lp < 5; lp++) { y = heights[lp]; angle = 0; for (lp1 = 0; lp1 < 8; lp1++) { shptr->x = (rcossin_tbl[angle << 1] * radii[lp]) >> 11; shptr->y = y; shptr->z = (rcossin_tbl[(angle << 1) + 1] * radii[lp]) >> 11; shptr->rgb = 0; // rgb. angle += 512; shptr++; } } } void LondonBossControl(short item_number) { if (!CreatureActive(item_number)) return; ITEM_INFO* item, *enemy; CREATURE_INFO* londonboss; short angle, head, tilt, lp, torso_y, torso_x; int lara_dx, lara_dz, lara_dy; AI_INFO info, lara_info; PHD_VECTOR pos1, trident[3]; item = &Items[item_number]; londonboss = (CREATURE_INFO*)item->data; head = angle = tilt = torso_x = torso_y = 0; if (item->itemFlags[2]) { if (item->itemFlags[2] == 1) item->hitPoints = 0; if (item->itemFlags[2] >= 12) TriggerDynamicLight(item->pos.xPos, item->pos.yPos, item->pos.zPos, (GetRandomControl() & 3) + 8, 0, (GetRandomControl() & 7) + 8, (GetRandomControl() & 7) + 16); else TriggerDynamicLight(item->pos.xPos, item->pos.yPos, item->pos.zPos, 25 - (item->itemFlags[2] << 1) + (GetRandomControl() & 1), (16 - item->itemFlags[2]) + (GetRandomControl() & 7), 32 - item->itemFlags[2], 31); } for (lp = 0; lp < 3; lp++) { trident[lp].x = londonboss_points[lp].x; trident[lp].y = londonboss_points[lp].y; trident[lp].z = londonboss_points[lp].z; GetJointAbsPosition(item, &trident[lp], londonboss_points[lp].meshNum); } if (item->hitPoints <= 0) { if (item->currentAnimState != LONDONBOSS_DEATH) { item->animNumber = Objects[item->objectNumber].animIndex + LONDONBOSS_DIE_ANIM; item->frameNumber = Anims[item->animNumber].frameBase; item->currentAnimState = LONDONBOSS_DEATH; } if (Anims[item->animNumber].frameEnd - item->frameNumber == 1) { item->frameNumber = Anims[item->animNumber].frameEnd - 1; item->meshBits = 0; // Don't draw any of the boss while I'm killing it. if (bossdata.explode_count == 0) { bossdata.ring_count = 0; for (lp = 0; lp < 6; lp++) { ExpRings[lp].on = false; ExpRings[lp].life = 32; ExpRings[lp].radius = 512; ExpRings[lp].speed = 128 + (lp << 5); ExpRings[lp].xrot = ((GetRandomControl() & 511) - 256) << 4; ExpRings[lp].zrot = ((GetRandomControl() & 511) - 256) << 4; } if (bossdata.dropped_icon == false) { //BossDropIcon(item_number); bossdata.dropped_icon = true; } } if (bossdata.explode_count < 256) bossdata.explode_count++; if (bossdata.explode_count > 128 && bossdata.ring_count == 6 && ExpRings[5].life == 0) { LondonBossDie(item_number); bossdata.dead = true; } else { ExplodeLondonBoss(item); } return; } } else { if (item->aiBits) GetAITarget(londonboss); CreatureAIInfo(item, &info); if (londonboss->enemy == LaraItem) { lara_info.angle = info.angle; lara_info.distance = info.distance; lara_info.xAngle = info.xAngle; } else { lara_dz = LaraItem->pos.zPos - item->pos.zPos; lara_dx = LaraItem->pos.xPos - item->pos.xPos; lara_dy = item->pos.yPos - LaraItem->pos.yPos; lara_info.angle = ATAN(lara_dz, lara_dx) - item->pos.yRot; //only need to fill out the bits of lara_info that will be needed by TargetVisible lara_info.distance = lara_dz * lara_dz + lara_dx * lara_dx; if (abs(lara_dx) > abs(lara_dz)) lara_info.xAngle = ATAN(abs(lara_dx) + (abs(lara_dz) >> 1), lara_dy); else lara_info.xAngle = ATAN(abs(lara_dz) + (abs(lara_dx) >> 1), lara_dy); } GetCreatureMood(item, &info, VIOLENT); CreatureMood(item, &info, VIOLENT); angle = CreatureTurn(item, londonboss->maximumTurn); enemy = londonboss->enemy; //TargetVisible uses enemy, so need to fill this in as lara if we're doing other things londonboss->enemy = LaraItem; if (item->hitStatus || lara_info.distance < LONDONBOSS_AWARE_DISTANCE || TargetVisible(item, &lara_info) || LaraItem->pos.yPos < item->pos.yPos) AlertAllGuards(item_number); londonboss->enemy = enemy; if (LaraItem->pos.yPos < item->pos.yPos) londonboss->hurtByLara = true; if (item->timer > 0) item->timer--; item->hitPoints = 300; switch (item->currentAnimState) { case LONDONBOSS_LAUGH: if (abs(lara_info.angle) < LONDONBOSS_WALK_TURN) item->pos.yRot += lara_info.angle; else if (lara_info.angle < 0) item->pos.yRot -= LONDONBOSS_WALK_TURN; else item->pos.yRot += LONDONBOSS_WALK_TURN; if (londonboss->alerted) { item->goalAnimState = LONDONBOSS_STAND; break; } break; case LONDONBOSS_STAND: londonboss->flags = 0; londonboss->maximumTurn = 0; if (londonboss->reachedGoal) //She's found this ambush point, so set up the next one { londonboss->reachedGoal = false; item->aiBits |= AMBUSH; item->itemFlags[3] += 0x2000; } head = lara_info.angle; if (item->aiBits & GUARD) { if ((head < -0x3000 || head > 0x3000) && item->pos.yPos > LONDONBOSS_FINAL_HEIGHT) { item->goalAnimState = LONDONBOSS_WALK; londonboss->maximumTurn = LONDONBOSS_WALK_TURN; } break; head = AIGuard(londonboss); if (!(GetRandomControl() & 0xF)) item->goalAnimState = LONDONBOSS_LAUGH; break; } else if ((item->pos.yPos <= LONDONBOSS_FINAL_HEIGHT || item->pos.yPos < LaraItem->pos.yPos) && !(GetRandomControl() & 0xF) && !bossdata.charged && item->timer) { item->goalAnimState = LONDONBOSS_LAUGH; } else if (londonboss->reachedGoal || LaraItem->pos.yPos > item->pos.yPos || item->pos.yPos <= LONDONBOSS_FINAL_HEIGHT) { if (bossdata.charged) item->goalAnimState = LONDONBOSS_BIGZAP; else if (!item->timer) item->goalAnimState = LONDONBOSS_SUMMON; else item->goalAnimState = LONDONBOSS_LILZAP; } else if (item->aiBits & PATROL1) { item->goalAnimState = LONDONBOSS_WALK; } else if (londonboss->mood == ESCAPE_MOOD || item->pos.yPos > LaraItem->pos.yPos) { item->goalAnimState = LONDONBOSS_RUN; } else if (londonboss->mood == BORED_MOOD || ((item->aiBits & FOLLOW) && (londonboss->reachedGoal || lara_info.distance > SQUARE(WALL_SIZE * 2)))) { if (item->requiredAnimState) item->goalAnimState = item->requiredAnimState; else if (info.ahead) item->goalAnimState = LONDONBOSS_STAND; else item->goalAnimState = LONDONBOSS_RUN; } else if (info.bite && info.distance < LONDONBOSS_WALK_RANGE) item->goalAnimState = LONDONBOSS_WALK; else item->goalAnimState = LONDONBOSS_RUN; break; case LONDONBOSS_WALK: head = lara_info.angle; londonboss->flags = 0; londonboss->maximumTurn = LONDONBOSS_WALK_TURN; if (item->aiBits & GUARD || (londonboss->reachedGoal && !(item->aiBits & FOLLOW))) { } else if (item->aiBits & PATROL1) { item->goalAnimState = LONDONBOSS_WALK; head = 0; } else if (londonboss->mood == ESCAPE_MOOD) { item->goalAnimState = LONDONBOSS_RUN; } else if (londonboss->mood == BORED_MOOD) { if (GetRandomControl() < LONDONBOSS_LAUGH_CHANCE) { item->requiredAnimState = LONDONBOSS_LAUGH; item->goalAnimState = LONDONBOSS_STAND; } } else if (info.distance > LONDONBOSS_WALK_RANGE) { item->goalAnimState = LONDONBOSS_RUN; } break; case LONDONBOSS_RUN: if (info.ahead) head = info.angle; londonboss->maximumTurn = LONDONBOSS_RUN_TURN; tilt = angle / 2; if (item->aiBits & GUARD || (londonboss->reachedGoal && !(item->aiBits & FOLLOW))) item->goalAnimState = LONDONBOSS_STAND; else if (londonboss->mood == ESCAPE_MOOD) break; else if ((item->aiBits & FOLLOW) && (londonboss->reachedGoal || lara_info.distance > SQUARE(WALL_SIZE * 2))) item->goalAnimState = LONDONBOSS_STAND; else if (londonboss->mood == BORED_MOOD) item->goalAnimState = LONDONBOSS_WALK; else if (info.ahead && info.distance < LONDONBOSS_WALK_RANGE) item->goalAnimState = LONDONBOSS_WALK; break; case LONDONBOSS_SUMMON: head = lara_info.angle; if (londonboss->reachedGoal) //She's found this ambush point, so set up the next one { londonboss->reachedGoal = false; item->aiBits = AMBUSH; item->itemFlags[3] += 0x2000; } if (item->animNumber == Objects[item->objectNumber].animIndex + LONDONBOSS_STND2SUM_ANIM) { if (item->frameNumber == Anims[item->animNumber].frameBase) { bossdata.hp_counter = item->hitPoints; item->timer = BIGZAP_TIMER; } else if (item->hitStatus && item->goalAnimState != LONDONBOSS_STAND) //if she takes *any* damage during the stand2summon anim, break off the summoning { StopSoundEffect(352); SoundEffect(353, &item->pos, 0); // Take Hit SoundEffect(355, &item->pos, 0); // Summon wind-down noise item->goalAnimState = LONDONBOSS_STAND; //Put a condition in here so she can do more than one attack per waypoint? } } else if (item->animNumber == Objects[item->objectNumber].animIndex + LONDONBOSS_SUMMON_ANIM && item->frameNumber == Anims[item->animNumber].frameEnd) bossdata.charged = true; if (abs(lara_info.angle) < LONDONBOSS_WALK_TURN) item->pos.yRot += lara_info.angle; else if (lara_info.angle < 0) item->pos.yRot -= LONDONBOSS_WALK_TURN; else item->pos.yRot += LONDONBOSS_WALK_TURN; // Summon. if ((Wibble & 7) == 0) { pos1.x = item->pos.xPos; pos1.y = (GetRandomControl() & 511) - 256; pos1.z = item->pos.zPos; TriggerLaserBolt(&pos1, item, SUMMON_BOLT, 0); for (lp = 0; lp < 6; lp++) { long rnd; if (!ExpRings[lp].on) { ExpRings[lp].on = true; ExpRings[lp].life = 64; ExpRings[lp].speed = (GetRandomControl() & 15) + 16; ExpRings[lp].x = item->pos.xPos; ExpRings[lp].y = item->pos.yPos + 128 - (rnd = (GetRandomControl() & 1023)); ExpRings[lp].z = item->pos.zPos; ExpRings[lp].xrot = ((GetRandomControl() & 511) - 256) << 4; ExpRings[lp].zrot = ((GetRandomControl() & 511) - 256) << 4; ExpRings[lp].radius = (1024 + (1024 - (abs(rnd - 512)))); break; } } } londonboss->maximumTurn = 0; break; case LONDONBOSS_BIGZAP: if (londonboss->reachedGoal) //She's found this ambush point, so set up the next one { londonboss->reachedGoal = false; item->aiBits = AMBUSH; item->itemFlags[3] += 0x2000; } bossdata.charged = false; if (abs(lara_info.angle) < LONDONBOSS_WALK_TURN) item->pos.yRot += lara_info.angle; else if (lara_info.angle < 0) item->pos.yRot -= LONDONBOSS_WALK_TURN; else item->pos.yRot += LONDONBOSS_WALK_TURN; torso_y = lara_info.angle; torso_x = lara_info.xAngle; londonboss->maximumTurn = 0; if (item->frameNumber == Anims[item->animNumber].frameBase + 36) { TriggerLaserBolt(&trident[RIGHT_PRONG], item, NORMAL_BOLT, item->pos.yRot + 0x200); TriggerLaserBolt(&trident[ICONPOS], item, LARGE_BOLT, item->pos.yRot); TriggerLaserBolt(&trident[LEFT_PRONG], item, NORMAL_BOLT, item->pos.yRot - 0x200); } break; case LONDONBOSS_LILZAP: if (londonboss->reachedGoal) //She's found this ambush point, so set up the next one { londonboss->reachedGoal = false; item->aiBits = AMBUSH; item->itemFlags[3] += 0x2000; } if (abs(lara_info.angle) < LONDONBOSS_WALK_TURN) item->pos.yRot += lara_info.angle; else if (lara_info.angle < 0) item->pos.yRot -= LONDONBOSS_WALK_TURN; else item->pos.yRot += LONDONBOSS_WALK_TURN; torso_y = lara_info.angle; torso_x = lara_info.xAngle; londonboss->maximumTurn = 0; if (item->frameNumber == Anims[item->animNumber].frameBase + 14) { TriggerLaserBolt(&trident[RIGHT_PRONG], item, NORMAL_BOLT, item->pos.yRot + 0x200); TriggerLaserBolt(&trident[LEFT_PRONG], item, NORMAL_BOLT, item->pos.yRot - 0x200); } break; } } CreatureTilt(item, tilt); CreatureJoint(item, 0, torso_y); CreatureJoint(item, 1, torso_x); CreatureJoint(item, 2, head); if ((item->currentAnimState < LONDONBOSS_VAULT2 || item->currentAnimState > LONDONBOSS_GODOWN) && item->currentAnimState != LONDONBOSS_DEATH) { switch (CreatureVault(item_number, angle, 2, LONDONBOSS_VAULT_SHIFT)) { case 2: /* Half block jump */ londonboss->maximumTurn = 0; item->animNumber = Objects[item->objectNumber].animIndex + LONDONBOSS_VAULT2_ANIM; item->frameNumber = Anims[item->animNumber].frameBase; item->currentAnimState = LONDONBOSS_VAULT2; break; case 3: /* 3/4 block jump */ londonboss->maximumTurn = 0; item->animNumber = Objects[item->objectNumber].animIndex + LONDONBOSS_VAULT3_ANIM; item->frameNumber = Anims[item->animNumber].frameBase; item->currentAnimState = LONDONBOSS_VAULT3; break; case 4: /* Full block jump */ londonboss->maximumTurn = 0; item->animNumber = Objects[item->objectNumber].animIndex + LONDONBOSS_VAULT4_ANIM; item->frameNumber = Anims[item->animNumber].frameBase; item->currentAnimState = LONDONBOSS_VAULT4; break; case -4: /* Full block fall */ londonboss->maximumTurn = 0; item->animNumber = Objects[item->objectNumber].animIndex + LONDONBOSS_GODOWN_ANIM; item->frameNumber = Anims[item->animNumber].frameBase; item->currentAnimState = LONDONBOSS_GODOWN; break; } } else { londonboss->maximumTurn = 0; CreatureAnimation(item_number, angle, 0); } // Do light. { long g, b; g = abs(rcossin_tbl[item->itemFlags[1] << 7] >> 7); g += (GetRandomControl() & 7); if (g > 31) g = 31; b = g >> 1; TriggerDynamicLight(trident[ICONPOS].x, trident[ICONPOS].y, trident[ICONPOS].z, 10, 0, g >> 1, b >> 1); item->itemFlags[1]++; item->itemFlags[1] &= 63; } // Knockback. if (item->hitPoints > 0 && item->itemFlags[0] != KNOCKBACK && LaraItem->hitPoints > 0) { long dx, dy, dz; dx = LaraItem->pos.xPos - item->pos.xPos; dy = LaraItem->pos.yPos - 256 - item->pos.yPos; dz = LaraItem->pos.zPos - item->pos.zPos; if (dx > 8000 || dx < -8000 || dy > 8000 || dy < -8000 || dz > 8000 || dz < -8000) { dx = 0xFFF; } else { dx = SQUARE(dx); dy = SQUARE(dy); dz = SQUARE(dz); dx = SQRT_ASM(dx + dy + dz); } if (dx < 0xB00) { item->itemFlags[0] = KNOCKBACK; for (lp = 0; lp < 3; lp++) { KBRings[lp].on = true; KBRings[lp].life = 32; KBRings[lp].speed = 64 + ((lp == 1) << 4); KBRings[lp].x = item->pos.xPos; KBRings[lp].y = item->pos.yPos - 384 - 128 + (lp << 7); KBRings[lp].z = item->pos.zPos; KBRings[lp].xrot = KBRings[lp].zrot = 0; KBRings[lp].radius = 512 + ((lp == 1) << 8); } } } else if (KBRings[0].on == false && KBRings[1].on == false && KBRings[2].on == false) { item->itemFlags[0] = 0; } } void S_DrawLondonBoss(ITEM_INFO* item) { DrawAnimatingItem(item); if (bossdata.explode_count) { DrawLondonBossShield(item); DrawExplosionRings(); } else { DrawSummonRings(); DrawKnockBackRings(); } if (item->hitPoints <= 0 && bossdata.explode_count == 0) { //UpdateElectricityPoints(); //LaraElectricDeath(0, item); //LaraElectricDeath(1, item); } }