MissionPhysObject.cpp 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821
  1. #include "MissionPhysObject.h"
  2. #define FADEOUT_TIME 1.0f
  3. MissionPhysObject::MissionPhysObject() : solid(_FL_),
  4. broken(_FL_)
  5. // ,lines(_FL_)
  6. {
  7. HP = 0.0f;
  8. flags = f_needCalcBox | f_isShowTips;
  9. hideTime = 3.0f;
  10. chacheIndex = -1;
  11. visAbb.min = visAbb.max = 0.0f;
  12. colAbb.min = colAbb.max = 0.0f;
  13. aiColider = null;
  14. }
  15. MissionPhysObject::~MissionPhysObject()
  16. {
  17. Release();
  18. if(EditMode_IsOn() && pattern.Validate())
  19. {
  20. pattern.Ptr()->regObjects.Del(this);
  21. }
  22. if(aiColider)
  23. {
  24. aiColider->Release();
  25. }
  26. }
  27. //Инициализировать объект
  28. bool MissionPhysObject::Create(MOPReader & reader)
  29. {
  30. DamageReceiver::Create(reader);
  31. if(!aiColider)
  32. {
  33. aiColider = QTCreateObject(MG_AI_COLLISION, _FL_);
  34. }
  35. aiColider->Activate(false);
  36. ResetFlag(flags, f_isFadeProcess | f_isShock);
  37. ReadParameters(reader);
  38. brokeEvent.Init(reader);
  39. if(reader.Bool())
  40. {
  41. SetFlag(flags, f_drawDebugBoxes);
  42. }else{
  43. ResetFlag(flags, f_drawDebugBoxes);
  44. }
  45. if(!pattern.Validate()) return false;
  46. UpdatePatternData();
  47. FindVisibleABB(solid, visAbb);
  48. Show(IsShow());
  49. return true;
  50. }
  51. //Пересоздать объект
  52. void MissionPhysObject::Restart()
  53. {
  54. Activate(false);
  55. SetShow(false);
  56. DelUpdate(&MissionPhysObject::HideTimer);
  57. DelUpdate(&MissionPhysObject::Fader);
  58. Release();
  59. if(pattern.Validate())
  60. {
  61. pattern.Ptr()->regObjects.Del(this);
  62. }
  63. HP = 0.0f;
  64. flags = f_needCalcBox;
  65. hideTime = 3.0f;
  66. pattern.Reset();
  67. chacheIndex = -1;
  68. visAbb.min = visAbb.max = 0.0f;
  69. colAbb.min = colAbb.max = 0.0f;
  70. waterLevel.Reset();
  71. ReCreate();
  72. }
  73. //Инициализировать объект
  74. bool MissionPhysObject::EditMode_Create(MOPReader & reader)
  75. {
  76. SetUpdate(&MissionPhysObject::EditMode_Draw, ML_GEOMETRY2);
  77. return EditMode_Update(reader);
  78. }
  79. //Обновить параметры
  80. bool MissionPhysObject::EditMode_Update(MOPReader & reader)
  81. {
  82. //Удаляем всё
  83. Release();
  84. pattern.Reset();
  85. //Читаем и заводим заново
  86. ReadParameters(reader);
  87. UpdatePatternPointer();
  88. UpdatePatternData();
  89. return true;
  90. }
  91. //Получить размеры описывающего ящика
  92. void MissionPhysObject::EditMode_GetSelectBox(Vector & min, Vector & max)
  93. {
  94. ABB abb; abb.min = abb.max = 0.0f;
  95. bool isAdd = false;
  96. for(long i = 0; i < solid; i++)
  97. {
  98. Part & part = solid[i];
  99. if(part.model)
  100. {
  101. const GMXBoundBox & bbx = part.model->GetBound();
  102. if(isAdd)
  103. {
  104. min.Min(min, bbx.vMin);
  105. max.Min(max, bbx.vMax);
  106. }else{
  107. min = bbx.vMin;
  108. max = bbx.vMax;
  109. isAdd = true;
  110. }
  111. /*
  112. MissionPhysObjPattern::PatternObject & po = pattern.Ptr()->objects[part.descIndex];
  113. const GMXBoundBox & bbx = part.model->GetLocalBound();
  114. Box::FindABBforOBB(po.mtx, bbx.vMin, bbx.vMax, abb.min, abb.max, isAdd);
  115. isAdd = true;
  116. */
  117. }
  118. }
  119. /*
  120. min = abb.min;
  121. max = abb.max;
  122. */
  123. }
  124. //Получить бокс, описывающий объект в локальных координатах
  125. void MissionPhysObject::GetBox(Vector & min, Vector & max)
  126. {
  127. min = visAbb.min;
  128. max = visAbb.max;
  129. }
  130. //Показать/скрыть объект
  131. void MissionPhysObject::Show(bool isShow)
  132. {
  133. if(EditMode_IsOn())
  134. {
  135. MissionObject::Show(isShow);
  136. return;
  137. }
  138. MissionObject::Show(isShow);
  139. SetShow(isShow);
  140. LogicDebug(isShow ? "Show" : "Hide");
  141. Activate(IsActive());
  142. ShowTips();
  143. }
  144. //Устоновить состояние подписки
  145. void MissionPhysObject::SetShow(bool isShow)
  146. {
  147. if(!pattern.Validate()) return;
  148. if(isShow)
  149. {
  150. DelUpdate(&MissionPhysObject::Draw);
  151. SetUpdate(&MissionPhysObject::Draw, pattern.Ptr()->level);
  152. if(CheckFlag(flags, f_isFadeProcess | f_isCheckLod))
  153. {
  154. SetUpdate(&MissionPhysObject::Draw, pattern.Ptr()->level + (ML_ALPHA1 + 1) - ML_GEOMETRY1);
  155. }
  156. if(pattern.Ptr()->shadowCast)
  157. {
  158. Registry(MG_SHADOWCAST, (MOF_EVENT)&MissionPhysObject::ShadowInfo, pattern.Ptr()->level);
  159. }else{
  160. Unregistry(MG_SHADOWCAST);
  161. }
  162. if(pattern.Ptr()->shadowReceive)
  163. {
  164. Unregistry(MG_SHADOWDONTRECEIVE);
  165. Registry(MG_SHADOWRECEIVE, (MOF_EVENT)&MissionPhysObject::ShadowDraw, pattern.Ptr()->level);
  166. }else{
  167. Registry(MG_SHADOWDONTRECEIVE, (MOF_EVENT)&MissionPhysObject::ShadowDraw, pattern.Ptr()->level);
  168. Unregistry(MG_SHADOWRECEIVE);
  169. }
  170. if(pattern.Ptr()->seaReflection)
  171. {
  172. Registry(MG_SEAREFLECTION, (MOF_EVENT)&MissionPhysObject::SeaReflection, pattern.Ptr()->level);
  173. }else{
  174. Unregistry(MG_SEAREFLECTION);
  175. }
  176. }else{
  177. DelUpdate(&MissionPhysObject::Draw);
  178. Unregistry(MG_SHADOWCAST);
  179. Unregistry(MG_SHADOWRECEIVE);
  180. Unregistry(MG_SEAREFLECTION);
  181. Unregistry(MG_SHADOWDONTRECEIVE);
  182. }
  183. }
  184. //Обновить состояние типсов
  185. void MissionPhysObject::ShowTips()
  186. {
  187. bool currentShow = IsShow() && CheckFlag(flags, f_isShowTips);
  188. SetTipsState(solid, currentShow && !CheckFlag(flags, f_isBroken));
  189. SetTipsState(broken, currentShow && CheckFlag(flags, f_isBroken));
  190. }
  191. //Установить состояние подсказки
  192. void MissionPhysObject::SetTipsState(array<Part> & obj, bool isActive)
  193. {
  194. for(long i = 0; i < obj; i++)
  195. {
  196. Part & part = obj[i];
  197. if(part.tip)
  198. {
  199. if(isActive)
  200. {
  201. part.tip->Activate(true);
  202. }else{
  203. part.tip->Activate(false);
  204. }
  205. }
  206. }
  207. }
  208. //Активировать/деактивировать объект
  209. void MissionPhysObject::Activate(bool isActive)
  210. {
  211. if(EditMode_IsOn())
  212. {
  213. MissionObject::Activate(isActive);
  214. return;
  215. }
  216. MissionObject::Activate(isActive);
  217. if(!IsShow())
  218. {
  219. isActive = false;
  220. }
  221. finder->Activate(isActive);
  222. if(isActive && !CheckFlag(flags, f_isBroken) && (solid > 0) && pattern.Validate() && !EditMode_IsOn())
  223. {
  224. MissionPhysObjPattern::PatternObject & po = pattern.Ptr()->objects[solid[0].descIndex];
  225. aiColider->SetBox(po.min, po.max);
  226. aiColider->SetMatrix(solid[0].mtx);
  227. aiColider->Activate(true);
  228. }else{
  229. aiColider->Activate(false);
  230. }
  231. if(isActive)
  232. {
  233. SetUpdate(&MissionPhysObject::Update, ML_EXECUTE1);
  234. ApplyDamage(0.0f, dt_unknown);
  235. if(!CheckFlag(flags, f_isBroken))
  236. {
  237. MakePhysics(solid);
  238. }else{
  239. MakePhysics(broken);
  240. if(!CheckFlag(flags, f_isFadeProcess))
  241. {
  242. SetUpdate(&MissionPhysObject::HideTimer, ML_EXECUTE1);
  243. }else{
  244. if(!CheckFlag(flags, f_isDead))
  245. {
  246. SetUpdate(&MissionPhysObject::Fader, ML_EXECUTE1);
  247. }else{
  248. hideTime = 1.0f;
  249. Fader(0.0f, 0);
  250. }
  251. }
  252. }
  253. LogicDebug("Activate");
  254. }else{
  255. DelUpdate(&MissionPhysObject::Update);
  256. DelUpdate(&MissionPhysObject::HideTimer);
  257. DelUpdate(&MissionPhysObject::Fader);
  258. ReleasePhysics(solid);
  259. ReleasePhysics(broken);
  260. LogicDebug("Deactivate");
  261. }
  262. }
  263. //Получить матрицу объекта
  264. Matrix & MissionPhysObject::GetMatrix(Matrix & mtx)
  265. {
  266. return mtx.SetIdentity();
  267. }
  268. //Обработчик команд для объекта
  269. void MissionPhysObject::Command(const char * id, dword numParams, const char ** params)
  270. {
  271. if(string::IsEqual(id, "broke"))
  272. {
  273. if(!CheckFlag(flags, f_isBroken))
  274. {
  275. Broke();
  276. if(numParams >= 2)
  277. {
  278. MOSafePointer mo;
  279. if(FindObject(ConstString(params[0]), mo))
  280. {
  281. Matrix mtx;
  282. mo.SPtr()->GetMatrix(mtx);
  283. char * endPtr = null;
  284. float forceMin = (float)strtod(params[1], &endPtr);
  285. float forceMax = forceMin*1.2f;
  286. if(numParams >= 3)
  287. {
  288. forceMax = (float)strtod(params[2], &endPtr);;
  289. }
  290. for(long i = 0; i < broken; i++)
  291. {
  292. Part & part = broken[i];
  293. Vector dir = part.mtx.pos - mtx.pos;
  294. dir.Normalize();
  295. Vector vforce(RRnd(forceMin, forceMax), RRnd(forceMin, forceMax), RRnd(forceMin, forceMax));
  296. part.impDir += dir*vforce;
  297. part.count++;
  298. }
  299. LogicDebug("Command: broke, take force [%f, %f] from object %s", forceMin, forceMax, params[0]);
  300. }else{
  301. LogicDebugError("Command: broke, object %s not found", params[0]);
  302. }
  303. }else{
  304. LogicDebug("Command: broke, without parameters");
  305. }
  306. ApplyImpulses(dt_unknown);
  307. }
  308. }else
  309. if(string::IsEqual(id, "shock"))
  310. {
  311. if(numParams >= 3)
  312. {
  313. char * endPtr = null;
  314. float time = (float)strtod(params[0], &endPtr);
  315. float force = (float)strtod(params[1], &endPtr);
  316. float period = (float)strtod(params[1], &endPtr);
  317. shockTime = Clampf(time, 0.0f, 10.0f);
  318. shockForce = Clampf(force, 0.0f, 100.0f);
  319. shockWaitTimeMid = Clampf(period, 0.0f, 10.0f);
  320. LogicDebug("Command: shock (set:%f, def:%f) (set:%f, def:%f) (set:%f, def:%f)", shockTime, time, shockForce, force, shockWaitTimeMid, period);
  321. if(shockTime > 1e-5f)
  322. {
  323. kShockTime = 1.0f/shockTime;
  324. }else{
  325. kShockTime = 0.0f;
  326. }
  327. shockTime = 0.0f;
  328. shockWaitTime = 0.0f;
  329. SetFlag(flags, f_isShock);
  330. }else{
  331. LogicDebugError("Command: shock, not enough parameters: shock 1:time 2:force");
  332. }
  333. }else
  334. if(string::IsEqual(id, "hackhideon"))
  335. {
  336. LogicDebug("Command: hackhideon !!!");
  337. SetFlag(flags, f_hackHide);
  338. }else
  339. if(string::IsEqual(id, "hackhideoff"))
  340. {
  341. LogicDebug("Command: hackhideoff");
  342. ResetFlag(flags, f_hackHide);
  343. }else{
  344. LogicDebugError("Unknown command: %s", id);
  345. }
  346. }
  347. //Обновление позиций
  348. void _cdecl MissionPhysObject::Update(float dltTime, long level)
  349. {
  350. array<Part> & parts = CheckFlag(flags, f_isBroken) ? broken : solid;
  351. if(CheckFlag(flags, f_isShock) && dltTime > 0.0f)
  352. {
  353. if(shockWaitTime <= 1e-10f)
  354. {
  355. float force = shockForce*RRnd(1.0f - shockTime, 1.0f);
  356. for(long i = 0; i < parts; i++)
  357. {
  358. Part & part = parts[i];
  359. if(part.physObject)
  360. {
  361. part.physObject->ApplyImpulse(Vector(0.0f, force, 0.0f), Vector(0.0f));
  362. }
  363. }
  364. shockWaitTime = shockWaitTimeMid*RRnd(0.8f, 1.2f);
  365. }else{
  366. shockWaitTime -= dltTime;
  367. }
  368. shockTime -= dltTime;
  369. if(shockTime <= 0.0f)
  370. {
  371. ResetFlag(flags, f_isShock);
  372. }
  373. }
  374. float wlevel = IWaterLevel::GetWaterLevel(Mission(), waterLevel);
  375. for(long i = 0; i < parts; i++)
  376. {
  377. Part & part = parts[i];
  378. if(part.sndMatMask)
  379. {
  380. part.sndCooldown -= dltTime;
  381. if(part.sndCooldown <= 0.0f)
  382. {
  383. part.sndMatMask = 0;
  384. }
  385. }
  386. if(part.physObject)
  387. {
  388. MissionPhysObjPattern::PatternObject & po = pattern.Ptr()->objects[part.descIndex];
  389. const Vector & com = po.centerOfMass;
  390. Vector curPos = part.mtx*com;
  391. part.physObject->GetTransform(part.mtx);
  392. if(part.model)
  393. {
  394. part.model->SetTransform(part.mtx);
  395. long mtlId = (long)part.physObject->GetContactReport();
  396. if(mtlId > pmtlid_air && mtlId <= pmtlid_fabrics)
  397. {
  398. //Попали в звучащий материал
  399. dword mask = 1 << mtlId;
  400. if((part.sndMatMask & mask) == 0)
  401. {
  402. part.sndCooldown = 0.8f;
  403. part.sndMatMask |= mask;
  404. Vector pos = part.physObject->GetContactPoint();
  405. float force = part.physObject->GetContactForce().GetLength2();
  406. pattern.Ptr()->PlayCollisionEffect(mtlId, pos, force);
  407. }
  408. }
  409. }
  410. if(part.tip)
  411. {
  412. part.tip->SetPos(part.mtx*po.tipPosition);
  413. }
  414. Vector newPos = part.mtx*com;
  415. if(newPos.y <= wlevel && curPos.y > wlevel)
  416. {
  417. WaterDrop(part, curPos, newPos, wlevel);
  418. }
  419. if(part.mtx.pos.y < -800.0f)
  420. {
  421. if(part.physObject)
  422. {
  423. part.physObject->Release();
  424. part.physObject = null;
  425. }
  426. if(part.model)
  427. {
  428. part.model->Release();
  429. part.model = null;
  430. }
  431. ResetFlag(part.flags, f_part_isCollision);
  432. }
  433. }
  434. }
  435. FindCollidersABB(parts, colAbb);
  436. finder->SetBox(colAbb.min, colAbb.max);
  437. FindVisibleABB(parts, visAbb);
  438. if(!CheckFlag(flags, f_isBroken) && solid > 0)
  439. {
  440. aiColider->SetMatrix(solid[0].mtx);
  441. }
  442. }
  443. //Отиграть эффект падения в воду
  444. void MissionPhysObject::WaterDrop(Part & part, Vector & pos1, Vector & pos2, float level)
  445. {
  446. const ConstString & prtName = pattern.Ptr()->objects[part.descIndex].waterParticle;
  447. const ConstString & sndName = pattern.Ptr()->objects[part.descIndex].waterSound;
  448. float k = (pos1.y - pos2.y);
  449. if(k > 0.0f)
  450. {
  451. k = (level - pos2.y)/k;
  452. }else{
  453. k = 0.0f;
  454. }
  455. Matrix mtx(0.0f, Rnd(2.0f*PI), 0.0f, pos2.x + (pos1.x - pos2.x)*k, level, pos2.z + (pos1.z - pos2.z)*k);
  456. if(prtName.NotEmpty())
  457. {
  458. Particles().CreateParticleSystemEx2(prtName.c_str(), mtx, true, _FL_);
  459. }
  460. if(sndName.NotEmpty())
  461. {
  462. Sound().Create3D(sndName, mtx.pos, _FL_);
  463. }
  464. }
  465. //Нарисовать модельку
  466. void _cdecl MissionPhysObject::Draw(float dltTime, long level)
  467. {
  468. Assert(pattern.Validate());
  469. array<Part> & parts = CheckFlag(flags, f_isBroken) ? broken : solid;
  470. bool haveSomeModels = false;
  471. Vector cam = Render().GetView().GetCamPos();
  472. Color userColor(0.0f, 0.0f, 0.0f, 1.0f);
  473. for(long i = 0; i < parts; i++)
  474. {
  475. Part & part = parts[i];
  476. if(part.model)
  477. {
  478. haveSomeModels = true;
  479. if(level == pattern.Ptr()->level)
  480. {
  481. if(CheckFlag(flags, f_isCheckLod))
  482. {
  483. const GMXBoundBox & gbb = part.model->GetBound();
  484. Vector center = (gbb.vMin + gbb.vMax)*0.5f;
  485. float k = (center - cam).GetAttenuation2(pattern.Ptr()->hideDistanceMin2, pattern.Ptr()->hideDistanceK2);
  486. if(k < 1.0f)
  487. {
  488. k *= part.alpha;
  489. if(k < 1e-5f)
  490. {
  491. continue;
  492. }
  493. userColor.a = k;
  494. part.model->SetUserColor(userColor);
  495. }else{
  496. userColor.a = part.alpha;
  497. part.model->SetUserColor(userColor);
  498. }
  499. }
  500. if(part.model->GetUserColor().a > 0.9999f)
  501. {
  502. if(!CheckFlag(flags, f_hackHide))
  503. {
  504. part.model->Draw();
  505. }
  506. ResetFlag(part.flags, f_part_isDrawInAlphaLevel);
  507. }else{
  508. SetFlag(part.flags, f_part_isDrawInAlphaLevel);
  509. }
  510. }else{
  511. if(CheckFlag(part.flags, f_part_isDrawInAlphaLevel))
  512. {
  513. if(!CheckFlag(flags, f_hackHide))
  514. {
  515. part.model->Draw();
  516. }
  517. }
  518. }
  519. }
  520. }
  521. if(level == pattern.Ptr()->level)
  522. {
  523. if(CheckFlag(flags, f_drawDebugBoxes))
  524. {
  525. for(long i = 0; i < parts; i++)
  526. {
  527. Part & part = parts[i];
  528. MissionPhysObjPattern::PatternObject & po = pattern.Ptr()->objects[part.descIndex];
  529. for(long j = 0; j < po.boxes; j++)
  530. {
  531. MissionPhysObjPattern::CollisionBox & box = po.boxes[j];
  532. Matrix mtx(box.mtx, part.mtx);
  533. Render().DrawBox(-box.size05, box.size05, mtx);
  534. }
  535. for(long j = 0; j < po.capsules; j++)
  536. {
  537. MissionPhysObjPattern::CollisionCapsule & cap = po.capsules[j];
  538. Matrix mtx(cap.mtx, part.mtx);
  539. Render().DrawBox(-cap.size05, cap.size05, mtx);
  540. }
  541. }
  542. }
  543. if(!haveSomeModels && !CheckFlag(flags, f_drawDebugBoxes))
  544. {
  545. SetShow(false);
  546. }
  547. }
  548. /*
  549. // Render().DrawBox(visAbb.min, visAbb.max);
  550. Render().DrawBox(colAbb.min, colAbb.max);
  551. static Vector poly[4] =
  552. {
  553. Vector(-1.0f, 0.0f, 0.5f),
  554. Vector(1.2f, 0.0f, 1.0f),
  555. Vector(0.7f, 0.0f, -1.0f),
  556. Vector(-1.0f, 0.0f, -1.0f)
  557. };
  558. static Vector p[4];
  559. static float cnt = 0.0f;
  560. cnt += dltTime;
  561. //#define CAMERA
  562. #ifndef CAMERA
  563. if(api->DebugKeyState(VK_SPACE)) cnt = 0.0f;
  564. if(cnt > 0.5f)
  565. {
  566. cnt = 0.0f;
  567. Matrix mtx(Vector().Rand(Vector(-PI), Vector(PI)), Vector().Rand(Vector(-4.0f), Vector(4.0f)));
  568. #else
  569. if(api->DebugKeyState(VK_SPACE))
  570. {
  571. Matrix mtx = Render().GetView();
  572. mtx.Inverse();
  573. #endif
  574. for(long i = 0; i < 4; i++)
  575. {
  576. p[i] = poly[i]*mtx;
  577. }
  578. }
  579. Vector size05(1.0f, 0.6f, 0.4f);
  580. bool bs = OverlapsBoxSphere(Matrix(), size05, p[0], 1.0f);
  581. bool bl = OverlapsBoxLine(Matrix(), size05, p[0], p[1]);
  582. bool bp = OverlapsBoxPoly(Matrix(), size05, p);
  583. //Render().DrawSphere(p[0], 1.0f, bs ? 0xffff0000 : 0xff0000ff);
  584. Render().DrawLine(p[0], bl ? 0xffff0000 : 0xff0000ff, p[1], bl ? 0xffff0000 : 0xff0000ff);
  585. // Render().DrawPolygon(p, 4, bp ? 0xffff0000 : 0xff0000ff);
  586. Render().DrawBox(-size05, size05);
  587. //*/
  588. /* Render().FlushBufferedLines();
  589. for(dword i = 0; i < lines.Size(); i++)
  590. {
  591. DLine & dl = lines[i];
  592. //Render().DrawBufferedLine(dl.from, dl.cf, dl.to, dl.ct);
  593. Render().DrawLine(dl.from, dl.cf, dl.to, dl.ct);
  594. }
  595. Render().FlushBufferedLines();
  596. */
  597. // Render().DrawBox(aiColider.GetBoxCenter() - aiColider.GetBoxSize05(), aiColider.GetBoxCenter() + aiColider.GetBoxSize05(), aiColider.GetMatrix());
  598. }
  599. //Нарисовать модельку
  600. void _cdecl MissionPhysObject::EditMode_Draw(float dltTime, long level)
  601. {
  602. /*
  603. Vector vmin, vmax;
  604. EditMode_GetSelectBox(vmin, vmax);
  605. Render().DrawBox(vmin, vmax, Matrix(), 0xffff0000);
  606. */
  607. if(!EditMode_IsVisible())
  608. {
  609. return;
  610. }
  611. if(!IsShow())
  612. {
  613. return;
  614. }
  615. if(!pattern.Validate())
  616. {
  617. UpdatePatternPointer();
  618. if(!pattern.Validate())
  619. {
  620. return;
  621. }
  622. UpdatePatternData();
  623. }
  624. for(long i = 0; i < solid; i++)
  625. {
  626. if(solid[i].model)
  627. {
  628. solid[i].model->Draw();
  629. }
  630. }
  631. }
  632. //Нарисовать модельку для тени
  633. void _cdecl MissionPhysObject::ShadowInfo(const char * group, MissionObject * sender)
  634. {
  635. if(CheckFlag(flags, f_hackHide))
  636. {
  637. return;
  638. }
  639. ((MissionShadowCaster *)sender)->AddObject(this, &MissionPhysObject::ShadowDraw, visAbb.min, visAbb.max);
  640. }
  641. //Нарисовать модельку для тени
  642. void _cdecl MissionPhysObject::ShadowDraw(const char * group, MissionObject * sender)
  643. {
  644. if(CheckFlag(flags, f_hackHide))
  645. {
  646. return;
  647. }
  648. Assert(pattern.Validate());
  649. array<Part> & parts = CheckFlag(flags, f_isBroken) ? broken : solid;
  650. for(long i = 0; i < parts; i++)
  651. {
  652. Part & part = parts[i];
  653. if(part.model)
  654. {
  655. part.model->Draw();
  656. }
  657. }
  658. }
  659. //Нарисовать отражёную модельку
  660. void _cdecl MissionPhysObject::SeaReflection(const char * group, MissionObject * sender)
  661. {
  662. ShadowDraw(null, null);
  663. }
  664. //Отсчёт исчезновения
  665. void _cdecl MissionPhysObject::HideTimer(float dltTime, long level)
  666. {
  667. brokeEvent.Activate(Mission());
  668. hideTime -= dltTime;
  669. if(hideTime > 0.0f) return;
  670. hideTime = 0.0f;
  671. DelUpdate(&MissionPhysObject::HideTimer);
  672. SetUpdate(&MissionPhysObject::Fader, ML_EXECUTE1);
  673. SetFlag(flags, f_isFadeProcess);
  674. LogicDebug("Start fade process");
  675. SetShow(IsShow());
  676. }
  677. //Процесс исчезновения, или другого окончания объекта
  678. void _cdecl MissionPhysObject::Fader(float dltTime, long level)
  679. {
  680. hideTime += dltTime*(1.0f/FADEOUT_TIME);
  681. if(hideTime > 1.0f)
  682. {
  683. SetFlag(flags, f_isDead);
  684. hideTime = 1.0f;
  685. DelUpdate(&MissionPhysObject::Fader);
  686. }
  687. bool isSomeVisible = false;
  688. Color userColor(0.0f, 0.0f, 0.0f, 1.0f - hideTime);
  689. for(long i = 0; i < broken; i++)
  690. {
  691. Part & part = broken[i];
  692. MissionPhysObjPattern::PatternObject & po = pattern.Ptr()->objects[part.descIndex];
  693. if(po.fe == MissionPhysObjPattern::fe_hide)
  694. {
  695. if(!CheckFlag(flags, f_isDead))
  696. {
  697. if(part.model)
  698. {
  699. part.model->SetUserColor(userColor);
  700. part.alpha = userColor.a;
  701. isSomeVisible = true;
  702. }
  703. }else{
  704. LogicDebug("Object's part %i is faded", i);
  705. if(part.model)
  706. {
  707. part.model->Release();
  708. part.model = null;
  709. }
  710. if(part.physObject)
  711. {
  712. part.physObject->Release();
  713. part.physObject = null;
  714. }
  715. ResetFlag(part.flags, f_part_isCollision);
  716. }
  717. }else
  718. if(po.fe == MissionPhysObjPattern::fe_static)
  719. {
  720. isSomeVisible = true;
  721. if(CheckFlag(flags, f_isDead))
  722. {
  723. LogicDebug("Object's part %i is modify to static", i);
  724. if(part.physObject)
  725. {
  726. part.physObject->Release();
  727. part.physObject = null;
  728. }
  729. }
  730. }else{
  731. isSomeVisible = true;
  732. }
  733. }
  734. if(!isSomeVisible)
  735. {
  736. LogicDebug("No more visible parts, object off");
  737. Show(false);
  738. }
  739. }
  740. //Воздействовать на объект сферой
  741. bool MissionPhysObject::Attack(MissionObject * obj, dword source, float hp, const Vector & center, float radius)
  742. {
  743. if(!pattern.Validate()) return false;
  744. if(!IsActive()) return false;
  745. DamageType dtype = DetectType(source);
  746. if(dtype == dt_pickup)
  747. {
  748. if(!pattern.Ptr()->isPickup || CheckFlag(flags, f_isBroken) || solid > 1)
  749. {
  750. return false;
  751. }
  752. }
  753. hp = ModifyHp(dtype, hp);
  754. array<Part> & parts = CheckFlag(flags, f_isBroken) ? broken : solid;
  755. bool isHit = false;
  756. float dmg = 0.0f;
  757. for(long i = 0; i < parts; i++)
  758. {
  759. Part & part = parts[i];
  760. part.impPos = 0.0f;
  761. part.impDir = 0.0f;
  762. part.count = 0;
  763. part.effectsPos = 0.0f;
  764. part.effectsCount = 0;
  765. if(!CheckFlag(part.flags, f_part_isCollision))
  766. {
  767. continue;
  768. }
  769. MissionPhysObjPattern::PatternObject & po = pattern.Ptr()->objects[part.descIndex];
  770. bool isApplyDmg = false;
  771. for(long j = 0; j < po.boxes; j++)
  772. {
  773. MissionPhysObjPattern::CollisionBox & box = po.boxes[j];
  774. Matrix mtx(box.mtx, part.mtx);
  775. if(Box::OverlapsBoxSphere(mtx, box.size05, center, radius))
  776. {
  777. isApplyDmg = true;
  778. ApplyImpulse(part, mtx, center, radius, hp, dtype);
  779. }
  780. }
  781. for(long j = 0; j < po.capsules; j++)
  782. {
  783. MissionPhysObjPattern::CollisionCapsule & cap = po.capsules[j];
  784. Matrix mtx(cap.mtx, part.mtx);
  785. if(Box::OverlapsBoxSphere(mtx, cap.size05, center, radius))
  786. {
  787. isApplyDmg = true;
  788. ApplyImpulse(part, mtx, center, radius, hp, dtype);
  789. }
  790. }
  791. if(isApplyDmg)
  792. {
  793. dmg += hp;
  794. isHit = true;
  795. }
  796. }
  797. ApplyDamage(dmg, dtype);
  798. ApplyImpulses(dtype);
  799. return isHit;
  800. }
  801. __forceinline void MissionPhysObject::ApplyImpulse(Part & p, const Matrix & boxTransform, const Vector & spherePos, float sphereRadius, float hp, DamageType dtype)
  802. {
  803. //Вычисляем направление импульса
  804. Vector dir = boxTransform.pos - spherePos;
  805. float dist = dir.Normalize();
  806. if(dist < 0.01f)
  807. {
  808. dir.Rand();
  809. }
  810. //Слегка подбросим вверх
  811. dir.y += RRnd(0.1f, 0.2f);
  812. dir.Normalize();
  813. //Считаем коэфициент затухания в зависимости от удалённости от центра
  814. float k;
  815. if(sphereRadius > 1e-10f)
  816. {
  817. k = 1.0f - Clampf(dist/sphereRadius);
  818. k = powf(k, 0.3f);
  819. }else{
  820. k = 0.0f;
  821. }
  822. //Прикладываем импульс к части
  823. ApplyImpulseResult(p, boxTransform.MulVertexByInverse(spherePos), dir, k, hp, dtype, spherePos);
  824. }
  825. //Воздействовать на объект линией
  826. bool MissionPhysObject::Attack(MissionObject * obj, dword source, float hp, const Vector & from, const Vector & to)
  827. {
  828. // lines.Add(DLine(from, 0xff00ffff, to, 0xff0000ff));
  829. if(!pattern.Validate()) return false;
  830. if(!IsActive()) return false;
  831. DamageType dtype = DetectType(source);
  832. hp = ModifyHp(dtype, hp);
  833. array<Part> & parts = CheckFlag(flags, f_isBroken) ? broken : solid;
  834. bool isHit = false;
  835. float dmg = 0.0f;
  836. for(long i = 0; i < parts; i++)
  837. {
  838. Part & part = parts[i];
  839. part.impPos = 0.0f;
  840. part.impDir = 0.0f;
  841. part.count = 0;
  842. part.effectsPos = 0.0f;
  843. part.effectsCount = 0;
  844. if(!CheckFlag(part.flags, f_part_isCollision))
  845. {
  846. continue;
  847. }
  848. MissionPhysObjPattern::PatternObject & po = pattern.Ptr()->objects[part.descIndex];
  849. bool isApplyDmg = false;
  850. for(long j = 0; j < po.boxes; j++)
  851. {
  852. MissionPhysObjPattern::CollisionBox & box = po.boxes[j];
  853. Matrix mtx(box.mtx, part.mtx);
  854. if(Box::OverlapsBoxLine(mtx, box.size05, from, to))
  855. {
  856. isApplyDmg = true;
  857. ApplyImpulse(part, mtx, from, to , hp, dtype);
  858. }
  859. }
  860. for(long j = 0; j < po.capsules; j++)
  861. {
  862. MissionPhysObjPattern::CollisionCapsule & cap = po.capsules[j];
  863. Matrix mtx(cap.mtx, part.mtx);
  864. if(Box::OverlapsBoxLine(mtx, cap.size05, from, to))
  865. {
  866. isApplyDmg = true;
  867. ApplyImpulse(part, mtx, from, to , hp, dtype);
  868. }
  869. }
  870. if(isApplyDmg)
  871. {
  872. dmg += hp;
  873. isHit = true;
  874. }
  875. }
  876. ApplyDamage(dmg, dtype);
  877. ApplyImpulses(dtype);
  878. return isHit;
  879. }
  880. void MissionPhysObject::ApplyImpulse(Part & p, const Matrix & boxTransform, const Vector & from, const Vector & to, float hp, DamageType dtype)
  881. {
  882. if(dtype == dt_sword)
  883. {
  884. Vector middle = (from + to)*0.5f;
  885. Vector dir = boxTransform.pos - middle;
  886. float dist = dir.Normalize();
  887. float k;
  888. if(dist < 1e-10f)
  889. {
  890. dir.Rand();
  891. k = 1.0f;
  892. }else{
  893. k = 1.0f/(1.0f + dist);
  894. }
  895. ApplyImpulseResult(p, boxTransform.MulVertexByInverse(middle), dir, k, hp, dtype, middle);
  896. return;
  897. }
  898. Vector dir = to - from;
  899. float distDir = dir.Normalize();
  900. ApplyImpulseResult(p, boxTransform.MulVertexByInverse(from), dir, 1.0f, hp, dtype, from);
  901. }
  902. //Воздействовать на объект выпуклым чехырёхугольником
  903. bool MissionPhysObject::Attack(MissionObject * obj, dword source, float hp, const Vector vrt[4])
  904. {
  905. if(!pattern.Validate()) return false;
  906. if(!IsActive()) return false;
  907. DamageType dtype = DetectType(source);
  908. array<Part> & parts = CheckFlag(flags, f_isBroken) ? broken : solid;
  909. bool isHit = false;
  910. float dmg = 0.0f;
  911. for(long i = 0; i < parts; i++)
  912. {
  913. Part & part = parts[i];
  914. part.impPos = 0.0f;
  915. part.impDir = 0.0f;
  916. part.count = 0;
  917. part.effectsPos = 0.0f;
  918. part.effectsCount = 0;
  919. if(!CheckFlag(part.flags, f_part_isCollision))
  920. {
  921. continue;
  922. }
  923. MissionPhysObjPattern::PatternObject & po = pattern.Ptr()->objects[part.descIndex];
  924. bool isApplyDmg = false;
  925. for(long j = 0; j < po.boxes; j++)
  926. {
  927. MissionPhysObjPattern::CollisionBox & box = po.boxes[j];
  928. Matrix mtx(box.mtx, part.mtx);
  929. if(Box::OverlapsBoxPoly(mtx, box.size05, vrt))
  930. {
  931. isApplyDmg = true;
  932. ApplyImpulse(part, mtx, vrt, hp, dtype);
  933. }
  934. }
  935. for(long j = 0; j < po.capsules; j++)
  936. {
  937. MissionPhysObjPattern::CollisionCapsule & cap = po.capsules[j];
  938. Matrix mtx(cap.mtx, part.mtx);
  939. if(Box::OverlapsBoxPoly(mtx, cap.size05, vrt))
  940. {
  941. isApplyDmg = true;
  942. ApplyImpulse(part, mtx, vrt, hp, dtype);
  943. }
  944. }
  945. if(isApplyDmg)
  946. {
  947. dmg += ModifyHp(dtype, hp);
  948. isHit = true;
  949. }
  950. }
  951. ApplyDamage(dmg, dtype);
  952. ApplyImpulses(dtype);
  953. return isHit;
  954. }
  955. __forceinline void MissionPhysObject::ApplyImpulse(Part & p, const Matrix & boxTransform, const Vector vrt[4], float hp, DamageType dtype)
  956. {
  957. //Направление силы в направлении тьраектории движения
  958. Vector dir = vrt[1] - vrt[2] + vrt[0] - vrt[3];
  959. dir.Normalize();
  960. //Позиция определяется серединой оружия на передней кромке
  961. Vector pos = (vrt[0] + vrt[1])*0.5f;
  962. //На силу влияет только наносимые повреждения
  963. ApplyImpulseResult(p, boxTransform.MulVertexByInverse(pos), dir, 1.0f, hp, dtype, pos);
  964. }
  965. //Применить импульс к части зная параметры импульса
  966. __forceinline void MissionPhysObject::ApplyImpulseResult(Part & p, const Vector & impulsePos, const Vector & impulseDir, float kAttenuation, float hp, DamageType dtype, const Vector & effectsPos)
  967. {
  968. //Добавляем эффект
  969. p.effectsPos += effectsPos;
  970. p.effectsCount++;
  971. //Модификатор импульса в зависимости от источника демеджа
  972. /*
  973. if(hp <= 1e-10f || kAttenuation <= 1e-10f)
  974. {
  975. return;
  976. }*/
  977. float force = 1.0f;
  978. switch(dtype)
  979. {
  980. case dt_sword:
  981. force = 1.0f;
  982. break;
  983. case dt_bomb:
  984. //force = 6.0f;
  985. force = 0.1f*Clampf(fabsf(hp)*RRnd(0.9f, 1.1f), 1.0f, 500.0f);
  986. break;
  987. case dt_bullet:
  988. force = 5.0f;
  989. break;
  990. case dt_cannon:
  991. //force = 6.0f;
  992. force = 0.1f*Clampf(fabsf(hp)*RRnd(0.9f, 1.1f), 1.0f, 500.0f);
  993. break;
  994. case dt_flame:
  995. force = 1.0f;
  996. break;
  997. case dt_shooter:
  998. force = 6.0f;
  999. break;
  1000. case dt_unknown:
  1001. force = 1.0f;
  1002. break;
  1003. case dt_pickup:
  1004. force = 0.0f;
  1005. break;
  1006. default:
  1007. force = 0.0f;
  1008. }
  1009. /* //Вычислим силу в зависимости от наносимого повреждения
  1010. force = Clampf(fabsf(hp)*RRnd(0.9f, 1.1f), 1.0f, 500.0f);
  1011. //Добавляем импульс к объекту
  1012. p.impPos += impulsePos;
  1013. p.impDir += impulseDir*(force*modifier*kAttenuation); //Уберём ослабление
  1014. */
  1015. //Вычислим силу в зависимости от наносимого повреждения
  1016. p.impPos += impulsePos;
  1017. if (dtype == dt_bullet)
  1018. {
  1019. Vector dir;
  1020. dir.Rand(0.0f,1.0f);
  1021. dir.y = fabs(dir.y);
  1022. p.impDir += dir*(force*RRnd(0.9f, 1.1f));
  1023. }
  1024. else
  1025. {
  1026. p.impDir += impulseDir*(force*RRnd(0.9f, 1.1f));
  1027. }
  1028. p.count++;
  1029. // lines.Add(DLine(p.effectsPos, 0xff00ffff, p.effectsPos + impulseDir, 0xffff00ff));
  1030. }
  1031. //Применить импульсы
  1032. __forceinline void MissionPhysObject::ApplyImpulses(DamageType type)
  1033. {
  1034. if(type == dt_check)
  1035. {
  1036. return;
  1037. }
  1038. array<Part> & parts = CheckFlag(flags, f_isBroken) ? broken : solid;
  1039. for(long i = 0; i < parts; i++)
  1040. {
  1041. Part & part = parts[i];
  1042. if(part.count && part.physObject)
  1043. {
  1044. float kNorm = 1.0f/float(part.count);
  1045. //Усредняем позицию и импульс по количеству приложенных
  1046. part.impPos *= kNorm;
  1047. part.impDir *= kNorm;
  1048. //Ограничиваем приложенный импульс предельным значением
  1049. float impulseScalar = part.impDir.Normalize();
  1050. static const float velocityMax = 100.0f; // m/s
  1051. const float impulseMax = velocityMax;
  1052. if(impulseScalar > impulseMax)
  1053. {
  1054. impulseScalar = impulseMax;
  1055. }
  1056. if(type != dt_unknown)
  1057. {
  1058. float mass = part.physObject->GetMass();
  1059. part.impDir *= impulseScalar/Clampf(mass, 0.5f, 10.0f);
  1060. }else{
  1061. part.impDir *= impulseScalar;
  1062. }
  1063. //Прикладываем полученный сумарный импульс к физическому объекту
  1064. part.physObject->ApplyImpulse(part.impDir, part.impPos);
  1065. }
  1066. //Проигрываем спецэффект в точки приложения импульса
  1067. if(part.effectsCount)
  1068. {
  1069. ConstString particlesName;
  1070. ConstString soundName;
  1071. switch(type)
  1072. {
  1073. case dt_sword:
  1074. particlesName = pattern.Ptr()->hitSword.particles;
  1075. soundName = pattern.Ptr()->hitSword.sound;
  1076. break;
  1077. case dt_bomb:
  1078. particlesName = pattern.Ptr()->hitBomb.particles;
  1079. soundName = pattern.Ptr()->hitBomb.sound;
  1080. break;
  1081. case dt_bullet:
  1082. particlesName = pattern.Ptr()->hitBullet.particles;
  1083. soundName = pattern.Ptr()->hitBullet.sound;
  1084. break;
  1085. case dt_cannon:
  1086. particlesName = pattern.Ptr()->hitCannon.particles;
  1087. soundName = pattern.Ptr()->hitCannon.sound;
  1088. break;
  1089. case dt_flame:
  1090. particlesName = pattern.Ptr()->hitFlame.particles;
  1091. soundName = pattern.Ptr()->hitFlame.sound;
  1092. break;
  1093. case dt_shooter:
  1094. particlesName = pattern.Ptr()->hitShooter.particles;
  1095. soundName = pattern.Ptr()->hitShooter.sound;
  1096. break;
  1097. }
  1098. part.effectsPos *= 1.0f/part.effectsCount;
  1099. // lines.Add(DLine(part.effectsPos, 0xffffffff, part.effectsPos - part.impDir, 0xffff0000));
  1100. if(particlesName.NotEmpty())
  1101. {
  1102. Matrix mtx;
  1103. if(!mtx.BuildOrient(-part.impDir, Vector(0.0f, 1.0f, 0.0f)))
  1104. {
  1105. mtx.SetIdentity();
  1106. }
  1107. mtx.pos = part.effectsPos;
  1108. Particles().CreateParticleSystemEx2(particlesName.c_str(), mtx, true, _FL_);
  1109. }
  1110. if(soundName.NotEmpty())
  1111. {
  1112. Sound().Create3D(soundName, part.effectsPos, _FL_);
  1113. }
  1114. }
  1115. part.impPos = 0.0f;
  1116. part.impDir = 0.0f;
  1117. part.effectsPos = 0.0f;
  1118. part.count = 0;
  1119. }
  1120. }
  1121. //Умирает
  1122. bool MissionPhysObject::IsDie()
  1123. {
  1124. return HP <= 0.0f;
  1125. }
  1126. //Мёртв
  1127. bool MissionPhysObject::IsDead()
  1128. {
  1129. return CheckFlag(flags, f_isDead);
  1130. }
  1131. //Проверить на пересечение отрезка и ящиков описывающих объекты
  1132. bool MissionPhysObject::OverlapLine(const Vector & v1, const Vector & v2, float skin)
  1133. {
  1134. array<Part> & parts = !CheckFlag(flags, f_isBroken) ? solid : broken;
  1135. for(long i = 0; i < parts; i++)
  1136. {
  1137. Part & part = parts[i];
  1138. if(!part.physObject || !CheckFlag(part.flags, f_part_isCollision))
  1139. {
  1140. continue;
  1141. }
  1142. Vector abbMin, abbMax;
  1143. bool isAdd = false;
  1144. MissionPhysObjPattern::PatternObject & po = pattern.Ptr()->objects[part.descIndex];
  1145. for(long j = 0; j < po.boxes; j++)
  1146. {
  1147. MissionPhysObjPattern::CollisionBox & box = po.boxes[j];
  1148. Box::FindABBforOBB(box.mtx, -box.size05, box.size05, abbMin, abbMax, isAdd);
  1149. isAdd = true;
  1150. }
  1151. for(long j = 0; j < po.capsules; j++)
  1152. {
  1153. MissionPhysObjPattern::CollisionCapsule & cap = po.capsules[j];
  1154. Box::FindABBforOBB(cap.mtx, -cap.size05, cap.size05, abbMin, abbMax, isAdd);
  1155. isAdd = true;
  1156. }
  1157. if(isAdd)
  1158. {
  1159. Vector size05 = (abbMax - abbMin)*0.5f;
  1160. Vector center = (abbMin + abbMax)*0.5f;
  1161. Matrix mtx(part.mtx);
  1162. mtx.pos = mtx*center;
  1163. if(Box::OverlapsBoxLine(mtx, size05 + Vector(skin), v1, v2))
  1164. {
  1165. return true;
  1166. }
  1167. }
  1168. }
  1169. return false;
  1170. };
  1171. //Патерн удаляется
  1172. void MissionPhysObject::DeletePattern()
  1173. {
  1174. Release();
  1175. pattern.Reset();
  1176. }
  1177. //Прочитать параметры
  1178. void MissionPhysObject::ReadParameters(MOPReader & reader)
  1179. {
  1180. Vector pos = reader.Position();
  1181. Vector ang = reader.Angles();
  1182. initMtx.Build(ang, pos);
  1183. patternName = reader.String();
  1184. pattern.Reset();
  1185. MissionObject::Show(reader.Bool());
  1186. MissionObject::Activate(reader.Bool());
  1187. UpdatePatternPointer();
  1188. if(reader.Bool())
  1189. {
  1190. reader.Float();
  1191. if(pattern.Validate())
  1192. {
  1193. HP = pattern.Ptr()->hp;
  1194. }else{
  1195. HP = 1.0f;
  1196. }
  1197. }else{
  1198. HP = reader.Float();
  1199. }
  1200. if(pattern.Validate())
  1201. {
  1202. hideTime = pattern.Ptr()->activeTime;
  1203. }
  1204. }
  1205. //Получить поинтер на патерн
  1206. void MissionPhysObject::UpdatePatternPointer()
  1207. {
  1208. if(patternName.IsEmpty())
  1209. {
  1210. pattern.Reset();
  1211. return;
  1212. }
  1213. FindObject(patternName, pattern.GetSPObject());
  1214. if(pattern.Validate())
  1215. {
  1216. MO_IS_IF_NOT(id_MissionPhysObjPattern, "MissionPhysObjPattern", pattern.Ptr())
  1217. {
  1218. pattern.Reset();
  1219. if(!EditMode_IsOn())
  1220. {
  1221. LogicDebug("Incorrect physic object pattern type (object: \"%s\", type: \"%s\")", patternName.c_str(), pattern.Ptr()->GetObjectType());
  1222. }
  1223. }
  1224. }else{
  1225. if(!EditMode_IsOn())
  1226. {
  1227. LogicDebug("Physic object pattern \"%s\" not found", patternName.c_str());
  1228. }
  1229. }
  1230. if(pattern.Validate())
  1231. {
  1232. if(!EditMode_IsOn())
  1233. {
  1234. pattern.Ptr()->CacheModels();
  1235. }else{
  1236. for(long i = 0; i < pattern.Ptr()->regObjects; i++)
  1237. {
  1238. if(pattern.Ptr()->regObjects[i] == this)
  1239. {
  1240. return;
  1241. }
  1242. }
  1243. pattern.Ptr()->regObjects.Add(this);
  1244. }
  1245. }
  1246. }
  1247. //Перестроить данные с паттерна
  1248. void MissionPhysObject::UpdatePatternData()
  1249. {
  1250. Release();
  1251. if(!pattern.Validate()) return;
  1252. if(pattern.Ptr()->hideDistanceMin2 > 1e-10f)
  1253. {
  1254. SetFlag(flags, f_isCheckLod);
  1255. }else{
  1256. ResetFlag(flags, f_isCheckLod);
  1257. }
  1258. bool isDynamicLighting = pattern.Ptr()->dynamicLighting;
  1259. bool isShadowReceive = pattern.Ptr()->shadowReceive;
  1260. ITipsManager * tmanager = ITipsManager::GetManager(&Mission());
  1261. solid.AddElements(pattern.Ptr()->soldObjectsCount);
  1262. for(long i = 0; i < solid; i++)
  1263. {
  1264. Part & psolid = solid[i];
  1265. psolid.mtx.EqMultiplyFast(pattern.Ptr()->objects[i].mtx, initMtx);
  1266. psolid.model = Geometry().CreateScene(pattern.Ptr()->objects[i].modelName.c_str(), &Animation(), &Particles(), &Sound(), _FL_);
  1267. if(psolid.model)
  1268. {
  1269. psolid.model->SetTransform(psolid.mtx);
  1270. psolid.model->SetDynamicLightState(isDynamicLighting);
  1271. psolid.model->SetShadowReceiveState(isShadowReceive);
  1272. }
  1273. psolid.physObject = null;
  1274. psolid.impPos = 0.0f;
  1275. psolid.impDir = 0.0f;
  1276. psolid.count = 0;
  1277. psolid.effectsPos = 0.0f;
  1278. psolid.effectsCount = 0;
  1279. psolid.alpha = 1.0f;
  1280. psolid.curAlpha = 1.0f;
  1281. psolid.descIndex = i;
  1282. psolid.tip = null;
  1283. psolid.flags = f_part_isCollision;
  1284. psolid.sndMatMask = -1;
  1285. psolid.sndCooldown = 3.0f;
  1286. if(!EditMode_IsOn())
  1287. {
  1288. if(tmanager && pattern.Ptr()->objects[i].tipId.NotEmpty())
  1289. {
  1290. psolid.tip = tmanager->CreateTip(pattern.Ptr()->objects[i].tipId, psolid.mtx.pos, this);
  1291. if(psolid.tip)
  1292. {
  1293. psolid.tip->Activate(false);
  1294. }
  1295. }
  1296. }
  1297. }
  1298. broken.AddElements(pattern.Ptr()->objects - pattern.Ptr()->soldObjectsCount);
  1299. for(long i = 0; i < broken; i++)
  1300. {
  1301. Part & pbroken = broken[i];
  1302. long index = pattern.Ptr()->soldObjectsCount + i;
  1303. pbroken.mtx.EqMultiplyFast(pattern.Ptr()->objects[index].mtx, initMtx);
  1304. pbroken.model = Geometry().CreateScene(pattern.Ptr()->objects[index].modelName.c_str(), &Animation(), &Particles(), &Sound(), _FL_);
  1305. if(pbroken.model)
  1306. {
  1307. pbroken.model->SetTransform(pbroken.mtx);
  1308. pbroken.model->SetDynamicLightState(isDynamicLighting);
  1309. pbroken.model->SetShadowReceiveState(isShadowReceive);
  1310. }
  1311. pbroken.physObject = null;
  1312. pbroken.impPos = 0.0f;
  1313. pbroken.impDir = 0.0f;
  1314. pbroken.count = 0;
  1315. pbroken.effectsPos = 0.0f;
  1316. pbroken.effectsCount = 0;
  1317. pbroken.alpha = 1.0f;
  1318. pbroken.curAlpha = 1.0f;
  1319. pbroken.descIndex = index;
  1320. pbroken.tip = null;
  1321. pbroken.flags = 0;
  1322. pbroken.sndMatMask = -1;
  1323. pbroken.sndCooldown = 3.0f;
  1324. if(!EditMode_IsOn())
  1325. {
  1326. if(tmanager && pattern.Ptr()->objects[index].tipId.NotEmpty())
  1327. {
  1328. pbroken.tip = tmanager->CreateTip(pattern.Ptr()->objects[index].tipId, pbroken.mtx.pos, this);
  1329. if(pbroken.tip)
  1330. {
  1331. pbroken.tip->Activate(false);
  1332. }
  1333. }
  1334. }
  1335. }
  1336. }
  1337. //Создать физические объекты
  1338. void MissionPhysObject::MakePhysics(array<Part> & obj)
  1339. {
  1340. if(EditMode_IsOn()) return;
  1341. if(!pattern.Validate()) return;
  1342. for(long i = 0; i < obj; i++)
  1343. {
  1344. Part & part = obj[i];
  1345. if(part.physObject) continue;
  1346. MissionPhysObjPattern::PatternObject & po = pattern.Ptr()->objects[part.descIndex];
  1347. if((po.boxes || po.capsules) && !po.isStatic)
  1348. {
  1349. IPhysCombined * cb = Physics().CreateCombined(_FL_, part.mtx, true);
  1350. part.physObject = cb;
  1351. for(long j = 0; j < po.boxes; j++)
  1352. {
  1353. cb->AddBox(po.boxes[j].size05*2.0f, po.boxes[j].mtx);
  1354. }
  1355. for(long j = 0; j < po.capsules; j++)
  1356. {
  1357. cb->AddCapsule(po.capsules[j].radius, po.capsules[j].height - po.capsules[j].radius*2.0f, po.capsules[j].mtx);
  1358. }
  1359. cb->Build();
  1360. cb->SetGroup(phys_physobjects);
  1361. cb->SetMaterial(pattern.Ptr()->materialId);
  1362. }
  1363. }
  1364. }
  1365. //Удалить физические объекты
  1366. void MissionPhysObject::ReleasePhysics(array<Part> & obj)
  1367. {
  1368. for(long i = 0; i < obj; i++)
  1369. {
  1370. Part & part = obj[i];
  1371. if(part.physObject)
  1372. {
  1373. part.physObject->Release();
  1374. part.physObject = null;
  1375. }
  1376. }
  1377. }
  1378. //Удалить модельки
  1379. void MissionPhysObject::ReleaseModels(array<Part> & obj)
  1380. {
  1381. for(long i = 0; i < obj; i++)
  1382. {
  1383. Part & part = obj[i];
  1384. if(part.model)
  1385. {
  1386. part.model->Release();
  1387. part.model = null;
  1388. }
  1389. if(part.tip)
  1390. {
  1391. part.tip->Release();
  1392. part.tip = null;
  1393. }
  1394. }
  1395. }
  1396. //Удалить данные
  1397. void MissionPhysObject::Release()
  1398. {
  1399. ReleasePhysics(solid);
  1400. ReleasePhysics(broken);
  1401. ReleaseModels(solid);
  1402. ReleaseModels(broken);
  1403. solid.DelAll();
  1404. broken.DelAll();
  1405. }
  1406. //Сломать
  1407. void MissionPhysObject::Broke()
  1408. {
  1409. LogicDebug("Broke");
  1410. //Обновляем состояние подписки
  1411. SetShow(IsShow());
  1412. //Ломаем
  1413. SetFlag(flags, f_isBroken);
  1414. //Больше нет препятствий для АИ
  1415. aiColider->Activate(false);
  1416. //Проигрываем звуки и партиклы
  1417. for(long i = 0; i < solid; i++)
  1418. {
  1419. Part & sld = solid[i];
  1420. ResetFlag(sld.flags, f_part_isCollision);
  1421. MissionPhysObjPattern::PatternObject & po = pattern.Ptr()->objects[sld.descIndex];
  1422. //Запускаем эффекты
  1423. for(long j = 0; j < po.particles; j++)
  1424. {
  1425. IParticleSystem * ps = Particles().CreateParticleSystemEx(po.particles[j].name.c_str(), _FL_);
  1426. if(ps)
  1427. {
  1428. ps->SetTransform(sld.mtx*po.particles[j].mtx);
  1429. ps->AutoDelete(true);
  1430. }
  1431. }
  1432. for(long j = 0; j < po.sounds; j++)
  1433. {
  1434. Sound().Create3D(po.sounds[j].name, sld.mtx*po.sounds[j].mtx.pos, _FL_);
  1435. }
  1436. //Взрываем если есть что
  1437. if(po.receiveBoom.NotEmpty())
  1438. {
  1439. MOSafePointer mo;
  1440. if(FindObject(po.receiveBoom, mo))
  1441. {
  1442. Vector p = sld.mtx*po.centerOfMass;
  1443. const char * params[3];
  1444. char pos[3][32];
  1445. for(long i = 0; i < 3; i++)
  1446. {
  1447. crt_snprintf(pos[i], sizeof(pos[i]), "%f", p.v[i]);
  1448. params[i] = pos[i];
  1449. }
  1450. mo.Ptr()->Command("boom", 3, params);
  1451. }
  1452. }
  1453. //Бросаем бонусы
  1454. if(po.bonusesTable.NotEmpty())
  1455. {
  1456. BonusesManager::CreateBonus(Mission(), sld.mtx.pos, po.bonusesTable);
  1457. }
  1458. }
  1459. //Удаляем физические объекты
  1460. ReleasePhysics(solid);
  1461. for(long i = 0; i < broken; i++)
  1462. {
  1463. //Проставляем матрицу для будующего сломаного объекта
  1464. MissionPhysObjPattern::PatternObject & po = pattern.Ptr()->objects[broken[i].descIndex];
  1465. long j = 0;
  1466. Part & brn = broken[i];
  1467. SetFlag(brn.flags, f_part_isCollision);
  1468. if(j < solid)
  1469. {
  1470. brn.mtx = solid[j].mtx;
  1471. brn.impPos = solid[j].impPos;
  1472. brn.impDir = solid[j].impDir;
  1473. brn.count = solid[j].count;
  1474. brn.effectsPos = 0.0f;
  1475. brn.effectsCount = 0;
  1476. }else{
  1477. brn.impPos = 0.0f;
  1478. brn.impDir = 0.0f;
  1479. brn.count = 1;
  1480. brn.effectsPos = 0.0f;
  1481. brn.effectsCount = 0;
  1482. }
  1483. if(po.isAddImpulse)
  1484. {
  1485. brn.impPos += brn.mtx*po.impulsePosition;
  1486. brn.impDir += brn.mtx.MulNormal(po.impulse)*10.0f;
  1487. brn.count += 1;
  1488. }
  1489. }
  1490. if(broken > 0)
  1491. {
  1492. broken[0].effectsPos = solid[0].effectsPos;
  1493. broken[0].effectsCount = solid[0].effectsCount;
  1494. }
  1495. //Создаём физические объекты
  1496. MakePhysics(broken);
  1497. //Установим таймер на исчезновение
  1498. SetUpdate(&MissionPhysObject::HideTimer, ML_EXECUTE1);
  1499. //Обновляем состояние типсов
  1500. ShowTips();
  1501. //
  1502. Update(0.0f, 0);
  1503. }
  1504. //Модифицировать наносимое повреждение с учётом коэфициентов демеджа
  1505. float MissionPhysObject::ModifyHp(DamageType type, float hp)
  1506. {
  1507. float modifier = pattern.Ptr()->damageModifier.Multiplier(type, 0.0f);
  1508. return hp*modifier;
  1509. }
  1510. //Нанести повреждение
  1511. bool MissionPhysObject::ApplyDamage(float damage, DamageType type)
  1512. {
  1513. if(!pattern.Validate()) return false;
  1514. if(type == dt_check) return false;
  1515. if(CheckFlag(flags, f_isBroken)) return false;
  1516. HP -= damage;
  1517. if(HP > 0.0f) return false;
  1518. Broke();
  1519. return true;
  1520. }
  1521. //Найти видимый ABB
  1522. void MissionPhysObject::FindVisibleABB(array<Part> & obj, ABB & abb)
  1523. {
  1524. abb.min = abb.max = 0.0f;
  1525. bool isAdd = false;
  1526. for(long i = 0; i < obj; i++)
  1527. {
  1528. Part & part = obj[i];
  1529. if(part.model)
  1530. {
  1531. const GMXBoundBox & bbx = part.model->GetLocalBound();
  1532. Box::FindABBforOBB(part.model->GetTransform(), bbx.vMin, bbx.vMax, abb.min, abb.max, isAdd);
  1533. isAdd = true;
  1534. }
  1535. }
  1536. }
  1537. //Найти для физики ABB
  1538. void MissionPhysObject::FindCollidersABB(array<Part> & obj, ABB & abb)
  1539. {
  1540. abb.min = abb.max = 0.0f;
  1541. bool isAdd = false;
  1542. for(long i = 0; i < obj; i++)
  1543. {
  1544. Part & part = obj[i];
  1545. MissionPhysObjPattern::PatternObject & po = pattern.Ptr()->objects[part.descIndex];
  1546. for(long j = 0; j < po.boxes; j++)
  1547. {
  1548. MissionPhysObjPattern::CollisionBox & box = po.boxes[j];
  1549. Box::FindABBforOBB(box.mtx*part.mtx, -box.size05, box.size05, abb.min, abb.max, isAdd);
  1550. isAdd = true;
  1551. }
  1552. for(long j = 0; j < po.capsules; j++)
  1553. {
  1554. MissionPhysObjPattern::CollisionCapsule & cap = po.capsules[j];
  1555. Box::FindABBforOBB(cap.mtx*part.mtx, -cap.size05, cap.size05, abb.min, abb.max, isAdd);
  1556. isAdd = true;
  1557. }
  1558. }
  1559. }
  1560. __forceinline MissionPhysObject::DamageType MissionPhysObject::DetectType(dword source)
  1561. {
  1562. switch(source)
  1563. {
  1564. case DamageReceiver::ds_sword:
  1565. return dt_sword;
  1566. case DamageReceiver::ds_bomb:
  1567. return dt_bomb;
  1568. case DamageReceiver::ds_bullet:
  1569. return dt_bullet;
  1570. case DamageReceiver::ds_cannon:
  1571. return dt_cannon;
  1572. case DamageReceiver::ds_flame:
  1573. return dt_flame;
  1574. case DamageReceiver::ds_shooter:
  1575. return dt_shooter;
  1576. case DamageReceiver::ds_unknown:
  1577. return dt_unknown;
  1578. case DamageReceiver::ds_check:
  1579. return dt_check;
  1580. case ds_pickup:
  1581. return dt_pickup;
  1582. }
  1583. return dt_unknown;
  1584. }
  1585. //Переместить целую часть в новую позицию
  1586. bool MissionPhysObject::SetPartMatrix(long partIndex, const Matrix & mtx)
  1587. {
  1588. if(IsActive() || CheckFlag(flags, f_isBroken)) return false;
  1589. if(partIndex >= solid || partIndex < 0) return false;
  1590. Part & part = solid[partIndex];
  1591. part.mtx = mtx;
  1592. if(part.model)
  1593. {
  1594. part.model->SetTransform(part.mtx);
  1595. }
  1596. if(part.tip)
  1597. {
  1598. part.tip->SetPos(part.mtx.pos);
  1599. }
  1600. float wlevel = IWaterLevel::GetWaterLevel(Mission(), waterLevel);
  1601. if(part.mtx.pos.y < wlevel)
  1602. {
  1603. const Vector & com = pattern.Ptr()->objects[part.descIndex].centerOfMass;
  1604. Vector newPos = part.mtx*com;
  1605. Vector curPos = Vector(newPos.x, wlevel - 1.0f, newPos.z);
  1606. WaterDrop(part, curPos, newPos, wlevel);
  1607. }
  1608. Update(0.0f, 0);
  1609. return true;
  1610. }
  1611. //Получить текущую матрицу целой части объекта
  1612. bool MissionPhysObject::GetPartMatrix(long partIndex, Matrix & mtx)
  1613. {
  1614. if(CheckFlag(flags, f_isBroken)) return false;
  1615. if(partIndex >= solid || partIndex < 0) return false;
  1616. Part & part = solid[partIndex];
  1617. mtx = part.mtx;
  1618. return true;
  1619. }
  1620. //Получить пивот целой части
  1621. bool MissionPhysObject::GetPartPivot(long partIndex, Matrix & mtx)
  1622. {
  1623. if(CheckFlag(flags, f_isBroken)) return false;
  1624. if(partIndex >= solid || partIndex < 0) return false;
  1625. Part & part = solid[partIndex];
  1626. MissionPhysObjPattern::PatternObject & po = pattern.Ptr()->objects[part.descIndex];
  1627. mtx = po.mtx;
  1628. return true;
  1629. }
  1630. //Применить импульс в мировых координатах
  1631. void MissionPhysObject::Impulse(const Vector & dir)
  1632. {
  1633. array<Part> & part = CheckFlag(flags, f_isBroken) ? broken : solid;
  1634. for(long i = 0; i < part; i++)
  1635. {
  1636. part[i].impDir = dir;
  1637. part[i].impPos = 0.0f;
  1638. part[i].count = 1;
  1639. }
  1640. ApplyImpulses(dt_unknown);
  1641. }
  1642. //Сломать объект
  1643. void MissionPhysObject::BrokeObject()
  1644. {
  1645. if(!CheckFlag(flags, f_isBroken))
  1646. {
  1647. Broke();
  1648. }
  1649. }
  1650. //Показать-скрыть подсказки
  1651. void MissionPhysObject::ShowTips(bool isShow)
  1652. {
  1653. if(CheckFlag(flags, f_isShowTips) != isShow)
  1654. {
  1655. if(isShow)
  1656. {
  1657. SetFlag(flags, f_isShowTips);
  1658. }else{
  1659. ResetFlag(flags, f_isShowTips);
  1660. }
  1661. ShowTips();
  1662. }
  1663. }
  1664. //Найти индекс локатора по имени, -1 если нет такого
  1665. long MissionPhysObject::GetLocatorIndexByName(const ConstString & name)
  1666. {
  1667. if(!pattern.Validate() || name.IsEmpty())
  1668. {
  1669. return -1;
  1670. }
  1671. MissionPhysObjPattern::Locator * locs = pattern.Ptr()->locators.GetBuffer();
  1672. long size = pattern.Ptr()->locators;
  1673. for(long i = 0; i < size; i++)
  1674. {
  1675. if(locs[i].name == name)
  1676. {
  1677. return i;
  1678. }
  1679. }
  1680. return -1;
  1681. }
  1682. //Получить позицию локатора по индексу
  1683. bool MissionPhysObject::GetLocator(long index, Matrix & mtx)
  1684. {
  1685. if(!pattern.Validate() || index < 0 || index >= pattern.Ptr()->locators)
  1686. {
  1687. return false;
  1688. }
  1689. MissionPhysObjPattern::Locator & loc = pattern.Ptr()->locators[index];
  1690. Part & part = (loc.objectIndex < pattern.Ptr()->soldObjectsCount) ? solid[loc.objectIndex] : broken[loc.objectIndex - pattern.Ptr()->soldObjectsCount];
  1691. mtx.EqMultiply(loc.mtx, part.mtx);
  1692. return true;
  1693. }
  1694. __forceinline void MissionPhysObject::ResetFlag(dword & f, dword value)
  1695. {
  1696. f &= ~value;
  1697. }
  1698. __forceinline void MissionPhysObject::SetFlag(dword & f, dword value)
  1699. {
  1700. f |= value;
  1701. }
  1702. __forceinline bool MissionPhysObject::CheckFlag(dword f, dword value)
  1703. {
  1704. return (f & value) != 0;
  1705. }
  1706. //============================================================================================
  1707. //Параметры инициализации
  1708. //============================================================================================
  1709. MOP_BEGINLISTCG(MissionPhysObject, "Physic object", '1.00', 1, "Patterned physic object\n Commands:\n broke [objectId impulseForce [impulseForceMax]]\n shock time[0..10] force[0..100] period[0..10]\n hackhideon\n hackhideoff", "Physics")
  1710. MOP_POSITIONC("Position", Vector(0.0f), "Model position in world")
  1711. MOP_ANGLESC("Angles", Vector(0.0f), "Model orientation in world")
  1712. MOP_STRINGC("Pattern", "", "Name of physics object pattern")
  1713. MOP_BOOLC("Show", true, "Show or hide geometry in start mission time")
  1714. MOP_BOOLC("Active", true, "Show or hide geometry in start mission time")
  1715. MOP_BOOLC("Use pattern HP", true, "Object HP")
  1716. MOP_FLOATEXC("HP", 0.0f, 0.0f, 100000.0f, "Physic objects HP")
  1717. MOP_MISSIONTRIGGERG("Broke trigger", "")
  1718. MOP_BOOLC("Draw collision", false, "Draw collision primitives")
  1719. MOP_ENDLIST(MissionPhysObject)