diff --git a/src/assets/cache.hpp b/src/assets/cache.hpp index 2a705ce..452dd99 100644 --- a/src/assets/cache.hpp +++ b/src/assets/cache.hpp @@ -13,6 +13,8 @@ #include "gfx/font.hpp" #endif +#include + namespace assets { @@ -33,6 +35,7 @@ public: } } + std::cout << "loading " << key << "..." << std::endl; PtrType obj = Load(key); cache_[key] = obj; // Cache the loaded object return obj; diff --git a/src/assets/model.cpp b/src/assets/model.cpp index 93601a2..c7af6af 100644 --- a/src/assets/model.cpp +++ b/src/assets/model.cpp @@ -180,13 +180,16 @@ std::shared_ptr assets::Model::LoadFromFile(const std::stri iss >> com.x >> com.y >> com.z; model->col_offset_ = com; } + else if (command == "param") + { + std::string key, val; + iss >> key >> val; + model->params_[key] = val; + } else { throw std::runtime_error("Unknown command in model file: " + command); } - - - // TODO: skeleton }); CLIENT_ONLY( @@ -215,3 +218,29 @@ std::shared_ptr assets::Model::LoadFromFile(const std::stri return model; } + +const std::string* assets::Model::GetParam(const std::string& key) const +{ + auto it = params_.find(key); + if (it == params_.end()) + return nullptr; + + return &it->second; +} + +bool assets::Model::GetParamFloat(const std::string& key, float& out) const +{ + auto str_val = GetParam(key); + if (!str_val) + return false; + + std::string str = *str_val; + + auto dashpos = str.find(','); + if (dashpos != std::string::npos) + str[dashpos] = '.'; + + out = std::strtof(str.c_str(), nullptr); + + return true; +} diff --git a/src/assets/model.hpp b/src/assets/model.hpp index a6d0fd5..d41e24a 100644 --- a/src/assets/model.hpp +++ b/src/assets/model.hpp @@ -34,7 +34,6 @@ namespace assets // }; // }; - class Model { public: @@ -51,6 +50,9 @@ public: CLIENT_ONLY(const std::shared_ptr& GetMesh() const { return mesh_; }) const AABB3& GetAABB() const { return aabb_; } + const std::string* GetParam(const std::string& key) const; + bool GetParamFloat(const std::string& key, float& out) const; + private: std::string name_; glm::vec3 col_offset_ = glm::vec3(0.0f); @@ -63,6 +65,8 @@ private: CLIENT_ONLY(std::shared_ptr mesh_;); AABB3 aabb_; + std::map params_; + }; } \ No newline at end of file diff --git a/src/audio/sound_source.cpp b/src/audio/sound_source.cpp index 515b82c..47d5ae5 100644 --- a/src/audio/sound_source.cpp +++ b/src/audio/sound_source.cpp @@ -3,6 +3,8 @@ #include #include +#include + audio::SoundSource::SoundSource(Player* player, std::shared_ptr sound) : Super(sound->GetCategoryName(), player), sound_(std::move(sound)) { @@ -26,6 +28,7 @@ void audio::SoundSource::SetPitch(float pitch) void audio::SoundSource::SetVolume(float volume) { Super::SetSourceVolume(sound_->GetVolume() * volume); + //std::cout << "src volume " << sound_->GetVolume() * volume << std::endl; } void audio::SoundSource::Update() diff --git a/src/client/app.cpp b/src/client/app.cpp index 1efc53e..24ff285 100644 --- a/src/client/app.cpp +++ b/src/client/app.cpp @@ -12,9 +12,9 @@ App::App() std::cout << "Initializing App..." << std::endl; #ifndef EMSCRIPTEN - audiomaster_.SetMasterVolume(0.2f); + audiomaster_.SetMasterVolume(1.0f); #else - audiomaster_.SetMasterVolume(0.8f); + audiomaster_.SetMasterVolume(2.0f); #endif font_ = assets::CacheManager::GetFont("data/comic32.font"); diff --git a/src/client/main.cpp b/src/client/main.cpp index 30d517c..79586be 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -73,9 +73,9 @@ static void InitSDL() #ifndef PG_GLES static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) { - //if (severity == 0x826b) - // return; - // + if (severity == 0x826b) + return; + ////std::cout << message << std::endl; fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), diff --git a/src/collision/object_info.hpp b/src/collision/object_info.hpp new file mode 100644 index 0000000..36fbaf1 --- /dev/null +++ b/src/collision/object_info.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +namespace collision +{ + +enum ObjectType : int +{ + OT_UNDEFINED, + OT_MAP_OBJECT, + OT_ENTITY, +}; + +using ObjectFlags = int; + +enum ObjectFlag : ObjectFlags +{ + OF_DESTRUCTIBLE = 0x01, + OF_NOTIFY_CONTACT = 0x02, +}; + +class ObjectCallback +{ +public: + ObjectCallback() = default; + + virtual void OnContact(float impulse) {} + + virtual ~ObjectCallback() = default; +}; + +inline void SetObjectInfo(btCollisionObject* obj, ObjectType type, ObjectFlags flags, ObjectCallback* callback) +{ + obj->setUserIndex(static_cast(type)); + obj->setUserIndex2(static_cast(flags)); + obj->setUserPointer(callback); +} + +inline void GetObjectInfo(const btCollisionObject* obj, ObjectType& type, ObjectFlags& flags, ObjectCallback*& callback) +{ + type = static_cast(obj->getUserIndex()); + flags = static_cast(obj->getUserIndex2()); + callback = static_cast(obj->getUserPointer()); +} + + +} \ No newline at end of file diff --git a/src/collision/object_type.hpp b/src/collision/object_type.hpp deleted file mode 100644 index 4cd35e9..0000000 --- a/src/collision/object_type.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -namespace collision -{ - -enum ObjectType -{ - OT_UNDEFINED = 0, - OT_MAP_STATIC = 1, - OT_MAP_DESTRUCTIBLE = 2, - - -}; - - -} \ No newline at end of file diff --git a/src/game/destroyed_object.cpp b/src/game/destroyed_object.cpp index 75e5b38..c302de6 100644 --- a/src/game/destroyed_object.cpp +++ b/src/game/destroyed_object.cpp @@ -1,11 +1,19 @@ #include "destroyed_object.hpp" +#include "utils/random.hpp" game::DestroyedObject::DestroyedObject(World& world, std::unique_ptr col) : Super(world, col->GetModel()->GetName()), col_(std::move(col)) { - // remove after 30s - Schedule(30000, [this]() + auto destr_snd_str = col_->GetModel()->GetParam("destr_snd"); + if (destr_snd_str) { + Schedule(1, [this, destr_snd_str] { + PlaySound(*destr_snd_str, RandomFloat(0.8f, 1.2f), RandomFloat(0.8f, 1.2f)); + }); + } + + // remove after 30s + Schedule(30000, [this] { Remove(); }); } diff --git a/src/game/drivable_vehicle.cpp b/src/game/drivable_vehicle.cpp index 5fb0aee..31e652b 100644 --- a/src/game/drivable_vehicle.cpp +++ b/src/game/drivable_vehicle.cpp @@ -1,5 +1,6 @@ #include "drivable_vehicle.hpp" #include "player_character.hpp" +#include "utils/random.hpp" game::DrivableVehicle::DrivableVehicle(World& world, std::string model_name, const glm::vec3& color) : Vehicle(world, std::move(model_name), color) @@ -13,6 +14,8 @@ void game::DrivableVehicle::Use(PlayerCharacter& character, uint32_t target_id) return; character.SetVehicle(this, target_id); // seat idx is same as target_id + PlaySound("cardoor", 1.0f, RandomFloat(0.9f, 1.1f)); + } bool game::DrivableVehicle::SetPassenger(uint32_t seat_idx, ControllableCharacter* character) diff --git a/src/game/entity.cpp b/src/game/entity.cpp index 193ec65..2aef0c7 100644 --- a/src/game/entity.cpp +++ b/src/game/entity.cpp @@ -52,6 +52,14 @@ void game::Entity::Attach(net::EntNum parentnum) SendAttachMsg(); } +void game::Entity::PlaySound(const std::string& name, float volume, float pitch) +{ + auto msg = BeginEntMsg(net::EMSG_PLAYSOUND); + msg.Write(net::SoundName(name)); + msg.Write(volume); + msg.Write(pitch); +} + void game::Entity::WriteNametag(net::OutMessage& msg) const { msg.Write(net::NameTag{nametag_}); diff --git a/src/game/entity.hpp b/src/game/entity.hpp index 3ea5df6..c5c4821 100644 --- a/src/game/entity.hpp +++ b/src/game/entity.hpp @@ -6,6 +6,7 @@ #include "transform_node.hpp" #include "utils/defs.hpp" #include "utils/scheduler.hpp" +#include "collision/object_info.hpp" namespace game { @@ -13,7 +14,7 @@ namespace game class World; class Player; -class Entity : public net::MsgProducer, public Scheduler +class Entity : public net::MsgProducer, public Scheduler, public collision::ObjectCallback { public: Entity(World& world, net::EntType viewtype); @@ -29,15 +30,18 @@ public: int64_t GetUpdateTime() const { return upd_time_; } void SetNametag(const std::string& nametag); - + void Attach(net::EntNum parentnum); net::EntNum GetParentNum() const { return parentnum_; } + void PlaySound(const std::string& name, float volume = 1.0f, float pitch = 1.0f); + void Remove() { removed_ = true; } bool IsRemoved() const { return removed_; } const TransformNode& GetRoot() const { return root_; } const Transform& GetRootTransform() const { return root_.local; } + float GetMaxDistance() const { return max_distance_; } virtual ~Entity() = default; diff --git a/src/game/mapinstance.cpp b/src/game/mapinstance.cpp index a7fd434..b3d0023 100644 --- a/src/game/mapinstance.cpp +++ b/src/game/mapinstance.cpp @@ -3,7 +3,6 @@ #include #include "assets/cache.hpp" -#include "collision/object_type.hpp" game::MapInstance::MapInstance(collision::DynamicsWorld& world, std::string mapname) : world_(world), mapname_(std::move(mapname)) @@ -67,14 +66,13 @@ game::MapObjectCollision::MapObjectCollision(collision::DynamicsWorld& world, const bool destructible = (flags & MAPOBJ_DESTRUCTIBLE) != 0 && cshape != nullptr; - collision::ObjectType obj_type = collision::OT_MAP_STATIC; - btVector3 local_inertia(0, 0, 0); float mass = 0.0f; - + collision::ObjectFlags oflags = 0; + if (destructible) { - obj_type = collision::OT_MAP_DESTRUCTIBLE; + oflags |= collision::OF_DESTRUCTIBLE; } // prefer simple cshape which allow destruction @@ -93,8 +91,8 @@ game::MapObjectCollision::MapObjectCollision(collision::DynamicsWorld& world, offset_trans.position += trans.rotation * model_->GetColOffset(); body_->setWorldTransform(offset_trans.ToBtTransform()); - body_->setUserIndex(static_cast(obj_type)); - body_->setUserPointer(this); + + collision::SetObjectInfo(body_.get(), collision::OT_MAP_OBJECT, oflags, this); // world_.GetBtWorld().addRigidBody(body_.get(), btBroadphaseProxy::StaticFilter, btBroadphaseProxy::AllFilter); world_.GetBtWorld().addRigidBody(body_.get()); @@ -119,7 +117,9 @@ void game::MapObjectCollision::Break() // make new body_ = std::make_unique(btRigidBody::btRigidBodyConstructionInfo(mass, nullptr, shape, local_inertia)); body_->setWorldTransform(trans); - body_->setUserIndex(static_cast(collision::OT_UNDEFINED)); + + collision::SetObjectInfo(body_.get(), collision::OT_UNDEFINED, 0, this); + world_.GetBtWorld().addRigidBody(body_.get()); } diff --git a/src/game/mapinstance.hpp b/src/game/mapinstance.hpp index c918b62..bc2faaf 100644 --- a/src/game/mapinstance.hpp +++ b/src/game/mapinstance.hpp @@ -3,6 +3,7 @@ #include "assets/map.hpp" #include "collision/dynamicsworld.hpp" #include "net/defs.hpp" +#include "collision/object_info.hpp" namespace game { @@ -14,7 +15,7 @@ enum MapObjectCollisionFlag : MapObjectCollisionFlags MAPOBJ_DESTRUCTIBLE = 0x01, }; -class MapObjectCollision +class MapObjectCollision : public collision::ObjectCallback { public: MapObjectCollision(collision::DynamicsWorld& world, std::shared_ptr model, @@ -29,8 +30,7 @@ public: btRigidBody& GetBtBody() { return *body_; } net::ObjNum GetNum() const { return num_; } - - ~MapObjectCollision(); + virtual ~MapObjectCollision() override; private: collision::DynamicsWorld& world_; diff --git a/src/game/openworld.cpp b/src/game/openworld.cpp index 8230589..aedeb44 100644 --- a/src/game/openworld.cpp +++ b/src/game/openworld.cpp @@ -82,10 +82,9 @@ void game::OpenWorld::DestructibleDestroyed(net::ObjNum num, std::unique_ptr(std::move(col)); - // Schedule(100000, [this, objnum = num]() - // { - // RespawnObj(objnum); - // }); + Schedule(10000, [this, num] { + RespawnObj(num); + }); } std::optional> game::OpenWorld::GetBestUseTarget(const glm::vec3& pos) const diff --git a/src/game/vehicle.cpp b/src/game/vehicle.cpp index 1a1e1a5..f5aa547 100644 --- a/src/game/vehicle.cpp +++ b/src/game/vehicle.cpp @@ -4,6 +4,7 @@ #include "net/utils.hpp" #include "player.hpp" #include "player_input.hpp" +#include "utils/random.hpp" #include @@ -33,6 +34,8 @@ game::Vehicle::Vehicle(World& world, std::string model_name, const glm::vec3& co body_ = std::make_unique(rb_info); body_->setActivationState(DISABLE_DEACTIVATION); + collision::SetObjectInfo(body_.get(), collision::OT_ENTITY, collision::OF_NOTIFY_CONTACT, this); + // setup vehicle btRaycastVehicle::btVehicleTuning tuning; vehicle_ = std::make_unique(tuning, body_.get(), &world_.GetVehicleRaycaster()); @@ -100,6 +103,7 @@ void game::Vehicle::Update() root_.UpdateMatrix(); flags_ = 0; + UpdateCrash(); ProcessInput(); UpdateWheels(); @@ -123,6 +127,27 @@ void game::Vehicle::SendInitData(Player& player, net::OutMessage& msg) const msg.WriteAt(fields_pos, fields); } +void game::Vehicle::OnContact(float impulse) +{ + Super::OnContact(impulse); + + crash_intensity_ += impulse; + + if (impulse < 1000.0f) + return; + + if (window_health_ > 0.0f) + { + window_health_ -= impulse; + + if (window_health_ <= 0.0f) // just broken + { + PlaySound("breakwindow", 1.0f, 1.0f); + } + } + +} + void game::Vehicle::SetInput(VehicleInputType type, bool enable) { if (enable) @@ -293,6 +318,47 @@ void game::Vehicle::ProcessInput() flags_ |= VF_BREAKING; } +void game::Vehicle::UpdateCrash() +{ + if (window_health_ <= 0.0f) + flags_ |= VF_BROKENWINDOWS; + + if (no_crash_frames_) + { + --no_crash_frames_; + } + else + { + if (crash_intensity_ > 1000.0f) + { + float volume = RandomFloat(0.9f, 1.2f); + float pitch = RandomFloat(1.0f, 1.3f); + + if (crash_intensity_ > 12000.0f) + { + volume *= 1.7f; + pitch *= 0.8f; + } + if (crash_intensity_ > 4000.0f) + { + volume *= 1.3f; + pitch *= 0.8f; + } + else + { + volume *= 0.8f; + pitch *= 1.2f; + } + + PlaySound("crash", volume, pitch); + no_crash_frames_ = 7 + rand() % 10; + } + + } + + crash_intensity_ = 0.0f; +} + void game::Vehicle::UpdateWheels() { for (size_t i = 0; i < num_wheels_; ++i) diff --git a/src/game/vehicle.hpp b/src/game/vehicle.hpp index 796f99f..f6f03c4 100644 --- a/src/game/vehicle.hpp +++ b/src/game/vehicle.hpp @@ -40,6 +40,8 @@ public: virtual void Update() override; virtual void SendInitData(Player& player, net::OutMessage& msg) const override; + virtual void OnContact(float impulse) override; + void SetInput(VehicleInputType type, bool enable); void SetInputs(VehicleInputFlags inputs) { in_ = inputs; } @@ -59,6 +61,7 @@ public: private: void ProcessInput(); + void UpdateCrash(); void UpdateWheels(); void UpdateSyncState(); VehicleSyncFieldFlags WriteState(net::OutMessage& msg, const VehicleSyncState& base) const; @@ -86,6 +89,11 @@ private: size_t sync_current_ = 0; VehicleInputFlags in_ = 0; + + float window_health_ = 10000.0f; + + float crash_intensity_ = 0.0f; + size_t no_crash_frames_ = 0; }; } // namespace game \ No newline at end of file diff --git a/src/game/vehicle_sync.hpp b/src/game/vehicle_sync.hpp index ff14018..c31bd5f 100644 --- a/src/game/vehicle_sync.hpp +++ b/src/game/vehicle_sync.hpp @@ -14,8 +14,9 @@ using VehicleFlags = uint8_t; enum VehicleFlag : VehicleFlags { VF_NONE, - VF_ACCELERATING = 1, - VF_BREAKING = 2, + VF_ACCELERATING = 0x01, + VF_BREAKING = 0x02, + VF_BROKENWINDOWS = 0x04, }; struct VehicleSyncState diff --git a/src/game/world.cpp b/src/game/world.cpp index 15e9939..62d76c8 100644 --- a/src/game/world.cpp +++ b/src/game/world.cpp @@ -5,7 +5,7 @@ #include "assets/cache.hpp" #include "utils/allocnum.hpp" -#include "collision/object_type.hpp" +#include "collision/object_info.hpp" game::World::World(std::string mapname) : Scheduler(time_ms_), map_(*this, std::move(mapname)) { @@ -49,7 +49,7 @@ void game::World::Update(int64_t delta_time) // GetBtWorld().stepSimulation(delta_s, 1, delta_s); GetBtWorld().stepSimulation(delta_s, 2, delta_s * 0.5f); - DetectDestructibleCollisions(); + HandleContacts(); RunTasks(); @@ -96,7 +96,7 @@ void game::World::RespawnObj(net::ObjNum objnum) } } -void game::World::DetectDestructibleCollisions() +void game::World::HandleContacts() { auto& bt_world = GetBtWorld(); int numManifolds = bt_world.getDispatcher()->getNumManifolds(); @@ -104,41 +104,48 @@ void game::World::DetectDestructibleCollisions() static std::vector to_destroy; to_destroy.clear(); + auto ProcessContact = [&](btRigidBody* body, btRigidBody* other_body, btManifoldPoint& pt) { + collision::ObjectType type; + collision::ObjectFlags flags; + collision::ObjectCallback* cb; + collision::GetObjectInfo(body, type, flags, cb); + + if (cb && (flags & collision::OF_NOTIFY_CONTACT)) + { + cb->OnContact(pt.getAppliedImpulse()); + } + + if (type == collision::OT_MAP_OBJECT && (flags & collision::OF_DESTRUCTIBLE)) + { + auto col = dynamic_cast(cb); + if (!col) + return; + + const float break_threshold = 100.0f; // TODO: per-object threshold + + if (pt.getAppliedImpulse() > break_threshold) + { + to_destroy.push_back(col->GetNum()); + other_body->applyCentralImpulse(pt.m_normalWorldOnB * pt.getAppliedImpulse() * 0.5f); + } + + return; + } + }; + // std::cout << "Checking " << numManifolds << " manifolds for destructible collisions..." << std::endl; for (int i = 0; i < numManifolds; i++) { btPersistentManifold* contactManifold = bt_world.getDispatcher()->getManifoldByIndexInternal(i); - const btRigidBody* bodyA = static_cast(contactManifold->getBody0()); - const btRigidBody* bodyB = static_cast(contactManifold->getBody1()); - - const btRigidBody* destructibleBody = nullptr; - - if (bodyA->getUserIndex() == collision::OT_MAP_DESTRUCTIBLE) - destructibleBody = bodyA; - else if (bodyB->getUserIndex() == collision::OT_MAP_DESTRUCTIBLE) - destructibleBody = bodyB; - - if (!destructibleBody) - continue; + btRigidBody* body0 = const_cast(static_cast(contactManifold->getBody0())); + btRigidBody* body1 = const_cast(static_cast(contactManifold->getBody1())); for (int j = 0; j < contactManifold->getNumContacts(); j++) { - const float break_threshold = 100.0f; // TODO: per-object threshold - btManifoldPoint& pt = contactManifold->getContactPoint(j); - - if (pt.getAppliedImpulse() > break_threshold) - { - std::cout << "Destructible collision detected: impulse = " << pt.getAppliedImpulse() << std::endl; - - MapObjectCollision* obj_col = static_cast(destructibleBody->getUserPointer()); - to_destroy.push_back(obj_col->GetNum()); - - const btRigidBody* otherBody = (destructibleBody == bodyA) ? bodyB : bodyA; - btRigidBody* otherBodyNonConst = const_cast(otherBody); - otherBodyNonConst->applyCentralImpulse(pt.m_normalWorldOnB * pt.getAppliedImpulse() * 0.5f); - } + ProcessContact(body0, body1, pt); + ProcessContact(body1, body0, pt); } } diff --git a/src/game/world.hpp b/src/game/world.hpp index e5121e1..cedc60f 100644 --- a/src/game/world.hpp +++ b/src/game/world.hpp @@ -56,7 +56,7 @@ public: virtual ~World() = default; private: - void DetectDestructibleCollisions(); + void HandleContacts(); void DestroyObject(net::ObjNum objnum); diff --git a/src/gameview/entityview.cpp b/src/gameview/entityview.cpp index 91da02c..04af451 100644 --- a/src/gameview/entityview.cpp +++ b/src/gameview/entityview.cpp @@ -23,6 +23,8 @@ bool game::view::EntityView::ProcessMsg(net::EntMsgType type, net::InMessage& ms return ReadNametag(msg); case net::EMSG_ATTACH: return ReadAttach(msg); + case net::EMSG_PLAYSOUND: + return ProcessPlaySoundMsg(msg); default: return false; } @@ -82,6 +84,21 @@ bool game::view::EntityView::ReadAttach(net::InMessage& msg) return msg.Read(parentnum_); } +bool game::view::EntityView::ProcessPlaySoundMsg(net::InMessage& msg) +{ + net::SoundName name; + float volume, pitch; + if (!msg.Read(name) || !msg.Read(volume) || !msg.Read(pitch)) + return false; + + auto sound = assets::CacheManager::GetSound("data/" + std::string(name) + ".snd"); + auto snd = audioplayer_.PlaySound(sound, &root_.local.position); + snd->SetVolume(volume); + snd->SetPitch(pitch); + + return true; +} + void game::view::EntityView::DrawNametag(const DrawArgs& args) { if (nametag_.empty()) diff --git a/src/gameview/entityview.hpp b/src/gameview/entityview.hpp index b735ac8..dc5a973 100644 --- a/src/gameview/entityview.hpp +++ b/src/gameview/entityview.hpp @@ -50,6 +50,7 @@ public: private: bool ReadNametag(net::InMessage& msg); bool ReadAttach(net::InMessage& msg); + bool ProcessPlaySoundMsg(net::InMessage& msg); void DrawNametag(const DrawArgs& args); void DrawAxes(const DrawArgs& args); diff --git a/src/gameview/vehicleview.cpp b/src/gameview/vehicleview.cpp index bc86bbb..8af44f4 100644 --- a/src/gameview/vehicleview.cpp +++ b/src/gameview/vehicleview.cpp @@ -15,7 +15,7 @@ game::view::VehicleView::VehicleView(WorldView& world, net::InMessage& msg) throw EntityInitError(); model_ = assets::CacheManager::GetVehicleModel("data/" + std::string(modelname) + ".veh"); - + mesh_ = *model_->GetModel()->GetMesh(); auto& modelwheels = model_->GetWheels(); wheels_.resize(modelwheels.size()); @@ -102,17 +102,26 @@ void game::view::VehicleView::Update(const UpdateInfo& info) snd_accel_src_->Delete(); snd_accel_src_ = nullptr; } + + // update windows + if ((flags_ & VF_BROKENWINDOWS) && !windows_broken_) + { + windows_broken_ = true; + + auto it = mesh_.surface_names.find("carwindows"); + if (it != mesh_.surface_names.end()) + { + size_t idx = it->second; + mesh_.surfaces[idx].texture = assets::CacheManager::GetTexture("data/carbrokenwindows.png"); + } + } } void game::view::VehicleView::Draw(const DrawArgs& args) { Super::Draw(args); - // TOOD: chceck and fix - const auto& model = *model_->GetModel(); - const auto& mesh = *model.GetMesh(); - - for (const auto& surface : mesh.surfaces) + for (const auto& surface : mesh_.surfaces) { gfx::DrawSurfaceCmd cmd; cmd.surface = &surface; diff --git a/src/gameview/vehicleview.hpp b/src/gameview/vehicleview.hpp index d1f4723..ce8aa59 100644 --- a/src/gameview/vehicleview.hpp +++ b/src/gameview/vehicleview.hpp @@ -36,6 +36,7 @@ private: private: std::shared_ptr model_; + assets::Mesh mesh_; glm::vec4 color_; game::VehicleSyncState sync_; @@ -48,6 +49,8 @@ private: std::shared_ptr snd_accel_; audio::SoundSource* snd_accel_src_ = nullptr; + + bool windows_broken_ = false; }; } \ No newline at end of file diff --git a/src/gameview/worldview.cpp b/src/gameview/worldview.cpp index bb82137..e0170d8 100644 --- a/src/gameview/worldview.cpp +++ b/src/gameview/worldview.cpp @@ -27,6 +27,12 @@ game::view::WorldView::WorldView(ClientSession& session, net::InMessage& msg) : map_.EnableObj(objnum, false); } + + // cache common snds and stuff + Cache(assets::CacheManager::GetSound("data/breaksign.snd")); + Cache(assets::CacheManager::GetSound("data/breakpatnik.snd")); + Cache(assets::CacheManager::GetSound("data/crash.snd")); + Cache(assets::CacheManager::GetSound("data/breakwindow.snd")); } bool game::view::WorldView::ProcessMsg(net::MessageType type, net::InMessage& msg) @@ -185,3 +191,8 @@ bool game::view::WorldView::ProcessObjDestroyOrRespawnMsg(net::InMessage& msg, b map_.EnableObj(objnum, enable); return true; } + +void game::view::WorldView::Cache(std::any val) +{ + cache_.emplace_back(std::move(val)); +} diff --git a/src/gameview/worldview.hpp b/src/gameview/worldview.hpp index af690bf..930e96d 100644 --- a/src/gameview/worldview.hpp +++ b/src/gameview/worldview.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include "assets/map.hpp" #include "draw_args.hpp" #include "net/defs.hpp" @@ -38,6 +40,8 @@ private: bool ProcessObjDestroyOrRespawnMsg(net::InMessage& msg, bool enable); + void Cache(std::any val); + private: ClientSession& session_; @@ -47,6 +51,8 @@ private: float time_ = 0.0f; audio::Master& audiomaster_; + + std::vector cache_; }; } // namespace game::view \ No newline at end of file diff --git a/src/net/defs.hpp b/src/net/defs.hpp index 094dd50..972fa5e 100644 --- a/src/net/defs.hpp +++ b/src/net/defs.hpp @@ -86,6 +86,7 @@ enum EntMsgType : uint8_t EMSG_NAMETAG, EMSG_ATTACH, EMSG_UPDATE, + EMSG_PLAYSOUND, }; using PositionElemQ = Quantized; @@ -110,6 +111,10 @@ using ColorQ = Quantized; using NameTag = FixedStr<64>; +using SoundName = FixedStr<64>; +using SoundVolumeQ = Quantized; +using SoundPitchQ = Quantized; + using AnimBlendQ = Quantized; using AnimTimeQ = Quantized; diff --git a/src/utils/random.hpp b/src/utils/random.hpp new file mode 100644 index 0000000..3e4e2ef --- /dev/null +++ b/src/utils/random.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include + +inline float RandomFloat(float min, float max) +{ + return min + (max - min) * static_cast(rand() % 100) * 0.01f; +}