1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-28 07:07:45 +00:00

Merge pull request #534 from NotAdam/develop

move mp calc into calcstats and some minor cleanup
This commit is contained in:
Mordred 2019-03-23 17:04:57 +01:00 committed by GitHub
commit aa549ca2ed
20 changed files with 287 additions and 110 deletions

View file

@ -579,6 +579,13 @@ namespace Sapphire::Common
TpLoss = 12, TpLoss = 12,
TpGain = 13, TpGain = 13,
GpGain = 14, GpGain = 14,
/*!
* @brief Tells the client that it should show combo indicators on actions.
*
* @param flags Required to be 128, doesn't show combo rings on hotbars otherwise
* @param value The actionid that starts/continues the combo. eg, 3617 will start a spinning slash and/or syphon strike combo
*/
StartActionCombo = 28,
Knockback = 33, Knockback = 33,
Mount = 38, Mount = 38,
VFX = 59, // links to VFX sheet VFX = 59, // links to VFX sheet
@ -596,7 +603,8 @@ namespace Sapphire::Common
enum ItemActionType : uint16_t enum ItemActionType : uint16_t
{ {
ItemActionVFX = 944, ItemActionVFX = 852,
ItemActionVFX2 = 944,
}; };
enum ActionEffectDisplayType : uint8_t enum ActionEffectDisplayType : uint8_t
@ -931,6 +939,18 @@ namespace Sapphire::Common
uint16_t cost; uint16_t cost;
}; };
enum LevelTableEntry : uint8_t
{
PIE,
MP,
MAIN,
SUB,
DIV,
HP,
ELMT,
THREAT
};
using PlayerStateFlagList = std::vector< PlayerStateFlag >; using PlayerStateFlagList = std::vector< PlayerStateFlag >;
} }

View file

@ -0,0 +1,22 @@
#include <Script/NativeScriptApi.h>
#include <ScriptObject.h>
#include <Actor/Player.h>
#include <Action/Action.h>
class ActionHardSlash3617 :
public Sapphire::ScriptAPI::ActionScript
{
public:
ActionHardSlash3617() :
Sapphire::ScriptAPI::ActionScript( 3617 )
{
}
void onExecute( Sapphire::Action::Action& action ) override
{
}
};
EXPOSE_SCRIPT( ActionHardSlash3617 );

View file

@ -0,0 +1,22 @@
#include <Script/NativeScriptApi.h>
#include <ScriptObject.h>
#include <Actor/Player.h>
#include <Action/Action.h>
class ActionPowerSlash3627 :
public Sapphire::ScriptAPI::ActionScript
{
public:
ActionPowerSlash3627() :
Sapphire::ScriptAPI::ActionScript( 3627 )
{
}
void onExecute( Sapphire::Action::Action& action ) override
{
}
};
EXPOSE_SCRIPT( ActionPowerSlash3627 );

View file

@ -0,0 +1,22 @@
#include <Script/NativeScriptApi.h>
#include <ScriptObject.h>
#include <Actor/Player.h>
#include <Action/Action.h>
class ActionSpinningSlash3619 :
public Sapphire::ScriptAPI::ActionScript
{
public:
ActionSpinningSlash3619() :
Sapphire::ScriptAPI::ActionScript( 3619 )
{
}
void onExecute( Sapphire::Action::Action& action ) override
{
}
};
EXPOSE_SCRIPT( ActionSpinningSlash3619 );

View file

@ -0,0 +1,22 @@
#include <Script/NativeScriptApi.h>
#include <ScriptObject.h>
#include <Actor/Player.h>
#include <Action/Action.h>
class ActionSyphonStrike3623 :
public Sapphire::ScriptAPI::ActionScript
{
public:
ActionSyphonStrike3623() :
Sapphire::ScriptAPI::ActionScript( 3623 )
{
}
void onExecute( Sapphire::Action::Action& action ) override
{
}
};
EXPOSE_SCRIPT( ActionSyphonStrike3623 );

View file

@ -31,7 +31,7 @@ private:
// buy // buy
if( result.param2 == 1 ) if( result.param2 == 1 )
{ {
auto shopMgr = getFramework()->get< Sapphire::World::Manager::ShopMgr >(); auto shopMgr = framework()->get< Sapphire::World::Manager::ShopMgr >();
shopMgr->purchaseGilShopItem( player, result.eventId, result.param3, result.param4 ); shopMgr->purchaseGilShopItem( player, result.eventId, result.param3, result.param4 );
} }

View file

@ -108,7 +108,7 @@ public:
void onTalk( uint32_t eventId, Entity::Player& player, uint64_t actorId ) override void onTalk( uint32_t eventId, Entity::Player& player, uint64_t actorId ) override
{ {
auto pExdData = getFramework()->get< Sapphire::Data::ExdDataGenerated >(); auto pExdData = framework()->get< Sapphire::Data::ExdDataGenerated >();
if( !pExdData ) if( !pExdData )
return; return;

View file

@ -21,7 +21,7 @@ public:
{ {
player.playScene( eventId, 0, HIDE_HOTBAR | NO_DEFAULT_CAMERA, [this, eventId]( Entity::Player& player, const Event::SceneResult& result ) player.playScene( eventId, 0, HIDE_HOTBAR | NO_DEFAULT_CAMERA, [this, eventId]( Entity::Player& player, const Event::SceneResult& result )
{ {
auto pExdData = getFramework()->get< Sapphire::Data::ExdDataGenerated >(); auto pExdData = framework()->get< Sapphire::Data::ExdDataGenerated >();
if( !pExdData ) if( !pExdData )
return; return;
@ -42,7 +42,7 @@ public:
// moving a player inside an event will crash the game so we end it hre // moving a player inside an event will crash the game so we end it hre
player.eventFinish( eventId, 1 ); player.eventFinish( eventId, 1 );
auto playerMgr = getFramework()->get< Sapphire::World::Manager::PlayerMgr >(); auto playerMgr = framework()->get< Sapphire::World::Manager::PlayerMgr >();
playerMgr->movePlayerToLandDestination( player, pHousingAethernet->level, housingZone->getWardNum() ); playerMgr->movePlayerToLandDestination( player, pHousingAethernet->level, housingZone->getWardNum() );
} ); } );
} }

View file

@ -26,7 +26,7 @@ public:
if( result.param2 != 1 ) if( result.param2 != 1 )
return; return;
auto terriMgr = getFramework()->get< Sapphire::World::Manager::TerritoryMgr >(); auto terriMgr = framework()->get< Sapphire::World::Manager::TerritoryMgr >();
if( !terriMgr ) if( !terriMgr )
return; return;

View file

@ -26,7 +26,7 @@ public:
{ {
auto callback = [ this ]( Entity::Player& player, const Event::SceneResult& result ) auto callback = [ this ]( Entity::Player& player, const Event::SceneResult& result )
{ {
auto pFw = getFramework(); auto pFw = framework();
if( !pFw ) if( !pFw )
return LandPurchaseResult::ERR_INTERNAL; return LandPurchaseResult::ERR_INTERNAL;
// Purchase Land // Purchase Land

View file

@ -27,12 +27,12 @@ public:
player.eventFinish( 1310721, 0 ); player.eventFinish( 1310721, 0 );
player.eventFinish( getId(), 1 ); player.eventFinish( getId(), 1 );
auto exdData = getFramework()->get< Sapphire::Data::ExdDataGenerated >(); auto exdData = framework()->get< Sapphire::Data::ExdDataGenerated >();
auto warp = exdData->get< Sapphire::Data::Warp >( getId() ); auto warp = exdData->get< Sapphire::Data::Warp >( getId() );
if( !warp ) if( !warp )
return; return;
auto playerMgr = getFramework()->get< Sapphire::World::Manager::PlayerMgr >(); auto playerMgr = framework()->get< Sapphire::World::Manager::PlayerMgr >();
playerMgr->movePlayerToLandDestination( player, warp->level, result.param3 ); playerMgr->movePlayerToLandDestination( player, warp->level, result.param3 );
} }
else else
@ -56,7 +56,7 @@ public:
void onTalk( uint32_t eventId, Entity::Player& player, uint64_t actorId ) override void onTalk( uint32_t eventId, Entity::Player& player, uint64_t actorId ) override
{ {
auto exdData = getFramework()->get< Sapphire::Data::ExdDataGenerated >(); auto exdData = framework()->get< Sapphire::Data::ExdDataGenerated >();
if( !exdData ) if( !exdData )
return; return;

View file

@ -5,6 +5,8 @@
#include "Framework.h" #include "Framework.h"
#include "Script/ScriptMgr.h" #include "Script/ScriptMgr.h"
#include <Math/CalcStats.h>
#include "Actor/Player.h" #include "Actor/Player.h"
#include "Actor/BNpc.h" #include "Actor/BNpc.h"
@ -279,6 +281,13 @@ void Sapphire::Action::Action::execute()
} }
} }
if( isComboAction() )
{
auto player = m_pSource->getAsPlayer();
player->sendDebug( "action combo success from action#{0}", player->getLastComboActionId() );
}
if( !hasClientsideTarget() ) if( !hasClientsideTarget() )
{ {
pScriptMgr->onExecute( *this ); pScriptMgr->onExecute( *this );
@ -288,6 +297,13 @@ void Sapphire::Action::Action::execute()
pScriptMgr->onEObjHit( *player, m_targetId, getId() ); pScriptMgr->onEObjHit( *player, m_targetId, getId() );
return; return;
} }
// set currently casted action as the combo action if it interrupts a combo
// ignore it otherwise (ogcds, etc.)
if( !m_actionData->preservesCombo )
{
m_pSource->setLastComboActionId( getId() );
}
} }
void Sapphire::Action::Action::calculateActionCost() void Sapphire::Action::Action::calculateActionCost()
@ -302,7 +318,8 @@ void Sapphire::Action::Action::calculateActionCost()
} }
case ActionPrimaryCostType::MagicPoints: case ActionPrimaryCostType::MagicPoints:
{ {
calculateMPCost( m_primaryCost ); // todo: not sure if we should store the final value like this?
m_primaryCost = Math::CalcStats::calculateMpCost( *m_pSource, m_primaryCost );
break; break;
} }
case ActionPrimaryCostType::TacticsPoints: case ActionPrimaryCostType::TacticsPoints:
@ -325,88 +342,6 @@ void Sapphire::Action::Action::calculateActionCost()
// todo: secondary cost type needs to be handled // todo: secondary cost type needs to be handled
} }
// todo: this shouldn't be in action and instead be in some general stat calc util
void Sapphire::Action::Action::calculateMPCost( uint16_t baseCost )
{
auto level = m_pSource->getLevel();
// each level range is 1-10, 11-20, 21-30, ... therefore:
// level 50 should be in the 4th group, not the 5th
// dividing by 10 on the border will break this unless we subtract 1
auto levelGroup = std::max< uint8_t >( level - 1, 1 ) / 10;
float cost = baseCost;
// thanks to andrew for helping me figure this shit out, should be pretty accurate
switch( levelGroup )
{
// level 1-10
case 0:
{
// r^2 = 0.9999
cost = 0.0952f * level + 0.9467f;
break;
}
// level 11-20
case 1:
{
// r^2 = 1
cost = 0.19f * level;
break;
}
// level 21-30
case 2:
{
// r^2 = 1
cost = 0.38f * level - 3.8f;
break;
}
// level 31-40
case 3:
{
// r^2 = 1
cost = 0.6652f * level - 12.358f;
break;
}
// level 41-50
case 4:
{
// r^2 = 1
cost = 1.2352f * level - 35.159f;
break;
}
// level 51-60
case 5:
{
// r^2 = 1
cost = 0.0654f * std::exp( 0.1201f * level );
break;
}
// level 61-70
case 6:
{
// r^2 = 0.9998
cost = 0.2313f * ( level * level ) - 26.98f * level + 875.21f;
break;
}
default:
return;
}
// m_primaryCost is the base cost, cost is the multiplier for the current player level
m_primaryCost = static_cast< uint16_t >( std::round( cost * baseCost ) );
if( auto player = m_pSource->getAsPlayer() )
player->sendDebug( "calculated mp cost: {0}", m_primaryCost );
}
bool Sapphire::Action::Action::precheck() bool Sapphire::Action::Action::precheck()
{ {
if( auto player = m_pSource->getAsPlayer() ) if( auto player = m_pSource->getAsPlayer() )
@ -479,3 +414,15 @@ void Sapphire::Action::Action::setAdditionalData( uint32_t data )
{ {
m_additionalData = data; m_additionalData = data;
} }
bool Sapphire::Action::Action::isComboAction() const
{
auto lastActionId = m_pSource->getLastComboActionId();
if( lastActionId == 0 )
{
return false;
}
return m_actionData->actionCombo == lastActionId;
}

View file

@ -45,6 +45,8 @@ namespace Sapphire::Action
uint32_t getAdditionalData() const; uint32_t getAdditionalData() const;
void setAdditionalData( uint32_t data ); void setAdditionalData( uint32_t data );
bool isComboAction() const;
/*! /*!
* @brief Checks if the action *may* target a resident instead of an actor * @brief Checks if the action *may* target a resident instead of an actor
* @return true if the target *may* be a resident and not an actor, otherwise false. * @return true if the target *may* be a resident and not an actor, otherwise false.

View file

@ -658,3 +658,13 @@ int64_t Sapphire::Entity::Chara::getLastUpdateTime() const
{ {
return m_lastUpdate; return m_lastUpdate;
} }
void Sapphire::Entity::Chara::setLastComboActionId( uint32_t actionId )
{
m_lastComboActionId = actionId;
}
uint32_t Sapphire::Entity::Chara::getLastComboActionId() const
{
return m_lastComboActionId;
}

View file

@ -102,6 +102,8 @@ namespace Sapphire::Entity
uint64_t m_targetId; uint64_t m_targetId;
/*! Ptr to a queued action */ /*! Ptr to a queued action */
Action::ActionPtr m_pCurrentAction; Action::ActionPtr m_pCurrentAction;
/*! the id of the last combo action used (IgnoresCombo) */
uint32_t m_lastComboActionId;
/*! Invincibility type */ /*! Invincibility type */
Common::InvincibilityType m_invincibilityType; Common::InvincibilityType m_invincibilityType;
@ -243,6 +245,9 @@ namespace Sapphire::Entity
void setCurrentAction( Action::ActionPtr pAction ); void setCurrentAction( Action::ActionPtr pAction );
uint32_t getLastComboActionId() const;
void setLastComboActionId( uint32_t actionId );
uint32_t getBonusStat( Common::BaseParam bonus ) const; uint32_t getBonusStat( Common::BaseParam bonus ) const;
}; };

View file

@ -77,6 +77,7 @@ void World::Manager::ActionMgr::handleItemAction( Sapphire::Entity::Player& play
} }
case Common::ItemActionType::ItemActionVFX: case Common::ItemActionType::ItemActionVFX:
case Common::ItemActionType::ItemActionVFX2:
{ {
handleItemActionVFX( player, itemId, itemActionData->data[ 0 ] ); handleItemActionVFX( player, itemId, itemActionData->data[ 0 ] );

View file

@ -13,9 +13,10 @@
using namespace Sapphire::Math; using namespace Sapphire::Math;
using namespace Sapphire::Entity; using namespace Sapphire::Entity;
const int levelTable[70][7] = const int levelTable[71][7] =
{ {
// PIE, MP, MAIN,SUB,DIV,HP,ELMT,THREAT // PIE, MP, MAIN,SUB,DIV,HP,ELMT,THREAT
{ 1, 1, 1, 1, 1, 1, 1 },
{ 50, 104, 20, 56, 56, 0, 52 }, { 50, 104, 20, 56, 56, 0, 52 },
{ 55, 114, 21, 57, 57, 0, 54 }, { 55, 114, 21, 57, 57, 0, 54 },
{ 60, 123, 22, 60, 60, 0, 56 }, { 60, 123, 22, 60, 60, 0, 56 },
@ -184,11 +185,91 @@ uint32_t CalcStats::calculateMaxMp( PlayerPtr pPlayer, Sapphire::FrameworkPtr pF
return result; return result;
} }
uint16_t CalcStats::calculateMpCost( const Sapphire::Entity::Chara& chara, uint16_t baseCost )
{
auto level = chara.getLevel();
// each level range is 1-10, 11-20, 21-30, ... therefore:
// level 50 should be in the 4th group, not the 5t
// dividing by 10 on the border will break this unless we subtract 1
auto levelGroup = std::max< uint8_t >( level - 1, 1 ) / 10;
float cost = baseCost;
// thanks to andrew for helping me figure this shit out
// played with this some more and it seems to be accurate for everything i've tried
switch( levelGroup )
{
// level 1-10
case 0:
{
// r^2 = 0.9999
cost = 0.0952f * level + 0.9467f;
break;
}
// level 11-20
case 1:
{
// r^2 = 1
cost = 0.19f * level;
break;
}
// level 21-30
case 2:
{
// r^2 = 1
cost = 0.38f * level - 3.8f;
break;
}
// level 31-40
case 3:
{
// r^2 = 1
cost = 0.6652f * level - 12.358f;
break;
}
// level 41-50
case 4:
{
// r^2 = 1
cost = 1.2352f * level - 35.159f;
break;
}
// level 51-60
case 5:
{
// r^2 = 1
cost = 0.0654f * std::exp( 0.1201f * level );
break;
}
// level 61-70
case 6:
{
// r^2 = 0.9998
cost = 0.2313f * ( level * level ) - 26.98f * level + 875.21f;
break;
}
default:
{
return 0;
}
}
return static_cast< uint16_t >( std::round( cost * baseCost ) );
}
float CalcStats::pBlk( const Chara& chara ) float CalcStats::pBlk( const Chara& chara )
{ {
auto level = chara.getLevel(); auto level = chara.getLevel();
float blockRate = static_cast< float >( chara.getBonusStat( Common::BaseParam::BlockRate ) ); float blockRate = static_cast< float >( chara.getBonusStat( Common::BaseParam::BlockRate ) );
float levelVal = static_cast< float >( levelTable[ level ][ 4 ] ); float levelVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
return std::floor( ( 30 * blockRate ) / levelVal + 10 ); return std::floor( ( 30 * blockRate ) / levelVal + 10 );
} }
@ -201,8 +282,8 @@ float CalcStats::pDhr( const Chara& chara )
float dhRate = static_cast< float >( chara.getBonusStat( Common::BaseParam::DirectHitRate ) ) + float dhRate = static_cast< float >( chara.getBonusStat( Common::BaseParam::DirectHitRate ) ) +
baseStats.accuracy; baseStats.accuracy;
float divVal = static_cast< float >( levelTable[ level ][ 4 ] ); float divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
float subVal = static_cast< float >( levelTable[ level ][ 3 ] ); float subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] );
return std::floor( 550.f * ( dhRate - subVal ) / divVal ) / 10.f; return std::floor( 550.f * ( dhRate - subVal ) / divVal ) / 10.f;
} }
@ -215,8 +296,8 @@ float CalcStats::pChr( const Chara& chara )
float chRate = static_cast< float >( chara.getBonusStat( Common::BaseParam::CriticalHit ) ) + float chRate = static_cast< float >( chara.getBonusStat( Common::BaseParam::CriticalHit ) ) +
baseStats.critHitRate; baseStats.critHitRate;
float divVal = static_cast< float >( levelTable[ level ][ 4 ] ); float divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] );
float subVal = static_cast< float >( levelTable[ level ][ 3 ] ); float subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] );
return std::floor( 200.f * ( chRate - subVal ) / divVal + 50.f ) / 10.f; return std::floor( 200.f * ( chRate - subVal ) / divVal + 50.f ) / 10.f;
} }

View file

@ -16,9 +16,32 @@ namespace Sapphire::Math
static uint32_t calculateMaxHp( Sapphire::Entity::PlayerPtr pPlayer, FrameworkPtr pFw ); static uint32_t calculateMaxHp( Sapphire::Entity::PlayerPtr pPlayer, FrameworkPtr pFw );
/*!
* @brief Calculates the MP cost of a spell given its base cost
* @param chara The Chara that is casting the action
* @param baseCost The action cost
* @return The total MP to be consumed by a successful cast
*/
static uint16_t calculateMpCost( const Sapphire::Entity::Chara& chara, uint16_t baseCost );
/*!
* @brief Calculates the probability of a block happening
* @return
*/
static float pBlk( const Sapphire::Entity::Chara& ); static float pBlk( const Sapphire::Entity::Chara& );
/*!
* @brief Calculates the probability of a direct hit happening
* @return
*/
static float pDhr( const Sapphire::Entity::Chara& ); static float pDhr( const Sapphire::Entity::Chara& );
/*!
* @brief Calculates the probability of a critical hit happening
* @return
*/
static float pChr( const Sapphire::Entity::Chara& ); static float pChr( const Sapphire::Entity::Chara& );
private: private:
}; };

View file

@ -37,7 +37,7 @@ namespace Sapphire::ScriptAPI
m_framework = fw; m_framework = fw;
} }
Sapphire::Framework* ScriptObject::getFramework() const Sapphire::Framework* ScriptObject::framework() const
{ {
return m_framework; return m_framework;
} }
@ -102,8 +102,8 @@ namespace Sapphire::ScriptAPI
/////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////
EventScript::EventScript( uint32_t questId ) : EventScript::EventScript( uint32_t eventId ) :
ScriptObject( questId, typeid( EventScript ).hash_code() ) ScriptObject( eventId, typeid( EventScript ).hash_code() )
{ {
} }
@ -131,8 +131,8 @@ namespace Sapphire::ScriptAPI
{ {
} }
void void EventScript::onEventItem( Entity::Player& player, uint32_t eventItemId, uint32_t eventId, uint32_t castTime,
EventScript::onEventItem( Entity::Player& player, uint32_t eventItemId, uint32_t eventId, uint32_t castTime, uint64_t targetId ) uint64_t targetId )
{ {
} }

View file

@ -61,7 +61,7 @@ namespace Sapphire::ScriptAPI
* *
* @return A pointer to Core::Framework * @return A pointer to Core::Framework
*/ */
virtual Sapphire::Framework* getFramework() const; virtual Sapphire::Framework* framework() const;
}; };
@ -161,7 +161,7 @@ namespace Sapphire::ScriptAPI
} }
public: public:
explicit EventScript( uint32_t questId ); explicit EventScript( uint32_t eventId );
virtual void onTalk( uint32_t eventId, Sapphire::Entity::Player& player, uint64_t actorId ); virtual void onTalk( uint32_t eventId, Sapphire::Entity::Player& player, uint64_t actorId );
@ -175,8 +175,8 @@ namespace Sapphire::ScriptAPI
virtual void onOutsideRange( Sapphire::Entity::Player& player, uint32_t eventId, uint32_t param1, float x, float y, float z ); virtual void onOutsideRange( Sapphire::Entity::Player& player, uint32_t eventId, uint32_t param1, float x, float y, float z );
virtual void virtual void onEventItem( Sapphire::Entity::Player& player, uint32_t eventItemId, uint32_t eventId, uint32_t castTime,
onEventItem( Sapphire::Entity::Player& player, uint32_t eventItemId, uint32_t eventId, uint32_t castTime, uint64_t targetId ); uint64_t targetId );
virtual void onEventHandlerTradeReturn( Sapphire::Entity::Player& player, uint32_t eventId, uint16_t subEvent, uint16_t param, virtual void onEventHandlerTradeReturn( Sapphire::Entity::Player& player, uint32_t eventId, uint16_t subEvent, uint16_t param,
uint32_t catalogId ); uint32_t catalogId );