mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-18 21:09:40 +00:00
GUI fixes. Animation fixes, i now understand movement accumulation better and was able to clean up some of the hacks with a better result. Lockpicks and probes now work. Haptics. Ready to be playtested.
This commit is contained in:
parent
cad6468518
commit
6474d703ae
27 changed files with 687 additions and 179 deletions
apps/openmw
mwbase
mwclass
mwgui
mwinput
mwmechanics
mwrender
mwvr
mwworld
components/myguiplatform
files/mygui
|
@ -65,6 +65,8 @@ namespace MWBase
|
|||
virtual void enableDetectingBindingMode (int action, bool keyboard) = 0;
|
||||
virtual void resetToDefaultKeyBindings() = 0;
|
||||
virtual void resetToDefaultControllerBindings() = 0;
|
||||
virtual void applyHapticsLeftHand(float intensity) = 0;
|
||||
virtual void applyHapticsRightHand(float intensity) = 0;
|
||||
|
||||
/// Returns if the last used input device was a joystick or a keyboard
|
||||
/// @return true if joystick, false otherwise
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "../mwmechanics/difficultyscaling.hpp"
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/inputmanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
@ -329,11 +330,11 @@ namespace MWClass
|
|||
|
||||
MWMechanics::diseaseContact(victim, ptr);
|
||||
|
||||
victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true);
|
||||
victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true, attackStrength);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const
|
||||
void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful, float hitStrength) const
|
||||
{
|
||||
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||
|
||||
|
@ -348,6 +349,8 @@ namespace MWClass
|
|||
if (isMobile(ptr) && !attacker.isEmpty())
|
||||
setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
|
||||
|
||||
bool attackerIsPlayer = attacker == MWMechanics::getPlayer();
|
||||
|
||||
// Attacker and target store each other as hitattemptactor if they have no one stored yet
|
||||
if (!attacker.isEmpty() && attacker.getClass().isActor())
|
||||
{
|
||||
|
@ -355,20 +358,20 @@ namespace MWClass
|
|||
// First handle the attacked actor
|
||||
if ((stats.getHitAttemptActorId() == -1)
|
||||
&& (statsAttacker.getAiSequence().isInCombat(ptr)
|
||||
|| attacker == MWMechanics::getPlayer()))
|
||||
|| attackerIsPlayer))
|
||||
stats.setHitAttemptActorId(statsAttacker.getActorId());
|
||||
|
||||
// Next handle the attacking actor
|
||||
if ((statsAttacker.getHitAttemptActorId() == -1)
|
||||
&& (statsAttacker.getAiSequence().isInCombat(ptr)
|
||||
|| attacker == MWMechanics::getPlayer()))
|
||||
|| attackerIsPlayer))
|
||||
statsAttacker.setHitAttemptActorId(stats.getActorId());
|
||||
}
|
||||
|
||||
if (!object.isEmpty())
|
||||
stats.setLastHitAttemptObject(object.getCellRef().getRefId());
|
||||
|
||||
if (setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer())
|
||||
if (setOnPcHitMe && !attacker.isEmpty() && attackerIsPlayer)
|
||||
{
|
||||
const std::string &script = ptr.get<ESM::Creature>()->mBase->mScript;
|
||||
/* Set the OnPCHitMe script variable. The script is responsible for clearing it. */
|
||||
|
@ -379,7 +382,7 @@ namespace MWClass
|
|||
if (!successful)
|
||||
{
|
||||
// Missed
|
||||
if (!attacker.isEmpty() && attacker == MWMechanics::getPlayer())
|
||||
if (!attacker.isEmpty() && attackerIsPlayer)
|
||||
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "miss", 1.0f, 1.0f);
|
||||
return;
|
||||
}
|
||||
|
@ -427,6 +430,16 @@ namespace MWClass
|
|||
stats.setFatigue(fatigue);
|
||||
}
|
||||
}
|
||||
|
||||
if(successful)
|
||||
{
|
||||
auto* inputManager = MWBase::Environment::get().getInputManager();
|
||||
if (attackerIsPlayer && hitStrength > 0.f)
|
||||
{
|
||||
float hapticIntensity = std::max(0.25f, std::min(1.f, hitStrength));
|
||||
inputManager->applyHapticsRightHand(hapticIntensity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<MWWorld::Action> Creature::activate (const MWWorld::Ptr& ptr,
|
||||
|
|
|
@ -57,7 +57,7 @@ namespace MWClass
|
|||
|
||||
virtual bool hit(const MWWorld::Ptr& ptr, float attackStrength, int type, bool simulated) const;
|
||||
|
||||
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const;
|
||||
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful, float hitStrength) const;
|
||||
|
||||
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
|
||||
const MWWorld::Ptr& actor) const;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/inputmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
@ -670,15 +671,16 @@ namespace MWClass
|
|||
|
||||
MWMechanics::diseaseContact(victim, ptr);
|
||||
|
||||
othercls.onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true);
|
||||
othercls.onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true, attackStrength);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const
|
||||
void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful, float hitStrength) const
|
||||
{
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||
bool wasDead = stats.isDead();
|
||||
float rawDamage = damage;
|
||||
|
||||
// Note OnPcHitMe is not set for friendly hits.
|
||||
bool setOnPcHitMe = true;
|
||||
|
@ -689,6 +691,8 @@ namespace MWClass
|
|||
stats.setAttacked(true);
|
||||
setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
|
||||
}
|
||||
bool attackerIsPlayer = attacker == MWMechanics::getPlayer();
|
||||
bool victimIsPlayer = ptr == MWMechanics::getPlayer();
|
||||
|
||||
// Attacker and target store each other as hitattemptactor if they have no one stored yet
|
||||
if (!attacker.isEmpty() && attacker.getClass().isActor())
|
||||
|
@ -697,20 +701,20 @@ namespace MWClass
|
|||
// First handle the attacked actor
|
||||
if ((stats.getHitAttemptActorId() == -1)
|
||||
&& (statsAttacker.getAiSequence().isInCombat(ptr)
|
||||
|| attacker == MWMechanics::getPlayer()))
|
||||
|| attackerIsPlayer))
|
||||
stats.setHitAttemptActorId(statsAttacker.getActorId());
|
||||
|
||||
// Next handle the attacking actor
|
||||
if ((statsAttacker.getHitAttemptActorId() == -1)
|
||||
&& (statsAttacker.getAiSequence().isInCombat(ptr)
|
||||
|| attacker == MWMechanics::getPlayer()))
|
||||
|| attackerIsPlayer))
|
||||
statsAttacker.setHitAttemptActorId(stats.getActorId());
|
||||
}
|
||||
|
||||
if (!object.isEmpty())
|
||||
stats.setLastHitAttemptObject(object.getCellRef().getRefId());
|
||||
|
||||
if (setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer())
|
||||
if (setOnPcHitMe && !attacker.isEmpty() && attackerIsPlayer)
|
||||
{
|
||||
const std::string &script = getScript(ptr);
|
||||
/* Set the OnPCHitMe script variable. The script is responsible for clearing it. */
|
||||
|
@ -721,7 +725,7 @@ namespace MWClass
|
|||
if (!successful)
|
||||
{
|
||||
// Missed
|
||||
if (!attacker.isEmpty() && attacker == MWMechanics::getPlayer())
|
||||
if (!attacker.isEmpty() && attackerIsPlayer)
|
||||
sndMgr->playSound3D(ptr, "miss", 1.0f, 1.0f);
|
||||
return;
|
||||
}
|
||||
|
@ -736,7 +740,7 @@ namespace MWClass
|
|||
if (damage < 0.001f)
|
||||
damage = 0;
|
||||
|
||||
bool godmode = ptr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
|
||||
bool godmode = victimIsPlayer && MWBase::Environment::get().getWorld()->getGodModeState();
|
||||
|
||||
if (godmode)
|
||||
damage = 0;
|
||||
|
@ -860,6 +864,23 @@ namespace MWClass
|
|||
|
||||
MWBase::Environment::get().getMechanicsManager()->actorKilled(ptr, attacker);
|
||||
}
|
||||
|
||||
// Apply haptics
|
||||
if (successful)
|
||||
{
|
||||
auto* inputManager = MWBase::Environment::get().getInputManager();
|
||||
if (victimIsPlayer)
|
||||
{
|
||||
float maxHealth = getCreatureStats(ptr).getHealth().getModified();
|
||||
float hapticIntensity = std::max(0.25f, std::min(1.f, rawDamage / ( maxHealth / 4.f)));
|
||||
inputManager->applyHapticsLeftHand(hapticIntensity);
|
||||
}
|
||||
else if (attackerIsPlayer && hitStrength > 0.f)
|
||||
{
|
||||
float hapticIntensity = std::max(0.25f, std::min(1.f, hitStrength));
|
||||
inputManager->applyHapticsRightHand(hapticIntensity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<MWWorld::Action> Npc::activate (const MWWorld::Ptr& ptr,
|
||||
|
|
|
@ -72,7 +72,7 @@ namespace MWClass
|
|||
|
||||
virtual bool hit(const MWWorld::Ptr& ptr, float attackStrength, int type, bool simulated) const;
|
||||
|
||||
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const;
|
||||
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful, float hitStrength) const;
|
||||
|
||||
virtual void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const;
|
||||
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel().
|
||||
|
|
|
@ -30,7 +30,11 @@ namespace MWGui
|
|||
std::string ToolTips::sSchoolNames[] = {"#{sSchoolAlteration}", "#{sSchoolConjuration}", "#{sSchoolDestruction}", "#{sSchoolIllusion}", "#{sSchoolMysticism}", "#{sSchoolRestoration}"};
|
||||
|
||||
ToolTips::ToolTips() :
|
||||
#ifdef USE_OPENXR
|
||||
Layout("openmw_tooltips_vr.layout")
|
||||
#else
|
||||
Layout("openmw_tooltips.layout")
|
||||
#endif
|
||||
, mFocusToolTipX(0.0)
|
||||
, mFocusToolTipY(0.0)
|
||||
, mHorizontalScrollIndex(0)
|
||||
|
|
|
@ -1945,7 +1945,7 @@ namespace MWGui
|
|||
// (Menu gets recreated next tick)
|
||||
if (xrMenuManager)
|
||||
{
|
||||
xrMenuManager->showGUIs(false);
|
||||
xrMenuManager->updateTracking();
|
||||
xrMenuManager = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -239,6 +239,9 @@ namespace MWInput
|
|||
|
||||
bool checkAllowedToUseItems() const;
|
||||
|
||||
void applyHapticsLeftHand(float intensity) override {};
|
||||
void applyHapticsRightHand(float intensity) override {};
|
||||
|
||||
protected:
|
||||
void toggleMainMenu();
|
||||
void toggleSpell();
|
||||
|
|
|
@ -49,6 +49,11 @@
|
|||
#include "actorutil.hpp"
|
||||
#include "spellcasting.hpp"
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
#include "../mwvr/vrenvironment.hpp"
|
||||
#include "../mwvr/vranimation.hpp"
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
@ -1582,9 +1587,14 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
|
|||
{
|
||||
MWWorld::ContainerStoreIterator weapon = mPtr.getClass().getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
MWWorld::Ptr item = *weapon;
|
||||
// TODO: this will only work for the player, and needs to be fixed if NPCs should ever use lockpicks/probes.
|
||||
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getFacedObject();
|
||||
std::string resultMessage, resultSound;
|
||||
// TODO: this will only work for the player, and needs to be fixed if NPCs should ever use lockpicks/probes.
|
||||
#ifdef USE_OPENXR
|
||||
auto* anim = MWVR::Environment::get().getPlayerAnimation();
|
||||
auto target = anim->getTarget("weapon bone");
|
||||
#else
|
||||
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getFacedObject();
|
||||
#endif
|
||||
|
||||
if(!target.isEmpty())
|
||||
{
|
||||
|
|
|
@ -211,7 +211,7 @@ namespace MWMechanics
|
|||
|
||||
if (Misc::Rng::roll0to99() >= getHitChance(attacker, victim, skillValue))
|
||||
{
|
||||
victim.getClass().onHit(victim, damage, false, projectile, attacker, osg::Vec3f(), false);
|
||||
victim.getClass().onHit(victim, damage, false, projectile, attacker, osg::Vec3f(), false, attackStrength);
|
||||
MWMechanics::reduceWeaponCondition(damage, false, weapon, attacker);
|
||||
return;
|
||||
}
|
||||
|
@ -259,7 +259,7 @@ namespace MWMechanics
|
|||
victim.getClass().getContainerStore(victim).add(projectile, 1, victim);
|
||||
}
|
||||
|
||||
victim.getClass().onHit(victim, damage, true, projectile, attacker, hitPosition, true);
|
||||
victim.getClass().onHit(victim, damage, true, projectile, attacker, hitPosition, true, attackStrength);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1062,29 +1062,18 @@ namespace MWRender
|
|||
return mNodeMap;
|
||||
}
|
||||
|
||||
// In VR, only jump, walk, and run groups should accumulate movement.
|
||||
static bool vrAccum(const std::string& groupname)
|
||||
{
|
||||
#ifdef USE_OPENXR
|
||||
if (groupname.compare(0, 4, "jump"))
|
||||
if (groupname.compare(0, 4, "walk"))
|
||||
if (groupname.compare(0, 3, "run"))
|
||||
if (groupname.compare(0, 4, "swim"))
|
||||
return false;
|
||||
#else
|
||||
(void)groupname;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool vrOverride(const std::string& groupname, const std::string& bone)
|
||||
{
|
||||
#ifdef USE_OPENXR
|
||||
// TODO: It's difficult to design a good override system when
|
||||
// I don't have a good understanding of the animation code. So for
|
||||
// now i just block adding updates for nodes that should not be animated in VR.
|
||||
|
||||
// TODO: Some overrides cause NaN during cull.
|
||||
// I believe this happens if an override causes a bone to never receive
|
||||
// a valid matrix, but i'm not totally sure.
|
||||
|
||||
// Add any bone+animation pair that is messing with Vr comfort here.
|
||||
// Add any bone+groupname pair that is messing with Vr comfort here.
|
||||
using Overrides = std::set<std::string>;
|
||||
using GroupOverrides = std::map<std::string, Overrides>;
|
||||
static GroupOverrides sVrOverrides =
|
||||
|
@ -1165,7 +1154,11 @@ namespace MWRender
|
|||
mActiveControllers.insert(std::make_pair(node, it->second));
|
||||
|
||||
if (blendMask == 0 && node == mAccumRoot
|
||||
&& (!isPlayer || vrAccum(active->first)))
|
||||
#ifdef USE_OPENXR
|
||||
// TODO: Little hack to keep certain animations from wobbling the camera in VR
|
||||
&& (!isPlayer)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
mAccumCtrl = it->second;
|
||||
|
||||
|
|
|
@ -442,7 +442,7 @@ public:
|
|||
void disable(const std::string &groupname);
|
||||
|
||||
/** Retrieves the velocity (in units per second) that the animation will move. */
|
||||
float getVelocity(const std::string &groupname) const;
|
||||
virtual float getVelocity(const std::string &groupname) const;
|
||||
|
||||
virtual osg::Vec3f runAnimation(float duration);
|
||||
|
||||
|
|
|
@ -113,10 +113,8 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
|
|||
return;
|
||||
|
||||
#ifdef USE_OPENXR
|
||||
// In VR player rotation and weapon aim are unrelated.
|
||||
auto* anim = MWVR::Environment::get().getPlayerAnimation();
|
||||
osg::Matrix worldMatrix = osg::computeLocalToWorld(anim->mWeaponDirectionTransform->getParentalNodePaths()[0]);
|
||||
osg::Quat orient = worldMatrix.getRotate();
|
||||
// In VR weapon aim is taken from the real orientation of the weapon.
|
||||
osg::Quat orient = MWVR::Environment::get().getPlayerAnimation()->getWeaponTransformMatrix().getRotate();
|
||||
#else
|
||||
// The orientation of the launched projectile. Always the same as the actor orientation, even if the ArrowBone's orientation dictates otherwise.
|
||||
osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0))
|
||||
|
|
|
@ -127,8 +127,6 @@ public:
|
|||
mOnActivate = changed && mActive;
|
||||
mOnDeactivate = changed && !mActive;
|
||||
|
||||
//if(openMWActionCode() == MWInput::InputManager::A_Journal)
|
||||
Log(Debug::Verbose) << "Action[" << mXRAction.mName << "]: old=" << old << " shouldQueue=" << shouldQueue() << ", active=" << mActive << ", value=" << mValue << " onActivate=" << mOnActivate << ", mOnDeactivate=" << mOnDeactivate;
|
||||
if (shouldQueue())
|
||||
{
|
||||
queue.push_back(this);
|
||||
|
@ -287,6 +285,7 @@ struct OpenXRInput
|
|||
{
|
||||
A_XrFirst = MWInput::InputManager::A_Last,
|
||||
A_ActivateTouch,
|
||||
A_RepositionMenu,
|
||||
A_XrLast
|
||||
};
|
||||
|
||||
|
@ -326,6 +325,11 @@ struct OpenXRInput
|
|||
const Action* nextAction();
|
||||
PoseSet getHandPoses(int64_t time, TrackedSpace space);
|
||||
|
||||
void applyHaptics(SubAction subAction, float intensity)
|
||||
{
|
||||
mHapticsAction.applyHaptics(subactionPath(subAction), intensity);
|
||||
}
|
||||
|
||||
SubActionPaths mSubactionPath;
|
||||
XrActionSet mActionSet = XR_NULL_HANDLE;
|
||||
|
||||
|
@ -345,6 +349,7 @@ struct OpenXRInput
|
|||
ControllerActionPaths mTriggerValuePath;
|
||||
|
||||
ActionPtr mGameMenu;
|
||||
ActionPtr mRepositionMenu;
|
||||
ActionPtr mInventory;
|
||||
ActionPtr mActivate;
|
||||
ActionPtr mUse;
|
||||
|
@ -355,18 +360,19 @@ struct OpenXRInput
|
|||
ActionPtr mCycleSpellRight;
|
||||
ActionPtr mCycleWeaponLeft;
|
||||
ActionPtr mCycleWeaponRight;
|
||||
ActionPtr mToggleSneak;
|
||||
ActionPtr mSneak;
|
||||
ActionPtr mQuickMenu;
|
||||
ActionPtr mLookLeftRight;
|
||||
ActionPtr mMoveForwardBackward;
|
||||
ActionPtr mMoveLeftRight;
|
||||
ActionPtr mJournal;
|
||||
//OpenXRAction_Delayed mQuickSave;
|
||||
|
||||
// Needed to access all the actions that don't fit on the controllers
|
||||
ActionPtr mQuickKeysMenu;
|
||||
|
||||
ActionPtr mQuickSave;
|
||||
ActionPtr mRest;
|
||||
ActionPtr mActivateTouch;
|
||||
ActionPtr mAlwaysRun;
|
||||
ActionPtr mAutoMove;
|
||||
ActionPtr mToggleHUD;
|
||||
ActionPtr mToggleDebug;
|
||||
|
||||
// Hand tracking
|
||||
OpenXRAction mHandPoseAction;
|
||||
|
@ -480,6 +486,8 @@ bool OpenXRAction::getPose(XrPath subactionPath)
|
|||
|
||||
bool OpenXRAction::applyHaptics(XrPath subactionPath, float amplitude)
|
||||
{
|
||||
amplitude = std::max(0.f, std::min(1.f, amplitude));
|
||||
|
||||
auto* xr = Environment::get().getManager();
|
||||
XrHapticVibration vibration{ XR_TYPE_HAPTIC_VIBRATION };
|
||||
vibration.amplitude = amplitude;
|
||||
|
@ -534,26 +542,31 @@ OpenXRInput::OpenXRInput()
|
|||
, mAPath(generateControllerActionPaths("/input/a/click"))
|
||||
, mBPath(generateControllerActionPaths("/input/b/click"))
|
||||
, mTriggerValuePath(generateControllerActionPaths("/input/trigger/value"))
|
||||
, mGameMenu(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_GameMenu, "game_menu", "GameMenu", { })))
|
||||
, mGameMenu(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_GameMenu, "game_menu", "Game Menu", { })))
|
||||
, mRepositionMenu(std::move(createMWAction<ButtonLongPressAction>(A_RepositionMenu, "reposition_menu", "Reposition Menu", { })))
|
||||
, mInventory(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_Inventory, "inventory", "Inventory", { })))
|
||||
, mActivate(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_Activate, "activate", "Activate", { })))
|
||||
, mUse(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_Use, "use", "Use", { })))
|
||||
, mUse(std::move(createMWAction<ButtonHoldAction>(MWInput::InputManager::A_Use, "use", "Use", { })))
|
||||
, mJump(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_Jump, "jump", "Jump", { })))
|
||||
, mToggleWeapon(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_ToggleWeapon, "weapon", "Weapon", { })))
|
||||
, mToggleSpell(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_ToggleSpell, "spell", "Spell", { })))
|
||||
, mCycleSpellLeft(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_CycleSpellLeft, "cycle_spell_left", "CycleSpellLeft", { })))
|
||||
, mCycleSpellRight(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_CycleSpellRight, "cycle_spell_right", "CycleSpellRight", { })))
|
||||
, mCycleWeaponLeft(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_CycleWeaponLeft, "cycle_weapon_left", "CycleWeaponLeft", { })))
|
||||
, mCycleWeaponRight(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_CycleWeaponRight, "cycle_weapon_right", "CycleWeaponRight", { })))
|
||||
, mToggleSneak(std::move(createMWAction<ButtonHoldAction>(MWInput::InputManager::A_ToggleSneak, "sneak", "Sneak", { })))
|
||||
, mQuickMenu(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_QuickMenu, "quick_menu", "QuickMenu", { })))
|
||||
, mLookLeftRight(std::move(createMWAction<AxisAction>(MWInput::InputManager::A_LookLeftRight, "look_left_right", "LookLeftRight", { })))
|
||||
, mMoveForwardBackward(std::move(createMWAction<AxisAction>(MWInput::InputManager::A_MoveForwardBackward, "move_forward_backward", "MoveForwardBackward", { })))
|
||||
, mMoveLeftRight(std::move(createMWAction<AxisAction>(MWInput::InputManager::A_MoveLeftRight, "move_left_right", "MoveLeftRight", { })))
|
||||
, mJournal(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_Journal, "journal_book", "Journal Book", { })))
|
||||
//, mQuickSave(std::move(createMWAction<ButtonHoldAction>(MWInput::InputManager::A_QuickSave, "quick_save", "Quick Save", { })))
|
||||
, mQuickKeysMenu(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_QuickKeysMenu, "quick_keys_menu", "Quick Keys Menu", { })))
|
||||
, mCycleSpellLeft(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_CycleSpellLeft, "cycle_spell_left", "Cycle Spell Left", { })))
|
||||
, mCycleSpellRight(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_CycleSpellRight, "cycle_spell_right", "Cycle Spell Right", { })))
|
||||
, mCycleWeaponLeft(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_CycleWeaponLeft, "cycle_weapon_left", "Cycle Weapon Left", { })))
|
||||
, mCycleWeaponRight(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_CycleWeaponRight, "cycle_weapon_right", "Cycle Weapon Right", { })))
|
||||
, mSneak(std::move(createMWAction<ButtonHoldAction>(MWInput::InputManager::A_Sneak, "sneak", "Sneak", { })))
|
||||
, mQuickMenu(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_QuickMenu, "quick_menu", "Quick Menu", { })))
|
||||
, mLookLeftRight(std::move(createMWAction<AxisAction>(MWInput::InputManager::A_LookLeftRight, "look_left_right", "Look Left Right", { })))
|
||||
, mMoveForwardBackward(std::move(createMWAction<AxisAction>(MWInput::InputManager::A_MoveForwardBackward, "move_forward_backward", "Move Forward Backward", { })))
|
||||
, mMoveLeftRight(std::move(createMWAction<AxisAction>(MWInput::InputManager::A_MoveLeftRight, "move_left_right", "Move Left Right", { })))
|
||||
, mJournal(std::move(createMWAction<ButtonLongPressAction>(MWInput::InputManager::A_Journal, "journal_book", "Journal Book", { })))
|
||||
, mQuickSave(std::move(createMWAction<ButtonLongPressAction>(MWInput::InputManager::A_QuickSave, "quick_save", "Quick Save", { })))
|
||||
, mRest(std::move(createMWAction<ButtonLongPressAction>(MWInput::InputManager::A_Rest, "rest", "Rest", { })))
|
||||
, mActivateTouch(std::move(createMWAction<AxisAction>(A_ActivateTouch, "activate_touched", "Activate Touch", { RIGHT_HAND })))
|
||||
, mAlwaysRun(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_AlwaysRun, "always_run", "Always Run", { })))
|
||||
, mAutoMove(std::move(createMWAction<ButtonPressAction>(MWInput::InputManager::A_AutoMove, "auto_move", "Auto Move", { })))
|
||||
, mToggleHUD(std::move(createMWAction<ButtonLongPressAction>(MWInput::InputManager::A_ToggleHUD, "toggle_hud", "Toggle HUD", { })))
|
||||
, mToggleDebug(std::move(createMWAction<ButtonLongPressAction>(MWInput::InputManager::A_ToggleDebug, "toggle_debug", "Toggle DEBUG", { })))
|
||||
, mHandPoseAction(std::move(createXRAction(XR_ACTION_TYPE_POSE_INPUT, "hand_pose", "Hand Pose", { LEFT_HAND, RIGHT_HAND })))
|
||||
, mHapticsAction(std::move(createXRAction(XR_ACTION_TYPE_VIBRATION_OUTPUT, "vibrate_hand", "Vibrate Hand", { LEFT_HAND, RIGHT_HAND })))
|
||||
{
|
||||
|
@ -562,6 +575,81 @@ OpenXRInput::OpenXRInput()
|
|||
XrPath oculusTouchInteractionProfilePath;
|
||||
CHECK_XRCMD(
|
||||
xrStringToPath(xr->impl().mInstance, "/interaction_profiles/oculus/touch_controller", &oculusTouchInteractionProfilePath));
|
||||
|
||||
/*
|
||||
// Applicable actions not (yet) included
|
||||
A_QuickKey1,
|
||||
A_QuickKey2,
|
||||
A_QuickKey3,
|
||||
A_QuickKey4,
|
||||
A_QuickKey5,
|
||||
A_QuickKey6,
|
||||
A_QuickKey7,
|
||||
A_QuickKey8,
|
||||
A_QuickKey9,
|
||||
A_QuickKey10,
|
||||
A_QuickKeysMenu,
|
||||
A_QuickLoad,
|
||||
A_CycleSpellLeft,
|
||||
A_CycleSpellRight,
|
||||
A_CycleWeaponLeft,
|
||||
A_CycleWeaponRight,
|
||||
A_Screenshot, // Generate a VR screenshot?
|
||||
A_Console, // Currently awkward due to a lack of virtual keyboard, but should be included when that's in place
|
||||
*/
|
||||
|
||||
/*
|
||||
Oculus Bindings:
|
||||
L-Squeeze:
|
||||
Hold: Sneak
|
||||
|
||||
R-Squeeze:
|
||||
Hold: Enable Pointer
|
||||
|
||||
L-Trigger:
|
||||
Press: Jump
|
||||
|
||||
R-Trigger:
|
||||
IF POINTER:
|
||||
Activate
|
||||
ELSE:
|
||||
Use
|
||||
|
||||
L-Thumbstick:
|
||||
X-Axis: MoveForwardBackward
|
||||
Y-Axis: MoveLeftRight
|
||||
Button:
|
||||
Press: AlwaysRun
|
||||
Long: ToggleHUD
|
||||
Touch:
|
||||
|
||||
R-Thumbstick:
|
||||
X-Axis: LookLeftRight
|
||||
Y-Axis:
|
||||
Button:
|
||||
Press: AutoMove
|
||||
Long: ToggleDebug
|
||||
Touch:
|
||||
|
||||
X:
|
||||
Press: Toggle Spell
|
||||
Long:
|
||||
Y:
|
||||
Press: Rest
|
||||
Long: Quick Save
|
||||
A:
|
||||
Press: Toggle Weapon
|
||||
Long:
|
||||
B:
|
||||
Press: Inventory
|
||||
Long: Journal
|
||||
|
||||
Menu:
|
||||
Press: GameMenun
|
||||
Long: Reposition GUI
|
||||
|
||||
*/
|
||||
|
||||
std::vector<XrActionSuggestedBinding> bindings{ {
|
||||
{mHandPoseAction, mPosePath[LEFT_HAND]},
|
||||
{mHandPoseAction, mPosePath[RIGHT_HAND]},
|
||||
|
@ -574,16 +662,22 @@ OpenXRInput::OpenXRInput()
|
|||
{*mUse, mTriggerValuePath[RIGHT_HAND]},
|
||||
{*mJump, mTriggerValuePath[LEFT_HAND]},
|
||||
{*mToggleWeapon, mAPath[RIGHT_HAND]},
|
||||
{*mToggleSpell, mAPath[RIGHT_HAND]},
|
||||
{*mCycleSpellLeft, mThumbstickClickPath[LEFT_HAND]},
|
||||
{*mCycleSpellRight, mThumbstickClickPath[RIGHT_HAND]},
|
||||
{*mCycleWeaponLeft, mThumbstickClickPath[LEFT_HAND]},
|
||||
{*mCycleWeaponRight, mThumbstickClickPath[RIGHT_HAND]},
|
||||
{*mToggleSneak, mXPath[LEFT_HAND]},
|
||||
{*mToggleSpell, mXPath[LEFT_HAND]},
|
||||
//{*mCycleSpellLeft, mThumbstickClickPath[LEFT_HAND]},
|
||||
//{*mCycleSpellRight, mThumbstickClickPath[RIGHT_HAND]},
|
||||
//{*mCycleWeaponLeft, mThumbstickClickPath[LEFT_HAND]},
|
||||
//{*mCycleWeaponRight, mThumbstickClickPath[RIGHT_HAND]},
|
||||
{*mAlwaysRun, mThumbstickClickPath[LEFT_HAND]},
|
||||
{*mAutoMove, mThumbstickClickPath[RIGHT_HAND]},
|
||||
{*mToggleHUD, mThumbstickClickPath[LEFT_HAND]},
|
||||
{*mToggleDebug, mThumbstickClickPath[RIGHT_HAND]},
|
||||
{*mSneak, mSqueezeValuePath[LEFT_HAND]},
|
||||
{*mInventory, mBPath[RIGHT_HAND]},
|
||||
{*mJournal, mYPath[LEFT_HAND]},
|
||||
//{*mQuickSave, mYPath[LEFT_HAND]},
|
||||
{*mRest, mYPath[LEFT_HAND]},
|
||||
{*mJournal, mBPath[RIGHT_HAND]},
|
||||
{*mQuickSave, mYPath[LEFT_HAND]},
|
||||
{*mGameMenu, mMenuClickPath[LEFT_HAND]},
|
||||
{*mRepositionMenu, mMenuClickPath[LEFT_HAND]},
|
||||
{*mActivateTouch, mSqueezeValuePath[RIGHT_HAND]},
|
||||
} };
|
||||
XrInteractionProfileSuggestedBinding suggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING };
|
||||
|
@ -591,24 +685,6 @@ OpenXRInput::OpenXRInput()
|
|||
suggestedBindings.suggestedBindings = bindings.data();
|
||||
suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
|
||||
CHECK_XRCMD(xrSuggestInteractionProfileBindings(xr->impl().mInstance, &suggestedBindings));
|
||||
|
||||
/*
|
||||
mActivate; // R-Squeeze
|
||||
mUse; // R-Trigger
|
||||
mJump; // L-Trigger. L-trigger has value, could use this to make measured jumps ?
|
||||
mToggleWeapon; // A
|
||||
mToggleSpell; // A + SpellModifier
|
||||
mRun; // Based on movement thumbstick value ?
|
||||
mCycleSpellLeft; // L-ThumbstickClick + SpellModifier
|
||||
mCycleSpellRight; // R-ThumbstickClick + SpellModifier
|
||||
mCycleWeaponLeft; // L-ThumbstickClick
|
||||
mCycleWeaponRight; // R-ThumbstickClick
|
||||
mToggleSneak; // X
|
||||
mInventory; // B
|
||||
mQuickMenu; // Y
|
||||
mGameMenu; // Menu
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
{ // Set up action spaces
|
||||
|
@ -691,6 +767,7 @@ OpenXRInput::updateControls()
|
|||
CHECK_XRCMD(xrSyncActions(xr->impl().mSession, &syncInfo));
|
||||
|
||||
mGameMenu->updateAndQueue(mActionQueue);
|
||||
mRepositionMenu->updateAndQueue(mActionQueue);
|
||||
mInventory->updateAndQueue(mActionQueue);
|
||||
mActivate->updateAndQueue(mActionQueue);
|
||||
mUse->updateAndQueue(mActionQueue);
|
||||
|
@ -701,15 +778,28 @@ OpenXRInput::updateControls()
|
|||
mCycleSpellRight->updateAndQueue(mActionQueue);
|
||||
mCycleWeaponLeft->updateAndQueue(mActionQueue);
|
||||
mCycleWeaponRight->updateAndQueue(mActionQueue);
|
||||
mToggleSneak->updateAndQueue(mActionQueue);
|
||||
mSneak->updateAndQueue(mActionQueue);
|
||||
mQuickMenu->updateAndQueue(mActionQueue);
|
||||
mLookLeftRight->updateAndQueue(mActionQueue);
|
||||
mMoveForwardBackward->updateAndQueue(mActionQueue);
|
||||
mMoveLeftRight->updateAndQueue(mActionQueue);
|
||||
mJournal->updateAndQueue(mActionQueue);
|
||||
//mQuickSave->updateAndQueue(mActionQueue);
|
||||
mQuickKeysMenu->updateAndQueue(mActionQueue);
|
||||
mQuickSave->updateAndQueue(mActionQueue);
|
||||
mRest->updateAndQueue(mActionQueue);
|
||||
mActivateTouch->updateAndQueue(mActionQueue);
|
||||
mAlwaysRun->updateAndQueue(mActionQueue);
|
||||
mAutoMove->updateAndQueue(mActionQueue);
|
||||
mToggleHUD->updateAndQueue(mActionQueue);
|
||||
mToggleDebug->updateAndQueue(mActionQueue);
|
||||
|
||||
//if (mActivateTouch->isActive())
|
||||
//{
|
||||
// mHapticsAction.applyHaptics(mSubactionPath[RIGHT_HAND], 0.5);
|
||||
//}
|
||||
//if (mSneak->isActive())
|
||||
//{
|
||||
// mHapticsAction.applyHaptics(mSubactionPath[LEFT_HAND], 0.5);
|
||||
//}
|
||||
}
|
||||
|
||||
XrPath OpenXRInput::generateXrPath(const std::string& path)
|
||||
|
@ -846,11 +936,11 @@ private:
|
|||
{
|
||||
injectMousePress(SDL_BUTTON_LEFT, onPress);
|
||||
}
|
||||
//else if (onPress)
|
||||
//{
|
||||
// // Other actions should only happen on release;
|
||||
// return;
|
||||
//}
|
||||
else if (onPress)
|
||||
{
|
||||
// Other actions should only happen on release;
|
||||
return;
|
||||
}
|
||||
else if (dnd.mIsOnDragAndDrop)
|
||||
{
|
||||
// Intersected with the world while drag and drop is active
|
||||
|
@ -879,6 +969,17 @@ private:
|
|||
mouseReleased(arg, sdlButton);
|
||||
}
|
||||
|
||||
// TODO: Configurable haptics: on/off + max intensity
|
||||
void OpenXRInputManager::applyHapticsLeftHand(float intensity)
|
||||
{
|
||||
mXRInput->applyHaptics(OpenXRInput::LEFT_HAND, intensity);
|
||||
}
|
||||
|
||||
void OpenXRInputManager::applyHapticsRightHand(float intensity)
|
||||
{
|
||||
mXRInput->applyHaptics(OpenXRInput::RIGHT_HAND, intensity);
|
||||
}
|
||||
|
||||
OpenXRInputManager::OpenXRInputManager(
|
||||
SDL_Window* window,
|
||||
osg::ref_ptr<osgViewer::Viewer> viewer,
|
||||
|
@ -903,7 +1004,7 @@ private:
|
|||
{
|
||||
// VR mode has no concept of these
|
||||
mControlSwitch["vanitymode"] = false;
|
||||
mGuiCursorEnabled = false;
|
||||
//mGuiCursorEnabled = false;
|
||||
}
|
||||
|
||||
OpenXRInputManager::~OpenXRInputManager()
|
||||
|
@ -913,7 +1014,7 @@ private:
|
|||
void OpenXRInputManager::changeInputMode(bool mode)
|
||||
{
|
||||
// VR mode has no concept of these
|
||||
mGuiCursorEnabled = false;
|
||||
//mGuiCursorEnabled = false;
|
||||
MWInput::InputManager::changeInputMode(mode);
|
||||
MWBase::Environment::get().getWindowManager()->showCrosshair(false);
|
||||
MWBase::Environment::get().getWindowManager()->setCursorVisible(false);
|
||||
|
@ -927,10 +1028,13 @@ private:
|
|||
mXRInput->updateControls();
|
||||
|
||||
auto* vrGuiManager = Environment::get().getGUIManager();
|
||||
vrGuiManager->updateFocus();
|
||||
bool vrHasFocus = vrGuiManager->updateFocus();
|
||||
auto guiCursor = vrGuiManager->guiCursor();
|
||||
mGuiCursorX = guiCursor.x();
|
||||
mGuiCursorY = guiCursor.y();
|
||||
if (vrHasFocus)
|
||||
{
|
||||
mGuiCursorX = guiCursor.x();
|
||||
mGuiCursorY = guiCursor.y();
|
||||
}
|
||||
|
||||
while (auto* action = mXRInput->nextAction())
|
||||
{
|
||||
|
@ -942,9 +1046,6 @@ private:
|
|||
MWInput::InputManager::update(dt, disableControls, disableEvents);
|
||||
|
||||
bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
|
||||
auto* xrGUIManager = Environment::get().getGUIManager();
|
||||
if(xrGUIManager)
|
||||
xrGUIManager->showGUIs(guiMode);
|
||||
|
||||
setPlayerControlsEnabled(!guiMode);
|
||||
|
||||
|
@ -981,6 +1082,14 @@ private:
|
|||
case A_MoveForwardBackward:
|
||||
mInputBinder->getChannel(A_MoveForwardBackward)->setValue(-action->value() / 2.f + 0.5f);
|
||||
break;
|
||||
case A_Sneak:
|
||||
if(!mSneakToggles)
|
||||
mInputBinder->getChannel(A_Sneak)->setValue(action->isActive() ? 1.f : 0.f);
|
||||
break;
|
||||
case A_Use:
|
||||
if (!(mActivationIndication || MWBase::Environment::get().getWindowManager()->isGuiMode()))
|
||||
mInputBinder->getChannel(A_Use)->setValue(action->value());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -992,10 +1101,6 @@ private:
|
|||
{
|
||||
case A_GameMenu:
|
||||
toggleMainMenu();
|
||||
// Explicitly request position update here so that the player can move the menu
|
||||
// using the menu key when the menu can't be toggled.
|
||||
// TODO: This should respond to a menu HODL instead
|
||||
// xrGUIManager->updateTracking();
|
||||
break;
|
||||
case A_Screenshot:
|
||||
screenshot();
|
||||
|
@ -1058,9 +1163,11 @@ private:
|
|||
showQuickKeysMenu();
|
||||
break;
|
||||
case A_ToggleHUD:
|
||||
Log(Debug::Verbose) << "Toggle HUD";
|
||||
MWBase::Environment::get().getWindowManager()->toggleHud();
|
||||
break;
|
||||
case A_ToggleDebug:
|
||||
Log(Debug::Verbose) << "Toggle Debug";
|
||||
MWBase::Environment::get().getWindowManager()->toggleDebugWindow();
|
||||
break;
|
||||
case A_QuickSave:
|
||||
|
@ -1085,27 +1192,33 @@ private:
|
|||
if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory))
|
||||
MWBase::Environment::get().getWindowManager()->cycleWeapon(true);
|
||||
break;
|
||||
case A_ToggleSneak:
|
||||
toggleSneaking();
|
||||
break;
|
||||
case A_Jump:
|
||||
mAttemptJump = true;
|
||||
break;
|
||||
case OpenXRInput::A_RepositionMenu:
|
||||
xrGUIManager->updateTracking();
|
||||
break;
|
||||
case A_Use:
|
||||
if (mActivationIndication || MWBase::Environment::get().getWindowManager()->isGuiMode())
|
||||
pointActivation(true);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// A few actions need both activate and deactivate
|
||||
// A few actions need to fire on deactivation
|
||||
if (action->onDeactivate())
|
||||
{
|
||||
switch (action->openMWActionCode())
|
||||
{
|
||||
case A_Use:
|
||||
mInputBinder->getChannel(A_Use)->setValue(0.f);
|
||||
if (mActivationIndication || MWBase::Environment::get().getWindowManager()->isGuiMode())
|
||||
pointActivation(action->onActivate());
|
||||
else
|
||||
mInputBinder->getChannel(A_Use)->setValue(action->isActive());
|
||||
pointActivation(false);
|
||||
break;
|
||||
case A_Sneak:
|
||||
if (mSneakToggles)
|
||||
toggleSneaking();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -49,6 +49,9 @@ namespace MWVR
|
|||
|
||||
void injectMousePress(int sdlButton, bool onPress);
|
||||
|
||||
void applyHapticsLeftHand(float intensity) override;
|
||||
void applyHapticsRightHand(float intensity) override;
|
||||
|
||||
std::unique_ptr<OpenXRInput> mXRInput;
|
||||
std::unique_ptr<RealisticCombat::StateMachine> mRealisticCombat;
|
||||
Pose mPreviousHeadPose{};
|
||||
|
|
|
@ -507,6 +507,8 @@ void VRAnimation::updateParts()
|
|||
removeIndividualPart(ESM::PartReferenceType::PRT_RAnkle);
|
||||
removeIndividualPart(ESM::PartReferenceType::PRT_LKnee);
|
||||
removeIndividualPart(ESM::PartReferenceType::PRT_RKnee);
|
||||
removeIndividualPart(ESM::PartReferenceType::PRT_LFoot);
|
||||
removeIndividualPart(ESM::PartReferenceType::PRT_RFoot);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -613,6 +615,11 @@ osg::ref_ptr<osg::Geometry> VRAnimation::createPointerGeometry(void)
|
|||
return geometry;
|
||||
}
|
||||
|
||||
float VRAnimation::getVelocity(const std::string& groupname) const
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
osg::Vec3f VRAnimation::runAnimation(float timepassed)
|
||||
{
|
||||
return NpcAnimation::runAnimation(timepassed);
|
||||
|
@ -662,6 +669,13 @@ void VRAnimation::addControllers()
|
|||
group->addChild(mModelOffset);
|
||||
mModelOffset->addChild(mObjectRoot);
|
||||
}
|
||||
|
||||
auto wb = mNodeMap.find("weapon bone");
|
||||
if (wb != mNodeMap.end())
|
||||
{
|
||||
wb->second->removeChild(mWeaponPointerTransform);
|
||||
wb->second->addChild(mWeaponPointerTransform);
|
||||
}
|
||||
}
|
||||
void VRAnimation::enableHeadAnimation(bool)
|
||||
{
|
||||
|
@ -692,6 +706,23 @@ const MWRender::RayResult& VRAnimation::getPointerTarget() const
|
|||
return mPointerTarget;
|
||||
}
|
||||
|
||||
|
||||
MWWorld::Ptr VRAnimation::getTarget(const std::string& directorNode)
|
||||
{
|
||||
auto node = mNodeMap.find(directorNode);
|
||||
auto* world = MWBase::Environment::get().getWorld();
|
||||
MWRender::RayResult result{};
|
||||
if (node != mNodeMap.end())
|
||||
if (world)
|
||||
world->getTargetObject(result, node->second);
|
||||
return result.mHitObject;
|
||||
}
|
||||
|
||||
osg::Matrix VRAnimation::getWeaponTransformMatrix() const
|
||||
{
|
||||
return osg::computeLocalToWorld(mWeaponDirectionTransform->getParentalNodePaths()[0]);
|
||||
}
|
||||
|
||||
void VRAnimation::updatePointerTarget()
|
||||
{
|
||||
auto* world = MWBase::Environment::get().getWorld();
|
||||
|
|
|
@ -63,9 +63,15 @@ public:
|
|||
|
||||
void updatePointerTarget();
|
||||
|
||||
public:
|
||||
MWWorld::Ptr getTarget(const std::string& directorNode);
|
||||
|
||||
osg::Matrix getWeaponTransformMatrix() const;
|
||||
|
||||
protected:
|
||||
static osg::ref_ptr<osg::Geometry> createPointerGeometry(void);
|
||||
|
||||
float getVelocity(const std::string& groupname) const override;
|
||||
|
||||
public:
|
||||
std::shared_ptr<OpenXRSession> mSession;
|
||||
osg::ref_ptr<ForearmController> mForearmControllers[2];
|
||||
|
|
|
@ -134,11 +134,11 @@ private:
|
|||
VRGUILayer::VRGUILayer(
|
||||
osg::ref_ptr<osg::Group> geometryRoot,
|
||||
osg::ref_ptr<osg::Group> cameraRoot,
|
||||
std::string filter,
|
||||
std::string layerName,
|
||||
LayerConfig config,
|
||||
VRGUIManager* parent)
|
||||
: mConfig(config)
|
||||
, mFilter(filter)
|
||||
, mLayerName(layerName)
|
||||
, mGeometryRoot(geometryRoot)
|
||||
, mCameraRoot(cameraRoot)
|
||||
{
|
||||
|
@ -176,6 +176,9 @@ VRGUILayer::VRGUILayer(
|
|||
mGeometry->setName("VRGUILayer");
|
||||
|
||||
// Create the camera that will render the menu texture
|
||||
std::string filter = mLayerName;
|
||||
if (!mConfig.extraLayers.empty())
|
||||
filter = filter + ";" + mConfig.extraLayers;
|
||||
mGUICamera = new GUICamera(config.pixelResolution.x(), config.pixelResolution.y(), config.backgroundColor);
|
||||
osgMyGUI::RenderManager& renderManager = static_cast<osgMyGUI::RenderManager&>(MyGUI::RenderManager::getInstance());
|
||||
mMyGUICamera = renderManager.createGUICamera(osg::Camera::NESTED_RENDER, filter);
|
||||
|
@ -201,8 +204,10 @@ VRGUILayer::VRGUILayer(
|
|||
mCameraRoot->addChild(mGUICamera);
|
||||
|
||||
// Edit offset to account for priority
|
||||
if(!mConfig.sideBySide)
|
||||
if (!mConfig.sideBySide)
|
||||
{
|
||||
mConfig.offset.y() -= 0.001f * mConfig.priority;
|
||||
}
|
||||
|
||||
mTransform->addUpdateCallback(new LayerUpdateCallback(this));
|
||||
}
|
||||
|
@ -302,7 +307,7 @@ void VRGUILayer::updateRect()
|
|||
}
|
||||
|
||||
// Some widgets don't capture the full visual
|
||||
if (mFilter == "JournalBooks" || mFilter == "MessageBox" )
|
||||
if (mLayerName == "JournalBooks" )
|
||||
{
|
||||
mRealRect.left = 0.f;
|
||||
mRealRect.top = 0.f;
|
||||
|
@ -310,7 +315,7 @@ void VRGUILayer::updateRect()
|
|||
mRealRect.bottom = 1.f;
|
||||
}
|
||||
|
||||
if (mFilter == "Notification")
|
||||
if (mLayerName == "Notification")
|
||||
{
|
||||
// The latest widget for notification is always the top one
|
||||
// So we just have to stretch the rectangle to the bottom
|
||||
|
@ -357,7 +362,7 @@ void VRGUILayer::update()
|
|||
{
|
||||
mTransform->setScale(osg::Vec3(w / res, 1.f, h / res));
|
||||
}
|
||||
if (mFilter == "Notification")
|
||||
if (mLayerName == "Notification")
|
||||
{
|
||||
auto viewSize = MyGUI::RenderManager::getInstance().getViewSize();
|
||||
h = (1.f - mRealRect.top) * viewSize.height;
|
||||
|
@ -421,44 +426,28 @@ VRGUIManager::~VRGUIManager(void)
|
|||
{
|
||||
}
|
||||
|
||||
void VRGUIManager::showGUIs(bool show)
|
||||
{
|
||||
}
|
||||
|
||||
static const LayerConfig createDefaultConfig(int priority)
|
||||
static const LayerConfig createDefaultConfig(int priority, bool background = true, SizingMode sizingMode = SizingMode::Auto)
|
||||
{
|
||||
return LayerConfig{
|
||||
1,
|
||||
priority,
|
||||
false, // side-by-side
|
||||
osg::Vec4{0.f,0.f,0.f,.75f}, // background
|
||||
background ? osg::Vec4{0.f,0.f,0.f,.75f} : osg::Vec4{}, // background
|
||||
osg::Vec3(0.f,0.66f,-.25f), // offset
|
||||
osg::Vec2(0.f,0.f), // center (model space)
|
||||
osg::Vec2(1.f, 1.f), // extent (meters)
|
||||
1024, // Spatial resolution (pixels per meter)
|
||||
osg::Vec2i(2048,2048), // Texture resolution
|
||||
osg::Vec2(1,1),
|
||||
SizingMode::Auto,
|
||||
TrackingMode::Menu
|
||||
sizingMode,
|
||||
TrackingMode::Menu,
|
||||
"Popup"
|
||||
};
|
||||
}
|
||||
LayerConfig gDefaultConfig = createDefaultConfig(1);
|
||||
LayerConfig gJournalBooksConfig = LayerConfig
|
||||
{
|
||||
2,
|
||||
gDefaultConfig.sideBySide,
|
||||
osg::Vec4{}, // background
|
||||
gDefaultConfig.offset,
|
||||
gDefaultConfig.center,
|
||||
gDefaultConfig.extent,
|
||||
gDefaultConfig.spatialResolution,
|
||||
gDefaultConfig.pixelResolution,
|
||||
gDefaultConfig.myGUIViewSize,
|
||||
SizingMode::Fixed,
|
||||
gDefaultConfig.trackingMode
|
||||
};
|
||||
LayerConfig gDefaultWindowsConfig = createDefaultConfig(3);
|
||||
LayerConfig gMessageBoxConfig = gJournalBooksConfig;
|
||||
LayerConfig gNotificationConfig = gJournalBooksConfig;
|
||||
LayerConfig gJournalBooksConfig = createDefaultConfig(2, false, SizingMode::Fixed);
|
||||
LayerConfig gDefaultWindowsConfig = createDefaultConfig(3, true);
|
||||
LayerConfig gMessageBoxConfig = createDefaultConfig(6, false, SizingMode::Auto);;
|
||||
LayerConfig gNotificationConfig = createDefaultConfig(7, false, SizingMode::Fixed);;
|
||||
|
||||
static const float sSideBySideRadius = 1.f;
|
||||
static const float sSideBySideAzimuthInterval = -osg::PI_4;
|
||||
|
@ -475,7 +464,8 @@ static const LayerConfig createSideBySideConfig(int priority)
|
|||
gDefaultConfig.pixelResolution,
|
||||
osg::Vec2(0.70f, 0.70f),
|
||||
SizingMode::Fixed,
|
||||
gDefaultConfig.trackingMode
|
||||
gDefaultConfig.trackingMode,
|
||||
""
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -499,6 +489,7 @@ LayerConfig gStatusHUDConfig = LayerConfig
|
|||
gDefaultConfig.myGUIViewSize,
|
||||
SizingMode::Auto,
|
||||
TrackingMode::HudLeftHand,
|
||||
""
|
||||
};
|
||||
|
||||
LayerConfig gPopupConfig = LayerConfig
|
||||
|
@ -514,6 +505,7 @@ LayerConfig gPopupConfig = LayerConfig
|
|||
gDefaultConfig.myGUIViewSize,
|
||||
SizingMode::Auto,
|
||||
TrackingMode::HudRightHand,
|
||||
""
|
||||
};
|
||||
|
||||
|
||||
|
@ -521,8 +513,7 @@ LayerConfig gPopupConfig = LayerConfig
|
|||
static std::map<std::string, LayerConfig&> gLayerConfigs =
|
||||
{
|
||||
{"StatusHUD", gStatusHUDConfig},
|
||||
//{"MinimapHUD", gMinimapHUDConfig},
|
||||
{"Popup", gPopupConfig},
|
||||
{"Tooltip", gPopupConfig},
|
||||
{"JournalBooks", gJournalBooksConfig},
|
||||
{"InventoryCompanionWindow", gInventoryCompanionWindowConfig},
|
||||
{"InventoryWindow", gInventoryWindowConfig},
|
||||
|
@ -532,13 +523,13 @@ static std::map<std::string, LayerConfig&> gLayerConfigs =
|
|||
{"DialogueWindow", gDialogueWindowConfig},
|
||||
{"MessageBox", gMessageBoxConfig},
|
||||
{"Windows", gDefaultWindowsConfig},
|
||||
{"Notification", gNotificationConfig}
|
||||
{"Notification", gNotificationConfig},
|
||||
};
|
||||
|
||||
static std::set<std::string> layerBlacklist =
|
||||
{
|
||||
"Overlay",
|
||||
"AdditiveOverlay"
|
||||
"AdditiveOverlay",
|
||||
};
|
||||
|
||||
void VRGUIManager::updateSideBySideLayers()
|
||||
|
@ -582,9 +573,6 @@ void VRGUIManager::insertLayer(const std::string& name)
|
|||
|
||||
layer->mGeometry->setUserData(new VRGUILayerUserData(mLayers[name]));
|
||||
|
||||
// Default new layer's pick to false
|
||||
// TODO: re-add widget->setLayerPick(false) somewhere;
|
||||
|
||||
if (config.sideBySide)
|
||||
{
|
||||
mSideBySideLayers.push_back(layer);
|
||||
|
@ -715,7 +703,7 @@ void VRGUIManager::updateTracking(void)
|
|||
layer.second->updateTracking(mHeadPose);
|
||||
}
|
||||
|
||||
void VRGUIManager::updateFocus()
|
||||
bool VRGUIManager::updateFocus()
|
||||
{
|
||||
auto* anim = MWVR::Environment::get().getPlayerAnimation();
|
||||
if (anim && anim->mPointerTarget.mHit)
|
||||
|
@ -728,12 +716,14 @@ void VRGUIManager::updateFocus()
|
|||
newFocusLayer = userData->mLayer.lock();
|
||||
}
|
||||
|
||||
if (newFocusLayer && newFocusLayer->mFilter != "Notification")
|
||||
if (newFocusLayer && newFocusLayer->mLayerName != "Notification")
|
||||
{
|
||||
setFocusLayer(newFocusLayer.get());
|
||||
computeGuiCursor(anim->mPointerTarget.mHitPointLocal);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void VRGUIManager::setFocusLayer(VRGUILayer* layer)
|
||||
|
|
|
@ -61,6 +61,7 @@ namespace MWVR
|
|||
osg::Vec2 myGUIViewSize; //!< Resizable elements are resized to this (fraction of full view)
|
||||
SizingMode sizingMode; //!< How to size the layer
|
||||
TrackingMode trackingMode; //!< Tracking mode
|
||||
std::string extraLayers; //!< Additional layers to draw (list separated by any non-alphabetic)
|
||||
|
||||
bool operator<(const LayerConfig& rhs) const { return priority < rhs.priority; }
|
||||
};
|
||||
|
@ -71,7 +72,7 @@ namespace MWVR
|
|||
VRGUILayer(
|
||||
osg::ref_ptr<osg::Group> geometryRoot,
|
||||
osg::ref_ptr<osg::Group> cameraRoot,
|
||||
std::string filter,
|
||||
std::string layerName,
|
||||
LayerConfig config,
|
||||
VRGUIManager* parent);
|
||||
~VRGUILayer();
|
||||
|
@ -95,7 +96,7 @@ namespace MWVR
|
|||
public:
|
||||
Pose mTrackedPose{};
|
||||
LayerConfig mConfig;
|
||||
std::string mFilter;
|
||||
std::string mLayerName;
|
||||
std::vector<MWGui::Layout*> mWidgets;
|
||||
osg::ref_ptr<osg::Group> mGeometryRoot;
|
||||
osg::ref_ptr<osg::Geometry> mGeometry{ new osg::Geometry };
|
||||
|
@ -123,8 +124,6 @@ namespace MWVR
|
|||
|
||||
~VRGUIManager(void);
|
||||
|
||||
void showGUIs(bool show);
|
||||
|
||||
void setVisible(MWGui::Layout*, bool visible);
|
||||
|
||||
void updateSideBySideLayers();
|
||||
|
@ -139,7 +138,7 @@ namespace MWVR
|
|||
|
||||
void updateTracking(void);
|
||||
|
||||
void updateFocus();
|
||||
bool updateFocus();
|
||||
|
||||
void setFocusLayer(VRGUILayer* layer);
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ namespace MWWorld
|
|||
throw std::runtime_error("class cannot block");
|
||||
}
|
||||
|
||||
void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, const osg::Vec3f& hitPosition, bool successful) const
|
||||
void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, const osg::Vec3f& hitPosition, bool successful, float hitStrength) const
|
||||
{
|
||||
throw std::runtime_error("class cannot be hit");
|
||||
}
|
||||
|
|
|
@ -131,11 +131,12 @@ namespace MWWorld
|
|||
/// @return True if the attack had a victim, regardless if hit was successful or not.
|
||||
/// (default implementation: throw an exception)
|
||||
|
||||
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const;
|
||||
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful, float hitStrength = 0.f) const;
|
||||
///< Alerts \a ptr that it's being hit for \a damage points to health if \a ishealth is
|
||||
/// true (else fatigue) by \a object (sword, arrow, etc). \a attacker specifies the
|
||||
/// actor responsible for the attack, and \a successful specifies if the hit is
|
||||
/// successful or not.
|
||||
/// successful or not. \a hitStrength is the fraction of max attack strength applied, and is
|
||||
/// used to determine haptic feedback intensity.
|
||||
|
||||
virtual void block (const Ptr& ptr) const;
|
||||
///< Play the appropriate sound for a blocked attack, depending on the currently equipped shield
|
||||
|
|
|
@ -277,8 +277,7 @@ namespace MWWorld
|
|||
#ifdef USE_OPENXR
|
||||
if (caster == MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||
{
|
||||
auto* anim = MWVR::Environment::get().getPlayerAnimation();
|
||||
osg::Matrix worldMatrix = osg::computeLocalToWorld(anim->mWeaponDirectionTransform->getParentalNodePaths()[0]);
|
||||
osg::Matrix worldMatrix = MWVR::Environment::get().getPlayerAnimation()->getWeaponTransformMatrix();
|
||||
orient = worldMatrix.getRotate();
|
||||
pos = worldMatrix.getTrans();
|
||||
}
|
||||
|
|
|
@ -1101,7 +1101,8 @@ namespace MWWorld
|
|||
MWWorld::Ptr World::getFacedObject()
|
||||
{
|
||||
#ifdef USE_OPENXR
|
||||
|
||||
// TODO: Rename this method to getTargetObject?
|
||||
// "getFacedObject" doesn't make sense with finger pointing.
|
||||
auto* anim = MWVR::Environment::get().getPlayerAnimation();
|
||||
if (anim && anim->mPointerTarget.mHit)
|
||||
return anim->mPointerTarget.mHitObject;
|
||||
|
@ -1163,12 +1164,8 @@ namespace MWWorld
|
|||
if (ptr == getPlayerPtr())
|
||||
{
|
||||
#ifdef USE_OPENXR
|
||||
// TODO: Configurable realistic fighting ?
|
||||
|
||||
// Use current aim of weapon to impact
|
||||
// TODO: Use bounding box of weapon instead ?
|
||||
auto* anim = MWVR::Environment::get().getPlayerAnimation();
|
||||
osg::Matrix worldMatrix = osg::computeLocalToWorld(anim->mWeaponDirectionTransform->getParentalNodePaths()[0]);
|
||||
osg::Matrix worldMatrix = MWVR::Environment::get().getPlayerAnimation()->getWeaponTransformMatrix();
|
||||
|
||||
auto result = mPhysics->getHitContact(ptr, worldMatrix.getTrans(), worldMatrix.getRotate(), distance, targets);
|
||||
if (!result.first.isEmpty())
|
||||
|
@ -3110,8 +3107,7 @@ namespace MWWorld
|
|||
#ifdef USE_OPENXR
|
||||
if (actor == MWMechanics::getPlayer())
|
||||
{
|
||||
auto* anim = MWVR::Environment::get().getPlayerAnimation();
|
||||
osg::Matrix worldMatrix = osg::computeLocalToWorld(anim->mWeaponDirectionTransform->getParentalNodePaths()[0]);
|
||||
osg::Matrix worldMatrix = MWVR::Environment::get().getPlayerAnimation()->getWeaponTransformMatrix();
|
||||
origin = worldMatrix.getTrans();
|
||||
orient = worldMatrix.getRotate();
|
||||
}
|
||||
|
|
|
@ -563,8 +563,10 @@ void GUICamera::collectDrawCalls(std::string filter)
|
|||
auto layer = myGUILayers->getLayer(i);
|
||||
auto name = layer->getName();
|
||||
|
||||
if (name == filter)
|
||||
if (filter.find(name) != std::string::npos)
|
||||
{
|
||||
layer->renderToTarget(this, mUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
end();
|
||||
|
|
|
@ -92,6 +92,7 @@ set(MYGUI_FILES
|
|||
openmw_text.skin.xml
|
||||
openmw_text_input.layout
|
||||
openmw_tooltips.layout
|
||||
openmw_tooltips_vr.layout
|
||||
openmw_trade_window.layout
|
||||
openmw_trade_window_vr.layout
|
||||
openmw_trainingwindow.layout
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
</Layer>
|
||||
<Layer name="Debug" overlapped="true" pick="true"/>
|
||||
<Layer name="Notification" overlapped="false" pick="false"/>
|
||||
<Layer name="Tooltip" overlapped="true" pick="true"/>
|
||||
<Layer name="Popup" overlapped="true" pick="true"/>
|
||||
<Layer name="DragAndDrop" overlapped="false" pick="false"/>
|
||||
<Layer name="LoadingScreen" overlapped="false" pick="true"/>
|
||||
|
|
322
files/mygui/openmw_tooltips_vr.layout
Normal file
322
files/mygui/openmw_tooltips_vr.layout
Normal file
|
@ -0,0 +1,322 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<MyGUI type="Layout">
|
||||
<Widget type="Widget" layer="Tooltip" position="0 0 300 300" name="_Main">
|
||||
|
||||
<!-- Dynamically constructed tooltip goes here -->
|
||||
<Widget type="Widget" skin="HUD_Box_NoTransp" position="0 0 300 300" align="Stretch" name="DynamicToolTipBox">
|
||||
</Widget>
|
||||
|
||||
<!-- Text tooltip, one line -->
|
||||
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 300 300" align="Stretch" name="TextToolTipOneLine">
|
||||
<Property key="AutoResize" value="true"/>
|
||||
<Property key="Padding" value="8"/>
|
||||
|
||||
<Widget type="AutoSizedTextBox" skin="SandText" position="8 8 284 284" align="Left Top" name="TextOneLine">
|
||||
<Property key="TextAlign" value="Left Top"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<!-- Text tooltip, multiline -->
|
||||
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 0" align="Stretch" name="TextToolTip">
|
||||
<Property key="AutoResize" value="true"/>
|
||||
<Property key="Padding" value="6"/>
|
||||
|
||||
<Widget type="AutoSizedEditBox" skin="SandText" position="8 8 268 0" align="Left Top" name="Text">
|
||||
<Property key="MultiLine" value="true"/>
|
||||
<Property key="WordWrap" value="true"/>
|
||||
<Property key="TextAlign" value="Left Top"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<!-- Faction tooltip -->
|
||||
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 0" align="Stretch" name="FactionToolTip">
|
||||
<Property key="AutoResize" value="true"/>
|
||||
<Property key="Padding" value="8"/>
|
||||
|
||||
<Widget type="AutoSizedEditBox" skin="SandText" position="8 8 436 0" align="Left Top" name="FactionText">
|
||||
<Property key="MultiLine" value="true"/>
|
||||
<Property key="WordWrap" value="true"/>
|
||||
<Property key="TextAlign" value="Left Top"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<!-- Race tooltip -->
|
||||
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 56" align="Stretch" name="RaceToolTip">
|
||||
<Property key="AutoResize" value="true"/>
|
||||
<Property key="Padding" value="8"/>
|
||||
<Property key="Spacing" value="8"/>
|
||||
|
||||
<Widget type="AutoSizedTextBox" skin="NormalText" position="8 8 0 18" align="Left Top" name="CenteredCaption">
|
||||
<Property key="TextAlign" value="Center"/>
|
||||
<UserString key="HStretch" value="true"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="AutoSizedEditBox" skin="SandText" position="8 30 430 18" align="Left Top" name="CenteredCaptionText">
|
||||
<Property key="MultiLine" value="true"/>
|
||||
<Property key="Shrink" value="true"/>
|
||||
<Property key="WordWrap" value="true"/>
|
||||
<Property key="TextAlign" value="Left Top"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<!-- Specialization tooltip -->
|
||||
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 56" align="Stretch" name="SpecializationToolTip">
|
||||
<Property key="AutoResize" value="true"/>
|
||||
<Property key="Padding" value="8"/>
|
||||
|
||||
<Widget type="AutoSizedTextBox" skin="NormalText" position="8 8 140 18" align="Left Top" name="Caption">
|
||||
<Property key="TextAlign" value="Center"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="AutoSizedEditBox" skin="SandText" position="0 30 140 18" align="Left Top" name="ColumnText">
|
||||
<Property key="MultiLine" value="true"/>
|
||||
<Property key="WordWrap" value="true"/>
|
||||
<Property key="TextAlign" value="Left Top"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<!-- Class tooltip -->
|
||||
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 78" align="Stretch" name="ClassToolTip">
|
||||
<Property key="AutoResize" value="true"/>
|
||||
<Property key="Padding" value="8"/>
|
||||
<Property key="Spacing" value="8"/>
|
||||
|
||||
<Widget type="AutoSizedTextBox" skin="NormalText" position="8 8 0 18" align="Left Top HStretch" name="ClassName">
|
||||
<Property key="TextAlign" value="Center"/>
|
||||
<UserString key="HStretch" value="true"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="AutoSizedEditBox" skin="SandText" position="8 30 430 18" align="Left Top" name="ClassDescription">
|
||||
<Property key="MultiLine" value="true"/>
|
||||
<Property key="Shrink" value="true"/>
|
||||
<Property key="WordWrap" value="true"/>
|
||||
<Property key="TextAlign" value="Left Top"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="AutoSizedTextBox" skin="SandText" position="8 52 0 18" align="Left Bottom" name="ClassSpecialisation">
|
||||
<Property key="TextAlign" value="Center"/>
|
||||
<UserString key="HStretch" value="true"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<!-- Hand-to-hand tooltip -->
|
||||
<Widget type="HBox" skin="HUD_Box_NoTransp" position="0 0 300 300" align="Stretch" name="HandToHandToolTip">
|
||||
<Property key="AutoResize" value="true"/>
|
||||
<Property key="Padding" value="6"/>
|
||||
|
||||
<Widget type="VBox">
|
||||
<UserString key="VStretch" value="true"/>
|
||||
<Widget type="ImageBox" skin="ImageBox" position="8 8 32 32" align="Left Top" name="HandToHandImage"/>
|
||||
<Widget type="Spacer"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="AutoSizedTextBox" skin="SandText" position="44 8 248 284" align="Left Top" name="HandToHandText">
|
||||
<Property key="TextAlign" value="Center"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<!-- Health/Magicka/Fatigue tooltip -->
|
||||
<Widget type="HBox" skin="HUD_Box_NoTransp" position="0 0 0 0" align="Stretch" name="HealthToolTip">
|
||||
<Property key="AutoResize" value="true"/>
|
||||
<Property key="Padding" value="14"/>
|
||||
<Property key="Spacing" value="8"/>
|
||||
|
||||
<Widget type="VBox">
|
||||
<UserString key="VStretch" value="true"/>
|
||||
<Widget type="ImageBox" skin="ImageBox" position="8 8 32 32" align="Left Top" name="HealthImage"/>
|
||||
<Widget type="Spacer"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="AutoSizedEditBox" skin="SandText" position="44 8 392 0" align="Left Top" name="HealthDescription">
|
||||
<Property key="MultiLine" value="true"/>
|
||||
<Property key="Shrink" value="true"/>
|
||||
<Property key="WordWrap" value="true"/>
|
||||
<Property key="TextAlign" value="Left Top"/>
|
||||
<UserString key="HStretch" value="true"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<!-- Attribute tooltip -->
|
||||
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 0" align="Stretch" name="AttributeToolTip">
|
||||
<Property key="AutoResize" value="true"/>
|
||||
<Property key="Padding" value="8"/>
|
||||
<Property key="Spacing" value="8"/>
|
||||
|
||||
<Widget type="HBox">
|
||||
<UserString key="HStretch" value="true"/>
|
||||
<Property key="Spacing" value="8"/>
|
||||
<Widget type="ImageBox" skin="ImageBox" position="8 8 32 32" align="Left Top" name="AttributeImage"/>
|
||||
|
||||
<Widget type="AutoSizedTextBox" skin="NormalText" position="44 8 0 32" align="Left Top" name="AttributeName">
|
||||
<Property key="TextAlign" value="Left VCenter"/>
|
||||
<UserString key="HStretch" value="true"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<Widget type="AutoSizedEditBox" skin="SandText" position="8 44 436 248" align="Left Top" name="AttributeDescription">
|
||||
<Property key="MultiLine" value="true"/>
|
||||
<Property key="Shrink" value="true"/>
|
||||
<Property key="WordWrap" value="true"/>
|
||||
<Property key="TextAlign" value="Left Top"/>
|
||||
<UserString key="HStretch" value="true"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<!-- Skill tooltip -->
|
||||
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 98" align="Stretch" name="SkillToolTip">
|
||||
<Property key="AutoResize" value="true"/>
|
||||
<Property key="Padding" value="8"/>
|
||||
|
||||
<Widget type="HBox">
|
||||
<UserString key="HStretch" value="true"/>
|
||||
<Property key="Spacing" value="8"/>
|
||||
<Widget type="ImageBox" skin="ImageBox" position="8 8 32 32" align="Left Top" name="SkillImage"/>
|
||||
|
||||
<Widget type="VBox">
|
||||
<Widget type="AutoSizedTextBox" skin="NormalText" position="44 8 0 16" align="Left Top" name="SkillName">
|
||||
<Property key="TextAlign" value="Left"/>
|
||||
<UserString key="HStretch" value="true"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="AutoSizedTextBox" skin="SandText" position="44 24 0 16" align="Left Top" name="SkillAttribute">
|
||||
<Property key="TextAlign" value="Left"/>
|
||||
<UserString key="HStretch" value="true"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<Widget type="Widget" skin="" position="0 0 0 2" align="Left Top" />
|
||||
|
||||
<Widget type="AutoSizedEditBox" skin="SandText" position="8 44 430 0" align="Left Top" name="SkillDescription">
|
||||
<Property key="MultiLine" value="true"/>
|
||||
<Property key="Shrink" value="true"/>
|
||||
<Property key="WordWrap" value="true"/>
|
||||
<Property key="TextAlign" value="Left Top"/>
|
||||
<Property key="Spacing" value="28"/>
|
||||
<UserString key="HStretch" value="true"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="Widget" skin="" position="0 0 0 2" align="Left Top" />
|
||||
|
||||
<Widget type="AutoSizedTextBox" skin="SandText" position="8 48 200 18" align="Left Bottom" name="SkillMaxed">
|
||||
<Property key="Caption" value="#{sSkillMaxReached}"/>
|
||||
<Property key="TextAlign" value="Center"/>
|
||||
<UserString key="HStretch" value="true"/>
|
||||
</Widget>
|
||||
<Widget type="VBox" name="SkillProgressVBox">
|
||||
<Widget type="AutoSizedTextBox" skin="NormalText" position="8 48 200 18" align="Left Bottom">
|
||||
<Property key="Caption" value="#{sSkillProgress}"/>
|
||||
<Property key="TextAlign" value="Center"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="ProgressBar" skin="MW_Progress_Red" position="50 70 200 20" align="HCenter Bottom" name="SkillProgress">
|
||||
<Widget type="TextBox" skin="ProgressText" position="0 0 200 16" align="Stretch" name="SkillProgressText">
|
||||
<Property key="TextAlign" value="Center"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<!-- Skill tooltip (without progress bar) -->
|
||||
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 52" align="Stretch" name="SkillNoProgressToolTip">
|
||||
<Property key="AutoResize" value="true"/>
|
||||
<Property key="Padding" value="8"/>
|
||||
|
||||
<Widget type="HBox">
|
||||
<UserString key="HStretch" value="true"/>
|
||||
<Property key="Spacing" value="8"/>
|
||||
|
||||
<Widget type="ImageBox" skin="ImageBox" position="8 8 32 32" align="Left Top" name="SkillNoProgressImage"/>
|
||||
|
||||
<Widget type="VBox">
|
||||
<Widget type="AutoSizedEditBox" skin="NormalText" position="44 8 0 16" align="Left Top" name="SkillNoProgressName">
|
||||
<Property key="TextAlign" value="Left"/>
|
||||
<UserString key="HStretch" value="true"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="AutoSizedTextBox" skin="SandText" position="44 24 0 16" align="Left Top" name="SkillNoProgressAttribute">
|
||||
<Property key="TextAlign" value="Left"/>
|
||||
<UserString key="HStretch" value="true"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<Widget type="Widget" skin="" position="0 0 0 2" align="Left Top" />
|
||||
|
||||
<Widget type="AutoSizedEditBox" skin="SandText" position="8 44 430 0" align="Left Top" name="SkillNoProgressDescription">
|
||||
<Property key="MultiLine" value="true"/>
|
||||
<Property key="WordWrap" value="true"/>
|
||||
<Property key="TextAlign" value="Left Top"/>
|
||||
<Property key="Spacing" value="28"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<!-- Level tooltip -->
|
||||
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 300 58" align="HStretch" name="LevelToolTip">
|
||||
<Property key="AutoResize" value="true"/>
|
||||
<Property key="Padding" value="8"/>
|
||||
|
||||
<Widget type="AutoSizedTextBox" skin="NormalText" position="8 8 284 18" align="Left Top">
|
||||
<Property key="Caption" value="#{sLevelProgress}"/>
|
||||
<Property key="TextAlign" value="Center"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="ProgressBar" skin="MW_Progress_Red" position="50 30 180 20" align="HCenter Bottom" name="LevelProgress">
|
||||
<Widget type="TextBox" skin="ProgressText" position="0 0 180 20" align="HCenter" name="LevelProgressText">
|
||||
<Property key="TextAlign" value="HCenter Top"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<!-- Birthsign tooltip -->
|
||||
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 300 300" align="Stretch" name="BirthSignToolTip">
|
||||
<Property key="AutoResize" value="true"/>
|
||||
<Property key="Padding" value="8"/>
|
||||
|
||||
<!-- Birthsign image -->
|
||||
<Widget type="Widget" skin="MW_Box" position="18 13 263 137" align="Top HCenter">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="2 2 259 133" name="BirthSignImage" align="Left Top"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="AutoSizedTextBox" skin="NormalText" position="8 154 284 138" align="Top" name="BirthSignText">
|
||||
<Property key="TextAlign" value="Top HCenter"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<!-- Magic effect tooltip -->
|
||||
<Widget type="VBox" skin="HUD_Box_NoTransp" position="0 0 0 0" align="Stretch" name="MagicEffectToolTip">
|
||||
<Property key="AutoResize" value="true"/>
|
||||
<Property key="Padding" value="8"/>
|
||||
|
||||
<Widget type="HBox">
|
||||
<UserString key="HStretch" value="true"/>
|
||||
<Property key="Spacing" value="8"/>
|
||||
|
||||
<Widget type="ImageBox" skin="ImageBox" position="8 8 32 32" align="Left Top" name="MagicEffectImage"/>
|
||||
|
||||
<Widget type="VBox">
|
||||
<Widget type="AutoSizedTextBox" skin="NormalText" position="44 8 404 16" align="Left Top" name="MagicEffectName">
|
||||
<Property key="TextAlign" value="Left"/>
|
||||
<UserString key="HStretch" value="true"/>
|
||||
</Widget>
|
||||
|
||||
|
||||
<Widget type="AutoSizedTextBox" skin="SandText" position="44 24 404 16" align="Left Top" name="MagicEffectSchool">
|
||||
<Property key="TextAlign" value="Left"/>
|
||||
<UserString key="HStretch" value="true"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
</Widget>
|
||||
|
||||
<Widget type="AutoSizedEditBox" skin="SandText" position="8 44 436 0" align="Left Top" name="MagicEffectDescription">
|
||||
<Property key="MultiLine" value="true"/>
|
||||
<Property key="WordWrap" value="true"/>
|
||||
<Property key="TextAlign" value="Left Top"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
</Widget>
|
||||
</MyGUI>
|
||||
|
Loading…
Reference in a new issue