mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@219 3b4a5df2-a311-0410-b5c6-a8a6f20db521
533 lines
14 KiB
C++
533 lines
14 KiB
C++
#ifdef PRECOMPILEDHEADERS
|
|
#include "Tactical All.h"
|
|
#else
|
|
#include "math.h"
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
#include "worlddef.h"
|
|
#include "renderworld.h"
|
|
#include "vsurface.h"
|
|
#include "Render Dirty.h"
|
|
#include "sysutil.h"
|
|
#include "container.h"
|
|
#include "wcheck.h"
|
|
#include "video.h"
|
|
#include "vobject_blitters.h"
|
|
#include "faces.h"
|
|
#include "utilities.h"
|
|
#include "overhead.h"
|
|
#include "gap.h"
|
|
#include "Soldier Profile.h"
|
|
#include "Bullets.h"
|
|
#include "los.h"
|
|
#include "worldman.h"
|
|
#include "random.h"
|
|
#include "GameSettings.h"
|
|
#include "FileMan.h"
|
|
#endif
|
|
|
|
|
|
// Defines
|
|
#define NUM_BULLET_SLOTS 50
|
|
|
|
|
|
// GLOBAL FOR FACES LISTING
|
|
BULLET gBullets[ NUM_BULLET_SLOTS ];
|
|
UINT32 guiNumBullets = 0;
|
|
|
|
|
|
INT32 GetFreeBullet(void)
|
|
{
|
|
UINT32 uiCount;
|
|
|
|
for(uiCount=0; uiCount < guiNumBullets; uiCount++)
|
|
{
|
|
if((gBullets[uiCount].fAllocated==FALSE) )
|
|
return((INT32)uiCount);
|
|
}
|
|
|
|
if(guiNumBullets < NUM_BULLET_SLOTS )
|
|
return((INT32)guiNumBullets++);
|
|
|
|
return(-1);
|
|
}
|
|
|
|
|
|
void RecountBullets(void)
|
|
{
|
|
INT32 uiCount;
|
|
|
|
for(uiCount=guiNumBullets-1; (uiCount >=0) ; uiCount--)
|
|
{
|
|
if( ( gBullets[uiCount].fAllocated ) )
|
|
{
|
|
guiNumBullets=(UINT32)(uiCount+1);
|
|
return;
|
|
}
|
|
}
|
|
guiNumBullets = 0;
|
|
}
|
|
|
|
|
|
INT32 CreateBullet( UINT8 ubFirerID, BOOLEAN fFake, UINT16 usFlags,UINT16 fromItem )
|
|
{
|
|
INT32 iBulletIndex;
|
|
BULLET *pBullet;
|
|
|
|
if( ( iBulletIndex = GetFreeBullet() )==(-1) )
|
|
return(-1);
|
|
|
|
memset(&gBullets[ iBulletIndex ], 0, sizeof( BULLET ) );
|
|
|
|
pBullet = &gBullets[ iBulletIndex ];
|
|
|
|
pBullet->iBullet = iBulletIndex;
|
|
pBullet->fAllocated = TRUE;
|
|
pBullet->fLocated = FALSE;
|
|
pBullet->ubFirerID = ubFirerID;
|
|
pBullet->usFlags = usFlags;
|
|
pBullet->usLastStructureHit = 0;
|
|
pBullet->fromItem = fromItem;
|
|
|
|
if (fFake)
|
|
{
|
|
pBullet->fReal = FALSE;
|
|
}
|
|
else
|
|
{
|
|
pBullet->fReal = TRUE;
|
|
}
|
|
|
|
return( iBulletIndex );
|
|
}
|
|
|
|
|
|
void HandleBulletSpecialFlags( INT32 iBulletIndex )
|
|
{
|
|
BULLET *pBullet;
|
|
ANITILE_PARAMS AniParams;
|
|
FLOAT dX, dY;
|
|
UINT8 ubDirection;
|
|
|
|
pBullet = &( gBullets[ iBulletIndex ] );
|
|
|
|
memset( &AniParams, 0, sizeof( ANITILE_PARAMS ) );
|
|
|
|
if ( pBullet->fReal )
|
|
{
|
|
// Create ani tile if this is a spit!
|
|
if ( pBullet->usFlags & ( BULLET_FLAG_KNIFE ) )
|
|
{
|
|
AniParams.sGridNo = (INT16)pBullet->sGridNo;
|
|
AniParams.ubLevelID = ANI_STRUCT_LEVEL;
|
|
AniParams.sDelay = 100;
|
|
AniParams.sStartFrame = 3;
|
|
AniParams.uiFlags = ANITILE_CACHEDTILE | ANITILE_FORWARD | ANITILE_LOOPING | ANITILE_USE_DIRECTION_FOR_START_FRAME;
|
|
AniParams.sX = FIXEDPT_TO_INT32( pBullet->qCurrX );
|
|
AniParams.sY = FIXEDPT_TO_INT32( pBullet->qCurrY );
|
|
AniParams.sZ = CONVERT_HEIGHTUNITS_TO_PIXELS( FIXEDPT_TO_INT32( pBullet->qCurrZ ) );
|
|
|
|
if ( pBullet->usFlags & ( BULLET_FLAG_CREATURE_SPIT ) )
|
|
{
|
|
strcpy( AniParams.zCachedFile, "TILECACHE\\SPIT2.STI" );
|
|
}
|
|
else if ( pBullet->usFlags & ( BULLET_FLAG_KNIFE ) )
|
|
{
|
|
strcpy( AniParams.zCachedFile, "TILECACHE\\KNIFING.STI" );
|
|
pBullet->ubItemStatus = pBullet->pFirer->inv[ HANDPOS ].bStatus[0];
|
|
}
|
|
|
|
// Get direction to use for this guy....
|
|
dX = ( (FLOAT)( pBullet->qIncrX ) / FIXEDPT_FRACTIONAL_RESOLUTION );
|
|
dY = ( (FLOAT)( pBullet->qIncrY ) / FIXEDPT_FRACTIONAL_RESOLUTION );
|
|
|
|
ubDirection = atan8( 0, 0, (INT16)( dX * 100 ), (INT16)( dY * 100 ) );
|
|
|
|
AniParams.uiUserData3 = ubDirection;
|
|
|
|
pBullet->pAniTile = CreateAnimationTile( &AniParams );
|
|
|
|
// IF we are anything that needs a shadow.. set it here....
|
|
if ( pBullet->usFlags & ( BULLET_FLAG_KNIFE ) )
|
|
{
|
|
AniParams.ubLevelID = ANI_SHADOW_LEVEL;
|
|
AniParams.sZ = 0;
|
|
pBullet->pShadowAniTile = CreateAnimationTile( &AniParams );
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void RemoveBullet( INT32 iBullet )
|
|
{
|
|
CHECKV( iBullet < NUM_BULLET_SLOTS );
|
|
|
|
// decrease soldier's bullet count
|
|
|
|
if (gBullets[ iBullet ].fReal)
|
|
{
|
|
// set to be deleted at next update
|
|
gBullets[ iBullet ].fToDelete = TRUE;
|
|
|
|
// decrement reference to bullet in the firer
|
|
gBullets[ iBullet ].pFirer->bBulletsLeft--;
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Ending bullet, bullets left %d", gBullets[ iBullet ].pFirer->bBulletsLeft ) );
|
|
|
|
if ( gBullets[ iBullet ].usFlags & ( BULLET_FLAG_KNIFE ) )
|
|
{
|
|
// Delete ani tile
|
|
if ( gBullets[ iBullet ].pAniTile != NULL )
|
|
{
|
|
DeleteAniTile( gBullets[ iBullet ].pAniTile );
|
|
gBullets[ iBullet ].pAniTile = NULL;
|
|
}
|
|
|
|
// Delete shadow
|
|
if ( gBullets[ iBullet ].usFlags & ( BULLET_FLAG_KNIFE ) )
|
|
{
|
|
if ( gBullets[ iBullet ].pShadowAniTile != NULL )
|
|
{
|
|
DeleteAniTile( gBullets[ iBullet ].pShadowAniTile );
|
|
gBullets[ iBullet ].pShadowAniTile = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// delete this fake bullet right away!
|
|
gBullets[ iBullet ].fAllocated = FALSE;
|
|
RecountBullets();
|
|
}
|
|
}
|
|
|
|
void LocateBullet( INT32 iBulletIndex )
|
|
{
|
|
if ( gGameSettings.fOptions[ TOPTION_SHOW_MISSES ] )
|
|
{
|
|
// Check if a bad guy fired!
|
|
if ( gBullets[ iBulletIndex ].ubFirerID != NOBODY )
|
|
{
|
|
if ( MercPtrs[ gBullets[ iBulletIndex ].ubFirerID ]->bSide == gbPlayerNum )
|
|
{
|
|
if ( !gBullets[ iBulletIndex ].fLocated )
|
|
{
|
|
gBullets[ iBulletIndex ].fLocated = TRUE;
|
|
|
|
//Only if we are in turnbased and noncombat
|
|
if ( gTacticalStatus.uiFlags & TURNBASED && (gTacticalStatus.uiFlags & INCOMBAT) )
|
|
{
|
|
LocateGridNo( (INT16)gBullets[ iBulletIndex ].sGridNo );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void UpdateBullets( )
|
|
{
|
|
UINT32 uiCount;
|
|
LEVELNODE *pNode;
|
|
BOOLEAN fDeletedSome = FALSE;
|
|
|
|
for ( uiCount = 0; uiCount < guiNumBullets; uiCount++ )
|
|
{
|
|
if ( gBullets[ uiCount ].fAllocated)
|
|
{
|
|
if (gBullets[ uiCount ].fReal && !( gBullets[ uiCount ].usFlags & BULLET_STOPPED ) )
|
|
{
|
|
// there are duplicate checks for deletion in case the bullet is deleted by shooting
|
|
// someone at point blank range, in the first MoveBullet call in the FireGun code
|
|
if ( gBullets[ uiCount ].fToDelete )
|
|
{
|
|
// Remove from old position
|
|
gBullets[ uiCount ].fAllocated = FALSE;
|
|
fDeletedSome = TRUE;
|
|
continue;
|
|
}
|
|
|
|
//if ( !( gGameSettings.fOptions[ TOPTION_HIDE_BULLETS ] ) )
|
|
{
|
|
// ALRIGHTY, CHECK WHAT TYPE OF BULLET WE ARE
|
|
|
|
if ( gBullets[ uiCount ].usFlags & ( BULLET_FLAG_CREATURE_SPIT | BULLET_FLAG_KNIFE | BULLET_FLAG_MISSILE | BULLET_FLAG_SMALL_MISSILE | BULLET_FLAG_TANK_CANNON | BULLET_FLAG_FLAME | BULLET_FLAG_TRACER ) )
|
|
{
|
|
}
|
|
else
|
|
{
|
|
RemoveStruct( gBullets[ uiCount ].sGridNo, BULLETTILE1 );
|
|
RemoveStruct( gBullets[ uiCount ].sGridNo, BULLETTILE2 );
|
|
}
|
|
}
|
|
|
|
MoveBullet( uiCount );
|
|
if ( gBullets[ uiCount ].fToDelete )
|
|
{
|
|
// Remove from old position
|
|
gBullets[ uiCount ].fAllocated = FALSE;
|
|
fDeletedSome = TRUE;
|
|
continue;
|
|
}
|
|
|
|
if ( gBullets[ uiCount ].usFlags & BULLET_STOPPED )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Display bullet
|
|
//if ( !( gGameSettings.fOptions[ TOPTION_HIDE_BULLETS ] ) )
|
|
{
|
|
if ( gBullets[ uiCount ].usFlags & ( BULLET_FLAG_KNIFE ) )
|
|
{
|
|
if ( gBullets[ uiCount ].pAniTile != NULL )
|
|
{
|
|
gBullets[ uiCount ].pAniTile->sRelativeX = (INT16) FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrX );
|
|
gBullets[ uiCount ].pAniTile->sRelativeY = (INT16) FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrY );
|
|
gBullets[ uiCount ].pAniTile->pLevelNode->sRelativeZ = (INT16) CONVERT_HEIGHTUNITS_TO_PIXELS( FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrZ ) );
|
|
|
|
if ( gBullets[ uiCount ].usFlags & ( BULLET_FLAG_KNIFE ) )
|
|
{
|
|
gBullets[ uiCount ].pShadowAniTile->sRelativeX = (INT16) FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrX );
|
|
gBullets[ uiCount ].pShadowAniTile->sRelativeY = (INT16) FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrY );
|
|
}
|
|
|
|
}
|
|
}
|
|
// Are we a missle?
|
|
else if ( gBullets[ uiCount ].usFlags & ( BULLET_FLAG_MISSILE | BULLET_FLAG_SMALL_MISSILE | BULLET_FLAG_TANK_CANNON | BULLET_FLAG_FLAME | BULLET_FLAG_CREATURE_SPIT ) )
|
|
{
|
|
}
|
|
else if ( gBullets[ uiCount ].usFlags & ( BULLET_FLAG_TRACER ) )
|
|
{
|
|
ManLooksForOtherTeams(gBullets[ uiCount ].pFirer);
|
|
}
|
|
else
|
|
{
|
|
pNode = AddStructToTail( gBullets[ uiCount ].sGridNo, BULLETTILE1 );
|
|
pNode->ubShadeLevel=DEFAULT_SHADE_LEVEL;
|
|
pNode->ubNaturalShadeLevel=DEFAULT_SHADE_LEVEL;
|
|
pNode->uiFlags |= ( LEVELNODE_USEABSOLUTEPOS | LEVELNODE_IGNOREHEIGHT );
|
|
pNode->sRelativeX = (INT16) FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrX );
|
|
pNode->sRelativeY = (INT16) FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrY );
|
|
pNode->sRelativeZ = (INT16) CONVERT_HEIGHTUNITS_TO_PIXELS( FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrZ ) );
|
|
|
|
// Display shadow
|
|
pNode = AddStructToTail( gBullets[ uiCount ].sGridNo, BULLETTILE2 );
|
|
pNode->ubShadeLevel=DEFAULT_SHADE_LEVEL;
|
|
pNode->ubNaturalShadeLevel=DEFAULT_SHADE_LEVEL;
|
|
pNode->uiFlags |= ( LEVELNODE_USEABSOLUTEPOS | LEVELNODE_IGNOREHEIGHT );
|
|
pNode->sRelativeX = (INT16) FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrX );
|
|
pNode->sRelativeY = (INT16) FIXEDPT_TO_INT32( gBullets[ uiCount ].qCurrY );
|
|
pNode->sRelativeZ = (INT16)gpWorldLevelData[ gBullets[ uiCount ].sGridNo ].sHeight;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( gBullets[ uiCount ].fToDelete )
|
|
{
|
|
gBullets[ uiCount ].fAllocated = FALSE;
|
|
fDeletedSome = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( fDeletedSome )
|
|
{
|
|
RecountBullets( );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BULLET *GetBulletPtr( INT32 iBullet )
|
|
{
|
|
BULLET *pBullet;
|
|
|
|
CHECKN( iBullet < NUM_BULLET_SLOTS );
|
|
|
|
pBullet = &gBullets[ iBullet ];
|
|
|
|
return( pBullet );
|
|
}
|
|
|
|
|
|
void AddMissileTrail( BULLET *pBullet, FIXEDPT qCurrX, FIXEDPT qCurrY, FIXEDPT qCurrZ )
|
|
{
|
|
ANITILE_PARAMS AniParams;
|
|
|
|
// If we are a small missle, don't show
|
|
if ( pBullet->usFlags & ( BULLET_FLAG_SMALL_MISSILE | BULLET_FLAG_FLAME | BULLET_FLAG_CREATURE_SPIT | BULLET_FLAG_TRACER ) )
|
|
{
|
|
if ( pBullet->iLoop < 5 )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If we are a small missle, don't show
|
|
if ( pBullet->usFlags & ( BULLET_FLAG_TANK_CANNON ) )
|
|
{
|
|
//if ( pBullet->iLoop < 40 )
|
|
//{
|
|
return;
|
|
//}
|
|
}
|
|
|
|
|
|
memset( &AniParams, 0, sizeof( ANITILE_PARAMS ) );
|
|
AniParams.sGridNo = (INT16)pBullet->sGridNo;
|
|
AniParams.ubLevelID = ANI_STRUCT_LEVEL;
|
|
AniParams.sDelay = (INT16)( 100 + Random( 100 ) );
|
|
AniParams.sStartFrame = 0;
|
|
AniParams.uiFlags = ANITILE_CACHEDTILE | ANITILE_FORWARD | ANITILE_ALWAYS_TRANSLUCENT;
|
|
AniParams.sX = FIXEDPT_TO_INT32( qCurrX );
|
|
AniParams.sY = FIXEDPT_TO_INT32( qCurrY );
|
|
AniParams.sZ = CONVERT_HEIGHTUNITS_TO_PIXELS( FIXEDPT_TO_INT32( qCurrZ ) );
|
|
|
|
|
|
if ( pBullet->usFlags & ( BULLET_FLAG_MISSILE | BULLET_FLAG_TANK_CANNON ) )
|
|
{
|
|
strcpy( AniParams.zCachedFile, "TILECACHE\\MSLE_SMK.STI" );
|
|
}
|
|
else if ( pBullet->usFlags & ( BULLET_FLAG_SMALL_MISSILE ) )
|
|
{
|
|
strcpy( AniParams.zCachedFile, "TILECACHE\\MSLE_SMA.STI" );
|
|
}
|
|
else if ( pBullet->usFlags & ( BULLET_FLAG_CREATURE_SPIT ) )
|
|
{
|
|
strcpy( AniParams.zCachedFile, "TILECACHE\\MSLE_SPT.STI" );
|
|
}
|
|
else if ( pBullet->usFlags & ( BULLET_FLAG_FLAME ) )
|
|
{
|
|
strcpy( AniParams.zCachedFile, "TILECACHE\\FLMTHR2.STI" );
|
|
AniParams.sDelay = (INT16)( 100 );
|
|
}
|
|
else if ( pBullet->usFlags & ( BULLET_FLAG_TRACER ) )
|
|
{
|
|
strcpy( AniParams.zCachedFile, "TILECACHE\\BULLET_TRACER.STI" );
|
|
AniParams.uiFlags |= ANITILE_LIGHT;
|
|
}
|
|
|
|
CreateAnimationTile( &AniParams );
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN SaveBulletStructureToSaveGameFile( HWFILE hFile )
|
|
{
|
|
UINT32 uiNumBytesWritten;
|
|
UINT16 usCnt;
|
|
UINT32 uiBulletCount=0;
|
|
|
|
//loop through and count the number of bullets
|
|
for( usCnt=0; usCnt<NUM_BULLET_SLOTS; usCnt++ )
|
|
{
|
|
//if the bullet is active, save it
|
|
if( gBullets[ usCnt ].fAllocated )
|
|
{
|
|
uiBulletCount++;
|
|
}
|
|
}
|
|
|
|
//Save the number of Bullets in the array
|
|
FileWrite( hFile, &uiBulletCount, sizeof( UINT32 ), &uiNumBytesWritten );
|
|
if( uiNumBytesWritten != sizeof( UINT32 ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if( uiBulletCount != 0 )
|
|
{
|
|
for( usCnt=0; usCnt<NUM_BULLET_SLOTS; usCnt++ )
|
|
{
|
|
//if the bullet is active, save it
|
|
if( gBullets[ usCnt ].fAllocated )
|
|
{
|
|
//Save the the Bullet structure
|
|
FileWrite( hFile, &gBullets[usCnt], sizeof( BULLET ), &uiNumBytesWritten );
|
|
if( uiNumBytesWritten != sizeof( BULLET ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOLEAN LoadBulletStructureFromSavedGameFile( HWFILE hFile )
|
|
{
|
|
UINT32 uiNumBytesRead;
|
|
UINT16 usCnt;
|
|
|
|
//make sure the bullets are not allocated
|
|
memset( gBullets, 0, NUM_BULLET_SLOTS * sizeof( BULLET ) );
|
|
|
|
//Load the number of Bullets in the array
|
|
FileRead( hFile, &guiNumBullets, sizeof( UINT32 ), &uiNumBytesRead );
|
|
if( uiNumBytesRead != sizeof( UINT32 ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
for( usCnt=0; usCnt<guiNumBullets; usCnt++ )
|
|
{
|
|
//Load the the Bullet structure
|
|
FileRead( hFile, &gBullets[usCnt], sizeof( BULLET ), &uiNumBytesRead );
|
|
if( uiNumBytesRead != sizeof( BULLET ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
//Set some parameters
|
|
gBullets[usCnt].uiLastUpdate = 0;
|
|
if( gBullets[usCnt].ubFirerID != NOBODY )
|
|
gBullets[usCnt].pFirer = &Menptr[ gBullets[usCnt].ubFirerID ];
|
|
else
|
|
gBullets[usCnt].pFirer = NULL;
|
|
|
|
gBullets[usCnt].pAniTile = NULL;
|
|
gBullets[usCnt].pShadowAniTile = NULL;
|
|
gBullets[usCnt].iBullet = usCnt;
|
|
|
|
HandleBulletSpecialFlags( gBullets[usCnt].iBullet );
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
void StopBullet( INT32 iBullet )
|
|
{
|
|
gBullets[ iBullet ].usFlags |= BULLET_STOPPED;
|
|
|
|
RemoveStruct( gBullets[ iBullet ].sGridNo, BULLETTILE1 );
|
|
RemoveStruct( gBullets[ iBullet ].sGridNo, BULLETTILE2 );
|
|
}
|
|
|
|
|
|
void DeleteAllBullets( )
|
|
{
|
|
UINT32 uiCount;
|
|
|
|
for ( uiCount = 0; uiCount < guiNumBullets; uiCount++ )
|
|
{
|
|
if ( gBullets[ uiCount ].fAllocated)
|
|
{
|
|
// Remove from old position
|
|
RemoveBullet( uiCount );
|
|
gBullets[ uiCount ].fAllocated = FALSE;
|
|
}
|
|
}
|
|
|
|
RecountBullets( );
|
|
|
|
} |