diff --git a/Build/PCTomb5.idb b/Build/PCTomb5.idb index bceb197b8..a9bb969c8 100644 Binary files a/Build/PCTomb5.idb and b/Build/PCTomb5.idb differ diff --git a/TR5Main/Game/control.h b/TR5Main/Game/control.h index aa5d9f10b..a2c13eac3 100644 --- a/TR5Main/Game/control.h +++ b/TR5Main/Game/control.h @@ -41,6 +41,7 @@ typedef enum GAME_STATUS { #define UpdateSpiders ((void (__cdecl*)()) 0x0047A340) #define UpdateShockwaves ((void (__cdecl*)()) 0x004849A0) #define UpdateLightning ((void (__cdecl*)()) 0x00484CB0) +#define ExplodeItemNode ((__int32 (__cdecl*)(ITEM_INFO*, __int32, __int32, __int32)) 0x0041ABF0) GAME_STATUS __cdecl DoTitle(__int32 index); GAME_STATUS __cdecl DoLevel(__int32 index, __int32 ambient, bool loadFromSavegame); diff --git a/TR5Main/Game/debris.h b/TR5Main/Game/debris.h index 50e96676b..2c94a34ca 100644 --- a/TR5Main/Game/debris.h +++ b/TR5Main/Game/debris.h @@ -1 +1,5 @@ #pragma once + +#include "..\Global\global.h" + +#define ShatterObject ((void (__cdecl*)(SHATTER_ITEM*, MESH_INFO*, __int16, __int16, __int32)) 0x0041D6B0) \ No newline at end of file diff --git a/TR5Main/Game/lot.cpp b/TR5Main/Game/lot.cpp index 4ff00b523..0246e7940 100644 --- a/TR5Main/Game/lot.cpp +++ b/TR5Main/Game/lot.cpp @@ -160,6 +160,12 @@ void __cdecl InitialiseSlot(__int16 itemNum, __int16 slot) creature->LOT.zone = 3; break; + case ID_SKELETON: + creature->LOT.step = 256; + creature->LOT.drop = -256; + creature->LOT.canJump = true; + break; + case ID_CROW: case ID_EAGLE: case ID_WILLOWISP: diff --git a/TR5Main/Global/objectslist.h b/TR5Main/Global/objectslist.h index 330e45d9e..e8162bba2 100644 --- a/TR5Main/Global/objectslist.h +++ b/TR5Main/Global/objectslist.h @@ -44,7 +44,7 @@ typedef enum object_types { ID_SKELETON, // TR4 - Working on ID_DOG, ID_CROCODILE, // TR4 - ID_LARA_EXTRA_ANIMS, // TR1-2-3 + ID_LARA_EXTRA_ANIMS, // TR1-2-3 - OK ID_MUMMY, // TR4 - OK ID_LARSON, ID_SMALL_SCORPION, // TR4 - OK @@ -158,7 +158,7 @@ typedef enum object_types { ID_TIGHT_ROPE, ID_PARALLEL_BARS, ID_XRAY_CONTROLLER, - ID_CUTSCENE_ROPE, + ID_RAT, // TR2 - OK ID_PORTAL, ID_GEN_SLOT1, ID_GEN_SLOT2, @@ -312,7 +312,7 @@ typedef enum object_types { ID_CLOSED_DOOR5, ID_RAPTOR, // TR3 - OK ID_CLOSED_DOOR6, - ID_TYRANNOSAUR, // TR3 - 287 + ID_TYRANNOSAUR, // TR3 - OK ID_LIFT_DOORS1, ID_COBRA, // TR3 - OK ID_LIFT_DOORS2, @@ -438,7 +438,7 @@ typedef enum object_types { ID_ANIMATING10, ID_BEAR, // TR1 - OK NEED TEST ID_ANIMATING11, - ID_GORILLA, // TR1 - 15 + ID_APE, // TR1 - OK NEED TEST ID_ANIMATING12, ID_WINGED_MUMMY, // TR1 - 20 ID_ANIMATING13, diff --git a/TR5Main/Global/types.h b/TR5Main/Global/types.h index 192305c08..49ac6e109 100644 --- a/TR5Main/Global/types.h +++ b/TR5Main/Global/types.h @@ -10,6 +10,14 @@ typedef struct vector_t __int32 pad; } VECTOR; +struct SPHERE +{ + long x; // size=0, offset=0 + long y; // size=0, offset=4 + long z; // size=0, offset=8 + long r; // size=0, offset=12 +}; + typedef struct svector_t { __int16 vx; @@ -1263,6 +1271,16 @@ typedef struct SPRITE float bottom; }; +struct SHATTER_ITEM +{ + SPHERE Sphere; // size=16, offset=0 + ITEM_LIGHT* il; // size=48, offset=16 + __int16* meshp; // size=0, offset=20 + __int32 bit; // size=0, offset=24 + __int16 yRot; // size=0, offset=28 + __int16 flags; // size=0, offset=30 +}; + typedef void (cdecl *EFFECT_ROUTINE)(ITEM_INFO*); typedef void (cdecl *LARA_COLLISION_ROUTINE)(ITEM_INFO*, COLL_INFO*); typedef void (cdecl *LARA_CONTROL_ROUTINE)(ITEM_INFO*, COLL_INFO*); diff --git a/TR5Main/Objects/animals.cpp b/TR5Main/Objects/animals.cpp index b81e9aa2b..fa6eacd17 100644 --- a/TR5Main/Objects/animals.cpp +++ b/TR5Main/Objects/animals.cpp @@ -21,6 +21,8 @@ BITE_INFO eagleBite = { 15, 46, 21, 6 }; BITE_INFO crowBite = { 2, 10, 60, 14 }; BITE_INFO wolfBite = { 0, -14, 174, 6 }; BITE_INFO bearBite = { 0, 96, 335, 14 }; +BITE_INFO apeBite = { 0, -19, 75, 15 }; +BITE_INFO mouseBite = { 0, 0, 57, 2 }; void __cdecl InitialiseWildBoar(__int16 itemNum) { @@ -1885,4 +1887,283 @@ void TyrannosaurControl(__int16 itemNum) CreatureAnimation(itemNum, angle, 0); item->collidable = true; -} \ No newline at end of file +} + +void __cdecl ApeControl(__int16 itemNum) +{ + if (!CreatureActive(itemNum)) + return; + + ITEM_INFO* item = &Items[itemNum]; + CREATURE_INFO* creature = (CREATURE_INFO*) item->data; + + __int16 head = 0; + __int16 angle = 0; + __int16 random = 0; + + if (item->hitPoints <= 0) + { + if (item->currentAnimState != 5) + { + item->animNumber = Objects[item->objectNumber].animIndex + 7 + (__int16)(GetRandomControl() / 0x4000); + item->frameNumber = Anims[item->animNumber].frameBase; + item->currentAnimState = 5; + } + } + else + { + AI_INFO info; + CreatureAIInfo(item, &info); + + if (info.ahead) + head = info.angle; + + GetCreatureMood(item, &info, TIMID); + CreatureMood(item, &info, TIMID); + + angle = CreatureTurn(item, creature->maximumTurn); + + if (item->hitStatus || info.distance < SQUARE(2048)) + creature->flags |= 1; + + switch (item->currentAnimState) + { + case 1: + if (creature->flags & 2) + { + item->pos.yRot -= ANGLE(90); + creature->flags -= 2; + } + else if (item->flags & 4) + { + item->pos.yRot += ANGLE(90); + creature->flags -= 4; + } + + if (item->requiredAnimState) + item->goalAnimState = item->requiredAnimState; + else if (info.bite && info.distance < SQUARE(430)) + item->goalAnimState = 4; + else if (!(creature->flags & 1) && + info.zoneNumber == info.enemyZone && info.ahead) + { + random = (__int16)(GetRandomControl() >> 5); + if (random < 0xA0) + item->goalAnimState = 10; + else if (random < 0x140) + item->goalAnimState = 6; + else if (random < 0x1E0) + item->goalAnimState = 7; + else if (random < 0x2F0) + { + item->goalAnimState = 8; + creature->maximumTurn = 0; + } + else + { + item->goalAnimState = 9; + creature->maximumTurn = 0; + } + } + else + item->goalAnimState = 3; + break; + + case 3: + creature->maximumTurn = ANGLE(5); + + if (creature->flags == 0 && info.angle > -ANGLE(45) && info.angle < ANGLE(45)) + item->goalAnimState = 1; + else if (info.ahead && (item->touchBits & 0xFF00)) + { + item->requiredAnimState = 4; + item->goalAnimState = 1; + } + else if (creature->mood != MOOD_TYPE::ESCAPE_MOOD) + { + random = (__int16)GetRandomControl(); + if (random < 0xA0) + { + item->requiredAnimState = 10; + item->goalAnimState = 1; + } + else if (random < 0x140) + { + item->requiredAnimState = 6; + item->goalAnimState = 1; + } + else if (random < 0x1E0) + { + item->requiredAnimState = 7; + item->goalAnimState = 1; + } + } + break; + + case 8: + if (!(creature->flags & 4)) + { + item->pos.yRot -= ANGLE(90); + creature->flags |= 4; + } + + item->goalAnimState = 1; + break; + + case 9: + if (!(creature->flags & 2)) + { + item->pos.yRot += ANGLE(90); + creature->flags |= 2; + } + + item->goalAnimState = 1; + break; + + case 4: + if (!item->requiredAnimState && (item->touchBits & 0xFF00)) + { + CreatureEffect(item, &apeBite, DoBloodSplat); + + LaraItem->hitPoints -= 200; + LaraItem->hitStatus = true; + + item->requiredAnimState = 1; + } + break; + } + } + + CreatureJoint(item, 0, head); + + if (item->currentAnimState != 11) + { + if (creature->flags & 2) + { + item->pos.yRot -= ANGLE(90); + creature->flags -= 2; + } + else if (item->flags & 4) + { + item->pos.yRot += ANGLE(90); + creature->flags -= 4; + } + + __int32 vault = CreatureVault(itemNum, angle, 2, 75); + + switch (vault) + { + case 2: + creature->maximumTurn = 0; + item->animNumber = Objects[item->objectNumber].animIndex + 19; + item->currentAnimState = 11; + item->frameNumber = Anims[item->animNumber].frameBase; + break; + + default: + return; + } + } + else + CreatureAnimation(itemNum, angle, 0); +} + +void __cdecl RatControl(__int16 itemNum) +{ + if (!CreatureActive(itemNum)) + return; + + ITEM_INFO* item = &Items[itemNum]; + CREATURE_INFO* creature = (CREATURE_INFO*) item->data; + + __int16 head = 0; + __int16 angle = 0; + __int16 random = 0; + + if (item->hitPoints <= 0) + { + if (item->currentAnimState != 6) + { + item->animNumber = Objects[item->objectNumber].animIndex + 9; + item->frameNumber = Anims[item->animNumber].frameBase; + item->currentAnimState = 6; + } + } + else + { + AI_INFO info; + CreatureAIInfo(item, &info); + + if (info.ahead) + head = info.angle; + + GetCreatureMood(item, &info, TIMID); + CreatureMood(item, &info, TIMID); + + angle = CreatureTurn(item, ANGLE(6)); + + switch (item->currentAnimState) + { + case 4: + if (creature->mood == MOOD_TYPE::BORED_MOOD || creature->mood == MOOD_TYPE::STALK_MOOD) + { + __int16 random = (__int16)GetRandomControl(); + if (random < 0x500) + item->requiredAnimState = 3; + else if (random > 0xA00) + item->requiredAnimState = 1; + } + else if (info.distance < SQUARE(340)) + item->requiredAnimState = 5; + else + item->requiredAnimState = 1; + + if (item->requiredAnimState) + item->goalAnimState = 2; + break; + + case 2: + creature->maximumTurn = 0; + + if (item->requiredAnimState) + item->goalAnimState = item->requiredAnimState; + break; + + case 1: + creature->maximumTurn = ANGLE(6); + + if (creature->mood == MOOD_TYPE::BORED_MOOD || creature->mood == MOOD_TYPE::STALK_MOOD) + { + random = (__int16)GetRandomControl(); + if (random < 0x500) + { + item->requiredAnimState = 3; + item->goalAnimState = 2; + } + else if (random < 0xA00) + item->goalAnimState = 2; + } + else if (info.ahead && info.distance < SQUARE(340)) + item->goalAnimState = 2; + break; + + case 5: + if (!item->requiredAnimState && (item->touchBits & 0x7F)) + { + CreatureEffect(item, &mouseBite, DoBloodSplat); + LaraItem->hitPoints -= 20; + LaraItem->hitStatus = true; + item->requiredAnimState = 2; + } + break; + + case 3: + if (GetRandomControl() < 0x500) + item->goalAnimState = 2; + break; + } + } + + CreatureJoint(item, 0, head); + CreatureAnimation(itemNum, angle, 0); +} diff --git a/TR5Main/Objects/objects.h b/TR5Main/Objects/objects.h index 42c7e5256..7e834e63b 100644 --- a/TR5Main/Objects/objects.h +++ b/TR5Main/Objects/objects.h @@ -8,6 +8,7 @@ void __cdecl ClampRotation(PHD_3DPOS *pos, __int16 angle, __int16 rot); void __cdecl InitialiseWolf(__int16 itemNum); void __cdecl WolfControl(__int16 itemNum); void __cdecl BearControl(__int16 itemNum); +void __cdecl ApeControl(__int16 itemNum); // TR2 objects void __cdecl BarracudaControl(__int16 itemNum); @@ -17,6 +18,7 @@ void __cdecl SpinningBlade(__int16 itemNum); void __cdecl InitialiseKillerStatue(__int16 itemNum); void __cdecl KillerStatueControl(__int16 itemNum); void __cdecl SpringBoardControl(__int16 itemNum); +void __cdecl RatControl(__int16 itemNum); // TR3 objects void __cdecl TigerControl(__int16 itemNum); diff --git a/TR5Main/Objects/skeleton.cpp b/TR5Main/Objects/skeleton.cpp index 87b93bbed..58dbc057e 100644 --- a/TR5Main/Objects/skeleton.cpp +++ b/TR5Main/Objects/skeleton.cpp @@ -9,6 +9,7 @@ #include "..\Game\sphere.h" #include "..\Game\effect2.h" #include "..\Game\people.h" +#include "..\Game\debris.h" BITE_INFO skeletonBite = { 0, -16, 200, 11 }; @@ -520,56 +521,164 @@ void __cdecl SkeletonControl(__int16 itemNum) { item->pos.yRot += info.angle; } - /*if (item->frameNumber > Anims[item->animNumber].frameBase + 15) + if (item->frameNumber > Anims[item->animNumber].frameBase + 15) { - v79 = Rooms + 148 * item->roomNumber; - v107 = 0; - v108 = 0; - v109 = 0; ROOM_INFO* room = &Rooms[item->roomNumber]; PHD_VECTOR pos; + GetJointAbsPosition(item, &pos, 16); - if ((*(*(v79 + 8) + 8 * (((v109 - *(v79 + 28)) >> 10) + *(v79 + 40) * ((v107 - *(v79 + 20)) >> 10)) + 2) & 0x8000) == 0x8000) + + FLOOR_INFO* floor = &room->floor[((z - room->z) >> 10) + room->ySize * ((x - room->x) >> 10)]; + if (floor->stopper) { - v80 = *(v79 + 16); - if (*(v79 + 50) > 0) + MESH_INFO* staticMesh = room->mesh; + if (room->numMeshes > 0) { - v105 = *(v79 + 50); - do + for (__int32 i = 0; i < room->numMeshes; i++) { - v81 = v109 ^ *(v80 + 8); - if (!(v81 & 0xFFFFFC00) && !((v107 ^ *v80) & 0xFFFFFC00) && *(v80 + 18) >= 50) + staticMesh = &room->mesh[i]; + if (abs(pos.x - staticMesh->x) < 1024 && abs(pos.z - staticMesh->z) < 1024 && staticMesh->staticNumber >= 50) { - LOWORD(v81) = LaraItem->roomNumber; - sub_432F00(0, v80, -64, v81, 0); - SoundEffect(347, pos, 0); - *(v80 + 16) &= 0xFEu; - v82 = *(v79 + 8); - v83 = ((v109 - *(v79 + 28)) >> 10) + *(v79 + 40) * ((v107 - *(v79 + 20)) >> 10); - *(v82 + 8 * v83 + 3) &= 0x7Fu; - GetHeight(v82 + 8 * v83, v107, v108, v109); + ShatterObject(0, staticMesh, -128, LaraItem->roomNumber, 0); + SoundEffect(347, &item->pos, 0); + staticMesh->Flags &= ~1; + floor->stopper = 0; + TrGetHeight(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos); TestTriggers(TriggerIndex, 1, 0); } - v80 += 20; - --v105; - } while (v105); + } } } - if (!creature_1->flags) + if (!creature->flags) { if (item->touchBits & 0x18000) { LaraItem->hitPoints -= 80; - HIWORD(v84) = HIWORD(LaraItem); - LaraItem->MainFlags |= 0x10u; - LOWORD(v84) = item->pos.yRot; - CreatureEffect2(item, &unk_4AB2E8, 10, v84, DoBloodSplat); - SoundEffect(70, pos, 0); - creature_1->flags = 1; + LaraItem->hitStatus = true; + CreatureEffect2(item, &skeletonBite, 10, item->pos.yRot, DoBloodSplat); + SoundEffect(70, &item->pos, 0); + creature->flags = 1; } } - }*/ + } + break; + + case 7: + if (item->hitStatus) + { + if (item->meshBits == -1 && laraInfo.angle && Lara.gunType == WEAPON_SHOTGUN) + { + if (GetRandomControl() & 3) + { + item->goalAnimState = 17; + } + else + { + ExplodeItemNode(item, 11, 1, -24); + } + } + else + { + LABEL_153: + item->goalAnimState = 2; + } + } + else if (Lara.target != item || item->meshBits != -1 || Lara.gunType != WEAPON_SHOTGUN || !(GetRandomControl() & 0x7F)) + { + item->goalAnimState = 2; + } + break; + + case 21: + if (item->animNumber == Objects[item->objectNumber].animIndex + 43) + { + roomNumber = item->roomNumber; + floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &roomNumber); + if (TrGetHeight(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos) > item->pos.yPos + 1280) + { + creature->maximumTurn = 0; + item->animNumber = Objects[item->objectNumber].animIndex + 44; + item->frameNumber = Anims[item->animNumber].frameBase; + item->currentAnimState = 23; + creature->LOT.isJumping = false; + item->gravityStatus = true; + } + } + break; + + case 23: + case 24: + roomNumber = item->roomNumber; + floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &roomNumber); + if (TrGetHeight(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos) <= item->pos.yPos) + { + if (item->active) + { + ExplodingDeath(itemNum, -1, 929); + KillItem(itemNum); + DisableBaddieAI(itemNum); + //Savegame.Kills++; + } + } + break; + + case 25: + case 11: + case 12: + case 13: + if ((item->currentAnimState == 12 || item->currentAnimState == 13) && + item->frameNumber < Anims[item->animNumber].frameBase + 20) + { + item->hitPoints = 25; + creature->maximumTurn = 0; + break; + } + if (item->currentAnimState == 11) + { + creature->maximumTurn = 0; + break; + } + + item->hitPoints = 25; + creature->LOT.isJumping = false; + + roomNumber = item->roomNumber; + floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &roomNumber); + if (TrGetHeight(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos) <= item->pos.yPos + 1024) + { + if (!(GetRandomControl() & 0x1F)) + { + item->goalAnimState = 14; + } + } + else + { + creature->maximumTurn = 0; + item->animNumber = Objects[item->objectNumber].animIndex + 47; + item->frameNumber = Anims[item->animNumber].frameBase; + item->currentAnimState = 24; + item->gravityStatus = true; + } + break; + + case 19: + case 20: + creature->alerted = false; + creature->maximumTurn = 0; + item->status = ITEM_ACTIVE; + break; + + case 0: + if (item->frameNumber - Anims[item->animNumber].frameBase < 32) + { + //WakeUpSkeleton(item); + } + break; + + default: break; } + + CreatureAnimation(itemNum, angle, 0); } } \ No newline at end of file diff --git a/TR5Main/Specific/setup.cpp b/TR5Main/Specific/setup.cpp index 9779e3a59..af6ad0fe9 100644 --- a/TR5Main/Specific/setup.cpp +++ b/TR5Main/Specific/setup.cpp @@ -498,6 +498,56 @@ void __cdecl NewObjects() Bones[obj->boneIndex + 10 * 4] |= ROT_Y; Bones[obj->boneIndex + 11 * 4] |= ROT_Y; } + + obj = &Objects[ID_APE]; + if (obj->loaded) + { + obj->control = ApeControl; + obj->collision = CreatureCollision; + obj->hitPoints = 22; + obj->shadowSize = 128; + obj->pivotLength = 250; + obj->radius = 340; + obj->intelligent = true; + obj->savePosition = true; + obj->saveHitpoints = true; + obj->saveAnim = true; + obj->saveFlags = true; + } + + obj = &Objects[ID_RAT]; + if (obj->loaded) + { + obj->control = RatControl; + obj->collision = CreatureCollision; + obj->hitPoints = 5; + obj->shadowSize = 128; + obj->pivotLength = 50; + obj->radius = 204; + obj->intelligent = true; + obj->savePosition = true; + obj->saveHitpoints = true; + obj->saveAnim = true; + obj->saveFlags = true; + } + + obj = &Objects[ID_SKELETON]; + if (obj->loaded) + { + obj->initialise = InitialiseSkeleton; + obj->control = SkeletonControl; + obj->collision = CreatureCollision; + obj->hitPoints = 15; + obj->shadowSize = 128; + obj->pivotLength = 50; + obj->radius = 128; + obj->explodableMeshbits = 0xA00; + obj->intelligent = true; + obj->savePosition = true; + obj->saveHitpoints = true; + obj->saveAnim = true; + obj->saveFlags = true; + } } void __cdecl CustomObjects()