mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
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
1118 lines
25 KiB
C++
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;
|
|
}
|
|
}
|