Vehicle seats & Remake openworld logic
This commit is contained in:
parent
bbf2788627
commit
26bb9b802f
@ -39,7 +39,23 @@ std::shared_ptr<const assets::VehicleModel> assets::VehicleModel::LoadFromFile(c
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const Transform* assets::VehicleModel::GetLocation(const std::string& name) const
|
||||
{
|
||||
auto it = locations_.find(name);
|
||||
if (it != locations_.end())
|
||||
return &it->second;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -34,10 +34,12 @@ public:
|
||||
|
||||
const std::shared_ptr<const Model>& GetModel() const { return basemodel_; }
|
||||
const std::vector<VehicleWheel>& GetWheels() const { return wheels_; }
|
||||
const Transform* GetLocation(const std::string& name) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<const Model> basemodel_;
|
||||
std::vector<VehicleWheel> wheels_;
|
||||
std::map<std::string, Transform> locations_;
|
||||
|
||||
|
||||
};
|
||||
|
||||
@ -6,333 +6,92 @@
|
||||
#include "player.hpp"
|
||||
#include "vehicle.hpp"
|
||||
|
||||
// /* --------------- awaitable machinery --------------- */
|
||||
|
||||
// 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")
|
||||
namespace game
|
||||
{
|
||||
srand(time(NULL));
|
||||
|
||||
// // spawn test vehicles
|
||||
// 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)
|
||||
struct ControllableCharacter : public Character
|
||||
{
|
||||
SpawnBot();
|
||||
using Super = Character;
|
||||
|
||||
Vehicle* vehicle = nullptr;
|
||||
bool is_driver = false;
|
||||
|
||||
ControllableCharacter(World& world) : Character(world, CharacterInfo{}) {}
|
||||
|
||||
virtual void VehicleChanged() = 0;
|
||||
};
|
||||
|
||||
struct PlayerCharacter : public ControllableCharacter
|
||||
{
|
||||
using Super = ControllableCharacter;
|
||||
|
||||
Player& player;
|
||||
|
||||
PlayerCharacter(World& world, Player& player) : ControllableCharacter(world), player(player) {
|
||||
VehicleChanged();
|
||||
}
|
||||
|
||||
virtual void VehicleChanged() override
|
||||
{
|
||||
if (vehicle)
|
||||
{
|
||||
player.SetCamera(vehicle->GetEntNum());
|
||||
}
|
||||
else
|
||||
{
|
||||
player.SetCamera(GetEntNum());
|
||||
}
|
||||
}
|
||||
|
||||
void game::OpenWorld::Update(int64_t delta_time)
|
||||
void UpdateInputs()
|
||||
{
|
||||
World::Update(delta_time);
|
||||
auto in = player.GetInput();
|
||||
CharacterInputFlags c_in = 0;
|
||||
VehicleInputFlags v_in = 0;
|
||||
|
||||
// for (auto bot : bots_)
|
||||
// {
|
||||
// bot->SetInput(VIN_FORWARD, true);
|
||||
|
||||
// if (rand() % 1000 < 10)
|
||||
// {
|
||||
// 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();
|
||||
// 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 });
|
||||
// }
|
||||
|
||||
// }
|
||||
if (in & (1 << IN_FORWARD))
|
||||
{
|
||||
c_in |= 1 << CIN_FORWARD;
|
||||
v_in |= 1 << VIN_FORWARD;
|
||||
}
|
||||
|
||||
void game::OpenWorld::PlayerJoined(Player& player)
|
||||
if (in & (1 << IN_BACKWARD))
|
||||
{
|
||||
// SpawnVehicle(player);
|
||||
SpawnCharacter(player);
|
||||
c_in |= 1 << CIN_BACKWARD;
|
||||
v_in |= 1 << VIN_BACKWARD;
|
||||
}
|
||||
|
||||
void game::OpenWorld::PlayerInput(Player& player, PlayerInputType type, bool enabled)
|
||||
if (in & (1 << IN_LEFT))
|
||||
{
|
||||
// auto vehicle = player_vehicles_.at(&player);
|
||||
// // player.SendChat("input zmenen: " + std::to_string(static_cast<int>(type)) + "=" + (enabled ? "1" : "0"));
|
||||
|
||||
// switch (type)
|
||||
// {
|
||||
// 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;
|
||||
}
|
||||
c_in |= 1 << CIN_LEFT;
|
||||
v_in |= 1 << VIN_LEFT;
|
||||
}
|
||||
|
||||
void game::OpenWorld::PlayerViewAnglesChanged(Player& player, float yaw, float pitch)
|
||||
if (in & (1 << IN_RIGHT))
|
||||
{
|
||||
auto character = player_characters_.at(&player);
|
||||
character->SetForwardYaw(yaw);
|
||||
c_in |= 1 << CIN_RIGHT;
|
||||
v_in |= 1 << VIN_RIGHT;
|
||||
}
|
||||
|
||||
void game::OpenWorld::PlayerLeft(Player& player)
|
||||
if (in & (1 << IN_JUMP))
|
||||
{
|
||||
// RemoveVehicle(player);
|
||||
RemoveCharacter(player);
|
||||
c_in |= 1 << CIN_JUMP;
|
||||
}
|
||||
|
||||
void game::OpenWorld::RemoveVehicle(Player& player)
|
||||
if (vehicle && is_driver)
|
||||
{
|
||||
auto it = player_vehicles_.find(&player);
|
||||
if (it != player_vehicles_.end())
|
||||
{
|
||||
it->second->Remove();
|
||||
player_vehicles_.erase(it);
|
||||
SetInputs(0);
|
||||
vehicle->SetInputs(v_in);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetInputs(c_in);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
player_characters_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static float GetTurnAngle2D(const glm::vec2& forward, const glm::vec2& to_target)
|
||||
{
|
||||
glm::vec2 forward_xy = glm::normalize(forward);
|
||||
@ -356,27 +115,90 @@ static float GetTurnAngle(const glm::vec3& pos, const glm::quat& rot, const glm:
|
||||
return GetTurnAngle2D(forward_xy, to_target_xy);
|
||||
}
|
||||
|
||||
static void SelectNextNode(BotThinkState& s)
|
||||
struct NpcCharacter : public ControllableCharacter
|
||||
{
|
||||
size_t node = s.path.back();
|
||||
size_t num_nbs = s.roads.nodes[node].num_nbs;
|
||||
using Super = ControllableCharacter;
|
||||
|
||||
// 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)
|
||||
{
|
||||
const auto& pos = s.roads.nodes[node].position;
|
||||
std::cout << "node " << node << " has no neighbors!!!1 position: " << pos.x << " " << pos.y << " " << pos.z << std::endl;
|
||||
const auto& pos = roads->nodes[node].position;
|
||||
std::cout << "node " << node << " has no neighbors!!!1 position: " << pos.x << " " << pos.y << " " << pos.z
|
||||
<< std::endl;
|
||||
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();
|
||||
glm::quat rot = s->vehicle.GetRotation();
|
||||
VehicleThink();
|
||||
}
|
||||
|
||||
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 target = s->roads.nodes[s->node].position;
|
||||
@ -384,33 +206,33 @@ static void BotThink(std::shared_ptr<BotThinkState> s)
|
||||
//
|
||||
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;
|
||||
if (glm::distance(glm::vec2(pos), glm::vec2(node_pos)) < 6.0f && s->path.size() > 1)
|
||||
glm::vec3 node_pos = roads->nodes[path[0]].position;
|
||||
if (glm::distance(glm::vec2(pos), glm::vec2(node_pos)) < 6.0f && path.size() > 1)
|
||||
{
|
||||
s->seg_start = node_pos;
|
||||
s->path.pop_front();
|
||||
SelectNextNode(*s);
|
||||
seg_start = node_pos;
|
||||
path.pop_front();
|
||||
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[1] = pos;
|
||||
|
||||
// find closest point on segment [seg_start -> 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);
|
||||
if (seg_len > 5.0f)
|
||||
{
|
||||
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);
|
||||
waypoints[2] = s->seg_start + t * seg_dir;
|
||||
float t = glm::clamp(glm::dot(pos - seg_start, seg_dir_norm) / seg_len, 0.0f, 1.0f);
|
||||
waypoints[2] = seg_start + t * seg_dir;
|
||||
if (glm::distance(waypoints[1], target_node_pos) > 10.0f)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -428,8 +249,8 @@ static void BotThink(std::shared_ptr<BotThinkState> s)
|
||||
|
||||
for (size_t i = 3; i < waypoints.size(); ++i)
|
||||
{
|
||||
size_t path_idx = glm::min(i - 3, s->path.size() - 1);
|
||||
waypoints[i] = s->roads.nodes[s->path[path_idx]].position;
|
||||
size_t path_idx = glm::min(i - 3, path.size() - 1);
|
||||
waypoints[i] = roads->nodes[path[path_idx]].position;
|
||||
}
|
||||
|
||||
// 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};
|
||||
|
||||
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";
|
||||
float abs_angle = fabsf(angle);
|
||||
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 = glm::max(speed_limit, 20.0f);
|
||||
// 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)
|
||||
@ -465,58 +288,61 @@ static void BotThink(std::shared_ptr<BotThinkState> s)
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
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++;
|
||||
if (s->stuck_counter > 20)
|
||||
stuck_counter++;
|
||||
if (stuck_counter > 20)
|
||||
{
|
||||
s->state_str = "stuck (reverse)";
|
||||
s->stuck_counter = 0;
|
||||
s->vehicle.SetSteering(true, -angle); // try turn away
|
||||
//s->state_str = "stuck (reverse)";
|
||||
//s->stuck_counter = 0;
|
||||
//s->vehicle.SetSteering(true, -angle); // try turn away
|
||||
|
||||
s->vehicle.SetInputs(0); // stop
|
||||
// stuck, go reverse for a while
|
||||
s->vehicle.SetInput(game::VIN_BACKWARD, true);
|
||||
s->vehicle.Schedule(2000, [s]() {
|
||||
s->vehicle.SetInput(game::VIN_BACKWARD, false);
|
||||
BotThink(s);
|
||||
} );
|
||||
//s->vehicle.SetInputs(0); // stop
|
||||
//// stuck, go reverse for a while
|
||||
//s->vehicle.SetInput(game::VIN_BACKWARD, true);
|
||||
//s->vehicle.Schedule(2000, [s]() {
|
||||
// s->vehicle.SetInput(game::VIN_BACKWARD, false);
|
||||
// BotThink(s);
|
||||
//});
|
||||
|
||||
vehicle->SetInputs(0); // stop
|
||||
is_driver = false; // TODO: fix
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s->stuck_counter = 0;
|
||||
s->last_pos = pos;
|
||||
stuck_counter = 0;
|
||||
last_pos = pos;
|
||||
}
|
||||
|
||||
s->vehicle.SetSteering(true, angle);
|
||||
vehicle->SetSteering(true, angle);
|
||||
|
||||
game::VehicleInputFlags vin = 0;
|
||||
|
||||
float speed = s->vehicle.GetSpeed();
|
||||
float speed = vehicle->GetSpeed();
|
||||
|
||||
// if (glm::distance(pos, target) < 10.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;
|
||||
}
|
||||
@ -526,30 +352,153 @@ static void BotThink(std::shared_ptr<BotThinkState> s)
|
||||
vin |= 1 << game::VIN_BACKWARD;
|
||||
}
|
||||
|
||||
s->vehicle.SetInputs(vin);
|
||||
vehicle->SetInputs(vin);
|
||||
}
|
||||
};
|
||||
|
||||
s->vehicle.Schedule(rand() % 120 + 40, [s]() {
|
||||
BotThink(s);
|
||||
} );
|
||||
struct VehicleSeat
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
static void BotNametagThink(std::shared_ptr<BotThinkState> s)
|
||||
void InitSeats()
|
||||
{
|
||||
std::string nametag;
|
||||
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); // + " ";
|
||||
const auto& veh = *GetModel();
|
||||
for (char c = '0'; c <= '9'; ++c)
|
||||
{
|
||||
auto trans = veh.GetLocation(std::string("seat") + c);
|
||||
if (!trans)
|
||||
break;
|
||||
|
||||
// nametag += "path=";
|
||||
// for (auto n : path)
|
||||
// {
|
||||
// nametag += std::to_string(n) + " ";
|
||||
// }
|
||||
s->vehicle.SetNametag(nametag);
|
||||
VehicleSeat seat{};
|
||||
seat.position = trans->position;
|
||||
seats.emplace_back(seat);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
s->vehicle.Schedule(240, [s]() {
|
||||
BotNametagThink(s);
|
||||
} );
|
||||
} // namespace game
|
||||
|
||||
game::OpenWorld::OpenWorld() : World("openworld")
|
||||
{
|
||||
srand(time(NULL));
|
||||
|
||||
// spawn bots
|
||||
for (size_t i = 0; i < 100; ++i)
|
||||
{
|
||||
SpawnBot();
|
||||
}
|
||||
}
|
||||
|
||||
void game::OpenWorld::Update(int64_t delta_time)
|
||||
{
|
||||
World::Update(delta_time);
|
||||
|
||||
}
|
||||
|
||||
void game::OpenWorld::PlayerJoined(Player& player)
|
||||
{
|
||||
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()
|
||||
@ -558,54 +507,41 @@ static const char* GetRandomCarModel()
|
||||
return vehicles[rand() % (sizeof(vehicles) / sizeof(vehicles[0]))];
|
||||
}
|
||||
|
||||
|
||||
void game::OpenWorld::SpawnBot()
|
||||
static game::DrivableVehicle& SpawnRandomVehicle(game::World& world)
|
||||
{
|
||||
auto roads = GetMap().GetGraph("roads");
|
||||
auto roads = world.GetMap().GetGraph("roads");
|
||||
|
||||
if (!roads)
|
||||
{
|
||||
std::cerr << "OpenWorld::SpawnBot: no roads graph in map\n";
|
||||
return;
|
||||
throw std::runtime_error("SpawnRandomVehicle: no roads graph in map");
|
||||
}
|
||||
|
||||
size_t start_node = rand() % roads->nodes.size();
|
||||
// auto color = glm::vec3{0.3f, 0.3f, 0.3f};
|
||||
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.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);
|
||||
BotThink(think_state);
|
||||
vehicle.Schedule(rand() % 500, [think_state]() {
|
||||
//BotNametagThink(think_state);
|
||||
} );
|
||||
return vehicle;
|
||||
}
|
||||
|
||||
void game::OpenWorld::SpawnBot()
|
||||
{
|
||||
auto& vehicle = SpawnRandomVehicle(*this);
|
||||
auto& driver = SpawnRandomCharacter<NpcCharacter>(*this);
|
||||
|
||||
// spawn driver
|
||||
auto& driver = SpawnRandomCharacter();
|
||||
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.SetYaw(0.5f * glm::pi<float>());
|
||||
}
|
||||
|
||||
void game::OpenWorld::SpawnVehicle(Player& player)
|
||||
{
|
||||
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));
|
||||
|
||||
driver.vehicle = &vehicle;
|
||||
driver.is_driver = true;
|
||||
driver.VehicleChanged();
|
||||
}
|
||||
|
||||
@ -7,6 +7,9 @@
|
||||
namespace game
|
||||
{
|
||||
|
||||
class PlayerCharacter;
|
||||
class NpcCharacter;
|
||||
|
||||
class OpenWorld : public World
|
||||
{
|
||||
public:
|
||||
@ -20,20 +23,14 @@ public:
|
||||
virtual void PlayerLeft(Player& player) override;
|
||||
|
||||
private:
|
||||
void SpawnVehicle(Player& player);
|
||||
void RemoveVehicle(Player& player);
|
||||
|
||||
Character& SpawnRandomCharacter();
|
||||
|
||||
void SpawnCharacter(Player& player);
|
||||
void RemoveCharacter(Player& player);
|
||||
void CreatePlayerCharacter(Player& player);
|
||||
void RemovePlayerCharacter(Player& player);
|
||||
|
||||
void SpawnBot();
|
||||
|
||||
private:
|
||||
std::map<Player*, Vehicle*> player_vehicles_;
|
||||
std::map<Player*, Character*> player_characters_;
|
||||
std::vector<Vehicle*> bots_;
|
||||
std::map<Player*, PlayerCharacter*> player_characters_;
|
||||
std::vector<NpcCharacter*> npcs_;
|
||||
};
|
||||
|
||||
}
|
||||
@ -51,6 +51,8 @@ public:
|
||||
|
||||
void SetSteering(bool analog, float value = 0.0f);
|
||||
|
||||
const std::shared_ptr<const assets::VehicleModel>& GetModel() const { return model_; }
|
||||
|
||||
virtual ~Vehicle();
|
||||
|
||||
private:
|
||||
|
||||
@ -40,15 +40,13 @@ public:
|
||||
|
||||
Entity* GetEntity(net::EntNum entnum);
|
||||
|
||||
const assets::Map& GetMap() const { return *map_; }
|
||||
const std::string& GetMapName() const { return mapname_; }
|
||||
const std::map<net::EntNum, std::unique_ptr<Entity>>& GetEntities() const { return ents_; }
|
||||
const int64_t& GetTime() const { return time_ms_; }
|
||||
|
||||
virtual ~World() = default;
|
||||
|
||||
protected:
|
||||
const assets::Map& GetMap() const { return *map_; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<const assets::Map> map_;
|
||||
std::string mapname_;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user