/* 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;iSBname[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<SBDamageBlock.Armour=NpcData->StartingStats.Armour<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;ideath_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); }