mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-28 07:09:42 +00:00
Add OpenMW commits up to 5 Sep 2019
# Conflicts: # apps/openmw/mwgui/container.cpp # apps/openmw/mwmechanics/actors.cpp # apps/openmw/mwworld/worldimp.hpp
This commit is contained in:
commit
ca67587b89
37 changed files with 287 additions and 192 deletions
|
@ -86,6 +86,7 @@ Programmers
|
|||
Jacob Essex (Yacoby)
|
||||
Jake Westrip (16bitint)
|
||||
James Carty (MrTopCat)
|
||||
James Stephens (james-h-stephens)
|
||||
Jan-Peter Nilsson (peppe)
|
||||
Jan Borsodi (am0s)
|
||||
Jason Hooks (jhooks)
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
Bug #3894: Hostile spell effects not detected/present on first frame of OnPCHitMe
|
||||
Bug #4202: Open .omwaddon files without needing toopen openmw-cs first
|
||||
Bug #4240: Ash storm origin coordinates and hand shielding animation behavior are incorrect
|
||||
Bug #4270: Closing doors while they are obstructed desyncs closing sfx
|
||||
Bug #4276: Resizing character window differs from vanilla
|
||||
Bug #4329: Removed birthsign abilities are restored after reloading the save
|
||||
Bug #4341: Error message about missing GDB is too vague
|
||||
|
@ -27,6 +28,7 @@
|
|||
Bug #4540: Rain delay when exiting water
|
||||
Bug #4600: Crash when no sound output is available or --no-sound is used.
|
||||
Bug #4639: Black screen after completing first mages guild mission + training
|
||||
Bug #4650: Focus is lost after pressing ESC in confirmation dialog inside savegame dialog
|
||||
Bug #4701: PrisonMarker record is not hardcoded like other markers
|
||||
Bug #4703: Editor: it's possible to preview levelled list records
|
||||
Bug #4705: Editor: unable to open exterior cell views from Instances table
|
||||
|
@ -128,12 +130,13 @@
|
|||
Bug #5106: Still can jump even when encumbered
|
||||
Bug #5110: ModRegion with a redundant numerical argument breaks script execution
|
||||
Bug #5112: Insufficient magicka for current spell not reflected on HUD icon
|
||||
Bug #5113: Unknown alchemy question mark not centered
|
||||
Bug #5123: Script won't run on respawn
|
||||
Bug #5124: Arrow remains attached to actor if pulling animation was cancelled
|
||||
Bug #5126: Swimming creatures without RunForward animations are motionless during combat
|
||||
Bug #5134: Doors rotation by "Lock" console command is inconsistent
|
||||
Bug #5126: Swimming creatures without RunForward animations are motionless during combat
|
||||
Bug #5137: Textures with Clamp Mode set to Clamp instead of Wrap are too dark outside the boundaries
|
||||
Bug #5149: Failing lock pick attempts isn't always a crime
|
||||
Feature #1774: Handle AvoidNode
|
||||
Feature #2229: Improve pathfinding AI
|
||||
Feature #3025: Analogue gamepad movement controls
|
||||
|
@ -177,6 +180,8 @@
|
|||
Feature #5122: Use magic glow for enchanted arrows
|
||||
Feature #5131: Custom skeleton bones
|
||||
Feature #5132: Unique animations for different weapon types
|
||||
Feature #5146: Safe Dispose corpse
|
||||
Feature #5147: Show spell magicka cost in spell buying window
|
||||
Task #4686: Upgrade media decoder to a more current FFmpeg API
|
||||
Task #4695: Optimize Distant Terrain memory consumption
|
||||
Task #4789: Optimize cell transitions
|
||||
|
|
|
@ -153,8 +153,8 @@ namespace MWBase
|
|||
/// @param container The container the item is in; may be empty for an item in the world
|
||||
virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, const MWWorld::Ptr& container,
|
||||
int count, bool alarm = true) = 0;
|
||||
/// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so
|
||||
virtual void objectOpened (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) = 0;
|
||||
/// Utility to check if unlocking this object is illegal and calling commitCrime if so
|
||||
virtual void unlockAttempted (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) = 0;
|
||||
/// Attempt sleeping in a bed. If this is illegal, call commitCrime.
|
||||
/// @return was it illegal, and someone saw you doing it?
|
||||
virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed) = 0;
|
||||
|
@ -247,6 +247,8 @@ namespace MWBase
|
|||
|
||||
virtual float getActorsProcessingRange() const = 0;
|
||||
|
||||
virtual void notifyDied(const MWWorld::Ptr& actor) = 0;
|
||||
|
||||
virtual bool onOpen(const MWWorld::Ptr& ptr) = 0;
|
||||
virtual void onClose(const MWWorld::Ptr& ptr) = 0;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <components/esm/cellid.hpp>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
#include "../mwworld/doorstate.hpp"
|
||||
|
||||
#include "../mwrender/rendermode.hpp"
|
||||
|
||||
|
@ -571,14 +572,14 @@ namespace MWBase
|
|||
/// update movement state of a non-teleport door as specified
|
||||
/// @param state see MWClass::setDoorState
|
||||
/// @note throws an exception when invoked on a teleport door
|
||||
virtual void activateDoor(const MWWorld::Ptr& door, int state) = 0;
|
||||
virtual void activateDoor(const MWWorld::Ptr& door, MWWorld::DoorState state) = 0;
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Useful self-contained method for saving door states
|
||||
*/
|
||||
virtual void saveDoorState(const MWWorld::Ptr& door, int state) = 0;
|
||||
virtual void saveDoorState(const MWWorld::Ptr& door, MWWorld::DoorState state) = 0;
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace MWClass
|
|||
class DoorCustomData : public MWWorld::CustomData
|
||||
{
|
||||
public:
|
||||
int mDoorState; // 0 = nothing, 1 = opening, 2 = closing
|
||||
MWWorld::DoorState mDoorState;
|
||||
|
||||
virtual MWWorld::CustomData *clone() const;
|
||||
|
||||
|
@ -83,7 +83,7 @@ namespace MWClass
|
|||
if (ptr.getRefData().getCustomData())
|
||||
{
|
||||
const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();
|
||||
if (customData.mDoorState > 0)
|
||||
if (customData.mDoorState != MWWorld::DoorState::Idle)
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState);
|
||||
}
|
||||
|
@ -265,12 +265,12 @@ namespace MWClass
|
|||
{
|
||||
// animated door
|
||||
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionDoor(ptr));
|
||||
int doorstate = getDoorState(ptr);
|
||||
const auto doorState = getDoorState(ptr);
|
||||
bool opening = true;
|
||||
float doorRot = ptr.getRefData().getPosition().rot[2] - ptr.getCellRef().getPosition().rot[2];
|
||||
if (doorstate == 1)
|
||||
if (doorState == MWWorld::DoorState::Opening)
|
||||
opening = false;
|
||||
if (doorstate == 0 && doorRot != 0)
|
||||
if (doorState == MWWorld::DoorState::Idle && doorRot != 0)
|
||||
opening = false;
|
||||
|
||||
if (opening)
|
||||
|
@ -429,20 +429,20 @@ namespace MWClass
|
|||
{
|
||||
std::unique_ptr<DoorCustomData> data(new DoorCustomData);
|
||||
|
||||
data->mDoorState = 0;
|
||||
data->mDoorState = MWWorld::DoorState::Idle;
|
||||
ptr.getRefData().setCustomData(data.release());
|
||||
}
|
||||
}
|
||||
|
||||
int Door::getDoorState (const MWWorld::ConstPtr &ptr) const
|
||||
MWWorld::DoorState Door::getDoorState (const MWWorld::ConstPtr &ptr) const
|
||||
{
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
return 0;
|
||||
return MWWorld::DoorState::Idle;
|
||||
const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();
|
||||
return customData.mDoorState;
|
||||
}
|
||||
|
||||
void Door::setDoorState (const MWWorld::Ptr &ptr, int state) const
|
||||
void Door::setDoorState (const MWWorld::Ptr &ptr, MWWorld::DoorState state) const
|
||||
{
|
||||
if (ptr.getCellRef().getTeleport())
|
||||
throw std::runtime_error("load doors can't be moved");
|
||||
|
@ -460,7 +460,7 @@ namespace MWClass
|
|||
DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();
|
||||
|
||||
const ESM::DoorState& state2 = dynamic_cast<const ESM::DoorState&>(state);
|
||||
customData.mDoorState = state2.mDoorState;
|
||||
customData.mDoorState = static_cast<MWWorld::DoorState>(state2.mDoorState);
|
||||
}
|
||||
|
||||
void Door::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const
|
||||
|
@ -473,7 +473,7 @@ namespace MWClass
|
|||
const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();
|
||||
|
||||
ESM::DoorState& state2 = dynamic_cast<ESM::DoorState&>(state);
|
||||
state2.mDoorState = customData.mDoorState;
|
||||
state2.mDoorState = static_cast<int>(customData.mDoorState);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -59,10 +59,9 @@ namespace MWClass
|
|||
|
||||
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
|
||||
|
||||
/// 0 = nothing, 1 = opening, 2 = closing
|
||||
virtual int getDoorState (const MWWorld::ConstPtr &ptr) const;
|
||||
virtual MWWorld::DoorState getDoorState (const MWWorld::ConstPtr &ptr) const;
|
||||
/// This does not actually cause the door to move. Use World::activateDoor instead.
|
||||
virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const;
|
||||
virtual void setDoorState (const MWWorld::Ptr &ptr, MWWorld::DoorState state) const;
|
||||
|
||||
|
||||
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
|
||||
|
|
|
@ -40,14 +40,13 @@ namespace MWGui
|
|||
|
||||
bool ConfirmationDialog::exit()
|
||||
{
|
||||
setVisible(false);
|
||||
eventCancelClicked();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConfirmationDialog::onCancelButtonClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
setVisible(false);
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
|
|
|
@ -22,12 +22,15 @@
|
|||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/scriptmanager.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
#include "../mwscript/interpretercontext.hpp"
|
||||
|
||||
#include "countdialog.hpp"
|
||||
#include "inventorywindow.hpp"
|
||||
|
||||
|
@ -384,23 +387,46 @@ namespace MWGui
|
|||
|
||||
if (mPtr.getClass().isPersistent(mPtr))
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sDisposeCorpseFail}");
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
Instead of deleting the corpse on this client, simply send an ID_OBJECT_DELETE
|
||||
packet to the server as a request for the deletion
|
||||
*/
|
||||
else
|
||||
{
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
Instead of deleting the corpse on this client, increasing the death count and
|
||||
running the dead actor's sccript, simply send an ID_OBJECT_DELETE packet to the server
|
||||
as a request for the deletion
|
||||
*/
|
||||
|
||||
/*
|
||||
MWMechanics::CreatureStats& creatureStats = mPtr.getClass().getCreatureStats(mPtr);
|
||||
|
||||
// If we dispose corpse before end of death animation, we should update death counter counter manually.
|
||||
// Also we should run actor's script - it may react on actor's death.
|
||||
if (creatureStats.isDead() && !creatureStats.isDeathAnimationFinished())
|
||||
{
|
||||
creatureStats.setDeathAnimationFinished(true);
|
||||
MWBase::Environment::get().getMechanicsManager()->notifyDied(mPtr);
|
||||
|
||||
const std::string script = mPtr.getClass().getScript(mPtr);
|
||||
if (!script.empty() && MWBase::Environment::get().getWorld()->getScriptsEnabled())
|
||||
{
|
||||
MWScript::InterpreterContext interpreterContext (&mPtr.getRefData().getLocals(), mPtr);
|
||||
MWBase::Environment::get().getScriptManager()->run (script, interpreterContext);
|
||||
}
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getWorld()->deleteObject(mPtr);
|
||||
*/
|
||||
|
||||
mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();
|
||||
objectList->reset();
|
||||
objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;
|
||||
objectList->addObjectDelete(mPtr);
|
||||
objectList->sendObjectDelete();
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
}
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -81,6 +81,7 @@ namespace MWGui
|
|||
toAdd->eventMouseWheel += MyGUI::newDelegate(this, &SpellBuyingWindow::onMouseWheel);
|
||||
toAdd->setUserString("ToolTipType", "Spell");
|
||||
toAdd->setUserString("Spell", spell.mId);
|
||||
toAdd->setUserString("SpellCost", std::to_string(spell.mData.mCost));
|
||||
toAdd->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onSpellButtonClick);
|
||||
mSpellsWidgetMap.insert(std::make_pair (toAdd, spell.mId));
|
||||
}
|
||||
|
|
|
@ -249,6 +249,9 @@ namespace MWGui
|
|||
int school = MWMechanics::getSpellSchool(spell, player);
|
||||
info.text = "#{sSchool}: " + sSchoolNames[school];
|
||||
}
|
||||
std::string cost = focus->getUserString("SpellCost");
|
||||
if (cost != "" && cost != "0")
|
||||
info.text += MWGui::ToolTips::getValueString(spell->mData.mCost, "#{sCastCost}");
|
||||
info.effects = effects;
|
||||
tooltipSize = createToolTip(info);
|
||||
}
|
||||
|
|
|
@ -375,7 +375,8 @@ namespace MWGui
|
|||
if (!mEffectParams.mKnown)
|
||||
{
|
||||
mTextWidget->setCaption ("?");
|
||||
mRequestedWidth = mTextWidget->getTextSize().width + 24;
|
||||
mTextWidget->setCoord(sIconOffset / 2, mTextWidget->getCoord().top, mTextWidget->getCoord().width, mTextWidget->getCoord().height); // Compensates for the missing image when effect is not known
|
||||
mRequestedWidth = mTextWidget->getTextSize().width + sIconOffset;
|
||||
mImageWidget->setImageTexture ("");
|
||||
return;
|
||||
}
|
||||
|
@ -466,7 +467,7 @@ namespace MWGui
|
|||
}
|
||||
|
||||
mTextWidget->setCaptionWithReplacing(spellLine);
|
||||
mRequestedWidth = mTextWidget->getTextSize().width + 24;
|
||||
mRequestedWidth = mTextWidget->getTextSize().width + sIconOffset;
|
||||
|
||||
mImageWidget->setImageTexture(MWBase::Environment::get().getWindowManager()->correctIconPath(magicEffect->mIcon));
|
||||
}
|
||||
|
|
|
@ -268,7 +268,8 @@ namespace MWGui
|
|||
virtual void initialiseOverride();
|
||||
|
||||
private:
|
||||
|
||||
static const int sIconOffset = 24;
|
||||
|
||||
void updateWidgets();
|
||||
|
||||
SpellEffectParams mEffectParams;
|
||||
|
|
|
@ -215,6 +215,11 @@ namespace MWInput
|
|||
|
||||
void InputManager::handleGuiArrowKey(int action)
|
||||
{
|
||||
// This is currently keyboard-specific code
|
||||
// TODO: see if GUI controls can be refactored into a single function
|
||||
if (mJoystickLastUsed)
|
||||
return;
|
||||
|
||||
if (SDL_IsTextInputActive())
|
||||
return;
|
||||
|
||||
|
@ -242,13 +247,10 @@ namespace MWInput
|
|||
MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false);
|
||||
}
|
||||
|
||||
bool InputManager::gamepadToGuiControl(const SDL_ControllerButtonEvent &arg, bool release=false)
|
||||
bool InputManager::gamepadToGuiControl(const SDL_ControllerButtonEvent &arg)
|
||||
{
|
||||
// Presumption of GUI mode will be removed in the future.
|
||||
// MyGUI KeyCodes *may* change.
|
||||
// Currently button releases are ignored.
|
||||
if (release)
|
||||
return false;
|
||||
|
||||
MyGUI::KeyCode key = MyGUI::KeyCode::None;
|
||||
switch (arg.button)
|
||||
|
@ -399,9 +401,6 @@ namespace MWInput
|
|||
case A_GameMenu:
|
||||
toggleMainMenu ();
|
||||
break;
|
||||
case A_OptionsMenu:
|
||||
toggleOptionsMenu();
|
||||
break;
|
||||
case A_Screenshot:
|
||||
screenshot();
|
||||
break;
|
||||
|
@ -419,8 +418,7 @@ namespace MWInput
|
|||
case A_MoveRight:
|
||||
case A_MoveForward:
|
||||
case A_MoveBackward:
|
||||
// Temporary shut-down of this function until deemed necessary.
|
||||
//handleGuiArrowKey(action);
|
||||
handleGuiArrowKey(action);
|
||||
break;
|
||||
case A_Journal:
|
||||
toggleJournal ();
|
||||
|
@ -1025,9 +1023,9 @@ namespace MWInput
|
|||
mJoystickLastUsed = true;
|
||||
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
|
||||
{
|
||||
if (gamepadToGuiControl(arg, false))
|
||||
if (gamepadToGuiControl(arg))
|
||||
return;
|
||||
else if (mGamepadGuiCursorEnabled)
|
||||
if (mGamepadGuiCursorEnabled)
|
||||
{
|
||||
// Temporary mouse binding until keyboard controls are available:
|
||||
if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click.
|
||||
|
@ -1068,9 +1066,7 @@ namespace MWInput
|
|||
mJoystickLastUsed = true;
|
||||
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
|
||||
{
|
||||
if (gamepadToGuiControl(arg, true))
|
||||
return;
|
||||
else if (mGamepadGuiCursorEnabled)
|
||||
if (mGamepadGuiCursorEnabled)
|
||||
{
|
||||
// Temporary mouse binding until keyboard controls are available:
|
||||
if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click.
|
||||
|
@ -1169,37 +1165,19 @@ namespace MWInput
|
|||
}
|
||||
|
||||
if (MWBase::Environment::get().getWindowManager()->isConsoleMode())
|
||||
return;
|
||||
|
||||
bool inGame = MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_NoGame;
|
||||
MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode();
|
||||
|
||||
if ((inGame && mode == MWGui::GM_MainMenu) || mode == MWGui::GM_Settings)
|
||||
MWBase::Environment::get().getWindowManager()->popGuiMode();
|
||||
|
||||
if (inGame && mode != MWGui::GM_MainMenu)
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_MainMenu);
|
||||
}
|
||||
|
||||
void InputManager::toggleOptionsMenu()
|
||||
{
|
||||
if (MyGUI::InputManager::getInstance().isModalAny())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->exitCurrentModal();
|
||||
MWBase::Environment::get().getWindowManager()->toggleConsole();
|
||||
return;
|
||||
}
|
||||
|
||||
if (MWBase::Environment::get().getWindowManager()->isConsoleMode())
|
||||
return;
|
||||
|
||||
MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode();
|
||||
bool inGame = MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_NoGame;
|
||||
|
||||
if ((inGame && mode == MWGui::GM_MainMenu) || mode == MWGui::GM_Settings)
|
||||
MWBase::Environment::get().getWindowManager()->popGuiMode();
|
||||
|
||||
if (inGame && mode != MWGui::GM_Settings)
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Settings);
|
||||
if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) //No open GUIs, open up the MainMenu
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
|
||||
}
|
||||
else //Close current GUI
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::quickLoad() {
|
||||
|
@ -1611,7 +1589,6 @@ namespace MWInput
|
|||
defaultButtonBindings[A_TogglePOV] = SDL_CONTROLLER_BUTTON_RIGHTSTICK;
|
||||
defaultButtonBindings[A_Inventory] = SDL_CONTROLLER_BUTTON_B;
|
||||
defaultButtonBindings[A_GameMenu] = SDL_CONTROLLER_BUTTON_START;
|
||||
defaultButtonBindings[A_OptionsMenu] = SDL_CONTROLLER_BUTTON_BACK;
|
||||
defaultButtonBindings[A_QuickSave] = SDL_CONTROLLER_BUTTON_GUIDE;
|
||||
defaultButtonBindings[A_MoveForward] = SDL_CONTROLLER_BUTTON_DPAD_UP;
|
||||
defaultButtonBindings[A_MoveLeft] = SDL_CONTROLLER_BUTTON_DPAD_LEFT;
|
||||
|
@ -1692,7 +1669,6 @@ namespace MWInput
|
|||
descriptions[A_Journal] = "sJournal";
|
||||
descriptions[A_Rest] = "sRestKey";
|
||||
descriptions[A_Inventory] = "sInventory";
|
||||
descriptions[A_OptionsMenu] = "sPreferences";
|
||||
descriptions[A_TogglePOV] = "sTogglePOVCmd";
|
||||
descriptions[A_QuickKeysMenu] = "sQuickMenu";
|
||||
descriptions[A_QuickKey1] = "sQuick1Cmd";
|
||||
|
@ -1830,7 +1806,6 @@ namespace MWInput
|
|||
ret.push_back(A_Inventory);
|
||||
ret.push_back(A_Journal);
|
||||
ret.push_back(A_Rest);
|
||||
ret.push_back(A_OptionsMenu);
|
||||
ret.push_back(A_Console);
|
||||
ret.push_back(A_QuickSave);
|
||||
ret.push_back(A_QuickLoad);
|
||||
|
@ -1863,7 +1838,6 @@ namespace MWInput
|
|||
ret.push_back(A_Inventory);
|
||||
ret.push_back(A_Journal);
|
||||
ret.push_back(A_Rest);
|
||||
ret.push_back(A_OptionsMenu);
|
||||
ret.push_back(A_QuickSave);
|
||||
ret.push_back(A_QuickLoad);
|
||||
ret.push_back(A_Screenshot);
|
||||
|
|
|
@ -226,7 +226,7 @@ namespace MWInput
|
|||
void setPlayerControlsEnabled(bool enabled);
|
||||
void handleGuiArrowKey(int action);
|
||||
// Return true if GUI consumes input.
|
||||
bool gamepadToGuiControl(const SDL_ControllerButtonEvent &arg, bool release);
|
||||
bool gamepadToGuiControl(const SDL_ControllerButtonEvent &arg);
|
||||
bool gamepadToGuiControl(const SDL_ControllerAxisEvent &arg);
|
||||
|
||||
void updateCursorMode();
|
||||
|
@ -235,7 +235,6 @@ namespace MWInput
|
|||
|
||||
private:
|
||||
void toggleMainMenu();
|
||||
void toggleOptionsMenu();
|
||||
void toggleSpell();
|
||||
void toggleWeapon();
|
||||
void toggleInventory();
|
||||
|
@ -328,8 +327,6 @@ namespace MWInput
|
|||
A_MoveForwardBackward,
|
||||
A_MoveLeftRight,
|
||||
|
||||
A_OptionsMenu,
|
||||
|
||||
A_Last // Marker for the last item
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1830,6 +1830,43 @@ namespace MWMechanics
|
|||
updateCombatMusic();
|
||||
}
|
||||
|
||||
void Actors::notifyDied(const MWWorld::Ptr &actor)
|
||||
{
|
||||
actor.getClass().getCreatureStats(actor).notifyDied();
|
||||
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
Only increment death count for an actor if we are its authority, to avoid
|
||||
situations where we increment it locally after having already received an
|
||||
ID_WORLD_KILL_COUNT packet about it
|
||||
*/
|
||||
bool isLocalActor = mwmp::Main::get().getCellController()->isLocalActor(actor);
|
||||
|
||||
if (isLocalActor)
|
||||
++mDeathCount[Misc::StringUtils::lowerCase(actor.getCellRef().getRefId())];
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Send an ID_WORLD_KILL_COUNT packet every time the kill count changes,
|
||||
as long as we are the authority over the actor's cell
|
||||
*/
|
||||
if (isLocalActor)
|
||||
{
|
||||
std::string refId = Misc::StringUtils::lowerCase(actor.getCellRef().getRefId());
|
||||
int number = mDeathCount[refId];
|
||||
|
||||
mwmp::Main::get().getLocalPlayer()->sendKill(refId, number);
|
||||
}
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
}
|
||||
|
||||
void Actors::killDeadActors()
|
||||
{
|
||||
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
||||
|
@ -1873,39 +1910,7 @@ namespace MWMechanics
|
|||
}
|
||||
else if (killResult == CharacterController::Result_DeathAnimJustFinished)
|
||||
{
|
||||
iter->first.getClass().getCreatureStats(iter->first).notifyDied();
|
||||
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
Only increment death count for an actor if we are its authority, to avoid
|
||||
situations where we increment it locally after having already received an
|
||||
ID_WORLD_KILL_COUNT packet about it
|
||||
*/
|
||||
bool isLocalActor = mwmp::Main::get().getCellController()->isLocalActor(iter->first);
|
||||
|
||||
if (isLocalActor)
|
||||
++mDeathCount[Misc::StringUtils::lowerCase(iter->first.getCellRef().getRefId())];
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Send an ID_WORLD_KILL_COUNT packet every time the kill count changes,
|
||||
as long as we are the authority over the actor's cell
|
||||
*/
|
||||
if (isLocalActor)
|
||||
{
|
||||
std::string refId = Misc::StringUtils::lowerCase(iter->first.getCellRef().getRefId());
|
||||
int number = mDeathCount[refId];
|
||||
|
||||
mwmp::Main::get().getLocalPlayer()->sendKill(refId, number);
|
||||
}
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
notifyDied(iter->first);
|
||||
|
||||
// Reset magic effects and recalculate derived effects
|
||||
// One case where we need this is to make sure bound items are removed upon death
|
||||
|
|
|
@ -71,6 +71,8 @@ namespace MWMechanics
|
|||
PtrActorMap::const_iterator begin() { return mActors.begin(); }
|
||||
PtrActorMap::const_iterator end() { return mActors.end(); }
|
||||
|
||||
void notifyDied(const MWWorld::Ptr &actor);
|
||||
|
||||
/// Check if the target actor was detected by an observer
|
||||
/// If the observer is a non-NPC, check all actors in AI processing distance as observers
|
||||
bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer);
|
||||
|
|
|
@ -44,7 +44,7 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont
|
|||
return true; // We have tried backing up for more than one second, we've probably cleared it
|
||||
}
|
||||
|
||||
if (!mDoorPtr.getClass().getDoorState(mDoorPtr))
|
||||
if (mDoorPtr.getClass().getDoorState(mDoorPtr) == MWWorld::DoorState::Idle)
|
||||
return true; //Door is no longer opening
|
||||
|
||||
ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door
|
||||
|
|
|
@ -232,7 +232,7 @@ void MWMechanics::AiPackage::openDoors(const MWWorld::Ptr& actor)
|
|||
return;
|
||||
|
||||
// note: AiWander currently does not open doors
|
||||
if (getTypeId() != TypeIdWander && !door.getCellRef().getTeleport() && door.getClass().getDoorState(door) == 0)
|
||||
if (getTypeId() != TypeIdWander && !door.getCellRef().getTeleport() && door.getClass().getDoorState(door) == MWWorld::DoorState::Idle)
|
||||
{
|
||||
if ((door.getCellRef().getTrap().empty() && door.getCellRef().getLockLevel() <= 0 ))
|
||||
{
|
||||
|
|
|
@ -2721,11 +2721,15 @@ void CharacterController::updateMagicEffects()
|
|||
if (!mPtr.getClass().isActor())
|
||||
return;
|
||||
|
||||
bool vampire = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).getMagnitude() > 0.0f;
|
||||
mAnimation->setVampire(vampire);
|
||||
|
||||
float light = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Light).getMagnitude();
|
||||
mAnimation->setLightEffect(light);
|
||||
|
||||
// If you're dead you don't care about whether you've started/stopped being a vampire or not
|
||||
if (mPtr.getClass().getCreatureStats(mPtr).isDead())
|
||||
return;
|
||||
|
||||
bool vampire = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).getMagnitude() > 0.0f;
|
||||
mAnimation->setVampire(vampire);
|
||||
}
|
||||
|
||||
void CharacterController::setVisibility(float visibility)
|
||||
|
|
|
@ -468,6 +468,11 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
void MechanicsManager::notifyDied(const MWWorld::Ptr& actor)
|
||||
{
|
||||
mActors.notifyDied(actor);
|
||||
}
|
||||
|
||||
float MechanicsManager::getActorsProcessingRange() const
|
||||
{
|
||||
return mActors.getProcessingRange();
|
||||
|
@ -1091,7 +1096,7 @@ namespace MWMechanics
|
|||
return false;
|
||||
}
|
||||
|
||||
void MechanicsManager::objectOpened(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item)
|
||||
void MechanicsManager::unlockAttempted(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item)
|
||||
{
|
||||
MWWorld::Ptr victim;
|
||||
if (isAllowedToUse(ptr, item, victim))
|
||||
|
|
|
@ -152,8 +152,8 @@ namespace MWMechanics
|
|||
/// @param container The container the item is in; may be empty for an item in the world
|
||||
virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, const MWWorld::Ptr& container,
|
||||
int count, bool alarm = true) override;
|
||||
/// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so
|
||||
virtual void objectOpened (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) override;
|
||||
/// Utility to check if unlocking this object is illegal and calling commitCrime if so
|
||||
virtual void unlockAttempted (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) override;
|
||||
/// Attempt sleeping in a bed. If this is illegal, call commitCrime.
|
||||
/// @return was it illegal, and someone saw you doing it? Also returns fail when enemies are nearby
|
||||
virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed) override;
|
||||
|
@ -221,6 +221,8 @@ namespace MWMechanics
|
|||
|
||||
virtual float getActorsProcessingRange() const override;
|
||||
|
||||
virtual void notifyDied(const MWWorld::Ptr& actor) override;
|
||||
|
||||
/// Check if the target actor was detected by an observer
|
||||
/// If the observer is a non-NPC, check all actors in AI processing distance as observers
|
||||
virtual bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer) override;
|
||||
|
|
|
@ -52,10 +52,10 @@ namespace MWMechanics
|
|||
// FIXME: cast
|
||||
const MWWorld::Ptr doorPtr = MWWorld::Ptr(&const_cast<MWWorld::LiveCellRef<ESM::Door> &>(ref), actor.getCell());
|
||||
|
||||
int doorState = doorPtr.getClass().getDoorState(doorPtr);
|
||||
const auto doorState = doorPtr.getClass().getDoorState(doorPtr);
|
||||
float doorRot = ref.mData.getPosition().rot[2] - doorPtr.getCellRef().getPosition().rot[2];
|
||||
|
||||
if (doorState != 0 || doorRot != 0)
|
||||
if (doorState != MWWorld::DoorState::Idle || doorRot != 0)
|
||||
continue; // the door is already opened/opening
|
||||
|
||||
doorPos.z() = 0;
|
||||
|
|
|
@ -62,7 +62,6 @@ namespace MWMechanics
|
|||
resultMessage = "#{sLockImpossible}";
|
||||
else
|
||||
{
|
||||
MWBase::Environment::get().getMechanicsManager()->objectOpened(mActor, lock);
|
||||
if (Misc::Rng::roll0to99() <= x)
|
||||
{
|
||||
/*
|
||||
|
@ -98,6 +97,7 @@ namespace MWMechanics
|
|||
resultMessage = "#{sLockFail}";
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getMechanicsManager()->unlockAttempted(mActor, lock);
|
||||
int uses = lockpick.getClass().getItemHealth(lockpick);
|
||||
--uses;
|
||||
lockpick.getCellRef().setCharge(uses);
|
||||
|
@ -127,7 +127,6 @@ namespace MWMechanics
|
|||
resultMessage = "#{sTrapImpossible}";
|
||||
else
|
||||
{
|
||||
MWBase::Environment::get().getMechanicsManager()->objectOpened(mActor, trap);
|
||||
if (Misc::Rng::roll0to99() <= x)
|
||||
{
|
||||
/*
|
||||
|
@ -163,6 +162,7 @@ namespace MWMechanics
|
|||
resultMessage = "#{sTrapFail}";
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getMechanicsManager()->unlockAttempted(mActor, trap);
|
||||
int uses = probe.getClass().getItemHealth(probe);
|
||||
--uses;
|
||||
probe.getCellRef().setCharge(uses);
|
||||
|
|
|
@ -790,9 +790,6 @@ namespace MWMechanics
|
|||
if (target.getCellRef().getLockLevel() > 0)
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock", 1.f, 1.f);
|
||||
if (!caster.isEmpty())
|
||||
MWBase::Environment::get().getMechanicsManager()->objectOpened(getPlayer(), target);
|
||||
// Use the player instead of the caster for vanilla crime compatibility
|
||||
|
||||
if (caster == getPlayer())
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicOpenSuccess}");
|
||||
|
@ -825,6 +822,10 @@ namespace MWMechanics
|
|||
}
|
||||
else
|
||||
MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock Fail", 1.f, 1.f);
|
||||
|
||||
if (!caster.isEmpty())
|
||||
MWBase::Environment::get().getMechanicsManager()->unlockAttempted(getPlayer(), target);
|
||||
// Use the player instead of the caster for vanilla crime compatibility
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/scriptmanager.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
|
@ -26,6 +27,8 @@
|
|||
|
||||
#include "../mwrender/animation.hpp"
|
||||
|
||||
#include "../mwscript/interpretercontext.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/containerstore.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
@ -483,6 +486,28 @@ void ObjectList::deleteObjects(MWWorld::CellStore* cellStore)
|
|||
}
|
||||
}
|
||||
|
||||
// Is this a dying actor being deleted before its death animation has finished? If so,
|
||||
// increase the death count for the actor if applicable and run the actor's script,
|
||||
// which is the same as what happens in OpenMW's ContainerWindow::onDisposeCorpseButtonClicked()
|
||||
// if an actor's corpse is disposed of before its death animation is finished
|
||||
if (ptrFound.getClass().isActor())
|
||||
{
|
||||
MWMechanics::CreatureStats& creatureStats = ptrFound.getClass().getCreatureStats(ptrFound);
|
||||
|
||||
if (creatureStats.isDead() && !creatureStats.isDeathAnimationFinished())
|
||||
{
|
||||
creatureStats.setDeathAnimationFinished(true);
|
||||
MWBase::Environment::get().getMechanicsManager()->notifyDied(ptrFound);
|
||||
|
||||
const std::string script = ptrFound.getClass().getScript(ptrFound);
|
||||
if (!script.empty() && MWBase::Environment::get().getWorld()->getScriptsEnabled())
|
||||
{
|
||||
MWScript::InterpreterContext interpreterContext(&ptrFound.getRefData().getLocals(), ptrFound);
|
||||
MWBase::Environment::get().getScriptManager()->run(script, interpreterContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getWorld()->deleteObject(ptrFound);
|
||||
}
|
||||
}
|
||||
|
@ -646,8 +671,10 @@ void ObjectList::activateDoors(MWWorld::CellStore* cellStore)
|
|||
LOG_APPEND(TimedLog::LOG_VERBOSE, "-- Found %s %i-%i", ptrFound.getCellRef().getRefId().c_str(),
|
||||
ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());
|
||||
|
||||
ptrFound.getClass().setDoorState(ptrFound, baseObject.doorState);
|
||||
MWBase::Environment::get().getWorld()->saveDoorState(ptrFound, baseObject.doorState);
|
||||
MWWorld::DoorState doorState = static_cast<MWWorld::DoorState>(baseObject.doorState);
|
||||
|
||||
ptrFound.getClass().setDoorState(ptrFound, doorState);
|
||||
MWBase::Environment::get().getWorld()->saveDoorState(ptrFound, doorState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1054,7 +1081,7 @@ void ObjectList::addObjectAnimPlay(const MWWorld::Ptr& ptr, std::string group, i
|
|||
addObject(baseObject);
|
||||
}
|
||||
|
||||
void ObjectList::addDoorState(const MWWorld::Ptr& ptr, int state)
|
||||
void ObjectList::addDoorState(const MWWorld::Ptr& ptr, MWWorld::DoorState state)
|
||||
{
|
||||
cell = *ptr.getCell()->getCell();
|
||||
|
||||
|
@ -1062,7 +1089,7 @@ void ObjectList::addDoorState(const MWWorld::Ptr& ptr, int state)
|
|||
baseObject.refId = ptr.getCellRef().getRefId();
|
||||
baseObject.refNum = ptr.getCellRef().getRefNum().mIndex;
|
||||
baseObject.mpNum = ptr.getCellRef().getMpNum();
|
||||
baseObject.doorState = state;
|
||||
baseObject.doorState = static_cast<int>(state);
|
||||
addObject(baseObject);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#define OPENMW_OBJECTLIST_HPP
|
||||
|
||||
#include <components/openmw-mp/Base/BaseObject.hpp>
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
#include "../mwworld/worldimp.hpp"
|
||||
#include <RakNetTypes.h>
|
||||
|
||||
namespace mwmp
|
||||
|
@ -61,7 +61,7 @@ namespace mwmp
|
|||
void addObjectState(const MWWorld::Ptr& ptr, bool objectState);
|
||||
void addObjectAnimPlay(const MWWorld::Ptr& ptr, std::string group, int mode);
|
||||
|
||||
void addDoorState(const MWWorld::Ptr& ptr, int state);
|
||||
void addDoorState(const MWWorld::Ptr& ptr, MWWorld::DoorState state);
|
||||
void addMusicPlay(std::string filename);
|
||||
void addVideoPlay(std::string filename, bool allowSkipping);
|
||||
void addScriptLocalShort(const MWWorld::Ptr& ptr, int index, int shortVal);
|
||||
|
|
|
@ -64,7 +64,7 @@ ActorAnimation::~ActorAnimation()
|
|||
mScabbard.reset();
|
||||
}
|
||||
|
||||
PartHolderPtr ActorAnimation::getWeaponPart(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor)
|
||||
PartHolderPtr ActorAnimation::attachMesh(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor)
|
||||
{
|
||||
osg::Group* parent = getBoneByName(bonename);
|
||||
if (!parent)
|
||||
|
@ -160,7 +160,7 @@ void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons)
|
|||
if (showHolsteredWeapons)
|
||||
{
|
||||
osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon);
|
||||
mScabbard = getWeaponPart(mesh, boneName, isEnchanted, &glowColor);
|
||||
mScabbard = attachMesh(mesh, boneName, isEnchanted, &glowColor);
|
||||
if (mScabbard)
|
||||
resetControllers(mScabbard->getNode());
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons)
|
|||
return;
|
||||
}
|
||||
|
||||
mScabbard = getWeaponPart(scabbardName, boneName);
|
||||
mScabbard = attachMesh(scabbardName, boneName);
|
||||
|
||||
osg::Group* weaponNode = getBoneByName("Bip01 Weapon");
|
||||
if (!weaponNode)
|
||||
|
|
|
@ -44,11 +44,11 @@ class ActorAnimation : public Animation, public MWWorld::ContainerStoreListener
|
|||
virtual void updateHolsteredWeapon(bool showHolsteredWeapons);
|
||||
virtual void updateQuiver();
|
||||
virtual std::string getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon);
|
||||
virtual PartHolderPtr getWeaponPart(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor);
|
||||
virtual PartHolderPtr getWeaponPart(const std::string& model, const std::string& bonename)
|
||||
virtual PartHolderPtr attachMesh(const std::string& model, const std::string& bonename, bool enchantedGlow, osg::Vec4f* glowColor);
|
||||
virtual PartHolderPtr attachMesh(const std::string& model, const std::string& bonename)
|
||||
{
|
||||
osg::Vec4f stubColor = osg::Vec4f(0,0,0,0);
|
||||
return getWeaponPart(model, bonename, false, &stubColor);
|
||||
return attachMesh(model, bonename, false, &stubColor);
|
||||
};
|
||||
|
||||
PartHolderPtr mScabbard;
|
||||
|
|
|
@ -247,7 +247,7 @@ namespace MWScript
|
|||
// This is done when using Lock in scripts, but not when using Lock spells.
|
||||
if (ptr.getTypeName() == typeid(ESM::Door).name() && !ptr.getCellRef().getTeleport())
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->activateDoor(ptr, 0);
|
||||
MWBase::Environment::get().getWorld()->activateDoor(ptr, MWWorld::DoorState::Idle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -473,12 +473,12 @@ namespace MWWorld
|
|||
return false;
|
||||
}
|
||||
|
||||
int Class::getDoorState (const MWWorld::ConstPtr &ptr) const
|
||||
MWWorld::DoorState Class::getDoorState (const MWWorld::ConstPtr &ptr) const
|
||||
{
|
||||
throw std::runtime_error("this is not a door");
|
||||
}
|
||||
|
||||
void Class::setDoorState (const MWWorld::Ptr &ptr, int state) const
|
||||
void Class::setDoorState (const MWWorld::Ptr &ptr, MWWorld::DoorState state) const
|
||||
{
|
||||
throw std::runtime_error("this is not a door");
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <osg/Vec4f>
|
||||
|
||||
#include "ptr.hpp"
|
||||
#include "doorstate.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
@ -309,7 +310,7 @@ namespace MWWorld
|
|||
|
||||
virtual bool allowTelekinesis(const MWWorld::ConstPtr& ptr) const { return true; }
|
||||
///< Return whether this class of object can be activated with telekinesis
|
||||
|
||||
|
||||
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
|
||||
virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const;
|
||||
|
||||
|
@ -361,10 +362,9 @@ namespace MWWorld
|
|||
|
||||
virtual bool isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const;
|
||||
|
||||
/// 0 = nothing, 1 = opening, 2 = closing
|
||||
virtual int getDoorState (const MWWorld::ConstPtr &ptr) const;
|
||||
virtual DoorState getDoorState (const MWWorld::ConstPtr &ptr) const;
|
||||
/// This does not actually cause the door to move. Use World::activateDoor instead.
|
||||
virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const;
|
||||
virtual void setDoorState (const MWWorld::Ptr &ptr, DoorState state) const;
|
||||
|
||||
virtual void respawn (const MWWorld::Ptr& ptr) const {}
|
||||
|
||||
|
|
14
apps/openmw/mwworld/doorstate.hpp
Normal file
14
apps/openmw/mwworld/doorstate.hpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef GAME_MWWORLD_DOORSTATE_H
|
||||
#define GAME_MWWORLD_DOORSTATE_H
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
enum class DoorState
|
||||
{
|
||||
Idle = 0,
|
||||
Opening = 1,
|
||||
Closing = 2,
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1847,7 +1847,7 @@ namespace MWWorld
|
|||
return result.mHit;
|
||||
}
|
||||
|
||||
bool World::rotateDoor(const Ptr door, int state, float duration)
|
||||
bool World::rotateDoor(const Ptr door, MWWorld::DoorState state, float duration)
|
||||
{
|
||||
const ESM::Position& objPos = door.getRefData().getPosition();
|
||||
float oldRot = objPos.rot[2];
|
||||
|
@ -1856,17 +1856,20 @@ namespace MWWorld
|
|||
float maxRot = minRot + osg::DegreesToRadians(90.f);
|
||||
|
||||
float diff = duration * osg::DegreesToRadians(90.f);
|
||||
float targetRot = std::min(std::max(minRot, oldRot + diff * (state == 1 ? 1 : -1)), maxRot);
|
||||
float targetRot = std::min(std::max(minRot, oldRot + diff * (state == MWWorld::DoorState::Opening ? 1 : -1)), maxRot);
|
||||
rotateObject(door, objPos.rot[0], objPos.rot[1], targetRot);
|
||||
|
||||
bool reached = (targetRot == maxRot && state) || targetRot == minRot;
|
||||
bool reached = (targetRot == maxRot && state != MWWorld::DoorState::Idle) || targetRot == minRot;
|
||||
|
||||
/// \todo should use convexSweepTest here
|
||||
bool collisionWithActor = false;
|
||||
std::vector<MWWorld::Ptr> collisions = mPhysics->getCollisions(door, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor);
|
||||
for (MWWorld::Ptr& ptr : collisions)
|
||||
{
|
||||
if (ptr.getClass().isActor())
|
||||
{
|
||||
collisionWithActor = true;
|
||||
|
||||
// Collided with actor, ask actor to try to avoid door
|
||||
if(ptr != getPlayerPtr() )
|
||||
{
|
||||
|
@ -1881,6 +1884,25 @@ namespace MWWorld
|
|||
}
|
||||
}
|
||||
|
||||
// Cancel door closing sound if collision with actor is detected
|
||||
if (collisionWithActor)
|
||||
{
|
||||
const ESM::Door* ref = door.get<ESM::Door>()->mBase;
|
||||
|
||||
if (state == MWWorld::DoorState::Opening)
|
||||
{
|
||||
const std::string& openSound = ref->mOpenSound;
|
||||
if (!openSound.empty() && MWBase::Environment::get().getSoundManager()->getSoundPlaying(door, openSound))
|
||||
MWBase::Environment::get().getSoundManager()->stopSound3D(door, openSound);
|
||||
}
|
||||
else if (state == MWWorld::DoorState::Closing)
|
||||
{
|
||||
const std::string& closeSound = ref->mCloseSound;
|
||||
if (!closeSound.empty() && MWBase::Environment::get().getSoundManager()->getSoundPlaying(door, closeSound))
|
||||
MWBase::Environment::get().getSoundManager()->stopSound3D(door, closeSound);
|
||||
}
|
||||
}
|
||||
|
||||
// the rotation order we want to use
|
||||
mWorldScene->updateObjectRotation(door, false);
|
||||
return reached;
|
||||
|
@ -1888,7 +1910,7 @@ namespace MWWorld
|
|||
|
||||
void World::processDoors(float duration)
|
||||
{
|
||||
std::map<MWWorld::Ptr, int>::iterator it = mDoorStates.begin();
|
||||
auto it = mDoorStates.begin();
|
||||
while (it != mDoorStates.end())
|
||||
{
|
||||
if (!mWorldScene->isCellActive(*it->first.getCell()) || !it->first.getRefData().getBaseNode())
|
||||
|
@ -1905,7 +1927,7 @@ namespace MWWorld
|
|||
if (reached)
|
||||
{
|
||||
// Mark as non-moving
|
||||
it->first.getClass().setDoorState(it->first, 0);
|
||||
it->first.getClass().setDoorState(it->first, MWWorld::DoorState::Idle);
|
||||
mDoorStates.erase(it++);
|
||||
}
|
||||
else
|
||||
|
@ -2802,21 +2824,21 @@ namespace MWWorld
|
|||
|
||||
void World::activateDoor(const MWWorld::Ptr& door)
|
||||
{
|
||||
int state = door.getClass().getDoorState(door);
|
||||
auto state = door.getClass().getDoorState(door);
|
||||
switch (state)
|
||||
{
|
||||
case 0:
|
||||
case MWWorld::DoorState::Idle:
|
||||
if (door.getRefData().getPosition().rot[2] == door.getCellRef().getPosition().rot[2])
|
||||
state = 1; // if closed, then open
|
||||
state = MWWorld::DoorState::Opening; // if closed, then open
|
||||
else
|
||||
state = 2; // if open, then close
|
||||
state = MWWorld::DoorState::Closing; // if open, then close
|
||||
break;
|
||||
case 2:
|
||||
state = 1; // if closing, then open
|
||||
case MWWorld::DoorState::Closing:
|
||||
state = MWWorld::DoorState::Opening; // if closing, then open
|
||||
break;
|
||||
case 1:
|
||||
case MWWorld::DoorState::Opening:
|
||||
default:
|
||||
state = 2; // if opening, then close
|
||||
state = MWWorld::DoorState::Closing; // if opening, then close
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2838,7 +2860,7 @@ namespace MWWorld
|
|||
mDoorStates[door] = state;
|
||||
}
|
||||
|
||||
void World::activateDoor(const Ptr &door, int state)
|
||||
void World::activateDoor(const Ptr &door, MWWorld::DoorState state)
|
||||
{
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
@ -2856,7 +2878,7 @@ namespace MWWorld
|
|||
|
||||
door.getClass().setDoorState(door, state);
|
||||
mDoorStates[door] = state;
|
||||
if (state == 0)
|
||||
if (state == MWWorld::DoorState::Idle)
|
||||
{
|
||||
mDoorStates.erase(door);
|
||||
rotateDoor(door, state, 1);
|
||||
|
@ -2868,10 +2890,10 @@ namespace MWWorld
|
|||
|
||||
Allow the saving of door states without going through World::activateDoor()
|
||||
*/
|
||||
void World::saveDoorState(const Ptr &door, int state)
|
||||
void World::saveDoorState(const Ptr &door, MWWorld::DoorState state)
|
||||
{
|
||||
mDoorStates[door] = state;
|
||||
if (state == 0)
|
||||
if (state == MWWorld::DoorState::Idle)
|
||||
mDoorStates.erase(door);
|
||||
}
|
||||
/*
|
||||
|
|
|
@ -118,7 +118,7 @@ namespace MWWorld
|
|||
|
||||
int mActivationDistanceOverride;
|
||||
|
||||
std::map<MWWorld::Ptr, int> mDoorStates;
|
||||
std::map<MWWorld::Ptr, MWWorld::DoorState> mDoorStates;
|
||||
///< only holds doors that are currently moving. 1 = opening, 2 = closing
|
||||
|
||||
std::string mStartCell;
|
||||
|
@ -155,7 +155,7 @@ namespace MWWorld
|
|||
void addContainerScripts(const Ptr& reference, CellStore* cell) override;
|
||||
void removeContainerScripts(const Ptr& reference) override;
|
||||
private:
|
||||
bool rotateDoor(const Ptr door, int state, float duration);
|
||||
bool rotateDoor(const Ptr door, DoorState state, float duration);
|
||||
|
||||
void processDoors(float duration);
|
||||
///< Run physics simulation and modify \a world accordingly.
|
||||
|
@ -680,14 +680,14 @@ namespace MWWorld
|
|||
/// update movement state of a non-teleport door as specified
|
||||
/// @param state see MWClass::setDoorState
|
||||
/// @note throws an exception when invoked on a teleport door
|
||||
void activateDoor(const MWWorld::Ptr& door, int state) override;
|
||||
void activateDoor(const MWWorld::Ptr& door, MWWorld::DoorState state) override;
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Useful self-contained method for saving door states
|
||||
*/
|
||||
void saveDoorState(const MWWorld::Ptr& door, int state) override;
|
||||
void saveDoorState(const MWWorld::Ptr& door, MWWorld::DoorState state) override;
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
|
|
@ -1072,28 +1072,34 @@ namespace NifOsg
|
|||
if (nifNode->recType == Nif::RC_NiTriShape)
|
||||
{
|
||||
const Nif::NiTriShape* triShape = static_cast<const Nif::NiTriShape*>(nifNode);
|
||||
const Nif::NiTriShapeData* data = triShape->data.getPtr();
|
||||
vertexColorsPresent = !data->colors.empty();
|
||||
triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triShape->name);
|
||||
if (!data->triangles.empty())
|
||||
geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, data->triangles.size(),
|
||||
(unsigned short*)data->triangles.data()));
|
||||
if (!triShape->data.empty())
|
||||
{
|
||||
const Nif::NiTriShapeData* data = triShape->data.getPtr();
|
||||
vertexColorsPresent = !data->colors.empty();
|
||||
triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triShape->name);
|
||||
if (!data->triangles.empty())
|
||||
geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, data->triangles.size(),
|
||||
(unsigned short*)data->triangles.data()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const Nif::NiTriStrips* triStrips = static_cast<const Nif::NiTriStrips*>(nifNode);
|
||||
const Nif::NiTriStripsData* data = triStrips->data.getPtr();
|
||||
vertexColorsPresent = !data->colors.empty();
|
||||
triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triStrips->name);
|
||||
if (!data->strips.empty())
|
||||
if (!triStrips->data.empty())
|
||||
{
|
||||
for (const std::vector<unsigned short>& strip : data->strips)
|
||||
const Nif::NiTriStripsData* data = triStrips->data.getPtr();
|
||||
vertexColorsPresent = !data->colors.empty();
|
||||
triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triStrips->name);
|
||||
if (!data->strips.empty())
|
||||
{
|
||||
// Can't make a triangle from less than three vertices.
|
||||
if (strip.size() < 3)
|
||||
continue;
|
||||
geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(),
|
||||
(unsigned short*)strip.data()));
|
||||
for (const std::vector<unsigned short>& strip : data->strips)
|
||||
{
|
||||
// Can't make a triangle from less than three vertices.
|
||||
if (strip.size() < 3)
|
||||
continue;
|
||||
geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(),
|
||||
(unsigned short*)strip.data()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,13 +72,10 @@ can loot during death animation
|
|||
:Default: True
|
||||
|
||||
If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation,
|
||||
if they are not in combat. However disposing corpses during death animation is not recommended -
|
||||
death counter may not be incremented, and this behaviour can break quests.
|
||||
This is how Morrowind behaves.
|
||||
if they are not in combat. In this case we have to increment death counter and run disposed actor's script instantly.
|
||||
|
||||
If this setting is false, player has to wait until end of death animation in all cases.
|
||||
This case is more safe, but makes using of summoned creatures exploit
|
||||
(looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder.
|
||||
Makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder.
|
||||
Conflicts with mannequin mods, which use SkipAnim to prevent end of death animation.
|
||||
|
||||
This setting can be toggled in Advanced tab of the launcher.
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<item>
|
||||
<widget class="QCheckBox" name="canLootDuringDeathAnimationCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, if they are not in combat. However disposing corpses during death animation is not recommended - death counter may not be incremented, and this behaviour can break quests. This is how original Morrowind behaves.</p><p>If this setting is false, player has to wait until end of death animation in all cases. This case is more safe, but makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder.</p></body></html></string>
|
||||
<string><html><head/><body><p>If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, if they are not in combat. In this case we have to increment death counter and run disposed actor's script instantly.</p><p>If this setting is false, player has to wait until end of death animation in all cases. Makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder. Conflicts with mannequin mods, which use SkipAnim to prevent end of death animation.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Can loot during death animation</string>
|
||||
|
|
Loading…
Reference in a new issue