mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 15:29:55 +00:00
Implement Murder crimes and OnMurder instruction (Fixes #1315)
This commit is contained in:
parent
3801dfb4ba
commit
2477456f99
12 changed files with 128 additions and 9 deletions
|
@ -628,6 +628,8 @@ namespace MWClass
|
|||
!MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker))
|
||||
MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
|
||||
|
||||
bool wasDead = getCreatureStats(ptr).isDead();
|
||||
|
||||
getCreatureStats(ptr).setAttacked(true);
|
||||
|
||||
if(!successful)
|
||||
|
@ -751,6 +753,17 @@ namespace MWClass
|
|||
fatigue.setCurrent(fatigue.getCurrent() - damage, true);
|
||||
getCreatureStats(ptr).setFatigue(fatigue);
|
||||
}
|
||||
|
||||
if (!wasDead && getCreatureStats(ptr).isDead())
|
||||
{
|
||||
// NPC was killed
|
||||
// Simple check for who attacked first: if the player attacked first, a crimeId should be set
|
||||
// Doesn't handle possible edge case where no one reported the assault, but in such a case,
|
||||
// for bystanders it is not possible to tell who attacked first, anyway.
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
if (ptr.getClass().getNpcStats(ptr).getCrimeId() != -1 && ptr != player)
|
||||
MWBase::Environment::get().getMechanicsManager()->commitCrime(player, ptr, MWBase::MechanicsManager::OT_Murder);
|
||||
}
|
||||
}
|
||||
|
||||
void Npc::block(const MWWorld::Ptr &ptr) const
|
||||
|
|
|
@ -40,6 +40,10 @@ namespace MWMechanics
|
|||
void readState (const ESM::ActiveSpells& state);
|
||||
void writeState (ESM::ActiveSpells& state) const;
|
||||
|
||||
TIterator begin() const;
|
||||
|
||||
TIterator end() const;
|
||||
|
||||
private:
|
||||
|
||||
mutable TContainer mSpells;
|
||||
|
@ -57,10 +61,6 @@ namespace MWMechanics
|
|||
|
||||
const TContainer& getActiveSpells() const;
|
||||
|
||||
TIterator begin() const;
|
||||
|
||||
TIterator end() const;
|
||||
|
||||
public:
|
||||
|
||||
ActiveSpells();
|
||||
|
|
|
@ -342,6 +342,8 @@ namespace MWMechanics
|
|||
CreatureStats &creatureStats = ptr.getClass().getCreatureStats(ptr);
|
||||
const MagicEffects &effects = creatureStats.getMagicEffects();
|
||||
|
||||
bool wasDead = creatureStats.isDead();
|
||||
|
||||
// attributes
|
||||
for(int i = 0;i < ESM::Attribute::Length;++i)
|
||||
{
|
||||
|
@ -454,6 +456,44 @@ namespace MWMechanics
|
|||
}
|
||||
creatureStats.setHealth(health);
|
||||
|
||||
if (!wasDead && creatureStats.isDead())
|
||||
{
|
||||
// The actor was killed by a magic effect. Figure out if the player was responsible for it.
|
||||
const ActiveSpells& spells = creatureStats.getActiveSpells();
|
||||
for (ActiveSpells::TIterator it = spells.begin(); it != spells.end(); ++it)
|
||||
{
|
||||
const ActiveSpells::ActiveSpellParams& spell = it->second;
|
||||
for (std::vector<ActiveSpells::ActiveEffect>::const_iterator effectIt = spell.mEffects.begin();
|
||||
effectIt != spell.mEffects.end(); ++effectIt)
|
||||
{
|
||||
int effectId = effectIt->mEffectId;
|
||||
bool isDamageEffect = false;
|
||||
for (unsigned int i=0; i<sizeof(damageEffects)/sizeof(int); ++i)
|
||||
{
|
||||
if (damageEffects[i] == effectId)
|
||||
isDamageEffect = true;
|
||||
}
|
||||
|
||||
|
||||
if (effectId == ESM::MagicEffect::DamageHealth || effectId == ESM::MagicEffect::AbsorbHealth)
|
||||
isDamageEffect = true;
|
||||
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(spell.mCasterActorId);
|
||||
if (isDamageEffect && caster == player)
|
||||
{
|
||||
// Simple check for who attacked first: if the player attacked first, a crimeId should be set
|
||||
// Doesn't handle possible edge case where no one reported the assault, but in such a case,
|
||||
// for bystanders it is not possible to tell who attacked first, anyway.
|
||||
if (ptr.getClass().isNpc() && ptr.getClass().getNpcStats(ptr).getCrimeId() != -1
|
||||
&& ptr != player)
|
||||
MWBase::Environment::get().getMechanicsManager()->commitCrime(player, ptr, MWBase::MechanicsManager::OT_Murder);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: dirty flag for magic effects to avoid some unnecessary work below?
|
||||
|
||||
// Update bound effects
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace MWMechanics
|
|||
int CreatureStats::sActorId = 0;
|
||||
|
||||
CreatureStats::CreatureStats()
|
||||
: mLevel (0), mDead (false), mDied (false), mFriendlyHits (0),
|
||||
: mLevel (0), mDead (false), mDied (false), mMurdered(false), mFriendlyHits (0),
|
||||
mTalkedTo (false), mAlarmed (false),
|
||||
mAttacked (false), mHostile (false),
|
||||
mAttackingOrSpell(false),
|
||||
|
@ -245,6 +245,21 @@ namespace MWMechanics
|
|||
mDied = false;
|
||||
}
|
||||
|
||||
bool CreatureStats::hasBeenMurdered() const
|
||||
{
|
||||
return mMurdered;
|
||||
}
|
||||
|
||||
void CreatureStats::notifyMurder()
|
||||
{
|
||||
mMurdered = true;
|
||||
}
|
||||
|
||||
void CreatureStats::clearHasBeenMurdered()
|
||||
{
|
||||
mMurdered = false;
|
||||
}
|
||||
|
||||
void CreatureStats::resurrect()
|
||||
{
|
||||
if (mDead)
|
||||
|
@ -479,6 +494,7 @@ namespace MWMechanics
|
|||
|
||||
state.mDead = mDead;
|
||||
state.mDied = mDied;
|
||||
state.mMurdered = mMurdered;
|
||||
state.mFriendlyHits = mFriendlyHits;
|
||||
state.mTalkedTo = mTalkedTo;
|
||||
state.mAlarmed = mAlarmed;
|
||||
|
@ -527,6 +543,7 @@ namespace MWMechanics
|
|||
|
||||
mDead = state.mDead;
|
||||
mDied = state.mDied;
|
||||
mMurdered = state.mMurdered;
|
||||
mFriendlyHits = state.mFriendlyHits;
|
||||
mTalkedTo = state.mTalkedTo;
|
||||
mAlarmed = state.mAlarmed;
|
||||
|
|
|
@ -35,6 +35,7 @@ namespace MWMechanics
|
|||
AiSequence mAiSequence;
|
||||
bool mDead;
|
||||
bool mDied;
|
||||
bool mMurdered;
|
||||
int mFriendlyHits;
|
||||
bool mTalkedTo;
|
||||
bool mAlarmed;
|
||||
|
@ -170,6 +171,12 @@ namespace MWMechanics
|
|||
|
||||
void clearHasDied();
|
||||
|
||||
bool hasBeenMurdered() const;
|
||||
|
||||
void clearHasBeenMurdered();
|
||||
|
||||
void notifyMurder();
|
||||
|
||||
void resurrect();
|
||||
|
||||
bool hasCommonDisease() const;
|
||||
|
|
|
@ -868,10 +868,16 @@ namespace MWMechanics
|
|||
// Find actors who directly witnessed the crime
|
||||
for (std::vector<MWWorld::Ptr>::iterator it = neighbors.begin(); it != neighbors.end(); ++it)
|
||||
{
|
||||
if (*it == player) continue; // not the player
|
||||
if (*it == player)
|
||||
continue; // skip player
|
||||
if (it->getClass().getCreatureStats(*it).isDead())
|
||||
continue;
|
||||
|
||||
// Was the crime seen?
|
||||
if (MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) )
|
||||
if ((MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) )
|
||||
// Murder crime can be reported even if no one saw it (hearing is enough, I guess).
|
||||
// TODO: Add mod support for stealth executions!
|
||||
|| (type == OT_Murder && *it != victim))
|
||||
{
|
||||
if (*it == victim)
|
||||
victimAware = true;
|
||||
|
@ -904,6 +910,9 @@ namespace MWMechanics
|
|||
{
|
||||
const MWWorld::Store<ESM::GameSetting>& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
|
||||
if (type == OT_Murder && !victim.isEmpty())
|
||||
victim.getClass().getCreatureStats(victim).notifyMurder();
|
||||
|
||||
// Bounty for each type of crime
|
||||
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
|
||||
arg = store.find("iCrimeTresspass")->getInt();
|
||||
|
@ -971,7 +980,7 @@ namespace MWMechanics
|
|||
for (std::vector<MWWorld::Ptr>::iterator it = neighbors.begin(); it != neighbors.end(); ++it)
|
||||
{
|
||||
if ( *it == player
|
||||
|| !it->getClass().isNpc()) continue;
|
||||
|| !it->getClass().isNpc() || it->getClass().getCreatureStats(*it).isDead()) continue;
|
||||
|
||||
int aggression = fight;
|
||||
|
||||
|
|
|
@ -398,5 +398,7 @@ op 0x2000245: ClearInfoActor
|
|||
op 0x2000246: ClearInfoActor, explicit
|
||||
op 0x2000247: BetaComment
|
||||
op 0x2000248: BetaComment, explicit
|
||||
op 0x2000249: OnMurder
|
||||
op 0x200024a: OnMurder, explicit
|
||||
|
||||
opcodes 0x2000249-0x3ffffff unused
|
||||
opcodes 0x200024b-0x3ffffff unused
|
||||
|
|
|
@ -1072,6 +1072,25 @@ namespace MWScript
|
|||
}
|
||||
};
|
||||
|
||||
template <class R>
|
||||
class OpOnMurder : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
Interpreter::Type_Integer value =
|
||||
ptr.getClass().getCreatureStats (ptr).hasBeenMurdered();
|
||||
|
||||
if (value)
|
||||
ptr.getClass().getCreatureStats (ptr).clearHasBeenMurdered();
|
||||
|
||||
runtime.push (value);
|
||||
}
|
||||
};
|
||||
|
||||
template <class R>
|
||||
class OpOnKnockout : public Interpreter::Opcode0
|
||||
{
|
||||
|
@ -1264,6 +1283,8 @@ namespace MWScript
|
|||
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnDeath, new OpOnDeath<ImplicitRef>);
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnDeathExplicit, new OpOnDeath<ExplicitRef>);
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnMurder, new OpOnMurder<ImplicitRef>);
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnMurderExplicit, new OpOnMurder<ExplicitRef>);
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockout, new OpOnKnockout<ImplicitRef>);
|
||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockoutExplicit, new OpOnKnockout<ExplicitRef>);
|
||||
|
||||
|
|
|
@ -449,6 +449,7 @@ namespace Compiler
|
|||
extensions.registerInstruction ("lowerrank", "", opcodeLowerRank, opcodeLowerRankExplicit);
|
||||
|
||||
extensions.registerFunction ("ondeath", 'l', "", opcodeOnDeath, opcodeOnDeathExplicit);
|
||||
extensions.registerFunction ("onmurder", 'l', "", opcodeOnMurder, opcodeOnMurderExplicit);
|
||||
extensions.registerFunction ("onknockout", 'l', "", opcodeOnKnockout, opcodeOnKnockoutExplicit);
|
||||
|
||||
extensions.registerFunction ("iswerewolf", 'l', "", opcodeIsWerewolf, opcodeIsWerewolfExplicit);
|
||||
|
|
|
@ -385,6 +385,8 @@ namespace Compiler
|
|||
const int opcodeLowerRankExplicit = 0x20001eb;
|
||||
const int opcodeOnDeath = 0x20001fc;
|
||||
const int opcodeOnDeathExplicit = 0x2000205;
|
||||
const int opcodeOnMurder = 0x2000249;
|
||||
const int opcodeOnMurderExplicit = 0x200024a;
|
||||
const int opcodeOnKnockout = 0x2000240;
|
||||
const int opcodeOnKnockoutExplicit = 0x2000241;
|
||||
|
||||
|
|
|
@ -21,6 +21,9 @@ void ESM::CreatureStats::load (ESMReader &esm)
|
|||
mDied = false;
|
||||
esm.getHNOT (mDied, "DIED");
|
||||
|
||||
mMurdered = false;
|
||||
esm.getHNOT (mMurdered, "MURD");
|
||||
|
||||
mFriendlyHits = 0;
|
||||
esm.getHNOT (mFriendlyHits, "FRHT");
|
||||
|
||||
|
@ -127,6 +130,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
|
|||
if (mDied)
|
||||
esm.writeHNT ("DIED", mDied);
|
||||
|
||||
if (mMurdered)
|
||||
esm.writeHNT ("MURD", mMurdered);
|
||||
|
||||
if (mFriendlyHits)
|
||||
esm.writeHNT ("FRHT", mFriendlyHits);
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace ESM
|
|||
|
||||
bool mDead;
|
||||
bool mDied;
|
||||
bool mMurdered;
|
||||
int mFriendlyHits;
|
||||
bool mTalkedTo;
|
||||
bool mAlarmed;
|
||||
|
|
Loading…
Reference in a new issue