Some sound effects

This commit is contained in:
tovjemam 2026-02-28 19:35:14 +01:00
parent 986cbc12a6
commit 55efcceaf0
28 changed files with 318 additions and 82 deletions

View File

@ -13,6 +13,8 @@
#include "gfx/font.hpp" #include "gfx/font.hpp"
#endif #endif
#include <iostream>
namespace assets namespace assets
{ {
@ -33,6 +35,7 @@ public:
} }
} }
std::cout << "loading " << key << "..." << std::endl;
PtrType obj = Load(key); PtrType obj = Load(key);
cache_[key] = obj; // Cache the loaded object cache_[key] = obj; // Cache the loaded object
return obj; return obj;

View File

@ -180,13 +180,16 @@ std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::stri
iss >> com.x >> com.y >> com.z; iss >> com.x >> com.y >> com.z;
model->col_offset_ = com; model->col_offset_ = com;
} }
else if (command == "param")
{
std::string key, val;
iss >> key >> val;
model->params_[key] = val;
}
else else
{ {
throw std::runtime_error("Unknown command in model file: " + command); throw std::runtime_error("Unknown command in model file: " + command);
} }
// TODO: skeleton
}); });
CLIENT_ONLY( CLIENT_ONLY(
@ -215,3 +218,29 @@ std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::stri
return model; 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;
}

View File

@ -34,7 +34,6 @@ namespace assets
// }; // };
// }; // };
class Model class Model
{ {
public: public:
@ -51,6 +50,9 @@ public:
CLIENT_ONLY(const std::shared_ptr<const Mesh>& GetMesh() const { return mesh_; }) CLIENT_ONLY(const std::shared_ptr<const Mesh>& GetMesh() const { return mesh_; })
const AABB3& GetAABB() const { return aabb_; } 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: private:
std::string name_; std::string name_;
glm::vec3 col_offset_ = glm::vec3(0.0f); glm::vec3 col_offset_ = glm::vec3(0.0f);
@ -63,6 +65,8 @@ private:
CLIENT_ONLY(std::shared_ptr<const Mesh> mesh_;); CLIENT_ONLY(std::shared_ptr<const Mesh> mesh_;);
AABB3 aabb_; AABB3 aabb_;
std::map<std::string, std::string> params_;
}; };
} }

View File

@ -3,6 +3,8 @@
#include <AL/al.h> #include <AL/al.h>
#include <AL/alc.h> #include <AL/alc.h>
#include <iostream>
audio::SoundSource::SoundSource(Player* player, std::shared_ptr<const Sound> sound) audio::SoundSource::SoundSource(Player* player, std::shared_ptr<const Sound> sound)
: Super(sound->GetCategoryName(), player), sound_(std::move(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) void audio::SoundSource::SetVolume(float volume)
{ {
Super::SetSourceVolume(sound_->GetVolume() * volume); Super::SetSourceVolume(sound_->GetVolume() * volume);
//std::cout << "src volume " << sound_->GetVolume() * volume << std::endl;
} }
void audio::SoundSource::Update() void audio::SoundSource::Update()

View File

@ -12,9 +12,9 @@ App::App()
std::cout << "Initializing App..." << std::endl; std::cout << "Initializing App..." << std::endl;
#ifndef EMSCRIPTEN #ifndef EMSCRIPTEN
audiomaster_.SetMasterVolume(0.2f); audiomaster_.SetMasterVolume(1.0f);
#else #else
audiomaster_.SetMasterVolume(0.8f); audiomaster_.SetMasterVolume(2.0f);
#endif #endif
font_ = assets::CacheManager::GetFont("data/comic32.font"); font_ = assets::CacheManager::GetFont("data/comic32.font");

View File

@ -73,9 +73,9 @@ static void InitSDL()
#ifndef PG_GLES #ifndef PG_GLES
static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) { static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) {
//if (severity == 0x826b) if (severity == 0x826b)
// return; return;
//
////std::cout << message << std::endl; ////std::cout << message << std::endl;
fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
(type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),

View File

@ -0,0 +1,49 @@
#pragma once
#include <cstdint>
#include <btBulletDynamicsCommon.h>
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<int>(type));
obj->setUserIndex2(static_cast<int>(flags));
obj->setUserPointer(callback);
}
inline void GetObjectInfo(const btCollisionObject* obj, ObjectType& type, ObjectFlags& flags, ObjectCallback*& callback)
{
type = static_cast<ObjectType>(obj->getUserIndex());
flags = static_cast<ObjectFlags>(obj->getUserIndex2());
callback = static_cast<ObjectCallback*>(obj->getUserPointer());
}
}

View File

@ -1,16 +0,0 @@
#pragma once
namespace collision
{
enum ObjectType
{
OT_UNDEFINED = 0,
OT_MAP_STATIC = 1,
OT_MAP_DESTRUCTIBLE = 2,
};
}

View File

@ -1,11 +1,19 @@
#include "destroyed_object.hpp" #include "destroyed_object.hpp"
#include "utils/random.hpp"
game::DestroyedObject::DestroyedObject(World& world, std::unique_ptr<MapObjectCollision> col) game::DestroyedObject::DestroyedObject(World& world, std::unique_ptr<MapObjectCollision> col)
: Super(world, col->GetModel()->GetName()), col_(std::move(col)) : Super(world, col->GetModel()->GetName()), col_(std::move(col))
{ {
// remove after 30s auto destr_snd_str = col_->GetModel()->GetParam("destr_snd");
Schedule(30000, [this]() 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(); Remove();
}); });
} }

View File

@ -1,5 +1,6 @@
#include "drivable_vehicle.hpp" #include "drivable_vehicle.hpp"
#include "player_character.hpp" #include "player_character.hpp"
#include "utils/random.hpp"
game::DrivableVehicle::DrivableVehicle(World& world, std::string model_name, const glm::vec3& color) game::DrivableVehicle::DrivableVehicle(World& world, std::string model_name, const glm::vec3& color)
: Vehicle(world, std::move(model_name), color) : Vehicle(world, std::move(model_name), color)
@ -13,6 +14,8 @@ void game::DrivableVehicle::Use(PlayerCharacter& character, uint32_t target_id)
return; return;
character.SetVehicle(this, target_id); // seat idx is same as target_id 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) bool game::DrivableVehicle::SetPassenger(uint32_t seat_idx, ControllableCharacter* character)

View File

@ -52,6 +52,14 @@ void game::Entity::Attach(net::EntNum parentnum)
SendAttachMsg(); 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<net::SoundVolumeQ>(volume);
msg.Write<net::SoundPitchQ>(pitch);
}
void game::Entity::WriteNametag(net::OutMessage& msg) const void game::Entity::WriteNametag(net::OutMessage& msg) const
{ {
msg.Write(net::NameTag{nametag_}); msg.Write(net::NameTag{nametag_});

View File

@ -6,6 +6,7 @@
#include "transform_node.hpp" #include "transform_node.hpp"
#include "utils/defs.hpp" #include "utils/defs.hpp"
#include "utils/scheduler.hpp" #include "utils/scheduler.hpp"
#include "collision/object_info.hpp"
namespace game namespace game
{ {
@ -13,7 +14,7 @@ namespace game
class World; class World;
class Player; class Player;
class Entity : public net::MsgProducer, public Scheduler class Entity : public net::MsgProducer, public Scheduler, public collision::ObjectCallback
{ {
public: public:
Entity(World& world, net::EntType viewtype); Entity(World& world, net::EntType viewtype);
@ -33,11 +34,14 @@ public:
void Attach(net::EntNum parentnum); void Attach(net::EntNum parentnum);
net::EntNum GetParentNum() const { return 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; } void Remove() { removed_ = true; }
bool IsRemoved() const { return removed_; } bool IsRemoved() const { return removed_; }
const TransformNode& GetRoot() const { return root_; } const TransformNode& GetRoot() const { return root_; }
const Transform& GetRootTransform() const { return root_.local; } const Transform& GetRootTransform() const { return root_.local; }
float GetMaxDistance() const { return max_distance_; } float GetMaxDistance() const { return max_distance_; }
virtual ~Entity() = default; virtual ~Entity() = default;

View File

@ -3,7 +3,6 @@
#include <stdexcept> #include <stdexcept>
#include "assets/cache.hpp" #include "assets/cache.hpp"
#include "collision/object_type.hpp"
game::MapInstance::MapInstance(collision::DynamicsWorld& world, std::string mapname) game::MapInstance::MapInstance(collision::DynamicsWorld& world, std::string mapname)
: world_(world), mapname_(std::move(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; const bool destructible = (flags & MAPOBJ_DESTRUCTIBLE) != 0 && cshape != nullptr;
collision::ObjectType obj_type = collision::OT_MAP_STATIC;
btVector3 local_inertia(0, 0, 0); btVector3 local_inertia(0, 0, 0);
float mass = 0.0f; float mass = 0.0f;
collision::ObjectFlags oflags = 0;
if (destructible) if (destructible)
{ {
obj_type = collision::OT_MAP_DESTRUCTIBLE; oflags |= collision::OF_DESTRUCTIBLE;
} }
// prefer simple cshape which allow destruction // prefer simple cshape which allow destruction
@ -93,8 +91,8 @@ game::MapObjectCollision::MapObjectCollision(collision::DynamicsWorld& world,
offset_trans.position += trans.rotation * model_->GetColOffset(); offset_trans.position += trans.rotation * model_->GetColOffset();
body_->setWorldTransform(offset_trans.ToBtTransform()); body_->setWorldTransform(offset_trans.ToBtTransform());
body_->setUserIndex(static_cast<int>(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(), btBroadphaseProxy::StaticFilter, btBroadphaseProxy::AllFilter);
world_.GetBtWorld().addRigidBody(body_.get()); world_.GetBtWorld().addRigidBody(body_.get());
@ -119,7 +117,9 @@ void game::MapObjectCollision::Break()
// make new // make new
body_ = std::make_unique<btRigidBody>(btRigidBody::btRigidBodyConstructionInfo(mass, nullptr, shape, local_inertia)); body_ = std::make_unique<btRigidBody>(btRigidBody::btRigidBodyConstructionInfo(mass, nullptr, shape, local_inertia));
body_->setWorldTransform(trans); body_->setWorldTransform(trans);
body_->setUserIndex(static_cast<int>(collision::OT_UNDEFINED));
collision::SetObjectInfo(body_.get(), collision::OT_UNDEFINED, 0, this);
world_.GetBtWorld().addRigidBody(body_.get()); world_.GetBtWorld().addRigidBody(body_.get());
} }

View File

@ -3,6 +3,7 @@
#include "assets/map.hpp" #include "assets/map.hpp"
#include "collision/dynamicsworld.hpp" #include "collision/dynamicsworld.hpp"
#include "net/defs.hpp" #include "net/defs.hpp"
#include "collision/object_info.hpp"
namespace game namespace game
{ {
@ -14,7 +15,7 @@ enum MapObjectCollisionFlag : MapObjectCollisionFlags
MAPOBJ_DESTRUCTIBLE = 0x01, MAPOBJ_DESTRUCTIBLE = 0x01,
}; };
class MapObjectCollision class MapObjectCollision : public collision::ObjectCallback
{ {
public: public:
MapObjectCollision(collision::DynamicsWorld& world, std::shared_ptr<const assets::Model> model, MapObjectCollision(collision::DynamicsWorld& world, std::shared_ptr<const assets::Model> model,
@ -29,8 +30,7 @@ public:
btRigidBody& GetBtBody() { return *body_; } btRigidBody& GetBtBody() { return *body_; }
net::ObjNum GetNum() const { return num_; } net::ObjNum GetNum() const { return num_; }
virtual ~MapObjectCollision() override;
~MapObjectCollision();
private: private:
collision::DynamicsWorld& world_; collision::DynamicsWorld& world_;

View File

@ -82,10 +82,9 @@ void game::OpenWorld::DestructibleDestroyed(net::ObjNum num, std::unique_ptr<Map
{ {
auto& destroyed_obj = Spawn<DestroyedObject>(std::move(col)); auto& destroyed_obj = Spawn<DestroyedObject>(std::move(col));
// Schedule(100000, [this, objnum = num]() Schedule(10000, [this, num] {
// { RespawnObj(num);
// RespawnObj(objnum); });
// });
} }
std::optional<std::pair<game::Usable&, const game::UseTarget&>> game::OpenWorld::GetBestUseTarget(const glm::vec3& pos) const std::optional<std::pair<game::Usable&, const game::UseTarget&>> game::OpenWorld::GetBestUseTarget(const glm::vec3& pos) const

View File

@ -4,6 +4,7 @@
#include "net/utils.hpp" #include "net/utils.hpp"
#include "player.hpp" #include "player.hpp"
#include "player_input.hpp" #include "player_input.hpp"
#include "utils/random.hpp"
#include <iostream> #include <iostream>
@ -33,6 +34,8 @@ game::Vehicle::Vehicle(World& world, std::string model_name, const glm::vec3& co
body_ = std::make_unique<btRigidBody>(rb_info); body_ = std::make_unique<btRigidBody>(rb_info);
body_->setActivationState(DISABLE_DEACTIVATION); body_->setActivationState(DISABLE_DEACTIVATION);
collision::SetObjectInfo(body_.get(), collision::OT_ENTITY, collision::OF_NOTIFY_CONTACT, this);
// setup vehicle // setup vehicle
btRaycastVehicle::btVehicleTuning tuning; btRaycastVehicle::btVehicleTuning tuning;
vehicle_ = std::make_unique<btRaycastVehicle>(tuning, body_.get(), &world_.GetVehicleRaycaster()); vehicle_ = std::make_unique<btRaycastVehicle>(tuning, body_.get(), &world_.GetVehicleRaycaster());
@ -100,6 +103,7 @@ void game::Vehicle::Update()
root_.UpdateMatrix(); root_.UpdateMatrix();
flags_ = 0; flags_ = 0;
UpdateCrash();
ProcessInput(); ProcessInput();
UpdateWheels(); UpdateWheels();
@ -123,6 +127,27 @@ void game::Vehicle::SendInitData(Player& player, net::OutMessage& msg) const
msg.WriteAt(fields_pos, fields); 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) void game::Vehicle::SetInput(VehicleInputType type, bool enable)
{ {
if (enable) if (enable)
@ -293,6 +318,47 @@ void game::Vehicle::ProcessInput()
flags_ |= VF_BREAKING; 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() void game::Vehicle::UpdateWheels()
{ {
for (size_t i = 0; i < num_wheels_; ++i) for (size_t i = 0; i < num_wheels_; ++i)

View File

@ -40,6 +40,8 @@ public:
virtual void Update() override; virtual void Update() override;
virtual void SendInitData(Player& player, net::OutMessage& msg) const override; virtual void SendInitData(Player& player, net::OutMessage& msg) const override;
virtual void OnContact(float impulse) override;
void SetInput(VehicleInputType type, bool enable); void SetInput(VehicleInputType type, bool enable);
void SetInputs(VehicleInputFlags inputs) { in_ = inputs; } void SetInputs(VehicleInputFlags inputs) { in_ = inputs; }
@ -59,6 +61,7 @@ public:
private: private:
void ProcessInput(); void ProcessInput();
void UpdateCrash();
void UpdateWheels(); void UpdateWheels();
void UpdateSyncState(); void UpdateSyncState();
VehicleSyncFieldFlags WriteState(net::OutMessage& msg, const VehicleSyncState& base) const; VehicleSyncFieldFlags WriteState(net::OutMessage& msg, const VehicleSyncState& base) const;
@ -86,6 +89,11 @@ private:
size_t sync_current_ = 0; size_t sync_current_ = 0;
VehicleInputFlags in_ = 0; VehicleInputFlags in_ = 0;
float window_health_ = 10000.0f;
float crash_intensity_ = 0.0f;
size_t no_crash_frames_ = 0;
}; };
} // namespace game } // namespace game

View File

@ -14,8 +14,9 @@ using VehicleFlags = uint8_t;
enum VehicleFlag : VehicleFlags enum VehicleFlag : VehicleFlags
{ {
VF_NONE, VF_NONE,
VF_ACCELERATING = 1, VF_ACCELERATING = 0x01,
VF_BREAKING = 2, VF_BREAKING = 0x02,
VF_BROKENWINDOWS = 0x04,
}; };
struct VehicleSyncState struct VehicleSyncState

View File

@ -5,7 +5,7 @@
#include "assets/cache.hpp" #include "assets/cache.hpp"
#include "utils/allocnum.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)) 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, 1, delta_s);
GetBtWorld().stepSimulation(delta_s, 2, delta_s * 0.5f); GetBtWorld().stepSimulation(delta_s, 2, delta_s * 0.5f);
DetectDestructibleCollisions(); HandleContacts();
RunTasks(); RunTasks();
@ -96,7 +96,7 @@ void game::World::RespawnObj(net::ObjNum objnum)
} }
} }
void game::World::DetectDestructibleCollisions() void game::World::HandleContacts()
{ {
auto& bt_world = GetBtWorld(); auto& bt_world = GetBtWorld();
int numManifolds = bt_world.getDispatcher()->getNumManifolds(); int numManifolds = bt_world.getDispatcher()->getNumManifolds();
@ -104,41 +104,48 @@ void game::World::DetectDestructibleCollisions()
static std::vector<net::ObjNum> to_destroy; static std::vector<net::ObjNum> to_destroy;
to_destroy.clear(); 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<MapObjectCollision*>(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; // std::cout << "Checking " << numManifolds << " manifolds for destructible collisions..." << std::endl;
for (int i = 0; i < numManifolds; i++) for (int i = 0; i < numManifolds; i++)
{ {
btPersistentManifold* contactManifold = bt_world.getDispatcher()->getManifoldByIndexInternal(i); btPersistentManifold* contactManifold = bt_world.getDispatcher()->getManifoldByIndexInternal(i);
const btRigidBody* bodyA = static_cast<const btRigidBody*>(contactManifold->getBody0()); btRigidBody* body0 = const_cast<btRigidBody*>(static_cast<const btRigidBody*>(contactManifold->getBody0()));
const btRigidBody* bodyB = static_cast<const btRigidBody*>(contactManifold->getBody1()); btRigidBody* body1 = const_cast<btRigidBody*>(static_cast<const btRigidBody*>(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;
for (int j = 0; j < contactManifold->getNumContacts(); j++) for (int j = 0; j < contactManifold->getNumContacts(); j++)
{ {
const float break_threshold = 100.0f; // TODO: per-object threshold
btManifoldPoint& pt = contactManifold->getContactPoint(j); btManifoldPoint& pt = contactManifold->getContactPoint(j);
ProcessContact(body0, body1, pt);
if (pt.getAppliedImpulse() > break_threshold) ProcessContact(body1, body0, pt);
{
std::cout << "Destructible collision detected: impulse = " << pt.getAppliedImpulse() << std::endl;
MapObjectCollision* obj_col = static_cast<MapObjectCollision*>(destructibleBody->getUserPointer());
to_destroy.push_back(obj_col->GetNum());
const btRigidBody* otherBody = (destructibleBody == bodyA) ? bodyB : bodyA;
btRigidBody* otherBodyNonConst = const_cast<btRigidBody*>(otherBody);
otherBodyNonConst->applyCentralImpulse(pt.m_normalWorldOnB * pt.getAppliedImpulse() * 0.5f);
}
} }
} }

View File

@ -56,7 +56,7 @@ public:
virtual ~World() = default; virtual ~World() = default;
private: private:
void DetectDestructibleCollisions(); void HandleContacts();
void DestroyObject(net::ObjNum objnum); void DestroyObject(net::ObjNum objnum);

View File

@ -23,6 +23,8 @@ bool game::view::EntityView::ProcessMsg(net::EntMsgType type, net::InMessage& ms
return ReadNametag(msg); return ReadNametag(msg);
case net::EMSG_ATTACH: case net::EMSG_ATTACH:
return ReadAttach(msg); return ReadAttach(msg);
case net::EMSG_PLAYSOUND:
return ProcessPlaySoundMsg(msg);
default: default:
return false; return false;
} }
@ -82,6 +84,21 @@ bool game::view::EntityView::ReadAttach(net::InMessage& msg)
return msg.Read(parentnum_); return msg.Read(parentnum_);
} }
bool game::view::EntityView::ProcessPlaySoundMsg(net::InMessage& msg)
{
net::SoundName name;
float volume, pitch;
if (!msg.Read(name) || !msg.Read<net::SoundVolumeQ>(volume) || !msg.Read<net::SoundPitchQ>(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) void game::view::EntityView::DrawNametag(const DrawArgs& args)
{ {
if (nametag_.empty()) if (nametag_.empty())

View File

@ -50,6 +50,7 @@ public:
private: private:
bool ReadNametag(net::InMessage& msg); bool ReadNametag(net::InMessage& msg);
bool ReadAttach(net::InMessage& msg); bool ReadAttach(net::InMessage& msg);
bool ProcessPlaySoundMsg(net::InMessage& msg);
void DrawNametag(const DrawArgs& args); void DrawNametag(const DrawArgs& args);
void DrawAxes(const DrawArgs& args); void DrawAxes(const DrawArgs& args);

View File

@ -15,7 +15,7 @@ game::view::VehicleView::VehicleView(WorldView& world, net::InMessage& msg)
throw EntityInitError(); throw EntityInitError();
model_ = assets::CacheManager::GetVehicleModel("data/" + std::string(modelname) + ".veh"); model_ = assets::CacheManager::GetVehicleModel("data/" + std::string(modelname) + ".veh");
mesh_ = *model_->GetModel()->GetMesh();
auto& modelwheels = model_->GetWheels(); auto& modelwheels = model_->GetWheels();
wheels_.resize(modelwheels.size()); wheels_.resize(modelwheels.size());
@ -102,17 +102,26 @@ void game::view::VehicleView::Update(const UpdateInfo& info)
snd_accel_src_->Delete(); snd_accel_src_->Delete();
snd_accel_src_ = nullptr; 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) void game::view::VehicleView::Draw(const DrawArgs& args)
{ {
Super::Draw(args); Super::Draw(args);
// TOOD: chceck and fix for (const auto& surface : mesh_.surfaces)
const auto& model = *model_->GetModel();
const auto& mesh = *model.GetMesh();
for (const auto& surface : mesh.surfaces)
{ {
gfx::DrawSurfaceCmd cmd; gfx::DrawSurfaceCmd cmd;
cmd.surface = &surface; cmd.surface = &surface;

View File

@ -36,6 +36,7 @@ private:
private: private:
std::shared_ptr<const assets::VehicleModel> model_; std::shared_ptr<const assets::VehicleModel> model_;
assets::Mesh mesh_;
glm::vec4 color_; glm::vec4 color_;
game::VehicleSyncState sync_; game::VehicleSyncState sync_;
@ -48,6 +49,8 @@ private:
std::shared_ptr<const audio::Sound> snd_accel_; std::shared_ptr<const audio::Sound> snd_accel_;
audio::SoundSource* snd_accel_src_ = nullptr; audio::SoundSource* snd_accel_src_ = nullptr;
bool windows_broken_ = false;
}; };
} }

View File

@ -27,6 +27,12 @@ game::view::WorldView::WorldView(ClientSession& session, net::InMessage& msg) :
map_.EnableObj(objnum, false); 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) 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); map_.EnableObj(objnum, enable);
return true; return true;
} }
void game::view::WorldView::Cache(std::any val)
{
cache_.emplace_back(std::move(val));
}

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <any>
#include "assets/map.hpp" #include "assets/map.hpp"
#include "draw_args.hpp" #include "draw_args.hpp"
#include "net/defs.hpp" #include "net/defs.hpp"
@ -38,6 +40,8 @@ private:
bool ProcessObjDestroyOrRespawnMsg(net::InMessage& msg, bool enable); bool ProcessObjDestroyOrRespawnMsg(net::InMessage& msg, bool enable);
void Cache(std::any val);
private: private:
ClientSession& session_; ClientSession& session_;
@ -47,6 +51,8 @@ private:
float time_ = 0.0f; float time_ = 0.0f;
audio::Master& audiomaster_; audio::Master& audiomaster_;
std::vector<std::any> cache_;
}; };
} // namespace game::view } // namespace game::view

View File

@ -86,6 +86,7 @@ enum EntMsgType : uint8_t
EMSG_NAMETAG, EMSG_NAMETAG,
EMSG_ATTACH, EMSG_ATTACH,
EMSG_UPDATE, EMSG_UPDATE,
EMSG_PLAYSOUND,
}; };
using PositionElemQ = Quantized<uint32_t, -10000, 10000, 1>; using PositionElemQ = Quantized<uint32_t, -10000, 10000, 1>;
@ -110,6 +111,10 @@ using ColorQ = Quantized<uint8_t, 0, 1>;
using NameTag = FixedStr<64>; using NameTag = FixedStr<64>;
using SoundName = FixedStr<64>;
using SoundVolumeQ = Quantized<uint8_t, 0, 2>;
using SoundPitchQ = Quantized<uint8_t, 0, 2>;
using AnimBlendQ = Quantized<uint8_t, 0, 1>; using AnimBlendQ = Quantized<uint8_t, 0, 1>;
using AnimTimeQ = Quantized<uint8_t, 0, 1>; using AnimTimeQ = Quantized<uint8_t, 0, 1>;

8
src/utils/random.hpp Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <cstdlib>
inline float RandomFloat(float min, float max)
{
return min + (max - min) * static_cast<float>(rand() % 100) * 0.01f;
}