Locomotor.cpp 89 KB

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