diff --git a/apps/openmw-mp/NetActor.cpp b/apps/openmw-mp/NetActor.cpp index 2d6c2c434..c3c14c0f4 100644 --- a/apps/openmw-mp/NetActor.cpp +++ b/apps/openmw-mp/NetActor.cpp @@ -21,6 +21,7 @@ NetActor::NetActor() : inventory(this), cellAPI(this), isActorPlayer(false) void NetActor::resetUpdateFlags() { baseInfoChanged = false; + shapeshiftChanged = false; levelChanged = false; statsChanged = false; positionChanged = false; diff --git a/apps/openmw-mp/NetActor.hpp b/apps/openmw-mp/NetActor.hpp index 734d384e2..bdef84ac3 100644 --- a/apps/openmw-mp/NetActor.hpp +++ b/apps/openmw-mp/NetActor.hpp @@ -68,7 +68,7 @@ public: bool isPlayer() const { return isActorPlayer; } Player *toPlayer(); protected: - bool baseInfoChanged, levelChanged, statsChanged, positionChanged, attributesChanged, skillsChanged; + bool baseInfoChanged, shapeshiftChanged, levelChanged, statsChanged, positionChanged, attributesChanged, skillsChanged; mwmp::BasePlayer *basePlayer; mwmp::BaseNetCreature *netCreature; diff --git a/apps/openmw-mp/Networking.cpp b/apps/openmw-mp/Networking.cpp index febd582b3..234b35c31 100644 --- a/apps/openmw-mp/Networking.cpp +++ b/apps/openmw-mp/Networking.cpp @@ -106,8 +106,12 @@ void Networking::processPlayerPacket(RakNet::Packet *packet) if (!player->isHandshaked()) { + player->incrementHandshakeAttempts(); LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Have not completed handshake with player %d", player->getId()); - if (player->handshakeAttempts() > 5) + + LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Attempts so far: %i", player->getHandshakeAttempts()); + + if (player->getHandshakeAttempts() > 5) kickPlayer(player->guid); return; } diff --git a/apps/openmw-mp/Player.cpp b/apps/openmw-mp/Player.cpp index 6bd6cd815..dd62dddfa 100644 --- a/apps/openmw-mp/Player.cpp +++ b/apps/openmw-mp/Player.cpp @@ -60,11 +60,13 @@ void Player::Init(LuaState &lua) "head", sol::property(&Player::getHead, &Player::setHead), "hair", sol::property(&Player::getHair, &Player::setHair), "birthsign", sol::property(&Player::getBirthsign, &Player::setBirthsign), + "setResetStats", &Player::setResetStats, + "bounty", sol::property(&Player::getBounty, &Player::setBounty), "reputation", sol::property(&Player::getReputation, &Player::setReputation), "levelProgress", sol::property(&Player::getLevelProgress, &Player::setLevelProgress), - "creatureModel", sol::property(&Player::getCreatureModel, &Player::setCreatureModel), - "isCreatureName", sol::property(&Player::isCreatureName, &Player::creatureName), + "creatureRefId", sol::property(&Player::getCreatureRefId, &Player::setCreatureRefId), + "displayCreatureName", sol::property(&Player::getCreatureNameDisplayState, &Player::setCreatureNameDisplayState), "resurrect", &Player::resurrect, "jail", &Player::jail, @@ -182,6 +184,14 @@ void Player::update() packet->Send(false); } + if (shapeshiftChanged) + { + auto packet = plPCtrl->GetPacket(ID_PLAYER_SHAPESHIFT); + packet->setPlayer(basePlayer); + packet->Send(false); + packet->Send(true); + } + // The character class can override values from below on the client, so send it first cClass.update(); @@ -300,9 +310,14 @@ void Player::setHandshake() handshakeCounter = numeric_limits::max(); } -int Player::handshakeAttempts() +void Player::incrementHandshakeAttempts() +{ + handshakeCounter++; +} + +int Player::getHandshakeAttempts() { - return handshakeCounter++; + return handshakeCounter; } int Player::getLoadState() @@ -627,6 +642,12 @@ void Player::setBirthsign(const std::string &sign) baseInfoChanged = true; } +void Player::setResetStats(bool state) +{ + resetStats = state; + baseInfoChanged = true; +} + int Player::getBounty() const { return npcStats.mBounty; @@ -668,26 +689,26 @@ void Player::setLevelProgress(int progress) levelChanged = true; } -std::string Player::getCreatureModel() const +std::string Player::getCreatureRefId() const { - return creatureModel; + return creatureRefId; } -void Player::setCreatureModel(const std::string &model) +void Player::setCreatureRefId(const std::string &refId) { - creatureModel = model; - baseInfoChanged = true; + creatureRefId = refId; + shapeshiftChanged = true; } -void Player::creatureName(bool useName) +bool Player::getCreatureNameDisplayState() const { - useCreatureName = useName; - baseInfoChanged = true; + return displayCreatureName; } -bool Player::isCreatureName() const +void Player::setCreatureNameDisplayState(bool useName) { - return useCreatureName; + displayCreatureName = useName; + shapeshiftChanged = true; } std::tuple Player::getAttribute(unsigned short attributeId) const @@ -846,11 +867,7 @@ bool Player::getWerewolfState() const void Player::setWerewolfState(bool state) { isWerewolf = state; - - auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_SHAPESHIFT); - packet->setPlayer(this); - packet->Send(false); - packet->Send(true); + shapeshiftChanged = true; } float Player::getScale() const @@ -861,11 +878,7 @@ float Player::getScale() const void Player::setScale(float newScale) { scale = newScale; - - auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_SHAPESHIFT); - packet->setPlayer(this); - packet->Send(false); - packet->Send(true); + shapeshiftChanged = true; } void Player::setMark(float x, float y, float z, float xRot, float zRot, const std::string &cellDescription) diff --git a/apps/openmw-mp/Player.hpp b/apps/openmw-mp/Player.hpp index 040aafc90..03a17a3dd 100644 --- a/apps/openmw-mp/Player.hpp +++ b/apps/openmw-mp/Player.hpp @@ -58,7 +58,8 @@ public: void setId(unsigned short id); bool isHandshaked(); - int handshakeAttempts(); + int getHandshakeAttempts(); + void incrementHandshakeAttempts(); void setHandshake(); void setLoadState(int state); @@ -101,6 +102,7 @@ public: void setHair(const std::string &hair); std::string getBirthsign() const; void setBirthsign(const std::string &sign); + void setResetStats(bool state); int getBounty() const; void setBounty(int bounty); @@ -148,10 +150,10 @@ public: float getScale() const; void setScale(float newScale); - std::string getCreatureModel() const; - void setCreatureModel(const std::string &model); - void creatureName(bool useName); - bool isCreatureName() const; + std::string getCreatureRefId() const; + void setCreatureRefId(const std::string &model); + bool getCreatureNameDisplayState() const; + void setCreatureNameDisplayState(bool useName); std::string getIP() const; @@ -207,8 +209,8 @@ public: private: CellController::TContainer cells; - int handshakeCounter; int loadState; + int handshakeCounter; bool /*statsChanged, attributesChanged, skillsChanged, baseInfoChanged, positionChanged,*/ changedMarkLocation, changedSelectedSpell, changedMap; CharClass cClass; GameSettings settings; diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 7586bb66a..7b65eb771 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" @@ -72,7 +74,10 @@ namespace MWGui MWWorld::ActionTeleport::getFollowersToTeleport(player, followers); // Apply followers cost, in vanilla one follower travels for free - price *= std::max(1, static_cast(followers.size())); + if (Settings::Manager::getBool("charge for every follower travelling", "Game")) + price *= 1 + static_cast(followers.size()); + else + price *= std::max(1, static_cast(followers.size())); MyGUI::Button* toAdd = mDestinationsView->createWidget("SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); toAdd->setEnabled(price <= playerGold); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 0fb247469..0fb40ff0e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1502,7 +1502,20 @@ namespace MWMechanics // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back. // Note: accidental or collateral damage attacks are ignored. if (!target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) - startCombat(target, attacker); + { + // If an actor has OnPCHitMe declared in his script, his Fight = 0 and the attacker is player, + // he will attack the player only if we will force him (e.g. via StartCombat console command) + bool peaceful = false; + std::string script = target.getClass().getScript(target); + if (!script.empty() && target.getRefData().getLocals().hasVar(script, "onpchitme") && attacker == getPlayer()) + { + int fight = std::max(0, target.getClass().getCreatureStats(target).getAiSetting(CreatureStats::AI_Fight).getModified()); + peaceful = (fight == 0); + } + + if (!peaceful) + startCombat(target, attacker); + } } return true; diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index efe6d2491..b4f5545e9 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -214,7 +214,7 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, } } -void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress) +void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress, bool readBook) { int base = getSkill (skillIndex).getBase(); @@ -256,9 +256,14 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas MWBase::Environment::get().getWindowManager()->playSound("skillraise"); std::stringstream message; + + if (readBook) + message << std::string("#{sBookSkillMessage}\n"); + message << boost::format(MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", "")) % std::string("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}") % static_cast (base); + MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), MWGui::ShowInDialogueMode_Never); if (mLevelProgress >= gmst.find("iLevelUpTotal")->getInt()) diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 515772bb5..69e7df3bb 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -81,7 +81,7 @@ namespace MWMechanics void useSkill (int skillIndex, const ESM::Class& class_, int usageType = -1, float extraFactor=1.f); ///< Increase skill by usage. - void increaseSkill (int skillIndex, const ESM::Class& class_, bool preserveProgress); + void increaseSkill (int skillIndex, const ESM::Class& class_, bool preserveProgress, bool readBook = false); int getLevelProgress() const; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 0638a5c24..190776cb0 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -586,7 +586,7 @@ namespace MWMechanics ActiveSpells::ActiveEffect effect_ = effect; effect_.mMagnitude *= -1; absorbEffects.push_back(effect_); - if (reflected && Settings::Manager::getBool("classic reflected absorb attribute behavior", "Game")) + if (reflected && Settings::Manager::getBool("classic reflect absorb attribute behavior", "Game")) target.getClass().getCreatureStats(target).getActiveSpells().addSpell("", true, absorbEffects, mSourceName, caster.getClass().getCreatureStats(caster).getActorId()); else diff --git a/apps/openmw/mwmp/DedicatedPlayer.cpp b/apps/openmw/mwmp/DedicatedPlayer.cpp index a945ba148..6ef03b4e3 100644 --- a/apps/openmw/mwmp/DedicatedPlayer.cpp +++ b/apps/openmw/mwmp/DedicatedPlayer.cpp @@ -1,7 +1,3 @@ -// -// Created by koncord on 02.01.16. -// - #include #include #include @@ -50,6 +46,10 @@ DedicatedPlayer::DedicatedPlayer(RakNet::RakNetGUID guid) : BasePlayer(guid) creatureStats.mDead = false; movementFlags = 0; attack.instant = false; + + cell.blank(); + position.pos[0] = position.pos[1] = Main::get().getCellController()->getCellSize() / 2; + position.pos[2] = 0; } DedicatedPlayer::~DedicatedPlayer() { @@ -98,7 +98,7 @@ void DedicatedPlayer::update(float dt) void DedicatedPlayer::move(float dt) { - if (state != 2) return; + if (!reference) return; ESM::Position refPos = ptr.getRefData().getPosition(); MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -130,6 +130,61 @@ void DedicatedPlayer::move(float dt) MWMechanics::zTurn(ptr, position.rot[2], osg::DegreesToRadians(1.0)); } +void DedicatedPlayer::setBaseInfo() +{ + MWBase::World *world = MWBase::Environment::get().getWorld(); + + if (reference) + { + deleteReference(); + } + + std::string recId = getNpcRecordId(); + createReference(recId); + + // Give this new character a temporary high fatigue of at least 1 so it doesn't + // spawn on the ground + creatureStats.mDynamic[2].mBase = 1000; +} + +void DedicatedPlayer::setShapeshift() +{ + MWBase::World *world = MWBase::Environment::get().getWorld(); + + if (reference) + { + deleteReference(); + } + + std::string recId; + + if (!creatureRefId.empty()) + { + const ESM::Creature *tmpCreature = world->getStore().get().search(creatureRefId); + if (tmpCreature != 0) + { + recId = getCreatureRecordId(); + } + } + + if (recId.empty()) + { + recId = getNpcRecordId(); + } + + createReference(recId); + + if (ptr.getTypeName() == typeid(ESM::NPC).name()) + { + MWBase::Environment::get().getMechanicsManager()->setWerewolf(ptr, isWerewolf); + + if (!isWerewolf) + setEquipment(); + } + + MWBase::Environment::get().getWorld()->scaleObject(ptr, scale); +} + void DedicatedPlayer::setAnimFlags() { using namespace MWMechanics; @@ -203,9 +258,8 @@ void DedicatedPlayer::setEquipment() void DedicatedPlayer::setCell() { - // Prevent cell update when player hasn't been instantiated yet - if (state == 0) - return; + // Prevent cell update when reference doesn't exist + if (!reference) return; MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -240,12 +294,6 @@ void DedicatedPlayer::setCell() Main::get().getCellController()->getCell(cell)->updateLocal(true); } -void DedicatedPlayer::setShapeshift() -{ - MWBase::Environment::get().getWorld()->scaleObject(ptr, scale); - MWBase::Environment::get().getMechanicsManager()->setWerewolf(ptr, isWerewolf); -} - void DedicatedPlayer::updateMarker() { if (!markerEnabled) @@ -301,14 +349,82 @@ void DedicatedPlayer::playSpeech() winMgr->messageBox(MWBase::Environment::get().getDialogueManager()->getVoiceCaption(sound), MWGui::ShowInDialogueMode_Never); } -MWWorld::Ptr DedicatedPlayer::getPtr() +std::string DedicatedPlayer::getNpcRecordId() { - return ptr; + MWBase::World *world = MWBase::Environment::get().getWorld(); + + MWWorld::Ptr player = world->getPlayerPtr(); + + ESM::NPC newNpc = *player.get()->mBase; + + // To avoid freezes caused by invalid races, only set race if we find it + // on our client + if (world->getStore().get().search(npc.mRace) != 0) + newNpc.mRace = npc.mRace; + + newNpc.mHead = npc.mHead; + newNpc.mHair = npc.mHair; + newNpc.mClass = npc.mClass; + newNpc.mName = npc.mName; + newNpc.mFlags = npc.mFlags; + + LOG_APPEND(Log::LOG_INFO, "- Creating new NPC record"); + newNpc.mId = "Dedicated Player"; + std::string recId = world->createRecord(newNpc)->mId; + + return recId; } -MWWorld::Ptr DedicatedPlayer::getLiveCellPtr() +std::string DedicatedPlayer::getCreatureRecordId() { - return reference->getPtr(); + MWBase::World *world = MWBase::Environment::get().getWorld(); + + ESM::Creature creature; + + const ESM::Creature *tmpCreature = world->getStore().get().search(creatureRefId); + + creature = *tmpCreature; + creature.mScript = ""; + if (!displayCreatureName) + creature.mName = npc.mName; + LOG_APPEND(Log::LOG_INFO, "Player %s looks like %s", npc.mName.c_str(), creatureRefId.c_str()); + + LOG_APPEND(Log::LOG_INFO, "- Creating new NPC record"); + creature.mId = "Dedicated Player"; + + std::string recId = world->createRecord(creature)->mId; + + return recId; +} + +void DedicatedPlayer::createReference(const std::string& recId) +{ + MWBase::World *world = MWBase::Environment::get().getWorld(); + + reference = new MWWorld::ManualRef(world->getStore(), recId, 1); + + LOG_APPEND(Log::LOG_INFO, "- Creating new reference pointer for %s", npc.mName.c_str()); + + ptr = world->placeObject(reference->getPtr(), Main::get().getCellController()->getCellStore(cell), position); + + ESM::CustomMarker mEditingMarker = Main::get().getGUIController()->createMarker(guid); + marker = mEditingMarker; + setMarkerState(true); +} + +void DedicatedPlayer::deleteReference() +{ + MWBase::World *world = MWBase::Environment::get().getWorld(); + + LOG_APPEND(Log::LOG_INFO, "- Deleting reference"); + world->deleteObject(ptr); + delete reference; + reference = nullptr; +} + +MWWorld::Ptr DedicatedPlayer::getPtr() +{ + return ptr; } MWWorld::ManualRef *DedicatedPlayer::getRef() diff --git a/apps/openmw/mwmp/DedicatedPlayer.hpp b/apps/openmw/mwmp/DedicatedPlayer.hpp index f2c793f2a..4a17ffa36 100644 --- a/apps/openmw/mwmp/DedicatedPlayer.hpp +++ b/apps/openmw/mwmp/DedicatedPlayer.hpp @@ -6,9 +6,12 @@ #define OPENMW_DEDICATEDPLAYER_HPP #include +#include #include #include +#include "../mwclass/npc.hpp" + #include "../mwmechanics/aisequence.hpp" #include "../mwworld/manualref.hpp" @@ -32,10 +35,11 @@ namespace mwmp void update(float dt); void move(float dt); + void setBaseInfo(); + void setShapeshift(); void setAnimFlags(); void setEquipment(); void setCell(); - void setShapeshift(); void updateMarker(); void removeMarker(); @@ -44,8 +48,13 @@ namespace mwmp void playAnimation(); void playSpeech(); + std::string getNpcRecordId(); + std::string getCreatureRecordId(); + + void createReference(const std::string& recId); + void deleteReference(); + MWWorld::Ptr getPtr(); - MWWorld::Ptr getLiveCellPtr(); MWWorld::ManualRef* getRef(); void setPtr(const MWWorld::Ptr& newPtr); @@ -55,7 +64,6 @@ namespace mwmp DedicatedPlayer(RakNet::RakNetGUID guid); virtual ~DedicatedPlayer(); - int state; MWWorld::ManualRef* reference; MWWorld::Ptr ptr; diff --git a/apps/openmw/mwmp/GUIController.cpp b/apps/openmw/mwmp/GUIController.cpp index a42a4e38e..f8fb59d82 100644 --- a/apps/openmw/mwmp/GUIController.cpp +++ b/apps/openmw/mwmp/GUIController.cpp @@ -334,7 +334,7 @@ ESM::CustomMarker mwmp::GUIController::createMarker(const RakNet::RakNetGUID &gu ESM::CustomMarker mEditingMarker; if (!player) { - LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Unknown player guid: %lu", guid.g); + LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Unknown player guid: %s", guid.ToString()); return mEditingMarker; } diff --git a/apps/openmw/mwmp/LocalPlayer.cpp b/apps/openmw/mwmp/LocalPlayer.cpp index 3537b8bb2..2bba7b842 100644 --- a/apps/openmw/mwmp/LocalPlayer.cpp +++ b/apps/openmw/mwmp/LocalPlayer.cpp @@ -431,13 +431,6 @@ void LocalPlayer::updateCell(bool forceUpdate) } } -void LocalPlayer::updateChar() -{ - MWBase::Environment::get().getMechanicsManager()->setPlayerRace(npc.mRace, npc.isMale(), npc.mHead, npc.mHair); - MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(birthsign); - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->rebuildAvatar(); -} - void LocalPlayer::updateEquipment(bool forceUpdate) { MWWorld::Ptr ptrPlayer = getPlayerPtr(); @@ -829,6 +822,40 @@ void LocalPlayer::closeInventoryWindows() MWBase::Environment::get().getWindowManager()->finishDragDrop(); } +void LocalPlayer::setCharacter() +{ + MWBase::World *world = MWBase::Environment::get().getWorld(); + + // Ignore invalid races + if (world->getStore().get().search(npc.mRace) != 0) + { + MWBase::Environment::get().getWorld()->getPlayer().setBirthSign(birthsign); + + if (resetStats) + MWBase::Environment::get().getMechanicsManager()->setPlayerRace(npc.mRace, npc.isMale(), npc.mHead, npc.mHair); + else + { + ESM::NPC player = *world->getPlayerPtr().get()->mBase; + + player.mRace = npc.mRace; + player.mHead = npc.mHead; + player.mHair = npc.mHair; + player.setIsMale(npc.isMale()); + world->createRecord(player); + + MWBase::Environment::get().getMechanicsManager()->playerLoaded(); + } + + setEquipment(); + + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->rebuildAvatar(); + } + else + { + LOG_APPEND(Log::LOG_INFO, "- Character update was ignored due to invalid race %s", npc.mRace.c_str()); + } +} + void LocalPlayer::setDynamicStats() { MWBase::World *world = MWBase::Environment::get().getWorld(); diff --git a/apps/openmw/mwmp/LocalPlayer.hpp b/apps/openmw/mwmp/LocalPlayer.hpp index 57b8b2440..7a78331b2 100644 --- a/apps/openmw/mwmp/LocalPlayer.hpp +++ b/apps/openmw/mwmp/LocalPlayer.hpp @@ -32,7 +32,6 @@ namespace mwmp void updateReputation(bool forceUpdate = false); void updatePosition(bool forceUpdate = false); void updateCell(bool forceUpdate = false); - void updateChar(); void updateEquipment(bool forceUpdate = false); void updateInventory(bool forceUpdate = false); void updateAttack(); @@ -51,6 +50,7 @@ namespace mwmp void closeInventoryWindows(); + void setCharacter(); void setDynamicStats(); void setAttributes(); void setSkills(); diff --git a/apps/openmw/mwmp/PlayerList.cpp b/apps/openmw/mwmp/PlayerList.cpp index 8c636a2de..24e392511 100644 --- a/apps/openmw/mwmp/PlayerList.cpp +++ b/apps/openmw/mwmp/PlayerList.cpp @@ -34,199 +34,24 @@ void PlayerList::update(float dt) } } -void PlayerList::createPlayer(RakNet::RakNetGUID guid) -{ - LOG_APPEND(Log::LOG_INFO, "- Setting up character info"); - - MWBase::World *world = MWBase::Environment::get().getWorld(); - - DedicatedPlayer *dedicPlayer = players[guid]; - - ESM::Creature creature; - ESM::NPC npc; - if (!dedicPlayer->creatureModel.empty()) - { - const ESM::Creature *tmpCreature = world->getStore().get().search(dedicPlayer->creatureModel); - if (tmpCreature == 0) - { - dedicPlayer->creatureModel = ""; - createPlayer(guid); - return; - } - creature = *tmpCreature; - creature.mScript = ""; - if (!dedicPlayer->useCreatureName) - creature.mName = dedicPlayer->npc.mName; - LOG_APPEND(Log::LOG_INFO, "Player %s looks like %s", dedicPlayer->npc.mName.c_str(), - dedicPlayer->creatureModel.c_str()); - } - else - { - MWWorld::Ptr player = world->getPlayerPtr(); - - npc = *player.get()->mBase; - - // To avoid freezes caused by invalid races, only set race if we find it - // on our client - if (world->getStore().get().search(dedicPlayer->npc.mRace) != 0) - npc.mRace = dedicPlayer->npc.mRace; - - npc.mHead = dedicPlayer->npc.mHead; - npc.mHair = dedicPlayer->npc.mHair; - npc.mClass = dedicPlayer->npc.mClass; - npc.mName = dedicPlayer->npc.mName; - npc.mFlags = dedicPlayer->npc.mFlags; - } - - bool reset = false; - if (dedicPlayer->reference) - { - bool isNPC = dedicPlayer->reference->getPtr().getTypeName() == typeid(ESM::NPC).name(); - if ((!dedicPlayer->creatureModel.empty() && isNPC) || - (dedicPlayer->creatureModel.empty() && !isNPC)) - { - if (dedicPlayer->reference) - { - LOG_APPEND(Log::LOG_INFO, "- Deleting old reference"); - dedicPlayer->state = 0; - world->deleteObject(dedicPlayer->ptr); - delete dedicPlayer->reference; - dedicPlayer->reference = nullptr; - reset = true; - } - } - } - - // Temporarily spawn or move player to the center of exterior 0,0 whenever setting base info - ESM::Position spawnPos; - spawnPos.pos[0] = spawnPos.pos[1] = Main::get().getCellController()->getCellSize() / 2; - spawnPos.pos[2] = 0; - MWWorld::CellStore *cellStore = world->getExterior(0, 0); - - if (dedicPlayer->state == 0) - { - string recid; - if (dedicPlayer->creatureModel.empty()) - { - LOG_APPEND(Log::LOG_INFO, "- Creating new NPC record"); - npc.mId = "Dedicated Player"; - recid = world->createRecord(npc)->mId; - } - else - { - LOG_APPEND(Log::LOG_INFO, "- Creating new Creature record"); - creature.mId = "Dedicated Player"; - recid = world->createRecord(creature)->mId; - } - - dedicPlayer->reference = new MWWorld::ManualRef(world->getStore(), recid, 1); - - LOG_APPEND(Log::LOG_INFO, "- Creating new reference pointer for %s", dedicPlayer->npc.mName.c_str()); - - MWWorld::Ptr tmp; - - if (reset) - { - if (dedicPlayer->cell.isExterior()) - cellStore = world->getExterior(dedicPlayer->cell.mData.mX, dedicPlayer->cell.mData.mY); - else - cellStore = world->getInterior(dedicPlayer->cell.mName); - - spawnPos = dedicPlayer->position; - } - - tmp = world->placeObject(dedicPlayer->reference->getPtr(), cellStore, spawnPos); - - dedicPlayer->ptr.mCell = tmp.mCell; - dedicPlayer->ptr.mRef = tmp.mRef; - - if (!reset) - { - dedicPlayer->cell = *dedicPlayer->ptr.getCell()->getCell(); - dedicPlayer->position = dedicPlayer->ptr.getRefData().getPosition(); - } - - ESM::CustomMarker mEditingMarker = Main::get().getGUIController()->createMarker(guid); - dedicPlayer->marker = mEditingMarker; - dedicPlayer->setMarkerState(true); - } - else - { - LOG_APPEND(Log::LOG_INFO, "- Updating reference pointer for %s", dedicPlayer->npc.mName.c_str()); - - MWWorld::ESMStore *store = const_cast(&world->getStore()); - MWWorld::Store *creature_store = const_cast *> (&store->get()); - MWWorld::Store *npc_store = const_cast *> (&store->get()); - - if (!dedicPlayer->creatureModel.empty()) - { - if (!npc.mId.empty() || npc.mId != "Dedicated Player") - { - LOG_APPEND(Log::LOG_INFO, "- Deleting NPC record"); - npc_store->erase(npc.mId); - npc.mId.clear(); - } - creature.mId = players[guid]->ptr.get()->mBase->mId; - creature_store->insert(creature); - } - else - { - if (!creature.mId.empty() || creature.mId != "Dedicated Player") - { - LOG_APPEND(Log::LOG_INFO, "- Deleting Creature record"); - creature_store->erase(creature.mId); - creature.mId.clear(); - } - npc.mId = players[guid]->ptr.get()->mBase->mId; - npc_store->insert(npc); - } - - // Disable Ptr to avoid graphical glitches caused by race changes - world->disable(players[guid]->ptr); - - dedicPlayer->setPtr(world->moveObject(dedicPlayer->ptr, cellStore, spawnPos.pos[0], spawnPos.pos[1], spawnPos.pos[2])); - dedicPlayer->setCell(); - } - - dedicPlayer->guid = guid; - dedicPlayer->state = 2; - - // Give this new character a fatigue of at least 1 so it doesn't spawn - // on the ground - dedicPlayer->creatureStats.mDynamic[2].mBase = 1; - - world->enable(players[guid]->ptr); -} - DedicatedPlayer *PlayerList::newPlayer(RakNet::RakNetGUID guid) { - LOG_APPEND(Log::LOG_INFO, "- Creating new DedicatedPlayer with guid %lu", guid.g); + LOG_APPEND(Log::LOG_INFO, "- Creating new DedicatedPlayer with guid %s", guid.ToString()); players[guid] = new DedicatedPlayer(guid); - players[guid]->state = 0; + + LOG_APPEND(Log::LOG_INFO, "- There are now %i DedicatedPlayers", players.size()); + return players[guid]; } -void PlayerList::disconnectPlayer(RakNet::RakNetGUID guid) +void PlayerList::deletePlayer(RakNet::RakNetGUID guid) { - if (players[guid]->state > 1) - { - players[guid]->state = 1; - - // Remove player's marker - players[guid]->setMarkerState(false); - - MWBase::World *world = MWBase::Environment::get().getWorld(); - world->disable(players[guid]->getPtr()); + if (players[guid]->reference) + players[guid]->deleteReference(); - // Move player to exterior 0,0 - ESM::Position newPos; - newPos.pos[0] = newPos.pos[1] = Main::get().getCellController()->getCellSize() / 2; - newPos.pos[2] = 0; - MWWorld::CellStore *cellStore = world->getExterior(0, 0); - - world->moveObject(players[guid]->getPtr(), cellStore, newPos.pos[0], newPos.pos[1], newPos.pos[2]); - } + delete players[guid]; + players.erase(guid); } void PlayerList::cleanUp() diff --git a/apps/openmw/mwmp/PlayerList.hpp b/apps/openmw/mwmp/PlayerList.hpp index b4f10c4c1..468c8a443 100644 --- a/apps/openmw/mwmp/PlayerList.hpp +++ b/apps/openmw/mwmp/PlayerList.hpp @@ -27,10 +27,9 @@ namespace mwmp static void update(float dt); - static void createPlayer(RakNet::RakNetGUID guid); static DedicatedPlayer *newPlayer(RakNet::RakNetGUID guid); - static void disconnectPlayer(RakNet::RakNetGUID guid); + static void deletePlayer(RakNet::RakNetGUID guid); static void cleanUp(); static DedicatedPlayer *getPlayer(RakNet::RakNetGUID guid); diff --git a/apps/openmw/mwmp/processors/player/ProcessorPlayerBaseInfo.hpp b/apps/openmw/mwmp/processors/player/ProcessorPlayerBaseInfo.hpp index 088b61e37..d572368f9 100644 --- a/apps/openmw/mwmp/processors/player/ProcessorPlayerBaseInfo.hpp +++ b/apps/openmw/mwmp/processors/player/ProcessorPlayerBaseInfo.hpp @@ -32,8 +32,8 @@ namespace mwmp } else { - LOG_APPEND(Log::LOG_INFO, "- Updating LocalPlayer"); - static_cast(player)->updateChar(); + LOG_APPEND(Log::LOG_INFO, "- Setting character for LocalPlayer"); + static_cast(player)->setCharacter(); } } else @@ -49,7 +49,7 @@ namespace mwmp packet.Read(); } - PlayerList::createPlayer(guid); + static_cast(player)->setBaseInfo(); } } }; diff --git a/apps/openmw/mwmp/processors/player/ProcessorUserDisconnected.hpp b/apps/openmw/mwmp/processors/player/ProcessorUserDisconnected.hpp index 2066cc7b2..393f9f6cb 100644 --- a/apps/openmw/mwmp/processors/player/ProcessorUserDisconnected.hpp +++ b/apps/openmw/mwmp/processors/player/ProcessorUserDisconnected.hpp @@ -26,7 +26,7 @@ namespace mwmp if (isLocal()) MWBase::Environment::get().getStateManager()->requestQuit(); else if (player != 0) - PlayerList::disconnectPlayer(guid); + PlayerList::deletePlayer(guid); } }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 3281a787b..6f0ddba3a 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -51,6 +51,16 @@ #include "terrainstorage.hpp" #include "util.hpp" +namespace +{ + float DLLandFogStart; + float DLLandFogEnd; + float DLUnderwaterFogStart; + float DLUnderwaterFogEnd; + float DLInteriorFogStart; + float DLInteriorFogEnd; +} + namespace MWRender { @@ -173,14 +183,18 @@ namespace MWRender , mResourceSystem(resourceSystem) , mWorkQueue(workQueue) , mUnrefQueue(new SceneUtil::UnrefQueue) - , mFogDepth(0.f) + , mLandFogStart(0.f) + , mLandFogEnd(std::numeric_limits::max()) + , mUnderwaterFogStart(0.f) + , mUnderwaterFogEnd(std::numeric_limits::max()) , mUnderwaterColor(fallback->getFallbackColour("Water_UnderwaterColor")) , mUnderwaterWeight(fallback->getFallbackFloat("Water_UnderwaterColorWeight")) - , mUnderwaterFog(0.f) , mUnderwaterIndoorFog(fallback->getFallbackFloat("Water_UnderwaterIndoorFog")) , mNightEyeFactor(0.f) - , mFieldOfViewOverride(0.f) + , mDistantFog(false) + , mDistantTerrain(false) , mFieldOfViewOverridden(false) + , mFieldOfViewOverride(0.f) { resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem); resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders"); @@ -216,12 +230,20 @@ namespace MWRender mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback, resourcePath)); - const bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain"); + DLLandFogStart = Settings::Manager::getFloat("distant land fog start", "Fog"); + DLLandFogEnd = Settings::Manager::getFloat("distant land fog end", "Fog"); + DLUnderwaterFogStart = Settings::Manager::getFloat("distant underwater fog start", "Fog"); + DLUnderwaterFogEnd = Settings::Manager::getFloat("distant underwater fog end", "Fog"); + DLInteriorFogStart = Settings::Manager::getFloat("distant interior fog start", "Fog"); + DLInteriorFogEnd = Settings::Manager::getFloat("distant interior fog end", "Fog"); + + mDistantFog = Settings::Manager::getBool("use distant fog", "Fog"); + mDistantTerrain = Settings::Manager::getBool("distant terrain", "Terrain"); mTerrainStorage = new TerrainStorage(mResourceSystem, Settings::Manager::getString("normal map pattern", "Shaders"), Settings::Manager::getString("normal height map pattern", "Shaders"), - Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), Settings::Manager::getString("terrain specular map pattern", "Shaders"), + Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), Settings::Manager::getString("terrain specular map pattern", "Shaders"), Settings::Manager::getBool("auto use terrain specular maps", "Shaders")); - if (distantTerrain) + if (mDistantTerrain) mTerrain.reset(new Terrain::QuadTreeWorld(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile)); else mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile)); @@ -479,14 +501,44 @@ namespace MWRender { osg::Vec4f color = SceneUtil::colourFromRGB(cell->mAmbi.mFog); - configureFog (cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, color); + if(mDistantFog) + { + float density = std::max(0.2f, cell->mAmbi.mFogDensity); + mLandFogStart = (DLInteriorFogEnd*(1.0f-density) + DLInteriorFogStart*density); + mLandFogEnd = DLInteriorFogEnd; + mUnderwaterFogStart = DLUnderwaterFogStart; + mUnderwaterFogEnd = DLUnderwaterFogEnd; + mFogColor = color; + } + else + configureFog(cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, 1.0f, 0.0f, color); } - void RenderingManager::configureFog(float fogDepth, float underwaterFog, const osg::Vec4f &color) + void RenderingManager::configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color) { - mFogDepth = fogDepth; + if(mDistantFog) + { + mLandFogStart = dlFactor * (DLLandFogStart - dlOffset*DLLandFogEnd); + mLandFogEnd = dlFactor * (1.0f-dlOffset) * DLLandFogEnd; + mUnderwaterFogStart = DLUnderwaterFogStart; + mUnderwaterFogEnd = DLUnderwaterFogEnd; + } + else + { + if(fogDepth == 0.0) + { + mLandFogStart = 0.0f; + mLandFogEnd = std::numeric_limits::max(); + } + else + { + mLandFogStart = mViewDistance * (1 - fogDepth); + mLandFogEnd = mViewDistance; + } + mUnderwaterFogStart = mViewDistance * (1 - underwaterFog); + mUnderwaterFogEnd = mViewDistance; + } mFogColor = color; - mUnderwaterFog = underwaterFog; } SkyManager* RenderingManager::getSkyManager() @@ -517,23 +569,15 @@ namespace MWRender float viewDistance = mViewDistance; viewDistance = std::min(viewDistance, 6666.f); setFogColor(mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight)); - mStateUpdater->setFogStart(viewDistance * (1 - mUnderwaterFog)); - mStateUpdater->setFogEnd(viewDistance); + mStateUpdater->setFogStart(mUnderwaterFogStart); + mStateUpdater->setFogEnd(mUnderwaterFogEnd); } else { setFogColor(mFogColor); - if (mFogDepth == 0.f) - { - mStateUpdater->setFogStart(0.f); - mStateUpdater->setFogEnd(std::numeric_limits::max()); - } - else - { - mStateUpdater->setFogStart(mViewDistance * (1 - mFogDepth)); - mStateUpdater->setFogEnd(mViewDistance); - } + mStateUpdater->setFogStart(mLandFogStart); + mStateUpdater->setFogEnd(mLandFogEnd); } } @@ -960,7 +1004,8 @@ namespace MWRender else if (it->first == "Camera" && it->second == "viewing distance") { mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); - mStateUpdater->setFogEnd(mViewDistance); + if(!mDistantFog) + mStateUpdater->setFogEnd(mViewDistance); updateProjectionMatrix(); } else if (it->first == "General" && (it->second == "texture filter" || diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index f0087e43d..1c689d29f 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -106,7 +106,7 @@ namespace MWRender void configureAmbient(const ESM::Cell* cell); void configureFog(const ESM::Cell* cell); - void configureFog(float fogDepth, float underwaterFog, const osg::Vec4f& colour); + void configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f& colour); void addCell(const MWWorld::CellStore* store); void removeCell(const MWWorld::CellStore* store); @@ -241,10 +241,12 @@ namespace MWRender osg::ref_ptr mStateUpdater; - float mFogDepth; + float mLandFogStart; + float mLandFogEnd; + float mUnderwaterFogStart; + float mUnderwaterFogEnd; osg::Vec4f mUnderwaterColor; float mUnderwaterWeight; - float mUnderwaterFog; float mUnderwaterIndoorFog; osg::Vec4f mFogColor; @@ -253,8 +255,10 @@ namespace MWRender float mNearClip; float mViewDistance; + bool mDistantFog : 1; + bool mDistantTerrain : 1; + bool mFieldOfViewOverridden : 1; float mFieldOfViewOverride; - bool mFieldOfViewOverridden; float mFieldOfView; float mFirstPersonFieldOfView; diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 4357d468c..a9345cdb4 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -65,6 +65,9 @@ namespace MWRender float mFogDepth; + float mDLFogFactor; + float mDLFogOffset; + float mWindSpeed; float mCloudSpeed; diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index 8e45f14e8..d81ad83f1 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -63,7 +63,7 @@ namespace MWWorld playerRef->mBase->mClass ); - npcStats.increaseSkill (ref->mBase->mData.mSkillId, *class_, true); + npcStats.increaseSkill (ref->mBase->mData.mSkillId, *class_, true, true); npcStats.flagAsUsed (ref->mBase->mId); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index cf6cb1df8..c958572ca 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -102,6 +102,8 @@ Weather::Weather(const std::string& name, const Fallback::Map& fallback, float stormWindSpeed, float rainSpeed, + float dlFactor, + float dlOffset, const std::string& particleEffect) : mCloudTexture(fallback.getFallbackString("Weather_" + name + "_Cloud_Texture")) , mSkyColor(fallback.getFallbackColour("Weather_" + name +"_Sky_Sunrise_Color"), @@ -141,6 +143,8 @@ Weather::Weather(const std::string& name, , mFlashDecrement(fallback.getFallbackFloat("Weather_" + name + "_Flash_Decrement")) , mFlashBrightness(0.0f) { + mDL.FogFactor = dlFactor; + mDL.FogOffset = dlOffset; mThunderSoundID[0] = fallback.getFallbackString("Weather_" + name + "_Thunder_Sound_ID_0"); mThunderSoundID[1] = fallback.getFallbackString("Weather_" + name + "_Thunder_Sound_ID_1"); mThunderSoundID[2] = fallback.getFallbackString("Weather_" + name + "_Thunder_Sound_ID_2"); @@ -541,16 +545,18 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, const Fall mTimeSettings.mSunriseTime = mSunriseTime; mWeatherSettings.reserve(10); - addWeather("Clear", fallback); // 0 - addWeather("Cloudy", fallback); // 1 - addWeather("Foggy", fallback); // 2 - addWeather("Overcast", fallback); // 3 - addWeather("Rain", fallback); // 4 - addWeather("Thunderstorm", fallback); // 5 - addWeather("Ashstorm", fallback, "meshes\\ashcloud.nif"); // 6 - addWeather("Blight", fallback, "meshes\\blightcloud.nif"); // 7 - addWeather("Snow", fallback, "meshes\\snow.nif"); // 8 - addWeather("Blizzard", fallback, "meshes\\blizzard.nif"); // 9 + // These distant land fog factor and offset values are the defaults MGE XE provides. Should be + // provided by settings somewhere? + addWeather("Clear", fallback, 1.0f, 0.0f); // 0 + addWeather("Cloudy", fallback, 0.9f, 0.0f); // 1 + addWeather("Foggy", fallback, 0.2f, 30.0f); // 2 + addWeather("Overcast", fallback, 0.7f, 0.0f); // 3 + addWeather("Rain", fallback, 0.5f, 10.0f); // 4 + addWeather("Thunderstorm", fallback, 0.5f, 20.0f); // 5 + addWeather("Ashstorm", fallback, 0.2f, 50.0f, "meshes\\ashcloud.nif"); // 6 + addWeather("Blight", fallback, 0.2f, 60.0f, "meshes\\blightcloud.nif"); // 7 + addWeather("Snow", fallback, 0.5f, 40.0f, "meshes\\snow.nif"); // 8 + addWeather("Blizzard", fallback, 0.16f, 70.0f, "meshes\\blizzard.nif"); // 9 Store::iterator it = store.get().begin(); for(; it != store.get().end(); ++it) @@ -718,7 +724,8 @@ void WeatherManager::update(float duration, bool paused, const TimeStamp& time, mRendering.getSkyManager()->setMasserState(mMasser.calculateState(time)); mRendering.getSkyManager()->setSecundaState(mSecunda.calculateState(time)); - mRendering.configureFog(mResult.mFogDepth, underwaterFog, mResult.mFogColor); + mRendering.configureFog(mResult.mFogDepth, underwaterFog, mResult.mDLFogFactor, + mResult.mDLFogOffset/100.0f, mResult.mFogColor); mRendering.setAmbientColour(mResult.mAmbientColor); mRendering.setSunColour(mResult.mSunColor, mResult.mSunColor * mResult.mGlareView); @@ -863,11 +870,12 @@ void WeatherManager::clear() inline void WeatherManager::addWeather(const std::string& name, const Fallback::Map& fallback, + float dlFactor, float dlOffset, const std::string& particleEffect) { static const float fStromWindSpeed = mStore.get().find("fStromWindSpeed")->getFloat(); - Weather weather(name, fallback, fStromWindSpeed, mRainSpeed, particleEffect); + Weather weather(name, fallback, fStromWindSpeed, mRainSpeed, dlFactor, dlOffset, particleEffect); mWeatherSettings.push_back(weather); } @@ -1055,6 +1063,8 @@ inline void WeatherManager::calculateResult(const int weatherID, const float gam mResult.mNight = (gameHour < mSunriseTime || gameHour > mTimeSettings.mNightStart - 1); mResult.mFogDepth = current.mLandFogDepth.getValue(gameHour, mTimeSettings); + mResult.mDLFogFactor = current.mDL.FogFactor; + mResult.mDLFogOffset = current.mDL.FogOffset; mResult.mFogColor = current.mFogColor.getValue(gameHour, mTimeSettings); mResult.mAmbientColor = current.mAmbientColor.getValue(gameHour, mTimeSettings); mResult.mSunColor = current.mSunColor.getValue(gameHour, mTimeSettings); @@ -1110,6 +1120,8 @@ inline void WeatherManager::calculateTransitionResult(const float factor, const mResult.mAmbientColor = lerp(current.mAmbientColor, other.mAmbientColor, factor); mResult.mSunDiscColor = lerp(current.mSunDiscColor, other.mSunDiscColor, factor); mResult.mFogDepth = lerp(current.mFogDepth, other.mFogDepth, factor); + mResult.mDLFogFactor = lerp(current.mDLFogFactor, other.mDLFogFactor, factor); + mResult.mDLFogOffset = lerp(current.mDLFogOffset, other.mDLFogOffset, factor); mResult.mWindSpeed = lerp(current.mWindSpeed, other.mWindSpeed, factor); mResult.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed, factor); mResult.mGlareView = lerp(current.mGlareView, other.mGlareView, factor); diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 86bbf7d3c..06d5758f6 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -78,6 +78,8 @@ namespace MWWorld const Fallback::Map& fallback, float stormWindSpeed, float rainSpeed, + float dlFactor, + float dlOffset, const std::string& particleEffect); std::string mCloudTexture; @@ -107,6 +109,12 @@ namespace MWWorld // Also appears to modify how visible the sun, moons, and stars are for various weather effects. float mGlareView; + // Fog factor and offset used with distant land rendering. + struct { + float FogFactor; + float FogOffset; + } mDL; + // Sound effect // This is used for Blight, Ashstorm and Blizzard (Bloodmoon) std::string mAmbientLoopSoundID; @@ -299,6 +307,7 @@ namespace MWWorld void addWeather(const std::string& name, const Fallback::Map& fallback, + float dlFactor, float dlOffset, const std::string& particleEffect = ""); void importRegions(); diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index aa92a01c6..d2c97953c 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -339,52 +339,76 @@ namespace Gui + MyGUI::utility::toString((fontSize-data[i].ascent))); code->addAttribute("size", MyGUI::IntSize(static_cast(data[i].width), static_cast(data[i].height))); - // More hacks! The french game uses several win1252 characters that are not included - // in the cp437 encoding of the font. Fall back to similar available characters. - if (mEncoding == ToUTF8::CP437) + // Fall back from unavailable Windows-1252 encoding symbols to similar characters available in the game fonts + std::multimap additional; // fallback glyph index, unicode + additional.insert(std::make_pair(156, 0x00A2)); // cent sign + additional.insert(std::make_pair(89, 0x00A5)); // yen sign + additional.insert(std::make_pair(221, 0x00A6)); // broken bar + additional.insert(std::make_pair(99, 0x00A9)); // copyright sign + additional.insert(std::make_pair(97, 0x00AA)); // prima ordinal indicator + additional.insert(std::make_pair(60, 0x00AB)); // double left-pointing angle quotation mark + additional.insert(std::make_pair(45, 0x00AD)); // soft hyphen + additional.insert(std::make_pair(114, 0x00AE)); // registered trademark symbol + additional.insert(std::make_pair(45, 0x00AF)); // macron + additional.insert(std::make_pair(241, 0x00B1)); // plus-minus sign + additional.insert(std::make_pair(50, 0x00B2)); // superscript two + additional.insert(std::make_pair(51, 0x00B3)); // superscript three + additional.insert(std::make_pair(44, 0x00B8)); // cedilla + additional.insert(std::make_pair(49, 0x00B9)); // superscript one + additional.insert(std::make_pair(111, 0x00BA)); // primo ordinal indicator + additional.insert(std::make_pair(62, 0x00BB)); // double right-pointing angle quotation mark + additional.insert(std::make_pair(63, 0x00BF)); // inverted question mark + additional.insert(std::make_pair(65, 0x00C6)); // latin capital ae ligature + additional.insert(std::make_pair(79, 0x00D8)); // latin capital o with stroke + additional.insert(std::make_pair(97, 0x00E6)); // latin small ae ligature + additional.insert(std::make_pair(111, 0x00F8)); // latin small o with stroke + additional.insert(std::make_pair(79, 0x0152)); // latin capital oe ligature + additional.insert(std::make_pair(111, 0x0153)); // latin small oe ligature + additional.insert(std::make_pair(83, 0x015A)); // latin capital s with caron + additional.insert(std::make_pair(115, 0x015B)); // latin small s with caron + additional.insert(std::make_pair(89, 0x0178)); // latin capital y with diaresis + additional.insert(std::make_pair(90, 0x017D)); // latin capital z with caron + additional.insert(std::make_pair(122, 0x017E)); // latin small z with caron + additional.insert(std::make_pair(102, 0x0192)); // latin small f with hook + additional.insert(std::make_pair(94, 0x02C6)); // circumflex modifier + additional.insert(std::make_pair(126, 0x02DC)); // small tilde + additional.insert(std::make_pair(69, 0x0401)); // cyrillic capital io (no diaeresis latin e is available) + additional.insert(std::make_pair(137, 0x0451)); // cyrillic small io + additional.insert(std::make_pair(45, 0x2012)); // figure dash + additional.insert(std::make_pair(45, 0x2013)); // en dash + additional.insert(std::make_pair(45, 0x2014)); // em dash + additional.insert(std::make_pair(39, 0x2018)); // left single quotation mark + additional.insert(std::make_pair(39, 0x2019)); // right single quotation mark + additional.insert(std::make_pair(44, 0x201A)); // single low quotation mark + additional.insert(std::make_pair(39, 0x201B)); // single high quotation mark (reversed) + additional.insert(std::make_pair(34, 0x201C)); // left double quotation mark + additional.insert(std::make_pair(34, 0x201D)); // right double quotation mark + additional.insert(std::make_pair(44, 0x201E)); // double low quotation mark + additional.insert(std::make_pair(34, 0x201F)); // double high quotation mark (reversed) + additional.insert(std::make_pair(43, 0x2020)); // dagger + additional.insert(std::make_pair(216, 0x2021)); // double dagger (note: this glyph is not available) + additional.insert(std::make_pair(46, 0x2026)); // ellipsis + additional.insert(std::make_pair(37, 0x2030)); // per mille sign + additional.insert(std::make_pair(60, 0x2039)); // single left-pointing angle quotation mark + additional.insert(std::make_pair(62, 0x203A)); // single right-pointing angle quotation mark + additional.insert(std::make_pair(101, 0x20AC)); // euro sign + additional.insert(std::make_pair(84, 0x2122)); // trademark sign + additional.insert(std::make_pair(45, 0x2212)); // minus sign + + for (std::multimap::iterator it = additional.begin(); it != additional.end(); ++it) { - std::multimap additional; // - additional.insert(std::make_pair(39, 0x2019)); // apostrophe - additional.insert(std::make_pair(45, 0x2013)); // dash - additional.insert(std::make_pair(45, 0x2014)); // dash - additional.insert(std::make_pair(34, 0x201D)); // right double quotation mark - additional.insert(std::make_pair(34, 0x201C)); // left double quotation mark - additional.insert(std::make_pair(44, 0x201A)); - additional.insert(std::make_pair(44, 0x201E)); - additional.insert(std::make_pair(43, 0x2020)); - additional.insert(std::make_pair(94, 0x02C6)); - additional.insert(std::make_pair(37, 0x2030)); - additional.insert(std::make_pair(83, 0x0160)); - additional.insert(std::make_pair(60, 0x2039)); - additional.insert(std::make_pair(79, 0x0152)); - additional.insert(std::make_pair(90, 0x017D)); - additional.insert(std::make_pair(39, 0x2019)); - additional.insert(std::make_pair(126, 0x02DC)); - additional.insert(std::make_pair(84, 0x2122)); - additional.insert(std::make_pair(83, 0x0161)); - additional.insert(std::make_pair(62, 0x203A)); - additional.insert(std::make_pair(111, 0x0153)); - additional.insert(std::make_pair(122, 0x017E)); - additional.insert(std::make_pair(89, 0x0178)); - additional.insert(std::make_pair(156, 0x00A2)); - additional.insert(std::make_pair(46, 0x2026)); - - for (std::multimap::iterator it = additional.begin(); it != additional.end(); ++it) - { - if (it->first != i) - continue; - - code = codes->createChild("Code"); - code->addAttribute("index", it->second); - code->addAttribute("coord", MyGUI::utility::toString(x1) + " " - + MyGUI::utility::toString(y1) + " " - + MyGUI::utility::toString(w) + " " - + MyGUI::utility::toString(h)); - code->addAttribute("advance", data[i].width); - code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " - + MyGUI::utility::toString((fontSize-data[i].ascent))); - code->addAttribute("size", MyGUI::IntSize(static_cast(data[i].width), static_cast(data[i].height))); - } + if (it->first != i) + continue; + code = codes->createChild("Code"); + code->addAttribute("index", it->second); + code->addAttribute("coord", MyGUI::utility::toString(x1) + " " + + MyGUI::utility::toString(y1) + " " + + MyGUI::utility::toString(w) + " " + + MyGUI::utility::toString(h)); + code->addAttribute("advance", data[i].width); + code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " + + MyGUI::utility::toString((fontSize-data[i].ascent))); + code->addAttribute("size", MyGUI::IntSize(static_cast(data[i].width), static_cast(data[i].height))); } // ASCII vertical bar, use this as text input cursor diff --git a/components/openmw-mp/Base/BasePlayer.hpp b/components/openmw-mp/Base/BasePlayer.hpp index 43ed28f27..f573e6d9a 100644 --- a/components/openmw-mp/Base/BasePlayer.hpp +++ b/components/openmw-mp/Base/BasePlayer.hpp @@ -254,8 +254,9 @@ namespace mwmp BasePlayer(RakNet::RakNetGUID guid) : guid(guid) { spellbookChanges.action = SpellbookChanges::Type::None; - useCreatureName = false; isWerewolf = false; + displayCreatureName = false; + resetStats = false; } BasePlayer() @@ -324,10 +325,12 @@ namespace mwmp std::string sound; Animation animation; + bool resetStats; float scale; bool isWerewolf; - std::string creatureModel; - bool useCreatureName; + + bool displayCreatureName; + std::string creatureRefId; std::string deathReason; diff --git a/components/openmw-mp/Packets/Player/PacketPlayerBaseInfo.cpp b/components/openmw-mp/Packets/Player/PacketPlayerBaseInfo.cpp index e026e5798..6247c4bd4 100644 --- a/components/openmw-mp/Packets/Player/PacketPlayerBaseInfo.cpp +++ b/components/openmw-mp/Packets/Player/PacketPlayerBaseInfo.cpp @@ -26,6 +26,5 @@ void PacketPlayerBaseInfo::Packet(RakNet::BitStream *bs, bool send) RW(player->birthsign, send, 1); - RW(player->creatureModel, send, 1); - RW(player->useCreatureName, send); + RW(player->resetStats, send); } diff --git a/components/openmw-mp/Packets/Player/PacketPlayerShapeshift.cpp b/components/openmw-mp/Packets/Player/PacketPlayerShapeshift.cpp index 352342ccd..a1757d20e 100644 --- a/components/openmw-mp/Packets/Player/PacketPlayerShapeshift.cpp +++ b/components/openmw-mp/Packets/Player/PacketPlayerShapeshift.cpp @@ -14,4 +14,7 @@ void PacketPlayerShapeshift::Packet(RakNet::BitStream *bs, bool send) RW(player->scale, send); RW(player->isWerewolf, send); + + RW(player->displayCreatureName, send); + RW(player->creatureRefId, send, 1); } diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 85d603d84..87443ff1a 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -90,6 +90,24 @@ pointers cache size = 40 # If true, use paging and LOD algorithms to display the entire terrain. If false, only display terrain of the loaded cells distant terrain = false +[Fog] + +# If true, use extended fog parameters for distant terrain not controlled by +# viewing distance. If false, use the standard fog calculations. +use distant fog = false + +distant land fog start = 16384 + +distant land fog end = 40960 + +distant underwater fog start = -4096 + +distant underwater fog end = 2457.6 + +distant interior fog start = 0 + +distant interior fog end = 16384 + [Map] # Size of each exterior cell in pixels in the world map. (e.g. 12 to 24). @@ -182,6 +200,9 @@ classic reflect absorb attribute behavior = true # Show duration of magic effect and lights in the spells window. show effect duration = false +# Account for the first follower in fast travel cost calculations. +charge for every follower travelling = false + # Prevents merchants from equipping items that are sold to them. prevent merchant equipping = false