Skip to content
Open
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
5 changes: 5 additions & 0 deletions include/reone/game/object/door.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,15 @@ class Door : public Object {
void loadFromBlueprint(const std::string &resRef);

bool isSelectable() const override;
void damage(int amount, uint32_t damager) override;

void open();
void close();

bool isLocked() const { return _locked; }
bool isStatic() const { return _static; }
bool isKeyRequired() const { return _keyRequired; }
bool isNotBlastable() const { return _notBlastable; }

void onOpen(const Object &triggerer);
void onFailToOpen(const Object &triggerer);
Expand Down Expand Up @@ -95,6 +97,7 @@ class Door : public Object {
int _hardness {0};
int _fortitude {0};
bool _lockable {false};
bool _notBlastable {false};
std::string _keyName;

// Walkmeshes
Expand All @@ -121,6 +124,8 @@ class Door : public Object {

void loadUTD(const resource::generated::UTD &utd);
void loadTransformFromGIT(const resource::generated::GIT_Door_List &git);
void runDamagedScript(uint32_t damagerId);
void runDeathScript(uint32_t damagerId);

void updateTransform() override;
};
Expand Down
6 changes: 5 additions & 1 deletion include/reone/game/reputes.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

#pragma once

#include "types.h"

namespace reone {

namespace resource {
Expand All @@ -34,6 +36,7 @@ class IReputes {
virtual ~IReputes() = default;

virtual bool getIsEnemy(const Creature &left, const Creature &right) const = 0;
virtual bool getIsEnemy(Faction left, Faction right) const = 0;
virtual bool getIsFriend(const Creature &left, const Creature &right) const = 0;
virtual bool getIsNeutral(const Creature &left, const Creature &right) const = 0;
};
Expand All @@ -47,13 +50,14 @@ class Reputes : public IReputes, boost::noncopyable {
void init();

bool getIsEnemy(const Creature &left, const Creature &right) const override;
bool getIsEnemy(Faction left, Faction right) const override;
bool getIsFriend(const Creature &left, const Creature &right) const override;
bool getIsNeutral(const Creature &left, const Creature &right) const override;

private:
resource::TwoDAs &_twoDas;

int getRepute(const Creature &left, const Creature &right) const;
int getRepute(Faction left, Faction right) const;
};

} // namespace game
Expand Down
54 changes: 54 additions & 0 deletions src/libs/game/object/door.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

#include "reone/game/object/door.h"

#include <algorithm>
#include <limits>

#include "reone/graphics/di/services.h"
#include "reone/resource/2da.h"
#include "reone/resource/di/services.h"
Expand Down Expand Up @@ -112,6 +115,34 @@ bool Door::isSelectable() const {
return !_static && !_open;
}

void Door::damage(int amount, uint32_t damager) {
if (_dead || _plot || _notBlastable) {
return;
}
if (amount <= 0) {
return;
}

int currentHitPoints = _currentHitPoints > 0 ? _currentHitPoints : _hitPoints;
if (amount == std::numeric_limits<int>::max()) {
_currentHitPoints = isMinOneHP() ? 1 : 0;
} else {
_currentHitPoints = std::max(isMinOneHP() ? 1 : 0, currentHitPoints - amount);
}

damager = damager ? damager : script::kObjectInvalid;
runDamagedScript(damager);

if (_currentHitPoints > 0) {
return;
}

_dead = true;
_locked = false;
open();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to run Door::onOpen here to call the corresponding script.

Currently Door::onOpen takes an Object &triggerer parameter, but I don't think that is necessary, since it doesn't need anything other than triggerer's ID. We can change that, and use damager as triggerer for onOpen.

runDeathScript(damager);
}

void Door::open() {
auto model = std::static_pointer_cast<ModelSceneNode>(_sceneNode);
if (model) {
Expand Down Expand Up @@ -171,6 +202,28 @@ void Door::onFailToOpen(const Object &triggerer) {
{script::ArgKind::ClickingObject, Variable::ofObject(triggerer.id())}});
}

void Door::runDamagedScript(uint32_t damagerId) {
if (_onDamaged.empty()) {
return;
}
_game.scriptRunner().run(
_onDamaged,
{{script::ArgKind::Caller, Variable::ofObject(_id)},
{script::ArgKind::LastAttacker, Variable::ofObject(damagerId)},
{script::ArgKind::LastDamager, Variable::ofObject(damagerId)}});
}

void Door::runDeathScript(uint32_t damagerId) {
if (_onDeath.empty()) {
return;
}
_game.scriptRunner().run(
_onDeath,
{{script::ArgKind::Caller, Variable::ofObject(_id)},
{script::ArgKind::LastAttacker, Variable::ofObject(damagerId)},
{script::ArgKind::LastDamager, Variable::ofObject(damagerId)}});
}

void Door::setLocked(bool locked) {
_locked = locked;
}
Expand All @@ -196,6 +249,7 @@ void Door::loadUTD(const resource::generated::UTD &utd) {
_fortitude = utd.Fort;
_genericType = utd.GenericType;
_static = utd.Static;
_notBlastable = utd.NotBlastable;

_onClosed = utd.OnClosed; // always empty, but could be useful
_onDamaged = utd.OnDamaged; // always empty, but could be useful
Expand Down
16 changes: 15 additions & 1 deletion src/libs/game/object/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ namespace reone {

namespace game {

static bool canBashDoor(const Door &door, const Creature &actor, const IReputes &reputes) {
return door.isLocked() &&
door.isSelectable() &&
!door.isDead() &&
!door.plotFlag() &&
!door.isNotBlastable() &&
reputes.getIsEnemy(actor.faction(), door.faction()) &&
(door.hitPoints() > 0 || door.currentHitPoints() > 0);
}

void Module::load(std::string name, const Gff &ifo, bool fromSave) {
_name = std::move(name);

Expand Down Expand Up @@ -342,7 +352,11 @@ std::vector<ContextAction> Module::getContextActions(const std::shared_ptr<Objec
}
case ObjectType::Door: {
auto door = std::static_pointer_cast<Door>(object);
if (door->isLocked() && !door->isKeyRequired() && _game.party().getLeader()->attributes().hasSkill(SkillType::Security)) {
auto leader = _game.party().getLeader();
if (canBashDoor(*door, *leader, _services.game.reputes)) {
actions.push_back(ContextAction(ActionType::AttackObject));
}
if (door->isLocked() && !door->isKeyRequired() && leader->attributes().hasSkill(SkillType::Security)) {
actions.push_back(ContextAction(SkillType::Security));
}
break;
Expand Down
14 changes: 9 additions & 5 deletions src/libs/game/reputes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,20 +69,24 @@ bool Reputes::getIsEnemy(const Creature &left, const Creature &right) const {
return left.isMinOneHP() && right.isMinOneHP();
}

return getIsEnemy(left.faction(), right.faction());
}

bool Reputes::getIsEnemy(Faction left, Faction right) const {
return getRepute(left, right) < 50;
}

bool Reputes::getIsFriend(const Creature &left, const Creature &right) const {
return getRepute(left, right) > 50;
return getRepute(left.faction(), right.faction()) > 50;
}

bool Reputes::getIsNeutral(const Creature &left, const Creature &right) const {
return getRepute(left, right) == 50;
return getRepute(left.faction(), right.faction()) == 50;
}

int Reputes::getRepute(const Creature &left, const Creature &right) const {
int leftFaction = static_cast<int>(left.faction());
int rightFaction = static_cast<int>(right.faction());
int Reputes::getRepute(Faction left, Faction right) const {
int leftFaction = static_cast<int>(left);
int rightFaction = static_cast<int>(right);

if (leftFaction < 0 || leftFaction >= g_factionValues.size() ||
rightFaction < 0 || rightFaction >= g_factionValues[leftFaction].size())
Expand Down
Loading