TombEngine/TR5Main/Objects/TR4/Entity/tr4_ahmet.cpp

401 lines
14 KiB
C++
Raw Normal View History

#include "framework.h"
#include "tr4_ahmet.h"
#include "control.h"
#include "sphere.h"
#include "effects\effects.h"
#include "effects\weather.h"
2021-09-08 18:19:06 +03:00
#include "Sound\sound.h"
#include "setup.h"
#include "box.h"
#include "level.h"
#include "misc.h"
#include "lara.h"
#include "people.h"
#include "items.h"
#include "lot.h"
2021-08-29 10:04:49 +02:00
#include "creature_info.h"
using namespace TEN::Effects::Environment;
2021-08-29 08:10:29 +02:00
namespace TEN::Entities::TR4
{
2021-08-29 08:10:29 +02:00
enum AHMET_STATE
{
2021-08-29 08:10:29 +02:00
STATE_AHMET_EMPTY,
STATE_AHMET_IDLE,
STATE_AHMET_WALK,
STATE_AHMET_RUN,
STATE_AHMET_STAND_DUALATK,
STATE_AHMET_JUMP_BITE,
STATE_AHMET_JUMP_DUALATK,
STATE_AHMET_DIE
};
constexpr auto AHMET_JUMP_ATK_ANIM = 4;
constexpr auto AHMET_START_JUMP_ANIM = 7;
constexpr auto AHMET_DIE_ANIM = 10;
constexpr auto AHMET_WALK_ANGLE = 5 * ONE_DEGREE;
constexpr auto AHMET_RUN_ANGLE = 8 * ONE_DEGREE;
constexpr auto AHMET_VIEW_ANGLE = 45 * ONE_DEGREE;
constexpr auto AHMET_ENEMY_ANGLE = 90 * ONE_DEGREE;
constexpr auto AHMET_AWARE_DISTANCE = SQUARE(1024);
constexpr auto AHMET_IDLE_RANGE = SQUARE(1280);
constexpr auto AHMET_RUN_RANGE = SQUARE(2560);
constexpr auto AHMET_STAND_DUALATK_RANGE = SQUARE(682);
constexpr auto AHMET_RIGHT_TOUCH = 0xF00000;
constexpr auto AHMET_LEFT_TOUCH = 0x3C000;
constexpr auto AHMET_HAND_DAMAGE = 80;
constexpr auto AHMET_JAW_DAMAGE = 120;
2021-08-29 08:10:29 +02:00
BITE_INFO ahmetBiteLeft = { 0, 0, 0, 16 };
BITE_INFO ahmetBiteRight = { 0, 0, 0, 22 };
BITE_INFO ahmetBiteJaw = { 0, 0, 0, 11 };
static void AhmetHeavyTriggers(ITEM_INFO* item)
{
2021-09-15 16:58:53 +03:00
TestTriggers(item, true);
}
2021-08-29 08:10:29 +02:00
static void TriggerAhmetDeathEffect(ITEM_INFO* item)
{
if (!(Wibble & 7))
{
SPHERE* sphere;
int meshCount;
2021-08-29 08:10:29 +02:00
// cant be FALSE here because else it will be local space not world
// because of that it cant be GetJointAbsPosition() !
meshCount = GetSpheres(item, CreatureSpheres, SPHERES_SPACE_WORLD, Matrix::Identity);
sphere = &CreatureSpheres[(Wibble / 8) & 1];
2021-08-29 08:10:29 +02:00
for (int i = meshCount; i > 0; i--, sphere += 2)
TriggerFireFlame(sphere->x, sphere->y, sphere->z, -1, 1);
}
2021-08-29 08:10:29 +02:00
// NOTE: fixed light below the ground with -STEP_L !
TriggerDynamicLight(item->pos.xPos, (item->pos.yPos - STEP_SIZE), item->pos.zPos, 13, (GetRandomControl() & 0x3F) - 64, (GetRandomControl() & 0x1F) + 96, 0);
SoundEffect(SFX_TR4_LOOP_FOR_SMALL_FIRES, &item->pos, NULL);
}
2021-08-29 08:10:29 +02:00
void InitialiseAhmet(short itemNumber)
{
ITEM_INFO* item;
item = &g_Level.Items[itemNumber];
InitialiseCreature(itemNumber);
item->animNumber = Objects[item->objectNumber].animIndex;
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
item->goalAnimState = STATE_AHMET_IDLE;
item->currentAnimState = STATE_AHMET_IDLE;
item->itemFlags[0] = item->pos.xPos / SECTOR(1);
item->itemFlags[1] = item->pos.yPos * 4 / SECTOR(1);
item->itemFlags[2] = item->pos.zPos / SECTOR(1);
}
2021-08-29 08:10:29 +02:00
void AhmetControl(short itemNumber)
{
2021-08-29 08:10:29 +02:00
if (!CreatureActive(itemNumber))
return;
2021-08-29 08:10:29 +02:00
ITEM_INFO* item;
CREATURE_INFO* ahmet;
AI_INFO lara_info, info;
short angle, head_y;
item = &g_Level.Items[itemNumber];
if (item->triggerFlags == 1)
{
2021-08-29 08:10:29 +02:00
item->triggerFlags = 0;
return;
}
2021-08-29 08:10:29 +02:00
ahmet = GetCreatureInfo(item);
angle = 0;
head_y = 0;
2021-08-29 08:10:29 +02:00
if (item->hitPoints <= 0)
{
2021-08-29 08:10:29 +02:00
if (item->currentAnimState == STATE_AHMET_DIE)
{
// dont clear it !
if (item->frameNumber == g_Level.Anims[item->animNumber].frameEnd)
{
item->collidable = false; // NOTE: not exist in the original game, avoid wreid collision with lara...
item->frameNumber = (g_Level.Anims[item->animNumber].frameEnd - 1);
}
}
else
{
item->animNumber = Objects[item->objectNumber].animIndex + AHMET_DIE_ANIM;
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
item->currentAnimState = STATE_AHMET_DIE;
item->goalAnimState = STATE_AHMET_DIE;
Lara.interactedItem = itemNumber;
2021-08-29 08:10:29 +02:00
}
TriggerAhmetDeathEffect(item);
}
else
{
2021-08-29 08:10:29 +02:00
if (item->aiBits & ALL_AIOBJ)
GetAITarget(ahmet);
2021-08-29 08:10:29 +02:00
CreatureAIInfo(item, &info);
2021-08-29 08:10:29 +02:00
if (ahmet->enemy == LaraItem)
{
2021-08-29 08:10:29 +02:00
lara_info.angle = info.angle;
lara_info.distance = info.distance;
}
2021-08-29 08:10:29 +02:00
else
{
2021-08-29 08:10:29 +02:00
int dx, dz;
int ang;
dx = LaraItem->pos.xPos - item->pos.xPos;
dz = LaraItem->pos.zPos - item->pos.zPos;
ang = phd_atan(dx, dz);
lara_info.angle = ang - item->pos.yRot;
lara_info.distance = SQUARE(dx) + SQUARE(dz);
}
2021-08-29 08:10:29 +02:00
GetCreatureMood(item, &info, TRUE);
CreatureMood(item, &info, TRUE);
angle = CreatureTurn(item, ahmet->maximumTurn);
ahmet->enemy = LaraItem;
if (lara_info.distance < AHMET_AWARE_DISTANCE
|| item->hitStatus
2021-08-29 08:10:29 +02:00
|| TargetVisible(item, &lara_info))
AlertAllGuards(itemNumber);
if (info.ahead)
head_y = info.angle;
switch (item->currentAnimState)
{
2021-08-29 08:10:29 +02:00
case STATE_AHMET_IDLE:
ahmet->maximumTurn = 0;
ahmet->flags = 0;
if (item->aiBits & GUARD)
{
2021-08-29 08:10:29 +02:00
head_y = AIGuard(ahmet);
item->goalAnimState = STATE_AHMET_IDLE;
}
2021-08-29 08:10:29 +02:00
else if (item->aiBits & PATROL1)
{
2021-08-29 08:10:29 +02:00
item->goalAnimState = STATE_AHMET_WALK;
head_y = 0;
}
else if (ahmet->mood == ATTACK_MOOD && ahmet->mood != ESCAPE_MOOD)
{
if (info.bite && info.distance < AHMET_STAND_DUALATK_RANGE)
{
2021-08-29 08:10:29 +02:00
item->goalAnimState = STATE_AHMET_STAND_DUALATK;
}
2021-08-29 08:10:29 +02:00
else if ((info.angle >= AHMET_VIEW_ANGLE || info.angle <= -AHMET_VIEW_ANGLE) || info.distance >= AHMET_IDLE_RANGE)
{
2021-08-29 08:10:29 +02:00
if (item->requiredAnimState)
{
item->goalAnimState = item->requiredAnimState;
}
else
2021-08-29 08:10:29 +02:00
{
if (!info.ahead || info.distance >= AHMET_RUN_RANGE)
item->goalAnimState = STATE_AHMET_RUN;
else
item->goalAnimState = STATE_AHMET_WALK;
}
}
else if (GetRandomControl() & 1)
{
item->goalAnimState = STATE_AHMET_JUMP_BITE;
}
else
{
item->goalAnimState = STATE_AHMET_JUMP_DUALATK;
}
}
else
{
2021-08-29 08:10:29 +02:00
if (Lara.target == item || !info.ahead)
item->goalAnimState = STATE_AHMET_RUN;
else
item->goalAnimState = STATE_AHMET_IDLE;
}
2021-08-29 08:10:29 +02:00
break;
2021-08-29 08:10:29 +02:00
case STATE_AHMET_WALK:
ahmet->maximumTurn = AHMET_WALK_ANGLE;
2021-08-29 08:10:29 +02:00
if (item->aiBits & PATROL1)
{
2021-08-29 08:10:29 +02:00
item->goalAnimState = STATE_AHMET_WALK;
head_y = 0;
}
2021-08-29 08:10:29 +02:00
else if (info.bite && info.distance < AHMET_IDLE_RANGE)
{
2021-08-29 08:10:29 +02:00
item->goalAnimState = STATE_AHMET_IDLE;
}
2021-08-29 08:10:29 +02:00
else if (ahmet->mood == ESCAPE_MOOD || info.distance > AHMET_RUN_RANGE || !info.ahead || (info.enemyFacing > -AHMET_ENEMY_ANGLE || info.enemyFacing < AHMET_ENEMY_ANGLE))
{
2021-08-29 08:10:29 +02:00
item->goalAnimState = STATE_AHMET_RUN;
}
2021-08-29 08:10:29 +02:00
break;
case STATE_AHMET_RUN:
ahmet->maximumTurn = AHMET_RUN_ANGLE;
ahmet->flags = 0;
if (item->aiBits & GUARD || (ahmet->mood == BORED_MOOD || ahmet->mood == ESCAPE_MOOD) && (Lara.target == item && info.ahead) || (info.bite && info.distance < AHMET_IDLE_RANGE))
item->goalAnimState = STATE_AHMET_IDLE;
else if (info.ahead && info.distance < AHMET_RUN_RANGE && (info.enemyFacing < -AHMET_ENEMY_ANGLE || info.enemyFacing > AHMET_ENEMY_ANGLE))
item->goalAnimState = STATE_AHMET_WALK;
break;
case STATE_AHMET_STAND_DUALATK:
ahmet->maximumTurn = 0;
if (abs(info.angle) >= 910)
{
if (info.angle >= 0)
item->pos.yRot += 910;
else
item->pos.yRot -= 910;
}
else
{
item->pos.yRot += info.angle;
}
2021-08-29 08:10:29 +02:00
if (!(ahmet->flags & 1) && item->frameNumber > (g_Level.Anims[item->animNumber].frameBase + 7) && (item->touchBits & AHMET_LEFT_TOUCH))
{
2020-07-07 07:32:33 +02:00
LaraItem->hitStatus = true;
LaraItem->hitPoints -= AHMET_HAND_DAMAGE;
CreatureEffect2(item, &ahmetBiteLeft, 10, -1, DoBloodSplat);
ahmet->flags |= 1;
}
2021-08-29 08:10:29 +02:00
else if (!(ahmet->flags & 2) && item->frameNumber > (g_Level.Anims[item->animNumber].frameBase + 32) && (item->touchBits & AHMET_RIGHT_TOUCH))
{
2020-07-07 07:32:33 +02:00
LaraItem->hitStatus = true;
LaraItem->hitPoints -= AHMET_HAND_DAMAGE;
CreatureEffect2(item, &ahmetBiteRight, 10, -1, DoBloodSplat);
ahmet->flags |= 2;
}
2021-08-29 08:10:29 +02:00
break;
case STATE_AHMET_JUMP_BITE:
ahmet->maximumTurn = 0;
if (item->animNumber == Objects[item->objectNumber].animIndex + AHMET_START_JUMP_ANIM)
{
if (abs(info.angle) >= 910)
{
if (info.angle >= 0)
item->pos.yRot += 910;
else
item->pos.yRot -= 910;
}
else
{
item->pos.yRot += info.angle;
}
}
else
{
if (!(ahmet->flags & 1) && item->animNumber == Objects[item->objectNumber].animIndex + AHMET_JUMP_ATK_ANIM)
{
if (item->frameNumber > (g_Level.Anims[item->animNumber].frameBase + 11) && (item->touchBits & AHMET_LEFT_TOUCH))
{
LaraItem->hitStatus = true;
LaraItem->hitPoints -= AHMET_JAW_DAMAGE;
CreatureEffect2(item, &ahmetBiteJaw, 10, -1, DoBloodSplat);
ahmet->flags |= 1;
}
}
}
break;
case STATE_AHMET_JUMP_DUALATK:
ahmet->maximumTurn = 0;
if (item->animNumber == Objects[item->objectNumber].animIndex + AHMET_START_JUMP_ANIM)
{
if (abs(info.angle) >= 910)
{
if (info.angle >= 0)
item->pos.yRot += 910;
else
item->pos.yRot -= 910;
}
else
{
item->pos.yRot += info.angle;
}
}
else
{
if (!(ahmet->flags & 1) && item->frameNumber > (g_Level.Anims[item->animNumber].frameBase + 14) && (item->touchBits & AHMET_LEFT_TOUCH))
{
LaraItem->hitStatus = true;
LaraItem->hitPoints -= AHMET_HAND_DAMAGE;
CreatureEffect2(item, &ahmetBiteLeft, 10, -1, DoBloodSplat);
ahmet->flags |= 1;
}
else if (!(ahmet->flags & 2) && item->frameNumber > (g_Level.Anims[item->animNumber].frameBase + 22) && (item->touchBits & AHMET_RIGHT_TOUCH))
{
LaraItem->hitStatus = true;
LaraItem->hitPoints -= AHMET_HAND_DAMAGE;
CreatureEffect2(item, &ahmetBiteRight, 10, -1, DoBloodSplat);
ahmet->flags |= 2;
}
}
break;
}
}
2021-08-29 08:10:29 +02:00
CreatureTilt(item, 0);
CreatureJoint(item, 0, head_y);
AhmetHeavyTriggers(item);
CreatureAnimation(itemNumber, angle, 0);
}
2021-08-29 08:10:29 +02:00
bool RespawnAhmet(short itemNumber)
{
ITEM_INFO* item = &g_Level.Items[itemNumber];
2021-08-29 08:10:29 +02:00
if (item->currentAnimState != 7 || item->frameNumber != g_Level.Anims[item->animNumber].frameEnd)
return false;
Weather.Flash(255, 64, 0, 0.03f);
2021-08-29 08:10:29 +02:00
item->pos.xPos = (item->itemFlags[0] * 1024) + 512;
item->pos.yPos = (item->itemFlags[1] * 256);
item->pos.zPos = (item->itemFlags[2] * 1024) + 512;
2021-08-29 08:10:29 +02:00
IsRoomOutside(item->pos.xPos, item->pos.yPos, item->pos.zPos);
2021-08-29 08:10:29 +02:00
if (item->roomNumber != IsRoomOutsideNo)
ItemNewRoom(itemNumber, IsRoomOutsideNo);
2021-08-29 08:10:29 +02:00
item->animNumber = Objects[item->objectNumber].animIndex;
item->goalAnimState = 1;
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
item->currentAnimState = 1;
item->hitPoints = Objects[item->objectNumber].hitPoints;
2021-08-29 08:10:29 +02:00
AddActiveItem(itemNumber);
2021-08-29 08:10:29 +02:00
item->flags &= 0xFE;
item->afterDeath = 0;
item->status = ITEM_ACTIVE;
item->collidable = true;
2021-08-29 08:10:29 +02:00
EnableBaddieAI(itemNumber, 1);
2021-08-29 08:10:29 +02:00
item->triggerFlags = 1;
return true;
}
}