From d5f794d4fbd16ccf3d82e877e98f1d731ce84061 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Wed, 15 Jan 2014 22:56:55 +0200 Subject: [PATCH 1/7] update to combat ai behaviour --- apps/openmw/mwmechanics/actors.cpp | 3 +- apps/openmw/mwmechanics/aicombat.cpp | 392 ++++++++++++++++++++------ apps/openmw/mwmechanics/aicombat.hpp | 28 +- apps/openmw/mwmechanics/character.cpp | 5 +- apps/openmw/mwmechanics/character.hpp | 10 +- 5 files changed, 344 insertions(+), 94 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0a4adb6e2..8ce03f2b9 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -13,6 +13,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/actionequip.hpp" +#include "../mwworld/player.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -190,7 +191,7 @@ namespace MWMechanics && LOS ) { - creatureStats.getAiSequence().stack(AiCombat("player")); + creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayer().getPlayer())); creatureStats.setHostile(true); } } diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 32b0063b6..5588e8482 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -4,14 +4,19 @@ #include "../mwworld/class.hpp" #include "../mwworld/timestamp.hpp" -#include "../mwbase/world.hpp" + #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/dialoguemanager.hpp" + +#include "character.hpp" +#include "../mwworld/inventorystore.hpp" #include "creaturestats.hpp" #include "npcstats.hpp" #include +#include namespace { @@ -25,111 +30,309 @@ namespace namespace MWMechanics { - - AiCombat::AiCombat(const std::string &targetId) - :mTargetId(targetId),mTimer(0),mTimer2(0) + AiCombat::AiCombat(const MWWorld::Ptr& actor) : + mTarget(actor), + mTimerAttack(0), + mTimerReact(0), + mTimerCombatMove(0), + mCloseUp(false), + mReadyToAttack(false), + mStrike(false), + mCombatMove(false), + mMovement() { } bool AiCombat::execute (const MWWorld::Ptr& actor,float duration) { - if(!MWWorld::Class::get(actor).getCreatureStats(actor).isHostile()) return true; + //General description + if(!actor.getClass().getCreatureStats(actor).isHostile()) + return true; + if(actor.getClass().getCreatureStats(actor).getHealth().getCurrent() <= 0) + return true; - const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mTargetId, false); + //update every frame + determineAttackType(actor, mMovement); - if(MWWorld::Class::get(actor).getCreatureStats(actor).getHealth().getCurrent() <= 0) return true; - - if(actor.getTypeName() == typeid(ESM::NPC).name()) + if(mCombatMove) { - MWWorld::Class::get(actor). - MWWorld::Class::get(actor).setStance(actor, MWWorld::Class::Run,true); - MWMechanics::DrawState_ state = MWWorld::Class::get(actor).getNpcStats(actor).getDrawState(); - if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) - MWWorld::Class::get(actor).getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon); - //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true); + mTimerCombatMove -= duration; + if( mTimerCombatMove <= 0) + { + mTimerCombatMove = 0; + mMovement.mPosition[1] = mMovement.mPosition[0] = 0; + mCombatMove = false; + } } - ESM::Position pos = actor.getRefData().getPosition(); - const ESM::Pathgrid *pathgrid = - MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); + actor.getClass().getMovementSettings(actor) = mMovement; + - float xCell = 0; - float yCell = 0; + //actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mReadyToAttack); + mTimerAttack -= duration; + actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mStrike); - if (actor.getCell()->mCell->isExterior()) + float tReaction = 0.25f; + if(mTimerReact < tReaction) { - xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; - yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; + mTimerReact += duration; + return false; } - - ESM::Pathgrid::Point dest; - dest.mX = target.getRefData().getPosition().pos[0]; - dest.mY = target.getRefData().getPosition().pos[1]; - dest.mZ = target.getRefData().getPosition().pos[2]; - - ESM::Pathgrid::Point start; - start.mX = pos.pos[0]; - start.mY = pos.pos[1]; - start.mZ = pos.pos[2]; - - mTimer2 = mTimer2 + duration; - - if(!mPathFinder.isPathConstructed()) - mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, true); else { - mPathFinder2.buildPath(start, dest, pathgrid, xCell, yCell, true); - ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); - if((mTimer2 > 0.25)&&(mPathFinder2.getPathSize() < mPathFinder.getPathSize() || - (dest.mX - lastPt.mX)*(dest.mX - lastPt.mX)+(dest.mY - lastPt.mY)*(dest.mY - lastPt.mY)+(dest.mZ - lastPt.mZ)*(dest.mZ - lastPt.mZ) > 200*200)) - { - mTimer2 = 0; - mPathFinder = mPathFinder2; - } + mTimerReact = 0; } - mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); - - float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - - // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; - - - float range = 100; - MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); - if((dest.mX - start.mX)*(dest.mX - start.mX)+(dest.mY - start.mY)*(dest.mY - start.mY)+(dest.mZ - start.mZ)*(dest.mZ - start.mZ) - < range*range) + //actual attacking logic + //TODO: Some skills affect period of strikes.For berserk-like style period ~ 0.25f + float attackPeriod = 1.0f; + if(mReadyToAttack) { - float directionX = dest.mX - start.mX; - float directionY = dest.mY - start.mY; - float directionResult = sqrt(directionX * directionX + directionY * directionY); - - zAngle = Ogre::Radian( Ogre::Math::ACos(directionY / directionResult) * sgn(Ogre::Math::ASin(directionX / directionResult)) ).valueDegrees(); - // TODO: use movement settings instead of rotating directly - MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); - - mPathFinder.clearPath(); - - if(mTimer == 0) + if(mTimerAttack <= -attackPeriod) { - MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); - //mTimer = mTimer + duration; + //TODO: should depend on time between 'start' to 'min attack' + //for better controlling of NPCs' attack strength. + //Also it seems that this time is different for slash/thrust/chop + mTimerAttack = 0.35f * static_cast(rand())/RAND_MAX; + mStrike = true; + + //say a provoking combat phrase + if (actor.getClass().isNpc()) + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + int chance = store.get().find("iVoiceAttackOdds")->getInt(); + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (roll < chance) + { + MWBase::Environment::get().getDialogueManager()->say(actor, "attack"); + } + } } - if( mTimer > 1) + else if (mTimerAttack <= 0) + mStrike = false; + } + else + { + mTimerAttack = -attackPeriod; + mStrike = false; + } + + const MWWorld::Class &cls = actor.getClass(); + const ESM::Weapon *weapon = NULL; + MWMechanics::WeaponType weaptype; + float weapRange, weapSpeed = 1.0f; + if(actor.getTypeName() == typeid(ESM::NPC).name()) + { + actor.getClass().setStance(actor, MWWorld::Class::Run,true); + MWMechanics::DrawState_ state = actor.getClass().getNpcStats(actor).getDrawState(); + if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) + actor.getClass().getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon); + + //Get weapon speed and range + MWWorld::ContainerStoreIterator weaponSlot = + MWMechanics::getActiveWeapon(cls.getNpcStats(actor), cls.getInventoryStore(actor), &weaptype); + if (weaptype == WeapType_HandToHand) { - MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true); - mTimer = 0; + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + weapRange = gmst.find("fHandToHandReach")->getFloat(); } else { - mTimer = mTimer + duration; + weapon = weaponSlot->get()->mBase; + weapRange = weapon->mData.mReach; + weapSpeed = weapon->mData.mSpeed; + } + weapRange *= 100.0f; + } + + //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); + + ESM::Position pos = actor.getRefData().getPosition(); + + float zAngle; + + float rangeMelee; + float rangeCloseUp; + bool distantCombat = false; + if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_ThowWeapon) // || WeapType_Spell_OnTarget + { + rangeMelee = 1000; // TODO: should depend on archer skill + rangeCloseUp = 0; //doesn't needed when attacking from distance + distantCombat = true; + } + else + { + rangeMelee = weapRange; + rangeCloseUp = 300; + } + + Ogre::Vector3 vStart(pos.pos[0], pos.pos[1], pos.pos[2]); + ESM::Position targetPos = mTarget.getRefData().getPosition(); + Ogre::Vector3 vDest(targetPos.pos[0], targetPos.pos[1], targetPos.pos[2]); + Ogre::Vector3 vDir = vDest - vStart; + float distBetween = vDir.length(); + + if(distBetween < rangeMelee || (distBetween <= rangeCloseUp && mCloseUp) ) + { + //Melee and Close-up combat + vDir.z = 0; + float dirLen = vDir.length(); + zAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / dirLen) * sgn(Ogre::Math::ASin(vDir.x / dirLen)) ).valueDegrees(); + + // TODO: use movement settings instead of rotating directly + MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + + if(mPathFinder.isPathConstructed()) + mPathFinder.clearPath(); + + //MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + + //bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget); + if (mCloseUp && distBetween > rangeMelee) + { + //Close-up combat: just run up on target + mMovement.mPosition[1] = 1; + } + else + { + //Melee: stop running and attack + mMovement.mPosition[1] = 0; + chooseBestAttack(weapon, mMovement); + + if(mMovement.mPosition[0] != 0 || mMovement.mPosition[1]) + { + mTimerCombatMove = 0.1f + 0.1f * static_cast(rand())/RAND_MAX; + mCombatMove = true; + } + else if(!distantCombat || (distantCombat && rangeMelee/5)) + { + //apply sideway movement (kind of dodging) with some probability + if(static_cast(rand())/RAND_MAX < 0.25) + { + mMovement.mPosition[0] = static_cast(rand())/RAND_MAX < 0.5? 1: -1; + mTimerCombatMove = 0.05f + 0.15f * static_cast(rand())/RAND_MAX; + mCombatMove = true; + } + } + + if(distantCombat && distBetween < rangeMelee/4) + { + mMovement.mPosition[1] = -1; + } + + mReadyToAttack = true; + //only once got in melee combat, actor is allowed to use close-up shortcutting + mCloseUp = true; + } + } + else + { + //target is at far distance: build & follow the path + mCloseUp = false; + + buildNewPath(actor); + + //delete visited path node + mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); + + zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + + // TODO: use movement settings instead of rotating directly + MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); + //MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + mMovement.mPosition[1] = 1; + mReadyToAttack = false; + } + + if(distBetween > rangeMelee) + { + //special run attack; it shouldn't affect melee combat tactics + if(actor.getClass().getMovementSettings(actor).mPosition[1] == 1) + { + //check if actor can overcome the distance = distToTarget - attackerWeapRange + //less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing) + //then start attacking + float speed1 = cls.getSpeed(actor); + float speed2 = cls.getSpeed(mTarget); + if(actor.getClass().getMovementSettings(mTarget).mPosition[0] == 0 + && actor.getClass().getMovementSettings(mTarget).mPosition[1] == 0) + speed2 = 0; + + float s1 = distBetween - weapRange; + float t = s1/speed1; + float s2 = speed2 * t; + float t_swing = 0.17f/weapSpeed;//0.17 should be the time of playing weapon anim from 'start' to 'hit' tags + if (t + s2/speed1 <= t_swing) + { + mReadyToAttack = true; + if(mTimerAttack <= -attackPeriod) + { + mTimerAttack = 0.45f*static_cast(rand())/RAND_MAX; + mStrike = true; + } + } + } + } + + return false; + } + + void AiCombat::buildNewPath(const MWWorld::Ptr& actor) + { + //Construct path to target + ESM::Pathgrid::Point dest; + dest.mX = mTarget.getRefData().getPosition().pos[0]; + dest.mY = mTarget.getRefData().getPosition().pos[1]; + dest.mZ = mTarget.getRefData().getPosition().pos[2]; + Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ); + + ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); + Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); + float dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); + + float targetPosThreshold; + bool isOutside = actor.getCell()->mCell->isExterior(); + if (isOutside) + targetPosThreshold = 300; + else + targetPosThreshold = 100; + + if(dist > targetPosThreshold) + { + //construct new path only if target has moved away more than on + ESM::Position pos = actor.getRefData().getPosition(); + + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; + start.mZ = pos.pos[2]; + + const ESM::Pathgrid *pathgrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*actor.getCell()->mCell); + + float xCell = 0; + float yCell = 0; + + if (actor.getCell()->mCell->isExterior()) + { + xCell = actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE; + yCell = actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE; } - MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; - //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(!MWWorld::Class::get(actor).getCreatureStats(actor).getAttackingOrSpell()); + if(!mPathFinder.isPathConstructed()) + mPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, isOutside); + else + { + PathFinder newPathFinder; + newPathFinder.buildPath(start, dest, pathgrid, xCell, yCell, isOutside); + + //TO EXPLORE: + //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path, + //not the actual path length. Here we should know if the new path is actually more effective. + //if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) + mPathFinder = newPathFinder; + } } - return false; } int AiCombat::getTypeId() const @@ -146,5 +349,36 @@ namespace MWMechanics { return new AiCombat(*this); } + + static void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) + { + if (movement.mPosition[0] && !movement.mPosition[1]) //sideway + actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); + else if (movement.mPosition[1]) //forward + actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Thrust); + else + actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); + } + + static void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) + { + if (weapon == NULL) + return; + + int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; + int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; + int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; + + float total = slash + chop + thrust; + + if(static_cast(rand())/RAND_MAX <= static_cast(slash)/total) + { + movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; + movement.mPosition[1] = 0; + } + if (static_cast(rand())/RAND_MAX <= static_cast(thrust)/total) + movement.mPosition[1] = 1; + //else chop + } } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index fa71e261f..f6a0c85c3 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -7,12 +7,14 @@ #include "movement.hpp" +#include "../mwbase/world.hpp" + namespace MWMechanics { class AiCombat : public AiPackage { public: - AiCombat(const std::string &targetId); + AiCombat(const MWWorld::Ptr& actor); virtual AiCombat *clone() const; @@ -24,13 +26,27 @@ namespace MWMechanics virtual unsigned int getPriority() const; private: - std::string mTargetId; - PathFinder mPathFinder; - PathFinder mPathFinder2; - float mTimer; - float mTimer2; + //controls duration of the actual strike + float mTimerAttack; + float mTimerReact; + //controls duration of the sideway & forward moves + //when mCombatMove is true + float mTimerCombatMove; + + bool mReadyToAttack, mStrike; + bool mCloseUp; + bool mCombatMove; + + MWMechanics::Movement mMovement; + MWWorld::Ptr mTarget; + + void buildNewPath(const MWWorld::Ptr& actor); }; + + static void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement); + //chooses an attack depending on probability to avoid uniformity + static void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement); } #endif \ No newline at end of file diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 2a077abc7..03d131c74 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -315,7 +315,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat } -void CharacterController::getWeaponGroup(WeaponType weaptype, std::string &group) +void getWeaponGroup(WeaponType weaptype, std::string &group) { const WeaponInfo *info = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(weaptype)); if(info != sWeaponTypeListEnd) @@ -323,7 +323,7 @@ void CharacterController::getWeaponGroup(WeaponType weaptype, std::string &group } -MWWorld::ContainerStoreIterator CharacterController::getActiveWeapon(NpcStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype) +MWWorld::ContainerStoreIterator getActiveWeapon(NpcStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype) { if(stats.getDrawState() == DrawState_Spell) { @@ -441,6 +441,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim { getWeaponGroup(mWeaponType, mCurrentWeapon); mUpperBodyState = UpperCharState_WeapEquiped; + mAnimation->showWeapons(true); } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 438f542f0..d1930b77a 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -168,12 +168,6 @@ class CharacterController void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false); - static void getWeaponGroup(WeaponType weaptype, std::string &group); - - static MWWorld::ContainerStoreIterator getActiveWeapon(NpcStats &stats, - MWWorld::InventoryStore &inv, - WeaponType *weaptype); - void clearAnimQueue(); bool updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak); @@ -205,6 +199,10 @@ public: void forceStateUpdate(); }; + void getWeaponGroup(WeaponType weaptype, std::string &group); + MWWorld::ContainerStoreIterator getActiveWeapon(NpcStats &stats, + MWWorld::InventoryStore &inv, + WeaponType *weaptype); } #endif /* GAME_MWMECHANICS_CHARACTER_HPP */ From 5357f569e629e5b108ae21016f63ac2e87956780 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 17 Jan 2014 19:36:22 +0200 Subject: [PATCH 2/7] fix for scrawl --- apps/openmw/mwmechanics/aicombat.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index e3cce0f95..4ab358a52 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -358,7 +358,7 @@ namespace MWMechanics return new AiCombat(*this); } - static void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) + void MWMechanics::determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) { if (movement.mPosition[0] && !movement.mPosition[1]) //sideway actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); @@ -368,7 +368,7 @@ namespace MWMechanics actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); } - static void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) + void MWMechanics::chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) { //the more damage attackType deals the more probability it has From 7df8273d717d71187ecb2774d3e9b0863fa8166b Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 17 Jan 2014 20:01:15 +0200 Subject: [PATCH 3/7] fix for everybody --- apps/openmw/mwmechanics/aicombat.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 4ab358a52..0f3536f0a 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -358,7 +358,7 @@ namespace MWMechanics return new AiCombat(*this); } - void MWMechanics::determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) + void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) { if (movement.mPosition[0] && !movement.mPosition[1]) //sideway actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); @@ -368,7 +368,7 @@ namespace MWMechanics actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); } - void MWMechanics::chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) + void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) { //the more damage attackType deals the more probability it has From 46a4790cb1213a626de2a11ff4a5d7410c0a96af Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 17 Jan 2014 20:33:49 +0200 Subject: [PATCH 4/7] final warnings removal --- apps/openmw/mwmechanics/aicombat.cpp | 83 +++++++++++++++------------- apps/openmw/mwmechanics/aicombat.hpp | 4 -- 2 files changed, 45 insertions(+), 42 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 0f3536f0a..487a4773e 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -26,6 +26,10 @@ namespace return 1.0; return -1.0; } + + void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement); + //chooses an attack depending on probability to avoid uniformity + void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement); } namespace MWMechanics @@ -256,7 +260,7 @@ namespace MWMechanics //less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing) //then start attacking float speed1 = cls.getSpeed(actor); - float speed2 = cls.getSpeed(mTarget); + float speed2 = mTarget.getClass().getSpeed(mTarget); if(actor.getClass().getMovementSettings(mTarget).mPosition[0] == 0 && actor.getClass().getMovementSettings(mTarget).mPosition[1] == 0) speed2 = 0; @@ -357,51 +361,54 @@ namespace MWMechanics { return new AiCombat(*this); } +} - void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) +namespace +{ +void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement) +{ + if (movement.mPosition[0] && !movement.mPosition[1]) //sideway + actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); + else if (movement.mPosition[1]) //forward + actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Thrust); + else + actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); +} + +void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) +{ + //the more damage attackType deals the more probability it has + + if (weapon == NULL) { - if (movement.mPosition[0] && !movement.mPosition[1]) //sideway - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Slash); - else if (movement.mPosition[1]) //forward - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Thrust); - else - actor.getClass().getCreatureStats(actor).setAttackType(MWMechanics::CreatureStats::AT_Chop); - } - - void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) - { - //the more damage attackType deals the more probability it has - - if (weapon == NULL) - { - //hand-to-hand deals equal damage - float roll = static_cast(rand())/RAND_MAX; - if(roll <= 0.333f) //side punch - { - movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; - movement.mPosition[1] = 0; - } - else if(roll <= 0.666f) //forward punch - movement.mPosition[1] = 1; - - return; - } - - int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; - int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; - int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; - - float total = slash + chop + thrust; - + //hand-to-hand deals equal damage float roll = static_cast(rand())/RAND_MAX; - if(roll <= static_cast(slash)/total) + if(roll <= 0.333f) //side punch { movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; movement.mPosition[1] = 0; } - else if(roll <= (static_cast(slash) + static_cast(thrust))/total) + else if(roll <= 0.666f) //forward punch movement.mPosition[1] = 1; - //else chop + + return; } + + int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; + int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; + int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; + + float total = slash + chop + thrust; + + float roll = static_cast(rand())/RAND_MAX; + if(roll <= static_cast(slash)/total) + { + movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; + movement.mPosition[1] = 0; + } + else if(roll <= (static_cast(slash) + static_cast(thrust))/total) + movement.mPosition[1] = 1; + //else chop } +} \ No newline at end of file diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 8c494648b..cb9eee973 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -45,10 +45,6 @@ namespace MWMechanics void buildNewPath(const MWWorld::Ptr& actor); }; - - static void determineAttackType(const MWWorld::Ptr& actor, MWMechanics::Movement &movement); - //chooses an attack depending on probability to avoid uniformity - static void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement); } #endif From 435bbdd53070f17c48fd1d8e393ae381f355e917 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 17 Jan 2014 20:55:21 +0200 Subject: [PATCH 5/7] one more mismatch fixed --- apps/openmw/mwmechanics/aicombat.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 487a4773e..9a78e44ff 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -261,8 +261,8 @@ namespace MWMechanics //then start attacking float speed1 = cls.getSpeed(actor); float speed2 = mTarget.getClass().getSpeed(mTarget); - if(actor.getClass().getMovementSettings(mTarget).mPosition[0] == 0 - && actor.getClass().getMovementSettings(mTarget).mPosition[1] == 0) + if(mTarget.getClass().getMovementSettings(mTarget).mPosition[0] == 0 + && mTarget.getClass().getMovementSettings(mTarget).mPosition[1] == 0) speed2 = 0; float s1 = distBetween - weapRange; From df4df5b0946708a5e8d98de8bac7c998a964e1fd Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 17 Jan 2014 23:30:28 +0200 Subject: [PATCH 6/7] fixed weapRange for creatures/startcombat script(?) --- apps/openmw/mwmechanics/aicombat.cpp | 6 ++++++ apps/openmw/mwscript/aiextensions.cpp | 8 ++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 9a78e44ff..6bf97f6ac 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -151,6 +151,11 @@ namespace MWMechanics } weapRange *= 100.0f; } + else //is creature + { + weaptype = WeapType_HandToHand; //doesn't matter, should only reflect if it is melee or distant weapon + weapRange = 100; //TODO: use true attack range (the same problem in Creature::hit) + } //MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false); @@ -381,6 +386,7 @@ void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement if (weapon == NULL) { + //hand-to-hand and creatures' attacks handled here //hand-to-hand deals equal damage float roll = static_cast(rand())/RAND_MAX; if(roll <= 0.333f) //side punch diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index d09a72acc..53b757d76 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -435,10 +435,14 @@ namespace MWScript std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); - creatureStats.getAiSequence().stack(MWMechanics::AiCombat(actor)); + MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); + if (actorID == "player") + { creatureStats.setHostile(true); + creatureStats.getAiSequence().stack( + MWMechanics::AiCombat(MWBase::Environment::get().getWorld()->getPtr(actorID,true))); + } } }; From 969340d61ba3e2a0fbc8fdf9fa3ab937b4a271c1 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sat, 18 Jan 2014 12:55:17 +0200 Subject: [PATCH 7/7] fixed StartCombat script --- apps/openmw/mwscript/aiextensions.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 53b757d76..759d0ba94 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -432,17 +432,14 @@ namespace MWScript virtual void execute (Interpreter::Runtime &runtime) { MWWorld::Ptr actor = R()(runtime); - std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); + std::string targetID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); - if (actorID == "player") - { - creatureStats.setHostile(true); - creatureStats.getAiSequence().stack( - MWMechanics::AiCombat(MWBase::Environment::get().getWorld()->getPtr(actorID,true))); - } + creatureStats.setHostile(true); + creatureStats.getAiSequence().stack( + MWMechanics::AiCombat(MWBase::Environment::get().getWorld()->getPtr(targetID, true) )); } };