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;
/// @param bias Can be used to add an additional aggression bias towards the 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) = 0;
virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0;
/// Resurrects the player if necessary
virtual void keepPlayerAlive() = 0;

@ -23,6 +23,33 @@
#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
{
void MechanicsManager::buildPlayer()
@ -983,32 +1010,32 @@ namespace MWMechanics
if (type == OT_Murder && !victim.isEmpty())
victim.getClass().getCreatureStats(victim).notifyMurder();
// Bounty for each type of crime
float dispTerm = 0.f, dispTermVictim = 0.f;
// Bounty and disposition penalty for each type of crime
float disp = 0.f, dispVictim = 0.f;
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
{
arg = store.find("iCrimeTresspass")->getInt();
dispTerm = dispTermVictim = store.find("iDispTresspass")->getInt();
disp = dispVictim = store.find("iDispTresspass")->getInt();
}
else if (type == OT_Pickpocket)
{
arg = store.find("iCrimePickPocket")->getInt();
dispTerm = dispTermVictim = store.find("fDispPickPocketMod")->getFloat();
disp = dispVictim = store.find("fDispPickPocketMod")->getFloat();
}
else if (type == OT_Assault)
{
arg = store.find("iCrimeAttack")->getInt();
dispTerm = store.find("fDispAttacking")->getFloat();
dispTermVictim = store.find("iDispAttackMod")->getInt();
disp = store.find("fDispAttacking")->getFloat();
dispVictim = store.find("iDispAttackMod")->getInt();
}
else if (type == OT_Murder)
{
arg = store.find("iCrimeKilling")->getInt();
dispTerm = dispTermVictim = store.find("iDispKilling")->getInt();
disp = dispVictim = store.find("iDispKilling")->getInt();
}
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 = 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
|| !it->getClass().isNpc() || it->getClass().getCreatureStats(*it).isDead()) continue;
int aggression = (*it == victim) ? fightVictim : fight;
if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim))
continue;
@ -1081,30 +1106,41 @@ namespace MWMechanics
}
else
{
int dispChange = (*it == victim) ? dispTermVictim : dispTerm;
NpcStats& observerStats = it->getClass().getNpcStats(*it);
int originalDisposition = observerStats.getBaseDisposition();
observerStats.setBaseDisposition(originalDisposition+dispChange);
float dispTerm = (*it == victim) ? dispVictim : disp;
float alarmTerm = 0.01 * it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase();
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);
if (aggressive)
int observerFightRating = it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Fight).getBase();
if (observerFightRating + fightTerm > 100)
fightTerm = 100 - observerFightRating;
fightTerm = std::max(0.f, fightTerm);
if (observerFightRating + fightTerm >= 100)
{
startCombat(*it, player);
NpcStats& observerStats = it->getClass().getNpcStats(*it);
// Apply aggression value to the base Fight rating, so that the actor can continue fighting
// after a Calm spell wears off
int fightBase = it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Fight).getBase();
it->getClass().getCreatureStats(*it).setAiSetting(CreatureStats::AI_Fight, fightBase + aggression);
observerStats.setAiSetting(CreatureStats::AI_Fight, observerFightRating + fightTerm);
observerStats.setBaseDisposition(observerStats.getBaseDisposition()+dispTerm);
// Set the crime ID, which we will use to calm down participants
// once the bounty has been paid.
it->getClass().getNpcStats(*it).setCrimeId(id);
observerStats.setCrimeId(id);
// 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();
}
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;
if (ptr.getClass().isNpc())
disposition = getDerivedDisposition(ptr);
int fight = std::max(0.f, ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified()
+ (iFightDistanceBase - fFightDistanceMultiplier * d)
+ ((50 - disposition) * fFightDispMult))
+ bias;
+ getFightDistanceBias(ptr, target)
+ getFightDispositionBias(disposition));
if (ptr.getClass().isNpc() && target.getClass().isNpc())
{

@ -162,9 +162,7 @@ namespace MWMechanics
virtual void clear();
/// @param bias Can be used to add an additional aggression bias towards the 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 bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target);
virtual void keepPlayerAlive();

Loading…
Cancel
Save