forked from mirror/openmw-tes3mp
Merge pull request #439 from TES3MP/0.6.3 while resolving conflicts
Conflicts: apps/openmw-mp/CMakeLists.txt apps/openmw-mp/Script/Functions/GUI.cpp apps/openmw-mp/Script/Functions/GUI.hpp apps/openmw-mp/Script/Functions/Worldstate.cpp apps/openmw-mp/Script/Functions/Worldstate.hpp apps/openmw-mp/processors/worldstate/ProcessorRecordDynamic.hpp apps/openmw/mwmp/processors/worldstate/ProcessorRecordDynamic.hpp components/CMakeLists.txt components/openmw-mp/Packets/Player/PacketGUIBoxes.cpp
This commit is contained in:
commit
3f7e163c92
51 changed files with 1118 additions and 171 deletions
|
@ -3,6 +3,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
|
#include <components/esm/esmreader.hpp>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/filesystem/fstream.hpp>
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
@ -653,12 +654,6 @@ void MwIniImporter::setVerbose(bool verbose) {
|
||||||
mVerbose = verbose;
|
mVerbose = verbose;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MwIniImporter::numberToString(int n) {
|
|
||||||
std::stringstream str;
|
|
||||||
str << n;
|
|
||||||
return str.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
MwIniImporter::multistrmap MwIniImporter::loadIniFile(const boost::filesystem::path& filename) const {
|
MwIniImporter::multistrmap MwIniImporter::loadIniFile(const boost::filesystem::path& filename) const {
|
||||||
std::cout << "load ini file: " << filename << std::endl;
|
std::cout << "load ini file: " << filename << std::endl;
|
||||||
|
|
||||||
|
@ -800,7 +795,7 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con
|
||||||
multistrmap::const_iterator it = ini.begin();
|
multistrmap::const_iterator it = ini.begin();
|
||||||
for(int i=0; it != ini.end(); i++) {
|
for(int i=0; it != ini.end(); i++) {
|
||||||
archive = baseArchive;
|
archive = baseArchive;
|
||||||
archive.append(this->numberToString(i));
|
archive.append(std::to_string(i));
|
||||||
|
|
||||||
it = ini.find(archive);
|
it = ini.find(archive);
|
||||||
if(it == ini.end()) {
|
if(it == ini.end()) {
|
||||||
|
@ -824,33 +819,105 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, const boost::filesystem::path& iniFilename) const {
|
void MwIniImporter::dependencySortStep(std::string& element, MwIniImporter::dependencyList& source, std::vector<std::string>& result)
|
||||||
std::vector<std::pair<std::time_t, std::string> > contentFiles;
|
{
|
||||||
|
auto iter = std::find_if(
|
||||||
|
source.begin(),
|
||||||
|
source.end(),
|
||||||
|
[&element](std::pair< std::string, std::vector<std::string> >& sourceElement)
|
||||||
|
{
|
||||||
|
return sourceElement.first == element;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (iter != source.end())
|
||||||
|
{
|
||||||
|
auto foundElement = std::move(*iter);
|
||||||
|
source.erase(iter);
|
||||||
|
for (auto name : foundElement.second)
|
||||||
|
{
|
||||||
|
MwIniImporter::dependencySortStep(name, source, result);
|
||||||
|
}
|
||||||
|
result.push_back(std::move(foundElement.first));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> MwIniImporter::dependencySort(MwIniImporter::dependencyList source)
|
||||||
|
{
|
||||||
|
std::vector<std::string> result;
|
||||||
|
while (!source.empty())
|
||||||
|
{
|
||||||
|
MwIniImporter::dependencySortStep(source.begin()->first, source, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string>::iterator MwIniImporter::findString(std::vector<std::string>& source, const std::string& string)
|
||||||
|
{
|
||||||
|
return std::find_if(source.begin(), source.end(), [&string](const std::string& sourceString)
|
||||||
|
{
|
||||||
|
return Misc::StringUtils::ciEqual(sourceString, string);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MwIniImporter::addPaths(std::vector<boost::filesystem::path>& output, std::vector<std::string> input) {
|
||||||
|
for (auto& path : input) {
|
||||||
|
if (path.front() == '"')
|
||||||
|
{
|
||||||
|
path.erase(path.begin());
|
||||||
|
path.erase(path.end() - 1);
|
||||||
|
}
|
||||||
|
output.emplace_back(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, const boost::filesystem::path& iniFilename) const
|
||||||
|
{
|
||||||
|
std::vector<std::pair<std::time_t, boost::filesystem::path>> contentFiles;
|
||||||
std::string baseGameFile("Game Files:GameFile");
|
std::string baseGameFile("Game Files:GameFile");
|
||||||
std::string gameFile("");
|
std::string gameFile("");
|
||||||
std::time_t defaultTime = 0;
|
std::time_t defaultTime = 0;
|
||||||
|
ToUTF8::Utf8Encoder encoder(mEncoding);
|
||||||
|
|
||||||
// assume the Game Files are all in a "Data Files" directory under the directory holding Morrowind.ini
|
std::vector<boost::filesystem::path> dataPaths;
|
||||||
const boost::filesystem::path gameFilesDir(iniFilename.parent_path() /= "Data Files");
|
if (cfg.count("data"))
|
||||||
|
addPaths(dataPaths, cfg["data"]);
|
||||||
|
|
||||||
|
if (cfg.count("data-local"))
|
||||||
|
addPaths(dataPaths, cfg["data-local"]);
|
||||||
|
|
||||||
|
dataPaths.push_back(iniFilename.parent_path() /= "Data Files");
|
||||||
|
|
||||||
multistrmap::const_iterator it = ini.begin();
|
multistrmap::const_iterator it = ini.begin();
|
||||||
for(int i=0; it != ini.end(); i++) {
|
for (int i=0; it != ini.end(); i++)
|
||||||
|
{
|
||||||
gameFile = baseGameFile;
|
gameFile = baseGameFile;
|
||||||
gameFile.append(this->numberToString(i));
|
gameFile.append(std::to_string(i));
|
||||||
|
|
||||||
it = ini.find(gameFile);
|
it = ini.find(gameFile);
|
||||||
if(it == ini.end()) {
|
if(it == ini.end())
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
for(std::vector<std::string>::const_iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) {
|
for(std::vector<std::string>::const_iterator entry = it->second.begin(); entry!=it->second.end(); ++entry)
|
||||||
|
{
|
||||||
std::string filetype(entry->substr(entry->length()-3));
|
std::string filetype(entry->substr(entry->length()-3));
|
||||||
Misc::StringUtils::lowerCaseInPlace(filetype);
|
Misc::StringUtils::lowerCaseInPlace(filetype);
|
||||||
|
|
||||||
if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) {
|
if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0)
|
||||||
boost::filesystem::path filepath(gameFilesDir);
|
{
|
||||||
filepath /= *entry;
|
bool found = false;
|
||||||
contentFiles.push_back(std::make_pair(lastWriteTime(filepath, defaultTime), *entry));
|
for (auto & dataPath : dataPaths)
|
||||||
|
{
|
||||||
|
boost::filesystem::path path = dataPath / *entry;
|
||||||
|
std::time_t time = lastWriteTime(path, defaultTime);
|
||||||
|
if (time != defaultTime)
|
||||||
|
{
|
||||||
|
contentFiles.push_back({time, path});
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
std::cout << "Warning: " << *entry << " not found, ignoring" << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -858,11 +925,46 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co
|
||||||
cfg.erase("content");
|
cfg.erase("content");
|
||||||
cfg.insert( std::make_pair("content", std::vector<std::string>() ) );
|
cfg.insert( std::make_pair("content", std::vector<std::string>() ) );
|
||||||
|
|
||||||
// this will sort files by time order first, then alphabetical (maybe), I suspect non ASCII filenames will be stuffed.
|
// sort by timestamp
|
||||||
sort(contentFiles.begin(), contentFiles.end());
|
sort(contentFiles.begin(), contentFiles.end());
|
||||||
for(std::vector<std::pair<std::time_t, std::string> >::const_iterator iter=contentFiles.begin(); iter!=contentFiles.end(); ++iter) {
|
|
||||||
cfg["content"].push_back(iter->second);
|
MwIniImporter::dependencyList unsortedFiles;
|
||||||
|
|
||||||
|
ESM::ESMReader reader;
|
||||||
|
reader.setEncoder(&encoder);
|
||||||
|
for (auto& file : contentFiles)
|
||||||
|
{
|
||||||
|
reader.open(file.second.string());
|
||||||
|
std::vector<std::string> dependencies;
|
||||||
|
for (auto& gameFile : reader.getGameFiles())
|
||||||
|
{
|
||||||
|
dependencies.push_back(gameFile.name);
|
||||||
|
}
|
||||||
|
unsortedFiles.emplace_back(boost::filesystem::path(reader.getName()).filename().string(), dependencies);
|
||||||
|
reader.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto sortedFiles = dependencySort(unsortedFiles);
|
||||||
|
|
||||||
|
// hard-coded dependency Morrowind - Tribunal - Bloodmoon
|
||||||
|
if(findString(sortedFiles, "Morrowind.esm") != sortedFiles.end())
|
||||||
|
{
|
||||||
|
auto tribunalIter = findString(sortedFiles, "Tribunal.esm");
|
||||||
|
auto bloodmoonIter = findString(sortedFiles, "Bloodmoon.esm");
|
||||||
|
|
||||||
|
if (bloodmoonIter != sortedFiles.end() && tribunalIter != sortedFiles.end())
|
||||||
|
{
|
||||||
|
size_t bloodmoonIndex = std::distance(sortedFiles.begin(), bloodmoonIter);
|
||||||
|
size_t tribunalIndex = std::distance(sortedFiles.begin(), tribunalIter);
|
||||||
|
if (bloodmoonIndex < tribunalIndex)
|
||||||
|
tribunalIndex++;
|
||||||
|
sortedFiles.insert(bloodmoonIter, *tribunalIter);
|
||||||
|
sortedFiles.erase(sortedFiles.begin() + tribunalIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& file : sortedFiles)
|
||||||
|
cfg["content"].push_back(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MwIniImporter::writeToFile(std::ostream &out, const multistrmap &cfg) {
|
void MwIniImporter::writeToFile(std::ostream &out, const multistrmap &cfg) {
|
||||||
|
@ -901,9 +1003,5 @@ std::time_t MwIniImporter::lastWriteTime(const boost::filesystem::path& filename
|
||||||
std::cout << "content file: " << resolved << " timestamp = (" << writeTime <<
|
std::cout << "content file: " << resolved << " timestamp = (" << writeTime <<
|
||||||
") " << timeStrBuffer << std::endl;
|
") " << timeStrBuffer << std::endl;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
std::cout << "content file: " << filename << " not found" << std::endl;
|
|
||||||
}
|
|
||||||
return writeTime;
|
return writeTime;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ class MwIniImporter {
|
||||||
public:
|
public:
|
||||||
typedef std::map<std::string, std::string> strmap;
|
typedef std::map<std::string, std::string> strmap;
|
||||||
typedef std::map<std::string, std::vector<std::string> > multistrmap;
|
typedef std::map<std::string, std::vector<std::string> > multistrmap;
|
||||||
|
typedef std::vector< std::pair< std::string, std::vector<std::string> > > dependencyList;
|
||||||
|
|
||||||
MwIniImporter();
|
MwIniImporter();
|
||||||
void setInputEncoding(const ToUTF8::FromType& encoding);
|
void setInputEncoding(const ToUTF8::FromType& encoding);
|
||||||
|
@ -27,9 +28,14 @@ class MwIniImporter {
|
||||||
void importArchives(multistrmap &cfg, const multistrmap &ini) const;
|
void importArchives(multistrmap &cfg, const multistrmap &ini) const;
|
||||||
static void writeToFile(std::ostream &out, const multistrmap &cfg);
|
static void writeToFile(std::ostream &out, const multistrmap &cfg);
|
||||||
|
|
||||||
|
static std::vector<std::string> dependencySort(MwIniImporter::dependencyList source);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static void dependencySortStep(std::string& element, MwIniImporter::dependencyList& source, std::vector<std::string>& result);
|
||||||
|
static std::vector<std::string>::iterator findString(std::vector<std::string>& source, const std::string& string);
|
||||||
|
|
||||||
static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value);
|
static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value);
|
||||||
static std::string numberToString(int n);
|
static void addPaths(std::vector<boost::filesystem::path>& output, std::vector<std::string> input);
|
||||||
|
|
||||||
/// \return file's "last modified time", used in original MW to determine plug-in load order
|
/// \return file's "last modified time", used in original MW to determine plug-in load order
|
||||||
static std::time_t lastWriteTime(const boost::filesystem::path& filename, std::time_t defaultTime);
|
static std::time_t lastWriteTime(const boost::filesystem::path& filename, std::time_t defaultTime);
|
||||||
|
@ -40,5 +46,4 @@ class MwIniImporter {
|
||||||
ToUTF8::FromType mEncoding;
|
ToUTF8::FromType mEncoding;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -71,25 +71,25 @@ source_group(tes3mp-server\\processors\\actor FILES ${PROCESSORS_ACTOR})
|
||||||
|
|
||||||
set(PROCESSORS_PLAYER
|
set(PROCESSORS_PLAYER
|
||||||
processors/player/ProcessorChatMsg.hpp processors/player/ProcessorGUIMessageBox.hpp
|
processors/player/ProcessorChatMsg.hpp processors/player/ProcessorGUIMessageBox.hpp
|
||||||
|
|
||||||
processors/player/ProcessorGUIWindow.hpp
|
processors/player/ProcessorGUIWindow.hpp
|
||||||
|
|
||||||
processors/player/ProcessorRecordDynamic.hpp processors/player/ProcessorGameWeather.hpp
|
processors/player/ProcessorGameWeather.hpp processors/player/ProcessorPlayerAnimFlags.hpp
|
||||||
processors/player/ProcessorPlayerAnimFlags.hpp processors/player/ProcessorPlayerAnimPlay.hpp
|
processors/player/ProcessorPlayerAnimPlay.hpp processors/player/ProcessorPlayerAttack.hpp
|
||||||
processors/player/ProcessorPlayerAttack.hpp processors/player/ProcessorPlayerAttribute.hpp
|
processors/player/ProcessorPlayerAttribute.hpp processors/player/ProcessorPlayerBook.hpp
|
||||||
processors/player/ProcessorPlayerBook.hpp processors/player/ProcessorPlayerBounty.hpp
|
processors/player/ProcessorPlayerBounty.hpp processors/player/ProcessorPlayerCellChange.hpp
|
||||||
processors/player/ProcessorPlayerCellChange.hpp processors/player/ProcessorPlayerCellState.hpp
|
processors/player/ProcessorPlayerCellState.hpp processors/player/ProcessorPlayerCharClass.hpp
|
||||||
processors/player/ProcessorPlayerCharClass.hpp processors/player/ProcessorPlayerCharGen.hpp
|
processors/player/ProcessorPlayerCharGen.hpp processors/player/ProcessorPlayerDeath.hpp
|
||||||
processors/player/ProcessorPlayerDeath.hpp processors/player/ProcessorPlayerDisposition.hpp
|
processors/player/ProcessorPlayerDisposition.hpp processors/player/ProcessorPlayerEquipment.hpp
|
||||||
processors/player/ProcessorPlayerEquipment.hpp processors/player/ProcessorPlayerFaction.hpp
|
processors/player/ProcessorPlayerFaction.hpp processors/player/ProcessorPlayerInteraction.hpp
|
||||||
processors/player/ProcessorPlayerInteraction.hpp processors/player/ProcessorPlayerInventory.hpp
|
processors/player/ProcessorPlayerInventory.hpp processors/player/ProcessorPlayerJournal.hpp
|
||||||
processors/player/ProcessorPlayerJournal.hpp processors/player/ProcessorPlayerKillCount.hpp
|
processors/player/ProcessorPlayerKillCount.hpp processors/player/ProcessorPlayerLevel.hpp
|
||||||
processors/player/ProcessorPlayerLevel.hpp processors/player/ProcessorPlayerMap.hpp
|
processors/player/ProcessorPlayerMap.hpp processors/player/ProcessorPlayerMiscellaneous.hpp
|
||||||
processors/player/ProcessorPlayerMiscellaneous.hpp processors/player/ProcessorPlayerPosition.hpp
|
processors/player/ProcessorPlayerPosition.hpp processors/player/ProcessorPlayerQuickKeys.hpp
|
||||||
processors/player/ProcessorPlayerQuickKeys.hpp processors/player/ProcessorPlayerRest.hpp
|
processors/player/ProcessorPlayerRest.hpp processors/player/ProcessorPlayerResurrect.hpp
|
||||||
processors/player/ProcessorPlayerResurrect.hpp processors/player/ProcessorPlayerShapeshift.hpp
|
processors/player/ProcessorPlayerShapeshift.hpp processors/player/ProcessorPlayerSkill.hpp
|
||||||
processors/player/ProcessorPlayerSkill.hpp processors/player/ProcessorPlayerSpeech.hpp
|
processors/player/ProcessorPlayerSpeech.hpp processors/player/ProcessorPlayerSpellbook.hpp
|
||||||
processors/player/ProcessorPlayerSpellbook.hpp processors/player/ProcessorPlayerStatsDynamic.hpp
|
processors/player/ProcessorPlayerStatsDynamic.hpp processors/player/ProcessorPlayerTopic.hpp
|
||||||
processors/player/ProcessorPlayerTopic.hpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
source_group(tes3mp-server\\processors\\player FILES ${PROCESSORS_PLAYER})
|
source_group(tes3mp-server\\processors\\player FILES ${PROCESSORS_PLAYER})
|
||||||
|
@ -109,6 +109,12 @@ set(PROCESSORS_OBJECT
|
||||||
|
|
||||||
source_group(tes3mp-server\\processors\\object FILES ${PROCESSORS_OBJECT})
|
source_group(tes3mp-server\\processors\\object FILES ${PROCESSORS_OBJECT})
|
||||||
|
|
||||||
|
set(PROCESSORS_WORLDSTATE
|
||||||
|
processors/worldstate/ProcessorRecordDynamic.hpp
|
||||||
|
)
|
||||||
|
|
||||||
|
source_group(tes3mp-server\\processors\\worldstate FILES ${PROCESSORS_WORLDSTATE})
|
||||||
|
|
||||||
set(PROCESSORS
|
set(PROCESSORS
|
||||||
processors/ProcessorInitializer.cpp
|
processors/ProcessorInitializer.cpp
|
||||||
processors/PlayerProcessor.cpp
|
processors/PlayerProcessor.cpp
|
||||||
|
|
|
@ -68,12 +68,13 @@ void GUI::customMessageBox(sol::function fn, const char *label, const char *butt
|
||||||
setChanged();
|
setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GUI::inputDialog(sol::function fn, const char *label, sol::this_environment te)
|
void GUI::inputDialog(sol::function fn, const char *label, const char *note, sol::this_environment te)
|
||||||
{
|
{
|
||||||
mwmp::BasePlayer::GUIMessageBox mbox;
|
mwmp::BasePlayer::GUIMessageBox mbox;
|
||||||
|
|
||||||
mbox.id = generateGuiId();
|
mbox.id = generateGuiId();
|
||||||
mbox.label = label;
|
mbox.label = label;
|
||||||
|
mbox.note = note;
|
||||||
mbox.type = Player::GUIMessageBox::Type::InputDialog;
|
mbox.type = Player::GUIMessageBox::Type::InputDialog;
|
||||||
|
|
||||||
guiQueue.emplace(std::move(mbox), std::move(fn));
|
guiQueue.emplace(std::move(mbox), std::move(fn));
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
//
|
|
||||||
// Created by koncord on 15.08.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
@ -21,7 +17,7 @@ public:
|
||||||
void messageBox(sol::function fn, const char *label, sol::this_environment te);
|
void messageBox(sol::function fn, const char *label, sol::this_environment te);
|
||||||
|
|
||||||
void customMessageBox(sol::function fn, const char *label, const char *buttons, sol::this_environment te);
|
void customMessageBox(sol::function fn, const char *label, const char *buttons, sol::this_environment te);
|
||||||
void inputDialog(sol::function fn, const char *label, sol::this_environment te);
|
void inputDialog(sol::function fn, const char *label, const char *note, sol::this_environment te);
|
||||||
void passwordDialog(sol::function fn, const char *label, const char *note, sol::this_environment te);
|
void passwordDialog(sol::function fn, const char *label, const char *note, sol::this_environment te);
|
||||||
|
|
||||||
void listBox(sol::function fn, const char *label, const char *items, sol::this_environment te);
|
void listBox(sol::function fn, const char *label, const char *items, sol::this_environment te);
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
//
|
|
||||||
// Created by koncord on 01.08.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
|
@ -264,8 +260,6 @@ LuaState::LuaState()
|
||||||
auto packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_TIME);
|
auto packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_TIME);
|
||||||
|
|
||||||
tempWorldstate.hour = hour;
|
tempWorldstate.hour = hour;
|
||||||
tempWorldstate.month = -1;
|
|
||||||
tempWorldstate.day = -1;
|
|
||||||
|
|
||||||
Players::for_each([&hour, &packet](Player *player){
|
Players::for_each([&hour, &packet](Player *player){
|
||||||
|
|
||||||
|
@ -279,9 +273,7 @@ LuaState::LuaState()
|
||||||
|
|
||||||
auto packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_TIME);
|
auto packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_TIME);
|
||||||
|
|
||||||
tempWorldstate.hour = -1;
|
|
||||||
tempWorldstate.month = month;
|
tempWorldstate.month = month;
|
||||||
tempWorldstate.day = -1;
|
|
||||||
|
|
||||||
Players::for_each([&month, &packet](Player *player){
|
Players::for_each([&month, &packet](Player *player){
|
||||||
|
|
||||||
|
@ -294,8 +286,6 @@ LuaState::LuaState()
|
||||||
lua->set_function("setDay", [](int day) {
|
lua->set_function("setDay", [](int day) {
|
||||||
auto packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_TIME);
|
auto packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_TIME);
|
||||||
|
|
||||||
tempWorldstate.hour = -1;
|
|
||||||
tempWorldstate.month = -1;
|
|
||||||
tempWorldstate.day = day;
|
tempWorldstate.day = day;
|
||||||
|
|
||||||
Players::for_each([&day, &packet](Player *player){
|
Players::for_each([&day, &packet](Player *player){
|
||||||
|
@ -306,6 +296,48 @@ LuaState::LuaState()
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
lua->set_function("setYear", [](int year) {
|
||||||
|
|
||||||
|
auto packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_TIME);
|
||||||
|
|
||||||
|
tempWorldstate.year = year;
|
||||||
|
|
||||||
|
Players::for_each([&year, &packet](Player *player) {
|
||||||
|
|
||||||
|
tempWorldstate.guid = player->guid;
|
||||||
|
packet->setWorldstate(&tempWorldstate);
|
||||||
|
packet->Send(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
lua->set_function("setDaysPassed", [](int daysPassed) {
|
||||||
|
|
||||||
|
auto packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_TIME);
|
||||||
|
|
||||||
|
tempWorldstate.daysPassed = daysPassed;
|
||||||
|
|
||||||
|
Players::for_each([&daysPassed, &packet](Player *player) {
|
||||||
|
|
||||||
|
tempWorldstate.guid = player->guid;
|
||||||
|
packet->setWorldstate(&tempWorldstate);
|
||||||
|
packet->Send(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
lua->set_function("setDaysPassed", [](float timeScale) {
|
||||||
|
|
||||||
|
auto packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_TIME);
|
||||||
|
|
||||||
|
tempWorldstate.timeScale = timeScale;
|
||||||
|
|
||||||
|
Players::for_each([&timeScale, &packet](Player *player) {
|
||||||
|
|
||||||
|
tempWorldstate.guid = player->guid;
|
||||||
|
packet->setWorldstate(&tempWorldstate);
|
||||||
|
packet->Send(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
lua->set_function("createChannel", [](){
|
lua->set_function("createChannel", [](){
|
||||||
return mwmp::Networking::get().createChannel();
|
return mwmp::Networking::get().createChannel();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
//
|
|
||||||
// Created by koncord on 31.03.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "ProcessorInitializer.hpp"
|
#include "ProcessorInitializer.hpp"
|
||||||
|
|
||||||
#include "Networking.hpp"
|
#include "Networking.hpp"
|
||||||
|
@ -11,7 +7,6 @@
|
||||||
#include "player/ProcessorGUIMessageBox.hpp"
|
#include "player/ProcessorGUIMessageBox.hpp"
|
||||||
#include "player/ProcessorGUIWindow.hpp"
|
#include "player/ProcessorGUIWindow.hpp"
|
||||||
#include "player/ProcessorGameWeather.hpp"
|
#include "player/ProcessorGameWeather.hpp"
|
||||||
#include "player/ProcessorRecordDynamic.hpp"
|
|
||||||
#include "player/ProcessorPlayerCharGen.hpp"
|
#include "player/ProcessorPlayerCharGen.hpp"
|
||||||
#include "player/ProcessorPlayerAnimFlags.hpp"
|
#include "player/ProcessorPlayerAnimFlags.hpp"
|
||||||
#include "player/ProcessorPlayerAnimPlay.hpp"
|
#include "player/ProcessorPlayerAnimPlay.hpp"
|
||||||
|
@ -79,7 +74,8 @@
|
||||||
#include "object/ProcessorScriptGlobalShort.hpp"
|
#include "object/ProcessorScriptGlobalShort.hpp"
|
||||||
#include "object/ProcessorScriptGlobalFloat.hpp"
|
#include "object/ProcessorScriptGlobalFloat.hpp"
|
||||||
#include "object/ProcessorVideoPlay.hpp"
|
#include "object/ProcessorVideoPlay.hpp"
|
||||||
|
#include "WorldstateProcessor.hpp"
|
||||||
|
#include "worldstate/ProcessorRecordDynamic.hpp"
|
||||||
|
|
||||||
using namespace mwmp;
|
using namespace mwmp;
|
||||||
|
|
||||||
|
@ -89,7 +85,6 @@ void ProcessorInitializer()
|
||||||
PlayerProcessor::AddProcessor(new ProcessorGUIMessageBox());
|
PlayerProcessor::AddProcessor(new ProcessorGUIMessageBox());
|
||||||
PlayerProcessor::AddProcessor(new ProcessorGUIWindow());
|
PlayerProcessor::AddProcessor(new ProcessorGUIWindow());
|
||||||
PlayerProcessor::AddProcessor(new ProcessorGameWeather());
|
PlayerProcessor::AddProcessor(new ProcessorGameWeather());
|
||||||
PlayerProcessor::AddProcessor(new ProcessorRecordDynamic());
|
|
||||||
PlayerProcessor::AddProcessor(new ProcessorPlayerCharGen());
|
PlayerProcessor::AddProcessor(new ProcessorPlayerCharGen());
|
||||||
PlayerProcessor::AddProcessor(new ProcessorPlayerAnimFlags());
|
PlayerProcessor::AddProcessor(new ProcessorPlayerAnimFlags());
|
||||||
PlayerProcessor::AddProcessor(new ProcessorPlayerAnimPlay());
|
PlayerProcessor::AddProcessor(new ProcessorPlayerAnimPlay());
|
||||||
|
@ -157,4 +152,6 @@ void ProcessorInitializer()
|
||||||
ObjectProcessor::AddProcessor(new ProcessorScriptGlobalShort());
|
ObjectProcessor::AddProcessor(new ProcessorScriptGlobalShort());
|
||||||
ObjectProcessor::AddProcessor(new ProcessorScriptGlobalFloat());
|
ObjectProcessor::AddProcessor(new ProcessorScriptGlobalFloat());
|
||||||
ObjectProcessor::AddProcessor(new ProcessorVideoPlay());
|
ObjectProcessor::AddProcessor(new ProcessorVideoPlay());
|
||||||
|
|
||||||
|
WorldstateProcessor::AddProcessor(new ProcessorRecordDynamic());
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ using namespace mwmp;
|
||||||
template<class T>
|
template<class T>
|
||||||
typename BasePacketProcessor<T>::processors_t BasePacketProcessor<T>::processors;
|
typename BasePacketProcessor<T>::processors_t BasePacketProcessor<T>::processors;
|
||||||
|
|
||||||
void WorldstateProcessor::Do(WorldstatePacket &packet, Player &player, BaseWorldstate &worldstate)
|
void WorldstateProcessor::Do(WorldstatePacket &packet, const std::shared_ptr<Player> &player, BaseWorldstate &worldstate)
|
||||||
{
|
{
|
||||||
packet.Send(true);
|
packet.Send(true);
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ bool WorldstateProcessor::Process(RakNet::Packet &packet, BaseWorldstate &worlds
|
||||||
myPacket->Read();
|
myPacket->Read();
|
||||||
|
|
||||||
if (worldstate.isValid)
|
if (worldstate.isValid)
|
||||||
processor.second->Do(*myPacket, *player, worldstate);
|
processor.second->Do(*myPacket, player, worldstate);
|
||||||
else
|
else
|
||||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Received %s that failed integrity check and was ignored!", processor.second->strPacketID.c_str());
|
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Received %s that failed integrity check and was ignored!", processor.second->strPacketID.c_str());
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace mwmp
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual void Do(WorldstatePacket &packet, Player &player, BaseWorldstate &worldstate);
|
virtual void Do(WorldstatePacket &packet, const std::shared_ptr<Player> &player, BaseWorldstate &worldstate);
|
||||||
|
|
||||||
static bool Process(RakNet::Packet &packet, BaseWorldstate &worldstate) noexcept;
|
static bool Process(RakNet::Packet &packet, BaseWorldstate &worldstate) noexcept;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
#ifndef OPENMW_PROCESSORRECORDDYNAMIC_HPP
|
#ifndef OPENMW_PROCESSORRECORDDYNAMIC_HPP
|
||||||
#define OPENMW_PROCESSORRECORDDYNAMIC_HPP
|
#define OPENMW_PROCESSORRECORDDYNAMIC_HPP
|
||||||
|
|
||||||
#include "../PlayerProcessor.hpp"
|
#include "../WorldstateProcessor.hpp"
|
||||||
|
|
||||||
namespace mwmp
|
namespace mwmp
|
||||||
{
|
{
|
||||||
class ProcessorRecordDynamic final : public PlayerProcessor
|
class ProcessorRecordDynamic final : public WorldstateProcessor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ProcessorRecordDynamic()
|
ProcessorRecordDynamic()
|
||||||
|
@ -13,7 +13,7 @@ namespace mwmp
|
||||||
BPP_INIT(ID_RECORD_DYNAMIC)
|
BPP_INIT(ID_RECORD_DYNAMIC)
|
||||||
}
|
}
|
||||||
|
|
||||||
void Do(PlayerPacket &packet, const std::shared_ptr<Player> &player) override
|
void Do(WorldstatePacket &packet, const std::shared_ptr<Player> &player, BaseWorldstate &worldstate) override
|
||||||
{
|
{
|
||||||
DEBUG_PRINTF(strPacketID.c_str());
|
DEBUG_PRINTF(strPacketID.c_str());
|
||||||
|
|
|
@ -114,15 +114,14 @@ add_openmw_dir (mwmp/processors/actor ProcessorActorAI ProcessorActorAnimFlags P
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwmp/processors/player ProcessorChatMessage ProcessorGUIMessageBox ProcessorHandshake
|
add_openmw_dir (mwmp/processors/player ProcessorChatMessage ProcessorGUIMessageBox ProcessorHandshake
|
||||||
ProcessorUserDisconnected ProcessorUserMyID ProcessorCellCreate ProcessorRecordDynamic ProcessorGameSettings
|
ProcessorUserDisconnected ProcessorUserMyID ProcessorCellCreate ProcessorGameSettings ProcessorGameWeather
|
||||||
ProcessorGameWeather ProcessorPlayerAnimFlags ProcessorPlayerAnimPlay ProcessorPlayerAttack ProcessorPlayerAttribute
|
ProcessorPlayerAnimFlags ProcessorPlayerAnimPlay ProcessorPlayerAttack ProcessorPlayerAttribute ProcessorPlayerBaseInfo
|
||||||
ProcessorPlayerBaseInfo ProcessorPlayerBehavior ProcessorPlayerBook ProcessorPlayerBounty ProcessorPlayerCellChange
|
ProcessorPlayerBehavior ProcessorPlayerBook ProcessorPlayerBounty ProcessorPlayerCellChange ProcessorPlayerCellState
|
||||||
ProcessorPlayerCellState ProcessorPlayerCharClass ProcessorPlayerCharGen ProcessorPlayerDeath ProcessorPlayerDisposition
|
ProcessorPlayerCharClass ProcessorPlayerCharGen ProcessorPlayerDeath ProcessorPlayerDisposition ProcessorPlayerEquipment
|
||||||
ProcessorPlayerEquipment ProcessorPlayerFaction ProcessorPlayerInteraction ProcessorPlayerInventory ProcessorPlayerJail
|
ProcessorPlayerFaction ProcessorPlayerInteraction ProcessorPlayerInventory ProcessorPlayerJail ProcessorPlayerJournal
|
||||||
ProcessorPlayerJournal ProcessorPlayerKillCount ProcessorPlayerLevel ProcessorPlayerMap ProcessorPlayerMiscellaneous
|
ProcessorPlayerKillCount ProcessorPlayerLevel ProcessorPlayerMap ProcessorPlayerMiscellaneous ProcessorPlayerMomentum
|
||||||
ProcessorPlayerMomentum ProcessorPlayerPosition ProcessorPlayerQuickKeys ProcessorPlayerReputation ProcessorPlayerResurrect
|
ProcessorPlayerPosition ProcessorPlayerQuickKeys ProcessorPlayerReputation ProcessorPlayerResurrect ProcessorPlayerShapeshift
|
||||||
ProcessorPlayerShapeshift ProcessorPlayerSkill ProcessorPlayerSpeech ProcessorPlayerSpellbook ProcessorPlayerStatsDynamic
|
ProcessorPlayerSkill ProcessorPlayerSpeech ProcessorPlayerSpellbook ProcessorPlayerStatsDynamic ProcessorPlayerTopic
|
||||||
ProcessorPlayerTopic
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwmp/processors/object BaseObjectProcessor ProcessorConsoleCommand ProcessorContainer ProcessorDoorDestination
|
add_openmw_dir (mwmp/processors/object BaseObjectProcessor ProcessorConsoleCommand ProcessorContainer ProcessorDoorDestination
|
||||||
|
@ -133,7 +132,7 @@ add_openmw_dir (mwmp/processors/object BaseObjectProcessor ProcessorConsoleComma
|
||||||
ProcessorScriptMemberFloat ProcessorScriptGlobalShort ProcessorScriptGlobalFloat
|
ProcessorScriptMemberFloat ProcessorScriptGlobalShort ProcessorScriptGlobalFloat
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwmp/processors/worldstate ProcessorWorldTime
|
add_openmw_dir (mwmp/processors/worldstate ProcessorRecordDynamic ProcessorWorldTime
|
||||||
)
|
)
|
||||||
|
|
||||||
# Main executable
|
# Main executable
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
#include <osg/ref_ptr>
|
||||||
|
#include <osg/Node>
|
||||||
|
|
||||||
#include <components/esm/cellid.hpp>
|
#include <components/esm/cellid.hpp>
|
||||||
|
|
||||||
#include "../mwworld/ptr.hpp"
|
#include "../mwworld/ptr.hpp"
|
||||||
|
@ -203,6 +206,36 @@ namespace MWBase
|
||||||
virtual void setDay (int day) = 0;
|
virtual void setDay (int day) = 0;
|
||||||
///< Set in-game time day.
|
///< Set in-game time day.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start of tes3mp addition
|
||||||
|
|
||||||
|
Make it possible to set the year from elsewhere
|
||||||
|
*/
|
||||||
|
virtual void setYear(int year) = 0;
|
||||||
|
/*
|
||||||
|
End of tes3mp addition
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start of tes3mp addition
|
||||||
|
|
||||||
|
Make it possible to set the number of days passed from elsewhere
|
||||||
|
*/
|
||||||
|
virtual void setDaysPassed(int daysPassed) = 0;
|
||||||
|
/*
|
||||||
|
End of tes3mp addition
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start of tes3mp addition
|
||||||
|
|
||||||
|
Make it possible to set a custom timeScale from elsewhere
|
||||||
|
*/
|
||||||
|
virtual void setTimeScale(float timeScale) = 0;
|
||||||
|
/*
|
||||||
|
End of tes3mp addition
|
||||||
|
*/
|
||||||
|
|
||||||
virtual int getDay() const = 0;
|
virtual int getDay() const = 0;
|
||||||
virtual int getMonth() const = 0;
|
virtual int getMonth() const = 0;
|
||||||
virtual int getYear() const = 0;
|
virtual int getYear() const = 0;
|
||||||
|
@ -424,6 +457,7 @@ namespace MWBase
|
||||||
virtual bool isUnderwater(const MWWorld::ConstPtr &object, const float heightRatio) const = 0;
|
virtual bool isUnderwater(const MWWorld::ConstPtr &object, const float heightRatio) const = 0;
|
||||||
virtual bool isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr &target) const = 0;
|
virtual bool isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr &target) const = 0;
|
||||||
virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0;
|
virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0;
|
||||||
|
virtual bool isIdle(const MWWorld::Ptr &ptr) const = 0;
|
||||||
|
|
||||||
virtual osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const = 0;
|
virtual osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const = 0;
|
||||||
|
|
||||||
|
@ -590,7 +624,7 @@ namespace MWBase
|
||||||
/// Spawn a blood effect for \a ptr at \a worldPosition
|
/// Spawn a blood effect for \a ptr at \a worldPosition
|
||||||
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) = 0;
|
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) = 0;
|
||||||
|
|
||||||
virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos) = 0;
|
virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true) = 0;
|
||||||
|
|
||||||
virtual void explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster,
|
virtual void explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster,
|
||||||
const MWWorld::Ptr& ignore, ESM::RangeType rangeType, const std::string& id,
|
const MWWorld::Ptr& ignore, ESM::RangeType rangeType, const std::string& id,
|
||||||
|
@ -632,6 +666,8 @@ namespace MWBase
|
||||||
|
|
||||||
/// Preload VFX associated with this effect list
|
/// Preload VFX associated with this effect list
|
||||||
virtual void preloadEffects(const ESM::EffectList* effectList) = 0;
|
virtual void preloadEffects(const ESM::EffectList* effectList) = 0;
|
||||||
|
|
||||||
|
virtual osg::ref_ptr<osg::Node> getInstance (const std::string& modelName) = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1137,7 +1137,7 @@ namespace MWClass
|
||||||
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
|
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
|
||||||
const float encumbranceTerm = gmst.fJumpEncumbranceBase->getFloat() +
|
const float encumbranceTerm = gmst.fJumpEncumbranceBase->getFloat() +
|
||||||
gmst.fJumpEncumbranceMultiplier->getFloat() *
|
gmst.fJumpEncumbranceMultiplier->getFloat() *
|
||||||
(1.0f - Npc::getEncumbrance(ptr)/Npc::getCapacity(ptr));
|
(1.0f - Npc::getNormalizedEncumbrance(ptr));
|
||||||
|
|
||||||
float a = static_cast<float>(npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified());
|
float a = static_cast<float>(npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified());
|
||||||
float b = 0.0f;
|
float b = 0.0f;
|
||||||
|
|
|
@ -866,8 +866,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
|
|
||||||
// place above to prevent moving inside objects, e.g. stairs, because a vector between pathgrids can be underground.
|
// place above to prevent moving inside objects, e.g. stairs, because a vector between pathgrids can be underground.
|
||||||
// Adding 20 in adjustPosition() is not enough.
|
dest.mZ += 80;
|
||||||
dest.mZ += 60;
|
|
||||||
|
|
||||||
ToWorldCoordinates(dest, actor.getCell()->getCell());
|
ToWorldCoordinates(dest, actor.getCell()->getCell());
|
||||||
|
|
||||||
|
|
|
@ -863,6 +863,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
|
||||||
refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true);
|
refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true);
|
||||||
|
|
||||||
mAnimation->runAnimation(0.f);
|
mAnimation->runAnimation(0.f);
|
||||||
|
mAnimation->updateEffects(0.f);
|
||||||
|
|
||||||
unpersistAnimationState();
|
unpersistAnimationState();
|
||||||
}
|
}
|
||||||
|
@ -1869,8 +1870,7 @@ void CharacterController::update(float duration)
|
||||||
|
|
||||||
if (cls.getEncumbrance(mPtr) <= cls.getCapacity(mPtr))
|
if (cls.getEncumbrance(mPtr) <= cls.getCapacity(mPtr))
|
||||||
{
|
{
|
||||||
const float encumbrance = cls.getEncumbrance(mPtr) / cls.getCapacity(mPtr);
|
const float encumbrance = cls.getNormalizedEncumbrance(mPtr);
|
||||||
|
|
||||||
if (sneak)
|
if (sneak)
|
||||||
fatigueLoss = fFatigueSneakBase + encumbrance * fFatigueSneakMult;
|
fatigueLoss = fFatigueSneakBase + encumbrance * fFatigueSneakMult;
|
||||||
else
|
else
|
||||||
|
@ -2118,6 +2118,13 @@ void CharacterController::update(float duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Vec3f moved = mAnimation->runAnimation(mSkipAnim ? 0.f : duration);
|
osg::Vec3f moved = mAnimation->runAnimation(mSkipAnim ? 0.f : duration);
|
||||||
|
|
||||||
|
// treat player specifically since he is not in rendering mObjects
|
||||||
|
if (mPtr == getPlayer())
|
||||||
|
{
|
||||||
|
mAnimation->updateEffects(mSkipAnim ? 0.f : duration);
|
||||||
|
}
|
||||||
|
|
||||||
if(duration > 0.0f)
|
if(duration > 0.0f)
|
||||||
moved /= duration;
|
moved /= duration;
|
||||||
else
|
else
|
||||||
|
@ -2150,7 +2157,8 @@ void CharacterController::update(float duration)
|
||||||
moved.z() = 1.0;
|
moved.z() = 1.0;
|
||||||
|
|
||||||
// Update movement
|
// Update movement
|
||||||
if(mMovementAnimationControlled && mPtr.getClass().isActor())
|
// We should not apply movement for standing actors
|
||||||
|
if(mMovementAnimationControlled && mPtr.getClass().isActor() && (movement.length2() > 0.f || !world->isIdle(mPtr)))
|
||||||
world->queueMovement(mPtr, moved);
|
world->queueMovement(mPtr, moved);
|
||||||
|
|
||||||
mSkipAnim = false;
|
mSkipAnim = false;
|
||||||
|
@ -2331,6 +2339,7 @@ void CharacterController::forceStateUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
mAnimation->runAnimation(0.f);
|
mAnimation->runAnimation(0.f);
|
||||||
|
mAnimation->updateEffects(0.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterController::KillResult CharacterController::kill()
|
CharacterController::KillResult CharacterController::kill()
|
||||||
|
|
|
@ -6,8 +6,12 @@
|
||||||
|
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
|
|
||||||
|
#include <osg/BoundingBox>
|
||||||
|
#include <osg/ComputeBoundsVisitor>
|
||||||
|
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Start of tes3mp addition
|
Start of tes3mp addition
|
||||||
|
@ -40,6 +44,7 @@
|
||||||
#include "../mwworld/inventorystore.hpp"
|
#include "../mwworld/inventorystore.hpp"
|
||||||
|
|
||||||
#include "../mwrender/animation.hpp"
|
#include "../mwrender/animation.hpp"
|
||||||
|
#include "../mwrender/vismask.hpp"
|
||||||
|
|
||||||
#include "npcstats.hpp"
|
#include "npcstats.hpp"
|
||||||
#include "actorutil.hpp"
|
#include "actorutil.hpp"
|
||||||
|
@ -1066,11 +1071,13 @@ namespace MWMechanics
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CastSpell::playSpellCastingEffects(const std::string &spellid){
|
void CastSpell::playSpellCastingEffects(const std::string &spellid)
|
||||||
|
{
|
||||||
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
|
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
|
||||||
|
|
||||||
|
std::vector<std::string> addedEffects;
|
||||||
|
|
||||||
for (std::vector<ESM::ENAMstruct>::const_iterator iter = spell->mEffects.mList.begin();
|
for (std::vector<ESM::ENAMstruct>::const_iterator iter = spell->mEffects.mList.begin();
|
||||||
iter != spell->mEffects.mList.end(); ++iter)
|
iter != spell->mEffects.mList.end(); ++iter)
|
||||||
{
|
{
|
||||||
|
@ -1079,18 +1086,72 @@ namespace MWMechanics
|
||||||
|
|
||||||
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster);
|
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster);
|
||||||
|
|
||||||
if (animation && mCaster.getClass().isActor()) // TODO: Non-actors should also create a spell cast vfx even if they are disabled (animation == NULL)
|
const ESM::Static* castStatic;
|
||||||
|
|
||||||
|
if (!effect->mCasting.empty())
|
||||||
|
castStatic = store.get<ESM::Static>().find (effect->mCasting);
|
||||||
|
else
|
||||||
|
castStatic = store.get<ESM::Static>().find ("VFX_DefaultCast");
|
||||||
|
|
||||||
|
// check if the effect was already added
|
||||||
|
if (std::find(addedEffects.begin(), addedEffects.end(), "meshes\\" + castStatic->mModel) != addedEffects.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string texture = effect->mParticle;
|
||||||
|
|
||||||
|
float scale = 1.0f;
|
||||||
|
osg::Vec3f pos (mCaster.getRefData().getPosition().asVec3());
|
||||||
|
|
||||||
|
if (animation && mCaster.getClass().isNpc())
|
||||||
{
|
{
|
||||||
const ESM::Static* castStatic;
|
// For NOC we should take race height as scaling factor
|
||||||
|
const ESM::NPC *npc = mCaster.get<ESM::NPC>()->mBase;
|
||||||
|
const MWWorld::ESMStore &esmStore =
|
||||||
|
MWBase::Environment::get().getWorld()->getStore();
|
||||||
|
|
||||||
if (!effect->mCasting.empty())
|
const ESM::Race *race =
|
||||||
castStatic = store.get<ESM::Static>().find (effect->mCasting);
|
esmStore.get<ESM::Race>().find(npc->mRace);
|
||||||
else
|
|
||||||
castStatic = store.get<ESM::Static>().find ("VFX_DefaultCast");
|
|
||||||
|
|
||||||
std::string texture = effect->mParticle;
|
scale = npc->isMale() ? race->mData.mHeight.mMale : race->mData.mHeight.mFemale;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string casterModel = mCaster.getClass().getModel(mCaster);
|
||||||
|
osg::ref_ptr<osg::Node> model = MWBase::Environment::get().getWorld()->getInstance(casterModel);
|
||||||
|
|
||||||
animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture);
|
osg::ComputeBoundsVisitor computeBoundsVisitor;
|
||||||
|
computeBoundsVisitor.setTraversalMask(~(MWRender::Mask_ParticleSystem|MWRender::Mask_Effect));
|
||||||
|
model->accept(computeBoundsVisitor);
|
||||||
|
osg::BoundingBox bounds = computeBoundsVisitor.getBoundingBox();
|
||||||
|
|
||||||
|
if (bounds.valid())
|
||||||
|
{
|
||||||
|
float meshSizeX = std::abs(bounds.xMax() - bounds.xMin());
|
||||||
|
float meshSizeY = std::abs(bounds.yMax() - bounds.yMin());
|
||||||
|
float meshSizeZ = std::abs(bounds.zMax() - bounds.zMin());
|
||||||
|
|
||||||
|
// TODO: take a size of particle or NPC with height and weight = 1.0 as scale = 1.0
|
||||||
|
float scaleX = meshSizeX/60.f;
|
||||||
|
float scaleY = meshSizeY/60.f;
|
||||||
|
float scaleZ = meshSizeZ/120.f;
|
||||||
|
|
||||||
|
scale = std::max({ scaleX, scaleY, scaleZ });
|
||||||
|
|
||||||
|
//pos = bounds.center();
|
||||||
|
//pos[2] = bounds.zMin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the caster has no animation, add the effect directly to the effectManager
|
||||||
|
if (animation)
|
||||||
|
{
|
||||||
|
animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture, scale);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We should set scale for effect manager manually
|
||||||
|
float meshScale = !mCaster.getClass().isActor() ? mCaster.getCellRef().getScale() : 1.0f;
|
||||||
|
MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + castStatic->mModel, effect->mParticle, pos, scale * meshScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (animation && !mCaster.getClass().isActor())
|
if (animation && !mCaster.getClass().isActor())
|
||||||
|
@ -1100,6 +1161,8 @@ namespace MWMechanics
|
||||||
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
|
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
addedEffects.push_back("meshes\\" + castStatic->mModel);
|
||||||
|
|
||||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||||
if(!effect->mCastSound.empty())
|
if(!effect->mCastSound.empty())
|
||||||
sndMgr->playSound3D(mCaster, effect->mCastSound, 1.0f, 1.0f);
|
sndMgr->playSound3D(mCaster, effect->mCastSound, 1.0f, 1.0f);
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#include "player/ProcessorHandshake.hpp"
|
#include "player/ProcessorHandshake.hpp"
|
||||||
#include "player/ProcessorUserDisconnected.hpp"
|
#include "player/ProcessorUserDisconnected.hpp"
|
||||||
#include "player/ProcessorCellCreate.hpp"
|
#include "player/ProcessorCellCreate.hpp"
|
||||||
#include "player/ProcessorRecordDynamic.hpp"
|
|
||||||
#include "player/ProcessorGameSettings.hpp"
|
#include "player/ProcessorGameSettings.hpp"
|
||||||
#include "player/ProcessorGameWeather.hpp"
|
#include "player/ProcessorGameWeather.hpp"
|
||||||
#include "player/ProcessorPlayerAnimFlags.hpp"
|
#include "player/ProcessorPlayerAnimFlags.hpp"
|
||||||
|
@ -92,6 +91,7 @@
|
||||||
#include "actor/ProcessorActorTest.hpp"
|
#include "actor/ProcessorActorTest.hpp"
|
||||||
|
|
||||||
#include "WorldstateProcessor.hpp"
|
#include "WorldstateProcessor.hpp"
|
||||||
|
#include "worldstate/ProcessorRecordDynamic.hpp"
|
||||||
#include "worldstate/ProcessorWorldTime.hpp"
|
#include "worldstate/ProcessorWorldTime.hpp"
|
||||||
|
|
||||||
using namespace mwmp;
|
using namespace mwmp;
|
||||||
|
@ -104,7 +104,6 @@ void ProcessorInitializer()
|
||||||
PlayerProcessor::AddProcessor(new ProcessorHandshake());
|
PlayerProcessor::AddProcessor(new ProcessorHandshake());
|
||||||
PlayerProcessor::AddProcessor(new ProcessorUserDisconnected());
|
PlayerProcessor::AddProcessor(new ProcessorUserDisconnected());
|
||||||
PlayerProcessor::AddProcessor(new ProcessorCellCreate());
|
PlayerProcessor::AddProcessor(new ProcessorCellCreate());
|
||||||
PlayerProcessor::AddProcessor(new ProcessorRecordDynamic());
|
|
||||||
PlayerProcessor::AddProcessor(new ProcessorGameSettings());
|
PlayerProcessor::AddProcessor(new ProcessorGameSettings());
|
||||||
PlayerProcessor::AddProcessor(new ProcessorGameWeather());
|
PlayerProcessor::AddProcessor(new ProcessorGameWeather());
|
||||||
PlayerProcessor::AddProcessor(new ProcessorPlayerAnimFlags());
|
PlayerProcessor::AddProcessor(new ProcessorPlayerAnimFlags());
|
||||||
|
@ -186,5 +185,6 @@ void ProcessorInitializer()
|
||||||
ActorProcessor::AddProcessor(new ProcessorActorStatsDynamic());
|
ActorProcessor::AddProcessor(new ProcessorActorStatsDynamic());
|
||||||
ActorProcessor::AddProcessor(new ProcessorActorTest());
|
ActorProcessor::AddProcessor(new ProcessorActorTest());
|
||||||
|
|
||||||
|
WorldstateProcessor::AddProcessor(new ProcessorRecordDynamic());
|
||||||
WorldstateProcessor::AddProcessor(new ProcessorWorldTime());
|
WorldstateProcessor::AddProcessor(new ProcessorWorldTime());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
#ifndef OPENMW_PROCESSORRECORDDYNAMIC_HPP
|
#ifndef OPENMW_PROCESSORRECORDDYNAMIC_HPP
|
||||||
#define OPENMW_PROCESSORRECORDDYNAMIC_HPP
|
#define OPENMW_PROCESSORRECORDDYNAMIC_HPP
|
||||||
|
|
||||||
#include "../PlayerProcessor.hpp"
|
#include "../WorldstateProcessor.hpp"
|
||||||
|
|
||||||
namespace mwmp
|
namespace mwmp
|
||||||
{
|
{
|
||||||
class ProcessorRecordDynamic final: public PlayerProcessor
|
class ProcessorRecordDynamic final : public WorldstateProcessor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ProcessorRecordDynamic()
|
ProcessorRecordDynamic()
|
||||||
|
@ -13,7 +13,7 @@ namespace mwmp
|
||||||
BPP_INIT(ID_RECORD_DYNAMIC)
|
BPP_INIT(ID_RECORD_DYNAMIC)
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Do(PlayerPacket &packet, BasePlayer *player)
|
virtual void Do(WorldstatePacket &packet, BaseWorldstate &worldstate)
|
||||||
{
|
{
|
||||||
// Placeholder
|
// Placeholder
|
||||||
}
|
}
|
|
@ -21,12 +21,24 @@ namespace mwmp
|
||||||
if (isLocal())
|
if (isLocal())
|
||||||
{
|
{
|
||||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||||
|
|
||||||
if (worldstate.hour != -1)
|
if (worldstate.hour != -1)
|
||||||
world->setHour(worldstate.hour);
|
world->setHour(worldstate.hour);
|
||||||
else if (worldstate.day != -1)
|
|
||||||
|
if (worldstate.day != -1)
|
||||||
world->setDay(worldstate.day);
|
world->setDay(worldstate.day);
|
||||||
else if (worldstate.month != -1)
|
|
||||||
|
if (worldstate.month != -1)
|
||||||
world->setMonth(worldstate.month);
|
world->setMonth(worldstate.month);
|
||||||
|
|
||||||
|
if (worldstate.year != -1)
|
||||||
|
world->setYear(worldstate.year);
|
||||||
|
|
||||||
|
if (worldstate.daysPassed != -1)
|
||||||
|
world->setDaysPassed(worldstate.daysPassed);
|
||||||
|
|
||||||
|
if (worldstate.timeScale != -1)
|
||||||
|
world->setTimeScale(worldstate.timeScale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace MWPhysics
|
||||||
|
|
||||||
Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr<const Resource::BulletShape> shape, btCollisionWorld* world)
|
Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr<const Resource::BulletShape> shape, btCollisionWorld* world)
|
||||||
: mCanWaterWalk(false), mWalkingOnWater(false)
|
: mCanWaterWalk(false), mWalkingOnWater(false)
|
||||||
, mCollisionObject(nullptr), mForce(0.f, 0.f, 0.f), mOnGround(true), mOnSlope(false)
|
, mCollisionObject(nullptr), mForce(0.f, 0.f, 0.f), mOnGround(true), mOnSlope(false), mIdle(true)
|
||||||
, mInternalCollisionMode(true)
|
, mInternalCollisionMode(true)
|
||||||
, mExternalCollisionMode(true)
|
, mExternalCollisionMode(true)
|
||||||
, mCollisionWorld(world)
|
, mCollisionWorld(world)
|
||||||
|
@ -195,6 +195,11 @@ void Actor::setOnSlope(bool slope)
|
||||||
mOnSlope = slope;
|
mOnSlope = slope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Actor::setIdle(bool idle)
|
||||||
|
{
|
||||||
|
mIdle = idle;
|
||||||
|
}
|
||||||
|
|
||||||
bool Actor::isWalkingOnWater() const
|
bool Actor::isWalkingOnWater() const
|
||||||
{
|
{
|
||||||
return mWalkingOnWater;
|
return mWalkingOnWater;
|
||||||
|
|
|
@ -139,6 +139,13 @@ namespace MWPhysics
|
||||||
return mInternalCollisionMode && mOnSlope;
|
return mInternalCollisionMode && mOnSlope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setIdle(bool idle);
|
||||||
|
|
||||||
|
bool getIdle() const
|
||||||
|
{
|
||||||
|
return mIdle;
|
||||||
|
}
|
||||||
|
|
||||||
btCollisionObject* getCollisionObject() const
|
btCollisionObject* getCollisionObject() const
|
||||||
{
|
{
|
||||||
return mCollisionObject.get();
|
return mCollisionObject.get();
|
||||||
|
@ -179,6 +186,7 @@ namespace MWPhysics
|
||||||
osg::Vec3f mForce;
|
osg::Vec3f mForce;
|
||||||
bool mOnGround;
|
bool mOnGround;
|
||||||
bool mOnSlope;
|
bool mOnSlope;
|
||||||
|
bool mIdle;
|
||||||
bool mInternalCollisionMode;
|
bool mInternalCollisionMode;
|
||||||
bool mExternalCollisionMode;
|
bool mExternalCollisionMode;
|
||||||
|
|
||||||
|
|
|
@ -249,7 +249,7 @@ namespace MWPhysics
|
||||||
// Check if we actually found a valid spawn point (use an infinitely thin ray this time).
|
// Check if we actually found a valid spawn point (use an infinitely thin ray this time).
|
||||||
// Required for some broken door destinations in Morrowind.esm, where the spawn point
|
// Required for some broken door destinations in Morrowind.esm, where the spawn point
|
||||||
// intersects with other geometry if the actor's base is taken into account
|
// intersects with other geometry if the actor's base is taken into account
|
||||||
btVector3 from = toBullet(position);
|
btVector3 from = toBullet(position + offset);
|
||||||
btVector3 to = from - btVector3(0,0,maxHeight);
|
btVector3 to = from - btVector3(0,0,maxHeight);
|
||||||
|
|
||||||
btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to);
|
btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to);
|
||||||
|
@ -683,6 +683,7 @@ namespace MWPhysics
|
||||||
, mWaterEnabled(false)
|
, mWaterEnabled(false)
|
||||||
, mParentNode(parentNode)
|
, mParentNode(parentNode)
|
||||||
, mPhysicsDt(1.f / 60.f)
|
, mPhysicsDt(1.f / 60.f)
|
||||||
|
, mIdleUpdateTimer(0)
|
||||||
{
|
{
|
||||||
mResourceSystem->addResourceManager(mShapeManager.get());
|
mResourceSystem->addResourceManager(mShapeManager.get());
|
||||||
|
|
||||||
|
@ -739,6 +740,18 @@ namespace MWPhysics
|
||||||
delete mBroadphase;
|
delete mBroadphase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PhysicsSystem::updateIdle()
|
||||||
|
{
|
||||||
|
for (ActorMap::iterator it = mActors.begin(); it != mActors.end(); ++it)
|
||||||
|
{
|
||||||
|
osg::Vec3f pos(it->second->getCollisionObjectPosition());
|
||||||
|
|
||||||
|
RayResult result = castRay(pos, pos - osg::Vec3f(0, 0, it->second->getHalfExtents().z() + 2), it->second->getPtr(), std::vector<MWWorld::Ptr>(), CollisionType_World|CollisionType_HeightMap|CollisionType_Door);
|
||||||
|
|
||||||
|
it->second->setIdle(result.mHit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PhysicsSystem::setUnrefQueue(SceneUtil::UnrefQueue *unrefQueue)
|
void PhysicsSystem::setUnrefQueue(SceneUtil::UnrefQueue *unrefQueue)
|
||||||
{
|
{
|
||||||
mUnrefQueue = unrefQueue;
|
mUnrefQueue = unrefQueue;
|
||||||
|
@ -1067,6 +1080,11 @@ namespace MWPhysics
|
||||||
return physactor && physactor->getOnGround();
|
return physactor && physactor->getOnGround();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PhysicsSystem::isIdle(const MWWorld::Ptr &actor)
|
||||||
|
{
|
||||||
|
Actor* physactor = getActor(actor);
|
||||||
|
return physactor && physactor->getIdle();
|
||||||
|
}
|
||||||
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);
|
||||||
|
@ -1359,6 +1377,10 @@ namespace MWPhysics
|
||||||
cmode = !cmode;
|
cmode = !cmode;
|
||||||
found->second->enableCollisionMode(cmode);
|
found->second->enableCollisionMode(cmode);
|
||||||
found->second->enableCollisionBody(cmode);
|
found->second->enableCollisionBody(cmode);
|
||||||
|
|
||||||
|
if (cmode)
|
||||||
|
queueObjectMovement(MWMechanics::getPlayer(), osg::Vec3f(0, 0, -0.1f));
|
||||||
|
|
||||||
return cmode;
|
return cmode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1478,6 +1500,13 @@ namespace MWPhysics
|
||||||
for (std::set<Object*>::iterator it = mAnimatedObjects.begin(); it != mAnimatedObjects.end(); ++it)
|
for (std::set<Object*>::iterator it = mAnimatedObjects.begin(); it != mAnimatedObjects.end(); ++it)
|
||||||
(*it)->animateCollisionShapes(mCollisionWorld);
|
(*it)->animateCollisionShapes(mCollisionWorld);
|
||||||
|
|
||||||
|
mIdleUpdateTimer -= dt;
|
||||||
|
if (mIdleUpdateTimer <= 0.f)
|
||||||
|
{
|
||||||
|
mIdleUpdateTimer = 0.5f;
|
||||||
|
updateIdle();
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef BT_NO_PROFILE
|
#ifndef BT_NO_PROFILE
|
||||||
CProfileManager::Reset();
|
CProfileManager::Reset();
|
||||||
CProfileManager::Increment_Frame_Counter();
|
CProfileManager::Increment_Frame_Counter();
|
||||||
|
|
|
@ -124,6 +124,7 @@ namespace MWPhysics
|
||||||
bool getLineOfSight(const MWWorld::ConstPtr& actor1, const MWWorld::ConstPtr& actor2) const;
|
bool getLineOfSight(const MWWorld::ConstPtr& actor1, const MWWorld::ConstPtr& actor2) const;
|
||||||
|
|
||||||
bool isOnGround (const MWWorld::Ptr& actor);
|
bool isOnGround (const MWWorld::Ptr& actor);
|
||||||
|
bool isIdle (const MWWorld::Ptr& actor);
|
||||||
|
|
||||||
bool canMoveToWaterSurface (const MWWorld::ConstPtr &actor, const float waterlevel);
|
bool canMoveToWaterSurface (const MWWorld::ConstPtr &actor, const float waterlevel);
|
||||||
|
|
||||||
|
@ -183,6 +184,7 @@ namespace MWPhysics
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void updateWater();
|
void updateWater();
|
||||||
|
void updateIdle();
|
||||||
|
|
||||||
osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;
|
osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;
|
||||||
|
|
||||||
|
@ -231,6 +233,7 @@ namespace MWPhysics
|
||||||
osg::ref_ptr<osg::Group> mParentNode;
|
osg::ref_ptr<osg::Group> mParentNode;
|
||||||
|
|
||||||
float mPhysicsDt;
|
float mPhysicsDt;
|
||||||
|
float mIdleUpdateTimer;
|
||||||
|
|
||||||
PhysicsSystem (const PhysicsSystem&);
|
PhysicsSystem (const PhysicsSystem&);
|
||||||
PhysicsSystem& operator= (const PhysicsSystem&);
|
PhysicsSystem& operator= (const PhysicsSystem&);
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
#include <osg/MatrixTransform>
|
#include <osg/MatrixTransform>
|
||||||
#include <osg/BlendFunc>
|
#include <osg/BlendFunc>
|
||||||
#include <osg/Material>
|
#include <osg/Material>
|
||||||
|
#include <osg/ComputeBoundsVisitor>
|
||||||
|
#include <osg/PositionAttitudeTransform>
|
||||||
|
|
||||||
#include <osgParticle/ParticleSystem>
|
#include <osgParticle/ParticleSystem>
|
||||||
#include <osgParticle/ParticleProcessor>
|
#include <osgParticle/ParticleProcessor>
|
||||||
|
@ -1115,8 +1117,6 @@ namespace MWRender
|
||||||
++stateiter;
|
++stateiter;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateEffects(duration);
|
|
||||||
|
|
||||||
if (mHeadController)
|
if (mHeadController)
|
||||||
{
|
{
|
||||||
const float epsilon = 0.001f;
|
const float epsilon = 0.001f;
|
||||||
|
@ -1366,7 +1366,7 @@ namespace MWRender
|
||||||
useQuadratic, quadraticValue, quadraticRadiusMult, useLinear, linearRadiusMult, linearValue);
|
useQuadratic, quadraticValue, quadraticRadiusMult, useLinear, linearRadiusMult, linearValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture)
|
void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture, float scale)
|
||||||
{
|
{
|
||||||
if (!mObjectRoot.get())
|
if (!mObjectRoot.get())
|
||||||
return;
|
return;
|
||||||
|
@ -1417,7 +1417,13 @@ namespace MWRender
|
||||||
|
|
||||||
overrideFirstRootTexture(texture, mResourceSystem, node);
|
overrideFirstRootTexture(texture, mResourceSystem, node);
|
||||||
|
|
||||||
// TODO: in vanilla morrowind the effect is scaled based on the host object's bounding box.
|
osg::Vec3f scale3f (scale, scale, scale);
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::PositionAttitudeTransform> trans = new osg::PositionAttitudeTransform;
|
||||||
|
trans->setScale(scale3f);
|
||||||
|
trans->addChild(node);
|
||||||
|
parentNode->removeChild(node);
|
||||||
|
parentNode->addChild(trans);
|
||||||
|
|
||||||
mEffects.push_back(params);
|
mEffects.push_back(params);
|
||||||
}
|
}
|
||||||
|
|
|
@ -364,7 +364,7 @@ public:
|
||||||
* @param texture override the texture specified in the model's materials - if empty, do not override
|
* @param texture override the texture specified in the model's materials - if empty, do not override
|
||||||
* @note Will not add an effect twice.
|
* @note Will not add an effect twice.
|
||||||
*/
|
*/
|
||||||
void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", const std::string& texture = "");
|
void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", const std::string& texture = "", float scale = 1.0f);
|
||||||
void removeEffect (int effectId);
|
void removeEffect (int effectId);
|
||||||
void getLoopingEffects (std::vector<int>& out) const;
|
void getLoopingEffects (std::vector<int>& out) const;
|
||||||
|
|
||||||
|
@ -446,7 +446,6 @@ public:
|
||||||
|
|
||||||
void setLoopingEnabled(const std::string &groupname, bool enabled);
|
void setLoopingEnabled(const std::string &groupname, bool enabled);
|
||||||
|
|
||||||
/// This is typically called as part of runAnimation, but may be called manually if needed.
|
|
||||||
void updateEffects(float duration);
|
void updateEffects(float duration);
|
||||||
|
|
||||||
/// Return a node with the specified name, or NULL if not existing.
|
/// Return a node with the specified name, or NULL if not existing.
|
||||||
|
|
|
@ -26,6 +26,13 @@ EffectManager::~EffectManager()
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectManager::addEffect(const std::string &model, const std::string& textureOverride, const osg::Vec3f &worldPosition, float scale, bool isMagicVFX)
|
void EffectManager::addEffect(const std::string &model, const std::string& textureOverride, const osg::Vec3f &worldPosition, float scale, bool isMagicVFX)
|
||||||
|
{
|
||||||
|
osg::Vec3f scale3f (scale, scale, scale);
|
||||||
|
|
||||||
|
addEffect(model, textureOverride, worldPosition, scale3f, isMagicVFX);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectManager::addEffect(const std::string &model, const std::string& textureOverride, const osg::Vec3f &worldPosition, const osg::Vec3f &scale, bool isMagicVFX)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(model);
|
osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(model);
|
||||||
|
|
||||||
|
@ -40,7 +47,7 @@ void EffectManager::addEffect(const std::string &model, const std::string& textu
|
||||||
|
|
||||||
osg::ref_ptr<osg::PositionAttitudeTransform> trans = new osg::PositionAttitudeTransform;
|
osg::ref_ptr<osg::PositionAttitudeTransform> trans = new osg::PositionAttitudeTransform;
|
||||||
trans->setPosition(worldPosition);
|
trans->setPosition(worldPosition);
|
||||||
trans->setScale(osg::Vec3f(scale, scale, scale));
|
trans->setScale(scale);
|
||||||
trans->addChild(node);
|
trans->addChild(node);
|
||||||
|
|
||||||
SceneUtil::AssignControllerSourcesVisitor assignVisitor(effect.mAnimTime);
|
SceneUtil::AssignControllerSourcesVisitor assignVisitor(effect.mAnimTime);
|
||||||
|
|
|
@ -33,6 +33,7 @@ namespace MWRender
|
||||||
|
|
||||||
/// Add an effect. When it's finished playing, it will be removed automatically.
|
/// Add an effect. When it's finished playing, it will be removed automatically.
|
||||||
void addEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPosition, float scale, bool isMagicVFX = true);
|
void addEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPosition, float scale, bool isMagicVFX = true);
|
||||||
|
void addEffect (const std::string &model, const std::string& textureOverride, const osg::Vec3f &worldPosition, const osg::Vec3f &scale3f, bool isMagicVFX);
|
||||||
|
|
||||||
void update(float dt);
|
void update(float dt);
|
||||||
|
|
||||||
|
|
|
@ -138,6 +138,14 @@ bool Objects::removeObject (const MWWorld::Ptr& ptr)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Objects::updateEffects(float duration)
|
||||||
|
{
|
||||||
|
for(PtrAnimationMap::iterator iter = mObjects.begin();iter != mObjects.end();)
|
||||||
|
{
|
||||||
|
iter->second->updateEffects(duration);
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Objects::removeCell(const MWWorld::CellStore* store)
|
void Objects::removeCell(const MWWorld::CellStore* store)
|
||||||
{
|
{
|
||||||
|
|
|
@ -88,6 +88,8 @@ public:
|
||||||
bool removeObject (const MWWorld::Ptr& ptr);
|
bool removeObject (const MWWorld::Ptr& ptr);
|
||||||
///< \return found?
|
///< \return found?
|
||||||
|
|
||||||
|
void updateEffects(float duration);
|
||||||
|
|
||||||
void removeCell(const MWWorld::CellStore* store);
|
void removeCell(const MWWorld::CellStore* store);
|
||||||
|
|
||||||
/// Updates containing cell for object rendering data
|
/// Updates containing cell for object rendering data
|
||||||
|
|
|
@ -557,6 +557,7 @@ namespace MWRender
|
||||||
mEffectManager->update(dt);
|
mEffectManager->update(dt);
|
||||||
mSky->update(dt);
|
mSky->update(dt);
|
||||||
mWater->update(dt);
|
mWater->update(dt);
|
||||||
|
mObjects->updateEffects(dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
mCamera->update(dt, paused);
|
mCamera->update(dt, paused);
|
||||||
|
@ -841,9 +842,11 @@ namespace MWRender
|
||||||
mObjects->updatePtr(old, updated);
|
mObjects->updatePtr(old, updated);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const osg::Vec3f &worldPosition, float scale, bool isMagicVFX)
|
void RenderingManager::spawnEffect(const std::string &model, const std::string& texture, const osg::Vec3f &worldPosition, float scale, bool isMagicVFX)
|
||||||
{
|
{
|
||||||
mEffectManager->addEffect(model, texture, worldPosition, scale, isMagicVFX);
|
osg::Vec3f scale3f (scale, scale, scale);
|
||||||
|
|
||||||
|
mEffectManager->addEffect(model, texture, worldPosition, scale3f, isMagicVFX);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::notifyWorldSpaceChanged()
|
void RenderingManager::notifyWorldSpaceChanged()
|
||||||
|
@ -1122,6 +1125,12 @@ namespace MWRender
|
||||||
updateProjectionMatrix();
|
updateProjectionMatrix();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Node> RenderingManager::getInstance(const std::string& modelName)
|
||||||
|
{
|
||||||
|
return mResourceSystem->getSceneManager()->getInstance(modelName);
|
||||||
|
}
|
||||||
|
|
||||||
void RenderingManager::exportSceneGraph(const MWWorld::Ptr &ptr, const std::string &filename, const std::string &format)
|
void RenderingManager::exportSceneGraph(const MWWorld::Ptr &ptr, const std::string &filename, const std::string &format)
|
||||||
{
|
{
|
||||||
osg::Node* node = mViewer->getSceneData();
|
osg::Node* node = mViewer->getSceneData();
|
||||||
|
|
|
@ -205,6 +205,8 @@ namespace MWRender
|
||||||
|
|
||||||
LandManager* getLandManager() const;
|
LandManager* getLandManager() const;
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Node> getInstance(const std::string& modelName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateProjectionMatrix();
|
void updateProjectionMatrix();
|
||||||
void updateTextureFiltering();
|
void updateTextureFiltering();
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <components/esm/loadcrea.hpp>
|
#include <components/esm/loadcrea.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
#include "../mwbase/scriptmanager.hpp"
|
#include "../mwbase/scriptmanager.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
@ -770,6 +771,8 @@ namespace MWScript
|
||||||
|
|
||||||
virtual void execute (Interpreter::Runtime& runtime)
|
virtual void execute (Interpreter::Runtime& runtime)
|
||||||
{
|
{
|
||||||
|
MWWorld::Ptr ptr = R()(runtime);
|
||||||
|
MWBase::Environment::get().getWorld()->queueMovement(ptr, osg::Vec3f(0, 0, -0.1f));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1121,6 +1124,7 @@ namespace MWScript
|
||||||
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr (targetId, false);
|
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr (targetId, false);
|
||||||
|
|
||||||
MWMechanics::CastSpell cast(ptr, target);
|
MWMechanics::CastSpell cast(ptr, target);
|
||||||
|
cast.playSpellCastingEffects(spell);
|
||||||
cast.mHitPosition = target.getRefData().getPosition().asVec3();
|
cast.mHitPosition = target.getRefData().getPosition().asVec3();
|
||||||
cast.mAlwaysSucceed = true;
|
cast.mAlwaysSucceed = true;
|
||||||
cast.cast(spell);
|
cast.cast(spell);
|
||||||
|
|
|
@ -506,7 +506,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
|
||||||
if (firstPersonCam != MWBase::Environment::get().getWorld()->isFirstPerson())
|
if (firstPersonCam != MWBase::Environment::get().getWorld()->isFirstPerson())
|
||||||
MWBase::Environment::get().getWorld()->togglePOV();
|
MWBase::Environment::get().getWorld()->togglePOV();
|
||||||
|
|
||||||
MWWorld::ConstPtr ptr = MWMechanics::getPlayer();
|
const MWWorld::Ptr ptr = MWMechanics::getPlayer();
|
||||||
|
|
||||||
if (ptr.isInCell())
|
if (ptr.isInCell())
|
||||||
{
|
{
|
||||||
|
@ -538,6 +538,9 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
|
||||||
// Since we passed "changeEvent=false" to changeCell, we shouldn't have triggered the cell change flag.
|
// Since we passed "changeEvent=false" to changeCell, we shouldn't have triggered the cell change flag.
|
||||||
// But make sure the flag is cleared anyway in case it was set from an earlier game.
|
// But make sure the flag is cleared anyway in case it was set from an earlier game.
|
||||||
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
|
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
|
||||||
|
|
||||||
|
// Workaround to fix camera position upon game load
|
||||||
|
MWBase::Environment::get().getWorld()->queueMovement(ptr, osg::Vec3f(0, 0, 0));
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -475,10 +475,15 @@ namespace MWWorld
|
||||||
float Class::getNormalizedEncumbrance(const Ptr &ptr) const
|
float Class::getNormalizedEncumbrance(const Ptr &ptr) const
|
||||||
{
|
{
|
||||||
float capacity = getCapacity(ptr);
|
float capacity = getCapacity(ptr);
|
||||||
|
float encumbrance = getEncumbrance(ptr);
|
||||||
|
|
||||||
|
if (encumbrance == 0)
|
||||||
|
return 0.f;
|
||||||
|
|
||||||
if (capacity == 0)
|
if (capacity == 0)
|
||||||
return 1.f;
|
return 1.f;
|
||||||
|
|
||||||
return getEncumbrance(ptr) / capacity;
|
return encumbrance / capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Class::getSound(const MWWorld::ConstPtr&) const
|
std::string Class::getSound(const MWWorld::ConstPtr&) const
|
||||||
|
|
|
@ -935,6 +935,45 @@ namespace MWWorld
|
||||||
mRendering->skySetDate (mDay->getInteger(), month);
|
mRendering->skySetDate (mDay->getInteger(), month);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start of tes3mp addition
|
||||||
|
|
||||||
|
Make it possible to set the year from elsewhere
|
||||||
|
*/
|
||||||
|
void World::setYear(int year)
|
||||||
|
{
|
||||||
|
mYear->setInteger(year);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
End of tes3mp addition
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start of tes3mp addition
|
||||||
|
|
||||||
|
Make it possible to set the number of days passed from elsewhere
|
||||||
|
*/
|
||||||
|
void World::setDaysPassed(int days)
|
||||||
|
{
|
||||||
|
mDaysPassed->setInteger(days);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
End of tes3mp addition
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start of tes3mp addition
|
||||||
|
|
||||||
|
Make it possible to set a custom timeScale from elsewhere
|
||||||
|
*/
|
||||||
|
void World::setTimeScale(float timeScale)
|
||||||
|
{
|
||||||
|
mTimeScale->setFloat(timeScale);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
End of tes3mp addition
|
||||||
|
*/
|
||||||
|
|
||||||
int World::getDay() const
|
int World::getDay() const
|
||||||
{
|
{
|
||||||
return mDay->getInteger();
|
return mDay->getInteger();
|
||||||
|
@ -1398,8 +1437,6 @@ namespace MWWorld
|
||||||
if (pos.z() < terrainHeight)
|
if (pos.z() < terrainHeight)
|
||||||
pos.z() = terrainHeight;
|
pos.z() = terrainHeight;
|
||||||
|
|
||||||
pos.z() += 20; // place slightly above. will snap down to ground with code below
|
|
||||||
|
|
||||||
if (force || !isFlying(ptr))
|
if (force || !isFlying(ptr))
|
||||||
{
|
{
|
||||||
osg::Vec3f traced = mPhysics->traceDown(ptr, pos, 500);
|
osg::Vec3f traced = mPhysics->traceDown(ptr, pos, 500);
|
||||||
|
@ -1645,6 +1682,11 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Node> World::getInstance (const std::string& modelName)
|
||||||
|
{
|
||||||
|
return mRendering->getInstance(modelName);
|
||||||
|
}
|
||||||
|
|
||||||
const ESM::Potion *World::createRecord (const ESM::Potion& record)
|
const ESM::Potion *World::createRecord (const ESM::Potion& record)
|
||||||
{
|
{
|
||||||
return mStore.insert(record);
|
return mStore.insert(record);
|
||||||
|
@ -1736,7 +1778,7 @@ namespace MWWorld
|
||||||
if (!paused)
|
if (!paused)
|
||||||
doPhysics (duration);
|
doPhysics (duration);
|
||||||
|
|
||||||
updatePlayer(paused);
|
updatePlayer();
|
||||||
|
|
||||||
mPhysics->debugDraw();
|
mPhysics->debugDraw();
|
||||||
|
|
||||||
|
@ -1752,7 +1794,7 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::updatePlayer(bool paused)
|
void World::updatePlayer()
|
||||||
{
|
{
|
||||||
MWWorld::Ptr player = getPlayerPtr();
|
MWWorld::Ptr player = getPlayerPtr();
|
||||||
|
|
||||||
|
@ -1785,7 +1827,7 @@ namespace MWWorld
|
||||||
bool swimming = isSwimming(player);
|
bool swimming = isSwimming(player);
|
||||||
|
|
||||||
static const float i1stPersonSneakDelta = getStore().get<ESM::GameSetting>().find("i1stPersonSneakDelta")->getFloat();
|
static const float i1stPersonSneakDelta = getStore().get<ESM::GameSetting>().find("i1stPersonSneakDelta")->getFloat();
|
||||||
if(!paused && sneaking && !(swimming || inair))
|
if (sneaking && !(swimming || inair))
|
||||||
mRendering->getCamera()->setSneakOffset(i1stPersonSneakDelta);
|
mRendering->getCamera()->setSneakOffset(i1stPersonSneakDelta);
|
||||||
else
|
else
|
||||||
mRendering->getCamera()->setSneakOffset(0.f);
|
mRendering->getCamera()->setSneakOffset(0.f);
|
||||||
|
@ -2272,6 +2314,11 @@ namespace MWWorld
|
||||||
return mPhysics->isOnGround(ptr);
|
return mPhysics->isOnGround(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool World::isIdle(const MWWorld::Ptr &ptr) const
|
||||||
|
{
|
||||||
|
return mPhysics->isIdle(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
void World::togglePOV()
|
void World::togglePOV()
|
||||||
{
|
{
|
||||||
mRendering->togglePOV();
|
mRendering->togglePOV();
|
||||||
|
@ -3528,9 +3575,9 @@ namespace MWWorld
|
||||||
mRendering->spawnEffect(model, texture, worldPosition, 1.0f, false);
|
mRendering->spawnEffect(model, texture, worldPosition, 1.0f, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::spawnEffect(const std::string &model, const std::string &textureOverride, const osg::Vec3f &worldPos)
|
void World::spawnEffect(const std::string &model, const std::string &textureOverride, const osg::Vec3f &worldPos, float scale, bool isMagicVFX)
|
||||||
{
|
{
|
||||||
mRendering->spawnEffect(model, textureOverride, worldPos);
|
mRendering->spawnEffect(model, textureOverride, worldPos, scale, isMagicVFX);
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const Ptr& caster, const Ptr& ignore, ESM::RangeType rangeType,
|
void World::explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const Ptr& caster, const Ptr& ignore, ESM::RangeType rangeType,
|
||||||
|
|
|
@ -129,7 +129,7 @@ namespace MWWorld
|
||||||
Ptr copyObjectToCell(const ConstPtr &ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos);
|
Ptr copyObjectToCell(const ConstPtr &ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos);
|
||||||
|
|
||||||
void updateSoundListener();
|
void updateSoundListener();
|
||||||
void updatePlayer(bool paused);
|
void updatePlayer();
|
||||||
|
|
||||||
void preloadSpells();
|
void preloadSpells();
|
||||||
|
|
||||||
|
@ -315,6 +315,36 @@ namespace MWWorld
|
||||||
void setDay (int day) override;
|
void setDay (int day) override;
|
||||||
///< Set in-game time day.
|
///< Set in-game time day.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start of tes3mp addition
|
||||||
|
|
||||||
|
Make it possible to set the year from elsewhere
|
||||||
|
*/
|
||||||
|
void setYear(int year) override;
|
||||||
|
/*
|
||||||
|
End of tes3mp addition
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start of tes3mp addition
|
||||||
|
|
||||||
|
Make it possible to set the number of days passed from elsewhere
|
||||||
|
*/
|
||||||
|
void setDaysPassed(int daysPassed) override;
|
||||||
|
/*
|
||||||
|
End of tes3mp addition
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start of tes3mp addition
|
||||||
|
|
||||||
|
Make it possible to set a custom timeScale from elsewhere
|
||||||
|
*/
|
||||||
|
void setTimeScale(float timeScale) override;
|
||||||
|
/*
|
||||||
|
End of tes3mp addition
|
||||||
|
*/
|
||||||
|
|
||||||
int getDay() const override;
|
int getDay() const override;
|
||||||
int getMonth() const override;
|
int getMonth() const override;
|
||||||
int getYear() const override;
|
int getYear() const override;
|
||||||
|
@ -523,6 +553,7 @@ namespace MWWorld
|
||||||
bool isWading(const MWWorld::ConstPtr &object) const override;
|
bool isWading(const MWWorld::ConstPtr &object) const override;
|
||||||
bool isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr &target) const override;
|
bool isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr &target) const override;
|
||||||
bool isOnGround(const MWWorld::Ptr &ptr) const override;
|
bool isOnGround(const MWWorld::Ptr &ptr) const override;
|
||||||
|
bool isIdle(const MWWorld::Ptr &ptr) const override;
|
||||||
|
|
||||||
osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const override;
|
osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const override;
|
||||||
|
|
||||||
|
@ -694,7 +725,7 @@ namespace MWWorld
|
||||||
/// Spawn a blood effect for \a ptr at \a worldPosition
|
/// Spawn a blood effect for \a ptr at \a worldPosition
|
||||||
void spawnBloodEffect (const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) override;
|
void spawnBloodEffect (const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) override;
|
||||||
|
|
||||||
void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos) override;
|
void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true) override;
|
||||||
|
|
||||||
void explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const MWWorld::Ptr& ignore,
|
void explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const MWWorld::Ptr& ignore,
|
||||||
ESM::RangeType rangeType, const std::string& id, const std::string& sourceName,
|
ESM::RangeType rangeType, const std::string& id, const std::string& sourceName,
|
||||||
|
@ -734,6 +765,8 @@ namespace MWWorld
|
||||||
|
|
||||||
/// Preload VFX associated with this effect list
|
/// Preload VFX associated with this effect list
|
||||||
void preloadEffects(const ESM::EffectList* effectList) override;
|
void preloadEffects(const ESM::EffectList* effectList) override;
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Node> getInstance (const std::string& modelName);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -231,29 +231,13 @@ void Wizard::MainWizard::setupInstallations()
|
||||||
|
|
||||||
void Wizard::MainWizard::runSettingsImporter()
|
void Wizard::MainWizard::runSettingsImporter()
|
||||||
{
|
{
|
||||||
|
writeSettings();
|
||||||
|
|
||||||
QString path(field(QLatin1String("installation.path")).toString());
|
QString path(field(QLatin1String("installation.path")).toString());
|
||||||
|
|
||||||
// Create the file if it doesn't already exist, else the importer will fail
|
|
||||||
QString userPath(toQString(mCfgMgr.getUserConfigPath()));
|
QString userPath(toQString(mCfgMgr.getUserConfigPath()));
|
||||||
QFile file(userPath + QLatin1String("openmw.cfg"));
|
QFile file(userPath + QLatin1String("openmw.cfg"));
|
||||||
|
|
||||||
if (!file.exists()) {
|
|
||||||
if (!file.open(QIODevice::ReadWrite)) {
|
|
||||||
// File cannot be created
|
|
||||||
QMessageBox msgBox;
|
|
||||||
msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
|
|
||||||
msgBox.setIcon(QMessageBox::Critical);
|
|
||||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
|
||||||
msgBox.setText(tr("<html><head/><body><p><b>Could not open or create %1 for writing</b></p> \
|
|
||||||
<p>Please make sure you have the right permissions \
|
|
||||||
and try again.</p></body></html>").arg(file.fileName()));
|
|
||||||
msgBox.exec();
|
|
||||||
return qApp->quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct the arguments to run the importer
|
// Construct the arguments to run the importer
|
||||||
QStringList arguments;
|
QStringList arguments;
|
||||||
|
|
||||||
|
|
|
@ -167,6 +167,7 @@ add_component_dir (openmw-mp/Packets
|
||||||
|
|
||||||
add_component_dir (openmw-mp/Packets/Actor
|
add_component_dir (openmw-mp/Packets/Actor
|
||||||
ActorPacket
|
ActorPacket
|
||||||
|
|
||||||
PacketActorList PacketActorAuthority PacketActorTest PacketActorAI PacketActorAnimFlags PacketActorAnimPlay
|
PacketActorList PacketActorAuthority PacketActorTest PacketActorAI PacketActorAnimFlags PacketActorAnimPlay
|
||||||
PacketActorAttack PacketActorCellChange PacketActorDeath PacketActorEquipment PacketActorInteraction PacketActorPosition
|
PacketActorAttack PacketActorCellChange PacketActorDeath PacketActorEquipment PacketActorInteraction PacketActorPosition
|
||||||
PacketActorSpeech PacketActorStatsDynamic
|
PacketActorSpeech PacketActorStatsDynamic
|
||||||
|
@ -174,9 +175,10 @@ add_component_dir (openmw-mp/Packets/Actor
|
||||||
|
|
||||||
add_component_dir (openmw-mp/Packets/Player
|
add_component_dir (openmw-mp/Packets/Player
|
||||||
PlayerPacket
|
PlayerPacket
|
||||||
|
|
||||||
PacketHandshake PacketChatMessage PacketGUIBoxes PacketGUIWindow PacketGameSettings PacketGameWeather
|
PacketHandshake PacketChatMessage PacketGUIBoxes PacketGUIWindow PacketGameSettings PacketGameWeather
|
||||||
|
|
||||||
PacketCellCreate PacketRecordDynamic
|
PacketCellCreate
|
||||||
|
|
||||||
PacketPlayerBaseInfo PacketPlayerCharGen PacketPlayerActiveSkills PacketPlayerAnimFlags PacketPlayerAnimPlay
|
PacketPlayerBaseInfo PacketPlayerCharGen PacketPlayerActiveSkills PacketPlayerAnimFlags PacketPlayerAnimPlay
|
||||||
PacketPlayerAttack PacketPlayerAttribute PacketPlayerBehavior PacketPlayerBook PacketPlayerBounty
|
PacketPlayerAttack PacketPlayerAttribute PacketPlayerBehavior PacketPlayerBook PacketPlayerBounty
|
||||||
|
@ -189,6 +191,7 @@ add_component_dir (openmw-mp/Packets/Player
|
||||||
|
|
||||||
add_component_dir (openmw-mp/Packets/Object
|
add_component_dir (openmw-mp/Packets/Object
|
||||||
ObjectPacket
|
ObjectPacket
|
||||||
|
|
||||||
PacketConsoleCommand PacketContainer PacketObjectAnimPlay PacketObjectAttach PacketObjectCollision
|
PacketConsoleCommand PacketContainer PacketObjectAnimPlay PacketObjectAttach PacketObjectCollision
|
||||||
PacketObjectDelete PacketDoorDestination PacketDoorState PacketObjectLock PacketObjectMove PacketObjectPlace
|
PacketObjectDelete PacketDoorDestination PacketDoorState PacketObjectLock PacketObjectMove PacketObjectPlace
|
||||||
PacketObjectReset PacketObjectRotate PacketObjectScale PacketObjectSpawn PacketObjectState PacketObjectTrap
|
PacketObjectReset PacketObjectRotate PacketObjectScale PacketObjectSpawn PacketObjectState PacketObjectTrap
|
||||||
|
@ -198,7 +201,8 @@ add_component_dir (openmw-mp/Packets/Object
|
||||||
|
|
||||||
add_component_dir (openmw-mp/Packets/Worldstate
|
add_component_dir (openmw-mp/Packets/Worldstate
|
||||||
WorldstatePacket
|
WorldstatePacket
|
||||||
PacketWorldTime
|
|
||||||
|
PacketRecordDynamic PacketWorldTime
|
||||||
)
|
)
|
||||||
|
|
||||||
add_component_dir (fallback
|
add_component_dir (fallback
|
||||||
|
|
|
@ -57,6 +57,7 @@ void ESMReader::close()
|
||||||
mCtx.subCached = false;
|
mCtx.subCached = false;
|
||||||
mCtx.recName.clear();
|
mCtx.recName.clear();
|
||||||
mCtx.subName.clear();
|
mCtx.subName.clear();
|
||||||
|
mHeader.blank();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESMReader::openRaw(Files::IStreamPtr _esm, const std::string& name)
|
void ESMReader::openRaw(Files::IStreamPtr _esm, const std::string& name)
|
||||||
|
|
|
@ -14,14 +14,23 @@ namespace mwmp
|
||||||
|
|
||||||
BaseWorldstate()
|
BaseWorldstate()
|
||||||
{
|
{
|
||||||
|
month = -1;
|
||||||
|
day = -1;
|
||||||
|
hour = -1;
|
||||||
|
|
||||||
|
daysPassed = -1;
|
||||||
|
timeScale = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
RakNet::RakNetGUID guid;
|
RakNet::RakNetGUID guid;
|
||||||
|
|
||||||
int month;
|
|
||||||
int day;
|
|
||||||
double hour;
|
double hour;
|
||||||
|
int day;
|
||||||
|
int month;
|
||||||
|
int year;
|
||||||
|
|
||||||
|
int daysPassed;
|
||||||
|
float timeScale;
|
||||||
|
|
||||||
bool isValid;
|
bool isValid;
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
#include "../Packets/Player/PacketGUIWindow.hpp"
|
#include "../Packets/Player/PacketGUIWindow.hpp"
|
||||||
#include "../Packets/Player/PacketLoaded.hpp"
|
#include "../Packets/Player/PacketLoaded.hpp"
|
||||||
#include "../Packets/Player/PacketCellCreate.hpp"
|
#include "../Packets/Player/PacketCellCreate.hpp"
|
||||||
#include "../Packets/Player/PacketRecordDynamic.hpp"
|
|
||||||
#include "../Packets/Player/PacketGameSettings.hpp"
|
#include "../Packets/Player/PacketGameSettings.hpp"
|
||||||
#include "../Packets/Player/PacketGameWeather.hpp"
|
#include "../Packets/Player/PacketGameWeather.hpp"
|
||||||
#include "../Packets/Player/PacketPlayerActiveSkills.hpp"
|
#include "../Packets/Player/PacketPlayerActiveSkills.hpp"
|
||||||
|
@ -57,7 +56,6 @@ mwmp::PlayerPacketController::PlayerPacketController(RakNet::RakPeerInterface *p
|
||||||
AddPacket<PacketGUIWindow>(&packets, peer);
|
AddPacket<PacketGUIWindow>(&packets, peer);
|
||||||
AddPacket<PacketLoaded>(&packets, peer);
|
AddPacket<PacketLoaded>(&packets, peer);
|
||||||
AddPacket<PacketCellCreate>(&packets, peer);
|
AddPacket<PacketCellCreate>(&packets, peer);
|
||||||
AddPacket<PacketRecordDynamic>(&packets, peer);
|
|
||||||
AddPacket<PacketGameSettings>(&packets, peer);
|
AddPacket<PacketGameSettings>(&packets, peer);
|
||||||
AddPacket<PacketGameWeather>(&packets, peer);
|
AddPacket<PacketGameWeather>(&packets, peer);
|
||||||
AddPacket<PacketPlayerActiveSkills>(&packets, peer);
|
AddPacket<PacketPlayerActiveSkills>(&packets, peer);
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
#include "../Packets/Worldstate/PacketRecordDynamic.hpp"
|
||||||
#include "../Packets/Worldstate/PacketWorldTime.hpp"
|
#include "../Packets/Worldstate/PacketWorldTime.hpp"
|
||||||
|
|
||||||
#include "WorldstatePacketController.hpp"
|
#include "WorldstatePacketController.hpp"
|
||||||
|
|
||||||
mwmp::WorldstatePacketController::WorldstatePacketController(RakNet::RakPeerInterface *peer)
|
mwmp::WorldstatePacketController::WorldstatePacketController(RakNet::RakPeerInterface *peer)
|
||||||
{
|
{
|
||||||
|
AddPacket<PacketRecordDynamic>(&packets, peer);
|
||||||
AddPacket<PacketWorldTime>(&packets, peer);
|
AddPacket<PacketWorldTime>(&packets, peer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
//
|
|
||||||
// Created by koncord on 23.07.16.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "PacketGUIBoxes.hpp"
|
#include "PacketGUIBoxes.hpp"
|
||||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||||
|
|
||||||
|
@ -25,7 +21,8 @@ void PacketGUIBoxes::Packet(RakNet::BitStream *bs, bool send)
|
||||||
|
|
||||||
if (player->guiMessageBox.type == BasePlayer::GUIMessageBox::Type::CustomMessageBox)
|
if (player->guiMessageBox.type == BasePlayer::GUIMessageBox::Type::CustomMessageBox)
|
||||||
RW(player->guiMessageBox.buttons, send);
|
RW(player->guiMessageBox.buttons, send);
|
||||||
else if (player->guiMessageBox.type == BasePlayer::GUIMessageBox::Type::PasswordDialog)
|
else if (player->guiMessageBox.type == BasePlayer::GUIMessageBox::Type::InputDialog ||
|
||||||
|
player->guiMessageBox.type == BasePlayer::GUIMessageBox::Type::PasswordDialog)
|
||||||
RW(player->guiMessageBox.note, send);
|
RW(player->guiMessageBox.note, send);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,15 @@
|
||||||
|
|
||||||
using namespace mwmp;
|
using namespace mwmp;
|
||||||
|
|
||||||
PacketRecordDynamic::PacketRecordDynamic(RakNet::RakPeerInterface *peer) : PlayerPacket(peer)
|
PacketRecordDynamic::PacketRecordDynamic(RakNet::RakPeerInterface *peer) : WorldstatePacket(peer)
|
||||||
{
|
{
|
||||||
packetID = ID_RECORD_DYNAMIC;
|
packetID = ID_RECORD_DYNAMIC;
|
||||||
orderChannel = CHANNEL_SYSTEM;
|
orderChannel = CHANNEL_WORLDSTATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PacketRecordDynamic::Packet(RakNet::BitStream *bs, bool send)
|
void PacketRecordDynamic::Packet(RakNet::BitStream *bs, bool send)
|
||||||
{
|
{
|
||||||
PlayerPacket::Packet(bs, send);
|
WorldstatePacket::Packet(bs, send);
|
||||||
|
|
||||||
// Placeholder
|
// Placeholder
|
||||||
}
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
#ifndef OPENMW_PACKETRECORDDYNAMIC_HPP
|
#ifndef OPENMW_PACKETRECORDDYNAMIC_HPP
|
||||||
#define OPENMW_PACKETRECORDDYNAMIC_HPP
|
#define OPENMW_PACKETRECORDDYNAMIC_HPP
|
||||||
|
|
||||||
#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>
|
#include <components/openmw-mp/Packets/Worldstate/WorldstatePacket.hpp>
|
||||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||||
|
|
||||||
namespace mwmp
|
namespace mwmp
|
||||||
{
|
{
|
||||||
class PacketRecordDynamic: public PlayerPacket
|
class PacketRecordDynamic: public WorldstatePacket
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PacketRecordDynamic(RakNet::RakPeerInterface *peer);
|
PacketRecordDynamic(RakNet::RakPeerInterface *peer);
|
|
@ -6,14 +6,18 @@ using namespace mwmp;
|
||||||
PacketWorldTime::PacketWorldTime(RakNet::RakPeerInterface *peer) : WorldstatePacket(peer)
|
PacketWorldTime::PacketWorldTime(RakNet::RakPeerInterface *peer) : WorldstatePacket(peer)
|
||||||
{
|
{
|
||||||
packetID = ID_WORLD_TIME;
|
packetID = ID_WORLD_TIME;
|
||||||
orderChannel = CHANNEL_SYSTEM;
|
orderChannel = CHANNEL_WORLDSTATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PacketWorldTime::Packet(RakNet::BitStream *bs, bool send)
|
void PacketWorldTime::Packet(RakNet::BitStream *bs, bool send)
|
||||||
{
|
{
|
||||||
WorldstatePacket::Packet(bs, send);
|
WorldstatePacket::Packet(bs, send);
|
||||||
|
|
||||||
RW(worldstate->month, send);
|
|
||||||
RW(worldstate->day, send);
|
|
||||||
RW(worldstate->hour, send);
|
RW(worldstate->hour, send);
|
||||||
|
RW(worldstate->day, send);
|
||||||
|
RW(worldstate->month, send);
|
||||||
|
RW(worldstate->year, send);
|
||||||
|
|
||||||
|
RW(worldstate->daysPassed, send);
|
||||||
|
RW(worldstate->timeScale, send);
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,7 +233,6 @@ QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resour
|
||||||
|
|
||||||
QuadTreeWorld::~QuadTreeWorld()
|
QuadTreeWorld::~QuadTreeWorld()
|
||||||
{
|
{
|
||||||
ensureQuadTreeBuilt();
|
|
||||||
mViewDataMap->clear();
|
mViewDataMap->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
293
docs/cs-manual/source/record-filters.rst
Normal file
293
docs/cs-manual/source/record-filters.rst
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
Record Filters
|
||||||
|
##############
|
||||||
|
|
||||||
|
Filters are a key element of the OpenMW CS user interface, they allow rapid and
|
||||||
|
easy access to records presented in all tables. In order to use this
|
||||||
|
application effectively you need to familiarise yourself with all the concepts
|
||||||
|
and instructions explained in this chapter. The filter system is somewhat
|
||||||
|
unusual at first glance, but once you understand the basics it will be fairly
|
||||||
|
intuitive and easy to use
|
||||||
|
|
||||||
|
Filters are a key element to using the OpenMW CS efficiently by allowing you to
|
||||||
|
narrow down the table entries very quickly and find what you are looking for.
|
||||||
|
The filter system might appear unusual at first, you don't just type in a word
|
||||||
|
and get all instances where it occurs, instead filters are first-class objects
|
||||||
|
in the CS with their own table. This allows you to define very specific filters
|
||||||
|
for your project and store them on disc to use in the next session. The CS
|
||||||
|
allows you fine-grained control, you can choose whether to make a filter
|
||||||
|
persistent between session, only for one session or use a one-off filter by
|
||||||
|
typing it directly into the filter field.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Terms used
|
||||||
|
**********
|
||||||
|
|
||||||
|
Filter
|
||||||
|
A Filter is generally speaking a tool able to filter the elements of a
|
||||||
|
table, that is select some elements while discarding others, according to
|
||||||
|
some criteria. These criteria are written using their own syntax.
|
||||||
|
|
||||||
|
Criterion
|
||||||
|
A criterion describes some condition a record needs to satisfy in order to
|
||||||
|
be selected. They are written using a special syntax which is explained
|
||||||
|
below. We can logically combine multiple criteria in a filter for finer
|
||||||
|
control.
|
||||||
|
|
||||||
|
Expression
|
||||||
|
Expressions are how we perform filtering. They look like functions in a
|
||||||
|
programming language: they have a name and accept a number of arguments.
|
||||||
|
The expression evaluates to either ``true`` or ``false`` for every record in
|
||||||
|
the table. The arguments are expressions themselves.
|
||||||
|
|
||||||
|
Arity
|
||||||
|
The arity of an expression tells us how many arguments it takes. Expressions
|
||||||
|
taking no arguments are called *nullary*, those taking one argument are
|
||||||
|
known as *unary* expressions and those taking two arguments are called
|
||||||
|
*binary*.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Interface
|
||||||
|
*********
|
||||||
|
|
||||||
|
Above each table there is a text field which is used to enter a filter: either
|
||||||
|
one predefined by the OpenMW CS developers or one made by you. Another
|
||||||
|
important element is the filter table found under *View* → *Filters*. You
|
||||||
|
should see the default filters made by the OpenMW team in the table. The table
|
||||||
|
has the columns *Filter*, *Description* and *Modified*.
|
||||||
|
|
||||||
|
ID
|
||||||
|
A unique name used to refer to this filter. Note that every ID has a
|
||||||
|
scope prefix, we will explain these soon.
|
||||||
|
|
||||||
|
Modified
|
||||||
|
This is the same as for all the other records, it tells us whether the
|
||||||
|
filter is *added* or *removed*. Filters are specific to a project instead of
|
||||||
|
a content file, they have no effect on the game itself.
|
||||||
|
|
||||||
|
Filter
|
||||||
|
The actual contents of the filter are given here using the filter syntax.
|
||||||
|
Change the expressions to modify what the filter returns.
|
||||||
|
|
||||||
|
Description
|
||||||
|
A textual description of what the filter does.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Using predefined filters
|
||||||
|
************************
|
||||||
|
|
||||||
|
To use a filter you have to type its ID into the filter field above a table.
|
||||||
|
|
||||||
|
For instance, try to opening the objects table (under the world menu) and type
|
||||||
|
into the filters field ``project::weapons``. As soon as you complete the text
|
||||||
|
the table will show only the weapons. The string ``project::weapons`` is the ID
|
||||||
|
of one of the predefined filters. This means that in order to use the filter
|
||||||
|
inside the table you type its name inside the filter field.
|
||||||
|
|
||||||
|
Filter IDs follow these general conventions:
|
||||||
|
|
||||||
|
- IDs of filters for a specific record type contain usually the name of a
|
||||||
|
specific group. For instance the ``project::weapons`` filter contains the
|
||||||
|
term ``weapons``. Plural form is always used.
|
||||||
|
|
||||||
|
- When filtering a specific subgroup the ID is prefixed with the name of the
|
||||||
|
more general filter. For instance ``project::weaponssilver`` will filter only
|
||||||
|
silver weapons and ``project::weaponsmagical`` will filter only magical
|
||||||
|
weapons.
|
||||||
|
|
||||||
|
- There are few exceptions from the above rule. For instance there are
|
||||||
|
``project::added``, ``project::removed``, ``project::modified`` and
|
||||||
|
``project::base``. You might except something more like
|
||||||
|
``project::statusadded`` but in this case requiring these extra characters
|
||||||
|
would not improve readability.
|
||||||
|
|
||||||
|
We strongly recommend you take a look at the filters table right now to see
|
||||||
|
what you can filter with the defaults. Try using the default filters first
|
||||||
|
before writing you own.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Writing your own filters
|
||||||
|
************************
|
||||||
|
|
||||||
|
As mentioned before, filters are just another type of record in the OpenMW CS.
|
||||||
|
To create a new filter you will have to add a new record to the *Filters* table
|
||||||
|
and set its properties to your liking. Filters are created by combining
|
||||||
|
existing filters into more complex ones.
|
||||||
|
|
||||||
|
|
||||||
|
Scopes
|
||||||
|
======
|
||||||
|
|
||||||
|
Every default filter has the prefix ``project``. This is a *scpoe*, a mechanism
|
||||||
|
that determines the lifetime of the filter. These are the supported scopes:
|
||||||
|
|
||||||
|
``project::``
|
||||||
|
Indicates that the filter is to be used throughout the project in multiple
|
||||||
|
sessions. You can restart the CS and the filter will still be there.
|
||||||
|
|
||||||
|
``session::``
|
||||||
|
Indicates that the filter is not stored between multiple sessions and once
|
||||||
|
you quit the OpenMW CS application the filter will be gone. Until then it
|
||||||
|
can be found inside the filters table.
|
||||||
|
|
||||||
|
Project-filters are stored in an internal project file, not final content file
|
||||||
|
meant for the player. Keep in mind when collaborating with other modders that
|
||||||
|
you need to share the same project file.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Writing expressions
|
||||||
|
===================
|
||||||
|
|
||||||
|
The syntax for expressions is as follows:
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
<name>
|
||||||
|
<name>(<arg1>)
|
||||||
|
<name>(<arg1>, <arg2>, ..., <argn>)
|
||||||
|
|
||||||
|
Where ``<name>`` is the name of the expression, such as ``string`` and the
|
||||||
|
``<arg>`` are expressions themselves. A nullary expression consists only of its
|
||||||
|
name. A unary expression contains its argument within a pair of parentheses
|
||||||
|
following the name. If there is more than one argument they are separated by
|
||||||
|
commas inside the parentheses.
|
||||||
|
|
||||||
|
An example of a binary expression is ``string("Record Type", weapon)``; the
|
||||||
|
name is ``string``, and it takes two arguments which are strings of string
|
||||||
|
type. The meaning of arguments depends on the expression itself. In this case
|
||||||
|
the first argument is the name of a record column and the second field is the
|
||||||
|
values we want to test it against.
|
||||||
|
|
||||||
|
Strings are sequences of characters and are case-insensitive. If a string
|
||||||
|
contains spaces it must be quoted, otherwise the quotes are optional and
|
||||||
|
ignored.
|
||||||
|
|
||||||
|
|
||||||
|
Constant Expressions
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
These expressions take no arguments and always return the same result.
|
||||||
|
|
||||||
|
``true``
|
||||||
|
Always evaluates to ``true``.
|
||||||
|
|
||||||
|
``false``
|
||||||
|
Always evaluates to ``false``.
|
||||||
|
|
||||||
|
|
||||||
|
Comparison Expressions
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
``string(<column>, <value>)``
|
||||||
|
The ``<value>`` is a regular expression pattern. The expressions evaluates
|
||||||
|
to ``true`` when the value of a record in ``<column>`` matches the pattern.
|
||||||
|
Since the majority of the columns contain string values, ``string`` is among
|
||||||
|
the most often used expressions. Examples:
|
||||||
|
|
||||||
|
``string("Record Type", "Weapon")``
|
||||||
|
Will evaluate to ``true`` for all records containing ``Weapon`` in the
|
||||||
|
*Record Type* column cell.
|
||||||
|
|
||||||
|
``string("Portable", "true")``
|
||||||
|
Will evaluate to ``true`` [#]_ for all records containing word ``true`` inside
|
||||||
|
*Portable* column cell.
|
||||||
|
|
||||||
|
.. [#] There is no Boolean (``true`` or ``false``) value in the OpenMW CS. You
|
||||||
|
should use a string for those.
|
||||||
|
|
||||||
|
|
||||||
|
``value(<value>, (<lower>, <upper>))``
|
||||||
|
Match a value type, such as a number, with a range of possible values. The
|
||||||
|
argument ``<value>`` is the string name of the value we want to compare, the
|
||||||
|
second argument is a pair of lower and upper bounds for the range interval.
|
||||||
|
|
||||||
|
One can use either parentheses ``()`` or brackets ``[]`` to surround the
|
||||||
|
pair. Brackets are inclusive and parentheses are exclusive. We can also mix
|
||||||
|
both styles:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
value("Weight", [20, 50))
|
||||||
|
|
||||||
|
This will match any objects with a weight greater or equal to 20 and
|
||||||
|
strictly less than 50.
|
||||||
|
|
||||||
|
|
||||||
|
Logical Expressions
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
``not <expression>``
|
||||||
|
Logically negates the result of an expression. If ``<expression>`` evaluates
|
||||||
|
to ``true`` the negation is ``false``, and if ``<expression>`` evaluates to
|
||||||
|
``false`` the negation is ``true``. Note that there are no parentheses
|
||||||
|
around the argument.
|
||||||
|
|
||||||
|
``or(<expr1>, <expr2>, ..., <exprN>)``
|
||||||
|
Logical disjunction, evaluates to ``true`` if at least one argument
|
||||||
|
evaluates to ``true`` as well, otherwise the expression evaluates to
|
||||||
|
``false``.
|
||||||
|
|
||||||
|
As an example assume we want to filter for both NPCs and creatures; the
|
||||||
|
expression for that use-case is
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
or(string("record type", "npc"), string("record type", "creature"))
|
||||||
|
|
||||||
|
In this particular case only one argument can evaluate to ``true``, but one
|
||||||
|
can write expressions where multiple arguments can be ``true`` at a time.
|
||||||
|
|
||||||
|
``or(<expr1>, <expr2>, ..., <exprN>)``
|
||||||
|
Logical conjunction, evaluates to ``true`` if and only if all arguments
|
||||||
|
evaluate to ``true`` as well, otherwise the expression evaluates to
|
||||||
|
``false``.
|
||||||
|
|
||||||
|
As an example assume we want to filter for weapons weighting less than a hundred
|
||||||
|
units The expression for that use-case is
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
and(string("record type", "weapon"), value("weight", (0, 100)))
|
||||||
|
|
||||||
|
|
||||||
|
Anonymous filters
|
||||||
|
=================
|
||||||
|
|
||||||
|
Creating a whole new filter when you only intend to use it once can be
|
||||||
|
cumbersome. For that reason the OpenMW CS supports *anonymous* filters which
|
||||||
|
can be typed directly into the filters field of a table. They are not stored
|
||||||
|
anywhere, when you clear the field the filter is gone forever.
|
||||||
|
|
||||||
|
In order to define an anonymous filter you type an exclamation mark as the
|
||||||
|
first character into the field followed by the filter definition (e.g.
|
||||||
|
``!string("Record Type", weapon)`` to filter only for weapons).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Creating and saving filters
|
||||||
|
***************************
|
||||||
|
|
||||||
|
Filters are managed the same way as other records: go to the filters table,
|
||||||
|
right click and select the option *Add Record* from the context menu. You are
|
||||||
|
given a choice between project- or session scope. Choose the scope from the
|
||||||
|
dropdown and type in your desired ID for the filter. A newly created filter
|
||||||
|
does nothing since it still lacks expressions. In order to add your queries you
|
||||||
|
have to edit the filter record.
|
||||||
|
|
||||||
|
|
||||||
|
Replacing the default filters set
|
||||||
|
=================================
|
||||||
|
|
||||||
|
OpenMW CS allows you to substitute the default filter set for the entire
|
||||||
|
application. This will affect the default filters for all content files that
|
||||||
|
have not been edited on this computer and user account.
|
||||||
|
|
||||||
|
Create a new content file, add the desired filters, remove the undesired ones
|
||||||
|
and save. Now rename the *project* file to ``defaultfilters`` and make sure the
|
||||||
|
``.omwaddon.project`` file extension is removed. This file will act as a
|
||||||
|
template for all new files from now on. If you wish to go back to the
|
||||||
|
old default set rename or remove this custom file.
|
62
docs/cs-manual/source/record-types.rst
Normal file
62
docs/cs-manual/source/record-types.rst
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
Record Types
|
||||||
|
############
|
||||||
|
|
||||||
|
A game world contains many items, such as chests, weapons and monsters. All
|
||||||
|
these items are merely instances of templates we call *Objects*. The OpenMW CS
|
||||||
|
*Objects* table contains information about each of these template objects, such
|
||||||
|
as its value and weight in the case of items, or an aggression level in the
|
||||||
|
case of NPCs.
|
||||||
|
|
||||||
|
The following is a list of all Record Types and what you can tell OpenMW CS
|
||||||
|
about each of them.
|
||||||
|
|
||||||
|
Activator
|
||||||
|
Activators can have a script attached to them. As long as the cell this
|
||||||
|
object is in is active the script will be run once per frame.
|
||||||
|
|
||||||
|
Potion
|
||||||
|
This is a potion which is not self-made. It has an Icon for your inventory,
|
||||||
|
weight, coin value, and an attribute called *Auto Calc* set to ``False``.
|
||||||
|
This means that the effects of this potion are pre-configured. This does not
|
||||||
|
happen when the player makes their own potion.
|
||||||
|
|
||||||
|
Apparatus
|
||||||
|
This is a tool to make potions. Again there’s an icon for your inventory as
|
||||||
|
well as a weight and a coin value. It also has a *Quality* value attached to
|
||||||
|
it: the higher the number, the better the effect on your potions will be.
|
||||||
|
The *Apparatus Type* describes if the item is a *Calcinator*, *Retort*,
|
||||||
|
*Alembic* or *Mortar & Pestle*.
|
||||||
|
|
||||||
|
Armor
|
||||||
|
This type of item adds *Enchantment Points* to the mix. Every piece of
|
||||||
|
clothing or armor has a "pool" of potential *Magicka* that gets unlocked
|
||||||
|
when the player enchants it. Strong enchantments consume more magicka from
|
||||||
|
this pool: the stronger the enchantment, the more *Enchantment Points* each
|
||||||
|
cast will take up. *Health* means the amount of hit points this piece of
|
||||||
|
armor has. If it sustains enough damage, the armor will be destroyed.
|
||||||
|
Finally, *Armor Value* tells the game how much points to add to the player
|
||||||
|
character’s *Armor Rating*.
|
||||||
|
|
||||||
|
Book
|
||||||
|
This includes scrolls and notes. For the game to make the distinction
|
||||||
|
between books and scrolls, an extra property, *Scroll*, has been added.
|
||||||
|
Under the *Skill* column a scroll or book can have an in-game skill listed.
|
||||||
|
Reading this item will raise the player’s level in that specific skill.
|
||||||
|
|
||||||
|
Clothing
|
||||||
|
These items work just like armors, but confer no protective properties.
|
||||||
|
Rather than *Armor Type*, these items have a *Clothing Type*.
|
||||||
|
|
||||||
|
Container
|
||||||
|
This is all the stuff that stores items, from chests to sacks to plants. Its
|
||||||
|
*Capacity* shows how much stuff you can put in the container. You can
|
||||||
|
compare it to the maximum allowed load a player character can carry. A
|
||||||
|
container, however, will just refuse to take the item in question when it
|
||||||
|
gets "over-encumbered". Organic Containers are containers such as plants.
|
||||||
|
Containers that respawn are not safe to store stuff in. After a certain
|
||||||
|
amount of time they will reset to their default contents, meaning that
|
||||||
|
everything in them is gone forever.
|
||||||
|
|
||||||
|
Creature
|
||||||
|
These can be monsters, animals and the like.
|
||||||
|
|
168
docs/cs-manual/source/tables.rst
Normal file
168
docs/cs-manual/source/tables.rst
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
Tables
|
||||||
|
######
|
||||||
|
|
||||||
|
If you have launched OpenMW CS already and played around with it for a bit, you
|
||||||
|
will have noticed that the interface is made entirely of tables. This does not
|
||||||
|
mean it works just like a spreadsheet application though, it would be more
|
||||||
|
accurate to think of databases instead. Due to the vast amounts of information
|
||||||
|
involved with Morrowind tables made the most sense. You have to be able to spot
|
||||||
|
information quickly and be able to change them on the fly.
|
||||||
|
|
||||||
|
|
||||||
|
Used Terms
|
||||||
|
**********
|
||||||
|
|
||||||
|
Record
|
||||||
|
An entry in OpenMW CS representing an item, location, sound, NPC or anything
|
||||||
|
else.
|
||||||
|
|
||||||
|
Instance, Object
|
||||||
|
When an item is placed in the world, it does not create a whole new record
|
||||||
|
each time, but an *instance* of the *object*.
|
||||||
|
|
||||||
|
For example, the game world might contain a lot of exquisite belts on
|
||||||
|
different NPCs and in many crates, but they all refer to one specific
|
||||||
|
instance: the Exquisite Belt record. In this case, all those belts in crates
|
||||||
|
and on NPCs are instances. The central Exquisite Belt instance is called an
|
||||||
|
*object*. This allows modders to make changes to all items of the same type
|
||||||
|
in one place.
|
||||||
|
|
||||||
|
If you wanted all exquisite belts to have 4000 enchantment points rather
|
||||||
|
than 400, you would only need to change the object Exquisite Belt rather
|
||||||
|
than all exquisite belt instances individually.
|
||||||
|
|
||||||
|
Some columns are recurring throughout OpenMW CS, they show up in (nearly) every
|
||||||
|
table.
|
||||||
|
|
||||||
|
ID
|
||||||
|
Each item, location, sound, etc. gets the same unique identifier in both
|
||||||
|
OpenMW CS and Morrowind. This is usually a very self-explanatory name. For
|
||||||
|
example, the ID for the (unique) black pants of Caius Cosades is
|
||||||
|
``Caius_pants``. This allows players to manipulate the game in many ways.
|
||||||
|
For example, they could add these pants to their inventory by opening the
|
||||||
|
console and entering: ``player- >addItem Caius_pants``. In both Morrowind
|
||||||
|
and OpenMW CS the ID is the primary way to identify all these different
|
||||||
|
parts of the game.
|
||||||
|
|
||||||
|
Modified
|
||||||
|
This column shows what has happened (if anything) to this record. There are
|
||||||
|
four possible states in which it can exist:
|
||||||
|
|
||||||
|
Base
|
||||||
|
The record is unmodified and from a content file other than the one
|
||||||
|
currently being edited.
|
||||||
|
|
||||||
|
Added
|
||||||
|
This record has been added in the currently content file.
|
||||||
|
|
||||||
|
Modified
|
||||||
|
Similar to *base*, but has been changed in some way.
|
||||||
|
|
||||||
|
Deleted
|
||||||
|
Similar to *base*, but has been removed as an entry. This does not mean,
|
||||||
|
however, that the occurrences in the game itself have been removed! For
|
||||||
|
example, if you were to remove the ``CharGen_Bed`` entry from
|
||||||
|
``morrowind.esm``, it does not mean the bedroll in the basement of the
|
||||||
|
Census and Excise Office in Seyda Neen will be gone. You will have to
|
||||||
|
delete that instance yourself or make sure that that object is replaced
|
||||||
|
by something that still exists otherwise the player will get crashes in
|
||||||
|
the worst case scenario.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
World Screens
|
||||||
|
*************
|
||||||
|
|
||||||
|
The contents of the game world can be changed by choosing one of the options in
|
||||||
|
the appropriate menu at the top of the screen.
|
||||||
|
|
||||||
|
|
||||||
|
Regions
|
||||||
|
=======
|
||||||
|
|
||||||
|
This describes the general areas of Vvardenfell. Each of these areas has
|
||||||
|
different rules about things such as encounters and weather.
|
||||||
|
|
||||||
|
Name
|
||||||
|
This is how the game will show the player's location in-game.
|
||||||
|
|
||||||
|
MapColour
|
||||||
|
This is a six-digit hexadecimal representation of the colour used to
|
||||||
|
identify the region on the map available in *World* → *Region Map*.
|
||||||
|
|
||||||
|
Sleep Encounter
|
||||||
|
These are the rules for what kinds of enemies the player might encounter
|
||||||
|
when sleeping outside in the wilderness.
|
||||||
|
|
||||||
|
|
||||||
|
Cells
|
||||||
|
=====
|
||||||
|
|
||||||
|
Expansive worlds such as Vvardenfell, with all its items, NPCs, etc. have a lot
|
||||||
|
going on simultaneously. But if the player is in Balmora, why would the
|
||||||
|
computer need to keep track the exact locations of NPCs walking through the
|
||||||
|
corridors in a Vivec canton? All that work would be quite useless and bring
|
||||||
|
the player's system down to its knees! So the world has been divided up into
|
||||||
|
squares we call *cells*. Once your character enters a cell, the game will load
|
||||||
|
everything that is going on in that cell so the player can interact with it.
|
||||||
|
|
||||||
|
In the original Morrowind this could be seen when a small loading bar would
|
||||||
|
appear near the bottom of the screen while travelling; the player had just
|
||||||
|
entered a new cell and the game had to load all the items and NPCs. The *Cells*
|
||||||
|
screen in OpenMW CS provides you with a list of cells in the game, both the
|
||||||
|
interior cells (houses, dungeons, mines, etc.) and the exterior cells (the
|
||||||
|
outside world).
|
||||||
|
|
||||||
|
Sleep Forbidden
|
||||||
|
Can the player sleep on the floor? In most cities it is forbidden to sleep
|
||||||
|
outside. Sleeping in the wilderness carries its own risks of attack, though,
|
||||||
|
and this entry lets you decide if a player should be allowed to sleep on the
|
||||||
|
floor in this cell or not.
|
||||||
|
|
||||||
|
Interior Water
|
||||||
|
Should water be rendered in this interior cell? The game world consists of
|
||||||
|
an endless ocean at height 0, then the landscape is added. If part of the
|
||||||
|
landscape goes below height 0, the player will see water.
|
||||||
|
|
||||||
|
Setting the cell’s Interior Water to true tells the game that this cell that
|
||||||
|
there needs to be water at height 0. This is useful for dungeons or mines
|
||||||
|
that have water in them.
|
||||||
|
|
||||||
|
Setting the cell’s Interior Water to ``false`` tells the game that the water
|
||||||
|
at height 0 should not be used. This flag is useless for outside cells.
|
||||||
|
|
||||||
|
Interior Sky
|
||||||
|
Should this interior cell have a sky? This is a rather unique case. The
|
||||||
|
Tribunal expansion took place in a city on the mainland. Normally this would
|
||||||
|
require the city to be composed of exterior cells so it has a sky, weather
|
||||||
|
and the like. But if the player is in an exterior cell and were to look at
|
||||||
|
their in-game map, they would see Vvardenfell with an overview of all
|
||||||
|
exterior cells. The player would have to see the city’s very own map, as if
|
||||||
|
they were walking around in an interior cell.
|
||||||
|
|
||||||
|
So the developers decided to create a workaround and take a bit of both: The
|
||||||
|
whole city would technically work exactly like an interior cell, but it
|
||||||
|
would need a sky as if it was an exterior cell. That is what this is. This
|
||||||
|
is why the vast majority of the cells you will find in this screen will have
|
||||||
|
this option set to false: It is only meant for these "fake exteriors".
|
||||||
|
|
||||||
|
Region
|
||||||
|
To which Region does this cell belong? This has an impact on the way the
|
||||||
|
game handles weather and encounters in this area. It is also possible for a
|
||||||
|
cell not to belong to any region.
|
||||||
|
|
||||||
|
|
||||||
|
Objects
|
||||||
|
=======
|
||||||
|
|
||||||
|
This is a library of all the items, triggers, containers, NPCs, etc. in the
|
||||||
|
game. There are several kinds of Record Types. Depending on which type a record
|
||||||
|
is, it will need specific information to function. For example, an NPC needs a
|
||||||
|
value attached to its aggression level. A chest, of course, does not. All
|
||||||
|
Record Types contain at least a 3D model or else the player would not see them.
|
||||||
|
Usually they also have a *Name*, which is what the players sees when they hover
|
||||||
|
their reticle over the object during the game.
|
||||||
|
|
||||||
|
Please refer to the Record Types chapter for an overview of what each type of
|
||||||
|
object does and what you can tell OpenMW CS about these objects.
|
||||||
|
|
|
@ -21,4 +21,6 @@ few chapters to familiarise yourself with the new interface.
|
||||||
tour
|
tour
|
||||||
files-and-directories
|
files-and-directories
|
||||||
starting-dialog
|
starting-dialog
|
||||||
|
tables
|
||||||
|
record-types
|
||||||
|
record-filters
|
||||||
|
|
Loading…
Reference in a new issue