2020-05-27 09:21:20 +02:00
|
|
|
#include "framework.h"
|
2020-05-27 13:23:38 +02:00
|
|
|
#include "tr4_sas.h"
|
2021-09-19 23:41:26 +03:00
|
|
|
#include "control/box.h"
|
2020-05-27 09:21:20 +02:00
|
|
|
#include "items.h"
|
|
|
|
#include "people.h"
|
|
|
|
#include "lara.h"
|
|
|
|
#include "setup.h"
|
|
|
|
#include "level.h"
|
2021-09-19 23:41:26 +03:00
|
|
|
#include "control/control.h"
|
2021-09-16 05:06:03 +03:00
|
|
|
#include "animation.h"
|
|
|
|
#include "effects/effects.h"
|
|
|
|
#include "effects/tomb4fx.h"
|
|
|
|
#include "Specific/input.h"
|
|
|
|
#include "Lara/lara_one_gun.h"
|
|
|
|
#include "itemdata/creature_info.h"
|
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
namespace TEN::Entities::TR4
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
enum SAS_STATES
|
|
|
|
{
|
|
|
|
STATE_SAS_EMPTY,
|
|
|
|
STATE_SAS_STOP,
|
|
|
|
STATE_SAS_WALK,
|
|
|
|
STATE_SAS_RUN,
|
|
|
|
STATE_SAS_WAIT,
|
|
|
|
STATE_SAS_SIGHT_SHOOT,
|
|
|
|
STATE_SAS_WALK_SHOOT,
|
|
|
|
STATE_SAS_DEATH,
|
|
|
|
STATE_SAS_SIGHT_AIM,
|
|
|
|
STATE_SAS_WALK_AIM,
|
|
|
|
STATE_SAS_HOLD_AIM,
|
|
|
|
STATE_SAS_HOLD_SHOOT,
|
|
|
|
STATE_SAS_KNEEL_AIM,
|
|
|
|
STATE_SAS_KNEEL_SHOOT,
|
|
|
|
STATE_SAS_KNEEL_STOP,
|
|
|
|
STATE_SAS_HOLD_PREPARE_GRENADE,
|
|
|
|
STATE_SAS_HOLD_SHOOT_GRENADE,
|
|
|
|
STATE_SAS_BLIND
|
|
|
|
};
|
|
|
|
|
|
|
|
enum SAS_ANIM
|
|
|
|
{
|
|
|
|
ANIMATION_SAS_WALK,
|
|
|
|
ANIMATION_SAS_RUN,
|
|
|
|
ANIMATION_SAS_SIGHT_SHOOT,
|
|
|
|
ANIMATION_SAS_STAND_TO_SIGHT_AIM,
|
|
|
|
ANIMATION_SAS_WALK_TO_WALK_AIM,
|
|
|
|
ANIMATION_SAS_RUN_TO_WALK,
|
|
|
|
ANIMATION_SAS_WALK_SHOOT,
|
|
|
|
ANIMATION_SAS_SIGHT_AIM_TO_STAND,
|
|
|
|
ANIMATION_SAS_WALK_AIM_TO_STAND,
|
|
|
|
ANIMATION_SAS_STAND_TO_RUN,
|
|
|
|
ANIMATION_SAS_STAND_TO_WAIT,
|
|
|
|
ANIMATION_SAS_STAND_TO_WALK,
|
|
|
|
ANIMATION_SAS_STAND,
|
|
|
|
ANIMATION_SAS_WAIT_TO_STAND,
|
|
|
|
ANIMATION_SAS_WAIT,
|
|
|
|
ANIMATION_SAS_WAIT_TO_SIGHT_AIM,
|
|
|
|
ANIMATION_SAS_WALK_TO_RUN,
|
|
|
|
ANIMATION_SAS_WALK_TO_STAND,
|
|
|
|
ANIMATION_SAS_WALK_AIM_TO_WALK,
|
|
|
|
ANIMATION_SAS_DEATH,
|
|
|
|
ANIMATION_SAS_STAND_TO_HOLD_AIM,
|
|
|
|
ANIMATION_SAS_HOLD_SHOOT,
|
|
|
|
ANIMATION_SAS_HOLD_AIM_TO_STAND,
|
|
|
|
ANIMATION_SAS_HOLD_PREPARE_GRENADE,
|
|
|
|
ANIMATION_SAS_HOLD_SHOOT_GRENADE,
|
|
|
|
ANIMATION_SAS_STAND_TO_KNEEL_AIM,
|
|
|
|
ANIMATION_SAS_KNEEL_SHOOT,
|
|
|
|
ANIMATION_SAS_KNEEL_AIM_TO_STAND,
|
|
|
|
ANIMATION_SAS_BLIND,
|
|
|
|
ANIMATION_SAS_BLIND_TO_STAND
|
|
|
|
};
|
|
|
|
|
|
|
|
BITE_INFO sasGun = { 0, 300, 64, 7 };
|
|
|
|
|
|
|
|
PHD_VECTOR SasDragBlokePosition = { 0, 0, -460 };
|
2021-10-27 19:00:30 +03:00
|
|
|
OBJECT_COLLISION_BOUNDS SasDragBlokeBounds =
|
|
|
|
{ -256, 256, -64, 100, -200, -460, ANGLE(-10), ANGLE(10), ANGLE(-30), ANGLE(30), 0, 0 };
|
2021-08-28 19:12:19 +02:00
|
|
|
|
|
|
|
void InitialiseSas(short itemNumber)
|
|
|
|
{
|
|
|
|
ITEM_INFO* item = &g_Level.Items[itemNumber];
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
ClearItem(itemNumber);
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
item->animNumber = Objects[item->objectNumber].animIndex + ANIMATION_SAS_STAND;
|
|
|
|
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
|
|
|
|
item->goalAnimState = STATE_SAS_STOP;
|
|
|
|
item->currentAnimState = STATE_SAS_STOP;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
void SasControl(short itemNumber)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (!CreatureActive(itemNumber))
|
|
|
|
return;
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
ITEM_INFO* item = &g_Level.Items[itemNumber];
|
|
|
|
CREATURE_INFO* creature = (CREATURE_INFO*)item->data;
|
|
|
|
ITEM_INFO* enemyItem = creature->enemy;
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
short tilt = 0;
|
|
|
|
short angle = 0;
|
|
|
|
short joint0 = 0;
|
|
|
|
short joint1 = 0;
|
|
|
|
short joint2 = 0;
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
// Handle SAS firing
|
|
|
|
if (item->firedWeapon)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
PHD_VECTOR pos;
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
pos.x = sasGun.x;
|
|
|
|
pos.y = sasGun.y;
|
|
|
|
pos.z = sasGun.z;
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
GetJointAbsPosition(item, &pos, sasGun.meshNum);
|
|
|
|
TriggerDynamicLight(pos.x, pos.y, pos.z, 10, 24, 16, 4);
|
|
|
|
item->firedWeapon--;
|
|
|
|
}
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
if (item->hitPoints > 0)
|
|
|
|
{
|
|
|
|
if (item->aiBits)
|
|
|
|
GetAITarget(creature);
|
|
|
|
else
|
|
|
|
creature->enemy = LaraItem;
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
AI_INFO info;
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
int distance = 0;
|
|
|
|
int ang = 0;
|
|
|
|
int dx = 0;
|
|
|
|
int dz = 0;
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
CreatureAIInfo(item, &info);
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
if (creature->enemy == LaraItem)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
ang = info.angle;
|
|
|
|
distance = info.distance;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
else
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
dx = LaraItem->pos.xPos - item->pos.xPos;
|
|
|
|
dz = LaraItem->pos.zPos - item->pos.zPos;
|
|
|
|
ang = phd_atan(dz, dx) - item->pos.yRot;
|
|
|
|
distance = dx * dx + dz * dz;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
GetCreatureMood(item, &info, creature->enemy != LaraItem);
|
|
|
|
|
|
|
|
// Vehicle handling
|
|
|
|
if (Lara.Vehicle != NO_ITEM && info.bite)
|
2021-09-25 16:03:28 -05:00
|
|
|
creature->mood = ESCAPE_MOOD;
|
2021-08-28 19:12:19 +02:00
|
|
|
|
|
|
|
CreatureMood(item, &info, creature->enemy != LaraItem);
|
|
|
|
angle = CreatureTurn(item, creature->maximumTurn);
|
|
|
|
|
|
|
|
if (item->hitStatus)
|
|
|
|
AlertAllGuards(itemNumber);
|
|
|
|
|
|
|
|
int angle1 = 0;
|
|
|
|
int angle2 = 0;
|
|
|
|
|
|
|
|
switch (item->currentAnimState)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
case STATE_SAS_STOP:
|
|
|
|
creature->flags = 0;
|
|
|
|
creature->maximumTurn = 0;
|
|
|
|
joint2 = ang;
|
|
|
|
if (item->animNumber == Objects[item->objectNumber].animIndex + ANIMATION_SAS_WALK_TO_STAND)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (abs(info.angle) >= ANGLE(10))
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (info.angle >= 0)
|
|
|
|
item->pos.yRot += ANGLE(10);
|
|
|
|
else
|
|
|
|
item->pos.yRot -= ANGLE(10);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item->pos.yRot += info.angle;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
else if (item->aiBits & MODIFY || Lara.Vehicle != NO_ITEM)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (abs(info.angle) >= ANGLE(2))
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (info.angle >= 0)
|
|
|
|
item->pos.yRot += ANGLE(2);
|
2019-12-05 17:35:57 +01:00
|
|
|
else
|
2021-08-28 19:12:19 +02:00
|
|
|
item->pos.yRot -= ANGLE(2);
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
else
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
item->pos.yRot += info.angle;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
|
|
|
|
if (item->aiBits & GUARD)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
joint2 = AIGuard(creature);
|
|
|
|
if (!GetRandomControl())
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (item->currentAnimState == STATE_SAS_STOP)
|
|
|
|
{
|
|
|
|
item->goalAnimState = STATE_SAS_WAIT;
|
|
|
|
break;
|
|
|
|
}
|
2020-05-27 13:23:38 +02:00
|
|
|
item->goalAnimState = STATE_SAS_STOP;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
}
|
|
|
|
else if (!(item->aiBits & PATROL1)
|
|
|
|
|| item->aiBits & MODIFY
|
|
|
|
|| Lara.Vehicle != NO_ITEM)
|
|
|
|
{
|
|
|
|
if (Targetable(item, &info))
|
|
|
|
{
|
|
|
|
if (info.distance < SQUARE(3072)
|
|
|
|
|| info.zoneNumber != info.enemyZone)
|
|
|
|
{
|
|
|
|
if (GetRandomControl() & 1)
|
|
|
|
{
|
|
|
|
item->goalAnimState = STATE_SAS_SIGHT_AIM;
|
|
|
|
}
|
|
|
|
else if (GetRandomControl() & 1)
|
|
|
|
{
|
|
|
|
item->goalAnimState = STATE_SAS_HOLD_AIM;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item->goalAnimState = STATE_SAS_KNEEL_AIM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!(item->aiBits & MODIFY))
|
|
|
|
{
|
|
|
|
item->goalAnimState = STATE_SAS_WALK;
|
|
|
|
}
|
|
|
|
}
|
2019-12-05 17:35:57 +01:00
|
|
|
else
|
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (item->aiBits & MODIFY)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
item->goalAnimState = STATE_SAS_STOP;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (creature->mood == ESCAPE_MOOD)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2020-05-27 13:23:38 +02:00
|
|
|
item->goalAnimState = STATE_SAS_RUN;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if ((creature->alerted
|
|
|
|
|| creature->mood != BORED_MOOD)
|
|
|
|
&& (!(item->aiBits & FOLLOW)
|
|
|
|
|| !creature->reachedGoal
|
|
|
|
&& distance <= SQUARE(2048)))
|
|
|
|
{
|
|
|
|
if (creature->mood == BORED_MOOD
|
|
|
|
|| info.distance <= SQUARE(2048))
|
|
|
|
{
|
|
|
|
item->goalAnimState = STATE_SAS_WALK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
item->goalAnimState = STATE_SAS_RUN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item->goalAnimState = STATE_SAS_STOP;
|
|
|
|
}
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
item->goalAnimState = STATE_SAS_WALK;
|
|
|
|
joint2 = 0;
|
|
|
|
}
|
|
|
|
break;
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
case STATE_SAS_WAIT:
|
|
|
|
joint2 = ang;
|
|
|
|
creature->flags = 0;
|
|
|
|
creature->maximumTurn = 0;
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
if (item->aiBits & GUARD)
|
|
|
|
{
|
|
|
|
joint2 = AIGuard(creature);
|
|
|
|
if (!GetRandomControl())
|
|
|
|
{
|
|
|
|
item->goalAnimState = STATE_SAS_STOP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (Targetable(item, &info)
|
|
|
|
|| creature->mood != BORED_MOOD
|
|
|
|
|| !info.ahead
|
|
|
|
|| item->aiBits & MODIFY
|
|
|
|
|| Lara.Vehicle != NO_ITEM)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2020-05-27 13:23:38 +02:00
|
|
|
item->goalAnimState = STATE_SAS_STOP;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
break;
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
case STATE_SAS_WALK:
|
|
|
|
creature->flags = 0;
|
|
|
|
creature->maximumTurn = ANGLE(5);
|
|
|
|
joint2 = ang;
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
if (item->aiBits & PATROL1)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
item->goalAnimState = STATE_SAS_WALK;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
else if (Lara.Vehicle == NO_ITEM
|
|
|
|
|| !(item->aiBits & MODIFY)
|
|
|
|
&& item->aiBits)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (creature->mood == ESCAPE_MOOD)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
item->goalAnimState = STATE_SAS_RUN;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
else
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (item->aiBits & GUARD
|
|
|
|
|| item->aiBits & FOLLOW
|
|
|
|
&& (creature->reachedGoal
|
|
|
|
|| distance > SQUARE(2048)))
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2020-05-27 13:23:38 +02:00
|
|
|
item->goalAnimState = STATE_SAS_STOP;
|
2019-12-05 17:35:57 +01:00
|
|
|
break;
|
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
if (Targetable(item, &info))
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (info.distance < SQUARE(3072)
|
|
|
|
|| info.enemyZone != info.zoneNumber)
|
|
|
|
{
|
|
|
|
item->goalAnimState = STATE_SAS_STOP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
item->goalAnimState = STATE_SAS_WALK_AIM;
|
|
|
|
}
|
|
|
|
else if (creature->mood)
|
|
|
|
{
|
|
|
|
if (info.distance > SQUARE(2048))
|
|
|
|
{
|
|
|
|
item->goalAnimState = STATE_SAS_RUN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (info.ahead)
|
|
|
|
{
|
|
|
|
item->goalAnimState = STATE_SAS_STOP;
|
|
|
|
break;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
else
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
item->goalAnimState = STATE_SAS_STOP;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
|
|
|
break;
|
2021-04-22 05:58:40 +02:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
case STATE_SAS_RUN:
|
|
|
|
if (info.ahead)
|
|
|
|
joint2 = info.angle;
|
|
|
|
creature->maximumTurn = ANGLE(10);
|
|
|
|
tilt = angle / 2;
|
|
|
|
|
|
|
|
if (Lara.Vehicle != NO_ITEM)
|
|
|
|
{
|
|
|
|
if (item->aiBits & MODIFY || !item->aiBits)
|
|
|
|
{
|
|
|
|
item->goalAnimState = STATE_SAS_WAIT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (item->aiBits & GUARD
|
|
|
|
|| item->aiBits & FOLLOW
|
|
|
|
&& (creature->reachedGoal
|
|
|
|
|| distance > SQUARE(2048)))
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2020-05-27 13:23:38 +02:00
|
|
|
item->goalAnimState = STATE_SAS_WALK;
|
2021-08-28 19:12:19 +02:00
|
|
|
break;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
|
|
|
|
if (creature->mood != ESCAPE_MOOD)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (Targetable(item, &info))
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2020-05-27 13:23:38 +02:00
|
|
|
item->goalAnimState = STATE_SAS_WALK;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (creature->mood != BORED_MOOD
|
|
|
|
|| creature->mood == STALK_MOOD
|
|
|
|
&& item->aiBits & FOLLOW
|
|
|
|
&& info.distance < SQUARE(2048))
|
|
|
|
{
|
|
|
|
item->goalAnimState = STATE_SAS_WALK;
|
|
|
|
}
|
|
|
|
}
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
break;
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
case STATE_SAS_SIGHT_AIM:
|
|
|
|
case STATE_SAS_HOLD_AIM:
|
|
|
|
case STATE_SAS_KNEEL_AIM:
|
|
|
|
creature->flags = 0;
|
|
|
|
if (info.ahead)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
joint1 = info.xAngle;
|
|
|
|
joint0 = info.angle;
|
|
|
|
if (Targetable(item, &info))
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (item->currentAnimState == STATE_SAS_SIGHT_AIM)
|
|
|
|
{
|
|
|
|
item->goalAnimState = STATE_SAS_SIGHT_SHOOT;
|
|
|
|
}
|
|
|
|
else if (item->currentAnimState == STATE_SAS_KNEEL_AIM)
|
|
|
|
{
|
|
|
|
item->goalAnimState = STATE_SAS_KNEEL_SHOOT;
|
|
|
|
}
|
|
|
|
else if (GetRandomControl() & 1)
|
|
|
|
{
|
|
|
|
item->goalAnimState = STATE_SAS_HOLD_SHOOT;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item->goalAnimState = STATE_SAS_HOLD_PREPARE_GRENADE;
|
|
|
|
}
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
else
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
item->goalAnimState = STATE_SAS_STOP;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_SAS_WALK_AIM:
|
|
|
|
creature->flags = 0;
|
|
|
|
if (info.ahead)
|
|
|
|
{
|
|
|
|
joint1 = info.xAngle;
|
|
|
|
joint0 = info.angle;
|
|
|
|
if (Targetable(item, &info))
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
item->goalAnimState = STATE_SAS_WALK_SHOOT;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
2021-04-22 05:58:40 +02:00
|
|
|
else
|
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
item->goalAnimState = STATE_SAS_WALK;
|
2021-04-22 05:58:40 +02:00
|
|
|
}
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_SAS_HOLD_PREPARE_GRENADE:
|
|
|
|
if (info.ahead)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
joint1 = info.xAngle;
|
|
|
|
joint0 = info.angle;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
break;
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
case STATE_SAS_HOLD_SHOOT_GRENADE:
|
|
|
|
if (info.ahead)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
angle1 = info.angle;
|
|
|
|
angle2 = info.xAngle;
|
|
|
|
joint1 = info.xAngle;
|
|
|
|
joint0 = info.angle;
|
|
|
|
|
|
|
|
if (info.distance > SQUARE(3072))
|
|
|
|
{
|
|
|
|
angle2 = sqrt(info.distance) + info.xAngle - 1024;
|
|
|
|
joint1 = angle2;
|
|
|
|
}
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
angle1 = 0;
|
|
|
|
angle2 = 0;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
if (item->frameNumber == g_Level.Anims[item->animNumber].frameBase + 20)
|
|
|
|
{
|
|
|
|
if (!creature->enemy->speed)
|
|
|
|
{
|
|
|
|
angle2 += (GetRandomControl() & 0x1FF) - 256;
|
|
|
|
joint1 = angle2;
|
|
|
|
angle1 += (GetRandomControl() & 0x1FF) - 256;
|
|
|
|
joint0 = angle1;
|
|
|
|
}
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
SasFireGrenade(item, angle2, angle1);
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
if (Targetable(item, &info))
|
|
|
|
item->goalAnimState = STATE_SAS_HOLD_PREPARE_GRENADE;
|
|
|
|
}
|
|
|
|
break;
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
case STATE_SAS_HOLD_SHOOT:
|
|
|
|
case STATE_SAS_KNEEL_SHOOT:
|
|
|
|
case STATE_SAS_SIGHT_SHOOT:
|
|
|
|
case STATE_SAS_WALK_SHOOT:
|
|
|
|
if (item->currentAnimState == STATE_SAS_HOLD_SHOOT
|
|
|
|
|| item->currentAnimState == STATE_SAS_KNEEL_SHOOT)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (item->goalAnimState != STATE_SAS_STOP
|
|
|
|
&& item->goalAnimState != STATE_SAS_KNEEL_STOP
|
|
|
|
&& (creature->mood == ESCAPE_MOOD
|
|
|
|
|| !Targetable(item, &info)))
|
|
|
|
{
|
|
|
|
if (item->currentAnimState == STATE_SAS_HOLD_SHOOT)
|
|
|
|
item->goalAnimState = STATE_SAS_STOP;
|
|
|
|
else
|
|
|
|
item->goalAnimState = STATE_SAS_KNEEL_STOP;
|
|
|
|
}
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
if (info.ahead)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
joint0 = info.angle;
|
|
|
|
joint1 = info.xAngle;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
|
|
|
|
if (creature->flags)
|
|
|
|
creature->flags -= 1;
|
|
|
|
else
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
ShotLara(item, &info, &sasGun, joint0, 15);
|
|
|
|
creature->flags = 5;
|
|
|
|
item->firedWeapon = 3;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_SAS_BLIND:
|
|
|
|
if (!WeaponEnemyTimer && !(GetRandomControl() & 0x7F))
|
|
|
|
item->goalAnimState = STATE_SAS_WAIT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
if (WeaponEnemyTimer > 100
|
|
|
|
&& item->currentAnimState != STATE_SAS_BLIND)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
creature->maximumTurn = 0;
|
|
|
|
item->animNumber = Objects[item->objectNumber].animIndex + ANIMATION_SAS_BLIND;
|
|
|
|
item->frameNumber = g_Level.Anims[item->animNumber].frameBase + (GetRandomControl() & 7);
|
|
|
|
item->currentAnimState = STATE_SAS_BLIND;
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
else
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (item->currentAnimState != STATE_SAS_DEATH)
|
|
|
|
{
|
|
|
|
item->animNumber = Objects[item->objectNumber].animIndex + ANIMATION_SAS_DEATH;
|
|
|
|
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
|
|
|
|
item->currentAnimState = STATE_SAS_DEATH;
|
|
|
|
}
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
2020-05-27 13:23:38 +02:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
CreatureTilt(item, tilt);
|
|
|
|
CreatureJoint(item, 0, joint0);
|
|
|
|
CreatureJoint(item, 1, joint1);
|
|
|
|
CreatureJoint(item, 2, joint2);
|
|
|
|
|
|
|
|
CreatureAnimation(itemNumber, angle, 0);
|
2019-12-05 17:35:57 +01:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
|
|
|
|
void SasFireGrenade(ITEM_INFO* item, short angle1, short angle2)
|
2019-12-05 17:35:57 +01:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
short itemNumber = CreateItem();
|
|
|
|
if (itemNumber != NO_ITEM)
|
2021-04-22 05:58:40 +02:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
ITEM_INFO* grenadeItem = &g_Level.Items[itemNumber];
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
grenadeItem->shade = -15856;
|
|
|
|
grenadeItem->objectNumber = ID_GRENADE;
|
|
|
|
grenadeItem->roomNumber = item->roomNumber;
|
2019-12-05 17:35:57 +01:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
PHD_VECTOR pos;
|
|
|
|
pos.x = sasGun.x;
|
|
|
|
pos.y = sasGun.y;
|
|
|
|
pos.z = sasGun.z;
|
2020-08-06 20:41:07 +02:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
GetJointAbsPosition(item, &pos, sasGun.meshNum);
|
2020-08-06 20:41:07 +02:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
grenadeItem->pos.xPos = pos.x;
|
|
|
|
grenadeItem->pos.yPos = pos.y;
|
|
|
|
grenadeItem->pos.zPos = pos.z;
|
|
|
|
|
|
|
|
FLOOR_INFO* floor = GetFloor(pos.x, pos.y, pos.z, &grenadeItem->roomNumber);
|
|
|
|
int height = GetFloorHeight(floor, pos.x, pos.y, pos.z);
|
|
|
|
|
|
|
|
if (height < grenadeItem->pos.yPos)
|
|
|
|
{
|
|
|
|
grenadeItem->pos.xPos = item->pos.xPos;
|
|
|
|
grenadeItem->pos.yPos = height;
|
|
|
|
grenadeItem->pos.zPos = item->pos.zPos;
|
|
|
|
grenadeItem->roomNumber = item->roomNumber;
|
|
|
|
}
|
|
|
|
|
|
|
|
SmokeCountL = 32;
|
|
|
|
SmokeWeapon = 5;
|
|
|
|
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
|
|
{
|
|
|
|
TriggerGunSmoke(pos.x, pos.y, pos.z, 0, 0, 0, 1, 5, 32);
|
|
|
|
}
|
|
|
|
|
|
|
|
InitialiseItem(itemNumber);
|
|
|
|
|
|
|
|
grenadeItem->pos.xRot = angle1 + item->pos.xRot;
|
|
|
|
grenadeItem->pos.yRot = angle2 + item->pos.yRot;
|
|
|
|
grenadeItem->pos.zRot = 0;
|
|
|
|
|
|
|
|
if (GetRandomControl() & 3)
|
|
|
|
{
|
|
|
|
grenadeItem->itemFlags[0] = GRENADE_NORMAL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
grenadeItem->itemFlags[0] = GRENADE_SUPER;
|
|
|
|
}
|
|
|
|
|
|
|
|
grenadeItem->itemFlags[2] = 1;
|
|
|
|
grenadeItem->speed = 128;
|
|
|
|
grenadeItem->currentAnimState = grenadeItem->pos.xRot;
|
|
|
|
grenadeItem->fallspeed = -128 * phd_sin(grenadeItem->pos.xRot);
|
|
|
|
grenadeItem->goalAnimState = grenadeItem->pos.yRot;
|
|
|
|
grenadeItem->requiredAnimState = 0;
|
|
|
|
grenadeItem->hitPoints = 120;
|
|
|
|
|
|
|
|
AddActiveItem(itemNumber);
|
2021-04-22 05:58:40 +02:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
}
|
2021-04-22 05:58:40 +02:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
void InitialiseInjuredSas(short itemNumber)
|
|
|
|
{
|
|
|
|
ITEM_INFO* item = &g_Level.Items[itemNumber];
|
2021-04-22 05:58:40 +02:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
if (item->triggerFlags)
|
2021-04-22 05:58:40 +02:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
item->animNumber = Objects[item->objectNumber].animIndex;
|
|
|
|
item->goalAnimState = item->currentAnimState = 1;
|
2021-04-22 05:58:40 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
item->animNumber = Objects[item->objectNumber].animIndex + 3;
|
|
|
|
item->goalAnimState = item->currentAnimState = 4;
|
2021-04-22 05:58:40 +02:00
|
|
|
}
|
2020-08-06 20:41:07 +02:00
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
|
2020-08-06 20:41:07 +02:00
|
|
|
}
|
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
void InjuredSasControl(short itemNumber)
|
2020-08-06 20:41:07 +02:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
ITEM_INFO* item = &g_Level.Items[itemNumber];
|
|
|
|
|
|
|
|
if (item->currentAnimState == 1)
|
2020-08-06 20:41:07 +02:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (!(GetRandomControl() & 0x7F))
|
|
|
|
{
|
|
|
|
item->goalAnimState = 2;
|
|
|
|
AnimateItem(item);
|
|
|
|
}
|
|
|
|
else if (!(byte)GetRandomControl())
|
|
|
|
{
|
|
|
|
item->goalAnimState = 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (item->currentAnimState == 4 && !(GetRandomControl() & 0x7F))
|
2020-08-06 20:41:07 +02:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
item->goalAnimState = 5;
|
2020-08-06 20:41:07 +02:00
|
|
|
AnimateItem(item);
|
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
else
|
2020-08-06 20:41:07 +02:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
AnimateItem(item);
|
2020-08-06 20:41:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-28 19:12:19 +02:00
|
|
|
void SasDragBlokeCollision(short itemNumber, ITEM_INFO* l, COLL_INFO* c)
|
2020-08-06 20:41:07 +02:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
ITEM_INFO* item = &g_Level.Items[itemNumber];
|
|
|
|
|
|
|
|
if ((!(TrInput & IN_ACTION)
|
|
|
|
|| l->currentAnimState != LS_STOP
|
|
|
|
|| l->animNumber != LA_STAND_IDLE
|
|
|
|
|| Lara.gunStatus
|
|
|
|
|| l->gravityStatus
|
|
|
|
|| item->flags & 0x3E00)
|
2021-09-05 11:18:37 +02:00
|
|
|
&& (!(Lara.isMoving) || Lara.interactedItem != itemNumber))
|
2020-08-06 20:41:07 +02:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (item->status == ITEM_ACTIVE)
|
2020-08-06 20:41:07 +02:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (item->frameNumber == g_Level.Anims[item->animNumber].frameEnd)
|
|
|
|
{
|
|
|
|
int x = l->pos.xPos - 512 * phd_sin(l->pos.yRot);
|
|
|
|
int y = l->pos.yPos;
|
|
|
|
int z = l->pos.zPos - 512 * phd_cos(l->pos.yRot);
|
|
|
|
|
2021-09-15 16:58:53 +03:00
|
|
|
TestTriggers(x, y, z, l->roomNumber, true);
|
2021-08-28 19:12:19 +02:00
|
|
|
|
|
|
|
RemoveActiveItem(itemNumber);
|
|
|
|
item->status = ITEM_NOT_ACTIVE;
|
|
|
|
}
|
2020-08-06 20:41:07 +02:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
|
|
|
|
ObjectCollision(itemNumber, l, c);
|
2020-08-06 20:41:07 +02:00
|
|
|
}
|
2021-08-28 19:12:19 +02:00
|
|
|
else
|
2020-08-06 20:41:07 +02:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (TestLaraPosition(&SasDragBlokeBounds, item, l))
|
2020-08-06 20:41:07 +02:00
|
|
|
{
|
2021-08-28 19:12:19 +02:00
|
|
|
if (MoveLaraPosition(&SasDragBlokePosition, item, l))
|
|
|
|
{
|
|
|
|
l->animNumber = LA_DRAG_BODY;
|
|
|
|
l->currentAnimState = LS_MISC_CONTROL;
|
|
|
|
l->frameNumber = g_Level.Anims[l->animNumber].frameBase;
|
|
|
|
l->pos.yRot = item->pos.yRot;
|
|
|
|
Lara.isMoving = false;
|
|
|
|
Lara.headXrot = 0;
|
|
|
|
Lara.headYrot = 0;
|
|
|
|
Lara.torsoXrot = 0;
|
|
|
|
Lara.torsoYrot = 0;
|
|
|
|
Lara.gunStatus = LG_HANDS_BUSY;
|
|
|
|
item->flags |= 0x3E00;
|
|
|
|
item->status = ITEM_ACTIVE;
|
|
|
|
AddActiveItem(itemNumber);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-09-05 11:18:37 +02:00
|
|
|
Lara.interactedItem = itemNumber;
|
2021-08-28 19:12:19 +02:00
|
|
|
}
|
2020-08-06 20:41:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-08-29 13:29:55 +02:00
|
|
|
}
|