Update crime system according to research wiki for more accurate attack responses

moveref
scrawl 10 years ago
parent 2410d79410
commit 9e5dfb6e98

@ -197,9 +197,7 @@ namespace MWBase
virtual void clear() = 0; virtual void clear() = 0;
/// @param bias Can be used to add an additional aggression bias towards the target, virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0;
/// making it more likely for the function to return true.
virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false) = 0;
/// Resurrects the player if necessary /// Resurrects the player if necessary
virtual void keepPlayerAlive() = 0; virtual void keepPlayerAlive() = 0;

@ -23,6 +23,33 @@
#include <limits.h> #include <limits.h>
namespace
{
float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2)
{
Ogre::Vector3 pos1 (actor1.getRefData().getPosition().pos);
Ogre::Vector3 pos2 (actor2.getRefData().getPosition().pos);
float d = pos1.distance(pos2);
static const int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"iFightDistanceBase")->getInt();
static const float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"fFightDistanceMultiplier")->getFloat();
return (iFightDistanceBase - fFightDistanceMultiplier * d);
}
float getFightDispositionBias(float disposition)
{
static const float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"fFightDispMult")->getFloat();
return ((50.f - disposition) * fFightDispMult);
}
}
namespace MWMechanics namespace MWMechanics
{ {
void MechanicsManager::buildPlayer() void MechanicsManager::buildPlayer()
@ -983,32 +1010,32 @@ namespace MWMechanics
if (type == OT_Murder && !victim.isEmpty()) if (type == OT_Murder && !victim.isEmpty())
victim.getClass().getCreatureStats(victim).notifyMurder(); victim.getClass().getCreatureStats(victim).notifyMurder();
// Bounty for each type of crime // Bounty and disposition penalty for each type of crime
float dispTerm = 0.f, dispTermVictim = 0.f; float disp = 0.f, dispVictim = 0.f;
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
{ {
arg = store.find("iCrimeTresspass")->getInt(); arg = store.find("iCrimeTresspass")->getInt();
dispTerm = dispTermVictim = store.find("iDispTresspass")->getInt(); disp = dispVictim = store.find("iDispTresspass")->getInt();
} }
else if (type == OT_Pickpocket) else if (type == OT_Pickpocket)
{ {
arg = store.find("iCrimePickPocket")->getInt(); arg = store.find("iCrimePickPocket")->getInt();
dispTerm = dispTermVictim = store.find("fDispPickPocketMod")->getFloat(); disp = dispVictim = store.find("fDispPickPocketMod")->getFloat();
} }
else if (type == OT_Assault) else if (type == OT_Assault)
{ {
arg = store.find("iCrimeAttack")->getInt(); arg = store.find("iCrimeAttack")->getInt();
dispTerm = store.find("fDispAttacking")->getFloat(); disp = store.find("fDispAttacking")->getFloat();
dispTermVictim = store.find("iDispAttackMod")->getInt(); dispVictim = store.find("iDispAttackMod")->getInt();
} }
else if (type == OT_Murder) else if (type == OT_Murder)
{ {
arg = store.find("iCrimeKilling")->getInt(); arg = store.find("iCrimeKilling")->getInt();
dispTerm = dispTermVictim = store.find("iDispKilling")->getInt(); disp = dispVictim = store.find("iDispKilling")->getInt();
} }
else if (type == OT_Theft) else if (type == OT_Theft)
{ {
dispTerm = dispTermVictim = store.find("fDispStealing")->getFloat() * arg; disp = dispVictim = store.find("fDispStealing")->getFloat() * arg;
arg *= store.find("fCrimeStealing")->getFloat(); arg *= store.find("fCrimeStealing")->getFloat();
arg = std::max(1, arg); // Minimum bounty of 1, in case items with zero value are stolen arg = std::max(1, arg); // Minimum bounty of 1, in case items with zero value are stolen
} }
@ -1057,8 +1084,6 @@ namespace MWMechanics
if ( *it == player if ( *it == player
|| !it->getClass().isNpc() || it->getClass().getCreatureStats(*it).isDead()) continue; || !it->getClass().isNpc() || it->getClass().getCreatureStats(*it).isDead()) continue;
int aggression = (*it == victim) ? fightVictim : fight;
if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim)) if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim))
continue; continue;
@ -1081,30 +1106,41 @@ namespace MWMechanics
} }
else else
{ {
int dispChange = (*it == victim) ? dispTermVictim : dispTerm; float dispTerm = (*it == victim) ? dispVictim : disp;
NpcStats& observerStats = it->getClass().getNpcStats(*it);
int originalDisposition = observerStats.getBaseDisposition(); float alarmTerm = 0.01 * it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase();
observerStats.setBaseDisposition(originalDisposition+dispChange); if (*it != victim)
dispTerm *= alarmTerm;
float fightTerm = (*it == victim) ? fightVictim : fight;
fightTerm += getFightDispositionBias(dispTerm);
fightTerm += getFightDistanceBias(*it, player);
if (type != OT_Pickpocket) // type check not in the wiki, but this seems to be needed for MW behaviour
fightTerm *= alarmTerm;
bool aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(*it, player, aggression, true); int observerFightRating = it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Fight).getBase();
if (aggressive) if (observerFightRating + fightTerm > 100)
fightTerm = 100 - observerFightRating;
fightTerm = std::max(0.f, fightTerm);
if (observerFightRating + fightTerm >= 100)
{ {
startCombat(*it, player); startCombat(*it, player);
NpcStats& observerStats = it->getClass().getNpcStats(*it);
// Apply aggression value to the base Fight rating, so that the actor can continue fighting // Apply aggression value to the base Fight rating, so that the actor can continue fighting
// after a Calm spell wears off // after a Calm spell wears off
int fightBase = it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Fight).getBase(); observerStats.setAiSetting(CreatureStats::AI_Fight, observerFightRating + fightTerm);
it->getClass().getCreatureStats(*it).setAiSetting(CreatureStats::AI_Fight, fightBase + aggression);
observerStats.setBaseDisposition(observerStats.getBaseDisposition()+dispTerm);
// Set the crime ID, which we will use to calm down participants // Set the crime ID, which we will use to calm down participants
// once the bounty has been paid. // once the bounty has been paid.
it->getClass().getNpcStats(*it).setCrimeId(id); observerStats.setCrimeId(id);
// Mark as Alarmed for dialogue // Mark as Alarmed for dialogue
it->getClass().getCreatureStats(*it).setAlarmed(true); observerStats.setAlarmed(true);
} }
else
observerStats.setBaseDisposition(originalDisposition);
} }
} }
@ -1318,30 +1354,15 @@ namespace MWMechanics
mActors.clear(); mActors.clear();
} }
bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target, int bias, bool ignoreDistance) bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target)
{ {
Ogre::Vector3 pos1 (ptr.getRefData().getPosition().pos);
Ogre::Vector3 pos2 (target.getRefData().getPosition().pos);
float d = 0;
if (!ignoreDistance)
d = pos1.distance(pos2);
static int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"iFightDistanceBase")->getInt();
static float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"fFightDistanceMultiplier")->getFloat();
static float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"fFightDispMult")->getFloat();
int disposition = 50; int disposition = 50;
if (ptr.getClass().isNpc()) if (ptr.getClass().isNpc())
disposition = getDerivedDisposition(ptr); disposition = getDerivedDisposition(ptr);
int fight = std::max(0.f, ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified() int fight = std::max(0.f, ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified()
+ (iFightDistanceBase - fFightDistanceMultiplier * d) + getFightDistanceBias(ptr, target)
+ ((50 - disposition) * fFightDispMult)) + getFightDispositionBias(disposition));
+ bias;
if (ptr.getClass().isNpc() && target.getClass().isNpc()) if (ptr.getClass().isNpc() && target.getClass().isNpc())
{ {

@ -162,9 +162,7 @@ namespace MWMechanics
virtual void clear(); virtual void clear();
/// @param bias Can be used to add an additional aggression bias towards the target, virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target);
/// making it more likely for the function to return true.
virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false);
virtual void keepPlayerAlive(); virtual void keepPlayerAlive();

Loading…
Cancel
Save