1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-23 19:02:39 +01:00

Garages continuation

This commit is contained in:
husho 2018-05-31 23:38:47 +03:00
parent 10ef3448aa
commit 92d2a614b7
17 changed files with 1084 additions and 982 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ documentation/
*swp
*.user*
.DS_Store
.vscode/

View File

@ -61,8 +61,8 @@ set(RWENGINE_SOURCES
src/engine/GameState.hpp
src/engine/GameWorld.cpp
src/engine/GameWorld.hpp
src/engine/GarageController.cpp
src/engine/GarageController.hpp
src/engine/Garage.cpp
src/engine/Garage.hpp
src/engine/Payphone.cpp
src/engine/Payphone.hpp
src/engine/SaveGame.cpp

View File

@ -211,56 +211,6 @@ struct BlipData {
}
};
enum class GarageType {
Mission = 1,
BombShop1 = 2,
BombShop2 = 3,
BombShop3 = 4,
Respray = 5,
CollectCars1 = 8,
CollectCars2 = 9,
MissionForCarToComeOut = 11,
Crusher = 13,
MissionKeepCar = 14,
Hideout1 = 16,
Hideout2 = 17,
Hideout3 = 18,
MissionToOpenAndClose = 19,
MissionForSpecificCar = 20,
MissionKeepCarAndRemainClosed = 21,
};
enum class GarageState { Closed, Closing, Opening, Opened };
/**
* Data for garages
*/
struct GarageInfo {
GarageType type;
int id;
glm::vec3 min;
glm::vec3 max;
GameObject* target;
GarageState state;
GarageInfo(int id_, const glm::vec3 min_, const glm::vec3 max_,
GarageType type_)
: type(type_)
, id(id_)
, min(min_)
, max(max_)
, target(nullptr)
, state(GarageState::Closed) {
}
int getScriptObjectID() const {
return id;
}
};
enum class HudFlash {
Disabled = -1,
FlashArmor = 3,
@ -406,8 +356,6 @@ public:
std::map<int, BlipData> radarBlips;
std::vector<GarageInfo> garages;
/**
* Bitsets for the car import / export list mission
*/

View File

@ -386,77 +386,11 @@ PickupObject* GameWorld::createPickup(const glm::vec3& pos, int id, int type) {
return pickup;
}
GarageInfo* GameWorld::createGarage(const glm::vec3 coord0,
const glm::vec3 coord1, const int type) {
glm::vec3 min;
glm::vec3 max;
glm::vec3 midpoint;
min.x = std::min(coord0.x, coord1.x);
min.y = std::min(coord0.y, coord1.y);
min.z = std::min(coord0.z, coord1.z);
max.x = std::max(coord0.x, coord1.x);
max.y = std::max(coord0.y, coord1.y);
max.z = std::max(coord0.z, coord1.z);
midpoint.x = (min.x + max.x) / 2;
midpoint.y = (min.y + max.y) / 2;
midpoint.z = (min.z + max.z) / 2;
// Find door object for this garage
InstanceObject* door = nullptr;
for (auto p : instancePool.objects) {
auto o = p.second;
if (!o->getModel()) continue;
if (!SimpleModelInfo::isDoorModel(
o->getModelInfo<BaseModelInfo>()->name))
continue;
// Is this how the game finds door object?
if (glm::distance(midpoint, o->getPosition()) < 20.f) {
door = static_cast<InstanceObject*>(o);
}
}
// Create garage
int id = state->garages.size();
GarageInfo* info =
new GarageInfo{id, min, max, static_cast<GarageType>(type)};
state->garages.push_back(*info);
switch (static_cast<GarageType>(type)) {
case GarageType::Mission:
case GarageType::CollectCars1:
case GarageType::CollectCars2:
case GarageType::MissionForCarToComeOut:
case GarageType::MissionKeepCar:
case GarageType::Hideout1:
case GarageType::Hideout2:
case GarageType::Hideout3:
case GarageType::MissionToOpenAndClose:
case GarageType::MissionForSpecificCar:
case GarageType::MissionKeepCarAndRemainClosed: {
info->state = GarageState::Closed;
break;
}
case GarageType::BombShop1:
case GarageType::BombShop2:
case GarageType::BombShop3:
case GarageType::Respray:
case GarageType::Crusher: {
info->state = GarageState::Opened;
break;
}
}
// Create controller
garageControllers.push_back(
std::make_unique<GarageController>(GarageController(this, info, door)));
return info;
Garage* GameWorld::createGarage(const glm::vec3 coord0, const glm::vec3 coord1,
Garage::Type type) {
const int id = garages.size();
garages.emplace_back(std::make_unique<Garage>(this, id, coord0, coord1, type));
return garages.back().get();
}
Payphone* GameWorld::createPayphone(const glm::vec2 coord) {

View File

@ -17,7 +17,7 @@
#include <ai/AIGraphNode.hpp>
#include <audio/SoundManager.hpp>
#include <engine/GarageController.hpp>
#include <engine/Garage.hpp>
#include <engine/Payphone.hpp>
#include <objects/ObjectTypes.hpp>
@ -35,7 +35,7 @@ class btSequentialImpulseConstraintSolver;
struct btDbvtBroadphase;
class GameState;
class GarageController;
class Garage;
class Payphone;
class PlayerController;
@ -150,8 +150,8 @@ public:
/**
* Creates a garage
*/
GarageInfo* createGarage(const glm::vec3 coord0, const glm::vec3 coord1,
const int type);
Garage* createGarage(const glm::vec3 coord0, const glm::vec3 coord1,
Garage::Type type);
/**
* Creates a payphone
@ -271,7 +271,7 @@ public:
std::vector<PlayerController*> players;
std::vector<std::unique_ptr<GarageController>> garageControllers;
std::vector<std::unique_ptr<Garage>> garages;
std::vector<std::unique_ptr<Payphone>> payphones;

View File

@ -0,0 +1,886 @@
#include "Garage.hpp"
#include <btBulletDynamicsCommon.h>
#include <glm/gtx/quaternion.hpp>
#include "dynamics/CollisionInstance.hpp"
#include "ai/PlayerController.hpp"
#include "engine/GameState.hpp"
#include "objects/CharacterObject.hpp"
#include "objects/GameObject.hpp"
#include "objects/InstanceObject.hpp"
#include "objects/VehicleObject.hpp"
Garage::Garage(GameWorld* engine_, const int id_, const glm::vec3 coord0,
const glm::vec3 coord1, const Type type_)
: engine(engine_), id(id_), type(type_) {
min.x = std::min(coord0.x, coord1.x);
min.y = std::min(coord0.y, coord1.y);
min.z = std::min(coord0.z, coord1.z);
max.x = std::max(coord0.x, coord1.x);
max.y = std::max(coord0.y, coord1.y);
max.z = std::max(coord0.z, coord1.z);
glm::vec2 midpoint;
midpoint.x = (min.x + max.x) / 2;
midpoint.y = (min.y + max.y) / 2;
// Find door objects for this garage
for (const auto p : engine->instancePool.objects) {
const auto inst = static_cast<InstanceObject*>(p.second);
if (!inst->getModel()) {
continue;
}
if (!SimpleModelInfo::isDoorModel(
inst->getModelInfo<BaseModelInfo>()->name)) {
continue;
}
const auto instPos = inst->getPosition();
const auto xDist = std::abs(instPos.x - midpoint.x);
const auto yDist = std::abs(instPos.y - midpoint.y);
if (xDist < 20.f && yDist < 20.f) {
if (!doorObject) {
doorObject = inst;
continue;
} else {
secondDoorObject = inst;
}
}
}
if (doorObject) {
startPosition = doorObject->getPosition();
// Setup door height based on model's bounding box
auto doorModel = doorObject->getModelInfo<BaseModelInfo>();
auto collision = doorModel->getCollision();
// Original behavior - game actually subtracts 0.1f
doorHeight =
collision->boundingBox.max.z - collision->boundingBox.min.z - 0.1f;
}
if (secondDoorObject) {
startPositionSecondDoor = secondDoorObject->getPosition();
}
step /= doorHeight;
switch (type) {
case Garage::Type::Mission:
case Garage::Type::CollectCars1:
case Garage::Type::CollectCars2:
case Garage::Type::MissionForCarToComeOut:
case Garage::Type::MissionKeepCar:
case Garage::Type::Hideout1:
case Garage::Type::Hideout2:
case Garage::Type::Hideout3:
case Garage::Type::MissionToOpenAndClose:
case Garage::Type::MissionForSpecificCar:
case Garage::Type::MissionKeepCarAndRemainClosed: {
state = State::Closed;
break;
}
case Garage::Type::BombShop1:
case Garage::Type::BombShop2:
case Garage::Type::BombShop3:
case Garage::Type::Respray:
case Garage::Type::Crusher: {
state = State::Opened;
break;
}
}
if (state == State::Closed) {
fraction = 0.f;
} else {
fraction = 1.f;
}
if (doorObject) {
updateDoor();
}
}
void Garage::makeDoorSwing() {
// This is permanent, you can't restore it
// back to non swing just like in original game
// Values are from original game
if (!swingType) {
swingType = true;
doorHeight /= 2.0f;
doorHeight -= 0.1f;
}
}
bool Garage::isTargetInsideGarage() const {
return state == State::Closed && isObjectInsideGarage(target);
}
void Garage::activate() {
active = true;
if (type == Type::MissionForCarToComeOut && state == State::Closed) {
state = State::Opening;
}
}
void Garage::deactivate() {
active = false;
}
void Garage::open() {
if (state == State::Closed || state == State::Closing) {
state = State::Opening;
}
}
void Garage::close() {
if (state == State::Opened || state == State::Opening) {
state = State::Closing;
}
}
float Garage::getDistanceToGarage(const glm::vec3 point) {
// Seems like original game ignores z axis
float dx = std::max({min.x - point.x, 0.f, point.x - max.x});
float dy = std::max({min.y - point.y, 0.f, point.y - max.y});
return std::sqrt(dx * dx + dy * dy);
}
bool Garage::isObjectInsideGarage(GameObject* object) const {
auto p = object->getPosition();
// Do basic check first
if (p.x < min.x) return false;
if (p.y < min.y) return false;
if (p.z < min.z) return false;
if (p.x > max.x) return false;
if (p.y > max.y) return false;
if (p.z > max.z) return false;
// Now check if all collision spheres are inside
// garage's bounding box
auto objectModel = object->getModelInfo<BaseModelInfo>();
auto collision = objectModel->getCollision();
// Peds don't have collisions currently?
if (collision) {
for (auto& sphere : collision->spheres) {
auto c = p + sphere.center;
auto r = sphere.radius;
if (c.x + r < min.x) return false;
if (c.y + r < min.y) return false;
if (c.z + r < min.z) return false;
if (c.x - r > max.x) return false;
if (c.y - r > max.y) return false;
if (c.z - r > max.z) return false;
}
}
return true;
}
bool Garage::shouldClose() {
auto player = engine->getPlayer();
auto plyChar = player->getCharacter();
auto playerPosition = plyChar->getPosition();
auto playerVehicle = plyChar->getCurrentVehicle();
bool playerIsInVehicle = playerVehicle != nullptr;
switch (type) {
case Type::Mission: {
if (!isObjectInsideGarage(static_cast<GameObject*>(plyChar)) &&
isObjectInsideGarage(target) && !playerIsInVehicle &&
getDistanceToGarage(playerPosition) >= 2.f) {
return true;
}
return false;
}
case Type::BombShop1:
case Type::BombShop2:
case Type::BombShop3: {
if (playerIsInVehicle) {
if (isObjectInsideGarage(
static_cast<GameObject*>(playerVehicle)) &&
playerVehicle->isStopped()) {
return true;
}
}
return false;
}
case Type::Respray: {
if (playerIsInVehicle) {
if (isObjectInsideGarage(
static_cast<GameObject*>(playerVehicle)) &&
playerVehicle->isStopped() && !resprayDone) {
return true;
} else if (!isObjectInsideGarage(
static_cast<GameObject*>(playerVehicle)) &&
getDistanceToGarage(playerVehicle->getPosition()) >=
2.f &&
resprayDone) {
resprayDone = false;
}
}
return false;
}
case Type::CollectCars1:
case Type::CollectCars2: {
if (playerIsInVehicle) {
if (isObjectInsideGarage(
static_cast<GameObject*>(playerVehicle))) {
if (playerVehicle->getLifetime() !=
GameObject::MissionLifetime) {
return true;
} else {
// @todo show message "come back when youre not busy"
}
}
}
return false;
}
case Type::MissionForCarToComeOut: {
// @todo unimplemented
return false;
}
case Type::Crusher: {
// @todo unimplemented
return false;
}
case Type::MissionKeepCar: {
// @todo unimplemented
return false;
}
case Type::Hideout1:
case Type::Hideout2:
case Type::Hideout3: {
// Not sure about these values
if ((!playerIsInVehicle &&
getDistanceToGarage(playerPosition) >= 5.f) ||
(playerIsInVehicle &&
getDistanceToGarage(playerPosition) >= 10.f)) {
return true;
}
return false;
}
case Type::MissionToOpenAndClose: {
// @todo unimplemented
return false;
}
case Type::MissionForSpecificCar: {
// @todo unimplemented
return false;
}
case Type::MissionKeepCarAndRemainClosed: {
// @todo unimplemented
return false;
}
default: { return false; }
}
return false;
}
bool Garage::shouldOpen() {
auto player = engine->getPlayer();
auto plyChar = player->getCharacter();
auto playerPosition = plyChar->getPosition();
auto playerVehicle = plyChar->getCurrentVehicle();
bool playerIsInVehicle = playerVehicle != nullptr;
switch (type) {
case Type::Mission: {
// Not sure about these values
if (playerIsInVehicle &&
getDistanceToGarage(playerPosition) < 8.f &&
playerVehicle == target) {
return true;
}
return false;
}
case Type::BombShop1:
case Type::BombShop2:
case Type::BombShop3:
case Type::Respray: {
if (garageTimer < engine->getGameTime()) {
return true;
}
return false;
}
case Type::CollectCars1:
case Type::CollectCars2: {
// @todo unimplemented
return false;
}
case Type::MissionForCarToComeOut: {
// @todo unimplemented
return false;
}
case Type::Crusher: {
// @todo unimplemented
return false;
}
case Type::MissionKeepCar: {
// @todo unimplemented
return false;
}
case Type::Hideout1:
case Type::Hideout2:
case Type::Hideout3: {
// Not sure about these values
if ((!playerIsInVehicle &&
getDistanceToGarage(playerPosition) < 5.f) ||
(playerIsInVehicle &&
getDistanceToGarage(playerPosition) < 10.f)) {
return true;
}
return false;
}
case Type::MissionToOpenAndClose: {
// @todo unimplemented
return false;
}
case Type::MissionForSpecificCar: {
// @todo unimplemented
return false;
}
case Type::MissionKeepCarAndRemainClosed: {
// @todo unimplemented
return false;
}
default: { return false; }
}
return false;
}
bool Garage::shouldStopClosing() {
auto player = engine->getPlayer();
auto plyChar = player->getCharacter();
auto playerPosition = plyChar->getPosition();
auto playerVehicle = plyChar->getCurrentVehicle();
bool playerIsInVehicle = playerVehicle != nullptr;
switch (type) {
case Type::Mission:
case Type::BombShop1:
case Type::BombShop2:
case Type::BombShop3:
case Type::Respray:
case Type::CollectCars1:
case Type::CollectCars2: {
return false;
}
case Type::MissionForCarToComeOut: {
// @todo unimplemented
return false;
}
case Type::Crusher: {
// @todo unimplemented
return false;
}
case Type::MissionKeepCar: {
// @todo unimplemented
return false;
}
case Type::Hideout1:
case Type::Hideout2:
case Type::Hideout3: {
// Not sure about these values
if ((!playerIsInVehicle &&
getDistanceToGarage(playerPosition) < 5.f) ||
(playerIsInVehicle &&
getDistanceToGarage(playerPosition) < 10.f)) {
return true;
}
return false;
}
case Type::MissionToOpenAndClose: {
// @todo unimplemented
return false;
}
case Type::MissionForSpecificCar: {
// @todo unimplemented
return false;
}
case Type::MissionKeepCarAndRemainClosed: {
// @todo unimplemented
return false;
}
default: { return false; }
}
return false;
}
bool Garage::shouldStopOpening() {
auto player = engine->getPlayer();
auto plyChar = player->getCharacter();
auto playerPosition = plyChar->getPosition();
auto playerVehicle = plyChar->getCurrentVehicle();
bool playerIsInVehicle = playerVehicle != nullptr;
switch (type) {
case Type::Mission:
case Type::BombShop1:
case Type::BombShop2:
case Type::BombShop3:
case Type::Respray:
case Type::CollectCars1:
case Type::CollectCars2: {
return false;
}
case Type::MissionForCarToComeOut: {
// @todo unimplemented
return false;
}
case Type::Crusher: {
// @todo unimplemented
return false;
}
case Type::MissionKeepCar: {
// @todo unimplemented
return false;
}
case Type::Hideout1:
case Type::Hideout2:
case Type::Hideout3: {
// Not sure about these values
if ((!playerIsInVehicle &&
getDistanceToGarage(playerPosition) >= 5.f) ||
(playerIsInVehicle &&
getDistanceToGarage(playerPosition) >= 10.f)) {
return true;
}
return false;
}
case Type::MissionToOpenAndClose: {
// @todo unimplemented
return false;
}
case Type::MissionForSpecificCar: {
// @todo unimplemented
return false;
}
case Type::MissionKeepCarAndRemainClosed: {
// @todo unimplemented
return false;
}
default: { return false; }
}
return false;
}
void Garage::doOnOpenEvent() {
auto player = engine->getPlayer();
auto plyChar = player->getCharacter();
auto playerPosition = plyChar->getPosition();
auto playerVehicle = plyChar->getCurrentVehicle();
bool playerIsInVehicle = playerVehicle != nullptr;
RW_UNUSED(playerPosition);
RW_UNUSED(playerIsInVehicle);
switch (type) {
case Type::Mission: {
break;
}
case Type::BombShop1: {
break;
}
case Type::BombShop2: {
break;
}
case Type::BombShop3: {
break;
}
case Type::Respray: {
break;
}
case Type::CollectCars1:
case Type::CollectCars2: {
break;
}
case Type::MissionForCarToComeOut: {
break;
}
case Type::Crusher: {
break;
}
case Type::MissionKeepCar: {
break;
}
case Type::Hideout1:
case Type::Hideout2:
case Type::Hideout3: {
break;
}
case Type::MissionToOpenAndClose: {
break;
}
case Type::MissionForSpecificCar: {
break;
}
case Type::MissionKeepCarAndRemainClosed: {
break;
}
default: { break; }
}
}
void Garage::doOnCloseEvent() {
auto player = engine->getPlayer();
auto plyChar = player->getCharacter();
auto playerPosition = plyChar->getPosition();
auto playerVehicle = plyChar->getCurrentVehicle();
bool playerIsInVehicle = playerVehicle != nullptr;
RW_UNUSED(playerPosition);
RW_UNUSED(playerIsInVehicle);
switch (type) {
case Type::Mission: {
player->setInputEnabled(true);
break;
}
case Type::BombShop1:
case Type::BombShop2:
case Type::BombShop3: {
// Find out real value
garageTimer = engine->getGameTime() + 1.5f;
break;
}
case Type::Respray: {
// Find out real value
garageTimer = engine->getGameTime() + 2.f;
playerVehicle->setHealth(1000.f);
break;
}
case Type::CollectCars1:
case Type::CollectCars2: {
break;
}
case Type::MissionForCarToComeOut: {
break;
}
case Type::Crusher: {
break;
}
case Type::MissionKeepCar: {
break;
}
case Type::Hideout1:
case Type::Hideout2:
case Type::Hideout3: {
break;
}
case Type::MissionToOpenAndClose: {
break;
}
case Type::MissionForSpecificCar: {
break;
}
case Type::MissionKeepCarAndRemainClosed: {
break;
}
default: { break; }
}
}
void Garage::doOnStartOpeningEvent() {
auto player = engine->getPlayer();
auto plyChar = player->getCharacter();
auto playerPosition = plyChar->getPosition();
auto playerVehicle = plyChar->getCurrentVehicle();
bool playerIsInVehicle = playerVehicle != nullptr;
RW_UNUSED(playerPosition);
RW_UNUSED(playerIsInVehicle);
switch (type) {
case Type::Mission: {
break;
}
case Type::CollectCars1:
case Type::CollectCars2: {
player->setInputEnabled(true);
break;
}
case Type::BombShop1:
case Type::BombShop2:
case Type::BombShop3: {
player->setInputEnabled(true);
playerVehicle->setHandbraking(false);
break;
}
case Type::Respray: {
player->setInputEnabled(true);
playerVehicle->setHandbraking(false);
resprayDone = true;
break;
}
case Type::MissionForCarToComeOut: {
break;
}
case Type::Crusher: {
break;
}
case Type::MissionKeepCar: {
break;
}
case Type::Hideout1:
case Type::Hideout2:
case Type::Hideout3: {
break;
}
case Type::MissionToOpenAndClose: {
break;
}
case Type::MissionForSpecificCar: {
break;
}
case Type::MissionKeepCarAndRemainClosed: {
break;
}
default: { break; }
}
}
void Garage::doOnStartClosingEvent() {
auto player = engine->getPlayer();
auto plyChar = player->getCharacter();
auto playerPosition = plyChar->getPosition();
auto playerVehicle = plyChar->getCurrentVehicle();
bool playerIsInVehicle = playerVehicle != nullptr;
RW_UNUSED(playerPosition);
RW_UNUSED(playerIsInVehicle);
switch (type) {
case Type::Mission:
case Type::CollectCars1:
case Type::CollectCars2: {
player->setInputEnabled(false);
break;
}
case Type::BombShop1:
case Type::BombShop2:
case Type::BombShop3:
case Type::Respray: {
player->setInputEnabled(false);
playerVehicle->setHandbraking(true);
break;
}
case Type::MissionForCarToComeOut: {
break;
}
case Type::Crusher: {
break;
}
case Type::MissionKeepCar: {
break;
}
case Type::MissionToOpenAndClose: {
break;
}
case Type::MissionForSpecificCar: {
break;
}
case Type::MissionKeepCarAndRemainClosed: {
break;
}
default: { break; }
}
}
void Garage::tick(float dt) {
if (!doorObject) {
return;
}
if (!active) {
return;
}
needsToUpdate = false;
switch (state) {
case State::Opened: {
if (shouldClose()) {
state = State::Closing;
doOnStartClosingEvent();
}
break;
}
case State::Closed: {
if (shouldOpen()) {
state = State::Opening;
doOnStartOpeningEvent();
}
break;
}
case State::Opening: {
if (shouldStopOpening()) {
state = State::Closing;
} else {
fraction += dt * step;
if (fraction >= 1.0f) {
state = State::Opened;
fraction = 1.f;
doOnOpenEvent();
}
needsToUpdate = true;
}
break;
}
case State::Closing: {
if (shouldStopClosing()) {
state = State::Opening;
} else {
fraction -= dt * step;
if (fraction <= 0.f) {
state = State::Closed;
fraction = 0.f;
doOnCloseEvent();
}
needsToUpdate = true;
}
break;
}
default: { break; }
}
if (needsToUpdate) {
updateDoor();
}
}
void Garage::updateDoor() {
if (swingType) {
doorObject->setRotation(glm::angleAxis(fraction * glm::radians(90.f),
glm::vec3(0.f, 1.f, 0.f)));
if (secondDoorObject) {
secondDoorObject->setRotation(glm::angleAxis(
fraction * glm::radians(90.f), glm::vec3(0.f, 1.f, 0.f)));
}
}
doorObject->setPosition(startPosition +
glm::vec3(0.f, 0.f, fraction * doorHeight));
if (secondDoorObject) {
secondDoorObject->setPosition(
startPositionSecondDoor +
glm::vec3(0.f, 0.f, fraction * doorHeight));
}
}

View File

@ -0,0 +1,112 @@
#ifndef _RWENGINE_Garage_HPP_
#define _RWENGINE_Garage_HPP_
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <rw/defines.hpp>
class PlayerController;
class GameWorld;
class CharacterObject;
class InstanceObject;
class GameObject;
class Garage {
private:
InstanceObject* doorObject = nullptr;
InstanceObject* secondDoorObject = nullptr;
bool active = true;
float garageTimer = 0.f;
bool swingType = false;
glm::vec3 startPosition;
glm::vec3 startPositionSecondDoor;
bool needsToUpdate = false;
float fraction = 0.f;
float step = 3.f; // this should be adjusted somehow
// to look similar to original game
float doorHeight = 4.f;
float getDistanceToGarage(glm::vec3 point);
bool shouldOpen();
bool shouldClose();
bool shouldStopOpening();
bool shouldStopClosing();
void doOnOpenEvent();
void doOnCloseEvent();
void doOnStartOpeningEvent();
void doOnStartClosingEvent();
void updateDoor();
public:
// Garage types are from original game
enum class Type {
Mission = 1,
BombShop1 = 2,
BombShop2 = 3,
BombShop3 = 4,
Respray = 5,
CollectCars1 = 8,
CollectCars2 = 9,
MissionForCarToComeOut = 11,
Crusher = 13,
MissionKeepCar = 14,
Hideout1 = 16,
Hideout2 = 17,
Hideout3 = 18,
MissionToOpenAndClose = 19,
MissionForSpecificCar = 20,
MissionKeepCarAndRemainClosed = 21,
};
enum class State { Closed, Closing, Opening, Opened };
GameWorld* engine;
int id;
int getScriptObjectID() const {
return id;
}
glm::vec3 min;
glm::vec3 max;
Type type;
GameObject* target = nullptr;
// @todo use model type
int targetModel = 0;
State state = State::Closed;
bool resprayDone = false;
Garage(GameWorld* engine_, const int id_, const glm::vec3 coord0,
const glm::vec3 coord1, const Type type_);
~Garage() = default;
void makeDoorSwing();
bool isObjectInsideGarage(GameObject* object) const;
bool isTargetInsideGarage() const;
void activate();
void deactivate();
void open();
void close();
void tick(float dt);
};
#endif

View File

@ -1,702 +0,0 @@
#include "GarageController.hpp"
#include <btBulletDynamicsCommon.h>
#include <glm/gtx/quaternion.hpp>
#include "dynamics/CollisionInstance.hpp"
#include "ai/PlayerController.hpp"
#include "engine/GameState.hpp"
#include "objects/CharacterObject.hpp"
#include "objects/GameObject.hpp"
#include "objects/InstanceObject.hpp"
#include "objects/VehicleObject.hpp"
GarageController::GarageController(GameWorld* engine, GarageInfo* info,
InstanceObject* door)
: swingType(false)
, needsToUpdate(false)
, fraction(0.f)
, step(0.5f)
, doorHeight(4.0f)
, engine(engine)
, garageInfo(info)
, doorObject(door) {
if (doorObject) {
startPosition = doorObject->getPosition();
startRotation = doorObject->getRotation();
}
if (garageInfo) {
if (garageInfo->state == GarageState::Closed) {
fraction = 0.f;
} else {
fraction = 1.f;
}
}
// @todo set door height according to model size
updateDoor();
}
GarageController::~GarageController() {
}
float GarageController::getDistanceToGarage(glm::vec3 point) {
float dx = std::max(
{garageInfo->min.x - point.x, 0.f, point.x - garageInfo->max.x});
float dy = std::max(
{garageInfo->min.y - point.y, 0.f, point.y - garageInfo->max.y});
// Seems like original game ignores z axis, bug or feature?
// float dz = std::max(
// {garageInfo->min.z - point.z, 0.f, point.z - garageInfo->max.z});
// return std::sqrt(dx * dx + dy * dy + dz * dz);
return std::sqrt(dx * dx + dy * dy);
}
bool GarageController::isObjectInsideGarage(GameObject* object) {
// This is not that trivial, we need to check if full vehicle body is inside
auto p = object->getPosition();
if (p.x >= (garageInfo->min.x) && p.y >= (garageInfo->min.y) &&
p.z >= (garageInfo->min.z) && p.x <= (garageInfo->max.x) &&
p.y <= (garageInfo->max.y) && p.z <= (garageInfo->max.z)) {
return true;
}
return false;
}
bool GarageController::shouldClose() {
auto controller = engine->players.at(0);
auto plyChar = controller->getCharacter();
auto playerPosition = plyChar->getPosition();
auto playerVehicle = plyChar->getCurrentVehicle();
bool playerIsInVehicle = playerVehicle != nullptr;
switch (garageInfo->type) {
case GarageType::Mission: {
if (!isObjectInsideGarage(static_cast<GameObject*>(plyChar)) &&
isObjectInsideGarage(garageInfo->target)) {
return true;
}
return false;
}
case GarageType::BombShop1:
case GarageType::BombShop2:
case GarageType::BombShop3:
case GarageType::Respray: {
if (playerIsInVehicle) {
if (isObjectInsideGarage(
static_cast<GameObject*>(playerVehicle)) &&
playerVehicle->isStopped()) {
return true;
}
}
return false;
}
case GarageType::CollectCars1:
case GarageType::CollectCars2: {
if (playerIsInVehicle) {
if (isObjectInsideGarage(
static_cast<GameObject*>(playerVehicle))) {
if (playerVehicle->getLifetime() !=
GameObject::MissionLifetime) {
return true;
} else {
// @todo show message "come back when youre not busy"
}
}
}
return false;
}
case GarageType::MissionForCarToComeOut: {
// @todo unimplemented
return false;
}
case GarageType::Crusher: {
// @todo unimplemented
return false;
}
case GarageType::MissionKeepCar: {
// @todo unimplemented
return false;
}
case GarageType::Hideout1:
case GarageType::Hideout2:
case GarageType::Hideout3: {
// Not sure about these values
if ((!playerIsInVehicle &&
getDistanceToGarage(playerPosition) >= 5.f) ||
(playerIsInVehicle &&
getDistanceToGarage(playerPosition) >= 10.f)) {
return true;
}
return false;
}
case GarageType::MissionToOpenAndClose: {
// @todo unimplemented
return false;
}
case GarageType::MissionForSpecificCar: {
// @todo unimplemented
return false;
}
case GarageType::MissionKeepCarAndRemainClosed: {
// @todo unimplemented
return false;
}
}
return false;
}
bool GarageController::shouldOpen() {
auto controller = engine->players.at(0);
auto plyChar = controller->getCharacter();
auto playerPosition = plyChar->getPosition();
auto playerVehicle = plyChar->getCurrentVehicle();
bool playerIsInVehicle = playerVehicle != nullptr;
switch (garageInfo->type) {
case GarageType::Mission: {
// Not sure about these values
if (playerIsInVehicle &&
getDistanceToGarage(playerPosition) < 8.f &&
playerVehicle == garageInfo->target) {
return true;
}
return false;
}
case GarageType::BombShop1:
case GarageType::BombShop2:
case GarageType::BombShop3:
case GarageType::Respray: {
if (garageTimer < engine->getGameTime()) {
return true;
}
return false;
}
case GarageType::CollectCars1:
case GarageType::CollectCars2: {
// @todo unimplemented
return false;
}
case GarageType::MissionForCarToComeOut: {
// @todo unimplemented
return false;
}
case GarageType::Crusher: {
// @todo unimplemented
return false;
}
case GarageType::MissionKeepCar: {
// @todo unimplemented
return false;
}
case GarageType::Hideout1:
case GarageType::Hideout2:
case GarageType::Hideout3: {
// Not sure about these values
if ((!playerIsInVehicle &&
getDistanceToGarage(playerPosition) < 5.f) ||
(playerIsInVehicle &&
getDistanceToGarage(playerPosition) < 10.f)) {
return true;
}
return false;
}
case GarageType::MissionToOpenAndClose: {
// @todo unimplemented
return false;
}
case GarageType::MissionForSpecificCar: {
// @todo unimplemented
return false;
}
case GarageType::MissionKeepCarAndRemainClosed: {
// @todo unimplemented
return false;
}
}
return false;
}
bool GarageController::shouldStopClosing() {
auto controller = engine->players.at(0);
auto plyChar = controller->getCharacter();
auto playerPosition = plyChar->getPosition();
auto playerVehicle = plyChar->getCurrentVehicle();
bool playerIsInVehicle = playerVehicle != nullptr;
switch (garageInfo->type) {
case GarageType::Mission:
case GarageType::BombShop1:
case GarageType::BombShop2:
case GarageType::BombShop3:
case GarageType::Respray:
case GarageType::CollectCars1:
case GarageType::CollectCars2: {
return false;
}
case GarageType::MissionForCarToComeOut: {
// @todo unimplemented
return false;
}
case GarageType::Crusher: {
// @todo unimplemented
return false;
}
case GarageType::MissionKeepCar: {
// @todo unimplemented
return false;
}
case GarageType::Hideout1:
case GarageType::Hideout2:
case GarageType::Hideout3: {
// Not sure about these values
if ((!playerIsInVehicle &&
getDistanceToGarage(playerPosition) < 5.f) ||
(playerIsInVehicle &&
getDistanceToGarage(playerPosition) < 10.f)) {
return true;
}
return false;
}
case GarageType::MissionToOpenAndClose: {
// @todo unimplemented
return false;
}
case GarageType::MissionForSpecificCar: {
// @todo unimplemented
return false;
}
case GarageType::MissionKeepCarAndRemainClosed: {
// @todo unimplemented
return false;
}
}
return false;
}
bool GarageController::shouldStopOpening() {
auto controller = engine->players.at(0);
auto plyChar = controller->getCharacter();
auto playerPosition = plyChar->getPosition();
auto playerVehicle = plyChar->getCurrentVehicle();
bool playerIsInVehicle = playerVehicle != nullptr;
switch (garageInfo->type) {
case GarageType::Mission:
case GarageType::BombShop1:
case GarageType::BombShop2:
case GarageType::BombShop3:
case GarageType::Respray:
case GarageType::CollectCars1:
case GarageType::CollectCars2: {
return false;
}
case GarageType::MissionForCarToComeOut: {
// @todo unimplemented
return false;
}
case GarageType::Crusher: {
// @todo unimplemented
return false;
}
case GarageType::MissionKeepCar: {
// @todo unimplemented
return false;
}
case GarageType::Hideout1:
case GarageType::Hideout2:
case GarageType::Hideout3: {
// Not sure about these values
if ((!playerIsInVehicle &&
getDistanceToGarage(playerPosition) >= 5.f) ||
(playerIsInVehicle &&
getDistanceToGarage(playerPosition) >= 10.f)) {
return true;
}
return false;
}
case GarageType::MissionToOpenAndClose: {
// @todo unimplemented
return false;
}
case GarageType::MissionForSpecificCar: {
// @todo unimplemented
return false;
}
case GarageType::MissionKeepCarAndRemainClosed: {
// @todo unimplemented
return false;
}
}
return false;
}
void GarageController::doOnOpenEvent() {
auto controller = engine->players.at(0);
auto plyChar = controller->getCharacter();
auto playerPosition = plyChar->getPosition();
auto playerVehicle = plyChar->getCurrentVehicle();
bool playerIsInVehicle = playerVehicle != nullptr;
switch (garageInfo->type) {
case GarageType::Mission: {
break;
}
case GarageType::BombShop1: {
break;
}
case GarageType::BombShop2: {
break;
}
case GarageType::BombShop3: {
break;
}
case GarageType::Respray: {
break;
}
case GarageType::CollectCars1:
case GarageType::CollectCars2: {
break;
}
case GarageType::MissionForCarToComeOut: {
break;
}
case GarageType::Crusher: {
break;
}
case GarageType::MissionKeepCar: {
break;
}
case GarageType::Hideout1:
case GarageType::Hideout2:
case GarageType::Hideout3: {
break;
}
case GarageType::MissionToOpenAndClose: {
break;
}
case GarageType::MissionForSpecificCar: {
break;
}
case GarageType::MissionKeepCarAndRemainClosed: {
break;
}
}
}
void GarageController::doOnCloseEvent() {
auto controller = engine->players.at(0);
auto plyChar = controller->getCharacter();
auto playerPosition = plyChar->getPosition();
auto playerVehicle = plyChar->getCurrentVehicle();
bool playerIsInVehicle = playerVehicle != nullptr;
switch (garageInfo->type) {
case GarageType::Mission: {
break;
}
case GarageType::BombShop1:
case GarageType::BombShop2:
case GarageType::BombShop3: {
// Find out real value
garageTimer = engine->getGameTime() + 1.5f;
break;
}
case GarageType::Respray: {
// Find out real value
garageTimer = engine->getGameTime() + 2.f;
playerVehicle->setHealth(1000.f);
break;
}
case GarageType::CollectCars1:
case GarageType::CollectCars2: {
break;
}
case GarageType::MissionForCarToComeOut: {
break;
}
case GarageType::Crusher: {
break;
}
case GarageType::MissionKeepCar: {
break;
}
case GarageType::Hideout1:
case GarageType::Hideout2:
case GarageType::Hideout3: {
break;
}
case GarageType::MissionToOpenAndClose: {
break;
}
case GarageType::MissionForSpecificCar: {
break;
}
case GarageType::MissionKeepCarAndRemainClosed: {
break;
}
}
}
void GarageController::doOnStartOpeningEvent() {
auto controller = engine->players.at(0);
auto plyChar = controller->getCharacter();
auto playerPosition = plyChar->getPosition();
auto playerVehicle = plyChar->getCurrentVehicle();
bool playerIsInVehicle = playerVehicle != nullptr;
switch (garageInfo->type) {
case GarageType::Mission:
case GarageType::CollectCars1:
case GarageType::CollectCars2: {
controller->setInputEnabled(true);
break;
}
case GarageType::BombShop1:
case GarageType::BombShop2:
case GarageType::BombShop3:
case GarageType::Respray: {
controller->setInputEnabled(true);
playerVehicle->setHandbraking(false);
break;
}
case GarageType::MissionForCarToComeOut: {
break;
}
case GarageType::Crusher: {
break;
}
case GarageType::MissionKeepCar: {
break;
}
case GarageType::Hideout1:
case GarageType::Hideout2:
case GarageType::Hideout3: {
break;
}
case GarageType::MissionToOpenAndClose: {
break;
}
case GarageType::MissionForSpecificCar: {
break;
}
case GarageType::MissionKeepCarAndRemainClosed: {
break;
}
}
}
void GarageController::doOnStartClosingEvent() {
auto controller = engine->players.at(0);
auto plyChar = controller->getCharacter();
auto playerPosition = plyChar->getPosition();
auto playerVehicle = plyChar->getCurrentVehicle();
bool playerIsInVehicle = playerVehicle != nullptr;
switch (garageInfo->type) {
case GarageType::Mission:
case GarageType::CollectCars1:
case GarageType::CollectCars2: {
controller->setInputEnabled(false);
break;
}
case GarageType::BombShop1:
case GarageType::BombShop2:
case GarageType::BombShop3:
case GarageType::Respray: {
controller->setInputEnabled(false);
playerVehicle->setHandbraking(true);
break;
}
case GarageType::MissionForCarToComeOut: {
break;
}
case GarageType::Crusher: {
break;
}
case GarageType::MissionKeepCar: {
break;
}
case GarageType::MissionToOpenAndClose: {
break;
}
case GarageType::MissionForSpecificCar: {
break;
}
case GarageType::MissionKeepCarAndRemainClosed: {
break;
}
}
}
void GarageController::tick(float dt) {
if (!garageInfo) return;
if (!doorObject) return;
needsToUpdate = false;
switch (garageInfo->state) {
case GarageState::Opened: {
if (shouldClose()) {
garageInfo->state = GarageState::Closing;
doOnStartClosingEvent();
}
break;
}
case GarageState::Closed: {
if (shouldOpen()) {
garageInfo->state = GarageState::Opening;
doOnStartOpeningEvent();
}
break;
}
case GarageState::Opening: {
if (shouldStopOpening()) {
garageInfo->state = GarageState::Closing;
} else {
fraction += dt * step;
if (fraction >= 1.0f) {
garageInfo->state = GarageState::Opened;
fraction = 1.f;
doOnOpenEvent();
}
needsToUpdate = true;
}
break;
}
case GarageState::Closing: {
if (shouldStopClosing()) {
garageInfo->state = GarageState::Opening;
} else {
fraction -= dt * step;
if (fraction <= 0.f) {
garageInfo->state = GarageState::Closed;
fraction = 0.f;
doOnCloseEvent();
}
needsToUpdate = true;
}
break;
}
}
if (needsToUpdate) {
updateDoor();
}
}
void GarageController::updateDoor() {
if (swingType) {
// @todo incomplete
doorObject->setRotation(
glm::angleAxis(fraction * 1.57079632679f, glm::vec3(0, 1, 0)));
} else {
doorObject->setPosition(startPosition +
glm::vec3(0.f, 0.f, fraction * doorHeight));
}
}

View File

@ -1,59 +0,0 @@
#ifndef _RWENGINE_GARAGECONTROLLER_HPP_
#define _RWENGINE_GARAGECONTROLLER_HPP_
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <rw/defines.hpp>
class PlayerController;
struct GarageInfo;
class GameWorld;
class CharacterObject;
class InstanceObject;
class GameObject;
class GarageController {
private:
float garageTimer = 0.f;
bool swingType;
glm::vec3 startPosition;
glm::quat startRotation;
bool needsToUpdate;
float fraction;
float step;
float doorHeight;
float getDistanceToGarage(glm::vec3 point);
bool isObjectInsideGarage(GameObject* object);
bool shouldOpen();
bool shouldClose();
bool shouldStopOpening();
bool shouldStopClosing();
void doOnOpenEvent();
void doOnCloseEvent();
void doOnStartOpeningEvent();
void doOnStartClosingEvent();
void updateDoor();
public:
GameWorld* engine;
GarageInfo* garageInfo;
InstanceObject* doorObject;
GarageController(GameWorld* engine, GarageInfo* info, InstanceObject* door);
~GarageController();
void tick(float dt);
};
#endif

View File

@ -1264,9 +1264,9 @@ bool SaveGame::loadGame(GameState& state, const std::string& file) {
// http://gtaforums.com/topic/758692-gta-iii-save-file-documentation/
for (size_t g = 0; g < garageData.garageCount; ++g) {
auto& garage = garages[g];
state.garages.emplace_back(
(int)g, glm::vec3(garage.x1, garage.y1, garage.z1),
glm::vec3(garage.x2, garage.y2, garage.z2), static_cast<GarageType>(garage.type));
state.world->createGarage(glm::vec3(garage.x1, garage.y1, garage.z1),
glm::vec3(garage.x2, garage.y2, garage.z2),
static_cast<Garage::Type>(garage.type));
}
for (auto &c : garageData.cars) {
if (c.modelId == 0) continue;

View File

@ -140,7 +140,7 @@ void do_unpacked_call(Tret (*const& func)(Targs...),
const ScriptArguments& args) {
script_bind::binder<Tret, Targs...>::call(func, args);
}
}
} // namespace script_bind
/**
* Interface for a collection of functions that can be exported to a game script
@ -154,8 +154,7 @@ void do_unpacked_call(Tret (*const& func)(Targs...),
class ScriptModule {
public:
template <class String>
ScriptModule(String&& _name)
: name(std::forward<String>(_name)) {
ScriptModule(String&& _name) : name(std::forward<String>(_name)) {
}
const std::string& getName() const {

View File

@ -10,8 +10,8 @@
#include "objects/InstanceObject.hpp"
#include "objects/PickupObject.hpp"
#include "objects/VehicleObject.hpp"
#include "script/ScriptMachine.hpp"
#include "script/SCMFile.hpp"
#include "script/ScriptMachine.hpp"
GameState* ScriptArguments::getState() const {
return getVM()->getState();
@ -47,6 +47,12 @@ GameObject* ScriptArguments::getPlayerCharacter(unsigned int player) const {
return controller->getCharacter();
}
// @todo figure out original cast
template <>
ScriptGarageType ScriptArguments::getParameter<ScriptGarageType>(unsigned int arg) const {
return static_cast<Garage::Type>(getParameters().at(arg).integerValue());
}
template <>
GameObject* ScriptArguments::getObject<PlayerController>(
unsigned int arg) const {
@ -208,14 +214,14 @@ ScriptObjectType<VehicleGenerator> ScriptArguments::getScriptObject(
return {param.handleValue(), generator};
}
template <>
ScriptObjectType<GarageInfo> ScriptArguments::getScriptObject(
ScriptObjectType<Garage> ScriptArguments::getScriptObject(
unsigned int arg) const {
auto& param = (*this)[arg];
RW_CHECK(param.isLvalue(), "Non lvalue passed as object");
auto& garages = getWorld()->state->garages;
GarageInfo* garage = nullptr;
auto& garages = getWorld()->garages;
Garage* garage = nullptr;
if (size_t(*param.handleValue()) < garages.size()) {
garage = &garages[*param.handleValue()];
garage = garages[*param.handleValue()].get();
}
return {param.handleValue(), garage};
}

View File

@ -11,6 +11,8 @@
#include <rw/defines.hpp>
#include "engine/Garage.hpp"
class CharacterObject;
class CutsceneObject;
class GameObject;
@ -24,6 +26,7 @@ struct SCMThread;
class GameState;
class GameWorld;
class Payphone;
class Garage;
typedef uint16_t SCMOpcode;
typedef char SCMByte;
@ -98,14 +101,13 @@ using ScriptPlayer = ScriptObjectType<PlayerController>;
using ScriptVehicle = ScriptObjectType<VehicleObject>;
using ScriptCharacter = ScriptObjectType<CharacterObject>;
using ScriptPickup = ScriptObjectType<PickupObject>;
using ScriptGarage = ScriptObjectType<Garage>;
struct VehicleGenerator;
struct BlipData;
struct GarageInfo;
using ScriptVehicleGenerator = ScriptObjectType<VehicleGenerator>;
using ScriptBlip = ScriptObjectType<BlipData>;
using ScriptGarage = ScriptObjectType<GarageInfo>;
using ScriptPayphone = ScriptObjectType<Payphone>;
/// @todo replace these with real types for sounds etc.
@ -159,7 +161,7 @@ using ScriptParticle = int;
using ScriptTempact = int;
using ScriptSoundType = int;
using ScriptPickupType = int;
using ScriptGarageType = int;
using ScriptGarageType = Garage::Type;
///////////////////////////////////////////////////////////
// Script Bytecode Types
@ -345,7 +347,7 @@ template <>
ScriptObjectType<VehicleGenerator> ScriptArguments::getScriptObject(
unsigned int arg) const;
template <>
ScriptObjectType<GarageInfo> ScriptArguments::getScriptObject(
ScriptObjectType<Garage> ScriptArguments::getScriptObject(
unsigned int arg) const;
typedef std::function<void(const ScriptArguments&)> ScriptFunction;

View File

@ -6103,8 +6103,8 @@ void opcode_0218(const ScriptArguments& args, const ScriptString gxtEntry, const
@arg arg7
@arg garage
*/
void opcode_0219(const ScriptArguments& args, ScriptVec3 coord0,
ScriptVec3 coord1, const ScriptInt type,
void opcode_0219(const ScriptArguments& args, const ScriptVec3 coord0,
const ScriptVec3 coord1, const ScriptGarageType type,
ScriptGarage& garage) {
garage = args.getWorld()->createGarage(coord0, coord1, type);
}
@ -6117,10 +6117,8 @@ void opcode_0219(const ScriptArguments& args, ScriptVec3 coord0,
@arg vehicle Car/vehicle
*/
void opcode_021b(const ScriptArguments& args, const ScriptGarage garage, const ScriptVehicle vehicle) {
RW_UNIMPLEMENTED_OPCODE(0x021b);
RW_UNUSED(garage);
RW_UNUSED(vehicle);
RW_UNUSED(args);
garage->target = vehicle.get();
}
/**
@ -6130,16 +6128,8 @@ void opcode_021b(const ScriptArguments& args, const ScriptGarage garage, const S
@arg garage
*/
bool opcode_021c(const ScriptArguments& args, const ScriptGarage garage) {
auto& objects = args.getWorld()->vehiclePool.objects;
for(auto& v : objects) {
// @todo if this car only accepts mission cars we probably have to filter here / only check for one specific car
auto vp = v.second->getPosition();
if (vp.x >= garage->min.x && vp.y >= garage->min.y && vp.z >= garage->min.z &&
vp.x <= garage->max.x && vp.y <= garage->max.y && vp.z <= garage->max.z) {
return true;
}
}
return false;
RW_UNUSED(args);
return garage->isTargetInsideGarage();
}
/**
@ -6843,9 +6833,8 @@ void opcode_0298(const ScriptArguments& args, const ScriptModelID model0, Script
@arg garage
*/
void opcode_0299(const ScriptArguments& args, const ScriptGarage garage) {
RW_UNIMPLEMENTED_OPCODE(0x0299);
RW_UNUSED(garage);
RW_UNUSED(args);
garage->activate();
}
/**
@ -7342,9 +7331,8 @@ bool opcode_02b8(const ScriptArguments& args, const ScriptPlayer player, ScriptV
@arg garage
*/
void opcode_02b9(const ScriptArguments& args, const ScriptGarage garage) {
RW_UNIMPLEMENTED_OPCODE(0x02b9);
RW_UNUSED(garage);
RW_UNUSED(args);
garage->deactivate();
}
/**
@ -8163,11 +8151,9 @@ void opcode_02f9(const ScriptArguments& args, const ScriptVehicle vehicle, Scrip
@arg garage0
@arg garage1
*/
void opcode_02fa(const ScriptArguments& args, const ScriptGarage garage0, const ScriptGarageType garage1) {
RW_UNIMPLEMENTED_OPCODE(0x02fa);
RW_UNUSED(garage0);
RW_UNUSED(garage1);
void opcode_02fa(const ScriptArguments& args, const ScriptGarage garage, const ScriptGarageType garageType) {
RW_UNUSED(args);
garage->type = garageType;
}
/**
@ -8894,18 +8880,8 @@ void opcode_0327(const ScriptArguments& args, ScriptVec2 coord0, ScriptVec2 coor
@arg garage Handle
*/
bool opcode_0329(const ScriptArguments& args, const ScriptGarage garage) {
if (garage->type != GarageType::Respray) {
return false;
}
auto playerobj = args.getWorld()->pedestrianPool.find(args.getState()->playerObject);
auto pp = playerobj->getPosition();
if (pp.x >= garage->min.x && pp.y >= garage->min.y && pp.z >= garage->min.z &&
pp.x <= garage->max.x && pp.y <= garage->max.y && pp.z <= garage->max.z) {
return true;
}
return false;
RW_UNUSED(args);
return garage->resprayDone;
}
/**
@ -9584,9 +9560,8 @@ void opcode_035f(const ScriptArguments& args, const ScriptCharacter character, c
@arg garage
*/
void opcode_0360(const ScriptArguments& args, const ScriptGarage garage) {
RW_UNIMPLEMENTED_OPCODE(0x0360);
RW_UNUSED(garage);
RW_UNUSED(args);
garage->open();
}
/**
@ -9596,9 +9571,8 @@ void opcode_0360(const ScriptArguments& args, const ScriptGarage garage) {
@arg garage
*/
void opcode_0361(const ScriptArguments& args, const ScriptGarage garage) {
RW_UNIMPLEMENTED_OPCODE(0x0361);
RW_UNUSED(garage);
RW_UNUSED(args);
garage->close();
}
/**
@ -10698,12 +10672,11 @@ void opcode_03a4(const ScriptArguments& args, const ScriptString name) {
@arg garage1
@arg model
*/
void opcode_03a5(const ScriptArguments& args, const ScriptGarage garage0, const ScriptGarageType garage1, const ScriptModelID model) {
RW_UNIMPLEMENTED_OPCODE(0x03a5);
RW_UNUSED(garage0);
RW_UNUSED(garage1);
RW_UNUSED(model);
void opcode_03a5(const ScriptArguments& args, const ScriptGarage garage, const ScriptGarageType garageType, const ScriptModelID model) {
RW_UNUSED(args);
garage->type = garageType;
garage->targetModel = model;
garage->state = Garage::State::Closed;
}
/**
@ -10826,10 +10799,8 @@ bool opcode_03b0(const ScriptArguments& args, const ScriptGarage garage) {
@arg garage
*/
bool opcode_03b1(const ScriptArguments& args, const ScriptGarage garage) {
RW_UNIMPLEMENTED_OPCODE(0x03b1);
RW_UNUSED(garage);
RW_UNUSED(args);
return false;
return garage->state == Garage::State::Closed;
}
/**
@ -10964,12 +10935,11 @@ void opcode_03ba(const ScriptArguments& args, ScriptVec3 coord0, ScriptVec3 coor
@brief set_garage %1d% door_type_to_swing_open
opcode 03bb
@arg garage
@arg garage
*/
void opcode_03bb(const ScriptArguments& args, const ScriptGarage garage) {
RW_UNIMPLEMENTED_OPCODE(0x03bb);
RW_UNUSED(garage);
RW_UNUSED(args);
garage->makeDoorSwing();
}
/**
@ -11302,21 +11272,18 @@ void opcode_03d3(const ScriptArguments& args, ScriptVec3 coord, ScriptFloat& xCo
@arg garage Handle
@arg arg2
*/
bool opcode_03d4(const ScriptArguments& args, const ScriptGarage garage, const ScriptInt arg2) {
int entryIndex = arg2;
bool opcode_03d4(const ScriptArguments& args, const ScriptGarage garage, const ScriptInt index) {
int entryIndex = index;
RW_CHECK(entryIndex >= 0, "Entry index too low");
RW_CHECK(entryIndex < 32, "Entry index too high");
// @todo reimplement
if (garage->type == GarageType::CollectCars1) {
return args.getState()->importExportPortland[entryIndex];
if (garage->type == Garage::Type::CollectCars1) {
return args.getState()->importExportPortland[entryIndex];
}
if (garage->type == GarageType::CollectCars2) {
return args.getState()->importExportShoreside[entryIndex];
if (garage->type == Garage::Type::CollectCars2) {
return args.getState()->importExportShoreside[entryIndex];
}
// if (garage->type == GarageType::CollectCars3) {
// return args.getState()->importExportUnused[entryIndex];
// }
return false;
}
@ -12179,18 +12146,7 @@ void opcode_0421(const ScriptArguments& args, const ScriptBoolean arg1) {
*/
bool opcode_0422(const ScriptArguments& args, const ScriptGarage garage, const ScriptVehicle vehicle) {
RW_UNUSED(args);
/// @todo move to garage code
if (vehicle) {
/// @todo if this car only accepts mission cars we probably have to filter here / only check for one specific car
auto vp = vehicle->getPosition();
if(vp.x >= garage->min.x && vp.y >= garage->min.y && vp.z >= garage->min.z &&
vp.x <= garage->max.x && vp.y <= garage->max.y && vp.z <= garage->max.z) {
return true;
}
}
return false;
return garage->isObjectInsideGarage(vehicle.get());
}
/**

View File

@ -537,8 +537,8 @@ void RWGame::tick(float dt) {
object->tick(dt);
}
for (auto& gc : world->garageControllers) {
gc->tick(dt);
for (auto& g : world->garages) {
g->tick(dt);
}
for (auto& p : world->payphones) {
@ -717,11 +717,11 @@ void RWGame::renderDebugPaths(float time) {
}
// Draw Garage bounds
for (const auto& garage : state.garages) {
for (const auto& garage : world->garages) {
btVector3 minColor(1.f, 0.f, 0.f);
btVector3 maxColor(0.f, 1.f, 0.f);
btVector3 min(garage.min.x, garage.min.y, garage.min.z);
btVector3 max(garage.max.x, garage.max.y, garage.max.z);
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);

View File

@ -16,6 +16,7 @@ set(TESTS
FileIndex
GameData
GameWorld
Garage
Input
Items
Lifetime

18
tests/test_Garage.cpp Normal file
View File

@ -0,0 +1,18 @@
#include <boost/test/unit_test.hpp>
#include <engine/Garage.hpp>
#include "test_Globals.hpp"
#if RW_TEST_WITH_DATA
BOOST_AUTO_TEST_SUITE(GarageTests)
BOOST_AUTO_TEST_CASE(test_garage_interaction) {
{
auto garage = Global::get().e->createGarage(
{0.f, 0.f, 0.f}, {3.f, 3.f, 3.f}, Garage::Type::Respray);
BOOST_REQUIRE(garage != nullptr);
}
}
BOOST_AUTO_TEST_SUITE_END()
#endif