avp/src/avp/bh_queen.c
2019-08-20 02:22:37 +02:00

5173 lines
139 KiB
C

#define DB_LEVEL 2
/*------------------------ChrisF 17/3/98-------------------------------
Source file for Queen AI behaviour functions. One year 30 days after bh_paq.
--------------------------------------------------------------------*/
#include "3dc.h"
#include "inline.h"
#include "module.h"
#include "stratdef.h"
#include "gamedef.h"
#include "bh_types.h"
#include "comp_shp.h"
#include "dynblock.h"
#include "dynamics.h"
#include "pfarlocs.h"
#include "pvisible.h"
#include "pheromon.h"
#include "bh_far.h"
#include "bh_pred.h"
#include "bh_paq.h"
#include "bh_queen.h"
#include "bh_debri.h"
#define UseLocalAssert Yes
#include "ourasert.h"
#include "psnd.h"
#include "weapons.h"
#include "extents.h"
#include "sequnces.h"
#include "showcmds.h"
#include "targeting.h"
#include "dxlog.h"
#include <math.h>
#include "db.h"
#include "los.h"
#include "bh_track.h"
#include "scream.h"
/* external global variables used in this file */
extern int ModuleArraySize;
extern char *ModuleCurrVisArray;
extern int NormalFrameTime;
extern SECTION * GetNamedHierarchyFromLibrary(const char * rif_name, const char * hier_name);
extern char LevelName[];
extern unsigned char Null_Name[8];
#define QueenAttackRange 3500
/*minimum time for flamethrower before queen takes notice*/
#define QueenMinimumFireTime ONE_FIXED/8
static BOOL PlayerInTrench=FALSE;
static BOOL PlayerInLocker=FALSE;
static int AirlockTimeOpen=0;
/*special hangar airlock state stuff*/
static BOOL UpperAirlockDoorOpen=FALSE;
static BOOL LowerAirlockDoorOpen=FALSE;
static STRATEGYBLOCK* UpperAirlockDoorSbptr=0;
static VECTORCH UpperAirlockDoorStart;
static STRATEGYBLOCK* LowerAirlockDoorSbptr=0;
static VECTORCH LowerAirlockDoorStart;
static STRATEGYBLOCK* LockerDoorSbptr=0;
#define QUEEN_MAX_OBJECT 10
int NumQueenObjects;
STRATEGYBLOCK* QueenObjectList[QUEEN_MAX_OBJECT];
void SetQueenShapeAnimSequence_Core(STRATEGYBLOCK *sbPtr,HMODEL_SEQUENCE_TYPES type, int subtype, int length, int tweeningtime);
void QueenMove_Standby(STRATEGYBLOCK *sbPtr);
void QueenMove_StepForward(STRATEGYBLOCK *sbPtr);
void QueenMove_StepBack(STRATEGYBLOCK *sbPtr);
void QueenMove_TurnLeft(STRATEGYBLOCK *sbPtr);
void QueenMove_TurnRight(STRATEGYBLOCK *sbPtr);
void QueenMove_Walk(STRATEGYBLOCK *sbPtr);
void QueenMove_Taunt(STRATEGYBLOCK *sbPtr);
void QueenMove_Hiss(STRATEGYBLOCK *sbPtr);
void QueenMove_LeftSwipe(STRATEGYBLOCK *sbPtr);
void QueenMove_RightSwipe(STRATEGYBLOCK *sbPtr);
void QueenMove_Charge(STRATEGYBLOCK *sbPtr);
void QueenMove_Close(STRATEGYBLOCK *sbPtr);
void Execute_Queen_Dying(STRATEGYBLOCK *sbPtr);
void KillQueen(STRATEGYBLOCK *sbPtr,DAMAGE_PROFILE *damage, int multiple,SECTION_DATA *Section, VECTORCH *incoming);
void QueenForceReconsider(STRATEGYBLOCK *sbPtr);
static BOOL TargetIsFiringFlamethrowerAtQueen(STRATEGYBLOCK *sbPtr);
static void MakeNonFragable(HMODELCONTROLLER *controller);
static void QueenCalculateTargetInfo(STRATEGYBLOCK *sbPtr);
void HandleHangarAirlock();
static BOOL LockerDoorIsClosed();
QUEEN_MANOEUVRE Queen_Next_Command;
int Queen_Next_Waypoint=0;
int Queen_Walk_Rate=100000;
int Queen_Walk_Step_Speed=ONE_FIXED/4.5;
int Queen_Charge_Rate=45000;
int Queen_ButtCharge_Rate=30000;
int Queen_Step_Time=50000;
int Queen_Step_Speed=10000; //16000;
int Queen_Charge_Step_Speed=7500;
int Queen_Step_Mode=1;
int Queen_Turn_Rate=(NPC_TURNRATE>>2);
#define QUEEN_BUTTCHARGE_SPEED 16000
#define QUEEN_CHARGE_SPEED 12000
#define QUEEN_CLOSE_SPEED 3000
#define QUEEN_WALK_SPEED 7000
#define QUEEN_THROWN_OBJECT_SPEED 60000
extern DAMAGE_PROFILE QueenButtDamage;
extern DAMAGE_PROFILE QueenImpactDamage;
extern DAMAGE_PROFILE VacuumDamage;
VECTORCH Queen_Target_Point={0,0,0};
VECTORCH Queen_Waypoints[] = {
{37361,2900,-51942},
{32937,2900,-19959},
{62809,2900,-28509},
{64658,2900,-13328},
{64658,0,-13328},
{64991,2900,-50362},
{32239,2900,-53578},
{-1,-1,-1}
};
void QComm_Stop(void) {
Queen_Next_Command=QM_Standby;
Queen_Next_Waypoint=-1;
}
void QComm_StepForward(void) {
Queen_Next_Command=QM_StepForward;
}
void QComm_StepBack(void) {
Queen_Next_Command=QM_StepBack;
}
void QComm_TurnLeft(void) {
Queen_Next_Command=QM_TurnLeft;
}
void QComm_TurnRight(void) {
Queen_Next_Command=QM_TurnRight;
}
void QComm_Heel(void) {
Queen_Next_Command=QM_ComeToPoint;
Queen_Target_Point=Player->ObStrategyBlock->DynPtr->Position;
}
void QComm_Taunt(void) {
Queen_Next_Command=QM_Taunt;
}
void QComm_Hiss(void) {
Queen_Next_Command=QM_Hiss;
}
void QComm_LeftSwipe(void) {
Queen_Next_Command=QM_LeftSwipe;
}
void QComm_RightSwipe(void) {
Queen_Next_Command=QM_RightSwipe;
}
void QComm_Route(void) {
Queen_Next_Command=QM_Standby;
Queen_Next_Waypoint=-2;
}
void QComm_Charge(void) {
Queen_Next_Command=QM_Charge;
Queen_Target_Point=Player->ObStrategyBlock->DynPtr->Position;
}
static void QueenSoundHiss(STRATEGYBLOCK* sbPtr)
{
QUEEN_STATUS_BLOCK *queenStatusPointer;
GLOBALASSERT(sbPtr);
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
if(queenStatusPointer->soundHandle==SOUND_NOACTIVEINDEX)
{
PlayQueenSound(0,QSC_Hiss,0,&queenStatusPointer->soundHandle,&sbPtr->DynPtr->Position);
queenStatusPointer->lastSoundCategory=QSC_Hiss;
}
}
static void QueenSoundHurt(STRATEGYBLOCK* sbPtr)
{
QUEEN_STATUS_BLOCK *queenStatusPointer;
GLOBALASSERT(sbPtr);
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
/*
If the queen is currently playing a non-hurt sound then cancel it.
*/
if(queenStatusPointer->soundHandle!=SOUND_NOACTIVEINDEX)
{
if(queenStatusPointer->lastSoundCategory!=QSC_Scream_Hurt)
{
Sound_Stop(queenStatusPointer->soundHandle);
}
}
if(queenStatusPointer->soundHandle==SOUND_NOACTIVEINDEX)
{
PlayQueenSound(0,QSC_Scream_Hurt,0,&queenStatusPointer->soundHandle,&sbPtr->DynPtr->Position);
queenStatusPointer->lastSoundCategory=QSC_Scream_Hurt;
}
}
static void ThrownObjectBounceNoise(int object_index,VECTORCH* location)
{
static int QueenObjectSoundHandles[QUEEN_MAX_OBJECT]={SOUND_NOACTIVEINDEX,SOUND_NOACTIVEINDEX,SOUND_NOACTIVEINDEX,SOUND_NOACTIVEINDEX,SOUND_NOACTIVEINDEX,SOUND_NOACTIVEINDEX,SOUND_NOACTIVEINDEX,SOUND_NOACTIVEINDEX,SOUND_NOACTIVEINDEX,SOUND_NOACTIVEINDEX};
GLOBALASSERT(location);
PlayQueenSound(0,QSC_Object_Bounce,0,&QueenObjectSoundHandles[object_index],location);
}
void InitQueenBehaviour(void* bhdata, STRATEGYBLOCK *sbPtr)
{
TOOLS_DATA_QUEEN *toolsData;
QUEEN_STATUS_BLOCK *queenStatus;
int i;
LOCALASSERT(sbPtr);
LOCALASSERT(bhdata);
toolsData = (TOOLS_DATA_QUEEN *)bhdata;
/* Reset command interface. */
Queen_Next_Command=QM_Standby;
/* check we're not in a net game */
if(AvP.Network != I_No_Network)
{
RemoveBehaviourStrategy(sbPtr);
return;
}
/* make the assumption that the loader has initialised the strategy
block sensibly...
so just set the shapeIndex from the tools data & copy the name id*/
sbPtr->shapeIndex = toolsData->shapeIndex;
for(i=0;i<SB_NAME_LENGTH;i++) sbPtr->SBname[i] = toolsData->nameID[i];
/* create, initialise and attach a dynamics block */
sbPtr->DynPtr = AllocateDynamicsBlock(DYNAMICS_TEMPLATE_SPRITE_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->UseDisplacement = 1;
dynPtr->Displacement.vx = 0;
dynPtr->Displacement.vy = 0;
dynPtr->Displacement.vz = 0;
dynPtr->Mass=60000; /* No knockback, please. */
}
else
{
RemoveBehaviourStrategy(sbPtr);
return;
}
/* Initialise alien's stats */
{
NPC_DATA *NpcData;
NpcData=GetThisNpcData(I_NPC_AlienQueen);
LOCALASSERT(NpcData);
sbPtr->SBDamageBlock.Health=NpcData->StartingStats.Health<<ONE_FIXED_SHIFT;
sbPtr->SBDamageBlock.Armour=NpcData->StartingStats.Armour<<ONE_FIXED_SHIFT;
sbPtr->SBDamageBlock.SB_H_flags=NpcData->StartingStats.SB_H_flags;
}
/* create, initialise and attach a predator-alien/queen data block */
sbPtr->SBdataptr = (void *)AllocateMem(sizeof(QUEEN_STATUS_BLOCK));
if(sbPtr->SBdataptr)
{
SECTION *root_section;
queenStatus = (QUEEN_STATUS_BLOCK *)sbPtr->SBdataptr;
NPC_InitMovementData(&(queenStatus->moveData));
NPC_InitWanderData(&(queenStatus->wanderData));
sbPtr->integrity = QUEEN_STARTING_HEALTH;
queenStatus->QueenState=QBS_Reconsider;
queenStatus->current_move=QM_Standby;
queenStatus->next_move=QM_Standby;
queenStatus->fixed_foot=RightFoot;
queenStatus->fixed_foot_section=NULL; //Stupid, but I wouldn't want in uninitialised.
queenStatus->fixed_foot_oldpos.vx=0;
queenStatus->fixed_foot_oldpos.vy=0;
queenStatus->fixed_foot_oldpos.vz=0;
queenStatus->TargetPos.vx=0;
queenStatus->TargetPos.vy=0;
queenStatus->TargetPos.vz=0;
queenStatus->moveTimer = 0;
for(i=0;i<SB_NAME_LENGTH;i++) queenStatus->death_target_ID[i] = toolsData->death_target_ID[i];
queenStatus->death_target_sbptr=0;
queenStatus->death_target_request=toolsData->death_target_request;
root_section=GetNamedHierarchyFromLibrary("queen","Template");
GLOBALASSERT(root_section);
Create_HModel(&queenStatus->HModelController,root_section);
InitHModelSequence(&queenStatus->HModelController,
(int)HMSQT_QueenRightStanceTemplate,
(int)QRSTSS_Standard,
ONE_FIXED);
queenStatus->HModelController.Looped=1;
queenStatus->attack_delta=Add_Delta_Sequence(&queenStatus->HModelController,"attack",
(int)HMSQT_QueenRightStanceTemplate,(int)QRSTSS_LeftSwipe,Queen_Step_Time);
queenStatus->attack_delta->Playing=0;
queenStatus->hit_delta=Add_Delta_Sequence(&queenStatus->HModelController,"hit",
(int)HMSQT_QueenRightStanceTemplate,(int)QRSTSS_LeftHit,Queen_Step_Time);
queenStatus->attack_delta->Playing=0;
queenStatus->TempTarget=FALSE;
queenStatus->CurrentQueenObject=-1;
queenStatus->QueenObjectBias=1;
if(!stricmp(LevelName,"hangar"))
{
queenStatus->QueenPlayerBias=1;
}
else
{
//int he predator version , make it more likely for the queen to go after the player
queenStatus->QueenPlayerBias=5;
}
queenStatus->QueenTargetSB=Player->ObStrategyBlock;
queenStatus->QueenTauntTimer=0;
queenStatus->QueenFireTimer=0;
queenStatus->LastVelocity.vx=0;
queenStatus->LastVelocity.vy=0;
queenStatus->LastVelocity.vz=0;
queenStatus->BeenInAirlock=FALSE;
queenStatus->QueenActivated=FALSE;
queenStatus->soundHandle=SOUND_NOACTIVEINDEX;
NumQueenObjects=-1;
if(AvP.PlayerType==I_Marine)
{
MakeNonFragable(&queenStatus->HModelController);
}
queenStatus->AttackDoneItsDamage = FALSE;
}
else
{
GLOBALASSERT(0);
RemoveBehaviourStrategy(sbPtr);
return;
}
}
static BOOL QueenPlayingStunAnimation(HMODELCONTROLLER* HModelController)
{
GLOBALASSERT(HModelController);
return(HModelController->Sequence_Type==HMSQT_QueenGeneral &&
HModelController->Sub_Sequence==QGSS_Explosion_Stun &&
!HModelAnimation_IsFinished(HModelController));
}
void SetQueenFoot(STRATEGYBLOCK *sbPtr, QUEEN_FOOT foot) {
QUEEN_STATUS_BLOCK *queenStatusPointer;
DISPLAYBLOCK *dPtr;
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
dPtr=sbPtr->SBdptr;
GLOBALASSERT(dPtr);
ProveHModel(dPtr->HModelControlBlock,dPtr);
switch (foot) {
case (LeftFoot):
queenStatusPointer->fixed_foot=LeftFoot;
queenStatusPointer->fixed_foot_section=GetThisSectionData(queenStatusPointer->HModelController.section_data,
"left foot");
GLOBALASSERT(queenStatusPointer->fixed_foot_section);
queenStatusPointer->fixed_foot_oldpos=queenStatusPointer->fixed_foot_section->World_Offset;
break;
case (RightFoot):
queenStatusPointer->fixed_foot=RightFoot;
queenStatusPointer->fixed_foot_section=GetThisSectionData(queenStatusPointer->HModelController.section_data,
"right foot");
GLOBALASSERT(queenStatusPointer->fixed_foot_section);
queenStatusPointer->fixed_foot_oldpos=queenStatusPointer->fixed_foot_section->World_Offset;
break;
default:
GLOBALASSERT(0);
break;
}
}
void MakeQueenNear(STRATEGYBLOCK *sbPtr)
{
extern MODULEMAPBLOCK AlienDefaultMap;
MODULE tempModule;
DISPLAYBLOCK *dPtr;
DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
QUEEN_STATUS_BLOCK *queenStatusPointer= (QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
/* This should only be called once! */
LOCALASSERT(queenStatusPointer);
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) {
GLOBALASSERT(0);
}
sbPtr->SBdptr = dPtr;
dPtr->ObStrategyBlock = sbPtr;
dPtr->ObMyModule = NULL;
/*Copy extents from the collision extents in extents.c*/
dPtr->ObMinX=-CollisionExtents[CE_QUEEN].CollisionRadius;
dPtr->ObMaxX=CollisionExtents [CE_QUEEN].CollisionRadius;
dPtr->ObMinZ=-CollisionExtents[CE_QUEEN].CollisionRadius;
dPtr->ObMaxZ=CollisionExtents [CE_QUEEN].CollisionRadius;
dPtr->ObMinY=CollisionExtents [CE_QUEEN].CrouchingTop;
dPtr->ObMaxY=CollisionExtents [CE_QUEEN].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 */
/* zero linear velocity in dynamics block */
dynPtr->LinVelocity.vx = 0;
dynPtr->LinVelocity.vy = 0;
dynPtr->LinVelocity.vz = 0;
/* initialise our sequence data */
dPtr->HModelControlBlock=&queenStatusPointer->HModelController;
SetQueenFoot(sbPtr,RightFoot);
/* Calls ProveHModel. */
}
void MakeQueenFar(STRATEGYBLOCK *sbPtr) {
/* get the queen's status block */
int i;
QUEEN_STATUS_BLOCK *queenStatusPointer= (QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
LOCALASSERT(sbPtr);
LOCALASSERT(queenStatusPointer);
LOCALASSERT(sbPtr->SBdptr != NULL);
/* get rid of the displayblock */
if(sbPtr->SBdptr)
{
i = DestroyActiveObject(sbPtr->SBdptr);
LOCALASSERT(i==0);
sbPtr->SBdptr = NULL;
}
/* zero linear velocity in dynamics block */
LOCALASSERT(sbPtr->DynPtr);
sbPtr->DynPtr->LinVelocity.vx = 0;
sbPtr->DynPtr->LinVelocity.vy = 0;
sbPtr->DynPtr->LinVelocity.vz = 0;
}
void SetQueenMovement_FromFoot(STRATEGYBLOCK *sbPtr) {
QUEEN_STATUS_BLOCK *queenStatusPointer;
VECTORCH delta_offset,real_pos;
DISPLAYBLOCK *dPtr;
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
dPtr=sbPtr->SBdptr;
GLOBALASSERT(dPtr);
real_pos=queenStatusPointer->fixed_foot_section->World_Offset;
ProveHModel(dPtr->HModelControlBlock,dPtr);
delta_offset.vx=queenStatusPointer->fixed_foot_oldpos.vx-queenStatusPointer->fixed_foot_section->World_Offset.vx;
delta_offset.vy=0;//queenStatusPointer->fixed_foot_oldpos.vy-queenStatusPointer->fixed_foot_section->World_Offset.vy;
delta_offset.vz=queenStatusPointer->fixed_foot_oldpos.vz-queenStatusPointer->fixed_foot_section->World_Offset.vz;
#if 1
delta_offset.vx=DIV_FIXED(delta_offset.vx,NormalFrameTime);
delta_offset.vy=0;//DIV_FIXED(delta_offset.vy,NormalFrameTime);
delta_offset.vz=DIV_FIXED(delta_offset.vz,NormalFrameTime);
sbPtr->DynPtr->LinVelocity.vx=delta_offset.vx;
sbPtr->DynPtr->LinVelocity.vy=delta_offset.vy;
sbPtr->DynPtr->LinVelocity.vz=delta_offset.vz;
{
int facex;
int facez;
facex=sbPtr->DynPtr->OrientMat.mat31;
facez=sbPtr->DynPtr->OrientMat.mat33;
if((MUL_FIXED(facex,sbPtr->DynPtr->LinVelocity.vx)+MUL_FIXED(facez,sbPtr->DynPtr->LinVelocity.vz))<0)
{
//don't want queen to move backwards
sbPtr->DynPtr->LinVelocity.vx = 0;
sbPtr->DynPtr->LinVelocity.vy = 0;
sbPtr->DynPtr->LinVelocity.vz = 0;
switch (queenStatusPointer->fixed_foot)
{
case (LeftFoot):
SetQueenFoot(sbPtr,LeftFoot);
break;
case (RightFoot):
SetQueenFoot(sbPtr,RightFoot);
break;
default:
GLOBALASSERT(0);
break;
}
}
}
#elif 1
sbPtr->DynPtr->Displacement.vx = delta_offset.vx;
sbPtr->DynPtr->Displacement.vy = delta_offset.vy;
sbPtr->DynPtr->Displacement.vz = delta_offset.vz;
//PrintDebuggingText("Displacement = %d %d %d\n",delta_offset.vx,delta_offset.vy,delta_offset.vz);
//
//LOGDXFMT(("New Foot Frame.\nFoot OldPos = %d %d %d.\nFoot NewPos = %d %d %d\nFoot Current Pos = %d %d %d.\nDisplacement = %d %d %d.\n\n",
// queenStatusPointer->fixed_foot_oldpos.vx,queenStatusPointer->fixed_foot_oldpos.vy,queenStatusPointer->fixed_foot_oldpos.vz,
// real_pos.vx,real_pos.vy,real_pos.vz,
// queenStatusPointer->fixed_foot_section->World_Offset.vx,queenStatusPointer->fixed_foot_section->World_Offset.vy,
// queenStatusPointer->fixed_foot_section->World_Offset.vz,delta_offset.vx,delta_offset.vy,delta_offset.vz));
//
//sbPtr->DynPtr->Displacement.vx = 0;
//sbPtr->DynPtr->Displacement.vy = 0;
//sbPtr->DynPtr->Displacement.vz = 200;
#else
sbPtr->DynPtr->Position.vx += delta_offset.vx;
sbPtr->DynPtr->Position.vy += delta_offset.vy;
sbPtr->DynPtr->Position.vz += delta_offset.vz;
#endif
}
void QueenMove_Standby(STRATEGYBLOCK *sbPtr) {
QUEEN_STATUS_BLOCK *queenStatusPointer;
/* Verify correct sequence. */
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
sbPtr->DynPtr->LinVelocity.vx = 0;
sbPtr->DynPtr->LinVelocity.vy = 0;
sbPtr->DynPtr->LinVelocity.vz = 0;
if(queenStatusPointer->PlayingHitDelta)
{
if(QueenPlayingStunAnimation(&queenStatusPointer->HModelController))
{
//queen is in the process of playing stun animation
return;
}
}
if(queenStatusPointer->moveTimer==0)
{
//start tweening to standing position
switch(queenStatusPointer->fixed_foot)
{
case (LeftFoot) :
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenLeftStanceTemplate,
(int)QLSTSS_Standard,-1,ONE_FIXED>>2);
break;
case (RightFoot) :
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenRightStanceTemplate,
(int)QRSTSS_Standard,-1,ONE_FIXED>>2);
break;
default:
GLOBALASSERT(0);
break;
}
queenStatusPointer->moveTimer=1;
}
}
void QueenMove_StepForward(STRATEGYBLOCK *sbPtr) {
QUEEN_STATUS_BLOCK *queenStatusPointer;
/* First movement function. */
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer->current_move==QM_StepForward);
if (queenStatusPointer->moveTimer==0) {
/* Do setup. */
int tweeiningtime=(ONE_FIXED>>3);
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,LeftFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenLeftStanceTemplate,
(int)QLSTSS_Forward_L2R,Queen_Step_Time,tweeiningtime);
break;
case (RightFoot):
SetQueenFoot(sbPtr,RightFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenRightStanceTemplate,
(int)QRSTSS_Forward_R2L,Queen_Step_Time,tweeiningtime);
break;
default:
GLOBALASSERT(0);
break;
}
}
if (queenStatusPointer->HModelController.Tweening==Controller_NoTweening) {
GLOBALASSERT(queenStatusPointer->HModelController.Looped==0);
}
/* Now... move forward? */
SetQueenMovement_FromFoot(sbPtr);
if ((queenStatusPointer->HModelController.Tweening==Controller_NoTweening)
&&(queenStatusPointer->HModelController.sequence_timer==(ONE_FIXED-1))) {
queenStatusPointer->current_move=QM_Standby;
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,RightFoot);
break;
case (RightFoot):
SetQueenFoot(sbPtr,LeftFoot);
break;
default:
GLOBALASSERT(0);
break;
}
}
queenStatusPointer->moveTimer+=NormalFrameTime;
}
void QueenMove_StepBack(STRATEGYBLOCK *sbPtr) {
QUEEN_STATUS_BLOCK *queenStatusPointer;
/* First movement function. */
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer->current_move==QM_StepBack);
if (queenStatusPointer->moveTimer==0) {
/* Do setup. */
int tweeiningtime=(ONE_FIXED>>3);
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,RightFoot);
/* Wrong Foot w.r.t. Forward. */
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenLeftStanceFull,
(int)QLSFSS_Backward_L2R,Queen_Step_Time,tweeiningtime);
break;
case (RightFoot):
SetQueenFoot(sbPtr,LeftFoot);
/* Wrong Foot w.r.t. Forward. */
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenRightStanceFull,
(int)QRSFSS_Backward_R2L,Queen_Step_Time,tweeiningtime);
break;
default:
GLOBALASSERT(0);
break;
}
}
if (queenStatusPointer->HModelController.Tweening==Controller_NoTweening) {
GLOBALASSERT(queenStatusPointer->HModelController.Looped==0);
}
/* Now... move forward? */
SetQueenMovement_FromFoot(sbPtr);
queenStatusPointer->moveTimer+=NormalFrameTime;
if ((queenStatusPointer->HModelController.Tweening==Controller_NoTweening)
&&(queenStatusPointer->HModelController.sequence_timer==(ONE_FIXED-1))) {
queenStatusPointer->current_move=QM_Standby;
/* Already changed foot. */
}
}
void QueenMove_TurnLeft(STRATEGYBLOCK *sbPtr) {
QUEEN_STATUS_BLOCK *queenStatusPointer;
/* First movement function. */
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer->current_move==QM_TurnLeft);
if (queenStatusPointer->moveTimer==0) {
/* Do setup. */
int tweeiningtime=(ONE_FIXED>>3);
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,LeftFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenLeftStanceFull,
(int)QLSFSS_Forward_L2R,Queen_Step_Time,tweeiningtime);
break;
case (RightFoot):
SetQueenFoot(sbPtr,RightFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenRightStanceFull,
(int)QRSFSS_Forward_R2L,Queen_Step_Time,tweeiningtime);
break;
default:
GLOBALASSERT(0);
break;
}
}
if (queenStatusPointer->HModelController.Tweening==Controller_NoTweening) {
GLOBALASSERT(queenStatusPointer->HModelController.Looped==0);
}
/* Now... turn left. */
{
VECTORCH left90;
DISPLAYBLOCK *dPtr;
dPtr=sbPtr->SBdptr;
GLOBALASSERT(dPtr);
ProveHModel(dPtr->HModelControlBlock,dPtr);
left90.vx=-sbPtr->DynPtr->OrientMat.mat11;
left90.vy=-sbPtr->DynPtr->OrientMat.mat12;
left90.vz=-sbPtr->DynPtr->OrientMat.mat13;
#if 1
NPCOrientateToVector(sbPtr, &left90,Queen_Turn_Rate,NULL);
SetQueenMovement_FromFoot(sbPtr);
#else
{
VECTORCH localOffset;
VECTORCH version1,version2;
//MATRIXCH WtoL;
ProveHModel(dPtr->HModelControlBlock,dPtr);
localOffset.vx=queenStatusPointer->fixed_foot_section->World_Offset.vx-sbPtr->DynPtr->Position.vx;
localOffset.vy=queenStatusPointer->fixed_foot_section->World_Offset.vy-sbPtr->DynPtr->Position.vy;
localOffset.vz=queenStatusPointer->fixed_foot_section->World_Offset.vz-sbPtr->DynPtr->Position.vz;
//WtoL=sbPtr->DynPtr->OrientMat;
//TransposeMatrixCH(&WtoL);
//RotateVector(&localOffset,&WtoL);
PrintDebuggingText("localOffset = %d %d %d\n",localOffset.vx,
localOffset.vy,localOffset.vz);
NPCOrientateToVector(sbPtr, &left90,Queen_Turn_Rate,&localOffset);
//version1=sbPtr->DynPtr->Displacement;
//SetQueenMovement_FromFoot(sbPtr);
//version2=sbPtr->DynPtr->Displacement;
//
//LOGDXFMT(("Displacement V1 = %d %d %d\nDisplacement V2 = %d %d %d\n",
// version1.vx,version1.vy,version1.vz,
// version2.vx,version2.vy,version2.vz));
}
#endif
}
if ((queenStatusPointer->HModelController.Tweening==Controller_NoTweening)
&&(queenStatusPointer->HModelController.sequence_timer==(ONE_FIXED-1))) {
queenStatusPointer->current_move=QM_Standby;
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,RightFoot);
break;
case (RightFoot):
SetQueenFoot(sbPtr,LeftFoot);
break;
default:
GLOBALASSERT(0);
break;
}
}
queenStatusPointer->moveTimer+=NormalFrameTime;
}
void QueenMove_TurnRight(STRATEGYBLOCK *sbPtr) {
QUEEN_STATUS_BLOCK *queenStatusPointer;
/* First movement function. */
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer->current_move==QM_TurnRight);
if (queenStatusPointer->moveTimer==0) {
/* Do setup. */
int tweeiningtime=(ONE_FIXED>>3);
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,LeftFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenLeftStanceFull,
(int)QLSFSS_Forward_L2R,Queen_Step_Time,tweeiningtime);
break;
case (RightFoot):
SetQueenFoot(sbPtr,RightFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenRightStanceFull,
(int)QRSFSS_Forward_R2L,Queen_Step_Time,tweeiningtime);
break;
default:
GLOBALASSERT(0);
break;
}
}
if (queenStatusPointer->HModelController.Tweening==Controller_NoTweening) {
GLOBALASSERT(queenStatusPointer->HModelController.Looped==0);
}
/* Now... turn right. */
{
VECTORCH right90;
DISPLAYBLOCK *dPtr;
dPtr=sbPtr->SBdptr;
GLOBALASSERT(dPtr);
ProveHModel(dPtr->HModelControlBlock,dPtr);
right90.vx=sbPtr->DynPtr->OrientMat.mat11;
right90.vy=sbPtr->DynPtr->OrientMat.mat12;
right90.vz=sbPtr->DynPtr->OrientMat.mat13;
NPCOrientateToVector(sbPtr, &right90,Queen_Turn_Rate,NULL);
SetQueenMovement_FromFoot(sbPtr);
}
if ((queenStatusPointer->HModelController.Tweening==Controller_NoTweening)
&&(queenStatusPointer->HModelController.sequence_timer==(ONE_FIXED-1))) {
queenStatusPointer->current_move=QM_Standby;
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,RightFoot);
break;
case (RightFoot):
SetQueenFoot(sbPtr,LeftFoot);
break;
default:
GLOBALASSERT(0);
break;
}
}
queenStatusPointer->moveTimer+=NormalFrameTime;
}
void SetQueenShapeAnimSequence_Core(STRATEGYBLOCK *sbPtr,HMODEL_SEQUENCE_TYPES type, int subtype, int length, int tweeningtime)
{
QUEEN_STATUS_BLOCK *queenStatus=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(length!=0);
if (tweeningtime<=0) {
InitHModelSequence(&queenStatus->HModelController,(int)type,subtype,length);
queenStatus->HModelController.Looped=0;
} else {
InitHModelTweening(&queenStatus->HModelController, tweeningtime, (int)type,subtype,length, 0);
queenStatus->HModelController.Looped=0;
}
}
void QueenMove_ComeToPoint(STRATEGYBLOCK *sbPtr) {
QUEEN_STATUS_BLOCK *queenStatusPointer;
VECTORCH vectotarget;
/* Complex movement function. */
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer->current_move==QM_ComeToPoint);
if (queenStatusPointer->moveTimer==0) {
/* Do setup. */
int tweeiningtime=0;
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,LeftFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenLeftStanceFull,
(int)QLSFSS_Forward_L2R,Queen_Step_Time,tweeiningtime);
break;
case (RightFoot):
SetQueenFoot(sbPtr,RightFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenRightStanceFull,
(int)QRSFSS_Forward_R2L,Queen_Step_Time,tweeiningtime);
break;
default:
GLOBALASSERT(0);
break;
}
}
if (queenStatusPointer->HModelController.Tweening==Controller_NoTweening) {
GLOBALASSERT(queenStatusPointer->HModelController.Looped==0);
}
vectotarget.vx=queenStatusPointer->TargetPos.vx-sbPtr->DynPtr->Position.vx;
vectotarget.vy=queenStatusPointer->TargetPos.vy-sbPtr->DynPtr->Position.vy;
vectotarget.vz=queenStatusPointer->TargetPos.vz-sbPtr->DynPtr->Position.vz;
/* Now... turn to face. */
{
DISPLAYBLOCK *dPtr;
dPtr=sbPtr->SBdptr;
GLOBALASSERT(dPtr);
ProveHModel(dPtr->HModelControlBlock,dPtr);
NPCOrientateToVector(sbPtr, &vectotarget,Queen_Turn_Rate,NULL);
SetQueenMovement_FromFoot(sbPtr);
}
queenStatusPointer->moveTimer+=NormalFrameTime;
if ((queenStatusPointer->HModelController.Tweening==Controller_NoTweening)
&&(queenStatusPointer->HModelController.sequence_timer==(ONE_FIXED-1))) {
int range;
/* Comsider next step. */
range=Approximate3dMagnitude(&vectotarget);
if (range<5000) {
queenStatusPointer->current_move=QM_Standby;
} else {
queenStatusPointer->moveTimer=0;
}
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,RightFoot);
break;
case (RightFoot):
SetQueenFoot(sbPtr,LeftFoot);
break;
default:
GLOBALASSERT(0);
break;
}
}
}
#if 1
void QueenMove_Walk(STRATEGYBLOCK *sbPtr) {
QUEEN_STATUS_BLOCK *queenStatusPointer;
BOOL ChangingFoot=FALSE;
/* Very complex movement function... */
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer->current_move==QM_ComeToPoint);
if (queenStatusPointer->moveTimer==0) {
/* Do setup. */
int tweeiningtime=(Queen_Step_Time>>2);
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,LeftFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenGeneral,
(int)QGSS_Walk,-1,tweeiningtime);
queenStatusPointer->HModelController.LoopAfterTweening=1;
queenStatusPointer->moveTimer=1; /* It's something of a state flag here. */
break;
case (RightFoot):
/* Argh! Can't start from right foot! */
queenStatusPointer->current_move=QM_Close;
queenStatusPointer->moveTimer=0;
queenStatusPointer->next_move=QM_ComeToPoint;
return;
break;
default:
GLOBALASSERT(0);
break;
}
}
/* Check for change foot? */
if (queenStatusPointer->HModelController.Tweening==Controller_NoTweening) {
if (queenStatusPointer->HModelController.keyframe_flags)
{
if (queenStatusPointer->HModelController.keyframe_flags & 1)
{
SetQueenFoot(sbPtr,LeftFoot);
ChangingFoot=TRUE;
}
if (queenStatusPointer->HModelController.keyframe_flags & 2)
{
SetQueenFoot(sbPtr,RightFoot);
ChangingFoot=TRUE;
}
if(queenStatusPointer->moveTimer==1) queenStatusPointer->moveTimer=3;
}
HModel_SetToolsRelativeSpeed(&queenStatusPointer->HModelController,(512*ONE_FIXED)/QUEEN_WALK_SPEED);
}
/* Now... turn to face. */
{
DISPLAYBLOCK *dPtr;
dPtr=sbPtr->SBdptr;
GLOBALASSERT(dPtr);
//ProveHModel(dPtr->HModelControlBlock,dPtr);
NPCOrientateToVector(sbPtr, &queenStatusPointer->VectToTarget,Queen_Turn_Rate,NULL);
//SetQueenMovement_FromFoot(sbPtr);
if (queenStatusPointer->moveTimer!=2)
{
if(queenStatusPointer->moveTimer==1)
{
SetQueenMovement_FromFoot(sbPtr);
}
else
{
VECTORCH velocity;
//int walkSpeed;
velocity.vx=sbPtr->DynPtr->OrientMat.mat31;
velocity.vy=0;
velocity.vz=sbPtr->DynPtr->OrientMat.mat33;
if ( (velocity.vx==0) && (velocity.vy==0) && (velocity.vz==0) ) {
sbPtr->DynPtr->LinVelocity.vx = 0;
sbPtr->DynPtr->LinVelocity.vy = 0;
sbPtr->DynPtr->LinVelocity.vz = 0;
return;
}
Normalise(&velocity);
//walkSpeed=DIV_FIXED(Queen_Walk_Step_Speed,Queen_Walk_Rate);
sbPtr->DynPtr->LinVelocity.vx = MUL_FIXED(velocity.vx,QUEEN_WALK_SPEED);
sbPtr->DynPtr->LinVelocity.vy = MUL_FIXED(velocity.vy,QUEEN_WALK_SPEED);
sbPtr->DynPtr->LinVelocity.vz = MUL_FIXED(velocity.vz,QUEEN_WALK_SPEED);
}
}
}
/* Consider exit state. */
if ((queenStatusPointer->HModelController.Tweening==Controller_NoTweening)
&&(queenStatusPointer->moveTimer==2)) {
/* Finished coming to a stop. */
queenStatusPointer->current_move=QM_Standby;
queenStatusPointer->next_move=QM_Standby;
queenStatusPointer->moveTimer=0;
switch (queenStatusPointer->fixed_foot) {
case (RightFoot):
SetQueenFoot(sbPtr,RightFoot);
break;
case (LeftFoot):
SetQueenFoot(sbPtr,LeftFoot);
break;
default:
GLOBALASSERT(0);
break;
}
/* Would you believe the end's in the middle? :-) */
}
//only check for exiting charge , when changing foot
{
int range;
QueenCalculateTargetInfo(sbPtr);
range=queenStatusPointer->TargetDistance;
if(range<3000)
{
queenStatusPointer->next_move=QM_Close;
}
if(ChangingFoot)
{
if(queenStatusPointer->PlayingHitDelta)
{
queenStatusPointer->HModelController.Playing=0;
queenStatusPointer->current_move=QM_Standby;
queenStatusPointer->moveTimer=0;
queenStatusPointer->next_move=QM_Standby;
return;
}
if(queenStatusPointer->moveTimer!=2)
{
if (queenStatusPointer->next_move!=QM_Standby && queenStatusPointer->next_move!=QM_ComeToPoint)
{
//exit without tweening to stopped position
queenStatusPointer->HModelController.Playing=0;
queenStatusPointer->current_move=queenStatusPointer->next_move;
queenStatusPointer->next_move=QM_Standby;
queenStatusPointer->moveTimer=0;
}
#if 0
else if(range<3000)
{
//come to a stop
/* Begin exit. */
int tweeiningtime=(Queen_Step_Time>>2);
queenStatusPointer->moveTimer=2; /* It's something of a state flag here. */
switch (queenStatusPointer->fixed_foot) {
case (RightFoot):
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenRightStanceTemplate,
(int)QRSTSS_Standard,(Queen_Step_Time<<1),tweeiningtime);
break;
case (LeftFoot):
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenLeftStanceTemplate,
(int)QLSTSS_Standard,(Queen_Step_Time<<1),tweeiningtime);
break;
default:
GLOBALASSERT(0);
break;
}
queenStatusPointer->HModelController.LoopAfterTweening=0;
}
#endif
else if(range>10000 && queenStatusPointer->fixed_foot==LeftFoot && !PlayerInTrench)
{
//go into a charge
queenStatusPointer->current_move=QM_Charge;
queenStatusPointer->next_move=QM_Standby;
queenStatusPointer->moveTimer=0;
}
if(range<5000)
{
/*if the queen is near but facing the wrong way need to go into close mode
(turning circle is too large in walk mode) */
if (queenStatusPointer->TargetDirection.vz<queenStatusPointer->TargetDirection.vx ||
queenStatusPointer->TargetDirection.vz<-queenStatusPointer->TargetDirection.vx)
{
//need to switch to close mode
queenStatusPointer->HModelController.Playing=0;
queenStatusPointer->current_move=QM_Close;
queenStatusPointer->moveTimer=0;
queenStatusPointer->next_move=QM_Standby;
return;
}
}
}
}
}
}
#else
void QueenMove_Walk(STRATEGYBLOCK *sbPtr) {
QUEEN_STATUS_BLOCK *queenStatusPointer;
VECTORCH vectotarget;
/* Very complex movement function... */
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer->current_move==QM_ComeToPoint);
if (queenStatusPointer->moveTimer==0) {
/* Do setup. */
int tweeiningtime=(Queen_Step_Time>>2);
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,LeftFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenGeneral,
(int)QGSS_Walk,(Queen_Step_Time<<1),tweeiningtime);
queenStatusPointer->HModelController.LoopAfterTweening=1;
queenStatusPointer->moveTimer=1; /* It's something of a state flag here. */
break;
case (RightFoot):
/* Argh! Can't start from right foot! */
queenStatusPointer->current_move=QM_StepForward;
queenStatusPointer->moveTimer=0;
queenStatusPointer->next_move=QM_ComeToPoint;
return;
break;
default:
GLOBALASSERT(0);
break;
}
}
vectotarget.vx=queenStatusPointer->TargetPos.vx-sbPtr->DynPtr->Position.vx;
vectotarget.vy=queenStatusPointer->TargetPos.vy-sbPtr->DynPtr->Position.vy;
vectotarget.vz=queenStatusPointer->TargetPos.vz-sbPtr->DynPtr->Position.vz;
/* Check for change foot? */
if (queenStatusPointer->HModelController.Tweening==Controller_NoTweening) {
if (queenStatusPointer->HModelController.keyframe_flags) {
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,RightFoot);
break;
case (RightFoot):
SetQueenFoot(sbPtr,LeftFoot);
break;
default:
GLOBALASSERT(0);
break;
}
}
}
/* Now... turn to face. */
{
DISPLAYBLOCK *dPtr;
dPtr=sbPtr->SBdptr;
GLOBALASSERT(dPtr);
ProveHModel(dPtr->HModelControlBlock,dPtr);
NPCOrientateToVector(sbPtr, &vectotarget,Queen_Turn_Rate,NULL);
SetQueenMovement_FromFoot(sbPtr);
}
/* Consider exit state. */
if ((queenStatusPointer->HModelController.Tweening==Controller_NoTweening)
&&(queenStatusPointer->moveTimer==2)) {
/* We must have finished. */
queenStatusPointer->current_move=QM_Standby;
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,RightFoot);
break;
case (RightFoot):
SetQueenFoot(sbPtr,LeftFoot);
break;
default:
GLOBALASSERT(0);
break;
}
/* Would you believe the end's in the middle? :-) */
}
{
int range;
range=Approximate3dMagnitude(&vectotarget);
if ((range<5000)&&(queenStatusPointer->moveTimer!=2)) {
/* Begin exit. */
int tweeiningtime=(Queen_Step_Time>>2);
queenStatusPointer->moveTimer=2; /* It's something of a state flag here. */
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenRightStanceTemplate,
(int)QRSTSS_Standard,(Queen_Step_Time<<1),tweeiningtime);
break;
case (RightFoot):
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenLeftStanceTemplate,
(int)QLSTSS_Standard,(Queen_Step_Time<<1),tweeiningtime);
break;
default:
GLOBALASSERT(0);
break;
}
queenStatusPointer->HModelController.LoopAfterTweening=0;
} else if (queenStatusPointer->moveTimer!=2) {
queenStatusPointer->moveTimer=1; /* It's something of a state flag here. */
}
}
#if 0
if ((queenStatusPointer->HModelController.Tweening==Controller_NoTweening)
&&(queenStatusPointer->HModelController.sequence_timer==(ONE_FIXED-1))) {
int range;
/* Comsider next step. */
range=Approximate3dMagnitude(&vectotarget);
if (range<5000) {
queenStatusPointer->current_move=QM_Standby;
} else {
queenStatusPointer->moveTimer=0;
}
}
#endif
}
#endif
void QueenMove_Taunt(STRATEGYBLOCK *sbPtr) {
QUEEN_STATUS_BLOCK *queenStatusPointer;
/* First movement function. */
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer->current_move==QM_Taunt);
if (queenStatusPointer->moveTimer==0) {
/* Do setup. */
int tweeiningtime=(ONE_FIXED>>3);
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,LeftFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenLeftStanceFull,
(int)QLSFSS_Taunt,-1,tweeiningtime);
break;
case (RightFoot):
SetQueenFoot(sbPtr,RightFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenRightStanceFull,
(int)QRSFSS_Taunt,-1,tweeiningtime);
break;
default:
GLOBALASSERT(0);
break;
}
}
if (queenStatusPointer->HModelController.Tweening==Controller_NoTweening) {
GLOBALASSERT(queenStatusPointer->HModelController.Looped==0);
}
/* Now... move forward? */
SetQueenMovement_FromFoot(sbPtr);
if ((queenStatusPointer->HModelController.Tweening==Controller_NoTweening)
&&(queenStatusPointer->HModelController.sequence_timer==(ONE_FIXED-1))) {
queenStatusPointer->current_move=QM_Standby;
/* Same foot. */
}
queenStatusPointer->moveTimer+=NormalFrameTime;
}
void QueenMove_Hiss(STRATEGYBLOCK *sbPtr) {
QUEEN_STATUS_BLOCK *queenStatusPointer;
/* First movement function. */
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer->current_move==QM_Hiss);
if (queenStatusPointer->moveTimer==0) {
/* Do setup. */
int tweeiningtime=(ONE_FIXED>>3);
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,LeftFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenLeftStanceFull,
(int)QLSFSS_Standard_Hiss,Queen_Step_Time,tweeiningtime);
queenStatusPointer->HModelController.LoopAfterTweening=1;
break;
case (RightFoot):
SetQueenFoot(sbPtr,RightFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenRightStanceFull,
(int)QRSFSS_Standard_Hiss,Queen_Step_Time,tweeiningtime);
queenStatusPointer->HModelController.LoopAfterTweening=1;
break;
default:
GLOBALASSERT(0);
break;
}
}
/*
if (queenStatusPointer->HModelController.Tweening==Controller_NoTweening)
{
GLOBALASSERT(queenStatusPointer->HModelController.Looped==0);
}
*/
/* Now... move forward? */
SetQueenMovement_FromFoot(sbPtr);
#if 0
if ((queenStatusPointer->HModelController.Tweening==Controller_NoTweening)
&&(queenStatusPointer->HModelController.sequence_timer==(ONE_FIXED-1))) {
queenStatusPointer->current_move=QM_Hiss;
/* Same foot. */
}
#endif
queenStatusPointer->moveTimer+=NormalFrameTime;
}
void QueenMove_LeftSwipe(STRATEGYBLOCK *sbPtr) {
QUEEN_STATUS_BLOCK *queenStatusPointer;
/* First movement function. */
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer->current_move==QM_LeftSwipe);
if (queenStatusPointer->moveTimer==0) {
/* Do setup. */
int tweeiningtime=(ONE_FIXED>>3);
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,LeftFoot);
//SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenLeftStanceTemplate,
// (int)QLSTSS_Forward_L2R,Queen_Step_Time,tweeiningtime);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenLeftStanceFull,
(int)QLSFSS_Standard_Hiss,Queen_Step_Time,tweeiningtime);
Start_Delta_Sequence(queenStatusPointer->attack_delta,
(int)HMSQT_QueenLeftStanceTemplate,(int)QLSTSS_LeftSwipe,Queen_Step_Time);
queenStatusPointer->attack_delta->Playing=1;
break;
case (RightFoot):
SetQueenFoot(sbPtr,RightFoot);
//SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenRightStanceTemplate,
// (int)QRSTSS_Forward_R2L,Queen_Step_Time,tweeiningtime);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenRightStanceFull,
(int)QRSFSS_Standard_Hiss,Queen_Step_Time,tweeiningtime);
Start_Delta_Sequence(queenStatusPointer->attack_delta,
(int)HMSQT_QueenRightStanceTemplate,(int)QRSTSS_LeftSwipe,Queen_Step_Time);
queenStatusPointer->attack_delta->Playing=1;
break;
default:
GLOBALASSERT(0);
break;
}
}
if (queenStatusPointer->HModelController.Tweening==Controller_NoTweening) {
GLOBALASSERT(queenStatusPointer->HModelController.Looped==0);
}
/* Now... move forward? */
SetQueenMovement_FromFoot(sbPtr);
if ((queenStatusPointer->HModelController.Tweening==Controller_NoTweening)
&&(queenStatusPointer->HModelController.sequence_timer==(ONE_FIXED-1))) {
queenStatusPointer->current_move=QM_Standby;
}
queenStatusPointer->moveTimer+=NormalFrameTime;
}
void QueenMove_RightSwipe(STRATEGYBLOCK *sbPtr) {
QUEEN_STATUS_BLOCK *queenStatusPointer;
/* First movement function. */
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer->current_move==QM_RightSwipe);
if (queenStatusPointer->moveTimer==0) {
/* Do setup. */
int tweeiningtime=(ONE_FIXED>>3);
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,LeftFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenLeftStanceFull,
(int)QLSFSS_Standard_Hiss,Queen_Step_Time,tweeiningtime);
Start_Delta_Sequence(queenStatusPointer->attack_delta,
(int)HMSQT_QueenLeftStanceTemplate,(int)QLSTSS_RightSwipe,Queen_Step_Time);
queenStatusPointer->attack_delta->Playing=1;
break;
case (RightFoot):
SetQueenFoot(sbPtr,RightFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenRightStanceFull,
(int)QRSFSS_Standard_Hiss,Queen_Step_Time,tweeiningtime);
Start_Delta_Sequence(queenStatusPointer->attack_delta,
(int)HMSQT_QueenRightStanceTemplate,(int)QRSTSS_RightSwipe,Queen_Step_Time);
queenStatusPointer->attack_delta->Playing=1;
break;
default:
GLOBALASSERT(0);
break;
}
}
if (queenStatusPointer->HModelController.Tweening==Controller_NoTweening) {
GLOBALASSERT(queenStatusPointer->HModelController.Looped==0);
}
/* Now... move forward? */
SetQueenMovement_FromFoot(sbPtr);
if ((queenStatusPointer->HModelController.Tweening==Controller_NoTweening)
&&(queenStatusPointer->HModelController.sequence_timer==(ONE_FIXED-1))) {
queenStatusPointer->current_move=QM_Standby;
}
queenStatusPointer->moveTimer+=NormalFrameTime;
}
void QueenMove_Charge(STRATEGYBLOCK *sbPtr) {
QUEEN_STATUS_BLOCK *queenStatusPointer;
BOOL ChangingFoot=FALSE;
/* Very different movement function... */
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer->current_move==QM_Charge);
/* Charge at the player. */
if(!queenStatusPointer->TempTarget)
{
// queenStatusPointer->TargetPos=Player->ObStrategyBlock->DynPtr->Position;
queenStatusPointer->TargetPos=queenStatusPointer->QueenTargetSB->DynPtr->Position;
}
if (queenStatusPointer->moveTimer==0)
{
/* Do setup. */
int tweeiningtime=(Queen_Step_Time>>2);
sbPtr->DynPtr->LinVelocity.vx = 0;
sbPtr->DynPtr->LinVelocity.vy = 0;
sbPtr->DynPtr->LinVelocity.vz = 0;
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,LeftFoot);
{
//which version of sprint should we use
if(DeltaAnimation_IsFinished(queenStatusPointer->attack_delta))
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenGeneral,
(int)QGSS_Sprint_Full,-1,tweeiningtime);
else
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenGeneral,
(int)QGSS_Sprint,-1,tweeiningtime);
queenStatusPointer->HModelController.LoopAfterTweening=1;
queenStatusPointer->moveTimer=1; /* It's something of a state flag here. */
}
break;
case (RightFoot):
/* Argh! Can't start from right foot! */
queenStatusPointer->current_move=QM_Close;
queenStatusPointer->moveTimer=0;
queenStatusPointer->next_move=QM_Charge;
return;
break;
default:
GLOBALASSERT(0);
break;
}
queenStatusPointer->SwerveTimer=(ONE_FIXED/2)+(FastRandom() & 0xffff)*2;
return;
}
/* Check for change foot? */
if (queenStatusPointer->HModelController.Tweening==Controller_NoTweening) {
if (queenStatusPointer->HModelController.keyframe_flags)
{
if (queenStatusPointer->HModelController.keyframe_flags & 1)
{
SetQueenFoot(sbPtr,LeftFoot);
ChangingFoot=TRUE;
}
if (queenStatusPointer->HModelController.keyframe_flags & 2)
{
SetQueenFoot(sbPtr,RightFoot);
ChangingFoot=TRUE;
}
if(queenStatusPointer->moveTimer==1) queenStatusPointer->moveTimer=3;
}
HModel_SetToolsRelativeSpeed(&queenStatusPointer->HModelController,(512*ONE_FIXED)/QUEEN_CHARGE_SPEED);
}
/* Now... turn to face. */
if (queenStatusPointer->moveTimer!=2) {
DISPLAYBLOCK *dPtr;
dPtr=sbPtr->SBdptr;
GLOBALASSERT(dPtr);
ProveHModel(dPtr->HModelControlBlock,dPtr);
#if 1
{
VECTORCH v;
if(queenStatusPointer->QueenTargetSB==Player->ObStrategyBlock && !queenStatusPointer->TempTarget && queenStatusPointer->TargetDistance>5000)
{
//if charging at player don't go in a straight line
#define QUEEN_COS 63302
#define QUEEN_SIN 16961
if(queenStatusPointer->SwerveDirection)
{
v.vx=MUL_FIXED(queenStatusPointer->VectToTarget.vx,QUEEN_COS)+MUL_FIXED(queenStatusPointer->VectToTarget.vz,QUEEN_SIN);
v.vy=0;
v.vz=MUL_FIXED(queenStatusPointer->VectToTarget.vz,QUEEN_COS)-MUL_FIXED(queenStatusPointer->VectToTarget.vx,QUEEN_SIN);
}
else
{
v.vx=MUL_FIXED(queenStatusPointer->VectToTarget.vx,QUEEN_COS)-MUL_FIXED(queenStatusPointer->VectToTarget.vz,QUEEN_SIN);
v.vy=0;
v.vz=MUL_FIXED(queenStatusPointer->VectToTarget.vz,QUEEN_COS)+MUL_FIXED(queenStatusPointer->VectToTarget.vx,QUEEN_SIN);
}
queenStatusPointer->SwerveTimer-=NormalFrameTime;
if(queenStatusPointer->SwerveTimer<=0)
{
//alter swerve direction , and keep it for the next .5 to 2.5 seconds
queenStatusPointer->SwerveTimer=(ONE_FIXED/2)+(FastRandom() & 0xffff)*2;
queenStatusPointer->SwerveDirection=!queenStatusPointer->SwerveDirection;
}
}
else
{
v=queenStatusPointer->VectToTarget;
}
NPCOrientateToVector(sbPtr, &v,Queen_Turn_Rate,NULL);
//NPCOrientateToVector(sbPtr, &queenStatusPointer->VectToTarget,Queen_Turn_Rate,NULL);
}
/* Now, just a normal lin velocity. */
{
if(queenStatusPointer->moveTimer==1)
{
SetQueenMovement_FromFoot(sbPtr);
}
else
{
VECTORCH velocity;
velocity.vx=sbPtr->DynPtr->OrientMat.mat31;
velocity.vy=0;
velocity.vz=sbPtr->DynPtr->OrientMat.mat33;
if ( (velocity.vx==0) && (velocity.vy==0) && (velocity.vz==0) ) {
sbPtr->DynPtr->LinVelocity.vx = 0;
sbPtr->DynPtr->LinVelocity.vy = 0;
sbPtr->DynPtr->LinVelocity.vz = 0;
return;
}
Normalise(&velocity);
sbPtr->DynPtr->LinVelocity.vx = MUL_FIXED(velocity.vx,QUEEN_CHARGE_SPEED);
sbPtr->DynPtr->LinVelocity.vy = MUL_FIXED(velocity.vy,QUEEN_CHARGE_SPEED);
sbPtr->DynPtr->LinVelocity.vz = MUL_FIXED(velocity.vz,QUEEN_CHARGE_SPEED);
}
}
#else
NPCOrientateToVector(sbPtr, &vectotarget,Queen_Turn_Rate,NULL);
SetQueenMovement_FromFoot(sbPtr);
#endif
}
/* Consider exit state. */
if ((queenStatusPointer->HModelController.Tweening==Controller_NoTweening)
&&(queenStatusPointer->moveTimer==2)) {
/* We must have finished. */
queenStatusPointer->current_move=QM_Standby;
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,RightFoot);
break;
case (RightFoot):
SetQueenFoot(sbPtr,LeftFoot);
break;
default:
GLOBALASSERT(0);
break;
}
/* Would you believe the end's in the middle? :-) */
}
if(ChangingFoot)
{
QueenCalculateTargetInfo(sbPtr);
if(queenStatusPointer->PlayingHitDelta)
{
queenStatusPointer->HModelController.Playing=0;
queenStatusPointer->current_move=QM_Standby;
queenStatusPointer->moveTimer=0;
queenStatusPointer->next_move=QM_Standby;
return;
}
//only check for exiting charge , when changing foot
{
/* Only charge if we're facing the right way. */
if (queenStatusPointer->TargetDirection.vz<queenStatusPointer->TargetDirection.vx ||
queenStatusPointer->TargetDirection.vz<-queenStatusPointer->TargetDirection.vx)
{
/* Spin round a bit more. */
queenStatusPointer->HModelController.Playing=0;
queenStatusPointer->current_move=QM_Close;
queenStatusPointer->moveTimer=0;
queenStatusPointer->next_move=QM_Charge;
return;
}
}
if (((queenStatusPointer->TargetDistance<8000 && queenStatusPointer->fixed_foot==LeftFoot && queenStatusPointer->TargetRelSpeed<QUEEN_CLOSE_SPEED) ||queenStatusPointer->next_move!=QM_Standby)&&(queenStatusPointer->moveTimer!=2))
{
queenStatusPointer->HModelController.Playing=0;
queenStatusPointer->current_move=queenStatusPointer->next_move;
queenStatusPointer->moveTimer=0;
if(queenStatusPointer->current_move==QM_Standby)
{
if(queenStatusPointer->QueenTargetSB==Player->ObStrategyBlock && !queenStatusPointer->TempTarget)
queenStatusPointer->current_move=QM_Close;
else
queenStatusPointer->current_move=QM_ComeToPoint;
}
queenStatusPointer->next_move=QM_Standby;
return;
#if 0
/* Begin exit. */
int tweeiningtime=(Queen_Step_Time>>2);
queenStatusPointer->moveTimer=2; /* It's something of a state flag here. */
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenGeneral,
(int)QGSS_Stop_To_Right,(Queen_Step_Time),tweeiningtime);
break;
case (RightFoot):
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenGeneral,
(int)QGSS_Stop_To_Left,(Queen_Step_Time),tweeiningtime);
break;
default:
GLOBALASSERT(0);
break;
}
queenStatusPointer->HModelController.LoopAfterTweening=0;
#endif
}
//check to see if queen should change between sprints
if(queenStatusPointer->fixed_foot==LeftFoot)
{
if(DeltaAnimation_IsFinished(queenStatusPointer->attack_delta))
{
//switch to full sprint if not already doing it
if(queenStatusPointer->HModelController.Sub_Sequence!=QGSS_Sprint_Full)
{
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenGeneral,
(int)QGSS_Sprint_Full,-1,ONE_FIXED>>3);
queenStatusPointer->HModelController.LoopAfterTweening=1;
}
}
else
{
//switch to template sprint if not already doing it
if(queenStatusPointer->HModelController.Sub_Sequence!=QGSS_Sprint)
{
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenGeneral,
(int)QGSS_Sprint,-1,ONE_FIXED>>3);
queenStatusPointer->HModelController.LoopAfterTweening=1;
}
}
}
}
}
void QueenMove_ButtAttack(STRATEGYBLOCK* sbPtr)
{
QUEEN_STATUS_BLOCK *queenStatusPointer;
/* First movement function. */
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer->current_move==QM_ButtAttack);
if (queenStatusPointer->moveTimer==0) {
/* Do setup. */
int tweeiningtime=(ONE_FIXED>>3);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenGeneral,
(int)QGSS_ButtConnect,-1,tweeiningtime);
}
if (queenStatusPointer->HModelController.Tweening==Controller_NoTweening) {
GLOBALASSERT(queenStatusPointer->HModelController.Looped==0);
}
if ((queenStatusPointer->HModelController.Tweening==Controller_NoTweening)
&&(queenStatusPointer->HModelController.sequence_timer==(ONE_FIXED-1)))
{
queenStatusPointer->current_move=QM_Standby;
//finished attack switch back to reconsider
queenStatusPointer->QueenState=QBS_Reconsider;
//queen ends this sequence with right foot forward
SetQueenFoot(sbPtr,RightFoot);
}
sbPtr->DynPtr->LinVelocity.vx = 0;
sbPtr->DynPtr->LinVelocity.vy = 0;
sbPtr->DynPtr->LinVelocity.vz = 0;
queenStatusPointer->moveTimer+=NormalFrameTime;
}
void QueenMove_ButtCharge(STRATEGYBLOCK* sbPtr)
{
QUEEN_STATUS_BLOCK *queenStatusPointer;
BOOL ChangingFoot=FALSE;
/* Very different movement function... */
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer->current_move==QM_ButtCharge);
/* Charge at the player. */
//queenStatusPointer->TargetPos=queenStatusPointer->QueenTargetSB->DynPtr->Position;
if (queenStatusPointer->moveTimer==0)
{
/* Do setup. */
int tweeiningtime=(Queen_Step_Time>>2);
sbPtr->DynPtr->LinVelocity.vx = 0;
sbPtr->DynPtr->LinVelocity.vy = 0;
sbPtr->DynPtr->LinVelocity.vz = 0;
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,LeftFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenGeneral,
(int)QGSS_RunButtAttack,-1,tweeiningtime);
queenStatusPointer->HModelController.LoopAfterTweening=1;
queenStatusPointer->moveTimer=1; /* It's something of a state flag here. */
break;
case (RightFoot):
/* Argh! Can't start from right foot! */
queenStatusPointer->current_move=QM_Close;
queenStatusPointer->moveTimer=0;
queenStatusPointer->next_move=QM_Charge;
return;
break;
default:
GLOBALASSERT(0);
break;
}
return;
}
/* Check for change foot? */
if (queenStatusPointer->HModelController.Tweening==Controller_NoTweening) {
if (queenStatusPointer->HModelController.keyframe_flags)
{
if (queenStatusPointer->HModelController.keyframe_flags & 1)
{
SetQueenFoot(sbPtr,LeftFoot);
ChangingFoot=TRUE;
}
if (queenStatusPointer->HModelController.keyframe_flags & 2)
{
SetQueenFoot(sbPtr,RightFoot);
ChangingFoot=TRUE;
}
if(queenStatusPointer->moveTimer==1) queenStatusPointer->moveTimer=3;
}
HModel_SetToolsRelativeSpeed(&queenStatusPointer->HModelController,(512*ONE_FIXED)/QUEEN_BUTTCHARGE_SPEED);
}
/* Now... turn to face. */
if (queenStatusPointer->moveTimer!=2) {
DISPLAYBLOCK *dPtr;
dPtr=sbPtr->SBdptr;
GLOBALASSERT(dPtr);
ProveHModel(dPtr->HModelControlBlock,dPtr);
NPCOrientateToVector(sbPtr, &queenStatusPointer->VectToTarget,Queen_Turn_Rate,NULL);
/* Now, just a normal lin velocity. */
if(queenStatusPointer->moveTimer==1)
{
SetQueenMovement_FromFoot(sbPtr);
}
else
{
VECTORCH velocity;
velocity.vx=sbPtr->DynPtr->OrientMat.mat31;
velocity.vy=0;
velocity.vz=sbPtr->DynPtr->OrientMat.mat33;
if ( (velocity.vx==0) && (velocity.vy==0) && (velocity.vz==0) ) {
sbPtr->DynPtr->LinVelocity.vx = 0;
sbPtr->DynPtr->LinVelocity.vy = 0;
sbPtr->DynPtr->LinVelocity.vz = 0;
return;
}
Normalise(&velocity);
//runSpeed=DIV_FIXED(Queen_Charge_Step_Speed,Queen_ButtCharge_Rate);
sbPtr->DynPtr->LinVelocity.vx = MUL_FIXED(velocity.vx,QUEEN_BUTTCHARGE_SPEED);
sbPtr->DynPtr->LinVelocity.vy = MUL_FIXED(velocity.vy,QUEEN_BUTTCHARGE_SPEED);
sbPtr->DynPtr->LinVelocity.vz = MUL_FIXED(velocity.vz,QUEEN_BUTTCHARGE_SPEED);
}
}
if(ChangingFoot)
{
int range;
QueenCalculateTargetInfo(sbPtr);
range=queenStatusPointer->TargetDistance;
//only check for exiting charge , when changing foot
if(queenStatusPointer->PlayingHitDelta)
{
queenStatusPointer->HModelController.Playing=0;
queenStatusPointer->current_move=QM_Standby;
queenStatusPointer->moveTimer=0;
queenStatusPointer->next_move=QM_Standby;
return;
}
if(PlayerInLocker)
{
//stop using butt charge , since the queen can't actually get to the player
queenStatusPointer->moveTimer=0;
queenStatusPointer->current_move=QM_Charge;
queenStatusPointer->next_move=QM_Standby;
return;
}
if (queenStatusPointer->TargetDirection.vz<queenStatusPointer->TargetDirection.vx ||
queenStatusPointer->TargetDirection.vz<-queenStatusPointer->TargetDirection.vx)
{
/* Spin round a bit more. */
queenStatusPointer->HModelController.Playing=0;
queenStatusPointer->current_move=QM_Close;
queenStatusPointer->moveTimer=0;
queenStatusPointer->next_move=QM_Standby;
return;
}
if (queenStatusPointer->moveTimer!=2 && queenStatusPointer->next_move!=QM_Standby)
{
queenStatusPointer->HModelController.Playing=0;
queenStatusPointer->current_move=queenStatusPointer->next_move;
queenStatusPointer->next_move=QM_Standby;
queenStatusPointer->moveTimer=0;
return;
}
}
}
void QueenMove_Climb(STRATEGYBLOCK* sbPtr)
{
QUEEN_STATUS_BLOCK *queenStatusPointer;
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer->current_move==QM_Climbing);
if (queenStatusPointer->moveTimer==0)
{
//just starting to climb out , so start the animation sequence
int tweeiningtime=(Queen_Step_Time>>2);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenGeneral,
(int)QGSS_ClimbOut,-1,tweeiningtime);
queenStatusPointer->HModelController.Playing=1;
//set the start position of this maneuver
queenStatusPointer->ClimbStartPosition=sbPtr->DynPtr->Position;
//while climbing out queen needs to ignore gravity and collisions
sbPtr->DynPtr->GravityOn=0;
sbPtr->DynPtr->OnlyCollideWithObjects=1;
/*moveTimer is being used as a state flag again*/
queenStatusPointer->moveTimer=1;
}
/*Adjust the queen's facing*/
{
VECTORCH direction={-ONE_FIXED,0,0};
NPCOrientateToVector(sbPtr, &direction,Queen_Turn_Rate,NULL);
}
/*If the queen has stopped tweening , then we need to deal with her movement*/
if (queenStatusPointer->HModelController.Tweening==Controller_NoTweening)
{
#define QueenClimbTime1 7598
#define QueenClimbTime2 16146
#define QueenClimbTime3 42740
ProveHModel(sbPtr->SBdptr->HModelControlBlock,sbPtr->SBdptr);
{
VECTORCH StageOneMovement={0,-6771,0};
VECTORCH StageTwoMovement={-3266,0,-372};
//work out where the queen should be
VECTORCH newPosition=queenStatusPointer->ClimbStartPosition;
int timer=queenStatusPointer->HModelController.sequence_timer;
if(timer<QueenClimbTime1)
{
//no movement
}
else if(timer<QueenClimbTime2)
{
int scale=DIV_FIXED(timer-QueenClimbTime1,QueenClimbTime2-QueenClimbTime1);
newPosition.vx+=MUL_FIXED(StageOneMovement.vx,scale);
newPosition.vy+=MUL_FIXED(StageOneMovement.vy,scale);
newPosition.vz+=MUL_FIXED(StageOneMovement.vz,scale);
}
else if(timer<QueenClimbTime3)
{
int scale=DIV_FIXED(timer-QueenClimbTime2,QueenClimbTime3-QueenClimbTime2);
AddVector(&StageOneMovement,&newPosition);
newPosition.vx+=MUL_FIXED(StageTwoMovement.vx,scale);
newPosition.vy+=MUL_FIXED(StageTwoMovement.vy,scale);
newPosition.vz+=MUL_FIXED(StageTwoMovement.vz,scale);
}
else
{
AddVector(&StageOneMovement,&newPosition);
AddVector(&StageTwoMovement,&newPosition);
}
sbPtr->DynPtr->Displacement=newPosition;
SubVector(&sbPtr->DynPtr->Position,&sbPtr->DynPtr->Displacement);
}
if(queenStatusPointer->HModelController.sequence_timer>62000)
{
//the queen has finished getting out
queenStatusPointer->current_move=QM_Standby;
queenStatusPointer->next_move=QM_Standby;
queenStatusPointer->moveTimer=0;
QueenForceReconsider(sbPtr);
sbPtr->DynPtr->GravityOn=1;
sbPtr->DynPtr->OnlyCollideWithObjects=0;
//the queen ends in right stance
queenStatusPointer->fixed_foot=RightFoot;
}
}
}
#define Queen_Swipe_Left 0
#define Queen_Swipe_Right 1
#define Queen_Swipe_Left_Low 2
#define Queen_Swipe_Right_Low 3
void Queen_Do_Swipe(STRATEGYBLOCK *sbPtr,int side)
{
QUEEN_STATUS_BLOCK *queenStatusPointer;
VECTORCH vectohand,targetpos;
int range_to_player;
SECTION_DATA *hand_section;
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
/*Is the queen already doing a swipe?*/
if ((queenStatusPointer->attack_delta->timer==(ONE_FIXED-1))
||(queenStatusPointer->attack_delta->timer==0))
{
/* She isn't , so start it */
switch (side)
{
case Queen_Swipe_Left :
Start_Delta_Sequence(queenStatusPointer->attack_delta,
(int)HMSQT_QueenRightStanceTemplate,(int)QRSTSS_LeftSwipe,ONE_FIXED);
break;
case Queen_Swipe_Right :
Start_Delta_Sequence(queenStatusPointer->attack_delta,
(int)HMSQT_QueenRightStanceTemplate,(int)QRSTSS_RightSwipe,ONE_FIXED);
break;
case Queen_Swipe_Left_Low :
Start_Delta_Sequence(queenStatusPointer->attack_delta,
(int)HMSQT_QueenRightStanceTemplate,(int)QRSTSS_LeftSwipe_Low,ONE_FIXED);
break;
case Queen_Swipe_Right_Low :
Start_Delta_Sequence(queenStatusPointer->attack_delta,
(int)HMSQT_QueenRightStanceTemplate,(int)QRSTSS_RightSwipe_Low,ONE_FIXED);
break;
default :
LOCALASSERT(1==0);
}
queenStatusPointer->attack_delta->Playing=1;
queenStatusPointer->attack_delta->Looped=0;
queenStatusPointer->AttackDoneItsDamage=FALSE;
}
if(queenStatusPointer->attack_delta->timer>37000 && !queenStatusPointer->AttackDoneItsDamage)
{
queenStatusPointer->AttackDoneItsDamage=TRUE;
//get the hand corresponding to the currently playing swipe
switch(queenStatusPointer->attack_delta->sub_sequence)
{
case QRSTSS_RightSwipe:
case QRSTSS_RightSwipe_Low:
hand_section=GetThisSectionData(queenStatusPointer->HModelController.section_data,"right hand");
break;
case QRSTSS_LeftSwipe:
case QRSTSS_LeftSwipe_Low:
hand_section=GetThisSectionData(queenStatusPointer->HModelController.section_data,"left hand");
break;
default :
LOCALASSERT(1==0);
}
GetTargetingPointOfObject(Player,&targetpos);
vectohand.vx=targetpos.vx-hand_section->World_Offset.vx;
vectohand.vy=0;//targetpos.vy-hand_section->World_Offset.vy;
vectohand.vz=targetpos.vz-hand_section->World_Offset.vz;
range_to_player=Approximate3dMagnitude(&vectohand);
//see if queen hit an intervening object
{
VECTORCH direction;
direction.vx=targetpos.vx-hand_section->World_Offset.vx;
direction.vy=targetpos.vy-hand_section->World_Offset.vy;
direction.vz=targetpos.vz-hand_section->World_Offset.vz;
Normalise(&direction);
LOS_ObjectHitPtr=0;
FindPolygonInLineOfSight(&direction,&hand_section->World_Offset,1,sbPtr->SBdptr);
if(LOS_ObjectHitPtr)
{
if(LOS_ObjectHitPtr->ObStrategyBlock)
{
if(LOS_ObjectHitPtr->ObStrategyBlock->I_SBtype==I_BehaviourInanimateObject ||
LOS_ObjectHitPtr->ObStrategyBlock->I_SBtype==I_BehaviourTrackObject)
{
//damage the object instead of the player
//if(LOS_Lambda<QueenAttackRange)
{
CauseDamageToObject(LOS_ObjectHitPtr->ObStrategyBlock,&TemplateAmmo[AMMO_NPC_PAQ_CLAW].MaxDamage[AvP.Difficulty], ONE_FIXED,NULL);
}
return;
}
}
}
}
/* ATM, target is always player. */
if (range_to_player<QueenAttackRange)
{
//do from .75 to 1.25 times base damage
CauseDamageToObject(Player->ObStrategyBlock,&TemplateAmmo[AMMO_NPC_PAQ_CLAW].MaxDamage[AvP.Difficulty],(ONE_FIXED*.75)+(FastRandom()&0x7fff),NULL);
//set the taunt timer
queenStatusPointer->QueenTauntTimer=ONE_FIXED/2;
}
}
}
void QueenMove_Close(STRATEGYBLOCK *sbPtr) {
QUEEN_STATUS_BLOCK *queenStatusPointer;
/* First movement function. */
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer->current_move==QM_Close);
if (queenStatusPointer->moveTimer==0)
{
/* Do setup. */
int tweeiningtime=(ONE_FIXED>>3);
sbPtr->DynPtr->LinVelocity.vx = 0;
sbPtr->DynPtr->LinVelocity.vy = 0;
sbPtr->DynPtr->LinVelocity.vz = 0;
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,LeftFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenLeftStanceTemplate,
(int)QLSTSS_Forward_L2R,-1,tweeiningtime);
queenStatusPointer->HModelController.LoopAfterTweening=0;
break;
case (RightFoot):
SetQueenFoot(sbPtr,RightFoot);
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenRightStanceTemplate,
(int)QRSTSS_Forward_R2L,-1,tweeiningtime);
queenStatusPointer->HModelController.LoopAfterTweening=0;
break;
default:
GLOBALASSERT(0);
break;
}
/* Go! */
queenStatusPointer->moveTimer++;
GLOBALASSERT(queenStatusPointer->HModelController.Looped==0);
return;
}
if (queenStatusPointer->HModelController.Tweening==Controller_NoTweening)
{
BOOL moveHalfSpeed=FALSE;
if (queenStatusPointer->HModelController.Looped!=0) {
/* Quirkafleeg */
GLOBALASSERT(queenStatusPointer->HModelController.Looped==0);
}
QueenCalculateTargetInfo(sbPtr);
//if the queen is close to her target , and the target is not in front of her
//then she needs to slow down to reduce her turning circle
if(queenStatusPointer->TargetDistance<3000)
{
if(queenStatusPointer->TargetDirection.vz<queenStatusPointer->TargetDirection.vx ||
queenStatusPointer->TargetDirection.vz<-queenStatusPointer->TargetDirection.vx)
{
moveHalfSpeed=TRUE;
}
}
if(moveHalfSpeed)
HModel_SetToolsRelativeSpeed(&queenStatusPointer->HModelController,(512*ONE_FIXED)/(QUEEN_CLOSE_SPEED/2));
else
HModel_SetToolsRelativeSpeed(&queenStatusPointer->HModelController,(512*ONE_FIXED)/QUEEN_CLOSE_SPEED);
}
/* Now... turn to face. */
{
DISPLAYBLOCK *dPtr;
dPtr=sbPtr->SBdptr;
GLOBALASSERT(dPtr);
NPCOrientateToVector(sbPtr,&queenStatusPointer->VectToTarget,Queen_Turn_Rate,NULL);
#if 1
SetQueenMovement_FromFoot(sbPtr);
#else
{
VECTORCH velocity;
int dotProduct;
int runSpeed;
velocity.vx=sbPtr->DynPtr->OrientMat.mat31;
velocity.vy=0;
velocity.vz=sbPtr->DynPtr->OrientMat.mat33;
if ( (velocity.vx==0) && (velocity.vy==0) && (velocity.vz==0) ) {
sbPtr->DynPtr->LinVelocity.vx = 0;
sbPtr->DynPtr->LinVelocity.vy = 0;
sbPtr->DynPtr->LinVelocity.vz = 0;
return;
}
Normalise(&velocity);
sbPtr->DynPtr->LinVelocity.vx = MUL_FIXED(velocity.vx,QUEEN_CLOSE_SPEED);
sbPtr->DynPtr->LinVelocity.vy = MUL_FIXED(velocity.vy,QUEEN_CLOSE_SPEED);
sbPtr->DynPtr->LinVelocity.vz = MUL_FIXED(velocity.vz,QUEEN_CLOSE_SPEED);
}
#endif
}
queenStatusPointer->moveTimer+=NormalFrameTime;
if ((queenStatusPointer->HModelController.Tweening==Controller_NoTweening)
&&(queenStatusPointer->HModelController.sequence_timer==(ONE_FIXED-1))) {
queenStatusPointer->current_move=QM_Standby;
switch (queenStatusPointer->fixed_foot) {
case (LeftFoot):
SetQueenFoot(sbPtr,RightFoot);
break;
case (RightFoot):
SetQueenFoot(sbPtr,LeftFoot);
break;
default:
GLOBALASSERT(0);
break;
}
queenStatusPointer->moveTimer=0;
queenStatusPointer->current_move=queenStatusPointer->next_move;
queenStatusPointer->next_move=QM_Standby;
}
}
void QueenIsDamaged(STRATEGYBLOCK *sbPtr, DAMAGE_PROFILE *damage, int multiple,SECTION_DATA *Section, VECTORCH *incoming, VECTORCH *point) {
QUEEN_STATUS_BLOCK *queenStatusPointer;
GLOBALASSERT(sbPtr);
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer);
/* Ouch. */
if (sbPtr->SBDamageBlock.Health <= 0) {
if (queenStatusPointer->QueenState!=QBS_Dead) {
KillQueen(sbPtr,damage,multiple,Section,incoming);
}
return;
}
//scream a bit
QueenSoundHurt(sbPtr);
GLOBALASSERT(damage);
if(damage->ExplosivePower && incoming)
{
/*
Don't allow the queen to be stunned if she is climbing out of the airlock.
This would screw up the sequence to much.
*/
if(queenStatusPointer->current_move!=QM_Climbing)
{
if(!QueenPlayingStunAnimation(&queenStatusPointer->HModelController))
{
if((damage->ExplosivePower==2)||(damage->ExplosivePower==6))
{
//big explosion, play fall over anim
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenGeneral,(int)QGSS_Explosion_Stun,-1,ONE_FIXED>>3);
queenStatusPointer->HModelController.LoopAfterTweening=0;
queenStatusPointer->current_move=QM_Stun;
queenStatusPointer->PlayingHitDelta=TRUE;
QueenForceReconsider(sbPtr);
//the queen ends in right stance
queenStatusPointer->fixed_foot=RightFoot;
}
else
{
//play a hit delta if not already doing so
if ((queenStatusPointer->hit_delta->timer==(ONE_FIXED-1))
||(queenStatusPointer->hit_delta->timer==0))
{
VECTORCH dir=*incoming;
MATRIXCH WtoL=sbPtr->DynPtr->OrientMat;
TransposeMatrixCH(&WtoL);
RotateVector(&dir,&WtoL);
if(dir.vx>0)
{
Start_Delta_Sequence(queenStatusPointer->hit_delta,
(int)HMSQT_QueenRightStanceTemplate,(int)QRSTSS_RightHit,-1);
queenStatusPointer->hit_delta->Playing=1;
}
else
{
Start_Delta_Sequence(queenStatusPointer->hit_delta,
(int)HMSQT_QueenRightStanceTemplate,(int)QRSTSS_LeftHit,-1);
queenStatusPointer->hit_delta->Playing=1;
}
queenStatusPointer->PlayingHitDelta=TRUE;
QueenForceReconsider(sbPtr);
}
}
}
}
}
if(AvP.PlayerType==I_Marine && Section)
{
//don't allow the marine to completely destroy a section
if(Section->current_damage.Health<=0)
{
Section->current_damage.Health=1;
}
}
}
void Execute_Queen_Dying(STRATEGYBLOCK *sbPtr) {
QUEEN_STATUS_BLOCK *queenStatusPointer;
GLOBALASSERT(sbPtr);
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer);
/* Here for completeness. Queens never melt away. */
}
void KillQueen(STRATEGYBLOCK *sbPtr,DAMAGE_PROFILE *damage, int multiple,SECTION_DATA *Section, VECTORCH *incoming)
{
QUEEN_STATUS_BLOCK *queenStatusPointer;
int tkd;
/* Oh my God! They've killed Queenie! */
GLOBALASSERT(sbPtr);
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer);
/* make a sound. Just this once without a check. */
Sound_Play(SID_ALIEN_KILL,"d",&sbPtr->DynPtr->Position);
/*If queen has a death target ,send a request*/
if(queenStatusPointer->death_target_sbptr)
{
RequestState(queenStatusPointer->death_target_sbptr,queenStatusPointer->death_target_request, 0);
}
/* Queens never gibb, they're that hard. */
tkd=TotalKineticDamage(damage);
/* Deal with sequence. */
RemoveAllDeltas(&queenStatusPointer->HModelController);
if (tkd>200) {
//SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenGeneral,QGSS_Explode_Death,(ONE_FIXED),(ONE_FIXED>>2));
/* That death doesn't work. */
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenGeneral,QGSS_FaceDeath,(ONE_FIXED),(ONE_FIXED>>2));
} else {
SetQueenShapeAnimSequence_Core(sbPtr,HMSQT_QueenGeneral,QGSS_FaceDeath,(ONE_FIXED),(ONE_FIXED>>2));
}
//if the queen is carrying an object , release it and reset gravity on it
if(queenStatusPointer->QueenState==QBS_CarryingObject)
{
if(!QueenObjectList[queenStatusPointer->CurrentQueenObject]->SBflags.destroyed_but_preserved)
{
QueenObjectList[queenStatusPointer->CurrentQueenObject]->DynPtr->GravityOn=1;
QueenObjectList[queenStatusPointer->CurrentQueenObject]->DynPtr->OnlyCollideWithEnvironment=0;
queenStatusPointer->CurrentQueenObject=-1;
}
}
/* More restrained death than before... */
{
queenStatusPointer->QueenState=QBS_Dead;
/* 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;
}
}
void FindQueenObjects()
{
extern int NumActiveStBlocks;
extern STRATEGYBLOCK *ActiveStBlockList[];
int sbIndex = 0;
STRATEGYBLOCK *sbPtr;
NumQueenObjects=0;
//search through all the strategies for the throwable objects
//also look for the special airlock doors
UpperAirlockDoorSbptr=0;
LowerAirlockDoorSbptr=0;
LockerDoorSbptr=0;
UpperAirlockDoorOpen=FALSE;
LowerAirlockDoorOpen=FALSE;
while(sbIndex < NumActiveStBlocks)
{
sbPtr = ActiveStBlockList[sbIndex++];
if(sbPtr->name)
{
if(strstr(sbPtr->name,"QueenAmmo"))
{
if(NumQueenObjects<QUEEN_MAX_OBJECT)
{
QueenObjectList[NumQueenObjects++]=sbPtr;
GLOBALASSERT(sbPtr->DynPtr);
sbPtr->SBflags.preserve_until_end_of_level=1;
}
}
else if(!strcmp(sbPtr->name,"a1door"))
{
UpperAirlockDoorSbptr=sbPtr;
UpperAirlockDoorStart=sbPtr->DynPtr->Position;
}
else if(!strcmp(sbPtr->name,"a3door"))
{
LowerAirlockDoorSbptr=sbPtr;
LowerAirlockDoorStart=sbPtr->DynPtr->Position;
}
else if(!strcmp(sbPtr->name,"locker door"))
{
LockerDoorSbptr=sbPtr;
sbPtr->SBflags.preserve_until_end_of_level=1;
}
}
}
}
BOOL CalculateTrajectory(VECTORCH* start,VECTORCH* dest,VECTORCH* velocity,int obj_speed,VECTORCH* result)
{
VECTORCH normal;
VECTORCH rotated_vel;
VECTORCH rotated_result;
int closing_speed;
int distance;
int vertical_distance;
int time_to_target;
//get a normalised vector from start to destination
normal=*dest;
SubVector(start,&normal);
vertical_distance=normal.vy-1000;
normal.vy=0;
if(!normal.vx && !normal.vz)
return FALSE;
distance=Magnitude(&normal);
Normalise(&normal);
//apply rotation to velocity that would rotate the normal to (ONE_FIXED,0,0)
rotated_vel.vx=MUL_FIXED(velocity->vx,normal.vx)+MUL_FIXED(velocity->vz,normal.vz);
rotated_vel.vy=0;
rotated_vel.vz=MUL_FIXED(velocity->vx,-normal.vz)+MUL_FIXED(velocity->vz,normal.vx);
if(rotated_vel.vz>=obj_speed || -rotated_vel.vz>=obj_speed)
{
//no hope of hitting
return FALSE;
}
rotated_result.vz=rotated_vel.vz;
//calculate x component using floats
{
float z=(float)rotated_result.vz;
float speed=(float)obj_speed;
float x=sqrt(speed*speed-z*z);
rotated_result.vx=x;
}
closing_speed=rotated_result.vx-rotated_vel.vx;
if(closing_speed<=0)
{
//can't hit
return FALSE;
}
time_to_target=DIV_FIXED(distance,closing_speed);
if(time_to_target>(3*ONE_FIXED))
{
//take to long to hit target
return FALSE;
}
//rotate result back
result->vx=MUL_FIXED(rotated_result.vx,normal.vx)+MUL_FIXED(rotated_result.vz,-normal.vz);
result->vy=0;
result->vz=MUL_FIXED(rotated_result.vx,normal.vz)+MUL_FIXED(rotated_result.vz,normal.vx);
//calculate required up component
//u=s/t - a*t/2
result->vy=DIV_FIXED(vertical_distance,time_to_target)-MUL_FIXED(time_to_target,GRAVITY_STRENGTH/2);
//we have a targeting solution.
return TRUE;
}
//calculate target's relative position
static void QueenCalculateTargetInfo(STRATEGYBLOCK *sbPtr)
{
QUEEN_STATUS_BLOCK *queenStatusPointer;
GLOBALASSERT(sbPtr);
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
GLOBALASSERT(queenStatusPointer);
if(queenStatusPointer->TargetInfoValid) return;
{
MATRIXCH WtoL;
//get vector from queen to target
queenStatusPointer->VectToTarget=queenStatusPointer->TargetPos;
SubVector(&sbPtr->DynPtr->Position,&queenStatusPointer->VectToTarget);
queenStatusPointer->VectToTarget.vy=0;
//get length of vector, and then normalise it
queenStatusPointer->TargetDistance=Approximate3dMagnitude(&queenStatusPointer->VectToTarget);
Normalise(&queenStatusPointer->VectToTarget);
//rotate vector to queen's local space to get relative direction
queenStatusPointer->TargetDirection=queenStatusPointer->VectToTarget;
WtoL=sbPtr->DynPtr->OrientMat;
TransposeMatrixCH(&WtoL);
RotateVector(&queenStatusPointer->TargetDirection,&WtoL);
queenStatusPointer->TargetRelSpeed=0;
if(!queenStatusPointer->TempTarget && queenStatusPointer->QueenTargetSB)
{
if(queenStatusPointer->QueenTargetSB->DynPtr)
{
VECTORCH facing;
facing.vx=sbPtr->DynPtr->OrientMat.mat31;
facing.vy=sbPtr->DynPtr->OrientMat.mat32;
facing.vz=sbPtr->DynPtr->OrientMat.mat33;
queenStatusPointer->TargetRelSpeed=DotProduct(&facing,&queenStatusPointer->QueenTargetSB->DynPtr->LinVelocity);
}
}
}
queenStatusPointer->TargetInfoValid=TRUE;
}
void QueenForceReconsider(STRATEGYBLOCK* sbPtr)
{
QUEEN_STATUS_BLOCK *queenStatusPointer;
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
if(queenStatusPointer->QueenState==QBS_Reconsider) return;
if(queenStatusPointer->QueenState==QBS_CarryingObject)
{
//need to drop the object
if(queenStatusPointer->CurrentQueenObject!=-1)
{
//if the object has been destroyed anywa, don't need to worry about dropping it
if(!QueenObjectList[queenStatusPointer->CurrentQueenObject]->SBflags.destroyed_but_preserved)
{
//put gravity back on
QueenObjectList[queenStatusPointer->CurrentQueenObject]->DynPtr->GravityOn=1;
QueenObjectList[queenStatusPointer->CurrentQueenObject]->DynPtr->OnlyCollideWithEnvironment=0;
{
MATRIXCH id_mat={ONE_FIXED,0,0,0,ONE_FIXED,0,0,0,ONE_FIXED};
QueenObjectList[queenStatusPointer->CurrentQueenObject]->DynPtr->OrientMat=id_mat;
}
//give the object a slight impulse away from the queen
QueenObjectList[queenStatusPointer->CurrentQueenObject]->DynPtr->LinImpulse.vx+=sbPtr->DynPtr->OrientMat.mat31/10;
QueenObjectList[queenStatusPointer->CurrentQueenObject]->DynPtr->LinImpulse.vz+=sbPtr->DynPtr->OrientMat.mat33/10;
}
}
}
queenStatusPointer->CurrentQueenObject=-1;
queenStatusPointer->QueenState=QBS_Reconsider;
queenStatusPointer->QueenTargetSB=Player->ObStrategyBlock;
queenStatusPointer->TempTarget=FALSE;
queenStatusPointer->TargetInfoValid=FALSE;
queenStatusPointer->TargetPos=queenStatusPointer->QueenTargetSB->DynPtr->Position;
}
#define AirlockMinX 33882
#define AirlockMaxX 45542
#define AirlockMinZ -13936
#define AirlockMaxZ -6266
#define AirlockCentreX ((AirlockMinX+AirlockMaxX)/2)
#define AirlockCentreZ ((AirlockMinZ+AirlockMaxZ)/2)
#define AirlockY 12000
#define HangarFloorLevel 3950
#define AirlockOpeningDistance 2500
#define AirlockOffset 1000
void QueenCheckForAvoidAirlock(STRATEGYBLOCK *sbPtr)
{
//only need to look out for the airlock in hangar
if(!stricmp(LevelName,"hangar"))
{
QUEEN_STATUS_BLOCK *queenStatusPointer =(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
VECTORCH* qpos=&sbPtr->DynPtr->Position;
VECTORCH* tpos=&queenStatusPointer->TargetPos;
if(!UpperAirlockDoorOpen) return;
if(PlayerInTrench) return;
if(queenStatusPointer->QueenState==QBS_ClimbingOutOfAirlock)
{
//not much point in trying to avoid airlock now!
return;
}
if(!queenStatusPointer->BeenInAirlock)
{
//if the queen hasn't fallen in yet , then she isn't careful while
//charging at the player
if(queenStatusPointer->current_move==QM_ButtCharge)
{
return;
}
}
if(qpos->vx>AirlockMaxX-1000)
{
if(tpos->vx<AirlockMaxX-1000)
{
//queen and target are on opposite sides of the max X side
//find out where the queen's path will intersect the side
int scale=DIV_FIXED((AirlockMaxX-1000)-qpos->vx,tpos->vx-qpos->vx);
int zintercept=MUL_FIXED(tpos->vz-qpos->vz,scale)+qpos->vz;
if(zintercept>=AirlockMinZ && zintercept<=AirlockMaxZ)
{
// textprint("Airlock Max X\n");
// return;
//head for the corner that is closest to the current target
queenStatusPointer->TargetPos.vx=AirlockMaxX;
if(abs(AirlockMinZ-tpos->vz)<abs(AirlockMaxZ-tpos->vz))
{
queenStatusPointer->TargetPos.vz=AirlockMinZ;
}
else
{
queenStatusPointer->TargetPos.vz=AirlockMaxZ;
}
queenStatusPointer->TempTarget=TRUE;
queenStatusPointer->TempTargetTimer=ONE_FIXED;
queenStatusPointer->TargetInfoValid=FALSE;
return;
}
}
}
else if(qpos->vx<AirlockMinX+1000)
{
if(tpos->vx>AirlockMinX+1000)
{
//queen and target are on opposite sides of the min X side
//find out where the queen's path will intersect the side
int scale=DIV_FIXED(AirlockMinX+1000-qpos->vx,tpos->vx-qpos->vx);
int zintercept=MUL_FIXED(tpos->vz-qpos->vz,scale)+qpos->vz;
if(zintercept>=AirlockMinZ && zintercept<=AirlockMaxZ)
{
// textprint("Airlock Min X\n");
// return;
//head for the corner that is closest to the current target
queenStatusPointer->TargetPos.vx=AirlockMinX;
if(abs(AirlockMinZ-tpos->vz)<abs(AirlockMaxZ-tpos->vz))
{
queenStatusPointer->TargetPos.vz=AirlockMinZ;
}
else
{
queenStatusPointer->TargetPos.vz=AirlockMaxZ;
}
queenStatusPointer->TempTarget=TRUE;
queenStatusPointer->TempTargetTimer=ONE_FIXED;
queenStatusPointer->TargetInfoValid=FALSE;
return;
}
}
}
if(qpos->vz>AirlockMaxZ-1000)
{
if(tpos->vz<AirlockMaxZ-1000)
{
//queen and target are on opposite sides of the max Z side
//find out where the queen's path will intersect the side
int scale=DIV_FIXED((AirlockMaxZ-1000)-qpos->vz,tpos->vz-qpos->vz);
int xintercept=MUL_FIXED(tpos->vx-qpos->vx,scale)+qpos->vx;
if(xintercept>=AirlockMinX && xintercept<=AirlockMaxX)
{
// textprint("Airlock Max Z\n");
// return;
//head for the corner that is closest to the current target
queenStatusPointer->TargetPos.vz=AirlockMaxZ;
if(abs(AirlockMinX-tpos->vx)<abs(AirlockMaxX-tpos->vx))
{
queenStatusPointer->TargetPos.vx=AirlockMinX;
}
else
{
queenStatusPointer->TargetPos.vx=AirlockMaxX;
}
queenStatusPointer->TempTarget=TRUE;
queenStatusPointer->TempTargetTimer=ONE_FIXED;
queenStatusPointer->TargetInfoValid=FALSE;
return;
}
}
}
else if(qpos->vz<AirlockMinZ+1000)
{
if(tpos->vz>AirlockMinZ+1000)
{
//queen and target are on opposite sides of the min Z side
//find out where the queen's path will intersect the side
int scale=DIV_FIXED(AirlockMinZ+1000-qpos->vz,tpos->vz-qpos->vz);
int xintercept=MUL_FIXED(tpos->vx-qpos->vx,scale)+qpos->vx;
if(xintercept>=AirlockMinX && xintercept<=AirlockMaxX)
{
// textprint("Airlock Min Z\n");
// return;
//head for the corner that is closest to the current target
queenStatusPointer->TargetPos.vz=AirlockMinZ;
if(abs(AirlockMinX-tpos->vx)<abs(AirlockMaxX-tpos->vx))
{
queenStatusPointer->TargetPos.vx=AirlockMinX;
}
else
{
queenStatusPointer->TargetPos.vx=AirlockMaxX;
}
queenStatusPointer->TempTarget=TRUE;
queenStatusPointer->TempTargetTimer=ONE_FIXED;
queenStatusPointer->TargetInfoValid=FALSE;
return;
}
}
}
textprint("Airlock not in the way\n");
}
}
#define HangarLockerMinX 17104
#define HangarLockerMaxX 19680
#define HangarLockerMinZ -32700
#define HangarLockerMaxZ -29661
#define HangarLockerCentreZ ((HangarLockerMinZ+HangarLockerMaxZ)/2)
BOOL ObjectIsInAirlock(STRATEGYBLOCK* sbPtr)
{
GLOBALASSERT(sbPtr);
GLOBALASSERT(sbPtr->DynPtr);
if(sbPtr->DynPtr->Position.vx>AirlockMinX && sbPtr->DynPtr->Position.vx<AirlockMaxX &&
sbPtr->DynPtr->Position.vz>AirlockMinZ && sbPtr->DynPtr->Position.vz<AirlockMaxZ &&
sbPtr->DynPtr->Position.vy>(HangarFloorLevel+1000))
{
return TRUE;
}
return FALSE;
}
void QueenPickupTargetObject(STRATEGYBLOCK *sbPtr)
{
QUEEN_STATUS_BLOCK *queenStatusPointer;
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
//queen must be going for an object
if(queenStatusPointer->QueenState!=QBS_GoingForObject) return;
//disable gravity for object , while it is being carried
queenStatusPointer->QueenTargetSB->DynPtr->GravityOn=0;
//also stop it colliding with the queen
queenStatusPointer->QueenTargetSB->DynPtr->OnlyCollideWithEnvironment=1;
//change queen's state
queenStatusPointer->QueenState=QBS_CarryingObject;
queenStatusPointer->QueenStateTimer=0;
//now heading for the player
queenStatusPointer->QueenTargetSB=Player->ObStrategyBlock;
queenStatusPointer->TargetPos=queenStatusPointer->QueenTargetSB->DynPtr->Position;
queenStatusPointer->TargetInfoValid=FALSE;
queenStatusPointer->next_move=QM_Close;
}
void QueenBehaviour(STRATEGYBLOCK *sbPtr)
{
QUEEN_STATUS_BLOCK *queenStatusPointer;
DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
int DistanceToPlayer;
BOOL JumpDesired=FALSE;
BOOL ConsiderJumping=FALSE;
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
if(NumQueenObjects==-1)
{
//first frame...
//find all objects that the queen can chuck
FindQueenObjects();
queenStatusPointer->CurrentQueenObject=-1;
//get the queens hand section
queenStatusPointer->QueenRightHand=GetThisSectionData(queenStatusPointer->HModelController.section_data,"rrrr fing");
GLOBALASSERT(queenStatusPointer->QueenRightHand);
}
if(!queenStatusPointer->QueenActivated)
{
//is the queen in a visible module?
GLOBALASSERT(sbPtr->containingModule);
if(ModuleCurrVisArray[sbPtr->containingModule->m_index]==2)
{
queenStatusPointer->QueenActivated=TRUE;
}
else
{
return;
}
}
HandleHangarAirlock();
if(queenStatusPointer->QueenState==QBS_Dead)
{
//check for victory on marine level.
if(!stricmp(LevelName,"hangar"))
{
//is the player in the locker ('hangar')
if(Player->ObWorld.vx> HangarLockerMinX && Player->ObWorld.vx < HangarLockerMaxX &&
Player->ObWorld.vz> HangarLockerMinZ && Player->ObWorld.vz < HangarLockerMaxZ)
PlayerInLocker=TRUE;
else
PlayerInLocker=FALSE;
//In the marine level the player can only win if he is inside the locker room area ,and
//the locker door is shut
if((PlayerInLocker && LockerDoorIsClosed()) ||
!(LowerAirlockDoorOpen && UpperAirlockDoorOpen))
{
AvP.LevelCompleted=1;
}
}
Execute_Queen_Dying(sbPtr);
return;
}
/*----------------------------------**
** Check state of delta animations **
**----------------------------------*/
if(queenStatusPointer->PlayingHitDelta)
{
//check to see if the queen is still stunned from an explosion
if (!QueenPlayingStunAnimation(&queenStatusPointer->HModelController) && DeltaAnimation_IsFinished(queenStatusPointer->hit_delta))
{
queenStatusPointer->hit_delta->Playing=0;
queenStatusPointer->hit_delta->timer=0;
queenStatusPointer->PlayingHitDelta=FALSE;
queenStatusPointer->current_move=QM_Close;
queenStatusPointer->next_move=QM_Standby;
queenStatusPointer->moveTimer=0;
}
}
if(DeltaAnimation_IsFinished(queenStatusPointer->attack_delta))
{
queenStatusPointer->attack_delta->Playing=0;
queenStatusPointer->attack_delta->timer=0;
}
queenStatusPointer->TargetInfoValid=FALSE;
if(queenStatusPointer->CurrentQueenObject!=-1)
{
if(QueenObjectList[queenStatusPointer->CurrentQueenObject]->SBflags.destroyed_but_preserved)
{
//the object that the queen was going for has been destroyed
queenStatusPointer->CurrentQueenObject=-1;
queenStatusPointer->QueenState=QBS_Reconsider;
}
}
if (sbPtr->SBdptr==NULL) {
/* No far behaviour. */
return;
}
if(!dynPtr->IsInContactWithFloor && queenStatusPointer->current_move!=QM_Jump && queenStatusPointer->current_move!=QM_Climbing)
{
//if queen is not on the ground , and not currently jumping
//switch to standby
queenStatusPointer->current_move=QM_Standby;
}
if(dynPtr->IsInContactWithFloor)
{
if(!stricmp(LevelName,"hangar"))
{
//is the player in the trench
if((dynPtr->Position.vy+1500)<Player->ObWorld.vy)
{
PlayerInTrench=TRUE;
if(queenStatusPointer->QueenState!=QBS_Engagement)
{
QueenForceReconsider(sbPtr);
}
}
else
{
PlayerInTrench=FALSE;
}
//is the player in the locker ('hangar')
if(Player->ObWorld.vx> HangarLockerMinX && Player->ObWorld.vx < HangarLockerMaxX &&
Player->ObWorld.vz> HangarLockerMinZ && Player->ObWorld.vz < HangarLockerMaxZ)
PlayerInLocker=TRUE;
else
PlayerInLocker=FALSE;
//is the queen in the airlock?
if(UpperAirlockDoorOpen && !LowerAirlockDoorOpen)
{
//has the queen fallen into the airlock
if(ObjectIsInAirlock(sbPtr) && !ObjectIsInAirlock(Player->ObStrategyBlock) && queenStatusPointer->QueenState!=QBS_ClimbingOutOfAirlock)
{
QueenForceReconsider(sbPtr);
}
}
}
}
queenStatusPointer->QueenStateTimer+=NormalFrameTime;
{
VECTORCH pos=Player->ObWorld;
SubVector(&dynPtr->Position,&pos);
DistanceToPlayer=Approximate3dMagnitude(&pos);
}
/*-------------------------------------------------------**
** Check collision reports , and check for need to jump **
**-------------------------------------------------------*/
JumpDesired=FALSE;
{
BOOL ignore_obstacles=FALSE;
if(queenStatusPointer->LastVelocity.vx || queenStatusPointer->LastVelocity.vz ||
queenStatusPointer->current_move==QM_Climbing)
{
/* Now, smash stuff. */
COLLISIONREPORT *nextReport;
nextReport = dynPtr->CollisionReportPtr;
while(nextReport)
{
int normalDotWithVelocity;
BOOL ConsiderJumpingForThisObject=FALSE;
{
VECTORCH normVelocity = sbPtr->DynPtr->LinVelocity;
normVelocity.vy=0;
Normalise(&normVelocity);
normalDotWithVelocity = DotProduct(&(nextReport->ObstacleNormal),&(normVelocity));
}
if(normalDotWithVelocity<-20000)//is the object in the way
{
if(nextReport->ObstacleNormal.vy<20000 && nextReport->ObstacleNormal.vy>-20000)
{
//obstacle is reasonably vertical , may need to jump
ConsiderJumpingForThisObject=TRUE;
}
if (nextReport->ObstacleSBPtr)
{
if(nextReport->ObstacleSBPtr->I_SBtype==I_BehaviourInanimateObject)
{
INANIMATEOBJECT_STATUSBLOCK* objectstatusptr = nextReport->ObstacleSBPtr->SBdataptr;
if((objectstatusptr)&&(objectstatusptr->Indestructable == 0))
{
int i;
BOOL AllowedToDestroy=TRUE;
//is this one of the queen's objects?
for(i=0;i<NumQueenObjects;i++)
{
if(QueenObjectList[i]==nextReport->ObstacleSBPtr)
{
AllowedToDestroy=FALSE;
}
}
if(AllowedToDestroy)
{
/* aha: an object which the queen can destroy... */
CauseDamageToObject(nextReport->ObstacleSBPtr,&TemplateAmmo[AMMO_NPC_PAQ_CLAW].MaxDamage[AvP.Difficulty], ONE_FIXED,NULL);
//no need to jump over this
ConsiderJumpingForThisObject=FALSE;
}
}
}
else if(nextReport->ObstacleSBPtr->I_SBtype==I_BehaviourTrackObject)
{
/* aha: an object which the queen can destroy... */
CauseDamageToObject(nextReport->ObstacleSBPtr,&TemplateAmmo[AMMO_NPC_PAQ_CLAW].MaxDamage[AvP.Difficulty], ONE_FIXED,NULL);
//no need to jump over this
ConsiderJumpingForThisObject=FALSE;
}
else
{
ConsiderJumpingForThisObject=FALSE;
}
if(queenStatusPointer->QueenState==QBS_GoingForObject)
{
//is this the object that the queen was heading for?
if(nextReport->ObstacleSBPtr==queenStatusPointer->QueenTargetSB)
{
QueenPickupTargetObject(sbPtr);
//no need to jump over this
ConsiderJumpingForThisObject=FALSE;
}
}
else if(queenStatusPointer->QueenState==QBS_CarryingObject)
{
//if the queen is colliding with the object she is carrying , ignore it
if(QueenObjectList[queenStatusPointer->CurrentQueenObject]==nextReport->ObstacleSBPtr)
{
//no need to jump over this
ConsiderJumpingForThisObject=FALSE;
}
}
if(nextReport->ObstacleSBPtr==Player->ObStrategyBlock)
{
ConsiderJumpingForThisObject=FALSE;
//have we connected with a butt attack
if(queenStatusPointer->current_move==QM_ButtCharge)
{
queenStatusPointer->current_move=QM_ButtAttack;
queenStatusPointer->next_move=QM_Standby;
queenStatusPointer->moveTimer=0;
{
//knock the player back
VECTORCH* player_impulse;
player_impulse=&Player->ObStrategyBlock->DynPtr->LinImpulse;
player_impulse->vx+=dynPtr->OrientMat.mat31/4;
player_impulse->vy-=6000;
player_impulse->vz+=dynPtr->OrientMat.mat33/4;
}
{
//do some damage
CauseDamageToObject(Player->ObStrategyBlock,&QueenButtDamage, ONE_FIXED,NULL);
}
}
else if(queenStatusPointer->current_move==QM_Climbing)
{
//need to push he player out of the way
Player->ObStrategyBlock->DynPtr->LinImpulse.vx=min(Player->ObStrategyBlock->DynPtr->LinImpulse.vx,-3000);
}
else if(queenStatusPointer->QueenState!=QBS_Engagement &&
queenStatusPointer->QueenState!=QBS_CarryingObject)
{
QueenForceReconsider(sbPtr);
}
}
}
/*Queen climbing out of the airlock?*/
if(queenStatusPointer->QueenState==QBS_ClimbingOutOfAirlock &&
queenStatusPointer->current_move!=QM_Climbing)
{
QueenCalculateTargetInfo(sbPtr);
if((dynPtr->Position.vx-AirlockMinX)<2800)
{
//queen has hit the end wall , so start climbing
queenStatusPointer->current_move=QM_Climbing;
queenStatusPointer->moveTimer=0;
}
}
}
if(ConsiderJumpingForThisObject) ConsiderJumping=TRUE;
nextReport = nextReport->NextCollisionReportPtr;
}
}
//if the queen is approaching the player in the locker
//ignore obstacles to allow the quuen to get to the player
if(PlayerInLocker && !queenStatusPointer->TempTarget &&
queenStatusPointer->QueenTargetSB==Player->ObStrategyBlock)
{
QueenCalculateTargetInfo(sbPtr);
if(queenStatusPointer->TargetDistance<12000)
{
ignore_obstacles=TRUE;
}
}
/*If the queen is trying to get out of the airlock , then we don't want to deviate because
of walls getting in the way*/
if(queenStatusPointer->QueenState==QBS_ClimbingOutOfAirlock)
{
ignore_obstacles=TRUE;
}
//check for nearby obstacles , and decide whether to jump.
if(!ignore_obstacles)
{
VECTORCH direction;
VECTORCH position=sbPtr->SBdptr->ObWorld;
QueenCalculateTargetInfo(sbPtr);
direction.vx=dynPtr->OrientMat.mat31;
direction.vy=0;
direction.vz=dynPtr->OrientMat.mat33;
Normalise(&direction);
//only bother to think of jumping if the queen is facing her target
if(queenStatusPointer->TargetDirection.vz>(queenStatusPointer->TargetDirection.vx*2) &&
queenStatusPointer->TargetDirection.vz>-(queenStatusPointer->TargetDirection.vx*2))
{
int left_dist,centre_dist,right_dist;
VECTORCH left_point,centre_point,right_point;
int distance;
//do several line of sight tests one metre above ground , in front of queen.
//if there is nothing near by , then queen should be able to jump over
position.vy-=1000;
LOS_ObjectHitPtr=0;
FindPolygonInLineOfSight(&queenStatusPointer->VectToTarget,&position,1,sbPtr->SBdptr);
centre_dist=LOS_Lambda;
centre_point=LOS_Point;
if(LOS_ObjectHitPtr && LOS_ObjectHitPtr->ObStrategyBlock)
{
if(LOS_ObjectHitPtr->ObStrategyBlock->I_SBtype==I_BehaviourInanimateObject ||
LOS_ObjectHitPtr->ObStrategyBlock->I_SBtype==I_BehaviourTrackObject ||
LOS_ObjectHitPtr==Player)
{
//not an obstacle
centre_dist=1000000;
}
}
//check on the right
position.vx+=dynPtr->OrientMat.mat11/32;
position.vy+=dynPtr->OrientMat.mat12/32;
position.vz+=dynPtr->OrientMat.mat13/32;
LOS_ObjectHitPtr=0;
FindPolygonInLineOfSight(&queenStatusPointer->VectToTarget,&position,1,sbPtr->SBdptr);
right_dist=LOS_Lambda;
right_point=LOS_Point;
if(LOS_ObjectHitPtr && LOS_ObjectHitPtr->ObStrategyBlock)
{
if(LOS_ObjectHitPtr->ObStrategyBlock->I_SBtype==I_BehaviourInanimateObject ||
LOS_ObjectHitPtr->ObStrategyBlock->I_SBtype==I_BehaviourTrackObject ||
LOS_ObjectHitPtr==Player)
{
//not an obstacle
right_dist=1000000;
}
}
//check on the left
position.vx-=dynPtr->OrientMat.mat11/16;
position.vy-=dynPtr->OrientMat.mat12/16;
position.vz-=dynPtr->OrientMat.mat13/16;
LOS_ObjectHitPtr=0;
FindPolygonInLineOfSight(&queenStatusPointer->VectToTarget,&position,1,sbPtr->SBdptr);
left_dist=LOS_Lambda;
left_point=LOS_Point;
if(LOS_ObjectHitPtr && LOS_ObjectHitPtr->ObStrategyBlock)
{
if(LOS_ObjectHitPtr->ObStrategyBlock->I_SBtype==I_BehaviourInanimateObject ||
LOS_ObjectHitPtr->ObStrategyBlock->I_SBtype==I_BehaviourTrackObject ||
LOS_ObjectHitPtr==Player)
{
//not an obstacle
left_dist=1000000;
}
}
distance=queenStatusPointer->TargetDistance;
if(distance>10000) distance=10000;
if(right_dist<distance || centre_dist<distance || left_dist<distance)
{
if(centre_dist<left_dist && centre_dist<right_dist)
{
queenStatusPointer->TargetPos=centre_point;
}
else if(right_dist<left_dist)
{
queenStatusPointer->TargetPos=right_point;
}
else
{
queenStatusPointer->TargetPos=left_point;
}
/*
if(right_dist<left_dist)
{
queenStatusPointer->TargetPos.vx-=dynPtr->OrientMat.mat11/8;
queenStatusPointer->TargetPos.vz-=dynPtr->OrientMat.mat13/8;
}
else
{
queenStatusPointer->TargetPos.vx+=dynPtr->OrientMat.mat11/8;
queenStatusPointer->TargetPos.vz+=dynPtr->OrientMat.mat13/8;
}
queenStatusPointer->TempTarget=TRUE;
queenStatusPointer->TempTargetTimer=6*ONE_FIXED;
queenStatusPointer->TargetInfoValid=FALSE;
*/
}
else if(ConsiderJumping)
{
JumpDesired=TRUE;
}
}
}
}
if(queenStatusPointer->QueenState==QBS_GoingForObject)
{
//if the queen is close enought to the object she is going for
//pick it up even without a collision
QueenCalculateTargetInfo(sbPtr);
if(queenStatusPointer->TargetDistance<1000)
{
QueenPickupTargetObject(sbPtr);
}
}
/*---------------------------------------------------**
** consider if queen should change her current plan **
**---------------------------------------------------*/
{
switch(queenStatusPointer->QueenState)
{
case QBS_Reconsider :
{
//find closest object that is on the ground
int closest=-1;
int DistanceToObject=1000000000;
int i;
textprint("Queen Reconsider\n");
if(queenStatusPointer->PlayingHitDelta)
{
break;
}
if(UpperAirlockDoorOpen && !LowerAirlockDoorOpen)
{
//has the queen fallen into the airlock
if(ObjectIsInAirlock(sbPtr))
{
//the queen is in the airlock
if(ObjectIsInAirlock(Player->ObStrategyBlock))
{
//player is in the airlock with the queen
//splat him
queenStatusPointer->QueenState=QBS_Engagement;
queenStatusPointer->QueenTargetSB=Player->ObStrategyBlock;
queenStatusPointer->TargetPos=queenStatusPointer->QueenTargetSB->DynPtr->Position;
}
else
{
//go into get out of airlock mode in that case
queenStatusPointer->QueenState=QBS_ClimbingOutOfAirlock;
queenStatusPointer->TempTarget=TRUE;
queenStatusPointer->TempTargetTimer=50*ONE_FIXED;
queenStatusPointer->TargetPos.vx=AirlockMinX-6000;
queenStatusPointer->TargetPos.vz=AirlockCentreZ;
queenStatusPointer->BeenInAirlock=TRUE;
}
queenStatusPointer->QueenStateTimer=0;
queenStatusPointer->TargetInfoValid=FALSE;
break;
}
}
for(i=0;i<NumQueenObjects;i++)
{
if(!QueenObjectList[i]->SBflags.destroyed_but_preserved)
{
if(QueenObjectList[i]->DynPtr->IsInContactWithFloor)
{
VECTORCH pos=QueenObjectList[i]->DynPtr->Position;
int dist;
SubVector(&dynPtr->Position,&pos);
if(!stricmp(LevelName,"battle"))
{
//In battle , the objects near the egg sack are hard for the queen to get to.
//So best ignore them.
if(QueenObjectList[i]->DynPtr->Position.vz>0) continue;
}
//if the y distance is more than 1500 , the object is probably inaccesible
if(pos.vy<=1500)
{
dist=Approximate3dMagnitude(&pos);
if(dist<DistanceToObject)
{
DistanceToObject=dist;
closest=i;
}
}
}
}
}
{
int player_value=MUL_FIXED(FastRandom()& 0xffff,DistanceToPlayer/queenStatusPointer->QueenPlayerBias);
int object_value=MUL_FIXED(FastRandom()& 0xffff,DistanceToObject/queenStatusPointer->QueenObjectBias);
if(object_value<player_value && closest!=-1 && !PlayerInTrench)
{
//go for the object
queenStatusPointer->QueenState=QBS_GoingForObject;
queenStatusPointer->CurrentQueenObject=closest;
queenStatusPointer->QueenTargetSB=QueenObjectList[queenStatusPointer->CurrentQueenObject];
if(!stricmp(LevelName,"hangar"))
{
queenStatusPointer->QueenPlayerBias++;
}
else
{
//int he predator version , make it more likely for the queen to go after the player
queenStatusPointer->QueenPlayerBias+=3;
}
queenStatusPointer->QueenObjectBias--;
if(queenStatusPointer->QueenObjectBias==0) queenStatusPointer->QueenObjectBias=1;
}
else
{
//go for the player
queenStatusPointer->QueenState=QBS_Engagement;
queenStatusPointer->CurrentQueenObject=-1;
queenStatusPointer->QueenTargetSB=Player->ObStrategyBlock;
queenStatusPointer->QueenObjectBias++;
queenStatusPointer->QueenPlayerBias--;
if(queenStatusPointer->QueenPlayerBias==0)queenStatusPointer->QueenPlayerBias=1;
}
queenStatusPointer->TargetPos=queenStatusPointer->QueenTargetSB->DynPtr->Position;
queenStatusPointer->QueenStateTimer=0;
queenStatusPointer->TargetInfoValid=FALSE;
}
}
break;
case QBS_Engagement :
{
if(queenStatusPointer->QueenStateTimer>10*ONE_FIXED && DistanceToPlayer>4000)
{
queenStatusPointer->QueenState=QBS_Reconsider;
queenStatusPointer->QueenStateTimer=0;
}
}
textprint("Queen Engagement\n");
break;
case QBS_GoingForObject :
{
if(queenStatusPointer->QueenStateTimer>15*ONE_FIXED)
{
QueenForceReconsider(sbPtr);
}
}
textprint("Queen Going for object\n");
break;
case QBS_CarryingObject :
{
if(queenStatusPointer->QueenStateTimer>15*ONE_FIXED)
{
QueenForceReconsider(sbPtr);
}
}
textprint("Queen Carrying object\n");
break;
case QBS_ClimbingOutOfAirlock :
textprint("Queen climbing out of airlock\n");
break;
default: ;
}
}
textprint("Queen Bias - Object %d Player %d\n",queenStatusPointer->QueenObjectBias,queenStatusPointer->QueenPlayerBias);
textprint("Queen Health %d\n",sbPtr->SBDamageBlock.Health>>16);
/*--------------------**
** Can queen attack? **
**--------------------*/
//queen must be in engagement mode , and not avoiding stuff
if(!queenStatusPointer->TempTarget && queenStatusPointer->QueenState==QBS_Engagement)
{
//player must be close enough and in front 90 degree arc
QueenCalculateTargetInfo(sbPtr);
if (queenStatusPointer->TargetDistance<4000 ||
(queenStatusPointer->TargetDistance<8000 && queenStatusPointer->TargetRelSpeed<1000))
{
if(queenStatusPointer->current_move==QM_Standby ||
queenStatusPointer->current_move==QM_Close ||
queenStatusPointer->current_move==QM_Charge)
{
if(queenStatusPointer->TargetDirection.vz>queenStatusPointer->TargetDirection.vx &&
queenStatusPointer->TargetDirection.vz>-queenStatusPointer->TargetDirection.vx)
{
int x=queenStatusPointer->TargetDirection.vx;
x+=(FastRandom() % 80000);
x-=40000;
if (x>0)
{
if(PlayerInTrench)
Queen_Do_Swipe(sbPtr,Queen_Swipe_Right_Low);
else
Queen_Do_Swipe(sbPtr,Queen_Swipe_Right);
}
else
{
if(PlayerInTrench)
Queen_Do_Swipe(sbPtr,Queen_Swipe_Left_Low);
else
Queen_Do_Swipe(sbPtr,Queen_Swipe_Left);
}
}
}
}
}
/*------------------------------------------**
** Decide if new movement state is required **
**------------------------------------------*/
if(queenStatusPointer->QueenState==QBS_CarryingObject)
{
//Queen is lining up to throw object
QueenCalculateTargetInfo(sbPtr);
if(queenStatusPointer->TargetDirection.vz>(queenStatusPointer->TargetDirection.vx*2) &&
queenStatusPointer->TargetDirection.vz>-(queenStatusPointer->TargetDirection.vx*2) && !queenStatusPointer->TempTarget)
{
//Time to throw object at player.
VECTORCH impulse;
if(CalculateTrajectory(&QueenObjectList[queenStatusPointer->CurrentQueenObject]->DynPtr->Position,
&Player->ObStrategyBlock->DynPtr->Position,
&Player->ObStrategyBlock->DynPtr->LinVelocity,
QUEEN_THROWN_OBJECT_SPEED,
&impulse))
{
//give the object an impulse ,and reinstate gravity
QueenObjectList[queenStatusPointer->CurrentQueenObject]->DynPtr->LinImpulse=impulse;
QueenObjectList[queenStatusPointer->CurrentQueenObject]->DynPtr->GravityOn=1;
QueenObjectList[queenStatusPointer->CurrentQueenObject]->DynPtr->OnlyCollideWithEnvironment=0;
{
MATRIXCH id_mat={ONE_FIXED,0,0,0,ONE_FIXED,0,0,0,ONE_FIXED};
QueenObjectList[queenStatusPointer->CurrentQueenObject]->DynPtr->OrientMat=id_mat;
}
//queen will need to choose a new objective
queenStatusPointer->QueenState=QBS_Reconsider;
queenStatusPointer->QueenTargetSB=Player->ObStrategyBlock;
queenStatusPointer->TargetInfoValid=FALSE;
}
else
{
//continue heading for target
if(queenStatusPointer->current_move==QM_Standby)
{
queenStatusPointer->next_move=QM_Close;
}
}
}
else
{
//Queen needs to turn some more
if(queenStatusPointer->current_move==QM_Standby)
{
queenStatusPointer->next_move=QM_Close;
}
}
}
//if in standby , need to choose a move
else if(queenStatusPointer->current_move==QM_Standby && queenStatusPointer->QueenState!=QBS_Reconsider)
{
QueenCalculateTargetInfo(sbPtr);
//queen is heading towards object , or player
if(((queenStatusPointer->TargetDistance>8000)||(queenStatusPointer->TargetDistance>2000 && queenStatusPointer->TargetRelSpeed>QUEEN_CLOSE_SPEED)) &&
queenStatusPointer->TargetDirection.vz>queenStatusPointer->TargetDirection.vx &&
queenStatusPointer->TargetDirection.vz>-queenStatusPointer->TargetDirection.vx)
{
queenStatusPointer->next_move=QM_Charge;
if(queenStatusPointer->QueenTargetSB==Player->ObStrategyBlock)
{
//going for player. possibility of butting player
//Nb. queen can't actually colide with player , if the player is in the locker area
if((FastRandom()&0xffff)<(ONE_FIXED/2) && !PlayerInLocker)
{
queenStatusPointer->next_move=QM_ButtCharge;
}
}
if(PlayerInTrench)
{
queenStatusPointer->next_move=QM_ComeToPoint;
}
}
else if (queenStatusPointer->TargetDistance>3000 || queenStatusPointer->TargetRelSpeed>0)
{
if(queenStatusPointer->QueenTargetSB==Player->ObStrategyBlock && !queenStatusPointer->TempTarget)
queenStatusPointer->next_move=QM_Close;
else
queenStatusPointer->next_move=QM_ComeToPoint;
}
else
{
if(queenStatusPointer->QueenState!=QBS_ClimbingOutOfAirlock)
{
if(queenStatusPointer->TempTarget)
{
queenStatusPointer->TempTarget=FALSE;
queenStatusPointer->TargetInfoValid=FALSE;
}
}
if(queenStatusPointer->QueenState==QBS_GoingForObject)
{
//have to keep going until queen hits object
queenStatusPointer->next_move=QM_Close;
}
else
{
//queen right next to target now , so wait
queenStatusPointer->next_move=QM_Standby;
//assuming queen is facing her target , that is.
if(queenStatusPointer->TargetDirection.vz<queenStatusPointer->TargetDirection.vx*2 ||
queenStatusPointer->TargetDirection.vz<-queenStatusPointer->TargetDirection.vx*2)
{
//queen isn't facing target , so she needs to continue closing after all
queenStatusPointer->next_move=QM_Close;
}
}
}
}
textprint("Queen target distance %d\n",queenStatusPointer->TargetDistance);
textprint("Queen target position %d : %d : %d\n",queenStatusPointer->TargetPos.vx,queenStatusPointer->TargetPos.vy,queenStatusPointer->TargetPos.vz);
///////////////////////////////////////////////////////////////////////
/*--------------------**
** Airlock avoidance **
**--------------------*/
QueenCheckForAvoidAirlock(sbPtr);
if(queenStatusPointer->TempTarget)
{
QueenCalculateTargetInfo(sbPtr);
if(queenStatusPointer->TargetDistance<4000)
{
queenStatusPointer->TempTarget=FALSE;
queenStatusPointer->TargetInfoValid=FALSE;
}
queenStatusPointer->TempTargetTimer-=NormalFrameTime;
if(queenStatusPointer->TempTargetTimer<0)
{
queenStatusPointer->TempTarget=FALSE;
queenStatusPointer->TargetInfoValid=FALSE;
}
}
/*-------------------------------------------------------------------**
** check for redirecting the queen , if the player is in the locker **
**-------------------------------------------------------------------*/
if(!queenStatusPointer->TempTarget &&
queenStatusPointer->QueenTargetSB==Player->ObStrategyBlock &&
PlayerInLocker)
{
//is the queen in line with the locker area?
if((dynPtr->Position.vz-HangarLockerMaxZ)*2>dynPtr->Position.vx-HangarLockerMaxX ||
(HangarLockerMinZ-dynPtr->Position.vz)*2>dynPtr->Position.vx-HangarLockerMaxX)
{
//nope
queenStatusPointer->TempTarget=TRUE;
queenStatusPointer->TempTargetTimer=2*ONE_FIXED;
queenStatusPointer->TargetInfoValid=FALSE;
//move the queen so that she is lined up with the locker
queenStatusPointer->TargetPos.vx=dynPtr->Position.vx;
queenStatusPointer->TargetPos.vy=dynPtr->Position.vy;
queenStatusPointer->TargetPos.vz=HangarLockerCentreZ;
}
}
/*---------------------**
** check for taunting **
**---------------------*/
if(queenStatusPointer->QueenTauntTimer>0 )
{
queenStatusPointer->QueenTauntTimer-=NormalFrameTime;
if(queenStatusPointer->QueenTargetSB==Player->ObStrategyBlock && DistanceToPlayer>10000)
{
QueenCalculateTargetInfo(sbPtr);
if(queenStatusPointer->TargetDirection.vz>queenStatusPointer->TargetDirection.vx &&
queenStatusPointer->TargetDirection.vz>-queenStatusPointer->TargetDirection.vx)
{
queenStatusPointer->QueenTauntTimer=0;
//Queen has recently hit player , and is facing player
//time to taunt
queenStatusPointer->next_move=QM_Taunt;
}
}
}
/*-------------------------------**
** Flamethrower avoidance stuff **
**-------------------------------*/
//only if queen is trying to attack the player in close combat
if(queenStatusPointer->QueenState==QBS_Engagement)
{
if(queenStatusPointer->QueenFireTimer>QueenMinimumFireTime)
{
/*stop and hiss for a bit*/
if(queenStatusPointer->current_move==QM_Standby ||
queenStatusPointer->current_move==QM_ComeToPoint ||
queenStatusPointer->current_move==QM_ButtCharge ||
queenStatusPointer->current_move==QM_Charge ||
queenStatusPointer->current_move==QM_Close)
{
queenStatusPointer->current_move=QM_Hiss;
queenStatusPointer->moveTimer=0;
}
}
else if(queenStatusPointer->QueenFireTimer==0)
{
if(queenStatusPointer->next_move==QM_Hiss)
{
queenStatusPointer->next_move=QM_Standby;
}
if(queenStatusPointer->current_move==QM_Hiss)
{
queenStatusPointer->moveTimer=0;
queenStatusPointer->current_move=queenStatusPointer->next_move;
queenStatusPointer->next_move=QM_Standby;
}
}
//update the timer
if(TargetIsFiringFlamethrowerAtQueen(sbPtr))
{
queenStatusPointer->QueenFireTimer+=NormalFrameTime;
if(queenStatusPointer->QueenFireTimer>2*ONE_FIXED)
{
queenStatusPointer->QueenFireTimer=2*ONE_FIXED;
}
}
else
{
queenStatusPointer->QueenFireTimer-=NormalFrameTime;
if(queenStatusPointer->QueenFireTimer<0)
{
queenStatusPointer->QueenFireTimer=0;
}
}
}
else
{
//make sure queen isn't hissing
if(queenStatusPointer->next_move==QM_Hiss)
{
queenStatusPointer->next_move=QM_Standby;
}
if(queenStatusPointer->current_move==QM_Hiss)
{
queenStatusPointer->moveTimer=0;
queenStatusPointer->current_move=queenStatusPointer->next_move;
queenStatusPointer->next_move=QM_Standby;
}
queenStatusPointer->QueenFireTimer=0;
}
if(!queenStatusPointer->TempTarget)
{
//update target location
queenStatusPointer->TargetPos=queenStatusPointer->QueenTargetSB->DynPtr->Position;
}
/*---------------------------**
** Handle Queeen's movement **
**---------------------------*/
dynPtr->Displacement.vx = 0;
dynPtr->Displacement.vy = 0;
dynPtr->Displacement.vz = 0;
if (queenStatusPointer->QueenState!=QBS_Dead)
{
switch (queenStatusPointer->current_move)
{
case (QM_Standby):
{
textprint("Queen State: Standby\n");
db_log3(("Queen State: Standby\n"));
if (queenStatusPointer->next_move==QM_Standby || queenStatusPointer->PlayingHitDelta)
{
/* Do not much. */
QueenMove_Standby(sbPtr);
}
else
{
queenStatusPointer->current_move=queenStatusPointer->next_move;
queenStatusPointer->next_move=QM_Standby;
queenStatusPointer->moveTimer=0;
//queenStatusPointer->TargetPos=Queen_Target_Point;
}
break;
}
case (QM_Stun) :
{
textprint("Queen State: Stunned\n");
//don't do anything
sbPtr->DynPtr->LinVelocity.vx = 0;
sbPtr->DynPtr->LinVelocity.vy = 0;
sbPtr->DynPtr->LinVelocity.vz = 0;
break;
}
case (QM_StepForward):
{
textprint("Queen State: Step Forward\n");
/* Move function. */
// QueenMove_StepForward(sbPtr);
break;
}
case (QM_StepBack):
{
textprint("Queen State: Step Back\n");
/* Move function. */
QueenMove_StepBack(sbPtr);
break;
}
case (QM_TurnLeft):
{
textprint("Queen State: Turn Left\n");
/* Move function. */
// QueenMove_TurnLeft(sbPtr);
break;
}
case (QM_TurnRight):
{
textprint("Queen State: Turn Right\n");
/* Move function. */
// QueenMove_TurnRight(sbPtr);
break;
}
case (QM_ComeToPoint):
{
textprint("Queen State: Come To Point\n");
db_log3(("Queen State: Come To Point\n"));
/* Move function. */
if(JumpDesired)
{
textprint("Jumping\n");
sbPtr->DynPtr->LinImpulse.vy-=10000;
queenStatusPointer->next_move=QM_Standby;
queenStatusPointer->current_move=QM_Jump;
}
else
{
QueenMove_Walk(sbPtr);
}
break;
}
case (QM_Taunt):
{
textprint("Queen State: Taunt\n");
db_log3(("Queen State: Taunt\n"));
/* Move function. */
QueenMove_Taunt(sbPtr);
break;
}
case (QM_Hiss):
{
textprint("Queen State: Hiss\n");
/* Move function. */
QueenMove_Hiss(sbPtr);
break;
}
case (QM_LeftSwipe):
{
textprint("Queen State: Left Swipe\n");
// QueenMove_LeftSwipe(sbPtr);
break;
}
case (QM_RightSwipe):
{
textprint("Queen State: Right Swipe\n");
// QueenMove_RightSwipe(sbPtr);
break;
}
case (QM_ButtAttack):
{
textprint("Queen State: Butt attack\n");
db_log3(("Queen State: Butt attack\n"));
QueenMove_ButtAttack(sbPtr);
break;
}
case (QM_Charge):
{
textprint("Queen State: Charging\n");
db_log3(("Queen State: Charging\n"));
/* Move function. */
if(JumpDesired)
{
textprint("Jumping\n");
sbPtr->DynPtr->LinImpulse.vy-=10000;
queenStatusPointer->next_move=QM_Standby;
queenStatusPointer->current_move=QM_Jump;
}
else
{
QueenMove_Charge(sbPtr);
}
break;
}
case (QM_Close):
{
textprint("Queen State: Closing\n");
db_log3(("Queen State: Closing\n"));
/* Move function. */
if(JumpDesired)
{
textprint("Jumping\n");
sbPtr->DynPtr->LinImpulse.vy-=10000;
queenStatusPointer->next_move=QM_Standby;
queenStatusPointer->current_move=QM_Jump;
}
else
{
QueenMove_Close(sbPtr);
}
break;
}
case (QM_ButtCharge) :
{
textprint("Queen State: Butt charging\n");
db_log3(("Queen State: Butt charging\n"));
/* Move function. */
if(JumpDesired)
{
textprint("Jumping\n");
sbPtr->DynPtr->LinImpulse.vy-=10000;
queenStatusPointer->next_move=QM_Standby;
queenStatusPointer->current_move=QM_Jump;
}
else
{
QueenMove_ButtCharge(sbPtr);
}
break;
}
case (QM_Jump):
{
textprint("Queen State: Jumping\n");
db_log3(("Queen State: Jumping\n"));
//stay in jump mode until queen hits the floor again
if(!dynPtr->IsInContactWithFloor)
{
sbPtr->DynPtr->LinVelocity.vx=sbPtr->DynPtr->OrientMat.mat31/10;
sbPtr->DynPtr->LinVelocity.vy=0;
sbPtr->DynPtr->LinVelocity.vz=sbPtr->DynPtr->OrientMat.mat33/10;
}
else
{
queenStatusPointer->current_move=queenStatusPointer->next_move;
queenStatusPointer->next_move=QM_Standby;
queenStatusPointer->moveTimer=0;
}
break;
}
case (QM_Climbing):
{
textprint("Queen State: Climbing\n");
sbPtr->DynPtr->LinVelocity.vx = 0;
sbPtr->DynPtr->LinVelocity.vy = 0;
sbPtr->DynPtr->LinVelocity.vz = 0;
QueenMove_Climb(sbPtr);
}
break;
default:
{
GLOBALASSERT(0);
return;
}
}
HModel_Regen(&queenStatusPointer->HModelController,(20*ONE_FIXED));
}
queenStatusPointer->LastVelocity=dynPtr->LinVelocity;
/*----------------------------------**
** Update object queen is carrying **
**----------------------------------*/
if(queenStatusPointer->QueenState==QBS_CarryingObject)
{
ProveHModel(sbPtr->SBdptr->HModelControlBlock,sbPtr->SBdptr);
QueenObjectList[queenStatusPointer->CurrentQueenObject]->DynPtr->Position=queenStatusPointer->QueenRightHand->World_Offset;
QueenObjectList[queenStatusPointer->CurrentQueenObject]->DynPtr->PrevPosition=queenStatusPointer->QueenRightHand->World_Offset;
QueenObjectList[queenStatusPointer->CurrentQueenObject]->DynPtr->OrientMat=queenStatusPointer->QueenRightHand->SecMat;
}
sbPtr->SBDamageBlock.IsOnFire=0;
/* That would be silly. */
/*----------------------------------------------------------------------------------------**
** Monitor the queen's objects , and check for any high speed collisions with the player **
**----------------------------------------------------------------------------------------*/
{
int i;
for(i=0;i<NumQueenObjects;i++)
{
if(!QueenObjectList[i]->SBflags.destroyed_but_preserved)
{
BOOL doneBounceNoise=FALSE;
COLLISIONREPORT *nextReport;
nextReport = QueenObjectList[i]->DynPtr->CollisionReportPtr;
while(nextReport)
{
int impulse=Approximate3dMagnitude(&QueenObjectList[i]->DynPtr->LinImpulse);
if(impulse>10000)
{
//object hit something while moving quickly , so play a bounce sound
if(!doneBounceNoise)
{
ThrownObjectBounceNoise(i,&nextReport->ObstaclePoint);
doneBounceNoise=TRUE;
}
if(nextReport->ObstacleSBPtr)
{
if(nextReport->ObstacleSBPtr==Player->ObStrategyBlock ||
nextReport->ObstacleSBPtr->I_SBtype==I_BehaviourInanimateObject ||
(nextReport->ObstacleSBPtr->name && !strcmp(nextReport->ObstacleSBPtr->name,"locker door")))
{
//object has hit the player at speed.
DAMAGE_PROFILE impact_damage=QueenImpactDamage;
VECTORCH direction;
VECTORCH hit_object_direction;
int dotproduct;
//adjust the object's position so it is ear the player
//back in the direction that it came from
direction=QueenObjectList[i]->DynPtr->Position;
SubVector(&QueenObjectList[i]->DynPtr->PrevPosition,&direction);
if(direction.vx || direction.vy || direction.vz)
{
Normalise(&direction);
}
hit_object_direction=nextReport->ObstacleSBPtr->DynPtr->Position;
SubVector(&nextReport->ObstacleSBPtr->DynPtr->PrevPosition,&hit_object_direction);
if(hit_object_direction.vx || hit_object_direction.vy || hit_object_direction.vz)
{
Normalise(&hit_object_direction);
}
dotproduct=Dot(&direction,&hit_object_direction);
//damage the player
impact_damage.Impact=40+MUL_FIXED(15,dotproduct);
CauseDamageToObject(nextReport->ObstacleSBPtr,&impact_damage, ONE_FIXED,NULL);
//damage the thrown object as well (almost certainly destroying it)
impact_damage.Impact*=10;
CauseDamageToObject(QueenObjectList[i],&impact_damage, ONE_FIXED,NULL);
{
//knock the object back
VECTORCH* impulse;
impulse=&nextReport->ObstacleSBPtr->DynPtr->LinImpulse;
impulse->vx+=direction.vx/4;
impulse->vz+=direction.vz/4;
}
QueenObjectList[i]->DynPtr->Position.vx=nextReport->ObstacleSBPtr->DynPtr->Position.vx+direction.vx/100;
QueenObjectList[i]->DynPtr->Position.vz=nextReport->ObstacleSBPtr->DynPtr->Position.vz+direction.vz/100;
QueenObjectList[i]->DynPtr->PrevPosition.vx=QueenObjectList[i]->DynPtr->Position.vx;
QueenObjectList[i]->DynPtr->PrevPosition.vz=QueenObjectList[i]->DynPtr->Position.vz;
//set the taunt timer
queenStatusPointer->QueenTauntTimer=ONE_FIXED/2;
break;
}
}
}
nextReport = nextReport->NextCollisionReportPtr;
}
}
}
}
QueenSoundHiss(sbPtr);
}
static BOOL LockerDoorIsClosed()
{
TRACK_OBJECT_BEHAV_BLOCK* door;
GLOBALASSERT(LockerDoorSbptr);
if(LockerDoorSbptr->SBflags.destroyed_but_preserved) return FALSE;
GLOBALASSERT(LockerDoorSbptr->SBdataptr);
door=(TRACK_OBJECT_BEHAV_BLOCK*)LockerDoorSbptr->SBdataptr;
GLOBALASSERT(door->to_track);
if(door->to_track->reverse && !door->to_track->playing) return TRUE;
return FALSE;
}
void HandleHangarAirlock()
{
//only need to look out for the airlock in hangar
int wind_multiplier=0;
if(!stricmp(LevelName,"hangar"))
{
GLOBALASSERT(UpperAirlockDoorSbptr);
GLOBALASSERT(UpperAirlockDoorSbptr->DynPtr);
GLOBALASSERT(LowerAirlockDoorSbptr);
GLOBALASSERT(LowerAirlockDoorSbptr->DynPtr);
//check to see which of the airlock doors are open
{
VECTORCH* door_pos=&UpperAirlockDoorSbptr->DynPtr->Position;
int upper_open_amount=0;
int lower_open_amount=0;
if(door_pos->vz==UpperAirlockDoorStart.vz)
{
UpperAirlockDoorOpen=FALSE;
}
else
{
UpperAirlockDoorOpen=TRUE;
upper_open_amount=DIV_FIXED(door_pos->vz-UpperAirlockDoorStart.vz,AirlockOpeningDistance);
}
door_pos=&LowerAirlockDoorSbptr->DynPtr->Position;
if(door_pos->vz==LowerAirlockDoorStart.vz)
{
LowerAirlockDoorOpen=FALSE;
}
else
{
LowerAirlockDoorOpen=TRUE;
lower_open_amount=DIV_FIXED(door_pos->vz-LowerAirlockDoorStart.vz,AirlockOpeningDistance);
}
wind_multiplier=MUL_FIXED(upper_open_amount,lower_open_amount);
if(wind_multiplier<0) wind_multiplier=-wind_multiplier;
//get a multiplier for the wind strength , based on how far the two pairs of doors are open
if(wind_multiplier>ONE_FIXED) wind_multiplier=ONE_FIXED;
if(wind_multiplier>=ONE_FIXED)
{
AirlockTimeOpen+=NormalFrameTime;
}
else
{
AirlockTimeOpen=0;
}
}
if(LowerAirlockDoorOpen && UpperAirlockDoorOpen)
{
extern int NumActiveBlocks;
int i = NumActiveBlocks;
extern DISPLAYBLOCK *ActiveBlockList[];
textprint("Wind strength %d\n",wind_multiplier);
for(i=0;i<NumActiveBlocks;i++)
{
STRATEGYBLOCK *sbPtr = ActiveBlockList[i]->ObStrategyBlock;
if(sbPtr && sbPtr->DynPtr && !sbPtr->DynPtr->IsStatic)
{
VECTORCH* pos=&sbPtr->DynPtr->Position;
VECTORCH* cur_impulse=&sbPtr->DynPtr->LinImpulse;
VECTORCH impulse;
BOOL above_airlock=FALSE;
if(pos->vx>HangarLockerMinX && pos->vx<HangarLockerMaxX && pos->vz>HangarLockerMinZ && pos->vz<HangarLockerMaxZ)
{
if(LockerDoorIsClosed())
{
continue;
}
}
if(pos->vy>20000)
{
//outside , so damage this object
if(sbPtr->I_SBtype==I_BehaviourQueenAlien)
//the queen has loads of health , so need to damage her quicker
CauseDamageToObject(sbPtr,&VacuumDamage,NormalFrameTime*30,NULL);
else
CauseDamageToObject(sbPtr,&VacuumDamage,NormalFrameTime,NULL);
if(pos->vy>21000)
{
//turn off gravity
sbPtr->DynPtr->GravityOn=0;
}
continue;
}
else if(AirlockTimeOpen> 10*ONE_FIXED)
{
/*
After 10 seconds start doing damage to objects even if they aren't outside yet.
Scale damage so it increases linearly until 30 seconds have passed
*/
int multiplier;
AirlockTimeOpen=max(AirlockTimeOpen,30*ONE_FIXED);
multiplier=MUL_FIXED(NormalFrameTime,(AirlockTimeOpen-10*ONE_FIXED)/20);
CauseDamageToObject(sbPtr,&VacuumDamage,multiplier,NULL);
}
if(pos->vx>AirlockMinX && pos->vx<AirlockMaxX && pos->vz>AirlockMinZ && pos->vz<AirlockMaxZ) above_airlock=TRUE;
impulse.vx=AirlockCentreX-pos->vx;
impulse.vy=0;
impulse.vz=AirlockCentreZ-pos->vz;
if(!above_airlock && impulse.vx<0)
{
if(impulse.vz*2>impulse.vx && impulse.vz*2<-impulse.vx)
{
if(impulse.vz>0)
{
impulse.vz-=12000;
}
else
{
impulse.vz+=12000;
}
}
}
Normalise(&impulse);
impulse.vx/=2;
if(above_airlock)
impulse.vy=30000;
else
impulse.vy=0;
impulse.vz/=2;
if(wind_multiplier<ONE_FIXED)
{
impulse.vx=MUL_FIXED(impulse.vx,wind_multiplier);
impulse.vy=MUL_FIXED(impulse.vy,wind_multiplier);
impulse.vz=MUL_FIXED(impulse.vz,wind_multiplier);
}
if(impulse.vx>0)
{
if(impulse.vx>cur_impulse->vx)
{
cur_impulse->vx+=MUL_FIXED(impulse.vx,NormalFrameTime);
cur_impulse->vx=min(cur_impulse->vx,impulse.vx);
}
}
else
{
if(impulse.vx<cur_impulse->vx)
{
cur_impulse->vx+=MUL_FIXED(impulse.vx,NormalFrameTime);
cur_impulse->vx=max(cur_impulse->vx,impulse.vx);
}
}
if(impulse.vy>0)
{
if(impulse.vy>cur_impulse->vy)
{
cur_impulse->vy+=MUL_FIXED(impulse.vy,NormalFrameTime);
cur_impulse->vy=min(cur_impulse->vy,impulse.vy);
}
}
else
{
if(impulse.vy<cur_impulse->vy)
{
cur_impulse->vy+=MUL_FIXED(impulse.vy,NormalFrameTime);
cur_impulse->vy=max(cur_impulse->vy,impulse.vy);
}
}
if(impulse.vz>0)
{
if(impulse.vz>cur_impulse->vz)
{
cur_impulse->vz+=MUL_FIXED(impulse.vz,NormalFrameTime);
cur_impulse->vz=min(cur_impulse->vz,impulse.vz);
}
}
else
{
if(impulse.vz<cur_impulse->vz)
{
cur_impulse->vz+=MUL_FIXED(impulse.vz,NormalFrameTime);
cur_impulse->vz=max(cur_impulse->vz,impulse.vz);
}
}
if((pos->vy+ActiveBlockList[i]->ObMaxY)>3950 && !above_airlock)
{
if(sbPtr->DynPtr->UseStandardGravity)
cur_impulse->vy-=MUL_FIXED(MUL_FIXED(GRAVITY_STRENGTH+5000,NormalFrameTime),wind_multiplier);
else
cur_impulse->vy-=MUL_FIXED(MUL_FIXED(5000,NormalFrameTime),wind_multiplier);
}
}
}
}
}
}
static BOOL TargetIsFiringFlamethrowerAtQueen(STRATEGYBLOCK *sbPtr)
{
QUEEN_STATUS_BLOCK *queenStatusPointer;
GLOBALASSERT(sbPtr);
queenStatusPointer=(QUEEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
//only marine has flamethrower
if(AvP.PlayerType!=I_Marine) return FALSE;
//queen must be heading for the player
if(queenStatusPointer->QueenTargetSB!=Player->ObStrategyBlock) return FALSE;
if(queenStatusPointer->TempTarget) return FALSE;
QueenCalculateTargetInfo(sbPtr);
if(queenStatusPointer->TargetDistance>30000) return FALSE; //queen is too far away to be harmed
//if(queenStatusPointer->TargetDistance<3000) return FALSE; //queen close enough to attack , so she may as well go ahead
/*
if (queenStatusPointer->attack_delta->timer!=(ONE_FIXED-1) &&
queenStatusPointer->attack_delta->timer!=0)
{
//currently attacking
return FALSE;
}
*/
{
/* Is the player firing a flamethrower? */
PLAYER_WEAPON_DATA *weaponPtr;
PLAYER_STATUS *playerStatusPtr= (PLAYER_STATUS *) (Player->ObStrategyBlock->SBdataptr);
GLOBALASSERT(playerStatusPtr);
weaponPtr = &(playerStatusPtr->WeaponSlot[playerStatusPtr->SelectedWeaponSlot]);
if (weaponPtr->WeaponIDNumber != WEAPON_FLAMETHROWER)
{
return(FALSE);
}
if (weaponPtr->CurrentState != WEAPONSTATE_FIRING_PRIMARY)
{
return(FALSE);
}
}
//The player is firing the flamethrower.
//are the queen and player facing each other
{
VECTORCH offset;
MATRIXCH WtoL;
WtoL=sbPtr->DynPtr->OrientMat;
offset=sbPtr->DynPtr->Position;
SubVector(&Player->ObWorld,&offset);
TransposeMatrixCH(&WtoL);
RotateVector(&offset,&WtoL);
if ( (offset.vz <0)
&& (offset.vz < offset.vx)
&& (offset.vz < -offset.vx)
&& (offset.vz < offset.vy)
&& (offset.vz < -offset.vy) ) {
/* 90 horizontal, 90 vertical... continue. */
} else {
return(FALSE);
}
/* Now test it for the other way round. */
WtoL=Player->ObMat;
offset=Player->ObWorld;
SubVector(&sbPtr->DynPtr->Position,&offset);
TransposeMatrixCH(&WtoL);
RotateVector(&offset,&WtoL);
if ( (offset.vz <0)
&& (offset.vz < offset.vx)
&& (offset.vz < -offset.vx)
&& (offset.vz < offset.vy)
&& (offset.vz < -offset.vy) ) {
/* 90 horizontal, 90 vertical... continue. */
} else {
return(FALSE);
}
}
/* If here, then it must be true! */
return(TRUE);
}
static void MakeNonFragable_Recursion(SECTION_DATA *this_section_data)
{
SECTION_DATA *sdptr;
sdptr=NULL;
/* flag this section. */
this_section_data->sempai->flags|=section_flag_never_frag;
/* Now call recursion... */
if (this_section_data->First_Child!=NULL) {
SECTION_DATA *child_list_ptr;
child_list_ptr=this_section_data->First_Child;
while (child_list_ptr!=NULL) {
MakeNonFragable_Recursion(child_list_ptr);
child_list_ptr=child_list_ptr->Next_Sibling;
}
}
return;
}
static void MakeNonFragable(HMODELCONTROLLER *controller)
{
MakeNonFragable_Recursion(controller->section_data);
}
/*--------------------**
** Loading and Saving **
**--------------------*/
#include "savegame.h"
typedef struct queen_save_block
{
SAVE_BLOCK_STRATEGY_HEADER header;
//behaviour block stuff
QUEEN_BEHAVIOUR_STATE QueenState;
QUEEN_MANOEUVRE current_move;
QUEEN_MANOEUVRE next_move;
QUEEN_FOOT fixed_foot;
VECTORCH fixed_foot_oldpos;
VECTORCH TargetPos;
int moveTimer;
NPC_WANDERDATA wanderData;
BOOL TempTarget;//going for an intermediate point
int TempTargetTimer;//time before queen gives up going for intermediate point
int CurrentQueenObject;
int QueenStateTimer;
int QueenObjectBias;
int QueenPlayerBias;
int QueenTauntTimer;
int QueenFireTimer;
VECTORCH LastVelocity;
BOOL BeenInAirlock;
BOOL QueenActivated; //queen is inactive until seen
BOOL TargetInfoValid; //have the next three items been set
int TargetDistance; //distance of current target from queen
int TargetRelSpeed; //targets speed in queen's direction
VECTORCH TargetDirection; //targets direction relative to queen
VECTORCH VectToTarget;
unsigned int PlayingHitDelta :1;
int SwerveTimer;
BOOL SwerveDirection;
QUEEN_SOUND_CATEGORY lastSoundCategory;
VECTORCH ClimbStartPosition; //used when climing out of the airlock
BOOL AttackDoneItsDamage;
//and now those evil globals...
BOOL PlayerInTrench;
BOOL PlayerInLocker;
int AirlockTimeOpen;
BOOL UpperAirlockDoorOpen;
BOOL LowerAirlockDoorOpen;
VECTORCH UpperAirlockDoorStart;
VECTORCH LowerAirlockDoorStart;
//annoying pointer related things
char Target_SBname[SB_NAME_LENGTH];
SECTION_DATA *fixed_foot_section;
SECTION_DATA* QueenRightHand;
//strategyblock stuff
int integrity;
DAMAGEBLOCK SBDamageBlock;
DYNAMICSBLOCK dynamics;
}QUEEN_SAVE_BLOCK;
//defines for load/save macros
#define SAVELOAD_BLOCK block
#define SAVELOAD_BEHAV queenStatusPointer
void LoadStrategy_Queen(SAVE_BLOCK_STRATEGY_HEADER* header)
{
STRATEGYBLOCK* sbPtr;
QUEEN_STATUS_BLOCK* queenStatusPointer;
QUEEN_SAVE_BLOCK* block = (QUEEN_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_BehaviourQueenAlien) return;
queenStatusPointer =(QUEEN_STATUS_BLOCK*) sbPtr->SBdataptr;
//normally done on first frame , but need to do this here when loading
FindQueenObjects();
//start copying stuff
COPYELEMENT_LOAD(QueenState)
COPYELEMENT_LOAD(current_move)
COPYELEMENT_LOAD(next_move)
COPYELEMENT_LOAD(fixed_foot)
COPYELEMENT_LOAD(fixed_foot_oldpos)
COPYELEMENT_LOAD(TargetPos)
COPYELEMENT_LOAD(moveTimer)
COPYELEMENT_LOAD(wanderData)
COPYELEMENT_LOAD(TempTarget)//going for an intermediate point
COPYELEMENT_LOAD(TempTargetTimer)//time before queen gives up going for intermediate point
COPYELEMENT_LOAD(CurrentQueenObject)
COPYELEMENT_LOAD(QueenStateTimer)
COPYELEMENT_LOAD(QueenObjectBias)
COPYELEMENT_LOAD(QueenPlayerBias)
COPYELEMENT_LOAD(QueenTauntTimer)
COPYELEMENT_LOAD(QueenFireTimer)
COPYELEMENT_LOAD(LastVelocity)
COPYELEMENT_LOAD(BeenInAirlock)
COPYELEMENT_LOAD(QueenActivated) //queen is inactive until seen
COPYELEMENT_LOAD(TargetInfoValid) //have the next three items been set
COPYELEMENT_LOAD(TargetDistance) //distance of current target from queen
COPYELEMENT_LOAD(TargetRelSpeed) //targets speed in queen's direction
COPYELEMENT_LOAD(TargetDirection) //targets direction relative to queen
COPYELEMENT_LOAD(VectToTarget)
COPYELEMENT_LOAD(PlayingHitDelta)
COPYELEMENT_LOAD(SwerveTimer)
COPYELEMENT_LOAD(SwerveDirection)
COPYELEMENT_LOAD(lastSoundCategory)
COPYELEMENT_LOAD(ClimbStartPosition) //used when climing out of the airlock
COPYELEMENT_LOAD(AttackDoneItsDamage)
//load globals
PlayerInTrench = block->PlayerInTrench;
PlayerInLocker = block->PlayerInLocker;
AirlockTimeOpen = block->AirlockTimeOpen;
UpperAirlockDoorOpen = block->UpperAirlockDoorOpen;
LowerAirlockDoorOpen = block->LowerAirlockDoorOpen;
UpperAirlockDoorStart = block->UpperAirlockDoorStart;
LowerAirlockDoorStart = block->LowerAirlockDoorStart;
//load target
queenStatusPointer->QueenTargetSB = FindSBWithName(block->Target_SBname);
//copy strategy block stuff
*sbPtr->DynPtr = block->dynamics;
sbPtr->integrity = block->integrity;
sbPtr->SBDamageBlock = block->SBDamageBlock;
//load hierarchy
{
SAVE_BLOCK_HEADER* hier_header = GetNextBlockIfOfType(SaveBlock_Hierarchy);
if(hier_header)
{
LoadHierarchy(hier_header,&queenStatusPointer->HModelController);
}
}
//get delta controller pointers
queenStatusPointer->attack_delta=Get_Delta_Sequence(&queenStatusPointer->HModelController,"attack");
queenStatusPointer->hit_delta=Get_Delta_Sequence(&queenStatusPointer->HModelController,"hit");
//get section data pointers
if(queenStatusPointer->fixed_foot == LeftFoot)
{
queenStatusPointer->fixed_foot_section=GetThisSectionData(queenStatusPointer->HModelController.section_data,
"left foot");
}
else
{
queenStatusPointer->fixed_foot_section=GetThisSectionData(queenStatusPointer->HModelController.section_data,
"right foot");
}
queenStatusPointer->QueenRightHand=GetThisSectionData(queenStatusPointer->HModelController.section_data,"rrrr fing");
Load_SoundState(&queenStatusPointer->soundHandle);
}
void SaveStrategy_Queen(STRATEGYBLOCK* sbPtr)
{
QUEEN_STATUS_BLOCK* queenStatusPointer;
QUEEN_SAVE_BLOCK* block;
GET_STRATEGY_SAVE_BLOCK(block,sbPtr);
queenStatusPointer = (QUEEN_STATUS_BLOCK*) sbPtr->SBdataptr;
//start copying stuff
COPYELEMENT_SAVE(QueenState)
COPYELEMENT_SAVE(current_move)
COPYELEMENT_SAVE(next_move)
COPYELEMENT_SAVE(fixed_foot)
COPYELEMENT_SAVE(fixed_foot_oldpos)
COPYELEMENT_SAVE(TargetPos)
COPYELEMENT_SAVE(moveTimer)
COPYELEMENT_SAVE(wanderData)
COPYELEMENT_SAVE(TempTarget)//going for an intermediate point
COPYELEMENT_SAVE(TempTargetTimer)//time before queen gives up going for intermediate point
COPYELEMENT_SAVE(CurrentQueenObject)
COPYELEMENT_SAVE(QueenStateTimer)
COPYELEMENT_SAVE(QueenObjectBias)
COPYELEMENT_SAVE(QueenPlayerBias)
COPYELEMENT_SAVE(QueenTauntTimer)
COPYELEMENT_SAVE(QueenFireTimer)
COPYELEMENT_SAVE(LastVelocity)
COPYELEMENT_SAVE(BeenInAirlock)
COPYELEMENT_SAVE(QueenActivated) //queen is inactive until seen
COPYELEMENT_SAVE(TargetInfoValid) //have the next three items been set
COPYELEMENT_SAVE(TargetDistance) //distance of current target from queen
COPYELEMENT_SAVE(TargetRelSpeed) //targets speed in queen's direction
COPYELEMENT_SAVE(TargetDirection) //targets direction relative to queen
COPYELEMENT_SAVE(VectToTarget)
COPYELEMENT_SAVE(PlayingHitDelta)
COPYELEMENT_SAVE(SwerveTimer)
COPYELEMENT_SAVE(SwerveDirection)
COPYELEMENT_SAVE(lastSoundCategory)
COPYELEMENT_SAVE(ClimbStartPosition) //used when climing out of the airlock
COPYELEMENT_SAVE(AttackDoneItsDamage)
//save globals
block->PlayerInTrench = PlayerInTrench;
block->PlayerInLocker = PlayerInLocker;
block->AirlockTimeOpen = AirlockTimeOpen;
block->UpperAirlockDoorOpen = UpperAirlockDoorOpen;
block->LowerAirlockDoorOpen = LowerAirlockDoorOpen;
block->UpperAirlockDoorStart = UpperAirlockDoorStart;
block->LowerAirlockDoorStart = LowerAirlockDoorStart;
//save target
if(queenStatusPointer->QueenTargetSB)
{
COPY_NAME(block->Target_SBname,queenStatusPointer->QueenTargetSB->SBname);
}
else
{
COPY_NAME(block->Target_SBname,Null_Name);
}
//save strategy block stuff
block->dynamics = *sbPtr->DynPtr;
block->dynamics.CollisionReportPtr=0;
block->integrity = sbPtr->integrity;
block->SBDamageBlock = sbPtr->SBDamageBlock;
//save the hierarchy
SaveHierarchy(&queenStatusPointer->HModelController);
Save_SoundState(&queenStatusPointer->soundHandle);
}