1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-07 03:12:36 +01:00

[Ready] Payphones (#498)

Payphones
This commit is contained in:
husho 2018-06-18 07:24:34 +03:00 committed by darkf
parent 81781a53ed
commit 6d04746222
15 changed files with 473 additions and 105 deletions

View File

@ -63,6 +63,8 @@ set(RWENGINE_SOURCES
src/engine/GameWorld.hpp src/engine/GameWorld.hpp
src/engine/GarageController.cpp src/engine/GarageController.cpp
src/engine/GarageController.hpp src/engine/GarageController.hpp
src/engine/Payphone.cpp
src/engine/Payphone.hpp
src/engine/SaveGame.cpp src/engine/SaveGame.cpp
src/engine/SaveGame.hpp src/engine/SaveGame.hpp
src/engine/ScreenText.cpp src/engine/ScreenText.cpp

View File

@ -4,18 +4,22 @@
#include <glm/gtc/quaternion.hpp> #include <glm/gtc/quaternion.hpp>
#include "engine/Animator.hpp"
#include "engine/GameState.hpp" #include "engine/GameState.hpp"
#include "engine/GameWorld.hpp" #include "engine/GameWorld.hpp"
#include "objects/CharacterObject.hpp" #include "objects/CharacterObject.hpp"
#include "objects/GameObject.hpp" #include "objects/GameObject.hpp"
#include "objects/VehicleObject.hpp" #include "objects/VehicleObject.hpp"
class Animator;
PlayerController::PlayerController() PlayerController::PlayerController()
: CharacterController() : CharacterController()
, lastRotation(glm::vec3(0.f, 0.f, 0.f)) , lastRotation(glm::vec3(0.f, 0.f, 0.f))
, missionRestartRequired(false) , missionRestartRequired(false)
, _enabled(true) , _enabled(true)
, restartState(Alive) { , restartState(Alive)
, payphoneState(Left) {
} }
void PlayerController::setInputEnabled(bool enabled) { void PlayerController::setInputEnabled(bool enabled) {
@ -275,6 +279,42 @@ void PlayerController::restartLogic() {
} }
} }
void PlayerController::pickUpPayphone() {
payphoneState = PayphoneState::PickingUp;
character->animator->playAnimation(
AnimIndexMovement, character->animations->animation(AnimCycle::PhoneIn),
1.f, false);
}
void PlayerController::hangUpPayphone() {
payphoneState = PayphoneState::HangingUp;
character->animator->playAnimation(
AnimIndexMovement,
character->animations->animation(AnimCycle::PhoneOut), 1.f, false);
}
void PlayerController::talkOnPayphone() {
payphoneState = PayphoneState::Talking;
}
void PlayerController::leavePayphone() {
payphoneState = Left;
}
bool PlayerController::isPickingUpPayphone() const {
return payphoneState == PayphoneState::PickingUp;
}
bool PlayerController::isHangingUpPayphone() const {
return payphoneState == PayphoneState::HangingUp;
}
bool PlayerController::isTalkingOnPayphone() const {
return payphoneState == PayphoneState::Talking;
}
void PlayerController::update(float dt) { void PlayerController::update(float dt) {
restartLogic(); restartLogic();

View File

@ -6,6 +6,7 @@
#include <glm/gtc/quaternion.hpp> #include <glm/gtc/quaternion.hpp>
class PlayerController : public CharacterController { class PlayerController : public CharacterController {
private:
glm::quat cameraRotation{1.0f, 0.0f, 0.0f, 0.0f}; glm::quat cameraRotation{1.0f, 0.0f, 0.0f, 0.0f};
glm::vec3 direction{}; glm::vec3 direction{};
@ -26,6 +27,13 @@ class PlayerController : public CharacterController {
FadingIn, FadingIn,
} restartState; } restartState;
enum PayphoneState {
Left,
Talking,
PickingUp,
HangingUp,
} payphoneState;
// handles player respawn logic // handles player respawn logic
void restartLogic(); void restartLogic();
@ -58,6 +66,22 @@ public:
// @todo not implemented yet // @todo not implemented yet
bool isBusted() const; bool isBusted() const;
// Play payphone pick up anim
void pickUpPayphone();
// Play payphone hang up anim
void hangUpPayphone();
// Play talking on payphone anim
void talkOnPayphone();
// Reset any payphone anim
void leavePayphone();
// Is payphone pick up anim playing
bool isPickingUpPayphone() const;
// Is payphone hang up anim playing
bool isHangingUpPayphone() const;
// Is talking on payphone anim playing
bool isTalkingOnPayphone() const;
void update(float dt) override; void update(float dt) override;
glm::vec3 getTargetPosition() override; glm::vec3 getTargetPosition() override;

View File

@ -459,6 +459,12 @@ GarageInfo* GameWorld::createGarage(const glm::vec3 coord0,
return info; return info;
} }
Payphone* GameWorld::createPayphone(const glm::vec2 coord) {
int id = payphones.size();
payphones.emplace_back(make_unique<Payphone>(this, id, coord));
return payphones.back().get();
}
void GameWorld::ObjectPool::insert(GameObject* object) { void GameWorld::ObjectPool::insert(GameObject* object) {
if (object->getGameObjectID() == 0) { if (object->getGameObjectID() == 0) {
// Find the lowest free GameObjectID. // Find the lowest free GameObjectID.

View File

@ -18,6 +18,7 @@
#include <audio/SoundManager.hpp> #include <audio/SoundManager.hpp>
#include <engine/GarageController.hpp> #include <engine/GarageController.hpp>
#include <engine/Payphone.hpp>
#include <objects/ObjectTypes.hpp> #include <objects/ObjectTypes.hpp>
#include <render/VisualFX.hpp> #include <render/VisualFX.hpp>
@ -35,6 +36,7 @@ struct btDbvtBroadphase;
class GameState; class GameState;
class GarageController; class GarageController;
class Payphone;
class PlayerController; class PlayerController;
class Logger; class Logger;
@ -151,6 +153,11 @@ public:
GarageInfo* createGarage(const glm::vec3 coord0, const glm::vec3 coord1, GarageInfo* createGarage(const glm::vec3 coord0, const glm::vec3 coord1,
const int type); const int type);
/**
* Creates a payphone
*/
Payphone* createPayphone(const glm::vec2 coord);
/** /**
* Destroys an existing Object * Destroys an existing Object
*/ */
@ -266,6 +273,8 @@ public:
std::vector<std::unique_ptr<GarageController>> garageControllers; std::vector<std::unique_ptr<GarageController>> garageControllers;
std::vector<std::unique_ptr<Payphone>> payphones;
/** /**
* @brief getBlipTarget * @brief getBlipTarget
* @param blip * @param blip

View File

@ -0,0 +1,127 @@
#include "engine/Payphone.hpp"
#include <rw/defines.hpp>
#include "ai/PlayerController.hpp"
#include "engine/GameState.hpp"
#include "engine/GameWorld.hpp"
#include "objects/CharacterObject.hpp"
#include "objects/GameObject.hpp"
#include "objects/InstanceObject.hpp"
Payphone::Payphone(GameWorld* engine_, const int id_, const glm::vec2 coord)
: engine(engine_), id(id_) {
// Find payphone object, original game does this differently
for (const auto& p : engine->instancePool.objects) {
auto o = p.second;
if (!o->getModel()) {
continue;
}
if (o->getModelInfo<BaseModelInfo>()->name != "phonebooth1") {
continue;
}
if (glm::distance(coord, glm::vec2(o->getPosition())) < 2.f) {
object = static_cast<InstanceObject*>(o);
break;
}
}
message.clear();
if (object) {
position = object->getPosition();
}
}
void Payphone::enable() {
state = State::Ringing;
}
void Payphone::disable() {
state = State::Idle;
}
bool Payphone::isTalking() const {
return state == State::Talking;
}
void Payphone::setMessageAndStartRinging(const std::string& m) {
state = State::Ringing;
message = m;
}
void Payphone::tick(float dt) {
RW_UNUSED(dt);
switch (state) {
case State::Idle: {
break;
}
case State::Ringing: {
if (glm::distance(
position,
engine->getPlayer()->getCharacter()->getPosition()) < 1.f) {
state = State::PickingUp;
engine->getPlayer()->pickUpPayphone();
engine->state->isCinematic = true;
engine->getPlayer()->prepareForCutscene();
engine->getPlayer()->getCharacter()->setHeading(glm::degrees(
glm::atan(position.x, position.y) + glm::half_pi<float>()));
}
// @todo Do wiggle animation
break;
}
case State::PickingUp: {
if (!engine->getPlayer()->isPickingUpPayphone()) {
state = State::Talking;
if (!message.empty()) {
const auto& text =
ScreenText::format(engine->data->texts.text(message));
engine->state->text.clear<ScreenTextType::HighPriority>();
engine->state->text.addText<ScreenTextType::HighPriority>(
ScreenTextEntry::makeHighPriority(message, text, 3000));
message.clear();
}
callTimer = engine->getGameTime() + 3.f;
}
break;
}
case State::Talking: {
if (callTimer <= engine->getGameTime()) {
state = State::HangingUp;
engine->getPlayer()->hangUpPayphone();
}
break;
}
case State::HangingUp: {
if (!engine->getPlayer()->isHangingUpPayphone()) {
state = State::Idle;
engine->state->isCinematic = false;
engine->getPlayer()->freeFromCutscene();
}
break;
}
default: { break; }
}
}

View File

@ -0,0 +1,49 @@
#ifndef _RWENGINE_PAYPHONE_HPP_
#define _RWENGINE_PAYPHONE_HPP_
#include <glm/glm.hpp>
#include <string>
class PlayerController;
class GameWorld;
class GameState;
class GameObject;
class InstanceObject;
class CharacterObject;
class Payphone {
private:
InstanceObject* object;
glm::vec3 position;
GameWorld* engine;
float callTimer = 0.f;
std::string message;
public:
enum class State { Idle, Ringing, PickingUp, Talking, HangingUp };
State state = State::Idle;
int id;
int getScriptObjectID() const {
return id;
}
Payphone(GameWorld* engine_, const int id_, const glm::vec2 coord);
~Payphone() = default;
// Makes a payphone ring
void enable();
// Disables ringing
void disable();
// Is currently used by player
bool isTalking() const;
// Sets a message and makes a payphone ring
void setMessageAndStartRinging(const std::string& m);
void tick(float dt);
};
#endif

View File

@ -328,11 +328,11 @@ struct Block7Data {
}; };
struct Block8Data { struct Block8Data {
BlockDword numPhones; BlockDword numPayphones;
BlockDword numActivePhones; BlockDword numActivePayphones;
}; };
struct Block8Phone { struct Block8Payphone {
glm::vec3 position{}; glm::vec3 position{};
BlockDword messagePtr[6]; BlockDword messagePtr[6];
BlockDword messageEndTime; BlockDword messageEndTime;
@ -815,25 +815,23 @@ bool SaveGame::loadGame(GameState& state, const std::string& file) {
#endif #endif
// Block 8 // Block 8
BlockSize phoneBlockSize; BlockSize payphoneBlockSize;
BLOCK_HEADER(phoneBlockSize) BLOCK_HEADER(payphoneBlockSize)
BlockDword phoneDataSize; BlockDword payphoneDataSize;
READ_VALUE(phoneDataSize) READ_VALUE(payphoneDataSize)
Block8Data phoneData; Block8Data payphoneData;
READ_VALUE(phoneData); READ_VALUE(payphoneData);
std::vector<Block8Phone> phones(phoneData.numPhones); std::vector<Block8Payphone> payphones(payphoneData.numPayphones);
for (size_t p = 0; p < phoneData.numPhones; ++p) { for (auto& payphone : payphones) {
Block8Phone& phone = phones[p]; READ_VALUE(payphone)
READ_VALUE(phone)
} }
#if RW_DEBUG #if RW_DEBUG
std::cout << "Phones: " << phoneData.numPhones << std::endl; std::cout << "Payphones: " << payphoneData.numPayphones << std::endl;
for (size_t p = 0; p < phoneData.numPhones; ++p) { for (const auto& payphone : payphones) {
Block8Phone& phone = phones[p]; std::cout << " " << uint16_t(payphone.state) << " " << payphone.position.x
std::cout << " " << uint16_t(phone.state) << " " << phone.position.x << " " << payphone.position.y << " " << payphone.position.z
<< " " << phone.position.y << " " << phone.position.z
<< std::endl; << std::endl;
} }
#endif #endif
@ -1257,6 +1255,11 @@ bool SaveGame::loadGame(GameState& state, const std::string& file) {
} }
} }
// @todo restore properly
for (const auto& payphone : payphones) {
state.world->createPayphone(glm::vec2(payphone.position));
}
// TODO restore garage data // TODO restore garage data
// http://gtaforums.com/topic/758692-gta-iii-save-file-documentation/ // http://gtaforums.com/topic/758692-gta-iii-save-file-documentation/
for (size_t g = 0; g < garageData.garageCount; ++g) { for (size_t g = 0; g < garageData.garageCount; ++g) {

View File

@ -122,6 +122,32 @@ void CharacterObject::destroyActor() {
glm::vec3 CharacterObject::updateMovementAnimation(float dt) { glm::vec3 CharacterObject::updateMovementAnimation(float dt) {
glm::vec3 animTranslate{}; glm::vec3 animTranslate{};
if (controller) {
auto c = static_cast<PlayerController*>(controller);
if (c->isTalkingOnPayphone()) {
animator->playAnimation(
AnimIndexMovement,
animations->animation(AnimCycle::PhoneTalk), 1.f,
true);
return glm::vec3();
}
if (c->isPickingUpPayphone()) {
if (animator->isCompleted(AnimIndexMovement)) {
c->talkOnPayphone();
} else {
return glm::vec3();
}
}
if (c->isHangingUpPayphone()) {
if (animator->isCompleted(AnimIndexMovement)) {
c->leavePayphone();
} else {
return glm::vec3();
}
}
}
if (motionBlockedByActivity) { if (motionBlockedByActivity) {
// Clear any residual motion animation // Clear any residual motion animation
animator->playAnimation(AnimIndexMovement, nullptr, 1.f, false); animator->playAnimation(AnimIndexMovement, nullptr, 1.f, false);
@ -584,8 +610,8 @@ void CharacterObject::resetToAINode() {
} }
} }
void CharacterObject::playActivityAnimation(const AnimationPtr& animation, bool repeat, void CharacterObject::playActivityAnimation(const AnimationPtr& animation,
bool blocked) { bool repeat, bool blocked) {
RW_CHECK(animator != nullptr, "No Animator"); RW_CHECK(animator != nullptr, "No Animator");
animator->playAnimation(AnimIndexAction, animation, 1.f, repeat); animator->playAnimation(AnimIndexAction, animation, 1.f, repeat);
motionBlockedByActivity = blocked; motionBlockedByActivity = blocked;

View File

@ -220,6 +220,17 @@ ScriptObjectType<GarageInfo> ScriptArguments::getScriptObject(
return {param.handleValue(), garage}; return {param.handleValue(), garage};
} }
template <> template <>
ScriptObjectType<Payphone> ScriptArguments::getScriptObject(
unsigned int arg) const {
auto& param = (*this)[arg];
RW_CHECK(param.isLvalue(), "Non lvalue passed as object");
auto index = *param.handleValue();
RW_CHECK(index >= 0, "Object index is negative");
auto& payphones = getWorld()->payphones;
auto payphone = payphones.at(size_t(index)).get();
return {param.handleValue(), payphone};
}
template <>
ScriptObjectType<BlipData> ScriptArguments::getScriptObject( ScriptObjectType<BlipData> ScriptArguments::getScriptObject(
unsigned int arg) const { unsigned int arg) const {
auto& param = (*this)[arg]; auto& param = (*this)[arg];

View File

@ -23,6 +23,7 @@ class ScriptModule;
struct SCMThread; struct SCMThread;
class GameState; class GameState;
class GameWorld; class GameWorld;
class Payphone;
typedef uint16_t SCMOpcode; typedef uint16_t SCMOpcode;
typedef char SCMByte; typedef char SCMByte;
@ -105,10 +106,10 @@ struct GarageInfo;
using ScriptVehicleGenerator = ScriptObjectType<VehicleGenerator>; using ScriptVehicleGenerator = ScriptObjectType<VehicleGenerator>;
using ScriptBlip = ScriptObjectType<BlipData>; using ScriptBlip = ScriptObjectType<BlipData>;
using ScriptGarage = ScriptObjectType<GarageInfo>; using ScriptGarage = ScriptObjectType<GarageInfo>;
using ScriptPayphone = ScriptObjectType<Payphone>;
/// @todo replace these with real types for sounds etc. /// @todo replace these with real types for sounds etc.
using ScriptSound = ScriptObjectType<int>; using ScriptSound = ScriptObjectType<int>;
using ScriptPhone = ScriptObjectType<int>;
using ScriptFire = ScriptObjectType<int>; using ScriptFire = ScriptObjectType<int>;
using ScriptSphere = ScriptObjectType<int>; using ScriptSphere = ScriptObjectType<int>;
@ -338,6 +339,9 @@ template <>
ScriptObjectType<BlipData> ScriptArguments::getScriptObject( ScriptObjectType<BlipData> ScriptArguments::getScriptObject(
unsigned int arg) const; unsigned int arg) const;
template <> template <>
ScriptObjectType<Payphone> ScriptArguments::getScriptObject(
unsigned int arg) const;
template <>
ScriptObjectType<VehicleGenerator> ScriptArguments::getScriptObject( ScriptObjectType<VehicleGenerator> ScriptArguments::getScriptObject(
unsigned int arg) const; unsigned int arg) const;
template <> template <>

View File

@ -6604,70 +6604,64 @@ void opcode_0249(const ScriptArguments& args, const ScriptModel model) {
} }
/** /**
@brief %3d% = create_phone_at %1d% %2d% @brief %3d% = create_payphone_at %1d% %2d%
opcode 024a opcode 024a
@arg coord Coordinates @arg coord Coordinates
@arg phone Handle @arg payphone Handle
*/ */
void opcode_024a(const ScriptArguments& args, ScriptVec2 coord, ScriptPhone& phone) { void opcode_024a(const ScriptArguments& args, const ScriptVec2 coord, ScriptPayphone& payphone) {
RW_UNIMPLEMENTED_OPCODE(0x024a); payphone = args.getWorld()->createPayphone(coord);
RW_UNUSED(coord);
RW_UNUSED(phone);
RW_UNUSED(args);
} }
/** /**
@brief text_phone_repeatedly %1d% %2g% @brief text_payphone_repeatedly %1d% %2g%
opcode 024b opcode 024b
@arg phone @arg payphone
@arg arg2 @arg arg2
*/ */
void opcode_024b(const ScriptArguments& args, const ScriptPhone phone, const ScriptString arg2) { void opcode_024b(const ScriptArguments& args, const ScriptPayphone payphone, const ScriptString arg2) {
RW_UNIMPLEMENTED_OPCODE(0x024b); RW_UNIMPLEMENTED_OPCODE(0x024b);
RW_UNUSED(phone); RW_UNUSED(payphone);
RW_UNUSED(arg2); RW_UNUSED(arg2);
RW_UNUSED(args); RW_UNUSED(args);
} }
/** /**
@brief text_phone %1d% %2g% @brief text_payphone %1d% %2g%
opcode 024c opcode 024c
@arg phone @arg payphone
@arg arg2 @arg text
*/ */
void opcode_024c(const ScriptArguments& args, const ScriptPhone phone, const ScriptString arg2) { void opcode_024c(const ScriptArguments& args, const ScriptPayphone payphone, const ScriptString text) {
RW_UNIMPLEMENTED_OPCODE(0x024c);
RW_UNUSED(phone);
RW_UNUSED(arg2);
RW_UNUSED(args); RW_UNUSED(args);
payphone->setMessageAndStartRinging(text);
} }
/** /**
@brief phone_text_been_displayed %1d% @brief payphone_text_been_displayed %1d%
opcode 024d opcode 024d
@arg phone @arg payphone
*/ */
bool opcode_024d(const ScriptArguments& args, const ScriptPhone phone) { bool opcode_024d(const ScriptArguments& args, const ScriptPayphone payphone) {
RW_UNIMPLEMENTED_OPCODE(0x024d); RW_UNIMPLEMENTED_OPCODE(0x024d);
RW_UNUSED(phone); RW_UNUSED(payphone);
RW_UNUSED(args); RW_UNUSED(args);
return false; return false;
} }
/** /**
@brief disable_phone %1d% @brief disable_payphone %1d%
opcode 024e opcode 024e
@arg phone @arg payphone
*/ */
void opcode_024e(const ScriptArguments& args, const ScriptPhone phone) { void opcode_024e(const ScriptArguments& args, const ScriptPayphone payphone) {
RW_UNIMPLEMENTED_OPCODE(0x024e);
RW_UNUSED(phone);
RW_UNUSED(args); RW_UNUSED(args);
payphone->disable();
} }
/** /**
@ -9955,49 +9949,49 @@ void opcode_0377(const ScriptArguments& args, const ScriptCharacter character) {
} }
/** /**
@brief text_phone_1string_repeatedly %1d% %2g% %3g% @brief text_payphone_1string_repeatedly %1d% %2g% %3g%
opcode 0378 opcode 0378
@arg phone @arg payphone
@arg arg2 @arg arg2
@arg arg3 @arg arg3
*/ */
void opcode_0378(const ScriptArguments& args, const ScriptPhone phone, const ScriptString arg2, const ScriptString arg3) { void opcode_0378(const ScriptArguments& args, const ScriptPayphone payphone, const ScriptString arg2, const ScriptString arg3) {
RW_UNIMPLEMENTED_OPCODE(0x0378); RW_UNIMPLEMENTED_OPCODE(0x0378);
RW_UNUSED(phone); RW_UNUSED(payphone);
RW_UNUSED(arg2); RW_UNUSED(arg2);
RW_UNUSED(arg3); RW_UNUSED(arg3);
RW_UNUSED(args); RW_UNUSED(args);
} }
/** /**
@brief text_phone_1string %1d% %2g% %3g% @brief text_payphone_1string %1d% %2g% %3g%
opcode 0379 opcode 0379
@arg phone @arg payphone
@arg arg2 @arg arg2
@arg arg3 @arg arg3
*/ */
void opcode_0379(const ScriptArguments& args, const ScriptPhone phone, const ScriptString arg2, const ScriptString arg3) { void opcode_0379(const ScriptArguments& args, const ScriptPayphone payphone, const ScriptString arg2, const ScriptString arg3) {
RW_UNIMPLEMENTED_OPCODE(0x0379); RW_UNIMPLEMENTED_OPCODE(0x0379);
RW_UNUSED(phone); RW_UNUSED(payphone);
RW_UNUSED(arg2); RW_UNUSED(arg2);
RW_UNUSED(arg3); RW_UNUSED(arg3);
RW_UNUSED(args); RW_UNUSED(args);
} }
/** /**
@brief text_phone_2strings_repeatedly %1d% %2g% %3g% %4g% @brief text_payphone_2strings_repeatedly %1d% %2g% %3g% %4g%
opcode 037a opcode 037a
@arg phone @arg payphone
@arg arg2 @arg arg2
@arg arg3 @arg arg3
@arg arg4 @arg arg4
*/ */
void opcode_037a(const ScriptArguments& args, const ScriptPhone phone, const ScriptString arg2, const ScriptString arg3, const ScriptString arg4) { void opcode_037a(const ScriptArguments& args, const ScriptPayphone payphone, const ScriptString arg2, const ScriptString arg3, const ScriptString arg4) {
RW_UNIMPLEMENTED_OPCODE(0x037a); RW_UNIMPLEMENTED_OPCODE(0x037a);
RW_UNUSED(phone); RW_UNUSED(payphone);
RW_UNUSED(arg2); RW_UNUSED(arg2);
RW_UNUSED(arg3); RW_UNUSED(arg3);
RW_UNUSED(arg4); RW_UNUSED(arg4);
@ -10005,17 +9999,17 @@ void opcode_037a(const ScriptArguments& args, const ScriptPhone phone, const Scr
} }
/** /**
@brief text_phone_2strings %1d% %2g% %3g% %4g% @brief text_payphone_2strings %1d% %2g% %3g% %4g%
opcode 037b opcode 037b
@arg phone @arg payphone
@arg arg2 @arg arg2
@arg arg3 @arg arg3
@arg arg4 @arg arg4
*/ */
void opcode_037b(const ScriptArguments& args, const ScriptPhone phone, const ScriptString arg2, const ScriptString arg3, const ScriptString arg4) { void opcode_037b(const ScriptArguments& args, const ScriptPayphone payphone, const ScriptString arg2, const ScriptString arg3, const ScriptString arg4) {
RW_UNIMPLEMENTED_OPCODE(0x037b); RW_UNIMPLEMENTED_OPCODE(0x037b);
RW_UNUSED(phone); RW_UNUSED(payphone);
RW_UNUSED(arg2); RW_UNUSED(arg2);
RW_UNUSED(arg3); RW_UNUSED(arg3);
RW_UNUSED(arg4); RW_UNUSED(arg4);
@ -10023,18 +10017,18 @@ void opcode_037b(const ScriptArguments& args, const ScriptPhone phone, const Scr
} }
/** /**
@brief text_phone_3strings_repeatedly %1d% %2g% %3g% %4g% %5g% @brief text_payphone_3strings_repeatedly %1d% %2g% %3g% %4g% %5g%
opcode 037c opcode 037c
@arg phone @arg payphone
@arg arg2 @arg arg2
@arg arg3 @arg arg3
@arg arg4 @arg arg4
@arg arg5 @arg arg5
*/ */
void opcode_037c(const ScriptArguments& args, const ScriptPhone phone, const ScriptString arg2, const ScriptString arg3, const ScriptString arg4, const ScriptString arg5) { void opcode_037c(const ScriptArguments& args, const ScriptPayphone payphone, const ScriptString arg2, const ScriptString arg3, const ScriptString arg4, const ScriptString arg5) {
RW_UNIMPLEMENTED_OPCODE(0x037c); RW_UNIMPLEMENTED_OPCODE(0x037c);
RW_UNUSED(phone); RW_UNUSED(payphone);
RW_UNUSED(arg2); RW_UNUSED(arg2);
RW_UNUSED(arg3); RW_UNUSED(arg3);
RW_UNUSED(arg4); RW_UNUSED(arg4);
@ -10043,18 +10037,18 @@ void opcode_037c(const ScriptArguments& args, const ScriptPhone phone, const Scr
} }
/** /**
@brief text_phone_3strings %1d% %2g% %3g% %4g% %5g% @brief text_payphone_3strings %1d% %2g% %3g% %4g% %5g%
opcode 037d opcode 037d
@arg phone @arg payphone
@arg arg2 @arg arg2
@arg arg3 @arg arg3
@arg arg4 @arg arg4
@arg arg5 @arg arg5
*/ */
void opcode_037d(const ScriptArguments& args, const ScriptPhone phone, const ScriptString arg2, const ScriptString arg3, const ScriptString arg4, const ScriptString arg5) { void opcode_037d(const ScriptArguments& args, const ScriptPayphone payphone, const ScriptString arg2, const ScriptString arg3, const ScriptString arg4, const ScriptString arg5) {
RW_UNIMPLEMENTED_OPCODE(0x037d); RW_UNIMPLEMENTED_OPCODE(0x037d);
RW_UNUSED(phone); RW_UNUSED(payphone);
RW_UNUSED(arg2); RW_UNUSED(arg2);
RW_UNUSED(arg3); RW_UNUSED(arg3);
RW_UNUSED(arg4); RW_UNUSED(arg4);
@ -10165,19 +10159,19 @@ void opcode_0385(const ScriptArguments& args, const ScriptString gxtEntry0, cons
} }
/** /**
@brief text_phone_4strings_repeatedly %1d% %2g% %3g% %4g% %5g% %6g% @brief text_payphone_4strings_repeatedly %1d% %2g% %3g% %4g% %5g% %6g%
opcode 0386 opcode 0386
@arg phone @arg payphone
@arg arg2 @arg arg2
@arg arg3 @arg arg3
@arg arg4 @arg arg4
@arg arg5 @arg arg5
@arg arg6 @arg arg6
*/ */
void opcode_0386(const ScriptArguments& args, const ScriptPhone phone, const ScriptString arg2, const ScriptString arg3, const ScriptString arg4, const ScriptString arg5, const ScriptString arg6) { void opcode_0386(const ScriptArguments& args, const ScriptPayphone payphone, const ScriptString arg2, const ScriptString arg3, const ScriptString arg4, const ScriptString arg5, const ScriptString arg6) {
RW_UNIMPLEMENTED_OPCODE(0x0386); RW_UNIMPLEMENTED_OPCODE(0x0386);
RW_UNUSED(phone); RW_UNUSED(payphone);
RW_UNUSED(arg2); RW_UNUSED(arg2);
RW_UNUSED(arg3); RW_UNUSED(arg3);
RW_UNUSED(arg4); RW_UNUSED(arg4);
@ -10187,19 +10181,19 @@ void opcode_0386(const ScriptArguments& args, const ScriptPhone phone, const Scr
} }
/** /**
@brief text_phone_4strings %1d% %2g% %3g% %4g% %5g% %6g% @brief text_payphone_4strings %1d% %2g% %3g% %4g% %5g% %6g%
opcode 0387 opcode 0387
@arg phone @arg payphone
@arg arg2 @arg arg2
@arg arg3 @arg arg3
@arg arg4 @arg arg4
@arg arg5 @arg arg5
@arg arg6 @arg arg6
*/ */
void opcode_0387(const ScriptArguments& args, const ScriptPhone phone, const ScriptString arg2, const ScriptString arg3, const ScriptString arg4, const ScriptString arg5, const ScriptString arg6) { void opcode_0387(const ScriptArguments& args, const ScriptPayphone payphone, const ScriptString arg2, const ScriptString arg3, const ScriptString arg4, const ScriptString arg5, const ScriptString arg6) {
RW_UNIMPLEMENTED_OPCODE(0x0387); RW_UNIMPLEMENTED_OPCODE(0x0387);
RW_UNUSED(phone); RW_UNUSED(payphone);
RW_UNUSED(arg2); RW_UNUSED(arg2);
RW_UNUSED(arg3); RW_UNUSED(arg3);
RW_UNUSED(arg4); RW_UNUSED(arg4);
@ -10209,10 +10203,10 @@ void opcode_0387(const ScriptArguments& args, const ScriptPhone phone, const Scr
} }
/** /**
@brief text_phone_5strings_repeatedly %1d% %2g% %3g% %4g% %5g% %6g% %7g% @brief text_payphone_5strings_repeatedly %1d% %2g% %3g% %4g% %5g% %6g% %7g%
opcode 0388 opcode 0388
@arg phone @arg payphone
@arg arg2 @arg arg2
@arg arg3 @arg arg3
@arg arg4 @arg arg4
@ -10220,9 +10214,9 @@ void opcode_0387(const ScriptArguments& args, const ScriptPhone phone, const Scr
@arg arg6 @arg arg6
@arg arg7 @arg arg7
*/ */
void opcode_0388(const ScriptArguments& args, const ScriptPhone phone, const ScriptString arg2, const ScriptString arg3, const ScriptString arg4, const ScriptString arg5, const ScriptString arg6, const ScriptString arg7) { void opcode_0388(const ScriptArguments& args, const ScriptPayphone payphone, const ScriptString arg2, const ScriptString arg3, const ScriptString arg4, const ScriptString arg5, const ScriptString arg6, const ScriptString arg7) {
RW_UNIMPLEMENTED_OPCODE(0x0388); RW_UNIMPLEMENTED_OPCODE(0x0388);
RW_UNUSED(phone); RW_UNUSED(payphone);
RW_UNUSED(arg2); RW_UNUSED(arg2);
RW_UNUSED(arg3); RW_UNUSED(arg3);
RW_UNUSED(arg4); RW_UNUSED(arg4);
@ -10233,10 +10227,10 @@ void opcode_0388(const ScriptArguments& args, const ScriptPhone phone, const Scr
} }
/** /**
@brief text_phone_5strings %1d% %2g% %3g% %4g% %5g% %6g% %7g% @brief text_payphone_5strings %1d% %2g% %3g% %4g% %5g% %6g% %7g%
opcode 0389 opcode 0389
@arg phone @arg payphone
@arg arg2 @arg arg2
@arg arg3 @arg arg3
@arg arg4 @arg arg4
@ -10244,9 +10238,9 @@ void opcode_0388(const ScriptArguments& args, const ScriptPhone phone, const Scr
@arg arg6 @arg arg6
@arg arg7 @arg arg7
*/ */
void opcode_0389(const ScriptArguments& args, const ScriptPhone phone, const ScriptString arg2, const ScriptString arg3, const ScriptString arg4, const ScriptString arg5, const ScriptString arg6, const ScriptString arg7) { void opcode_0389(const ScriptArguments& args, const ScriptPayphone payphone, const ScriptString arg2, const ScriptString arg3, const ScriptString arg4, const ScriptString arg5, const ScriptString arg6, const ScriptString arg7) {
RW_UNIMPLEMENTED_OPCODE(0x0389); RW_UNIMPLEMENTED_OPCODE(0x0389);
RW_UNUSED(phone); RW_UNUSED(payphone);
RW_UNUSED(arg2); RW_UNUSED(arg2);
RW_UNUSED(arg3); RW_UNUSED(arg3);
RW_UNUSED(arg4); RW_UNUSED(arg4);
@ -11028,16 +11022,14 @@ void opcode_03c1(const ScriptArguments& args, const ScriptPlayer player, ScriptV
} }
/** /**
@brief phone %1d% answered @brief payphone %1d% answered
opcode 03c2 opcode 03c2
@arg phone @arg payphone
*/ */
bool opcode_03c2(const ScriptArguments& args, const ScriptPhone phone) { bool opcode_03c2(const ScriptArguments& args, const ScriptPayphone payphone) {
RW_UNIMPLEMENTED_OPCODE(0x03c2);
RW_UNUSED(phone);
RW_UNUSED(args); RW_UNUSED(args);
return false; return payphone->isTalking();
} }
/** /**
@ -11819,15 +11811,14 @@ void opcode_0404(const ScriptArguments& args) {
} }
/** /**
@brief enable_phone %1d% @brief enable_payphone %1d%
opcode 0405 opcode 0405
@arg phone Handle @arg payphone Handle
*/ */
void opcode_0405(const ScriptArguments& args, const ScriptPhone phone) { void opcode_0405(const ScriptArguments& args, const ScriptPayphone payphone) {
RW_UNIMPLEMENTED_OPCODE(0x0405);
RW_UNUSED(phone);
RW_UNUSED(args); RW_UNUSED(args);
payphone->enable();
} }
/** /**
@ -12586,16 +12577,15 @@ void opcode_0446(const ScriptArguments& args, const ScriptCharacter character, c
} }
/** /**
@brief is_player_lifting_a_phone %1d% @brief is_player_lifting_a_payphone %1d%
opcode 0447 opcode 0447
@arg player Player @arg player Player
*/ */
bool opcode_0447(const ScriptArguments& args, const ScriptPlayer player) { bool opcode_0447(const ScriptArguments& args, const ScriptPlayer player) {
RW_UNIMPLEMENTED_OPCODE(0x0447);
RW_UNUSED(player);
RW_UNUSED(args); RW_UNUSED(args);
return false; return player->isPickingUpPayphone() || player->isTalkingOnPayphone() ||
player->isHangingUpPayphone();
} }
/** /**

View File

@ -545,6 +545,10 @@ void RWGame::tick(float dt) {
gc->tick(dt); gc->tick(dt);
} }
for (auto& p : world->payphones) {
p->tick(dt);
}
world->destroyQueuedObjects(); world->destroyQueuedObjects();
state.text.tick(dt); state.text.tick(dt);

View File

@ -25,6 +25,7 @@ set(TESTS
Menu Menu
Object Object
ObjectData ObjectData
Payphone
Pickup Pickup
Renderer Renderer
RWBStream RWBStream

72
tests/test_Payphone.cpp Normal file
View File

@ -0,0 +1,72 @@
#include <boost/test/unit_test.hpp>
#include <ai/PlayerController.hpp>
#include <engine/GameWorld.hpp>
#include <engine/Payphone.hpp>
#include <objects/CharacterObject.hpp>
#include <objects/GameObject.hpp>
#include <objects/InstanceObject.hpp>
#include "test_Globals.hpp"
#if RW_TEST_WITH_DATA
BOOST_AUTO_TEST_SUITE(PayphoneTests)
BOOST_AUTO_TEST_CASE(test_payphone_interaction) {
{
const auto playerID = 7777;
auto character = Global::get().e->createPlayer(
{0.f, 0.f, 0.f}, {1.f, 0.f, 0.f, 0.f}, playerID);
BOOST_REQUIRE(character != nullptr);
Global::get().e->state->playerObject = playerID;
// phonebooth1 from ipl file
const auto modelID = 1335;
auto payphoneObj = Global::get().e->createInstance(
modelID, {1.f, 0.f, 0.f}, {1.f, 0.f, 0.f, 0.f});
BOOST_REQUIRE(payphoneObj != nullptr);
auto payphone = Global::get().e->createPayphone({1.f, 0.f});
BOOST_REQUIRE(payphone != nullptr);
payphone->setMessageAndStartRinging("");
auto dt = 0.016f;
Global::get().e->state->gameTime += dt;
character->tick(dt);
payphone->tick(dt);
BOOST_CHECK(!Global::get().e->getPlayer()->isPickingUpPayphone());
character->setPosition(character->getPosition() + glm::vec3{0.001f, 0.f, 0.f});
dt = 0.016f;
Global::get().e->state->gameTime += dt;
// character->tick(dt);
payphone->tick(dt);
BOOST_CHECK(Global::get().e->getPlayer()->isPickingUpPayphone());
BOOST_CHECK(!payphone->isTalking());
dt = 10.f;
Global::get().e->state->gameTime += dt;
character->tick(dt);
payphone->tick(dt);
BOOST_CHECK(Global::get().e->getPlayer()->isTalkingOnPayphone());
BOOST_CHECK(payphone->isTalking());
dt = 3.f;
Global::get().e->state->gameTime += dt;
character->tick(dt);
payphone->tick(dt);
BOOST_CHECK(Global::get().e->getPlayer()->isHangingUpPayphone());
BOOST_CHECK(!payphone->isTalking());
Global::get().e->destroyObject(payphoneObj);
Global::get().e->destroyObject(character);
}
}
BOOST_AUTO_TEST_SUITE_END()
#endif