1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-20 21:23:52 +00:00

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:
scrawl 2014-12-19 21:45:26 +01:00
parent 307b84e9f6
commit 866fdfe8bd
6 changed files with 57 additions and 69 deletions

View file

@ -118,16 +118,14 @@ namespace MWBase
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
* @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,
OffenseType type, int arg=0) = 0;
virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
OffenseType type, int arg=0) = 0;
OffenseType type, int arg=0, bool victimAware=false) = 0;
/// @return false if the attack was considered a "friendly hit" and forgiven
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

View file

@ -301,10 +301,9 @@ namespace MWGui
MWMechanics::Pickpocket pickpocket(player, mPtr);
if (pickpocket.finish())
{
MWBase::Environment::get().getMechanicsManager()->reportCrime(
player, mPtr, MWBase::MechanicsManager::OT_Pickpocket);
MWBase::Environment::get().getMechanicsManager()->commitCrime(
player, mPtr, MWBase::MechanicsManager::OT_Pickpocket, 0, true);
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief");
mPickpocketDetected = true;
return;
}
@ -384,10 +383,9 @@ namespace MWGui
if (pickpocket.pick(item.mBase, count))
{
int value = item.mBase.getClass().getValue(item.mBase) * count;
MWBase::Environment::get().getMechanicsManager()->reportCrime(
player, MWWorld::Ptr(), MWBase::MechanicsManager::OT_Theft, value);
MWBase::Environment::get().getMechanicsManager()->commitCrime(
player, MWWorld::Ptr(), MWBase::MechanicsManager::OT_Theft, value, true);
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief");
mPickpocketDetected = true;
return false;
}

View file

@ -338,9 +338,8 @@ namespace MWGui
if (msg.find("%s") != std::string::npos)
msg.replace(msg.find("%s"), 2, item.getClass().getName(item));
MWBase::Environment::get().getWindowManager()->messageBox(msg);
MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief");
MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
item.getClass().getValue(item));
MWBase::Environment::get().getMechanicsManager()->commitCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
item.getClass().getValue(item), true);
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
return;

View file

@ -290,10 +290,9 @@ namespace MWGui
if (msg.find("%s") != std::string::npos)
msg.replace(msg.find("%s"), 2, it->mBase.getClass().getName(it->mBase));
MWBase::Environment::get().getWindowManager()->messageBox(msg);
MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief");
MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
MWBase::Environment::get().getMechanicsManager()->commitCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
it->mBase.getClass().getValue(it->mBase)
* it->mCount);
* it->mCount, true);
onCancelButtonClicked(mCancelButton);
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
return;

View file

@ -917,24 +917,19 @@ namespace MWMechanics
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
// Only player can commit crime
if (player.getRefData().getHandle() != "player")
return false;
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
// Find all the actors within the alarm radius
std::vector<MWWorld::Ptr> neighbors;
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();
mActors.getObjectsInRange(from, radius, neighbors);
@ -943,11 +938,8 @@ namespace MWMechanics
if (!victim.isEmpty() && from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius)
neighbors.push_back(victim);
bool victimAware = false;
// Find actors who directly witnessed the crime
// Did anyone see it?
bool crimeSeen = false;
bool reported = false;
for (std::vector<MWWorld::Ptr>::iterator it = neighbors.begin(); it != neighbors.end(); ++it)
{
if (*it == player)
@ -955,17 +947,13 @@ namespace MWMechanics
if (it->getClass().getCreatureStats(*it).isDead())
continue;
// Was the crime seen?
if ((MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) )
if ((*it == victim && victimAware)
|| (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).
// TODO: Add mod support for stealth executions!
|| (type == OT_Murder && *it != victim))
{
if (*it == victim)
victimAware = true;
// TODO: are there other messages?
if (type == OT_Theft)
if (type == OT_Theft || type == OT_Pickpocket)
MWBase::Environment::get().getDialogueManager()->say(*it, "thief");
// Crime reporting only applies to NPCs
@ -977,20 +965,13 @@ namespace MWMechanics
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);
else if (victimAware && !victim.isEmpty() && type == OT_Assault)
startCombat(victim, player);
return reported;
else if (type == OT_Assault && !victim.isEmpty())
startCombat(victim, player); // TODO: combat should be started with an "unaware" flag, which makes the victim flee?
return crimeSeen;
}
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
}
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
std::vector<MWWorld::Ptr> neighbors;
@ -1082,6 +1047,8 @@ namespace MWMechanics
else if (type == OT_Theft)
fight = fightVictim = esmStore.get<ESM::GameSetting>().find("fFightStealing")->getFloat();
bool reported = false;
// Tell everyone (including the original reporter) in alarm range
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))
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"))
{
// Mark as Alarmed for dialogue
@ -1132,6 +1105,25 @@ namespace MWMechanics
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)

View file

@ -110,16 +110,14 @@ namespace MWMechanics
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
* @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,
OffenseType type, int arg=0);
virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
OffenseType type, int arg=0);
OffenseType type, int arg=0, bool victimAware=false);
/// @return false if the attack was considered a "friendly hit" and forgiven
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
@ -171,6 +169,10 @@ namespace MWMechanics
virtual void keepPlayerAlive();
virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const;
private:
void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
OffenseType type, int arg=0);
};
}