void CUser::UserInOut(uint8 bType) { if (GetRegion() == nullptr) return; Packet result; if (bType != INOUT_OUT) ResetGMVisibility(); GetInOut(result, bType); if (bType == INOUT_OUT) GetRegion()->Remove(this); else GetRegion()->Add(this); SendToRegion(&result, this, GetEventRoom()); if (bType == INOUT_OUT || !isBlinking()) { result.Initialize(AG_USER_INOUT); result.SByte(); result << bType << GetSocketID() << GetName() << m_curx << m_curz; Send_AIServer(&result); } }
void CUser::MoveProcess(Packet & pkt) { ASSERT(GetMap() != nullptr); if (m_bWarp || isDead()) return; uint16 will_x, will_z, will_y; int16 speed=0; float real_x, real_z, real_y; uint8 echo; pkt >> will_x >> will_z >> will_y >> speed >> echo; real_x = will_x/10.0f; real_z = will_z/10.0f; real_y = will_y/10.0f; if (!isGM()) { // TO-DO: Handle proper speed checks against server-side amounts. // We should also avoid relying on what the client has sent us. if (speed > 200) // What is the signifance of this number? Considering 90 is light feet... // We shouldn't be using magic numbers at all here. { Disconnect(); return; } } if (!GetMap()->IsValidPosition(real_x, real_z, real_y)) return; if (m_oldx != GetX() || m_oldz != GetZ()) { m_oldx = GetX(); m_oldy = GetY(); m_oldz = GetZ(); } // TO-DO: Ensure this is checked properly to prevent speedhacking SetPosition(real_x, real_y, real_z); if (RegisterRegion()) { g_pMain->RegionNpcInfoForMe(this); g_pMain->RegionUserInOutForMe(this); g_pMain->MerchantUserInOutForMe(this); } if (m_bInvisibilityType == INVIS_DISPEL_ON_MOVE) CMagicProcess::RemoveStealth(this, INVIS_DISPEL_ON_MOVE); Packet result(WIZ_MOVE); result << GetSocketID() << will_x << will_z << will_y << speed << echo; SendToRegion(&result); GetMap()->CheckEvent(real_x, real_z, this); result.Initialize(AG_USER_MOVE); result << GetSocketID() << m_curx << m_curz << m_cury << speed; Send_AIServer(&result); }
void CUser::Rotate(Packet & pkt) { if (isDead()) return; Packet result(WIZ_ROTATE); pkt >> m_sDirection; result << GetSocketID() << m_sDirection; SendToRegion(&result, this); }
/** * @brief Sends the movement packet for the NPC. * * @param fPosX The position x coordinate. * @param fPosY The position y coordinate. * @param fPosZ The position z coordinate. * @param fSpeed The speed. */ void CNpc::MoveResult(float fPosX, float fPosY, float fPosZ, float fSpeed) { Packet result(WIZ_NPC_MOVE); SetPosition(fPosX, fPosY, fPosZ); RegisterRegion(); result << GetID() << GetSPosX() << GetSPosZ() << GetSPosY() << uint16(fSpeed * 10); SendToRegion(&result); }
void CUser::MerchantClose() { if (!isMerchanting()) return; m_bMerchantState = MERCHANT_STATE_NONE; GiveMerchantItems(); // Give back to the user that which hasn't been sold, if any. Packet result(WIZ_MERCHANT, uint8(MERCHANT_CLOSE)); result << GetSocketID(); SendToRegion(&result); }
void CUser::BuyingMerchantInsertRegion() { Packet result(WIZ_MERCHANT, uint8(MERCHANT_BUY_REGION_INSERT)); result << GetSocketID(); for (int i = 0; i < 4; i++) { result << m_arMerchantItems[i].nNum; } SendToRegion(&result); }
void CUser::BuyingMerchantClose() { if (isMerchanting()) m_bMerchantState = MERCHANT_STATE_NONE; else if (m_sMerchantsSocketID >= 0) RemoveFromMerchantLookers(); else return; Packet result(WIZ_MERCHANT, uint8(MERCHANT_BUY_CLOSE)); result << GetSocketID(); SendToRegion(&result); }
void CUser::MoveProcess(Packet & pkt) { ASSERT(GetMap() != nullptr); if (m_bWarp || isDead()) return; uint16 will_x, will_z, will_y; int16 speed=0; float real_x, real_z, real_y; uint8 echo; pkt >> will_x >> will_z >> will_y >> speed >> echo; real_x = will_x/10.0f; real_z = will_z/10.0f; real_y = will_y/10.0f; m_sSpeed = speed; SpeedHackUser(); if (!GetMap()->IsValidPosition(real_x, real_z, real_y)) return; if (m_oldx != GetX() || m_oldz != GetZ()) { m_oldx = GetX(); m_oldy = GetY(); m_oldz = GetZ(); } // TODO: Ensure this is checked properly to prevent speedhacking SetPosition(real_x, real_y, real_z); if (RegisterRegion()) { g_pMain->RegionNpcInfoForMe(this); g_pMain->RegionUserInOutForMe(this); g_pMain->MerchantUserInOutForMe(this); } if (m_bInvisibilityType == INVIS_DISPEL_ON_MOVE) CMagicProcess::RemoveStealth(this, INVIS_DISPEL_ON_MOVE); Packet result(WIZ_MOVE); result << GetSocketID() << will_x << will_z << will_y << speed << echo; SendToRegion(&result); GetMap()->CheckEvent(real_x, real_z, this); result.Initialize(AG_USER_MOVE); result << GetSocketID() << m_curx << m_curz << m_cury << speed; Send_AIServer(&result); }
void CUser::MerchantInsert(Packet & pkt) { string advertMessage; // check here maybe to make sure they're not using it otherwise? pkt >> advertMessage; if (advertMessage.size() > MAX_MERCH_MESSAGE) return; m_bMerchantState = MERCHANT_STATE_SELLING; Packet result(WIZ_MERCHANT, uint8(MERCHANT_INSERT)); result << uint16(1) << advertMessage << GetSocketID() << m_bPremiumMerchant; for (int i = 0; i < MAX_MERCH_ITEMS; i++) result << m_arMerchantItems[i].nNum; SendToRegion(&result); }
void CUser::MerchantInsert(Packet & pkt) { std::string advertMessage; pkt >> advertMessage; if (advertMessage.empty() || advertMessage.size() >= MAX_MERCH_MESSAGE) return; m_bIsMerchanting = true; TakeMerchantItems(); // Removing the items from the user's inventory Packet result(WIZ_MERCHANT, uint8(MERCHANT_INSERT)); result << uint16(1) << advertMessage << GetSocketID() << uint8(0); // 0 is for "normal" merchant mode, 1 for "premium" merchant mode. Send "normal" until we have support for the skills. for (int i = 0; i < MAX_MERCH_ITEMS; i++) result << uint32(m_arSellingItems[i].nNum); SendToRegion(&result); }
/** * @brief Sends a gate status. * * @param ObjectType object type * @param bFlag The flag (open or shut). * @param bSendAI true to update the AI server. */ void CNpc::SendGateFlag(uint8 objectType,uint8 bFlag /*= -1*/, bool bSendAI /*= true*/) { Packet result(WIZ_OBJECT_EVENT, uint8(objectType)); // If there's a flag to set, set it now. if (bFlag >= 0) m_byGateOpen = (bFlag == 1); // Tell everyone nearby our new status. result << uint8(1) << GetID() << m_byGateOpen; SendToRegion(&result); // Tell the AI server our new status if (bSendAI) { result.Initialize(AG_NPC_GATE_OPEN); result << GetID() << m_byGateOpen; Send_AIServer(&result); } }
void CUser::MoveProcess(Packet & pkt) { ASSERT(GetMap() != NULL); if (m_bWarp || isDead()) return; uint16 will_x, will_z, will_y, speed=0; float real_x, real_z, real_y; uint8 echo; pkt >> will_x >> will_z >> will_y >> speed >> echo; real_x = will_x/10.0f; real_z = will_z/10.0f; real_y = will_y/10.0f; if (!GetMap()->IsValidPosition(real_x, real_z, real_y)) return; // TO-DO: Ensure this is checked properly to prevent speedhacking m_curx = real_x; m_curz = real_z; m_cury = real_y; if (RegisterRegion()) { g_pMain->RegionNpcInfoForMe(this); g_pMain->RegionUserInOutForMe(this); g_pMain->MerchantUserInOutForMe(this); } if (m_bInvisibilityType == INVIS_DISPEL_ON_MOVE) StateChangeServerDirect(7, INVIS_NONE); Packet result(WIZ_MOVE); result << GetSocketID() << will_x << will_z << will_y << speed << echo; SendToRegion(&result); GetMap()->CheckEvent(real_x, real_z, this); result.Initialize(AG_USER_MOVE); result << GetSocketID() << m_curx << m_curz << m_cury << speed; Send_AIServer(&result); }
void CUser::UserInOut(uint8 bType) { if (GetRegion() == NULL) return; Packet result; GetInOut(result, bType); if (bType == INOUT_OUT) GetRegion()->Remove(this); else GetRegion()->Add(this); SendToRegion(&result, this); if (bType == INOUT_OUT || !isBlinking()) { result.Initialize(AG_USER_INOUT); result.SByte(); result << bType << GetSocketID() << m_pUserData.m_id << m_pUserData.m_curx << m_pUserData.m_curz; g_pMain->Send_AIServer(&result); } }
/** * @brief Constructs and sends an in/out packet for the NPC. * * @param bType The type (in or out). * @param fX The x coordinate. * @param fZ The z coordinate. * @param fY The y coordinate. */ void CNpc::SendInOut(uint8 bType, float fX, float fZ, float fY) { if (GetRegion() == nullptr) { SetRegion(GetNewRegionX(), GetNewRegionZ()); if (GetRegion() == nullptr) return; } if (bType == INOUT_OUT) { GetRegion()->Remove(this); } else { GetRegion()->Add(this); SetPosition(fX, fY, fZ); } Packet result; GetInOut(result, bType); SendToRegion(&result); }
void CUser::Attack(Packet & pkt) { int16 sid = -1, tid = -1, damage, delaytime, distance; uint8 bType, bResult = 0; Unit * pTarget = nullptr; pkt >> bType >> bResult >> tid >> delaytime >> distance; // delaytime = delaytime / 100.0f; // distance = distance / 10.0f; if (isIncapacitated()) return; if (isInSafetyArea()) return; if (m_bInvisibilityType != INVIS_NONE) { CMagicProcess::RemoveStealth(this, INVIS_DISPEL_ON_MOVE); CMagicProcess::RemoveStealth(this, INVIS_DISPEL_ON_ATTACK); } // If you're holding a weapon, do a client-based (ugh, do not trust!) delay check. _ITEM_TABLE *pTable = GetItemPrototype(RIGHTHAND); if (pTable != nullptr) { if (delaytime < (pTable->m_sDelay + 10) // client adds 0.1 onto the interval (0.1 of 100 is 10) || distance > pTable->m_sRange) return; } // Empty handed. else if (delaytime < 100) return; pTarget = g_pMain->GetUnitPtr(tid); bResult = ATTACK_FAIL; if (pTarget != nullptr && isInAttackRange(pTarget) && CanAttack(pTarget)) { if (isAttackable(pTarget) && CanCastRHit(GetSocketID())) { if (isInTempleEventZone()) if (GetUserGroup() != -1 && !isSameUserGroup(pTarget)) return; CUser *pUser = g_pMain->GetUserPtr(GetSocketID()); if (pUser != nullptr) pUser->m_RHitRepeatList.insert(std::make_pair(GetSocketID(), UNIXTIME)); damage = GetDamage(pTarget); // Can't use R attacks in the Snow War. if (GetZoneID() == ZONE_SNOW_BATTLE && g_pMain->m_byBattleOpen == SNOW_BATTLE) damage = 0; if (damage > 0) { pTarget->HpChange(-damage, this); if (pTarget->isDead()) bResult = ATTACK_TARGET_DEAD; else bResult = ATTACK_SUCCESS; // Every attack takes a little of your weapon's durability. ItemWoreOut(ATTACK, damage); // Every hit takes a little of the defender's armour durability. if (pTarget->isPlayer()) TO_USER(pTarget)->ItemWoreOut(DEFENCE, damage); } } } Packet result(WIZ_ATTACK, bType); result << bResult << GetSocketID() << tid; SendToRegion(&result); }
void CUser::Chat(Packet & pkt) { Packet result; uint16 sessID; uint8 type = pkt.read<uint8>(), bNation; string chatstr, finalstr, strSender, * strMessage; bool isAnnouncement = false; if (isMuted()) return; pkt >> chatstr; if (chatstr.empty() || chatstr.size() > 128) return; // Process GM commands if (isGM() && ProcessChatCommand(chatstr)) return; #if 0 // Removed this - all it seems to do is cause chat to break for GMs (is it 19xx+ only?) if( isGM() && type == GENERAL_CHAT) type = 0x14; #endif // Handle GM notice & announcement commands if (type == PUBLIC_CHAT || type == ANNOUNCEMENT_CHAT) { // Trying to use a GM command without authorisation? Bad player! if (!isGM()) return; if (type == ANNOUNCEMENT_CHAT) type = WAR_SYSTEM_CHAT; // This is horrible, but we'll live with it for now. // Pull the notice string (#### NOTICE : %s ####) from the database. // Format the chat string around it, so our chat data is within the notice g_pMain->GetServerResource(IDP_ANNOUNCEMENT, &finalstr, chatstr.c_str()); isAnnouncement = true; } if (isAnnouncement) { // GM notice/announcements show no name, so don't bother setting it. strMessage = &finalstr; // use the formatted message from the user bNation = KARUS; // arbitrary nation sessID = -1; } else { strMessage = &chatstr; // use the raw message from the user strSender = GetName(); // everything else uses a name, so set it bNation = GetNation(); sessID = GetSocketID(); } ChatPacket::Construct(&result, type, strMessage, &strSender, bNation, sessID); switch (type) { case GENERAL_CHAT: g_pMain->Send_NearRegion(&result, GetMap(), GetRegionX(), GetRegionZ(), GetX(), GetZ()); break; case PRIVATE_CHAT: { CUser *pUser = g_pMain->GetUserPtr(m_sPrivateChatUser); if (pUser != nullptr) pUser->Send(&result); } break; case PARTY_CHAT: if (isInParty()) g_pMain->Send_PartyMember(m_sPartyIndex, &result); break; case SHOUT_CHAT: if (m_sMp < (m_iMaxMp / 5)) break; // Characters under level 35 require 3,000 coins to shout. if (!isGM() && GetLevel() < 35 && !GoldLose(SHOUT_COIN_REQUIREMENT)) break; MSpChange(-(m_iMaxMp / 5)); SendToRegion(&result); break; case KNIGHTS_CHAT: if (isInClan()) g_pMain->Send_KnightsMember(GetClanID(), &result); break; case PUBLIC_CHAT: case ANNOUNCEMENT_CHAT: if (isGM()) g_pMain->Send_All(&result); break; case COMMAND_CHAT: if (GetFame() == COMMAND_CAPTAIN) g_pMain->Send_CommandChat(&result, m_bNation, this); break; case MERCHANT_CHAT: if (isMerchanting()) SendToRegion(&result); break; case ALLIANCE_CHAT: if (isInClan()) { CKnights *pKnights = g_pMain->GetClanPtr(GetClanID()); if (pKnights != nullptr && pKnights->isInAlliance()) g_pMain->Send_KnightsAlliance(pKnights->GetAllianceID(), &result); } break; case WAR_SYSTEM_CHAT: if (isGM()) g_pMain->Send_All(&result); break; } }
void CUser::Chat(Packet & pkt) { Packet result(WIZ_CHAT); uint8 type = pkt.read<uint8>(); char finalstr[1024] = ""; std::string buff, chatstr; bool isAnnouncement = false; if (isMuted()) return; pkt >> chatstr; if (chatstr.empty() || chatstr.size() > 128) return; // Process GM commands if (isGM() && ProcessChatCommand(chatstr)) return; #if 0 // Removed this - all it seems to do is cause chat to break for GMs (is it 19xx+ only?) if( isGM() && type == GENERAL_CHAT) type = 0x14; #endif uint8 bNation = GetNation(); uint16 sessID = GetSocketID(); // Handle GM notice & announcement commands if (type == PUBLIC_CHAT || type == ANNOUNCEMENT_CHAT) { // Trying to use a GM command without authorisation? Bad player! if (!isGM()) return; if (type == ANNOUNCEMENT_CHAT) type = WAR_SYSTEM_CHAT; // This is horrible, but we'll live with it for now. // Pull the notice string (#### NOTICE : %s ####) from the database. CString noticeText = g_pMain->GetServerResource(IDP_ANNOUNCEMENT); // Format the chat string around it, so our chat data is within the notice sprintf_s(finalstr, sizeof(finalstr), noticeText, chatstr.c_str()); bNation = KARUS; // arbitrary nation sessID = -1; isAnnouncement = true; } result.SByte(); result << type << bNation << sessID; if (isAnnouncement) { result << uint8(0); // GM notice/announcements show no name (so specify length of 0) result.DByte(); result << finalstr; // now tack on the formatted message from the user } else { result << m_pUserData.m_id; // everything else provides a name result.DByte(); result << chatstr; // now tack on the chat message from the user } switch (type) { case GENERAL_CHAT: g_pMain->Send_NearRegion(&result, GetMap(), GetRegionX(), GetRegionZ(), GetX(), GetZ()); break; case PRIVATE_CHAT: { if (m_sPrivateChatUser == GetSocketID()) break; CUser *pUser = g_pMain->GetUserPtr(m_sPrivateChatUser); if (pUser != NULL) pUser->Send(&result); } break; case PARTY_CHAT: if (isInParty()) g_pMain->Send_PartyMember(m_sPartyIndex, &result); break; case SHOUT_CHAT: if (m_pUserData.m_sMp < (m_iMaxMp / 5)) break; // Characters under level 35 require 3,000 coins to shout. if (!isGM() && GetLevel() < 35 && !GoldLose(SHOUT_COIN_REQUIREMENT)) break; MSpChange(-(m_iMaxMp / 5)); SendToRegion(&result); break; case KNIGHTS_CHAT: if (isInClan()) g_pMain->Send_KnightsMember(GetClanID(), &result); break; case PUBLIC_CHAT: case ANNOUNCEMENT_CHAT: if (isGM()) g_pMain->Send_All(&result); break; case COMMAND_CHAT: if (getFame() == COMMAND_CAPTAIN) g_pMain->Send_CommandChat(&result, m_pUserData.m_bNation, this); break; case MERCHANT_CHAT: if (isMerchanting()) SendToRegion(&result); break; case WAR_SYSTEM_CHAT: if (isGM()) g_pMain->Send_All(&result); break; } }
void CUser::Attack(Packet & pkt) { Packet result; int16 sid = -1, tid = -1, damage, delaytime, distance; uint8 bType, bResult; CUser* pTUser = NULL; pkt >> bType >> bResult >> tid >> delaytime >> distance; // delaytime = delaytime / 100.0f; // distance = distance / 10.0f; if (isBlinking() || isDead()) return; _ITEM_TABLE *pTable = GetItemPrototype(RIGHTHAND); if (pTable == NULL) return; // If you're holding a weapon, do a client-based (ugh, do not trust!) delay check. if (pTable && (delaytime < pTable->m_sDelay || distance > pTable->m_sRange)) return; // Empty handed. else if (delaytime < 100) return; // We're attacking a player... if (tid < NPC_BAND) { pTUser = g_pMain->GetUserPtr(tid); if (pTUser == NULL || pTUser->isDead() || pTUser->isBlinking() || (pTUser->GetNation() == GetNation() && GetZoneID() != 48 /* TO-DO: implement better checks */) || !isAttackZone()) bResult = 0; else { damage = GetDamage(pTUser, NULL); if (GetZoneID() == ZONE_SNOW_BATTLE && g_pMain->m_byBattleOpen == SNOW_BATTLE) damage = 0; if (damage <= 0) bResult = 0; else { // TO-DO: Move all this redundant code into appropriate event-based methods so that all the other cases don't have to copypasta (and forget stuff). pTUser->HpChange(-damage, this); if (pTUser->isDead()) bResult = 2; ItemWoreOut(ATTACK, damage); pTUser->ItemWoreOut(DEFENCE, damage); SendTargetHP(0, tid, -damage); } } } // We're attacking an NPC... else if (tid >= NPC_BAND) { // AI hasn't loaded yet if (g_pMain->m_bPointCheckFlag == FALSE) return; CNpc *pNpc = g_pMain->m_arNpcArray.GetData(tid); if (pNpc != NULL && pNpc->isAlive() && (pNpc->GetNation() == 0 || pNpc->GetNation() == GetNation())) { result.SetOpcode(AG_ATTACK_REQ); result << bType << bResult << GetSocketID() << tid << uint16(m_sTotalHit * m_bAttackAmount / 100) << uint16(m_sTotalAc + m_sACAmount) << m_sTotalHitrate /* this is actually a float. screwed up naming... */ << m_sTotalEvasionrate /* also a float */ << m_sItemAc << m_bMagicTypeLeftHand << m_bMagicTypeRightHand << m_sMagicAmountLeftHand << m_sMagicAmountRightHand; g_pMain->Send_AIServer(&result); return; } } result.SetOpcode(WIZ_ATTACK); result << bType << bResult << GetSocketID() << tid; SendToRegion(&result); if (tid < NPC_BAND && bResult == 2 // 2 means a player died. && pTUser) { pTUser->Send(&result); TRACE("*** User Attack Dead, id=%s, result=%d, type=%d, HP=%d\n", pTUser->m_pUserData->m_id, bResult, pTUser->m_bResHpType, pTUser->m_pUserData->m_sHp); } }