mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
Added missing #includes Added LUA scripting and console Changed the way tracers are visualized, so they work more like real tracers instead of a light fountain Fixed signedness of many grid variables used in GetMouseMapPos calls Fixed enemy weapon choosing: Sometimes a mortar is chosen but later rejected, but the grenade class was not reset. Caused assertion failure Added checks so enemies don't try to chuck RPG grenades with their hands Now possible to shoot a someone in the head if he's in water Fixed InitSightArrays to also clear soldier interrupt duel points. This was causing an assertion failure elsewhere in the code because the interrupt list still had soldiers on it sometimes when this function was called. Soldiers are much less willing to forfeit their turn over an attempt to use more APs than they have Removed early setting of muzzle flash. This would allow enemies to get an interrupt before you even fired. Fixed item dropping by AI. If AI tried to drop something while standing it would cause deadlock Change to greatly speed up closing the sector inventory window in an unloaded sector Added conditional compile flag to always give robot weapon ready advantage, even for 360 degree sighting Check builddefines.h for the conditional flags. Of greatest interest might be LUA_CONSOLE. This will cause the game to bring up a command console when run. However this console is severely lacking in many areas. If anybody knows of an open-source terminal/console that could be used instead, it would be appreciated. The existing console does bad things when you try to close it, and since it counts as a separate app, it pauses the game while it has focus. LUA scripting is very limited, basically just proof of concept. There is one global variable, the Soldiers array. An array index gives you the soldier in that slot in the currently loaded sector. The soldier has a few things that can be accessed: name (short name); fullname (long name); grid (current grid #, can be changed); walkto(grid) (function to walk to another grid); runto(grid) (function to run to another grid) git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@924 3b4a5df2-a311-0410-b5c6-a8a6f20db521
3718 lines
102 KiB
C++
3718 lines
102 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 "worldman.h"
|
|
#include "jascreens.h"
|
|
#include "edit_sys.h"
|
|
#include "Isometric Utils.h"
|
|
#include "line.h"
|
|
#include "Animation Control.h"
|
|
#include "Animation Data.h"
|
|
#include "Timer Control.h"
|
|
#include "Radar Screen.h"
|
|
#include "Render Dirty.h"
|
|
#include "Font Control.h"
|
|
#include "Sys Globals.h"
|
|
#include "Render Dirty.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 ( usTileNo >= NOWHERE )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( usSrcTileNo >= NOWHERE )
|
|
{
|
|
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 ( usTileNo == 10125 || usTileNo == 10126 )
|
|
//{
|
|
// int i = 0;
|
|
//}
|
|
|
|
if ( usTileNo >= NOWHERE )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( usSrcTileNo >= NOWHERE )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// Get direction
|
|
//bDirection = atan8( iX, iY, iSrcX, iSrcY );
|
|
bDirection = atan8( iSrcX, iSrcY, iX, iY );
|
|
|
|
if ( usTileNo == 20415 && bDirection == 3 )
|
|
{
|
|
int i = 0;
|
|
}
|
|
|
|
|
|
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 < NUMBEROFTILES )
|
|
{
|
|
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( INT16 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 += ubShadeAdd;
|
|
if (fFake)
|
|
{
|
|
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 -= ubShadeSubtract;
|
|
}
|
|
if (fFake)
|
|
{
|
|
if (ubShadeSubtract > pNode->ubFakeShadeLevel)
|
|
{
|
|
pNode->ubFakeShadeLevel = 0;
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
UINT32 uiTile;
|
|
BOOLEAN fLitWall=FALSE;
|
|
BOOLEAN fFake;
|
|
|
|
Assert(gpWorldLevelData!=NULL);
|
|
|
|
uiTile= MAPROWCOLTOPOS( iY, iX );
|
|
|
|
if ( uiTile >= NOWHERE )
|
|
{
|
|
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 < NUMBEROFTILES )
|
|
{
|
|
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 < NUMBEROFTILES )
|
|
{
|
|
LightAddTileNode(pObject, uiLightType, ubShadeAdd, FALSE);
|
|
}
|
|
pObject=pObject->pNext;
|
|
}
|
|
|
|
if(uiFlags&LIGHT_BACKLIGHT)
|
|
ubShadeAdd=(INT16)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 < NUMBEROFTILES )
|
|
{
|
|
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;
|
|
UINT32 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 ( uiTile >= NOWHERE )
|
|
{
|
|
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 < NUMBEROFTILES )
|
|
{
|
|
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 < NUMBEROFTILES )
|
|
{
|
|
LightSubtractTileNode(pObject, uiLightType, ubShadeSubtract, FALSE);
|
|
}
|
|
pObject=pObject->pNext;
|
|
}
|
|
|
|
if(uiFlags&LIGHT_BACKLIGHT)
|
|
ubShadeSubtract=(INT16)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 < NUMBEROFTILES )
|
|
{
|
|
LightSubtractTileNode(pRoof, uiLightType, ubShadeSubtract, fFake);
|
|
}
|
|
pRoof=pRoof->pNext;
|
|
}
|
|
|
|
pOnRoof = gpWorldLevelData[uiTile].pOnRoofHead;
|
|
while(pOnRoof!=NULL)
|
|
{
|
|
if ( pOnRoof->usIndex < NUMBEROFTILES )
|
|
{
|
|
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!=0xffff);
|
|
|
|
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!=0xffff);
|
|
|
|
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)
|
|
{
|
|
BOOLEAN fDuplicate=FALSE;
|
|
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+=XAdvance;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i<=XDelta; i++)
|
|
{
|
|
LightAddNode(iLight, iStartX, iStartY, iXPos, iYPos, ubStartIntens, usFlags);
|
|
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
|
|
ReCreateSoldierLight( pSoldier );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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( (INT16)uiTile ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
pStruct=gpWorldLevelData[uiTile].pStructHead;
|
|
|
|
//while(pStruct!=NULL)
|
|
while(pStruct!=NULL && pStruct->usIndex<NUMBEROFTILES) //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<NUMBEROFTILES) //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<NUMBEROFTILES) // 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<NUMBEROFTILES) //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;
|
|
LIGHT_NODE *pNewLight=NULL, *pLastLight=NULL;
|
|
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(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(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;
|
|
BOOLEAN fRenderLights=FALSE;
|
|
|
|
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(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(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(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);
|
|
|
|
}
|
|
|
|
|