diff --git a/TR5Main/Game/box.cpp b/TR5Main/Game/box.cpp index 3be3bdbee..c5ed8f925 100644 --- a/TR5Main/Game/box.cpp +++ b/TR5Main/Game/box.cpp @@ -359,7 +359,7 @@ void CreatureFloat(short itemNumber) { item->pos.yPos = waterLevel; item->collidable = false; - item->status = ITEM_DESACTIVATED; + item->status = ITEM_DEACTIVATED; DisableBaddieAI(itemNumber); RemoveActiveItem(itemNumber); item->afterDeath = 1; @@ -535,7 +535,7 @@ int CreatureAnimation(short itemNumber, short angle, short tilt) } AnimateItem(item); - if (item->status == ITEM_DESACTIVATED) + if (item->status == ITEM_DEACTIVATED) { CreatureDie(itemNumber, FALSE); return FALSE; diff --git a/TR5Main/Game/control.cpp b/TR5Main/Game/control.cpp index dd1baf522..a80e14c53 100644 --- a/TR5Main/Game/control.cpp +++ b/TR5Main/Game/control.cpp @@ -2268,7 +2268,7 @@ int GetTargetOnLOS(GAME_VECTOR* src, GAME_VECTOR* dest, int DrawTarget, int firi } } } - if (item->status != ITEM_DESACTIVATED) + if (item->status != ITEM_DEACTIVATED) { AddActiveItem(itemNumber); item->status = ITEM_ACTIVE; @@ -2376,7 +2376,7 @@ int ObjectOnLOS2(GAME_VECTOR* start, GAME_VECTOR* end, PHD_VECTOR* vec, MESH_INF { item = &Items[linknum]; - if (item->status != ITEM_DESACTIVATED + if (item->status != ITEM_DEACTIVATED && item->status != ITEM_INVISIBLE && (item->objectNumber != ID_LARA && Objects[item->objectNumber].collision != NULL || item->objectNumber == ID_LARA && GetLaraOnLOS)) @@ -2769,7 +2769,7 @@ void AnimateItem(ITEM_INFO* item) case COMMAND_DEACTIVATE: if (Objects[item->objectNumber].intelligent && !item->afterDeath) item->afterDeath = 1; - item->status = ITEM_DESACTIVATED; + item->status = ITEM_DEACTIVATED; break; case COMMAND_SOUND_FX: case COMMAND_EFFECT: diff --git a/TR5Main/Game/items.h b/TR5Main/Game/items.h index 773a24a10..63a7a7e19 100644 --- a/TR5Main/Game/items.h +++ b/TR5Main/Game/items.h @@ -17,7 +17,7 @@ typedef enum ItemStatus { ITEM_ACTIVE, ITEM_NOT_ACTIVE, - ITEM_DESACTIVATED, + ITEM_DEACTIVATED, ITEM_INVISIBLE }; diff --git a/TR5Main/Game/lara.cpp b/TR5Main/Game/lara.cpp index 90b1150c4..3e5cd813b 100644 --- a/TR5Main/Game/lara.cpp +++ b/TR5Main/Game/lara.cpp @@ -1797,13 +1797,13 @@ void lara_as_pulley(ITEM_INFO* item, COLL_INFO* coll)//1B288, 1B3BC (F) if (p->itemFlags[2]) { p->itemFlags[2] = 0; - p->status = ITEM_DESACTIVATED; + p->status = ITEM_DEACTIVATED; } } else { if (!p->itemFlags[1]) - p->status = ITEM_DESACTIVATED; + p->status = ITEM_DEACTIVATED; p->itemFlags[2] = 1; diff --git a/TR5Main/Game/lara1gun.cpp b/TR5Main/Game/lara1gun.cpp index c71460060..8d10a4d32 100644 --- a/TR5Main/Game/lara1gun.cpp +++ b/TR5Main/Game/lara1gun.cpp @@ -1490,7 +1490,7 @@ void undraw_shotgun(int weapon) AnimateItem(item); - if (item->status == ITEM_DESACTIVATED) + if (item->status == ITEM_DEACTIVATED) { Lara.gunStatus = LG_NO_ARMS; Lara.target = NULL; diff --git a/TR5Main/Game/pickup.cpp b/TR5Main/Game/pickup.cpp index 8cafb9ecf..6f1795cfc 100644 --- a/TR5Main/Game/pickup.cpp +++ b/TR5Main/Game/pickup.cpp @@ -447,7 +447,7 @@ int KeyTrigger(short itemNum) oldkey = KeyTriggerActive; if (!oldkey) - item->status = ITEM_DESACTIVATED; + item->status = ITEM_DEACTIVATED; KeyTriggerActive = false; @@ -1615,7 +1615,7 @@ void SearchObjectControl(short itemNumber) } - if (item->status == ITEM_DESACTIVATED) + if (item->status == ITEM_DEACTIVATED) { if (item->objectNumber == ID_SEARCH_OBJECT4) { diff --git a/TR5Main/Game/savegame.cpp b/TR5Main/Game/savegame.cpp index dcd57c051..4f0cca01e 100644 --- a/TR5Main/Game/savegame.cpp +++ b/TR5Main/Game/savegame.cpp @@ -575,7 +575,7 @@ bool SaveGame::readItem() m_reader->ReadChunks(&readItemChunks, itemNumber); DisableBaddieAI(itemNumber); KillItem(itemNumber); - item->status = ITEM_DESACTIVATED; + item->status = ITEM_DEACTIVATED; item->flags |= ONESHOT; item->afterDeath = 128; } @@ -589,7 +589,7 @@ bool SaveGame::readItem() } // Some post-processing things - if (obj->isPuzzleHole && (item->status == ITEM_DESACTIVATED || item->status == ITEM_ACTIVE)) + if (obj->isPuzzleHole && (item->status == ITEM_DEACTIVATED || item->status == ITEM_ACTIVE)) item->objectNumber += NUM_PUZZLES; if (item->objectNumber >= ID_SMASH_OBJECT1 && item->objectNumber <= ID_SMASH_OBJECT8 && (item->flags & ONESHOT)) diff --git a/TR5Main/Game/switch.cpp b/TR5Main/Game/switch.cpp index 516aa0af9..3f45d5366 100644 --- a/TR5Main/Game/switch.cpp +++ b/TR5Main/Game/switch.cpp @@ -1228,7 +1228,7 @@ int GetSwitchTrigger(ITEM_INFO* item, short* itemNos, int AttatchedToSwitch) int SwitchTrigger(short itemNum, short timer) { ITEM_INFO* item = &Items[itemNum]; - if (item->status == ITEM_DESACTIVATED) + if (item->status == ITEM_DEACTIVATED) { if ((!item->currentAnimState && item->objectNumber != ID_JUMP_SWITCH || item->currentAnimState == 1 && item->objectNumber == ID_JUMP_SWITCH) && timer > 0) { diff --git a/TR5Main/Game/traps.cpp b/TR5Main/Game/traps.cpp index 3ecc77b40..e64ac337a 100644 --- a/TR5Main/Game/traps.cpp +++ b/TR5Main/Game/traps.cpp @@ -323,7 +323,7 @@ void CeilingTrapDoorCollision(short itemNumber, ITEM_INFO* l, COLL_INFO* coll) / l->pos.yRot += ANGLE(180); result2 = TestLaraPosition(CeilingTrapDoorBounds, item, l); l->pos.yRot += ANGLE(180); - if (TrInput & IN_ACTION && item->status != ITEM_DESACTIVATED && l->currentAnimState == STATE_LARA_JUMP_UP && l->gravityStatus && Lara.gunStatus == LG_NO_ARMS && (result || result2)) + if (TrInput & IN_ACTION && item->status != ITEM_DEACTIVATED && l->currentAnimState == STATE_LARA_JUMP_UP && l->gravityStatus && Lara.gunStatus == LG_NO_ARMS && (result || result2)) { AlignLaraPosition(&CeilingTrapDoorPos, item, l); if (result2) @@ -363,7 +363,7 @@ void FloorTrapDoorCollision(short itemNumber, ITEM_INFO* l, COLL_INFO* coll) // ITEM_INFO* item; item = &Items[itemNumber]; - if (TrInput & IN_ACTION && item->status != ITEM_DESACTIVATED && l->currentAnimState == STATE_LARA_STOP && l->animNumber == ANIMATION_LARA_STAY_IDLE && Lara.gunStatus == LG_NO_ARMS + if (TrInput & IN_ACTION && item->status != ITEM_DEACTIVATED && l->currentAnimState == STATE_LARA_STOP && l->animNumber == ANIMATION_LARA_STAY_IDLE && Lara.gunStatus == LG_NO_ARMS || Lara.isMoving && Lara.generalPtr == (void *) itemNumber) { if (TestLaraPosition(FloorTrapDoorBounds, item, l)) diff --git a/TR5Main/Objects/TR1/Entity/tr1_natla_mutant.cpp b/TR5Main/Objects/TR1/Entity/tr1_natla_mutant.cpp index c9f2f54ae..9e042dc0f 100644 --- a/TR5Main/Objects/TR1/Entity/tr1_natla_mutant.cpp +++ b/TR5Main/Objects/TR1/Entity/tr1_natla_mutant.cpp @@ -214,7 +214,7 @@ void NatlaEvilControl(short itemNum) CreatureAnimation(itemNum, 0, 0); /* Explode on death and set off heavy trigger into the bargain */ - if (item->status == ITEM_DESACTIVATED) + if (item->status == ITEM_DEACTIVATED) { SoundEffect(171, &item->pos, NULL); ExplodingDeath(itemNum, 0xffffffff, ABORT_PART_DAMAGE); @@ -224,6 +224,6 @@ void NatlaEvilControl(short itemNum) TestTriggers(TriggerIndex, TRUE, 0); KillItem(itemNum); - item->status = ITEM_DESACTIVATED; + item->status = ITEM_DEACTIVATED; } } \ No newline at end of file diff --git a/TR5Main/Objects/TR2/Entity/tr2_dragon.cpp b/TR5Main/Objects/TR2/Entity/tr2_dragon.cpp index 08807d73a..a460eede8 100644 --- a/TR5Main/Objects/TR2/Entity/tr2_dragon.cpp +++ b/TR5Main/Objects/TR2/Entity/tr2_dragon.cpp @@ -322,9 +322,9 @@ void DragonControl(short backNum) // all over DisableBaddieAI(itemNum); KillItem(backNum); - back->status = ITEM_DESACTIVATED; + back->status = ITEM_DEACTIVATED; KillItem(itemNum); - item->status = ITEM_DESACTIVATED; + item->status = ITEM_DEACTIVATED; return; } else if (dragon->flags < -100) diff --git a/TR5Main/Objects/TR2/Entity/tr2_skidman.cpp b/TR5Main/Objects/TR2/Entity/tr2_skidman.cpp index bcf923a72..bb8748872 100644 --- a/TR5Main/Objects/TR2/Entity/tr2_skidman.cpp +++ b/TR5Main/Objects/TR2/Entity/tr2_skidman.cpp @@ -232,7 +232,7 @@ void SkidManControl(short riderNum) rider->animNumber = item->animNumber + (Objects[ID_SNOWMOBILE_DRIVER].animIndex - Objects[ID_SNOWMOBILE_GUN].animIndex); rider->frameNumber = item->frameNumber + (Anims[rider->animNumber].frameBase - Anims[item->animNumber].frameBase); } - else if (rider->status == ITEM_DESACTIVATED && item->speed == 0 && item->fallspeed == 0) + else if (rider->status == ITEM_DEACTIVATED && item->speed == 0 && item->fallspeed == 0) { /* If rider has reached end of his death animation, turn his skidoo into one that Lara can ride */ RemoveActiveItem(riderNum); @@ -242,7 +242,7 @@ void SkidManControl(short riderNum) DisableBaddieAI(item_number); item->objectNumber = ID_SNOWMOBILE; - item->status = ITEM_DESACTIVATED; + item->status = ITEM_DEACTIVATED; InitialiseSkidoo(item_number); ((SKIDOO_INFO*)item->data)->armed = true; diff --git a/TR5Main/Objects/TR2/Vehicles/snowmobile.cpp b/TR5Main/Objects/TR2/Vehicles/snowmobile.cpp index 9e36f716c..63705f210 100644 --- a/TR5Main/Objects/TR2/Vehicles/snowmobile.cpp +++ b/TR5Main/Objects/TR2/Vehicles/snowmobile.cpp @@ -196,7 +196,7 @@ static void SkidooExplode(ITEM_INFO* skidoo) ExplodingDeath(Lara.Vehicle, -1, 256); KillItem(Lara.Vehicle); - skidoo->status = ITEM_DESACTIVATED; + skidoo->status = ITEM_DEACTIVATED; SoundEffect(SFX_EXPLOSION1, 0, 0); SoundEffect(SFX_EXPLOSION2, 0, 0); Lara.Vehicle = NO_ITEM; diff --git a/TR5Main/Objects/TR3/Vehicles/quad.cpp b/TR5Main/Objects/TR3/Vehicles/quad.cpp index fb440f9a5..2e03d5695 100644 --- a/TR5Main/Objects/TR3/Vehicles/quad.cpp +++ b/TR5Main/Objects/TR3/Vehicles/quad.cpp @@ -153,7 +153,7 @@ static void QuadbikeExplode(ITEM_INFO* item) ExplodingDeath(Lara.Vehicle, 0xfffffffe, 1); KillItem(Lara.Vehicle); - item->status = ITEM_DESACTIVATED; + item->status = ITEM_DEACTIVATED; SoundEffect(SFX_EXPLOSION1, NULL, 0); SoundEffect(SFX_EXPLOSION2, NULL, 0); diff --git a/TR5Main/Objects/TR4/Entity/tr4_baboon.cpp b/TR5Main/Objects/TR4/Entity/tr4_baboon.cpp new file mode 100644 index 000000000..14a9bd295 --- /dev/null +++ b/TR5Main/Objects/TR4/Entity/tr4_baboon.cpp @@ -0,0 +1,658 @@ +#include "framework.h" +#include "tr4_baboon.h" +#include "Box.h" +#include "lot.h" +#include "setup.h" +#include "control.h" +#include "misc.h" +#include "Lara.h" +#include "switch.h" +#include "tomb4fx.h" + +BaboonRespawnClass BaboonRespawn; +static BITE_INFO baboonBite = { 10, 10, 11, 4 }; + +enum BABOON_STATE +{ + BABOON_NULL, + BABOON_EMPTY, + BABOON_WALK, + BABOON_IDLE, + BABOON_RUN, + BABOON_PICKUP, + BABOON_SIT_IDLE, + BABOON_SIT_EAT, + BABOON_SIT_SCRATCH, + BABOON_RUN_ROLL, + BABOON_HITGROUND, + BABOON_DEATH, + BABOON_ATK1, + BABOON_JUMPATK, + BABOON_SUPERJUMPATK, + /// NOT USED IN TR4: + BABOON_CLIMB_4CLICK, + BABOON_CLIMB_3CLICK, + BABOON_CLIMB_2CLICK, + BABOON_FALL_4CLICK, + BABOON_FALL_3CLICK, + BABOON_FALL_2CLICK, + ///!END + BABOON_ACTIVATE_SWITCH +}; + +constexpr auto NO_BABOON = -1; +constexpr auto NO_BABOON_COUNT = -2; +constexpr auto NO_CROWBAR_SWITCH_FOUND = -1; + +#define BABOON_SIT_IDLE_ANIM 2 +#define BABOON_IDLE_ANIM 9 +#define BABOON_DEATH_ANIM 14 +#define BABOON_SWITCH_ANIM 31 + +#define BABOON_DAMAGE 70 +#define BABOON_IDLE_DISTANCE SQUARE(WALL_SIZE) +#define BABOON_ATTACK_ANGLE ANGLE(7.0f) +#define BABOON_ATK_RANGE 0x718E4 +#define BABOON_ATK_NORMALRANGE 0x1C639 +#define BABOON_JUMP_RANGE 0x718E4 +#define BABOON_FOLLOW_RANGE 0x400000 +#define BABOON_RUNROLL_RANGE 0x100000 +#define BABOON_WALK_ANGLE ANGLE(7.0f) +#define BABOON_RUN_ANGLE ANGLE(11.0f) +/// NOTE (TokyoSU): these touchbits is fixed ! +/// now the baboon is a killing machine :D +#define BABOON_RIGHT_TOUCHBITS 814 +#define BABOON_JUMP_TOUCHBITS 280 +#define BABOON_TOUCHBITS 0x1800 + +static void TriggerBaboonShockwave(PHD_3DPOS pos, short xRot) +{ + short shockwaveID = GetFreeShockwave(); + if (shockwaveID != NO_ITEM) + { + SHOCKWAVE_STRUCT* dieEffect = &ShockWaves[shockwaveID]; + dieEffect->x = pos.xPos; + dieEffect->y = pos.yPos; + dieEffect->z = pos.zPos; + dieEffect->innerRad = 0x2000280; + dieEffect->outerRad = 0x28802000; + dieEffect->xRot = xRot; + dieEffect->r = 255; + dieEffect->g = 64; + dieEffect->b = 0; + dieEffect->speed = -600; + dieEffect->life = 64; + } +} + +void BaboonDieEffect(ITEM_INFO* item) +{ + PHD_3DPOS pos = PHD_3DPOS(item->pos.xPos, item->pos.yPos - 128, item->pos.zPos); + + // trigger shockwave effect + TriggerBaboonShockwave(pos, ANGLE(0.0f)); + TriggerBaboonShockwave(pos, ANGLE(45.0f)); + TriggerBaboonShockwave(pos, ANGLE(90.0f)); + TriggerBaboonShockwave(pos, ANGLE(135.0f)); + + // trigger flash screen + FlashFadeR = 255; + FlashFadeG = 64; + FlashFadeB = 0; + FlashFader = 32; +} + +static void KillRespawnedBaboon(short itemNumber, bool remove = false) +{ + ITEM_INFO* item; + item = &Items[itemNumber]; + item->hitPoints = 0; + RemoveActiveItem(itemNumber); // remove it from the active item list + item->flags = IFLAG_CLEAR_BODY; + item->afterDeath = 128; // instant disappear ! + item->status = ITEM_DEACTIVATED; // wont triggered again... + if (remove) + item->itemFlags[0] = NO_BABOON; + DisableBaddieAI(itemNumber); // desactivate this AI or you will get crash later... +} + +static bool CheckRespawnedBaboon(short itemNumber) +{ + ITEM_INFO* item; + BaboonRespawnStruct* baboon; + + item = &Items[itemNumber]; + if (item->itemFlags[0] == NO_BABOON) // NORMAL/INV for now + { + KillRespawnedBaboon(itemNumber); + return false; + } + + baboon = BaboonRespawn.GetBaboonRespawn(item->itemFlags[0]); + if (baboon == nullptr) + return false; + + if (baboon->count == baboon->max_count) + { + KillRespawnedBaboon(itemNumber, true); + return false; + } + return true; +} + +static void UpdateRespawnedBaboon(short itemNumber) +{ + ITEM_INFO* item; + ObjectInfo* obj; + BaboonRespawnStruct* baboon; + + item = &Items[itemNumber]; + obj = &Objects[item->objectNumber]; + baboon = BaboonRespawn.GetBaboonRespawn(item->itemFlags[0]); + if (baboon == nullptr) + return; + + item->pos = baboon->pos; + IsRoomOutside(item->pos.xPos, item->pos.yPos, item->pos.zPos); + if (item->roomNumber != IsRoomOutsideNo) + ItemNewRoom(itemNumber, IsRoomOutsideNo); + if (baboon->count < baboon->max_count) + baboon->count++; + + item->animNumber = obj->animIndex + BABOON_SIT_IDLE_ANIM; + item->frameNumber = Anims[item->animNumber].frameBase; + item->currentAnimState = BABOON_SIT_IDLE; + item->goalAnimState = BABOON_SIT_IDLE; + item->hitPoints = obj->hitPoints; + + RemoveActiveItem(itemNumber); + item->flags = NULL; + item->afterDeath = 0; + item->status = ITEM_INVISIBLE; + + DisableBaddieAI(itemNumber); + if (item->objectNumber == ID_BABOON_NORMAL) + { + if (item->triggerFlags == 1) + return; + else + item->collidable = TRUE; + } + else if (item->triggerFlags == 0) + { + item->collidable = TRUE; + } +} + +void BaboonRespawnFunction(short itemNumber) +{ + ITEM_INFO* item; + item = &Items[itemNumber]; + BaboonDieEffect(item); + + if (!CheckRespawnedBaboon(itemNumber)) + return; + UpdateRespawnedBaboon(itemNumber); +} + +void InitialiseBaboon(short itemNumber) +{ + ITEM_INFO* item; + InitialiseCreature(itemNumber); + + item = &Items[itemNumber]; + item->animNumber = Objects[item->objectNumber].animIndex + BABOON_SIT_IDLE_ANIM; + item->frameNumber = Anims[item->animNumber].frameBase; + item->goalAnimState = BABOON_SIT_IDLE; + item->currentAnimState = BABOON_SIT_IDLE; + + if (item->objectNumber == ID_BABOON_SILENT) + BaboonRespawn.Add(item, 2); + else + item->itemFlags[0] = NO_BABOON; +} + +void BaboonControl(short itemNumber) +{ + if (!CreatureActive(itemNumber)) + return; + + ITEM_INFO* item; + CREATURE_INFO* baboon; + FLOOR_INFO* floor; + AI_INFO info, Lara_info; + short tilt, angle, head_y; + + item = &Items[itemNumber]; + baboon = GetCreatureInfo(item); + head_y = 0; + tilt = 0; + angle = 0; + + if (item->hitPoints <= 0 && item->hitPoints != NOT_TARGETABLE) + { + if (item->currentAnimState == BABOON_DEATH) + { + if (item->frameNumber == Anims[item->animNumber].frameEnd) + BaboonRespawnFunction(itemNumber); + } + else if (item->currentAnimState != BABOON_ACTIVATE_SWITCH) + { + item->animNumber = Objects[item->objectNumber].animIndex + BABOON_DEATH_ANIM; + item->frameNumber = Anims[item->animNumber].frameBase; + item->currentAnimState = BABOON_DEATH; + item->goalAnimState = BABOON_DEATH; + } + } + else + { + GetAITarget(baboon); + CreatureAIInfo(item, &info); + + if (!item->hitStatus && item->objectNumber == ID_BABOON_NORMAL) + { + int dx, dz; + dx = LaraItem->pos.xPos - item->pos.xPos; + dz = LaraItem->pos.zPos - item->pos.zPos; + Lara_info.angle = phd_atan(dx, dz) - item->pos.yRot; + Lara_info.distance = SQUARE(dx) + SQUARE(dz); + if (baboon->enemy == nullptr || baboon->enemy == LaraItem) + baboon->enemy = nullptr; + } + else + { + Lara_info.angle = info.angle; + Lara_info.distance = info.distance; + baboon->enemy = LaraItem; + } + + GetCreatureMood(item, &info, TRUE); + CreatureMood(item, &info, TRUE); + angle = CreatureTurn(item, baboon->maximumTurn); + + if (baboon->enemy != nullptr && baboon->enemy != LaraItem && baboon->enemy->objectNumber == ID_AI_FOLLOW) + { + if (baboon->reachedGoal + && (abs(item->pos.xPos - baboon->enemy->pos.xPos) < CLICK(1) + && abs(item->pos.yPos - baboon->enemy->pos.yPos) < CLICK(1) + && abs(item->pos.zPos - baboon->enemy->pos.zPos) < CLICK(1))) + { + item->pos.xPos = baboon->enemy->pos.xPos; + item->pos.yPos = baboon->enemy->pos.yPos; + item->pos.zPos = baboon->enemy->pos.zPos; + item->pos.yRot = baboon->enemy->pos.yRot; + item->animNumber = Objects[item->objectNumber].animIndex + BABOON_SWITCH_ANIM; + item->frameNumber = Anims[item->animNumber].frameBase; + item->goalAnimState = BABOON_ACTIVATE_SWITCH; + item->currentAnimState = BABOON_ACTIVATE_SWITCH; + item->aiBits &= ~(FOLLOW); + floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &item->roomNumber); + GetFloorHeight(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos); + TestTriggers(TriggerIndex, TRUE, FALSE); + baboon->enemy = nullptr; + } + } + + switch (item->currentAnimState) + { + case BABOON_IDLE: + baboon->maximumTurn = 0; + baboon->flags = 0; + + if (item->aiBits & GUARD) + { + AIGuard(baboon); + if (!(GetRandomControl() & 0xF)) + { + if (GetRandomControl() & 1) + item->goalAnimState = BABOON_HITGROUND; + else + item->goalAnimState = BABOON_SIT_IDLE; + } + } + else if (item->aiBits & PATROL1) + { + item->goalAnimState = BABOON_WALK; + } + else if (baboon->mood == ESCAPE_MOOD) + { + if (info.ahead && Lara.target != item) + item->goalAnimState = BABOON_IDLE; + else + item->goalAnimState = BABOON_RUN; + } + else if (baboon->mood == ATTACK_MOOD) + { + if (!(item->aiBits & FOLLOW) || (!item->gravityStatus && info.distance <= BABOON_RUNROLL_RANGE)) + { + if (info.bite && info.distance < BABOON_ATK_NORMALRANGE) + { + if (LaraItem->pos.yPos >= item->pos.yPos) + item->goalAnimState = BABOON_ATK1; + else + item->goalAnimState = BABOON_JUMPATK; + } + else if (info.bite && info.distance < BABOON_JUMP_RANGE) + { + item->goalAnimState = BABOON_SUPERJUMPATK; + } + else if (info.bite && info.distance < BABOON_RUNROLL_RANGE) + { + item->goalAnimState = BABOON_RUN_ROLL; + } + else + { + item->goalAnimState = BABOON_RUN; + } + } + else if (item->requiredAnimState) + { + item->goalAnimState = item->requiredAnimState; + } + else if (GetRandomControl() & 1) + { + item->goalAnimState = BABOON_SIT_IDLE; + } + } + else if (item->requiredAnimState) + { + item->goalAnimState = item->requiredAnimState; + } + else if (!(GetRandomControl() & 3)) + { + item->goalAnimState = BABOON_WALK; + } + else if (!(GetRandomControl() & 1)) + { + item->goalAnimState = BABOON_RUN_ROLL; + } + else if (GetRandomControl() & 4) + { + item->goalAnimState = BABOON_HITGROUND; + } + break; + case BABOON_SIT_IDLE: + baboon->maximumTurn = 0; + baboon->flags = 0; + + if (item->aiBits & GUARD) + { + AIGuard(baboon); + if (GetRandomControl() & 0xF) + item->goalAnimState = BABOON_SIT_EAT; + else if (GetRandomControl() & 0xA) + item->goalAnimState = BABOON_SIT_SCRATCH; + } + else if (item->aiBits & PATROL1) + { + item->goalAnimState = BABOON_WALK; + } + else if (baboon->mood != ESCAPE_MOOD) + { + if (baboon->mood == BORED_MOOD) + { + if (item->requiredAnimState) + { + item->goalAnimState = item->requiredAnimState; + } + // NOTE: it's not the original code, but it's too wreid + // that the baboon repeat the same move so i included the sit_idle with more random number + // (the eat not exist in the bored mood, i added it !) + else if (GetRandomControl() & 0x10) + { + item->goalAnimState = BABOON_SIT_IDLE; + } + else if (GetRandomControl() & 0x500) + { + if (GetRandomControl() & 0x200) + item->goalAnimState = BABOON_SIT_SCRATCH; + else if (GetRandomControl() & 0x250) + item->goalAnimState = BABOON_SIT_EAT; + } + else if (GetRandomControl() & 0x1000 || item->aiBits & FOLLOW) + { + item->goalAnimState = BABOON_WALK; + } + } + else if ((item->aiBits & FOLLOW) && info.distance > BABOON_IDLE_DISTANCE) + { + if (item->requiredAnimState) + item->goalAnimState = item->requiredAnimState; + else + item->goalAnimState = BABOON_WALK; + } + else + { + item->goalAnimState = BABOON_WALK; + } + } + else + { + item->goalAnimState = BABOON_IDLE; + } + break; + case BABOON_WALK: + baboon->maximumTurn = BABOON_WALK_ANGLE; + + if (item->aiBits & PATROL1) + { + item->goalAnimState = BABOON_WALK; + } + else if (baboon->mood == BORED_MOOD) + { + if (item->aiBits & FOLLOW) + item->goalAnimState = BABOON_WALK; + else if (GetRandomControl() < 256) + item->goalAnimState = BABOON_SIT_IDLE; + } + else if (baboon->mood == ESCAPE_MOOD) + { + item->goalAnimState = BABOON_RUN; + } + else if (baboon->mood == ATTACK_MOOD) + { + if (info.bite && info.distance < BABOON_ATK_RANGE) + item->goalAnimState = BABOON_IDLE; + } + else if (GetRandomControl() < 256) + { + item->goalAnimState = BABOON_SIT_IDLE; + } + break; + case BABOON_RUN: + baboon->maximumTurn = BABOON_RUN_ANGLE; + tilt = angle / 2; + + if (item->aiBits & GUARD) + { + item->goalAnimState = BABOON_IDLE; + } + else if (baboon->mood == ESCAPE_MOOD) + { + if (info.ahead && Lara.target != item) + item->goalAnimState = BABOON_IDLE; + } + else if (item->aiBits & FOLLOW && (item->gravityStatus || info.distance > BABOON_FOLLOW_RANGE)) + { + item->goalAnimState = BABOON_IDLE; + } + else if (baboon->mood == ATTACK_MOOD) + { + if (info.distance < BABOON_ATK_RANGE) + item->goalAnimState = BABOON_IDLE; + else if (info.bite && info.distance < BABOON_RUNROLL_RANGE) + item->goalAnimState = BABOON_RUN_ROLL; + } + else + { + item->goalAnimState = BABOON_RUN_ROLL; + } + break; + case BABOON_PICKUP: + baboon->maximumTurn = 0; + // NOTE: baboon not use it ! (only TR3 one) + break; + case BABOON_ACTIVATE_SWITCH: + baboon->maximumTurn = 0; + item->hitPoints = NOT_TARGETABLE; + + if (item->frameNumber == Anims[item->animNumber].frameBase + 212) + { + GAME_VECTOR pos; + pos.x = 0; + pos.y = 0; + pos.z = 0; + pos.boxNumber = 0; + pos.roomNumber = NO_ROOM; + + switch (item->pos.yRot) + { + case -0x4000: // WEST (OK) + pos.x = item->pos.xPos - SECTOR(1); + pos.z = item->pos.zPos; + break; + case 0x4000: // EAST (OK) + pos.x = item->pos.xPos + SECTOR(1); + pos.z = item->pos.zPos; + break; + case 0: // NORTH (NOP) maybe okay now with TR5Main + pos.x = item->pos.xPos; + pos.z = item->pos.zPos + SECTOR(1); + break; + case -0x8000: // SOUTH (OK) + pos.x = item->pos.xPos; + pos.z = item->pos.zPos - SECTOR(1); + break; + } + + pos.y = item->pos.yPos; + pos.roomNumber = item->roomNumber; + floor = GetFloor(pos.x, pos.y, pos.z, &pos.roomNumber); + int height = GetFloorHeight(floor, pos.x, pos.y, pos.z); + item->floor = height; + TestTriggersAtXYZ(pos.x, pos.y, pos.z, pos.roomNumber, TRUE, NULL); + item->triggerFlags = 1; + } + break; + case BABOON_ATK1: + case BABOON_JUMPATK: + case BABOON_SUPERJUMPATK: + if (info.ahead) + head_y = info.angle; + baboon->maximumTurn = 0; + + if (abs(info.angle) >= BABOON_ATTACK_ANGLE) + { + if (info.angle >= 0) + item->pos.yRot += BABOON_ATTACK_ANGLE; + else + item->pos.yRot -= BABOON_ATTACK_ANGLE; + } + else + { + item->pos.yRot += info.angle; + } + + if ( baboon->flags == 0 + && ((item->touchBits & BABOON_TOUCHBITS) + || (item->touchBits & BABOON_RIGHT_TOUCHBITS) + || (item->touchBits & BABOON_JUMP_TOUCHBITS))) + { + LaraItem->hitPoints -= BABOON_DAMAGE; + LaraItem->hitStatus = TRUE; + CreatureEffect2(item, &baboonBite, 10, -1, DoBloodSplat); + baboon->flags = 1; + } + break; + } + } + + CreatureTilt(item, tilt); + CreatureJoint(item, 0, head_y); + CreatureAnimation(itemNumber, angle, tilt); +} + +void BaboonRespawnClass::Free(void) +{ + baboonRespawnArray.clear(); +} + +void BaboonRespawnClass::Add(ITEM_INFO* item, int max_count) +{ + BaboonRespawnStruct toAdd; + toAdd.id = GetBaboonFreePlace(); + toAdd.pos = item->pos; + toAdd.count = 0; + toAdd.max_count = max_count; + item->itemFlags[0] = toAdd.id; // conserve the id of baboon respawn position on the array... + baboonRespawnArray.push_back(toAdd); +} + +void BaboonRespawnClass::Remove(int id) +{ + if (baboonRespawnArray.empty()) + return; + + for (auto i = baboonRespawnArray.begin(); i != baboonRespawnArray.end(); i++) + { + if (i->id == id) + baboonRespawnArray.erase(i); + } +} + +int BaboonRespawnClass::GetBaboonFreePlace() +{ + if (baboonRespawnArray.empty()) + return 0; + + int j = 0; + for (auto i = baboonRespawnArray.begin(); i != baboonRespawnArray.end(); i++, j++) + { + if (i->id == NO_BABOON) + return j; + } + + return NO_BABOON; +} + +BaboonRespawnStruct* BaboonRespawnClass::GetBaboonRespawn(int id) +{ + if (baboonRespawnArray.empty()) + return nullptr; + + for (auto i = baboonRespawnArray.begin(); i != baboonRespawnArray.end(); i++) + { + if (i->id == id) + return &*i; + } + + return nullptr; +} + +int BaboonRespawnClass::GetCount(int id) +{ + if (baboonRespawnArray.empty()) + return NO_BABOON_COUNT; + + for (auto i = baboonRespawnArray.begin(); i != baboonRespawnArray.end(); i++) + { + if (i->id == id) + return i->count; + } + + return NO_BABOON_COUNT; +} + +int BaboonRespawnClass::GetCountMax(int id) +{ + if (baboonRespawnArray.empty()) + return NO_BABOON_COUNT; + + for (auto i = baboonRespawnArray.begin(); i != baboonRespawnArray.end(); i++) + { + if (i->id == id) + return i->max_count; + } + + return NO_BABOON_COUNT; +} \ No newline at end of file diff --git a/TR5Main/Objects/TR4/Entity/tr4_baboon.h b/TR5Main/Objects/TR4/Entity/tr4_baboon.h new file mode 100644 index 000000000..b9002f83c --- /dev/null +++ b/TR5Main/Objects/TR4/Entity/tr4_baboon.h @@ -0,0 +1,31 @@ +#pragma once +#include "phd_global.h" +#include "items.h" + +struct BaboonRespawnStruct +{ + int id; + PHD_3DPOS pos; + int count; + int max_count; // used to limit the number of respawn ! +}; + +class BaboonRespawnClass +{ +private: + vector baboonRespawnArray; +public: + void Free(void); + void Add(ITEM_INFO* item, int max_count); + void Remove(int id); + int GetBaboonFreePlace(void); + BaboonRespawnStruct* GetBaboonRespawn(int id); + int GetCount(int id); + int GetCountMax(int id); +}; + + +extern BaboonRespawnClass BaboonRespawn; + +extern void InitialiseBaboon(short itemNumber); +extern void BaboonControl(short itemNumber); \ No newline at end of file diff --git a/TR5Main/Objects/TR4/Entity/tr4_sentrygun.cpp b/TR5Main/Objects/TR4/Entity/tr4_sentrygun.cpp index e86827235..06e5e9e55 100644 --- a/TR5Main/Objects/TR4/Entity/tr4_sentrygun.cpp +++ b/TR5Main/Objects/TR4/Entity/tr4_sentrygun.cpp @@ -215,7 +215,7 @@ void SentryGunControl(short itemNum) KillItem(itemNum); item->flags |= 1u; - item->status = ITEM_DESACTIVATED; + item->status = ITEM_DEACTIVATED; RemoveAllItemsInRoom(item->roomNumber, ID_SMOKE_EMITTER_BLACK); diff --git a/TR5Main/Objects/TR4/Entity/tr4_skeleton.cpp b/TR5Main/Objects/TR4/Entity/tr4_skeleton.cpp index 25f37074a..d7663d309 100644 --- a/TR5Main/Objects/TR4/Entity/tr4_skeleton.cpp +++ b/TR5Main/Objects/TR4/Entity/tr4_skeleton.cpp @@ -110,7 +110,7 @@ void InitialiseSkeleton(short itemNumber) item->currentAnimState = 25; item->animNumber = obj->animIndex; item->frameNumber = Anims[item->animNumber].frameBase; - item->status = ITEM_DESACTIVATED; + item->status = ITEM_DEACTIVATED; break; } diff --git a/TR5Main/Objects/TR4/Trap/tr4_spikyceiling.cpp b/TR5Main/Objects/TR4/Trap/tr4_spikyceiling.cpp index 6b4d80d9d..9391e4215 100644 --- a/TR5Main/Objects/TR4/Trap/tr4_spikyceiling.cpp +++ b/TR5Main/Objects/TR4/Trap/tr4_spikyceiling.cpp @@ -11,7 +11,7 @@ void ControlSpikyCeiling(short itemNumber) { ITEM_INFO* item = &Items[itemNumber]; - if (TriggerActive(item) && item->status != ITEM_DESACTIVATED) + if (TriggerActive(item) && item->status != ITEM_DEACTIVATED) { int y = item->pos.yPos + ((item->itemFlags[0] == 1) ? 10 : 5); @@ -20,7 +20,7 @@ void ControlSpikyCeiling(short itemNumber) if (GetFloorHeight(floor, item->pos.xPos, y, item->pos.zPos) < y + 1024) { - item->status = ITEM_DESACTIVATED; + item->status = ITEM_DEACTIVATED; StopSoundEffect(147); } else @@ -45,6 +45,6 @@ void ControlSpikyCeiling(short itemNumber) SoundEffect(56, &item->pos, 0); } - if (TriggerActive(item) && item->status != ITEM_DESACTIVATED && item->itemFlags[0] == 1) + if (TriggerActive(item) && item->status != ITEM_DEACTIVATED && item->itemFlags[0] == 1) AnimateItem(item); } \ No newline at end of file diff --git a/TR5Main/Objects/TR4/Trap/tr4_spikywall.cpp b/TR5Main/Objects/TR4/Trap/tr4_spikywall.cpp index fd1cc35f0..1e873db65 100644 --- a/TR5Main/Objects/TR4/Trap/tr4_spikywall.cpp +++ b/TR5Main/Objects/TR4/Trap/tr4_spikywall.cpp @@ -12,7 +12,7 @@ void ControlSpikyWall(short itemNum) ITEM_INFO* item = &Items[itemNum]; /* Move wall */ - if (TriggerActive(item) && item->status != ITEM_DESACTIVATED) + if (TriggerActive(item) && item->status != ITEM_DEACTIVATED) { int x = item->pos.xPos + phd_sin(item->pos.yRot) >> WALL_SHIFT; int z = item->pos.zPos + phd_cos(item->pos.yRot) >> WALL_SHIFT; @@ -22,7 +22,7 @@ void ControlSpikyWall(short itemNum) if (GetFloorHeight(floor, x, item->pos.yPos, z) != item->pos.yPos) { - item->status = ITEM_DESACTIVATED; + item->status = ITEM_DEACTIVATED; StopSoundEffect(SFX_ROLLING_BALL); } else diff --git a/TR5Main/Objects/TR4/Vehicles/jeep.cpp b/TR5Main/Objects/TR4/Vehicles/jeep.cpp index 69a92f7f3..a01dc6b05 100644 --- a/TR5Main/Objects/TR4/Vehicles/jeep.cpp +++ b/TR5Main/Objects/TR4/Vehicles/jeep.cpp @@ -609,7 +609,7 @@ static void JeepExplode(ITEM_INFO* item) ExplodingDeath(Lara.Vehicle, -1, 256); KillItem(Lara.Vehicle); - item->status = ITEM_DESACTIVATED; + item->status = ITEM_DEACTIVATED; SoundEffect(SFX_EXPLOSION1, 0, 0); SoundEffect(SFX_EXPLOSION2, 0, 0); Lara.Vehicle = NO_ITEM; diff --git a/TR5Main/Objects/TR4/tr4_objects.cpp b/TR5Main/Objects/TR4/tr4_objects.cpp index 5ad9b3049..097249447 100644 --- a/TR5Main/Objects/TR4/tr4_objects.cpp +++ b/TR5Main/Objects/TR4/tr4_objects.cpp @@ -22,6 +22,7 @@ #include "tr4_troops.h" // OK #include "tr4_wildboar.h" // OK #include "tr4_wraith.h" // OFF +#include "tr4_baboon.h" // OK /// objects #include "tr4_sarcophagus.h" /// puzzle @@ -470,19 +471,61 @@ static void StartBaddy(ObjectInfo* obj) obj = &Objects[ID_BABOON_NORMAL]; if (obj->loaded) { - + obj->initialise = InitialiseBaboon; + obj->control = BaboonControl; + obj->collision = CreatureCollision; + obj->shadowSize = 128; + obj->hitPoints = 30; + obj->pivotLength = 200; + obj->radius = 256; + obj->intelligent = true; + obj->saveAnim = true; + obj->saveFlags = true; + obj->saveHitpoints = true; + obj->savePosition = true; + obj->hitEffect = HIT_BLOOD; } obj = &Objects[ID_BABOON_INV]; if (obj->loaded) { + obj->initialise = InitialiseBaboon; + obj->control = BaboonControl; + obj->collision = CreatureCollision; + obj->shadowSize = 128; + obj->hitPoints = 30; + obj->pivotLength = 200; + obj->radius = 256; + obj->intelligent = true; + obj->saveAnim = true; + obj->saveFlags = true; + obj->saveHitpoints = true; + obj->savePosition = true; + obj->hitEffect = HIT_BLOOD; + if (Objects[ID_BABOON_NORMAL].loaded) + Objects[ID_BABOON_INV].animIndex = Objects[ID_BABOON_NORMAL].animIndex; } obj = &Objects[ID_BABOON_SILENT]; if (obj->loaded) { + obj->initialise = InitialiseBaboon; + obj->control = BaboonControl; + obj->collision = CreatureCollision; + obj->shadowSize = 128; + obj->hitPoints = 30; + obj->pivotLength = 200; + obj->radius = 256; + obj->intelligent = true; + obj->saveAnim = true; + obj->saveFlags = true; + obj->saveHitpoints = true; + obj->savePosition = true; + obj->hitEffect = HIT_BLOOD; + if (Objects[ID_BABOON_NORMAL].loaded) + Objects[ID_BABOON_SILENT].animIndex = Objects[ID_BABOON_NORMAL].animIndex; } } diff --git a/TR5Main/Objects/TR5/Entity/tr5_larson.cpp b/TR5Main/Objects/TR5/Entity/tr5_larson.cpp index ad3222332..280c84085 100644 --- a/TR5Main/Objects/TR5/Entity/tr5_larson.cpp +++ b/TR5Main/Objects/TR5/Entity/tr5_larson.cpp @@ -106,7 +106,7 @@ void LarsonControl(short itemNumber) item->gravityStatus = false; item->hitStatus = false; item->collidable = false; - item->status = ITEM_DESACTIVATED; + item->status = ITEM_DEACTIVATED; } else { diff --git a/TR5Main/Objects/TR5/Object/tr5_pushableblock.cpp b/TR5Main/Objects/TR5/Object/tr5_pushableblock.cpp index f7ef52a53..4ade8b30b 100644 --- a/TR5Main/Objects/TR5/Object/tr5_pushableblock.cpp +++ b/TR5Main/Objects/TR5/Object/tr5_pushableblock.cpp @@ -268,14 +268,14 @@ void PushableBlockControl(short itemNumber) { item->gravityStatus = false; item->pos.yPos = height; - item->status = ITEM_DESACTIVATED; + item->status = ITEM_DEACTIVATED; floor_shake_effect(item); SoundEffect(SFX_LARA_THUD, &item->pos, 0); } if (item->roomNumber != roomNumber) ItemNewRoom(itemNumber, roomNumber); - if (item->status == ITEM_DESACTIVATED) + if (item->status == ITEM_DEACTIVATED) { item->status = ITEM_NOT_ACTIVE; RemoveActiveItem(itemNumber); diff --git a/TR5Main/Objects/TR5/Shatter/tr5_smashobject.cpp b/TR5Main/Objects/TR5/Shatter/tr5_smashobject.cpp index 0548d332a..0a4a3d490 100644 --- a/TR5Main/Objects/TR5/Shatter/tr5_smashobject.cpp +++ b/TR5Main/Objects/TR5/Shatter/tr5_smashobject.cpp @@ -41,7 +41,7 @@ void SmashObject(short itemNumber) if (item->status == ITEM_ACTIVE) RemoveActiveItem(itemNumber); - item->status = ITEM_DESACTIVATED; + item->status = ITEM_DEACTIVATED; } void SmashObjectControl(short itemNumber) diff --git a/TR5Main/Objects/TR5/Trap/tr5_fallingceiling.cpp b/TR5Main/Objects/TR5/Trap/tr5_fallingceiling.cpp index a05054f65..e2a7b7602 100644 --- a/TR5Main/Objects/TR5/Trap/tr5_fallingceiling.cpp +++ b/TR5Main/Objects/TR5/Trap/tr5_fallingceiling.cpp @@ -24,7 +24,7 @@ void FallingCeilingControl(short itemNumber) AnimateItem(item); - if (item->status == ITEM_DESACTIVATED) + if (item->status == ITEM_DEACTIVATED) { RemoveActiveItem(itemNumber); } diff --git a/TR5Main/Objects/TR5/tr5_objects.cpp b/TR5Main/Objects/TR5/tr5_objects.cpp index eed1435c9..ffb4ca637 100644 --- a/TR5Main/Objects/TR5/tr5_objects.cpp +++ b/TR5Main/Objects/TR5/tr5_objects.cpp @@ -983,6 +983,17 @@ static void StartObject(ObjectInfo* obj) obj->saveAnim = true; obj->saveFlags = true; } + + for (int objectNumber = ID_AI_GUARD; objectNumber <= ID_AI_X2; objectNumber++) + { + obj = &Objects[objectNumber]; + if (obj->loaded) + { + obj->drawRoutine = nullptr; + obj->collision = AIPickupCollision; + obj->hitPoints = 0; + } + } } static void StartTrap(ObjectInfo* obj) diff --git a/TR5Main/Scripting/GameLogicScript.cpp b/TR5Main/Scripting/GameLogicScript.cpp index 310939fc4..d2970856d 100644 --- a/TR5Main/Scripting/GameLogicScript.cpp +++ b/TR5Main/Scripting/GameLogicScript.cpp @@ -1535,7 +1535,7 @@ void GameScriptItem::EnableItem() { if (Objects[NativeItem->objectNumber].intelligent) { - if (NativeItem->status == ITEM_DESACTIVATED) + if (NativeItem->status == ITEM_DEACTIVATED) { NativeItem->touchBits = 0; NativeItem->status = ITEM_ACTIVE; @@ -1570,7 +1570,7 @@ void GameScriptItem::DisableItem() if (NativeItem->status == ITEM_ACTIVE) { NativeItem->touchBits = 0; - NativeItem->status = ITEM_DESACTIVATED; + NativeItem->status = ITEM_DEACTIVATED; RemoveActiveItem(NativeItemNumber); DisableBaddieAI(NativeItemNumber); } @@ -1579,7 +1579,7 @@ void GameScriptItem::DisableItem() { NativeItem->touchBits = 0; RemoveActiveItem(NativeItemNumber); - NativeItem->status = ITEM_DESACTIVATED; + NativeItem->status = ITEM_DEACTIVATED; } } } diff --git a/TR5Main/TR5Main.vcxproj b/TR5Main/TR5Main.vcxproj index 23efa5108..856bdbe8f 100644 --- a/TR5Main/TR5Main.vcxproj +++ b/TR5Main/TR5Main.vcxproj @@ -149,6 +149,7 @@ xcopy /Y "$(ProjectDir)Scripting\Scripts\*.lua" "$(TargetDir)\Scripts" + @@ -389,6 +390,7 @@ xcopy /Y "$(ProjectDir)Scripting\Scripts\*.lua" "$(TargetDir)\Scripts" + diff --git a/TR5Main/TR5Main.vcxproj.filters b/TR5Main/TR5Main.vcxproj.filters index dd8002aaa..8da724036 100644 --- a/TR5Main/TR5Main.vcxproj.filters +++ b/TR5Main/TR5Main.vcxproj.filters @@ -774,6 +774,9 @@ File di intestazione + + File di intestazione + @@ -1415,6 +1418,9 @@ File di origine + + File di origine +