1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-25 11:52:40 +01:00
openrw/rwgame/RWGame.cpp

847 lines
27 KiB
C++
Raw Normal View History

#include "RWGame.hpp"
#include <glm/gtx/norm.hpp>
2018-12-20 20:31:14 +01:00
#include "RWImGui.hpp"
#include "GameInput.hpp"
#include "State.hpp"
2018-12-09 22:43:42 +01:00
#include "StateManager.hpp"
2016-09-09 22:13:20 +02:00
#include "states/BenchmarkState.hpp"
#include "states/IngameState.hpp"
2016-09-09 22:13:20 +02:00
#include "states/LoadingState.hpp"
#include "states/MenuState.hpp"
#include <core/Profiler.hpp>
2018-12-10 01:51:05 +01:00
#include <engine/Payphone.hpp>
2016-09-09 22:13:20 +02:00
#include <engine/SaveGame.hpp>
#include <objects/GameObject.hpp>
#include <script/SCMFile.hpp>
2018-12-10 01:51:05 +01:00
#include <ai/AIGraphNode.hpp>
#include <ai/PlayerController.hpp>
2018-12-09 22:43:42 +01:00
#include <core/Logger.hpp>
#include <objects/CharacterObject.hpp>
#include <objects/VehicleObject.hpp>
2016-08-09 14:03:38 +02:00
#include <boost/algorithm/string/predicate.hpp>
#include <chrono>
2016-09-01 23:14:15 +02:00
#include <functional>
#include <iomanip>
#include <iostream>
2016-08-09 14:03:38 +02:00
2018-12-09 22:43:42 +01:00
#ifdef _MSC_VER
#pragma warning(disable : 4305 5033)
#endif
// FIXME: should be in rwengine, deeply hidden
#include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
#ifdef _MSC_VER
#pragma warning(default : 4305 5033)
#endif
namespace {
2018-10-29 22:57:18 +01:00
static constexpr std::array<
std::tuple<GameRenderer::SpecialModel, char const*, char const*>, 3>
kSpecialModels{{{GameRenderer::ZoneCylinderA, "zonecyla.dff", "particle"},
{GameRenderer::ZoneCylinderB, "zonecylb.dff", "particle"},
{GameRenderer::Arrow, "arrow.dff", ""}}};
constexpr float kMaxPhysicsSubSteps = 2;
} // namespace
#define MOUSE_SENSITIVITY_SCALE 2.5f
RWGame::RWGame(Logger& log, const std::optional<RWArgConfigLayer> &args)
: GameBase(log, args)
, data(&log, config.gamedataPath())
2018-12-20 20:31:14 +01:00
, renderer(&log, &data)
, imgui(*this) {
2018-08-30 16:47:12 +02:00
RW_PROFILE_THREAD("Main");
RW_TIMELINE_ENTER("Startup", MP_YELLOW);
auto loadTimeStart = std::chrono::steady_clock::now();
bool newgame = false;
bool test = false;
std::optional<std::string> startSave;
std::optional<std::string> benchFile;
if (args.has_value()) {
newgame = args->newGame;
test = args->test;
startSave = args->loadGamePath;
benchFile = args->benchmarkPath;
}
2016-09-09 22:13:20 +02:00
imgui.init();
2016-09-09 22:13:20 +02:00
log.info("Game", "Game directory: " + config.gamedataPath());
if (!data.load()) {
2016-09-09 22:13:20 +02:00
throw std::runtime_error("Invalid game directory path: " +
config.gamedataPath());
2016-09-09 22:13:20 +02:00
}
2018-10-29 22:57:18 +01:00
for (const auto& [specialModel, fileName, name] : kSpecialModels) {
auto model = data.loadClump(fileName, name);
renderer.setSpecialModel(specialModel, model);
2016-10-10 00:08:52 +02:00
}
2016-09-09 22:13:20 +02:00
// Set up text renderer
2017-09-07 19:03:18 +02:00
renderer.text.setFontTexture(FONT_PAGER, "pager");
renderer.text.setFontTexture(FONT_PRICEDOWN, "font1");
renderer.text.setFontTexture(FONT_ARIAL, "font2");
2016-09-09 22:13:20 +02:00
hudDrawer.applyHUDScale(config.hudScale());
renderer.map.scaleHUD(config.hudScale());
debug.setDebugMode(btIDebugDraw::DBG_DrawWireframe |
btIDebugDraw::DBG_DrawConstraints |
btIDebugDraw::DBG_DrawConstraintLimits);
debug.setShaderProgram(renderer.worldProg.get());
2016-09-09 22:13:20 +02:00
2019-01-03 12:59:57 +01:00
data.loadDynamicObjects((std::filesystem::path{config.gamedataPath()} / "data/object.dat")
.string()); // FIXME: use path
2016-09-09 22:13:20 +02:00
data.loadGXT("text/" + config.gameLanguage() + ".gxt");
2016-09-09 22:13:20 +02:00
getRenderer().water.setWaterTable(data.waterHeights, 48, data.realWater,
128 * 128);
2016-09-09 22:13:20 +02:00
for (int m = 0; m < MAP_BLOCK_SIZE; ++m) {
std::ostringstream oss;
oss << "radar" << std::setw(2) << std::setfill('0') << m << ".txd";
data.loadTXD(oss.str());
2016-09-09 22:13:20 +02:00
}
stateManager.enter<LoadingState>(this, [=]() {
if (benchFile.has_value()) {
stateManager.enter<BenchmarkState>(this, *benchFile);
} else if (test) {
stateManager.enter<IngameState>(this, true, "test");
} else if (newgame) {
stateManager.enter<IngameState>(this, true);
} else if (startSave.has_value()) {
stateManager.enter<IngameState>(this, true, *startSave);
} else {
stateManager.enter<MenuState>(this);
}
});
2016-09-09 22:13:20 +02:00
auto loadTimeEnd = std::chrono::steady_clock::now();
auto loadTime =
std::chrono::duration_cast<std::chrono::milliseconds>(loadTimeEnd - loadTimeStart);
log.info("Game", "Loading took " + std::to_string(loadTime.count()) + " ms");
2016-09-09 22:13:20 +02:00
log.info("Game", "Started");
2018-08-30 16:47:12 +02:00
RW_TIMELINE_LEAVE("Startup");
}
2016-09-09 22:13:20 +02:00
RWGame::~RWGame() {
log.info("Game", "Beginning cleanup");
}
2016-09-09 22:13:20 +02:00
void RWGame::newGame() {
// Get a fresh state
state = GameState();
2016-10-20 01:49:15 +02:00
// Destroy the current world and start over
2016-12-02 01:56:38 +01:00
world = std::make_unique<GameWorld>(&log, &data);
world->dynamicsWorld->setDebugDrawer(&debug);
2016-09-09 22:13:20 +02:00
// Associate the new world with the new state and vice versa
2016-10-20 01:49:15 +02:00
state.world = world.get();
world->state = &state;
2016-09-09 22:13:20 +02:00
2016-10-20 01:49:15 +02:00
for (auto ipl : world->data->iplLocations) {
world->data->loadZone(ipl.second);
world->placeItems(ipl.second);
2016-09-09 22:13:20 +02:00
}
}
2018-12-09 22:43:42 +01:00
bool RWGame::hitWorldRay(glm::vec3 &hit, glm::vec3 &normal, GameObject **object) {
auto vc = currentCam;
glm::vec3 from(vc.position.x, vc.position.y, vc.position.z);
glm::vec3 tmp = vc.rotation * glm::vec3(1000.f, 0.f, 0.f);
return hitWorldRay(from, tmp, hit, normal, object);
}
bool RWGame::hitWorldRay(const glm::vec3 &start, const glm::vec3 &direction, glm::vec3 &hit, glm::vec3 &normal, GameObject **object) {
auto from = btVector3(start.x, start.y, start.z);
auto to = btVector3(start.x + direction.x, start.y + direction.y,
start.z + direction.z);
btCollisionWorld::ClosestRayResultCallback ray(from, to);
world->dynamicsWorld->rayTest(from, to, ray);
if (ray.hasHit()) {
hit = glm::vec3(ray.m_hitPointWorld.x(), ray.m_hitPointWorld.y(),
ray.m_hitPointWorld.z());
normal =
glm::vec3(ray.m_hitNormalWorld.x(), ray.m_hitNormalWorld.y(),
ray.m_hitNormalWorld.z());
if (object) {
*object = static_cast<GameObject*>(
ray.m_collisionObject->getUserPointer());
}
return true;
}
return false;
}
2016-09-09 22:13:20 +02:00
void RWGame::saveGame(const std::string& savename) {
RW_UNUSED(savename);
}
2016-09-09 22:13:20 +02:00
void RWGame::loadGame(const std::string& savename) {
delete state.script;
2016-09-09 22:13:20 +02:00
log.info("Game", "Loading game " + savename);
2016-09-09 22:13:20 +02:00
newGame();
2016-09-09 22:13:20 +02:00
startScript("data/main.scm");
if (!SaveGame::loadGame(state, savename)) {
2016-09-09 22:13:20 +02:00
log.error("Game", "Failed to load game");
}
2018-06-05 02:01:42 +02:00
// Set fade splash
state.world->data->loadSplash("SPLASH1");
}
2016-09-09 22:13:20 +02:00
void RWGame::startScript(const std::string& name) {
script = data.loadSCM(name);
if (script) {
vm = std::make_unique<ScriptMachine>(&state, script, &opcodes);
state.script = vm.get();
2016-09-09 22:13:20 +02:00
} else {
log.error("Game", "Failed to load SCM: " + name);
}
}
2016-09-01 23:14:15 +02:00
// Modifiers for GTA3 we try to recreate
#define RW_GAME_VERSION 1100
#define RW_GAME_GTA3_GERMAN 0
#define RW_GAME_GTA3_ANNIVERSARY 0
void RWGame::handleCheatInput(char symbol) {
2016-09-09 22:13:20 +02:00
cheatInputWindow = cheatInputWindow.substr(1) + symbol;
// Helper to check for cheats
2017-09-12 01:40:11 +02:00
auto checkForCheat = [this](const std::string& cheat,
2016-09-09 22:13:20 +02:00
std::function<void()> action) {
RW_CHECK(cheatInputWindow.length() >= cheat.length(), "Cheat too long");
size_t offset = cheatInputWindow.length() - cheat.length();
if (cheat == cheatInputWindow.substr(offset)) {
log.info("Game", "Cheat triggered: '" + cheat + "'");
if (action) {
action();
}
}
};
// Player related cheats
{
auto player = getWorld()->getPlayer()->getCharacter();
2016-09-09 22:13:20 +02:00
#ifdef RW_GAME_GTA3_GERMAN // Germans got their own cheat
std::string health_cheat = "GESUNDHEIT";
2016-09-01 23:14:15 +02:00
#else
2016-09-09 22:13:20 +02:00
std::string health_cheat = "HEALTH";
2016-09-01 23:14:15 +02:00
#endif
2016-09-09 22:13:20 +02:00
checkForCheat(health_cheat, [&] {
player->getCurrentState().health = 100.f;
state.showHelpMessage("CHEAT3"); // III / VC: Inputting health cheat.
2016-09-09 22:13:20 +02:00
});
#if RW_GAME_VERSION >= 1100 // Changed cheat name in version 1.1
std::string armor_cheat = "TORTOISE";
2016-09-01 23:14:15 +02:00
#else
2016-09-09 22:13:20 +02:00
std::string armor_cheat = "TURTOISE";
2016-09-01 23:14:15 +02:00
#endif
2016-09-09 22:13:20 +02:00
checkForCheat(armor_cheat, [&] {
player->getCurrentState().armour = 100.f;
state.showHelpMessage("CHEAT4"); // III / VC: Inputting armor cheat.
2016-09-09 22:13:20 +02:00
});
checkForCheat("GUNSGUNSGUNS", [&] {
2018-10-29 22:57:18 +01:00
static constexpr std::array<int, 11> ammo = {{
1, //baseball bat
100,//pistol
100,//uzi
20, //shotgun
5, //grenade
5, //molotov
5, //rocket launcher
20, //flamethrower
200,//ak47
200,//m16
5 //sniper rifle
}};
2018-10-29 22:57:18 +01:00
for (auto i = 0u; i < ammo.size(); i++)
2018-08-30 02:02:02 +02:00
player->addToInventory(static_cast<int>(i+1),ammo[i]);
state.showHelpMessage("CHEAT2"); // III / VC: Inputting weapon cheats.
2016-09-09 22:13:20 +02:00
});
checkForCheat("IFIWEREARICHMAN", [&] {
state.playerInfo.money += 250000;
state.showHelpMessage("CHEAT6"); // III: Inputting money cheat.
2016-09-09 22:13:20 +02:00
});
checkForCheat("MOREPOLICEPLEASE", [&] {
// @todo raise to next wanted level
state.showHelpMessage("CHEAT5"); // III / VC: Inputting wanted level cheats.
2016-09-09 22:13:20 +02:00
});
checkForCheat("NOPOLICEPLEASE", [&] {
// @todo lower to next lower wanted level
state.showHelpMessage("CHEAT5"); // III / VC: Inputting wanted level cheats.
2016-09-09 22:13:20 +02:00
});
}
// Misc cheats.
{
checkForCheat("BANGBANGBANG", [&] {
// @todo Explode nearby vehicles
// @todo What radius?
state.showHelpMessage("CHEAT1"); // III / VC: Inputting most cheats.
2016-09-09 22:13:20 +02:00
});
checkForCheat("GIVEUSATANK", [&] {
// The iPod / Android version of the game (10th year anniversary) spawns random
// (?) vehicles instead of always rhino
#if RW_GAME_GTA3_ANNIVERSARY != 0
uint16_t vehicleModel = 110; // @todo Which cars are spawned?!
2016-09-01 23:14:15 +02:00
#else
uint16_t vehicleModel = 122;
2016-09-01 23:14:15 +02:00
#endif
const auto ch = getWorld()->getPlayer()->getCharacter();// @todo Change spawn place to be more like in original game
const auto playerRot = ch->getRotation();
const auto spawnPos = ch->getPosition() + playerRot * glm::vec3(0.f, 3.f, 0.f);
const auto spawnRot = glm::quat(
glm::vec3(0.f, 0.f, glm::roll(playerRot) + glm::half_pi<float>()));
getWorld()->createVehicle(vehicleModel, spawnPos, spawnRot);
state.showHelpMessage("CHEAT1"); // III / VC: Inputting most cheats.
2016-09-09 22:13:20 +02:00
});
checkForCheat("CORNERSLIKEMAD", [&] {
// @todo Weird car handling
state.showHelpMessage("CHEAT1"); // III / VC: Inputting most cheats.
2016-09-09 22:13:20 +02:00
});
checkForCheat("ANICESETOFWHEELS", [&] {
// @todo Hide car bodies
state.showHelpMessage("CHEAT1"); // III / VC: Inputting most cheats.
2016-09-09 22:13:20 +02:00
});
checkForCheat("CHITTYCHITTYBB", [&] {
// @todo Cars can fly
state.showHelpMessage("CHEAT1"); // III / VC: Inputting most cheats.
2016-09-09 22:13:20 +02:00
});
checkForCheat("NASTYLIMBCHEAT", [&] {
// @todo Makes it possible to shoot limbs off, iirc?
state.showHelpMessage("CHEAT1"); // III / VC: Inputting most cheats.
2016-09-09 22:13:20 +02:00
});
checkForCheat("ILIKEDRESSINGUP", [&] {
// @todo Which skins will be chosen?
state.showHelpMessage("CHEAT1"); // III / VC: Inputting most cheats.
2016-09-09 22:13:20 +02:00
});
}
// Pedestrian cheats
{
checkForCheat("WEAPONSFORALL", [&] {
// @todo Give all pedestrians weapons.. this is also saved in the
// savegame?!
// @todo Which weapons do they get?
state.showHelpMessage("CHEAT1"); // III / VC: Inputting most cheats.
2016-09-09 22:13:20 +02:00
});
checkForCheat("NOBODYLIKESME", [&] {
// @todo Set all pedestrians hostile towards player.. this is also
// saved in the savegame?!
state.showHelpMessage("CHEAT1"); // III / VC: Inputting most cheats.
2016-09-09 22:13:20 +02:00
});
checkForCheat("ITSALLGOINGMAAAD", [&] {
// @todo Set all pedestrians to fighting each other.. this is also
// saved in the savegame?!
state.showHelpMessage("CHEAT1"); // III / VC: Inputting most cheats.
2016-09-09 22:13:20 +02:00
});
// Game speed cheats
checkForCheat("TIMEFLIESWHENYOU", [&] {
// @todo Set fast gamespeed
state.showHelpMessage("CHEAT1"); // III / VC: Inputting most cheats.
2016-09-09 22:13:20 +02:00
});
checkForCheat("BOOOOORING", [&] {
// @todo Set slow gamespeed
state.showHelpMessage("CHEAT1"); // III / VC: Inputting most cheats.
2016-09-09 22:13:20 +02:00
});
}
// Weather cheats
{
checkForCheat("ILIKESCOTLAND", [&] {
// @todo Set weather to cloudy
state.showHelpMessage("CHEAT7"); // III / VC: Inputting weather cheats.
2016-09-09 22:13:20 +02:00
});
checkForCheat("SKINCANCERFORME", [&] {
// @todo Set sunny / normal weather
state.showHelpMessage("CHEAT7"); // III / VC: Inputting weather cheats.
2016-09-09 22:13:20 +02:00
});
checkForCheat("MADWEATHER", [&] {
// @todo Set bad weather
state.showHelpMessage("CHEAT7"); // III / VC: Inputting weather cheats.
2016-09-09 22:13:20 +02:00
});
checkForCheat("ILOVESCOTLAND", [&] {
// @todo Set weather to rainy
state.showHelpMessage("CHEAT7"); // III / VC: Inputting weather cheats.
2016-09-09 22:13:20 +02:00
});
checkForCheat("PEASOUP", [&] {
// @todo Set weather to foggy
state.showHelpMessage("CHEAT7"); // III / VC: Inputting weather cheats.
2016-09-09 22:13:20 +02:00
});
}
2016-09-01 23:14:15 +02:00
}
2016-09-09 22:13:20 +02:00
int RWGame::run() {
namespace chrono = std::chrono;
auto lastFrame = chrono::steady_clock::now();
2018-05-26 21:32:33 +02:00
const float deltaTime = GAME_TIMESTEP;
float accumulatedTime = 0.0f;
2016-09-09 22:13:20 +02:00
// Loop until we run out of states.
bool running = true;
while (stateManager.currentState() && running) {
2016-09-09 22:13:20 +02:00
RW_PROFILE_FRAME_BOUNDARY();
RW_PROFILE_SCOPE("Main Loop");
2016-09-09 22:13:20 +02:00
running = updateInput();
2016-09-09 22:13:20 +02:00
auto currentFrame = chrono::steady_clock::now();
auto frameTime =
chrono::duration<float>(currentFrame - lastFrame).count();
lastFrame = currentFrame;
if (!world->isPaused()) {
accumulatedTime += frameTime;
2016-09-09 22:13:20 +02:00
// Clamp frameTime, so we won't freeze completely
if (frameTime > 0.1f) {
frameTime = 0.1f;
}
accumulatedTime = tickWorld(deltaTime, accumulatedTime);
}
render(1, frameTime);
getWindow().swap();
2016-09-09 22:13:20 +02:00
// Make sure the topmost state is the correct state
stateManager.updateStack();
}
2016-09-09 22:13:20 +02:00
stateManager.clear();
return 0;
}
2016-09-09 22:13:20 +02:00
float RWGame::tickWorld(const float deltaTime, float accumulatedTime) {
RW_PROFILE_SCOPEC(__func__, MP_GREEN);
auto deltaTimeWithTimeScale =
deltaTime * world->state->basic.timeScale;
2016-09-09 22:13:20 +02:00
while (accumulatedTime >= deltaTime) {
if (!stateManager.currentState()) {
break;
2016-09-09 22:13:20 +02:00
}
{
RW_PROFILE_SCOPEC("stepSimulation", MP_DARKORANGE1);
world->dynamicsWorld->stepSimulation(
deltaTimeWithTimeScale, kMaxPhysicsSubSteps, deltaTime);
}
2016-09-09 22:13:20 +02:00
stateManager.tick(deltaTimeWithTimeScale);
tick(deltaTimeWithTimeScale);
2016-09-09 22:13:20 +02:00
getState()->swapInputState();
accumulatedTime -= deltaTime;
2016-09-09 22:13:20 +02:00
}
return accumulatedTime;
}
2016-09-09 22:13:20 +02:00
bool RWGame::updateInput() {
2018-08-30 16:47:12 +02:00
RW_PROFILE_SCOPE(__func__);
SDL_Event event;
while (SDL_PollEvent(&event)) {
2019-05-21 21:04:55 +02:00
if (imgui.processEvent(event)) {
2018-12-20 20:31:14 +01:00
continue;
}
switch (event.type) {
case SDL_QUIT:
return false;
case SDL_WINDOWEVENT:
switch (event.window.event) {
case SDL_WINDOWEVENT_FOCUS_GAINED:
inFocus = true;
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
inFocus = false;
break;
}
break;
case SDL_KEYDOWN:
globalKeyEvent(event);
break;
case SDL_MOUSEMOTION:
event.motion.xrel *= MOUSE_SENSITIVITY_SCALE;
event.motion.yrel *= MOUSE_SENSITIVITY_SCALE;
break;
}
GameInput::updateGameInputState(&getState()->input[0], event);
if (stateManager.currentState()) {
2018-08-30 16:47:12 +02:00
RW_PROFILE_SCOPE("State");
stateManager.currentState()->handleEvent(event);
}
}
return true;
}
2016-09-09 22:13:20 +02:00
void RWGame::tick(float dt) {
2018-08-30 16:47:12 +02:00
RW_PROFILE_SCOPE(__func__);
State* currState = stateManager.states.back().get();
2016-09-09 22:13:20 +02:00
static float clockAccumulator = 0.f;
static float scriptTimerAccumulator = 0.f;
static ScriptInt beepTime = std::numeric_limits<ScriptInt>::max();
2016-09-09 22:13:20 +02:00
if (currState->shouldWorldUpdate()) {
2017-10-12 18:27:05 +02:00
world->chase.update(dt);
2016-09-09 22:13:20 +02:00
// Clear out any per-tick state.
world->clearTickData();
state.gameTime += dt;
2016-09-09 22:13:20 +02:00
clockAccumulator += dt;
while (clockAccumulator >= 1.f) {
state.basic.gameMinute++;
while (state.basic.gameMinute >= 60) {
state.basic.gameMinute = 0;
state.basic.gameHour++;
while (state.basic.gameHour >= 24) {
state.basic.gameHour = 0;
2016-09-09 22:13:20 +02:00
}
}
clockAccumulator -= 1.f;
}
constexpr float timerClockRate = 1.f / 30.f;
if (state.scriptTimerVariable && !state.scriptTimerPaused) {
scriptTimerAccumulator += dt;
while (scriptTimerAccumulator >= timerClockRate) {
// Original game uses milliseconds
(*state.scriptTimerVariable) -= timerClockRate * 1000;
// 11 seconds
if (*state.scriptTimerVariable <= 11000 &&
beepTime - *state.scriptTimerVariable >= 1000) {
beepTime = *state.scriptTimerVariable;
// @todo beep
}
if (*state.scriptTimerVariable <= 0) {
(*state.scriptTimerVariable) = 0;
state.scriptTimerVariable = nullptr;
}
scriptTimerAccumulator -= timerClockRate;
}
}
tickObjects(dt);
2016-09-09 22:13:20 +02:00
state.text.tick(dt);
2016-09-09 22:13:20 +02:00
if (vm) {
2016-09-09 22:13:20 +02:00
try {
vm->execute(dt);
2016-09-09 22:13:20 +02:00
} catch (SCMException& ex) {
2019-01-18 01:37:23 +01:00
std::cerr << ex.what() << '\n';
2016-09-09 22:13:20 +02:00
log.error("Script", ex.what());
throw;
}
}
/// @todo this doesn't make sense as the condition
if (state.playerObject) {
currentCam.frustum.update(currentCam.frustum.projection() *
currentCam.getView());
2016-09-09 22:13:20 +02:00
// Use the current camera position to spawn pedestrians.
world->cleanupTraffic(currentCam);
// Only create new traffic outside cutscenes
if (!state.currentCutscene) {
world->createTraffic(currentCam);
}
2016-09-09 22:13:20 +02:00
}
}
}
void RWGame::tickObjects(float dt) const {
RW_PROFILE_SCOPEC(__func__, MP_MAGENTA1);
world->updateEffects();
{
RW_PROFILE_SCOPEC("allObjects", MP_HOTPINK1);
RW_PROFILE_COUNTER_SET("tickObjects/allObjects", world->allObjects.size());
for (auto &object : world->allObjects) {
object->tick(dt);
}
}
{
RW_PROFILE_SCOPEC("garages", MP_HOTPINK2);
for (auto &g : world->garages) {
g->tick(dt);
}
}
{
RW_PROFILE_SCOPEC("payphones", MP_HOTPINK3);
for (auto &p : world->payphones) {
p->tick(dt);
}
}
world->destroyQueuedObjects();
}
2016-09-09 22:13:20 +02:00
void RWGame::render(float alpha, float time) {
RW_PROFILE_SCOPEC(__func__, MP_CORNFLOWERBLUE);
2019-05-21 21:04:55 +02:00
RW_UNUSED(time);
2018-12-01 18:57:06 +01:00
lastDraws = getRenderer().getRenderer().getDrawCount();
2016-09-09 22:13:20 +02:00
2018-12-01 18:57:06 +01:00
getRenderer().getRenderer().swap();
2019-05-21 21:04:55 +02:00
imgui.startFrame();
2016-09-09 22:13:20 +02:00
// Update the camera
if (!stateManager.states.empty()) {
currentCam = stateManager.states.back()->getCamera(alpha);
}
glm::ivec2 windowSize = getWindow().getSize();
renderer.setViewport(windowSize.x, windowSize.y);
2016-09-09 22:13:20 +02:00
ViewCamera viewCam = currentCam;
2016-09-09 22:13:20 +02:00
viewCam.frustum.aspectRatio =
windowSize.x / static_cast<float>(windowSize.y);
if (state.isCinematic) {
2016-09-09 22:13:20 +02:00
viewCam.frustum.fov *= viewCam.frustum.aspectRatio;
}
2018-08-12 16:28:13 +02:00
world->sound.updateListenerTransform(viewCam);
2016-09-09 22:13:20 +02:00
glEnable(GL_DEPTH_TEST);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
2018-12-01 18:57:06 +01:00
renderer.getRenderer().pushDebugGroup("World");
2016-09-09 22:13:20 +02:00
2016-10-20 01:49:15 +02:00
renderer.renderWorld(world.get(), viewCam, alpha);
2016-09-09 22:13:20 +02:00
2018-12-01 18:57:06 +01:00
renderer.getRenderer().popDebugGroup();
2016-09-09 22:13:20 +02:00
2019-05-21 21:04:55 +02:00
renderDebugView();
2018-12-01 18:57:06 +01:00
if (!world->isPaused()) hudDrawer.drawOnScreenText(world.get(), renderer);
if (stateManager.currentState()) {
2018-08-30 16:47:12 +02:00
RW_PROFILE_SCOPE("state");
stateManager.draw(renderer);
}
2018-12-20 20:31:14 +01:00
2019-05-21 21:04:55 +02:00
imgui.endFrame(viewCam);
}
2019-05-21 21:04:55 +02:00
void RWGame::renderDebugView() {
2018-08-30 16:47:12 +02:00
RW_PROFILE_SCOPE(__func__);
2016-10-12 01:29:24 +02:00
switch (debugview_) {
case DebugViewMode::Physics:
2016-10-20 01:49:15 +02:00
world->dynamicsWorld->debugDrawWorld();
2018-12-01 18:57:06 +01:00
debug.flush(renderer);
2016-10-12 01:29:24 +02:00
break;
case DebugViewMode::Navigation:
2019-05-21 21:04:55 +02:00
renderDebugPaths();
2016-10-12 01:29:24 +02:00
break;
default:
break;
2016-09-09 22:13:20 +02:00
}
2015-02-16 01:39:19 +01:00
}
2019-05-21 21:04:55 +02:00
void RWGame::renderDebugPaths() {
2016-09-09 22:13:20 +02:00
btVector3 roadColour(1.f, 0.f, 0.f);
btVector3 pedColour(0.f, 0.f, 1.f);
2018-09-08 01:22:18 +02:00
for (const auto& n : world->aigraph.nodes) {
2016-09-09 22:13:20 +02:00
btVector3 p(n->position.x, n->position.y, n->position.z);
2018-12-10 01:51:05 +01:00
auto& col = n->type == ai::NodeType::Pedestrian ? pedColour : roadColour;
debug.drawLine(p - btVector3(0.f, 0.f, 1.f),
p + btVector3(0.f, 0.f, 1.f), col);
debug.drawLine(p - btVector3(1.f, 0.f, 0.f),
p + btVector3(1.f, 0.f, 0.f), col);
debug.drawLine(p - btVector3(0.f, 1.f, 0.f),
p + btVector3(0.f, 1.f, 0.f), col);
2016-09-09 22:13:20 +02:00
2018-09-08 01:22:18 +02:00
for (const auto& c : n->connections) {
2016-09-09 22:13:20 +02:00
btVector3 f(c->position.x, c->position.y, c->position.z);
debug.drawLine(p, f, col);
2016-09-09 22:13:20 +02:00
}
}
// Draw Garage bounds
2018-05-31 22:38:47 +02:00
for (const auto& garage : world->garages) {
2016-09-09 22:13:20 +02:00
btVector3 minColor(1.f, 0.f, 0.f);
btVector3 maxColor(0.f, 1.f, 0.f);
2018-05-31 22:38:47 +02:00
btVector3 min(garage->min.x, garage->min.y, garage->min.z);
btVector3 max(garage->max.x, garage->max.y, garage->max.z);
debug.drawLine(min, min + btVector3(0.5f, 0.f, 0.f), minColor);
debug.drawLine(min, min + btVector3(0.f, 0.5f, 0.f), minColor);
debug.drawLine(min, min + btVector3(0.f, 0.f, 0.5f), minColor);
2016-09-09 22:13:20 +02:00
debug.drawLine(max, max - btVector3(0.5f, 0.f, 0.f), maxColor);
debug.drawLine(max, max - btVector3(0.f, 0.5f, 0.f), maxColor);
debug.drawLine(max, max - btVector3(0.f, 0.f, 0.5f), maxColor);
2016-09-09 22:13:20 +02:00
}
// Draw vehicle generators
for (const auto& generator : state.vehicleGenerators) {
2016-09-09 22:13:20 +02:00
btVector3 color(1.f, 0.f, 0.f);
btVector3 position(generator.position.x, generator.position.y,
generator.position.z);
float heading = glm::radians(generator.heading);
auto back =
btVector3(0.f, -1.f, 0.f).rotate(btVector3(0.f, 0.f, 1.f), heading);
auto right = btVector3(0.15f, -0.15f, 0.f)
.rotate(btVector3(0.f, 0.f, 1.f), heading);
auto left = btVector3(-0.15f, -0.15f, 0.f)
.rotate(btVector3(0.f, 0.f, 1.f), heading);
debug.drawLine(position, position + back, color);
debug.drawLine(position, position + right, color);
debug.drawLine(position, position + left, color);
2016-09-09 22:13:20 +02:00
}
// Draw the targetNode if a character is driving a vehicle
for (auto& p : world->pedestrianPool.objects) {
2018-09-05 00:14:27 +02:00
auto v = static_cast<CharacterObject*>(p.second.get());
static const glm::vec3 color(1.f, 1.f, 0.f);
if (auto vehicle = v->getCurrentVehicle(); vehicle)
{
if (v->controller->targetNode) {
debug.drawLine(v->getPosition(), v->controller->targetNode->position, color);
}
2018-08-08 01:24:39 +02:00
auto [center, halfSize] = vehicle->obstacleCheckVolume();
std::array<glm::vec3, 8> corners { {
glm::vec3{- halfSize.x, - halfSize.y, - halfSize.z},
glm::vec3{+ halfSize.x, - halfSize.y, - halfSize.z},
glm::vec3{- halfSize.x, - halfSize.y, + halfSize.z},
glm::vec3{+ halfSize.x, - halfSize.y, + halfSize.z},
glm::vec3{- halfSize.x, + halfSize.y, - halfSize.z},
glm::vec3{+ halfSize.x, + halfSize.y, - halfSize.z},
glm::vec3{- halfSize.x, + halfSize.y, + halfSize.z},
glm::vec3{+ halfSize.x, + halfSize.y, + halfSize.z},
}
};
const auto iRotation = (vehicle->getRotation());
const auto rCenter = iRotation * center;
std::transform(corners.begin(), corners.end(), corners.begin(),
[&](const auto& p) -> glm::vec3 {
return vehicle->getPosition() + rCenter + iRotation * p;
});
static const glm::vec3 color2(1.f, 0.f, 0.f);
debug.drawLine(corners[0], corners[1], color2);
debug.drawLine(corners[0], corners[2], color2);
debug.drawLine(corners[3], corners[1], color2);
debug.drawLine(corners[3], corners[2], color2);
debug.drawLine(corners[0], corners[4], color2);
debug.drawLine(corners[1], corners[5], color2);
debug.drawLine(corners[2], corners[6], color2);
debug.drawLine(corners[3], corners[7], color2);
debug.drawLine(corners[4], corners[5], color2);
debug.drawLine(corners[4], corners[6], color2);
debug.drawLine(corners[7], corners[5], color2);
debug.drawLine(corners[7], corners[6], color2);
}
}
2018-12-01 18:57:06 +01:00
debug.flush(renderer);
}
2016-09-09 22:13:20 +02:00
void RWGame::globalKeyEvent(const SDL_Event& event) {
2016-10-12 01:29:24 +02:00
const auto toggle_debug = [&](DebugViewMode m) {
debugview_ = debugview_ == m ? DebugViewMode::Disabled : m;
};
2016-09-09 22:13:20 +02:00
switch (event.key.keysym.sym) {
case SDLK_LEFTBRACKET:
world->offsetGameTime(-30);
break;
case SDLK_RIGHTBRACKET:
world->offsetGameTime(30);
break;
case SDLK_9:
world->state->basic.timeScale *= 0.5f;
2016-09-09 22:13:20 +02:00
break;
case SDLK_0:
world->state->basic.timeScale *= 2.0f;
2016-09-09 22:13:20 +02:00
break;
case SDLK_F1:
2016-10-12 01:29:24 +02:00
toggle_debug(DebugViewMode::General);
2016-09-09 22:13:20 +02:00
break;
case SDLK_F2:
2016-10-12 01:29:24 +02:00
toggle_debug(DebugViewMode::Navigation);
2016-09-09 22:13:20 +02:00
break;
case SDLK_F3:
2016-10-12 01:29:24 +02:00
toggle_debug(DebugViewMode::Physics);
break;
case SDLK_F4:
toggle_debug(DebugViewMode::Objects);
2016-09-09 22:13:20 +02:00
break;
default:
break;
}
std::string keyName = SDL_GetKeyName(event.key.keysym.sym);
if (getWorld()->getPlayer() && keyName.length() == 1) {
2016-09-09 22:13:20 +02:00
char symbol = keyName[0];
handleCheatInput(symbol);
}
}