TerrainLogic.cpp 93 KB

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