mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
Gas grenades now don't cause direct damage on hit anymore. They will cause damage on start of turn or if you move inside a gas cloud. Of course gas mask still helps (except for creature gas and fire). Every tile moved inside a gas cloud deals a percentage of the normal damage (default 10%). This is set in Item_Settings.ini (DAMAGE_HEALTH_MOVE_EXPLOSIVE_MODIFIER, DAMAGE_BREATH_MOVE_EXPLOSIVE_MODIFIER). Tiles can now have multiple gas effects on them. Unfortunately this is not visualized (yet). Smoke doesn't make a character immune to other gas types anymore. The 25% chance of taking damage while moving through a gas cloud has been removed. Damage is now applied 100% of the time. - New modifiers for explosive damage in Item_Settings.ini (DAMAGE_HEALTH_EXPLOSIVE_MODIFIER, DAMAGE_BREATH_EXPLOSIVE_MODIFIER). These could fully replace EXPLOSIVES_DAMAGE_MODIFIER in Ja2_Options.ini but I left that general modifier untouched for now. - Some compiler warning fixes. git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@6517 3b4a5df2-a311-0410-b5c6-a8a6f20db521
3731 lines
101 KiB
C++
3731 lines
101 KiB
C++
#include "builddefines.h"
|
|
|
|
/****************************************************************************************
|
|
* JA2 Lighting Module
|
|
*
|
|
* Tile-based, ray-casted lighting system.
|
|
*
|
|
* Lights are precalculated into linked lists containing offsets from 0,0, and a light
|
|
* level to add at that tile. Lists are constructed by casting a ray from the origin of
|
|
* the light, and each tile stopped at is stored as a node in the list. To draw the light
|
|
* during runtime, you traverse the list, checking at each tile that it isn't of the type
|
|
* that can obscure light. If it is, you keep traversing the list until you hit a node
|
|
* with a marker LIGHT_NEW_RAY, which means you're back at the origin, and have skipped
|
|
* the remainder of the last ray.
|
|
*
|
|
* Written by Derek Beland, April 14, 1997
|
|
*
|
|
***************************************************************************************/
|
|
#ifdef PRECOMPILEDHEADERS
|
|
#include "TileEngine All.h"
|
|
#else
|
|
#include "math.h"
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
#include "worlddef.h"
|
|
#include "renderworld.h"
|
|
#include "vsurface.h"
|
|
#include "input.h"
|
|
#include "sysutil.h"
|
|
#include "wchar.h"
|
|
#include "video.h"
|
|
#include "vobject_blitters.h"
|
|
#include "debug.h"
|
|
#include "wcheck.h"
|
|
#include "jascreens.h"
|
|
#include "Isometric Utils.h"
|
|
#include "line.h"
|
|
#include "Animation Control.h"
|
|
#include "Animation Data.h"
|
|
#include "Sys Globals.h"
|
|
#include "tiledef.h"
|
|
#include "lighting.h"
|
|
#include "Structure Internals.h"
|
|
#include "structure wrap.h"
|
|
#include "Shade Table Util.h"
|
|
#include "rotting corpses.h"
|
|
#include "Fileman.h"
|
|
#include "environment.h"
|
|
#include "PATHAI.H"
|
|
#endif
|
|
|
|
#define LVL1_L1_PER (50)
|
|
#define LVL1_L2_PER (50)
|
|
|
|
#define LVL2_L1_PER (30)
|
|
#define LVL2_L2_PER (70)
|
|
|
|
|
|
#define LIGHT_TREE_REVEAL 5 // width of rect
|
|
|
|
|
|
// Local-use only prototypes
|
|
|
|
|
|
BOOLEAN LightTileBlocked(INT16 iSrcX, INT16 iSrcY, INT16 iX, INT16 iY);
|
|
BOOLEAN LightDraw(UINT32 uiLightType, INT32 iLight, INT16 iX, INT16 iY, UINT32 uiSprite);
|
|
BOOLEAN LightDelete(INT32 iLight);
|
|
INT32 LightGetFree(void);
|
|
INT32 LinearDistance(INT16 iX1, INT16 iY1, INT16 iX2, INT16 iY2);
|
|
DOUBLE LinearDistanceDouble(INT16 iX1, INT16 iY1, INT16 iX2, INT16 iY2);
|
|
BOOLEAN LightAddTile(UINT32 uiLightType, INT16 iSrcX, INT16 iSrcY, INT16 iX, INT16 iY, UINT8 ubShade, UINT32 uiFlags, BOOLEAN fOnlyWalls );
|
|
BOOLEAN LightSubtractTile(UINT32 uiLightType, INT16 iSrcX, INT16 iSrcY, INT16 iX, INT16 iY, UINT8 ubShade, UINT32 uiFlags, BOOLEAN fOnlyWalls );
|
|
BOOLEAN LightResetTile(INT16 iX, INT16 iY);
|
|
BOOLEAN LightSetTile(INT16 iX, INT16 iY, UINT8 ubShade, UINT32 uiLightType);
|
|
BOOLEAN LightSetNaturalTile(INT16 iX, INT16 iY, UINT8 ubShade);
|
|
UINT16 LightGetLastNode(INT32 iLight);
|
|
BOOLEAN LightAddNode(INT32 iLight, INT16 iHotSpotX, INT16 iHotSpotY, INT16 iX, INT16 iY, UINT8 ubIntensity, UINT16 uiFlags);
|
|
BOOLEAN LightInsertNode(INT32 iLight, UINT16 usLightIns, INT16 iHotSpotX, INT16 iHotSpotY, INT16 iX, INT16 iY, UINT8 ubIntensity, UINT16 uiFlags);
|
|
UINT16 LightFindNextRay(INT32 iLight, UINT16 usIndex);
|
|
BOOLEAN LightCastRay(INT32 iLight, INT16 iStartX, INT16 iStartY, INT16 iEndPointX, INT16 iEndPointY, UINT8 ubStartIntens, UINT8 ubEndIntens);
|
|
BOOLEAN LightGenerateElliptical(INT32 iLight, UINT8 iIntensity, INT16 iA, INT16 iB);
|
|
BOOLEAN LightGenerateBeam(INT32 iLight, UINT8 iIntensity, INT16 iLength, INT16 iRadius, INT16 iDirection);
|
|
BOOLEAN LightCalcRect(INT32 iLight);
|
|
BOOLEAN LightIlluminateWall(INT16 iSourceX, INT16 iSourceY, INT16 iTileX, INT16 iTileY, LEVELNODE *pStruct);
|
|
BOOLEAN LightTileHasWall( INT16 iSrcX, INT16 iSrcY, INT16 iX, INT16 iY );
|
|
|
|
INT32 LightSpriteGetFree(void);
|
|
BOOLEAN LightSpriteDirty(INT32 iLight);
|
|
|
|
// Top node of linked lists, NULL = FREE
|
|
LIGHT_NODE *pLightList[MAX_LIGHT_TEMPLATES];
|
|
UINT16 usTemplateSize[MAX_LIGHT_TEMPLATES];
|
|
UINT16 *pLightRayList[MAX_LIGHT_TEMPLATES];
|
|
UINT16 usRaySize[MAX_LIGHT_TEMPLATES];
|
|
INT16 LightHeight[MAX_LIGHT_TEMPLATES];
|
|
INT16 LightWidth[MAX_LIGHT_TEMPLATES];
|
|
INT16 LightXOffset[MAX_LIGHT_TEMPLATES];
|
|
INT16 LightYOffset[MAX_LIGHT_TEMPLATES];
|
|
INT16 LightMapLeft[MAX_LIGHT_TEMPLATES];
|
|
INT16 LightMapTop[MAX_LIGHT_TEMPLATES];
|
|
INT16 LightMapRight[MAX_LIGHT_TEMPLATES];
|
|
INT16 LightMapBottom[MAX_LIGHT_TEMPLATES];
|
|
STR pLightNames[MAX_LIGHT_TEMPLATES];
|
|
|
|
// Sprite data
|
|
LIGHT_SPRITE LightSprites[MAX_LIGHT_SPRITES];
|
|
|
|
// Lighting system general data
|
|
UINT8 ubAmbientLightLevel=DEFAULT_SHADE_LEVEL;
|
|
UINT8 gubNumLightColors=1;
|
|
|
|
// Externed in Rotting Corpses.c
|
|
SGPPaletteEntry gpLightColors[3]={{0,0,0,0}, {0,0,255,0}, {0,0,0,0}};
|
|
|
|
SGPPaletteEntry gpOrigLights[2]={{0,0,0,0}, {0,0,255,0}};
|
|
|
|
|
|
/*
|
|
UINT16 gusShadeLevels[16][3]={{500, 500, 500}, // green table
|
|
{450, 450, 450}, // bright
|
|
{350, 350, 350},
|
|
{300, 300, 300},
|
|
{255, 255, 255}, // normal
|
|
{227, 227, 227},
|
|
{198, 198, 198},
|
|
{171, 171, 171},
|
|
{143, 143, 143},
|
|
{115, 115, 160}, // darkening
|
|
{87, 87, 176},
|
|
{60, 60, 160},
|
|
{48, 48, 192},
|
|
{36, 36, 208},
|
|
{18, 18, 224},
|
|
{48, 222, 48}};
|
|
*/
|
|
// Externed in Rotting Corpses.c
|
|
//Kris' attempt at blue night lights
|
|
/*
|
|
UINT16 gusShadeLevels[16][3]={{500, 500, 500}, // green table
|
|
{450, 450, 450}, // bright
|
|
{350, 350, 350},
|
|
{300, 300, 300},
|
|
{255, 255, 255}, // normal
|
|
{215, 215, 227},
|
|
{179, 179, 179},
|
|
{149, 149, 149},
|
|
{125, 125, 128},
|
|
{104, 104, 128}, // darkening
|
|
{86, 86, 128},
|
|
{72, 72, 128},
|
|
{60, 60, 128},
|
|
{36, 36, 208},
|
|
{18, 18, 224},
|
|
{48, 222, 48}};
|
|
*/
|
|
|
|
/*
|
|
//Linda's final version
|
|
|
|
UINT16 gusShadeLevels[16][3] =
|
|
{
|
|
500, 500, 500,
|
|
450, 450, 450, //bright
|
|
350, 350, 350,
|
|
300, 300, 300,
|
|
255, 255, 255, //normal
|
|
222, 200, 200,
|
|
174, 167, 167,
|
|
150, 137, 137,
|
|
122, 116, 116, //darkening
|
|
96, 96, 96,
|
|
77, 77, 84,
|
|
58, 58, 69,
|
|
44, 44, 66, //night
|
|
36, 36, 244,
|
|
18, 18, 224,
|
|
48, 222, 48,
|
|
};
|
|
*/
|
|
|
|
// JA2 Gold:
|
|
UINT16 gusShadeLevels[16][3] =
|
|
{
|
|
500, 500, 500,
|
|
450, 450, 450, //bright
|
|
350, 350, 350,
|
|
300, 300, 300,
|
|
255, 255, 255, //normal
|
|
231, 199, 199,
|
|
209, 185, 185,
|
|
187, 171, 171,
|
|
165, 157, 157, //darkening
|
|
143, 143, 143,
|
|
121, 121, 129,
|
|
99, 99, 115 ,
|
|
77, 77, 101, //night
|
|
36, 36, 244,
|
|
18, 18, 224,
|
|
48, 222, 48,
|
|
};
|
|
|
|
//Set this true if you want the shadetables to be loaded from the text file.
|
|
BOOLEAN gfLoadShadeTablesFromTextFile = FALSE;
|
|
|
|
void LoadShadeTablesFromTextFile()
|
|
{
|
|
FILE *fp;
|
|
INT32 i, j;
|
|
INT32 num;
|
|
CHAR8 str[10];
|
|
if( gfLoadShadeTablesFromTextFile )
|
|
{
|
|
fp = fopen( "ShadeTables.txt", "r" );
|
|
Assert( fp );
|
|
if( fp )
|
|
{
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
for( j = 0; j < 3; j++ )
|
|
{
|
|
fscanf( fp, "%s", str );
|
|
sscanf( str, "%d", &num );
|
|
gusShadeLevels[i][j] = (UINT16)num;
|
|
}
|
|
}
|
|
fclose( fp );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Debug variable
|
|
UINT32 gNodesAdded=0;
|
|
|
|
/****************************************************************************************
|
|
InitLightingSystem
|
|
|
|
Initializes the lighting system.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN InitLightingSystem(void)
|
|
{
|
|
UINT32 uiCount;
|
|
|
|
LoadShadeTablesFromTextFile();
|
|
|
|
// init all light lists
|
|
for(uiCount=0; uiCount < MAX_LIGHT_TEMPLATES; uiCount++)
|
|
{
|
|
pLightList[uiCount]=NULL;
|
|
pLightNames[uiCount]=NULL;
|
|
pLightRayList[uiCount]=NULL;
|
|
usTemplateSize[uiCount]=0;
|
|
usRaySize[uiCount]=0;
|
|
}
|
|
|
|
// init all light sprites
|
|
for(uiCount=0; uiCount < MAX_LIGHT_SPRITES; uiCount++)
|
|
memset(&LightSprites[uiCount], 0, sizeof(LIGHT_SPRITE));
|
|
|
|
if(LightLoad("TRANSLUC.LHT")!=0)
|
|
{
|
|
DebugMsg(TOPIC_GAME, DBG_LEVEL_0, String("Failed to load translucency template"));
|
|
return(FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
// THIS MUST BE CALLED ONCE ALL SURFACE VIDEO OBJECTS HAVE BEEN LOADED!
|
|
BOOLEAN SetDefaultWorldLightingColors(void)
|
|
{
|
|
SGPPaletteEntry pPal[2];
|
|
|
|
pPal[0].peRed=0;
|
|
pPal[0].peGreen=0;
|
|
pPal[0].peBlue=0;
|
|
pPal[1].peRed=0;
|
|
pPal[1].peGreen=0;
|
|
pPal[1].peBlue=128;
|
|
|
|
LightSetColors(&pPal[0], 1);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
ShutdownLightingSystem
|
|
|
|
Closes down the lighting system. Any lights that were created are destroyed, and the
|
|
memory attached to them freed up.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN ShutdownLightingSystem(void)
|
|
{
|
|
UINT32 uiCount;
|
|
|
|
// free up all allocated light nodes
|
|
for(uiCount=0; uiCount < MAX_LIGHT_TEMPLATES; uiCount++)
|
|
if(pLightList[uiCount]!=NULL)
|
|
LightDelete(uiCount);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightReset
|
|
|
|
Removes all currently active lights, without undrawing them.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightReset(void)
|
|
{
|
|
UINT32 uiCount;
|
|
|
|
// reset all light lists
|
|
for(uiCount=0; uiCount < MAX_LIGHT_TEMPLATES; uiCount++)
|
|
if(pLightList[uiCount]!=NULL)
|
|
LightDelete(uiCount);
|
|
|
|
// init all light sprites
|
|
for(uiCount=0; uiCount < MAX_LIGHT_SPRITES; uiCount++)
|
|
memset(&LightSprites[uiCount], 0, sizeof(LIGHT_SPRITE));
|
|
|
|
if(LightLoad("TRANSLUC.LHT")!=0)
|
|
{
|
|
DebugMsg(TOPIC_GAME, DBG_LEVEL_0, String("Failed to load translucency template"));
|
|
return(FALSE);
|
|
}
|
|
|
|
// Loop through mercs and reset light value
|
|
for ( uiCount = 0; uiCount < MAX_NUM_SOLDIERS; uiCount++ )
|
|
{
|
|
MercPtrs[ uiCount ]->iLight = -1;
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/****************************************************************************************
|
|
LightCreateTemplateNode
|
|
|
|
Creates a new node, and appends it to the template list. The index into the list is
|
|
returned.
|
|
|
|
***************************************************************************************/
|
|
UINT16 LightCreateTemplateNode(INT32 iLight, INT16 iX, INT16 iY, UINT8 ubLight)
|
|
{
|
|
UINT16 usNumNodes;
|
|
|
|
|
|
// create a new list
|
|
if(pLightList[iLight]==NULL)
|
|
{
|
|
if((pLightList[iLight]= (LIGHT_NODE *) MemAlloc(sizeof(LIGHT_NODE)))==NULL)
|
|
return(65535);
|
|
|
|
pLightList[iLight]->iDX=iX;
|
|
pLightList[iLight]->iDY=iY;
|
|
pLightList[iLight]->ubLight=ubLight;
|
|
pLightList[iLight]->uiFlags=0;
|
|
|
|
usTemplateSize[iLight]=1;
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
usNumNodes=usTemplateSize[iLight];
|
|
pLightList[iLight]= (LIGHT_NODE *) MemRealloc(pLightList[iLight], (usNumNodes+1)*sizeof(LIGHT_NODE));
|
|
(pLightList[iLight]+usNumNodes)->iDX=iX;
|
|
(pLightList[iLight]+usNumNodes)->iDY=iY;
|
|
(pLightList[iLight]+usNumNodes)->ubLight=ubLight;
|
|
(pLightList[iLight]+usNumNodes)->uiFlags=0;
|
|
usTemplateSize[iLight]=usNumNodes+1;
|
|
return(usNumNodes);
|
|
}
|
|
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightAddTemplateNode
|
|
|
|
Adds a node to the template list. If the node does not exist, it creates a new one.
|
|
Returns the index into the list.
|
|
|
|
***************************************************************************************/
|
|
UINT16 LightAddTemplateNode(INT32 iLight, INT16 iX, INT16 iY, UINT8 ubLight)
|
|
{
|
|
UINT16 usCount;
|
|
|
|
for(usCount=0; usCount < usTemplateSize[iLight]; usCount++)
|
|
{
|
|
if(((pLightList[iLight]+usCount)->iDX==iX) &&
|
|
((pLightList[iLight]+usCount)->iDY==iY))
|
|
{
|
|
return(usCount);
|
|
}
|
|
}
|
|
|
|
return(LightCreateTemplateNode(iLight, iX, iY, ubLight));
|
|
}
|
|
|
|
|
|
/****************************************************************************************
|
|
LightAddRayNode
|
|
|
|
Adds a node to the ray casting list.
|
|
|
|
***************************************************************************************/
|
|
UINT16 LightAddRayNode(INT32 iLight, INT16 iX, INT16 iY, UINT8 ubLight, UINT16 usFlags)
|
|
{
|
|
UINT16 usNumNodes;
|
|
|
|
|
|
// create a new list
|
|
if(pLightRayList[iLight]==NULL)
|
|
{
|
|
if((pLightRayList[iLight]= (UINT16 *) MemAlloc(sizeof(UINT16)))==NULL)
|
|
return(65535);
|
|
|
|
*pLightRayList[iLight]=(LightAddTemplateNode(iLight, iX, iY, ubLight) | usFlags);
|
|
|
|
usRaySize[iLight]=1;
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
usNumNodes=usRaySize[iLight];
|
|
pLightRayList[iLight]= (UINT16 *) MemRealloc(pLightRayList[iLight], (usNumNodes+1)*sizeof(UINT16));
|
|
*(pLightRayList[iLight]+usNumNodes)=(LightAddTemplateNode(iLight, iX, iY, ubLight) | usFlags);
|
|
usRaySize[iLight]=usNumNodes+1;
|
|
return(usNumNodes);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightAddRayNode
|
|
|
|
Adds a node to the ray casting list.
|
|
|
|
***************************************************************************************/
|
|
UINT16 LightInsertRayNode(INT32 iLight, UINT16 usIndex, INT16 iX, INT16 iY, UINT8 ubLight, UINT16 usFlags)
|
|
{
|
|
UINT16 usNumNodes;
|
|
|
|
// create a new list
|
|
if(pLightRayList[iLight]==NULL)
|
|
{
|
|
if((pLightRayList[iLight]= (UINT16 *) MemAlloc(sizeof(UINT16)))==NULL)
|
|
return(65535);
|
|
|
|
*pLightRayList[iLight]=(LightAddTemplateNode(iLight, iX, iY, ubLight) | usFlags);
|
|
|
|
usRaySize[iLight]=1;
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
usNumNodes=usRaySize[iLight];
|
|
pLightRayList[iLight]= (UINT16 *) MemRealloc(pLightRayList[iLight], (usNumNodes+1)*sizeof(UINT16));
|
|
|
|
if(usIndex < usRaySize[iLight])
|
|
{
|
|
memmove(pLightRayList[iLight]+usIndex+1,
|
|
pLightRayList[iLight]+usIndex,
|
|
(usRaySize[iLight]-usIndex)*sizeof(UINT16));
|
|
}
|
|
|
|
*(pLightRayList[iLight]+usIndex)=(LightAddTemplateNode(iLight, iX, iY, ubLight) | usFlags);
|
|
usRaySize[iLight]=usNumNodes+1;
|
|
return(usNumNodes);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightTileBlocked
|
|
|
|
Returns TRUE/FALSE if the tile at the specified tile number can block light.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightTileBlocked(INT16 iSrcX, INT16 iSrcY, INT16 iX, INT16 iY)
|
|
{
|
|
UINT16 usTileNo, usSrcTileNo;
|
|
|
|
Assert(gpWorldLevelData!=NULL);
|
|
|
|
usTileNo=MAPROWCOLTOPOS(iY, iX);
|
|
usSrcTileNo=MAPROWCOLTOPOS(iSrcY, iSrcX);
|
|
|
|
if (TileIsOutOfBounds(usTileNo))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if (TileIsOutOfBounds(usSrcTileNo))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if(gpWorldLevelData[ usTileNo ].sHeight > gpWorldLevelData[ usSrcTileNo ].sHeight)
|
|
return(TRUE);
|
|
{
|
|
UINT16 usTileNo;
|
|
LEVELNODE *pStruct;
|
|
|
|
usTileNo=MAPROWCOLTOPOS(iY, iX);
|
|
|
|
pStruct = gpWorldLevelData[ usTileNo ].pStructHead;
|
|
if ( pStruct != NULL )
|
|
{
|
|
// IF WE ARE A WINDOW, DO NOT BLOCK!
|
|
if ( FindStructure( usTileNo, STRUCTURE_WALLNWINDOW ) != NULL )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
}
|
|
|
|
return(LightTileHasWall( iSrcX, iSrcY, iX, iY));
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightTileHasWall
|
|
|
|
Returns TRUE/FALSE if the tile at the specified coordinates contains a wall.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightTileHasWall( INT16 iSrcX, INT16 iSrcY, INT16 iX, INT16 iY)
|
|
{
|
|
//LEVELNODE *pStruct;
|
|
//UINT32 uiType;
|
|
UINT16 usTileNo;
|
|
UINT16 usSrcTileNo;
|
|
INT8 bDirection;
|
|
UINT8 ubTravelCost;
|
|
//INT8 bWallCount = 0;
|
|
//UINT16 usWallOrientation;
|
|
|
|
Assert(gpWorldLevelData!=NULL);
|
|
|
|
usTileNo=MAPROWCOLTOPOS(iY, iX);
|
|
usSrcTileNo=MAPROWCOLTOPOS(iSrcY, iSrcX);
|
|
|
|
if ( usTileNo == usSrcTileNo )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( TileIsOutOfBounds(usTileNo))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( TileIsOutOfBounds(usSrcTileNo))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// Get direction
|
|
//bDirection = atan8( iX, iY, iSrcX, iSrcY );
|
|
bDirection = atan8( iSrcX, iSrcY, iX, iY );
|
|
|
|
#if 0
|
|
if ( usTileNo == 20415 && bDirection == 3 )
|
|
{
|
|
int i = 0;
|
|
}
|
|
#endif
|
|
|
|
ubTravelCost = gubWorldMovementCosts[ usTileNo ][ bDirection ][ 0 ];
|
|
|
|
if ( ubTravelCost == TRAVELCOST_WALL )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
|
|
if ( IS_TRAVELCOST_DOOR( ubTravelCost ) )
|
|
{
|
|
ubTravelCost = DoorTravelCost( NULL, usTileNo, ubTravelCost, TRUE, NULL );
|
|
|
|
if ( ubTravelCost == TRAVELCOST_OBSTACLE || ubTravelCost == TRAVELCOST_DOOR )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
pStruct = gpWorldLevelData[ usTileNo ].pStructHead;
|
|
while ( pStruct != NULL )
|
|
{
|
|
if ( pStruct->usIndex < giNumberOfTiles )
|
|
{
|
|
GetTileType( pStruct->usIndex, &uiType );
|
|
|
|
// ATE: Changed to use last decordations rather than last decal
|
|
// Could maybe check orientation value? Depends on our
|
|
// use of the orientation value flags..
|
|
if((uiType >= FIRSTWALL) && (uiType <=LASTDECORATIONS ))
|
|
{
|
|
GetWallOrientation(pStruct->usIndex, &usWallOrientation);
|
|
|
|
bWallCount++;
|
|
}
|
|
}
|
|
|
|
pStruct=pStruct->pNext;
|
|
}
|
|
|
|
if ( bWallCount )
|
|
{
|
|
// ATE: If TWO or more - assume it's BLOCKED and return TRUE
|
|
if ( bWallCount != 1 )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
|
|
switch(usWallOrientation)
|
|
{
|
|
case INSIDE_TOP_RIGHT:
|
|
case OUTSIDE_TOP_RIGHT:
|
|
return( iSrcX < iX );
|
|
|
|
case INSIDE_TOP_LEFT:
|
|
case OUTSIDE_TOP_LEFT:
|
|
return( iSrcY < iY );
|
|
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightDelete
|
|
|
|
Removes a light template from the list, and frees up the associated node memory.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightDelete(INT32 iLight)
|
|
{
|
|
if(pLightList[iLight]!=NULL)
|
|
{
|
|
if(pLightList[iLight]!=NULL)
|
|
{
|
|
MemFree(pLightList[iLight]);
|
|
pLightList[iLight]=NULL;
|
|
}
|
|
|
|
if(pLightRayList[iLight]!=NULL)
|
|
{
|
|
MemFree(pLightRayList[iLight]);
|
|
pLightRayList[iLight]=NULL;
|
|
}
|
|
|
|
if(pLightNames[iLight]!=NULL)
|
|
{
|
|
MemFree(pLightNames[iLight]);
|
|
pLightNames[iLight]=NULL;
|
|
}
|
|
|
|
usTemplateSize[iLight]=0;
|
|
usRaySize[iLight]=0;
|
|
|
|
return(TRUE);
|
|
}
|
|
else
|
|
return(FALSE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightGetFree
|
|
|
|
Returns an available slot for a new light template. If none are available, (-1) is
|
|
returned.
|
|
|
|
***************************************************************************************/
|
|
INT32 LightGetFree(void)
|
|
{
|
|
UINT32 uiCount;
|
|
|
|
for(uiCount=0; uiCount < MAX_LIGHT_TEMPLATES; uiCount++)
|
|
if(pLightList[uiCount]==NULL)
|
|
return(uiCount);
|
|
|
|
return(-1);
|
|
}
|
|
|
|
|
|
/****************************************************************************************
|
|
LinearDistance
|
|
|
|
Calculates the 2D linear distance between two points.
|
|
|
|
***************************************************************************************/
|
|
INT32 LinearDistance(INT16 iX1, INT16 iY1, INT16 iX2, INT16 iY2)
|
|
{
|
|
INT32 iDx, iDy;
|
|
|
|
iDx=abs(iX1-iX2);
|
|
iDx*=iDx;
|
|
iDy=abs(iY1-iY2);
|
|
iDy*=iDy;
|
|
|
|
return((INT32)sqrt((DOUBLE)(iDx+iDy)));
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LinearDistanceDouble
|
|
|
|
Calculates the 2D linear distance between two points. Returns the result in a DOUBLE
|
|
for greater accuracy.
|
|
|
|
***************************************************************************************/
|
|
DOUBLE LinearDistanceDouble(INT16 iX1, INT16 iY1, INT16 iX2, INT16 iY2)
|
|
{
|
|
INT32 iDx, iDy;
|
|
|
|
iDx=abs(iX1-iX2);
|
|
iDx*=iDx;
|
|
iDy=abs(iY1-iY2);
|
|
iDy*=iDy;
|
|
|
|
return(sqrt((DOUBLE)(iDx+iDy)));
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightTrueLevel
|
|
|
|
Returns the light level at a particular level without fake lights
|
|
|
|
***************************************************************************************/
|
|
UINT8 LightTrueLevel( INT32 sGridNo, INT16 bLevel )
|
|
{
|
|
LEVELNODE * pNode;
|
|
INT32 iSum;
|
|
|
|
if (bLevel == 0)
|
|
{
|
|
pNode = gpWorldLevelData[sGridNo].pLandHead;
|
|
}
|
|
else
|
|
{
|
|
pNode = gpWorldLevelData[sGridNo].pRoofHead;
|
|
}
|
|
|
|
if (pNode == NULL)
|
|
{
|
|
return( ubAmbientLightLevel );
|
|
}
|
|
else
|
|
{
|
|
iSum=pNode->ubNaturalShadeLevel - (pNode->ubSumLights - pNode->ubFakeShadeLevel );
|
|
|
|
iSum=__min(SHADE_MIN, iSum);
|
|
iSum=__max(SHADE_MAX, iSum);
|
|
return( (UINT8) iSum );
|
|
}
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightAddNodeTile
|
|
|
|
Does the addition of light values to individual LEVELNODEs in the world tile list.
|
|
|
|
***************************************************************************************/
|
|
void LightAddTileNode(LEVELNODE *pNode, UINT32 uiLightType, UINT8 ubShadeAdd, BOOLEAN fFake)
|
|
{
|
|
INT16 sSum;
|
|
|
|
pNode->ubSumLights = pNode->ubSumLights + ubShadeAdd;
|
|
if (fFake)
|
|
{
|
|
pNode->ubFakeShadeLevel = pNode->ubFakeShadeLevel + ubShadeAdd;
|
|
}
|
|
|
|
// Now set max
|
|
pNode->ubMaxLights = __max( pNode->ubMaxLights, ubShadeAdd );
|
|
|
|
sSum=pNode->ubNaturalShadeLevel - pNode->ubMaxLights;
|
|
|
|
sSum=__min(SHADE_MIN, sSum);
|
|
sSum=__max(SHADE_MAX, sSum);
|
|
|
|
pNode->ubShadeLevel=(UINT8)sSum;
|
|
}
|
|
|
|
|
|
/****************************************************************************************
|
|
LightAddNodeTile
|
|
|
|
Does the subtraction of light values to individual LEVELNODEs in the world tile list.
|
|
|
|
***************************************************************************************/
|
|
void LightSubtractTileNode(LEVELNODE *pNode, UINT32 uiLightType, UINT8 ubShadeSubtract, BOOLEAN fFake)
|
|
{
|
|
INT16 sSum;
|
|
|
|
if (ubShadeSubtract > pNode->ubSumLights )
|
|
{
|
|
pNode->ubSumLights = 0;
|
|
}
|
|
else
|
|
{
|
|
pNode->ubSumLights = pNode->ubSumLights - ubShadeSubtract;
|
|
}
|
|
if (fFake)
|
|
{
|
|
if (ubShadeSubtract > pNode->ubFakeShadeLevel)
|
|
{
|
|
pNode->ubFakeShadeLevel = 0;
|
|
}
|
|
else
|
|
{
|
|
pNode->ubFakeShadeLevel = pNode->ubFakeShadeLevel - ubShadeSubtract;
|
|
}
|
|
}
|
|
|
|
// Now set max
|
|
pNode->ubMaxLights = __min( pNode->ubMaxLights, pNode->ubSumLights );
|
|
|
|
|
|
sSum=pNode->ubNaturalShadeLevel - pNode->ubMaxLights;
|
|
|
|
sSum=__min(SHADE_MIN, sSum);
|
|
sSum=__max(SHADE_MAX, sSum);
|
|
|
|
pNode->ubShadeLevel=(UINT8)sSum;
|
|
}
|
|
|
|
|
|
/****************************************************************************************
|
|
LightAddTile
|
|
|
|
Adds a specified amount of light to all objects on a given tile.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightAddTile(UINT32 uiLightType, INT16 iSrcX, INT16 iSrcY, INT16 iX, INT16 iY, UINT8 ubShade, UINT32 uiFlags, BOOLEAN fOnlyWalls )
|
|
{
|
|
LEVELNODE *pLand, *pStruct, *pObject, *pMerc, *pRoof, *pOnRoof;
|
|
UINT8 ubShadeAdd;
|
|
INT32 uiTile;
|
|
BOOLEAN fLitWall=FALSE;
|
|
BOOLEAN fFake;
|
|
|
|
Assert(gpWorldLevelData!=NULL);
|
|
|
|
uiTile= MAPROWCOLTOPOS( iY, iX );
|
|
|
|
if ( TileIsOutOfBounds( uiTile ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
gpWorldLevelData[uiTile].uiFlags|=MAPELEMENT_REDRAW;
|
|
|
|
//if((uiFlags&LIGHT_BACKLIGHT) && !(uiFlags&LIGHT_ROOF_ONLY))
|
|
// ubShadeAdd=ubShade*7/10;
|
|
//else
|
|
ubShadeAdd=ubShade;
|
|
|
|
// Lesh: it is not recomended due to problems with roof lighting
|
|
//if ( uiLightType == LIGHT_TYPE_BRIGHT )
|
|
// ubShadeAdd += -6;
|
|
|
|
if (uiFlags&LIGHT_FAKE)
|
|
{
|
|
fFake = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fFake = FALSE;
|
|
}
|
|
|
|
if(!(uiFlags&LIGHT_ROOF_ONLY) || (uiFlags&LIGHT_EVERYTHING))
|
|
{
|
|
pStruct = gpWorldLevelData[uiTile].pStructHead;
|
|
while(pStruct!=NULL)
|
|
{
|
|
if ( pStruct->usIndex < giNumberOfTiles )
|
|
{
|
|
if((gTileDatabase[ pStruct->usIndex ].fType != FIRSTCLIFFHANG) || (uiFlags&LIGHT_EVERYTHING))
|
|
{
|
|
if( (uiFlags&LIGHT_IGNORE_WALLS ) || gfCaves )
|
|
LightAddTileNode(pStruct, uiLightType, ubShadeAdd, FALSE);
|
|
else if(LightIlluminateWall(iSrcX, iSrcY, iX, iY, pStruct))
|
|
{
|
|
if(LightTileHasWall(iSrcX, iSrcY, iX, iY))
|
|
fLitWall=TRUE;
|
|
|
|
// ATE: Limit shade for walls if in caves
|
|
if ( fLitWall && gfCaves )
|
|
{
|
|
LightAddTileNode(pStruct, uiLightType, ( UINT8 ) __min( ubShadeAdd, ( SHADE_MAX + 5 ) ), FALSE);
|
|
}
|
|
else if ( fLitWall )
|
|
{
|
|
LightAddTileNode(pStruct, uiLightType, ubShadeAdd, FALSE);
|
|
}
|
|
else if ( !fOnlyWalls )
|
|
{
|
|
LightAddTileNode(pStruct, uiLightType, ubShadeAdd, FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LightAddTileNode(pStruct, uiLightType, ubShadeAdd, FALSE);
|
|
}
|
|
pStruct=pStruct->pNext;
|
|
}
|
|
|
|
ubShadeAdd=ubShade;
|
|
|
|
if ( !fOnlyWalls )
|
|
{
|
|
pLand = gpWorldLevelData[uiTile].pLandHead;
|
|
|
|
while( pLand )
|
|
{
|
|
if( gfCaves || !fLitWall )
|
|
{
|
|
LightAddTileNode(pLand, uiLightType, ubShadeAdd, fFake);
|
|
}
|
|
pLand=pLand->pNext;
|
|
}
|
|
|
|
pObject = gpWorldLevelData[uiTile].pObjectHead;
|
|
while(pObject!=NULL)
|
|
{
|
|
if ( pObject->usIndex < giNumberOfTiles )
|
|
{
|
|
LightAddTileNode(pObject, uiLightType, ubShadeAdd, FALSE);
|
|
}
|
|
pObject=pObject->pNext;
|
|
}
|
|
|
|
if(uiFlags&LIGHT_BACKLIGHT)
|
|
ubShadeAdd=(UINT8)((UINT16)ubShade*7/10);
|
|
|
|
pMerc = gpWorldLevelData[uiTile].pMercHead;
|
|
while(pMerc!=NULL)
|
|
{
|
|
LightAddTileNode(pMerc, uiLightType, ubShadeAdd, FALSE);
|
|
pMerc=pMerc->pNext;
|
|
}
|
|
}
|
|
}
|
|
|
|
if((uiFlags&LIGHT_ROOF_ONLY) || (uiFlags&LIGHT_EVERYTHING))
|
|
{
|
|
pRoof = gpWorldLevelData[uiTile].pRoofHead;
|
|
while(pRoof!=NULL)
|
|
{
|
|
if ( pRoof->usIndex < giNumberOfTiles )
|
|
{
|
|
LightAddTileNode(pRoof, uiLightType, ubShadeAdd, fFake);
|
|
}
|
|
pRoof=pRoof->pNext;
|
|
}
|
|
|
|
pOnRoof = gpWorldLevelData[uiTile].pOnRoofHead;
|
|
while(pOnRoof!=NULL)
|
|
{
|
|
LightAddTileNode(pOnRoof, uiLightType, ubShadeAdd, FALSE);
|
|
|
|
pOnRoof=pOnRoof->pNext;
|
|
}
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightSubtractTile
|
|
|
|
Subtracts a specified amount of light to a given tile.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightSubtractTile(UINT32 uiLightType, INT16 iSrcX, INT16 iSrcY, INT16 iX, INT16 iY, UINT8 ubShade, UINT32 uiFlags, BOOLEAN fOnlyWalls )
|
|
{
|
|
LEVELNODE *pLand, *pStruct, *pObject, *pMerc, *pRoof, *pOnRoof;
|
|
UINT8 ubShadeSubtract;
|
|
INT32 uiTile;
|
|
BOOLEAN fLitWall=FALSE;
|
|
BOOLEAN fFake; // only passed in to land and roof layers; others get fed FALSE
|
|
|
|
Assert(gpWorldLevelData!=NULL);
|
|
|
|
uiTile= MAPROWCOLTOPOS( iY, iX );
|
|
|
|
if ( TileIsOutOfBounds( uiTile ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
gpWorldLevelData[uiTile].uiFlags|=MAPELEMENT_REDRAW;
|
|
|
|
// if((uiFlags&LIGHT_BACKLIGHT) && !(uiFlags&LIGHT_ROOF_ONLY))
|
|
// ubShadeSubtract=ubShade*7/10;
|
|
// else
|
|
ubShadeSubtract=ubShade;
|
|
|
|
// Lesh: it is not recomended due to problems with roof lighting
|
|
//if ( uiLightType == LIGHT_TYPE_BRIGHT )
|
|
// ubShadeSubtract += -6;
|
|
|
|
if (uiFlags&LIGHT_FAKE)
|
|
{
|
|
fFake = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fFake = FALSE;
|
|
}
|
|
|
|
if(!(uiFlags&LIGHT_ROOF_ONLY) || (uiFlags&LIGHT_EVERYTHING))
|
|
{
|
|
pStruct = gpWorldLevelData[uiTile].pStructHead;
|
|
while(pStruct!=NULL)
|
|
{
|
|
if ( pStruct->usIndex < giNumberOfTiles )
|
|
{
|
|
if((gTileDatabase[ pStruct->usIndex ].fType != FIRSTCLIFFHANG) || (uiFlags&LIGHT_EVERYTHING))
|
|
{
|
|
if( (uiFlags&LIGHT_IGNORE_WALLS ) || gfCaves )
|
|
LightSubtractTileNode(pStruct, uiLightType, ubShadeSubtract, FALSE);
|
|
else if(LightIlluminateWall(iSrcX, iSrcY, iX, iY, pStruct))
|
|
{
|
|
if(LightTileHasWall( iSrcX, iSrcY, iX, iY))
|
|
fLitWall=TRUE;
|
|
|
|
// ATE: Limit shade for walls if in caves
|
|
if ( fLitWall && gfCaves )
|
|
{
|
|
LightSubtractTileNode(pStruct, uiLightType, ( UINT8 ) __max( ( ubShadeSubtract - 5 ), 0 ), FALSE);
|
|
}
|
|
else if ( fLitWall )
|
|
{
|
|
LightSubtractTileNode(pStruct, uiLightType, ubShadeSubtract, FALSE);
|
|
}
|
|
else if ( !fOnlyWalls )
|
|
{
|
|
LightSubtractTileNode(pStruct, uiLightType, ubShadeSubtract, FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LightSubtractTileNode(pStruct, uiLightType, ubShadeSubtract, FALSE);
|
|
}
|
|
pStruct=pStruct->pNext;
|
|
}
|
|
|
|
ubShadeSubtract=ubShade;
|
|
|
|
if ( !fOnlyWalls )
|
|
{
|
|
pLand = gpWorldLevelData[uiTile].pLandHead;
|
|
|
|
while( pLand )
|
|
{
|
|
if( gfCaves || !fLitWall )
|
|
{
|
|
LightSubtractTileNode(pLand, uiLightType, ubShadeSubtract, fFake);
|
|
}
|
|
pLand=pLand->pNext;
|
|
}
|
|
|
|
pObject = gpWorldLevelData[uiTile].pObjectHead;
|
|
while(pObject!=NULL)
|
|
{
|
|
if ( pObject->usIndex < giNumberOfTiles )
|
|
{
|
|
LightSubtractTileNode(pObject, uiLightType, ubShadeSubtract, FALSE);
|
|
}
|
|
pObject=pObject->pNext;
|
|
}
|
|
|
|
if(uiFlags&LIGHT_BACKLIGHT)
|
|
ubShadeSubtract=(UINT8) ((UINT16)ubShade*7/10);
|
|
|
|
pMerc = gpWorldLevelData[uiTile].pMercHead;
|
|
while(pMerc!=NULL)
|
|
{
|
|
LightSubtractTileNode(pMerc, uiLightType, ubShadeSubtract, FALSE);
|
|
pMerc=pMerc->pNext;
|
|
}
|
|
}
|
|
}
|
|
|
|
if((uiFlags&LIGHT_ROOF_ONLY) || (uiFlags&LIGHT_EVERYTHING))
|
|
{
|
|
pRoof = gpWorldLevelData[uiTile].pRoofHead;
|
|
while(pRoof!=NULL)
|
|
{
|
|
if ( pRoof->usIndex < giNumberOfTiles )
|
|
{
|
|
LightSubtractTileNode(pRoof, uiLightType, ubShadeSubtract, fFake);
|
|
}
|
|
pRoof=pRoof->pNext;
|
|
}
|
|
|
|
pOnRoof = gpWorldLevelData[uiTile].pOnRoofHead;
|
|
while(pOnRoof!=NULL)
|
|
{
|
|
if ( pOnRoof->usIndex < giNumberOfTiles )
|
|
{
|
|
LightSubtractTileNode(pOnRoof, uiLightType, ubShadeSubtract, FALSE);
|
|
}
|
|
pOnRoof=pOnRoof->pNext;
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightSetNaturalTileNode
|
|
|
|
Sets the natural light level (as well as the current) on individual LEVELNODEs.
|
|
|
|
***************************************************************************************/
|
|
void LightSetNaturalTileNode(LEVELNODE *pNode, UINT8 ubShade)
|
|
{
|
|
Assert(pNode!=NULL);
|
|
|
|
pNode->ubSumLights=0;
|
|
pNode->ubMaxLights=0;
|
|
pNode->ubNaturalShadeLevel = ubShade;
|
|
pNode->ubShadeLevel = ubShade;
|
|
//LightAddTileNode(pNode, 0, (INT16)(SHADE_MIN-ubShade));
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightSetNaturalTile
|
|
|
|
Sets the natural light value of all objects on a given tile to the specified value.
|
|
This is the light value a tile has with no artificial lighting affecting it.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightSetNaturalTile(INT16 iX, INT16 iY, UINT8 ubShade)
|
|
{
|
|
LEVELNODE *pLand, *pStruct, *pObject, *pRoof, *pOnRoof, *pTopmost, *pMerc;
|
|
UINT32 uiIndex;
|
|
|
|
CHECKF(gpWorldLevelData!=NULL);
|
|
|
|
uiIndex = MAPROWCOLTOPOS( iY, iX );
|
|
|
|
Assert(uiIndex!=0xffffffff);
|
|
|
|
ubShade=__max(SHADE_MAX, ubShade);
|
|
ubShade=__min(SHADE_MIN, ubShade);
|
|
|
|
pLand = gpWorldLevelData[ uiIndex ].pLandHead;
|
|
|
|
while(pLand!=NULL)
|
|
{
|
|
LightSetNaturalTileNode(pLand, ubShade);
|
|
pLand=pLand->pNext;
|
|
}
|
|
|
|
pStruct = gpWorldLevelData[ uiIndex ].pStructHead;
|
|
|
|
while(pStruct!=NULL)
|
|
{
|
|
LightSetNaturalTileNode(pStruct, ubShade);
|
|
pStruct=pStruct->pNext;
|
|
}
|
|
|
|
pObject = gpWorldLevelData[ uiIndex ].pObjectHead;
|
|
while(pObject!=NULL)
|
|
{
|
|
LightSetNaturalTileNode(pObject, ubShade);
|
|
pObject=pObject->pNext;
|
|
}
|
|
|
|
pRoof = gpWorldLevelData[ uiIndex ].pRoofHead;
|
|
while(pRoof!=NULL)
|
|
{
|
|
LightSetNaturalTileNode(pRoof, ubShade);
|
|
pRoof=pRoof->pNext;
|
|
}
|
|
|
|
pOnRoof = gpWorldLevelData[ uiIndex ].pOnRoofHead;
|
|
while(pOnRoof!=NULL)
|
|
{
|
|
LightSetNaturalTileNode(pOnRoof, ubShade);
|
|
pOnRoof=pOnRoof->pNext;
|
|
}
|
|
|
|
pTopmost = gpWorldLevelData[ uiIndex ].pTopmostHead;
|
|
while(pTopmost!=NULL)
|
|
{
|
|
LightSetNaturalTileNode(pTopmost, ubShade);
|
|
pTopmost=pTopmost->pNext;
|
|
}
|
|
|
|
pMerc = gpWorldLevelData[ uiIndex ].pMercHead;
|
|
while(pMerc!=NULL)
|
|
{
|
|
LightSetNaturalTileNode(pMerc, ubShade);
|
|
pMerc=pMerc->pNext;
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightResetTileNode
|
|
|
|
Resets the light level of individual LEVELNODEs to the value contained in the
|
|
natural light level.
|
|
|
|
***************************************************************************************/
|
|
void LightResetTileNode(LEVELNODE *pNode)
|
|
{
|
|
pNode->ubSumLights=0;
|
|
pNode->ubMaxLights=0;
|
|
pNode->ubShadeLevel = pNode->ubNaturalShadeLevel;
|
|
pNode->ubFakeShadeLevel = 0;
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightResetTile
|
|
|
|
Resets the light values of all objects on a given tile to the "natural" light level
|
|
for that tile.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightResetTile(INT16 iX, INT16 iY)
|
|
{
|
|
LEVELNODE *pLand, *pStruct, *pObject, *pRoof, *pOnRoof, *pTopmost, *pMerc;
|
|
UINT32 uiTile;
|
|
|
|
CHECKF(gpWorldLevelData!=NULL);
|
|
|
|
uiTile = MAPROWCOLTOPOS( iY, iX );
|
|
|
|
CHECKF(uiTile!=0xffffffff);
|
|
|
|
pLand = gpWorldLevelData[uiTile].pLandHead;
|
|
|
|
while(pLand!=NULL)
|
|
{
|
|
LightResetTileNode(pLand);
|
|
pLand=pLand->pNext;
|
|
}
|
|
|
|
pStruct = gpWorldLevelData[ uiTile ].pStructHead;
|
|
|
|
while(pStruct!=NULL)
|
|
{
|
|
LightResetTileNode(pStruct);
|
|
pStruct=pStruct->pNext;
|
|
}
|
|
|
|
pObject = gpWorldLevelData[ uiTile ].pObjectHead;
|
|
while(pObject!=NULL)
|
|
{
|
|
LightResetTileNode(pObject);
|
|
pObject=pObject->pNext;
|
|
}
|
|
|
|
pRoof = gpWorldLevelData[ uiTile ].pRoofHead;
|
|
while(pRoof!=NULL)
|
|
{
|
|
LightResetTileNode(pRoof);
|
|
pRoof=pRoof->pNext;
|
|
}
|
|
|
|
pOnRoof = gpWorldLevelData[ uiTile ].pOnRoofHead;
|
|
while(pOnRoof!=NULL)
|
|
{
|
|
LightResetTileNode(pOnRoof);
|
|
pOnRoof=pOnRoof->pNext;
|
|
}
|
|
|
|
pTopmost = gpWorldLevelData[ uiTile ].pTopmostHead;
|
|
while(pTopmost!=NULL)
|
|
{
|
|
LightResetTileNode(pTopmost);
|
|
pTopmost=pTopmost->pNext;
|
|
}
|
|
|
|
pMerc = gpWorldLevelData[ uiTile ].pMercHead;
|
|
while(pMerc!=NULL)
|
|
{
|
|
LightResetTileNode(pMerc);
|
|
pMerc=pMerc->pNext;
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightResetAllTiles
|
|
|
|
Resets all tiles on the map to their baseline values.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightResetAllTiles(void)
|
|
{
|
|
INT16 iCountY, iCountX;
|
|
|
|
for(iCountY=0; iCountY < WORLD_ROWS; iCountY++)
|
|
for(iCountX=0; iCountX < WORLD_COLS; iCountX++)
|
|
LightResetTile(iCountX, iCountY);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************************
|
|
LightSetTile
|
|
|
|
Sets the current light value of all objects on a given tile to the specified value.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightSetTile(INT16 iX, INT16 iY, UINT8 ubShade, UINT32 uiLightType)
|
|
{
|
|
/*LEVELNODE *pLand, *pStruct, *pObject, *pRoof, *pOnRoof, *pTopmost, *pFog;
|
|
UINT32 uiIndex;
|
|
|
|
ubShade=__max(SHADE_MAX, ubShade);
|
|
ubShade=__min(SHADE_MIN, ubShade);
|
|
|
|
uiIndex = MAPROWCOLTOPOS( iY, iX );
|
|
pLand = gpWorldLevelData[ uiIndex ].pLandHead;
|
|
|
|
while(pLand!=NULL)
|
|
{
|
|
pLand->sSumLights[uiLightType]= ubShade-SHADE_MAX;
|
|
pLand->ubShadeLevel = ubShade;
|
|
pLand=pLand->pNext;
|
|
}
|
|
|
|
pStruct = gpWorldLevelData[ uiIndex ].pStructHead;
|
|
|
|
while(pStruct!=NULL)
|
|
{
|
|
pStruct->sSumLights[uiLightType]= ubShade-SHADE_MAX;
|
|
pStruct->ubShadeLevel = ubShade;
|
|
pStruct=pStruct->pNext;
|
|
}
|
|
|
|
pObject = gpWorldLevelData[ uiIndex ].pObjectHead;
|
|
while(pObject!=NULL)
|
|
{
|
|
pObject->sSumLights[uiLightType]= ubShade-SHADE_MAX;
|
|
pObject->ubShadeLevel = ubShade;
|
|
pObject=pObject->pNext;
|
|
}
|
|
|
|
pRoof = gpWorldLevelData[ uiIndex ].pRoofHead;
|
|
while(pRoof!=NULL)
|
|
{
|
|
pRoof->sSumLights[uiLightType]= ubShade-SHADE_MAX;
|
|
pRoof->ubShadeLevel = ubShade;
|
|
pRoof=pRoof->pNext;
|
|
}
|
|
|
|
pOnRoof = gpWorldLevelData[ uiIndex ].pOnRoofHead;
|
|
while(pOnRoof!=NULL)
|
|
{
|
|
pOnRoof->sSumLights[uiLightType]= ubShade-SHADE_MAX;
|
|
pOnRoof->ubShadeLevel = ubShade;
|
|
pOnRoof=pOnRoof->pNext;
|
|
}
|
|
|
|
pFog = gpWorldLevelData[ uiIndex ].pFogHead;
|
|
while(pFog!=NULL)
|
|
{
|
|
pFog->sSumLights[uiLightType]= ubShade-SHADE_MAX;
|
|
pFog->ubShadeLevel = ubShade;
|
|
pFog=pFog->pNext;
|
|
}
|
|
|
|
pTopmost = gpWorldLevelData[ uiIndex ].pTopmostHead;
|
|
while(pTopmost!=NULL)
|
|
{
|
|
pTopmost->sSumLights[uiLightType]= ubShade-SHADE_MAX;
|
|
pTopmost->ubShadeLevel = ubShade;
|
|
pTopmost=pTopmost->pNext;
|
|
} */
|
|
return(TRUE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightGetLastNode
|
|
|
|
Returns a pointer to the last node in a light list. If the list is empty, NULL is
|
|
returned.
|
|
|
|
***************************************************************************************/
|
|
UINT16 LightGetLastNode(INT32 iLight)
|
|
{
|
|
return(usRaySize[iLight]);
|
|
}
|
|
|
|
|
|
/****************************************************************************************
|
|
LightAddNode
|
|
|
|
Creates a new node, and adds it to the end of a light list.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightAddNode(INT32 iLight, INT16 iHotSpotX, INT16 iHotSpotY, INT16 iX, INT16 iY, UINT8 ubIntensity, UINT16 uiFlags)
|
|
{
|
|
DOUBLE dDistance;
|
|
UINT8 ubShade;
|
|
INT32 iLightDecay;
|
|
|
|
dDistance=LinearDistanceDouble(iX, iY, iHotSpotX, iHotSpotY);
|
|
dDistance/=DISTANCE_SCALE;
|
|
|
|
iLightDecay=(INT32)(dDistance*LIGHT_DECAY);
|
|
|
|
if((iLightDecay >= (INT32)ubIntensity))
|
|
ubShade=0;
|
|
else
|
|
ubShade=ubIntensity-(UINT8)iLightDecay;
|
|
|
|
iX/=DISTANCE_SCALE;
|
|
iY/=DISTANCE_SCALE;
|
|
|
|
LightAddRayNode(iLight, iX, iY, ubShade, uiFlags);
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/****************************************************************************************
|
|
LightInsertNode
|
|
|
|
Creates a new node, and inserts it after the specified node.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightInsertNode(INT32 iLight, UINT16 usLightIns, INT16 iHotSpotX, INT16 iHotSpotY, INT16 iX, INT16 iY, UINT8 ubIntensity, UINT16 uiFlags)
|
|
{
|
|
DOUBLE dDistance;
|
|
UINT8 ubShade;
|
|
INT32 iLightDecay;
|
|
|
|
dDistance=LinearDistanceDouble(iX, iY, iHotSpotX, iHotSpotY);
|
|
dDistance/=DISTANCE_SCALE;
|
|
|
|
iLightDecay=(INT32)(dDistance*LIGHT_DECAY);
|
|
|
|
if((iLightDecay >= (INT32)ubIntensity))
|
|
ubShade=0;
|
|
else
|
|
ubShade=ubIntensity-(UINT8)iLightDecay;
|
|
|
|
iX/=DISTANCE_SCALE;
|
|
iY/=DISTANCE_SCALE;
|
|
|
|
LightInsertRayNode(iLight, usLightIns, iX, iY, ubShade, uiFlags);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightFindNextRay
|
|
|
|
Traverses the linked list until a node with the LIGHT_NEW_RAY marker, and returns
|
|
the pointer. If the end of list is reached, NULL is returned.
|
|
|
|
***************************************************************************************/
|
|
UINT16 LightFindNextRay(INT32 iLight, UINT16 usIndex)
|
|
{
|
|
UINT16 usNodeIndex;
|
|
|
|
usNodeIndex=usIndex;
|
|
while((usNodeIndex < usRaySize[iLight]) && !(*(pLightRayList[iLight]+usNodeIndex)&LIGHT_NEW_RAY))
|
|
usNodeIndex++;
|
|
|
|
return(usNodeIndex);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightCastRay
|
|
|
|
Casts a ray from an origin to an end point, creating nodes and adding them to the
|
|
light list.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightCastRay(INT32 iLight, INT16 iStartX, INT16 iStartY, INT16 iEndPointX, INT16 iEndPointY, UINT8 ubStartIntens, UINT8 ubEndIntens)
|
|
{
|
|
INT16 AdjUp, AdjDown, ErrorTerm, XAdvance, XDelta, YDelta;
|
|
INT32 WholeStep, InitialPixelCount, FinalPixelCount, i, j, RunLength;
|
|
INT16 iXPos, iYPos, iEndY, iEndX;
|
|
UINT16 usCurNode=0, usFlags=0;
|
|
BOOLEAN fInsertNodes=FALSE;
|
|
|
|
if((iEndPointX > 0) && (iEndPointY > 0))
|
|
usFlags=LIGHT_BACKLIGHT;
|
|
|
|
/* We'll always draw top to bottom, to reduce the number of cases we have to
|
|
handle, and to make lines between the same endpoints draw the same pixels */
|
|
if (iStartY > iEndPointY)
|
|
{
|
|
iXPos=iEndPointX;
|
|
iEndX=iStartX;
|
|
iYPos=iEndPointY;
|
|
iEndY=iStartY;
|
|
fInsertNodes=TRUE;
|
|
}
|
|
else
|
|
{
|
|
iXPos=iStartX;
|
|
iEndX=iEndPointX;
|
|
iYPos=iStartY;
|
|
iEndY=iEndPointY;
|
|
}
|
|
|
|
/* Figure out whether we're going left or right, and how far we're
|
|
going horizontally */
|
|
if ((XDelta = (iEndX - iXPos)) < 0)
|
|
{
|
|
XAdvance = -1;
|
|
XDelta = -XDelta;
|
|
}
|
|
else
|
|
{
|
|
XAdvance = 1;
|
|
}
|
|
/* Figure out how far we're going vertically */
|
|
YDelta = iEndY - iYPos;
|
|
|
|
// Check for 0 length ray
|
|
if((XDelta==0) && (YDelta==0))
|
|
return(FALSE);
|
|
|
|
//DebugMsg(TOPIC_GAME, DBG_LEVEL_0, String("Drawing (%d,%d) to (%d,%d)", iXPos, iYPos, iEndX, iEndY));
|
|
LightAddNode(iLight, 32767, 32767, 32767, 32767, 0, LIGHT_NEW_RAY);
|
|
if(fInsertNodes)
|
|
usCurNode=LightGetLastNode(iLight);
|
|
|
|
|
|
/* Special-case horizontal, vertical, and diagonal lines, for speed
|
|
and to avoid nasty boundary conditions and division by 0 */
|
|
if (XDelta == 0)
|
|
{
|
|
/* Vertical line */
|
|
if(fInsertNodes)
|
|
{
|
|
for (i=0; i<=YDelta; i++)
|
|
{
|
|
LightInsertNode(iLight, usCurNode, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
iYPos++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i<=YDelta; i++)
|
|
{
|
|
LightAddNode(iLight, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
iYPos++;
|
|
}
|
|
}
|
|
return(TRUE);
|
|
}
|
|
if (YDelta == 0)
|
|
{
|
|
/* Horizontal line */
|
|
if(fInsertNodes)
|
|
{
|
|
for (i=0; i<=XDelta; i++)
|
|
{
|
|
LightInsertNode(iLight, usCurNode, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
iXPos=iXPos+XAdvance;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i<=XDelta; i++)
|
|
{
|
|
LightAddNode(iLight, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
iXPos=iXPos+XAdvance;
|
|
}
|
|
}
|
|
return(TRUE);
|
|
}
|
|
if (XDelta == YDelta)
|
|
{
|
|
/* Diagonal line */
|
|
if(fInsertNodes)
|
|
{
|
|
for (i=0; i<=XDelta; i++)
|
|
{
|
|
LightInsertNode(iLight, usCurNode, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
iXPos+=XAdvance;
|
|
iYPos++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i<=XDelta; i++)
|
|
{
|
|
LightAddNode(iLight, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
iXPos+=XAdvance;
|
|
iYPos++;
|
|
}
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
/* Determine whether the line is X or Y major, and handle accordingly */
|
|
if (XDelta >= YDelta)
|
|
{
|
|
/* X major line */
|
|
/* Minimum # of pixels in a run in this line */
|
|
WholeStep = XDelta / YDelta;
|
|
|
|
/* Error term adjust each time Y steps by 1; used to tell when one
|
|
extra pixel should be drawn as part of a run, to account for
|
|
fractional steps along the X axis per 1-pixel steps along Y */
|
|
AdjUp = (XDelta % YDelta) * 2;
|
|
|
|
/* Error term adjust when the error term turns over, used to factor
|
|
out the X step made at that time */
|
|
AdjDown = YDelta * 2;
|
|
|
|
/* Initial error term; reflects an initial step of 0.5 along the Y
|
|
axis */
|
|
ErrorTerm = (XDelta % YDelta) - (YDelta * 2);
|
|
|
|
/* The initial and last runs are partial, because Y advances only 0.5
|
|
for these runs, rather than 1. Divide one full run, plus the
|
|
initial pixel, between the initial and last runs */
|
|
InitialPixelCount = (WholeStep / 2) + 1;
|
|
FinalPixelCount = InitialPixelCount;
|
|
|
|
/* If the basic run length is even and there's no fractional
|
|
advance, we have one pixel that could go to either the initial
|
|
or last partial run, which we'll arbitrarily allocate to the
|
|
last run */
|
|
if ((AdjUp == 0) && ((WholeStep & 0x01) == 0))
|
|
{
|
|
InitialPixelCount--;
|
|
}
|
|
/* If there're an odd number of pixels per run, we have 1 pixel that can't
|
|
be allocated to either the initial or last partial run, so we'll add 0.5
|
|
to error term so this pixel will be handled by the normal full-run loop */
|
|
if ((WholeStep & 0x01) != 0)
|
|
{
|
|
ErrorTerm += YDelta;
|
|
}
|
|
/* Draw the first, partial run of pixels */
|
|
//DrawHorizontalRun(&ScreenPtr, XAdvance, InitialPixelCount, Color);
|
|
if(fInsertNodes)
|
|
{
|
|
for (i=0; i<InitialPixelCount; i++)
|
|
{
|
|
LightInsertNode(iLight, usCurNode, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
iXPos+=XAdvance;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i<InitialPixelCount; i++)
|
|
{
|
|
LightAddNode(iLight, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
iXPos+=XAdvance;
|
|
}
|
|
}
|
|
iYPos++;
|
|
|
|
/* Draw all full runs */
|
|
for (j=0; j<(YDelta-1); j++)
|
|
{
|
|
RunLength = WholeStep; /* run is at least this long */
|
|
/* Advance the error term and add an extra pixel if the error
|
|
term so indicates */
|
|
if ((ErrorTerm += AdjUp) > 0)
|
|
{
|
|
RunLength++;
|
|
ErrorTerm -= AdjDown; /* reset the error term */
|
|
}
|
|
/* Draw this scan line's run */
|
|
//DrawHorizontalRun(&ScreenPtr, XAdvance, RunLength, Color);
|
|
if(fInsertNodes)
|
|
{
|
|
for (i=0; i<RunLength; i++)
|
|
{
|
|
LightInsertNode(iLight, usCurNode, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
iXPos+=XAdvance;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i<RunLength; i++)
|
|
{
|
|
LightAddNode(iLight, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
iXPos+=XAdvance;
|
|
}
|
|
}
|
|
iYPos++;
|
|
}
|
|
/* Draw the final run of pixels */
|
|
//DrawHorizontalRun(&ScreenPtr, XAdvance, FinalPixelCount, Color);
|
|
if(fInsertNodes)
|
|
{
|
|
for (i=0; i<FinalPixelCount; i++)
|
|
{
|
|
LightInsertNode(iLight, usCurNode, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
iXPos+=XAdvance;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i<FinalPixelCount; i++)
|
|
{
|
|
LightAddNode(iLight, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
iXPos+=XAdvance;
|
|
}
|
|
}
|
|
iYPos++;
|
|
}
|
|
else
|
|
{
|
|
/* Y major line */
|
|
|
|
/* Minimum # of pixels in a run in this line */
|
|
WholeStep = YDelta / XDelta;
|
|
|
|
/* Error term adjust each time X steps by 1; used to tell when 1 extra
|
|
pixel should be drawn as part of a run, to account for
|
|
fractional steps along the Y axis per 1-pixel steps along X */
|
|
AdjUp = (YDelta % XDelta) * 2;
|
|
|
|
/* Error term adjust when the error term turns over, used to factor
|
|
out the Y step made at that time */
|
|
AdjDown = XDelta * 2;
|
|
|
|
/* Initial error term; reflects initial step of 0.5 along the X axis */
|
|
ErrorTerm = (YDelta % XDelta) - (XDelta * 2);
|
|
|
|
/* The initial and last runs are partial, because X advances only 0.5
|
|
for these runs, rather than 1. Divide one full run, plus the
|
|
initial pixel, between the initial and last runs */
|
|
InitialPixelCount = (WholeStep / 2) + 1;
|
|
FinalPixelCount = InitialPixelCount;
|
|
|
|
/* If the basic run length is even and there's no fractional advance, we
|
|
have 1 pixel that could go to either the initial or last partial run,
|
|
which we'll arbitrarily allocate to the last run */
|
|
if ((AdjUp == 0) && ((WholeStep & 0x01) == 0))
|
|
{
|
|
InitialPixelCount--;
|
|
}
|
|
/* If there are an odd number of pixels per run, we have one pixel
|
|
that can't be allocated to either the initial or last partial
|
|
run, so we'll add 0.5 to the error term so this pixel will be
|
|
handled by the normal full-run loop */
|
|
if ((WholeStep & 0x01) != 0)
|
|
{
|
|
ErrorTerm += XDelta;
|
|
}
|
|
/* Draw the first, partial run of pixels */
|
|
if(fInsertNodes)
|
|
{
|
|
for (i=0; i<InitialPixelCount; i++)
|
|
{
|
|
LightInsertNode(iLight, usCurNode, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
iYPos++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i<InitialPixelCount; i++)
|
|
{
|
|
LightAddNode(iLight, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
iYPos++;
|
|
}
|
|
}
|
|
iXPos+=XAdvance;
|
|
//DrawVerticalRun(&ScreenPtr, XAdvance, InitialPixelCount, Color);
|
|
|
|
/* Draw all full runs */
|
|
for (j=0; j<(XDelta-1); j++)
|
|
{
|
|
RunLength = WholeStep; /* run is at least this long */
|
|
/* Advance the error term and add an extra pixel if the error
|
|
term so indicates */
|
|
if ((ErrorTerm += AdjUp) > 0)
|
|
{
|
|
RunLength++;
|
|
ErrorTerm -= AdjDown; /* reset the error term */
|
|
}
|
|
/* Draw this scan line's run */
|
|
//DrawVerticalRun(&ScreenPtr, XAdvance, RunLength, Color);
|
|
if(fInsertNodes)
|
|
{
|
|
for (i=0; i<RunLength; i++)
|
|
{
|
|
LightInsertNode(iLight, usCurNode, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
iYPos++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i<RunLength; i++)
|
|
{
|
|
LightAddNode(iLight, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
iYPos++;
|
|
}
|
|
}
|
|
iXPos+=XAdvance;
|
|
}
|
|
/* Draw the final run of pixels */
|
|
//DrawVerticalRun(&ScreenPtr, XAdvance, FinalPixelCount, Color);
|
|
if(fInsertNodes)
|
|
{
|
|
for (i=0; i<FinalPixelCount; i++)
|
|
{
|
|
LightInsertNode(iLight, usCurNode, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
iYPos++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i<FinalPixelCount; i++)
|
|
{
|
|
LightAddNode(iLight, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
iYPos++;
|
|
}
|
|
}
|
|
iXPos+=XAdvance;
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightGenerateElliptical
|
|
|
|
Creates an elliptical light, taking two radii.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightGenerateElliptical(INT32 iLight, UINT8 iIntensity, INT16 iA, INT16 iB)
|
|
{
|
|
INT16 iX, iY;
|
|
INT32 WorkingX, WorkingY;
|
|
DOUBLE ASquared;
|
|
DOUBLE BSquared;
|
|
DOUBLE Temp;
|
|
|
|
iX=0;
|
|
iY=0;
|
|
ASquared = (DOUBLE) iA * iA;
|
|
BSquared = (DOUBLE) iB * iB;
|
|
|
|
/* Draw the four symmetric arcs for which X advances faster (that is,
|
|
for which X is the major axis) */
|
|
/* Draw the initial top & bottom points */
|
|
LightCastRay(iLight, iX, iY, (INT16)iX, (INT16)(iY+iB), iIntensity, 1);
|
|
LightCastRay(iLight, iX, iY, (INT16)iX, (INT16)(iY-iB), iIntensity, 1);
|
|
|
|
/* Draw the four arcs */
|
|
for (WorkingX = 0; ; )
|
|
{
|
|
/* Advance one pixel along the X axis */
|
|
WorkingX++;
|
|
|
|
/* Calculate the corresponding point along the Y axis. Guard
|
|
against floating-point roundoff making the intermediate term
|
|
less than 0 */
|
|
Temp = BSquared - (BSquared * WorkingX * WorkingX / ASquared);
|
|
|
|
if(Temp >= 0)
|
|
WorkingY= (INT32)(sqrt(Temp)+0.5);
|
|
else
|
|
WorkingY=0;
|
|
|
|
/* Stop if X is no longer the major axis (the arc has passed the
|
|
45-degree point) */
|
|
if(((DOUBLE)WorkingY/BSquared) <= ((DOUBLE)WorkingX/ASquared))
|
|
break;
|
|
|
|
/* Draw the 4 symmetries of the current point */
|
|
LightCastRay(iLight, iX, iY, (INT16)(iX+WorkingX), (INT16)(iY-WorkingY), iIntensity, 1);
|
|
LightCastRay(iLight, iX, iY, (INT16)(iX-WorkingX), (INT16)(iY-WorkingY), iIntensity, 1);
|
|
LightCastRay(iLight, iX, iY, (INT16)(iX+WorkingX), (INT16)(iY+WorkingY), iIntensity, 1);
|
|
LightCastRay(iLight, iX, iY, (INT16)(iX-WorkingX), (INT16)(iY+WorkingY), iIntensity, 1);
|
|
}
|
|
|
|
/* Draw the four symmetric arcs for which Y advances faster (that is,
|
|
for which Y is the major axis) */
|
|
/* Draw the initial left & right points */
|
|
LightCastRay(iLight, iX, iY, (INT16)(iX+iA), iY, iIntensity, 1);
|
|
LightCastRay(iLight, iX, iY, (INT16)(iX-iA), iY, iIntensity, 1);
|
|
|
|
/* Draw the four arcs */
|
|
for (WorkingY = 0; ; )
|
|
{
|
|
/* Advance one pixel along the Y axis */
|
|
WorkingY++;
|
|
|
|
/* Calculate the corresponding point along the X axis. Guard
|
|
against floating-point roundoff making the intermediate term
|
|
less than 0 */
|
|
Temp = ASquared - (ASquared * WorkingY * WorkingY / BSquared);
|
|
|
|
if(Temp >= 0)
|
|
WorkingX = (INT32)(sqrt(Temp)+0.5);
|
|
else
|
|
WorkingX = 0;
|
|
|
|
/* Stop if Y is no longer the major axis (the arc has passed the
|
|
45-degree point) */
|
|
if (((DOUBLE)WorkingX/ASquared) < ((DOUBLE)WorkingY/BSquared))
|
|
break;
|
|
|
|
/* Draw the 4 symmetries of the current point */
|
|
LightCastRay(iLight, iX, iY, (INT16)(iX+WorkingX), (INT16)(iY-WorkingY), iIntensity, 1);
|
|
LightCastRay(iLight, iX, iY, (INT16)(iX-WorkingX), (INT16)(iY-WorkingY), iIntensity, 1);
|
|
LightCastRay(iLight, iX, iY, (INT16)(iX+WorkingX), (INT16)(iY+WorkingY), iIntensity, 1);
|
|
LightCastRay(iLight, iX, iY, (INT16)(iX-WorkingX), (INT16)(iY+WorkingY), iIntensity, 1);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightGenerateSquare
|
|
|
|
Creates an square light, taking two radii.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightGenerateSquare(INT32 iLight, UINT8 iIntensity, INT16 iA, INT16 iB)
|
|
{
|
|
INT16 iX, iY;
|
|
|
|
for(iX=0-iA; iX <= 0+iA; iX++)
|
|
LightCastRay(iLight, 0, 0, iX, (INT16)(0-iB), iIntensity, 1);
|
|
|
|
for(iX=0-iA; iX <= 0+iA; iX++)
|
|
LightCastRay(iLight, 0, 0, iX, (INT16)(0+iB), iIntensity, 1);
|
|
|
|
for(iY=0-iB; iY <= 0+iB; iY++)
|
|
LightCastRay(iLight, 0, 0, (INT16)(0-iA), iY, iIntensity, 1);
|
|
|
|
for(iY=0-iB; iY <= 0+iB; iY++)
|
|
LightCastRay(iLight, 0, 0, (INT16)(0+iA), iY, iIntensity, 1);
|
|
|
|
|
|
/*for(iY=0-iB; iY <= 0+iB; iY++)
|
|
LightCastRay(iLight, 0, iY, (INT16)(0+iA), iY, iIntensity, 1);
|
|
|
|
for(iY=0+iB; iY >= 0-iB; iY--)
|
|
LightCastRay(iLight, 0, iY, (INT16)(0-iA), iY, iIntensity, 1);
|
|
|
|
for(iX=0-iA; iX <= 0+iA; iX++)
|
|
LightCastRay(iLight, iX, 0, iX, (INT16)(0+iB), iIntensity, 1);
|
|
|
|
for(iX=0+iA; iX >= 0-iA; iX--)
|
|
LightCastRay(iLight, iX, 0, iX, (INT16)(0-iB), iIntensity, 1); */
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightGenerateBeam
|
|
|
|
Creates a directional light.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightGenerateBeam(INT32 iLight, UINT8 iIntensity, INT16 iLength, INT16 iRadius, INT16 iDirection)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightSetBaseLevel
|
|
|
|
Sets the current and natural light settings for all tiles in the world.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightSetBaseLevel(UINT8 iIntensity)
|
|
{
|
|
INT16 iCountY, iCountX;
|
|
UINT32 cnt;
|
|
SOLDIERTYPE *pSoldier;
|
|
|
|
ubAmbientLightLevel=iIntensity;
|
|
|
|
if( !gfEditMode )
|
|
{
|
|
// Loop for all good guys in tactical map and add a light if required
|
|
for ( cnt = 0; cnt < guiNumMercSlots; cnt++ )
|
|
{
|
|
pSoldier = MercSlots[ cnt ];
|
|
|
|
if ( pSoldier != NULL )
|
|
{
|
|
if ( pSoldier->bTeam == gbPlayerNum )
|
|
{
|
|
// Re-create soldier lights
|
|
pSoldier->ReCreateSoldierLight( );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for(iCountY=0; iCountY < WORLD_ROWS; iCountY++)
|
|
for(iCountX=0; iCountX < WORLD_COLS; iCountX++)
|
|
LightSetNaturalTile(iCountX, iCountY, iIntensity);
|
|
|
|
LightSpriteRenderAll();
|
|
|
|
if(iIntensity >= LIGHT_DUSK_CUTOFF)
|
|
RenderSetShadows(FALSE);
|
|
else
|
|
RenderSetShadows(TRUE);
|
|
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightAddBaseLevel
|
|
|
|
Adds a light value to all the tiles in the world
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightAddBaseLevel(UINT32 uiLightType, UINT8 iIntensity)
|
|
{
|
|
INT16 iCountY, iCountX;
|
|
|
|
ubAmbientLightLevel=__max(SHADE_MAX, ubAmbientLightLevel-iIntensity);
|
|
|
|
for(iCountY=0; iCountY < WORLD_ROWS; iCountY++)
|
|
for(iCountX=0; iCountX < WORLD_COLS; iCountX++)
|
|
LightAddTile(uiLightType, iCountX, iCountY, iCountX, iCountY, iIntensity, LIGHT_IGNORE_WALLS|LIGHT_EVERYTHING, FALSE );
|
|
|
|
if(ubAmbientLightLevel >= LIGHT_DUSK_CUTOFF)
|
|
RenderSetShadows(FALSE);
|
|
else
|
|
RenderSetShadows(TRUE);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightSubtractBaseLevel
|
|
|
|
Sets the current and natural light settings for all tiles in the world.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightSubtractBaseLevel(UINT32 uiLightType, UINT8 iIntensity)
|
|
{
|
|
INT16 iCountY, iCountX;
|
|
|
|
ubAmbientLightLevel=__min(SHADE_MIN, ubAmbientLightLevel+iIntensity);
|
|
|
|
for(iCountY=0; iCountY < WORLD_ROWS; iCountY++)
|
|
for(iCountX=0; iCountX < WORLD_COLS; iCountX++)
|
|
LightSubtractTile(uiLightType, iCountX, iCountY, iCountX, iCountY, iIntensity, LIGHT_IGNORE_WALLS|LIGHT_EVERYTHING, FALSE );
|
|
|
|
if(ubAmbientLightLevel >= LIGHT_DUSK_CUTOFF)
|
|
RenderSetShadows(FALSE);
|
|
else
|
|
RenderSetShadows(TRUE);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightCreateOmni
|
|
|
|
Creates a circular light.
|
|
|
|
***************************************************************************************/
|
|
INT32 LightCreateOmni(UINT8 ubIntensity, INT16 iRadius)
|
|
{
|
|
INT32 iLight;
|
|
CHAR8 usName[14];
|
|
|
|
iLight=LightGetFree();
|
|
if(iLight!=(-1))
|
|
{
|
|
LightGenerateElliptical(iLight, ubIntensity, (INT16)(iRadius*DISTANCE_SCALE), (INT16)(iRadius*DISTANCE_SCALE));
|
|
}
|
|
|
|
sprintf(usName, "LTO%d.LHT", iRadius);
|
|
pLightNames[iLight]= (STR) MemAlloc(strlen(usName)+1);
|
|
strcpy(pLightNames[iLight], usName);
|
|
|
|
return(iLight);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightCreateSquare
|
|
|
|
Creates a square light.
|
|
|
|
***************************************************************************************/
|
|
INT32 LightCreateSquare(UINT8 ubIntensity, INT16 iRadius1, INT16 iRadius2)
|
|
{
|
|
INT32 iLight;
|
|
CHAR8 usName[14];
|
|
|
|
iLight=LightGetFree();
|
|
if(iLight!=(-1))
|
|
{
|
|
LightGenerateSquare(iLight, ubIntensity, (INT16)(iRadius1*DISTANCE_SCALE), (INT16)(iRadius2*DISTANCE_SCALE));
|
|
}
|
|
|
|
sprintf(usName, "LTS%d-%d.LHT", iRadius1, iRadius2);
|
|
pLightNames[iLight]= (STR) MemAlloc(strlen(usName)+1);
|
|
strcpy(pLightNames[iLight], usName);
|
|
|
|
return(iLight);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightCreateOval
|
|
|
|
Creates an elliptical light.
|
|
|
|
***************************************************************************************/
|
|
INT32 LightCreateElliptical(UINT8 ubIntensity, INT16 iRadius1, INT16 iRadius2)
|
|
{
|
|
INT32 iLight;
|
|
CHAR8 usName[14];
|
|
|
|
iLight=LightGetFree();
|
|
if(iLight!=(-1))
|
|
LightGenerateElliptical(iLight, ubIntensity, (INT16)(iRadius1*DISTANCE_SCALE), (INT16)(iRadius2*DISTANCE_SCALE));
|
|
|
|
sprintf(usName, "LTE%d-%d.LHT", iRadius1, iRadius2);
|
|
pLightNames[iLight]= (STR) MemAlloc(strlen(usName)+1);
|
|
strcpy(pLightNames[iLight], usName);
|
|
|
|
return(iLight);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightIlluminateWall
|
|
|
|
Renders a light template at the specified X,Y coordinates.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightIlluminateWall(INT16 iSourceX, INT16 iSourceY, INT16 iTileX, INT16 iTileY, LEVELNODE *pStruct)
|
|
{
|
|
|
|
// return( LightTileHasWall( iSourceX, iSourceY, iTileX, iTileY ) );
|
|
|
|
#if 0
|
|
UINT16 usWallOrientation;
|
|
|
|
GetWallOrientation(pStruct->usIndex, &usWallOrientation);
|
|
|
|
switch(usWallOrientation)
|
|
{
|
|
case NO_ORIENTATION:
|
|
return(TRUE);
|
|
|
|
case INSIDE_TOP_RIGHT:
|
|
case OUTSIDE_TOP_RIGHT:
|
|
return(iSourceX >= iTileX);
|
|
|
|
case INSIDE_TOP_LEFT:
|
|
case OUTSIDE_TOP_LEFT:
|
|
return(iSourceY >= iTileY);
|
|
|
|
}
|
|
return(FALSE);
|
|
|
|
#endif
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightDraw
|
|
|
|
Renders a light template at the specified X,Y coordinates.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightDraw(UINT32 uiLightType, INT32 iLight, INT16 iX, INT16 iY, UINT32 uiSprite)
|
|
{
|
|
LIGHT_NODE *pLight;
|
|
UINT16 uiCount;
|
|
UINT16 usNodeIndex;
|
|
UINT32 uiFlags;
|
|
INT32 iOldX, iOldY;
|
|
BOOLEAN fBlocked = FALSE;
|
|
BOOLEAN fOnlyWalls;
|
|
|
|
//MAP_ELEMENT * pMapElement;
|
|
|
|
if(pLightList[iLight]==NULL)
|
|
return(FALSE);
|
|
|
|
// clear out all the flags
|
|
for(uiCount=0; uiCount < usTemplateSize[iLight]; uiCount++)
|
|
{
|
|
pLight=pLightList[iLight]+uiCount;
|
|
pLight->uiFlags&=(~LIGHT_NODE_DRAWN);
|
|
}
|
|
|
|
/*
|
|
if (!(LightSprites[uiSprite].uiFlags & MERC_LIGHT))
|
|
{
|
|
uiFlags |= LIGHT_FAKE
|
|
|
|
pMapElement = &(gpWorldLevelData[]);
|
|
if (pMapElement->pLand != NULL)
|
|
{
|
|
// only do this for visible locations
|
|
// adjust tile's recorded light level
|
|
pMapElement->sSumRealLight1+=sShadeAdd;
|
|
|
|
sSum=pMapElement->pLand->ubNaturalShadeLevel - __max( pMapElement->sSumRealLights[0], pMapElement->pLand->sSumLights[1]);
|
|
|
|
sSum=__min(SHADE_MIN, sSum);
|
|
sSum=__max(SHADE_MAX, sSum);
|
|
|
|
gpWorldLevelData[ ].ubRealShadeLevel = (UINT8) sSum;
|
|
|
|
}
|
|
|
|
}
|
|
*/
|
|
|
|
iOldX = iX;
|
|
iOldY = iY;
|
|
|
|
for(uiCount=0; uiCount < usRaySize[iLight]; uiCount++)
|
|
{
|
|
usNodeIndex=*(pLightRayList[iLight]+uiCount);
|
|
|
|
if(!(usNodeIndex&LIGHT_NEW_RAY))
|
|
{
|
|
fBlocked = FALSE;
|
|
fOnlyWalls = FALSE;
|
|
|
|
pLight=pLightList[iLight]+(usNodeIndex&(~LIGHT_BACKLIGHT));
|
|
|
|
if(!(LightSprites[uiSprite].uiFlags&LIGHT_SPR_ONROOF))
|
|
{
|
|
if(LightTileBlocked( (INT16)iOldX, (INT16)iOldY, (INT16)(iX+pLight->iDX), (INT16)(iY+pLight->iDY)))
|
|
{
|
|
uiCount=LightFindNextRay(iLight, uiCount);
|
|
|
|
fOnlyWalls = TRUE;
|
|
fBlocked = TRUE;
|
|
}
|
|
}
|
|
|
|
if(!(pLight->uiFlags&LIGHT_NODE_DRAWN) && (pLight->ubLight) )
|
|
{
|
|
uiFlags=(UINT32)(usNodeIndex&LIGHT_BACKLIGHT);
|
|
if (LightSprites[uiSprite].uiFlags & MERC_LIGHT)
|
|
uiFlags |= LIGHT_FAKE;
|
|
if(LightSprites[uiSprite].uiFlags&LIGHT_SPR_ONROOF)
|
|
uiFlags|=LIGHT_ROOF_ONLY;
|
|
|
|
LightAddTile(uiLightType, (INT16)iOldX, (INT16)iOldY, (INT16)(iX+pLight->iDX), (INT16)(iY+pLight->iDY), pLight->ubLight, uiFlags, fOnlyWalls );
|
|
|
|
pLight->uiFlags|=LIGHT_NODE_DRAWN;
|
|
|
|
}
|
|
|
|
if ( fBlocked )
|
|
{
|
|
iOldX = iX;
|
|
iOldY = iY;
|
|
}
|
|
else
|
|
{
|
|
iOldX = iX+pLight->iDX;
|
|
iOldY = iY+pLight->iDY;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
iOldX = iX;
|
|
iOldY = iY;
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
BOOLEAN LightRevealWall(INT16 sX, INT16 sY, INT16 sSrcX, INT16 sSrcY)
|
|
{
|
|
LEVELNODE *pStruct;
|
|
UINT32 uiTile;
|
|
BOOLEAN fRerender=FALSE, fHitWall=FALSE, fDoRightWalls=TRUE, fDoLeftWalls=TRUE;
|
|
TILE_ELEMENT *TileElem;
|
|
|
|
Assert(gpWorldLevelData!=NULL);
|
|
|
|
uiTile=MAPROWCOLTOPOS(sY, sX);
|
|
|
|
if(sX < sSrcX)
|
|
fDoRightWalls=FALSE;
|
|
|
|
if(sY < sSrcY)
|
|
fDoLeftWalls=FALSE;
|
|
|
|
// IF A FENCE, RETURN FALSE
|
|
if ( IsFencePresentAtGridNo( uiTile ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
pStruct=gpWorldLevelData[uiTile].pStructHead;
|
|
|
|
//while(pStruct!=NULL)
|
|
while(pStruct!=NULL && pStruct->usIndex<giNumberOfTiles) //lal bugfix
|
|
{
|
|
TileElem = &(gTileDatabase[pStruct->usIndex]);
|
|
switch(TileElem->usWallOrientation)
|
|
{
|
|
case INSIDE_TOP_RIGHT:
|
|
case OUTSIDE_TOP_RIGHT:
|
|
if(!fDoRightWalls)
|
|
fDoLeftWalls=FALSE;
|
|
break;
|
|
|
|
case INSIDE_TOP_LEFT:
|
|
case OUTSIDE_TOP_LEFT:
|
|
if(!fDoLeftWalls)
|
|
fDoRightWalls=FALSE;
|
|
break;
|
|
}
|
|
pStruct=pStruct->pNext;
|
|
}
|
|
|
|
pStruct=gpWorldLevelData[uiTile].pStructHead;
|
|
|
|
//while(pStruct!=NULL)
|
|
while(pStruct!=NULL && pStruct->usIndex<giNumberOfTiles) //lal bugfix
|
|
{
|
|
TileElem = &(gTileDatabase[pStruct->usIndex]);
|
|
switch(TileElem->usWallOrientation)
|
|
{
|
|
case NO_ORIENTATION:
|
|
break;
|
|
|
|
case INSIDE_TOP_RIGHT:
|
|
case OUTSIDE_TOP_RIGHT:
|
|
fHitWall=TRUE;
|
|
if((fDoRightWalls) && (sX >= sSrcX))
|
|
{
|
|
pStruct->uiFlags|=LEVELNODE_REVEAL;
|
|
fRerender=TRUE;
|
|
}
|
|
break;
|
|
|
|
case INSIDE_TOP_LEFT:
|
|
case OUTSIDE_TOP_LEFT:
|
|
fHitWall=TRUE;
|
|
if((fDoLeftWalls) && (sY >= sSrcY))
|
|
{
|
|
pStruct->uiFlags|=LEVELNODE_REVEAL;
|
|
fRerender=TRUE;
|
|
}
|
|
break;
|
|
}
|
|
pStruct=pStruct->pNext;
|
|
}
|
|
|
|
if(fRerender)
|
|
SetRenderFlags(RENDER_FLAG_FULL);
|
|
|
|
return(fHitWall);
|
|
}
|
|
|
|
BOOLEAN LightHideWall(INT16 sX, INT16 sY, INT16 sSrcX, INT16 sSrcY)
|
|
{
|
|
LEVELNODE *pStruct;
|
|
UINT32 uiTile;
|
|
BOOLEAN fRerender=FALSE, fHitWall=FALSE, fDoRightWalls=TRUE, fDoLeftWalls=TRUE;
|
|
TILE_ELEMENT *TileElem;
|
|
|
|
Assert(gpWorldLevelData!=NULL);
|
|
|
|
uiTile=MAPROWCOLTOPOS(sY, sX);
|
|
|
|
if(sX < sSrcX)
|
|
fDoRightWalls=FALSE;
|
|
|
|
if(sY < sSrcY)
|
|
fDoLeftWalls=FALSE;
|
|
|
|
pStruct=gpWorldLevelData[uiTile].pStructHead;
|
|
|
|
//while(pStruct!=NULL)
|
|
while(pStruct!=NULL && pStruct->usIndex<giNumberOfTiles) // lal bugfix
|
|
{
|
|
TileElem = &(gTileDatabase[pStruct->usIndex]);
|
|
switch(TileElem->usWallOrientation)
|
|
{
|
|
case INSIDE_TOP_RIGHT:
|
|
case OUTSIDE_TOP_RIGHT:
|
|
if(!fDoRightWalls)
|
|
fDoLeftWalls=FALSE;
|
|
break;
|
|
|
|
case INSIDE_TOP_LEFT:
|
|
case OUTSIDE_TOP_LEFT:
|
|
if(!fDoLeftWalls)
|
|
fDoRightWalls=FALSE;
|
|
break;
|
|
}
|
|
pStruct=pStruct->pNext;
|
|
}
|
|
|
|
pStruct=gpWorldLevelData[uiTile].pStructHead;
|
|
|
|
//while(pStruct!=NULL)
|
|
while(pStruct!=NULL && pStruct->usIndex<giNumberOfTiles) //lal bugfix
|
|
{
|
|
TileElem = &(gTileDatabase[pStruct->usIndex]);
|
|
switch(TileElem->usWallOrientation)
|
|
{
|
|
case NO_ORIENTATION:
|
|
break;
|
|
|
|
case INSIDE_TOP_RIGHT:
|
|
case OUTSIDE_TOP_RIGHT:
|
|
fHitWall=TRUE;
|
|
if((fDoRightWalls) && (sX >= sSrcX))
|
|
{
|
|
pStruct->uiFlags&=(~LEVELNODE_REVEAL);
|
|
fRerender=TRUE;
|
|
}
|
|
break;
|
|
|
|
case INSIDE_TOP_LEFT:
|
|
case OUTSIDE_TOP_LEFT:
|
|
fHitWall=TRUE;
|
|
if((fDoLeftWalls) && (sY >= sSrcY))
|
|
{
|
|
pStruct->uiFlags&=(~LEVELNODE_REVEAL);
|
|
fRerender=TRUE;
|
|
}
|
|
break;
|
|
}
|
|
pStruct=pStruct->pNext;
|
|
}
|
|
|
|
if(fRerender)
|
|
SetRenderFlags(RENDER_FLAG_FULL);
|
|
|
|
return(fHitWall);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
CalcTranslucentWalls
|
|
|
|
Tags walls as being translucent using a light template.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN CalcTranslucentWalls(INT16 iX, INT16 iY)
|
|
{
|
|
LIGHT_NODE *pLight;
|
|
UINT16 uiCount;
|
|
UINT16 usNodeIndex;
|
|
if(pLightList[0]==NULL)
|
|
return(FALSE);
|
|
for(uiCount=0; uiCount < usRaySize[0]; uiCount++)
|
|
{
|
|
usNodeIndex=*(pLightRayList[0]+uiCount);
|
|
|
|
if(!(usNodeIndex&LIGHT_NEW_RAY))
|
|
{
|
|
pLight=pLightList[0]+(usNodeIndex&(~LIGHT_BACKLIGHT));
|
|
|
|
//Kris: added map boundary checking!!!
|
|
if(LightRevealWall(
|
|
(INT16)min(max((iX+pLight->iDX),0),WORLD_COLS-1),
|
|
(INT16)min(max((iY+pLight->iDY),0),WORLD_ROWS-1),
|
|
(INT16)min(max(iX,0),WORLD_COLS-1),
|
|
(INT16)min(max(iY,0),WORLD_ROWS-1)
|
|
))
|
|
{
|
|
uiCount=LightFindNextRay(0, uiCount);
|
|
SetRenderFlags(RENDER_FLAG_FULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
BOOLEAN LightGreenTile(INT16 sX, INT16 sY, INT16 sSrcX, INT16 sSrcY)
|
|
{
|
|
LEVELNODE *pStruct, *pLand;
|
|
UINT32 uiTile;
|
|
BOOLEAN fRerender=FALSE, fHitWall=FALSE, fThroughWall=FALSE;
|
|
TILE_ELEMENT *TileElem;
|
|
|
|
Assert(gpWorldLevelData!=NULL);
|
|
|
|
uiTile=MAPROWCOLTOPOS(sY, sX);
|
|
pStruct=gpWorldLevelData[uiTile].pStructHead;
|
|
|
|
if((sX < sSrcX) || (sY < sSrcY))
|
|
fThroughWall=TRUE;
|
|
|
|
while(pStruct!=NULL)
|
|
{
|
|
TileElem = &(gTileDatabase[pStruct->usIndex]);
|
|
switch(TileElem->usWallOrientation)
|
|
{
|
|
case NO_ORIENTATION:
|
|
break;
|
|
|
|
case INSIDE_TOP_RIGHT:
|
|
case OUTSIDE_TOP_RIGHT:
|
|
fHitWall=TRUE;
|
|
if(sX >= sSrcX)
|
|
{
|
|
pStruct->uiFlags|=LEVELNODE_REVEAL;
|
|
fRerender=TRUE;
|
|
}
|
|
break;
|
|
|
|
case INSIDE_TOP_LEFT:
|
|
case OUTSIDE_TOP_LEFT:
|
|
fHitWall=TRUE;
|
|
if(sY >= sSrcY)
|
|
{
|
|
pStruct->uiFlags|=LEVELNODE_REVEAL;
|
|
fRerender=TRUE;
|
|
}
|
|
break;
|
|
}
|
|
pStruct=pStruct->pNext;
|
|
}
|
|
|
|
//if(fRerender)
|
|
//{
|
|
pLand=gpWorldLevelData[uiTile].pLandHead;
|
|
while(pLand!=NULL)
|
|
{
|
|
pLand->ubShadeLevel=0;
|
|
pLand=pLand->pNext;
|
|
}
|
|
|
|
gpWorldLevelData[uiTile].uiFlags|=MAPELEMENT_REDRAW;
|
|
SetRenderFlags(RENDER_FLAG_MARKED);
|
|
//}
|
|
|
|
return(fHitWall);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightShowRays
|
|
|
|
Draws a template by making the ground tiles green. Must be polled once for
|
|
each tile drawn to facilitate animating the drawing process for debugging.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightShowRays(INT16 iX, INT16 iY, BOOLEAN fReset)
|
|
{
|
|
LIGHT_NODE *pLight;
|
|
static UINT16 uiCount=0;
|
|
UINT16 usNodeIndex;
|
|
|
|
if(fReset)
|
|
uiCount=0;
|
|
|
|
if(pLightList[0]==NULL)
|
|
return(FALSE);
|
|
|
|
if(uiCount < usRaySize[0])
|
|
{
|
|
usNodeIndex=*(pLightRayList[0]+uiCount);
|
|
|
|
if(!(usNodeIndex&LIGHT_NEW_RAY))
|
|
{
|
|
pLight=pLightList[0]+(usNodeIndex&(~LIGHT_BACKLIGHT));
|
|
|
|
if(LightGreenTile((INT16)(iX+pLight->iDX), (INT16)(iY+pLight->iDY), iX, iY))
|
|
{
|
|
uiCount=LightFindNextRay(0, uiCount);
|
|
SetRenderFlags(RENDER_FLAG_MARKED);
|
|
}
|
|
}
|
|
|
|
uiCount++;
|
|
return(TRUE);
|
|
}
|
|
else
|
|
return(FALSE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightHideGreen
|
|
|
|
Removes the green from the tiles that was drawn to show the path of the rays.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightHideGreen(INT16 sX, INT16 sY, INT16 sSrcX, INT16 sSrcY)
|
|
{
|
|
LEVELNODE *pStruct, *pLand;
|
|
UINT32 uiTile;
|
|
BOOLEAN fRerender=FALSE, fHitWall=FALSE;
|
|
TILE_ELEMENT *TileElem;
|
|
|
|
Assert(gpWorldLevelData!=NULL);
|
|
|
|
uiTile=MAPROWCOLTOPOS(sY, sX);
|
|
pStruct=gpWorldLevelData[uiTile].pStructHead;
|
|
|
|
while(pStruct!=NULL)
|
|
{
|
|
TileElem = &(gTileDatabase[pStruct->usIndex]);
|
|
switch(TileElem->usWallOrientation)
|
|
{
|
|
case NO_ORIENTATION:
|
|
break;
|
|
|
|
case INSIDE_TOP_RIGHT:
|
|
case OUTSIDE_TOP_RIGHT:
|
|
fHitWall=TRUE;
|
|
if(sX >= sSrcX)
|
|
{
|
|
pStruct->uiFlags&=(~LEVELNODE_REVEAL);
|
|
fRerender=TRUE;
|
|
}
|
|
break;
|
|
|
|
case INSIDE_TOP_LEFT:
|
|
case OUTSIDE_TOP_LEFT:
|
|
fHitWall=TRUE;
|
|
if(sY >= sSrcY)
|
|
{
|
|
pStruct->uiFlags&=(~LEVELNODE_REVEAL);
|
|
fRerender=TRUE;
|
|
}
|
|
break;
|
|
}
|
|
pStruct=pStruct->pNext;
|
|
}
|
|
|
|
//if(fRerender)
|
|
//{
|
|
pLand=gpWorldLevelData[uiTile].pLandHead;
|
|
while(pLand!=NULL)
|
|
{
|
|
pLand->ubShadeLevel=pLand->ubNaturalShadeLevel;
|
|
pLand=pLand->pNext;
|
|
}
|
|
|
|
gpWorldLevelData[uiTile].uiFlags|=MAPELEMENT_REDRAW;
|
|
SetRenderFlags(RENDER_FLAG_MARKED);
|
|
//}
|
|
|
|
return(fHitWall);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightHideRays
|
|
|
|
Hides walls that were revealed by CalcTranslucentWalls.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightHideRays(INT16 iX, INT16 iY)
|
|
{
|
|
LIGHT_NODE *pLight;
|
|
UINT16 uiCount;
|
|
UINT16 usNodeIndex;
|
|
|
|
if(pLightList[0]==NULL)
|
|
return(FALSE);
|
|
|
|
for(uiCount=0; uiCount < usRaySize[0]; uiCount++)
|
|
{
|
|
usNodeIndex=*(pLightRayList[0]+uiCount);
|
|
|
|
if(!(usNodeIndex&LIGHT_NEW_RAY))
|
|
{
|
|
pLight=pLightList[0]+(usNodeIndex&(~LIGHT_BACKLIGHT));
|
|
|
|
if(LightHideWall((INT16)(iX+pLight->iDX), (INT16)(iY+pLight->iDY), iX, iY))
|
|
{
|
|
uiCount=LightFindNextRay(0, uiCount);
|
|
SetRenderFlags(RENDER_FLAG_MARKED);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
ApplyTranslucencyToWalls
|
|
|
|
Hides walls that were revealed by CalcTranslucentWalls.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN ApplyTranslucencyToWalls(INT16 iX, INT16 iY)
|
|
{
|
|
LIGHT_NODE *pLight;
|
|
UINT16 uiCount;
|
|
UINT16 usNodeIndex;
|
|
|
|
if(pLightList[0]==NULL)
|
|
return(FALSE);
|
|
|
|
for(uiCount=0; uiCount < usRaySize[0]; uiCount++)
|
|
{
|
|
usNodeIndex=*(pLightRayList[0]+uiCount);
|
|
|
|
if(!(usNodeIndex&LIGHT_NEW_RAY))
|
|
{
|
|
pLight=pLightList[0]+(usNodeIndex&(~LIGHT_BACKLIGHT));
|
|
//Kris: added map boundary checking!!!
|
|
if(LightHideWall(
|
|
(INT16)min(max((iX+pLight->iDX),0),WORLD_COLS-1),
|
|
(INT16)min(max((iY+pLight->iDY),0),WORLD_ROWS-1),
|
|
(INT16)min(max(iX,0),WORLD_COLS-1),
|
|
(INT16)min(max(iY,0),WORLD_ROWS-1)
|
|
))
|
|
{
|
|
uiCount=LightFindNextRay(0, uiCount);
|
|
SetRenderFlags(RENDER_FLAG_FULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/****************************************************************************************
|
|
LightTranslucentTrees
|
|
|
|
Makes all the near-side trees around a given coordinate translucent.
|
|
***************************************************************************************/
|
|
BOOLEAN LightTranslucentTrees(INT16 iX, INT16 iY)
|
|
{
|
|
INT32 iCountX, iCountY;
|
|
UINT32 uiTile;
|
|
LEVELNODE *pNode;
|
|
BOOLEAN fRerender=FALSE;
|
|
UINT32 fTileFlags;
|
|
|
|
for(iCountY=iY; iCountY < (INT16)(iY+LIGHT_TREE_REVEAL); iCountY++)
|
|
for(iCountX=iX; iCountX < (INT16)(iX+LIGHT_TREE_REVEAL); iCountX++)
|
|
{
|
|
uiTile=MAPROWCOLTOPOS(iCountY, iCountX);
|
|
pNode=gpWorldLevelData[uiTile].pStructHead;
|
|
while(pNode!=NULL)
|
|
{
|
|
GetTileFlags( pNode->usIndex, &fTileFlags );
|
|
|
|
if ( fTileFlags & FULL3D_TILE )
|
|
{
|
|
if ( !( pNode->uiFlags & LEVELNODE_REVEALTREES ) )
|
|
{
|
|
//pNode->uiFlags |= ( LEVELNODE_REVEALTREES | LEVELNODE_ERASEZ );
|
|
pNode->uiFlags |= ( LEVELNODE_REVEALTREES );
|
|
gpWorldLevelData[uiTile].uiFlags |= MAPELEMENT_REDRAW;
|
|
}
|
|
|
|
fRerender=TRUE;
|
|
}
|
|
pNode=pNode->pNext;
|
|
}
|
|
}
|
|
|
|
if(fRerender)
|
|
{
|
|
//SetRenderFlags(RENDER_FLAG_MARKED);
|
|
SetRenderFlags(RENDER_FLAG_FULL);
|
|
return(TRUE);
|
|
}
|
|
else
|
|
return(FALSE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightHideTrees
|
|
|
|
Removes the translucency from any trees in the area.
|
|
***************************************************************************************/
|
|
BOOLEAN LightHideTrees(INT16 iX, INT16 iY)
|
|
{
|
|
INT32 iCountX, iCountY;
|
|
UINT32 uiTile;
|
|
LEVELNODE *pNode;
|
|
BOOLEAN fRerender=FALSE;
|
|
UINT32 fTileFlags;
|
|
|
|
//Kris: added map boundary checking!!!
|
|
for(iCountY=(INT16)__max(iY-LIGHT_TREE_REVEAL,0); iCountY < (INT16)__min(iY+LIGHT_TREE_REVEAL,WORLD_ROWS-1); iCountY++)
|
|
for(iCountX=(INT16)__max(iX-LIGHT_TREE_REVEAL,0); iCountX < (INT16)__min(iX+LIGHT_TREE_REVEAL,WORLD_COLS-1); iCountX++)
|
|
{
|
|
uiTile=MAPROWCOLTOPOS(iCountY, iCountX);
|
|
pNode=gpWorldLevelData[uiTile].pStructHead;
|
|
while(pNode!=NULL)
|
|
{
|
|
GetTileFlags( pNode->usIndex, &fTileFlags );
|
|
|
|
if ( fTileFlags & FULL3D_TILE )
|
|
{
|
|
|
|
if ( ( pNode->uiFlags & LEVELNODE_REVEALTREES ) )
|
|
{
|
|
//pNode->uiFlags &=(~( LEVELNODE_REVEALTREES | LEVELNODE_ERASEZ ) );
|
|
pNode->uiFlags &=(~( LEVELNODE_REVEALTREES ) );
|
|
gpWorldLevelData[uiTile].uiFlags |= MAPELEMENT_REDRAW;
|
|
}
|
|
|
|
fRerender=TRUE;
|
|
}
|
|
pNode=pNode->pNext;
|
|
}
|
|
}
|
|
|
|
if(fRerender)
|
|
{
|
|
//SetRenderFlags(RENDER_FLAG_MARKED);
|
|
SetRenderFlags(RENDER_FLAG_FULL );
|
|
return(TRUE);
|
|
}
|
|
else
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************************
|
|
LightErase
|
|
|
|
Reverts all tiles a given light affects to their natural light levels.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightErase(UINT32 uiLightType, INT32 iLight, INT16 iX, INT16 iY, UINT32 uiSprite)
|
|
{
|
|
LIGHT_NODE *pLight;
|
|
UINT16 uiCount;
|
|
UINT16 usNodeIndex;
|
|
UINT32 uiFlags;
|
|
INT32 iOldX, iOldY;
|
|
BOOLEAN fBlocked = FALSE;
|
|
BOOLEAN fOnlyWalls;
|
|
|
|
|
|
if(pLightList[iLight]==NULL)
|
|
return(FALSE);
|
|
|
|
// clear out all the flags
|
|
for(uiCount=0; uiCount < usTemplateSize[iLight]; uiCount++)
|
|
{
|
|
pLight=pLightList[iLight]+uiCount;
|
|
pLight->uiFlags&=(~LIGHT_NODE_DRAWN);
|
|
}
|
|
|
|
iOldX = iX;
|
|
iOldY = iY;
|
|
|
|
for(uiCount=0; uiCount < usRaySize[iLight]; uiCount++)
|
|
{
|
|
usNodeIndex=*(pLightRayList[iLight]+uiCount);
|
|
if(!(usNodeIndex&LIGHT_NEW_RAY))
|
|
{
|
|
fBlocked = FALSE;
|
|
fOnlyWalls = FALSE;
|
|
|
|
pLight=pLightList[iLight]+(usNodeIndex&(~LIGHT_BACKLIGHT));
|
|
|
|
if(!(LightSprites[uiSprite].uiFlags&LIGHT_SPR_ONROOF))
|
|
{
|
|
if(LightTileBlocked( (INT16)iOldX, (INT16)iOldY, (INT16)(iX+pLight->iDX), (INT16)(iY+pLight->iDY)))
|
|
{
|
|
uiCount=LightFindNextRay(iLight, uiCount);
|
|
|
|
fOnlyWalls = TRUE;
|
|
fBlocked = TRUE;
|
|
}
|
|
}
|
|
|
|
if(!(pLight->uiFlags&LIGHT_NODE_DRAWN) && (pLight->ubLight) )
|
|
{
|
|
uiFlags=(UINT32)(usNodeIndex&LIGHT_BACKLIGHT);
|
|
if (LightSprites[uiSprite].uiFlags & MERC_LIGHT)
|
|
uiFlags |= LIGHT_FAKE;
|
|
if(LightSprites[uiSprite].uiFlags&LIGHT_SPR_ONROOF)
|
|
uiFlags|=LIGHT_ROOF_ONLY;
|
|
|
|
LightSubtractTile(uiLightType, (INT16)iOldX, (INT16)iOldY, (INT16)(iX+pLight->iDX), (INT16)(iY+pLight->iDY), pLight->ubLight, uiFlags, fOnlyWalls );
|
|
pLight->uiFlags|=LIGHT_NODE_DRAWN;
|
|
}
|
|
|
|
if ( fBlocked )
|
|
{
|
|
iOldX = iX;
|
|
iOldY = iY;
|
|
}
|
|
else
|
|
{
|
|
iOldX = iX+pLight->iDX;
|
|
iOldY = iY+pLight->iDY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iOldX = iX;
|
|
iOldY = iY;
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightCalcRect
|
|
|
|
Calculates the rect size of a given light, used in dirtying the screen after updating
|
|
a light.
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightCalcRect(INT32 iLight)
|
|
{
|
|
SGPRect MaxRect;
|
|
INT16 sXValue, sYValue, sDummy;
|
|
UINT32 uiCount;
|
|
LIGHT_NODE *pLight;
|
|
|
|
if(pLightList[iLight]==NULL)
|
|
return(FALSE);
|
|
|
|
pLight=pLightList[iLight];
|
|
|
|
MaxRect.iLeft=99999;
|
|
MaxRect.iRight=-99999;
|
|
MaxRect.iTop=99999;
|
|
MaxRect.iBottom=-99999;
|
|
|
|
for(uiCount=0; uiCount < usTemplateSize[iLight]; uiCount++)
|
|
{
|
|
pLight=pLightList[iLight]+uiCount;
|
|
if(pLight->ubLight)
|
|
{
|
|
MaxRect.iLeft=__min(MaxRect.iLeft, pLight->iDX);
|
|
MaxRect.iRight=__max(MaxRect.iRight, pLight->iDX);
|
|
MaxRect.iTop=__min(MaxRect.iTop, pLight->iDY);
|
|
MaxRect.iBottom=__max(MaxRect.iBottom, pLight->iDY);
|
|
}
|
|
}
|
|
|
|
FromCellToScreenCoordinates((INT16)(MaxRect.iLeft*CELL_X_SIZE),
|
|
(INT16)(MaxRect.iTop*CELL_Y_SIZE),
|
|
&sDummy, &sYValue);
|
|
|
|
LightMapLeft[iLight]=(INT16)MaxRect.iLeft;
|
|
LightMapTop[iLight]=(INT16)MaxRect.iTop;
|
|
LightMapRight[iLight]=(INT16)MaxRect.iRight;
|
|
LightMapBottom[iLight]=(INT16)MaxRect.iBottom;
|
|
|
|
LightHeight[iLight]=-sYValue;
|
|
LightYOffset[iLight]=sYValue;
|
|
|
|
FromCellToScreenCoordinates((INT16)(MaxRect.iRight*CELL_X_SIZE),
|
|
(INT16)(MaxRect.iBottom*CELL_Y_SIZE),
|
|
&sDummy, &sYValue);
|
|
LightHeight[iLight]+=sYValue;
|
|
|
|
FromCellToScreenCoordinates((INT16)(MaxRect.iLeft*CELL_X_SIZE),
|
|
(INT16)(MaxRect.iBottom*CELL_Y_SIZE),
|
|
&sXValue, &sDummy);
|
|
LightWidth[iLight]=-sXValue;
|
|
LightXOffset[iLight]=sXValue;
|
|
|
|
FromCellToScreenCoordinates((INT16)(MaxRect.iRight*CELL_X_SIZE),
|
|
(INT16)(MaxRect.iTop*CELL_Y_SIZE),
|
|
&sXValue, &sDummy);
|
|
LightWidth[iLight]+=sXValue;
|
|
|
|
LightHeight[iLight]+=WORLD_TILE_X*2;
|
|
LightWidth[iLight]+=WORLD_TILE_Y*3;
|
|
LightXOffset[iLight]-=WORLD_TILE_X*2;
|
|
LightYOffset[iLight]-=WORLD_TILE_Y*2;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/****************************************************************************************
|
|
LightSave
|
|
|
|
Saves the light list of a given template to a file. Passing in NULL for the
|
|
filename forces the system to save the light with the internal filename (recommended).
|
|
|
|
***************************************************************************************/
|
|
BOOLEAN LightSave(INT32 iLight, STR pFilename)
|
|
{
|
|
HWFILE hFile;
|
|
STR pName;
|
|
|
|
if(pLightList[iLight]==NULL)
|
|
return(FALSE);
|
|
else
|
|
{
|
|
if(pFilename==NULL)
|
|
pName=pLightNames[iLight];
|
|
else
|
|
pName=pFilename;
|
|
|
|
if((hFile=FileOpen(pName, FILE_ACCESS_WRITE|FILE_CREATE_ALWAYS, FALSE))!=0)
|
|
{
|
|
FileWrite(hFile, &usTemplateSize[iLight], sizeof(UINT16), NULL);
|
|
FileWrite(hFile, pLightList[iLight], sizeof(LIGHT_NODE)*usTemplateSize[iLight], NULL);
|
|
FileWrite(hFile, &usRaySize[iLight], sizeof(UINT16), NULL);
|
|
FileWrite(hFile, pLightRayList[iLight], sizeof(UINT16)*usRaySize[iLight], NULL);
|
|
|
|
FileClose(hFile);
|
|
}
|
|
else
|
|
return(FALSE);
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightLoad
|
|
|
|
Loads a light template from disk. The light template number is returned, or (-1)
|
|
if the file wasn't loaded.
|
|
|
|
***************************************************************************************/
|
|
INT32 LightLoad(STR pFilename)
|
|
{
|
|
HWFILE hFile;
|
|
INT32 iLight;
|
|
|
|
if((iLight=LightGetFree())==(-1))
|
|
return(-1);
|
|
else
|
|
{
|
|
if((hFile=FileOpen(pFilename, FILE_ACCESS_READ, FALSE))!=0)
|
|
{
|
|
FileRead(hFile, &usTemplateSize[iLight], sizeof(UINT16), NULL);
|
|
if((pLightList[iLight]= (LIGHT_NODE *) MemAlloc(usTemplateSize[iLight]*sizeof(LIGHT_NODE)))==NULL)
|
|
{
|
|
usTemplateSize[iLight]=0;
|
|
return(-1);
|
|
}
|
|
FileRead(hFile, pLightList[iLight], sizeof(LIGHT_NODE)*usTemplateSize[iLight], NULL);
|
|
|
|
FileRead(hFile, &usRaySize[iLight], sizeof(UINT16), NULL);
|
|
if((pLightRayList[iLight]= (UINT16 *) MemAlloc(usRaySize[iLight]*sizeof(UINT16)))==NULL)
|
|
{
|
|
usTemplateSize[iLight]=0;
|
|
usRaySize[iLight]=0;
|
|
MemFree(pLightList[iLight]);
|
|
return(-1);
|
|
}
|
|
FileRead(hFile, pLightRayList[iLight], sizeof(UINT16)*usRaySize[iLight], NULL);
|
|
|
|
FileClose(hFile);
|
|
|
|
pLightNames[iLight]= (STR) MemAlloc(strlen(pFilename)+1);
|
|
strcpy(pLightNames[iLight], pFilename);
|
|
}
|
|
else
|
|
return(-1);
|
|
}
|
|
LightCalcRect(iLight);
|
|
return(iLight);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightLoadCachedTemplate
|
|
|
|
Figures out whether a light template is already in memory, or needs to be loaded from
|
|
disk. Returns the index of the template, or (-1) if it couldn't be loaded.
|
|
|
|
***************************************************************************************/
|
|
INT32 LightLoadCachedTemplate(STR pFilename)
|
|
{
|
|
INT32 iCount;
|
|
|
|
for(iCount=0; iCount < MAX_LIGHT_TEMPLATES; iCount++)
|
|
{
|
|
if((pLightNames[iCount]!=NULL) && !(_stricmp(pFilename, pLightNames[iCount])))
|
|
return(iCount);
|
|
}
|
|
|
|
return(LightLoad(pFilename));
|
|
}
|
|
|
|
UINT8 LightGetColors(SGPPaletteEntry *pPal)
|
|
{
|
|
if(pPal!=NULL)
|
|
memcpy(pPal, &gpOrigLights[0], sizeof(SGPPaletteEntry)*gubNumLightColors);
|
|
|
|
return(gubNumLightColors);
|
|
}
|
|
|
|
/****************************************************************************************
|
|
LightSetColors
|
|
|
|
Sets the number of light colors, and the RGB value for each.
|
|
|
|
***************************************************************************************/
|
|
#ifdef JA2EDITOR
|
|
BOOLEAN gfEditorForceRebuildAllColors = FALSE;
|
|
#endif
|
|
|
|
extern void SetAllNewTileSurfacesLoaded( BOOLEAN fNew );
|
|
|
|
BOOLEAN LightSetColors(SGPPaletteEntry *pPal, UINT8 ubNumColors)
|
|
{
|
|
INT16 sRed, sGreen, sBlue;
|
|
|
|
Assert( ubNumColors >=1 && ubNumColors <=2 );
|
|
Assert( pPal );
|
|
|
|
if( pPal[0].peRed != gpLightColors[0].peRed ||
|
|
pPal[0].peGreen != gpLightColors[0].peGreen ||
|
|
pPal[0].peBlue != gpLightColors[0].peBlue )
|
|
{ //Set the entire tileset database so that it reloads everything. It has to because the
|
|
//colors have changed.
|
|
SetAllNewTileSurfacesLoaded( TRUE );
|
|
}
|
|
|
|
// before doing anything, get rid of all the old palettes
|
|
DestroyTileShadeTables( );
|
|
|
|
// we will have at least one light color
|
|
memcpy(&gpLightColors[0], &pPal[0], sizeof(SGPPaletteEntry));
|
|
memcpy(&gpOrigLights[0], &pPal[0], sizeof(SGPPaletteEntry)*2);
|
|
|
|
gubNumLightColors=ubNumColors;
|
|
|
|
// if there are two colors, calculate a third palette that is a mix of the two
|
|
if(ubNumColors==2)
|
|
{
|
|
sRed=__min((((INT16)pPal[0].peRed)*LVL1_L1_PER/100 + ((INT16)pPal[1].peRed)*LVL1_L2_PER/100), 255);
|
|
sGreen=__min((((INT16)pPal[0].peGreen)*LVL1_L1_PER/100 + ((INT16)pPal[1].peGreen)*LVL1_L2_PER/100), 255);
|
|
sBlue=__min((((INT16)pPal[0].peBlue)*LVL1_L1_PER/100 + ((INT16)pPal[1].peBlue)*LVL1_L2_PER/100), 255);
|
|
|
|
gpLightColors[1].peRed=(UINT8)(sRed);
|
|
gpLightColors[1].peGreen=(UINT8)(sGreen);
|
|
gpLightColors[1].peBlue=(UINT8)(sBlue);
|
|
|
|
sRed=__min((((INT16)pPal[0].peRed)*LVL2_L1_PER/100 + ((INT16)pPal[1].peRed)*LVL2_L2_PER/100), 255);
|
|
sGreen=__min((((INT16)pPal[0].peGreen)*LVL2_L1_PER/100 + ((INT16)pPal[1].peGreen)*LVL2_L2_PER/100), 255);
|
|
sBlue=__min((((INT16)pPal[0].peBlue)*LVL2_L1_PER/100 + ((INT16)pPal[1].peBlue)*LVL2_L2_PER/100), 255);
|
|
|
|
gpLightColors[2].peRed=(UINT8)(sRed);
|
|
gpLightColors[2].peGreen=(UINT8)(sGreen);
|
|
gpLightColors[2].peBlue=(UINT8)(sBlue);
|
|
|
|
}
|
|
|
|
BuildTileShadeTables( );
|
|
|
|
// Build all palettes for all soldiers in the world
|
|
// ( THIS FUNCTION WILL ERASE THEM IF THEY EXIST )
|
|
RebuildAllSoldierShadeTables( );
|
|
|
|
RebuildAllCorpseShadeTables( );
|
|
|
|
SetRenderFlags(RENDER_FLAG_FULL);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// Light Manipulation Layer
|
|
//---------------------------------------------------------------------------------------
|
|
|
|
/********************************************************************************
|
|
* LightSpriteGetFree
|
|
*
|
|
* Returns the index of the next available sprite.
|
|
*
|
|
********************************************************************************/
|
|
INT32 LightSpriteGetFree(void)
|
|
{
|
|
INT32 iCount;
|
|
|
|
for(iCount=0; iCount < MAX_LIGHT_SPRITES; iCount++)
|
|
{
|
|
if(!(LightSprites[iCount].uiFlags&LIGHT_SPR_ACTIVE))
|
|
return(iCount);
|
|
}
|
|
|
|
return(-1);
|
|
}
|
|
|
|
/********************************************************************************
|
|
* LightSpriteCreate
|
|
*
|
|
* Creates an instance of a light. The template is loaded if it isn't already.
|
|
* If this function fails (out of sprites, or bad template name) it returns (-1).
|
|
*
|
|
********************************************************************************/
|
|
INT32 LightSpriteCreate(STR pName, UINT32 uiLightType)
|
|
{
|
|
INT32 iSprite;
|
|
|
|
if((iSprite=LightSpriteGetFree())!=(-1))
|
|
{
|
|
memset(&LightSprites[iSprite], 0, sizeof(LIGHT_SPRITE));
|
|
|
|
LightSprites[iSprite].iX=WORLD_COLS+1;
|
|
LightSprites[iSprite].iY=WORLD_ROWS+1;
|
|
LightSprites[iSprite].iOldX=WORLD_COLS+1;
|
|
LightSprites[iSprite].iOldY=WORLD_ROWS+1;
|
|
LightSprites[iSprite].uiLightType=uiLightType;
|
|
|
|
if((LightSprites[iSprite].iTemplate=LightLoadCachedTemplate( pName))==(-1))
|
|
return(-1);
|
|
|
|
LightSprites[iSprite].uiFlags|=LIGHT_SPR_ACTIVE;
|
|
}
|
|
|
|
return(iSprite);
|
|
}
|
|
|
|
|
|
/********************************************************************************
|
|
* LightSpriteFake
|
|
*
|
|
* Sets the flag of a light sprite to "fake" (in game for merc navig purposes)
|
|
*
|
|
********************************************************************************/
|
|
BOOLEAN LightSpriteFake(INT32 iSprite)
|
|
{
|
|
if ( (iSprite >= MAX_LIGHT_SPRITES ) || (iSprite < 0) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if(LightSprites[iSprite].uiFlags&LIGHT_SPR_ACTIVE)
|
|
{
|
|
LightSprites[iSprite].uiFlags|=MERC_LIGHT;
|
|
return( TRUE );
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
/********************************************************************************
|
|
* LightSpriteDestroy
|
|
*
|
|
* Removes an instance of a light. If it was on, it is erased from the scene.
|
|
*
|
|
********************************************************************************/
|
|
BOOLEAN LightSpriteDestroy(INT32 iSprite)
|
|
{
|
|
if ( (iSprite >= MAX_LIGHT_SPRITES ) || (iSprite < 0) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if(LightSprites[iSprite].uiFlags&LIGHT_SPR_ACTIVE)
|
|
{
|
|
if(LightSprites[iSprite].uiFlags&LIGHT_SPR_ERASE)
|
|
{
|
|
if((LightSprites[iSprite].iX < WORLD_COLS) && (LightSprites[iSprite].iY < WORLD_ROWS))
|
|
{
|
|
LightErase(LightSprites[iSprite].uiLightType, LightSprites[iSprite].iTemplate, LightSprites[iSprite].iX, LightSprites[iSprite].iY, iSprite);
|
|
LightSpriteDirty(iSprite);
|
|
}
|
|
LightSprites[iSprite].uiFlags&=(~LIGHT_SPR_ERASE);
|
|
}
|
|
|
|
LightSprites[iSprite].uiFlags&=(~LIGHT_SPR_ACTIVE);
|
|
return(TRUE);
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
/********************************************************************************
|
|
* LightSpriteRender
|
|
*
|
|
* Currently unused.
|
|
*
|
|
********************************************************************************/
|
|
BOOLEAN LightSpriteRender(void)
|
|
{
|
|
//INT32 iCount;
|
|
//BOOLEAN fRenderLights=FALSE;
|
|
|
|
return(FALSE);
|
|
|
|
/* for(iCount=0; iCount < MAX_LIGHT_SPRITES; iCount++)
|
|
{
|
|
if(LightSprites[iCount].uiFlags&LIGHT_SPR_ACTIVE)
|
|
{
|
|
if((LightSprites[iCount].iX!=LightSprites[iCount].iOldX)
|
|
|| (LightSprites[iCount].iY!=LightSprites[iCount].iOldY)
|
|
|| (LightSprites[iCount].uiFlags&LIGHT_SPR_REDRAW))
|
|
{
|
|
if(LightSprites[iCount].iOldX < WORLD_COLS)
|
|
{
|
|
fRenderLights=TRUE;
|
|
LightSpriteDirty(iCount);
|
|
}
|
|
|
|
LightSprites[iCount].iOldX=LightSprites[iCount].iX;
|
|
LightSprites[iCount].iOldY=LightSprites[iCount].iY;
|
|
|
|
if(LightSprites[iCount].uiFlags&LIGHT_SPR_ON)
|
|
{
|
|
LightSpriteDirty(iCount);
|
|
fRenderLights=TRUE;
|
|
}
|
|
|
|
LightSprites[iCount].uiFlags&=(~LIGHT_SPR_REDRAW);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(fRenderLights)
|
|
{
|
|
LightResetAllTiles();
|
|
for(iCount=0; iCount < MAX_LIGHT_SPRITES; iCount++)
|
|
if((LightSprites[iCount].uiFlags&LIGHT_SPR_ACTIVE) && (LightSprites[iCount].uiFlags&LIGHT_SPR_ON))
|
|
LightDraw(LightSprites[iCount].iTemplate, LightSprites[iCount].iX, LightSprites[iCount].iY, iCount);
|
|
return(TRUE);
|
|
}
|
|
|
|
return(FALSE); */
|
|
}
|
|
|
|
/********************************************************************************
|
|
* LightSpriteRenderAll
|
|
*
|
|
* Resets all tiles in the world to the ambient light level, and redraws all
|
|
* active lights.
|
|
*
|
|
********************************************************************************/
|
|
BOOLEAN LightSpriteRenderAll(void)
|
|
{
|
|
INT32 iCount;
|
|
|
|
LightResetAllTiles();
|
|
for(iCount=0; iCount < MAX_LIGHT_SPRITES; iCount++)
|
|
{
|
|
LightSprites[iCount].uiFlags&=(~LIGHT_SPR_ERASE);
|
|
|
|
if((LightSprites[iCount].uiFlags&LIGHT_SPR_ACTIVE) && (LightSprites[iCount].uiFlags&LIGHT_SPR_ON))
|
|
{
|
|
LightDraw(LightSprites[iCount].uiLightType, LightSprites[iCount].iTemplate, LightSprites[iCount].iX, LightSprites[iCount].iY, iCount);
|
|
LightSprites[iCount].uiFlags|=LIGHT_SPR_ERASE;
|
|
LightSpriteDirty(iCount);
|
|
}
|
|
|
|
LightSprites[iCount].iOldX=LightSprites[iCount].iX;
|
|
LightSprites[iCount].iOldY=LightSprites[iCount].iY;
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/********************************************************************************
|
|
* LightSpritePosition
|
|
*
|
|
* Sets the X,Y position (IN TILES) of a light instance.
|
|
*
|
|
********************************************************************************/
|
|
BOOLEAN LightSpritePosition(INT32 iSprite, INT16 iX, INT16 iY)
|
|
{
|
|
if ( (iSprite >= MAX_LIGHT_SPRITES ) || (iSprite < 0) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if(LightSprites[iSprite].uiFlags&LIGHT_SPR_ACTIVE)
|
|
{
|
|
if((LightSprites[iSprite].iX==iX) && (LightSprites[iSprite].iY==iY))
|
|
return(TRUE);
|
|
|
|
if(LightSprites[iSprite].uiFlags&LIGHT_SPR_ERASE)
|
|
{
|
|
if((LightSprites[iSprite].iX < WORLD_COLS) && (LightSprites[iSprite].iY < WORLD_ROWS))
|
|
{
|
|
LightErase(LightSprites[iSprite].uiLightType, LightSprites[iSprite].iTemplate, LightSprites[iSprite].iX, LightSprites[iSprite].iY, iSprite);
|
|
LightSpriteDirty(iSprite);
|
|
}
|
|
}
|
|
|
|
//LightSprites[iSprite].iOldX=LightSprites[iSprite].iX;
|
|
//LightSprites[iSprite].iOldY=LightSprites[iSprite].iY;
|
|
|
|
LightSprites[iSprite].iX=iX;
|
|
LightSprites[iSprite].iY=iY;
|
|
|
|
if(LightSprites[iSprite].uiFlags&LIGHT_SPR_ON)
|
|
{
|
|
if((LightSprites[iSprite].iX < WORLD_COLS) && (LightSprites[iSprite].iY < WORLD_ROWS))
|
|
{
|
|
LightDraw(LightSprites[iSprite].uiLightType, LightSprites[iSprite].iTemplate, iX, iY, iSprite);
|
|
LightSprites[iSprite].uiFlags|=LIGHT_SPR_ERASE;
|
|
LightSpriteDirty(iSprite);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
return(FALSE);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/********************************************************************************
|
|
* LightSpriteRoofStatus
|
|
*
|
|
* Determines whether a light is on a roof or not.
|
|
*
|
|
********************************************************************************/
|
|
BOOLEAN LightSpriteRoofStatus(INT32 iSprite, BOOLEAN fOnRoof)
|
|
{
|
|
if ( (iSprite >= MAX_LIGHT_SPRITES ) || (iSprite < 0) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if(fOnRoof && (LightSprites[iSprite].uiFlags&LIGHT_SPR_ONROOF))
|
|
return(FALSE);
|
|
|
|
if(!fOnRoof && !(LightSprites[iSprite].uiFlags&LIGHT_SPR_ONROOF))
|
|
return(FALSE);
|
|
|
|
if(LightSprites[iSprite].uiFlags&LIGHT_SPR_ACTIVE)
|
|
{
|
|
if(LightSprites[iSprite].uiFlags&LIGHT_SPR_ERASE)
|
|
{
|
|
if((LightSprites[iSprite].iX < WORLD_COLS) && (LightSprites[iSprite].iY < WORLD_ROWS))
|
|
{
|
|
LightErase(LightSprites[iSprite].uiLightType, LightSprites[iSprite].iTemplate, LightSprites[iSprite].iX, LightSprites[iSprite].iY, iSprite);
|
|
LightSpriteDirty(iSprite);
|
|
}
|
|
}
|
|
|
|
if(fOnRoof)
|
|
LightSprites[iSprite].uiFlags|=LIGHT_SPR_ONROOF;
|
|
else
|
|
LightSprites[iSprite].uiFlags&=(~LIGHT_SPR_ONROOF);
|
|
|
|
|
|
if(LightSprites[iSprite].uiFlags&LIGHT_SPR_ON)
|
|
{
|
|
if((LightSprites[iSprite].iX < WORLD_COLS) && (LightSprites[iSprite].iY < WORLD_ROWS))
|
|
{
|
|
LightDraw(LightSprites[iSprite].uiLightType, LightSprites[iSprite].iTemplate, LightSprites[iSprite].iX, LightSprites[iSprite].iY, iSprite);
|
|
LightSprites[iSprite].uiFlags|=LIGHT_SPR_ERASE;
|
|
LightSpriteDirty(iSprite);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
return(FALSE);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/********************************************************************************
|
|
* LightSpritePower
|
|
*
|
|
* Turns on or off a light, based on the BOOLEAN.
|
|
*
|
|
********************************************************************************/
|
|
BOOLEAN LightSpritePower(INT32 iSprite, BOOLEAN fOn)
|
|
{
|
|
if ( (iSprite >= MAX_LIGHT_SPRITES ) || (iSprite < 0) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if(fOn)
|
|
{
|
|
//LightSprites[iSprite].uiFlags|=(LIGHT_SPR_ON|LIGHT_SPR_REDRAW);
|
|
LightSprites[iSprite].uiFlags|=(LIGHT_SPR_ON);
|
|
//LightSprites[iSprite].uiFlags&=(~LIGHT_SPR_ERASE);
|
|
LightSprites[iSprite].iOldX=WORLD_COLS;
|
|
}
|
|
else
|
|
{
|
|
LightSprites[iSprite].uiFlags&=(~LIGHT_SPR_ON);
|
|
//LightSprites[iSprite].uiFlags&=(~LIGHT_SPR_ERASE);
|
|
LightSprites[iSprite].uiFlags|=(LIGHT_SPR_ERASE);
|
|
}
|
|
|
|
if(LightSprites[iSprite].uiFlags&LIGHT_SPR_ACTIVE)
|
|
{
|
|
if(LightSprites[iSprite].uiFlags&LIGHT_SPR_ERASE)
|
|
{
|
|
if((LightSprites[iSprite].iX < WORLD_COLS) && (LightSprites[iSprite].iY < WORLD_ROWS))
|
|
{
|
|
LightErase(LightSprites[iSprite].uiLightType, LightSprites[iSprite].iTemplate, LightSprites[iSprite].iX, LightSprites[iSprite].iY, iSprite);
|
|
LightSpriteDirty(iSprite);
|
|
}
|
|
}
|
|
|
|
if(LightSprites[iSprite].uiFlags&LIGHT_SPR_ON)
|
|
{
|
|
if((LightSprites[iSprite].iX < WORLD_COLS) && (LightSprites[iSprite].iY < WORLD_ROWS))
|
|
{
|
|
LightDraw(LightSprites[iSprite].uiLightType, LightSprites[iSprite].iTemplate, LightSprites[iSprite].iX, LightSprites[iSprite].iY, iSprite);
|
|
LightSprites[iSprite].uiFlags|=LIGHT_SPR_ERASE;
|
|
LightSpriteDirty(iSprite);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
/********************************************************************************
|
|
* LightSpriteDirty
|
|
*
|
|
* Sets the flag for the renderer to draw all marked tiles.
|
|
*
|
|
********************************************************************************/
|
|
BOOLEAN LightSpriteDirty(INT32 iSprite)
|
|
{
|
|
//INT16 iLeft_s, iTop_s;
|
|
//INT16 iMapLeft, iMapTop, iMapRight, iMapBottom;
|
|
|
|
//CellXYToScreenXY((INT16)(LightSprites[iSprite].iX*CELL_X_SIZE),
|
|
// (INT16)(LightSprites[iSprite].iY*CELL_Y_SIZE), &iLeft_s, &iTop_s);
|
|
|
|
//iLeft_s+=LightXOffset[LightSprites[iSprite].iTemplate];
|
|
//iTop_s+=LightYOffset[LightSprites[iSprite].iTemplate];
|
|
|
|
//iMapLeft=LightSprites[iSprite].iX+LightMapLeft[LightSprites[iSprite].iTemplate];
|
|
//iMapTop=LightSprites[iSprite].iY+LightMapTop[LightSprites[iSprite].iTemplate];
|
|
//iMapRight=LightSprites[iSprite].iX+LightMapRight[LightSprites[iSprite].iTemplate];
|
|
//iMapBottom=LightSprites[iSprite].iY+LightMapBottom[LightSprites[iSprite].iTemplate];
|
|
|
|
//ReRenderWorld(iMapLeft, iMapTop, iMapRight, iMapBottom);
|
|
//UpdateSaveBuffer();
|
|
//AddBaseDirtyRect(gsVIEWPORT_START_X, gsVIEWPORT_START_Y, gsVIEWPORT_END_X, gsVIEWPORT_END_Y );
|
|
//AddBaseDirtyRect(iLeft_s, iTop_s,
|
|
// (INT16)(iLeft_s+LightWidth[LightSprites[iSprite].iTemplate]),
|
|
// (INT16)(iTop_s+LightHeight[LightSprites[iSprite].iTemplate]));
|
|
|
|
SetRenderFlags(RENDER_FLAG_MARKED);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
BOOLEAN CreateObjectPalette(HVOBJECT pObj, UINT32 uiBase, SGPPaletteEntry *pShadePal)
|
|
{
|
|
UINT32 uiCount;
|
|
|
|
pObj->pShades[uiBase]=Create16BPPPaletteShaded( pShadePal, gusShadeLevels[0][0],
|
|
gusShadeLevels[0][1],
|
|
gusShadeLevels[0][2], TRUE);
|
|
|
|
for(uiCount=1; uiCount < 16; uiCount++)
|
|
{
|
|
pObj->pShades[uiBase+uiCount]=Create16BPPPaletteShaded( pShadePal, gusShadeLevels[uiCount][0],
|
|
gusShadeLevels[uiCount][1],
|
|
gusShadeLevels[uiCount][2], FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
BOOLEAN CreateSoldierShadedPalette( SOLDIERTYPE *pSoldier, UINT32 uiBase, SGPPaletteEntry *pShadePal)
|
|
{
|
|
UINT32 uiCount;
|
|
|
|
pSoldier->pShades[uiBase]=Create16BPPPaletteShaded( pShadePal, gusShadeLevels[0][0],
|
|
gusShadeLevels[0][1],
|
|
gusShadeLevels[0][2], TRUE);
|
|
|
|
for(uiCount=1; uiCount < 16; uiCount++)
|
|
{
|
|
pSoldier->pShades[uiBase+uiCount]=Create16BPPPaletteShaded( pShadePal, gusShadeLevels[uiCount][0],
|
|
gusShadeLevels[uiCount][1],
|
|
gusShadeLevels[uiCount][2], FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/**********************************************************************************************
|
|
CreateObjectPaletteTables
|
|
|
|
Creates the shading tables for 8-bit brushes. One highlight table is created, based on
|
|
the object-type, 3 brightening tables, 1 normal, and 11 darkening tables. The entries are
|
|
created iteratively, rather than in a loop to allow hand-tweaking of the values. If you
|
|
change the HVOBJECT_SHADE_TABLES symbol, remember to add/delete entries here, it won't
|
|
adjust automagically.
|
|
|
|
**********************************************************************************************/
|
|
#ifdef JA2TESTVERSION
|
|
extern UINT32 uiNumTablesSaved;
|
|
#endif
|
|
|
|
UINT16 CreateTilePaletteTables(HVOBJECT pObj, UINT32 uiTileIndex, BOOLEAN fForce )
|
|
{
|
|
UINT32 uiCount;
|
|
SGPPaletteEntry LightPal[256];
|
|
BOOLEAN fLoaded = FALSE;
|
|
|
|
Assert(pObj!=NULL);
|
|
|
|
// create the basic shade table
|
|
if( !gfForceBuildShadeTables && !fForce )
|
|
{ //The overwhelming majority of maps use the neutral 0,0,0 light for outdoors. These shadetables
|
|
//are extremely time consuming to generate, so we will attempt to load them. If we do, then
|
|
//we skip the generation process altogether.
|
|
if( LoadShadeTable( pObj, uiTileIndex ) )
|
|
{
|
|
fLoaded = TRUE;
|
|
}
|
|
}
|
|
if( !fLoaded )
|
|
{ //This is expensive as hell to call!
|
|
for(uiCount=0; uiCount < 256; uiCount++)
|
|
{
|
|
// combine the rgb of the light color with the object's palette
|
|
LightPal[uiCount].peRed=(UINT8)(__min((UINT16)pObj->pPaletteEntry[uiCount].peRed+(UINT16)gpLightColors[0].peRed, 255));
|
|
LightPal[uiCount].peGreen=(UINT8)(__min((UINT16)pObj->pPaletteEntry[uiCount].peGreen+(UINT16)gpLightColors[0].peGreen, 255));
|
|
LightPal[uiCount].peBlue=(UINT8)(__min((UINT16)pObj->pPaletteEntry[uiCount].peBlue+(UINT16)gpLightColors[0].peBlue, 255));
|
|
}
|
|
// build the shade tables
|
|
CreateObjectPalette(pObj, 0, LightPal);
|
|
|
|
//We paid to generate the shade table, so now save it, so we don't have to regenerate it ever
|
|
//again!
|
|
if( !gfForceBuildShadeTables && !gpLightColors[0].peRed && !gpLightColors[0].peGreen && !gpLightColors[0].peBlue )
|
|
{
|
|
SaveShadeTable( pObj, uiTileIndex );
|
|
}
|
|
#ifdef JA2TESTVERSION
|
|
else
|
|
uiNumTablesSaved++;
|
|
#endif
|
|
}
|
|
|
|
// if two lights are active
|
|
if(gubNumLightColors==2)
|
|
{
|
|
// build the second light's palette and table
|
|
for(uiCount=0; uiCount < 256; uiCount++)
|
|
{
|
|
LightPal[uiCount].peRed=(UINT8)(__min((UINT16)pObj->pPaletteEntry[uiCount].peRed+(UINT16)gpLightColors[1].peRed, 255));
|
|
LightPal[uiCount].peGreen=(UINT8)(__min((UINT16)pObj->pPaletteEntry[uiCount].peGreen+(UINT16)gpLightColors[1].peGreen, 255));
|
|
LightPal[uiCount].peBlue=(UINT8)(__min((UINT16)pObj->pPaletteEntry[uiCount].peBlue+(UINT16)gpLightColors[1].peBlue, 255));
|
|
}
|
|
CreateObjectPalette(pObj, 16, LightPal);
|
|
|
|
// build a table that is a mix of the first two
|
|
for(uiCount=0; uiCount < 256; uiCount++)
|
|
{
|
|
LightPal[uiCount].peRed=(UINT8)(__min((UINT16)pObj->pPaletteEntry[uiCount].peRed+(UINT16)gpLightColors[2].peRed, 255));
|
|
LightPal[uiCount].peGreen=(UINT8)(__min((UINT16)pObj->pPaletteEntry[uiCount].peGreen+(UINT16)gpLightColors[2].peGreen, 255));
|
|
LightPal[uiCount].peBlue=(UINT8)(__min((UINT16)pObj->pPaletteEntry[uiCount].peBlue+(UINT16)gpLightColors[2].peBlue, 255));
|
|
}
|
|
CreateObjectPalette(pObj, 32, LightPal);
|
|
}
|
|
|
|
// build neutral palette as well!
|
|
// Set current shade table to neutral color
|
|
pObj->pShadeCurrent=pObj->pShades[4];
|
|
pObj->pGlow=pObj->pShades[0];
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
UINT16 CreateSoldierPaletteTables(SOLDIERTYPE *pSoldier, UINT32 uiType)
|
|
{
|
|
SGPPaletteEntry LightPal[256];
|
|
UINT32 uiCount;
|
|
|
|
// create the basic shade table
|
|
for(uiCount=0; uiCount < 256; uiCount++)
|
|
{
|
|
// combine the rgb of the light color with the object's palette
|
|
LightPal[uiCount].peRed=(UINT8)(__min((UINT16)pSoldier->p8BPPPalette[uiCount].peRed+(UINT16)gpLightColors[0].peRed, 255));
|
|
LightPal[uiCount].peGreen=(UINT8)(__min((UINT16)pSoldier->p8BPPPalette[uiCount].peGreen+(UINT16)gpLightColors[0].peGreen, 255));
|
|
LightPal[uiCount].peBlue=(UINT8)(__min((UINT16)pSoldier->p8BPPPalette[uiCount].peBlue+(UINT16)gpLightColors[0].peBlue, 255));
|
|
}
|
|
// build the shade tables
|
|
CreateSoldierShadedPalette(pSoldier, 0, LightPal);
|
|
|
|
// if two lights are active
|
|
if(gubNumLightColors==2)
|
|
{
|
|
// build the second light's palette and table
|
|
for(uiCount=0; uiCount < 256; uiCount++)
|
|
{
|
|
LightPal[uiCount].peRed=(UINT8)(__min((UINT16)pSoldier->p8BPPPalette[uiCount].peRed+(UINT16)gpLightColors[1].peRed, 255));
|
|
LightPal[uiCount].peGreen=(UINT8)(__min((UINT16)pSoldier->p8BPPPalette[uiCount].peGreen+(UINT16)gpLightColors[1].peGreen, 255));
|
|
LightPal[uiCount].peBlue=(UINT8)(__min((UINT16)pSoldier->p8BPPPalette[uiCount].peBlue+(UINT16)gpLightColors[1].peBlue, 255));
|
|
}
|
|
CreateSoldierShadedPalette(pSoldier, 16, LightPal);
|
|
|
|
// build a table that is a mix of the first two
|
|
for(uiCount=0; uiCount < 256; uiCount++)
|
|
{
|
|
LightPal[uiCount].peRed=(UINT8)(__min((UINT16)pSoldier->p8BPPPalette[uiCount].peRed+(UINT16)gpLightColors[2].peRed, 255));
|
|
LightPal[uiCount].peGreen=(UINT8)(__min((UINT16)pSoldier->p8BPPPalette[uiCount].peGreen+(UINT16)gpLightColors[2].peGreen, 255));
|
|
LightPal[uiCount].peBlue=(UINT8)(__min((UINT16)pSoldier->p8BPPPalette[uiCount].peBlue+(UINT16)gpLightColors[2].peBlue, 255));
|
|
}
|
|
CreateSoldierShadedPalette(pSoldier, 32, LightPal);
|
|
}
|
|
|
|
// build neutral palette as well!
|
|
// Set current shade table to neutral color
|
|
pSoldier->pCurrentShade=pSoldier->pShades[4];
|
|
//pSoldier->pGlow=pSoldier->pShades[0];
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
|
|
|