mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-03-29 16:36:43 +00:00
Update crime system according to research wiki for more accurate attack responses
This commit is contained in:
parent
2410d79410
commit
9e5dfb6e98
3 changed files with 63 additions and 46 deletions
|
@ -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();
|
|
||||||
observerStats.setBaseDisposition(originalDisposition+dispChange);
|
|
||||||
|
|
||||||
bool aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(*it, player, aggression, true);
|
float alarmTerm = 0.01 * it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase();
|
||||||
if (aggressive)
|
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;
|
||||||
|
|
||||||
|
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);
|
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…
Reference in a new issue