Compare commits

..

3 Commits

Author SHA1 Message Date
tovjemam
d3848169e8 Generalize characters for animals, cow 2026-06-05 20:36:52 +02:00
tovjemam
c136ccf26d Generalize rideables 2026-06-04 16:04:36 +02:00
tovjemam
8e83f3635a Prevent duplicate map object destruction 2026-06-03 17:58:26 +02:00
28 changed files with 612 additions and 341 deletions

View File

@ -132,10 +132,12 @@ set(CLIENT_ONLY_SOURCES
) )
set(SERVER_ONLY_SOURCES set(SERVER_ONLY_SOURCES
"src/game/animal.hpp"
"src/game/animal.cpp"
"src/game/character.hpp" "src/game/character.hpp"
"src/game/character.cpp" "src/game/character.cpp"
"src/game/controllable_character.hpp" "src/game/cow.hpp"
"src/game/controllable_character.cpp" "src/game/cow.cpp"
"src/game/destroyed_object.hpp" "src/game/destroyed_object.hpp"
"src/game/destroyed_object.cpp" "src/game/destroyed_object.cpp"
"src/game/drivable_vehicle.hpp" "src/game/drivable_vehicle.hpp"
@ -146,6 +148,8 @@ set(SERVER_ONLY_SOURCES
"src/game/entity.cpp" "src/game/entity.cpp"
"src/game/game.hpp" "src/game/game.hpp"
"src/game/game.cpp" "src/game/game.cpp"
"src/game/human_character.hpp"
"src/game/human_character.cpp"
"src/game/mapinstance.hpp" "src/game/mapinstance.hpp"
"src/game/mapinstance.cpp" "src/game/mapinstance.cpp"
"src/game/marker.hpp" "src/game/marker.hpp"
@ -160,6 +164,8 @@ set(SERVER_ONLY_SOURCES
"src/game/player.cpp" "src/game/player.cpp"
"src/game/remote_menu.hpp" "src/game/remote_menu.hpp"
"src/game/remote_menu.cpp" "src/game/remote_menu.cpp"
"src/game/rideable.hpp"
"src/game/rideable.cpp"
"src/game/simple_entity.hpp" "src/game/simple_entity.hpp"
"src/game/simple_entity.cpp" "src/game/simple_entity.cpp"
"src/game/tuning_world.hpp" "src/game/tuning_world.hpp"

66
src/game/animal.cpp Normal file
View File

@ -0,0 +1,66 @@
#include "animal.hpp"
#include "player_character.hpp"
#include "input_mapping.hpp"
game::Animal::Animal(World& world, const CharacterTuning& tuning, const glm::vec3& position, float yaw)
: Character(world, tuning), Usable(root_.matrix), Rideable(*this, RIDEABLE_ANIMAL)
{
SetPosition(position);
SetYaw(yaw);
EnablePhysics(true);
collision::AddObjectFlags(&GetController()->GetBtGhost(), collision::OF_USABLE);
}
bool game::Animal::QueryUseTarget(PlayerCharacter& character, uint32_t target_id, UseTargetQueryResult& res)
{
if (character.GetRideable())
return false; // already in something
res.enabled = true;
res.error_text = nullptr;
bool seat_occupied = GetPassenger(target_id) != nullptr;
res.delay = seat_occupied ? 2.0f : 0.25f;
return true;
}
void game::Animal::Use(PlayerCharacter& character, uint32_t target_id)
{
if (target_id >= GetNumSeats())
return;
character.Ride(this, target_id);
}
void game::Animal::SetRideableInput(PlayerInputFlags in)
{
SetInputs(MapPlayerInputToCharacterInput(in));
}
void game::Animal::SetRideableYaw(float yaw)
{
SetForwardYaw(yaw);
}
void game::Animal::OnPassengerChanged(size_t seat_idx, HumanCharacter* passenger)
{
if (seat_idx == 0 && !passenger)
{
SetInputs(0);
}
}
void game::Animal::SetUseMessage(const std::string& message)
{
use_message_ = message;
}
void game::Animal::AddAnimalSeat(const glm::vec3& offset)
{
size_t seat_idx = AddSeat(offset);
use_targets_.emplace_back(this, static_cast<uint32_t>(seat_idx), offset + glm::vec3(0.0f, 0.0f, 1.0f),
use_message_ + " (místo " + std::to_string(seat_idx + 1) + ")");
}

31
src/game/animal.hpp Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include "character.hpp"
#include "usable.hpp"
#include "rideable.hpp"
namespace game
{
class Animal : public Character, public Usable, public Rideable
{
public:
Animal(World& world, const CharacterTuning& tuning, const glm::vec3& position, float yaw);
virtual bool QueryUseTarget(PlayerCharacter& character, uint32_t target_id, UseTargetQueryResult& res) override;
virtual void Use(PlayerCharacter& character, uint32_t target_id) override;
virtual void SetRideableInput(PlayerInputFlags in) override;
virtual void SetRideableYaw(float yaw) override;
protected:
virtual void OnPassengerChanged(size_t seat_idx, HumanCharacter* passenger) override;
void SetUseMessage(const std::string& message);
void AddAnimalSeat(const glm::vec3& offset);
private:
std::string use_message_;
};
}

View File

@ -9,9 +9,7 @@ game::Character::Character(World& world, const CharacterTuning& tuning)
{ {
z_offset_ = tuning_.shape.height * 0.5f + tuning_.shape.radius - 0.05f; z_offset_ = tuning_.shape.height * 0.5f + tuning_.shape.radius - 0.05f;
sk_ = SkeletonInstance(assets::CacheManager::GetSkeleton("data/human.sk"), &root_); sk_ = SkeletonInstance(assets::CacheManager::GetSkeleton("data/" + tuning.model_name + ".sk"), &root_);
animstate_.idle_anim_idx = GetAnim("idle");
animstate_.walk_anim_idx = GetAnim("walk");
} }
static bool Turn(float& angle, float target, float step) static bool Turn(float& angle, float target, float step)
@ -52,6 +50,9 @@ void game::Character::SendInitData(Player& player, net::OutMessage& msg) const
{ {
Super::SendInitData(player, msg); Super::SendInitData(player, msg);
// write model name
msg.Write(net::ModelName(tuning_.model_name));
// write clothes // write clothes
msg.Write<net::NumClothes>(tuning_.clothes.size()); msg.Write<net::NumClothes>(tuning_.clothes.size());
for (const auto& clothes : tuning_.clothes) for (const auto& clothes : tuning_.clothes)
@ -83,7 +84,7 @@ void game::Character::EnablePhysics(bool enable)
{ {
if (enable && !controller_) if (enable && !controller_)
{ {
controller_ = std::make_unique<CharacterPhysicsController>(world_.GetBtWorld(), bt_shape_); controller_ = std::make_unique<CharacterPhysicsController>(*this, world_.GetBtWorld(), bt_shape_);
SyncControllerTransform(); SyncControllerTransform();
} }
else if (!enable && controller_) else if (!enable && controller_)
@ -100,43 +101,27 @@ void game::Character::SetInput(CharacterInputType type, bool enable)
in_ &= ~(1 << type); in_ &= ~(1 << type);
} }
// static bool SweepCapsule(btCollisionWorld& world, const btCapsuleShapeZ& shape, const glm::vec3& start,
// const glm::vec3& end, float& hit_fraction, glm::vec3& hit_normal)
// {
// btVector3 bt_start(start.x, start.y, start.z);
// btVector3 bt_end(end.x, end.y, end.z);
// static const btMatrix3x3 bt_basis(1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
// btTransform start_transform(bt_basis, bt_start);
// btTransform end_transform(bt_basis, bt_end);
// btCollisionWorld::ClosestConvexResultCallback result_callback(bt_start, bt_end);
// world.convexSweepTest(&shape, start_transform, end_transform, result_callback);
// if (result_callback.hasHit())
// {
// hit_fraction = result_callback.m_closestHitFraction;
// hit_normal = glm::vec3(result_callback.m_hitNormalWorld.x(), result_callback.m_hitNormalWorld.y(),
// result_callback.m_hitNormalWorld.z());
// return true;
// }
// return false;
// }
void game::Character::SetPosition(const glm::vec3& position) void game::Character::SetPosition(const glm::vec3& position)
{ {
root_.local.position = position; root_.local.position = position;
SyncControllerTransform(); SyncControllerTransform();
} }
void game::Character::SetMainAnim(const std::string& anim_name) void game::Character::SetIdleAnim(const std::string& anim_name)
{ {
animstate_.idle_anim_idx = GetAnim(anim_name); animstate_.idle_anim_idx = GetAnim(anim_name);
} }
void game::Character::SetWalkAnim(const std::string& anim_name)
{
animstate_.walk_anim_idx = GetAnim(anim_name);
}
void game::Character::SetRunAnim(const std::string& anim_name)
{
animstate_.run_anim_idx = GetAnim(anim_name);
}
void game::Character::SyncControllerTransform() void game::Character::SyncControllerTransform()
{ {
if (!controller_) if (!controller_)
@ -300,48 +285,13 @@ game::CharacterSyncFieldFlags game::Character::WriteState(net::OutMessage& msg,
return fields; return fields;
} }
void game::Character::Move(glm::vec3& velocity, float t)
{
// glm::vec3 u = velocity * t; // Calculate the movement vector
// btCollisionWorld& bt_world = world_.GetBtWorld();
// btCapsuleShapeZ bt_shape(shape_.radius, shape_.height);
// const int MAX_ITERS = 16;
// for (size_t i = 0; i < MAX_ITERS && glm::dot(u, u) > 0.0f; ++i)
// {
// // printf("Entity::Move: Iteration %zu, u = (%f, %f, %f)\n", i, u.x, u.y, u.z);
// glm::vec3 to = position_ + u;
// float hit_fraction = 1.0f;
// glm::vec3 hit_normal;
// bool hit = SweepCapsule(bt_world, bt_shape, position_, to, hit_fraction, hit_normal);
// // Update the position based on the hit fraction
// position_ += hit_fraction * u;
// if (!hit)
// break;
// // hit_normal *= -1.0f; // Invert the normal to point outwards
// // printf("Entity::Move: Hit detected, hit_fraction = %f, hit_normal = (%f, %f, %f)\n", hit_fraction,
// // hit_normal.x, hit_normal.y, hit_normal.z);
// u -= hit_fraction * u; // Reduce the movement vector by the hit fraction
// u -= glm::dot(u, hit_normal) * hit_normal; // Reflect the velocity along the hit normal
// velocity -= glm::dot(velocity, hit_normal) * hit_normal; // Adjust the velocity
// }
}
assets::AnimIdx game::Character::GetAnim(const std::string& name) const assets::AnimIdx game::Character::GetAnim(const std::string& name) const
{ {
return sk_.GetSkeleton()->GetAnimationIdx(name); return sk_.GetSkeleton()->GetAnimationIdx(name);
} }
game::CharacterPhysicsController::CharacterPhysicsController(btDynamicsWorld& bt_world, btCapsuleShapeZ& bt_shape) game::CharacterPhysicsController::CharacterPhysicsController(Character& character, btDynamicsWorld& bt_world, btCapsuleShapeZ& bt_shape)
: bt_world_(bt_world), bt_character_(&bt_ghost_, &bt_shape, 0.3f, btVector3(0, 0, 1)) : character_(character), bt_world_(bt_world), bt_character_(&bt_ghost_, &bt_shape, 0.3f, btVector3(0, 0, 1))
{ {
btTransform start_transform; btTransform start_transform;
start_transform.setIdentity(); start_transform.setIdentity();
@ -349,6 +299,8 @@ game::CharacterPhysicsController::CharacterPhysicsController(btDynamicsWorld& bt
bt_ghost_.setCollisionShape(&bt_shape); bt_ghost_.setCollisionShape(&bt_shape);
bt_ghost_.setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); bt_ghost_.setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT);
collision::SetObjectInfo(&bt_ghost_, collision::OT_ENTITY, 0, &character);
bt_world_.addCollisionObject(&bt_ghost_, btBroadphaseProxy::CharacterFilter, bt_world_.addCollisionObject(&bt_ghost_, btBroadphaseProxy::CharacterFilter,
btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter); btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter);
// bt_world.addCollisionObject(&bt_ghost_); // bt_world.addCollisionObject(&bt_ghost_);

View File

@ -23,10 +23,12 @@ enum CharacterInputType
CIN_SPRINT, CIN_SPRINT,
}; };
class Character;
class CharacterPhysicsController class CharacterPhysicsController
{ {
public: public:
CharacterPhysicsController(btDynamicsWorld& bt_world, btCapsuleShapeZ& bt_shape); CharacterPhysicsController(Character& character, btDynamicsWorld& bt_world, btCapsuleShapeZ& bt_shape);
DELETE_COPY_MOVE(CharacterPhysicsController) DELETE_COPY_MOVE(CharacterPhysicsController)
btKinematicCharacterController& GetBtController() { return bt_character_; } btKinematicCharacterController& GetBtController() { return bt_character_; }
@ -37,6 +39,7 @@ public:
~CharacterPhysicsController(); ~CharacterPhysicsController();
private: private:
Character& character_;
btDynamicsWorld& bt_world_; btDynamicsWorld& bt_world_;
btPairCachingGhostObject bt_ghost_; btPairCachingGhostObject bt_ghost_;
btKinematicCharacterController bt_character_; btKinematicCharacterController bt_character_;
@ -57,16 +60,20 @@ public:
const CharacterTuning& GetTuning() const { return tuning_; } const CharacterTuning& GetTuning() const { return tuning_; }
void EnablePhysics(bool enable); void EnablePhysics(bool enable);
CharacterPhysicsController* GetController() { return controller_.get(); }
void SetInput(CharacterInputType type, bool enable); void SetInput(CharacterInputType type, bool enable);
void SetInputs(CharacterInputFlags inputs) { in_ = inputs; } void SetInputs(CharacterInputFlags inputs) { in_ = inputs; }
void SetForwardYaw(float yaw) { forward_yaw_ = yaw; } void SetForwardYaw(float yaw) { forward_yaw_ = yaw; }
float GetForwardYaw() const { return forward_yaw_; }
void SetYaw(float yaw) { yaw_ = yaw; } void SetYaw(float yaw) { yaw_ = yaw; }
void SetPosition(const glm::vec3& position); void SetPosition(const glm::vec3& position);
void SetMainAnim(const std::string& anim_name); void SetIdleAnim(const std::string& anim_name);
void SetWalkAnim(const std::string& anim_name);
void SetRunAnim(const std::string& anim_name);
~Character() override = default; ~Character() override = default;
@ -79,8 +86,6 @@ private:
void SendUpdateMsg(); void SendUpdateMsg();
CharacterSyncFieldFlags WriteState(net::OutMessage& msg, const CharacterSyncState& base) const; CharacterSyncFieldFlags WriteState(net::OutMessage& msg, const CharacterSyncState& base) const;
void Move(glm::vec3& velocity, float t);
assets::AnimIdx GetAnim(const std::string& name) const; assets::AnimIdx GetAnim(const std::string& name) const;
private: private:

View File

@ -23,6 +23,7 @@ struct CharacterConfigClothes
struct CharacterTuning struct CharacterTuning
{ {
CharacterShape shape = CharacterShape(0.3f, 0.75f); CharacterShape shape = CharacterShape(0.3f, 0.75f);
std::string model_name;
std::vector<CharacterConfigClothes> clothes; std::vector<CharacterConfigClothes> clothes;
}; };

View File

@ -1,54 +0,0 @@
#include "controllable_character.hpp"
#include "drivable_vehicle.hpp"
game::ControllableCharacter::ControllableCharacter(World& world, const CharacterTuning& tuning) : Character(world, tuning) {}
void game::ControllableCharacter::SetVehicle(DrivableVehicle* vehicle, uint32_t seat)
{
if ((vehicle && vehicle_) || (!vehicle && !vehicle_))
return;
if (vehicle)
{
if (!vehicle->SetPassenger(seat, this))
return;
auto seat_loc = vehicle->GetModel()->GetLocation("seat" + std::to_string(seat));
if (seat_loc)
SetPosition(seat_loc->position);
EnablePhysics(false);
Attach(vehicle->GetEntNum());
SetMainAnim(seat == 0 ? "vehicle_drive" : "vehicle_passenger");
SetYaw(0.5f * glm::pi<float>());
}
else
{
vehicle_->SetPassenger(seat_idx_, nullptr);
EnablePhysics(true);
glm::vec3 seat_loc = vehicle_->GetModel()->GetLocation("seat" + std::to_string(seat_idx_))->position;
seat_loc.x += glm::sign(seat_loc.x) * 0.5f; // to the side
glm::vec3 pos = vehicle_->GetRoot().matrix * glm::vec4(seat_loc, 1.0f);
pos.z += 0.5f;
SetPosition(pos);
Attach(0);
SetMainAnim("idle");
// SetYaw(0.0f);
}
vehicle_ = vehicle;
seat_idx_ = seat;
is_driver_ = vehicle && seat == 0;
VehicleChanged();
}
game::ControllableCharacter::~ControllableCharacter()
{
if (vehicle_)
{
vehicle_->SetPassenger(seat_idx_, nullptr);
}
}

View File

@ -1,32 +0,0 @@
#pragma once
#include "character.hpp"
namespace game
{
class DrivableVehicle;
class ControllableCharacter : public Character
{
public:
using Super = Character;
ControllableCharacter(World& world, const CharacterTuning& tuning);
void SetVehicle(DrivableVehicle* vehicle, uint32_t seat);
DrivableVehicle* GetVehicle() const { return vehicle_; }
bool IsDriver() const { return is_driver_; }
~ControllableCharacter() override;
protected:
virtual void VehicleChanged() = 0;
size_t seat_idx_ = 0;
bool is_driver_ = false;
DrivableVehicle* vehicle_ = nullptr;
};
}

20
src/game/cow.cpp Normal file
View File

@ -0,0 +1,20 @@
#include "cow.hpp"
static game::CharacterTuning GetCowTuning()
{
game::CharacterTuning ct{};
ct.shape = game::CharacterShape(0.3f, 0.75f);
ct.model_name = "cow";
return ct;
}
game::Cow::Cow(World& world, const glm::vec3& position, float yaw) : Animal(world, GetCowTuning(), position, yaw)
{
SetUseMessage("vlízt na krávu");
AddAnimalSeat(glm::vec3(0.0f, -0.098926f, 0.447576f));
AddAnimalSeat(glm::vec3(0.0f, 0.394027f, 0.458536f));
SetIdleAnim("idle");
SetWalkAnim("walk");
}

20
src/game/cow.hpp Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include "animal.hpp"
namespace game
{
class Cow : public Animal
{
public:
Cow(World& world, const glm::vec3& position, float yaw);
private:
};
}

View File

@ -1,8 +1,9 @@
#include "drivable_vehicle.hpp" #include "drivable_vehicle.hpp"
#include "player_character.hpp" #include "player_character.hpp"
#include "utils/random.hpp" #include "utils/random.hpp"
#include "input_mapping.hpp"
game::DrivableVehicle::DrivableVehicle(World& world, const VehicleTuning& tuning) : Vehicle(world, tuning), Usable(GetRoot().matrix) game::DrivableVehicle::DrivableVehicle(World& world, const VehicleTuning& tuning) : Vehicle(world, tuning), Usable(GetRoot().matrix), Rideable(*this, RIDEABLE_VEHICLE)
{ {
InitSeats(); InitSeats();
OnPhysicsChanged(); OnPhysicsChanged();
@ -11,7 +12,7 @@ game::DrivableVehicle::DrivableVehicle(World& world, const VehicleTuning& tuning
void game::DrivableVehicle::Update() void game::DrivableVehicle::Update()
{ {
float daytime = world_.GetDayTime(); float daytime = world_.GetDayTime();
SetLightsOn(seats_[0].occupant && (daytime < 6.0f || daytime > 18.0f)); SetLightsOn(GetPassenger(0) && (daytime < 6.0f || daytime > 18.0f));
Super::Update(); Super::Update();
@ -31,13 +32,13 @@ void game::DrivableVehicle::OnPhysicsChanged()
bool game::DrivableVehicle::QueryUseTarget(PlayerCharacter& character, uint32_t target_id, UseTargetQueryResult& res) bool game::DrivableVehicle::QueryUseTarget(PlayerCharacter& character, uint32_t target_id, UseTargetQueryResult& res)
{ {
if (character.GetVehicle()) if (character.GetRideable())
return false; // already in vehicle return false; // already in something
res.enabled = true; res.enabled = true;
res.error_text = nullptr; res.error_text = nullptr;
bool seat_occupied = seats_[target_id].occupant != nullptr; bool seat_occupied = GetPassenger(target_id) != nullptr;
res.delay = seat_occupied ? 2.0f : 0.25f; res.delay = seat_occupied ? 2.0f : 0.25f;
return true; return true;
@ -45,52 +46,25 @@ bool game::DrivableVehicle::QueryUseTarget(PlayerCharacter& character, uint32_t
void game::DrivableVehicle::Use(PlayerCharacter& character, uint32_t target_id) void game::DrivableVehicle::Use(PlayerCharacter& character, uint32_t target_id)
{ {
if (target_id >= seats_.size()) if (target_id >= GetNumSeats())
return; return;
character.SetVehicle(this, target_id); // seat idx is same as target_id character.Ride(this, target_id);
PlaySound("cardoor", 1.0f, RandomFloat(0.9f, 1.1f)); PlaySound("cardoor", 1.0f, RandomFloat(0.9f, 1.1f));
} }
bool game::DrivableVehicle::SetPassenger(uint32_t seat_idx, ControllableCharacter* character)
void game::DrivableVehicle::SetRideableInput(PlayerInputFlags in)
{ {
if (seat_idx >= seats_.size()) SetInputs(MapPlayerInputToVehicleInput(in));
return false;
auto& seat_info = seats_[seat_idx];
if (seat_info.occupant == character)
return true; // already sitting here
if (seat_info.occupant && character)
{
seat_info.occupant->SetVehicle(nullptr, 0); // remove current occupant
} }
seat_info.occupant = character; void game::DrivableVehicle::OnPassengerChanged(size_t seat_idx, HumanCharacter* passenger)
if (seat_idx == 0)
{ {
if (!character) if (seat_idx == 0 && !passenger)
{ {
// clear inputs // driver left
SetInputs(0); SetInputs(0);
SetSteering(false, 0.0f);
}
}
return true;
}
game::DrivableVehicle::~DrivableVehicle()
{
// remove occupants
for (auto& seat : seats_)
{
if (seat.occupant)
seat.occupant->SetVehicle(nullptr, 0);
} }
} }
@ -120,13 +94,11 @@ void game::DrivableVehicle::InitSeats()
if (!trans) if (!trans)
break; break;
VehicleSeat seat{}; auto position = trans->position;
seat.position = trans->position; size_t seat_idx = AddSeat(position);
seat.position.z += 1.0f; // the original pos is for animated character which is under vehicle
seats_.emplace_back(seat);
uint32_t id = seats_.size() - 1; position.z += 1.0f; // the original pos is for animated character which is under vehicle
use_targets_.emplace_back(this, id, seat.position, std::string()); use_targets_.emplace_back(this, static_cast<uint32_t>(seat_idx), position, std::string());
} }
UpdateUseTargetNames(); UpdateUseTargetNames();

View File

@ -2,18 +2,13 @@
#include "vehicle.hpp" #include "vehicle.hpp"
#include "usable.hpp" #include "usable.hpp"
#include "controllable_character.hpp" #include "rideable.hpp"
#include "human_character.hpp"
namespace game namespace game
{ {
struct VehicleSeat class DrivableVehicle : public Vehicle, public Usable, public Rideable
{
glm::vec3 position;
ControllableCharacter* occupant;
};
class DrivableVehicle : public Vehicle, public Usable
{ {
public: public:
using Super = Vehicle; using Super = Vehicle;
@ -29,19 +24,15 @@ public:
virtual bool QueryUseTarget(PlayerCharacter& character, uint32_t target_id, UseTargetQueryResult& res) override; virtual bool QueryUseTarget(PlayerCharacter& character, uint32_t target_id, UseTargetQueryResult& res) override;
virtual void Use(PlayerCharacter& character, uint32_t target_id) override; virtual void Use(PlayerCharacter& character, uint32_t target_id) override;
bool SetPassenger(uint32_t seat_idx, ControllableCharacter* character); virtual void SetRideableInput(PlayerInputFlags in) override;
size_t GetNumSeats() const { return seats_.size(); } protected:
ControllableCharacter* GetPassenger(size_t idx) const { return seats_[idx].occupant; } virtual void OnPassengerChanged(size_t seat_idx, HumanCharacter* passenger) override;
~DrivableVehicle() override;
private: private:
void InitSeats(); void InitSeats();
void UpdateUseTargetNames(); void UpdateUseTargetNames();
private:
std::vector<VehicleSeat> seats_;
}; };
} }

View File

@ -5,7 +5,7 @@
game::EnterableWorld::EnterableWorld(std::string mapname) : World(std::move(mapname)) {} game::EnterableWorld::EnterableWorld(std::string mapname) : World(std::move(mapname)) {}
game::PlayerCharacter& game::EnterableWorld::InsertPlayer(Player& player, const CharacterTuning& tuning, const glm::vec3& pos, float yaw) game::PlayerCharacter& game::EnterableWorld::InsertPlayer(Player& player, const HumanCharacterTuning& tuning, const glm::vec3& pos, float yaw)
{ {
return CreatePlayerCharacter(player, tuning, pos, yaw); return CreatePlayerCharacter(player, tuning, pos, yaw);
} }
@ -23,8 +23,8 @@ void game::EnterableWorld::PlayerInput(Player& player, PlayerInputType type, boo
// case IN_DEBUG1: // case IN_DEBUG1:
// if (enabled) // if (enabled)
// { // {
// if (character->GetVehicle()) // if (character->GetVehicleOld())
// character->GetVehicle()->SetPosition({100.0f, 100.0f, 5.0f}); // character->GetVehicleOld()->SetPosition({100.0f, 100.0f, 5.0f});
// else // else
// character->SetPosition({100.0f, 100.0f, 5.0f}); // character->SetPosition({100.0f, 100.0f, 5.0f});
// } // }
@ -62,7 +62,7 @@ game::PlayerCharacter* game::EnterableWorld::GetPlayerCharacter(Player& player)
return it->second; return it->second;
} }
game::PlayerCharacter& game::EnterableWorld::CreatePlayerCharacter(Player& player, const CharacterTuning& tuning, const glm::vec3& position, float yaw) game::PlayerCharacter& game::EnterableWorld::CreatePlayerCharacter(Player& player, const HumanCharacterTuning& tuning, const glm::vec3& position, float yaw)
{ {
RemovePlayerCharacter(player); RemovePlayerCharacter(player);

View File

@ -7,7 +7,7 @@ namespace game
class Player; class Player;
class PlayerCharacter; class PlayerCharacter;
class CharacterTuning; class HumanCharacterTuning;
class DrivableVehicle; class DrivableVehicle;
class EnterableWorld : public World class EnterableWorld : public World
@ -16,7 +16,7 @@ public:
EnterableWorld(std::string mapname); EnterableWorld(std::string mapname);
// events // events
virtual PlayerCharacter& InsertPlayer(Player& player, const CharacterTuning& tuning, const glm::vec3& pos, float yaw); virtual PlayerCharacter& InsertPlayer(Player& player, const HumanCharacterTuning& tuning, const glm::vec3& pos, float yaw);
virtual void PlayerInput(Player& player, PlayerInputType type, bool enabled); virtual void PlayerInput(Player& player, PlayerInputType type, bool enabled);
virtual void PlayerViewAnglesChanged(Player& player, float yaw, float pitch); virtual void PlayerViewAnglesChanged(Player& player, float yaw, float pitch);
virtual void RemovePlayer(Player& player); virtual void RemovePlayer(Player& player);
@ -28,7 +28,7 @@ public:
PlayerCharacter* GetPlayerCharacter(Player& player); PlayerCharacter* GetPlayerCharacter(Player& player);
private: private:
PlayerCharacter& CreatePlayerCharacter(Player& player, const CharacterTuning& tuning, const glm::vec3& position, float yaw); PlayerCharacter& CreatePlayerCharacter(Player& player, const HumanCharacterTuning& tuning, const glm::vec3& position, float yaw);
void RemovePlayerCharacter(Player& player); void RemovePlayerCharacter(Player& player);
private: private:

View File

@ -52,7 +52,7 @@ void game::Game::PlayerJoined(Player& player)
player_info.world = openworld_.get(); player_info.world = openworld_.get();
player.SetWorld(openworld_.get()); player.SetWorld(openworld_.get());
CharacterTuning tuning{}; HumanCharacterTuning tuning{};
tuning.clothes.push_back({"tshirt", GetRandomColor24()}); tuning.clothes.push_back({"tshirt", GetRandomColor24()});
tuning.clothes.push_back({"shorts", GetRandomColor24()}); tuning.clothes.push_back({"shorts", GetRandomColor24()});
@ -142,7 +142,7 @@ game::PlayerCharacter& game::Game::MovePlayerToWorld(PlayerGameInfo& player_info
auto& old_world = *player_info.world; auto& old_world = *player_info.world;
auto old_character = old_world.GetPlayerCharacter(player); auto old_character = old_world.GetPlayerCharacter(player);
auto& tuning = old_character->GetTuning(); auto& tuning = old_character->GetHumanTuning();
old_world.RemovePlayer(player); old_world.RemovePlayer(player);
player.SetWorld(&new_world); player.SetWorld(&new_world);
@ -180,7 +180,7 @@ void game::Game::MoveVehicleToWorld(DrivableVehicle& vehicle, EnterableWorld& ne
auto& player_info = GetPlayerInfo(*player); auto& player_info = GetPlayerInfo(*player);
auto& new_character = MovePlayerToWorld(player_info, new_world, glm::vec3(0.0f), 0.0f); auto& new_character = MovePlayerToWorld(player_info, new_world, glm::vec3(0.0f), 0.0f);
new_character.SetVehicle(&new_vehicle, i); new_character.Ride(&new_vehicle, i);
} }
vehicle.Remove(); vehicle.Remove();

View File

@ -0,0 +1,74 @@
#include "human_character.hpp"
#include "drivable_vehicle.hpp"
static game::CharacterTuning GetCharacterTuning(const game::HumanCharacterTuning& tuning)
{
game::CharacterTuning ct{};
ct.shape = game::CharacterShape(0.3f, 0.75f);
ct.model_name = "human";
ct.clothes = tuning.clothes;
return ct;
}
game::HumanCharacter::HumanCharacter(World& world, const HumanCharacterTuning& tuning) : Character(world, GetCharacterTuning(tuning)), human_tuning_(tuning)
{
SetIdleAnim("idle");
SetWalkAnim("walk");
}
void game::HumanCharacter::SetRideable(Rideable* rideable, size_t seat_idx)
{
if (rideable == rideable_ && seat_idx == seat_idx_)
return;
if (rideable)
{
SetPosition(rideable->GetSeatOffset(seat_idx));
EnablePhysics(false);
Attach(rideable->GetEntity().GetEntNum());
SetIdleAnim((rideable->GetRideableType() == RIDEABLE_VEHICLE && seat_idx == 0) ? "vehicle_drive" : "vehicle_passenger");
SetYaw(rideable->GetRideableType() == RIDEABLE_VEHICLE ? 0.5f * glm::pi<float>() : 1.0f * glm::pi<float>());
}
else
{
EnablePhysics(true);
glm::vec3 seat_loc = rideable_->GetSeatOffset(seat_idx_);
seat_loc.x += glm::sign(seat_loc.x) * 0.5f; // to the side
glm::vec3 pos = rideable_->GetEntity().GetRoot().matrix * glm::vec4(seat_loc, 1.0f);
pos.z += 0.5f;
SetPosition(pos);
Attach(0);
SetIdleAnim("idle");
}
rideable_ = rideable;
vehicle_ = dynamic_cast<DrivableVehicle*>(rideable);
seat_idx_ = seat_idx;
is_driver_ = rideable && seat_idx_ == 0;
OnRideableChanged();
}
void game::HumanCharacter::Ride(Rideable* rideable, size_t seat_idx)
{
if (rideable_)
{
rideable_->SetPassenger(seat_idx_, 0);
}
if (rideable)
{
rideable->SetPassenger(seat_idx, this);
}
}
game::HumanCharacter::~HumanCharacter()
{
Ride(nullptr, 0); // exit rideable
}

View File

@ -0,0 +1,48 @@
#pragma once
#include "character.hpp"
namespace game
{
struct HumanCharacterTuning
{
std::vector<CharacterConfigClothes> clothes;
};
class Rideable;
class DrivableVehicle;
class HumanCharacter : public Character
{
public:
using Super = Character;
HumanCharacter(World& world, const HumanCharacterTuning& tuning);
const HumanCharacterTuning& GetHumanTuning() const { return human_tuning_; }
void SetRideable(Rideable* rideable, size_t seat_idx); // called by Rideable!!
void Ride(Rideable* rideable, size_t seat_idx);
Rideable* GetRideable() const { return rideable_; }
DrivableVehicle* GetVehicle() const { return vehicle_; }
size_t GeatSeatIdx() const { return seat_idx_; }
bool IsDriver() const { return is_driver_; }
virtual ~HumanCharacter() override;
protected:
virtual void OnRideableChanged() {}
private:
HumanCharacterTuning human_tuning_;
Rideable* rideable_ = nullptr;
DrivableVehicle* vehicle_ = nullptr;
size_t seat_idx_ = 0;
bool is_driver_ = false;
};
}

View File

@ -0,0 +1,52 @@
#include "player_input.hpp"
#include "vehicle.hpp"
#include "character.hpp"
namespace game
{
inline game::CharacterInputFlags MapPlayerInputToCharacterInput(game::PlayerInputFlags in)
{
game::CharacterInputFlags c_in = 0;
if (in & (1 << game::IN_FORWARD))
c_in |= 1 << game::CIN_FORWARD;
if (in & (1 << game::IN_BACKWARD))
c_in |= 1 << game::CIN_BACKWARD;
if (in & (1 << game::IN_LEFT))
c_in |= 1 << game::CIN_LEFT;
if (in & (1 << game::IN_RIGHT))
c_in |= 1 << game::CIN_RIGHT;
if (in & (1 << game::IN_JUMP))
c_in |= 1 << game::CIN_JUMP;
if (in & (1 << game::IN_SPRINT))
c_in |= 1 << game::CIN_SPRINT;
return c_in;
}
static game::VehicleInputFlags MapPlayerInputToVehicleInput(game::PlayerInputFlags in)
{
game::VehicleInputFlags vin = 0;
if (in & (1 << game::IN_FORWARD))
vin |= 1 << game::VIN_FORWARD;
if (in & (1 << game::IN_BACKWARD))
vin |= 1 << game::VIN_BACKWARD;
if (in & (1 << game::IN_LEFT))
vin |= 1 << game::VIN_LEFT;
if (in & (1 << game::IN_RIGHT))
vin |= 1 << game::VIN_RIGHT;
return vin;
}
}

View File

@ -5,11 +5,24 @@
#include <array> #include <array>
#include <iostream> #include <iostream>
game::NpcCharacter::NpcCharacter(World& world, const CharacterTuning& tuning) : Super(world, tuning) { game::NpcCharacter::NpcCharacter(World& world, const HumanCharacterTuning& tuning) : Super(world, tuning) {
VehicleChanged(); UpdateVehicleState();
} }
void game::NpcCharacter::VehicleChanged() void game::NpcCharacter::Update()
{
Super::Update();
if (GetVehicle() && IsDriver())
VehicleThink();
}
void game::NpcCharacter::OnRideableChanged()
{
UpdateVehicleState();
}
void game::NpcCharacter::UpdateVehicleState()
{ {
roads_ = nullptr; roads_ = nullptr;
path_.clear(); path_.clear();
@ -85,9 +98,11 @@ static float GetTurnAngle(const glm::vec3& pos, const glm::quat& rot, const glm:
void game::NpcCharacter::VehicleThink() void game::NpcCharacter::VehicleThink()
{ {
if (!IsDriver() || !GetVehicle() || !roads_) if (!roads_)
return; return;
auto vehicle = GetVehicle();
if (vehicle_state_ == NVT_REVERSING) if (vehicle_state_ == NVT_REVERSING)
{ {
if (reversing_frames_ > 0) if (reversing_frames_ > 0)
@ -98,13 +113,13 @@ void game::NpcCharacter::VehicleThink()
{ {
vehicle_state_ = NVT_NORMAL; vehicle_state_ = NVT_NORMAL;
stuck_counter_ = 0; stuck_counter_ = 0;
vehicle_->SetInput(game::VIN_BACKWARD, false); vehicle->SetInput(game::VIN_BACKWARD, false);
} }
return; return;
} }
const auto& vehicle_trans = GetVehicle()->GetRootTransform(); const auto& vehicle_trans = vehicle->GetRootTransform();
const glm::vec3& pos = vehicle_trans.position; const glm::vec3& pos = vehicle_trans.position;
const glm::quat& rot = vehicle_trans.rotation; const glm::quat& rot = vehicle_trans.rotation;
@ -220,13 +235,13 @@ void game::NpcCharacter::VehicleThink()
// BotThink(s); // BotThink(s);
//}); //});
vehicle_->SetSteering(true, -angle); // try turn away while reversing vehicle->SetSteering(true, -angle); // try turn away while reversing
vehicle_->SetInputs(0); // stop vehicle->SetInputs(0); // stop
vehicle_->SetInput(game::VIN_BACKWARD, true); vehicle->SetInput(game::VIN_BACKWARD, true);
vehicle_state_ = NVT_REVERSING; vehicle_state_ = NVT_REVERSING;
reversing_frames_ = 50; // reverse for 50 frames reversing_frames_ = 50; // reverse for 50 frames
// GetVehicle()->SetInputs(0); // stop // GetVehicleOld()->SetInputs(0); // stop
// is_driver_ = false; // TODO: fix // is_driver_ = false; // TODO: fix
return; return;
} }
@ -237,11 +252,11 @@ void game::NpcCharacter::VehicleThink()
last_pos_ = pos; last_pos_ = pos;
} }
GetVehicle()->SetSteering(true, angle); vehicle->SetSteering(true, angle);
game::VehicleInputFlags vin = 0; game::VehicleInputFlags vin = 0;
float speed = GetVehicle()->GetSpeed(); float speed = vehicle->GetSpeed();
// if (glm::distance(pos, target) < 10.0f) // if (glm::distance(pos, target) < 10.0f)
// { // {
@ -267,5 +282,5 @@ void game::NpcCharacter::VehicleThink()
vin |= 1 << game::VIN_BACKWARD; vin |= 1 << game::VIN_BACKWARD;
} }
GetVehicle()->SetInputs(vin); vehicle->SetInputs(vin);
} }

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "assets/map.hpp" #include "assets/map.hpp"
#include "controllable_character.hpp" #include "human_character.hpp"
namespace game namespace game
{ {
@ -14,24 +14,20 @@ enum NpcVehicleThinkState
NVT_REVERSING, NVT_REVERSING,
}; };
class NpcCharacter : public ControllableCharacter class NpcCharacter : public HumanCharacter
{ {
public: public:
using Super = ControllableCharacter; using Super = HumanCharacter;
NpcCharacter(World& world, const CharacterTuning& tuning); NpcCharacter(World& world, const HumanCharacterTuning& tuning);
virtual void VehicleChanged() override; virtual void Update() override;
protected:
virtual void Update() override virtual void OnRideableChanged() override;
{
Super::Update();
VehicleThink();
}
private: private:
void UpdateVehicleState();
void SelectNextNode(); void SelectNextNode();
void VehicleThink(); void VehicleThink();

View File

@ -11,6 +11,7 @@
#include "marker.hpp" #include "marker.hpp"
#include "tuning_world.hpp" #include "tuning_world.hpp"
#include "game.hpp" #include "game.hpp"
#include "cow.hpp"
namespace game namespace game
{ {
@ -87,6 +88,9 @@ game::OpenWorld::OpenWorld(Game& game) : EnterableWorld("openworld"), game_(game
CreateTuningGarage(loc.transform.position, glm::eulerAngles(loc.transform.rotation).x); CreateTuningGarage(loc.transform.position, glm::eulerAngles(loc.transform.rotation).x);
} }
// cow
auto& cow = Spawn<Cow>(glm::vec3(0.0f, 0.0f, 2.0f), 0.0f);
cow.SetNametag("no ty krávo");
} }
void game::OpenWorld::Update(int64_t delta_time) void game::OpenWorld::Update(int64_t delta_time)
@ -169,12 +173,12 @@ void game::OpenWorld::SpawnBot()
auto& vehicle = SpawnRandomVehicle(); auto& vehicle = SpawnRandomVehicle();
vehicle.SetPosition(roads->nodes[start_node].position + glm::vec3{0.0f, 0.0f, 5.0f}); vehicle.SetPosition(roads->nodes[start_node].position + glm::vec3{0.0f, 0.0f, 5.0f});
CharacterTuning npc_tuning; HumanCharacterTuning npc_tuning;
npc_tuning.clothes.push_back({ "tshirt", GetRandomColor24() }); npc_tuning.clothes.push_back({ "tshirt", GetRandomColor24() });
npc_tuning.clothes.push_back({ "shorts", GetRandomColor24() }); npc_tuning.clothes.push_back({ "shorts", GetRandomColor24() });
auto& driver = Spawn<NpcCharacter>(npc_tuning); auto& driver = Spawn<NpcCharacter>(npc_tuning);
driver.SetVehicle(&vehicle, 0); driver.Ride(&vehicle, 0);
} }
void game::OpenWorld::CreateTuningGarage(const glm::vec3& position, float yaw) void game::OpenWorld::CreateTuningGarage(const glm::vec3& position, float yaw)

View File

@ -1,13 +1,12 @@
#include "player_character.hpp" #include "player_character.hpp"
#include "world.hpp" #include "world.hpp"
#include "input_mapping.hpp"
game::PlayerCharacter::PlayerCharacter(World& world, Player& player, const CharacterTuning& tuning) : Super(world, tuning), player_(&player) game::PlayerCharacter::PlayerCharacter(World& world, Player& player, const HumanCharacterTuning& tuning) : Super(world, tuning), player_(&player)
{ {
EnablePhysics(true); EnablePhysics(true);
VehicleChanged(); UpdatePlayerCamera();
SetNametag(player.GetName()); SetNametag(player.GetName());
SendUseTargetInfo(); SendUseTargetInfo();
} }
@ -15,23 +14,11 @@ void game::PlayerCharacter::Update()
{ {
UpdateUseTarget(); UpdateUseTarget();
Super::Update(); Super::Update();
}
void game::PlayerCharacter::VehicleChanged() if (GetRideable() && IsDriver())
{ {
if (!player_) GetRideable()->SetRideableYaw(GetForwardYaw());
return;
if (vehicle_)
{
player_->SetCamera(vehicle_->GetEntNum());
} }
else
{
player_->SetCamera(GetEntNum());
}
UpdateInputs();
} }
void game::PlayerCharacter::ProcessInput(PlayerInputType type, bool enabled) void game::PlayerCharacter::ProcessInput(PlayerInputType type, bool enabled)
@ -53,58 +40,43 @@ void game::PlayerCharacter::DetachFromPlayer()
player_ = nullptr; player_ = nullptr;
} }
void game::PlayerCharacter::OnRideableChanged()
{
UpdatePlayerCamera();
UpdateInputs();
}
void game::PlayerCharacter::UpdatePlayerCamera()
{
if (!player_)
return;
if (auto rideable = GetRideable(); rideable)
{
player_->SetCamera(rideable->GetEntity().GetEntNum());
}
else
{
player_->SetCamera(GetEntNum());
}
}
void game::PlayerCharacter::UpdateInputs() void game::PlayerCharacter::UpdateInputs()
{ {
auto in = player_ ? player_->GetInput() : 0; auto in = player_ ? player_->GetInput() : 0;
CharacterInputFlags c_in = 0;
VehicleInputFlags v_in = 0;
if (in & (1 << IN_FORWARD)) if (auto rideable = GetRideable(); rideable)
{
c_in |= 1 << CIN_FORWARD;
v_in |= 1 << VIN_FORWARD;
}
if (in & (1 << IN_BACKWARD))
{
c_in |= 1 << CIN_BACKWARD;
v_in |= 1 << VIN_BACKWARD;
}
if (in & (1 << IN_LEFT))
{
c_in |= 1 << CIN_LEFT;
v_in |= 1 << VIN_LEFT;
}
if (in & (1 << IN_RIGHT))
{
c_in |= 1 << CIN_RIGHT;
v_in |= 1 << VIN_RIGHT;
}
if (in & (1 << IN_JUMP))
{
c_in |= 1 << CIN_JUMP;
}
if (in & (1 << IN_SPRINT))
{
c_in |= 1 << CIN_SPRINT;
}
if (vehicle_)
{ {
SetInputs(0); SetInputs(0);
if (is_driver_) if (IsDriver())
{ {
vehicle_->SetInputs(v_in); rideable->SetRideableInput(in);
} }
} }
else else
{ {
SetInputs(c_in); SetInputs(MapPlayerInputToCharacterInput(in));
} }
} }
@ -142,8 +114,9 @@ void game::PlayerCharacter::UseChanged(bool enabled)
{ {
if (!use_target_) if (!use_target_)
{ {
if (vehicle_ && enabled) // exit rideable if not target
SetVehicle(nullptr, 0);// no use target and in vehicle -> exit if (enabled && GetRideable())
Ride(nullptr, 0);
return; return;
} }

View File

@ -1,4 +1,4 @@
#include "controllable_character.hpp" #include "human_character.hpp"
#include "drivable_vehicle.hpp" #include "drivable_vehicle.hpp"
#include "player.hpp" #include "player.hpp"
@ -7,24 +7,26 @@
namespace game namespace game
{ {
class PlayerCharacter : public ControllableCharacter class PlayerCharacter : public HumanCharacter
{ {
public: public:
using Super = ControllableCharacter; using Super = HumanCharacter;
PlayerCharacter(World& world, Player& player, const CharacterTuning& tuning); PlayerCharacter(World& world, Player& player, const HumanCharacterTuning& tuning);
virtual void Update() override; virtual void Update() override;
virtual void VehicleChanged() override;
void ProcessInput(PlayerInputType type, bool enabled); void ProcessInput(PlayerInputType type, bool enabled);
void DetachFromPlayer(); void DetachFromPlayer();
Player* GetPlayer() const { return player_; } Player* GetPlayer() const { return player_; }
protected:
virtual void OnRideableChanged() override;
private: private:
void UpdatePlayerCamera();
void UpdateInputs(); void UpdateInputs();
void UpdateUseTarget(); void UpdateUseTarget();

69
src/game/rideable.cpp Normal file
View File

@ -0,0 +1,69 @@
#include "rideable.hpp"
#include <stdexcept>
game::Rideable::Rideable(Entity& entity, RideableType type) : entity_(entity), type_(type) {}
void game::Rideable::SetPassenger(size_t seat_idx, HumanCharacter* passenger)
{
if (seat_idx >= seats_.size())
throw std::runtime_error("Invalid seat index");
auto& seat = seats_[seat_idx];
if (seat.passenger == passenger)
return; // already sitting here
if (seat.passenger)
{
seat.passenger->SetRideable(nullptr, 0); // remove current passenger
}
seat.passenger = passenger;
if (passenger)
{
passenger->SetRideable(this, seat_idx);
}
OnPassengerChanged(seat_idx, passenger);
}
game::HumanCharacter* game::Rideable::GetPassenger(size_t seat_idx)
{
if (seat_idx >= seats_.size())
return nullptr;
return seats_[seat_idx].passenger;
}
const glm::vec3& game::Rideable::GetSeatOffset(size_t seat_idx)
{
if (seat_idx >= seats_.size())
throw std::runtime_error("Invalid seat index");
return seats_[seat_idx].offset;
}
void game::Rideable::KickAll()
{
for (size_t i = 0; i < seats_.size(); ++i)
{
if (seats_[i].passenger)
SetPassenger(i, nullptr);
}
}
game::Rideable::~Rideable()
{
// kick passengers
KickAll();
}
size_t game::Rideable::AddSeat(const glm::vec3& offset)
{
RideableSeat seat{};
seat.offset = offset;
seats_.emplace_back(seat);
return seats_.size() - 1;
}

54
src/game/rideable.hpp Normal file
View File

@ -0,0 +1,54 @@
#pragma once
#include "human_character.hpp"
#include "player_input.hpp"
namespace game
{
enum RideableType
{
RIDEABLE_NONE,
RIDEABLE_VEHICLE,
RIDEABLE_ANIMAL,
};
struct RideableSeat
{
glm::vec3 offset;
HumanCharacter* passenger;
};
class Rideable
{
public:
Rideable(Entity& entity, RideableType type);
void SetPassenger(size_t seat_idx, HumanCharacter* passenger);
HumanCharacter* GetPassenger(size_t seat_idx);
const glm::vec3& GetSeatOffset(size_t seat_idx);
size_t GetNumSeats() const { return seats_.size(); }
void KickAll();
virtual void SetRideableInput(PlayerInputFlags in) {}
virtual void SetRideableYaw(float yaw) {}
RideableType GetRideableType() const { return type_; }
Entity& GetEntity() { return entity_; }
const Entity& GetEntity() const { return entity_; }
virtual ~Rideable();
protected:
size_t AddSeat(const glm::vec3& offset);
virtual void OnPassengerChanged(size_t seat_idx, HumanCharacter* passenger) {}
private:
Entity& entity_;
RideableType type_;
std::vector<RideableSeat> seats_;
};
}

View File

@ -29,8 +29,6 @@ public:
private: private:
void Setup(); void Setup();
void AddTuningGroupSelect(game::RemoteMenu& menu, const VehicleTuningGroup& group);
void OpenMainTuningMenu(); void OpenMainTuningMenu();
void OpenGroupMenu(const VehicleTuningGroup& group); void OpenGroupMenu(const VehicleTuningGroup& group);
std::string GetCurrentPartId(const std::string& group_id); std::string GetCurrentPartId(const std::string& group_id);

View File

@ -112,11 +112,11 @@ struct UseTargetAabbCallback : public btBroadphaseAabbCallback
UseTargetAabbCallback(game::PlayerCharacter& character, game::UseTargetQueryResult& res) : character(character), pos(character.GetRoot().GetGlobalPosition()), best_res(res) UseTargetAabbCallback(game::PlayerCharacter& character, game::UseTargetQueryResult& res) : character(character), pos(character.GetRoot().GetGlobalPosition()), best_res(res)
{ {
auto vehicle = character.GetVehicle(); auto rideable_entity = dynamic_cast<game::Entity*>(character.GetRideable());
if (vehicle) if (rideable_entity)
{ {
radius = 5.0f; radius = 5.0f;
pos = vehicle->GetRoot().GetGlobalPosition(); pos = rideable_entity->GetRoot().GetGlobalPosition();
} }
} }
@ -245,6 +245,9 @@ void game::World::HandleContacts()
void game::World::DestroyObject(net::ObjNum objnum) void game::World::DestroyObject(net::ObjNum objnum)
{ {
if (destroyed_objs_.contains(objnum))
return;
SendObjDestroyedMsg(objnum); SendObjDestroyedMsg(objnum);
destroyed_objs_.insert(objnum); destroyed_objs_.insert(objnum);

View File

@ -6,7 +6,12 @@
game::view::CharacterView::CharacterView(WorldView& world, net::InMessage& msg) : EntityView(world, msg), ubo_(sk_) game::view::CharacterView::CharacterView(WorldView& world, net::InMessage& msg) : EntityView(world, msg), ubo_(sk_)
{ {
basemodel_ = assets::CacheManager::GetModel("data/human.mdl"); // read model name
net::ModelName model_name;
if (!msg.Read(model_name))
throw EntityInitError();;
basemodel_ = assets::CacheManager::GetModel("data/" + std::string(model_name) + ".mdl");
sk_ = SkeletonInstance(basemodel_->GetSkeleton(), &root_); sk_ = SkeletonInstance(basemodel_->GetSkeleton(), &root_);
ubo_.Update(); ubo_.Update();
ubo_valid_ = true; ubo_valid_ = true;
@ -90,17 +95,17 @@ void game::view::CharacterView::Draw(const DrawArgs& args)
//args.dlist.AddBeam(start, end, 0xFF007700, 0.05f); //args.dlist.AddBeam(start, end, 0xFF007700, 0.05f);
//// draw bones debug //// draw bones debug
// const auto& bone_nodes = sk_.GetBoneNodes(); const auto& bone_nodes = sk_.GetBoneNodes();
// for (const auto& bone_node : bone_nodes) for (const auto& bone_node : bone_nodes)
//{ {
// if (!bone_node.parent) if (!bone_node.parent)
// continue; continue;
// glm::vec3 p0 = bone_node.parent->matrix[3]; glm::vec3 p0 = bone_node.parent->matrix[3];
// glm::vec3 p1 = bone_node.matrix[3]; glm::vec3 p1 = bone_node.matrix[3];
// args.dlist.AddBeam(p0, p1, 0xFF00EEEE, 0.01f); args.dlist.AddBeam(p0, p1, 0xFF00EEEE, 0.01f);
//} }
// update skinning matrices // update skinning matrices
if (!ubo_valid_) if (!ubo_valid_)