From 26bb9b802fcd7f6edfc1b42975d0eb9993037ab3 Mon Sep 17 00:00:00 2001 From: tovjemam Date: Sun, 15 Feb 2026 17:30:11 +0100 Subject: [PATCH] Vehicle seats & Remake openworld logic --- src/assets/vehiclemdl.cpp | 16 + src/assets/vehiclemdl.hpp | 2 + src/game/openworld.cpp | 952 ++++++++++++++++++-------------------- src/game/openworld.hpp | 17 +- src/game/vehicle.hpp | 2 + src/game/world.hpp | 4 +- 6 files changed, 472 insertions(+), 521 deletions(-) diff --git a/src/assets/vehiclemdl.cpp b/src/assets/vehiclemdl.cpp index 153b942..3b9e12b 100644 --- a/src/assets/vehiclemdl.cpp +++ b/src/assets/vehiclemdl.cpp @@ -39,7 +39,23 @@ std::shared_ptr 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; +} diff --git a/src/assets/vehiclemdl.hpp b/src/assets/vehiclemdl.hpp index f0afcac..c402278 100644 --- a/src/assets/vehiclemdl.hpp +++ b/src/assets/vehiclemdl.hpp @@ -34,10 +34,12 @@ public: const std::shared_ptr& GetModel() const { return basemodel_; } const std::vector& GetWheels() const { return wheels_; } + const Transform* GetLocation(const std::string& name) const; private: std::shared_ptr basemodel_; std::vector wheels_; + std::map locations_; }; diff --git a/src/game/openworld.cpp b/src/game/openworld.cpp index e143366..4a4da8b 100644 --- a/src/game/openworld.cpp +++ b/src/game/openworld.cpp @@ -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 wrapper; - -// explicit CoroTask(std::coroutine_handle<> h) : wrapper(std::make_shared(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 -// 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("twingo", glm::vec3{0.3f, 0.3f, 0.3f}); - // vehicle.SetPosition({ static_cast(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) +struct ControllableCharacter : public Character { - World::Update(delta_time); + using Super = Character; - // 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); - // } + Vehicle* vehicle = nullptr; + bool is_driver = 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 }); - // } - - // } -} + ControllableCharacter(World& world) : Character(world, CharacterInfo{}) {} -void game::OpenWorld::PlayerJoined(Player& player) + virtual void VehicleChanged() = 0; +}; + +struct PlayerCharacter : public ControllableCharacter { - // SpawnVehicle(player); - SpawnCharacter(player); -} + using Super = ControllableCharacter; -void game::OpenWorld::PlayerInput(Player& player, PlayerInputType type, bool enabled) -{ - // auto vehicle = player_vehicles_.at(&player); - // // player.SendChat("input zmenen: " + std::to_string(static_cast(type)) + "=" + (enabled ? "1" : "0")); + Player& player; - // 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; - } -} - -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(); + PlayerCharacter(World& world, Player& player) : ControllableCharacter(world), player(player) { + VehicleChanged(); } - return color; -} - -game::Character& game::OpenWorld::SpawnRandomCharacter() -{ - CharacterInfo cinfo; - auto& character = Spawn(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()) + virtual void VehicleChanged() override { - it->second->Remove(); - player_characters_.erase(it); + if (vehicle) + { + player.SetCamera(vehicle->GetEntNum()); + } + else + { + player.SetCamera(GetEntNum()); + } } -} -// 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 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}) + void UpdateInputs() { - seg_start = v.GetPosition(); + 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) { glm::vec2 forward_xy = glm::normalize(forward); @@ -356,200 +115,390 @@ 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; - if (num_nbs < 1) + // driver + const assets::MapGraph* roads; + glm::vec3 seg_start; + std::deque 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) { - 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; - throw std::runtime_error("no neighbors"); + VehicleChanged(); } - s.path.push_back(s.roads.nbs[s.roads.nodes[node].nbs + (rand() % num_nbs)]); -} - -static void BotThink(std::shared_ptr s) -{ - s->state_str = "path"; - - glm::vec3 pos = s->vehicle.GetPosition(); - glm::quat rot = s->vehicle.GetRotation(); - glm::vec3 forward = rot * glm::vec3{0.0f, 1.0f, 0.0f}; - - //glm::vec3 target = s->roads.nodes[s->node].position; - - // - std::array waypoints; - - while (s->path.size() < waypoints.size() - 1) + virtual void VehicleChanged() override { - SelectNextNode(*s); - } + roads = nullptr; + path.clear(); + gas = false; + stuck_counter = 0; + speed_limit = 0.0f; - 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) - { - s->seg_start = node_pos; - s->path.pop_front(); - SelectNextNode(*s); - } - - glm::vec3 target_node_pos = s->roads.nodes[s->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; - 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; - if (glm::distance(waypoints[1], target_node_pos) > 10.0f) + if (vehicle && is_driver) { - waypoints[2] += seg_dir_norm * 10.0f; // look a bit ahead on segment + roads = world_.GetMap().GetGraph("roads"); + + seg_start = vehicle->GetPosition(); + + size_t start_node = 0; + float min_dist = std::numeric_limits().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 = 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"); + } + + path.push_back(roads->nbs[roads->nodes[node].nbs + (rand() % num_nbs)]); + } + + virtual void Update() override + { + Super::Update(); + + 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; + + // + std::array waypoints; + + while (path.size() < waypoints.size() - 1) + { + SelectNextNode(); + } + + glm::vec3 node_pos = roads->nodes[path[0]].position; + if (glm::distance(glm::vec2(pos), glm::vec2(node_pos)) < 6.0f && path.size() > 1) + { + seg_start = node_pos; + path.pop_front(); + SelectNextNode(); + } + + 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 - 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 - 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 + } + else + { + waypoints[2] = target_node_pos; + } } else { waypoints[2] = target_node_pos; } - - } - else - { - waypoints[2] = target_node_pos; - } - 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; - } - - // decrease speed based on curvature - const float base_speed = 100.0f; - float target_speed = base_speed; - float dist_accum = 0.0f; - for (size_t i = 1; i < waypoints.size() - 1; ++i) - { - glm::vec3 dir1 = waypoints[i] - waypoints[i - 1]; - glm::vec3 dir2 = waypoints[i + 1] - waypoints[i]; - float dist = glm::length(dir1); - dist_accum += dist; - - glm::vec2 dir1_xy = glm::vec2{dir1.x, dir1.y}; - 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; - // std::cout << "angle: " << angle << "\n"; - float abs_angle = fabsf(angle); - if (abs_angle > glm::radians(7.0f)) + for (size_t i = 3; i < waypoints.size(); ++i) { - // float speed_limit = 50.0f / abs_angle; // sharper turn -> lower speed - // 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() / 2.0f) * 50.0f / glm::max(dist_accum - 1.0f, 1.0f); - + size_t path_idx = glm::min(i - 3, path.size() - 1); + waypoints[i] = roads->nodes[path[path_idx]].position; } - if (dist_accum > 200.0f) - break; + // decrease speed based on curvature + const float base_speed = 100.0f; + float target_speed = base_speed; + float dist_accum = 0.0f; + for (size_t i = 1; i < waypoints.size() - 1; ++i) + { + glm::vec3 dir1 = waypoints[i] - waypoints[i - 1]; + glm::vec3 dir2 = waypoints[i + 1] - waypoints[i]; + float dist = glm::length(dir1); + dist_accum += dist; + + glm::vec2 dir1_xy = glm::vec2{dir1.x, dir1.y}; + 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; + // std::cout << "angle: " << angle << "\n"; + float abs_angle = fabsf(angle); + if (abs_angle > glm::radians(7.0f)) + { + // float speed_limit = 50.0f / abs_angle; // sharper turn -> lower speed + // 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() / 2.0f) * 50.0f / glm::max(dist_accum - 1.0f, 1.0f); + } + + if (dist_accum > 200.0f) + break; + } + + target_speed = glm::clamp(target_speed, 25.0f, 100.0f); + speed_limit = target_speed; + + // std::cout << "target speed: " << target_speed << "\n"; + + float angle = GetTurnAngle(pos, rot, waypoints[2]); + + if (glm::distance(pos, last_pos) < 2.0f) + { + stuck_counter++; + if (stuck_counter > 20) + { + //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); + //}); + + vehicle->SetInputs(0); // stop + is_driver = false; // TODO: fix + return; + } + } + else + { + stuck_counter = 0; + last_pos = pos; + } + + vehicle->SetSteering(true, angle); + + game::VehicleInputFlags vin = 0; + + float speed = vehicle->GetSpeed(); + + // if (glm::distance(pos, target) < 10.0f) + // { + // target_speed = 20.0f; + // } + + if (speed < target_speed * 0.9f && !gas) + { + gas = true; + } + else if (speed > target_speed * 1.1f && gas) + { + gas = false; + } + + if (gas) + { + vin |= 1 << game::VIN_FORWARD; + } + + if (speed > target_speed * 1.4f) + { + vin |= 1 << game::VIN_BACKWARD; + } + + vehicle->SetInputs(vin); + } +}; + +struct VehicleSeat +{ + glm::vec3 position; + ControllableCharacter* occupant; +}; + +struct DrivableVehicle : public Vehicle +{ + std::vector seats; + + DrivableVehicle(World& world, std::string model_name, const glm::vec3& color) + : Vehicle(world, std::move(model_name), color) + { + InitSeats(); } - target_speed = glm::clamp(target_speed, 25.0f, 100.0f); - s->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) + void InitSeats() { - s->stuck_counter++; - if (s->stuck_counter > 20) + const auto& veh = *GetModel(); + for (char c = '0'; c <= '9'; ++c) { - 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); - } ); - return; + auto trans = veh.GetLocation(std::string("seat") + c); + if (!trans) + break; + + VehicleSeat seat{}; + seat.position = trans->position; + seats.emplace_back(seat); } } - else +}; + +} // namespace game + +game::OpenWorld::OpenWorld() : World("openworld") +{ + srand(time(NULL)); + + // spawn bots + for (size_t i = 0; i < 100; ++i) { - s->stuck_counter = 0; - s->last_pos = pos; + SpawnBot(); } - - s->vehicle.SetSteering(true, angle); - - game::VehicleInputFlags vin = 0; - - float speed = s->vehicle.GetSpeed(); - - // 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); - - s->vehicle.Schedule(rand() % 120 + 40, [s]() { - BotThink(s); - } ); } -static void BotNametagThink(std::shared_ptr s) +void game::OpenWorld::Update(int64_t delta_time) { - std::string nametag; - nametag += "s=" + std::to_string(static_cast(s->vehicle.GetSpeed())) + "/" + std::to_string(static_cast(s->speed_limit)) + " "; - nametag += "sc=" + std::to_string(s->stuck_counter) + " "; - nametag += "st=" + std::string(s->state_str); // + " "; + World::Update(delta_time); - // nametag += "path="; - // for (auto n : path) - // { - // nametag += std::to_string(n) + " "; - // } - s->vehicle.SetNametag(nametag); +} - s->vehicle.Schedule(240, [s]() { - BotNametagThink(s); - } ); +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 +static T& SpawnRandomCharacter(game::World& world, TArgs&&... args) +{ + auto& character = world.Spawn(std::forward(args)...); + + // add clothes + character.AddClothes("tshirt", GetRandomColor()); + character.AddClothes("shorts", GetRandomColor()); + + return character; +} + +void game::OpenWorld::CreatePlayerCharacter(Player& player) +{ + RemovePlayerCharacter(player); + + auto& character = SpawnRandomCharacter(*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(GetRandomCarModel(), color); - //vehicle.SetNametag("bot (" + std::to_string(vehicle.GetEntNum()) + ")"); + auto& vehicle = world.Spawn(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(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(*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()); -} - -void game::OpenWorld::SpawnVehicle(Player& player) -{ - RemoveVehicle(player); - - // spawn him car - // ranodm color - - - auto vehicle_name = GetRandomCarModel(); - auto& vehicle = Spawn(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(); } diff --git a/src/game/openworld.hpp b/src/game/openworld.hpp index 4d79f7c..a2940a8 100644 --- a/src/game/openworld.hpp +++ b/src/game/openworld.hpp @@ -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); + void CreatePlayerCharacter(Player& player); + void RemovePlayerCharacter(Player& player); - Character& SpawnRandomCharacter(); - - void SpawnCharacter(Player& player); - void RemoveCharacter(Player& player); - void SpawnBot(); private: - std::map player_vehicles_; - std::map player_characters_; - std::vector bots_; + std::map player_characters_; + std::vector npcs_; }; } \ No newline at end of file diff --git a/src/game/vehicle.hpp b/src/game/vehicle.hpp index a03ffec..221de8e 100644 --- a/src/game/vehicle.hpp +++ b/src/game/vehicle.hpp @@ -51,6 +51,8 @@ public: void SetSteering(bool analog, float value = 0.0f); + const std::shared_ptr& GetModel() const { return model_; } + virtual ~Vehicle(); private: diff --git a/src/game/world.hpp b/src/game/world.hpp index 70a82df..9fe3424 100644 --- a/src/game/world.hpp +++ b/src/game/world.hpp @@ -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>& 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 map_; std::string mapname_;