Add bots
This commit is contained in:
parent
b7e2e06ac3
commit
faa02c4005
@ -28,6 +28,7 @@ set(COMMON_SOURCES
|
|||||||
"src/collision/trianglemesh.cpp"
|
"src/collision/trianglemesh.cpp"
|
||||||
"src/game/player_input.hpp"
|
"src/game/player_input.hpp"
|
||||||
"src/game/transform_node.hpp"
|
"src/game/transform_node.hpp"
|
||||||
|
"src/game/vehicle_sync.hpp"
|
||||||
"src/net/defs.hpp"
|
"src/net/defs.hpp"
|
||||||
"src/net/fixed_str.hpp"
|
"src/net/fixed_str.hpp"
|
||||||
"src/net/inmessage.hpp"
|
"src/net/inmessage.hpp"
|
||||||
@ -39,6 +40,7 @@ set(COMMON_SOURCES
|
|||||||
"src/utils/allocnum.hpp"
|
"src/utils/allocnum.hpp"
|
||||||
"src/utils/defs.hpp"
|
"src/utils/defs.hpp"
|
||||||
"src/utils/files.hpp"
|
"src/utils/files.hpp"
|
||||||
|
"src/utils/scheduler.hpp"
|
||||||
"src/utils/transform.hpp"
|
"src/utils/transform.hpp"
|
||||||
"src/utils/validate.hpp"
|
"src/utils/validate.hpp"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,12 +1,32 @@
|
|||||||
#include "map.hpp"
|
#include "map.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "cache.hpp"
|
#include "cache.hpp"
|
||||||
#include "utils/files.hpp"
|
|
||||||
#include "cmdfile.hpp"
|
#include "cmdfile.hpp"
|
||||||
|
#include "utils/files.hpp"
|
||||||
|
|
||||||
std::shared_ptr<const assets::Map> assets::Map::LoadFromFile(const std::string& filename)
|
std::shared_ptr<const assets::Map> assets::Map::LoadFromFile(const std::string& filename)
|
||||||
{
|
{
|
||||||
auto map = std::make_shared<Map>();
|
auto map = std::make_shared<Map>();
|
||||||
|
|
||||||
|
MapGraph* graph = nullptr;
|
||||||
|
std::vector<std::tuple<size_t, size_t>> graph_edges;
|
||||||
|
|
||||||
|
auto ProcessGraph = [&]() {
|
||||||
|
std::sort(graph_edges.begin(), graph_edges.end());
|
||||||
|
for (const auto& [from_idx, to_idx] : graph_edges)
|
||||||
|
{
|
||||||
|
graph->nbs.push_back(to_idx);
|
||||||
|
|
||||||
|
// update node info
|
||||||
|
auto& node = graph->nodes[from_idx];
|
||||||
|
if (node.nbs == 0)
|
||||||
|
node.nbs = graph->nbs.size() - 1;
|
||||||
|
node.num_nbs++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
LoadCMDFile(filename, [&](const std::string& command, std::istringstream& iss) {
|
LoadCMDFile(filename, [&](const std::string& command, std::istringstream& iss) {
|
||||||
if (command == "basemodel")
|
if (command == "basemodel")
|
||||||
{
|
{
|
||||||
@ -44,14 +64,56 @@ std::shared_ptr<const assets::Map> assets::Map::LoadFromFile(const std::string&
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
map->static_objects_.push_back(std::move(obj));
|
map->static_objects_.push_back(std::move(obj));
|
||||||
}
|
}
|
||||||
|
else if (command == "graph")
|
||||||
|
{
|
||||||
|
if (graph)
|
||||||
|
ProcessGraph();
|
||||||
|
|
||||||
|
std::string graph_name;
|
||||||
|
iss >> graph_name;
|
||||||
|
|
||||||
|
graph = &map->graphs_[graph_name];
|
||||||
|
graph_edges.clear();
|
||||||
|
}
|
||||||
|
else if (command == "n")
|
||||||
|
{
|
||||||
|
if (!graph)
|
||||||
|
throw std::runtime_error("Map file error: 'n' command without active graph");
|
||||||
|
|
||||||
|
MapGraphNode node;
|
||||||
|
|
||||||
|
iss >> node.position.x >> node.position.y >> node.position.z;
|
||||||
|
|
||||||
|
graph->nodes.emplace_back(std::move(node));
|
||||||
|
}
|
||||||
|
else if (command == "e")
|
||||||
|
{
|
||||||
|
if (!graph)
|
||||||
|
throw std::runtime_error("Map file error: 'e' command without active graph");
|
||||||
|
|
||||||
|
size_t from_idx, to_idx;
|
||||||
|
iss >> from_idx >> to_idx;
|
||||||
|
|
||||||
|
graph_edges.emplace_back(from_idx, to_idx);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (graph)
|
||||||
|
ProcessGraph();
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const assets::MapGraph* assets::Map::GetGraph(const std::string& name) const
|
||||||
|
{
|
||||||
|
auto it = graphs_.find(name);
|
||||||
|
if (it != graphs_.end())
|
||||||
|
return &it->second;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CLIENT
|
#ifdef CLIENT
|
||||||
void assets::Map::Draw(gfx::DrawList& dlist) const
|
void assets::Map::Draw(gfx::DrawList& dlist) const
|
||||||
{
|
{
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "model.hpp"
|
#include "model.hpp"
|
||||||
#include "game/transform_node.hpp"
|
#include "game/transform_node.hpp"
|
||||||
@ -20,6 +22,19 @@ struct MapStaticObject
|
|||||||
glm::vec3 color = glm::vec3(1.0f);
|
glm::vec3 color = glm::vec3(1.0f);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct MapGraphNode
|
||||||
|
{
|
||||||
|
glm::vec3 position = glm::vec3(0.0f);
|
||||||
|
size_t num_nbs = 0;
|
||||||
|
size_t nbs = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MapGraph
|
||||||
|
{
|
||||||
|
std::vector<MapGraphNode> nodes;
|
||||||
|
std::vector<size_t> nbs;
|
||||||
|
};
|
||||||
|
|
||||||
class Map
|
class Map
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -28,12 +43,14 @@ public:
|
|||||||
|
|
||||||
const std::shared_ptr<const Model>& GetBaseModel() const { return basemodel_; }
|
const std::shared_ptr<const Model>& GetBaseModel() const { return basemodel_; }
|
||||||
const std::vector<MapStaticObject>& GetStaticObjects() const { return static_objects_; }
|
const std::vector<MapStaticObject>& GetStaticObjects() const { return static_objects_; }
|
||||||
|
const MapGraph* GetGraph(const std::string& name) const;
|
||||||
|
|
||||||
CLIENT_ONLY(void Draw(gfx::DrawList& dlist) const;)
|
CLIENT_ONLY(void Draw(gfx::DrawList& dlist) const;)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<const Model> basemodel_;
|
std::shared_ptr<const Model> basemodel_;
|
||||||
std::vector<MapStaticObject> static_objects_;
|
std::vector<MapStaticObject> static_objects_;
|
||||||
|
std::map<std::string, MapGraph> graphs_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace assets
|
} // namespace assets
|
||||||
@ -2,7 +2,13 @@
|
|||||||
|
|
||||||
#include "world.hpp"
|
#include "world.hpp"
|
||||||
|
|
||||||
game::Entity::Entity(World& world, net::EntType viewtype) : world_(world), entnum_(world.GetNewEntnum()), viewtype_(viewtype) {}
|
game::Entity::Entity(World& world, net::EntType viewtype) : Scheduler(world.GetTime()), world_(world), entnum_(world.GetNewEntnum()), viewtype_(viewtype) {}
|
||||||
|
|
||||||
|
void game::Entity::Update()
|
||||||
|
{
|
||||||
|
ResetMsg();
|
||||||
|
RunTasks();
|
||||||
|
}
|
||||||
|
|
||||||
net::OutMessage game::Entity::BeginEntMsg(net::EntMsgType type)
|
net::OutMessage game::Entity::BeginEntMsg(net::EntMsgType type)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include "net/msg_producer.hpp"
|
#include "net/msg_producer.hpp"
|
||||||
#include "transform_node.hpp"
|
#include "transform_node.hpp"
|
||||||
#include "utils/defs.hpp"
|
#include "utils/defs.hpp"
|
||||||
|
#include "utils/scheduler.hpp"
|
||||||
|
|
||||||
namespace game
|
namespace game
|
||||||
{
|
{
|
||||||
@ -12,7 +13,7 @@ namespace game
|
|||||||
class World;
|
class World;
|
||||||
class Player;
|
class Player;
|
||||||
|
|
||||||
class Entity : public net::MsgProducer
|
class Entity : public net::MsgProducer, public Scheduler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Entity(World& world, net::EntType viewtype);
|
Entity(World& world, net::EntType viewtype);
|
||||||
@ -21,7 +22,7 @@ public:
|
|||||||
net::EntNum GetEntNum() const { return entnum_; }
|
net::EntNum GetEntNum() const { return entnum_; }
|
||||||
net::EntType GetViewType() const { return viewtype_; }
|
net::EntType GetViewType() const { return viewtype_; }
|
||||||
|
|
||||||
virtual void Update() { ResetMsg(); }
|
virtual void Update();
|
||||||
virtual void SendInitData(Player& player, net::OutMessage& msg) const {}
|
virtual void SendInitData(Player& player, net::OutMessage& msg) const {}
|
||||||
|
|
||||||
void Remove() { removed_ = true; }
|
void Remove() { removed_ = true; }
|
||||||
|
|||||||
@ -1,19 +1,102 @@
|
|||||||
#include "openworld.hpp"
|
#include "openworld.hpp"
|
||||||
|
|
||||||
|
#include <coroutine>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include "player.hpp"
|
#include "player.hpp"
|
||||||
#include "vehicle.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")
|
game::OpenWorld::OpenWorld() : World("openworld")
|
||||||
{
|
{
|
||||||
srand(time(NULL));
|
srand(time(NULL));
|
||||||
|
|
||||||
// spawn test vehicles
|
// // spawn test vehicles
|
||||||
for (size_t i = 0; i < 150; ++i)
|
// 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 < 20; ++i)
|
||||||
{
|
{
|
||||||
auto& vehicle = Spawn<Vehicle>("pickup_hd", glm::vec3{1.0f, 0.0f, 0.0f});
|
SpawnBot();
|
||||||
vehicle.SetPosition({ static_cast<float>(i * 3), 150.0f, 5.0f });
|
|
||||||
vehicle.SetInput(VIN_FORWARD, true);
|
|
||||||
bots_.push_back(&vehicle);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,29 +104,29 @@ void game::OpenWorld::Update(int64_t delta_time)
|
|||||||
{
|
{
|
||||||
World::Update(delta_time);
|
World::Update(delta_time);
|
||||||
|
|
||||||
for (auto bot : bots_)
|
// for (auto bot : bots_)
|
||||||
{
|
// {
|
||||||
bot->SetInput(VIN_FORWARD, true);
|
// bot->SetInput(VIN_FORWARD, true);
|
||||||
|
|
||||||
if (rand() % 1000 < 10)
|
// if (rand() % 1000 < 10)
|
||||||
{
|
// {
|
||||||
bool turn_left = rand() % 2;
|
// bool turn_left = rand() % 2;
|
||||||
bot->SetInput(VIN_LEFT, turn_left);
|
// bot->SetInput(VIN_LEFT, turn_left);
|
||||||
bot->SetInput(VIN_RIGHT, !turn_left);
|
// bot->SetInput(VIN_RIGHT, !turn_left);
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
bot->SetInput(VIN_LEFT, false);
|
// bot->SetInput(VIN_LEFT, false);
|
||||||
bot->SetInput(VIN_RIGHT, false);
|
// bot->SetInput(VIN_RIGHT, false);
|
||||||
}
|
// }
|
||||||
|
|
||||||
auto pos = bot->GetPosition();
|
// auto pos = bot->GetPosition();
|
||||||
if (glm::distance(pos, glm::vec3(0.0f, 0.0f, 0.0f)) > 1000.0f || pos.z < -20.0f)
|
// 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 });
|
// bot->SetPosition({ rand() % 30 * 3 + 100.0f, 200.0f, 10.0f });
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
void game::OpenWorld::PlayerJoined(Player& player)
|
void game::OpenWorld::PlayerJoined(Player& player)
|
||||||
@ -104,15 +187,180 @@ void game::OpenWorld::RemoveVehicle(Player& player)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
size_t node = 0;
|
||||||
|
bool gas = false;
|
||||||
|
size_t stuck_counter = 0;
|
||||||
|
glm::vec3 last_pos = glm::vec3(0.0f);
|
||||||
|
|
||||||
|
BotThinkState(game::Vehicle& v, const assets::MapGraph& g, size_t n)
|
||||||
|
: vehicle(v), roads(g), node(n) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static float GetSteeringAngle(const glm::vec3& pos, const glm::quat& rot, const glm::vec3& target)
|
||||||
|
{
|
||||||
|
glm::vec3 forward = rot * glm::vec3{0.0f, 1.0f, 0.0f};
|
||||||
|
glm::vec2 forward_xy = glm::normalize(glm::vec2{forward.x, forward.y});
|
||||||
|
glm::vec3 to_target = target - pos;
|
||||||
|
glm::vec2 to_target_xy = glm::normalize(glm::vec2{to_target.x, to_target.y});
|
||||||
|
float dot = glm::dot(forward_xy, to_target_xy);
|
||||||
|
float cross = forward_xy.x * to_target_xy.y - forward_xy.y * to_target_xy.x;
|
||||||
|
|
||||||
|
float angle = acosf(glm::clamp(dot, -1.0f, 1.0f)); // in [0, pi]
|
||||||
|
|
||||||
|
if (cross < 0)
|
||||||
|
angle = -angle;
|
||||||
|
|
||||||
|
return angle; // in [-pi, pi]
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BotThink(std::shared_ptr<BotThinkState> s)
|
||||||
|
{
|
||||||
|
glm::vec3 pos = s->vehicle.GetPosition();
|
||||||
|
glm::quat rot = s->vehicle.GetRotation();
|
||||||
|
|
||||||
|
glm::vec3 target = s->roads.nodes[s->node].position;
|
||||||
|
float angle = GetSteeringAngle(pos, rot, target);
|
||||||
|
|
||||||
|
if (glm::distance(pos, s->last_pos) < 2.0f)
|
||||||
|
{
|
||||||
|
s->stuck_counter++;
|
||||||
|
if (s->stuck_counter > 20)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
} );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s->stuck_counter = 0;
|
||||||
|
s->last_pos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->vehicle.SetSteering(true, angle);
|
||||||
|
|
||||||
|
game::VehicleInputFlags vin = 0;
|
||||||
|
|
||||||
|
float speed = s->vehicle.GetSpeed();
|
||||||
|
float target_speed = 50.0f;
|
||||||
|
|
||||||
|
if (glm::distance(pos, target) < 10.0f)
|
||||||
|
{
|
||||||
|
target_speed = 20.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (speed < target_speed * 0.9f && !s->gas)
|
||||||
|
{
|
||||||
|
s->gas = true;
|
||||||
|
}
|
||||||
|
else if (speed > target_speed * 1.1f && s->gas)
|
||||||
|
{
|
||||||
|
s->gas = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->gas)
|
||||||
|
{
|
||||||
|
vin |= 1 << game::VIN_FORWARD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (speed > target_speed * 1.4f)
|
||||||
|
{
|
||||||
|
vin |= 1 << game::VIN_BACKWARD;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->vehicle.SetInputs(vin);
|
||||||
|
|
||||||
|
float dist_to_node = glm::distance(pos, s->roads.nodes[s->node].position);
|
||||||
|
if (dist_to_node < 5.0f)
|
||||||
|
{
|
||||||
|
// advance to next node
|
||||||
|
const auto& graph = s->roads;
|
||||||
|
const auto& current_node = graph.nodes[s->node];
|
||||||
|
|
||||||
|
if (current_node.num_nbs > 0)
|
||||||
|
{
|
||||||
|
size_t next_idx = rand() % current_node.num_nbs;
|
||||||
|
s->node = s->roads.nbs[current_node.nbs + next_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s->vehicle.Schedule(rand() % 120 + 40, [s]() {
|
||||||
|
BotThink(s);
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* GetRandomCarModel()
|
||||||
|
{
|
||||||
|
const char* vehicles[] = {"pickup_hd", "passat", "twingo", "polskifiat"};
|
||||||
|
return vehicles[rand() % (sizeof(vehicles) / sizeof(vehicles[0]))];
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::OpenWorld::SpawnBot()
|
||||||
|
{
|
||||||
|
auto roads = GetMap()->GetGraph("roads");
|
||||||
|
|
||||||
|
if (!roads)
|
||||||
|
{
|
||||||
|
std::cerr << "OpenWorld::SpawnBot: no roads graph in map\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t start_node = rand() % roads->nodes.size();
|
||||||
|
auto& vehicle = Spawn<Vehicle>(GetRandomCarModel(), glm::vec3{0.3f, 0.3f, 0.3f});
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
void game::OpenWorld::SpawnVehicle(Player& player)
|
void game::OpenWorld::SpawnVehicle(Player& player)
|
||||||
{
|
{
|
||||||
RemoveVehicle(player);
|
RemoveVehicle(player);
|
||||||
|
|
||||||
// spawn him car
|
// spawn him car
|
||||||
// random model
|
|
||||||
const char* vehicles[] = {"pickup_hd", "passat", "twingo", "polskifiat"};
|
|
||||||
auto vehicle_name = vehicles[rand() % (sizeof(vehicles) / sizeof(vehicles[0]))];
|
|
||||||
|
|
||||||
// ranodm color
|
// ranodm color
|
||||||
glm::vec3 color;
|
glm::vec3 color;
|
||||||
for (int i = 0; i < 3; ++i)
|
for (int i = 0; i < 3; ++i)
|
||||||
@ -122,6 +370,7 @@ void game::OpenWorld::SpawnVehicle(Player& player)
|
|||||||
color[i] = qcol.Decode();
|
color[i] = qcol.Decode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto vehicle_name = GetRandomCarModel();
|
||||||
auto& vehicle = Spawn<Vehicle>(vehicle_name, color);
|
auto& vehicle = Spawn<Vehicle>(vehicle_name, color);
|
||||||
vehicle.SetPosition({ 100.0f, 100.0f, 5.0f });
|
vehicle.SetPosition({ 100.0f, 100.0f, 5.0f });
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,8 @@ private:
|
|||||||
void SpawnVehicle(Player& player);
|
void SpawnVehicle(Player& player);
|
||||||
void RemoveVehicle(Player& player);
|
void RemoveVehicle(Player& player);
|
||||||
|
|
||||||
|
void SpawnBot();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::map<Player*, Vehicle*> player_vehicles_;
|
std::map<Player*, Vehicle*> player_vehicles_;
|
||||||
std::vector<Vehicle*> bots_;
|
std::vector<Vehicle*> bots_;
|
||||||
|
|||||||
@ -140,6 +140,23 @@ void game::Vehicle::SetPosition(const glm::vec3& pos)
|
|||||||
body_->setWorldTransform(t);
|
body_->setWorldTransform(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glm::quat game::Vehicle::GetRotation() const
|
||||||
|
{
|
||||||
|
btQuaternion rot = body_->getWorldTransform().getRotation();
|
||||||
|
return glm::quat(rot.w(), rot.x(), rot.y(), rot.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
float game::Vehicle::GetSpeed() const
|
||||||
|
{
|
||||||
|
return vehicle_->getCurrentSpeedKmHour();
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::Vehicle::SetSteering(bool analog, float value)
|
||||||
|
{
|
||||||
|
steering_analog_ = analog;
|
||||||
|
target_steering_ = value;
|
||||||
|
}
|
||||||
|
|
||||||
game::Vehicle::~Vehicle()
|
game::Vehicle::~Vehicle()
|
||||||
{
|
{
|
||||||
auto& bt_world = world_.GetBtWorld();
|
auto& bt_world = world_.GetBtWorld();
|
||||||
@ -196,6 +213,8 @@ void game::Vehicle::ProcessInput()
|
|||||||
breakingForce = maxBreakingForce * 0.05f;
|
breakingForce = maxBreakingForce * 0.05f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!steering_analog_)
|
||||||
|
{
|
||||||
if (in_left)
|
if (in_left)
|
||||||
{
|
{
|
||||||
if (steering_ < steeringClamp)
|
if (steering_ < steeringClamp)
|
||||||
@ -223,6 +242,27 @@ void game::Vehicle::ProcessInput()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (steering_ < target_steering_)
|
||||||
|
{
|
||||||
|
steering_ += steeringIncrement * t_delta;
|
||||||
|
if (steering_ > target_steering_)
|
||||||
|
steering_ = target_steering_;
|
||||||
|
}
|
||||||
|
else if (steering_ > target_steering_)
|
||||||
|
{
|
||||||
|
steering_ -= steeringIncrement * t_delta;
|
||||||
|
if (steering_ < target_steering_)
|
||||||
|
steering_ = target_steering_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (steering_ > steeringClamp)
|
||||||
|
steering_ = steeringClamp;
|
||||||
|
else if (steering_ < -steeringClamp)
|
||||||
|
steering_ = -steeringClamp;
|
||||||
|
}
|
||||||
|
|
||||||
vehicle_->applyEngineForce(engineForce, 2);
|
vehicle_->applyEngineForce(engineForce, 2);
|
||||||
vehicle_->applyEngineForce(engineForce, 3);
|
vehicle_->applyEngineForce(engineForce, 3);
|
||||||
|
|||||||
@ -41,10 +41,16 @@ public:
|
|||||||
virtual void SendInitData(Player& player, net::OutMessage& msg) const override;
|
virtual void SendInitData(Player& player, net::OutMessage& msg) const override;
|
||||||
|
|
||||||
void SetInput(VehicleInputType type, bool enable);
|
void SetInput(VehicleInputType type, bool enable);
|
||||||
|
void SetInputs(VehicleInputFlags inputs) { in_ = inputs; }
|
||||||
|
|
||||||
glm::vec3 GetPosition() const;
|
glm::vec3 GetPosition() const;
|
||||||
void SetPosition(const glm::vec3& pos);
|
void SetPosition(const glm::vec3& pos);
|
||||||
|
|
||||||
|
glm::quat GetRotation() const;
|
||||||
|
float GetSpeed() const;
|
||||||
|
|
||||||
|
void SetSteering(bool analog, float value = 0.0f);
|
||||||
|
|
||||||
virtual ~Vehicle();
|
virtual ~Vehicle();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -64,6 +70,8 @@ private:
|
|||||||
std::unique_ptr<btRaycastVehicle> vehicle_;
|
std::unique_ptr<btRaycastVehicle> vehicle_;
|
||||||
|
|
||||||
float steering_ = 0.0f;
|
float steering_ = 0.0f;
|
||||||
|
bool steering_analog_ = false;
|
||||||
|
float target_steering_ = 0.0f;
|
||||||
float wheel_z_offset_ = 0.0f;
|
float wheel_z_offset_ = 0.0f;
|
||||||
|
|
||||||
size_t num_wheels_ = 0;
|
size_t num_wheels_ = 0;
|
||||||
|
|||||||
@ -35,7 +35,7 @@ void game::World::RegisterEntity(std::unique_ptr<Entity> ent)
|
|||||||
void game::World::Update(int64_t delta_time)
|
void game::World::Update(int64_t delta_time)
|
||||||
{
|
{
|
||||||
time_ms_ += delta_time;
|
time_ms_ += delta_time;
|
||||||
GetBtWorld().stepSimulation(static_cast<float>(delta_time) * 0.001f, 10);
|
GetBtWorld().stepSimulation(static_cast<float>(delta_time) * 0.001f, 5);
|
||||||
|
|
||||||
// update entities
|
// update entities
|
||||||
for (auto it = ents_.begin(); it != ents_.end();)
|
for (auto it = ents_.begin(); it != ents_.end();)
|
||||||
|
|||||||
@ -41,7 +41,7 @@ public:
|
|||||||
|
|
||||||
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_; }
|
||||||
int64_t GetTime() const { return time_ms_; }
|
const int64_t& GetTime() const { return time_ms_; }
|
||||||
|
|
||||||
virtual ~World() = default;
|
virtual ~World() = default;
|
||||||
|
|
||||||
|
|||||||
45
src/utils/scheduler.hpp
Normal file
45
src/utils/scheduler.hpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
class Scheduler
|
||||||
|
{
|
||||||
|
struct Task
|
||||||
|
{
|
||||||
|
int64_t execute_at;
|
||||||
|
std::function<void()> task;
|
||||||
|
|
||||||
|
// order by priority (earliest LAST)
|
||||||
|
bool operator<(const Task& other) const
|
||||||
|
{
|
||||||
|
return execute_at > other.execute_at;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
Scheduler(const int64_t& time) : time_(time) {}
|
||||||
|
|
||||||
|
void Schedule(int64_t delay_ms, std::function<void()> task)
|
||||||
|
{
|
||||||
|
task_queue_.push(Task{time_ + delay_ms, std::move(task)});
|
||||||
|
}
|
||||||
|
|
||||||
|
// int64_t GetTime() const { return time_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void RunTasks()
|
||||||
|
{
|
||||||
|
while (!task_queue_.empty() && task_queue_.top().execute_at <= time_)
|
||||||
|
{
|
||||||
|
auto scheduled_task = task_queue_.top();
|
||||||
|
task_queue_.pop();
|
||||||
|
scheduled_task.task();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const int64_t& time_; // Reference to external time source
|
||||||
|
std::priority_queue<Task, std::vector<Task>> task_queue_;
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user