void CUser::ZoneChange(int zone, float x, float z) { m_bZoneChangeFlag = true; C3DMap* pMap = nullptr; _ZONE_SERVERINFO *pInfo = nullptr; pMap = g_pMain->GetZoneByID(zone); if (!pMap) return; if( pMap->m_bType == 2 ) { // If Target zone is frontier zone. if( GetLevel() < 20 && g_pMain->m_byBattleOpen != SNOW_BATTLE) return; } if( g_pMain->m_byBattleOpen == NATION_BATTLE ) { // Battle zone open if( m_bZone == BATTLE_ZONE ) { if( pMap->m_bType == 1 && m_bNation != zone && (zone < 10 || zone > 21)) { // ???? ?????? ???? ????.. if( m_bNation == KARUS && !g_pMain->m_byElmoradOpenFlag ) { TRACE("#### ZoneChange Fail ,,, id=%s, nation=%d, flag=%d\n", GetName().c_str(), m_bNation, g_pMain->m_byElmoradOpenFlag); return; } else if( m_bNation == ELMORAD && !g_pMain->m_byKarusOpenFlag ) { TRACE("#### ZoneChange Fail ,,, id=%s, nation=%d, flag=%d\n", GetName().c_str(), m_bNation, g_pMain->m_byKarusOpenFlag); return; } } } else if( pMap->m_bType == 1 && m_bNation != zone && (zone < 10 || zone > 21)) { // ???? ?????? ???? ????.. return; } // else if( pMap->m_bType == 2 && zone == ZONE_RONARK_LAND ) { // You can't go to frontier zone when Battlezone is open. Packet result(WIZ_WARP_LIST, uint8(2)); result << uint8(0); Send(&result); return; } // } else if( g_pMain->m_byBattleOpen == SNOW_BATTLE ) { // Snow Battle zone open if( pMap->m_bType == 1 && m_bNation != zone ) { // ???? ?????? ???? ????.. return; } else if( pMap->m_bType == 2 && (zone == ZONE_RONARK_LAND || zone == ZONE_BATTLE ) ) { // You can't go to frontier zone when Battlezone is open. return; } } else { // Battle zone close if( pMap->m_bType == 1 && m_bNation != zone && (zone < 10 || zone > 21)) return; } m_bWarp = 0x01; UserInOut(INOUT_OUT); if( m_bZone == ZONE_SNOW_BATTLE ) { //TRACE("ZoneChange - name=%s\n", m_id); SetMaxHp( 1 ); } bool bSameZone = (GetZoneID() == zone); m_bZone = zone; m_curx = x; m_curz = z; if (!bSameZone) { SetZoneAbilityChange(); // Reset the user's anger gauge when leaving the zone // Unknown if this is official behaviour, but it's logical. if (GetAngerGauge() > 0) UpdateAngerGauge(0); /* Here we also send a clan packet with subopcode 0x16 (with a byte flag of 2) if war zone/Moradon or subopcode 0x17 (with nWarEnemyID) for all else */ #if 0 if (isInClan()) { CKnights * pKnights = g_pMain->GetClanPtr(GetClanID()); if (pKnights != nullptr && pKnights->bKnightsWarStarted) { Packet clanPacket(WIZ_KNIGHTS_PROCESS); if ((GetZoneID() / 100) == 1 || GetZoneID() == 21) clanPacket << uint8(0x17) << uint8(2); else clanPacket << uint16(0x16) << uint16(0 /*nWarEnemyID*/); Send(&clanPacket); } } #endif if (GetZoneID() == ZONE_SNOW_BATTLE) { SetMaxHp(); } if (isInParty()) PartyRemove(GetSocketID()); ResetWindows(); } m_pMap = pMap; if( g_pMain->m_nServerNo != pMap->m_nServerNo ) { pInfo = g_pMain->m_ServerArray.GetData( pMap->m_nServerNo ); if( !pInfo ) return; UserDataSaveToAgent(); m_bLogout = 2; // server change flag SendServerChange(pInfo->strServerIP, 2); return; } SetRegion(GetNewRegionX(), GetNewRegionZ()); Packet result(WIZ_ZONE_CHANGE, uint8(3)); // magic numbers, sigh. result << uint16(GetZoneID()) << GetSPosX() << GetSPosZ() << GetSPosY() << g_pMain->m_byOldVictory; Send(&result); if (!m_bZoneChangeSameZone) { m_sWhoKilledMe = -1; m_iLostExp = 0; m_bRegeneType = 0; m_tLastRegeneTime = 0; m_sBind = -1; InitType3(); InitType4(); CMagicProcess::CheckExpiredType9Skills(this, true); SetUserAbility(); } result.Initialize(AG_ZONE_CHANGE); result << GetSocketID() << GetZoneID(); Send_AIServer(&result); m_bZoneChangeSameZone = false; m_bZoneChangeFlag = false; }
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::Regene(uint8 regene_type, uint32 magicid /*= 0*/) { ASSERT(GetMap() != NULL); CUser* pUser = NULL; _OBJECT_EVENT* pEvent = NULL; _HOME_INFO* pHomeInfo = NULL; _MAGIC_TYPE5* pType = NULL; if (!isDead()) return; InitType3(); InitType4(); if (regene_type != 1 && regene_type != 2) { regene_type = 1; } if (regene_type == 2) { magicid = 490041; // The Stone of Ressurection magic ID if (!RobItem(379006000, 3 * GetLevel())) { return; // Subtract resurrection stones. } if (GetLevel() <= 5) { return; // 5 level minimum. } } pHomeInfo = g_pMain->m_HomeArray.GetData(m_pUserData->m_bNation); if (!pHomeInfo) return; UserInOut(INOUT_OUT); float x = 0.0f, z = 0.0f; x = (float)(myrand( 0, 400 )/100.0f); z = (float)(myrand( 0, 400 )/100.0f); if( x < 2.5f ) x = 1.5f + x; if( z < 2.5f ) z = 1.5f + z; pEvent = GetMap()->GetObjectEvent(m_pUserData->m_sBind); // TO-DO: Clean this entire thing up. Wow. if (magicid == 0) { if( pEvent && pEvent->byLife == 1 ) { // Bind Point m_pUserData->m_curx = pEvent->fPosX + x; m_pUserData->m_curz = pEvent->fPosZ + z; m_pUserData->m_cury = 0; } else if( m_pUserData->m_bNation != m_pUserData->m_bZone) { // Free Zone or Opposite Zone if(m_pUserData->m_bZone > 200) { // Frontier Zone... x = (float)(pHomeInfo->FreeZoneX + myrand(0, pHomeInfo->FreeZoneLX)); z = (float)(pHomeInfo->FreeZoneZ + myrand(0, pHomeInfo->FreeZoneLZ)); } // else if(m_pUserData->m_bZone > 100 && m_pUserData->m_bZone < 200) { // Battle Zone... /* m_bResHpType = USER_STANDING; HpChange( m_iMaxHp ); KickOutZoneUser(); // Go back to your own zone! return; */ x = (float)(pHomeInfo->BattleZoneX + myrand(0, pHomeInfo->BattleZoneLX)); z = (float)(pHomeInfo->BattleZoneZ + myrand(0, pHomeInfo->BattleZoneLZ)); if (m_pUserData->m_bZone == ZONE_SNOW_BATTLE) { x = (float)(pHomeInfo->FreeZoneX + myrand(0, pHomeInfo->FreeZoneLX)); z = (float)(pHomeInfo->FreeZoneZ + myrand(0, pHomeInfo->FreeZoneLZ)); } } else if (m_pUserData->m_bZone > 10 && m_pUserData->m_bZone < 20) { x = (float)(527 + myrand(0, 10)); z = (float)(543 + myrand(0, 10)); } else if (m_pUserData->m_bZone < 3) { // Specific Lands... if (m_pUserData->m_bNation == KARUS) { x = (float)(pHomeInfo->ElmoZoneX + myrand(0, pHomeInfo->ElmoZoneLX)); z = (float)(pHomeInfo->ElmoZoneZ + myrand(0, pHomeInfo->ElmoZoneLZ)); } else if (m_pUserData->m_bNation == ELMORAD) { x = (float)(pHomeInfo->KarusZoneX + myrand(0, pHomeInfo->KarusZoneLX)); z = (float)(pHomeInfo->KarusZoneZ + myrand(0, pHomeInfo->KarusZoneLZ)); } else return; } else { short sx, sz; GetStartPosition(sx, sz); x = sx; z = sz; } m_pUserData->m_curx = x; m_pUserData->m_curz = z; } else { if (m_pUserData->m_bNation == KARUS) { x = (float)(pHomeInfo->KarusZoneX + myrand(0, pHomeInfo->KarusZoneLX)); z = (float)(pHomeInfo->KarusZoneZ + myrand(0, pHomeInfo->KarusZoneLZ)); } else if (m_pUserData->m_bNation == ELMORAD) { x = (float)(pHomeInfo->ElmoZoneX + myrand(0, pHomeInfo->ElmoZoneLX)); z = (float)(pHomeInfo->ElmoZoneZ + myrand(0, pHomeInfo->ElmoZoneLZ)); } else return; m_pUserData->m_curx = x; m_pUserData->m_curz = z; } } Packet result(WIZ_REGENE); result << GetSPosX() << GetSPosZ() << GetSPosY(); Send(&result); if (magicid > 0) { // Clerical Resurrection. pType = g_pMain->m_Magictype5Array.GetData(magicid); if ( !pType ) return; m_bResHpType = USER_STANDING; MSpChange(-m_iMaxMp); // Empty out MP. if (m_sWhoKilledMe == -1 && regene_type == 1) { ExpChange((m_iLostExp * pType->bExpRecover) / 100); // Restore Target Experience. } m_bRegeneType = REGENE_MAGIC; } else { // Normal Regene. // m_bAbnormalType = ABNORMAL_BLINKING; // m_bResHpType = USER_STANDING; m_bRegeneType = REGENE_NORMAL; } HpChange(m_iMaxHp); m_fLastRegeneTime = TimeGet(); m_sWhoKilledMe = -1; m_iLostExp = 0; if (!isBlinking()) { result.Initialize(AG_USER_REGENE); result << GetSocketID() << m_pUserData->m_sHp; g_pMain->Send_AIServer(&result); } SetRegion(GetNewRegionX(), GetNewRegionZ()); UserInOut(INOUT_RESPAWN); g_pMain->RegionUserInOutForMe(this); g_pMain->RegionNpcInfoForMe(this); BlinkStart(); if (isInParty()) { // TO-DO: Wrap these up into Party-specific methods (nothing for that yet) // UPDATE: Sticking them in the CUser class for the moment. Need to have them make sense, though. if (!m_bType3Flag) SendPartyStatusUpdate(1); if (!m_bType4Flag) SendPartyStatusUpdate(2); } }
void CUser::ZoneChange(uint16 sNewZone, float x, float z) { C3DMap * pMap = g_pMain->GetZoneByID(sNewZone); if (pMap == nullptr) return; ZoneChangeError errorReason; if (!CanChangeZone(pMap, errorReason)) { Packet result; switch (errorReason) { case ZoneChangeErrorWrongLevel: /* this will depend on the zone */ break; case ZoneChangeErrorWarActive: result.Initialize(WIZ_WARP_LIST); result << uint8(2) << uint8(4); Send(&result); break; case ZoneChangeErrorNeedLoyalty: /* does this have an error? */ break; } return; } m_bWarp = true; m_bZoneChangeFlag = true; UserInOut(INOUT_OUT); if (sNewZone == ZONE_SNOW_BATTLE) SetMaxHp(1); if (GetZoneID() != sNewZone) { SetZoneAbilityChange(); // Reset the user's anger gauge when leaving the zone // Unknown if this is official behaviour, but it's logical. if (GetAngerGauge() > 0) UpdateAngerGauge(0); /* Here we also send a clan packet with subopcode 0x16 (with a byte flag of 2) if war zone/Moradon or subopcode 0x17 (with nWarEnemyID) for all else */ #if 0 if (isInClan()) { CKnights * pKnights = g_pMain->GetClanPtr(GetClanID()); if (pKnights != nullptr && pKnights->bKnightsWarStarted) { Packet clanPacket(WIZ_KNIGHTS_PROCESS); if (pMap->isWarZone() || byNewZone == ZONE_MORADON) clanPacket << uint8(0x17) << uint8(2); else clanPacket << uint16(0x16) << uint16(0 /*nWarEnemyID*/); Send(&clanPacket); } } #endif if (sNewZone == ZONE_SNOW_BATTLE) SetMaxHp(); if (isInParty()) PartyRemove(GetSocketID()); ResetWindows(); } m_bZone = (uint8) sNewZone; // this is 2 bytes to support the warp data loaded from SMDs. It should not go above a byte, however. SetPosition(x, 0.0f, z); m_pMap = pMap; if (g_pMain->m_nServerNo != pMap->m_nServerNo) { _ZONE_SERVERINFO *pInfo = g_pMain->m_ServerArray.GetData(pMap->m_nServerNo); if (pInfo == nullptr) return; UserDataSaveToAgent(); m_bLogout = 2; // server change flag SendServerChange(pInfo->strServerIP, 2); return; } SetRegion(GetNewRegionX(), GetNewRegionZ()); Packet result(WIZ_ZONE_CHANGE, uint8(ZoneChangeTeleport)); result << uint16(GetZoneID()) << GetSPosX() << GetSPosZ() << GetSPosY() << g_pMain->m_byOldVictory; Send(&result); if (!m_bZoneChangeSameZone) { m_sWhoKilledMe = -1; m_iLostExp = 0; m_bRegeneType = 0; m_tLastRegeneTime = 0; m_sBind = -1; InitType3(); InitType4(); CMagicProcess::CheckExpiredType9Skills(this, true); SetUserAbility(); } result.Initialize(AG_ZONE_CHANGE); result << GetSocketID() << GetZoneID(); Send_AIServer(&result); m_bZoneChangeSameZone = false; m_bZoneChangeFlag = false; }