Files
source/TileEngine/Interactive Tiles.cpp
T
Overhaul 7a27e7105d New attack busy system
Added missing #includes

Added LUA scripting and console

Changed the way tracers are visualized, so they work more like real tracers instead of a light fountain

Fixed signedness of many grid variables used in GetMouseMapPos calls

Fixed enemy weapon choosing:  Sometimes a mortar is chosen but later rejected, but the grenade class was not reset.  Caused assertion failure

Added checks so enemies don't try to chuck RPG grenades with their hands

Now possible to shoot a someone in the head if he's in water

Fixed InitSightArrays to also clear soldier interrupt duel points.  This was causing an assertion failure elsewhere in the code because the interrupt list still had soldiers on it sometimes when this function was called.

Soldiers are much less willing to forfeit their turn over an attempt to use more APs than they have

Removed early setting of muzzle flash.  This would allow enemies to get an interrupt before you even fired.

Fixed item dropping by AI.  If AI tried to drop something while standing it would cause deadlock

Change to greatly speed up closing the sector inventory window in an unloaded sector

Added conditional compile flag to always give robot weapon ready advantage, even for 360 degree sighting


Check builddefines.h for the conditional flags.  Of greatest interest might be LUA_CONSOLE.  This will cause the game to bring up a command console when run.  However this console is severely lacking in many areas.  If anybody knows of an open-source terminal/console that could be used instead, it would be appreciated.  The existing console does bad things when you try to close it, and since it counts as a separate app, it pauses the game while it has focus.

LUA scripting is very limited, basically just proof of concept.  There is one global variable, the Soldiers array.  An array index gives you the soldier in that slot in the currently loaded sector.  The soldier has a few things that can be accessed:  name (short name); fullname (long name); grid (current grid #, can be changed); walkto(grid) (function to walk to another grid); runto(grid) (function to run to another grid)


git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@924 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2007-06-09 09:01:04 +00:00

1118 lines
25 KiB
C++

#include "builddefines.h"
#ifdef PRECOMPILEDHEADERS
#include "TileEngine All.h"
#else
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include "sgp.h"
#include "himage.h"
#include "vsurface.h"
#include "vsurface_private.h"
#include "wcheck.h"
#include "sysutil.h"
#include "renderworld.h"
#include "interface.h"
#include "Sound Control.h"
#include "worlddef.h"
#include "Interactive Tiles.h"
#include "interface cursors.h"
#include "worldman.h"
#include "structure.h"
#include "Animation Control.h"
#include "points.h"
#include "overhead.h"
#include "structure wrap.h"
#include "tile animation.h"
#include "tile cache.h"
#include "handle doors.h"
#include "Strategicmap.h"
#include "Quests.h"
#include "Dialogue Control.h"
#include "Random.h"
#include "english.h"
#include "handle items.h"
#include "message.h"
#include "handle ui.h"
#include "NPC.h"
#include "Explosion Control.h"
#include "Text.h"
#include "GameSettings.h"
#include "environment.h"
#endif
#define MAX_INTTILE_STACK 10
typedef struct
{
INT16 sGridNo;
UINT8 ubFlags;
INT16 sTileIndex;
INT16 sMaxScreenY;
INT16 sHeighestScreenY;
BOOLEAN fFound;
LEVELNODE *pFoundNode;
INT16 sFoundGridNo;
UINT16 usStructureID;
BOOLEAN fStructure;
} CUR_INTERACTIVE_TILE;
typedef struct
{
INT8 bNum;
CUR_INTERACTIVE_TILE bTiles[ MAX_INTTILE_STACK ];
INT8 bCur;
} INTERACTIVE_TILE_STACK_TYPE;
INTERACTIVE_TILE_STACK_TYPE gCurIntTileStack;
BOOLEAN gfCycleIntTile = FALSE;
CUR_INTERACTIVE_TILE gCurIntTile;
BOOLEAN gfOverIntTile = FALSE;
// Values to determine if we should check or not
INT16 gsINTOldRenderCenterX = 0;
INT16 gsINTOldRenderCenterY = 0;
UINT16 gusINTOldMousePosX = 0;
UINT16 gusINTOldMousePosY = 0;
BOOLEAN RefinePointCollisionOnStruct( INT16 sGridNo, INT16 sTestX, INT16 sTestY, INT16 sSrcX, INT16 sSrcY, LEVELNODE *pNode );
BOOLEAN CheckVideoObjectScreenCoordinateInData( HVOBJECT hSrcVObject, UINT16 usIndex, INT32 iTextX, INT32 iTestY );
BOOLEAN RefineLogicOnStruct( INT16 sGridNo, LEVELNODE *pNode );
BOOLEAN InitInteractiveTileManagement( )
{
return( TRUE );
}
void ShutdownInteractiveTileManagement( )
{
}
BOOLEAN AddInteractiveTile( INT16 sGridNo, LEVELNODE *pLevelNode, UINT32 uiFlags, UINT16 usType )
{
return( TRUE );
}
BOOLEAN StartInteractiveObject( INT16 sGridNo, UINT16 usStructureID, SOLDIERTYPE *pSoldier, UINT8 ubDirection )
{
STRUCTURE * pStructure;
// ATE: Patch fix: Don't allow if alreay in animation
if ( pSoldier->usAnimState == OPEN_STRUCT || pSoldier->usAnimState == OPEN_STRUCT_CROUCHED ||
pSoldier->usAnimState == BEGIN_OPENSTRUCT || pSoldier->usAnimState == BEGIN_OPENSTRUCT_CROUCHED )
{
return( FALSE );
}
pStructure = FindStructureByID( sGridNo, usStructureID );
if (pStructure == NULL)
{
return( FALSE );
}
if (pStructure->fFlags & STRUCTURE_ANYDOOR)
{
// Add soldier event for opening door....
pSoldier->ubPendingAction = MERC_OPENDOOR;
pSoldier->uiPendingActionData1 = usStructureID;
pSoldier->sPendingActionData2 = sGridNo;
pSoldier->bPendingActionData3 = ubDirection;
pSoldier->ubPendingActionAnimCount = 0;
}
else
{
// Add soldier event for opening door....
pSoldier->ubPendingAction = MERC_OPENSTRUCT;
pSoldier->uiPendingActionData1 = usStructureID;
pSoldier->sPendingActionData2 = sGridNo;
pSoldier->bPendingActionData3 = ubDirection;
pSoldier->ubPendingActionAnimCount = 0;
}
return( TRUE );
}
BOOLEAN CalcInteractiveObjectAPs( INT16 sGridNo, STRUCTURE * pStructure, INT16 *psAPCost, INT16 *psBPCost )
{
if (pStructure == NULL)
{
return( FALSE );
}
if (pStructure->fFlags & STRUCTURE_ANYDOOR)
{
// For doors, if open, we can safely add APs for closing
// If closed, we do not know what to do yet...
//if ( pStructure->fFlags & STRUCTURE_OPEN )
//{
*psAPCost = AP_OPEN_DOOR;
*psBPCost = AP_OPEN_DOOR;
//}
//else
//{
// *psAPCost = 0;
// *psBPCost = 0;
//}
}
else
{
*psAPCost = AP_OPEN_DOOR;
*psBPCost = AP_OPEN_DOOR;
}
return( TRUE );
}
BOOLEAN InteractWithInteractiveObject( SOLDIERTYPE *pSoldier, STRUCTURE *pStructure, UINT8 ubDirection )
{
BOOLEAN fDoor = FALSE;
if (pStructure == NULL)
{
return( FALSE );
}
if (pStructure->fFlags & STRUCTURE_ANYDOOR)
{
fDoor = TRUE;
}
InteractWithOpenableStruct( pSoldier, pStructure, ubDirection, fDoor );
return( TRUE );
}
BOOLEAN SoldierHandleInteractiveObject( SOLDIERTYPE *pSoldier )
{
STRUCTURE *pStructure;
UINT16 usStructureID;
INT16 sGridNo;
sGridNo = pSoldier->sPendingActionData2;
usStructureID = (UINT16)pSoldier->uiPendingActionData1;
// HANDLE SOLDIER ACTIONS
pStructure = FindStructureByID( sGridNo, usStructureID );
if (pStructure == NULL)
{
//DEBUG MSG!
return( FALSE );
}
return( HandleOpenableStruct( pSoldier, sGridNo, pStructure ) );
}
void HandleStructChangeFromGridNo( SOLDIERTYPE *pSoldier, INT16 sGridNo )
{
STRUCTURE *pStructure, *pNewStructure;
INT16 sAPCost = 0, sBPCost = 0;
ITEM_POOL *pItemPool;
BOOLEAN fDidMissingQuote = FALSE;
pStructure = FindStructure( sGridNo, STRUCTURE_OPENABLE );
if ( pStructure == NULL )
{
#ifdef JA2TESTVERSION
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"ERROR: Told to handle struct that does not exist at %d.", sGridNo );
#endif
return;
}
// Do sound...
if ( !( pStructure->fFlags & STRUCTURE_OPEN ) )
{
// Play Opening sound...
PlayJA2Sample( GetStructureOpenSound( pStructure, FALSE ), RATE_11025, SoundVolume( HIGHVOLUME, sGridNo ), 1, SoundDir( sGridNo ) );
}
else
{
// Play Opening sound...
PlayJA2Sample( ( GetStructureOpenSound( pStructure, TRUE ) ), RATE_11025, SoundVolume( HIGHVOLUME, sGridNo ), 1, SoundDir( sGridNo ) );
}
// ATE: Don't handle switches!
if ( !( pStructure->fFlags & STRUCTURE_SWITCH ) )
{
if ( pSoldier->bTeam == gbPlayerNum )
{
if ( sGridNo == BOBBYR_SHIPPING_DEST_GRIDNO && gWorldSectorX == BOBBYR_SHIPPING_DEST_SECTOR_X && gWorldSectorY == BOBBYR_SHIPPING_DEST_SECTOR_Y && gbWorldSectorZ == BOBBYR_SHIPPING_DEST_SECTOR_Z && CheckFact( FACT_PABLOS_STOLE_FROM_LATEST_SHIPMENT, 0 ) && !(CheckFact( FACT_PLAYER_FOUND_ITEMS_MISSING, 0) ) )
{
SayQuoteFromNearbyMercInSector( BOBBYR_SHIPPING_DEST_GRIDNO, 3, QUOTE_STUFF_MISSING_DRASSEN );
fDidMissingQuote = TRUE;
}
}
else if ( pSoldier->bTeam == CIV_TEAM )
{
if ( pSoldier->ubProfile != NO_PROFILE )
{
TriggerNPCWithGivenApproach( pSoldier->ubProfile, APPROACH_DONE_OPEN_STRUCTURE, FALSE );
}
}
// LOOK for item pool here...
if ( GetItemPool( (INT16)sGridNo, &pItemPool, pSoldier->bLevel ) )
{
// Update visiblity....
if ( !( pStructure->fFlags & STRUCTURE_OPEN ) )
{
BOOLEAN fDoHumm = TRUE;
BOOLEAN fDoLocators = TRUE;
if ( pSoldier->bTeam != gbPlayerNum )
{
fDoHumm = FALSE;
fDoLocators = FALSE;
}
// Look for ownership here....
if ( gWorldItems[ pItemPool->iItemIndex ].o.usItem == OWNERSHIP )
{
fDoHumm = FALSE;
TacticalCharacterDialogueWithSpecialEvent( pSoldier, 0, DIALOGUE_SPECIAL_EVENT_DO_BATTLE_SND, BATTLE_SOUND_NOTHING , 500 );
}
// If now open, set visible...
SetItemPoolVisibilityOn( pItemPool, ANY_VISIBILITY_VALUE, fDoLocators );
// Display quote!
//TacticalCharacterDialogue( pSoldier, (UINT16)( QUOTE_SPOTTED_SOMETHING_ONE + Random( 2 ) ) );
// ATE: Check now many things in pool.....
if ( !fDidMissingQuote )
{
if ( pItemPool->pNext != NULL )
{
if ( pItemPool->pNext->pNext != NULL )
{
fDoHumm = FALSE;
TacticalCharacterDialogueWithSpecialEvent( pSoldier, 0, DIALOGUE_SPECIAL_EVENT_DO_BATTLE_SND, BATTLE_SOUND_COOL1 , 500 );
}
}
if ( fDoHumm )
{
TacticalCharacterDialogueWithSpecialEvent( pSoldier, 0, DIALOGUE_SPECIAL_EVENT_DO_BATTLE_SND, BATTLE_SOUND_HUMM , 500 );
}
}
}
else
{
SetItemPoolVisibilityHidden( pItemPool );
}
}
else
{
if ( !( pStructure->fFlags & STRUCTURE_OPEN ) )
{
TacticalCharacterDialogueWithSpecialEvent( pSoldier, 0, DIALOGUE_SPECIAL_EVENT_DO_BATTLE_SND, BATTLE_SOUND_NOTHING , 500 );
}
}
}
// Deduct points!
// CalcInteractiveObjectAPs( sGridNo, pStructure, &sAPCost, &sBPCost );
// DeductPoints( pSoldier, sAPCost, sBPCost );
pNewStructure = SwapStructureForPartner( sGridNo, pStructure );
if ( pNewStructure != NULL)
{
RecompileLocalMovementCosts( sGridNo );
SetRenderFlags( RENDER_FLAG_FULL );
if ( pNewStructure->fFlags & STRUCTURE_SWITCH )
{
// just turned a switch on!
ActivateSwitchInGridNo( pSoldier->ubID, sGridNo );
}
}
}
UINT32 GetInteractiveTileCursor( UINT32 uiOldCursor, BOOLEAN fConfirm )
{
LEVELNODE *pIntNode;
STRUCTURE *pStructure;
INT16 sGridNo;
// OK, first see if we have an in tile...
pIntNode = GetCurInteractiveTileGridNoAndStructure( &sGridNo, &pStructure );
if ( pIntNode != NULL && pStructure != NULL )
{
if( pStructure->fFlags & STRUCTURE_ANYDOOR )
{
SetDoorString( sGridNo );
if ( fConfirm )
{
return( OKHANDCURSOR_UICURSOR );
}
else
{
return( NORMALHANDCURSOR_UICURSOR );
}
}
else
{
if( pStructure->fFlags & STRUCTURE_SWITCH )
{
wcscpy( gzIntTileLocation, gzLateLocalizedString[ 25 ] );
gfUIIntTileLocation = TRUE;
}
if ( fConfirm )
{
return( OKHANDCURSOR_UICURSOR );
}
else
{
return( NORMALHANDCURSOR_UICURSOR );
}
}
}
return( uiOldCursor );
}
void SetActionModeDoorCursorText( )
{
LEVELNODE *pIntNode;
STRUCTURE *pStructure;
INT16 sGridNo;
// If we are over a merc, don't
if ( gfUIFullTargetFound )
{
return;
}
// OK, first see if we have an in tile...
pIntNode = GetCurInteractiveTileGridNoAndStructure( &sGridNo, &pStructure );
if ( pIntNode != NULL && pStructure != NULL )
{
if( pStructure->fFlags & STRUCTURE_ANYDOOR )
{
SetDoorString( sGridNo );
}
}
}
void GetLevelNodeScreenRect( LEVELNODE *pNode, SGPRect *pRect, INT16 sXPos, INT16 sYPos, INT16 sGridNo )
{
INT16 sScreenX, sScreenY;
INT16 sOffsetX, sOffsetY;
INT16 sTempX_S, sTempY_S;
ETRLEObject *pTrav;
UINT32 usHeight, usWidth;
TILE_ELEMENT *TileElem;
// Get 'TRUE' merc position
sOffsetX = sXPos - gsRenderCenterX;
sOffsetY = sYPos - gsRenderCenterY;
FromCellToScreenCoordinates( sOffsetX, sOffsetY, &sTempX_S, &sTempY_S );
if ( pNode->uiFlags & LEVELNODE_CACHEDANITILE )
{
pTrav = &(gpTileCache[ pNode->pAniTile->sCachedTileID ].pImagery->vo->pETRLEObject[ pNode->pAniTile->sCurrentFrame ] );
}
else
{
TileElem = &(gTileDatabase[pNode->usIndex]);
//Adjust for current frames and animations....
if ( TileElem->uiFlags & ANIMATED_TILE)
{
Assert( TileElem->pAnimData != NULL );
TileElem = &gTileDatabase[TileElem->pAnimData->pusFrames[TileElem->pAnimData->bCurrentFrame]];
}
else if( ( pNode->uiFlags & LEVELNODE_ANIMATION ) )
{
if ( pNode->sCurrentFrame != -1 )
{
Assert( TileElem->pAnimData != NULL );
TileElem = &gTileDatabase[TileElem->pAnimData->pusFrames[pNode->sCurrentFrame]];
}
}
pTrav = &(TileElem->hTileSurface->pETRLEObject[ TileElem->usRegionIndex ] );
}
sScreenX = ( ( gsVIEWPORT_END_X - gsVIEWPORT_START_X ) /2 ) + (INT16)sTempX_S;
sScreenY = ( ( gsVIEWPORT_END_Y - gsVIEWPORT_START_Y ) /2 ) + (INT16)sTempY_S;
// Adjust for offset position on screen
sScreenX -= gsRenderWorldOffsetX;
sScreenY -= gsRenderWorldOffsetY;
sScreenY -= gpWorldLevelData[ sGridNo ].sHeight;
// Adjust based on interface level
if ( gsInterfaceLevel > 0 )
{
sScreenY += ROOF_LEVEL_HEIGHT;
}
// Adjust for render height
sScreenY += gsRenderHeight;
usHeight = (UINT32)pTrav->usHeight;
usWidth = (UINT32)pTrav->usWidth;
// Add to start position of dest buffer
sScreenX += ( pTrav->sOffsetX - ( WORLD_TILE_X/2 ) );
sScreenY += ( pTrav->sOffsetY - ( WORLD_TILE_Y/2 ) );
// Adjust y offset!
sScreenY += ( WORLD_TILE_Y/2);
pRect->iLeft = sScreenX;
pRect->iTop = sScreenY;
pRect->iBottom = sScreenY + usHeight;
pRect->iRight = sScreenX + usWidth;
}
void CompileInteractiveTiles( )
{
}
void LogMouseOverInteractiveTile( INT16 sGridNo )
{
SGPRect aRect;
INT16 sXMapPos, sYMapPos, sScreenX, sScreenY;
LEVELNODE *pNode;
// OK, for now, don't allow any interactive tiles on higher interface level!
if ( gsInterfaceLevel > 0 )
{
return;
}
// Also, don't allow for mercs who are on upper level...
if ( gusSelectedSoldier != NOBODY && MercPtrs[ gusSelectedSoldier ]->bLevel == 1 )
{
return;
}
// Get World XY From gridno
ConvertGridNoToCellXY( sGridNo, &sXMapPos, &sYMapPos );
// Set mouse stuff
sScreenX = gusMouseXPos;
sScreenY = gusMouseYPos;
pNode = gpWorldLevelData[ sGridNo ].pStructHead;
while( pNode != NULL )
{
{
GetLevelNodeScreenRect( pNode, &aRect, sXMapPos, sYMapPos , sGridNo );
// Make sure we are always on guy if we are on same gridno
if ( IsPointInScreenRect( sScreenX, sScreenY, &aRect ) )
{
// OK refine it!
if ( RefinePointCollisionOnStruct( sGridNo, sScreenX, sScreenY, (INT16)aRect.iLeft, (INT16)aRect.iBottom, pNode ) )
{
// Do some additional checks here!
if ( RefineLogicOnStruct( sGridNo, pNode ) )
{
gCurIntTile.fFound = TRUE;
// Only if we are not currently cycling....
if ( !gfCycleIntTile )
{
// Accumulate them!
gCurIntTileStack.bTiles[ gCurIntTileStack.bNum ].pFoundNode = pNode;
gCurIntTileStack.bTiles[ gCurIntTileStack.bNum ].sFoundGridNo = sGridNo;
gCurIntTileStack.bNum++;
// Determine if it's the best one
if ( aRect.iBottom > gCurIntTile.sHeighestScreenY )
{
gCurIntTile.sMaxScreenY = (UINT16)aRect.iBottom;
gCurIntTile.sHeighestScreenY = gCurIntTile.sMaxScreenY;
// Set it!
gCurIntTile.pFoundNode = pNode;
gCurIntTile.sFoundGridNo = sGridNo;
// Set stack current one...
gCurIntTileStack.bCur = gCurIntTileStack.bNum-1;
}
}
}
}
}
pNode = pNode->pNext;
}
}
}
LEVELNODE *InternalGetCurInteractiveTile( BOOLEAN fRejectItemsOnTop )
{
LEVELNODE *pNode = NULL;
STRUCTURE *pStructure = NULL;
// OK, Look for our tile!
// Check for shift down!
if ( _KeyDown( SHIFT ) )
{
return( NULL );
}
if ( gfOverIntTile )
{
pNode = gpWorldLevelData[ gCurIntTile.sGridNo ].pStructHead;
while( pNode != NULL )
{
if ( pNode->usIndex == gCurIntTile.sTileIndex )
{
if ( fRejectItemsOnTop )
{
// get strucuture here...
if ( gCurIntTile.fStructure )
{
pStructure = FindStructureByID( gCurIntTile.sGridNo, gCurIntTile.usStructureID );
if (pStructure != NULL)
{
if ( pStructure->fFlags & STRUCTURE_HASITEMONTOP )
{
return( NULL );
}
}
else
{
return( NULL );
}
}
}
return( pNode );
}
pNode = pNode->pNext;
}
}
return( NULL );
}
LEVELNODE *GetCurInteractiveTile( )
{
return( InternalGetCurInteractiveTile( TRUE ) );
}
LEVELNODE *GetCurInteractiveTileGridNo( INT16 *psGridNo )
{
LEVELNODE *pNode;
pNode = GetCurInteractiveTile( );
if ( pNode != NULL )
{
*psGridNo = gCurIntTile.sGridNo;
}
else
{
*psGridNo = NOWHERE;
}
return( pNode );
}
LEVELNODE *ConditionalGetCurInteractiveTileGridNoAndStructure( INT16 *psGridNo, STRUCTURE **ppStructure, BOOLEAN fRejectOnTopItems )
{
LEVELNODE *pNode;
STRUCTURE *pStructure;
*ppStructure = NULL;
pNode = InternalGetCurInteractiveTile( fRejectOnTopItems );
if ( pNode != NULL )
{
*psGridNo = gCurIntTile.sGridNo;
}
else
{
*psGridNo = NOWHERE;
}
if ( pNode != NULL )
{
if ( gCurIntTile.fStructure )
{
pStructure = FindStructureByID( gCurIntTile.sGridNo, gCurIntTile.usStructureID );
if (pStructure == NULL)
{
*ppStructure = NULL;
return( NULL );
}
else
{
*ppStructure = pStructure;
}
}
}
return( pNode );
}
LEVELNODE *GetCurInteractiveTileGridNoAndStructure( INT16 *psGridNo, STRUCTURE **ppStructure )
{
return( ConditionalGetCurInteractiveTileGridNoAndStructure( psGridNo, ppStructure, TRUE ) );
}
void BeginCurInteractiveTileCheck( UINT8 bCheckFlags )
{
gfOverIntTile = FALSE;
// OK, release our stack, stuff could be different!
gfCycleIntTile = FALSE;
// Reset some highest values
gCurIntTile.sHeighestScreenY = 0;
gCurIntTile.fFound = FALSE;
gCurIntTile.ubFlags = bCheckFlags;
// Reset stack values
gCurIntTileStack.bNum = 0;
}
void EndCurInteractiveTileCheck( )
{
CUR_INTERACTIVE_TILE *pCurIntTile;
if ( gCurIntTile.fFound )
{
// Set our currently cycled guy.....
if ( gfCycleIntTile )
{
// OK, we're over this cycled node
pCurIntTile = &( gCurIntTileStack.bTiles[ gCurIntTileStack.bCur ] );
}
else
{
// OK, we're over this levelnode,
pCurIntTile = &gCurIntTile;
}
gCurIntTile.sGridNo = pCurIntTile->sFoundGridNo;
gCurIntTile.sTileIndex = pCurIntTile->pFoundNode->usIndex;
if ( pCurIntTile->pFoundNode->pStructureData != NULL )
{
gCurIntTile.usStructureID = pCurIntTile->pFoundNode->pStructureData->usStructureID;
gCurIntTile.fStructure = TRUE;
}
else
{
gCurIntTile.fStructure = FALSE;
}
gfOverIntTile = TRUE;
}
else
{
// If we are in cycle mode, end it
if ( gfCycleIntTile )
{
gfCycleIntTile = FALSE;
}
}
}
BOOLEAN RefineLogicOnStruct( INT16 sGridNo, LEVELNODE *pNode )
{
TILE_ELEMENT *TileElem;
STRUCTURE *pStructure;
if ( pNode->uiFlags & LEVELNODE_CACHEDANITILE )
{
return ( FALSE );
}
TileElem = &(gTileDatabase[pNode->usIndex]);
if ( gCurIntTile.ubFlags == INTILE_CHECK_SELECTIVE )
{
// See if we are on an interactable tile!
// Try and get struct data from levelnode pointer
pStructure = pNode->pStructureData;
// If no data, quit
if ( pStructure == NULL )
{
return( FALSE );
}
if ( !( pStructure->fFlags & ( STRUCTURE_OPENABLE | STRUCTURE_HASITEMONTOP ) ) )
{
return( FALSE );
}
if ( gusSelectedSoldier != NOBODY && MercPtrs[ gusSelectedSoldier ]->ubBodyType == ROBOTNOWEAPON )
{
return( FALSE );
}
// If we are a door, we need a different definition of being visible than other structs
if ( pStructure->fFlags & STRUCTURE_ANYDOOR )
{
if ( !IsDoorVisibleAtGridNo( sGridNo ) )
{
return( FALSE );
}
// OK, For a OPENED door, addition requirements are: need to be in 'HAND CURSOR' mode...
if ( pStructure->fFlags & STRUCTURE_OPEN )
{
//Are we in hand cursor mode?
if ( gCurrentUIMode != HANDCURSOR_MODE && gCurrentUIMode != ACTION_MODE )
{
return( FALSE );
}
}
// If this option is on...
if ( !gGameSettings.fOptions[ TOPTION_SNAP_CURSOR_TO_DOOR ] )
{
if ( gCurrentUIMode != HANDCURSOR_MODE )
{
return( FALSE );
}
}
}
else
{
// IF we are a switch, reject in another direction...
if ( pStructure->fFlags & STRUCTURE_SWITCH )
{
// Find a new gridno based on switch's orientation...
INT16 sNewGridNo = NOWHERE;
switch( pStructure->pDBStructureRef->pDBStructure->ubWallOrientation )
{
case OUTSIDE_TOP_LEFT:
case INSIDE_TOP_LEFT:
// Move south...
sNewGridNo = NewGridNo( sGridNo, DirectionInc( SOUTH ) );
break;
case OUTSIDE_TOP_RIGHT:
case INSIDE_TOP_RIGHT:
// Move east...
sNewGridNo = NewGridNo( sGridNo, DirectionInc( EAST ) );
break;
}
if ( sNewGridNo != NOWHERE )
{
// If we are hidden by a roof, reject it!
if ( !gfBasement && IsRoofVisible2( sNewGridNo ) && !( gTacticalStatus.uiFlags&SHOW_ALL_ITEMS ) )
{
return( FALSE );
}
}
}
else
{
// If we are hidden by a roof, reject it!
if ( !gfBasement && IsRoofVisible( sGridNo ) && !( gTacticalStatus.uiFlags&SHOW_ALL_ITEMS ) )
{
return( FALSE );
}
}
}
// Check if it's a hidden struct and we have not revealed anything!
if ( TileElem->uiFlags & HIDDEN_TILE )
{
if ( !IsHiddenStructureVisible( sGridNo, pNode->usIndex ) )
{
// Return false
return( FALSE );
}
}
}
return( TRUE );
}
BOOLEAN RefinePointCollisionOnStruct( INT16 sGridNo, INT16 sTestX, INT16 sTestY, INT16 sSrcX, INT16 sSrcY, LEVELNODE *pNode )
{
TILE_ELEMENT *TileElem;
if ( pNode->uiFlags & LEVELNODE_CACHEDANITILE )
{
//Check it!
return ( CheckVideoObjectScreenCoordinateInData( gpTileCache[ pNode->pAniTile->sCachedTileID ].pImagery->vo, pNode->pAniTile->sCurrentFrame, (INT32)( sTestX - sSrcX ), (INT32)( -1 * ( sTestY - sSrcY ) ) ) );
}
else
{
TileElem = &( gTileDatabase[pNode->usIndex] );
//Adjust for current frames and animations....
if ( TileElem->uiFlags & ANIMATED_TILE)
{
Assert( TileElem->pAnimData != NULL );
TileElem = &gTileDatabase[TileElem->pAnimData->pusFrames[TileElem->pAnimData->bCurrentFrame]];
}
else if( ( pNode->uiFlags & LEVELNODE_ANIMATION ) )
{
if ( pNode->sCurrentFrame != -1 )
{
Assert( TileElem->pAnimData != NULL );
TileElem = &gTileDatabase[TileElem->pAnimData->pusFrames[pNode->sCurrentFrame]];
}
}
//Check it!
return ( CheckVideoObjectScreenCoordinateInData( TileElem->hTileSurface, TileElem->usRegionIndex, (INT32)( sTestX - sSrcX ), (INT32)( -1 * ( sTestY - sSrcY ) ) ) );
}
}
// This function will check the video object at SrcX and SrcY for the lack of transparency
// will return true if data found, else false
BOOLEAN CheckVideoObjectScreenCoordinateInData( HVOBJECT hSrcVObject, UINT16 usIndex, INT32 iTestX, INT32 iTestY )
{
UINT32 uiOffset;
UINT32 usHeight, usWidth;
UINT8 *SrcPtr;
UINT32 LineSkip;
ETRLEObject *pTrav;
BOOLEAN fDataFound = FALSE;
INT32 iTestPos, iStartPos;
// Assertions
Assert( hSrcVObject != NULL );
// Get Offsets from Index into structure
pTrav = &(hSrcVObject->pETRLEObject[ usIndex ] );
usHeight = (UINT32)pTrav->usHeight;
usWidth = (UINT32)pTrav->usWidth;
uiOffset = pTrav->uiDataOffset;
// Calculate test position we are looking for!
// Calculate from 0, 0 at top left!
iTestPos = ( ( usHeight - iTestY ) * usWidth ) + iTestX;
iStartPos = 0;
LineSkip = usWidth;
SrcPtr= (UINT8 *)hSrcVObject->pPixData + uiOffset;
__asm {
mov esi, SrcPtr
mov edi, iStartPos
xor eax, eax
xor ebx, ebx
xor ecx, ecx
BlitDispatch:
mov cl, [esi]
inc esi
or cl, cl
js BlitTransparent
jz BlitDoneLine
//BlitNonTransLoop:
clc
rcr cl, 1
jnc BlitNTL2
inc esi
// Check
cmp edi, iTestPos
je BlitFound
add edi, 1
BlitNTL2:
clc
rcr cl, 1
jnc BlitNTL3
add esi, 2
// Check
cmp edi, iTestPos
je BlitFound
add edi, 1
// Check
cmp edi, iTestPos
je BlitFound
add edi, 1
BlitNTL3:
or cl, cl
jz BlitDispatch
xor ebx, ebx
BlitNTL4:
add esi, 4
// Check
cmp edi, iTestPos
je BlitFound
add edi, 1
// Check
cmp edi, iTestPos
je BlitFound
add edi, 1
// Check
cmp edi, iTestPos
je BlitFound
add edi, 1
// Check
cmp edi, iTestPos
je BlitFound
add edi, 1
dec cl
jnz BlitNTL4
jmp BlitDispatch
BlitTransparent:
and ecx, 07fH
// shl ecx, 1
add edi, ecx
jmp BlitDispatch
BlitDoneLine:
// Here check if we have passed!
cmp edi, iTestPos
jge BlitDone
dec usHeight
jz BlitDone
// add edi, LineSkip
jmp BlitDispatch
BlitFound:
mov fDataFound, 1
BlitDone:
}
return(fDataFound);
}
BOOLEAN ShouldCheckForMouseDetections( )
{
BOOLEAN fOK = FALSE;
if ( gsINTOldRenderCenterX != gsRenderCenterX || gsINTOldRenderCenterY != gsRenderCenterY ||
gusINTOldMousePosX != gusMouseXPos || gusINTOldMousePosY != gusMouseYPos )
{
fOK = TRUE;
}
// Set old values
gsINTOldRenderCenterX = gsRenderCenterX;
gsINTOldRenderCenterY = gsRenderCenterY;
gusINTOldMousePosX = gusMouseXPos;
gusINTOldMousePosY = gusMouseYPos;
return( fOK );
}
void CycleIntTileFindStack( UINT16 usMapPos )
{
gfCycleIntTile = TRUE;
// Cycle around!
gCurIntTileStack.bCur++;
//PLot new movement
gfPlotNewMovement = TRUE;
if ( gCurIntTileStack.bCur == gCurIntTileStack.bNum )
{
gCurIntTileStack.bCur = 0;
}
}