Locomotor.cpp 91 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: Locomotor.cpp ///////////////////////////////////////////////////////////////////////////////
  24. // Author: Steven Johnson, Feb 2002
  25. // Desc: Locomotor descriptions
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #define DEFINE_SURFACECATEGORY_NAMES
  30. #define DEFINE_LOCO_Z_NAMES
  31. #define DEFINE_LOCO_APPEARANCE_NAMES
  32. #include "Common/INI.h"
  33. #include "GameLogic/GameLogic.h"
  34. #include "GameLogic/PartitionManager.h"
  35. #include "GameLogic/Locomotor.h"
  36. #include "GameLogic/Object.h"
  37. #include "GameLogic/AI.h"
  38. #include "GameLogic/AIPathfind.h"
  39. #include "GameLogic/Module/PhysicsUpdate.h"
  40. #include "GameLogic/Module/BodyModule.h"
  41. #include "GameLogic/Module/AIUpdate.h"
  42. #ifdef _INTERNAL
  43. // for occasional debugging...
  44. //#pragma optimize("", off)
  45. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  46. #endif
  47. static const Real DONUT_TIME_DELAY_SECONDS=2.5f;
  48. static const Real DONUT_DISTANCE=4.0*PATHFIND_CELL_SIZE_F;
  49. #define MAX_BRAKING_FACTOR 5.0f
  50. ///////////////////////////////////////////////////////////////////////////////////////////////////
  51. // PUBLIC DATA ////////////////////////////////////////////////////////////////////////////////////
  52. ///////////////////////////////////////////////////////////////////////////////////////////////////
  53. LocomotorStore *TheLocomotorStore = NULL; ///< the Locomotor store definition
  54. const Real BIGNUM = 99999.0f;
  55. static const char *TheLocomotorPriorityNames[] =
  56. {
  57. "MOVES_BACK",
  58. "MOVES_MIDDLE",
  59. "MOVES_FRONT",
  60. NULL
  61. };
  62. ///////////////////////////////////////////////////////////////////////////////////////////////////
  63. // PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
  64. ///////////////////////////////////////////////////////////////////////////////////////////////////
  65. ///////////////////////////////////////////////////////////////////////////////////////////////////
  66. // PRIVATE FUNCTIONS //////////////////////////////////////////////////////////////////////////////
  67. ///////////////////////////////////////////////////////////////////////////////////////////////////
  68. //-------------------------------------------------------------------------------------------------
  69. static Real calcSlowDownDist(Real curSpeed, Real desiredSpeed, Real maxBraking)
  70. {
  71. Real delta = curSpeed - desiredSpeed;
  72. if (delta <= 0)
  73. return 0.0f;
  74. Real dist = (sqr(delta) / fabs(maxBraking)) * 0.5f;
  75. // use a little fudge so that things can stop "on a dime" more easily...
  76. const Real FUDGE = 1.05f;
  77. return dist * FUDGE;
  78. }
  79. //-----------------------------------------------------------------------------
  80. inline Bool isNearlyZero(Real a)
  81. {
  82. const Real TINY_EPSILON = 0.001f;
  83. return fabs(a) < TINY_EPSILON;
  84. }
  85. //-----------------------------------------------------------------------------
  86. inline Bool isNearly(Real a, Real val)
  87. {
  88. const Real TINY_EPSILON = 0.001f;
  89. return fabs(a - val) < TINY_EPSILON;
  90. }
  91. //-----------------------------------------------------------------------------
  92. // return the angle delta (in 3-space) we turned.
  93. static Real tryToRotateVector3D(
  94. Real maxAngle, // if negative, it's a percent (0...1) of the dist to rotate 'em
  95. const Vector3& inCurDir,
  96. const Vector3& inGoalDir,
  97. Vector3& actualDir
  98. )
  99. {
  100. if (isNearlyZero(maxAngle))
  101. {
  102. actualDir = inCurDir;
  103. return 0.0f;
  104. }
  105. Vector3 curDir = inCurDir;
  106. curDir.Normalize();
  107. Vector3 goalDir = inGoalDir;
  108. goalDir.Normalize();
  109. // dot of two unit vectors is cos of angle between them.
  110. Real cosine = Vector3::Dot_Product(curDir, goalDir);
  111. // bound it in case of numerical error
  112. Real angleBetween = (Real)ACos(clamp(-1.0f, cosine, 1.0f));
  113. if (maxAngle < 0)
  114. {
  115. maxAngle = -maxAngle * angleBetween;
  116. if (isNearlyZero(maxAngle))
  117. {
  118. actualDir = inCurDir;
  119. return 0.0f;
  120. }
  121. }
  122. if (fabs(angleBetween) <= maxAngle)
  123. {
  124. // close enough
  125. actualDir = goalDir;
  126. }
  127. else
  128. {
  129. // nah, try as much as we can in the right dir.
  130. // we need to rotate around the axis perpendicular to these two vecs.
  131. // but: cross of two vectors is the perpendicular axis!
  132. #ifdef ALLOW_TEMPORARIES
  133. Vector3 objCrossGoal = Vector3::Cross_Product(curDir, goalDir);
  134. objCrossGoal.Normalize();
  135. #else
  136. Vector3 objCrossGoal;
  137. Vector3::Normalized_Cross_Product(curDir, goalDir, &objCrossGoal);
  138. #endif
  139. angleBetween = maxAngle;
  140. Matrix3D rotMtx(objCrossGoal, angleBetween);
  141. actualDir = rotMtx.Rotate_Vector(curDir);
  142. }
  143. return angleBetween;
  144. }
  145. //-------------------------------------------------------------------------------------------------
  146. static Real tryToOrientInThisDirection3D(Object* obj, Real maxTurnRate, const Vector3& desiredDir)
  147. {
  148. Vector3 actualDir;
  149. Real relAngle = tryToRotateVector3D(maxTurnRate, obj->getTransformMatrix()->Get_X_Vector(), desiredDir, actualDir);
  150. if (relAngle != 0.0f)
  151. {
  152. Vector3 objPos(obj->getPosition()->x, obj->getPosition()->y, obj->getPosition()->z);
  153. Matrix3D newXform;
  154. newXform.buildTransformMatrix( objPos, actualDir );
  155. obj->setTransformMatrix( &newXform );
  156. }
  157. return relAngle;
  158. }
  159. //-------------------------------------------------------------------------------------------------
  160. inline Real tryToOrientInThisDirection3D(Object* obj, Real maxTurnRate, const Coord3D* dir)
  161. {
  162. return tryToOrientInThisDirection3D(obj, maxTurnRate, Vector3(dir->x, dir->y, dir->z));
  163. }
  164. //-----------------------------------------------------------------------------
  165. static void calcDirectionToApplyThrust(
  166. const Object* obj,
  167. const PhysicsBehavior* physics,
  168. const Coord3D& ingoalPos,
  169. Real maxAccel,
  170. Vector3& goalDir
  171. )
  172. {
  173. /*
  174. our meta-goal here is to calculate the direction we should apply our motive force
  175. in order to minimize the angle between (our velocity) and (direction towards goalpos).
  176. this is complicated by the fact that we generally have an intrinsic velocity already,
  177. that must be accounted for, and by the fact that we can only apply force in our
  178. forward-x-direction (with a thrust-angle-range), and (due to limited range) might not
  179. be able to apply the force in the optimal direction!
  180. */
  181. // convert to Vector3, to use all its handy stuff
  182. Vector3 objPos(obj->getPosition()->x, obj->getPosition()->y, obj->getPosition()->z);
  183. Vector3 goalPos(ingoalPos.x, ingoalPos.y, ingoalPos.z);
  184. Vector3 vecToGoal = goalPos - objPos;
  185. if (isNearlyZero(vecToGoal.Length2()))
  186. {
  187. // goal pos is essentially same as current pos, so just stay the same & return
  188. goalDir = obj->getTransformMatrix()->Get_X_Vector();
  189. return;
  190. }
  191. /*
  192. get our cur vel into a useful Vector3 form
  193. */
  194. Vector3 curVel(physics->getVelocity()->x, physics->getVelocity()->y, physics->getVelocity()->z);
  195. // add gravity to our vel so that we account for it in our calcs
  196. curVel.Z += TheGlobalData->m_gravity;
  197. Bool foundSolution = false;
  198. Real distToGoalSqr = vecToGoal.Length2();
  199. Real distToGoal = sqrt(distToGoalSqr);
  200. Real curVelMagSqr = curVel.Length2();
  201. Real curVelMag = sqrt(curVelMagSqr);
  202. Real maxAccelSqr = sqr(maxAccel);
  203. Real denom = curVelMagSqr - maxAccelSqr;
  204. if (!isNearlyZero(denom))
  205. {
  206. // solve the (greatly simplified) quadratic...
  207. Real t = (distToGoal * (curVelMag + maxAccel)) / denom;
  208. Real t2 = (distToGoal * (curVelMag - maxAccel)) / denom;
  209. if (t >= 0 || t2 >= 0)
  210. {
  211. // choose the smallest positive t.
  212. if (t < 0 || (t2 >= 0 && t2 < t))
  213. t = t2;
  214. // plug it in.
  215. if (!isNearlyZero(t))
  216. {
  217. goalDir.X = (vecToGoal.X / t) - curVel.X;
  218. goalDir.Y = (vecToGoal.Y / t) - curVel.Y;
  219. goalDir.Z = (vecToGoal.Z / t) - curVel.Z;
  220. goalDir.Normalize();
  221. foundSolution = true;
  222. }
  223. }
  224. }
  225. if (!foundSolution)
  226. {
  227. // Doh... no (useful) solution. revert to dumb.
  228. goalDir = vecToGoal;
  229. goalDir.Normalize();
  230. }
  231. }
  232. ///////////////////////////////////////////////////////////////////////////////////////////////////
  233. // PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
  234. ///////////////////////////////////////////////////////////////////////////////////////////////////
  235. //-------------------------------------------------------------------------------------------------
  236. LocomotorTemplate::LocomotorTemplate()
  237. {
  238. // these values mean "make the same as undamaged if not explicitly specified"
  239. m_maxSpeedDamaged = -1.0f;
  240. m_maxTurnRateDamaged = -1.0f;
  241. m_accelerationDamaged = -1.0f;
  242. m_liftDamaged = -1.0f;
  243. m_surfaces = 0;
  244. m_maxSpeed = 0.0f;
  245. m_maxTurnRate = 0.0f;
  246. m_acceleration = 0.0f;
  247. m_lift = 0.0f;
  248. m_braking = BIGNUM;
  249. m_minSpeed = 0.0f;
  250. m_minTurnSpeed = BIGNUM;
  251. m_behaviorZ = Z_NO_Z_MOTIVE_FORCE;
  252. m_appearance = LOCO_OTHER;
  253. m_movePriority = LOCO_MOVES_MIDDLE;
  254. m_preferredHeight = 0;
  255. m_preferredHeightDamping = 1.0f;
  256. m_circlingRadius = 0;
  257. m_maxThrustAngle = 0;
  258. m_speedLimitZ = 999999.0f;
  259. m_extra2DFriction = 0.0f;
  260. m_accelPitchLimit = 0;
  261. m_decelPitchLimit = 0;
  262. m_bounceKick = 0;
  263. // m_pitchStiffness = 0;
  264. // m_rollStiffness = 0;
  265. // m_pitchDamping = 0;
  266. // m_rollDamping = 0;
  267. // it's highly unlikely you want zero for the defaults for stiffness and damping... (srj)
  268. // for stiffness: stiffness of the "springs" in the suspension 0 = no stiffness, 1 = totally stiff (huh huh, he said "stiff")
  269. // for damping: 0=perfect spring, bounces forever. 1=glued to terrain.
  270. m_pitchStiffness = 0.1f;
  271. m_rollStiffness = 0.1f;
  272. m_pitchDamping = 0.9f;
  273. m_rollDamping = 0.9f;
  274. m_forwardVelCoef = 0;
  275. m_pitchByZVelCoef = 0;
  276. m_thrustRoll = 0.0f;
  277. m_wobbleRate = 0.0f;
  278. m_minWobble = 0.0f;
  279. m_maxWobble = 0.0f;
  280. m_lateralVelCoef = 0;
  281. m_forwardAccelCoef = 0;
  282. m_lateralAccelCoef = 0;
  283. m_uniformAxialDamping = 1.0f;
  284. m_turnPivotOffset = 0;
  285. m_apply2DFrictionWhenAirborne = false;
  286. m_downhillOnly = false;
  287. m_allowMotiveForceWhileAirborne = false;
  288. m_locomotorWorksWhenDead = false;
  289. m_airborneTargetingHeight = INT_MAX;
  290. m_stickToGround = false;
  291. m_canMoveBackward = false;
  292. m_hasSuspension = false;
  293. m_wheelTurnAngle = 0;
  294. m_maximumWheelExtension = 0;
  295. m_maximumWheelCompression = 0;
  296. m_closeEnoughDist = 1.0f;
  297. m_isCloseEnoughDist3D = FALSE;
  298. m_ultraAccurateSlideIntoPlaceFactor = 0.0f;
  299. m_wanderWidthFactor = 0.0f;
  300. m_wanderLengthFactor = 1.0f;
  301. m_wanderAboutPointRadius = 0.0f;
  302. m_rudderCorrectionDegree = 0.0f;
  303. m_rudderCorrectionRate = 0.0f;
  304. m_elevatorCorrectionDegree = 0.0f;
  305. m_elevatorCorrectionRate = 0.0f;
  306. }
  307. //-------------------------------------------------------------------------------------------------
  308. LocomotorTemplate::~LocomotorTemplate()
  309. {
  310. }
  311. //-------------------------------------------------------------------------------------------------
  312. void LocomotorTemplate::validate()
  313. {
  314. // this is ok; parachutes need it!
  315. //DEBUG_ASSERTCRASH(m_lift == 0.0f || m_lift > fabs(TheGlobalData->m_gravity), ("Lift is too low to counteract gravity!"));
  316. //DEBUG_ASSERTCRASH(m_liftDamaged == 0.0f || m_liftDamaged > fabs(TheGlobalData->m_gravity), ("LiftDamaged is too low to counteract gravity!"));
  317. //DEBUG_ASSERTCRASH(m_preferredHeight == 0.0f || (m_behaviorZ == Z_SURFACE_RELATIVE_HEIGHT || m_behaviorZ == Z_ABSOLUTE_HEIGHT || m_appearance == LOCO_THRUST),
  318. // ("You must use Z_SURFACE_RELATIVE_HEIGHT or Z_ABSOLUTE_HEIGHT (or THRUST) to use preferredHeight"));
  319. // for 'damaged' stuff that was omitted, set 'em to be the same as 'undamaged'...
  320. if (m_maxSpeedDamaged < 0.0f)
  321. m_maxSpeedDamaged = m_maxSpeed;
  322. if (m_maxTurnRateDamaged < 0.0f)
  323. m_maxTurnRateDamaged = m_maxTurnRate;
  324. if (m_accelerationDamaged < 0.0f)
  325. m_accelerationDamaged = m_acceleration;
  326. if (m_liftDamaged < 0.0f)
  327. m_liftDamaged = m_lift;
  328. if (m_appearance == LOCO_WINGS)
  329. {
  330. if (m_minSpeed <= 0.0f)
  331. {
  332. DEBUG_CRASH(("WINGS should always have positive minSpeeds (otherwise, they hover)"));
  333. m_minSpeed = 0.01f;
  334. }
  335. if (m_minTurnSpeed <= 0.0f)
  336. {
  337. DEBUG_CRASH(("WINGS should always have positive minTurnSpeed"));
  338. m_minTurnSpeed = 0.01f;
  339. }
  340. }
  341. if (m_appearance == LOCO_THRUST)
  342. {
  343. if (m_behaviorZ != Z_NO_Z_MOTIVE_FORCE ||
  344. m_lift != 0.0f ||
  345. m_liftDamaged != 0.0f)
  346. {
  347. DEBUG_CRASH(("THRUST locos may not use ZAxisBehavior or lift!\n"));
  348. throw INI_INVALID_DATA;
  349. }
  350. if (m_maxSpeed <= 0.0f)
  351. {
  352. // if one of these was omitted, it defaults to zero... just quietly heal it here, rather than crashing
  353. DEBUG_LOG(("THRUST locos may not have zero m_maxSpeed; healing...\n"));
  354. m_maxSpeed = 0.01f;
  355. }
  356. if (m_maxSpeedDamaged <= 0.0f)
  357. {
  358. // if one of these was omitted, it defaults to zero... just quietly heal it here, rather than crashing
  359. DEBUG_LOG(("THRUST locos may not have zero m_maxSpeedDamaged; healing...\n"));
  360. m_maxSpeedDamaged = 0.01f;
  361. }
  362. if (m_minSpeed <= 0.0f)
  363. {
  364. // if one of these was omitted, it defaults to zero... just quietly heal it here, rather than crashing
  365. DEBUG_LOG(("THRUST locos may not have zero m_minSpeed; healing...\n"));
  366. m_minSpeed = 0.01f;
  367. }
  368. }
  369. }
  370. //-------------------------------------------------------------------------------------------------
  371. static void parseFrictionPerSec( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  372. {
  373. Real fricPerSec = INI::scanReal(ini->getNextToken());
  374. Real fricPerFrame = fricPerSec * SECONDS_PER_LOGICFRAME_REAL;
  375. *(Real *)store = fricPerFrame;
  376. }
  377. //-------------------------------------------------------------------------------------------------
  378. const FieldParse* LocomotorTemplate::getFieldParse() const
  379. {
  380. static const FieldParse TheFieldParse[] =
  381. {
  382. { "Surfaces", INI::parseBitString32, TheLocomotorSurfaceTypeNames, offsetof(LocomotorTemplate, m_surfaces) },
  383. { "Speed", INI::parseVelocityReal, NULL, offsetof(LocomotorTemplate, m_maxSpeed) },
  384. { "SpeedDamaged", INI::parseVelocityReal, NULL, offsetof( LocomotorTemplate, m_maxSpeedDamaged ) },
  385. { "TurnRate", INI::parseAngularVelocityReal, NULL, offsetof(LocomotorTemplate, m_maxTurnRate) },
  386. { "TurnRateDamaged", INI::parseAngularVelocityReal, NULL, offsetof( LocomotorTemplate, m_maxTurnRateDamaged ) },
  387. { "Acceleration", INI::parseAccelerationReal, NULL, offsetof(LocomotorTemplate, m_acceleration) },
  388. { "AccelerationDamaged", INI::parseAccelerationReal, NULL, offsetof( LocomotorTemplate, m_accelerationDamaged ) },
  389. { "Lift", INI::parseAccelerationReal, NULL, offsetof(LocomotorTemplate, m_lift) },
  390. { "LiftDamaged", INI::parseAccelerationReal, NULL, offsetof( LocomotorTemplate, m_liftDamaged ) },
  391. { "Braking", INI::parseAccelerationReal, NULL, offsetof(LocomotorTemplate, m_braking) },
  392. { "MinSpeed", INI::parseVelocityReal, NULL, offsetof(LocomotorTemplate, m_minSpeed) },
  393. { "MinTurnSpeed", INI::parseVelocityReal, NULL, offsetof(LocomotorTemplate, m_minTurnSpeed) },
  394. { "PreferredHeight", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_preferredHeight) },
  395. { "PreferredHeightDamping", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_preferredHeightDamping) },
  396. { "CirclingRadius", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_circlingRadius) },
  397. { "Extra2DFriction", parseFrictionPerSec, NULL, offsetof(LocomotorTemplate, m_extra2DFriction) },
  398. { "SpeedLimitZ", INI::parseVelocityReal, NULL, offsetof(LocomotorTemplate, m_speedLimitZ) },
  399. { "MaxThrustAngle", INI::parseAngleReal, NULL, offsetof(LocomotorTemplate, m_maxThrustAngle) }, // yes, angle, not angular-vel
  400. { "ZAxisBehavior", INI::parseIndexList, TheLocomotorBehaviorZNames, offsetof(LocomotorTemplate, m_behaviorZ) },
  401. { "Appearance", INI::parseIndexList, TheLocomotorAppearanceNames, offsetof(LocomotorTemplate, m_appearance) }, \
  402. { "GroupMovementPriority", INI::parseIndexList, TheLocomotorPriorityNames, offsetof(LocomotorTemplate, m_movePriority) }, \
  403. { "AccelerationPitchLimit", INI::parseAngleReal, NULL, offsetof(LocomotorTemplate, m_accelPitchLimit) },
  404. { "DecelerationPitchLimit", INI::parseAngleReal, NULL, offsetof(LocomotorTemplate, m_decelPitchLimit) },
  405. { "BounceAmount", INI::parseAngularVelocityReal, NULL, offsetof(LocomotorTemplate, m_bounceKick) },
  406. { "PitchStiffness", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_pitchStiffness) },
  407. { "RollStiffness", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_rollStiffness) },
  408. { "PitchDamping", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_pitchDamping) },
  409. { "RollDamping", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_rollDamping) },
  410. { "ThrustRoll", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_thrustRoll) },
  411. { "ThrustWobbleRate", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_wobbleRate) },
  412. { "ThrustMinWobble", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_minWobble) },
  413. { "ThrustMaxWobble", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_maxWobble) },
  414. { "PitchInDirectionOfZVelFactor", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_pitchByZVelCoef) },
  415. { "ForwardVelocityPitchFactor", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_forwardVelCoef) },
  416. { "LateralVelocityRollFactor", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_lateralVelCoef) },
  417. { "ForwardAccelerationPitchFactor", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_forwardAccelCoef) },
  418. { "LateralAccelerationRollFactor", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_lateralAccelCoef) },
  419. { "UniformAxialDamping", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_uniformAxialDamping) },
  420. { "TurnPivotOffset", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_turnPivotOffset) },
  421. { "Apply2DFrictionWhenAirborne", INI::parseBool, NULL, offsetof(LocomotorTemplate, m_apply2DFrictionWhenAirborne) },
  422. { "DownhillOnly", INI::parseBool, NULL, offsetof(LocomotorTemplate, m_downhillOnly) },
  423. { "AllowAirborneMotiveForce", INI::parseBool, NULL, offsetof(LocomotorTemplate, m_allowMotiveForceWhileAirborne) },
  424. { "LocomotorWorksWhenDead", INI::parseBool, NULL, offsetof(LocomotorTemplate, m_locomotorWorksWhenDead) },
  425. { "AirborneTargetingHeight", INI::parseInt, NULL, offsetof( LocomotorTemplate, m_airborneTargetingHeight ) },
  426. { "StickToGround", INI::parseBool, NULL, offsetof(LocomotorTemplate, m_stickToGround) },
  427. { "CanMoveBackwards", INI::parseBool, NULL, offsetof(LocomotorTemplate, m_canMoveBackward) },
  428. { "HasSuspension", INI::parseBool, NULL, offsetof(LocomotorTemplate, m_hasSuspension) },
  429. { "FrontWheelTurnAngle", INI::parseAngleReal, NULL, offsetof(LocomotorTemplate, m_wheelTurnAngle) },
  430. { "MaximumWheelExtension", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_maximumWheelExtension) },
  431. { "MaximumWheelCompression", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_maximumWheelCompression) },
  432. { "CloseEnoughDist", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_closeEnoughDist) },
  433. { "CloseEnoughDist3D", INI::parseBool, NULL, offsetof(LocomotorTemplate, m_isCloseEnoughDist3D) },
  434. { "SlideIntoPlaceTime", INI::parseDurationReal, NULL, offsetof(LocomotorTemplate, m_ultraAccurateSlideIntoPlaceFactor) },
  435. { "WanderWidthFactor", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_wanderWidthFactor) },
  436. { "WanderLengthFactor", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_wanderLengthFactor) },
  437. { "WanderAboutPointRadius", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_wanderAboutPointRadius) },
  438. { "RudderCorrectionDegree", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_rudderCorrectionDegree) },
  439. { "RudderCorrectionRate", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_rudderCorrectionRate) },
  440. { "ElevatorCorrectionDegree", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_elevatorCorrectionDegree) },
  441. { "ElevatorCorrectionRate", INI::parseReal, NULL, offsetof(LocomotorTemplate, m_elevatorCorrectionRate) },
  442. { NULL, NULL, NULL, 0 } // keep this last
  443. };
  444. return TheFieldParse;
  445. }
  446. //-------------------------------------------------------------------------------------------------
  447. //-------------------------------------------------------------------------------------------------
  448. //-------------------------------------------------------------------------------------------------
  449. LocomotorStore::LocomotorStore()
  450. {
  451. }
  452. //-------------------------------------------------------------------------------------------------
  453. LocomotorStore::~LocomotorStore()
  454. {
  455. // delete all the templates, then clear out the table.
  456. LocomotorTemplateMap::iterator it;
  457. for (it = m_locomotorTemplates.begin(); it != m_locomotorTemplates.end(); ++it) {
  458. it->second->deleteInstance();
  459. }
  460. m_locomotorTemplates.clear();
  461. }
  462. //-------------------------------------------------------------------------------------------------
  463. LocomotorTemplate* LocomotorStore::findLocomotorTemplate(NameKeyType namekey)
  464. {
  465. if (namekey == NAMEKEY_INVALID)
  466. return NULL;
  467. LocomotorTemplateMap::iterator it = m_locomotorTemplates.find(namekey);
  468. if (it == m_locomotorTemplates.end())
  469. return NULL;
  470. else
  471. return (*it).second;
  472. }
  473. //-------------------------------------------------------------------------------------------------
  474. const LocomotorTemplate* LocomotorStore::findLocomotorTemplate(NameKeyType namekey) const
  475. {
  476. if (namekey == NAMEKEY_INVALID)
  477. return NULL;
  478. LocomotorTemplateMap::const_iterator it = m_locomotorTemplates.find(namekey);
  479. if (it == m_locomotorTemplates.end())
  480. {
  481. return NULL;
  482. }
  483. else
  484. {
  485. return (*it).second;
  486. }
  487. }
  488. //-------------------------------------------------------------------------------------------------
  489. void LocomotorStore::update()
  490. {
  491. }
  492. //-------------------------------------------------------------------------------------------------
  493. void LocomotorStore::reset()
  494. {
  495. // cleanup overrides.
  496. LocomotorTemplateMap::iterator it;
  497. for (it = m_locomotorTemplates.begin(); it != m_locomotorTemplates.end(); ) {
  498. Overridable *locoTemp = it->second->deleteOverrides();
  499. if (!locoTemp)
  500. {
  501. m_locomotorTemplates.erase(it);
  502. }
  503. else
  504. {
  505. ++it;
  506. }
  507. }
  508. }
  509. //-------------------------------------------------------------------------------------------------
  510. LocomotorTemplate *LocomotorStore::newOverride( LocomotorTemplate *locoTemplate )
  511. {
  512. if (locoTemplate == NULL)
  513. return NULL;
  514. // allocate new template
  515. LocomotorTemplate *newTemplate = newInstance(LocomotorTemplate);
  516. // copy data from final override to 'newTemplate' as a set of initial default values
  517. *newTemplate = *locoTemplate;
  518. locoTemplate->setNextOverride(newTemplate);
  519. newTemplate->markAsOverride();
  520. // return the newly created override for us to set values with etc
  521. return newTemplate;
  522. } // end newOverride
  523. //-------------------------------------------------------------------------------------------------
  524. /*static*/ void LocomotorStore::parseLocomotorTemplateDefinition(INI* ini)
  525. {
  526. if (!TheLocomotorStore)
  527. throw INI_INVALID_DATA;
  528. Bool isOverride = false;
  529. // read the Locomotor name
  530. const char* token = ini->getNextToken();
  531. NameKeyType namekey = NAMEKEY(token);
  532. LocomotorTemplate *loco = TheLocomotorStore->findLocomotorTemplate(namekey);
  533. if (loco) {
  534. if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES) {
  535. loco = TheLocomotorStore->newOverride((LocomotorTemplate*) loco->friend_getFinalOverride());
  536. }
  537. isOverride = true;
  538. } else {
  539. loco = newInstance(LocomotorTemplate);
  540. if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES) {
  541. loco->markAsOverride();
  542. }
  543. }
  544. loco->friend_setName(token);
  545. ini->initFromINI(loco, loco->getFieldParse());
  546. loco->validate();
  547. // if this is an override, then we want the pointer on the existing named locomotor to point us
  548. // to the override, so don't add it to the map.
  549. if (!isOverride)
  550. TheLocomotorStore->m_locomotorTemplates[namekey] = loco;
  551. }
  552. //-------------------------------------------------------------------------------------------------
  553. /*static*/ void INI::parseLocomotorTemplateDefinition( INI* ini )
  554. {
  555. LocomotorStore::parseLocomotorTemplateDefinition(ini);
  556. }
  557. //-------------------------------------------------------------------------------------------------
  558. //-------------------------------------------------------------------------------------------------
  559. //-------------------------------------------------------------------------------------------------
  560. //-------------------------------------------------------------------------------------------------
  561. //-------------------------------------------------------------------------------------------------
  562. Locomotor::Locomotor(const LocomotorTemplate* tmpl)
  563. {
  564. m_template = tmpl;
  565. m_brakingFactor = 1.0f;
  566. m_maxLift = BIGNUM;
  567. m_maxSpeed = BIGNUM;
  568. m_maxAccel = BIGNUM;
  569. m_maxBraking = BIGNUM;
  570. m_maxTurnRate = BIGNUM;
  571. m_flags = 0;
  572. m_closeEnoughDist = m_template->m_closeEnoughDist;
  573. setFlag(IS_CLOSE_ENOUGH_DIST_3D, m_template->m_isCloseEnoughDist3D);
  574. #ifdef CIRCLE_FOR_LANDING
  575. m_circleThresh = 0.0f;
  576. #endif
  577. m_preferredHeight = m_template->m_preferredHeight;
  578. m_preferredHeightDamping = m_template->m_preferredHeightDamping;
  579. m_angleOffset = GameLogicRandomValueReal(-PI/6, PI/6);
  580. m_offsetIncrement = (PI/40) * (GameLogicRandomValueReal(0.8f, 1.2f)/m_template->m_wanderLengthFactor);
  581. setFlag(OFFSET_INCREASING, GameLogicRandomValue(0,1));
  582. m_donutTimer = TheGameLogic->getFrame()+DONUT_TIME_DELAY_SECONDS*LOGICFRAMES_PER_SECOND;
  583. }
  584. //-------------------------------------------------------------------------------------------------
  585. Locomotor::Locomotor(const Locomotor& that)
  586. {
  587. //Added By Sadullah Nader
  588. //Initializations
  589. m_angleOffset = 0.0f;
  590. m_maintainPos.zero();
  591. //
  592. m_template = that.m_template;
  593. m_brakingFactor = that.m_brakingFactor;
  594. m_maxLift = that.m_maxLift;
  595. m_maxSpeed = that.m_maxSpeed;
  596. m_maxAccel = that.m_maxAccel;
  597. m_maxBraking = that.m_maxBraking;
  598. m_maxTurnRate = that.m_maxTurnRate;
  599. m_flags = that.m_flags;
  600. m_closeEnoughDist = that.m_closeEnoughDist;
  601. #ifdef CIRCLE_FOR_LANDING
  602. m_circleThresh = that.m_circleThresh;
  603. #endif
  604. m_preferredHeight = that.m_preferredHeight;
  605. m_preferredHeightDamping = that.m_preferredHeightDamping;
  606. m_angleOffset = that.m_angleOffset;
  607. m_offsetIncrement = that.m_offsetIncrement;
  608. }
  609. //-------------------------------------------------------------------------------------------------
  610. Locomotor& Locomotor::operator=(const Locomotor& that)
  611. {
  612. if (this != &that)
  613. {
  614. m_template = that.m_template;
  615. m_brakingFactor = that.m_brakingFactor;
  616. m_maxLift = that.m_maxLift;
  617. m_maxSpeed = that.m_maxSpeed;
  618. m_maxAccel = that.m_maxAccel;
  619. m_maxBraking = that.m_maxBraking;
  620. m_maxTurnRate = that.m_maxTurnRate;
  621. m_flags = that.m_flags;
  622. m_closeEnoughDist = that.m_closeEnoughDist;
  623. #ifdef CIRCLE_FOR_LANDING
  624. m_circleThresh = that.m_circleThresh;
  625. #endif
  626. m_preferredHeight = that.m_preferredHeight;
  627. m_preferredHeightDamping = that.m_preferredHeightDamping;
  628. }
  629. return *this;
  630. }
  631. //-------------------------------------------------------------------------------------------------
  632. Locomotor::~Locomotor()
  633. {
  634. }
  635. // ------------------------------------------------------------------------------------------------
  636. /** CRC */
  637. // ------------------------------------------------------------------------------------------------
  638. void Locomotor::crc( Xfer *xfer )
  639. {
  640. } // end crc
  641. // ------------------------------------------------------------------------------------------------
  642. /** Xfer method
  643. * Version Info:
  644. * 1: Initial version */
  645. // ------------------------------------------------------------------------------------------------
  646. void Locomotor::xfer( Xfer *xfer )
  647. {
  648. // version
  649. const XferVersion currentVersion = 2;
  650. XferVersion version = currentVersion;
  651. xfer->xferVersion( &version, currentVersion );
  652. if (version>=2) {
  653. xfer->xferUnsignedInt(&m_donutTimer);
  654. }
  655. xfer->xferCoord3D(&m_maintainPos);
  656. xfer->xferReal(&m_brakingFactor);
  657. xfer->xferReal(&m_maxLift);
  658. xfer->xferReal(&m_maxSpeed);
  659. xfer->xferReal(&m_maxAccel);
  660. xfer->xferReal(&m_maxBraking);
  661. xfer->xferReal(&m_maxTurnRate);
  662. xfer->xferReal(&m_closeEnoughDist);
  663. #ifdef CIRCLE_FOR_LANDING
  664. DEBUG_CRASH(("not supported, must fix me"));
  665. #endif
  666. xfer->xferUnsignedInt(&m_flags);
  667. xfer->xferReal(&m_preferredHeight);
  668. xfer->xferReal(&m_preferredHeightDamping);
  669. xfer->xferReal(&m_angleOffset);
  670. xfer->xferReal(&m_offsetIncrement);
  671. } // end xfer
  672. // ------------------------------------------------------------------------------------------------
  673. /** Load post process */
  674. // ------------------------------------------------------------------------------------------------
  675. void Locomotor::loadPostProcess( void )
  676. {
  677. } // end loadPostProcess
  678. //-------------------------------------------------------------------------------------------------
  679. void Locomotor::startMove(void)
  680. {
  681. // Reset the donut timer.
  682. m_donutTimer = TheGameLogic->getFrame()+DONUT_TIME_DELAY_SECONDS*LOGICFRAMES_PER_SECOND;
  683. }
  684. //-------------------------------------------------------------------------------------------------
  685. Real Locomotor::getMaxSpeedForCondition(BodyDamageType condition) const
  686. {
  687. Real speed;
  688. if( IS_CONDITION_BETTER( condition, TheGlobalData->m_movementPenaltyDamageState ) )
  689. speed = m_template->m_maxSpeed;
  690. else
  691. speed = m_template->m_maxSpeedDamaged;
  692. if (speed > m_maxSpeed)
  693. speed = m_maxSpeed;
  694. return speed;
  695. }
  696. //-------------------------------------------------------------------------------------------------
  697. Real Locomotor::getMaxTurnRate(BodyDamageType condition) const
  698. {
  699. Real turn;
  700. if( IS_CONDITION_BETTER( condition, TheGlobalData->m_movementPenaltyDamageState ) )
  701. turn = m_template->m_maxTurnRate;
  702. else
  703. turn = m_template->m_maxTurnRateDamaged;
  704. if (turn > m_maxTurnRate)
  705. turn = m_maxTurnRate;
  706. const Real TURN_FACTOR = 2;
  707. if (getFlag(ULTRA_ACCURATE))
  708. turn *= TURN_FACTOR; // monster turning ability
  709. return turn;
  710. }
  711. //-------------------------------------------------------------------------------------------------
  712. Real Locomotor::getMaxAcceleration(BodyDamageType condition) const
  713. {
  714. Real accel;
  715. if( IS_CONDITION_BETTER( condition, TheGlobalData->m_movementPenaltyDamageState ) )
  716. accel = m_template->m_acceleration;
  717. else
  718. accel = m_template->m_accelerationDamaged;
  719. if (accel > m_maxAccel)
  720. accel = m_maxAccel;
  721. return accel;
  722. }
  723. //-------------------------------------------------------------------------------------------------
  724. Real Locomotor::getBraking() const
  725. {
  726. Real braking = m_template->m_braking;
  727. if (braking > m_maxBraking)
  728. braking = m_maxBraking;
  729. return braking;
  730. }
  731. //-------------------------------------------------------------------------------------------------
  732. Real Locomotor::getMaxLift(BodyDamageType condition) const
  733. {
  734. Real lift;
  735. if( IS_CONDITION_BETTER( condition, TheGlobalData->m_movementPenaltyDamageState ) )
  736. lift = m_template->m_lift;
  737. else
  738. lift = m_template->m_liftDamaged;
  739. if (lift > m_maxLift)
  740. lift = m_maxLift;
  741. return lift;
  742. }
  743. //-------------------------------------------------------------------------------------------------
  744. void Locomotor::locoUpdate_moveTowardsAngle(Object* obj, Real goalAngle)
  745. {
  746. setFlag(MAINTAIN_POS_IS_VALID, false);
  747. if (obj == NULL || m_template == NULL)
  748. return;
  749. PhysicsBehavior *physics = obj->getPhysics();
  750. if (physics == NULL)
  751. {
  752. DEBUG_CRASH(("you can only apply Locomotors to objects with Physics"));
  753. return;
  754. }
  755. // Skip moveTowardsAngle if physics say you're stunned
  756. if(physics->getIsStunned())
  757. {
  758. return;
  759. }
  760. #ifdef DEBUG_OBJECT_ID_EXISTS
  761. // DEBUG_ASSERTLOG(obj->getID() != TheObjectIDToDebug, ("locoUpdate_moveTowardsAngle %f (%f deg), spd %f (%f)\n",goalAngle,goalAngle*180/PI,physics->getSpeed(),physics->getForwardSpeed2D()));
  762. #endif
  763. Real minSpeed = getMinSpeed();
  764. if (minSpeed > 0)
  765. {
  766. // can't stay in one place; move in the desired direction at min speed.
  767. Coord3D desiredPos = *obj->getPosition();
  768. desiredPos.x += Cos(goalAngle) * minSpeed * 2;
  769. desiredPos.y += Sin(goalAngle) * minSpeed * 2;
  770. // pass a huge num for "dist to goal", so that we don't think we're nearing
  771. // our destination and thus slow down...
  772. const Real onPathDistToGoal = 99999.0f;
  773. Bool blocked = false;
  774. locoUpdate_moveTowardsPosition(obj, desiredPos, onPathDistToGoal, minSpeed, &blocked);
  775. // don't need to call handleBehaviorZ() here, since locoUpdate_moveTowardsPosition() will do so
  776. return;
  777. }
  778. else
  779. {
  780. DEBUG_ASSERTCRASH(m_template->m_appearance != LOCO_THRUST, ("THRUST should always have minspeeds!\n"));
  781. Coord3D desiredPos = *obj->getPosition();
  782. desiredPos.x += Cos(goalAngle) * 1000.0f;
  783. desiredPos.y += Sin(goalAngle) * 1000.0f;
  784. PhysicsTurningType rotating = rotateTowardsPosition(obj, desiredPos);
  785. physics->setTurning(rotating);
  786. handleBehaviorZ(obj, physics, *obj->getPosition());
  787. }
  788. }
  789. //-------------------------------------------------------------------------------------------------
  790. PhysicsTurningType Locomotor::rotateTowardsPosition(Object* obj, const Coord3D& goalPos, Real *relAngle)
  791. {
  792. BodyDamageType bdt = obj->getBodyModule()->getDamageState();
  793. Real turnRate = getMaxTurnRate(bdt);
  794. PhysicsTurningType rotating = rotateObjAroundLocoPivot(obj, goalPos, turnRate, relAngle);
  795. return rotating;
  796. }
  797. //-------------------------------------------------------------------------------------------------
  798. void Locomotor::setPhysicsOptions(Object* obj)
  799. {
  800. PhysicsBehavior *physics = obj->getPhysics();
  801. if (physics == NULL)
  802. {
  803. DEBUG_CRASH(("you can only apply Locomotors to objects with Physics"));
  804. return;
  805. }
  806. // crank up the friction in ultra-accurate mode to increase movement precision.
  807. const Real EXTRA_FRIC = 0.5f;
  808. Real extraExtraFriction = getFlag(ULTRA_ACCURATE) ? EXTRA_FRIC : 0.0f;
  809. physics->setExtraFriction(m_template->m_extra2DFriction + extraExtraFriction);
  810. physics->setAllowAirborneFriction(getApply2DFrictionWhenAirborne()); // you'd think we wouldn't want friction in the air, but it's needed for realistic behavior.
  811. physics->setStickToGround(getStickToGround()); // walking guys aren't allowed to catch huge (or even small) air.
  812. }
  813. //-------------------------------------------------------------------------------------------------
  814. void Locomotor::locoUpdate_moveTowardsPosition(Object* obj, const Coord3D& goalPos,
  815. Real onPathDistToGoal, Real desiredSpeed, Bool *blocked)
  816. {
  817. setFlag(MAINTAIN_POS_IS_VALID, false);
  818. BodyDamageType bdt = obj->getBodyModule()->getDamageState();
  819. Real maxSpeed = getMaxSpeedForCondition(bdt);
  820. // sanity, we cannot use desired speed that is greater than our max speed we are capable of moving at
  821. if( desiredSpeed > maxSpeed )
  822. desiredSpeed = maxSpeed;
  823. Real distToStopAtMaxSpeed = (maxSpeed/getBraking()) * (maxSpeed)/2.0f;
  824. if (onPathDistToGoal>PATHFIND_CELL_SIZE_F && onPathDistToGoal > distToStopAtMaxSpeed)
  825. {
  826. setFlag(IS_BRAKING, false);
  827. m_brakingFactor = 1.0f;
  828. }
  829. PhysicsBehavior *physics = obj->getPhysics();
  830. if (physics == NULL)
  831. {
  832. DEBUG_CRASH(("you can only apply Locomotors to objects with Physics"));
  833. return;
  834. }
  835. // Skip moveTowardsPosition if physics say you're stunned
  836. if(physics->getIsStunned())
  837. {
  838. return;
  839. }
  840. #ifdef DEBUG_OBJECT_ID_EXISTS
  841. // DEBUG_ASSERTLOG(obj->getID() != TheObjectIDToDebug, ("locoUpdate_moveTowardsPosition %f %f %f (dtg %f, spd %f), speed %f (%f)\n",goalPos.x,goalPos.y,goalPos.z,onPathDistToGoal,desiredSpeed,physics->getSpeed(),physics->getForwardSpeed2D()));
  842. #endif
  843. //
  844. // do not allow for invalid positions that the pathfinder cannot handle ... for airborne
  845. // objects we don't need the pathfinder so we'll ignore this
  846. //
  847. if( BitTest( m_template->m_surfaces, LOCOMOTORSURFACE_AIR ) == false &&
  848. !TheAI->pathfinder()->validMovementTerrain(obj->getLayer(), this, obj->getPosition()) &&
  849. !getFlag(ALLOW_INVALID_POSITION))
  850. {
  851. // Somehow, we have gotten to an invalid location.
  852. if (fixInvalidPosition(obj, physics))
  853. {
  854. // the we adjusted us toward a legal position, so just return.
  855. return;
  856. }
  857. }
  858. // If the actual distance is farther, then use the actual distance so we get there.
  859. Real dx = goalPos.x - obj->getPosition()->x;
  860. Real dy = goalPos.y - obj->getPosition()->y;
  861. Real dz = goalPos.z - obj->getPosition()->z;
  862. Real dist = sqrt(dx*dx+dy*dy);
  863. if (dist>onPathDistToGoal)
  864. {
  865. if (!obj->isKindOf(KINDOF_PROJECTILE) && dist>2*onPathDistToGoal)
  866. {
  867. setFlag(IS_BRAKING, true);
  868. }
  869. onPathDistToGoal = dist;
  870. }
  871. Coord3D nullAccel;
  872. Bool treatAsAirborne = false;
  873. Coord3D pos = *obj->getPosition();
  874. Real heightAboveSurface = pos.z - TheTerrainLogic->getLayerHeight(pos.x, pos.y, obj->getLayer());
  875. if( obj->getStatusBits().test( OBJECT_STATUS_DECK_HEIGHT_OFFSET ) )
  876. {
  877. heightAboveSurface -= obj->getCarrierDeckHeight();
  878. }
  879. if (heightAboveSurface > -(3*3)*TheGlobalData->m_gravity)
  880. {
  881. // If we get high enough to stay up for 3 frames, then we left the ground.
  882. treatAsAirborne = true;
  883. }
  884. // We apply a zero acceleration to all units, as the call to
  885. // applyMotiveForce flags an object as being "driven" by a locomotor, rather
  886. // than being pushed around by objects bumping it.
  887. nullAccel.x = nullAccel.y = nullAccel.z = 0;
  888. physics->applyMotiveForce(&nullAccel);
  889. if (*blocked)
  890. {
  891. if (desiredSpeed > physics->getVelocityMagnitude())
  892. {
  893. *blocked = false;
  894. }
  895. if (treatAsAirborne && BitTest( m_template->m_surfaces, LOCOMOTORSURFACE_AIR ) )
  896. {
  897. // Airborne flying objects don't collide for now. jba.
  898. *blocked = false;
  899. }
  900. }
  901. if (*blocked)
  902. {
  903. physics->scrubVelocity2D(desiredSpeed); // stop if we are about to run into the blocking object.
  904. Real turnRate = getMaxTurnRate(obj->getBodyModule()->getDamageState());
  905. if (m_template->m_wanderWidthFactor == 0.0f)
  906. {
  907. *blocked = (TURN_NONE != rotateObjAroundLocoPivot(obj, goalPos, turnRate));
  908. }
  909. // it is very important to be sure to call this in all situations, even if not moving in 2d space.
  910. handleBehaviorZ(obj, physics, goalPos);
  911. return;
  912. }
  913. if (
  914. // srj sez: I don't know why we didn't want HOVERs to allow to "brake".
  915. // we actually really want them to, because it allows much more precise destination positioning.
  916. // m_template->m_appearance == LOCO_HOVER ||
  917. m_template->m_appearance == LOCO_WINGS)
  918. {
  919. setFlag(IS_BRAKING, false);
  920. }
  921. Bool wasBraking = obj->getStatusBits().test( OBJECT_STATUS_BRAKING );
  922. physics->setTurning(TURN_NONE);
  923. if (getAllowMotiveForceWhileAirborne() || !treatAsAirborne)
  924. {
  925. switch (m_template->m_appearance)
  926. {
  927. case LOCO_LEGS_TWO:
  928. moveTowardsPositionLegs(obj, physics, goalPos, onPathDistToGoal, desiredSpeed);
  929. break;
  930. case LOCO_CLIMBER:
  931. moveTowardsPositionClimb(obj, physics, goalPos, onPathDistToGoal, desiredSpeed);
  932. break;
  933. case LOCO_WHEELS_FOUR:
  934. case LOCO_MOTORCYCLE:
  935. moveTowardsPositionWheels( obj, physics, goalPos, onPathDistToGoal, desiredSpeed );
  936. break;
  937. case LOCO_TREADS:
  938. moveTowardsPositionTreads(obj, physics, goalPos, onPathDistToGoal, desiredSpeed);
  939. break;
  940. case LOCO_HOVER:
  941. moveTowardsPositionHover(obj, physics, goalPos, onPathDistToGoal, desiredSpeed);
  942. break;
  943. case LOCO_WINGS:
  944. moveTowardsPositionWings(obj, physics, goalPos, onPathDistToGoal, desiredSpeed);
  945. break;
  946. case LOCO_THRUST:
  947. moveTowardsPositionThrust(obj, physics, goalPos, onPathDistToGoal, desiredSpeed);
  948. break;
  949. case LOCO_OTHER:
  950. default:
  951. moveTowardsPositionOther(obj, physics, goalPos, onPathDistToGoal, desiredSpeed);
  952. break;
  953. }
  954. }
  955. handleBehaviorZ(obj, physics, goalPos);
  956. // Objects that are braking don't follow the normal physics, so they end up at their destination exactly.
  957. obj->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_BRAKING ), getFlag(IS_BRAKING) );
  958. if (wasBraking)
  959. {
  960. #define MIN_VEL (PATHFIND_CELL_SIZE_F/(LOGICFRAMES_PER_SECOND))
  961. Coord3D pos = *obj->getPosition();
  962. if (obj->isKindOf(KINDOF_PROJECTILE))
  963. {
  964. // Projectiles never stop braking once they start. jba.
  965. obj->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_BRAKING ) );
  966. // Projectiles cheat in 3 dimensions.
  967. dist = sqrt(dx*dx+dy*dy+dz*dz);
  968. Real vel = physics->getVelocityMagnitude();
  969. if (vel < MIN_VEL)
  970. vel = MIN_VEL;
  971. if (vel > dist)
  972. vel = dist; // do not overcompensate!
  973. // Normalize.
  974. if (dist > 0.001f)
  975. {
  976. dist = 1.0f / dist;
  977. dx *= dist;
  978. dy *= dist;
  979. dz *= dist;
  980. pos.x += dx * vel;
  981. pos.y += dy * vel;
  982. pos.z += dz * vel;
  983. }
  984. }
  985. else
  986. {
  987. // not projectiles only cheat in x & y.
  988. // Normalize.
  989. if (dist > 0.001f)
  990. {
  991. Real vel = fabs(physics->getForwardSpeed2D());
  992. if (vel < MIN_VEL)
  993. vel = MIN_VEL;
  994. if (vel > dist)
  995. vel = dist; // do not overcompensate!
  996. dist = 1.0f / dist;
  997. dx *= dist;
  998. dy *= dist;
  999. pos.x += dx * vel;
  1000. pos.y += dy * vel;
  1001. }
  1002. }
  1003. obj->setPosition(&pos);
  1004. }
  1005. }
  1006. //-------------------------------------------------------------------------------------------------
  1007. void Locomotor::moveTowardsPositionTreads(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos, Real onPathDistToGoal, Real desiredSpeed)
  1008. {
  1009. // sanity, we cannot use desired speed that is greater than our max speed we are capable of moving at
  1010. BodyDamageType bdt = obj->getBodyModule()->getDamageState();
  1011. Real maxSpeed = getMaxSpeedForCondition(bdt);
  1012. if( desiredSpeed > maxSpeed )
  1013. desiredSpeed = maxSpeed;
  1014. Real maxAcceleration = getMaxAcceleration(bdt);
  1015. // Locomotion for treaded vehicles, ie tanks.
  1016. //
  1017. // Orient toward goal position
  1018. //
  1019. // Real angle = obj->getOrientation();
  1020. // Real relAngle = ThePartitionManager->getRelativeAngle2D( obj, &goalPos );
  1021. // Real desiredAngle = angle + relAngle;
  1022. Real relAngle ;
  1023. PhysicsTurningType rotating = rotateTowardsPosition(obj, goalPos, &relAngle);
  1024. physics->setTurning(rotating);
  1025. //
  1026. // Modulate speed according to turning. The more we have to turn, the slower we go
  1027. //
  1028. const Real QUAETERPI = PI / 4.0f;
  1029. Real angleCoeff = (Real)fabs( relAngle ) / QUAETERPI;
  1030. if (angleCoeff > 1.0f)
  1031. angleCoeff = 1.0;
  1032. Real dx = obj->getPosition()->x - goalPos.x;
  1033. Real dy = obj->getPosition()->y - goalPos.y;
  1034. Real goalSpeed = (1.0f - angleCoeff) * desiredSpeed;
  1035. // if (speed < m_minTurnSpeed)
  1036. // speed = m_minTurnSpeed;
  1037. Real actualSpeed = physics->getForwardSpeed2D();
  1038. Real slowDownTime = actualSpeed / getBraking();
  1039. Real slowDownDist = (actualSpeed/1.50f) * slowDownTime;
  1040. if (sqr(dx)+sqr(dy)<sqr(2*PATHFIND_CELL_SIZE_F) && angleCoeff > 0.05) {
  1041. goalSpeed = actualSpeed*0.6f;
  1042. }
  1043. if (onPathDistToGoal < slowDownDist && !getFlag(IS_BRAKING) && !getFlag(NO_SLOW_DOWN_AS_APPROACHING_DEST))
  1044. {
  1045. setFlag(IS_BRAKING, true);
  1046. m_brakingFactor = 1.1f;
  1047. }
  1048. if (onPathDistToGoal>PATHFIND_CELL_SIZE_F && onPathDistToGoal > 2.0*slowDownDist)
  1049. {
  1050. setFlag(IS_BRAKING, false);
  1051. }
  1052. if (getFlag(IS_BRAKING))
  1053. {
  1054. m_brakingFactor = slowDownDist/onPathDistToGoal;
  1055. m_brakingFactor *= m_brakingFactor;
  1056. if (m_brakingFactor>MAX_BRAKING_FACTOR) {
  1057. m_brakingFactor = MAX_BRAKING_FACTOR;
  1058. }
  1059. if (slowDownDist>onPathDistToGoal) {
  1060. goalSpeed = actualSpeed-getBraking();
  1061. if (goalSpeed<0.0f) goalSpeed= 0.0f;
  1062. } else if (slowDownDist>onPathDistToGoal*0.75f) {
  1063. goalSpeed = actualSpeed-getBraking()/2.0f;
  1064. if (goalSpeed<0.0f) goalSpeed = 0.0f;
  1065. } else {
  1066. goalSpeed = actualSpeed;
  1067. }
  1068. }
  1069. //DEBUG_LOG(("Actual speed %f, Braking factor %f, slowDownDist %f, Pathdist %f, goalSpeed %f\n",
  1070. // actualSpeed, m_brakingFactor, slowDownDist, onPathDistToGoal, goalSpeed));
  1071. //
  1072. // Maintain goal speed
  1073. //
  1074. Real speedDelta = goalSpeed - actualSpeed;
  1075. if (speedDelta != 0.0f)
  1076. {
  1077. Real mass = physics->getMass();
  1078. Real acceleration = (speedDelta > 0.0f) ? maxAcceleration : -m_brakingFactor*getBraking();
  1079. Real accelForce = mass * acceleration;
  1080. /*
  1081. don't accelerate/brake more than necessary. do a quick calc to
  1082. see how much force we really need to achieve our goal speed...
  1083. */
  1084. Real maxForceNeeded = mass * speedDelta;
  1085. if (fabs(accelForce) > fabs(maxForceNeeded))
  1086. accelForce = maxForceNeeded;
  1087. const Coord3D *dir = obj->getUnitDirectionVector2D();
  1088. Coord3D force;
  1089. force.x = accelForce * dir->x;
  1090. force.y = accelForce * dir->y;
  1091. force.z = 0.0f;
  1092. // apply forces to object
  1093. physics->applyMotiveForce( &force );
  1094. }
  1095. }
  1096. //-------------------------------------------------------------------------------------------------
  1097. void Locomotor::moveTowardsPositionWheels(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos, Real onPathDistToGoal, Real desiredSpeed)
  1098. {
  1099. BodyDamageType bdt = obj->getBodyModule()->getDamageState();
  1100. Real maxSpeed = getMaxSpeedForCondition(bdt);
  1101. Real maxTurnRate = getMaxTurnRate(bdt);
  1102. Real maxAcceleration = getMaxAcceleration(bdt);
  1103. // sanity, we cannot use desired speed that is greater than our max speed we are capable of moving at
  1104. if( desiredSpeed > maxSpeed )
  1105. desiredSpeed = maxSpeed;
  1106. // Locomotion for wheeled vehicles, ie trucks.
  1107. //
  1108. // See if we are turning. If so, use the min turn speed.
  1109. //
  1110. Real turnSpeed = m_template->m_minTurnSpeed;
  1111. Real angle = obj->getOrientation();
  1112. // Real relAngle = ThePartitionManager->getRelativeAngle2D( obj, &goalPos );
  1113. // Real desiredAngle = angle + relAngle;
  1114. Real desiredAngle = atan2(goalPos.y - obj->getPosition()->y, goalPos.x - obj->getPosition()->x);
  1115. Real relAngle = stdAngleDiff(desiredAngle, angle);
  1116. Bool moveBackwards = false;
  1117. // Wheeled vehicles can only turn while moving, so make sure the turn speed is reasonable.
  1118. if (turnSpeed < maxSpeed/4.0f)
  1119. {
  1120. turnSpeed = maxSpeed/4.0f;
  1121. }
  1122. Real actualSpeed = physics->getForwardSpeed2D();
  1123. Bool do3pointTurn = false;
  1124. #if 1
  1125. if (actualSpeed==0.0f) {
  1126. setFlag(MOVING_BACKWARDS, false);
  1127. if (m_template->m_canMoveBackward && fabs(relAngle) > PI/2) {
  1128. setFlag(MOVING_BACKWARDS, true );
  1129. setFlag(DOING_THREE_POINT_TURN, onPathDistToGoal>5*obj->getGeometryInfo().getMajorRadius());
  1130. }
  1131. }
  1132. if (getFlag(MOVING_BACKWARDS)) {
  1133. if (fabs(relAngle) < PI/2) {
  1134. moveBackwards = false;
  1135. setFlag(MOVING_BACKWARDS, false);
  1136. } else {
  1137. moveBackwards = true;
  1138. setFlag(DOING_THREE_POINT_TURN, onPathDistToGoal>5*obj->getGeometryInfo().getMajorRadius());
  1139. do3pointTurn = getFlag(DOING_THREE_POINT_TURN);
  1140. if (!do3pointTurn) {
  1141. desiredAngle = stdAngleDiff(desiredAngle, PI);
  1142. relAngle = stdAngleDiff(desiredAngle, angle);
  1143. }
  1144. }
  1145. }
  1146. #endif
  1147. const Real SMALL_TURN = PI / 20.0f;
  1148. if ((Real)fabs( relAngle ) > SMALL_TURN)
  1149. {
  1150. if (desiredSpeed>turnSpeed)
  1151. {
  1152. desiredSpeed = turnSpeed;
  1153. }
  1154. }
  1155. Real goalSpeed = desiredSpeed;
  1156. if (moveBackwards) {
  1157. actualSpeed = -actualSpeed;
  1158. }
  1159. Real slowDownTime = actualSpeed / getBraking() + 1.0f;
  1160. Real slowDownDist = (actualSpeed/1.5f) * slowDownTime + actualSpeed;
  1161. Real effectiveSlowDownDist = slowDownDist;
  1162. if (effectiveSlowDownDist < 1*PATHFIND_CELL_SIZE) {
  1163. effectiveSlowDownDist = 1*PATHFIND_CELL_SIZE;
  1164. }
  1165. const Real FIFTEEN_DEGREES = PI / 12.0f;
  1166. const Real PROJECT_FRAMES = LOGICFRAMES_PER_SECOND/2; // Project out 1/2 second.
  1167. if (fabs( relAngle ) > FIFTEEN_DEGREES)
  1168. {
  1169. // If we're turning more than 10 degrees, check & see if we're moving into "impassable territory"
  1170. Real distance = PROJECT_FRAMES * (goalSpeed+actualSpeed)/2.0f;
  1171. Real targetAngle = obj->getOrientation();
  1172. Real turnFactor = ((goalSpeed+actualSpeed)/2.0f)/turnSpeed;
  1173. if (turnFactor > 1.0f)
  1174. turnFactor = 1.0f;
  1175. Real turnAmount = PROJECT_FRAMES*turnFactor*maxTurnRate/4.0f;
  1176. if (relAngle < 0)
  1177. {
  1178. targetAngle -= turnAmount;
  1179. }
  1180. else
  1181. {
  1182. targetAngle += turnAmount;
  1183. }
  1184. Coord3D offset;
  1185. offset.x = Cos(targetAngle)*distance;
  1186. offset.y = Sin(targetAngle)*distance;
  1187. offset.z = 0;
  1188. const Coord3D* pos = obj->getPosition();
  1189. Coord3D nextPos;
  1190. nextPos.x = pos->x+offset.x;
  1191. nextPos.y = pos->y+offset.y;
  1192. nextPos.z = pos->z;
  1193. pos = obj->getPosition();
  1194. Coord3D halfPos;
  1195. halfPos.x = pos->x+offset.x/2;
  1196. halfPos.y = pos->y+offset.y/2;
  1197. halfPos.z = pos->z;
  1198. if (!TheAI->pathfinder()->validMovementTerrain(obj->getLayer(), this, &halfPos) ||
  1199. !TheAI->pathfinder()->validMovementTerrain(obj->getLayer(), this, &nextPos))
  1200. {
  1201. PhysicsTurningType rotating = rotateTowardsPosition(obj, goalPos);
  1202. physics->setTurning(rotating);
  1203. // apply a zero force to object so that it acts "driven"
  1204. Coord3D force;
  1205. force.zero();
  1206. physics->applyMotiveForce( &force );
  1207. return;
  1208. }
  1209. }
  1210. if (onPathDistToGoal < effectiveSlowDownDist && !getFlag(IS_BRAKING) && !getFlag(NO_SLOW_DOWN_AS_APPROACHING_DEST))
  1211. {
  1212. setFlag(IS_BRAKING, true);
  1213. m_brakingFactor = 1.1f;
  1214. }
  1215. if (onPathDistToGoal>PATHFIND_CELL_SIZE_F && onPathDistToGoal > 2.0*slowDownDist)
  1216. {
  1217. setFlag(IS_BRAKING, false);
  1218. }
  1219. if (onPathDistToGoal > DONUT_DISTANCE) {
  1220. m_donutTimer = TheGameLogic->getFrame()+DONUT_TIME_DELAY_SECONDS*LOGICFRAMES_PER_SECOND;
  1221. } else {
  1222. if (m_donutTimer < TheGameLogic->getFrame()) {
  1223. setFlag(IS_BRAKING, true);
  1224. }
  1225. }
  1226. if (getFlag(IS_BRAKING))
  1227. {
  1228. m_brakingFactor = slowDownDist/onPathDistToGoal;
  1229. m_brakingFactor *= m_brakingFactor;
  1230. if (m_brakingFactor>MAX_BRAKING_FACTOR) {
  1231. m_brakingFactor = MAX_BRAKING_FACTOR;
  1232. }
  1233. m_brakingFactor = 1.0f;
  1234. if (slowDownDist>onPathDistToGoal) {
  1235. goalSpeed = actualSpeed-getBraking();
  1236. if (goalSpeed<0.0f) goalSpeed= 0.0f;
  1237. } else if (slowDownDist>onPathDistToGoal*0.75f) {
  1238. goalSpeed = actualSpeed-getBraking()/2.0f;
  1239. if (goalSpeed<0.0f) goalSpeed = 0.0f;
  1240. } else {
  1241. goalSpeed = actualSpeed;
  1242. }
  1243. }
  1244. //DEBUG_LOG(("Actual speed %f, Braking factor %f, slowDownDist %f, Pathdist %f, goalSpeed %f\n",
  1245. // actualSpeed, m_brakingFactor, slowDownDist, onPathDistToGoal, goalSpeed));
  1246. // Wheeled can only turn while moving.
  1247. Real turnFactor = actualSpeed/turnSpeed;
  1248. if (turnFactor<0) {
  1249. turnFactor = -turnFactor; // in case we're sliding backwards in a 3 pt turn.
  1250. }
  1251. if (turnFactor > 1.0f)
  1252. turnFactor = 1.0f;
  1253. Real turnAmount = turnFactor*maxTurnRate;
  1254. PhysicsTurningType rotating;
  1255. if (moveBackwards && !do3pointTurn) {
  1256. Coord3D backwardPos = *obj->getPosition();
  1257. backwardPos.x += -(goalPos.x - obj->getPosition()->x);
  1258. backwardPos.y += -(goalPos.y - obj->getPosition()->y);
  1259. rotating = rotateObjAroundLocoPivot(obj, backwardPos, turnAmount);
  1260. } else {
  1261. rotating = rotateObjAroundLocoPivot(obj, goalPos, turnAmount);
  1262. }
  1263. physics->setTurning(rotating);
  1264. //
  1265. // Maintain goal speed
  1266. //
  1267. Real speedDelta = goalSpeed - actualSpeed;
  1268. if (moveBackwards) {
  1269. speedDelta = -goalSpeed+actualSpeed;
  1270. }
  1271. if (speedDelta != 0.0f)
  1272. {
  1273. Real mass = physics->getMass();
  1274. Real acceleration;
  1275. if (moveBackwards) {
  1276. acceleration = (speedDelta < 0.0f) ? -maxAcceleration : m_brakingFactor*getBraking();
  1277. } else {
  1278. acceleration = (speedDelta > 0.0f) ? maxAcceleration : -m_brakingFactor*getBraking();
  1279. }
  1280. Real accelForce = mass * acceleration;
  1281. /*
  1282. don't accelerate/brake more than necessary. do a quick calc to
  1283. see how much force we really need to achieve our goal speed...
  1284. */
  1285. Real maxForceNeeded = mass * speedDelta;
  1286. if (fabs(accelForce) > fabs(maxForceNeeded))
  1287. accelForce = maxForceNeeded;
  1288. //DEBUG_LOG(("Braking %d, actualSpeed %f, goalSpeed %f, delta %f, accel %f\n", getFlag(IS_BRAKING),
  1289. //actualSpeed, goalSpeed, speedDelta, accelForce));
  1290. const Coord3D *dir = obj->getUnitDirectionVector2D();
  1291. Coord3D force;
  1292. force.x = accelForce * dir->x;
  1293. force.y = accelForce * dir->y;
  1294. force.z = 0.0f;
  1295. // apply forces to object
  1296. physics->applyMotiveForce( &force );
  1297. }
  1298. }
  1299. //-------------------------------------------------------------------------------------------------
  1300. Bool Locomotor::fixInvalidPosition(Object* obj, PhysicsBehavior *physics)
  1301. {
  1302. if (obj->isKindOf(KINDOF_DOZER)) {
  1303. // don't fix him.
  1304. return false;
  1305. }
  1306. #define no_IGNORE_INVALID
  1307. #ifdef IGNORE_INVALID
  1308. // Right now we ignore invalid positions, so when units clip the edge of a building or cliff
  1309. // they don't get stuck. jba. 12SEPT02
  1310. return false;
  1311. #else
  1312. Int dx = 0;
  1313. Int dy = 0;
  1314. Int i, j;
  1315. for (j=-1; j<2; j++) {
  1316. for (i=-1; i<2; i++) {
  1317. Coord3D thePos = *obj->getPosition();
  1318. thePos.x += i*PATHFIND_CELL_SIZE_F;
  1319. thePos.y += j*PATHFIND_CELL_SIZE_F;
  1320. if (!TheAI->pathfinder()->validMovementTerrain(obj->getLayer(), this, &thePos)) {
  1321. if (i<0) dx += 1;
  1322. if (i>0) dx -= 1;
  1323. if (j<0) dy += 1;
  1324. if (j>0) dy -= 1;
  1325. }
  1326. }
  1327. }
  1328. if (dx || dy) {
  1329. Coord3D correction;
  1330. correction.x = dx*physics->getMass()/5;
  1331. correction.y = dy*physics->getMass()/5;
  1332. correction.z = 0;
  1333. Coord3D correctionNormalized = correction;
  1334. correctionNormalized.normalize();
  1335. Coord3D velocity;
  1336. // Kill current velocity in the direction of the correction.
  1337. velocity = *physics->getVelocity();
  1338. Real dot = (velocity.x*correctionNormalized.x) + (velocity.y*correctionNormalized.y);
  1339. if (dot>.25f) {
  1340. // It was already leaving.
  1341. return false;
  1342. }
  1343. // Kill current accel
  1344. //physics->clearAcceleration();
  1345. if (dot<0) {
  1346. dot = sqrt(-dot);
  1347. correctionNormalized.x *= dot*physics->getMass();
  1348. correctionNormalized.y *= dot*physics->getMass();
  1349. physics->applyMotiveForce(&correctionNormalized);
  1350. }
  1351. // apply correction.
  1352. physics->applyMotiveForce(&correction);
  1353. return true;
  1354. }
  1355. return false;
  1356. #endif
  1357. }
  1358. //-------------------------------------------------------------------------------------------------
  1359. Real Locomotor::calcMinTurnRadius(BodyDamageType condition, Real* timeToTravelThatDist) const
  1360. {
  1361. Real minSpeed = getMinSpeed(); // in dist/frame
  1362. Real maxTurnRate = getMaxTurnRate(condition); // in rads/frame
  1363. /*
  1364. our minimum circumference will be like so:
  1365. Real minTurnCircum = maxSpeed * (2*PI / maxTurnRate);
  1366. so therefore our minimum turn radius is:
  1367. Real minTurnRadius = minTurnCircum / 2*PI;
  1368. so we just eliminate the middleman:
  1369. */
  1370. // if we can't turn, return a huge-but-finite radius rather than NAN...
  1371. Real minTurnRadius = (maxTurnRate > 0.0f) ? minSpeed / maxTurnRate : BIGNUM;
  1372. if (timeToTravelThatDist)
  1373. *timeToTravelThatDist = (minSpeed > 0.0f) ? (minTurnRadius / minSpeed) : 0.0f;
  1374. return minTurnRadius;
  1375. }
  1376. //-------------------------------------------------------------------------------------------------
  1377. void Locomotor::moveTowardsPositionLegs(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos, Real onPathDistToGoal, Real desiredSpeed)
  1378. {
  1379. if (getIsDownhillOnly() && obj->getPosition()->z < goalPos.z)
  1380. {
  1381. return;
  1382. }
  1383. Real maxAcceleration = getMaxAcceleration( obj->getBodyModule()->getDamageState() );
  1384. // sanity, we cannot use desired speed that is greater than our max speed we are capable of moving at
  1385. Real maxSpeed = getMaxSpeedForCondition( obj->getBodyModule()->getDamageState() );
  1386. if( desiredSpeed > maxSpeed )
  1387. desiredSpeed = maxSpeed;
  1388. // Locomotion for infantry.
  1389. //
  1390. // Orient toward goal position
  1391. //
  1392. Real actualSpeed = physics->getForwardSpeed2D();
  1393. Real angle = obj->getOrientation();
  1394. // Real relAngle = ThePartitionManager->getRelativeAngle2D( obj, &goalPos );
  1395. // Real desiredAngle = angle + relAngle;
  1396. Real desiredAngle = atan2(goalPos.y - obj->getPosition()->y, goalPos.x - obj->getPosition()->x);
  1397. if (m_template->m_wanderWidthFactor != 0.0f) {
  1398. Real angleLimit = PI/8 * m_template->m_wanderWidthFactor;
  1399. // This is the wander offline code - it forces the desired angle away from the goal, so we wander back & forth. jba.
  1400. if (getFlag(OFFSET_INCREASING)) {
  1401. m_angleOffset += m_offsetIncrement*actualSpeed;
  1402. if (m_angleOffset > angleLimit) {
  1403. setFlag(OFFSET_INCREASING, false);
  1404. }
  1405. } else {
  1406. m_angleOffset -= m_offsetIncrement*actualSpeed;
  1407. if (m_angleOffset<-angleLimit) {
  1408. setFlag(OFFSET_INCREASING, true);
  1409. }
  1410. }
  1411. desiredAngle = normalizeAngle(desiredAngle+m_angleOffset);
  1412. }
  1413. Real relAngle = stdAngleDiff(desiredAngle, angle);
  1414. locoUpdate_moveTowardsAngle(obj, desiredAngle);
  1415. //
  1416. // Modulate speed according to turning. The more we have to turn, the slower we go
  1417. //
  1418. const Real QUARTERPI = PI / 4.0f;
  1419. Real angleCoeff = (Real)fabs( relAngle ) / (QUARTERPI);
  1420. if (angleCoeff > 1.0f)
  1421. angleCoeff = 1.0;
  1422. Real goalSpeed = (1.0f - angleCoeff) * desiredSpeed;
  1423. //Real slowDownDist = (actualSpeed - m_template->m_minSpeed) / getBraking();
  1424. Real slowDownDist = calcSlowDownDist(actualSpeed, m_template->m_minSpeed, getBraking());
  1425. if (onPathDistToGoal < slowDownDist && !getFlag(NO_SLOW_DOWN_AS_APPROACHING_DEST))
  1426. {
  1427. goalSpeed = m_template->m_minSpeed;
  1428. }
  1429. //
  1430. // Maintain goal speed
  1431. //
  1432. Real speedDelta = goalSpeed - actualSpeed;
  1433. if (speedDelta != 0.0f)
  1434. {
  1435. Real mass = physics->getMass();
  1436. Real acceleration = (speedDelta > 0.0f) ? maxAcceleration : -getBraking();
  1437. Real accelForce = mass * acceleration;
  1438. /*
  1439. don't accelerate/brake more than necessary. do a quick calc to
  1440. see how much force we really need to achieve our goal speed...
  1441. */
  1442. Real maxForceNeeded = mass * speedDelta;
  1443. if (fabs(accelForce) > fabs(maxForceNeeded))
  1444. accelForce = maxForceNeeded;
  1445. const Coord3D *dir = obj->getUnitDirectionVector2D();
  1446. Coord3D force;
  1447. force.x = accelForce * dir->x;
  1448. force.y = accelForce * dir->y;
  1449. force.z = 0.0f;
  1450. // apply forces to object
  1451. physics->applyMotiveForce( &force );
  1452. }
  1453. }
  1454. //-------------------------------------------------------------------------------------------------
  1455. void Locomotor::moveTowardsPositionClimb(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos, Real onPathDistToGoal, Real desiredSpeed)
  1456. {
  1457. Real maxAcceleration = getMaxAcceleration( obj->getBodyModule()->getDamageState() );
  1458. // sanity, we cannot use desired speed that is greater than our max speed we are capable of moving at
  1459. Real maxSpeed = getMaxSpeedForCondition( obj->getBodyModule()->getDamageState() );
  1460. if( desiredSpeed > maxSpeed )
  1461. desiredSpeed = maxSpeed;
  1462. // Locomotion for climbing infantry.
  1463. Bool moveBackwards = false;
  1464. Real dx, dy, dz;
  1465. Coord3D pos = *obj->getPosition();
  1466. dx = pos.x - goalPos.x;
  1467. dy = pos.y - goalPos.y;
  1468. dz = pos.z - goalPos.z;
  1469. if (dz*dz > sqr(PATHFIND_CELL_SIZE_F)) {
  1470. setFlag(CLIMBING, true);
  1471. }
  1472. if (fabs(dz)<1) {
  1473. setFlag(CLIMBING, false);
  1474. }
  1475. //setFlag(CLIMBING, true);
  1476. if (getFlag(CLIMBING)) {
  1477. Coord3D delta = goalPos;
  1478. delta.x -= pos.x;
  1479. delta.y -= pos.y;
  1480. delta.z = 0;
  1481. delta.normalize();
  1482. delta.x += pos.x;
  1483. delta.y += pos.y;
  1484. delta.z = TheTerrainLogic->getGroundHeight(delta.x, delta.y);
  1485. if (delta.z < pos.z-0.1) {
  1486. moveBackwards = true;
  1487. }
  1488. Real groundSlope = fabs(delta.z - pos.z);
  1489. if (groundSlope<1.0f) groundSlope = 1.0f;
  1490. if (groundSlope>1.0f) {
  1491. desiredSpeed /= groundSlope*4;
  1492. }
  1493. }
  1494. setFlag(MOVING_BACKWARDS, moveBackwards);
  1495. //
  1496. // Orient toward goal position
  1497. //
  1498. Real angle = obj->getOrientation();
  1499. // Real relAngle = ThePartitionManager->getRelativeAngle2D( obj, &goalPos );
  1500. // Real desiredAngle = angle + relAngle;
  1501. Real desiredAngle = atan2(goalPos.y - obj->getPosition()->y, goalPos.x - obj->getPosition()->x);
  1502. Real relAngle = stdAngleDiff(desiredAngle, angle);
  1503. if (moveBackwards) {
  1504. desiredAngle = stdAngleDiff(desiredAngle, PI);
  1505. relAngle = stdAngleDiff(desiredAngle, angle);
  1506. }
  1507. locoUpdate_moveTowardsAngle(obj, desiredAngle);
  1508. //
  1509. // Modulate speed according to turning. The more we have to turn, the slower we go
  1510. //
  1511. const Real QUARTERPI = PI / 4.0f;
  1512. Real angleCoeff = (Real)fabs( relAngle ) / (QUARTERPI);
  1513. if (angleCoeff > 1.0f)
  1514. angleCoeff = 1.0;
  1515. Real goalSpeed = (1.0f - angleCoeff) * desiredSpeed;
  1516. Real actualSpeed = physics->getForwardSpeed2D();
  1517. if (moveBackwards) {
  1518. actualSpeed = -actualSpeed;
  1519. }
  1520. //Real slowDownDist = (actualSpeed - m_template->m_minSpeed) / getBraking();
  1521. Real slowDownDist = calcSlowDownDist(actualSpeed, m_template->m_minSpeed, getBraking());
  1522. if (onPathDistToGoal < slowDownDist && !getFlag(NO_SLOW_DOWN_AS_APPROACHING_DEST))
  1523. {
  1524. goalSpeed = m_template->m_minSpeed;
  1525. }
  1526. //
  1527. // Maintain goal speed
  1528. //
  1529. Real speedDelta = goalSpeed - actualSpeed;
  1530. if (moveBackwards) {
  1531. speedDelta = -goalSpeed+actualSpeed;
  1532. }
  1533. if (speedDelta != 0.0f)
  1534. {
  1535. Real mass = physics->getMass();
  1536. Real acceleration;
  1537. if (moveBackwards) {
  1538. acceleration = (speedDelta < 0.0f) ? -maxAcceleration : getBraking();
  1539. } else {
  1540. acceleration = (speedDelta > 0.0f) ? maxAcceleration : -getBraking();
  1541. }
  1542. Real accelForce = mass * acceleration;
  1543. /*
  1544. don't accelerate/brake more than necessary. do a quick calc to
  1545. see how much force we really need to achieve our goal speed...
  1546. */
  1547. Real maxForceNeeded = mass * speedDelta;
  1548. if (fabs(accelForce) > fabs(maxForceNeeded))
  1549. accelForce = maxForceNeeded;
  1550. const Coord3D *dir = obj->getUnitDirectionVector2D();
  1551. Coord3D force;
  1552. force.x = accelForce * dir->x;
  1553. force.y = accelForce * dir->y;
  1554. force.z = 0.0f;
  1555. // apply forces to object
  1556. physics->applyMotiveForce( &force );
  1557. }
  1558. }
  1559. //-------------------------------------------------------------------------------------------------
  1560. void Locomotor::moveTowardsPositionWings(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos, Real onPathDistToGoal, Real desiredSpeed)
  1561. {
  1562. #ifdef CIRCLE_FOR_LANDING
  1563. if (m_circleThresh > 0.0f)
  1564. {
  1565. // if we are going a mostly-vertical maneuver, circle in order to
  1566. // gain/lose altitude, then resume course...
  1567. const Coord3D* pos = obj->getPosition();
  1568. Real dx = goalPos.x - pos->x;
  1569. Real dy = goalPos.y - pos->y;
  1570. Real dz = goalPos.z - pos->z;
  1571. if (fabs(dz) > m_circleThresh)
  1572. {
  1573. // aim for the spot on the opposite side of the circle.
  1574. // find the direction towards our goal pos
  1575. Real angleTowardPos =
  1576. (isNearlyZero(dx) && isNearlyZero(dy)) ?
  1577. obj->getOrientation() :
  1578. atan2(dy, dx);
  1579. Real aimDir = (PI - PI/8);
  1580. angleTowardPos += aimDir;
  1581. BodyDamageType bdt = obj->getBodyModule()->getDamageState();
  1582. Real turnRadius = calcMinTurnRadius(bdt, NULL) * 4;
  1583. // project a spot "radius" dist away from it, in that dir
  1584. Coord3D desiredPos = goalPos;
  1585. desiredPos.x += Cos(angleTowardPos) * turnRadius;
  1586. desiredPos.y += Sin(angleTowardPos) * turnRadius;
  1587. moveTowardsPositionOther(obj, physics, desiredPos, 0, desiredSpeed);
  1588. return;
  1589. }
  1590. }
  1591. #endif
  1592. // handle the 2D component.
  1593. moveTowardsPositionOther(obj, physics, goalPos, onPathDistToGoal, desiredSpeed);
  1594. }
  1595. //-------------------------------------------------------------------------------------------------
  1596. void Locomotor::moveTowardsPositionHover(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos, Real onPathDistToGoal, Real desiredSpeed)
  1597. {
  1598. // handle the 2D component.
  1599. moveTowardsPositionOther(obj, physics, goalPos, onPathDistToGoal, desiredSpeed);
  1600. // Only hover locomotors care about their OverWater special effects. (OverWater also affects speed, so this is not a client thing)
  1601. Coord3D newPosition = *obj->getPosition();
  1602. if( TheTerrainLogic->isUnderwater( newPosition.x, newPosition.y ) )
  1603. {
  1604. if( ! getFlag( OVER_WATER ) )
  1605. {
  1606. // Change my model condition because I used to not be over water, but now I am
  1607. setFlag( OVER_WATER, TRUE );
  1608. obj->setModelConditionState( MODELCONDITION_OVER_WATER );
  1609. }
  1610. }
  1611. else
  1612. {
  1613. if( getFlag( OVER_WATER ) )
  1614. {
  1615. // Here, I was, but now I'm not
  1616. setFlag( OVER_WATER, FALSE );
  1617. obj->clearModelConditionState( MODELCONDITION_OVER_WATER );
  1618. }
  1619. }
  1620. }
  1621. //-------------------------------------------------------------------------------------------------
  1622. void Locomotor::moveTowardsPositionThrust(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos, Real onPathDistToGoal, Real desiredSpeed)
  1623. {
  1624. BodyDamageType bdt = obj->getBodyModule()->getDamageState();
  1625. Real maxForwardSpeed = getMaxSpeedForCondition(bdt);
  1626. desiredSpeed = clamp(m_template->m_minSpeed, desiredSpeed, maxForwardSpeed);
  1627. Real actualForwardSpeed = physics->getForwardSpeed3D();
  1628. if (getBraking() > 0)
  1629. {
  1630. //Real slowDownDist = (actualForwardSpeed - m_template->m_minSpeed) / getBraking();
  1631. Real slowDownDist = calcSlowDownDist(actualForwardSpeed, m_template->m_minSpeed, getBraking());
  1632. if (onPathDistToGoal < slowDownDist && !getFlag(NO_SLOW_DOWN_AS_APPROACHING_DEST))
  1633. desiredSpeed = m_template->m_minSpeed;
  1634. }
  1635. Coord3D localGoalPos = goalPos;
  1636. #ifdef USE_ZDIR_DAMPING
  1637. Real zDirDamping = 0.0f;
  1638. #endif
  1639. //out of the handleBehaviorZ() function
  1640. Coord3D pos = *obj->getPosition();
  1641. if( m_preferredHeight != 0.0f && !getFlag(PRECISE_Z_POS) )
  1642. {
  1643. // If we have a preferred flight height, and we haven't been told explicitly to ignore it...
  1644. Real surfaceHt = getSurfaceHtAtPt(pos.x, pos.y);
  1645. localGoalPos.z = m_preferredHeight + surfaceHt;
  1646. // localGoalPos.z = goalPos.z;
  1647. Real delta = localGoalPos.z - pos.z;
  1648. delta *= getPreferredHeightDamping();
  1649. localGoalPos.z = pos.z + delta;
  1650. #ifdef USE_ZDIR_DAMPING
  1651. // closer we get to the preferred height, less we adjust z-thrust,
  1652. // so we tend to "level out" at that height. we don't use this till
  1653. // below, but go ahead and calc it now...
  1654. Real MAX_VERTICAL_DAMP_RANGE = m_preferredHeight * 0.5;
  1655. delta = fabs(delta);
  1656. if (delta > MAX_VERTICAL_DAMP_RANGE)
  1657. delta = MAX_VERTICAL_DAMP_RANGE;
  1658. zDirDamping = 1.0f - (delta / MAX_VERTICAL_DAMP_RANGE);
  1659. #endif
  1660. }
  1661. Vector3 forwardDir = obj->getTransformMatrix()->Get_X_Vector();
  1662. // Maintain goal speed
  1663. Real forwardSpeedDelta = desiredSpeed - actualForwardSpeed;
  1664. Real maxAccel = (forwardSpeedDelta > 0.0f || getBraking() == 0) ? getMaxAcceleration(bdt) : -getBraking();
  1665. Real maxTurnRate = getMaxTurnRate(bdt);
  1666. // what direction do we need to thrust in, in order to reach the goalpos?
  1667. Vector3 desiredThrustDir;
  1668. calcDirectionToApplyThrust(obj, physics, localGoalPos, maxAccel, desiredThrustDir);
  1669. // we might not be able to thrust in that dir, so thrust as closely as we can
  1670. Real maxThrustAngle = (maxTurnRate > 0) ? (m_template->m_maxThrustAngle) : 0;
  1671. Vector3 thrustDir;
  1672. Real thrustAngle = tryToRotateVector3D(maxThrustAngle, forwardDir, desiredThrustDir, thrustDir);
  1673. // note that we are trying to orient in the direction of our vel, not the dir of our thrust.
  1674. if (!isNearlyZero(physics->getVelocityMagnitude()))
  1675. {
  1676. const Coord3D* veltmp = physics->getVelocity();
  1677. Vector3 vel(veltmp->x, veltmp->y, veltmp->z);
  1678. Bool adjust = true;
  1679. if( obj->getStatusBits().test( OBJECT_STATUS_BRAKING ) )
  1680. {
  1681. // align to target, cause that's where we're going anyway.
  1682. vel.Set(goalPos.x - pos.x, goalPos.y-pos.y, goalPos.z-pos.z);
  1683. if (isNearlyZero(sqr(vel.X)+sqr(vel.Y)+sqr(vel.Z))) {
  1684. // we are at target.
  1685. adjust = false;
  1686. }
  1687. maxTurnRate = 3*maxTurnRate;
  1688. }
  1689. #ifdef USE_ZDIR_DAMPING
  1690. if (zDirDamping != 0.0f)
  1691. {
  1692. Vector3 vel2D(veltmp->x, veltmp->y, 0);
  1693. // no need to normalize -- this call does that internally
  1694. tryToRotateVector3D(-zDirDamping, vel, vel2D, vel);
  1695. }
  1696. #endif
  1697. if (adjust) {
  1698. /*Real orient =*/ tryToOrientInThisDirection3D(obj, maxTurnRate, vel);
  1699. }
  1700. }
  1701. if (forwardSpeedDelta != 0.0f || thrustAngle != 0.0f)
  1702. {
  1703. if (maxForwardSpeed <= 0.0f)
  1704. {
  1705. maxForwardSpeed = 0.01f; // In some cases, this is 0, hack for now. jba.
  1706. }
  1707. Real damping = clamp(0.0f, maxAccel / maxForwardSpeed, 1.0f);
  1708. Vector3 curVel(physics->getVelocity()->x, physics->getVelocity()->y, physics->getVelocity()->z);
  1709. Vector3 accelVec = thrustDir * maxAccel - curVel * damping;
  1710. //DEBUG_LOG(("accel %f (max %f) vel %f (max %f) damping %f\n",accelVec.Length(),maxAccel,curVel.Length(),maxForwardSpeed,damping));
  1711. Real mass = physics->getMass();
  1712. Coord3D force;
  1713. force.x = mass * accelVec.X;
  1714. force.y = mass * accelVec.Y;
  1715. force.z = mass * accelVec.Z;
  1716. // apply forces to object
  1717. physics->applyMotiveForce( &force );
  1718. }
  1719. }
  1720. //-------------------------------------------------------------------------------------------------
  1721. Real Locomotor::getSurfaceHtAtPt(Real x, Real y)
  1722. {
  1723. Real ht = 0;
  1724. Real z,waterZ;
  1725. if (TheTerrainLogic->isUnderwater(x, y, &waterZ, &z)) {
  1726. ht += waterZ;
  1727. } else {
  1728. ht += z;
  1729. }
  1730. return ht;
  1731. }
  1732. //-------------------------------------------------------------------------------------------------
  1733. Real Locomotor::calcLiftToUseAtPt(Object* obj, PhysicsBehavior *physics, Real curZ, Real surfaceAtPt, Real preferredHeight)
  1734. {
  1735. /*
  1736. take the classic equation:
  1737. x = x0 + v*t + 0.5*a*t^2
  1738. and solve for acceleration.
  1739. */
  1740. BodyDamageType bdt = obj->getBodyModule()->getDamageState();
  1741. Real maxGrossLift = getMaxLift(bdt);
  1742. Real maxNetLift = maxGrossLift + TheGlobalData->m_gravity; // note that gravity is always negative.
  1743. if (maxNetLift < 0)
  1744. maxNetLift = 0;
  1745. Real curVelZ = physics->getVelocity()->z;
  1746. // going down, braking is limited by net lift; going up, braking is limited by gravity
  1747. Real maxAccel;
  1748. if (getFlag(ULTRA_ACCURATE))
  1749. maxAccel = (curVelZ < 0) ? 2*maxNetLift : -2*maxNetLift;
  1750. else
  1751. maxAccel = (curVelZ < 0) ? maxNetLift : TheGlobalData->m_gravity;
  1752. // see how far we need to slow to dead stop, given max braking
  1753. Real desiredAccel;
  1754. const Real TINY_ACCEL = 0.001f;
  1755. if (fabs(maxAccel) > TINY_ACCEL)
  1756. {
  1757. Real deltaZ = preferredHeight - curZ;
  1758. // calc how far it will take for us to go from cur speed to zero speed, at max accel.
  1759. // Real brakeDist = calcSlowDownDist(curVelZ, 0, maxAccel);
  1760. // in theory, the above is the correct calculation, but in practice,
  1761. // doesn't work in some situations (eg, opening of USA01 map). Why, I dunno.
  1762. // But for now I have gone back to the old, looks-incorrect-to-me-but-works calc. (srj)
  1763. Real brakeDist = (sqr(curVelZ) / fabs(maxAccel));
  1764. if (fabs(brakeDist) > fabs(deltaZ))
  1765. {
  1766. // if the dist-to-accel (or dist-to-brake) is further than the dist-to-go,
  1767. // use the max accel.
  1768. desiredAccel = maxAccel;
  1769. }
  1770. else if (fabs(curVelZ) > m_template->m_speedLimitZ)
  1771. {
  1772. // or, if we're going too fast, limit it here.
  1773. desiredAccel = m_template->m_speedLimitZ - curVelZ;
  1774. }
  1775. else
  1776. {
  1777. // ok, figure out the correct accel to use to get us there at zero.
  1778. //
  1779. // dz = v t + 0.5 a t^2
  1780. // thus
  1781. // a = 2(dz - v t)/t^2
  1782. // and
  1783. // t = (-v +- sqrt(v*v + 2*a*dz))/a
  1784. //
  1785. // but if we assume t=1, then
  1786. // a=2(dz-v)
  1787. // then, plug it back in and see if t is really 1...
  1788. desiredAccel = 2.0f * (deltaZ - curVelZ);
  1789. }
  1790. }
  1791. else
  1792. {
  1793. desiredAccel = 0.0f;
  1794. }
  1795. Real liftToUse = desiredAccel - TheGlobalData->m_gravity;
  1796. if (getFlag(ULTRA_ACCURATE))
  1797. {
  1798. // in ultra-accurate mode, we allow cheating.
  1799. const Real UP_FACTOR = 3.0f;
  1800. if (liftToUse > UP_FACTOR*maxGrossLift)
  1801. liftToUse = UP_FACTOR*maxGrossLift;
  1802. // srj sez: we used to clip lift to zero here (not allowing neg lift).
  1803. // however, I now think that allowing neg lift in ultra-accurate mode is
  1804. // a good and desirable thing; in particular, it enables jets to complete
  1805. // "short" landings more accurately (previously they sometimes would "float"
  1806. // down, which sucked.) if you need to bump this back to zero, check it carefully...
  1807. else if (liftToUse < -maxGrossLift)
  1808. liftToUse = -maxGrossLift;
  1809. }
  1810. else
  1811. {
  1812. if (liftToUse > maxGrossLift)
  1813. liftToUse = maxGrossLift;
  1814. else if (liftToUse < 0.0f)
  1815. liftToUse = 0.0f;
  1816. }
  1817. return liftToUse;
  1818. }
  1819. //-------------------------------------------------------------------------------------------------
  1820. PhysicsTurningType Locomotor::rotateObjAroundLocoPivot(Object* obj, const Coord3D& goalPos,
  1821. Real maxTurnRate, Real *relAngle)
  1822. {
  1823. Real angle = obj->getOrientation();
  1824. Real offset = getTurnPivotOffset();
  1825. PhysicsTurningType turn = TURN_NONE;
  1826. if (getFlag(IS_BRAKING)) offset = 0.0f; // When braking we do exact movement towards goal, instead of physics.
  1827. //Rotating about pivot moves the object, and can make us miss our goal, so it is disabled. jba.
  1828. if (offset != 0.0f)
  1829. {
  1830. Real radius = obj->getGeometryInfo().getBoundingCircleRadius();
  1831. Real turnPointOffset = offset * radius;
  1832. Coord3D turnPos = *obj->getPosition();
  1833. const Coord3D* dir = obj->getUnitDirectionVector2D();
  1834. turnPos.x += dir->x * turnPointOffset;
  1835. turnPos.y += dir->y * turnPointOffset;
  1836. Real dx =goalPos.x - turnPos.x;
  1837. Real dy = goalPos.y - turnPos.y;
  1838. // If we are very close to the goal, we twitch due to rounding error. So just return. jba.
  1839. if (fabs(dx)<0.1f && fabs(dy)<0.1f) return TURN_NONE;
  1840. Real desiredAngle = atan2(dy, dx);
  1841. Real amount = stdAngleDiff(desiredAngle, angle);
  1842. if (relAngle) *relAngle = amount;
  1843. if (amount>maxTurnRate) {
  1844. amount = maxTurnRate;
  1845. turn = TURN_POSITIVE;
  1846. } else if (amount < -maxTurnRate) {
  1847. amount = -maxTurnRate;
  1848. turn = TURN_NEGATIVE;
  1849. } else {
  1850. turn = TURN_NONE;
  1851. }
  1852. #if 0
  1853. Coord3D desiredPos = *obj->getPosition(); // well, desired Dir, anyway
  1854. desiredPos.x += Cos(angle + amount) * radius;
  1855. desiredPos.y += Sin(angle + amount) * radius;
  1856. // so, the thing is, we want to rotate ourselves so that our *center* is rotated
  1857. // by the given amount, but the rotation must be around turnPos. so do a little
  1858. // back-calculation.
  1859. Real angleDesiredForTurnPos = atan2(desiredPos.y - turnPos.y, desiredPos.x - turnPos.x);
  1860. amount = angleDesiredForTurnPos - angle;
  1861. #endif
  1862. /// @todo srj -- there's probably a more efficient & more direct way to do this. find it.
  1863. Matrix3D mtx;
  1864. Matrix3D tmp(1);
  1865. tmp.Translate(turnPos.x, turnPos.y, 0);
  1866. tmp.In_Place_Pre_Rotate_Z(amount);
  1867. tmp.Translate(-turnPos.x, -turnPos.y, 0);
  1868. mtx.mul(tmp, *obj->getTransformMatrix());
  1869. obj->setTransformMatrix(&mtx);
  1870. }
  1871. else
  1872. {
  1873. Real desiredAngle = atan2(goalPos.y - obj->getPosition()->y, goalPos.x - obj->getPosition()->x);
  1874. Real amount = stdAngleDiff(desiredAngle, angle);
  1875. if (relAngle) *relAngle = amount;
  1876. if (amount>maxTurnRate) {
  1877. amount = maxTurnRate;
  1878. turn = TURN_POSITIVE;
  1879. } else if (amount < -maxTurnRate) {
  1880. amount = -maxTurnRate;
  1881. turn = TURN_NEGATIVE;
  1882. } else {
  1883. turn = TURN_NONE;
  1884. }
  1885. obj->setOrientation( normalizeAngle(angle + amount) );
  1886. }
  1887. return turn;
  1888. }
  1889. //-------------------------------------------------------------------------------------------------
  1890. /*
  1891. return true if we can maintain the position without being called every frame (eg, we are
  1892. resting on the ground), false if not (eg, we are hovering or circling)
  1893. */
  1894. Bool Locomotor::handleBehaviorZ(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos)
  1895. {
  1896. Bool requiresConstantCalling = TRUE;
  1897. // keep the agent aligned on the terrain
  1898. switch(m_template->m_behaviorZ)
  1899. {
  1900. case Z_NO_Z_MOTIVE_FORCE:
  1901. // nothing to do.
  1902. requiresConstantCalling = FALSE;
  1903. break;
  1904. case Z_SEA_LEVEL:
  1905. requiresConstantCalling = TRUE;
  1906. if( !obj->isDisabledByType( DISABLED_HELD ) )
  1907. {
  1908. Coord3D pos = *obj->getPosition();
  1909. Real waterZ;
  1910. if (TheTerrainLogic->isUnderwater(pos.x, pos.y, &waterZ)) {
  1911. pos.z = waterZ;
  1912. } else {
  1913. pos.z = TheTerrainLogic->getLayerHeight(pos.x, pos.y, obj->getLayer());
  1914. }
  1915. obj->setPosition(&pos);
  1916. }
  1917. break;
  1918. case Z_FIXED_SURFACE_RELATIVE_HEIGHT:
  1919. case Z_FIXED_ABSOLUTE_HEIGHT:
  1920. requiresConstantCalling = TRUE;
  1921. {
  1922. Coord3D pos = *obj->getPosition();
  1923. Bool surfaceRel = (m_template->m_behaviorZ == Z_FIXED_SURFACE_RELATIVE_HEIGHT);
  1924. Real surfaceHt = surfaceRel ? getSurfaceHtAtPt(pos.x, pos.y) : 0.0f;
  1925. pos.z = m_preferredHeight + (surfaceRel ? surfaceHt : 0);
  1926. obj->setPosition(&pos);
  1927. }
  1928. break;
  1929. case Z_RELATIVE_TO_GROUND_AND_BUILDINGS:
  1930. requiresConstantCalling = TRUE;
  1931. {
  1932. // srj sez: use getGroundOrStructureHeight(), because someday it will cache building heights...
  1933. Coord3D pos = *obj->getPosition();
  1934. Real surfaceHt = ThePartitionManager->getGroundOrStructureHeight(pos.x, pos.y);
  1935. pos.z = m_preferredHeight + surfaceHt;
  1936. obj->setPosition(&pos);
  1937. }
  1938. break;
  1939. case Z_SMOOTH_RELATIVE_TO_HIGHEST_LAYER:
  1940. requiresConstantCalling = TRUE;
  1941. {
  1942. if (m_preferredHeight != 0.0f || getFlag(PRECISE_Z_POS))
  1943. {
  1944. Coord3D pos = *obj->getPosition();
  1945. // srj sez: if we aren't on the ground, never find the ground layer
  1946. PathfindLayerEnum layerAtDest = obj->getLayer();
  1947. if (layerAtDest == LAYER_GROUND)
  1948. layerAtDest = TheTerrainLogic->getHighestLayerForDestination( &pos );
  1949. Real surfaceHt;
  1950. Coord3D normal;
  1951. const Bool clip = false; // return the height, even if off the edge of the bridge proper.
  1952. surfaceHt = TheTerrainLogic->getLayerHeight( pos.x, pos.y, layerAtDest, &normal, clip );
  1953. Real preferredHeight = m_preferredHeight + surfaceHt;
  1954. if (getFlag(PRECISE_Z_POS))
  1955. preferredHeight = goalPos.z;
  1956. Real delta = preferredHeight - pos.z;
  1957. delta *= getPreferredHeightDamping();
  1958. preferredHeight = pos.z + delta;
  1959. Real liftToUse = calcLiftToUseAtPt(obj, physics, pos.z, surfaceHt, preferredHeight);
  1960. //DEBUG_LOG(("HandleBZ %d LiftToUse %f\n",TheGameLogic->getFrame(),liftToUse));
  1961. if (liftToUse != 0.0f)
  1962. {
  1963. Coord3D force;
  1964. force.x = 0.0f;
  1965. force.y = 0.0f;
  1966. force.z = liftToUse * physics->getMass();
  1967. physics->applyMotiveForce(&force);
  1968. }
  1969. }
  1970. }
  1971. break;
  1972. case Z_SURFACE_RELATIVE_HEIGHT:
  1973. case Z_ABSOLUTE_HEIGHT:
  1974. requiresConstantCalling = TRUE;
  1975. {
  1976. if (m_preferredHeight != 0.0f || getFlag(PRECISE_Z_POS))
  1977. {
  1978. Coord3D pos = *obj->getPosition();
  1979. Bool surfaceRel = (m_template->m_behaviorZ == Z_SURFACE_RELATIVE_HEIGHT);
  1980. Real surfaceHt = surfaceRel ? getSurfaceHtAtPt(pos.x, pos.y) : 0.0f;
  1981. Real preferredHeight = m_preferredHeight + (surfaceRel ? surfaceHt : 0);
  1982. if (getFlag(PRECISE_Z_POS))
  1983. preferredHeight = goalPos.z;
  1984. Real delta = preferredHeight - pos.z;
  1985. delta *= getPreferredHeightDamping();
  1986. preferredHeight = pos.z + delta;
  1987. Real liftToUse = calcLiftToUseAtPt(obj, physics, pos.z, surfaceHt, preferredHeight);
  1988. //DEBUG_LOG(("HandleBZ %d LiftToUse %f\n",TheGameLogic->getFrame(),liftToUse));
  1989. if (liftToUse != 0.0f)
  1990. {
  1991. Coord3D force;
  1992. force.x = 0.0f;
  1993. force.y = 0.0f;
  1994. force.z = liftToUse * physics->getMass();
  1995. physics->applyMotiveForce(&force);
  1996. }
  1997. }
  1998. }
  1999. break;
  2000. }
  2001. return requiresConstantCalling;
  2002. }
  2003. //-------------------------------------------------------------------------------------------------
  2004. void Locomotor::moveTowardsPositionOther(Object* obj, PhysicsBehavior *physics, const Coord3D& goalPos, Real onPathDistToGoal, Real desiredSpeed)
  2005. {
  2006. BodyDamageType bdt = obj->getBodyModule()->getDamageState();
  2007. Real maxAcceleration = getMaxAcceleration(bdt);
  2008. // sanity, we cannot use desired speed that is greater than our max speed we are capable of moving at
  2009. Real maxSpeed = getMaxSpeedForCondition(bdt);
  2010. if( desiredSpeed > maxSpeed )
  2011. desiredSpeed = maxSpeed;
  2012. Real goalSpeed = desiredSpeed;
  2013. Real actualSpeed = physics->getForwardSpeed2D();
  2014. // Locomotion for other things, ie don't know what it is jba :)
  2015. //
  2016. // Orient toward goal position
  2017. // exception: if very close (ie, we could get there in 2 frames or less),\
  2018. // and ULTRA_ACCURATE, just slide into place
  2019. //
  2020. const Coord3D* pos = obj->getPosition();
  2021. Coord3D dirToApplyForce = *obj->getUnitDirectionVector2D();
  2022. //DEBUG_ASSERTLOG(!getFlag(ULTRA_ACCURATE),("thresh %f %f (%f %f)\n",
  2023. //fabs(goalPos.y - pos->y),fabs(goalPos.x - pos->x),
  2024. //fabs(goalPos.y - pos->y)/goalSpeed,fabs(goalPos.x - pos->x)/goalSpeed));
  2025. if (getFlag(ULTRA_ACCURATE) &&
  2026. fabs(goalPos.y - pos->y) <= goalSpeed * m_template->m_ultraAccurateSlideIntoPlaceFactor &&
  2027. fabs(goalPos.x - pos->x) <= goalSpeed * m_template->m_ultraAccurateSlideIntoPlaceFactor)
  2028. {
  2029. // don't turn, just slide in the right direction
  2030. physics->setTurning(TURN_NONE);
  2031. dirToApplyForce.x = goalPos.x - pos->x;
  2032. dirToApplyForce.y = goalPos.y - pos->y;
  2033. dirToApplyForce.z = 0.0f;
  2034. dirToApplyForce.normalize();
  2035. }
  2036. else
  2037. {
  2038. PhysicsTurningType rotating = rotateTowardsPosition(obj, goalPos);
  2039. physics->setTurning(rotating);
  2040. }
  2041. if (!getFlag(NO_SLOW_DOWN_AS_APPROACHING_DEST))
  2042. {
  2043. Real slowDownDist = calcSlowDownDist(actualSpeed, m_template->m_minSpeed, getBraking());
  2044. if (onPathDistToGoal < slowDownDist)
  2045. {
  2046. goalSpeed = m_template->m_minSpeed;
  2047. }
  2048. }
  2049. //
  2050. // Maintain goal speed
  2051. //
  2052. Real speedDelta = goalSpeed - actualSpeed;
  2053. if (speedDelta != 0.0f)
  2054. {
  2055. Real mass = physics->getMass();
  2056. Real acceleration = (speedDelta > 0.0f) ? maxAcceleration : -getBraking();
  2057. Real accelForce = mass * acceleration;
  2058. /*
  2059. don't accelerate/brake more than necessary. do a quick calc to
  2060. see how much force we really need to achieve our goal speed...
  2061. */
  2062. Real maxForceNeeded = mass * speedDelta;
  2063. if (fabs(accelForce) > fabs(maxForceNeeded))
  2064. accelForce = maxForceNeeded;
  2065. Coord3D force;
  2066. force.x = accelForce * dirToApplyForce.x;
  2067. force.y = accelForce * dirToApplyForce.y;
  2068. force.z = 0.0f;
  2069. // apply forces to object
  2070. physics->applyMotiveForce( &force );
  2071. }
  2072. }
  2073. //-------------------------------------------------------------------------------------------------
  2074. /*
  2075. return true if we can maintain the position without being called every frame (eg, we are
  2076. resting on the ground), false if not (eg, we are hovering or circling)
  2077. */
  2078. Bool Locomotor::locoUpdate_maintainCurrentPosition(Object* obj)
  2079. {
  2080. if (!getFlag(MAINTAIN_POS_IS_VALID))
  2081. {
  2082. m_maintainPos = *obj->getPosition();
  2083. setFlag(MAINTAIN_POS_IS_VALID, true);
  2084. }
  2085. m_donutTimer = TheGameLogic->getFrame()+DONUT_TIME_DELAY_SECONDS*LOGICFRAMES_PER_SECOND;
  2086. setFlag(IS_BRAKING, false);
  2087. PhysicsBehavior *physics = obj->getPhysics();
  2088. if (physics == NULL)
  2089. {
  2090. DEBUG_CRASH(("you can only apply Locomotors to objects with Physics"));
  2091. return TRUE;
  2092. }
  2093. #ifdef DEBUG_OBJECT_ID_EXISTS
  2094. // DEBUG_ASSERTLOG(obj->getID() != TheObjectIDToDebug, ("locoUpdate_maintainCurrentPosition %f %f %f, speed %f (%f)\n",m_maintainPos.x,m_maintainPos.y,m_maintainPos.z,physics->getSpeed(),physics->getForwardSpeed2D()));
  2095. #endif
  2096. Bool requiresConstantCalling = TRUE; // assume the worst.
  2097. switch (m_template->m_appearance)
  2098. {
  2099. case LOCO_THRUST:
  2100. maintainCurrentPositionThrust(obj, physics);
  2101. requiresConstantCalling = TRUE;
  2102. break;
  2103. case LOCO_LEGS_TWO:
  2104. maintainCurrentPositionLegs(obj, physics);
  2105. requiresConstantCalling = FALSE;
  2106. break;
  2107. case LOCO_CLIMBER:
  2108. maintainCurrentPositionLegs(obj, physics);
  2109. requiresConstantCalling = FALSE;
  2110. break;
  2111. case LOCO_WHEELS_FOUR:
  2112. case LOCO_MOTORCYCLE:
  2113. maintainCurrentPositionWheels(obj, physics);
  2114. requiresConstantCalling = FALSE;
  2115. break;
  2116. case LOCO_TREADS:
  2117. maintainCurrentPositionTreads(obj, physics);
  2118. requiresConstantCalling = FALSE;
  2119. break;
  2120. case LOCO_HOVER:
  2121. maintainCurrentPositionHover(obj, physics);
  2122. requiresConstantCalling = TRUE;
  2123. break;
  2124. case LOCO_WINGS:
  2125. maintainCurrentPositionWings(obj, physics);
  2126. requiresConstantCalling = TRUE;
  2127. break;
  2128. case LOCO_OTHER:
  2129. default:
  2130. maintainCurrentPositionOther(obj, physics);
  2131. requiresConstantCalling = TRUE;
  2132. break;
  2133. }
  2134. // but we do need to do this even if not moving, for hovering/Thrusting things.
  2135. if (handleBehaviorZ(obj, physics, m_maintainPos))
  2136. requiresConstantCalling = TRUE;
  2137. return requiresConstantCalling;
  2138. }
  2139. //-------------------------------------------------------------------------------------------------
  2140. void Locomotor::maintainCurrentPositionThrust(Object* obj, PhysicsBehavior *physics)
  2141. {
  2142. DEBUG_ASSERTCRASH(getFlag(MAINTAIN_POS_IS_VALID), ("invalid maintain pos"));
  2143. /// @todo srj -- should these also use the "circling radius" stuff, like wings?
  2144. moveTowardsPositionThrust(obj, physics, m_maintainPos, 0, getMinSpeed());
  2145. }
  2146. //-------------------------------------------------------------------------------------------------
  2147. void Locomotor::maintainCurrentPositionWings(Object* obj, PhysicsBehavior *physics)
  2148. {
  2149. DEBUG_ASSERTCRASH(getFlag(MAINTAIN_POS_IS_VALID), ("invalid maintain pos"));
  2150. physics->setTurning(TURN_NONE);
  2151. if (physics->isMotive() && obj->isAboveTerrain()) // no need to stop something that isn't moving (or is just sitting on the ground)
  2152. {
  2153. // aim for the spot on the opposite side of the circle.
  2154. BodyDamageType bdt = obj->getBodyModule()->getDamageState();
  2155. Real turnRadius = m_template->m_circlingRadius;
  2156. if (turnRadius == 0.0f)
  2157. turnRadius = calcMinTurnRadius(bdt, NULL);
  2158. // find the direction towards our "maintain pos"
  2159. const Coord3D* pos = obj->getPosition();
  2160. Real dx = m_maintainPos.x - pos->x;
  2161. Real dy = m_maintainPos.y - pos->y;
  2162. Real angleTowardMaintainPos =
  2163. (isNearlyZero(dx) && isNearlyZero(dy)) ?
  2164. obj->getOrientation() :
  2165. atan2(dy, dx);
  2166. Real aimDir = (PI - PI/8);
  2167. if (turnRadius < 0)
  2168. {
  2169. turnRadius = -turnRadius;
  2170. aimDir = -aimDir;
  2171. }
  2172. angleTowardMaintainPos += aimDir;
  2173. // project a spot "radius" dist away from it, in that dir
  2174. Coord3D desiredPos = m_maintainPos;
  2175. desiredPos.x += Cos(angleTowardMaintainPos) * turnRadius;
  2176. desiredPos.y += Sin(angleTowardMaintainPos) * turnRadius;
  2177. moveTowardsPositionWings(obj, physics, desiredPos, 0, m_template->m_minSpeed);
  2178. }
  2179. }
  2180. //-------------------------------------------------------------------------------------------------
  2181. void Locomotor::maintainCurrentPositionHover(Object* obj, PhysicsBehavior *physics)
  2182. {
  2183. physics->setTurning(TURN_NONE);
  2184. if (physics->isMotive()) // no need to stop something that isn't moving.
  2185. {
  2186. DEBUG_ASSERTCRASH(m_template->m_minSpeed == 0.0f, ("HOVER should always have zero minSpeeds (otherwise, they WING)"));
  2187. BodyDamageType bdt = obj->getBodyModule()->getDamageState();
  2188. Real maxAcceleration = getMaxAcceleration(bdt);
  2189. Real actualSpeed = physics->getForwardSpeed2D();
  2190. //
  2191. // Stop
  2192. //
  2193. Real minSpeed = max( 1.0E-10f, m_template->m_minSpeed );
  2194. Real speedDelta = minSpeed - actualSpeed;
  2195. if (fabs(speedDelta) > minSpeed)
  2196. {
  2197. Real mass = physics->getMass();
  2198. Real acceleration = (speedDelta > 0.0f) ? maxAcceleration : -getBraking();
  2199. Real accelForce = mass * acceleration;
  2200. /*
  2201. don't accelerate/brake more than necessary. do a quick calc to
  2202. see how much force we really need to achieve our goal speed...
  2203. */
  2204. Real maxForceNeeded = mass * speedDelta;
  2205. if (fabs(accelForce) > fabs(maxForceNeeded))
  2206. accelForce = maxForceNeeded;
  2207. const Coord3D *dir = obj->getUnitDirectionVector2D();
  2208. Coord3D force;
  2209. force.x = accelForce * dir->x;
  2210. force.y = accelForce * dir->y;
  2211. force.z = 0.0f;
  2212. // Apply a random kick (if applicable) to dirty-up visually.
  2213. // The idea is that chopper pilots have to do course corrections all the time
  2214. // Because of changes in wind, pressure, etc.
  2215. // Those changes are added here, then the
  2216. // apply forces to object
  2217. physics->applyMotiveForce( &force );
  2218. }
  2219. }
  2220. }
  2221. //-------------------------------------------------------------------------------------------------
  2222. void Locomotor::maintainCurrentPositionOther(Object* obj, PhysicsBehavior *physics)
  2223. {
  2224. physics->setTurning(TURN_NONE);
  2225. if (physics->isMotive()) // no need to stop something that isn't moving.
  2226. {
  2227. physics->scrubVelocity2D(0); // stop.
  2228. }
  2229. }
  2230. //-------------------------------------------------------------------------------------------------
  2231. //-------------------------------------------------------------------------------------------------
  2232. //-------------------------------------------------------------------------------------------------
  2233. //-------------------------------------------------------------------------------------------------
  2234. LocomotorSet::LocomotorSet()
  2235. {
  2236. m_locomotors.clear();
  2237. m_validLocomotorSurfaces = 0;
  2238. m_downhillOnly = FALSE;
  2239. }
  2240. //-------------------------------------------------------------------------------------------------
  2241. LocomotorSet::LocomotorSet(const LocomotorSet& that)
  2242. {
  2243. DEBUG_CRASH(("unimplemented"));
  2244. }
  2245. //-------------------------------------------------------------------------------------------------
  2246. LocomotorSet& LocomotorSet::operator=(const LocomotorSet& that)
  2247. {
  2248. if (this != &that)
  2249. {
  2250. DEBUG_CRASH(("unimplemented"));
  2251. }
  2252. return *this;
  2253. }
  2254. //-------------------------------------------------------------------------------------------------
  2255. LocomotorSet::~LocomotorSet()
  2256. {
  2257. clear();
  2258. }
  2259. // ------------------------------------------------------------------------------------------------
  2260. /** CRC */
  2261. // ------------------------------------------------------------------------------------------------
  2262. void LocomotorSet::crc( Xfer *xfer )
  2263. {
  2264. } // end crc
  2265. // ------------------------------------------------------------------------------------------------
  2266. /** Xfer method
  2267. * Version Info:
  2268. * 1: Initial version */
  2269. // ------------------------------------------------------------------------------------------------
  2270. void LocomotorSet::xfer( Xfer *xfer )
  2271. {
  2272. // version
  2273. const XferVersion currentVersion = 1;
  2274. XferVersion version = currentVersion;
  2275. xfer->xferVersion( &version, currentVersion );
  2276. // count of vector
  2277. UnsignedShort count = m_locomotors.size();
  2278. xfer->xferUnsignedShort( &count );
  2279. // data
  2280. if (xfer->getXferMode() == XFER_SAVE)
  2281. {
  2282. for (LocomotorVector::iterator it = m_locomotors.begin(); it != m_locomotors.end(); ++it)
  2283. {
  2284. Locomotor* loco = *it;
  2285. AsciiString name = loco->getTemplateName();
  2286. xfer->xferAsciiString(&name);
  2287. xfer->xferSnapshot(loco);
  2288. }
  2289. }
  2290. else if (xfer->getXferMode() == XFER_LOAD)
  2291. {
  2292. // vector should be empty at this point
  2293. if (m_locomotors.empty() == FALSE)
  2294. {
  2295. DEBUG_CRASH(( "LocomotorSet::xfer - vector is not empty, but should be\n" ));
  2296. throw XFER_LIST_NOT_EMPTY;
  2297. }
  2298. for (UnsignedShort i = 0; i < count; ++i)
  2299. {
  2300. AsciiString name;
  2301. xfer->xferAsciiString(&name);
  2302. const LocomotorTemplate* lt = TheLocomotorStore->findLocomotorTemplate(NAMEKEY(name));
  2303. if (lt == NULL)
  2304. {
  2305. DEBUG_CRASH(( "LocomotorSet::xfer - template %s not found\n", name.str() ));
  2306. throw XFER_UNKNOWN_STRING;
  2307. }
  2308. Locomotor* loco = TheLocomotorStore->newLocomotor(lt);
  2309. xfer->xferSnapshot(loco);
  2310. m_locomotors.push_back(loco);
  2311. }
  2312. }
  2313. xfer->xferInt(&m_validLocomotorSurfaces);
  2314. xfer->xferBool(&m_downhillOnly);
  2315. }
  2316. // ------------------------------------------------------------------------------------------------
  2317. /** Load post process */
  2318. // ------------------------------------------------------------------------------------------------
  2319. void LocomotorSet::loadPostProcess( void )
  2320. {
  2321. } // end loadPostProcess
  2322. //-------------------------------------------------------------------------------------------------
  2323. void LocomotorSet::xferSelfAndCurLocoPtr(Xfer *xfer, Locomotor** loco)
  2324. {
  2325. xfer->xferSnapshot(this);
  2326. if (xfer->getXferMode() == XFER_SAVE)
  2327. {
  2328. AsciiString name;
  2329. if (*loco)
  2330. name = (*loco)->getTemplateName();
  2331. xfer->xferAsciiString(&name);
  2332. }
  2333. else if (xfer->getXferMode() == XFER_LOAD)
  2334. {
  2335. AsciiString name;
  2336. xfer->xferAsciiString(&name);
  2337. if (name.isEmpty())
  2338. {
  2339. *loco = NULL;
  2340. }
  2341. else
  2342. {
  2343. for (int i = 0; i < m_locomotors.size(); ++i)
  2344. {
  2345. if (m_locomotors[i]->getTemplateName() == name)
  2346. {
  2347. *loco = m_locomotors[i];
  2348. return;
  2349. }
  2350. }
  2351. DEBUG_CRASH(( "LocomotorSet::xfer - template %s not found\n", name.str() ));
  2352. throw XFER_UNKNOWN_STRING;
  2353. }
  2354. }
  2355. }
  2356. //-------------------------------------------------------------------------------------------------
  2357. void LocomotorSet::clear()
  2358. {
  2359. for (int i = 0; i < m_locomotors.size(); ++i)
  2360. {
  2361. if (m_locomotors[i])
  2362. m_locomotors[i]->deleteInstance();
  2363. }
  2364. m_locomotors.clear();
  2365. m_validLocomotorSurfaces = 0;
  2366. m_downhillOnly = FALSE;
  2367. }
  2368. //-------------------------------------------------------------------------------------------------
  2369. void LocomotorSet::addLocomotor(const LocomotorTemplate* lt)
  2370. {
  2371. Locomotor* loco = TheLocomotorStore->newLocomotor(lt);
  2372. if (loco)
  2373. {
  2374. m_locomotors.push_back(loco);
  2375. m_validLocomotorSurfaces |= loco->getLegalSurfaces();
  2376. if (loco->getIsDownhillOnly())
  2377. {
  2378. m_downhillOnly = TRUE;
  2379. }
  2380. else // Previous locos were gravity only, but this one isn't!
  2381. {
  2382. DEBUG_ASSERTCRASH(!m_downhillOnly,("LocomotorSet, YOU CAN NOT MIX DOWNHILL-ONLY LOCOMOTORS WITH NON-DOWNHILL-ONLY ONES."));
  2383. }
  2384. }
  2385. }
  2386. //-------------------------------------------------------------------------------------------------
  2387. Locomotor* LocomotorSet::findLocomotor(LocomotorSurfaceTypeMask t)
  2388. {
  2389. for (LocomotorVector::iterator it = m_locomotors.begin(); it != m_locomotors.end(); ++it)
  2390. {
  2391. Locomotor* curLocomotor = *it;
  2392. if (curLocomotor && (curLocomotor->getLegalSurfaces() & t))
  2393. return curLocomotor;
  2394. }
  2395. return NULL;
  2396. }