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);
}
Beispiel #4
0
/**
* @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);
}
Beispiel #11
0
/**
* @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);
	}
}
Beispiel #14
0
/**
* @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);
}
Beispiel #15
0
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);
}
Beispiel #16
0
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);
	}
}