int32 Client::CalcBaseHP() { if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) { int stats = GetSTA(); if (stats > 255) { stats = (stats - 255) / 2; stats += 255; } base_hp = 5; auto base_data = database.GetBaseData(GetLevel(), GetClass()); if (base_data) { base_hp += base_data->base_hp + (base_data->hp_factor * stats); base_hp += (GetHeroicSTA() * 10); } } else { uint32 Post255; uint32 lm = GetClassLevelFactor(); if ((GetSTA() - 255) / 2 > 0) { Post255 = (GetSTA() - 255) / 2; } else { Post255 = 0; } base_hp = (5) + (GetLevel() * lm / 10) + (((GetSTA() - Post255) * GetLevel() * lm / 3000)) + ((Post255 * GetLevel()) * lm / 6000); } return base_hp; }
void Client::RefreshGuildInfo() { uint32 OldGuildID = guild_id; guildrank = GUILD_RANK_NONE; guild_id = GUILD_NONE; bool WasBanker = GuildBanker; CharGuildInfo info; if(!guild_mgr.GetCharInfo(CharacterID(), info)) { Log(Logs::Detail, Logs::Guilds, "Unable to obtain guild char info for %s (%d)", GetName(), CharacterID()); return; } guildrank = info.rank; guild_id = info.guild_id; GuildBanker = info.banker || guild_mgr.IsGuildLeader(GuildID(), CharacterID()); if(((int)zone->GetZoneID() == RuleI(World, GuildBankZoneID))) { if(WasBanker != GuildBanker) { auto outapp = new EQApplicationPacket(OP_SetGuildRank, sizeof(GuildSetRank_Struct)); GuildSetRank_Struct *gsrs = (GuildSetRank_Struct*)outapp->pBuffer; gsrs->Rank = guildrank; strn0cpy(gsrs->MemberName, GetName(), sizeof(gsrs->MemberName)); gsrs->Banker = GuildBanker; FastQueuePacket(&outapp); } if((guild_id != OldGuildID) && GuildBanks) { // Unsure about this for RoF+ ... But they don't have that action anymore so f**k it if (ClientVersion() < EQEmu::versions::ClientVersion::RoF) ClearGuildBank(); if(guild_id != GUILD_NONE) GuildBanks->SendGuildBank(this); } } SendGuildSpawnAppearance(); }
int32 Client::CalcBaseEndurance() { int32 base_end = 0; if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) { double heroic_stats = (GetHeroicSTR() + GetHeroicSTA() + GetHeroicDEX() + GetHeroicAGI()) / 4.0f; double stats = (GetSTR() + GetSTA() + GetDEX() + GetAGI()) / 4.0f; if (stats > 201.0f) { stats = 1.25f * (stats - 201.0f) + 352.5f; } else if (stats > 100.0f) { stats = 2.5f * (stats - 100.0f) + 100.0f; } auto base_data = database.GetBaseData(GetLevel(), GetClass()); if (base_data) { base_end = base_data->base_end + (heroic_stats * 10.0f) + (base_data->endurance_factor * static_cast<int>(stats)); } } else { int Stats = GetSTR() + GetSTA() + GetDEX() + GetAGI(); int LevelBase = GetLevel() * 15; int at_most_800 = Stats; if (at_most_800 > 800) { at_most_800 = 800; } int Bonus400to800 = 0; int HalfBonus400to800 = 0; int Bonus800plus = 0; int HalfBonus800plus = 0; int BonusUpto800 = int( at_most_800 / 4 ) ; if (Stats > 400) { Bonus400to800 = int( (at_most_800 - 400) / 4 ); HalfBonus400to800 = int( std::max( ( at_most_800 - 400 ), 0 ) / 8 ); if (Stats > 800) { Bonus800plus = int( (Stats - 800) / 8 ) * 2; HalfBonus800plus = int( (Stats - 800) / 16 ); } } int bonus_sum = BonusUpto800 + Bonus400to800 + HalfBonus400to800 + Bonus800plus + HalfBonus800plus; base_end = LevelBase; //take all of the sums from above, then multiply by level*0.075 base_end += ( bonus_sum * 3 * GetLevel() ) / 40; } return base_end; }
void Client::SendGuildURL() { if (ClientVersion() < EQEmu::versions::ClientVersion::SoF) return; if(IsInAGuild()) { auto outapp = new EQApplicationPacket(OP_GuildUpdateURLAndChannel, sizeof(GuildUpdateURLAndChannel_Struct)); GuildUpdateURLAndChannel_Struct *guuacs = (GuildUpdateURLAndChannel_Struct*) outapp->pBuffer; if(guild_mgr.GetGuildURL(GuildID(), guuacs->Text)) { guuacs->Action = 0; FastQueuePacket(&outapp); } else safe_delete(outapp); } }
int32 Client::GetMaxStat() const { if ((RuleI(Character, StatCap)) > 0) { return (RuleI(Character, StatCap)); } int level = GetLevel(); int32 base = 0; if (level < 61) { base = 255; } else if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF) { base = 255 + 5 * (level - 60); } else if (level < 71) { base = 255 + 5 * (level - 60); } else { base = 330; } return (base); }
void Client::SendGuildSpawnAppearance() { if (!IsInAGuild()) { // clear guildtag SendAppearancePacket(AT_GuildID, GUILD_NONE); Log(Logs::Detail, Logs::Guilds, "Sending spawn appearance for no guild tag."); } else { uint8 rank = guild_mgr.GetDisplayedRank(GuildID(), GuildRank(), CharacterID()); Log(Logs::Detail, Logs::Guilds, "Sending spawn appearance for guild %d at rank %d", GuildID(), rank); SendAppearancePacket(AT_GuildID, GuildID()); if (ClientVersion() >= EQEmu::versions::ClientVersion::RoF) { switch (rank) { case 0: { rank = 5; break; } // GUILD_MEMBER 0 case 1: { rank = 3; break; } // GUILD_OFFICER 1 case 2: { rank = 1; break; } // GUILD_LEADER 2 default: { break; } // GUILD_NONE } } SendAppearancePacket(AT_GuildRank, rank); } UpdateWho(); }
void Client::SendGuildRanks() { if (ClientVersion() < EQEmu::versions::ClientVersion::RoF) return; int permissions = 30 + 1; //Static number of permissions in all EQ clients as of May 2014 int ranks = 8 + 1; // Static number of RoF+ ranks as of May 2014 int j = 1; int i = 1; if(IsInAGuild()) { while(j < ranks) { while(i < permissions) { auto outapp = new EQApplicationPacket(OP_GuildUpdateURLAndChannel, sizeof(GuildUpdateRanks_Struct)); GuildUpdateRanks_Struct *guuacs = (GuildUpdateRanks_Struct*) outapp->pBuffer; //guuacs->Unknown0008 = this->GuildID(); strncpy(guuacs->Unknown0012, this->GetCleanName(), 64); guuacs->Action = 5; guuacs->RankID = j; guuacs->GuildID = this->GuildID(); guuacs->PermissionID = i; guuacs->PermissionVal = 1; guuacs->Unknown0089[0] = 0x2c; guuacs->Unknown0089[1] = 0x01; guuacs->Unknown0089[2] = 0x00; FastQueuePacket(&outapp); i++; } j++; i = 1; } } }
void ClientVersion::loadVersion(pugi::xml_node versionNode) { pugi::xml_attribute attribute; if (!(attribute = versionNode.attribute("name"))) { wxLogError(wxT("Node 'client' must contain 'name', 'data_directory' and 'otb' tags.")); return; } const std::string& versionName = attribute.as_string(); if (!(attribute = versionNode.attribute("data_directory"))) { wxLogError(wxT("Node 'client' must contain 'name', 'data_directory' and 'otb' tags.")); return; } const std::string& dataPath = attribute.as_string(); if (!(attribute = versionNode.attribute("otb"))) { wxLogError(wxT("Node 'client' must contain 'name', 'data_directory' and 'otb' tags.")); return; } const std::string& otbVersionName = attribute.as_string(); if (otb_versions.find(otbVersionName) == otb_versions.end()) { wxLogError(wxT("Node 'client' 'otb' tag is invalid (couldn't find this otb version).")); return; } ClientVersion* version = newd ClientVersion(otb_versions[otbVersionName], versionName, wxstr(dataPath)); bool should_be_default = versionNode.attribute("default").as_bool(); version->visible = versionNode.attribute("visible").as_bool(); for (pugi::xml_node childNode = versionNode.first_child(); childNode; childNode = childNode.next_sibling()) { const std::string& childName = as_lower_str(childNode.name()); if (childName == "otbm") { if (!(attribute = childNode.attribute("version"))) { wxLogError(wxT("Node 'otbm' missing version.")); continue; } int32_t otbmVersion = pugi::cast<int32_t>(attribute.value()) - 1; if (otbmVersion < MAP_OTBM_1 || otbmVersion > MAP_OTBM_4) { wxLogError(wxT("Node 'otbm' unsupported version.")); continue; } if (childNode.attribute("preffered").as_bool() || version->preferred_map_version == MAP_OTBM_UNKNOWN) { version->preferred_map_version = static_cast<MapVersionID>(otbmVersion); } version->map_versions_supported.push_back(version->preferred_map_version); } else if (childName == "fucked_up_charges") { version->usesFuckedUpCharges = true; } else if (childName == "data") { if (!(attribute = childNode.attribute("sprversion"))) { wxLogError(wxT("Node 'data' does not have 'datversion' / 'sprversion' tags.")); continue; } const std::string& sprVersion = attribute.as_string(); if (!(attribute = childNode.attribute("datversion"))) { wxLogError(wxT("Node 'data' does not have 'datversion' / 'sprversion' tags.")); continue; } const std::string& datVersion = attribute.as_string(); ClientData client_data = {DAT_VERSION_74, SPR_VERSION_70, 0, 0}; if (datVersion == "7.4") { client_data.datVersion = DAT_VERSION_74; } else if (datVersion == "7.6") { client_data.datVersion = DAT_VERSION_76; } else if (datVersion == "7.8") { client_data.datVersion = DAT_VERSION_78; } else if (datVersion == "8.6") { client_data.datVersion = DAT_VERSION_86; } else if (datVersion == "9.6") { client_data.datVersion = DAT_VERSION_96; } else { wxLogError(wxT("Node 'data' 'datversion' is invalid (7.4, 7.6, 7.8, 8.6 and 9.6 are supported)")); continue; } if (sprVersion == "7.0") { client_data.sprVersion = SPR_VERSION_70; } else if (sprVersion == "9.6") { client_data.sprVersion = SPR_VERSION_96; } else { wxLogError(wxT("Node 'data' 'sprversion' is invalid (7.0 and 9.6 are supported)")); continue; } if (!(attribute = childNode.attribute("dat")) || !wxString(attribute.as_string(), wxConvUTF8).ToULong((unsigned long*)&client_data.datSignature, 16)) { wxLogError(wxT("Node 'data' 'dat' tag is not hex-formatted.")); continue; } if (!(attribute = childNode.attribute("spr")) || !wxString(attribute.as_string(), wxConvUTF8).ToULong((unsigned long*)&client_data.sprSignature, 16)) { wxLogError(wxT("Node 'data' 'spr' tag is not hex-formatted.")); continue; } version->data_versions.push_back(client_data); } } if (client_versions[version->getID()] != nullptr) { wxString error; error << wxT("Duplicate version id ") << version->getID() << wxT(", discarding version '") << version->name << wxT("'."); wxLogError(error); delete version; return; } client_versions[version->getID()] = version; if (should_be_default) latest_version = version; }
uint32 Client::CalcCurrentWeight() { const EQEmu::ItemData* TempItem = nullptr; EQEmu::ItemInstance* ins = nullptr; uint32 Total = 0; int x; for (x = EQEmu::invslot::POSSESSIONS_BEGIN; x <= EQEmu::invslot::POSSESSIONS_END; x++) { TempItem = 0; ins = GetInv().GetItem(x); if (ins) { TempItem = ins->GetItem(); } if (TempItem) { Total += TempItem->Weight; } } for (x = EQEmu::invbag::GENERAL_BAGS_BEGIN; x <= EQEmu::invbag::CURSOR_BAG_END; x++) { int TmpWeight = 0; TempItem = 0; ins = GetInv().GetItem(x); if (ins) { TempItem = ins->GetItem(); } if (TempItem) { TmpWeight = TempItem->Weight; } if (TmpWeight > 0) { // this code indicates that weight redux bags can only be in the first general inventory slot to be effective... // is this correct? or can we scan for the highest weight redux and use that? (need client verifications) int bagslot = EQEmu::invslot::slotGeneral1; int reduction = 0; for (int m = EQEmu::invbag::GENERAL_BAGS_BEGIN + EQEmu::invbag::SLOT_COUNT; m <= EQEmu::invbag::CURSOR_BAG_END; m += EQEmu::invbag::SLOT_COUNT) { if (x >= m) { bagslot += 1; } } EQEmu::ItemInstance* baginst = GetInv().GetItem(bagslot); if (baginst && baginst->GetItem() && baginst->IsClassBag()) { reduction = baginst->GetItem()->BagWR; } if (reduction > 0) { TmpWeight -= TmpWeight * reduction / 100; } Total += TmpWeight; } } //TODO: coin weight reduction (from purses, etc), since client already calculates it /* From the Wiki http://www.eqemulator.net/wiki/wikka.php?wakka=EQEmuDBSchemaitems under bagwr (thanks Trevius): Interestingly, you can also have bags that reduce coin weight. However, in order to set bags to reduce coin weight, you MUST set the Item ID somewhere between 17201 and 17230. This is hard coded into the client. The client is set to have certain coin weight reduction on a per Item ID basis within this range. The best way to create an new item to reduce coin weight is to examine existing bags in this range. Search for the words "coin purse" with the #finditem command in game and the Bag WR setting on those bags is the amount they will reduce coin weight. It is easiest to overwrite one of those bags if you wish to create one with the same weight reduction amount for coins. You can use other Item IDs in this range for setting coin weight reduction, but by using an existing item, at least you will know the amount the client will reduce it by before you create it. This is the ONLY instance I have seen where the client is hard coded to particular Item IDs to set a certain property for an item. It is very odd. */ // SoD+ client has no weight for coin if (EQEmu::behavior::StaticLookup(EQEmu::versions::ConvertClientVersionToMobVersion(ClientVersion()))->CoinHasWeight) { Total += (m_pp.platinum + m_pp.gold + m_pp.silver + m_pp.copper) / 4; } float Packrat = (float)spellbonuses.Packrat + (float)aabonuses.Packrat + (float)itembonuses.Packrat; if (Packrat > 0) { Total = (uint32)((float)Total * (1.0f - ((Packrat * 1.0f) / 100.0f))); //AndMetal: 1% per level, up to 5% (calculated from Titanium client). verified thru client that it reduces coin weight by the same % } //without casting to float & back to uint32, this didn't work right return Total; }
int32 Client::CalcBaseMana() { int ConvertedWisInt = 0; int MindLesserFactor, MindFactor; int WisInt = 0; int base_mana = 0; int wisint_mana = 0; int32 max_m = 0; switch (GetCasterClass()) { case 'I': WisInt = GetINT(); if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) { if (WisInt > 100) { ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100); if (WisInt > 201) { ConvertedWisInt -= ((WisInt - 201) * 5 / 4); } } else { ConvertedWisInt = WisInt; } auto base_data = database.GetBaseData(GetLevel(), GetClass()); if (base_data) { max_m = base_data->base_mana + (ConvertedWisInt * base_data->mana_factor) + (GetHeroicINT() * 10); } } else { if ((( WisInt - 199 ) / 2) > 0) { MindLesserFactor = ( WisInt - 199 ) / 2; } else { MindLesserFactor = 0; } MindFactor = WisInt - MindLesserFactor; if (WisInt > 100) { max_m = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40); } else { max_m = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100); } } break; case 'W': WisInt = GetWIS(); if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) { if (WisInt > 100) { ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100); if (WisInt > 201) { ConvertedWisInt -= ((WisInt - 201) * 5 / 4); } } else { ConvertedWisInt = WisInt; } auto base_data = database.GetBaseData(GetLevel(), GetClass()); if (base_data) { max_m = base_data->base_mana + (ConvertedWisInt * base_data->mana_factor) + (GetHeroicWIS() * 10); } } else { if ((( WisInt - 199 ) / 2) > 0) { MindLesserFactor = ( WisInt - 199 ) / 2; } else { MindLesserFactor = 0; } MindFactor = WisInt - MindLesserFactor; if (WisInt > 100) { max_m = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40); } else { max_m = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100); } } break; case 'N': { max_m = 0; break; } default: { Log(Logs::General, Logs::None, "Invalid Class '%c' in CalcMaxMana", GetCasterClass()); max_m = 0; break; } } #if EQDEBUG >= 11 Log(Logs::General, Logs::None, "Client::CalcBaseMana() called for %s - returning %d", GetName(), max_m); #endif return max_m; }