forked from mirror/openmw-tes3mp
Use fFight<Crime> GMSTs to control attacks in response to crimes
This commit is contained in:
parent
8ac019611d
commit
ffb6f5d555
5 changed files with 105 additions and 57 deletions
|
@ -120,6 +120,7 @@ namespace MWBase
|
||||||
/**
|
/**
|
||||||
* @brief Commit a crime. If any actors witness the crime and report it,
|
* @brief Commit a crime. If any actors witness the crime and report it,
|
||||||
* reportCrime will be called automatically.
|
* reportCrime will be called automatically.
|
||||||
|
* @note victim may be empty
|
||||||
* @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen.
|
* @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen.
|
||||||
* @return was the crime reported?
|
* @return was the crime reported?
|
||||||
*/
|
*/
|
||||||
|
@ -192,7 +193,9 @@ namespace MWBase
|
||||||
|
|
||||||
virtual void clear() = 0;
|
virtual void clear() = 0;
|
||||||
|
|
||||||
virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 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) = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -789,7 +789,6 @@ namespace MWMechanics
|
||||||
|
|
||||||
if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.isHostile())
|
if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.isHostile())
|
||||||
{
|
{
|
||||||
/// \todo Move me! I shouldn't be here...
|
|
||||||
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
||||||
float cutoff = float(esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->getInt());
|
float cutoff = float(esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->getInt());
|
||||||
// Force dialogue on sight if bounty is greater than the cutoff
|
// Force dialogue on sight if bounty is greater than the cutoff
|
||||||
|
@ -817,7 +816,6 @@ namespace MWMechanics
|
||||||
creatureStats.getAiSequence().stopCombat();
|
creatureStats.getAiSequence().stopCombat();
|
||||||
|
|
||||||
// Reset factors to attack
|
// Reset factors to attack
|
||||||
// TODO: Not a complete list, disposition changes?
|
|
||||||
creatureStats.setHostile(false);
|
creatureStats.setHostile(false);
|
||||||
creatureStats.setAttacked(false);
|
creatureStats.setAttacked(false);
|
||||||
creatureStats.setAlarmed(false);
|
creatureStats.setAlarmed(false);
|
||||||
|
@ -825,15 +823,6 @@ namespace MWMechanics
|
||||||
// Update witness crime id
|
// Update witness crime id
|
||||||
npcStats.setCrimeId(-1);
|
npcStats.setCrimeId(-1);
|
||||||
}
|
}
|
||||||
else if (!creatureStats.isHostile() && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue)
|
|
||||||
{
|
|
||||||
if (ptr.getClass().isClass(ptr, "Guard"))
|
|
||||||
creatureStats.getAiSequence().stack(AiPursue(player), ptr);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,6 +221,11 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor)
|
||||||
{
|
{
|
||||||
return; // target is already pursued
|
return; // target is already pursued
|
||||||
}
|
}
|
||||||
|
if((*iter)->getTypeId() == AiPackage::TypeIdCombat && package.getTypeId() == AiPackage::TypeIdCombat
|
||||||
|
&& static_cast<const AiCombat*>(*iter)->getTarget() == static_cast<const AiCombat*>(&package)->getTarget())
|
||||||
|
{
|
||||||
|
return; // already in combat with this actor
|
||||||
|
}
|
||||||
else if ((*iter)->getTypeId() == AiPackage::TypeIdWander)
|
else if ((*iter)->getTypeId() == AiPackage::TypeIdWander)
|
||||||
static_cast<AiWander*>(*iter)->setReturnPosition(Ogre::Vector3(actor.getRefData().getPosition().pos));
|
static_cast<AiWander*>(*iter)->setReturnPosition(Ogre::Vector3(actor.getRefData().getPosition().pos));
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "../mwworld/player.hpp"
|
#include "../mwworld/player.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/aicombat.hpp"
|
#include "../mwmechanics/aicombat.hpp"
|
||||||
|
#include "../mwmechanics/aipursue.hpp"
|
||||||
|
|
||||||
#include <OgreSceneNode.h>
|
#include <OgreSceneNode.h>
|
||||||
|
|
||||||
|
@ -836,7 +837,7 @@ namespace MWMechanics
|
||||||
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
||||||
|
|
||||||
// What amount of alarm did this crime generate?
|
// What amount of alarm did this crime generate?
|
||||||
int alarm;
|
int alarm = 0;
|
||||||
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
|
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
|
||||||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmTresspass")->getInt();
|
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmTresspass")->getInt();
|
||||||
else if (type == OT_Pickpocket)
|
else if (type == OT_Pickpocket)
|
||||||
|
@ -847,17 +848,14 @@ namespace MWMechanics
|
||||||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmKilling")->getInt();
|
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmKilling")->getInt();
|
||||||
else if (type == OT_Theft)
|
else if (type == OT_Theft)
|
||||||
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmStealing")->getInt();
|
alarm = esmStore.get<ESM::GameSetting>().find("iAlarmStealing")->getInt();
|
||||||
else
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Innocent until proven guilty
|
|
||||||
bool reported = false;
|
bool reported = false;
|
||||||
|
|
||||||
// Find all the actors within the alarm radius
|
// Find all the actors within the alarm radius
|
||||||
std::vector<MWWorld::Ptr> neighbors;
|
std::vector<MWWorld::Ptr> neighbors;
|
||||||
|
|
||||||
Ogre::Vector3 from = Ogre::Vector3(player.getRefData().getPosition().pos);
|
Ogre::Vector3 from = Ogre::Vector3(player.getRefData().getPosition().pos);
|
||||||
int radius = esmStore.get<ESM::GameSetting>().find("fAlarmRadius")->getInt();
|
float radius = esmStore.get<ESM::GameSetting>().find("fAlarmRadius")->getFloat();
|
||||||
|
|
||||||
mActors.getObjectsInRange(from, radius, neighbors);
|
mActors.getObjectsInRange(from, radius, neighbors);
|
||||||
|
|
||||||
|
@ -865,9 +863,9 @@ namespace MWMechanics
|
||||||
if (!victim.isEmpty() && from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius)
|
if (!victim.isEmpty() && from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius)
|
||||||
neighbors.push_back(victim);
|
neighbors.push_back(victim);
|
||||||
|
|
||||||
int id = MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId();
|
bool victimAware = false;
|
||||||
|
|
||||||
// Find actors who 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; // not the player
|
||||||
|
@ -875,22 +873,13 @@ namespace MWMechanics
|
||||||
// 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) )
|
||||||
{
|
{
|
||||||
// TODO: Add more messages
|
if (*it == victim)
|
||||||
|
victimAware = true;
|
||||||
|
|
||||||
|
// TODO: are there other messages?
|
||||||
if (type == OT_Theft)
|
if (type == OT_Theft)
|
||||||
MWBase::Environment::get().getDialogueManager()->say(*it, "thief");
|
MWBase::Environment::get().getDialogueManager()->say(*it, "thief");
|
||||||
|
|
||||||
if (*it == victim)
|
|
||||||
{
|
|
||||||
// Self-defense
|
|
||||||
// The victim is aware of the criminal/assailant. If being assaulted, fight back now
|
|
||||||
// (regardless of whether the assault is reported or not)
|
|
||||||
// This applies to both NPCs and creatures
|
|
||||||
|
|
||||||
// ... except if this is a guard: then the player is given a chance to pay a fine / go to jail instead
|
|
||||||
if (type == OT_Assault && !it->getClass().isClass(*it, "guard"))
|
|
||||||
MWBase::Environment::get().getMechanicsManager()->startCombat(victim, player);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Crime reporting only applies to NPCs
|
// Crime reporting only applies to NPCs
|
||||||
if (!it->getClass().isNpc())
|
if (!it->getClass().isNpc())
|
||||||
continue;
|
continue;
|
||||||
|
@ -899,32 +888,19 @@ namespace MWMechanics
|
||||||
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm)
|
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm)
|
||||||
{
|
{
|
||||||
reported = true;
|
reported = true;
|
||||||
|
|
||||||
// Tell everyone, including yourself
|
|
||||||
for (std::vector<MWWorld::Ptr>::iterator it1 = neighbors.begin(); it1 != neighbors.end(); ++it1)
|
|
||||||
{
|
|
||||||
if ( *it1 == player
|
|
||||||
|| !it1->getClass().isNpc()) continue; // not the player and is an NPC
|
|
||||||
|
|
||||||
// Will other witnesses paticipate in crime
|
|
||||||
if ( it1->getClass().getCreatureStats(*it1).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm
|
|
||||||
|| type == OT_Assault )
|
|
||||||
{
|
|
||||||
it1->getClass().getNpcStats(*it1).setCrimeId(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark as Alarmed for dialogue
|
|
||||||
it1->getClass().getCreatureStats(*it1).setAlarmed(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reported)
|
if (reported)
|
||||||
reportCrime(player, victim, type, arg);
|
reportCrime(player, victim, type, arg);
|
||||||
|
else if (victimAware && !victim.isEmpty() && type == OT_Assault)
|
||||||
|
startCombat(victim, player);
|
||||||
|
|
||||||
return reported;
|
return reported;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MechanicsManager::reportCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg)
|
void MechanicsManager::reportCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg)
|
||||||
{
|
{
|
||||||
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>();
|
||||||
|
|
||||||
|
@ -944,7 +920,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}");
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}");
|
||||||
ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty()
|
player.getClass().getNpcStats(player).setBounty(player.getClass().getNpcStats(player).getBounty()
|
||||||
+ arg);
|
+ arg);
|
||||||
|
|
||||||
// If committing a crime against a faction member, expell from the faction
|
// If committing a crime against a faction member, expell from the faction
|
||||||
|
@ -953,9 +929,81 @@ namespace MWMechanics
|
||||||
std::string factionID;
|
std::string factionID;
|
||||||
if(!victim.getClass().getNpcStats(victim).getFactionRanks().empty())
|
if(!victim.getClass().getNpcStats(victim).getFactionRanks().empty())
|
||||||
factionID = victim.getClass().getNpcStats(victim).getFactionRanks().begin()->first;
|
factionID = victim.getClass().getNpcStats(victim).getFactionRanks().begin()->first;
|
||||||
if (ptr.getClass().getNpcStats(ptr).isSameFaction(victim.getClass().getNpcStats(victim)))
|
if (player.getClass().getNpcStats(player).isSameFaction(victim.getClass().getNpcStats(victim)))
|
||||||
{
|
{
|
||||||
ptr.getClass().getNpcStats(ptr).expell(factionID);
|
player.getClass().getNpcStats(player).expell(factionID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make surrounding actors within alarm distance respond to the crime
|
||||||
|
std::vector<MWWorld::Ptr> neighbors;
|
||||||
|
|
||||||
|
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
||||||
|
|
||||||
|
Ogre::Vector3 from = Ogre::Vector3(player.getRefData().getPosition().pos);
|
||||||
|
float radius = esmStore.get<ESM::GameSetting>().find("fAlarmRadius")->getFloat();
|
||||||
|
|
||||||
|
mActors.getObjectsInRange(from, radius, neighbors);
|
||||||
|
|
||||||
|
// victim should be considered even beyond alarm radius
|
||||||
|
if (!victim.isEmpty() && from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius)
|
||||||
|
neighbors.push_back(victim);
|
||||||
|
|
||||||
|
int id = MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId();
|
||||||
|
|
||||||
|
// What amount of provocation did this crime generate?
|
||||||
|
// Controls whether witnesses will engage combat with the criminal.
|
||||||
|
int fight = 0;
|
||||||
|
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
|
||||||
|
fight = esmStore.get<ESM::GameSetting>().find("iFightTrespass")->getInt();
|
||||||
|
else if (type == OT_Pickpocket)
|
||||||
|
fight = esmStore.get<ESM::GameSetting>().find("iFightPickpocket")->getInt();
|
||||||
|
else if (type == OT_Assault) // Note: iFightAttack is for the victim, iFightAttacking for witnesses?
|
||||||
|
fight = esmStore.get<ESM::GameSetting>().find("iFightAttack")->getInt();
|
||||||
|
else if (type == OT_Murder)
|
||||||
|
fight = esmStore.get<ESM::GameSetting>().find("iFightKilling")->getInt();
|
||||||
|
else if (type == OT_Theft)
|
||||||
|
fight = esmStore.get<ESM::GameSetting>().find("fFightStealing")->getFloat();
|
||||||
|
|
||||||
|
const int iFightAttacking = esmStore.get<ESM::GameSetting>().find("iFightAttacking")->getInt();
|
||||||
|
|
||||||
|
// Tell everyone (including the original reporter) in alarm range
|
||||||
|
for (std::vector<MWWorld::Ptr>::iterator it = neighbors.begin(); it != neighbors.end(); ++it)
|
||||||
|
{
|
||||||
|
if ( *it == player
|
||||||
|
|| !it->getClass().isNpc()) continue;
|
||||||
|
|
||||||
|
int aggression = fight;
|
||||||
|
|
||||||
|
// Note: iFightAttack is used for the victim, iFightAttacking for witnesses?
|
||||||
|
if (*it != victim && type == OT_Assault)
|
||||||
|
aggression = iFightAttacking;
|
||||||
|
|
||||||
|
if (it->getClass().isClass(*it, "guard"))
|
||||||
|
{
|
||||||
|
// Mark as Alarmed for dialogue
|
||||||
|
it->getClass().getCreatureStats(*it).setAlarmed(true);
|
||||||
|
|
||||||
|
// Set the crime ID, which we will use to calm down participants
|
||||||
|
// once the bounty has been paid.
|
||||||
|
it->getClass().getNpcStats(*it).setCrimeId(id);
|
||||||
|
|
||||||
|
it->getClass().getCreatureStats(*it).getAiSequence().stack(AiPursue(player), *it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(*it, player, aggression);
|
||||||
|
if (aggressive)
|
||||||
|
{
|
||||||
|
startCombat(*it, player);
|
||||||
|
|
||||||
|
// Set the crime ID, which we will use to calm down participants
|
||||||
|
// once the bounty has been paid.
|
||||||
|
it->getClass().getNpcStats(*it).setCrimeId(id);
|
||||||
|
|
||||||
|
// Mark as Alarmed for dialogue
|
||||||
|
it->getClass().getCreatureStats(*it).setAlarmed(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1099,7 +1147,7 @@ namespace MWMechanics
|
||||||
mActors.clear();
|
mActors.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target)
|
bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target, int bias)
|
||||||
{
|
{
|
||||||
Ogre::Vector3 pos1 (ptr.getRefData().getPosition().pos);
|
Ogre::Vector3 pos1 (ptr.getRefData().getPosition().pos);
|
||||||
Ogre::Vector3 pos2 (target.getRefData().getPosition().pos);
|
Ogre::Vector3 pos2 (target.getRefData().getPosition().pos);
|
||||||
|
@ -1117,9 +1165,10 @@ namespace MWMechanics
|
||||||
if (ptr.getClass().isNpc())
|
if (ptr.getClass().isNpc())
|
||||||
disposition = getDerivedDisposition(ptr);
|
disposition = getDerivedDisposition(ptr);
|
||||||
|
|
||||||
int fight = 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)
|
+ (iFightDistanceBase - fFightDistanceMultiplier * d)
|
||||||
+ ((50 - disposition) * fFightDispMult);
|
+ ((50 - disposition) * fFightDispMult))
|
||||||
|
+ bias;
|
||||||
|
|
||||||
return (fight >= 100);
|
return (fight >= 100);
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,7 +157,9 @@ namespace MWMechanics
|
||||||
|
|
||||||
virtual void clear();
|
virtual void clear();
|
||||||
|
|
||||||
virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target);
|
/// @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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue