// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #ifndef EGS_PHRASE_UTILITIES_FUNCTIONS_H #define EGS_PHRASE_UTILITIES_FUNCTIONS_H // misc #include "nel/misc/types_nl.h" // game_share #include "game_share/mode_and_behaviour.h" #include "game_share/armor_types.h" #include "game_share/damage_types.h" #include "game_share/shield_types.h" #include "game_share/slot_equipment.h" #include "game_share/weapon_types.h" #include "game_share/creature_size.h" #include "game_share/intensity_types.h" #include "game_share/entity_action.h" #include "game_share/sentence_appraisal.h" // #include "entity_base.h" // Stuff used for management of log messages extern bool VerboseBrickManagerInfo; #define INFOLOG if (!VerboseBrickManagerInfo) {} else nlinfo extern bool VerboseBrickManagerDbg; #define DEBUGLOG if (!VerboseBrickManagerDbg) {} else nldebug extern bool AggroLog; #define AGGROLOG if (!AggroLog) {} else nlinfo namespace PHRASE_UTILITIES { enum ERange { UNDEFINED = 0, POINT_BLANK, // bout-portant SHORT_RANGE, MEDIUM_RANGE, LONG_RANGE, OUT_OF_RANGE, }; /** * convert a ERange value to the corresponding string */ std::string toString(ERange range); #define HANDS_DAMAGE_FACTOR_MULTIPLIER 0.8 typedef std::pair TPairSlotShield; typedef std::pair TPairUInt8UInt16; /** * struct for damage localisation * \author David Fleury * \author Nevrax France * \date 2002 */ struct SLocalisation { uint8 LocalisationNumber; SHIELDTYPE::EShieldType ShieldType; SLOT_EQUIPMENT::TSlotEquipment Slot; bool ShieldIsEffective; SLocalisation() { Slot = SLOT_EQUIPMENT::UNDEFINED; ShieldType = SHIELDTYPE::NONE; ShieldIsEffective = false; LocalisationNumber = 0; } /// egality operator inline bool operator == ( const SLocalisation& p ) const { return ( ( ShieldType == p.ShieldType ) && ( LocalisationNumber == p.LocalisationNumber ) ); } /// < operator inline bool operator <( const SLocalisation& p ) const { if ( LocalisationNumber < p.LocalisationNumber ) return true; else if ( (LocalisationNumber == p.LocalisationNumber ) && ShieldType < p.ShieldType ) return true; else return false; } }; /** * get a pointer on the CEntityBase object related to the specified entity Id * \param id the entity id * \return pointer on the CEntityBase object, or NULL if not found */ CEntityBase* entityPtrFromId( const NLMISC::CEntityId &id ); /** * get a pointer on the CEntityBase object related to the specified datasetrow * \param entityRowId the entity datasetrow * \return pointer on the CEntityBase object, or NULL if not found */ inline CEntityBase* entityPtrFromId( const TDataSetRow &entityRowId ) { if (entityRowId.isValid() && TheDataset.isDataSetRowStillValid(entityRowId)) { return entityPtrFromId( TheDataset.getEntityId( entityRowId ) ); } else return NULL; } /** * get the chance of sucess for a relative level * \param playerRelativeLevel relative level of the player * \return chance of success */ uint8 getSuccessChance( sint32 playerRelativeLevel ); /** * get Success Factor * \param chance of success (5 - 95) * \param tirage (0 - 100 ) * \return 1.1 for critical success * \return 1.0 for success * \return 1 < SF < 0 for partial success * \return for failure * \return SF < 0 for critical failure */ float getSucessFactor( uint8 chances, uint8 tirage ); /** * test the target is a valid target for Combat * \param targetId the target entity Id * \param errorCode reference on the string that will receive the error code if any * \return true if the target is valid, false otherwise */ bool testTargetValidityForCombat( const NLMISC::CEntityId &targetId, std::string &errorCode ); /** * test the target is a valid target for Combat * \param targetId the target entity Id * \param errorCode reference on the string that will receive the error code if any * \return true if the target is valid, false otherwise */ inline bool testTargetValidityForCombat( const TDataSetRow &entityRowId, std::string &errorCode ) { if ( entityRowId.isValid() && TheDataset.isDataSetRowStillValid(entityRowId) ) return testTargetValidityForCombat( TheDataset.getEntityId(entityRowId), errorCode ); else return false; } /** * get the damage localisation * \param shield the shield used by the target * \param forcedSlot the hit slot MUST be of this type, but the effectiveness of the shield is still to be determined * \param adjustement the adjustement (for size for exemple) * \return a pair indicating the slot hit and the effectiveness of the shield (true = shield is effective) */ TPairSlotShield getLocalisation( SHIELDTYPE::EShieldType shield = SHIELDTYPE::NONE, sint8 adjustement = 0, SLOT_EQUIPMENT::TSlotEquipment forcedSlot = SLOT_EQUIPMENT::UNDEFINED); /** * get the localisation size adjustment * \param attacker Id of the attacker * \param defender Id of the defender * \return the adjustement value */ sint8 getLocalisationSizeAdjustement( const NLMISC::CEntityId &attacker, const NLMISC::CEntityId &defender ); inline sint8 getLocalisationSizeAdjustement( const TDataSetRow &attackerRowId, const TDataSetRow &defenderRowId ) { if (attackerRowId.isValid() && TheDataset.isDataSetRowStillValid(attackerRowId) && defenderRowId.isValid() && TheDataset.isDataSetRowStillValid(defenderRowId)) return getLocalisationSizeAdjustement( TheDataset.getEntityId(attackerRowId), TheDataset.getEntityId(defenderRowId) ); else return 0; } /** * get the weapon damage factor * \param type the weapon type (LIGHT, MEDIUM, HEAVY...) * \param quality the weapon quality * \param entityLevel the level of the entity using this weapon (default = 0 -> no influence on damage factor) * \return the weapon damage factor */ float getWeaponDamageFactor( WEAPONTYPE::EWeaponType type, uint16 quality, uint16 entityLevel = 0 ); /** * get the armor protection characteritics (protection in % and max protection points) * \param type the armor type (LIGHT, MEDIUM, HEAVY...) * \param quality the armor quality * \return the armor charac */ TPairUInt8UInt16 getArmorCharacteristics( ARMORTYPE::EArmorType type, uint16 quality ); /** * load the localisation table * \param name and path of the file to load */ void loadLocalisationTable( const std::string &tableName ); /** * load the localisation size adjusment table * \param name and path of the file to load */ void loadLocalisationSizeAdjusmentTable( const std::string &tableName ); /** * get the entity level in the specified skill and specialization * \param entityId the entity NLMISC::CEntityId * \param skill the skill used * \param skillModifier bonus/malus applied on skill level (-20 = -2 level) * \param skillMultiplier factor on skill value (>0) * \return the entity level (uint32) */ uint32 getEntityLevel( const NLMISC::CEntityId &entityId, SKILLS::ESkills skill, sint32 skillModifier = 0, float skillMultiplier = 1.0f); inline uint32 getEntityLevel( const TDataSetRow &entityRowId, SKILLS::ESkills skill, sint32 skillModifier = 0,float skillMultiplier = 1.0f) { if ( entityRowId.isValid() && TheDataset.isDataSetRowStillValid(entityRowId) ) { return getEntityLevel( TheDataset.getEntityId(entityRowId), skill, skillModifier, skillMultiplier); } else return 0; } /** * get the entity level in the specified score * \param entityId the entity NLMISC::CEntityId * \param score the scor used * \param scoreModifier bonus/malus applied on score (-20 = -2 level) * \param scoreMultiplier factor applied on score * \return the entity level (uint32) */ uint32 getEntityLevel( const NLMISC::CEntityId &entityId, SCORES::TScores score, sint32 scoreModifier = 0, float scoreMultiplier = 1.0f ); inline uint32 getEntityLevel( const TDataSetRow &entityRowId, SCORES::TScores score, sint32 scoreModifier = 0, float scoreMultiplier = 1.0f ) { if ( entityRowId.isValid() && TheDataset.isDataSetRowStillValid(entityRowId) ) { return getEntityLevel( TheDataset.getEntityId(entityRowId), score, scoreModifier, scoreMultiplier ); } else return 0; } /** * get the distance between two entities * \param entity1 the first entity * \param entity2 the second entity * \return the distance in meters, -1 if invalid entities or properties not found */ double getDistance( const NLMISC::CEntityId &entity1, const NLMISC::CEntityId &entity2 ); inline double getDistance( const TDataSetRow &entity1RowId, const TDataSetRow &entity2RowId ) { if ( entity1RowId.isValid() && TheDataset.isDataSetRowStillValid(entity1RowId) && entity2RowId.isValid() && TheDataset.isDataSetRowStillValid(entity2RowId) ) { return getDistance( TheDataset.getEntityId(entity1RowId), TheDataset.getEntityId(entity2RowId) ); } else return 0.0; } /** * inflict damage to the specified Item * \param ownerId the NLMISC::CEntityId of the item's owner * \param slot the slot where this item is stored * \param damage the amount of damage dealt to the item */ //void inflictDamageOnItem( const NLMISC::CEntityId &itemId, SLOT_EQUIPMENT::TSlotEquipment slot, sint32 damage ); /** * inflict damage to the specified Item * \param itemPtr pointre on the CGameItem to damage * \param damage the amount of damage dealt to the item */ //void inflictDamageOnItem( CGameItemPtr itemPtr, sint32 damage ); /** * send Death messages when an entity kills another one * \param killerId NLMISC::CEntityId of the acting entity (the one which kills the other one) * \param deadId NLMISC::CEntityId of the dead entity */ void sendDeathMessages( const NLMISC::CEntityId &killerId, const NLMISC::CEntityId &deadId ); inline void sendDeathMessages( const TDataSetRow &killerRowId, const TDataSetRow &deadRowId ) { if (killerRowId.isValid() && TheDataset.isDataSetRowStillValid(killerRowId) && deadRowId.isValid() && TheDataset.isDataSetRowStillValid(deadRowId)) { sendDeathMessages( TheDataset.getEntityId( killerRowId ), TheDataset.getEntityId( deadRowId ) ); } } /** * an entity hits another entity, send all relevant messages to the entities around * \param aggressorId NLMISC::CEntityId of the acting entity (the one which deals damage) * \param victimId NLMISC::CEntityId of the victim entity * \param sentence pointer on the calling sentence if any * \param amount the amount of damage dealt to the victim entity * \param lostStamina the amount of stamina lost by the victim entity * \param lostSap the amount of sap lost by the victim entity */ void sendHitMessages( const NLMISC::CEntityId &aggressorId, const NLMISC::CEntityId &victimId, sint32 amount, sint32 lostStamina = 0, sint32 lostSap = 0); inline void sendHitMessages( const TDataSetRow &aggressorRowId, const TDataSetRow &victimRowId, sint32 amount, sint32 lostStamina = 0, sint32 lostSap = 0) { if (aggressorRowId.isValid() && TheDataset.isDataSetRowStillValid(aggressorRowId) && victimRowId.isValid() && TheDataset.isDataSetRowStillValid(victimRowId) ) { sendHitMessages( TheDataset.getEntityId( aggressorRowId ), TheDataset.getEntityId( victimRowId ), amount, lostStamina, lostSap ); } } /** * an entity hits another entity but does no damage, send all relevant messages to the entities around * \param aggressorId NLMISC::CEntityId of the acting entity (the one which deals damage) * \param victimId NLMISC::CEntityId of the victim entity */ void sendHitNullMessages( const NLMISC::CEntityId &aggressorId, const NLMISC::CEntityId &victimId ); inline void sendHitNullMessages( const TDataSetRow &aggressorRowId, const TDataSetRow &victimRowId ) { if (aggressorRowId.isValid() && TheDataset.isDataSetRowStillValid(aggressorRowId) && victimRowId.isValid() && TheDataset.isDataSetRowStillValid(victimRowId)) { sendHitNullMessages( TheDataset.getEntityId( aggressorRowId ), TheDataset.getEntityId( victimRowId ) ); } } /** * fumble messages * \param aggressorId NLMISC::CEntityId of the acting entity * \param victimId NLMISC::CEntityId of the targeted entity */ void sendFumbleMessage( const NLMISC::CEntityId &aggressorId, const NLMISC::CEntityId &victimId ); inline void sendFumbleMessage( const TDataSetRow &aggressorRowId, const TDataSetRow &victimRowId ) { if (aggressorRowId.isValid() && TheDataset.isDataSetRowStillValid(aggressorRowId) && victimRowId.isValid() && TheDataset.isDataSetRowStillValid(victimRowId)) { sendFumbleMessage( TheDataset.getEntityId( aggressorRowId ), TheDataset.getEntityId( victimRowId ) ); } } /** * an entity resits the use of a strategy of another entity, send all relevant messages to the entities around and the entites concerned * \param aggressorId NLMISC::CEntityId of the acting entity * \param victimId NLMISC::CEntityId of the victim entity */ void sendCombatResistMessages( const NLMISC::CEntityId &aggressorId, const NLMISC::CEntityId &victimId ); /** * an entity resits the use of a strategy of another entity, send all relevant messages to the entities around and the entites concerned * \param aggressorRowId row id of the acting entity * \param victimRowId row id of the victim entity */ inline void sendCombatResistMessages( const TDataSetRow &aggressorRowId, const TDataSetRow &victimRowId ) { if (aggressorRowId.isValid() && TheDataset.isDataSetRowStillValid(aggressorRowId) && victimRowId.isValid() && TheDataset.isDataSetRowStillValid(victimRowId)) { sendCombatResistMessages( TheDataset.getEntityId( aggressorRowId ), TheDataset.getEntityId( victimRowId ) ); } } /** * send a simple (only a string) message to the specified entity (or chat group) * \param entityId CEntityId of the entity to whom the message is sent * \param msgName name of the message */ void sendSimpleMessage( const NLMISC::CEntityId &entityId, const std::string &msgName ); inline void sendSimpleMessage( const TDataSetRow &entityRowId, const std::string &msgName ) { if (entityRowId.isValid() && TheDataset.isDataSetRowStillValid(entityRowId) ) { sendSimpleMessage( TheDataset.getEntityId( entityRowId ), msgName ); } } /** * send a message with a string param to the specified entity (or chat group) * \param entityId CEntityId of the entity to whom the message is sent * \param msgName name of the message * \param txt the text param */ void sendMessage( const NLMISC::CEntityId &entityId, const std::string &msgName, const ucstring &txt ); /** * send a message with a string param to the specified entity (or chat group) * \param entityId CEntityId of the entity to whom the message is sent * \param msgName name of the message * \param entityIdForText entityId which name appears in the message */ void sendMessage( const NLMISC::CEntityId &entityId, const std::string &msgName, const NLMISC::CEntityId &entityIdForText ); inline void sendMessage( const TDataSetRow &entityRowId, const std::string &msgName, const TDataSetRow &entityRowIdForText ) { if (entityRowId.isValid() && TheDataset.isDataSetRowStillValid(entityRowId) && entityRowIdForText.isValid() && TheDataset.isDataSetRowStillValid(entityRowIdForText) ) sendMessage( TheDataset.getEntityId( entityRowId ), msgName, TheDataset.getEntityId( entityRowIdForText ) ); } /** * send a dynamic message to the chat * \param entityId CEntityId of the entity executiong a sentence * \param msgText text of the message */ void sendDynamicChatMessage( const NLMISC::CEntityId &entityId, const std::string &msgText ); /** * send a message to the GPMS requesting all the entities of chosen types in a circle of specified radius around the target entity * \param target Id of the targeted entity (must be valid) * \param radius radius of the circle in meters * \param validTargets list of valid target types * \param sentence the calling sentence (needed to find it when the GPMS answer) */ //void sendAreaRequest( const NLMISC::CEntityId &targetEntity, float radius, const std::list &validTypes, CSentence *sentence); /** * send a message to update an entity behaviour, sent to the OPS or to the AGG * \param entityId id of the entity changing it's behaviour * \param behaviour the new entity behaviour */ void sendUpdateBehaviour( const NLMISC::CEntityId &entityId, const MBEHAV::CBehaviour &behaviour, bool forceUpdate = false ); /** * send a message to update an entity behaviour, sent to the OPS or to the AGG * \param entityRowId row id of the entity changing it's behaviour * \param behaviour the new entity behaviour */ inline void sendUpdateBehaviour( const TDataSetRow &entityRowId, const MBEHAV::CBehaviour &behaviour, bool forceUpdate = false ) { if (entityRowId.isValid()) { sendUpdateBehaviour( TheDataset.getEntityId( entityRowId ), behaviour, forceUpdate ); } } /** * an entity engage another entity in melee combat * \param entityId the active entity Id * \param targetId the entity being engaged * \param mode the mode of the engagement (default = 1 = normal) * \return true if the target has been engaged, false otherwise */ bool engageTargetInMelee( const NLMISC::CEntityId &entityId , const NLMISC::CEntityId &targetId , sint8 mode = 1); /** * an entity engage another entity in range combat * \param entityId the active entity Id * \param targetId the entity being engaged * \return true if the target has been engaged, false otherwise */ bool engageTargetRange( const NLMISC::CEntityId &entityId , const NLMISC::CEntityId &targetId ); /** * send engage messages to clients * \param aggressorId id of the attacking entity * \param victimId id of the attacked entity */ void sendEngageMessages( const NLMISC::CEntityId &aggressorId, const NLMISC::CEntityId &victimId ); /** * send disengage messages to clients * \param aggressorId id of the attacking entity now disengaging from combat * \param victimId id of the attacked entity */ void sendDisengageMessages( const NLMISC::CEntityId &aggressorId, const NLMISC::CEntityId &victimId ); /** * send spell resists message * \param aggressorId id of the attacking entity * \param victimId id of the attacked entity */ void sendSpellResistMessages( const TDataSetRow &aggressorRowId, const TDataSetRow &victimRowId ); /** * send spell begin cast message */ void sendSpellBeginCastMessages( const TDataSetRow &aggressorRowId, const TDataSetRow &victimRowId, ACTNATURE::EActionNature nature ); void sendSpellSuccessMessages( const TDataSetRow &aggressorRowId, const TDataSetRow &victimRowId ); void sendSpellFailedMessages( const TDataSetRow &aggressorRowId, const TDataSetRow &victimRowId ); void sendSpellFumbleMessages( const TDataSetRow &aggressorRowId, const TDataSetRow &victimRowId ); /** * send critical hit messages * \param aggressorId id of the attacking entity * \param victimId id of the attacked entity */ void sendCriticalHitMessage( const NLMISC::CEntityId &aggressorId, const NLMISC::CEntityId &victimId ); inline void sendCriticalHitMessage( const TDataSetRow &aggressorRowId, const TDataSetRow &victimRowId ) { if ( aggressorRowId.isValid() && TheDataset.isDataSetRowStillValid(aggressorRowId) && victimRowId.isValid() && TheDataset.isDataSetRowStillValid(victimRowId) ) { sendCriticalHitMessage( TheDataset.getEntityId(aggressorRowId), TheDataset.getEntityId(victimRowId) ); } } /** * get the relative intensity of the impact according to target * \param impact strength of the impact * \param targetId the target id * \param score the affected score * \return the impact intensity */ INTENSITY_TYPE::TImpactIntensity getImpactIntensity( sint32 impact, const TDataSetRow &victimRowId, SCORES::TScores score = SCORES::hit_points ); /** * get the intensity of the attack * \param attackLevel level of the attack * \return the attack intensity */ INTENSITY_TYPE::TAttackIntensity getAttackIntensity( uint32 attackLevel); // get entity skill value inline uint32 getEntitySkill( const NLMISC::CEntityId &entityId, SKILLS::ESkills skill ) { CEntityBase* entity = entityPtrFromId( entityId ); if (entity == NULL) { nlwarning(" Invalid entity Id %s", entityId.toString().c_str() ); return 0; } if ( skill < SKILLS::NUM_SKILLS ) { return entity->getSkills()._Skills[ skill ].Current; } return 0; } /** * get the level for a given 'skill' */ inline uint32 getLevelFromSkill(uint32 comp) { /* if (comp % 10 != 0) { comp /= 10; ++comp; } else { comp /= 10; } return comp; */ // 0 -> locked skill 1-9 -> level 0 but unlocked, 10-19 level 1, etc...) return comp /= 10; } /** * get the level for a given 'skill' */ inline uint32 getLevelFromScore(uint32 scoreValue) { //for now we consider scores as skills return getLevelFromSkill(scoreValue); } /** * get the direction (as text) pointed by the specified vector * \param dir the direction vector * \return the direction as a string */ std::string getOrientationString( const NLMISC::CVector &dir); /** * get the direction (as text) */ std::string getOrientationString( sint32 x, sint32 y); /** * get the current action type for specified entity * \param entityId id of the entity to check * \return the entity action as an enum value */ ENTITY_ACTION::EEntityAction getEntityCurrentAction( const NLMISC::CEntityId &entityId); /** * test if an entity spell casting is broken when it has been hit * \param entity pointer on the entity being hit * \param attacker pointer on the entity dealing damage * \param damage amount of damage done * \param weaponType the type of weapon used (if any, optionnal param) */ void testSpellBreakOnDamage( CEntityBase *entity, CEntityBase * attacker, sint32 damage, WEAPONTYPE::EWeaponType weaponType = WEAPONTYPE::UNKNOWN ); /** * test if PVP or any other offensive action is allowed against target entity * \param actorId if the acting entity * \param targetId if the target entity * \param errorCode the string that will receive the errorCode if any * \return true if an offensive action is allowed */ bool testOffensiveActionAllowed( const NLMISC::CEntityId &actorId, const NLMISC::CEntityId &targetId, std::string &errorCode); inline bool testOffensiveActionAllowed( const TDataSetRow &actorRowId, const TDataSetRow &targetRowId, std::string &errorCode) { return testOffensiveActionAllowed( TheDataset.getEntityId(actorRowId), TheDataset.getEntityId(targetRowId), errorCode); } /** * test if the target of a spell is valid * \param actorRowId spell caster * \param targetRowId target id * \param action nature of the spelll * \return true if allowed */ inline bool validateSpellTarget( const TDataSetRow &actorRowId, const TDataSetRow &targetRowId, ACTNATURE::EActionNature action ) { ///\todo nico do something with the error code std::string errorCode; if ( action == ACTNATURE::OFFENSIVE ) { return PHRASE_UTILITIES::testOffensiveActionAllowed(actorRowId, targetRowId, errorCode); } else { // defensive spells are allowed on everybody except attackable ones CEntityBase * target = CEntityBaseManager::getEntityBasePtr( targetRowId ); return !(target->getContextualProperty().getValue().attackable()); } } /////////////////////////// temp ///////////////////////////////// // send dmg spell message void sendScoreModifierSpellMessage( CEntityBase * aggressor, CEntityBase* victim, sint32 value, SCORES::TScores score, ACTNATURE::EActionNature nature ); // send heal spell message // send resist message }; // PHRASE_UTILITIES #endif // EGS_PHRASE_UTILITIES_FUNCTIONS_H /* End of phrase_utilities_functions.h */