avp/src/avp/pvisible.c
2019-08-20 02:22:36 +02:00

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;
}