// // Created by koncord on 02.01.16. // #include "DedicatedPlayer.hpp" #include #include "../mwbase/environment.hpp" #include "../mwstate/statemanagerimp.hpp" #include "../mwinput/inputmanagerimp.hpp" #include "../mwgui/windowmanagerimp.hpp" #include "../mwworld/worldimp.hpp" #include "../mwworld/player.hpp" #include "../mwworld/customdata.hpp" #include "../mwclass/npc.hpp" #include "../mwmechanics/actor.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/mechanicsmanagerimp.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/action.hpp" #include "Main.hpp" #include #include #include using namespace mwmp; using namespace std; std::map Players::players; DedicatedPlayer::DedicatedPlayer(RakNet::RakNetGUID guid) : BasePlayer(guid) { getAttack()->pressed = 0; CreatureStats()->mDead = false; movementFlags = 0; } DedicatedPlayer::~DedicatedPlayer() { } MWWorld::Ptr DedicatedPlayer::getPtr() { return ptr; } void Players::createPlayer(RakNet::RakNetGUID guid) { LOG_APPEND(Log::LOG_INFO, "%s", "- Setting up character info"); MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); ESM::NPC npc = *player.get()->mBase; DedicatedPlayer *dedicPlayer = players[guid.g]; 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; if (dedicPlayer->state == 0) { npc.mId = "Dedicated Player"; std::string recid = world->createRecord(npc)->mId; dedicPlayer->reference = new MWWorld::ManualRef(world->getStore(), recid, 1); } // Temporarily spawn or move player to ToddTest whenever setting base info ESM::Position newPos; world->findInteriorPosition("ToddTest", newPos); MWWorld::CellStore *cellStore = world->getInterior("ToddTest"); if (dedicPlayer->state == 0) { LOG_APPEND(Log::LOG_INFO, "- Creating new reference pointer for %s", dedicPlayer->Npc()->mName.c_str()); MWWorld::Ptr tmp = world->placeObject(dedicPlayer->reference->getPtr(), cellStore, newPos); dedicPlayer->ptr.mCell = tmp.mCell; dedicPlayer->ptr.mRef = tmp.mRef; dedicPlayer->cell = *dedicPlayer->ptr.getCell()->getCell(); dedicPlayer->pos = dedicPlayer->ptr.getRefData().getPosition(); } else { LOG_APPEND(Log::LOG_INFO, "- Updating reference pointer for %s", dedicPlayer->Npc()->mName.c_str()); dedicPlayer->ptr.getBase()->canChangeCell = true; dedicPlayer->updatePtr(world->moveObject(dedicPlayer->ptr, cellStore, newPos.pos[0], newPos.pos[1], newPos.pos[2])); npc.mId = players[guid.g]->ptr.get()->mBase->mId; MWWorld::ESMStore *store = const_cast(&world->getStore()); MWWorld::Store *esm_store = const_cast *> (&store->get()); esm_store->insert(npc); dedicPlayer->updateCell(); ESM::CustomMarker mEditingMarker = Main::get().getGUIController()->CreateMarker(guid); dedicPlayer->marker = mEditingMarker; dedicPlayer->setMarkerState(true); } dedicPlayer->guid = guid; dedicPlayer->state = 2; world->enable(players[guid.g]->ptr); } void Players::cleanUp() { for (std::map ::iterator it = players.begin(); it != players.end(); it++) delete it->second; } void Players::disconnectPlayer(RakNet::RakNetGUID guid) { if (players[guid.g]->state > 1) { players[guid.g]->state = 1; // Remove player's marker players[guid.g]->setMarkerState(false); MWBase::World *world = MWBase::Environment::get().getWorld(); world->disable(players[guid.g]->getPtr()); // Move player to ToddTest ESM::Position newPos; world->findInteriorPosition("ToddTest", newPos); MWWorld::CellStore *store = world->getInterior("ToddTest"); players[guid.g]->getPtr().getBase()->canChangeCell = true; world->moveObject(players[guid.g]->getPtr(), store, newPos.pos[0], newPos.pos[1], newPos.pos[2]); } } DedicatedPlayer *Players::getPlayer(RakNet::RakNetGUID guid) { return players[guid.g]; } MWWorld::Ptr DedicatedPlayer::getLiveCellPtr() { return reference->getPtr(); } MWWorld::ManualRef *DedicatedPlayer::getRef() { return reference; } ESM::Position Slerp(ESM::Position start, ESM::Position end, float percent) { // dot product - the cosine of the angle between 2 vectors. float dot = start.asVec3() * end.asVec3(); // clamp it to be in the range of Acos() // This may be unnecessary, but floating point // precision can be a fickle mistress. dot = boost::algorithm::clamp(dot, -1.0f, 1.0f); // acos(dot) returns the angle between start and end, // And multiplying that by percent returns the angle between // start and the final result. float theta = acos(dot) * percent; osg::Vec3f relativeVec = end.asVec3() - start.asVec3() * dot; relativeVec.normalize(); // Orthonormal basis // result osg::Vec3f tmp ((start.asVec3() * cos(theta)) + (relativeVec * sin(theta))); ESM::Position result; result.pos[0] = tmp.x(); result.pos[1] = tmp.y(); result.pos[2] = tmp.z(); result.rot[0] = start.rot[0]; result.rot[1] = start.rot[1]; result.rot[2] = result.rot[2]; return result; } osg::Vec3f Lerp(osg::Vec3f start, osg::Vec3f end, float percent) { osg::Vec3f p(percent, percent, percent); return (start + osg::componentMultiply(p, (end - start))); } void DedicatedPlayer::move(float dt) { if (state != 2) return; ESM::Position refPos = ptr.getRefData().getPosition(); MWBase::World *world = MWBase::Environment::get().getWorld(); { osg::Vec3f lerp = Lerp(refPos.asVec3(), pos.asVec3(), dt * 15); refPos.pos[0] = lerp.x(); refPos.pos[1] = lerp.y(); refPos.pos[2] = lerp.z(); world->moveObject(ptr, refPos.pos[0], refPos.pos[1], refPos.pos[2]); } MWMechanics::Movement *move = &ptr.getClass().getMovementSettings(ptr); move->mPosition[0] = dir.pos[0]; move->mPosition[1] = dir.pos[1]; move->mPosition[2] = dir.pos[2]; world->rotateObject(ptr, pos.rot[0], pos.rot[1], pos.rot[2]); } void Players::update(float dt) { for (std::map ::iterator it = players.begin(); it != players.end(); it++) { DedicatedPlayer *pl = it->second; if (pl == 0) continue; MWMechanics::NpcStats *ptrNpcStats = &pl->ptr.getClass().getNpcStats(pl->getPtr()); MWMechanics::DynamicStat value; if (pl->CreatureStats()->mDead) { value.readState(pl->CreatureStats()->mDynamic[0]); ptrNpcStats->setHealth(value); continue; } value.readState(pl->CreatureStats()->mDynamic[0]); ptrNpcStats->setHealth(value); value.readState(pl->CreatureStats()->mDynamic[1]); ptrNpcStats->setMagicka(value); value.readState(pl->CreatureStats()->mDynamic[2]); ptrNpcStats->setFatigue(value); if (ptrNpcStats->isDead()) ptrNpcStats->resurrect(); ptrNpcStats->setAttacked(false); ptrNpcStats->getAiSequence().stopCombat(); ptrNpcStats->setAlarmed(false); ptrNpcStats->setAiSetting(MWMechanics::CreatureStats::AI_Alarm, 0); ptrNpcStats->setAiSetting(MWMechanics::CreatureStats::AI_Fight, 0); ptrNpcStats->setAiSetting(MWMechanics::CreatureStats::AI_Flee, 0); ptrNpcStats->setAiSetting(MWMechanics::CreatureStats::AI_Hello, 0); ptrNpcStats->setBaseDisposition(255); pl->move(dt); pl->updateDrawState(); } } void DedicatedPlayer::updatePtr(MWWorld::Ptr newPtr) { ptr.mCell = newPtr.mCell; ptr.mRef = newPtr.mRef; ptr.mContainerStore = newPtr.mContainerStore; // Disallow this player's reference from moving across cells until // the correct packet is sent by the player ptr.getBase()->canChangeCell = false; } DedicatedPlayer *Players::newPlayer(RakNet::RakNetGUID guid) { LOG_APPEND(Log::LOG_INFO, "- Creating new DedicatedPlayer with guid %lu", guid.g); players[guid.g] = new DedicatedPlayer(guid); players[guid.g]->state = 0; return players[guid.g]; } void DedicatedPlayer::updateInventory() { MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr); for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) { MWWorld::ContainerStoreIterator it = invStore.getSlot(slot); const string &dedicItem = EquipedItem(slot)->refid; std::string item = ""; bool equal = false; if (it != invStore.end()) { item = it->getCellRef().getRefId(); if (!Misc::StringUtils::ciEqual(item, dedicItem)) // if other item equiped { MWWorld::ContainerStore &store = ptr.getClass().getContainerStore(ptr); store.remove(item, store.count(item), ptr); } else equal = true; } if (dedicItem.empty() || equal) continue; const int count = EquipedItem(slot)->count; ptr.getClass().getContainerStore(ptr).add(dedicItem, count, ptr); for (MWWorld::ContainerStoreIterator it2 = invStore.begin(); it2 != invStore.end(); ++it2) { if (::Misc::StringUtils::ciEqual(it2->getCellRef().getRefId(), dedicItem)) // equip item { boost::shared_ptr action = it2->getClass().use(*it2); action->execute(ptr); break; } } } } const std::string DedicatedPlayer::getAnim() { static string anim; static string animDir; static string animWeap; MWMechanics::NpcStats *npcStats = &ptr.getClass().getNpcStats(ptr); if (movementFlags & MWMechanics::CreatureStats::Flag_Run) anim = "run"; else if (movementFlags & MWMechanics::CreatureStats::Flag_Sneak) anim = "sneak"; else anim = "walk"; if (movementAnim != 0) { if (movementAnim == 3) animDir = "forward"; else if (movementAnim == 4) animDir = "back"; else if (movementAnim == 2) animDir = "left"; else if (movementAnim == 1) animDir = "right"; } else { anim = "idle"; animDir = ""; } if (npcStats->getDrawState() == MWMechanics::DrawState_Weapon) { MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr); MWWorld::ContainerStoreIterator weaponSlot = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if (weaponSlot != invStore.end() && weaponSlot->getTypeName() == typeid(ESM::Weapon).name()) { int type = weaponSlot->get()->mBase->mData.mType; if (type == ESM::Weapon::ShortBladeOneHand || type == ESM::Weapon::LongBladeOneHand || type == ESM::Weapon::BluntOneHand || type == ESM::Weapon::AxeOneHand /*|| type == ESM::Weapon::MarksmanThrown || type == ESM::Weapon::MarksmanCrossbow || type == ESM::Weapon::MarksmanBow*/) animWeap = "1h"; else if (type == ESM::Weapon::LongBladeTwoHand || type == ESM::Weapon::BluntTwoClose || type == ESM::Weapon::AxeTwoHand) animWeap = "2c"; else if (type == ESM::Weapon::BluntTwoWide || type == ESM::Weapon::SpearTwoWide) animWeap = "2w"; } else animWeap = "hh"; } else if (movementAnim == 0 && npcStats->getDrawState() == MWMechanics::DrawState_Spell) animWeap = "spell"; else animWeap = ""; return (anim + animDir + animWeap); } DedicatedPlayer *Players::getPlayer(const MWWorld::Ptr &ptr) { std::map ::iterator it = players.begin(); for (; it != players.end(); it++) { if (it->second == 0 || it->second->getPtr().mRef == 0) continue; string refid = ptr.getCellRef().getRefId(); if (it->second->getPtr().getCellRef().getRefId() == refid) return it->second; } return 0; } void DedicatedPlayer::updateDrawState() { using namespace MWMechanics; if (drawState == 0) ptr.getClass().getNpcStats(ptr).setDrawState(DrawState_Nothing); else if (drawState == 1) ptr.getClass().getNpcStats(ptr).setDrawState(DrawState_Weapon); else if (drawState == 2) ptr.getClass().getNpcStats(ptr).setDrawState(DrawState_Spell); MWMechanics::NpcStats *ptrNpcStats = &ptr.getClass().getNpcStats(ptr); ptrNpcStats->setMovementFlag(CreatureStats::Flag_Run, (movementFlags & CreatureStats::Flag_Run) != 0); ptrNpcStats->setMovementFlag(CreatureStats::Flag_Sneak, (movementFlags & CreatureStats::Flag_Sneak) != 0); ptrNpcStats->setMovementFlag(CreatureStats::Flag_ForceJump, (movementFlags & CreatureStats::Flag_ForceJump) != 0); ptrNpcStats->setMovementFlag(CreatureStats::Flag_ForceMoveJump, (movementFlags & CreatureStats::Flag_ForceMoveJump) != 0); } void DedicatedPlayer::updateCell() { // Prevent cell update when player hasn't been instantiated yet if (state == 0) return; MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::CellStore *cellStore; LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Server says %s (%s) moved to %s", ptr.getBase()->mRef.getRefId().c_str(), this->Npc()->mName.c_str(), cell.getDescription().c_str()); try { if (cell.isExterior()) cellStore = world->getExterior(cell.mData.mX, cell.mData.mY); else cellStore = world->getInterior(cell.mName); } // If the intended cell doesn't exist on this client, use ToddTest as a replacement catch (std::exception&) { cellStore = world->getInterior("ToddTest"); LOG_APPEND(Log::LOG_INFO, "%s", "- Cell doesn't exist on this client"); } if (!cellStore) return; // Allow this player's reference to move across a cell now that a manual cell // update has been called ptr.getBase()->canChangeCell = true; updatePtr(world->moveObject(ptr, cellStore, pos.pos[0], pos.pos[1], pos.pos[2])); } void DedicatedPlayer::updateMarker() { if (!markerEnabled) return; GUIController *gui = Main::get().getGUIController(); if (gui->mPlayerMarkers.contains(marker)) { gui->mPlayerMarkers.deleteMarker(marker); marker = gui->CreateMarker(guid); gui->mPlayerMarkers.addMarker(marker); } else gui->mPlayerMarkers.addMarker(marker, true); } void DedicatedPlayer::removeMarker() { if (!markerEnabled) return; markerEnabled = false; Main::get().getGUIController()->mPlayerMarkers.deleteMarker(marker); } void DedicatedPlayer::setMarkerState(bool state) { if (state) { markerEnabled = true; updateMarker(); } else removeMarker(); } void DedicatedPlayer::updateActor(MWMechanics::Actor *actor) { actor->getCharacterController()->setAttackingOrSpell(getAttack()->pressed); }