diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 53d7021ba..3fe2430db 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -35,6 +35,7 @@ static const uint32 MAX_MERC = 100; static const uint32 MAX_MERC_GRADES = 10; static const uint32 MAX_MERC_STANCES = 10; static const uint32 BLOCKED_BUFF_COUNT = 20; +static const uint32 QUESTREWARD_COUNT = 8; /* @@ -2180,14 +2181,7 @@ struct QuestReward_Struct /*024*/ uint32 silver; // Gives silver to the client /*028*/ uint32 gold; // Gives gold to the client /*032*/ uint32 platinum; // Gives platinum to the client - /*036*/ uint32 item_id; - /*040*/ uint32 unknown040; - /*044*/ uint32 unknown044; - /*048*/ uint32 unknown048; - /*052*/ uint32 unknown052; - /*056*/ uint32 unknown056; - /*060*/ uint32 unknown060; - /*064*/ uint32 unknown064; + /*036*/ int32 item_id[QUESTREWARD_COUNT]; // -1 for nothing /*068*/ }; diff --git a/zone/attack.cpp b/zone/attack.cpp index 911f22dfb..308592041 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -726,18 +726,18 @@ int Mob::GetClassRaceACBonus() } int weight = IsClient() ? CastToClient()->CalcCurrentWeight()/10 : 0; if (weight < hardcap - 1) { - int temp = level + 5; + double temp = level + 5; if (weight > softcap) { - double redux = (weight - softcap) * 6.66667; + double redux = static_cast(weight - softcap) * 6.66667; redux = (100.0 - std::min(100.0, redux)) * 0.01; - temp = std::max(0, static_cast(temp * redux)); + temp = std::max(0.0, temp * redux); } - ac_bonus = (4 * temp) / 3; + ac_bonus = static_cast((4.0 * temp) / 3.0); } else if (weight > hardcap + 1) { - int temp = level + 5; - double multiplier = std::min(1.0, (weight - (hardcap - 10.0)) / 100.0); - temp = (4 * temp) / 3; + double temp = level + 5; + double multiplier = std::min(1.0, (weight - (static_cast(hardcap) - 10.0)) / 100.0); + temp = (4.0 * temp) / 3.0; ac_bonus -= static_cast(temp * multiplier); } } diff --git a/zone/client.cpp b/zone/client.cpp index 9ea27869a..6b366afba 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -8533,13 +8533,13 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, memset(outapp->pBuffer, 0, sizeof(QuestReward_Struct)); QuestReward_Struct* qr = (QuestReward_Struct*)outapp->pBuffer; - qr->mob_id = target->GetID(); // Entity ID for the from mob name + qr->mob_id = target ? target->GetID() : 0; // Entity ID for the from mob name qr->target_id = GetID(); // The Client ID (this) qr->copper = copper; qr->silver = silver; qr->gold = gold; qr->platinum = platinum; - qr->item_id = itemid; + qr->item_id[0] = itemid; qr->exp_reward = exp; if (copper > 0 || silver > 0 || gold > 0 || platinum > 0) @@ -8550,7 +8550,7 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, if (faction) { - if (target->IsNPC()) + if (target && target->IsNPC()) { int32 nfl_id = target->CastToNPC()->GetNPCFactionID(); SetFactionLevel(CharacterID(), nfl_id, GetBaseClass(), GetBaseRace(), GetDeity(), true); @@ -8566,6 +8566,42 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, safe_delete(outapp); } +void Client::QuestReward(Mob* target, const QuestReward_Struct &reward, bool faction) +{ + auto outapp = new EQApplicationPacket(OP_Sound, sizeof(QuestReward_Struct)); + memset(outapp->pBuffer, 0, sizeof(QuestReward_Struct)); + QuestReward_Struct* qr = (QuestReward_Struct*)outapp->pBuffer; + + memcpy(qr, &reward, sizeof(QuestReward_Struct)); + + // not set in caller because reasons + qr->mob_id = target ? target->GetID() : 0; // Entity ID for the from mob name + + if (reward.copper > 0 || reward.silver > 0 || reward.gold > 0 || reward.platinum > 0) + AddMoneyToPP(reward.copper, reward.silver, reward.gold, reward.platinum, false); + + for (int i = 0; i < QUESTREWARD_COUNT; ++i) + if (reward.item_id[i] > 0) + SummonItem(reward.item_id[i], 0, 0, 0, 0, 0, 0, false, EQEmu::invslot::slotCursor); + + if (faction) + { + if (target && target->IsNPC()) + { + int32 nfl_id = target->CastToNPC()->GetNPCFactionID(); + SetFactionLevel(CharacterID(), nfl_id, GetBaseClass(), GetBaseRace(), GetDeity(), true); + qr->faction = target->CastToNPC()->GetPrimaryFaction(); + qr->faction_mod = 1; // Too lazy to get real value, not sure if this is even used by client anyhow. + } + } + + if (reward.exp_reward> 0) + AddEXP(reward.exp_reward); + + QueuePacket(outapp, true, Client::CLIENT_CONNECTED); + safe_delete(outapp); +} + void Client::SendHPUpdateMarquee(){ if (!this || !this->IsClient() || !this->current_hp || !this->max_hp) return; diff --git a/zone/client.h b/zone/client.h index 4ff431520..300073bb5 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1282,6 +1282,7 @@ public: int32 GetMeleeDamage(Mob* other, bool GetMinDamage = false); void QuestReward(Mob* target, uint32 copper = 0, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0, uint32 itemid = 0, uint32 exp = 0, bool faction = false); + void QuestReward(Mob* target, const QuestReward_Struct &reward, bool faction); // TODO: Fix faction processing void ResetHPUpdateTimer() { hpupdate_timer.Start(); } diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index 2d61a59ec..255201090 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -336,6 +336,10 @@ int32 Client::CalcMaxHP() current_hp = curHP_cap; } } + + // hack fix for client health not reflecting server value + last_max_hp = 0; + return max_hp; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index ebc6bd38a..dfb7ab673 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -11130,6 +11130,11 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) break; } + if (player_to_invite_group && player_to_invite_group->IsGroupMember(this)) { + MessageString(Chat::Red, ALREADY_IN_PARTY); + break; + } + if (player_to_invite_group && !player_to_invite_group->IsLeader(player_to_invite)) { Message(Chat::Red, "You can only invite an ungrouped player or group leader to join your raid."); break; diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 14c009b12..9d0873360 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -1385,18 +1385,23 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { return; } - uint32 copper = 0; - uint32 silver = 0; - uint32 gold = 0; - uint32 platinum = 0; - uint32 itemid = 0; - uint32 exp = 0; + QuestReward_Struct quest_reward; + quest_reward.mob_id = 0; + quest_reward.target_id = self->GetID(); + quest_reward.copper = 0; + quest_reward.silver = 0; + quest_reward.gold = 0; + quest_reward.platinum = 0; + quest_reward.exp_reward = 0; + quest_reward.faction = 0; + quest_reward.faction_mod = 0; bool faction = false; + std::fill(std::begin(quest_reward.item_id), std::end(quest_reward.item_id), -1); auto cur = reward["copper"]; if (luabind::type(cur) != LUA_TNIL) { try { - copper = luabind::object_cast(cur); + quest_reward.copper = luabind::object_cast(cur); } catch (luabind::cast_failed &) { } } @@ -1404,7 +1409,7 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { cur = reward["silver"]; if (luabind::type(cur) != LUA_TNIL) { try { - silver = luabind::object_cast(cur); + quest_reward.silver = luabind::object_cast(cur); } catch (luabind::cast_failed &) { } } @@ -1412,7 +1417,7 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { cur = reward["gold"]; if (luabind::type(cur) != LUA_TNIL) { try { - gold = luabind::object_cast(cur); + quest_reward.gold = luabind::object_cast(cur); } catch (luabind::cast_failed &) { } } @@ -1420,7 +1425,7 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { cur = reward["platinum"]; if (luabind::type(cur) != LUA_TNIL) { try { - platinum = luabind::object_cast(cur); + quest_reward.platinum = luabind::object_cast(cur); } catch (luabind::cast_failed &) { } } @@ -1428,7 +1433,30 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { cur = reward["itemid"]; if (luabind::type(cur) != LUA_TNIL) { try { - itemid = luabind::object_cast(cur); + quest_reward.item_id[0] = luabind::object_cast(cur); + } catch (luabind::cast_failed &) { + } + } + + // if you define both an itemid and items table, the itemid is thrown away + // should we error? + cur = reward["items"]; + if (luabind::type(cur) == LUA_TTABLE) { + try { + // assume they defined a compatible table + for (int i = 1; i <= QUESTREWARD_COUNT; ++i) { + auto item = cur[i]; + int cur_value = -1; + if (luabind::type(item) != LUA_TNIL) { + try { + cur_value = luabind::object_cast(item); + } catch (luabind::cast_failed &) { + } + } else { + break; + } + quest_reward.item_id[i - 1] = cur_value; + } } catch (luabind::cast_failed &) { } } @@ -1436,7 +1464,7 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { cur = reward["exp"]; if (luabind::type(cur) != LUA_TNIL) { try { - exp = luabind::object_cast(cur); + quest_reward.exp_reward = luabind::object_cast(cur); } catch (luabind::cast_failed &) { } } @@ -1449,7 +1477,7 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { } } - self->QuestReward(target, copper, silver, gold, platinum, itemid, exp, faction); + self->QuestReward(target, quest_reward, faction); } bool Lua_Client::IsDead() { diff --git a/zone/mob.cpp b/zone/mob.cpp index 71e876938..a6de82eee 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -186,6 +186,7 @@ Mob::Mob( last_hp_percent = 0; last_hp = 0; + last_max_hp = 0; current_speed = base_runspeed; @@ -1334,6 +1335,16 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal * If our HP is different from last HP update call - let's update selves */ if (IsClient()) { + + // delay to allow the client to catch up on buff states + if (max_hp != last_max_hp) { + + last_max_hp = max_hp; + CastToClient()->hp_self_update_throttle_timer.Trigger(); + + return; + } + if (current_hp != last_hp || force_update_all) { /** @@ -1341,10 +1352,12 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal */ if (this->CastToClient()->hp_self_update_throttle_timer.Check() || force_update_all) { Log(Logs::General, Logs::HPUpdate, - "Mob::SendHPUpdate :: Update HP of self (%s) HP: %i last: %i skip_self: %s", + "Mob::SendHPUpdate :: Update HP of self (%s) HP: %i/%i last: %i/%i skip_self: %s", this->GetCleanName(), current_hp, + max_hp, last_hp, + last_max_hp, (skip_self ? "true" : "false") ); diff --git a/zone/mob.h b/zone/mob.h index d48f47e6d..7c095a001 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1526,6 +1526,7 @@ protected: int8 last_hp_percent; int32 last_hp; + int32 last_max_hp; int cur_wp; glm::vec4 m_CurrentWayPoint; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 693222e9f..63a4a113f 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -286,6 +286,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove dmg = -dmg; } + // hack fix for client health not reflecting server value + last_hp = 0; + //do any AAs apply to these spells? if(dmg < 0) { if (!PassCastRestriction(false, spells[spell_id].base2[i], true)) diff --git a/zone/string_ids.h b/zone/string_ids.h index 0948d13a8..25321b3c9 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -417,6 +417,7 @@ #define TARGET_PLAYER_FOR_GUILD_STATUS 12260 #define GROUP_INVITEE_NOT_FOUND 12268 //You must target a player or use /invite to invite someone to your group. #define GROUP_INVITEE_SELF 12270 //12270 You cannot invite yourself. +#define ALREADY_IN_PARTY 12272 //That person is already in your party. #define NO_LONGER_HIDDEN 12337 //You are no longer hidden. #define STOP_SNEAKING 12338 //You stop sneaking #define NOT_IN_CONTROL 12368 //You do not have control of yourself right now. diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 0bd18edbf..65f4fd099 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -771,7 +771,7 @@ void Mob::FixZ(int32 z_find_offset /*= 5*/, bool fix_client_z /*= false*/) { float Mob::GetZOffset() const { float offset = 3.125f; - switch (race) { + switch (GetModel()) { case RACE_BASILISK_436: offset = 0.577f; break;