mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 17:59:56 +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()->isAggressive(ptr, attacker))
|
||||||
MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
|
MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
|
||||||
|
|
||||||
|
bool wasDead = getCreatureStats(ptr).isDead();
|
||||||
|
|
||||||
getCreatureStats(ptr).setAttacked(true);
|
getCreatureStats(ptr).setAttacked(true);
|
||||||
|
|
||||||
if(!successful)
|
if(!successful)
|
||||||
|
@ -751,6 +753,17 @@ namespace MWClass
|
||||||
fatigue.setCurrent(fatigue.getCurrent() - damage, true);
|
fatigue.setCurrent(fatigue.getCurrent() - damage, true);
|
||||||
getCreatureStats(ptr).setFatigue(fatigue);
|
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
|
void Npc::block(const MWWorld::Ptr &ptr) const
|
||||||
|
|
|
@ -40,6 +40,10 @@ namespace MWMechanics
|
||||||
void readState (const ESM::ActiveSpells& state);
|
void readState (const ESM::ActiveSpells& state);
|
||||||
void writeState (ESM::ActiveSpells& state) const;
|
void writeState (ESM::ActiveSpells& state) const;
|
||||||
|
|
||||||
|
TIterator begin() const;
|
||||||
|
|
||||||
|
TIterator end() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
mutable TContainer mSpells;
|
mutable TContainer mSpells;
|
||||||
|
@ -57,10 +61,6 @@ namespace MWMechanics
|
||||||
|
|
||||||
const TContainer& getActiveSpells() const;
|
const TContainer& getActiveSpells() const;
|
||||||
|
|
||||||
TIterator begin() const;
|
|
||||||
|
|
||||||
TIterator end() const;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ActiveSpells();
|
ActiveSpells();
|
||||||
|
|
|
@ -342,6 +342,8 @@ namespace MWMechanics
|
||||||
CreatureStats &creatureStats = ptr.getClass().getCreatureStats(ptr);
|
CreatureStats &creatureStats = ptr.getClass().getCreatureStats(ptr);
|
||||||
const MagicEffects &effects = creatureStats.getMagicEffects();
|
const MagicEffects &effects = creatureStats.getMagicEffects();
|
||||||
|
|
||||||
|
bool wasDead = creatureStats.isDead();
|
||||||
|
|
||||||
// attributes
|
// attributes
|
||||||
for(int i = 0;i < ESM::Attribute::Length;++i)
|
for(int i = 0;i < ESM::Attribute::Length;++i)
|
||||||
{
|
{
|
||||||
|
@ -454,6 +456,44 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
creatureStats.setHealth(health);
|
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?
|
// TODO: dirty flag for magic effects to avoid some unnecessary work below?
|
||||||
|
|
||||||
// Update bound effects
|
// Update bound effects
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace MWMechanics
|
||||||
int CreatureStats::sActorId = 0;
|
int CreatureStats::sActorId = 0;
|
||||||
|
|
||||||
CreatureStats::CreatureStats()
|
CreatureStats::CreatureStats()
|
||||||
: mLevel (0), mDead (false), mDied (false), mFriendlyHits (0),
|
: mLevel (0), mDead (false), mDied (false), mMurdered(false), mFriendlyHits (0),
|
||||||
mTalkedTo (false), mAlarmed (false),
|
mTalkedTo (false), mAlarmed (false),
|
||||||
mAttacked (false), mHostile (false),
|
mAttacked (false), mHostile (false),
|
||||||
mAttackingOrSpell(false),
|
mAttackingOrSpell(false),
|
||||||
|
@ -245,6 +245,21 @@ namespace MWMechanics
|
||||||
mDied = false;
|
mDied = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CreatureStats::hasBeenMurdered() const
|
||||||
|
{
|
||||||
|
return mMurdered;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreatureStats::notifyMurder()
|
||||||
|
{
|
||||||
|
mMurdered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreatureStats::clearHasBeenMurdered()
|
||||||
|
{
|
||||||
|
mMurdered = false;
|
||||||
|
}
|
||||||
|
|
||||||
void CreatureStats::resurrect()
|
void CreatureStats::resurrect()
|
||||||
{
|
{
|
||||||
if (mDead)
|
if (mDead)
|
||||||
|
@ -479,6 +494,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
state.mDead = mDead;
|
state.mDead = mDead;
|
||||||
state.mDied = mDied;
|
state.mDied = mDied;
|
||||||
|
state.mMurdered = mMurdered;
|
||||||
state.mFriendlyHits = mFriendlyHits;
|
state.mFriendlyHits = mFriendlyHits;
|
||||||
state.mTalkedTo = mTalkedTo;
|
state.mTalkedTo = mTalkedTo;
|
||||||
state.mAlarmed = mAlarmed;
|
state.mAlarmed = mAlarmed;
|
||||||
|
@ -527,6 +543,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
mDead = state.mDead;
|
mDead = state.mDead;
|
||||||
mDied = state.mDied;
|
mDied = state.mDied;
|
||||||
|
mMurdered = state.mMurdered;
|
||||||
mFriendlyHits = state.mFriendlyHits;
|
mFriendlyHits = state.mFriendlyHits;
|
||||||
mTalkedTo = state.mTalkedTo;
|
mTalkedTo = state.mTalkedTo;
|
||||||
mAlarmed = state.mAlarmed;
|
mAlarmed = state.mAlarmed;
|
||||||
|
|
|
@ -35,6 +35,7 @@ namespace MWMechanics
|
||||||
AiSequence mAiSequence;
|
AiSequence mAiSequence;
|
||||||
bool mDead;
|
bool mDead;
|
||||||
bool mDied;
|
bool mDied;
|
||||||
|
bool mMurdered;
|
||||||
int mFriendlyHits;
|
int mFriendlyHits;
|
||||||
bool mTalkedTo;
|
bool mTalkedTo;
|
||||||
bool mAlarmed;
|
bool mAlarmed;
|
||||||
|
@ -170,6 +171,12 @@ namespace MWMechanics
|
||||||
|
|
||||||
void clearHasDied();
|
void clearHasDied();
|
||||||
|
|
||||||
|
bool hasBeenMurdered() const;
|
||||||
|
|
||||||
|
void clearHasBeenMurdered();
|
||||||
|
|
||||||
|
void notifyMurder();
|
||||||
|
|
||||||
void resurrect();
|
void resurrect();
|
||||||
|
|
||||||
bool hasCommonDisease() const;
|
bool hasCommonDisease() const;
|
||||||
|
|
|
@ -868,10 +868,16 @@ namespace MWMechanics
|
||||||
// Find actors who directly witnessed the crime
|
// Find actors who directly witnessed the crime
|
||||||
for (std::vector<MWWorld::Ptr>::iterator it = neighbors.begin(); it != neighbors.end(); ++it)
|
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?
|
// 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)
|
if (*it == victim)
|
||||||
victimAware = true;
|
victimAware = true;
|
||||||
|
@ -904,6 +910,9 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
const MWWorld::Store<ESM::GameSetting>& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
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
|
// Bounty for each type of crime
|
||||||
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
|
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
|
||||||
arg = store.find("iCrimeTresspass")->getInt();
|
arg = store.find("iCrimeTresspass")->getInt();
|
||||||
|
@ -971,7 +980,7 @@ namespace MWMechanics
|
||||||
for (std::vector<MWWorld::Ptr>::iterator it = neighbors.begin(); it != neighbors.end(); ++it)
|
for (std::vector<MWWorld::Ptr>::iterator it = neighbors.begin(); it != neighbors.end(); ++it)
|
||||||
{
|
{
|
||||||
if ( *it == player
|
if ( *it == player
|
||||||
|| !it->getClass().isNpc()) continue;
|
|| !it->getClass().isNpc() || it->getClass().getCreatureStats(*it).isDead()) continue;
|
||||||
|
|
||||||
int aggression = fight;
|
int aggression = fight;
|
||||||
|
|
||||||
|
|
|
@ -398,5 +398,7 @@ op 0x2000245: ClearInfoActor
|
||||||
op 0x2000246: ClearInfoActor, explicit
|
op 0x2000246: ClearInfoActor, explicit
|
||||||
op 0x2000247: BetaComment
|
op 0x2000247: BetaComment
|
||||||
op 0x2000248: BetaComment, explicit
|
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>
|
template <class R>
|
||||||
class OpOnKnockout : public Interpreter::Opcode0
|
class OpOnKnockout : public Interpreter::Opcode0
|
||||||
{
|
{
|
||||||
|
@ -1264,6 +1283,8 @@ namespace MWScript
|
||||||
|
|
||||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnDeath, new OpOnDeath<ImplicitRef>);
|
interpreter.installSegment5 (Compiler::Stats::opcodeOnDeath, new OpOnDeath<ImplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnDeathExplicit, new OpOnDeath<ExplicitRef>);
|
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::opcodeOnKnockout, new OpOnKnockout<ImplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockoutExplicit, new OpOnKnockout<ExplicitRef>);
|
interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockoutExplicit, new OpOnKnockout<ExplicitRef>);
|
||||||
|
|
||||||
|
|
|
@ -449,6 +449,7 @@ namespace Compiler
|
||||||
extensions.registerInstruction ("lowerrank", "", opcodeLowerRank, opcodeLowerRankExplicit);
|
extensions.registerInstruction ("lowerrank", "", opcodeLowerRank, opcodeLowerRankExplicit);
|
||||||
|
|
||||||
extensions.registerFunction ("ondeath", 'l', "", opcodeOnDeath, opcodeOnDeathExplicit);
|
extensions.registerFunction ("ondeath", 'l', "", opcodeOnDeath, opcodeOnDeathExplicit);
|
||||||
|
extensions.registerFunction ("onmurder", 'l', "", opcodeOnMurder, opcodeOnMurderExplicit);
|
||||||
extensions.registerFunction ("onknockout", 'l', "", opcodeOnKnockout, opcodeOnKnockoutExplicit);
|
extensions.registerFunction ("onknockout", 'l', "", opcodeOnKnockout, opcodeOnKnockoutExplicit);
|
||||||
|
|
||||||
extensions.registerFunction ("iswerewolf", 'l', "", opcodeIsWerewolf, opcodeIsWerewolfExplicit);
|
extensions.registerFunction ("iswerewolf", 'l', "", opcodeIsWerewolf, opcodeIsWerewolfExplicit);
|
||||||
|
|
|
@ -385,6 +385,8 @@ namespace Compiler
|
||||||
const int opcodeLowerRankExplicit = 0x20001eb;
|
const int opcodeLowerRankExplicit = 0x20001eb;
|
||||||
const int opcodeOnDeath = 0x20001fc;
|
const int opcodeOnDeath = 0x20001fc;
|
||||||
const int opcodeOnDeathExplicit = 0x2000205;
|
const int opcodeOnDeathExplicit = 0x2000205;
|
||||||
|
const int opcodeOnMurder = 0x2000249;
|
||||||
|
const int opcodeOnMurderExplicit = 0x200024a;
|
||||||
const int opcodeOnKnockout = 0x2000240;
|
const int opcodeOnKnockout = 0x2000240;
|
||||||
const int opcodeOnKnockoutExplicit = 0x2000241;
|
const int opcodeOnKnockoutExplicit = 0x2000241;
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,9 @@ void ESM::CreatureStats::load (ESMReader &esm)
|
||||||
mDied = false;
|
mDied = false;
|
||||||
esm.getHNOT (mDied, "DIED");
|
esm.getHNOT (mDied, "DIED");
|
||||||
|
|
||||||
|
mMurdered = false;
|
||||||
|
esm.getHNOT (mMurdered, "MURD");
|
||||||
|
|
||||||
mFriendlyHits = 0;
|
mFriendlyHits = 0;
|
||||||
esm.getHNOT (mFriendlyHits, "FRHT");
|
esm.getHNOT (mFriendlyHits, "FRHT");
|
||||||
|
|
||||||
|
@ -127,6 +130,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
|
||||||
if (mDied)
|
if (mDied)
|
||||||
esm.writeHNT ("DIED", mDied);
|
esm.writeHNT ("DIED", mDied);
|
||||||
|
|
||||||
|
if (mMurdered)
|
||||||
|
esm.writeHNT ("MURD", mMurdered);
|
||||||
|
|
||||||
if (mFriendlyHits)
|
if (mFriendlyHits)
|
||||||
esm.writeHNT ("FRHT", mFriendlyHits);
|
esm.writeHNT ("FRHT", mFriendlyHits);
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ namespace ESM
|
||||||
|
|
||||||
bool mDead;
|
bool mDead;
|
||||||
bool mDied;
|
bool mDied;
|
||||||
|
bool mMurdered;
|
||||||
int mFriendlyHits;
|
int mFriendlyHits;
|
||||||
bool mTalkedTo;
|
bool mTalkedTo;
|
||||||
bool mAlarmed;
|
bool mAlarmed;
|
||||||
|
|
Loading…
Reference in a new issue