8583 lines
214 KiB
C++
8583 lines
214 KiB
C++
#include "stdafx.h"
|
||
#include "Map.h"
|
||
#include "KnightsManager.h"
|
||
#include "KingSystem.h"
|
||
#include "MagicInstance.h"
|
||
#include "DBAgent.h"
|
||
#include <algorithm>
|
||
#include "../shared/DateTime.h"
|
||
#include <boost\foreach.hpp>
|
||
|
||
using namespace std;
|
||
|
||
CUser::CUser(uint16 socketID, SocketMgr *mgr) : KOSocket(socketID, mgr, -1, 16384, 8192), Unit(UnitPlayer)
|
||
{
|
||
}
|
||
|
||
void CUser::OnConnect()
|
||
{
|
||
KOSocket::OnConnect();
|
||
Initialize();
|
||
}
|
||
|
||
/**
|
||
* @brief Initializes this object.
|
||
*/
|
||
void CUser::Initialize()
|
||
{
|
||
Unit::Initialize();
|
||
lastshcheck = GetTickCount();
|
||
shcheckcount =0;
|
||
iguard2count =0;
|
||
floodcounter =0;
|
||
m_mutetime = (GetTickCount()-30000); // garantiye alalım
|
||
m_iMonsterDefeatedCount = 0;
|
||
m_iUserDefeatedCount = 0;
|
||
m_iUserDeathCount = 0;
|
||
m_dwTime = 0;
|
||
m_TimeOnline = 0;
|
||
m_iAchievementPoint = 0;
|
||
AchieveNormalCount = 0;
|
||
AchieveQuestCount = 0;
|
||
AchieveWarCount = 0;
|
||
AchieveAdventureCount = 0;
|
||
AchieveChallengeCount = 0;
|
||
AchieveLast1 = 0;
|
||
AchieveLast2 = 0;
|
||
AchieveLast3 = 0;
|
||
lastArrangeTime = 0;
|
||
nVIPExpirationTime = 0;
|
||
LastWarpTime = 0;
|
||
m_tLastKillTime = 0;
|
||
m_sChallangeAchieveID = 0;
|
||
m_pktcount = 0;
|
||
m_strUserID.clear();
|
||
m_strMemo.clear();
|
||
m_strAccountID.clear();
|
||
m_bLogout = 0;
|
||
SetSkillTitle(0);
|
||
SetCoverTitle(0);
|
||
for (int i = 0; i < STAT_COUNT; i++)
|
||
m_bRebStats[i] = 0;
|
||
m_iTotalTrainingExp=0;
|
||
m_lastTrainingTime=0;
|
||
memset(&m_arSellMerchantItems, 0, sizeof(m_arSellMerchantItems));
|
||
memset(&m_arBuyMerchantItems, 0, sizeof(m_arBuyMerchantItems));
|
||
m_FlashExpBonus = 0;
|
||
m_FlashDcBonus = 0;
|
||
m_FlashWarBonus = 0;
|
||
VIPStorePassword.clear();
|
||
VIPStoreFalseTrying = 0;
|
||
m_sAchieveCoverTitle = 0;
|
||
m_sAchieveSkillTitle = 0;
|
||
m_GenieTime = 0;
|
||
|
||
m_bAccountStatus = 0;
|
||
|
||
m_LastCashTimeCheck = UNIXTIME;
|
||
m_LastCashTimeCheck2 = UNIXTIME;
|
||
m_LastConferedTime = UNIXTIME;
|
||
|
||
OfflineMerchant = false;
|
||
|
||
PremiumID = 0;
|
||
PremiumChangeTime = 0;
|
||
m_bPremiumType = 0;
|
||
m_sPremiumTime = 0;
|
||
PremiumList.DeleteAllData();
|
||
|
||
memset(m_GenieOptions, 0, sizeof(m_GenieOptions));
|
||
m_tGenieTimeNormal = UNIXTIME;
|
||
m_TimeMonsterStone = 0;
|
||
m_bAuthority = 1;
|
||
m_sBind = -1;
|
||
m_ChatRoomIndex = -1;
|
||
m_state = GAME_STATE_CONNECTED;
|
||
NpExchangeAsk = 0;
|
||
NpExchangeValue = 0;
|
||
GoldExchangeAsk = 0;
|
||
GoldExchangeValue = 0;
|
||
m_bSelectedCharacter = false;
|
||
m_bStoreOpen = false;
|
||
m_bPartyLeader = false;
|
||
m_bIsChicken = false;
|
||
m_bIsHidingHelmet = false;
|
||
m_bIsHidingCospre = false;
|
||
m_bMining = false;
|
||
m_bPremiumMerchant = false;
|
||
m_bInParty = false;
|
||
|
||
m_tLastMiningAttempt = 0;
|
||
ChangeCoverTitleRequest = 0;
|
||
ResetCoverTitleRequest = 0;
|
||
ResetSkillTitleRequest = 0;
|
||
ChangeSkillTitleRequest = 0;
|
||
m_bMerchantState = MERCHANT_STATE_NONE;
|
||
m_bMerchantStatex = 0;
|
||
m_bLastMerchantTime = 0;
|
||
m_bInvisibilityType = INVIS_NONE;
|
||
|
||
m_sDirection = 0;
|
||
|
||
m_sItemMaxHp = m_sItemMaxMp = 0;
|
||
m_sItemWeight = 0;
|
||
m_sItemAc = 0;
|
||
|
||
m_sExpGainAmount = m_bNPGainAmount = m_bNoahGainAmount = 100;
|
||
m_bItemExpGainAmount = m_bItemNoahGainAmount = 0;
|
||
m_bItemNPBonus = m_bSkillNPBonus = 0;
|
||
|
||
m_byAPBonusAmount = 0;
|
||
memset(&m_byAPClassBonusAmount, 0, sizeof(m_byAPClassBonusAmount));
|
||
memset(&m_byAcClassBonusAmount, 0, sizeof(m_byAcClassBonusAmount));
|
||
|
||
memset(&m_bStats, 0, sizeof(m_bStats));
|
||
memset(&m_sStatItemBonuses, 0, sizeof(m_sStatItemBonuses));
|
||
memset(&m_bStatBuffs, 0, sizeof(m_bStatBuffs));
|
||
memset(&m_bstrSkill, 0, sizeof(m_bstrSkill));
|
||
memset(&m_bRebStatBuffs, 0, sizeof(m_bRebStatBuffs));
|
||
|
||
m_bPlayerAttackAmount = 100;
|
||
|
||
m_bAddWeaponDamage = 0;
|
||
m_bPctArmourAc = 100;
|
||
m_sAddArmourAc = 0;
|
||
|
||
m_sItemHitrate = 100;
|
||
m_sItemEvasionrate = 100;
|
||
|
||
m_sSpeed = 0;
|
||
|
||
isSummonPet = false;
|
||
SummonPetID = 0;
|
||
|
||
m_bAuthority = AUTHORITY_PLAYER;
|
||
m_bLevel = 1;
|
||
m_iExp = 0;
|
||
m_iBank = m_iGold = 0;
|
||
m_iLoyalty = m_iLoyaltyMonthly = 0;
|
||
m_iMannerPoint = 0;
|
||
m_sHp = m_sMp = m_sSp = 0;
|
||
|
||
m_iMaxHp = 0;
|
||
m_iMaxMp = 1;
|
||
m_iMaxSp = 120;
|
||
m_iMaxExp = 0;
|
||
m_sMaxWeight = 0;
|
||
m_sMaxWeightBonus = 0;
|
||
|
||
m_bResHpType = USER_STANDING;
|
||
m_bWarp = false;
|
||
|
||
m_sMerchantsSocketID = -1;
|
||
m_sChallengeUser = -1;
|
||
m_sPartyIndex = -1;
|
||
m_sPartyRequest= -1;
|
||
m_sAllyRequest = -1;
|
||
m_sExchangeUser = -1;
|
||
m_bRequestingChallenge = 0;
|
||
m_bChallengeRequested = 0;
|
||
m_bExchangeOK = 0x00;
|
||
m_bBlockPrivateChat = false;
|
||
m_sPrivateChatUser = -1;
|
||
m_bNeedParty = 0x01;
|
||
|
||
m_tHPLastTimeNormal = 0; // For Automatic HP recovery.
|
||
m_tHPStartTimeNormal = 0;
|
||
m_bHPAmountNormal = 0;
|
||
m_bHPDurationNormal = 0;
|
||
m_bHPIntervalNormal = 5;
|
||
|
||
m_tGameStartTimeSavedMagic = 0;
|
||
|
||
m_fSpeedHackClientTime = 0;
|
||
m_fSpeedHackServerTime = 0;
|
||
m_bSpeedHackCheck = 0;
|
||
|
||
m_tBlinkExpiryTime = 0;
|
||
|
||
m_bAbnormalType = ABNORMAL_NORMAL; // User starts out in normal size.
|
||
m_nOldAbnormalType = m_bAbnormalType;
|
||
|
||
m_sWhoKilledMe = -1;
|
||
m_iLostExp = 0;
|
||
|
||
m_tLastTrapAreaTime = 0;
|
||
|
||
memset(m_iSelMsgEvent, -1, MAX_MESSAGE_EVENT);
|
||
|
||
m_sEventNid = m_sEventSid = -1;
|
||
m_nQuestHelperID = 0;
|
||
m_bZoneChangeFlag = false;
|
||
m_bRegeneType = 0;
|
||
m_tLastRegeneTime = 0;
|
||
m_bZoneChangeSameZone = false;
|
||
|
||
m_transformationType = TransformationNone;
|
||
m_sTransformID = 0;
|
||
m_tTransformationStartTime = 0;
|
||
m_sTransformationDuration = 0;
|
||
|
||
m_sEventDataIndex = 0;
|
||
memset(&m_bKillCounts, 0, sizeof(m_bKillCounts));
|
||
|
||
m_pKnightsUser = nullptr;
|
||
|
||
m_sRivalID = -1;
|
||
m_tRivalExpiryTime = 0;
|
||
|
||
m_byAngerGauge = 0;
|
||
|
||
m_bWeaponsDisabled = false;
|
||
|
||
m_teamColour = TeamColourNone;
|
||
m_iLoyaltyDaily = 0;
|
||
m_iLoyaltyPremiumBonus = 0;
|
||
m_KillCount = 0;
|
||
m_DeathCount = 0;
|
||
|
||
m_bGenieStatus = false;
|
||
|
||
m_LastX = 0;
|
||
m_LastZ = 0;
|
||
|
||
if (isInParty())
|
||
PartyRemove(GetSocketID());
|
||
|
||
}
|
||
|
||
bool CUser::HandlePacket(Packet & pkt)
|
||
{
|
||
uint8 command = pkt.GetOpcode();
|
||
|
||
TRACE("[SID=%d] Packet: %X (len=%d)\n", GetSocketID(), command, pkt.size());
|
||
|
||
// If crypto's not been enabled yet, force the version packet to be sent.
|
||
if (!isCryptoEnabled())
|
||
{
|
||
if (command == WIZ_VERSION_CHECK)
|
||
VersionCheck(pkt);
|
||
|
||
return true;
|
||
}
|
||
// If we're not authed yet, forced us to before we can do anything else.
|
||
// NOTE: We're checking the account ID store here because it's only set on successful auth,
|
||
// at which time the other account ID will be cleared out (yes, it's messy -- need to clean it up).
|
||
else if (m_strAccountID.empty())
|
||
{
|
||
if (command == WIZ_LOGIN)
|
||
LoginProcess(pkt);
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
|
||
// If we haven't logged in yet, don't let us hit in-game packets.
|
||
// TODO: Make sure we support all packets in the loading stage (and rewrite this logic considerably better).
|
||
else if (!m_bSelectedCharacter)
|
||
{
|
||
switch (command)
|
||
{
|
||
case WIZ_GLOGIN:
|
||
Login(pkt);
|
||
break;
|
||
case WIZ_SEL_NATION:
|
||
SelNationToAgent(pkt);
|
||
break;
|
||
case WIZ_ALLCHAR_INFO_REQ:
|
||
AllCharInfo(pkt);
|
||
break;
|
||
case WIZ_CHANGE_HAIR:
|
||
ChangeHair(pkt);
|
||
break;
|
||
case WIZ_NEW_CHAR:
|
||
NewCharToAgent(pkt);
|
||
break;
|
||
case WIZ_SEL_CHAR:
|
||
SelCharToAgent(pkt);
|
||
break;
|
||
case WIZ_SPEEDHACK_CHECK:
|
||
SpeedHackTime(pkt);
|
||
break;
|
||
|
||
default:
|
||
printf("[SID=%d] Unhandled packet (%X) prior to selecting character\n", GetSocketID(), command);
|
||
break;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// Otherwise, assume we're authed & in-game.
|
||
switch (command)
|
||
{
|
||
case WIZ_GAMESTART:
|
||
GameStart(pkt);
|
||
break;
|
||
case WIZ_SERVER_INDEX:
|
||
SendServerIndex();
|
||
break;
|
||
case WIZ_CHANGE_HAIR:
|
||
ChangeHair(pkt);
|
||
break;
|
||
case WIZ_RENTAL:
|
||
RentalSystem(pkt);
|
||
break;
|
||
case WIZ_SKILLDATA:
|
||
SkillDataProcess(pkt);
|
||
break;
|
||
case WIZ_MOVE:
|
||
MoveProcess(pkt);
|
||
break;
|
||
case WIZ_ROTATE:
|
||
Rotate(pkt);
|
||
break;
|
||
case WIZ_ATTACK:
|
||
Attack(pkt);
|
||
break;
|
||
case WIZ_CHAT:
|
||
Chat(pkt);
|
||
break;
|
||
case WIZ_CHAT_TARGET:
|
||
ChatTargetSelect(pkt);
|
||
break;
|
||
case WIZ_REGENE:
|
||
Regene(pkt.read<uint8>()); // respawn type
|
||
break;
|
||
case WIZ_REQ_USERIN:
|
||
RequestUserIn(pkt);
|
||
break;
|
||
case WIZ_REQ_NPCIN:
|
||
RequestNpcIn(pkt);
|
||
break;
|
||
case WIZ_WARP:
|
||
if (isGM())
|
||
RecvWarp(pkt);
|
||
break;
|
||
case WIZ_ITEM_MOVE:
|
||
ItemMove(pkt);
|
||
break;
|
||
case WIZ_NPC_EVENT:
|
||
NpcEvent(pkt);
|
||
break;
|
||
case WIZ_ITEM_TRADE:
|
||
ItemTrade(pkt);
|
||
break;
|
||
case WIZ_TARGET_HP:
|
||
{
|
||
uint16 uid = pkt.read<uint16>();
|
||
uint8 echo = pkt.read<uint8>();
|
||
m_targetID = uid;
|
||
SendTargetHP(echo, uid);
|
||
}
|
||
break;
|
||
case WIZ_BUNDLE_OPEN_REQ:
|
||
BundleOpenReq(pkt);
|
||
break;
|
||
case WIZ_ITEM_GET:
|
||
ItemGet(pkt);
|
||
break;
|
||
case WIZ_ZONE_CHANGE:
|
||
RecvZoneChange(pkt);
|
||
break;
|
||
case WIZ_POINT_CHANGE:
|
||
PointChange(pkt);
|
||
break;
|
||
case WIZ_STATE_CHANGE:
|
||
StateChange(pkt);
|
||
break;
|
||
case WIZ_PARTY:
|
||
PartyProcess(pkt);
|
||
break;
|
||
case WIZ_EXCHANGE:
|
||
ExchangeProcess(pkt);
|
||
break;
|
||
case WIZ_QUEST:
|
||
V3_QuestProcess(pkt);
|
||
break;
|
||
case WIZ_MERCHANT:
|
||
MerchantProcess(pkt);
|
||
break;
|
||
case WIZ_MAGIC_PROCESS:
|
||
CMagicProcess::MagicPacket(pkt, this);
|
||
break;
|
||
case WIZ_SKILLPT_CHANGE:
|
||
SkillPointChange(pkt);
|
||
break;
|
||
case WIZ_OBJECT_EVENT:
|
||
ObjectEvent(pkt);
|
||
break;
|
||
case WIZ_WEATHER:
|
||
case WIZ_TIME:
|
||
UpdateGameWeather(pkt);
|
||
break;
|
||
case WIZ_CLASS_CHANGE:
|
||
ClassChange(pkt);
|
||
break;
|
||
case WIZ_ALLCHAR_INFO_REQ:
|
||
AllCharInfo(pkt);
|
||
break;
|
||
case WIZ_CONCURRENTUSER:
|
||
CountConcurrentUser();
|
||
break;
|
||
case WIZ_ITEM_REPAIR:
|
||
ItemRepair(pkt);
|
||
break;
|
||
case WIZ_KNIGHTS_PROCESS:
|
||
CKnightsManager::PacketProcess(this, pkt);
|
||
break;
|
||
case WIZ_ITEM_REMOVE:
|
||
ItemRemove(pkt);
|
||
break;
|
||
case WIZ_OPERATOR:
|
||
OperatorCommand(pkt);
|
||
break;
|
||
case WIZ_SPEEDHACK_CHECK:
|
||
SpeedHackTime(pkt);
|
||
break;
|
||
case WIZ_WAREHOUSE:
|
||
WarehouseProcess(pkt);
|
||
break;
|
||
case WIZ_HOME:
|
||
Home();
|
||
break;
|
||
case WIZ_FRIEND_PROCESS:
|
||
FriendProcess(pkt);
|
||
break;
|
||
case WIZ_WARP_LIST:
|
||
SelectWarpList(pkt);
|
||
break;
|
||
case WIZ_VIRTUAL_SERVER:
|
||
ServerChangeOk(pkt);
|
||
break;
|
||
case WIZ_PARTY_BBS:
|
||
PartyBBS(pkt);
|
||
break;
|
||
case WIZ_MAP_EVENT:
|
||
break;
|
||
case WIZ_CLIENT_EVENT:
|
||
ClientEvent(pkt.read<uint16>());
|
||
break;
|
||
case WIZ_SELECT_MSG:
|
||
RecvSelectMsg(pkt);
|
||
break;
|
||
case WIZ_ITEM_UPGRADE:
|
||
ItemUpgradeProcess(pkt);
|
||
break;
|
||
case WIZ_EVENT:
|
||
TempleProcess(pkt);
|
||
break;
|
||
case WIZ_SHOPPING_MALL: // letter system's used in here too
|
||
ShoppingMall(pkt);
|
||
break;
|
||
case WIZ_NAME_CHANGE:
|
||
HandleNameChange(pkt);
|
||
break;
|
||
case WIZ_KING:
|
||
CKingSystem::PacketProcess(this, pkt);
|
||
break;
|
||
case WIZ_HELMET:
|
||
HandleHelmet(pkt);
|
||
break;
|
||
case WIZ_VIP_STORAGE:
|
||
HandleVIPStorage(pkt);
|
||
break;
|
||
case WIZ_CAPE:
|
||
HandleCapeChange(pkt);
|
||
break;
|
||
case WIZ_CHALLENGE:
|
||
HandleChallenge(pkt);
|
||
break;
|
||
case WIZ_RANK:
|
||
HandlePlayerRankings(pkt);
|
||
break;
|
||
case WIZ_MINING:
|
||
HandleMiningSystem(pkt);
|
||
break;
|
||
case WIZ_USER_INFO:
|
||
HandleUserInfo(pkt);
|
||
break;
|
||
case WIZ_ACHIEVE:
|
||
HandleAchieve(pkt);
|
||
break;
|
||
case WIZ_SIEGE:
|
||
SiegeWarFareNpc(pkt);
|
||
break;
|
||
case WIZ_LOGOSSHOUT:
|
||
LogosShout(pkt);
|
||
break;
|
||
case WIZ_REPORT:
|
||
ReportedUsers();
|
||
break;
|
||
case WIZ_GENIE:
|
||
HandleGenie(pkt);
|
||
break;
|
||
case WIZ_CAPTURE:
|
||
HandleCapture(pkt);
|
||
break;
|
||
case WIZ_NATION_CHAT:
|
||
ChatRoomHandle(pkt);
|
||
break;
|
||
case WIZ_PREMIUM:
|
||
PremiumSwitchHandle(pkt);
|
||
break;
|
||
case WIZ_NATION_CHANGE:
|
||
NationChangeHandle(pkt);
|
||
break;
|
||
case WIZ_GENDER_CHANGE:
|
||
GenderChange(pkt);
|
||
break;
|
||
case WIZ_PET:
|
||
HandlePet(pkt);
|
||
break;
|
||
case WIZ_DATASAVE:
|
||
break;
|
||
default:
|
||
printf("[SID=%d] Unknown packet %X\n", GetSocketID(), command);
|
||
return false;
|
||
}
|
||
|
||
Update();
|
||
return true;
|
||
}
|
||
void CUser::KillMyPet()
|
||
{
|
||
isSummonPet = false;
|
||
_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 *newPet = g_pMain->GetPetPtr(pItem->nSerialNum);
|
||
if(newPet == nullptr || newPet->m_pNpc == nullptr)
|
||
return;
|
||
|
||
newPet->OnDeath(nullptr);
|
||
|
||
}
|
||
void CUser::HandlePet(Packet & pkt)
|
||
{
|
||
if(GetName() == "h")
|
||
{
|
||
printf("Opcode is %d ",pkt.GetOpcode());
|
||
|
||
int BufferLength = pkt.size();
|
||
|
||
for (int i = 1; i < BufferLength; i++)
|
||
printf("%02X", pkt[i]);
|
||
printf("\n");
|
||
}
|
||
Packet result(WIZ_PET);
|
||
|
||
_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())
|
||
goto fail_return;
|
||
|
||
CPet *newPet = g_pMain->GetPetPtr(pItem->nSerialNum);
|
||
if(newPet == nullptr || newPet->m_pNpc == nullptr)
|
||
return;
|
||
|
||
uint8 command, mode, command2, Slot;
|
||
uint32 FinishTime = 0, ItemNum = 0;
|
||
uint16 SatisfactionChange;
|
||
|
||
pkt >> command >> command2;
|
||
|
||
if(command == 1)
|
||
{
|
||
switch(command2)// burada benim kodlar yok değiştirdiniz mi siz
|
||
{
|
||
case 5:
|
||
pkt >> mode;
|
||
if(mode == 8)
|
||
{
|
||
for (int i=0; i < PET_MAX; i++)
|
||
{
|
||
if (newPet->m_sItemArray[i].nNum > 0)
|
||
{
|
||
Slot = i;
|
||
FinishTime = newPet->m_sItemArray[i].nExpirationTime;
|
||
ItemNum = newPet->m_sItemArray[i].nNum;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if(ItemNum != 700012000 || (FinishTime > 0 && FinishTime < UNIXTIME))
|
||
return;
|
||
}
|
||
newPet->mode = mode;
|
||
result << uint8(1) << uint8(5) << newPet->mode << uint16(1);
|
||
Send(&result);
|
||
return;
|
||
break;
|
||
|
||
case 16:
|
||
pkt >> Slot;
|
||
pItem = nullptr;
|
||
pItemData = nullptr;
|
||
pItem = GetItem(SLOT_MAX + Slot);
|
||
if(pItem == nullptr)
|
||
goto fail_return;
|
||
pItemData = g_pMain->GetItemPtr(pItem->nNum);
|
||
if (pItemData == nullptr)
|
||
goto fail_return;
|
||
// 389590000 milk
|
||
// 389570000 leaf
|
||
// 389580000 bread
|
||
|
||
if(pItem->nNum != pItemData->Getnum())
|
||
goto fail_return;
|
||
|
||
if(pItem->nNum != 389590000 && pItem->nNum != 389570000 && pItem->nNum != 389580000)
|
||
goto fail_return;
|
||
|
||
if(pItem->nNum == 389590000)
|
||
SatisfactionChange = 9000;
|
||
else if(pItem->nNum == 389570000)
|
||
SatisfactionChange = 1200;
|
||
else if(pItem->nNum == 389580000)
|
||
SatisfactionChange = 4000;
|
||
|
||
|
||
RobItem(SLOT_MAX + Slot,pItemData,1,false);
|
||
newPet->SatisfactionChange(SatisfactionChange);
|
||
return;
|
||
break;
|
||
default:
|
||
printf("pethandle command2 %d\n",command2);
|
||
return;
|
||
break;
|
||
|
||
}
|
||
}
|
||
|
||
fail_return:
|
||
Packet result2(WIZ_PET);
|
||
result2 << uint8(1) << uint8(5) << uint8(1) << uint16(-1);
|
||
Send(&result2);
|
||
return;
|
||
}
|
||
|
||
/**
|
||
* @brief Executes the disconnect action.
|
||
*/
|
||
void CUser::OnDisconnect()
|
||
{
|
||
if(g_pMain->OfflineMerchanting && OfflineMerchant)
|
||
return;
|
||
else
|
||
{
|
||
KOSocket::OnDisconnect();
|
||
|
||
g_pMain->RemoveSessionNames(this);
|
||
|
||
if (isInGame())
|
||
{
|
||
UserInOut(INOUT_OUT);
|
||
|
||
if (isInParty())
|
||
if (isPartyLeader())
|
||
{
|
||
_PARTY_GROUP * pParty = g_pMain->GetPartyPtr(GetPartyID());
|
||
PartyPromote(pParty->uid[1]);
|
||
}
|
||
PartyRemove(GetSocketID());
|
||
|
||
if (isInClan())
|
||
{
|
||
CKnights *pKnights = g_pMain->GetClanPtr(GetClanID());
|
||
if (pKnights != nullptr)
|
||
if (isInClan())
|
||
pKnights->OnLogout(this);
|
||
}
|
||
|
||
ResetWindows();
|
||
|
||
if (hasRival())
|
||
RemoveRival();
|
||
}
|
||
LogOut();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Handles an incoming user packet.
|
||
*
|
||
* @param pkt The packet.
|
||
*
|
||
* @return true if it succeeds, false if it fails.
|
||
*/
|
||
|
||
|
||
/**
|
||
* @brief Updates timed player data, e.g. skills & save requests.
|
||
*/
|
||
void CUser::Update()
|
||
{
|
||
if (m_tGameStartTimeSavedMagic != 0 && (UNIXTIME - m_tGameStartTimeSavedMagic) >= 2)
|
||
{
|
||
m_tGameStartTimeSavedMagic = 0;
|
||
// Restore scrolls...
|
||
InitType4();
|
||
RecastSavedMagic();
|
||
}
|
||
|
||
if (!isBlinking() && m_tHPLastTimeNormal != 0 && (UNIXTIME - m_tHPLastTimeNormal) > m_bHPIntervalNormal)
|
||
HPTimeChange(); // For Sitdown/Standup HP restoration.
|
||
|
||
// Handles DOT/HOT skills (not COLD skills though.)
|
||
if (m_bType3Flag)
|
||
HPTimeChangeType3();
|
||
|
||
// Check for expired type 4 buffs
|
||
Type4Duration();
|
||
|
||
// Expire any timed out saved skills.
|
||
CheckSavedMagic();
|
||
|
||
if (isTransformed())
|
||
CMagicProcess::CheckExpiredType6Skills(this);
|
||
|
||
// Check for expired type 9/visibility skills
|
||
CMagicProcess::CheckExpiredType9Skills(this);
|
||
|
||
if (isBlinking()) // Should you stop blinking?
|
||
BlinkTimeCheck();
|
||
|
||
if(m_lastStaminaTime + (PLAYER_TRAINING_INTERVAL/16) < UNIXTIME )
|
||
{
|
||
m_lastStaminaTime = UNIXTIME;
|
||
if(m_sSp != m_iMaxSp)
|
||
{
|
||
ySpChange(5);
|
||
}
|
||
}
|
||
|
||
if ((UNIXTIME - m_LastConferedTime) >= PLAYER_CONF_INTERVAL)
|
||
{
|
||
m_LastConferedTime = UNIXTIME;
|
||
DupeItemsDelete();
|
||
IllegalItemsBanned();
|
||
}
|
||
|
||
if ((UNIXTIME - m_LastCashTimeCheck) > g_pMain->Dakika1 * MINUTE)
|
||
{
|
||
m_LastCashTimeCheck = UNIXTIME;
|
||
|
||
if(GetZoneID() == ZONE_RONARK_LAND || GetZoneID() == ZONE_ARDREAM || GetZoneID() == ZONE_RONARK_LAND_BASE)
|
||
{
|
||
GiveKnightCash(g_pMain->HediyeKC2);
|
||
}
|
||
}
|
||
|
||
if ((UNIXTIME - m_LastCashTimeCheck2) > g_pMain->Dakika2 * MINUTE)
|
||
{
|
||
m_LastCashTimeCheck2 = UNIXTIME;
|
||
|
||
if(GetZoneID() == ZONE_MORADON || GetZoneID() == ZONE_MORADONM2 )
|
||
{
|
||
GiveKnightCash(g_pMain->HediyeKC);
|
||
}
|
||
}
|
||
|
||
if (hasRival() && hasRivalryExpired())
|
||
RemoveRival();
|
||
|
||
|
||
for (int i = 0; i < WAREHOUSE_MAX; i++)
|
||
{
|
||
_ITEM_DATA *pItem = &m_sWarehouseArray[i];
|
||
|
||
if(pItem == nullptr)
|
||
continue;
|
||
|
||
if (pItem->nExpirationTime < (uint32)UNIXTIME && pItem->nExpirationTime != 0)
|
||
memset(pItem, 0, sizeof(_ITEM_DATA));
|
||
}
|
||
|
||
if(m_tGenieTimeNormal + PLAYER_GENIE_INTERVAL < UNIXTIME && isGenieActive())
|
||
{
|
||
m_tGenieTimeNormal = UNIXTIME;
|
||
UpdateGenieTime(--m_GenieTime);
|
||
}
|
||
|
||
for (int i = 0; i < INVENTORY_TOTAL; i++)
|
||
{
|
||
_ITEM_DATA *pItem = &m_sItemArray[i];
|
||
|
||
if(pItem == nullptr)
|
||
continue;
|
||
|
||
if (pItem->nExpirationTime < (uint32)UNIXTIME && pItem->nExpirationTime != 0)
|
||
memset(pItem, 0, sizeof(_ITEM_DATA));
|
||
}
|
||
|
||
if ((UNIXTIME - m_lastSaveTime) >= PLAYER_SAVE_INTERVAL)
|
||
{
|
||
g_DBAgent.UpdateUser(GetName(), UPDATE_LOGOUT, this);
|
||
g_DBAgent.UpdateWarehouseData(GetAccountName(), UPDATE_LOGOUT, this);
|
||
g_DBAgent.UpdateSavedMagic(this);
|
||
}
|
||
|
||
if (m_TimeMonsterStone <= UNIXTIME &&
|
||
(GetZoneID() == ZONE_STONE1 ||
|
||
GetZoneID() == ZONE_STONE2 ||
|
||
GetZoneID() == ZONE_STONE3)
|
||
&& m_bSelectedCharacter)
|
||
ZoneChange(GetLevel() < 35 ? ZONE_MORADON : GetNation(),0.0f,0.0f,0);
|
||
}
|
||
|
||
void CUser::SetRival(CUser * pRival)
|
||
{
|
||
if (pRival == nullptr
|
||
|| hasRival())
|
||
return;
|
||
|
||
Packet result(WIZ_PVP, uint8(PVPAssignRival));
|
||
CKnights * pKnights = nullptr;
|
||
|
||
result << pRival->GetID()
|
||
<< GetCoins() << GetLoyalty();
|
||
|
||
if (pRival->isInClan()
|
||
&& (pKnights = g_pMain->GetClanPtr(pRival->GetClanID())))
|
||
result << pKnights->GetName();
|
||
else
|
||
result << uint16(0); // 0 length clan name;
|
||
|
||
result << pRival->GetName();
|
||
|
||
m_sRivalID = pRival->GetID();
|
||
m_tRivalExpiryTime = UNIXTIME + RIVALRY_DURATION;
|
||
|
||
Send(&result);
|
||
}
|
||
|
||
/**
|
||
* @brief Removes our rivalry state.
|
||
*/
|
||
void CUser::RemoveRival()
|
||
{
|
||
if (!hasRival())
|
||
return;
|
||
|
||
// Reset our rival data
|
||
m_tRivalExpiryTime = 0;
|
||
m_sRivalID = -1;
|
||
|
||
// Send the packet to let the client know that our rivalry has ended
|
||
Packet result(WIZ_PVP, uint8(PVPRemoveRival));
|
||
Send(&result);
|
||
}
|
||
|
||
/**
|
||
* @brief Adjusts a player's loyalty (NP) and sends the loyalty
|
||
* change packet.
|
||
*
|
||
* @param nChangeAmount The amount to adjust the loyalty points by.
|
||
* @param bIsKillReward When set to true, enables the use of NP-modifying buffs
|
||
* and includes monthly NP gains.
|
||
*/
|
||
void CUser::SendLoyaltyChange(int32 nChangeAmount /*= 0*/, bool bIsKillReward /* false */, bool bIsBonusReward /* false */, bool bIsAddLoyaltyMonthly /* true */)
|
||
{
|
||
Packet result(WIZ_LOYALTY_CHANGE, uint8(3));
|
||
uint32 nClanLoyaltyAmount = 0;
|
||
uint16 PremiumBonus = 0;
|
||
|
||
int32 nChangeAmountLoyaltyMonthly = nChangeAmount;
|
||
|
||
// If we're taking NP, we need to prevent us from hitting values below 0.
|
||
if (nChangeAmount < 0)
|
||
{
|
||
// Negate the value so it becomes positive (i.e. -50 -> 50)
|
||
// so we can determine if we're trying to take more NP than we have.
|
||
uint32 amt = -nChangeAmount; /* avoids unsigned/signed comparison warning */
|
||
|
||
if (amt > m_iLoyalty)
|
||
m_iLoyalty = 0;
|
||
else
|
||
m_iLoyalty -= amt;
|
||
|
||
// We should only adjust monthly NP when NP was lost when killing a player.
|
||
if (bIsKillReward)
|
||
{
|
||
if (bIsAddLoyaltyMonthly)
|
||
{
|
||
if (amt > m_iLoyaltyMonthly)
|
||
m_iLoyaltyMonthly = 0;
|
||
else
|
||
m_iLoyaltyMonthly += nChangeAmountLoyaltyMonthly;
|
||
}
|
||
}
|
||
}
|
||
// We're simply adding NP here.
|
||
else
|
||
{
|
||
// If you're using an NP modifying buff then add the bonus
|
||
nChangeAmount = m_bNPGainAmount * nChangeAmount / 100;
|
||
|
||
// Add on any additional NP earned because of a global NP event.
|
||
// NOTE: They officially check to see if the NP is <= 100,000.
|
||
nChangeAmount = nChangeAmount * (100 + g_pMain->m_byNPEventAmount) / 100;
|
||
|
||
// We should only apply NP bonuses when NP was gained as a reward for killing a player.
|
||
if (bIsKillReward)
|
||
{
|
||
// Add on any additional NP gained from items/skills.
|
||
nChangeAmount += m_bItemNPBonus + m_bSkillNPBonus + (m_FlashWarBonus / 10);
|
||
nChangeAmountLoyaltyMonthly = nChangeAmount;
|
||
|
||
// Add monument bonus.
|
||
if (GetZoneID() == ZONE_RONARK_LAND && GetPVPMonumentNation() == GetNation())
|
||
{
|
||
nChangeAmount += PVP_MONUMENT_NP_BONUS;
|
||
nChangeAmountLoyaltyMonthly += PVP_MONUMENT_NP_BONUS;
|
||
}
|
||
|
||
else if ((GetZoneID() == ZONE_BORDER_DEFENSE_WAR || GetZoneID() == ZONE_ARDREAM || GetZoneID() == ZONE_PVP_EVENT || GetZoneID() == ZONE_RONARK_LAND_BASE)
|
||
&& GetEventMonumentNation() == GetNation())
|
||
{
|
||
nChangeAmount += EVENT_MONUMENT_NP_BONUS;
|
||
nChangeAmountLoyaltyMonthly += EVENT_MONUMENT_NP_BONUS;
|
||
}
|
||
}
|
||
|
||
if (m_iLoyalty + nChangeAmount > LOYALTY_MAX)
|
||
m_iLoyalty = LOYALTY_MAX;
|
||
else
|
||
m_iLoyalty += nChangeAmount;
|
||
|
||
if (isInPKZone() && !bIsBonusReward)
|
||
{
|
||
m_iLoyaltyDaily += nChangeAmount;
|
||
UpdatePlayerRank();
|
||
}
|
||
|
||
//// We should only apply additional monthly NP when NP was gained as a reward for killing a player.
|
||
if (!bIsBonusReward)
|
||
{
|
||
if (bIsAddLoyaltyMonthly)
|
||
{
|
||
if (m_iLoyaltyMonthly + nChangeAmountLoyaltyMonthly > LOYALTY_MAX)
|
||
m_iLoyaltyMonthly = LOYALTY_MAX;
|
||
else
|
||
m_iLoyaltyMonthly += nChangeAmountLoyaltyMonthly;
|
||
}
|
||
}
|
||
|
||
if (bIsKillReward)
|
||
{
|
||
if (GetPremiumProperty(PremiumBonusLoyalty) > 0)
|
||
{
|
||
m_iLoyalty += GetPremiumProperty(PremiumBonusLoyalty);
|
||
PremiumBonus += GetPremiumProperty(PremiumBonusLoyalty);
|
||
if (bIsAddLoyaltyMonthly)
|
||
{
|
||
m_iLoyaltyMonthly += GetPremiumProperty(PremiumBonusLoyalty);
|
||
m_iLoyaltyPremiumBonus += GetPremiumProperty(PremiumBonusLoyalty);
|
||
}
|
||
}
|
||
}
|
||
|
||
CKnights * pKnights = g_pMain->GetClanPtr(GetClanID());
|
||
|
||
if (pKnights && pKnights->m_byFlag >= ClanTypeAccredited5 && pKnights->GetClanPointMethod() == 0 && !bIsBonusReward)
|
||
{
|
||
if (pKnights->m_sMembers > 0 && pKnights->m_sMembers <= MAX_CLAN_USERS)
|
||
{
|
||
if (pKnights->m_sMembers <= 5)
|
||
nClanLoyaltyAmount = 1;
|
||
else if (pKnights->m_sMembers <= 10)
|
||
nClanLoyaltyAmount = 2;
|
||
else if (pKnights->m_sMembers <= 15)
|
||
nClanLoyaltyAmount = 3;
|
||
else if (pKnights->m_sMembers <= 20)
|
||
nClanLoyaltyAmount = 4;
|
||
else if (pKnights->m_sMembers <= 25)
|
||
nClanLoyaltyAmount = 5;
|
||
else if (pKnights->m_sMembers <= 30)
|
||
nClanLoyaltyAmount = 6;
|
||
else if (pKnights->m_sMembers > 30)
|
||
nClanLoyaltyAmount = 7;
|
||
|
||
if(nClanLoyaltyAmount < m_iLoyalty)
|
||
{
|
||
m_iLoyalty -= nClanLoyaltyAmount;
|
||
|
||
if (g_DBAgent.DonateClanPoints(this, nClanLoyaltyAmount))
|
||
CKnightsManager::AddUserDonatedNPinGame(GetClanID(), this, nClanLoyaltyAmount, true);
|
||
}
|
||
else
|
||
nClanLoyaltyAmount = 0;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
|
||
result << m_iLoyalty << m_iLoyaltyMonthly
|
||
<< uint32(0) // Clan donations(? Donations made by this user? For the clan overall?)
|
||
<< nClanLoyaltyAmount
|
||
<< PremiumBonus
|
||
<< PremiumBonus; // Premium NP(? Additional NP gained?)
|
||
|
||
Send(&result);
|
||
|
||
// Player is give first np, second exp and third meat dumpling etc.
|
||
if (bIsKillReward && nChangeAmount > 0)
|
||
{
|
||
if (PremiumID > 0 && (isInPKZone() || GetMap()->isWarZone()))
|
||
GoldGain(PVP_BONUS_GOLD+PVP_BONUS_GOLD/2);
|
||
else if (PremiumID == 0 && (isInPKZone() || GetMap()->isWarZone()))
|
||
GoldGain(PVP_BONUS_GOLD);
|
||
|
||
// Additionally, we should receive a "Meat dumpling"
|
||
/*if (isInPKZone())
|
||
GiveItem(ITEM_MEAT_DUMPLING);*/
|
||
}
|
||
}
|
||
void CUser::TempleEventJoin()
|
||
{
|
||
if (g_pMain->pTempleEvent.ActiveEvent == TEMPLE_EVENT_JURAD_MOUNTAIN && !isEventUser())
|
||
{
|
||
C3DMap * pMap = g_pMain->GetZoneByID(ZONE_JURAD_MOUNTAIN);
|
||
|
||
if (pMap == nullptr)
|
||
return;
|
||
|
||
WarpListResponse errorReason;
|
||
if (!CanChangeZone(pMap, errorReason))
|
||
{
|
||
Packet hata(WIZ_WARP_LIST, uint8(2));
|
||
|
||
hata << uint8(errorReason);
|
||
|
||
if (errorReason == WarpListMinLevel)
|
||
hata << pMap->GetMinLevelReq();
|
||
|
||
if (g_pMain->pTempleEvent.ActiveEvent != -1 && GetLevel() < 70)
|
||
Send(&hata);
|
||
return;
|
||
}
|
||
|
||
Packet result (WIZ_SELECT_MSG);
|
||
TempleOperations(TEMPLE_EVENT_JOIN);
|
||
result << uint16(0x00) << uint8(0x07) << uint64(0x00) << uint32(0x06) << g_pMain->pTempleEvent.KarusUserCount << uint16(0x00) << g_pMain->pTempleEvent.ElMoradUserCount << uint16(0x00) << g_pMain->m_nTempleEventRemainSeconds << uint16(0x00);
|
||
g_pMain->Send_All(&result,nullptr,0,0,true,0);
|
||
}
|
||
}
|
||
void CUser::SendKnightCash(int32 nCashPoint)
|
||
{
|
||
if (nCashPoint == 0)
|
||
return;
|
||
|
||
g_DBAgent.UpdateAccountKnightCash(GetAccountName(), nCashPoint);
|
||
}
|
||
/**
|
||
* @brief Get a player loyalty reward.
|
||
*
|
||
* @param isMonthly Monthly reward.
|
||
*/
|
||
uint8 CUser::GetRankReward(bool isMonthly)
|
||
{
|
||
enum RankErrorCodes
|
||
{
|
||
NoRank = 0,
|
||
RewardSuccessfull = 1,
|
||
RewardAlreadyTaken = 2
|
||
};
|
||
|
||
int8 nRank = -1;
|
||
int32 nGoldAmount = 0;
|
||
|
||
Guard lock(g_pMain->m_userRankingsLock);
|
||
|
||
string strUserID = GetName();
|
||
STRTOUPPER(strUserID);
|
||
|
||
UserNameRankMap::iterator itr;
|
||
|
||
if (isMonthly)
|
||
{
|
||
itr = g_pMain->m_UserPersonalRankMap.find(strUserID);
|
||
nRank = itr != g_pMain->m_UserPersonalRankMap.end() ? int8(itr->second->nRank) : -1;
|
||
}
|
||
else
|
||
{
|
||
itr = g_pMain->m_UserKnightsRankMap.find(strUserID);
|
||
nRank = itr != g_pMain->m_UserKnightsRankMap.end() ? int8(itr->second->nRank) : -1;
|
||
}
|
||
|
||
nRank = 1;
|
||
|
||
if (nRank > 0 && nRank <= 100)
|
||
{
|
||
if (nRank == 1)
|
||
nGoldAmount = 1000000;
|
||
else if (nRank >= 2 && nRank <= 4)
|
||
nGoldAmount = 700000;
|
||
else if (nRank >= 5 && nRank <= 10)
|
||
nGoldAmount = 500000;
|
||
else if (nRank >= 11 && nRank <= 40)
|
||
nGoldAmount = 300000;
|
||
else if (nRank >= 41 && nRank <= 100)
|
||
nGoldAmount = 200000;
|
||
else
|
||
nGoldAmount = 0;
|
||
|
||
if (nGoldAmount > 0)
|
||
{
|
||
if (GetUserDailyOp(isMonthly ? DAILY_USER_PERSONAL_RANK_REWARD : DAILY_USER_RANK_REWARD) == 0)
|
||
return RewardAlreadyTaken;
|
||
|
||
GoldGain(nGoldAmount);
|
||
return RewardSuccessfull;
|
||
|
||
}
|
||
}
|
||
|
||
return NoRank;
|
||
}
|
||
/**
|
||
* @brief Changes a player's fame.
|
||
*
|
||
* @param bFame The fame.
|
||
*/
|
||
void CUser::ChangeFame(uint8 bFame)
|
||
{
|
||
Packet result(WIZ_AUTHORITY_CHANGE, uint8(COMMAND_AUTHORITY));
|
||
|
||
m_bFame = bFame;
|
||
result << GetSocketID() << GetFame();
|
||
SendToRegion(&result,nullptr,GetEventRoom());
|
||
}
|
||
/**
|
||
* @brief Sends the server index.
|
||
*/
|
||
void CUser::SendServerIndex()
|
||
{
|
||
Packet result(WIZ_SERVER_INDEX);
|
||
result << uint16(1) << uint16(g_pMain->m_nServerNo);
|
||
Send(&result);
|
||
|
||
}
|
||
/**
|
||
* @brief Packet handler for skillbar requests.
|
||
*
|
||
* @param pkt The packet.
|
||
*/
|
||
void CUser::SkillDataProcess(Packet & pkt)
|
||
{
|
||
uint8 opcode = pkt.read<uint8>();
|
||
switch (opcode)
|
||
{
|
||
case SKILL_DATA_SAVE:
|
||
SkillDataSave(pkt);
|
||
break;
|
||
|
||
case SKILL_DATA_LOAD:
|
||
SkillDataLoad();
|
||
break;
|
||
}
|
||
}
|
||
/**
|
||
* @brief Packet handler for saving a skillbar.
|
||
*
|
||
* @param pkt The packet.
|
||
*/
|
||
void CUser::SkillDataSave(Packet & pkt)
|
||
{
|
||
Packet result(WIZ_SKILLDATA, uint8(SKILL_DATA_SAVE));
|
||
uint16 sCount = pkt.read<uint16>();
|
||
if (sCount == 0 || sCount > 64)
|
||
return;
|
||
|
||
result << sCount;
|
||
for (int i = 0; i < sCount; i++)
|
||
result << pkt.read<uint32>();
|
||
|
||
g_pMain->AddDatabaseRequest(result, this);
|
||
}
|
||
|
||
/**
|
||
* @brief Packet handler for loading a skillbar.
|
||
*/
|
||
void CUser::SkillDataLoad()
|
||
{
|
||
Packet result(WIZ_SKILLDATA, uint8(SKILL_DATA_LOAD));
|
||
g_pMain->AddDatabaseRequest(result, this);
|
||
}
|
||
|
||
/**
|
||
* @brief Initiates a database request to save the character's information.
|
||
*/
|
||
|
||
/**
|
||
* @brief Logs a player out.
|
||
*/
|
||
void CUser::LogOut()
|
||
{
|
||
if (m_strUserID.empty())
|
||
return;
|
||
|
||
m_LastOnline = uint32(UNIXTIME);
|
||
KillMyPet();
|
||
Packet result(AG_USER_LOG_OUT);
|
||
result << GetID() << GetName();
|
||
Send_AIServer(&result);
|
||
RemoveRegionChat();
|
||
UserInOut(INOUT_OUT);
|
||
result.Initialize(WIZ_LOGOUT);
|
||
m_deleted = true; // make this session unusable until the logout is complete
|
||
g_pMain->AddDatabaseRequest(result, this);
|
||
}
|
||
|
||
void CUser::SendBoard(uint16 npcID)
|
||
{
|
||
Packet result(WIZ_BATTLE_EVENT);
|
||
result.DByte();
|
||
// 13 0 No content in board.
|
||
uint8 Count = 0, ListCount = 0, i = 1, TotalCount = 0;
|
||
uint16 ClanID = 0;
|
||
uint8 NationArray = GetNation() - 1;
|
||
if(NationArray > 1)
|
||
{
|
||
result << uint8(13) << uint8(0);
|
||
Send(&result);
|
||
return;
|
||
}
|
||
|
||
if(g_pMain->BoardCache[NationArray].size() > 0)
|
||
{
|
||
g_pMain->BoardCache[NationArray].DByte();
|
||
Send(&g_pMain->BoardCache[NationArray]);
|
||
return;
|
||
}
|
||
|
||
CKnights * pKnights = nullptr;
|
||
CKingSystem * pKingSystem = g_pMain->m_KingSystemArray.GetData(GetNation());
|
||
|
||
if(pKingSystem != nullptr)
|
||
{
|
||
if(!pKingSystem->m_strKingName.empty())
|
||
Count += 1;
|
||
}
|
||
|
||
|
||
ListCount = g_pMain->m_KnightsRatingArray[NationArray].GetSize();
|
||
if(ListCount > 9)
|
||
Count += 10;
|
||
else
|
||
Count += ListCount;
|
||
|
||
ListCount = g_pMain->m_playerPersonalRankings[NationArray].size();
|
||
if(ListCount > 9)
|
||
Count += 10;
|
||
else
|
||
Count += ListCount;
|
||
|
||
if(Count == 0)
|
||
{
|
||
result << uint8(13) << uint8(0);
|
||
Send(&result);
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
|
||
result << uint8(13) << uint8(Count); // Opens
|
||
|
||
if(!pKingSystem->m_strKingName.empty())
|
||
{
|
||
ClanID = g_DBAgent.LoadCharKnights(pKingSystem->m_strKingName);
|
||
if(ClanID > 0)
|
||
pKnights = g_pMain->GetClanPtr(ClanID);
|
||
else
|
||
pKnights = nullptr;
|
||
|
||
|
||
result << uint16(KING_RANK) << pKingSystem->m_strKingName << uint16(0) << uint16(ClanID) << uint16(pKnights == nullptr ? 0 : pKnights->m_sMarkVersion) << (pKnights == nullptr ? "" : pKnights->GetName()) << int16(1);
|
||
TotalCount++;
|
||
}
|
||
|
||
i = 1;
|
||
BOOST_FOREACH (auto itr, g_pMain->m_playerPersonalRankings[NationArray])
|
||
{
|
||
if(i == 11)
|
||
break;
|
||
|
||
if(itr.second == nullptr)
|
||
continue;
|
||
|
||
if(itr.first != i)
|
||
continue;
|
||
|
||
if(itr.second->strUserID[NationArray].empty())
|
||
continue;
|
||
|
||
|
||
ClanID = g_DBAgent.LoadCharKnights(itr.second->strUserID[NationArray]);
|
||
if(ClanID > 0)
|
||
pKnights = g_pMain->GetClanPtr(ClanID);
|
||
else
|
||
pKnights = nullptr;
|
||
|
||
result << uint16(LADDER_RANK + i) << itr.second->strUserID[NationArray] << uint16(0) << uint16(ClanID) << uint16(pKnights == nullptr ? 0 : pKnights->m_sMarkVersion) << (pKnights == nullptr ? "" : pKnights->GetName()) << int16(1);
|
||
i++;
|
||
TotalCount++;
|
||
}
|
||
|
||
|
||
i = 1;
|
||
foreach_stlmap(itr, g_pMain->m_KnightsRatingArray[NationArray])
|
||
{
|
||
if(i == 11)
|
||
break;
|
||
|
||
if(itr->second == nullptr)
|
||
continue;
|
||
|
||
if(itr->second->sClanID == 0)
|
||
continue;
|
||
|
||
|
||
pKnights = g_pMain->GetClanPtr(itr->second->sClanID);
|
||
|
||
if(pKnights == nullptr)
|
||
continue;
|
||
|
||
|
||
|
||
result << uint16(CLAN_RANK + i) << pKnights->m_strChief << uint16(0) << uint16(pKnights->GetID()) << uint16(pKnights->m_sMarkVersion) << (pKnights->GetName()) << int16(1);
|
||
i++;
|
||
TotalCount++;
|
||
}
|
||
|
||
if(Count != TotalCount)
|
||
printf("%d is count, %d is totalcount\n",Count,TotalCount);
|
||
|
||
|
||
|
||
Send(&result);
|
||
g_pMain->BoardCache[NationArray] = result;
|
||
}
|
||
/**
|
||
* @brief Sends the player's information on initial login.
|
||
*/
|
||
void CUser::SendMyInfo()
|
||
{
|
||
C3DMap* pMap = GetMap();
|
||
CKnights* pKnights = nullptr;
|
||
|
||
if (!pMap->IsValidPosition(GetX(), GetZ(), 0.0f))
|
||
{
|
||
short x = 0, z = 0;
|
||
GetStartPosition(x, z);
|
||
|
||
m_curx = (float)x;
|
||
m_curz = (float)z;
|
||
}
|
||
|
||
V3_QuestDataRequest();
|
||
V3_RequestStart();
|
||
|
||
GirisNotice();
|
||
RobChaosSkillItems();
|
||
|
||
if(g_pMain->AutoSkills)
|
||
SendSkillQuestFinish();
|
||
|
||
if (isKurianPortu() && isMastered())
|
||
{
|
||
if (!V3_CheckExistEvent(1377, 2))
|
||
V3_QuestEvent(1377, 2);
|
||
if (!V3_CheckExistEvent(1378, 2))
|
||
V3_QuestEvent(1378, 2);
|
||
}
|
||
|
||
Packet result(WIZ_MYINFO);
|
||
|
||
// Load up our user rankings (for our NP symbols).
|
||
g_pMain->GetUserRank(this);
|
||
|
||
// Are we the King? Let's see, shall we?
|
||
CKingSystem * pData = g_pMain->m_KingSystemArray.GetData(GetNation());
|
||
if (pData != nullptr && STRCASECMP(pData->m_strKingName.c_str(), m_strUserID.c_str()) == 0)
|
||
m_bRank = 1; // We're da King, man.
|
||
else if(GetAuthority() == 0)
|
||
m_bRank = 2; // totally not da King.
|
||
else
|
||
m_bRank = 0;
|
||
|
||
|
||
|
||
result.SByte(); // character name has a single byte length
|
||
result << GetSocketID()
|
||
<< GetName()
|
||
<< GetSPosX() << GetSPosZ() << GetSPosY()
|
||
<< GetNation()
|
||
<< m_bRace << m_sClass << m_bFace
|
||
<< m_nHair
|
||
<< m_bRank << m_bTitle;
|
||
#if (__VERSION >= 1950)
|
||
result << uint8(1) << uint8(1);
|
||
#endif
|
||
result << GetLevel()
|
||
<< m_sPoints
|
||
<< m_iMaxExp << m_iExp
|
||
<< GetLoyalty() << GetMonthlyLoyalty()
|
||
<< GetClanID() << GetFame();
|
||
|
||
if (isInClan())
|
||
pKnights = g_pMain->GetClanPtr(GetClanID());
|
||
|
||
if (pKnights == nullptr)
|
||
{
|
||
result << uint64(0) << uint16(-1) << uint32(0);
|
||
}
|
||
else
|
||
{
|
||
CKnights *aKnights = g_pMain->GetClanPtr(pKnights->GetAllianceID());
|
||
|
||
if (isInClan())
|
||
pKnights->OnLogin(this);
|
||
|
||
if (aKnights != nullptr && aKnights->isInAlliance())
|
||
{
|
||
result << pKnights->GetAllianceID()
|
||
<< pKnights->m_byFlag
|
||
<< pKnights->m_strName
|
||
<< pKnights->m_byGrade << pKnights->m_byRanking
|
||
<< uint16(pKnights->m_sMarkVersion)
|
||
<< pKnights->GetCapeID(aKnights)
|
||
<< pKnights->m_bCapeR << pKnights->m_bCapeG << pKnights->m_bCapeB << uint8(0);
|
||
}
|
||
else
|
||
{
|
||
result << pKnights->GetAllianceID()
|
||
<< pKnights->m_byFlag
|
||
<< pKnights->m_strName
|
||
<< pKnights->m_byGrade << pKnights->m_byRanking
|
||
<< uint16(pKnights->m_sMarkVersion)
|
||
<< uint16(pKnights->m_sCape)
|
||
<< pKnights->m_bCapeR << pKnights->m_bCapeG << pKnights->m_bCapeB << uint8(0);
|
||
}
|
||
}
|
||
|
||
result << uint8(2) << uint8(3) << uint8(4) << uint8(5) // unknown
|
||
<< m_iMaxHp << m_sHp
|
||
<< m_iMaxMp << m_sMp
|
||
<< m_sMaxWeight << m_sItemWeight
|
||
<< GetStat(STAT_STR) << uint8(GetStatItemBonus(STAT_STR) + GetRebStatBuff(STAT_STR))
|
||
<< GetStat(STAT_STA) << uint8(GetStatItemBonus(STAT_STA) + GetRebStatBuff(STAT_STA))
|
||
<< GetStat(STAT_DEX) << uint8(GetStatItemBonus(STAT_DEX) + GetRebStatBuff(STAT_DEX))
|
||
<< GetStat(STAT_INT) << uint8(GetStatItemBonus(STAT_INT) + GetRebStatBuff(STAT_INT))
|
||
<< GetStat(STAT_CHA) << uint8(GetStatItemBonus(STAT_CHA) + GetRebStatBuff(STAT_CHA))
|
||
<< m_sTotalHit << m_sTotalAc
|
||
<< uint8(m_sFireR) << uint8(m_sColdR) << uint8(m_sLightningR)
|
||
<< uint8(m_sMagicR) << uint8(m_sDiseaseR) << uint8(m_sPoisonR)
|
||
<< m_iGold
|
||
<< m_bAuthority
|
||
<< m_bKnightsRank << m_bPersonalRank; // national rank, leader rank
|
||
|
||
result.append(m_bstrSkill, 9);
|
||
|
||
for (int i = 0; i < INVENTORY_TOTAL; i++)
|
||
{
|
||
_ITEM_DATA *pItem = GetItem(i);
|
||
#if (__VERSION >= 1950)
|
||
if(i == BAG1)
|
||
pItem = GetItem(FAIRY);
|
||
else if(i== FAIRY)
|
||
pItem = GetItem(BAG1);
|
||
#endif
|
||
if((pItem->nExpirationTime - UNIXTIME < 0 && pItem->nExpirationTime != 0) || pItem->sCount < 1)
|
||
{
|
||
result << uint32(0) << uint16(0) << uint16(0) << uint8(0) << uint16(0) << uint32(0) << uint32(0);
|
||
pItem->nNum = 0;
|
||
}
|
||
else
|
||
{
|
||
result << pItem->nNum << pItem->sDuration << pItem->sCount << pItem->bFlag // item type flag (e.g. rented)
|
||
<< pItem->sRemainingRentalTime; // remaining time
|
||
SetSpecialItemData(pItem,result);
|
||
result << pItem->nExpirationTime; // expiration date in unix time
|
||
}
|
||
}
|
||
|
||
m_bIsChicken = V3_CheckExistEvent(50, 1);
|
||
//result << m_bAccountStatus; // account status (0 = none, 1 = normal prem with expiry in hours, 2 = pc room)
|
||
|
||
|
||
result << m_bAccountStatus << uint8(PremiumList.GetSize());
|
||
|
||
foreach_stlmap_nolock(itr, PremiumList)
|
||
{
|
||
auto pPremiumData = itr->second;
|
||
result << pPremiumData->PremiumType << pPremiumData->PremiumTime;
|
||
}
|
||
|
||
result << PremiumID;
|
||
|
||
|
||
result << m_bIsChicken // chicken/beginner flag
|
||
<< m_iMannerPoint;
|
||
|
||
|
||
|
||
//Esland 3 moradon 5
|
||
//karus elmorad karuseslant humaneslant moradon
|
||
#if (__VERSION >= 1950) // Military Camp // genie
|
||
result << uint8((GetNation() == Nation::KARUS || isGM()) ? g_pMain->KarusBaseMilitaryCampCount : 0);
|
||
result << uint8((GetNation() == Nation::ELMORAD || isGM()) ? g_pMain->ElmoradBaseMilitaryCampCount : 0);
|
||
result << uint8((GetNation() == Nation::KARUS || isGM()) ? g_pMain->KarusEslantMilitaryCampCount : 0);
|
||
result << uint8((GetNation() == Nation::ELMORAD || isGM()) ? g_pMain->ElmoradEslantMilitaryCampCount : 0);
|
||
result << uint8(g_pMain->MoradonMilitaryCampCount);
|
||
|
||
result << uint8(0) << uint16(GetGenieTime());
|
||
#endif
|
||
#if (__VERSION >= 2010) // V2 ise gerekli bilgileri de ekle
|
||
//Rebirth Level
|
||
result << GetRebLevel(); //Get Rebirth Level
|
||
|
||
// Achieve || Rebirth Stats by Terry
|
||
result << GetRebStatBuff(STAT_STR) // RebStat STR
|
||
<< GetRebStatBuff(STAT_STA) // RebStat STA
|
||
<< GetRebStatBuff(STAT_DEX) // RebStat DEX
|
||
<< GetRebStatBuff(STAT_INT) // RebStat INT
|
||
<< GetRebStatBuff(STAT_CHA); // RebStat CHA
|
||
|
||
result << uint64(1000000000) // Exp Seal Point
|
||
<< uint16(GetCoverTitle()) // Achieve Title
|
||
<< uint16(GetSkillTitle())
|
||
<< uint8(0) // ?
|
||
<< isReturnee()
|
||
<< uint32(0) ;// ??
|
||
#endif
|
||
|
||
SendCompressed(&result);
|
||
|
||
g_pMain->AddCharacterName(this);
|
||
|
||
if (g_pMain->isWarOpen())
|
||
g_pMain->LunarGoldShells(this);
|
||
|
||
SetZoneAbilityChange(GetZoneID());
|
||
Send2AI_UserUpdateInfo(true);
|
||
|
||
/*
|
||
type1 is king
|
||
type2 Achieve Contribution x NP
|
||
type3 Reach Level x
|
||
type5 Achieve Knight Contribution Point of x CLAN NPSİ
|
||
type10 Become a member of Knight Trainees
|
||
|
||
*/
|
||
|
||
AchieveType4(1);
|
||
AchieveType4(2);
|
||
AchieveType4(3);
|
||
AchieveType4(5);
|
||
AchieveType4(10);
|
||
}
|
||
|
||
void CUser::SetMerchantSpecialItemData(_MERCH_DATA * pSlot, ByteBuffer & result)
|
||
{
|
||
result.DByte();
|
||
_ITEM_TABLE * pTable = g_pMain->GetItemPtr(pSlot->nNum);
|
||
if (pTable != nullptr)
|
||
{
|
||
if (pTable->isPet()) // Pets 151 kind
|
||
{
|
||
CPet * newPets = g_pMain->GetPetPtr(pSlot->nSerialNum);
|
||
|
||
if(newPets == nullptr)
|
||
{
|
||
|
||
result << uint32(0);
|
||
}
|
||
else
|
||
{
|
||
//printf("nSerialNum : " I64FMTD " PetID: %s\n",pSlot->nSerialNum,newPets->m_strPetID.c_str());
|
||
// nick Level Exp sadisfaction rate uint8(0)
|
||
result << uint32(newPets->SpecialPetID) << newPets->m_strPetID << newPets->m_sClass << newPets->m_bLevel << uint16((newPets->m_iExp* 10000) / g_pMain->GetPetExpByLevel(newPets->m_bLevel)) << uint16(newPets->m_sSatisfaction);
|
||
}
|
||
}
|
||
else if (pTable->isCyhperRing()) // Cypher Ring 160 king
|
||
{
|
||
_CYPHERRING_DATA * pRingData = g_pMain->GetCypherRingPtr(pSlot->nSerialNum);
|
||
if (pRingData != nullptr)
|
||
result << pRingData->ID << pRingData->UserName << uint8(pRingData->sClass) << pRingData->bLevel << uint16((pRingData->iExp * 10000) / g_pMain->GetExpByLevel(pRingData->bLevel)) << uint16(pRingData->bRace);
|
||
else
|
||
result << uint32(0);
|
||
}
|
||
else
|
||
result << uint32(0);
|
||
}
|
||
else
|
||
result << uint32(0);
|
||
}
|
||
void CUser::SetExchangeSpecialItemData(_EXCHANGE_ITEM * pSlot, ByteBuffer & result)
|
||
{
|
||
result.DByte();
|
||
_ITEM_TABLE * pTable = g_pMain->GetItemPtr(pSlot->nItemID);
|
||
if (pTable != nullptr)
|
||
{
|
||
if (pTable->isPet()) // Pets 151 kind
|
||
{
|
||
CPet * newPets = g_pMain->GetPetPtr(pSlot->nSerialNum);
|
||
|
||
if(newPets == nullptr)
|
||
{
|
||
|
||
result << uint32(0);
|
||
}
|
||
else
|
||
{
|
||
//printf("nSerialNum : " I64FMTD " PetID: %s\n",pSlot->nSerialNum,newPets->m_strPetID.c_str());
|
||
// nick Level Exp sadisfaction rate uint8(0)
|
||
result << uint32(newPets->SpecialPetID) << newPets->m_strPetID << newPets->m_sClass << newPets->m_bLevel << uint16((newPets->m_iExp* 10000) / g_pMain->GetPetExpByLevel(newPets->m_bLevel)) << uint16(newPets->m_sSatisfaction);
|
||
}
|
||
}
|
||
else if (pTable->isCyhperRing()) // Cypher Ring 160 king
|
||
{
|
||
_CYPHERRING_DATA * pRingData = g_pMain->GetCypherRingPtr(pSlot->nSerialNum);
|
||
if (pRingData != nullptr)
|
||
result << pRingData->ID << pRingData->UserName << uint8(pRingData->sClass) << pRingData->bLevel << uint16((pRingData->iExp * 10000) / g_pMain->GetExpByLevel(pRingData->bLevel)) << uint16(pRingData->bRace);
|
||
else
|
||
result << uint32(0);
|
||
}
|
||
else
|
||
result << uint32(0);
|
||
}
|
||
else
|
||
result << uint32(0);
|
||
}
|
||
void CUser::SetSpecialItemData(_ITEM_DATA * pSlot, ByteBuffer & result)
|
||
{
|
||
result.DByte();
|
||
|
||
if(pSlot == nullptr)
|
||
{
|
||
result << uint32(0);
|
||
return;
|
||
}
|
||
|
||
_ITEM_TABLE * pTable = g_pMain->GetItemPtr(pSlot->nNum);
|
||
if (pTable != nullptr)
|
||
{
|
||
if (pTable->isPet()) // Pets 151 kind
|
||
{
|
||
CPet * newPets = g_pMain->GetPetPtr(pSlot->nSerialNum);
|
||
|
||
if(newPets == nullptr)
|
||
{
|
||
|
||
result << uint32(0);
|
||
}
|
||
else
|
||
{
|
||
//printf("nSerialNum : " I64FMTD " PetID: %s\n",pSlot->nSerialNum,newPets->m_strPetID.c_str());
|
||
// nick Level Exp sadisfaction rate uint8(0)
|
||
result << uint32(newPets->SpecialPetID) << newPets->m_strPetID << newPets->m_sClass << newPets->m_bLevel << uint16((newPets->m_iExp* 10000) / g_pMain->GetPetExpByLevel(newPets->m_bLevel)) << uint16(newPets->m_sSatisfaction);
|
||
}
|
||
}
|
||
else if (pTable->isCyhperRing()) // Cypher Ring 160 king
|
||
{
|
||
_CYPHERRING_DATA * pRingData = g_pMain->GetCypherRingPtr(pSlot->nSerialNum);
|
||
if (pRingData != nullptr)
|
||
result << pRingData->ID << pRingData->UserName << uint8(pRingData->sClass) << pRingData->bLevel << uint16((pRingData->iExp * 10000) / g_pMain->GetExpByLevel(pRingData->bLevel)) << uint16(pRingData->bRace);
|
||
else
|
||
result << uint32(0);
|
||
}
|
||
else
|
||
result << uint32(0);
|
||
}
|
||
else
|
||
result << uint32(0);
|
||
|
||
}
|
||
|
||
|
||
void CUser::GirisNotice()
|
||
{
|
||
DateTime time;
|
||
std::string GirisNotice;
|
||
std::string ServerDuyuru;
|
||
std::string Welcome;
|
||
GirisNotice = string_format("Server antihile uygulamasi aktif!");
|
||
ServerDuyuru = string_format("Sunucu Zamanı : %02d-%02d-%04d %02d:%02d", time.GetDay(), time.GetMonth(), time.GetYear(), time.GetHour(), time.GetMinute());
|
||
Welcome = string_format("Selam %s, Oyuna hoşgeldin.", GetName().c_str());
|
||
/*Packet SecurityNotice(WIZ_CHAT, uint8(GENERAL_CHAT));
|
||
SecurityNotice << GetNation() << GetSocketID() << uint8(0) << GirisNotice;
|
||
Send(&SecurityNotice);*/
|
||
Packet DuyuruNotice(WIZ_CHAT, uint8(PUBLIC_CHAT));
|
||
DuyuruNotice << GetNation() << GetSocketID() << uint8(0) << ServerDuyuru;
|
||
Send(&DuyuruNotice);
|
||
Packet WelcomeNotice(WIZ_CHAT, uint8(PUBLIC_CHAT));
|
||
WelcomeNotice << GetNation() << GetSocketID() << uint8(0) << Welcome;
|
||
Send(&WelcomeNotice);
|
||
|
||
|
||
}
|
||
/**
|
||
* @brief Calculates & sets a player's maximum HP.
|
||
*
|
||
* @param iFlag If set to 1, additionally resets the HP to max.
|
||
* If set to 2, additionally resets the max HP to 100 (i.e. Snow war).
|
||
*/
|
||
void CUser::SetMaxHp(int iFlag)
|
||
{
|
||
_CLASS_COEFFICIENT* p_TableCoefficient = nullptr;
|
||
p_TableCoefficient = g_pMain->m_CoefficientArray.GetData( m_sClass );
|
||
if( !p_TableCoefficient ) return;
|
||
|
||
int temp_sta = getStatTotal(STAT_STA);
|
||
|
||
if (GetZoneID() == ZONE_SNOW_BATTLE && iFlag == 0)
|
||
if(GetFame() == COMMAND_CAPTAIN || isKing())
|
||
m_iMaxHp = 300;
|
||
else
|
||
m_iMaxHp = 100;
|
||
|
||
else if (GetZoneID() == ZONE_CHAOS_DUNGEON && iFlag == 0)
|
||
m_iMaxHp = 1000;
|
||
else
|
||
{
|
||
m_iMaxHp = (short)(((p_TableCoefficient->HP * GetLevel() * GetLevel() * temp_sta )
|
||
+ 0.1 * (GetLevel() * temp_sta) + (temp_sta / 5)) + m_sMaxHPAmount + m_sItemMaxHp + 20);
|
||
|
||
// A player's max HP should be capped at (currently) 14,000 HP.
|
||
if (m_iMaxHp > MAX_PLAYER_HP && !isGM())
|
||
m_iMaxHp = MAX_PLAYER_HP;
|
||
|
||
if (iFlag == 1)
|
||
m_sHp = m_iMaxHp;
|
||
else if (iFlag == 2)
|
||
m_iMaxHp = 100;
|
||
}
|
||
|
||
if(m_iMaxHp < m_sHp)
|
||
{
|
||
m_sHp = m_iMaxHp;
|
||
HpChange( m_sHp );
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Calculates & sets a player's maximum MP.
|
||
*/
|
||
void CUser::SetMaxMp()
|
||
{
|
||
_CLASS_COEFFICIENT* p_TableCoefficient = nullptr;
|
||
p_TableCoefficient = g_pMain->m_CoefficientArray.GetData( m_sClass );
|
||
if( !p_TableCoefficient ) return;
|
||
|
||
int temp_intel = 0, temp_sta = 0;
|
||
temp_intel = getStatTotal(STAT_INT) + 30;
|
||
// if( temp_intel > 255 ) temp_intel = 255;
|
||
temp_sta = getStatTotal(STAT_STA);
|
||
// if( temp_sta > 255 ) temp_sta = 255;
|
||
|
||
if( p_TableCoefficient->MP != 0)
|
||
{
|
||
m_iMaxMp = (short)((p_TableCoefficient->MP * GetLevel() * GetLevel() * temp_intel)
|
||
+ (0.1f * GetLevel() * 2 * temp_intel) + (temp_intel / 5) + m_sMaxMPAmount + m_sItemMaxMp + 20);
|
||
}
|
||
else if( p_TableCoefficient->SP != 0)
|
||
{
|
||
m_iMaxMp = (short)((p_TableCoefficient->SP * GetLevel() * GetLevel() * temp_sta )
|
||
+ (0.1f * GetLevel() * temp_sta) + (temp_sta / 5) + m_sMaxMPAmount + m_sItemMaxMp);
|
||
}
|
||
|
||
if(m_iMaxMp < m_sMp) {
|
||
m_sMp = m_iMaxMp;
|
||
MSpChange( m_sMp );
|
||
}
|
||
}
|
||
|
||
|
||
void CUser::SetMaxSp()
|
||
{
|
||
m_iMaxSp = 120;
|
||
ySpChange(0);
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Sends the server time.
|
||
*/
|
||
void CUser::SendTime()
|
||
{
|
||
Packet result(WIZ_TIME);
|
||
result << uint16(g_pMain->m_sYear) << uint16(g_pMain->m_sMonth) << uint16(g_pMain->m_sDate)
|
||
<< uint16(g_pMain->m_sHour) << uint16(g_pMain->m_sMin);
|
||
Send(&result);
|
||
}
|
||
|
||
/**
|
||
* @brief Sends the weather status.
|
||
*/
|
||
void CUser::SendWeather()
|
||
{
|
||
Packet result(WIZ_WEATHER);
|
||
result << g_pMain->m_byWeather << g_pMain->m_sWeatherAmount;
|
||
Send(&result);
|
||
}
|
||
|
||
/**
|
||
* @brief Sets various zone flags to control how
|
||
* the client handles other players/NPCs.
|
||
* Also sends the zone's current tax rate.
|
||
*/
|
||
void CUser::SetZoneAbilityChange(uint16 sNewZone)
|
||
{
|
||
C3DMap * pMap = g_pMain->GetZoneByID(sNewZone);
|
||
_KNIGHTS_SIEGE_WARFARE *pSiegeWar = g_pMain->GetSiegeMasterKnightsPtr(1);
|
||
CKingSystem *pKingSystem = g_pMain->m_KingSystemArray.GetData(GetNation());
|
||
if (pMap == nullptr)
|
||
return;
|
||
|
||
pMap->SetTariff(g_pMain->GetTariffByZone(sNewZone));
|
||
if (sNewZone == ZONE_RONARK_LAND ||
|
||
sNewZone == ZONE_RONARK_LAND_BASE ||
|
||
sNewZone == ZONE_ARDREAM ||
|
||
sNewZone == ZONE_ARENA ||
|
||
sNewZone == ZONE_BORDER_DEFENSE_WAR ||
|
||
sNewZone == ZONE_BIFROST ||
|
||
sNewZone == ZONE_JURAD_MOUNTAIN ||
|
||
sNewZone == ZONE_BATTLE ||
|
||
sNewZone == ZONE_BATTLE2 ||
|
||
sNewZone == ZONE_BATTLE3 ||
|
||
sNewZone == ZONE_BATTLE4 ||
|
||
sNewZone == ZONE_BATTLE5 ||
|
||
sNewZone == ZONE_BATTLE6 ||
|
||
sNewZone == ZONE_SNOW_BATTLE ||
|
||
sNewZone == ZONE_BATTLE_BASE ||
|
||
sNewZone == ZONE_DARK_LAND ||
|
||
sNewZone == ZONE_KROWAZ_DOMINION ||
|
||
sNewZone == ZONE_DRAGON_CAVE ||
|
||
sNewZone == ZONE_FELANKOR_ARENA ||
|
||
sNewZone == ZONE_ISILOON_ARENA ||
|
||
sNewZone == ZONE_LOST_TEMPLE)
|
||
pMap->SetTariff(g_pMain->GetTariffByZone(GetNation()));
|
||
else if(sNewZone == ZONE_MORADONM2 || sNewZone == ZONE_HELL_ABYSS || sNewZone == ZONE_DESPERATION_ABYSS)
|
||
pMap->SetTariff(g_pMain->GetTariffByZone(ZONE_MORADON));
|
||
|
||
|
||
Packet result(WIZ_ZONEABILITY, uint8(1));
|
||
|
||
result << pMap->canTradeWithOtherNation()
|
||
<< pMap->GetZoneType()
|
||
<< pMap->canTalkToOtherNation()
|
||
<< uint16(pMap->GetTariff());
|
||
|
||
Send(&result);
|
||
|
||
if (!isGM())
|
||
PlayerRankingProcess(sNewZone,false);
|
||
|
||
g_pMain->KillNpc(GetSocketID());
|
||
|
||
if (sNewZone == ZONE_BIFROST || sNewZone == ZONE_BATTLE4 || sNewZone == ZONE_RONARK_LAND)
|
||
g_pMain->SendEventRemainingTime(false, this, (uint8)sNewZone);
|
||
|
||
// Clear skill cooldowns...
|
||
m_RHitRepeatList.clear();
|
||
|
||
Guard lock(_unitlock);
|
||
m_CoolDownList.clear();
|
||
m_MagicTypeCooldownList.clear();
|
||
}
|
||
|
||
/**
|
||
* @brief Sends the user's premium state.
|
||
*/
|
||
void CUser::SendPremiumInfo()
|
||
{
|
||
Packet result(WIZ_PREMIUM, uint8(1));
|
||
result << uint8(PremiumList.GetSize());
|
||
|
||
foreach_stlmap_nolock(itr,PremiumList)
|
||
{
|
||
result << itr->second->PremiumType << itr->second->PremiumTime;
|
||
}
|
||
|
||
result << PremiumID;
|
||
|
||
Send(&result);
|
||
}
|
||
|
||
/**
|
||
* @brief Requests user info for the specified session IDs.
|
||
*
|
||
* @param pkt The packet.
|
||
*/
|
||
void CUser::RequestUserIn(Packet & pkt)
|
||
{
|
||
Packet result(WIZ_REQ_USERIN);
|
||
uint16 user_count = pkt.read<uint16>(), online_count = 0;
|
||
|
||
if(user_count > 75)
|
||
user_count = 75;
|
||
|
||
result << uint16(0); // placeholder for user count
|
||
|
||
for (int i = 0; i < user_count; i++)
|
||
{
|
||
uint16 nid = pkt.read<uint16>();
|
||
if(nid < MAX_USER)
|
||
{
|
||
CUser *pUser = g_pMain->GetUserPtr(nid);
|
||
|
||
if (pUser == nullptr
|
||
|| !pUser->isInGame()
|
||
|| (pUser->GetEventRoom() != GetEventRoom() && GetEventRoom() > 0))
|
||
continue;
|
||
|
||
result << uint8(0) << pUser->GetSocketID();
|
||
pUser->GetUserInfo(result);
|
||
}
|
||
else
|
||
{
|
||
CBot *pBot = g_pMain->m_arBotArray.GetData(nid);
|
||
|
||
if (pBot == nullptr || !pBot->isInGame())
|
||
continue;
|
||
|
||
result << uint8(0) << pBot->GetID();
|
||
pBot->GetUserInfo(result);
|
||
}
|
||
online_count++;
|
||
|
||
if(online_count == 10)
|
||
{
|
||
result.put(0, online_count); // substitute count in
|
||
Send(&result);
|
||
result.clear();
|
||
result << uint16(0); // placeholder for user count
|
||
online_count = 0;
|
||
}
|
||
|
||
}
|
||
|
||
if(online_count > 0)
|
||
{
|
||
result.put(0, online_count); // substitute count in
|
||
Send(&result);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Request NPC info for the specified NPC IDs.
|
||
*
|
||
* @param pkt The packet.
|
||
*/
|
||
void CUser::RequestNpcIn(Packet & pkt)
|
||
{
|
||
if (g_pMain->m_bPointCheckFlag == false)
|
||
return;
|
||
|
||
Packet result(WIZ_REQ_NPCIN);
|
||
uint16 npc_count = pkt.read<uint16>();
|
||
if (npc_count > 1000)
|
||
npc_count = 1000;
|
||
|
||
result << uint16(0); // NPC count placeholder
|
||
CKnights *pKnights = g_pMain->GetClanPtr(m_bKnights);
|
||
_KNIGHTS_SIEGE_WARFARE *pSiegeWars = g_pMain->GetSiegeMasterKnightsPtr(1);
|
||
for (int i = 0; i < npc_count; i++)
|
||
{
|
||
uint16 nid = pkt.read<uint16>();
|
||
if (nid < 0)
|
||
continue;
|
||
|
||
CNpc *pNpc = g_pMain->GetNpcPtr(nid);
|
||
if (pNpc == nullptr)
|
||
continue;
|
||
|
||
if (pNpc->isDead()
|
||
|| (pNpc->GetEventRoom() != GetEventRoom()
|
||
&& GetEventRoom() > 0))
|
||
continue;
|
||
|
||
result << pNpc->GetID();
|
||
|
||
if (pNpc->m_sSid == 541 && pNpc->GetType() == NPC_DESTROYED_ARTIFACT && pNpc->m_bZone == ZONE_DELOS)
|
||
{
|
||
if (pKnights != nullptr && pSiegeWars != nullptr)
|
||
{
|
||
if ((pSiegeWars->sMasterKnights == pKnights->m_sAlliance && pKnights->m_sAlliance != 0 ) || pSiegeWars->sMasterKnights == pKnights->m_sIndex)
|
||
pNpc->GetNpcInfo(result,3);
|
||
else
|
||
pNpc->GetNpcInfo(result,0);
|
||
}else
|
||
pNpc->GetNpcInfo(result,0);
|
||
}
|
||
else
|
||
pNpc->GetNpcInfo(result);
|
||
|
||
}
|
||
|
||
result.put(0, npc_count);
|
||
SendCompressed(&result);
|
||
}
|
||
|
||
/**
|
||
* @brief Calculates & resets item stats/bonuses.
|
||
*/
|
||
void CUser::SetSlotItemValue()
|
||
{
|
||
_ITEM_TABLE* pTable = nullptr;
|
||
int item_hit = 0, item_ac = 0;
|
||
|
||
m_sItemMaxHp = m_sItemMaxMp = 0;
|
||
m_sItemAc = 0;
|
||
m_sItemWeight = m_sMaxWeightBonus = 0;
|
||
m_sItemHitrate = m_sItemEvasionrate = 100;
|
||
|
||
memset(m_sStatItemBonuses, 0, sizeof(uint16) * STAT_COUNT);
|
||
m_sFireR = m_sColdR = m_sLightningR = m_sMagicR = m_sDiseaseR = m_sPoisonR = 0;
|
||
m_sDaggerR = m_sSwordR = m_sAxeR = m_sMaceR = m_sSpearR = m_sBowR = 0;
|
||
|
||
m_byAPBonusAmount = 0;
|
||
memset(&m_byAPClassBonusAmount, 0, sizeof(m_byAPClassBonusAmount));
|
||
memset(&m_byAcClassBonusAmount, 0, sizeof(m_byAcClassBonusAmount));
|
||
|
||
m_bItemExpGainAmount = m_bItemNPBonus = m_bItemNoahGainAmount = 0;
|
||
|
||
Guard lock(_unitlock);
|
||
m_equippedItemBonuses.clear();
|
||
|
||
map<uint16, uint32> setItems;
|
||
|
||
// Apply stat bonuses from all equipped & cospre items.
|
||
// Total up the weight of all items.
|
||
for (int i = 0; i < INVENTORY_TOTAL; i++)
|
||
{
|
||
_ITEM_DATA * pItem = nullptr;
|
||
pTable = GetItemPrototype(i, pItem);
|
||
if (pTable == nullptr)
|
||
continue;
|
||
|
||
// Bags increase max weight, they do not weigh anything.
|
||
if (i == INVENTORY_COSP + COSP_BAG1
|
||
|| i == INVENTORY_COSP + COSP_BAG2)
|
||
{
|
||
m_sMaxWeightBonus += pTable->m_sDuration;
|
||
}
|
||
// All other items are attributed to the total weight of items in our inventory.
|
||
else
|
||
{
|
||
// Non-stackable items should have a count of Weight collun in table , by Terry
|
||
if (pTable->GetKind() == 255)
|
||
m_sItemWeight += pTable->m_sWeight;
|
||
else
|
||
m_sItemWeight += pTable->m_sWeight * pItem->sCount;
|
||
}
|
||
|
||
// Do not apply stats to unequipped items
|
||
if ((i >= SLOT_MAX && i < INVENTORY_COSP)
|
||
// or disabled weapons.
|
||
|| (isWeaponsDisabled()
|
||
&& (i == RIGHTHAND || i == LEFTHAND)
|
||
&& !pTable->isShield())
|
||
// or items in magic bags.
|
||
|| i >= INVENTORY_MBAG
|
||
|| pItem->isDuplicate())
|
||
continue;
|
||
|
||
if (pTable->GetKind() != ITEM_KIND_COSPRE
|
||
&& i > INVENTORY_INVENT)
|
||
continue;
|
||
|
||
item_ac = pTable->m_sAc;
|
||
if (pItem->sDuration == 0)
|
||
item_ac /= 10;
|
||
|
||
m_sItemMaxHp += pTable->m_MaxHpB;
|
||
m_sItemMaxMp += pTable->m_MaxMpB;
|
||
m_sItemAc += item_ac;
|
||
m_sStatItemBonuses[STAT_STR] += pTable->m_sStrB;
|
||
m_sStatItemBonuses[STAT_STA] += pTable->m_sStaB;
|
||
m_sStatItemBonuses[STAT_DEX] += pTable->m_sDexB;
|
||
m_sStatItemBonuses[STAT_INT] += pTable->m_sIntelB;
|
||
m_sStatItemBonuses[STAT_CHA] += pTable->m_sChaB;
|
||
m_sItemHitrate += pTable->m_sHitrate;
|
||
m_sItemEvasionrate += pTable->m_sEvarate;
|
||
|
||
m_sFireR += pTable->m_bFireR;
|
||
m_sColdR += pTable->m_bColdR;
|
||
m_sLightningR += pTable->m_bLightningR;
|
||
m_sMagicR += pTable->m_bMagicR;
|
||
m_sDiseaseR += pTable->m_bCurseR;
|
||
m_sPoisonR += pTable->m_bPoisonR;
|
||
|
||
m_sDaggerR += pTable->m_sDaggerAc;
|
||
m_sSwordR += pTable->m_sSwordAc;
|
||
m_sAxeR += pTable->m_sAxeAc;
|
||
m_sMaceR += pTable->m_sMaceAc;
|
||
m_sSpearR += pTable->m_sSpearAc;
|
||
m_sBowR += pTable->m_sBowAc;
|
||
|
||
ItemBonusMap bonusMap;
|
||
if (pTable->m_bFireDamage)
|
||
bonusMap.insert(std::make_pair(ITEM_TYPE_FIRE, pTable->m_bFireDamage));
|
||
|
||
if (pTable->m_bIceDamage)
|
||
bonusMap.insert(std::make_pair(ITEM_TYPE_COLD, pTable->m_bIceDamage));
|
||
|
||
if (pTable->m_bLightningDamage)
|
||
bonusMap.insert(std::make_pair(ITEM_TYPE_LIGHTNING, pTable->m_bLightningDamage));
|
||
|
||
if (pTable->m_bPoisonDamage)
|
||
bonusMap.insert(std::make_pair(ITEM_TYPE_POISON, pTable->m_bPoisonDamage));
|
||
|
||
if (pTable->m_bHPDrain)
|
||
bonusMap.insert(std::make_pair(ITEM_TYPE_HP_DRAIN, pTable->m_bHPDrain));
|
||
|
||
if (pTable->m_bMPDamage)
|
||
bonusMap.insert(std::make_pair(ITEM_TYPE_MP_DAMAGE, pTable->m_bMPDamage));
|
||
|
||
if (pTable->m_bMPDrain)
|
||
bonusMap.insert(std::make_pair(ITEM_TYPE_MP_DRAIN, pTable->m_bMPDrain));
|
||
|
||
if (pTable->m_bMirrorDamage)
|
||
bonusMap.insert(std::make_pair(ITEM_TYPE_MIRROR_DAMAGE, pTable->m_bMirrorDamage));
|
||
|
||
// If we have bonuses to apply, store them.
|
||
if (!bonusMap.empty())
|
||
m_equippedItemBonuses[i] = bonusMap;
|
||
|
||
// Apply cospre item stats
|
||
if (pTable->GetKind() == ITEM_KIND_COSPRE)
|
||
{
|
||
// If this item exists in the set table, it has bonuses to be applied.
|
||
_SET_ITEM * pSetItem = g_pMain->m_SetItemArray.GetData(pTable->m_iNum);
|
||
if (pSetItem != nullptr)
|
||
ApplySetItemBonuses(pSetItem);
|
||
}
|
||
|
||
// All set items start with race over 100
|
||
if (pTable->m_bRace < 100)
|
||
continue;
|
||
|
||
// Each set is uniquely identified by item's race
|
||
auto itr = setItems.find(pTable->m_bRace);
|
||
|
||
// If the item doesn't exist in our map yet...
|
||
if (itr == setItems.end())
|
||
{
|
||
// Generate the base set ID and insert it into our map
|
||
setItems.insert(make_pair(pTable->m_bRace, pTable->m_bRace * 10000));
|
||
itr = setItems.find(pTable->m_bRace);
|
||
}
|
||
|
||
// Update the final set ID depending on the equipped set item
|
||
switch (pTable->m_bSlot)
|
||
{
|
||
case ItemSlotHelmet:
|
||
itr->second += 2;
|
||
break;
|
||
case ItemSlotPauldron:
|
||
itr->second += 16;
|
||
break;
|
||
case ItemSlotPads:
|
||
itr->second += 512;
|
||
break;
|
||
case ItemSlotGloves:
|
||
itr->second += 2048;
|
||
break;
|
||
case ItemSlotBoots:
|
||
itr->second += 4096;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Now we can add up all the set bonuses, if any.
|
||
BOOST_FOREACH (auto itr, setItems)
|
||
{
|
||
// Test if this set item exists (if we're not using at least 2 items from the set, this will fail)
|
||
_SET_ITEM * pItem = g_pMain->m_SetItemArray.GetData(itr.second);
|
||
if (pItem == nullptr)
|
||
continue;
|
||
|
||
ApplySetItemBonuses(pItem);
|
||
}
|
||
|
||
|
||
if (m_sAddArmourAc > 0)
|
||
m_sItemAc += m_sAddArmourAc;
|
||
else
|
||
m_sItemAc = m_sItemAc * m_bPctArmourAc / 100;
|
||
|
||
for (int i = 0; i < INVENTORY_TOTAL; i++)
|
||
{
|
||
_ITEM_DATA *pItem = GetItem(i);
|
||
|
||
if(i == BAG1)
|
||
pItem = GetItem(FAIRY);
|
||
else if(i== FAIRY)
|
||
pItem = GetItem(BAG1);
|
||
|
||
if((pItem->nExpirationTime - UNIXTIME < 0 && pItem->nExpirationTime != 0) || pItem->sCount < 1)
|
||
RobItem(i);
|
||
}
|
||
}
|
||
|
||
void CUser::ApplyAchieveSkillBonuses(uint16 pAchieveSkill, uint8 eNum)
|
||
{
|
||
|
||
_ACHIEVE_TITLE * pAchieve = g_pMain->ACHIEVE_TITLE.GetData(pAchieveSkill);
|
||
|
||
if (pAchieve == nullptr)
|
||
return;
|
||
if (eNum == 1)
|
||
{
|
||
m_sFireR += pAchieve->FlameResistence;
|
||
m_sColdR += pAchieve->IceResistence;
|
||
m_sLightningR += pAchieve->ElectrickShockResistence;
|
||
m_sMagicR += pAchieve->MagicResistence;
|
||
m_sDiseaseR += pAchieve->SpellResistence;
|
||
m_sPoisonR += pAchieve->PoisonResistence;
|
||
m_sStatItemBonuses[STAT_STA] += pAchieve->Health;
|
||
m_sStatItemBonuses[STAT_DEX] += pAchieve->Dexterity;
|
||
m_sStatItemBonuses[STAT_INT] += pAchieve->Intelligence;
|
||
m_sStatItemBonuses[STAT_CHA] += pAchieve->MagicPower;
|
||
m_sStatItemBonuses[STAT_STR] += pAchieve->Strength;
|
||
m_sDaggerR += pAchieve->ShortSwordDefense;
|
||
m_sSwordR += pAchieve->SwordDefense;
|
||
m_sAxeR += pAchieve->AxeDefense;
|
||
m_sMaceR += pAchieve->BlowDefense;
|
||
m_sSpearR += pAchieve->SpearDefense;
|
||
m_sBowR += pAchieve->ArrowDefense;
|
||
m_bItemExpGainAmount += pAchieve->ExpBonusPercent;
|
||
m_bItemNoahGainAmount += pAchieve->UnKnow1;
|
||
m_bItemNPBonus += pAchieve->Contribution;
|
||
}
|
||
else if(eNum == 2)
|
||
{
|
||
m_sTotalHit += pAchieve->Attack;
|
||
m_sTotalAc += pAchieve->Defense;
|
||
}
|
||
}
|
||
|
||
void CUser::ApplySetItemBonuses(_SET_ITEM * pItem)
|
||
{
|
||
m_sItemAc += pItem->ACBonus;
|
||
m_sItemMaxHp += pItem->HPBonus;
|
||
m_sItemMaxMp += pItem->MPBonus;
|
||
|
||
m_sStatItemBonuses[STAT_STR] += pItem->StrengthBonus;
|
||
m_sStatItemBonuses[STAT_STA] += pItem->StaminaBonus;
|
||
m_sStatItemBonuses[STAT_DEX] += pItem->DexterityBonus;
|
||
m_sStatItemBonuses[STAT_INT] += pItem->IntelBonus;
|
||
m_sStatItemBonuses[STAT_CHA] += pItem->CharismaBonus;
|
||
|
||
m_sFireR += pItem->FlameResistance;
|
||
m_sColdR += pItem->GlacierResistance;
|
||
m_sLightningR += pItem->LightningResistance;
|
||
m_sMagicR += pItem->MagicResistance;
|
||
m_sDiseaseR += pItem->CurseResistance;
|
||
m_sPoisonR += pItem->PoisonResistance;
|
||
|
||
m_bItemExpGainAmount += pItem->XPBonusPercent;
|
||
m_bItemNoahGainAmount += pItem->CoinBonusPercent;
|
||
m_bItemNPBonus += pItem->NPBonus;
|
||
|
||
m_sMaxWeightBonus += pItem->MaxWeightBonus;
|
||
|
||
// NOTE: The following percentages use values such as 3 to indicate +3% (not the typical 103%).
|
||
// Also note that at this time, there are no negative values used, so we can assume it's always a bonus.
|
||
m_byAPBonusAmount += pItem->APBonusPercent;
|
||
if (pItem->APBonusClassType >= 1 && pItem->APBonusClassType <= 4)
|
||
m_byAPClassBonusAmount[pItem->APBonusClassType - 1] += pItem->APBonusClassPercent;
|
||
|
||
if (pItem->ACBonusClassType >= 1 && pItem->ACBonusClassType <= 4)
|
||
m_byAcClassBonusAmount[pItem->ACBonusClassType - 1] += pItem->ACBonusClassPercent;
|
||
}
|
||
|
||
void CUser::RecvUserExp(Packet & pkt)
|
||
{
|
||
CNpc * pNpc;
|
||
_PARTY_GROUP * pParty;
|
||
uint16 sNpcID;
|
||
int32 iDamage, iTotalDamage, iNpcExp, iNpcLoyalty;
|
||
uint32 nFinalExp, nFinalLoyalty;
|
||
double TempValue = 0;
|
||
|
||
pkt >> sNpcID >> iDamage >> iTotalDamage >> iNpcExp >> iNpcLoyalty;
|
||
|
||
pNpc = g_pMain->GetNpcPtr(sNpcID);
|
||
if (pNpc == nullptr
|
||
|| !isInRangeSlow(pNpc, 50.0f)
|
||
|| (iNpcExp <= 0 && iNpcLoyalty <= 0))
|
||
return;
|
||
|
||
// Calculate base XP earned for the damage dealt.
|
||
if (iNpcExp > 0)
|
||
{
|
||
TempValue = iNpcExp * ((double)iDamage / (double)iTotalDamage);
|
||
nFinalExp = (int) TempValue;
|
||
if (TempValue > nFinalExp)
|
||
nFinalExp++;
|
||
}
|
||
|
||
// Calculate base NP earned for the damage dealt.
|
||
if (iNpcLoyalty > 0)
|
||
{
|
||
TempValue = iNpcLoyalty * ((double)iDamage / (double)iTotalDamage);
|
||
nFinalLoyalty = (int) TempValue;
|
||
if (TempValue > nFinalLoyalty)
|
||
nFinalLoyalty++;
|
||
}
|
||
|
||
// Handle solo XP/NP gain
|
||
if (!isInParty()
|
||
|| (pParty = g_pMain->GetPartyPtr(GetPartyID())) == nullptr)
|
||
{
|
||
if (isDead())
|
||
return;
|
||
|
||
// Calculate the amount to adjust the XP/NP based on level difference.
|
||
float fModifier = pNpc->GetRewardModifier(GetLevel());
|
||
float fModifierMS;
|
||
// Give solo XP
|
||
if (iNpcExp > 0 && (GetZoneID() != ZONE_STONE1 && GetZoneID() != ZONE_STONE2 && GetZoneID() != ZONE_STONE3 && GetZoneID() != ZONE_JURAD_MOUNTAIN))
|
||
{
|
||
TempValue = nFinalExp * fModifier;
|
||
nFinalExp = (int) TempValue;
|
||
if (TempValue > nFinalExp)
|
||
nFinalExp++;
|
||
|
||
ExpChange(nFinalExp);
|
||
ExpEvent(nFinalExp);
|
||
}else if(iNpcExp > 0 && (GetZoneID() == ZONE_STONE1 || GetZoneID() == ZONE_STONE2 || GetZoneID() == ZONE_STONE3 || GetZoneID() == ZONE_JURAD_MOUNTAIN))
|
||
{
|
||
uint64 TempExp = g_pMain->GetExpByLevel(GetLevel());
|
||
uint8 LevelDifference;
|
||
if(GetLevel() < 40)
|
||
LevelDifference = (40 - GetLevel());
|
||
else if(GetLevel() < 50)
|
||
LevelDifference = (50 - GetLevel());
|
||
else if(GetLevel() < 60)
|
||
LevelDifference = (60 - GetLevel());
|
||
else if(GetLevel() < 70)
|
||
LevelDifference = (70 - GetLevel());
|
||
else if(GetLevel() < 80)
|
||
LevelDifference = (80 - GetLevel());
|
||
else if(GetLevel() < 84)
|
||
LevelDifference = (84 - GetLevel());
|
||
else
|
||
LevelDifference = 0;
|
||
|
||
if(GetLevel() < 40)
|
||
fModifierMS = 0.02f;
|
||
else if(GetLevel() < 50)
|
||
fModifierMS = 0.01f;
|
||
else if(GetLevel() < 60)
|
||
fModifierMS = 0.005f;
|
||
else if(GetLevel() < 70)
|
||
fModifierMS = 0.0025f;
|
||
else if(GetLevel() < 80)
|
||
fModifierMS = 0.0020f;
|
||
else if(GetLevel() < 84)
|
||
fModifierMS = 0.0018f;
|
||
else
|
||
fModifierMS = 0.0f;
|
||
|
||
fModifierMS = fModifierMS / 4;
|
||
|
||
TempValue = fModifierMS * LevelDifference * TempExp;
|
||
nFinalExp = (int) TempValue;
|
||
|
||
if(GetZoneID() == ZONE_JURAD_MOUNTAIN)
|
||
nFinalExp = nFinalExp / 2;
|
||
|
||
if (TempValue > nFinalExp)
|
||
nFinalExp++;
|
||
|
||
ExpChange(nFinalExp);
|
||
ExpEvent(nFinalExp);
|
||
}
|
||
|
||
// Give solo NP
|
||
if (iNpcLoyalty > 0)
|
||
{
|
||
bool UseModifier = false;
|
||
|
||
if (UseModifier)
|
||
{
|
||
TempValue = nFinalLoyalty * fModifier;
|
||
nFinalLoyalty = (int) TempValue;
|
||
if (TempValue > nFinalLoyalty)
|
||
nFinalLoyalty++;
|
||
}
|
||
|
||
SendLoyaltyChange(nFinalLoyalty);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
// Handle party XP/NP gain
|
||
std::vector<CUser *> partyUsers;
|
||
uint32 nTotalLevel = 0;
|
||
for (int i = 0; i < MAX_PARTY_USERS; i++)
|
||
{
|
||
CUser * pUser = g_pMain->GetUserPtr(pParty->uid[i]);
|
||
if (pUser == nullptr)
|
||
continue;
|
||
|
||
partyUsers.push_back(pUser);
|
||
nTotalLevel += pUser->GetLevel();
|
||
}
|
||
|
||
const float fPartyModifierXP = 0.3f;
|
||
const float fPartyModifierNP = 0.2f;
|
||
|
||
uint32 nPartyMembers = (uint32) partyUsers.size();
|
||
|
||
// Calculate the amount to adjust the XP/NP based on level difference.
|
||
float fModifier = pNpc->GetPartyRewardModifier(nTotalLevel, nPartyMembers);
|
||
|
||
if (iNpcExp > 0)
|
||
{
|
||
TempValue = nFinalExp * fModifier;
|
||
nFinalExp = (int) TempValue;
|
||
if (TempValue > nFinalExp)
|
||
nFinalExp++;
|
||
}
|
||
|
||
if (iNpcLoyalty > 0)
|
||
{
|
||
TempValue = nFinalLoyalty * fModifier;
|
||
nFinalLoyalty = (int) TempValue;
|
||
if (TempValue > nFinalLoyalty)
|
||
nFinalLoyalty++;
|
||
}
|
||
|
||
// Hand out kill rewards to all users in the party and still in range.
|
||
int PartyUsers = 0;
|
||
BOOST_FOREACH (auto itr, partyUsers)
|
||
{
|
||
|
||
CUser * pUser = (itr);
|
||
|
||
if (pUser == nullptr)
|
||
continue;
|
||
|
||
if (pUser->isDead()
|
||
|| !pUser->isInRange(pNpc, RANGE_50M)
|
||
|| GetZoneID() != pUser->GetZoneID())
|
||
continue;
|
||
|
||
PartyUsers++;
|
||
}
|
||
|
||
if (PartyUsers == 1){
|
||
|
||
if (isDead())
|
||
return;
|
||
|
||
// Calculate the amount to adjust the XP/NP based on level difference.
|
||
float fModifier = pNpc->GetRewardModifier(GetLevel());
|
||
float fModifierMS;
|
||
|
||
if (iNpcExp > 0 && (GetZoneID() != ZONE_STONE1 && GetZoneID() != ZONE_STONE2 && GetZoneID() != ZONE_STONE3 && GetZoneID() != ZONE_JURAD_MOUNTAIN))
|
||
{
|
||
TempValue = nFinalExp * fModifier;
|
||
nFinalExp = (int) TempValue;
|
||
if (TempValue > nFinalExp)
|
||
nFinalExp++;
|
||
|
||
ExpChange(nFinalExp);
|
||
ExpEvent(nFinalExp);
|
||
}else if(iNpcExp > 0 && (GetZoneID() == ZONE_STONE1 || GetZoneID() == ZONE_STONE2 || GetZoneID() == ZONE_STONE3 || GetZoneID() == ZONE_JURAD_MOUNTAIN))
|
||
{
|
||
uint64 TempExp = g_pMain->GetExpByLevel(GetLevel());
|
||
uint8 LevelDifference;
|
||
if(GetLevel() < 40)
|
||
LevelDifference = (40 - GetLevel());
|
||
else if(GetLevel() < 50)
|
||
LevelDifference = (50 - GetLevel());
|
||
else if(GetLevel() < 60)
|
||
LevelDifference = (60 - GetLevel());
|
||
else if(GetLevel() < 70)
|
||
LevelDifference = (70 - GetLevel());
|
||
else if(GetLevel() < 80)
|
||
LevelDifference = (80 - GetLevel());
|
||
else if(GetLevel() < 84)
|
||
LevelDifference = (84 - GetLevel());
|
||
else
|
||
LevelDifference = 0;
|
||
|
||
if(GetLevel() < 40)
|
||
fModifierMS = 0.02f;
|
||
else if(GetLevel() < 50)
|
||
fModifierMS = 0.01f;
|
||
else if(GetLevel() < 60)
|
||
fModifierMS = 0.005f;
|
||
else if(GetLevel() < 70)
|
||
fModifierMS = 0.0025f;
|
||
else if(GetLevel() < 80)
|
||
fModifierMS = 0.0020f;
|
||
else if(GetLevel() < 84)
|
||
fModifierMS = 0.0018f;
|
||
else
|
||
fModifierMS = 0.0f;
|
||
|
||
|
||
fModifierMS = fModifierMS / 4;
|
||
|
||
TempValue = fModifierMS * LevelDifference * TempExp;
|
||
nFinalExp = (int) TempValue;
|
||
|
||
if(GetZoneID() == ZONE_JURAD_MOUNTAIN)
|
||
nFinalExp = nFinalExp / 2;
|
||
|
||
if (TempValue > nFinalExp)
|
||
nFinalExp++;
|
||
|
||
ExpChange(nFinalExp);
|
||
ExpEvent(nFinalExp);
|
||
}
|
||
|
||
|
||
if (iNpcLoyalty > 0)
|
||
{
|
||
TempValue = iNpcLoyalty * ((double)iDamage / (double)iTotalDamage);
|
||
nFinalLoyalty = (int) TempValue;
|
||
if (TempValue > nFinalLoyalty)
|
||
nFinalLoyalty++;
|
||
|
||
SendLoyaltyChange(nFinalLoyalty);
|
||
}
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
BOOST_FOREACH (auto itr, partyUsers)
|
||
{
|
||
|
||
CUser * pUser = (itr);
|
||
if (pUser->isDead()
|
||
|| !pUser->isInRange(pNpc, RANGE_50M)
|
||
|| GetZoneID() != pUser->GetZoneID())
|
||
continue;
|
||
|
||
|
||
|
||
if (iNpcExp > 0 && (GetZoneID() != ZONE_STONE1 && GetZoneID() != ZONE_STONE2 && GetZoneID() != ZONE_STONE3 && GetZoneID() != ZONE_JURAD_MOUNTAIN))
|
||
{
|
||
TempValue = (nFinalExp * (1 + fPartyModifierXP * (nPartyMembers - 1))) * (double)pUser->GetLevel() / (double)nTotalLevel;
|
||
int iExp = (int) TempValue;
|
||
if (TempValue > iExp)
|
||
iExp++;
|
||
|
||
|
||
if (iExp > (pUser->m_iMaxExp / 10))
|
||
iExp = (int) pUser->m_iMaxExp / 10;
|
||
|
||
pUser->ExpChange(iExp);
|
||
pUser->ExpEvent(iExp);
|
||
}else if(iNpcExp > 0 && (pUser->GetZoneID() == ZONE_STONE1 || pUser->GetZoneID() == ZONE_STONE2 || pUser->GetZoneID() == ZONE_STONE3 || pUser->GetZoneID() == ZONE_JURAD_MOUNTAIN))
|
||
{
|
||
uint64 TempExp = g_pMain->GetExpByLevel(GetLevel());
|
||
uint8 LevelDifference;
|
||
float fModifierMS;
|
||
if(GetLevel() < 40)
|
||
LevelDifference = (40 - pUser->GetLevel());
|
||
else if(GetLevel() < 50)
|
||
LevelDifference = (50 - pUser->GetLevel());
|
||
else if(GetLevel() < 60)
|
||
LevelDifference = (60 - pUser->GetLevel());
|
||
else if(GetLevel() < 70)
|
||
LevelDifference = (70 - pUser->GetLevel());
|
||
else if(GetLevel() < 80)
|
||
LevelDifference = (80 - pUser->GetLevel());
|
||
else if(GetLevel() < 84)
|
||
LevelDifference = (84 - pUser->GetLevel());
|
||
else
|
||
LevelDifference = 0;
|
||
|
||
if(pUser->GetLevel() < 40)
|
||
fModifierMS = 0.02f;
|
||
else if(pUser->GetLevel() < 50)
|
||
fModifierMS = 0.01f;
|
||
else if(pUser->GetLevel() < 60)
|
||
fModifierMS = 0.005f;
|
||
else if(pUser->GetLevel() < 70)
|
||
fModifierMS = 0.0025f;
|
||
else if(pUser->GetLevel() < 80)
|
||
fModifierMS = 0.0020f;
|
||
else if(pUser->GetLevel() < 84)
|
||
fModifierMS = 0.0018f;
|
||
else
|
||
fModifierMS = 0.0f;
|
||
|
||
TempValue = fModifierMS * LevelDifference * TempExp;
|
||
nFinalExp = (int) TempValue;
|
||
|
||
if(pUser->GetZoneID() == ZONE_JURAD_MOUNTAIN)
|
||
nFinalExp = nFinalExp / 2;
|
||
|
||
if (TempValue > nFinalExp)
|
||
nFinalExp++;
|
||
|
||
pUser->ExpChange(nFinalExp);
|
||
pUser->ExpEvent(nFinalExp);
|
||
}
|
||
|
||
if (iNpcLoyalty > 0)
|
||
{
|
||
TempValue = (nFinalLoyalty * (1 + fPartyModifierNP * (nPartyMembers - 1))) * (double)pUser->GetLevel() / (double)nTotalLevel;
|
||
int iLoyalty = (int) TempValue;
|
||
if (TempValue > iLoyalty)
|
||
iLoyalty++;
|
||
|
||
pUser->SendLoyaltyChange(iLoyalty);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Changes the player's experience points by iExp.
|
||
*
|
||
* @param iExp The amount of experience points to adjust by.
|
||
*/
|
||
void CUser::ExpChange(int64 iExp, bool bIsBonusReward)
|
||
{
|
||
// Stop players level 5 or under from losing XP on death.
|
||
if ((GetLevel() < 6 && iExp < 0)
|
||
// Stop players in the war zone (TODO: Add other war zones) from losing XP on death.
|
||
|| (GetMap()->isWarZone() && iExp < 0))
|
||
return;
|
||
|
||
// Despite being signed, we don't want m_iExp ever going below 0.
|
||
// If this happens, we need to investigate why -- not sweep it under the rug.
|
||
ASSERT(m_iExp >= 0);
|
||
|
||
if (iExp > 0)
|
||
{
|
||
if (!bIsBonusReward)
|
||
{
|
||
if(GetZoneID() == ZONE_RONARK_LAND)
|
||
iExp = iExp * (100 + g_pMain->m_byPKZoneExpEventAmount ) / 100;
|
||
// Adjust the exp gained based on the percent set by the buff
|
||
iExp = iExp * (m_sExpGainAmount + m_bItemExpGainAmount) / 100;
|
||
|
||
// Add on any additional XP earned because of a global XP event.
|
||
// NOTE: They officially check to see if the XP is <= 100,000.
|
||
iExp = iExp * (100 + g_pMain->m_byExpEventAmount) / 100;
|
||
iExp = iExp * (m_sExpGainAmount+m_FlashExpBonus) /100;
|
||
|
||
if (GetPremiumProperty(PremiumExpPercent) > 0 && !isDead())
|
||
iExp = iExp * (100 + GetPremiumProperty(PremiumExpPercent)) / 100;
|
||
}
|
||
}
|
||
|
||
bool bLevel = true;
|
||
if (iExp < 0
|
||
&& (m_iExp + iExp) < 0)
|
||
bLevel = false;
|
||
else
|
||
m_iExp += iExp;
|
||
|
||
// If we need to delevel...
|
||
if (!bLevel && GetRebLevel() == 0)
|
||
{
|
||
// Drop us back a level.
|
||
m_bLevel--;
|
||
|
||
// Get the excess XP (i.e. below 0), so that we can take it off the max XP of the previous level
|
||
// Remember: we're deleveling, not necessarily starting from scratch at the previous level
|
||
int64 diffXP = m_iExp + iExp;
|
||
|
||
// Now reset our XP to max for the former level.
|
||
m_iExp = g_pMain->GetExpByLevel(GetLevel());
|
||
|
||
// Get new stats etc.
|
||
LevelChange(GetLevel(), false);
|
||
|
||
// Take the remainder of the XP off (and delevel again if necessary).
|
||
ExpChange(diffXP);
|
||
return;
|
||
}
|
||
// If we've exceeded our XP requirement, we've leveled.
|
||
else if (m_iExp >= m_iMaxExp)
|
||
{
|
||
if (GetLevel() < g_pMain->MAXLVLINI
|
||
&& !(g_pMain->MaxLevel62Control && GetLevel() == 62))
|
||
{
|
||
// Reset our XP, level us up.
|
||
m_iExp -= m_iMaxExp;
|
||
LevelChange(++m_bLevel);
|
||
return;
|
||
}
|
||
|
||
// Hit the max level? Can't level any further. Cap the XP.
|
||
m_iExp = m_iMaxExp;
|
||
}
|
||
|
||
// Tell the client our new XP
|
||
Packet result(WIZ_EXP_CHANGE);
|
||
result << uint8(1) << m_iExp; // NOTE: Use proper flag
|
||
Send(&result);
|
||
|
||
// If we've lost XP, save it for possible refund later.
|
||
if (iExp < 0)
|
||
m_iLostExp = -iExp;
|
||
}
|
||
|
||
/**
|
||
* @brief Get premium properties.
|
||
*/
|
||
uint16 CUser::GetPremiumProperty(PremiumPropertyOpCodes type)
|
||
{
|
||
if (PremiumID <= 0
|
||
|| PremiumList.GetSize() < 1)
|
||
return 0;
|
||
|
||
_PREMIUM_ITEM * pPremiumItem = g_pMain->m_PremiumItemArray.GetData(PremiumID);
|
||
if (pPremiumItem == nullptr)
|
||
return 0;
|
||
|
||
switch (type)
|
||
{
|
||
case PremiumExpRestorePercent:
|
||
return pPremiumItem->ExpRestorePercent;
|
||
case PremiumNoahPercent:
|
||
return pPremiumItem->NoahPercent;
|
||
case PremiumDropPercent:
|
||
return pPremiumItem->DropPercent;
|
||
case PremiumBonusLoyalty:
|
||
return pPremiumItem->BonusLoyalty;
|
||
case PremiumRepairDiscountPercent:
|
||
return pPremiumItem->RepairDiscountPercent;
|
||
case PremiumItemSellPercent:
|
||
return pPremiumItem->ItemSellPercent;
|
||
case PremiumExpPercent:
|
||
{
|
||
foreach_stlmap_nolock (itr, g_pMain->m_PremiumItemExpArray)
|
||
{
|
||
_PREMIUM_ITEM_EXP *pPremiumItemExp = g_pMain->m_PremiumItemExpArray.GetData(itr->first);
|
||
|
||
if (pPremiumItemExp == nullptr)
|
||
continue;
|
||
|
||
if (PremiumID == pPremiumItemExp->Type && GetLevel() >= pPremiumItemExp->MinLevel && GetLevel() <= pPremiumItemExp->MaxLevel)
|
||
return pPremiumItemExp->sPercent;
|
||
}
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief Handles stat updates after a level change.
|
||
* It does not change the level.
|
||
*
|
||
* @param level The level we've changed to.
|
||
* @param bLevelUp true to level up, false for deleveling.
|
||
*/
|
||
void CUser::LevelChange(uint8 level, bool bLevelUp)
|
||
{
|
||
if(level == 84)
|
||
ExpChange(-m_iExp,false);
|
||
|
||
if (level < 1 || level > g_pMain->MAXLVLINI)
|
||
return;
|
||
|
||
if(g_pMain->MaxLevel62Control
|
||
&& ((GetLevel() == 62 && level == 83) || (GetLevel() == 83 && level == 62)))
|
||
{
|
||
for (int i = 0; i < SLOT_MAX; i++)
|
||
{
|
||
if (m_sItemArray[i].nNum)
|
||
return;
|
||
}
|
||
}
|
||
|
||
|
||
if (bLevelUp && level > GetLevel() + 1)
|
||
{
|
||
int16 nStatTotal = 300 + (level - 1) * 3;
|
||
uint8 nSkillTotal = (level - 9) * 2;
|
||
|
||
if (level > 60)
|
||
nStatTotal += 2 * (level - 60);
|
||
|
||
m_sPoints += nStatTotal - GetStatTotal();
|
||
m_bstrSkill[SkillPointFree] += nSkillTotal - GetTotalSkillPoints();
|
||
m_bLevel = level;
|
||
}
|
||
else if (bLevelUp)
|
||
{
|
||
// On each level up, we should give 3 stat points for levels 1-60.
|
||
// For each level above that, we give an additional 2 stat points (so 5 stat points per level).
|
||
int levelsAfter60 = (level > 60 ? level - 60 : 0);
|
||
if ((m_sPoints + GetStatTotal()) < int32(297 + (3 * level) + (2 * levelsAfter60)))
|
||
m_sPoints += (levelsAfter60 == 0 ? 3 : 5);
|
||
|
||
if (level >= 10 && GetTotalSkillPoints() < 2 * (level - 9))
|
||
m_bstrSkill[SkillPointFree] += 2;
|
||
}
|
||
|
||
|
||
|
||
m_bLevel = level;
|
||
|
||
|
||
m_iMaxExp = g_pMain->GetExpByLevel(level);
|
||
SetUserAbility();
|
||
|
||
m_sMp = m_iMaxMp;
|
||
HpChange(GetMaxHealth());
|
||
|
||
Send2AI_UserUpdateInfo();
|
||
|
||
Packet result(WIZ_LEVEL_CHANGE);
|
||
result << GetSocketID()
|
||
<< GetLevel() << m_sPoints << m_bstrSkill[SkillPointFree]
|
||
<< m_iMaxExp << m_iExp
|
||
<< m_iMaxHp << m_sHp
|
||
<< m_iMaxMp << m_sMp
|
||
<< m_sMaxWeight << m_sItemWeight;
|
||
|
||
g_pMain->Send_Region(&result, GetMap(), GetRegionX(), GetRegionZ(),nullptr,GetEventRoom());
|
||
|
||
if(isKurian())
|
||
ySpChange(m_iMaxSp);
|
||
|
||
if (isInParty())
|
||
{
|
||
// TODO: Move this to party specific code
|
||
result.Initialize(WIZ_PARTY);
|
||
result << uint8(PARTY_LEVELCHANGE) << GetSocketID() << GetLevel();
|
||
g_pMain->Send_PartyMember(GetPartyID(), &result);
|
||
|
||
if (m_bIsChicken)
|
||
GrantChickenManner();
|
||
}
|
||
|
||
if(bLevelUp)
|
||
{
|
||
if(m_FlashExpBonus > 0)
|
||
m_FlashExpBonus = 0;
|
||
SendNotice();
|
||
}
|
||
|
||
// Auto-Novice
|
||
if (g_pMain->AutoNovice && (GetLevel() >= 10 && isBeginner() && !isNovice()))
|
||
{
|
||
PromoteUserNovice();
|
||
}
|
||
// Auto-Master
|
||
if (g_pMain->AutoMaster && (level >= 60 && isNovice() && !isMastered()))
|
||
{
|
||
PromoteUser();
|
||
//SendPartyClassUpdate();
|
||
}
|
||
|
||
AchieveType4(3);
|
||
// We should kick players out of the zone if their level no longer matches the requirements for this zone.
|
||
/*if (GetLevel() < GetMap()->GetMinLevelReq() || GetLevel() > GetMap()->GetMaxLevelReq())
|
||
KickOutZoneUser(); */
|
||
}
|
||
|
||
/**
|
||
* @brief Handles player stat assignment.
|
||
*
|
||
* @param pkt The packet.
|
||
*/
|
||
void CUser::PointChange(Packet & pkt)
|
||
{
|
||
uint8 type = pkt.read<uint8>();
|
||
StatType statType = (StatType)(type - 1);
|
||
|
||
if (statType < STAT_STR || statType >= STAT_COUNT
|
||
|| m_sPoints < 1
|
||
|| GetStat(statType) >= STAT_MAX)
|
||
return;
|
||
|
||
Packet result(WIZ_POINT_CHANGE, type);
|
||
|
||
m_sPoints--; // remove a free point
|
||
result << uint16(++m_bStats[statType]); // assign the free point to a stat
|
||
SetUserAbility();
|
||
result << m_iMaxHp << m_iMaxMp << m_sTotalHit << m_sMaxWeight;
|
||
Send(&result);
|
||
SendItemMove(1);
|
||
}
|
||
|
||
/**
|
||
* @brief Changes a user's HP.
|
||
*
|
||
* @param amount The amount to change by.
|
||
* @param pAttacker The attacker.
|
||
* @param bSendToAI true to update the AI server.
|
||
*/
|
||
void CUser::HpChange(int amount, Unit *pAttacker /*= nullptr*/, bool bSendToAI /*= true*/)
|
||
{
|
||
if(pAttacker != nullptr)// Maradonda adam kesme için geçiçi çözüm
|
||
{
|
||
if(pAttacker->isPlayer())
|
||
{
|
||
if(TO_USER(pAttacker)->GetZoneID() == ZONE_MORADON && !TO_USER(pAttacker)->isInArena() && !TO_USER(pAttacker)->isInPartyArena() && amount < 0)
|
||
return;
|
||
}
|
||
}
|
||
Packet result(WIZ_HP_CHANGE);
|
||
uint16 tid = (pAttacker != nullptr ? pAttacker->GetID() : -1);
|
||
int16 oldHP = m_sHp;
|
||
int originalAmount = amount;
|
||
int mirrorDamage = 0;
|
||
|
||
// No cheats allowed
|
||
if (pAttacker && pAttacker->GetZoneID() != GetZoneID())
|
||
return;
|
||
|
||
// Implement damage/HP cap.
|
||
if (amount < -MAX_DAMAGE)
|
||
amount = -MAX_DAMAGE;
|
||
else if (amount > MAX_DAMAGE)
|
||
amount = MAX_DAMAGE;
|
||
|
||
// If we're taking damage...
|
||
if (amount < 0)
|
||
{
|
||
if (isGM())
|
||
return;
|
||
|
||
RemoveStealth();
|
||
|
||
// Handle the mirroring of damage.
|
||
if (m_bMirrorDamage && isInParty() && GetZoneID() != ZONE_CHAOS_DUNGEON)
|
||
{
|
||
_PARTY_GROUP *pParty = nullptr;
|
||
CUser *pUser = nullptr;
|
||
mirrorDamage = (m_byMirrorAmount * amount) / 100;
|
||
amount -= mirrorDamage;
|
||
pParty = g_pMain->GetPartyPtr(GetPartyID());
|
||
if (pParty != nullptr)
|
||
{
|
||
mirrorDamage = mirrorDamage / (GetPartyMemberAmount(pParty) - 1);
|
||
for (int i = 0; i < MAX_PARTY_USERS; i++)
|
||
{
|
||
pUser = g_pMain->GetUserPtr(pParty->uid[i]);
|
||
if(pUser == nullptr || pUser == this)
|
||
continue;
|
||
|
||
pUser->HpChange(mirrorDamage);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Handle mana absorb skills
|
||
if (m_bManaAbsorb > 0 && GetZoneID() != ZONE_CHAOS_DUNGEON)
|
||
{
|
||
if ((m_bManaAbsorb == 15 && AbsorbCount > 0) || m_bManaAbsorb != 0)
|
||
{
|
||
|
||
if (m_bManaAbsorb == 15)
|
||
AbsorbCount--;
|
||
|
||
int toBeAbsorbed = 0;
|
||
toBeAbsorbed = (originalAmount*m_bManaAbsorb) / 100;
|
||
amount -= toBeAbsorbed;
|
||
|
||
MSpChange(toBeAbsorbed);
|
||
}
|
||
}
|
||
|
||
// Handle mastery passives
|
||
if (isMastered() && GetZoneID() != ZONE_CHAOS_DUNGEON)
|
||
{
|
||
// Matchless: [Passive]Decreases all damages received by 15%
|
||
if (CheckSkillPoint(SkillPointMaster, 10, 83))
|
||
amount = (85 * amount) / 100;
|
||
// Absoluteness: [Passive]Decrease 10 % demage of all attacks
|
||
else if (CheckSkillPoint(SkillPointMaster, 5, 9))
|
||
amount = (90 * amount) / 100;
|
||
}
|
||
}
|
||
// If we're receiving HP and we're undead, all healing must become damage.
|
||
else if (m_bIsUndead)
|
||
{
|
||
amount = -amount;
|
||
originalAmount = amount;
|
||
}
|
||
|
||
|
||
if (amount < 0 && -amount >= m_sHp)
|
||
m_sHp = 0;
|
||
else if (amount >= 0 && m_sHp + amount > m_iMaxHp)
|
||
m_sHp = m_iMaxHp;
|
||
else
|
||
m_sHp += amount;
|
||
|
||
|
||
// Absorbed System by Terry
|
||
if (isDevil() && pAttacker != nullptr && pAttacker->isPlayer() )
|
||
{
|
||
if (amount < 0)
|
||
{
|
||
int32 Receive = int32(amount / 3.1);
|
||
|
||
Packet result(WIZ_SP_CHANGE);
|
||
result << uint8(2) << uint8(1);
|
||
result << Receive;
|
||
Send(&result);
|
||
|
||
AbsorbedAmmount += Receive;
|
||
|
||
if(m_sHp > 0)
|
||
m_sHp -= int16(Receive);
|
||
|
||
if (AbsorbedAmmount <= ABSORBED_TOTAL)
|
||
CMagicProcess::RemoveType4Buff(BUFF_TYPE_DEVIL_TRANSFORM, this);
|
||
}
|
||
}
|
||
|
||
result << m_iMaxHp << m_sHp << tid;
|
||
|
||
if (GetHealth() > 0
|
||
&& isMastered()
|
||
&& !isMage() && GetZoneID() != ZONE_CHAOS_DUNGEON)
|
||
{
|
||
const uint16 hp30Percent = (30 * GetMaxHealth()) / 100;
|
||
if ((oldHP >= hp30Percent && m_sHp < hp30Percent)
|
||
|| (m_sHp > hp30Percent))
|
||
{
|
||
/*SetUserAbility();*/
|
||
|
||
if (m_sHp < hp30Percent)
|
||
ShowEffect(106800); // skill ID for "Boldness", shown when a player takes damage.
|
||
}
|
||
}
|
||
|
||
Send(&result);
|
||
|
||
if (bSendToAI)
|
||
{
|
||
result.Initialize(AG_USER_SET_HP);
|
||
result << GetSocketID() << m_sHp << tid;
|
||
Send_AIServer(&result);
|
||
}
|
||
|
||
if (isInParty() && GetZoneID() != ZONE_CHAOS_DUNGEON)
|
||
SendPartyHPUpdate();
|
||
|
||
// Ensure we send the original damage (prior to passives) amount to the attacker
|
||
// as it appears to behave that way officially.
|
||
if (pAttacker != nullptr
|
||
&& pAttacker->isPlayer())
|
||
TO_USER(pAttacker)->SendTargetHP(0, GetID(), originalAmount);
|
||
|
||
if(m_sHp < 0)
|
||
m_sHp = 0;
|
||
|
||
if (m_sHp == 0)
|
||
OnDeath(pAttacker);
|
||
}
|
||
|
||
/**
|
||
* @brief Changes a user's mana points.
|
||
*
|
||
* @param amount The amount to adjust by.
|
||
*/
|
||
void CUser::MSpChange(int amount)
|
||
{
|
||
Packet result(WIZ_MSP_CHANGE);
|
||
int16 oldMP = m_sMp;
|
||
|
||
if (isGM() && amount < 0)
|
||
return;
|
||
|
||
// TODO: Make this behave unsigned.
|
||
m_sMp += amount;
|
||
if (m_sMp < 0)
|
||
m_sMp = 0;
|
||
else if (m_sMp > m_iMaxMp)
|
||
m_sMp = m_iMaxMp;
|
||
|
||
if (isMasteredMage())
|
||
{
|
||
const uint16 mp30Percent = (30 * GetMaxMana()) / 100;
|
||
if (oldMP >= mp30Percent
|
||
&& GetMana() < mp30Percent)
|
||
ShowEffect(106800); // skill ID for "Boldness", shown when a player loses mana.
|
||
}
|
||
|
||
result << m_iMaxMp << m_sMp;
|
||
Send(&result);
|
||
|
||
if (isInParty())
|
||
SendPartyHPUpdate(); // handles MP too
|
||
}
|
||
|
||
void CUser::ySpChange(int amount)
|
||
{
|
||
Packet result(WIZ_SP_CHANGE);
|
||
|
||
|
||
m_iMaxSp=120;
|
||
if ((m_sSp + amount) > m_iMaxSp)
|
||
m_sSp = m_iMaxSp;
|
||
else if((m_sSp + amount) < 0)
|
||
m_sSp = 0;
|
||
else
|
||
m_sSp += amount;
|
||
|
||
result << uint8(1) << uint8(1) << uint8(m_iMaxSp) << uint8(m_sSp);
|
||
Send(&result);
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Sends a HP update to the user's party.
|
||
*/
|
||
void CUser::SendPartyHPUpdate()
|
||
{
|
||
Packet result(WIZ_PARTY);
|
||
result << uint8(PARTY_HPCHANGE)
|
||
<< GetSocketID()
|
||
<< m_iMaxHp << m_sHp
|
||
<< m_iMaxMp << m_sMp;
|
||
g_pMain->Send_PartyMember(GetPartyID(), &result);
|
||
}
|
||
|
||
/**
|
||
* @brief Shows the specified skill's effect
|
||
* to the surrounding regions.
|
||
*
|
||
* @param nSkillID Skill identifier.
|
||
*/
|
||
void CUser::ShowEffect(uint32 nSkillID)
|
||
{
|
||
Packet result(WIZ_EFFECT);
|
||
result << GetID() << nSkillID;
|
||
SendToRegion(&result,nullptr,GetEventRoom());
|
||
}
|
||
|
||
/**
|
||
* @brief Shows an effect on the NPC currently
|
||
* being interacted with.
|
||
*
|
||
* @param nEffectID Identifier for the effect.
|
||
*/
|
||
void CUser::ShowNpcEffect(uint32 nEffectID, bool bSendToRegion)
|
||
{
|
||
Packet result(WIZ_OBJECT_EVENT, uint8(OBJECT_NPC));
|
||
result << uint8(3) << m_sEventNid << nEffectID;
|
||
if (bSendToRegion)
|
||
SendToRegion(&result,nullptr,GetEventRoom());
|
||
else
|
||
Send(&result);
|
||
}
|
||
|
||
/**
|
||
* @brief Sends a player's base information to the AI server.
|
||
*
|
||
* @param initialInfo true when initially sending a player's information
|
||
* to the server.
|
||
*/
|
||
void CUser::Send2AI_UserUpdateInfo(bool initialInfo /*= false*/)
|
||
{
|
||
Packet result(initialInfo ? AG_USER_INFO : AG_USER_UPDATE);
|
||
GetUserInfoForAI(result);
|
||
Send_AIServer(&result);
|
||
}
|
||
|
||
/**
|
||
* @brief Calculates and resets the player's stats/resistances.
|
||
*
|
||
* @param bSendPacket true to send a subsequent item movement packet
|
||
* which is almost always required in addition to
|
||
* using this method.
|
||
*/
|
||
void CUser::SetUserAbility(bool bSendPacket /*= true*/)
|
||
{
|
||
bool bHaveBow = false;
|
||
_CLASS_COEFFICIENT* p_TableCoefficient = g_pMain->m_CoefficientArray.GetData(GetClass());
|
||
uint16 sItemDamage = 0;
|
||
if (p_TableCoefficient == nullptr)
|
||
return;
|
||
|
||
float hitcoefficient = 0.0f;
|
||
|
||
|
||
if (!isWeaponsDisabled())
|
||
{
|
||
_ITEM_TABLE * pRightHand = GetItemPrototype(RIGHTHAND);
|
||
_ITEM_DATA * pRightData = GetItem(RIGHTHAND);
|
||
if (pRightHand != nullptr)
|
||
{
|
||
switch (pRightHand->m_bKind/10)
|
||
{
|
||
case WEAPON_DAGGER:
|
||
hitcoefficient = p_TableCoefficient->ShortSword;
|
||
break;
|
||
case WEAPON_SWORD:
|
||
hitcoefficient = p_TableCoefficient->Sword;
|
||
break;
|
||
case WEAPON_AXE:
|
||
hitcoefficient = p_TableCoefficient->Axe;
|
||
break;
|
||
case WEAPON_MACE:
|
||
case WEAPON_MACE2:
|
||
hitcoefficient = p_TableCoefficient->Club;
|
||
break;
|
||
case WEAPON_SPEAR:
|
||
hitcoefficient = p_TableCoefficient->Spear;
|
||
break;
|
||
case WEAPON_BOW:
|
||
case WEAPON_LONGBOW:
|
||
case WEAPON_LAUNCHER:
|
||
hitcoefficient = p_TableCoefficient->Bow;
|
||
bHaveBow = true;
|
||
break;
|
||
case WEAPON_JAMADAR:
|
||
hitcoefficient = p_TableCoefficient->ShortSword;
|
||
break;
|
||
case WEAPON_STAFF:
|
||
hitcoefficient = p_TableCoefficient->Staff;
|
||
break;
|
||
|
||
}
|
||
|
||
if (isKurian()
|
||
&& pRightHand->m_bKind%10 == 1
|
||
&& (pRightHand->m_bKind/10 == WEAPON_AXE
|
||
|| pRightHand->m_bKind/10 == WEAPON_MACE))
|
||
hitcoefficient = p_TableCoefficient->Pole;
|
||
|
||
if (pRightData->sDuration == 0)
|
||
sItemDamage += (pRightHand->m_sDamage + m_bAddWeaponDamage)/2;
|
||
else
|
||
sItemDamage += pRightHand->m_sDamage + m_bAddWeaponDamage;
|
||
}
|
||
|
||
_ITEM_TABLE *pLeftHand = GetItemPrototype(LEFTHAND);
|
||
_ITEM_DATA * pLeftData = GetItem(LEFTHAND);
|
||
if (pLeftHand != nullptr)
|
||
{
|
||
if (pLeftHand->isBow())
|
||
{
|
||
hitcoefficient = p_TableCoefficient->Bow;
|
||
bHaveBow = true;
|
||
if (pLeftData->sDuration == 0)
|
||
sItemDamage = (pLeftHand->m_sDamage + m_bAddWeaponDamage)/2;
|
||
else
|
||
sItemDamage = pLeftHand->m_sDamage + m_bAddWeaponDamage;
|
||
}
|
||
else
|
||
{
|
||
if (pLeftData->sDuration == 0)
|
||
sItemDamage += ((pLeftHand->m_sDamage + m_bAddWeaponDamage) / 2) /2;
|
||
else
|
||
sItemDamage += (pLeftHand->m_sDamage + m_bAddWeaponDamage) / 2;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*if (m_sACAmount < 0)
|
||
m_sACAmount = 0;*/
|
||
|
||
m_sTotalHit = 0;
|
||
|
||
if (sItemDamage < 3)
|
||
sItemDamage = 3;
|
||
|
||
// Update stats based on item data
|
||
SetSlotItemValue();
|
||
|
||
|
||
ApplyAchieveSkillBonuses(GetSkillTitle(),1);
|
||
int temp_str = GetStat(STAT_STR), temp_dex = getStatTotal(STAT_DEX);
|
||
// if( temp_str > 255 ) temp_str = 255;
|
||
// if( temp_dex > 255 ) temp_dex = 255;
|
||
|
||
uint32 baseAP = 0, ap_stat = 0, additionalAP = 3;
|
||
if (temp_str > 150)
|
||
baseAP = temp_str - 150;
|
||
|
||
if (temp_str == 160)
|
||
baseAP--;
|
||
|
||
temp_str += GetStatBonusTotal(STAT_STR);
|
||
|
||
|
||
|
||
m_sMaxWeight = ((((GetStatWithItemBonus(STAT_STR) + GetLevel()) * 50) + m_sMaxWeightBonus) * (m_bMaxWeightAmount <= 0 ? 1 : m_bMaxWeightAmount / 100))/2;
|
||
|
||
if (isRogue())
|
||
{
|
||
ap_stat = temp_dex;
|
||
}
|
||
else
|
||
{
|
||
ap_stat = temp_str;
|
||
additionalAP += baseAP;
|
||
}
|
||
if (isWarrior() || isPriest() || isKurian())
|
||
{
|
||
m_sTotalHit = (uint16)((0.010f * sItemDamage * (ap_stat + 40)) + (hitcoefficient * sItemDamage * GetLevel() * ap_stat));
|
||
m_sTotalHit = (m_sTotalHit + additionalAP) * (100 + m_byAPBonusAmount) / 100;
|
||
}
|
||
if(isRogue())
|
||
{
|
||
m_sTotalHit = (uint16)((0.007f * sItemDamage * (ap_stat + 40)) + (hitcoefficient * sItemDamage * GetLevel() * ap_stat));
|
||
m_sTotalHit = (m_sTotalHit + additionalAP) * (100 + m_byAPBonusAmount) / 100;
|
||
}
|
||
else if(isMage())
|
||
{
|
||
m_sTotalHit = (uint16)((0.005f * sItemDamage * (ap_stat + 40)) + (hitcoefficient * sItemDamage * GetLevel()));
|
||
m_sTotalHit = (m_sTotalHit + additionalAP) * (100 + m_byAPBonusAmount) / 100;
|
||
}
|
||
m_sTotalHit = (m_sTotalHit + additionalAP) * (100 + m_byAPBonusAmount) / 100;
|
||
|
||
m_sTotalAc = (short)(p_TableCoefficient->AC * (GetLevel() + m_sItemAc));
|
||
if (m_sACPercent <= 0)
|
||
m_sACPercent = 100;
|
||
m_sTotalAc = m_sTotalAc * m_sACPercent / 100;
|
||
|
||
m_fTotalHitrate = ((1 + p_TableCoefficient->Hitrate * GetLevel() * temp_dex ) * m_sItemHitrate/100 ) * (m_bHitRateAmount/100);
|
||
|
||
m_fTotalEvasionrate = ((1 + p_TableCoefficient->Evasionrate * GetLevel() * temp_dex ) * m_sItemEvasionrate/100) * (m_sAvoidRateAmount/100);
|
||
|
||
|
||
|
||
SetMaxHp();
|
||
SetMaxMp();
|
||
|
||
if (isKurian())
|
||
SetMaxSp();
|
||
|
||
uint8 bDefenseBonus = 0, bResistanceBonus = 0;
|
||
|
||
// Reset resistance bonus
|
||
m_bResistanceBonus = 0;
|
||
|
||
// Apply passive skill bonuses
|
||
// NOTE: This is how it's done officially (we should really clean this up)
|
||
// Passive bonuses do NOT stack.
|
||
if (isWarrior() || isKurian())
|
||
{
|
||
// NOTE: These may need updating (they're based on 1.298 stats)
|
||
if (CheckSkillPoint(PRO_SKILL2, 5, 14))
|
||
bDefenseBonus = 20;
|
||
else if (CheckSkillPoint(PRO_SKILL2, 15, 34))
|
||
bDefenseBonus = 30;
|
||
else if (CheckSkillPoint(PRO_SKILL2, 35, 54))
|
||
bDefenseBonus = 40;
|
||
else if (CheckSkillPoint(PRO_SKILL2, 55, 69))
|
||
bDefenseBonus = 50;
|
||
else if (CheckSkillPoint(PRO_SKILL2, 70, 83))
|
||
{
|
||
// Level 70 skill quest
|
||
if (V3_CheckExistEvent(51, 2))
|
||
bDefenseBonus = 60;
|
||
else
|
||
bDefenseBonus = 50;
|
||
}
|
||
|
||
|
||
// Resist: [Passive]Increase all resistance by 30. If a shield is not equipped, the effect will decrease by half.
|
||
if (CheckSkillPoint(PRO_SKILL2, 10, 19))
|
||
bResistanceBonus = 30;
|
||
// Endure: [Passive]Increase all resistance by 60. If a shield is not equipped, the effect will decrease by half.
|
||
else if (CheckSkillPoint(PRO_SKILL2, 20, 39))
|
||
bResistanceBonus = 60;
|
||
// Immunity: [Passive]Increase all resistance by 90. If a shield is not equipped, the effect will decrease by half.
|
||
else if (CheckSkillPoint(PRO_SKILL2, 40, 83))
|
||
bResistanceBonus = 90;
|
||
|
||
// If a shield's not equipped, bonuses are decreased by half.
|
||
_ITEM_TABLE *pLeftHand = GetItemPrototype(LEFTHAND);
|
||
if (pLeftHand == nullptr || !pLeftHand->isShield())
|
||
{
|
||
bResistanceBonus /= 2;
|
||
bDefenseBonus /= 2;
|
||
}
|
||
|
||
m_bResistanceBonus = bResistanceBonus;
|
||
m_sTotalAc += bDefenseBonus * m_sTotalAc / 100;
|
||
}
|
||
|
||
// Mastered warriors / mastered priests
|
||
if (CheckClass(6, 12))
|
||
{
|
||
// Boldness/Daring: [Passive]Increase your defense by 20% when your HP is down to 30% or lower.
|
||
if (m_sHp < 30 * m_iMaxHp / 100)
|
||
m_sTotalAc += 20 * m_sTotalAc / 100;
|
||
}
|
||
|
||
else if (isRogue())
|
||
{
|
||
// Valor: [Passive]Increase your resistance by 50 when your HP is down to 30% or below.
|
||
if (m_sHp < 30 * m_iMaxHp / 100)
|
||
m_bResistanceBonus += 50;
|
||
}
|
||
|
||
if (m_bAddWeaponDamage > 0)
|
||
++m_sTotalHit;
|
||
|
||
if (m_sAddArmourAc > 0 || m_bPctArmourAc > 100)
|
||
++m_sTotalAc;
|
||
|
||
uint8 bSta = GetStat(STAT_STA);
|
||
if (bSta > 100)
|
||
{
|
||
m_sTotalAc += bSta - 100;
|
||
}
|
||
|
||
uint8 bInt = GetStat(STAT_INT);
|
||
if (bInt > 100)
|
||
m_bResistanceBonus += (bInt - 100) / 2;
|
||
|
||
// TODO: Transformation stats need to be applied here
|
||
ApplyAchieveSkillBonuses(GetSkillTitle(),2);
|
||
|
||
if (bSendPacket)
|
||
SendItemMove(2);
|
||
|
||
// Update the AI server
|
||
Send2AI_UserUpdateInfo();
|
||
}
|
||
|
||
/**
|
||
* @brief Sends the target's HP to the player.
|
||
*
|
||
* @param echo Client-based flag that we must echo back to the client.
|
||
* Set to 0 if not responding to the client.
|
||
* @param tid The target's ID.
|
||
* @param damage The amount of damage taken on this request, 0 if it does not apply.
|
||
*/
|
||
void CUser::SendTargetHP( uint8 echo, int tid, int damage )
|
||
{
|
||
int hp = 0, maxhp = 0;
|
||
|
||
Unit *pTarget = nullptr;
|
||
|
||
if (tid >= NPC_BAND)
|
||
{
|
||
if (g_pMain->m_bPointCheckFlag == false) return;
|
||
CNpc *pNpc = g_pMain->GetNpcPtr(tid);
|
||
if (pNpc == nullptr)
|
||
return;
|
||
|
||
if(pNpc->GetZoneID() != GetZoneID())
|
||
return;
|
||
|
||
hp = pNpc->m_iHP;
|
||
maxhp = pNpc->m_iMaxHP;
|
||
pTarget = pNpc;
|
||
}
|
||
else if ( tid < MAX_USER)
|
||
{
|
||
CUser *pUser = g_pMain->GetUserPtr(tid);
|
||
if (pUser == nullptr
|
||
|| pUser->isDead()
|
||
|| !pUser->isInGame()
|
||
|| pUser->GetZoneID() != GetZoneID())
|
||
return;
|
||
|
||
hp = pUser->m_sHp;
|
||
maxhp = pUser->m_iMaxHp;
|
||
pTarget = pUser;
|
||
}else
|
||
{
|
||
hp = 15;
|
||
maxhp = 15;
|
||
}
|
||
|
||
Packet result(WIZ_TARGET_HP);
|
||
result << uint16(tid) << echo << maxhp << hp << uint16(damage);
|
||
Send(&result);
|
||
}
|
||
|
||
/**
|
||
* @brief Handler for opening a loot box.
|
||
*
|
||
* @param pkt The packet.
|
||
*/
|
||
void CUser::BundleOpenReq(Packet & pkt)
|
||
{
|
||
Packet result(WIZ_BUNDLE_OPEN_REQ);
|
||
uint32 bundle_index = pkt.read<uint32>();
|
||
C3DMap* pMap = GetMap();
|
||
|
||
if (pMap == nullptr
|
||
|| bundle_index < 1
|
||
|| isDead()) // yeah, we know people abuse this. We do not care!
|
||
return;
|
||
|
||
Guard lock(pMap->m_RegionItemArray.m_lock);
|
||
_LOOT_BUNDLE *pBundle = pMap->m_RegionItemArray.GetData(bundle_index);
|
||
if (pBundle == nullptr)
|
||
{
|
||
result << bundle_index << uint8(0);
|
||
Send(&result);
|
||
return;
|
||
}
|
||
|
||
CUser *pBundleUser = g_pMain->GetUserPtr(pBundle->LooterID);
|
||
|
||
if(pBundleUser == nullptr)
|
||
return;
|
||
|
||
if(pBundleUser != this
|
||
&& !pBundleUser->isInParty())
|
||
return;
|
||
|
||
if(!isInRange(pBundle->x, pBundle->z, MAX_LOOT_RANGE)
|
||
|| (pBundleUser != this && !isInSameParty(pBundleUser)))
|
||
return;
|
||
|
||
|
||
result << bundle_index << uint8(pBundle->ItemsCount > 0 ? 1 : 0);
|
||
|
||
if(pBundle->ItemsCount < 1)
|
||
goto failed_return;
|
||
|
||
|
||
// The client expects all n items, so if there's any excess...
|
||
// send placeholder data for them.
|
||
for (uint32 i = 0; i < NPC_HAVE_ITEM_LIST; i++)
|
||
result << pBundle->Items[i].nItemID << pBundle->Items[i].sCount;
|
||
|
||
failed_return:
|
||
Send(&result);
|
||
}
|
||
|
||
/**
|
||
* @brief Handler for looting an item from a loot box.
|
||
*
|
||
* @param pkt The packet.
|
||
*/
|
||
void CUser::ItemGet(Packet & pkt)
|
||
{
|
||
enum LootErrorCodes
|
||
{
|
||
LootError = 0,
|
||
LootSolo = 1,
|
||
LootPartyCoinDistribution = 2,
|
||
LootPartyNotification = 3,
|
||
LootPartyItemGivenAway = 4,
|
||
LootPartyItemGivenToUs = 5,
|
||
LootNoRoom = 6
|
||
};
|
||
|
||
Packet result(WIZ_ITEM_GET);
|
||
uint32 nBundleID, nItemID;
|
||
uint16 SlotIndex;
|
||
_LOOT_BUNDLE * pBundle = nullptr;
|
||
_LOOT_ITEM * pItem = nullptr;
|
||
C3DMap * pMap = GetMap();
|
||
CUser * pReceiver = nullptr;
|
||
|
||
if(pMap == nullptr)
|
||
return;
|
||
|
||
pkt >> nBundleID >> nItemID >> SlotIndex;
|
||
|
||
// Lock the array while we process this request
|
||
// to prevent any race conditions between getting/removing the items...
|
||
Guard lock(pMap->m_RegionItemArray.m_lock);
|
||
|
||
if((pBundle = pMap->m_RegionItemArray.GetData(nBundleID)) == nullptr)
|
||
goto fail_return;
|
||
|
||
CUser *pBundleUser = g_pMain->GetUserPtr(pBundle->LooterID);
|
||
|
||
if(pBundleUser == nullptr)
|
||
return;
|
||
|
||
if(pBundleUser != this && !pBundleUser->isInParty())
|
||
return;
|
||
|
||
if(!isInRange(pBundle->x, pBundle->z, MAX_LOOT_RANGE)
|
||
|| (pBundleUser != this && !isInSameParty(pBundleUser)))
|
||
return;
|
||
|
||
// Are we in any region? skype gitti abi
|
||
if (isTrading()
|
||
|| isMerchanting()
|
||
|| isMining()
|
||
|| isDead()
|
||
|| SlotIndex < 0
|
||
|| SlotIndex > NPC_HAVE_ITEM_LIST
|
||
|| !isInRange(pBundle->x, pBundle->z, MAX_LOOT_RANGE))
|
||
goto fail_return;
|
||
|
||
|
||
if(pBundle->ItemsCount == 0)
|
||
goto fail_return;
|
||
|
||
pItem = &pBundle->Items[SlotIndex];
|
||
|
||
// Attempt to loot the specified item.
|
||
// If looting is possible, we can then give the receiver the item.
|
||
if (pItem == nullptr
|
||
|| pItem->sCount == 0
|
||
|| (pReceiver = GetLootUser(pBundle, pItem)) == nullptr)
|
||
goto fail_return;
|
||
|
||
// If we're dealing with coins, either:
|
||
// - we're not in a party, in which case the coins go to us.
|
||
// - we're in a party, in which case we need to distribute the coins (proportionately, by their level).
|
||
// Error handling should already have occurred in GetLootUser().
|
||
if (nItemID == ITEM_GOLD)
|
||
{
|
||
_PARTY_GROUP * pParty;
|
||
uint32 pGold = 0;
|
||
// Not in a party, so all the coins go to us.
|
||
if (!isInParty()
|
||
|| (pParty = g_pMain->GetPartyPtr(GetPartyID())) == nullptr)
|
||
{
|
||
// NOTE: Coins have been checked already.
|
||
if (GetPremiumProperty(PremiumNoahPercent) > 0)
|
||
pGold = pItem->sCount * (100 + GetPremiumProperty(PremiumNoahPercent)) / 100;
|
||
else
|
||
pGold = pItem->sCount;
|
||
|
||
GoldGain(pGold, false, true);
|
||
|
||
result << uint8(LootSolo) << nBundleID << int8(-1) << nItemID << pItem->sCount << GetCoins();
|
||
pReceiver->Send(&result);
|
||
GoldEvent(pGold);
|
||
}
|
||
// In a party, so distribute the coins relative to their level.
|
||
else
|
||
{
|
||
uint16 sumOfLevels = 0;
|
||
vector<CUser *> partyUsers;
|
||
for (int i = 0; i < MAX_PARTY_USERS; i++)
|
||
{
|
||
CUser * pUser = g_pMain->GetUserPtr(pParty->uid[i]);
|
||
if (pUser == nullptr)
|
||
continue;
|
||
|
||
sumOfLevels += pUser->GetLevel();
|
||
partyUsers.push_back(pUser);
|
||
}
|
||
|
||
if (partyUsers.empty())
|
||
goto fail_return;
|
||
|
||
BOOST_FOREACH (auto itr, partyUsers)
|
||
{
|
||
if ((itr)->isDead() ||
|
||
!(itr)->isInRange(pBundle->x, pBundle->z, RANGE_50M))
|
||
continue;
|
||
|
||
// Calculate the number of coins to give the player
|
||
// Give each party member coins relative to their level.
|
||
int coins = (int)(pItem->sCount * (float)((itr)->GetLevel() / (float)sumOfLevels));
|
||
|
||
if ((itr)->GetPremiumProperty(PremiumNoahPercent) > 0)
|
||
pGold = coins * (100 + (itr)->GetPremiumProperty(PremiumNoahPercent)) / 100;
|
||
else
|
||
pGold = coins;
|
||
|
||
(itr)->GoldGain(pGold, false, true);
|
||
|
||
// Let each player know they received coins.
|
||
result.clear();
|
||
result << uint8(LootPartyCoinDistribution) << nBundleID << uint8(-1) << nItemID << (itr)->GetCoins();
|
||
(itr)->Send(&result);
|
||
|
||
(itr)->GoldEvent(pGold);
|
||
}
|
||
}
|
||
} // end of coin distribution
|
||
// If the item selected is actually an item...
|
||
else
|
||
{
|
||
if (pReceiver->isDead() ||
|
||
!pReceiver->isInRange(pBundle->x, pBundle->z, RANGE_50M))
|
||
(pReceiver = GetLootUser(pBundle, pItem));
|
||
|
||
// Retrieve the position for this item.
|
||
int8 bDstPos = pReceiver->FindSlotForItem(pItem->nItemID, pItem->sCount);
|
||
|
||
// This should NOT happen unless their inventory changed after the check.
|
||
// The item won't be removed until after processing's complete, so it's OK to error out here.
|
||
if (bDstPos < 0)
|
||
goto fail_return;
|
||
|
||
// Ensure there's enough room in this user's inventory.
|
||
if (!pReceiver->CheckWeight(pItem->nItemID, pItem->sCount))
|
||
{
|
||
result << uint8(LootNoRoom);
|
||
pReceiver->Send(&result);
|
||
return; // don't need to remove the item, so stop here.
|
||
}
|
||
|
||
// Add item to receiver's inventory
|
||
_ITEM_TABLE * pTable = g_pMain->GetItemPtr(nItemID); // note: already looked up in GetLootUser() so it definitely exists
|
||
_ITEM_DATA * pDstItem = &pReceiver->m_sItemArray[bDstPos];
|
||
|
||
pDstItem->nNum = pItem->nItemID;
|
||
pDstItem->sCount += pItem->sCount;
|
||
|
||
if (pDstItem->sCount == pItem->sCount)
|
||
{
|
||
pDstItem->nSerialNum = g_pMain->GenerateItemSerial();
|
||
|
||
// NOTE: Obscure special items that act as if their durations are their stack sizes
|
||
// will be broken here, but usual cases are typically only given in the PUS.
|
||
// Will need to revise this logic (rather, shift it out into its own method).
|
||
if(pTable == nullptr)
|
||
goto fail_return;
|
||
else
|
||
pDstItem->sDuration = pTable->m_sDuration;
|
||
}
|
||
|
||
if (pDstItem->sCount > MAX_ITEM_COUNT)
|
||
pDstItem->sCount = MAX_ITEM_COUNT;
|
||
|
||
result << uint8(pReceiver == this ? LootSolo : LootPartyItemGivenToUs)
|
||
<< nBundleID
|
||
<< uint8(bDstPos - SLOT_MAX)
|
||
<< pDstItem->nNum << pDstItem->sCount
|
||
<< pReceiver->GetCoins();
|
||
|
||
pReceiver->Send(&result);
|
||
|
||
pReceiver->SetUserAbility(false);
|
||
pReceiver->SendItemWeight();
|
||
|
||
bool HasObtained = false;
|
||
|
||
if (pTable->m_ItemType >= 4 && pTable->m_ItemType != 5)
|
||
HasObtained = true;
|
||
|
||
if(HasObtained && pTable->Getnum() != ITEM_MONSTER_STONE) //Degerli ürün
|
||
{
|
||
Packet drop(WIZ_LOGOSSHOUT,uint8(0x02));
|
||
drop.SByte();
|
||
drop << uint8(0x04) << pReceiver->GetName() << pTable->m_iNum;
|
||
g_pMain->Send_All(&drop);
|
||
}
|
||
// Now notify the party that we've looted, if applicable.
|
||
if (isInParty())
|
||
{
|
||
result.clear();
|
||
result << uint8(LootPartyNotification) << nBundleID << nItemID << pReceiver->GetName();
|
||
g_pMain->Send_PartyMember(GetPartyID(), &result);
|
||
|
||
// If we're not the receiver, i.e. round-robin gave it to someone else
|
||
// we should let us know that this was done (otherwise we'll be like, "GM!!? WHERE'S MY ITEM?!?")
|
||
if (pReceiver != this)
|
||
{
|
||
result.clear();
|
||
result << uint8(LootPartyItemGivenAway);
|
||
Send(&result);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Everything is OK, we have a target. Now remove the item from the bundle.
|
||
// If there's nothing else in the bundle, remove the bundle from the region.
|
||
GetMap()->RegionItemRemove(pBundle, pItem);
|
||
return;
|
||
|
||
fail_return:
|
||
// Generic error
|
||
result << uint8(LootError);
|
||
Send(&result);
|
||
}
|
||
|
||
/**
|
||
* @brief Gets the user to give the loot to.
|
||
*
|
||
* @param pBundle The loot bundle.
|
||
* @param pItem The item being looted.
|
||
*
|
||
* @return null if it fails, else the loot user.
|
||
*/
|
||
CUser * CUser::GetLootUser(_LOOT_BUNDLE * pBundle, _LOOT_ITEM * pItem)
|
||
{
|
||
CUser * pReceiver = nullptr;
|
||
|
||
if (pBundle == nullptr
|
||
|| pItem == nullptr)
|
||
return nullptr;
|
||
|
||
// If we're dealing with coins, either:
|
||
// - we're in a party, in which case we need to distribute the coins (proportionately, by their level).
|
||
// No checks are necessary here (the coins will be miniscule, so if there's no room we can safely ignore them)
|
||
// - we're not in a party, in which case the coins go to us.
|
||
// In this case, we MUST check to be sure we have room for the coins.
|
||
if (pItem->nItemID == ITEM_GOLD)
|
||
{
|
||
// NOTE: No checks are necessary if we're in a party.
|
||
if (!isInParty())
|
||
{
|
||
// We're not in a party, so we must check to be
|
||
// sure we have enough room for the coins.
|
||
if ((GetCoins() + pItem->sCount) > COIN_MAX)
|
||
return nullptr;
|
||
}
|
||
|
||
// The caller will perform the distribution.
|
||
return this;
|
||
}
|
||
|
||
// If we're dealing with items:
|
||
// - if we're in a party:
|
||
// distribute the item to the next player in the party in round-robin fashion,
|
||
// whilst ensuring that user can actually hold the item.
|
||
// - if not in a party:
|
||
// simply ensure that we can hold the item.
|
||
if (isInParty())
|
||
{
|
||
// This ensures the user can hold the item.
|
||
return GetItemRoutingUser(pItem->nItemID, pItem->sCount, pBundle);
|
||
}
|
||
else
|
||
{
|
||
// NOTE: We check to see if they can hold this item in the caller.
|
||
pReceiver = this;
|
||
}
|
||
|
||
return pReceiver;
|
||
}
|
||
/**
|
||
* @brief Packet handler for various player state changes.
|
||
*
|
||
* @param pkt The packet.
|
||
*/
|
||
void CUser::StateChange(Packet & pkt)
|
||
{
|
||
if (isDead())
|
||
return;
|
||
|
||
uint8 bType = pkt.read<uint8>(), buff;
|
||
uint32 nBuff = pkt.read<uint32>();
|
||
buff = *(uint8 *)&nBuff; // don't ask
|
||
m_iTotalTrainingExp = 0;
|
||
m_lastTrainingTime = 0;
|
||
|
||
|
||
switch (bType)
|
||
{
|
||
case 1:
|
||
if (buff != USER_STANDING && buff != USER_SITDOWN)
|
||
return;
|
||
break;
|
||
|
||
case 3:
|
||
// /unview | /view
|
||
if ((buff == 1 || buff == 5) && !isGM())
|
||
return;
|
||
break;
|
||
|
||
case 4: // emotions
|
||
switch (buff)
|
||
{
|
||
case 1: // Greeting 1-3
|
||
case 2:
|
||
case 3:
|
||
case 11: // Provoke 1-3
|
||
case 12:
|
||
case 13:
|
||
case 14: // additional animations randomly used when hitting spacebar
|
||
case 15:
|
||
break; // don't do anything with them (this can be handled neater, but just for testing purposes), just make sure they're allowed
|
||
|
||
default:
|
||
printf("[SID=%d] StateChange: %s tripped (bType=%d, buff=%d, nBuff=%d) somehow, HOW!?\n",
|
||
GetSocketID(), GetName().c_str(), bType, buff, nBuff);
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case 5:
|
||
if (!isGM())
|
||
return;
|
||
break;
|
||
|
||
case 7: // invisibility flag, we don't want users overriding server behaviour.
|
||
return;
|
||
|
||
default:
|
||
printf("[SID=%d] StateChange: %s tripped (bType=%d, buff=%d, nBuff=%d) somehow, HOW!?\n",
|
||
GetSocketID(), GetName().c_str(), bType, buff, nBuff);
|
||
return;
|
||
}
|
||
|
||
StateChangeServerDirect(bType, nBuff);
|
||
}
|
||
|
||
/**
|
||
* @brief Changes a player's state directly from the server
|
||
* without any checks.
|
||
*
|
||
* @param bType State type.
|
||
* @param nBuff The buff/flag (depending on the state type).
|
||
*/
|
||
void CUser::StateChangeServerDirect(uint8 bType, uint32 nBuff)
|
||
{
|
||
uint8 buff = *(uint8 *)&nBuff; // don't ask
|
||
switch (bType)
|
||
{
|
||
case 1:
|
||
m_bResHpType = buff;
|
||
break;
|
||
|
||
case 2:
|
||
m_bNeedParty = buff;
|
||
break;
|
||
|
||
case 3:
|
||
m_nOldAbnormalType = m_bAbnormalType;
|
||
|
||
// If we're a GM, we need to show ourselves before transforming.
|
||
// Otherwise the visibility state is completely desynced.
|
||
if (isGM())
|
||
StateChangeServerDirect(5, 1);
|
||
|
||
m_bAbnormalType = nBuff;
|
||
break;
|
||
|
||
case 5:
|
||
m_bAbnormalType = nBuff;
|
||
break;
|
||
|
||
case 6:
|
||
nBuff = m_bPartyLeader; // we don't set this here.
|
||
break;
|
||
|
||
case 7:
|
||
UpdateVisibility((InvisibilityType)buff);
|
||
break;
|
||
|
||
case 8: // beginner quest
|
||
break;
|
||
|
||
case 14:
|
||
break;
|
||
}
|
||
|
||
Packet result(WIZ_STATE_CHANGE);
|
||
result << GetSocketID() << bType << nBuff;
|
||
SendToRegion(&result,nullptr,GetEventRoom());
|
||
}
|
||
|
||
/**
|
||
* @brief Takes a target's loyalty points (NP)
|
||
* and rewards some/all to the killer (current user).
|
||
*
|
||
* @param tid The target's ID.
|
||
* @param bonusNP Bonus NP to be awarded to the killer as-is.
|
||
*/
|
||
void CUser::LoyaltyChange(int16 tid, uint16 bonusNP /*= 0*/)
|
||
{
|
||
short loyalty_source = 0, loyalty_target = 0;
|
||
|
||
if (GetMap() == nullptr)
|
||
return;
|
||
|
||
// TODO: Rewrite this out, it shouldn't handle all cases so generally like this
|
||
if (!GetMap()->isNationPVPZone() && !g_pMain->m_byBattleSiegeWarOpen
|
||
|| GetZoneID() == ZONE_DESPERATION_ABYSS
|
||
|| GetZoneID() == ZONE_HELL_ABYSS
|
||
|| GetZoneID() == ZONE_DRAGON_CAVE
|
||
|| GetZoneID() == ZONE_CAITHAROS_ARENA)
|
||
return;
|
||
|
||
CUser* pTUser = g_pMain->GetUserPtr(tid);
|
||
if (pTUser == nullptr)
|
||
return;
|
||
|
||
if (pTUser->GetNation() != GetNation() || g_pMain->m_byBattleSiegeWarOpen)
|
||
{
|
||
if (pTUser->GetLoyalty() == 0)
|
||
{
|
||
loyalty_source = 0;
|
||
loyalty_target = 0;
|
||
}
|
||
// Ardream
|
||
else if (pTUser->GetZoneID() == ZONE_ARDREAM)
|
||
{
|
||
loyalty_source = g_pMain->m_Loyalty_Ardream_Source;
|
||
loyalty_target = g_pMain->m_Loyalty_Ardream_Target;
|
||
}
|
||
// Ronark Land Base
|
||
else if (pTUser->GetZoneID() == ZONE_RONARK_LAND_BASE)
|
||
{
|
||
loyalty_source = g_pMain->m_Loyalty_Ronark_Land_Base_Source;
|
||
loyalty_target = g_pMain->m_Loyalty_Ronark_Land_Base_Target;
|
||
}
|
||
else if (pTUser->GetZoneID() == ZONE_RONARK_LAND)
|
||
{
|
||
loyalty_source = g_pMain->m_Loyalty_Ronark_Land_Source;
|
||
loyalty_target = g_pMain->m_Loyalty_Ronark_Land_Target;
|
||
}
|
||
else if (pTUser->GetZoneID() == ZONE_PVP_EVENT)
|
||
{
|
||
loyalty_source = g_pMain->m_Loyalty_Mini_Pvp_Source;
|
||
loyalty_target = g_pMain->m_Loyalty_Mini_Pvp_Target;
|
||
}
|
||
else if (GetMap()->isWarZone() || GetZoneID() < (ZONE_ELMORAD+1))
|
||
{
|
||
loyalty_source = g_pMain->m_Loyalty_Ronark_Land_Source;
|
||
loyalty_target = g_pMain->m_Loyalty_Ronark_Land_Target;
|
||
}
|
||
// Other zones
|
||
else
|
||
{
|
||
loyalty_source = g_pMain->m_Loyalty_Other_Zone_Source;
|
||
loyalty_target = g_pMain->m_Loyalty_Other_Zone_Target;
|
||
}
|
||
}
|
||
|
||
// Include any bonus NP (e.g. rival NP bonus)
|
||
loyalty_source += bonusNP;
|
||
|
||
SendLoyaltyChange(loyalty_source, true, false, pTUser->GetMonthlyLoyalty() > 0 ? true : false);
|
||
|
||
pTUser->SendLoyaltyChange(loyalty_target, true, false, pTUser->GetMonthlyLoyalty() > 0 ? true : false);
|
||
|
||
AchieveType4(2);
|
||
// TODO: Move this to a better place (death handler, preferrably)
|
||
// If a war's running, and we died/killed in a war zone... (this method should NOT be so tied up in specifics(
|
||
if (g_pMain->m_byBattleOpen && GetMap()->isWarZone())
|
||
{
|
||
// Update the casualty count
|
||
if (pTUser->GetNation() == KARUS)
|
||
g_pMain->m_sKarusDead++;
|
||
else
|
||
g_pMain->m_sElmoradDead++;
|
||
}
|
||
}
|
||
|
||
void CUser::SpeedHackUser()
|
||
{
|
||
if (!isInGame() || isGM())
|
||
return;
|
||
|
||
int16 nMaxSpeed = 45;
|
||
|
||
if (GetFame() == COMMAND_CAPTAIN || isRogue())
|
||
nMaxSpeed = 92;
|
||
else if (isWarrior() || isMage() || isPriest() || isKurian())
|
||
nMaxSpeed = 69;
|
||
|
||
if (m_sSpeed > nMaxSpeed /*|| m_sSpeed < -nMaxSpeed*/)
|
||
{
|
||
DateTime time;
|
||
Disconnect();
|
||
g_pMain->SendFormattedNotice("%s is currently disconnect for speed hack.",Nation::ALL,GetName().c_str());
|
||
g_pMain->WriteCheatLogFile(string_format("[ SpeedHack - %d:%d:%d ] %s is Disconnected.\n", time.GetHour(),time.GetMinute(),time.GetSecond(),GetName().c_str()));
|
||
}
|
||
}
|
||
|
||
void CUser::UserLookChange(int pos, int itemid, int durability)
|
||
{
|
||
if (pos >= SLOT_MAX) // let's leave it at this for the moment, the updated check needs considerable reworking
|
||
return;
|
||
|
||
Packet result(WIZ_USERLOOK_CHANGE);
|
||
result << GetSocketID() << uint8(pos) << itemid << uint16(durability);
|
||
|
||
SendToRegion(&result, this, GetEventRoom());
|
||
}
|
||
|
||
void CUser::SendNotice()
|
||
{
|
||
Packet result(WIZ_NOTICE);
|
||
uint8 count = 0;
|
||
|
||
result << uint8(2); // new-style notices (top-right of screen)
|
||
result << count; // placeholder the count
|
||
|
||
// Use first line for header, 2nd line for data, 3rd line for header... etc.
|
||
// It's most likely what they do officially (as usual, | is their line separator)
|
||
for (int i = 0; i < 10; i += 2)
|
||
AppendNoticeEntry(result, count, g_pMain->m_ppNotice[i + 1], g_pMain->m_ppNotice[i]);
|
||
|
||
AppendExtraNoticeData(result, count);
|
||
result.put(1, count); // replace the placeholdered line count
|
||
|
||
Send(&result);
|
||
}
|
||
void CUser::TopSendNotice()
|
||
{
|
||
Packet result(WIZ_NOTICE);
|
||
uint8 count = 0;//uint8
|
||
|
||
result << uint8(1); // Old-style notices (top-right of screen)
|
||
result << count; // placeholder the
|
||
result.SByte();
|
||
// Use first line for header, 2nd line for data, 3rd line for header... etc.
|
||
// It's most likely what they do officially (as usual, | is their line separator)
|
||
for (int i = 0; i < 20; i++)
|
||
AppendNoticeEntryOld(result, count, g_pMain->m_peNotice[i]);
|
||
|
||
AppendExtraNoticeData(result, count);
|
||
result.put(1, count); // replace the placeholdered line count
|
||
Send(&result);
|
||
}
|
||
void CUser::AppendNoticeEntryOld(Packet & pkt, uint8 & elementCount, const char * message)
|
||
{
|
||
if (message == nullptr || *message == '\0')
|
||
return;
|
||
|
||
pkt << message;
|
||
elementCount++;
|
||
}
|
||
void CUser::AppendNoticeEntry(Packet & pkt, uint8 & elementCount, const char * message, const char * title)
|
||
{
|
||
if (message == nullptr || *message == '\0' || title == nullptr || *title == '\0')
|
||
return;
|
||
|
||
pkt << title << message;
|
||
elementCount++;
|
||
}
|
||
|
||
void CUser::AppendExtraNoticeData(Packet & pkt, uint8 & elementCount)
|
||
{
|
||
string message;
|
||
|
||
if (g_pMain->m_byExpEventAmount > 0)
|
||
{
|
||
g_pMain->GetServerResource(IDS_EXP_REPAY_EVENT, &message, g_pMain->m_byExpEventAmount);
|
||
AppendNoticeEntry(pkt, elementCount, message.c_str(), "EXP Event");
|
||
}
|
||
|
||
if (g_pMain->m_byCoinEventAmount > 0)
|
||
{
|
||
g_pMain->GetServerResource(IDS_MONEY_REPAY_EVENT, &message, g_pMain->m_byCoinEventAmount);
|
||
AppendNoticeEntry(pkt, elementCount, message.c_str(), "Noah Event");
|
||
}
|
||
|
||
if (g_pMain->m_byNPEventAmount > 0)
|
||
{
|
||
g_pMain->GetServerResource(IDS_NP_REPAY_EVENT, &message, g_pMain->m_byNPEventAmount);
|
||
AppendNoticeEntry(pkt, elementCount, message.c_str(), "NP Event");
|
||
}
|
||
|
||
if (m_FlashExpBonus > 0)
|
||
{
|
||
g_pMain->GetServerResource(IDS_EXP_REPAY_FLASH, &message, m_FlashExpBonus);
|
||
AppendNoticeEntry(pkt,elementCount,message.c_str(),"EXP Flash");
|
||
}
|
||
|
||
if ( m_FlashDcBonus > 0)
|
||
{
|
||
g_pMain->GetServerResource(IDS_DC_REPAY_FLASH, &message, m_FlashDcBonus);
|
||
AppendNoticeEntry(pkt,elementCount,message.c_str(),"DC Flash");
|
||
}
|
||
|
||
if ( m_FlashWarBonus > 0)
|
||
{
|
||
g_pMain->GetServerResource(IDS_WAR_REPAY_FLASH, &message, (m_FlashWarBonus/10));
|
||
AppendNoticeEntry(pkt,elementCount,message.c_str(),"WAR Flash");
|
||
}
|
||
|
||
if(g_pMain->KCaktifmi)
|
||
g_pMain->SendHelpDescription(this,"You can transform NP to KC with chat '+nptokc <value>'.");
|
||
|
||
if(g_pMain->KCaktifmi2)
|
||
g_pMain->SendHelpDescription(this,"You can transform Gold to KC with chat '+goldtokc <value>'.");
|
||
|
||
|
||
}
|
||
|
||
void CUser::SkillPointChange(Packet & pkt)
|
||
{
|
||
uint8 type = pkt.read<uint8>();
|
||
Packet result(WIZ_SKILLPT_CHANGE, type);
|
||
// invalid type
|
||
if (type < SkillPointCat1 || type > SkillPointMaster
|
||
// not enough free skill points to allocate
|
||
|| m_bstrSkill[0] < 1
|
||
// restrict skill points per category to your level
|
||
|| m_bstrSkill[type] + 1 > GetLevel()
|
||
// we need our first job change to assign skill points
|
||
|| (GetClass() % 100) <= 4
|
||
// to set points in the mastery category, we need to be mastered.
|
||
|| (type == SkillPointMaster && (!isMastered()
|
||
// force a limit of MAX_LEVEL - 60 (the level you can do the mastery quest)
|
||
// on the master skill category, so the limit's 23 skill points with a level 83 cap.
|
||
|| m_bstrSkill[type] >= (MAX_LEVEL - 60)
|
||
// allow only 1 point in the master category for every level above 60.
|
||
|| m_bstrSkill[type] >= (GetLevel() - 60))))
|
||
|
||
{
|
||
result << m_bstrSkill[type]; // only send the packet on failure
|
||
Send(&result);
|
||
return;
|
||
}
|
||
|
||
--m_bstrSkill[0];
|
||
++m_bstrSkill[type];
|
||
|
||
if(isKurianPortu() && (type == SkillPointCat2 ))
|
||
{
|
||
SetUserAbility();
|
||
}
|
||
}
|
||
|
||
void CUser::UpdateGameWeather(Packet & pkt)
|
||
{
|
||
if (!isGM()) // is this user a GM?
|
||
return;
|
||
|
||
if (pkt.GetOpcode() == WIZ_WEATHER)
|
||
{
|
||
pkt >> g_pMain->m_byWeather >> g_pMain->m_sWeatherAmount;
|
||
}
|
||
else
|
||
{
|
||
uint16 y, m, d;
|
||
pkt >> y >> m >> d >> g_pMain->m_sHour >> g_pMain->m_sMin;
|
||
}
|
||
Send(&pkt); // pass the packet straight on
|
||
}
|
||
|
||
void CUser::GetUserInfoForAI(Packet & result)
|
||
{
|
||
Guard lock(_unitlock);
|
||
|
||
result.SByte();
|
||
result << GetSocketID()
|
||
<< GetName() << GetZoneID() << GetNation() << GetLevel()
|
||
<< m_sHp << m_sMp
|
||
<< m_sTotalHit << m_bAttackAmount
|
||
<< m_sTotalAc << m_sACAmount
|
||
<< m_fTotalHitrate << m_fTotalEvasionrate
|
||
<< m_sItemAc
|
||
<< GetPartyID() << GetAuthority()
|
||
<< m_bInvisibilityType
|
||
<< uint32(m_equippedItemBonuses.size());
|
||
|
||
BOOST_FOREACH (auto itr, m_equippedItemBonuses)
|
||
{
|
||
result << itr.first << uint32(itr.second.size()); // slot ID & number of bonuses
|
||
BOOST_FOREACH (auto bonusItr, itr.second)
|
||
result << bonusItr.first << bonusItr.second; // bonus type, bonus amount
|
||
}
|
||
}
|
||
|
||
void CUser::CountConcurrentUser()
|
||
{
|
||
if (!isGM())
|
||
return;
|
||
|
||
uint16 count = 0;
|
||
SessionMap sessMap = g_pMain->m_socketMgr.GetActiveSessionMap();
|
||
BOOST_FOREACH (auto itr, sessMap)
|
||
{
|
||
if (TO_USER(itr.second)->isInGame())
|
||
count++;
|
||
}
|
||
|
||
Guard lock(g_pMain->m_BotcharacterNameLock);
|
||
count += g_pMain->m_BotcharacterNameMap.size();
|
||
|
||
Packet result(WIZ_CONCURRENTUSER);
|
||
result << count;
|
||
Send(&result);
|
||
}
|
||
|
||
/**
|
||
* @brief Takes a target's loyalty points (NP)
|
||
* and rewards some/all to the killer's party (current user).
|
||
*
|
||
* @param tid The target's ID.
|
||
* @param bonusNP Bonus NP to be awarded to the killer's party as-is.
|
||
*/
|
||
void CUser::LoyaltyDivide(int16 tid, uint16 bonusNP /*= 0*/)
|
||
{
|
||
if (m_bZone == ZONE_SNOW_BATTLE
|
||
|| m_bZone == ZONE_DESPERATION_ABYSS
|
||
|| m_bZone == ZONE_HELL_ABYSS
|
||
|| m_bZone == ZONE_DRAGON_CAVE
|
||
|| m_bZone == ZONE_CAITHAROS_ARENA)
|
||
return;
|
||
int16 loyalty_source = 0, loyalty_target = 0;
|
||
uint8 total_member = 0;
|
||
|
||
if (!isInParty())
|
||
return;
|
||
|
||
_PARTY_GROUP *pParty = g_pMain->GetPartyPtr(GetPartyID());
|
||
if (pParty == nullptr)
|
||
return;
|
||
|
||
CUser* pTUser = g_pMain->GetUserPtr(tid);
|
||
if (pTUser == nullptr)
|
||
return;
|
||
|
||
for (int i = 0; i < MAX_PARTY_USERS; i++)
|
||
{
|
||
CUser *pUser = g_pMain->GetUserPtr(pParty->uid[i]);
|
||
if (pUser == nullptr)
|
||
continue;
|
||
|
||
total_member++;
|
||
}
|
||
|
||
if (total_member <= 0)
|
||
return;
|
||
|
||
// This is for the Event Battle on Wednesday :(
|
||
if (g_pMain->m_byBattleOpen && GetZoneID() == (ZONE_BATTLE_BASE + g_pMain->m_byBattleZone))
|
||
{
|
||
if (pTUser->GetNation() == KARUS)
|
||
g_pMain->m_sKarusDead++;
|
||
else
|
||
g_pMain->m_sElmoradDead++;
|
||
}
|
||
|
||
if (pTUser->GetNation() != GetNation() || GetZoneID() == ZONE_DELOS) // Different nations!!!
|
||
{
|
||
if (pTUser->GetLoyalty() == 0) // No cheats allowed...
|
||
{
|
||
loyalty_source = 0;
|
||
loyalty_target = 0;
|
||
}
|
||
else
|
||
{
|
||
loyalty_source = GetLoyaltyDivideSource(total_member);
|
||
loyalty_target = GetLoyaltyDivideTarget();
|
||
|
||
if (loyalty_source == 0)
|
||
{
|
||
loyalty_source = 0;
|
||
loyalty_target = 0;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
return;
|
||
|
||
for (int j = 0; j < MAX_PARTY_USERS; j++) // Distribute loyalty amongst party members.
|
||
{
|
||
CUser *pUser = g_pMain->GetUserPtr(pParty->uid[j]);
|
||
if (pUser == nullptr)
|
||
continue;
|
||
|
||
if(pUser->isDead())
|
||
continue;
|
||
|
||
if (pUser->isAlive() && ((pUser->hasRival() && !pUser->hasRivalryExpired() && (pUser->GetRivalID() == pTUser->GetID())
|
||
|| (pUser->GetRivalID() == pTUser->GetID() && pUser->isPriest()))))
|
||
{
|
||
pUser->SendLoyaltyChange(loyalty_source + bonusNP, true, false, pTUser->GetMonthlyLoyalty() > 0 ? true : false);
|
||
pUser->RemoveRival();
|
||
}
|
||
else
|
||
pUser->SendLoyaltyChange(loyalty_source, true, false, pTUser->GetMonthlyLoyalty() > 0 ? true : false);
|
||
}
|
||
|
||
pTUser->SendLoyaltyChange(loyalty_target, true, false, pTUser->GetMonthlyLoyalty() > 0 ? true : false);
|
||
}
|
||
|
||
int16 CUser::GetLoyaltyDivideSource(uint8 totalmember)
|
||
{
|
||
int16 nBaseLoyalty = 0;
|
||
|
||
if (GetZoneID() == ZONE_ARDREAM)
|
||
nBaseLoyalty = g_pMain->m_Loyalty_Ardream_Source;
|
||
else if (GetZoneID() == ZONE_RONARK_LAND_BASE)
|
||
nBaseLoyalty = g_pMain->m_Loyalty_Ronark_Land_Base_Source;
|
||
else if (GetZoneID() == ZONE_RONARK_LAND)
|
||
nBaseLoyalty = g_pMain->m_Loyalty_Ronark_Land_Source;
|
||
else if (GetZoneID() == ZONE_BORDER_DEFENSE_WAR)
|
||
nBaseLoyalty = (g_pMain->m_Loyalty_Other_Zone_Source / 5);
|
||
else
|
||
nBaseLoyalty = g_pMain->m_Loyalty_Other_Zone_Source;
|
||
|
||
int16 nMaxLoyalty = (nBaseLoyalty * 3) - 2;
|
||
int16 nMinLoyalty = nMaxLoyalty / MAX_PARTY_USERS;
|
||
int16 nLoyaltySource = nMinLoyalty;
|
||
|
||
if (nLoyaltySource > 0)
|
||
{
|
||
for (int i = 0; i < (MAX_PARTY_USERS - totalmember); i++)
|
||
nLoyaltySource += 2;
|
||
}
|
||
|
||
return nLoyaltySource -1;
|
||
}
|
||
|
||
int16 CUser::GetLoyaltyDivideTarget()
|
||
{
|
||
if (GetZoneID() == ZONE_ARDREAM)
|
||
return g_pMain->m_Loyalty_Ardream_Target;
|
||
else if (GetZoneID() == ZONE_RONARK_LAND_BASE)
|
||
return g_pMain->m_Loyalty_Ronark_Land_Base_Target;
|
||
else if (GetZoneID() == ZONE_RONARK_LAND)
|
||
return g_pMain->m_Loyalty_Ronark_Land_Target;
|
||
else if (GetZoneID() == ZONE_BORDER_DEFENSE_WAR)
|
||
return (g_pMain->m_Loyalty_Other_Zone_Target / 5);
|
||
else
|
||
return g_pMain->m_Loyalty_Other_Zone_Target;
|
||
|
||
return 0;
|
||
}
|
||
|
||
void CUser::ItemWoreOut(int type, int damage)
|
||
{
|
||
static uint8 ArmourTypes[] = {RIGHTHAND, LEFTHAND, HEAD, BREAST, LEG, GLOVE, FOOT};
|
||
uint8 nTotalSlots = 0;
|
||
|
||
int iWorerate = (int)sqrt(damage / 10.0f);
|
||
if (iWorerate == 0)
|
||
return;
|
||
|
||
// Attack ise
|
||
if (type == ATTACK)
|
||
nTotalSlots = 2;
|
||
// Defans, Tamir v.b. Özel Durumlar
|
||
else if (type == DEFENCE
|
||
|| type == REPAIR_ALL
|
||
|| type == ACID_ALL)
|
||
nTotalSlots = sizeof(ArmourTypes) / sizeof(*ArmourTypes); // All Slots
|
||
|
||
for (uint8 i = 0; i < nTotalSlots; i++)
|
||
{
|
||
uint8 nSlot = ArmourTypes[i];
|
||
_ITEM_DATA *pItem = GetItem(nSlot);
|
||
if (pItem == nullptr
|
||
|| pItem->nNum == 0)
|
||
continue;
|
||
|
||
_ITEM_TABLE *pTable = nullptr;
|
||
|
||
// Magic Hammer veya Özel Magic Hammer Değil ise
|
||
if ((!(type == REPAIR_ALL
|
||
|| type == ACID_ALL) && pItem->sDuration <= 0)
|
||
// İtem Yok ise
|
||
|| (pTable = g_pMain->GetItemPtr(pItem->nNum)) == nullptr
|
||
// Sağ El veya Sol ise
|
||
|| (type == ATTACK && ((nSlot == LEFTHAND || nSlot == RIGHTHAND) && pTable->m_bSlot == ItemSlot1HLeftHand))
|
||
// Tamir Edilmeyen Eşya ise
|
||
|| (type == REPAIR_ALL && pTable->m_iSellPrice == SellTypeNoRepairs))
|
||
continue;
|
||
|
||
if (type == REPAIR_ALL
|
||
|| type == ACID_ALL)
|
||
{
|
||
if ((pItem->sDuration + damage) > pTable->m_sDuration)
|
||
damage = pTable->m_sDuration;
|
||
|
||
pItem->sDuration = pTable->m_sDuration;
|
||
SendDurability(nSlot, pItem->sDuration);
|
||
UserLookChange(nSlot, pItem->nNum, pItem->sDuration);
|
||
|
||
continue;
|
||
}
|
||
|
||
int iBeforePercent = (int)((pItem->sDuration / (double)pTable->m_sDuration) * 100);
|
||
|
||
if (iWorerate > pItem->sDuration)
|
||
pItem->sDuration = 0;
|
||
else
|
||
pItem->sDuration -= iWorerate;
|
||
|
||
if (pItem->sDuration < 0)
|
||
pItem->sDuration = 0;
|
||
|
||
if (pItem->sDuration == 0)
|
||
{
|
||
SendDurability(nSlot, 0);
|
||
SetUserAbility(false);
|
||
SendItemMove(1);
|
||
continue;
|
||
}
|
||
|
||
SendDurability(nSlot, pItem->sDuration);
|
||
|
||
int iCurrentPercent = (int)((pItem->sDuration / (double)pTable->m_sDuration) * 100);
|
||
|
||
if ((iCurrentPercent / 5) != (iBeforePercent / 5))
|
||
{
|
||
if ((iCurrentPercent >= 65 && iCurrentPercent < 70)
|
||
|| (iCurrentPercent >= 25 && iCurrentPercent <= 30))
|
||
UserLookChange(nSlot, pItem->nNum, pItem->sDuration);
|
||
}
|
||
}
|
||
}
|
||
|
||
void CUser::SendDurability(uint8 slot, uint16 durability)
|
||
{
|
||
Packet result(WIZ_DURATION, slot);
|
||
result << durability;
|
||
Send(&result);
|
||
}
|
||
|
||
void CUser::SendItemMove(uint8 subcommand)
|
||
{
|
||
Packet result(WIZ_ITEM_MOVE, uint8(1));
|
||
|
||
if (m_bAttackAmount < 1)
|
||
m_bAttackAmount = 1;
|
||
|
||
// If the subcommand is not error, send the stats.
|
||
if (subcommand != 0)
|
||
{
|
||
result << subcommand
|
||
<< uint16(m_sTotalHit * m_bAttackAmount / 100)
|
||
<< uint16(m_sTotalAc + m_sACAmount)
|
||
<< m_sMaxWeight
|
||
<< uint8(1)
|
||
<< m_iMaxHp << m_iMaxMp
|
||
<< GetStatBonusTotal(STAT_STR) << GetStatBonusTotal(STAT_STA)
|
||
<< GetStatBonusTotal(STAT_DEX) << GetStatBonusTotal(STAT_INT)
|
||
<< GetStatBonusTotal(STAT_CHA)
|
||
<< uint16(((m_sFireR + m_bAddFireR) * m_bPctFireR / 100) + m_bResistanceBonus)
|
||
<< uint16(((m_sColdR + m_bAddColdR) * m_bPctColdR / 100) + m_bResistanceBonus)
|
||
<< uint16(((m_sLightningR + m_bAddLightningR) * m_bPctLightningR / 100) + m_bResistanceBonus)
|
||
<< uint16(((m_sMagicR + m_bAddMagicR) * m_bPctMagicR / 100) + m_bResistanceBonus)
|
||
<< uint16(((m_sDiseaseR + m_bAddDiseaseR) * m_bPctDiseaseR / 100) + m_bResistanceBonus)
|
||
<< uint16(((m_sPoisonR + m_bAddPoisonR) * m_bPctPoisonR / 100) + m_bResistanceBonus);
|
||
}
|
||
Send(&result);
|
||
}
|
||
|
||
void CUser::HPTimeChange()
|
||
{
|
||
m_tHPLastTimeNormal = UNIXTIME;
|
||
|
||
if (isDead())
|
||
return;
|
||
|
||
int mpPercent = 100;
|
||
|
||
if (GetZoneID() == ZONE_SNOW_BATTLE
|
||
&& g_pMain->m_byBattleOpen == SNOW_BATTLE)
|
||
{
|
||
HpChange(5);
|
||
return;
|
||
}
|
||
|
||
// For mages with under 30% of HP
|
||
if (CheckClass(110, 210)
|
||
&& m_sMp < (30 * m_iMaxMp / 100))
|
||
mpPercent = 120;
|
||
|
||
if (m_bResHpType == USER_STANDING
|
||
/* // unknown types
|
||
|| m_bResHpType == 4
|
||
|| m_bResHpType == 5*/)
|
||
{
|
||
if (m_iMaxMp != m_sMp)
|
||
{
|
||
MSpChange((int)(((GetLevel() * (1 + GetLevel() / 60.0) + 1) * 0.2) + 3) * mpPercent / 100);
|
||
}
|
||
}
|
||
else if (m_bResHpType == USER_SITDOWN)
|
||
{
|
||
if(m_lastTrainingTime == 0)
|
||
m_lastTrainingTime = UNIXTIME;
|
||
|
||
if(m_lastTrainingTime + PLAYER_TRAINING_INTERVAL < UNIXTIME && GetLevel() > 30)
|
||
{
|
||
uint64 myExp = g_pMain->GetExpByLevel(GetLevel());
|
||
if (GetLevel() >= MAX_LEVEL && m_iExp == myExp || GetRebLevel() > 11)
|
||
return;
|
||
|
||
m_lastTrainingTime = UNIXTIME;
|
||
//uint32 iTrainingExp = ((GetLevel()*15)*53) /60;
|
||
uint32 iTrainingExp = ((GetLevel()*15)*53) /120;
|
||
|
||
m_iTotalTrainingExp += iTrainingExp;
|
||
|
||
ExpChange(iTrainingExp);
|
||
|
||
Packet result(WIZ_MINING,uint8(0x12));
|
||
result << uint8(0x03) << m_iTotalTrainingExp;
|
||
|
||
Send(&result);
|
||
}
|
||
if (isGM())
|
||
{
|
||
HpChange(m_iMaxHp);
|
||
MSpChange(m_iMaxMp);
|
||
}
|
||
else
|
||
{
|
||
if (m_iMaxHp != m_sHp)
|
||
HpChange((int)(GetLevel() * (1 + GetLevel() / 30.0)) + 3);
|
||
|
||
if (m_iMaxMp != m_sMp)
|
||
MSpChange((int)(((m_iMaxMp * 5) / ((GetLevel() - 1) + 30 )) + 3) * mpPercent / 100);
|
||
}
|
||
}
|
||
}
|
||
|
||
void CUser::HPTimeChangeType3()
|
||
{
|
||
if (isDead()
|
||
|| !m_bType3Flag)
|
||
return;
|
||
|
||
uint16 totalActiveDurationalSkills = 0,
|
||
totalActiveDOTSkills = 0;
|
||
bool bIsDOT = false, isUsedGiver = false;
|
||
for (int i = 0; i < MAX_TYPE3_REPEAT; i++)
|
||
{
|
||
MagicType3 * pEffect = &m_durationalSkills[i];
|
||
|
||
if (!pEffect->m_byUsed)
|
||
continue;
|
||
|
||
if (!pEffect->m_sTo){
|
||
|
||
/// KENDINE USERE
|
||
// Has the required interval elapsed before using this skill?
|
||
if ((UNIXTIME - pEffect->m_tHPLastTime) >= pEffect->m_bHPInterval)
|
||
{
|
||
Unit * pUnit = g_pMain->GetUnitPtr(pEffect->m_sSourceID);
|
||
|
||
// Reduce the HP
|
||
if(!isUsedGiver)
|
||
HpChange(pEffect->m_sHPAmount, pUnit); // do we need to specify the source of the DOT?
|
||
pEffect->m_tHPLastTime = UNIXTIME;
|
||
|
||
if (pEffect->m_sHPAmount < 0)
|
||
bIsDOT = true;
|
||
|
||
if(pEffect->m_sHPAmount > 0)
|
||
isUsedGiver = true;
|
||
|
||
// Has the skill expired yet?
|
||
if (++pEffect->m_bTickCount == pEffect->m_bTickLimit)
|
||
{
|
||
Packet result(WIZ_MAGIC_PROCESS, uint8(MAGIC_DURATION_EXPIRED));
|
||
|
||
// Healing-over-time skills require the type 100
|
||
if (pEffect->m_sHPAmount > 0)
|
||
result << uint8(100);
|
||
else // Damage-over-time requires 200.
|
||
result << uint8(200);
|
||
|
||
Send(&result);
|
||
|
||
// If the skill inflicts poison damage, remember to remove the poison indicator!
|
||
if (pEffect->m_byAttribute == POISON_R)
|
||
SendUserStatusUpdate(USER_STATUS_POISON, USER_STATUS_CURE);
|
||
|
||
pEffect->Reset();
|
||
}
|
||
}
|
||
|
||
if (pEffect->m_byUsed)
|
||
{
|
||
totalActiveDurationalSkills++;
|
||
if (pEffect->m_sHPAmount < 0)
|
||
totalActiveDOTSkills++;
|
||
}
|
||
}else{
|
||
//// NPCYE YARATIGA
|
||
// Has the required interval elapsed before using this skill?
|
||
if ((UNIXTIME - pEffect->m_tHPLastTime) >= pEffect->m_bHPInterval)
|
||
{
|
||
CNpc * pNpc = g_pMain->GetNpcPtr(pEffect->m_sSourceID);
|
||
|
||
if (pNpc == nullptr)
|
||
continue;
|
||
|
||
// Reduce the HP
|
||
pNpc->HpChange(pEffect->m_sHPAmount, this);
|
||
pEffect->m_tHPLastTime = UNIXTIME;
|
||
|
||
// Has the skill expired yet?
|
||
if (++pEffect->m_bTickCount == pEffect->m_bTickLimit)
|
||
pEffect->Reset();
|
||
|
||
}
|
||
|
||
if (pEffect->m_byUsed)
|
||
{
|
||
totalActiveDurationalSkills++;
|
||
if (pEffect->m_sHPAmount < 0)
|
||
totalActiveDOTSkills++;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
// Have all the skills expired?
|
||
if (totalActiveDurationalSkills == 0)
|
||
m_bType3Flag = false;
|
||
|
||
// If there was DOT skills when we started, but none anymore... revert the HP bar.
|
||
if (bIsDOT && totalActiveDOTSkills == 0)
|
||
SendUserStatusUpdate(USER_STATUS_DOT, USER_STATUS_CURE);
|
||
}
|
||
|
||
void CUser::Type4Duration()
|
||
{
|
||
Guard lock(m_buffLock);
|
||
if (m_buffMap.empty())
|
||
return;
|
||
|
||
foreach (itr, m_buffMap)
|
||
{
|
||
if (itr->second.m_tEndTime > UNIXTIME)
|
||
continue;
|
||
|
||
CMagicProcess::RemoveType4Buff(itr->first, this, true,isLockableScroll(itr->second.m_bBuffType));
|
||
|
||
|
||
break; // only ever handle one at a time with the current logic
|
||
}
|
||
|
||
if (!isDebuffed())
|
||
SendUserStatusUpdate(USER_STATUS_POISON, USER_STATUS_CURE);
|
||
}
|
||
|
||
void CUser::SendAllKnightsID()
|
||
{
|
||
Packet result(WIZ_KNIGHTS_LIST, uint8(1));
|
||
uint16 count = 0;
|
||
|
||
foreach_stlmap (itr, g_pMain->m_KnightsArray)
|
||
{
|
||
CKnights *pKnights = itr->second;
|
||
if (pKnights == nullptr)
|
||
continue;
|
||
result << pKnights->m_sIndex << pKnights->m_strName;
|
||
count++;
|
||
}
|
||
|
||
result.put(0, count);
|
||
SendCompressed(&result);
|
||
}
|
||
|
||
void CUser::OperatorCommand(Packet & pkt)
|
||
{
|
||
if (!isGM())
|
||
return;
|
||
|
||
std::string strUserID;
|
||
uint8 opcode;
|
||
bool bIsOnline = false;
|
||
std::string sNoticeMessage, sOperatorCommandType;
|
||
pkt >> opcode >> strUserID;
|
||
if (strUserID.empty() || strUserID.size() > MAX_ID_SIZE)
|
||
return;
|
||
|
||
CUser *pUser = g_pMain->GetUserPtr(strUserID, TYPE_CHARACTER);
|
||
CBot *pBot = g_pMain->GetBotPtr(strUserID, TYPE_CHARACTER);
|
||
if (pUser == nullptr)
|
||
{
|
||
if(pBot != nullptr && (opcode == OPERATOR_ARREST || opcode == OPERATOR_CUTOFF))
|
||
{
|
||
if(pBot->isInGame())
|
||
bIsOnline=true;
|
||
else
|
||
bIsOnline=false;
|
||
}
|
||
else
|
||
bIsOnline = false;
|
||
}
|
||
else
|
||
bIsOnline = true;
|
||
|
||
|
||
|
||
switch (opcode)
|
||
{
|
||
case OPERATOR_ARREST:
|
||
if (bIsOnline)
|
||
{
|
||
if(pUser == nullptr)
|
||
ZoneChange(pBot->m_bZone,pBot->m_curx,pBot->m_curz);
|
||
else
|
||
ZoneChange(pUser->GetZoneID(), pUser->m_curx, pUser->m_curz);
|
||
sOperatorCommandType = "OPERATOR_ARREST";
|
||
}
|
||
break;
|
||
case OPERATOR_SUMMON:
|
||
if (bIsOnline)
|
||
{
|
||
pUser->ZoneChange(GetZoneID(), m_curx, m_curz);
|
||
sOperatorCommandType = "OPERATOR_SUMMON";
|
||
}
|
||
break;
|
||
case OPERATOR_CUTOFF:
|
||
if (bIsOnline)
|
||
{
|
||
if(pUser == nullptr)
|
||
pBot->UserInOut(INOUT_OUT);
|
||
else
|
||
pUser->Disconnect();
|
||
sOperatorCommandType = "OPERATOR_CUTOFF";
|
||
}
|
||
break;
|
||
case OPERATOR_BAN:
|
||
case OPERATOR_BAN_ACCOUNT: // ban account is meant to call a proc to do so
|
||
if (bIsOnline)
|
||
{
|
||
|
||
pUser->m_bAuthority = AUTHORITY_BANNED;
|
||
pUser->Disconnect();
|
||
}
|
||
else
|
||
g_DBAgent.UpdateUserAuthority(strUserID,AUTHORITY_BANNED);
|
||
|
||
sOperatorCommandType = "OPERATOR_BAN_ACCOUNT";
|
||
sNoticeMessage = string_format("%s is currently blocked for illegal activity.", strUserID.c_str());
|
||
break;
|
||
|
||
case OPERATOR_MUTE:
|
||
if (bIsOnline)
|
||
pUser->m_bAuthority = AUTHORITY_MUTED;
|
||
else
|
||
g_DBAgent.UpdateUserAuthority(strUserID,AUTHORITY_MUTED);
|
||
|
||
sOperatorCommandType = "OPERATOR_MUTE";
|
||
sNoticeMessage = string_format("%s is currently muted.", strUserID.c_str());
|
||
break;
|
||
case OPERATOR_DISABLE_ATTACK:
|
||
if (bIsOnline)
|
||
pUser->m_bAuthority = AUTHORITY_ATTACK_DISABLED;
|
||
else
|
||
g_DBAgent.UpdateUserAuthority(strUserID,AUTHORITY_ATTACK_DISABLED);
|
||
sOperatorCommandType = "OPERATOR_DISABLE_ATTACK";
|
||
sNoticeMessage = string_format("%s is currently attack disabled.", strUserID.c_str());
|
||
break;
|
||
case OPERATOR_ENABLE_ATTACK:
|
||
if (bIsOnline)
|
||
pUser->m_bAuthority = AUTHORITY_PLAYER;
|
||
else
|
||
g_DBAgent.UpdateUserAuthority(strUserID,AUTHORITY_PLAYER);
|
||
sOperatorCommandType = "OPERATOR_ENABLE_ATTACK";
|
||
sNoticeMessage = string_format("%s is currently attack enabled.", strUserID.c_str());
|
||
break;
|
||
case OPERATOR_UNMUTE:
|
||
if (bIsOnline)
|
||
pUser->m_bAuthority = AUTHORITY_PLAYER;
|
||
else
|
||
g_DBAgent.UpdateUserAuthority(strUserID,AUTHORITY_PLAYER);
|
||
sOperatorCommandType = "OPERATOR_UNMUTE";
|
||
sNoticeMessage = string_format("%s is currently unmuted.", strUserID.c_str());
|
||
break;
|
||
}
|
||
|
||
if (!sNoticeMessage.empty())
|
||
g_pMain->SendNotice(sNoticeMessage.c_str(),Nation::ALL);
|
||
|
||
if (!sOperatorCommandType.empty())
|
||
{
|
||
DateTime time;
|
||
g_pMain->WriteGMLogFile(string_format("[ GAME MASTER - %d:%d:%d ] %s : %s %s ( Zone=%d, X=%d, Z=%d )\n",time.GetHour(),time.GetMinute(),time.GetSecond(),GetName().c_str(),sOperatorCommandType.c_str(),strUserID.c_str(),GetZoneID(),uint16(GetX()),uint16(GetZ())));
|
||
}
|
||
}
|
||
|
||
void CUser::SpeedHackTime(Packet& pkt)
|
||
{
|
||
if (!isInGame() || isGM())
|
||
return;
|
||
|
||
float nSpeed = 45.0f;
|
||
|
||
if (GetFame() == COMMAND_CAPTAIN || isRogue() || GetZoneID() == ZONE_CHAOS_DUNGEON)
|
||
nSpeed = 90.0f;
|
||
else if (isWarrior() || isMage() || isPriest() || isKurian())
|
||
nSpeed = 67.0f;
|
||
|
||
float nRange = (pow(GetX() - m_LastX, 2.0f) + pow(GetZ() - m_LastZ, 2.0f)) / 100.0f;
|
||
|
||
if (nRange >= nSpeed)
|
||
{
|
||
DateTime time;
|
||
g_pMain->WriteCheatLogFile(string_format("[ SpeedHack - %d:%d:%d ] %s is Warp to Last Position.\n", time.GetHour(),time.GetMinute(),time.GetSecond(),GetName().c_str()));
|
||
Warp(uint16(m_LastX) * 10, uint16(m_LastZ) * 10);
|
||
}
|
||
else
|
||
{
|
||
m_LastX = GetX();
|
||
m_LastZ = GetZ();
|
||
}
|
||
}
|
||
|
||
int CUser::FindSlotForItem(uint32 nItemID, uint16 sCount /*= 1*/)
|
||
{
|
||
int result = -1;
|
||
_ITEM_TABLE *pTable = g_pMain->GetItemPtr(nItemID);
|
||
if (pTable == nullptr)
|
||
return result;
|
||
|
||
// If the item's stackable, try to find it a home.
|
||
// We could do this in the same logic, but I'd prefer one initial check
|
||
// over the additional logic hit each loop iteration.
|
||
if (pTable->m_bCountable)
|
||
{
|
||
for (int i = SLOT_MAX; i < SLOT_MAX+HAVE_MAX; i++)
|
||
{
|
||
_ITEM_DATA *pItem = GetItem(i);
|
||
|
||
if(pItem == nullptr)
|
||
continue;
|
||
|
||
// If it's the item we're after, and there will be room to store it...
|
||
if (pItem
|
||
&& pItem->nNum == nItemID
|
||
&& pItem->sCount + sCount <= ITEMCOUNT_MAX)
|
||
return i;
|
||
|
||
// Found a free slot, we'd prefer to stack it though
|
||
// so store the first free slot, and ignore it.
|
||
if (pItem->nNum == 0
|
||
&& result < 0)
|
||
result = i;
|
||
}
|
||
|
||
// If we didn't find a slot countaining our stackable item, it's possible we found
|
||
// an empty slot. So return that (or -1 if it none was found; no point searching again).
|
||
return result;
|
||
}
|
||
|
||
// If it's not stackable, don't need any additional logic.
|
||
// Just find the first free slot.
|
||
return GetEmptySlot();
|
||
}
|
||
|
||
int CUser::GetEmptySlotCount()
|
||
{
|
||
uint8 SlotCount = 0;
|
||
for (int i = SLOT_MAX; i < SLOT_MAX+HAVE_MAX; i++)
|
||
{
|
||
_ITEM_DATA *pItem = GetItem(i);
|
||
|
||
if (!pItem)
|
||
continue;
|
||
|
||
if (pItem->nNum == 0)
|
||
SlotCount++;
|
||
}
|
||
|
||
return SlotCount;
|
||
}
|
||
|
||
|
||
int CUser::GetEmptySlot()
|
||
{
|
||
for (int i = SLOT_MAX; i < SLOT_MAX+HAVE_MAX; i++)
|
||
{
|
||
_ITEM_DATA *pItem = GetItem(i);
|
||
|
||
if (!pItem)
|
||
continue;
|
||
|
||
if (pItem->nNum == 0)
|
||
return i;
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
void CUser::Home()
|
||
{
|
||
if (isDead()
|
||
// When transformed into a "Kaul", you are unable to /town or attack.
|
||
|| isKaul()
|
||
/* No cheats allowed */
|
||
|| GetHealth() < (GetMaxHealth() / 2)
|
||
|| GetZoneID() == ZONE_CHAOS_DUNGEON
|
||
|| GetZoneID() == ZONE_BORDER_DEFENSE_WAR
|
||
|| hasBuff(BUFF_TYPE_FREEZE)
|
||
|| isSummonPet && SummonPetID > 0 && isGenieActive())
|
||
return;
|
||
|
||
// The point where you will be warped to.
|
||
short x = 0, z = 0;
|
||
|
||
|
||
_OBJECT_EVENT* pEvent = nullptr;
|
||
pEvent = GetMap()->GetObjectEvent(m_sBind);
|
||
// Resurrect at a bind/respawn point
|
||
if (pEvent && pEvent->byLife == 1 && GetZoneID() != ZONE_DELOS && pEvent->sIndex != 101 && pEvent->sIndex != 201)
|
||
{
|
||
SetPosition(pEvent->fPosX + x, 0.0f, pEvent->fPosZ + z);
|
||
x = short(pEvent->fPosX);
|
||
z = short(pEvent->fPosZ);
|
||
Warp(x * 10, z * 10);
|
||
return;
|
||
}
|
||
|
||
// Forgotten Temple
|
||
if (GetZoneID() == ZONE_FORGOTTEN_TEMPLE)
|
||
{
|
||
KickOutZoneUser(true);
|
||
return;
|
||
}
|
||
// Prevent /town'ing in quest arenas
|
||
else if ((GetZoneID() / 10) == 5
|
||
|| !GetStartPosition(x, z))
|
||
return;
|
||
Warp(x * 10, z * 10);
|
||
}
|
||
|
||
bool CUser::GetStartPosition(short & x, short & z, uint8 bZone /*= 0 */)
|
||
{
|
||
// Get start position data for current zone (unless we specified a zone).
|
||
int nZoneID = (bZone == 0 ? GetZoneID() : bZone);
|
||
_START_POSITION *pData = g_pMain->GetStartPosition(nZoneID);
|
||
_KNIGHTS_SIEGE_WARFARE *pKnightSiege = g_pMain->GetSiegeMasterKnightsPtr(1);
|
||
CKnights *pKnights = g_pMain->GetClanPtr(pKnightSiege->sMasterKnights);
|
||
if (pData == nullptr)
|
||
return false;
|
||
|
||
// TODO: Allow for Delos/CSW.
|
||
|
||
// NOTE: This is how mgame does it.
|
||
// This only allows for positive randomisation; we should really allow for the full range...
|
||
if (pKnightSiege->sMasterKnights == GetClanID() && GetZoneID() == ZONE_DELOS && GetClanID() != 0)
|
||
{
|
||
x = 505 + myrand(0, pData->bRangeX);
|
||
z = 840 + myrand(0, pData->bRangeZ);
|
||
}
|
||
else
|
||
{
|
||
// NOTE: This is how mgame does it.
|
||
// This only allows for positive randomisation; we should really allow for the full range...
|
||
if (GetNation() == KARUS)
|
||
{
|
||
x = pData->sKarusX + myrand(0, pData->bRangeX);
|
||
z = pData->sKarusZ + myrand(0, pData->bRangeZ);
|
||
}
|
||
else
|
||
{
|
||
x = pData->sElmoradX + myrand(0, pData->bRangeX);
|
||
z = pData->sElmoradZ + myrand(0, pData->bRangeZ);
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
bool CUser::GetStartPositionRandom(short & x, short & z, uint8 bZone)
|
||
{
|
||
int nRandom = myrand(0, g_pMain->m_StartPositionRandomArray.GetSize() - 1);
|
||
goto GetPosition;
|
||
|
||
GetPosition:
|
||
{
|
||
if (g_pMain->m_StartPositionRandomArray.GetData(nRandom)->ZoneID == (bZone == 0 ? GetZoneID() : bZone))
|
||
{
|
||
x = g_pMain->m_StartPositionRandomArray.GetData(nRandom)->PosX + myrand(0, g_pMain->m_StartPositionRandomArray.GetData(nRandom)->Radius);
|
||
z = g_pMain->m_StartPositionRandomArray.GetData(nRandom)->PosZ + myrand(0, g_pMain->m_StartPositionRandomArray.GetData(nRandom)->Radius);
|
||
return true;
|
||
}
|
||
|
||
nRandom = myrand(0, g_pMain->m_StartPositionRandomArray.GetSize() - 1);
|
||
goto GetPosition;
|
||
}
|
||
|
||
return GetStartPosition(x, z);
|
||
}
|
||
|
||
void CUser::ResetWindows()
|
||
{
|
||
/*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->Dead();
|
||
}
|
||
}*/
|
||
|
||
if (isTrading())
|
||
ExchangeCancel();
|
||
|
||
if (m_bRequestingChallenge)
|
||
HandleChallengeCancelled(m_bRequestingChallenge);
|
||
|
||
if (m_bChallengeRequested)
|
||
HandleChallengeRejected(m_bChallengeRequested);
|
||
|
||
// If we're a vendor, close the stall
|
||
if (isMerchanting() || m_bMerchantStatex)
|
||
MerchantClose();
|
||
|
||
// If we're just browsing, free up our spot so others can browse the vendor.
|
||
if (m_sMerchantsSocketID >= 0)
|
||
CancelMerchant();
|
||
|
||
if(isMining())
|
||
{
|
||
HandleMiningStop((Packet)(WIZ_MINING, MiningStop));
|
||
HandleFishingStop((Packet)(WIZ_MINING,FishingStop));
|
||
}
|
||
}
|
||
|
||
CUser * CUser::GetItemRoutingUser(uint32 nItemID, uint16 sCount, _LOOT_BUNDLE * pBundle)
|
||
{
|
||
if (!isInParty())
|
||
return this;
|
||
|
||
_ITEM_TABLE * pTable;
|
||
_PARTY_GROUP * pParty = g_pMain->GetPartyPtr(GetPartyID());
|
||
if (pParty == nullptr
|
||
|| (pTable = g_pMain->GetItemPtr(nItemID)) == nullptr
|
||
|| pParty->bItemRouting >= MAX_PARTY_USERS)
|
||
return nullptr;
|
||
|
||
for (int i = 0; i < MAX_PARTY_USERS; i++)
|
||
{
|
||
CUser * pUser = g_pMain->GetUserPtr(pParty->uid[pParty->bItemRouting]);
|
||
|
||
if (pParty->bItemRouting > 6)
|
||
pParty->bItemRouting = 0;
|
||
else
|
||
pParty->bItemRouting++;
|
||
|
||
if (pUser != nullptr
|
||
&& pUser->CheckWeight(pTable, nItemID, sCount)
|
||
&& pUser->isInRange(pBundle->x, pBundle->z, MAX_LOOT_RANGE))
|
||
return pUser;
|
||
}
|
||
|
||
return nullptr;
|
||
}
|
||
|
||
void CUser::ClassChangeReq()
|
||
{
|
||
Packet result(WIZ_CLASS_CHANGE, uint8(CLASS_CHANGE_RESULT));
|
||
if (GetLevel() < 10) // if we haven't got our first job change
|
||
result << uint8(2);
|
||
else if ((m_sClass % 100) > 4) // if we've already got our job change
|
||
result << uint8(3);
|
||
else // otherwise
|
||
result << uint8(1);
|
||
Send(&result);
|
||
}
|
||
|
||
// Dialog
|
||
void CUser::SendStatSkillDistribute()
|
||
{
|
||
Packet result(WIZ_CLASS_CHANGE,uint8(CLASS_CHANGE_REQ));
|
||
Send(&result);
|
||
}
|
||
|
||
void CUser::SendPetUpgrade()
|
||
{
|
||
Packet Pet(WIZ_SELECT_MSG);
|
||
Pet << uint16(0x00) << uint8(0x09);
|
||
Send(&Pet);
|
||
}
|
||
|
||
void CUser::AllSkillPointChange(bool bIsFree)
|
||
{
|
||
Packet result(WIZ_CLASS_CHANGE, uint8(ALL_SKILLPT_CHANGE));
|
||
int index = 0, skill_point = 0, money = 0, temp_value = 0, old_money = 0;
|
||
uint8 type = 0;
|
||
|
||
temp_value = (int)pow((GetLevel() * 2.0f), 3.4f);
|
||
if (GetLevel() < 30)
|
||
temp_value = (int)(temp_value * 0.4f);
|
||
else if (GetLevel() >= 60)
|
||
temp_value = (int)(temp_value * 1.5f);
|
||
|
||
temp_value = (int)(temp_value * 1.5f);
|
||
|
||
// If global discounts are enabled
|
||
if (g_pMain->m_sDiscount == 2 // or war discounts are enabled
|
||
|| (g_pMain->m_sDiscount == 1 && g_pMain->m_byOldVictory == m_bNation))
|
||
temp_value /= 2;
|
||
|
||
// Level too low.
|
||
if (GetLevel() < 10)
|
||
goto fail_return;
|
||
|
||
if (bIsFree)
|
||
{
|
||
for (int i = 0; i < SLOT_MAX; i++)
|
||
{
|
||
if (m_sItemArray[i].nNum)
|
||
goto fail_return;
|
||
}
|
||
}
|
||
|
||
|
||
// Get total skill points
|
||
for (int i = 1; i < 9; i++)
|
||
skill_point += m_bstrSkill[i];
|
||
|
||
// If we don't have any skill points, there's no point resetting now is there.
|
||
/*if (skill_point <= 0)
|
||
{
|
||
type = 2;
|
||
goto fail_return;
|
||
}*/
|
||
|
||
// Not enough money.
|
||
if (!bIsFree && g_pMain->Reskillmoney != 1){
|
||
if(!GoldLose(temp_value, false))
|
||
goto fail_return;
|
||
}
|
||
|
||
// Reset skill points.
|
||
m_bstrSkill[0] = (GetLevel() - 9) * 2;
|
||
for (int i = 1; i < 9; i++)
|
||
m_bstrSkill[i] = 0;
|
||
|
||
result << uint8(1) << GetCoins() << m_bstrSkill[0];
|
||
Send(&result);
|
||
return;
|
||
|
||
fail_return:
|
||
result << type << temp_value;
|
||
Send(&result);
|
||
}
|
||
|
||
void CUser::AllPointChange(bool bIsFree)
|
||
{
|
||
Packet result(WIZ_CLASS_CHANGE, uint8(ALL_POINT_CHANGE));
|
||
int temp_money;
|
||
uint16 statTotal;
|
||
|
||
uint16 byStr, bySta, byDex, byInt, byCha;
|
||
uint8 bResult = 0;
|
||
|
||
if (GetLevel() > g_pMain->MAXLVLINI)
|
||
goto fail_return;
|
||
|
||
temp_money = (int)pow((GetLevel() * 2.0f ), 3.4f);
|
||
if (GetLevel() < 30)
|
||
temp_money = (int)(temp_money * 0.4f);
|
||
else if (GetLevel() >= 60)
|
||
temp_money = (int)(temp_money * 1.5f);
|
||
|
||
if ((g_pMain->m_sDiscount == 1 && g_pMain->m_byOldVictory == GetNation())
|
||
|| g_pMain->m_sDiscount == 2)
|
||
temp_money /= 2;
|
||
|
||
for (int i = 0; i < SLOT_MAX; i++)
|
||
{
|
||
if (m_sItemArray[i].nNum) {
|
||
bResult = 4;
|
||
goto fail_return;
|
||
}
|
||
}
|
||
|
||
// It's 300-10 for clarity (the 10 being the stat points assigned on char creation)
|
||
/*if (GetStatTotal() == 290)
|
||
{
|
||
bResult = 2; // don't need to reallocate stats, it has been done already...
|
||
goto fail_return;
|
||
}*/
|
||
|
||
// Not enough coins
|
||
if (!bIsFree && g_pMain->Restatmoney != 1){
|
||
if(!GoldLose(temp_money, false))
|
||
goto fail_return;
|
||
}
|
||
|
||
// TODO: Pull this from the database.
|
||
switch (m_bRace)
|
||
{
|
||
case KARUS_BIG:
|
||
if(GROUP_WARRIOR)
|
||
{
|
||
SetStat(STAT_STR, 65);
|
||
SetStat(STAT_STA, 65);
|
||
SetStat(STAT_DEX, 60);
|
||
SetStat(STAT_INT, 50);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
break;
|
||
case KARUS_MIDDLE:
|
||
if(GROUP_WARRIOR)
|
||
{
|
||
SetStat(STAT_STR, 65);
|
||
SetStat(STAT_STA, 65);
|
||
SetStat(STAT_DEX, 60);
|
||
SetStat(STAT_INT, 50);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
else
|
||
{
|
||
SetStat(STAT_STR, 60);
|
||
SetStat(STAT_STA, 60);
|
||
SetStat(STAT_DEX, 70);
|
||
SetStat(STAT_INT, 50);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
break;
|
||
case KARUS_SMALL:
|
||
if (GROUP_WARRIOR)
|
||
{
|
||
SetStat(STAT_STR, 65);
|
||
SetStat(STAT_STA, 65);
|
||
SetStat(STAT_DEX, 60);
|
||
SetStat(STAT_INT, 50);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
else if(GROUP_ROGUE)
|
||
{
|
||
SetStat(STAT_STR, 60);
|
||
SetStat(STAT_STA, 60);
|
||
SetStat(STAT_DEX, 70);
|
||
SetStat(STAT_INT, 50);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
else if(GROUP_CLERIC)
|
||
{
|
||
SetStat(STAT_STR, 50);
|
||
SetStat(STAT_STA, 50);
|
||
SetStat(STAT_DEX, 70);
|
||
SetStat(STAT_INT, 70);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
else if(GROUP_MAGE)
|
||
{
|
||
SetStat(STAT_STR, 50);
|
||
SetStat(STAT_STA, 60);
|
||
SetStat(STAT_DEX, 60);
|
||
SetStat(STAT_INT, 70);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
break;
|
||
case KARUS_WOMAN:
|
||
if(GROUP_WARRIOR)
|
||
{
|
||
SetStat(STAT_STR, 65);
|
||
SetStat(STAT_STA, 65);
|
||
SetStat(STAT_DEX, 60);
|
||
SetStat(STAT_INT, 50);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
else if(GROUP_ROGUE)
|
||
{
|
||
SetStat(STAT_STR, 60);
|
||
SetStat(STAT_STA, 60);
|
||
SetStat(STAT_DEX, 70);
|
||
SetStat(STAT_INT, 50);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
else if(GROUP_CLERIC)
|
||
{
|
||
SetStat(STAT_STR, 50);
|
||
SetStat(STAT_STA, 50);
|
||
SetStat(STAT_DEX, 70);
|
||
SetStat(STAT_INT, 70);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
else if(GROUP_MAGE)
|
||
{
|
||
SetStat(STAT_STR, 50);
|
||
SetStat(STAT_STA, 60);
|
||
SetStat(STAT_DEX, 60);
|
||
SetStat(STAT_INT, 70);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
break;
|
||
case KARUS_MONSTER:
|
||
SetStat(STAT_STR, 65);
|
||
SetStat(STAT_STA, 65);
|
||
SetStat(STAT_DEX, 60);
|
||
SetStat(STAT_INT, 50);
|
||
SetStat(STAT_CHA, 50);
|
||
break;
|
||
case BABARIAN:
|
||
if(GROUP_WARRIOR)
|
||
{
|
||
SetStat(STAT_STR, 65);
|
||
SetStat(STAT_STA, 65);
|
||
SetStat(STAT_DEX, 60);
|
||
SetStat(STAT_INT, 50);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
break;
|
||
case ELMORAD_MAN:
|
||
if(GROUP_WARRIOR)
|
||
{
|
||
SetStat(STAT_STR, 65);
|
||
SetStat(STAT_STA, 65);
|
||
SetStat(STAT_DEX, 60);
|
||
SetStat(STAT_INT, 50);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
else if(GROUP_ROGUE)
|
||
{
|
||
SetStat(STAT_STR, 60);
|
||
SetStat(STAT_STA, 60);
|
||
SetStat(STAT_DEX, 70);
|
||
SetStat(STAT_INT, 50);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
else if(GROUP_CLERIC)
|
||
{
|
||
SetStat(STAT_STR, 50);
|
||
SetStat(STAT_STA, 50);
|
||
SetStat(STAT_DEX, 70);
|
||
SetStat(STAT_INT, 70);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
else if(GROUP_MAGE)
|
||
{
|
||
SetStat(STAT_STR, 50);
|
||
SetStat(STAT_STA, 60);
|
||
SetStat(STAT_DEX, 60);
|
||
SetStat(STAT_INT, 70);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
break;
|
||
case ELMORAD_WOMAN:
|
||
if(GROUP_WARRIOR)
|
||
{
|
||
SetStat(STAT_STR, 65);
|
||
SetStat(STAT_STA, 65);
|
||
SetStat(STAT_DEX, 60);
|
||
SetStat(STAT_INT, 50);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
else if(GROUP_ROGUE)
|
||
{
|
||
SetStat(STAT_STR, 60);
|
||
SetStat(STAT_STA, 60);
|
||
SetStat(STAT_DEX, 70);
|
||
SetStat(STAT_INT, 50);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
else if(GROUP_CLERIC)
|
||
{
|
||
SetStat(STAT_STR, 50);
|
||
SetStat(STAT_STA, 50);
|
||
SetStat(STAT_DEX, 70);
|
||
SetStat(STAT_INT, 70);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
else if(GROUP_MAGE)
|
||
{
|
||
SetStat(STAT_STR, 50);
|
||
SetStat(STAT_STA, 60);
|
||
SetStat(STAT_DEX, 60);
|
||
SetStat(STAT_INT, 70);
|
||
SetStat(STAT_CHA, 50);
|
||
}
|
||
break;
|
||
case ELMORAD_MONSTER:
|
||
SetStat(STAT_STR, 65);
|
||
SetStat(STAT_STA, 65);
|
||
SetStat(STAT_DEX, 60);
|
||
SetStat(STAT_INT, 50);
|
||
SetStat(STAT_CHA, 50);
|
||
break;
|
||
}
|
||
|
||
// Players gain 3 stats points for each level up to and including 60.
|
||
// They also received 10 free stat points on creation.
|
||
m_sPoints = 10 + (GetLevel() - 1) * 3;
|
||
|
||
// For every level after 60, we add an additional two points.
|
||
if (GetLevel() > 60)
|
||
m_sPoints += 2 * (GetLevel() - 60);
|
||
|
||
|
||
statTotal = GetStatTotal();
|
||
|
||
SetUserAbility();
|
||
Send2AI_UserUpdateInfo();
|
||
|
||
byStr = GetStat(STAT_STR);
|
||
bySta = GetStat(STAT_STA),
|
||
byDex = GetStat(STAT_DEX);
|
||
byInt = GetStat(STAT_INT),
|
||
byCha = GetStat(STAT_CHA);
|
||
|
||
RobItem(700001000, 1);
|
||
|
||
result << uint8(1) // result (success)
|
||
<< GetCoins()
|
||
<< byStr << bySta << byDex << byInt << byCha
|
||
<< m_iMaxHp << m_iMaxMp << m_sTotalHit << m_sMaxWeight << m_sPoints;
|
||
Send(&result);
|
||
return;
|
||
|
||
fail_return:
|
||
result << bResult << temp_money;
|
||
Send(&result);
|
||
}
|
||
void CUser::GiveKnightCash(uint32 nKnightCash)
|
||
{
|
||
if (isDead()
|
||
|| isTrading()
|
||
|| isMerchanting()
|
||
|| isSellingMerchant()
|
||
|| isBuyingMerchant()
|
||
|| isStoreOpen()
|
||
|| isMining()
|
||
|| m_bMerchantStatex)
|
||
return;
|
||
|
||
if (nKnightCash <= 0)
|
||
return;
|
||
|
||
m_nKnightCash += nKnightCash;
|
||
g_DBAgent.UpdateAccountKnightCash(m_strAccountID,nKnightCash);
|
||
|
||
g_pMain->SendHelpDescription(this,string_format("[%d] Knight Cash has sent to your account '%s'.",nKnightCash,m_strAccountID.c_str()));
|
||
}
|
||
void CUser::GoldChange(short tid, int gold)
|
||
{
|
||
if (m_bZone == ZONE_SNOW_BATTLE
|
||
|| m_bZone == ZONE_DESPERATION_ABYSS
|
||
|| m_bZone == ZONE_HELL_ABYSS
|
||
|| m_bZone == ZONE_DRAGON_CAVE
|
||
|| m_bZone == ZONE_CAITHAROS_ARENA)
|
||
return;
|
||
|
||
CUser* pTUser = g_pMain->GetUserPtr(tid);
|
||
if (pTUser == nullptr || pTUser->m_iGold <= 0)
|
||
return;
|
||
|
||
// Reward money in war zone
|
||
if (gold == 0)
|
||
{
|
||
// If we're not in a party, we can distribute cleanly.
|
||
if (!isInParty())
|
||
{
|
||
GoldGain((pTUser->m_iGold * 4) / 10);
|
||
pTUser->GoldLose(pTUser->m_iGold / 2);
|
||
return;
|
||
}
|
||
|
||
// Otherwise, if we're in a party, we need to divide it up.
|
||
_PARTY_GROUP* pParty = g_pMain->GetPartyPtr(GetPartyID());
|
||
if (pParty == nullptr)
|
||
return;
|
||
|
||
int userCount = 0, levelSum = 0, temp_gold = (pTUser->m_iGold * 4) / 10;
|
||
pTUser->GoldLose(pTUser->m_iGold / 2);
|
||
|
||
// TODO: Clean up the party system.
|
||
for (int i = 0; i < MAX_PARTY_USERS; i++)
|
||
{
|
||
CUser *pUser = g_pMain->GetUserPtr(pParty->uid[i]);
|
||
if (pUser == nullptr)
|
||
continue;
|
||
|
||
userCount++;
|
||
levelSum += pUser->GetLevel();
|
||
}
|
||
|
||
// No users (this should never happen! Needs to be cleaned up...), don't bother with the below loop.
|
||
if (userCount == 0)
|
||
return;
|
||
|
||
for (int i = 0; i < MAX_PARTY_USERS; i++)
|
||
{
|
||
CUser * pUser = g_pMain->GetUserPtr(pParty->uid[i]);
|
||
if (pUser == nullptr)
|
||
continue;
|
||
|
||
pUser->GoldGain((int)(temp_gold * (float)(pUser->GetLevel() / (float)levelSum)));
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Otherwise, use the coin amount provided.
|
||
|
||
// Source gains money
|
||
if (gold > 0)
|
||
{
|
||
GoldGain(gold);
|
||
pTUser->GoldLose(gold);
|
||
}
|
||
// Source loses money
|
||
else
|
||
{
|
||
GoldLose(gold);
|
||
pTUser->GoldGain(gold);
|
||
}
|
||
}
|
||
|
||
void CUser::SelectWarpList(Packet & pkt)
|
||
{
|
||
if (isDead())
|
||
return;
|
||
|
||
uint16 npcid, warpid;
|
||
pkt >> npcid >> warpid;
|
||
|
||
_WARP_INFO *pWarp = GetMap()->GetWarp(warpid);
|
||
if (pWarp == nullptr
|
||
|| (pWarp->sNation != 0 && pWarp->sNation != GetNation()))
|
||
return;
|
||
|
||
C3DMap *pMap = g_pMain->GetZoneByID(pWarp->sZone);
|
||
if (pMap == nullptr)
|
||
return;
|
||
|
||
_ZONE_SERVERINFO *pInfo = g_pMain->m_ServerArray.GetData(pMap->m_nServerNo);
|
||
if (pInfo == nullptr)
|
||
return;
|
||
|
||
float rx = 0.0f, rz = 0.0f;
|
||
rx = (float)myrand(0, (int)pWarp->fR * 2);
|
||
if (rx < pWarp->fR)
|
||
rx = -rx;
|
||
|
||
rz = (float)myrand(0, (int)pWarp->fR * 2);
|
||
if (rz < pWarp->fR)
|
||
rz = -rz;
|
||
|
||
if (m_bZone == pWarp->sZone)
|
||
{
|
||
m_bZoneChangeSameZone = true;
|
||
|
||
Packet result(WIZ_WARP_LIST, uint8(2));
|
||
result << uint8(1);
|
||
Send(&result);
|
||
}
|
||
|
||
ZoneChange(pWarp->sZone, pWarp->fX + rx, pWarp->fZ + rz);
|
||
|
||
if (GetZoneID() == pWarp->sZone && pWarp->dwPay > 0 && hasCoins(pWarp->dwPay))
|
||
GoldLose(pWarp->dwPay);
|
||
}
|
||
|
||
void CUser::ServerChangeOk(Packet & pkt)
|
||
{
|
||
return;
|
||
C3DMap* pMap = GetMap();
|
||
if (pMap == nullptr)
|
||
return;
|
||
|
||
uint16 warpid = pkt.read<uint16>();
|
||
_WARP_INFO* pWarp = pMap->GetWarp(warpid);
|
||
if (pWarp == nullptr)
|
||
return;
|
||
|
||
float rx = 0.0f, rz = 0.0f;
|
||
rx = (float)myrand(0, (int)pWarp->fR * 2);
|
||
if (rx < pWarp->fR)
|
||
rx = -rx;
|
||
|
||
rz = (float)myrand(0, (int)pWarp->fR * 2);
|
||
if (rz < pWarp->fR)
|
||
rz = -rz;
|
||
|
||
ZoneChange(pWarp->sZone, pWarp->fX + rx, pWarp->fZ + rz);
|
||
}
|
||
|
||
bool CUser::GetWarpList(int warp_group)
|
||
{
|
||
Packet result(WIZ_WARP_LIST, uint8(1));
|
||
C3DMap* pMap = GetMap();
|
||
set<_WARP_INFO*> warpList;
|
||
if(pMap == nullptr)
|
||
return false;
|
||
|
||
pMap->GetWarpList(warp_group, warpList);
|
||
|
||
result << uint16(warpList.size());
|
||
BOOST_FOREACH (auto itr, warpList)
|
||
{
|
||
C3DMap *pDstMap = g_pMain->GetZoneByID((itr)->sZone);
|
||
if (pDstMap == nullptr)
|
||
continue;
|
||
|
||
if (g_pMain->OpenArdream
|
||
&& (itr->sZone == ZONE_RONARK_LAND
|
||
|| itr->sZone == ZONE_RONARK_LAND_BASE))
|
||
continue;
|
||
|
||
if (g_pMain->OpenCZ
|
||
&& (itr->sZone == ZONE_ARDREAM
|
||
|| itr->sZone == ZONE_RONARK_LAND_BASE))
|
||
continue;
|
||
|
||
if (g_pMain->isWarOpen()
|
||
&& ((g_pMain->m_byBattleZoneType != ZONE_ARDREAM
|
||
&& ((itr)->sZone == ZONE_ARDREAM
|
||
|| (itr)->sZone == ZONE_RONARK_LAND_BASE
|
||
|| (itr)->sZone == ZONE_RONARK_LAND))
|
||
|| (g_pMain->m_byBattleZoneType == ZONE_ARDREAM
|
||
&& (itr)->sZone == ZONE_ARDREAM)))
|
||
continue;
|
||
|
||
if (g_pMain->m_bEventZoneIsActive
|
||
&& g_pMain->m_nEventZoneTime == 5
|
||
&& (itr)->sZone == ZONE_RONARK_LAND)
|
||
continue;
|
||
|
||
result << (itr)->sWarpID
|
||
<< (itr)->strWarpName << (itr)->strAnnounce
|
||
<< (itr)->sZone
|
||
<< pDstMap->m_sMaxUser
|
||
<< uint32((itr)->dwPay);
|
||
}
|
||
|
||
Send(&result);
|
||
return true;
|
||
}
|
||
|
||
bool CUser::BindObjectEvent(_OBJECT_EVENT *pEvent)
|
||
{
|
||
if (pEvent->sBelong != 0 && pEvent->sBelong != GetNation())
|
||
return false;
|
||
|
||
Packet result(WIZ_OBJECT_EVENT, uint8(pEvent->sType));
|
||
|
||
m_sBind = pEvent->sIndex;
|
||
|
||
result << uint8(1);
|
||
Send(&result);
|
||
return true;
|
||
}
|
||
bool CUser::GateLeverObjectEvent(_OBJECT_EVENT *pEvent, int nid)
|
||
{
|
||
_OBJECT_EVENT *pGateEvent;
|
||
CNpc* pNpc, *pGateNpc;
|
||
_KNIGHTS_SIEGE_WARFARE *pKnightSiegewars = g_pMain->GetSiegeMasterKnightsPtr(1);
|
||
CKnights *pKnight = g_pMain->GetClanPtr(GetClanID());
|
||
|
||
// Does the lever (object) NPC exist?
|
||
if ((pNpc = g_pMain->GetNpcPtr(nid)) == nullptr
|
||
// Does the corresponding gate object event exist?
|
||
|| (pGateEvent = GetMap()->GetObjectEvent(pEvent->sControlNpcID)) == nullptr
|
||
// Does the corresponding gate (object) NPC exist?
|
||
|| (pGateNpc = g_pMain->FindNpcInZone(pEvent->sControlNpcID,GetZoneID())) == nullptr
|
||
// Is it even a gate?
|
||
|| !pGateNpc->isGate()
|
||
|| pGateNpc->isDead()
|
||
|| ((pKnight == nullptr || pKnightSiegewars == nullptr) && GetZoneID() == ZONE_DELOS)
|
||
// If the gate's closed (i.e. the lever is down), we can't open it unless the lever isn't nation-specific
|
||
// or we're the correct nation. Seems the other nation cannot close them.
|
||
|| (pNpc->isGateClosed() && pNpc->GetNation() != 0 && pNpc->GetNation() != GetNation() && GetZoneID() != ZONE_DELOS))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
if ((GetZoneID() == ZONE_DELOS && pKnightSiegewars->sMasterKnights != 0 && pKnight->m_sIndex == pKnightSiegewars->sMasterKnights)
|
||
|| ((GetFame() == COMMAND_CAPTAIN || isKing()) && GetZoneID() != ZONE_DELOS) && (pNpc->GetNation() == GetNation() || pNpc->GetNation() == 0))
|
||
{
|
||
// Move the lever (up/down).
|
||
pNpc->SendGateFlag(!pNpc->m_byGateOpen);
|
||
|
||
// Open/close the gate.
|
||
pGateNpc->SendGateFlag(!pGateNpc->m_byGateOpen);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
/***
|
||
* Not sure what this is used for, so keeping logic the same just in case.
|
||
***/
|
||
bool CUser::FlagObjectEvent(_OBJECT_EVENT *pEvent, int nid)
|
||
{
|
||
_OBJECT_EVENT *pFlagEvent;
|
||
CNpc *pNpc, *pFlagNpc;
|
||
|
||
// Does the flag object NPC exist?
|
||
if ((pNpc = g_pMain->GetNpcPtr(nid)) == nullptr
|
||
// Does the corresponding flag event exist?
|
||
|| (pFlagEvent = GetMap()->GetObjectEvent(pEvent->sControlNpcID)) == nullptr
|
||
// Does the corresponding flag object NPC exist?
|
||
|| (pFlagNpc = g_pMain->FindNpcInZone(pEvent->sControlNpcID, GetZoneID())) == nullptr
|
||
// Is this marked a gate? (i.e. can control)
|
||
|| !pFlagNpc->isGate()
|
||
// Is the war over or the gate closed?
|
||
|| g_pMain->m_bVictory > 0 || pNpc->isGateClosed())
|
||
return false;
|
||
|
||
// Reset objects
|
||
pNpc->SendGateFlag(0);
|
||
pFlagNpc->SendGateFlag(0);
|
||
|
||
// Add flag score (not sure what this is, is this even used anymore?)
|
||
if (GetNation() == KARUS)
|
||
g_pMain->m_bKarusFlag++;
|
||
else
|
||
g_pMain->m_bElmoradFlag++;
|
||
|
||
// Did one of the teams win?
|
||
g_pMain->BattleZoneVictoryCheck();
|
||
return true;
|
||
}
|
||
|
||
bool CUser::WarpListObjectEvent(_OBJECT_EVENT *pEvent)
|
||
{
|
||
// If the warp gate belongs to a nation, which isn't us...
|
||
if (pEvent->sBelong != 0 && pEvent->sBelong != GetNation()
|
||
// or we're in the opposing nation's zone...
|
||
|| (GetZoneID() != GetNation() && GetZoneID() <= ELMORAD)
|
||
// or we're unable to retrieve the warp list...
|
||
|| !GetWarpList(pEvent->sControlNpcID))
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
void CUser::ObjectEvent(Packet & pkt)
|
||
{
|
||
if (g_pMain->m_bPointCheckFlag == false || isDead())
|
||
return;
|
||
|
||
bool bSuccess = false;
|
||
uint16 objectindex, nid;
|
||
pkt >> objectindex >> nid;
|
||
|
||
|
||
_OBJECT_EVENT * pEvent = GetMap()->GetObjectEvent(objectindex);
|
||
if (pEvent != nullptr
|
||
&& isInRange(pEvent->fPosX, pEvent->fPosZ, MAX_OBJECT_RANGE))
|
||
{
|
||
switch (pEvent->sType)
|
||
{
|
||
case OBJECT_GATE:
|
||
case OBJECT_BIND:
|
||
case OBJECT_REMOVE_BIND:
|
||
bSuccess = BindObjectEvent(pEvent);
|
||
break;
|
||
|
||
case OBJECT_GATE_LEVER:
|
||
bSuccess = GateLeverObjectEvent(pEvent, nid);
|
||
break;
|
||
|
||
case OBJECT_FLAG_LEVER:
|
||
bSuccess = FlagObjectEvent(pEvent, nid);
|
||
break;
|
||
|
||
case OBJECT_WARP_GATE:
|
||
bSuccess = WarpListObjectEvent(pEvent);
|
||
if (bSuccess)
|
||
return;
|
||
break;
|
||
|
||
case OBJECT_ANVIL:
|
||
SendAnvilRequest(nid);
|
||
return;
|
||
}
|
||
|
||
}
|
||
|
||
if (!bSuccess)
|
||
{
|
||
Packet result(WIZ_OBJECT_EVENT, uint8(pEvent == nullptr ? 0 : pEvent->sType));
|
||
result << uint8(0);
|
||
Send(&result);
|
||
}
|
||
}
|
||
|
||
void CUser::SendAnvilRequest(uint16 sNpcID, uint8 bType /*= ITEM_UPGRADE_REQ*/)
|
||
{
|
||
Packet result(WIZ_ITEM_UPGRADE, uint8(bType));
|
||
result << sNpcID;
|
||
Send(&result);
|
||
}
|
||
|
||
void CUser::UpdateVisibility(InvisibilityType bNewType)
|
||
{
|
||
Packet result(AG_USER_VISIBILITY);
|
||
m_bInvisibilityType = (uint8)(bNewType);
|
||
result << GetID() << m_bInvisibilityType;
|
||
Send_AIServer(&result);
|
||
}
|
||
|
||
/**
|
||
* @brief Forces a reset of the GM's persistent visibility
|
||
* state.
|
||
*/
|
||
void CUser::ResetGMVisibility()
|
||
{
|
||
if (!isGM()
|
||
|| isTransformed())
|
||
return;
|
||
|
||
// Force the client to reset
|
||
if (m_bAbnormalType != ABNORMAL_INVISIBLE)
|
||
{
|
||
// Only send this packet to the GM as other users
|
||
// will already see the GM as invisible.
|
||
Packet result(WIZ_STATE_CHANGE);
|
||
result << GetID() << uint8(5) << uint32(0);
|
||
Send(&result);
|
||
}
|
||
|
||
m_bAbnormalType = ABNORMAL_INVISIBLE;
|
||
}
|
||
|
||
void CUser::BlinkStart()
|
||
{
|
||
if (isGM()
|
||
|| isInPVPZone()
|
||
|| GetMap()->isWarZone()
|
||
|| isMonsterTransformation()
|
||
|| isNPCTransformation()
|
||
|| isSiegeTransformation())
|
||
return;
|
||
|
||
m_bAbnormalType = ABNORMAL_BLINKING;
|
||
m_tBlinkExpiryTime = UNIXTIME + BLINK_TIME;
|
||
m_bRegeneType = REGENE_ZONECHANGE;
|
||
|
||
UpdateVisibility(INVIS_DISPEL_ON_ATTACK); // AI shouldn't see us
|
||
m_bInvisibilityType = INVIS_NONE; // but players should.
|
||
|
||
StateChangeServerDirect(3, ABNORMAL_BLINKING);
|
||
}
|
||
|
||
void CUser::BlinkTimeCheck()
|
||
{
|
||
if (UNIXTIME < m_tBlinkExpiryTime)
|
||
return;
|
||
|
||
m_bRegeneType = REGENE_NORMAL;
|
||
|
||
m_bCanUseSkills = true;
|
||
|
||
StateChangeServerDirect(3, ABNORMAL_NORMAL);
|
||
|
||
Packet result(AG_USER_REGENE);
|
||
result << GetSocketID() << m_sHp;
|
||
Send_AIServer(&result);
|
||
|
||
result.Initialize(AG_USER_INOUT);
|
||
result.SByte(); // TODO: Remove this redundant uselessness that is mgame
|
||
result << uint8(INOUT_RESPAWN) << GetSocketID()
|
||
<< GetName()
|
||
<< GetX() << GetZ();
|
||
Send_AIServer(&result);
|
||
|
||
UpdateVisibility(INVIS_NONE);
|
||
}
|
||
|
||
void CUser::GoldGain(uint32 gold, bool bSendPacket /*= true*/, bool bApplyBonus /*= false*/)
|
||
{
|
||
// Assuming it works like this, although this affects (probably) all gold gained (including kills in PvP zones)
|
||
// If this is wrong and it should ONLY affect gold gained from monsters, let us know!
|
||
if (bApplyBonus)
|
||
gold = gold * (m_bNoahGainAmount + m_bItemNoahGainAmount) / 100;
|
||
|
||
if (m_iGold + gold > COIN_MAX)
|
||
m_iGold = COIN_MAX;
|
||
else
|
||
m_iGold += gold;
|
||
|
||
if (bSendPacket)
|
||
{
|
||
Packet result(WIZ_GOLD_CHANGE, uint8(CoinGain));
|
||
result << gold << GetCoins();
|
||
Send(&result);
|
||
}
|
||
}
|
||
|
||
bool CUser::GoldLose(uint32 gold, bool bSendPacket /*= true*/)
|
||
{
|
||
if (!hasCoins(gold))
|
||
return false;
|
||
|
||
m_iGold -= gold;
|
||
|
||
if (bSendPacket)
|
||
{
|
||
Packet result(WIZ_GOLD_CHANGE, uint8(CoinLoss));
|
||
result << gold << GetCoins();
|
||
Send(&result);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool CUser::CheckSkillPoint(uint8 skillnum, uint8 min, uint8 max)
|
||
{
|
||
if (skillnum < 5 || skillnum > 8)
|
||
return false;
|
||
|
||
return (m_bstrSkill[skillnum] >= min && m_bstrSkill[skillnum] <= max);
|
||
}
|
||
|
||
bool CUser::CheckClass(short class1, short class2, short class3, short class4, short class5, short class6)
|
||
{
|
||
return (JobGroupCheck(class1) || JobGroupCheck(class2) || JobGroupCheck(class3) || JobGroupCheck(class4) || JobGroupCheck(class5) || JobGroupCheck(class6));
|
||
}
|
||
|
||
bool CUser::JobGroupCheck(short jobgroupid)
|
||
{
|
||
if (jobgroupid > 100)
|
||
return GetClass() == jobgroupid;
|
||
|
||
ClassType subClass = GetBaseClassType();
|
||
switch (jobgroupid)
|
||
{
|
||
case GROUP_WARRIOR:
|
||
return (subClass == ClassWarrior || subClass == ClassWarriorNovice || subClass == ClassWarriorMaster);
|
||
|
||
case GROUP_KURIAN:
|
||
return (subClass == ClassPorutu || subClass == ClassPorutuSkilled || subClass == ClassPorutuMaster );
|
||
|
||
case GROUP_ROGUE:
|
||
return (subClass == ClassRogue || subClass == ClassRogueNovice || subClass == ClassRogueMaster);
|
||
|
||
case GROUP_MAGE:
|
||
return (subClass == ClassMage || subClass == ClassMageNovice || subClass == ClassMageMaster);
|
||
|
||
case GROUP_CLERIC:
|
||
return (subClass == ClassPriest || subClass == ClassPriestNovice || subClass == ClassPriestMaster);
|
||
}
|
||
|
||
return (subClass == jobgroupid);
|
||
}
|
||
|
||
void CUser::TrapProcess()
|
||
{
|
||
// If the time interval has passed
|
||
if ((UNIXTIME - m_tLastTrapAreaTime) >= ZONE_TRAP_INTERVAL)
|
||
{
|
||
if(GetZoneID() == ZONE_BIFROST)
|
||
SendUserStatusUpdate(USER_STATUS_BLIND,USER_STATUS_INFLICT);
|
||
|
||
HpChange(-ZONE_TRAP_DAMAGE, this);
|
||
m_tLastTrapAreaTime = UNIXTIME;
|
||
}
|
||
}
|
||
|
||
void CUser::KickOutZoneUser(bool home, uint8 nZoneID)
|
||
{
|
||
if (home)
|
||
{
|
||
C3DMap* pMap = g_pMain->GetZoneByID(nZoneID);
|
||
if (pMap == nullptr)
|
||
return;
|
||
|
||
int eventID = 0;
|
||
int random = myrand(0, 9000);
|
||
if (random >= 0 && random < 3000) eventID = 0;
|
||
else if (random >= 3000 && random < 6000) eventID = 1;
|
||
else if (random >= 6000 && random < 9001) eventID = 2;
|
||
|
||
_REGENE_EVENT* pRegene = pMap->GetRegeneEvent(eventID);
|
||
if (pRegene == nullptr)
|
||
{
|
||
KickOutZoneUser();
|
||
return;
|
||
}
|
||
|
||
ZoneChange(pMap->m_nZoneNumber,
|
||
pRegene->fRegenePosX + (float)myrand(0, (int)pRegene->fRegeneAreaX),
|
||
pRegene->fRegenePosZ + (float)myrand(0, (int)pRegene->fRegeneAreaZ));
|
||
return;
|
||
}
|
||
|
||
// Teleport the player to their native zone.
|
||
short x, z;
|
||
|
||
if (GetStartPosition(x,z,GetNation()))
|
||
ZoneChange(GetNation(), x, z);
|
||
else
|
||
{
|
||
TRACE("### KickOutZoneUser - StartPosition not found : Nation=%d",GetNation());
|
||
}
|
||
}
|
||
|
||
void CUser::NativeZoneReturn()
|
||
{
|
||
_START_POSITION *pStartPosition = g_pMain->m_StartPositionArray.GetData(m_bNation);
|
||
if (pStartPosition == nullptr)
|
||
return;
|
||
|
||
m_bZone = m_bNation;
|
||
|
||
m_curx = (float)((m_bNation == KARUS ? pStartPosition->sKarusX : pStartPosition->sElmoradX) + myrand(0, pStartPosition->bRangeX));
|
||
m_curz = (float)((m_bNation == KARUS ? pStartPosition->sKarusZ : pStartPosition->sElmoradZ) + myrand(0, pStartPosition->bRangeZ));
|
||
}
|
||
|
||
/**
|
||
* @brief Sends a packet to all players within the
|
||
* user's current region and surrounding regions
|
||
* (i.e. visible area)
|
||
*
|
||
* @param pkt The packet.
|
||
* @param pExceptUser User to except. If specified, will ignore this user.
|
||
*/
|
||
void CUser::SendToRegion(Packet *pkt, CUser *pExceptUser /*= nullptr*/, uint16 nEventRoom /*-1*/)
|
||
{
|
||
g_pMain->Send_Region(pkt, GetMap(), GetRegionX(), GetRegionZ(), pExceptUser, nEventRoom);
|
||
}
|
||
/**
|
||
* @brief Sends a packet to all players within the
|
||
* user's current zone.
|
||
*
|
||
* @param pkt The packet.
|
||
* @param pExceptUser User to except. If specified, will ignore this user.
|
||
*/
|
||
void CUser::SendToZone(Packet *pkt, CUser *pExceptUser /*= nullptr*/, uint16 nEventRoom /*-1*/, float fRange)
|
||
{
|
||
g_pMain->Send_Zone(pkt, GetZoneID(), pExceptUser, 3, nEventRoom, fRange);
|
||
}
|
||
|
||
void CUser::OnDeath(Unit *pKiller)
|
||
{
|
||
if (m_bResHpType == USER_DEAD)
|
||
return;
|
||
|
||
m_bResHpType = USER_DEAD;
|
||
m_iUserDeathCount++;
|
||
AchieveType1(20);
|
||
// Player is dead stop other process.
|
||
ResetWindows();
|
||
|
||
if (GetFame() == COMMAND_CAPTAIN)
|
||
{
|
||
if (GetNation() == KARUS)
|
||
g_pMain->Announcement(KARUS_CAPTAIN_DEPRIVE_NOTIFY, KARUS, 8, this);
|
||
else
|
||
g_pMain->Announcement(ELMORAD_CAPTAIN_DEPRIVE_NOTIFY, ELMORAD, 8, this);
|
||
}
|
||
|
||
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;
|
||
}
|
||
}
|
||
|
||
InitType3();
|
||
InitType4();
|
||
|
||
DateTime time;
|
||
|
||
if (pKiller != nullptr)
|
||
{
|
||
DeathNoticeType noticeType = DeathNoticeNone;
|
||
|
||
if (pKiller->isNPC())
|
||
{
|
||
CNpc *pNpc = TO_NPC(pKiller);
|
||
|
||
int64 nExpLost = 0;
|
||
//if (pNpc->GetType() == NPC_GUARD_TOWER1 || pNpc->GetType() == NPC_GUARD_TOWER2 || (GetZoneID() != GetNation() && GetZoneID() <= ELMORAD))
|
||
if (pNpc->GetType() == NPC_PATROL_GUARD || (GetZoneID() != GetNation() && GetZoneID() <= ELMORAD))
|
||
nExpLost = m_iMaxExp / 100;
|
||
else
|
||
nExpLost = m_iMaxExp / 20;
|
||
|
||
if(GetRebLevel() > 0)
|
||
nExpLost = 0;
|
||
|
||
if ((pNpc->GetType() == NPC_GUARD_TOWER1 || pNpc->GetType() == NPC_GUARD_TOWER2) && isInPKZone())
|
||
noticeType = DeathNotice;
|
||
|
||
if (GetPremiumProperty(PremiumExpRestorePercent) > 0)
|
||
nExpLost = nExpLost * (GetPremiumProperty(PremiumExpRestorePercent)) / 100;
|
||
|
||
g_pMain->WriteDeathUserLogFile(string_format("[ NPC/MONSTER - %d:%d:%d ] SID=%d,Killer=%s,Target=%s,Zone=%d,X=%d,Z=%d,TargetExp=%d,LostExp=%d\n",time.GetHour(),time.GetMinute(),time.GetSecond(),pNpc->GetProtoID(),pKiller->GetName().c_str(),GetName().c_str(),GetZoneID(),uint16(GetX()),uint16(GetZ()),m_iExp, nExpLost));
|
||
|
||
if (GetZoneID() != ZONE_FORGOTTEN_TEMPLE)
|
||
ExpChange(-nExpLost);
|
||
|
||
if (GetZoneID() == ZONE_FORGOTTEN_TEMPLE)
|
||
{
|
||
KickOutZoneUser(true);
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
CUser *pUser = TO_USER(pKiller);
|
||
|
||
// Looks like we died of "natural causes!" (probably residual DOT)
|
||
if (pUser == this)
|
||
{
|
||
m_sWhoKilledMe = -1;
|
||
}
|
||
// Someone else killed us? Need to clean this up.
|
||
else
|
||
{
|
||
pUser->m_iUserDefeatedCount++;
|
||
pUser->AchieveType1(1);
|
||
|
||
if (GetZoneID() == ZONE_CHAOS_DUNGEON)
|
||
{
|
||
noticeType = DeathNoticeCoordinates;
|
||
RobChaosSkillItems();
|
||
m_DeathCount++;
|
||
UpdatePlayerRank();
|
||
|
||
pUser->AchieveType1(11);
|
||
pUser->m_KillCount++;
|
||
pUser->UpdatePlayerRank();
|
||
}
|
||
else if (GetZoneID() == ZONE_BORDER_DEFENSE_WAR || GetZoneID() == ZONE_JURAD_MOUNTAIN)
|
||
{
|
||
Packet result(WIZ_EVENT, uint8(TEMPLE_EVENT_BORDER_COUNTER));
|
||
noticeType = DeathNoticeCoordinates;
|
||
|
||
GetNation() == KARUS ? g_pMain->pTempleEvent.KarusDeathCount[GetEventRoom()]++ : g_pMain->pTempleEvent.ElmoDeathCount[GetEventRoom()]++;
|
||
|
||
if (!pUser->isInParty())
|
||
pUser->LoyaltyChange(GetID());
|
||
else
|
||
pUser->LoyaltyDivide(GetID());
|
||
|
||
pUser->m_iLoyaltyDaily++;
|
||
pUser->UpdatePlayerRank();
|
||
result << g_pMain->pTempleEvent.ElmoDeathCount[GetEventRoom()] << uint16(0x00) << g_pMain->pTempleEvent.KarusDeathCount[GetEventRoom()] << uint16(0x00);
|
||
g_pMain->Send_Zone(&result, GetZoneID(), this, Nation::ALL,GetEventRoom());
|
||
|
||
if (g_pMain->pTempleEvent.ElmoDeathCount[GetEventRoom()] > 59 || g_pMain->pTempleEvent.KarusDeathCount[GetEventRoom()] > 59)
|
||
g_pMain->TempleEventFinish(GetEventRoom(),g_pMain->pTempleEvent.ElmoDeathCount[GetEventRoom()] > 59 ? 1 : 2);
|
||
}
|
||
else
|
||
{
|
||
// Did we get killed in the snow war? Handle appropriately.
|
||
if (GetZoneID() == ZONE_SNOW_BATTLE
|
||
&& g_pMain->m_byBattleOpen == SNOW_BATTLE)
|
||
{
|
||
pUser->GoldGain(SNOW_EVENT_MONEY);
|
||
|
||
if (!pUser->isInParty())
|
||
pUser->LoyaltyChange(GetID());
|
||
// In parties, the loyalty should be divided up across the party.
|
||
// Each party member in range should also receive a "Meat Dumpling".
|
||
else
|
||
pUser->LoyaltyChange(GetID());
|
||
|
||
if (GetNation() == KARUS)
|
||
g_pMain->m_sKarusDead++;
|
||
else
|
||
g_pMain->m_sElmoradDead++;
|
||
}
|
||
// All zones other than the snow war.
|
||
else if (GetZoneID() == ZONE_DELOS
|
||
&& g_pMain->m_byBattleOpen == CLAN_BATTLE)
|
||
{
|
||
noticeType = DeathNoticeCoordinates;
|
||
if (!pUser->isInParty())
|
||
pUser->LoyaltyChange(GetID());
|
||
// In parties, the loyalty should be divided up across the party.
|
||
// Each party member in range should also receive a "Meat Dumpling".
|
||
else
|
||
pUser->LoyaltyDivide(GetID());
|
||
}
|
||
else
|
||
{
|
||
if (isInArena() || isInPartyArena())
|
||
{
|
||
// Show death notices in the arena
|
||
noticeType = DeathNoticeCoordinates;
|
||
}
|
||
else
|
||
{
|
||
uint16 bonusNP = 0;
|
||
bool bKilledByRival = false;
|
||
|
||
if (!GetMap()->isWarZone() && g_pMain->m_byBattleOpen != NATION_BATTLE)
|
||
{
|
||
// Show death notices in PVP zones
|
||
noticeType = DeathNoticeCoordinates;
|
||
|
||
// If the killer has us set as their rival, reward them & remove the rivalry.
|
||
bKilledByRival = (!pUser->hasRivalryExpired() && pUser->GetRivalID() == GetID());
|
||
if (bKilledByRival)
|
||
{
|
||
// If we are our killer's rival, use the rival notice instead.
|
||
noticeType = DeathNoticeRival;
|
||
|
||
// Apply bonus NP for rival kills
|
||
if (GetZoneID() == ZONE_PVP_EVENT)
|
||
bonusNP += MINIRIVALRY_NP_BONUS;
|
||
else
|
||
bonusNP += RIVALRY_NP_BONUS;
|
||
|
||
pUser->AchieveType1(3);
|
||
|
||
pUser->RemoveRival();
|
||
|
||
// This player is no longer our rival
|
||
}
|
||
|
||
// The anger gauge is increased on each death.
|
||
// When your anger gauge is full (5 deaths), you can use the "Anger Explosion" skill.
|
||
if (!hasFullAngerGauge())
|
||
UpdateAngerGauge(++m_byAngerGauge);
|
||
|
||
}
|
||
|
||
// Loyalty should be awarded on kill.
|
||
if (!pUser->isInParty())
|
||
pUser->LoyaltyChange(GetID(), bonusNP);
|
||
// In parties, the loyalty should be divided up across the party.
|
||
// Each party member in range should also receive a "Meat Dumpling".
|
||
else
|
||
pUser->LoyaltyDivide(GetID(), bonusNP);
|
||
|
||
/*if (GetZoneID() != GetNation() && GetZoneID() <= ELMORAD)
|
||
{
|
||
int64 nExpLost = m_iMaxExp / 100;
|
||
|
||
if (GetPremiumProperty(PremiumExpRestorePercent) > 0)
|
||
nExpLost = nExpLost * (GetPremiumProperty(PremiumExpRestorePercent)) / 100;
|
||
|
||
ExpChange(-nExpLost);
|
||
}*/
|
||
|
||
// If we don't have a rival, this player is now our rival for 3 minutes.
|
||
if (isInPKZone()
|
||
&& !hasRival())
|
||
SetRival(pUser);
|
||
|
||
if (GetNation() == KARUS && !pUser->isInParty())
|
||
pUser->V3_MonsterCount(KARUS);
|
||
else if(GetNation() == ELMORAD && !pUser->isInParty())
|
||
pUser->V3_MonsterCount(ELMORAD);
|
||
|
||
if (pUser->isInParty() && (GetNation() == KARUS || GetNation() == ELMORAD))
|
||
{
|
||
_PARTY_GROUP *pParty = g_pMain->GetPartyPtr(pUser->GetPartyID());
|
||
if (pParty != nullptr)
|
||
{
|
||
for (int i = 0; i < 8; i++)
|
||
{
|
||
if (pParty->uid[i] >= 0)
|
||
{
|
||
CUser * pUserRange = g_pMain->GetUserPtr(pParty->uid[i]);
|
||
if (!isInRangeSlow(pUserRange, 50.0f) || pUserRange == nullptr)
|
||
continue;
|
||
CUser * pUserParty = g_pMain->GetUserPtr(pParty->uid[i]);
|
||
|
||
if (GetNation() == KARUS)
|
||
pUserParty->V3_MonsterCount(KARUS);
|
||
else if (GetNation() == ELMORAD)
|
||
pUserParty->V3_MonsterCount(ELMORAD);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
m_sWhoKilledMe = pUser->GetID();
|
||
}
|
||
|
||
string pKillerPartyUsers;
|
||
string pTargetPartyUsers;
|
||
|
||
if (GetZoneID() != ZONE_CHAOS_DUNGEON && (pUser->isInParty() || isInParty()))
|
||
{
|
||
CUser *pPartyUser;
|
||
_PARTY_GROUP *pParty = g_pMain->GetPartyPtr(pUser->GetPartyID());
|
||
if (pParty)
|
||
{
|
||
for (int i = 0; i < MAX_PARTY_USERS; i++)
|
||
{
|
||
pPartyUser = g_pMain->GetUserPtr(pParty->uid[i]);
|
||
if (pPartyUser)
|
||
pKillerPartyUsers += string_format("%s,",pPartyUser->GetName().c_str());
|
||
}
|
||
}
|
||
|
||
pParty = g_pMain->GetPartyPtr(GetPartyID());
|
||
if (pParty)
|
||
{
|
||
for (int i = 0; i < MAX_PARTY_USERS; i++)
|
||
{
|
||
pPartyUser = g_pMain->GetUserPtr(pParty->uid[i]);
|
||
if (pPartyUser)
|
||
pTargetPartyUsers += string_format("%s,",pPartyUser->GetName().c_str());
|
||
}
|
||
}
|
||
|
||
if (!pKillerPartyUsers.empty())
|
||
pKillerPartyUsers = pKillerPartyUsers.substr(0,pKillerPartyUsers.length() - 1);
|
||
|
||
if (!pTargetPartyUsers.empty())
|
||
pTargetPartyUsers = pTargetPartyUsers.substr(0,pTargetPartyUsers.length() - 1);
|
||
}
|
||
|
||
if (pKillerPartyUsers.empty() && pTargetPartyUsers.empty())
|
||
g_pMain->WriteDeathUserLogFile(string_format("[ USER - %d:%d:%d ] Killer=%s,Target=%s,Zone=%d,X=%d,Z=%d,LoyaltyKiller=%d,LoyaltyMonthlyKiller=%d,LoyaltyTarget=%d,LoyaltyMonthlyTarget=%d\n",time.GetHour(),time.GetMinute(),time.GetSecond(),pKiller->GetName().c_str(),GetName().c_str(),GetZoneID(),uint16(GetX()),uint16(GetZ()),TO_USER(pKiller)->GetLoyalty(),TO_USER(pKiller)->GetMonthlyLoyalty(),GetLoyalty(),GetMonthlyLoyalty()));
|
||
else if (pKillerPartyUsers.empty() && !pTargetPartyUsers.empty())
|
||
g_pMain->WriteDeathUserLogFile(string_format("[ USER - %d:%d:%d ] Killer=%s,Target=%s,TargetParty=%s,Zone=%d,X=%d,Z=%d,LoyaltyKiller=%d,LoyaltyMonthlyKiller=%d,LoyaltyTarget=%d,LoyaltyMonthlyTarget=%d\n",time.GetHour(),time.GetMinute(),time.GetSecond(),pKiller->GetName().c_str(),GetName().c_str(), pTargetPartyUsers.c_str(),GetZoneID(),uint16(GetX()),uint16(GetZ()),TO_USER(pKiller)->GetLoyalty(),TO_USER(pKiller)->GetMonthlyLoyalty(),GetLoyalty(),GetMonthlyLoyalty()));
|
||
else if (!pKillerPartyUsers.empty() && pTargetPartyUsers.empty())
|
||
g_pMain->WriteDeathUserLogFile(string_format("[ USER - %d:%d:%d ] Killer=%s,KillerParty=%s,Target=%s,Zone=%d,X=%d,Z=%d,LoyaltyKiller=%d,LoyaltyMonthlyKiller=%d,LoyaltyTarget=%d,LoyaltyMonthlyTarget=%d\n",time.GetHour(),time.GetMinute(),time.GetSecond(),pKiller->GetName().c_str(),pKillerPartyUsers.c_str(),GetName().c_str(),GetZoneID(),uint16(GetX()),uint16(GetZ()),TO_USER(pKiller)->GetLoyalty(),TO_USER(pKiller)->GetMonthlyLoyalty(),GetLoyalty(),GetMonthlyLoyalty()));
|
||
else if (!pKillerPartyUsers.empty() && !pTargetPartyUsers.empty())
|
||
g_pMain->WriteDeathUserLogFile(string_format("[ USER - %d:%d:%d ] Killer=%s,KillerParty=%s,Target=%s,TargetParty=%s,Zone=%d,X=%d,Z=%d,LoyaltyKiller=%d,LoyaltyMonthlyKiller=%d,LoyaltyTarget=%d,LoyaltyMonthlyTarget=%d\n",time.GetHour(),time.GetMinute(),time.GetSecond(),pKiller->GetName().c_str(),pKillerPartyUsers.c_str(),GetName().c_str(), pTargetPartyUsers.c_str(),GetZoneID(),uint16(GetX()),uint16(GetZ()),TO_USER(pKiller)->GetLoyalty(),TO_USER(pKiller)->GetMonthlyLoyalty(),GetLoyalty(),GetMonthlyLoyalty()));
|
||
}
|
||
|
||
if (noticeType != DeathNoticeNone)
|
||
SendDeathNotice(pKiller,noticeType);
|
||
}
|
||
|
||
Unit::OnDeath(pKiller);
|
||
}
|
||
|
||
/**
|
||
* @brief Updates the player's anger gauge level, setting it to
|
||
* byAngerGauge.
|
||
*
|
||
* @param byAngerGauge The anger gauge level.
|
||
*/
|
||
void CUser::UpdateAngerGauge(uint8 byAngerGauge)
|
||
{
|
||
Packet result(WIZ_PVP, uint8(byAngerGauge == 0 ? PVPResetHelmet : PVPUpdateHelmet));
|
||
|
||
if (byAngerGauge > MAX_ANGER_GAUGE)
|
||
byAngerGauge = MAX_ANGER_GAUGE;
|
||
|
||
m_byAngerGauge = byAngerGauge;
|
||
if (byAngerGauge > 0)
|
||
result << uint8(byAngerGauge) << hasFullAngerGauge();
|
||
|
||
Send(&result);
|
||
}
|
||
|
||
// We have no clan handler, we probably won't need to implement it (but we'll see).
|
||
void CUser::SendClanUserStatusUpdate(bool bToRegion /*= true*/)
|
||
{
|
||
Packet result(WIZ_KNIGHTS_PROCESS, uint8(KNIGHTS_MODIFY_FAME));
|
||
result << uint8(1) << GetSocketID()
|
||
<< GetClanID() << GetFame();
|
||
|
||
// TODO: Make this region code user-specific to perform faster.
|
||
if (bToRegion)
|
||
SendToRegion(&result,nullptr,GetEventRoom());
|
||
else
|
||
Send(&result);
|
||
}
|
||
|
||
void CUser::SendPartyStatusUpdate(uint8 bStatus, uint8 bResult /*= 0*/)
|
||
{
|
||
if (!isInParty())
|
||
return;
|
||
|
||
Packet result(WIZ_PARTY, uint8(PARTY_STATUSCHANGE));
|
||
result << GetSocketID() << bStatus << bResult;
|
||
g_pMain->Send_PartyMember(GetPartyID(), &result);
|
||
}
|
||
|
||
void CUser::HandleHelmet(Packet & pkt)
|
||
{
|
||
if (isDead())
|
||
return;
|
||
|
||
Packet result(WIZ_HELMET);
|
||
pkt >> m_bIsHidingHelmet;
|
||
pkt >> m_bIsHidingCospre;
|
||
|
||
result << m_bIsHidingHelmet;
|
||
result << m_bIsHidingCospre;
|
||
result << uint32(GetSocketID());
|
||
SendToRegion(&result,nullptr,GetEventRoom());
|
||
}
|
||
|
||
void CUser::HandleVIPStorage(Packet & pkt)
|
||
{
|
||
if (isDead()
|
||
|| isTrading()
|
||
|| isMerchanting()
|
||
|| isStoreOpen()
|
||
|| isSellingMerchant()
|
||
|| isBuyingMerchant()
|
||
|| isMining()
|
||
|| m_bMerchantStatex)
|
||
return;
|
||
|
||
|
||
uint8 OpCode;
|
||
|
||
pkt >> OpCode;
|
||
|
||
|
||
switch (OpCode)
|
||
{
|
||
case 1: // request
|
||
VipStorageOpenRequest(pkt);
|
||
break;
|
||
case 2: // item move Inventory -> Vip
|
||
VipStorageItemInput(pkt);
|
||
break;
|
||
case 3: // item move Vip -> Inventory
|
||
VipStorageItemOutput(pkt);
|
||
break;
|
||
case 4: // item move Vip -> Vip
|
||
VipStorageItemMove(pkt);
|
||
break;
|
||
case 5: // item move Inventory -> Inventory
|
||
VipStorageInventoryItemMove(pkt);
|
||
break;
|
||
case 6: // keyi kullanarak aktif etmek slotları
|
||
VipStorageUseKey(pkt);
|
||
break;
|
||
case 8: // yeni sifre belirleme
|
||
VipStorageNewPassword(pkt);
|
||
break;
|
||
case 10: // şifreyi değiştirme
|
||
VipStorageChangePassword(pkt);
|
||
case 11: // şifre girince
|
||
VipStorageOpen(pkt);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
//printf(" sonunda %d \n",VIPStoreFalseTrying);
|
||
}
|
||
|
||
|
||
|
||
bool Unit::isInAttackRange(Unit * pTarget, _MAGIC_TABLE * pSkill /*= nullptr*/)
|
||
{
|
||
if (pTarget == nullptr)
|
||
return false;
|
||
|
||
if (pTarget == this
|
||
|| !isPlayer())
|
||
return true;
|
||
|
||
const float fBaseMeleeRange = 15.0f; // far too generous
|
||
const float fBaseRangedRange = 65.0f;
|
||
|
||
float fRange = fBaseMeleeRange, fWeaponRange = 0.0f;
|
||
|
||
_ITEM_DATA * pItem = nullptr;
|
||
_ITEM_TABLE * pTable = TO_USER(this)->GetItemPrototype(RIGHTHAND, pItem);
|
||
|
||
if (pTable != nullptr
|
||
&& pItem->sDuration > 0)
|
||
{
|
||
fWeaponRange = pTable->m_sRange;
|
||
}
|
||
else
|
||
{
|
||
pTable = TO_USER(this)->GetItemPrototype(LEFTHAND, pItem);
|
||
if (pTable != nullptr
|
||
&& pItem->sDuration != 0)
|
||
fWeaponRange = pTable->m_sRange;
|
||
}
|
||
|
||
if (pSkill != nullptr)
|
||
{
|
||
// Not an attack skill, don't need to enforce these restrictions.
|
||
if (pSkill->bMoral != MORAL_ENEMY && pSkill->bMoral > MORAL_PARTY)
|
||
return true;
|
||
|
||
// For physical attack skills (type 1 - melee, type 2 - ranged), we'll need take into account
|
||
// the weapon's range.
|
||
if (pSkill->bType[0] != 3)
|
||
fRange = fWeaponRange;
|
||
|
||
// For physical melee & magic skills, try to use the skill's range if it's set.
|
||
// Need to allow more for lag, and poorly thought out skill ranges.
|
||
// If not, resort to using the weapon range -- or predefined 15m range in the case of type 3 skills.
|
||
if (pSkill->bType[0] != 2)
|
||
{
|
||
return isInRangeSlow(pTarget, fBaseMeleeRange + (pSkill->sRange == 0 ? fRange : pSkill->sRange * 2) + (pSkill->bType[0] == 1 ? fWeaponRange : 0));
|
||
}
|
||
// Ranged skills (type 2) don't typically have the main skill range set to anything useful, so
|
||
// we need to allow for the: bow's range, flying skill-specific range, and an extra 50m for the
|
||
// also extremely poorly thought out ranges.
|
||
else
|
||
{
|
||
_MAGIC_TYPE2 * pType2 = g_pMain->m_Magictype2Array.GetData(pSkill->iNum);
|
||
return pType2 != nullptr && isInRangeSlow(pTarget, fRange + pType2->sAddRange + fBaseRangedRange);
|
||
}
|
||
}
|
||
|
||
// Regular attack range.
|
||
if (fWeaponRange != 0.0f)
|
||
fRange = fBaseMeleeRange + fWeaponRange;
|
||
|
||
return isInRangeSlow(pTarget, fRange);
|
||
}
|
||
|
||
/**
|
||
* @brief Determine if we can use the specified item
|
||
* via the magic/skill system.
|
||
*
|
||
* @param itemid The ID of the item.
|
||
* @param count Stack (can probably disregard, as it's always 1).
|
||
*
|
||
* @return true if we can use item, false if not.
|
||
*/
|
||
bool CUser::CanUseItem(uint32 nItemID, uint16 sCount /*= 1*/)
|
||
{
|
||
_ITEM_TABLE* pItem = pItem = g_pMain->GetItemPtr(nItemID);
|
||
if (pItem == nullptr)
|
||
return false;
|
||
|
||
if (this == nullptr)
|
||
return false;
|
||
|
||
if(isTrading()
|
||
|| isMerchanting()
|
||
|| isStoreOpen()
|
||
|| m_bMerchantStatex
|
||
|| isMining())
|
||
return false;
|
||
|
||
// Disable scroll usage while transformed.
|
||
|
||
/*if(m_transformationType != TransformationNone
|
||
&& m_transformationType != TransformationMonster
|
||
&& m_transformationType != TransformationNPC
|
||
&& m_transformationType != TransformationSiege
|
||
)
|
||
return false;
|
||
Gereksiz..
|
||
*/
|
||
|
||
if (isTransformed())
|
||
{
|
||
// Various NPC transformations ("Transform Scrolls") are exempt from this rule -- it's just monsters.
|
||
// Also, siege transformations can use their own buff scrolls.
|
||
if (isNPCTransformation() && isSiegeTransformation())
|
||
return false;
|
||
}// 07.01.2016
|
||
|
||
// If the item's class specific, ensure it can be used by this user.
|
||
if ((pItem->m_bClass != 0 && !JobGroupCheck(pItem->m_bClass))
|
||
// Check the item's level requirement
|
||
|| (GetLevel() < pItem->m_bReqLevel || GetLevel() > pItem->m_bReqLevelMax)
|
||
// Ensure the item exists.
|
||
|| !CheckExistItem(nItemID, sCount))
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
void CUser::SendUserStatusUpdate(UserStatus type, UserStatusBehaviour status)
|
||
{
|
||
Packet result(WIZ_ZONEABILITY, uint8(2));
|
||
result << uint8(type) << uint8(status);
|
||
/*
|
||
1 , 0 = Cure damage over time
|
||
1 , 1 = Damage over time
|
||
2 , 0 = Cure poison
|
||
2 , 1 = poison (purple)
|
||
3 , 0 = Cure disease
|
||
3 , 1 = disease (green)
|
||
4 , 1 = blind
|
||
5 , 0 = Cure grey HP
|
||
5 , 1 = HP is grey (not sure what this is)
|
||
*/
|
||
Send(&result);
|
||
|
||
if (isInParty())
|
||
SendPartyStatusUpdate(type, status);
|
||
}
|
||
|
||
/**
|
||
* @brief Gets an item's prototype from a slot in a player's inventory.
|
||
*
|
||
* @param pos The position of the item in the player's inventory.
|
||
* @returns nullptr if an invalid position is specified, or if there's no item at that position.
|
||
* The item's prototype (_ITEM_TABLE *) otherwise.
|
||
*/
|
||
_ITEM_TABLE* CUser::GetItemPrototype(uint8 pos, _ITEM_DATA *& pItem)
|
||
{
|
||
if (pos >= INVENTORY_TOTAL)
|
||
return nullptr;
|
||
|
||
pItem = GetItem(pos);
|
||
return pItem->nNum == 0 ? nullptr : g_pMain->GetItemPtr(pItem->nNum);
|
||
}
|
||
|
||
/**
|
||
* @brief Checks & removes any expired skills from
|
||
* the saved magic list.
|
||
*/
|
||
void CUser::CheckSavedMagic()
|
||
{
|
||
Guard lock(_unitlock);
|
||
if (m_savedMagicMap.empty())
|
||
return;
|
||
|
||
set<uint32> deleteSet;
|
||
BOOST_FOREACH (auto itr, m_savedMagicMap)
|
||
{
|
||
if (itr.second <= UNIXTIME)
|
||
deleteSet.insert(itr.first);
|
||
}
|
||
BOOST_FOREACH (auto itr, deleteSet)
|
||
m_savedMagicMap.erase(itr);
|
||
}
|
||
|
||
/**
|
||
* @brief Inserts a skill to the saved magic list
|
||
* to persist across zone changes/logouts.
|
||
*
|
||
* @param nSkillID Identifier for the skill.
|
||
* @param sDuration The duration.
|
||
*/
|
||
void CUser::InsertSavedMagic(uint32 nSkillID, uint16 sDuration)
|
||
{
|
||
Guard lock(_unitlock);
|
||
UserSavedMagicMap::iterator itr = m_savedMagicMap.find(nSkillID);
|
||
|
||
// If the buff is already in the savedBuffMap there's no need to add it again!
|
||
if (itr != m_savedMagicMap.end())
|
||
return;
|
||
|
||
m_savedMagicMap.insert(make_pair(nSkillID, UNIXTIME + sDuration));
|
||
}
|
||
|
||
/**
|
||
* @brief Removes the specified skill from the saved magic list.
|
||
*
|
||
* @param nSkillID Identifier for the skill.
|
||
*/
|
||
void CUser::RemoveSavedMagic(uint32 nSkillID)
|
||
{
|
||
Guard lock(_unitlock);
|
||
m_savedMagicMap.erase(nSkillID);
|
||
}
|
||
|
||
/**
|
||
* @brief Checks if the given skill ID is already in our
|
||
* saved magic list.
|
||
*
|
||
* @param nSkillID Identifier for the skill.
|
||
*
|
||
* @return true if the skill exists in the saved magic list, false if not.
|
||
*/
|
||
bool CUser::HasSavedMagic(uint32 nSkillID)
|
||
{
|
||
Guard lock(_unitlock);
|
||
return m_savedMagicMap.find(nSkillID) != m_savedMagicMap.end();
|
||
}
|
||
|
||
/**
|
||
* @brief Gets the duration for a saved skill,
|
||
* if applicable.
|
||
*
|
||
* @param nSkillID Identifier for the skill.
|
||
*
|
||
* @return The saved magic duration.
|
||
*/
|
||
int16 CUser::GetSavedMagicDuration(uint32 nSkillID)
|
||
{
|
||
Guard lock(_unitlock);
|
||
auto itr = m_savedMagicMap.find(nSkillID);
|
||
if (itr == m_savedMagicMap.end())
|
||
return 0;
|
||
|
||
return int16(itr->second - UNIXTIME);
|
||
}
|
||
|
||
/**
|
||
* @brief Recasts any saved skills on login/zone change.
|
||
*/
|
||
void CUser::RecastSavedMagic(uint8 buffType /* = 0*/)
|
||
{
|
||
Guard lock(_unitlock);
|
||
UserSavedMagicMap castSet;
|
||
BOOST_FOREACH (auto itr, m_savedMagicMap)
|
||
{
|
||
if (itr.first != 0 || itr.second != 0)
|
||
castSet.insert(make_pair(itr.first, itr.second));
|
||
}
|
||
|
||
if (castSet.empty())
|
||
return;
|
||
|
||
BOOST_FOREACH (auto itr, castSet)
|
||
{
|
||
if (buffType > 0)
|
||
{
|
||
_MAGIC_TYPE4 * pType = g_pMain->m_Magictype4Array.GetData(itr.first);
|
||
|
||
if (pType == nullptr)
|
||
continue;
|
||
|
||
if (pType->bBuffType != buffType)
|
||
continue;
|
||
}
|
||
|
||
MagicInstance instance;
|
||
instance.sCasterID = GetID();
|
||
instance.sTargetID = GetID();
|
||
instance.nSkillID = itr.first;
|
||
instance.bIsRecastingSavedMagic = true;
|
||
|
||
instance.Run();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Recasts any lockable scrolls on debuff.
|
||
*/
|
||
void CUser::RecastLockableScrolls(uint8 buffType)
|
||
{
|
||
InitType4(false, buffType);
|
||
RecastSavedMagic(buffType);
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Displays the player rankings board in PK zones,
|
||
* when left-ALT is held.
|
||
*
|
||
* @param pkt The packet.
|
||
*/
|
||
void CUser::HandlePlayerRankings(Packet & pkt)
|
||
{
|
||
if (g_pMain->m_IsPlayerRankingUpdateProcess)
|
||
return;
|
||
|
||
uint8 nRankType = 0;
|
||
pkt >> nRankType;
|
||
|
||
Packet result(WIZ_RANK, nRankType);
|
||
|
||
uint16 nMyRank = 0;
|
||
uint16 sCount = 0;
|
||
size_t wpos = 0;
|
||
|
||
std::vector<_USER_RANKING> UserRankingSorted[ALL]; // 0 = Karus, 1 = Human and 2 = Both Nations
|
||
|
||
for (int nation = KARUS_ARRAY; nation <= ELMORAD_ARRAY; nation++)
|
||
{
|
||
foreach_stlmap (itr, g_pMain->m_UserRankingArray[nation])
|
||
UserRankingSorted[nRankType == RANK_TYPE_CHAOS_DUNGEON ? ALL - 1 : nation].push_back(*itr->second);
|
||
|
||
if (nRankType == RANK_TYPE_PK_ZONE
|
||
|| nRankType == RANK_TYPE_ZONE_BORDER_DEFENSE_WAR)
|
||
{
|
||
sCount = 0;
|
||
wpos = result.wpos();
|
||
result << sCount;
|
||
|
||
std::sort(UserRankingSorted[nation].begin(), UserRankingSorted[nation].end(),
|
||
[] (_USER_RANKING const &a, _USER_RANKING const &b ){ return a.m_iLoyaltyDaily > b.m_iLoyaltyDaily; });
|
||
|
||
if ((uint32)UserRankingSorted[nation].size() > 0)
|
||
{
|
||
// Get my rank...
|
||
if ((nation + 1) == GetNation())
|
||
{
|
||
for (int i = 0; i < (int32)UserRankingSorted[nation].size(); i++)
|
||
{
|
||
if (GetZoneID() != UserRankingSorted[nation][i].pUser->GetZoneID())
|
||
continue;
|
||
|
||
nMyRank++;
|
||
|
||
if (UserRankingSorted[nation][i].pUser->GetID() == GetID())
|
||
break;
|
||
}
|
||
}
|
||
|
||
for (int i = 0; i < (int32)UserRankingSorted[nation].size(); i++)
|
||
{
|
||
if ((nRankType == RANK_TYPE_PK_ZONE && sCount > 9)
|
||
|| (nRankType == RANK_TYPE_ZONE_BORDER_DEFENSE_WAR && sCount > 7))
|
||
break;
|
||
|
||
_USER_RANKING * pRankInfo = &UserRankingSorted[nation][i];
|
||
|
||
if (pRankInfo == nullptr)
|
||
continue;
|
||
|
||
if (GetZoneID() == pRankInfo->pUser->GetZoneID()
|
||
&& (GetEventRoom() == pRankInfo->pUser->GetEventRoom() || GetEventRoom() == 0))
|
||
{
|
||
CUser *pUser = g_pMain->GetUserPtr(pRankInfo->m_socketID);
|
||
|
||
if (pUser == nullptr)
|
||
continue;
|
||
|
||
if (!pUser->isInGame())
|
||
continue;
|
||
|
||
result << pUser->GetName() << true;
|
||
|
||
CKnights * pKnights = g_pMain->GetClanPtr(pUser->GetClanID());
|
||
|
||
if (pKnights == nullptr)
|
||
result << uint16(0) << uint16(0) << (std::string)"";
|
||
else
|
||
result << pKnights->GetID() << pKnights->m_sMarkVersion << pKnights->GetName();
|
||
|
||
result << pRankInfo->m_iLoyaltyDaily;
|
||
|
||
if(nRankType == RANK_TYPE_PK_ZONE)
|
||
result << pRankInfo->m_iLoyaltyPremiumBonus;
|
||
|
||
sCount++;
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
result.put(wpos, sCount);
|
||
wpos = result.wpos();
|
||
}
|
||
}
|
||
|
||
if (nRankType == RANK_TYPE_CHAOS_DUNGEON && (uint32)UserRankingSorted[ALL-1].size() > 0)
|
||
{
|
||
std::sort(UserRankingSorted[ALL-1].begin(), UserRankingSorted[ALL-1].end(),
|
||
[]( _USER_RANKING const &a, _USER_RANKING const &b ){ return a.m_KillCount != b.m_KillCount ? a.m_KillCount > b.m_KillCount : a.m_DeathCount > b.m_DeathCount; });
|
||
result.DByte();
|
||
// Get Event Room Users count
|
||
result << uint8(g_pMain->TempleEventGetRoomUsers(GetEventRoom()));
|
||
|
||
for (int i = 0; i < (int32)UserRankingSorted[ALL-1].size(); i++)
|
||
{
|
||
_USER_RANKING * pRankInfo = &UserRankingSorted[ALL-1][i];
|
||
|
||
if (pRankInfo == nullptr)
|
||
continue;
|
||
|
||
if (GetSocketID() == pRankInfo->m_socketID)
|
||
continue;
|
||
|
||
if (GetZoneID() == pRankInfo->pUser->GetZoneID()
|
||
&& (GetEventRoom() == pRankInfo->pUser->GetEventRoom() || GetEventRoom() == 0))
|
||
{
|
||
CUser *pUser = g_pMain->GetUserPtr(pRankInfo->m_socketID);
|
||
|
||
if (pUser == nullptr)
|
||
continue;
|
||
|
||
if (!pUser->isInGame())
|
||
continue;
|
||
|
||
result << pUser->GetName()
|
||
<< pUser->m_KillCount << pUser->m_DeathCount;
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
if (nRankType == RANK_TYPE_PK_ZONE)
|
||
result << nMyRank << m_iLoyaltyDaily << m_iLoyaltyPremiumBonus;
|
||
else if (nRankType == RANK_TYPE_ZONE_BORDER_DEFENSE_WAR)
|
||
{
|
||
int32 nChangeExpMax = 0, nChangeExpMin = 0;
|
||
if (GetLevel() > 20)
|
||
nChangeExpMax = (40+m_iLoyaltyDaily)*(GetLevel()-19)*1800;
|
||
else
|
||
nChangeExpMax = 100000;
|
||
|
||
if(GetLevel() > 20)
|
||
nChangeExpMin = ((40+m_iLoyaltyDaily)*(GetLevel()-19)*1260) - (40+m_iLoyaltyDaily);
|
||
else
|
||
nChangeExpMin = 50000;
|
||
|
||
result << int32(nChangeExpMax)/*Win*/ << int32(nChangeExpMin)/*Lose*/;
|
||
|
||
}else if (nRankType == RANK_TYPE_CHAOS_DUNGEON)
|
||
{
|
||
uint64 nGainedExp = uint32(pow(GetLevel(),3) * 0.15 * (5 * ((m_KillCount - m_DeathCount) > 0 ? (m_KillCount - m_DeathCount) : 1)));
|
||
uint64 nPremiumGainedExp = nGainedExp * 2;
|
||
|
||
if (nGainedExp > 8000000)
|
||
nGainedExp = 8000000;
|
||
|
||
if (nPremiumGainedExp > 8000000)
|
||
nPremiumGainedExp = 8000000;
|
||
|
||
|
||
result << GetName()
|
||
<< m_KillCount << m_DeathCount
|
||
<< nGainedExp << nPremiumGainedExp;
|
||
}
|
||
|
||
Send(&result);
|
||
}
|
||
|
||
uint16 CUser::GetPlayerRank(uint8 nRankType)
|
||
{
|
||
uint16 nMyRank = 0;
|
||
uint8 nRankArrayIndex = (nRankType == RANK_TYPE_PK_ZONE
|
||
|| nRankType == RANK_TYPE_ZONE_BORDER_DEFENSE_WAR
|
||
? GetNation() -1
|
||
: ALL-1);
|
||
|
||
std::vector<_USER_RANKING> UserRankingSorted[ALL]; // 0 = Karus, 1 = Human and 2 = Both Nations
|
||
|
||
for (int nation = KARUS_ARRAY; nation <= ELMORAD_ARRAY; nation++)
|
||
{
|
||
foreach_stlmap (itr, g_pMain->m_UserRankingArray[nation])
|
||
UserRankingSorted[nRankType == RANK_TYPE_CHAOS_DUNGEON ? ALL -1 : nation].push_back(*itr->second);
|
||
}
|
||
|
||
if (nRankArrayIndex < ELMORAD)
|
||
{
|
||
std::sort(UserRankingSorted[nRankArrayIndex].begin(), UserRankingSorted[nRankArrayIndex].end(),
|
||
[] (_USER_RANKING const &a, _USER_RANKING const &b ){ return a.m_iLoyaltyDaily > b.m_iLoyaltyDaily; });
|
||
}
|
||
else if (nRankArrayIndex == ELMORAD)
|
||
{
|
||
std::sort(UserRankingSorted[nRankArrayIndex].begin(), UserRankingSorted[nRankArrayIndex].end(),
|
||
[]( _USER_RANKING const &a, _USER_RANKING const &b ){ return a.m_KillCount > b.m_KillCount; });
|
||
}
|
||
|
||
for (int i = 0; i < (int32)UserRankingSorted[nRankArrayIndex].size(); i++)
|
||
{
|
||
_USER_RANKING * pRankInfo = &UserRankingSorted[nRankArrayIndex][i];
|
||
|
||
if (pRankInfo)
|
||
{
|
||
if (GetZoneID() == pRankInfo->pUser->GetZoneID()
|
||
&& GetEventRoom() == pRankInfo->pUser->GetEventRoom())
|
||
{
|
||
|
||
nMyRank++;
|
||
|
||
if (GetSocketID() == pRankInfo->m_socketID)
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
return nMyRank;
|
||
}
|
||
|
||
/**
|
||
* @brief Handles packets related to the mining system.
|
||
* Also handles soccer-related packets (yuck).
|
||
*
|
||
* @param pkt The packet.
|
||
*/
|
||
void CUser::HandleMiningSystem(Packet & pkt)
|
||
{
|
||
uint8 opcode;
|
||
pkt >> opcode;
|
||
|
||
if(isTrading() || m_bMerchantStatex || isMerchanting() || isStoreOpen())
|
||
return;
|
||
|
||
switch (opcode)
|
||
{
|
||
case MiningStart:
|
||
HandleMiningStart(pkt);
|
||
break;
|
||
|
||
case MiningAttempt:
|
||
HandleMiningAttempt(pkt);
|
||
break;
|
||
|
||
case MiningStop:
|
||
HandleMiningStop(pkt);
|
||
break;
|
||
|
||
case FishingStart:
|
||
HandleFishingStart(pkt);
|
||
break;
|
||
|
||
case FishingAttempt:
|
||
HandleFishingAttempt(pkt);
|
||
break;
|
||
|
||
case FishingStop:
|
||
HandleFishingStop(pkt);
|
||
break;
|
||
|
||
case MiningThing:
|
||
HandleMiningThing(pkt);
|
||
break;
|
||
|
||
case MiningSoccer:
|
||
HandleSoccer(pkt);
|
||
break;
|
||
|
||
default:
|
||
TRACE("[SID=%d] Unknown packet %X\n", GetSocketID(), opcode);
|
||
return;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Handles users requesting to start mining.
|
||
* NOTE: This is a mock-up, so be warned that it does not
|
||
* handle checks such as identifying if the user is allowed
|
||
* to mine in this area.
|
||
*
|
||
* @param pkt The packet.
|
||
*/
|
||
void CUser::HandleMiningStart(Packet & pkt)
|
||
{
|
||
Packet result(WIZ_MINING, uint8(MiningStart));
|
||
uint16 resultCode = MiningResultSuccess;
|
||
|
||
|
||
// Are we mining already?
|
||
if (isMining())
|
||
resultCode = MiningResultMiningAlready;
|
||
|
||
// Do we have a pickaxe? Is it worn?
|
||
_ITEM_DATA * pItem;
|
||
_ITEM_TABLE * pTable = GetItemPrototype(RIGHTHAND, pItem);
|
||
if (pItem == nullptr || pTable == nullptr
|
||
|| pItem->sDuration <= 0
|
||
|| !pTable->isPickaxe())
|
||
resultCode = MiningResultNotPickaxe;
|
||
|
||
result << resultCode;
|
||
|
||
// If nothing went wrong, allow the user to start mining.
|
||
// Be sure to let everyone know we're mining.
|
||
if (resultCode == MiningResultSuccess)
|
||
{
|
||
m_bResHpType = USER_MINING;
|
||
m_bMining = true;
|
||
result << GetID();
|
||
SendToRegion(&result,nullptr,GetEventRoom());
|
||
}
|
||
else
|
||
{
|
||
Send(&result);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Handles a user's mining attempt by finding a random reward (or none at all).
|
||
* This is sent automatically by the client every MINING_DELAY (5) seconds.
|
||
*
|
||
* @param pkt The packet.
|
||
*/
|
||
void CUser::HandleMiningAttempt(Packet & pkt)
|
||
{
|
||
if (!isMining())
|
||
return;
|
||
|
||
Packet result(WIZ_MINING, uint8(MiningAttempt));
|
||
uint16 resultCode = MiningResultSuccess;
|
||
|
||
// Do we have a pickaxe? Is it worn?
|
||
_ITEM_DATA * pItem;
|
||
_ITEM_TABLE * pTable = GetItemPrototype(RIGHTHAND, pItem);
|
||
if (pItem == nullptr || pTable == nullptr
|
||
|| pItem->sDuration <= 0 // are we supposed to wear the pickaxe on use? Need to verify.
|
||
|| !pTable->isPickaxe())
|
||
resultCode = MiningResultNotPickaxe;
|
||
|
||
// Check to make sure we're not spamming the packet...
|
||
if ((UNIXTIME - m_tLastMiningAttempt) < MINING_DELAY)
|
||
resultCode = MiningResultMiningAlready; // as close an error as we're going to get...
|
||
|
||
// Effect to show to clients
|
||
uint16 sEffect = 0;
|
||
|
||
// This is just a mock-up based on another codebase's implementation.
|
||
// Need to log official data to get a proper idea of how it behaves, rate-wise,
|
||
// so that we can then implement it more dynamically.
|
||
if (resultCode == MiningResultSuccess) // kazı başarılı ise
|
||
{
|
||
uint32 Random;
|
||
uint32 DropBonus = 0;
|
||
|
||
|
||
if (GetPremiumProperty(PremiumDropPercent) > 0) // premium varsa şansı premium rate kadar yükselt
|
||
{
|
||
DropBonus += (100) * GetPremiumProperty(PremiumDropPercent);
|
||
}
|
||
|
||
if (m_FlashDcBonus > 0 )
|
||
{
|
||
DropBonus += (m_FlashDcBonus * 10);
|
||
}
|
||
|
||
if (pTable->m_iNum == GOLDEN_MATTOCK) // golden mottok kulanıyorsa %2 oranında şansı yükselt
|
||
{
|
||
DropBonus += 200;
|
||
}
|
||
|
||
if (DropBonus > 0 )
|
||
Random = myrand(0, (9999 - DropBonus));
|
||
else
|
||
Random = myrand(0, 9999);
|
||
|
||
while(Random > 9999)
|
||
{
|
||
if (Random > 9999)
|
||
Random -= (2*(Random-9999));
|
||
}
|
||
if (Random < 1)
|
||
Random = 1;
|
||
|
||
|
||
|
||
uint32 nItemID = 0;
|
||
|
||
if(pTable->m_iNum != GOLDEN_MATTOCK)
|
||
nItemID = g_pMain->bRandArrayNormalMattock[Random];
|
||
else
|
||
nItemID = g_pMain->bRandArrayGoldenMattock[Random];
|
||
|
||
if(nItemID == 900001000) // EXP
|
||
{
|
||
ExpChange(1);
|
||
sEffect = 13082; // "XP" effect
|
||
}
|
||
else
|
||
{
|
||
GiveItem(nItemID, 1);
|
||
sEffect = 13081; // "Item" effect
|
||
}
|
||
// Finally, give our item.
|
||
|
||
|
||
|
||
|
||
m_tLastMiningAttempt = UNIXTIME;
|
||
}
|
||
|
||
result << resultCode << GetID() << sEffect;
|
||
|
||
ItemWoreOut(ATTACK,100);
|
||
|
||
if (resultCode != MiningResultSuccess
|
||
&& resultCode != MiningResultNothingFound)
|
||
{
|
||
// Tell us the error first
|
||
Send(&result);
|
||
|
||
// and then tell the client to stop mining
|
||
HandleMiningStop(pkt);
|
||
return;
|
||
}
|
||
|
||
if(resultCode != MiningResultNothingFound)
|
||
SendToRegion(&result,nullptr,GetEventRoom());
|
||
else if(resultCode == MiningResultNothingFound)
|
||
Send(&result);
|
||
}
|
||
/**
|
||
* @brief Handles when a user stops mining.
|
||
*
|
||
* @param pkt The packet.
|
||
*/
|
||
void CUser::HandleMiningStop(Packet & pkt)
|
||
{
|
||
if (!isMining())
|
||
return;
|
||
|
||
Packet result(WIZ_MINING, uint8(MiningStop));
|
||
result << uint16(1) << GetID();
|
||
m_bResHpType = USER_STANDING;
|
||
m_bMining = false;
|
||
SendToRegion(&result,nullptr,GetEventRoom());
|
||
}
|
||
|
||
void CUser::HandleMiningThing(Packet & pkt)
|
||
{
|
||
Packet result(WIZ_MINING, uint8(MiningSoccer));
|
||
uint16 resultCode = MiningResultSuccess;
|
||
|
||
// Are we mining already?
|
||
if (isMining())
|
||
resultCode = MiningResultMiningAlready;
|
||
|
||
result << resultCode;
|
||
|
||
// If nothing went wrong, allow the user to start mining.
|
||
// Be sure to let everyone know we're mining.
|
||
if (resultCode == MiningResultSuccess)
|
||
{
|
||
m_bResHpType = USER_FLASHING;
|
||
m_bMining = true;
|
||
result << GetID();
|
||
SendToRegion(&result,nullptr,GetEventRoom());
|
||
}
|
||
else
|
||
{
|
||
Send(&result);
|
||
}
|
||
}
|
||
|
||
void CUser::HandleSoccer(Packet & pkt)
|
||
{
|
||
|
||
}
|
||
|
||
/// Fish Stard
|
||
void CUser::HandleFishingStart(Packet & pkt)
|
||
{
|
||
Packet result(WIZ_MINING, uint8(FishingStart));
|
||
uint16 resultCode = MiningResultSuccess;
|
||
|
||
// Are we mining already?
|
||
if (isMining())
|
||
resultCode = MiningResultMiningAlready;
|
||
|
||
// Do we have a pickaxe? Is it worn?
|
||
_ITEM_DATA * pItem;
|
||
_ITEM_TABLE * pTable = GetItemPrototype(RIGHTHAND, pItem);
|
||
if (pItem == nullptr || pTable == nullptr
|
||
|| pItem->sDuration <= 0
|
||
|| !pTable->isFishing()
|
||
|| GetItemCount(RAINWORM) <= 0)
|
||
resultCode = MiningResultNotPickaxe;
|
||
|
||
result << resultCode;
|
||
|
||
// If nothing went wrong, allow the user to start mining.
|
||
// Be sure to let everyone know we're mining.
|
||
if (resultCode == MiningResultSuccess)
|
||
{
|
||
m_bResHpType = USER_FLASHING;
|
||
m_bMining = true;
|
||
result << GetID();
|
||
SendToRegion(&result,nullptr,GetEventRoom());
|
||
}
|
||
else
|
||
{
|
||
Send(&result);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Handles a user's mining attempt by finding a random reward (or none at all).
|
||
* This is sent automatically by the client every MINING_DELAY (5) seconds.
|
||
*
|
||
* @param pkt The packet.
|
||
*/
|
||
void CUser::HandleFishingAttempt(Packet & pkt)
|
||
{
|
||
if (!isMining())
|
||
return;
|
||
|
||
Packet result(WIZ_MINING, uint8(FishingAttempt));
|
||
|
||
uint16 resultCode = MiningResultSuccess;
|
||
|
||
// Do we have a pickaxe? Is it worn?
|
||
_ITEM_DATA * pItem;
|
||
_ITEM_TABLE * pTable = GetItemPrototype(RIGHTHAND, pItem);
|
||
if (pItem == nullptr || pTable == nullptr
|
||
|| pItem->sDuration <= 0 // are we supposed to wear the pickaxe on use? Need to verify.
|
||
|| !pTable->isFishing()
|
||
|| (GetItemCount(RAINWORM) <= 0 && pTable->m_iNum != GOLDEN_FISHING))
|
||
resultCode = MiningResultNotPickaxe;
|
||
|
||
// Check to make sure we're not spamming the packet...
|
||
if ((UNIXTIME - m_tLastMiningAttempt) < MINING_DELAY)
|
||
resultCode = MiningResultMiningAlready; // as close an error as we're going to get...
|
||
|
||
// Effect to show to clients
|
||
uint16 sEffect = 0;
|
||
|
||
// This is just a mock-up based on another codebase's implementation.
|
||
// Need to log official data to get a proper idea of how it behaves, rate-wise,
|
||
// so that we can then implement it more dynamically.
|
||
if (resultCode == MiningResultSuccess)
|
||
{
|
||
int rate = myrand(1, 100), random = myrand(1, 10000);
|
||
|
||
if (GetPremiumProperty(PremiumDropPercent) > 0)
|
||
{
|
||
rate += (rate / 100) * GetPremiumProperty(PremiumDropPercent);
|
||
random += (rate / 100) * GetPremiumProperty(PremiumDropPercent);
|
||
}
|
||
|
||
if (pTable->m_iNum == GOLDEN_FISHING)
|
||
{
|
||
rate += (rate / 100) * 10;
|
||
random += (random / 100) * 10;
|
||
}
|
||
|
||
if (rate > 100)
|
||
rate = 100;
|
||
if (random > 10000)
|
||
random = 10000;
|
||
|
||
if (random <= 5000)
|
||
{
|
||
if (GetLevel() >= 35 && GetLevel() <= 59)
|
||
ExpChange(100);
|
||
if (GetLevel() >= 60 && GetLevel() <= 69)
|
||
ExpChange(200);
|
||
if (GetLevel() >= 70)
|
||
ExpChange(300);
|
||
sEffect = 13082; // "XP" effect
|
||
}
|
||
else if (random <= 6500)
|
||
{
|
||
if (pTable->m_iNum == GOLDEN_FISHING)
|
||
GiveItem(g_pMain->f_Drop1);
|
||
if (pTable->m_iNum == FISHING)
|
||
GiveItem(g_pMain->gf_Drop1);
|
||
sEffect = 30730; // "Fish" effect
|
||
}
|
||
else if (random <= 8000)
|
||
{
|
||
if (pTable->m_iNum == GOLDEN_FISHING)
|
||
GiveItem(g_pMain->f_Drop2);
|
||
if (pTable->m_iNum == FISHING)
|
||
GiveItem(g_pMain->gf_Drop2);
|
||
sEffect = 30730; // "Fish" effect
|
||
}
|
||
else if (random <= 10000)
|
||
{
|
||
if (pTable->m_iNum == GOLDEN_FISHING)
|
||
GiveItem(g_pMain->f_Drop3);
|
||
if (pTable->m_iNum == FISHING)
|
||
GiveItem(g_pMain->gf_Drop3);
|
||
sEffect = 30730; // "Fish" effect
|
||
}
|
||
else
|
||
{
|
||
sEffect = 30731; // "Fail" effect
|
||
}
|
||
m_tLastMiningAttempt = UNIXTIME;
|
||
}
|
||
|
||
result << resultCode << GetID() << sEffect;
|
||
|
||
if(pTable == nullptr)
|
||
return;
|
||
|
||
if (pTable->m_iNum != GOLDEN_FISHING)
|
||
RobItem(RAINWORM,1);
|
||
ItemWoreOut(ATTACK,100);
|
||
|
||
if (resultCode != MiningResultSuccess
|
||
&& resultCode != MiningResultNothingFound)
|
||
{
|
||
// Tell us the error first
|
||
Send(&result);
|
||
|
||
// and then tell the client to stop mining
|
||
HandleFishingStop(pkt);
|
||
return;
|
||
}
|
||
|
||
if(resultCode != MiningResultNothingFound)
|
||
SendToRegion(&result,nullptr,GetEventRoom());
|
||
else if(resultCode == MiningResultNothingFound)
|
||
Send(&result);
|
||
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* @brief Handles when a user stops mining.
|
||
*
|
||
* @param pkt The packet.
|
||
*/
|
||
void CUser::HandleFishingStop(Packet & pkt)
|
||
{
|
||
if (!isMining())
|
||
return;
|
||
|
||
Packet result(WIZ_MINING, uint8(FishingStop));
|
||
result << uint16(1) << GetID();
|
||
m_bResHpType = USER_STANDING;
|
||
m_bMining = false;
|
||
SendToRegion(&result,nullptr,GetEventRoom());
|
||
}
|
||
//Stop
|
||
void CUser::InitializeStealth()
|
||
{
|
||
Packet pkt(WIZ_STEALTH);
|
||
pkt << uint8(0) << uint16(0);
|
||
Send(&pkt);
|
||
}
|
||
|
||
void CUser::GrantChickenManner()
|
||
{
|
||
uint8 bLevel = GetLevel(), bManner = 0;
|
||
// No manner points if you're not a chicken anymore nor when you're not in a party.
|
||
if (!m_bIsChicken || !isInParty())
|
||
return;
|
||
|
||
_PARTY_GROUP *pParty = nullptr;
|
||
pParty = g_pMain->GetPartyPtr(GetPartyID());
|
||
|
||
if(pParty == nullptr)
|
||
return;
|
||
|
||
for (int i = 0; i < MAX_PARTY_USERS; i++)
|
||
{
|
||
CUser *pTargetUser = nullptr;
|
||
if (pParty->uid[i] != GetSocketID())
|
||
pTargetUser = g_pMain->GetUserPtr(pParty->uid[i]);
|
||
|
||
if (pTargetUser == nullptr
|
||
|| pTargetUser->isDead()
|
||
|| pTargetUser->m_bIsChicken)
|
||
continue;
|
||
|
||
if (!isInRange(pTargetUser, RANGE_50M))
|
||
continue;
|
||
|
||
if (pTargetUser->GetLevel() > 20 && pTargetUser->GetLevel() < 40)
|
||
bManner = pTargetUser->GetLevel() / 10;
|
||
else
|
||
bManner = 1;
|
||
|
||
pTargetUser->SendMannerChange(bManner);
|
||
}
|
||
}
|
||
|
||
void CUser::SendMannerChange(int32 iMannerPoints)
|
||
{
|
||
//Make sure we don't have too many or too little manner points!
|
||
if(m_iMannerPoint + iMannerPoints > LOYALTY_MAX)
|
||
m_iMannerPoint = LOYALTY_MAX;
|
||
else if (m_iMannerPoint + iMannerPoints < 0)
|
||
m_iMannerPoint = 0;
|
||
else
|
||
m_iMannerPoint += iMannerPoints;
|
||
|
||
Packet pkt(WIZ_LOYALTY_CHANGE, uint8(LOYALTY_MANNER_POINTS));
|
||
pkt << m_iMannerPoint;
|
||
Send(&pkt);
|
||
}
|
||
|
||
uint8 CUser::GetUserDailyOp(uint8 type)
|
||
{
|
||
if (type == 0)
|
||
return 0;
|
||
|
||
int32 nUnixTime = -1;
|
||
|
||
UserDailyOpMap::iterator itr = g_pMain->m_UserDailyOpMap.find(GetName());
|
||
|
||
if (itr != g_pMain->m_UserDailyOpMap.end())
|
||
{
|
||
if (type == DAILY_CHAOS_MAP)
|
||
nUnixTime = int(itr->second->ChaosMapTime);
|
||
else if (type == DAILY_USER_RANK_REWARD)
|
||
nUnixTime = int(itr->second->UserRankRewardTime);
|
||
else if (type == DAILY_USER_PERSONAL_RANK_REWARD)
|
||
nUnixTime = int(itr->second->PersonalRankRewardTime);
|
||
else if (type == DAILY_KING_WING)
|
||
nUnixTime = int(itr->second->KingWingTime);
|
||
else if (type == DAILY_WARDER_KILLER_WING1)
|
||
nUnixTime = int(itr->second->WarderKillerTime1);
|
||
else if (type == DAILY_WARDER_KILLER_WING2)
|
||
nUnixTime = int(itr->second->WarderKillerTime2);
|
||
else if (type == DAILY_KEEPER_KILLER_WING)
|
||
nUnixTime = int(itr->second->KeeperKillerTime);
|
||
else if (type == DAILY_USER_LOYALTY_WING_REWARD)
|
||
nUnixTime = int(itr->second->UserLoyaltyWingRewardTime);
|
||
|
||
if (nUnixTime == -1)
|
||
SetUserDailyOp(type);
|
||
else
|
||
{
|
||
if (((int32(UNIXTIME) - nUnixTime) / 60) > DAILY_OPERATIONS_MINUTE)
|
||
SetUserDailyOp(type);
|
||
else
|
||
return 0;
|
||
}
|
||
}
|
||
else
|
||
SetUserDailyOp(type, true);
|
||
|
||
return 1;
|
||
}
|
||
|
||
void CUser::SetUserDailyOp(uint8 type, bool isInsert)
|
||
{
|
||
if (type == 0)
|
||
return;
|
||
|
||
int32 nUnixTime = int32(UNIXTIME);
|
||
|
||
if (isInsert)
|
||
{
|
||
_USER_DAILY_OP * pData = new _USER_DAILY_OP;
|
||
|
||
pData->strUserId = GetName();
|
||
pData->ChaosMapTime = -1;
|
||
pData->UserRankRewardTime = -1;
|
||
pData->PersonalRankRewardTime = -1;
|
||
pData->KingWingTime = -1;
|
||
pData->WarderKillerTime1 = -1;
|
||
pData->WarderKillerTime2 = -1;
|
||
pData->KeeperKillerTime = -1;
|
||
pData->UserLoyaltyWingRewardTime = -1;
|
||
|
||
if (type == DAILY_CHAOS_MAP)
|
||
pData->ChaosMapTime = nUnixTime;
|
||
else if (type == DAILY_USER_RANK_REWARD)
|
||
pData->UserRankRewardTime = nUnixTime;
|
||
else if (type == DAILY_USER_PERSONAL_RANK_REWARD)
|
||
pData->PersonalRankRewardTime = nUnixTime;
|
||
else if (type == DAILY_KING_WING)
|
||
pData->KingWingTime = nUnixTime;
|
||
else if (type == DAILY_WARDER_KILLER_WING1)
|
||
pData->WarderKillerTime1 = nUnixTime;
|
||
else if (type == DAILY_WARDER_KILLER_WING2)
|
||
pData->WarderKillerTime2 = nUnixTime;
|
||
else if (type == DAILY_KEEPER_KILLER_WING)
|
||
pData->KeeperKillerTime = nUnixTime;
|
||
else if (type == DAILY_USER_LOYALTY_WING_REWARD)
|
||
pData->UserLoyaltyWingRewardTime = nUnixTime;
|
||
|
||
g_pMain->m_UserDailyOpMap.insert(make_pair(pData->strUserId, pData));
|
||
g_DBAgent.InsertUserDailyOp(pData);
|
||
}
|
||
else
|
||
{
|
||
UserDailyOpMap::iterator itr = g_pMain->m_UserDailyOpMap.find(GetName());
|
||
if (itr != g_pMain->m_UserDailyOpMap.end())
|
||
{
|
||
if (type == DAILY_CHAOS_MAP)
|
||
itr->second->ChaosMapTime = nUnixTime;
|
||
else if (type == DAILY_USER_RANK_REWARD)
|
||
itr->second->UserRankRewardTime = nUnixTime;
|
||
else if (type == DAILY_USER_PERSONAL_RANK_REWARD)
|
||
itr->second->PersonalRankRewardTime = nUnixTime;
|
||
else if (type == DAILY_KING_WING)
|
||
itr->second->KingWingTime = nUnixTime;
|
||
else if (type == DAILY_WARDER_KILLER_WING1)
|
||
itr->second->WarderKillerTime1 = nUnixTime;
|
||
else if (type == DAILY_WARDER_KILLER_WING2)
|
||
itr->second->WarderKillerTime2 = nUnixTime;
|
||
else if (type == DAILY_KEEPER_KILLER_WING)
|
||
itr->second->KeeperKillerTime = nUnixTime;
|
||
else if (type == DAILY_USER_LOYALTY_WING_REWARD)
|
||
itr->second->UserLoyaltyWingRewardTime = nUnixTime;
|
||
|
||
g_DBAgent.UpdateUserDailyOp(GetName(), type, nUnixTime);
|
||
}
|
||
}
|
||
}
|
||
|
||
uint32 CUser::GetEventTrigger()
|
||
{
|
||
CNpc *pNpc = g_pMain->GetNpcPtr(GetTargetID());
|
||
if (pNpc == nullptr)
|
||
return 0;
|
||
|
||
foreach_stlmap_nolock (itr, g_pMain->m_EventTriggerArray) {
|
||
_EVENT_TRIGGER *pEventTrigger = g_pMain->m_EventTriggerArray.GetData(itr->first);
|
||
|
||
if (pEventTrigger == nullptr)
|
||
continue;
|
||
|
||
if (pNpc->m_tNpcType != pEventTrigger->bNpcType)
|
||
continue;
|
||
|
||
if (pNpc->m_byTrapNumber == pEventTrigger->sNpcID)
|
||
return pEventTrigger->nTriggerNum;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
void CUser::RemoveStealth()
|
||
{
|
||
if (this->m_bInvisibilityType != INVIS_NONE)
|
||
{
|
||
CMagicProcess::RemoveStealth(this, INVIS_DISPEL_ON_MOVE);
|
||
CMagicProcess::RemoveStealth(this, INVIS_DISPEL_ON_ATTACK);
|
||
}
|
||
}
|
||
|
||
void CUser::GivePremium(uint8 bPremiumType, uint16 sPremiumTime)
|
||
{
|
||
|
||
m_bAccountStatus = 1;
|
||
_PREMIUM_TYPE * pPremium = new _PREMIUM_TYPE;
|
||
_PREMIUM_TYPE * nPremium = PremiumList.GetData(bPremiumType);
|
||
|
||
pPremium->PremiumTime = sPremiumTime * 24;
|
||
pPremium->PremiumType = bPremiumType;
|
||
|
||
if (nPremium == nullptr)
|
||
PremiumList.PutData(pPremium->PremiumType,pPremium);
|
||
else
|
||
nPremium->PremiumTime += sPremiumTime * 24;
|
||
|
||
if (PremiumID == 0)
|
||
PremiumID = bPremiumType;
|
||
|
||
g_DBAgent.SavePremiumServiceUser(this, pPremium);
|
||
SendPremiumInfo();
|
||
}
|
||
|
||
void CUser::PremiumSwitchHandle(Packet & pkt)
|
||
{
|
||
|
||
uint8 ChangePremiumID;
|
||
uint8 opcode = pkt.read<uint8>();
|
||
_PREMIUM_TYPE * pPremium;
|
||
Packet result(WIZ_PREMIUM);
|
||
|
||
switch(opcode)
|
||
{
|
||
case 4:
|
||
ChangePremiumID = pkt.read<uint8>();
|
||
result << uint8(4);
|
||
pPremium = PremiumList.GetData(ChangePremiumID);
|
||
|
||
if (pPremium == nullptr)
|
||
return;
|
||
|
||
if (PremiumChangeTime + PREMIUM_CHANGE_TIME >= UNIXTIME)
|
||
goto failed_return;
|
||
|
||
PremiumChangeTime = UNIXTIME;
|
||
PremiumID = ChangePremiumID;
|
||
g_DBAgent.UpdatePremiumType(PremiumID,GetAccountName());
|
||
|
||
result << PremiumID << int16(1);
|
||
Send(&result);
|
||
return;
|
||
break;
|
||
default:
|
||
printf("PremiumSwitchHandle Handle Unknow : %d\n",opcode);
|
||
break;
|
||
}
|
||
|
||
failed_return:
|
||
result << ChangePremiumID << int16(-1) << uint16(PREMIUM_CHANGE_TIME - (UNIXTIME - PremiumChangeTime));
|
||
Send(&result);
|
||
}
|
||
|
||
void CUser::RobChaosSkillItems()
|
||
{
|
||
if (GetItemCount(ITEM_LIGHT_PIT) > 0)
|
||
RobItem(ITEM_LIGHT_PIT, GetItemCount(ITEM_LIGHT_PIT));
|
||
if (GetItemCount(ITEM_DRAIN_RESTORE) > 0)
|
||
RobItem(ITEM_DRAIN_RESTORE, GetItemCount(ITEM_DRAIN_RESTORE));
|
||
if (GetItemCount(ITEM_KILLING_BLADE) > 0)
|
||
RobItem(ITEM_KILLING_BLADE, GetItemCount(ITEM_KILLING_BLADE));
|
||
}
|
||
|
||
void CUser::SiegeWarFareNpc(Packet & pkt)
|
||
{
|
||
uint8 opcode , type ;
|
||
uint16 tarrif;
|
||
pkt >> opcode >> type >> tarrif;
|
||
_KNIGHTS_SIEGE_WARFARE *pKnightSiegeWarFare = g_pMain->GetSiegeMasterKnightsPtr(1);
|
||
CKnights *pKnight = g_pMain->GetClanPtr(pKnightSiegeWarFare->sMasterKnights);
|
||
|
||
if(pKnightSiegeWarFare == nullptr)
|
||
return;
|
||
|
||
|
||
|
||
Packet result(WIZ_SIEGE);
|
||
switch (opcode)
|
||
{
|
||
case 3: //moradon npc
|
||
{
|
||
result << opcode << type;
|
||
switch (type)
|
||
{
|
||
case 2:
|
||
result << pKnightSiegeWarFare->sCastleIndex
|
||
<< uint16(pKnightSiegeWarFare->bySiegeType)
|
||
<< pKnightSiegeWarFare->byWarDay
|
||
<< pKnightSiegeWarFare->byWarTime
|
||
<< pKnightSiegeWarFare->byWarMinute;
|
||
Send(&result);
|
||
break;
|
||
|
||
case 4:
|
||
result.SByte();
|
||
result
|
||
<< pKnightSiegeWarFare->sCastleIndex
|
||
<< uint8(1)
|
||
<< pKnight->GetName()
|
||
<< pKnight->m_byNation
|
||
<< pKnight->m_sMembers
|
||
<< pKnightSiegeWarFare->byWarRequestDay
|
||
<< pKnightSiegeWarFare->byWarRequestTime
|
||
<< pKnightSiegeWarFare->byWarRequestMinute;
|
||
Send(&result);
|
||
break;
|
||
|
||
case 5:
|
||
result.SByte();
|
||
result
|
||
<< pKnightSiegeWarFare->sCastleIndex
|
||
<< pKnightSiegeWarFare->bySiegeType
|
||
<< pKnight->GetName()
|
||
<< pKnight->m_byNation
|
||
<< pKnight->m_sMembers;
|
||
Send(&result);
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}break;
|
||
|
||
case 4: //delos npc
|
||
{
|
||
|
||
if(pKnight == nullptr
|
||
|| GetClanID() == 0
|
||
|| pKnightSiegeWarFare->sMasterKnights != GetClanID()
|
||
|| !isClanLeader())
|
||
return;
|
||
|
||
C3DMap* TMap = g_pMain->GetZoneByID(ZONE_MORADON);
|
||
C3DMap* TMap2 = g_pMain->GetZoneByID(ZONE_MORADONM2);
|
||
C3DMap* TsMap = g_pMain->GetZoneByID(ZONE_DELOS);
|
||
result << opcode << type;
|
||
switch (type)
|
||
{
|
||
case 2:
|
||
pKnightSiegeWarFare->nDungeonCharge += pKnightSiegeWarFare->nDellosTax + pKnightSiegeWarFare->nMoradonTax;
|
||
pKnightSiegeWarFare->nDellosTax = 0;
|
||
pKnightSiegeWarFare->nMoradonTax = 0;
|
||
GoldGain(pKnightSiegeWarFare->nDungeonCharge,true);
|
||
pKnightSiegeWarFare->nDungeonCharge = 0;
|
||
g_pMain->UpdateSiegeTax(0 , 0);
|
||
break;
|
||
case 3:
|
||
result << pKnightSiegeWarFare->sCastleIndex
|
||
<< pKnightSiegeWarFare->sMoradonTariff
|
||
<< pKnightSiegeWarFare->sDellosTariff
|
||
<< (pKnightSiegeWarFare->nDungeonCharge += pKnightSiegeWarFare->nDellosTax + pKnightSiegeWarFare->nMoradonTax);
|
||
pKnightSiegeWarFare->nDellosTax = 0;
|
||
pKnightSiegeWarFare->nMoradonTax = 0;
|
||
Send(&result);
|
||
break;
|
||
case 4:
|
||
if(tarrif > 20
|
||
|| tarrif < 0)
|
||
tarrif = 20;
|
||
pKnightSiegeWarFare->sMoradonTariff = tarrif;
|
||
TMap->SetTariff(uint8(tarrif));
|
||
TMap2->SetTariff(uint8(tarrif));
|
||
g_pMain->UpdateSiegeTax(ZONE_MORADON , tarrif);
|
||
result << uint16(1) << tarrif << uint8(ZONE_MORADON);
|
||
g_pMain->Send_All(&result);
|
||
g_pMain->m_KnightsSiegeWarfareArray.GetData(pKnightSiegeWarFare->sMasterKnights);
|
||
break;
|
||
case 5:
|
||
if(tarrif > 20
|
||
|| tarrif < 0)
|
||
tarrif = 20;
|
||
pKnightSiegeWarFare->sDellosTariff = tarrif;
|
||
TsMap->SetTariff(uint8(tarrif));
|
||
g_pMain->UpdateSiegeTax(ZONE_DELOS , tarrif);
|
||
result << uint16(1) << tarrif << uint8(ZONE_DELOS);
|
||
g_pMain->Send_All(&result);
|
||
g_pMain->m_KnightsSiegeWarfareArray.GetData(pKnightSiegeWarFare->sMasterKnights);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
void CUser::LogosShout(Packet & pkt)
|
||
{
|
||
|
||
if(isTrading() || isMerchanting() || m_bMerchantStatex || isDead())
|
||
return;
|
||
|
||
if (!CheckExistItem(800075000,1))
|
||
return;
|
||
|
||
uint8 opcode;
|
||
uint32 RGB;
|
||
std::string Notice;
|
||
pkt.SByte();
|
||
pkt >> opcode >> RGB >> Notice;
|
||
|
||
Packet result(WIZ_LOGOSSHOUT);
|
||
result.SByte();
|
||
result << uint8(2) << uint8(1) << RGB << Notice;
|
||
RobItem(800075000,1);
|
||
g_pMain->Send_All(&result);
|
||
|
||
}
|
||
void CUser::HandleGenie(Packet & pkt)
|
||
{
|
||
uint8 command = pkt.read<uint8>();
|
||
|
||
switch (command)
|
||
{
|
||
case 0x01:
|
||
GenieNonAttackProgress(pkt);
|
||
break;
|
||
case 0x02:
|
||
GenieAttackProgress(pkt);
|
||
break;
|
||
default:
|
||
printf("[%s] Genie Non Attack Unknow Handle %d Packet Len [%d]\r\n",GetName().c_str(),command,pkt.size());
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
void CUser::GenieNonAttackProgress(Packet & pkt)
|
||
{
|
||
|
||
enum GenieNonAttackType
|
||
{
|
||
GenieUseSpiringPotion = 1,
|
||
GenieLoadOptions = 0x02,
|
||
GenielSaveOptions = 0x03,
|
||
GenieStartHandle = 0x04,
|
||
GenieStopHandle = 0x05
|
||
};
|
||
|
||
uint8 command = pkt.read<uint8>();
|
||
|
||
switch (command)
|
||
{
|
||
case GenieUseSpiringPotion:
|
||
GenieUseGenieSpirint();
|
||
break;
|
||
case GenieStartHandle:
|
||
GenieStart();
|
||
break;
|
||
case GenielSaveOptions:
|
||
for(int i=0; i<sizeof(m_GenieOptions); i++)
|
||
{
|
||
*(uint8 *)(m_GenieOptions + i ) = pkt.read<uint8>();
|
||
}
|
||
break;
|
||
case GenieLoadOptions:
|
||
{
|
||
Packet result(WIZ_GENIE);
|
||
|
||
result << uint8(0x01) << uint8(2);// << m_GenieOptions;
|
||
|
||
for(int i=0; i<sizeof(m_GenieOptions); i++)
|
||
result << uint8(*(uint8 *)(m_GenieOptions + i));
|
||
|
||
Send(&result);
|
||
}
|
||
break;
|
||
case GenieStopHandle:
|
||
GenieStop();
|
||
break;
|
||
default:
|
||
TRACE("[%s] Genie Non Attack Unknow Handle %d Packet Len [%d]\r\n",GetName().c_str(),command,pkt.size());
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
void CUser::GenieAttackProgress(Packet & pkt)
|
||
{
|
||
enum GenieAttackHandle
|
||
{
|
||
GenieMove = 1,
|
||
GenieSkillSave = 2,
|
||
GenieMainAttack = 3,
|
||
GenieMagic = 4
|
||
};
|
||
|
||
uint8 command = pkt.read<uint8>();
|
||
|
||
if(m_GenieTime == 0)
|
||
GenieStop();
|
||
|
||
switch (command)
|
||
{
|
||
case GenieMove:
|
||
MoveProcess(pkt);
|
||
break;
|
||
case GenieMagic:
|
||
CMagicProcess::MagicPacket(pkt, this);
|
||
break;
|
||
case GenieMainAttack:
|
||
Attack(pkt);
|
||
break;
|
||
default:
|
||
TRACE("[%s] Genie Unknow Attack Handle %d Packet Len[%d]\r\n",GetName().c_str(),command,pkt.size());
|
||
break;
|
||
}
|
||
}
|
||
|
||
void CUser::GenieStart()
|
||
{
|
||
Packet result(WIZ_GENIE);
|
||
|
||
result << uint8(0x01) << uint8(4) << uint16(1) << uint16(m_GenieTime);
|
||
m_bGenieStatus = true;
|
||
UserInOut(INOUT_IN);
|
||
Send(&result);
|
||
}
|
||
|
||
void CUser::GenieStop()
|
||
{
|
||
Packet result(WIZ_GENIE);
|
||
|
||
result << uint8(0x01) << uint8(5) << uint16(1) << uint16(m_GenieTime);
|
||
m_bGenieStatus = false;
|
||
UserInOut(INOUT_IN);
|
||
Send(&result);
|
||
}
|
||
|
||
void CUser::UpdateGenieTime(uint16 m_sTime)
|
||
{
|
||
Packet result(WIZ_GENIE);
|
||
|
||
result << uint8(0x01) << uint8(4) << uint16(1) << uint16(m_GenieTime);
|
||
m_bGenieStatus = true;
|
||
|
||
Send(&result);
|
||
}
|
||
|
||
void CUser::GenieUseGenieSpirint()
|
||
{
|
||
Packet result(WIZ_GENIE);
|
||
|
||
// item check and rob
|
||
uint16 CountA,CountB;
|
||
|
||
CountA = GetItemCount(810305000);
|
||
CountB = GetItemCount(810306000);
|
||
|
||
if(CountA < 1 && CountB < 1)
|
||
return;
|
||
else if(CountA > 0 )
|
||
RobItem(810305000);
|
||
else if(CountB > 0)
|
||
RobItem(810306000);
|
||
|
||
|
||
|
||
m_GenieTime = 120;
|
||
|
||
result << uint8(1) << uint8(1) << GetGenieTime();
|
||
|
||
Send(&result);
|
||
|
||
}
|
||
|
||
void CUser::GenieInfo()
|
||
{
|
||
Packet result(WIZ_GENIE);
|
||
result << uint8(1) << uint8(1) << GetGenieTime();
|
||
Send(&result);
|
||
}
|
||
|
||
void CUser::ExpFlash()
|
||
{
|
||
if (GetLevel() > 79)
|
||
return;
|
||
|
||
if(PremiumID == 11)
|
||
{
|
||
if(m_FlashExpBonus == 1)
|
||
m_FlashExpBonus = 0;
|
||
|
||
m_FlashExpBonus += 10;
|
||
|
||
if(m_FlashExpBonus > 80)
|
||
m_FlashExpBonus = 80;
|
||
|
||
SendNotice();
|
||
}
|
||
else
|
||
{
|
||
|
||
if(m_FlashExpBonus == 1)
|
||
m_FlashExpBonus = 0;
|
||
|
||
m_FlashExpBonus += 10;
|
||
|
||
if(m_FlashExpBonus > 40)
|
||
m_FlashExpBonus = 40;
|
||
|
||
SendNotice();
|
||
|
||
}
|
||
}
|
||
|
||
void CUser::DcFlash()
|
||
{
|
||
if(PremiumID == 10)
|
||
{
|
||
if(m_FlashDcBonus == 1)
|
||
m_FlashDcBonus = 0;
|
||
|
||
m_FlashDcBonus += 10;
|
||
|
||
if(m_FlashDcBonus > 100)
|
||
m_FlashDcBonus = 100;
|
||
|
||
SendNotice();
|
||
}
|
||
}
|
||
|
||
void CUser::WarFlash()
|
||
{
|
||
if(PremiumID == 12)
|
||
{
|
||
if(m_FlashWarBonus == 1)
|
||
m_FlashWarBonus = 0;
|
||
|
||
m_FlashWarBonus += 10;
|
||
|
||
if(m_FlashWarBonus > 100)
|
||
m_FlashWarBonus = 100;
|
||
|
||
SendNotice();
|
||
}
|
||
}
|
||
void CUser::GoldEvent(uint32 gold)
|
||
{
|
||
// Assuming it works like this, although this affects (probably) all gold gained (including kills in PvP zones)
|
||
// If this is wrong and it should ONLY affect gold gained from monsters, let us know!
|
||
if (ITEM_GOLD)
|
||
{
|
||
uint16 m_bGoldEvent = 0;
|
||
int rand = 0;
|
||
rand = myrand(1,10000);
|
||
|
||
if(rand > g_pMain->m_byGoldTotal)
|
||
return;
|
||
|
||
if(rand > 0 && rand < g_pMain->m_byGoldx2)
|
||
rand = 2;
|
||
else if(rand >= g_pMain->m_byGoldx2 && rand < g_pMain->m_byGoldx10)
|
||
rand = 10;
|
||
else if(rand >= g_pMain->m_byGoldx10 && rand < g_pMain->m_byGoldx50)
|
||
rand = 50;
|
||
else if(rand >= g_pMain->m_byGoldx50 && rand < g_pMain->m_byGoldx100)
|
||
rand = 100;
|
||
else if(rand >= g_pMain->m_byGoldx100 && rand < g_pMain->m_byGoldx500)
|
||
rand = 500;
|
||
else if(rand >= g_pMain->m_byGoldx500 && rand < (g_pMain->m_byGoldTotal + 1))
|
||
rand = 1000;
|
||
|
||
m_bGoldEvent = rand;
|
||
Packet result(WIZ_GOLD_CHANGE,uint8(CoinEvent));
|
||
result.SByte();
|
||
result << uint16(740) << uint16(0) << uint32(0) << m_bGoldEvent << GetID();
|
||
SendToRegion(&result,nullptr,GetEventRoom());
|
||
gold = gold * (rand);
|
||
GoldGain(gold,true,false);
|
||
|
||
}
|
||
}
|
||
|
||
void CUser::ExpEvent(int64 exp)
|
||
{
|
||
int rand = 0;
|
||
int64 m_bExpEvent = 0;
|
||
rand = myrand(1,10000);
|
||
|
||
if(rand > g_pMain->m_byExpTotal)
|
||
return;
|
||
|
||
if(rand > 0 && rand <= g_pMain->m_byExpx2)
|
||
rand = 2;
|
||
else if(rand >= g_pMain->m_byExpx2 && rand <= g_pMain->m_byExpx10)
|
||
rand = 10;
|
||
else if(rand >= g_pMain->m_byExpx10 && rand <= g_pMain->m_byExpx50)
|
||
rand = 50;
|
||
else if(rand >= g_pMain->m_byExpx50 && rand <= g_pMain->m_byExpx100)
|
||
rand = 100;
|
||
else if(rand >= g_pMain->m_byExpx100 && rand <= g_pMain->m_byExpx500)
|
||
rand = 500;
|
||
else if(rand >= g_pMain->m_byExpx500 && rand <= g_pMain->m_byExpx1000)
|
||
rand = 1000;
|
||
|
||
|
||
|
||
m_bExpEvent = rand * exp;
|
||
|
||
Packet result(WIZ_EXP_CHANGE,uint8(2));
|
||
result << GetID() << rand << m_iExp;
|
||
SendToRegion(&result,nullptr,GetEventRoom());
|
||
ExpChange(m_bExpEvent);
|
||
}
|
||
void CUser::RemoveRegionChat()
|
||
{
|
||
Packet result(WIZ_USER_INFO,uint8(0x04));
|
||
result.SByte();
|
||
result<<GetName();
|
||
g_pMain->Send_Zone(&result,GetZoneID(),nullptr,Nation::ALL,GetEventRoom());
|
||
}
|
||
|
||
void CUser::HandleUserInfo(Packet & pkt)
|
||
{
|
||
Packet result(WIZ_USER_INFO);
|
||
uint16 sCount = 0;
|
||
|
||
uint8 command;
|
||
|
||
pkt >> command;
|
||
if(command == 0x02)
|
||
{
|
||
result << uint8(0x02) << uint8(0x01);
|
||
|
||
std::string strCharName;
|
||
pkt.SByte();
|
||
pkt >> strCharName;
|
||
CUser* pUser = g_pMain->GetUserPtr(strCharName, TYPE_CHARACTER);
|
||
|
||
if(pUser == nullptr)
|
||
{
|
||
CBot * pBot = g_pMain->GetBotPtr(strCharName, TYPE_CHARACTER);
|
||
if(pBot == nullptr)
|
||
return;
|
||
else if(pBot->isInGame())
|
||
{
|
||
result.SByte();
|
||
result << pBot->GetName() << pBot->m_bLevel << pBot->m_sClass << uint32(100) << pBot->GetMonthlyLoyalty() << uint8(1);
|
||
|
||
CKnights * pKnights = g_pMain->GetClanPtr(pBot->GetClanID());
|
||
if (pKnights == nullptr)
|
||
result << uint16(0) << uint16(0) << uint16(0) << uint16(0);
|
||
else
|
||
result << pBot->GetClanID() << pKnights->m_sMarkVersion << pKnights->m_byFlag << pKnights->m_byGrade << pKnights->m_strName << pKnights->m_strChief;
|
||
result << pBot->GetRebLevel();
|
||
Send(&result);
|
||
|
||
Packet result2(WIZ_ITEM_UPGRADE, uint8(ITEM_CHARACTER_SEAL)); // User Inventory View,By Terry.17.04.02..
|
||
result2.SByte();
|
||
result2 << uint8(0x04) << uint8(1)
|
||
<< pBot->GetName()
|
||
<< pBot->GetNation()
|
||
<< pBot->GetRace()
|
||
<< pBot->GetClass()
|
||
<< pBot->GetLevel()
|
||
<< pBot->GetStat(STAT_STR)
|
||
<< pBot->GetStat(STAT_STA)
|
||
<< pBot->GetStat(STAT_DEX)
|
||
<< pBot->GetStat(STAT_INT)
|
||
<< pBot->GetStat(STAT_CHA)
|
||
<< pBot->GetCoins()
|
||
<< uint16(0) << uint16(0) << uint8(0);
|
||
result2 << pBot->m_bstrSkill[SkillPointCat1] << pBot->m_bstrSkill[SkillPointCat2] << pBot->m_bstrSkill[SkillPointCat3] << pBot->m_bstrSkill[SkillPointMaster];
|
||
|
||
for (int i = 0; i < SLOT_MAX + HAVE_MAX; i++)
|
||
{
|
||
_ITEM_DATA * pItem = pBot->GetItem(i);
|
||
|
||
result2 << pItem->nNum << pItem->sDuration << pItem->sCount << pItem->bFlag ;
|
||
}
|
||
Send(&result2);
|
||
|
||
return;
|
||
|
||
}
|
||
else
|
||
return;
|
||
}
|
||
|
||
result.SByte();
|
||
result << pUser->GetName() << pUser->GetLevel() << pUser->GetClass() << pUser->GetLoyalty() << pUser->GetMonthlyLoyalty() << uint8(1);
|
||
|
||
CKnights * pKnights = g_pMain->GetClanPtr(pUser->GetClanID());
|
||
if (pKnights == nullptr)
|
||
result << uint16(0) << uint16(0) << uint16(0) << uint16(0) ;
|
||
else
|
||
result << pUser->GetClanID() << pKnights->m_sMarkVersion << pKnights->m_byFlag << pKnights->m_byGrade << pKnights->m_strName << pKnights->m_strChief;
|
||
result << pUser->GetRebLevel();
|
||
|
||
Send(&result);
|
||
|
||
Packet result2(WIZ_ITEM_UPGRADE, uint8(ITEM_CHARACTER_SEAL)); // User Inventory View,By Terry.17.04.02..
|
||
result2.SByte();
|
||
result2 << uint8(0x04) << uint8(1)
|
||
<< pUser->GetName()
|
||
<< pUser->GetNation()
|
||
<< pUser->GetRace()
|
||
<< pUser->GetClass()
|
||
<< pUser->GetLevel()
|
||
<< pUser->GetStat(STAT_STR)
|
||
<< pUser->GetStat(STAT_STA)
|
||
<< pUser->GetStat(STAT_DEX)
|
||
<< pUser->GetStat(STAT_INT)
|
||
<< pUser->GetStat(STAT_CHA)
|
||
<< pUser->GetCoins()
|
||
<< uint16(0) << uint16(0) << uint8(0);
|
||
result2 << pUser->m_bstrSkill[SkillPointCat1] << pUser->m_bstrSkill[SkillPointCat2] << pUser->m_bstrSkill[SkillPointCat3] << pUser->m_bstrSkill[SkillPointMaster];
|
||
|
||
for (int i = 0; i < SLOT_MAX + HAVE_MAX; i++)
|
||
{
|
||
_ITEM_DATA * pItem = pUser->GetItem(i);
|
||
|
||
result2 << pItem->nNum << pItem->sDuration << pItem->sCount << pItem->bFlag ;
|
||
}
|
||
Send(&result2);
|
||
|
||
}
|
||
if( command == 0x04 )
|
||
RemoveRegionChat();
|
||
if(command == 0x03 || command == 0x01 )
|
||
{
|
||
result << uint8(command) << uint8(0x01) << GetZoneID() << uint8(0) << sCount;
|
||
for(int i=0; i< MAX_USER; i++)
|
||
{
|
||
CUser *pUser = g_pMain->GetUserPtr(i);
|
||
if (pUser == nullptr
|
||
|| !pUser->isInGame()
|
||
|| pUser->GetZoneID() == ZONE_CHAOS_DUNGEON
|
||
|| pUser->GetZoneID() != GetZoneID()
|
||
|| pUser->isGM()
|
||
|| (pUser->GetEventRoom() != GetEventRoom() && GetEventRoom() > 0))
|
||
continue;
|
||
result.SByte();
|
||
result << pUser->GetName()
|
||
<< pUser->GetNation()
|
||
<< uint16(0x01)
|
||
<< pUser->GetSPosX()
|
||
<< pUser->GetSPosZ()
|
||
<< pUser->GetClanID();
|
||
|
||
CKnights * pKnights = g_pMain->GetClanPtr(pUser->GetClanID());
|
||
if (pKnights == nullptr)
|
||
result << uint16(0) << uint16(0);
|
||
else
|
||
result << pKnights->m_sMarkVersion << pKnights->m_byFlag << pKnights->m_byGrade;
|
||
|
||
result << uint16(4);
|
||
|
||
sCount++;
|
||
}
|
||
|
||
foreach_stlmap(itr,g_pMain->m_arBotArray)
|
||
{
|
||
CBot *pBot = itr->second;
|
||
if (pBot == nullptr
|
||
|| !pBot->isInGame()
|
||
|| pBot->m_bZone == ZONE_CHAOS_DUNGEON
|
||
|| pBot->m_bZone != GetZoneID())
|
||
continue;
|
||
result.SByte();
|
||
result << pBot->GetName()
|
||
<< pBot->m_bNation
|
||
<< uint16(0x01)
|
||
<< pBot->GetSPosX()
|
||
<< pBot->GetSPosZ()
|
||
<< pBot->GetClanID();
|
||
|
||
CKnights * pKnights = g_pMain->GetClanPtr(pBot->GetClanID());
|
||
if (pKnights == nullptr)
|
||
result << uint16(0) << uint16(0);
|
||
else
|
||
result << pKnights->m_sMarkVersion << pKnights->m_byFlag << pKnights->m_byGrade;
|
||
|
||
result << uint16(4);
|
||
|
||
sCount++;
|
||
|
||
}
|
||
|
||
result.put(4, sCount);
|
||
Send(&result);
|
||
}
|
||
|
||
}
|
||
void CUser::HandleUserInfoShow(Packet & pkt)
|
||
{
|
||
Packet result(WIZ_USER_INFO,uint8(UserInfoAll));
|
||
|
||
result
|
||
|
||
<< GetName()
|
||
<<uint16(0);
|
||
|
||
Send(&result);
|
||
|
||
}
|
||
void CUser::HandleUserInfoNick(Packet & pkt)
|
||
{
|
||
Packet result(WIZ_USER_INFO,uint8(UserInfoNick));
|
||
|
||
result
|
||
<<uint8(1)
|
||
<< uint8(KNIGHTS_WAR_SURRENDER)
|
||
<<uint8(0)
|
||
<< GetName()
|
||
<<uint16(0)
|
||
<<uint16(0);
|
||
Send(&result);
|
||
|
||
}
|
||
|
||
void CUser::HandleUserInfoDetail(Packet & pkt)
|
||
{
|
||
Packet result(WIZ_USER_INFO, uint8(UserInfo));
|
||
result.SByte();
|
||
result
|
||
<< GetName()
|
||
<< GetLevel()
|
||
<< m_sClass
|
||
<< uint16(0) //Status
|
||
<< GetLoyalty()
|
||
<< GetClanID()
|
||
<< GetFame();
|
||
Send(&result);
|
||
}
|
||
|
||
void CUser::PetSkill(uint64 nSerial)
|
||
{
|
||
CPet * newPet = g_pMain->GetPetPtr(nSerial);
|
||
|
||
if(newPet != nullptr)
|
||
newPet->SummonPet(this);
|
||
|
||
}
|
||
|
||
void CUser::DupeItemsDelete()
|
||
{
|
||
|
||
std::string sNoticeMessage;
|
||
bool iFindItem = false;
|
||
|
||
for(int j = 0; j < INVENTORY_TOTAL; j++)
|
||
{
|
||
if(GetItem(j)->nNum == 0)
|
||
continue;
|
||
|
||
std::string strUserID, strAccountID;
|
||
_ITEM_DUPER * pDupers = nullptr;
|
||
|
||
foreach_stlmap (itr, g_pMain->m_ItemDupersArray)
|
||
{
|
||
pDupers = itr->second;
|
||
|
||
if (pDupers->d_ItemID != GetItem(j)->nNum)
|
||
continue;
|
||
|
||
if((pDupers->d_Serial == (GetItem(j)->nSerialNum)))
|
||
{
|
||
TRACE("Item Deleted pos(%d), ItewmID: %d\n", j, GetItem(j)->nNum);
|
||
m_sItemArray[j].nNum = 0;
|
||
m_sItemArray[j].sCount = 0;
|
||
m_sItemArray[j].sDuration = 0;
|
||
m_sItemArray[j].nSerialNum = 0;
|
||
m_sItemArray[j].bFlag = 0;
|
||
m_sItemArray[j].sRemainingRentalTime = 0;
|
||
m_sItemArray[j].nExpirationTime = 0;
|
||
m_sItemArray[j].IsSelling = false;
|
||
SendItemMove(2);
|
||
iFindItem = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
if(iFindItem)
|
||
{
|
||
sNoticeMessage = string_format("Dupe items were deleted from your inventory : User = %s .", GetName().c_str());
|
||
|
||
if (!sNoticeMessage.empty())
|
||
g_pMain->SendNotice(sNoticeMessage.c_str(),Nation::ALL);
|
||
Disconnect();
|
||
}
|
||
}
|
||
|
||
void CUser::IllegalItemsBanned()
|
||
{
|
||
//Ilegal Item Banned
|
||
std::string sNoticeMessage;
|
||
bool iFindItem = false;
|
||
|
||
for(int e = 0; e < INVENTORY_TOTAL; e++)
|
||
{
|
||
if(GetItem(e)->nNum == 0)
|
||
continue;
|
||
|
||
_ITEM_TABLE * pItem= nullptr;
|
||
_ILEGAL_ITEMS * pIlegal = nullptr;
|
||
|
||
foreach_stlmap (itr, g_pMain->m_IlegalItemsArray)
|
||
{
|
||
pIlegal = itr->second;
|
||
|
||
if(!pIlegal)
|
||
continue;
|
||
|
||
if (pIlegal->i_ItemID != GetItem(e)->nNum)
|
||
continue;
|
||
|
||
if (isGM())
|
||
continue;
|
||
|
||
if (pIlegal->i_ItemID == GetItem(e)->nNum)
|
||
{
|
||
pItem= nullptr;
|
||
|
||
pItem = g_pMain->m_ItemtableArray.GetData(GetItem(e)->nNum);
|
||
|
||
if(!pItem)
|
||
continue;
|
||
|
||
sNoticeMessage = string_format("%s have illegal item (%s).", GetName().c_str(), pItem->m_sName.c_str());
|
||
if (!sNoticeMessage.empty())
|
||
g_pMain->SendNotice(sNoticeMessage.c_str(),Nation::ALL);
|
||
|
||
g_pMain->WriteCheatLogFile(string_format("%s is currently blocked for item (%s | %d) in illegal activity.\n", GetName().c_str(), pItem->m_sName.c_str(), pItem->m_iNum));
|
||
iFindItem = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (iFindItem)
|
||
{
|
||
sNoticeMessage = string_format("%s is currently blocked for illegal activity.", GetName().c_str());
|
||
if (!sNoticeMessage.empty())
|
||
g_pMain->SendNotice(sNoticeMessage.c_str(),Nation::ALL);
|
||
|
||
m_bAuthority = AUTHORITY_BANNED;
|
||
Disconnect();
|
||
}
|
||
}
|
||
|
||
void CUser::ReportedUsers()
|
||
{
|
||
Packet result(WIZ_REPORT);
|
||
|
||
uint8 opcode = result.read<uint8>();
|
||
|
||
switch (opcode)
|
||
{
|
||
case 1:
|
||
|
||
break;
|
||
|
||
case 2:
|
||
|
||
break;
|
||
}
|
||
|
||
} |