976 lines
24 KiB
C++
976 lines
24 KiB
C++
#include "stdafx.h"
|
|
#include "Map.h"
|
|
|
|
void CUser::MoveProcess(Packet & pkt)
|
|
{
|
|
ASSERT(GetMap() != nullptr);
|
|
if (m_bWarp || isDead())
|
|
return;
|
|
|
|
uint16 will_x, will_z, will_y;
|
|
int16 speed = 0;
|
|
float real_x, real_z, real_y;
|
|
uint8 echo;
|
|
|
|
pkt >> will_x >> will_z >> will_y >> speed >> echo;
|
|
real_x = will_x/10.0f; real_z = will_z/10.0f; real_y = will_y/10.0f;
|
|
|
|
m_sSpeed = speed;
|
|
|
|
|
|
SpeedHackUser();
|
|
|
|
if (!GetMap()->IsValidPosition(real_x, real_z, real_y))
|
|
return;
|
|
|
|
if (m_oldx != GetX() || m_oldz != GetZ())
|
|
{
|
|
m_oldx = GetX();
|
|
m_oldy = GetY();
|
|
m_oldz = GetZ();
|
|
}
|
|
|
|
// TODO: Ensure this is checked properly to prevent speedhacking
|
|
SetPosition(real_x, real_y, real_z);
|
|
|
|
if (RegisterRegion())
|
|
{
|
|
g_pMain->RegionNpcInfoForMe(this);
|
|
g_pMain->RegionUserInOutForMe(this);
|
|
g_pMain->MerchantUserInOutForMe(this);
|
|
}
|
|
|
|
if (m_bInvisibilityType == INVIS_DISPEL_ON_MOVE)
|
|
CMagicProcess::RemoveStealth(this, INVIS_DISPEL_ON_MOVE);
|
|
|
|
Packet result(WIZ_MOVE);
|
|
result << GetSocketID() << will_x << will_z << will_y << speed << echo;
|
|
SendToRegion(&result,nullptr,GetEventRoom());
|
|
|
|
GetMap()->CheckEvent(real_x, real_z, this);
|
|
|
|
// Petimi yolla pnp
|
|
if(isSummonPet)
|
|
{
|
|
_ITEM_TABLE * pItemData = nullptr;
|
|
if((pItemData = GetItemPrototype(SHOULDER)) == nullptr
|
|
|| !pItemData->isPet())
|
|
return;
|
|
|
|
_ITEM_DATA *pItem = nullptr;
|
|
|
|
if ((pItem = GetItem(SHOULDER)) == nullptr
|
|
|| pItem->nNum != pItemData->Getnum())
|
|
return;
|
|
|
|
CPet * pPet = g_pMain->GetPetPtr(pItem->nSerialNum);
|
|
if (pPet == nullptr)
|
|
return;
|
|
|
|
if(isSummonPet && SummonPetID > 0)
|
|
{
|
|
float x,z;
|
|
if( m_curx < m_oldx || m_curx == m_oldx)
|
|
x = real_x - 1;
|
|
else
|
|
x = real_x + 1;
|
|
|
|
if( m_curz < m_oldz || m_curz == m_oldz)
|
|
z = real_z - 1;
|
|
else
|
|
z = real_z + 1;
|
|
pPet->Moving(x,m_cury,z,uint8(speed));
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
result.Initialize(AG_USER_MOVE);
|
|
result << GetSocketID() << m_curx << m_curz << m_cury << speed;
|
|
Send_AIServer(&result);
|
|
|
|
}
|
|
|
|
void CUser::AddToRegion(int16 new_region_x, int16 new_region_z)
|
|
{
|
|
GetRegion()->Remove(this);
|
|
SetRegion(new_region_x, new_region_z);
|
|
GetRegion()->Add(this);
|
|
}
|
|
|
|
void CUser::GetInOut(Packet & result, uint8 bType)
|
|
{
|
|
result.Initialize(WIZ_USER_INOUT);
|
|
result << uint16(bType) << GetID();
|
|
if (bType != INOUT_OUT)
|
|
GetUserInfo(result);
|
|
}
|
|
|
|
void CUser::UserInOut(uint8 bType)
|
|
{
|
|
if (GetRegion() == NULL)
|
|
return;
|
|
if(this == nullptr)
|
|
return;
|
|
Packet result;
|
|
|
|
if (bType != INOUT_OUT)
|
|
ResetGMVisibility();
|
|
|
|
GetInOut(result, bType);
|
|
|
|
if (bType == INOUT_OUT)
|
|
GetRegion()->Remove(this);
|
|
else
|
|
GetRegion()->Add(this);
|
|
|
|
// View Bug Fix by Terry
|
|
|
|
if (g_pMain->pTempleEvent.ActiveEvent != -1 && isInTempleEventZone())
|
|
{
|
|
SendToRegion(&result, this, GetEventRoom());
|
|
}
|
|
else
|
|
SendToRegion(&result);
|
|
|
|
if (bType == INOUT_OUT || !isBlinking())
|
|
{
|
|
result.Initialize(AG_USER_INOUT);
|
|
result.SByte();
|
|
result << bType << GetSocketID() << GetName() << m_curx << m_curz;
|
|
Send_AIServer(&result);
|
|
}
|
|
}
|
|
|
|
void CUser::GetUserInfo(Packet & pkt)
|
|
{
|
|
if (m_bAbnormalType == ABNORMAL_INVISIBLE)
|
|
return;
|
|
|
|
pkt.SByte();
|
|
pkt << GetName()
|
|
<< uint16(GetNation()) << GetClanID() << GetFame();
|
|
|
|
CKnights * pKnights = g_pMain->GetClanPtr(GetClanID());
|
|
if (pKnights == nullptr)
|
|
{
|
|
pkt << uint32(0) << uint16(0) << uint8(0) << uint16(-1) << uint32(0) << uint8(0);
|
|
}
|
|
else
|
|
{
|
|
CKnights *aKnights = g_pMain->GetClanPtr(pKnights->GetAllianceID());
|
|
|
|
pkt << pKnights->GetAllianceID()
|
|
<< pKnights->m_strName
|
|
<< pKnights->m_byGrade << pKnights->m_byRanking
|
|
<< uint16(pKnights->m_sMarkVersion) // symbol/mark version
|
|
<< pKnights->GetCapeID(aKnights) // cape ID
|
|
<< pKnights->m_bCapeR << pKnights->m_bCapeG << pKnights->m_bCapeB << uint8(0) // this is stored in 4 bytes after all.
|
|
// not sure what this is, but it (just?) enables the clan symbol on the cape
|
|
// value in dump was 9, but everything tested seems to behave as equally well...
|
|
// we'll probably have to implement logic to respect requirements.
|
|
<< uint8(1);
|
|
}
|
|
|
|
// There are two event-driven invisibility states; dispel on attack, and dispel on move.
|
|
// These are handled primarily server-side; from memory the client only cares about value 1 (which we class as 'dispel on move').
|
|
// As this is the only place where this flag is actually sent to the client, we'll just convert 'dispel on attack'
|
|
// back to 'dispel on move' as the client expects.
|
|
uint8 bInvisibilityType = m_bInvisibilityType;
|
|
if (bInvisibilityType != INVIS_NONE)
|
|
bInvisibilityType = INVIS_DISPEL_ON_MOVE;
|
|
|
|
pkt << GetLevel() << m_bRace << m_sClass
|
|
<< GetSPosX() << GetSPosZ() << GetSPosY()
|
|
<< m_bFace << m_nHair
|
|
<< m_bResHpType << uint32(m_bAbnormalType)
|
|
<< m_bNeedParty;
|
|
if (!isGM())
|
|
pkt << m_bAuthority;
|
|
else
|
|
pkt << uint8(250);
|
|
pkt << m_bPartyLeader // is party leader (bool)
|
|
<< bInvisibilityType // visibility state
|
|
<< uint8(m_teamColour); // team colour (i.e. in soccer, 0=none, 1=blue, 2=red)
|
|
if(isDevil())
|
|
pkt << uint16(1);
|
|
else
|
|
pkt << m_bIsHidingHelmet << m_bIsDevil; // either this is correct and items are super buggy, or it causes baldness. You choose.
|
|
pkt << m_sDirection // direction
|
|
<< m_bIsChicken // chicken/beginner flag
|
|
<< m_bRank // king flag
|
|
<< uint16(0)
|
|
<< m_bKnightsRank << m_bPersonalRank; // NP ranks (total, monthly)
|
|
|
|
|
|
uint8 equippedItems[] =
|
|
{
|
|
BREAST, LEG, HEAD, GLOVE, FOOT, SHOULDER, RIGHTHAND, LEFTHAND, CWING, CHELMET, CRIGHT, CLEFT, CTOP, FAIRY
|
|
};
|
|
|
|
|
|
|
|
foreach_array (i, equippedItems)
|
|
{
|
|
_ITEM_DATA * pItem = GetItem(equippedItems[i]);
|
|
|
|
if(pItem == nullptr)
|
|
continue;
|
|
|
|
/*if(!m_bIsHidingCospre)
|
|
equippedItems[8] = 0;
|
|
else
|
|
equippedItems[8] = CWING;*/
|
|
|
|
pkt << pItem->nNum << pItem->sDuration << pItem->bFlag;
|
|
|
|
}
|
|
|
|
|
|
|
|
pkt << GetZoneID() << uint8(-1) << uint8(-1) << uint16(0) << uint16(0) << uint16(0) << m_bIsHidingCospre << isGenieActive();
|
|
|
|
pkt << GetRebLevel() << GetCoverTitle() << uint32(isReturnee());
|
|
}
|
|
|
|
void CUser::Rotate(Packet & pkt)
|
|
{
|
|
if (isDead())
|
|
return;
|
|
|
|
Packet result(WIZ_ROTATE);
|
|
pkt >> m_sDirection;
|
|
result << GetSocketID() << m_sDirection;
|
|
|
|
SendToRegion(&result, this, GetEventRoom());
|
|
}
|
|
|
|
bool CUser::CanChangeZone(C3DMap * pTargetMap, WarpListResponse & errorReason)
|
|
{
|
|
// While unofficial, game masters should be allowed to teleport anywhere.
|
|
if (isGM())
|
|
return true;
|
|
|
|
// Generic error reason; this should only be checked when the method returns false.
|
|
errorReason = WarpListGenericError;
|
|
|
|
if (GetLevel() < pTargetMap->GetMinLevelReq()
|
|
|| (pTargetMap->GetID() == ZONE_ARDREAM && !g_pMain->OpenArdream)
|
|
|| GetLevel() > pTargetMap->GetMinLevelReq()
|
|
&& (pTargetMap->GetID() == ZONE_RONARK_LAND && !g_pMain->OpenCZ)
|
|
|| GetLevel() > pTargetMap->GetMinLevelReq()
|
|
&& (pTargetMap->GetID() == ZONE_RONARK_LAND_BASE && !g_pMain->OpenCZ))
|
|
{
|
|
errorReason = WarpListMinLevel;
|
|
return false;
|
|
}
|
|
|
|
if (GetLevel() > pTargetMap->GetMaxLevelReq()
|
|
&& (pTargetMap->GetID() == ZONE_ARDREAM && !g_pMain->OpenArdream)
|
|
|| GetLevel() > pTargetMap->GetMaxLevelReq()
|
|
&& (pTargetMap->GetID() == ZONE_RONARK_LAND && !g_pMain->OpenCZ)
|
|
|| GetLevel() > pTargetMap->GetMaxLevelReq()
|
|
&& (pTargetMap->GetID() == ZONE_RONARK_LAND_BASE && !g_pMain->OpenCZ))
|
|
{
|
|
errorReason = WarpListDoNotQualify;
|
|
return false;
|
|
}
|
|
|
|
switch (pTargetMap->GetID())
|
|
{
|
|
case ZONE_KARUS:
|
|
// Users may enter Luferson (1)/El Morad (2) if they are that nation,
|
|
if (GetNation() == pTargetMap->GetID())
|
|
return true;
|
|
|
|
// Users may also enter if there's a war invasion happening in that zone.
|
|
if (GetNation() == ELMORAD)
|
|
return g_pMain->m_byKarusOpenFlag;
|
|
else
|
|
return g_pMain->m_byElmoradOpenFlag;
|
|
case ZONE_ELMORAD:
|
|
// Users may enter Luferson (1)/El Morad (2) if they are that nation,
|
|
if (GetNation() == pTargetMap->GetID())
|
|
return true;
|
|
|
|
// Users may also enter if there's a war invasion happening in that zone.
|
|
if (GetNation() == KARUS)
|
|
return g_pMain->m_byElmoradOpenFlag;
|
|
else
|
|
return g_pMain->m_byKarusOpenFlag;
|
|
case ZONE_KARUS_ESLANT:
|
|
return GetNation() == pTargetMap->GetID() - 10;
|
|
case ZONE_ELMORAD_ESLANT:
|
|
return GetNation() == pTargetMap->GetID() - 10;
|
|
case ZONE_DELOS: // TODO: implement CSW logic.
|
|
if (g_pMain->m_byBattleOpen == CLAN_BATTLE && !g_pMain->m_byBattleSiegeWarTeleport || g_pMain->m_byBattleOpen == CLAN_BATTLE && GetClanID() <= 0 )
|
|
{
|
|
errorReason = WarpListNotDuringCSW;
|
|
return false;
|
|
}
|
|
if (GetLoyalty() <= 0)
|
|
{
|
|
errorReason = WarpListNeedNP;
|
|
return false;
|
|
}
|
|
return true;
|
|
case ZONE_JURAD_MOUNTAIN:
|
|
break;
|
|
case ZONE_BORDER_DEFENSE_WAR:
|
|
break;
|
|
|
|
case ZONE_BIFROST:
|
|
case ZONE_DARK_LAND:
|
|
case ZONE_PVP_EVENT:
|
|
if (g_pMain->isWarOpen() || g_pMain->OpenArdream)
|
|
{
|
|
errorReason = WarpListNotDuringWar;
|
|
return false;
|
|
}
|
|
|
|
if (GetLoyalty() <= 0)
|
|
{
|
|
errorReason = WarpListNeedNP;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
|
|
case ZONE_ARDREAM:
|
|
if (g_pMain->isWarOpen())
|
|
{
|
|
errorReason = WarpListNotDuringWar;
|
|
return false;
|
|
}
|
|
|
|
if (GetLoyalty() <= 0)
|
|
{
|
|
errorReason = WarpListNeedNP;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
|
|
case ZONE_RONARK_LAND_BASE:
|
|
case ZONE_RONARK_LAND:
|
|
if ((g_pMain->isWarOpen() && g_pMain->m_byBattleZoneType != ZONE_ARDREAM) || (g_pMain->m_bEventZoneIsActive && g_pMain->m_nEventZoneTime == 5))
|
|
{
|
|
errorReason = WarpListNotDuringWar;
|
|
return false;
|
|
}
|
|
|
|
if (GetLoyalty() <= 0)
|
|
{
|
|
errorReason = WarpListNeedNP;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
|
|
default:
|
|
// War zones may only be entered if that war zone is active.
|
|
if (pTargetMap->isWarZone())
|
|
{
|
|
if(pTargetMap->GetID() == ZONE_SNOW_BATTLE)
|
|
{
|
|
if ((pTargetMap->GetID() - ZONE_SNOW_BATTLE) != g_pMain->m_byBattleZone)
|
|
return false;
|
|
}
|
|
else if ((pTargetMap->GetID() - ZONE_BATTLE_BASE) != g_pMain->m_byBattleZone)
|
|
return false;
|
|
else if ((GetNation() == ELMORAD && g_pMain->m_byElmoradOpenFlag)
|
|
|| (GetNation() == KARUS && g_pMain->m_byKarusOpenFlag))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CUser::CanLevelQualify(uint8 sLevel)
|
|
{
|
|
int16 nStatTotal = 300 + (sLevel - 1) * 3;
|
|
uint8 nSkillTotal = (sLevel - 9) * 2;
|
|
|
|
if (sLevel > 60)
|
|
nStatTotal += 2 * (sLevel - 60);
|
|
|
|
if ((m_sPoints + GetStatTotal()) > nStatTotal || GetTotalSkillPoints() > nSkillTotal)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void CUser::ZoneChange(uint16 sNewZone, float x, float z, uint16 nEventRoom)
|
|
{
|
|
C3DMap * pMap = g_pMain->GetZoneByID(sNewZone);
|
|
_KNIGHTS_SIEGE_WARFARE *pKnightSiege = g_pMain->GetSiegeMasterKnightsPtr(1);
|
|
CKnights *pKnightsMaster = g_pMain->GetClanPtr(pKnightSiege->sMasterKnights);
|
|
|
|
if (pMap == nullptr)
|
|
return;
|
|
|
|
WarpListResponse errorReason;
|
|
if (!CanChangeZone(pMap, errorReason))
|
|
{
|
|
Packet result(WIZ_WARP_LIST, uint8(2));
|
|
|
|
result << uint8(errorReason);
|
|
|
|
if (errorReason == WarpListMinLevel)
|
|
result << pMap->GetMinLevelReq();
|
|
|
|
Send(&result);
|
|
return;
|
|
}
|
|
|
|
|
|
if(GetZoneID() == ZONE_STONE1)
|
|
g_pMain->IsBusy1[GetEventRoom()] = false;
|
|
if(GetZoneID() == ZONE_STONE2)
|
|
g_pMain->IsBusy2[GetEventRoom()] = false;
|
|
if(GetZoneID() == ZONE_STONE3)
|
|
g_pMain->IsBusy3[GetEventRoom()] = false;
|
|
|
|
if (x == 0.0f && z == 0.0f)
|
|
{
|
|
_START_POSITION * pStartPosition = g_pMain->GetStartPosition(sNewZone);
|
|
if (pStartPosition != nullptr)
|
|
{
|
|
x = (float)(GetNation() == KARUS ? pStartPosition->sKarusX : pStartPosition->sElmoradX + myrand(0, pStartPosition->bRangeX));
|
|
z = (float)(GetNation() == KARUS ? pStartPosition->sKarusZ : pStartPosition->sElmoradZ + myrand(0, pStartPosition->bRangeZ));
|
|
}
|
|
}
|
|
|
|
if (sNewZone == ZONE_DELOS)
|
|
{
|
|
if (pKnightSiege->sMasterKnights == GetClanID() && GetClanID())
|
|
{
|
|
if (GetNation() == KARUS)
|
|
{
|
|
x = (float)(455 + myrand(0, 5));
|
|
z = (float)(790 + myrand(0, 5));
|
|
}
|
|
else
|
|
{
|
|
x = (float)(555 + myrand(0, 5));
|
|
z = (float)(790 + myrand(0, 5));
|
|
}
|
|
}
|
|
}
|
|
|
|
m_bWarp = true;
|
|
m_bZoneChangeFlag = true;
|
|
|
|
// Random respawn position...
|
|
if (sNewZone == ZONE_CHAOS_DUNGEON)
|
|
{
|
|
short sx, sz;
|
|
GetStartPositionRandom(sx,sz,(uint8)sNewZone);
|
|
x = (float)sx;
|
|
z = (float)sz;
|
|
}
|
|
|
|
m_LastX = x;
|
|
m_LastZ = z;
|
|
|
|
|
|
uint16 OldEventRoom = GetEventRoom();
|
|
if (GetZoneID() != sNewZone && isInTempleEventZone((uint8)sNewZone))
|
|
{
|
|
if (!isEventUser() && !isGM())
|
|
g_pMain->AddEventUser(this);
|
|
|
|
if(!isGM())
|
|
g_pMain->SetEventUser(this);
|
|
|
|
nEventRoom = GetEventRoom();
|
|
SetUserEventRoom(OldEventRoom);
|
|
SetUnitEventRoom(OldEventRoom);
|
|
}
|
|
|
|
if(isGM())
|
|
m_TimeMonsterStone = UNIXTIME + 3000;
|
|
|
|
|
|
|
|
if (GetZoneID() != sNewZone)
|
|
{
|
|
|
|
UserInOut(INOUT_OUT);
|
|
|
|
m_bZoneChangeSameZone = false;
|
|
SetZoneAbilityChange(sNewZone);
|
|
|
|
SetUserEventRoom(nEventRoom);
|
|
SetUnitEventRoom(nEventRoom);
|
|
|
|
|
|
// Reset the user's anger gauge when leaving the zone
|
|
// Unknown if this is official behaviour, but it's logical.
|
|
if (GetAngerGauge() > 0)
|
|
UpdateAngerGauge(0);
|
|
|
|
|
|
/*
|
|
Here we also send a clan packet with subopcode 0x16 (with a byte flag of 2) if war zone/Moradon
|
|
or subopcode 0x17 (with nWarEnemyID) for all else
|
|
*/
|
|
|
|
if (isInParty() && !m_bZoneChangeSameZone)
|
|
if (isPartyLeader())
|
|
{
|
|
_PARTY_GROUP * pParty = g_pMain->GetPartyPtr(GetPartyID());
|
|
PartyPromote(pParty->uid[1]);
|
|
}
|
|
PartyRemove(GetSocketID());
|
|
|
|
if (hasRival())
|
|
RemoveRival();
|
|
|
|
ResetWindows();
|
|
}
|
|
else
|
|
{
|
|
m_bWarp = false;
|
|
Warp(uint16(x * 10), uint16(z * 10));
|
|
return;
|
|
}
|
|
|
|
|
|
if (sNewZone != ZONE_SNOW_BATTLE && GetZoneID() == ZONE_SNOW_BATTLE)
|
|
SetMaxHp(1);
|
|
else if (sNewZone != ZONE_CHAOS_DUNGEON && GetZoneID() == ZONE_CHAOS_DUNGEON)
|
|
{
|
|
SetMaxHp(1);
|
|
RobChaosSkillItems();
|
|
nEventRoom = 0;
|
|
}
|
|
else if((sNewZone != ZONE_BORDER_DEFENSE_WAR && GetZoneID() == ZONE_BORDER_DEFENSE_WAR)
|
|
|| (sNewZone != ZONE_JURAD_MOUNTAIN && GetZoneID() == ZONE_JURAD_MOUNTAIN))
|
|
{
|
|
SetMaxHp(1);
|
|
nEventRoom = 0;
|
|
}
|
|
else if (sNewZone == ZONE_FORGOTTEN_TEMPLE)
|
|
g_pMain->m_nForgettenTempleUsers.push_back(GetSocketID());
|
|
else if (sNewZone != ZONE_FORGOTTEN_TEMPLE && GetZoneID() == ZONE_FORGOTTEN_TEMPLE)
|
|
g_pMain->m_nForgettenTempleUsers.erase(std::remove(g_pMain->m_nForgettenTempleUsers.begin(), g_pMain->m_nForgettenTempleUsers.end(), GetSocketID()), g_pMain->m_nForgettenTempleUsers.end());
|
|
|
|
else if (sNewZone == ZONE_DARK_LAND || sNewZone == ZONE_KROWAZ_DOMINION || sNewZone == ZONE_RONARK_LAND_BASE || sNewZone == ZONE_ARDREAM || sNewZone == ZONE_PVP_EVENT || (sNewZone == ZONE_JURAD_MOUNTAIN && GetZoneID() != sNewZone) || sNewZone == ZONE_LOST_TEMPLE)
|
|
g_pMain->m_nEventZoneUsers.push_back(GetSocketID());
|
|
else if ((sNewZone != ZONE_DARK_LAND && GetZoneID() == ZONE_DARK_LAND) || (sNewZone != ZONE_LOST_TEMPLE && GetZoneID() == ZONE_LOST_TEMPLE) || (sNewZone != ZONE_KROWAZ_DOMINION && GetZoneID() == ZONE_KROWAZ_DOMINION)
|
|
|| (sNewZone != ZONE_JURAD_MOUNTAIN && GetZoneID() == ZONE_JURAD_MOUNTAIN) || (sNewZone != ZONE_RONARK_LAND_BASE && GetZoneID() == ZONE_RONARK_LAND_BASE) || (sNewZone != ZONE_ARDREAM && GetZoneID() == ZONE_ARDREAM) || (sNewZone != ZONE_PVP_EVENT && GetZoneID() == ZONE_PVP_EVENT))
|
|
g_pMain->m_nEventZoneUsers.erase(std::remove(g_pMain->m_nEventZoneUsers.begin(), g_pMain->m_nEventZoneUsers.end(), GetSocketID()), g_pMain->m_nEventZoneUsers.end());
|
|
|
|
m_bZone = (uint8) sNewZone; // this is 2 bytes to support the warp data loaded from SMDs. It should not go above a byte, however.
|
|
|
|
if(GetZoneID() != sNewZone)
|
|
{
|
|
Packet result2(AG_USER_EVENTROOM);
|
|
result2 << GetID() << uint16(nEventRoom);
|
|
g_pMain->Send_AIServer(&result2);
|
|
}
|
|
|
|
SetPosition(x, 0.0f, z);
|
|
m_pMap = pMap;
|
|
|
|
|
|
SetRegion(GetNewRegionX(), GetNewRegionZ());
|
|
|
|
Packet result(WIZ_ZONE_CHANGE, uint8(ZoneChangeTeleport));
|
|
result << uint16(GetZoneID()) << GetSPosX() << GetSPosZ() << GetSPosY() << g_pMain->m_byOldVictory;
|
|
Send(&result);
|
|
|
|
if (!m_bZoneChangeSameZone)
|
|
{
|
|
m_sWhoKilledMe = -1;
|
|
m_iLostExp = 0;
|
|
m_bRegeneType = 0;
|
|
m_tLastRegeneTime = 0;
|
|
m_sBind = -1;
|
|
InitType3();
|
|
InitType4();
|
|
CMagicProcess::CheckExpiredType9Skills(this, true);
|
|
SetUserAbility();
|
|
}
|
|
|
|
if(isSummonPet)
|
|
{
|
|
_ITEM_TABLE * pItemData = nullptr;
|
|
if(this == nullptr
|
|
|| (pItemData = GetItemPrototype(SHOULDER)) == nullptr
|
|
|| !pItemData->isPet())
|
|
return;
|
|
_ITEM_DATA *pItem = nullptr;
|
|
|
|
if ((pItem = GetItem(SHOULDER)) == nullptr
|
|
|| pItem->nNum != pItemData->Getnum())
|
|
return;
|
|
|
|
CPet *newPet = g_pMain->GetPetPtr(pItem->nSerialNum);
|
|
if(newPet == nullptr || newPet->m_pNpc == nullptr)
|
|
return;
|
|
|
|
if(newPet->m_pNpc != nullptr)
|
|
{
|
|
if(newPet->m_pNpc->isAlive())
|
|
newPet->Dead();
|
|
isSummonPet = false;
|
|
}
|
|
}
|
|
|
|
result.Initialize(AG_ZONE_CHANGE);
|
|
result << GetSocketID() << GetZoneID();
|
|
Send_AIServer(&result);
|
|
|
|
g_pMain->TempleEventSendActiveEventTime(this);
|
|
|
|
m_bZoneChangeFlag = false;
|
|
|
|
if (GetZoneID() == ZONE_BATTLE4)
|
|
g_pMain->NereidsMonumentEvent(0,0,this);
|
|
|
|
|
|
}
|
|
|
|
|
|
void CUser::CheckWaiting(uint8 sNewZone,uint16 Time)
|
|
{
|
|
uint16 m_RemainEventZone = Time;
|
|
|
|
if (sNewZone != ZONE_STONE1
|
|
&& sNewZone != ZONE_STONE2
|
|
&& sNewZone != ZONE_STONE3)
|
|
{
|
|
Packet hata(WIZ_EVENT);
|
|
hata << uint8(1); // Inventory Hatas?.
|
|
Send(&hata);
|
|
}
|
|
|
|
if (sNewZone == ZONE_CHAOS_DUNGEON)
|
|
{
|
|
Packet chaos(WIZ_SELECT_MSG);
|
|
chaos << uint16(0x00) << uint8(0x07) << uint64(0x00) << 0x09 << uint8(0x18) << uint16(0x1B) << uint8(0x00) << Time;
|
|
Send(&chaos);
|
|
|
|
Packet chaostime(WIZ_BIFROST, uint8(MONSTER_SQUARD));
|
|
chaostime << uint16(m_RemainEventZone);
|
|
Send(&chaostime);
|
|
}
|
|
|
|
if (sNewZone == ZONE_BORDER_DEFENSE_WAR)
|
|
{
|
|
Packet border(WIZ_SELECT_MSG);
|
|
border << uint16(0x00) << uint8(0x07) << uint64(0x00) << uint8(0x08) << uint32(0x00) << uint8(0x09) << Time << uint16(0x00);
|
|
Send(&border);
|
|
|
|
Packet bordertime(WIZ_BIFROST, uint8(MONSTER_SQUARD));
|
|
bordertime << uint16(m_RemainEventZone);
|
|
Send(&bordertime);
|
|
}
|
|
|
|
if (sNewZone == ZONE_JURAD_MOUNTAIN)
|
|
{
|
|
/*Packet juraid(WIZ_SELECT_MSG);
|
|
juraid << uint16(0x00) << uint8(0x07) << uint64(0x00) << uint8(0x08) << uint32(0x00) << uint8(0x09) << Time << uint16(0x00);
|
|
Send(&juraid);
|
|
|
|
Packet juraidtime(WIZ_BIFROST, uint8(MONSTER_SQUARD));
|
|
juraidtime << uint16(m_RemainEventZone);
|
|
Send(&juraidtime);*/
|
|
|
|
}
|
|
|
|
if (sNewZone == ZONE_STONE1 || sNewZone == ZONE_STONE2 || sNewZone == ZONE_STONE3)
|
|
{
|
|
|
|
Packet stone(WIZ_SELECT_MSG);
|
|
stone << uint16(0x00) << uint8(0x09) << uint64(0x00) << uint8(0x09) << uint32(0x00) << uint8(0x10) << Time << uint16(0x00);
|
|
Send(&stone);
|
|
|
|
Packet stonetime(WIZ_BIFROST, uint8(MONSTER_SQUARD));
|
|
stonetime << uint16(m_RemainEventZone);
|
|
Send(&stonetime);
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
void CUser::PlayerRankingProcess(uint16 ZoneID, bool RemoveInZone)
|
|
{
|
|
if(m_bZoneChangeSameZone)
|
|
return;
|
|
|
|
if (ZoneID == ZONE_ARDREAM
|
|
|| ZoneID == ZONE_RONARK_LAND_BASE
|
|
|| ZoneID == ZONE_PVP_EVENT
|
|
|| ZoneID == ZONE_RONARK_LAND
|
|
|| ZoneID == ZONE_BORDER_DEFENSE_WAR
|
|
|| ZoneID == ZONE_CHAOS_DUNGEON
|
|
|| ZoneID == ZONE_JURAD_MOUNTAIN)
|
|
{
|
|
if (RemoveInZone)
|
|
RemovePlayerRank();
|
|
else
|
|
{
|
|
RemovePlayerRank();
|
|
AddPlayerRank(ZoneID);
|
|
}
|
|
}
|
|
else
|
|
RemovePlayerRank();
|
|
}
|
|
|
|
void CUser::AddPlayerRank(uint16 ZoneID)
|
|
{
|
|
m_iLoyaltyDaily = 0;
|
|
m_iLoyaltyPremiumBonus = 0;
|
|
m_KillCount = 0;
|
|
m_DeathCount = 0;
|
|
|
|
_USER_RANKING * pData = new _USER_RANKING;
|
|
|
|
pData->m_socketID = GetSocketID();
|
|
pData->m_iLoyaltyDaily = m_iLoyaltyDaily;
|
|
pData->m_iLoyaltyPremiumBonus = m_iLoyaltyPremiumBonus;
|
|
pData->m_KillCount = 0;
|
|
pData->m_DeathCount = 0;
|
|
pData->pUser = this;
|
|
|
|
if (!g_pMain->m_UserRankingArray[GetNation() - 1].PutData(pData->m_socketID, pData))
|
|
delete pData;
|
|
}
|
|
|
|
void CUser::RemovePlayerRank()
|
|
{
|
|
if(GetNation() < 1 || GetNation() > 2)
|
|
return;
|
|
|
|
g_pMain->m_UserRankingArray[GetNation() - 1].DeleteData(GetSocketID());
|
|
|
|
}
|
|
|
|
void CUser::UpdatePlayerRank()
|
|
{
|
|
if (isGM())
|
|
return;
|
|
|
|
_USER_RANKING * pRankInfo = g_pMain->m_UserRankingArray[GetNation() -1].GetData(GetSocketID());
|
|
if (pRankInfo == nullptr)
|
|
return;
|
|
|
|
pRankInfo->m_iLoyaltyDaily = m_iLoyaltyDaily;
|
|
pRankInfo->m_iLoyaltyPremiumBonus = m_iLoyaltyPremiumBonus;
|
|
pRankInfo->m_KillCount = m_KillCount;
|
|
pRankInfo->m_DeathCount = m_DeathCount;
|
|
}
|
|
/**
|
|
* @brief Changes the zone of all party members within the user's zone.
|
|
* If the user is not in a party, they should still be teleported.
|
|
*
|
|
* @param sNewZone ID of the new zone.
|
|
* @param x The x coordinate.
|
|
* @param z The z coordinate.
|
|
*/
|
|
void CUser::ZoneChangeParty(uint16 sNewZone, float x, float z)
|
|
{
|
|
_PARTY_GROUP * pParty = g_pMain->GetPartyPtr(GetPartyID());
|
|
if (pParty == nullptr)
|
|
return ZoneChange(sNewZone, x, z);
|
|
|
|
short partyUsers[MAX_PARTY_USERS];
|
|
|
|
for (int i = 0; i < MAX_PARTY_USERS; i++)
|
|
partyUsers[i] = pParty->uid[i];
|
|
|
|
for (int i = 0; i < MAX_PARTY_USERS; i++)
|
|
{
|
|
CUser * pUser = g_pMain->GetUserPtr(partyUsers[i]);
|
|
if (pUser != nullptr)
|
|
pUser->ZoneChange(sNewZone, x, z);
|
|
}
|
|
}
|
|
/**
|
|
* @brief Changes the zone of all clan members in home/neutral zones (including Eslant).
|
|
* If the user is not in a clan, they should not be teleported.
|
|
*
|
|
* @param sNewZone ID of the new zone.
|
|
* @param x The x coordinate.
|
|
* @param z The z coordinate.
|
|
*/
|
|
void CUser::ZoneChangeClan(uint16 sNewZone, float x, float z)
|
|
{
|
|
CKnights * pKnights = g_pMain->GetClanPtr(GetClanID());
|
|
if (pKnights == nullptr)
|
|
return;
|
|
|
|
for (int i = 0; i < MAX_CLAN_USERS; i++)
|
|
{
|
|
_KNIGHTS_USER * p = &pKnights->m_arKnightsUser[i];
|
|
CUser * pUser = p->pSession;
|
|
if (p->byUsed && pUser != nullptr && pUser->GetZoneID() < ZONE_DELOS)
|
|
pUser->ZoneChange(sNewZone, x, z);
|
|
}
|
|
}
|
|
|
|
void CUser::Warp(uint16 sPosX, uint16 sPosZ)
|
|
{
|
|
ASSERT(GetMap() != nullptr);
|
|
if (m_bWarp)
|
|
return;
|
|
|
|
float real_x = sPosX / 10.0f, real_z = sPosZ / 10.0f;
|
|
if (!GetMap()->IsValidPosition(real_x, real_z, 0.0f))
|
|
{
|
|
TRACE("Invalid position %f,%f\n", real_x, real_z);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
m_LastX = real_x;
|
|
m_LastZ = real_z;
|
|
|
|
Packet result(WIZ_WARP);
|
|
result << sPosX << sPosZ;
|
|
Send(&result);
|
|
|
|
|
|
|
|
UserInOut(INOUT_OUT);
|
|
|
|
m_curx = real_x;
|
|
m_curz = real_z;
|
|
|
|
Packet resul(AG_USER_MOVE);
|
|
resul << GetSocketID() << m_curx << m_curz << m_cury << int16(0);
|
|
Send_AIServer(&resul);
|
|
|
|
SetRegion(GetNewRegionX(), GetNewRegionZ());
|
|
|
|
UserInOut(INOUT_WARP);
|
|
|
|
|
|
g_pMain->UserInOutForMe(this);
|
|
g_pMain->RegionNpcInfoForMe(this);
|
|
g_pMain->MerchantUserInOutForMe(this);
|
|
LastWarpTime = UNIXTIME;
|
|
|
|
if(isSummonPet)
|
|
{
|
|
_ITEM_DATA *pItem = nullptr;
|
|
|
|
if ((pItem = GetItem(SHOULDER)) == nullptr)
|
|
return;
|
|
|
|
CPet *newPet = g_pMain->GetPetPtr(pItem->nSerialNum);
|
|
if(newPet == nullptr || newPet->m_pNpc == nullptr)
|
|
return;
|
|
|
|
if(newPet->m_pNpc != nullptr)
|
|
{
|
|
newPet->m_pNpc->SendInOut(INOUT_OUT,newPet->m_pNpc->GetX(),newPet->m_pNpc->GetZ(),newPet->m_pNpc->GetY());
|
|
SetPosition(m_curx,m_cury,m_curz);
|
|
newPet->m_pNpc->SendInOut(INOUT_IN,m_curx,m_cury,m_curz);
|
|
}
|
|
}
|
|
|
|
ResetWindows();
|
|
}
|
|
|
|
|
|
void CUser::RecvWarp(Packet & pkt)
|
|
{
|
|
uint16 warp_x, warp_z;
|
|
pkt >> warp_x >> warp_z;
|
|
Warp(warp_x, warp_z);
|
|
}
|
|
|
|
void CUser::RecvZoneChange(Packet & pkt)
|
|
{
|
|
if (isDead()) // we also need to make sure we're actually waiting on this request...
|
|
return;
|
|
|
|
uint8 opcode = pkt.read<uint8>();
|
|
if (opcode == ZoneChangeLoading)
|
|
{
|
|
g_pMain->RegionUserInOutForMe(this);
|
|
g_pMain->NpcInOutForMe(this);
|
|
g_pMain->MerchantUserInOutForMe(this);
|
|
|
|
Packet result(WIZ_ZONE_CHANGE, uint8(ZoneChangeLoaded)); // finalise the zone change
|
|
Send(&result);
|
|
|
|
}
|
|
else if (opcode == ZoneChangeLoaded)
|
|
{
|
|
UserInOut(INOUT_RESPAWN);
|
|
|
|
// TODO: Fix all this up (it's too messy/confusing)
|
|
if (!m_bZoneChangeSameZone)
|
|
BlinkStart();
|
|
|
|
if (GetZoneID() != ZONE_CHAOS_DUNGEON)
|
|
{
|
|
InitType4();
|
|
RecastSavedMagic();
|
|
}
|
|
|
|
m_bZoneChangeFlag = false;
|
|
m_bWarp = false;
|
|
|
|
_KNIGHTS_SIEGE_WARFARE *pKnightSiege = g_pMain->GetSiegeMasterKnightsPtr(1);
|
|
CKnights *pKnightsMaster = g_pMain->GetClanPtr(pKnightSiege->sMasterKnights);
|
|
|
|
if (GetZoneID() == ZONE_DELOS)
|
|
{
|
|
Packet result16(WIZ_SIEGE, uint8(2));
|
|
result16.SByte();
|
|
result16 << uint8((pKnightsMaster != nullptr && g_pMain->m_byBattleSiegeWarOpen ? 1 : 0) + 1)<< (pKnightsMaster != nullptr ? pKnightsMaster->GetID() : uint16(0)) << (pKnightsMaster != nullptr ? pKnightsMaster->m_sMarkVersion : uint16(0)) << uint16(0)
|
|
<< uint32(g_pMain->m_byBattleSiegeWarOpen ? g_pMain->m_byBattleSiegeWarOccupy : 0) << uint16(g_pMain->m_byBattleSiegeWarOpen ? ((50 * MINUTE) - g_pMain->m_sBattleTimeDelay) : 0)
|
|
<< (pKnightsMaster != nullptr ? pKnightsMaster->GetName() : std::string(""));
|
|
g_pMain->Send_Zone(&result16,ZONE_DELOS);
|
|
}
|
|
|
|
}
|
|
else if (opcode == MilitaryCampChange)
|
|
{
|
|
|
|
uint8 MilitaryCampID = pkt.read<uint8>();
|
|
|
|
if (GetZoneID() != ZONE_MORADON
|
|
&& GetZoneID() != ZONE_MORADONM2)
|
|
return;
|
|
if (MilitaryCampID != ZONE_MORADON
|
|
&& MilitaryCampID != ZONE_MORADONM2)
|
|
return;
|
|
|
|
if (MilitaryCampID == GetZoneID())
|
|
return;
|
|
|
|
ZoneChange(MilitaryCampID, 0.0f, 0.0f);
|
|
// Karus
|
|
// 1 1
|
|
// 2 5
|
|
// 3 6
|
|
// Elmorad
|
|
// 1 2
|
|
// 2 7
|
|
// 3 8
|
|
// Maradon
|
|
// 1 21
|
|
// 2 22
|
|
// 3 23
|
|
// 4 24
|
|
// 5 25
|
|
// Elmorad Eslant
|
|
// 1 12
|
|
// 2 15
|
|
// 3 16
|
|
// Karus Eslant
|
|
// 1 11
|
|
// 2 13
|
|
// 3 14
|
|
|
|
}
|
|
} |