4b825dc642cb6eb9a060e54bf8d69288fbee490494a6641b73026d662261604d7d192beae70b11dc
 
 
1
#include "system.h"
 
 
2
#include "prototyp.h"
 
 
3
#include "stratdef.h"
 
 
4
#include "game_statistics.h"
 
 
5
#include "bh_types.h"
 
 
6
#include "npc_facehugger.h"
 
 
7
#include "weapons.h"
 
 
8
#include "psndplat.h"
 
 
9
#include "targeting.h"
 
 
10
#include "hud.h"
 
 
11
#include "pheromon.h"
 
 
12
#include "pldghost.h"
 
 
13
#include "npc_marine.h"
 
 
14
#include "extents.h"
 
 
15
#include <stdio.h>
 
 
16
#include <stdlib.h>
 
 
17
#include <assert.h>
 
 
18
 
 
 
19
extern int deathFadeLevel;
 
 
20
extern int RealFrameTime;
 
 
21
extern void FadeDownScreen(int brightness, int colour);
 
 
22
 
 
 
23
enum HuggerSubSequences
 
 
24
{
 
 
25
    HSS_Stand = 0,
 
 
26
    HSS_Run,
 
 
27
    HSS_Dies,
 
 
28
    HSS_Jump,
 
 
29
    HSS_Attack,
 
 
30
    HSS_DieOnFire,
 
 
31
    HSS_Floats
 
 
32
};
 
 
33
 
 
 
34
#define HUGGER_STATE_PRINT    0
 
 
35
 
 
 
36
extern uint8_t Null_Name[8];
 
 
37
 
 
 
38
extern SECTION * GetNamedHierarchyFromLibrary(const char * rif_name, const char *);
 
 
39
extern void AssignNewSBName(STRATEGYBLOCK *sbPtr);
 
 
40
 
 
 
41
static int make_new_facehugger(STRATEGYBLOCK* sbPtr, FACEHUGGER_NEAR_BHSTATE starting_state)
 
 
42
{
 
 
43
    sbPtr->dataptr = malloc(sizeof(FACEHUGGER_STATUS_BLOCK));
 
 
44
 
 
 
45
    if(NULL == sbPtr->dataptr)
 
 
46
    {                       
 
 
47
        RemoveBehaviourStrategy(sbPtr);
 
 
48
        return 0;
 
 
49
    }
 
 
50
 
 
 
51
    sbPtr->maintainVisibility = 1;
 
 
52
    sbPtr->containingModule = ModuleFromPosition(&sbPtr->DynPtr->Position, NULL);
 
 
53
 
 
 
54
    FACEHUGGER_STATUS_BLOCK *new_facehugger = (FACEHUGGER_STATUS_BLOCK *)sbPtr->dataptr;
 
 
55
 
 
 
56
    new_facehugger->HModelController.Deltas = NULL;
 
 
57
    new_facehugger->HModelController.Root_Section = NULL;
 
 
58
    new_facehugger->HModelController.section_data = NULL;
 
 
59
    new_facehugger->nearBehaviourState = starting_state;
 
 
60
    new_facehugger->stateTimer = 0;
 
 
61
    new_facehugger->CurveRadius = 0;
 
 
62
    new_facehugger->CurveLength = 0;
 
 
63
    new_facehugger->CurveTimeOut = 0;
 
 
64
    new_facehugger->soundHandle_fire = SOUND_NOACTIVEINDEX;
 
 
65
 
 
 
66
    {
 
 
67
    int i = 0;
 
 
68
    for(; i < SB_NAME_LENGTH; i++)
 
 
69
        new_facehugger->death_target_ID[i] = 0;
 
 
70
    }
 
 
71
 
 
 
72
    COPY_NAME(new_facehugger->Target_SBname, Null_Name);
 
 
73
    new_facehugger->death_target_sbptr = NULL;
 
 
74
    new_facehugger->death_target_request = 0;
 
 
75
    new_facehugger->Target = NULL;
 
 
76
 
 
 
77
    NPC_InitMovementData(&new_facehugger->moveData);
 
 
78
 
 
 
79
    {
 
 
80
        const NPC_DATA *NpcData = &NpcDataList[I_NPC_FaceHugger];
 
 
81
        sbPtr->DamageBlock = NpcData->StartingStats;
 
 
82
        sbPtr->DamageBlock.Health = NpcData->StartingStats.Health << ONE_FIXED_SHIFT;
 
 
83
        sbPtr->DamageBlock.Armour = NpcData->StartingStats.Armour << ONE_FIXED_SHIFT;
 
 
84
    }
 
 
85
 
 
 
86
    SECTION *root_section = GetNamedHierarchyFromLibrary("hnpchugger", "Template");
 
 
87
 
 
 
88
    if (!root_section || !sbPtr->containingModule)
 
 
89
    {
 
 
90
        RemoveBehaviourStrategy(sbPtr);
 
 
91
        return 0;
 
 
92
    }
 
 
93
 
 
 
94
    Create_HModel(&new_facehugger->HModelController, root_section);
 
 
95
    InitHModelSequence(&new_facehugger->HModelController, 0, 0, ONE_FIXED);
 
 
96
    ProveHModel_Far(&new_facehugger->HModelController, sbPtr);
 
 
97
 
 
 
98
    return 1;
 
 
99
}
 
 
100
 
 
 
101
static void PlotFaceHugger(STRATEGYBLOCK *sbPtr) 
 
 
102
{
 
 
103
    extern void DisplayFacehuggerOnHud(DISPLAYBLOCK *dbPtr);
 
 
104
 
 
 
105
    DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
 
 
106
    assert(sbPtr->DisplayBlock);    
 
 
107
 
 
 
108
    {
 
 
109
        VECTORCH x,y,z;
 
 
110
 
 
 
111
        x.vx = -Global_VDB.VDB_Mat.mat11;
 
 
112
        x.vy = -Global_VDB.VDB_Mat.mat21;
 
 
113
        x.vz = -Global_VDB.VDB_Mat.mat31;
 
 
114
        y.vx = -Global_VDB.VDB_Mat.mat13;
 
 
115
        y.vy = -Global_VDB.VDB_Mat.mat23;
 
 
116
        y.vz = -Global_VDB.VDB_Mat.mat33;
 
 
117
        z.vx = -Global_VDB.VDB_Mat.mat12;
 
 
118
        z.vy = -Global_VDB.VDB_Mat.mat22;
 
 
119
        z.vz = -Global_VDB.VDB_Mat.mat32;
 
 
120
 
 
 
121
        Normalise(&x);
 
 
122
        Normalise(&y);
 
 
123
        Normalise(&z);
 
 
124
 
 
 
125
        dynPtr->OrientMat.mat11 = x.vx;
 
 
126
        dynPtr->OrientMat.mat12 = x.vy;
 
 
127
        dynPtr->OrientMat.mat13 = x.vz;
 
 
128
        dynPtr->OrientMat.mat21 = y.vx;
 
 
129
        dynPtr->OrientMat.mat22 = y.vy;
 
 
130
        dynPtr->OrientMat.mat23 = y.vz;
 
 
131
        dynPtr->OrientMat.mat31 = z.vx;
 
 
132
        dynPtr->OrientMat.mat32 = z.vy;
 
 
133
        dynPtr->OrientMat.mat33 = z.vz;
 
 
134
    }
 
 
135
 
 
 
136
    /* set position */
 
 
137
    dynPtr->Position.vx = 0;
 
 
138
    dynPtr->Position.vz = FACEHUGGER_ATTACKZOFFSET / 4;
 
 
139
    dynPtr->Position.vy = FACEHUGGER_ATTACKYOFFSET;
 
 
140
 
 
 
141
    {
 
 
142
        MATRIXCH myMat = Global_VDB.VDB_Mat;
 
 
143
        TransposeMatrixCH(&myMat);
 
 
144
        RotateVector(&dynPtr->Position, &myMat);    
 
 
145
    }
 
 
146
 
 
 
147
    dynPtr->Position.vx += Global_VDB.VDB_World.vx;
 
 
148
    dynPtr->Position.vy += Global_VDB.VDB_World.vy;
 
 
149
    dynPtr->Position.vz += Global_VDB.VDB_World.vz;
 
 
150
 
 
 
151
    sbPtr->DisplayBlock->ObFlags &= ~ObFlag_NotVis;
 
 
152
    sbPtr->DisplayBlock->ObWorld = dynPtr->Position;
 
 
153
    sbPtr->DisplayBlock->ObMat = dynPtr->OrientMat;
 
 
154
 
 
 
155
    ProveHModel(sbPtr->DisplayBlock->HModelControlBlock, sbPtr->DisplayBlock);
 
 
156
    DisplayFacehuggerOnHud(sbPtr->DisplayBlock);
 
 
157
 
 
 
158
    sbPtr->DisplayBlock->ObFlags |= ObFlag_NotVis;
 
 
159
 
 
 
160
    if(deathFadeLevel > ONE_FIXED/2)
 
 
161
    {
 
 
162
        deathFadeLevel -= RealFrameTime / 8;
 
 
163
        //FadeDownScreen(deathFadeLevel, 0);
 
 
164
    }
 
 
165
}
 
 
166
 
 
 
167
void InitFacehuggerBehaviour(void* bhdata, STRATEGYBLOCK *sbPtr)
 
 
168
{
 
 
169
    TOOLS_DATA_FACEHUGGER *toolsData = (TOOLS_DATA_FACEHUGGER *)bhdata; 
 
 
170
    int i;
 
 
171
 
 
 
172
    for(i=0; i < SB_NAME_LENGTH; i++)
 
 
173
        sbPtr->SBname[i] = toolsData->nameID[i];
 
 
174
 
 
 
175
    sbPtr->DynPtr = AllocateDynamicsBlock(DYNAMICS_TEMPLATE_DEFAULT);
 
 
176
 
 
 
177
    if(sbPtr->DynPtr)
 
 
178
    {
 
 
179
        DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
 
 
180
        dynPtr->PrevPosition = dynPtr->Position = toolsData->position;
 
 
181
        CreateEulerMatrix(&dynPtr->OrientEuler, &dynPtr->OrientMat);
 
 
182
        TransposeMatrixCH(&dynPtr->OrientMat);              
 
 
183
        dynPtr->Mass = 3;
 
 
184
        dynPtr->ToppleForce = TOPPLE_FORCE_ALIEN;
 
 
185
 
 
 
186
        if(make_new_facehugger(sbPtr, (toolsData->startInactive ? FHNS_Floating : FHNS_Approach)))
 
 
187
        {
 
 
188
            FACEHUGGER_STATUS_BLOCK *facehuggerStatus = (FACEHUGGER_STATUS_BLOCK *)sbPtr->dataptr;
 
 
189
 
 
 
190
            if (toolsData->startInactive)
 
 
191
            {
 
 
192
                InitHModelTweening(&facehuggerStatus->HModelController, (ONE_FIXED>>3), (int)HMSQT_Hugger, (int)HSS_Floats, (ONE_FIXED << 1),
1);
 
 
193
                sbPtr->DynPtr->GravityOn = 0;
 
 
194
            }
 
 
195
            else
 
 
196
            {
 
 
197
                InitHModelTweening(&facehuggerStatus->HModelController, (ONE_FIXED>>3), (int)HMSQT_Hugger, (int)HSS_Stand, ONE_FIXED, 0);
 
 
198
                facehuggerStatus->HModelController.Playing = 0; // don't show up on motion tracker
 
 
199
            }
 
 
200
 
 
 
201
            for(i=0; i < SB_NAME_LENGTH; i++)
 
 
202
                facehuggerStatus->death_target_ID[i] = toolsData->death_target_ID[i];
 
 
203
 
 
 
204
            facehuggerStatus->death_target_request = toolsData->death_target_request;
 
 
205
        }
 
 
206
    }
 
 
207
    else
 
 
208
    {
 
 
209
        RemoveBehaviourStrategy(sbPtr);
 
 
210
    }
 
 
211
}
 
 
212
 
 
 
213
static void Execute_FHNS_Approach(STRATEGYBLOCK *sbPtr)
 
 
214
{
 
 
215
    FACEHUGGER_STATUS_BLOCK *fhugStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->dataptr);    
 
 
216
    DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
 
 
217
 
 
 
218
    /* do climb on walls, etc */
 
 
219
    dynPtr->UseStandardGravity = 1;
 
 
220
    /* If you think I'm going to let facehuggers climb on walls for *
 
 
221
     * ONE SECOND, you are INSANE, Jack!!! */
 
 
222
 
 
 
223
    VECTORCH targetPos = fhugStatusPointer->Target->DynPtr->Position;
 
 
224
 
 
 
225
    /* translate target into hugger local space */
 
 
226
    {
 
 
227
        MATRIXCH toLocalSpaceMatrix = dynPtr->OrientMat;
 
 
228
        TransposeMatrixCH(&toLocalSpaceMatrix);
 
 
229
 
 
 
230
        targetPos.vx -= dynPtr->Position.vx;
 
 
231
        targetPos.vy -= dynPtr->Position.vy;
 
 
232
        targetPos.vz -= dynPtr->Position.vz;
 
 
233
        RotateVector(&targetPos, &toLocalSpaceMatrix);
 
 
234
    }
 
 
235
 
 
 
236
    /* Fix vy. */
 
 
237
    targetPos.vy = 0;
 
 
238
 
 
 
239
    /* tracking movement */
 
 
240
    if (dynPtr->IsInContactWithFloor)
 
 
241
    {
 
 
242
        int distanceToTarget = Magnitude(&targetPos);
 
 
243
 
 
 
244
        if (fhugStatusPointer->CurveTimeOut <= 0)
 
 
245
        {
 
 
246
            fhugStatusPointer->CurveLength = distanceToTarget;
 
 
247
            fhugStatusPointer->CurveRadius = ((FastRandom()&16383)-8192)*2;
 
 
248
            fhugStatusPointer->CurveTimeOut= ONE_FIXED * 3;
 
 
249
        }
 
 
250
        else
 
 
251
        {
 
 
252
            fhugStatusPointer->CurveTimeOut -= NormalFrameTime;
 
 
253
        }
 
 
254
 
 
 
255
        int offset = MUL_FIXED(fhugStatusPointer->CurveRadius, GetCos((1024 * distanceToTarget / fhugStatusPointer->CurveLength) & 4095));
 
 
256
 
 
 
257
        dynPtr->LinVelocity.vx = 
 
 
258
            WideMulNarrowDiv(FACEHUGGER_NEAR_SPEED, targetPos.vx, distanceToTarget)
 
 
259
            -WideMulNarrowDiv(offset, targetPos.vz, distanceToTarget);
 
 
260
 
 
 
261
        dynPtr->LinVelocity.vz = 
 
 
262
            WideMulNarrowDiv(FACEHUGGER_NEAR_SPEED, targetPos.vz, distanceToTarget)
 
 
263
            +
 
 
264
            WideMulNarrowDiv(offset, targetPos.vx, distanceToTarget);
 
 
265
 
 
 
266
        RotateVector(&dynPtr->LinVelocity, &dynPtr->OrientMat);
 
 
267
        /* align to velocity */
 
 
268
 
 
 
269
        NPCOrientateToVector(sbPtr, &dynPtr->LinVelocity, NPC_TURNRATE);
 
 
270
 
 
 
271
        //if(!DynamicObjectIsMoving(&fhugStatusPointer->Target->DynPtr))
 
 
272
        {
 
 
273
            /* should we jump at the player? */
 
 
274
            int distanceToTarget = VectorDistance(&dynPtr->Position, &fhugStatusPointer->Target->DynPtr->Position);
 
 
275
 
 
 
276
            if(distanceToTarget <= FACEHUGGER_JUMPDISTANCE)
 
 
277
            {
 
 
278
                fhugStatusPointer->nearBehaviourState = FHNS_AboutToJump;
 
 
279
                fhugStatusPointer->stateTimer = 0;
 
 
280
                return;
 
 
281
            }
 
 
282
        }
 
 
283
    }
 
 
284
 
 
 
285
    {
 
 
286
        STRATEGYBLOCK *destructableObject;
 
 
287
        NPC_OBSTRUCTIONREPORT obstruction;
 
 
288
        NPC_IsObstructed(sbPtr, &fhugStatusPointer->moveData, &obstruction, &destructableObject);
 
 
289
 
 
 
290
        if(obstruction.environment)
 
 
291
        {
 
 
292
            /* go to avoidance */
 
 
293
            NPC_InitMovementData(&fhugStatusPointer->moveData);
 
 
294
            NPCGetAvoidanceDirection(sbPtr, &fhugStatusPointer->moveData.avoidanceDirn, &obstruction);
 
 
295
            fhugStatusPointer->nearBehaviourState = FHNS_Avoidance;
 
 
296
            fhugStatusPointer->stateTimer = NPC_AVOIDTIME;
 
 
297
            /* no sequence change required */
 
 
298
            return;
 
 
299
        }
 
 
300
        else if(obstruction.destructableObject)
 
 
301
        {
 
 
302
            assert(destructableObject);
 
 
303
            CauseDamageToObject(destructableObject, &TemplateAmmo[AMMO_NPC_OBSTACLE_CLEAR].MaxDamage, ONE_FIXED, NULL);
 
 
304
        }
 
 
305
    }
 
 
306
 
 
 
307
    {
 
 
308
        VECTORCH velocityDirection = dynPtr->LinVelocity;
 
 
309
        Normalise(&velocityDirection);
 
 
310
 
 
 
311
        if(NPC_CannotReachTarget(&fhugStatusPointer->moveData, &targetPos, &velocityDirection))
 
 
312
        {
 
 
313
            /* go to avoidance */
 
 
314
            NPC_OBSTRUCTIONREPORT obstruction = {1,0,0,0};
 
 
315
            NPC_InitMovementData(&fhugStatusPointer->moveData);
 
 
316
            NPCGetAvoidanceDirection(sbPtr, &fhugStatusPointer->moveData.avoidanceDirn, &obstruction);
 
 
317
            fhugStatusPointer->nearBehaviourState = FHNS_Avoidance;
 
 
318
            fhugStatusPointer->stateTimer = NPC_AVOIDTIME;
 
 
319
            /* no sequence change required */
 
 
320
        }
 
 
321
    }
 
 
322
}
 
 
323
 
 
 
324
static void FHugApplyPounceImpulse(STRATEGYBLOCK *sbPtr)
 
 
325
{
 
 
326
    VECTORCH pounceVector, targetPoint;
 
 
327
 
 
 
328
    FACEHUGGER_STATUS_BLOCK *fhugStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->dataptr);    
 
 
329
    DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
 
 
330
 
 
 
331
    if (fhugStatusPointer->Target)
 
 
332
        GetTargetingPointOfObject(fhugStatusPointer->Target->DisplayBlock, &targetPoint);
 
 
333
 
 
 
334
    int dist = VectorDistance(&dynPtr->Position, &targetPoint);
 
 
335
 
 
 
336
    /* Apply a correction based on range. */
 
 
337
    targetPoint.vy -= (dist >> 3);
 
 
338
 
 
 
339
    pounceVector.vx = targetPoint.vx - dynPtr->Position.vx;
 
 
340
    pounceVector.vy = targetPoint.vy - dynPtr->Position.vy;
 
 
341
    pounceVector.vz = targetPoint.vz - dynPtr->Position.vz;
 
 
342
 
 
 
343
    Normalise(&pounceVector);
 
 
344
    /* Must jump at least a little bit upwards. */
 
 
345
 
 
 
346
    if (pounceVector.vy > -10000)
 
 
347
        pounceVector.vy = -10000;
 
 
348
 
 
 
349
    pounceVector.vx = MUL_FIXED(FACEHUGGER_JUMP_SPEED, pounceVector.vx);
 
 
350
    pounceVector.vy = MUL_FIXED(FACEHUGGER_JUMP_SPEED, pounceVector.vy);
 
 
351
    pounceVector.vz = MUL_FIXED(FACEHUGGER_JUMP_SPEED, pounceVector.vz);
 
 
352
 
 
 
353
    sbPtr->DynPtr->LinImpulse = pounceVector;
 
 
354
}
 
 
355
 
 
 
356
static void Execute_FHNS_Jumping(STRATEGYBLOCK *sbPtr)
 
 
357
{
 
 
358
    FACEHUGGER_STATUS_BLOCK *fhugStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->dataptr);    
 
 
359
    DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
 
 
360
 
 
 
361
    /* don't climb on walls, etc */
 
 
362
    dynPtr->UseStandardGravity = 1;
 
 
363
 
 
 
364
    /* Firstly, are we actually pouncing yet? */
 
 
365
    /* NearStateTimer is a status flag. */
 
 
366
 
 
 
367
    if (fhugStatusPointer->stateTimer != 2)
 
 
368
    {
 
 
369
        /* Still tweening? */
 
 
370
 
 
 
371
        if (fhugStatusPointer->HModelController.keyframe_flags)
 
 
372
        {
 
 
373
            /* We have the flag. */
 
 
374
            fhugStatusPointer->HModelController.Playing = 0;
 
 
375
            fhugStatusPointer->stateTimer = 1;
 
 
376
        }
 
 
377
 
 
 
378
        if (fhugStatusPointer->stateTimer == 1)
 
 
379
        {
 
 
380
            /* We've finished!  Are we facing right? */
 
 
381
            VECTORCH orientationDirn = { 0,0,0 };
 
 
382
 
 
 
383
            if (fhugStatusPointer->Target)
 
 
384
            {
 
 
385
                orientationDirn.vx = fhugStatusPointer->Target->DynPtr->Position.vx - dynPtr->Position.vx;
 
 
386
                orientationDirn.vz = fhugStatusPointer->Target->DynPtr->Position.vz - dynPtr->Position.vz;
 
 
387
            }
 
 
388
 
 
 
389
            if (!NPCOrientateToVector(sbPtr, &orientationDirn, NPC_TURNRATE << 2))
 
 
390
            {
 
 
391
                /* Still not right!  Wait for proper facing. */
 
 
392
                fhugStatusPointer->HModelController.Playing = 0;
 
 
393
                return;
 
 
394
            }
 
 
395
            else
 
 
396
            {
 
 
397
                /* Okay, pounce! */
 
 
398
                FHugApplyPounceImpulse(sbPtr);
 
 
399
                fhugStatusPointer->HModelController.Playing = 1;
 
 
400
                fhugStatusPointer->stateTimer = 2;
 
 
401
            }
 
 
402
        }
 
 
403
    }
 
 
404
    else if(dynPtr->CollisionReportPtr)
 
 
405
    {
 
 
406
        /* We must be in the jump.  Can't break out of this until we hit something. */
 
 
407
        COLLISIONREPORT *reportPtr = dynPtr->CollisionReportPtr;
 
 
408
 
 
 
409
        fhugStatusPointer->nearBehaviourState = FHNS_Approach;
 
 
410
        fhugStatusPointer->stateTimer = fhugStatusPointer->CurveTimeOut = 0;
 
 
411
 
 
 
412
        do
 
 
413
        {
 
 
414
            STRATEGYBLOCK *hitSbPtr = reportPtr->ObstacleSBPtr;
 
 
415
            /* You know what?  Just did! What? */
 
 
416
 
 
 
417
            if (hitSbPtr && hitSbPtr->DisplayBlock && (hitSbPtr->DisplayBlock == fhugStatusPointer->Target->DisplayBlock))
 
 
418
            {
 
 
419
                /* Got him, My Precious, we've Got Him! */
 
 
420
                /* Test for attach, or merely bite? */
 
 
421
                fhugStatusPointer->nearBehaviourState = FHNS_Attached;
 
 
422
                fhugStatusPointer->stateTimer = FACEHUGGER_NEARATTACKTIME;
 
 
423
                InitHModelTweening(&fhugStatusPointer->HModelController, (ONE_FIXED>>3), (int)HMSQT_Hugger, (int)HSS_Attack, ONE_FIXED, 1);
 
 
424
                dynPtr->DynamicsType = DYN_TYPE_NO_COLLISIONS;     /* turn off collisons */    
 
 
425
                dynPtr->GravityOn = sbPtr->maintainVisibility = 0; /* turn off visibility support- be carefull! */
 
 
426
                sbPtr->DisplayBlock->ObFlags |= ObFlag_NotVis; /* Make not vis */
 
 
427
 
 
 
428
                if (hitSbPtr->DisplayBlock == PlayerStatus.DisplayBlock) 
 
 
429
                {
 
 
430
                    deathFadeLevel = ONE_FIXED;
 
 
431
                    Sound_Play(SID_FACEHUGGERSLAP, "h");
 
 
432
                }
 
 
433
                break;
 
 
434
            }
 
 
435
 
 
 
436
            reportPtr = reportPtr->NextCollisionReportPtr;
 
 
437
 
 
 
438
        } while (reportPtr);
 
 
439
    }
 
 
440
}
 
 
441
 
 
 
442
static int TargetFilter(STRATEGYBLOCK *candidate)
 
 
443
{
 
 
444
    switch (candidate->type)
 
 
445
    {
 
 
446
        case I_BehaviourMarinePlayer:
 
 
447
        case I_BehaviourPredatorPlayer:
 
 
448
        case I_BehaviourPredator:
 
 
449
        case I_BehaviourMarine:
 
 
450
            return 1;
 
 
451
        case I_BehaviourNetGhost:
 
 
452
        {
 
 
453
            NETGHOSTDATABLOCK *dataptr = candidate->dataptr;
 
 
454
 
 
 
455
            switch (dataptr->type)
 
 
456
            {
 
 
457
                case I_BehaviourMarinePlayer:
 
 
458
                case I_BehaviourPredatorPlayer:
 
 
459
                    return 1;
 
 
460
                default:
 
 
461
                    return 0;
 
 
462
            }
 
 
463
        }
 
 
464
        default:
 
 
465
            return 0;
 
 
466
    }
 
 
467
}
 
 
468
 
 
 
469
static STRATEGYBLOCK *GetNewTarget(VECTORCH *facehuggerpos)
 
 
470
{
 
 
471
    int i,neardist = ONE_FIXED;
 
 
472
    STRATEGYBLOCK *nearest = NULL;
 
 
473
    MODULE *dmod = ModuleFromPosition(facehuggerpos, PlayerStatus.sbptr->containingModule);
 
 
474
    //MODULE *dmod = ModuleFromPosition(facehuggerpos, NULL);
 
 
475
 
 
 
476
    assert(dmod);
 
 
477
 
 
 
478
    for (i=0; i < NumActiveStBlocks; i++) 
 
 
479
    {
 
 
480
        STRATEGYBLOCK *candidate = ActiveStBlockList[i];
 
 
481
 
 
 
482
        if (TargetFilter(candidate)) 
 
 
483
        {
 
 
484
            VECTORCH offset;
 
 
485
 
 
 
486
            offset.vx = facehuggerpos->vx - candidate->DynPtr->Position.vx;
 
 
487
            offset.vy = facehuggerpos->vy - candidate->DynPtr->Position.vy;
 
 
488
            offset.vz = facehuggerpos->vz - candidate->DynPtr->Position.vz;
 
 
489
 
 
 
490
            int dist = Approximate3dMagnitude(&offset);
 
 
491
            /* Preferentially ignore predators? */
 
 
492
 
 
 
493
            if (candidate->type == I_BehaviourPredator) 
 
 
494
                dist <<= 2;
 
 
495
 
 
 
496
            if ((dist < neardist) && !NPC_IsDead(candidate) && IsModuleVisibleFromModule(dmod, candidate->containingModule)) 
 
 
497
            {
 
 
498
                nearest = candidate;
 
 
499
                neardist = dist;
 
 
500
            }
 
 
501
        }
 
 
502
    }
 
 
503
 
 
 
504
return nearest;
 
 
505
}
 
 
506
 
 
 
507
void FacehuggerBehaviour(STRATEGYBLOCK *sbPtr)
 
 
508
{
 
 
509
    FACEHUGGER_STATUS_BLOCK *facehuggerStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->dataptr);    
 
 
510
 
 
 
511
    /* set velocity to zero */
 
 
512
    sbPtr->DynPtr->LinVelocity.vx = sbPtr->DynPtr->LinVelocity.vy = sbPtr->DynPtr->LinVelocity.vz = 0;
 
 
513
 
 
 
514
    if(FHNS_Dying == facehuggerStatusPointer->nearBehaviourState)
 
 
515
    {
 
 
516
        #if HUGGER_STATE_PRINT
 
 
517
            printf("Hugger Dying...\n");
 
 
518
        #endif
 
 
519
 
 
 
520
        facehuggerStatusPointer->stateTimer -= NormalFrameTime;
 
 
521
 
 
 
522
        if(sbPtr->DisplayBlock)
 
 
523
        {
 
 
524
            sbPtr->DisplayBlock->SpecialFXFlags |= SFXFLAG_MELTINGINTOGROUND;
 
 
525
            sbPtr->DisplayBlock->ObFlags2 = facehuggerStatusPointer->stateTimer / 2;
 
 
526
        }
 
 
527
 
 
 
528
        sbPtr->please_destroy_me = (facehuggerStatusPointer->stateTimer <= 0);
 
 
529
        return;
 
 
530
    }
 
 
531
    else if(NULL == sbPtr->DisplayBlock)
 
 
532
    {
 
 
533
        return; // No far behaviour for facehuggers
 
 
534
    }
 
 
535
 
 
 
536
    if (sbPtr->DamageBlock.IsOnFire)
 
 
537
    {
 
 
538
        facehuggerStatusPointer->Target = NULL;
 
 
539
        facehuggerStatusPointer->nearBehaviourState = FHNS_Avoidance;
 
 
540
 
 
 
541
        CauseDamageToObject(sbPtr, &damage_profiles[FIREDAMAGE], NormalFrameTime, NULL);
 
 
542
 
 
 
543
        if (facehuggerStatusPointer->soundHandle_fire != SOUND_NOACTIVEINDEX)
 
 
544
            Sound_Update3d(facehuggerStatusPointer->soundHandle_fire, &sbPtr->DynPtr->Position);
 
 
545
        else
 
 
546
             Sound_Play(SID_FIRE, "dle", &sbPtr->DynPtr->Position, &facehuggerStatusPointer->soundHandle_fire);
 
 
547
    }
 
 
548
    else
 
 
549
    {
 
 
550
        if (facehuggerStatusPointer->soundHandle_fire != SOUND_NOACTIVEINDEX)
 
 
551
            Sound_Stop(facehuggerStatusPointer->soundHandle_fire);
 
 
552
 
 
 
553
        if(NULL != facehuggerStatusPointer->Target)
 
 
554
        {
 
 
555
            if (NPC_IsDead(facehuggerStatusPointer->Target) || !NAME_ISEQUAL(facehuggerStatusPointer->Target->SBname,
facehuggerStatusPointer->Target_SBname))
 
 
556
            {
 
 
557
                if(FHNS_Attached == facehuggerStatusPointer->nearBehaviourState)
 
 
558
                {
 
 
559
                    InitHModelTweening(&facehuggerStatusPointer->HModelController, (ONE_FIXED>>3), (int)HMSQT_Hugger, (int)HSS_Dies,
FACEHUGGER_DYINGTIME >> 3, 1);
 
 
560
                    sbPtr->DynPtr->GravityOn = 1;
 
 
561
 
 
 
562
                    facehuggerStatusPointer->nearBehaviourState = FHNS_Dying;
 
 
563
                    facehuggerStatusPointer->HModelController.Looped = 0;
 
 
564
                    facehuggerStatusPointer->HModelController.LoopAfterTweening = 0;
 
 
565
                    facehuggerStatusPointer->stateTimer = FACEHUGGER_DYINGTIME;
 
 
566
                    //facehuggerStatusPointer->Target = NULL;
 
 
567
                }
 
 
568
                else
 
 
569
                {
 
 
570
                    facehuggerStatusPointer->Target = NULL;
 
 
571
                    facehuggerStatusPointer->stateTimer = 0;
 
 
572
                    facehuggerStatusPointer->nearBehaviourState = FHNS_Wait;
 
 
573
                }
 
 
574
            }
 
 
575
        }
 
 
576
        else
 
 
577
        {
 
 
578
            facehuggerStatusPointer->Target = GetNewTarget(&sbPtr->DynPtr->Position);
 
 
579
 
 
 
580
            if (NULL != facehuggerStatusPointer->Target)
 
 
581
            {
 
 
582
                COPY_NAME(facehuggerStatusPointer->Target_SBname, facehuggerStatusPointer->Target->SBname);
 
 
583
                facehuggerStatusPointer->nearBehaviourState = FHNS_Approach;
 
 
584
            }
 
 
585
            else
 
 
586
            {
 
 
587
                facehuggerStatusPointer->stateTimer = 0;
 
 
588
                facehuggerStatusPointer->nearBehaviourState = FHNS_Wait;
 
 
589
            }
 
 
590
        }
 
 
591
    }
 
 
592
 
 
 
593
    #if DEBUG
 
 
594
    if(sbPtr->maintainVisibility)
 
 
595
        assert(ModuleCurrVisArray[sbPtr->containingModule->m_index]);
 
 
596
    #endif
 
 
597
 
 
 
598
    switch(facehuggerStatusPointer->nearBehaviourState)
 
 
599
    {
 
 
600
        case FHNS_Approach:
 
 
601
        {
 
 
602
            #if HUGGER_STATE_PRINT
 
 
603
                printf("Hugger Approaching...\n");
 
 
604
            #endif
 
 
605
 
 
 
606
            if(HSS_Run != facehuggerStatusPointer->HModelController.Sub_Sequence)
 
 
607
                InitHModelTweening(&facehuggerStatusPointer->HModelController, (ONE_FIXED>>3), (int)HMSQT_Hugger, (int)HSS_Run, (ONE_FIXED*2)/3,
1);
 
 
608
 
 
 
609
            Execute_FHNS_Approach(sbPtr);
 
 
610
        }
 
 
611
        break;
 
 
612
        case FHNS_Attached:
 
 
613
        {
 
 
614
            #if HUGGER_STATE_PRINT
 
 
615
                printf("Hugger Attacking...\n");
 
 
616
            #endif
 
 
617
 
 
 
618
            if (facehuggerStatusPointer->Target == PlayerStatus.sbptr)
 
 
619
            {
 
 
620
                PlayerStatus.sbptr->DamageBlock.Indestructable = 0; // there's no escape
 
 
621
                PlayerStatus.FirstPersonView = 1;
 
 
622
                PlotFaceHugger(sbPtr);
 
 
623
            }
 
 
624
 
 
 
625
            facehuggerStatusPointer->stateTimer -= NormalFrameTime;
 
 
626
 
 
 
627
            if(facehuggerStatusPointer->stateTimer <= 0)
 
 
628
            {
 
 
629
                facehuggerStatusPointer->stateTimer = FACEHUGGER_NEARATTACKTIME;
 
 
630
                CauseDamageToObject(facehuggerStatusPointer->Target, &TemplateAmmo[AMMO_FACEHUGGER].MaxDamage, ONE_FIXED, NULL);
 
 
631
            }
 
 
632
        }
 
 
633
        break;
 
 
634
        case FHNS_Jumping:
 
 
635
        {
 
 
636
            #if HUGGER_STATE_PRINT
 
 
637
                printf("Hugger Jumping...\n");
 
 
638
            #endif
 
 
639
 
 
 
640
            Execute_FHNS_Jumping(sbPtr);
 
 
641
        }
 
 
642
        break;
 
 
643
        case FHNS_AboutToJump:
 
 
644
        {
 
 
645
            #if HUGGER_STATE_PRINT
 
 
646
                printf("Hugger AboutToJump...\n");
 
 
647
            #endif
 
 
648
 
 
 
649
            /* Should still be playing Walk. */
 
 
650
            DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
 
 
651
 
 
 
652
            /* don't climb on walls, etc */
 
 
653
            dynPtr->UseStandardGravity = 1;
 
 
654
            /* Orientate to target. */
 
 
655
            VECTORCH orientationDirn;
 
 
656
            orientationDirn.vx = facehuggerStatusPointer->Target->DynPtr->Position.vx - dynPtr->Position.vx;
 
 
657
            orientationDirn.vy = 0;
 
 
658
            orientationDirn.vz = facehuggerStatusPointer->Target->DynPtr->Position.vz - dynPtr->Position.vz;
 
 
659
 
 
 
660
            if (NPCOrientateToVector(sbPtr, &orientationDirn, NPC_TURNRATE))
 
 
661
            {
 
 
662
                /* Okay, pounce! */
 
 
663
                int distanceToTarget = VectorDistance(&dynPtr->Position, &facehuggerStatusPointer->Target->DynPtr->Position);
 
 
664
 
 
 
665
                if((distanceToTarget <= FACEHUGGER_JUMPDISTANCE) && dynPtr->IsInContactWithFloor)
 
 
666
                {
 
 
667
                    InitHModelTweening(&facehuggerStatusPointer->HModelController, (ONE_FIXED>>3), (int)HMSQT_Hugger, (int)HSS_Jump, ONE_FIXED, 1);
 
 
668
                    facehuggerStatusPointer->HModelController.LoopAfterTweening = 0;
 
 
669
                    facehuggerStatusPointer->CurveTimeOut = 0;
 
 
670
                    facehuggerStatusPointer->HModelController.Looped = 0;
 
 
671
                    facehuggerStatusPointer->nearBehaviourState = FHNS_Jumping;
 
 
672
                }
 
 
673
                else
 
 
674
                {
 
 
675
                    /* Return to approach. */
 
 
676
                    NPC_InitMovementData(&facehuggerStatusPointer->moveData);
 
 
677
                    facehuggerStatusPointer->nearBehaviourState = FHNS_Approach;
 
 
678
                    /* no sequence change required */
 
 
679
                }
 
 
680
 
 
 
681
                facehuggerStatusPointer->stateTimer = 0;
 
 
682
            }
 
 
683
        }
 
 
684
        break;
 
 
685
        case FHNS_Wait:
 
 
686
        {
 
 
687
            #if HUGGER_STATE_PRINT
 
 
688
                printf("Hugger Waiting...\n");
 
 
689
            #endif
 
 
690
 
 
 
691
            if (facehuggerStatusPointer->Target)
 
 
692
            {
 
 
693
                NPC_InitMovementData(&facehuggerStatusPointer->moveData);
 
 
694
                facehuggerStatusPointer->nearBehaviourState = FHNS_Approach;
 
 
695
                facehuggerStatusPointer->stateTimer = 0;
 
 
696
                facehuggerStatusPointer->CurveTimeOut = 0;
 
 
697
            }
 
 
698
            else if(HSS_Stand != facehuggerStatusPointer->HModelController.Sub_Sequence)
 
 
699
            {
 
 
700
                InitHModelTweening(&facehuggerStatusPointer->HModelController, (ONE_FIXED>>3), (int)HMSQT_Hugger, (int)HSS_Stand, ONE_FIXED, 0);
 
 
701
                facehuggerStatusPointer->HModelController.Playing = 0; // don't show up on motion tracker
 
 
702
            }
 
 
703
        }
 
 
704
        break;
 
 
705
        case FHNS_Avoidance:
 
 
706
        {
 
 
707
            #if HUGGER_STATE_PRINT
 
 
708
                printf("Hugger Avoiding...\n");
 
 
709
            #endif
 
 
710
 
 
 
711
            int terminateState = 0;
 
 
712
 
 
 
713
            assert((facehuggerStatusPointer->moveData.avoidanceDirn.vx !=0)
 
 
714
            ||
 
 
715
            (facehuggerStatusPointer->moveData.avoidanceDirn.vy != 0)
 
 
716
            ||
 
 
717
            (facehuggerStatusPointer->moveData.avoidanceDirn.vz != 0));
 
 
718
 
 
 
719
            NPCSetVelocity(sbPtr, &facehuggerStatusPointer->moveData.avoidanceDirn, FACEHUGGER_NEAR_SPEED);
 
 
720
 
 
 
721
            /* next, decrement state timer */
 
 
722
            facehuggerStatusPointer->stateTimer -= NormalFrameTime;
 
 
723
 
 
 
724
            if(facehuggerStatusPointer->stateTimer <= 0)
 
 
725
                terminateState = 1;
 
 
726
 
 
 
727
            /* and check for an impeding collision */
 
 
728
            {
 
 
729
                STRATEGYBLOCK *destructableObject = NULL;
 
 
730
                NPC_OBSTRUCTIONREPORT obstruction;
 
 
731
                NPC_IsObstructed(sbPtr, &facehuggerStatusPointer->moveData, &obstruction, &destructableObject);
 
 
732
 
 
 
733
                if(obstruction.anySingleObstruction)
 
 
734
                    terminateState = 1;
 
 
735
            }
 
 
736
 
 
 
737
            if(terminateState)
 
 
738
            {
 
 
739
                if(facehuggerStatusPointer->Target)
 
 
740
                {
 
 
741
                    /* switch to approach */
 
 
742
                    NPC_InitMovementData(&facehuggerStatusPointer->moveData);
 
 
743
                    facehuggerStatusPointer->nearBehaviourState = FHNS_Approach;
 
 
744
                    facehuggerStatusPointer->stateTimer = 0;
 
 
745
                    /* no sequence change required */
 
 
746
                }
 
 
747
                else if (!sbPtr->DamageBlock.IsOnFire)
 
 
748
                {
 
 
749
                    /* switch to wait */
 
 
750
                    NPC_InitMovementData(&facehuggerStatusPointer->moveData);
 
 
751
                    facehuggerStatusPointer->nearBehaviourState = FHNS_Wait;          
 
 
752
                    facehuggerStatusPointer->stateTimer = 0;
 
 
753
                    /* no sequence change required */
 
 
754
                }
 
 
755
            }
 
 
756
        }
 
 
757
        break;
 
 
758
        case FHNS_Floating:
 
 
759
        {
 
 
760
            #if HUGGER_STATE_PRINT
 
 
761
                printf("Hugger Floating...\n");
 
 
762
            #endif
 
 
763
            DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
 
 
764
 
 
 
765
            dynPtr->LinVelocity.vx = dynPtr->LinVelocity.vy = dynPtr->LinVelocity.vz = 0;    
 
 
766
            dynPtr->LinImpulse.vx = dynPtr->LinImpulse.vy = dynPtr->LinImpulse.vz = 0;    
 
 
767
            dynPtr->GravityOn = 0;
 
 
768
 
 
 
769
            /* Just float there... */
 
 
770
        }
 
 
771
        default:
 
 
772
        break;
 
 
773
    }
 
 
774
}
 
 
775
 
 
 
776
void CreateFacehuggerBot(VECTORCH *Position)
 
 
777
{
 
 
778
    STRATEGYBLOCK* sbPtr = CreateActiveStrategyBlock(I_BehaviourFaceHugger);
 
 
779
 
 
 
780
    if(!sbPtr)
 
 
781
    {
 
 
782
        GADGET_NewOnScreenMessage("FAILED TO CREATE BOT: SB CREATION FAILURE");
 
 
783
        return;
 
 
784
    }
 
 
785
 
 
 
786
    sbPtr->DynPtr = AllocateDynamicsBlock(DYNAMICS_TEMPLATE_DEFAULT);
 
 
787
 
 
 
788
    if(sbPtr->DynPtr)
 
 
789
    {
 
 
790
        DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
 
 
791
        AssignNewSBName(sbPtr);
 
 
792
        dynPtr->PrevPosition = dynPtr->Position = *Position;
 
 
793
        CreateEulerMatrix(&dynPtr->OrientEuler, &dynPtr->OrientMat);
 
 
794
        TransposeMatrixCH(&dynPtr->OrientMat);
 
 
795
        dynPtr->Mass = 3;
 
 
796
        dynPtr->ToppleForce = TOPPLE_FORCE_ALIEN;
 
 
797
        make_new_facehugger(sbPtr, FHNS_Wait);
 
 
798
    }
 
 
799
    else
 
 
800
    {
 
 
801
        RemoveBehaviourStrategy(sbPtr);
 
 
802
        GADGET_NewOnScreenMessage("FAILED TO CREATE BOT: DYNBLOCK CREATION FAILURE");
 
 
803
    }
 
 
804
}
 
 
805
 
 
 
806
void CastFacehuggerBot()
 
 
807
{
 
 
808
    #define BOTRANGE 3000
 
 
809
 
 
 
810
    VECTORCH position;
 
 
811
    position = PlayerStatus.sbptr->DynPtr->Position;
 
 
812
    position.vx += MUL_FIXED(PlayerStatus.sbptr->DynPtr->OrientMat.mat31, BOTRANGE);
 
 
813
    position.vy += MUL_FIXED(PlayerStatus.sbptr->DynPtr->OrientMat.mat32, BOTRANGE);
 
 
814
    position.vz += MUL_FIXED(PlayerStatus.sbptr->DynPtr->OrientMat.mat33, BOTRANGE);
 
 
815
 
 
 
816
    CreateFacehuggerBot(&position);
 
 
817
}
 
 
818
 
 
 
819
void MakeFacehuggerNear(STRATEGYBLOCK *sbPtr)
 
 
820
{
 
 
821
    DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
 
 
822
    FACEHUGGER_STATUS_BLOCK *facehuggerStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->dataptr);    
 
 
823
    DISPLAYBLOCK *dPtr = CreateActiveObject();
 
 
824
 
 
 
825
    if(dPtr == NULL)
 
 
826
        return; /* cannot create displayblock, so leave far */
 
 
827
 
 
 
828
    sbPtr->DisplayBlock = dPtr;
 
 
829
    dPtr->ObStrategyBlock = sbPtr;
 
 
830
 
 
 
831
    /* need to initialise positional information in the new display block */ 
 
 
832
    dPtr->ObWorld = dynPtr->Position;
 
 
833
    dPtr->ObEuler = dynPtr->OrientEuler;
 
 
834
    dPtr->ObMat = dynPtr->OrientMat;
 
 
835
 
 
 
836
    facehuggerStatusPointer->stateTimer = 0;
 
 
837
    facehuggerStatusPointer->CurveRadius = 0;
 
 
838
    facehuggerStatusPointer->CurveLength = 0;
 
 
839
    facehuggerStatusPointer->CurveTimeOut = 0;
 
 
840
    facehuggerStatusPointer->Target = NULL;
 
 
841
 
 
 
842
       /* state and sequence init */
 
 
843
    dPtr->HModelControlBlock = &facehuggerStatusPointer->HModelController;
 
 
844
 
 
 
845
       switch(facehuggerStatusPointer->nearBehaviourState)
 
 
846
    {
 
 
847
        case FHNS_Floating:
 
 
848
        case FHNS_Dying:
 
 
849
        break;
 
 
850
        default:
 
 
851
        {
 
 
852
            NPC_InitMovementData(&facehuggerStatusPointer->moveData);
 
 
853
            facehuggerStatusPointer->nearBehaviourState = facehuggerStatusPointer->Target ? FHNS_Approach : FHNS_Wait;
 
 
854
        }
 
 
855
    }
 
 
856
 
 
 
857
    ProveHModel(dPtr->HModelControlBlock, dPtr);
 
 
858
}
 
 
859
 
 
 
860
void MakeFacehuggerFar(STRATEGYBLOCK *sbPtr)
 
 
861
{
 
 
862
    FACEHUGGER_STATUS_BLOCK *facehuggerStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->dataptr);    
 
 
863
 
 
 
864
    if (facehuggerStatusPointer->soundHandle_fire != SOUND_NOACTIVEINDEX)
 
 
865
        Sound_Stop(facehuggerStatusPointer->soundHandle_fire);
 
 
866
 
 
 
867
    DestroyActiveObject(&sbPtr->DisplayBlock);
 
 
868
    facehuggerStatusPointer->Target = NULL;
 
 
869
    facehuggerStatusPointer->stateTimer = 0;
 
 
870
    sbPtr->DynPtr->LinVelocity.vx = sbPtr->DynPtr->LinVelocity.vy = sbPtr->DynPtr->LinVelocity.vz = 0;    
 
 
871
 
 
 
872
    if(sbPtr->DamageBlock.IsOnFire)
 
 
873
    {
 
 
874
        facehuggerStatusPointer->nearBehaviourState = FHNS_Dying;
 
 
875
    }
 
 
876
    else
 
 
877
    {
 
 
878
        facehuggerStatusPointer->nearBehaviourState = FHNS_Wait;
 
 
879
        InitHModelTweening(&facehuggerStatusPointer->HModelController, (ONE_FIXED>>3), (int)HMSQT_Hugger, (int)HSS_Stand, ONE_FIXED, 0);
 
 
880
        facehuggerStatusPointer->HModelController.Playing = 0; // don't show up on motion tracker
 
 
881
    }
 
 
882
}
 
 
883
 
 
 
884
void FacehuggerIsDamaged(STRATEGYBLOCK *sbPtr, const DAMAGE_PROFILE *damage, VECTORCH *incoming)
 
 
885
{
 
 
886
    FACEHUGGER_STATUS_BLOCK *facehuggerStatusPointer = (FACEHUGGER_STATUS_BLOCK *)(sbPtr->dataptr);    
 
 
887
 
 
 
888
    //if (!sbPtr->DamageBlock.IsOnFire) Sound_Play(SID_ACID_SPRAY, "dp", &sbPtr->DynPtr->Position, (FastRandom() & 255) - 128);
 
 
889
 
 
 
890
    switch(facehuggerStatusPointer->nearBehaviourState)
 
 
891
    {
 
 
892
        case FHNS_Dying:
 
 
893
        return;
 
 
894
        case FHNS_Floating:
 
 
895
        {
 
 
896
            facehuggerStatusPointer->nearBehaviourState = FHNS_Approach;
 
 
897
            sbPtr->DynPtr->GravityOn = 1;
 
 
898
        } // no break;
 
 
899
        default:
 
 
900
        if (sbPtr->DamageBlock.Health <= 0)
 
 
901
        {
 
 
902
            DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
 
 
903
 
 
 
904
            switch(damage->Id)
 
 
905
            {
 
 
906
                case EXPLOSIONFIRE_BLAST:
 
 
907
                case AMMO_FRAGMENTATION_GRENADE:
 
 
908
                    Extreme_Gibbing(sbPtr, facehuggerStatusPointer->HModelController.section_data, ONE_FIXED, incoming);
 
 
909
                    sbPtr->please_destroy_me = 1;
 
 
910
                break;
 
 
911
                case AMMO_FLECHETTE: 
 
 
912
                    Extreme_Gibbing(sbPtr, facehuggerStatusPointer->HModelController.section_data, ONE_FIXED >> 4, incoming);
 
 
913
                    sbPtr->please_destroy_me = 1;
 
 
914
                break;
 
 
915
                default:
 
 
916
                {
 
 
917
                    /* Stop any hugger sound if playing */
 
 
918
                    /*If face hugger has a death target, send a request*/
 
 
919
                    if(facehuggerStatusPointer->death_target_sbptr)
 
 
920
                        RequestState(facehuggerStatusPointer->death_target_sbptr, facehuggerStatusPointer->death_target_request, 0);
 
 
921
 
 
 
922
                    /* More restrained death. */
 
 
923
                    /* switch sequence */
 
 
924
    InitHModelTweening(&facehuggerStatusPointer->HModelController, (ONE_FIXED>>3), (int)HMSQT_Hugger, (int)(sbPtr->DamageBlock.IsOnFire ?
HSS_DieOnFire : HSS_Dies), FACEHUGGER_DYINGTIME >> 3, 1);
 
 
925
 
 
 
926
                    facehuggerStatusPointer->HModelController.Looped = 0;
 
 
927
                    facehuggerStatusPointer->HModelController.LoopAfterTweening = 0;
 
 
928
                    facehuggerStatusPointer->stateTimer = FACEHUGGER_DYINGTIME;
 
 
929
 
 
 
930
                    /* stop motion */
 
 
931
                    dynPtr->Friction = 400000;
 
 
932
                    dynPtr->LinImpulse.vx = sbPtr->DynPtr->LinVelocity.vx;
 
 
933
                    dynPtr->LinImpulse.vy = sbPtr->DynPtr->LinVelocity.vy;
 
 
934
                    dynPtr->LinImpulse.vz = sbPtr->DynPtr->LinVelocity.vz;
 
 
935
                    dynPtr->LinVelocity.vx = sbPtr->DynPtr->LinVelocity.vy = sbPtr->DynPtr->LinVelocity.vz = 0;
 
 
936
                    /* Okay... */
 
 
937
                }
 
 
938
            }
 
 
939
 
 
 
940
                CurrentGameStats_CreatureKilled(sbPtr, NULL);
 
 
941
                facehuggerStatusPointer->nearBehaviourState = FHNS_Dying;
 
 
942
        }
 
 
943
    }
 
 
944
}
 
 
945
 
 
 
946
/*--------------------**
 
 
947
** Loading and Saving **
 
 
948
**--------------------*/
 
 
949
#include "savegame.h"
 
 
950
 
 
 
951
typedef struct face_hugger_save_block
 
 
952
{
 
 
953
    SAVE_BLOCK_STRATEGY_HEADER header;
 
 
954
 
 
 
955
    FACEHUGGER_NEAR_BHSTATE nearBehaviourState;    
 
 
956
    int stateTimer;
 
 
957
    int CurveRadius;
 
 
958
    int CurveLength;
 
 
959
    int CurveTimeOut;
 
 
960
    STRATEGYBLOCK *Target;
 
 
961
    char Target_SBname[SB_NAME_LENGTH];
 
 
962
 
 
 
963
    //strategyblock stuff
 
 
964
    DAMAGEBLOCK DamageBlock;
 
 
965
    DYNAMICSBLOCK dynamics;
 
 
966
 
 
 
967
} FACE_HUGGER_SAVE_BLOCK;
 
 
968
 
 
 
969
//defines for load/save macros
 
 
970
#define SAVELOAD_BLOCK block
 
 
971
#define SAVELOAD_BEHAV huggerStatusPointer
 
 
972
 
 
 
973
void LoadStrategy_FaceHugger(SAVE_BLOCK_STRATEGY_HEADER* header)
 
 
974
{
 
 
975
    FACE_HUGGER_SAVE_BLOCK* block = (FACE_HUGGER_SAVE_BLOCK*) header; 
 
 
976
 
 
 
977
    //check the size of the save block
 
 
978
    if(header->size != sizeof(*block))
 
 
979
        return;
 
 
980
 
 
 
981
    //find the existing strategy block
 
 
982
    STRATEGYBLOCK* sbPtr = FindSBWithName(header->SBname);
 
 
983
 
 
 
984
    if(!sbPtr)
 
 
985
        return;
 
 
986
 
 
 
987
    //make sure the strategy found is of the right type
 
 
988
    if(sbPtr->type != I_BehaviourFaceHugger)
 
 
989
        return;
 
 
990
 
 
 
991
    FACEHUGGER_STATUS_BLOCK* huggerStatusPointer = (FACEHUGGER_STATUS_BLOCK*)sbPtr->dataptr;
 
 
992
 
 
 
993
    //start copying stuff
 
 
994
    COPYELEMENT_LOAD(nearBehaviourState)
 
 
995
    COPYELEMENT_LOAD(stateTimer)
 
 
996
    COPYELEMENT_LOAD(CurveRadius)
 
 
997
    COPYELEMENT_LOAD(CurveLength)
 
 
998
    COPYELEMENT_LOAD(CurveTimeOut)
 
 
999
    COPY_NAME(huggerStatusPointer->Target_SBname, block->Target_SBname);
 
 
1000
    huggerStatusPointer->Target = FindSBWithName(huggerStatusPointer->Target_SBname);
 
 
1001
 
 
 
1002
    //copy strategy block stuff
 
 
1003
    *sbPtr->DynPtr = block->dynamics;
 
 
1004
    sbPtr->DamageBlock = block->DamageBlock;
 
 
1005
 
 
 
1006
    //load the hierarchy
 
 
1007
    {
 
 
1008
        SAVE_BLOCK_HEADER* hier_header = GetNextBlockIfOfType(SaveBlock_Hierarchy);
 
 
1009
        if(hier_header)
 
 
1010
            LoadHierarchy(hier_header,&huggerStatusPointer->HModelController);
 
 
1011
    }
 
 
1012
 
 
 
1013
    Load_SoundState(&huggerStatusPointer->soundHandle_fire);
 
 
1014
}
 
 
1015
 
 
 
1016
void SaveStrategy_FaceHugger(STRATEGYBLOCK* sbPtr)
 
 
1017
{
 
 
1018
    FACE_HUGGER_SAVE_BLOCK* block;
 
 
1019
    FACEHUGGER_STATUS_BLOCK* huggerStatusPointer = (FACEHUGGER_STATUS_BLOCK*)sbPtr->dataptr;
 
 
1020
 
 
 
1021
    GET_STRATEGY_SAVE_BLOCK(block, sbPtr);
 
 
1022
 
 
 
1023
    //start copying stuff
 
 
1024
    COPYELEMENT_SAVE(nearBehaviourState)
 
 
1025
    COPYELEMENT_SAVE(stateTimer)
 
 
1026
    COPYELEMENT_SAVE(CurveRadius)
 
 
1027
    COPYELEMENT_SAVE(CurveLength)
 
 
1028
    COPYELEMENT_SAVE(CurveTimeOut)
 
 
1029
    COPY_NAME(block->Target_SBname, huggerStatusPointer->Target_SBname);
 
 
1030
 
 
 
1031
    //save strategy block stuff
 
 
1032
    block->dynamics = *sbPtr->DynPtr;
 
 
1033
    block->dynamics.CollisionReportPtr = 0;
 
 
1034
    block->DamageBlock = sbPtr->DamageBlock;
 
 
1035
 
 
 
1036
    SaveHierarchy(&huggerStatusPointer->HModelController);
 
 
1037
    Save_SoundState(&huggerStatusPointer->soundHandle_fire);
 
 
1038
}