Vehicle seats & Remake openworld logic

This commit is contained in:
tovjemam 2026-02-15 17:30:11 +01:00
parent bbf2788627
commit 26bb9b802f
6 changed files with 472 additions and 521 deletions

View File

@ -39,7 +39,23 @@ std::shared_ptr<const assets::VehicleModel> assets::VehicleModel::LoadFromFile(c
veh->wheels_.emplace_back(wheel); veh->wheels_.emplace_back(wheel);
} }
else if (command == "loc")
{
std::string loc_name;
iss >> loc_name;
Transform& trans = veh->locations_[loc_name];
ParseTransform(iss, trans);
}
}); });
return veh; return veh;
} }
const Transform* assets::VehicleModel::GetLocation(const std::string& name) const
{
auto it = locations_.find(name);
if (it != locations_.end())
return &it->second;
return nullptr;
}

View File

@ -34,10 +34,12 @@ public:
const std::shared_ptr<const Model>& GetModel() const { return basemodel_; } const std::shared_ptr<const Model>& GetModel() const { return basemodel_; }
const std::vector<VehicleWheel>& GetWheels() const { return wheels_; } const std::vector<VehicleWheel>& GetWheels() const { return wheels_; }
const Transform* GetLocation(const std::string& name) const;
private: private:
std::shared_ptr<const Model> basemodel_; std::shared_ptr<const Model> basemodel_;
std::vector<VehicleWheel> wheels_; std::vector<VehicleWheel> wheels_;
std::map<std::string, Transform> locations_;
}; };

View File

@ -6,333 +6,92 @@
#include "player.hpp" #include "player.hpp"
#include "vehicle.hpp" #include "vehicle.hpp"
// /* --------------- awaitable machinery --------------- */ namespace game
// struct CoroWrapper
// {
// bool destroy = true;
// std::coroutine_handle<> handle;
// CoroWrapper(std::coroutine_handle<> h) : handle(h) {}
// ~CoroWrapper() { if (handle && destroy) handle.destroy(); }
// };
// struct CoroTask
// {
// std::shared_ptr<CoroWrapper> wrapper;
// explicit CoroTask(std::coroutine_handle<> h) : wrapper(std::make_shared<CoroWrapper>(h)) {}
// void operator()() const
// {
// if (wrapper && wrapper->handle && !wrapper->handle.done())
// {
// wrapper->destroy = false;
// wrapper->handle.resume();
// }
// }
// };
// class SchedulerAwaiter
// {
// Scheduler& sch_;
// int64_t when_;
// public:
// explicit SchedulerAwaiter(Scheduler& s, int64_t t) noexcept
// : sch_(s), when_(t) {}
// // --- await interface ---
// bool await_ready() const noexcept { return when_ <= 0; }
// void await_suspend(std::coroutine_handle<> h) const
// {
// sch_.Schedule(when_, CoroTask{h}); // resume coro later
// }
// void await_resume() const noexcept {}
// };
// /* --------------- task type (minimal) --------------- */
// template<typename T = void>
// struct task
// {
// struct promise_type
// {
// std::suspend_never initial_suspend() noexcept { return {}; }
// std::suspend_never final_suspend() noexcept { return {}; }
// task get_return_object() { return {}; }
// void return_void() {}
// void unhandled_exception() { std::terminate(); }
// };
// };
// static task<> BotThink(game::Vehicle& vehicle)
// {
// while (true)
// {
// vehicle.SetInput(game::VIN_FORWARD, true);
// co_await SchedulerAwaiter{vehicle, 1000};
// vehicle.SetInput(game::VIN_FORWARD, false);
// co_await SchedulerAwaiter{vehicle, 1000};
// }
// }
game::OpenWorld::OpenWorld() : World("openworld")
{ {
srand(time(NULL));
// // spawn test vehicles struct ControllableCharacter : public Character
// for (size_t i = 0; i < 3; ++i)
// {
// auto& vehicle = Spawn<Vehicle>("twingo", glm::vec3{0.3f, 0.3f, 0.3f});
// vehicle.SetPosition({ static_cast<float>(i * 3), 150.0f, 5.0f });
// // vehicle.SetInput(VIN_FORWARD, true);
// BotThink(vehicle);
// bots_.push_back(&vehicle);
// }
// spawn bots
for (size_t i = 0; i < 100; ++i)
{
SpawnBot();
}
}
void game::OpenWorld::Update(int64_t delta_time)
{ {
World::Update(delta_time); using Super = Character;
// for (auto bot : bots_) Vehicle* vehicle = nullptr;
// { bool is_driver = false;
// bot->SetInput(VIN_FORWARD, true);
// if (rand() % 1000 < 10) ControllableCharacter(World& world) : Character(world, CharacterInfo{}) {}
// {
// bool turn_left = rand() % 2;
// bot->SetInput(VIN_LEFT, turn_left);
// bot->SetInput(VIN_RIGHT, !turn_left);
// }
// else
// {
// bot->SetInput(VIN_LEFT, false);
// bot->SetInput(VIN_RIGHT, false);
// }
// auto pos = bot->GetPosition(); virtual void VehicleChanged() = 0;
// if (glm::distance(pos, glm::vec3(0.0f, 0.0f, 0.0f)) > 1000.0f || pos.z < -20.0f) };
// {
// bot->SetPosition({ rand() % 30 * 3 + 100.0f, 200.0f, 10.0f });
// }
// } struct PlayerCharacter : public ControllableCharacter
}
void game::OpenWorld::PlayerJoined(Player& player)
{ {
// SpawnVehicle(player); using Super = ControllableCharacter;
SpawnCharacter(player);
}
void game::OpenWorld::PlayerInput(Player& player, PlayerInputType type, bool enabled) Player& player;
{
// auto vehicle = player_vehicles_.at(&player);
// // player.SendChat("input zmenen: " + std::to_string(static_cast<int>(type)) + "=" + (enabled ? "1" : "0"));
// switch (type) PlayerCharacter(World& world, Player& player) : ControllableCharacter(world), player(player) {
// { VehicleChanged();
// case IN_FORWARD:
// vehicle->SetInput(VIN_FORWARD, enabled);
// break;
// case IN_BACKWARD:
// vehicle->SetInput(VIN_BACKWARD, enabled);
// break;
// case IN_LEFT:
// vehicle->SetInput(VIN_LEFT, enabled);
// break;
// case IN_RIGHT:
// vehicle->SetInput(VIN_RIGHT, enabled);
// break;
// case IN_DEBUG1:
// if (enabled)
// vehicle->SetPosition({ 100.0f, 100.0f, 5.0f });
// break;
// case IN_DEBUG2:
// if (enabled)
// SpawnVehicle(player);
// break;
// default:
// break;
// }
auto character = player_characters_.at(&player);
switch (type)
{
case IN_FORWARD:
character->SetInput(CIN_FORWARD, enabled);
break;
case IN_BACKWARD:
character->SetInput(CIN_BACKWARD, enabled);
break;
case IN_LEFT:
character->SetInput(CIN_LEFT, enabled);
break;
case IN_RIGHT:
character->SetInput(CIN_RIGHT, enabled);
break;
case IN_JUMP:
character->SetInput(CIN_JUMP, enabled);
break;
case IN_DEBUG1:
if (enabled)
character->SetPosition({ 100.0f, 100.0f, 5.0f });
break;
case IN_DEBUG2:
if (enabled)
SpawnCharacter(player);
break;
default:
break;
}
}
void game::OpenWorld::PlayerViewAnglesChanged(Player& player, float yaw, float pitch)
{
auto character = player_characters_.at(&player);
character->SetForwardYaw(yaw);
}
void game::OpenWorld::PlayerLeft(Player& player)
{
// RemoveVehicle(player);
RemoveCharacter(player);
}
void game::OpenWorld::RemoveVehicle(Player& player)
{
auto it = player_vehicles_.find(&player);
if (it != player_vehicles_.end())
{
it->second->Remove();
player_vehicles_.erase(it);
}
}
static glm::vec3 GetRandomColor()
{
glm::vec3 color;
// shittiest way to do it
for (int i = 0; i < 3; ++i)
{
net::ColorQ qcol;
qcol.value = rand() % 256;
color[i] = qcol.Decode();
} }
return color; virtual void VehicleChanged() override
}
game::Character& game::OpenWorld::SpawnRandomCharacter()
{
CharacterInfo cinfo;
auto& character = Spawn<Character>(cinfo);
// add clothes
character.AddClothes("tshirt", GetRandomColor());
character.AddClothes("shorts", GetRandomColor());
return character;
}
void game::OpenWorld::SpawnCharacter(Player& player)
{
RemoveCharacter(player);
auto& character = SpawnRandomCharacter();
character.SetNametag("player (" + std::to_string(character.GetEntNum()) + ")");
character.SetPosition({ 100.0f, 100.0f, 5.0f });
character.EnablePhysics(true);
player.SetCamera(character.GetEntNum());
player_characters_[&player] = &character;
}
void game::OpenWorld::RemoveCharacter(Player& player)
{
auto it = player_characters_.find(&player);
if (it != player_characters_.end())
{ {
it->second->Remove(); if (vehicle)
player_characters_.erase(it); {
player.SetCamera(vehicle->GetEntNum());
} }
} else
// static void BotThink(game::Vehicle& vehicle)
// {
// int direction = rand() % 3; // 0=none, 1=forward, 2=backward
// int steer = 0; // 0=none, 1=left, 2=right
// if (direction && rand() % 1000 < 300)
// {
// steer = (rand() % 2) ? 1 : 2;
// }
// game::VehicleInputFlags vin = 0;
// if (direction == 1)
// vin |= 1 << game::VIN_FORWARD;
// else if (direction == 2)
// vin |= 1 << game::VIN_BACKWARD;
// if (steer == 1)
// vin |= 1 << game::VIN_LEFT;
// else if (steer == 2)
// vin |= 1 << game::VIN_RIGHT;
// vehicle.SetInputs(vin);
// int time = 300 + (rand() % 3000);
// vehicle.Schedule(time, [&vehicle]() {
// BotThink(vehicle);
// } );
// }
struct BotThinkState
{
game::Vehicle& vehicle;
const assets::MapGraph& roads;
glm::vec3 seg_start;
std::deque<size_t> path;
bool gas = false;
size_t stuck_counter = 0;
glm::vec3 last_pos = glm::vec3(0.0f);
float speed_limit = 0.0f;
const char* state_str = "init";
BotThinkState(game::Vehicle& v, const assets::MapGraph& g, size_t n)
: vehicle(v), roads(g), path({n})
{ {
seg_start = v.GetPosition(); player.SetCamera(GetEntNum());
}
}
void UpdateInputs()
{
auto in = player.GetInput();
CharacterInputFlags c_in = 0;
VehicleInputFlags v_in = 0;
if (in & (1 << IN_FORWARD))
{
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 (vehicle && is_driver)
{
SetInputs(0);
vehicle->SetInputs(v_in);
}
else
{
SetInputs(c_in);
}
} }
}; };
static float GetTurnAngle2D(const glm::vec2& forward, const glm::vec2& to_target) static float GetTurnAngle2D(const glm::vec2& forward, const glm::vec2& to_target)
{ {
glm::vec2 forward_xy = glm::normalize(forward); glm::vec2 forward_xy = glm::normalize(forward);
@ -356,61 +115,124 @@ static float GetTurnAngle(const glm::vec3& pos, const glm::quat& rot, const glm:
return GetTurnAngle2D(forward_xy, to_target_xy); return GetTurnAngle2D(forward_xy, to_target_xy);
} }
static void SelectNextNode(BotThinkState& s) struct NpcCharacter : public ControllableCharacter
{ {
size_t node = s.path.back(); using Super = ControllableCharacter;
size_t num_nbs = s.roads.nodes[node].num_nbs;
// driver
const assets::MapGraph* roads;
glm::vec3 seg_start;
std::deque<size_t> path;
bool gas = false;
size_t stuck_counter = 0;
glm::vec3 last_pos = glm::vec3(0.0f);
float speed_limit = 0.0f;
NpcCharacter(World& world) : ControllableCharacter(world)
{
VehicleChanged();
}
virtual void VehicleChanged() override
{
roads = nullptr;
path.clear();
gas = false;
stuck_counter = 0;
speed_limit = 0.0f;
if (vehicle && is_driver)
{
roads = world_.GetMap().GetGraph("roads");
seg_start = vehicle->GetPosition();
size_t start_node = 0;
float min_dist = std::numeric_limits<float>().infinity();
for (size_t i = 0; i < roads->nodes.size(); ++i)
{
auto& node = roads->nodes[i];
float dist = glm::distance(node.position, seg_start);
if (dist < min_dist)
{
min_dist = dist;
start_node = i;
}
}
path.push_back(start_node);
}
else
{
}
}
void SelectNextNode()
{
size_t node = path.back();
size_t num_nbs = roads->nodes[node].num_nbs;
if (num_nbs < 1) if (num_nbs < 1)
{ {
const auto& pos = s.roads.nodes[node].position; const auto& pos = roads->nodes[node].position;
std::cout << "node " << node << " has no neighbors!!!1 position: " << pos.x << " " << pos.y << " " << pos.z << std::endl; std::cout << "node " << node << " has no neighbors!!!1 position: " << pos.x << " " << pos.y << " " << pos.z
<< std::endl;
throw std::runtime_error("no neighbors"); throw std::runtime_error("no neighbors");
} }
s.path.push_back(s.roads.nbs[s.roads.nodes[node].nbs + (rand() % num_nbs)]); path.push_back(roads->nbs[roads->nodes[node].nbs + (rand() % num_nbs)]);
} }
static void BotThink(std::shared_ptr<BotThinkState> s) virtual void Update() override
{ {
s->state_str = "path"; Super::Update();
glm::vec3 pos = s->vehicle.GetPosition(); VehicleThink();
glm::quat rot = s->vehicle.GetRotation(); }
void VehicleThink()
{
if (!is_driver || !vehicle || !roads)
return;
glm::vec3 pos = vehicle->GetPosition();
glm::quat rot = vehicle->GetRotation();
glm::vec3 forward = rot * glm::vec3{0.0f, 1.0f, 0.0f}; glm::vec3 forward = rot * glm::vec3{0.0f, 1.0f, 0.0f};
//glm::vec3 target = s->roads.nodes[s->node].position; // glm::vec3 target = s->roads.nodes[s->node].position;
// //
std::array<glm::vec3, 8> waypoints; std::array<glm::vec3, 8> waypoints;
while (s->path.size() < waypoints.size() - 1) while (path.size() < waypoints.size() - 1)
{ {
SelectNextNode(*s); SelectNextNode();
} }
glm::vec3 node_pos = s->roads.nodes[s->path[0]].position; glm::vec3 node_pos = roads->nodes[path[0]].position;
if (glm::distance(glm::vec2(pos), glm::vec2(node_pos)) < 6.0f && s->path.size() > 1) if (glm::distance(glm::vec2(pos), glm::vec2(node_pos)) < 6.0f && path.size() > 1)
{ {
s->seg_start = node_pos; seg_start = node_pos;
s->path.pop_front(); path.pop_front();
SelectNextNode(*s); SelectNextNode();
} }
glm::vec3 target_node_pos = s->roads.nodes[s->path[0]].position; glm::vec3 target_node_pos = roads->nodes[path[0]].position;
waypoints[0] = pos - glm::normalize(forward) * 3.0f; waypoints[0] = pos - glm::normalize(forward) * 3.0f;
waypoints[1] = pos; waypoints[1] = pos;
// find closest point on segment [seg_start -> target_node_pos] // find closest point on segment [seg_start -> target_node_pos]
glm::vec3 seg_end = target_node_pos; glm::vec3 seg_end = target_node_pos;
glm::vec3 seg_dir = seg_end - s->seg_start; glm::vec3 seg_dir = seg_end - seg_start;
float seg_len = glm::length(seg_dir); float seg_len = glm::length(seg_dir);
if (seg_len > 5.0f) if (seg_len > 5.0f)
{ {
glm::vec3 seg_dir_norm = seg_dir / seg_len; glm::vec3 seg_dir_norm = seg_dir / seg_len;
float t = glm::clamp(glm::dot(pos - s->seg_start, seg_dir_norm) / seg_len, 0.0f, 1.0f); float t = glm::clamp(glm::dot(pos - seg_start, seg_dir_norm) / seg_len, 0.0f, 1.0f);
waypoints[2] = s->seg_start + t * seg_dir; waypoints[2] = seg_start + t * seg_dir;
if (glm::distance(waypoints[1], target_node_pos) > 10.0f) if (glm::distance(waypoints[1], target_node_pos) > 10.0f)
{ {
waypoints[2] += seg_dir_norm * 10.0f; // look a bit ahead on segment waypoints[2] += seg_dir_norm * 10.0f; // look a bit ahead on segment
@ -419,7 +241,6 @@ static void BotThink(std::shared_ptr<BotThinkState> s)
{ {
waypoints[2] = target_node_pos; waypoints[2] = target_node_pos;
} }
} }
else else
{ {
@ -428,8 +249,8 @@ static void BotThink(std::shared_ptr<BotThinkState> s)
for (size_t i = 3; i < waypoints.size(); ++i) for (size_t i = 3; i < waypoints.size(); ++i)
{ {
size_t path_idx = glm::min(i - 3, s->path.size() - 1); size_t path_idx = glm::min(i - 3, path.size() - 1);
waypoints[i] = s->roads.nodes[s->path[path_idx]].position; waypoints[i] = roads->nodes[path[path_idx]].position;
} }
// decrease speed based on curvature // decrease speed based on curvature
@ -447,7 +268,9 @@ static void BotThink(std::shared_ptr<BotThinkState> s)
glm::vec2 dir2_xy = glm::vec2{dir2.x, dir2.y}; glm::vec2 dir2_xy = glm::vec2{dir2.x, dir2.y};
const float min_dir_length = 0.001f; const float min_dir_length = 0.001f;
float angle = glm::length(dir1_xy) > min_dir_length && glm::length(dir2_xy) > min_dir_length ? GetTurnAngle2D(dir1_xy, dir2_xy) : 0.0f; float angle = glm::length(dir1_xy) > min_dir_length && glm::length(dir2_xy) > min_dir_length
? GetTurnAngle2D(dir1_xy, dir2_xy)
: 0.0f;
// std::cout << "angle: " << angle << "\n"; // std::cout << "angle: " << angle << "\n";
float abs_angle = fabsf(angle); float abs_angle = fabsf(angle);
if (abs_angle > glm::radians(7.0f)) if (abs_angle > glm::radians(7.0f))
@ -456,8 +279,8 @@ static void BotThink(std::shared_ptr<BotThinkState> s)
// speed_limit *= dist_accum / 20.0f; // more distance to turn -> higher speed // speed_limit *= dist_accum / 20.0f; // more distance to turn -> higher speed
// speed_limit = glm::max(speed_limit, 20.0f); // speed_limit = glm::max(speed_limit, 20.0f);
// max_speed = glm::min(max_speed, speed_limit); // max_speed = glm::min(max_speed, speed_limit);
target_speed -= abs_angle * (base_speed / glm::pi<float>() / 2.0f) * 50.0f / glm::max(dist_accum - 1.0f, 1.0f); target_speed -=
abs_angle * (base_speed / glm::pi<float>() / 2.0f) * 50.0f / glm::max(dist_accum - 1.0f, 1.0f);
} }
if (dist_accum > 200.0f) if (dist_accum > 200.0f)
@ -465,58 +288,61 @@ static void BotThink(std::shared_ptr<BotThinkState> s)
} }
target_speed = glm::clamp(target_speed, 25.0f, 100.0f); target_speed = glm::clamp(target_speed, 25.0f, 100.0f);
s->speed_limit = target_speed; speed_limit = target_speed;
// std::cout << "target speed: " << target_speed << "\n"; // std::cout << "target speed: " << target_speed << "\n";
float angle = GetTurnAngle(pos, rot, waypoints[2]); float angle = GetTurnAngle(pos, rot, waypoints[2]);
if (glm::distance(pos, s->last_pos) < 2.0f) if (glm::distance(pos, last_pos) < 2.0f)
{ {
s->stuck_counter++; stuck_counter++;
if (s->stuck_counter > 20) if (stuck_counter > 20)
{ {
s->state_str = "stuck (reverse)"; //s->state_str = "stuck (reverse)";
s->stuck_counter = 0; //s->stuck_counter = 0;
s->vehicle.SetSteering(true, -angle); // try turn away //s->vehicle.SetSteering(true, -angle); // try turn away
s->vehicle.SetInputs(0); // stop //s->vehicle.SetInputs(0); // stop
// stuck, go reverse for a while //// stuck, go reverse for a while
s->vehicle.SetInput(game::VIN_BACKWARD, true); //s->vehicle.SetInput(game::VIN_BACKWARD, true);
s->vehicle.Schedule(2000, [s]() { //s->vehicle.Schedule(2000, [s]() {
s->vehicle.SetInput(game::VIN_BACKWARD, false); // s->vehicle.SetInput(game::VIN_BACKWARD, false);
BotThink(s); // BotThink(s);
} ); //});
vehicle->SetInputs(0); // stop
is_driver = false; // TODO: fix
return; return;
} }
} }
else else
{ {
s->stuck_counter = 0; stuck_counter = 0;
s->last_pos = pos; last_pos = pos;
} }
s->vehicle.SetSteering(true, angle); vehicle->SetSteering(true, angle);
game::VehicleInputFlags vin = 0; game::VehicleInputFlags vin = 0;
float speed = s->vehicle.GetSpeed(); float speed = vehicle->GetSpeed();
// if (glm::distance(pos, target) < 10.0f) // if (glm::distance(pos, target) < 10.0f)
// { // {
// target_speed = 20.0f; // target_speed = 20.0f;
// } // }
if (speed < target_speed * 0.9f && !s->gas) if (speed < target_speed * 0.9f && !gas)
{ {
s->gas = true; gas = true;
} }
else if (speed > target_speed * 1.1f && s->gas) else if (speed > target_speed * 1.1f && gas)
{ {
s->gas = false; gas = false;
} }
if (s->gas) if (gas)
{ {
vin |= 1 << game::VIN_FORWARD; vin |= 1 << game::VIN_FORWARD;
} }
@ -526,30 +352,153 @@ static void BotThink(std::shared_ptr<BotThinkState> s)
vin |= 1 << game::VIN_BACKWARD; vin |= 1 << game::VIN_BACKWARD;
} }
s->vehicle.SetInputs(vin); vehicle->SetInputs(vin);
}
};
s->vehicle.Schedule(rand() % 120 + 40, [s]() { struct VehicleSeat
BotThink(s); {
} ); glm::vec3 position;
ControllableCharacter* occupant;
};
struct DrivableVehicle : public Vehicle
{
std::vector<VehicleSeat> seats;
DrivableVehicle(World& world, std::string model_name, const glm::vec3& color)
: Vehicle(world, std::move(model_name), color)
{
InitSeats();
}
void InitSeats()
{
const auto& veh = *GetModel();
for (char c = '0'; c <= '9'; ++c)
{
auto trans = veh.GetLocation(std::string("seat") + c);
if (!trans)
break;
VehicleSeat seat{};
seat.position = trans->position;
seats.emplace_back(seat);
}
}
};
} // namespace game
game::OpenWorld::OpenWorld() : World("openworld")
{
srand(time(NULL));
// spawn bots
for (size_t i = 0; i < 100; ++i)
{
SpawnBot();
}
} }
static void BotNametagThink(std::shared_ptr<BotThinkState> s) void game::OpenWorld::Update(int64_t delta_time)
{ {
std::string nametag; World::Update(delta_time);
nametag += "s=" + std::to_string(static_cast<int>(s->vehicle.GetSpeed())) + "/" + std::to_string(static_cast<int>(s->speed_limit)) + " ";
nametag += "sc=" + std::to_string(s->stuck_counter) + " ";
nametag += "st=" + std::string(s->state_str); // + " ";
// nametag += "path="; }
// for (auto n : path)
// {
// nametag += std::to_string(n) + " ";
// }
s->vehicle.SetNametag(nametag);
s->vehicle.Schedule(240, [s]() { void game::OpenWorld::PlayerJoined(Player& player)
BotNametagThink(s); {
} ); CreatePlayerCharacter(player);
}
void game::OpenWorld::PlayerInput(Player& player, PlayerInputType type, bool enabled)
{
auto character = player_characters_.at(&player);
switch (type)
{
case IN_DEBUG1:
if (enabled)
{
if (character->vehicle)
character->vehicle->SetPosition({100.0f, 100.0f, 5.0f});
else
character->SetPosition({100.0f, 100.0f, 5.0f});
}
break;
case IN_DEBUG2:
if (enabled)
CreatePlayerCharacter(player);
break;
default:
character->UpdateInputs();
break;
}
}
void game::OpenWorld::PlayerViewAnglesChanged(Player& player, float yaw, float pitch)
{
auto character = player_characters_.at(&player);
character->SetForwardYaw(yaw);
}
void game::OpenWorld::PlayerLeft(Player& player)
{
RemovePlayerCharacter(player);
}
static glm::vec3 GetRandomColor()
{
glm::vec3 color;
// shittiest way to do it
for (int i = 0; i < 3; ++i)
{
net::ColorQ qcol;
qcol.value = rand() % 256;
color[i] = qcol.Decode();
}
return color;
}
template <class T, typename... TArgs>
static T& SpawnRandomCharacter(game::World& world, TArgs&&... args)
{
auto& character = world.Spawn<T>(std::forward<TArgs>(args)...);
// add clothes
character.AddClothes("tshirt", GetRandomColor());
character.AddClothes("shorts", GetRandomColor());
return character;
}
void game::OpenWorld::CreatePlayerCharacter(Player& player)
{
RemovePlayerCharacter(player);
auto& character = SpawnRandomCharacter<PlayerCharacter>(*this, player);
character.SetNametag("player (" + std::to_string(character.GetEntNum()) + ")");
character.SetPosition({100.0f, 100.0f, 5.0f});
character.EnablePhysics(true);
player.SetCamera(character.GetEntNum());
player_characters_[&player] = &character;
}
void game::OpenWorld::RemovePlayerCharacter(Player& player)
{
auto it = player_characters_.find(&player);
if (it != player_characters_.end())
{
it->second->Remove();
player_characters_.erase(it);
}
} }
static const char* GetRandomCarModel() static const char* GetRandomCarModel()
@ -558,54 +507,41 @@ static const char* GetRandomCarModel()
return vehicles[rand() % (sizeof(vehicles) / sizeof(vehicles[0]))]; return vehicles[rand() % (sizeof(vehicles) / sizeof(vehicles[0]))];
} }
static game::DrivableVehicle& SpawnRandomVehicle(game::World& world)
void game::OpenWorld::SpawnBot()
{ {
auto roads = GetMap().GetGraph("roads"); auto roads = world.GetMap().GetGraph("roads");
if (!roads) if (!roads)
{ {
std::cerr << "OpenWorld::SpawnBot: no roads graph in map\n"; throw std::runtime_error("SpawnRandomVehicle: no roads graph in map");
return;
} }
size_t start_node = rand() % roads->nodes.size(); size_t start_node = rand() % roads->nodes.size();
// auto color = glm::vec3{0.3f, 0.3f, 0.3f}; // auto color = glm::vec3{0.3f, 0.3f, 0.3f};
auto color = GetRandomColor(); auto color = GetRandomColor();
auto& vehicle = Spawn<Vehicle>(GetRandomCarModel(), color); auto& vehicle = world.Spawn<game::DrivableVehicle>(GetRandomCarModel(), color);
//vehicle.SetNametag("bot (" + std::to_string(vehicle.GetEntNum()) + ")"); // vehicle.SetNametag("bot (" + std::to_string(vehicle.GetEntNum()) + ")");
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});
auto think_state = std::make_shared<BotThinkState>(vehicle, *roads, start_node); return vehicle;
BotThink(think_state); }
vehicle.Schedule(rand() % 500, [think_state]() {
//BotNametagThink(think_state); void game::OpenWorld::SpawnBot()
} ); {
auto& vehicle = SpawnRandomVehicle(*this);
auto& driver = SpawnRandomCharacter<NpcCharacter>(*this);
// spawn driver
auto& driver = SpawnRandomCharacter();
driver.Attach(vehicle.GetEntNum()); driver.Attach(vehicle.GetEntNum());
driver.SetPosition(glm::vec3(0.0f, 0.0f, 0.0f));
if (vehicle.seats.size() > 0)
{
driver.SetPosition(vehicle.seats[0].position);
}
driver.SetMainAnim("vehicle_drive"); driver.SetMainAnim("vehicle_drive");
driver.SetYaw(0.5f * glm::pi<float>()); driver.SetYaw(0.5f * glm::pi<float>());
}
driver.vehicle = &vehicle;
void game::OpenWorld::SpawnVehicle(Player& player) driver.is_driver = true;
{ driver.VehicleChanged();
RemoveVehicle(player);
// spawn him car
// ranodm color
auto vehicle_name = GetRandomCarModel();
auto& vehicle = Spawn<Vehicle>(vehicle_name, GetRandomColor());
vehicle.SetNametag("player (" + std::to_string(vehicle.GetEntNum()) + ")");
vehicle.SetPosition({ 100.0f, 100.0f, 5.0f });
player.SetCamera(vehicle.GetEntNum());
player_vehicles_[&player] = &vehicle;
player.SendChat("dostals " + std::string(vehicle_name));
} }

View File

@ -7,6 +7,9 @@
namespace game namespace game
{ {
class PlayerCharacter;
class NpcCharacter;
class OpenWorld : public World class OpenWorld : public World
{ {
public: public:
@ -20,20 +23,14 @@ public:
virtual void PlayerLeft(Player& player) override; virtual void PlayerLeft(Player& player) override;
private: private:
void SpawnVehicle(Player& player); void CreatePlayerCharacter(Player& player);
void RemoveVehicle(Player& player); void RemovePlayerCharacter(Player& player);
Character& SpawnRandomCharacter();
void SpawnCharacter(Player& player);
void RemoveCharacter(Player& player);
void SpawnBot(); void SpawnBot();
private: private:
std::map<Player*, Vehicle*> player_vehicles_; std::map<Player*, PlayerCharacter*> player_characters_;
std::map<Player*, Character*> player_characters_; std::vector<NpcCharacter*> npcs_;
std::vector<Vehicle*> bots_;
}; };
} }

View File

@ -51,6 +51,8 @@ public:
void SetSteering(bool analog, float value = 0.0f); void SetSteering(bool analog, float value = 0.0f);
const std::shared_ptr<const assets::VehicleModel>& GetModel() const { return model_; }
virtual ~Vehicle(); virtual ~Vehicle();
private: private:

View File

@ -40,15 +40,13 @@ public:
Entity* GetEntity(net::EntNum entnum); Entity* GetEntity(net::EntNum entnum);
const assets::Map& GetMap() const { return *map_; }
const std::string& GetMapName() const { return mapname_; } const std::string& GetMapName() const { return mapname_; }
const std::map<net::EntNum, std::unique_ptr<Entity>>& GetEntities() const { return ents_; } const std::map<net::EntNum, std::unique_ptr<Entity>>& GetEntities() const { return ents_; }
const int64_t& GetTime() const { return time_ms_; } const int64_t& GetTime() const { return time_ms_; }
virtual ~World() = default; virtual ~World() = default;
protected:
const assets::Map& GetMap() const { return *map_; }
private: private:
std::shared_ptr<const assets::Map> map_; std::shared_ptr<const assets::Map> map_;
std::string mapname_; std::string mapname_;