Implemented Mutant and Locusts

- Added ItemNearTarget().
This commit is contained in:
TokyoSU 2020-06-10 21:38:25 +02:00
parent b58fc1473e
commit 2b2a58d44c
17 changed files with 816 additions and 140 deletions

View file

@ -36,6 +36,54 @@ typedef enum ZoneType
ZONE_APE, // only 2 click climb ZONE_APE, // only 2 click climb
}; };
typedef struct OBJECT_BONES
{
short bone0;
short bone1;
short bone2;
short bone3;
OBJECT_BONES()
{
this->bone0 = 0;
this->bone1 = 0;
this->bone2 = 0;
this->bone3 = 0;
}
OBJECT_BONES(short all)
{
this->bone0 = all;
this->bone1 = all;
this->bone2 = -all;
this->bone3 = -all;
}
OBJECT_BONES(short angleY, short angleX)
{
this->bone0 = angleY;
this->bone1 = angleX;
this->bone2 = angleY;
this->bone3 = angleX;
}
OBJECT_BONES(short angleY, short angleX, bool total)
{
this->bone0 = angleY;
this->bone1 = angleX;
if (total)
{
this->bone2 = angleY;
this->bone3 = angleX;
}
else
{
this->bone2 = 0;
this->bone3 = 0;
}
}
};
typedef struct BOX_NODE typedef struct BOX_NODE
{ {
short exitBox; short exitBox;

View file

@ -38,6 +38,7 @@
#include "tr5_rats_emitter.h" #include "tr5_rats_emitter.h"
#include "tr5_bats_emitter.h" #include "tr5_bats_emitter.h"
#include "tr5_spider_emitter.h" #include "tr5_spider_emitter.h"
#include "tr4_locusts.h"
short ShatterSounds[18][10] = short ShatterSounds[18][10] =
{ {
@ -512,6 +513,7 @@ GAME_STATUS ControlPhase(int numFrames, int demoMode)
UpdateBats(); UpdateBats();
UpdateSpiders(); UpdateSpiders();
UpdateShockwaves(); UpdateShockwaves();
UpdateLocusts();
//Legacy_UpdateLightning(); //Legacy_UpdateLightning();
AnimateWaterfalls(); AnimateWaterfalls();

View file

@ -18,6 +18,7 @@
#include "tr5_bats_emitter.h" #include "tr5_bats_emitter.h"
#include "tr5_spider_emitter.h" #include "tr5_spider_emitter.h"
constexpr auto ITEM_RADIUS_YMAX = SECTOR(3);
int wf = 256; int wf = 256;
extern std::deque<FOOTPRINT_STRUCT> footprints; extern std::deque<FOOTPRINT_STRUCT> footprints;
short FXType; short FXType;
@ -474,51 +475,85 @@ void ControlWaterfallMist(short itemNumber) // ControlWaterfallMist
short DoBloodSplat(int x, int y, int z, short a4, short a5, short roomNumber) short DoBloodSplat(int x, int y, int z, short a4, short a5, short roomNumber)
{ {
GetFloor(x, y, z, &roomNumber); short roomNum = roomNumber;
if (Rooms[roomNumber].flags & ENV_FLAG_WATER) GetFloor(x, y, z, &roomNum);
if (Rooms[roomNum].flags & ENV_FLAG_WATER)
TriggerUnderwaterBlood(x, y, z, a4); TriggerUnderwaterBlood(x, y, z, a4);
else else
TriggerBlood(x, y, z, a5 >> 4, a4); TriggerBlood(x, y, z, a5 >> 4, a4);
return 0; return 0;
} }
int ItemNearLara(PHD_3DPOS* pos, int radius) static bool ItemCollide(int value, int radius)
{ {
int dx = pos->xPos - LaraItem->pos.xPos; return value >= -radius && value <= radius;
int dy = pos->yPos - LaraItem->pos.yPos; }
int dz = pos->zPos - LaraItem->pos.zPos;
if (dx >= -radius static bool ItemInRange(int x, int z, int radius)
&& dx <= radius {
&& dz >= -radius return (SQUARE(x) + SQUARE(z)) <= SQUARE(radius);
&& dz <= radius }
&& dy >= -3072
&& dy <= 3072
&& SQUARE(dx) + SQUARE(dz) <= SQUARE(radius))
{
short* bounds = GetBoundsAccurate(LaraItem);
if (dy >= bounds[2] && dy <= bounds[3] + 100)
return 1;
}
return 0; bool ItemNearLara(PHD_3DPOS* pos, int radius)
{
ANIM_FRAME* bounds;
GAME_VECTOR target;
target.x = pos->xPos - LaraItem->pos.xPos;
target.y = pos->yPos - LaraItem->pos.yPos;
target.z = pos->zPos - LaraItem->pos.zPos;
if (!ItemCollide(target.y, ITEM_RADIUS_YMAX))
return false;
if (!ItemCollide(target.x, radius) || !ItemCollide(target.z, radius))
return false;
if (!ItemInRange(target.x, target.z, radius))
return false;
bounds = (ANIM_FRAME*)GetBoundsAccurate(LaraItem);
if (target.y >= bounds->MinY && target.y <= (bounds->MaxY + LARA_RAD))
return true;
return false;
}
bool ItemNearTarget(PHD_3DPOS* src, ITEM_INFO* target, int radius)
{
ANIM_FRAME* bounds;
PHD_VECTOR pos;
pos.x = src->xPos - target->pos.xPos;
pos.y = src->yPos - target->pos.yPos;
pos.z = src->zPos - target->pos.zPos;
if (!ItemCollide(pos.y, ITEM_RADIUS_YMAX))
return false;
if (!ItemCollide(pos.x, radius) || !ItemCollide(pos.z, radius))
return false;
if (!ItemInRange(pos.x, pos.z, radius))
return false;
bounds = (ANIM_FRAME*)GetBoundsAccurate(target);
if (pos.y >= bounds->MinY && pos.y <= bounds->MaxY)
return true;
return false;
} }
void Richochet(PHD_3DPOS* pos) void Richochet(PHD_3DPOS* pos)
{ {
short angle = mGetAngle(pos->zPos, pos->xPos, LaraItem->pos.zPos, LaraItem->pos.xPos); short angle = mGetAngle(pos->zPos, pos->xPos, LaraItem->pos.zPos, LaraItem->pos.xPos);
TriggerRicochetSpark((GAME_VECTOR*)pos, angle / 16, 3, 0); GAME_VECTOR target;
target.x = pos->xPos;
target.y = pos->yPos;
target.z = pos->zPos;
TriggerRicochetSpark(&target, angle / 16, 3, 0);
SoundEffect(SFX_LARA_RICOCHET, pos, 0); SoundEffect(SFX_LARA_RICOCHET, pos, 0);
} }
void DoLotsOfBlood(int x, int y, int z, int speed, short direction, short roomNumber, int count) void DoLotsOfBlood(int x, int y, int z, int speed, short direction, short roomNumber, int count)
{ {
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
DoBloodSplat(x + 256 - (GetRandomControl() * 512 / 0x8000), DoBloodSplat(x + 256 - (GetRandomControl() * 512 / 0x8000),
y + 256 - (GetRandomControl() * 512 / 0x8000), y + 256 - (GetRandomControl() * 512 / 0x8000),
z + 256 - (GetRandomControl() * 512 / 0x8000), z + 256 - (GetRandomControl() * 512 / 0x8000),
speed, direction, roomNumber); speed, direction, roomNumber);
} }
} }

View file

@ -23,7 +23,8 @@ struct FX_INFO
extern function<EffectFunction> effect_routines[]; extern function<EffectFunction> effect_routines[];
extern FX_INFO* Effects; extern FX_INFO* Effects;
int ItemNearLara(PHD_3DPOS* pos, int radius); bool ItemNearLara(PHD_3DPOS* pos, int radius);
bool ItemNearTarget(PHD_3DPOS* src, ITEM_INFO* target, int radius);
void StopSoundEffect(short sampleIndex); void StopSoundEffect(short sampleIndex);
short DoBloodSplat(int x, int y, int z, short speed, short yRot, short roomNumber); short DoBloodSplat(int x, int y, int z, short speed, short yRot, short roomNumber);
//void SoundEffects(); //void SoundEffects();

View file

@ -20,6 +20,34 @@ CREATURE_INFO* GetCreatureInfo(ITEM_INFO* item)
return (CREATURE_INFO*)item->data; return (CREATURE_INFO*)item->data;
} }
void TargetNearestEntity(ITEM_INFO* item, CREATURE_INFO* creature)
{
ITEM_INFO* target;
int bestdistance;
int distance;
int x, z;
bestdistance = MAXINT;
for (int i = 0; i < LevelItems; i++)
{
target = &Items[i];
if (target != nullptr)
{
if (target != item && target->hitPoints > 0 && target->status != ITEM_INVISIBLE)
{
x = target->pos.xPos - item->pos.xPos;
z = target->pos.zPos - item->pos.zPos;
distance = SQUARE(z) + SQUARE(x);
if (distance < bestdistance)
{
creature->enemy = target;
bestdistance = distance;
}
}
}
}
}
void GetRoomList(short roomNumber, short* roomArray, short* numRooms) void GetRoomList(short roomNumber, short* roomArray, short* numRooms)
{ {
short numDoors, *door, adjoiningRoom; short numDoors, *door, adjoiningRoom;

View file

@ -21,6 +21,7 @@ enum LARA_MESH_MASK
short GF(short animIndex, short frameToStart); // for lara short GF(short animIndex, short frameToStart); // for lara
short GF2(short objectID, short animIndex, short frameToStart); // for entity short GF2(short objectID, short animIndex, short frameToStart); // for entity
CREATURE_INFO* GetCreatureInfo(ITEM_INFO* item); CREATURE_INFO* GetCreatureInfo(ITEM_INFO* item);
void TargetNearestEntity(ITEM_INFO* item, CREATURE_INFO* creature);
void GetRoomList(short roomNumber, short* roomArray, short* numRooms); // return the value via roomArray and numRooms void GetRoomList(short roomNumber, short* roomArray, short* numRooms); // return the value via roomArray and numRooms
void GetRoomList(short roomNumber, vector<short>* DestRoomList); // return the value via DestRoomList void GetRoomList(short roomNumber, vector<short>* DestRoomList); // return the value via DestRoomList

View file

@ -9,6 +9,7 @@
#include "effect.h" #include "effect.h"
#include "level.h" #include "level.h"
#include "lara.h" #include "lara.h"
#include "tr4_mutant.h"
void BubblesEffect1(short fxNum, short xVel, short yVel, short zVel) void BubblesEffect1(short fxNum, short xVel, short yVel, short zVel)
{ {
@ -112,58 +113,6 @@ void BubblesEffect2(short fxNum, short xVel, short yVel, short zVel)
} }
} }
void BubblesEffect3(short fxNum, short xVel, short yVel, short zVel)
{
FX_INFO* fx = &Effects[fxNum];
int dx = LaraItem->pos.xPos - fx->pos.xPos;
int dz = LaraItem->pos.zPos - fx->pos.zPos;
if (dx >= -16384 && dx <= 16384 && dz >= -16384 && dz <= 16384)
{
SPARKS* spark = &Sparks[GetFreeSpark()];
spark->on = 1;
spark->sB = 0;
spark->sR = (GetRandomControl() & 0x3F) + -128;
spark->sG = spark->sG >> 1;
spark->dB = 0;
spark->dR = (GetRandomControl() & 0x3F) + -128;
spark->dG = spark->dG >> 1;
spark->fadeToBlack = 8;
spark->colFadeSpeed = (GetRandomControl() & 3) + 8;
spark->transType = COLADD;
spark->dynamic = -1;
spark->life = spark->sLife = (GetRandomControl() & 7) + 32;
spark->y = 0;
spark->x = (GetRandomControl() & 0xF) - 8;
spark->z = (GetRandomControl() & 0xF) - 8;
spark->x += fx->pos.xPos;
spark->y += fx->pos.yPos;
spark->z += fx->pos.zPos;
spark->xVel = xVel;
spark->yVel = yVel;
spark->zVel = zVel;
spark->friction = 34;
spark->flags = 538;
spark->rotAng = GetRandomControl() & 0xFFF;
if (GetRandomControl() & 1)
{
spark->rotAdd = -32 - (GetRandomControl() & 0x1F);
}
else
{
spark->rotAdd = (GetRandomControl() & 0x1F) + 32;
}
spark->gravity = 0;
spark->maxYvel = 0;
spark->fxObj = fxNum;
spark->scalar = 2;
spark->sSize = spark->size = (GetRandomControl() & 0xF) + 128;
spark->dSize = spark->size >> 2;
}
}
void BubblesEffect4(short fxNum, short xVel, short yVel, short zVel) void BubblesEffect4(short fxNum, short xVel, short yVel, short zVel)
{ {
FX_INFO* fx = &Effects[fxNum]; FX_INFO* fx = &Effects[fxNum];
@ -463,29 +412,25 @@ void BubblesControl(short fxNum)
int dy = oldY - fx->pos.yPos; int dy = oldY - fx->pos.yPos;
int dz = oldZ - fx->pos.zPos; int dz = oldZ - fx->pos.zPos;
if (Wibble & 4 || fx->flag1 == 1 || fx->flag1 == 5 || fx->flag1 == 2) if (Wibble & 4)
{ {
if (fx->flag1) switch (fx->flag1)
{ {
if (fx->flag1 == 1) default:
{ case 1:
BubblesEffect1(fxNum, 32 * dx, 32 * dy, 32 * dz); BubblesEffect1(fxNum, 32 * dx, 32 * dy, 32 * dz);
} break;
else if (fx->flag1 < 3 || fx->flag1 > 5) case 2:
{ BubblesEffect2(fxNum, 16 * dx, 16 * dy, 16 * dz);
if (fx->flag1 == 2) break;
BubblesEffect2(fxNum, 16 * dx, 16 * dy, 16 * dz); case 3:
else if (fx->flag1 == 6) case 4:
BubblesEffect3(fxNum, 16 * dx, 16 * dy, 16 * dz); case 5:
}
else
{
BubblesEffect4(fxNum, 16 * dx, 16 * dy, 16 * dz); BubblesEffect4(fxNum, 16 * dx, 16 * dy, 16 * dz);
} break;
} case 6:
else TriggerMutantRocketEffects(fxNum, 16 * dx, 16 * dy, 16 * dz);
{ break;
BubblesEffect1(fxNum, 16 * dx, 16 * dy, 16 * dz);
} }
} }
} }

View file

@ -0,0 +1,231 @@
#include "framework.h"
#include "tr4_locusts.h"
#include "sound.h"
#include "trmath.h"
#include "sphere.h"
#include "misc.h"
#include "lara.h"
#include "tomb4fx.h"
constexpr auto MAX_LOCUSTS = 64;
struct LOCUST_INFO
{
bool on;
PHD_3DPOS pos;
ITEM_INFO* target;
short roomNumber;
short randomRotation;
short escapeXrot;
short escapeYrot;
short escapeZrot;
BYTE counter;
};
LOCUST_INFO Locusts[MAX_LOCUSTS];
constexpr auto LOCUST_LARA_DAMAGE = 3;
constexpr auto LOCUST_ENTITY_DAMAGE = 1;
int CreateLocust(void)
{
LOCUST_INFO* locust;
for (int i = 0; i < MAX_LOCUSTS; i++)
{
locust = &Locusts[i];
if (!locust->on)
return i;
}
return NO_ITEM;
}
void SpawnLocust(ITEM_INFO* item)
{
LOCUST_INFO* locust;
ITEM_INFO* target;
PHD_VECTOR start, end;
short angles[2];
short locustNumber = NO_ITEM;
locustNumber = CreateLocust();
if (locustNumber != NO_ITEM)
{
locust = &Locusts[locustNumber];
// emitter
if (item->objectNumber == ID_LOCUSTS_EMITTER)
{
end.x = item->pos.xPos;
end.y = item->pos.yPos;
end.z = item->pos.zPos;
angles[0] = item->pos.yRot - ANGLE(180.0f);
angles[1] = 0;
}
// mutant
else
{
start.x = 0;
start.y = -96;
start.z = 144;
GetJointAbsPosition(item, &start, 9);
end.x = 0;
end.y = -128;
end.z = 288;
GetJointAbsPosition(item, &end, 9);
phd_GetVectorAngles(end.x - start.x, end.y - start.y, end.z - start.z, angles);
}
target = GetCreatureInfo(item)->enemy;
locust->on = true;
locust->target = target != nullptr ? target : nullptr;
locust->pos.xPos = end.x;
locust->pos.yPos = end.y;
locust->pos.zPos = end.z;
locust->pos.yRot = (GetRandomControl() & 0x7FF) + angles[0] - 0x400;
locust->pos.xRot = (GetRandomControl() & 0x3FF) + angles[1] - 0x200;
locust->roomNumber = item->roomNumber;
locust->randomRotation = (GetRandomControl() & 0x1F) + 0x10;
locust->escapeYrot = (GetRandomControl() & 0x1FF);
locust->escapeXrot = ((GetRandomControl() & 0x7) + 0xF) * 20;
locust->counter = 0;
}
}
void InitialiseLocust(short itemNumber)
{
ITEM_INFO* item = &Items[itemNumber];
if (item->pos.yRot > 0)
{
if (item->pos.yRot == 0x4000)
item->pos.xPos += CLICK(2);
}
else if (item->pos.yRot < 0)
{
if (item->pos.yRot == -0x8000)
item->pos.zPos -= CLICK(2);
else if (item->pos.yRot == -0x4000)
item->pos.xPos -= CLICK(2);
}
else
{
item->pos.zPos += CLICK(2);
}
}
void LocustControl(short itemNumber)
{
ITEM_INFO* item = &Items[itemNumber];
if (TriggerActive(item))
{
if (item->triggerFlags)
{
SpawnLocust(item);
item->triggerFlags--;
}
else
{
KillItem(itemNumber);
}
}
}
void UpdateLocusts(void)
{
LOCUST_INFO* locust;
ITEM_INFO* target;
PHD_VECTOR posAngle;
int distance;
int square;
short angles[2];
for (int i = 0; i < MAX_LOCUSTS; i++)
{
locust = &Locusts[i];
if (locust->on)
{
if (locust->target == nullptr)
locust->target = LaraItem;
if (locust->target->hitPoints <= 0 && locust->counter >= 90 && !(GetRandomControl() & 7))
locust->counter = 90;
locust->counter--;
if (locust->counter == 0)
{
locust->on = FALSE;
break;
}
if (!(GetRandomControl() & 7))
{
locust->escapeYrot = GetRandomControl() % 640 + 128;
locust->escapeXrot = (GetRandomControl() & 0x7F) - 64;
locust->escapeZrot = (GetRandomControl() & 0x7F) - 64;
}
phd_GetVectorAngles(
locust->target->pos.xPos + 8 * locust->escapeXrot - locust->pos.xPos,
locust->target->pos.yPos - locust->escapeYrot - locust->pos.yPos,
locust->target->pos.zPos + 8 * locust->escapeZrot - locust->pos.zPos,
angles);
distance = SQUARE(locust->target->pos.zPos - locust->pos.zPos) + SQUARE(locust->target->pos.xPos - locust->pos.xPos);
square = int(sqrt(distance)) >> 3;
if (square <= 128)
{
if (square < 48)
square = 48;
}
else
{
square = 128;
}
if (locust->randomRotation < square)
locust->randomRotation += 1;
else if (locust->randomRotation > square)
locust->randomRotation -= 1;
if (locust->counter > 90)
{
short resultYrot, resultXrot;
int shiftYrot, shiftXrot;
int random = locust->randomRotation << 7;
resultYrot = angles[0] - locust->pos.yRot;
if (abs(resultYrot) > 0x8000)
resultYrot = locust->pos.yRot - angles[0];
resultXrot = angles[1] - locust->pos.xRot;
if (abs(resultXrot) > 0x8000)
resultXrot = locust->pos.xRot - angles[0];
shiftYrot = resultYrot >> 3;
shiftXrot = resultXrot >> 3;
if (shiftYrot > random || shiftYrot < -random)
shiftYrot = -random;
if (shiftXrot > random || shiftXrot < -random)
shiftXrot = -random;
locust->pos.yRot += shiftYrot;
locust->pos.xRot += shiftXrot;
}
locust->pos.xPos += (locust->randomRotation * phd_cos(locust->pos.xRot) >> W2V_SHIFT) * phd_sin(locust->pos.yRot) >> W2V_SHIFT;
locust->pos.yPos += locust->randomRotation * phd_sin(-locust->pos.xRot) >> W2V_SHIFT;
locust->pos.zPos += (locust->randomRotation * phd_cos(locust->pos.xRot) >> W2V_SHIFT) * phd_cos(locust->pos.yRot) >> W2V_SHIFT;
if (ItemNearTarget(&locust->pos, locust->target, CLICK(1) / 2))
{
TriggerBlood(locust->pos.xPos, locust->pos.yPos, locust->pos.zPos, 2 * GetRandomControl(), 2);
if (locust->target == LaraItem)
locust->target->hitPoints -= LOCUST_LARA_DAMAGE;
else
locust->target->hitPoints -= LOCUST_ENTITY_DAMAGE;
}
if (locust->counter > 0)
SoundEffect(SFX_TR4_LOCUSTS_LOOP, &locust->pos, NULL);
}
}
}
void DrawLocust(void)
{
// TODO: no render for the locusts !
}

View file

@ -0,0 +1,10 @@
#pragma once
#include "phd_global.h"
#include "items.h"
extern int CreateLocust(void);
extern void SpawnLocust(ITEM_INFO* item);
extern void InitialiseLocust(short itemNumber);
extern void LocustControl(short itemNumber);
extern void UpdateLocusts(void);
extern void DrawLocust(void);

View file

@ -10,38 +10,6 @@
#include "draw.h" #include "draw.h"
#include "misc.h" #include "misc.h"
struct CROCODILE_BONE
{
short torsoY;
short torsoX;
short hipsY;
short hipsX;
CROCODILE_BONE()
{
this->torsoY = 0;
this->torsoX = 0;
this->hipsY = 0;
this->hipsX = 0;
}
CROCODILE_BONE(short angle)
{
this->torsoY = angle;
this->torsoX = angle;
this->hipsY = -angle;
this->hipsX = -angle;
}
CROCODILE_BONE(short torsoY, short torsoX)
{
this->torsoY = torsoY;
this->torsoX = torsoX;
this->hipsY = 0;
this->hipsX = 0;
}
};
enum CROCODILE_STATE enum CROCODILE_STATE
{ {
CROC_EMPTY, CROC_EMPTY,
@ -136,7 +104,7 @@ void CrocodileControl(short itemNumber)
ObjectInfo* obj; ObjectInfo* obj;
CREATURE_INFO* crocodile; CREATURE_INFO* crocodile;
AI_INFO info; AI_INFO info;
CROCODILE_BONE boneRot; OBJECT_BONES boneRot;
short angle; short angle;
short bone_angle; short bone_angle;
@ -330,15 +298,15 @@ void CrocodileControl(short itemNumber)
} }
if (item->currentAnimState == CROC_IDLE || item->currentAnimState == CROC_ATK || item->currentAnimState == WCROC_ATK) if (item->currentAnimState == CROC_IDLE || item->currentAnimState == CROC_ATK || item->currentAnimState == WCROC_ATK)
boneRot = CROCODILE_BONE(info.angle, info.xAngle); boneRot = OBJECT_BONES(info.angle, info.xAngle);
else else
boneRot = CROCODILE_BONE(bone_angle); boneRot = OBJECT_BONES(bone_angle);
CreatureTilt(item, 0); CreatureTilt(item, 0);
CreatureJoint(item, 0, boneRot.torsoY); CreatureJoint(item, 0, boneRot.bone0);
CreatureJoint(item, 1, boneRot.torsoX); CreatureJoint(item, 1, boneRot.bone1);
CreatureJoint(item, 2, boneRot.hipsY); CreatureJoint(item, 2, boneRot.bone2);
CreatureJoint(item, 3, boneRot.hipsX); CreatureJoint(item, 3, boneRot.bone3);
if (item->currentAnimState < WCROC_SWIM) if (item->currentAnimState < WCROC_SWIM)
CalcItemToFloorRotation(item, 2); CalcItemToFloorRotation(item, 2);

View file

@ -0,0 +1,350 @@
#include "framework.h"
#include "tr4_mutant.h"
#include "tr4_locusts.h"
#include "effect.h"
#include "effect2.h"
#include "misc.h"
#include "lara.h"
#include "setup.h"
#include "sphere.h"
#include "objectslist.h"
#include "trmath.h"
enum MUTANT_STATE
{
MUTANT_EMPTY,
MUTANT_APPEAR,
MUTANT_IDLE,
MUTANT_SHOOT,
MUTANT_LOCUST1,
MUTANT_LOCUST2,
};
enum class MissileRotationType
{
M_FRONT,
M_LEFT,
M_RIGHT
};
constexpr auto MUTANT_ANIM_APPEAR = 0;
constexpr auto MUTANT_SHOOT_RANGE = SQUARE(SECTOR(10));
constexpr auto MUTANT_LOCUST1_RANGE = SQUARE(SECTOR(15));
constexpr auto MUTANT_LOCUST2_RANGE = SQUARE(SECTOR(30));
static void TriggerMutantRocket(PHD_3DPOS* src, short roomNumber, short counter)
{
FX_INFO* fx;
short fxNumber = NO_ITEM;
fxNumber = CreateNewEffect(roomNumber);
if (fxNumber != NO_ITEM)
{
fx = &Effects[fxNumber];
fx->pos.xPos = src->xPos;
fx->pos.yPos = src->yPos - (GetRandomControl() & 0x3F) - 32;
fx->pos.zPos = src->zPos;
fx->pos.xRot = src->xRot;
fx->pos.yRot = src->yRot;
fx->pos.zRot = 0;
fx->roomNumber = roomNumber;
fx->counter = 16 * counter + 15;
fx->objectNumber = ID_ENERGY_BUBBLES;
fx->frameNumber = Objects[fx->objectNumber].meshIndex + 5 * 2;
fx->speed = (GetRandomControl() & 0x1F) + 96;
fx->flag1 = 6;
}
}
void TriggerMutantRocketEffects(short fxNumber, short xVel, short yVel, short zVel)
{
FX_INFO* fx;
SPARKS* sptr;
BYTE color, life, size;
//x = LaraItem->pos.xPos - Effects[m_fxNumber].pos.xPos;
//z = LaraItem->pos.zPos - Effects[m_fxNumber].pos.zPos;
//if (x >= -0x4000u && x <= 0x4000 && z >= -0x4000u && z <= 0x4000)
fx = &Effects[fxNumber];
sptr = &Sparks[GetFreeSpark()];
sptr->on = TRUE;
color = (GetRandomControl() & 0x3F) - 128;
sptr->sB = 0;
sptr->sR = color;
sptr->sG = color >> 1;
color = (GetRandomControl() & 0x3F) - 128;
sptr->dB = 0;
sptr->dR = color;
sptr->dG = color >> 1;
sptr->fadeToBlack = 8;
sptr->colFadeSpeed = (GetRandomControl() & 3) + 8;
sptr->transType = COLADD;
sptr->dynamic = -1;
life = (GetRandomControl() & 7) + 32;
sptr->life = life;
sptr->sLife = life;
sptr->x = (GetRandomControl() & 0xF) - 8;
sptr->y = 0;
sptr->z = (GetRandomControl() & 0xF) - 8;
sptr->x += fx->pos.xPos;
sptr->y += fx->pos.yPos;
sptr->z += fx->pos.zPos;
sptr->xVel = xVel;
sptr->yVel = yVel;
sptr->zVel = zVel;
sptr->friction = 34;
sptr->flags = SP_EXPDEF | SP_ROTATE | SP_DEF | SP_SCALE;
sptr->rotAng = GetRandomControl() & 0xFFF;
if (GetRandomControl() & 1)
sptr->rotAdd = (GetRandomControl() & 0x1F) - 32;
else
sptr->rotAdd = (GetRandomControl() & 0x1F) + 32;
sptr->gravity = 0;
sptr->maxYvel = 0;
sptr->fxObj = byte(fxNumber);
sptr->scalar = 2;
size = (GetRandomControl() & 0xF) + 128;
sptr->size = size;
sptr->sSize = size;
sptr->dSize = size >> 2;
}
static void ShootFireball(PHD_3DPOS* src, MissileRotationType rotation, short roomNumber, int timer)
{
switch (rotation)
{
case MissileRotationType::M_LEFT:
src->yRot -= GetRandomControl() % 0x2000;
break;
case MissileRotationType::M_RIGHT:
src->yRot += GetRandomControl() % 0x2000;
break;
}
TriggerMutantRocket(src, roomNumber, timer);
}
static bool ShootFrame(ITEM_INFO* item)
{
short frameNumber = (item->frameNumber - Anims[item->objectNumber].frameBase);
if (frameNumber == 45
//|| frameNumber == 50
//|| frameNumber == 55
|| frameNumber == 60
//|| frameNumber == 65
//|| frameNumber == 70
|| frameNumber == 75)
return true;
else
return false;
}
static void RotateHeadToTarget(ITEM_INFO* item, CREATURE_INFO* creature, int joint, short& headAngle)
{
if (creature->enemy == nullptr)
{
headAngle = item->pos.yRot;
return;
}
ITEM_INFO* enemy = creature->enemy;
PHD_VECTOR pos;
int x, z;
pos.x = 0;
pos.y = 0;
pos.z = 0;
GetJointAbsPosition(item, &pos, joint);
x = enemy->pos.xPos - pos.x;
z = enemy->pos.zPos - pos.z;
headAngle = (short)(phd_atan(z, x) - item->pos.yRot) / 2;
}
static void GetTargetPosition(ITEM_INFO* item, PHD_3DPOS* target)
{
PHD_VECTOR start, end;
short angles[2];
start.x = 0;
start.y = -96;
start.z = 144;
GetJointAbsPosition(item, &start, 9);
end.x = 0;
end.y = -128;
end.z = 288;
GetJointAbsPosition(item, &end, 9);
phd_GetVectorAngles(end.x - start.x, end.y - start.y, end.z - start.z, angles);
target->xPos = end.x;
target->yPos = end.y;
target->zPos = end.z;
target->yRot = angles[0];
target->xRot = angles[1];
target->zRot = 0;
}
enum CARDINAL_POINT
{
C_NORTH = 0,
C_NORTH_EAST = 45,
C_EAST = 90,
C_EAST_SOUTH = 135,
C_SOUTH = 180,
C_SOUTH_WEST = 225,
C_WEST = 270,
C_WEST_NORTH = 315
};
static void MoveItemFront(ITEM_INFO* item, int distance)
{
short degree = short(TO_DEGREES(item->pos.yRot));
switch (degree)
{
case C_NORTH:
item->pos.zPos += distance;
break;
case C_EAST:
item->pos.xPos += distance;
break;
case C_SOUTH:
item->pos.zPos -= distance;
break;
case C_WEST:
item->pos.xPos -= distance;
break;
}
}
static void MoveItemBack(ITEM_INFO* item, int distance)
{
short degree = short(TO_DEGREES(item->pos.yRot));
switch (degree)
{
case C_NORTH:
item->pos.zPos -= distance;
break;
case C_EAST:
item->pos.xPos -= distance;
break;
case C_SOUTH:
item->pos.zPos += distance;
break;
case C_WEST:
item->pos.xPos += distance;
break;
}
}
static void MutantAIFix(ITEM_INFO* item, AI_INFO* info)
{
MoveItemFront(item, SECTOR(2));
item->pos.yPos -= CLICK(3);
CreatureAIInfo(item, info);
item->pos.yPos += CLICK(3);
MoveItemBack(item, SECTOR(2));
}
void InitialiseMutant(short itemNumber)
{
ITEM_INFO* item;
InitialiseCreature(itemNumber);
item = &Items[itemNumber];
item->animNumber = Objects[item->objectNumber].animIndex + MUTANT_ANIM_APPEAR;
item->frameNumber = Anims[item->animNumber].frameBase;
item->currentAnimState = MUTANT_APPEAR;
item->goalAnimState = MUTANT_APPEAR;
}
void MutantControl(short itemNumber)
{
if (!CreatureActive(itemNumber))
return;
ITEM_INFO* item;
CREATURE_INFO* mutant;
AI_INFO info;
OBJECT_BONES mutant_joint;
short frameNumber;
short headY;
short angle;
item = &Items[itemNumber];
mutant = GetCreatureInfo(item);
angle = 0;
headY = 0;
if (item->aiBits & ALL_AIOBJ)
GetAITarget(mutant);
else if (mutant->hurtByLara)
mutant->enemy = LaraItem;
else
TargetNearestEntity(item, mutant);
MutantAIFix(item, &info);
RotateHeadToTarget(item, mutant, 9, headY);
GetCreatureMood(item, &info, VIOLENT);
CreatureMood(item, &info, VIOLENT);
mutant->maximumTurn = 0;
angle = CreatureTurn(item, 0);
switch (item->currentAnimState)
{
case MUTANT_IDLE:
if (info.ahead)
{
int random = GetRandomControl() & 31;
if ((random > 0 && random < 10) && info.distance <= MUTANT_SHOOT_RANGE)
item->goalAnimState = MUTANT_SHOOT;
else if ((random > 10 && random < 20) && info.distance <= MUTANT_LOCUST1_RANGE)
item->goalAnimState = MUTANT_LOCUST1;
else if ((random > 20 && random < 30) && info.distance <= MUTANT_LOCUST2_RANGE)
item->goalAnimState = MUTANT_LOCUST2;
}
break;
case MUTANT_SHOOT:
frameNumber = (item->frameNumber - Anims[item->objectNumber].frameBase);
if (frameNumber >= 94 && frameNumber <= 96)
{
PHD_3DPOS src;
GetTargetPosition(item, &src);
if (frameNumber == 94)
{
ShootFireball(&src, MissileRotationType::M_FRONT, item->roomNumber, 0);
}
else if (frameNumber == 95)
{
ShootFireball(&src, MissileRotationType::M_LEFT, item->roomNumber, 1);
//ShootFireball(&src, MissileRotationType::M_LEFT, item->roomNumber, 1);
}
else if (frameNumber == 96)
{
ShootFireball(&src, MissileRotationType::M_RIGHT, item->roomNumber, 1);
//ShootFireball(&src, MissileRotationType::M_RIGHT, item->roomNumber, 1);
}
}
break;
case MUTANT_LOCUST1:
frameNumber = (item->frameNumber - Anims[item->objectNumber].frameBase);
if (frameNumber >= 60 && frameNumber <= 120)
SpawnLocust(item);
break;
case MUTANT_LOCUST2:
if (ShootFrame(item))
{
PHD_3DPOS src;
GetTargetPosition(item, &src);
ShootFireball(&src, MissileRotationType::M_FRONT, item->roomNumber, 1);
}
break;
}
if (item->currentAnimState != MUTANT_LOCUST1)
mutant_joint = OBJECT_BONES(headY, info.xAngle, true);
else
mutant_joint = OBJECT_BONES(0);
CreatureJoint(item, 0, mutant_joint.bone0);
CreatureJoint(item, 1, mutant_joint.bone1);
CreatureJoint(item, 2, mutant_joint.bone2);
CreatureJoint(item, 3, mutant_joint.bone3);
CreatureAnimation(itemNumber, angle, 0);
}

View file

@ -0,0 +1,5 @@
#pragma once
void InitialiseMutant(short itemNumber);
void MutantControl(short itemNumber);
void TriggerMutantRocketEffects(short fxNumber, short xVel, short yVel, short zVel);

View file

@ -23,6 +23,8 @@
#include "tr4_wildboar.h" // OK #include "tr4_wildboar.h" // OK
#include "tr4_wraith.h" // OFF #include "tr4_wraith.h" // OFF
#include "tr4_baboon.h" // OK #include "tr4_baboon.h" // OK
#include "tr4_mutant.h" // OK
#include "tr4_locusts.h" // OK
/// objects /// objects
#include "tr4_sarcophagus.h" #include "tr4_sarcophagus.h"
/// puzzle /// puzzle
@ -49,7 +51,6 @@
/// vehicles /// vehicles
#include "motorbike.h" #include "motorbike.h"
#include "jeep.h" #include "jeep.h"
/// necessary import /// necessary import
#include "collide.h" #include "collide.h"
#include "objects.h" #include "objects.h"
@ -527,6 +528,36 @@ static void StartBaddy(ObjectInfo* obj)
if (Objects[ID_BABOON_NORMAL].loaded) if (Objects[ID_BABOON_NORMAL].loaded)
Objects[ID_BABOON_SILENT].animIndex = Objects[ID_BABOON_NORMAL].animIndex; Objects[ID_BABOON_SILENT].animIndex = Objects[ID_BABOON_NORMAL].animIndex;
} }
obj = &Objects[ID_CROCODILE_GOD];
if (obj->loaded)
{
obj->initialise = InitialiseMutant;
obj->control = MutantControl;
obj->collision = CreatureCollision;
obj->shadowSize = UNIT_SHADOW / 2;
obj->hitPoints = NOT_TARGETABLE;
obj->pivotLength = 50;
obj->radius = 128;
obj->intelligent = TRUE;
obj->saveAnim = TRUE;
obj->saveFlags = TRUE;
obj->saveMesh = TRUE;
obj->savePosition = TRUE;
obj->undead = TRUE;
obj->hitEffect = HIT_SMOKE;
Bones[obj->boneIndex + 6 * 4] |= ROT_Y | ROT_X;
Bones[obj->boneIndex + 7 * 4] |= ROT_Y | ROT_X;
}
obj = &Objects[ID_LOCUSTS_EMITTER];
if (obj->loaded)
{
obj->initialise = InitialiseLocust;
obj->control = LocustControl;
obj->drawRoutine = NULL;
obj->saveFlags = TRUE;
}
} }
static void StartObject(ObjectInfo* obj) static void StartObject(ObjectInfo* obj)

View file

@ -105,6 +105,9 @@ typedef enum GAME_OBJECT_ID
ID_SPIDERS_EMITTER, ID_SPIDERS_EMITTER,
ID_LION, ID_LION,
ID_DOBERMAN, ID_DOBERMAN,
ID_HAMMERHEAD,
ID_CROCODILE_GOD, // TR4 Citadel Gate Mutant
ID_LOCUSTS_EMITTER,
/* Humans */ /* Humans */
ID_SCUBA_HARPOON = 150, ID_SCUBA_HARPOON = 150,

View file

@ -116,6 +116,8 @@ GameScript::GameScript(sol::state* lua)
{"SPIDERS_EMITTER", ID_SPIDERS_EMITTER}, {"SPIDERS_EMITTER", ID_SPIDERS_EMITTER},
{"LION", ID_LION}, {"LION", ID_LION},
{"DOBERMAN", ID_DOBERMAN}, {"DOBERMAN", ID_DOBERMAN},
{"HAMMERHEAD", ID_HAMMERHEAD},
{"CROCODILE_GOD", ID_CROCODILE_GOD},
{"SCUBA_HARPOON", ID_SCUBA_HARPOON}, {"SCUBA_HARPOON", ID_SCUBA_HARPOON},
{"SCUBA_DIVER", ID_SCUBA_DIVER}, {"SCUBA_DIVER", ID_SCUBA_DIVER},
{"GOON_SILENCER1", ID_GOON_SILENCER1}, {"GOON_SILENCER1", ID_GOON_SILENCER1},

View file

@ -148,9 +148,11 @@ xcopy /Y "$(ProjectDir)Scripting\Scripts\*.lua" "$(TargetDir)\Scripts"</Command>
<ClInclude Include="Libs\zlib\zip.h" /> <ClInclude Include="Libs\zlib\zip.h" />
<ClInclude Include="Libs\zlib\zlib.h" /> <ClInclude Include="Libs\zlib\zlib.h" />
<ClInclude Include="Objects\Effects\tr4_bubbles.h" /> <ClInclude Include="Objects\Effects\tr4_bubbles.h" />
<ClInclude Include="Objects\Effects\tr4_locusts.h" />
<ClInclude Include="Objects\Effects\tr5_electricity.h" /> <ClInclude Include="Objects\Effects\tr5_electricity.h" />
<ClInclude Include="Objects\TR1\Entity\tr1_bigrat.h" /> <ClInclude Include="Objects\TR1\Entity\tr1_bigrat.h" />
<ClInclude Include="Objects\TR4\Entity\tr4_baboon.h" /> <ClInclude Include="Objects\TR4\Entity\tr4_baboon.h" />
<ClInclude Include="Objects\TR4\Entity\tr4_mutant.h" />
<ClInclude Include="Objects\TR4\Object\tr4_laradouble.h" /> <ClInclude Include="Objects\TR4\Object\tr4_laradouble.h" />
<ClInclude Include="Objects\TR4\Object\tr4_sarcophagus.h" /> <ClInclude Include="Objects\TR4\Object\tr4_sarcophagus.h" />
<ClInclude Include="Objects\TR4\Object\tr4_scales.h" /> <ClInclude Include="Objects\TR4\Object\tr4_scales.h" />
@ -390,9 +392,11 @@ xcopy /Y "$(ProjectDir)Scripting\Scripts\*.lua" "$(TargetDir)\Scripts"</Command>
<ClCompile Include="Game\footprint.cpp" /> <ClCompile Include="Game\footprint.cpp" />
<ClCompile Include="Game\misc.cpp" /> <ClCompile Include="Game\misc.cpp" />
<ClCompile Include="Game\trmath.cpp" /> <ClCompile Include="Game\trmath.cpp" />
<ClCompile Include="Objects\Effects\tr4_locusts.cpp" />
<ClCompile Include="Objects\Effects\tr5_electricity.cpp" /> <ClCompile Include="Objects\Effects\tr5_electricity.cpp" />
<ClCompile Include="Objects\TR1\Entity\tr1_bigrat.cpp" /> <ClCompile Include="Objects\TR1\Entity\tr1_bigrat.cpp" />
<ClCompile Include="Objects\TR4\Entity\tr4_baboon.cpp" /> <ClCompile Include="Objects\TR4\Entity\tr4_baboon.cpp" />
<ClCompile Include="Objects\TR4\Entity\tr4_mutant.cpp" />
<ClCompile Include="Objects\TR4\Object\tr4_laradouble.cpp" /> <ClCompile Include="Objects\TR4\Object\tr4_laradouble.cpp" />
<ClCompile Include="Objects\TR4\Object\tr4_sarcophagus.cpp" /> <ClCompile Include="Objects\TR4\Object\tr4_sarcophagus.cpp" />
<ClCompile Include="Objects\TR4\Object\tr4_scales.cpp" /> <ClCompile Include="Objects\TR4\Object\tr4_scales.cpp" />

View file

@ -780,6 +780,12 @@
<ClInclude Include="Objects\TR1\Entity\tr1_bigrat.h"> <ClInclude Include="Objects\TR1\Entity\tr1_bigrat.h">
<Filter>File di intestazione</Filter> <Filter>File di intestazione</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Objects\Effects\tr4_locusts.h">
<Filter>File di intestazione</Filter>
</ClInclude>
<ClInclude Include="Objects\TR4\Entity\tr4_mutant.h">
<Filter>File di intestazione</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="Game\box.cpp"> <ClCompile Include="Game\box.cpp">
@ -1427,6 +1433,12 @@
<ClCompile Include="Objects\TR1\Entity\tr1_bigrat.cpp"> <ClCompile Include="Objects\TR1\Entity\tr1_bigrat.cpp">
<Filter>File di origine</Filter> <Filter>File di origine</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Objects\Effects\tr4_locusts.cpp">
<Filter>File di origine</Filter>
</ClCompile>
<ClCompile Include="Objects\TR4\Entity\tr4_mutant.cpp">
<Filter>File di origine</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />