mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-20 17:23:53 +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:
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)
|
||||
};
|
||||
/**
|
||||
* @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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue