forked from mirror/openmw-tes3mp
Crime system improvements
- If someone saw the crime, they will notify everyone else in range, even if the Alarm rating of the witness is 0. - Pickpocket and selling stolen items now works properly, i.e. honors the victim's Alarm rating instead of always being reported.
This commit is contained in:
parent
307b84e9f6
commit
866fdfe8bd
6 changed files with 57 additions and 69 deletions
|
@ -118,16 +118,14 @@ namespace MWBase
|
||||||
OT_Pickpocket // Entering pickpocket mode, leaving it, and being detected. Any items stolen are a separate crime (Theft)
|
OT_Pickpocket // Entering pickpocket mode, leaving it, and being detected. Any items stolen are a separate crime (Theft)
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* @brief Commit a crime. If any actors witness the crime and report it,
|
|
||||||
* reportCrime will be called automatically.
|
|
||||||
* @note victim may be empty
|
* @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?
|
* @param victimAware Is the victim already aware of the crime?
|
||||||
|
* If this parameter is false, it will be determined by a line-of-sight and awareness check.
|
||||||
|
* @return was the crime seen?
|
||||||
*/
|
*/
|
||||||
virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
|
virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
|
||||||
OffenseType type, int arg=0) = 0;
|
OffenseType type, int arg=0, bool victimAware=false) = 0;
|
||||||
virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
|
|
||||||
OffenseType type, int arg=0) = 0;
|
|
||||||
/// @return false if the attack was considered a "friendly hit" and forgiven
|
/// @return false if the attack was considered a "friendly hit" and forgiven
|
||||||
virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) = 0;
|
virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) = 0;
|
||||||
/// Utility to check if taking this item is illegal and calling commitCrime if so
|
/// Utility to check if taking this item is illegal and calling commitCrime if so
|
||||||
|
|
|
@ -301,10 +301,9 @@ namespace MWGui
|
||||||
MWMechanics::Pickpocket pickpocket(player, mPtr);
|
MWMechanics::Pickpocket pickpocket(player, mPtr);
|
||||||
if (pickpocket.finish())
|
if (pickpocket.finish())
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getMechanicsManager()->reportCrime(
|
MWBase::Environment::get().getMechanicsManager()->commitCrime(
|
||||||
player, mPtr, MWBase::MechanicsManager::OT_Pickpocket);
|
player, mPtr, MWBase::MechanicsManager::OT_Pickpocket, 0, true);
|
||||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
|
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
|
||||||
MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief");
|
|
||||||
mPickpocketDetected = true;
|
mPickpocketDetected = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -384,10 +383,9 @@ namespace MWGui
|
||||||
if (pickpocket.pick(item.mBase, count))
|
if (pickpocket.pick(item.mBase, count))
|
||||||
{
|
{
|
||||||
int value = item.mBase.getClass().getValue(item.mBase) * count;
|
int value = item.mBase.getClass().getValue(item.mBase) * count;
|
||||||
MWBase::Environment::get().getMechanicsManager()->reportCrime(
|
MWBase::Environment::get().getMechanicsManager()->commitCrime(
|
||||||
player, MWWorld::Ptr(), MWBase::MechanicsManager::OT_Theft, value);
|
player, MWWorld::Ptr(), MWBase::MechanicsManager::OT_Theft, value, true);
|
||||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
|
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
|
||||||
MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief");
|
|
||||||
mPickpocketDetected = true;
|
mPickpocketDetected = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -338,9 +338,8 @@ namespace MWGui
|
||||||
if (msg.find("%s") != std::string::npos)
|
if (msg.find("%s") != std::string::npos)
|
||||||
msg.replace(msg.find("%s"), 2, item.getClass().getName(item));
|
msg.replace(msg.find("%s"), 2, item.getClass().getName(item));
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox(msg);
|
MWBase::Environment::get().getWindowManager()->messageBox(msg);
|
||||||
MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief");
|
MWBase::Environment::get().getMechanicsManager()->commitCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
|
||||||
MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
|
item.getClass().getValue(item), true);
|
||||||
item.getClass().getValue(item));
|
|
||||||
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);
|
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);
|
||||||
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
|
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -290,10 +290,9 @@ namespace MWGui
|
||||||
if (msg.find("%s") != std::string::npos)
|
if (msg.find("%s") != std::string::npos)
|
||||||
msg.replace(msg.find("%s"), 2, it->mBase.getClass().getName(it->mBase));
|
msg.replace(msg.find("%s"), 2, it->mBase.getClass().getName(it->mBase));
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox(msg);
|
MWBase::Environment::get().getWindowManager()->messageBox(msg);
|
||||||
MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief");
|
MWBase::Environment::get().getMechanicsManager()->commitCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
|
||||||
MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
|
|
||||||
it->mBase.getClass().getValue(it->mBase)
|
it->mBase.getClass().getValue(it->mBase)
|
||||||
* it->mCount);
|
* it->mCount, true);
|
||||||
onCancelButtonClicked(mCancelButton);
|
onCancelButtonClicked(mCancelButton);
|
||||||
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
|
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -917,24 +917,19 @@ namespace MWMechanics
|
||||||
commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count);
|
commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MechanicsManager::commitCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg)
|
bool MechanicsManager::commitCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg, bool victimAware)
|
||||||
{
|
{
|
||||||
// NOTE: int arg can be from itemTaken() so DON'T modify it, since it is
|
|
||||||
// passed to reportCrime later on in this function.
|
|
||||||
|
|
||||||
// NOTE: victim may be empty
|
// NOTE: victim may be empty
|
||||||
|
|
||||||
// Only player can commit crime
|
// Only player can commit crime
|
||||||
if (player.getRefData().getHandle() != "player")
|
if (player.getRefData().getHandle() != "player")
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
|
||||||
|
|
||||||
|
|
||||||
// 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);
|
||||||
|
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
||||||
float radius = esmStore.get<ESM::GameSetting>().find("fAlarmRadius")->getFloat();
|
float radius = esmStore.get<ESM::GameSetting>().find("fAlarmRadius")->getFloat();
|
||||||
|
|
||||||
mActors.getObjectsInRange(from, radius, neighbors);
|
mActors.getObjectsInRange(from, radius, neighbors);
|
||||||
|
@ -943,11 +938,8 @@ 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);
|
||||||
|
|
||||||
bool victimAware = false;
|
// Did anyone see it?
|
||||||
|
|
||||||
// Find actors who directly witnessed the crime
|
|
||||||
bool crimeSeen = false;
|
bool crimeSeen = false;
|
||||||
bool reported = false;
|
|
||||||
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)
|
||||||
|
@ -955,17 +947,13 @@ namespace MWMechanics
|
||||||
if (it->getClass().getCreatureStats(*it).isDead())
|
if (it->getClass().getCreatureStats(*it).isDead())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Was the crime seen?
|
if ((*it == victim && victimAware)
|
||||||
if ((MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) )
|
|| (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).
|
// Murder crime can be reported even if no one saw it (hearing is enough, I guess).
|
||||||
// TODO: Add mod support for stealth executions!
|
// TODO: Add mod support for stealth executions!
|
||||||
|| (type == OT_Murder && *it != victim))
|
|| (type == OT_Murder && *it != victim))
|
||||||
{
|
{
|
||||||
if (*it == victim)
|
if (type == OT_Theft || type == OT_Pickpocket)
|
||||||
victimAware = true;
|
|
||||||
|
|
||||||
// TODO: are there other messages?
|
|
||||||
if (type == OT_Theft)
|
|
||||||
MWBase::Environment::get().getDialogueManager()->say(*it, "thief");
|
MWBase::Environment::get().getDialogueManager()->say(*it, "thief");
|
||||||
|
|
||||||
// Crime reporting only applies to NPCs
|
// Crime reporting only applies to NPCs
|
||||||
|
@ -977,20 +965,13 @@ namespace MWMechanics
|
||||||
|
|
||||||
crimeSeen = true;
|
crimeSeen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will the witness report the crime?
|
|
||||||
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= 100)
|
|
||||||
{
|
|
||||||
reported = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (crimeSeen && reported)
|
if (crimeSeen)
|
||||||
reportCrime(player, victim, type, arg);
|
reportCrime(player, victim, type, arg);
|
||||||
else if (victimAware && !victim.isEmpty() && type == OT_Assault)
|
else if (type == OT_Assault && !victim.isEmpty())
|
||||||
startCombat(victim, player);
|
startCombat(victim, player); // TODO: combat should be started with an "unaware" flag, which makes the victim flee?
|
||||||
|
return crimeSeen;
|
||||||
return reported;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MechanicsManager::reportCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg)
|
void MechanicsManager::reportCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg)
|
||||||
|
@ -1030,22 +1011,6 @@ namespace MWMechanics
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}");
|
|
||||||
player.getClass().getNpcStats(player).setBounty(player.getClass().getNpcStats(player).getBounty()
|
|
||||||
+ arg);
|
|
||||||
|
|
||||||
// If committing a crime against a faction member, expell from the faction
|
|
||||||
if (!victim.isEmpty() && victim.getClass().isNpc())
|
|
||||||
{
|
|
||||||
std::string factionID;
|
|
||||||
if(!victim.getClass().getNpcStats(victim).getFactionRanks().empty())
|
|
||||||
factionID = victim.getClass().getNpcStats(victim).getFactionRanks().begin()->first;
|
|
||||||
if (player.getClass().getNpcStats(player).isSameFaction(victim.getClass().getNpcStats(victim)))
|
|
||||||
{
|
|
||||||
player.getClass().getNpcStats(player).expell(factionID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make surrounding actors within alarm distance respond to the crime
|
// Make surrounding actors within alarm distance respond to the crime
|
||||||
std::vector<MWWorld::Ptr> neighbors;
|
std::vector<MWWorld::Ptr> neighbors;
|
||||||
|
|
||||||
|
@ -1082,6 +1047,8 @@ namespace MWMechanics
|
||||||
else if (type == OT_Theft)
|
else if (type == OT_Theft)
|
||||||
fight = fightVictim = esmStore.get<ESM::GameSetting>().find("fFightStealing")->getFloat();
|
fight = fightVictim = esmStore.get<ESM::GameSetting>().find("fFightStealing")->getFloat();
|
||||||
|
|
||||||
|
bool reported = false;
|
||||||
|
|
||||||
// Tell everyone (including the original reporter) in alarm range
|
// Tell everyone (including the original reporter) in alarm range
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
@ -1093,6 +1060,12 @@ namespace MWMechanics
|
||||||
if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim))
|
if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// Will the witness report the crime?
|
||||||
|
if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= 100)
|
||||||
|
{
|
||||||
|
reported = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (it->getClass().isClass(*it, "guard"))
|
if (it->getClass().isClass(*it, "guard"))
|
||||||
{
|
{
|
||||||
// Mark as Alarmed for dialogue
|
// Mark as Alarmed for dialogue
|
||||||
|
@ -1132,6 +1105,25 @@ namespace MWMechanics
|
||||||
observerStats.setBaseDisposition(originalDisposition);
|
observerStats.setBaseDisposition(originalDisposition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reported)
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}");
|
||||||
|
player.getClass().getNpcStats(player).setBounty(player.getClass().getNpcStats(player).getBounty()
|
||||||
|
+ arg);
|
||||||
|
|
||||||
|
// If committing a crime against a faction member, expell from the faction
|
||||||
|
if (!victim.isEmpty() && victim.getClass().isNpc())
|
||||||
|
{
|
||||||
|
std::string factionID;
|
||||||
|
if(!victim.getClass().getNpcStats(victim).getFactionRanks().empty())
|
||||||
|
factionID = victim.getClass().getNpcStats(victim).getFactionRanks().begin()->first;
|
||||||
|
if (player.getClass().getNpcStats(player).isSameFaction(victim.getClass().getNpcStats(victim)))
|
||||||
|
{
|
||||||
|
player.getClass().getNpcStats(player).expell(factionID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MechanicsManager::actorAttacked(const MWWorld::Ptr &ptr, const MWWorld::Ptr &attacker)
|
bool MechanicsManager::actorAttacked(const MWWorld::Ptr &ptr, const MWWorld::Ptr &attacker)
|
||||||
|
|
|
@ -110,16 +110,14 @@ namespace MWMechanics
|
||||||
virtual void startCombat (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target);
|
virtual void startCombat (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Commit a crime. If any actors witness the crime and report it,
|
|
||||||
* reportCrime will be called automatically.
|
|
||||||
* @note victim may be empty
|
* @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?
|
* @param victimAware Is the victim already aware of the crime?
|
||||||
|
* If this parameter is false, it will be determined by a line-of-sight and awareness check.
|
||||||
|
* @return was the crime seen?
|
||||||
*/
|
*/
|
||||||
virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
|
virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
|
||||||
OffenseType type, int arg=0);
|
OffenseType type, int arg=0, bool victimAware=false);
|
||||||
virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
|
|
||||||
OffenseType type, int arg=0);
|
|
||||||
/// @return false if the attack was considered a "friendly hit" and forgiven
|
/// @return false if the attack was considered a "friendly hit" and forgiven
|
||||||
virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker);
|
virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker);
|
||||||
/// Utility to check if taking this item is illegal and calling commitCrime if so
|
/// Utility to check if taking this item is illegal and calling commitCrime if so
|
||||||
|
@ -171,6 +169,10 @@ namespace MWMechanics
|
||||||
virtual void keepPlayerAlive();
|
virtual void keepPlayerAlive();
|
||||||
|
|
||||||
virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const;
|
virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
|
||||||
|
OffenseType type, int arg=0);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue