TerrainLogic.cpp 95 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: TerrainLogic.cpp /////////////////////////////////////////////////////////////////////////
  24. // Logical terrain representation for the game logic side
  25. // Author: Colin Day, April 2001
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  28. #include "Common/DataChunk.h"
  29. #include "Common/GameState.h"
  30. #include "Common/MapObject.h"
  31. #include "Common/Radar.h"
  32. #include "Common/ThingFactory.h"
  33. #include "Common/ThingTemplate.h"
  34. #include "Common/WellKnownKeys.h"
  35. #include "Common/Xfer.h"
  36. #include "GameClient/TerrainVisual.h"
  37. #include "GameLogic/AI.h"
  38. #include "GameLogic/AIPathfind.h"
  39. #include "GameLogic/GameLogic.h"
  40. #include "GameLogic/Damage.h"
  41. #include "GameLogic/Object.h"
  42. #include "GameLogic/PartitionManager.h"
  43. #include "GameLogic/PolygonTrigger.h"
  44. #include "GameLogic/Scripts.h"
  45. #include "GameLogic/SidesList.h"
  46. #include "GameLogic/TerrainLogic.h"
  47. #include "GameLogic/Module/BodyModule.h"
  48. #include "GameLogic/Module/BridgeBehavior.h"
  49. #include "GameLogic/Module/BridgeTowerBehavior.h"
  50. #include "GameLogic/GhostObject.h"
  51. #include "WWMath/plane.h"
  52. #include "WWMath/tri.h"
  53. #ifdef _INTERNAL
  54. // for occasional debugging...
  55. //#pragma optimize("", off)
  56. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  57. #endif
  58. // GLOBALS ////////////////////////////////////////////////////////////////////////////////////////
  59. TerrainLogic *TheTerrainLogic = NULL;
  60. // STATIC /////////////////////////////////////////////////////////////////////////////////////////
  61. WaterHandle TerrainLogic::m_gridWaterHandle;
  62. // Waypoint ///////////////////////////////////////////////////////////////////////////////////////
  63. //-------------------------------------------------------------------------------------------------
  64. //-------------------------------------------------------------------------------------------------
  65. Waypoint::Waypoint(WaypointID id, AsciiString name, const Coord3D *pLoc, AsciiString label1, AsciiString label2,
  66. AsciiString label3, Bool biDirectional) :
  67. m_name(name),
  68. m_pNext(NULL),
  69. m_location(*pLoc),
  70. m_id(id),
  71. m_pathLabel1(label1),
  72. m_pathLabel2(label2),
  73. m_pathLabel3(label3),
  74. m_numLinks(0),
  75. m_biDirectional(biDirectional)
  76. {
  77. Int i;
  78. for (i=0; i<MAX_LINKS; i++) {
  79. m_links[i] = NULL;
  80. }
  81. } // end Waypoint
  82. //-------------------------------------------------------------------------------------------------
  83. //-------------------------------------------------------------------------------------------------
  84. Waypoint::~Waypoint()
  85. {
  86. } // end ~Waypoint
  87. // Bridge ////////////////////////////////////////////////////////////////////////////////////////
  88. // ------------------------------------------------------------------------------------------------
  89. // ------------------------------------------------------------------------------------------------
  90. BridgeInfo::BridgeInfo()
  91. {
  92. from.zero();
  93. to.zero();
  94. bridgeWidth = 0.0f;
  95. fromLeft.zero();
  96. fromRight.zero();
  97. toLeft.zero();
  98. toRight.zero();
  99. bridgeIndex = 0;
  100. curDamageState = BODY_PRISTINE;
  101. damageStateChanged = FALSE;
  102. bridgeObjectID = INVALID_ID;
  103. for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i )
  104. towerObjectID[ i ] = INVALID_ID;
  105. }
  106. // ------------------------------------------------------------------------------------------------
  107. /** Create a tower object for the bridge of the specified type (and therefore position) */
  108. // ------------------------------------------------------------------------------------------------
  109. Object *Bridge::createTower( Coord3D *worldPos,
  110. BridgeTowerType towerType,
  111. const ThingTemplate *towerTemplate,
  112. Object *bridge )
  113. {
  114. // sanity
  115. if( towerTemplate == NULL || bridge == NULL )
  116. {
  117. DEBUG_CRASH(( "Bridge::createTower(): Invalid params\n" ));
  118. return NULL;
  119. } // end if
  120. // create the tower object
  121. Object *tower = TheThingFactory->newObject( towerTemplate, bridge->getTeam() );
  122. // location information
  123. Real angle = 0;
  124. switch( towerType )
  125. {
  126. // --------------------------------------------------------------------------------------------
  127. case BRIDGE_TOWER_FROM_LEFT:
  128. angle = bridge->getOrientation() + PI;
  129. break;
  130. // --------------------------------------------------------------------------------------------
  131. case BRIDGE_TOWER_FROM_RIGHT:
  132. angle = bridge->getOrientation() + PI;
  133. break;
  134. // --------------------------------------------------------------------------------------------
  135. case BRIDGE_TOWER_TO_LEFT:
  136. angle = bridge->getOrientation();
  137. break;
  138. // --------------------------------------------------------------------------------------------
  139. case BRIDGE_TOWER_TO_RIGHT:
  140. angle = bridge->getOrientation();
  141. break;
  142. // --------------------------------------------------------------------------------------------
  143. default:
  144. DEBUG_CRASH(( "Bridge::createTower - Unknown bridge tower type '%d'\n", towerType ));
  145. return NULL;
  146. } // end switch
  147. // set the position and angle
  148. tower->setPosition( worldPos );
  149. tower->setOrientation( angle );
  150. // tie it to the bridge
  151. BridgeBehaviorInterface *bridgeInterface = BridgeBehavior::getBridgeBehaviorInterfaceFromObject( bridge );
  152. DEBUG_ASSERTCRASH( bridgeInterface != NULL, ("Bridge::createTower - no 'BridgeBehaviorInterface' found\n") );
  153. if( bridgeInterface )
  154. bridgeInterface->setTower( towerType, tower );
  155. // tie the bridge to us
  156. BridgeTowerBehaviorInterface *bridgeTowerInterface = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( tower );
  157. DEBUG_ASSERTCRASH( bridgeTowerInterface != NULL, ("Bridge::createTower - no 'BridgeTowerBehaviorInterface' found\n") );
  158. if( bridgeTowerInterface )
  159. {
  160. // set bridge object
  161. bridgeTowerInterface->setBridge( bridge );
  162. // save our position type
  163. bridgeTowerInterface->setTowerType( towerType );
  164. } // end if
  165. // if the bridge is indestructible, so is this tower
  166. BodyModuleInterface *bridgeBody = bridge->getBodyModule();
  167. if( bridgeBody->isIndestructible() )
  168. {
  169. BodyModuleInterface *towerBody = tower->getBodyModule();
  170. towerBody->setIndestructible( TRUE );
  171. } // end if
  172. // return the newly created tower
  173. return tower;
  174. } // end createTower
  175. //-------------------------------------------------------------------------------------------------
  176. //-------------------------------------------------------------------------------------------------
  177. Bridge::Bridge(BridgeInfo &theInfo, Dict *props, AsciiString bridgeTemplateName) :
  178. m_bridgeInfo(theInfo)
  179. {
  180. // save the template name
  181. m_templateName = bridgeTemplateName;
  182. //Coord3D fromLeft, fromRight, toLeft, toRight; /// The 4 corners of the rectangle that the bridge covers.
  183. m_bounds.lo.x = m_bridgeInfo.fromLeft.x;
  184. m_bounds.lo.y = m_bridgeInfo.fromLeft.y;
  185. m_bounds.hi = m_bounds.lo;
  186. if (m_bounds.lo.x > m_bridgeInfo.fromRight.x) m_bounds.lo.x = m_bridgeInfo.fromRight.x;
  187. if (m_bounds.lo.y > m_bridgeInfo.fromRight.y) m_bounds.lo.y = m_bridgeInfo.fromRight.y;
  188. if (m_bounds.hi.x < m_bridgeInfo.fromRight.x) m_bounds.hi.x = m_bridgeInfo.fromRight.x;
  189. if (m_bounds.hi.y < m_bridgeInfo.fromRight.y) m_bounds.hi.y = m_bridgeInfo.fromRight.y;
  190. if (m_bounds.lo.x > m_bridgeInfo.toLeft.x) m_bounds.lo.x = m_bridgeInfo.toLeft.x;
  191. if (m_bounds.lo.y > m_bridgeInfo.toLeft.y) m_bounds.lo.y = m_bridgeInfo.toLeft.y;
  192. if (m_bounds.hi.x < m_bridgeInfo.toLeft.x) m_bounds.hi.x = m_bridgeInfo.toLeft.x;
  193. if (m_bounds.hi.y < m_bridgeInfo.toLeft.y) m_bounds.hi.y = m_bridgeInfo.toLeft.y;
  194. if (m_bounds.lo.x > m_bridgeInfo.toRight.x) m_bounds.lo.x = m_bridgeInfo.toRight.x;
  195. if (m_bounds.lo.y > m_bridgeInfo.toRight.y) m_bounds.lo.y = m_bridgeInfo.toRight.y;
  196. if (m_bounds.hi.x < m_bridgeInfo.toRight.x) m_bounds.hi.x = m_bridgeInfo.toRight.x;
  197. if (m_bounds.hi.y < m_bridgeInfo.toRight.y) m_bounds.hi.y = m_bridgeInfo.toRight.y;
  198. m_bridgeInfo.curDamageState = BODY_PRISTINE;
  199. static const ThingTemplate* genericBridgeTemplate = TheThingFactory->findTemplate("GenericBridge");
  200. if (!genericBridgeTemplate) {
  201. DEBUG_LOG(("*** GenericBridge template not found."));
  202. return;
  203. }
  204. Object *bridge = TheThingFactory->newObject(genericBridgeTemplate, NULL);
  205. Coord3D center;
  206. center.x = (m_bridgeInfo.fromLeft.x + m_bridgeInfo.toRight.x)/2.0f;
  207. center.y = (m_bridgeInfo.fromLeft.y + m_bridgeInfo.toRight.y)/2.0f;
  208. center.z = (m_bridgeInfo.fromLeft.z + m_bridgeInfo.toRight.z)/2.0f;
  209. bridge->setPosition(&center);
  210. m_bridgeInfo.bridgeObjectID = bridge->getID();
  211. bridge->updateObjValuesFromMapProperties(props);
  212. //
  213. // we'll say the angle of this object representing the bridge is from the 'from' side
  214. // to the 'to' side.
  215. //
  216. Coord2D v;
  217. v.x = m_bridgeInfo.toLeft.x - m_bridgeInfo.fromLeft.x;
  218. v.y = m_bridgeInfo.toLeft.y - m_bridgeInfo.fromLeft.y;
  219. bridge->setOrientation( v.toAngle() );
  220. v.x = m_bridgeInfo.toLeft.x - m_bridgeInfo.toRight.x;
  221. v.y = m_bridgeInfo.toLeft.y - m_bridgeInfo.toRight.y;
  222. v.normalize();
  223. // get the template of the bridge
  224. TerrainRoadType *bridgeTemplate = TheTerrainRoads->findBridge( bridgeTemplateName );
  225. if( bridgeTemplate == NULL ) {
  226. DEBUG_LOG(( "*** Bridge Template Not Found '%s'.", bridgeTemplateName ));
  227. return;
  228. }
  229. #define no_BRIDGE_TOWERS // since they aren't destructable, don't need towers.
  230. #if BRIDGE_TOWERS
  231. // initialize each of the tower positions to that of the bridge info bounding rect
  232. Coord3D towerPos[ BRIDGE_MAX_TOWERS ];
  233. towerPos[ BRIDGE_TOWER_FROM_LEFT ] = m_bridgeInfo.fromLeft;
  234. towerPos[ BRIDGE_TOWER_FROM_RIGHT ] = m_bridgeInfo.fromRight;
  235. towerPos[ BRIDGE_TOWER_TO_LEFT ] = m_bridgeInfo.toLeft;
  236. towerPos[ BRIDGE_TOWER_TO_RIGHT ] = m_bridgeInfo.toRight;
  237. // create objects targetable objects for the 4 tower pieces
  238. const ThingTemplate *towerTemplate;
  239. BridgeTowerType type;
  240. Object *tower;
  241. Real offset = PATHFIND_CELL_SIZE_F/2.0f;
  242. for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i )
  243. {
  244. // create the tower
  245. type = (BridgeTowerType)i;
  246. towerTemplate = TheThingFactory->findTemplate( bridgeTemplate->getTowerObjectName( type ) );
  247. if (towerTemplate) {
  248. offset = towerTemplate->getTemplateGeometryInfo().getMajorRadius();
  249. }
  250. Coord3D pos = towerPos[type];
  251. switch( type )
  252. {
  253. case BRIDGE_TOWER_FROM_LEFT:
  254. case BRIDGE_TOWER_TO_LEFT:
  255. pos.x += v.x*offset;
  256. pos.y += v.y*offset;
  257. break;
  258. case BRIDGE_TOWER_FROM_RIGHT:
  259. case BRIDGE_TOWER_TO_RIGHT:
  260. pos.x -= v.x*offset;
  261. pos.y -= v.y*offset;
  262. break;
  263. } // end switch
  264. tower = createTower( &pos, type, towerTemplate, bridge );
  265. // store the tower object ID
  266. m_bridgeInfo.towerObjectID[ i ] = tower->getID();
  267. } // end for, i
  268. #endif
  269. m_next = NULL;
  270. } // end Bridge
  271. //-------------------------------------------------------------------------------------------------
  272. //-------------------------------------------------------------------------------------------------
  273. Bridge::Bridge(Object *bridgeObj)
  274. {
  275. // save the template name
  276. m_templateName = bridgeObj->getTemplate()->getName();
  277. DEBUG_ASSERTLOG( bridgeObj->getGeometryInfo().getGeomType()==GEOMETRY_BOX, ("Bridges need to be rectangles.\n"));
  278. const Coord3D *pos = bridgeObj->getPosition();
  279. Real angle = bridgeObj->getOrientation();
  280. Real halfsizeX = bridgeObj->getGeometryInfo().getMajorRadius();
  281. Real halfsizeY = bridgeObj->getGeometryInfo().getMinorRadius();
  282. m_bridgeInfo.bridgeWidth = 2*halfsizeY;
  283. Real c = (Real)Cos(angle);
  284. Real s = (Real)Sin(angle);
  285. m_bridgeInfo.fromLeft.set(pos->x-halfsizeX*c-halfsizeY*s, pos->y + halfsizeY*c - halfsizeX*s, pos->z);
  286. m_bridgeInfo.toLeft.set(pos->x+halfsizeX*c-halfsizeY*s, pos->y + halfsizeY*c + halfsizeX*s, pos->z);
  287. m_bridgeInfo.fromRight.set(pos->x-halfsizeX*c+halfsizeY*s, pos->y - halfsizeY*c - halfsizeX*s, pos->z);
  288. m_bridgeInfo.toRight.set(pos->x+halfsizeX*c+halfsizeY*s, pos->y - halfsizeY*c + halfsizeX*s, pos->z);
  289. m_bridgeInfo.from.x = (m_bridgeInfo.fromLeft.x + m_bridgeInfo.fromRight.x)/2.0f;
  290. m_bridgeInfo.from.y = (m_bridgeInfo.fromLeft.y + m_bridgeInfo.fromRight.y)/2.0f;
  291. m_bridgeInfo.from.z = (m_bridgeInfo.fromLeft.z + m_bridgeInfo.fromRight.z)/2.0f;
  292. m_bridgeInfo.to.x = (m_bridgeInfo.toLeft.x + m_bridgeInfo.toRight.x)/2.0f;
  293. m_bridgeInfo.to.y = (m_bridgeInfo.toLeft.y + m_bridgeInfo.toRight.y)/2.0f;
  294. m_bridgeInfo.to.z = (m_bridgeInfo.toLeft.z + m_bridgeInfo.toRight.z)/2.0f;
  295. //Coord3D fromLeft, fromRight, toLeft, toRight; /// The 4 corners of the rectangle that the bridge covers.
  296. m_bounds.lo.x = m_bridgeInfo.fromLeft.x;
  297. m_bounds.lo.y = m_bridgeInfo.fromLeft.y;
  298. m_bounds.hi = m_bounds.lo;
  299. if (m_bounds.lo.x > m_bridgeInfo.fromRight.x) m_bounds.lo.x = m_bridgeInfo.fromRight.x;
  300. if (m_bounds.lo.y > m_bridgeInfo.fromRight.y) m_bounds.lo.y = m_bridgeInfo.fromRight.y;
  301. if (m_bounds.hi.x < m_bridgeInfo.fromRight.x) m_bounds.hi.x = m_bridgeInfo.fromRight.x;
  302. if (m_bounds.hi.y < m_bridgeInfo.fromRight.y) m_bounds.hi.y = m_bridgeInfo.fromRight.y;
  303. if (m_bounds.lo.x > m_bridgeInfo.toLeft.x) m_bounds.lo.x = m_bridgeInfo.toLeft.x;
  304. if (m_bounds.lo.y > m_bridgeInfo.toLeft.y) m_bounds.lo.y = m_bridgeInfo.toLeft.y;
  305. if (m_bounds.hi.x < m_bridgeInfo.toLeft.x) m_bounds.hi.x = m_bridgeInfo.toLeft.x;
  306. if (m_bounds.hi.y < m_bridgeInfo.toLeft.y) m_bounds.hi.y = m_bridgeInfo.toLeft.y;
  307. if (m_bounds.lo.x > m_bridgeInfo.toRight.x) m_bounds.lo.x = m_bridgeInfo.toRight.x;
  308. if (m_bounds.lo.y > m_bridgeInfo.toRight.y) m_bounds.lo.y = m_bridgeInfo.toRight.y;
  309. if (m_bounds.hi.x < m_bridgeInfo.toRight.x) m_bounds.hi.x = m_bridgeInfo.toRight.x;
  310. if (m_bounds.hi.y < m_bridgeInfo.toRight.y) m_bounds.hi.y = m_bridgeInfo.toRight.y;
  311. m_bridgeInfo.curDamageState = BODY_PRISTINE;
  312. m_bridgeInfo.bridgeObjectID = bridgeObj->getID();
  313. // get the template of the bridge
  314. AsciiString bridgeTemplateName = bridgeObj->getTemplate()->getName();
  315. TerrainRoadType *bridgeTemplate = TheTerrainRoads->findBridge( bridgeTemplateName );
  316. if( bridgeTemplate == NULL ) {
  317. DEBUG_LOG(( "*** Bridge Template Not Found '%s'.", bridgeTemplateName ));
  318. return;
  319. }
  320. Coord2D v;
  321. v.x = m_bridgeInfo.toLeft.x - m_bridgeInfo.toRight.x;
  322. v.y = m_bridgeInfo.toLeft.y - m_bridgeInfo.toRight.y;
  323. v.normalize();
  324. // initialize each of the tower positions to that of the bridge info bounding rect
  325. Coord3D towerPos[ BRIDGE_MAX_TOWERS ];
  326. towerPos[ BRIDGE_TOWER_FROM_LEFT ] = m_bridgeInfo.fromLeft;
  327. towerPos[ BRIDGE_TOWER_FROM_RIGHT ] = m_bridgeInfo.fromRight;
  328. towerPos[ BRIDGE_TOWER_TO_LEFT ] = m_bridgeInfo.toLeft;
  329. towerPos[ BRIDGE_TOWER_TO_RIGHT ] = m_bridgeInfo.toRight;
  330. Real offset = PATHFIND_CELL_SIZE_F/2.0f;
  331. // create objects targetable objects for the 4 tower pieces
  332. const ThingTemplate *towerTemplate;
  333. BridgeTowerType type;
  334. Object *tower;
  335. for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i )
  336. {
  337. type = (BridgeTowerType)i;
  338. towerTemplate = TheThingFactory->findTemplate( bridgeTemplate->getTowerObjectName( type ) );
  339. if (towerTemplate) {
  340. offset = towerTemplate->getTemplateGeometryInfo().getMajorRadius();
  341. }
  342. Coord3D pos = towerPos[type];
  343. switch( type )
  344. {
  345. case BRIDGE_TOWER_FROM_LEFT:
  346. case BRIDGE_TOWER_TO_LEFT:
  347. pos.x += v.x*offset;
  348. pos.y += v.y*offset;
  349. break;
  350. case BRIDGE_TOWER_FROM_RIGHT:
  351. case BRIDGE_TOWER_TO_RIGHT:
  352. pos.x -= v.x*offset;
  353. pos.y -= v.y*offset;
  354. break;
  355. } // end switch
  356. tower = createTower( &pos, type, towerTemplate, bridgeObj );
  357. if( tower )
  358. {
  359. // store the tower object ID
  360. m_bridgeInfo.towerObjectID[ i ] = tower->getID();
  361. }
  362. } // end for, i
  363. m_next = NULL;
  364. } // end Bridge
  365. //-------------------------------------------------------------------------------------------------
  366. //-------------------------------------------------------------------------------------------------
  367. Bridge::~Bridge()
  368. {
  369. } // end ~Bridge
  370. //-------------------------------------------------------------------------------------------------
  371. /** isPointOnBridge - see if point is on bridge. */
  372. //-------------------------------------------------------------------------------------------------
  373. Bool Bridge::isPointOnBridge(const Coord3D *pLoc)
  374. {
  375. if (pLoc->x < m_bounds.lo.x) return(false);
  376. if (pLoc->x > m_bounds.hi.x) return(false);
  377. if (pLoc->y < m_bounds.lo.y) return(false);
  378. if (pLoc->y > m_bounds.hi.y) return(false);
  379. Vector3 testPt(pLoc->x, pLoc->y, pLoc->z);
  380. Vector3 left1(m_bridgeInfo.fromLeft.x, m_bridgeInfo.fromLeft.y, m_bridgeInfo.fromLeft.z);
  381. Vector3 right1(m_bridgeInfo.fromRight.x, m_bridgeInfo.fromRight.y, m_bridgeInfo.fromRight.z);
  382. Vector3 left2(m_bridgeInfo.toLeft.x, m_bridgeInfo.toLeft.y, m_bridgeInfo.toLeft.z);
  383. Vector3 right2(m_bridgeInfo.toRight.x, m_bridgeInfo.toRight.y, m_bridgeInfo.toRight.z);
  384. unsigned char flags;
  385. if (Point_In_Triangle_2D(left1, right1, left2, testPt, 0, 1, flags)) {
  386. return true;
  387. }
  388. if (Point_In_Triangle_2D(right1, left2, right2, testPt, 0, 1, flags)) {
  389. return true;
  390. }
  391. return(false);
  392. }
  393. /*-------------------------------------------------------------------------------------------------
  394. /** Clip a floating point line to the region provided. The source line runs from p1 to p2, and is clipped
  395. * using the clipRegion.
  396. *
  397. * Return values:
  398. * TRUE - Line intersects the region
  399. * FALSE - Line does not intersect the region
  400. */
  401. //-------------------------------------------------------------------------------------------------
  402. Bool LineInRegion( const Coord2D *p1, const Coord2D *p2, const Region2D *clipRegion )
  403. {
  404. enum { CLIP_LEFT = 0x01,
  405. CLIP_RIGHT = 0x02,
  406. CLIP_BOTTOM = 0x04,
  407. CLIP_TOP = 0x08 };
  408. Real x1, y1, x2, y2;
  409. Real clipLeft;
  410. Real clipRight;
  411. Real clipTop;
  412. Real clipBottom;
  413. Int clipCode1;
  414. Int clipCode2;
  415. Real diff;
  416. // Use clip window that includes bottom right pixel
  417. clipLeft = clipRegion->lo.x;
  418. clipRight = clipRegion->hi.x;
  419. clipTop = clipRegion->lo.y;
  420. clipBottom = clipRegion->hi.y;
  421. x1 = p1->x;
  422. y1 = p1->y;
  423. x2 = p2->x;
  424. y2 = p2->y;
  425. // Test first point
  426. clipCode1 = 0;
  427. if (x1 < clipLeft)
  428. clipCode1 = CLIP_LEFT;
  429. else
  430. if (x1 > clipRight)
  431. clipCode1 = CLIP_RIGHT;
  432. if (y1 < clipTop)
  433. clipCode1 |= CLIP_TOP;
  434. else
  435. if (y1 > clipBottom)
  436. clipCode1 |= CLIP_BOTTOM;
  437. // Test second point
  438. clipCode2 = 0;
  439. if (x2 < clipLeft)
  440. clipCode2 = CLIP_LEFT;
  441. else
  442. if (x2 > clipRight)
  443. clipCode2 = CLIP_RIGHT;
  444. if (y2 < clipTop)
  445. clipCode2 |= CLIP_TOP;
  446. else
  447. if (y2 > clipBottom)
  448. clipCode2 |= CLIP_BOTTOM;
  449. // Both points inside window?
  450. if ((clipCode1 | clipCode2) == 0)
  451. {
  452. return TRUE;
  453. } // end if
  454. // Both points outside window?
  455. if (clipCode1 & clipCode2)
  456. return FALSE;
  457. // First point outside window?
  458. if (clipCode1)
  459. {
  460. if (clipCode1 & CLIP_TOP)
  461. {
  462. if ((diff = (y2 - y1)) == 0)
  463. return FALSE;
  464. x1 += (x2 - x1) * (clipTop - y1) / diff;
  465. y1 = clipTop;
  466. }
  467. else
  468. if (clipCode1 & CLIP_BOTTOM)
  469. {
  470. if ((diff = (y2 - y1)) == 0)
  471. return FALSE;
  472. x1 += (x2 - x1) * (clipBottom - y1) / diff;
  473. y1 = clipBottom;
  474. }
  475. if (x1 > clipRight)
  476. {
  477. if ((diff = (x2 - x1)) == 0)
  478. return FALSE;
  479. y1 += (y2 - y1) * (clipRight - x1) / diff;
  480. x1 = clipRight;
  481. }
  482. else
  483. if (x1 < clipLeft)
  484. {
  485. if ((diff = (x2 - x1)) == 0)
  486. return FALSE;
  487. y1 += (y2 - y1) * (clipLeft - x1) / diff;
  488. x1 = clipLeft;
  489. }
  490. }
  491. // Second point outside window?
  492. if (clipCode2)
  493. {
  494. if (clipCode2 & CLIP_TOP)
  495. {
  496. if ((diff = (y2 - y1)) == 0)
  497. return FALSE;
  498. x2 += (x2 - x1) * (clipTop - y2) / diff;
  499. y2 = clipTop;
  500. }
  501. else
  502. if (clipCode2 & CLIP_BOTTOM)
  503. {
  504. if ((diff = (y2 - y1)) == 0)
  505. return FALSE;
  506. x2 += (x2 - x1) * (clipBottom - y2) / diff;
  507. y2 = clipBottom;
  508. }
  509. if (x2 > clipRight)
  510. {
  511. if ((diff = (x2 - x1)) == 0)
  512. return FALSE;
  513. y2 += (y2 - y1) * (clipRight - x2) / diff;
  514. x2 = clipRight;
  515. }
  516. else
  517. if (x2 < clipLeft)
  518. {
  519. if ((diff = (x2 - x1)) == 0)
  520. return FALSE;
  521. y2 += (y2 - y1) * (clipLeft - x2) / diff;
  522. x2 = clipLeft;
  523. }
  524. }
  525. // Line is visible
  526. return (x1 >= clipLeft && x1 <= clipRight &&
  527. y1 >= clipTop && y1 <= clipBottom &&
  528. x2 >= clipLeft && x2 <= clipRight &&
  529. y2 >= clipTop && y2 <= clipBottom);
  530. } // end LineInRegion
  531. static Bool PointInRegion2D( const Coord3D *pt, const Region2D *clipRegion )
  532. {
  533. return (pt->x>=clipRegion->lo.x &&
  534. pt->y>=clipRegion->lo.y &&
  535. pt->x<=clipRegion->hi.x &&
  536. pt->y<=clipRegion->hi.y);
  537. }
  538. //-------------------------------------------------------------------------------------------------
  539. /** isCellOnEnd - see if cell is on the end of the bridge. */
  540. //-------------------------------------------------------------------------------------------------
  541. Bool Bridge::isCellOnEnd(const Region2D *cell)
  542. {
  543. Coord3D endVector;
  544. endVector.x = m_bridgeInfo.fromRight.x - m_bridgeInfo.fromLeft.x;
  545. endVector.y = m_bridgeInfo.fromRight.y - m_bridgeInfo.fromLeft.y;
  546. endVector.z = m_bridgeInfo.fromRight.z - m_bridgeInfo.fromLeft.z;
  547. endVector.normalize();
  548. // Offset by 1 pathfind cell.
  549. endVector.x *= PATHFIND_CELL_SIZE;
  550. endVector.y *= PATHFIND_CELL_SIZE;
  551. Coord3D fromLeft = m_bridgeInfo.fromLeft;
  552. fromLeft.x += endVector.x;
  553. fromLeft.y += endVector.y;
  554. Coord3D fromRight = m_bridgeInfo.fromRight;
  555. fromRight.x -= endVector.x;
  556. fromRight.y -= endVector.y;
  557. Coord3D toLeft = m_bridgeInfo.toLeft;
  558. toLeft.x += endVector.x;
  559. toLeft.y += endVector.y;
  560. Coord3D toRight = m_bridgeInfo.toRight;
  561. toRight.x -= endVector.x;
  562. toRight.y -= endVector.y;
  563. /* if (PointInRegion2D(&fromLeft, cell)) return false;
  564. if (PointInRegion2D(&fromRight, cell)) return false;
  565. if (PointInRegion2D(&toLeft, cell)) return false;
  566. if (PointInRegion2D(&toRight, cell)) return false; */
  567. Coord2D line1, line2;
  568. line1.x = fromLeft.x;
  569. line1.y = fromLeft.y;
  570. line2.x = fromRight.x;
  571. line2.y = fromRight.y;
  572. if (LineInRegion(&line1, &line2, cell)) {
  573. return true;
  574. }
  575. line1.x = toLeft.x;
  576. line1.y = toLeft.y;
  577. line2.x = toRight.x;
  578. line2.y = toRight.y;
  579. if (LineInRegion(&line1, &line2, cell)) {
  580. return true;
  581. }
  582. return(false);
  583. }
  584. //-------------------------------------------------------------------------------------------------
  585. /** isCellOnSide - see if cell is on the end of the bridge. */
  586. //-------------------------------------------------------------------------------------------------
  587. Bool Bridge::isCellOnSide(const Region2D *cell)
  588. {
  589. Coord3D endVector;
  590. endVector.x = m_bridgeInfo.fromRight.x - m_bridgeInfo.fromLeft.x;
  591. endVector.y = m_bridgeInfo.fromRight.y - m_bridgeInfo.fromLeft.y;
  592. endVector.z = m_bridgeInfo.fromRight.z - m_bridgeInfo.fromLeft.z;
  593. endVector.normalize();
  594. // Offset by 1 pathfind cell.
  595. endVector.x *= PATHFIND_CELL_SIZE*0.51f;
  596. endVector.y *= PATHFIND_CELL_SIZE*0.51f;
  597. Coord3D fromLeft = m_bridgeInfo.fromLeft;
  598. fromLeft.x -= endVector.x;
  599. fromLeft.y -= endVector.y;
  600. Coord3D fromRight = m_bridgeInfo.fromRight;
  601. fromRight.x += endVector.x;
  602. fromRight.y += endVector.y;
  603. Coord3D toLeft = m_bridgeInfo.toLeft;
  604. toLeft.x -= endVector.x;
  605. toLeft.y -= endVector.y;
  606. Coord3D toRight = m_bridgeInfo.toRight;
  607. toRight.x += endVector.x;
  608. toRight.y += endVector.y;
  609. Coord2D line1, line2;
  610. line1.x = fromLeft.x;
  611. line1.y = fromLeft.y;
  612. line2.x = toLeft.x;
  613. line2.y = toLeft.y;
  614. if (LineInRegion(&line1, &line2, cell)) {
  615. return true;
  616. }
  617. line1.x = fromRight.x;
  618. line1.y = fromRight.y;
  619. line2.x = toRight.x;
  620. line2.y = toRight.y;
  621. if (LineInRegion(&line1, &line2, cell)) {
  622. return true;
  623. }
  624. fromLeft.x -= endVector.x;
  625. fromLeft.y -= endVector.y;
  626. fromRight.x += endVector.x;
  627. fromRight.y += endVector.y;
  628. toLeft.x -= endVector.x;
  629. toLeft.y -= endVector.y;
  630. toRight.x += endVector.x;
  631. toRight.y += endVector.y;
  632. line1.x = fromLeft.x;
  633. line1.y = fromLeft.y;
  634. line2.x = toLeft.x;
  635. line2.y = toLeft.y;
  636. if (LineInRegion(&line1, &line2, cell)) {
  637. return true;
  638. }
  639. line1.x = fromRight.x;
  640. line1.y = fromRight.y;
  641. line2.x = toRight.x;
  642. line2.y = toRight.y;
  643. if (LineInRegion(&line1, &line2, cell)) {
  644. return true;
  645. }
  646. return(false);
  647. }
  648. //-------------------------------------------------------------------------------------------------
  649. /** isCellEntryPoint - Is a pathfind cell a spot to move onto the bridge. */
  650. //-------------------------------------------------------------------------------------------------
  651. Bool Bridge::isCellEntryPoint(const Region2D *cell)
  652. {
  653. Coord3D endVector;
  654. endVector.x = m_bridgeInfo.fromRight.x - m_bridgeInfo.fromLeft.x;
  655. endVector.y = m_bridgeInfo.fromRight.y - m_bridgeInfo.fromLeft.y;
  656. endVector.z = m_bridgeInfo.fromRight.z - m_bridgeInfo.fromLeft.z;
  657. endVector.normalize();
  658. // Offset by 1 pathfind cell.
  659. endVector.x *= PATHFIND_CELL_SIZE;
  660. endVector.y *= PATHFIND_CELL_SIZE;
  661. Coord3D bridgeVector;
  662. bridgeVector.x = m_bridgeInfo.to.x - m_bridgeInfo.from.x;
  663. bridgeVector.y = m_bridgeInfo.to.y - m_bridgeInfo.from.y;
  664. bridgeVector.z = m_bridgeInfo.to.z - m_bridgeInfo.from.z;
  665. bridgeVector.normalize();
  666. // Offset by 1/2 pathfind cell.
  667. bridgeVector.x *= PATHFIND_CELL_SIZE/2;
  668. bridgeVector.y *= PATHFIND_CELL_SIZE/2;
  669. Coord3D fromLeft = m_bridgeInfo.fromLeft;
  670. fromLeft.x -= bridgeVector.x;
  671. fromLeft.y -= bridgeVector.y;
  672. fromLeft.x += endVector.x;
  673. fromLeft.y += endVector.y;
  674. Coord3D fromRight = m_bridgeInfo.fromRight;
  675. fromRight.x -= bridgeVector.x;
  676. fromRight.y -= bridgeVector.y;
  677. fromRight.x -= endVector.x;
  678. fromRight.y -= endVector.y;
  679. Coord3D toLeft = m_bridgeInfo.toLeft;
  680. toLeft.x += bridgeVector.x;
  681. toLeft.y += bridgeVector.y;
  682. toLeft.x += endVector.x;
  683. toLeft.y += endVector.y;
  684. Coord3D toRight = m_bridgeInfo.toRight;
  685. toRight.x += bridgeVector.x;
  686. toRight.y += bridgeVector.y;
  687. toRight.x -= endVector.x;
  688. toRight.y -= endVector.y;
  689. /* if (PointInRegion2D(&fromLeft, cell)) return false;
  690. if (PointInRegion2D(&fromRight, cell)) return false;
  691. if (PointInRegion2D(&toLeft, cell)) return false;
  692. if (PointInRegion2D(&toRight, cell)) return false;
  693. */
  694. Coord2D line1, line2;
  695. line1.x = fromLeft.x;
  696. line1.y = fromLeft.y;
  697. line2.x = fromRight.x;
  698. line2.y = fromRight.y;
  699. if (LineInRegion(&line1, &line2, cell)) {
  700. return true;
  701. }
  702. line1.x = toLeft.x;
  703. line1.y = toLeft.y;
  704. line2.x = toRight.x;
  705. line2.y = toRight.y;
  706. if (LineInRegion(&line1, &line2, cell)) {
  707. return true;
  708. }
  709. return(false);
  710. }
  711. //-------------------------------------------------------------------------------------------------
  712. /** pickBridge - see if point is on bridge. */
  713. //-------------------------------------------------------------------------------------------------
  714. Drawable *Bridge::pickBridge(const Vector3 &from, const Vector3 &to, Vector3 *pos)
  715. {
  716. Vector3 left1(m_bridgeInfo.fromLeft.x, m_bridgeInfo.fromLeft.y, m_bridgeInfo.fromLeft.z);
  717. Vector3 right1(m_bridgeInfo.fromRight.x, m_bridgeInfo.fromRight.y, m_bridgeInfo.fromRight.z);
  718. Vector3 left2(m_bridgeInfo.toLeft.x, m_bridgeInfo.toLeft.y, m_bridgeInfo.toLeft.z);
  719. PlaneClass plane(left1, right1, left2);
  720. Real t;
  721. plane.Compute_Intersection(from, to, &t);
  722. Vector3 intersectPos;
  723. intersectPos = from + (to-from) * t;
  724. Coord3D loc;
  725. loc.x = intersectPos.X;
  726. loc.y = intersectPos.Y;
  727. loc.z = intersectPos.Z;
  728. if (isPointOnBridge(&loc)) {
  729. *pos = intersectPos;
  730. //DEBUG_LOG(("Picked bridge %.2f, %.2f, %.2f\n", intersectPos.X, intersectPos.Y, intersectPos.Z));
  731. Object *bridge = TheGameLogic->findObjectByID(m_bridgeInfo.bridgeObjectID);
  732. if (bridge) {
  733. return bridge->getDrawable();
  734. }
  735. }
  736. return NULL;
  737. }
  738. //-------------------------------------------------------------------------------------------------
  739. /** updateDamageState - Update the damage state. */
  740. //-------------------------------------------------------------------------------------------------
  741. void Bridge::updateDamageState( void )
  742. {
  743. m_bridgeInfo.damageStateChanged = false;
  744. if (m_bridgeInfo.bridgeObjectID==0) return;
  745. Object *bridge = TheGameLogic->findObjectByID(m_bridgeInfo.bridgeObjectID);
  746. if (bridge) {
  747. // get object damage state
  748. {
  749. enum BodyDamageType damageState = bridge->getBodyModule()->getDamageState();
  750. enum BodyDamageType curState = m_bridgeInfo.curDamageState;
  751. if (damageState != curState) {
  752. m_bridgeInfo.curDamageState = damageState;
  753. if (damageState == BODY_RUBBLE) {
  754. TheAI->pathfinder()->changeBridgeState(m_layer, false);
  755. m_bridgeInfo.damageStateChanged = true;
  756. Object *obj;
  757. for (obj = TheGameLogic->getFirstObject(); obj; obj=obj->getNextObject()) {
  758. if (obj->getLayer() == m_layer) {
  759. // don't consider the bridge health, 'cuz it's already dead. (srj)
  760. const Bool considerBridgeHealth = false;
  761. if (TheTerrainLogic->objectInteractsWithBridgeLayer(obj, obj->getLayer(), considerBridgeHealth))
  762. {
  763. // srj sez: if we use this threshold, then stuff on the bridge apron doesn't die but
  764. // might sink thru the eyecandy of bridge drbris, looking funny. so now we just indiscriminately
  765. // kill everything that was on the bridge, regardless of height they might fall.
  766. //Real deltaHeight = obj->getPosition()->z - TheTerrainLogic->getGroundHeight(obj->getPosition()->x, obj->getPosition()->y);
  767. //if (deltaHeight>PATHFIND_CELL_SIZE_F * 0.5f)
  768. {
  769. // The object fell off the bridge.
  770. // Destroy it.
  771. DamageInfo extraDamageInfo;
  772. extraDamageInfo.in.m_damageType = DAMAGE_FALLING;
  773. extraDamageInfo.in.m_deathType = DEATH_SPLATTED;
  774. extraDamageInfo.in.m_sourceID = obj->getID();
  775. extraDamageInfo.in.m_amount = HUGE_DAMAGE_AMOUNT;
  776. obj->attemptDamage(&extraDamageInfo);
  777. }
  778. }
  779. }
  780. }
  781. }
  782. if (curState==BODY_RUBBLE) {
  783. //
  784. // we do not set the bridge as usable if scaffolding is up ... the scaffolding
  785. // code will take care of that
  786. //
  787. BridgeBehaviorInterface *bbi = BridgeBehavior::getBridgeBehaviorInterfaceFromObject( bridge );
  788. if( bbi == NULL || bbi->isScaffoldPresent() == FALSE )
  789. TheAI->pathfinder()->changeBridgeState(m_layer, true);
  790. m_bridgeInfo.damageStateChanged = true;
  791. }
  792. }
  793. }
  794. } else {
  795. m_bridgeInfo.bridgeObjectID = INVALID_ID;
  796. DEBUG_CRASH(("Bridge object disappeared - unexpected. jba."));
  797. }
  798. }
  799. //-------------------------------------------------------------------------------------------------
  800. /** getHeight - Get the height for an object on bridge.. */
  801. //-------------------------------------------------------------------------------------------------
  802. Real Bridge::getBridgeHeight(const Coord3D *pLoc, Coord3D* normal)
  803. {
  804. Vector3 left1(m_bridgeInfo.fromLeft.x, m_bridgeInfo.fromLeft.y, m_bridgeInfo.fromLeft.z);
  805. Vector3 right1(m_bridgeInfo.fromRight.x, m_bridgeInfo.fromRight.y, m_bridgeInfo.fromRight.z);
  806. Vector3 left2(m_bridgeInfo.toLeft.x, m_bridgeInfo.toLeft.y, m_bridgeInfo.toLeft.z);
  807. PlaneClass plane(left1, right1, left2);
  808. const Real factor = 1000.0f;
  809. Vector3 bottom(pLoc->x, pLoc->y, 0);
  810. Vector3 top(pLoc->x, pLoc->y, factor);
  811. Real t;
  812. plane.Compute_Intersection(bottom, top, &t);
  813. if (normal) {
  814. normal->x = plane.N.X;
  815. normal->y = plane.N.Y;
  816. normal->z = plane.N.Z;
  817. }
  818. return t*factor;
  819. }
  820. //-------------------------------------------------------------------------------------------------
  821. //-------------------------------------------------------------------------------------------------
  822. TerrainLogic::TerrainLogic()
  823. {
  824. Int i;
  825. //Added By Sadullah Nader
  826. //Initialization(s) inserted
  827. m_activeBoundary = 0;
  828. m_waterGridEnabled = FALSE;
  829. //
  830. for( i = 0; i < MAX_DYNAMIC_WATER; ++i )
  831. {
  832. m_waterToUpdate[ i ].waterTable = NULL;
  833. m_waterToUpdate[ i ].changePerFrame = 0.0f;
  834. m_waterToUpdate[ i ].targetHeight = 0.0f;
  835. m_waterToUpdate[ i ].damageAmount = 0.0f;
  836. m_waterToUpdate[ i ].currentHeight = 0.0f;
  837. } // end for i
  838. m_numWaterToUpdate = 0;
  839. m_waypointListHead = NULL;
  840. m_bridgeListHead = NULL;
  841. m_mapData = NULL;
  842. m_bridgeDamageStatesChanged = FALSE;
  843. m_mapDX = 0;
  844. m_mapDY = 0;
  845. } // end TerrainLogic
  846. //-------------------------------------------------------------------------------------------------
  847. //-------------------------------------------------------------------------------------------------
  848. TerrainLogic::~TerrainLogic()
  849. {
  850. reset(); // just in case
  851. } // end ~TerrainLogic
  852. //-------------------------------------------------------------------------------------------------
  853. /** Init */
  854. //-------------------------------------------------------------------------------------------------
  855. void TerrainLogic::init( void )
  856. {
  857. } // end init
  858. //-------------------------------------------------------------------------------------------------
  859. /** Reset */
  860. //-------------------------------------------------------------------------------------------------
  861. void TerrainLogic::reset( void )
  862. {
  863. deleteWaypoints();
  864. deleteBridges();
  865. PolygonTrigger::deleteTriggers();
  866. m_numWaterToUpdate = 0;
  867. } // end reset
  868. //-------------------------------------------------------------------------------------------------
  869. /** Update */
  870. //-------------------------------------------------------------------------------------------------
  871. void TerrainLogic::update( void )
  872. {
  873. // bridge damage states have not changed this frame now
  874. m_bridgeDamageStatesChanged = false;
  875. // update any water tables that we need to
  876. if( m_numWaterToUpdate )
  877. {
  878. const WaterHandle *water;
  879. Real changePerFrame,
  880. damageAmount,
  881. targetHeight,
  882. currentHeight;
  883. Bool finalTransition,
  884. doDamageThisFrame = (TheGameLogic->getFrame() % LOGICFRAMES_PER_SECOND) == 0;
  885. for( Int i = m_numWaterToUpdate - 1; i >= 0; --i )
  886. {
  887. // get the water info
  888. water = m_waterToUpdate[ i ].waterTable;
  889. changePerFrame = m_waterToUpdate[ i ].changePerFrame;
  890. targetHeight = m_waterToUpdate[ i ].targetHeight;
  891. damageAmount = m_waterToUpdate[ i ].damageAmount;
  892. currentHeight = m_waterToUpdate[ i ].currentHeight;
  893. //
  894. // check to see if this change per frame will get us to our target height, if so
  895. // we adjust the changePerFrame to make us be exactly at our target height, and after
  896. // the change we will remove our entry from this update phase
  897. //
  898. finalTransition = FALSE;
  899. if( changePerFrame > 0 )
  900. {
  901. if( currentHeight + changePerFrame >= targetHeight )
  902. finalTransition = TRUE;
  903. } // end if
  904. else
  905. {
  906. if( currentHeight + changePerFrame <= targetHeight )
  907. finalTransition = TRUE;
  908. } // end else
  909. if( finalTransition == TRUE )
  910. {
  911. //
  912. // make the final water height change, note we do damage on the final transition
  913. // in all situations by passing a valid damage amount
  914. //
  915. setWaterHeight( water, targetHeight, damageAmount, TRUE );
  916. //
  917. // remove our water entry from the per frame water list, we're processing this array
  918. // backwards which makes cleanup easy, we just move everything after our index
  919. // position up one
  920. //
  921. for( Int j = i; j < m_numWaterToUpdate; j++ )
  922. m_waterToUpdate[ i ] = m_waterToUpdate[ j ];
  923. m_numWaterToUpdate -= 1;
  924. } // end if
  925. else
  926. {
  927. //
  928. // we're not doing damage every frame (0 damage) from the water
  929. // because it's an expensive process
  930. //
  931. if( doDamageThisFrame == FALSE )
  932. damageAmount = 0.0f;
  933. //
  934. // because some water implementation store the height as integers, some changes
  935. // are too small to keep track of in the actual water data structures so we have to
  936. // keep track of it outselves
  937. //
  938. currentHeight += changePerFrame;
  939. m_waterToUpdate[ i ].currentHeight = currentHeight;
  940. // update actual water
  941. setWaterHeight( water, currentHeight, damageAmount, FALSE );
  942. } // end else
  943. } // end for i
  944. } // end if
  945. } // end update
  946. //-------------------------------------------------------------------------------------------------
  947. /** newMap */
  948. //-------------------------------------------------------------------------------------------------
  949. void TerrainLogic::newMap( Bool saveGame )
  950. {
  951. // Set waypoint's z value, now that the height map is loaded.
  952. for( Waypoint *way = m_waypointListHead; way; way = way->getNext() )
  953. {
  954. const Coord3D* loc = way->getLocation();
  955. way->setLocationZ(getGroundHeight(loc->x, loc->y));
  956. }
  957. //
  958. // until we have a real way to specify different water planes in the map, we will check
  959. // for a special waypoint name that we will put in maps that we want to have a
  960. // water grid
  961. /// @todo Mark W, remove this when you have water plane placements in the map done (Colin)
  962. //
  963. Waypoint *waypoint = getWaypointByName( "WaveGuide1" );
  964. Bool enable = FALSE;
  965. if( waypoint )
  966. enable = TRUE;
  967. enableWaterGrid( enable );
  968. } // end newMap
  969. // ------------------------------------------------------------------------------------------------
  970. // ------------------------------------------------------------------------------------------------
  971. void TerrainLogic::enableWaterGrid( Bool enable )
  972. {
  973. // set our internal variable we can query
  974. m_waterGridEnabled = enable;
  975. //
  976. // set the vertex animated water properties, that is, the clamps, the water position,
  977. // the grid resolution etc ...
  978. //
  979. if( enable == TRUE )
  980. {
  981. /** @todo we should have this stuff stored with the map and have a real interface for
  982. design to edit such things so that people can put gridded water in any map without all
  983. this hard coded nasty stuff, but this is what "they" want for now */
  984. Int waterSettingIndex = -1;
  985. for( Int i = 0; i < GlobalData::MAX_WATER_GRID_SETTINGS; i++ )
  986. {
  987. if( TheGlobalData->m_mapName.compareNoCase( TheGlobalData->m_vertexWaterAvailableMaps[ i ].str() ) == 0 )
  988. {
  989. waterSettingIndex = i;
  990. break; // exit for i
  991. } // end if
  992. //
  993. // no exact map name (including path) was found, try to look for a match in just the
  994. // mapname.map without any path information. This is necessary for save/load due to
  995. // the fact that the map Data\CHI01\CHI01.map will turn into Save\CHI01.map when
  996. // loading the map from a save game file
  997. //
  998. AsciiString strippedMapNameOnly;
  999. AsciiString strippedCompareMapNameOnly;
  1000. char *c;
  1001. // create stripped map name
  1002. c = strrchr( TheGlobalData->m_mapName.str(), '\\' );
  1003. if( c )
  1004. strippedMapNameOnly.set( c );
  1005. else
  1006. strippedMapNameOnly = TheGlobalData->m_mapName;
  1007. // create stripped compare name
  1008. c = strrchr( TheGlobalData->m_vertexWaterAvailableMaps[ i ].str(), '\\' );
  1009. if( c )
  1010. strippedCompareMapNameOnly.set( c );
  1011. else
  1012. strippedCompareMapNameOnly = TheGlobalData->m_vertexWaterAvailableMaps[ i ];
  1013. // now try this compare
  1014. if( strippedMapNameOnly.compareNoCase( strippedCompareMapNameOnly.str() ) == 0 )
  1015. {
  1016. waterSettingIndex = i;
  1017. break; // exit for i
  1018. } // end if
  1019. } // end for i
  1020. // check for no match found
  1021. if( waterSettingIndex == -1 )
  1022. {
  1023. DEBUG_CRASH(( "!!!!!! Deformable water won't work because there was no group of vertex water data defined in GameData.INI for this map name '%s' !!!!!! (C. Day)\n",
  1024. TheGlobalData->m_mapName.str() ));
  1025. return;
  1026. } // end if
  1027. TheTerrainVisual->setWaterGridHeightClamps( NULL,
  1028. TheGlobalData->m_vertexWaterHeightClampLow[ waterSettingIndex ],
  1029. TheGlobalData->m_vertexWaterHeightClampHi[ waterSettingIndex ] );
  1030. TheTerrainVisual->setWaterTransform( NULL,
  1031. TheGlobalData->m_vertexWaterAngle[ waterSettingIndex ],
  1032. TheGlobalData->m_vertexWaterXPosition[ waterSettingIndex ],
  1033. TheGlobalData->m_vertexWaterYPosition[ waterSettingIndex ],
  1034. TheGlobalData->m_vertexWaterZPosition[ waterSettingIndex ] );
  1035. TheTerrainVisual->setWaterGridResolution( NULL,
  1036. TheGlobalData->m_vertexWaterXGridCells[ waterSettingIndex ],
  1037. TheGlobalData->m_vertexWaterYGridCells[ waterSettingIndex ],
  1038. TheGlobalData->m_vertexWaterGridSize[ waterSettingIndex ] );
  1039. TheTerrainVisual->setWaterAttenuationFactors( NULL,
  1040. TheGlobalData->m_vertexWaterAttenuationA[ waterSettingIndex ],
  1041. TheGlobalData->m_vertexWaterAttenuationB[ waterSettingIndex ],
  1042. TheGlobalData->m_vertexWaterAttenuationC[ waterSettingIndex ],
  1043. TheGlobalData->m_vertexWaterAttenuationRange[ waterSettingIndex ] );
  1044. } // end if
  1045. // notify the terrain visual of the change
  1046. TheTerrainVisual->enableWaterGrid( enable );
  1047. } // end enableWaterGrid
  1048. //-------------------------------------------------------------------------------------------------
  1049. /** device independent terrain logic load. If query is true, we are just loading it to get
  1050. look at some data rather than running a game, so don't pass this load to the client. */
  1051. //-------------------------------------------------------------------------------------------------
  1052. Bool TerrainLogic::loadMap( AsciiString filename, Bool query )
  1053. {
  1054. // sanity
  1055. if( filename.isEmpty() )
  1056. return FALSE;
  1057. // copy filename
  1058. m_filenameString = filename;
  1059. // Add waypoint objects.
  1060. MapObject *pObj;
  1061. for (pObj = MapObject::getFirstMapObject(); pObj; pObj = pObj->getNext()) {
  1062. if (pObj->isWaypoint()) {
  1063. addWaypoint(pObj);
  1064. }
  1065. }
  1066. CachedFileInputStream theInputStream;
  1067. if (theInputStream.open(AsciiString(m_filenameString.str())))
  1068. try {
  1069. ChunkInputStream *pStrm = &theInputStream;
  1070. pStrm->absoluteSeek(0);
  1071. DataChunkInput file( pStrm );
  1072. if (file.isValidFileType()) { // Backwards compatible files aren't valid data chunk files.
  1073. // Read the waypoints.
  1074. file.registerParser( AsciiString("WaypointsList"), AsciiString::TheEmptyString, parseWaypointDataChunk );
  1075. if (!file.parse(this)) {
  1076. DEBUG_CRASH(("Unable to read waypoint info."));
  1077. return false;
  1078. }
  1079. }
  1080. theInputStream.close();
  1081. } catch (...) {
  1082. // Eat the error - legacy files are not valid chunk format (and don't have waypoint info.)
  1083. DEBUG_LOG(("Unable to read waypoint info."));
  1084. }
  1085. #if 0 //def DEBUG_LOGGING
  1086. // Dump out the waypoint links.
  1087. Waypoint *pWay;
  1088. // Traverse all waypoints.
  1089. int count = 0;
  1090. for (pWay = getFirstWaypoint(); pWay; pWay = pWay->getNext()) {
  1091. count++;
  1092. Coord3D loc;
  1093. pWay->getLocation(&loc);
  1094. DEBUG_LOG(("Waypoint %d - '%s' id=%d ", count, pWay->getName().str(), pWay->getID()));
  1095. DEBUG_LOG(("{%.2f, %.2f, %.2f} ", loc.x, loc.y, loc.z));
  1096. Int i;
  1097. if (pWay->getNumLinks()) {
  1098. DEBUG_LOG(("Links to: "));
  1099. for (i=0; i<pWay->getNumLinks(); i++) {
  1100. Waypoint *pLink = pWay->getLink(i);
  1101. DEBUG_LOG(("'%s' id=%d ", pLink->getName().str(), pLink->getID()));
  1102. }
  1103. } else {
  1104. DEBUG_LOG(("No links."));
  1105. }
  1106. DEBUG_LOG(("\n"));
  1107. }
  1108. DEBUG_LOG(("Total of %d waypoints.\n", count));
  1109. #endif
  1110. if (!query) {
  1111. // tell the game interface a new terrain file has been loaded up
  1112. TheTerrainVisual->load( getSourceFilename() );
  1113. }
  1114. return TRUE; // success
  1115. } // end load
  1116. //-------------------------------------------------------------------------------------------------
  1117. /** Reads in the waypoint chunk */
  1118. //-------------------------------------------------------------------------------------------------
  1119. Bool TerrainLogic::parseWaypointDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData)
  1120. {
  1121. TerrainLogic *pThis = (TerrainLogic *)userData;
  1122. return pThis->parseWaypointData(file, info, userData);
  1123. }
  1124. //-------------------------------------------------------------------------------------------------
  1125. /** Reads in the waypoint chunk */
  1126. //-------------------------------------------------------------------------------------------------
  1127. Bool TerrainLogic::parseWaypointData(DataChunkInput &file, DataChunkInfo *info, void *userData)
  1128. {
  1129. Int numWaypointLinks = file.readInt();
  1130. Int i;
  1131. for (i=0; i<numWaypointLinks; i++) {
  1132. Int waypoint1 = file.readInt();
  1133. Int waypoint2 = file.readInt();
  1134. addWaypointLink(waypoint1, waypoint2);
  1135. }
  1136. DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Unexpected data left over."));
  1137. return true;
  1138. }
  1139. //-------------------------------------------------------------------------------------------------
  1140. /** Adds one waypoint. */
  1141. //-------------------------------------------------------------------------------------------------
  1142. void TerrainLogic::addWaypoint(MapObject *pMapObj)
  1143. {
  1144. Coord3D loc = *pMapObj->getLocation();
  1145. // Snap the waypoint down to the terrain.
  1146. loc.z = getGroundHeight(loc.x, loc.y);
  1147. Bool exists;
  1148. AsciiString label1, label2, label3;
  1149. label1 = pMapObj->getProperties()->getAsciiString(TheKey_waypointPathLabel1, &exists);
  1150. label2 = pMapObj->getProperties()->getAsciiString(TheKey_waypointPathLabel2, &exists);
  1151. label3 = pMapObj->getProperties()->getAsciiString(TheKey_waypointPathLabel3, &exists);
  1152. Bool biDirectional;
  1153. biDirectional = pMapObj->getProperties()->getBool(TheKey_waypointPathBiDirectional, &exists);
  1154. DEBUG_ASSERTCRASH(pMapObj->isWaypoint(), ("not a waypoint"));
  1155. Waypoint *pWay = newInstance(Waypoint)(pMapObj->getWaypointID(), pMapObj->getWaypointName(),
  1156. &loc, label1, label2, label3, biDirectional);
  1157. pWay->setNext(m_waypointListHead);
  1158. m_waypointListHead = pWay;
  1159. }
  1160. //-------------------------------------------------------------------------------------------------
  1161. /** Links 2 waypoints. */
  1162. //-------------------------------------------------------------------------------------------------
  1163. void TerrainLogic::addWaypointLink(Int id1, Int id2)
  1164. {
  1165. Waypoint *pWay1 = NULL;
  1166. Waypoint *pWay2 = NULL;
  1167. Waypoint *pWay;
  1168. // Traverse all waypoints.
  1169. /// @todo ID's should be UnsignedInts (MSB)
  1170. for (pWay = getFirstWaypoint(); pWay; pWay = pWay->getNext()) {
  1171. if (pWay->getID() == (UnsignedInt)id1) {
  1172. pWay1 = pWay;
  1173. }
  1174. if (pWay->getID() == (UnsignedInt)id2) {
  1175. pWay2 = pWay;
  1176. }
  1177. }
  1178. if (pWay1 && pWay2 && (pWay1 != pWay2)) {
  1179. Int i;
  1180. for (i=0; i<pWay1->getNumLinks(); i++) {
  1181. if (pWay1->getLink(i) == pWay2) {
  1182. return; // already linked;
  1183. }
  1184. }
  1185. pWay1->addLink(pWay2);
  1186. if (pWay1->getBiDirectional()) {
  1187. // Link the other way.
  1188. for (i=0; i<pWay2->getNumLinks(); i++) {
  1189. if (pWay2->getLink(i) == pWay1) {
  1190. return; // already linked;
  1191. }
  1192. }
  1193. pWay2->addLink(pWay1);
  1194. }
  1195. }
  1196. }
  1197. //-------------------------------------------------------------------------------------------------
  1198. /** Deletes the waypoints list. */
  1199. //-------------------------------------------------------------------------------------------------
  1200. void TerrainLogic::deleteWaypoints(void)
  1201. {
  1202. Waypoint *pNext = NULL;
  1203. Waypoint *pWay;
  1204. // Traverse all waypoints.
  1205. for (pWay = getFirstWaypoint(); pWay; pWay = pNext) {
  1206. pNext = pWay->getNext();
  1207. pWay->setNext(NULL);
  1208. pWay->deleteInstance();
  1209. }
  1210. m_waypointListHead = NULL;
  1211. }
  1212. //-------------------------------------------------------------------------------------------------
  1213. Bool TerrainLogic::isClearLineOfSight(const Coord3D& pos, const Coord3D& posOther) const
  1214. {
  1215. DEBUG_CRASH(("implement ME"));
  1216. return false;
  1217. }
  1218. //-------------------------------------------------------------------------------------------------
  1219. /** default get height for terrain logic */
  1220. //-------------------------------------------------------------------------------------------------
  1221. Real TerrainLogic::getGroundHeight( Real x, Real y, Coord3D* normal ) const
  1222. {
  1223. if( normal )
  1224. normal->zero();
  1225. return 0;
  1226. } // end getHight
  1227. //-------------------------------------------------------------------------------------------------
  1228. /** default get height for terrain logic */
  1229. //-------------------------------------------------------------------------------------------------
  1230. Real TerrainLogic::getLayerHeight( Real x, Real y, PathfindLayerEnum layer, Coord3D* normal, Bool clip ) const
  1231. {
  1232. if( normal )
  1233. normal->zero();
  1234. return 0;
  1235. } // end getLayerHeight
  1236. //-------------------------------------------------------------------------------------------------
  1237. /** default isCliffCell for terrain logic */
  1238. //-------------------------------------------------------------------------------------------------
  1239. Bool TerrainLogic::isCliffCell( Real x, Real y) const
  1240. {
  1241. return false;
  1242. } // end isCliffCell
  1243. //-------------------------------------------------------------------------------------------------
  1244. void makeAlignToNormalMatrix( Real angle, const Coord3D& pos, const Coord3D& normal, Matrix3D& mtx)
  1245. {
  1246. Coord3D x, y, z;
  1247. z = normal;
  1248. /*
  1249. It is extremely important that the resulting matrix is such that
  1250. the xvector points in the angle we specified; specifically,
  1251. that atan2(xvec.y, xvec.x) == angle. So we must construct
  1252. the matrix carefully to ensure this!
  1253. */
  1254. x.x = Cos( angle );
  1255. x.y = Sin( angle );
  1256. x.z = 0.0f;
  1257. //x.normalize(); -- redundant; is normalized by definition
  1258. // dot of two unit vectors is cos of angle between them;
  1259. // we want there to be a 90-deg angle between the x and z
  1260. // vectors, so calc x.z to satisfy this (ie, cos==0)
  1261. /*
  1262. xx*zx + xy*zy + xz*zz = 0
  1263. xz = (-xx*zz - xy*zy)/zz
  1264. */
  1265. if (z.z != 0.0f)
  1266. {
  1267. x.z = -(x.x*z.x + x.y*z.y) / z.z;
  1268. x.normalize();
  1269. }
  1270. DEBUG_ASSERTCRASH(fabs(x.x*z.x + x.y*z.y + x.z*z.z)<0.0001,("dot is not zero (%f)\n",fabs(x.x*z.x + x.y*z.y + x.z*z.z)));
  1271. // now computing the y vector is trivial.
  1272. y.crossProduct( &z, &x, &y );
  1273. y.normalize();
  1274. mtx.Set( x.x, y.x, z.x, pos.x,
  1275. x.y, y.y, z.y, pos.y,
  1276. x.z, y.z, z.z, pos.z );
  1277. }
  1278. //-------------------------------------------------------------------------------------------------
  1279. /** given angle and position, return the matrix aligning this
  1280. * position with the ground */
  1281. //-------------------------------------------------------------------------------------------------
  1282. PathfindLayerEnum TerrainLogic::alignOnTerrain( Real angle, const Coord3D& pos, Bool stickToGround, Matrix3D& mtx)
  1283. {
  1284. Coord3D terrainNormal;
  1285. PathfindLayerEnum layer;
  1286. layer = getLayerForDestination(&pos);
  1287. // get the normal of the terrain at our position
  1288. Real terrainAtPos = getLayerHeight(pos.x, pos.y, layer, &terrainNormal );
  1289. if (layer != LAYER_GROUND) {
  1290. /// @todo - fix brutal hack for bridges that are too high. jba
  1291. terrainAtPos += 2.5f;
  1292. }
  1293. makeAlignToNormalMatrix(angle, pos, terrainNormal, mtx);
  1294. if (stickToGround)
  1295. mtx.Set_Z_Translation(terrainAtPos);
  1296. return layer;
  1297. }
  1298. //-------------------------------------------------------------------------------------------------
  1299. /** Adds a bridge's info get height function for logical terrain */
  1300. //-------------------------------------------------------------------------------------------------
  1301. void TerrainLogic::addBridgeToLogic(BridgeInfo *pInfo, Dict *props, AsciiString bridgeTemplateName)
  1302. {
  1303. Bridge *pBridge = newInstance(Bridge)(*pInfo, props, bridgeTemplateName);
  1304. pBridge->setNext(m_bridgeListHead);
  1305. m_bridgeListHead = pBridge;
  1306. PathfindLayerEnum layer = TheAI->pathfinder()->addBridge(pBridge);
  1307. pBridge->setLayer(layer);
  1308. }
  1309. //-------------------------------------------------------------------------------------------------
  1310. /** Adds a bridge's info get height function for logical terrain */
  1311. //-------------------------------------------------------------------------------------------------
  1312. void TerrainLogic::addLandmarkBridgeToLogic(Object *bridgeObj)
  1313. {
  1314. Bridge *pBridge = newInstance(Bridge)(bridgeObj);
  1315. pBridge->setNext(m_bridgeListHead);
  1316. m_bridgeListHead = pBridge;
  1317. PathfindLayerEnum layer = TheAI->pathfinder()->addBridge(pBridge);
  1318. pBridge->setLayer(layer);
  1319. }
  1320. //-------------------------------------------------------------------------------------------------
  1321. /** Given a name, return the associated waypoint. */
  1322. //-------------------------------------------------------------------------------------------------
  1323. Waypoint *TerrainLogic::getWaypointByName( AsciiString name )
  1324. {
  1325. for( Waypoint *way = m_waypointListHead; way; way = way->getNext() )
  1326. if (way->getName() == name)
  1327. return way;
  1328. return NULL;
  1329. }
  1330. //-------------------------------------------------------------------------------------------------
  1331. /** Given a unique integer ID, return the associated waypoint. */
  1332. //-------------------------------------------------------------------------------------------------
  1333. Waypoint *TerrainLogic::getWaypointByID( UnsignedInt id )
  1334. {
  1335. for( Waypoint *way = m_waypointListHead; way; way = way->getNext() )
  1336. if (way->getID() == id)
  1337. return way;
  1338. return NULL;
  1339. }
  1340. //-------------------------------------------------------------------------------------------------
  1341. /** Return the closest waypoint on the labeled path. */
  1342. //-------------------------------------------------------------------------------------------------
  1343. Waypoint *TerrainLogic::getClosestWaypointOnPath( const Coord3D *pos, AsciiString label )
  1344. {
  1345. Real distSqr = 0;
  1346. Waypoint *pClosestWay = NULL;
  1347. if (label.isEmpty()) {
  1348. DEBUG_LOG(("***Warning - asking for empty path label.\n"));
  1349. return NULL;
  1350. }
  1351. for( Waypoint *way = m_waypointListHead; way; way = way->getNext() ) {
  1352. Bool match = false;
  1353. if (label.compareNoCase(way->getPathLabel1())==0) match = true;
  1354. if (label.compareNoCase(way->getPathLabel2())==0) match = true;
  1355. if (label.compareNoCase(way->getPathLabel3())==0) match = true;
  1356. if (match) {
  1357. Coord3D curPos = *way->getLocation();
  1358. Real newDistSqr = (curPos.x-pos->x)*(curPos.x-pos->x) + (curPos.y-pos->y)*(curPos.y-pos->y);
  1359. if (pClosestWay==NULL) {
  1360. pClosestWay = way;
  1361. distSqr = newDistSqr;
  1362. } else if (newDistSqr < distSqr) {
  1363. pClosestWay = way;
  1364. distSqr = newDistSqr;
  1365. }
  1366. }
  1367. }
  1368. return pClosestWay;
  1369. }
  1370. //-------------------------------------------------------------------------------------------------
  1371. /** Return true if the waypoint path containing pWay is labeled with the label. */
  1372. //-------------------------------------------------------------------------------------------------
  1373. Bool TerrainLogic::isPurposeOfPath( Waypoint *pWay, AsciiString label )
  1374. {
  1375. if (label.isEmpty() || pWay==NULL) {
  1376. DEBUG_LOG(("***Warning - asking for empth path label.\n"));
  1377. return false;
  1378. }
  1379. Bool match = false;
  1380. if (label == pWay->getPathLabel1()) match = true;
  1381. if (label == pWay->getPathLabel2()) match = true;
  1382. if (label == pWay->getPathLabel3()) match = true;
  1383. return match;
  1384. }
  1385. //-------------------------------------------------------------------------------------------------
  1386. /** Given a name, return the associated trigger area, or NULL if one doesn't exist. */
  1387. //-------------------------------------------------------------------------------------------------
  1388. PolygonTrigger *TerrainLogic::getTriggerAreaByName( AsciiString name )
  1389. {
  1390. for (PolygonTrigger* pTrig = PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext()) {
  1391. AsciiString trigName = pTrig->getTriggerName();
  1392. if (name == trigName)
  1393. return pTrig;
  1394. }
  1395. return NULL;
  1396. }
  1397. //-------------------------------------------------------------------------------------------------
  1398. /** Finds the bridge at a given x/y coordinate. */
  1399. //-------------------------------------------------------------------------------------------------
  1400. Bridge * TerrainLogic::findBridgeAt( const Coord3D *pLoc) const
  1401. {
  1402. Bridge *pBridge = getFirstBridge();
  1403. while (pBridge) {
  1404. if (pBridge->isPointOnBridge(pLoc)) {
  1405. return(pBridge);
  1406. }
  1407. pBridge = pBridge->getNext();
  1408. }
  1409. return(NULL);
  1410. }
  1411. //-------------------------------------------------------------------------------------------------
  1412. /** Finds the bridge at a given x/y coordinate. On a layer. */
  1413. //-------------------------------------------------------------------------------------------------
  1414. Bridge * TerrainLogic::findBridgeLayerAt( const Coord3D *pLoc, PathfindLayerEnum layer, Bool clip) const
  1415. {
  1416. if (layer == LAYER_GROUND)
  1417. return NULL;
  1418. Bridge *pBridge = getFirstBridge();
  1419. while (pBridge)
  1420. {
  1421. if (pBridge->getLayer() == layer && (!clip || pBridge->isPointOnBridge(pLoc)))
  1422. {
  1423. return(pBridge);
  1424. }
  1425. pBridge = pBridge->getNext();
  1426. }
  1427. return(NULL);
  1428. }
  1429. //-------------------------------------------------------------------------------------------------
  1430. /** Returns the layer id for the bridge, if any, at this destination. Otherwisee
  1431. return LAYER_GROUND. */
  1432. //-------------------------------------------------------------------------------------------------
  1433. PathfindLayerEnum TerrainLogic::getLayerForDestination(const Coord3D *pos)
  1434. {
  1435. Bridge *pBridge = getFirstBridge();
  1436. PathfindLayerEnum bestLayer = LAYER_GROUND;
  1437. Real bestDistance = fabs(pos->z - getGroundHeight(pos->x, pos->y));
  1438. if (bestDistance > TheAI->pathfinder()->getWallHeight()/2) {
  1439. // check wall.
  1440. if (TheAI->pathfinder()->isPointOnWall(pos)) {
  1441. Real delta = fabs(pos->z-TheAI->pathfinder()->getWallHeight());
  1442. if (delta<bestDistance) {
  1443. bestLayer = (PathfindLayerEnum)LAYER_WALL;
  1444. bestDistance = delta;
  1445. }
  1446. }
  1447. }
  1448. while (pBridge ) {
  1449. if (pBridge->isPointOnBridge(pos) ) {
  1450. Real delta = fabs(pos->z-pBridge->getBridgeHeight(pos, NULL));
  1451. if (delta<bestDistance) {
  1452. bestLayer = pBridge->getLayer();
  1453. bestDistance = delta;
  1454. }
  1455. }
  1456. pBridge = pBridge->getNext();
  1457. }
  1458. return(bestLayer);
  1459. }
  1460. //-------------------------------------------------------------------------------------------------
  1461. // this is just like getLayerForDestination, but always return the highest layer that will be <= z at that point
  1462. // (unlike getLayerForDestination, which will return the closest layer)
  1463. PathfindLayerEnum TerrainLogic::getHighestLayerForDestination(const Coord3D *pos, Bool onlyHealthyBridges)
  1464. {
  1465. PathfindLayerEnum bestLayer = LAYER_GROUND;
  1466. Real bestDistance = pos->z - getGroundHeight(pos->x, pos->y); // NOT fabs in this case.
  1467. if (bestDistance > TheAI->pathfinder()->getWallHeight()/2) {
  1468. // check wall.
  1469. if (TheAI->pathfinder()->isPointOnWall(pos)) {
  1470. Real delta = pos->z - TheAI->pathfinder()->getWallHeight();
  1471. // must be ABOVE (or on) the wall for this call. (srj)
  1472. if (delta >= 0 && fabs(delta) < fabs(bestDistance)) {
  1473. bestLayer = (PathfindLayerEnum)LAYER_WALL;
  1474. bestDistance = delta;
  1475. }
  1476. }
  1477. }
  1478. for (Bridge *pBridge = getFirstBridge(); pBridge != NULL; pBridge = pBridge->getNext()) {
  1479. if (onlyHealthyBridges && pBridge->peekBridgeInfo()->curDamageState == BODY_RUBBLE)
  1480. continue;
  1481. if (pBridge->isPointOnBridge(pos) ) {
  1482. Real delta = pos->z - pBridge->getBridgeHeight(pos, NULL);
  1483. // must be ABOVE (or on) the bridge for this call. (srj)
  1484. if (delta >= 0 && fabs(delta) < fabs(bestDistance)) {
  1485. bestLayer = pBridge->getLayer();
  1486. bestDistance = delta;
  1487. }
  1488. }
  1489. }
  1490. return(bestLayer);
  1491. }
  1492. //-------------------------------------------------------------------------------------------------
  1493. /** Determines whether the object interacts with the bridge on specified layer. */
  1494. //-------------------------------------------------------------------------------------------------
  1495. Bool TerrainLogic::objectInteractsWithBridgeLayer(Object *obj, Int layer, Bool considerBridgeHealth) const
  1496. {
  1497. if (layer == LAYER_GROUND) return false;
  1498. if (layer == LAYER_WALL) {
  1499. if (obj->getLayer() == LAYER_WALL) {
  1500. return true; // objects on the wall can't fall off :)
  1501. }
  1502. if (TheAI->pathfinder()->isPointOnWall(obj->getPosition())) {
  1503. return true;
  1504. }
  1505. return false;
  1506. }
  1507. Bridge *pBridge = getFirstBridge();
  1508. while (pBridge ) {
  1509. if (pBridge->getLayer() == layer) {
  1510. Bool match = false;
  1511. if (pBridge->isPointOnBridge(obj->getPosition()) ) {
  1512. match = true;
  1513. }
  1514. Real radius = obj->getGeometryInfo().getMinorRadius();
  1515. radius += PATHFIND_CELL_SIZE_F/2.0f;
  1516. Region2D bounds;
  1517. bounds.lo.x = obj->getPosition()->x;
  1518. bounds.lo.y = obj->getPosition()->y;
  1519. bounds.hi = bounds.lo;
  1520. bounds.lo.x -= radius;
  1521. bounds.lo.y -= radius;
  1522. bounds.hi.x += radius;
  1523. bounds.hi.y += radius;
  1524. if (pBridge->isCellOnEnd(&bounds)) {
  1525. match = true;
  1526. }
  1527. if (match) {
  1528. Real bridgeHeight = pBridge->getBridgeHeight(obj->getPosition(), NULL);
  1529. Real delta = fabs(obj->getPosition()->z-bridgeHeight);
  1530. if (delta>LAYER_Z_CLOSE_ENOUGH_F) {
  1531. return false;
  1532. }
  1533. // make sure it's not destroyed. can't interact with dead bridges.
  1534. if (considerBridgeHealth && pBridge->peekBridgeInfo()->curDamageState == BODY_RUBBLE)
  1535. {
  1536. return false;
  1537. }
  1538. return true;
  1539. }
  1540. return false;
  1541. }
  1542. pBridge = pBridge->getNext();
  1543. }
  1544. return(false);
  1545. }
  1546. //-------------------------------------------------------------------------------------------------
  1547. /** Determines whether the object interacts with the bridge on specified layer. */
  1548. //-------------------------------------------------------------------------------------------------
  1549. Bool TerrainLogic::objectInteractsWithBridgeEnd(Object *obj, Int layer) const
  1550. {
  1551. if (layer == LAYER_GROUND) return NULL;
  1552. Bridge *pBridge = getFirstBridge();
  1553. while (pBridge ) {
  1554. if (pBridge->getLayer() == layer) {
  1555. Bool match = false;
  1556. Real radius = obj->getGeometryInfo().getMinorRadius();
  1557. radius += PATHFIND_CELL_SIZE_F/2.0f;
  1558. Region2D bounds;
  1559. bounds.lo.x = obj->getPosition()->x;
  1560. bounds.lo.y = obj->getPosition()->y;
  1561. bounds.hi = bounds.lo;
  1562. bounds.lo.x -= radius;
  1563. bounds.lo.y -= radius;
  1564. bounds.hi.x += radius;
  1565. bounds.hi.y += radius;
  1566. if (pBridge->isCellOnEnd(&bounds)) {
  1567. match = true;
  1568. }
  1569. if (match) {
  1570. Real bridgeHeight = pBridge->getBridgeHeight(obj->getPosition(), NULL);
  1571. Real delta = fabs(obj->getPosition()->z-bridgeHeight);
  1572. if (delta>LAYER_Z_CLOSE_ENOUGH_F)
  1573. {
  1574. return false;
  1575. }
  1576. return true;
  1577. }
  1578. return false;
  1579. }
  1580. pBridge = pBridge->getNext();
  1581. }
  1582. return(false);
  1583. }
  1584. //-------------------------------------------------------------------------------------------------
  1585. /** Updates the damage state of the bridge from the logic. */
  1586. //-------------------------------------------------------------------------------------------------
  1587. void TerrainLogic::updateBridgeDamageStates( void )
  1588. {
  1589. Bridge *pBridge = getFirstBridge();
  1590. while (pBridge) {
  1591. pBridge->updateDamageState();
  1592. pBridge = pBridge->getNext();
  1593. }
  1594. m_bridgeDamageStatesChanged = true;
  1595. }
  1596. //-------------------------------------------------------------------------------------------------
  1597. /** Checks if a bridge is repaired. */
  1598. //-------------------------------------------------------------------------------------------------
  1599. Bool TerrainLogic::isBridgeRepaired(const Object *bridge)
  1600. {
  1601. if (!bridge) return false;
  1602. ObjectID id = bridge->getID();
  1603. Bridge *pBridge = getFirstBridge();
  1604. while (pBridge) {
  1605. const BridgeInfo *info = pBridge->peekBridgeInfo();
  1606. if (info->bridgeObjectID == id) {
  1607. // found the right bridge.
  1608. if (info->damageStateChanged) {
  1609. // Damage state just changed.
  1610. if (info->curDamageState != BODY_RUBBLE) {
  1611. return true;
  1612. }
  1613. }
  1614. return false;
  1615. }
  1616. pBridge = pBridge->getNext();
  1617. }
  1618. return false;
  1619. }
  1620. //-------------------------------------------------------------------------------------------------
  1621. /** Checks if a bridge is broken. */
  1622. //-------------------------------------------------------------------------------------------------
  1623. Bool TerrainLogic::isBridgeBroken( const Object *bridge )
  1624. {
  1625. if (!bridge) return false;
  1626. ObjectID id = bridge->getID();
  1627. Bridge *pBridge = getFirstBridge();
  1628. while (pBridge) {
  1629. const BridgeInfo *info = pBridge->peekBridgeInfo();
  1630. if (info->bridgeObjectID == id) {
  1631. // found the right bridge.
  1632. if (info->damageStateChanged) {
  1633. // Damage state just changed.
  1634. if (info->curDamageState == BODY_RUBBLE) {
  1635. return true;
  1636. }
  1637. }
  1638. return false;
  1639. }
  1640. pBridge = pBridge->getNext();
  1641. }
  1642. return false;
  1643. }
  1644. //-------------------------------------------------------------------------------------------------
  1645. /** Gets the attack points for a bridge. */
  1646. //-------------------------------------------------------------------------------------------------
  1647. void TerrainLogic::getBridgeAttackPoints(const Object *bridge, TBridgeAttackInfo *attackInfo)
  1648. {
  1649. ObjectID id = bridge->getID();
  1650. Bridge *pBridge = getFirstBridge();
  1651. while (pBridge) {
  1652. const BridgeInfo *info = pBridge->peekBridgeInfo();
  1653. if (info->bridgeObjectID == id) {
  1654. // found the right bridge.
  1655. Coord3D delta;
  1656. delta.x = info->to.x - info->from.x;
  1657. delta.y = info->to.y - info->from.y;
  1658. delta.z = info->to.z - info->from.z;
  1659. delta.normalize();
  1660. Coord3D width;
  1661. width.x = info->fromRight.x - info->fromLeft.x;
  1662. width.y = info->fromRight.y - info->fromLeft.y;
  1663. width.z = info->fromRight.z - info->fromLeft.z;
  1664. Real len = width.length();
  1665. len /= 2.0f;
  1666. attackInfo->attackPoint1.x = info->from.x + delta.x*len;
  1667. attackInfo->attackPoint1.y = info->from.y + delta.y*len;
  1668. attackInfo->attackPoint1.z = info->from.z + delta.z*len;
  1669. attackInfo->attackPoint2.x = info->to.x - delta.x*len;
  1670. attackInfo->attackPoint2.y = info->to.y - delta.y*len;
  1671. attackInfo->attackPoint2.z = info->to.z - delta.z*len;
  1672. return;
  1673. }
  1674. pBridge = pBridge->getNext();
  1675. }
  1676. attackInfo->attackPoint1 = *bridge->getPosition();
  1677. attackInfo->attackPoint2 = *bridge->getPosition();
  1678. }
  1679. //-------------------------------------------------------------------------------------------------
  1680. /** Picks a bridge, and returns it's drawable. */
  1681. //-------------------------------------------------------------------------------------------------
  1682. Drawable *TerrainLogic::pickBridge(const Vector3 &from, const Vector3 &to, Vector3 *pos)
  1683. {
  1684. Drawable *curDraw = NULL;
  1685. Vector3 curPos(0,0,0);
  1686. Bridge *pBridge = getFirstBridge();
  1687. while (pBridge) {
  1688. Vector3 thisPos;
  1689. Drawable *thisDraw = pBridge->pickBridge(from, to , &thisPos);
  1690. if (!curDraw) {
  1691. curDraw = thisDraw;
  1692. curPos = thisPos;
  1693. }
  1694. pBridge = pBridge->getNext();
  1695. }
  1696. *pos = curPos;
  1697. return(curDraw);
  1698. }
  1699. //-------------------------------------------------------------------------------------------------
  1700. /** Deletes the bridges list. */
  1701. //-------------------------------------------------------------------------------------------------
  1702. void TerrainLogic::deleteBridges(void)
  1703. {
  1704. Bridge *pNext = NULL;
  1705. Bridge *pBridge;
  1706. // Traverse all waypoints.
  1707. for (pBridge = getFirstBridge(); pBridge; pBridge = pNext) {
  1708. pNext = pBridge->getNext();
  1709. pBridge->setNext(NULL);
  1710. pBridge->deleteInstance();
  1711. }
  1712. m_bridgeListHead = NULL;
  1713. }
  1714. //-------------------------------------------------------------------------------------------------
  1715. /** Delete the bridge specified */
  1716. //-------------------------------------------------------------------------------------------------
  1717. void TerrainLogic::deleteBridge( Bridge *bridge )
  1718. {
  1719. // sanity
  1720. if( bridge == NULL )
  1721. return;
  1722. // check for removing the head
  1723. if( m_bridgeListHead == bridge )
  1724. {
  1725. m_bridgeListHead = bridge->getNext();
  1726. } // end if
  1727. else
  1728. {
  1729. for( Bridge *otherBridge = getFirstBridge();
  1730. otherBridge;
  1731. otherBridge = otherBridge->getNext() )
  1732. {
  1733. //
  1734. // if the next bridge is the one in question to delete, set this bridge to point
  1735. // to the next pointer of the bridge we are deleting
  1736. //
  1737. if( otherBridge->getNext() == bridge )
  1738. {
  1739. otherBridge->setNext( bridge->getNext() );
  1740. break; // exit for
  1741. } // end if
  1742. } // end for, otherBridge
  1743. } // end else
  1744. // delete object associated with bridge if present
  1745. BridgeInfo bridgeInfo;
  1746. bridge->getBridgeInfo( &bridgeInfo );
  1747. TheAI->pathfinder()->changeBridgeState(bridge->getLayer(), false);
  1748. Object *bridgeObj = TheGameLogic->findObjectByID( bridgeInfo.bridgeObjectID );
  1749. if( bridgeObj )
  1750. TheGameLogic->destroyObject( bridgeObj );
  1751. // delete the bridge in question
  1752. bridge->deleteInstance();
  1753. } // end deleteBridge
  1754. //-------------------------------------------------------------------------------------------------
  1755. /** Returns the ground aligned point on the bounding box closest to the given point*/
  1756. //-------------------------------------------------------------------------------------------------
  1757. Coord3D TerrainLogic::findClosestEdgePoint ( const Coord3D *closestTo ) const
  1758. {
  1759. Region3D mapExtent;
  1760. getExtent( &mapExtent );
  1761. Real distances[4];
  1762. distances[0] = fabs( closestTo->y - mapExtent.lo.y );//top
  1763. distances[1] = fabs( closestTo->x - mapExtent.hi.x );//right
  1764. distances[2] = fabs( closestTo->y - mapExtent.hi.y );//bottom
  1765. distances[3] = fabs( closestTo->x - mapExtent.lo.x );//left
  1766. Real bestDistance = distances[0];
  1767. Int bestDistanceIndex = 0;
  1768. for( Int lameIndex = 1; lameIndex < 4; lameIndex++ )
  1769. {
  1770. if( distances[lameIndex] < bestDistance )
  1771. {
  1772. bestDistance = distances[lameIndex];
  1773. bestDistanceIndex = lameIndex;
  1774. }
  1775. }
  1776. Coord3D retVal = *closestTo;
  1777. if( bestDistanceIndex == 0 )
  1778. {
  1779. retVal.y = mapExtent.lo.y;
  1780. }
  1781. else if( bestDistanceIndex == 1 )
  1782. {
  1783. retVal.x = mapExtent.hi.x;
  1784. }
  1785. else if( bestDistanceIndex == 2 )
  1786. {
  1787. retVal.y = mapExtent.hi.y;
  1788. }
  1789. else
  1790. {
  1791. retVal.x = mapExtent.lo.x;
  1792. }
  1793. retVal.z = getGroundHeight( retVal.x, retVal.y );
  1794. return retVal;
  1795. }
  1796. //-------------------------------------------------------------------------------------------------
  1797. /** Returns the ground aligned point on the bounding box farthest from the given point*/
  1798. //-------------------------------------------------------------------------------------------------
  1799. // Lorenzen was here
  1800. Coord3D TerrainLogic::findFarthestEdgePoint( const Coord3D *farthestFrom ) const
  1801. {
  1802. Region3D mapExtent;
  1803. getExtent( &mapExtent );
  1804. Coord3D retVal = *farthestFrom;
  1805. if (farthestFrom->x < (mapExtent.width()/2) )
  1806. retVal.x = mapExtent.hi.x;
  1807. else
  1808. retVal.x = mapExtent.lo.x;
  1809. if (farthestFrom->y < (mapExtent.height()/2) )
  1810. retVal.y = mapExtent.hi.y;
  1811. else
  1812. retVal.y = mapExtent.lo.y;
  1813. retVal.z = getGroundHeight( retVal.x, retVal.y );
  1814. return retVal;
  1815. }
  1816. //-------------------------------------------------------------------------------------------------
  1817. /** See if a location is underwater, and what the water height is. */
  1818. //-------------------------------------------------------------------------------------------------
  1819. Bool TerrainLogic::isUnderwater( Real x, Real y, Real *waterZ, Real *terrainZ )
  1820. {
  1821. // get the water handle at this location
  1822. const WaterHandle *waterHandle = getWaterHandle( x, y );
  1823. // if no water here, no height, no nuttin
  1824. if( waterHandle == NULL )
  1825. {
  1826. // but we have to return the terrain Z if requested!
  1827. if (terrainZ)
  1828. *terrainZ=getGroundHeight(x,y);
  1829. return FALSE;
  1830. }
  1831. //
  1832. // if this water handle is a grid water use the grid height function, otherwise look into
  1833. // the polygon trigger
  1834. //
  1835. Real wZ = 0.0f;
  1836. if( waterHandle == &m_gridWaterHandle )
  1837. TheTerrainVisual->getWaterGridHeight( x, y, &wZ );
  1838. else
  1839. wZ = getWaterHeight( waterHandle );
  1840. // fill out the waterZ parameter with the water height
  1841. if( waterZ )
  1842. *waterZ = wZ;
  1843. // see if the terrain height here is below the water
  1844. Real terrainHeight = getGroundHeight( x, y );
  1845. if (terrainZ)
  1846. *terrainZ = terrainHeight;
  1847. return terrainHeight < wZ;
  1848. }
  1849. // ------------------------------------------------------------------------------------------------
  1850. /** Get the water table with the highest water Z value at the location */
  1851. // ------------------------------------------------------------------------------------------------
  1852. const WaterHandle* TerrainLogic::getWaterHandle( Real x, Real y )
  1853. {
  1854. const WaterHandle *waterHandle = NULL;
  1855. Real waterZ = 0.0f;
  1856. ICoord3D iLoc;
  1857. iLoc.x = REAL_TO_INT_FLOOR( x + 0.5f );
  1858. iLoc.y = REAL_TO_INT_FLOOR( y + 0.5f );
  1859. iLoc.z = 0;
  1860. // Look for water areas in the polygon triggers
  1861. for( PolygonTrigger *pTrig = PolygonTrigger::getFirstPolygonTrigger();
  1862. pTrig;
  1863. pTrig = pTrig->getNext() )
  1864. {
  1865. if( !pTrig->isWaterArea() )
  1866. continue;
  1867. // See if point is in a water area
  1868. if( pTrig->pointInTrigger( iLoc ) )
  1869. {
  1870. if( pTrig->getPoint( 0 )->z >= waterZ )
  1871. {
  1872. waterZ = pTrig->getPoint( 0 )->z;
  1873. waterHandle = pTrig->getWaterHandle();
  1874. } // end if
  1875. } // end if
  1876. } // end for
  1877. /**@todo: Remove this after we have all water types included
  1878. in water triggers. For now do special check for water grid mesh. */
  1879. Real meshZ;
  1880. if( TheTerrainVisual->getWaterGridHeight( x, y, &meshZ ) )
  1881. {
  1882. //
  1883. // point falls on water grid, return the special handle for the grid water, since we
  1884. // only have one of them and don't yet support multiple gridded water sections
  1885. //
  1886. if( meshZ >= waterZ )
  1887. {
  1888. waterZ = meshZ;
  1889. waterHandle = &m_gridWaterHandle;
  1890. } // end if
  1891. } // end if
  1892. return waterHandle;
  1893. } // end getWaterHandle
  1894. // ------------------------------------------------------------------------------------------------
  1895. /** Get water handle by name assigned from the editor */
  1896. // ------------------------------------------------------------------------------------------------
  1897. const WaterHandle* TerrainLogic::getWaterHandleByName( AsciiString name )
  1898. {
  1899. if (name.compare(WATER_GRID) == 0)
  1900. return &TerrainLogic::m_gridWaterHandle;
  1901. PolygonTrigger *trig = PolygonTrigger::getFirstPolygonTrigger();
  1902. while (trig)
  1903. {
  1904. if (trig->getTriggerName().compare(name) == 0 && trig->isWaterArea())
  1905. return trig->getWaterHandle();
  1906. trig = trig->getNext();
  1907. }
  1908. return NULL;
  1909. } // end getWaterHandleByName
  1910. // ------------------------------------------------------------------------------------------------
  1911. // ------------------------------------------------------------------------------------------------
  1912. Real TerrainLogic::getWaterHeight( const WaterHandle *water )
  1913. {
  1914. // sanity
  1915. if( water == NULL )
  1916. return 0.0f;
  1917. //
  1918. // when querying the water height given a handle, we cannot query gridded water in this
  1919. // way because it's variable across the whole surface of the water
  1920. //
  1921. if( water == &m_gridWaterHandle )
  1922. {
  1923. DEBUG_CRASH(( "TerrainLogic::getWaterHeight( WaterHandle *water ) - water is a grid handle, cannot make this query\n" ));
  1924. return 0.0f;
  1925. } // end if
  1926. // sanity
  1927. DEBUG_ASSERTCRASH( water->m_polygon != NULL, ("getWaterHeight: polygon trigger in water handle is NULL\n") );
  1928. // return the height of the water using the polygon trigger
  1929. return water->m_polygon->getPoint( 0 )->z;
  1930. } // end getWaterHeight
  1931. // ------------------------------------------------------------------------------------------------
  1932. /** Set the water height. If the water rises, then any objects that now find themselves
  1933. * underwater will be damaged by the amount provided in the parameter 'damageAmount' */
  1934. // ------------------------------------------------------------------------------------------------
  1935. void TerrainLogic::setWaterHeight( const WaterHandle *water, Real height, Real damageAmount,
  1936. Bool forcePathfindUpdate )
  1937. {
  1938. // sanity
  1939. if( water == NULL )
  1940. return;
  1941. //
  1942. // if this is a handle to gridded water simple change the transform to raise/lower the whole
  1943. // water table. Note: The other meaning this *could* have if we want it to is to leave
  1944. // the water table transform where it is and actually change the height of the water at
  1945. // every grid point
  1946. //
  1947. Real previousHeight = 0.0f;
  1948. if( water == &m_gridWaterHandle )
  1949. {
  1950. // get transform information
  1951. Matrix3D transform;
  1952. TheTerrainVisual->getWaterTransform( water, &transform );
  1953. // save the old height
  1954. previousHeight = transform.Get_Z_Translation();
  1955. // set the new height
  1956. transform.Set_Z_Translation( height );
  1957. TheTerrainVisual->setWaterTransform( &transform );
  1958. } // end if
  1959. else
  1960. {
  1961. // save the previous height
  1962. previousHeight = getWaterHeight( water );
  1963. // set the new height at all the points in the polygon trigger
  1964. const ICoord3D *p;
  1965. ICoord3D newPoint;
  1966. Int numPoints = water->m_polygon->getNumPoints();
  1967. for( Int i = 0; i < numPoints; ++i )
  1968. {
  1969. p = water->m_polygon->getPoint( i );
  1970. newPoint.x = p->x;
  1971. newPoint.y = p->y;
  1972. newPoint.z = height;
  1973. water->m_polygon->setPoint( newPoint, i );
  1974. } // end for
  1975. height = getWaterHeight(water);
  1976. } // end else
  1977. // find the bounding rectangle of this water area
  1978. Region3D affectedRegion;
  1979. affectedRegion.zero();
  1980. findAxisAlignedBoundingRect( water, &affectedRegion );
  1981. // changes in the water level force us to recalculate the pathfinding map
  1982. if( forcePathfindUpdate || previousHeight != height )
  1983. {
  1984. // do the pathfind remapping
  1985. TheAI->pathfinder()->forceMapRecalculation();
  1986. } // end if
  1987. //
  1988. // if the water height has risen, we need apply water damage to things that are now
  1989. // under the water
  1990. //
  1991. if( damageAmount > 0.0f && height > previousHeight )
  1992. {
  1993. // find the center of the water "area" given the bounding region
  1994. Coord3D center;
  1995. center.x = affectedRegion.lo.x + affectedRegion.width() / 2.0f;
  1996. center.y = affectedRegion.lo.y + affectedRegion.height() / 2.0f;
  1997. center.z = 0.0f; // irrelavant
  1998. // the max radius to scan around us is the diagonal of the bounding region
  1999. Real maxDist = sqrt( affectedRegion.width() * affectedRegion.width() +
  2000. affectedRegion.height() * affectedRegion.height() );
  2001. // scan the objects in the area of the water affected
  2002. ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( &center,
  2003. maxDist,
  2004. FROM_CENTER_2D,
  2005. NULL );
  2006. MemoryPoolObjectHolder hold( iter );
  2007. Object *obj;
  2008. const Coord3D *objPos;
  2009. for( obj = iter->first(); obj; obj = iter->next() )
  2010. {
  2011. // get other object position
  2012. objPos = obj->getPosition();
  2013. // if this object is underwater, do some damage
  2014. if( isUnderwater( objPos->x, objPos->y ) )
  2015. {
  2016. // do a lot of water damage
  2017. DamageInfo damageInfo;
  2018. damageInfo.in.m_damageType = DAMAGE_WATER;
  2019. damageInfo.in.m_deathType = DEATH_NORMAL;
  2020. damageInfo.in.m_sourceID = INVALID_ID;
  2021. damageInfo.in.m_amount = damageAmount;
  2022. obj->attemptDamage( &damageInfo );
  2023. } // end if
  2024. } // end for obj
  2025. } // end if, water has risen
  2026. } // end setWaterHeight
  2027. // ------------------------------------------------------------------------------------------------
  2028. /** Change the height of a water table over time */
  2029. // ------------------------------------------------------------------------------------------------
  2030. void TerrainLogic::changeWaterHeightOverTime( const WaterHandle *water,
  2031. Real finalHeight,
  2032. Real transitionTimeInSeconds,
  2033. Real damageAmount )
  2034. {
  2035. // if we don't have room, oops!
  2036. if( m_numWaterToUpdate >= MAX_DYNAMIC_WATER )
  2037. {
  2038. DEBUG_CRASH(( "Only '%d' simultaneous water table changes are supported\n", MAX_DYNAMIC_WATER ));
  2039. return;
  2040. } // end if
  2041. // sanity
  2042. if( water == NULL )
  2043. return;
  2044. // if this water table already has an entry in the array to update, remove it
  2045. for( Int i = 0; i < m_numWaterToUpdate; i++ )
  2046. {
  2047. if( m_waterToUpdate[ i ].waterTable == water )
  2048. {
  2049. // put the entry at the end of the list here
  2050. m_waterToUpdate[ i ] = m_waterToUpdate[ m_numWaterToUpdate - 1 ];
  2051. // we now have one less entry
  2052. --m_numWaterToUpdate;
  2053. //
  2054. // process this index over again just to be complete, but we should never find "another"
  2055. // duplicate water entry
  2056. //
  2057. --i;
  2058. } // end if
  2059. } // end for i
  2060. // get the current height of the water
  2061. Real currentHeight = getWaterHeight( water );
  2062. // add the entry into the array of water to update
  2063. m_waterToUpdate[ m_numWaterToUpdate ].waterTable = water;
  2064. m_waterToUpdate[ m_numWaterToUpdate ].changePerFrame = (finalHeight - currentHeight) /
  2065. (LOGICFRAMES_PER_SECOND * transitionTimeInSeconds);
  2066. m_waterToUpdate[ m_numWaterToUpdate ].targetHeight = finalHeight;
  2067. m_waterToUpdate[ m_numWaterToUpdate ].damageAmount = damageAmount;
  2068. m_waterToUpdate[ m_numWaterToUpdate ].currentHeight = currentHeight;
  2069. // we now have one more entry to update
  2070. ++m_numWaterToUpdate;
  2071. } // end chanageWaterHeightOverTime
  2072. // ------------------------------------------------------------------------------------------------
  2073. /** Find the axis aligned bounding region around a water table */
  2074. // ------------------------------------------------------------------------------------------------
  2075. void TerrainLogic::findAxisAlignedBoundingRect( const WaterHandle *water, Region3D *region )
  2076. {
  2077. // sanity
  2078. if( water == NULL || region == NULL )
  2079. return;
  2080. // setup the lo and high of the region to the *opposite* side of the map plus some big number
  2081. #define BUFFER 99999.9f /// just to have extreme regions outside of the map
  2082. Region3D mapExtent;
  2083. getExtent( &mapExtent );
  2084. region->lo.x = mapExtent.hi.x + BUFFER;
  2085. region->lo.y = mapExtent.hi.y + BUFFER;
  2086. region->hi.x = mapExtent.lo.x - BUFFER;
  2087. region->hi.y = mapExtent.lo.y - BUFFER;
  2088. // for water grid we must access the transform
  2089. if( water == &m_gridWaterHandle )
  2090. {
  2091. Int i;
  2092. ICoord3D p[ 4 ];
  2093. // compute the 4 corners of the table according to the grids and grid spacing
  2094. Real gridX, gridY, cellSize;
  2095. TheTerrainVisual->getWaterGridResolution( water, &gridX, &gridY, &cellSize );
  2096. p[ 0 ].x = 0;
  2097. p[ 0 ].y = 0;
  2098. p[ 1 ].x = gridX * cellSize;
  2099. p[ 1 ].y = 0;
  2100. p[ 2 ].x = gridX * cellSize;
  2101. p[ 2 ].y = gridY * cellSize;
  2102. p[ 3 ].x = 0;
  2103. p[ 3 ].y = gridY * cellSize;
  2104. // transform the 4 points using the transform matrix of the water
  2105. Vector3 v;
  2106. Matrix3D transform;
  2107. TheTerrainVisual->getWaterTransform( water, &transform );
  2108. for( i = 0; i < 4; i++ )
  2109. {
  2110. v.Set( p[ i ].x, p[ i ].y, p[ i ].z );
  2111. transform.Transform_Vector( transform, v, &v );
  2112. // do the region compares
  2113. if( v.X < region->lo.x )
  2114. region->lo.x = v.X;
  2115. if( v.X > region->hi.x )
  2116. region->hi.x = v.X;
  2117. if( v.Y < region->lo.y )
  2118. region->lo.y = v.Y;
  2119. if( v.Y > region->hi.y )
  2120. region->hi.y = v.Y;
  2121. } // end for i
  2122. } // end if
  2123. else
  2124. {
  2125. // go through each polygon point and find the extents
  2126. const ICoord3D *p;
  2127. Int numPoints = water->m_polygon->getNumPoints();
  2128. for( Int i = 0; i < numPoints; i++ )
  2129. {
  2130. // get this point
  2131. p = water->m_polygon->getPoint( i );
  2132. // compare to our region
  2133. if( p->x < region->lo.x )
  2134. region->lo.x = p->x;
  2135. if( p->x > region->hi.x )
  2136. region->hi.x = p->x;
  2137. if( p->y < region->lo.y )
  2138. region->lo.y = p->y;
  2139. if( p->y > region->hi.y )
  2140. region->hi.y = p->y;
  2141. if( p->z < region->lo.z )
  2142. region->lo.z = p->z;
  2143. if( p->z > region->hi.z )
  2144. region->hi.z = p->z;
  2145. } // end for i
  2146. } // end else
  2147. } // end findAxisAlignedBoundingRect
  2148. void TerrainLogic::setActiveBoundary(Int newActiveBoundary)
  2149. {
  2150. if (newActiveBoundary < 0 || newActiveBoundary >= m_boundaries.size()) {
  2151. // probably should DEBUG_ASSERT here
  2152. return;
  2153. }
  2154. if (newActiveBoundary == m_activeBoundary) {
  2155. // since this is fairly expensive (causes reset of PartitionManager as well as pathfinding),
  2156. // we should probably return
  2157. return;
  2158. }
  2159. if (m_boundaries[newActiveBoundary].x == 0 || m_boundaries[newActiveBoundary].y == 0) {
  2160. return;
  2161. }
  2162. ShroudStatusStoreRestore partitionStore;
  2163. // Can't have any lingering looks persist over the resize, so flush the queue now
  2164. ThePartitionManager->processEntirePendingUndoShroudRevealQueue();
  2165. //Store fogged cells
  2166. ThePartitionManager->storeFoggedCells(partitionStore, TRUE);
  2167. m_activeBoundary = newActiveBoundary;
  2168. //Remove ghost objects from partition manager so that their partition data
  2169. //can be released when parent object detaches.
  2170. TheGhostObjectManager->releasePartitionData();
  2171. //Remove objects from partition manager so that any remaining cleared
  2172. //cells must be permanently revealed.
  2173. Object *obj = TheGameLogic->getFirstObject();
  2174. while (obj) {
  2175. obj->friend_prepareForMapBoundaryAdjust();
  2176. obj = obj->getNextObject();
  2177. }
  2178. //Store permanently revealed cells.
  2179. ThePartitionManager->storeFoggedCells(partitionStore, FALSE);
  2180. ThePartitionManager->reset();
  2181. ThePartitionManager->init();
  2182. TheRadar->newMap(TheTerrainLogic);
  2183. ThePartitionManager->restoreFoggedCells(partitionStore, FALSE);
  2184. //Tell the ghost object manager to not allow creation/modification of
  2185. //ghost objects. This will prevent new ones from being recreated and allow
  2186. //us to restore the existing ones.
  2187. TheGhostObjectManager->lockGhostObjects(TRUE);
  2188. obj = TheGameLogic->getFirstObject();
  2189. while (obj) {
  2190. obj->friend_notifyOfNewMapBoundary();
  2191. obj = obj->getNextObject();
  2192. }
  2193. ThePartitionManager->restoreFoggedCells(partitionStore, TRUE);
  2194. //reinsert ghost objects into the partition manager.
  2195. TheGhostObjectManager->restorePartitionData();
  2196. //Allow creation of new ghost objects since we restored all existing ones.
  2197. TheGhostObjectManager->lockGhostObjects(FALSE);
  2198. // Don't do a newMap on the pathfinder - It uses the largest active boundary to start. jba.
  2199. //TheAI->pathfinder()->newMap();
  2200. TheTacticalView->forceCameraConstraintRecalc();
  2201. }
  2202. // ------------------------------------------------------------------------------------------------
  2203. /** Flatten the terrain beneath a struture. */
  2204. // ------------------------------------------------------------------------------------------------
  2205. void TerrainLogic::flattenTerrain(Object *obj)
  2206. {
  2207. if (obj->getGeometryInfo().getIsSmall()) {
  2208. return;
  2209. }
  2210. const Coord3D *pos = obj->getPosition();
  2211. switch(obj->getGeometryInfo().getGeomType())
  2212. {
  2213. case GEOMETRY_BOX:
  2214. {
  2215. Real angle = obj->getOrientation();
  2216. Real halfsizeX = obj->getGeometryInfo().getMajorRadius();
  2217. Real halfsizeY = obj->getGeometryInfo().getMinorRadius();
  2218. Real c = (Real)Cos(angle);
  2219. Real s = (Real)Sin(angle);
  2220. Vector3 topLeft(pos->x-halfsizeX*c-halfsizeY*s, pos->y + halfsizeY*c - halfsizeX*s, 0);
  2221. Vector3 topRight(pos->x+halfsizeX*c-halfsizeY*s, pos->y + halfsizeY*c + halfsizeX*s, 0);
  2222. Vector3 bottomRight(pos->x+halfsizeX*c+halfsizeY*s, pos->y - halfsizeY*c + halfsizeX*s, 0);
  2223. Vector3 bottomLeft(pos->x-halfsizeX*c+halfsizeY*s, pos->y - halfsizeY*c - halfsizeX*s, 0);
  2224. Real minX = topLeft.X;
  2225. if (minX>topRight.X) minX = topRight.X;
  2226. if (minX>bottomRight.X) minX = bottomRight.X;
  2227. if (minX>bottomLeft.X) minX = bottomLeft.X;
  2228. Real maxX = topLeft.X;
  2229. if (maxX<topRight.X) maxX = topRight.X;
  2230. if (maxX<bottomRight.X) maxX = bottomRight.X;
  2231. if (maxX<bottomLeft.X) maxX = bottomLeft.X;
  2232. Real minY = topLeft.Y;
  2233. if (minY>topRight.Y) minY = topRight.Y;
  2234. if (minY>bottomRight.Y) minY = bottomRight.Y;
  2235. if (minY>bottomLeft.Y) minY = bottomLeft.Y;
  2236. Real maxY = topLeft.Y;
  2237. if (maxY<topRight.Y) maxY = topRight.Y;
  2238. if (maxY<bottomRight.Y) maxY = bottomRight.Y;
  2239. if (maxY<bottomLeft.Y) maxY = bottomLeft.Y;
  2240. ICoord2D iMin, iMax;
  2241. iMin.x = REAL_TO_INT_FLOOR(minX/MAP_XY_FACTOR);
  2242. iMin.y = REAL_TO_INT_FLOOR(minY/MAP_XY_FACTOR);
  2243. iMax.x = REAL_TO_INT_FLOOR(maxX/MAP_XY_FACTOR);
  2244. iMax.y = REAL_TO_INT_FLOOR(maxY/MAP_XY_FACTOR);
  2245. Int i, j;
  2246. Real totalHeight = 0;
  2247. Int numSamples = 0;
  2248. for (i=iMin.x; i<=iMax.x; i++) {
  2249. for (j=0; j<=iMax.y; j++) {
  2250. Vector3 testPt(i*MAP_XY_FACTOR, j*MAP_XY_FACTOR, 0);
  2251. Bool match = false;
  2252. unsigned char flags;
  2253. if (Point_In_Triangle_2D(topLeft, topRight, bottomLeft, testPt, 0, 1, flags)) {
  2254. match = true;
  2255. }
  2256. if (Point_In_Triangle_2D(topRight, bottomRight, bottomLeft, testPt, 0, 1, flags)) {
  2257. match = true;
  2258. }
  2259. if (match) {
  2260. totalHeight += TheTerrainLogic->getGroundHeight(testPt.X, testPt.Y);
  2261. numSamples++;
  2262. }
  2263. }
  2264. }
  2265. if (numSamples == 0) return;
  2266. Real avgHeight = totalHeight/numSamples;
  2267. Int rawDataHeight = REAL_TO_INT_FLOOR(0.5f + avgHeight/MAP_HEIGHT_SCALE);
  2268. // Compare to the height at the building's origin, because setRawMapHeight will only lower,
  2269. // not raise. jba
  2270. Int centerHeight = REAL_TO_INT_FLOOR(TheTerrainLogic->getGroundHeight(pos->x, pos->y)/MAP_HEIGHT_SCALE);
  2271. if (rawDataHeight>centerHeight) rawDataHeight = centerHeight;
  2272. for (i=iMin.x; i<=iMax.x; i++) {
  2273. for (j=0; j<=iMax.y; j++) {
  2274. Vector3 testPt(i*MAP_XY_FACTOR, j*MAP_XY_FACTOR, 0);
  2275. Bool match = false;
  2276. unsigned char flags;
  2277. if (Point_In_Triangle_2D(topLeft, topRight, bottomLeft, testPt, 0, 1, flags)) {
  2278. match = true;
  2279. }
  2280. if (Point_In_Triangle_2D(topRight, bottomRight, bottomLeft, testPt, 0, 1, flags)) {
  2281. match = true;
  2282. }
  2283. if (match) {
  2284. ICoord2D gridPos;
  2285. gridPos.x = i;
  2286. gridPos.y = j;
  2287. TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight);
  2288. gridPos.x = i-1;
  2289. gridPos.y = j;
  2290. TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight);
  2291. gridPos.x = i+1;
  2292. gridPos.y = j;
  2293. TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight);
  2294. gridPos.x = i;
  2295. gridPos.y = j-1;
  2296. TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight);
  2297. gridPos.x = i;
  2298. gridPos.y = j+1;
  2299. TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight);
  2300. //Added the corners so it does a whole 3X3 square... ML
  2301. gridPos.x = i-1;
  2302. gridPos.y = j-1;
  2303. TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight);
  2304. gridPos.x = i+1;
  2305. gridPos.y = j+1;
  2306. TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight);
  2307. gridPos.x = i+1;
  2308. gridPos.y = j-1;
  2309. TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight);
  2310. gridPos.x = i-1;
  2311. gridPos.y = j+1;
  2312. TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight);
  2313. }
  2314. }
  2315. }
  2316. break;
  2317. }
  2318. case GEOMETRY_SPHERE: // not quite right, but close enough
  2319. case GEOMETRY_CYLINDER:
  2320. {
  2321. // fill in all cells that overlap as obstacle cells
  2322. Real radius = obj->getGeometryInfo().getMajorRadius();
  2323. Real radiusSqr = sqr(radius);
  2324. ICoord2D iMin, iMax;
  2325. iMin.x = REAL_TO_INT_FLOOR((pos->x-radius)/MAP_XY_FACTOR);
  2326. iMin.y = REAL_TO_INT_FLOOR((pos->y-radius)/MAP_XY_FACTOR);
  2327. iMax.x = REAL_TO_INT_FLOOR((pos->x+radius)/MAP_XY_FACTOR);
  2328. iMax.y = REAL_TO_INT_FLOOR((pos->y+radius)/MAP_XY_FACTOR);
  2329. Int i, j;
  2330. Real totalHeight = 0;
  2331. Int numSamples = 0;
  2332. for (i=iMin.x; i<=iMax.x; i++) {
  2333. for (j=0; j<=iMax.y; j++) {
  2334. Vector3 testPt(i*MAP_XY_FACTOR, j*MAP_XY_FACTOR, 0);
  2335. Bool match = false;
  2336. Real dx = testPt.X - pos->x;
  2337. Real dy = testPt.Y - pos->y;
  2338. if ( dx*dx+dy*dy<radiusSqr) {
  2339. match = true;
  2340. }
  2341. if (match) {
  2342. totalHeight += TheTerrainLogic->getGroundHeight(testPt.X, testPt.Y);
  2343. numSamples++;
  2344. }
  2345. }
  2346. }
  2347. if (numSamples == 0) return;
  2348. Real avgHeight = totalHeight/numSamples;
  2349. Int rawDataHeight = REAL_TO_INT_FLOOR(0.5f + avgHeight/MAP_HEIGHT_SCALE);
  2350. for (i=iMin.x; i<=iMax.x; i++) {
  2351. for (j=0; j<=iMax.y; j++) {
  2352. Vector3 testPt(i*MAP_XY_FACTOR, j*MAP_XY_FACTOR, 0);
  2353. Bool match = false;
  2354. Real dx = testPt.X - pos->x;
  2355. Real dy = testPt.Y - pos->y;
  2356. if ( dx*dx+dy*dy<radiusSqr) {
  2357. match = true;
  2358. }
  2359. if (match) {
  2360. ICoord2D gridPos;
  2361. gridPos.x = i;
  2362. gridPos.y = j;
  2363. TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight);
  2364. gridPos.x = i-1;
  2365. gridPos.y = j;
  2366. TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight);
  2367. gridPos.x = i+1;
  2368. gridPos.y = j;
  2369. TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight);
  2370. gridPos.x = i;
  2371. gridPos.y = j-1;
  2372. TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight);
  2373. gridPos.x = i;
  2374. gridPos.y = j+1;
  2375. TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight);
  2376. //Added the corners so it does a whole 3X3 square... ML
  2377. gridPos.x = i-1;
  2378. gridPos.y = j-1;
  2379. TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight);
  2380. gridPos.x = i+1;
  2381. gridPos.y = j+1;
  2382. TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight);
  2383. gridPos.x = i+1;
  2384. gridPos.y = j-1;
  2385. TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight);
  2386. gridPos.x = i-1;
  2387. gridPos.y = j+1;
  2388. TheTerrainVisual->setRawMapHeight(&gridPos, rawDataHeight);
  2389. }
  2390. }
  2391. }
  2392. } // cylinder
  2393. break;
  2394. } // switch
  2395. }
  2396. // ------------------------------------------------------------------------------------------------
  2397. /** Dig a deep circular gorge into the terrain beneath an object. */
  2398. // ------------------------------------------------------------------------------------------------
  2399. void TerrainLogic::createCraterInTerrain(Object *obj)
  2400. {
  2401. if (obj->getGeometryInfo().getIsSmall())
  2402. return;
  2403. const Coord3D *pos = obj->getPosition();
  2404. Real radius = obj->getGeometryInfo().getMajorRadius();
  2405. if ( radius <= 0.0f )
  2406. return; // sanity
  2407. ICoord2D iMin, iMax;
  2408. iMin.x = REAL_TO_INT_FLOOR( ( pos->x - radius ) / MAP_XY_FACTOR );
  2409. iMin.y = REAL_TO_INT_FLOOR( ( pos->y - radius ) / MAP_XY_FACTOR );
  2410. iMax.x = REAL_TO_INT_FLOOR( ( pos->x + radius ) / MAP_XY_FACTOR );
  2411. iMax.y = REAL_TO_INT_FLOOR( ( pos->y + radius ) / MAP_XY_FACTOR );
  2412. Real deltaX, deltaY;
  2413. for (Int i = iMin.x; i <= iMax.x; i++ )
  2414. {
  2415. for ( Int j=0; j <= iMax.y; j++ )
  2416. {
  2417. deltaX = ( i * MAP_XY_FACTOR ) - pos->x;
  2418. deltaY = ( j * MAP_XY_FACTOR ) - pos->y;
  2419. Real distance = sqrt( sqr( deltaX ) + sqr( deltaY ) );
  2420. if ( distance < radius ) //inside circle
  2421. {
  2422. ICoord2D gridPos;
  2423. gridPos.x = i;
  2424. gridPos.y = j;
  2425. Real displacementAmount = radius * (1.0f - distance / radius );
  2426. Int targetHeight = MAX( 1, TheTerrainVisual->getRawMapHeight( &gridPos ) - displacementAmount );
  2427. TheTerrainVisual->setRawMapHeight( &gridPos, targetHeight );
  2428. }
  2429. } // next j
  2430. } // next i
  2431. }
  2432. // ------------------------------------------------------------------------------------------------
  2433. /** CRC */
  2434. // ------------------------------------------------------------------------------------------------
  2435. void TerrainLogic::crc( Xfer *xfer )
  2436. {
  2437. } // end crc
  2438. // ------------------------------------------------------------------------------------------------
  2439. /** Xfer
  2440. * Version Info:
  2441. * 1: Initial version
  2442. * 2: Added water updates over time (CBD)
  2443. */
  2444. // ------------------------------------------------------------------------------------------------
  2445. void TerrainLogic::xfer( Xfer *xfer )
  2446. {
  2447. // version
  2448. const XferVersion currentVersion = 2;
  2449. XferVersion version = currentVersion;
  2450. xfer->xferVersion( &version, currentVersion );
  2451. // active boundrary
  2452. Int activeBoundary = m_activeBoundary;
  2453. xfer->xferInt( &activeBoundary );
  2454. if( xfer->getXferMode() == XFER_LOAD )
  2455. setActiveBoundary( activeBoundary );
  2456. // updatable water tables
  2457. if( version >= 2 )
  2458. {
  2459. // number of water entries in our update array
  2460. xfer->xferInt( &m_numWaterToUpdate );
  2461. // water update entry data
  2462. for( UnsignedInt i = 0; i < m_numWaterToUpdate; ++i )
  2463. {
  2464. // water handle
  2465. if( xfer->getXferMode() == XFER_SAVE )
  2466. {
  2467. // write ID of polygon trigger that this water handle is representing
  2468. Int triggerID = m_waterToUpdate[ i ].waterTable->m_polygon->getID();
  2469. xfer->xferInt( &triggerID );
  2470. } // end if, save
  2471. else if (xfer->getXferMode() == XFER_LOAD)
  2472. {
  2473. // read trigger id
  2474. Int triggerID;
  2475. xfer->xferInt( &triggerID );
  2476. // find polygon trigger
  2477. PolygonTrigger *poly = PolygonTrigger::getPolygonTriggerByID( triggerID );
  2478. // sanity
  2479. if( poly == NULL )
  2480. {
  2481. DEBUG_CRASH(( "TerrainLogic::xfer - Unable to find polygon trigger for water table with trigger ID '%d'\n",
  2482. triggerID ));
  2483. throw SC_INVALID_DATA;
  2484. } // end if
  2485. // set water handle
  2486. m_waterToUpdate[ i ].waterTable = poly->getWaterHandle();
  2487. // sanity
  2488. if( m_waterToUpdate[ i ].waterTable == NULL )
  2489. {
  2490. DEBUG_CRASH(( "TerrainLogic::xfer - Polygon trigger to use for water handle has no water handle!\n" ));
  2491. throw SC_INVALID_DATA;
  2492. } // end if
  2493. } // end else, load
  2494. // change per frame
  2495. xfer->xferReal( &m_waterToUpdate[ i ].changePerFrame );
  2496. // target height
  2497. xfer->xferReal( &m_waterToUpdate[ i ].targetHeight );
  2498. // damage amount
  2499. xfer->xferReal( &m_waterToUpdate[ i ].damageAmount );
  2500. // current height
  2501. xfer->xferReal( &m_waterToUpdate[ i ].currentHeight );
  2502. } // end for, i
  2503. } // end if
  2504. } // end xfer
  2505. // ------------------------------------------------------------------------------------------------
  2506. /** Load post process */
  2507. // ------------------------------------------------------------------------------------------------
  2508. void TerrainLogic::loadPostProcess( void )
  2509. {
  2510. Bridge* pBridge = getFirstBridge();
  2511. Bridge* pNext;
  2512. while (pBridge)
  2513. {
  2514. pNext = pBridge->getNext();
  2515. Object* obj = TheGameLogic->findObjectByID(pBridge->peekBridgeInfo()->bridgeObjectID);
  2516. if (obj == NULL)
  2517. {
  2518. deleteBridge(pBridge);
  2519. }
  2520. pBridge = pNext;
  2521. }
  2522. } // end loadPostProcess