Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions rwengine/src/engine/GameState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ GameStats::GameStats()

GameState::GameState()
: basic{}
, rampage(this)
, gameTime(0.f)
, currentProgress(0)
, maxProgress(1)
Expand Down Expand Up @@ -216,4 +217,80 @@ bool GameState::isFading() const {

void GameState::setFadeColour(glm::i32vec3 colour) {
fadeColour = colour;
}

Rampage::Status Rampage::getStatus() const {
return status;
}

void Rampage::init(const std::string& gxtEntry, const int32_t weaponID,
const int32_t time, const int32_t kills,
const std::array<int32_t, 4>& modelsToKill,
const bool headshot) {
if (gxtEntry != "PAGE_00") {
special = true;
}
status = Ongoing;
weapon = weaponID;
endTime = state->world->getGameTime() + static_cast<float>(time) / 1000.f;
killsRequired = kills;
modelIDsToKill = modelsToKill;
headshotOnly = headshot;
}

void Rampage::tick(float dt) {
RW_UNUSED(dt);
if (getStatus() != Ongoing) {
return;
}

if (endTime <= state->world->getGameTime()) {
status = Failed;
}

if (killsRequired <= 0) {
if (!special) {
state->gameStats.rampagesPassed++;
}

status = Passed;
}

}

float Rampage::getRemainingTime() const {
return endTime - state->world->getGameTime();
}

uint32_t Rampage::getKillsForThisModel(ModelID model) {
const auto& search = modelIDsKilled.find(model);
if (search != modelIDsKilled.end()) {
return search->second;
}
return 0;
}

void Rampage::clearKills() {
modelIDsKilled.clear();
}

void Rampage::onCharacterDie(CharacterObject* victim, GameObject* killer) {
if (getStatus() != Ongoing) {
return;
}

if (killer) {
if (!static_cast<CharacterObject*>(killer)->isPlayer()) {
return;
}
}

const auto modelID = victim->getModelInfo<BaseModelInfo>()->id();
const auto isRightModel = std::find(std::begin(modelIDsToKill),
std::end(modelIDsToKill), modelID);

if (isRightModel != std::end(modelIDsToKill)) {
modelIDsKilled[modelID]++;
killsRequired--;
}
}
43 changes: 43 additions & 0 deletions rwengine/src/engine/GameState.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@

#include <engine/GameWorld.hpp>
#include <engine/ScreenText.hpp>
#include <objects/CharacterObject.hpp>
#include <objects/ObjectTypes.hpp>

class GameWorld;
class GameObject;
class ScriptMachine;
struct CutsceneData;

class CharacterObject;

struct SystemTime {
uint16_t year;
uint16_t month;
Expand Down Expand Up @@ -290,6 +293,44 @@ struct ScriptContactData {
uint32_t baseBrief;
};

class Rampage {
int32_t weapon = 0;
float endTime = 0.f;
uint32_t killsRequired = 0;
std::array<int32_t, 4> modelIDsToKill{};
bool special = false;
bool headshotOnly = false;
std::unordered_map<int32_t, int32_t> modelIDsKilled{};

public:
enum Status {
None = 0,
Ongoing = 1,
Passed = 2,
Failed = 3,
} status = None;

GameState* state;

Status getStatus() const;

void init(const std::string& gxtEntry, const int32_t weaponID,
const int32_t time, const int32_t kills,
const std::array<int32_t, 4>& modelsToKill, const bool headshot);

float getRemainingTime() const;

uint32_t getKillsForThisModel(ModelID model);
void clearKills();

void tick(float dt);

void onCharacterDie(CharacterObject* character, GameObject* killer);

Rampage(GameState* s) : state(s) {
}
};

/**
* Game and Runtime state
*
Expand All @@ -315,6 +356,8 @@ class GameState {
*/
GameStats gameStats;

Rampage rampage;

/**
* Second since game was started
*/
Expand Down
16 changes: 7 additions & 9 deletions rwengine/src/objects/PickupObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,14 +173,12 @@ PickupObject::~PickupObject() {
}

void PickupObject::tick(float dt) {
if (isRampage()) {
if (engine->state->scriptOnMissionFlag != nullptr) {
if (*(engine->state->scriptOnMissionFlag) != 0 && isEnabled()) {
setEnabled(false);
} else if (*(engine->state->scriptOnMissionFlag) == 0 &&
!isEnabled()) {
setEnabled(true);
}
if (isRampage() && engine->state->scriptOnMissionFlag != nullptr) {
if (*(engine->state->scriptOnMissionFlag) != 0 && isEnabled()) {
setEnabled(false);
} else if (*(engine->state->scriptOnMissionFlag) == 0 && !isEnabled() &&
!isCollected()) {
setEnabled(true);
}
}

Expand All @@ -204,7 +202,7 @@ void PickupObject::tick(float dt) {
m_corona->particle.colour =
glm::vec4(red / 255.f, green / 255.f, blue / 255.f, 1.f) * colourValue;

if (m_enabled) {
if (!isCollected()) {
// Sort out interactions with things that may or may not be players.
btManifoldArray manifoldArray;
btBroadphasePairArray& pairArray =
Expand Down
86 changes: 40 additions & 46 deletions rwengine/src/script/modules/GTA3ModuleImpl.inl
Original file line number Diff line number Diff line change
Expand Up @@ -5602,31 +5602,28 @@ void opcode_01f7(const ScriptArguments& args, const ScriptPlayer player, const S
}

/**
@brief init_rampage %1g% weapon %2d% time %3d% %4d% targets %5o% %6o% %7o% %8o% flag %9d%
@brief init_rampage %1g% weapon %2d% time %3d% %4d% targets %5o% %6o% %7o%
%8o% flag %9d%

opcode 01f9
@arg gxtEntry GXT entry
@arg weaponID Weapon ID
@arg time Time (ms)
@arg arg4
@arg arg4
@arg model0 Model ID
@arg model1 Model ID
@arg model2 Model ID
@arg model3 Model ID
@arg arg9 Boolean true/false
*/
void opcode_01f9(const ScriptArguments& args, const ScriptString gxtEntry, const ScriptWeaponType weaponID, const ScriptInt time, const ScriptInt arg4, const ScriptModelID model0, const ScriptModelID model1, const ScriptModelID model2, const ScriptModelID model3, const ScriptBoolean arg9) {
RW_UNIMPLEMENTED_OPCODE(0x01f9);
RW_UNUSED(gxtEntry);
RW_UNUSED(weaponID);
RW_UNUSED(time);
RW_UNUSED(arg4);
RW_UNUSED(model0);
RW_UNUSED(model1);
RW_UNUSED(model2);
RW_UNUSED(model3);
RW_UNUSED(arg9);
RW_UNUSED(args);
void opcode_01f9(const ScriptArguments& args, const ScriptString gxtEntry,
const ScriptWeaponType weaponID, const ScriptInt time,
const ScriptInt kills, const ScriptModelID model0,
const ScriptModelID model1, const ScriptModelID model2,
const ScriptModelID model3, const ScriptBoolean unused) {
RW_UNUSED(unused);
args.getState()->rampage.init(gxtEntry, weaponID, time, kills,
{model0, model1, model2, model3}, false);
}

/**
Expand All @@ -5635,10 +5632,8 @@ void opcode_01f9(const ScriptArguments& args, const ScriptString gxtEntry, const
opcode 01fa
@arg arg1
*/
void opcode_01fa(const ScriptArguments& args, ScriptInt& arg1) {
RW_UNIMPLEMENTED_OPCODE(0x01fa);
RW_UNUSED(arg1);
RW_UNUSED(args);
void opcode_01fa(const ScriptArguments& args, ScriptInt& status) {
status = args.getState()->rampage.getStatus();
}

/**
Expand Down Expand Up @@ -6001,12 +5996,15 @@ void opcode_0211(const ScriptArguments& args, const ScriptCharacter character, S

opcode 0213
@arg model Model ID
@arg pickup0
@arg pickup0
@arg coord Coordinates
@arg pickup1 Pickup
*/
void opcode_0213(const ScriptArguments& args, const ScriptModel model, const ScriptPickupType pickup0, ScriptVec3 coord, ScriptPickup& pickup1) {
pickup1 = args.getWorld()->createPickup(coord, script::getModel(args, model), pickup0);
void opcode_0213(const ScriptArguments& args, const ScriptModel model,
const ScriptPickupType pickupType, ScriptVec3 coord,
ScriptPickup& pickup) {
pickup = args.getWorld()->createPickup(coord, script::getModel(args, model),
pickupType);
}

/**
Expand All @@ -6021,8 +6019,10 @@ bool opcode_0214(const ScriptArguments& args, const ScriptPickup pickup) {
if (!pickup) {
return false;
}
bool collected = pickup->isCollected();
pickup->setCollected(false);
const bool collected = pickup->isCollected();
if (collected) {
pickup->setCollected(false);
}
return collected;
}

Expand Down Expand Up @@ -6814,8 +6814,7 @@ void opcode_0296(const ScriptArguments& args, const ScriptInt arg1) {
opcode 0297
*/
void opcode_0297(const ScriptArguments& args) {
RW_UNIMPLEMENTED_OPCODE(0x0297);
RW_UNUSED(args);
args.getState()->rampage.clearKills();
}

/**
Expand All @@ -6825,11 +6824,9 @@ void opcode_0297(const ScriptArguments& args) {
@arg model0 Player
@arg model1 Model ID
*/
void opcode_0298(const ScriptArguments& args, const ScriptModelID model0, ScriptInt& model1) {
RW_UNIMPLEMENTED_OPCODE(0x0298);
RW_UNUSED(model0);
RW_UNUSED(model1);
RW_UNUSED(args);
void opcode_0298(const ScriptArguments& args, const ScriptModelID model,
ScriptInt& kills) {
kills = args.getState()->rampage.getKillsForThisModel(model);
}

/**
Expand Down Expand Up @@ -9670,31 +9667,28 @@ bool opcode_0366(const ScriptArguments& args, const ScriptObject object) {
}

/**
@brief init_headshot_rampage %1g% weapon %2d% time %3d% %4d% targets %5o% %6o% %7o% %8o% flag %9d%
@brief init_headshot_rampage %1g% weapon %2d% time %3d% %4d% targets %5o%
%6o% %7o% %8o% flag %9d%

opcode 0367
@arg gxtEntry GXT entry
@arg arg2
@arg arg3
@arg arg4
@arg arg2
@arg arg3
@arg arg4
@arg model0 Model ID
@arg model1 Model ID
@arg model2 Model ID
@arg model3 Model ID
@arg arg9 Boolean true/false
*/
void opcode_0367(const ScriptArguments& args, const ScriptString gxtEntry, const ScriptWeaponType arg2, const ScriptInt arg3, const ScriptInt arg4, const ScriptModelID model0, const ScriptModelID model1, const ScriptModelID model2, const ScriptModelID model3, const ScriptBoolean arg9) {
RW_UNIMPLEMENTED_OPCODE(0x0367);
RW_UNUSED(gxtEntry);
RW_UNUSED(arg2);
RW_UNUSED(arg3);
RW_UNUSED(arg4);
RW_UNUSED(model0);
RW_UNUSED(model1);
RW_UNUSED(model2);
RW_UNUSED(model3);
RW_UNUSED(arg9);
RW_UNUSED(args);
void opcode_0367(const ScriptArguments& args, const ScriptString gxtEntry,
const ScriptWeaponType weaponID, const ScriptInt time,
const ScriptInt kills, const ScriptModelID model0,
const ScriptModelID model1, const ScriptModelID model2,
const ScriptModelID model3, const ScriptBoolean unused) {
RW_UNUSED(unused);
args.getState()->rampage.init(gxtEntry, weaponID, time, kills,
{model0, model1, model2, model3}, true);
}

/**
Expand Down
Loading