Exit Grids.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. #ifdef PRECOMPILEDHEADERS
  2. #include "TileEngine All.h"
  3. #else
  4. #include <stdio.h>
  5. #include "FileMan.h"
  6. #include "debug.h"
  7. #include "types.h"
  8. #include "worlddef.h"
  9. #include "worldman.h"
  10. #include "smooth.h"
  11. #include "Exit Grids.h"
  12. #include "Editor Undo.h"
  13. #include "StrategicMap.h"
  14. #include "Strategic Movement.h"
  15. #include "message.h"
  16. #include "Font Control.h"
  17. #include "pathai.h"
  18. #include "overhead.h"
  19. #include "Animation Control.h"
  20. #include "Sys Globals.h"
  21. #include "quests.h"
  22. #include "SaveLoadMap.h"
  23. #endif
  24. BOOLEAN gfLoadingExitGrids = FALSE;
  25. //used by editor.
  26. EXITGRID gExitGrid = {0,1,1,0};
  27. BOOLEAN gfOverrideInsertionWithExitGrid = FALSE;
  28. INT32 ConvertExitGridToINT32( EXITGRID *pExitGrid )
  29. {
  30. INT32 iExitGridInfo;
  31. iExitGridInfo = (pExitGrid->ubGotoSectorX-1)<< 28;
  32. iExitGridInfo += (pExitGrid->ubGotoSectorY-1)<< 24;
  33. iExitGridInfo += pExitGrid->ubGotoSectorZ << 20;
  34. iExitGridInfo += pExitGrid->usGridNo & 0x0000ffff;
  35. return iExitGridInfo;
  36. }
  37. void ConvertINT32ToExitGrid( INT32 iExitGridInfo, EXITGRID *pExitGrid )
  38. {
  39. //convert the int into 4 unsigned bytes.
  40. pExitGrid->ubGotoSectorX = (UINT8)(((iExitGridInfo & 0xf0000000)>>28)+1);
  41. pExitGrid->ubGotoSectorY = (UINT8)(((iExitGridInfo & 0x0f000000)>>24)+1);
  42. pExitGrid->ubGotoSectorZ = (UINT8)((iExitGridInfo & 0x00f00000)>>20);
  43. pExitGrid->usGridNo = (UINT16)(iExitGridInfo & 0x0000ffff);
  44. }
  45. BOOLEAN GetExitGrid( UINT16 usMapIndex, EXITGRID *pExitGrid )
  46. {
  47. LEVELNODE *pShadow;
  48. pShadow = gpWorldLevelData[ usMapIndex ].pShadowHead;
  49. //Search through object layer for an exitgrid
  50. while( pShadow )
  51. {
  52. if ( pShadow->uiFlags & LEVELNODE_EXITGRID )
  53. {
  54. ConvertINT32ToExitGrid( pShadow->iExitGridInfo, pExitGrid );
  55. return TRUE;
  56. }
  57. pShadow = pShadow->pNext;
  58. }
  59. pExitGrid->ubGotoSectorX = 0;
  60. pExitGrid->ubGotoSectorY = 0;
  61. pExitGrid->ubGotoSectorZ = 0;
  62. pExitGrid->usGridNo = 0;
  63. return FALSE;
  64. }
  65. BOOLEAN ExitGridAtGridNo( UINT16 usMapIndex )
  66. {
  67. LEVELNODE *pShadow;
  68. pShadow = gpWorldLevelData[ usMapIndex ].pShadowHead;
  69. //Search through object layer for an exitgrid
  70. while( pShadow )
  71. {
  72. if ( pShadow->uiFlags & LEVELNODE_EXITGRID )
  73. {
  74. return TRUE;
  75. }
  76. pShadow = pShadow->pNext;
  77. }
  78. return FALSE;
  79. }
  80. BOOLEAN GetExitGridLevelNode( UINT16 usMapIndex, LEVELNODE **ppLevelNode )
  81. {
  82. LEVELNODE *pShadow;
  83. pShadow = gpWorldLevelData[ usMapIndex ].pShadowHead;
  84. //Search through object layer for an exitgrid
  85. while( pShadow )
  86. {
  87. if ( pShadow->uiFlags & LEVELNODE_EXITGRID )
  88. {
  89. *ppLevelNode = pShadow;
  90. return TRUE;
  91. }
  92. pShadow = pShadow->pNext;
  93. }
  94. return FALSE;
  95. }
  96. void AddExitGridToWorld( INT32 iMapIndex, EXITGRID *pExitGrid )
  97. {
  98. LEVELNODE *pShadow, *tail;
  99. pShadow = gpWorldLevelData[ iMapIndex ].pShadowHead;
  100. //Search through object layer for an exitgrid
  101. while( pShadow )
  102. {
  103. tail = pShadow;
  104. if( pShadow->uiFlags & LEVELNODE_EXITGRID )
  105. { //we have found an existing exitgrid in this node, so replace it with the new information.
  106. pShadow->iExitGridInfo = ConvertExitGridToINT32( pExitGrid );
  107. //SmoothExitGridRadius( (UINT16)iMapIndex, 0 );
  108. return;
  109. }
  110. pShadow = pShadow->pNext;
  111. }
  112. // Add levelnode
  113. AddShadowToHead( iMapIndex, MOCKFLOOR1 );
  114. // Get new node
  115. pShadow = gpWorldLevelData[ iMapIndex ].pShadowHead;
  116. //fill in the information for the new exitgrid levelnode.
  117. pShadow->iExitGridInfo = ConvertExitGridToINT32( pExitGrid );
  118. pShadow->uiFlags |= ( LEVELNODE_EXITGRID | LEVELNODE_HIDDEN );
  119. //Add the exit grid to the sector, only if we call ApplyMapChangesToMapTempFile() first.
  120. if( !gfEditMode && !gfLoadingExitGrids )
  121. {
  122. AddExitGridToMapTempFile( (UINT16)iMapIndex, pExitGrid, gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
  123. }
  124. }
  125. void RemoveExitGridFromWorld( INT32 iMapIndex )
  126. {
  127. UINT16 usDummy;
  128. if( TypeExistsInShadowLayer( iMapIndex, MOCKFLOOR, &usDummy ) )
  129. {
  130. RemoveAllShadowsOfTypeRange( iMapIndex, MOCKFLOOR, MOCKFLOOR );
  131. }
  132. }
  133. void SaveExitGrids( HWFILE fp, UINT16 usNumExitGrids )
  134. {
  135. EXITGRID exitGrid;
  136. UINT16 usNumSaved = 0;
  137. UINT16 x;
  138. UINT32 uiBytesWritten;
  139. FileWrite( fp, &usNumExitGrids, 2, &uiBytesWritten );
  140. for( x = 0; x < WORLD_MAX; x++ )
  141. {
  142. if( GetExitGrid( x, &exitGrid ) )
  143. {
  144. FileWrite( fp, &x, 2, &uiBytesWritten );
  145. FileWrite( fp, &exitGrid, 5, &uiBytesWritten );
  146. usNumSaved++;
  147. }
  148. }
  149. //If these numbers aren't equal, something is wrong!
  150. Assert( usNumExitGrids == usNumSaved );
  151. }
  152. void LoadExitGrids( INT8 **hBuffer )
  153. {
  154. EXITGRID exitGrid;
  155. UINT16 x;
  156. UINT16 usNumSaved;
  157. UINT16 usMapIndex;
  158. gfLoadingExitGrids = TRUE;
  159. LOADDATA( &usNumSaved, *hBuffer, 2 );
  160. //FileRead( hfile, &usNumSaved, 2, NULL);
  161. for( x = 0; x < usNumSaved; x++ )
  162. {
  163. LOADDATA( &usMapIndex, *hBuffer, 2 );
  164. //FileRead( hfile, &usMapIndex, 2, NULL);
  165. LOADDATA( &exitGrid, *hBuffer, 5 );
  166. //FileRead( hfile, &exitGrid, 5, NULL);
  167. AddExitGridToWorld( usMapIndex, &exitGrid );
  168. }
  169. gfLoadingExitGrids = FALSE;
  170. }
  171. void AttemptToChangeFloorLevel( INT8 bRelativeZLevel )
  172. {
  173. UINT8 ubLookForLevel=0;
  174. UINT16 i;
  175. if( bRelativeZLevel != 1 && bRelativeZLevel != -1 )
  176. return;
  177. //Check if on ground level -- if so, can't go up!
  178. if( bRelativeZLevel == -1 && !gbWorldSectorZ )
  179. {
  180. ScreenMsg( FONT_DKYELLOW, MSG_INTERFACE, pMessageStrings[ MSG_CANT_GO_UP ], ubLookForLevel );
  181. return;
  182. }
  183. //Check if on bottom level -- if so, can't go down!
  184. if( bRelativeZLevel == 1 && gbWorldSectorZ == 3 )
  185. {
  186. ScreenMsg( FONT_DKYELLOW, MSG_INTERFACE, pMessageStrings[ MSG_CANT_GO_DOWN ], ubLookForLevel );
  187. return;
  188. }
  189. ubLookForLevel = (UINT8)(gbWorldSectorZ + bRelativeZLevel);
  190. for( i = 0; i < WORLD_MAX; i++ )
  191. {
  192. if( GetExitGrid( i, &gExitGrid ) )
  193. {
  194. if( gExitGrid.ubGotoSectorZ == ubLookForLevel )
  195. { //found an exit grid leading to the goal sector!
  196. gfOverrideInsertionWithExitGrid = TRUE;
  197. //change all current mercs in the loaded sector, and move them
  198. //to the new sector.
  199. MoveAllGroupsInCurrentSectorToSector( (UINT8)gWorldSectorX, (UINT8)gWorldSectorY, ubLookForLevel );
  200. if( ubLookForLevel )
  201. ScreenMsg( FONT_YELLOW, MSG_INTERFACE, pMessageStrings[ MSG_ENTERING_LEVEL ], ubLookForLevel );
  202. else
  203. ScreenMsg( FONT_YELLOW, MSG_INTERFACE, pMessageStrings[ MSG_LEAVING_BASEMENT ] );
  204. SetCurrentWorldSector( gWorldSectorX, gWorldSectorY, ubLookForLevel );
  205. gfOverrideInsertionWithExitGrid = FALSE;
  206. }
  207. }
  208. }
  209. }
  210. UINT16 FindGridNoFromSweetSpotCloseToExitGrid( SOLDIERTYPE *pSoldier, INT16 sSweetGridNo, INT8 ubRadius, UINT8 *pubDirection )
  211. {
  212. INT16 sTop, sBottom;
  213. INT16 sLeft, sRight;
  214. INT16 cnt1, cnt2;
  215. INT16 sGridNo;
  216. INT32 uiRange, uiLowestRange = 999999;
  217. INT16 sLowestGridNo=0;
  218. INT32 leftmost;
  219. BOOLEAN fFound = FALSE;
  220. SOLDIERTYPE soldier;
  221. UINT8 ubSaveNPCAPBudget;
  222. UINT8 ubSaveNPCDistLimit;
  223. EXITGRID ExitGrid;
  224. UINT8 ubGotoSectorX, ubGotoSectorY, ubGotoSectorZ;
  225. // Turn off at end of function...
  226. gfPlotPathToExitGrid = TRUE;
  227. //Save AI pathing vars. changing the distlimit restricts how
  228. //far away the pathing will consider.
  229. ubSaveNPCAPBudget = gubNPCAPBudget;
  230. ubSaveNPCDistLimit = gubNPCDistLimit;
  231. gubNPCAPBudget = 0;
  232. gubNPCDistLimit = ubRadius;
  233. //create dummy soldier, and use the pathing to determine which nearby slots are
  234. //reachable.
  235. memset( &soldier, 0, sizeof( SOLDIERTYPE ) );
  236. soldier.bLevel = 0;
  237. soldier.bTeam = 1;
  238. soldier.sGridNo = pSoldier->sGridNo;
  239. // OK, Get an exit grid ( if possible )
  240. if ( !GetExitGrid( sSweetGridNo, &ExitGrid ) )
  241. {
  242. return( NOWHERE );
  243. }
  244. // Copy our dest values.....
  245. ubGotoSectorX = ExitGrid.ubGotoSectorX;
  246. ubGotoSectorY = ExitGrid.ubGotoSectorY;
  247. ubGotoSectorZ = ExitGrid.ubGotoSectorZ;
  248. sTop = ubRadius;
  249. sBottom = -ubRadius;
  250. sLeft = - ubRadius;
  251. sRight = ubRadius;
  252. //clear the mapelements of potential residue MAPELEMENT_REACHABLE flags
  253. //in the square region.
  254. for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ )
  255. {
  256. for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ )
  257. {
  258. sGridNo = pSoldier->sGridNo + (WORLD_COLS * cnt1) + cnt2;
  259. if ( sGridNo >= 0 && sGridNo < WORLD_MAX )
  260. {
  261. gpWorldLevelData[ sGridNo ].uiFlags &= (~MAPELEMENT_REACHABLE);
  262. }
  263. }
  264. }
  265. //Now, find out which of these gridnos are reachable
  266. //(use the fake soldier and the pathing settings)
  267. FindBestPath( &soldier, NOWHERE, 0, WALKING, COPYREACHABLE, PATH_THROUGH_PEOPLE );
  268. uiLowestRange = 999999;
  269. for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ )
  270. {
  271. leftmost = ( ( pSoldier->sGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS;
  272. for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ )
  273. {
  274. sGridNo = pSoldier->sGridNo + ( WORLD_COLS * cnt1 ) + cnt2;
  275. if ( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) &&
  276. gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE )
  277. {
  278. // Go on sweet stop
  279. // ATE: Added this check because for all intensive purposes, cavewalls will be not an OKDEST
  280. // but we want thenm too...
  281. if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->bLevel ) )
  282. {
  283. if ( GetExitGrid( sGridNo, &ExitGrid ) )
  284. {
  285. // Is it the same exitgrid?
  286. if ( ExitGrid.ubGotoSectorX == ubGotoSectorX && ExitGrid.ubGotoSectorY == ubGotoSectorY && ExitGrid.ubGotoSectorZ == ubGotoSectorZ )
  287. {
  288. uiRange = GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sGridNo );
  289. if ( uiRange < uiLowestRange )
  290. {
  291. sLowestGridNo = sGridNo;
  292. uiLowestRange = uiRange;
  293. fFound = TRUE;
  294. }
  295. }
  296. }
  297. }
  298. }
  299. }
  300. }
  301. gubNPCAPBudget = ubSaveNPCAPBudget;
  302. gubNPCDistLimit = ubSaveNPCDistLimit;
  303. gfPlotPathToExitGrid = FALSE;
  304. if ( fFound )
  305. {
  306. // Set direction to center of map!
  307. *pubDirection = (UINT8)GetDirectionToGridNoFromGridNo( sLowestGridNo, ( ( ( WORLD_ROWS / 2 ) * WORLD_COLS ) + ( WORLD_COLS / 2 ) ) );
  308. return( sLowestGridNo );
  309. }
  310. else
  311. {
  312. return( NOWHERE );
  313. }
  314. }
  315. UINT16 FindClosestExitGrid( SOLDIERTYPE *pSoldier, INT16 sSrcGridNo, INT8 ubRadius )
  316. {
  317. INT16 sTop, sBottom;
  318. INT16 sLeft, sRight;
  319. INT16 cnt1, cnt2;
  320. INT16 sGridNo;
  321. INT32 uiRange, uiLowestRange = 999999;
  322. INT16 sLowestGridNo=0;
  323. INT32 leftmost;
  324. BOOLEAN fFound = FALSE;
  325. EXITGRID ExitGrid;
  326. sTop = ubRadius;
  327. sBottom = -ubRadius;
  328. sLeft = - ubRadius;
  329. sRight = ubRadius;
  330. //clear the mapelements of potential residue MAPELEMENT_REACHABLE flags
  331. uiLowestRange = 999999;
  332. for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ )
  333. {
  334. leftmost = ( ( sSrcGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS;
  335. for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ )
  336. {
  337. sGridNo = sSrcGridNo + ( WORLD_COLS * cnt1 ) + cnt2;
  338. if( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) )
  339. {
  340. if ( GetExitGrid( sGridNo, &ExitGrid ) )
  341. {
  342. uiRange = GetRangeInCellCoordsFromGridNoDiff( sSrcGridNo, sGridNo );
  343. if ( uiRange < uiLowestRange )
  344. {
  345. sLowestGridNo = sGridNo;
  346. uiLowestRange = uiRange;
  347. fFound = TRUE;
  348. }
  349. }
  350. }
  351. }
  352. }
  353. if ( fFound )
  354. {
  355. return( sLowestGridNo );
  356. }
  357. else
  358. {
  359. return( NOWHERE );
  360. }
  361. }