diff --git a/rwengine/src/data/ModelData.hpp b/rwengine/src/data/ModelData.hpp index 4bb0b09d..504af1d1 100644 --- a/rwengine/src/data/ModelData.hpp +++ b/rwengine/src/data/ModelData.hpp @@ -463,24 +463,32 @@ struct DynamicObjectData { float mass; // Kg float turnMass; // Kg m^3 float airRes; // fraction - float elacticity; // " - float bouancy; + float elasticity; // " + float buoyancy; float uprootForce; // Force float collDamageMulti; - /* - * 1: change model - * 2: split model - * 3: smash - * 4: change and smash - */ - uint8_t collDamageFlags; - /* - * 1: lampost - * 2: smallbox - * 3: bigbox - * 4: fencepart - */ + + enum { + Damage_ChangeModel = 1, + Damage_SplitModel = 2, + Damage_Smash = 3, + Damage_ChangeThenSmash = 4, + Damage_SmashCardboard = 50, + Damage_SmashWoodenBox = 60, + Damage_SmashTrafficCone = 70, + Damage_SmashBarPost = 80, + }; + uint8_t collDamageEffect; + + enum { + Response_None = 0, + Response_LampPost = 1, + Response_SmallBox = 2, + Response_BigBox = 3, + Response_FencePart = 4, + }; uint8_t collResponseFlags; + bool cameraAvoid; }; diff --git a/rwengine/src/engine/GameWorld.cpp b/rwengine/src/engine/GameWorld.cpp index d3eac714..7dfbb090 100644 --- a/rwengine/src/engine/GameWorld.cpp +++ b/rwengine/src/engine/GameWorld.cpp @@ -587,9 +587,12 @@ void handleInstanceResponse(InstanceObject *instance, const btManifoldPoint &mp, auto impulse = mp.getAppliedImpulse(); if (impulse > 0.0f) { + ///@ todo Correctness: object damage calculation + constexpr auto kMinimumDamageImpulse = 500.f; + const auto hp = std::max(0.f, impulse - kMinimumDamageImpulse); instance->takeDamage({{dmg.x(), dmg.y(), dmg.z()}, {dmg.x(), dmg.y(), dmg.z()}, - 0.f, + hp, GameObject::DamageInfo::Physics, impulse}); } diff --git a/rwengine/src/loaders/GenericDATLoader.cpp b/rwengine/src/loaders/GenericDATLoader.cpp index 42f90215..ac5b6641 100644 --- a/rwengine/src/loaders/GenericDATLoader.cpp +++ b/rwengine/src/loaders/GenericDATLoader.cpp @@ -37,9 +37,9 @@ void GenericDATLoader::loadDynamicObjects(const std::string& name, if (ss.peek() == ',') ss.ignore(1); ss >> dyndata->airRes; if (ss.peek() == ',') ss.ignore(1); - ss >> dyndata->elacticity; + ss >> dyndata->elasticity; if (ss.peek() == ',') ss.ignore(1); - ss >> dyndata->bouancy; + ss >> dyndata->buoyancy; if (ss.peek() == ',') ss.ignore(1); ss >> dyndata->uprootForce; if (ss.peek() == ',') ss.ignore(1); @@ -47,7 +47,7 @@ void GenericDATLoader::loadDynamicObjects(const std::string& name, if (ss.peek() == ',') ss.ignore(1); int tmp; ss >> tmp; - dyndata->collDamageFlags = tmp; + dyndata->collDamageEffect = tmp; if (ss.peek() == ',') ss.ignore(1); ss >> tmp; dyndata->collResponseFlags = tmp; diff --git a/rwengine/src/objects/InstanceObject.cpp b/rwengine/src/objects/InstanceObject.cpp index 8a5f640a..5a8cc715 100644 --- a/rwengine/src/objects/InstanceObject.cpp +++ b/rwengine/src/objects/InstanceObject.cpp @@ -47,86 +47,81 @@ InstanceObject::~InstanceObject() { } void InstanceObject::tick(float dt) { - if (dynamics && body) { - if (_enablePhysics) { - if (body->getBulletBody()->isStaticObject()) { - body->changeMass(dynamics->mass); - } - } + if (animator) animator->tick(dt); - const glm::vec3& ws = getPosition(); - auto wX = (int)((ws.x + WATER_WORLD_SIZE / 2.f) / - (WATER_WORLD_SIZE / WATER_HQ_DATA_SIZE)); - auto wY = (int)((ws.y + WATER_WORLD_SIZE / 2.f) / - (WATER_WORLD_SIZE / WATER_HQ_DATA_SIZE)); - float vH = ws.z; // - _collisionHeight/2.f; - float wH = 0.f; + if (!body || !dynamics) { + return; + } - if (wX >= 0 && wX < WATER_HQ_DATA_SIZE && wY >= 0 && - wY < WATER_HQ_DATA_SIZE) { - int i = (wX * WATER_HQ_DATA_SIZE) + wY; - int hI = engine->data->realWater[i]; - if (hI < NO_WATER_INDEX) { - wH = engine->data->waterHeights[hI]; - wH += engine->data->getWaveHeightAt(ws); - inWater = vH <= wH; - } else { - inWater = false; - } - } - _lastHeight = ws.z; - - if (inWater) { - float oZ = - -(body->getBoundingHeight() * (dynamics->bouancy / 100.f)); - body->getBulletBody()->activate(true); - // Damper motion - body->getBulletBody()->setDamping(0.95f, 0.9f); - - auto wi = engine->data->getWaterIndexAt(ws); - if (wi != NO_WATER_INDEX) { - float h = engine->data->waterHeights[wi] + oZ; - - // Calculate wave height - h += engine->data->getWaveHeightAt(ws); - - if (ws.z <= h) { - /*if( dynamics->uprootForce > 0.f && - (body->body->getCollisionFlags() & - btRigidBody::CF_STATIC_OBJECT) != 0 ) { - // Apparently bodies must be removed and re-added if - their mass changes. - engine->dynamicsWorld->removeRigidBody(body->body); - btVector3 inert; - body->getCollisionShape()->calculateLocalInertia(dynamics->mass, - inert); - body->setMassProps(dynamics->mass, inert); - engine->dynamicsWorld->addRigidBody(body); - }*/ - - float x = (h - ws.z); - float F = - WATER_BUOYANCY_K * x + - -WATER_BUOYANCY_C * - body->getBulletBody()->getLinearVelocity().z(); - btVector3 forcePos = btVector3(0.f, 0.f, 2.f) - .rotate(body->getBulletBody() - ->getOrientation() - .getAxis(), - body->getBulletBody() - ->getOrientation() - .getAngle()); - body->getBulletBody()->applyForce(btVector3(0.f, 0.f, F), - forcePos); - } - } + if (changeAtomic != -1) { + RW_ASSERT(getModelInfo()->getNumAtomics() > + changeAtomic); + changeModel(getModelInfo(), changeAtomic); + changeAtomic = -1; + } + if (_enablePhysics) { + if (body->getBulletBody()->isStaticObject()) { + body->changeMass(dynamics->mass); } } - if (animator) animator->tick(dt); + const glm::vec3& ws = getPosition(); + auto wX = (int)((ws.x + WATER_WORLD_SIZE / 2.f) / + (WATER_WORLD_SIZE / WATER_HQ_DATA_SIZE)); + auto wY = (int)((ws.y + WATER_WORLD_SIZE / 2.f) / + (WATER_WORLD_SIZE / WATER_HQ_DATA_SIZE)); + float vH = ws.z; // - _collisionHeight/2.f; + float wH = 0.f; + + if (wX >= 0 && wX < WATER_HQ_DATA_SIZE && wY >= 0 && + wY < WATER_HQ_DATA_SIZE) { + int i = (wX * WATER_HQ_DATA_SIZE) + wY; + int hI = engine->data->realWater[i]; + if (hI < NO_WATER_INDEX) { + wH = engine->data->waterHeights[hI]; + wH += engine->data->getWaveHeightAt(ws); + inWater = vH <= wH; + } else { + inWater = false; + } + } + _lastHeight = ws.z; + + if (inWater) { + float oZ = + -(body->getBoundingHeight() * (dynamics->buoyancy / 100.f)); + body->getBulletBody()->activate(true); + // Damper motion + body->getBulletBody()->setDamping(0.95f, 0.9f); + + auto wi = engine->data->getWaterIndexAt(ws); + if (wi != NO_WATER_INDEX) { + float h = engine->data->waterHeights[wi] + oZ; + + // Calculate wave height + h += engine->data->getWaveHeightAt(ws); + + if (ws.z <= h) { + float x = (h - ws.z); + float F = + WATER_BUOYANCY_K * x + + -WATER_BUOYANCY_C * + body->getBulletBody()->getLinearVelocity().z(); + btVector3 forcePos = btVector3(0.f, 0.f, 2.f) + .rotate(body->getBulletBody() + ->getOrientation() + .getAxis(), + body->getBulletBody() + ->getOrientation() + .getAngle()); + body->getBulletBody()->applyForce(btVector3(0.f, 0.f, F), + forcePos); + } + } + } } -void InstanceObject::changeModel(BaseModelInfo* incoming) { +void InstanceObject::changeModel(BaseModelInfo* incoming, int atomicNumber) { if (body) { body.reset(); } @@ -141,12 +136,13 @@ void InstanceObject::changeModel(BaseModelInfo* incoming) { setModel(getModelInfo()->getModel()); auto collision = getModelInfo()->getCollision(); - auto modelatomic = getModelInfo()->getAtomic(0); - if (modelatomic) { - auto previousatomic = atomic_; - atomic_ = modelatomic->clone(); - if (previousatomic) { - atomic_->setFrame(previousatomic->getFrame()); + RW_ASSERT(getModelInfo()->getNumAtomics() > atomicNumber); + auto atomic = getModelInfo()->getAtomic(atomicNumber); + if (atomic) { + auto previous = atomic_; + atomic_ = atomic->clone(); + if (previous) { + atomic_->setFrame(previous->getFrame()); } else { atomic_->setFrame(std::make_shared()); } @@ -182,20 +178,41 @@ void InstanceObject::setRotation(const glm::quat& r) { } bool InstanceObject::takeDamage(const GameObject::DamageInfo& dmg) { - bool smash = false; - if (dynamics) { - smash = dynamics->collDamageFlags == 80; + if (!dynamics) { + return false; + } - if (dmg.impulse >= dynamics->uprootForce && - body->getBulletBody()->isStaticObject()) { - _enablePhysics = true; + const auto effect = dynamics->collDamageEffect; + + if (dmg.hitpoints > 0.f) { + switch (effect) { + case DynamicObjectData::Damage_ChangeModel: + changeAtomic = 1; + break; + case DynamicObjectData::Damage_ChangeThenSmash: + changeAtomic = 1; + RW_UNIMPLEMENTED("Collision Damage Effect: Changing, then Smashing"); + break; + case DynamicObjectData::Damage_Smash: + RW_UNIMPLEMENTED("Collision Damage Effect: Smashing"); + break; + case DynamicObjectData::Damage_SmashCardboard: + case DynamicObjectData::Damage_SmashWoodenBox: + case DynamicObjectData::Damage_SmashTrafficCone: + case DynamicObjectData::Damage_SmashBarPost: + RW_UNIMPLEMENTED("Collision Damage Effect"); + break; + default: + break; } } - if (smash) { - health -= dmg.hitpoints; - return true; + + if (dmg.impulse >= dynamics->uprootForce && + body->getBulletBody()->isStaticObject()) { + _enablePhysics = true; } - return false; + + return true; } void InstanceObject::setSolid(bool solid) { diff --git a/rwengine/src/objects/InstanceObject.hpp b/rwengine/src/objects/InstanceObject.hpp index dd96c7f6..9f4fc20c 100644 --- a/rwengine/src/objects/InstanceObject.hpp +++ b/rwengine/src/objects/InstanceObject.hpp @@ -19,6 +19,7 @@ class GameWorld; class InstanceObject : public GameObject { float health; bool visible = true; + int changeAtomic = -1; /** * The Atomic instance for this object @@ -47,7 +48,7 @@ public: void tick(float dt) override; - void changeModel(BaseModelInfo* incoming); + void changeModel(BaseModelInfo* incoming, int atomicNumber = 0); void setPosition(const glm::vec3& pos) override; diff --git a/tests/test_Data.cpp b/tests/test_Data.cpp index cf191d92..1fbe9b9c 100644 --- a/tests/test_Data.cpp +++ b/tests/test_Data.cpp @@ -59,11 +59,11 @@ BOOST_AUTO_TEST_CASE(test_dynamic_dat_loader) { BOOST_CHECK_EQUAL(lamp->mass, 600.0); BOOST_CHECK_EQUAL(lamp->turnMass, 4000.0); BOOST_CHECK_CLOSE(lamp->airRes, 0.99, 1.0); - BOOST_CHECK_CLOSE(lamp->elacticity, 0.05, 0.01); - BOOST_CHECK_EQUAL(lamp->bouancy, 50.0); + BOOST_CHECK_CLOSE(lamp->elasticity, 0.05, 0.01); + BOOST_CHECK_EQUAL(lamp->buoyancy, 50.0); BOOST_CHECK_EQUAL(lamp->uprootForce, 400); BOOST_CHECK_EQUAL(lamp->collDamageMulti, 1.0); - BOOST_CHECK_EQUAL(lamp->collDamageFlags, 1); + BOOST_CHECK_EQUAL(lamp->collDamageEffect, 1); BOOST_CHECK_EQUAL(lamp->collResponseFlags, 1); BOOST_CHECK_EQUAL(lamp->cameraAvoid, false); }