knightonline/server/GameServer/AttackHandler.cpp

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();
}
}