1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-02-28 16:39:39 +00:00

Merge branch 'master' into HEAD

This commit is contained in:
elsid 2019-08-27 20:49:12 +02:00
commit cb0a609d59
No known key found for this signature in database
GPG key ID: B845CB9FEE18AB40
55 changed files with 469 additions and 365 deletions

View file

@ -98,6 +98,7 @@
Bug #4984: "Friendly hits" feature should be used only for player's followers Bug #4984: "Friendly hits" feature should be used only for player's followers
Bug #4989: Object dimension-dependent VFX scaling behavior is inconsistent Bug #4989: Object dimension-dependent VFX scaling behavior is inconsistent
Bug #4990: Dead bodies prevent you from hitting Bug #4990: Dead bodies prevent you from hitting
Bug #4991: Jumping occasionally takes too much fatigue
Bug #4999: Drop instruction behaves differently from vanilla Bug #4999: Drop instruction behaves differently from vanilla
Bug #5001: Possible data race in the Animation::setAlpha() Bug #5001: Possible data race in the Animation::setAlpha()
Bug #5004: Werewolves shield their eyes during storm Bug #5004: Werewolves shield their eyes during storm
@ -115,11 +116,13 @@
Bug #5069: Blocking creatures' attacks doesn't degrade shields Bug #5069: Blocking creatures' attacks doesn't degrade shields
Bug #5074: Paralyzed actors greet the player Bug #5074: Paralyzed actors greet the player
Bug #5075: Enchanting cast style can be changed if there's no object Bug #5075: Enchanting cast style can be changed if there's no object
Bug #5078: DisablePlayerLooking is broken
Bug #5082: Scrolling with controller in GUI mode is broken Bug #5082: Scrolling with controller in GUI mode is broken
Bug #5089: Swimming/Underwater creatures only swim around ground level Bug #5089: Swimming/Underwater creatures only swim around ground level
Bug #5092: NPCs with enchanted weapons play sound when out of charges Bug #5092: NPCs with enchanted weapons play sound when out of charges
Bug #5093: Hand to hand sound plays on knocked out enemies Bug #5093: Hand to hand sound plays on knocked out enemies
Bug #5099: Non-swimming enemies will enter water if player is water walking Bug #5099: Non-swimming enemies will enter water if player is water walking
Bug #5103: Sneaking state behavior is still inconsistent
Bug #5104: Black Dart's enchantment doesn't trigger at low Enchant levels Bug #5104: Black Dart's enchantment doesn't trigger at low Enchant levels
Bug #5105: NPCs start combat with werewolves from any distance Bug #5105: NPCs start combat with werewolves from any distance
Bug #5106: Still can jump even when encumbered Bug #5106: Still can jump even when encumbered
@ -127,6 +130,9 @@
Bug #5112: Insufficient magicka for current spell not reflected on HUD icon Bug #5112: Insufficient magicka for current spell not reflected on HUD icon
Bug #5123: Script won't run on respawn Bug #5123: Script won't run on respawn
Bug #5124: Arrow remains attached to actor if pulling animation was cancelled 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 #5137: Textures with Clamp Mode set to Clamp instead of Wrap are too dark outside the boundaries
Feature #1774: Handle AvoidNode Feature #1774: Handle AvoidNode
Feature #2229: Improve pathfinding AI Feature #2229: Improve pathfinding AI
Feature #3025: Analogue gamepad movement controls Feature #3025: Analogue gamepad movement controls
@ -147,6 +153,7 @@
Feature #4812: Support NiSwitchNode Feature #4812: Support NiSwitchNode
Feature #4836: Daytime node switch Feature #4836: Daytime node switch
Feature #4859: Make water reflections more configurable Feature #4859: Make water reflections more configurable
Feature #4882: Support for NiPalette node
Feature #4887: Add openmw command option to set initial random seed Feature #4887: Add openmw command option to set initial random seed
Feature #4890: Make Distant Terrain configurable Feature #4890: Make Distant Terrain configurable
Feature #4958: Support eight blood types Feature #4958: Support eight blood types
@ -162,6 +169,7 @@
Feature #5036: Allow scripted faction leaving Feature #5036: Allow scripted faction leaving
Feature #5046: Gamepad thumbstick cursor speed Feature #5046: Gamepad thumbstick cursor speed
Feature #5051: Provide a separate textures for scrollbars Feature #5051: Provide a separate textures for scrollbars
Feature #5091: Human-readable light source duration
Feature #5094: Unix like console hotkeys Feature #5094: Unix like console hotkeys
Feature #5098: Allow user controller bindings Feature #5098: Allow user controller bindings
Feature #5121: Handle NiTriStrips and NiTriStripsData Feature #5121: Handle NiTriStrips and NiTriStripsData

View file

@ -9,6 +9,7 @@
#include <components/esm/cellid.hpp> #include <components/esm/cellid.hpp>
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "../mwworld/doorstate.hpp"
#include "../mwrender/rendermode.hpp" #include "../mwrender/rendermode.hpp"
@ -407,7 +408,6 @@ namespace MWBase
virtual void togglePreviewMode(bool enable) = 0; virtual void togglePreviewMode(bool enable) = 0;
virtual bool toggleVanityMode(bool enable) = 0; virtual bool toggleVanityMode(bool enable) = 0;
virtual void allowVanityMode(bool allow) = 0; virtual void allowVanityMode(bool allow) = 0;
virtual void togglePlayerLooking(bool enable) = 0;
virtual void changeVanityModeScale(float factor) = 0; virtual void changeVanityModeScale(float factor) = 0;
virtual bool vanityRotateCamera(float * rot) = 0; virtual bool vanityRotateCamera(float * rot) = 0;
virtual void setCameraDistance(float dist, bool adjust = false, bool override = true)=0; virtual void setCameraDistance(float dist, bool adjust = false, bool override = true)=0;
@ -420,7 +420,7 @@ namespace MWBase
/// update movement state of a non-teleport door as specified /// update movement state of a non-teleport door as specified
/// @param state see MWClass::setDoorState /// @param state see MWClass::setDoorState
/// @note throws an exception when invoked on a teleport door /// @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;
virtual void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector<MWWorld::Ptr> &actors) = 0; ///< get a list of actors standing on \a object virtual void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector<MWWorld::Ptr> &actors) = 0; ///< get a list of actors standing on \a object
virtual bool getPlayerStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is standing on \a object virtual bool getPlayerStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is standing on \a object

View file

@ -103,7 +103,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Activator> *ref = ptr.get<ESM::Activator>(); const MWWorld::LiveCellRef<ESM::Activator> *ref = ptr.get<ESM::Activator>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
std::string text; std::string text;
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) if (MWBase::Environment::get().getWindowManager()->getFullHelp())

View file

@ -105,7 +105,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>(); const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

View file

@ -211,7 +211,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>(); const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

View file

@ -121,7 +121,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>(); const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

View file

@ -169,7 +169,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>(); const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

View file

@ -271,7 +271,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>(); const MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName; info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName);
std::string text; std::string text;
int lockLevel = ptr.getCellRef().getLockLevel(); int lockLevel = ptr.getCellRef().getLockLevel();

View file

@ -586,7 +586,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>(); const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName; info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName);
std::string text; std::string text;
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) if (MWBase::Environment::get().getWindowManager()->getFullHelp())

View file

@ -34,7 +34,7 @@ namespace MWClass
class DoorCustomData : public MWWorld::CustomData class DoorCustomData : public MWWorld::CustomData
{ {
public: public:
int mDoorState; // 0 = nothing, 1 = opening, 2 = closing MWWorld::DoorState mDoorState;
virtual MWWorld::CustomData *clone() const; virtual MWWorld::CustomData *clone() const;
@ -71,7 +71,7 @@ namespace MWClass
if (ptr.getRefData().getCustomData()) if (ptr.getRefData().getCustomData())
{ {
const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); 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); MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState);
} }
@ -201,12 +201,12 @@ namespace MWClass
{ {
// animated door // animated door
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionDoor(ptr)); std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionDoor(ptr));
int doorstate = getDoorState(ptr); const auto doorState = getDoorState(ptr);
bool opening = true; bool opening = true;
float doorRot = ptr.getRefData().getPosition().rot[2] - ptr.getCellRef().getPosition().rot[2]; float doorRot = ptr.getRefData().getPosition().rot[2] - ptr.getCellRef().getPosition().rot[2];
if (doorstate == 1) if (doorState == MWWorld::DoorState::Opening)
opening = false; opening = false;
if (doorstate == 0 && doorRot != 0) if (doorState == MWWorld::DoorState::Idle && doorRot != 0)
opening = false; opening = false;
if (opening) if (opening)
@ -293,7 +293,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Door> *ref = ptr.get<ESM::Door>(); const MWWorld::LiveCellRef<ESM::Door> *ref = ptr.get<ESM::Door>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName; info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName);
std::string text; std::string text;
@ -365,20 +365,20 @@ namespace MWClass
{ {
std::unique_ptr<DoorCustomData> data(new DoorCustomData); std::unique_ptr<DoorCustomData> data(new DoorCustomData);
data->mDoorState = 0; data->mDoorState = MWWorld::DoorState::Idle;
ptr.getRefData().setCustomData(data.release()); 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()) if (!ptr.getRefData().getCustomData())
return 0; return MWWorld::DoorState::Idle;
const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();
return customData.mDoorState; 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()) if (ptr.getCellRef().getTeleport())
throw std::runtime_error("load doors can't be moved"); throw std::runtime_error("load doors can't be moved");
@ -396,7 +396,7 @@ namespace MWClass
DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();
const ESM::DoorState& state2 = dynamic_cast<const ESM::DoorState&>(state); 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 void Door::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const
@ -409,7 +409,7 @@ namespace MWClass
const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData(); const DoorCustomData& customData = ptr.getRefData().getCustomData()->asDoorCustomData();
ESM::DoorState& state2 = dynamic_cast<ESM::DoorState&>(state); ESM::DoorState& state2 = dynamic_cast<ESM::DoorState&>(state);
state2.mDoorState = customData.mDoorState; state2.mDoorState = static_cast<int>(customData.mDoorState);
} }
} }

View file

@ -59,10 +59,9 @@ namespace MWClass
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
/// 0 = nothing, 1 = opening, 2 = closing virtual MWWorld::DoorState getDoorState (const MWWorld::ConstPtr &ptr) const;
virtual int getDoorState (const MWWorld::ConstPtr &ptr) const;
/// This does not actually cause the door to move. Use World::activateDoor instead. /// 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) virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)

View file

@ -117,7 +117,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>(); const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

View file

@ -151,18 +151,14 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>(); const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;
if (Settings::Manager::getBool("show effect duration","Game")) // Don't show duration for infinite light sources.
{ if (Settings::Manager::getBool("show effect duration","Game") && ptr.getClass().getRemainingUsageTime(ptr) != -1)
// -1 is infinite light source, so duration makes no sense here. Other negative values are treated as 0. text += MWGui::ToolTips::getDurationString(ptr.getClass().getRemainingUsageTime(ptr), "\n#{sDuration}");
float remainingTime = ptr.getClass().getRemainingUsageTime(ptr);
if (remainingTime != -1.0f)
text += "\n#{sDuration}: " + MWGui::ToolTips::toString(std::max(0.f, remainingTime));
}
text += MWGui::ToolTips::getWeightString(ref->mBase->mData.mWeight, "#{sWeight}"); text += MWGui::ToolTips::getWeightString(ref->mBase->mData.mWeight, "#{sWeight}");
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");

View file

@ -116,7 +116,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>(); const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

View file

@ -159,7 +159,7 @@ namespace MWClass
else // gold displays its count also if it's 1. else // gold displays its count also if it's 1.
countString = " (" + std::to_string(count) + ")"; countString = " (" + std::to_string(count) + ")";
info.caption = ref->mBase->mName + countString; info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + countString;
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
if (ref->mRef.getSoul() != "") if (ref->mRef.getSoul() != "")

View file

@ -940,10 +940,9 @@ namespace MWClass
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr); const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
bool swimming = world->isSwimming(ptr); bool swimming = world->isSwimming(ptr);
bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr);
bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run);
bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr); bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr);
bool sneaking = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak);
sneaking = sneaking && (inair || MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr));
bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run);
running = running && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr)); running = running && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr));
float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()*
@ -1071,11 +1070,11 @@ namespace MWClass
bool fullHelp = MWBase::Environment::get().getWindowManager()->getFullHelp(); bool fullHelp = MWBase::Environment::get().getWindowManager()->getFullHelp();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = getName(ptr); info.caption = MyGUI::TextIterator::toTagsString(getName(ptr));
if(fullHelp && ptr.getRefData().getCustomData() && ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats.isWerewolf()) if(fullHelp && ptr.getRefData().getCustomData() && ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats.isWerewolf())
{ {
info.caption += " ("; info.caption += " (";
info.caption += ref->mBase->mName; info.caption += MyGUI::TextIterator::toTagsString(ref->mBase->mName);
info.caption += ")"; info.caption += ")";
} }

View file

@ -110,7 +110,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Potion> *ref = ptr.get<ESM::Potion>(); const MWWorld::LiveCellRef<ESM::Potion> *ref = ptr.get<ESM::Potion>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

View file

@ -116,7 +116,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>(); const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

View file

@ -117,7 +117,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>(); const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

View file

@ -168,7 +168,7 @@ namespace MWClass
const ESM::WeaponType* weaponType = MWMechanics::getWeaponType(ref->mBase->mData.mType); const ESM::WeaponType* weaponType = MWMechanics::getWeaponType(ref->mBase->mData.mType);
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = ref->mBase->mName + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();

View file

@ -137,27 +137,8 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->getGameSettingString("spoint", "") ); MWBase::Environment::get().getWindowManager()->getGameSettingString("spoint", "") );
} }
} }
if (effectInfo.mRemainingTime > -1 && if (effectInfo.mRemainingTime > -1 && Settings::Manager::getBool("show effect duration","Game"))
Settings::Manager::getBool("show effect duration","Game")) { sourcesDescription += MWGui::ToolTips::getDurationString(effectInfo.mRemainingTime, " #{sDuration}");
sourcesDescription += " #{sDuration}: ";
float duration = effectInfo.mRemainingTime;
if (duration > 3600)
{
int hour = duration / 3600;
duration -= hour*3600;
sourcesDescription += MWGui::ToolTips::toString(hour) + "h";
}
if (duration > 60)
{
int minute = duration / 60;
duration -= minute*60;
sourcesDescription += MWGui::ToolTips::toString(minute) + "m";
}
if (duration > 0.1)
{
sourcesDescription += MWGui::ToolTips::toString(duration) + "s";
}
}
addNewLine = true; addNewLine = true;
} }

View file

@ -667,6 +667,60 @@ namespace MWGui
return ret; return ret;
} }
std::string ToolTips::getDurationString(float duration, const std::string& prefix)
{
std::string ret;
ret = prefix + ": ";
if (duration < 1.f)
{
ret += "0 s";
return ret;
}
constexpr int secondsPerMinute = 60; // 60 seconds
constexpr int secondsPerHour = secondsPerMinute * 60; // 60 minutes
constexpr int secondsPerDay = secondsPerHour * 24; // 24 hours
constexpr int secondsPerMonth = secondsPerDay * 30; // 30 days
constexpr int secondsPerYear = secondsPerDay * 365;
int fullDuration = static_cast<int>(duration);
int units = 0;
int years = fullDuration / secondsPerYear;
int months = fullDuration % secondsPerYear / secondsPerMonth;
int days = fullDuration % secondsPerYear % secondsPerMonth / secondsPerDay; // Because a year is not exactly 12 "months"
int hours = fullDuration % secondsPerDay / secondsPerHour;
int minutes = fullDuration % secondsPerHour / secondsPerMinute;
int seconds = fullDuration % secondsPerMinute;
if (years)
{
units++;
ret += toString(years) + " y ";
}
if (months)
{
units++;
ret += toString(months) + " mo ";
}
if (units < 2 && days)
{
units++;
ret += toString(days) + " d ";
}
if (units < 2 && hours)
{
units++;
ret += toString(hours) + " h ";
}
if (units >= 2)
return ret;
if (minutes)
ret += toString(minutes) + " min ";
if (seconds)
ret += toString(seconds) + " s ";
return ret;
}
bool ToolTips::toggleFullHelp() bool ToolTips::toggleFullHelp()
{ {
mFullHelp = !mFullHelp; mFullHelp = !mFullHelp;

View file

@ -84,6 +84,9 @@ namespace MWGui
static std::string getCellRefString(const MWWorld::CellRef& cellref); static std::string getCellRefString(const MWWorld::CellRef& cellref);
///< Returns a string containing debug tooltip information about the given cellref. ///< Returns a string containing debug tooltip information about the given cellref.
static std::string getDurationString (float duration, const std::string& prefix);
///< Returns duration as two largest time units, rounded down. Note: not localized; no line break.
// these do not create an actual tooltip, but they fill in the data that is required so the tooltip // these do not create an actual tooltip, but they fill in the data that is required so the tooltip
// system knows what to show in case this widget is hovered // system knows what to show in case this widget is hovered
static void createSkillToolTip(MyGUI::Widget* widget, int skillId); static void createSkillToolTip(MyGUI::Widget* widget, int skillId);

View file

@ -202,6 +202,11 @@ namespace MWInput
void InputManager::handleGuiArrowKey(int action) 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()) if (SDL_IsTextInputActive())
return; return;
@ -229,13 +234,10 @@ namespace MWInput
MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false); 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. // Presumption of GUI mode will be removed in the future.
// MyGUI KeyCodes *may* change. // MyGUI KeyCodes *may* change.
// Currently button releases are ignored.
if (release)
return false;
MyGUI::KeyCode key = MyGUI::KeyCode::None; MyGUI::KeyCode key = MyGUI::KeyCode::None;
switch (arg.button) switch (arg.button)
@ -386,9 +388,6 @@ namespace MWInput
case A_GameMenu: case A_GameMenu:
toggleMainMenu (); toggleMainMenu ();
break; break;
case A_OptionsMenu:
toggleOptionsMenu();
break;
case A_Screenshot: case A_Screenshot:
screenshot(); screenshot();
break; break;
@ -406,8 +405,7 @@ namespace MWInput
case A_MoveRight: case A_MoveRight:
case A_MoveForward: case A_MoveForward:
case A_MoveBackward: case A_MoveBackward:
// Temporary shut-down of this function until deemed necessary. handleGuiArrowKey(action);
//handleGuiArrowKey(action);
break; break;
case A_Journal: case A_Journal:
toggleJournal (); toggleJournal ();
@ -594,7 +592,7 @@ namespace MWInput
rot[2] = xAxis * (dt * 100.0f) * 10.0f * mCameraSensitivity * (1.0f/256.f) * (mInvertX ? -1 : 1); rot[2] = xAxis * (dt * 100.0f) * 10.0f * mCameraSensitivity * (1.0f/256.f) * (mInvertX ? -1 : 1);
// Only actually turn player when we're not in vanity mode // Only actually turn player when we're not in vanity mode
if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot)) if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && mControlSwitch["playerlooking"])
{ {
mPlayer->yaw(rot[2]); mPlayer->yaw(rot[2]);
mPlayer->pitch(rot[0]); mPlayer->pitch(rot[0]);
@ -832,9 +830,6 @@ namespace MWInput
void InputManager::toggleControlSwitch (const std::string& sw, bool value) void InputManager::toggleControlSwitch (const std::string& sw, bool value)
{ {
if (mControlSwitch[sw] == value) {
return;
}
/// \note 7 switches at all, if-else is relevant /// \note 7 switches at all, if-else is relevant
if (sw == "playercontrols" && !value) { if (sw == "playercontrols" && !value) {
mPlayer->setLeftRight(0); mPlayer->setLeftRight(0);
@ -846,8 +841,8 @@ namespace MWInput
mPlayer->setUpDown(0); mPlayer->setUpDown(0);
} else if (sw == "vanitymode") { } else if (sw == "vanitymode") {
MWBase::Environment::get().getWorld()->allowVanityMode(value); MWBase::Environment::get().getWorld()->allowVanityMode(value);
} else if (sw == "playerlooking") { } else if (sw == "playerlooking" && !value) {
MWBase::Environment::get().getWorld()->togglePlayerLooking(value); MWBase::Environment::get().getWorld()->rotateObject(mPlayer->getPlayer(), 0.f, 0.f, 0.f);
} }
mControlSwitch[sw] = value; mControlSwitch[sw] = value;
} }
@ -981,7 +976,7 @@ namespace MWInput
rot[2] = -x; rot[2] = -x;
// Only actually turn player when we're not in vanity mode // Only actually turn player when we're not in vanity mode
if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot)) if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && mControlSwitch["playerlooking"])
{ {
mPlayer->yaw(x); mPlayer->yaw(x);
mPlayer->pitch(y); mPlayer->pitch(y);
@ -1005,9 +1000,9 @@ namespace MWInput
mJoystickLastUsed = true; mJoystickLastUsed = true;
if (MWBase::Environment::get().getWindowManager()->isGuiMode()) if (MWBase::Environment::get().getWindowManager()->isGuiMode())
{ {
if (gamepadToGuiControl(arg, false)) if (gamepadToGuiControl(arg))
return; return;
else if (mGamepadGuiCursorEnabled) if (mGamepadGuiCursorEnabled)
{ {
// Temporary mouse binding until keyboard controls are available: // Temporary mouse binding until keyboard controls are available:
if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click. if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click.
@ -1048,9 +1043,7 @@ namespace MWInput
mJoystickLastUsed = true; mJoystickLastUsed = true;
if (MWBase::Environment::get().getWindowManager()->isGuiMode()) if (MWBase::Environment::get().getWindowManager()->isGuiMode())
{ {
if (gamepadToGuiControl(arg, true)) if (mGamepadGuiCursorEnabled)
return;
else if (mGamepadGuiCursorEnabled)
{ {
// Temporary mouse binding until keyboard controls are available: // Temporary mouse binding until keyboard controls are available:
if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click. if (arg.button == SDL_CONTROLLER_BUTTON_A) // We'll pretend that A is left click.
@ -1149,37 +1142,19 @@ namespace MWInput
} }
if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) 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; return;
} }
if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) //No open GUIs, open up the MainMenu
return; {
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); }
bool inGame = MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_NoGame; else //Close current GUI
{
if ((inGame && mode == MWGui::GM_MainMenu) || mode == MWGui::GM_Settings) MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
MWBase::Environment::get().getWindowManager()->popGuiMode(); }
if (inGame && mode != MWGui::GM_Settings)
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Settings);
} }
void InputManager::quickLoad() { void InputManager::quickLoad() {
@ -1534,7 +1509,6 @@ namespace MWInput
defaultButtonBindings[A_TogglePOV] = SDL_CONTROLLER_BUTTON_RIGHTSTICK; defaultButtonBindings[A_TogglePOV] = SDL_CONTROLLER_BUTTON_RIGHTSTICK;
defaultButtonBindings[A_Inventory] = SDL_CONTROLLER_BUTTON_B; defaultButtonBindings[A_Inventory] = SDL_CONTROLLER_BUTTON_B;
defaultButtonBindings[A_GameMenu] = SDL_CONTROLLER_BUTTON_START; defaultButtonBindings[A_GameMenu] = SDL_CONTROLLER_BUTTON_START;
defaultButtonBindings[A_OptionsMenu] = SDL_CONTROLLER_BUTTON_BACK;
defaultButtonBindings[A_QuickSave] = SDL_CONTROLLER_BUTTON_GUIDE; defaultButtonBindings[A_QuickSave] = SDL_CONTROLLER_BUTTON_GUIDE;
defaultButtonBindings[A_MoveForward] = SDL_CONTROLLER_BUTTON_DPAD_UP; defaultButtonBindings[A_MoveForward] = SDL_CONTROLLER_BUTTON_DPAD_UP;
defaultButtonBindings[A_MoveLeft] = SDL_CONTROLLER_BUTTON_DPAD_LEFT; defaultButtonBindings[A_MoveLeft] = SDL_CONTROLLER_BUTTON_DPAD_LEFT;
@ -1615,7 +1589,6 @@ namespace MWInput
descriptions[A_Journal] = "sJournal"; descriptions[A_Journal] = "sJournal";
descriptions[A_Rest] = "sRestKey"; descriptions[A_Rest] = "sRestKey";
descriptions[A_Inventory] = "sInventory"; descriptions[A_Inventory] = "sInventory";
descriptions[A_OptionsMenu] = "sPreferences";
descriptions[A_TogglePOV] = "sTogglePOVCmd"; descriptions[A_TogglePOV] = "sTogglePOVCmd";
descriptions[A_QuickKeysMenu] = "sQuickMenu"; descriptions[A_QuickKeysMenu] = "sQuickMenu";
descriptions[A_QuickKey1] = "sQuick1Cmd"; descriptions[A_QuickKey1] = "sQuick1Cmd";
@ -1753,7 +1726,6 @@ namespace MWInput
ret.push_back(A_Inventory); ret.push_back(A_Inventory);
ret.push_back(A_Journal); ret.push_back(A_Journal);
ret.push_back(A_Rest); ret.push_back(A_Rest);
ret.push_back(A_OptionsMenu);
ret.push_back(A_Console); ret.push_back(A_Console);
ret.push_back(A_QuickSave); ret.push_back(A_QuickSave);
ret.push_back(A_QuickLoad); ret.push_back(A_QuickLoad);
@ -1786,7 +1758,6 @@ namespace MWInput
ret.push_back(A_Inventory); ret.push_back(A_Inventory);
ret.push_back(A_Journal); ret.push_back(A_Journal);
ret.push_back(A_Rest); ret.push_back(A_Rest);
ret.push_back(A_OptionsMenu);
ret.push_back(A_QuickSave); ret.push_back(A_QuickSave);
ret.push_back(A_QuickLoad); ret.push_back(A_QuickLoad);
ret.push_back(A_Screenshot); ret.push_back(A_Screenshot);

View file

@ -226,7 +226,7 @@ namespace MWInput
void setPlayerControlsEnabled(bool enabled); void setPlayerControlsEnabled(bool enabled);
void handleGuiArrowKey(int action); void handleGuiArrowKey(int action);
// Return true if GUI consumes input. // 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); bool gamepadToGuiControl(const SDL_ControllerAxisEvent &arg);
void updateCursorMode(); void updateCursorMode();
@ -235,7 +235,6 @@ namespace MWInput
private: private:
void toggleMainMenu(); void toggleMainMenu();
void toggleOptionsMenu();
void toggleSpell(); void toggleSpell();
void toggleWeapon(); void toggleWeapon();
void toggleInventory(); void toggleInventory();
@ -328,8 +327,6 @@ namespace MWInput
A_MoveForwardBackward, A_MoveForwardBackward,
A_MoveLeftRight, A_MoveLeftRight,
A_OptionsMenu,
A_Last // Marker for the last item A_Last // Marker for the last item
}; };
}; };

View file

@ -1785,14 +1785,7 @@ namespace MWMechanics
MWWorld::Ptr player = getPlayer(); MWWorld::Ptr player = getPlayer();
CreatureStats& stats = player.getClass().getCreatureStats(player); if (!MWBase::Environment::get().getMechanicsManager()->isSneaking(player))
MWBase::World* world = MWBase::Environment::get().getWorld();
bool sneaking = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak);
bool inair = !world->isOnGround(player) && !world->isSwimming(player) && !world->isFlying(player);
sneaking = sneaking && (ctrl->isSneaking() || inair);
if (!sneaking)
{ {
MWBase::Environment::get().getWindowManager()->setSneakVisibility(false); MWBase::Environment::get().getWindowManager()->setSneakVisibility(false);
return; return;
@ -1800,6 +1793,7 @@ namespace MWMechanics
static float sneakSkillTimer = 0.f; // Times sneak skill progress from "avoid notice" static float sneakSkillTimer = 0.f; // Times sneak skill progress from "avoid notice"
MWBase::World* world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting>& gmst = world->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& gmst = world->getStore().get<ESM::GameSetting>();
static const float fSneakUseDist = gmst.find("fSneakUseDist")->mValue.getFloat(); static const float fSneakUseDist = gmst.find("fSneakUseDist")->mValue.getFloat();
static const float fSneakUseDelay = gmst.find("fSneakUseDelay")->mValue.getFloat(); static const float fSneakUseDelay = gmst.find("fSneakUseDelay")->mValue.getFloat();

View file

@ -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 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 return true; //Door is no longer opening
ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door

View file

@ -232,7 +232,7 @@ void MWMechanics::AiPackage::openDoors(const MWWorld::Ptr& actor)
return; return;
// note: AiWander currently does not open doors // 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 )) if ((door.getCellRef().getTrap().empty() && door.getCellRef().getLockLevel() <= 0 ))
{ {

View file

@ -529,19 +529,7 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
if(!mAnimation->hasAnimation(movementAnimName)) if(!mAnimation->hasAnimation(movementAnimName))
{ {
std::string::size_type swimpos = movementAnimName.find("swim"); std::string::size_type swimpos = movementAnimName.find("swim");
if(swimpos == std::string::npos) if (swimpos != std::string::npos)
{
std::string::size_type runpos = movementAnimName.find("run");
if (runpos != std::string::npos)
{
movementAnimName.replace(runpos, runpos+3, "walk");
if (!mAnimation->hasAnimation(movementAnimName))
movementAnimName.clear();
}
else
movementAnimName.clear();
}
else
{ {
movementAnimName.erase(swimpos, 4); movementAnimName.erase(swimpos, 4);
if (!weapShortGroup.empty()) if (!weapShortGroup.empty())
@ -552,8 +540,18 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
else else
movementAnimName = fallbackShortWeaponGroup(movementAnimName, &movemask); movementAnimName = fallbackShortWeaponGroup(movementAnimName, &movemask);
} }
}
if (!mAnimation->hasAnimation(movementAnimName)) if (swimpos == std::string::npos || !mAnimation->hasAnimation(movementAnimName))
{
std::string::size_type runpos = movementAnimName.find("run");
if (runpos != std::string::npos)
{
movementAnimName.replace(runpos, runpos+3, "walk");
if (!mAnimation->hasAnimation(movementAnimName))
movementAnimName.clear();
}
else
movementAnimName.clear(); movementAnimName.clear();
} }
} }
@ -570,10 +568,6 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
mCurrentMovement = movementAnimName; mCurrentMovement = movementAnimName;
if(!mCurrentMovement.empty()) if(!mCurrentMovement.empty())
{ {
bool isflying = MWBase::Environment::get().getWorld()->isFlying(mPtr);
bool isrunning = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run) && !isflying;
bool issneaking = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak) && !isflying;
// For non-flying creatures, MW uses the Walk animation to calculate the animation velocity // For non-flying creatures, MW uses the Walk animation to calculate the animation velocity
// even if we are running. This must be replicated, otherwise the observed speed would differ drastically. // even if we are running. This must be replicated, otherwise the observed speed would differ drastically.
std::string anim = mCurrentMovement; std::string anim = mCurrentMovement;
@ -606,7 +600,7 @@ void CharacterController::refreshMovementAnims(const std::string& weapShortGroup
// The first person anims don't have any velocity to calculate a speed multiplier from. // The first person anims don't have any velocity to calculate a speed multiplier from.
// We use the third person velocities instead. // We use the third person velocities instead.
// FIXME: should be pulled from the actual animation, but it is not presently loaded. // FIXME: should be pulled from the actual animation, but it is not presently loaded.
mMovementAnimSpeed = (issneaking ? 33.5452f : (isrunning ? 222.857f : 154.064f)); mMovementAnimSpeed = (isSneaking() ? 33.5452f : (isRunning() ? 222.857f : 154.064f));
mMovementAnimationControlled = false; mMovementAnimationControlled = false;
} }
} }
@ -2048,28 +2042,6 @@ void CharacterController::update(float duration, bool animationOnly)
lat.normalize(); lat.normalize();
vec = osg::Vec3f(lat.x(), lat.y(), 1.0f) * z * 0.707f; vec = osg::Vec3f(lat.x(), lat.y(), 1.0f) * z * 0.707f;
} }
// advance acrobatics
// also set jumping flag to allow GetPCJumping works
if (isPlayer)
{
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0);
MWBase::Environment::get().getWorld()->getPlayer().setJumping(true);
}
// decrease fatigue
const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->mValue.getFloat();
const float fatigueJumpMult = gmst.find("fFatigueJumpMult")->mValue.getFloat();
float normalizedEncumbrance = mPtr.getClass().getNormalizedEncumbrance(mPtr);
if (normalizedEncumbrance > 1)
normalizedEncumbrance = 1;
const float fatigueDecrease = fatigueJumpBase + normalizedEncumbrance * fatigueJumpMult;
if (!godmode)
{
fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease);
cls.getCreatureStats(mPtr).setFatigue(fatigue);
}
} }
} }
else if(mJumpState == JumpState_InAir && !inwater && !flying && solid) else if(mJumpState == JumpState_InAir && !inwater && !flying && solid)
@ -2282,7 +2254,9 @@ void CharacterController::update(float duration, bool animationOnly)
movement = vec; movement = vec;
cls.getMovementSettings(mPtr).mPosition[0] = cls.getMovementSettings(mPtr).mPosition[1] = 0; cls.getMovementSettings(mPtr).mPosition[0] = cls.getMovementSettings(mPtr).mPosition[1] = 0;
// Can't reset jump state (mPosition[2]) here; we don't know for sure whether the PhysicSystem will actually handle it in this frame if (movement.z() == 0.f)
cls.getMovementSettings(mPtr).mPosition[2] = 0;
// Can't reset jump state (mPosition[2]) here in full; we don't know for sure whether the PhysicSystem will actually handle it in this frame
// due to the fixed minimum timestep used for the physics update. It will be reset in PhysicSystem::move once the jump is handled. // due to the fixed minimum timestep used for the physics update. It will be reset in PhysicSystem::move once the jump is handled.
if (!mSkipAnim) if (!mSkipAnim)

View file

@ -477,7 +477,12 @@ namespace MWMechanics
bool MechanicsManager::isSneaking(const MWWorld::Ptr& ptr) bool MechanicsManager::isSneaking(const MWWorld::Ptr& ptr)
{ {
return mActors.isSneaking(ptr); CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
MWBase::World* world = MWBase::Environment::get().getWorld();
bool animActive = mActors.isSneaking(ptr);
bool stanceOn = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak);
bool inair = !world->isOnGround(ptr) && !world->isSwimming(ptr) && !world->isFlying(ptr);
return stanceOn && (animActive || inair);
} }
void MechanicsManager::rest(double hours, bool sleep) void MechanicsManager::rest(double hours, bool sleep)
@ -965,8 +970,7 @@ namespace MWMechanics
return true; return true;
// check if a player tries to pickpocket a target NPC // check if a player tries to pickpocket a target NPC
if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak) if (target.getClass().getCreatureStats(target).getKnockedDown() || isSneaking(ptr))
|| target.getClass().getCreatureStats(target).getKnockedDown())
return false; return false;
return true; return true;
@ -1586,9 +1590,7 @@ namespace MWMechanics
return false; return false;
float sneakTerm = 0; float sneakTerm = 0;
if (ptr.getClass().getCreatureStats(ptr).getStance(CreatureStats::Stance_Sneak) if (isSneaking(ptr))
&& !MWBase::Environment::get().getWorld()->isSwimming(ptr)
&& MWBase::Environment::get().getWorld()->isOnGround(ptr))
{ {
static float fSneakSkillMult = store.find("fSneakSkillMult")->mValue.getFloat(); static float fSneakSkillMult = store.find("fSneakSkillMult")->mValue.getFloat();
static float fSneakBootMult = store.find("fSneakBootMult")->mValue.getFloat(); static float fSneakBootMult = store.find("fSneakBootMult")->mValue.getFloat();
@ -1596,7 +1598,7 @@ namespace MWMechanics
int agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); int agility = stats.getAttribute(ESM::Attribute::Agility).getModified();
int luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); int luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float bootWeight = 0; float bootWeight = 0;
if (ptr.getClass().isNpc()) if (ptr.getClass().isNpc() && MWBase::Environment::get().getWorld()->isOnGround(ptr))
{ {
const MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr); const MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr);
MWWorld::ConstContainerStoreIterator it = inv.getSlot(MWWorld::InventoryStore::Slot_Boots); MWWorld::ConstContainerStoreIterator it = inv.getSlot(MWWorld::InventoryStore::Slot_Boots);

View file

@ -2,8 +2,6 @@
#include <components/sceneutil/positionattitudetransform.hpp> #include <components/sceneutil/positionattitudetransform.hpp>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
@ -54,10 +52,10 @@ namespace MWMechanics
// FIXME: cast // FIXME: cast
const MWWorld::Ptr doorPtr = MWWorld::Ptr(&const_cast<MWWorld::LiveCellRef<ESM::Door> &>(ref), actor.getCell()); 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]; 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 continue; // the door is already opened/opening
doorPos.z() = 0; doorPos.z() = 0;
@ -78,10 +76,8 @@ namespace MWMechanics
return MWWorld::Ptr(); // none found return MWWorld::Ptr(); // none found
} }
ObstacleCheck::ObstacleCheck(): ObstacleCheck::ObstacleCheck()
mPrevX(0) // to see if the moved since last time : mWalkState(State_Norm)
, mPrevY(0)
, mWalkState(State_Norm)
, mStuckDuration(0) , mStuckDuration(0)
, mEvadeDuration(0) , mEvadeDuration(0)
, mDistSameSpot(-1) // avoid calculating it each time , mDistSameSpot(-1) // avoid calculating it each time
@ -125,21 +121,15 @@ namespace MWMechanics
*/ */
void ObstacleCheck::update(const MWWorld::Ptr& actor, float duration) void ObstacleCheck::update(const MWWorld::Ptr& actor, float duration)
{ {
const ESM::Position pos = actor.getRefData().getPosition(); const osg::Vec3f pos = actor.getRefData().getPosition().asVec3();
if (mDistSameSpot == -1) if (mDistSameSpot == -1)
{ mDistSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor);
const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);
mDistSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor) + 1.2 * std::max(halfExtents.x(), halfExtents.y());
}
const float distSameSpot = mDistSameSpot * duration; const float distSameSpot = mDistSameSpot * duration;
const float squaredMovedDistance = (osg::Vec2f(pos.pos[0], pos.pos[1]) - osg::Vec2f(mPrevX, mPrevY)).length2(); const bool samePosition = (pos - mPrev).length2() < distSameSpot * distSameSpot;
const bool samePosition = squaredMovedDistance < distSameSpot * distSameSpot;
// update position mPrev = pos;
mPrevX = pos.pos[0];
mPrevY = pos.pos[1];
switch(mWalkState) switch(mWalkState)
{ {

View file

@ -1,6 +1,8 @@
#ifndef OPENMW_MECHANICS_OBSTACLE_H #ifndef OPENMW_MECHANICS_OBSTACLE_H
#define OPENMW_MECHANICS_OBSTACLE_H #define OPENMW_MECHANICS_OBSTACLE_H
#include <osg/Vec3f>
namespace MWWorld namespace MWWorld
{ {
class Ptr; class Ptr;
@ -37,9 +39,8 @@ namespace MWMechanics
private: private:
// for checking if we're stuck (ignoring Z axis) // for checking if we're stuck
float mPrevX; osg::Vec3f mPrev;
float mPrevY;
// directions to try moving in when get stuck // directions to try moving in when get stuck
static const float evadeDirections[NUM_EVADE_DIRECTIONS][2]; static const float evadeDirections[NUM_EVADE_DIRECTIONS][2];

View file

@ -238,7 +238,7 @@ namespace MWMechanics
{ {
/* short group */ "", /* short group */ "",
/* long group */ "", /* long group */ "",
/* sound ID */ "Item Weapon Ammo", /* sound ID */ "Item Ammo",
/* attach bone */ "ArrowBone", /* attach bone */ "ArrowBone",
/* sheath bone */ "", /* sheath bone */ "",
/* usage skill */ ESM::Skill::Marksman, /* usage skill */ ESM::Skill::Marksman,
@ -252,7 +252,7 @@ namespace MWMechanics
{ {
/* short group */ "", /* short group */ "",
/* long group */ "", /* long group */ "",
/* sound ID */ "Item Weapon Ammo", /* sound ID */ "Item Ammo",
/* attach bone */ "ArrowBone", /* attach bone */ "ArrowBone",
/* sheath bone */ "", /* sheath bone */ "",
/* usage skill */ ESM::Skill::Marksman, /* usage skill */ ESM::Skill::Marksman,

View file

@ -34,6 +34,7 @@
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
#include "../mwworld/player.hpp"
#include "../mwrender/bulletdebugdraw.hpp" #include "../mwrender/bulletdebugdraw.hpp"
@ -326,7 +327,30 @@ namespace MWPhysics
if (movement.z() > 0 && ptr.getClass().getCreatureStats(ptr).isDead() && position.z() < swimlevel) if (movement.z() > 0 && ptr.getClass().getCreatureStats(ptr).isDead() && position.z() < swimlevel)
velocity = osg::Vec3f(0,0,1) * 25; velocity = osg::Vec3f(0,0,1) * 25;
ptr.getClass().getMovementSettings(ptr).mPosition[2] = 0; if (ptr.getClass().getMovementSettings(ptr).mPosition[2])
{
const bool isPlayer = (ptr == MWMechanics::getPlayer());
// Advance acrobatics and set flag for GetPCJumping
if (isPlayer)
{
ptr.getClass().skillUsageSucceeded(ptr, ESM::Skill::Acrobatics, 0);
MWBase::Environment::get().getWorld()->getPlayer().setJumping(true);
}
// Decrease fatigue
if (!isPlayer || !MWBase::Environment::get().getWorld()->getGodModeState())
{
const MWWorld::Store<ESM::GameSetting> &gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
const float fFatigueJumpBase = gmst.find("fFatigueJumpBase")->mValue.getFloat();
const float fFatigueJumpMult = gmst.find("fFatigueJumpMult")->mValue.getFloat();
const float normalizedEncumbrance = std::min(1.f, ptr.getClass().getNormalizedEncumbrance(ptr));
const float fatigueDecrease = fFatigueJumpBase + normalizedEncumbrance * fFatigueJumpMult;
MWMechanics::DynamicStat<float> fatigue = ptr.getClass().getCreatureStats(ptr).getFatigue();
fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease);
ptr.getClass().getCreatureStats(ptr).setFatigue(fatigue);
}
ptr.getClass().getMovementSettings(ptr).mPosition[2] = 0;
}
// Now that we have the effective movement vector, apply wind forces to it // Now that we have the effective movement vector, apply wind forces to it
if (MWBase::Environment::get().getWorld()->isInStorm()) if (MWBase::Environment::get().getWorld()->isInStorm())

View file

@ -64,7 +64,7 @@ ActorAnimation::~ActorAnimation()
mScabbard.reset(); 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); osg::Group* parent = getBoneByName(bonename);
if (!parent) if (!parent)
@ -160,7 +160,7 @@ void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons)
if (showHolsteredWeapons) if (showHolsteredWeapons)
{ {
osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon); osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon);
mScabbard = getWeaponPart(mesh, boneName, isEnchanted, &glowColor); mScabbard = attachMesh(mesh, boneName, isEnchanted, &glowColor);
if (mScabbard) if (mScabbard)
resetControllers(mScabbard->getNode()); resetControllers(mScabbard->getNode());
} }
@ -168,7 +168,7 @@ void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons)
return; return;
} }
mScabbard = getWeaponPart(scabbardName, boneName); mScabbard = attachMesh(scabbardName, boneName);
osg::Group* weaponNode = getBoneByName("Bip01 Weapon"); osg::Group* weaponNode = getBoneByName("Bip01 Weapon");
if (!weaponNode) if (!weaponNode)

View file

@ -44,11 +44,11 @@ class ActorAnimation : public Animation, public MWWorld::ContainerStoreListener
virtual void updateHolsteredWeapon(bool showHolsteredWeapons); virtual void updateHolsteredWeapon(bool showHolsteredWeapons);
virtual void updateQuiver(); virtual void updateQuiver();
virtual std::string getHolsteredWeaponBoneName(const MWWorld::ConstPtr& weapon); 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 attachMesh(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)
{ {
osg::Vec4f stubColor = osg::Vec4f(0,0,0,0); osg::Vec4f stubColor = osg::Vec4f(0,0,0,0);
return getWeaponPart(model, bonename, false, &stubColor); return attachMesh(model, bonename, false, &stubColor);
}; };
PartHolderPtr mScabbard; PartHolderPtr mScabbard;

View file

@ -18,12 +18,14 @@
#include <components/resource/keyframemanager.hpp> #include <components/resource/keyframemanager.hpp>
#include <components/misc/constants.hpp> #include <components/misc/constants.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/nifosg/nifloader.hpp> // KeyframeHolder #include <components/nifosg/nifloader.hpp> // KeyframeHolder
#include <components/nifosg/controller.hpp> #include <components/nifosg/controller.hpp>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
#include <components/sceneutil/actorutil.hpp>
#include <components/sceneutil/statesetupdater.hpp> #include <components/sceneutil/statesetupdater.hpp>
#include <components/sceneutil/visitor.hpp> #include <components/sceneutil/visitor.hpp>
#include <components/sceneutil/lightmanager.hpp> #include <components/sceneutil/lightmanager.hpp>
@ -245,7 +247,10 @@ namespace
void apply(osg::Node& node) void apply(osg::Node& node)
{ {
if (SceneUtil::hasUserDescription(&node, "CustomBone")) if (SceneUtil::hasUserDescription(&node, "CustomBone"))
{
mFoundBones.emplace_back(&node, node.getParent(0)); mFoundBones.emplace_back(&node, node.getParent(0));
return;
}
traverse(node); traverse(node);
} }
@ -1378,6 +1383,9 @@ namespace MWRender
void injectCustomBones(osg::ref_ptr<osg::Node>& node, const std::string& model, Resource::ResourceSystem* resourceSystem) void injectCustomBones(osg::ref_ptr<osg::Node>& node, const std::string& model, Resource::ResourceSystem* resourceSystem)
{ {
if (model.empty())
return;
const std::map<std::string, VFS::File*>& index = resourceSystem->getVFS()->getIndex(); const std::map<std::string, VFS::File*>& index = resourceSystem->getVFS()->getIndex();
std::string animationPath = model; std::string animationPath = model;
@ -1405,14 +1413,7 @@ namespace MWRender
} }
} }
enum InjectType osg::ref_ptr<osg::Node> getModelInstance(Resource::ResourceSystem* resourceSystem, const std::string& model, bool baseonly, bool inject, const std::string& defaultSkeleton)
{
None,
Model,
ModelWithFallback
};
osg::ref_ptr<osg::Node> getModelInstance(Resource::ResourceSystem* resourceSystem, const std::string& model, bool baseonly, InjectType inject)
{ {
Resource::SceneManager* sceneMgr = resourceSystem->getSceneManager(); Resource::SceneManager* sceneMgr = resourceSystem->getSceneManager();
if (baseonly) if (baseonly)
@ -1424,11 +1425,11 @@ namespace MWRender
{ {
osg::ref_ptr<osg::Node> created = sceneMgr->getInstance(model); osg::ref_ptr<osg::Node> created = sceneMgr->getInstance(model);
if (inject == InjectType::ModelWithFallback) if (inject)
injectCustomBones(created, "meshes\\xbase_anim.nif", resourceSystem); {
injectCustomBones(created, defaultSkeleton, resourceSystem);
if (inject != InjectType::None)
injectCustomBones(created, model, resourceSystem); injectCustomBones(created, model, resourceSystem);
}
SceneUtil::CleanObjectRootVisitor removeDrawableVisitor; SceneUtil::CleanObjectRootVisitor removeDrawableVisitor;
created->accept(removeDrawableVisitor); created->accept(removeDrawableVisitor);
@ -1445,11 +1446,11 @@ namespace MWRender
{ {
osg::ref_ptr<osg::Node> created = sceneMgr->getInstance(model); osg::ref_ptr<osg::Node> created = sceneMgr->getInstance(model);
if (inject == InjectType::ModelWithFallback) if (inject)
injectCustomBones(created, "meshes\\xbase_anim.nif", resourceSystem); {
injectCustomBones(created, defaultSkeleton, resourceSystem);
if (inject != InjectType::None)
injectCustomBones(created, model, resourceSystem); injectCustomBones(created, model, resourceSystem);
}
return created; return created;
} }
@ -1475,17 +1476,44 @@ namespace MWRender
mAccumCtrl = nullptr; mAccumCtrl = nullptr;
static const bool useAdditionalSources = Settings::Manager::getBool ("use additional anim sources", "Game"); static const bool useAdditionalSources = Settings::Manager::getBool ("use additional anim sources", "Game");
InjectType inject = useAdditionalSources && mPtr.getClass().isActor() ? InjectType::Model : InjectType::None; std::string defaultSkeleton;
if (inject != InjectType::None && isCreature) bool inject = false;
if (useAdditionalSources && mPtr.getClass().isActor())
{ {
MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>(); if (isCreature)
if(ref->mBase->mFlags & ESM::Creature::Bipedal) {
inject = InjectType::ModelWithFallback; MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>();
if(ref->mBase->mFlags & ESM::Creature::Bipedal)
{
defaultSkeleton = "meshes\\xbase_anim.nif";
inject = true;
}
}
else
{
inject = true;
MWWorld::LiveCellRef<ESM::NPC> *ref = mPtr.get<ESM::NPC>();
if (!ref->mBase->mModel.empty())
{
// If NPC has a custom animation model attached, we should inject bones from default skeleton for given race and gender as well
// Since it is a quite rare case, there should not be a noticable performance loss
// Note: consider that player and werewolves have no custom animation files attached for now
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Race *race = store.get<ESM::Race>().find(ref->mBase->mRace);
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
bool isFemale = !ref->mBase->isMale();
defaultSkeleton = SceneUtil::getActorSkeleton(false, isFemale, isBeast, false);
defaultSkeleton = Misc::ResourceHelpers::correctActorModelPath(defaultSkeleton, mResourceSystem->getVFS());
}
}
} }
if (!forceskeleton) if (!forceskeleton)
{ {
osg::ref_ptr<osg::Node> created = getModelInstance(mResourceSystem, model, baseonly, inject); osg::ref_ptr<osg::Node> created = getModelInstance(mResourceSystem, model, baseonly, inject, defaultSkeleton);
mInsert->addChild(created); mInsert->addChild(created);
mObjectRoot = created->asGroup(); mObjectRoot = created->asGroup();
if (!mObjectRoot) if (!mObjectRoot)
@ -1501,7 +1529,7 @@ namespace MWRender
} }
else else
{ {
osg::ref_ptr<osg::Node> created = getModelInstance(mResourceSystem, model, baseonly, inject); osg::ref_ptr<osg::Node> created = getModelInstance(mResourceSystem, model, baseonly, inject, defaultSkeleton);
osg::ref_ptr<SceneUtil::Skeleton> skel = dynamic_cast<SceneUtil::Skeleton*>(created.get()); osg::ref_ptr<SceneUtil::Skeleton> skel = dynamic_cast<SceneUtil::Skeleton*>(created.get());
if (!skel) if (!skel)
{ {

View file

@ -48,7 +48,6 @@ namespace MWRender
mAnimation(nullptr), mAnimation(nullptr),
mFirstPersonView(true), mFirstPersonView(true),
mPreviewMode(false), mPreviewMode(false),
mFreeLook(true),
mNearest(30.f), mNearest(30.f),
mFurthest(800.f), mFurthest(800.f),
mIsNearest(false), mIsNearest(false),
@ -393,11 +392,6 @@ namespace MWRender
camera = focal + offset; camera = focal + offset;
} }
void Camera::togglePlayerLooking(bool enable)
{
mFreeLook = enable;
}
bool Camera::isVanityOrPreviewModeEnabled() bool Camera::isVanityOrPreviewModeEnabled()
{ {
return mPreviewMode || mVanity.enabled; return mPreviewMode || mVanity.enabled;

View file

@ -37,7 +37,6 @@ namespace MWRender
bool mFirstPersonView; bool mFirstPersonView;
bool mPreviewMode; bool mPreviewMode;
bool mFreeLook;
float mNearest; float mNearest;
float mFurthest; float mFurthest;
bool mIsNearest; bool mIsNearest;
@ -119,8 +118,6 @@ namespace MWRender
/// Stores focal and camera world positions in passed arguments /// Stores focal and camera world positions in passed arguments
void getPosition(osg::Vec3f &focal, osg::Vec3f &camera); void getPosition(osg::Vec3f &focal, osg::Vec3f &camera);
void togglePlayerLooking(bool enable);
bool isVanityOrPreviewModeEnabled(); bool isVanityOrPreviewModeEnabled();
bool isNearest(); bool isNearest();

View file

@ -1353,11 +1353,6 @@ namespace MWRender
mCamera->allowVanityMode(allow); mCamera->allowVanityMode(allow);
} }
void RenderingManager::togglePlayerLooking(bool enable)
{
mCamera->togglePlayerLooking(enable);
}
void RenderingManager::changeVanityModeScale(float factor) void RenderingManager::changeVanityModeScale(float factor)
{ {
if(mCamera->isVanityOrPreviewModeEnabled()) if(mCamera->isVanityOrPreviewModeEnabled())

View file

@ -208,7 +208,6 @@ namespace MWRender
void togglePreviewMode(bool enable); void togglePreviewMode(bool enable);
bool toggleVanityMode(bool enable); bool toggleVanityMode(bool enable);
void allowVanityMode(bool allow); void allowVanityMode(bool allow);
void togglePlayerLooking(bool enable);
void changeVanityModeScale(float factor); void changeVanityModeScale(float factor);
/// temporarily override the field of view with given value. /// temporarily override the field of view with given value.

View file

@ -186,14 +186,7 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime) virtual void execute (Interpreter::Runtime& runtime)
{ {
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); runtime.push(MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr));
MWBase::World* world = MWBase::Environment::get().getWorld();
bool stanceOn = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak);
bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr);
bool inair = !world->isOnGround(ptr) && !world->isSwimming(ptr) && !world->isFlying(ptr);
runtime.push(stanceOn && (sneaking || inair));
} }
}; };

View file

@ -188,13 +188,7 @@ namespace MWScript
// This is done when using Lock in scripts, but not when using Lock spells. // This is done when using Lock in scripts, but not when using Lock spells.
if (ptr.getTypeName() == typeid(ESM::Door).name() && !ptr.getCellRef().getTeleport()) 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);
float xr = ptr.getCellRef().getPosition().rot[0];
float yr = ptr.getCellRef().getPosition().rot[1];
float zr = ptr.getCellRef().getPosition().rot[2];
MWBase::Environment::get().getWorld()->rotateObject(ptr, xr, yr, zr);
} }
} }
}; };

View file

@ -798,7 +798,7 @@ bool OpenAL_Output::init(const std::string &devname, const std::string &hrtfname
if(alGetError() == AL_NO_ERROR) if(alGetError() == AL_NO_ERROR)
Log(Debug::Info) << "Standard Reverb supported"; Log(Debug::Info) << "Standard Reverb supported";
} }
EFXEAXREVERBPROPERTIES props = EFX_REVERB_PRESET_GENERIC; EFXEAXREVERBPROPERTIES props = EFX_REVERB_PRESET_LIVINGROOM;
props.flGain = 0.0f; props.flGain = 0.0f;
LoadEffect(mDefaultEffect, props); LoadEffect(mDefaultEffect, props);
} }

View file

@ -460,12 +460,12 @@ namespace MWWorld
return false; 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"); 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"); throw std::runtime_error("this is not a door");
} }

View file

@ -9,6 +9,7 @@
#include <osg/Vec4f> #include <osg/Vec4f>
#include "ptr.hpp" #include "ptr.hpp"
#include "doorstate.hpp"
namespace ESM namespace ESM
{ {
@ -350,10 +351,9 @@ namespace MWWorld
virtual bool isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const; virtual bool isClass(const MWWorld::ConstPtr& ptr, const std::string &className) const;
/// 0 = nothing, 1 = opening, 2 = closing virtual DoorState getDoorState (const MWWorld::ConstPtr &ptr) const;
virtual int getDoorState (const MWWorld::ConstPtr &ptr) const;
/// This does not actually cause the door to move. Use World::activateDoor instead. /// 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 {} virtual void respawn (const MWWorld::Ptr& ptr) const {}

View 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

View file

@ -1458,6 +1458,9 @@ namespace MWWorld
{ {
mRendering->rotateObject(ptr, rotate); mRendering->rotateObject(ptr, rotate);
mPhysics->updateRotation(ptr); mPhysics->updateRotation(ptr);
if (const auto object = mPhysics->getObject(ptr))
updateNavigatorObject(object);
} }
} }
@ -1614,9 +1617,48 @@ namespace MWWorld
return result.mHit; return result.mHit;
} }
bool World::rotateDoor(const Ptr door, MWWorld::DoorState state, float duration)
{
const ESM::Position& objPos = door.getRefData().getPosition();
float oldRot = objPos.rot[2];
float minRot = door.getCellRef().getPosition().rot[2];
float maxRot = minRot + osg::DegreesToRadians(90.f);
float diff = duration * osg::DegreesToRadians(90.f);
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 != MWWorld::DoorState::Idle) || targetRot == minRot;
/// \todo should use convexSweepTest here
std::vector<MWWorld::Ptr> collisions = mPhysics->getCollisions(door, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor);
for (MWWorld::Ptr& ptr : collisions)
{
if (ptr.getClass().isActor())
{
// Collided with actor, ask actor to try to avoid door
if(ptr != getPlayerPtr() )
{
MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();
if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) //Only add it once
seq.stack(MWMechanics::AiAvoidDoor(door),ptr);
}
// we need to undo the rotation
rotateObject(door, objPos.rot[0], objPos.rot[1], oldRot);
reached = false;
}
}
// the rotation order we want to use
mWorldScene->updateObjectRotation(door, false);
return reached;
}
void World::processDoors(float duration) void World::processDoors(float duration)
{ {
std::map<MWWorld::Ptr, int>::iterator it = mDoorStates.begin(); auto it = mDoorStates.begin();
while (it != mDoorStates.end()) while (it != mDoorStates.end())
{ {
if (!mWorldScene->isCellActive(*it->first.getCell()) || !it->first.getRefData().getBaseNode()) if (!mWorldScene->isCellActive(*it->first.getCell()) || !it->first.getRefData().getBaseNode())
@ -1628,45 +1670,12 @@ namespace MWWorld
} }
else else
{ {
const ESM::Position& objPos = it->first.getRefData().getPosition(); bool reached = rotateDoor(it->first, it->second, duration);
float oldRot = objPos.rot[2];
float minRot = it->first.getCellRef().getPosition().rot[2];
float maxRot = minRot + osg::DegreesToRadians(90.f);
float diff = duration * osg::DegreesToRadians(90.f);
float targetRot = std::min(std::max(minRot, oldRot + diff * (it->second == 1 ? 1 : -1)), maxRot);
rotateObject(it->first, objPos.rot[0], objPos.rot[1], targetRot);
bool reached = (targetRot == maxRot && it->second) || targetRot == minRot;
/// \todo should use convexSweepTest here
std::vector<MWWorld::Ptr> collisions = mPhysics->getCollisions(it->first, MWPhysics::CollisionType_Door, MWPhysics::CollisionType_Actor);
for (MWWorld::Ptr& ptr : collisions)
{
if (ptr.getClass().isActor())
{
// Collided with actor, ask actor to try to avoid door
if(ptr != getPlayerPtr() )
{
MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();
if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) //Only add it once
seq.stack(MWMechanics::AiAvoidDoor(it->first),ptr);
}
// we need to undo the rotation
rotateObject(it->first, objPos.rot[0], objPos.rot[1], oldRot);
reached = false;
}
}
// the rotation order we want to use
mWorldScene->updateObjectRotation(it->first, false);
if (reached) if (reached)
{ {
// Mark as non-moving // Mark as non-moving
it->first.getClass().setDoorState(it->first, 0); it->first.getClass().setDoorState(it->first, MWWorld::DoorState::Idle);
mDoorStates.erase(it++); mDoorStates.erase(it++);
} }
else else
@ -2389,11 +2398,6 @@ namespace MWWorld
mRendering->allowVanityMode(allow); mRendering->allowVanityMode(allow);
} }
void World::togglePlayerLooking(bool enable)
{
mRendering->togglePlayerLooking(enable);
}
void World::changeVanityModeScale(float factor) void World::changeVanityModeScale(float factor)
{ {
mRendering->changeVanityModeScale(factor); mRendering->changeVanityModeScale(factor);
@ -2506,33 +2510,36 @@ namespace MWWorld
void World::activateDoor(const MWWorld::Ptr& door) void World::activateDoor(const MWWorld::Ptr& door)
{ {
int state = door.getClass().getDoorState(door); auto state = door.getClass().getDoorState(door);
switch (state) switch (state)
{ {
case 0: case MWWorld::DoorState::Idle:
if (door.getRefData().getPosition().rot[2] == door.getCellRef().getPosition().rot[2]) 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 else
state = 2; // if open, then close state = MWWorld::DoorState::Closing; // if open, then close
break; break;
case 2: case MWWorld::DoorState::Closing:
state = 1; // if closing, then open state = MWWorld::DoorState::Opening; // if closing, then open
break; break;
case 1: case MWWorld::DoorState::Opening:
default: default:
state = 2; // if opening, then close state = MWWorld::DoorState::Closing; // if opening, then close
break; break;
} }
door.getClass().setDoorState(door, state); door.getClass().setDoorState(door, state);
mDoorStates[door] = state; mDoorStates[door] = state;
} }
void World::activateDoor(const Ptr &door, int state) void World::activateDoor(const Ptr &door, MWWorld::DoorState state)
{ {
door.getClass().setDoorState(door, state); door.getClass().setDoorState(door, state);
mDoorStates[door] = state; mDoorStates[door] = state;
if (state == 0) if (state == MWWorld::DoorState::Idle)
{
mDoorStates.erase(door); mDoorStates.erase(door);
rotateDoor(door, state, 1);
}
} }
bool World::getPlayerStandingOn (const MWWorld::ConstPtr& object) bool World::getPlayerStandingOn (const MWWorld::ConstPtr& object)

View file

@ -118,7 +118,7 @@ namespace MWWorld
int mActivationDistanceOverride; 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 ///< only holds doors that are currently moving. 1 = opening, 2 = closing
std::string mStartCell; std::string mStartCell;
@ -146,6 +146,8 @@ namespace MWWorld
private: private:
void PCDropped (const Ptr& item); void PCDropped (const Ptr& item);
bool rotateDoor(const Ptr door, DoorState state, float duration);
void processDoors(float duration); void processDoors(float duration);
///< Run physics simulation and modify \a world accordingly. ///< Run physics simulation and modify \a world accordingly.
@ -526,8 +528,6 @@ namespace MWWorld
void allowVanityMode(bool allow) override; void allowVanityMode(bool allow) override;
void togglePlayerLooking(bool enable) override;
void changeVanityModeScale(float factor) override; void changeVanityModeScale(float factor) override;
bool vanityRotateCamera(float * rot) override; bool vanityRotateCamera(float * rot) override;
@ -542,7 +542,7 @@ namespace MWWorld
/// update movement state of a non-teleport door as specified /// update movement state of a non-teleport door as specified
/// @param state see MWClass::setDoorState /// @param state see MWClass::setDoorState
/// @note throws an exception when invoked on a teleport door /// @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;
void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector<MWWorld::Ptr> &actors) override; ///< get a list of actors standing on \a object void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector<MWWorld::Ptr> &actors) override; ///< get a list of actors standing on \a object
bool getPlayerStandingOn (const MWWorld::ConstPtr& object) override; ///< @return true if the player is standing on \a object bool getPlayerStandingOn (const MWWorld::ConstPtr& object) override; ///< @return true if the player is standing on \a object

View file

@ -163,22 +163,23 @@ void NiPixelData::read(NIFStream *nif)
{ {
fmt = (Format)nif->getUInt(); fmt = (Format)nif->getUInt();
rmask = nif->getInt(); // usually 0xff rmask = nif->getUInt(); // usually 0xff
gmask = nif->getInt(); // usually 0xff00 gmask = nif->getUInt(); // usually 0xff00
bmask = nif->getInt(); // usually 0xff0000 bmask = nif->getUInt(); // usually 0xff0000
amask = nif->getInt(); // usually 0xff000000 or zero amask = nif->getUInt(); // usually 0xff000000 or zero
bpp = nif->getInt(); bpp = nif->getUInt();
// Unknown // 8 bytes of "Old Fast Compare". Whatever that means.
nif->skip(12); nif->skip(8);
palette.read(nif);
numberOfMipmaps = nif->getInt(); numberOfMipmaps = nif->getUInt();
// Bytes per pixel, should be bpp * 8 // Bytes per pixel, should be bpp * 8
/* int bytes = */ nif->getInt(); /* int bytes = */ nif->getUInt();
for(int i=0; i<numberOfMipmaps; i++) for(unsigned int i=0; i<numberOfMipmaps; i++)
{ {
// Image size and offset in the following data field // Image size and offset in the following data field
Mipmap m; Mipmap m;
@ -189,12 +190,17 @@ void NiPixelData::read(NIFStream *nif)
} }
// Read the data // Read the data
unsigned int dataSize = nif->getInt(); unsigned int dataSize = nif->getUInt();
data.reserve(dataSize); data.reserve(dataSize);
for (unsigned i=0; i<dataSize; ++i) for (unsigned i=0; i<dataSize; ++i)
data.push_back((unsigned char)nif->getChar()); data.push_back((unsigned char)nif->getChar());
} }
void NiPixelData::post(NIFFile *nif)
{
palette.post(nif);
}
void NiColorData::read(NIFStream *nif) void NiColorData::read(NIFStream *nif)
{ {
mKeyMap = std::make_shared<Vector4KeyMap>(); mKeyMap = std::make_shared<Vector4KeyMap>();
@ -278,4 +284,14 @@ void NiKeyframeData::read(NIFStream *nif)
mScales->read(nif); mScales->read(nif);
} }
void NiPalette::read(NIFStream *nif)
{
unsigned int alphaMask = !nif->getChar() ? 0xFF000000 : 0;
// Fill the entire palette with black even if there isn't enough entries.
colors.resize(256);
unsigned int numEntries = nif->getUInt();
for (unsigned int i = 0; i < numEntries; i++)
colors[i] = nif->getUInt() | alphaMask;
}
} // Namespace } // Namespace

View file

@ -116,6 +116,7 @@ public:
NIPXFMT_RGB8, NIPXFMT_RGB8,
NIPXFMT_RGBA8, NIPXFMT_RGBA8,
NIPXFMT_PAL8, NIPXFMT_PAL8,
NIPXFMT_PALA8,
NIPXFMT_DXT1, NIPXFMT_DXT1,
NIPXFMT_DXT3, NIPXFMT_DXT3,
NIPXFMT_DXT5, NIPXFMT_DXT5,
@ -123,8 +124,10 @@ public:
}; };
Format fmt; Format fmt;
unsigned int rmask, gmask, bmask, amask; unsigned int rmask, gmask, bmask, amask, bpp;
int bpp, numberOfMipmaps;
NiPalettePtr palette;
unsigned int numberOfMipmaps;
struct Mipmap struct Mipmap
{ {
@ -136,6 +139,7 @@ public:
std::vector<unsigned char> data; std::vector<unsigned char> data;
void read(NIFStream *nif); void read(NIFStream *nif);
void post(NIFFile *nif);
}; };
class NiColorData : public Record class NiColorData : public Record
@ -219,5 +223,14 @@ struct NiKeyframeData : public Record
void read(NIFStream *nif); void read(NIFStream *nif);
}; };
class NiPalette : public Record
{
public:
// 32-bit RGBA colors that correspond to 8-bit indices
std::vector<unsigned int> colors;
void read(NIFStream *nif);
};
} // Namespace } // Namespace
#endif #endif

View file

@ -112,6 +112,7 @@ static std::map<std::string,RecordFactoryEntry> makeFactory()
newFactory.insert(makeEntry("NiSourceTexture", &construct <NiSourceTexture> , RC_NiSourceTexture )); newFactory.insert(makeEntry("NiSourceTexture", &construct <NiSourceTexture> , RC_NiSourceTexture ));
newFactory.insert(makeEntry("NiSkinInstance", &construct <NiSkinInstance> , RC_NiSkinInstance )); newFactory.insert(makeEntry("NiSkinInstance", &construct <NiSkinInstance> , RC_NiSkinInstance ));
newFactory.insert(makeEntry("NiLookAtController", &construct <NiLookAtController> , RC_NiLookAtController )); newFactory.insert(makeEntry("NiLookAtController", &construct <NiLookAtController> , RC_NiLookAtController ));
newFactory.insert(makeEntry("NiPalette", &construct <NiPalette> , RC_NiPalette ));
return newFactory; return newFactory;
} }

View file

@ -97,7 +97,8 @@ enum RecordType
RC_NiSkinInstance, RC_NiSkinInstance,
RC_RootCollisionNode, RC_RootCollisionNode,
RC_NiSphericalCollider, RC_NiSphericalCollider,
RC_NiLookAtController RC_NiLookAtController,
RC_NiPalette
}; };
/// Base class for all records /// Base class for all records

View file

@ -140,6 +140,7 @@ class NiSkinInstance;
class NiSourceTexture; class NiSourceTexture;
class NiRotatingParticlesData; class NiRotatingParticlesData;
class NiAutoNormalParticlesData; class NiAutoNormalParticlesData;
class NiPalette;
typedef RecordPtrT<Node> NodePtr; typedef RecordPtrT<Node> NodePtr;
typedef RecordPtrT<Extra> ExtraPtr; typedef RecordPtrT<Extra> ExtraPtr;
@ -160,6 +161,7 @@ typedef RecordPtrT<NiSkinInstance> NiSkinInstancePtr;
typedef RecordPtrT<NiSourceTexture> NiSourceTexturePtr; typedef RecordPtrT<NiSourceTexture> NiSourceTexturePtr;
typedef RecordPtrT<NiRotatingParticlesData> NiRotatingParticlesDataPtr; typedef RecordPtrT<NiRotatingParticlesData> NiRotatingParticlesDataPtr;
typedef RecordPtrT<NiAutoNormalParticlesData> NiAutoNormalParticlesDataPtr; typedef RecordPtrT<NiAutoNormalParticlesData> NiAutoNormalParticlesDataPtr;
typedef RecordPtrT<NiPalette> NiPalettePtr;
typedef RecordListT<Node> NodeList; typedef RecordListT<Node> NodeList;
typedef RecordListT<Property> PropertyList; typedef RecordListT<Property> PropertyList;

View file

@ -408,8 +408,8 @@ namespace NifOsg
unsigned int clamp = static_cast<unsigned int>(textureEffect->clamp); unsigned int clamp = static_cast<unsigned int>(textureEffect->clamp);
int wrapT = (clamp) & 0x1; int wrapT = (clamp) & 0x1;
int wrapS = (clamp >> 1) & 0x1; int wrapS = (clamp >> 1) & 0x1;
texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP); texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);
texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP); texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);
osg::ref_ptr<osg::TexEnvCombine> texEnv = new osg::TexEnvCombine; osg::ref_ptr<osg::TexEnvCombine> texEnv = new osg::TexEnvCombine;
texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE); texEnv->setCombine_Alpha(osg::TexEnvCombine::REPLACE);
@ -777,8 +777,8 @@ namespace NifOsg
// inherit wrap settings from the target slot // inherit wrap settings from the target slot
osg::Texture2D* inherit = dynamic_cast<osg::Texture2D*>(stateset->getTextureAttribute(flipctrl->mTexSlot, osg::StateAttribute::TEXTURE)); osg::Texture2D* inherit = dynamic_cast<osg::Texture2D*>(stateset->getTextureAttribute(flipctrl->mTexSlot, osg::StateAttribute::TEXTURE));
osg::Texture2D::WrapMode wrapS = osg::Texture2D::CLAMP; osg::Texture2D::WrapMode wrapS = osg::Texture2D::CLAMP_TO_EDGE;
osg::Texture2D::WrapMode wrapT = osg::Texture2D::CLAMP; osg::Texture2D::WrapMode wrapT = osg::Texture2D::CLAMP_TO_EDGE;
if (inherit) if (inherit)
{ {
wrapS = inherit->getWrap(osg::Texture2D::WRAP_S); wrapS = inherit->getWrap(osg::Texture2D::WRAP_S);
@ -1072,28 +1072,34 @@ namespace NifOsg
if (nifNode->recType == Nif::RC_NiTriShape) if (nifNode->recType == Nif::RC_NiTriShape)
{ {
const Nif::NiTriShape* triShape = static_cast<const Nif::NiTriShape*>(nifNode); const Nif::NiTriShape* triShape = static_cast<const Nif::NiTriShape*>(nifNode);
const Nif::NiTriShapeData* data = triShape->data.getPtr(); if (!triShape->data.empty())
vertexColorsPresent = !data->colors.empty(); {
triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triShape->name); const Nif::NiTriShapeData* data = triShape->data.getPtr();
if (!data->triangles.empty()) vertexColorsPresent = !data->colors.empty();
geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, data->triangles.size(), triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triShape->name);
(unsigned short*)data->triangles.data())); if (!data->triangles.empty())
geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, data->triangles.size(),
(unsigned short*)data->triangles.data()));
}
} }
else else
{ {
const Nif::NiTriStrips* triStrips = static_cast<const Nif::NiTriStrips*>(nifNode); const Nif::NiTriStrips* triStrips = static_cast<const Nif::NiTriStrips*>(nifNode);
const Nif::NiTriStripsData* data = triStrips->data.getPtr(); if (!triStrips->data.empty())
vertexColorsPresent = !data->colors.empty();
triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triStrips->name);
if (!data->strips.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. for (const std::vector<unsigned short>& strip : data->strips)
if (strip.size() < 3) {
continue; // Can't make a triangle from less than three vertices.
geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(), if (strip.size() < 3)
(unsigned short*)strip.data())); continue;
geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(),
(unsigned short*)strip.data()));
}
} }
} }
} }
@ -1276,9 +1282,11 @@ namespace NifOsg
switch (pixelData->fmt) switch (pixelData->fmt)
{ {
case Nif::NiPixelData::NIPXFMT_RGB8: case Nif::NiPixelData::NIPXFMT_RGB8:
case Nif::NiPixelData::NIPXFMT_PAL8:
pixelformat = GL_RGB; pixelformat = GL_RGB;
break; break;
case Nif::NiPixelData::NIPXFMT_RGBA8: case Nif::NiPixelData::NIPXFMT_RGBA8:
case Nif::NiPixelData::NIPXFMT_PALA8:
pixelformat = GL_RGBA; pixelformat = GL_RGBA;
break; break;
default: default:
@ -1293,7 +1301,7 @@ namespace NifOsg
int height = 0; int height = 0;
std::vector<unsigned int> mipmapVector; std::vector<unsigned int> mipmapVector;
for (unsigned int i=0; i<pixelData->mipmaps.size()-3; ++i) for (unsigned int i=0; i<pixelData->mipmaps.size(); ++i)
{ {
const Nif::NiPixelData::Mipmap& mip = pixelData->mipmaps[i]; const Nif::NiPixelData::Mipmap& mip = pixelData->mipmaps[i];
@ -1319,10 +1327,59 @@ namespace NifOsg
return nullptr; return nullptr;
} }
unsigned char* data = new unsigned char[pixelData->data.size()]; const std::vector<unsigned char>& pixels = pixelData->data;
memcpy(data, pixelData->data.data(), pixelData->data.size()); switch (pixelData->fmt)
{
case Nif::NiPixelData::NIPXFMT_RGB8:
case Nif::NiPixelData::NIPXFMT_RGBA8:
{
unsigned char* data = new unsigned char[pixels.size()];
memcpy(data, pixels.data(), pixels.size());
image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data, osg::Image::USE_NEW_DELETE);
break;
}
case Nif::NiPixelData::NIPXFMT_PAL8:
case Nif::NiPixelData::NIPXFMT_PALA8:
{
if (pixelData->palette.empty() || pixelData->bpp != 8)
{
Log(Debug::Info) << "Palettized texture in " << mFilename << " is invalid, ignoring";
return nullptr;
}
// We're going to convert the indices that pixel data contains
// into real colors using the palette.
const std::vector<unsigned int>& palette = pixelData->palette->colors;
if (pixelData->fmt == Nif::NiPixelData::NIPXFMT_PAL8)
{
unsigned char* data = new unsigned char[pixels.size() * 3];
for (size_t i = 0; i < pixels.size(); i++)
{
unsigned int color = palette[pixels[i]];
data[i * 3 + 0] = (color >> 0) & 0xFF;
data[i * 3 + 1] = (color >> 8) & 0xFF;
data[i * 3 + 2] = (color >> 16) & 0xFF;
}
image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data, osg::Image::USE_NEW_DELETE);
}
else // if (fmt = NIPXFMT_PALA8)
{
unsigned char* data = new unsigned char[pixels.size() * 4];
for (size_t i = 0; i < pixels.size(); i++)
{
unsigned int color = palette[pixels[i]];
data[i * 4 + 0] = (color >> 0) & 0xFF;
data[i * 4 + 1] = (color >> 8) & 0xFF;
data[i * 4 + 2] = (color >> 16) & 0xFF;
data[i * 4 + 3] = (color >> 24) & 0xFF;
}
image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data, osg::Image::USE_NEW_DELETE);
}
break;
}
default:
return nullptr;
}
image->setImage(width, height, 1, pixelformat, pixelformat, GL_UNSIGNED_BYTE, data, osg::Image::USE_NEW_DELETE);
image->setMipmapLevels(mipmapVector); image->setMipmapLevels(mipmapVector);
image->flipVertical(); image->flipVertical();
@ -1392,8 +1449,8 @@ namespace NifOsg
int wrapT = (clamp) & 0x1; int wrapT = (clamp) & 0x1;
int wrapS = (clamp >> 1) & 0x1; int wrapS = (clamp >> 1) & 0x1;
texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP); texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);
texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP); texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE);
int texUnit = boundTextures.size(); int texUnit = boundTextures.size();