diff options
Diffstat (limited to 'src/avp/bh_alien.c')
| -rw-r--r-- | src/avp/bh_alien.c | 2496 |
1 files changed, 2496 insertions, 0 deletions
diff --git a/src/avp/bh_alien.c b/src/avp/bh_alien.c new file mode 100644 index 0000000..ea3cc6d --- /dev/null +++ b/src/avp/bh_alien.c @@ -0,0 +1,2496 @@ +/*------------------------Patrick 6/11/96----------------------------- + Source file for alien 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 "load_shp.h" +#include "bh_types.h" +#include "bh_debri.h" +#include "bh_far.h" +#include "bh_near.h" +#include "bh_gener.h" +#include "bh_pred.h" +#include "bh_alien.h" +#include "bh_marin.h" +#include "weapons.h" +#include "pheromon.h" +#include "pfarlocs.h" +#include "pvisible.h" +#include "psnd.h" +#include "psndplat.h" +#include "extents.h" +#include "huddefs.h" +#include "pldghost.h" +#include "bh_corpse.h" +#include "bh_dummy.h" +#include "game_statistics.h" +#include "scream.h" + +#define UseLocalAssert Yes +#include "ourasert.h" +#include "pldnet.h" +#include "AvP_UserProfile.h" + +/* external global variables used in this file */ +extern int ModuleArraySize; +extern char *ModuleCurrVisArray; +extern int NormalFrameTime; +extern unsigned char Null_Name[8]; +extern ACTIVESOUNDSAMPLE ActiveSounds[]; + +extern SECTION * GetNamedHierarchyFromLibrary(const char * rif_name, const char * hier_name); +extern void StartAlienAttackSequence(STRATEGYBLOCK *sbPtr); + +extern int NearAliens; +extern int Alt_NearAliens; +extern int FarAliens; +extern int Alt_FarAliens; +extern int ShowHiveState; + +/* prototypes for this file */ + +void KillAlien(STRATEGYBLOCK *sbPtr, int wounds, DAMAGE_PROFILE *damage, int multiple,VECTORCH *incoming); +void Execute_Alien_Dying(STRATEGYBLOCK *sbPtr); +STRATEGYBLOCK *Alien_GetNewTarget(VECTORCH *alienpos, STRATEGYBLOCK *me); +void CreateAlienBot(VECTORCH *Position,int type); + +/*----------------------Patrick 15/11/96----------------------------- + Alien's map: used to generate display blocks for aliens when they + come into view + +--------------------------------------------------------------------*/ +/* KJL 17:16:34 11/25/96 - I've wrapped the necessary fields in the +mapblock with #if StandardStrategyAndCollisions */ + +MODULEMAPBLOCK AlienDefaultMap = +{ + MapType_Sprite, + I_ShapeCube, /* default value */ + 0,0,0, + 0,0,0, + ObFlag_NoInfLSrc|ObFlag_MultLSrc, + 0, + 0, + 0, + 0, + 0, + 0,0,0, + 0, + 0, + 0,0,0, +}; + +/* CDF 12/2/98 */ + +void CastAlienBot(void) { + + #define BOTRANGE 2000 + + VECTORCH position; + #if 0 + if (AvP.Network!=I_No_Network) { + NewOnScreenMessage("NO ALIENBOTS IN MULTIPLAYER MODE"); + return; + } + #endif + position=Player->ObStrategyBlock->DynPtr->Position; + position.vx+=MUL_FIXED(Player->ObStrategyBlock->DynPtr->OrientMat.mat31,BOTRANGE); + position.vy+=MUL_FIXED(Player->ObStrategyBlock->DynPtr->OrientMat.mat32,BOTRANGE); + position.vz+=MUL_FIXED(Player->ObStrategyBlock->DynPtr->OrientMat.mat33,BOTRANGE); + + CreateAlienBot(&position,0); + +} + +void CastPredAlienBot(void) { + + #define BOTRANGE 2000 + + VECTORCH position; + #if 0 + if (AvP.Network!=I_No_Network) { + NewOnScreenMessage("NO ALIENBOTS IN MULTIPLAYER MODE"); + return; + } + #endif + position=Player->ObStrategyBlock->DynPtr->Position; + position.vx+=MUL_FIXED(Player->ObStrategyBlock->DynPtr->OrientMat.mat31,BOTRANGE); + position.vy+=MUL_FIXED(Player->ObStrategyBlock->DynPtr->OrientMat.mat32,BOTRANGE); + position.vz+=MUL_FIXED(Player->ObStrategyBlock->DynPtr->OrientMat.mat33,BOTRANGE); + + CreateAlienBot(&position,1); + +} + +void CastPraetorianBot(void) { + + #define BOTRANGE 2000 + + VECTORCH position; + #if 0 + if (AvP.Network!=I_No_Network) { + NewOnScreenMessage("NO ALIENBOTS IN MULTIPLAYER MODE"); + return; + } + #endif + position=Player->ObStrategyBlock->DynPtr->Position; + position.vx+=MUL_FIXED(Player->ObStrategyBlock->DynPtr->OrientMat.mat31,BOTRANGE); + position.vy+=MUL_FIXED(Player->ObStrategyBlock->DynPtr->OrientMat.mat32,BOTRANGE); + position.vz+=MUL_FIXED(Player->ObStrategyBlock->DynPtr->OrientMat.mat33,BOTRANGE); + + CreateAlienBot(&position,2); + +} + +void CreateAlienBot(VECTORCH *Position,int type) +{ + STRATEGYBLOCK* sbPtr; + + /* create and initialise a strategy block */ + sbPtr = CreateActiveStrategyBlock(); + if(!sbPtr) { + NewOnScreenMessage("FAILED TO CREATE BOT: SB CREATION FAILURE"); + return; /* failure */ + } + InitialiseSBValues(sbPtr); + + sbPtr->I_SBtype = I_BehaviourAlien; + + AssignNewSBName(sbPtr); + + /* 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 = *Position; + dynPtr->OrientEuler = zeroEuler; + CreateEulerMatrix(&dynPtr->OrientEuler, &dynPtr->OrientMat); + TransposeMatrixCH(&dynPtr->OrientMat); + } + else + { + /* dynamics block allocation failed... */ + RemoveBehaviourStrategy(sbPtr); + NewOnScreenMessage("FAILED TO CREATE BOT: DYNBLOCK CREATION FAILURE"); + return; + } + + sbPtr->shapeIndex = 0; + + sbPtr->maintainVisibility = 1; + sbPtr->containingModule = ModuleFromPosition(&(sbPtr->DynPtr->Position), (MODULE*)0); + + /* create, initialise and attach an alien data block */ + sbPtr->SBdataptr = (void *)AllocateMem(sizeof(ALIEN_STATUS_BLOCK)); + if(sbPtr->SBdataptr) + { + SECTION *root_section; + ALIEN_STATUS_BLOCK *alienStatus = (ALIEN_STATUS_BLOCK *)sbPtr->SBdataptr; + int i; + + NPC_InitMovementData(&(alienStatus->moveData)); + NPC_InitWanderData(&(alienStatus->wanderData)); + switch (type) { + case 0: + default: + alienStatus->Type = AT_Standard; + break; + case 1: + alienStatus->Type = AT_Predalien; + break; + case 2: + alienStatus->Type = AT_Praetorian; + break; + } + + /* Initialise alien's stats */ + { + NPC_DATA *NpcData; + + switch (alienStatus->Type) { + case AT_Standard: + NpcData=GetThisNpcData(I_NPC_Alien); + alienStatus->MaxSpeed=ALIEN_FORWARDVELOCITY; + alienStatus->EnableWaypoints=1; + alienStatus->PreferToCrouch=0; + sbPtr->DynPtr->Mass = ALIEN_MASS; + break; + case AT_Predalien: + NpcData=GetThisNpcData(I_NPC_PredatorAlien); + alienStatus->MaxSpeed=MUL_FIXED(ALIEN_FORWARDVELOCITY,PREDALIEN_SPEED_FACTOR); + alienStatus->EnableWaypoints=1; + alienStatus->PreferToCrouch=0; + sbPtr->DynPtr->Mass = PREDALIEN_MASS; + break; + case AT_Praetorian: + NpcData=GetThisNpcData(I_NPC_PraetorianGuard); + alienStatus->MaxSpeed=MUL_FIXED(ALIEN_FORWARDVELOCITY,MUL_FIXED(PRAETORIAN_WALKSPEED_FACTOR,PRAETORIAN_SPEED_FACTOR)); + alienStatus->EnableWaypoints=0; + alienStatus->PreferToCrouch=0; + sbPtr->DynPtr->Mass = PRAETORIAN_MASS; + break; + default: + GLOBALASSERT(0); + break; + } + 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; + } + + + alienStatus->Health = ALIEN_STARTING_HEALTH; + alienStatus->GibbFactor=0; + alienStatus->Wounds=0; + alienStatus->last_anim_length=ONE_FIXED; + sbPtr->integrity = alienStatus->Health; + alienStatus->BehaviourState = ABS_Hunt; + alienStatus->current_attack=NULL; + + alienStatus->Target=NULL; + COPY_NAME(alienStatus->Target_SBname,Null_Name); + alienStatus->Mission=AM_Hunt; /* Was GlobalHunt... */ + + alienStatus->my_containing_module=NULL; + alienStatus->huntingModule=NULL; + + Initialise_AvoidanceManager(sbPtr,&alienStatus->avoidanceManager); + InitWaypointManager(&alienStatus->waypointManager); + + alienStatus->CurveRadius = 0; + alienStatus->CurveLength = 0; + alienStatus->CurveTimeOut = 0; + alienStatus->FarStateTimer = ALIEN_FAR_MOVE_TIME; + alienStatus->NearStateTimer = 0; + alienStatus->IAmCrouched = 0; + + alienStatus->soundHandle = SOUND_NOACTIVEINDEX; + alienStatus->soundHandle2 = SOUND_NOACTIVEINDEX; + + alienStatus->incidentFlag=0; + alienStatus->incidentTimer=0; + + //alien created by generator won't have a death target + for(i=0;i<SB_NAME_LENGTH;i++) alienStatus->death_target_ID[i] =0; + alienStatus->death_target_sbptr=0; + + //this alien wasn't produced by a generator + alienStatus->generator_sbptr=0; + + + alienStatus->HModelController.section_data=NULL; + alienStatus->HModelController.Deltas=NULL; + + switch (alienStatus->Type) { + case AT_Standard: + root_section=GetNamedHierarchyFromLibrary("hnpcalien","alien"); + break; + case AT_Predalien: + root_section=GetNamedHierarchyFromLibrary("hnpcpred_alien","TEMPLATE"); + break; + case AT_Praetorian: + root_section=GetNamedHierarchyFromLibrary("hnpcpretorian","Template"); + break; + default: + GLOBALASSERT(0); + break; + } + + if (!root_section) { + RemoveBehaviourStrategy(sbPtr); + NewOnScreenMessage("FAILED TO CREATE BOT: NO HMODEL"); + return; + } + Create_HModel(&alienStatus->HModelController,root_section); + InitHModelSequence(&alienStatus->HModelController,0,0,ONE_FIXED); + + if (SLUGTRAIL_MODE) { + SECTION_DATA *leg; + /* Blow off a leg? */ + leg=GetThisSectionData(alienStatus->HModelController.section_data,"left thigh"); + if (leg) { + Prune_HModel_Virtual(leg); + alienStatus->Wounds|=section_flag_left_leg; + alienStatus->Wounds|=section_flag_left_foot; + + sbPtr->SBDamageBlock.Health-=(20<<ONE_FIXED_SHIFT); + RecomputeAlienSpeed(sbPtr); + } + } + + + if (HModelSequence_Exists(&alienStatus->HModelController,(int)HMSQT_AlienStand,(int)ASSS_Hit_Right)) { + DELTA_CONTROLLER *delta; + delta=Add_Delta_Sequence(&alienStatus->HModelController,"HitDelta",(int)HMSQT_AlienStand,(int)ASSS_Hit_Right,-1); + GLOBALASSERT(delta); + delta->Playing=0; + } + + /* Containment test NOW! */ + if(!(sbPtr->containingModule)) + { + /* no containing module can be found... abort*/ + RemoveBehaviourStrategy(sbPtr); + NewOnScreenMessage("FAILED TO CREATE BOT: MODULE CONTAINMENT FAILURE"); + return; + } + LOCALASSERT(sbPtr->containingModule); + + if (HModelSequence_Exists(&alienStatus->HModelController,HMSQT_AlienRun,ARSS_Jump)) { + alienStatus->JumpDetected=1; + } else { + alienStatus->JumpDetected=0; + } + + if (GetAlienPounceAttack(&alienStatus->HModelController,0,1)) { + /* Pounce will be unset eventually. */ + alienStatus->PounceDetected=1; + alienStatus->EnablePounce=1; + } else { + alienStatus->PounceDetected=0; + alienStatus->EnablePounce=0; + } + + alienStatus->aliensIgniterId=NULL; + + MakeAlienNear(sbPtr); + + NewOnScreenMessage("ALIENBOT CREATED"); + + } + else + { + /* no data block can be allocated */ + RemoveBehaviourStrategy(sbPtr); + NewOnScreenMessage("FAILED TO CREATE BOT: MALLOC FAILURE"); + return; + } + +} + + +/*----------------------Patrick 15/11/96----------------------------- +This function is called by the alien generators, to dynamically +create a new alien, during the game. The alien is initialised +as invisible. + +1. create a new strategy block (no diplay block) +2. attach a dynamics block +3. attach an alien data block +4. initialise for far behaviour + +NB the strategyblock passed here is a reference to the generator sb +--------------------------------------------------------------------*/ +void CreateAlienDynamic(STRATEGYBLOCK *Generator, ALIEN_TYPE type_of_alien) +{ + STRATEGYBLOCK* sbPtr; + //int i; + + + /* create and initialise a strategy block */ + sbPtr = CreateActiveStrategyBlock(); + if(!sbPtr) return; /* failure */ + InitialiseSBValues(sbPtr); + + sbPtr->I_SBtype = I_BehaviourAlien; + + AssignNewSBName(sbPtr); + + /* 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 = ((GENERATOR_BLOCK* )Generator->SBdataptr)->Position; + dynPtr->OrientEuler = zeroEuler; + CreateEulerMatrix(&dynPtr->OrientEuler, &dynPtr->OrientMat); + TransposeMatrixCH(&dynPtr->OrientMat); + } + else + { + /* dynamics block allocation failed... */ + RemoveBehaviourStrategy(sbPtr); + return; + } + + sbPtr->shapeIndex = Generator->shapeIndex; + + sbPtr->maintainVisibility = 1; + sbPtr->containingModule = ModuleFromPosition(&(sbPtr->DynPtr->Position), (MODULE*)0); + LOCALASSERT(sbPtr->containingModule); + if(!(sbPtr->containingModule)) + { + /* no containing module can be found... abort*/ + RemoveBehaviourStrategy(sbPtr); + return; + } + + /* assert alien is starting as invisible */ + LOCALASSERT(ModuleCurrVisArray[(sbPtr->containingModule->m_index)] == 0); + + /* create, initialise and attach an alien data block */ + sbPtr->SBdataptr = (void *)AllocateMem(sizeof(ALIEN_STATUS_BLOCK)); + if(sbPtr->SBdataptr) + { + SECTION *root_section; + ALIEN_STATUS_BLOCK *alienStatus = (ALIEN_STATUS_BLOCK *)sbPtr->SBdataptr; + int i; + + NPC_InitMovementData(&(alienStatus->moveData)); + NPC_InitWanderData(&(alienStatus->wanderData)); + + alienStatus->Type = type_of_alien; + + /* Initialise alien's stats */ + { + NPC_DATA *NpcData; + + switch (alienStatus->Type) { + case AT_Standard: + NpcData=GetThisNpcData(I_NPC_Alien); + alienStatus->MaxSpeed=ALIEN_FORWARDVELOCITY; + alienStatus->EnableWaypoints=1; + alienStatus->PreferToCrouch=0; + sbPtr->DynPtr->Mass = ALIEN_MASS; + break; + case AT_Predalien: + NpcData=GetThisNpcData(I_NPC_PredatorAlien); + alienStatus->MaxSpeed=MUL_FIXED(ALIEN_FORWARDVELOCITY,PREDALIEN_SPEED_FACTOR); + alienStatus->EnableWaypoints=1; + alienStatus->PreferToCrouch=0; + sbPtr->DynPtr->Mass = PREDALIEN_MASS; + break; + case AT_Praetorian: + NpcData=GetThisNpcData(I_NPC_PraetorianGuard); + alienStatus->MaxSpeed=MUL_FIXED(ALIEN_FORWARDVELOCITY,MUL_FIXED(PRAETORIAN_WALKSPEED_FACTOR,PRAETORIAN_SPEED_FACTOR)); + alienStatus->EnableWaypoints=0; + alienStatus->PreferToCrouch=0; + sbPtr->DynPtr->Mass = PRAETORIAN_MASS; + break; + default: + GLOBALASSERT(0); + break; + } + 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; + } + + + alienStatus->Health = ALIEN_STARTING_HEALTH; + alienStatus->GibbFactor=0; + alienStatus->Wounds=0; + alienStatus->last_anim_length=ONE_FIXED; + sbPtr->integrity = alienStatus->Health; + alienStatus->BehaviourState = ABS_Hunt; + alienStatus->current_attack=NULL; + alienStatus->Wounds=0; + + alienStatus->Target=NULL; + COPY_NAME(alienStatus->Target_SBname,Null_Name); + alienStatus->Mission=AM_Hunt; + + alienStatus->my_containing_module=NULL; + alienStatus->huntingModule=NULL; + + alienStatus->CurveRadius = 0; + alienStatus->CurveLength = 0; + alienStatus->CurveTimeOut = 0; + alienStatus->FarStateTimer = ALIEN_FAR_MOVE_TIME; + alienStatus->NearStateTimer = 0; + alienStatus->IAmCrouched = 0; + + Initialise_AvoidanceManager(sbPtr,&alienStatus->avoidanceManager); + InitWaypointManager(&alienStatus->waypointManager); + + alienStatus->soundHandle = SOUND_NOACTIVEINDEX; + alienStatus->soundHandle2 = SOUND_NOACTIVEINDEX; + + alienStatus->incidentFlag=0; + alienStatus->incidentTimer=0; + + //alien created by generator won't have a death target + for(i=0;i<SB_NAME_LENGTH;i++) alienStatus->death_target_ID[i] =0; + alienStatus->death_target_sbptr=0; + + //note the generator that produced this alien + alienStatus->generator_sbptr=Generator; + + switch (alienStatus->Type) { + case AT_Standard: + root_section=GetNamedHierarchyFromLibrary("hnpcalien","alien"); + break; + case AT_Predalien: + root_section=GetNamedHierarchyFromLibrary("hnpcpred_alien","TEMPLATE"); + break; + case AT_Praetorian: + root_section=GetNamedHierarchyFromLibrary("hnpcpretorian","Template"); + break; + default: + GLOBALASSERT(0); + break; + } + + Create_HModel(&alienStatus->HModelController,root_section); + InitHModelSequence(&alienStatus->HModelController,0,0,ONE_FIXED); + + if (SLUGTRAIL_MODE) { + SECTION_DATA *leg; + /* Blow off a leg? */ + leg=GetThisSectionData(alienStatus->HModelController.section_data,"left thigh"); + if (leg) { + Prune_HModel_Virtual(leg); + alienStatus->Wounds|=section_flag_left_leg; + alienStatus->Wounds|=section_flag_left_foot; + + sbPtr->SBDamageBlock.Health-=(20<<ONE_FIXED_SHIFT); + RecomputeAlienSpeed(sbPtr); + } + } + + if (HModelSequence_Exists(&alienStatus->HModelController,(int)HMSQT_AlienStand,(int)ASSS_Hit_Right)) { + DELTA_CONTROLLER *delta; + delta=Add_Delta_Sequence(&alienStatus->HModelController,"HitDelta",(int)HMSQT_AlienStand,(int)ASSS_Hit_Right,-1); + GLOBALASSERT(delta); + delta->Playing=0; + } + + ProveHModel_Far(&alienStatus->HModelController,sbPtr); + + if (HModelSequence_Exists(&alienStatus->HModelController,HMSQT_AlienRun,ARSS_Jump)) { + alienStatus->JumpDetected=1; + } else { + alienStatus->JumpDetected=0; + } + + if (GetAlienPounceAttack(&alienStatus->HModelController,0,1)) { + /* Pounce will be unset eventually. */ + alienStatus->PounceDetected=1; + alienStatus->EnablePounce=1; + } else { + alienStatus->PounceDetected=0; + alienStatus->EnablePounce=0; + } + alienStatus->aliensIgniterId=NULL; + + } + else + { + /* no data block can be allocated */ + RemoveBehaviourStrategy(sbPtr); + return; + } +} + +/*----------------------Patrick 7/11/96----------------------------- +This function is used to initialise a riff-loaded in alien. +-------------------------------------------------------------------*/ +void InitAlienBehaviour(void* bhdata, STRATEGYBLOCK *sbPtr) +{ + TOOLS_DATA_ALIEN *toolsData = (TOOLS_DATA_ALIEN *)bhdata; + int i; + + /* check we're not in a net game */ + if(AvP.Network != I_No_Network && !AvP.NetworkAIServer) + { + RemoveBehaviourStrategy(sbPtr); + return; + } + + /* strategy block should be initialised to general default values: + I_behaviourAlien, shapeIndex, and everything else 0.... + + NB it is necessary that placed aliens are initialised without a + displayblock, otherwise far aliens will be re-initialised by + MakeAlienFar() to a hunting state instead of a wait state....*/ + LOCALASSERT(!(sbPtr->SBdptr)); + + /* strategy block initialisation */ + sbPtr->shapeIndex = toolsData->shapeIndex; + + if(AvP.Network==I_No_Network) + { + for(i=0;i<SB_NAME_LENGTH;i++) sbPtr->SBname[i] = toolsData->nameID[i]; + } + else + { + //in a network game , generate a new sb name (so that it gets a reasonably low value) + AssignNewSBName(sbPtr); + } + + /* create, initialise and attach a dynamics block */ + sbPtr->DynPtr = AllocateDynamicsBlock(DYNAMICS_TEMPLATE_ALIEN_NPC); + if(sbPtr->DynPtr) + { + DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr; + dynPtr->PrevPosition = dynPtr->Position = toolsData->position; + dynPtr->OrientEuler = toolsData->starteuler; + CreateEulerMatrix(&dynPtr->OrientEuler, &dynPtr->OrientMat); + TransposeMatrixCH(&dynPtr->OrientMat); + } + else + { + /* no dynamics block can be allocated */ + RemoveBehaviourStrategy(sbPtr); + return; + } + + /* create, initialise and attach an alien data block */ + sbPtr->SBdataptr = (void *)AllocateMem(sizeof(ALIEN_STATUS_BLOCK)); + if(sbPtr->SBdataptr) + { + SECTION *root_section; + ALIEN_STATUS_BLOCK * alienStatus = (ALIEN_STATUS_BLOCK *)sbPtr->SBdataptr; + + NPC_InitMovementData(&(alienStatus->moveData)); + NPC_InitWanderData(&(alienStatus->wanderData)); + + alienStatus->Type = toolsData->type; + + /* Initialise alien's stats */ + { + NPC_DATA *NpcData; + + switch (alienStatus->Type) { + case AT_Standard: + NpcData=GetThisNpcData(I_NPC_Alien); + alienStatus->MaxSpeed=ALIEN_FORWARDVELOCITY; + alienStatus->EnableWaypoints=1; + alienStatus->PreferToCrouch=0; + sbPtr->DynPtr->Mass = ALIEN_MASS; + break; + case AT_Predalien: + NpcData=GetThisNpcData(I_NPC_PredatorAlien); + alienStatus->MaxSpeed=MUL_FIXED(ALIEN_FORWARDVELOCITY,PREDALIEN_SPEED_FACTOR); + alienStatus->EnableWaypoints=1; + alienStatus->PreferToCrouch=0; + sbPtr->DynPtr->Mass = PREDALIEN_MASS; + break; + case AT_Praetorian: + NpcData=GetThisNpcData(I_NPC_PraetorianGuard); + alienStatus->MaxSpeed=MUL_FIXED(ALIEN_FORWARDVELOCITY,MUL_FIXED(PRAETORIAN_WALKSPEED_FACTOR,PRAETORIAN_SPEED_FACTOR)); + alienStatus->EnableWaypoints=0; + alienStatus->PreferToCrouch=0; + sbPtr->DynPtr->Mass = PRAETORIAN_MASS; + break; + default: + GLOBALASSERT(0); + break; + } + 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; + } + + alienStatus->Health = ALIEN_STARTING_HEALTH; + sbPtr->integrity = alienStatus->Health; + alienStatus->GibbFactor=0; + alienStatus->Wounds=0; + alienStatus->last_anim_length=ONE_FIXED; + + if (toolsData->start_inactive) { + alienStatus->BehaviourState = ABS_Dormant; + } else { + alienStatus->BehaviourState = ABS_Wait; + } + + alienStatus->current_attack=NULL; + alienStatus->Wounds=0; + + alienStatus->Target=NULL; + COPY_NAME(alienStatus->Target_SBname,Null_Name); + alienStatus->Mission=AM_Hunt; + + alienStatus->my_containing_module=NULL; + alienStatus->huntingModule=NULL; + + alienStatus->CurveRadius = 0; + alienStatus->CurveLength = 0; + alienStatus->CurveTimeOut = 0; + alienStatus->FarStateTimer = ALIEN_FAR_MOVE_TIME; + alienStatus->NearStateTimer = 0; + alienStatus->IAmCrouched = 0; + + Initialise_AvoidanceManager(sbPtr,&alienStatus->avoidanceManager); + InitWaypointManager(&alienStatus->waypointManager); + + alienStatus->soundHandle = SOUND_NOACTIVEINDEX; + alienStatus->soundHandle2 = SOUND_NOACTIVEINDEX; + + alienStatus->incidentFlag=0; + alienStatus->incidentTimer=0; + + for(i=0;i<SB_NAME_LENGTH;i++) alienStatus->death_target_ID[i] = toolsData->death_target_ID[i]; + alienStatus->death_target_sbptr=0; + + //this alien wasn't produced by a generator + alienStatus->generator_sbptr=0; + + + switch (alienStatus->Type) { + case AT_Standard: + root_section=GetNamedHierarchyFromLibrary("hnpcalien","alien"); + break; + case AT_Predalien: + root_section=GetNamedHierarchyFromLibrary("hnpcpred_alien","TEMPLATE"); + break; + case AT_Praetorian: + root_section=GetNamedHierarchyFromLibrary("hnpcpretorian","Template"); + break; + default: + GLOBALASSERT(0); + break; + } + + Create_HModel(&alienStatus->HModelController,root_section); + + if (toolsData->start_inactive) { + if (HModelSequence_Exists(&alienStatus->HModelController,HMSQT_AlienStand,ASSS_Dormant)) { + InitHModelSequence(&alienStatus->HModelController,HMSQT_AlienStand,ASSS_Dormant,-1); + } else { + InitHModelSequence(&alienStatus->HModelController,HMSQT_AlienStand,ASSS_Standard,ONE_FIXED); + } + } else { + InitHModelSequence(&alienStatus->HModelController,0,0,ONE_FIXED); + } + + if (SLUGTRAIL_MODE) { + SECTION_DATA *leg; + /* Blow off a leg? */ + leg=GetThisSectionData(alienStatus->HModelController.section_data,"left thigh"); + if (leg) { + Prune_HModel_Virtual(leg); + alienStatus->Wounds|=section_flag_left_leg; + alienStatus->Wounds|=section_flag_left_foot; + + sbPtr->SBDamageBlock.Health-=(20<<ONE_FIXED_SHIFT); + RecomputeAlienSpeed(sbPtr); + } + } + + if (HModelSequence_Exists(&alienStatus->HModelController,(int)HMSQT_AlienStand,(int)ASSS_Hit_Right)) { + DELTA_CONTROLLER *delta; + delta=Add_Delta_Sequence(&alienStatus->HModelController,"HitDelta",(int)HMSQT_AlienStand,(int)ASSS_Hit_Right,-1); + GLOBALASSERT(delta); + delta->Playing=0; + } + + ProveHModel_Far(&alienStatus->HModelController,sbPtr); + + if (HModelSequence_Exists(&alienStatus->HModelController,HMSQT_AlienRun,ARSS_Jump)) { + alienStatus->JumpDetected=1; + } else { + alienStatus->JumpDetected=0; + } + + if (GetAlienPounceAttack(&alienStatus->HModelController,0,1)) { + /* Pounce will be unset eventually. */ + alienStatus->PounceDetected=1; + alienStatus->EnablePounce=1; + } else { + alienStatus->PounceDetected=0; + alienStatus->EnablePounce=0; + } + + alienStatus->aliensIgniterId=NULL; + + } + else + { + /* no data block can be allocated */ + RemoveBehaviourStrategy(sbPtr); + return; + } +} + + +static BOOL PlayerIsDeadAndNoLivingNetghosts() +{ + if(AvP.Network == I_No_Network) + { + //just need to check for the only player we have + return NPC_IsDead(Player->ObStrategyBlock); + + } + else + { + int sbIndex; + //first check the host player + if(!NPC_IsDead(Player->ObStrategyBlock)) return FALSE; + + /* go through the strategy blocks looking for players*/ + for(sbIndex=0;sbIndex<NumActiveStBlocks;sbIndex++) + { + STRATEGYBLOCK *playerSbPtr = ActiveStBlockList[sbIndex]; + NETGHOSTDATABLOCK *ghostData; + if(playerSbPtr->I_SBtype!=I_BehaviourNetGhost) continue; + ghostData = (NETGHOSTDATABLOCK *)playerSbPtr->SBdataptr; + + if(ghostData->type==I_BehaviourMarinePlayer || + ghostData->type==I_BehaviourPredatorPlayer) + { + //found a nethost of a player + return FALSE; + } + } + } + //Dead, all dead. + return TRUE; +} + +/*----------------------Patrick 7/11/96----------------------------- +AI alien behaviour execution shell: + +1. patch to trap aliens who's current module is not set (and set it). +2. call the visibility checking function. +3. select either near or far behaviour functions. + +NB the visibility checking function initialises near/far behaviour and +allocates/deallocates displayblock based on changes in visibility +for the alien's module. This will, in due course, be invoked by a +call back function from the module handler. +--------------------------------------------------------------------*/ + +void AlienBehaviour(STRATEGYBLOCK *sbPtr) +{ + /* get the alien's status block */ + ALIEN_STATUS_BLOCK *alienStatusPointer; + int alienIsNear; + + LOCALASSERT(sbPtr); + alienStatusPointer= (ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr); + LOCALASSERT(alienStatusPointer); + + /* test if we've got a containing module: if we haven't, do nothing. + This is important as the alien could have been marked for deletion by the visibility + management system...*/ + if(!sbPtr->containingModule) + { + DestroyAnyStrategyBlock(sbPtr); /* just to make sure */ + return; + } + + if(sbPtr->SBdptr) { + alienIsNear=1; + LOCALASSERT(ModuleCurrVisArray[(sbPtr->containingModule->m_index)]); + } else { + alienIsNear=0; + } + + /* Unset incident flag. */ + alienStatusPointer->incidentFlag=0; + + alienStatusPointer->incidentTimer-=NormalFrameTime; + + if (alienStatusPointer->incidentTimer<0) { + alienStatusPointer->incidentFlag=1; + alienStatusPointer->incidentTimer=32767+(FastRandom()&65535); + } + + if(alienStatusPointer->soundHandle!=SOUND_NOACTIVEINDEX) Sound_Update3d(alienStatusPointer->soundHandle,&(sbPtr->DynPtr->Position)); + + if (sbPtr->SBDamageBlock.IsOnFire) { + + if (alienStatusPointer->soundHandle!=SOUND_NOACTIVEINDEX) { + if (ActiveSounds[alienStatusPointer->soundHandle].soundIndex!=SID_FIRE) { + Sound_Stop(alienStatusPointer->soundHandle); + Sound_Play(SID_FIRE,"dlev",&(sbPtr->DynPtr->Position),&alienStatusPointer->soundHandle,127); + } + } else { + Sound_Play(SID_FIRE,"dlev",&(sbPtr->DynPtr->Position),&alienStatusPointer->soundHandle,127); + } + //for multiplayer games it is necessary to say who is doing this damage + //(that would be the person who set the alien on fire in the first place) + { + extern DPID myNetworkKillerId; + extern DPID AVPDPNetID; + + myNetworkKillerId=alienStatusPointer->aliensIgniterId; + CauseDamageToObject(sbPtr,&firedamage,NormalFrameTime,NULL); + myNetworkKillerId=AVPDPNetID; + } + if (sbPtr->I_SBtype==I_BehaviourNetCorpse) { + /* Gettin' out of here... */ + return; + } + } else { + if (alienStatusPointer->soundHandle!=SOUND_NOACTIVEINDEX) { + if (ActiveSounds[alienStatusPointer->soundHandle].soundIndex==SID_FIRE) { + Sound_Stop(alienStatusPointer->soundHandle); + } + } + } + + if (alienStatusPointer->GibbFactor) { + /* If you're gibbed, you're dead. */ + sbPtr->SBDamageBlock.Health = 0; + } + + /* check if we've been killed */ + if ( (sbPtr->SBDamageBlock.Health <= 0)&&(alienStatusPointer->BehaviourState!=ABS_Dying) ) + { + textprint("Zombie Alien!!! State is %d.\n",alienStatusPointer->BehaviourState); + return; + } + + if ((alienStatusPointer->BehaviourState!=ABS_Dying) + &&(alienStatusPointer->BehaviourState!=ABS_Dormant) + &&(alienStatusPointer->BehaviourState!=ABS_Awakening)) { + + /* Target handling. */ + if (Validate_Target(alienStatusPointer->Target,alienStatusPointer->Target_SBname)==0) { + + /* Target must be dead... */ + alienStatusPointer->Target=NULL; + /* Now try to get a new target. */ + } + + if ((alienIsNear)||(alienStatusPointer->incidentFlag)) { + if (alienStatusPointer->Target==NULL) { + + alienStatusPointer->Target=Alien_GetNewTarget(&sbPtr->DynPtr->Position,sbPtr); + + if (alienStatusPointer->Target!=NULL) { + textprint("Alien gets new target.\n"); + + COPY_NAME(alienStatusPointer->Target_SBname,alienStatusPointer->Target->SBname); + + if (alienStatusPointer->BehaviourState!=ABS_Wait) { + alienStatusPointer->BehaviourState=ABS_Hunt; + } + alienStatusPointer->FarStateTimer = ALIEN_FAR_MOVE_TIME; + alienStatusPointer->NearStateTimer = 0; + + } else { + textprint("Alien found no target!\n"); + if (alienStatusPointer->BehaviourState!=ABS_Wait) { + AIMODULE *targetModule; + /* Hunt or wander? */ + if ((AvP.PlayerType==I_Alien)||(Observer)) { + /* Eew! What a nasty hack! Eeeww! */ + targetModule = FarNPC_GetTargetAIModuleForGlobalHunt(sbPtr); + } else { + /* This just makes them hunt a bit more... */ + targetModule = FarNPC_GetTargetAIModuleForHunt(sbPtr,1); + } + + if ((!targetModule)||(PlayerIsDeadAndNoLivingNetghosts())) { + if (alienStatusPointer->BehaviourState!=ABS_Wander) { + NPC_InitMovementData(&(alienStatusPointer->moveData)); + alienStatusPointer->FarStateTimer = ALIEN_FAR_MOVE_TIME; + alienStatusPointer->NearStateTimer = 0; + } + alienStatusPointer->BehaviourState=ABS_Wander; + } else { + if (alienStatusPointer->BehaviourState!=ABS_Hunt) { + alienStatusPointer->FarStateTimer = ALIEN_FAR_MOVE_TIME; + alienStatusPointer->NearStateTimer = 0; + } + alienStatusPointer->BehaviourState=ABS_Hunt; + } + } + } + } + } + } + + if(sbPtr->SBdptr) + { + LOCALASSERT(ModuleCurrVisArray[(sbPtr->containingModule->m_index)]); + #if 0/*SupportWindows95 */ + textprint("Near Alien in module %s \n",sbPtr->containingModule->name); + #endif + NearAlienBehaviour(sbPtr); + Alt_NearAliens++; + } + else + { + /* NB if this assert fires, we may just have run out of displayblocks */ + LOCALASSERT(ModuleCurrVisArray[(sbPtr->containingModule->m_index)] == 0); + #if 0/*SupportWindows95 */ + textprint("Far Alien in module %s \n",sbPtr->containingModule->name); + #endif + FarAlienBehaviour(sbPtr); + Alt_FarAliens++; + } + + /* if we have actually died, we need to remove the strategyblock... so + do this here */ + if((alienStatusPointer->BehaviourState == ABS_Dying)&&(alienStatusPointer->NearStateTimer <= 0)) { + + if(alienStatusPointer->soundHandle!=SOUND_NOACTIVEINDEX) Sound_Stop(alienStatusPointer->soundHandle); + if(alienStatusPointer->soundHandle2!=SOUND_NOACTIVEINDEX) Sound_Stop(alienStatusPointer->soundHandle2); + DestroyAnyStrategyBlock(sbPtr); + } + + /* Update containing module? */ + if (sbPtr->containingModule!=alienStatusPointer->my_containing_module) { + /* This is to slow down new hunting target computation. */ + alienStatusPointer->my_containing_module=sbPtr->containingModule; + } + + /* Update delta playing flag. */ + { + DELTA_CONTROLLER *hitdelta; + + hitdelta=Get_Delta_Sequence(&alienStatusPointer->HModelController,"HitDelta"); + + if (hitdelta) { + if (DeltaAnimation_IsFinished(hitdelta)) { + hitdelta->Playing=0; + } + } + } +} + + + +/*----------------------Patrick 7/11/96----------------------------- +This pair of functions handle the alien visibility switching.... +Note that the ai-pheromone system is maintained by these functions. +--------------------------------------------------------------------*/ +void MakeAlienNear(STRATEGYBLOCK *sbPtr) +{ + MODULE tempModule; + DISPLAYBLOCK *dPtr; + DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr; + ALIEN_STATUS_BLOCK *alienStatusPointer= (ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr); + + LOCALASSERT(alienStatusPointer); + LOCALASSERT(dynPtr); + LOCALASSERT(sbPtr->SBdptr == NULL); + + /* first of all, see how many aliens are currently near: if there are too many, + destroy this alien, and try to force a generator to make a replacement */ + if(NumGeneratorNPCsVisible() >= MAX_VISIBLEGENERATORNPCS) + { + DestroyAnyStrategyBlock(sbPtr); + ForceAGenerator(); + } + + 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: leave alien far */ + + sbPtr->SBdptr = dPtr; + dPtr->ObStrategyBlock = sbPtr; + dPtr->ObMyModule = NULL; + + + /*Copy extents from the collision extents in extents.c*/ + dPtr->ObMinX=-CollisionExtents[CE_ALIEN].CollisionRadius; + dPtr->ObMaxX=CollisionExtents[CE_ALIEN].CollisionRadius; + dPtr->ObMinZ=-CollisionExtents[CE_ALIEN].CollisionRadius; + dPtr->ObMaxZ=CollisionExtents[CE_ALIEN].CollisionRadius; + dPtr->ObMinY=CollisionExtents[CE_ALIEN].CrouchingTop; + dPtr->ObMaxY=CollisionExtents[CE_ALIEN].Bottom; + dPtr->ObRadius = 1000; + /* also need to initialise positional information in the new display block, + from the existing dynamics block. + NB this necessary because this function is (usually) called between the + dynamics and rendering systems so it is not initialised by the dynamics + system the first frame it is drawn. */ + dPtr->ObWorld = dynPtr->Position; + dPtr->ObEuler = dynPtr->OrientEuler; + dPtr->ObMat = dynPtr->OrientMat; + + /* init state timers */ + alienStatusPointer->NearStateTimer = 0; + alienStatusPointer->FarStateTimer = ALIEN_FAR_MOVE_TIME; + alienStatusPointer->CurveRadius = 0; + alienStatusPointer->CurveLength = 0; + alienStatusPointer->CurveTimeOut = 0; + + /* zero linear velocity in dynamics block */ + dynPtr->LinVelocity.vx = 0; + dynPtr->LinVelocity.vy = 0; + dynPtr->LinVelocity.vz = 0; + + /* initialise our sequence data */ + //dPtr->ShapeAnimControlBlock = &alienStatusPointer->ShpAnimCtrl; + dPtr->HModelControlBlock=&alienStatusPointer->HModelController; + + ProveHModel(dPtr->HModelControlBlock,dPtr); + + if(AlienShouldBeCrawling(sbPtr)) alienStatusPointer->IAmCrouched = 1; + else alienStatusPointer->IAmCrouched = 0; + + RecomputeAlienSpeed(sbPtr); + + InitWaypointManager(&alienStatusPointer->waypointManager); + + /* initialise our near state and specific sequence */ + if ((alienStatusPointer->BehaviourState == ABS_Dying) + ||(alienStatusPointer->BehaviourState == ABS_Dormant) + ||(alienStatusPointer->BehaviourState == ABS_Awakening)) { + /* Do nothing. */ + } else if(alienStatusPointer->BehaviourState == ABS_Wait) { + NPC_InitMovementData(&(alienStatusPointer->moveData)); + alienStatusPointer->BehaviourState = ABS_Wait; + if(alienStatusPointer->IAmCrouched) SetAlienShapeAnimSequence(sbPtr,HMSQT_AlienCrouch,(int)ACrSS_Standard,ONE_FIXED); + else SetAlienShapeAnimSequence(sbPtr,HMSQT_AlienStand,(int)ASSS_Standard,ONE_FIXED); + } else if(AlienIsAwareOfTarget(sbPtr)) { + NPC_InitMovementData(&(alienStatusPointer->moveData)); + alienStatusPointer->BehaviourState = ABS_Approach; + if(alienStatusPointer->IAmCrouched) SetAlienShapeAnimSequence(sbPtr,HMSQT_AlienCrawl,(int)ACSS_Standard,ONE_FIXED>>1); + else SetAlienShapeAnimSequence(sbPtr,HMSQT_AlienRun,(int)ARSS_Standard,ONE_FIXED>>1); + } else { + NPC_InitMovementData(&(alienStatusPointer->moveData)); + /* Was Wander, now Hunt. Hunt should kick in wander if needed. */ + alienStatusPointer->BehaviourState = ABS_Hunt; + if(alienStatusPointer->IAmCrouched) SetAlienShapeAnimSequence(sbPtr,HMSQT_AlienCrawl,(int)ACSS_Standard,ONE_FIXED>>1); + else SetAlienShapeAnimSequence(sbPtr,HMSQT_AlienRun,(int)ARSS_Standard,ONE_FIXED>>1); + } +} + +void MakeAlienFar(STRATEGYBLOCK *sbPtr) +{ + /* get the alien's status block */ + ALIEN_STATUS_BLOCK *alienStatusPointer; + int i; + + LOCALASSERT(sbPtr); + alienStatusPointer=(ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr); + LOCALASSERT(alienStatusPointer); + LOCALASSERT(sbPtr->SBdptr != NULL); + + /* get rid of the displayblock */ + if(sbPtr->SBdptr) + { + i = DestroyActiveObject(sbPtr->SBdptr); + LOCALASSERT(i==0); + sbPtr->SBdptr = NULL; + } + + if(alienStatusPointer->soundHandle!=SOUND_NOACTIVEINDEX) Sound_Stop(alienStatusPointer->soundHandle); + if(alienStatusPointer->soundHandle2!=SOUND_NOACTIVEINDEX) Sound_Stop(alienStatusPointer->soundHandle2); + + /* initialise our far state */ + if ((alienStatusPointer->BehaviourState!=ABS_Dying) + &&(alienStatusPointer->BehaviourState!=ABS_Dormant) + &&(alienStatusPointer->BehaviourState!=ABS_Awakening)) { + /* No zombie aliens here! */ + if(AlienIsAwareOfTarget(sbPtr)) + { + alienStatusPointer->BehaviourState = ABS_Hunt; + } + else + { + alienStatusPointer->BehaviourState = ABS_Wander; + } + } + + NPC_InitWanderData(&(alienStatusPointer->wanderData)); + /* init state timers */ + alienStatusPointer->NearStateTimer = 0; + alienStatusPointer->FarStateTimer = ALIEN_FAR_MOVE_TIME; + + /* zero linear velocity in dynamics block */ + LOCALASSERT(sbPtr->DynPtr); + sbPtr->DynPtr->LinVelocity.vx = 0; + sbPtr->DynPtr->LinVelocity.vy = 0; + sbPtr->DynPtr->LinVelocity.vz = 0; +} + + +static void DoAlienAIHiss(STRATEGYBLOCK *sbPtr) { + + DYNAMICSBLOCK *dynPtr; + int pitch = (FastRandom() & 255) - 128; + SOUNDINDEX soundIndex; + ALIEN_STATUS_BLOCK *alienStatusPointer; + + GLOBALASSERT(sbPtr); + alienStatusPointer = (ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr); + LOCALASSERT(alienStatusPointer); + dynPtr = sbPtr->DynPtr; + LOCALASSERT(dynPtr); + GLOBALASSERT(alienStatusPointer->soundHandle2==SOUND_NOACTIVEINDEX); + + /* This one is for ALIEN DAMAGE SCREAM. */ + + soundIndex=SID_NOSOUND; + + if (alienStatusPointer->soundHandle2==SOUND_NOACTIVEINDEX) { + PlayAlienSound((int)alienStatusPointer->Type,ASC_Scream_Hurt,pitch, + &alienStatusPointer->soundHandle2,&sbPtr->DynPtr->Position); + + if (AvP.Network != I_No_Network) + { + AddNetMsg_SpotAlienSound(ASC_Scream_Hurt,(int)alienStatusPointer->Type,pitch,&dynPtr->Position); + } + + } + + +} + +static void DoAlienDeathScream(STRATEGYBLOCK *sbPtr) { + + DYNAMICSBLOCK *dynPtr; + SOUNDINDEX soundIndex; + ALIEN_STATUS_BLOCK *alienStatusPointer; + + GLOBALASSERT(sbPtr); + alienStatusPointer = (ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr); + LOCALASSERT(alienStatusPointer); + dynPtr = sbPtr->DynPtr; + LOCALASSERT(dynPtr); + GLOBALASSERT(alienStatusPointer->soundHandle2==SOUND_NOACTIVEINDEX); + + soundIndex=SID_NOSOUND; + + /* This one is for ALIEN DEATH SCREAM. */ + + soundIndex=SID_NOSOUND; + + if (alienStatusPointer->soundHandle2==SOUND_NOACTIVEINDEX) { + PlayAlienSound((int)alienStatusPointer->Type,ASC_Scream_Dying,0, + &alienStatusPointer->soundHandle2,&sbPtr->DynPtr->Position); + + if (AvP.Network != I_No_Network) + { + AddNetMsg_SpotAlienSound((int)ASC_Scream_Dying,(int)alienStatusPointer->Type,0,&dynPtr->Position); + } + } + + +} + +static void DoAlienDeathSound(STRATEGYBLOCK *sbPtr) { + + DYNAMICSBLOCK *dynPtr; + SOUNDINDEX soundIndex; + ALIEN_STATUS_BLOCK *alienStatusPointer; + + GLOBALASSERT(sbPtr); + alienStatusPointer = (ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr); + LOCALASSERT(alienStatusPointer); + dynPtr = sbPtr->DynPtr; + LOCALASSERT(dynPtr); + + soundIndex=SID_NOSOUND; + + /* This one is for ALIEN DEATH SOUND. */ + + soundIndex=SID_NOSOUND; + + PlayAlienSound((int)alienStatusPointer->Type,ASC_Death,0, + NULL,&sbPtr->DynPtr->Position); + + #if 0 + if (AvP.Network != I_No_Network) + { + soundIndex=ActiveSounds[alienStatusPointer->soundHandle2].soundIndex; + if (soundIndex!=SID_NOSOUND) { + AddNetMsg_SpotAlienSound(soundIndex,pitch,&dynPtr->Position); + } + } + #endif + +} + +extern void DoAlienLimbLossSound(VECTORCH *position) { + + /* This one is for ALIEN LIMBLOSS SOUND. */ + + PlayAlienSound(0,ASC_LimbLoss,0,NULL,position); + +} + +/*----------------------Patrick 7/11/96----------------------------- +Handle weapon impact on an alien +--------------------------------------------------------------------*/ +/* KJL 11:46:43 12/17/96 - rewritten */ + +// JB 16/6/97 rewritten +// Patrick 29/7/97 rewritten again. again. +// ChrisF 16/9/97 rewritten again again, again. +// ChrisF 26/11/97 rewritten again, again, again, again. +// ChrisF 1/4/98 rewritten again again again again again. Okay, 'modified' then. Added directional parameter. +// ChrisF 20/11/98 rewritten again again again again again again, added hit delta support. +// ChrisF 15/2/99 rewritten again again again again again again again, added fragging noise. + +void AlienIsDamaged(STRATEGYBLOCK *sbPtr, DAMAGE_PROFILE *damage, int multiple, int wounds,SECTION_DATA *Section,VECTORCH *incoming, DISPLAYBLOCK *frag) +{ + + int tkd; + ALIEN_STATUS_BLOCK *alienStatusPointer; + GLOBALASSERT(sbPtr); + + alienStatusPointer = (ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr); + LOCALASSERT(alienStatusPointer); + + LOCALASSERT(sbPtr); + + alienStatusPointer->Wounds|=wounds; + + if (incoming) { + textprint("Alien hit from %d %d %d\n",incoming->vx,incoming->vy,incoming->vz); + /* Knockback effects? */ + } + + if (frag) { + DoAlienLimbLossSound(&sbPtr->DynPtr->Position); + } + +#if 0 + ALIEN_STATUS_BLOCK *alienStatusPointer; + GLOBALASSERT(sbPtr); + + /* reduce alien health */ + alienStatusPointer = (ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr); + LOCALASSERT(alienStatusPointer); + if(alienStatusPointer->Health>0) (alienStatusPointer->Health) -= damage; +#endif + /* Perhaps I'd better explain... I now handle damage + for you. These functions now are only for special effects, + and if you need to do anything when damage happens. */ + + if (alienStatusPointer->BehaviourState==ABS_Dormant) { + Alien_Awaken(sbPtr); + } + + /* Speaking of which... */ + { + + /* reduce alien health */ + + if (sbPtr->SBDamageBlock.Health <= 0) { + + /* Oh yes, kill them, too. */ + if (alienStatusPointer->BehaviourState!=ABS_Dying) + { + if (AvP.PlayerType!=I_Alien) { + CurrentGameStats_CreatureKilled(sbPtr,Section); + } + KillAlien(sbPtr,wounds,damage,multiple,incoming); + } + + } else { + #if WOUNDING_SPEED_EFFECTS + /* Alien wounding effects? */ + int factor; + int changespeed; + + RecomputeAlienSpeed(sbPtr); + + /* Now, get anim speed. */ + factor=GetAlienSpeedFactor(sbPtr); + changespeed=0; + + switch (alienStatusPointer->Type) { + case AT_Standard: + if (factor!=ONE_FIXED) { + changespeed=1; + } + break; + case AT_Predalien: + if (factor!=PREDALIEN_SPEED_FACTOR) { + changespeed=1; + } + break; + case AT_Praetorian: + if (factor!=PRAETORIAN_SPEED_FACTOR) { + changespeed=1; + } + break; + default: + GLOBALASSERT(0); + break; + } + + if (changespeed) { + int newspeed; + /* ONE_FIXED implies we're playing an invariant length anim. */ + newspeed=DIV_FIXED(alienStatusPointer->last_anim_length,factor); + + HModel_ChangeSpeed(&alienStatusPointer->HModelController,newspeed); + } + #endif + + /* If you got here, you should still be alive. */ + + if ((alienStatusPointer->BehaviourState!=ABS_Dying)&&(alienStatusPointer->soundHandle2==SOUND_NOACTIVEINDEX)) { + DoAlienAIHiss(sbPtr); + } + + tkd=TotalKineticDamage(damage); + + if (tkd>=10) { + /* If not dead, play a hit delta. */ + DELTA_CONTROLLER *hitdelta; + int frontback; + + hitdelta=Get_Delta_Sequence(&alienStatusPointer->HModelController,"HitDelta"); + + if (incoming) { + if (incoming->vz>=0) { + frontback=0; + } else { + frontback=1; + } + } else { + /* Default to front. */ + frontback=1; + } + + /* Only do it from the front. */ + if ((hitdelta)&&(frontback)) { + if (hitdelta->Playing==0) { + /* A hierarchy with hit deltas! */ + int CrouchSubSequence; + int StandSubSequence; + + if (Section==NULL) { + if ((FastRandom()&65535)<32767) { + CrouchSubSequence=ACrSS_Hit_Left; + StandSubSequence=ASSS_Hit_Left; + } else { + CrouchSubSequence=ACrSS_Hit_Right; + StandSubSequence=ASSS_Hit_Right; + } + } else if ((Section->sempai->flags§ion_flag_left_arm) + ||(Section->sempai->flags§ion_flag_left_hand)) { + + CrouchSubSequence=ACrSS_Hit_Left; + StandSubSequence=ASSS_Hit_Left; + } else if ((Section->sempai->flags§ion_flag_right_arm) + ||(Section->sempai->flags§ion_flag_right_hand)) { + + CrouchSubSequence=ACrSS_Hit_Right; + StandSubSequence=ASSS_Hit_Right; + + } else { + /* Chest or misc. hit. */ + if ((FastRandom()&65535)<32767) { + CrouchSubSequence=ACrSS_Hit_Left; + StandSubSequence=ASSS_Hit_Left; + } else { + CrouchSubSequence=ACrSS_Hit_Right; + StandSubSequence=ASSS_Hit_Right; + } + } + + + if(alienStatusPointer->IAmCrouched) { + if (HModelSequence_Exists(&alienStatusPointer->HModelController,(int)HMSQT_AlienCrouch,CrouchSubSequence)) { + Start_Delta_Sequence(hitdelta,(int)HMSQT_AlienCrouch,CrouchSubSequence,-1); + } + } else { + if (HModelSequence_Exists(&alienStatusPointer->HModelController,(int)HMSQT_AlienStand,StandSubSequence)) { + Start_Delta_Sequence(hitdelta,(int)HMSQT_AlienStand,StandSubSequence,-1); + } + } + hitdelta->Playing=1; + /* Not looped. */ + if (alienStatusPointer->BehaviourState==ABS_Attack) { + StartAlienAttackSequence(sbPtr); + alienStatusPointer->NearStateTimer = ALIEN_ATTACKTIME; + } + } + } + } + } + } + +} + +/* patrick 29/7/97 ----------------------------------- +This function to be called only from behaviour +-- ChrisF 1/4/98 What a stupid idea. This function --- +-- to be called only from AlienIsDamaged. ------------ +------------------------------------------------------*/ +void KillAlien(STRATEGYBLOCK *sbPtr,int wounds,DAMAGE_PROFILE *damage, int multiple,VECTORCH *incoming) +{ + ALIEN_STATUS_BLOCK *alienStatusPointer; + int tkd,deathtype; + SECTION_DATA *head; + + LOCALASSERT(sbPtr); + LOCALASSERT(sbPtr->DynPtr); + + + alienStatusPointer=(ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr); + + /* make a sound. Just this once without a check. */ + + DoAlienDeathSound(sbPtr); + + /*If alien has a death target ,send a request*/ + if(alienStatusPointer->death_target_sbptr) + { + RequestState(alienStatusPointer->death_target_sbptr,1, 0); + } + + /* Set up gibb factor. */ + + tkd=TotalKineticDamage(damage); + deathtype=0; + + if (damage->ExplosivePower==1) { + /* Explosion case. */ + if (MUL_FIXED(tkd,(multiple&((ONE_FIXED<<1)-1)))>20) { + /* Okay, you can gibb now. */ + alienStatusPointer->GibbFactor=ONE_FIXED>>1; + deathtype=2; + } + } else if ((tkd<40)&&((multiple>>16)>1)) { + int newmult; + + newmult=DIV_FIXED(multiple,NormalFrameTime); + if (MUL_FIXED(tkd,newmult)>700) { + /* Excessive bullets case 1. */ + alienStatusPointer->GibbFactor=ONE_FIXED>>2; + deathtype=2; + } else if (MUL_FIXED(tkd,newmult)>250) { + /* Excessive bullets case 2. */ + alienStatusPointer->GibbFactor=ONE_FIXED>>3; + deathtype=1; + } + } + + /* Predaliens and preatorians only gibb for sadars. */ + if (alienStatusPointer->Type!=AT_Standard) { + alienStatusPointer->GibbFactor=0; + /* But retain deathtype. */ + } + + if ((damage->ExplosivePower==2)||(damage->ExplosivePower==6)) { + /* Basically SADARS only. */ + if (alienStatusPointer->Type==AT_Standard) { + alienStatusPointer->GibbFactor=ONE_FIXED; + } else { + alienStatusPointer->GibbFactor=ONE_FIXED>>2; + } + deathtype=3; + } + + if (damage->ForceBoom) { + deathtype+=damage->ForceBoom; + } + + if ( (damage->Impact==0) + &&(damage->Cutting==0) + &&(damage->Penetrative==0) + &&(damage->Fire!=0) + &&(damage->Electrical==0) + &&(damage->Acid==0)) + { + /* that sounds like the flamethrower... */ + alienStatusPointer->GibbFactor=ONE_FIXED>>2; + /* Gibb just a little for now. */ + } + + if (damage->Id==AMMO_PREDPISTOL_STRIKE) { + /* Blow up if hit by the bolt. */ + alienStatusPointer->GibbFactor=ONE_FIXED>>3; + } else if (damage->Id==AMMO_FLECHETTE_POSTMAX) { + alienStatusPointer->GibbFactor=ONE_FIXED>>2; + } + + { + SECTION_DATA *chest=GetThisSectionData(alienStatusPointer->HModelController.section_data,"chest"); + + if (chest==NULL) { + /* I'm impressed. */ + deathtype+=2; + } else if ((chest->flags§ion_data_notreal) + &&(chest->flags§ion_data_terminate_here)) { + /* That's gotta hurt. */ + deathtype++; + } + } + + /* Gibb noise? */ + if (alienStatusPointer->GibbFactor) { + DoAlienLimbLossSound(&sbPtr->DynPtr->Position); + } else { + /* make a sound... if you have a head. */ + head=GetThisSectionData(alienStatusPointer->HModelController.section_data,"head"); + + /* Is it still attached? */ + if (head) { + if (head->flags§ion_data_notreal) { + head=NULL; + } + } + + if ((alienStatusPointer->soundHandle2==SOUND_NOACTIVEINDEX)&&(head)) { + DoAlienDeathScream(sbPtr); + } + } + + /* More restrained death than before... */ + { + + + DEATH_DATA *this_death; + HIT_FACING facing; + int electrical; + + facing.Front=0; + facing.Back=0; + facing.Left=0; + facing.Right=0; + + if (incoming) { + if (incoming->vz>0) { + facing.Back=1; + } else { + facing.Front=1; + } + if (incoming->vx>0) { + facing.Right=1; + } else { + facing.Left=1; + } + } + + if ((damage->Impact==0) + &&(damage->Cutting==0) + &&(damage->Penetrative==0) + &&(damage->Fire==0) + &&(damage->Electrical>0) + &&(damage->Acid==0) + ) { + electrical=1; + } else { + electrical=0; + } + + this_death=GetAlienDeathSequence(&alienStatusPointer->HModelController,NULL,alienStatusPointer->Wounds,alienStatusPointer->Wounds, + deathtype,&facing,0,alienStatusPointer->IAmCrouched,electrical); + + #if 0 + GLOBALASSERT(this_death); + + SetAlienShapeAnimSequence_Core(sbPtr,this_death->Sequence_Type,this_death->Sub_Sequence, + this_death->Sequence_Length,this_death->TweeningTime); + + alienStatusPointer->NearStateTimer=ALIEN_DYINGTIME; + alienStatusPointer->HModelController.Looped=0; + alienStatusPointer->HModelController.LoopAfterTweening=0; + /* switch state */ + alienStatusPointer->BehaviourState=ABS_Dying; + + /* stop sound, if we have one */ + if(alienStatusPointer->soundHandle!=SOUND_NOACTIVEINDEX) Sound_Stop(alienStatusPointer->soundHandle); + + /* stop motion */ + LOCALASSERT(sbPtr->DynPtr); + sbPtr->DynPtr->Friction = 400000; + sbPtr->DynPtr->LinImpulse.vx+=sbPtr->DynPtr->LinVelocity.vx; + sbPtr->DynPtr->LinImpulse.vy+=sbPtr->DynPtr->LinVelocity.vy; + sbPtr->DynPtr->LinImpulse.vz+=sbPtr->DynPtr->LinVelocity.vz; + sbPtr->DynPtr->LinVelocity.vx = sbPtr->DynPtr->LinVelocity.vy = sbPtr->DynPtr->LinVelocity.vz = 0; + sbPtr->DynPtr->IgnoreSameObjectsAsYou = 1; + /* Experiment... */ + sbPtr->DynPtr->UseStandardGravity=1; + sbPtr->DynPtr->Mass = 160; + /* Okay... */ + #else + Convert_Alien_To_Corpse(sbPtr,this_death,damage); + #endif + } +} + +void Execute_Alien_Dying(STRATEGYBLOCK *sbPtr) +{ + ALIEN_STATUS_BLOCK *alienStatusPointer; + + LOCALASSERT(sbPtr); + alienStatusPointer = (ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr); + LOCALASSERT(alienStatusPointer); + + //sbPtr->DynPtr->LinVelocity.vx = 0; + //sbPtr->DynPtr->LinVelocity.vy = 0; + //sbPtr->DynPtr->LinVelocity.vz = 0; + + //sbPtr->DynPtr->LinImpulse.vx = 0; + //sbPtr->DynPtr->LinImpulse.vy = 0; + //sbPtr->DynPtr->LinImpulse.vz = 0; + + { + DISPLAYBLOCK *dispPtr = sbPtr->SBdptr; + /* do we have a displayblock? */ + if (dispPtr) + { + dispPtr->SpecialFXFlags |= SFXFLAG_MELTINGINTOGROUND; + dispPtr->ObFlags2 = alienStatusPointer->NearStateTimer/2; + + } + } + alienStatusPointer->NearStateTimer -= NormalFrameTime; +} + +void RecomputeAlienSpeed(STRATEGYBLOCK *sbPtr) { + + ALIEN_STATUS_BLOCK *alienStatusPointer; + int factor,basespeed; + + LOCALASSERT(sbPtr); + alienStatusPointer = (ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr); + LOCALASSERT(alienStatusPointer); + + /* Change max speed. */ + factor=GetAlienSpeedFactor_ForSequence(sbPtr,HMSQT_AlienRun,(int)ARSS_Standard); + + switch (alienStatusPointer->Type) { + case AT_Standard: + default: + basespeed=ALIEN_FORWARDVELOCITY; + break; + case AT_Predalien: + basespeed=MUL_FIXED(ALIEN_FORWARDVELOCITY,PREDALIEN_SPEED_FACTOR); + break; + case AT_Praetorian: + /* Could this be dependent on crouching? */ + if (alienStatusPointer->IAmCrouched) { + basespeed=MUL_FIXED(ALIEN_FORWARDVELOCITY,MUL_FIXED(PRAETORIAN_CRAWLSPEED_FACTOR,PRAETORIAN_SPEED_FACTOR)); + } else { + basespeed=MUL_FIXED(ALIEN_FORWARDVELOCITY,MUL_FIXED(PRAETORIAN_WALKSPEED_FACTOR,PRAETORIAN_SPEED_FACTOR)); + } + break; + } + + alienStatusPointer->MaxSpeed=MUL_FIXED(factor,basespeed); + +} + +/* Wounding effect function... */ + +int GetAlienSpeedFactor(STRATEGYBLOCK *sbPtr) { + + ALIEN_STATUS_BLOCK *alienStatusPointer; + HMODEL_SEQUENCE_TYPES sequence_type; + int subsequence; + + LOCALASSERT(sbPtr); + alienStatusPointer = (ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr); + LOCALASSERT(alienStatusPointer); + + sequence_type=(HMODEL_SEQUENCE_TYPES)alienStatusPointer->HModelController.Sequence_Type; + subsequence=alienStatusPointer->HModelController.Sub_Sequence; + /* That is what the controller thinks the shape is playing. */ + + return(GetAlienSpeedFactor_ForSequence(sbPtr,sequence_type,subsequence)); + +} + +int GetAlienSpeedFactor_ForSequence(STRATEGYBLOCK *sbPtr, HMODEL_SEQUENCE_TYPES sequence_type,int subsequence) { + + ALIEN_STATUS_BLOCK *alienStatusPointer; + int factor,factortype; + + LOCALASSERT(sbPtr); + alienStatusPointer = (ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr); + LOCALASSERT(alienStatusPointer); + + factortype=-1; + + switch (sequence_type) { + + case HMSQT_AlienRun: + switch ((ALIENRUN_SUBSEQUENCES)subsequence) { + case ARSS_Standard: + factortype=2; + break; + case ARSS_Attack_Swipe: + case ARSS_Jump: + factortype=1; + break; + case ARSS_Dies: + factortype=0; + break; + default: + GLOBALASSERT(0); + break; + } + break; + case HMSQT_AlienCrawl: + switch ((ALIENCRAWL_SUBSEQUENCES)subsequence) { + case ACSS_Standard: + case ACSS_Crawl_Hurt: + case ACSS_Scamper: + factortype=2; + break; + case ACSS_Attack_Bite: + case ACSS_Attack_Tail: + factortype=1; + break; + case ACSS_Dies: + case ACSS_Pain_Fall_Fwd: + case ACSS_Pain_Fall_Back: + case ACSS_Pain_Fall_Left: + case ACSS_Pain_Fall_Right: + case ACSS_Boom_Fall_Fwd: + case ACSS_Boom_Fall_Back: + case ACSS_Boom_Fall_Left: + case ACSS_Boom_Fall_Right: + factortype=0; + break; + default: + GLOBALASSERT(0); + break; + } + break; + case HMSQT_AlienStand: + switch ((ALIENSTAND_SUBSEQUENCES)subsequence) { + case ASSS_Taunt: + case ASSS_Taunt2: + case ASSS_Taunt3: + case ASSS_Fear: + case ASSS_Standard: + case ASSS_FidgetA: + case ASSS_FidgetB: + case ASSS_Attack_Right_Swipe_In: + case ASSS_Attack_Left_Swipe_In: + case ASSS_Attack_Both_In: + case ASSS_Attack_Both_Down: + case ASSS_Attack_Bite: + case ASSS_Attack_Tail: + case ASSS_Attack_Low_Left_Swipe: + case ASSS_Attack_Low_Right_Swipe: + case ASSS_Feed: + case ASSS_Unfurl: + case ASSS_Dormant: + factortype=1; + break; + case ASSS_Dies: + case ASSS_Pain_Fall_Fwd: + case ASSS_Pain_Fall_Back: + case ASSS_Pain_Fall_Left: + case ASSS_Pain_Fall_Right: + case ASSS_Boom_Fall_Fwd: + case ASSS_Boom_Fall_Back: + case ASSS_Boom_Fall_Left: + case ASSS_Boom_Fall_Right: + case ASSS_Spin_Clockwise: + case ASSS_Spin_Anticlockwise: + case ASSS_BurningDeath: + factortype=0; + break; + default: + GLOBALASSERT(0); + break; + } + break; + case HMSQT_AlienCrouch: + switch ((ALIENCROUCH_SUBSEQUENCES)subsequence) { + case ACrSS_Standard: + factortype=2; + break; + case ACrSS_Attack_Bite: + case ACrSS_Attack_Tail: + case ACrSS_Attack_Swipe: + case ACrSS_Pounce: + case ACrSS_Taunt: + factortype=1; + break; + case ACrSS_Dies: + case ACrSS_Dies_Thrash: + factortype=0; + break; + default: + GLOBALASSERT(0); + break; + } + break; + case HMSQT_Hugger: + default: + /* Nooooo! */ + GLOBALASSERT(0); + break; + } + + LOCALASSERT(factortype!=-1); + + switch (factortype) { + case 0: + return(ONE_FIXED); + break; + case 1: + { + /* Affected by alien type. */ + int prefactor; + + switch (alienStatusPointer->Type) { + case AT_Standard: + prefactor=ONE_FIXED; + break; + case AT_Predalien: + prefactor=PREDALIEN_SPEED_FACTOR; + break; + case AT_Praetorian: + prefactor=PRAETORIAN_SPEED_FACTOR; + break; + default: + GLOBALASSERT(0); + break; + } + + return(prefactor); + + } + break; + case 2: + { + /* More complex. Affected by alien type and health. */ + NPC_DATA *NpcData; + int prefactor; + + switch (alienStatusPointer->Type) { + case AT_Standard: + NpcData=GetThisNpcData(I_NPC_Alien); + prefactor=ONE_FIXED; + break; + case AT_Predalien: + NpcData=GetThisNpcData(I_NPC_PredatorAlien); + prefactor=PREDALIEN_SPEED_FACTOR; + break; + case AT_Praetorian: + NpcData=GetThisNpcData(I_NPC_PraetorianGuard); + prefactor=PRAETORIAN_SPEED_FACTOR; + break; + default: + GLOBALASSERT(0); + break; + } + + LOCALASSERT(NpcData); + + factor=sbPtr->SBDamageBlock.Health/NpcData->StartingStats.Health; + /* ONE_FIXED shift already included. */ + LOCALASSERT(factor<=ONE_FIXED); + + factor+=ONE_FIXED; + factor>>=1; + + /* Wounding effects. */ + if (alienStatusPointer->Wounds&(section_flag_left_leg|section_flag_right_leg)) { + /* Missing a leg. */ + factor-=(ONE_FIXED/3); + } else if ((alienStatusPointer->Wounds§ion_flag_left_foot) + &&(alienStatusPointer->Wounds§ion_flag_right_foot)) { + /* Missing both feet. */ + factor-=(ONE_FIXED>>2); + } else if ((alienStatusPointer->Wounds§ion_flag_left_foot) + ||(alienStatusPointer->Wounds§ion_flag_right_foot)) { + /* Missing one foot. */ + factor-=(ONE_FIXED>>3); + } + + if (factor<(ONE_FIXED>>3)) { + factor=(ONE_FIXED>>3); + } + + if (prefactor!=ONE_FIXED) { + factor=MUL_FIXED(prefactor,factor); + } + + return(factor); + } + break; + default: + GLOBALASSERT(0); + break; + } + + return(0); + +} + +int Alien_TargetFilter(STRATEGYBLOCK *candidate) { + + switch (candidate->I_SBtype) { + case I_BehaviourAlienPlayer: + case I_BehaviourMarinePlayer: + case I_BehaviourPredatorPlayer: + { + if (Observer) { + return(0); + } + + if(AvP.Network != I_No_Network) + { + //In multiplayer games we don't want the aliens to be going after + //the host once he's dead. + PLAYER_STATUS *playerStatusPtr= (PLAYER_STATUS *) (candidate->SBdataptr); + if(!playerStatusPtr->IsAlive) + { + return(0); + } + } + + switch(AvP.PlayerType) + { + case I_Marine: + case I_Predator: + return(1); + break; + case I_Alien: + return(0); + break; + default: + GLOBALASSERT(0); + return(0); + break; + } + break; + } + case I_BehaviourDummy: + { + DUMMY_STATUS_BLOCK *dummyStatusPointer; + dummyStatusPointer = (DUMMY_STATUS_BLOCK *)(candidate->SBdataptr); + LOCALASSERT(dummyStatusPointer); + switch (dummyStatusPointer->PlayerType) { + case I_Marine: + case I_Predator: + return(1); + break; + case I_Alien: + return(0); + break; + default: + GLOBALASSERT(0); + return(0); + break; + } + break; + } + case I_BehaviourQueenAlien: + case I_BehaviourFaceHugger: + case I_BehaviourAlien: + { + return(0); + break; + } + case I_BehaviourPredator: + case I_BehaviourXenoborg: + case I_BehaviourMarine: + case I_BehaviourSeal: + case I_BehaviourPredatorAlien: + /* Valid. */ + return(1); + break; + #if SupportWindows95 + case I_BehaviourNetGhost: + { + NETGHOSTDATABLOCK *dataptr; + dataptr=candidate->SBdataptr; + switch (dataptr->type) { + case I_BehaviourMarinePlayer: + case I_BehaviourPredatorPlayer: + return(1); + //return(0); + break; + case I_BehaviourAlienPlayer: + default: + return(0); + break; + } + } + break; + #endif + default: + return(0); + break; + } + +} + +STRATEGYBLOCK *Alien_GetNewTarget(VECTORCH *alienpos, STRATEGYBLOCK *me) { + + int neardist; + STRATEGYBLOCK *nearest; + int a; + STRATEGYBLOCK *candidate; + MODULE *dmod; + + dmod=ModuleFromPosition(alienpos,playerPherModule); + + LOCALASSERT(dmod); + + nearest=NULL; + neardist=ONE_FIXED; + + for (a=0; a<NumActiveStBlocks; a++) { + candidate=ActiveStBlockList[a]; + if (candidate!=me) { + if (candidate->DynPtr) { + if (Alien_TargetFilter(candidate)) { + VECTORCH offset; + int dist; + + offset.vx=alienpos->vx-candidate->DynPtr->Position.vx; + offset.vy=alienpos->vy-candidate->DynPtr->Position.vy; + offset.vz=alienpos->vz-candidate->DynPtr->Position.vz; + + dist=Approximate3dMagnitude(&offset); + /* Preferentially ignore predators? */ + if (candidate->I_SBtype==I_BehaviourPredator) { + dist<<=2; + } + + if (dist<neardist) { + /* Check visibility? */ + //if (candidate->SBdptr) { + if (!NPC_IsDead(candidate)) { + if ((IsModuleVisibleFromModule(dmod,candidate->containingModule))) { + nearest=candidate; + neardist=dist; + } + } + //} + } + } + } + } + } + + #if 0 + if (nearest==NULL) { + if (Alien_TargetFilter(Player->ObStrategyBlock)) { + nearest=Player->ObStrategyBlock; + } else { + nearest=NULL; /* Erk! */ + } + } + #endif + + return(nearest); + +} + +void Alien_Awaken(STRATEGYBLOCK *sbPtr) { + + ALIEN_STATUS_BLOCK *alienStatusPointer; + + LOCALASSERT(sbPtr); + alienStatusPointer = (ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr); + LOCALASSERT(alienStatusPointer); + + alienStatusPointer->BehaviourState = ABS_Awakening; + + if(HModelSequence_Exists(&alienStatusPointer->HModelController,HMSQT_AlienStand,ASSS_Unfurl)) { + SetAlienShapeAnimSequence_Core(sbPtr,HMSQT_AlienStand,ASSS_Unfurl,-1,(ONE_FIXED>>2)); + } else { + SetAlienShapeAnimSequence_Core(sbPtr,HMSQT_AlienStand,ASSS_Standard,(ONE_FIXED),(ONE_FIXED>>2)); + } + alienStatusPointer->HModelController.LoopAfterTweening=0; + +} + + +void Alien_GoToApproach(STRATEGYBLOCK *sbPtr) { + + ALIEN_STATUS_BLOCK *alienStatusPointer; + + LOCALASSERT(sbPtr); + alienStatusPointer = (ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr); + LOCALASSERT(alienStatusPointer); + + NPC_InitMovementData(&(alienStatusPointer->moveData)); + alienStatusPointer->BehaviourState = ABS_Approach; + InitWaypointManager(&alienStatusPointer->waypointManager); + if(alienStatusPointer->IAmCrouched) { + SetAlienShapeAnimSequence(sbPtr,HMSQT_AlienCrawl,(int)ACSS_Standard,ONE_FIXED>>1); + } else { + SetAlienShapeAnimSequence(sbPtr,HMSQT_AlienRun,(int)ARSS_Standard,ONE_FIXED>>1); + } + RecomputeAlienSpeed(sbPtr); + alienStatusPointer->CurveTimeOut = 0; + alienStatusPointer->NearStateTimer = 0; + +} + +int AlienIsCrawling(STRATEGYBLOCK *sbPtr) { + + /* For external calls. */ + + DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr; + LOCALASSERT(dynPtr); + + if (sbPtr->I_SBtype!=I_BehaviourAlien) { + return(0); + } + + if (dynPtr->UseStandardGravity!=0) { + return(0); + } else { + return(1); + } +} + + + +/*--------------------** +** Loading and Saving ** +**--------------------*/ +#include "savegame.h" +typedef struct alien_save_block +{ + SAVE_BLOCK_STRATEGY_HEADER header; + + ALIEN_TYPE Type; + signed char Health; + int GibbFactor; + int Wounds; + int last_anim_length; + ALIEN_BHSTATE BehaviourState; + int PounceDetected; + int JumpDetected; + int EnablePounce; + + + int incidentFlag; + int incidentTimer; + + ALIEN_MISSION Mission; + + int CurveRadius; + int CurveLength; + int CurveTimeOut; + int FarStateTimer; + int NearStateTimer; + int IAmCrouched; + + + int huntingModuleIndex; + int currentAttackCode; +// NPC_MOVEMENTDATA moveData; + NPC_WANDERDATA wanderData; + +// HMODELCONTROLLER HModelController; + +// STRATEGYBLOCK* generator_sbptr;//0 unless created by a generator + + char Target_SBname[SB_NAME_LENGTH]; + + char Generator_SBname[SB_NAME_LENGTH]; + + //strategyblock stuff + int integrity; + DAMAGEBLOCK SBDamageBlock; + DYNAMICSBLOCK dynamics; +}ALIEN_SAVE_BLOCK; + + + +//defines for load/save macros +#define SAVELOAD_BLOCK block +#define SAVELOAD_BEHAV alienStatusPointer + + +void LoadStrategy_Alien(SAVE_BLOCK_STRATEGY_HEADER* header) +{ + STRATEGYBLOCK* sbPtr; + ALIEN_STATUS_BLOCK* alienStatusPointer; + ALIEN_SAVE_BLOCK* block = (ALIEN_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) + { + //make sure the strategy found is of the right type + if(sbPtr->I_SBtype != I_BehaviourAlien) return; + } + else + { + //we will have to generate an alien then + TOOLS_DATA_ALIEN tda; + + //make sure the alien is in a module + if(!ModuleFromPosition(&block->dynamics.Position,NULL)) return; + + sbPtr = CreateActiveStrategyBlock(); + if(!sbPtr) return; + + sbPtr->I_SBtype = I_BehaviourAlien; + sbPtr->shapeIndex = 0; + sbPtr->maintainVisibility = 1; + COPY_NAME(sbPtr->SBname,block->header.SBname); + + //create using a fake tools data + tda.position = block->dynamics.Position; + tda.shapeIndex = 0; + COPY_NAME(tda.nameID,block->header.SBname); + COPY_NAME(tda.death_target_ID,Null_Name); + tda.type = block->Type; + tda.start_inactive = 0; + + tda.starteuler.EulerX = 0; + tda.starteuler.EulerY = 0; + tda.starteuler.EulerZ = 0; + + EnableBehaviourType(sbPtr,I_BehaviourAlien , &tda ); + + } + + + alienStatusPointer = (ALIEN_STATUS_BLOCK*)sbPtr->SBdataptr; + + //start copying stuff + + COPYELEMENT_LOAD(Type) + COPYELEMENT_LOAD(Health) + COPYELEMENT_LOAD(GibbFactor) + COPYELEMENT_LOAD(Wounds) + COPYELEMENT_LOAD(last_anim_length) + COPYELEMENT_LOAD(BehaviourState) + COPYELEMENT_LOAD(PounceDetected) + COPYELEMENT_LOAD(JumpDetected) + COPYELEMENT_LOAD(EnablePounce) + COPYELEMENT_LOAD(incidentFlag) + COPYELEMENT_LOAD(incidentTimer) + COPYELEMENT_LOAD(Mission) + COPYELEMENT_LOAD(CurveRadius) + COPYELEMENT_LOAD(CurveLength) + COPYELEMENT_LOAD(CurveTimeOut) + COPYELEMENT_LOAD(FarStateTimer) + COPYELEMENT_LOAD(NearStateTimer) + COPYELEMENT_LOAD(IAmCrouched) + COPYELEMENT_LOAD(wanderData) + + //load target + COPY_NAME(alienStatusPointer->Target_SBname,block->Target_SBname); + alienStatusPointer->Target = FindSBWithName(alienStatusPointer->Target_SBname); + + //load hunting module + if(block->huntingModuleIndex>=0 && block->huntingModuleIndex< AIModuleArraySize) + { + alienStatusPointer->huntingModule = &AIModuleArray[block->huntingModuleIndex]; + } + else + { + alienStatusPointer->huntingModule = NULL; + } + + //get the alien's attack from the attack code + alienStatusPointer->current_attack = GetThisAttack_FromUniqueCode(block->currentAttackCode); + + *sbPtr->DynPtr = block->dynamics; + sbPtr->integrity = block->integrity; + sbPtr->SBDamageBlock = block->SBDamageBlock; + + //find the alien's generator + alienStatusPointer->generator_sbptr = FindSBWithName(block->Generator_SBname); + + //load the aliens hierarchy + { + SAVE_BLOCK_HEADER* hier_header = GetNextBlockIfOfType(SaveBlock_Hierarchy); + if(hier_header) + { + LoadHierarchy(hier_header,&alienStatusPointer->HModelController); + } + } + + Load_SoundState(&alienStatusPointer->soundHandle); + Load_SoundState(&alienStatusPointer->soundHandle2); +} + + +void SaveStrategy_Alien(STRATEGYBLOCK* sbPtr) +{ + ALIEN_SAVE_BLOCK *block; + ALIEN_STATUS_BLOCK* alienStatusPointer; + + + alienStatusPointer = (ALIEN_STATUS_BLOCK*)sbPtr->SBdataptr; + + GET_STRATEGY_SAVE_BLOCK(block,sbPtr); + + //start copying stuff + + COPYELEMENT_SAVE(Type) + COPYELEMENT_SAVE(Health) + COPYELEMENT_SAVE(GibbFactor) + COPYELEMENT_SAVE(Wounds) + COPYELEMENT_SAVE(last_anim_length) + COPYELEMENT_SAVE(BehaviourState) + COPYELEMENT_SAVE(PounceDetected) + COPYELEMENT_SAVE(JumpDetected) + COPYELEMENT_SAVE(EnablePounce) + COPYELEMENT_SAVE(incidentFlag) + COPYELEMENT_SAVE(incidentTimer) + COPYELEMENT_SAVE(Mission) + COPYELEMENT_SAVE(CurveRadius) + COPYELEMENT_SAVE(CurveLength) + COPYELEMENT_SAVE(CurveTimeOut) + COPYELEMENT_SAVE(FarStateTimer) + COPYELEMENT_SAVE(NearStateTimer) + COPYELEMENT_SAVE(IAmCrouched) + COPYELEMENT_SAVE(wanderData) + + + //save target + COPY_NAME(block->Target_SBname,alienStatusPointer->Target_SBname); + + //save hunting module + if(alienStatusPointer->huntingModule) + { + block->huntingModuleIndex = alienStatusPointer->huntingModule->m_index; + } + else + { + block->huntingModuleIndex = -1; + } + + //save attack code + if(alienStatusPointer->current_attack) + { + block->currentAttackCode = alienStatusPointer->current_attack->Unique_Code; + } + else + { + block->currentAttackCode = -1; + } + + //save the alien's generator name + if(alienStatusPointer->generator_sbptr) + { + COPY_NAME(block->Generator_SBname,alienStatusPointer->generator_sbptr->SBname); + } + else + { + COPY_NAME(block->Generator_SBname,Null_Name); + } + + + block->dynamics = *sbPtr->DynPtr; + block->dynamics.CollisionReportPtr=0; + + block->integrity = sbPtr->integrity; + block->SBDamageBlock = sbPtr->SBDamageBlock; + + //save the aliens hierarchy + SaveHierarchy(&alienStatusPointer->HModelController); + + Save_SoundState(&alienStatusPointer->soundHandle); + Save_SoundState(&alienStatusPointer->soundHandle2); +} |
