PageRenderTime 42ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/src/object/task/taskshield.cpp

https://bitbucket.org/zielmicha/colobot
C++ | 557 lines | 418 code | 110 blank | 29 comment | 93 complexity | 7d83959848b017fe445842b03477b69a MD5 | raw file
  1. // * This file is part of the COLOBOT source code
  2. // * Copyright (C) 2001-2008, Daniel ROUX & EPSITEC SA, www.epsitec.ch
  3. // *
  4. // * This program is free software: you can redistribute it and/or modify
  5. // * it under the terms of the GNU General Public License as published by
  6. // * the Free Software Foundation, either version 3 of the License, or
  7. // * (at your option) any later version.
  8. // *
  9. // * This program is distributed in the hope that it will be useful,
  10. // * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // * GNU General Public License for more details.
  13. // *
  14. // * You should have received a copy of the GNU General Public License
  15. // * along with this program. If not, see http://www.gnu.org/licenses/.
  16. // taskshield.cpp
  17. #include "object/task/taskshield.h"
  18. #include "common/iman.h"
  19. #include "graphics/core/light.h"
  20. #include "graphics/engine/particle.h"
  21. #include "graphics/engine/lightman.h"
  22. #include "math/geometry.h"
  23. #include "object/brain.h"
  24. #include "physics/physics.h"
  25. #include <string.h>
  26. const float ENERGY_TIME = 20.0f; // maximum duration if full battery
  27. // Object's constructor.
  28. CTaskShield::CTaskShield(CInstanceManager* iMan, CObject* object)
  29. : CTask(iMan, object)
  30. {
  31. m_rankSphere = -1;
  32. m_soundChannel = -1;
  33. m_effectLight = -1;
  34. }
  35. // Object's destructor.
  36. CTaskShield::~CTaskShield()
  37. {
  38. Abort();
  39. }
  40. // Management of an event.
  41. bool CTaskShield::EventProcess(const Event &event)
  42. {
  43. CObject* power;
  44. Math::Matrix* mat;
  45. Math::Matrix matrix;
  46. Math::Vector pos, speed, goal, angle;
  47. Gfx::Color color;
  48. Math::Point dim;
  49. float energy;
  50. if ( m_engine->GetPause() ) return true;
  51. if ( event.type != EVENT_FRAME ) return true;
  52. if ( m_bError ) return false;
  53. m_progress += event.rTime*m_speed; // others advance
  54. m_time += event.rTime;
  55. m_delay -= event.rTime;
  56. mat = m_object->GetWorldMatrix(0);
  57. pos = Math::Vector(7.0f, 15.0f, 0.0f);
  58. pos = Math::Transform(*mat, pos); // sphere position
  59. m_shieldPos = pos;
  60. if ( m_rankSphere != -1 )
  61. {
  62. m_particle->SetPosition(m_rankSphere, m_shieldPos);
  63. dim.x = GetRadius();
  64. dim.y = dim.x;
  65. m_particle->SetDimension(m_rankSphere, dim);
  66. }
  67. if ( m_phase == TS_UP1 )
  68. {
  69. pos.x = 7.0f;
  70. pos.y = 4.5f+Math::Bounce(m_progress)*3.0f;
  71. pos.z = 0.0f;
  72. m_object->SetPosition(2, pos);
  73. }
  74. if ( m_phase == TS_UP2 )
  75. {
  76. pos.x = 0.0f;
  77. pos.y = 1.0f+Math::Bounce(m_progress)*3.0f;
  78. pos.z = 0.0f;
  79. m_object->SetPosition(3, pos);
  80. }
  81. if ( m_phase == TS_SHIELD )
  82. {
  83. energy = (1.0f/ENERGY_TIME)*event.rTime;
  84. energy *= GetRadius()/RADIUS_SHIELD_MAX;
  85. power = m_object->GetPower();
  86. if ( power != 0 )
  87. {
  88. power->SetEnergy(power->GetEnergy()-energy/power->GetCapacity());
  89. }
  90. m_energyUsed += energy;
  91. if ( m_soundChannel == -1 )
  92. {
  93. m_soundChannel = m_sound->Play(SOUND_SHIELD, m_shieldPos, 0.5f, 0.5f, true);
  94. m_sound->AddEnvelope(m_soundChannel, 1.0f, 1.0f, 2.0f, SOPER_CONTINUE);
  95. m_sound->AddEnvelope(m_soundChannel, 1.0f, 1.0f, 1.0f, SOPER_LOOP);
  96. }
  97. else
  98. {
  99. m_sound->Position(m_soundChannel, m_shieldPos);
  100. }
  101. pos = m_shieldPos;
  102. pos.y += GetRadius()*(2.0f+sinf(m_time*9.0f)*0.2f);
  103. if ( m_effectLight == -1 )
  104. {
  105. CreateLight(pos);
  106. }
  107. else
  108. {
  109. m_lightMan->SetLightPos(m_effectLight, pos);
  110. color.r = 0.0f+sinf(m_time*33.2f)*0.2f;
  111. color.g = 0.5f+sinf(m_time*20.0f)*0.5f;
  112. color.b = 0.5f+sinf(m_time*21.3f)*1.0f;
  113. color.a = 0.0f;
  114. m_lightMan->SetLightColor(m_effectLight, color);
  115. }
  116. if ( m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time )
  117. {
  118. m_lastParticle = m_time;
  119. pos = m_shieldPos;
  120. pos.x += (Math::Rand()-0.5f)*5.0f;
  121. pos.z += (Math::Rand()-0.5f)*5.0f;
  122. speed.x = (Math::Rand()-0.5f)*0.0f;
  123. speed.z = (Math::Rand()-0.5f)*0.0f;
  124. speed.y = Math::Rand()*15.0f;
  125. dim.x = Math::Rand()*6.0f+4.0f;
  126. dim.y = dim.x;
  127. m_particle->CreateParticle(pos, speed, dim, Gfx::PARTIBLUE, 1.0f, 0.0f, 0.0f);
  128. }
  129. if ( m_lastRay+m_engine->ParticleAdapt(0.05f) <= m_time )
  130. {
  131. m_lastRay = m_time;
  132. pos = m_shieldPos;
  133. dim.x = GetRadius()/20.0f;
  134. dim.y = dim.x;
  135. angle.x = (Math::Rand()-0.5f)*Math::PI*1.2f;
  136. angle.y = 0.0f;
  137. angle.z = (Math::Rand()-0.5f)*Math::PI*1.2f;
  138. Math::LoadRotationXZYMatrix(matrix, angle);
  139. goal = Math::Transform(matrix, Math::Vector(0.0f, GetRadius()-dim.x, 0.0f));
  140. goal += pos;
  141. m_particle->CreateRay(pos, goal, Gfx::PARTIRAY2, dim, 0.3f);
  142. }
  143. if ( m_lastIncrease+0.2f <= m_time )
  144. {
  145. m_lastIncrease = m_time;
  146. IncreaseShield();
  147. }
  148. }
  149. if ( m_phase == TS_SMOKE )
  150. {
  151. if ( m_soundChannel != -1 )
  152. {
  153. m_sound->FlushEnvelope(m_soundChannel);
  154. m_sound->AddEnvelope(m_soundChannel, 0.5f, 0.5f, 2.0f, SOPER_STOP);
  155. m_soundChannel = -1;
  156. }
  157. if ( m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time )
  158. {
  159. m_lastParticle = m_time;
  160. pos = m_shieldPos;
  161. pos.x += (Math::Rand()-0.5f)*5.0f;
  162. pos.z += (Math::Rand()-0.5f)*5.0f;
  163. speed.x = (Math::Rand()-0.5f)*3.0f;
  164. speed.z = (Math::Rand()-0.5f)*3.0f;
  165. speed.y = (Math::Rand()-0.5f)*3.0f;
  166. dim.x = Math::Rand()*1.5f+2.0f;
  167. dim.y = dim.x;
  168. m_particle->CreateParticle(pos, speed, dim, Gfx::PARTISMOKE3, 4.0f);
  169. }
  170. }
  171. if ( m_phase == TS_DOWN1 )
  172. {
  173. pos.x = 0.0f;
  174. pos.y = 1.0f+(1.0f-Math::Bounce(m_progress))*3.0f;
  175. pos.z = 0.0f;
  176. m_object->SetPosition(3, pos);
  177. }
  178. if ( m_phase == TS_DOWN2 )
  179. {
  180. pos.x = 7.0f;
  181. pos.y = 4.5f+(1.0f-Math::Bounce(m_progress))*3.0f;
  182. pos.z = 0.0f;
  183. m_object->SetPosition(2, pos);
  184. }
  185. return true;
  186. }
  187. // Deploys the shield.
  188. // The period is only useful with TSM_UP!
  189. Error CTaskShield::Start(TaskShieldMode mode, float delay)
  190. {
  191. CObject* power;
  192. Math::Matrix* mat;
  193. Math::Vector pos, iPos, oPos, speed;
  194. ObjectType type;
  195. float energy;
  196. if ( mode == TSM_DOWN )
  197. {
  198. return Stop();
  199. }
  200. if ( mode == TSM_UPDATE )
  201. {
  202. if ( m_object->GetSelect() )
  203. {
  204. m_brain->UpdateInterface();
  205. }
  206. return ERR_OK;
  207. }
  208. type = m_object->GetType();
  209. if ( type != OBJECT_MOBILErs ) return ERR_SHIELD_VEH;
  210. m_bError = true; // operation impossible
  211. if ( !m_physics->GetLand() ) return ERR_SHIELD_VEH;
  212. power = m_object->GetPower();
  213. if ( power == 0 ) return ERR_SHIELD_ENERGY;
  214. energy = power->GetEnergy();
  215. if ( energy == 0.0f ) return ERR_SHIELD_ENERGY;
  216. mat = m_object->GetWorldMatrix(0);
  217. pos = Math::Vector(7.0f, 15.0f, 0.0f);
  218. pos = Transform(*mat, pos); // sphere position
  219. m_shieldPos = pos;
  220. m_sound->Play(SOUND_PSHHH2, m_shieldPos, 1.0f, 0.7f);
  221. m_phase = TS_UP1;
  222. m_progress = 0.0f;
  223. m_speed = 1.0f/1.0f;
  224. m_time = 0.0f;
  225. m_delay = delay;
  226. m_lastParticle = 0.0f;
  227. m_lastRay = 0.0f;
  228. m_lastIncrease = 0.0f;
  229. m_energyUsed = 0.0f;
  230. m_bError = false; // ok
  231. if ( m_object->GetSelect() )
  232. {
  233. m_brain->UpdateInterface();
  234. }
  235. //? m_camera->StartCentering(m_object, Math::PI*0.85f, -Math::PI*0.15f, GetRadius()+40.0f, 3.0f);
  236. return ERR_OK;
  237. }
  238. // Returns the shield.
  239. Error CTaskShield::Stop()
  240. {
  241. float time;
  242. if ( m_phase == TS_SHIELD )
  243. {
  244. m_object->SetShieldRadius(0.0f);
  245. if ( m_rankSphere != -1 )
  246. {
  247. m_particle->SetPhase(m_rankSphere, Gfx::PARPHEND, 3.0f);
  248. m_rankSphere = -1;
  249. }
  250. if ( m_effectLight != -1 )
  251. {
  252. m_lightMan->DeleteLight(m_effectLight);
  253. m_effectLight = -1;
  254. }
  255. time = m_energyUsed*4.0f;
  256. if ( time < 1.0f ) time = 1.0f;
  257. if ( time > 4.0f ) time = 4.0f;
  258. m_phase = TS_SMOKE;
  259. m_speed = 1.0f/time;
  260. m_camera->StopCentering(m_object, 4.0f);
  261. if ( m_object->GetSelect() )
  262. {
  263. m_brain->UpdateInterface();
  264. }
  265. return ERR_CONTINUE;
  266. }
  267. return ERR_OK;
  268. }
  269. // Indicates whether the action is finished.
  270. Error CTaskShield::IsEnded()
  271. {
  272. CObject* power;
  273. Math::Vector pos, speed;
  274. Math::Point dim;
  275. float energy;
  276. if ( m_engine->GetPause() ) return ERR_CONTINUE;
  277. if ( m_bError ) return ERR_STOP;
  278. if ( m_phase == TS_SHIELD )
  279. {
  280. m_object->SetShieldRadius(GetRadius());
  281. power = m_object->GetPower();
  282. if ( power == 0 )
  283. {
  284. energy = 0.0f;
  285. }
  286. else
  287. {
  288. energy = power->GetEnergy();
  289. }
  290. if ( energy == 0.0f || m_delay <= 0.0f )
  291. {
  292. Stop();
  293. }
  294. return ERR_CONTINUE;
  295. }
  296. if ( m_progress < 1.0f ) return ERR_CONTINUE;
  297. m_progress = 0.0f;
  298. if ( m_phase == TS_UP1 )
  299. {
  300. pos.x = 7.0f;
  301. pos.y = 4.5f+3.0f;
  302. pos.z = 0.0f;
  303. m_object->SetPosition(2, pos);
  304. m_sound->Play(SOUND_PSHHH2, m_shieldPos, 1.0f, 1.0f);
  305. m_phase = TS_UP2;
  306. m_speed = 1.0f/0.8f;
  307. return ERR_CONTINUE;
  308. }
  309. if ( m_phase == TS_UP2 )
  310. {
  311. pos.x = 0.0f;
  312. pos.y = 1.0f+3.0f;
  313. pos.z = 0.0f;
  314. m_object->SetPosition(3, pos);
  315. m_object->SetShieldRadius(GetRadius());
  316. pos = m_shieldPos;
  317. speed = Math::Vector(0.0f, 0.0f, 0.0f);
  318. dim.x = GetRadius();
  319. dim.y = dim.x;
  320. m_rankSphere = m_particle->CreateParticle(pos, speed, dim, Gfx::PARTISPHERE3, 2.0f, 0.0f, 0.0f);
  321. m_phase = TS_SHIELD;
  322. m_speed = 1.0f/999.9f;
  323. if ( m_object->GetSelect() )
  324. {
  325. m_brain->UpdateInterface();
  326. }
  327. return ERR_CONTINUE;
  328. }
  329. if ( m_phase == TS_SMOKE )
  330. {
  331. m_sound->Play(SOUND_PSHHH2, m_shieldPos, 1.0f, 1.0f);
  332. m_phase = TS_DOWN1;
  333. m_speed = 1.0f/0.8f;
  334. return ERR_CONTINUE;
  335. }
  336. if ( m_phase == TS_DOWN1 )
  337. {
  338. m_sound->Play(SOUND_PSHHH2, m_shieldPos, 1.0f, 0.7f);
  339. m_phase = TS_DOWN2;
  340. m_speed = 1.0f/1.0f;
  341. return ERR_CONTINUE;
  342. }
  343. Abort();
  344. return ERR_STOP;
  345. }
  346. // Indicates whether the action is pending.
  347. bool CTaskShield::IsBusy()
  348. {
  349. if ( m_phase == TS_SHIELD )
  350. {
  351. return false;
  352. }
  353. return true;
  354. }
  355. // Suddenly ends the current action.
  356. bool CTaskShield::Abort()
  357. {
  358. Math::Vector pos;
  359. m_object->SetShieldRadius(0.0f);
  360. pos.x = 7.0f;
  361. pos.y = 4.5f;
  362. pos.z = 0.0f;
  363. m_object->SetPosition(2, pos);
  364. pos.x = 0.0f;
  365. pos.y = 1.0f;
  366. pos.z = 0.0f;
  367. m_object->SetPosition(3, pos);
  368. if ( m_soundChannel != -1 )
  369. {
  370. m_sound->FlushEnvelope(m_soundChannel);
  371. m_sound->AddEnvelope(m_soundChannel, 0.5f, 0.5f, 2.0f, SOPER_STOP);
  372. m_soundChannel = -1;
  373. }
  374. if ( m_rankSphere != -1 )
  375. {
  376. m_particle->SetPhase(m_rankSphere, Gfx::PARPHEND, 3.0f);
  377. m_rankSphere = -1;
  378. }
  379. if ( m_effectLight != -1 )
  380. {
  381. m_lightMan->DeleteLight(m_effectLight);
  382. m_effectLight = -1;
  383. }
  384. m_camera->StopCentering(m_object, 2.0f);
  385. return true;
  386. }
  387. // Creates the light to accompany a pyrotechnic effect.
  388. bool CTaskShield::CreateLight(Math::Vector pos)
  389. {
  390. Gfx::Light light;
  391. if ( !m_engine->GetLightMode() ) return true;
  392. memset(&light, 0, sizeof(light));
  393. light.type = Gfx::LIGHT_SPOT;
  394. light.diffuse.r = 0.0f;
  395. light.diffuse.g = 1.0f;
  396. light.diffuse.b = 2.0f;
  397. light.position.x = pos.x;
  398. light.position.y = pos.y;
  399. light.position.z = pos.z;
  400. light.direction.x = 0.0f;
  401. light.direction.y = -1.0f; // against the bottom
  402. light.direction.z = 0.0f;
  403. light.spotIntensity = 128;
  404. light.attenuation0 = 1.0f;
  405. light.attenuation1 = 0.0f;
  406. light.attenuation2 = 0.0f;
  407. light.spotAngle = 90.0f*Math::PI/180.0f;
  408. m_effectLight = m_lightMan->CreateLight();
  409. if ( m_effectLight == -1 ) return false;
  410. m_lightMan->SetLight(m_effectLight, light);
  411. m_lightMan->SetLightIntensity(m_effectLight, 1.0f);
  412. return true;
  413. }
  414. // Repaired the shielded objects within the sphere of the shield.
  415. void CTaskShield::IncreaseShield()
  416. {
  417. ObjectType type;
  418. CObject* pObj;
  419. Math::Vector oPos;
  420. float dist, shield;
  421. int i;
  422. for ( i=0 ; i<1000000 ; i++ )
  423. {
  424. pObj = static_cast<CObject*>(m_iMan->SearchInstance(CLASS_OBJECT, i));
  425. if ( pObj == 0 ) break;
  426. type = pObj->GetType();
  427. if ( type == OBJECT_MOTHER ||
  428. type == OBJECT_ANT ||
  429. type == OBJECT_SPIDER ||
  430. type == OBJECT_BEE ||
  431. type == OBJECT_WORM ) continue;
  432. oPos = pObj->GetPosition(0);
  433. dist = Math::Distance(oPos, m_shieldPos);
  434. if ( dist <= GetRadius()+10.0f )
  435. {
  436. shield = pObj->GetShield();
  437. shield += 0.1f;
  438. if ( shield > 1.0f ) shield = 1.0f;
  439. pObj->SetShield(shield);
  440. }
  441. }
  442. }
  443. // Returns the radius of the shield.
  444. float CTaskShield::GetRadius()
  445. {
  446. return RADIUS_SHIELD_MIN + (RADIUS_SHIELD_MAX-RADIUS_SHIELD_MIN)*m_object->GetParam();
  447. }