2504 lines
98 KiB
C
2504 lines
98 KiB
C
/*------------------------Patrick 14/1/97-----------------------------
|
|
This source file contains all the functions for the object visibility
|
|
management system (which controls visibility for aliens, objects,
|
|
pickups, autoguns, etc....)
|
|
It also contains initialisation and behaviour functions for
|
|
inanimate objects (like chairs, weapons, etc...).
|
|
--------------------------------------------------------------------*/
|
|
#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 "bh_alien.h"
|
|
#include "pvisible.h"
|
|
#include "bh_pred.h"
|
|
#include "bh_xeno.h"
|
|
#include "bh_paq.h"
|
|
#include "bh_queen.h"
|
|
#include "bh_marin.h"
|
|
#include "bh_fhug.h"
|
|
#include "bh_debri.h"
|
|
#include "bh_plachier.h"
|
|
#include "plat_shp.h"
|
|
#include "psnd.h"
|
|
#include "lighting.h"
|
|
#include "pldnet.h"
|
|
#include "bh_dummy.h"
|
|
#include "bh_videoscreen.h"
|
|
#include "bh_plift.h"
|
|
|
|
#if SupportWindows95
|
|
/* for win95 net game support */
|
|
#include "pldghost.h"
|
|
#endif
|
|
|
|
#include "pfarlocs.h"
|
|
|
|
#define UseLocalAssert Yes
|
|
#include "ourasert.h"
|
|
|
|
#define HMODEL_HACK 0
|
|
|
|
/* prototypes... */
|
|
static int WorldPointIsInModule(MODULE* thisModule, VECTORCH* thisPoint);
|
|
static int EmergencyRelocateObject(STRATEGYBLOCK *sbPtr);
|
|
static void EmergencyPlaceObjectInModule(STRATEGYBLOCK *sbPtr, AIMODULE* target);
|
|
static void FragmentInanimateObject(STRATEGYBLOCK *sbptr);
|
|
static void ExplodeInanimateObject(STRATEGYBLOCK *sbptr);
|
|
static void RespawnInanimateObject(STRATEGYBLOCK *sbPtr);
|
|
void KillFragmentalObjectForRespawn(STRATEGYBLOCK *sbPtr);
|
|
|
|
void IdentifyObject(STRATEGYBLOCK *sbPtr);
|
|
|
|
/* external global variables used in this file */
|
|
extern int ModuleArraySize;
|
|
extern char *ModuleCurrVisArray;
|
|
extern int NumActiveBlocks;
|
|
extern int NormalFrameTime;
|
|
extern int GlobalFrameCounter;
|
|
|
|
extern void ActivateSelfDestructSequence(int seconds);
|
|
|
|
extern SOUND3DDATA Explosion_SoundData;
|
|
|
|
/* this is a default map used in visibility management
|
|
NB If you use this, you MUST set the shapeIndex field appropriately*/
|
|
MODULEMAPBLOCK VisibilityDefaultObjectMap =
|
|
{
|
|
MapType_Default,
|
|
I_ShapeCube, /* this is a default value */
|
|
0,0,0,
|
|
0,0,0,
|
|
#if StandardStrategyAndCollisions
|
|
ObFlag_Dynamic|ObFlag_NewtonMovement|ObFlag_MatMul,
|
|
#else
|
|
0,
|
|
#endif
|
|
0,
|
|
0,
|
|
#if StandardStrategyAndCollisions
|
|
StrategyI_Null,
|
|
0,
|
|
GameCollStrat_Default,
|
|
ShapeCStrat_DoubleExtentEllipsoid,
|
|
0,
|
|
#endif
|
|
0,
|
|
0,
|
|
0,
|
|
#if StandardStrategyAndCollisions
|
|
0,
|
|
0,0,0,
|
|
#endif
|
|
0,0,0,
|
|
0,
|
|
0,
|
|
#if StandardStrategyAndCollisions
|
|
0,
|
|
0,
|
|
#endif
|
|
0,0,0,
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*---------------------Patrick 14/1/97-----------------------------
|
|
|
|
SOURCE FOR VISIBILITY MANAGEMENT SYSTEM
|
|
|
|
-----------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------Patrick 16/1/97-----------------------------
|
|
This function must be called to initialise the 'containingModule'
|
|
field in all strategyblocks for rif-loaded objects. This cannot
|
|
be done in the first-stage initialisation (via enableBehaviourTypes)
|
|
as the module system is not enabled at this point.
|
|
--------------------------------------------------------------------*/
|
|
void InitObjectVisibilities(void)
|
|
{
|
|
extern int NumActiveStBlocks;
|
|
extern STRATEGYBLOCK *ActiveStBlockList[];
|
|
|
|
int sbIndex = 0;
|
|
STRATEGYBLOCK *sbPtr;
|
|
|
|
/* loop thro' the strategy block list, looking for objects that will have
|
|
their visibilities managed ... */
|
|
while(sbIndex < NumActiveStBlocks)
|
|
{
|
|
sbPtr = ActiveStBlockList[sbIndex++];
|
|
if( (sbPtr->I_SBtype == I_BehaviourAlien)||
|
|
(sbPtr->I_SBtype == I_BehaviourMarine)||
|
|
(sbPtr->I_SBtype == I_BehaviourInanimateObject)||
|
|
(sbPtr->I_SBtype == I_BehaviourVideoScreen)||
|
|
(sbPtr->I_SBtype == I_BehaviourQueenAlien)||
|
|
(sbPtr->I_SBtype == I_BehaviourFaceHugger)||
|
|
(sbPtr->I_SBtype == I_BehaviourPredator)||
|
|
(sbPtr->I_SBtype == I_BehaviourAutoGun)||
|
|
(sbPtr->I_SBtype == I_BehaviourPlatform)||
|
|
(sbPtr->I_SBtype == I_BehaviourBinarySwitch && sbPtr->DynPtr)||/*Allow for switches with no shape*/
|
|
(sbPtr->I_SBtype == I_BehaviourLinkSwitch && sbPtr->DynPtr)||
|
|
(sbPtr->I_SBtype == I_BehaviourTrackObject)||
|
|
(sbPtr->I_SBtype == I_BehaviourFan)||
|
|
(sbPtr->I_SBtype == I_BehaviourPlacedHierarchy)||
|
|
(sbPtr->I_SBtype == I_BehaviourPlacedLight)||
|
|
(sbPtr->I_SBtype == I_BehaviourXenoborg)||
|
|
(sbPtr->I_SBtype == I_BehaviourSeal)||
|
|
(sbPtr->I_SBtype == I_BehaviourDatabase)||
|
|
(sbPtr->I_SBtype == I_BehaviourDummy)||
|
|
(sbPtr->I_SBtype == I_BehaviourPredatorAlien)
|
|
)
|
|
{
|
|
DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
|
|
LOCALASSERT(dynPtr);
|
|
|
|
sbPtr->containingModule = ModuleFromPosition(&(dynPtr->Position), (MODULE *)0);
|
|
|
|
sbPtr->maintainVisibility = 1;
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
/* just in case ... */
|
|
if(sbPtr->I_SBtype != I_BehaviourGenerator &&
|
|
sbPtr->I_SBtype != I_BehaviourMarinePlayer)
|
|
{
|
|
sbPtr->containingModule = (MODULE *)0;
|
|
}
|
|
sbPtr->maintainVisibility = 0;
|
|
}
|
|
|
|
}
|
|
|
|
/* initialise each object's visibility */
|
|
DoObjectVisibilities();
|
|
|
|
}
|
|
|
|
|
|
/*----------------------Patrick 13/1/97-----------------------------
|
|
This function should be called after the dynamics, and before
|
|
rendering (ie via Cris H's module handler call-back function).
|
|
|
|
It identifies appropriate strategy blocks in the current block list,
|
|
(those which have a non-zero 'containingModule' field), and calls
|
|
DoObjectVisibility().
|
|
--------------------------------------------------------------------*/
|
|
|
|
void DoObjectVisibilities(void)
|
|
{
|
|
extern int NumActiveStBlocks;
|
|
extern STRATEGYBLOCK *ActiveStBlockList[];
|
|
|
|
int sbIndex = 0;
|
|
STRATEGYBLOCK *sbPtr;
|
|
|
|
/* loop thro' the strategy block list, looking for objects that need to have
|
|
their visibilities managed ... */
|
|
while(sbIndex < NumActiveStBlocks)
|
|
{
|
|
sbPtr = ActiveStBlockList[sbIndex++];
|
|
if(sbPtr->maintainVisibility)
|
|
DoObjectVisibility(sbPtr);
|
|
}
|
|
}
|
|
|
|
|
|
void DoObjectVisibility(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
if(!(sbPtr->SBdptr))
|
|
{
|
|
/* Note that we don't call modulefromposition() for far objects, as they mostly don't
|
|
move, and those that do (eg AIs) move to precalculated positions in modules. Thus
|
|
invisible objects are responsible for looking after their own containingModule field.
|
|
This should always be ok: we should always have a correct and valid containing
|
|
module. However, we will do a paranoia check for a null containingModule... */
|
|
if(!sbPtr->containingModule)
|
|
{
|
|
textprint("Calling Far EmergencyRelocateObject, On object %x, type %d!\n",(int)sbPtr, sbPtr->I_SBtype);
|
|
IdentifyObject(sbPtr);
|
|
if(!(EmergencyRelocateObject(sbPtr))) {
|
|
textprint("Relocate failed!\n");
|
|
return;
|
|
}
|
|
}
|
|
if (!sbPtr->containingModule)
|
|
{
|
|
textprint("Relocate failed and reported success\n");
|
|
return;
|
|
}
|
|
|
|
/* Now do the visibility check: the object has no display block, so check if
|
|
it's module is visible. If so, make the object visibile too. */
|
|
|
|
|
|
if ( (sbPtr->I_SBtype == I_BehaviourPlacedLight)
|
|
&&ThisObjectIsInAModuleVisibleFromCurrentlyVisibleModules(sbPtr))
|
|
{
|
|
MakePlacedLightNear(sbPtr);
|
|
return;
|
|
}
|
|
else if (sbPtr->I_SBtype == I_BehaviourNetGhost)
|
|
{
|
|
NETGHOSTDATABLOCK *ghostDataPtr = (NETGHOSTDATABLOCK *)sbPtr->SBdataptr;
|
|
if (ghostDataPtr && ghostDataPtr->type == I_BehaviourFlareGrenade)
|
|
{
|
|
if(ThisObjectIsInAModuleVisibleFromCurrentlyVisibleModules(sbPtr))
|
|
{
|
|
MakeGhostNear(sbPtr);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else if (sbPtr->I_SBtype == I_BehaviourPlatform)
|
|
{
|
|
PLATFORMLIFT_BEHAVIOUR_BLOCK *platformliftdata = (PLATFORMLIFT_BEHAVIOUR_BLOCK *)sbPtr->SBdataptr;
|
|
//platform lift needs to be made near if its module near or
|
|
//if it is moving
|
|
if(ModuleCurrVisArray[(sbPtr->containingModule->m_index)] ||
|
|
platformliftdata->state==PLBS_GoingUp ||
|
|
platformliftdata->state==PLBS_GoingDown)
|
|
{
|
|
MakeObjectNear(sbPtr);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(ModuleCurrVisArray[(sbPtr->containingModule->m_index)])
|
|
{
|
|
/* module is visible, so make object visible too */
|
|
switch(sbPtr->I_SBtype)
|
|
{
|
|
case(I_BehaviourAlien):
|
|
{
|
|
MakeAlienNear(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourVideoScreen):
|
|
{
|
|
MakeObjectNear(sbPtr);
|
|
if(sbPtr->SBdptr) AddLightingEffectToObject(sbPtr->SBdptr,LFX_FLARE);
|
|
break;
|
|
}
|
|
case(I_BehaviourRubberDuck):
|
|
case(I_BehaviourInanimateObject):
|
|
{
|
|
MakeObjectNear(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourAutoGun):
|
|
{
|
|
MakeSentrygunNear(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourPlatform):
|
|
{
|
|
MakeObjectNear(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourBinarySwitch):
|
|
{
|
|
MakeObjectNear(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourDatabase):
|
|
{
|
|
MakeObjectNear(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourLinkSwitch):
|
|
{
|
|
MakeObjectNear(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourPredator):
|
|
{
|
|
MakePredatorNear(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourXenoborg):
|
|
{
|
|
MakeXenoborgNear(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourQueenAlien):
|
|
{
|
|
MakeQueenNear(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourPredatorAlien):
|
|
{
|
|
GLOBALASSERT(0);
|
|
//MakePAQNear(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourFaceHugger):
|
|
{
|
|
MakeFacehuggerNear(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourMarine):
|
|
{
|
|
MakeMarineNear(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourSeal):
|
|
{
|
|
MakeMarineNear(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourNetGhost):
|
|
{
|
|
NETGHOSTDATABLOCK *ghostDataPtr = (NETGHOSTDATABLOCK *)sbPtr->SBdataptr;
|
|
|
|
/* KJL 16:42:40 23/01/99 - near behaviour is triggered differently for
|
|
lightsources such as flares */
|
|
if (ghostDataPtr && ghostDataPtr->type != I_BehaviourFlareGrenade)
|
|
{
|
|
MakeGhostNear(sbPtr);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case(I_BehaviourTrackObject):
|
|
{
|
|
MakeObjectNear(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourFan):
|
|
{
|
|
MakeObjectNear(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourNetCorpse):
|
|
{
|
|
MakeCorpseNear(sbPtr);
|
|
break;
|
|
}
|
|
case (I_BehaviourPlacedHierarchy):
|
|
{
|
|
MakePlacedHierarchyNear(sbPtr);
|
|
break;
|
|
}
|
|
case (I_BehaviourPlacedLight):
|
|
{
|
|
/* KJL 16:42:40 23/01/99 - do nothing; near behaviour is triggered
|
|
differently for lightsources */
|
|
break;
|
|
}
|
|
case (I_BehaviourDummy):
|
|
{
|
|
MakeDummyNear(sbPtr);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
/* only the above object types should get here */
|
|
LOCALASSERT(1==0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* object is currently visible.
|
|
first need to get it's module, as it may have moved under dynamics */
|
|
MODULE* newModule;
|
|
DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
|
|
LOCALASSERT(dynPtr);
|
|
|
|
newModule = ModuleFromPosition(&(dynPtr->Position), (sbPtr->containingModule));
|
|
if(!(newModule))
|
|
{
|
|
/* attempt to relocate object */
|
|
textprint("Calling Near EmergencyRelocateObject, On object %x, type %d!\n",(int)sbPtr, sbPtr->I_SBtype);
|
|
IdentifyObject(sbPtr);
|
|
if(!(EmergencyRelocateObject(sbPtr))) {
|
|
textprint("Relocate failed!\n");
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
/* update object's module field */
|
|
sbPtr->containingModule = newModule;
|
|
|
|
/* now check the object's module */
|
|
if (sbPtr->I_SBtype == I_BehaviourPlacedLight)
|
|
{
|
|
if(!ThisObjectIsInAModuleVisibleFromCurrentlyVisibleModules(sbPtr))
|
|
{
|
|
MakeObjectFar(sbPtr);
|
|
}
|
|
}
|
|
else if(ModuleCurrVisArray[(sbPtr->containingModule->m_index)] == 0)
|
|
{
|
|
/* module is invisible, so make object invisible too */
|
|
switch(sbPtr->I_SBtype)
|
|
{
|
|
case(I_BehaviourAlien):
|
|
{
|
|
MakeAlienFar(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourRubberDuck):
|
|
case(I_BehaviourVideoScreen):
|
|
case(I_BehaviourInanimateObject):
|
|
{
|
|
MakeObjectFar(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourAutoGun):
|
|
{
|
|
MakeSentrygunFar(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourPlatform):
|
|
{
|
|
PLATFORMLIFT_BEHAVIOUR_BLOCK *platformliftdata = (PLATFORMLIFT_BEHAVIOUR_BLOCK *)sbPtr->SBdataptr;
|
|
//don't make platform lift far if it is currently moving
|
|
//(otherwise the lift won't be able to move)
|
|
if(platformliftdata->state!=PLBS_GoingUp &&
|
|
platformliftdata->state!=PLBS_GoingDown)
|
|
{
|
|
MakeObjectFar(sbPtr);
|
|
}
|
|
break;
|
|
}
|
|
case(I_BehaviourBinarySwitch):
|
|
{
|
|
MakeObjectFar(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourDatabase):
|
|
{
|
|
MakeObjectFar(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourLinkSwitch):
|
|
{
|
|
MakeObjectFar(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourPredator):
|
|
{
|
|
MakePredatorFar(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourXenoborg):
|
|
{
|
|
MakeXenoborgFar(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourQueenAlien):
|
|
{
|
|
MakeQueenFar(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourPredatorAlien):
|
|
{
|
|
//MakePAQFar(sbPtr);
|
|
GLOBALASSERT(0);
|
|
/* Should be BehaviourAlien! */
|
|
break;
|
|
}
|
|
case(I_BehaviourFaceHugger):
|
|
{
|
|
MakeFacehuggerFar(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourMarine):
|
|
{
|
|
MakeMarineFar(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourSeal):
|
|
{
|
|
MakeMarineFar(sbPtr); /* not yet supported */
|
|
break;
|
|
}
|
|
case(I_BehaviourNetGhost):
|
|
{
|
|
#if PSX
|
|
GLOBALASSERT(1==2);
|
|
#else
|
|
MakeGhostFar(sbPtr);
|
|
#endif
|
|
break;
|
|
}
|
|
case(I_BehaviourTrackObject):
|
|
{
|
|
MakeObjectFar(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourFan):
|
|
{
|
|
MakeObjectFar(sbPtr);
|
|
break;
|
|
}
|
|
case(I_BehaviourNetCorpse):
|
|
{
|
|
MakeCorpseFar(sbPtr);
|
|
break;
|
|
}
|
|
case (I_BehaviourPlacedHierarchy):
|
|
{
|
|
MakeObjectFar(sbPtr);
|
|
break;
|
|
}
|
|
case (I_BehaviourDummy):
|
|
{
|
|
MakeDummyFar(sbPtr);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
/* only the above object types should get here */
|
|
LOCALASSERT(1==0);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*-----------------------Patrick 15/1/97---------------------------
|
|
This pair of functions can be used to control the visibility of
|
|
most objects in the environment. Object specific stuff can be done
|
|
after the approriate call in doObjectVisibility, or a separate
|
|
function can be used (as for aliens)
|
|
-------------------------------------------------------------------*/
|
|
|
|
HMODELCONTROLLER DropShipHModelController;
|
|
|
|
void MakeObjectNear(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
MODULE tempModule;
|
|
DISPLAYBLOCK *dPtr;
|
|
DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
|
|
|
|
LOCALASSERT(dynPtr);
|
|
LOCALASSERT(sbPtr->SBdptr == NULL);
|
|
|
|
VisibilityDefaultObjectMap.MapShape = sbPtr->shapeIndex;
|
|
tempModule.m_mapptr = &VisibilityDefaultObjectMap;
|
|
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; /* this is important */
|
|
#if SupportWIndows95
|
|
tempModule.name = NULL; /* this is important */
|
|
#endif
|
|
AllocateModuleObject(&tempModule);
|
|
dPtr = tempModule.m_dptr;
|
|
if(dPtr==NULL) return; /* cannot create displayblock, so leave object "far" */
|
|
|
|
sbPtr->SBdptr = dPtr;
|
|
dPtr->ObStrategyBlock = sbPtr;
|
|
dPtr->ObMyModule = NULL;
|
|
|
|
/* also need to initialise positional information in the new display
|
|
block from the existing dynamics block: 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 time it is
|
|
drawn. */
|
|
dPtr->ObWorld = dynPtr->Position;
|
|
dPtr->ObEuler = dynPtr->OrientEuler;
|
|
dPtr->ObMat = dynPtr->OrientMat;
|
|
|
|
#if HMODEL_HACK
|
|
|
|
if (dPtr->ObShape==GetLoadedShapeMSL("computer")) {
|
|
|
|
extern SECTION Chest;
|
|
SECTION *Test_Section;
|
|
|
|
extern SECTION *GetHierarchyFromLibrary(const char *rif_name);
|
|
|
|
Test_Section=GetHierarchyFromLibrary("hnpcalien");
|
|
|
|
//Preprocess_HModel(Test_Section);
|
|
Create_HModel(&DropShipHModelController,Test_Section);
|
|
InitHModelSequence(&DropShipHModelController,3,3,ONE_FIXED<<1);
|
|
dPtr->HModelControlBlock=&DropShipHModelController;
|
|
|
|
|
|
//dPtr->HModelControlBlock->Playing=1;
|
|
dPtr->HModelControlBlock->Looped=1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
void MakeObjectFar(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
int i;
|
|
|
|
#if HMODEL_HACK
|
|
|
|
if (sbPtr->SBdptr->ObShape==GetLoadedShapeMSL("chest")) {
|
|
Dispel_HModel(&DropShipHModelController);
|
|
}
|
|
|
|
#endif
|
|
|
|
LOCALASSERT(sbPtr->SBdptr != NULL);
|
|
|
|
/* get rid of the displayblock */
|
|
i = DestroyActiveObject(sbPtr->SBdptr);
|
|
if(i!=0)
|
|
{
|
|
LOCALASSERT(1==0);
|
|
}
|
|
sbPtr->SBdptr = NULL;
|
|
|
|
}
|
|
|
|
|
|
/*----------------------Patrick 15/1/97-----------------------------
|
|
This function relocates an object back into the environment:
|
|
this may be an npc, inanimate object, etc.
|
|
It is called if the visibility system fails to find a
|
|
containing module for the object, which may happen for a number
|
|
of reasons, eg: an object is blown out of the visible part of the
|
|
environment, or an npc falls out...
|
|
NB returns 0 if relocation failed.
|
|
--------------------------------------------------------------------*/
|
|
static EmergRelocCalls = 0;
|
|
static int EmergencyRelocateObject(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
|
|
EmergRelocCalls++;
|
|
|
|
if(sbPtr->I_SBtype == I_BehaviourNetGhost) return 1;
|
|
|
|
/* KJL 14:48:36 09/02/98 - ignore platform lifts */
|
|
if (sbPtr->I_SBtype == I_BehaviourPlatform) return 1;
|
|
|
|
/* first, try to reset the object's position: if it has a valid 'containingModule',
|
|
this means that it's last position was in that module, so we can use that as a reset
|
|
position.
|
|
NB this means that NPC's will be unable to 'run away' into an invisible module.
|
|
*/
|
|
|
|
if(sbPtr->containingModule)
|
|
{
|
|
|
|
/* Nooo, this doesn't work! CDF 3/12/97 Hack. */
|
|
|
|
if ((sbPtr->I_SBtype!=I_BehaviourAlien)
|
|
&&(sbPtr->I_SBtype!=I_BehaviourMarine)
|
|
) {
|
|
textprint("Valid containingModule.\n");
|
|
sbPtr->DynPtr->Position = sbPtr->DynPtr->PrevPosition;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* so, we don't have a previous module... then search the environment for the
|
|
nearest invisible module that has entry point locations, and relocate to one of
|
|
these locations. */
|
|
{
|
|
extern SCENE Global_Scene;
|
|
extern SCENEMODULE **Global_ModulePtr;
|
|
|
|
AIMODULE *targetModule = 0;
|
|
int targetModuleDistance = 0;
|
|
//SCENEMODULE *ScenePtr;
|
|
AIMODULE *moduleListPointer;
|
|
int moduleCounter;
|
|
|
|
LOCALASSERT(ModuleArraySize);
|
|
LOCALASSERT(Global_ModulePtr);
|
|
LOCALASSERT(FALLP_EntryPoints); /* NB should never get here in a net game, so the codition should be true */
|
|
|
|
//ScenePtr = Global_ModulePtr[Global_Scene];
|
|
//moduleListPointer = ScenePtr->sm_marray;
|
|
{
|
|
extern AIMODULE *AIModuleArray;
|
|
moduleListPointer = AIModuleArray;
|
|
}
|
|
|
|
for(moduleCounter = 0; moduleCounter < AIModuleArraySize; moduleCounter++)
|
|
{
|
|
AIMODULE *thisModule = &(moduleListPointer[moduleCounter]);
|
|
//MODULE* thisModule = moduleListPointer[moduleCounter];
|
|
|
|
if( (!(ModuleCurrVisArray[thisModule->m_index]))&&
|
|
(FALLP_EntryPoints[thisModule->m_index].numEntryPoints != 0)
|
|
)
|
|
{
|
|
/* a candidate */
|
|
int thisModuleDistance =
|
|
VectorDistance(&(thisModule->m_world), &(sbPtr->DynPtr->Position));
|
|
|
|
if((!targetModule) || (thisModuleDistance < targetModuleDistance))
|
|
{
|
|
targetModule = thisModule;
|
|
targetModuleDistance = thisModuleDistance;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!targetModule)
|
|
{
|
|
/* this condition shouldn't happen, but since I'm paranoid... */
|
|
//This can happen on the skirmish levels (where the whole level is visible at once)
|
|
//Therefore if the object has a previous containing module , use it.
|
|
//Otherwise get rid of it.
|
|
|
|
//LOCALASSERT(1==0);
|
|
if(sbPtr->containingModule)
|
|
{
|
|
|
|
/* Nooo, this doesn't work! CDF 3/12/97 Hack. */
|
|
|
|
textprint("Valid containingModule.\n");
|
|
sbPtr->DynPtr->Position = sbPtr->DynPtr->PrevPosition;
|
|
return 1;
|
|
|
|
}
|
|
DestroyAnyStrategyBlock(sbPtr);
|
|
return 0;
|
|
}
|
|
|
|
EmergencyPlaceObjectInModule(sbPtr, targetModule);
|
|
|
|
#if debug
|
|
if (!sbPtr->containingModule)
|
|
{
|
|
textprint("WARNING!! EmgcyPlcObInMod failed\n");
|
|
}
|
|
#endif
|
|
|
|
|
|
return sbPtr->containingModule ? 1 : 0;
|
|
}
|
|
}
|
|
|
|
static void EmergencyPlaceObjectInModule(STRATEGYBLOCK *sbPtr, AIMODULE* targetModule)
|
|
{
|
|
int noOfEntryPoints;
|
|
FARENTRYPOINT *entryPointsList;
|
|
VECTORCH newPosition;
|
|
MODULE *renderModule;
|
|
|
|
textprint("Calling EmergencyPlaceObjectInModule!\n");
|
|
|
|
/* first off, assert a few pre-conditions */
|
|
GLOBALASSERT(AIModuleIsPhysical(targetModule));
|
|
GLOBALASSERT(sbPtr->maintainVisibility);
|
|
GLOBALASSERT(FALLP_EntryPoints);
|
|
|
|
noOfEntryPoints = FALLP_EntryPoints[(targetModule->m_index)].numEntryPoints;
|
|
entryPointsList = FALLP_EntryPoints[(targetModule->m_index)].entryPointsList;
|
|
|
|
GLOBALASSERT(noOfEntryPoints);
|
|
|
|
/* just use the first entry point */
|
|
newPosition = entryPointsList[0].position;
|
|
|
|
/* now set the object's new position and current module.
|
|
NB this is world position + a little extra in y to make sure */
|
|
{
|
|
DYNAMICSBLOCK *dynPtr;
|
|
|
|
dynPtr = sbPtr->DynPtr;
|
|
dynPtr->Position = newPosition;
|
|
|
|
dynPtr->Position.vx += targetModule->m_world.vx;
|
|
dynPtr->Position.vy += targetModule->m_world.vy;
|
|
dynPtr->Position.vz += targetModule->m_world.vz;
|
|
|
|
dynPtr->PrevPosition = dynPtr->Position;
|
|
|
|
}
|
|
/* finally, update the sb's module */
|
|
renderModule=ModuleFromPosition(&sbPtr->DynPtr->Position,NULL);
|
|
|
|
sbPtr->containingModule = renderModule;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*------------------------Patrick 14/1/97-----------------------------
|
|
This function returns the module in which a given world-space
|
|
position is located. A starting point for the search may also be
|
|
passed, so that the following search pattern is used:
|
|
1. is the location in the indicated starting module.
|
|
2. is the location in any of the starting module's visible-links.
|
|
3. finally, all modules in the environment are searched until
|
|
a containing module is found.
|
|
|
|
If no module is found, a null module pointer is returned.
|
|
NB only 'physical' modules are returned, ie infinite and
|
|
terminator modules are ignored.
|
|
|
|
The function is designed to be used for objects which move over small
|
|
distances, and for which a recently containing module is known (eg
|
|
avp aliens).... so that the first stage of the search will be successful
|
|
in the majority of cases, and the third stage rarely used.
|
|
Note that the fn returns the first module which contains the given
|
|
point, so problems may arise if the point exists in more than one
|
|
module. This should be ok for environments like avp, where v-linking
|
|
is not used.
|
|
--------------------------------------------------------------------*/
|
|
|
|
static int WorldPointIsInModule_WithTolerance(MODULE* thisModule, VECTORCH* thisPoint);
|
|
static MODULE* ModuleFromPosition_WithTolerance(VECTORCH *position, MODULE* startingModule);
|
|
|
|
MODULE* ModuleFromPosition(VECTORCH *position, MODULE* startingModule)
|
|
{
|
|
if((startingModule) && (ModuleIsPhysical(startingModule)))
|
|
{
|
|
/* first test for the most trivial, and most likely case */
|
|
if(WorldPointIsInModule(startingModule, position))
|
|
return startingModule;
|
|
|
|
/* now test visible-linked modules (If there are any) */
|
|
{
|
|
VMODULE *vlPtr = startingModule->m_vmptr;
|
|
if(vlPtr)
|
|
{
|
|
while(vlPtr->vmod_type != vmtype_term)
|
|
{
|
|
MODULE *mPtr = vlPtr->vmod_mref.mref_ptr;
|
|
if(mPtr)
|
|
{
|
|
if(ModuleIsPhysical(mPtr))
|
|
if(WorldPointIsInModule(mPtr, position))
|
|
return mPtr;
|
|
}
|
|
vlPtr++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* either there is no starting module; the starting module is not physical;
|
|
we are not in the starting module and it has no visibility-linked modules;
|
|
or we haven't found a module yet: so search the entire module list */
|
|
{
|
|
extern SCENE Global_Scene;
|
|
extern SCENEMODULE **Global_ModulePtr;
|
|
extern int ModuleArraySize;
|
|
MODULE **moduleListPointer;
|
|
int moduleCounter;
|
|
|
|
LOCALASSERT(ModuleArraySize);
|
|
LOCALASSERT(Global_ModulePtr);
|
|
|
|
moduleListPointer = (Global_ModulePtr[Global_Scene])->sm_marray;
|
|
moduleCounter = ModuleArraySize;
|
|
while(moduleCounter>0)
|
|
{
|
|
MODULE *thisModule;
|
|
|
|
thisModule = *moduleListPointer++;
|
|
if(ModuleIsPhysical(thisModule))
|
|
if(WorldPointIsInModule(thisModule, position))
|
|
return (thisModule);
|
|
|
|
moduleCounter--;
|
|
}
|
|
}
|
|
|
|
/* couldn't find a module */
|
|
/*Try with slightly larger module bounding boxes*/
|
|
return ModuleFromPosition_WithTolerance(position,startingModule);
|
|
}
|
|
|
|
|
|
/*-----------------------Patrick 14/1/97---------------------------
|
|
Returns 1 if the given WORLD point is in a given module,
|
|
or 0 otherwise.
|
|
|
|
NB the bizzare structure of this function produces optimum pentium
|
|
instructions... apparently.
|
|
-------------------------------------------------------------------*/
|
|
static int WorldPointIsInModule(MODULE* thisModule, VECTORCH* thisPoint)
|
|
{
|
|
VECTORCH localpoint = *thisPoint;
|
|
localpoint.vx -= thisModule->m_world.vx;
|
|
localpoint.vy -= thisModule->m_world.vy;
|
|
localpoint.vz -= thisModule->m_world.vz;
|
|
|
|
if(localpoint.vx <= thisModule->m_maxx)
|
|
if(localpoint.vx >= thisModule->m_minx)
|
|
if(localpoint.vy <= thisModule->m_maxy)
|
|
if(localpoint.vy >= thisModule->m_miny)
|
|
if(localpoint.vz <= thisModule->m_maxz)
|
|
if(localpoint.vz >= thisModule->m_minz)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
#define ModuleFromPositionTolerance 50
|
|
static MODULE* ModuleFromPosition_WithTolerance(VECTORCH *position, MODULE* startingModule)
|
|
{
|
|
if((startingModule) && (ModuleIsPhysical(startingModule)))
|
|
{
|
|
/* first test for the most trivial, and most likely case */
|
|
if(WorldPointIsInModule_WithTolerance(startingModule, position))
|
|
return startingModule;
|
|
|
|
/* now test visible-linked modules (If there are any) */
|
|
{
|
|
VMODULE *vlPtr = startingModule->m_vmptr;
|
|
if(vlPtr)
|
|
{
|
|
while(vlPtr->vmod_type != vmtype_term)
|
|
{
|
|
MODULE *mPtr = vlPtr->vmod_mref.mref_ptr;
|
|
if(mPtr)
|
|
{
|
|
if(ModuleIsPhysical(mPtr))
|
|
if(WorldPointIsInModule_WithTolerance(mPtr, position))
|
|
return mPtr;
|
|
}
|
|
vlPtr++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* either there is no starting module; the starting module is not physical;
|
|
we are not in the starting module and it has no visibility-linked modules;
|
|
or we haven't found a module yet: so search the entire module list */
|
|
{
|
|
extern SCENE Global_Scene;
|
|
extern SCENEMODULE **Global_ModulePtr;
|
|
extern int ModuleArraySize;
|
|
MODULE **moduleListPointer;
|
|
int moduleCounter;
|
|
|
|
LOCALASSERT(ModuleArraySize);
|
|
LOCALASSERT(Global_ModulePtr);
|
|
|
|
moduleListPointer = (Global_ModulePtr[Global_Scene])->sm_marray;
|
|
moduleCounter = ModuleArraySize;
|
|
while(moduleCounter>0)
|
|
{
|
|
MODULE *thisModule;
|
|
|
|
thisModule = *moduleListPointer++;
|
|
if(ModuleIsPhysical(thisModule))
|
|
if(WorldPointIsInModule_WithTolerance(thisModule, position))
|
|
return (thisModule);
|
|
|
|
moduleCounter--;
|
|
}
|
|
}
|
|
|
|
/* couldn't find a module */
|
|
return (MODULE *)0;
|
|
}
|
|
|
|
|
|
static int WorldPointIsInModule_WithTolerance(MODULE* thisModule, VECTORCH* thisPoint)
|
|
{
|
|
VECTORCH localpoint = *thisPoint;
|
|
localpoint.vx -= thisModule->m_world.vx;
|
|
localpoint.vy -= thisModule->m_world.vy;
|
|
localpoint.vz -= thisModule->m_world.vz;
|
|
|
|
if(localpoint.vx <= thisModule->m_maxx + ModuleFromPositionTolerance)
|
|
if(localpoint.vx >= thisModule->m_minx - ModuleFromPositionTolerance)
|
|
if(localpoint.vy <= thisModule->m_maxy + ModuleFromPositionTolerance)
|
|
if(localpoint.vy >= thisModule->m_miny - ModuleFromPositionTolerance)
|
|
if(localpoint.vz <= thisModule->m_maxz + ModuleFromPositionTolerance)
|
|
if(localpoint.vz >= thisModule->m_minz - ModuleFromPositionTolerance)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/*---------------------Patrick 14/1/97-----------------------------
|
|
|
|
SUPPORT FUNCTIONS FOR INANIMATE OBJECTS
|
|
|
|
-----------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------Patrick 16/1/97---------------------------
|
|
Inanimate object initialisation and behaviour and functions.
|
|
-------------------------------------------------------------------*/
|
|
void InitInanimateObject(void* bhdata, STRATEGYBLOCK *sbPtr)
|
|
{
|
|
TOOLS_DATA_INANIMATEOBJECT *toolsData = (TOOLS_DATA_INANIMATEOBJECT *)bhdata;
|
|
INANIMATEOBJECT_STATUSBLOCK* objectstatusptr;
|
|
enum DYNAMICS_TEMPLATE_ID inanimateDynamicsInitialiser;
|
|
int i;
|
|
|
|
LOCALASSERT(sbPtr->I_SBtype == I_BehaviourInanimateObject);
|
|
LOCALASSERT(toolsData);
|
|
|
|
/* create, initialise and attach a data block */
|
|
objectstatusptr = (void *)AllocateMem(sizeof(INANIMATEOBJECT_STATUSBLOCK));
|
|
if(!objectstatusptr)
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
}
|
|
|
|
sbPtr->SBdataptr = objectstatusptr;
|
|
objectstatusptr->respawnTimer = 0;
|
|
objectstatusptr->lifespanTimer = 0;
|
|
objectstatusptr->explosionTimer = 0;
|
|
|
|
/* these should be loaded */
|
|
objectstatusptr->typeId = toolsData->typeId;
|
|
objectstatusptr->subType = toolsData->subType;
|
|
|
|
/* set default indestructibility */
|
|
objectstatusptr->Indestructable = No;
|
|
objectstatusptr->ghosted_object=0;
|
|
|
|
/* set the default inanimate object dynamics template: Inanimate for single player,
|
|
and Static for multiplayer
|
|
NB some objects are always static, and initialised using
|
|
the static dynamics template directly
|
|
NB2 PSX: all objects are static */
|
|
#if SupportWindows95
|
|
// if(AvP.Network==I_No_Network) inanimateDynamicsInitialiser = DYNAMICS_TEMPLATE_INANIMATE;
|
|
// else inanimateDynamicsInitialiser = DYNAMICS_TEMPLATE_STATIC;
|
|
inanimateDynamicsInitialiser = DYNAMICS_TEMPLATE_INANIMATE;
|
|
#else
|
|
inanimateDynamicsInitialiser = DYNAMICS_TEMPLATE_STATIC;
|
|
#endif
|
|
|
|
/* Initialise object's stats */
|
|
{
|
|
NPC_DATA *NpcData;
|
|
|
|
//#warning Change Me!!!
|
|
|
|
NpcData=GetThisNpcData(I_NPC_DefaultInanimate);
|
|
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;
|
|
}
|
|
|
|
sbPtr->SBDamageBlock.Health*=toolsData->integrity;
|
|
|
|
if(toolsData->triggering_event)
|
|
{
|
|
objectstatusptr->event_target=(OBJECT_EVENT_TARGET*)AllocateMem(sizeof(OBJECT_EVENT_TARGET));
|
|
|
|
objectstatusptr->event_target->triggering_event=toolsData->triggering_event;
|
|
objectstatusptr->event_target->request=toolsData->event_request;
|
|
for(i=0;i<SB_NAME_LENGTH;i++) objectstatusptr->event_target->event_target_ID[i] = toolsData->event_target_ID[i];
|
|
objectstatusptr->event_target->event_target_sbptr=0;
|
|
}
|
|
else
|
|
{
|
|
objectstatusptr->event_target=0;
|
|
}
|
|
|
|
objectstatusptr->explosionType=toolsData->explosionType;
|
|
|
|
/* set inanimate-object-type-specific stuff...
|
|
1. dynamics block allocation
|
|
2. integrity */
|
|
switch(objectstatusptr->typeId)
|
|
{
|
|
case IOT_HitMeAndIllDestroyBase:
|
|
case IOT_Static:
|
|
{
|
|
sbPtr->DynPtr = AllocateDynamicsBlock(DYNAMICS_TEMPLATE_STATIC);
|
|
if(!sbPtr->DynPtr)
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
sbPtr->DynPtr->Mass = toolsData->mass;
|
|
if (toolsData->integrity > 20)
|
|
{
|
|
objectstatusptr->Indestructable = Yes;
|
|
sbPtr->integrity = DEFAULT_OBJECT_INTEGRITY;
|
|
}
|
|
else if (toolsData->integrity < 1)
|
|
{
|
|
sbPtr->integrity = 1; // die immediately
|
|
}
|
|
else
|
|
{
|
|
sbPtr->integrity = (DEFAULT_OBJECT_INTEGRITY)*(toolsData->integrity);
|
|
}
|
|
break;
|
|
}
|
|
case IOT_Furniture:
|
|
{
|
|
sbPtr->DynPtr = AllocateDynamicsBlock(inanimateDynamicsInitialiser);
|
|
if(!sbPtr->DynPtr)
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
sbPtr->DynPtr->Mass = toolsData->mass;
|
|
if (toolsData->integrity > 20)
|
|
{
|
|
objectstatusptr->Indestructable = Yes;
|
|
sbPtr->integrity = DEFAULT_OBJECT_INTEGRITY;
|
|
}
|
|
else if (toolsData->integrity < 1)
|
|
{
|
|
sbPtr->integrity = 1; // die immediately
|
|
}
|
|
else
|
|
{
|
|
sbPtr->integrity = (DEFAULT_OBJECT_INTEGRITY)*(toolsData->integrity);
|
|
}
|
|
break;
|
|
}
|
|
case IOT_Weapon:
|
|
{
|
|
sbPtr->DynPtr = AllocateDynamicsBlock(DYNAMICS_TEMPLATE_PICKUPOBJECT);
|
|
if(!sbPtr->DynPtr)
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
sbPtr->integrity = DEFAULT_OBJECT_INTEGRITY;
|
|
if(objectstatusptr->subType==WEAPON_FLAMETHROWER)
|
|
{
|
|
//flamethrowers explode using a molotov explosion
|
|
objectstatusptr->explosionType=3;
|
|
}
|
|
break;
|
|
}
|
|
case IOT_Ammo:
|
|
{
|
|
sbPtr->DynPtr = AllocateDynamicsBlock(DYNAMICS_TEMPLATE_PICKUPOBJECT);
|
|
if(!sbPtr->DynPtr)
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
sbPtr->integrity = DEFAULT_OBJECT_INTEGRITY;
|
|
|
|
if(objectstatusptr->subType==AMMO_FLAMETHROWER)
|
|
{
|
|
//flamethrowers explode using a molotov explosion
|
|
objectstatusptr->explosionType=3;
|
|
}
|
|
break;
|
|
}
|
|
case IOT_Health:
|
|
{
|
|
sbPtr->DynPtr = AllocateDynamicsBlock(DYNAMICS_TEMPLATE_PICKUPOBJECT);
|
|
|
|
if(!sbPtr->DynPtr)
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
|
|
sbPtr->integrity = DEFAULT_OBJECT_INTEGRITY;
|
|
break;
|
|
}
|
|
case IOT_Armour:
|
|
{
|
|
sbPtr->DynPtr = AllocateDynamicsBlock(DYNAMICS_TEMPLATE_PICKUPOBJECT);
|
|
if(!sbPtr->DynPtr)
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
|
|
sbPtr->integrity = DEFAULT_OBJECT_INTEGRITY;
|
|
break;
|
|
}
|
|
case IOT_Key:
|
|
{
|
|
sbPtr->DynPtr = AllocateDynamicsBlock(DYNAMICS_TEMPLATE_PICKUPOBJECT);
|
|
if(!sbPtr->DynPtr)
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
|
|
sbPtr->integrity = DEFAULT_OBJECT_INTEGRITY;
|
|
break;
|
|
}
|
|
case IOT_BoxedSentryGun:
|
|
{
|
|
sbPtr->DynPtr = AllocateDynamicsBlock(inanimateDynamicsInitialiser);
|
|
if(!sbPtr->DynPtr)
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
|
|
sbPtr->integrity = DEFAULT_OBJECT_INTEGRITY;
|
|
break;
|
|
}
|
|
case IOT_IRGoggles:
|
|
{
|
|
sbPtr->DynPtr = AllocateDynamicsBlock(inanimateDynamicsInitialiser);
|
|
if(!sbPtr->DynPtr)
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
sbPtr->integrity = DEFAULT_OBJECT_INTEGRITY;
|
|
break;
|
|
}
|
|
case IOT_DataTape:
|
|
{
|
|
sbPtr->DynPtr = AllocateDynamicsBlock(DYNAMICS_TEMPLATE_STATIC);
|
|
if(!sbPtr->DynPtr)
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
sbPtr->integrity = DEFAULT_OBJECT_INTEGRITY;
|
|
break;
|
|
}
|
|
case IOT_MTrackerUpgrade:
|
|
{
|
|
sbPtr->DynPtr = AllocateDynamicsBlock(DYNAMICS_TEMPLATE_PICKUPOBJECT);
|
|
if(!sbPtr->DynPtr)
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
sbPtr->integrity = DEFAULT_OBJECT_INTEGRITY;
|
|
break;
|
|
}
|
|
case IOT_PheromonePod:
|
|
{
|
|
sbPtr->DynPtr = AllocateDynamicsBlock(DYNAMICS_TEMPLATE_PICKUPOBJECT);
|
|
if(!sbPtr->DynPtr)
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
sbPtr->integrity = DEFAULT_OBJECT_INTEGRITY;
|
|
break;
|
|
}
|
|
case IOT_SpecialPickupObject:
|
|
{
|
|
sbPtr->DynPtr = AllocateDynamicsBlock(DYNAMICS_TEMPLATE_PICKUPOBJECT);
|
|
if(!sbPtr->DynPtr)
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
if (toolsData->integrity > 20)
|
|
{
|
|
objectstatusptr->Indestructable = Yes;
|
|
sbPtr->integrity = DEFAULT_OBJECT_INTEGRITY;
|
|
}
|
|
else if (toolsData->integrity < 1)
|
|
{
|
|
sbPtr->integrity = 1; // die immediately
|
|
}
|
|
else
|
|
{
|
|
sbPtr->integrity = (DEFAULT_OBJECT_INTEGRITY)*(toolsData->integrity);
|
|
}
|
|
break;
|
|
}
|
|
case IOT_FieldCharge:
|
|
{
|
|
sbPtr->DynPtr = AllocateDynamicsBlock(DYNAMICS_TEMPLATE_PICKUPOBJECT);
|
|
if(!sbPtr->DynPtr)
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
|
|
sbPtr->integrity = DEFAULT_OBJECT_INTEGRITY;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
LOCALASSERT(1==0);
|
|
break;
|
|
}
|
|
}
|
|
/*check to see if object is animated.*/
|
|
{
|
|
TXACTRLBLK **pptxactrlblk;
|
|
int item_num;
|
|
int shape_num = toolsData->shapeIndex;
|
|
SHAPEHEADER *shptr = GetShapeData(shape_num);
|
|
pptxactrlblk = &objectstatusptr->inan_tac;
|
|
for(item_num = 0; item_num < shptr->numitems; item_num ++)
|
|
{
|
|
POLYHEADER *poly = (POLYHEADER*)(shptr->items[item_num]);
|
|
LOCALASSERT(poly);
|
|
|
|
SetupPolygonFlagAccessForShape(shptr);
|
|
|
|
if((Request_PolyFlags((void *)poly)) & iflag_txanim)
|
|
{
|
|
TXACTRLBLK *pnew_txactrlblk;
|
|
|
|
pnew_txactrlblk = AllocateMem(sizeof(TXACTRLBLK));
|
|
if(pnew_txactrlblk)
|
|
{
|
|
pnew_txactrlblk->tac_flags = 0;
|
|
pnew_txactrlblk->tac_item = item_num;
|
|
pnew_txactrlblk->tac_sequence = 0;
|
|
pnew_txactrlblk->tac_node = 0;
|
|
pnew_txactrlblk->tac_txarray = GetTxAnimArrayZ(shape_num, item_num);
|
|
pnew_txactrlblk->tac_txah_s = GetTxAnimHeaderFromShape(pnew_txactrlblk, shape_num);
|
|
|
|
*pptxactrlblk = pnew_txactrlblk;
|
|
pptxactrlblk = &pnew_txactrlblk->tac_next;
|
|
}
|
|
else *pptxactrlblk = NULL;
|
|
}
|
|
}
|
|
*pptxactrlblk=0;
|
|
|
|
}
|
|
|
|
/* Initialise the dynamics block */
|
|
{
|
|
DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
|
|
GLOBALASSERT(dynPtr);
|
|
|
|
dynPtr->PrevPosition = dynPtr->Position = toolsData->position;
|
|
dynPtr->OrientEuler = toolsData->orientation;
|
|
CreateEulerMatrix(&dynPtr->OrientEuler, &dynPtr->OrientMat);
|
|
TransposeMatrixCH(&dynPtr->OrientMat);
|
|
}
|
|
|
|
/* strategy block initialisation */
|
|
sbPtr->shapeIndex = toolsData->shapeIndex;
|
|
for(i=0;i<SB_NAME_LENGTH;i++) sbPtr->SBname[i] = toolsData->nameID[i];
|
|
|
|
/* these must be initialised for respawning objects in multiplayer game */
|
|
objectstatusptr->startingHealth = sbPtr->SBDamageBlock.Health;
|
|
objectstatusptr->startingArmour = sbPtr->SBDamageBlock.Armour;
|
|
|
|
if(AvP.Network != I_No_Network)
|
|
{
|
|
//if in a network game , see if this is an allowable weapon
|
|
if(objectstatusptr->typeId==IOT_Weapon)
|
|
{
|
|
if((!netGameData.allowSmartgun && objectstatusptr->subType==WEAPON_SMARTGUN)||
|
|
(!netGameData.allowFlamer && objectstatusptr->subType==WEAPON_FLAMETHROWER)||
|
|
(!netGameData.allowSadar && objectstatusptr->subType==WEAPON_SADAR)||
|
|
(!netGameData.allowGrenadeLauncher && objectstatusptr->subType==WEAPON_GRENADELAUNCHER)||
|
|
(!netGameData.allowPistols && objectstatusptr->subType==WEAPON_MARINE_PISTOL)||
|
|
(!netGameData.allowSmartDisc && objectstatusptr->subType==WEAPON_FRISBEE_LAUNCHER)||
|
|
(!netGameData.allowMinigun && objectstatusptr->subType==WEAPON_MINIGUN))
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
}
|
|
if(objectstatusptr->typeId==IOT_Ammo)
|
|
{
|
|
if((!netGameData.allowSmartgun && objectstatusptr->subType==AMMO_SMARTGUN)||
|
|
(!netGameData.allowFlamer && objectstatusptr->subType==AMMO_FLAMETHROWER)||
|
|
(!netGameData.allowSadar && objectstatusptr->subType==AMMO_SADAR_TOW)||
|
|
(!netGameData.allowGrenadeLauncher && objectstatusptr->subType==AMMO_GRENADE)||
|
|
(!netGameData.allowPistols && objectstatusptr->subType==AMMO_MARINE_PISTOL_PC)||
|
|
(!netGameData.allowSmartDisc && objectstatusptr->subType==AMMO_FRISBEE)||
|
|
(!netGameData.allowMinigun && objectstatusptr->subType==AMMO_MINIGUN))
|
|
{
|
|
RemoveBehaviourStrategy(sbPtr);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void InanimateObjectBehaviour(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
/* handle respawn timer here */
|
|
INANIMATEOBJECT_STATUSBLOCK* objectstatusptr = sbPtr->SBdataptr;
|
|
LOCALASSERT(objectstatusptr);
|
|
|
|
LOCALASSERT(!(objectstatusptr->respawnTimer<0)); /* this should never happen */
|
|
|
|
/* Lock disc pickups in place. */
|
|
if ((objectstatusptr->typeId == IOT_Ammo)
|
|
&&(objectstatusptr->subType == (int)AMMO_PRED_DISC)) {
|
|
|
|
sbPtr->DynPtr->LinVelocity.vx=0;
|
|
sbPtr->DynPtr->LinVelocity.vy=0;
|
|
sbPtr->DynPtr->LinVelocity.vz=0;
|
|
|
|
sbPtr->DynPtr->LinImpulse.vx=0;
|
|
sbPtr->DynPtr->LinImpulse.vy=0;
|
|
sbPtr->DynPtr->LinImpulse.vz=0;
|
|
}
|
|
|
|
if(objectstatusptr->inan_tac)
|
|
{
|
|
DISPLAYBLOCK* dptr = sbPtr->SBdptr;
|
|
|
|
/*deal with texture animation*/
|
|
if(dptr)
|
|
{
|
|
if(!dptr->ObTxAnimCtrlBlks)
|
|
{
|
|
dptr->ObTxAnimCtrlBlks = objectstatusptr->inan_tac;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if(objectstatusptr->explosionTimer)
|
|
{
|
|
//the object is about to explode
|
|
if(objectstatusptr->explosionStartFrame==GlobalFrameCounter)
|
|
{
|
|
//the explosion was triggered earlier this frame
|
|
//therefore don't bother altering the timer until next frame
|
|
return;
|
|
}
|
|
|
|
objectstatusptr->explosionTimer-=NormalFrameTime;
|
|
if(objectstatusptr->explosionTimer<=0)
|
|
{
|
|
objectstatusptr->explosionTimer=-1;
|
|
InanimateObjectIsDamaged(sbPtr,0,0);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
if(AvP.Network!=I_No_Network)
|
|
{
|
|
//see if dropped weapons have timed out
|
|
if(objectstatusptr->lifespanTimer>0)
|
|
{
|
|
objectstatusptr->lifespanTimer-=NormalFrameTime;
|
|
if(objectstatusptr->lifespanTimer<=0)
|
|
{
|
|
FragmentInanimateObject(sbPtr);
|
|
DestroyAnyStrategyBlock(sbPtr);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* do nothing if the timer is zero */
|
|
if(objectstatusptr->respawnTimer==0 || objectstatusptr->respawnTimer==OBJECT_RESPAWN_NO_RESPAWN) return;
|
|
|
|
/* textprint("RESPAWN TIMER %d \n", objectstatusptr->respawnTimer); */
|
|
/* If we get here, the object is in a respawn state:
|
|
this should only happen in a net-game */
|
|
LOCALASSERT(AvP.Network!=I_No_Network);
|
|
LOCALASSERT(!sbPtr->SBdptr);
|
|
LOCALASSERT(!sbPtr->maintainVisibility);
|
|
|
|
objectstatusptr->respawnTimer -= NormalFrameTime;
|
|
if(objectstatusptr->respawnTimer<=0)
|
|
{
|
|
/* time to respawn, then */
|
|
RespawnInanimateObject(sbPtr);
|
|
objectstatusptr->respawnTimer=0;
|
|
}
|
|
}
|
|
|
|
/* this global flag is used to distinguish between messages from the host,
|
|
and locally caused damage */
|
|
int InanimateDamageFromNetHost = 0;
|
|
|
|
|
|
void InanimateObjectIsDamaged(STRATEGYBLOCK *sbPtr, DAMAGE_PROFILE *damage, int multiple)
|
|
{
|
|
INANIMATEOBJECT_STATUSBLOCK* objectstatusptr = sbPtr->SBdataptr;
|
|
LOCALASSERT(objectstatusptr);
|
|
|
|
#if SupportWindows95
|
|
if((AvP.Network==I_Peer)&&(!InanimateDamageFromNetHost))
|
|
{
|
|
/* this means that the damage was generated locally in a net-game:
|
|
in this case, just send a damage message to the host */
|
|
AddNetMsg_InanimateObjectDamaged(sbPtr,damage,multiple);
|
|
return;
|
|
}
|
|
else if(AvP.Network==I_Host)
|
|
{
|
|
/* if we're the host, inform everyone that the object is dead */
|
|
if(sbPtr->SBDamageBlock.Health <= 0) AddNetMsg_InanimateObjectDestroyed(sbPtr);
|
|
}
|
|
#endif
|
|
|
|
if(sbPtr->SBflags.please_destroy_me)
|
|
{
|
|
//object has already been destroyed , so ignore any further damage
|
|
return;
|
|
}
|
|
|
|
//if object has been destroyed , see if it has a target that it needs to notify
|
|
if(objectstatusptr->event_target)
|
|
{
|
|
if(sbPtr->SBDamageBlock.Health <= 0 && !objectstatusptr->Indestructable)
|
|
{
|
|
if(objectstatusptr->event_target->triggering_event & ObjectEventFlag_Destroyed)
|
|
{
|
|
RequestState(objectstatusptr->event_target->event_target_sbptr,objectstatusptr->event_target->request,0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!objectstatusptr->Indestructable && objectstatusptr->explosionType)
|
|
{
|
|
|
|
|
|
if(InanimateDamageFromNetHost)
|
|
{
|
|
//do explosion effect , without the damage
|
|
switch(objectstatusptr->explosionType)
|
|
{
|
|
case 1:
|
|
{
|
|
//small explosion
|
|
MakeVolumetricExplosionAt(&(sbPtr->DynPtr->Position),EXPLOSION_SMALL_NOCOLLISIONS);
|
|
|
|
Explosion_SoundData.position=sbPtr->DynPtr->Position;
|
|
|
|
Sound_Play(SID_NADEEXPLODE,"n",&Explosion_SoundData);
|
|
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
//big explosion
|
|
MakeVolumetricExplosionAt(&(sbPtr->DynPtr->Position),EXPLOSION_HUGE_NOCOLLISIONS);
|
|
|
|
Explosion_SoundData.position=sbPtr->DynPtr->Position;
|
|
|
|
Sound_Play(SID_NICE_EXPLOSION,"n",&Explosion_SoundData);
|
|
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
//molotov explosion
|
|
MakeVolumetricExplosionAt(&(sbPtr->DynPtr->Position),EXPLOSION_MOLOTOV);
|
|
|
|
Explosion_SoundData.position=sbPtr->DynPtr->Position;
|
|
|
|
Sound_Play(SID_NADEEXPLODE,"n",&Explosion_SoundData);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(objectstatusptr->explosionTimer==0)
|
|
{
|
|
if(sbPtr->SBDamageBlock.Health <= 0)
|
|
{
|
|
//start explosion timer
|
|
//this gives a slight time delay for object destruction, to allow for chain reactions
|
|
//(rather than all explosive objects going up in one frame)
|
|
objectstatusptr->explosionTimer=ONE_FIXED/10;
|
|
objectstatusptr->explosionStartFrame=GlobalFrameCounter;
|
|
return;
|
|
}
|
|
}
|
|
else if(objectstatusptr->explosionTimer<0)
|
|
{
|
|
//time for the explosion
|
|
objectstatusptr->explosionTimer=0;
|
|
switch(objectstatusptr->explosionType)
|
|
{
|
|
case 1:
|
|
{
|
|
//small explosion
|
|
HandleEffectsOfExplosion
|
|
(
|
|
sbPtr,
|
|
&(sbPtr->DynPtr->Position),
|
|
5000,
|
|
&SmallExplosionDamage,
|
|
0
|
|
);
|
|
Explosion_SoundData.position=sbPtr->DynPtr->Position;
|
|
|
|
Sound_Play(SID_NADEEXPLODE,"n",&Explosion_SoundData);
|
|
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
//big explosion
|
|
HandleEffectsOfExplosion
|
|
(
|
|
sbPtr,
|
|
&(sbPtr->DynPtr->Position),
|
|
10000,
|
|
&BigExplosionDamage,
|
|
0
|
|
);
|
|
Explosion_SoundData.position=sbPtr->DynPtr->Position;
|
|
|
|
Sound_Play(SID_NICE_EXPLOSION,"n",&Explosion_SoundData);
|
|
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
//molotov explosion
|
|
HandleEffectsOfExplosion
|
|
(
|
|
sbPtr,
|
|
&(sbPtr->DynPtr->Position),
|
|
TemplateAmmo[AMMO_MOLOTOV].MaxRange,
|
|
&TemplateAmmo[AMMO_MOLOTOV].MaxDamage[AvP.Difficulty],
|
|
TemplateAmmo[AMMO_MOLOTOV].ExplosionIsFlat
|
|
);
|
|
Explosion_SoundData.position=sbPtr->DynPtr->Position;
|
|
|
|
Sound_Play(SID_NADEEXPLODE,"n",&Explosion_SoundData);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//set health to zero (in case object has recovered in the delay)
|
|
sbPtr->SBDamageBlock.Health=0;
|
|
}
|
|
else
|
|
{
|
|
//currently in explosion count down , so wait
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
switch(objectstatusptr->typeId)
|
|
{
|
|
case IOT_Static:
|
|
{
|
|
if (!objectstatusptr->Indestructable)
|
|
{
|
|
if(sbPtr->SBDamageBlock.Health <= 0)
|
|
{
|
|
FragmentInanimateObject(sbPtr);
|
|
if(AvP.Network==I_No_Network) DestroyAnyStrategyBlock(sbPtr);
|
|
else KillFragmentalObjectForRespawn(sbPtr);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case IOT_Furniture:
|
|
{
|
|
if (!objectstatusptr->Indestructable)
|
|
{
|
|
if(sbPtr->SBDamageBlock.Health <= 0)
|
|
{
|
|
FragmentInanimateObject(sbPtr);
|
|
DestroyAnyStrategyBlock(sbPtr);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case IOT_Weapon:
|
|
{
|
|
if(sbPtr->SBDamageBlock.Health <= 0)
|
|
{
|
|
FragmentInanimateObject(sbPtr);
|
|
if(AvP.Network==I_No_Network) DestroyAnyStrategyBlock(sbPtr);
|
|
else KillInanimateObjectForRespawn(sbPtr);
|
|
}
|
|
break;
|
|
}
|
|
case IOT_Ammo:
|
|
{
|
|
if(sbPtr->SBDamageBlock.Health <= 0)
|
|
{
|
|
FragmentInanimateObject(sbPtr);
|
|
if(AvP.Network==I_No_Network) DestroyAnyStrategyBlock(sbPtr);
|
|
else KillInanimateObjectForRespawn(sbPtr);
|
|
}
|
|
break;
|
|
}
|
|
case IOT_Health:
|
|
{
|
|
if(sbPtr->SBDamageBlock.Health <= 0)
|
|
{
|
|
FragmentInanimateObject(sbPtr);
|
|
if(AvP.Network==I_No_Network) DestroyAnyStrategyBlock(sbPtr);
|
|
else KillInanimateObjectForRespawn(sbPtr);
|
|
}
|
|
break;
|
|
}
|
|
case IOT_Armour:
|
|
{
|
|
if(sbPtr->SBDamageBlock.Health <= 0)
|
|
{
|
|
FragmentInanimateObject(sbPtr);
|
|
if(AvP.Network==I_No_Network) DestroyAnyStrategyBlock(sbPtr);
|
|
else KillInanimateObjectForRespawn(sbPtr);
|
|
}
|
|
break;
|
|
}
|
|
case IOT_Key:
|
|
{
|
|
/* do nothing */
|
|
break;
|
|
}
|
|
case IOT_BoxedSentryGun:
|
|
{
|
|
if(sbPtr->SBDamageBlock.Health <= 0)
|
|
{
|
|
ExplodeInanimateObject(sbPtr);
|
|
if(AvP.Network==I_No_Network) DestroyAnyStrategyBlock(sbPtr);
|
|
else KillInanimateObjectForRespawn(sbPtr);
|
|
}
|
|
break;
|
|
}
|
|
case IOT_IRGoggles:
|
|
{
|
|
if(sbPtr->SBDamageBlock.Health <= 0)
|
|
{
|
|
ExplodeInanimateObject(sbPtr);
|
|
if(AvP.Network==I_No_Network) DestroyAnyStrategyBlock(sbPtr);
|
|
else KillInanimateObjectForRespawn(sbPtr);
|
|
}
|
|
break;
|
|
}
|
|
case IOT_DataTape:
|
|
{
|
|
/* do nothing */
|
|
break;
|
|
}
|
|
case IOT_MTrackerUpgrade:
|
|
{
|
|
if(sbPtr->SBDamageBlock.Health <= 0)
|
|
{
|
|
ExplodeInanimateObject(sbPtr);
|
|
if(AvP.Network==I_No_Network) DestroyAnyStrategyBlock(sbPtr);
|
|
else KillInanimateObjectForRespawn(sbPtr);
|
|
}
|
|
break;
|
|
}
|
|
case IOT_PheromonePod:
|
|
{
|
|
if(sbPtr->SBDamageBlock.Health <= 0)
|
|
{
|
|
ExplodeInanimateObject(sbPtr);
|
|
if(AvP.Network==I_No_Network) DestroyAnyStrategyBlock(sbPtr);
|
|
else KillInanimateObjectForRespawn(sbPtr);
|
|
}
|
|
break;
|
|
}
|
|
case IOT_SpecialPickupObject:
|
|
{
|
|
if (!objectstatusptr->Indestructable)
|
|
{
|
|
if(sbPtr->SBDamageBlock.Health <= 0)
|
|
{
|
|
FragmentInanimateObject(sbPtr);
|
|
if(AvP.Network==I_No_Network) DestroyAnyStrategyBlock(sbPtr);
|
|
else KillInanimateObjectForRespawn(sbPtr);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case IOT_HitMeAndIllDestroyBase:
|
|
{
|
|
if (damage->Penetrative>0) {
|
|
/* Destroy base. */
|
|
if (AvP.DestructTimer==-1) {
|
|
textprint("Boom! You've blown up the base!\n");
|
|
ActivateSelfDestructSequence(60);
|
|
/* Dummy switch hack. */
|
|
{
|
|
STRATEGYBLOCK *evil_switch_SBPtr;
|
|
char Evil_Switch_SBname[] = {0x46,0xb9,0x01,0xf0,0x63,0xe4,0x56,0x0,};
|
|
|
|
evil_switch_SBPtr=FindSBWithName(Evil_Switch_SBname);
|
|
|
|
GLOBALASSERT(evil_switch_SBPtr);
|
|
|
|
RequestState(evil_switch_SBPtr,1,Player->ObStrategyBlock);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case IOT_FieldCharge:
|
|
{
|
|
if(sbPtr->SBDamageBlock.Health <= 0)
|
|
{
|
|
FragmentInanimateObject(sbPtr);
|
|
if(AvP.Network==I_No_Network) DestroyAnyStrategyBlock(sbPtr);
|
|
else KillInanimateObjectForRespawn(sbPtr);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
LOCALASSERT(1==0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define ObjectRequest_AdjustIntegrity 0x00000001
|
|
void SendRequestToInanimateObject(STRATEGYBLOCK* sbptr,BOOL state,int extended_data)
|
|
{
|
|
INANIMATEOBJECT_STATUSBLOCK* objectstatusptr = sbptr->SBdataptr;
|
|
GLOBALASSERT(objectstatusptr);
|
|
GLOBALASSERT((sbptr->I_SBtype == I_BehaviourInanimateObject));
|
|
|
|
|
|
if(state)
|
|
{
|
|
if(extended_data & ObjectRequest_AdjustIntegrity)
|
|
{
|
|
int new_integrity=(extended_data>>7)&0xff;
|
|
sbptr->SBDamageBlock.Health=(10<<ONE_FIXED_SHIFT)*new_integrity;
|
|
sbptr->integrity = DEFAULT_OBJECT_INTEGRITY*new_integrity;
|
|
|
|
if(new_integrity>20)
|
|
objectstatusptr->Indestructable = Yes;
|
|
else
|
|
objectstatusptr->Indestructable = No;
|
|
|
|
if(sbptr->integrity==0)
|
|
{
|
|
//destroy the object by applying some damage to it
|
|
InanimateObjectIsDamaged(sbptr, &certainDeath, ONE_FIXED);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static void FragmentInanimateObject(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
MakeFragments(sbPtr);
|
|
}
|
|
|
|
static void ExplodeInanimateObject(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
Sound_Play(SID_EXPLOSION,"d",&(sbPtr->DynPtr->Position));
|
|
}
|
|
|
|
/*-------------------Patrick 30/4/96-----------------------
|
|
A couple of functions for supporting respawning objects
|
|
in network games...
|
|
---------------------------------------------------------*/
|
|
static void RespawnInanimateObject(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
INANIMATEOBJECT_STATUSBLOCK* objectstatusptr = sbPtr->SBdataptr;
|
|
LOCALASSERT(objectstatusptr);
|
|
|
|
sbPtr->maintainVisibility = 1;
|
|
MakeObjectNear(sbPtr);
|
|
|
|
/* must respawn health too... */
|
|
sbPtr->SBDamageBlock.Health = objectstatusptr->startingHealth;
|
|
sbPtr->SBDamageBlock.Armour = objectstatusptr->startingArmour;
|
|
|
|
}
|
|
|
|
void KillInanimateObjectForRespawn(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
INANIMATEOBJECT_STATUSBLOCK* objectstatusptr = sbPtr->SBdataptr;
|
|
LOCALASSERT(objectstatusptr);
|
|
LOCALASSERT(AvP.Network!=I_No_Network);
|
|
|
|
/* make the object invisible, and remove it from visibility management */
|
|
if(!objectstatusptr->lifespanTimer)
|
|
{
|
|
sbPtr->maintainVisibility = 0;
|
|
if(sbPtr->SBdptr) MakeObjectFar(sbPtr);
|
|
|
|
if(netGameData.timeForRespawn>0)
|
|
objectstatusptr->respawnTimer = netGameData.timeForRespawn*ONE_FIXED;
|
|
else
|
|
objectstatusptr->respawnTimer = OBJECT_RESPAWN_NO_RESPAWN;
|
|
}
|
|
else
|
|
{
|
|
//get rid of this object permanently
|
|
DestroyAnyStrategyBlock(sbPtr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void KillFragmentalObjectForRespawn(STRATEGYBLOCK *sbPtr)
|
|
{
|
|
INANIMATEOBJECT_STATUSBLOCK* objectstatusptr = sbPtr->SBdataptr;
|
|
LOCALASSERT(objectstatusptr);
|
|
LOCALASSERT(AvP.Network!=I_No_Network);
|
|
|
|
/* make the object invisible, and remove it from visibility management */
|
|
sbPtr->maintainVisibility = 0;
|
|
if(sbPtr->SBdptr) MakeObjectFar(sbPtr);
|
|
|
|
/* KJL 12:44:23 24/05/98 -
|
|
|
|
Set the respawn counter to be the max allowable, so that if all the
|
|
respawn counters are set to zero, it forces all the objects that have been
|
|
destroyed (eg. glass) to respawn simultaneously.
|
|
|
|
Not a perfect solution: the object will respawn in 9.1 hours on its own. */
|
|
|
|
objectstatusptr->respawnTimer = OBJECT_RESPAWN_NO_RESPAWN;
|
|
|
|
}
|
|
|
|
void RespawnAllObjects(void)
|
|
{
|
|
int i;
|
|
|
|
LOCALASSERT(AvP.Network!=I_No_Network);
|
|
|
|
for (i=0; i<NumActiveStBlocks; i++)
|
|
{
|
|
STRATEGYBLOCK *sbPtr = ActiveStBlockList[i];
|
|
|
|
if(sbPtr->I_SBtype == I_BehaviourInanimateObject)
|
|
{
|
|
INANIMATEOBJECT_STATUSBLOCK* objectStatusPtr = sbPtr->SBdataptr;
|
|
LOCALASSERT(objectStatusPtr);
|
|
|
|
if(objectStatusPtr->respawnTimer!=0)
|
|
{
|
|
RespawnInanimateObject(sbPtr);
|
|
objectStatusPtr->respawnTimer=0;
|
|
}
|
|
}
|
|
else if(sbPtr->I_SBtype == I_BehaviourPlacedLight)
|
|
{
|
|
RespawnLight(sbPtr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RespawnAllPickups(void)
|
|
{
|
|
int i;
|
|
|
|
LOCALASSERT(AvP.Network!=I_No_Network);
|
|
|
|
for (i=0; i<NumActiveStBlocks; i++)
|
|
{
|
|
STRATEGYBLOCK *sbPtr = ActiveStBlockList[i];
|
|
|
|
if(sbPtr->I_SBtype == I_BehaviourInanimateObject)
|
|
{
|
|
INANIMATEOBJECT_STATUSBLOCK* objectStatusPtr = sbPtr->SBdataptr;
|
|
LOCALASSERT(objectStatusPtr);
|
|
|
|
if(objectStatusPtr->typeId==IOT_Weapon ||
|
|
objectStatusPtr->typeId==IOT_Ammo ||
|
|
objectStatusPtr->typeId==IOT_Health ||
|
|
objectStatusPtr->typeId==IOT_Armour ||
|
|
objectStatusPtr->typeId==IOT_FieldCharge)
|
|
{
|
|
if(objectStatusPtr->respawnTimer!=0)
|
|
{
|
|
RespawnInanimateObject(sbPtr);
|
|
objectStatusPtr->respawnTimer=0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void IdentifyObject(STRATEGYBLOCK *sbPtr) {
|
|
|
|
if (sbPtr==NULL) {
|
|
textprint("Object is NULL!\n");
|
|
return;
|
|
}
|
|
|
|
if (sbPtr->I_SBtype == I_BehaviourInanimateObject) {
|
|
INANIMATEOBJECT_STATUSBLOCK* objStatPtr = sbPtr->SBdataptr;
|
|
|
|
switch(objStatPtr->typeId)
|
|
{
|
|
case(IOT_Weapon):
|
|
{
|
|
switch(objStatPtr->subType)
|
|
{
|
|
case WEAPON_PULSERIFLE:
|
|
{
|
|
textprint("Object is a Pulse Rifle.\n");
|
|
return;
|
|
}
|
|
case WEAPON_AUTOSHOTGUN:
|
|
{
|
|
textprint("Object is an Autoshotgun.\n");
|
|
return;
|
|
}
|
|
case WEAPON_SMARTGUN:
|
|
{
|
|
textprint("Object is a Smartgun.\n");
|
|
return;
|
|
}
|
|
case WEAPON_FLAMETHROWER:
|
|
{
|
|
textprint("Object is a Flamethrower.\n");
|
|
return;
|
|
}
|
|
case WEAPON_PLASMAGUN:
|
|
{
|
|
textprint("Object is a Plasmagun!\n");
|
|
return;
|
|
}
|
|
case WEAPON_SADAR:
|
|
{
|
|
textprint("Object is a Sadar.\n");
|
|
return;
|
|
}
|
|
case WEAPON_GRENADELAUNCHER:
|
|
{
|
|
textprint("Object is a Grenade Launcher.\n");
|
|
return;
|
|
}
|
|
case WEAPON_MINIGUN:
|
|
{
|
|
textprint("Object is a Minigun.\n");
|
|
return;
|
|
}
|
|
|
|
default:
|
|
textprint("Object is unknown weapon (subtype %d).\n",(int)objStatPtr->subType);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case(IOT_Ammo):
|
|
{
|
|
switch(objStatPtr->subType)
|
|
{
|
|
case AMMO_10MM_CULW:
|
|
{
|
|
textprint("Object is Pulse Rifle ammo.\n");
|
|
break;
|
|
}
|
|
case AMMO_SHOTGUN:
|
|
{
|
|
textprint("Object is Shotgun ammo.\n");
|
|
break;
|
|
}
|
|
case AMMO_SMARTGUN:
|
|
{
|
|
textprint("Object is Smartgun ammo.\n");
|
|
break;
|
|
}
|
|
case AMMO_FLAMETHROWER:
|
|
{
|
|
textprint("Object is Flamethrower ammo.\n");
|
|
break;
|
|
}
|
|
case AMMO_PLASMA:
|
|
{
|
|
textprint("Object is Plasmagun ammo!\n");
|
|
break;
|
|
}
|
|
case AMMO_SADAR_TOW:
|
|
{
|
|
textprint("Object is Sadar ammo.\n");
|
|
break;
|
|
}
|
|
case AMMO_GRENADE:
|
|
{
|
|
textprint("Object is Grenade Launcher ammo.\n");
|
|
break;
|
|
}
|
|
case AMMO_MINIGUN:
|
|
{
|
|
textprint("Object is Minigun ammo.\n");
|
|
break;
|
|
}
|
|
default:
|
|
textprint("Object is unlisted ammo (subtype %d).\n",(int)objStatPtr->subType);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case(IOT_Health):
|
|
{
|
|
textprint("Object is health.\n");
|
|
break;
|
|
}
|
|
case(IOT_Armour):
|
|
{
|
|
textprint("Object is armour.\n");
|
|
break;
|
|
}
|
|
case(IOT_FieldCharge):
|
|
{
|
|
textprint("Object is field charge powerup.\n");
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
textprint("Object is unlisted subtype (%d).\n",(int)objStatPtr->subType);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
switch(sbPtr->I_SBtype) {
|
|
case I_BehaviourMarine:
|
|
{
|
|
textprint("Object is a marine.\n");
|
|
textprint("Marine is in %s\n",sbPtr->containingModule->name);
|
|
break;
|
|
}
|
|
case I_BehaviourAlien:
|
|
{
|
|
textprint("Object is an alien.\n");
|
|
break;
|
|
}
|
|
case I_BehaviourPredator:
|
|
{
|
|
textprint("Object is a predator.\n");
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
textprint("Object is not supported - change PVisible.c for expanded diagnostics!\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!sbPtr->DynPtr) {
|
|
textprint("Object has no dynamics block!\n");
|
|
} else {
|
|
textprint("Object world co-ords: %d %d %d\n",sbPtr->DynPtr->Position.vx,sbPtr->DynPtr->Position.vy,sbPtr->DynPtr->Position.vz);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
STRATEGYBLOCK* CreateMultiplayerWeaponPickup(VECTORCH* location,int type,char* name)
|
|
{
|
|
TOOLS_DATA_INANIMATEOBJECT toolsdata;
|
|
STRATEGYBLOCK * sbPtr;
|
|
INANIMATEOBJECT_STATUSBLOCK* objectstatusptr;
|
|
int trueType;
|
|
|
|
if ((type==WEAPON_TWO_PISTOLS)||(type==WEAPON_MARINE_PISTOL)) {
|
|
if (!netGameData.allowPistols) {
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
if (type==WEAPON_TWO_PISTOLS) {
|
|
trueType=WEAPON_MARINE_PISTOL;
|
|
} else {
|
|
trueType=type;
|
|
}
|
|
|
|
//fill out a tools template , and use the normal inanimate object creation function
|
|
toolsdata.position=*location;
|
|
toolsdata.orientation.EulerX=0;
|
|
toolsdata.orientation.EulerY=0;
|
|
toolsdata.orientation.EulerZ=0;
|
|
|
|
toolsdata.typeId=IOT_Weapon;
|
|
toolsdata.subType=trueType;
|
|
toolsdata.mass=5;
|
|
toolsdata.integrity=2;
|
|
toolsdata.triggering_event=0;
|
|
toolsdata.explosionType=0;
|
|
|
|
toolsdata.shapeIndex=-1;
|
|
|
|
//find the weapon's shape
|
|
switch(trueType)
|
|
{
|
|
case WEAPON_MARINE_PISTOL:
|
|
toolsdata.shapeIndex=GetLoadedShapeMSL("Pistol");
|
|
break;
|
|
case WEAPON_PULSERIFLE:
|
|
toolsdata.shapeIndex=GetLoadedShapeMSL("pulse");
|
|
break;
|
|
case WEAPON_SMARTGUN:
|
|
toolsdata.shapeIndex=GetLoadedShapeMSL("smart");
|
|
break;
|
|
case WEAPON_FLAMETHROWER:
|
|
toolsdata.shapeIndex=GetLoadedShapeMSL("flame");
|
|
break;
|
|
case WEAPON_FRISBEE_LAUNCHER:
|
|
toolsdata.shapeIndex=GetLoadedShapeMSL("Skeeter");
|
|
toolsdata.orientation.EulerZ=1024;
|
|
break;
|
|
case WEAPON_SADAR:
|
|
toolsdata.shapeIndex=GetLoadedShapeMSL("sadar");
|
|
break;
|
|
case WEAPON_GRENADELAUNCHER:
|
|
toolsdata.shapeIndex=GetLoadedShapeMSL("grenade");
|
|
break;
|
|
case WEAPON_MINIGUN:
|
|
toolsdata.shapeIndex=GetLoadedShapeMSL("minigun");
|
|
break;
|
|
|
|
}
|
|
|
|
if(toolsdata.shapeIndex==-1)
|
|
{
|
|
//failed to find shape
|
|
//forget about it
|
|
return 0;
|
|
}
|
|
//adjust the weapon , so it isn't stuck through the floor
|
|
toolsdata.position.vy-=(mainshapelist[toolsdata.shapeIndex]->shapemaxy);
|
|
|
|
|
|
|
|
sbPtr = CreateActiveStrategyBlock();
|
|
|
|
InitialiseSBValues(sbPtr);
|
|
if(!sbPtr) return 0;
|
|
|
|
sbPtr->I_SBtype = I_BehaviourInanimateObject;
|
|
sbPtr->shapeIndex=toolsdata.shapeIndex;
|
|
|
|
EnableBehaviourType(sbPtr,I_BehaviourInanimateObject, &toolsdata );
|
|
if(!sbPtr->SBdataptr) return 0;
|
|
|
|
if(!name)
|
|
{
|
|
AssignNewSBName(sbPtr);
|
|
AddNetMsg_CreateWeapon(&sbPtr->SBname[0],trueType,location);
|
|
}
|
|
else
|
|
{
|
|
COPY_NAME(sbPtr->SBname,name);
|
|
}
|
|
|
|
objectstatusptr=(INANIMATEOBJECT_STATUSBLOCK*)sbPtr->SBdataptr;
|
|
|
|
objectstatusptr->lifespanTimer=20*ONE_FIXED;
|
|
|
|
//sort out object visibility
|
|
{
|
|
DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
|
|
LOCALASSERT(dynPtr);
|
|
|
|
sbPtr->containingModule = ModuleFromPosition(&(dynPtr->Position), (MODULE *)0);
|
|
sbPtr->maintainVisibility = 1;
|
|
|
|
|
|
dynPtr->GravityOn = 1;
|
|
|
|
}
|
|
|
|
return sbPtr;
|
|
}
|
|
|
|
void MakePlayersWeaponPickupVisible()
|
|
{
|
|
extern int NumActiveStBlocks;
|
|
extern STRATEGYBLOCK *ActiveStBlockList[];
|
|
STRATEGYBLOCK* sbPtr;
|
|
int i;
|
|
/*
|
|
Search through the strategy block list for weapons.
|
|
Any weapons that have a lifespan timer should be made visible.
|
|
There should only be one such object , so we can stop looking once we find it
|
|
*/
|
|
|
|
for(i=NumActiveStBlocks-1;i>=0;i--)
|
|
{
|
|
sbPtr=ActiveStBlockList[i];
|
|
if(sbPtr->I_SBtype==I_BehaviourInanimateObject && !sbPtr->maintainVisibility)
|
|
{
|
|
INANIMATEOBJECT_STATUSBLOCK* objectstatusptr;
|
|
objectstatusptr=(INANIMATEOBJECT_STATUSBLOCK*)sbPtr->SBdataptr;
|
|
|
|
if(objectstatusptr->typeId==IOT_Weapon)
|
|
{
|
|
if(objectstatusptr->lifespanTimer>0)
|
|
{
|
|
//okay we've found the object , so allow it to be visible
|
|
sbPtr->maintainVisibility = 1;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
STRATEGYBLOCK* Create_Pred_Disc_Pickup_For_Load()
|
|
{
|
|
STRATEGYBLOCK* sbPtr;
|
|
TOOLS_DATA_INANIMATEOBJECT toolsData;
|
|
int discShapeIndex;
|
|
|
|
{
|
|
extern SECTION * GetNamedHierarchyFromLibrary(const char * rif_name, const char * hier_name);
|
|
SECTION* section;
|
|
/*
|
|
We need to find the shape index of the disc object. To do this , we need to
|
|
get the disc head-up-display hierarchy , and search for the disc section
|
|
*/
|
|
section = GetNamedHierarchyFromLibrary("pred_HUD","disk");
|
|
if(!section) return NULL;
|
|
|
|
section = GetThisSection(section,"disk");
|
|
if(!section) return NULL;
|
|
|
|
//found the disc section
|
|
discShapeIndex = section->ShapeNum;
|
|
|
|
}
|
|
|
|
//fill in a tools data for the disc
|
|
memset(&toolsData,0,sizeof(toolsData));
|
|
toolsData.typeId = IOT_Ammo;
|
|
toolsData.subType = (int)AMMO_PRED_DISC;
|
|
toolsData.shapeIndex = discShapeIndex;
|
|
|
|
|
|
sbPtr = CreateActiveStrategyBlock();
|
|
|
|
if(!sbPtr)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
InitialiseSBValues(sbPtr);
|
|
sbPtr->I_SBtype = I_BehaviourInanimateObject;
|
|
sbPtr->shapeIndex = discShapeIndex;
|
|
|
|
EnableBehaviourType(sbPtr,I_BehaviourInanimateObject, &toolsData );
|
|
sbPtr->maintainVisibility = 1;
|
|
|
|
return sbPtr;
|
|
|
|
}
|
|
|
|
|
|
/*--------------------**
|
|
** Loading and Saving **
|
|
**--------------------*/
|
|
#include "savegame.h"
|
|
typedef struct inanimate_object_save_block
|
|
{
|
|
SAVE_BLOCK_STRATEGY_HEADER header;
|
|
|
|
INANIMATEOBJECT_TYPE typeId;
|
|
int subType; /* weapon id, security level or other relevant enumerated type... */
|
|
|
|
BOOL Indestructable;
|
|
int explosionTimer; //slight time delay after destruction for explosion
|
|
|
|
//strategyblock stuff
|
|
int integrity;
|
|
DAMAGEBLOCK SBDamageBlock;
|
|
DYNAMICSBLOCK dynamics;
|
|
}INANIMATE_OBJECT_SAVE_BLOCK;
|
|
|
|
|
|
//defines for load/save macros
|
|
#define SAVELOAD_BLOCK block
|
|
#define SAVELOAD_BEHAV objectstatusptr
|
|
|
|
void LoadStrategy_InanimateObject(SAVE_BLOCK_STRATEGY_HEADER* header)
|
|
{
|
|
STRATEGYBLOCK* sbPtr;
|
|
INANIMATEOBJECT_STATUSBLOCK* objectstatusptr;
|
|
INANIMATE_OBJECT_SAVE_BLOCK* block = (INANIMATE_OBJECT_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)
|
|
{
|
|
//Didn't find the object already existing.
|
|
//Might be a predator disc pickup however.
|
|
if(block->typeId == IOT_Ammo && block->subType == (int)AMMO_PRED_DISC)
|
|
{
|
|
//okay we need to create a disc then
|
|
sbPtr = Create_Pred_Disc_Pickup_For_Load();
|
|
}
|
|
if(!sbPtr) return;
|
|
|
|
}
|
|
|
|
//make sure the strategy found is of the right type
|
|
if(sbPtr->I_SBtype != I_BehaviourInanimateObject) return;
|
|
|
|
objectstatusptr = (INANIMATEOBJECT_STATUSBLOCK*)sbPtr->SBdataptr;
|
|
|
|
//start copying stuff
|
|
|
|
COPYELEMENT_LOAD(Indestructable)
|
|
COPYELEMENT_LOAD(explosionTimer)
|
|
COPYELEMENT_LOAD(typeId)
|
|
COPYELEMENT_LOAD(subType)
|
|
|
|
|
|
|
|
*sbPtr->DynPtr = block->dynamics;
|
|
sbPtr->integrity = block->integrity;
|
|
sbPtr->SBDamageBlock = block->SBDamageBlock;
|
|
|
|
}
|
|
|
|
|
|
void SaveStrategy_InanimateObject(STRATEGYBLOCK* sbPtr)
|
|
{
|
|
INANIMATE_OBJECT_SAVE_BLOCK *block;
|
|
INANIMATEOBJECT_STATUSBLOCK* objectstatusptr;
|
|
|
|
objectstatusptr = (INANIMATEOBJECT_STATUSBLOCK*)sbPtr->SBdataptr;
|
|
|
|
GET_STRATEGY_SAVE_BLOCK(block,sbPtr);
|
|
|
|
//start copying stuff
|
|
|
|
COPYELEMENT_SAVE(Indestructable)
|
|
COPYELEMENT_SAVE(explosionTimer)
|
|
COPYELEMENT_SAVE(typeId)
|
|
COPYELEMENT_SAVE(subType)
|
|
|
|
|
|
block->dynamics = *sbPtr->DynPtr;
|
|
block->dynamics.CollisionReportPtr=0;
|
|
|
|
block->integrity = sbPtr->integrity;
|
|
block->SBDamageBlock = sbPtr->SBDamageBlock;
|
|
}
|