301 lines
7.9 KiB
C++
301 lines
7.9 KiB
C++
#include "stdafx.h"
|
|
#include "Map.h"
|
|
|
|
void CUser::Attack(Packet & pkt)
|
|
{
|
|
int16 sid = -1, tid = -1, damage, delaytime, distance;
|
|
uint8 bType, bResult = 0;
|
|
Unit * pTarget = nullptr;
|
|
|
|
pkt >> bType >> bResult >> tid >> delaytime >> distance;
|
|
|
|
// delaytime = delaytime / 100.0f;
|
|
// distance = distance / 10.0f;
|
|
|
|
if (isIncapacitated())
|
|
return;
|
|
|
|
RemoveStealth();
|
|
|
|
// If you're holding a weapon, do a client-based (ugh, do not trust!) delay check.
|
|
_ITEM_TABLE *pTable = GetItemPrototype(RIGHTHAND);
|
|
if (pTable != nullptr && !isMage())
|
|
{
|
|
if (distance > pTable->m_sRange)
|
|
return;
|
|
}
|
|
// Empty handed.
|
|
else if (delaytime < 100)
|
|
return;
|
|
|
|
pTarget = g_pMain->GetUnitPtr(tid);
|
|
bResult = ATTACK_FAIL;
|
|
|
|
|
|
if (pTarget != nullptr
|
|
&& isInAttackRange(pTarget)
|
|
&& CanAttack(pTarget))
|
|
{
|
|
if (isAttackable(pTarget) && CanCastRHit(GetSocketID()))
|
|
{
|
|
if (isInTempEventZone() &&
|
|
(!isSameEventRoom(pTarget)
|
|
|| !g_pMain->pTempleEvent.isAttackable))
|
|
return;
|
|
|
|
if(GetEventRoom() > 0 && pTarget->GetEventRoom() != GetEventRoom())
|
|
return;
|
|
|
|
CUser *pUser = g_pMain->GetUserPtr(GetSocketID());
|
|
|
|
if (pUser != nullptr)
|
|
pUser->m_RHitRepeatList.insert(std::make_pair(GetSocketID(), UNIXTIME));
|
|
|
|
damage = GetDamage(pTarget);
|
|
|
|
if(pTarget->GetID() > NPC_BAND)
|
|
{
|
|
|
|
switch(TO_NPC(pTarget)->GetType())
|
|
{
|
|
case NPC_FOSSIL:
|
|
damage = 1;
|
|
break;
|
|
case NPC_TREE:
|
|
damage = 20;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Can't use R attacks in the Snow War.
|
|
if (GetZoneID() == ZONE_SNOW_BATTLE && g_pMain->m_byBattleOpen == SNOW_BATTLE)
|
|
damage = 0;
|
|
else if (GetZoneID() == ZONE_CHAOS_DUNGEON && g_pMain->pTempleEvent.isAttackable)
|
|
damage = 500 / 10;
|
|
else if (GetZoneID() == ZONE_PRISON)
|
|
{
|
|
if (GetMana() < (m_iMaxMp / 5))
|
|
return;
|
|
|
|
damage = 1;
|
|
MSpChange(-(m_iMaxMp / 5));
|
|
}
|
|
|
|
if (damage > 0)
|
|
{
|
|
pTarget->HpChange(-damage, this);
|
|
if (pTarget->isDead())
|
|
bResult = ATTACK_TARGET_DEAD;
|
|
else
|
|
bResult = ATTACK_SUCCESS;
|
|
|
|
// Every attack takes a little of your weapon's durability.
|
|
ItemWoreOut(ATTACK, damage);
|
|
|
|
// Every hit takes a little of the defender's armour durability.
|
|
if (pTarget->isPlayer())
|
|
TO_USER(pTarget)->ItemWoreOut(DEFENCE, damage);
|
|
}
|
|
}
|
|
}
|
|
|
|
Packet result(WIZ_ATTACK, bType);
|
|
result << bResult << GetSocketID() << tid;
|
|
SendToRegion(&result);
|
|
}
|
|
|
|
void CUser::Regene(uint8 regene_type, uint32 magicid /*= 0*/)
|
|
{
|
|
ASSERT(GetMap() != nullptr);
|
|
|
|
_OBJECT_EVENT* pEvent = nullptr;
|
|
_START_POSITION* pStartPosition = nullptr;
|
|
float x = 0.0f, z = 0.0f;
|
|
|
|
if (!isDead())
|
|
return;
|
|
|
|
if (regene_type != 1 && regene_type != 2)
|
|
regene_type = 1;
|
|
|
|
if (regene_type == 2)
|
|
{
|
|
// Is our level high enough to be able to resurrect using this skill?
|
|
if (GetLevel() <= 5
|
|
// Do we have enough resurrection stones?
|
|
|| !RobItem(379006000, 3 * GetLevel()))
|
|
return;
|
|
}
|
|
|
|
// If we're in a home zone, we'll want the coordinates from there. Otherwise, assume our own home zone.
|
|
pStartPosition = g_pMain->m_StartPositionArray.GetData(GetZoneID());
|
|
if (pStartPosition == nullptr)
|
|
return;
|
|
|
|
UserInOut(INOUT_OUT);
|
|
|
|
pEvent = GetMap()->GetObjectEvent(m_sBind);
|
|
|
|
// If we're not using a spell to resurrect.
|
|
if (magicid == 0)
|
|
{
|
|
// Resurrect at a bind/respawn point
|
|
if (pEvent && pEvent->byLife == 1 && GetZoneID() != ZONE_DELOS)
|
|
{
|
|
SetPosition(pEvent->fPosX + x, 0.0f, pEvent->fPosZ + z);
|
|
x = pEvent->fPosX;
|
|
z = pEvent->fPosZ;
|
|
}
|
|
// Are we trying to respawn in a home zone?
|
|
// If we're in a war zone (aside from snow wars, which apparently use different coords), use BattleZone coordinates.
|
|
else if ((GetZoneID() <= ZONE_ELMORAD) || (GetZoneID() != ZONE_SNOW_BATTLE && GetZoneID() == (ZONE_BATTLE_BASE + g_pMain->m_byBattleZone)))
|
|
{
|
|
// Use the proper respawn area for our nation, as the opposite nation can
|
|
// enter this zone at a war's invasion stage.
|
|
x = (float)((GetNation() == KARUS ? pStartPosition->sKarusX : pStartPosition->sElmoradX) + myrand(0, pStartPosition->bRangeX));
|
|
z = (float)((GetNation() == KARUS ? pStartPosition->sKarusZ : pStartPosition->sElmoradZ) + myrand(0, pStartPosition->bRangeZ));
|
|
}
|
|
else
|
|
{
|
|
short sx, sz;
|
|
// If we're in a war zone (aside from snow wars, which apparently use different coords), use BattleZone coordinates.
|
|
if ((GetZoneID() == ZONE_MORADON || GetZoneID() == ZONE_MORADONM2) && (isInArena() || isInPartyArena()))
|
|
{
|
|
x = (float)(MINI_ARENA_RESPAWN_X + myrand(-MINI_ARENA_RESPAWN_RADIUS, MINI_ARENA_RESPAWN_RADIUS));
|
|
z = (float)(MINI_ARENA_RESPAWN_Z + myrand(-MINI_ARENA_RESPAWN_RADIUS, MINI_ARENA_RESPAWN_RADIUS));
|
|
}
|
|
else if (GetZoneID() == ZONE_CHAOS_DUNGEON)
|
|
{
|
|
GetStartPositionRandom(sx, sz);
|
|
x = sx;
|
|
z = sz;
|
|
}
|
|
else if (GetZoneID() == ZONE_JURAD_MOUNTAIN)
|
|
{
|
|
uint16 KillCount1, KillCount2, KillCount3;
|
|
KillCount1 = GetNation() == KARUS ? g_pMain->pTempleEvent.KarusDeathRoom1[GetEventRoom()] : g_pMain->pTempleEvent.ElmoDeathRoom1[GetEventRoom()];
|
|
KillCount2 = GetNation() == KARUS ? g_pMain->pTempleEvent.KarusDeathRoom2[GetEventRoom()] : g_pMain->pTempleEvent.ElmoDeathRoom2[GetEventRoom()];
|
|
KillCount3 = GetNation() == KARUS ? g_pMain->pTempleEvent.KarusDeathRoom3[GetEventRoom()] : g_pMain->pTempleEvent.ElmoDeathRoom3[GetEventRoom()];
|
|
|
|
if (KillCount1 > 3 && KillCount2 < 4)
|
|
{
|
|
if (GetNation() == KARUS)
|
|
{
|
|
x = (float) (223 + (myrand(-1,1)));
|
|
z = (float) (672 + (myrand(-1,1)));
|
|
}else
|
|
{
|
|
x = (float) (800 + (myrand(-1,1)));
|
|
z = (float) (343 + (myrand(-1,1)));
|
|
}
|
|
}else if(KillCount2 > 3 && KillCount3 < 4)
|
|
{
|
|
if (GetNation() == KARUS)
|
|
{
|
|
x = (float) (340 + (myrand(-1,1)));
|
|
z = (float) (847 + (myrand(-1,1)));
|
|
}else
|
|
{
|
|
x = (float) (690 + (myrand(-1,1)));
|
|
z = (float) (172 + (myrand(-1,1)));
|
|
}
|
|
}else if(KillCount3 > 3)
|
|
{
|
|
if (GetNation() == KARUS)
|
|
{
|
|
x = (float) (512 + (myrand(-1,1)));
|
|
z = (float) (736 + (myrand(-1,1)));
|
|
}else
|
|
{
|
|
x = (float) (512 + (myrand(-1,1)));
|
|
z = (float) (282 + (myrand(-1,1)));
|
|
}
|
|
}else
|
|
{
|
|
GetStartPosition(sx, sz);
|
|
x = sx;
|
|
z = sz;
|
|
}
|
|
|
|
|
|
}
|
|
// For all else, just grab the start position (/town coordinates) from the START_POSITION table.
|
|
else
|
|
{
|
|
GetStartPosition(sx, sz);
|
|
x = sx;
|
|
z = sz;
|
|
}
|
|
}
|
|
|
|
SetPosition(x, 0.0f, z);
|
|
|
|
m_LastX = x;
|
|
m_LastZ = z;
|
|
|
|
m_bResHpType = USER_STANDING;
|
|
m_bRegeneType = REGENE_NORMAL;
|
|
}
|
|
else // we're respawning using a resurrect skill.
|
|
{
|
|
_MAGIC_TYPE5 * pType = g_pMain->m_Magictype5Array.GetData(magicid);
|
|
if (pType == nullptr)
|
|
return;
|
|
|
|
MSpChange(-m_iMaxMp); // reset us to 0 MP.
|
|
|
|
if (m_sWhoKilledMe == -1)
|
|
ExpChange((m_iLostExp * pType->bExpRecover) / 100); // Restore
|
|
|
|
m_bResHpType = USER_STANDING;
|
|
m_bRegeneType = REGENE_MAGIC;
|
|
}
|
|
|
|
Packet result(WIZ_REGENE);
|
|
result << GetSPosX() << GetSPosZ() << GetSPosY();
|
|
Send(&result);
|
|
|
|
m_tLastRegeneTime = UNIXTIME;
|
|
m_sWhoKilledMe = -1;
|
|
m_iLostExp = 0;
|
|
|
|
if (!isBlinking())
|
|
{
|
|
result.Initialize(AG_USER_REGENE);
|
|
result << GetSocketID() << m_sHp;
|
|
Send_AIServer(&result);
|
|
}
|
|
|
|
SetRegion(GetNewRegionX(), GetNewRegionZ());
|
|
|
|
UserInOut(INOUT_RESPAWN);
|
|
|
|
g_pMain->RegionUserInOutForMe(this);
|
|
g_pMain->RegionNpcInfoForMe(this);
|
|
|
|
InitializeStealth();
|
|
SendUserStatusUpdate(USER_STATUS_DOT, USER_STATUS_CURE);
|
|
SendUserStatusUpdate(USER_STATUS_POISON, USER_STATUS_CURE);
|
|
|
|
if (isInArena() || isInPartyArena())
|
|
SendUserStatusUpdate(USER_STATUS_SPEED, USER_STATUS_CURE);
|
|
|
|
HpChange(GetMaxHealth());
|
|
|
|
InitType4();
|
|
if (GetZoneID() != ZONE_CHAOS_DUNGEON)
|
|
RecastSavedMagic();
|
|
HpChange(GetMaxHealth());
|
|
|
|
// If we actually respawned (i.e. we weren't resurrected by a skill)...
|
|
if (magicid == 0)
|
|
{
|
|
BlinkStart();
|
|
// In PVP zones (not war zones), we must kick out players if they no longer
|
|
// have any national points.
|
|
if (GetLoyalty() == 0
|
|
&& (GetMap()->isWarZone()
|
|
|| isInPKZone()))
|
|
KickOutZoneUser();
|
|
}
|
|
} |