Merge branch 'openmw-29'

Conflicts:
	apps/openmw/mwmechanics/aicombat.cpp
actorid
Marc Zinnschlag 11 years ago
commit f9d2fde783

@ -7,7 +7,6 @@ branches:
- /openmw-.*$/
before_install:
- pwd
- git fetch --tags
- echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse"
- echo "yes" | sudo apt-add-repository ppa:openmw/openmw
- sudo apt-get update -qq

@ -6,34 +6,63 @@ if (APPLE)
set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}")
endif (APPLE)
# Macros
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
include(OpenMWMacros)
# Version
message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 29)
set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_VERSION_COMMITHASH "")
set(OPENMW_VERSION_TAGHASH "")
set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
if(EXISTS ${PROJECT_SOURCE_DIR}/.git)
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow)
find_package(Git)
if(GIT_FOUND)
include(GetGitRevisionDescription)
get_git_tag_revision(TAGHASH --tags --max-count=1)
get_git_head_revision(REFSPEC COMMITHASH)
git_describe(VERSION --tags ${TAGHASH})
string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}")
if(MATCH)
string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" GIT_VERSION_MAJOR "${VERSION}")
string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" GIT_VERSION_MINOR "${VERSION}")
string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" GIT_VERSION_RELEASE "${VERSION}")
set(GIT_VERSION "${GIT_VERSION_MAJOR}.${GIT_VERSION_MINOR}.${GIT_VERSION_RELEASE}")
if(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION})
message(FATAL_ERROR "Silly Zini forgot to update the version again...")
else(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION})
set(OPENMW_VERSION_MAJOR ${GIT_VERSION_MAJOR})
set(OPENMW_VERSION_MINOR ${GIT_VERSION_MINOR})
set(OPENMW_VERSION_RELEASE ${GIT_VERSION_RELEASE})
set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}")
set(OPENMW_VERSION_TAGHASH "${TAGHASH}")
endif(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION})
message(STATUS "OpenMW version ${OPENMW_VERSION}")
else(MATCH)
message(WARNING "Failed to get valid version information from Git")
endif(MATCH)
else(GIT_FOUND)
message(WARNING "Git executable not found")
endif(GIT_FOUND)
else(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow)
message(STATUS "Shallow Git clone detected, not attempting to retrieve version info")
endif(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow)
endif(EXISTS ${PROJECT_SOURCE_DIR}/.git)
include(GetGitRevisionDescription)
get_git_tag_revision(TAGHASH --tags --max-count=1 "HEAD...")
get_git_head_revision(REFSPEC COMMITHASH)
git_describe(VERSION --tags ${TAGHASH})
string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}")
if (MATCH)
string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" OPENMW_VERSION_MAJOR "${VERSION}")
string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_MINOR "${VERSION}")
string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_RELEASE "${VERSION}")
set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}")
set(OPENMW_VERSION_TAGHASH "${TAGHASH}")
message(STATUS "Configuring OpenMW ${OPENMW_VERSION}...")
else (MATCH)
message(FATAL_ERROR "Failed to get valid version information from Git")
endif (MATCH)
# Macros
include(OpenMWMacros)
# doxygen main page

@ -76,17 +76,20 @@ Launcher::MainDialog::MainDialog(QWidget *parent)
QString revision(OPENMW_VERSION_COMMITHASH);
QString tag(OPENMW_VERSION_TAGHASH);
if (revision == tag) {
versionLabel->setText(tr("OpenMW %0 release").arg(OPENMW_VERSION));
} else {
versionLabel->setText(tr("OpenMW development (%0)").arg(revision.left(10)));
}
if (!revision.isEmpty() && !tag.isEmpty())
{
if (revision == tag) {
versionLabel->setText(tr("OpenMW %0 release").arg(OPENMW_VERSION));
} else {
versionLabel->setText(tr("OpenMW development (%0)").arg(revision.left(10)));
}
// Add the compile date and time
versionLabel->setToolTip(tr("Compiled on %0 %1").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(),
QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate),
QLocale(QLocale::C).toTime(QString(__TIME__).simplified(),
QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate)));
// Add the compile date and time
versionLabel->setToolTip(tr("Compiled on %0 %1").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(),
QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate),
QLocale(QLocale::C).toTime(QString(__TIME__).simplified(),
QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate)));
}
createIcons();
}

@ -456,7 +456,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
else
{
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
pos.rot[0] = pos.rot[1] = pos.pos[2] = 0;
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
world->changeToExteriorCell (pos);
}

@ -206,9 +206,9 @@ struct JournalViewModelImpl : JournalViewModel
const MWDialogue::Quest& quest = i->second;
// Unfortunately Morrowind.esm has no quest names, since the quest book was added with tribunal.
if (quest.getName().empty())
visitor (reinterpret_cast <QuestId> (&i->second), toUtf8Span (i->first));
else
// Note that even with Tribunal, some quests still don't have quest names. I'm assuming those are not supposed
// to appear in the quest book.
if (!quest.getName().empty())
visitor (reinterpret_cast <QuestId> (&i->second), toUtf8Span (quest.getName()));
}
}

@ -455,7 +455,7 @@ namespace MWInput
mInputBinder->adjustMouseRegion(width, height);
}
bool InputManager::keyPressed( const SDL_KeyboardEvent &arg )
void InputManager::keyPressed( const SDL_KeyboardEvent &arg )
{
// Cut, copy & paste
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
@ -501,7 +501,6 @@ namespace MWInput
if (kc != OIS::KC_UNASSIGNED)
MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(kc), 0);
return true;
}
void InputManager::textInput(const SDL_TextInputEvent &arg)
@ -512,23 +511,21 @@ namespace MWInput
MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::None, *it);
}
bool InputManager::keyReleased(const SDL_KeyboardEvent &arg )
void InputManager::keyReleased(const SDL_KeyboardEvent &arg )
{
mInputBinder->keyReleased (arg);
OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym);
MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc));
return true;
}
bool InputManager::mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id )
void InputManager::mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id )
{
mInputBinder->mousePressed (arg, id);
if (id != SDL_BUTTON_LEFT && id != SDL_BUTTON_RIGHT)
return true; // MyGUI has no use for these events
return; // MyGUI has no use for these events
MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, sdlButtonToMyGUI(id));
if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0)
@ -539,20 +536,16 @@ namespace MWInput
MWBase::Environment::get().getSoundManager ()->playSound ("Menu Click", 1.f, 1.f);
}
}
return true;
}
bool InputManager::mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id )
void InputManager::mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id )
{
mInputBinder->mouseReleased (arg, id);
MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, sdlButtonToMyGUI(id));
return true;
}
bool InputManager::mouseMoved(const SFO::MouseMotionEvent &arg )
void InputManager::mouseMoved(const SFO::MouseMotionEvent &arg )
{
mInputBinder->mouseMoved (arg);
@ -600,8 +593,6 @@ namespace MWInput
MWBase::Environment::get().getWorld()->setCameraDistance(arg.zrel, true, true);
}
}
return true;
}
void InputManager::windowFocusChange(bool have_focus)

@ -86,13 +86,13 @@ namespace MWInput
virtual void resetToDefaultBindings();
public:
virtual bool keyPressed(const SDL_KeyboardEvent &arg );
virtual bool keyReleased( const SDL_KeyboardEvent &arg );
virtual void keyPressed(const SDL_KeyboardEvent &arg );
virtual void keyReleased( const SDL_KeyboardEvent &arg );
virtual void textInput (const SDL_TextInputEvent &arg);
virtual bool mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id );
virtual bool mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id );
virtual bool mouseMoved( const SFO::MouseMotionEvent &arg );
virtual void mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id );
virtual void mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id );
virtual void mouseMoved( const SFO::MouseMotionEvent &arg );
virtual void windowVisibilityChange( bool visible );
virtual void windowFocusChange( bool have_focus );

@ -15,7 +15,7 @@
#include "../mwbase/dialoguemanager.hpp"
#include "npcstats.hpp"
#include "creaturestats.hpp"
#include "steering.hpp"
#include "movement.hpp"
#include "character.hpp" // fixme: for getActiveWeapon
@ -140,11 +140,12 @@ namespace MWMechanics
{
MWMechanics::DrawState_ state = actor.getClass().getCreatureStats(actor).getDrawState();
if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing)
actor.getClass().getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon);
actor.getClass().getCreatureStats(actor).setDrawState(MWMechanics::DrawState_Weapon);
//Get weapon speed and range
MWWorld::ContainerStoreIterator weaponSlot =
MWMechanics::getActiveWeapon(cls.getNpcStats(actor), cls.getInventoryStore(actor), &weaptype);
MWMechanics::getActiveWeapon(cls.getCreatureStats(actor), cls.getInventoryStore(actor), &weaptype);
if (weaptype == WeapType_HandToHand)
{
const MWWorld::Store<ESM::GameSetting> &gmst =
@ -242,17 +243,21 @@ namespace MWMechanics
//target is at far distance: build path to target OR follow target (if previously actor had reached it once)
mFollowTarget = false;
buildNewPath(actor);
buildNewPath(actor); //may fail to build a path, check before use
//delete visited path node
mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]);
//try shortcut
if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget))
mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees();
else
mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
mRotate = true;
//if no new path leave mTargetAngle unchanged
if(!mPathFinder.getPath().empty())
{
//try shortcut
if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget))
mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees();
else
mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
mRotate = true;
}
mMovement.mPosition[1] = 1;
mReadyToAttack = false;
@ -302,9 +307,13 @@ namespace MWMechanics
dest.mZ = mTarget.getRefData().getPosition().pos[2];
Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ);
ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back();
Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ);
float dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length());
float dist = -1; //hack to indicate first time, to construct a new path
if(!mPathFinder.getPath().empty())
{
ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back();
Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ);
dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length());
}
float targetPosThreshold;
bool isOutside = actor.getCell()->getCell()->isExterior();
@ -313,7 +322,7 @@ namespace MWMechanics
else
targetPosThreshold = 100;
if(dist > targetPosThreshold)
if((dist < 0) || (dist > targetPosThreshold))
{
//construct new path only if target has moved away more than on <targetPosThreshold>
ESM::Position pos = actor.getRefData().getPosition();
@ -334,8 +343,11 @@ namespace MWMechanics
//maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path,
//not the actual path length. Here we should know if the new path is actually more effective.
//if(pathFinder2.getPathSize() < mPathFinder.getPathSize())
newPathFinder.syncStart(mPathFinder.getPath());
mPathFinder = newPathFinder;
if(!mPathFinder.getPath().empty())
{
newPathFinder.syncStart(mPathFinder.getPath());
mPathFinder = newPathFinder;
}
}
}
}

@ -393,6 +393,8 @@ namespace MWMechanics
void PathFinder::syncStart(const std::list<ESM::Pathgrid::Point> &path)
{
if (mPath.size() < 2)
return; //nothing to pop
std::list<ESM::Pathgrid::Point>::const_iterator oldStart = path.begin();
std::list<ESM::Pathgrid::Point>::iterator iter = ++mPath.begin();

@ -293,6 +293,17 @@ void Animation::addAnimSource(const std::string &model)
}
}
if (grp == 0 && dstval->getNode()->getName() == "Bip01")
{
mNonAccumRoot = dstval->getNode();
mAccumRoot = mNonAccumRoot->getParent();
if(!mAccumRoot)
{
std::cerr<< "Non-Accum root for "<<mPtr.getCellRef().mRefID<<" is skeleton root??" <<std::endl;
mNonAccumRoot = NULL;
}
}
ctrls[i].setSource(mAnimationTimePtr[grp]);
grpctrls[grp].push_back(ctrls[i]);
}

@ -981,13 +981,11 @@ void RenderingManager::screenshot(Image &image, int w, int h)
Ogre::PixelFormat pf = rt->suggestPixelFormat();
std::vector<Ogre::uchar> data;
data.resize(w * h * Ogre::PixelUtil::getNumElemBytes(pf));
Ogre::PixelBox pb(w, h, 1, pf, &data[0]);
rt->copyContentsToMemory(pb);
image.loadDynamicImage(&data[0], w, h, pf);
image.loadDynamicImage(
OGRE_ALLOC_T(Ogre::uchar, w * h * Ogre::PixelUtil::getNumElemBytes(pf), Ogre::MEMCATEGORY_GENERAL),
w, h, 1, pf, true // autoDelete=true, frees memory we allocate
);
rt->copyContentsToMemory(image.getPixelBox()); // getPixelBox returns a box sharing the same memory as the image
Ogre::TextureManager::getSingleton().remove(tempName);
mRendering.getCamera()->setAspectRatio(oldAspect);

@ -16,9 +16,9 @@
namespace MWRender
{
Ogre::AxisAlignedBox TerrainStorage::getBounds()
void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY)
{
int minX = 0, minY = 0, maxX = 0, maxY = 0;
minX = 0, minY = 0, maxX = 0, maxY = 0;
const MWWorld::ESMStore &esmStore =
MWBase::Environment::get().getWorld()->getStore();
@ -39,8 +39,6 @@ namespace MWRender
// since grid coords are at cell origin, we need to add 1 cell
maxX += 1;
maxY += 1;
return Ogre::AxisAlignedBox(minX, minY, 0, maxX, maxY, 0);
}
ESM::Land* TerrainStorage::getLand(int cellX, int cellY)

@ -1,6 +1,9 @@
#ifndef MWRENDER_TERRAINSTORAGE_H
#define MWRENDER_TERRAINSTORAGE_H
#include <components/esm/loadland.hpp>
#include <components/esm/loadltex.hpp>
#include <components/terrain/storage.hpp>
namespace MWRender
@ -14,7 +17,7 @@ namespace MWRender
public:
/// Get bounds of the whole terrain in cell units
virtual Ogre::AxisAlignedBox getBounds();
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY);
/// Get the minimum and maximum heights of a terrain chunk.
/// @note Should only be called for chunks <= 1 cell, i.e. leafs of the quad tree.

@ -172,6 +172,7 @@ class OpenAL_SoundStream : public Sound
DecoderPtr mDecoder;
volatile bool mIsFinished;
volatile bool mIsInitialBatchEnqueued;
void updateAll(bool local);
@ -264,7 +265,7 @@ private:
OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder, float basevol, float pitch, int flags)
: Sound(Ogre::Vector3(0.0f), 1.0f, basevol, pitch, 1.0f, 1000.0f, flags)
, mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true)
, mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true), mIsInitialBatchEnqueued(false)
{
throwALerror();
@ -315,26 +316,8 @@ void OpenAL_SoundStream::play()
alSourcei(mSource, AL_BUFFER, 0);
throwALerror();
mSamplesQueued = 0;
std::vector<char> data(mBufferSize);
bool finished = false;
for(ALuint i = 0;i < sNumBuffers && !finished;i++)
{
size_t got = mDecoder->read(&data[0], data.size());
finished = (got < data.size());
if(got > 0)
{
ALuint bufid = mBuffers[i];
alBufferData(bufid, mFormat, &data[0], got, mSampleRate);
alSourceQueueBuffers(mSource, 1, &bufid);
throwALerror();
mSamplesQueued += getBufferSampleCount(bufid);
}
}
mIsFinished = finished;
alSourcePlay(mSource);
mIsFinished = false;
mIsInitialBatchEnqueued = false;
mOutput.mStreamThread->add(this);
}
@ -342,6 +325,7 @@ void OpenAL_SoundStream::stop()
{
mOutput.mStreamThread->remove(this);
mIsFinished = true;
mIsInitialBatchEnqueued = false;
alSourceStop(mSource);
alSourcei(mSource, AL_BUFFER, 0);
@ -454,6 +438,24 @@ bool OpenAL_SoundStream::process()
} while(processed > 0);
throwALerror();
}
else if (!mIsInitialBatchEnqueued) { // nothing enqueued yet
std::vector<char> data(mBufferSize);
for(ALuint i = 0;i < sNumBuffers && !finished;i++)
{
size_t got = mDecoder->read(&data[0], data.size());
finished = (got < data.size());
if(got > 0)
{
ALuint bufid = mBuffers[i];
alBufferData(bufid, mFormat, &data[0], got, mSampleRate);
alSourceQueueBuffers(mSource, 1, &bufid);
throwALerror();
mSamplesQueued += getBufferSampleCount(bufid);
}
}
mIsInitialBatchEnqueued = true;
}
if(state != AL_PLAYING && state != AL_PAUSED)
{
@ -471,6 +473,7 @@ bool OpenAL_SoundStream::process()
std::cout<< "Error updating stream \""<<mDecoder->getName()<<"\"" <<std::endl;
mSamplesQueued = 0;
mIsFinished = true;
mIsInitialBatchEnqueued = false;
}
return !mIsFinished;
}

@ -177,7 +177,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
else
slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile);
std::ofstream stream (slot->mPath.string().c_str());
std::ofstream stream (slot->mPath.string().c_str(), std::ios::binary);
ESM::ESMWriter writer;

@ -1,177 +0,0 @@
# Locate SDL library
# This module defines
# SDL_LIBRARY, the name of the library to link against
# SDL_FOUND, if false, do not try to link to SDL
# SDL_INCLUDE_DIR, where to find SDL.h
#
# This module responds to the the flag:
# SDL_BUILDING_LIBRARY
# If this is defined, then no SDL_main will be linked in because
# only applications need main().
# Otherwise, it is assumed you are building an application and this
# module will attempt to locate and set the the proper link flags
# as part of the returned SDL_LIBRARY variable.
#
# Don't forget to include SDLmain.h and SDLmain.m your project for the
# OS X framework based version. (Other versions link to -lSDLmain which
# this module will try to find on your behalf.) Also for OS X, this
# module will automatically add the -framework Cocoa on your behalf.
#
#
# Additional Note: If you see an empty SDL_LIBRARY_TEMP in your configuration
# and no SDL_LIBRARY, it means CMake did not find your SDL library
# (SDL.dll, libsdl.so, SDL.framework, etc).
# Set SDL_LIBRARY_TEMP to point to your SDL library, and configure again.
# Similarly, if you see an empty SDLMAIN_LIBRARY, you should set this value
# as appropriate. These values are used to generate the final SDL_LIBRARY
# variable, but when these values are unset, SDL_LIBRARY does not get created.
#
#
# $SDLDIR is an environment variable that would
# correspond to the ./configure --prefix=$SDLDIR
# used in building SDL.
# l.e.galup 9-20-02
#
# Modified by Eric Wing.
# Added code to assist with automated building by using environmental variables
# and providing a more controlled/consistent search behavior.
# Added new modifications to recognize OS X frameworks and
# additional Unix paths (FreeBSD, etc).
# Also corrected the header search path to follow "proper" SDL guidelines.
# Added a search for SDLmain which is needed by some platforms.
# Added a search for threads which is needed by some platforms.
# Added needed compile switches for MinGW.
#
# On OSX, this will prefer the Framework version (if found) over others.
# People will have to manually change the cache values of
# SDL_LIBRARY to override this selection or set the CMake environment
# CMAKE_INCLUDE_PATH to modify the search paths.
#
# Note that the header path has changed from SDL/SDL.h to just SDL.h
# This needed to change because "proper" SDL convention
# is #include "SDL.h", not <SDL/SDL.h>. This is done for portability
# reasons because not all systems place things in SDL/ (see FreeBSD).
#=============================================================================
# Copyright 2003-2009 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
FIND_PATH(SDL_INCLUDE_DIR SDL.h
HINTS
$ENV{SDLDIR}
PATH_SUFFIXES include/SDL include
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local/include/SDL12
/usr/local/include/SDL11 # FreeBSD ports
/usr/include/SDL12
/usr/include/SDL11
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
)
#MESSAGE("SDL_INCLUDE_DIR is ${SDL_INCLUDE_DIR}")
# SDL-1.1 is the name used by FreeBSD ports...
# don't confuse it for the version number.
FIND_LIBRARY(SDL_LIBRARY_TEMP
NAMES SDL SDL-1.1
HINTS
$ENV{SDLDIR}
PATH_SUFFIXES lib64 lib
PATHS
/sw
/opt/local
/opt/csw
/opt
)
#MESSAGE("SDL_LIBRARY_TEMP is ${SDL_LIBRARY_TEMP}")
IF(NOT SDL_BUILDING_LIBRARY)
IF(NOT ${SDL_INCLUDE_DIR} MATCHES ".framework")
# Non-OS X framework versions expect you to also dynamically link to
# SDLmain. This is mainly for Windows and OS X. Other (Unix) platforms
# seem to provide SDLmain for compatibility even though they don't
# necessarily need it.
FIND_LIBRARY(SDLMAIN_LIBRARY
NAMES SDLmain SDLmain-1.1
HINTS
$ENV{SDLDIR}
PATH_SUFFIXES lib64 lib
PATHS
/sw
/opt/local
/opt/csw
/opt
)
ENDIF(NOT ${SDL_INCLUDE_DIR} MATCHES ".framework")
ENDIF(NOT SDL_BUILDING_LIBRARY)
# SDL may require threads on your system.
# The Apple build may not need an explicit flag because one of the
# frameworks may already provide it.
# But for non-OSX systems, I will use the CMake Threads package.
IF(NOT APPLE)
FIND_PACKAGE(Threads)
ENDIF(NOT APPLE)
# MinGW needs an additional library, mwindows
# It's total link flags should look like -lmingw32 -lSDLmain -lSDL -lmwindows
# (Actually on second look, I think it only needs one of the m* libraries.)
IF(MINGW)
SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW")
ENDIF(MINGW)
SET(SDL_FOUND "NO")
IF(SDL_LIBRARY_TEMP)
# For SDLmain
IF(NOT SDL_BUILDING_LIBRARY)
IF(SDLMAIN_LIBRARY)
SET(SDL_LIBRARY_TEMP ${SDLMAIN_LIBRARY} ${SDL_LIBRARY_TEMP})
ENDIF(SDLMAIN_LIBRARY)
ENDIF(NOT SDL_BUILDING_LIBRARY)
# For OS X, SDL uses Cocoa as a backend so it must link to Cocoa.
# CMake doesn't display the -framework Cocoa string in the UI even
# though it actually is there if I modify a pre-used variable.
# I think it has something to do with the CACHE STRING.
# So I use a temporary variable until the end so I can set the
# "real" variable in one-shot.
IF(APPLE)
SET(SDL_LIBRARY_TEMP ${SDL_LIBRARY_TEMP} "-framework Cocoa")
ENDIF(APPLE)
# For threads, as mentioned Apple doesn't need this.
# In fact, there seems to be a problem if I used the Threads package
# and try using this line, so I'm just skipping it entirely for OS X.
IF(NOT APPLE)
SET(SDL_LIBRARY_TEMP ${SDL_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT})
ENDIF(NOT APPLE)
# For MinGW library
IF(MINGW)
SET(SDL_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL_LIBRARY_TEMP})
ENDIF(MINGW)
# Set the final string here so the GUI reflects the final state.
SET(SDL_LIBRARY ${SDL_LIBRARY_TEMP} CACHE STRING "Where the SDL Library can be found")
# Set the temp variable to INTERNAL so it is not seen in the CMake GUI
SET(SDL_LIBRARY_TEMP "${SDL_LIBRARY_TEMP}" CACHE INTERNAL "")
SET(SDL_FOUND "YES")
ENDIF(SDL_LIBRARY_TEMP)
#MESSAGE("SDL_LIBRARY is ${SDL_LIBRARY}")

@ -85,10 +85,6 @@ function(get_git_head_revision _refspecvar _hashvar)
endfunction()
function(git_describe _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
#get_git_head_revision(refspec hash)
if(NOT GIT_FOUND)
@ -133,7 +129,8 @@ endfunction()
function(get_git_tag_revision _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
return()
endif()
execute_process(COMMAND

@ -80,8 +80,8 @@ namespace ESM
rec.name = name;
rec.position = mStream->tellp();
rec.size = 0;
writeT<int>(0); // Size goes here
writeT<int>(0); // Unused header?
writeT<uint32_t>(0); // Size goes here
writeT<uint32_t>(0); // Unused header?
writeT(flags);
mRecords.push_back(rec);
@ -105,7 +105,7 @@ namespace ESM
rec.name = name;
rec.position = mStream->tellp();
rec.size = 0;
writeT<int>(0); // Size goes here
writeT<uint32_t>(0); // Size goes here
mRecords.push_back(rec);
assert(mRecords.back().size == 0);
@ -120,7 +120,7 @@ namespace ESM
mStream->seekp(rec.position);
mCounting = false;
write (reinterpret_cast<const char*> (&rec.size), sizeof(int));
write (reinterpret_cast<const char*> (&rec.size), sizeof(uint32_t));
mCounting = true;
mStream->seekp(0, std::ios::end);

@ -17,7 +17,7 @@ class ESMWriter
{
std::string name;
std::streampos position;
size_t size;
uint32_t size;
};
public:

@ -219,7 +219,7 @@ LowLevelFile::LowLevelFile ()
LowLevelFile::~LowLevelFile ()
{
if (mHandle == INVALID_HANDLE_VALUE)
if (mHandle != INVALID_HANDLE_VALUE)
CloseHandle (mHandle);
}

@ -1,9 +1,6 @@
#ifndef COMPONENTS_TERRAIN_STORAGE_H
#define COMPONENTS_TERRAIN_STORAGE_H
#include <components/esm/loadland.hpp>
#include <components/esm/loadltex.hpp>
#include <OgreAxisAlignedBox.h>
#include <OgreHardwareVertexBuffer.h>
@ -27,7 +24,7 @@ namespace Terrain
public:
/// Get bounds of the whole terrain in cell units
virtual Ogre::AxisAlignedBox getBounds() = 0;
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) = 0;
/// Get the minimum and maximum heights of a terrain chunk.
/// @note Should only be called for chunks <= 1 cell, i.e. leafs of the quad tree.

@ -62,6 +62,10 @@ namespace Terrain
, mShaders(shaders)
, mVisible(true)
, mLoadingListener(loadingListener)
, mMaxX(0)
, mMinX(0)
, mMaxY(0)
, mMinY(0)
{
loadingListener->setLabel("Creating terrain");
loadingListener->indicateProgress();
@ -76,20 +80,21 @@ namespace Terrain
mCompositeMapRenderTarget->setAutoUpdated(false);
mCompositeMapRenderTarget->addViewport(compositeMapCam);
mBounds = storage->getBounds();
storage->getBounds(mMinX, mMaxX, mMinY, mMaxY);
int origSizeX = mBounds.getSize().x;
int origSizeY = mBounds.getSize().y;
int origSizeX = mMaxX-mMinX;
int origSizeY = mMaxY-mMinY;
// Dividing a quad tree only works well for powers of two, so round up to the nearest one
int size = nextPowerOfTwo(std::max(origSizeX, origSizeY));
// Adjust the center according to the new size
Ogre::Vector3 center = mBounds.getCenter() + Ogre::Vector3((size-origSizeX)/2.f, (size-origSizeY)/2.f, 0);
float centerX = (mMinX+mMaxX)/2.f + (size-origSizeX)/2.f;
float centerY = (mMinY+mMaxY)/2.f + (size-origSizeY)/2.f;
mRootSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(center.x, center.y), NULL);
mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(centerX, centerY), NULL);
buildQuadTree(mRootNode);
loadingListener->indicateProgress();
mRootNode->initAabb();
@ -122,10 +127,10 @@ namespace Terrain
return;
}
if (node->getCenter().x - halfSize > mBounds.getMaximum().x
|| node->getCenter().x + halfSize < mBounds.getMinimum().x
|| node->getCenter().y - halfSize > mBounds.getMaximum().y
|| node->getCenter().y + halfSize < mBounds.getMinimum().y )
if (node->getCenter().x - halfSize > mMaxX
|| node->getCenter().x + halfSize < mMinX
|| node->getCenter().y - halfSize > mMaxY
|| node->getCenter().y + halfSize < mMinY )
// Out of bounds of the actual terrain - this will happen because
// we rounded the size up to the next power of two
{
@ -162,10 +167,10 @@ namespace Terrain
Ogre::AxisAlignedBox World::getWorldBoundingBox (const Ogre::Vector2& center)
{
if (center.x > mBounds.getMaximum().x
|| center.x < mBounds.getMinimum().x
|| center.y > mBounds.getMaximum().y
|| center.y < mBounds.getMinimum().y)
if (center.x > mMaxX
|| center.x < mMinX
|| center.y > mMaxY
|| center.y < mMinY)
return Ogre::AxisAlignedBox::BOX_NULL;
QuadTreeNode* node = findNode(center, mRootNode);
Ogre::AxisAlignedBox box = node->getBoundingBox();

@ -105,7 +105,7 @@ namespace Terrain
Ogre::SceneManager* mCompositeMapSceneMgr;
/// Bounds in cell units
Ogre::AxisAlignedBox mBounds;
float mMinX, mMaxX, mMinY, mMaxY;
/// Minimum size of a terrain batch along one side (in cell units)
float mMinBatchSize;

@ -20,6 +20,7 @@ Artem Kotsynyak (greye)
athile
Britt Mathis (galdor557)
BrotherBrick
cc9cii
Chris Robinson (KittyCat)
Cory F. Cohen (cfcohen)
Cris Mihalache (Mirceam)

@ -102,19 +102,19 @@ namespace ICS
JoystickIDList& getJoystickIdList(){ return mJoystickIDList; };
// MouseListener
bool mouseMoved(const SFO::MouseMotionEvent &evt);
bool mousePressed(const SDL_MouseButtonEvent &evt, Uint8);
bool mouseReleased(const SDL_MouseButtonEvent &evt, Uint8);
void mouseMoved(const SFO::MouseMotionEvent &evt);
void mousePressed(const SDL_MouseButtonEvent &evt, Uint8);
void mouseReleased(const SDL_MouseButtonEvent &evt, Uint8);
// KeyListener
bool keyPressed(const SDL_KeyboardEvent &evt);
bool keyReleased(const SDL_KeyboardEvent &evt);
void keyPressed(const SDL_KeyboardEvent &evt);
void keyReleased(const SDL_KeyboardEvent &evt);
// JoyStickListener
bool buttonPressed(const SDL_JoyButtonEvent &evt, int button);
bool buttonReleased(const SDL_JoyButtonEvent &evt, int button);
bool axisMoved(const SDL_JoyAxisEvent &evt, int axis);
bool povMoved(const SDL_JoyHatEvent &evt, int index);
void buttonPressed(const SDL_JoyButtonEvent &evt, int button);
void buttonReleased(const SDL_JoyButtonEvent &evt, int button);
void axisMoved(const SDL_JoyAxisEvent &evt, int axis);
void povMoved(const SDL_JoyHatEvent &evt, int index);
//TODO: does this have an SDL equivalent?
//bool sliderMoved(const OIS::JoyStickEvent &evt, int index);

@ -318,7 +318,7 @@ namespace ICS
}
// joyStick listeners
bool InputControlSystem::buttonPressed(const SDL_JoyButtonEvent &evt, int button)
void InputControlSystem::buttonPressed(const SDL_JoyButtonEvent &evt, int button)
{
if(mActive)
{
@ -354,11 +354,9 @@ namespace ICS
mDetectingBindingControl, evt.which, button, mDetectingBindingDirection);
}
}
return true;
}
bool InputControlSystem::buttonReleased(const SDL_JoyButtonEvent &evt, int button)
void InputControlSystem::buttonReleased(const SDL_JoyButtonEvent &evt, int button)
{
if(mActive)
{
@ -371,10 +369,9 @@ namespace ICS
}
}
}
return true;
}
bool InputControlSystem::axisMoved(const SDL_JoyAxisEvent &evt, int axis)
void InputControlSystem::axisMoved(const SDL_JoyAxisEvent &evt, int axis)
{
if(mActive)
{
@ -417,12 +414,10 @@ namespace ICS
}
}
}
return true;
}
//Here be dragons, apparently
bool InputControlSystem::povMoved(const SDL_JoyHatEvent &evt, int index)
void InputControlSystem::povMoved(const SDL_JoyHatEvent &evt, int index)
{
if(mActive)
{
@ -542,13 +537,11 @@ namespace ICS
}
}
}
return true;
}
//TODO: does this have an SDL equivalent?
/*
bool InputControlSystem::sliderMoved(const OIS::JoyStickEvent &evt, int index)
void InputControlSystem::sliderMoved(const OIS::JoyStickEvent &evt, int index)
{
if(mActive)
{
@ -590,8 +583,6 @@ namespace ICS
}
}
}
return true;
}
*/

@ -85,7 +85,7 @@ namespace ICS
return SDLK_UNKNOWN;
}
bool InputControlSystem::keyPressed(const SDL_KeyboardEvent &evt)
void InputControlSystem::keyPressed(const SDL_KeyboardEvent &evt)
{
if(mActive)
{
@ -118,11 +118,9 @@ namespace ICS
mDetectingBindingControl, evt.keysym.sym, mDetectingBindingDirection);
}
}
}
return true;
}
bool InputControlSystem::keyReleased(const SDL_KeyboardEvent &evt)
void InputControlSystem::keyReleased(const SDL_KeyboardEvent &evt)
{
if(mActive)
{
@ -132,8 +130,6 @@ namespace ICS
it->second.control->setChangingDirection(Control::STOP);
}
}
return true;
}
void DetectingBindingListener::keyBindingDetected(InputControlSystem* ICS, Control* control

@ -219,7 +219,7 @@ namespace ICS
}
// mouse Listeners
bool InputControlSystem::mouseMoved(const SFO::MouseMotionEvent& evt)
void InputControlSystem::mouseMoved(const SFO::MouseMotionEvent& evt)
{
if(mActive)
{
@ -304,11 +304,9 @@ namespace ICS
}
}
}
return true;
}
bool InputControlSystem::mousePressed(const SDL_MouseButtonEvent &evt, Uint8 btn)
void InputControlSystem::mousePressed(const SDL_MouseButtonEvent &evt, Uint8 btn)
{
if(mActive)
{
@ -341,11 +339,9 @@ namespace ICS
mDetectingBindingControl, btn, mDetectingBindingDirection);
}
}
return true;
}
bool InputControlSystem::mouseReleased(const SDL_MouseButtonEvent &evt, Uint8 btn)
void InputControlSystem::mouseReleased(const SDL_MouseButtonEvent &evt, Uint8 btn)
{
if(mActive)
{
@ -355,8 +351,6 @@ namespace ICS
it->second.control->setChangingDirection(Control::STOP);
}
}
return true;
}
// mouse auto bindings

@ -26,9 +26,9 @@ class MouseListener
{
public:
virtual ~MouseListener() {}
virtual bool mouseMoved( const MouseMotionEvent &arg ) = 0;
virtual bool mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0;
virtual bool mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0;
virtual void mouseMoved( const MouseMotionEvent &arg ) = 0;
virtual void mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0;
virtual void mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0;
};
class KeyListener
@ -36,8 +36,8 @@ class KeyListener
public:
virtual ~KeyListener() {}
virtual void textInput (const SDL_TextInputEvent& arg) {}
virtual bool keyPressed(const SDL_KeyboardEvent &arg) = 0;
virtual bool keyReleased(const SDL_KeyboardEvent &arg) = 0;
virtual void keyPressed(const SDL_KeyboardEvent &arg) = 0;
virtual void keyReleased(const SDL_KeyboardEvent &arg) = 0;
};
class JoyListener
@ -45,18 +45,18 @@ class JoyListener
public:
virtual ~JoyListener() {}
/** @remarks Joystick button down event */
virtual bool buttonPressed( const SDL_JoyButtonEvent &evt, int button ) = 0;
virtual void buttonPressed( const SDL_JoyButtonEvent &evt, int button ) = 0;
/** @remarks Joystick button up event */
virtual bool buttonReleased( const SDL_JoyButtonEvent &evt, int button ) = 0;
virtual void buttonReleased( const SDL_JoyButtonEvent &evt, int button ) = 0;
/** @remarks Joystick axis moved event */
virtual bool axisMoved( const SDL_JoyAxisEvent &arg, int axis ) = 0;
virtual void axisMoved( const SDL_JoyAxisEvent &arg, int axis ) = 0;
//-- Not so common control events, so are not required --//
//! Joystick Event, and povID
virtual bool povMoved( const SDL_JoyHatEvent &arg, int index) {return true;}
virtual void povMoved( const SDL_JoyHatEvent &arg, int index) {}
};
class WindowListener

@ -23,7 +23,7 @@ namespace Physic
mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation);
mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation, true);
Ogre::Quaternion inverse = mBoxRotation.Inverse();
mBoxRotationInverse = btQuaternion(inverse.x, inverse.y, inverse.z,inverse.w);
mBoxRotationInverse = Ogre::Quaternion(inverse.w, inverse.x, inverse.y,inverse.z);
mEngine->addRigidBody(mBody, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map
}
@ -85,8 +85,8 @@ namespace Physic
Ogre::Quaternion PhysicActor::getRotation()
{
assert(mBody);
btQuaternion quat = mBody->getWorldTransform().getRotation() * mBoxRotationInverse;
return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ());
btQuaternion quat = mBody->getWorldTransform().getRotation();
return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ()) * mBoxRotationInverse;
}
void PhysicActor::setScale(float scale){

@ -162,7 +162,7 @@ namespace Physic
Ogre::Vector3 mBoxScaledTranslation;
Ogre::Quaternion mBoxRotation;
btQuaternion mBoxRotationInverse;
Ogre::Quaternion mBoxRotationInverse;
Ogre::Vector3 mForce;
bool mOnGround;

@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind
OpenMW is an attempt at recreating the engine for the popular role-playing game
Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work.
Version: 0.28.0
Version: 0.29.0
License: GPL (see GPL3.txt for more information)
Website: http://www.openmw.org
@ -97,6 +97,74 @@ Allowed options:
CHANGELOG
0.29.0
Bug #556: Video soundtrack not played when music volume is set to zero
Bug #829: OpenMW uses up all available vram, when playing for extended time
Bug #848: Wrong amount of footsteps playing in 1st person
Bug #888: Ascended Sleepers have movement issues
Bug #892: Explicit references are allowed on all script functions
Bug #999: Graphic Herbalism (mod): sometimes doesn't activate properly
Bug #1009: Lake Fjalding AI related slowdown.
Bug #1041: Music playback issues on OS X >= 10.9
Bug #1043: No message box when advancing skill "Speechcraft" while in dialog window
Bug #1060: Some message boxes are cut off at the bottom
Bug #1062: Bittercup script does not work ('end' variable)
Bug #1074: Inventory paperdoll obscures armour rating
Bug #1077: Message after killing an essential NPC disappears too fast
Bug #1078: "Clutterbane" shows empty charge bar
Bug #1083: UndoWerewolf fails
Bug #1088: Better Clothes Bloodmoon Plus 1.5 by Spirited Treasure pants are not rendered
Bug #1090: Start scripts fail when going to a non-predefined cell
Bug #1091: Crash: Assertion `!q.isNaN() && "Invalid orientation supplied as parameter"' failed.
Bug #1093: Weapons of aggressive NPCs are invisible after you exit and re-enter interior
Bug #1105: Magicka is depleted when using uncastable spells
Bug #1106: Creatures should be able to run
Bug #1107: TR cliffs have way too huge collision boxes in OpenMW
Bug #1109: Cleaning True Light and Darkness with Tes3cmd makes Addamasartus , Zenarbael and Yasamsi flooded.
Bug #1114: Bad output for desktop-file-validate on openmw.desktop (and opencs.desktop)
Bug #1115: Memory leak when spying on Fargoth
Bug #1137: Script execution fails (drenSlaveOwners script)
Bug #1143: Mehra Milo quest (vivec informants) is broken
Bug #1145: Issues with moving gold between inventory and containers
Bug #1146: Issues with picking up stacks of gold
Bug #1147: Dwemer Crossbows are held incorrectly
Bug #1158: Armor rating should always stay below inventory mannequin
Bug #1159: Quick keys can be set during character generation
Bug #1160: Crash on equip lockpick when
Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file
Bug #1184: Game Save: overwriting an existing save does not actually overwrites the file
Feature #30: Loading/Saving (still missing a few parts)
Feature #101: AI Package: Activate
Feature #103: AI Package: Follow, FollowCell
Feature #138: Editor: Drag & Drop
Feature #428: Player death
Feature #505: Editor: Record Cloning
Feature #701: Levelled creatures
Feature #708: Improved Local Variable handling
Feature #709: Editor: Script verifier
Feature #764: Missing journal backend features
Feature #777: Creature weapons/shields
Feature #789: Editor: Referenceable record verifier
Feature #924: Load/Save GUI (still missing loading screen and progress bars)
Feature #946: Knockdown
Feature #947: Decrease fatigue when running, swimming and attacking
Feature #956: Melee Combat: Blocking
Feature #957: Area magic
Feature #960: Combat/AI combat for creatures
Feature #962: Combat-Related AI instructions
Feature #1075: Damage/Restore skill/attribute magic effects
Feature #1076: Soultrap magic effect
Feature #1081: Disease contraction
Feature #1086: Blood particles
Feature #1092: Interrupt resting
Feature #1101: Inventory equip scripts
Feature #1116: Version/Build number in Launcher window
Feature #1119: Resistance/weakness to normal weapons magic effect
Feature #1123: Slow Fall magic effect
Feature #1130: Auto-calculate spells
Feature #1164: Editor: Case-insensitive sorting in tables
0.28.0
Bug #399: Inventory changes are not visible immediately

Loading…
Cancel
Save