예제 #1
0
    void SoundManager::say(const MWWorld::Ptr &ptr, const std::string& filename)
    {
        if(!mOutput->isInitialized())
            return;
        try
        {
            float basevol = volumeFromType(Play_TypeVoice);
            std::string filePath = "Sound/"+filename;
            const ESM::Position &pos = ptr.getRefData().getPosition();
            const osg::Vec3f objpos(pos.asVec3());

            MWBase::World* world = MWBase::Environment::get().getWorld();
            static const float fAudioMinDistanceMult = world->getStore().get<ESM::GameSetting>().find("fAudioMinDistanceMult")->getFloat();
            static const float fAudioMaxDistanceMult = world->getStore().get<ESM::GameSetting>().find("fAudioMaxDistanceMult")->getFloat();
            static const float fAudioVoiceDefaultMinDistance = world->getStore().get<ESM::GameSetting>().find("fAudioVoiceDefaultMinDistance")->getFloat();
            static const float fAudioVoiceDefaultMaxDistance = world->getStore().get<ESM::GameSetting>().find("fAudioVoiceDefaultMaxDistance")->getFloat();

            float minDistance = fAudioVoiceDefaultMinDistance * fAudioMinDistanceMult;
            float maxDistance = fAudioVoiceDefaultMaxDistance * fAudioMaxDistanceMult;
            minDistance = std::max(minDistance, 1.f);
            maxDistance = std::max(minDistance, maxDistance);

            MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f,
                                                          minDistance, maxDistance, Play_Normal|Play_TypeVoice, 0, true);
            mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound"));
        }
        catch(std::exception &e)
        {
            std::cout <<"Sound Error: "<<e.what()<< std::endl;
        }
    }
예제 #2
0
    // Convert a soundId to file name, and modify the volume
    // according to the sounds local volume setting, minRange and
    // maxRange.
    std::string SoundManager::lookup(const std::string &soundId,
                       float &volume, float &min, float &max)
    {
        MWBase::World* world = MWBase::Environment::get().getWorld();
        const ESM::Sound *snd = world->getStore().get<ESM::Sound>().find(soundId);

        volume *= static_cast<float>(pow(10.0, (snd->mData.mVolume / 255.0*3348.0 - 3348.0) / 2000.0));

        if(snd->mData.mMinRange == 0 && snd->mData.mMaxRange == 0)
        {
            static const float fAudioDefaultMinDistance = world->getStore().get<ESM::GameSetting>().find("fAudioDefaultMinDistance")->getFloat();
            static const float fAudioDefaultMaxDistance = world->getStore().get<ESM::GameSetting>().find("fAudioDefaultMaxDistance")->getFloat();
            min = fAudioDefaultMinDistance;
            max = fAudioDefaultMaxDistance;
        }
        else
        {
            min = snd->mData.mMinRange;
            max = snd->mData.mMaxRange;
        }

        static const float fAudioMinDistanceMult = world->getStore().get<ESM::GameSetting>().find("fAudioMinDistanceMult")->getFloat();
        static const float fAudioMaxDistanceMult = world->getStore().get<ESM::GameSetting>().find("fAudioMaxDistanceMult")->getFloat();
        min *= fAudioMinDistanceMult;
        max *= fAudioMaxDistanceMult;
        min = std::max(min, 1.0f);
        max = std::max(min, max);

        return "Sound/"+snd->mSound;
    }
예제 #3
0
    void LevelupDialog::open()
    {
        MWBase::World *world = MWBase::Environment::get().getWorld();
        MWWorld::Ptr player = world->getPlayerPtr();
        MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats (player);
        MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player);

        mSpentAttributes.clear();
        resetCoins();

        setAttributeValues();

        const ESM::NPC *playerData = player.get<ESM::NPC>()->mBase;

        // set class image
        const ESM::Class *cls =
            world->getStore().get<ESM::Class>().find(playerData->mClass);

        // Vanilla uses thief.dds for custom classes.  A player with a custom class
        // doesn't have mId set, see mwworld/esmstore.hpp where it is initialised as
        // "$dynamic0".  This check should resolve bug #1260.
        // Choosing Stealth specialization and Speed/Agility as attributes.
        if(world->getStore().get<ESM::Class>().isDynamic(cls->mId))
        {
            MWWorld::SharedIterator<ESM::Class> it = world->getStore().get<ESM::Class>().begin();
            for(; it != world->getStore().get<ESM::Class>().end(); it++)
            {
                if(it->mData.mIsPlayable && it->mData.mSpecialization == 2 && it->mData.mAttribute[0] == 4 && it->mData.mAttribute[1] == 3)
                    break;
            }
            mClassImage->setImageTexture ("textures\\levelup\\" + it->mId + ".dds");
        }
        else
            mClassImage->setImageTexture ("textures\\levelup\\" + cls->mId + ".dds");

        int level = creatureStats.getLevel ()+1;
        mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + boost::lexical_cast<std::string>(level));

        std::string levelupdescription;
        if(level>20)
            levelupdescription=world->getFallback()->getFallbackString("Level_Up_Default");
        else
            levelupdescription=world->getFallback()->getFallbackString("Level_Up_Level"+boost::lexical_cast<std::string>(level));

        mLevelDescription->setCaption (levelupdescription);

        for (int i=0; i<8; ++i)
        {
            MyGUI::TextBox* text = mAttributeMultipliers[i];
            int mult = pcStats.getLevelupAttributeMultiplier (i);
            text->setCaption(mult <= 1 ? "" : "x" + boost::lexical_cast<std::string>(mult));
        }

        center();
    }
예제 #4
0
    std::string InterpreterContext::getPCNextRank() const
    {
        MWBase::World *world = MWBase::Environment::get().getWorld();
        MWWorld::Ptr player = world->getPlayerPtr();

        std::string factionId = mReference.getClass().getNpcStats (mReference).getFactionRanks().begin()->first;

        std::map<std::string, int> ranks = player.getClass().getNpcStats (player).getFactionRanks();
        std::map<std::string, int>::const_iterator it = ranks.find(factionId);
        int rank = -1;
        if (it != ranks.end())
            rank = it->second;

        ++rank; // Next rank

        // if we are already at max rank, there is no next rank
        if (rank > 9)
            rank = 9;

        const MWWorld::ESMStore &store = world->getStore();
        const ESM::Faction *faction = store.get<ESM::Faction>().find(factionId);

        if(rank < 0 || rank > 9)
            return "";

        return faction->mRanks[rank];
    }
예제 #5
0
    std::string InterpreterContext::getPCRank() const
    {
        MWBase::World *world = MWBase::Environment::get().getWorld();
        MWWorld::Ptr player = world->getPlayerPtr();

        std::string factionId = mReference.getClass().getNpcStats (mReference).getFactionRanks().begin()->first;

        std::map<std::string, int> ranks = player.getClass().getNpcStats (player).getFactionRanks();
        std::map<std::string, int>::const_iterator it = ranks.find(factionId);
        int rank = -1;
        if (it != ranks.end())
            rank = it->second;

        // If you are not in the faction, PcRank returns the first rank, for whatever reason.
        // This is used by the dialogue when joining the Thieves Guild in Balmora.
        if (rank == -1)
            rank = 0;

        const MWWorld::ESMStore &store = world->getStore();
        const ESM::Faction *faction = store.get<ESM::Faction>().find(factionId);

        if(rank < 0 || rank > 9) // there are only 10 ranks
            return "";

        return faction->mRanks[rank];
    }
예제 #6
0
void Pathgrid::enableCellPathgrid(const MWWorld::CellStore *store)
{
    MWBase::World* world = MWBase::Environment::get().getWorld();
    const ESM::Pathgrid *pathgrid =
        world->getStore().get<ESM::Pathgrid>().search(*store->getCell());
    if (!pathgrid) return;

    osg::Vec3f cellPathGridPos(0, 0, 0);
    MWMechanics::CoordinateConverter(store->getCell()).toWorld(cellPathGridPos);

    osg::ref_ptr<osg::PositionAttitudeTransform> cellPathGrid = new osg::PositionAttitudeTransform;
    cellPathGrid->setPosition(cellPathGridPos);

    osg::ref_ptr<osg::Geometry> geometry = SceneUtil::createPathgridGeometry(*pathgrid);

    cellPathGrid->addChild(geometry);

    mPathGridRoot->addChild(cellPathGrid);

    if (store->getCell()->isExterior())
    {
        mExteriorPathgridNodes[std::make_pair(store->getCell()->getGridX(), store->getCell()->getGridY())] = cellPathGrid;
    }
    else
    {
        assert(mInteriorPathgridNode == NULL);
        mInteriorPathgridNode = cellPathGrid;
    }
}
예제 #7
0
    std::string InterpreterContext::getPCRank() const
    {
        MWBase::World *world = MWBase::Environment::get().getWorld();
        MWWorld::Ptr player = world->getPlayerPtr();

        std::string factionId = getReferenceImp().getClass().getPrimaryFaction(getReferenceImp());
        if (factionId.empty())
            throw std::runtime_error("getPCRank(): NPC is not in a faction");

        const std::map<std::string, int>& ranks = player.getClass().getNpcStats (player).getFactionRanks();
        std::map<std::string, int>::const_iterator it = ranks.find(Misc::StringUtils::lowerCase(factionId));
        int rank = -1;
        if (it != ranks.end())
            rank = it->second;

        // If you are not in the faction, PcRank returns the first rank, for whatever reason.
        // This is used by the dialogue when joining the Thieves Guild in Balmora.
        if (rank == -1)
            rank = 0;

        const MWWorld::ESMStore &store = world->getStore();
        const ESM::Faction *faction = store.get<ESM::Faction>().find(factionId);

        if(rank < 0 || rank > 9) // there are only 10 ranks
            return "";

        return faction->mRanks[rank];
    }
예제 #8
0
    std::string InterpreterContext::getPCNextRank() const
    {
        MWBase::World *world = MWBase::Environment::get().getWorld();
        MWWorld::Ptr player = world->getPlayerPtr();

        std::string factionId = getReferenceImp().getClass().getPrimaryFaction(getReferenceImp());
        if (factionId.empty())
            throw std::runtime_error("getPCNextRank(): NPC is not in a faction");

        const std::map<std::string, int>& ranks = player.getClass().getNpcStats (player).getFactionRanks();
        std::map<std::string, int>::const_iterator it = ranks.find(Misc::StringUtils::lowerCase(factionId));
        int rank = -1;
        if (it != ranks.end())
            rank = it->second;

        ++rank; // Next rank

        // if we are already at max rank, there is no next rank
        if (rank > 9)
            rank = 9;

        const MWWorld::ESMStore &store = world->getStore();
        const ESM::Faction *faction = store.get<ESM::Faction>().find(factionId);

        if(rank < 0 || rank > 9)
            return "";

        return faction->mRanks[rank];
    }
예제 #9
0
    void SoundManager::updateRegionSound(float duration)
    {
        static float sTimeToNextEnvSound = 0.0f;
        static int total = 0;
        static std::string regionName = "";
        static float sTimePassed = 0.0;
        MWBase::World *world = MWBase::Environment::get().getWorld();
        const MWWorld::Ptr player = world->getPlayerPtr();
        const ESM::Cell *cell = player.getCell()->getCell();

        sTimePassed += duration;
        if(!cell->isExterior() || sTimePassed < sTimeToNextEnvSound)
            return;

        float a = Misc::Rng::rollClosedProbability();
        // NOTE: We should use the "Minimum Time Between Environmental Sounds" and
        // "Maximum Time Between Environmental Sounds" fallback settings here.
        sTimeToNextEnvSound = 5.0f*a + 15.0f*(1.0f-a);
        sTimePassed = 0;

        if(regionName != cell->mRegion)
        {
            regionName = cell->mRegion;
            total = 0;
        }

        const ESM::Region *regn = world->getStore().get<ESM::Region>().search(regionName);
        if(regn == NULL)
            return;

        std::vector<ESM::Region::SoundRef>::const_iterator soundIter;
        if(total == 0)
        {
            soundIter = regn->mSoundList.begin();
            while(soundIter != regn->mSoundList.end())
            {
                total += (int)soundIter->mChance;
                ++soundIter;
            }
            if(total == 0)
                return;
        }

        int r = Misc::Rng::rollDice(total);
        int pos = 0;

        soundIter = regn->mSoundList.begin();
        while(soundIter != regn->mSoundList.end())
        {
            if(r - pos < soundIter->mChance)
            {
                playSound(soundIter->mSound.toString(), 1.0f, 1.0f);
                break;
            }
            pos += soundIter->mChance;

            ++soundIter;
        }
    }
예제 #10
0
void RecordHelper::updateNpcRecord(const ESM::NPC& npc)
{
    MWBase::World *world = MWBase::Environment::get().getWorld();

    MWWorld::ESMStore *esmStore = const_cast<MWWorld::ESMStore *>(&world->getStore());
    MWWorld::Store<ESM::NPC> *npcStore = const_cast<MWWorld::Store<ESM::NPC> *> (&esmStore->get<ESM::NPC>());

    npcStore->insert(npc);
}
예제 #11
0
void RecordHelper::updateCreatureRecord(const ESM::Creature& creature)
{
    MWBase::World *world = MWBase::Environment::get().getWorld();

    MWWorld::ESMStore *esmStore = const_cast<MWWorld::ESMStore *>(&world->getStore());
    MWWorld::Store<ESM::Creature> *creatureStore = const_cast<MWWorld::Store<ESM::Creature> *> (&esmStore->get<ESM::Creature>());

    creatureStore->insert(creature);
}
예제 #12
0
    void WaitDialog::startWaiting(int hoursToWait)
    {
        if(Settings::Manager::getBool("autosave","Saves")) //autosaves when enabled
            MWBase::Environment::get().getStateManager()->quickSave("Autosave");

        MWBase::World* world = MWBase::Environment::get().getWorld();
        MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.2f);
        mFadeTimeRemaining = 0.4f;
        setVisible(false);

        mHours = hoursToWait;

        // FIXME: move this somewhere else?
        mInterruptAt = -1;
        MWWorld::Ptr player = world->getPlayerPtr();
        if (mSleeping && player.getCell()->isExterior())
        {
            std::string regionstr = player.getCell()->getCell()->mRegion;
            if (!regionstr.empty())
            {
                const ESM::Region *region = world->getStore().get<ESM::Region>().find (regionstr);
                if (!region->mSleepList.empty())
                {
                    // figure out if player will be woken while sleeping
                    int x = Misc::Rng::rollDice(hoursToWait);
                    float fSleepRandMod = world->getStore().get<ESM::GameSetting>().find("fSleepRandMod")->getFloat();
                    if (x < fSleepRandMod * hoursToWait)
                    {
                        float fSleepRestMod = world->getStore().get<ESM::GameSetting>().find("fSleepRestMod")->getFloat();
                        int interruptAtHoursRemaining = int(fSleepRestMod * hoursToWait);
                        if (interruptAtHoursRemaining != 0)
                        {
                            mInterruptAt = hoursToWait - interruptAtHoursRemaining;
                            mInterruptCreatureList = region->mSleepList;
                        }
                    }
                }
            }
        }

        mProgressBar.setProgress (0, hoursToWait);
    }
예제 #13
0
    std::string InterpreterContext::getNPCRank() const
    {
        std::map<std::string, int> ranks = mReference.getClass().getNpcStats (mReference).getFactionRanks();
        std::map<std::string, int>::const_iterator it = ranks.begin();

        MWBase::World *world = MWBase::Environment::get().getWorld();
        const MWWorld::ESMStore &store = world->getStore();
        const ESM::Faction *faction = store.get<ESM::Faction>().find(it->first);

        return faction->mRanks[it->second];
    }
예제 #14
0
    void WaitDialog::startWaiting(int hoursToWait)
    {
        if(Settings::Manager::getBool("autosave","Saves") && mSleeping) //autosaves when enabled and sleeping
            MWBase::Environment::get().getStateManager()->quickSave("Autosave");

        MWBase::World* world = MWBase::Environment::get().getWorld();
        MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.2);
        setVisible(false);
        mProgressBar.setVisible (true);

        mWaiting = true;
        mCurHour = 0;
        mHours = hoursToWait;

        // FIXME: move this somewhere else?
        mInterruptAt = -1;
        MWWorld::Ptr player = world->getPlayerPtr();
        if (mSleeping && player.getCell()->isExterior())
        {
            std::string regionstr = player.getCell()->getCell()->mRegion;
            if (!regionstr.empty())
            {
                const ESM::Region *region = world->getStore().get<ESM::Region>().find (regionstr);
                if (!region->mSleepList.empty())
                {
                    float fSleepRandMod = world->getStore().get<ESM::GameSetting>().find("fSleepRandMod")->getFloat();
                    int x = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * hoursToWait; // [0, hoursRested]
                    float y = fSleepRandMod * hoursToWait;
                    if (x > y)
                    {
                        float fSleepRestMod = world->getStore().get<ESM::GameSetting>().find("fSleepRestMod")->getFloat();
                        mInterruptAt = hoursToWait - int(fSleepRestMod * hoursToWait);
                        mInterruptCreatureList = region->mSleepList;
                    }
                }
            }
        }

        mRemainingTime = 0.05;
        mProgressBar.setProgress (0, mHours);
    }
예제 #15
0
    std::string InterpreterContext::getNPCRank() const
    {
        const MWWorld::Ptr& ptr = getReferenceImp();
        std::string faction = ptr.getClass().getPrimaryFaction(ptr);
        if (faction.empty())
            throw std::runtime_error("getNPCRank(): NPC is not in a faction");

        int rank = ptr.getClass().getPrimaryFactionRank(ptr);
        if (rank < 0 || rank > 9)
            throw std::runtime_error("getNPCRank(): invalid rank");

        MWBase::World *world = MWBase::Environment::get().getWorld();
        const MWWorld::ESMStore &store = world->getStore();
        const ESM::Faction *fact = store.get<ESM::Faction>().find(faction);
        return fact->mRanks[rank];
    }
예제 #16
0
    Player::Player (MWRender::Player *renderer, const ESM::NPC *player, const MWBase::World& world) :
      mCellStore (0), mRenderer (renderer), mClass (0),
      mAutoMove (false), mForwardBackward (0)
    {
        mPlayer.base = player;
        mPlayer.ref.refID = "player";
        mName = player->name;
        mMale = !(player->flags & ESM::NPC::Female);
        mRace = player->race;

        float* playerPos = mPlayer.mData.getPosition().pos;
        playerPos[0] = playerPos[1] = playerPos[2] = 0;

        mPlayer.mData.setBaseNode(renderer->getNode());
        /// \todo Do not make a copy of classes defined in esm/p records.
        mClass = new ESM::Class (*world.getStore().classes.find (player->cls));
    }
예제 #17
0
void Pathgrid::enableCellPathgrid(const MWWorld::CellStore *store)
{
    MWBase::World* world = MWBase::Environment::get().getWorld();
    const ESM::Pathgrid *pathgrid =
        world->getStore().get<ESM::Pathgrid>().search(*store->getCell());
    if (!pathgrid) return;

    osg::Vec3f cellPathGridPos(0, 0, 0);
    if (store->getCell()->isExterior())
    {
        cellPathGridPos.x() = static_cast<float>(store->getCell()->mData.mX * ESM::Land::REAL_SIZE);
        cellPathGridPos.y() = static_cast<float>(store->getCell()->mData.mY * ESM::Land::REAL_SIZE);
    }

    osg::ref_ptr<osg::PositionAttitudeTransform> cellPathGrid = new osg::PositionAttitudeTransform;
    cellPathGrid->setPosition(cellPathGridPos);

    osg::ref_ptr<osg::Geode> lineGeode = new osg::Geode;
    osg::ref_ptr<osg::Geometry> lines = createPathgridLines(pathgrid);
    lineGeode->addDrawable(lines);

    osg::ref_ptr<osg::Geode> pointGeode = new osg::Geode;
    osg::ref_ptr<osg::Geometry> points = createPathgridPoints(pathgrid);
    pointGeode->addDrawable(points);

    cellPathGrid->addChild(lineGeode);
    cellPathGrid->addChild(pointGeode);

    mPathGridRoot->addChild(cellPathGrid);

    if (store->getCell()->isExterior())
    {
        mExteriorPathgridNodes[std::make_pair(store->getCell()->getGridX(), store->getCell()->getGridY())] = cellPathGrid;
    }
    else
    {
        assert(mInteriorPathgridNode == NULL);
        mInteriorPathgridNode = cellPathGrid;
    }
}
예제 #18
0
    float getHitChance(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, int skillValue)
    {
        MWMechanics::CreatureStats &stats = attacker.getClass().getCreatureStats(attacker);
        const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();

        MWBase::World *world = MWBase::Environment::get().getWorld();
        const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();

        float defenseTerm = 0;
        MWMechanics::CreatureStats& victimStats = victim.getClass().getCreatureStats(victim);
        if (victimStats.getFatigue().getCurrent() >= 0)
        {
            // Maybe we should keep an aware state for actors updated every so often instead of testing every time
            bool unaware = (!victimStats.getAiSequence().isInCombat())
                    && (attacker == getPlayer())
                    && (!MWBase::Environment::get().getMechanicsManager()->awarenessCheck(attacker, victim));
            if (!(victimStats.getKnockedDown() ||
                    victimStats.isParalyzed()
                    || unaware ))
            {
                defenseTerm = victimStats.getEvasion();
            }
            defenseTerm += std::min(100.f,
                                    gmst.find("fCombatInvisoMult")->getFloat() *
                                    victimStats.getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude());
            defenseTerm += std::min(100.f,
                                    gmst.find("fCombatInvisoMult")->getFloat() *
                                    victimStats.getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude());
        }
        float attackTerm = skillValue +
                          (stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) +
                          (stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f);
        attackTerm *= stats.getFatigueTerm();
        attackTerm += mageffects.get(ESM::MagicEffect::FortifyAttack).getMagnitude() -
                     mageffects.get(ESM::MagicEffect::Blind).getMagnitude();

        return round(attackTerm - defenseTerm);
    }
예제 #19
0
    void projectileHit(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, MWWorld::Ptr weapon, const MWWorld::Ptr &projectile,
                       const osg::Vec3f& hitPosition, float attackStrength)
    {
        MWBase::World *world = MWBase::Environment::get().getWorld();
        const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();

        if(victim.isEmpty() || !victim.getClass().isActor() || victim.getClass().getCreatureStats(victim).isDead())
            // Can't hit non-actors or dead actors
        {
            reduceWeaponCondition(0.f, false, weapon, attacker);
            return;
        }

        if(attacker == getPlayer())
            MWBase::Environment::get().getWindowManager()->setEnemy(victim);

        int weapskill = ESM::Skill::Marksman;
        if(!weapon.isEmpty())
            weapskill = weapon.getClass().getEquipmentSkill(weapon);

        int skillValue = attacker.getClass().getSkill(attacker,
                                           weapon.getClass().getEquipmentSkill(weapon));

        if (Misc::Rng::roll0to99() >= getHitChance(attacker, victim, skillValue))
        {
            victim.getClass().onHit(victim, 0.0f, false, projectile, attacker, false);
            MWMechanics::reduceWeaponCondition(0.f, false, weapon, attacker);
            return;
        }


        const unsigned char* attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
        float damage = attack[0] + ((attack[1]-attack[0])*attackStrength); // Bow/crossbow damage

        // Arrow/bolt damage
        // NB in case of thrown weapons, we are applying the damage twice since projectile == weapon
        attack = projectile.get<ESM::Weapon>()->mBase->mData.mChop;
        damage += attack[0] + ((attack[1]-attack[0])*attackStrength);

        adjustWeaponDamage(damage, weapon, attacker);
        reduceWeaponCondition(damage, true, weapon, attacker);

        if(attacker == getPlayer())
            attacker.getClass().skillUsageSucceeded(attacker, weapskill, 0);

        if (victim.getClass().getCreatureStats(victim).getKnockedDown())
            damage *= gmst.find("fCombatKODamageMult")->getFloat();

        // Apply "On hit" effect of the weapon
        bool appliedEnchantment = applyEnchantment(attacker, victim, weapon, hitPosition);
        if (weapon != projectile)
            appliedEnchantment = applyEnchantment(attacker, victim, projectile, hitPosition);

        if (damage > 0)
            MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);

        // Non-enchanted arrows shot at enemies have a chance to turn up in their inventory
        if (victim != getPlayer()
                && !appliedEnchantment)
        {
            float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->getFloat();
            if (Misc::Rng::rollProbability() < fProjectileThrownStoreChance / 100.f)
                victim.getClass().getContainerStore(victim).add(projectile, 1, victim);
        }

        victim.getClass().onHit(victim, damage, true, projectile, attacker, true);
    }
예제 #20
0
    bool AiCombat::reactionTimeActions(const MWWorld::Ptr& actor, CharacterController& characterController, 
        AiCombatStorage& storage, MWWorld::Ptr target)
    {
        MWMechanics::Movement& movement = storage.mMovement;

        if (isTargetMagicallyHidden(target))
        {
            storage.stopAttack();
            return false; // TODO: run away instead of doing nothing
        }

        const MWWorld::CellStore*& currentCell = storage.mCell;
        bool cellChange = currentCell && (actor.getCell() != currentCell);
        if(!currentCell || cellChange)
        {
            currentCell = actor.getCell();
        }

        const MWWorld::Class& actorClass = actor.getClass();
        actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);

        float& actionCooldown = storage.mActionCooldown;
        if (actionCooldown > 0)
            return false;

        float rangeAttack = 0;
        float rangeFollow = 0;
        boost::shared_ptr<Action>& currentAction = storage.mCurrentAction;
        if (characterController.readyToPrepareAttack())
        {
            currentAction = prepareNextAction(actor, target);
            actionCooldown = currentAction->getActionCooldown();
        }

        if (currentAction.get())
            currentAction->getCombatRange(rangeAttack, rangeFollow);

        // FIXME: consider moving this stuff to ActionWeapon::getCombatRange
        const ESM::Weapon *weapon = NULL;
        MWMechanics::WeaponType weaptype = WeapType_None;
        float weapRange = 1.0f;

        // Get weapon characteristics
        MWBase::World* world = MWBase::Environment::get().getWorld();
        if (actorClass.hasInventoryStore(actor))
        {
            //Get weapon range
            MWWorld::ContainerStoreIterator weaponSlot =
                MWMechanics::getActiveWeapon(actorClass.getCreatureStats(actor), actorClass.getInventoryStore(actor), &weaptype);

            if (weaptype == WeapType_HandToHand)
            {
                static float fHandToHandReach =
                    world->getStore().get<ESM::GameSetting>().find("fHandToHandReach")->getFloat();
                weapRange = fHandToHandReach;
            }
            else if (weaptype != WeapType_PickProbe && weaptype != WeapType_Spell && weaptype != WeapType_None)
            {
                // All other WeapTypes are actually weapons, so get<ESM::Weapon> is safe.
                weapon = weaponSlot->get<ESM::Weapon>()->mBase;
                weapRange = weapon->mData.mReach;
            }
            weapRange *= 100.0f;
        }
        else //is creature
        {
            weaptype = actorClass.getCreatureStats(actor).getDrawState() == DrawState_Spell ? WeapType_Spell : WeapType_HandToHand;
            weapRange = 150.0f; //TODO: use true attack range (the same problem in Creature::hit)
        }

        bool distantCombat = false;
        if (weaptype != WeapType_Spell)
        {
            // TODO: move to ActionWeapon
            if (weaptype == WeapType_BowAndArrow || weaptype == WeapType_Crossbow || weaptype == WeapType_Thrown)
            {
                rangeAttack = 1000;
                rangeFollow = 0; // not needed in ranged combat
                distantCombat = true;
            }
            else
            {
                rangeAttack = weapRange;
                rangeFollow = 300;
            }
        }
        else
        {
            distantCombat = (rangeAttack > 500);
        }

        
        bool& readyToAttack = storage.mReadyToAttack;
        // start new attack
        storage.startAttackIfReady(actor, characterController, weapon, distantCombat);

        /*
         * Some notes on meanings of variables:
         *
         * rangeAttack:
         *
         *  - Distance where attack using the actor's weapon is possible:
         *    longer for ranged weapons (obviously?) vs. melee weapons
         *  - Determined by weapon's reach parameter; hardcoded value
         *    for ranged weapon and for creatures
         *  - Once within this distance mFollowTarget is triggered
         *
         * rangeFollow:
         *
         *  - Applies to melee weapons or hand to hand only (or creatures without
         *    weapons)
         *  - Distance a little further away than the actor's weapon reach
         *    i.e. rangeFollow > rangeAttack for melee weapons
         *  - Hardcoded value (0 for ranged weapons)
         *  - Once the target gets beyond this distance mFollowTarget is cleared
         *    and a path to the target needs to be found
         *
         * mFollowTarget:
         *
         *  - Once triggered, the actor follows the target with LOS shortcut
         *    (the shortcut really only applies to cells where pathgrids are
         *    available, since the default path without pathgrids is direct to
         *    target even if LOS is not achieved)
         */

        ESM::Position pos = actor.getRefData().getPosition();
        osg::Vec3f vActorPos(pos.asVec3());
        osg::Vec3f vTargetPos(target.getRefData().getPosition().asVec3());

        osg::Vec3f vAimDir = MWBase::Environment::get().getWorld()->aimToTarget(actor, target);
        float distToTarget = MWBase::Environment::get().getWorld()->getHitDistance(actor, target);
        
        osg::Vec3f& lastActorPos = storage.mLastActorPos;
        bool& followTarget = storage.mFollowTarget;

        bool isStuck = false;
        float speed = 0.0f;
        if(movement.mPosition[1] && (lastActorPos - vActorPos).length() < (speed = actorClass.getSpeed(actor)) * REACTION_INTERVAL / 2)
            isStuck = true;

        lastActorPos = vActorPos;

        // check if actor can move along z-axis
        bool canMoveByZ = (actorClass.canSwim(actor) && world->isSwimming(actor))
            || world->isFlying(actor);

        // can't fight if attacker can't go where target is.  E.g. A fish can't attack person on land.
        if (distToTarget >= rangeAttack
                && !actorClass.isNpc() && !MWMechanics::isEnvironmentCompatible(actor, target))
        {
            // TODO: start fleeing?
            storage.stopAttack();
            return false;
        }

        // for distant combat we should know if target is in LOS even if distToTarget < rangeAttack 
        bool inLOS = distantCombat ? world->getLOS(actor, target) : true;

        // (within attack dist) || (not quite attack dist while following)
        if(inLOS && (distToTarget < rangeAttack || (distToTarget <= rangeFollow && followTarget && !isStuck)))
        {
            mPathFinder.clearPath();
            //Melee and Close-up combat
            
            // getXAngleToDir determines vertical angle to target:
            // if actor can move along z-axis it will control movement dir
            // if can't - it will control correct aiming.
            // note: in getZAngleToDir if we preserve dir.z then horizontal angle can be inaccurate
            if (distantCombat)
            {
                osg::Vec3f& lastTargetPos = storage.mLastTargetPos;
                vAimDir = AimDirToMovingTarget(actor, target, lastTargetPos, REACTION_INTERVAL, weaptype,
                    storage.mStrength);
                lastTargetPos = vTargetPos;
                movement.mRotation[0] = getXAngleToDir(vAimDir);
                movement.mRotation[2] = getZAngleToDir(vAimDir);
            }
            else
            {
                movement.mRotation[0] = getXAngleToDir(vAimDir);
                movement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos)); // using vAimDir results in spastic movements since the head is animated
            }

            // (not quite attack dist while following)
            if (followTarget && distToTarget > rangeAttack)
            {
                //Close-up combat: just run up on target
                storage.stopCombatMove();
                movement.mPosition[1] = 1;
            }
            else // (within attack dist)
            {
                storage.startCombatMove(actorClass.isNpc(), distantCombat, distToTarget, rangeAttack);

                readyToAttack = true;
                //only once got in melee combat, actor is allowed to use close-up shortcutting
                followTarget = true;
            }
        }
        else // remote pathfinding
        {
            bool preferShortcut = false;
            if (!distantCombat) inLOS = world->getLOS(actor, target);

            // check if shortcut is available
            bool& forceNoShortcut = storage.mForceNoShortcut;
            ESM::Position& shortcutFailPos = storage.mShortcutFailPos;
            
            if(inLOS && (!isStuck || readyToAttack)
                && (!forceNoShortcut || (shortcutFailPos.asVec3() - vActorPos).length() >= PATHFIND_SHORTCUT_RETRY_DIST))
            {
                if(speed == 0.0f) speed = actorClass.getSpeed(actor);
                // maximum dist before pit/obstacle for actor to avoid them depending on his speed
                float maxAvoidDist = REACTION_INTERVAL * speed + speed / MAX_VEL_ANGULAR_RADIANS * 2; // *2 - for reliability
                preferShortcut = checkWayIsClear(vActorPos, vTargetPos, osg::Vec3f(vAimDir.x(), vAimDir.y(), 0).length() > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2);
            }

            // don't use pathgrid when actor can move in 3 dimensions
            if (canMoveByZ)
            {
                preferShortcut = true;
                movement.mRotation[0] = getXAngleToDir(vAimDir);
            }

            if(preferShortcut)
            {
                movement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos));
                forceNoShortcut = false;
                shortcutFailPos.pos[0] = shortcutFailPos.pos[1] = shortcutFailPos.pos[2] = 0;
                mPathFinder.clearPath();
            }
            else // if shortcut failed stick to path grid
            {
                if(!isStuck && shortcutFailPos.pos[0] == 0.0f && shortcutFailPos.pos[1] == 0.0f && shortcutFailPos.pos[2] == 0.0f)
                {
                    forceNoShortcut = true;
                    shortcutFailPos = pos;
                }

                followTarget = false;

                buildNewPath(actor, target);

                // should always return a path (even if it's just go straight on target.)
                assert(mPathFinder.isPathConstructed());
            }

            if (readyToAttack)
            {
                // to stop possible sideway moving after target moved out of attack range
                storage.stopCombatMove();
                readyToAttack = false;
            }
            movement.mPosition[1] = 1;
        }

        return false;
    }
예제 #21
0
파일: combat.cpp 프로젝트: Chiur/openmw
    void projectileHit(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, MWWorld::Ptr weapon, const MWWorld::Ptr &projectile,
                       const Ogre::Vector3& hitPosition)
    {
        MWBase::World *world = MWBase::Environment::get().getWorld();
        const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();

        MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker);

        const MWWorld::Class &othercls = victim.getClass();
        if(!othercls.isActor()) // Can't hit non-actors
            return;
        MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim);
        if(otherstats.isDead()) // Can't hit dead actors
            return;

        if(attacker.getRefData().getHandle() == "player")
            MWBase::Environment::get().getWindowManager()->setEnemy(victim);

        int weapskill = ESM::Skill::Marksman;
        if(!weapon.isEmpty())
            weapskill = weapon.getClass().getEquipmentSkill(weapon);

        float skillValue = attacker.getClass().getSkill(attacker,
                                           weapon.getClass().getEquipmentSkill(weapon));

        if((::rand()/(RAND_MAX+1.0)) > getHitChance(attacker, victim, skillValue)/100.0f)
        {
            victim.getClass().onHit(victim, 0.0f, false, projectile, attacker, false);
            return;
        }

        float damage = 0.0f;

        float fDamageStrengthBase = gmst.find("fDamageStrengthBase")->getFloat();
        float fDamageStrengthMult = gmst.find("fDamageStrengthMult")->getFloat();

        const unsigned char* attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
        damage = attack[0] + ((attack[1]-attack[0])*attackerStats.getAttackStrength()); // Bow/crossbow damage
        if (weapon != projectile)
        {
            // Arrow/bolt damage
            attack = projectile.get<ESM::Weapon>()->mBase->mData.mChop;
            damage += attack[0] + ((attack[1]-attack[0])*attackerStats.getAttackStrength());
        }

        damage *= fDamageStrengthBase +
                (attackerStats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1);

        if(attacker.getRefData().getHandle() == "player")
            attacker.getClass().skillUsageSucceeded(attacker, weapskill, 0);

        bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(attacker, victim);
        if(!detected)
        {
            damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat();
            MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
            MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
        }
        if (victim.getClass().getCreatureStats(victim).getKnockedDown())
            damage *= gmst.find("fCombatKODamageMult")->getFloat();

        // Apply "On hit" effect of the weapon
        applyEnchantment(attacker, victim, weapon, hitPosition);
        if (weapon != projectile)
            applyEnchantment(attacker, victim, projectile, hitPosition);

        if (damage > 0)
            MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);

        float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->getFloat();
        if ((::rand()/(RAND_MAX+1.0)) < fProjectileThrownStoreChance/100.f)
            victim.getClass().getContainerStore(victim).add(projectile, 1, victim);

        victim.getClass().onHit(victim, damage, true, projectile, attacker, true);
    }
예제 #22
0
    void LevelupDialog::open()
    {
        MWBase::World *world = MWBase::Environment::get().getWorld();
        MWWorld::Ptr player = world->getPlayerPtr();
        MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats(player);
        MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player);

        const ESM::NPC *playerData = player.get<ESM::NPC>()->mBase;

        // set class image
        const ESM::Class *cls =
            world->getStore().get<ESM::Class>().find(playerData->mClass);

        if(world->getStore().get<ESM::Class>().isDynamic(cls->mId))
        {
            // Vanilla uses thief.dds for custom classes.
            // Choosing Stealth specialization and Speed/Agility as attributes, if possible. Otherwise fall back to first class found.
            MWWorld::SharedIterator<ESM::Class> it = world->getStore().get<ESM::Class>().begin();
            for(; it != world->getStore().get<ESM::Class>().end(); it++)
            {
                if(it->mData.mIsPlayable && it->mData.mSpecialization == 2 && it->mData.mAttribute[0] == 4 && it->mData.mAttribute[1] == 3)
                    break;
            }
            if (it == world->getStore().get<ESM::Class>().end())
                it = world->getStore().get<ESM::Class>().begin();
            if (it != world->getStore().get<ESM::Class>().end())
                cls = &*it;
        }

        mClassImage->setImageTexture ("textures\\levelup\\" + cls->mId + ".dds");

        int level = creatureStats.getLevel ()+1;
        mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + boost::lexical_cast<std::string>(level));

        std::string levelupdescription;
        if(level > 20)
            levelupdescription=world->getFallback()->getFallbackString("Level_Up_Default");
        else
            levelupdescription=world->getFallback()->getFallbackString("Level_Up_Level"+boost::lexical_cast<std::string>(level));

        mLevelDescription->setCaption (levelupdescription);

        unsigned int availableAttributes = 0;
        for (int i = 0; i < 8; ++i)
        {
            MyGUI::TextBox* text = mAttributeMultipliers[i];
            if (pcStats.getAttribute(i).getBase() < 100)
            {
                mAttributes[i]->setEnabled(true);
                availableAttributes++;

                int mult = pcStats.getLevelupAttributeMultiplier (i);
                text->setCaption(mult <= 1 ? "" : "x" + boost::lexical_cast<std::string>(mult));
            }
            else
            {
                mAttributes[i]->setEnabled(false);

                text->setCaption("");
            }
        }

        mCoinCount = std::min(sMaxCoins, availableAttributes);

        for (unsigned int i = 0; i < sMaxCoins; i++)
        {
            if (i < mCoinCount)
                mCoins[i]->attachToWidget(mCoinBox);
            else
                mCoins[i]->detachFromWidget();
        }

        mSpentAttributes.clear();
        resetCoins();

        setAttributeValues();

        center();
    }
예제 #23
0
    bool AiTravel::execute (const MWWorld::Ptr& actor,float duration)
    {
        MWBase::World *world = MWBase::Environment::get().getWorld();
        ESM::Position pos = actor.getRefData().getPosition();
        Movement &movement = actor.getClass().getMovementSettings(actor);
        const ESM::Cell *cell = actor.getCell()->mCell;

        MWWorld::Ptr player = world->getPlayer().getPlayer();
        if(cell->mData.mX != player.getCell()->mCell->mData.mX)
        {
            int sideX = sgn(cell->mData.mX - player.getCell()->mCell->mData.mX);
            //check if actor is near the border of an inactive cell. If so, stop walking.
            if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) >
               sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
            {
                movement.mPosition[1] = 0;
                return false;
            }
        }
        if(cell->mData.mY != player.getCell()->mCell->mData.mY)
        {
            int sideY = sgn(cell->mData.mY - player.getCell()->mCell->mData.mY);
            //check if actor is near the border of an inactive cell. If so, stop walking.
            if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) >
               sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
            {
                movement.mPosition[1] = 0;
                return false;
            }
        }

        const ESM::Pathgrid *pathgrid = world->getStore().get<ESM::Pathgrid>().search(*cell);
        bool cellChange = cell->mData.mX != cellX || cell->mData.mY != cellY;
        if(!mPathFinder.isPathConstructed() || cellChange)
        {
            cellX = cell->mData.mX;
            cellY = cell->mData.mY;
            float xCell = 0;
            float yCell = 0;

            if(cell->isExterior())
            {
                xCell = cell->mData.mX * ESM::Land::REAL_SIZE;
                yCell = cell->mData.mY * ESM::Land::REAL_SIZE;
            }

            ESM::Pathgrid::Point dest;
            dest.mX = mX;
            dest.mY = mY;
            dest.mZ = mZ;

            ESM::Pathgrid::Point start;
            start.mX = pos.pos[0];
            start.mY = pos.pos[1];
            start.mZ = pos.pos[2];

            mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true);
        }

        if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
        {
            movement.mPosition[1] = 0;
            return true;
        }

        float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
        // TODO: use movement settings instead of rotating directly
        world->rotateObject(actor, 0, 0, zAngle, false);
        movement.mPosition[1] = 1;

        return false;
    }
예제 #24
0
    /*
     * Current AiCombat movement states (as of 0.29.0), ignoring the details of the
     * attack states such as CombatMove, Strike and ReadyToAttack:
     *
     *    +----(within strike range)----->attack--(beyond strike range)-->follow
     *    |                                 | ^                            | |
     *    |                                 | |                            | |
     *  pursue<---(beyond follow range)-----+ +----(within strike range)---+ |
     *    ^                                                                  |
     *    |                                                                  |
     *    +-------------------------(beyond follow range)--------------------+
     *
     *
     * Below diagram is high level only, the code detail is a little different
     * (but including those detail will just complicate the diagram w/o adding much)
     *
     *    +----------(same)-------------->attack---------(same)---------->follow
     *    |                                 |^^                            |||
     *    |                                 |||                            |||
     *    |       +--(same)-----------------+|+----------(same)------------+||
     *    |       |                          |                              ||
     *    |       |                          | (in range)                   ||
     *    |   <---+         (too far)        |                              ||
     *  pursue<-------------------------[door open]<-----+                  ||
     *    ^^^                                            |                  ||
     *    |||                                            |                  ||
     *    ||+----------evade-----+                       |                  ||
     *    ||                     |    [closed door]      |                  ||
     *    |+----> maybe stuck, check --------------> back up, check door    ||
     *    |         ^   |   ^                          |   ^                ||
     *    |         |   |   |                          |   |                ||
     *    |         |   +---+                          +---+                ||
     *    |         +-------------------------------------------------------+|
     *    |                                                                  |
     *    +---------------------------(same)---------------------------------+
     *
     * FIXME:
     *
     * The new scheme is way too complicated, should really be implemented as a
     * proper state machine.
     *
     * TODO:
     *
     * Use the Observer Pattern to co-ordinate attacks, provide intelligence on
     * whether the target was hit, etc.
     */
    bool AiCombat::execute (const MWWorld::Ptr& actor, AiState& state, float duration)
    {
        // get or create temporary storage
        AiCombatStorage& storage = state.get<AiCombatStorage>();
        

        
        //General description
        if(actor.getClass().getCreatureStats(actor).isDead())
            return true;

        MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
        if (target.isEmpty())
            return false;

        if(!target.getRefData().getCount() || !target.getRefData().isEnabled()  // Really we should be checking whether the target is currently registered
                                                                                // with the MechanicsManager
                || target.getClass().getCreatureStats(target).isDead())
            return true;

        const MWWorld::Class& actorClass = actor.getClass();
        MWBase::World* world = MWBase::Environment::get().getWorld();


        //Update every frame
        bool& combatMove = storage.mCombatMove;
        float& timerCombatMove = storage.mTimerCombatMove; 
        MWMechanics::Movement& movement = storage.mMovement;
        if(combatMove)
        {
            timerCombatMove -= duration;
            if( timerCombatMove <= 0)
            {
                timerCombatMove = 0;
                movement.mPosition[1] = movement.mPosition[0] = 0;
                combatMove = false;
            }
        }

        actorClass.getMovementSettings(actor) = movement;
        actorClass.getMovementSettings(actor).mRotation[0] = 0;
        actorClass.getMovementSettings(actor).mRotation[2] = 0;

        if(movement.mRotation[2] != 0)
        {
            if(zTurn(actor, Ogre::Degree(movement.mRotation[2]))) movement.mRotation[2] = 0;
        }

        if(movement.mRotation[0] != 0)
        {
            if(smoothTurn(actor, Ogre::Degree(movement.mRotation[0]), 0)) movement.mRotation[0] = 0;
        }

        //TODO: Some skills affect period of strikes.For berserk-like style period ~ 0.25f
        float attacksPeriod = 1.0f;

        ESM::Weapon::AttackType attackType;


        
        
        bool& attack = storage.mAttack;
        bool& readyToAttack = storage.mReadyToAttack;
        float& timerAttack = storage.mTimerAttack;
        
        bool& minMaxAttackDurationInitialised = storage.mMinMaxAttackDurationInitialised;
        float (&minMaxAttackDuration)[3][2] = storage.mMinMaxAttackDuration;
        
        if(readyToAttack)
        {
            if (!minMaxAttackDurationInitialised)
            {
                // TODO: this must be updated when a different weapon is equipped
                getMinMaxAttackDuration(actor, minMaxAttackDuration);
                minMaxAttackDurationInitialised = true;
            }

            if (timerAttack < 0) attack = false;

            timerAttack -= duration;
        }
        else
        {
            timerAttack = -attacksPeriod;
            attack = false;
        }

        actorClass.getCreatureStats(actor).setAttackingOrSpell(attack);


        float& actionCooldown = storage.mActionCooldown;
        actionCooldown -= duration;
        
        float& timerReact = storage.mTimerReact;
        float tReaction = 0.25f;
        if(timerReact < tReaction)
        {
            timerReact += duration;
            return false;
        }

        //Update with period = tReaction

        // Stop attacking if target is not seen
        if (target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude() > 0
                || target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude() > 75)
        {
            movement.mPosition[1] = movement.mPosition[0] = 0;
            return false; // TODO: run away instead of doing nothing
        }

        timerReact = 0;
        const MWWorld::CellStore*& currentCell = storage.mCell;
        bool cellChange = currentCell && (actor.getCell() != currentCell);
        if(!currentCell || cellChange)
        {
            currentCell = actor.getCell();
        }

        MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(actor);
        if (!anim) // shouldn't happen
            return false;

        actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);

        if (actionCooldown > 0)
            return false;

        float rangeAttack = 0;
        float rangeFollow = 0;
        boost::shared_ptr<Action>& currentAction = storage.mCurrentAction;
        if (anim->upperBodyReady())
        {
            currentAction = prepareNextAction(actor, target);
            actionCooldown = currentAction->getActionCooldown();
        }

        if (currentAction.get())
            currentAction->getCombatRange(rangeAttack, rangeFollow);

        // FIXME: consider moving this stuff to ActionWeapon::getCombatRange
        const ESM::Weapon *weapon = NULL;
        MWMechanics::WeaponType weaptype = WeapType_None;
        float weapRange = 1.0f;

        // Get weapon characteristics
        if (actorClass.hasInventoryStore(actor))
        {
            // TODO: Check equipped weapon and equip a different one if we can't attack with it
            // (e.g. no ammunition, or wrong type of ammunition equipped, etc. autoEquip is not very smart in this regard))

            //Get weapon speed and range
            MWWorld::ContainerStoreIterator weaponSlot =
                MWMechanics::getActiveWeapon(actorClass.getCreatureStats(actor), actorClass.getInventoryStore(actor), &weaptype);

            if (weaptype == WeapType_HandToHand)
            {
                static float fHandToHandReach =
                    world->getStore().get<ESM::GameSetting>().find("fHandToHandReach")->getFloat();
                weapRange = fHandToHandReach;
            }
            else if (weaptype != WeapType_PickProbe && weaptype != WeapType_Spell && weaptype != WeapType_None)
            {
                // All other WeapTypes are actually weapons, so get<ESM::Weapon> is safe.
                weapon = weaponSlot->get<ESM::Weapon>()->mBase;
                weapRange = weapon->mData.mReach;
            }
            weapRange *= 100.0f;
        }
        else //is creature
        {
            weaptype = actorClass.getCreatureStats(actor).getDrawState() == DrawState_Spell ? WeapType_Spell : WeapType_HandToHand;
            weapRange = 150.0f; //TODO: use true attack range (the same problem in Creature::hit)
        }

        bool distantCombat = false;
        if (weaptype != WeapType_Spell)
        {
            // TODO: move to ActionWeapon
            if (weaptype == WeapType_BowAndArrow || weaptype == WeapType_Crossbow || weaptype == WeapType_Thrown)
            {
                rangeAttack = 1000;
                rangeFollow = 0; // not needed in ranged combat
                distantCombat = true;
            }
            else
            {
                rangeAttack = weapRange;
                rangeFollow = 300;
            }
        }
        else
        {
            distantCombat = (rangeAttack > 500);
            weapRange = 150.f;
        }

        
        float& strength = storage.mStrength;      
        // start new attack
        if(readyToAttack)
        {
            if(timerAttack <= -attacksPeriod)
            {
                attack = true; // attack starts just now

                if (!distantCombat) attackType = chooseBestAttack(weapon, movement);
                else attackType = ESM::Weapon::AT_Chop; // cause it's =0

                strength = OEngine::Misc::Rng::rollClosedProbability();

                // Note: may be 0 for some animations
                timerAttack = minMaxAttackDuration[attackType][0] + 
                    (minMaxAttackDuration[attackType][1] - minMaxAttackDuration[attackType][0]) * strength;

                //say a provoking combat phrase
                if (actor.getClass().isNpc())
                {
                    const MWWorld::ESMStore &store = world->getStore();
                    int chance = store.get<ESM::GameSetting>().find("iVoiceAttackOdds")->getInt();
                    if (OEngine::Misc::Rng::roll0to99() < chance)
                    {
                        MWBase::Environment::get().getDialogueManager()->say(actor, "attack");
                    }
                }
            }
        }


        /*
         * Some notes on meanings of variables:
         *
         * rangeAttack:
         *
         *  - Distance where attack using the actor's weapon is possible:
         *    longer for ranged weapons (obviously?) vs. melee weapons
         *  - Determined by weapon's reach parameter; hardcoded value
         *    for ranged weapon and for creatures
         *  - Once within this distance mFollowTarget is triggered
         *
         * rangeFollow:
         *
         *  - Applies to melee weapons or hand to hand only (or creatures without
         *    weapons)
         *  - Distance a little further away than the actor's weapon reach
         *    i.e. rangeFollow > rangeAttack for melee weapons
         *  - Hardcoded value (0 for ranged weapons)
         *  - Once the target gets beyond this distance mFollowTarget is cleared
         *    and a path to the target needs to be found
         *
         * mFollowTarget:
         *
         *  - Once triggered, the actor follows the target with LOS shortcut
         *    (the shortcut really only applies to cells where pathgrids are
         *    available, since the default path without pathgrids is direct to
         *    target even if LOS is not achieved)
         */

        ESM::Position pos = actor.getRefData().getPosition();
        Ogre::Vector3 vActorPos(pos.pos);
        Ogre::Vector3 vTargetPos(target.getRefData().getPosition().pos);
        Ogre::Vector3 vDirToTarget = vTargetPos - vActorPos;
        float distToTarget = vDirToTarget.length();
        
        Ogre::Vector3& lastActorPos = storage.mLastActorPos;
        bool& followTarget = storage.mFollowTarget;

        bool isStuck = false;
        float speed = 0.0f;
        if(movement.mPosition[1] && (lastActorPos - vActorPos).length() < (speed = actorClass.getSpeed(actor)) * tReaction / 2)
            isStuck = true;

        lastActorPos = vActorPos;

        // check if actor can move along z-axis
        bool canMoveByZ = (actorClass.canSwim(actor) && world->isSwimming(actor))
            || world->isFlying(actor);

        // for distant combat we should know if target is in LOS even if distToTarget < rangeAttack 
        bool inLOS = distantCombat ? world->getLOS(actor, target) : true;

        // can't fight if attacker can't go where target is.  E.g. A fish can't attack person on land.
        if (distToTarget >= rangeAttack
                && !actorClass.isNpc() && !MWMechanics::isEnvironmentCompatible(actor, target))
        {
            // TODO: start fleeing?
            movement.mPosition[0] = 0;
            movement.mPosition[1] = 0;
            movement.mPosition[2] = 0;
            readyToAttack = false;
            actorClass.getCreatureStats(actor).setAttackingOrSpell(false);
            return false;
        }

        // (within attack dist) || (not quite attack dist while following)
        if(inLOS && (distToTarget < rangeAttack || (distToTarget <= rangeFollow && followTarget && !isStuck)))
        {
            //Melee and Close-up combat
            
            // getXAngleToDir determines vertical angle to target:
            // if actor can move along z-axis it will control movement dir
            // if can't - it will control correct aiming.
            // note: in getZAngleToDir if we preserve dir.z then horizontal angle can be inaccurate
            if (distantCombat)
            {
                Ogre::Vector3& lastTargetPos = storage.mLastTargetPos;
                Ogre::Vector3 vAimDir = AimDirToMovingTarget(actor, target, lastTargetPos, tReaction, weaptype, strength);
                lastTargetPos = vTargetPos;
                movement.mRotation[0] = getXAngleToDir(vAimDir);
                movement.mRotation[2] = getZAngleToDir(vAimDir);
            }
            else
            {
                movement.mRotation[0] = getXAngleToDir(vDirToTarget, distToTarget);
                movement.mRotation[2] = getZAngleToDir(vDirToTarget);
            }

            // (not quite attack dist while following)
            if (followTarget && distToTarget > rangeAttack)
            {
                //Close-up combat: just run up on target
                movement.mPosition[1] = 1;
            }
            else // (within attack dist)
            {
                if(movement.mPosition[0] || movement.mPosition[1])
                {
                    timerCombatMove = 0.1f + 0.1f * OEngine::Misc::Rng::rollClosedProbability();
                    combatMove = true;
                }
                // only NPCs are smart enough to use dodge movements
                else if(actorClass.isNpc() && (!distantCombat || (distantCombat && distToTarget < rangeAttack/2)))
                {
                    //apply sideway movement (kind of dodging) with some probability
                    if (OEngine::Misc::Rng::rollClosedProbability() < 0.25)
                    {
                        movement.mPosition[0] = OEngine::Misc::Rng::rollProbability() < 0.5 ? 1.0f : -1.0f;
                        timerCombatMove = 0.05f + 0.15f * OEngine::Misc::Rng::rollClosedProbability();
                        combatMove = true;
                    }
                }

                if(distantCombat && distToTarget < rangeAttack/4)
                {
                    movement.mPosition[1] = -1;
                }

                readyToAttack = true;
                //only once got in melee combat, actor is allowed to use close-up shortcutting
                followTarget = true;
            }
        }
        else // remote pathfinding
        {
            bool preferShortcut = false;
            if (!distantCombat) inLOS = world->getLOS(actor, target);

            // check if shortcut is available
            bool& forceNoShortcut = storage.mForceNoShortcut;
            ESM::Position& shortcutFailPos = storage.mShortcutFailPos;
            
            if(inLOS && (!isStuck || readyToAttack)
                && (!forceNoShortcut || (Ogre::Vector3(shortcutFailPos.pos) - vActorPos).length() >= PATHFIND_SHORTCUT_RETRY_DIST))
            {
                if(speed == 0.0f) speed = actorClass.getSpeed(actor);
                // maximum dist before pit/obstacle for actor to avoid them depending on his speed
                float maxAvoidDist = tReaction * speed + speed / MAX_VEL_ANGULAR.valueRadians() * 2; // *2 - for reliability
				preferShortcut = checkWayIsClear(vActorPos, vTargetPos, Ogre::Vector3(vDirToTarget.x, vDirToTarget.y, 0).length() > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2);
            }

            // don't use pathgrid when actor can move in 3 dimensions
            if(canMoveByZ) preferShortcut = true;

            if(preferShortcut)
            {
                if (canMoveByZ)
                    movement.mRotation[0] = getXAngleToDir(vDirToTarget, distToTarget);
                movement.mRotation[2] = getZAngleToDir(vDirToTarget);
                forceNoShortcut = false;
                shortcutFailPos.pos[0] = shortcutFailPos.pos[1] = shortcutFailPos.pos[2] = 0;
                mPathFinder.clearPath();
            }
            else // if shortcut failed stick to path grid
            {
                if(!isStuck && shortcutFailPos.pos[0] == 0.0f && shortcutFailPos.pos[1] == 0.0f && shortcutFailPos.pos[2] == 0.0f)
                {
                    forceNoShortcut = true;
                    shortcutFailPos = pos;
                }

                followTarget = false;

                buildNewPath(actor, target); //may fail to build a path, check before use

                //delete visited path node
                mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1]);

                // This works on the borders between the path grid and areas with no waypoints.
                if(inLOS && mPathFinder.getPath().size() > 1)
                {
                    // get point just before target
                    std::list<ESM::Pathgrid::Point>::const_iterator pntIter = --mPathFinder.getPath().end();
                    --pntIter;
                    Ogre::Vector3 vBeforeTarget(PathFinder::MakeOgreVector3(*pntIter));

                    // if current actor pos is closer to target then last point of path (excluding target itself) then go straight on target
                    if(distToTarget <= (vTargetPos - vBeforeTarget).length())
                    {
                        movement.mRotation[2] = getZAngleToDir(vDirToTarget);
                        preferShortcut = true;
                    }
                }

                // if there is no new path, then go straight on target
                if(!preferShortcut)
                {
                    if(!mPathFinder.getPath().empty())
                        movement.mRotation[2] = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
                    else
                        movement.mRotation[2] = getZAngleToDir(vDirToTarget);
                }
            }

            movement.mPosition[1] = 1;
            if (readyToAttack)
            {
                // to stop possible sideway moving after target moved out of attack range
                combatMove = true;
                timerCombatMove = 0;
            }
            readyToAttack = false;
        }

        if(!isStuck && distToTarget > rangeAttack && !distantCombat)
        {
            //special run attack; it shouldn't affect melee combat tactics
            if(actorClass.getMovementSettings(actor).mPosition[1] == 1)
            {
                /*  check if actor can overcome the distance = distToTarget - attackerWeapRange
                    less than in time of swinging with weapon (t_swing), then start attacking 
                */
                float speed1 = actorClass.getSpeed(actor);
                float speed2 = target.getClass().getSpeed(target);
                if(target.getClass().getMovementSettings(target).mPosition[0] == 0
                        && target.getClass().getMovementSettings(target).mPosition[1] == 0)
                    speed2 = 0;

                float s1 = distToTarget - weapRange;
                float t = s1/speed1;
                float s2 = speed2 * t;
                float t_swing = 
                    minMaxAttackDuration[ESM::Weapon::AT_Thrust][0] + 
                    (minMaxAttackDuration[ESM::Weapon::AT_Thrust][1] - minMaxAttackDuration[ESM::Weapon::AT_Thrust][0]) * OEngine::Misc::Rng::rollClosedProbability();

                if (t + s2/speed1 <= t_swing)
                {
                    readyToAttack = true;
                    if(timerAttack <= -attacksPeriod)
                    {
                        timerAttack = t_swing;
                        attack = true;
                    }
                }
            }
        }

        // NOTE: This section gets updated every tReaction, which is currently hard
        //       coded at 250ms or 1/4 second
        //
        // TODO: Add a parameter to vary DURATION_SAME_SPOT?
        if((distToTarget > rangeAttack || followTarget) &&
            mObstacleCheck.check(actor, tReaction)) // check if evasive action needed
        {
            // probably walking into another NPC TODO: untested in combat situation
            // TODO: diagonal should have same animation as walk forward
            //       but doesn't seem to do that?
            actor.getClass().getMovementSettings(actor).mPosition[0] = 1;
            actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f;
            // change the angle a bit, too
            if(mPathFinder.isPathConstructed())
                zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1])));

            if(followTarget)
                followTarget = false;
            // FIXME: can fool actors to stay behind doors, etc.
            // Related to Bug#1102 and to some degree #1155 as well
        }

        return false;
    }
예제 #25
0
 std::string InterpreterContext::getPCRace() const
 {
     MWBase::World *world = MWBase::Environment::get().getWorld();
     std::string race = world->getPlayerPtr().get<ESM::NPC>()->mBase->mRace;
     return world->getStore().get<ESM::Race>().find(race)->mName;
 }
예제 #26
0
bool AiWander::execute (const MWWorld::Ptr& actor,float duration)
{
    if (actor.getClass().isNpc())
        actor.getClass().getNpcStats(actor).setDrawState(DrawState_Nothing);
    MWBase::World *world = MWBase::Environment::get().getWorld();
    if(mDuration)
    {
        // End package if duration is complete or mid-night hits:
        MWWorld::TimeStamp currentTime = world->getTimeStamp();
        if(currentTime.getHour() >= mStartTime.getHour() + mDuration)
        {
            if(!mRepeat)
            {
                stopWalking(actor);
                return true;
            }
            else
                mStartTime = currentTime;
        }
        else if(int(currentTime.getHour()) == 0 && currentTime.getDay() != mStartTime.getDay())
        {
            if(!mRepeat)
            {
                stopWalking(actor);
                return true;
            }
            else
                mStartTime = currentTime;
        }
    }

    ESM::Position pos = actor.getRefData().getPosition();

    if(!mStoredAvailableNodes)
    {
        mStoredAvailableNodes = true;
        mPathgrid = world->getStore().get<ESM::Pathgrid>().search(*actor.getCell()->mCell);

        mCellX = actor.getCell()->mCell->mData.mX;
        mCellY = actor.getCell()->mCell->mData.mY;

        if(!mPathgrid)
            mDistance = 0;
        else if(mPathgrid->mPoints.empty())
            mDistance = 0;

        if(mDistance)
        {
            mXCell = 0;
            mYCell = 0;
            if(actor.getCell()->mCell->isExterior())
            {
                mXCell = mCellX * ESM::Land::REAL_SIZE;
                mYCell = mCellY * ESM::Land::REAL_SIZE;
            }

            Ogre::Vector3 npcPos(actor.getRefData().getPosition().pos);
            npcPos[0] = npcPos[0] - mXCell;
            npcPos[1] = npcPos[1] - mYCell;

            for(unsigned int counter = 0; counter < mPathgrid->mPoints.size(); counter++)
            {
                Ogre::Vector3 nodePos(mPathgrid->mPoints[counter].mX, mPathgrid->mPoints[counter].mY,
                                      mPathgrid->mPoints[counter].mZ);
                if(npcPos.squaredDistance(nodePos) <= mDistance * mDistance)
                    mAllowedNodes.push_back(mPathgrid->mPoints[counter]);
            }
            if(!mAllowedNodes.empty())
            {
                Ogre::Vector3 firstNodePos(mAllowedNodes[0].mX, mAllowedNodes[0].mY, mAllowedNodes[0].mZ);
                float closestNode = npcPos.squaredDistance(firstNodePos);
                unsigned int index = 0;
                for(unsigned int counterThree = 1; counterThree < mAllowedNodes.size(); counterThree++)
                {
                    Ogre::Vector3 nodePos(mAllowedNodes[counterThree].mX, mAllowedNodes[counterThree].mY,
                                          mAllowedNodes[counterThree].mZ);
                    float tempDist = npcPos.squaredDistance(nodePos);
                    if(tempDist < closestNode)
                        index = counterThree;
                }
                mCurrentNode = mAllowedNodes[index];
                mAllowedNodes.erase(mAllowedNodes.begin() + index);
            }
        }
    }

    if(mAllowedNodes.empty())
        mDistance = 0;

    // Don't try to move if you are in a new cell (ie: positioncell command called) but still play idles.
    if(mDistance && (mCellX != actor.getCell()->mCell->mData.mX || mCellY != actor.getCell()->mCell->mData.mY))
        mDistance = 0;

    if(mChooseAction)
    {
        mPlayedIdle = 0;
        unsigned short idleRoll = 0;

        for(unsigned int counter = 0; counter < mIdle.size(); counter++)
        {
            unsigned short idleChance = mIdleChanceMultiplier * mIdle[counter];
            unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / mIdleChanceMultiplier));
            if(randSelect < idleChance && randSelect > idleRoll)
            {
                mPlayedIdle = counter+2;
                idleRoll = randSelect;
            }
        }

        if(!mPlayedIdle && mDistance)
        {
            mChooseAction = false;
            mMoveNow = true;
        }
        else
        {
            // Play idle animation and recreate vanilla (broken?) behavior of resetting start time of AIWander:
            MWWorld::TimeStamp currentTime = world->getTimeStamp();
            mStartTime = currentTime;
            playIdle(actor, mPlayedIdle);
            mChooseAction = false;
            mIdleNow = true;
        }
    }

    if(mIdleNow)
    {
        if(!checkIdle(actor, mPlayedIdle))
        {
            mPlayedIdle = 0;
            mIdleNow = false;
            mChooseAction = true;
        }
    }

    if(mMoveNow && mDistance)
    {
        if(!mPathFinder.isPathConstructed())
        {
            assert(mAllowedNodes.size());
            unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size());
            Ogre::Vector3 destNodePos(mAllowedNodes[randNode].mX, mAllowedNodes[randNode].mY, mAllowedNodes[randNode].mZ);

            ESM::Pathgrid::Point dest;
            dest.mX = destNodePos[0] + mXCell;
            dest.mY = destNodePos[1] + mYCell;
            dest.mZ = destNodePos[2];

            ESM::Pathgrid::Point start;
            start.mX = pos.pos[0];
            start.mY = pos.pos[1];
            start.mZ = pos.pos[2];

            mPathFinder.buildPath(start, dest, mPathgrid, mXCell, mYCell, false);

            if(mPathFinder.isPathConstructed())
            {
                // Remove this node as an option and add back the previously used node (stops NPC from picking the same node):
                ESM::Pathgrid::Point temp = mAllowedNodes[randNode];
                mAllowedNodes.erase(mAllowedNodes.begin() + randNode);
                mAllowedNodes.push_back(mCurrentNode);
                mCurrentNode = temp;

                mMoveNow = false;
                mWalking = true;
            }
            // Choose a different node and delete this one from possible nodes because it is uncreachable:
            else
                mAllowedNodes.erase(mAllowedNodes.begin() + randNode);
        }
    }

    if(mWalking)
    {
        float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
        // TODO: use movement settings instead of rotating directly
        world->rotateObject(actor, 0, 0, zAngle, false);
        MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;

        if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
        {
            stopWalking(actor);
            mMoveNow = false;
            mWalking = false;
            mChooseAction = true;
        }
    }

    return false;
}
예제 #27
0
 std::string InterpreterContext::getPCClass() const
 {
     MWBase::World *world = MWBase::Environment::get().getWorld();
     std::string class_ = world->getPlayerPtr().get<ESM::NPC>()->mBase->mClass;
     return world->getStore().get<ESM::Class>().find(class_)->mName;
 }
예제 #28
0
파일: combat.cpp 프로젝트: nthauvin/openmw
    void projectileHit(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, MWWorld::Ptr weapon, const MWWorld::Ptr &projectile,
                       const Ogre::Vector3& hitPosition)
    {
        MWBase::World *world = MWBase::Environment::get().getWorld();
        const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();

        MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker);

        if(victim.isEmpty() || !victim.getClass().isActor() || victim.getClass().getCreatureStats(victim).isDead())
            // Can't hit non-actors or dead actors
        {
            reduceWeaponCondition(0.f, false, weapon, attacker);
            return;
        }

        if(attacker.getRefData().getHandle() == "player")
            MWBase::Environment::get().getWindowManager()->setEnemy(victim);

        int weapskill = ESM::Skill::Marksman;
        if(!weapon.isEmpty())
            weapskill = weapon.getClass().getEquipmentSkill(weapon);

        float skillValue = attacker.getClass().getSkill(attacker,
                                           weapon.getClass().getEquipmentSkill(weapon));

        if((::rand()/(RAND_MAX+1.0)) > getHitChance(attacker, victim, skillValue)/100.0f)
        {
            victim.getClass().onHit(victim, 0.0f, false, projectile, attacker, false);
            MWMechanics::reduceWeaponCondition(0.f, false, weapon, attacker);
            return;
        }

        float damage = 0.0f;

        float fDamageStrengthBase = gmst.find("fDamageStrengthBase")->getFloat();
        float fDamageStrengthMult = gmst.find("fDamageStrengthMult")->getFloat();

        const unsigned char* attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
        damage = attack[0] + ((attack[1]-attack[0])*attackerStats.getAttackStrength()); // Bow/crossbow damage
        if (weapon != projectile)
        {
            // Arrow/bolt damage
            attack = projectile.get<ESM::Weapon>()->mBase->mData.mChop;
            damage += attack[0] + ((attack[1]-attack[0])*attackerStats.getAttackStrength());
        }

        damage *= fDamageStrengthBase +
                (attackerStats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1);

        adjustWeaponDamage(damage, weapon);
        reduceWeaponCondition(damage, true, weapon, attacker);

        if(attacker.getRefData().getHandle() == "player")
            attacker.getClass().skillUsageSucceeded(attacker, weapskill, 0);

        if (victim.getClass().getCreatureStats(victim).getKnockedDown())
            damage *= gmst.find("fCombatKODamageMult")->getFloat();

        // Apply "On hit" effect of the weapon
        applyEnchantment(attacker, victim, weapon, hitPosition);
        if (weapon != projectile)
            applyEnchantment(attacker, victim, projectile, hitPosition);

        if (damage > 0)
            MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);

        // Arrows shot at enemies have a chance to turn up in their inventory
        if (victim != MWBase::Environment::get().getWorld()->getPlayerPtr())
        {
            float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->getFloat();
            if ((::rand()/(RAND_MAX+1.0)) < fProjectileThrownStoreChance/100.f)
                victim.getClass().getContainerStore(victim).add(projectile, 1, victim);
        }

        victim.getClass().onHit(victim, damage, true, projectile, attacker, true);
    }
예제 #29
0
bool RecordHelper::doesCreatureExist(const std::string& refId)
{
    MWBase::World *world = MWBase::Environment::get().getWorld();

    return world->getStore().get<ESM::Creature>().search(refId);
}
예제 #30
0
    void LevelupDialog::open()
    {
        MWBase::World *world = MWBase::Environment::get().getWorld();
        MWWorld::Ptr player = world->getPlayerPtr();
        MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats(player);
        MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player);

        const ESM::NPC *playerData = player.get<ESM::NPC>()->mBase;

        // set class image
        const ESM::Class *cls =
            world->getStore().get<ESM::Class>().find(playerData->mClass);

        if(world->getStore().get<ESM::Class>().isDynamic(cls->mId))
        {
            // Choosing Stealth specialization and Speed/Agility as attributes, if possible. Otherwise fall back to first class found.
            MWWorld::SharedIterator<ESM::Class> it = world->getStore().get<ESM::Class>().begin();
            for(; it != world->getStore().get<ESM::Class>().end(); ++it)
            {
                if(it->mData.mIsPlayable && it->mData.mSpecialization == 2 && it->mData.mAttribute[0] == 4 && it->mData.mAttribute[1] == 3)
                    break;
            }
            if (it == world->getStore().get<ESM::Class>().end())
                it = world->getStore().get<ESM::Class>().begin();
            if (it != world->getStore().get<ESM::Class>().end())
                cls = &*it;
        }

        setClassImage(mClassImage, cls->mId);

        int level = creatureStats.getLevel ()+1;
        mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + MyGUI::utility::toString(level));

        std::string levelupdescription;
        if(level > 20)
            levelupdescription=world->getFallback()->getFallbackString("Level_Up_Default");
        else
            levelupdescription=world->getFallback()->getFallbackString("Level_Up_Level"+MyGUI::utility::toString(level));

        mLevelDescription->setCaption (levelupdescription);

        unsigned int availableAttributes = 0;
        for (int i = 0; i < 8; ++i)
        {
            MyGUI::TextBox* text = mAttributeMultipliers[i];
            if (pcStats.getAttribute(i).getBase() < 100)
            {
                mAttributes[i]->setEnabled(true);
                mAttributeValues[i]->setEnabled(true);
                availableAttributes++;

                int mult = pcStats.getLevelupAttributeMultiplier (i);
                mult = std::min(mult, 100-pcStats.getAttribute(i).getBase());
                text->setCaption(mult <= 1 ? "" : "x" + MyGUI::utility::toString(mult));
            }
            else
            {
                mAttributes[i]->setEnabled(false);
                mAttributeValues[i]->setEnabled(false);

                text->setCaption("");
            }
        }

        mCoinCount = std::min(sMaxCoins, availableAttributes);

        mSpentAttributes.clear();
        resetCoins();

        setAttributeValues();

        center();

        // Play LevelUp Music
        MWBase::Environment::get().getSoundManager()->streamMusic("Special/MW_Triumph.mp3");
    }