/** * @brief Determine if an NPC can attack the specified unit. * * @param pTarget The target we are attempting to attack. * * @return true if we can attack, false if not. */ bool CNpc::CanAttack(Unit * pTarget) { if (!Unit::CanAttack(pTarget)) return false; // A nation of 0 indicates friendliness to all if (GetNation() == Nation::ALL) return false; // A nation of 3 indicates hostility to all (or friendliness to none) if (GetNation() == Nation::NONE) return true; // An NPC cannot attack a unit of the same nation return (GetNation() != pTarget->GetNation()); }
void CUser::GetUserInfo(Packet & pkt) { CKnights *pKnights = NULL; pkt.SByte(); pkt << GetName() << uint16(GetNation()) << GetClanID() << getFame(); pKnights = g_pMain->GetClanPtr(GetClanID()); if (pKnights == NULL) { pkt << uint32(0) << uint16(0) << uint8(0) << uint16(-1) << uint32(0) << uint8(0); } else { pkt << uint16(pKnights->m_sAlliance) << pKnights->m_strName << pKnights->m_byGrade << pKnights->m_byRanking << uint16(pKnights->m_sMarkVersion) // symbol/mark version << uint16(pKnights->m_sCape) // cape ID << pKnights->m_bCapeR << pKnights->m_bCapeG << pKnights->m_bCapeB << uint8(0) // this is stored in 4 bytes after all. // not sure what this is, but it (just?) enables the clan symbol on the cape // value in dump was 9, but everything tested seems to behave as equally well... // we'll probably have to implement logic to respect requirements. << uint8(1); } pkt << GetLevel() << m_bRace << m_sClass << GetSPosX() << GetSPosZ() << GetSPosY() << m_bFace << m_nHair << m_bResHpType << uint32(m_bAbnormalType) << m_bNeedParty << m_bAuthority << m_bPartyLeader // is party leader (bool) << m_bInvisibilityType // visibility state << uint8(0) // team colour (i.e. in soccer, 0=none, 1=blue, 2=red) << m_bIsHidingHelmet // either this is correct and items are super buggy, or it causes baldness. You choose. << m_sDirection // direction << m_bIsChicken // chicken/beginner flag << m_bRank // king flag << m_bPersonalRank << m_bKnightsRank // NP ranks (total, monthly) << m_sItemArray[BREAST].nNum << m_sItemArray[BREAST].sDuration << uint8(0) << m_sItemArray[LEG].nNum << m_sItemArray[LEG].sDuration << uint8(0) << m_sItemArray[HEAD].nNum << m_sItemArray[HEAD].sDuration << uint8(0) << m_sItemArray[GLOVE].nNum << m_sItemArray[GLOVE].sDuration << uint8(0) << m_sItemArray[FOOT].nNum << m_sItemArray[FOOT].sDuration << uint8(0) << m_sItemArray[SHOULDER].nNum << m_sItemArray[SHOULDER].sDuration << uint8(0) << m_sItemArray[RIGHTHAND].nNum << m_sItemArray[RIGHTHAND].sDuration << uint8(0) << m_sItemArray[LEFTHAND].nNum << m_sItemArray[LEFTHAND].sDuration << uint8(0) << m_sItemArray[CWING].nNum << m_sItemArray[CWING].sDuration << uint8(0) << m_sItemArray[CTOP].nNum << m_sItemArray[CTOP].sDuration << uint8(0) << m_sItemArray[CHELMET].nNum << m_sItemArray[CHELMET].sDuration << uint8(0) << m_sItemArray[CRIGHT].nNum << m_sItemArray[CRIGHT].sDuration << uint8(0) << m_sItemArray[CLEFT].nNum << m_sItemArray[CLEFT].sDuration << uint8(0) << GetZoneID() << uint8(-1) << uint8(-1) << uint16(0) << uint16(0) << uint16(0); }
/** * @brief Gets NPC information for use in various NPC packets. * * @param pkt The packet the information will be stored in. */ void CNpc::GetNpcInfo(Packet & pkt) { pkt << GetProtoID() << uint8(isMonster() ? 1 : 2) // Monster = 1, NPC = 2 (need to use a better flag) << m_sPid << GetType() << m_iSellingGroup << m_sSize << m_iWeapon_1 << m_iWeapon_2 // Monsters require 0 regardless, otherwise they'll act as NPCs. << uint8(isMonster() ? 0 : GetNation()) << GetLevel() << GetSPosX() << GetSPosZ() << GetSPosY() << uint32(isGateOpen()) << m_byObjectType << uint16(0) << uint16(0) // unknown << int16(m_byDirection); }
void CUser::SelectCharacter(Packet & pkt) { Packet result(WIZ_SEL_CHAR); uint8 bResult, bInit; if (isBanned()) { Disconnect(); return; } pkt >> bResult >> bInit; result << bResult; if (bResult == 0 || !GetZoneID()) goto fail_return; m_pMap = g_pMain->GetZoneByID(GetZoneID()); if (GetMap() == nullptr) goto fail_return; if (g_pMain->m_nServerNo != GetMap()->m_nServerNo) { _ZONE_SERVERINFO *pInfo = g_pMain->m_ServerArray.GetData(GetMap()->m_nServerNo); if (pInfo == nullptr) goto fail_return; SendServerChange(pInfo->strServerIP, bInit); return; } if (!g_pMain->isWarOpen() && GetFame() == COMMAND_CAPTAIN) m_bFame = CHIEF; // Disallow players from relogging in the opposite nation's home zone when an invasion's not running. if (((GetZoneID() != GetNation() && GetZoneID() <= ZONE_ELMORAD && !g_pMain->m_byBattleOpen) // also disallow players from logging back into war zones that aren't currently active... || (GetMap()->isWarZone() && !g_pMain->m_byBattleOpen) // Chaos, bdw and juraid montuain || isInTempleEventZone() // Ronark Land, Ardream, RLB, Bifrost, Krowaz Dominion. || (g_pMain->m_byBattleOpen && (GetZoneID() == ZONE_RONARK_LAND || GetZoneID() == ZONE_ARDREAM || GetZoneID() == ZONE_RONARK_LAND_BASE || GetZoneID() == ZONE_BIFROST || GetZoneID() == ZONE_KROWAZ_DOMINION))) && !isGM()) { NativeZoneReturn(); Disconnect(); return; } SetLogInInfoToDB(bInit); result << GetZoneID() << GetSPosX() << GetSPosZ() << GetSPosY() << g_pMain->m_byOldVictory; m_bSelectedCharacter = true; Send(&result); SetUserAbility(false); if (GetLevel() > MAX_LEVEL) { Disconnect(); return; } m_iMaxExp = g_pMain->GetExpByLevel(GetLevel()); SetRegion(GetNewRegionX(), GetNewRegionZ()); if (GetClanID() == -1) { SetClanID(0); m_bFame = 0; return; } else if (GetClanID() != 0 && GetZoneID() > 2) { result.Initialize(WIZ_KNIGHTS_PROCESS); result << uint8(KNIGHTS_LIST_REQ) << GetClanID(); g_pMain->AddDatabaseRequest(result, this); } return; fail_return: Send(&result); }
void CUser::GetUserInfo(Packet & pkt) { pkt.SByte(); pkt << GetName() << uint16(GetNation()) << GetClanID() << GetFame(); CKnights * pKnights = g_pMain->GetClanPtr(GetClanID()); if (pKnights == nullptr) { pkt << uint32(0) << uint16(0) << uint8(0) << uint16(-1) << uint32(0) << uint8(0); } else { pkt << pKnights->GetAllianceID() << pKnights->m_strName << pKnights->m_byGrade << pKnights->m_byRanking << uint16(pKnights->m_sMarkVersion) // symbol/mark version << uint16(pKnights->m_sCape) // cape ID << pKnights->m_bCapeR << pKnights->m_bCapeG << pKnights->m_bCapeB << uint8(0) // this is stored in 4 bytes after all. // not sure what this is, but it (just?) enables the clan symbol on the cape // value in dump was 9, but everything tested seems to behave as equally well... // we'll probably have to implement logic to respect requirements. << uint8(1); } // There are two event-driven invisibility states; dispel on attack, and dispel on move. // These are handled primarily server-side; from memory the client only cares about value 1 (which we class as 'dispel on move'). // As this is the only place where this flag is actually sent to the client, we'll just convert 'dispel on attack' // back to 'dispel on move' as the client expects. uint8 bInvisibilityType = m_bInvisibilityType; if (bInvisibilityType == INVIS_DISPEL_ON_ATTACK) bInvisibilityType = INVIS_DISPEL_ON_MOVE; pkt << GetLevel() << m_bRace << m_sClass << GetSPosX() << GetSPosZ() << GetSPosY() << m_bFace << m_nHair << m_bResHpType << uint32(m_bAbnormalType) << m_bNeedParty << m_bAuthority << m_bPartyLeader // is party leader (bool) << bInvisibilityType // visibility state << uint8(0) // team colour (i.e. in soccer, 0=none, 1=blue, 2=red) << m_bIsHidingHelmet // either this is correct and items are super buggy, or it causes baldness. You choose. << m_sDirection // direction << m_bIsChicken // chicken/beginner flag << m_bRank // king flag << m_bPersonalRank << m_bKnightsRank // NP ranks (total, monthly) << m_sItemArray[BREAST].nNum << m_sItemArray[BREAST].sDuration << uint8(0) << m_sItemArray[LEG].nNum << m_sItemArray[LEG].sDuration << uint8(0) << m_sItemArray[HEAD].nNum << m_sItemArray[HEAD].sDuration << uint8(0) << m_sItemArray[GLOVE].nNum << m_sItemArray[GLOVE].sDuration << uint8(0) << m_sItemArray[FOOT].nNum << m_sItemArray[FOOT].sDuration << uint8(0) << m_sItemArray[SHOULDER].nNum << m_sItemArray[SHOULDER].sDuration << uint8(0) << m_sItemArray[RIGHTHAND].nNum << m_sItemArray[RIGHTHAND].sDuration << uint8(0) << m_sItemArray[LEFTHAND].nNum << m_sItemArray[LEFTHAND].sDuration << uint8(0) << m_sItemArray[CWING].nNum << m_sItemArray[CWING].sDuration << uint8(0) << m_sItemArray[CTOP].nNum << m_sItemArray[CTOP].sDuration << uint8(0) << m_sItemArray[CHELMET].nNum << m_sItemArray[CHELMET].sDuration << uint8(0) << m_sItemArray[CRIGHT].nNum << m_sItemArray[CRIGHT].sDuration << uint8(0) << m_sItemArray[CLEFT].nNum << m_sItemArray[CLEFT].sDuration << uint8(0) << GetZoneID() << uint8(-1) << uint8(-1) << uint16(0) << uint16(0) << uint16(0); }
void CUser::Regene(uint8 regene_type, uint32 magicid /*= 0*/) { ASSERT(GetMap() != nullptr); _OBJECT_EVENT* pEvent = nullptr; _HOME_INFO* pHomeInfo = nullptr; float x = 0.0f, z = 0.0f; if (!isDead()) return; if (regene_type != 1 && regene_type != 2) regene_type = 1; if (regene_type == 2) { magicid = 490041; // The Stone of Ressurection magic ID // Is our level high enough to be able to resurrect using this skill? if (GetLevel() <= 5 // Do we have enough resurrection stones? || !RobItem(379006000, 3 * GetLevel())) return; } // If we're in a home zone, we'll want the coordinates from there. Otherwise, assume our own home zone. pHomeInfo = g_pMain->m_HomeArray.GetData(GetZoneID() <= ZONE_ELMORAD ? GetZoneID() : GetNation()); if (pHomeInfo == nullptr) return; UserInOut(INOUT_OUT); pEvent = GetMap()->GetObjectEvent(m_sBind); // If we're not using a spell to resurrect. if (magicid == 0) { // Resurrect at a bind/respawn point if (pEvent != nullptr && pEvent->byLife == 1) { SetPosition(pEvent->fPosX + x, 0.0f, pEvent->fPosZ + z); } // Are we trying to respawn in a home zone? else if (GetZoneID() <= ZONE_ELMORAD) { // Use the proper respawn area for our nation, as the opposite nation can // enter this zone at a war's invasion stage. if (GetNation() == KARUS) { x = (float)(pHomeInfo->KarusZoneX + myrand(0, pHomeInfo->KarusZoneLX)); z = (float)(pHomeInfo->KarusZoneZ + myrand(0, pHomeInfo->KarusZoneLZ)); } else { x = (float)(pHomeInfo->ElmoZoneX + myrand(0, pHomeInfo->ElmoZoneLX)); z = (float)(pHomeInfo->ElmoZoneZ + myrand(0, pHomeInfo->ElmoZoneLZ)); } } else { // If we're in a war zone (aside from snow wars, which apparently use different coords), use BattleZone coordinates. if (GetZoneID() != ZONE_SNOW_BATTLE && GetZoneID() == (ZONE_BATTLE_BASE + g_pMain->m_byBattleZone)) { x = (float)(pHomeInfo->BattleZoneX + myrand(0, pHomeInfo->BattleZoneLX)); z = (float)(pHomeInfo->BattleZoneZ + myrand(0, pHomeInfo->BattleZoneLZ)); } // If we died in the Moradon arena, we need to spawn near the Arena. else if (GetZoneID() == ZONE_MORADON && isInArena()) { x = (float)(MINI_ARENA_RESPAWN_X + myrand(-MINI_ARENA_RESPAWN_RADIUS, MINI_ARENA_RESPAWN_RADIUS)); z = (float)(MINI_ARENA_RESPAWN_Z + myrand(-MINI_ARENA_RESPAWN_RADIUS, MINI_ARENA_RESPAWN_RADIUS)); } // For all else, just grab the start position (/town coordinates) from the START_POSITION table. else { short sx, sz; GetStartPosition(sx, sz); x = sx; z = sz; } } SetPosition(x, 0.0f, z); m_bResHpType = USER_STANDING; m_bRegeneType = REGENE_NORMAL; } else // we're respawning using a resurrect skill. { _MAGIC_TYPE5 * pType = g_pMain->m_Magictype5Array.GetData(magicid); if (pType == nullptr) return; MSpChange(-m_iMaxMp); // reset us to 0 MP. if (m_sWhoKilledMe == -1) ExpChange((m_iLostExp * pType->bExpRecover) / 100); // Restore m_bResHpType = USER_STANDING; m_bRegeneType = REGENE_MAGIC; } Packet result(WIZ_REGENE); result << GetSPosX() << GetSPosZ() << GetSPosY(); Send(&result); HpChange(GetMaxHealth()); m_tLastRegeneTime = UNIXTIME; m_sWhoKilledMe = -1; m_iLostExp = 0; if (magicid == 0) BlinkStart(); if (!isBlinking()) { result.Initialize(AG_USER_REGENE); result << GetSocketID() << m_sHp; Send_AIServer(&result); } SetRegion(GetNewRegionX(), GetNewRegionZ()); UserInOut(INOUT_RESPAWN); g_pMain->RegionUserInOutForMe(this); g_pMain->RegionNpcInfoForMe(this); InitializeStealth(); SendUserStatusUpdate(USER_STATUS_DOT, USER_STATUS_CURE); SendUserStatusUpdate(USER_STATUS_POISON, USER_STATUS_CURE); if (isInArena()) SendUserStatusUpdate(USER_STATUS_SPEED, USER_STATUS_CURE); RecastSavedMagic(); // If we actually respawned (i.e. we weren't resurrected by a skill)... if (magicid == 0) { // In PVP zones (not war zones), we must kick out players if they no longer // have any national points. if (GetMap()->isNationPVPZone() && GetMap()->isWarZone() && GetLoyalty() == 0) KickOutZoneUser(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::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); } }
void CUser::SelectCharacter(Packet & pkt) { Packet result(WIZ_SEL_CHAR); uint8 bResult, bInit; if (isBanned()) { Disconnect(); return; } pkt >> bResult >> bInit; result << bResult; if (bResult == 0 || !GetZoneID()) goto fail_return; m_pMap = g_pMain->GetZoneByID(GetZoneID()); if (GetMap() == NULL) goto fail_return; // Temporarily convert the old quest storage format to the new one. // This won't be necessary when Aujard's out of the picture. m_questMap.clear(); for (int i = 0, index = 0; i < m_pUserData->m_sQuestCount; i++) { uint16 sQuestID = GetShort(m_pUserData->m_bstrQuest, index); uint8 bQuestState = GetByte(m_pUserData->m_bstrQuest, index); m_questMap.insert(std::make_pair(sQuestID, bQuestState)); } if (g_pMain->m_nServerNo != GetMap()->m_nServerNo) { _ZONE_SERVERINFO *pInfo = g_pMain->m_ServerArray.GetData(GetMap()->m_nServerNo); if (pInfo == NULL) goto fail_return; SendServerChange(pInfo->strServerIP, bInit); return; } if (g_pMain->m_byBattleOpen == NO_BATTLE && getFame() == COMMAND_CAPTAIN) m_pUserData->m_bFame = CHIEF; if ((GetZoneID() != GetNation() && GetZoneID() < 3 && !g_pMain->m_byBattleOpen) || (GetZoneID() == ZONE_BATTLE && (g_pMain->m_byBattleOpen != NATION_BATTLE)) || (GetZoneID() == ZONE_SNOW_BATTLE && (g_pMain->m_byBattleOpen != SNOW_BATTLE)) || (GetZoneID() == ZONE_FRONTIER && g_pMain->m_byBattleOpen)) { NativeZoneReturn(); Disconnect(); return; } SetLogInInfoToDB(bInit); result << GetZoneID() << GetSPosX() << GetSPosZ() << GetSPosY() << g_pMain->m_byOldVictory; m_bSelectedCharacter = true; Send(&result); SetSlotItemValue(); SetUserAbility(false); if (GetLevel() > MAX_LEVEL) { Disconnect(); return; } m_iMaxExp = g_pMain->GetExpByLevel(GetLevel()); SetRegion(GetNewRegionX(), GetNewRegionZ()); if (GetClanID() == -1) { SetClanID(0); m_pUserData->m_bFame = 0; return; } else if (GetClanID() != 0) { CKnights* pKnights = g_pMain->GetClanPtr( GetClanID() ); if (pKnights != NULL) { g_pMain->m_KnightsManager.SetKnightsUser( GetClanID(), m_pUserData->m_id ); } else if (GetZoneID() > 2) { result.Initialize(WIZ_KNIGHTS_PROCESS); result << uint8(KNIGHTS_LIST_REQ) << GetClanID(); g_pMain->m_LoggerSendQueue.PutData(&result, GetSocketID()); } } return; fail_return: Send(&result); }
void CUser::GetUserInfo(Packet & pkt) { pkt.SByte(); pkt << GetName() << GetNation() << GetClanID() << GetFame(); CKnights * pKnights = g_pMain->GetClanPtr(GetClanID()); if (pKnights == nullptr) { //pkt /*<< uint8(0)*/ << uint16(0) << uint8(0) << uint8(0); pkt << uint32(0) << uint16(0) << uint8(0) << uint16(-1); } else { pkt << pKnights->GetAllianceID() << pKnights->m_strName << pKnights->m_byGrade << pKnights->m_byRanking << uint16(pKnights->m_sMarkVersion) // symbol/mark version << pKnights->GetCapeID(pKnights); // cape ID } // There are two event-driven invisibility states; dispel on attack, and dispel on move. // These are handled primarily server-side; from memory the client only cares about value 1 (which we class as 'dispel on move'). // As this is the only place where this flag is actually sent to the client, we'll just convert 'dispel on attack' // back to 'dispel on move' as the client expects. uint8 bInvisibilityType = m_bInvisibilityType; if (bInvisibilityType != INVIS_NONE) bInvisibilityType = INVIS_DISPEL_ON_MOVE; pkt << GetLevel() << m_bRace << m_sClass << GetSPosX() << GetSPosZ() << GetSPosY() << m_bFace << m_nHair << m_bResHpType << uint32(m_bAbnormalType)//uint8(m_bAbnormalType) << m_bNeedParty << m_bAuthority << m_bPartyLeader // is party leader (bool) << bInvisibilityType // visibility state //<< uint8(m_teamColour) // team colour (i.e. in soccer, 0=none, 1=blue, 2=red) //<< m_bIsHidingHelmet // either this is correct and items are super buggy, or it causes baldness. You choose. << m_sDirection // direction << m_bIsChicken // chicken/beginner flag << m_bRank // king flag << m_bKnightsRank << m_bPersonalRank; // NP ranks (total, monthly) uint8 equippedItems[] = { BREAST, LEG, HEAD, GLOVE, FOOT, SHOULDER, RIGHTHAND, LEFTHAND, CTOP, CHELMET }; foreach_array(i, equippedItems) { _ITEM_DATA * pItem = GetItem(equippedItems[i]); if (pItem == nullptr) continue; pkt << pItem->nNum << pItem->sDuration << pItem->bFlag; }
void CUser::SelectCharacter(Packet & pkt) { Packet result(WIZ_SEL_CHAR); uint8 bResult, bInit; if (isBanned()) { Disconnect(); return; } pkt >> bResult >> bInit; result << bResult; if (bResult == 0 || !GetZoneID()) goto fail_return; m_pMap = g_pMain->GetZoneByID(GetZoneID()); if (GetMap() == nullptr) goto fail_return; if (g_pMain->m_nServerNo != GetMap()->m_nServerNo) { _ZONE_SERVERINFO *pInfo = g_pMain->m_ServerArray.GetData(GetMap()->m_nServerNo); if (pInfo == nullptr) goto fail_return; SendServerChange(pInfo->strServerIP, bInit); return; } if (g_pMain->m_byBattleOpen == NO_BATTLE && GetFame() == COMMAND_CAPTAIN) m_bFame = CHIEF; if ((GetZoneID() != GetNation() && GetZoneID() < 3 && !g_pMain->m_byBattleOpen) || (GetZoneID() == ZONE_BATTLE && (g_pMain->m_byBattleOpen != NATION_BATTLE)) || (GetZoneID() == ZONE_SNOW_BATTLE && (g_pMain->m_byBattleOpen != SNOW_BATTLE)) || (GetZoneID() == ZONE_RONARK_LAND && g_pMain->m_byBattleOpen)) { NativeZoneReturn(); Disconnect(); return; } SetLogInInfoToDB(bInit); result << GetZoneID() << GetSPosX() << GetSPosZ() << GetSPosY() << g_pMain->m_byOldVictory; m_bSelectedCharacter = true; Send(&result); SetSlotItemValue(); SetUserAbility(false); if (GetLevel() > MAX_LEVEL) { Disconnect(); return; } m_iMaxExp = g_pMain->GetExpByLevel(GetLevel()); SetRegion(GetNewRegionX(), GetNewRegionZ()); if (GetClanID() == -1) { SetClanID(0); m_bFame = 0; return; } else if (GetClanID() != 0 && GetZoneID() > 2) { result.Initialize(WIZ_KNIGHTS_PROCESS); result << uint8(KNIGHTS_LIST_REQ) << GetClanID(); g_pMain->AddDatabaseRequest(result, this); } return; fail_return: Send(&result); }
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; } }
bool CUser::CanChangeZone(C3DMap * pTargetMap, ZoneChangeError & errorReason) { // While unofficial, game masters should be allowed to teleport anywhere. if (isGM()) return true; // Generic error reason; this should only be checked when the method returns false. errorReason = ZoneChangeErrorGeneric; // Ensure the user meets the zone's level requirements if (GetLevel() < pTargetMap->GetMinLevelReq() || GetLevel() > pTargetMap->GetMaxLevelReq()) { errorReason = ZoneChangeErrorWrongLevel; return false; } switch (pTargetMap->GetID()) { case ZONE_ELMORAD: case ZONE_KARUS: // Users may enter Luferson (1)/El Morad (2) if they are that nation, if (GetNation() == pTargetMap->GetID()) return true; // Users may also enter if there's a war invasion happening in that zone. if (GetNation() == KARUS) return g_pMain->m_byElmoradOpenFlag; else return g_pMain->m_byKarusOpenFlag; case ZONE_KARUS_ESLANT: return GetNation() == KARUS; case ZONE_ELMORAD_ESLANT: return GetNation() == ELMORAD; // Delos (30) may be entered by anybody, unless CSW is started -- in which case, it should only allow members of the clans competing for the castle (right?). case ZONE_DELOS: // TO-DO: implement CSW logic return true; // Bifrost (31) may only be entered if the zone is open by the event. // If it's open, it should only be open for the zone in control of the monument in the first stage. // After that, it's open to both zones (note: this extended logic won't be implemented until we actually implement the event -- #84). case ZONE_BIFROST: // TO-DO: implement Bifrost logic return true; case ZONE_RONARK_LAND: case ZONE_ARDREAM: // PVP zones such as Ronark Land/Ardream (do we include both?) may only be entered if a war is not started. if (g_pMain->m_byBattleOpen != NO_BATTLE) { errorReason = ZoneChangeErrorWarActive; return false; } // They may also not be entered if the user has no NP. if (GetLoyalty() <= 0) { errorReason = ZoneChangeErrorNeedLoyalty; return false; } break; default: // War zones may only be entered if that war zone is active. if (pTargetMap->isWarZone()) { if ((ZONE_BATTLE_BASE - pTargetMap->GetID()) != g_pMain->m_byBattleZone) return false; } } return true; }