diff --git a/TR5Main/Objects/TR4/Entity/tr4_wraith.cpp b/TR5Main/Objects/TR4/Entity/tr4_wraith.cpp index fd3e10af0..37a45936f 100644 --- a/TR5Main/Objects/TR4/Entity/tr4_wraith.cpp +++ b/TR5Main/Objects/TR4/Entity/tr4_wraith.cpp @@ -7,24 +7,13 @@ #include "trmath.h" #include #include +#include +#include constexpr auto WRAITH_COUNT = 8; short WraithSpeed = 64; -struct WRAITH_INFO -{ - int xPos; - int yPos; - int zPos; - short xRot; - short yRot; - short zRot; - unsigned char unknown01; - unsigned char unknown02; - unsigned char unknown03; -}; - void InitialiseWraith(short itemNumber) { ITEM_INFO* item; @@ -46,9 +35,9 @@ void InitialiseWraith(short itemNumber) wraithData->zRot = 0; wraithData->yPos = 0; wraithData->xPos = 0; - wraithData->unknown01 = 0; - wraithData->unknown02 = 0; - wraithData->unknown03 = 0; + wraithData->r = 0; + wraithData->g = 0; + wraithData->b = 0; wraithData++; } @@ -66,7 +55,7 @@ void WraithControl(short itemNumber) else target = LaraItem; - int x, y, z, distance, dx, dy, dz; + int x, y, z, distance, dx, dy, dz, oldX, oldY, oldZ; if (target == LaraItem || target->objectNumber == 445) { @@ -163,11 +152,13 @@ void WraithControl(short itemNumber) FLOOR_INFO* floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &roomNumber); int height = GetFloorHeight(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos); int ceiling = GetCeiling(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos); - int doEffect = 0; + bool hitWall = false; if (height < item->pos.yPos || ceiling > item->pos.yPos) - { - doEffect = 1; - } + hitWall = true; + + oldX = item->pos.xPos; + oldY = item->pos.yPos; + oldZ = item->pos.zPos; item->pos.xPos += item->speed * phd_sin(item->pos.yRot) >> W2V_SHIFT; item->pos.yPos += item->speed * phd_sin(item->pos.xRot) >> W2V_SHIFT; @@ -200,9 +191,244 @@ void WraithControl(short itemNumber) item->hitPoints = linkNum; } } + + if (item->objectNumber != ID_WRAITH3) + { + // WRAITH1 AND WRAITH2 can die on contact with water + // WRAITH1 dies because it's fire and it dies on contact with water, WRAITH2 instead triggers a flipmap for making icy water + if (g_Level.Rooms[item->roomNumber].flags & ENV_FLAG_WATER) + { + TriggerExplosionSparks(item->pos.xPos, item->pos.yPos, item->pos.zPos, 2, -2, 1, item->roomNumber); + item->itemFlags[1]--; + if (item->itemFlags[1] < -1) + { + if (item->itemFlags[1] < 30) + { + if (item->objectNumber == ID_WRAITH2) + { + if (item->triggerFlags) + { + if (!FlipStats[item->triggerFlags]) + { + DoFlipMap(item->triggerFlags); + FlipStats[item->triggerFlags] = 1; + } + } + } + KillItem(itemNumber); + } + } + else + { + item->itemFlags[1] = -1; + } + } + else + { + item->itemFlags[1]--; + if (item->itemFlags[1] < 0) + { + item->itemFlags[1] = 0; + } + } + } + + if (distance >= 28900 || abs(item->pos.yPos - target->pos.yPos + 384) >= 256) + { + if (Wibble & 16) + { + if (item->speed < WraithSpeed) + { + item->speed++; + } + if (item->hitPoints) + { + if (item->TOSSPAD & 0x3E00) + { + item->TOSSPAD = ((item->TOSSPAD & 0xFE00) - 1) & 0x3E00; + } + } + } + } + else + { + if (item->speed > 32) + { + item->speed -= 12; + } + if (target == LaraItem) + { + target->hitPoints -= distance / 1024; + + // WRAITH1 can burn Lara + if (item->objectNumber == ID_WRAITH1) + { + item->itemFlags[1] += 400; + if (item->itemFlags[1] > 8000) + { + LaraBurn(); + } + } + } + else if (target->objectNumber == ID_ANIMATING10) + { + // ANIMATING10 is the sacred pedistal that can kill WRAITH + item->TOSSPAD = ((item->TOSSPAD & 0xFE00) + 512) & 0x3E00; // HACK: maybe create new var for this + if (item->TOSSPAD & 0x3E00 > 12800) + { + item->pos.xPos = target->pos.xPos; + item->pos.yPos = target->pos.yPos - 384; + item->pos.zPos = target->pos.zPos; + WraithExplosionEffect(item, 96, 96, 96, -32); + WraithExplosionEffect(item, 48, 48, 48, 48); + target->triggerFlags--; + target->hitPoints = 0; + if (target->triggerFlags > 0) + { + target->frameNumber = g_Level.Anims[target->animNumber].frameBase; + } + KillItem(itemNumber); + } + } + else + { + // Target is another WRAITH (fire vs ice), they kill both themselves + item->TOSSPAD = item->TOSSPAD & 0xD5FF | 0x1400; + if (item->TOSSPAD & 0x3E00) + { + TriggerExplosionSparks(item->pos.xPos, item->pos.yPos, item->pos.zPos, 2, -2, 1, item->roomNumber); + target->hitPoints = 0; + KillItem(item->hitPoints); + KillItem(itemNumber); + } + } + } + + // Check if WRAITH is going below floor or above ceiling and trigger sparks + roomNumber = item->roomNumber; + floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &roomNumber); + if (GetFloorHeight(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos) < item->pos.yPos + || GetCeiling(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos) > item->pos.yPos) + { + if (!hitWall) + { + WraithWallsEffect(oldX, oldY, oldZ, item->pos.yRot + -ANGLE(180), item->objectNumber); + } + } + else if (hitWall) + { + WraithWallsEffect(item->pos.xPos, item->pos.yPos, item->pos.zPos, item->pos.yRot, item->objectNumber); + } + + // Update WRAITH nodes + WRAITH_INFO* creature = (WRAITH_INFO*)item->data; + int j = 0; + for (int i = WRAITH_COUNT - 1; i > 0; i--) + { + creature[i - 1].xPos += (creature[i - 1].xRot / 16); + creature[i - 1].yPos += (creature[i - 1].yRot / 16); + creature[i - 1].zPos += (creature[i - 1].zRot / 16); + + creature[i - 1].xRot -= (creature[i - 1].xRot / 16); + creature[i - 1].yRot -= (creature[i - 1].yRot / 16); + creature[i - 1].zRot -= (creature[i - 1].zRot / 16); + + creature[i].xPos = creature[i - 1].xPos; + creature[i].yPos = creature[i - 1].yPos; + creature[i].zPos = creature[i - 1].zPos; + + creature[i].xRot = creature[i - 1].xRot; + creature[i].yRot = creature[i - 1].yRot; + creature[i].zRot = creature[i - 1].zRot; + + if (item->objectNumber == ID_WRAITH1) + { + creature[i].r = (GetRandomControl() & 0x3F) - 64; + creature[i].g = 16 * (j + 1) + (GetRandomControl() & 0x3F); + creature[i].b = GetRandomControl() & 0xF; + } + else if (item->objectNumber == ID_WRAITH2) + { + creature[i].r = GetRandomControl() & 0xF; + creature[i].g = 16 * (j + 1) + (GetRandomControl() & 0x3F); + creature[i].b = (GetRandomControl() & 0x3F) - 64; + } + else + { + creature[i].r = 8 * (j + 2) + (GetRandomControl() & 0x3F); + creature[i].g = creature[i].r; + creature[i].b = creature[i].r + (GetRandomControl() & 0xF); + } + + j++; + } + + creature[0].xPos = item->pos.xPos; + creature[0].yPos = item->pos.yPos; + creature[0].zPos = item->pos.zPos; + + creature[0].xRot = (item->pos.xPos - oldX); + creature[0].yRot = (item->pos.yPos - oldY); + creature[0].zRot = (item->pos.zPos - oldZ); + + // Standard WRAITH drawing code + DrawWraith( + item->pos.xPos, + item->pos.yPos, + item->pos.zPos, + creature[0].xRot, + creature[0].yRot, + creature[0].zRot, + item->objectNumber); + + DrawWraith( + (oldX + item->pos.xPos) / 2, + (oldY + item->pos.yPos) / 2, + (oldZ + item->pos.zPos) / 2, + creature[0].xRot, + creature[0].yRot, + creature[0].zRot, + item->objectNumber); + + // Lighting for WRAITH + byte r, g, b; + if (item->objectNumber == ID_WRAITH3) + { + r = creature[5].r; + g = creature[5].g; + b = creature[5].b; + } + else + { + r = creature[1].r; + g = creature[1].g; + b = creature[1].b; + } + + TriggerDynamicLight( + creature[0].xPos, + creature[0].yPos, + creature[0].zPos, + 16, + r, g, b); } -void WraithEffects(int x, int y, int z, short xVel, short yVel, short zVel, int objNumber) +void WraithExplosionEffect(ITEM_INFO* item, byte r, byte g, byte b, int speed) +{ + short inner = speed >= 0 ? 32 : 640; + short outer = speed >= 0 ? 160 : 512; + + item->pos.yPos -= 384; + + TriggerShockwave(&item->pos, inner, outer, speed, r, g, b, 24, 0, 0); + TriggerShockwave(&item->pos, inner, outer, speed, r, g, b, 24, ANGLE(45), 0); + TriggerShockwave(&item->pos, inner, outer, speed, r, g, b, 24, ANGLE(90), 0); + TriggerShockwave(&item->pos, inner, outer, speed, r, g, b, 24, ANGLE(135), 0); + + item->pos.yPos += 384; +} + +void DrawWraith(int x, int y, int z, short xVel, short yVel, short zVel, int objNumber) { unsigned char size, life; BYTE color; @@ -227,7 +453,6 @@ void WraithEffects(int x, int y, int z, short xVel, short yVel, short zVel, int spark->dR = 24; spark->dG = (GetRandomControl() & 0x1F) + 64; } - else { color = (GetRandomControl() & 0x1F) + 64; spark->dG = color; @@ -247,7 +472,7 @@ void WraithEffects(int x, int y, int z, short xVel, short yVel, short zVel, int spark->x = (GetRandomControl() & 0x1F) + x - 16; spark->y = y; spark->friction = 85; - spark->flags = 522; + spark->flags = SP_EXPDEF | SP_DEF | SP_SCALE; spark->z = (GetRandomControl() & 0x1F) + z - 16; spark->xVel = xVel; spark->yVel = yVel; @@ -261,70 +486,73 @@ void WraithEffects(int x, int y, int z, short xVel, short yVel, short zVel, int spark->size = size; } -/* -void DrawWraithEffect(int x, int y, int z, short yrot, short objNumber) +void WraithWallsEffect(int x, int y, int z, short yrot, short objNumber) { - BYTE life; - unsigned char size; - signed int i; - int objNumbera,v5,v6,v7,v8,v9,v10,v11,v13; - SPARKS* spark; - spark = &Sparks[GetFreeSpark()]; - + byte sR, sG, sB, dR, dG, dB; + short color; + if (objNumber == ID_WRAITH1) { - v5 = GetRandomControl() << 8; - v6 = ((GetRandomControl() | v5) << 32) | 0x18000001; - v7 = ((GetRandomControl() & 0x1F) + 48) << 8; - v8 = (((GetRandomControl() & 0x1F) + 128) << 16) | v7 | 0x18; + sR = (GetRandomControl() & 0x1F) + -128; + sB = 24; + sG = (GetRandomControl() & 0x1F) + 48; + dR = (GetRandomControl() & 0x1F) + -128; + dB = 24; + dG = (GetRandomControl() & 0x1F) + 64; } else if (objNumber == ID_WRAITH2) { - v9 = ((GetRandomControl() & 0x1F) << 24) - 0x7FFFFFFF; - v6 = (GetRandomControl() << 32) | v9 | 1; - v10 = ((GetRandomControl() & 0x1F) + 48) << 8; - v8 = ((GetRandomControl() & 0x1F) + 128) | v10 | 0x180000; + sB = (GetRandomControl() & 0x1F) + -128; + sR = 24; + sG = (GetRandomControl() & 0x1F) + -128; + dB = (GetRandomControl() & 0x1F) + -128; + dR = 24; + dG = (GetRandomControl() & 0x1F) + 64; } else { - v11 = (GetRandomControl() & 0xF) + 64; - v6 = ((v11 | ((v11 | (v11 << 8)) << 8)) << 24) | 1; - v8 = ((v11 | (v11 << 8)) << 8) | (v11 + (GetRandomControl() & 0xF)); + color = (GetRandomControl() & 0x1F) + 64; + dG = color; + dR = color; + sB = color; + sG = color; + sR = color; + dB = sB + (GetRandomControl() & 0x1F); } - objNumbera = (v8 & 0xFF00) | (v8 << 16) | BYTE2(v8); - for (int i = 0; i < 15; i++) { - *&spark->on = v6; - *&spark->dR = objNumbera; - *&spark->colFadeSpeed = 1796; - v13 = GetRandomControl(); + SPARKS* spark = &Sparks[GetFreeSpark()]; + spark->on = true; + spark->sR = dR; + spark->sG = dG; + spark->sB = dB; + spark->dR = dR; + spark->dG = dG; + spark->dB = dB; + spark->colFadeSpeed = 4; + spark->fadeToBlack = 7; spark->transType = COLADD; - life = (v13 & 7) + 32; + short life = (GetRandomControl() & 7) + 32; spark->life = life; spark->sLife = life; spark->x = (GetRandomControl() & 0x1F) + x - 16; spark->y = (GetRandomControl() & 0x1F) + y - 16; spark->z = (GetRandomControl() & 0x1F) + z - 16; - v15 = GetRandomControl() - 0x4000; - v16 = (GetRandomControl() & 0x3FF) + 1024; - v17 = ((yrot + v15) >> 3) & 0x1FFE; - spark->xVel = v16 * 4 * rcossin_tbl[v17] >> 14; + short rot = yrot + GetRandomControl() - ANGLE(90); + short velocity = ((GetRandomControl() & 0x3FF) + 1024); + spark->xVel = velocity * phd_sin(rot) >> W2V_SHIFT; spark->yVel = (GetRandomControl() & 0x7F) - 64; - spark->zVel = v16 * 4 * rcossin_tbl[v17 + 1] >> 14; + spark->zVel = velocity * phd_cos(rot) >> W2V_SHIFT; spark->friction = 4; - spark->flags = 522; - v18 = GetRandomControl(); + spark->flags = SP_EXPDEF | SP_DEF | SP_SCALE; spark->maxYvel = 0; spark->scalar = 3; - spark->gravity = (v18 & 0x7F) - 64; - size = (GetRandomControl() & 0x1F) + 48; + spark->gravity = (GetRandomControl() & 0x7F) - 64; + short size = (GetRandomControl() & 0x1F) + 48; spark->sSize = size; spark->size = size; spark->dSize = size >> 2; } - } -*/ void DrawWraith(ITEM_INFO* item) { diff --git a/TR5Main/Objects/TR4/Entity/tr4_wraith.h b/TR5Main/Objects/TR4/Entity/tr4_wraith.h index 252accf6e..7a992817b 100644 --- a/TR5Main/Objects/TR4/Entity/tr4_wraith.h +++ b/TR5Main/Objects/TR4/Entity/tr4_wraith.h @@ -1,10 +1,23 @@ #pragma once #include "items.h" +struct WRAITH_INFO +{ + int xPos; + int yPos; + int zPos; + short xRot; + short yRot; + short zRot; + byte r; + byte g; + byte b; +}; void InitialiseWraith(short itemNumber); void WraithControl(short itemNumber); -void WraithEffects(int x, int y, int z, short xVel, short yVel, short zVel, int objNumber); -void DrawWraithEffect(int x, int y, int z, short yrot, short objNumber); +void WraithWallsEffect(int x, int y, int z, short yrot, short objNumber); +void DrawWraith(int x, int y, int z, short xVel, short yVel, short zVel, int objNumber); void DrawWraith(ITEM_INFO* item); void KillWraith(ITEM_INFO* item); +void WraithExplosionEffect(ITEM_INFO* item, byte r, byte g, byte b, int speed); \ No newline at end of file diff --git a/TR5Main/Objects/TR4/tr4_objects.cpp b/TR5Main/Objects/TR4/tr4_objects.cpp index 422fb9802..718382e25 100644 --- a/TR5Main/Objects/TR4/tr4_objects.cpp +++ b/TR5Main/Objects/TR4/tr4_objects.cpp @@ -580,11 +580,38 @@ static void StartBaddy(OBJECT_INFO* obj) obj->zoneType = ZONE_BASIC; } + obj = &Objects[ID_WRAITH1]; + if (obj->loaded) + { + obj->initialise = InitialiseWraith; + obj->control = WraithControl; + obj->savePosition = true; + obj->saveHitpoints = true; + obj->saveFlags = true; + obj->saveAnim = true; + } + + obj = &Objects[ID_WRAITH2]; + if (obj->loaded) + { + obj->initialise = InitialiseWraith; + obj->control = WraithControl; + obj->savePosition = true; + obj->saveHitpoints = true; + obj->saveFlags = true; + obj->saveAnim = true; + } + obj = &Objects[ID_WRAITH3]; if (obj->loaded) { - //not decompiled yet and multiple versions of wraiths exist - } + obj->initialise = InitialiseWraith; + obj->control = WraithControl; + obj->savePosition = true; + obj->saveHitpoints = true; + obj->saveFlags = true; + obj->saveAnim = true; + } } static void StartObject(OBJECT_INFO* obj)