Files
source/Editor/Cursor Modes.cpp
T
00fb898600 Delete builddefines.h
Nothing was left in it but the include of profiler.h, and 132 translation units
were including it for that alone. Fifteen files were leaning on profiler.h to
drag in <set>, <vector> and <ostream> for them; those now include what they use.

This is the commit that moves line numbers. Removing an include line shifts
__LINE__ by one for everything below it, and __LINE__ is an immediate operand in
every Assert() and DebugMsg() call, so the four game executables differ from
their predecessors by roughly a thousand 32-bit constants each. Every one of
those is accounted for: each is a single immediate that moved by -1 where the
builddefines.h include went away, or +1 where a <set>/<vector> include was
added. Nothing else in .text, .rdata or .data moves, no object file's section
sizes change, and symbolize_crash and Ja2Export stay bit-identical.

The one non-immediate difference is that the 24 Editor translation units of the
non-editor apps stop emitting __Avx2WmemEnabledWeakValue, a 4-byte weak COMDAT
they only ever instantiated through profiler.h's <vector>. It is a UCRT weak
default that other translation units still provide.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 01:07:26 -03:00

461 lines
12 KiB
C++

#ifdef JA2EDITOR
#include "types.h"
#include "Cursor Modes.h"
#include "renderworld.h"
#include "random.h"
#include "worldman.h"
#include "Interface.h"
#include "Isometric Utils.h"
#include "editscreen.h"
#include "EditorDefines.h"
#include "input.h"
#include "english.h"
#include "Editor Taskbar Utils.h"
#include "Overhead.h"
#include "EditorMercs.h"
#include "EditorBuildings.h"
#include "Text.h"
BOOLEAN fValidCursor = FALSE;
BOOLEAN fAnchored = FALSE;
BOOLEAN gfBrushEnabled = TRUE;
UINT16 gusSelectionWidth = 1, gusPreserveSelectionWidth = 1;
UINT16 gusSelectionType = SMALLSELECTION;
UINT16 gusSelectionDensity = 2;
UINT16 gusSavedSelectionType = SMALLSELECTION;
UINT16 gusSavedBuildingSelectionType = AREASELECTION;
extern INT16 sGridX; // symbol already declared globally in editscreen.cpp (jonathanl)
extern INT16 sGridY; // symbol already declared globally in editscreen.cpp (jonathanl)
INT32 sBadMarker = -1;
//STR16 wszSelType[6]= { L"Small", L"Medium", L"Large", L"XLarge", L"Width: xx", L"Area" };
BOOLEAN gfAllowRightButtonSelections = FALSE;
BOOLEAN gfCurrentSelectionWithRightButton = FALSE;
void DrawBuildingLayout( INT32 iMapIndex );
void RemoveBuildingLayout();
void ValidateSelectionRegionBoundaries();
void ForceAreaSelectionWidth();
BOOLEAN HandleAreaSelection();
void EnsureSelectionType();
BOOLEAN gfUsingOffset;
//Based on the density level setting and the selection type, this test will
//randomly choose TRUE or FALSE to reflect the *odds*.
BOOLEAN PerformDensityTest()
{
if( Random(100) < gusSelectionDensity )
return TRUE;
return FALSE;
}
void IncreaseSelectionDensity()
{
if( gusSelectionDensity == 100 )
gusSelectionDensity = 2;
else if( gusSelectionDensity == 2 )
gusSelectionDensity = 5;
else if( gusSelectionDensity == 5 )
gusSelectionDensity = 10;
else
gusSelectionDensity += 10;
}
void DecreaseSelectionDensity()
{
if( gusSelectionDensity == 10 )
gusSelectionDensity = 5;
else if( gusSelectionDensity == 5 )
gusSelectionDensity = 2;
else if( gusSelectionDensity == 2 )
gusSelectionDensity = 100;
else
gusSelectionDensity -= 10;
}
void RemoveCursors()
{
if((gSelectRegion.iRight + gSelectRegion.iBottom * WORLD_COLS) >= WORLD_MAX)//dnl ch43 280909 Prevent CTD when world size is changed
return;
INT32 x, y, iMapIndex;
if( gpBuildingLayoutList )
{
RemoveBuildingLayout();
}
Assert( gSelectRegion.iTop >= 0 && gSelectRegion.iTop <= gSelectRegion.iBottom );
Assert( gSelectRegion.iLeft >= 0 && gSelectRegion.iLeft <= gSelectRegion.iRight );
for( y = gSelectRegion.iTop; y <= gSelectRegion.iBottom; y++ )
{
for( x = gSelectRegion.iLeft; x <= gSelectRegion.iRight; x++ )
{
LEVELNODE* pNode;
iMapIndex = y * WORLD_COLS + x;
if( gfUsingOffset )
iMapIndex += ROOF_OFFSET;
pNode = gpWorldLevelData[ iMapIndex ].pTopmostHead;
while( pNode )
{
if( pNode->usIndex == FIRSTPOINTERS1 || pNode->usIndex == FIRSTPOINTERS5 || pNode->usIndex == BADMARKER1 )
{
RemoveTopmost( iMapIndex, pNode->usIndex );
break;
}
pNode = pNode->pNext;
}
}
}
fValidCursor = FALSE;
gfUsingOffset = FALSE;
}
void RemoveBadMarker()
{
LEVELNODE *pNode;
if( sBadMarker < 0 )
return;
pNode = gpWorldLevelData[ sBadMarker ].pTopmostHead;
while( pNode )
{
if( pNode->usIndex == BADMARKER1 )
{
RemoveTopmost( sBadMarker, pNode->usIndex );
sBadMarker = -1;
break;
}
pNode = pNode->pNext;
}
}
void UpdateCursorAreas()
{
INT32 x, y, iMapIndex;
RemoveCursors();
EnsureSelectionType();
//Determine if the mouse is currently in the world.
if( gViewportRegion.uiFlags & MSYS_MOUSE_IN_AREA && GetMouseXY( &sGridX, &sGridY ) )
{
iMapIndex = MAPROWCOLTOPOS( sGridY, sGridX );
if( gpBuildingLayoutList )
{
gSelectRegion.iLeft = gSelectRegion.iRight = sGridX;
gSelectRegion.iTop = gSelectRegion.iBottom = sGridY;
fValidCursor = TRUE;
DrawBuildingLayout( iMapIndex );
}
else switch( gusSelectionType )
{
case SMALLSELECTION:
gSelectRegion.iLeft = gSelectRegion.iRight = sGridX;
gSelectRegion.iTop = gSelectRegion.iBottom = sGridY;
fValidCursor = TRUE;
break;
case MEDIUMSELECTION:
case LARGESELECTION:
case XLARGESELECTION:
//The mouse mode value reflects the size of the cursor.
gSelectRegion.iTop = sGridY - gusSelectionType;
gSelectRegion.iBottom = sGridY + gusSelectionType;
gSelectRegion.iLeft = sGridX - gusSelectionType;
gSelectRegion.iRight = sGridX + gusSelectionType;
ValidateSelectionRegionBoundaries();
fValidCursor = TRUE;
break;
case LINESELECTION:
fValidCursor = HandleAreaSelection();
ForceAreaSelectionWidth();
ValidateSelectionRegionBoundaries();
break;
case AREASELECTION:
fValidCursor = HandleAreaSelection();
break;
}
}
//Draw all of the area cursors here.
if( fValidCursor )
{
if( gfRoofPlacement || iDrawMode == DRAW_MODE_ENEMY || iDrawMode == DRAW_MODE_CREATURE ||
iDrawMode == DRAW_MODE_REBEL || iDrawMode == DRAW_MODE_CIVILIAN ||
iDrawMode == DRAW_MODE_SCHEDULEACTION )
{
iMapIndex = gSelectRegion.iTop * WORLD_COLS + gSelectRegion.iLeft;
if( !IsLocationSittable( iMapIndex, gfRoofPlacement ) && iDrawMode != DRAW_MODE_SCHEDULEACTION ||
!IsLocationSittableExcludingPeople( iMapIndex, gfRoofPlacement ) && iDrawMode == DRAW_MODE_SCHEDULEACTION )
{
if( sBadMarker != iMapIndex )
{
RemoveBadMarker();
if( gfRoofPlacement && FlatRoofAboveGridNo( iMapIndex ) )
{
AddTopmostToTail( iMapIndex + ROOF_OFFSET, BADMARKER1 );
sBadMarker = iMapIndex + ROOF_OFFSET;
gfUsingOffset = TRUE;
}
else
{
AddTopmostToTail( (iMapIndex), BADMARKER1 );
sBadMarker = iMapIndex;
}
}
}
else
{
RemoveBadMarker();
if( gfRoofPlacement && FlatRoofAboveGridNo( iMapIndex ) )
{
AddTopmostToTail( iMapIndex + ROOF_OFFSET, FIRSTPOINTERS5 );
gfUsingOffset = TRUE;
}
else
AddTopmostToTail( iMapIndex, FIRSTPOINTERS1 );
}
}
else for( y = gSelectRegion.iTop; y <= gSelectRegion.iBottom; y++ )
{
for( x = gSelectRegion.iLeft; x <= gSelectRegion.iRight; x++ )
{
iMapIndex = y * WORLD_COLS + x;
AddTopmostToTail( iMapIndex, FIRSTPOINTERS1 );
}
}
}
}
void ForceAreaSelectionWidth()
{
UINT16 gusDecSelWidth;
//If the anchor isn't set, we don't want to force the size yet.
if( !fAnchored )
return;
gusDecSelWidth = gusSelectionWidth - 1;
//compare the region with the anchor and determine if we are going to force size via
//height or width depending on the cursor distance from the anchor.
if( abs( sGridX - gSelectAnchor.iX ) < abs( sGridY - gSelectAnchor.iY ) )
{ //restrict the x axis
if( sGridX < gSelectAnchor.iX )
{ //to the left
gSelectRegion.iLeft = gSelectAnchor.iX - gusDecSelWidth;
gSelectRegion.iRight = gSelectAnchor.iX;
}
else
{ //to the right
gSelectRegion.iLeft = gSelectAnchor.iX;
gSelectRegion.iRight = gSelectAnchor.iX + gusDecSelWidth;
}
}
else
{ //restrict the y axis
if( sGridY < gSelectAnchor.iY )
{ //to the upper
gSelectRegion.iTop = gSelectAnchor.iY - gusDecSelWidth;
gSelectRegion.iBottom = gSelectAnchor.iY;
}
else
{ //to the lower
gSelectRegion.iBottom = gSelectAnchor.iY + gusDecSelWidth;
gSelectRegion.iTop = gSelectAnchor.iY;
}
}
}
BOOLEAN HandleAreaSelection()
{
//When the user releases the left button, then clear and process the area.
if( fAnchored )
{
if( !gfLeftButtonState && !gfCurrentSelectionWithRightButton ||
!gfRightButtonState && gfCurrentSelectionWithRightButton )
{
fAnchored = FALSE;
ProcessAreaSelection( (BOOLEAN)!gfCurrentSelectionWithRightButton );
gfCurrentSelectionWithRightButton = FALSE;
return FALSE;
}
}
//When the user first clicks, anchor the area.
if( !fAnchored )
{
if( gfLeftButtonState || gfRightButtonState && gfAllowRightButtonSelections )
{
if( gfRightButtonState && !gfLeftButtonState )
gfCurrentSelectionWithRightButton = TRUE;
else
gfCurrentSelectionWithRightButton = FALSE;
fAnchored = TRUE;
gSelectAnchor.iX = sGridX;
gSelectAnchor.iY = sGridY;
gSelectRegion.iLeft = gSelectRegion.iRight = sGridX;
gSelectRegion.iTop = gSelectRegion.iBottom = sGridY;
return TRUE;
}
}
//If no anchoring, then we are really dealing with a single cursor,
//until the user clicks and holds the mouse button to anchor the cursor.
if( !fAnchored )
{
gSelectRegion.iLeft = gSelectRegion.iRight = sGridX;
gSelectRegion.iTop = gSelectRegion.iBottom = sGridY;
return TRUE;
}
//Base the area from the anchor to the current mouse position.
if ( sGridX <= gSelectAnchor.iX )
{
gSelectRegion.iLeft = sGridX;
gSelectRegion.iRight = gSelectAnchor.iX;
}
else
{
gSelectRegion.iRight = sGridX;
gSelectRegion.iLeft = gSelectAnchor.iX;
}
if ( sGridY <= gSelectAnchor.iY )
{
gSelectRegion.iTop = sGridY;
gSelectRegion.iBottom = gSelectAnchor.iY;
}
else
{
gSelectRegion.iBottom = sGridY;
gSelectRegion.iTop = gSelectAnchor.iY;
}
return TRUE;
}
//dnl ch43 280909
void ValidateSelectionRegionBoundaries()
{
gSelectRegion.iLeft = max(min(WORLD_COLS-1, gSelectRegion.iLeft), 0);
gSelectRegion.iRight = max(min(WORLD_COLS-1, gSelectRegion.iRight), 0);
gSelectRegion.iTop = max(min(WORLD_ROWS-1, gSelectRegion.iTop), 0);
gSelectRegion.iBottom = max(min(WORLD_ROWS-1, gSelectRegion.iBottom), 0);
}
void EnsureSelectionType()
{
BOOLEAN fPrevBrushEnabledState = gfBrushEnabled;
//At time of writing, the only drawing mode supporting right mouse button
//area selections is the cave drawing mode.
gfAllowRightButtonSelections = ( iDrawMode == DRAW_MODE_CAVES );
//if we are erasing, we have more flexibility with the drawing modes.
if( iDrawMode >= DRAW_MODE_ERASE )
{
//erase modes supporting any cursor mode
gusSavedSelectionType = gusSelectionType;
gusSelectionWidth = gusPreserveSelectionWidth;
gfBrushEnabled = TRUE;
}
else switch( iDrawMode )
{ //regular modes
case DRAW_MODE_SAW_ROOM:
case DRAW_MODE_ROOM:
case DRAW_MODE_CAVES:
gusSavedBuildingSelectionType = gusSelectionType;
gusSelectionWidth = gusPreserveSelectionWidth;
gfBrushEnabled = TRUE;
break;
case DRAW_MODE_SLANTED_ROOF:
gusSelectionType = LINESELECTION;
gusSelectionWidth = 8;
gfBrushEnabled = FALSE;
break;
case DRAW_MODE_EXITGRID:
case DRAW_MODE_ROOMNUM:
case DRAW_MODE_FLOORS:
case DRAW_MODE_GROUND:
case DRAW_MODE_OSTRUCTS:
case DRAW_MODE_OSTRUCTS1:
case DRAW_MODE_OSTRUCTS2:
case DRAW_MODE_DEBRIS:
//supports all modes
gusSavedSelectionType = gusSelectionType;
gusSelectionWidth = gusPreserveSelectionWidth;
gfBrushEnabled = TRUE;
break;
default:
gusSelectionType = SMALLSELECTION;
gusSelectionWidth = gusPreserveSelectionWidth;
gfBrushEnabled = FALSE;
break;
}
if( gfBrushEnabled != fPrevBrushEnabledState )
{
if( gfBrushEnabled )
{
EnableEditorButton( TERRAIN_CYCLE_BRUSHSIZE );
EnableEditorButton( BUILDING_CYCLE_BRUSHSIZE );
EnableEditorButton( MAPINFO_CYCLE_BRUSHSIZE );
}
else
{
DisableEditorButton( TERRAIN_CYCLE_BRUSHSIZE );
DisableEditorButton( BUILDING_CYCLE_BRUSHSIZE );
DisableEditorButton( MAPINFO_CYCLE_BRUSHSIZE );
}
}
}
void DrawBuildingLayout( INT32 iMapIndex )
{
BUILDINGLAYOUTNODE *curr;
INT32 iOffset;
LEVELNODE* pNode;
BOOLEAN fAdd;
iOffset = iMapIndex - gsBuildingLayoutAnchorGridNo;
curr = gpBuildingLayoutList;
while( curr )
{
iMapIndex = curr->sGridNo + iOffset;
if( iMapIndex > 0 && iMapIndex < WORLD_MAX )
{
fAdd = TRUE;
pNode = gpWorldLevelData[ iMapIndex ].pTopmostHead;
while( pNode )
{
if( pNode->usIndex == FIRSTPOINTERS1 )
{
fAdd = FALSE;
break;
}
pNode = pNode->pNext;
}
if( fAdd )
AddTopmostToTail( iMapIndex, FIRSTPOINTERS1 );
}
curr = curr->next;
}
}
void RemoveBuildingLayout()
{
BUILDINGLAYOUTNODE *curr;
INT32 iOffset;
INT32 iMapIndex;
iMapIndex = gSelectRegion.iLeft + gSelectRegion.iTop * WORLD_COLS;
iOffset = iMapIndex - gsBuildingLayoutAnchorGridNo;
curr = gpBuildingLayoutList;
while( curr )
{
iMapIndex = curr->sGridNo + iOffset;
if( iMapIndex > 0 && iMapIndex < WORLD_MAX )
RemoveTopmost( iMapIndex, FIRSTPOINTERS1 );
curr = curr->next;
}
}
#endif