
Source code release, imported from: https://www.gamefront.com/games/aliens-vs-predator-3/file/avp-gold-complete-source-code All text files were converted to Unix format.
1335 lines
No EOL
37 KiB
C
1335 lines
No EOL
37 KiB
C
/* Patrick 9/7/97 ----------------------------------------------------
|
|
Source file for Facehugger AI behaviour functions....
|
|
--------------------------------------------------------------------*/
|
|
#include "3dc.h"
|
|
|
|
#include "inline.h"
|
|
#include "module.h"
|
|
#include "stratdef.h"
|
|
#include "gamedef.h"
|
|
#include "dynblock.h"
|
|
#include "dynamics.h"
|
|
#include "comp_shp.h"
|
|
#include "bh_types.h"
|
|
#include "bh_pred.h"
|
|
#include "bh_fhug.h"
|
|
#include "bh_debri.h"
|
|
#include "pfarlocs.h"
|
|
#include "pvisible.h"
|
|
#include "weapons.h"
|
|
#include "psnd.h"
|
|
#include "psndplat.h"
|
|
#include "targeting.h"
|
|
|
|
#define UseLocalAssert Yes
|
|
#include "ourasert.h"
|
|
#include "ShowCmds.h"
|
|
#include "sfx.h"
|
|
|
|
#define HUGGER_STATE_PRINT 0
|
|
|
|
/* external global variables used in this file */
|
|
extern int ModuleArraySize;
|
|
extern char *ModuleCurrVisArray;
|
|
extern int NormalFrameTime;
|
|
extern int cosine[], sine[];
|
|
|
|
extern ACTIVESOUNDSAMPLE ActiveSounds[];
|
|
|
|
/* prototypes for this file */
|
|
static void Execute_FHNS_Approach(STRATEGYBLOCK *sbPtr);
|
|
static void Execute_FHNS_Attack(STRATEGYBLOCK *sbPtr);
|
|
static void Execute_FHNS_Wait(STRATEGYBLOCK *sbPtr);
|
|
static void Execute_FHNS_Avoidance(STRATEGYBLOCK *sbPtr);
|
|
static void Execute_FHNS_Dying(STRATEGYBLOCK *sbPtr);
|
|
static void Execute_FHNS_Float(STRATEGYBLOCK *sbPtr);
|
|
static void Execute_FHNS_Jumping(STRATEGYBLOCK *sbPtr);
|
|
static void Execute_FHNS_AboutToJump(STRATEGYBLOCK *sbPtr);
|
|
|
|
void Wake_Hugger(STRATEGYBLOCK *sbPtr);
|
|
|
|
static int HuggerShouldAttackPlayer(void);
|
|
static void SetHuggerAnimationSequence(STRATEGYBLOCK *sbPtr, HUGGER_SUBSEQUENCES seq, int length);
|
|
static void KillFaceHugger(STRATEGYBLOCK *sbPtr,DAMAGE_PROFILE *damage);
|
|
|
|
static int InContactWithPlayer(DYNAMICSBLOCK *dynPtr);
|
|
static void JumpAtPlayer(STRATEGYBLOCK *sbPtr);
|
|
|
|
extern SECTION *GetHierarchyFromLibrary(const char *rif_name);
|
|
|
|
/* -------------------------------------------------------------------
|
|
Initilaiser, damage, and visibility functions + behaviour shell
|
|
--------------------------------------------------------------------*/
|
|
void InitFacehuggerBehaviour(void* bhdata, STRATEGYBLOCK *sbPtr)
|
|
{
|
|
TOOLS_DATA_FACEHUGGER *toolsData;
|
|
int i;
|
|
|
|
LOCALASSERT(bhdata);
|
|
LOCALASSERT(sbPtr);
|
|
toolsData = (TOOLS_DATA_FACEHUGGER *)bhdata;
|
|
|
|
/* check we're not in a net game */
|
|
if(AvP.Network != I_No_Network)
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
|
|
/* make the assumption that the loader has initialised the strategy block sensibly...
|
|
so just set the shapeIndex from the tools data & copy the name id*/
|
|
sbPtr->shapeIndex = toolsData->shapeIndex;
|
|
for(i=0;i<SB_NAME_LENGTH;i++) sbPtr->SBname[i] = toolsData->nameID[i];
|
|
|
|
sbPtr->SBdptr=NULL;
|
|
/* create, initialise and attach a dynamics block */
|
|
sbPtr->DynPtr = AllocateDynamicsBlock(DYNAMICS_TEMPLATE_ALIEN_NPC);
|
|
if(sbPtr->DynPtr)
|
|
{
|
|
EULER zeroEuler = {0,0,0};
|
|
DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
|
|
dynPtr->PrevPosition = dynPtr->Position = toolsData->position;
|
|
dynPtr->OrientEuler = zeroEuler;
|
|
CreateEulerMatrix(&dynPtr->OrientEuler, &dynPtr->OrientMat);
|
|
TransposeMatrixCH(&dynPtr->OrientMat);
|
|
/* zero linear velocity in dynamics block */
|
|
dynPtr->LinVelocity.vx = 0;
|
|
dynPtr->LinVelocity.vy = 0;
|
|
dynPtr->LinVelocity.vz = 0;
|
|
dynPtr->Mass=10;
|
|
}
|
|
else
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
|
|
/* Initialise hugger's stats */
|
|
{
|
|
NPC_DATA *NpcData;
|
|
|
|
NpcData=GetThisNpcData(I_NPC_FaceHugger);
|
|
LOCALASSERT(NpcData);
|
|
sbPtr->SBDamageBlock.Health=NpcData->StartingStats.Health<<ONE_FIXED_SHIFT;
|
|
sbPtr->SBDamageBlock.Armour=NpcData->StartingStats.Armour<<ONE_FIXED_SHIFT;
|
|
sbPtr->SBDamageBlock.SB_H_flags=NpcData->StartingStats.SB_H_flags;
|
|
}
|
|
/* create, initialise and attach a facehugger data block */
|
|
sbPtr->SBdataptr = (void *)AllocateMem(sizeof(FACEHUGGER_STATUS_BLOCK));
|
|
if(sbPtr->SBdataptr)
|
|
{
|
|
SECTION *root_section;
|
|
FACEHUGGER_STATUS_BLOCK *facehuggerStatus = (FACEHUGGER_STATUS_BLOCK *)sbPtr->SBdataptr;
|
|
|
|
NPC_InitMovementData(&(facehuggerStatus->moveData));
|
|
facehuggerStatus->health = FACEHUGGER_STARTING_HEALTH;
|
|
sbPtr->integrity = facehuggerStatus->health;
|
|
facehuggerStatus->stateTimer = 0;
|
|
facehuggerStatus->DoomTimer = 0;
|
|
facehuggerStatus->CurveRadius = 0;
|
|
facehuggerStatus->CurveLength = 0;
|
|
facehuggerStatus->CurveTimeOut = 0;
|
|
facehuggerStatus->jumping = 0;
|
|
|
|
root_section=GetHierarchyFromLibrary("hnpchugger");
|
|
Create_HModel(&facehuggerStatus->HModelController,root_section);
|
|
InitHModelSequence(&facehuggerStatus->HModelController,0,0,ONE_FIXED);
|
|
ProveHModel_Far(&facehuggerStatus->HModelController,sbPtr);
|
|
|
|
if (toolsData->startInactive==0) {
|
|
facehuggerStatus->nearBehaviourState = FHNS_Approach;
|
|
SetHuggerAnimationSequence(sbPtr,HSS_Stand,ONE_FIXED);
|
|
} else {
|
|
facehuggerStatus->nearBehaviourState = FHNS_Floating;
|
|
SetHuggerAnimationSequence(sbPtr,HSS_Floats,(ONE_FIXED<<1));
|
|
sbPtr->DynPtr->GravityOn=0;
|
|
}
|
|
|
|
facehuggerStatus->soundHandle = SOUND_NOACTIVEINDEX;
|
|
facehuggerStatus->soundHandle2 = SOUND_NOACTIVEINDEX;
|
|
|
|
for(i=0;i<SB_NAME_LENGTH;i++) facehuggerStatus->death_target_ID[i] = toolsData->death_target_ID[i];
|
|
facehuggerStatus->death_target_sbptr=0;
|
|
facehuggerStatus->death_target_request=toolsData->death_target_request;
|
|
}
|
|
else
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
void FacehuggerBehaviour(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
FACEHUGGER_STATUS_BLOCK *facehuggerStatusPointer;
|
|
|
|
LOCALASSERT(sbPtr);
|
|
facehuggerStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->SBdataptr);
|
|
LOCALASSERT(facehuggerStatusPointer);
|
|
|
|
/* test if we've got a containing module: if we haven't, do nothing.
|
|
This is important as the object could have been marked for deletion by the visibility
|
|
management system...*/
|
|
if(!sbPtr->containingModule)
|
|
{
|
|
if (facehuggerStatusPointer->soundHandle2 != SOUND_NOACTIVEINDEX) {
|
|
Sound_Stop(facehuggerStatusPointer->soundHandle2);
|
|
}
|
|
DestroyAnyStrategyBlock(sbPtr); /* just to make sure */
|
|
return;
|
|
}
|
|
|
|
/* set velocity to zero */
|
|
LOCALASSERT(sbPtr->DynPtr);
|
|
sbPtr->DynPtr->LinVelocity.vx = 0;
|
|
sbPtr->DynPtr->LinVelocity.vy = 0;
|
|
sbPtr->DynPtr->LinVelocity.vz = 0;
|
|
|
|
if (sbPtr->SBDamageBlock.IsOnFire) {
|
|
|
|
CauseDamageToObject(sbPtr,&firedamage,NormalFrameTime,NULL);
|
|
|
|
if (facehuggerStatusPointer->soundHandle2==SOUND_NOACTIVEINDEX) {
|
|
Sound_Play(SID_FIRE,"dlev",&(sbPtr->DynPtr->Position),&facehuggerStatusPointer->soundHandle2,127);
|
|
} else {
|
|
if (ActiveSounds[facehuggerStatusPointer->soundHandle2].soundIndex!=SID_FIRE) {
|
|
Sound_Stop(facehuggerStatusPointer->soundHandle2);
|
|
} else {
|
|
Sound_Update3d(facehuggerStatusPointer->soundHandle2,&(sbPtr->DynPtr->Position));
|
|
}
|
|
}
|
|
} else {
|
|
Sound_Stop(facehuggerStatusPointer->soundHandle2);
|
|
}
|
|
|
|
/* No far behaviour for facehuggerss */
|
|
if(sbPtr->SBdptr)
|
|
{
|
|
if(sbPtr->maintainVisibility) LOCALASSERT(ModuleCurrVisArray[(sbPtr->containingModule->m_index)]);
|
|
switch(facehuggerStatusPointer->nearBehaviourState)
|
|
{
|
|
case(FHNS_Approach):
|
|
{
|
|
#if HUGGER_STATE_PRINT
|
|
PrintDebuggingText("Hugger Approaching...\n");
|
|
#endif
|
|
Execute_FHNS_Approach(sbPtr);
|
|
break;
|
|
}
|
|
case(FHNS_Attack):
|
|
{
|
|
#if HUGGER_STATE_PRINT
|
|
PrintDebuggingText("Hugger Attacking...\n");
|
|
#endif
|
|
Execute_FHNS_Attack(sbPtr);
|
|
break;
|
|
}
|
|
case(FHNS_Wait):
|
|
{
|
|
#if HUGGER_STATE_PRINT
|
|
PrintDebuggingText("Hugger Waiting...\n");
|
|
#endif
|
|
Execute_FHNS_Wait(sbPtr);
|
|
break;
|
|
}
|
|
case(FHNS_Avoidance):
|
|
{
|
|
#if HUGGER_STATE_PRINT
|
|
PrintDebuggingText("Hugger Avoiding...\n");
|
|
#endif
|
|
Execute_FHNS_Avoidance(sbPtr);
|
|
break;
|
|
}
|
|
case(FHNS_Dying):
|
|
{
|
|
#if HUGGER_STATE_PRINT
|
|
PrintDebuggingText("Hugger Dying...\n");
|
|
#endif
|
|
Execute_FHNS_Dying(sbPtr);
|
|
break;
|
|
}
|
|
case(FHNS_Floating):
|
|
{
|
|
#if HUGGER_STATE_PRINT
|
|
PrintDebuggingText("Hugger Floating...\n");
|
|
#endif
|
|
Execute_FHNS_Float(sbPtr);
|
|
break;
|
|
}
|
|
case(FHNS_Jumping):
|
|
{
|
|
#if HUGGER_STATE_PRINT
|
|
PrintDebuggingText("Hugger Jumping...\n");
|
|
#endif
|
|
Execute_FHNS_Jumping(sbPtr);
|
|
break;
|
|
}
|
|
case(FHNS_AboutToJump):
|
|
{
|
|
#if HUGGER_STATE_PRINT
|
|
PrintDebuggingText("Hugger AboutToJump...\n");
|
|
#endif
|
|
Execute_FHNS_AboutToJump(sbPtr);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
LOCALASSERT(1==0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if((facehuggerStatusPointer->nearBehaviourState == FHNS_Dying)&&(facehuggerStatusPointer->stateTimer <= 0)) {
|
|
if (facehuggerStatusPointer->soundHandle2 != SOUND_NOACTIVEINDEX) {
|
|
Sound_Stop(facehuggerStatusPointer->soundHandle2);
|
|
}
|
|
DestroyAnyStrategyBlock(sbPtr);
|
|
} else if (facehuggerStatusPointer->DoomTimer>(ONE_FIXED*FACEHUGGER_EXPIRY_TIME)) {
|
|
/* Kill facehugger */
|
|
sbPtr->SBDamageBlock.Health=0;
|
|
if (facehuggerStatusPointer->nearBehaviourState != FHNS_Dying) {
|
|
KillFaceHugger(sbPtr,NULL);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void MakeFacehuggerNear(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
extern MODULEMAPBLOCK AlienDefaultMap;
|
|
|
|
MODULE tempModule;
|
|
DISPLAYBLOCK *dPtr;
|
|
DYNAMICSBLOCK *dynPtr;
|
|
FACEHUGGER_STATUS_BLOCK *facehuggerStatusPointer;
|
|
|
|
LOCALASSERT(sbPtr);
|
|
dynPtr = sbPtr->DynPtr;
|
|
facehuggerStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->SBdataptr);
|
|
LOCALASSERT(facehuggerStatusPointer);
|
|
LOCALASSERT(dynPtr);
|
|
LOCALASSERT(sbPtr->SBdptr == NULL);
|
|
|
|
AlienDefaultMap.MapShape = sbPtr->shapeIndex;
|
|
tempModule.m_mapptr = &AlienDefaultMap;
|
|
tempModule.m_sbptr = (STRATEGYBLOCK*)NULL;
|
|
tempModule.m_numlights = 0;
|
|
tempModule.m_lightarray = (struct lightblock *)0;
|
|
tempModule.m_extraitemdata = (struct extraitemdata *)0;
|
|
tempModule.m_dptr = NULL;
|
|
AllocateModuleObject(&tempModule);
|
|
dPtr = tempModule.m_dptr;
|
|
if(dPtr==NULL) return; /* cannot create displayblock, so leave far */
|
|
|
|
sbPtr->SBdptr = dPtr;
|
|
dPtr->ObStrategyBlock = sbPtr;
|
|
dPtr->ObMyModule = NULL;
|
|
|
|
/* need to initialise positional information in the new display block */
|
|
dPtr->ObWorld = dynPtr->Position;
|
|
dPtr->ObEuler = dynPtr->OrientEuler;
|
|
dPtr->ObMat = dynPtr->OrientMat;
|
|
|
|
/* zero linear velocity in dynamics block */
|
|
sbPtr->DynPtr->LinVelocity.vx = 0;
|
|
sbPtr->DynPtr->LinVelocity.vy = 0;
|
|
sbPtr->DynPtr->LinVelocity.vz = 0;
|
|
|
|
/* facehugger data block init */
|
|
facehuggerStatusPointer->stateTimer = 0;
|
|
facehuggerStatusPointer->CurveRadius = 0;
|
|
facehuggerStatusPointer->CurveLength = 0;
|
|
facehuggerStatusPointer->CurveTimeOut = 0;
|
|
facehuggerStatusPointer->jumping = 0;
|
|
|
|
/* state and sequence init */
|
|
//dPtr->ShapeAnimControlBlock = &facehuggerStatusPointer->ShpAnimCtrl;
|
|
dPtr->HModelControlBlock=&facehuggerStatusPointer->HModelController;
|
|
if ((facehuggerStatusPointer->nearBehaviourState!=FHNS_Floating)
|
|
&&(facehuggerStatusPointer->nearBehaviourState!=FHNS_Dying)) {
|
|
if(HuggerShouldAttackPlayer())
|
|
{
|
|
NPC_InitMovementData(&(facehuggerStatusPointer->moveData));
|
|
facehuggerStatusPointer->nearBehaviourState = FHNS_Approach;
|
|
SetHuggerAnimationSequence(sbPtr,HSS_Run,(ONE_FIXED*2)/3);
|
|
}
|
|
else
|
|
{
|
|
NPC_InitMovementData(&(facehuggerStatusPointer->moveData));
|
|
facehuggerStatusPointer->nearBehaviourState = FHNS_Wait;
|
|
SetHuggerAnimationSequence(sbPtr,HSS_Stand,ONE_FIXED);
|
|
}
|
|
}
|
|
ProveHModel(dPtr->HModelControlBlock,dPtr);
|
|
|
|
}
|
|
|
|
void MakeFacehuggerFar(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
int i;
|
|
LOCALASSERT(sbPtr->SBdptr != NULL);
|
|
/* get rid of the displayblock */
|
|
i = DestroyActiveObject(sbPtr->SBdptr);
|
|
LOCALASSERT(i==0);
|
|
sbPtr->SBdptr = NULL;
|
|
/* zero linear velocity in dynamics block */
|
|
sbPtr->DynPtr->LinVelocity.vx = 0;
|
|
sbPtr->DynPtr->LinVelocity.vy = 0;
|
|
sbPtr->DynPtr->LinVelocity.vz = 0;
|
|
}
|
|
|
|
void FacehuggerIsDamaged(STRATEGYBLOCK *sbPtr, DAMAGE_PROFILE *damage, int multiple)
|
|
{
|
|
FACEHUGGER_STATUS_BLOCK *facehuggerStatusPointer;
|
|
|
|
LOCALASSERT(sbPtr);
|
|
//Sound_Play(SID_ACID_SPRAY,"dp",&(sbPtr->DynPtr->Position),(FastRandom() & 255) - 128);
|
|
|
|
facehuggerStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->SBdataptr);
|
|
LOCALASSERT(facehuggerStatusPointer);
|
|
|
|
#if 0
|
|
/* reduce facehugger health */
|
|
if(facehuggerStatusPointer->health>0) facehuggerStatusPointer->health -= damage;
|
|
#endif
|
|
|
|
if (facehuggerStatusPointer->nearBehaviourState==FHNS_Floating) {
|
|
Wake_Hugger(sbPtr);
|
|
}
|
|
|
|
/* check if we've been killed */
|
|
if ( (sbPtr->SBDamageBlock.Health <= 0)&&(facehuggerStatusPointer->nearBehaviourState!=FHNS_Dying) )
|
|
{
|
|
CurrentGameStats_CreatureKilled(sbPtr,NULL);
|
|
KillFaceHugger(sbPtr,damage);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
static void KillFaceHugger(STRATEGYBLOCK *sbPtr,DAMAGE_PROFILE *damage)
|
|
{
|
|
FACEHUGGER_STATUS_BLOCK *fhugStatusPointer;
|
|
DYNAMICSBLOCK *dynPtr;
|
|
|
|
LOCALASSERT(sbPtr);
|
|
fhugStatusPointer=(FACEHUGGER_STATUS_BLOCK *)(sbPtr->SBdataptr);
|
|
dynPtr=sbPtr->DynPtr;
|
|
LOCALASSERT(fhugStatusPointer);
|
|
LOCALASSERT(dynPtr);
|
|
|
|
fhugStatusPointer->nearBehaviourState=FHNS_Dying;
|
|
fhugStatusPointer->stateTimer=FACEHUGGER_DYINGTIME<<1;
|
|
|
|
/* Stop any hugger sound if playing */
|
|
if (fhugStatusPointer->soundHandle != SOUND_NOACTIVEINDEX)
|
|
{
|
|
Sound_Stop(fhugStatusPointer->soundHandle);
|
|
}
|
|
|
|
//Sound_Play(SID_BUGDIE3,"d",&(dynPtr->Position));
|
|
|
|
#if 1
|
|
{
|
|
PLAYER_STATUS *playerStatusPointer= (PLAYER_STATUS *) (Player->ObStrategyBlock->SBdataptr);
|
|
|
|
if (playerStatusPointer->MyFaceHugger==sbPtr) {
|
|
/* You cheater! */
|
|
playerStatusPointer->MyFaceHugger=NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*If face hugger has a death target ,send a request*/
|
|
if(fhugStatusPointer->death_target_sbptr)
|
|
{
|
|
RequestState(fhugStatusPointer->death_target_sbptr,fhugStatusPointer->death_target_request, 0);
|
|
}
|
|
|
|
/* More restrained death. */
|
|
{
|
|
/* switch sequence */
|
|
if (damage) {
|
|
if ( (damage->Impact==0)
|
|
&&(damage->Cutting==0)
|
|
&&(damage->Penetrative==0)
|
|
&&(damage->Fire!=0)
|
|
&&(damage->Electrical==0)
|
|
&&(damage->Acid==0))
|
|
{
|
|
SetHuggerAnimationSequence(sbPtr,HSS_DieOnFire,FACEHUGGER_DYINGTIME>>3);
|
|
} else {
|
|
SetHuggerAnimationSequence(sbPtr,HSS_Dies,FACEHUGGER_DYINGTIME>>3);
|
|
}
|
|
} else {
|
|
SetHuggerAnimationSequence(sbPtr,HSS_Dies,FACEHUGGER_DYINGTIME>>3);
|
|
}
|
|
fhugStatusPointer->HModelController.Looped=0;
|
|
fhugStatusPointer->HModelController.LoopAfterTweening=0;
|
|
/* switch state */
|
|
fhugStatusPointer->nearBehaviourState=FHNS_Dying;
|
|
fhugStatusPointer->stateTimer=FACEHUGGER_DYINGTIME;
|
|
|
|
/* stop motion */
|
|
LOCALASSERT(sbPtr->DynPtr);
|
|
dynPtr->Friction = 400000;
|
|
dynPtr->LinImpulse.vx=sbPtr->DynPtr->LinVelocity.vx;
|
|
dynPtr->LinImpulse.vy=sbPtr->DynPtr->LinVelocity.vy;
|
|
dynPtr->LinImpulse.vz=sbPtr->DynPtr->LinVelocity.vz;
|
|
dynPtr->LinVelocity.vx = sbPtr->DynPtr->LinVelocity.vy = sbPtr->DynPtr->LinVelocity.vz = 0;
|
|
/* Okay... */
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/* -------------------------------------------------------------------
|
|
Behaviour state functions
|
|
--------------------------------------------------------------------*/
|
|
|
|
static void Execute_FHNS_Approach(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
FACEHUGGER_STATUS_BLOCK *fhugStatusPointer;
|
|
DYNAMICSBLOCK *dynPtr;
|
|
VECTORCH targetPos;
|
|
//int approachingAirDuct = 0;
|
|
|
|
LOCALASSERT(sbPtr);
|
|
fhugStatusPointer=(FACEHUGGER_STATUS_BLOCK *)(sbPtr->SBdataptr);
|
|
dynPtr=sbPtr->DynPtr;
|
|
LOCALASSERT(fhugStatusPointer);
|
|
LOCALASSERT(dynPtr);
|
|
|
|
/* Stop any hugger attack sound if playing */
|
|
if (fhugStatusPointer->soundHandle != SOUND_NOACTIVEINDEX &&
|
|
ActiveSounds[fhugStatusPointer->soundHandle].soundIndex != SID_FHUG_MOVE)
|
|
{
|
|
Sound_Stop(fhugStatusPointer->soundHandle);
|
|
}
|
|
|
|
/* Start the hugger movement sound if needed */
|
|
if (fhugStatusPointer->soundHandle == SOUND_NOACTIVEINDEX)
|
|
{
|
|
|
|
Sound_Play(SID_FHUG_MOVE,"ed",&fhugStatusPointer->soundHandle,&dynPtr->Position);
|
|
}
|
|
|
|
/* do climb on walls, etc */
|
|
dynPtr->UseStandardGravity=1;
|
|
/* If you think I'm going to let facehuggers climb on walls for *
|
|
* ONE SECOND, you are INSANE, Jack!!! */
|
|
|
|
/* target acquisition ? */
|
|
{
|
|
extern DISPLAYBLOCK *Player;
|
|
if (sbPtr->SBDamageBlock.IsOnFire==0) {
|
|
targetPos=Player->ObStrategyBlock->DynPtr->Position;
|
|
} else {
|
|
targetPos=Player->ObStrategyBlock->DynPtr->Position;
|
|
|
|
targetPos.vx+=((FastRandom()&8191)-4096);
|
|
targetPos.vy+=((FastRandom()&8191)-4096);
|
|
targetPos.vz+=((FastRandom()&8191)-4096);
|
|
}
|
|
}
|
|
|
|
/* translate target into hugger local space */
|
|
{
|
|
MATRIXCH toLocalSpaceMatrix = dynPtr->OrientMat;
|
|
TransposeMatrixCH(&toLocalSpaceMatrix);
|
|
|
|
targetPos.vx -= dynPtr->Position.vx;
|
|
targetPos.vy -= dynPtr->Position.vy;
|
|
targetPos.vz -= dynPtr->Position.vz;
|
|
RotateVector(&targetPos,&toLocalSpaceMatrix);
|
|
}
|
|
|
|
/* Fix vy. */
|
|
targetPos.vy=0;
|
|
|
|
/* tracking movement */
|
|
{
|
|
int distanceToTarget = Magnitude(&targetPos);
|
|
if (dynPtr->IsInContactWithFloor)
|
|
{
|
|
int offset;
|
|
|
|
if (fhugStatusPointer->CurveTimeOut<=0)
|
|
{
|
|
fhugStatusPointer->CurveLength = distanceToTarget;
|
|
fhugStatusPointer->CurveRadius = ((FastRandom()&16383)-8192)*2;
|
|
fhugStatusPointer->CurveTimeOut= ONE_FIXED*3;
|
|
} else {
|
|
fhugStatusPointer->CurveTimeOut-=NormalFrameTime;
|
|
}
|
|
|
|
offset =
|
|
MUL_FIXED
|
|
(
|
|
fhugStatusPointer->CurveRadius,
|
|
GetCos((1024*(distanceToTarget)/fhugStatusPointer->CurveLength)&4095)
|
|
);
|
|
|
|
dynPtr->LinVelocity.vx =
|
|
WideMulNarrowDiv
|
|
(
|
|
FACEHUGGER_NEAR_SPEED,
|
|
targetPos.vx,
|
|
distanceToTarget
|
|
)
|
|
-WideMulNarrowDiv
|
|
(
|
|
offset,
|
|
targetPos.vz,
|
|
distanceToTarget
|
|
);
|
|
|
|
|
|
dynPtr->LinVelocity.vz =
|
|
WideMulNarrowDiv
|
|
(
|
|
FACEHUGGER_NEAR_SPEED,
|
|
targetPos.vz,
|
|
distanceToTarget
|
|
)+
|
|
WideMulNarrowDiv
|
|
(
|
|
offset,
|
|
targetPos.vx,
|
|
distanceToTarget
|
|
);
|
|
|
|
RotateVector(&dynPtr->LinVelocity,&dynPtr->OrientMat);
|
|
/* align to velocity */
|
|
|
|
NPCOrientateToVector(sbPtr,&dynPtr->LinVelocity,NPC_TURNRATE,NULL);
|
|
|
|
/* within test for in contact with floor: test if
|
|
jump flag is set- if so switch anim back to run... */
|
|
if(fhugStatusPointer->jumping==1)
|
|
{
|
|
fhugStatusPointer->jumping = 0;
|
|
SetHuggerAnimationSequence(sbPtr,HSS_Run,(ONE_FIXED*2)/3);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check for state changes:
|
|
firstly, are we in contact with the player? */
|
|
#if 0
|
|
if(InContactWithPlayer(dynPtr)&&HuggerShouldAttackPlayer())
|
|
{
|
|
fhugStatusPointer->nearBehaviourState = FHNS_Attack;
|
|
fhugStatusPointer->stateTimer = FACEHUGGER_NEARATTACKTIME;
|
|
SetHuggerAnimationSequence(sbPtr,HSS_Attack,ONE_FIXED);
|
|
dynPtr->DynamicsType = DYN_TYPE_NO_COLLISIONS; /* turn off collisons */
|
|
dynPtr->GravityOn = 0; /* turn off gravity */
|
|
sbPtr->maintainVisibility = 0; /* turn off visibility support- be carefull! */
|
|
|
|
/* Attach to player! */
|
|
{
|
|
PLAYER_STATUS *playerStatusPointer= (PLAYER_STATUS *) (Player->ObStrategyBlock->SBdataptr);
|
|
|
|
playerStatusPointer->MyFaceHugger=sbPtr;
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/* is player visible?: if not, go to wait */
|
|
if(!HuggerShouldAttackPlayer())
|
|
{
|
|
fhugStatusPointer->nearBehaviourState = FHNS_Wait;
|
|
fhugStatusPointer->stateTimer = 0;
|
|
return;
|
|
}
|
|
|
|
#if 1
|
|
/* should we jump at the player? */
|
|
if (sbPtr->SBDamageBlock.IsOnFire==0) {
|
|
int distanceToPlayer = VectorDistance(&(dynPtr->Position),&(Player->ObStrategyBlock->DynPtr->Position));
|
|
if((distanceToPlayer<=FACEHUGGER_JUMPDISTANCE)&&(dynPtr->IsInContactWithFloor))
|
|
{
|
|
#if 0
|
|
JumpAtPlayer(sbPtr);
|
|
#else
|
|
fhugStatusPointer->nearBehaviourState = FHNS_AboutToJump;
|
|
fhugStatusPointer->stateTimer = 0;
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
{
|
|
STRATEGYBLOCK *destructableObject = NULL;
|
|
NPC_OBSTRUCTIONREPORT obstruction;
|
|
NPC_IsObstructed(sbPtr,&(fhugStatusPointer->moveData),&obstruction,&destructableObject);
|
|
if(obstruction.environment)
|
|
{
|
|
/* go to avoidance */
|
|
NPC_InitMovementData(&(fhugStatusPointer->moveData));
|
|
NPCGetAvoidanceDirection(sbPtr, &(fhugStatusPointer->moveData.avoidanceDirn),&obstruction);
|
|
fhugStatusPointer->nearBehaviourState = FHNS_Avoidance;
|
|
fhugStatusPointer->stateTimer = NPC_AVOIDTIME;
|
|
/* no sequence change required */
|
|
return;
|
|
}
|
|
if(obstruction.destructableObject)
|
|
{
|
|
LOCALASSERT(destructableObject);
|
|
|
|
CauseDamageToObject(destructableObject,&TemplateAmmo[AMMO_ALIEN_OBSTACLE_CLEAR].MaxDamage[AvP.Difficulty], ONE_FIXED,NULL);
|
|
|
|
}
|
|
}
|
|
|
|
{
|
|
VECTORCH velocityDirection = dynPtr->LinVelocity;
|
|
Normalise(&velocityDirection);
|
|
|
|
if(NPC_CannotReachTarget(&(fhugStatusPointer->moveData), &targetPos, &velocityDirection))
|
|
{
|
|
/* go to avoidance */
|
|
NPC_OBSTRUCTIONREPORT obstruction = {1,0,0};
|
|
NPC_InitMovementData(&(fhugStatusPointer->moveData));
|
|
NPCGetAvoidanceDirection(sbPtr, &(fhugStatusPointer->moveData.avoidanceDirn),&obstruction);
|
|
fhugStatusPointer->nearBehaviourState = FHNS_Avoidance;
|
|
fhugStatusPointer->stateTimer = NPC_AVOIDTIME;
|
|
/* no sequence change required */
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PlotFaceHugger(STRATEGYBLOCK *sbPtr) {
|
|
|
|
extern void RenderThisDisplayblock(DISPLAYBLOCK *dbPtr);
|
|
extern VIEWDESCRIPTORBLOCK *ActiveVDBList[];
|
|
|
|
VIEWDESCRIPTORBLOCK *VDBPtr = ActiveVDBList[0];
|
|
DYNAMICSBLOCK *dynPtr;
|
|
FACEHUGGER_STATUS_BLOCK *facehuggerStatusPointer;
|
|
|
|
LOCALASSERT(sbPtr);
|
|
dynPtr=sbPtr->DynPtr;
|
|
facehuggerStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->SBdataptr);
|
|
LOCALASSERT(facehuggerStatusPointer);
|
|
LOCALASSERT(dynPtr);
|
|
LOCALASSERT(sbPtr->SBdptr);
|
|
|
|
|
|
{
|
|
VECTORCH x,y,z;
|
|
|
|
x.vx = -VDBPtr->VDB_Mat.mat11;
|
|
x.vy = -VDBPtr->VDB_Mat.mat21;
|
|
x.vz = -VDBPtr->VDB_Mat.mat31;
|
|
y.vx = -VDBPtr->VDB_Mat.mat13;
|
|
y.vy = -VDBPtr->VDB_Mat.mat23;
|
|
y.vz = -VDBPtr->VDB_Mat.mat33;
|
|
z.vx = -VDBPtr->VDB_Mat.mat12;
|
|
z.vy = -VDBPtr->VDB_Mat.mat22;
|
|
z.vz = -VDBPtr->VDB_Mat.mat32;
|
|
|
|
Normalise(&x);
|
|
Normalise(&y);
|
|
Normalise(&z);
|
|
|
|
dynPtr->OrientMat.mat11 = x.vx;
|
|
dynPtr->OrientMat.mat12 = x.vy;
|
|
dynPtr->OrientMat.mat13 = x.vz;
|
|
dynPtr->OrientMat.mat21 = y.vx;
|
|
dynPtr->OrientMat.mat22 = y.vy;
|
|
dynPtr->OrientMat.mat23 = y.vz;
|
|
dynPtr->OrientMat.mat31 = z.vx;
|
|
dynPtr->OrientMat.mat32 = z.vy;
|
|
dynPtr->OrientMat.mat33 = z.vz;
|
|
}
|
|
|
|
/* set position */
|
|
dynPtr->Position.vx = 0;
|
|
dynPtr->Position.vz = FACEHUGGER_ATTACKZOFFSET/4;
|
|
dynPtr->Position.vy = FACEHUGGER_ATTACKYOFFSET;
|
|
{
|
|
MATRIXCH myMat = VDBPtr->VDB_Mat;
|
|
TransposeMatrixCH(&myMat);
|
|
RotateVector(&(dynPtr->Position), &(myMat));
|
|
}
|
|
|
|
dynPtr->Position.vx += VDBPtr->VDB_World.vx;
|
|
dynPtr->Position.vy += VDBPtr->VDB_World.vy;
|
|
dynPtr->Position.vz += VDBPtr->VDB_World.vz;
|
|
sbPtr->SBdptr->ObFlags&=~ObFlag_NotVis;
|
|
|
|
sbPtr->SBdptr->ObWorld = dynPtr->Position;
|
|
sbPtr->SBdptr->ObMat = dynPtr->OrientMat;
|
|
|
|
ProveHModel(sbPtr->SBdptr->HModelControlBlock,sbPtr->SBdptr);
|
|
RenderThisDisplayblock(sbPtr->SBdptr);
|
|
|
|
sbPtr->SBdptr->ObFlags|=ObFlag_NotVis;
|
|
|
|
}
|
|
|
|
static void Execute_FHNS_Attack(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
extern VIEWDESCRIPTORBLOCK *ActiveVDBList[];
|
|
|
|
DYNAMICSBLOCK *dynPtr;
|
|
FACEHUGGER_STATUS_BLOCK *facehuggerStatusPointer;
|
|
|
|
LOCALASSERT(sbPtr);
|
|
dynPtr=sbPtr->DynPtr;
|
|
facehuggerStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->SBdataptr);
|
|
LOCALASSERT(facehuggerStatusPointer);
|
|
LOCALASSERT(dynPtr);
|
|
LOCALASSERT(sbPtr->SBdptr);
|
|
|
|
/* Play the hugger attack loop sound */
|
|
if (facehuggerStatusPointer->soundHandle == SOUND_NOACTIVEINDEX)
|
|
{
|
|
Sound_Play(SID_FHUG_ATTACKLOOP,"edl",&facehuggerStatusPointer->soundHandle,&dynPtr->Position);
|
|
}
|
|
|
|
textprint("face hugger attack \n");
|
|
|
|
/* Make not vis */
|
|
|
|
sbPtr->SBdptr->ObFlags|=ObFlag_NotVis;
|
|
|
|
/* do damage */
|
|
facehuggerStatusPointer->DoomTimer += NormalFrameTime;
|
|
facehuggerStatusPointer->stateTimer -= NormalFrameTime;
|
|
if(facehuggerStatusPointer->stateTimer <= 0)
|
|
{
|
|
facehuggerStatusPointer->stateTimer = FACEHUGGER_NEARATTACKTIME;
|
|
CauseDamageToObject(Player->ObStrategyBlock, &TemplateAmmo[AMMO_FACEHUGGER].MaxDamage[AvP.Difficulty], ONE_FIXED,NULL);
|
|
/* FRI? */
|
|
}
|
|
|
|
}
|
|
|
|
static void Execute_FHNS_Wait(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
FACEHUGGER_STATUS_BLOCK *facehuggerStatusPointer;
|
|
|
|
LOCALASSERT(sbPtr);
|
|
facehuggerStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->SBdataptr);
|
|
LOCALASSERT(facehuggerStatusPointer);
|
|
|
|
if(HuggerShouldAttackPlayer())
|
|
{
|
|
NPC_InitMovementData(&(facehuggerStatusPointer->moveData));
|
|
facehuggerStatusPointer->nearBehaviourState = FHNS_Approach;
|
|
facehuggerStatusPointer->stateTimer = 0;
|
|
facehuggerStatusPointer->CurveTimeOut = 0;
|
|
SetHuggerAnimationSequence(sbPtr,HSS_Run,(ONE_FIXED*2)/3);
|
|
}
|
|
}
|
|
|
|
static void Execute_FHNS_Avoidance(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
int terminateState = 0;
|
|
FACEHUGGER_STATUS_BLOCK *fhugStatusPointer;
|
|
DYNAMICSBLOCK *dynPtr;
|
|
|
|
LOCALASSERT(sbPtr);
|
|
fhugStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->SBdataptr);
|
|
dynPtr=sbPtr->DynPtr;
|
|
LOCALASSERT(fhugStatusPointer);
|
|
LOCALASSERT(dynPtr);
|
|
|
|
/* Stop any hugger attack sound if playing */
|
|
if (fhugStatusPointer->soundHandle != SOUND_NOACTIVEINDEX &&
|
|
ActiveSounds[fhugStatusPointer->soundHandle].soundIndex != SID_FHUG_MOVE)
|
|
{
|
|
Sound_Stop(fhugStatusPointer->soundHandle);
|
|
}
|
|
|
|
/* Start the hugger movement sound if needed */
|
|
if (fhugStatusPointer->soundHandle == SOUND_NOACTIVEINDEX)
|
|
{
|
|
Sound_Play(SID_FHUG_MOVE,"ed",&fhugStatusPointer->soundHandle,&dynPtr->Position);
|
|
}
|
|
|
|
/* set velocity */
|
|
LOCALASSERT((fhugStatusPointer->moveData.avoidanceDirn.vx!=0)||
|
|
(fhugStatusPointer->moveData.avoidanceDirn.vy!=0)||
|
|
(fhugStatusPointer->moveData.avoidanceDirn.vz!=0));
|
|
NPCSetVelocity(sbPtr, &(fhugStatusPointer->moveData.avoidanceDirn), (FACEHUGGER_NEAR_SPEED));
|
|
|
|
/* next, decrement state timer */
|
|
fhugStatusPointer->stateTimer -= NormalFrameTime;
|
|
if(fhugStatusPointer->stateTimer <= 0) terminateState = 1;
|
|
|
|
/* and check for an impeding collision */
|
|
{
|
|
STRATEGYBLOCK *destructableObject = NULL;
|
|
NPC_OBSTRUCTIONREPORT obstruction;
|
|
NPC_IsObstructed(sbPtr,&(fhugStatusPointer->moveData),&obstruction,&destructableObject);
|
|
if(obstruction.anySingleObstruction)
|
|
{
|
|
terminateState = 1;
|
|
}
|
|
}
|
|
|
|
if(terminateState)
|
|
{
|
|
if(HuggerShouldAttackPlayer())
|
|
{
|
|
/* switch to approach */
|
|
NPC_InitMovementData(&(fhugStatusPointer->moveData));
|
|
fhugStatusPointer->nearBehaviourState = FHNS_Approach;
|
|
fhugStatusPointer->stateTimer = 0;
|
|
/* no sequence change required */
|
|
}
|
|
else
|
|
{
|
|
/* switch to wait */
|
|
NPC_InitMovementData(&(fhugStatusPointer->moveData));
|
|
fhugStatusPointer->nearBehaviourState = FHNS_Wait;
|
|
fhugStatusPointer->stateTimer = 0;
|
|
/* no sequence change required */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------
|
|
FaceHugger behaviour support functions
|
|
--------------------------------------------------------------------*/
|
|
static void SetHuggerAnimationSequence(STRATEGYBLOCK *sbPtr, HUGGER_SUBSEQUENCES seq, int length)
|
|
{
|
|
|
|
FACEHUGGER_STATUS_BLOCK *fhugStatus=(FACEHUGGER_STATUS_BLOCK *)(sbPtr->SBdataptr);
|
|
|
|
InitHModelTweening(&fhugStatus->HModelController,(ONE_FIXED>>3),(int)HMSQT_Hugger,(int)seq,length,1);
|
|
|
|
if (seq==HSS_Jump) fhugStatus->HModelController.LoopAfterTweening=0;
|
|
|
|
}
|
|
|
|
static int HuggerShouldAttackPlayer(void)
|
|
{
|
|
/* test for player being cloaked */
|
|
{
|
|
PLAYER_STATUS *playerStatusPointer= (PLAYER_STATUS *) (Player->ObStrategyBlock->SBdataptr);
|
|
LOCALASSERT(playerStatusPointer);
|
|
|
|
if((playerStatusPointer->cloakOn==1)&&(playerStatusPointer->cloakPositionGivenAway==0)) {
|
|
|
|
return 1;
|
|
/* Was '0'. */
|
|
}
|
|
|
|
if (playerStatusPointer->MyFaceHugger!=NULL) return(0);
|
|
}
|
|
|
|
/* test for player being an alien */
|
|
if(AvP.PlayerType==I_Alien) return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int InContactWithPlayer(DYNAMICSBLOCK *dynPtr)
|
|
{
|
|
struct collisionreport *nextReport;
|
|
|
|
LOCALASSERT(dynPtr);
|
|
nextReport = dynPtr->CollisionReportPtr;
|
|
|
|
/* walk the collision report list, looking for collisions against the player */
|
|
while(nextReport)
|
|
{
|
|
if(nextReport->ObstacleSBPtr == Player->ObStrategyBlock) return 1;
|
|
nextReport = nextReport->NextCollisionReportPtr;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void JumpAtPlayer(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
|
|
FACEHUGGER_STATUS_BLOCK *fhugStatusPointer=(FACEHUGGER_STATUS_BLOCK *)(sbPtr->SBdataptr);
|
|
/* Set up jump! */
|
|
|
|
if (sbPtr->DynPtr->IsInContactWithFloor==0) {
|
|
/* Jump Not! */
|
|
return;
|
|
}
|
|
|
|
/* set animation */
|
|
SetHuggerAnimationSequence(sbPtr, HSS_Jump, (ONE_FIXED));
|
|
fhugStatusPointer->jumping = 1;
|
|
fhugStatusPointer->CurveTimeOut = 0;
|
|
|
|
fhugStatusPointer->HModelController.Looped=0;
|
|
fhugStatusPointer->HModelController.LoopAfterTweening=0;
|
|
|
|
fhugStatusPointer->nearBehaviourState = FHNS_Jumping;
|
|
fhugStatusPointer->stateTimer=0;
|
|
|
|
}
|
|
|
|
static void FHugApplyPounceImpulse(STRATEGYBLOCK *sbPtr) {
|
|
|
|
FACEHUGGER_STATUS_BLOCK *fhugStatusPointer;
|
|
DYNAMICSBLOCK *dynPtr;
|
|
VECTORCH pounceVector,targetPoint;
|
|
int dist,speed;
|
|
|
|
LOCALASSERT(sbPtr);
|
|
fhugStatusPointer=(FACEHUGGER_STATUS_BLOCK *)(sbPtr->SBdataptr);
|
|
dynPtr = sbPtr->DynPtr;
|
|
LOCALASSERT(fhugStatusPointer);
|
|
LOCALASSERT(dynPtr);
|
|
|
|
GetTargetingPointOfObject(Player,&targetPoint);
|
|
|
|
dist = VectorDistance(&(dynPtr->Position),&targetPoint);
|
|
|
|
/* Apply a correction based on range. */
|
|
targetPoint.vy-=(dist>>3);
|
|
|
|
pounceVector.vx=targetPoint.vx-dynPtr->Position.vx;
|
|
pounceVector.vy=targetPoint.vy-dynPtr->Position.vy;
|
|
pounceVector.vz=targetPoint.vz-dynPtr->Position.vz;
|
|
|
|
Normalise(&pounceVector);
|
|
/* Must jump at least a little bit upwards. */
|
|
if (pounceVector.vy>-10000) {
|
|
pounceVector.vy=-10000;
|
|
}
|
|
|
|
speed=FACEHUGGER_JUMP_SPEED;
|
|
|
|
pounceVector.vx=MUL_FIXED(speed,pounceVector.vx);
|
|
pounceVector.vy=MUL_FIXED(speed,pounceVector.vy);
|
|
pounceVector.vz=MUL_FIXED(speed,pounceVector.vz);
|
|
|
|
sbPtr->DynPtr->LinImpulse=pounceVector;
|
|
|
|
}
|
|
|
|
static void Execute_FHNS_Jumping(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
FACEHUGGER_STATUS_BLOCK *fhugStatusPointer;
|
|
DYNAMICSBLOCK *dynPtr;
|
|
|
|
LOCALASSERT(sbPtr);
|
|
fhugStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->SBdataptr);
|
|
dynPtr=sbPtr->DynPtr;
|
|
LOCALASSERT(fhugStatusPointer);
|
|
LOCALASSERT(dynPtr);
|
|
|
|
/* don't climb on walls, etc */
|
|
dynPtr->UseStandardGravity=1;
|
|
|
|
/* Firstly, are we actually pouncing yet? */
|
|
/* NearStateTimer is a status flag. */
|
|
|
|
if (fhugStatusPointer->stateTimer!=2) {
|
|
/* Still tweening? */
|
|
|
|
if (fhugStatusPointer->HModelController.keyframe_flags) {
|
|
/* We have the flag. */
|
|
fhugStatusPointer->HModelController.Playing=0;
|
|
fhugStatusPointer->stateTimer=1;
|
|
}
|
|
|
|
if (fhugStatusPointer->stateTimer==1) {
|
|
/* We've finished! Are we facing right? */
|
|
VECTORCH orientationDirn;
|
|
int i;
|
|
|
|
orientationDirn.vx = Player->ObWorld.vx - dynPtr->Position.vx;
|
|
orientationDirn.vy = 0;
|
|
orientationDirn.vz = Player->ObWorld.vz - dynPtr->Position.vz;
|
|
i = NPCOrientateToVector(sbPtr, &orientationDirn,NPC_TURNRATE<<2,NULL);
|
|
|
|
if (i==0) {
|
|
/* Still not right! Wait for proper facing. */
|
|
fhugStatusPointer->HModelController.Playing=0;
|
|
//CheckPounceIntegrity(sbPtr);
|
|
return;
|
|
} else {
|
|
/* Okay, pounce! */
|
|
|
|
FHugApplyPounceImpulse(sbPtr);
|
|
|
|
fhugStatusPointer->HModelController.Playing=1;
|
|
fhugStatusPointer->stateTimer=2;
|
|
}
|
|
} else {
|
|
/* Yup, still tweening. Check state validity? */
|
|
//CheckPounceIntegrity(sbPtr);
|
|
}
|
|
} else {
|
|
/* We must be in the jump. Can't break out of this until we hit something. */
|
|
COLLISIONREPORT *reportPtr = dynPtr->CollisionReportPtr;
|
|
|
|
while (reportPtr)
|
|
{
|
|
STRATEGYBLOCK *hitSbPtr = reportPtr->ObstacleSBPtr;
|
|
/* You know what? Just did! */
|
|
|
|
fhugStatusPointer->nearBehaviourState=FHNS_Approach;
|
|
fhugStatusPointer->stateTimer = 0;
|
|
fhugStatusPointer->CurveTimeOut = 0;
|
|
|
|
if (hitSbPtr) {
|
|
if ((hitSbPtr->SBdptr==Player)&&(sbPtr->SBDamageBlock.IsOnFire==0)) {
|
|
/* Got him, My Precious, we've Got Him! */
|
|
|
|
Sound_Play(SID_ED_FACEHUGGERSLAP,"h");
|
|
|
|
/* Test for attach, or merely bite? */
|
|
fhugStatusPointer->nearBehaviourState = FHNS_Attack;
|
|
fhugStatusPointer->stateTimer = FACEHUGGER_NEARATTACKTIME;
|
|
SetHuggerAnimationSequence(sbPtr,HSS_Attack,ONE_FIXED);
|
|
dynPtr->DynamicsType = DYN_TYPE_NO_COLLISIONS; /* turn off collisons */
|
|
dynPtr->GravityOn = 0; /* turn off gravity */
|
|
sbPtr->maintainVisibility = 0; /* turn off visibility support- be carefull! */
|
|
|
|
/* Attach to player! */
|
|
{
|
|
PLAYER_STATUS *playerStatusPointer= (PLAYER_STATUS *) (Player->ObStrategyBlock->SBdataptr);
|
|
|
|
playerStatusPointer->MyFaceHugger=sbPtr;
|
|
}
|
|
return;
|
|
|
|
}
|
|
}
|
|
|
|
reportPtr = reportPtr->NextCollisionReportPtr;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void Execute_FHNS_Dying(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
FACEHUGGER_STATUS_BLOCK *huggerStatusPointer;
|
|
DISPLAYBLOCK *dispPtr;
|
|
|
|
LOCALASSERT(sbPtr);
|
|
huggerStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->SBdataptr);
|
|
LOCALASSERT(huggerStatusPointer);
|
|
|
|
huggerStatusPointer->stateTimer -= NormalFrameTime;
|
|
|
|
dispPtr=sbPtr->SBdptr;
|
|
|
|
if (dispPtr)
|
|
{
|
|
dispPtr->SpecialFXFlags |= SFXFLAG_MELTINGINTOGROUND;
|
|
dispPtr->ObFlags2 = huggerStatusPointer->stateTimer/2;
|
|
}
|
|
}
|
|
|
|
static void Execute_FHNS_Float(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
FACEHUGGER_STATUS_BLOCK *facehuggerStatusPointer;
|
|
DYNAMICSBLOCK *dynPtr;
|
|
|
|
LOCALASSERT(sbPtr);
|
|
facehuggerStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->SBdataptr);
|
|
dynPtr=sbPtr->DynPtr;
|
|
LOCALASSERT(facehuggerStatusPointer);
|
|
LOCALASSERT(dynPtr);
|
|
|
|
dynPtr->LinVelocity.vx=0;
|
|
dynPtr->LinVelocity.vy=0;
|
|
dynPtr->LinVelocity.vz=0;
|
|
|
|
dynPtr->LinImpulse.vx=0;
|
|
dynPtr->LinImpulse.vy=0;
|
|
dynPtr->LinImpulse.vz=0;
|
|
|
|
dynPtr->GravityOn=0;
|
|
|
|
/* Just float there... */
|
|
}
|
|
|
|
void Wake_Hugger(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
FACEHUGGER_STATUS_BLOCK *facehuggerStatusPointer;
|
|
DYNAMICSBLOCK *dynPtr;
|
|
|
|
LOCALASSERT(sbPtr);
|
|
facehuggerStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->SBdataptr);
|
|
dynPtr=sbPtr->DynPtr;
|
|
LOCALASSERT(facehuggerStatusPointer);
|
|
LOCALASSERT(dynPtr);
|
|
|
|
if (facehuggerStatusPointer->nearBehaviourState==FHNS_Floating) {
|
|
facehuggerStatusPointer->nearBehaviourState = FHNS_Approach;
|
|
dynPtr->GravityOn=1;
|
|
SetHuggerAnimationSequence(sbPtr,HSS_Run,(ONE_FIXED*2)/3);
|
|
}
|
|
}
|
|
|
|
static void Execute_FHNS_AboutToJump(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
FACEHUGGER_STATUS_BLOCK *fhugStatusPointer;
|
|
DYNAMICSBLOCK *dynPtr;
|
|
|
|
/* Should still be playing Walk. */
|
|
|
|
LOCALASSERT(sbPtr);
|
|
fhugStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->SBdataptr);
|
|
dynPtr=sbPtr->DynPtr;
|
|
LOCALASSERT(fhugStatusPointer);
|
|
LOCALASSERT(dynPtr);
|
|
|
|
/* don't climb on walls, etc */
|
|
dynPtr->UseStandardGravity=1;
|
|
|
|
{
|
|
/* Orientate to target. */
|
|
VECTORCH orientationDirn;
|
|
int i;
|
|
|
|
orientationDirn.vx = Player->ObWorld.vx - dynPtr->Position.vx;
|
|
orientationDirn.vy = 0;
|
|
orientationDirn.vz = Player->ObWorld.vz - dynPtr->Position.vz;
|
|
i = NPCOrientateToVector(sbPtr, &orientationDirn,NPC_TURNRATE,NULL);
|
|
|
|
if (i==0) {
|
|
/* Still not right! Wait for proper facing. */
|
|
return;
|
|
} else {
|
|
/* Okay, pounce! */
|
|
int distanceToPlayer = VectorDistance(&(dynPtr->Position),&(Player->ObStrategyBlock->DynPtr->Position));
|
|
if((distanceToPlayer<=FACEHUGGER_JUMPDISTANCE)&&(dynPtr->IsInContactWithFloor)
|
|
&&(sbPtr->SBDamageBlock.IsOnFire==0)) {
|
|
JumpAtPlayer(sbPtr);
|
|
return;
|
|
} else {
|
|
/* Return to approach. */
|
|
NPC_InitMovementData(&(fhugStatusPointer->moveData));
|
|
fhugStatusPointer->nearBehaviourState = FHNS_Approach;
|
|
fhugStatusPointer->stateTimer = 0;
|
|
/* no sequence change required */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*--------------------**
|
|
** Loading and Saving **
|
|
**--------------------*/
|
|
#include "savegame.h"
|
|
|
|
typedef struct face_hugger_save_block
|
|
{
|
|
SAVE_BLOCK_STRATEGY_HEADER header;
|
|
|
|
signed int health;
|
|
FACEHUGGER_NEAR_BHSTATE nearBehaviourState;
|
|
int stateTimer;
|
|
int DoomTimer;
|
|
int CurveRadius;
|
|
int CurveLength;
|
|
int CurveTimeOut;
|
|
BOOL jumping;
|
|
|
|
|
|
//strategyblock stuff
|
|
int integrity;
|
|
DAMAGEBLOCK SBDamageBlock;
|
|
DYNAMICSBLOCK dynamics;
|
|
}FACE_HUGGER_SAVE_BLOCK;
|
|
|
|
//defines for load/save macros
|
|
#define SAVELOAD_BLOCK block
|
|
#define SAVELOAD_BEHAV huggerStatusPointer
|
|
|
|
void LoadStrategy_FaceHugger(SAVE_BLOCK_STRATEGY_HEADER* header)
|
|
{
|
|
STRATEGYBLOCK* sbPtr;
|
|
FACEHUGGER_STATUS_BLOCK* huggerStatusPointer;
|
|
FACE_HUGGER_SAVE_BLOCK* block = (FACE_HUGGER_SAVE_BLOCK*) header;
|
|
|
|
//check the size of the save block
|
|
if(header->size!=sizeof(*block)) return;
|
|
|
|
//find the existing strategy block
|
|
sbPtr = FindSBWithName(header->SBname);
|
|
if(!sbPtr) return;
|
|
|
|
//make sure the strategy found is of the right type
|
|
if(sbPtr->I_SBtype != I_BehaviourFaceHugger) return;
|
|
|
|
huggerStatusPointer = (FACEHUGGER_STATUS_BLOCK*)sbPtr->SBdataptr;
|
|
|
|
//start copying stuff
|
|
COPYELEMENT_LOAD(health)
|
|
COPYELEMENT_LOAD(nearBehaviourState)
|
|
COPYELEMENT_LOAD(stateTimer)
|
|
COPYELEMENT_LOAD(DoomTimer)
|
|
COPYELEMENT_LOAD(CurveRadius)
|
|
COPYELEMENT_LOAD(CurveLength)
|
|
COPYELEMENT_LOAD(CurveTimeOut)
|
|
COPYELEMENT_LOAD(jumping)
|
|
|
|
//copy strategy block stuff
|
|
*sbPtr->DynPtr = block->dynamics;
|
|
sbPtr->integrity = block->integrity;
|
|
sbPtr->SBDamageBlock = block->SBDamageBlock;
|
|
|
|
|
|
//load the hierarchy
|
|
{
|
|
SAVE_BLOCK_HEADER* hier_header = GetNextBlockIfOfType(SaveBlock_Hierarchy);
|
|
if(hier_header)
|
|
{
|
|
LoadHierarchy(hier_header,&huggerStatusPointer->HModelController);
|
|
}
|
|
}
|
|
Load_SoundState(&huggerStatusPointer->soundHandle);
|
|
Load_SoundState(&huggerStatusPointer->soundHandle2);
|
|
}
|
|
|
|
void SaveStrategy_FaceHugger(STRATEGYBLOCK* sbPtr)
|
|
{
|
|
FACE_HUGGER_SAVE_BLOCK* block;
|
|
FACEHUGGER_STATUS_BLOCK* huggerStatusPointer;
|
|
|
|
huggerStatusPointer = (FACEHUGGER_STATUS_BLOCK*)sbPtr->SBdataptr;
|
|
GET_STRATEGY_SAVE_BLOCK(block,sbPtr);
|
|
|
|
//start copying stuff
|
|
COPYELEMENT_SAVE(health)
|
|
COPYELEMENT_SAVE(nearBehaviourState)
|
|
COPYELEMENT_SAVE(stateTimer)
|
|
COPYELEMENT_SAVE(DoomTimer)
|
|
COPYELEMENT_SAVE(CurveRadius)
|
|
COPYELEMENT_SAVE(CurveLength)
|
|
COPYELEMENT_SAVE(CurveTimeOut)
|
|
COPYELEMENT_SAVE(jumping)
|
|
|
|
//save strategy block stuff
|
|
block->dynamics = *sbPtr->DynPtr;
|
|
block->dynamics.CollisionReportPtr=0;
|
|
|
|
block->integrity = sbPtr->integrity;
|
|
block->SBDamageBlock = sbPtr->SBDamageBlock;
|
|
|
|
//save the hierarchy
|
|
SaveHierarchy(&huggerStatusPointer->HModelController);
|
|
|
|
Save_SoundState(&huggerStatusPointer->soundHandle);
|
|
Save_SoundState(&huggerStatusPointer->soundHandle2);
|
|
|
|
} |