vehicle.cpp 73 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653
  1. /*
  2. ** Command & Conquer Renegade(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. *** Confidential - Westwood Studios ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Commando *
  23. * *
  24. * $Archive:: /Commando/Code/Combat/vehicle.cpp $*
  25. * *
  26. * $Author:: Byon_g $*
  27. * *
  28. * $Modtime:: 3/29/02 4:08p $*
  29. * *
  30. * $Revision:: 266 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. /*
  36. ** Includes
  37. */
  38. #include "vehicle.h"
  39. #include "animcontrol.h"
  40. #include "debug.h"
  41. #include "combat.h"
  42. #include "pscene.h"
  43. #include "assets.h"
  44. #include "rendobj.h"
  45. #include "soldier.h"
  46. #include "weapons.h"
  47. #include "damage.h"
  48. #include "wwpacket.h"
  49. #include "gameobjmanager.h"
  50. #include <stdio.h>
  51. #include "WWAudio.H"
  52. #include "Sound3D.H"
  53. #include "vehiclephys.h"
  54. #include "motorcycle.h"
  55. #include "trackedvehicle.h"
  56. #include "vtolvehicle.h"
  57. #include "wheelvehicle.h"
  58. #include "wheel.h"
  59. #include "phys3.h"
  60. #include "humanphys.h"
  61. #include "persistfactory.h"
  62. #include "combatchunkid.h"
  63. #include "simpledefinitionfactory.h"
  64. #include "wwhack.h"
  65. #include "explosion.h"
  66. #include "surfaceeffects.h"
  67. #include "bitpackids.h"
  68. #include "wwprofile.h"
  69. #include "playertype.h"
  70. #include "playerdata.h"
  71. #include "translatedb.h"
  72. #include "apppackettypes.h"
  73. #include "stealtheffect.h"
  74. #include "gametype.h"
  75. #include "globalsettings.h"
  76. #include "basecontroller.h"
  77. #include "string_ids.h"
  78. #include "specialbuilds.h"
  79. /*
  80. ** Local prototypes
  81. */
  82. static void Set_Subobject_Visibility (RenderObjClass *model, int bone_index, bool show);
  83. /*
  84. ** Defines
  85. */
  86. #define NORMALIZE_RADIANS( v ) while ( v > (float)DEG_TO_RAD( 180.0f ) ) v -= (float)DEG_TO_RAD( 360.0f ); \
  87. while ( v < (float)DEG_TO_RAD( -180.0f ) ) v += (float)DEG_TO_RAD( 360.0f );
  88. //
  89. // Class statics
  90. //
  91. bool VehicleGameObj::DefaultDriverIsGunner = false;
  92. bool VehicleGameObj::CameraLockedToTurret = false;
  93. /*
  94. ** Target Steering Option.
  95. ** (gth) 07/11/2001 - making this default to true
  96. ** (gth) 08/21/2001 - new default is false, user can turn it on with a console command...
  97. */
  98. bool _Use_Target_Steering = false;
  99. bool VehicleGameObj::Toggle_Target_Steering( void )
  100. {
  101. return _Use_Target_Steering = !_Use_Target_Steering;
  102. }
  103. void VehicleGameObj::Set_Target_Steering( bool onoff )
  104. {
  105. _Use_Target_Steering = onoff;
  106. return ;
  107. }
  108. bool VehicleGameObj::Is_Target_Steering( void )
  109. {
  110. return _Use_Target_Steering;
  111. }
  112. /*
  113. ** VehicleGameObjDef
  114. */
  115. DECLARE_FORCE_LINK( Vehicle )
  116. SimplePersistFactoryClass<VehicleGameObjDef, CHUNKID_GAME_OBJECT_DEF_VEHICLE> _VehicleGameObjDefPersistFactory;
  117. DECLARE_DEFINITION_FACTORY(VehicleGameObjDef, CLASSID_GAME_OBJECT_DEF_VEHICLE, "Vehicle") _VehicleGameObjDefDefFactory;
  118. VehicleGameObjDef::VehicleGameObjDef( void ) :
  119. Type( VEHICLE_TYPE_CAR ),
  120. TurnRadius( 10.0f ),
  121. OccupantsVisible( true ),
  122. EngineSoundMaxPitchFactor( 2.0F ),
  123. SightDownMuzzle( false ),
  124. Aim2D( true ),
  125. SquishVelocity( 1.5f ),
  126. VehicleNameID( 0 ),
  127. NumSeats( 2 ),
  128. GDIDamageReportID(0),
  129. NodDamageReportID(0),
  130. GDIDestroyReportID(0),
  131. NodDestroyReportID(0)
  132. {
  133. // initialize all engine sound defs to zero
  134. for (int i=0; i<MAX_ENGINE_SOUND_STATES; i++) {
  135. EngineSound[i] = 0;
  136. }
  137. MODEL_DEF_PARAM( VehicleGameObjDef, PhysDefID, "DynamicPhysDef" );
  138. EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_STRING, TypeName );
  139. EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_STRING, Fire0Anim );
  140. EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_STRING, Fire1Anim );
  141. EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_STRING, Profile );
  142. EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_FLOAT, TurnRadius );
  143. EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_FLOAT, SquishVelocity );
  144. EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_BOOL, Aim2D );
  145. #ifdef PARAM_EDITING_ON
  146. EnumParameterClass *param;
  147. param = new EnumParameterClass( (int*)&Type );
  148. param->Set_Name ("Type");
  149. param->Add_Value ( "Car", VEHICLE_TYPE_CAR );
  150. param->Add_Value ( "Tank", VEHICLE_TYPE_TANK );
  151. param->Add_Value ( "Bike", VEHICLE_TYPE_BIKE );
  152. param->Add_Value ( "Flying", VEHICLE_TYPE_FLYING );
  153. param->Add_Value ( "Turret", VEHICLE_TYPE_TURRET );
  154. GENERIC_EDITABLE_PARAM(VehicleGameObjDef,param)
  155. #endif
  156. EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_BOOL, OccupantsVisible );
  157. // engine sounds
  158. FLOAT_EDITABLE_PARAM(VehicleGameObjDef, EngineSoundMaxPitchFactor, 1.0F, 10.0F);
  159. NAMED_EDITABLE_PARAM(VehicleGameObjDef, ParameterClass::TYPE_SOUNDDEFINITIONID, EngineSound[ENGINE_SOUND_STATE_STARTING],"Engine Start Sound");
  160. NAMED_EDITABLE_PARAM(VehicleGameObjDef, ParameterClass::TYPE_SOUNDDEFINITIONID, EngineSound[ENGINE_SOUND_STATE_RUNNING],"Engine Running Loop");
  161. NAMED_EDITABLE_PARAM(VehicleGameObjDef, ParameterClass::TYPE_SOUNDDEFINITIONID, EngineSound[ENGINE_SOUND_STATE_STOPPING],"Engine Stop Sound");
  162. // NAMED_EDITABLE_PARAM(VehicleGameObjDef, ParameterClass::TYPE_SOUNDDEFINITIONID, EngineSound[ENGINE_SOUND_STATE_OFF],"Engine Off Sound");
  163. EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_BOOL, SightDownMuzzle );
  164. EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, VehicleNameID );
  165. EDITABLE_PARAM( VehicleGameObjDef, ParameterClass::TYPE_INT, NumSeats );
  166. EDITABLE_PARAM(VehicleGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, GDIDamageReportID);
  167. EDITABLE_PARAM(VehicleGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, NodDamageReportID);
  168. EDITABLE_PARAM(VehicleGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, GDIDestroyReportID);
  169. EDITABLE_PARAM(VehicleGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, NodDestroyReportID);
  170. }
  171. VehicleGameObjDef::~VehicleGameObjDef( void )
  172. {
  173. Free_Transition_List();
  174. }
  175. uint32 VehicleGameObjDef::Get_Class_ID (void) const
  176. {
  177. return CLASSID_GAME_OBJECT_DEF_VEHICLE;
  178. }
  179. PersistClass * VehicleGameObjDef::Create( void ) const
  180. {
  181. VehicleGameObj * obj = new VehicleGameObj;
  182. obj->Init( *this );
  183. return obj;
  184. }
  185. enum {
  186. CHUNKID_DEF_PARENT = 930991656,
  187. CHUNKID_DEF_VARIABLES,
  188. CHUNKID_DEF_TRANSITION,
  189. MICROCHUNKID_TYPE = 1,
  190. MICROCHUNKID_TYPE_NAME,
  191. MICROCHUNKID_FIRE0ANIM,
  192. MICROCHUNKID_FIRE1ANIM,
  193. MICROCHUNKID_PROFILE,
  194. MICROCHUNKID_WEAPON_TURN_TRANS,
  195. XXXMICROCHUNKID_SOUND,
  196. MICROCHUNKID_EMITER_NAME,
  197. MICROCHUNKID_EMITER_OFFSET,
  198. MICROCHUNKID_EMITER2_NAME,
  199. MICROCHUNKID_EMITER2_OFFSET,
  200. XXX_MICROCHUNKID_MASS,
  201. XXX_MICROCHUNKID_MAX_ENGINE_TORQUE,
  202. XXX_MICROCHUNKID_STEERING_ANGLE,
  203. XXX_MICROCHUNKID_SPRING_CONSTANT,
  204. XXX_MICROCHUNKID_DAMPING_COEFFIENT,
  205. XXX_MICROCHUNKID_SPRING_LENGTH,
  206. MICROCHUNKID_PHYS_ID,
  207. MICROCHUNKID_TURN_RADIUS,
  208. MICROCHUNKID_OCCUPANTS_VISIBLE,
  209. XXX_MICROCHUNKID_ENGINE_SOUND_RPM_SCALE_MIN,
  210. XXX_MICROCHUNKID_ENGINE_SOUND_RPM_SCALE_MAX,
  211. MICROCHUNKID_ENGINE_START_SOUND,
  212. MICROCHUNKID_ENGINE_RUN_SOUND,
  213. MICROCHUNKID_ENGINE_STOP_SOUND,
  214. MICROCHUNKID_ENGINE_OFF_SOUND,
  215. MICROCHUNKID_DEF_SIGHT_DOWN_MUZZLE,
  216. MICROCHUNKID_DEF_AIM_2D,
  217. MICROCHUNKID_DEF_SQUISH_VELOCITY,
  218. MICROCHUNKID_ENGINE_SOUND_MAX_PITCH_FACTOR,
  219. MICROCHUNKID_DEF_VEHICLE_NAME_ID,
  220. MICROCHUNKID_DEF_NUM_SEATS,
  221. MICROCHUNKID_DEF_GDI_DAMAGE_REPORT_ID,
  222. MICROCHUNKID_DEF_NOD_DAMAGE_REPORT_ID,
  223. MICROCHUNKID_DEF_GDI_DESTROY_REPORT_ID,
  224. MICROCHUNKID_DEF_NOD_DESTROY_REPORT_ID,
  225. };
  226. bool VehicleGameObjDef::Save( ChunkSaveClass & csave )
  227. {
  228. csave.Begin_Chunk( CHUNKID_DEF_PARENT );
  229. SmartGameObjDef::Save( csave );
  230. csave.End_Chunk();
  231. csave.Begin_Chunk( CHUNKID_DEF_VARIABLES );
  232. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TYPE, Type );
  233. WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_TYPE_NAME, TypeName );
  234. WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_FIRE0ANIM, Fire0Anim );
  235. WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_FIRE1ANIM, Fire1Anim );
  236. WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_PROFILE, Profile );
  237. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TURN_RADIUS, TurnRadius );
  238. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_OCCUPANTS_VISIBLE, OccupantsVisible );
  239. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ENGINE_SOUND_MAX_PITCH_FACTOR, EngineSoundMaxPitchFactor );
  240. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ENGINE_START_SOUND, EngineSound[ENGINE_SOUND_STATE_STARTING] );
  241. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ENGINE_RUN_SOUND, EngineSound[ENGINE_SOUND_STATE_RUNNING] );
  242. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ENGINE_STOP_SOUND, EngineSound[ENGINE_SOUND_STATE_STOPPING] );
  243. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ENGINE_OFF_SOUND, EngineSound[ENGINE_SOUND_STATE_OFF] );
  244. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_SIGHT_DOWN_MUZZLE, SightDownMuzzle );
  245. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_AIM_2D, Aim2D );
  246. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_SQUISH_VELOCITY, SquishVelocity );
  247. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_VEHICLE_NAME_ID, VehicleNameID );
  248. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_NUM_SEATS, NumSeats );
  249. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GDI_DAMAGE_REPORT_ID, GDIDamageReportID );
  250. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_NOD_DAMAGE_REPORT_ID, NodDamageReportID );
  251. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GDI_DESTROY_REPORT_ID, GDIDestroyReportID );
  252. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_NOD_DESTROY_REPORT_ID, NodDestroyReportID );
  253. csave.End_Chunk();
  254. // Save each of the transition 'definitions' in our list.
  255. for( int index = 0; index < Transitions.Count (); index ++ ) {
  256. TransitionDataClass *transition = Transitions[index];
  257. if( transition != NULL ) {
  258. // Save this transition 'defintion' to its own chunk.
  259. csave.Begin_Chunk( CHUNKID_DEF_TRANSITION );
  260. transition->Save( csave );
  261. csave.End_Chunk();
  262. }
  263. }
  264. return true;
  265. }
  266. bool VehicleGameObjDef::Load( ChunkLoadClass &cload )
  267. {
  268. Free_Transition_List ();
  269. while (cload.Open_Chunk()) {
  270. switch(cload.Cur_Chunk_ID()) {
  271. case CHUNKID_DEF_PARENT:
  272. SmartGameObjDef::Load( cload );
  273. break;
  274. case CHUNKID_DEF_TRANSITION:
  275. {
  276. TransitionDataClass *transition = new TransitionDataClass;
  277. transition->Load( cload );
  278. Transitions.Add( transition );
  279. }
  280. break;
  281. case CHUNKID_DEF_VARIABLES:
  282. while (cload.Open_Micro_Chunk()) {
  283. switch(cload.Cur_Micro_Chunk_ID()) {
  284. READ_MICRO_CHUNK( cload, MICROCHUNKID_TYPE, Type );
  285. READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_TYPE_NAME, TypeName );
  286. READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_FIRE0ANIM, Fire0Anim );
  287. READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_FIRE1ANIM, Fire1Anim );
  288. READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_PROFILE, Profile );
  289. READ_MICRO_CHUNK( cload, MICROCHUNKID_PHYS_ID, PhysDefID );
  290. READ_MICRO_CHUNK( cload, MICROCHUNKID_TURN_RADIUS, TurnRadius );
  291. READ_MICRO_CHUNK( cload, MICROCHUNKID_OCCUPANTS_VISIBLE, OccupantsVisible );
  292. READ_MICRO_CHUNK( cload, MICROCHUNKID_ENGINE_SOUND_MAX_PITCH_FACTOR, EngineSoundMaxPitchFactor);
  293. READ_MICRO_CHUNK( cload, MICROCHUNKID_ENGINE_START_SOUND, EngineSound[ENGINE_SOUND_STATE_STARTING] );
  294. READ_MICRO_CHUNK( cload, MICROCHUNKID_ENGINE_RUN_SOUND, EngineSound[ENGINE_SOUND_STATE_RUNNING] );
  295. READ_MICRO_CHUNK( cload, MICROCHUNKID_ENGINE_STOP_SOUND, EngineSound[ENGINE_SOUND_STATE_STOPPING] );
  296. READ_MICRO_CHUNK( cload, MICROCHUNKID_ENGINE_OFF_SOUND, EngineSound[ENGINE_SOUND_STATE_OFF] );
  297. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_SIGHT_DOWN_MUZZLE, SightDownMuzzle );
  298. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_AIM_2D, Aim2D );
  299. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_SQUISH_VELOCITY, SquishVelocity );
  300. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_VEHICLE_NAME_ID, VehicleNameID );
  301. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_NUM_SEATS, NumSeats );
  302. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GDI_DAMAGE_REPORT_ID, GDIDamageReportID );
  303. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_NOD_DAMAGE_REPORT_ID, NodDamageReportID );
  304. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GDI_DESTROY_REPORT_ID, GDIDestroyReportID );
  305. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_NOD_DESTROY_REPORT_ID, NodDestroyReportID );
  306. default:
  307. Debug_Say(( "Unrecognized VehicleDef Variable chunkID\n" ));
  308. break;
  309. }
  310. cload.Close_Micro_Chunk();
  311. }
  312. break;
  313. default:
  314. Debug_Say(( "Unrecognized VehicleDef chunkID\n" ));
  315. break;
  316. }
  317. cload.Close_Chunk();
  318. }
  319. return true;
  320. }
  321. /*
  322. **
  323. */
  324. void VehicleGameObjDef::Free_Transition_List( void )
  325. {
  326. // Delete each of the transition 'definitions' in our list.
  327. for( int index = 0; index < Transitions.Count (); index ++ ) {
  328. TransitionDataClass *transition = Transitions[index];
  329. if( transition != NULL ) {
  330. delete transition;
  331. }
  332. }
  333. Transitions.Delete_All();
  334. return ;
  335. }
  336. const PersistFactoryClass & VehicleGameObjDef::Get_Factory (void) const
  337. {
  338. return _VehicleGameObjDefPersistFactory;
  339. }
  340. int VehicleGameObjDef::Get_Damage_Report(int team) const
  341. {
  342. if (PLAYERTYPE_GDI == team) {
  343. return GDIDamageReportID;
  344. } else if (PLAYERTYPE_NOD == team) {
  345. return NodDamageReportID;
  346. }
  347. return 0;
  348. }
  349. int VehicleGameObjDef::Get_Destroy_Report(int team) const
  350. {
  351. if (PLAYERTYPE_GDI == team) {
  352. return GDIDestroyReportID;
  353. } else if (PLAYERTYPE_NOD == team) {
  354. return NodDestroyReportID;
  355. }
  356. return 0;
  357. }
  358. /*
  359. ** VehicleGameObj
  360. */
  361. SimplePersistFactoryClass<VehicleGameObj, CHUNKID_GAME_OBJECT_VEHICLE> _VehicleGameObjPersistFactory;
  362. const PersistFactoryClass & VehicleGameObj::Get_Factory (void) const
  363. {
  364. return _VehicleGameObjPersistFactory;
  365. }
  366. VehicleGameObj::VehicleGameObj() :
  367. TurretBone( 0 ),
  368. BarrelBone( 0 ),
  369. TurretTurn( 0 ),
  370. BarrelTilt( 0 ),
  371. BarrelOffset( 0 ),
  372. Sound( NULL ),
  373. EngineSoundState( ENGINE_SOUND_STATE_OFF ),
  374. CachedEngineSound( NULL ),
  375. WheelSurfaceSound(NULL),
  376. TransitionsEnabled( true ),
  377. OccupiedSeats( 0 ),
  378. HasEnterTransitions( false ),
  379. HasExitTransitions( false ),
  380. VehicleDelivered(false),
  381. LockTimer( 0 )
  382. {
  383. DriverIsGunner = DefaultDriverIsGunner;
  384. Set_App_Packet_Type(APPPACKETTYPE_VEHICLE);
  385. }
  386. VehicleGameObj::~VehicleGameObj()
  387. {
  388. if ( Peek_Physical_Object() != NULL && COMBAT_SCENE != NULL) {
  389. // Make sure the exiters don't hit me
  390. Peek_Physical_Object()->Set_Collision_Group( UNCOLLIDEABLE_GROUP );
  391. // Eject all the occupants around the vehicle so they don't intersect
  392. Vector3 vehicle_pos;
  393. Peek_Physical_Object()->Get_Position( &vehicle_pos );
  394. for ( int i = 0; i < SeatOccupants.Length(); i++ ) {
  395. if ( SeatOccupants[i] != NULL ) {
  396. SeatOccupants[i]->Exit_Destroyed_Vehicle( i, vehicle_pos );
  397. }
  398. SeatOccupants[i] = NULL;
  399. }
  400. } else {
  401. for ( int i = 0; i < SeatOccupants.Length(); i++ ) {
  402. if ( SeatOccupants[i] != NULL ) {
  403. SeatOccupants[i]->Exit_Vehicle();
  404. }
  405. SeatOccupants[i] = NULL;
  406. }
  407. }
  408. Destroy_Transitions();
  409. if (Sound) {
  410. Sound->Remove_From_Scene ();
  411. Sound->Release_Ref ();
  412. Sound = NULL;
  413. }
  414. if (CachedEngineSound) {
  415. CachedEngineSound->Remove_From_Scene();
  416. REF_PTR_RELEASE(CachedEngineSound);
  417. }
  418. Release_Turret_Bones();
  419. Shutdown_Wheel_Effects();
  420. }
  421. /*
  422. **
  423. */
  424. void VehicleGameObj::Init( void )
  425. {
  426. DriverIsGunner = DefaultDriverIsGunner;
  427. // Vehicles are too complex to re-init
  428. // Init( Get_Definition() );
  429. }
  430. void VehicleGameObj::Init( const VehicleGameObjDef & definition )
  431. {
  432. DriverIsGunner = DefaultDriverIsGunner;
  433. SmartGameObj::Init( definition );
  434. SeatOccupants.Resize(definition.NumSeats);
  435. for ( int i = 0; i < SeatOccupants.Length(); i++ ) {
  436. SeatOccupants[i] = NULL;
  437. }
  438. Aquire_Turret_Bones();
  439. Init_Wheel_Effects();
  440. Create_And_Destroy_Transitions();
  441. Update_Damage_Meshes();
  442. //
  443. // Refine the app packet type if this is a turret
  444. //
  445. if (Get_Definition().Type == VEHICLE_TYPE_TURRET)
  446. {
  447. Set_App_Packet_Type(APPPACKETTYPE_TURRET);
  448. }
  449. //WWDEBUG_SAY((">> VehicleGameObj::Init, Type = %d\n", Get_Definition().Type));
  450. }
  451. const VehicleGameObjDef & VehicleGameObj::Get_Definition( void ) const
  452. {
  453. return (const VehicleGameObjDef &)BaseGameObj::Get_Definition();
  454. }
  455. /*
  456. ** VehicleGameObj Save and Load
  457. */
  458. enum {
  459. CHUNKID_PARENT = 923991632,
  460. CHUNKID_VARIABLES,
  461. XXXCHUNKID_ANIM_CONTROL,
  462. XXXCHUNKID_TRANSITION_LIST_OLD,
  463. CHUNKID_SEAT_LIST,
  464. MICROCHUNKID_TURRET_BONE = 1,
  465. MICROCHUNKID_BARREL_BONE,
  466. XXXMICROCHUNKID_PHYSICAL_OBJECT,
  467. MICROCHUNKID_TRANSITION_LIST_ENTRY_OLD,
  468. MICROCHUNKID_TURRET_TURN,
  469. MICROCHUNKID_BARREL_TILT,
  470. MICROCHUNKID_TRANSITIONS_ENABLED,
  471. MICROCHUNKID_OCCUPIED_SEATS,
  472. MICROCHUNKID_NUM_SEATS,
  473. };
  474. bool VehicleGameObj::Save( ChunkSaveClass & csave )
  475. {
  476. // Destroy the transitions, because we don't save them, and don't
  477. // want to have the vehicle have a un-matched reference to them.
  478. // Re-create at the end of save
  479. Destroy_Transitions();
  480. csave.Begin_Chunk( CHUNKID_PARENT );
  481. SmartGameObj::Save( csave );
  482. csave.End_Chunk();
  483. csave.Begin_Chunk( CHUNKID_VARIABLES );
  484. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TURRET_TURN, TurretTurn );
  485. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_BARREL_TILT, BarrelTilt );
  486. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TRANSITIONS_ENABLED, TransitionsEnabled );
  487. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_OCCUPIED_SEATS, OccupiedSeats );
  488. int num_seats = SeatOccupants.Length();
  489. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_NUM_SEATS, num_seats );
  490. csave.End_Chunk();
  491. if ( num_seats != 0 ) {
  492. csave.Begin_Chunk( CHUNKID_SEAT_LIST );
  493. csave.Write( &SeatOccupants[0], num_seats * sizeof( SeatOccupants[0] ) );
  494. csave.End_Chunk();
  495. }
  496. Create_And_Destroy_Transitions();
  497. return true;
  498. }
  499. bool VehicleGameObj::Load( ChunkLoadClass &cload )
  500. {
  501. int num_seats = 0;
  502. while (cload.Open_Chunk()) {
  503. switch(cload.Cur_Chunk_ID()) {
  504. case CHUNKID_PARENT:
  505. SmartGameObj::Load( cload );
  506. break;
  507. case CHUNKID_VARIABLES:
  508. {
  509. while (cload.Open_Micro_Chunk()) {
  510. switch(cload.Cur_Micro_Chunk_ID()) {
  511. READ_MICRO_CHUNK( cload, MICROCHUNKID_TURRET_TURN, TurretTurn );
  512. READ_MICRO_CHUNK( cload, MICROCHUNKID_BARREL_TILT, BarrelTilt );
  513. READ_MICRO_CHUNK( cload, MICROCHUNKID_TRANSITIONS_ENABLED, TransitionsEnabled );
  514. READ_MICRO_CHUNK( cload, MICROCHUNKID_OCCUPIED_SEATS, OccupiedSeats );
  515. READ_MICRO_CHUNK( cload, MICROCHUNKID_NUM_SEATS, num_seats );
  516. default:
  517. Debug_Say(( "Unrecognized Vehicle Variable chunkID\n" ));
  518. break;
  519. }
  520. cload.Close_Micro_Chunk();
  521. }
  522. break;
  523. }
  524. case CHUNKID_SEAT_LIST:
  525. {
  526. if ( (num_seats == 0) || (num_seats != Get_Definition().NumSeats)) {
  527. SeatOccupants.Resize(Get_Definition().NumSeats);
  528. for ( int i = 0; i < SeatOccupants.Length(); i++ ) {
  529. SeatOccupants[i] = NULL;
  530. }
  531. break; // May be legacy
  532. }
  533. SeatOccupants.Resize( num_seats );
  534. cload.Read( &SeatOccupants[0], num_seats * sizeof( SeatOccupants[0] ) );
  535. for ( int i = 0; i < num_seats; i++ ) {
  536. if ( SeatOccupants[i] != NULL ) {
  537. REQUEST_POINTER_REMAP( (void **)&SeatOccupants[i] );
  538. }
  539. }
  540. break;
  541. }
  542. default:
  543. Debug_Say(( "Unrecognized Vehicle chunkID\n" ));
  544. break;
  545. }
  546. cload.Close_Chunk();
  547. }
  548. SaveLoadSystemClass::Register_Post_Load_Callback(this);
  549. return true;
  550. }
  551. void VehicleGameObj::On_Post_Load( void )
  552. {
  553. DriverIsGunner = DefaultDriverIsGunner;
  554. Init_Wheel_Effects();
  555. Aquire_Turret_Bones();
  556. SmartGameObj::On_Post_Load();
  557. Create_And_Destroy_Transitions();
  558. Update_Damage_Meshes();
  559. // For some reason??? some vehicles come in with an anim control, but no model in the anim control.
  560. if ( Get_Anim_Control() != NULL && Get_Anim_Control()->Peek_Model() == NULL ) {
  561. Get_Anim_Control()->Set_Model( Peek_Model() );
  562. }
  563. //
  564. // Refine the app packet type if this is a turret
  565. //
  566. if (Get_Definition().Type == VEHICLE_TYPE_TURRET)
  567. {
  568. Set_App_Packet_Type(APPPACKETTYPE_TURRET);
  569. }
  570. }
  571. #if 0
  572. void VehicleGameObj::Create_Transitions( void )
  573. {
  574. Destroy_Transitions();
  575. WWASSERT( TransitionInstances.Count() == 0 );
  576. const TRANSITION_DATA_LIST & trans_data_list = Get_Definition().Get_Transition_List();
  577. if ( trans_data_list.Count() != 0 ) { // new style
  578. if ( Seats[0] == NULL ) {
  579. Create_New_Transitions( TransitionDataClass::VEHICLE_ENTER_0 );
  580. } else {
  581. Create_New_Transitions( TransitionDataClass::VEHICLE_EXIT_0 );
  582. }
  583. if ( Seats[1] == NULL ) {
  584. Create_New_Transitions( TransitionDataClass::VEHICLE_ENTER_1 );
  585. } else {
  586. Create_New_Transitions( TransitionDataClass::VEHICLE_EXIT_1 );
  587. }
  588. }
  589. }
  590. #endif
  591. void VehicleGameObj::Destroy_Transitions( void )
  592. {
  593. HasEnterTransitions = false;
  594. HasExitTransitions = false;
  595. while ( TransitionInstances.Count() ) {
  596. TransitionManager::Destroy( TransitionInstances[0] );
  597. TransitionInstances.Delete( 0 );
  598. }
  599. // They get put in a list, so flush the list.
  600. TransitionManager::Destroy_Pending();
  601. }
  602. void VehicleGameObj::Create_And_Destroy_Transitions( void )
  603. {
  604. // Manage the enter transitions
  605. bool should_have_enter_transitions = (OccupiedSeats < Get_Definition().NumSeats) && TransitionsEnabled;
  606. if ( should_have_enter_transitions != HasEnterTransitions ) {
  607. if ( should_have_enter_transitions ) {
  608. Create_New_Transitions( TransitionDataClass::VEHICLE_ENTER );
  609. HasEnterTransitions = true;
  610. } else {
  611. Remove_Transitions( TransitionDataClass::VEHICLE_ENTER );
  612. HasEnterTransitions = false;
  613. }
  614. }
  615. // Manage the exit transitions
  616. bool should_have_exit_transitions = (OccupiedSeats > 0) && TransitionsEnabled;
  617. if ( should_have_exit_transitions != HasExitTransitions ) {
  618. if ( should_have_exit_transitions ) {
  619. Create_New_Transitions( TransitionDataClass::VEHICLE_EXIT );
  620. HasExitTransitions = true;
  621. } else {
  622. Remove_Transitions( TransitionDataClass::VEHICLE_EXIT );
  623. HasExitTransitions = false;
  624. }
  625. }
  626. }
  627. /*
  628. **
  629. */
  630. void VehicleGameObj::Aquire_Turret_Bones( void )
  631. {
  632. if ( TurretBone == 0 ) {
  633. TurretBone = Peek_Model()->Get_Bone_Index( "turret" );
  634. if ( TurretBone != 0 ) {
  635. Peek_Model()->Capture_Bone( TurretBone );
  636. if ( !Peek_Model()->Is_Bone_Captured( TurretBone ) ) {
  637. Debug_Say(( "VehicleGameObj::Aquire_Bones() : Turret Bone not captured for model %s\n",
  638. Peek_Model()->Get_Name()));
  639. TurretBone = 0;
  640. }
  641. }
  642. }
  643. BarrelOffset = 0;
  644. if ( BarrelBone == 0 ) {
  645. BarrelBone = Peek_Model()->Get_Bone_Index( "barrel" );
  646. if ( BarrelBone != 0 ) {
  647. Peek_Model()->Capture_Bone( BarrelBone );
  648. if ( !Peek_Model()->Is_Bone_Captured( BarrelBone ) ) {
  649. BarrelBone = 0;
  650. Debug_Say(( "VehicleGameObj::Aquire_Bones() : Barrel Bone not captured for model %s\n",
  651. Peek_Model()->Get_Name()));
  652. }
  653. }
  654. // find the barrel in turret space
  655. if ( TurretBone != 0 ) {
  656. Matrix3D turret_base = Peek_Model()->Get_Bone_Transform( TurretBone );
  657. Vector3 barrel_pos = Peek_Model()->Get_Bone_Transform( BarrelBone ).Get_Translation();
  658. Vector3 turret_space_barrel;
  659. Matrix3D::Inverse_Transform_Vector( turret_base, barrel_pos, &turret_space_barrel );
  660. BarrelOffset = turret_space_barrel.Y;
  661. if ( WWMath::Fabs( BarrelOffset ) < 0.1f ) {
  662. BarrelOffset = 0;
  663. }
  664. }
  665. }
  666. }
  667. void VehicleGameObj::Release_Turret_Bones( void )
  668. {
  669. if ( Peek_Model() ) {
  670. if ( TurretBone != 0 ) {
  671. if ( Peek_Model()->Is_Bone_Captured( TurretBone ) ) {
  672. Peek_Model()->Release_Bone( TurretBone );
  673. TurretBone = 0;
  674. }
  675. }
  676. if ( BarrelBone != 0 ) {
  677. if ( Peek_Model()->Is_Bone_Captured( BarrelBone ) ) {
  678. Peek_Model()->Release_Bone( BarrelBone );
  679. BarrelBone = 0;
  680. }
  681. }
  682. }
  683. BarrelOffset = 0;
  684. }
  685. void VehicleGameObj::Export_Creation( BitStreamClass &packet )
  686. {
  687. SmartGameObj::Export_Creation( packet );
  688. //
  689. // Send the lock status to the client
  690. //
  691. int lock_owner_id = 0;
  692. if (LockOwner != NULL) {
  693. lock_owner_id = LockOwner.Get_Ptr ()->Get_ID ();
  694. }
  695. packet.Add(lock_owner_id);
  696. if (lock_owner_id != 0) {
  697. packet.Add(LockTimer,BITPACK_VEHICLE_LOCK_TIMER);
  698. }
  699. return ;
  700. }
  701. void VehicleGameObj::Import_Creation( BitStreamClass &packet )
  702. {
  703. SmartGameObj::Import_Creation( packet );
  704. //
  705. // Get the lock status from the server
  706. //
  707. int lock_owner_id;
  708. packet.Get(lock_owner_id);
  709. if (lock_owner_id != 0) {
  710. LockOwner = GameObjManager::Find_PhysicalGameObj( lock_owner_id );
  711. packet.Get(LockTimer,BITPACK_VEHICLE_LOCK_TIMER);
  712. }
  713. return ;
  714. }
  715. void VehicleGameObj::Export_Rare( BitStreamClass &packet )
  716. {
  717. SmartGameObj::Export_Rare( packet );
  718. //
  719. // Export the seat occupants
  720. //
  721. for (int i = 0; i < SeatOccupants.Length(); i++) {
  722. if (SeatOccupants[i] == NULL) {
  723. const int NO_OCCUPANTS = -1;
  724. packet.Add(NO_OCCUPANTS);
  725. } else {
  726. packet.Add(SeatOccupants[i]->Get_ID());
  727. }
  728. }
  729. packet.Add(VehicleDelivered);
  730. }
  731. void VehicleGameObj::Import_Rare( BitStreamClass &packet )
  732. {
  733. WWASSERT(CombatManager::I_Am_Only_Client());
  734. SmartGameObj::Import_Rare( packet );
  735. //
  736. // Update the seat occupants
  737. //
  738. for (int i = 0; i < SeatOccupants.Length(); i++) {
  739. int occupant = packet.Get(occupant);
  740. if (occupant == -1) {
  741. //
  742. // Remove the occupant from the seat
  743. //
  744. if (SeatOccupants[i] != NULL) {
  745. Remove_Occupant (SeatOccupants[i]);
  746. }
  747. } else {
  748. if ((SeatOccupants[i] == NULL) || (SeatOccupants[i]->Get_ID() != occupant)) {
  749. SmartGameObj * obj = GameObjManager::Find_SmartGameObj(occupant);
  750. if (SeatOccupants[i] != NULL) {
  751. Remove_Occupant (SeatOccupants[i]);
  752. }
  753. if (obj != NULL) {
  754. //
  755. // Add the occupant to the seat
  756. //
  757. if (SeatOccupants[i] == NULL) {
  758. Add_Occupant (obj->As_SoldierGameObj(), i);
  759. }
  760. }
  761. }
  762. }
  763. }
  764. bool wasDelivered = VehicleDelivered;
  765. packet.Get(VehicleDelivered);
  766. if (!wasDelivered && VehicleDelivered) {
  767. BaseControllerClass* base = BaseControllerClass::Find_Base(Get_Player_Type());
  768. if (base) {
  769. base->On_Vehicle_Delivered(this);
  770. }
  771. }
  772. }
  773. //-----------------------------------------------------------------------------
  774. /*
  775. **
  776. */
  777. void VehicleGameObj::Import_Frequent(BitStreamClass & packet)
  778. {
  779. /*
  780. if (Get_Definition().Type == VEHICLE_TYPE_TURRET) {
  781. WWDEBUG_SAY(("VEHICLE_TYPE_TURRET::Import_Frequent: %s, %d, %d\n",
  782. Get_Definition().Get_Name(),
  783. Get_Definition().NumSeats,
  784. SeatOccupants.Length()));
  785. }
  786. */
  787. WWASSERT(CombatManager::I_Am_Only_Client());
  788. int rounds = packet.Get(rounds);
  789. if (Get_Weapon() != NULL) {
  790. Get_Weapon()->Set_Total_Rounds(rounds);
  791. }
  792. switch(Get_Definition().Type) {
  793. case VEHICLE_TYPE_BIKE:
  794. case VEHICLE_TYPE_CAR:
  795. case VEHICLE_TYPE_TANK:
  796. case VEHICLE_TYPE_FLYING:
  797. {
  798. VehiclePhysClass * p_obj = Peek_Vehicle_Phys();
  799. if (p_obj != NULL) {
  800. Vector3 sc_position;
  801. Quaternion q;
  802. Vector3 vel;
  803. Vector3 ang_vel;
  804. bool is_engine_on;
  805. packet.Get(is_engine_on);
  806. packet.Get(sc_position.X, BITPACK_WORLD_POSITION_X);
  807. packet.Get(sc_position.Y, BITPACK_WORLD_POSITION_Y);
  808. packet.Get(sc_position.Z, BITPACK_WORLD_POSITION_Z);
  809. packet.Get(q.X, BITPACK_VEHICLE_QUATERNION);
  810. packet.Get(q.Y, BITPACK_VEHICLE_QUATERNION);
  811. packet.Get(q.Z, BITPACK_VEHICLE_QUATERNION);
  812. packet.Get(q.W, BITPACK_VEHICLE_QUATERNION);
  813. q.Normalize();
  814. #ifdef MULTIPLAYERDEMO
  815. //
  816. // Mix up the packet order to make demo/non-demo code more incompatible.
  817. //
  818. packet.Get(vel.Y, BITPACK_VEHICLE_VELOCITY);
  819. packet.Get(vel.Z, BITPACK_VEHICLE_VELOCITY);
  820. packet.Get(vel.X, BITPACK_VEHICLE_VELOCITY);
  821. #else
  822. packet.Get(vel.X, BITPACK_VEHICLE_VELOCITY);
  823. packet.Get(vel.Y, BITPACK_VEHICLE_VELOCITY);
  824. packet.Get(vel.Z, BITPACK_VEHICLE_VELOCITY);
  825. #endif
  826. packet.Get(ang_vel.X, BITPACK_VEHICLE_ANGULAR_VELOCITY);
  827. packet.Get(ang_vel.Y, BITPACK_VEHICLE_ANGULAR_VELOCITY);
  828. packet.Get(ang_vel.Z, BITPACK_VEHICLE_ANGULAR_VELOCITY);
  829. if (COMBAT_STAR && (COMBAT_STAR->Get_Vehicle() == this)) {
  830. p_obj->Network_Latency_State_Update( sc_position,
  831. q,
  832. vel,
  833. ang_vel);
  834. } else {
  835. p_obj->Network_Interpolate_State_Update( sc_position,
  836. q,
  837. vel,
  838. ang_vel,
  839. 0.1f );
  840. }
  841. p_obj->Enable_Engine(is_engine_on);
  842. }
  843. break;
  844. }
  845. case VEHICLE_TYPE_TURRET:
  846. //WWDEBUG_SAY(("turret read %d\n", packet.Get_Bit_Write_Position()));
  847. break;
  848. default:
  849. WWASSERT( 0 );
  850. break;
  851. }
  852. packet.Get( DriverIsGunner );
  853. SmartGameObj::Import_Frequent(packet);
  854. WWASSERT(packet.Is_Flushed());
  855. }
  856. //-----------------------------------------------------------------------------
  857. void VehicleGameObj::Export_Frequent(BitStreamClass & packet)
  858. {
  859. Apply_Control(); // Make sure we have our drivers controls
  860. /*
  861. if (Get_Definition().Type == VEHICLE_TYPE_TURRET) {
  862. WWDEBUG_SAY(("VEHICLE_TYPE_TURRET::Export_Frequent: %s, %d, %d\n",
  863. Get_Definition().Get_Name(),
  864. Get_Definition().NumSeats,
  865. SeatOccupants.Length()));
  866. }
  867. */
  868. //
  869. // Vehicles never change their weapon type
  870. //
  871. int total_rounds = 0;
  872. if (Get_Weapon() != NULL) {
  873. total_rounds = Get_Weapon()->Get_Total_Rounds();
  874. }
  875. packet.Add(total_rounds);
  876. switch(Get_Definition().Type) {
  877. case VEHICLE_TYPE_BIKE:
  878. case VEHICLE_TYPE_TANK:
  879. case VEHICLE_TYPE_CAR:
  880. case VEHICLE_TYPE_FLYING:
  881. {
  882. VehiclePhysClass * p_obj = Peek_Vehicle_Phys();
  883. if (p_obj != NULL) {
  884. Vector3 pos;
  885. Quaternion q;
  886. Vector3 vel;
  887. Vector3 ang_vel;
  888. bool is_engine_on;
  889. p_obj->Get_Position(&pos);
  890. p_obj->Get_Orientation(&q);
  891. p_obj->Get_Velocity(&vel);
  892. p_obj->Get_Angular_Velocity(&ang_vel);
  893. is_engine_on = p_obj->Is_Engine_Enabled();
  894. packet.Add(is_engine_on);
  895. packet.Add(pos.X, BITPACK_WORLD_POSITION_X);
  896. packet.Add(pos.Y, BITPACK_WORLD_POSITION_Y);
  897. packet.Add(pos.Z, BITPACK_WORLD_POSITION_Z);
  898. packet.Add(q.X, BITPACK_VEHICLE_QUATERNION);
  899. packet.Add(q.Y, BITPACK_VEHICLE_QUATERNION);
  900. packet.Add(q.Z, BITPACK_VEHICLE_QUATERNION);
  901. packet.Add(q.W, BITPACK_VEHICLE_QUATERNION);
  902. #ifdef MULTIPLAYERDEMO
  903. //
  904. // Mix up the packet order to make demo/non-demo code more incompatible.
  905. //
  906. packet.Add(vel.Y, BITPACK_VEHICLE_VELOCITY);
  907. packet.Add(vel.Z, BITPACK_VEHICLE_VELOCITY);
  908. packet.Add(vel.X, BITPACK_VEHICLE_VELOCITY);
  909. #else
  910. packet.Add(vel.X, BITPACK_VEHICLE_VELOCITY);
  911. packet.Add(vel.Y, BITPACK_VEHICLE_VELOCITY);
  912. packet.Add(vel.Z, BITPACK_VEHICLE_VELOCITY);
  913. #endif
  914. packet.Add(ang_vel.X, BITPACK_VEHICLE_ANGULAR_VELOCITY);
  915. packet.Add(ang_vel.Y, BITPACK_VEHICLE_ANGULAR_VELOCITY);
  916. packet.Add(ang_vel.Z, BITPACK_VEHICLE_ANGULAR_VELOCITY);
  917. }
  918. break;
  919. }
  920. case VEHICLE_TYPE_TURRET:
  921. //WWDEBUG_SAY(("turret write %d\n", packet.Get_Bit_Write_Position()));
  922. break;
  923. default:
  924. WWASSERT(0);
  925. break;
  926. }
  927. packet.Add( DriverIsGunner );
  928. SmartGameObj::Export_Frequent(packet);
  929. }
  930. //-----------------------------------------------------------------------------
  931. void VehicleGameObj::Import_State_Cs(BitStreamClass & packet)
  932. {
  933. SmartGameObj::Import_State_Cs(packet);
  934. }
  935. void VehicleGameObj::Export_State_Cs(BitStreamClass & packet)
  936. {
  937. SmartGameObj::Export_State_Cs(packet);
  938. }
  939. void VehicleGameObj::Get_Velocity(Vector3 & vel)
  940. {
  941. VehiclePhysClass * vp = Peek_Vehicle_Phys();
  942. if (vp != NULL) {
  943. vp->Get_Velocity(&vel);
  944. } else {
  945. vel.Set(0,0,0);
  946. }
  947. }
  948. void VehicleGameObj::Set_Velocity(Vector3 & vel)
  949. {
  950. VehiclePhysClass * vehicle = Peek_Vehicle_Phys();
  951. if (vehicle != NULL) {
  952. vehicle->Set_Velocity(vel);
  953. }
  954. }
  955. void VehicleGameObj::Startup( void )
  956. {
  957. // Setup all Seats for entry
  958. Create_And_Destroy_Transitions();
  959. }
  960. void VehicleGameObj::Update_Turret( float weapon_turn, float weapon_tilt )
  961. {
  962. if ( TurretBone != 0 ) {
  963. Matrix3D facing(1);
  964. facing.Rotate_Z( weapon_turn );
  965. if ( BarrelBone == 0 ) { // if no barrel bone
  966. facing.Rotate_Y( -weapon_tilt ); // neg rotate y tilts up
  967. }
  968. Peek_Model()->Control_Bone( TurretBone, facing );
  969. }
  970. if ( BarrelBone != 0 ) {
  971. Matrix3D facing(1);
  972. facing.Rotate_Y( -weapon_tilt ); // neg rotate y tilts up
  973. Peek_Model()->Control_Bone( BarrelBone, facing );
  974. }
  975. }
  976. /*
  977. ** Vehicle Weapons
  978. */
  979. bool VehicleGameObj::Set_Targeting( const Vector3 & target_pos, bool do_tilt )
  980. {
  981. bool ready = true;
  982. //Debug_Say(( "Weapon set targeting %f %f %f\n", target_pos.X, target_pos.Y, target_pos.Z ));
  983. SmartGameObj::Set_Targeting( target_pos );
  984. // Find the desired turret tilt and turn
  985. float relative_turn = 0;
  986. float relative_tilt = 0;
  987. if ( TurretBone != 0 ) {
  988. // find the target pos in turret space
  989. Matrix3D turret_base = Peek_Model()->Get_Bone_Transform( TurretBone );
  990. Vector3 turret_space_target;
  991. Matrix3D::Inverse_Transform_Vector( turret_base, target_pos, &turret_space_target );
  992. // Set the tilt and turn
  993. relative_turn = WWMath::Atan2( turret_space_target.Y, turret_space_target.X );
  994. if ( BarrelOffset ) {
  995. turret_space_target.Z = 0;
  996. float barrel_offset_angle = WWMath::Atan2( BarrelOffset, turret_space_target.Length() );
  997. // Debug_Say(( "barrel angle %f %f\n", RAD_TO_DEG( barrel_offset_angle ), RAD_TO_DEG( relative_turn + barrel_offset_angle ) ));
  998. relative_turn -= barrel_offset_angle;
  999. }
  1000. }
  1001. if ( WWMath::Fabs( relative_turn ) < DEG_TO_RAD( 80 ) ) {
  1002. if ( BarrelBone != 0 ) {
  1003. // find the target pos in barrel space
  1004. Matrix3D barrel_base = Peek_Model()->Get_Bone_Transform( BarrelBone );
  1005. Vector3 barrel_space_target;
  1006. Matrix3D::Inverse_Transform_Vector( barrel_base, target_pos, &barrel_space_target );
  1007. float dist = barrel_space_target.Length();
  1008. if ( dist ) {
  1009. // Only tilt when the turn is within 80 deg
  1010. relative_tilt = WWMath::Fast_Asin( barrel_space_target.Z / dist );
  1011. }
  1012. }
  1013. }
  1014. // Move the tilt and turn towards the desired, following rates and limits
  1015. float max_move;
  1016. max_move = Get_Definition().WeaponTurnRate * TimeManager::Get_Frame_Seconds();
  1017. if ( Get_Definition().WeaponTurnRate < DEG_TO_RAD(1000) ) {
  1018. TurretTurn += WWMath::Clamp( relative_turn, -max_move, max_move );
  1019. if ( WWMath::Fabs( relative_turn ) > WWMath::Fabs( max_move ) ) {
  1020. ready = false;
  1021. }
  1022. } else {
  1023. TurretTurn += relative_turn;
  1024. }
  1025. TurretTurn = WWMath::Clamp( TurretTurn, Get_Definition().WeaponTurnMin, Get_Definition().WeaponTurnMax );
  1026. max_move = Get_Definition().WeaponTiltRate * TimeManager::Get_Frame_Seconds();
  1027. if ( do_tilt ) {
  1028. if ( Get_Definition().WeaponTiltRate < DEG_TO_RAD(1000) ) {
  1029. BarrelTilt += WWMath::Clamp( relative_tilt, -max_move, max_move );
  1030. if ( WWMath::Fabs( relative_tilt ) > WWMath::Fabs( max_move ) ) {
  1031. ready = false;
  1032. }
  1033. } else {
  1034. BarrelTilt += relative_tilt;
  1035. }
  1036. }
  1037. BarrelTilt = WWMath::Clamp( BarrelTilt, Get_Definition().WeaponTiltMin, Get_Definition().WeaponTiltMax );
  1038. // Apply the turn and tilt to the bones
  1039. Update_Turret( TurretTurn, BarrelTilt );
  1040. // if a fast turner and had to turn, do it again,just to make sure (trying to fix obelisk)
  1041. if ( Get_Definition().WeaponTurnRate > DEG_TO_RAD(1000) &&
  1042. ( WWMath::Fabs(relative_turn) >= DEG_TO_RAD( 2 ) || WWMath::Fabs(relative_tilt) > DEG_TO_RAD( 2 ) ) ) {
  1043. static int calls = 0;
  1044. if ( calls < 3 ) {
  1045. calls++;
  1046. Set_Targeting( target_pos, do_tilt ); // recurse
  1047. calls--;
  1048. }
  1049. ready = true;
  1050. }
  1051. return ready;
  1052. }
  1053. int VehicleGameObj::Get_Weapon_Control_Owner(void)
  1054. {
  1055. SoldierGameObj * gunner = Get_Gunner();
  1056. if ( gunner && !Get_Driver_Is_Gunner() ) {
  1057. return gunner->Get_Control_Owner();
  1058. }
  1059. SoldierGameObj * driver = Get_Driver();
  1060. if ( driver ) {
  1061. return driver->Get_Control_Owner();
  1062. }
  1063. return Get_Control_Owner();
  1064. }
  1065. /*
  1066. void VehicleGameObj::Apply_Control( void )
  1067. {
  1068. SoldierGameObj * driver = Seats[DRIVER_SEAT];
  1069. SoldierGameObj * gunner = Seats[GUNNER_SEAT];
  1070. if ( gunner == NULL ) {
  1071. gunner = driver;
  1072. }
  1073. bool target_steering = false;
  1074. // If I have a driver, use his control
  1075. if ( driver && !driver->In_Transition() ) {
  1076. //Debug_Say(( "Vehicle copy driver's control\n" )); //copy his control???
  1077. Control = driver->Get_Control();
  1078. target_steering = true;
  1079. } else if ( !Get_Action()->Is_Acting() ) {
  1080. Control.Clear_Control(); // Bad for network????
  1081. }
  1082. // If i have a gunner, use him for the gun control
  1083. if ( gunner && (gunner != driver) && !gunner->In_Transition() ) {
  1084. Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_FIRE_PRIMARY, gunner->Get_Control().Get_Boolean( ControlClass::BOOLEAN_WEAPON_FIRE_PRIMARY ) );
  1085. Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_FIRE_SECONDARY, gunner->Get_Control().Get_Boolean( ControlClass::BOOLEAN_WEAPON_FIRE_SECONDARY ) );
  1086. Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_NEXT, gunner->Get_Control().Get_Boolean( ControlClass::BOOLEAN_WEAPON_NEXT ) );
  1087. Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_PREV, gunner->Get_Control().Get_Boolean( ControlClass::BOOLEAN_WEAPON_PREV ) );
  1088. Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_RELOAD, gunner->Get_Control().Get_Boolean( ControlClass::BOOLEAN_WEAPON_RELOAD ) );
  1089. Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_USE, gunner->Get_Control().Get_Boolean( ControlClass::BOOLEAN_WEAPON_USE ) );
  1090. }
  1091. if ( target_steering ) {
  1092. // Lets make turns come from the targeting
  1093. Vector3 target_pos = Get_Targeting_Pos();
  1094. Vector3 obj_space_target;
  1095. Matrix3D::Inverse_Transform_Vector( Get_Transform(), target_pos, &obj_space_target );
  1096. float target_direction = WWMath::Atan2( obj_space_target.Y, obj_space_target.X );
  1097. float turn_amount = target_direction / DEG_TO_RAD( 60 );
  1098. Control.Set_Analog( ControlClass::ANALOG_TURN_LEFT, turn_amount );
  1099. }
  1100. // Clamp turning
  1101. Control.Set_Analog( ControlClass::ANALOG_TURN_LEFT,
  1102. WWMath::Clamp( Control.Get_Analog( ControlClass::ANALOG_TURN_LEFT ), -1.0F, 1.0F ) );
  1103. SmartGameObj::Apply_Control();
  1104. }
  1105. */
  1106. void VehicleGameObj::Apply_Control( void )
  1107. {
  1108. SoldierGameObj * driver = Get_Driver();
  1109. SoldierGameObj * gunner = Get_Gunner();
  1110. if ( gunner == NULL || Get_Driver_Is_Gunner() ) {
  1111. gunner = driver;
  1112. }
  1113. // if ( gunner || driver ) {
  1114. if ( !CombatManager::Is_Gameplay_Permitted() ) {
  1115. Clear_Control();
  1116. Controller.Reset();
  1117. if ( Peek_Vehicle_Phys() != NULL) {
  1118. Peek_Vehicle_Phys()->Set_Velocity( Vector3(0,0,0) );
  1119. }
  1120. return;
  1121. }
  1122. // }
  1123. bool target_steering = false;
  1124. // If I have a driver, use his control
  1125. if ( driver && !driver->In_Transition() && driver->Is_Human_Controlled () == true) {
  1126. /*
  1127. bool use_control = CombatManager::I_Am_Server() ||
  1128. (CombatManager::Get_Client_Interpolation_Model() !=
  1129. CLIENT_INTERPOLATION_PATHFIND) ||
  1130. (CombatManager::I_Am_Client() &&
  1131. driver->Get_Control_Owner() == CombatManager::Get_My_Id());
  1132. //Debug_Say(( "Vehicle copy driver's control\n" )); //copy his control???
  1133. if (use_control) {
  1134. Control = driver->Get_Control();
  1135. target_steering = true;
  1136. }
  1137. */
  1138. //
  1139. // This could be a little dodgy... does client do this?
  1140. //
  1141. Control = driver->Get_Control();
  1142. target_steering = true;
  1143. }
  1144. // If i have a gunner, use him for the gun control
  1145. if ( gunner && (gunner != driver) && !gunner->In_Transition() && !Get_Driver_Is_Gunner() ) {
  1146. Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_FIRE_PRIMARY, gunner->Get_Control().Get_Boolean( ControlClass::BOOLEAN_WEAPON_FIRE_PRIMARY ) );
  1147. Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_FIRE_SECONDARY, gunner->Get_Control().Get_Boolean( ControlClass::BOOLEAN_WEAPON_FIRE_SECONDARY ) );
  1148. Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_RELOAD, gunner->Get_Control().Get_Boolean( ControlClass::BOOLEAN_WEAPON_RELOAD ) );
  1149. }
  1150. // Vehicles never change weapons
  1151. Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_NEXT, false );
  1152. Control.Set_Boolean( ControlClass::BOOLEAN_WEAPON_PREV, false );
  1153. if ( target_steering && _Use_Target_Steering ) {
  1154. // Target Steering kicks in if the user is pressing the accelerator key.
  1155. if (Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ) >= 0.0f) {
  1156. // Lets make turns come from the targeting
  1157. Vector3 target_pos = Get_Targeting_Pos();
  1158. Vector3 obj_space_target;
  1159. Matrix3D::Inverse_Transform_Vector( Get_Transform(), target_pos, &obj_space_target );
  1160. float target_direction = WWMath::Atan2( obj_space_target.Y, obj_space_target.X );
  1161. // Offset the target direction if the user is turning while driving forward
  1162. const float TARGET_STRAFE_ANGLE = DEG_TO_RADF(50.0f);
  1163. target_direction += TARGET_STRAFE_ANGLE * Control.Get_Analog( ControlClass::ANALOG_TURN_LEFT );
  1164. // Set the final steering angle
  1165. float turn_amount = target_direction / DEG_TO_RAD( 60 ) * Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD );
  1166. Control.Set_Analog( ControlClass::ANALOG_TURN_LEFT, turn_amount );
  1167. } else {
  1168. // When over-riding the target steering with keyboard steering,
  1169. // scale the turn rate down if we're going forward.
  1170. if (Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ) > 0.0f) {
  1171. Control.Set_Analog( ControlClass::ANALOG_TURN_LEFT, 0.5f * Control.Get_Analog(ControlClass::ANALOG_TURN_LEFT) );
  1172. }
  1173. }
  1174. }
  1175. // Clamp turning
  1176. Control.Set_Analog( ControlClass::ANALOG_TURN_LEFT,
  1177. WWMath::Clamp( Control.Get_Analog( ControlClass::ANALOG_TURN_LEFT ), -1.0F, 1.0F ) );
  1178. SmartGameObj::Apply_Control();
  1179. }
  1180. static char * _profile_name = "Vehicle Think";
  1181. void VehicleGameObj::Think( void )
  1182. {
  1183. { WWPROFILE( _profile_name );
  1184. Apply_Control(); // ????
  1185. Update_Transitions();
  1186. }
  1187. SmartGameObj::Think(); // Perform smart object thinking
  1188. { WWPROFILE( _profile_name );
  1189. Update_Sound_Effects();
  1190. Update_Wheel_Effects();
  1191. // Update the lock status
  1192. if (LockTimer > 0.0f) {
  1193. LockTimer -= TimeManager::Get_Frame_Seconds();
  1194. } else {
  1195. LockOwner = NULL;
  1196. }
  1197. // UnStealth if we don't have any occupants, and we aren't in single play
  1198. if (StealthEffect != NULL) {
  1199. if ((Get_Occupant_Count() == 0 ) && ( !IS_MISSION )) {
  1200. StealthEffect->Enable_Stealth(false);
  1201. }
  1202. }
  1203. }
  1204. }
  1205. static char * _post_profile_name = "Vehicle PostThink";
  1206. void VehicleGameObj::Post_Think( void )
  1207. {
  1208. { WWPROFILE( _post_profile_name );
  1209. RenderObjClass * model = Peek_Model();
  1210. WWASSERT( model );
  1211. for ( int i = 0; i < SeatOccupants.Length(); i++ ) {
  1212. if ( SeatOccupants[i] != NULL ) {
  1213. char bone_name[80];
  1214. sprintf( bone_name, "SEAT%d", i );
  1215. int seat_bone_index = model->Get_Bone_Index( bone_name );
  1216. if ( seat_bone_index != -1 ) {
  1217. Matrix3D seat = model->Get_Bone_Transform( seat_bone_index );
  1218. if ( !SeatOccupants[i]->In_Transition() ) {
  1219. SeatOccupants[i]->Set_Transform( seat );
  1220. }
  1221. } else {
  1222. Debug_Say(( "No Seat bone %s\n", bone_name ));
  1223. }
  1224. }
  1225. }
  1226. }
  1227. SmartGameObj::Post_Think();
  1228. { WWPROFILE( _post_profile_name );
  1229. if ( Get_Weapon() && !Get_Definition().Fire0Anim.Is_Empty() ) {
  1230. Get_Weapon()->Reset_Anim_Update();
  1231. int state = Get_Weapon()->Get_Anim_State();
  1232. if ( state == WEAPON_ANIM_NOT_FIRING ) {
  1233. Set_Animation( NULL );
  1234. } else if ( state == WEAPON_ANIM_FIRING_0 ) {
  1235. Set_Animation( Get_Definition().Fire0Anim );
  1236. } else if ( state == WEAPON_ANIM_FIRING_1 ) {
  1237. Set_Animation( !Get_Definition().Fire1Anim.Is_Empty() ? Get_Definition().Fire1Anim : Get_Definition().Fire0Anim );
  1238. } else {
  1239. Debug_Say(( "Bad Weapon Anim State\n" ));
  1240. }
  1241. }
  1242. }
  1243. }
  1244. /*
  1245. **
  1246. */
  1247. int VehicleGameObj::Get_Player_Type(void) const
  1248. {
  1249. // If they have a driver, they are his team.
  1250. for (int i = 0; i < SeatOccupants.Length(); i++) {
  1251. if (SeatOccupants[i] != NULL) {
  1252. return SeatOccupants[i]->Get_Player_Type();
  1253. }
  1254. }
  1255. #if 0 // Do this at exit time
  1256. // in MP, empty vehicles are neutral
  1257. if ( !IS_MISSION ) {
  1258. return PLAYERTYPE_NEUTRAL;
  1259. }
  1260. #endif
  1261. return SmartGameObj::Get_Player_Type();
  1262. }
  1263. /*
  1264. **
  1265. */
  1266. void VehicleGameObj::Init_Wheel_Effects( void )
  1267. {
  1268. // Create an array of persistant surface emitters, one for each "real" wheel
  1269. WWASSERT( WheelSurfaceEmitters.Length() == 0 );
  1270. if ( (Peek_Vehicle_Phys() != NULL) &&
  1271. (Peek_Vehicle_Phys()->Get_VehiclePhysDef()->Is_Fake() == false) )
  1272. {
  1273. int real_wheel_count = Peek_Vehicle_Phys()->Get_Real_Wheel_Count();
  1274. WheelSurfaceEmitters.Resize(real_wheel_count);
  1275. for (int i=0; i<real_wheel_count; i++) {
  1276. WheelSurfaceEmitters[i] = SurfaceEffectsManager::Create_Persistant_Emitter();
  1277. }
  1278. }
  1279. // Create a single persistant surface effect sound for tire squealing
  1280. WWASSERT(WheelSurfaceSound == NULL);
  1281. WheelSurfaceSound = SurfaceEffectsManager::Create_Persistant_Sound();
  1282. }
  1283. void VehicleGameObj::Shutdown_Wheel_Effects( void )
  1284. {
  1285. // Release the persistant surface emitters
  1286. for (int i=0; i<WheelSurfaceEmitters.Length(); i++) {
  1287. SurfaceEffectsManager::Destroy_Persistant_Emitter( WheelSurfaceEmitters[i] );
  1288. WheelSurfaceEmitters[i] = NULL;
  1289. }
  1290. WheelSurfaceEmitters.Clear();
  1291. // Release the persistant surface sound effect
  1292. SurfaceEffectsManager::Destroy_Persistant_Sound(WheelSurfaceSound);
  1293. WheelSurfaceSound = NULL;
  1294. }
  1295. void VehicleGameObj::Update_Wheel_Effects( void )
  1296. {
  1297. if ( (Peek_Vehicle_Phys() == NULL) ||
  1298. (Peek_Vehicle_Phys()->Get_VehiclePhysDef()->Is_Fake()) )
  1299. {
  1300. return;
  1301. }
  1302. int roll_hitter_type = SurfaceEffectsManager::HITTER_TYPE_TIRE_ROLLING;
  1303. int slide_hitter_type = SurfaceEffectsManager::HITTER_TYPE_TIRE_SLIDING;
  1304. if ( Peek_Vehicle_Phys()->As_TrackedVehicleClass() != NULL) {
  1305. roll_hitter_type = SurfaceEffectsManager::HITTER_TYPE_TRACK_ROLLING;
  1306. slide_hitter_type = SurfaceEffectsManager::HITTER_TYPE_TRACK_SLIDING;
  1307. }
  1308. Vector3 vel;
  1309. Peek_Vehicle_Phys()->Get_Velocity(&vel);
  1310. float vlen2 = vel.Length2();
  1311. // Update the emitters and sound
  1312. const float EMITTER_SLIDE_THRESHOLD = 1.0f;
  1313. const float EMITTER_ROLL_THRESHOLD2 = 2.0f * 2.0f;
  1314. const float EMITTER_CINEMATIC_ROLL_AVEL = DEG_TO_RADF(1.0f);
  1315. bool sound_done = false;
  1316. if (WheelSurfaceEmitters.Length() > 0) {
  1317. if ( Peek_Vehicle_Phys() != NULL) {
  1318. int emitter_index = 0;
  1319. int wheel_count = Peek_Vehicle_Phys()->Get_Wheel_Count();
  1320. for (int i=0; i<wheel_count; i++) {
  1321. SuspensionElementClass * wheel = Peek_Vehicle_Phys()->Peek_Wheel(i);
  1322. if (wheel->Get_Flag(SuspensionElementClass::FAKE) == false) {
  1323. // Look up the surface and hitter types
  1324. int surface_type = wheel->Get_Contact_Surface();
  1325. int hitter_type = SurfaceEffectsManager::HITTER_TYPE_NONE;
  1326. // If stealthed, no emitters.
  1327. if (Is_Stealthed()) {
  1328. surface_type = SURFACE_TYPE_DEFAULT;
  1329. }
  1330. if (wheel->Get_Flag( SuspensionElementClass::INCONTACT ) ) {
  1331. if (wheel->Get_Slip_Factor() > EMITTER_SLIDE_THRESHOLD) {
  1332. hitter_type = slide_hitter_type;
  1333. } else {
  1334. // normally, trigger roll emitters by the velocity of the vehicle
  1335. if (vlen2 > EMITTER_ROLL_THRESHOLD2) {
  1336. hitter_type = roll_hitter_type;
  1337. }
  1338. // when attached to a cinematic, we deduce whether the wheels are rolling
  1339. // by using the rotation delta feature of wheels
  1340. if (Is_Attached_To_An_Object()) {
  1341. float wheel_avel = WWMath::Fabs(wheel->Get_Rotation_Delta() / TimeManager::Get_Frame_Seconds());
  1342. if (wheel_avel > EMITTER_CINEMATIC_ROLL_AVEL) {
  1343. hitter_type = roll_hitter_type;
  1344. }
  1345. }
  1346. }
  1347. }
  1348. // Update the emitter and move to the next emitter
  1349. SurfaceEffectsManager::Update_Persistant_Emitter( WheelSurfaceEmitters[emitter_index],
  1350. surface_type,
  1351. hitter_type,
  1352. wheel->Get_Wheel_Transform());
  1353. emitter_index++;
  1354. // If this is the sound generating wheel, update the sound
  1355. // I'm just using the first engine wheel as the one that generates the tire sound effects.
  1356. if (!sound_done && wheel->Get_Flag(SuspensionElementClass::ENGINE)) {
  1357. SurfaceEffectsManager::Update_Persistant_Sound( WheelSurfaceSound,
  1358. surface_type,
  1359. hitter_type,
  1360. Get_Transform());
  1361. sound_done = true;
  1362. }
  1363. }
  1364. }
  1365. }
  1366. }
  1367. }
  1368. void VehicleGameObj::Update_Sound_Effects(void)
  1369. {
  1370. if (Peek_Vehicle_Phys() == NULL) {
  1371. return;
  1372. }
  1373. switch (EngineSoundState)
  1374. {
  1375. case ENGINE_SOUND_STATE_STARTING:
  1376. {
  1377. // play our ENGINE_STARTING sound, if it is finished or not found, destroy and go to ENGINE_RUNNING
  1378. if ((CachedEngineSound == NULL) || ((CachedEngineSound != NULL) && (CachedEngineSound->Get_State()==AudibleSoundClass::STATE_STOPPED))) {
  1379. Change_Engine_Sound_State(ENGINE_SOUND_STATE_RUNNING);
  1380. }
  1381. }
  1382. break;
  1383. case ENGINE_SOUND_STATE_RUNNING:
  1384. {
  1385. if (CachedEngineSound != NULL) {
  1386. Update_Engine_Sound_Pitch ();
  1387. }
  1388. if (Peek_Vehicle_Phys()->Is_Engine_Enabled() == false) {
  1389. Change_Engine_Sound_State(ENGINE_SOUND_STATE_STOPPING);
  1390. }
  1391. }
  1392. break;
  1393. case ENGINE_SOUND_STATE_STOPPING:
  1394. {
  1395. // play the ENGINE_STOPPING sound, if it is finished or not found, destroy and go to ENGINE_OFF
  1396. if ((CachedEngineSound == NULL) || ((CachedEngineSound != NULL) && (CachedEngineSound->Get_State()==AudibleSoundClass::STATE_STOPPED))) {
  1397. Change_Engine_Sound_State(ENGINE_SOUND_STATE_OFF);
  1398. }
  1399. }
  1400. break;
  1401. case ENGINE_SOUND_STATE_OFF:
  1402. {
  1403. // if the engine is on, go to ENGINE_STARTING
  1404. if (Peek_Vehicle_Phys()->Is_Engine_Enabled() == true) {
  1405. Change_Engine_Sound_State(ENGINE_SOUND_STATE_STARTING);
  1406. }
  1407. }
  1408. break;
  1409. default:
  1410. WWDEBUG_SAY(("Error: Invalid Engine Sound State %d! file: %s line: %d\n",EngineSoundState,__FILE__,__LINE__));
  1411. break;
  1412. }
  1413. }
  1414. void VehicleGameObj::Change_Engine_Sound_State(int new_state)
  1415. {
  1416. if (CachedEngineSound != NULL) {
  1417. CachedEngineSound->Remove_From_Scene();
  1418. CachedEngineSound->Release_Ref();
  1419. CachedEngineSound = NULL;
  1420. }
  1421. EngineSoundState = new_state;
  1422. DefinitionClass * sound_def = DefinitionMgrClass::Find_Definition(Get_Definition().EngineSound[new_state]);
  1423. if (sound_def != NULL) {
  1424. CachedEngineSound = (AudibleSoundClass *)sound_def->Create();
  1425. }
  1426. if (CachedEngineSound != NULL) {
  1427. CachedEngineSound->Add_To_Scene();
  1428. CachedEngineSound->Attach_To_Object(Peek_Model());
  1429. }
  1430. return ;
  1431. }
  1432. void VehicleGameObj::Update_Engine_Sound_Pitch(void)
  1433. {
  1434. if (CachedEngineSound == NULL) {
  1435. return ;
  1436. }
  1437. float pitch_factor = 1.0F;
  1438. if (Peek_Vehicle_Phys() != NULL) {
  1439. MotorVehicleClass * motophys = Peek_Physical_Object()->As_MotorVehicleClass();
  1440. TrackedVehicleClass * trackphys = Peek_Physical_Object()->As_TrackedVehicleClass();
  1441. VTOLVehicleClass * vtolphys = Peek_Physical_Object()->As_VTOLVehicleClass();
  1442. float max_factor = Get_Definition().EngineSoundMaxPitchFactor;
  1443. if (motophys != NULL) {
  1444. // Wheeled vehicle engine pitch depends on their rpms
  1445. float norm_rpm = motophys->Get_Normalized_Engine_RPM();
  1446. if (norm_rpm < 0) {
  1447. norm_rpm = -norm_rpm;
  1448. }
  1449. norm_rpm = WWMath::Clamp(norm_rpm,0.0f,1.0f);
  1450. pitch_factor = 1.0F + ((max_factor - 1.0F) * norm_rpm);
  1451. } else if (trackphys != NULL) {
  1452. // Tracked vehicle engine pitch depends on their overall speed
  1453. const float MAX_TRACKED_SPEED = 10.0f;
  1454. Vector3 vel;
  1455. trackphys->Get_Velocity(&vel);
  1456. float norm_speed = WWMath::Clamp(vel.Length() / MAX_TRACKED_SPEED,0.0f,1.0f);
  1457. pitch_factor = 1.0F + ((max_factor - 1.0F) * norm_speed);
  1458. } else if (vtolphys != NULL) {
  1459. // VTOL vehicle engine pitch depends on absolute acceleration
  1460. const float MAX_VTOL_SPEED = 20.0f;
  1461. Vector3 vel;
  1462. vtolphys->Get_Velocity(&vel);
  1463. float norm_speed = WWMath::Clamp(vel.Length() / MAX_VTOL_SPEED,0.0f,1.0f);
  1464. pitch_factor = 1.0F + ((max_factor - 1.0F) * norm_speed);
  1465. }
  1466. }
  1467. CachedEngineSound->Set_Pitch_Factor (pitch_factor);
  1468. return ;
  1469. }
  1470. VehiclePhysClass *VehicleGameObj::Peek_Vehicle_Phys( void ) const
  1471. {
  1472. WWASSERT( Peek_Physical_Object() );
  1473. return Peek_Physical_Object()->As_VehiclePhysClass(); // NOTE: sometimes will return NULL!
  1474. }
  1475. /*
  1476. ** Occupants ( drivers, gunners, passengers )
  1477. */
  1478. void VehicleGameObj::Add_Occupant( SoldierGameObj * occupant )
  1479. {
  1480. // Add the the lowest empty seat
  1481. for ( int i = 0; i < Get_Definition().NumSeats; i++ ) {
  1482. if ( SeatOccupants[i] == NULL ) {
  1483. Add_Occupant( occupant, i );
  1484. break;;
  1485. }
  1486. }
  1487. }
  1488. void VehicleGameObj::Add_Occupant( SoldierGameObj * occupant, int seat_id )
  1489. {
  1490. WWASSERT( occupant );
  1491. if ( SeatOccupants[seat_id] != NULL ) {
  1492. Debug_Say(( "Vehicle already has an occupant in that seat\n" ));
  1493. return;
  1494. }
  1495. if ( Find_Seat( occupant ) != -1 ) {
  1496. Debug_Say(( "Soldier already in vehicle\n" ));
  1497. }
  1498. if ( seat_id == 0 ) {
  1499. DriverIsGunner = DefaultDriverIsGunner;
  1500. }
  1501. Debug_Say(( "Soldier %p added to seat %d\n", occupant, seat_id ));
  1502. if ( seat_id > Get_Definition().NumSeats-1 ) {
  1503. Debug_Say(( "Adding to extra seat %d\n", seat_id ));
  1504. }
  1505. SeatOccupants[seat_id] = occupant;
  1506. OccupiedSeats++;
  1507. Set_Object_Dirty_Bit( NetworkObjectClass::BIT_RARE, true );
  1508. const char * anim_name = NULL;
  1509. if ( Get_Definition().Type == VEHICLE_TYPE_BIKE ) {
  1510. anim_name = "S_A_HUMAN.H_A_V20A";
  1511. } else {
  1512. anim_name = "S_A_HUMAN.H_A_V10A";
  1513. }
  1514. occupant->Enter_Vehicle( this, anim_name );
  1515. // Debug_Say(( "Vehicle has a occupant in seat %d\n", seat_id ));
  1516. // possibily delete transitions
  1517. Create_And_Destroy_Transitions();
  1518. Enable_Engine(true);
  1519. if ( Get_Definition().OccupantsVisible == false ) {
  1520. occupant->Peek_Model()->Set_Hidden( true );
  1521. }
  1522. // Notify the observers that someone entered
  1523. const GameObjObserverList & observer_list = Get_Observers();
  1524. for( int index = 0; index < observer_list.Count(); index++ ) {
  1525. observer_list[ index ]->Custom( this, CUSTOM_EVENT_VEHICLE_ENTERED, seat_id, occupant );
  1526. }
  1527. //
  1528. // Reveal this vehicle to the player if he was the one to enter the vehicle
  1529. //
  1530. if ( occupant == COMBAT_STAR ) {
  1531. EncyclopediaMgrClass::Reveal_Object( this );
  1532. }
  1533. //
  1534. // Reset any action the vehicle was performing
  1535. //
  1536. if (Get_Action() != NULL) {
  1537. Get_Action()->Reset(1);
  1538. }
  1539. //
  1540. // Unlock the vehicle!
  1541. //
  1542. LockTimer = 0.0f;
  1543. LockOwner = NULL;
  1544. }
  1545. int VehicleGameObj::Find_Seat( SoldierGameObj * occupant )
  1546. {
  1547. for ( int i = 0; i < SeatOccupants.Length(); i++ ) {
  1548. if ( SeatOccupants[i] == occupant ) {
  1549. return i;
  1550. }
  1551. }
  1552. return -1;
  1553. }
  1554. void VehicleGameObj::Remove_Occupant( SoldierGameObj * occupant )
  1555. {
  1556. WWASSERT( occupant );
  1557. int seat_num = Find_Seat( occupant );
  1558. if ( seat_num == -1 ) {
  1559. Debug_Say(( "Object is not in this vehicle\n" ));
  1560. return;
  1561. }
  1562. // Do this anyway, just to make sure it happens.
  1563. if ( Get_Definition().OccupantsVisible == false ) {
  1564. if ( SeatOccupants[seat_num] && SeatOccupants[seat_num]->Peek_Model() ) {
  1565. SeatOccupants[seat_num]->Peek_Model()->Set_Hidden( false );
  1566. }
  1567. }
  1568. Debug_Say(( "Soldier %p removed from seat %d\n", occupant, seat_num ));
  1569. SeatOccupants[seat_num]->Exit_Vehicle();
  1570. SeatOccupants[seat_num] = NULL;
  1571. OccupiedSeats--;
  1572. // in MP, empty vehicles are neutral
  1573. if ( !IS_MISSION && OccupiedSeats == 0 ) {
  1574. Set_Player_Type( PLAYERTYPE_NEUTRAL );
  1575. }
  1576. Set_Object_Dirty_Bit( NetworkObjectClass::BIT_RARE, true );
  1577. // possibily re-enable transitions
  1578. Create_And_Destroy_Transitions();
  1579. if ( seat_num == 0 ) {
  1580. Enable_Engine(false);
  1581. }
  1582. // Notify the observers that someone left
  1583. const GameObjObserverList & observer_list = Get_Observers();
  1584. for( int index = 0; index < observer_list.Count(); index++ ) {
  1585. observer_list[ index ]->Custom( this, CUSTOM_EVENT_VEHICLE_EXITED, seat_num, occupant );
  1586. }
  1587. }
  1588. bool VehicleGameObj::Contains_Occupant( SoldierGameObj * occupant )
  1589. {
  1590. WWASSERT( occupant );
  1591. for ( int i = 0; i < SeatOccupants.Length(); i++ ) {
  1592. if ( SeatOccupants[i] == occupant ) {
  1593. return true;
  1594. }
  1595. }
  1596. return false;
  1597. }
  1598. int VehicleGameObj::Get_Occupant_Count(void)
  1599. {
  1600. int count = 0;
  1601. for ( int i = 0; i < SeatOccupants.Length(); i++ ) {
  1602. if ( SeatOccupants[i] != NULL ) {
  1603. count++;
  1604. }
  1605. }
  1606. return count;
  1607. }
  1608. bool VehicleGameObj::Is_Entry_Permitted( SoldierGameObj * p_soldier )
  1609. {
  1610. if (!CombatManager::Is_Gameplay_Permitted()) {
  1611. //
  1612. // You can't enter a vehicle if there are no opponents, etc
  1613. //
  1614. return false;
  1615. }
  1616. //
  1617. // Do not permit a soldier to enter a vehicle if there is already
  1618. // another soldier of a different player type in the vehicle.
  1619. // Actually, only allow 2 Nods or 2 GDI's to share a vehicle...
  1620. //
  1621. WWASSERT(p_soldier != NULL);
  1622. bool is_permitted = true;
  1623. int player_type = p_soldier->Get_Player_Type();
  1624. int i;
  1625. for (i = 0; i < SeatOccupants.Length(); i++) {
  1626. if (SeatOccupants[i] != NULL) {
  1627. int seated_pt = SeatOccupants[i]->Get_Player_Type();
  1628. if (seated_pt != player_type ||
  1629. (seated_pt != PLAYERTYPE_NOD && seated_pt != PLAYERTYPE_GDI)) {
  1630. is_permitted = false;
  1631. break;
  1632. }
  1633. }
  1634. }
  1635. // If there are no entry transitions, entry is not possible
  1636. bool entry_exists = false;
  1637. for ( i = 0; i < TransitionInstances.Count(); i++ ) {
  1638. // if ( ( TransitionInstances[i]->Get_Type() == TransitionDataClass::LEGACY_VEHICLE_ENTER_0 ) ||
  1639. // ( TransitionInstances[i]->Get_Type() == TransitionDataClass::LEGACY_VEHICLE_ENTER_1 ) ) {
  1640. if ( TransitionInstances[i]->Get_Type() == TransitionDataClass::VEHICLE_ENTER ) {
  1641. entry_exists = true;
  1642. }
  1643. }
  1644. if ( !entry_exists ) {
  1645. is_permitted = false;
  1646. }
  1647. // If the vehicle is locked and you are not the lock owner, entry is not permitted
  1648. if (Is_Locked() && (p_soldier != LockOwner.Get_Ptr())) {
  1649. is_permitted = false;
  1650. }
  1651. return is_permitted;
  1652. }
  1653. void VehicleGameObj::Remove_Transitions( TransitionDataClass::StyleType transition_type )
  1654. {
  1655. for ( int i = 0; i < TransitionInstances.Count(); i++ ) {
  1656. if ( TransitionInstances[i]->Get_Type() == transition_type ) {
  1657. TransitionManager::Destroy( TransitionInstances[i] );
  1658. TransitionInstances.Delete( i );
  1659. i--;
  1660. }
  1661. }
  1662. }
  1663. void VehicleGameObj::Create_New_Transitions( TransitionDataClass::StyleType transition_type )
  1664. {
  1665. const TRANSITION_DATA_LIST & trans_data_list = Get_Definition().Get_Transition_List();
  1666. for ( int i = 0; i < trans_data_list.Count(); i++ ) {
  1667. if ( trans_data_list[i]->Get_Type() == transition_type ) {
  1668. // make new instance
  1669. TransitionInstanceClass * trans = new TransitionInstanceClass( *trans_data_list[i] );
  1670. // setup
  1671. trans->Set_Vehicle( this );
  1672. // add to our list
  1673. TransitionInstances.Add( trans );
  1674. // add to master list
  1675. TransitionManager::Add( trans );
  1676. }
  1677. }
  1678. }
  1679. void VehicleGameObj::Update_Transitions( void )
  1680. {
  1681. Matrix3D tm = Get_Transform();
  1682. // Update all transitions
  1683. for ( int i = 0; i < TransitionInstances.Count(); i++ ) {
  1684. TransitionInstances[i]->Set_Parent_Transform( tm );
  1685. }
  1686. }
  1687. void VehicleGameObj::Passenger_Entering( void )
  1688. {
  1689. // Play the passenger entering animation
  1690. char string[80];
  1691. sprintf( string, "V_%sL1.M_%sCL", Get_Definition().TypeName, Get_Definition().TypeName );
  1692. Set_Animation( string );
  1693. Get_Anim_Control()->Set_Mode( ANIM_MODE_ONCE );
  1694. //Debug_Say(("VehicleGameObj::Passenger_Entering\n"));
  1695. }
  1696. void VehicleGameObj::Passenger_Exiting( void )
  1697. {
  1698. // Play the passenger leaving animation
  1699. char string[80];
  1700. sprintf( string, "V_%sL1.M_%sOP", Get_Definition().TypeName, Get_Definition().TypeName );
  1701. Set_Animation( string );
  1702. Get_Anim_Control()->Set_Mode( ANIM_MODE_ONCE );
  1703. Control.Clear_Control();
  1704. }
  1705. //void VehicleGameObj::Get_Extended_Information(StringClass & description)
  1706. void VehicleGameObj::Get_Description(StringClass & description)
  1707. {
  1708. StringClass line;
  1709. line.Format("ID: %d\n", Get_ID());
  1710. description += line;
  1711. line.Format("NAME: %s\n", Get_Definition().Get_Name());
  1712. description += line;
  1713. line.Format("TEAM: %d\n", Get_Player_Type());
  1714. description += line;
  1715. line.Format("CONTR: %d\n", Get_Control_Owner());
  1716. description += line;
  1717. Vector3 position;
  1718. Get_Position(&position);
  1719. line.Format("POS: %-5.2f, %-5.2f, %-5.2f\n", position.X, position.Y, position.Z);
  1720. description += line;
  1721. Vector3 target = Get_Targeting_Pos();
  1722. line.Format("TGT: %-5.2f, %-5.2f, %-5.2f\n", target.X, target.Y, target.Z);
  1723. description += line;
  1724. Vector3 velocity;
  1725. Get_Velocity(velocity);
  1726. line.Format("VEL: %-5.2f, %-5.2f, %-5.2f\n", velocity.X, velocity.Y, velocity.Z);
  1727. description += line;
  1728. line.Format("SPD: %-5.2f\n", velocity.Length());
  1729. description += line;
  1730. WeaponClass * p_weapon = Get_Weapon();
  1731. if (p_weapon != NULL) {
  1732. line.Format("WEAP: %s\n", p_weapon->Get_Name());
  1733. description += line;
  1734. line.Format("RNDS: %d\n", p_weapon->Get_Total_Rounds());
  1735. description += line;
  1736. }
  1737. if (Get_Defense_Object() != NULL) {
  1738. line.Format("HLTH: %-5.2f\n", Get_Defense_Object()->Get_Health());
  1739. description += line;
  1740. }
  1741. if (Get_Driver() != NULL) {
  1742. line.Format("DRVR: %d\n", Get_Driver()->Get_ID());
  1743. description += line;
  1744. }
  1745. if (Get_Gunner() != NULL) {
  1746. line.Format("GUNR: %d\n", Get_Gunner()->Get_ID());
  1747. description += line;
  1748. }
  1749. line.Format("HIB: %d\n", Is_Hibernating());
  1750. description += line;
  1751. ControlClass & control = Get_Control();
  1752. line.Format("CTRL: %d, %d, %5.2f, %5.2f, %5.2f, %5.2f\n",
  1753. control.Get_One_Time_Boolean_Bits(),
  1754. control.Get_Continuous_Boolean_Bits(),
  1755. control.Get_Analog(ControlClass::ANALOG_MOVE_FORWARD),
  1756. control.Get_Analog(ControlClass::ANALOG_MOVE_LEFT),
  1757. control.Get_Analog(ControlClass::ANALOG_MOVE_UP),
  1758. control.Get_Analog(ControlClass::ANALOG_TURN_LEFT));
  1759. description += line;
  1760. if (Get_Action() != NULL) {
  1761. line.Format("ACTCT: %d\n", Get_Action()->Get_Act_Count());
  1762. description += line;
  1763. Vector3 move_loc = Get_Action()->Get_Parameters().MoveLocation;
  1764. line.Format("MVLOC: (%5.2f, %5.2f, %5.2f)\n", move_loc.X, move_loc.Y, move_loc.Z);
  1765. description += line;
  1766. }
  1767. line.Format ("ATTCH: %d\n", Is_Attached_To_An_Object ());
  1768. description += line;
  1769. line.Format("stlth: %d\n", Is_Stealth_Enabled());
  1770. description += line;
  1771. line.Format(" on: %d\n", Is_Stealthed());
  1772. description += line;
  1773. line.Format("ISC: %d\n", Get_Import_State_Count());
  1774. description += line;
  1775. }
  1776. bool VehicleGameObj::Is_Engine_Enabled (void) const
  1777. {
  1778. if (Peek_Vehicle_Phys() != NULL) {
  1779. return Peek_Vehicle_Phys()->Is_Engine_Enabled();
  1780. } else {
  1781. return false;
  1782. }
  1783. }
  1784. void VehicleGameObj::Enable_Engine (bool onoff)
  1785. {
  1786. if (Peek_Vehicle_Phys() != NULL) {
  1787. Peek_Vehicle_Phys()->Enable_Engine(onoff);
  1788. }
  1789. }
  1790. void VehicleGameObj::Set_Precision(void)
  1791. {
  1792. cEncoderList::Set_Precision(BITPACK_VEHICLE_VELOCITY,
  1793. -90.0f, 90.0f, 0.01f); // 200 mph = approx. 90 m/s
  1794. cEncoderList::Set_Precision(BITPACK_VEHICLE_ANGULAR_VELOCITY,
  1795. -20.0f, 20.0f, 0.01f); // to approx 6 PI
  1796. cEncoderList::Set_Precision(BITPACK_VEHICLE_QUATERNION,
  1797. -1.0f, 1.0f, 0.0005f); // -1 to 1
  1798. cEncoderList::Set_Precision(BITPACK_VEHICLE_LOCK_TIMER,
  1799. 0.0f, 16.0f, 0.25f);
  1800. }
  1801. void VehicleGameObj::Apply_Damage( const OffenseObjectClass & damager, float scale, int alternate_skin )
  1802. {
  1803. float starting_health = Get_Defense_Object()->Get_Health();
  1804. //
  1805. // Let the base class actually handle the call
  1806. //
  1807. SmartGameObj::Apply_Damage( damager, scale, alternate_skin );
  1808. // Stats
  1809. if ( starting_health > 0 && Get_Defense_Object()->Get_Health() <= 0 ) {
  1810. if ( damager.Get_Owner() && damager.Get_Owner()->As_SoldierGameObj() != NULL ) {
  1811. if ( damager.Get_Owner()->As_SoldierGameObj()->Get_Player_Data() != NULL ) {
  1812. damager.Get_Owner()->As_SoldierGameObj()->Get_Player_Data()->Stats_Add_Vehicle_Destroyed();
  1813. }
  1814. }
  1815. }
  1816. //
  1817. // Now make sure the model is in a state to match its damage level
  1818. //
  1819. Update_Damage_Meshes();
  1820. }
  1821. void VehicleGameObj::Update_Damage_Meshes( void )
  1822. {
  1823. //
  1824. // Calculate what percent health we have after this damage
  1825. //
  1826. DefenseObjectClass *defense_object = Get_Defense_Object();
  1827. if ( defense_object != NULL ) {
  1828. float ending_health = defense_object->Get_Health() / defense_object->Get_Health_Max();
  1829. //
  1830. // Make sure we have a render object to lookup bones on
  1831. //
  1832. RenderObjClass *model = Peek_Model ();
  1833. if (model != NULL) {
  1834. bool show_damage25 = false;
  1835. bool show_damage50 = false;
  1836. bool show_damage75 = false;
  1837. //
  1838. // Determine what the visibility state of these bones should be
  1839. //
  1840. if (ending_health < 0.25F) {
  1841. show_damage25 = true;
  1842. show_damage50 = true;
  1843. show_damage75 = true;
  1844. } else if (ending_health < 0.5F) {
  1845. show_damage25 = true;
  1846. show_damage50 = true;
  1847. } else if (ending_health < 0.75F) {
  1848. show_damage25 = true;
  1849. }
  1850. static const char * DAMAGE25_BONE_NAME = "DAMAGE25";
  1851. static const char * DAMAGE50_BONE_NAME = "DAMAGE50";
  1852. static const char * DAMAGE75_BONE_NAME = "DAMAGE75";
  1853. static int DAMAGE25_BONE_NAME_LEN = ::strlen( DAMAGE25_BONE_NAME );
  1854. static int DAMAGE50_BONE_NAME_LEN = ::strlen( DAMAGE50_BONE_NAME );
  1855. static int DAMAGE75_BONE_NAME_LEN = ::strlen( DAMAGE75_BONE_NAME );
  1856. //
  1857. // Loop over all the bones in the model, showing and hiding any that represent
  1858. // damage levels
  1859. //
  1860. int bone_count = model->Get_Num_Bones();
  1861. for ( int index = 0; index < bone_count; index ++) {
  1862. const char *bone_name = model->Get_Bone_Name( index );
  1863. if ( ::strnicmp( bone_name, DAMAGE25_BONE_NAME, DAMAGE25_BONE_NAME_LEN ) == 0 ) {
  1864. //
  1865. // Show/hide the subobjects associated with 25% damage
  1866. //
  1867. ::Set_Subobject_Visibility( model, index, show_damage25 );
  1868. } else if ( ::strnicmp( bone_name, DAMAGE50_BONE_NAME, DAMAGE50_BONE_NAME_LEN ) == 0 ) {
  1869. //
  1870. // Show/hide the subobjects associated with 50% damage
  1871. //
  1872. ::Set_Subobject_Visibility( model, index, show_damage50 );
  1873. } else if ( ::strnicmp( bone_name, DAMAGE75_BONE_NAME, DAMAGE75_BONE_NAME_LEN ) == 0 ) {
  1874. //
  1875. // Show/hide the subobjects associated with 75% damage
  1876. //
  1877. ::Set_Subobject_Visibility( model, index, show_damage75 );
  1878. }
  1879. }
  1880. }
  1881. }
  1882. return ;
  1883. }
  1884. void Set_Subobject_Visibility (RenderObjClass *model, int bone_index, bool show)
  1885. {
  1886. WWASSERT( model != NULL );
  1887. //
  1888. // Loop over all the subobjects that are attached to this bone
  1889. //
  1890. int count = model->Get_Num_Sub_Objects_On_Bone( bone_index );
  1891. for (int index = 0; index < count; index ++) {
  1892. RenderObjClass *sub_obj = model->Get_Sub_Object_On_Bone(index, bone_index);
  1893. if (sub_obj != NULL) {
  1894. //
  1895. // Show or hide this subobject
  1896. //
  1897. sub_obj->Set_Hidden (!show);
  1898. sub_obj->Release_Ref();
  1899. }
  1900. }
  1901. return ;
  1902. }
  1903. Matrix3D VehicleGameObj::Get_Look_Transform(void)
  1904. {
  1905. if ( Get_Definition().SightDownMuzzle ) {
  1906. return Get_Muzzle();
  1907. }
  1908. return SmartGameObj::Get_Look_Transform();
  1909. }
  1910. const WCHAR * VehicleGameObj::Get_Vehicle_Name( void )
  1911. {
  1912. if ( Get_Definition().VehicleNameID != 0 ) {
  1913. return TranslateDBClass::Get_String( Get_Definition().VehicleNameID );
  1914. }
  1915. return TRANSLATE(IDS_HUD_VEHICLE_NAME);
  1916. }
  1917. // Vehicles should explode when expired / flipped
  1918. ExpirationReactionType VehicleGameObj::Object_Expired( PhysClass * observed_obj )
  1919. {
  1920. // Only on the server
  1921. if ( CombatManager::I_Am_Server() ) {
  1922. if ( Get_Definition().KilledExplosion != 0 && !Is_Delete_Pending() ) {
  1923. Vector3 pos;
  1924. Get_Position(&pos);
  1925. WWASSERT(pos.Is_Valid());// most likely candidate for explosion damage bug....?
  1926. ExplosionManager::Server_Explode( Get_Definition().KilledExplosion,pos,0 );
  1927. }
  1928. if ( !Is_Delete_Pending() ) {
  1929. // notify the observers of killed
  1930. const GameObjObserverList & observer_list = Get_Observers();
  1931. for( int index = 0; index < observer_list.Count(); index++ ) {
  1932. observer_list[ index ]->Killed( this, NULL );
  1933. }
  1934. }
  1935. return SmartGameObj::Object_Expired( observed_obj );
  1936. } else {
  1937. return EXPIRATION_DENIED;
  1938. }
  1939. }
  1940. /*
  1941. **
  1942. */
  1943. SoldierGameObj * VehicleGameObj::Get_Driver(void)
  1944. {
  1945. if ( SeatOccupants.Length() > DRIVER_SEAT ) {
  1946. return SeatOccupants[DRIVER_SEAT];
  1947. }
  1948. return NULL;
  1949. }
  1950. SoldierGameObj * VehicleGameObj::Get_Gunner(void)
  1951. {
  1952. if ( SeatOccupants.Length() > GUNNER_SEAT ) {
  1953. return SeatOccupants[GUNNER_SEAT];
  1954. }
  1955. return NULL;
  1956. }
  1957. SoldierGameObj * VehicleGameObj::Get_Actual_Gunner(void)
  1958. {
  1959. SoldierGameObj * gunner = Get_Gunner();
  1960. if ( gunner && !Get_Driver_Is_Gunner() ) {
  1961. return gunner;
  1962. }
  1963. return Get_Driver();
  1964. }
  1965. float VehicleGameObj::Get_Stealth_Fade_Distance(void) const
  1966. {
  1967. if (IS_MISSION) {
  1968. return GlobalSettingsDef::Get_Global_Settings()->Get_Stealth_Distance_Vehicle();
  1969. } else {
  1970. return GlobalSettingsDef::Get_Global_Settings()->Get_MP_Stealth_Distance_Vehicle();
  1971. }
  1972. }
  1973. void VehicleGameObj::Set_Vehicle_Delivered(void)
  1974. {
  1975. VehicleDelivered = true;
  1976. Set_Object_Dirty_Bit( NetworkObjectClass::BIT_RARE, true );
  1977. }
  1978. float VehicleGameObj::Get_Filter_Distance(void) const
  1979. {
  1980. if (Get_Definition().Type == VEHICLE_TYPE_TURRET) {
  1981. WeaponClass *weapon = ((VehicleGameObj*)this)->Get_Weapon();
  1982. float range = Get_Definition().SightRange;
  1983. if (weapon != NULL) {
  1984. range = min(weapon->Get_Range(), range);
  1985. }
  1986. return(range);
  1987. }
  1988. return(SmartGameObj::Get_Filter_Distance());
  1989. }
  1990. void VehicleGameObj::Ignore_Occupants( void )
  1991. {
  1992. for (int i = 0; i < SeatOccupants.Length(); i++) {
  1993. if (SeatOccupants[i] != NULL) {
  1994. if ( SeatOccupants[i]->Peek_Physical_Object() != NULL ) {
  1995. SeatOccupants[i]->Peek_Physical_Object()->Inc_Ignore_Counter();
  1996. }
  1997. }
  1998. }
  1999. }
  2000. void VehicleGameObj::Unignore_Occupants( void )
  2001. {
  2002. for (int i = 0; i < SeatOccupants.Length(); i++) {
  2003. if (SeatOccupants[i] != NULL) {
  2004. if ( SeatOccupants[i]->Peek_Physical_Object() != NULL ) {
  2005. SeatOccupants[i]->Peek_Physical_Object()->Dec_Ignore_Counter();
  2006. }
  2007. }
  2008. }
  2009. }
  2010. /*
  2011. Vector3 current_position;
  2012. Get_Position(&current_position);
  2013. Vector3 pos_error = current_position - sc_position;
  2014. if (pos_error.Length() > 2.0f && Is_Attached_To_An_Object () == false) {
  2015. Matrix3D tm(q, sc_position);
  2016. p_obj->Set_Transform(tm);
  2017. p_obj->Set_Velocity(vel);
  2018. p_obj->Set_Angular_Velocity(ang_vel);
  2019. } else if (Is_Attached_To_An_Object ()) {
  2020. Debug_Say(( "Skipping vehicle position update - attached to bone.\n" ));
  2021. }
  2022. */
  2023. /*
  2024. // New temporary scheme : constantly correct to server position by linearly
  2025. // interpolating a fraction of the error
  2026. //
  2027. Matrix3D dest_tm(q, sc_position);
  2028. Matrix3D tm = ::Lerp(Get_Transform (), dest_tm, 0.1F);
  2029. p_obj->Set_Transform(tm);
  2030. p_obj->Set_Velocity(vel);
  2031. p_obj->Set_Angular_Velocity(ang_vel);
  2032. */
  2033. /*
  2034. packet.Add_Vector3(pos);
  2035. packet.Add_Quaternion(q);
  2036. packet.Add_Vector3(vel);
  2037. packet.Add_Vector3(ang_vel);
  2038. */
  2039. /*
  2040. void VehicleGameObj::Import_State_Sc(BitStreamClass & packet)
  2041. {
  2042. WWASSERT(CombatManager::I_Am_Only_Client());
  2043. if (Get_Weapon() != NULL) {
  2044. int rounds = packet.Get(rounds);
  2045. Get_Weapon()->Set_Total_Rounds(rounds);
  2046. }
  2047. for (int i = 0; i < NUM_SEATS; i++) {
  2048. int occupant = packet.Get(occupant);
  2049. if (occupant == -1) {
  2050. Seats[i] = NULL;
  2051. } else {
  2052. if ((Seats[i] == NULL) || (Seats[i]->Get_ID() != occupant)) {
  2053. SmartGameObj * obj = GameObjManager::Find_SmartGameObj(occupant);
  2054. Seats[i] = NULL;
  2055. if (obj != NULL) {
  2056. Seats[i] = obj->As_SoldierGameObj();
  2057. }
  2058. }
  2059. }
  2060. }
  2061. switch(Get_Definition().Type) {
  2062. case VEHICLE_TYPE_BIKE:
  2063. case VEHICLE_TYPE_CAR:
  2064. case VEHICLE_TYPE_TANK:
  2065. {
  2066. VehiclePhysClass * p_obj = Peek_Vehicle_Phys();
  2067. WWASSERT(p_obj != NULL);
  2068. Vector3 sc_position;
  2069. Quaternion q;
  2070. Vector3 vel;
  2071. Vector3 ang_vel;
  2072. //packet.Get_Vector3(pos);
  2073. //packet.Get_Quaternion(q);
  2074. //packet.Get_Vector3(vel);
  2075. //packet.Get_Vector3(ang_vel);
  2076. packet.Get(sc_position.X, BITPACK_WORLD_POSITION_X);
  2077. packet.Get(sc_position.Y, BITPACK_WORLD_POSITION_Y);
  2078. packet.Get(sc_position.Z, BITPACK_WORLD_POSITION_Z);
  2079. packet.Get(q.X, BITPACK_VEHICLE_QUATERNION);
  2080. packet.Get(q.Y, BITPACK_VEHICLE_QUATERNION);
  2081. packet.Get(q.Z, BITPACK_VEHICLE_QUATERNION);
  2082. packet.Get(q.W, BITPACK_VEHICLE_QUATERNION);
  2083. packet.Get(vel.X, BITPACK_VEHICLE_VELOCITY);
  2084. packet.Get(vel.Y, BITPACK_VEHICLE_VELOCITY);
  2085. packet.Get(vel.Z, BITPACK_VEHICLE_VELOCITY);
  2086. packet.Get(ang_vel.X, BITPACK_VEHICLE_ANGULAR_VELOCITY);
  2087. packet.Get(ang_vel.Y, BITPACK_VEHICLE_ANGULAR_VELOCITY);
  2088. packet.Get(ang_vel.Z, BITPACK_VEHICLE_ANGULAR_VELOCITY);
  2089. switch (CombatManager::Get_Client_Interpolation_Model()) {
  2090. case CLIENT_INTERPOLATION_SERVER_AUTHORITATIVE:
  2091. case CLIENT_INTERPOLATION_SERVER_AUTHORITATIVE_WITH_BLENDING:
  2092. {
  2093. //Matrix3D tm(q, sc_position);
  2094. //p_obj->Set_Transform(tm);
  2095. //p_obj->Set_Velocity(vel);
  2096. //p_obj->Set_Angular_Velocity(ang_vel);
  2097. Vector3 current_position;
  2098. Get_Position(&current_position);
  2099. Vector3 pos_error = current_position - sc_position;
  2100. if (pos_error.Length() > 2.0f) {
  2101. Matrix3D tm(q, sc_position);
  2102. p_obj->Set_Transform(tm);
  2103. p_obj->Set_Velocity(vel);
  2104. p_obj->Set_Angular_Velocity(ang_vel);
  2105. }
  2106. break;
  2107. }
  2108. case CLIENT_INTERPOLATION_PATHFIND: {
  2109. SoldierGameObj * p_driver = Seats[DRIVER_SEAT];
  2110. if (p_driver != NULL && p_driver->Get_Control_Owner() == CombatManager::Get_My_Id()) {
  2111. Matrix3D tm(q, sc_position);
  2112. p_obj->Set_Transform(tm);
  2113. p_obj->Set_Velocity(vel);
  2114. p_obj->Set_Angular_Velocity(ang_vel);
  2115. } else {
  2116. ActionParamsStruct parameters;
  2117. parameters.Priority = 1;
  2118. parameters.MoveLocation = sc_position;
  2119. parameters.MoveArrivedDistance = 0.1f;
  2120. WWASSERT(Get_Action() != NULL);
  2121. Get_Action()->Goto(parameters);
  2122. }
  2123. break;
  2124. }
  2125. default:
  2126. WWASSERT(0);
  2127. }
  2128. break;
  2129. }
  2130. case VEHICLE_TYPE_FLYING:
  2131. break;
  2132. case VEHICLE_TYPE_TURRET:
  2133. break;
  2134. default:
  2135. WWASSERT( 0 );
  2136. break;
  2137. }
  2138. SmartGameObj::Import_State_Sc(packet);
  2139. WWASSERT(packet.Is_Flushed());
  2140. }
  2141. */