mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-28 12:40:06 +00:00
Merge branch 'master' of https://github.com/OpenMW/openmw
This commit is contained in:
commit
777c4b9aad
@ -1,7 +1,7 @@
|
|||||||
os:
|
os:
|
||||||
- linux
|
- linux
|
||||||
- osx
|
- osx
|
||||||
osx_image: xcode7.3
|
osx_image: xcode8.2
|
||||||
language: cpp
|
language: cpp
|
||||||
sudo: required
|
sudo: required
|
||||||
dist: trusty
|
dist: trusty
|
||||||
|
@ -71,6 +71,7 @@ Programmers
|
|||||||
John Blomberg (fstp)
|
John Blomberg (fstp)
|
||||||
Jordan Ayers
|
Jordan Ayers
|
||||||
Jordan Milne
|
Jordan Milne
|
||||||
|
Jules Blok (Armada651)
|
||||||
Julien Voisin (jvoisin/ap0)
|
Julien Voisin (jvoisin/ap0)
|
||||||
Karl-Felix Glatzer (k1ll)
|
Karl-Felix Glatzer (k1ll)
|
||||||
Kevin Poitra (PuppyKevin)
|
Kevin Poitra (PuppyKevin)
|
||||||
@ -80,6 +81,7 @@ Programmers
|
|||||||
lazydev
|
lazydev
|
||||||
Leon Krieg (lkrieg)
|
Leon Krieg (lkrieg)
|
||||||
Leon Saunders (emoose)
|
Leon Saunders (emoose)
|
||||||
|
logzero
|
||||||
lohikaarme
|
lohikaarme
|
||||||
Lukasz Gromanowski (lgro)
|
Lukasz Gromanowski (lgro)
|
||||||
Manuel Edelmann (vorenon)
|
Manuel Edelmann (vorenon)
|
||||||
|
75
CHANGELOG.md
75
CHANGELOG.md
@ -1,3 +1,78 @@
|
|||||||
|
0.41.0
|
||||||
|
------
|
||||||
|
|
||||||
|
Bug #1138: Casting water walking doesn't move the player out of the water
|
||||||
|
Bug #1931: Rocks from blocked passage in Bamz-Amschend, Radacs Forge can reset and cant be removed again.
|
||||||
|
Bug #2048: Almvisi and Divine Intervention display wrong spell effect
|
||||||
|
Bug #2054: Show effect-indicator for "instant effect" spells and potions
|
||||||
|
Bug #2150: Clockwork City door animation problem
|
||||||
|
Bug #2288: Playback of weapon idle animation not correct
|
||||||
|
Bug #2410: Stat-review window doesn't display starting spells, powers, or abilities
|
||||||
|
Bug #2493: Repairing occasionally very slow
|
||||||
|
Bug #2716: [OSG] Water surface is too transparent from some angles
|
||||||
|
Bug #2859: [MAC OS X] Cannot exit fullscreen once enabled
|
||||||
|
Bug #3091: Editor: will not save addon if global variable value type is null
|
||||||
|
Bug #3277: Editor: Non-functional nested tables in subviews need to be hidden instead of being disabled
|
||||||
|
Bug #3348: Disabled map markers show on minimap
|
||||||
|
Bug #3350: Extending selection to instances with same object results in duplicates.
|
||||||
|
Bug #3353: [Mod] Romance version 3.7 script failed
|
||||||
|
Bug #3376: [Mod] Vampire Embrace script fails to execute
|
||||||
|
Bug #3385: Banners don't animate in stormy weather as they do in the original game
|
||||||
|
Bug #3393: Akulakhan re-enabled after main quest
|
||||||
|
Bug #3427: Editor: OpenMW-CS instances won´t get deleted
|
||||||
|
Bug #3451: Feril Salmyn corpse isn't where it is supposed to be
|
||||||
|
Bug #3497: Zero-weight armor is displayed as "heavy" in inventory tooltip
|
||||||
|
Bug #3499: Idle animations don't always loop
|
||||||
|
Bug #3500: Spark showers at Sotha Sil do not appear until you look at the ceiling
|
||||||
|
Bug #3515: Editor: Moved objects in interior cells are teleported to exterior cells.
|
||||||
|
Bug #3520: Editor: OpenMW-CS cannot find project file when launching the game
|
||||||
|
Bug #3521: Armed NPCs don't use correct melee attacks
|
||||||
|
Bug #3535: Changing cell immediately after dying causes character to freeze.
|
||||||
|
Bug #3542: Unable to rest if unalerted slaughterfish are in the cell with you
|
||||||
|
Bug #3549: Blood effects occur even when a hit is resisted
|
||||||
|
Bug #3551: NPC Todwendy in german version can't interact
|
||||||
|
Bug #3552: Opening the journal when fonts are missing results in a crash
|
||||||
|
Bug #3555: SetInvisible command should not apply graphic effect
|
||||||
|
Bug #3561: Editor: changes from omwaddon are not loaded in [New Addon] mode
|
||||||
|
Bug #3562: Non-hostile NPCs can be disarmed by stealing their weapons via sneaking
|
||||||
|
Bug #3564: Editor: openmw-cs verification results
|
||||||
|
Bug #3568: Items that should be invisible are shown in the inventory
|
||||||
|
Bug #3574: Alchemy: Alembics and retorts are used in reverse
|
||||||
|
Bug #3575: Diaglog choices don't work in mw 0.40
|
||||||
|
Bug #3576: Minor differences in AI reaction to hostile spell effects
|
||||||
|
Bug #3577: not local nolore dialog test
|
||||||
|
Bug #3578: Animation Replacer hangs after one cicle/step
|
||||||
|
Bug #3579: Bound Armor skillups and sounds
|
||||||
|
Bug #3583: Targetted GetCurrentAiPackage returns 0
|
||||||
|
Bug #3584: Persuasion bug
|
||||||
|
Bug #3590: Vendor, Ilen Faveran, auto equips items from stock
|
||||||
|
Bug #3594: Weather doesn't seem to update correctly in Mournhold
|
||||||
|
Bug #3598: Saving doesn't save status of objects
|
||||||
|
Bug #3600: Screen goes black when trying to travel to Sadrith Mora
|
||||||
|
Bug #3608: Water ripples aren't created when walking on water
|
||||||
|
Bug #3626: Argonian NPCs swim like khajiits
|
||||||
|
Bug #3627: Cannot delete "Blessed touch" spell from spellbook
|
||||||
|
Bug #3634: An enchanted throwing weapon consumes charges from the stack in your inventory. (0.40.0)
|
||||||
|
Bug #3635: Levelled items in merchants are "re-rolled" (not bug 2952, see inside)
|
||||||
|
Feature #1118: AI combat: flee
|
||||||
|
Feature #1596: Editor: Render water
|
||||||
|
Feature #2042: Adding a non-portable Light to the inventory should cause the player to glow
|
||||||
|
Feature #3166: Editor: Instance editing mode - rotate sub mode
|
||||||
|
Feature #3167: Editor: Instance editing mode - scale sub mode
|
||||||
|
Feature #3420: ess-Importer: player control flags
|
||||||
|
Feature #3489: You shouldn't be be able to re-cast a bound equipment spell
|
||||||
|
Feature #3496: Zero-weight boots should play light boot footsteps
|
||||||
|
Feature #3516: Water Walking should give a "can't cast" message and fail when you are too deep
|
||||||
|
Feature #3519: Play audio and visual effects for all effects in a spell
|
||||||
|
Feature #3527: Double spell explosion scaling
|
||||||
|
Feature #3534: Play particle textures for spell effects
|
||||||
|
Feature #3539: Make NPCs use opponent's weapon range to decide whether to dodge
|
||||||
|
Feature #3540: Allow dodging for creatures with "biped" flag
|
||||||
|
Feature #3545: Drop shadow for items in menu
|
||||||
|
Feature #3558: Implement same spell range for "on touch" spells as original engine
|
||||||
|
Feature #3560: Allow using telekinesis with touch spells on objects
|
||||||
|
Task #3585: Some objects added by Morrowind Rebirth do not display properly their texture
|
||||||
|
|
||||||
0.40.0
|
0.40.0
|
||||||
------
|
------
|
||||||
|
|
||||||
|
@ -7,5 +7,5 @@ brew rm pkgconfig || true
|
|||||||
brew rm qt5 || true
|
brew rm qt5 || true
|
||||||
brew install cmake pkgconfig $macos_qt_formula
|
brew install cmake pkgconfig $macos_qt_formula
|
||||||
|
|
||||||
curl http://downloads.openmw.org/osx/dependencies/openmw-deps-263d4a8.zip -o ~/openmw-deps.zip
|
curl https://downloads.openmw.org/osx/dependencies/openmw-deps-0ecece4.zip -o ~/openmw-deps.zip
|
||||||
unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
||||||
|
@ -12,7 +12,7 @@ cd build
|
|||||||
cmake \
|
cmake \
|
||||||
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \
|
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \
|
||||||
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.8" \
|
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.8" \
|
||||||
-D CMAKE_OSX_SYSROOT="macosx10.11" \
|
-D CMAKE_OSX_SYSROOT="macosx10.12" \
|
||||||
-D CMAKE_BUILD_TYPE=Debug \
|
-D CMAKE_BUILD_TYPE=Debug \
|
||||||
-D OPENMW_OSX_DEPLOYMENT=TRUE \
|
-D OPENMW_OSX_DEPLOYMENT=TRUE \
|
||||||
-D DESIRED_QT_VERSION=5 \
|
-D DESIRED_QT_VERSION=5 \
|
||||||
|
@ -25,7 +25,7 @@ endif()
|
|||||||
message(STATUS "Configuring OpenMW...")
|
message(STATUS "Configuring OpenMW...")
|
||||||
|
|
||||||
set(OPENMW_VERSION_MAJOR 0)
|
set(OPENMW_VERSION_MAJOR 0)
|
||||||
set(OPENMW_VERSION_MINOR 40)
|
set(OPENMW_VERSION_MINOR 41)
|
||||||
set(OPENMW_VERSION_RELEASE 0)
|
set(OPENMW_VERSION_RELEASE 0)
|
||||||
|
|
||||||
set(OPENMW_VERSION_COMMITHASH "")
|
set(OPENMW_VERSION_COMMITHASH "")
|
||||||
|
@ -7,7 +7,7 @@ OpenMW is a recreation of the engine for the popular role-playing game Morrowind
|
|||||||
|
|
||||||
OpenMW also comes with OpenMW-CS, a replacement for Morrowind's TES Construction Set.
|
OpenMW also comes with OpenMW-CS, a replacement for Morrowind's TES Construction Set.
|
||||||
|
|
||||||
* Version: 0.40.0
|
* Version: 0.41.0
|
||||||
* License: GPLv3 (see [docs/license/GPL3.txt](https://github.com/OpenMW/openmw/blob/master/docs/license/GPL3.txt) for more information)
|
* License: GPLv3 (see [docs/license/GPL3.txt](https://github.com/OpenMW/openmw/blob/master/docs/license/GPL3.txt) for more information)
|
||||||
* Website: http://www.openmw.org
|
* Website: http://www.openmw.org
|
||||||
* IRC: #openmw on irc.freenode.net
|
* IRC: #openmw on irc.freenode.net
|
||||||
|
@ -75,6 +75,14 @@ namespace ESSImport
|
|||||||
out.mMarkedPosition.rot[0] = out.mMarkedPosition.rot[1] = 0.0f;
|
out.mMarkedPosition.rot[0] = out.mMarkedPosition.rot[1] = 0.0f;
|
||||||
out.mMarkedPosition.rot[2] = mark.mRotZ;
|
out.mMarkedPosition.rot[2] = mark.mRotZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pcdt.mHasENAM)
|
||||||
|
{
|
||||||
|
const int cellSize = 8192;
|
||||||
|
out.mLastKnownExteriorPosition[0] = (pcdt.mENAM.mCellX + 0.5f) * cellSize;
|
||||||
|
out.mLastKnownExteriorPosition[1] = (pcdt.mENAM.mCellY + 0.5f) * cellSize;
|
||||||
|
out.mLastKnownExteriorPosition[2] = 0.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,10 @@ namespace ESSImport
|
|||||||
playerCellId.mPaged = true;
|
playerCellId.mPaged = true;
|
||||||
playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0;
|
playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0;
|
||||||
mPlayer.mCellId = playerCellId;
|
mPlayer.mCellId = playerCellId;
|
||||||
//mPlayer.mLastKnownExteriorPosition
|
mPlayer.mLastKnownExteriorPosition[0]
|
||||||
|
= mPlayer.mLastKnownExteriorPosition[1]
|
||||||
|
= mPlayer.mLastKnownExteriorPosition[2]
|
||||||
|
= 0.0f;
|
||||||
mPlayer.mHasMark = 0;
|
mPlayer.mHasMark = 0;
|
||||||
mPlayer.mCurrentCrimeId = 0; // TODO
|
mPlayer.mCurrentCrimeId = 0; // TODO
|
||||||
mPlayer.mObject.blank();
|
mPlayer.mObject.blank();
|
||||||
|
@ -37,6 +37,14 @@ namespace ESSImport
|
|||||||
if (esm.isNextSub("NAM9"))
|
if (esm.isNextSub("NAM9"))
|
||||||
esm.skipHSub();
|
esm.skipHSub();
|
||||||
|
|
||||||
|
// Rest state. You shouldn't even be able to save during rest, but skip just in case.
|
||||||
|
if (esm.isNextSub("RNAM"))
|
||||||
|
/*
|
||||||
|
int hoursLeft;
|
||||||
|
float x, y, z; // resting position
|
||||||
|
*/
|
||||||
|
esm.skipHSub(); // 16 bytes
|
||||||
|
|
||||||
mBounty = 0;
|
mBounty = 0;
|
||||||
esm.getHNOT(mBounty, "CNAM");
|
esm.getHNOT(mBounty, "CNAM");
|
||||||
|
|
||||||
@ -70,12 +78,19 @@ namespace ESSImport
|
|||||||
mFactions.push_back(fnam);
|
mFactions.push_back(fnam);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (esm.isNextSub("AADT"))
|
mHasAADT = false;
|
||||||
esm.skipHSub(); // 44 bytes, no clue
|
if (esm.isNextSub("AADT")) // Attack animation data?
|
||||||
|
{
|
||||||
|
mHasAADT = true;
|
||||||
|
esm.getHT(mAADT);
|
||||||
|
}
|
||||||
|
|
||||||
if (esm.isNextSub("KNAM"))
|
if (esm.isNextSub("KNAM"))
|
||||||
esm.skipHSub(); // assigned Quick Keys, I think
|
esm.skipHSub(); // assigned Quick Keys, I think
|
||||||
|
|
||||||
|
if (esm.isNextSub("ANIS"))
|
||||||
|
esm.skipHSub(); // 16 bytes
|
||||||
|
|
||||||
if (esm.isNextSub("WERE"))
|
if (esm.isNextSub("WERE"))
|
||||||
{
|
{
|
||||||
// some werewolf data, 152 bytes
|
// some werewolf data, 152 bytes
|
||||||
@ -83,10 +98,6 @@ namespace ESSImport
|
|||||||
esm.getSubHeader();
|
esm.getSubHeader();
|
||||||
esm.skip(152);
|
esm.skip(152);
|
||||||
}
|
}
|
||||||
|
|
||||||
// unsure if before or after WERE
|
|
||||||
if (esm.isNextSub("ANIS"))
|
|
||||||
esm.skipHSub();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,12 @@ struct PCDT
|
|||||||
int mCellX;
|
int mCellX;
|
||||||
int mCellY;
|
int mCellY;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AADT // 44 bytes
|
||||||
|
{
|
||||||
|
int animGroupIndex; // See convertANIS() for the mapping.
|
||||||
|
unsigned char mUnknown5[40];
|
||||||
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
std::vector<FNAM> mFactions;
|
std::vector<FNAM> mFactions;
|
||||||
@ -109,6 +115,9 @@ struct PCDT
|
|||||||
bool mHasENAM;
|
bool mHasENAM;
|
||||||
ENAM mENAM; // last exterior cell
|
ENAM mENAM; // last exterior cell
|
||||||
|
|
||||||
|
bool mHasAADT;
|
||||||
|
AADT mAADT;
|
||||||
|
|
||||||
void load(ESM::ESMReader& esm);
|
void load(ESM::ESMReader& esm);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <set>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace osg
|
namespace osg
|
||||||
@ -200,6 +201,10 @@ namespace MWBase
|
|||||||
|
|
||||||
virtual std::list<MWWorld::Ptr> getEnemiesNearby(const MWWorld::Ptr& actor) = 0;
|
virtual std::list<MWWorld::Ptr> getEnemiesNearby(const MWWorld::Ptr& actor) = 0;
|
||||||
|
|
||||||
|
/// Recursive versions of above methods
|
||||||
|
virtual void getActorsFollowing(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out) = 0;
|
||||||
|
virtual void getActorsSidingWith(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out) = 0;
|
||||||
|
|
||||||
virtual void playerLoaded() = 0;
|
virtual void playerLoaded() = 0;
|
||||||
|
|
||||||
virtual int countSavedGameRecords() const = 0;
|
virtual int countSavedGameRecords() const = 0;
|
||||||
|
@ -448,14 +448,14 @@ namespace MWDialogue
|
|||||||
{
|
{
|
||||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue);
|
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue);
|
||||||
|
|
||||||
|
// Apply disposition change to NPC's base disposition
|
||||||
|
if (mActor.getClass().isNpc())
|
||||||
|
{
|
||||||
// Clamp permanent disposition change so that final disposition doesn't go below 0 (could happen with intimidate)
|
// Clamp permanent disposition change so that final disposition doesn't go below 0 (could happen with intimidate)
|
||||||
float curDisp = static_cast<float>(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor, false));
|
float curDisp = static_cast<float>(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor, false));
|
||||||
if (curDisp + mPermanentDispositionChange < 0)
|
if (curDisp + mPermanentDispositionChange < 0)
|
||||||
mPermanentDispositionChange = -curDisp;
|
mPermanentDispositionChange = -curDisp;
|
||||||
|
|
||||||
// Apply disposition change to NPC's base disposition
|
|
||||||
if (mActor.getClass().isNpc())
|
|
||||||
{
|
|
||||||
MWMechanics::NpcStats& npcStats = mActor.getClass().getNpcStats(mActor);
|
MWMechanics::NpcStats& npcStats = mActor.getClass().getNpcStats(mActor);
|
||||||
npcStats.setBaseDisposition(static_cast<int>(npcStats.getBaseDisposition() + mPermanentDispositionChange));
|
npcStats.setBaseDisposition(static_cast<int>(npcStats.getBaseDisposition() + mPermanentDispositionChange));
|
||||||
}
|
}
|
||||||
|
@ -107,11 +107,11 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
|
|||||||
bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const
|
bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const
|
||||||
{
|
{
|
||||||
const MWWorld::Ptr player = MWMechanics::getPlayer();
|
const MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||||
|
MWMechanics::NpcStats& stats = player.getClass().getNpcStats (player);
|
||||||
|
|
||||||
// check player faction
|
// check player faction and rank
|
||||||
if (!info.mPcFaction.empty())
|
if (!info.mPcFaction.empty())
|
||||||
{
|
{
|
||||||
MWMechanics::NpcStats& stats = player.getClass().getNpcStats (player);
|
|
||||||
std::map<std::string,int>::const_iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase (info.mPcFaction));
|
std::map<std::string,int>::const_iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase (info.mPcFaction));
|
||||||
|
|
||||||
if(iter==stats.getFactionRanks().end())
|
if(iter==stats.getFactionRanks().end())
|
||||||
@ -121,6 +121,18 @@ bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const
|
|||||||
if (iter->second < info.mData.mPCrank)
|
if (iter->second < info.mData.mPCrank)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
else if (info.mData.mPCrank != -1)
|
||||||
|
{
|
||||||
|
// required PC faction is not specified but PC rank is; use speaker's faction
|
||||||
|
std::map<std::string,int>::const_iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase (mActor.getClass().getPrimaryFaction(mActor)));
|
||||||
|
|
||||||
|
if(iter==stats.getFactionRanks().end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// check rank
|
||||||
|
if (iter->second < info.mData.mPCrank)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// check cell
|
// check cell
|
||||||
if (!info.mCell.empty())
|
if (!info.mCell.empty())
|
||||||
|
@ -428,7 +428,9 @@ namespace MWGui
|
|||||||
{
|
{
|
||||||
// split lines
|
// split lines
|
||||||
const int lineHeight = currentFontHeight();
|
const int lineHeight = currentFontHeight();
|
||||||
unsigned int lastLine = (mPaginator.getStartTop() + mPaginator.getPageHeight() - mPaginator.getCurrentTop()) / lineHeight;
|
unsigned int lastLine = (mPaginator.getStartTop() + mPaginator.getPageHeight() - mPaginator.getCurrentTop());
|
||||||
|
if (lineHeight > 0)
|
||||||
|
lastLine /= lineHeight;
|
||||||
int ret = mPaginator.getCurrentTop() + lastLine * lineHeight;
|
int ret = mPaginator.getCurrentTop() + lastLine * lineHeight;
|
||||||
|
|
||||||
// first empty lines that would go to the next page should be ignored
|
// first empty lines that would go to the next page should be ignored
|
||||||
|
@ -55,7 +55,7 @@ namespace MWGui
|
|||||||
|
|
||||||
void TravelWindow::addDestination(const std::string& name,ESM::Position pos,bool interior)
|
void TravelWindow::addDestination(const std::string& name,ESM::Position pos,bool interior)
|
||||||
{
|
{
|
||||||
int price = 0;
|
int price;
|
||||||
|
|
||||||
const MWWorld::Store<ESM::GameSetting> &gmst =
|
const MWWorld::Store<ESM::GameSetting> &gmst =
|
||||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||||
@ -70,14 +70,21 @@ namespace MWGui
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
ESM::Position PlayerPos = player.getRefData().getPosition();
|
ESM::Position PlayerPos = player.getRefData().getPosition();
|
||||||
float d = sqrt( pow(pos.pos[0] - PlayerPos.pos[0],2) + pow(pos.pos[1] - PlayerPos.pos[1],2) + pow(pos.pos[2] - PlayerPos.pos[2],2) );
|
float d = sqrt(pow(pos.pos[0] - PlayerPos.pos[0], 2) + pow(pos.pos[1] - PlayerPos.pos[1], 2) + pow(pos.pos[2] - PlayerPos.pos[2], 2));
|
||||||
price = static_cast<int>(d / gmst.find("fTravelMult")->getFloat());
|
price = static_cast<int>(d / gmst.find("fTravelMult")->getFloat());
|
||||||
}
|
}
|
||||||
|
|
||||||
price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true);
|
price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true);
|
||||||
|
|
||||||
|
// Add price for the travelling followers
|
||||||
|
std::set<MWWorld::Ptr> followers;
|
||||||
|
MWWorld::ActionTeleport::getFollowersToTeleport(player, followers);
|
||||||
|
|
||||||
|
// Apply followers cost, in vanilla one follower travels for free
|
||||||
|
price *= std::max(1, static_cast<int>(followers.size()));
|
||||||
|
|
||||||
MyGUI::Button* toAdd = mDestinationsView->createWidget<MyGUI::Button>("SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default);
|
MyGUI::Button* toAdd = mDestinationsView->createWidget<MyGUI::Button>("SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default);
|
||||||
toAdd->setEnabled(price<=playerGold);
|
toAdd->setEnabled(price <= playerGold);
|
||||||
mCurrentY += sLineHeight;
|
mCurrentY += sLineHeight;
|
||||||
if(interior)
|
if(interior)
|
||||||
toAdd->setUserString("interior","y");
|
toAdd->setUserString("interior","y");
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "movement.hpp"
|
#include "movement.hpp"
|
||||||
#include "character.hpp"
|
#include "character.hpp"
|
||||||
#include "aicombat.hpp"
|
#include "aicombat.hpp"
|
||||||
|
#include "aicombataction.hpp"
|
||||||
#include "aifollow.hpp"
|
#include "aifollow.hpp"
|
||||||
#include "aipursue.hpp"
|
#include "aipursue.hpp"
|
||||||
#include "actor.hpp"
|
#include "actor.hpp"
|
||||||
@ -286,10 +287,12 @@ namespace MWMechanics
|
|||||||
|
|
||||||
void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer)
|
void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer)
|
||||||
{
|
{
|
||||||
CreatureStats& creatureStats = actor1.getClass().getCreatureStats(actor1);
|
const CreatureStats& creatureStats1 = actor1.getClass().getCreatureStats(actor1);
|
||||||
|
if (creatureStats1.getAiSequence().isInCombat(actor2))
|
||||||
|
return;
|
||||||
|
|
||||||
if (actor2.getClass().getCreatureStats(actor2).isDead()
|
const CreatureStats& creatureStats2 = actor2.getClass().getCreatureStats(actor2);
|
||||||
|| actor1.getClass().getCreatureStats(actor1).isDead())
|
if (creatureStats1.isDead() || creatureStats2.isDead())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const ESM::Position& actor1Pos = actor1.getRefData().getPosition();
|
const ESM::Position& actor1Pos = actor1.getRefData().getPosition();
|
||||||
@ -298,55 +301,26 @@ namespace MWMechanics
|
|||||||
if (sqrDist > sqrAiProcessingDistance)
|
if (sqrDist > sqrAiProcessingDistance)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// pure water creatures won't try to fight with the target on the ground
|
// No combat for totally static creatures
|
||||||
// except that creature is already hostile
|
|
||||||
if ((againstPlayer || !creatureStats.getAiSequence().isInCombat())
|
|
||||||
&& !MWMechanics::isEnvironmentCompatible(actor1, actor2)) // creature can't swim to target
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// no combat for totally static creatures (they have no movement or attack animations anyway)
|
|
||||||
if (!actor1.getClass().isMobile(actor1))
|
if (!actor1.getClass().isMobile(actor1))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool aggressive;
|
// Start combat if target actor is in combat with one of our followers or escorters
|
||||||
|
const std::list<MWWorld::Ptr>& followersAndEscorters = getActorsSidingWith(actor1);
|
||||||
if (againstPlayer)
|
for (std::list<MWWorld::Ptr>::const_iterator it = followersAndEscorters.begin(); it != followersAndEscorters.end(); ++it)
|
||||||
{
|
{
|
||||||
// followers with high fight should not engage in combat with the player (e.g. bm_bear_black_summon)
|
// Need to check both ways since player doesn't use AI packages
|
||||||
const std::list<MWWorld::Ptr>& followers = getActorsSidingWith(actor2);
|
|
||||||
if (std::find(followers.begin(), followers.end(), actor1) != followers.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
aggressive = false;
|
|
||||||
|
|
||||||
// Make guards fight aggressive creatures
|
|
||||||
if (!actor1.getClass().isNpc() && actor2.getClass().isClass(actor2, "Guard"))
|
|
||||||
{
|
|
||||||
if (creatureStats.getAiSequence().isInCombat() && MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2))
|
|
||||||
aggressive = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// start combat if target actor is in combat with one of our followers
|
|
||||||
const std::list<MWWorld::Ptr>& followers = getActorsSidingWith(actor1);
|
|
||||||
const CreatureStats& creatureStats2 = actor2.getClass().getCreatureStats(actor2);
|
|
||||||
for (std::list<MWWorld::Ptr>::const_iterator it = followers.begin(); it != followers.end(); ++it)
|
|
||||||
{
|
|
||||||
// need to check both ways since player doesn't use AI packages
|
|
||||||
if ((creatureStats2.getAiSequence().isInCombat(*it)
|
if ((creatureStats2.getAiSequence().isInCombat(*it)
|
||||||
|| it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(actor2))
|
|| it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(actor2))
|
||||||
&& !creatureStats.getAiSequence().isInCombat(*it))
|
&& !creatureStats1.getAiSequence().isInCombat(*it))
|
||||||
aggressive = true;
|
{
|
||||||
|
MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// start combat if target actor is in combat with someone we are following
|
// Start combat if target actor is in combat with someone we are following through a follow package
|
||||||
for (std::list<MWMechanics::AiPackage*>::const_iterator it = creatureStats.getAiSequence().begin(); it != creatureStats.getAiSequence().end(); ++it)
|
for (std::list<MWMechanics::AiPackage*>::const_iterator it = creatureStats1.getAiSequence().begin(); it != creatureStats1.getAiSequence().end(); ++it)
|
||||||
{
|
{
|
||||||
if (!(*it)->sideWithTarget())
|
if (!(*it)->sideWithTarget())
|
||||||
continue;
|
continue;
|
||||||
@ -356,20 +330,63 @@ namespace MWMechanics
|
|||||||
if (followTarget.isEmpty())
|
if (followTarget.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (creatureStats.getAiSequence().isInCombat(followTarget))
|
if (creatureStats1.getAiSequence().isInCombat(followTarget))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// need to check both ways since player doesn't use AI packages
|
// Need to check both ways since player doesn't use AI packages
|
||||||
if (creatureStats2.getAiSequence().isInCombat(followTarget)
|
if (creatureStats2.getAiSequence().isInCombat(followTarget)
|
||||||
|| followTarget.getClass().getCreatureStats(followTarget).getAiSequence().isInCombat(actor2))
|
|| followTarget.getClass().getCreatureStats(followTarget).getAiSequence().isInCombat(actor2))
|
||||||
aggressive = true;
|
{
|
||||||
|
MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(aggressive)
|
// Start combat with the player if we are already in combat with a player follower or escorter
|
||||||
|
const std::list<MWWorld::Ptr>& playerFollowersAndEscorters = getActorsSidingWith(getPlayer());
|
||||||
|
if (againstPlayer)
|
||||||
|
{
|
||||||
|
for (std::list<MWWorld::Ptr>::const_iterator it = playerFollowersAndEscorters.begin(); it != playerFollowersAndEscorters.end(); ++it)
|
||||||
|
{
|
||||||
|
if (creatureStats1.getAiSequence().isInCombat(*it))
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, don't initiate combat with an unreachable target
|
||||||
|
if (!MWMechanics::canFight(actor1,actor2))
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool aggressive = false;
|
||||||
|
|
||||||
|
if (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end())
|
||||||
|
{
|
||||||
|
// Player followers and escorters with high fight should not initiate combat here with the player or with
|
||||||
|
// other player followers or escorters
|
||||||
|
if (std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor1) != playerFollowersAndEscorters.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Make guards fight aggressive creatures
|
||||||
|
if (!actor1.getClass().isNpc() && actor2.getClass().isClass(actor2, "Guard"))
|
||||||
|
{
|
||||||
|
if (creatureStats1.getAiSequence().isInCombat() && MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2))
|
||||||
|
aggressive = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aggressive)
|
||||||
{
|
{
|
||||||
bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2);
|
bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2);
|
||||||
|
|
||||||
if (againstPlayer) LOS &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1);
|
if (againstPlayer || std::find(playerFollowersAndEscorters.begin(), playerFollowersAndEscorters.end(), actor2) != playerFollowersAndEscorters.end())
|
||||||
|
LOS &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1);
|
||||||
|
|
||||||
if (LOS)
|
if (LOS)
|
||||||
{
|
{
|
||||||
@ -1482,6 +1499,20 @@ namespace MWMechanics
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Actors::getActorsFollowing(const MWWorld::Ptr &actor, std::set<MWWorld::Ptr>& out) {
|
||||||
|
std::list<MWWorld::Ptr> followers = getActorsFollowing(actor);
|
||||||
|
for(std::list<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)
|
||||||
|
if (out.insert(*it).second)
|
||||||
|
getActorsFollowing(*it, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Actors::getActorsSidingWith(const MWWorld::Ptr &actor, std::set<MWWorld::Ptr>& out) {
|
||||||
|
std::list<MWWorld::Ptr> followers = getActorsSidingWith(actor);
|
||||||
|
for(std::list<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)
|
||||||
|
if (out.insert(*it).second)
|
||||||
|
getActorsSidingWith(*it, out);
|
||||||
|
}
|
||||||
|
|
||||||
std::list<int> Actors::getActorsFollowingIndices(const MWWorld::Ptr &actor)
|
std::list<int> Actors::getActorsFollowingIndices(const MWWorld::Ptr &actor)
|
||||||
{
|
{
|
||||||
std::list<int> list;
|
std::list<int> list;
|
||||||
|
@ -123,6 +123,11 @@ namespace MWMechanics
|
|||||||
std::list<MWWorld::Ptr> getActorsSidingWith(const MWWorld::Ptr& actor);
|
std::list<MWWorld::Ptr> getActorsSidingWith(const MWWorld::Ptr& actor);
|
||||||
std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor);
|
std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor);
|
||||||
|
|
||||||
|
/// Recursive version of getActorsFollowing
|
||||||
|
void getActorsFollowing(const MWWorld::Ptr &actor, std::set<MWWorld::Ptr>& out);
|
||||||
|
/// Recursive version of getActorsSidingWith
|
||||||
|
void getActorsSidingWith(const MWWorld::Ptr &actor, std::set<MWWorld::Ptr>& out);
|
||||||
|
|
||||||
/// Get the list of AiFollow::mFollowIndex for all actors following this target
|
/// Get the list of AiFollow::mFollowIndex for all actors following this target
|
||||||
std::list<int> getActorsFollowingIndices(const MWWorld::Ptr& actor);
|
std::list<int> getActorsFollowingIndices(const MWWorld::Ptr& actor);
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "aicombataction.hpp"
|
#include "aicombataction.hpp"
|
||||||
#include "combat.hpp"
|
#include "combat.hpp"
|
||||||
#include "coordinateconverter.hpp"
|
#include "coordinateconverter.hpp"
|
||||||
|
#include "actorutil.hpp"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@ -210,13 +211,14 @@ namespace MWMechanics
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
timerReact = 0;
|
timerReact = 0;
|
||||||
attack(actor, target, storage, characterController);
|
if (attack(actor, target, storage, characterController))
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController)
|
bool AiCombat::attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController)
|
||||||
{
|
{
|
||||||
const MWWorld::CellStore*& currentCell = storage.mCell;
|
const MWWorld::CellStore*& currentCell = storage.mCell;
|
||||||
bool cellChange = currentCell && (actor.getCell() != currentCell);
|
bool cellChange = currentCell && (actor.getCell() != currentCell);
|
||||||
@ -231,7 +233,10 @@ namespace MWMechanics
|
|||||||
storage.stopAttack();
|
storage.stopAttack();
|
||||||
characterController.setAttackingOrSpell(false);
|
characterController.setAttackingOrSpell(false);
|
||||||
storage.mActionCooldown = 0.f;
|
storage.mActionCooldown = 0.f;
|
||||||
|
if (target == MWMechanics::getPlayer())
|
||||||
forceFlee = true;
|
forceFlee = true;
|
||||||
|
else
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MWWorld::Class& actorClass = actor.getClass();
|
const MWWorld::Class& actorClass = actor.getClass();
|
||||||
@ -243,7 +248,7 @@ namespace MWMechanics
|
|||||||
if (!forceFlee)
|
if (!forceFlee)
|
||||||
{
|
{
|
||||||
if (actionCooldown > 0)
|
if (actionCooldown > 0)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
if (characterController.readyToPrepareAttack())
|
if (characterController.readyToPrepareAttack())
|
||||||
{
|
{
|
||||||
@ -258,7 +263,7 @@ namespace MWMechanics
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!currentAction)
|
if (!currentAction)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
if (storage.isFleeing() != currentAction->isFleeing())
|
if (storage.isFleeing() != currentAction->isFleeing())
|
||||||
{
|
{
|
||||||
@ -266,7 +271,7 @@ namespace MWMechanics
|
|||||||
{
|
{
|
||||||
storage.startFleeing();
|
storage.startFleeing();
|
||||||
MWBase::Environment::get().getDialogueManager()->say(actor, "flee");
|
MWBase::Environment::get().getDialogueManager()->say(actor, "flee");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
storage.stopFleeing();
|
storage.stopFleeing();
|
||||||
@ -311,6 +316,7 @@ namespace MWMechanics
|
|||||||
storage.mMovement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos)); // using vAimDir results in spastic movements since the head is animated
|
storage.mMovement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos)); // using vAimDir results in spastic movements since the head is animated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWMechanics::AiCombat::updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, MWMechanics::AiCombatStorage& storage)
|
void MWMechanics::AiCombat::updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, MWMechanics::AiCombatStorage& storage)
|
||||||
|
@ -59,7 +59,8 @@ namespace MWMechanics
|
|||||||
|
|
||||||
int mTargetActorId;
|
int mTargetActorId;
|
||||||
|
|
||||||
void attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController);
|
/// Returns true if combat should end
|
||||||
|
bool attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController);
|
||||||
|
|
||||||
void updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, AiCombatStorage& storage);
|
void updateLOS(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, float duration, AiCombatStorage& storage);
|
||||||
|
|
||||||
|
@ -978,18 +978,6 @@ namespace MWMechanics
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void getFollowers (const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out)
|
|
||||||
{
|
|
||||||
std::list<MWWorld::Ptr> followers = MWBase::Environment::get().getMechanicsManager()->getActorsSidingWith(actor);
|
|
||||||
for(std::list<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)
|
|
||||||
{
|
|
||||||
if (out.insert(*it).second)
|
|
||||||
{
|
|
||||||
getFollowers(*it, out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MechanicsManager::commitCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg, bool victimAware)
|
bool MechanicsManager::commitCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg, bool victimAware)
|
||||||
{
|
{
|
||||||
// NOTE: victim may be empty
|
// NOTE: victim may be empty
|
||||||
@ -1013,7 +1001,7 @@ namespace MWMechanics
|
|||||||
|
|
||||||
// get the player's followers / allies (works recursively) that will not report crimes
|
// get the player's followers / allies (works recursively) that will not report crimes
|
||||||
std::set<MWWorld::Ptr> playerFollowers;
|
std::set<MWWorld::Ptr> playerFollowers;
|
||||||
getFollowers(player, playerFollowers);
|
getActorsSidingWith(player, playerFollowers);
|
||||||
|
|
||||||
// Did anyone see it?
|
// Did anyone see it?
|
||||||
bool crimeSeen = false;
|
bool crimeSeen = false;
|
||||||
@ -1437,6 +1425,14 @@ namespace MWMechanics
|
|||||||
return mActors.getEnemiesNearby(actor);
|
return mActors.getEnemiesNearby(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MechanicsManager::getActorsFollowing(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out) {
|
||||||
|
mActors.getActorsFollowing(actor, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MechanicsManager::getActorsSidingWith(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out) {
|
||||||
|
mActors.getActorsSidingWith(actor, out);
|
||||||
|
}
|
||||||
|
|
||||||
int MechanicsManager::countSavedGameRecords() const
|
int MechanicsManager::countSavedGameRecords() const
|
||||||
{
|
{
|
||||||
return 1 // Death counter
|
return 1 // Death counter
|
||||||
|
@ -165,6 +165,11 @@ namespace MWMechanics
|
|||||||
virtual std::list<MWWorld::Ptr> getActorsFighting(const MWWorld::Ptr& actor);
|
virtual std::list<MWWorld::Ptr> getActorsFighting(const MWWorld::Ptr& actor);
|
||||||
virtual std::list<MWWorld::Ptr> getEnemiesNearby(const MWWorld::Ptr& actor);
|
virtual std::list<MWWorld::Ptr> getEnemiesNearby(const MWWorld::Ptr& actor);
|
||||||
|
|
||||||
|
/// Recursive version of getActorsFollowing
|
||||||
|
virtual void getActorsFollowing(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out);
|
||||||
|
/// Recursive version of getActorsSidingWith
|
||||||
|
virtual void getActorsSidingWith(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out);
|
||||||
|
|
||||||
virtual bool toggleAI();
|
virtual bool toggleAI();
|
||||||
virtual bool isAIActive();
|
virtual bool isAIActive();
|
||||||
|
|
||||||
|
@ -613,6 +613,11 @@ namespace MWMechanics
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (target.getClass().isActor() && effectId == ESM::MagicEffect::Dispel)
|
||||||
|
{
|
||||||
|
target.getClass().getCreatureStats(target).getActiveSpells().purgeAll(magnitude);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
else if (target.getClass().isActor() && target == getPlayer())
|
else if (target.getClass().isActor() && target == getPlayer())
|
||||||
{
|
{
|
||||||
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mCaster);
|
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mCaster);
|
||||||
@ -1140,9 +1145,6 @@ namespace MWMechanics
|
|||||||
case ESM::MagicEffect::CureCorprusDisease:
|
case ESM::MagicEffect::CureCorprusDisease:
|
||||||
actor.getClass().getCreatureStats(actor).getSpells().purgeCorprusDisease();
|
actor.getClass().getCreatureStats(actor).getSpells().purgeCorprusDisease();
|
||||||
break;
|
break;
|
||||||
case ESM::MagicEffect::Dispel:
|
|
||||||
actor.getClass().getCreatureStats(actor).getActiveSpells().purgeAll(magnitude);
|
|
||||||
break;
|
|
||||||
case ESM::MagicEffect::RemoveCurse:
|
case ESM::MagicEffect::RemoveCurse:
|
||||||
actor.getClass().getCreatureStats(actor).getSpells().purgeCurses();
|
actor.getClass().getCreatureStats(actor).getSpells().purgeCurses();
|
||||||
break;
|
break;
|
||||||
|
@ -55,29 +55,46 @@ namespace MWPhysics
|
|||||||
static const float sMaxSlope = 49.0f;
|
static const float sMaxSlope = 49.0f;
|
||||||
static const float sStepSizeUp = 34.0f;
|
static const float sStepSizeUp = 34.0f;
|
||||||
static const float sStepSizeDown = 62.0f;
|
static const float sStepSizeDown = 62.0f;
|
||||||
|
static const float sMinStep = 10.f;
|
||||||
|
|
||||||
// Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared.
|
// Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared.
|
||||||
static const int sMaxIterations = 8;
|
static const int sMaxIterations = 8;
|
||||||
|
|
||||||
// FIXME: move to a separate file
|
static bool isActor(const btCollisionObject *obj)
|
||||||
class MovementSolver
|
|
||||||
{
|
{
|
||||||
private:
|
assert(obj);
|
||||||
static float getSlope(osg::Vec3f normal)
|
return obj->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor;
|
||||||
{
|
|
||||||
normal.normalize();
|
|
||||||
return osg::RadiansToDegrees(std::acos(normal * osg::Vec3f(0.f, 0.f, 1.f)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum StepMoveResult
|
template <class Vec3>
|
||||||
|
static bool isWalkableSlope(const Vec3 &normal)
|
||||||
{
|
{
|
||||||
Result_Blocked, // unable to move over obstacle
|
static const float sMaxSlopeCos = std::cos(osg::DegreesToRadians(sMaxSlope));
|
||||||
Result_MaxSlope, // unable to end movement on this slope
|
return (normal.z() > sMaxSlopeCos);
|
||||||
Result_Success
|
}
|
||||||
};
|
|
||||||
|
|
||||||
static StepMoveResult stepMove(const btCollisionObject *colobj, osg::Vec3f &position,
|
static bool canStepDown(const ActorTracer &stepper)
|
||||||
const osg::Vec3f &toMove, float &remainingTime, const btCollisionWorld* collisionWorld)
|
{
|
||||||
|
return stepper.mHitObject && isWalkableSlope(stepper.mPlaneNormal) && !isActor(stepper.mHitObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Stepper
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
const btCollisionWorld *mColWorld;
|
||||||
|
const btCollisionObject *mColObj;
|
||||||
|
|
||||||
|
ActorTracer mTracer, mUpStepper, mDownStepper;
|
||||||
|
bool mHaveMoved;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Stepper(const btCollisionWorld *colWorld, const btCollisionObject *colObj)
|
||||||
|
: mColWorld(colWorld)
|
||||||
|
, mColObj(colObj)
|
||||||
|
, mHaveMoved(true)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool step(osg::Vec3f &position, const osg::Vec3f &toMove, float &remainingTime)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Slide up an incline or set of stairs. Should be called only after a
|
* Slide up an incline or set of stairs. Should be called only after a
|
||||||
@ -123,12 +140,14 @@ namespace MWPhysics
|
|||||||
* +--+ +--------
|
* +--+ +--------
|
||||||
* ==============================================
|
* ==============================================
|
||||||
*/
|
*/
|
||||||
ActorTracer tracer, stepper;
|
if (mHaveMoved)
|
||||||
|
{
|
||||||
stepper.doTrace(colobj, position, position+osg::Vec3f(0.0f,0.0f,sStepSizeUp), collisionWorld);
|
mHaveMoved = false;
|
||||||
if(stepper.mFraction < std::numeric_limits<float>::epsilon())
|
mUpStepper.doTrace(mColObj, position, position+osg::Vec3f(0.0f,0.0f,sStepSizeUp), mColWorld);
|
||||||
return Result_Blocked; // didn't even move the smallest representable amount
|
if(mUpStepper.mFraction < std::numeric_limits<float>::epsilon())
|
||||||
|
return false; // didn't even move the smallest representable amount
|
||||||
// (TODO: shouldn't this be larger? Why bother with such a small amount?)
|
// (TODO: shouldn't this be larger? Why bother with such a small amount?)
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try moving from the elevated position using tracer.
|
* Try moving from the elevated position using tracer.
|
||||||
@ -143,9 +162,10 @@ namespace MWPhysics
|
|||||||
* +--+
|
* +--+
|
||||||
* ==============================================
|
* ==============================================
|
||||||
*/
|
*/
|
||||||
tracer.doTrace(colobj, stepper.mEndPos, stepper.mEndPos + toMove, collisionWorld);
|
osg::Vec3f tracerPos = mUpStepper.mEndPos;
|
||||||
if(tracer.mFraction < std::numeric_limits<float>::epsilon())
|
mTracer.doTrace(mColObj, tracerPos, tracerPos + toMove, mColWorld);
|
||||||
return Result_Blocked; // didn't even move the smallest representable amount
|
if(mTracer.mFraction < std::numeric_limits<float>::epsilon())
|
||||||
|
return false; // didn't even move the smallest representable amount
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try moving back down sStepSizeDown using stepper.
|
* Try moving back down sStepSizeDown using stepper.
|
||||||
@ -162,26 +182,40 @@ namespace MWPhysics
|
|||||||
* +--+ +--+
|
* +--+ +--+
|
||||||
* ==============================================
|
* ==============================================
|
||||||
*/
|
*/
|
||||||
stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), collisionWorld);
|
mDownStepper.doTrace(mColObj, mTracer.mEndPos, mTracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), mColWorld);
|
||||||
if (getSlope(stepper.mPlaneNormal) > sMaxSlope)
|
if (!canStepDown(mDownStepper))
|
||||||
return Result_MaxSlope;
|
{
|
||||||
if(stepper.mFraction < 1.0f)
|
// Try again with increased step length
|
||||||
|
if (mTracer.mFraction < 1.0f || toMove.length2() > sMinStep*sMinStep)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
osg::Vec3f direction = toMove;
|
||||||
|
direction.normalize();
|
||||||
|
mTracer.doTrace(mColObj, tracerPos, tracerPos + direction*sMinStep, mColWorld);
|
||||||
|
if (mTracer.mFraction < 0.001f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
mDownStepper.doTrace(mColObj, mTracer.mEndPos, mTracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), mColWorld);
|
||||||
|
if (!canStepDown(mDownStepper))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (mDownStepper.mFraction < 1.0f)
|
||||||
{
|
{
|
||||||
// don't allow stepping up other actors
|
|
||||||
if (stepper.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor)
|
|
||||||
return Result_Blocked;
|
|
||||||
// only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall.
|
// only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall.
|
||||||
// TODO: stepper.mPlaneNormal does not appear to be reliable - needs more testing
|
// TODO: stepper.mPlaneNormal does not appear to be reliable - needs more testing
|
||||||
// NOTE: caller's variables 'position' & 'remainingTime' are modified here
|
// NOTE: caller's variables 'position' & 'remainingTime' are modified here
|
||||||
position = stepper.mEndPos;
|
position = mDownStepper.mEndPos;
|
||||||
remainingTime *= (1.0f-tracer.mFraction); // remaining time is proportional to remaining distance
|
remainingTime *= (1.0f-mTracer.mFraction); // remaining time is proportional to remaining distance
|
||||||
return Result_Success;
|
mHaveMoved = true;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
return Result_Blocked;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MovementSolver
|
||||||
|
{
|
||||||
|
private:
|
||||||
///Project a vector u on another vector v
|
///Project a vector u on another vector v
|
||||||
static inline osg::Vec3f project(const osg::Vec3f& u, const osg::Vec3f &v)
|
static inline osg::Vec3f project(const osg::Vec3f& u, const osg::Vec3f &v)
|
||||||
{
|
{
|
||||||
@ -229,14 +263,14 @@ namespace MWPhysics
|
|||||||
collisionWorld->rayTest(from, to, resultCallback1);
|
collisionWorld->rayTest(from, to, resultCallback1);
|
||||||
|
|
||||||
if (resultCallback1.hasHit() &&
|
if (resultCallback1.hasHit() &&
|
||||||
( (toOsg(resultCallback1.m_hitPointWorld) - tracer.mEndPos).length() > 35
|
( (toOsg(resultCallback1.m_hitPointWorld) - tracer.mEndPos).length2() > 35*35
|
||||||
|| getSlope(tracer.mPlaneNormal) > sMaxSlope))
|
|| !isWalkableSlope(tracer.mPlaneNormal)))
|
||||||
{
|
{
|
||||||
actor->setOnGround(getSlope(toOsg(resultCallback1.m_hitNormalWorld)) <= sMaxSlope);
|
actor->setOnGround(isWalkableSlope(resultCallback1.m_hitNormalWorld));
|
||||||
return toOsg(resultCallback1.m_hitPointWorld) + osg::Vec3f(0.f, 0.f, 1.f);
|
return toOsg(resultCallback1.m_hitPointWorld) + osg::Vec3f(0.f, 0.f, 1.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
actor->setOnGround(getSlope(tracer.mPlaneNormal) <= sMaxSlope);
|
actor->setOnGround(isWalkableSlope(tracer.mPlaneNormal));
|
||||||
|
|
||||||
return tracer.mEndPos;
|
return tracer.mEndPos;
|
||||||
}
|
}
|
||||||
@ -312,8 +346,8 @@ namespace MWPhysics
|
|||||||
velocity *= 1.f-(fStromWalkMult * (angleDegrees/180.f));
|
velocity *= 1.f-(fStromWalkMult * (angleDegrees/180.f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stepper stepper(collisionWorld, colobj);
|
||||||
osg::Vec3f origVelocity = velocity;
|
osg::Vec3f origVelocity = velocity;
|
||||||
|
|
||||||
osg::Vec3f newPosition = position;
|
osg::Vec3f newPosition = position;
|
||||||
/*
|
/*
|
||||||
* A loop to find newPosition using tracer, if successful different from the starting position.
|
* A loop to find newPosition using tracer, if successful different from the starting position.
|
||||||
@ -332,10 +366,7 @@ namespace MWPhysics
|
|||||||
newPosition.z() <= swimlevel)
|
newPosition.z() <= swimlevel)
|
||||||
{
|
{
|
||||||
const osg::Vec3f down(0,0,-1);
|
const osg::Vec3f down(0,0,-1);
|
||||||
float movelen = velocity.normalize();
|
velocity = slide(velocity, down);
|
||||||
osg::Vec3f reflectdir = reflect(velocity, down);
|
|
||||||
reflectdir.normalize();
|
|
||||||
velocity = slide(reflectdir, down)*movelen;
|
|
||||||
// NOTE: remainingTime is unchanged before the loop continues
|
// NOTE: remainingTime is unchanged before the loop continues
|
||||||
continue; // velocity updated, calculate nextpos again
|
continue; // velocity updated, calculate nextpos again
|
||||||
}
|
}
|
||||||
@ -364,19 +395,25 @@ namespace MWPhysics
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We are touching something.
|
||||||
osg::Vec3f oldPosition = newPosition;
|
if (tracer.mFraction < 1E-9f)
|
||||||
// We hit something. Try to step up onto it. (NOTE: stepMove does not allow stepping over)
|
|
||||||
// NOTE: stepMove modifies newPosition if successful
|
|
||||||
const float minStep = 10.f;
|
|
||||||
StepMoveResult result = stepMove(colobj, newPosition, velocity*remainingTime, remainingTime, collisionWorld);
|
|
||||||
if (result == Result_MaxSlope && (velocity*remainingTime).length() < minStep) // to make sure the maximum stepping distance isn't framerate-dependent or movement-speed dependent
|
|
||||||
{
|
{
|
||||||
osg::Vec3f normalizedVelocity = velocity;
|
// Try to separate by backing off slighly to unstuck the solver
|
||||||
normalizedVelocity.normalize();
|
const osg::Vec3f backOff = (newPosition - tracer.mHitPoint) * 1E-3f;
|
||||||
result = stepMove(colobj, newPosition, normalizedVelocity*minStep, remainingTime, collisionWorld);
|
newPosition += backOff;
|
||||||
}
|
}
|
||||||
if(result == Result_Success)
|
|
||||||
|
// We hit something. Check if we can step up.
|
||||||
|
float hitHeight = tracer.mHitPoint.z() - tracer.mEndPos.z() + halfExtents.z();
|
||||||
|
osg::Vec3f oldPosition = newPosition;
|
||||||
|
bool result = false;
|
||||||
|
if (hitHeight < sStepSizeUp && !isActor(tracer.mHitObject))
|
||||||
|
{
|
||||||
|
// Try to step up onto it.
|
||||||
|
// NOTE: stepMove does not allow stepping over, modifies newPosition if successful
|
||||||
|
result = stepper.step(newPosition, velocity*remainingTime, remainingTime);
|
||||||
|
}
|
||||||
|
if (result)
|
||||||
{
|
{
|
||||||
// don't let pure water creatures move out of water after stepMove
|
// don't let pure water creatures move out of water after stepMove
|
||||||
if (ptr.getClass().isPureWaterCreature(ptr)
|
if (ptr.getClass().isPureWaterCreature(ptr)
|
||||||
@ -386,23 +423,19 @@ namespace MWPhysics
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Can't move this way, try to find another spot along the plane
|
// Can't move this way, try to find another spot along the plane
|
||||||
osg::Vec3f direction = velocity;
|
osg::Vec3f newVelocity = slide(velocity, tracer.mPlaneNormal);
|
||||||
float movelen = direction.normalize();
|
|
||||||
osg::Vec3f reflectdir = reflect(velocity, tracer.mPlaneNormal);
|
// Do not allow sliding upward if there is gravity.
|
||||||
reflectdir.normalize();
|
// Stepping will have taken care of that.
|
||||||
|
if(!(newPosition.z() < swimlevel || isFlying))
|
||||||
|
newVelocity.z() = std::min(newVelocity.z(), 0.0f);
|
||||||
|
|
||||||
osg::Vec3f newVelocity = slide(reflectdir, tracer.mPlaneNormal)*movelen;
|
|
||||||
if ((newVelocity-velocity).length2() < 0.01)
|
if ((newVelocity-velocity).length2() < 0.01)
|
||||||
break;
|
break;
|
||||||
if ((velocity * origVelocity) <= 0.f)
|
if ((newVelocity * origVelocity) <= 0.f)
|
||||||
break; // ^ dot product
|
break; // ^ dot product
|
||||||
|
|
||||||
velocity = newVelocity;
|
velocity = newVelocity;
|
||||||
|
|
||||||
// Do not allow sliding upward if there is gravity. Stepping will have taken
|
|
||||||
// care of that.
|
|
||||||
if(!(newPosition.z() < swimlevel || isFlying))
|
|
||||||
velocity.z() = std::min(velocity.z(), 0.0f);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,7 +446,7 @@ namespace MWPhysics
|
|||||||
osg::Vec3f to = newPosition - (physicActor->getOnGround() ?
|
osg::Vec3f to = newPosition - (physicActor->getOnGround() ?
|
||||||
osg::Vec3f(0,0,sStepSizeDown+2.f) : osg::Vec3f(0,0,2.f));
|
osg::Vec3f(0,0,sStepSizeDown+2.f) : osg::Vec3f(0,0,2.f));
|
||||||
tracer.doTrace(colobj, from, to, collisionWorld);
|
tracer.doTrace(colobj, from, to, collisionWorld);
|
||||||
if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope
|
if(tracer.mFraction < 1.0f && isWalkableSlope(tracer.mPlaneNormal)
|
||||||
&& tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup != CollisionType_Actor)
|
&& tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup != CollisionType_Actor)
|
||||||
{
|
{
|
||||||
const btCollisionObject* standingOn = tracer.mHitObject;
|
const btCollisionObject* standingOn = tracer.mHitObject;
|
||||||
@ -996,6 +1029,8 @@ namespace MWPhysics
|
|||||||
bool PhysicsSystem::canMoveToWaterSurface(const MWWorld::ConstPtr &actor, const float waterlevel)
|
bool PhysicsSystem::canMoveToWaterSurface(const MWWorld::ConstPtr &actor, const float waterlevel)
|
||||||
{
|
{
|
||||||
const Actor* physicActor = getActor(actor);
|
const Actor* physicActor = getActor(actor);
|
||||||
|
if (!physicActor)
|
||||||
|
return false;
|
||||||
const float halfZ = physicActor->getHalfExtents().z();
|
const float halfZ = physicActor->getHalfExtents().z();
|
||||||
const osg::Vec3f actorPosition = physicActor->getPosition();
|
const osg::Vec3f actorPosition = physicActor->getPosition();
|
||||||
const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ);
|
const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ);
|
||||||
|
@ -78,6 +78,7 @@ void ActorTracer::doTrace(const btCollisionObject *actor, const osg::Vec3f& star
|
|||||||
mFraction = newTraceCallback.m_closestHitFraction;
|
mFraction = newTraceCallback.m_closestHitFraction;
|
||||||
mPlaneNormal = osg::Vec3f(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z());
|
mPlaneNormal = osg::Vec3f(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z());
|
||||||
mEndPos = (end-start)*mFraction + start;
|
mEndPos = (end-start)*mFraction + start;
|
||||||
|
mHitPoint = toOsg(newTraceCallback.m_hitPointWorld);
|
||||||
mHitObject = newTraceCallback.m_hitCollisionObject;
|
mHitObject = newTraceCallback.m_hitCollisionObject;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -85,6 +86,7 @@ void ActorTracer::doTrace(const btCollisionObject *actor, const osg::Vec3f& star
|
|||||||
mEndPos = end;
|
mEndPos = end;
|
||||||
mPlaneNormal = osg::Vec3f(0.0f, 0.0f, 1.0f);
|
mPlaneNormal = osg::Vec3f(0.0f, 0.0f, 1.0f);
|
||||||
mFraction = 1.0f;
|
mFraction = 1.0f;
|
||||||
|
mHitPoint = end;
|
||||||
mHitObject = NULL;
|
mHitObject = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ namespace MWPhysics
|
|||||||
{
|
{
|
||||||
osg::Vec3f mEndPos;
|
osg::Vec3f mEndPos;
|
||||||
osg::Vec3f mPlaneNormal;
|
osg::Vec3f mPlaneNormal;
|
||||||
|
osg::Vec3f mHitPoint;
|
||||||
const btCollisionObject* mHitObject;
|
const btCollisionObject* mHitObject;
|
||||||
|
|
||||||
float mFraction;
|
float mFraction;
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <osg/Group>
|
#include <osg/Group>
|
||||||
#include <osg/Geometry>
|
#include <osg/Geometry>
|
||||||
#include <osg/Depth>
|
#include <osg/Depth>
|
||||||
|
#include <osg/TexEnvCombine>
|
||||||
|
|
||||||
#include <osgDB/WriteFile>
|
#include <osgDB/WriteFile>
|
||||||
|
|
||||||
@ -144,6 +145,10 @@ namespace MWRender
|
|||||||
image->allocateImage(mWidth, mHeight, 1, GL_RGB, GL_UNSIGNED_BYTE);
|
image->allocateImage(mWidth, mHeight, 1, GL_RGB, GL_UNSIGNED_BYTE);
|
||||||
unsigned char* data = image->data();
|
unsigned char* data = image->data();
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Image> alphaImage = new osg::Image;
|
||||||
|
alphaImage->allocateImage(mWidth, mHeight, 1, GL_ALPHA, GL_UNSIGNED_BYTE);
|
||||||
|
unsigned char* alphaData = alphaImage->data();
|
||||||
|
|
||||||
for (int x = mMinX; x <= mMaxX; ++x)
|
for (int x = mMinX; x <= mMaxX; ++x)
|
||||||
{
|
{
|
||||||
for (int y = mMinY; y <= mMaxY; ++y)
|
for (int y = mMinY; y <= mMaxY; ++y)
|
||||||
@ -208,6 +213,8 @@ namespace MWRender
|
|||||||
data[texelY * mWidth * 3 + texelX * 3] = r;
|
data[texelY * mWidth * 3 + texelX * 3] = r;
|
||||||
data[texelY * mWidth * 3 + texelX * 3+1] = g;
|
data[texelY * mWidth * 3 + texelX * 3+1] = g;
|
||||||
data[texelY * mWidth * 3 + texelX * 3+2] = b;
|
data[texelY * mWidth * 3 + texelX * 3+2] = b;
|
||||||
|
|
||||||
|
alphaData[texelY * mWidth+ texelX] = (y2 < 0) ? static_cast<unsigned char>(0) : static_cast<unsigned char>(255);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loadingListener->increaseProgress();
|
loadingListener->increaseProgress();
|
||||||
@ -224,6 +231,14 @@ namespace MWRender
|
|||||||
mBaseTexture->setImage(image);
|
mBaseTexture->setImage(image);
|
||||||
mBaseTexture->setResizeNonPowerOfTwoHint(false);
|
mBaseTexture->setResizeNonPowerOfTwoHint(false);
|
||||||
|
|
||||||
|
mAlphaTexture = new osg::Texture2D;
|
||||||
|
mAlphaTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||||
|
mAlphaTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
||||||
|
mAlphaTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
||||||
|
mAlphaTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
||||||
|
mAlphaTexture->setImage(alphaImage);
|
||||||
|
mAlphaTexture->setResizeNonPowerOfTwoHint(false);
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
loadingListener->loadingOff();
|
loadingListener->loadingOff();
|
||||||
@ -299,6 +314,28 @@ namespace MWRender
|
|||||||
stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);
|
stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);
|
||||||
stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
||||||
stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
|
stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
|
||||||
|
|
||||||
|
if (mAlphaTexture)
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::Vec2Array> texcoords = new osg::Vec2Array;
|
||||||
|
|
||||||
|
float x1 = x / static_cast<float>(mWidth);
|
||||||
|
float x2 = (x + width) / static_cast<float>(mWidth);
|
||||||
|
float y1 = y / static_cast<float>(mHeight);
|
||||||
|
float y2 = (y + height) / static_cast<float>(mHeight);
|
||||||
|
texcoords->push_back(osg::Vec2f(x1, y1));
|
||||||
|
texcoords->push_back(osg::Vec2f(x1, y2));
|
||||||
|
texcoords->push_back(osg::Vec2f(x2, y2));
|
||||||
|
texcoords->push_back(osg::Vec2f(x2, y1));
|
||||||
|
geom->setTexCoordArray(1, texcoords, osg::Array::BIND_PER_VERTEX);
|
||||||
|
|
||||||
|
stateset->setTextureAttributeAndModes(1, mAlphaTexture, osg::StateAttribute::ON);
|
||||||
|
osg::ref_ptr<osg::TexEnvCombine> texEnvCombine = new osg::TexEnvCombine;
|
||||||
|
texEnvCombine->setCombine_RGB(osg::TexEnvCombine::REPLACE);
|
||||||
|
texEnvCombine->setSource0_RGB(osg::TexEnvCombine::PREVIOUS);
|
||||||
|
stateset->setTextureAttributeAndModes(1, texEnvCombine);
|
||||||
|
}
|
||||||
|
|
||||||
camera->addChild(geom);
|
camera->addChild(geom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +107,7 @@ namespace MWRender
|
|||||||
std::vector< std::pair<int,int> > mExploredCells;
|
std::vector< std::pair<int,int> > mExploredCells;
|
||||||
|
|
||||||
osg::ref_ptr<osg::Texture2D> mBaseTexture;
|
osg::ref_ptr<osg::Texture2D> mBaseTexture;
|
||||||
|
osg::ref_ptr<osg::Texture2D> mAlphaTexture;
|
||||||
|
|
||||||
// GPU copy of overlay
|
// GPU copy of overlay
|
||||||
// Note, uploads are pushed through a Camera, instead of through mOverlayImage
|
// Note, uploads are pushed through a Camera, instead of through mOverlayImage
|
||||||
|
@ -437,7 +437,9 @@ bool OpenAL_SoundStream::process()
|
|||||||
alGetSourcei(mSource, AL_SOURCE_STATE, &state);
|
alGetSourcei(mSource, AL_SOURCE_STATE, &state);
|
||||||
if(state != AL_PLAYING && state != AL_PAUSED)
|
if(state != AL_PLAYING && state != AL_PAUSED)
|
||||||
{
|
{
|
||||||
|
// Ensure all processed buffers are removed so we don't replay them.
|
||||||
refillQueue();
|
refillQueue();
|
||||||
|
|
||||||
alSourcePlay(mSource);
|
alSourcePlay(mSource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -906,7 +908,10 @@ void OpenAL_Output::finishSound(MWBase::SoundPtr sound)
|
|||||||
ALuint source = GET_PTRID(sound->mHandle);
|
ALuint source = GET_PTRID(sound->mHandle);
|
||||||
sound->mHandle = 0;
|
sound->mHandle = 0;
|
||||||
|
|
||||||
alSourceStop(source);
|
// Rewind the stream instead of stopping it, this puts the source into an AL_INITIAL state,
|
||||||
|
// which works around a bug in the MacOS OpenAL implementation which would otherwise think
|
||||||
|
// the initial queue already played when it hasn't.
|
||||||
|
alSourceRewind(source);
|
||||||
alSourcei(source, AL_BUFFER, 0);
|
alSourcei(source, AL_BUFFER, 0);
|
||||||
|
|
||||||
mFreeSources.push_back(source);
|
mFreeSources.push_back(source);
|
||||||
@ -1006,7 +1011,10 @@ void OpenAL_Output::finishStream(MWBase::SoundStreamPtr sound)
|
|||||||
sound->mHandle = 0;
|
sound->mHandle = 0;
|
||||||
mStreamThread->remove(stream);
|
mStreamThread->remove(stream);
|
||||||
|
|
||||||
alSourceStop(source);
|
// Rewind the stream instead of stopping it, this puts the source into an AL_INITIAL state,
|
||||||
|
// which works around a bug in the MacOS OpenAL implementation which would otherwise think
|
||||||
|
// the initial queue already played when it hasn't.
|
||||||
|
alSourceRewind(source);
|
||||||
alSourcei(source, AL_BUFFER, 0);
|
alSourcei(source, AL_BUFFER, 0);
|
||||||
|
|
||||||
mFreeSources.push_back(source);
|
mFreeSources.push_back(source);
|
||||||
|
@ -8,23 +8,6 @@
|
|||||||
|
|
||||||
#include "player.hpp"
|
#include "player.hpp"
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
void getFollowers (const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out)
|
|
||||||
{
|
|
||||||
std::list<MWWorld::Ptr> followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor);
|
|
||||||
for(std::list<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)
|
|
||||||
{
|
|
||||||
if (out.insert(*it).second)
|
|
||||||
{
|
|
||||||
getFollowers(*it, out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
ActionTeleport::ActionTeleport (const std::string& cellName,
|
ActionTeleport::ActionTeleport (const std::string& cellName,
|
||||||
@ -37,22 +20,13 @@ namespace MWWorld
|
|||||||
{
|
{
|
||||||
if (mTeleportFollowers)
|
if (mTeleportFollowers)
|
||||||
{
|
{
|
||||||
//find any NPC that is following the actor and teleport him too
|
// Find any NPCs that are following the actor and teleport them with him
|
||||||
std::set<MWWorld::Ptr> followers;
|
std::set<MWWorld::Ptr> followers;
|
||||||
getFollowers(actor, followers);
|
getFollowersToTeleport(actor, followers);
|
||||||
for(std::set<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)
|
|
||||||
{
|
|
||||||
MWWorld::Ptr follower = *it;
|
|
||||||
|
|
||||||
std::string script = follower.getClass().getScript(follower);
|
for (std::set<MWWorld::Ptr>::iterator it = followers.begin(); it != followers.end(); ++it)
|
||||||
if (!script.empty() && follower.getRefData().getLocals().getIntVar(script, "stayoutside") == 1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ((follower.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3()).length2()
|
|
||||||
<= 800*800)
|
|
||||||
teleport(*it);
|
teleport(*it);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
teleport(actor);
|
teleport(actor);
|
||||||
}
|
}
|
||||||
@ -82,4 +56,21 @@ namespace MWWorld
|
|||||||
world->moveObject(actor,world->getInterior(mCellName),mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]);
|
world->moveObject(actor,world->getInterior(mCellName),mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ActionTeleport::getFollowersToTeleport(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out) {
|
||||||
|
std::set<MWWorld::Ptr> followers;
|
||||||
|
MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor, followers);
|
||||||
|
|
||||||
|
for(std::set<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)
|
||||||
|
{
|
||||||
|
MWWorld::Ptr follower = *it;
|
||||||
|
|
||||||
|
std::string script = follower.getClass().getScript(follower);
|
||||||
|
if (!script.empty() && follower.getRefData().getLocals().getIntVar(script, "stayoutside") == 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((follower.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3()).length2() <= 800*800)
|
||||||
|
out.insert(follower);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#ifndef GAME_MWWORLD_ACTIONTELEPORT_H
|
#ifndef GAME_MWWORLD_ACTIONTELEPORT_H
|
||||||
#define GAME_MWWORLD_ACTIONTELEPORT_H
|
#define GAME_MWWORLD_ACTIONTELEPORT_H
|
||||||
|
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <components/esm/defs.hpp>
|
#include <components/esm/defs.hpp>
|
||||||
@ -23,9 +24,12 @@ namespace MWWorld
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ActionTeleport (const std::string& cellName, const ESM::Position& position, bool teleportFollowers);
|
/// If cellName is empty, an exterior cell is assumed.
|
||||||
///< If cellName is empty, an exterior cell is assumed.
|
|
||||||
/// @param teleportFollowers Whether to teleport any following actors of the target actor as well.
|
/// @param teleportFollowers Whether to teleport any following actors of the target actor as well.
|
||||||
|
ActionTeleport (const std::string& cellName, const ESM::Position& position, bool teleportFollowers);
|
||||||
|
|
||||||
|
/// Outputs every actor follower who is in teleport range and wasn't ordered to not enter interiors
|
||||||
|
static void getFollowersToTeleport(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +137,9 @@ namespace
|
|||||||
iter->load (state);
|
iter->load (state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::cerr << "Dropping reference to " << state.mRef.mRefID << " (invalid content file link)" << std::endl;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// new reference
|
// new reference
|
||||||
|
@ -402,8 +402,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
|
|||||||
std::pair<std::vector<int>, bool> itemsSlots =
|
std::pair<std::vector<int>, bool> itemsSlots =
|
||||||
weapon->getClass().getEquipmentSlots (*weapon);
|
weapon->getClass().getEquipmentSlots (*weapon);
|
||||||
|
|
||||||
for (std::vector<int>::const_iterator slot (itemsSlots.first.begin());
|
if (!itemsSlots.first.empty())
|
||||||
slot!=itemsSlots.first.end(); ++slot)
|
|
||||||
{
|
{
|
||||||
if (!itemsSlots.second)
|
if (!itemsSlots.second)
|
||||||
{
|
{
|
||||||
@ -413,8 +412,8 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
slots_[*slot] = weapon;
|
int slot = itemsSlots.first.front();
|
||||||
break;
|
slots_[slot] = weapon;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -280,7 +280,9 @@ namespace MWWorld
|
|||||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||||
for (size_t it = 0; it != state.mSoundIds.size(); it++)
|
for (size_t it = 0; it != state.mSoundIds.size(); it++)
|
||||||
{
|
{
|
||||||
state.mSounds.push_back(sndMgr->playSound3D(pos, state.mSoundIds.at(it), 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop));
|
MWBase::SoundPtr sound = sndMgr->playSound3D(pos, state.mSoundIds.at(it), 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop);
|
||||||
|
if (sound)
|
||||||
|
state.mSounds.push_back(sound);
|
||||||
}
|
}
|
||||||
|
|
||||||
mMagicBolts.push_back(state);
|
mMagicBolts.push_back(state);
|
||||||
@ -571,8 +573,10 @@ namespace MWWorld
|
|||||||
|
|
||||||
for (size_t soundIter = 0; soundIter != state.mSoundIds.size(); soundIter++)
|
for (size_t soundIter = 0; soundIter != state.mSoundIds.size(); soundIter++)
|
||||||
{
|
{
|
||||||
state.mSounds.push_back(sndMgr->playSound3D(esm.mPosition, state.mSoundIds.at(soundIter), 1.0f, 1.0f,
|
MWBase::SoundPtr sound = sndMgr->playSound3D(esm.mPosition, state.mSoundIds.at(soundIter), 1.0f, 1.0f,
|
||||||
MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop));
|
MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop);
|
||||||
|
if (sound)
|
||||||
|
state.mSounds.push_back(sound);
|
||||||
}
|
}
|
||||||
|
|
||||||
mMagicBolts.push_back(state);
|
mMagicBolts.push_back(state);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user