GameObject.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 Lasse Öörni
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Precompiled.h"
  24. #include "AnimatedModel.h"
  25. #include "Animation.h"
  26. #include "AnimationState.h"
  27. #include "Deserializer.h"
  28. #include "Engine.h"
  29. #include "Entity.h"
  30. #include "Exception.h"
  31. #include "GameObject.h"
  32. #include "GameEvents.h"
  33. #include "Light.h"
  34. #include "MemoryBuffer.h"
  35. #include "Ninja.h"
  36. #include "PhysicsEvents.h"
  37. #include "PositionalChannel.h"
  38. #include "Potion.h"
  39. #include "ReplicationUtils.h"
  40. #include "ResourceCache.h"
  41. #include "RigidBody.h"
  42. #include "Sound.h"
  43. #include "Scene.h"
  44. #include "Serializer.h"
  45. #include "SnowBall.h"
  46. #include "SnowCrate.h"
  47. #include "XMLElement.h"
  48. #include "DebugNew.h"
  49. GameObject::GameObject() :
  50. mDuration(-1.0f),
  51. mMaxHealth(1),
  52. mHealth(1),
  53. mCanCollide(true),
  54. mOnGround(false),
  55. mIsSliding(false),
  56. mSide(SIDE_NEUTRAL),
  57. mOrigin(0),
  58. mLastDamageOrigin(0)
  59. {
  60. // Do not replicate the GameObject component by default
  61. setNetFlags(NET_SYNCTONONE);
  62. subscribeToEvent(EVENT_PHYSICSPOSTSTEP, EVENT_HANDLER(GameObject, handlePhysicsPostStep));
  63. subscribeToEvent(EVENT_ENTITYCOLLISION, EVENT_HANDLER(GameObject, handleEntityCollision));
  64. }
  65. GameObject::~GameObject()
  66. {
  67. }
  68. void GameObject::save(Serializer& dest)
  69. {
  70. // Write Component properties
  71. Component::save(dest);
  72. // Write GameObject properties
  73. dest.writeFloat(mDuration);
  74. dest.writeInt(mSide);
  75. dest.writeInt(mHealth);
  76. dest.writeInt(mMaxHealth);
  77. dest.writeBool(mOnGround);
  78. dest.writeBool(mIsSliding);
  79. dest.writeBool(mCanCollide);
  80. dest.writeUInt(mOrigin);
  81. dest.writeUInt(mLastDamageOrigin);
  82. dest.writeInt(mPoints);
  83. }
  84. void GameObject::load(Deserializer& source, ResourceCache* cache)
  85. {
  86. // Read Component properties
  87. Component::load(source, cache);
  88. // Read GameObject properties
  89. mDuration = source.readFloat();
  90. mSide = source.readInt();
  91. mHealth = source.readInt();
  92. mMaxHealth = source.readInt();
  93. mOnGround = source.readBool();
  94. mIsSliding = source.readBool();
  95. mCanCollide = source.readBool();
  96. mOrigin = source.readUInt();
  97. mLastDamageOrigin = source.readUInt();
  98. mPoints = source.readInt();
  99. }
  100. void GameObject::saveXML(XMLElement& dest)
  101. {
  102. // Write Component properties
  103. Component::saveXML(dest);
  104. // Write GameObject properties
  105. XMLElement gameObjectElem = dest.createChildElement("gameobject");
  106. gameObjectElem.setFloat("duration", mDuration);
  107. gameObjectElem.setInt("side", mSide);
  108. gameObjectElem.setInt("health", mHealth);
  109. gameObjectElem.setInt("maxhealth", mMaxHealth);
  110. gameObjectElem.setBool("onground", mOnGround);
  111. gameObjectElem.setBool("issliding", mIsSliding);
  112. gameObjectElem.setBool("cancollide", mCanCollide);
  113. gameObjectElem.setInt("origin", mOrigin);
  114. gameObjectElem.setInt("damageorigin", mLastDamageOrigin);
  115. gameObjectElem.setInt("points", mPoints);
  116. }
  117. void GameObject::loadXML(const XMLElement& source, ResourceCache* cache)
  118. {
  119. // Read Component properties
  120. Component::loadXML(source, cache);
  121. // Read GameObject properties
  122. XMLElement gameObjectElem = source.getChildElement("gameobject");
  123. mDuration = gameObjectElem.getFloat("duration");
  124. mSide = gameObjectElem.getInt("side");
  125. mHealth = gameObjectElem.getInt("health");
  126. mMaxHealth = gameObjectElem.getInt("maxhealth");
  127. mOnGround = gameObjectElem.getBool("onground");
  128. mIsSliding = gameObjectElem.getBool("issliding");
  129. mCanCollide = gameObjectElem.getBool("cancollide");
  130. mOrigin = gameObjectElem.getInt("origin");
  131. mLastDamageOrigin = gameObjectElem.getInt("damageorigin");
  132. mPoints = gameObjectElem.getInt("points");
  133. }
  134. bool GameObject::writeNetUpdate(Serializer& dest, Serializer& destRevision, Deserializer& baseRevision, const NetUpdateInfo& info)
  135. {
  136. // Build bitmask of changed properties
  137. unsigned char bits = 0;
  138. checkFloat(mDuration, -1.0f, baseRevision, bits, 1);
  139. checkInt(mSide, SIDE_NEUTRAL, baseRevision, bits, 2);
  140. checkInt(mHealth, 1, baseRevision, bits, 4);
  141. checkInt(mMaxHealth, 1, baseRevision, bits, 8);
  142. checkBool(mCanCollide, true, baseRevision, bits, 16);
  143. checkUInt(mOrigin, 0, baseRevision, bits, 32);
  144. checkUInt(mLastDamageOrigin, 0, baseRevision, bits, 64);
  145. checkInt(mPoints, 0, baseRevision, bits, 128);
  146. // Update replication state fully, and network stream by delta
  147. dest.writeUByte(bits);
  148. writeFloatDelta(mDuration, dest, destRevision, bits & 1);
  149. writeIntDelta(mSide, dest, destRevision, bits & 2);
  150. writeIntDelta(mHealth, dest, destRevision, bits & 4);
  151. writeIntDelta(mMaxHealth, dest, destRevision, bits & 8);
  152. writeBoolDelta(mCanCollide, dest, destRevision, bits & 16);
  153. writeUIntDelta(mOrigin, dest, destRevision, bits & 32);
  154. writeUIntDelta(mLastDamageOrigin, dest, destRevision, bits & 64);
  155. writeIntDelta(mPoints, dest, destRevision, bits & 128);
  156. return bits != 0;
  157. }
  158. void GameObject::readNetUpdate(Deserializer& source, ResourceCache* cache, const NetUpdateInfo& info)
  159. {
  160. unsigned char bits = source.readUByte();
  161. readFloatDelta(mDuration, source, bits & 1);
  162. readIntDelta(mSide, source, bits & 2);
  163. readIntDelta(mHealth, source, bits & 4);
  164. readIntDelta(mMaxHealth, source, bits & 8);
  165. if (bits & 16)
  166. setCanCollide(source.readBool());
  167. readUIntDelta(mOrigin, source, bits & 32);
  168. readUIntDelta(mLastDamageOrigin, source, bits & 64);
  169. readIntDelta(mPoints, source, bits & 128);
  170. }
  171. void GameObject::create(const Vector3& position, const Quaternion& orientation)
  172. {
  173. if (!mEntity)
  174. EXCEPTION("GameObject must belong to an entity before being created");
  175. // Subclass specific creation (create entity, bodies & geometries)
  176. onCreate(position, orientation);
  177. // Initialize health
  178. setHealth(mMaxHealth);
  179. // Send creation event
  180. using namespace Create;
  181. VariantMap eventData;
  182. eventData[P_OBJECT] = (void*)this;
  183. sendEvent(EVENT_CREATE, eventData);
  184. }
  185. void GameObject::postUpdateFixed(float time)
  186. {
  187. bool goOn = true;
  188. // Perform subclass specific update
  189. goOn = onUpdate(time);
  190. // Reset ground/sliding flag for the next frame
  191. RigidBody* body = getBody();
  192. if (body)
  193. {
  194. if (body->isActive())
  195. {
  196. mOnGround = false;
  197. mIsSliding = false;
  198. }
  199. else
  200. {
  201. // If body is sleeping, assume it rests on ground
  202. mOnGround = true;
  203. mIsSliding = false;
  204. }
  205. }
  206. // Decrement lifetime duration
  207. if (mDuration >= 0)
  208. {
  209. mDuration -= time;
  210. if (mDuration <= 0) goOn = false;
  211. }
  212. // Kind of hack: if there is a light attached, decrease its intensity
  213. Light* light = mEntity->getComponent<Light>();
  214. if (light)
  215. light->setColor(light->getColor() * max(1.0f - time * 10.0f, 0.0f));
  216. // Only remove the entity if we are authoritative
  217. if ((!isProxy()) && (!goOn))
  218. {
  219. onRemove();
  220. // Send removal event
  221. using namespace Remove;
  222. VariantMap eventData;
  223. eventData[P_OBJECT] = (void*)this;
  224. sendEvent(EVENT_REMOVE, eventData);
  225. // Remove through the scene. Note: no operations on the GameObject are safe after this!
  226. getScene()->removeEntity(mEntity);
  227. return;
  228. }
  229. }
  230. void GameObject::setCanCollide(bool enable)
  231. {
  232. mCanCollide = enable;
  233. RigidBody* body = getBody();
  234. // Collision to static objects always on, enable/disable dynamic object collision as necessary
  235. if (body)
  236. body->setCollisionMask(enable ? 3 : 2);
  237. }
  238. void GameObject::setHealth(int health)
  239. {
  240. if (health < 0) health = 0;
  241. mHealth = health;
  242. }
  243. bool GameObject::damage(int damage, GameObject* source)
  244. {
  245. // Only take damage if the object allows it
  246. if (onDamage(damage))
  247. {
  248. mHealth -= damage;
  249. if (mHealth < 0)
  250. mHealth = 0;
  251. // Record who is responsible for last damage
  252. if (source)
  253. mLastDamageOrigin = source->mOrigin;
  254. else
  255. mLastDamageOrigin = 0;
  256. using namespace Damage;
  257. VariantMap eventData;
  258. eventData[P_OBJECT] = (void*)this;
  259. sendEvent(EVENT_DAMAGE, eventData);
  260. return true;
  261. }
  262. return false;
  263. }
  264. bool GameObject::heal(int amount)
  265. {
  266. if (mHealth >= mMaxHealth) return false;
  267. // Only heal if the object allows it
  268. if (onHeal(amount))
  269. {
  270. mHealth += amount;
  271. if (mHealth > mMaxHealth) mHealth = mMaxHealth;
  272. using namespace Heal;
  273. VariantMap eventData;
  274. eventData[P_OBJECT] = (void*)this;
  275. sendEvent(EVENT_HEAL, eventData);
  276. return true;
  277. }
  278. return false;
  279. }
  280. void GameObject::enableAnim(const std::string& name, bool restart)
  281. {
  282. AnimatedModel* model = mEntity->getComponent<AnimatedModel>();
  283. if (!model)
  284. return;
  285. AnimationState* state = model->getAnimationState(name);
  286. if (state)
  287. {
  288. // Start from beginning if restart explicitly requested
  289. if (restart)
  290. state->setTime(0.0f);
  291. state->setWeight(1.0f);
  292. }
  293. else
  294. {
  295. AnimationState* state = model->addAnimationState(getAnim(name));
  296. if (state)
  297. state->setWeight(1.0f);
  298. }
  299. }
  300. void GameObject::enableOnlyAnim(const std::string& name, bool restart)
  301. {
  302. AnimatedModel* model = mEntity->getComponent<AnimatedModel>();
  303. if (!model)
  304. return;
  305. // Enable only the animation we want
  306. enableAnim(name, restart);
  307. // Disable all others
  308. std::vector<AnimationState*> states = model->getAnimationStates();
  309. for (unsigned i = 0; i < states.size(); ++i)
  310. {
  311. if (states[i]->getAnimation()->getName() != name)
  312. model->removeAnimationState(states[i]);
  313. }
  314. }
  315. void GameObject::enableOnlyAnimSmooth(const std::string& name, bool restart, float time)
  316. {
  317. AnimatedModel* model = mEntity->getComponent<AnimatedModel>();
  318. if (!model)
  319. return;
  320. AnimationState* state = model->getAnimationState(name);
  321. if (!state)
  322. state = model->addAnimationState(getAnim(name));
  323. if (state)
  324. {
  325. if (restart)
  326. state->setTime(0.0f);
  327. // Increase weight of the animation we're activating, start from 0 if was disabled
  328. state->addWeight(time);
  329. }
  330. // Decrease weight of all the rest, until at weight 0 and can be disabled
  331. std::vector<AnimationState*> states = model->getAnimationStates();
  332. for (unsigned i = 0; i < states.size(); ++i)
  333. {
  334. if (states[i]->getAnimation()->getName() != name)
  335. {
  336. states[i]->addWeight(-time);
  337. if (states[i]->getWeight() == 0.0f)
  338. model->removeAnimationState(states[i]);
  339. }
  340. }
  341. }
  342. void GameObject::disableAnim(const std::string& name)
  343. {
  344. AnimatedModel* model = mEntity->getComponent<AnimatedModel>();
  345. if (!model)
  346. return;
  347. model->removeAnimationState(name);
  348. }
  349. void GameObject::advanceAnim(const std::string& name, bool looped, float time)
  350. {
  351. AnimatedModel* model = mEntity->getComponent<AnimatedModel>();
  352. if (!model)
  353. return;
  354. AnimationState* state = model->getAnimationState(name);
  355. if (state)
  356. {
  357. state->setLooped(looped);
  358. state->addTime(time);
  359. }
  360. }
  361. void GameObject::setAnimWeight(const std::string& name, float weight)
  362. {
  363. AnimatedModel* model = mEntity->getComponent<AnimatedModel>();
  364. if (!model)
  365. return;
  366. AnimationState* state = model->getAnimationState(name);
  367. if (state)
  368. state->setWeight(weight);
  369. }
  370. void GameObject::setAnimPriority(const std::string& name, int priority)
  371. {
  372. AnimatedModel* model = mEntity->getComponent<AnimatedModel>();
  373. if (!model)
  374. return;
  375. AnimationState* state = model->getAnimationState(name);
  376. if (state)
  377. state->setPriority(priority);
  378. }
  379. void GameObject::playSound(const std::string& name)
  380. {
  381. // Need an unique name to ensure proper replication (we potentially have many sounds in the same entity)
  382. PositionalChannel* channel = createComponent<PositionalChannel>(mEntity->getUniqueComponentName());
  383. channel->setAutoRemove(true);
  384. // Attach to the first scenenode component we find from the entity
  385. Node* node = mEntity->getDerivedComponent<Node>();
  386. if (node)
  387. node->addChild(channel);
  388. else
  389. return;
  390. Sound* sound = getResourceCache()->getResource<Sound>(name);
  391. channel->setDistanceAttenuation(200.0f, 5000.0f, 1.0f);
  392. channel->play(sound, sound->getFrequency());
  393. }
  394. void GameObject::playSound(const std::string& name, const Vector3& position)
  395. {
  396. // Need an unique name to ensure proper replication (we potentially have many sounds in the same entity)
  397. PositionalChannel* channel = createComponent<PositionalChannel>(mEntity->getUniqueComponentName());
  398. Sound* sound = getResourceCache()->getResource<Sound>(name);
  399. channel->setAutoRemove(true);
  400. channel->setPosition(position);
  401. channel->setDistanceAttenuation(200.0f, 5000.0f, 1.0f);
  402. channel->play(sound, sound->getFrequency());
  403. }
  404. const std::string& GameObject::getName() const
  405. {
  406. return mEntity->getName();
  407. }
  408. RigidBody* GameObject::getBody()
  409. {
  410. return mEntity->getComponent<RigidBody>();
  411. }
  412. void GameObject::checkOnGround(VariantMap& collision)
  413. {
  414. using namespace EntityCollision;
  415. RigidBody* body = getBody();
  416. // Should not happen
  417. if (!body)
  418. return;
  419. // The contacts data is stored in a slightly malicious way; have to deserialize it from a byte buffer
  420. const std::vector<unsigned char>& contacts = collision[P_CONTACTS].getBuffer();
  421. MemoryBuffer contactsBuffer(&contacts[0], contacts.size());
  422. while (!contactsBuffer.isEof())
  423. {
  424. Vector3 contactPosition = contactsBuffer.readVector3();
  425. Vector3 contactNormal = contactsBuffer.readVector3();
  426. float contactDepth = contactsBuffer.readFloat();
  427. float contactVelocity = contactsBuffer.readFloat();
  428. // If contact is below center and mostly vertical, assume it's ground contact
  429. if (contactPosition.mY < body->getPhysicsPosition().mY)
  430. {
  431. float level = fabsf(contactNormal.mY);
  432. if (level > 0.75f)
  433. mOnGround = true;
  434. else
  435. {
  436. // If contact is somewhere inbetween vertical/horizontal, the object is sliding a slope
  437. if (level > 0.1f)
  438. mIsSliding = true;
  439. }
  440. }
  441. // Ground contact has priority over sliding contact
  442. if (mOnGround == true)
  443. mIsSliding = false;
  444. }
  445. }
  446. Animation* GameObject::getAnim(const std::string& name)
  447. {
  448. return getResourceCache()->getResource<Animation>(name);
  449. }
  450. Scene* GameObject::getScene() const
  451. {
  452. return mEntity ? mEntity->getScene() : 0;
  453. }
  454. ResourceCache* GameObject::getResourceCache() const
  455. {
  456. Scene* scene = getScene();
  457. if (scene)
  458. return scene->getResourceCache();
  459. else
  460. return 0;
  461. }
  462. void GameObject::handlePhysicsPostStep(StringHash eventType, VariantMap& eventData)
  463. {
  464. using namespace PhysicsPostStep;
  465. // Check that the scene matches
  466. Scene* scene = mEntity ? mEntity->getScene() : 0;
  467. if (eventData[P_SCENE].getPtr() == (void*)scene)
  468. postUpdateFixed(eventData[P_TIMESTEP].getFloat());
  469. }
  470. void GameObject::handleEntityCollision(StringHash eventType, VariantMap& eventData)
  471. {
  472. using namespace EntityCollision;
  473. checkOnGround(eventData);
  474. if (!mCanCollide)
  475. return;
  476. Entity* otherEntity = static_cast<Entity*>(eventData[P_OTHERENTITY].getPtr());
  477. if (otherEntity)
  478. {
  479. // Dig out the GameObject
  480. // If no game object, it's a world collision
  481. GameObject* other = otherEntity->getDerivedComponent<GameObject>();
  482. if (!other)
  483. onCollide(eventData);
  484. else
  485. {
  486. if (other->mCanCollide)
  487. onCollide(other, eventData);
  488. }
  489. }
  490. }