384 lines
12 KiB
C++
384 lines
12 KiB
C++
#pragma once
|
|
|
|
#if defined(AI_SERVER)
|
|
# define KOMap MAP
|
|
#else
|
|
# define KOMap C3DMap
|
|
#endif
|
|
|
|
#include "Define.h"
|
|
#include "GameDefine.h"
|
|
#include "../shared/ReferenceObject.h"
|
|
#include <map>
|
|
|
|
// Maximum range allowed between a player and an NPC.
|
|
#define MAX_NPC_RANGE (121.0f) // pow(11.0f, 2.0f), to save having to calculate it in the code.
|
|
|
|
// Maximum range allowed between a unit and an object
|
|
#define MAX_OBJECT_RANGE (100.0f) // pow(10.0f, 2.0f)
|
|
|
|
// Maximum range allowed between a player & their loot.
|
|
#define MAX_LOOT_RANGE (121.0f) // pow(11.0f, 2.0f)
|
|
|
|
struct _MAGIC_TABLE;
|
|
struct _BUFF_TYPE4_INFO;
|
|
struct _BUFF_TYPE9_INFO;
|
|
class CRegion;
|
|
class KOMap;
|
|
class Packet;
|
|
|
|
typedef std::map<uint8, _BUFF_TYPE4_INFO> Type4BuffMap;
|
|
typedef std::map<uint8, _BUFF_TYPE9_INFO> Type9BuffMap;
|
|
|
|
enum AttackType { AttackTypeNone, AttackTypePhysical, AttackTypeMagic };
|
|
enum UnitType { UnitPlayer, UnitNPC, UnitPet };
|
|
|
|
/**
|
|
* This class is a bridge between the CNpc & CUser classes
|
|
**/
|
|
class Unit : public ReferenceObject
|
|
{
|
|
public:
|
|
Unit(UnitType unitType);
|
|
|
|
virtual void Initialize();
|
|
|
|
INLINE UnitType GetUnitType() { return m_unitType; }
|
|
INLINE void SetUnitType(UnitType unitType) { m_unitType = unitType; }
|
|
|
|
INLINE bool isPlayer() { return GetUnitType() == UnitPlayer; }
|
|
INLINE bool isNPC() { return GetUnitType() == UnitNPC; }
|
|
|
|
INLINE KOMap * GetMap() { return m_pMap; }
|
|
|
|
virtual uint16 GetID() = 0;
|
|
INLINE uint8 GetZoneID() { return m_bZone; }
|
|
INLINE uint16 GetEventRoom() { return m_bEventRoom > (uint16)MAX_MONSTER_STONE_EVENT_ROOM ? 0 : m_bEventRoom; }
|
|
INLINE void SetUnitEventRoom(uint16 nEventRoom) { m_bEventRoom = nEventRoom; }
|
|
|
|
INLINE bool isInTempleEventZone(uint8 nZoneID = 0)
|
|
{
|
|
if (nZoneID == 0)
|
|
nZoneID = GetZoneID();
|
|
|
|
return nZoneID == ZONE_BORDER_DEFENSE_WAR || nZoneID == ZONE_CHAOS_DUNGEON || nZoneID == ZONE_JURAD_MOUNTAIN;
|
|
}
|
|
|
|
INLINE bool isInTempEventZone(uint8 nZoneID = 0)
|
|
{
|
|
if (nZoneID == 0)
|
|
nZoneID = GetZoneID();
|
|
|
|
return nZoneID == ZONE_BORDER_DEFENSE_WAR || nZoneID == ZONE_CHAOS_DUNGEON;
|
|
}
|
|
|
|
INLINE bool BorderTempleEventZone(uint8 nZoneID = 0)
|
|
{
|
|
if (nZoneID == 0)
|
|
nZoneID = GetZoneID();
|
|
|
|
return nZoneID == ZONE_BORDER_DEFENSE_WAR;
|
|
}
|
|
|
|
INLINE bool ChaosTempleEventZone(uint8 nZoneID = 0)
|
|
{
|
|
if (nZoneID == 0)
|
|
nZoneID = GetZoneID();
|
|
|
|
return nZoneID == ZONE_CHAOS_DUNGEON;
|
|
}
|
|
|
|
INLINE bool JuraidTempleEventZone(uint8 nZoneID = 0)
|
|
{
|
|
if (nZoneID == 0)
|
|
nZoneID = GetZoneID();
|
|
|
|
return nZoneID == ZONE_JURAD_MOUNTAIN;
|
|
}
|
|
INLINE float GetX() { return m_curx; }
|
|
INLINE float GetY() { return m_cury; }
|
|
INLINE float GetZ() { return m_curz; }
|
|
|
|
INLINE void SetPosition(float fx, float fy, float fz)
|
|
{
|
|
m_curx = fx;
|
|
m_curz = fz;
|
|
m_cury = fy;
|
|
}
|
|
|
|
INLINE uint16 GetSPosX() { return uint16(GetX() * 10); };
|
|
INLINE uint16 GetSPosY() { return uint16(GetY() * 10); };
|
|
INLINE uint16 GetSPosZ() { return uint16(GetZ() * 10); };
|
|
|
|
INLINE uint16 GetRegionX() { return m_sRegionX; }
|
|
INLINE uint16 GetRegionZ() { return m_sRegionZ; }
|
|
|
|
INLINE uint16 GetNewRegionX() { return (uint16)(GetX()) / VIEW_DISTANCE; }
|
|
INLINE uint16 GetNewRegionZ() { return (uint16)(GetZ()) / VIEW_DISTANCE; }
|
|
|
|
INLINE CRegion * GetRegion() { return m_pRegion; }
|
|
void SetRegion(uint16 x = -1, uint16 z = -1);
|
|
|
|
virtual std::string & GetName() = 0; // this is especially fun...
|
|
|
|
INLINE uint8 GetNation() { return m_bNation; }
|
|
INLINE uint8 GetLevel() { return m_bLevel; }
|
|
|
|
INLINE uint8 GetRebLevel() { return m_reblvl; }
|
|
|
|
virtual int32 GetHealth() = 0;
|
|
virtual int32 GetMaxHealth() = 0;
|
|
virtual int32 GetMana() = 0;
|
|
virtual int32 GetMaxMana() = 0;
|
|
|
|
INLINE bool isIncapacitated() { return isDead() || isBlinded() || isBlinking() || isKaul(); }
|
|
INLINE bool isBlinded() { return m_bIsBlinded; }
|
|
INLINE bool canUseSkills() { return !(!isBlinded() && hasBuff(BUFF_TYPE_BLIND)) && m_bCanUseSkills && !isKaul(); }
|
|
INLINE bool canUsePotions() { return m_bCanUsePotions; }
|
|
INLINE bool canTeleport() { return m_bCanTeleport; }
|
|
INLINE bool isKaul() { return m_bIsKaul; }
|
|
INLINE bool isDevil() { return m_bIsDevil; }
|
|
INLINE bool isReturnee() { return m_bisReturnee; }
|
|
|
|
INLINE bool isBuffed(bool bIsOnlyScroll = false)
|
|
{
|
|
Guard lock(_unitlock);
|
|
|
|
// Check the buff counter.
|
|
// We cannot check the map itself, as the map contains both buffs and debuffs.
|
|
if (bIsOnlyScroll)
|
|
{
|
|
foreach (itr, m_buffMap)
|
|
if (itr->second.m_nSkillID > 500000)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
INLINE bool isDebuffed()
|
|
{
|
|
Guard lock(_unitlock);
|
|
|
|
// As the 'buff' map contains both buffs and debuffs, if the number of buffs/debuffs in the map doesn't
|
|
// match the number of buffs we have, we can conclude we have some debuffs in there.
|
|
return (uint8) m_buffMap.size() != m_buffCount;
|
|
}
|
|
|
|
INLINE bool hasBuff(uint8 buff, bool isOnlyBuff = false)
|
|
{
|
|
Guard lock(_unitlock);
|
|
|
|
if (isOnlyBuff)
|
|
{
|
|
auto itr = m_buffMap.find(buff);
|
|
if (itr != m_buffMap.end() && itr->second.isBuff())
|
|
return true;
|
|
}
|
|
|
|
return m_buffMap.find(buff) != m_buffMap.end();
|
|
}
|
|
|
|
INLINE bool hasDebuff(uint8 buff)
|
|
{
|
|
Guard lock(_unitlock);
|
|
auto itr = m_buffMap.find(buff);
|
|
if (itr != m_buffMap.end() && itr->second.isDebuff())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
INLINE bool canInstantCast() { return m_bInstantCast; }
|
|
INLINE bool canStealth() { return m_bCanStealth; }
|
|
|
|
virtual bool isBlinking() { return false; }
|
|
|
|
virtual bool isDead() = 0;
|
|
virtual bool isAlive() { return !isDead(); }
|
|
|
|
float GetDistance(float fx, float fz);
|
|
float GetDistance(Unit * pTarget);
|
|
float GetDistanceSqrt(Unit * pTarget);
|
|
|
|
bool isInRange(Unit * pTarget, float fSquaredRange);
|
|
bool isInRange(float fx, float fz, float fSquaredRange);
|
|
bool isInRangeSlow(Unit * pTarget, float fNonSquaredRange);
|
|
bool isInRangeSlow(float fx, float fz, float fNonSquaredRange);
|
|
|
|
static float GetDistance(float fStartX, float fStartZ, float fEndX, float fEndZ);
|
|
static bool isInRange(float fStartX, float fStartZ, float fEndX, float fEndZ, float fSquaredRange);
|
|
static bool isInRangeSlow(float fStartX, float fStartZ, float fEndX, float fEndZ, float fNonSquaredRange);
|
|
|
|
virtual void GetInOut(Packet & result, uint8 bType) = 0;
|
|
|
|
bool RegisterRegion();
|
|
virtual void AddToRegion(int16 new_region_x, int16 new_region_z) = 0;
|
|
void RemoveRegion(int16 del_x, int16 del_z);
|
|
void InsertRegion(int16 insert_x, int16 insert_z);
|
|
|
|
bool isInAttackRange(Unit * pTarget, _MAGIC_TABLE * pSkill = nullptr);
|
|
virtual short GetDamage(Unit *pTarget, _MAGIC_TABLE *pSkill = nullptr, bool bPreviewOnly = false) = 0;
|
|
virtual void OnAttack(Unit * pTarget, AttackType attackType) {}
|
|
virtual void OnDefend(Unit * pAttacker, AttackType attackType) {}
|
|
|
|
short GetMagicDamage(int damage, Unit *pTarget, bool bPreviewOnly = false);
|
|
short GetACDamage(int damage, Unit *pTarget);
|
|
uint8 GetHitRate(float rate);
|
|
|
|
virtual void InsertSavedMagic(uint32 nSkillID, uint16 sDuration) {}
|
|
virtual bool HasSavedMagic(uint32 nSkillID) { return false; }
|
|
virtual int16 GetSavedMagicDuration(uint32 nSkillID) { return -1; }
|
|
|
|
virtual void HpChange(int amount, Unit *pAttacker = nullptr, bool bSendToAI = true) = 0;
|
|
virtual void HpChangeMagic(int amount, Unit *pAttacker = nullptr, AttributeType attributeType = AttributeNone) { HpChange(amount, pAttacker); }
|
|
virtual void MSpChange(int amount) = 0;
|
|
|
|
void SendToRegion(Packet *result);
|
|
void Send_AIServer(Packet *result);
|
|
|
|
void InitType3();
|
|
void InitType4(bool bRemoveSavedMagic = false, uint8 buffType = 0);
|
|
void AddType4Buff(uint8 bBuffType, _BUFF_TYPE4_INFO & pBuffInfo);
|
|
|
|
virtual void StateChangeServerDirect(uint8 bType, uint32 nBuff) {}
|
|
virtual bool isHostileTo(Unit * pTarget) = 0;
|
|
virtual bool CanAttack(Unit * pTarget);
|
|
virtual bool isAttackable(Unit * pTarget = nullptr);
|
|
virtual bool CanCastRHit(uint16 m_SocketID);
|
|
virtual bool isSameEventRoom(Unit * pTarget);
|
|
|
|
void OnDeath(Unit *pKiller);
|
|
void SendDeathAnimation(Unit *pKiller = nullptr);
|
|
|
|
// public for the moment
|
|
// protected:
|
|
KOMap * m_pMap;
|
|
CRegion * m_pRegion;
|
|
|
|
uint8 m_bZone;
|
|
float m_curx, m_curz, m_cury;
|
|
uint16 m_sRegionX, m_sRegionZ; // this is probably redundant
|
|
|
|
UnitType m_unitType;
|
|
|
|
uint8 m_bLevel;
|
|
uint8 m_reblvl;
|
|
uint8 m_bNation;
|
|
|
|
uint16 m_sTotalHit;
|
|
uint16 m_sTotalAc;
|
|
float m_fTotalHitrate;
|
|
float m_fTotalEvasionrate;
|
|
|
|
short m_sACAmount; // additional absolute AC
|
|
int16 m_sACPercent; // percentage of total AC to modify by
|
|
int16 m_bAttackAmount;
|
|
short m_sMagicAttackAmount;
|
|
short m_sMaxHPAmount, m_sMaxMPAmount;
|
|
uint8 m_bHitRateAmount;
|
|
short m_sAvoidRateAmount;
|
|
|
|
float m_bIceSpeedAmount;
|
|
int16 m_sAttackSpeedAmount;
|
|
uint8 m_bSpeedAmount;
|
|
|
|
|
|
int16 AbsorbedAmmount;
|
|
|
|
// Item calculated elemental resistances.
|
|
uint16 m_sFireR, m_sColdR, m_sLightningR,
|
|
m_sMagicR, m_sDiseaseR, m_sPoisonR;
|
|
|
|
// Additional elemental resistance amounts from skills (note: NOT percentages)
|
|
uint8 m_bAddFireR, m_bAddColdR, m_bAddLightningR,
|
|
m_bAddMagicR, m_bAddDiseaseR, m_bAddPoisonR;
|
|
|
|
// Elemental resistance percentages (adjusted by debuffs)
|
|
uint8 m_bPctFireR, m_bPctColdR, m_bPctLightningR,
|
|
m_bPctMagicR, m_bPctDiseaseR, m_bPctPoisonR;
|
|
|
|
uint8 m_bMagicDamageReduction;
|
|
uint8 m_bManaAbsorb, AbsorbCount;
|
|
uint8 m_bRadiusAmount;
|
|
|
|
uint8 m_bResistanceBonus;
|
|
|
|
BYTE m_bMagicTypeLeftHand; // The type of magic item in user's left hand
|
|
BYTE m_bMagicTypeRightHand; // The type of magic item in user's right hand
|
|
short m_sMagicAmountLeftHand; // The amount of magic item in user's left hand
|
|
short m_sMagicAmountRightHand; // The amount of magic item in user's left hand
|
|
|
|
// bonus type -> amount
|
|
typedef std::map<uint8, int16> ItemBonusMap;
|
|
|
|
// slot id -> bonus map
|
|
typedef std::map<uint8, ItemBonusMap> EquippedItemBonuses;
|
|
|
|
// This map is for applying item bonuses from equipped skills, i.e. resistances, drains, damage reflection, etc.
|
|
// It is indexed by slot ID (this should really work with the item container), and contains a map of each bonus (indexed by type)
|
|
// supported by this item (we support multiple bonuses, official most likely still overrides them).
|
|
EquippedItemBonuses m_equippedItemBonuses;
|
|
|
|
// Weapon resistances
|
|
int16 m_sDaggerR;
|
|
uint8 m_byDaggerRAmount;
|
|
int16 m_sSwordR;
|
|
int16 m_sAxeR;
|
|
int16 m_sMaceR;
|
|
int16 m_sSpearR;
|
|
int16 m_sBowR;
|
|
uint8 m_byBowRAmount;
|
|
|
|
struct MagicType3
|
|
{
|
|
bool m_byUsed; // indicates whether this element is used
|
|
time_t m_tHPLastTime; // time when the durational skill last affected the unit
|
|
int16 m_sHPAmount; // HP amount to affet the unit by (negative for damage, positive for HP recovery)
|
|
uint8 m_bHPInterval; // interval (in seconds) between each durational skill effect
|
|
uint8 m_bTickCount; //
|
|
uint8 m_bTickLimit; // number of ticks required before the skill expires
|
|
uint16 m_sSourceID; // ID of the unit that used this skill on the unit
|
|
uint8 m_byAttribute; // skill attribute
|
|
bool m_sTo;
|
|
MagicType3() { Reset(); }
|
|
|
|
INLINE void Reset()
|
|
{
|
|
m_byUsed = false;
|
|
m_tHPLastTime = 0;
|
|
m_sHPAmount = 0;
|
|
m_bHPInterval = 0;
|
|
m_bTickCount = 0;
|
|
m_bTickLimit = 0;
|
|
m_sSourceID = -1;
|
|
m_byAttribute = AttributeNone;
|
|
}
|
|
};
|
|
|
|
MagicType3 m_durationalSkills[MAX_TYPE3_REPEAT];
|
|
bool m_bType3Flag;
|
|
|
|
Type4BuffMap m_buffMap;
|
|
Type9BuffMap m_type9BuffMap;
|
|
std::recursive_mutex _unitlock, m_buffLock;
|
|
uint8 m_buffCount; // counter for buffs (not debuffs). Used for identifying when the user is buffed.
|
|
|
|
bool m_bIsBlinded;
|
|
bool m_bCanUseSkills; // blinding prevents you from using skills or attacks, skills like "Full Skill Gear" prevent use of skills only.
|
|
bool m_bCanUsePotions;
|
|
bool m_bCanTeleport;
|
|
bool m_bCanStealth;
|
|
bool m_bInstantCast;
|
|
bool m_bBlockCurses, m_bReflectCurses;
|
|
bool m_bMirrorDamage;
|
|
uint8 m_byMirrorAmount;
|
|
uint8 m_bReflectArmorType;
|
|
bool m_bIsUndead, m_bIsKaul,m_bIsDevil,m_bisReturnee;
|
|
|
|
bool m_bBlockPhysical;
|
|
bool m_bBlockMagic;
|
|
|
|
int16 m_oSocketID; // owner user
|
|
uint16 m_bEventRoom;
|
|
};
|