1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-09-15 15:02:34 +02:00

Merge pull request #446 from husho/respawn

[Ready] Respawn functionality
This commit is contained in:
Daniel Evans 2018-05-18 02:35:10 +01:00 committed by GitHub
commit b77ca47fd4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 397 additions and 67 deletions

View File

@ -4,6 +4,7 @@
#include <glm/gtc/quaternion.hpp>
#include "engine/GameState.hpp"
#include "engine/GameWorld.hpp"
#include "objects/CharacterObject.hpp"
#include "objects/GameObject.hpp"
@ -12,7 +13,9 @@
PlayerController::PlayerController()
: CharacterController()
, lastRotation(glm::vec3(0.f, 0.f, 0.f))
, _enabled(true) {
, missionRestartRequired(false)
, _enabled(true)
, restartState(Alive) {
}
void PlayerController::setInputEnabled(bool enabled) {
@ -58,12 +61,210 @@ void PlayerController::enterNearestVehicle() {
}
if (nearest) {
setNextActivity(std::make_unique<Activities::EnterVehicle>(nearest, 0));
setNextActivity(
std::make_unique<Activities::EnterVehicle>(nearest, 0));
}
}
}
void PlayerController::requestMissionRestart() {
missionRestartRequired = true;
}
bool PlayerController::isMissionRestartRequired() const {
return missionRestartRequired;
}
bool PlayerController::isWasted() const {
return character->isDead();
}
bool PlayerController::isBusted() const {
return false;
}
void PlayerController::restart() {
GameWorld* world = character->engine;
GameState* state = character->engine->state;
glm::vec3 restartPosition;
float restartHeading = 0.f;
if (state->overrideNextRestart) {
restartPosition = glm::vec3(state->nextRestartLocation);
restartHeading = state->nextRestartLocation.w;
} else {
GameState::RestartType type;
if (isBusted()) {
type = GameState::RestartType::Police;
}
if (isWasted()) {
type = GameState::RestartType::Hospital;
}
glm::vec4 closestRestart =
state->getClosestRestart(type, character->getPosition());
restartPosition = glm::vec3(closestRestart);
restartHeading = closestRestart.w;
}
if (isWasted()) {
if (!state->playerInfo.singlePayerHealthcare) {
if (state->playerInfo.money >= 1000) {
state->playerInfo.money -= 1000;
} else {
state->playerInfo.money = 0;
}
} else {
state->playerInfo.singlePayerHealthcare = false;
}
state->gameStats.timesHospital++;
}
if (isBusted()) {
if (!state->playerInfo.thaneOfLibertyCity) {
// @todo implement wanted system
uint8_t wantedLevel = 0;
uint32_t penalty = 0;
switch (wantedLevel) {
case 0:
penalty = 100;
break;
case 1:
penalty = 100;
break;
case 2:
penalty = 200;
break;
case 3:
penalty = 400;
break;
case 4:
penalty = 600;
break;
case 5:
penalty = 900;
break;
case 6:
penalty = 1500;
break;
}
if (state->playerInfo.money >= penalty) {
state->playerInfo.money -= penalty;
} else {
state->playerInfo.money = 0;
}
} else {
state->playerInfo.thaneOfLibertyCity = false;
}
state->gameStats.timesBusted++;
}
// Clean up mission restart
missionRestartRequired = false;
// Clean up overrides
state->hospitalIslandOverride = 0;
state->policeIslandOverride = 0;
// Set position and heading for any restart
character->setPosition(restartPosition);
character->setHeading(restartHeading);
if (isWasted() || isBusted()) {
// Advance 12 hours
world->offsetGameTime(60 * 12);
character->getCurrentState().health = 100.f;
// Reset dying state
character->getCurrentState().isDying = false;
character->getCurrentState().isDead = false;
// Remove weapons
character->clearInventory();
}
}
void PlayerController::restartLogic() {
GameWorld* world = character->engine;
GameState* state = character->engine->state;
switch (restartState) {
case Alive: {
if (isWasted() || isBusted() || isMissionRestartRequired()) {
state->fade(2000 / 1000.f, false);
restartState = FadingIn;
}
if (isWasted()) {
state->setFadeColour(glm::i32vec3(0xc8, 0xc8, 0xc8));
// Show "wasted" text
const auto& gxtEntry = "DEAD";
const auto& text = world->data->texts.text(gxtEntry);
state->text.addText<ScreenTextType::Big>(
ScreenTextEntry::makeBig(gxtEntry, text, 3, 4000));
}
if (isBusted()) {
state->setFadeColour(glm::i32vec3(0x00, 0x00, 0x00));
// Show "busted" text
const auto& gxtEntry = "BUSTED";
const auto& text = world->data->texts.text(gxtEntry);
state->text.addText<ScreenTextType::Big>(
ScreenTextEntry::makeBig(gxtEntry, text, 3, 5000));
}
if (isMissionRestartRequired()) {
state->setFadeColour(glm::i32vec3(0x00, 0x00, 0x00));
}
break;
}
case FadingIn: {
if (!state->isFading()) {
restartState = Restarting;
}
break;
}
case Restarting: {
state->fade(4000 / 1000.f, true);
if (isWasted()) {
state->setFadeColour(glm::i32vec3(0xc8, 0xc8, 0xc8));
}
if (isBusted() || isMissionRestartRequired()) {
state->setFadeColour(glm::i32vec3(0x00, 0x00, 0x00));
}
restart();
restartState = FadingOut;
break;
}
case FadingOut: {
if (!state->isFading()) {
restartState = Alive;
}
break;
}
}
}
void PlayerController::update(float dt) {
restartLogic();
CharacterController::update(dt);
}

View File

@ -1,19 +1,33 @@
#ifndef _RWENGINE_PLAYERCONTROLLER_HPP_
#define _RWENGINE_PLAYERCONTROLLER_HPP_
#include <ai/CharacterController.hpp>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <ai/CharacterController.hpp>
class PlayerController : public CharacterController {
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::quat lastRotation;
bool missionRestartRequired;
bool _enabled;
enum RestartState {
Alive,
FadingIn,
Restarting,
FadingOut,
} restartState;
// handles player respawn logic
void restartLogic();
void restart();
public:
PlayerController();
@ -33,6 +47,14 @@ public:
void enterNearestVehicle();
void requestMissionRestart();
bool isMissionRestartRequired() const;
bool isWasted() const;
// @todo not implemented yet
bool isBusted() const;
void update(float dt) override;
glm::vec3 getTargetPosition() override;

View File

@ -99,7 +99,12 @@ GameState::GameState()
, maxWantedLevel(0)
, playerObject(0)
, scriptOnMissionFlag(nullptr)
, overrideNextRestart(false)
, nextRestartLocation{}
, hospitalRestarts{}
, policeRestarts{}
, hospitalIslandOverride(false)
, policeIslandOverride(false)
, fadeOut(true)
, fadeStart(0.f)
, fadeTime(0.f)
@ -114,7 +119,7 @@ GameState::GameState()
, cameraNear(0.1f)
, cameraFixed(false)
, cameraPosition{}
, cameraRotation{1.0f,0.0f,0.0f,0.0f}
, cameraRotation{1.0f, 0.0f, 0.0f, 0.0f}
, cameraTarget(0)
, importExportPortland(0)
, importExportShoreside(0)
@ -125,7 +130,7 @@ GameState::GameState()
int GameState::addRadarBlip(BlipData& blip) {
int l = 0;
for (const auto &radarBlip : radarBlips) {
for (const auto& radarBlip : radarBlips) {
if ((radarBlip.first) != l) {
l = radarBlip.first - 1;
} else {
@ -145,3 +150,70 @@ void GameState::removeBlip(int blip) {
radarBlips.erase(it);
}
}
void GameState::addHospitalRestart(const glm::vec4 location) {
hospitalRestarts.push_back(location);
}
void GameState::addPoliceRestart(const glm::vec4 location) {
policeRestarts.push_back(location);
}
void GameState::overrideRestart(const glm::vec4 location) {
overrideNextRestart = true;
nextRestartLocation = location;
}
void GameState::cancelRestartOverride() {
overrideNextRestart = false;
}
const glm::vec4 GameState::getClosestRestart(
RestartType type, const glm::vec3 playerPosition) const {
float closest = 10000.f;
glm::vec4 result;
ZoneData* playerZone = world->data->findZoneAt(playerPosition);
const std::vector<glm::vec4>* iter = nullptr;
int islandOverride = 0;
if (type == Hospital) {
iter = &hospitalRestarts;
islandOverride = hospitalIslandOverride;
} else if (type == Police) {
iter = &policeRestarts;
islandOverride = policeIslandOverride;
}
for (auto& location : *iter) {
glm::vec3 location3d(location);
ZoneData* restartZone = world->data->findZoneAt(location3d);
if ((playerZone->island == restartZone->island &&
islandOverride != 0) ||
islandOverride == 0) {
if (glm::distance(location3d, playerPosition) < closest) {
result = location;
closest = glm::distance(location3d, playerPosition);
}
}
}
return result;
}
void GameState::fade(float time, bool f) {
fadeTime = time;
fadeOut = f;
fadeStart = world->getGameTime();
}
bool GameState::isFading() const {
return world->getGameTime() < fadeStart + fadeTime;
}
void GameState::setFadeColour(glm::i32vec3 colour) {
fadeColour = colour;
}

View File

@ -2,8 +2,8 @@
#define _RWENGINE_GAMESTATE_HPP_
#include <bitset>
#include <cstdint>
#include <string>
#include <map>
#include <string>
#include <vector>
#include <glm/glm.hpp>
@ -11,7 +11,9 @@
#include <data/GameTexts.hpp>
#include <data/VehicleGenerator.hpp>
#include <engine/GameData.hpp>
#include <engine/GameInputState.hpp>
#include <engine/GameWorld.hpp>
#include <engine/ScreenText.hpp>
#include <objects/ObjectTypes.hpp>
@ -191,7 +193,7 @@ struct BlipData {
uint16_t size = 3; // Only used if texture is empty
uint8_t brightness = 1; // Don't really know how it is used
uint8_t brightness = 1; // Don't really know how it is used
enum DisplayMode { Hide = 0, MarkerOnly = 1, RadarOnly = 2, ShowBoth = 3 };
@ -323,8 +325,20 @@ public:
/** Objects created by the current mission */
std::vector<GameObject*> missionObjects;
bool overrideNextStart;
bool overrideNextRestart;
glm::vec4 nextRestartLocation;
std::vector<glm::vec4> hospitalRestarts, policeRestarts;
int hospitalIslandOverride, policeIslandOverride;
void addHospitalRestart(const glm::vec4 location);
void addPoliceRestart(const glm::vec4 location);
void overrideRestart(const glm::vec4 location);
void cancelRestartOverride();
enum RestartType { Hospital, Police };
const glm::vec4 getClosestRestart(RestartType type,
const glm::vec3 playerPosition) const;
bool fadeOut;
float fadeStart;
@ -332,6 +346,11 @@ public:
bool fadeSound;
glm::u16vec3 fadeColour;
// @todo fadeOut should be replaced with enum?
void fade(float time, bool f);
bool isFading() const;
void setFadeColour(glm::i32vec3 colour);
std::string currentSplash;
bool skipCutscene;
@ -393,7 +412,7 @@ public:
*/
ScriptMachine* script;
std::array<ScriptContactData, 16> scriptContacts = { };
std::array<ScriptContactData, 16> scriptContacts = {};
GameState();

View File

@ -148,6 +148,11 @@ glm::vec3 CharacterObject::updateMovementAnimation(float dt) {
movementAnimation =
animations->animation(AnimCycle::KnockOutShotFront0);
repeat = false;
if (currentAnim ==
animations->animation(AnimCycle::KnockOutShotFront0) &&
animator->isCompleted(AnimIndexMovement)) {
SetDead();
}
} else if (jumped) {
repeat = false;
if (currentAnim == animations->animation(AnimCycle::JumpLaunch) &&
@ -407,8 +412,26 @@ bool CharacterObject::isPlayer() const {
return engine->state->playerObject == getGameObjectID();
}
bool CharacterObject::isDying() const {
return currentState.isDying;
}
bool CharacterObject::isDead() const {
return currentState.isDead;
}
bool CharacterObject::isAlive() const {
return currentState.health > 0.f;
return !isDying() && !isDead();
}
void CharacterObject::Die() {
currentState.isDying = true;
currentState.isDead = false;
}
void CharacterObject::SetDead() {
currentState.isDying = false;
currentState.isDead = true;
}
bool CharacterObject::enterVehicle(VehicleObject* vehicle, size_t seat) {
@ -486,6 +509,9 @@ bool CharacterObject::takeDamage(const GameObject::DamageInfo& dmg) {
if (dmgPoints > 0.f) {
currentState.health = std::max(0.f, currentState.health - dmgPoints);
}
if (currentState.health <= 0.f) {
Die();
}
return true;
}
@ -652,3 +678,9 @@ void CharacterObject::useItem(bool active, bool primary) {
}
}
}
void CharacterObject::clearInventory() {
for (int slot = 0; slot < kMaxInventorySlots; ++slot) {
removeFromInventory(slot);
}
}

View File

@ -7,8 +7,8 @@
#include <cstdint>
#include <string>
#include <glm/gtc/constants.hpp>
#include <glm/glm.hpp>
#include <glm/gtc/constants.hpp>
#include <rw/forward.hpp>
@ -37,6 +37,8 @@ struct CharacterWeaponSlot {
struct CharacterState {
float health = 100.f;
float armour = 0.f;
bool isDying = false;
bool isDead = false;
std::array<CharacterWeaponSlot, kMaxInventorySlots> weapons;
uint16_t currentWeapon = 0;
uint32_t lastFireTimeMS = 0;
@ -129,7 +131,13 @@ public:
bool isPlayer() const;
bool isDying() const;
bool isDead() const;
bool isAlive() const;
void Die();
void SetDead();
bool takeDamage(const DamageInfo& damage) override;
bool enterVehicle(VehicleObject* vehicle, size_t seat);
@ -186,7 +194,8 @@ public:
glm::vec3 getLookDirection() const {
float theta = m_look.y - glm::half_pi<float>();
return glm::vec3(std::sin(-m_look.x) * std::cos(theta),
std::cos(-m_look.x) * std::cos(theta), std::sin(theta));
std::cos(-m_look.x) * std::cos(theta),
std::sin(theta));
}
/**
@ -250,6 +259,8 @@ public:
void useItem(bool active, bool primary = true);
void cycleInventory(bool up);
void clearInventory();
};
#endif

View File

@ -3183,10 +3183,8 @@ void opcode_0114(const ScriptArguments& args, const ScriptCharacter character, c
@arg player Player
*/
bool opcode_0117(const ScriptArguments& args, const ScriptPlayer player) {
RW_UNIMPLEMENTED_OPCODE(0x0117);
RW_UNUSED(player);
RW_UNUSED(args);
return false;
return player->isWasted();
}
/**
@ -3877,7 +3875,7 @@ void opcode_0168(const ScriptArguments& args, const ScriptBlip blip, const Scrip
@arg colour Colour (0-255)
*/
void opcode_0169(const ScriptArguments& args, ScriptRGB colour) {
args.getState()->fadeColour = colour;
args.getState()->setFadeColour(colour);
}
/**
@ -3888,9 +3886,7 @@ void opcode_0169(const ScriptArguments& args, ScriptRGB colour) {
@arg scriptFade Boolean true/false
*/
void opcode_016a(const ScriptArguments& args, const ScriptInt time, const ScriptBoolean scriptFade) {
args.getState()->fadeTime = time / 1000.f;
args.getState()->fadeOut = scriptFade;
args.getState()->fadeStart = args.getWorld()->getGameTime();
args.getState()->fade(time / 1000.f, scriptFade);
}
/**
@ -3902,8 +3898,7 @@ bool opcode_016b(const ScriptArguments& args) {
if (args.getWorld()->state->skipCutscene) {
return false;
}
return args.getWorld()->getGameTime() <
args.getState()->fadeStart + args.getState()->fadeTime;
return args.getState()->isFading();
}
/**
@ -3913,11 +3908,9 @@ bool opcode_016b(const ScriptArguments& args) {
@arg coord Coordinates
@arg angle Angle
*/
void opcode_016c(const ScriptArguments& args, ScriptVec3 coord, const ScriptFloat angle) {
RW_UNIMPLEMENTED_OPCODE(0x016c);
RW_UNUSED(coord);
RW_UNUSED(angle);
RW_UNUSED(args);
void opcode_016c(const ScriptArguments& args, ScriptVec3 coord, const ScriptFloat heading) {
coord = script::getGround(args, coord);
args.getState()->addHospitalRestart(glm::vec4(coord, heading));
}
/**
@ -3927,11 +3920,9 @@ void opcode_016c(const ScriptArguments& args, ScriptVec3 coord, const ScriptFloa
@arg coord Coordinates
@arg angle Angle
*/
void opcode_016d(const ScriptArguments& args, ScriptVec3 coord, const ScriptFloat angle) {
RW_UNIMPLEMENTED_OPCODE(0x016d);
RW_UNUSED(coord);
RW_UNUSED(angle);
RW_UNUSED(args);
void opcode_016d(const ScriptArguments& args, ScriptVec3 coord, const ScriptFloat heading) {
coord = script::getGround(args, coord);
args.getState()->addPoliceRestart(glm::vec4(coord, heading));
}
/**
@ -3941,10 +3932,9 @@ void opcode_016d(const ScriptArguments& args, ScriptVec3 coord, const ScriptFloa
@arg coord Coordinates
@arg angle Angle
*/
void opcode_016e(const ScriptArguments& args, ScriptVec3 coord, const ScriptFloat angle) {
args.getState()->overrideNextStart = true;
/// @todo why is this a vec4
args.getState()->nextRestartLocation = glm::vec4(coord, angle);
void opcode_016e(const ScriptArguments& args, ScriptVec3 coord, const ScriptFloat heading) {
coord = script::getGround(args, coord);
args.getState()->overrideRestart(glm::vec4(coord, heading));
}
/**
@ -5634,7 +5624,7 @@ void opcode_01f5(const ScriptArguments& args, const ScriptPlayer player, ScriptC
opcode 01f6
*/
void opcode_01f6(const ScriptArguments& args) {
args.getState()->overrideNextStart = false;
args.getState()->cancelRestartOverride();
}
/**
@ -6827,21 +6817,12 @@ void opcode_0254(const ScriptArguments& args) {
@arg coord Coordinates
@arg angle Angle
*/
void opcode_0255(const ScriptArguments& args, ScriptVec3 coord, const ScriptFloat angle) {
auto object = args.getWorld()->pedestrianPool.find(args.getState()->playerObject);
RW_CHECK(object != nullptr ,"No player found");
auto player = static_cast<CharacterObject*>(object);
/// @todo Implment a proper force exit vehicle path
auto cv = player->getCurrentVehicle();
if ( cv != nullptr )
{
cv->setOccupant( player->getCurrentSeat(), nullptr );
player->setCurrentVehicle(nullptr, 0);
}
player->setPosition(coord + script::kSpawnOffset);
player->setHeading(angle);
void opcode_0255(const ScriptArguments& args, ScriptVec3 coord, const ScriptFloat heading) {
coord = script::getGround(args, coord);
args.getState()->overrideRestart(glm::vec4(coord, heading));
// @todo Add support for multiple players
PlayerController* player = args.getState()->world->players.at(0);
player->requestMissionRestart();
}
/**
@ -6851,10 +6832,8 @@ void opcode_0255(const ScriptArguments& args, ScriptVec3 coord, const ScriptFloa
@arg player Player
*/
bool opcode_0256(const ScriptArguments& args, const ScriptPlayer player) {
RW_UNIMPLEMENTED_OPCODE(0x0256);
RW_UNUSED(player);
RW_UNUSED(args);
return player.get() != nullptr;
return !player->isWasted() && !player->isBusted();
}
/**
@ -12202,10 +12181,9 @@ void opcode_0412(const ScriptArguments& args, const ScriptVehicle vehicle, const
@arg arg2 Boolean true/false
*/
void opcode_0413(const ScriptArguments& args, const ScriptPlayer player, const ScriptBoolean arg2) {
RW_UNIMPLEMENTED_OPCODE(0x0413);
// @todo support multiple players?
RW_UNUSED(player);
RW_UNUSED(arg2);
RW_UNUSED(args);
args.getState()->playerInfo.thaneOfLibertyCity = arg2;
}
/**
@ -12216,10 +12194,9 @@ void opcode_0413(const ScriptArguments& args, const ScriptPlayer player, const S
@arg arg2 Boolean true/false
*/
void opcode_0414(const ScriptArguments& args, const ScriptPlayer player, const ScriptBoolean arg2) {
RW_UNIMPLEMENTED_OPCODE(0x0414);
// @todo support multiple players?
RW_UNUSED(player);
RW_UNUSED(arg2);
RW_UNUSED(args);
args.getState()->playerInfo.singlePayerHealthcare = arg2;
}
/**
@ -12338,9 +12315,7 @@ void opcode_041e(const ScriptArguments& args, const ScriptRadio arg1, const Scri
@arg arg1
*/
void opcode_041f(const ScriptArguments& args, const ScriptLevel arg1) {
RW_UNIMPLEMENTED_OPCODE(0x041f);
RW_UNUSED(arg1);
RW_UNUSED(args);
args.getState()->hospitalIslandOverride = arg1;
}
/**
@ -12350,9 +12325,7 @@ void opcode_041f(const ScriptArguments& args, const ScriptLevel arg1) {
@arg arg1
*/
void opcode_0420(const ScriptArguments& args, const ScriptLevel arg1) {
RW_UNIMPLEMENTED_OPCODE(0x0420);
RW_UNUSED(arg1);
RW_UNUSED(args);
args.getState()->policeIslandOverride = arg1;
}
/**