From aa0e53f5fcce901fd721301a010decf4f3dc471b Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 4 May 2024 19:07:17 -0400 Subject: [PATCH] [Messages] Add GM Status and Invulnerability Messages (#4266) * [Messages] Add GM Status and Invulnerability Messages * Update zoning.cpp * Finalize. * Update corpse.cpp * Update message --------- Co-authored-by: Akkadius --- world/shared_task_manager.cpp | 4 ++ zone/client_packet.cpp | 75 +++++++++++++++++++++------- zone/client_process.cpp | 48 +++++++++++------- zone/corpse.cpp | 43 ++++++++++------ zone/doors.cpp | 69 ++++++++++++++++++-------- zone/entity.cpp | 4 ++ zone/expedition_request.cpp | 4 ++ zone/object.cpp | 20 +++++--- zone/questmgr.cpp | 76 ++++++++++++++--------------- zone/spell_effects.cpp | 4 ++ zone/spells.cpp | 92 +++++++++++++++++++++++++++++------ zone/tasks.cpp | 4 ++ zone/tradeskills.cpp | 12 ++++- zone/trading.cpp | 12 +++++ zone/trap.cpp | 16 ++++-- zone/zoning.cpp | 30 +++++++----- 16 files changed, 364 insertions(+), 149 deletions(-) diff --git a/world/shared_task_manager.cpp b/world/shared_task_manager.cpp index 992c557b7..dab7ca6b8 100644 --- a/world/shared_task_manager.cpp +++ b/world/shared_task_manager.cpp @@ -1355,6 +1355,10 @@ bool SharedTaskManager::CanRequestSharedTask(uint32_t task_id, const SharedTaskR return false; } + if (is_gm) { + client_list.SendCharacterMessage(requester->CharID(), Chat::White, "Your GM Flag allows you to bypass shared task minimum player requirements."); + } + // check if party member count is above the maximum // todo: live creates the shared task but truncates members if it exceeds max (sorted by leader and raid group numbers) if (task.max_players > 0 && request.members.size() > task.max_players) { diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 806dc11c6..a15daeaf7 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -709,23 +709,28 @@ void Client::CompleteConnect() } case SE_Levitate: { - if (!zone->CanLevitate()) - { - if (!GetGM()) - { + if (!zone->CanLevitate()) { + if (!GetGM()) { SendAppearancePacket(AppearanceType::FlyMode, 0); BuffFadeByEffect(SE_Levitate); Message(Chat::Red, "You can't levitate in this zone."); + break; } + + Message(Chat::White, "Your GM Flag allows you to levitate in this zone."); } - else { - if (spell.limit_value[x1] == 1) { - SendAppearancePacket(AppearanceType::FlyMode, EQ::constants::GravityBehavior::LevitateWhileRunning, true, true); - } - else { - SendAppearancePacket(AppearanceType::FlyMode, EQ::constants::GravityBehavior::Levitating, true, true); - } - } + + SendAppearancePacket( + AppearanceType::FlyMode, + ( + spell.limit_value[x1] == 1 ? + EQ::constants::GravityBehavior::LevitateWhileRunning : + EQ::constants::GravityBehavior::Levitating + ), + true, + true + ); + break; } case SE_AddMeleeProc: @@ -2127,8 +2132,19 @@ void Client::Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app) if (item->MaxCharges != 0) charges = item->MaxCharges; - if (RuleB(Character, EnableDiscoveredItems) && !GetGM() && !IsDiscovered(item->ID)) { - DiscoverItem(item->ID); + if (RuleB(Character, EnableDiscoveredItems) && !IsDiscovered(item->ID)) { + if (!GetGM()) { + DiscoverItem(item->ID); + } else { + const std::string& item_link = database.CreateItemLink(item->ID); + Message( + Chat::White, + fmt::format( + "Your GM Flag prevents {} from being added to discovered items.", + item_link + ).c_str() + ); + } } EQ::ItemInstance *inst = database.CreateItem(item, charges); @@ -2682,8 +2698,19 @@ void Client::Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app) RecordPlayerEventLog(PlayerEvent::MERCHANT_PURCHASE, e); } - if (RuleB(Character, EnableDiscoveredItems) && !GetGM() && !IsDiscovered(item->ID)) { - DiscoverItem(item->ID); + if (RuleB(Character, EnableDiscoveredItems) && !IsDiscovered(item->ID)) { + if (!GetGM()) { + DiscoverItem(item->ID); + } else { + const std::string& item_link = database.CreateItemLink(item->ID); + Message( + Chat::White, + fmt::format( + "Your GM Flag prevents {} from being added to discovered items.", + item_link + ).c_str() + ); + } } EQ::ItemInstance *inst = database.CreateItem(item, charges); @@ -14214,8 +14241,20 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) RecordPlayerEventLog(PlayerEvent::MERCHANT_PURCHASE, e); } - if (RuleB(Character, EnableDiscoveredItems) && !GetGM() && !IsDiscovered(item_id)) { - DiscoverItem(item_id); + + if (RuleB(Character, EnableDiscoveredItems) && !IsDiscovered(item_id)) { + if (!GetGM()) { + DiscoverItem(item_id); + } else { + const std::string& item_link = database.CreateItemLink(item_id); + Message( + Chat::White, + fmt::format( + "Your GM Flag prevents {} from being added to discovered items.", + item_link + ).c_str() + ); + } } t1.stop(); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 828101b7a..63e3e0624 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -1895,31 +1895,45 @@ void Client::DoManaRegen() { void Client::DoStaminaHungerUpdate() { auto outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); - Stamina_Struct *sta = (Stamina_Struct *)outapp->pBuffer; + auto sta = (Stamina_Struct*) outapp->pBuffer; LogFood("hunger_level: [{}] thirst_level: [{}] before loss", m_pp.hunger_level, m_pp.thirst_level); - if (zone->GetZoneID() != 151 && !GetGM()) { - int loss = RuleI(Character, FoodLossPerUpdate); - if (GetHorseId() != 0) - loss *= 3; + if (zone->GetZoneID() != Zones::BAZAAR) { + if (!GetGM()) { + int loss = RuleI(Character, FoodLossPerUpdate); + if (GetHorseId() != 0) { + loss *= 3; + } - m_pp.hunger_level = EQ::Clamp(m_pp.hunger_level - loss, 0, 6000); - m_pp.thirst_level = EQ::Clamp(m_pp.thirst_level - loss, 0, 6000); - if (spellbonuses.hunger) { - m_pp.hunger_level = EQ::ClampLower(m_pp.hunger_level, 3500); - m_pp.thirst_level = EQ::ClampLower(m_pp.thirst_level, 3500); + m_pp.hunger_level = EQ::Clamp(m_pp.hunger_level - loss, 0, 6000); + m_pp.thirst_level = EQ::Clamp(m_pp.thirst_level - loss, 0, 6000); + + if (spellbonuses.hunger) { + m_pp.hunger_level = EQ::ClampLower(m_pp.hunger_level, 3500); + m_pp.thirst_level = EQ::ClampLower(m_pp.thirst_level, 3500); + } + + sta->food = m_pp.hunger_level; + sta->water = m_pp.thirst_level; + } else { + sta->food = 6000; + sta->water = 6000; + + Message(Chat::White, "Your GM Flag prevents you from consuming food or water."); } - sta->food = m_pp.hunger_level; - sta->water = m_pp.thirst_level; - } else { - // No auto food/drink consumption in the Bazaar - sta->food = 6000; + } else { // No auto food/drink consumption in the Bazaar + sta->food = 6000; sta->water = 6000; } - LogFood("Current hunger_level: [{}] = ([{}] minutes left) thirst_level: [{}] = ([{}] minutes left) - after loss", - m_pp.hunger_level, m_pp.hunger_level, m_pp.thirst_level, m_pp.thirst_level); + LogFood( + "Current hunger_level: [{}] = ([{}] minutes left) thirst_level: [{}] = ([{}] minutes left) - after loss", + m_pp.hunger_level, + m_pp.hunger_level, + m_pp.thirst_level, + m_pp.thirst_level + ); FastQueuePacket(&outapp); } diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 531ba6ddd..11dbb0782 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1159,10 +1159,10 @@ void Corpse::MakeLootRequestPackets(Client *c, const EQApplicationPacket *app) if (c->GetGM()) { if (c->Admin() >= AccountStatus::GMAdmin) { m_loot_request_type = LootRequestType::GMAllowed; - - } - else { + c->Message(Chat::White, "Your GM Status allows you to loot any items on this corpse."); + } else { m_loot_request_type = LootRequestType::GMPeek; + c->Message(Chat::White, "Your GM Flag allows you to look at the items on this corpse."); } } else { @@ -1615,14 +1615,21 @@ void Corpse::LootCorpseItem(Client *c, const EQApplicationPacket *app) // safe to ACK now c->QueuePacket(app); - if ( - !IsPlayerCorpse() && - RuleB(Character, EnableDiscoveredItems) && - c && - !c->GetGM() && - !c->IsDiscovered(inst->GetItem()->ID) - ) { - c->DiscoverItem(inst->GetItem()->ID); + if (!IsPlayerCorpse()) { + if (RuleB(Character, EnableDiscoveredItems) && c && !c->IsDiscovered(inst->GetItem()->ID)) { + if (!c->GetGM()) { + c->DiscoverItem(inst->GetItem()->ID); + } else { + const std::string& item_link = database.CreateItemLink(inst->GetItem()->ID); + c->Message( + Chat::White, + fmt::format( + "Your GM Flag prevents {} from being added to discovered items.", + item_link + ).c_str() + ); + } + } } if (zone->adv_data) { @@ -2287,10 +2294,14 @@ void Corpse::CastRezz(uint16 spell_id, Mob *caster) // Rez timer has expired, only GMs can rez at this point. (uses rezzable) if (!IsRezzable()) { - if (caster && caster->IsClient() && !caster->CastToClient()->GetGM()) { - caster->MessageString(Chat::White, REZZ_ALREADY_PENDING); - caster->MessageString(Chat::White, CORPSE_TOO_OLD); - return; + if (caster && caster->IsClient()) { + if (!caster->CastToClient()->GetGM()) { + caster->MessageString(Chat::White, REZZ_ALREADY_PENDING); + caster->MessageString(Chat::White, CORPSE_TOO_OLD); + return; + } + + caster->Message(Chat::White, "Your GM Flag allows you to resurrect this corpse."); } } @@ -2302,6 +2313,8 @@ void Corpse::CastRezz(uint16 spell_id, Mob *caster) if (c->GetGM()) { m_rezzed_experience = m_gm_rezzed_experience; m_gm_rezzed_experience = 0; + + c->Message(Chat::White, "Your GM Flag allows you to resurrect this corpse and return experience."); } } } diff --git a/zone/doors.cpp b/zone/doors.cpp index b297ad536..a98f90af0 100644 --- a/zone/doors.cpp +++ b/zone/doors.cpp @@ -286,13 +286,17 @@ void Doors::HandleClick(Client *sender, uint8 trigger) // enforce flags before they hit zoning process auto z = GetZone(m_destination_zone_name, 0); if (z && !z->flag_needed.empty() && Strings::IsNumber(z->flag_needed) && Strings::ToInt(z->flag_needed) == 1) { - if (!sender->GetGM() && !sender->HasZoneFlag(z->zoneidnumber)) { - LogInfo( - "Character [{}] does not have the flag to be in this zone [{}]!", - sender->GetCleanName(), - z->flag_needed - ); - sender->MessageString(Chat::LightBlue, DOORS_LOCKED); + if (!sender->HasZoneFlag(z->zoneidnumber)) { + if (!sender->GetGM()) { + LogInfo( + "Character [{}] does not have the flag to be in this zone [{}]!", + sender->GetCleanName(), + z->flag_needed + ); + sender->MessageString(Chat::LightBlue, DOORS_LOCKED); + } else { + sender->Message(Chat::White, "Your GM Flag allows you to use this door."); + } } } @@ -318,20 +322,40 @@ void Doors::HandleClick(Client *sender, uint8 trigger) /** * Guild Doors */ - if ((GetGuildID() > 0) && !sender->GetGM()) { + if (GetGuildID() > 0) { std::string guild_name; - char door_message[240]; + const bool has_guild_name = guild_mgr.GetGuildNameByID(m_guild_id, guild_name); + if (!sender->GetGM()) { + std::string door_message; - if (guild_mgr.GetGuildNameByID(m_guild_id, guild_name)) { - sprintf(door_message, "Only members of the <%s> guild may enter here", guild_name.c_str()); - } - else { - strcpy(door_message, "Door is locked by an unknown guild"); - } + if (has_guild_name) { + door_message = fmt::format( + "Only members of the <{}> guild may enter here.", + guild_name + ); + } else { + door_message = "Door is locked by an unknown guild."; + } - sender->Message(Chat::LightBlue, door_message); - safe_delete(outapp); - return; + sender->Message(Chat::LightBlue, door_message.c_str()); + safe_delete(outapp); + return; + } else { + sender->Message( + Chat::White, + fmt::format( + "Your GM Flag allows you to use this door{}.", + ( + has_guild_name ? + fmt::format( + " assigned to the <{}> guild", + guild_name + ) : + "" + ) + ).c_str() + ); + } } /** @@ -515,8 +539,13 @@ void Doors::HandleClick(Client *sender, uint8 trigger) } // teleport door - if (((m_open_type == 57) || (m_open_type == 58)) && HasDestinationZone()) { - bool has_key_required = (required_key_item && ((required_key_item == player_key) || sender->GetGM())); + if (EQ::ValueWithin(m_open_type, 57, 58) && HasDestinationZone()) { + bool has_key_required = (required_key_item && required_key_item == player_key); + + if (sender->GetGM() && has_key_required) { + has_key_required = false; + sender->Message(Chat::White, "Your GM Flag allows you to open this door without a key."); + } if (IsDestinationZoneSame() && (!required_key_item)) { if (!disable_add_to_key_ring) { diff --git a/zone/entity.cpp b/zone/entity.cpp index a5654f3b5..44a93453f 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1654,6 +1654,10 @@ void EntityList::QueueClientsByTarget(Mob *sender, const EQApplicationPacket *ap if (inspect_buffs) { // if inspect_buffs is true we're sending a mob's buffs to those with the LAA Send = clear_target_window; if (c->GetGM() || RuleB(Spells, AlwaysSendTargetsBuffs)) { + if (c->GetGM()) { + c->Message(Chat::White, "Your GM Flag allows you to always see your targets' buffs."); + } + Send = !clear_target_window; } else if (c->IsRaidGrouped()) { Raid *raid = c->GetRaid(); diff --git a/zone/expedition_request.cpp b/zone/expedition_request.cpp index 0a19532a6..4c00f00dc 100644 --- a/zone/expedition_request.cpp +++ b/zone/expedition_request.cpp @@ -359,5 +359,9 @@ bool ExpeditionRequest::IsPlayerCountValidated() }); } + if (gm_bypass) { + m_requester->Message(Chat::White, "Your GM Status allows you to bypass expedition minimum and maximum player restrictions."); + } + return requirements_met; } diff --git a/zone/object.cpp b/zone/object.cpp index 48dfc7d0f..c92406ca7 100644 --- a/zone/object.cpp +++ b/zone/object.cpp @@ -642,13 +642,19 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object) sender->SendItemPacket(EQ::invslot::slotCursor, m_inst, ItemPacketTrade); // Could be an undiscovered ground_spawn - if ( - m_ground_spawn && - RuleB(Character, EnableDiscoveredItems) && - !sender->GetGM() && - !sender->IsDiscovered(item->ID) - ) { - sender->DiscoverItem(item->ID); + if (m_ground_spawn && RuleB(Character, EnableDiscoveredItems) && !sender->IsDiscovered(item->ID)) { + if (!sender->GetGM()) { + sender->DiscoverItem(item->ID); + } else { + const std::string& item_link = database.CreateItemLink(item->ID); + sender->Message( + Chat::White, + fmt::format( + "Your GM Flag prevents {} from being added to discovered items.", + item_link + ).c_str() + ); + } } if (cursor_delete) { // delete the item if it's a duplicate lore. We have to do this because the client expects the item packet diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index bbd408e1c..ee3e3f7f1 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2961,50 +2961,50 @@ bool QuestManager::createBot(const char *name, const char *lastname, uint8 level auto spawned_bot_count = Bot::SpawnedBotCount(initiator->CharacterID()); - if ( - bot_spawn_limit >= 0 && - spawned_bot_count >= bot_spawn_limit && - !initiator->GetGM() - ) { - std::string message; - if (bot_spawn_limit) { - message = fmt::format( - "You cannot have more than {} spawned bot{}.", - bot_spawn_limit, - bot_spawn_limit != 1 ? "s" : "" - ); - } else { - message = "You are not currently allowed to spawn any bots."; - } + if (bot_spawn_limit >= 0 && spawned_bot_count >= bot_spawn_limit) { + if (!initiator->GetGM()) { + std::string message; + if (bot_spawn_limit) { + message = fmt::format( + "You cannot have more than {} spawned bot{}.", + bot_spawn_limit, + bot_spawn_limit != 1 ? "s" : "" + ); + } else { + message = "You are not currently allowed to spawn any bots."; + } - initiator->Message(Chat::White, message.c_str()); - return false; + initiator->Message(Chat::White, message.c_str()); + return false; + } else { + initiator->Message(Chat::White, "Your GM Flag allows you to bypass bot spawn limits."); + } } auto spawned_bot_count_class = Bot::SpawnedBotCount(initiator->CharacterID(), botclass); - if ( - bot_spawn_limit_class >= 0 && - spawned_bot_count_class >= bot_spawn_limit_class && - !initiator->GetGM() - ) { - std::string message; - if (bot_spawn_limit_class) { - message = fmt::format( - "You cannot have more than {} spawned {} bot{}.", - bot_spawn_limit_class, - GetClassIDName(botclass), - bot_spawn_limit_class != 1 ? "s" : "" - ); - } else { - message = fmt::format( - "You are not currently allowed to spawn any {} bots.", - GetClassIDName(botclass) - ); - } + if (bot_spawn_limit_class >= 0 && spawned_bot_count_class >= bot_spawn_limit_class) { + if (!initiator->GetGM()) { + std::string message; + if (bot_spawn_limit_class) { + message = fmt::format( + "You cannot have more than {} spawned {} bot{}.", + bot_spawn_limit_class, + GetClassIDName(botclass), + bot_spawn_limit_class != 1 ? "s" : "" + ); + } else { + message = fmt::format( + "You are not currently allowed to spawn any {} bots.", + GetClassIDName(botclass) + ); + } - initiator->Message(Chat::White, message.c_str()); - return false; + initiator->Message(Chat::White, message.c_str()); + return false; + } else { + initiator->Message(Chat::White, "Your GM Flag allows you to bypass bot class-based spawn limits."); + } } std::string test_name = name; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 4f57c90ee..94d877909 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -946,6 +946,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #endif if (IsClient()) { if (CastToClient()->GetGM() || RuleB(Character, BindAnywhere)) { + if (CastToClient()->GetGM()) { + Message(Chat::White, "Your GM Flag allows you to bind anywhere."); + } + auto action_packet = new EQApplicationPacket(OP_Action, sizeof(Action_Struct)); Action_Struct* action = (Action_Struct*) action_packet->pBuffer; diff --git a/zone/spells.cpp b/zone/spells.cpp index 96f16285d..f03fd6bbd 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -682,16 +682,34 @@ bool Mob::DoCastingChecksZoneRestrictions(bool check_on_casting, int32 spell_id) If the spell is a initiated from SpellFinished, then check at start of SpellFinished. */ - bool ignore_if_npc_or_gm = false; - if (!IsClient() || (IsClient() && CastToClient()->GetGM())) { - ignore_if_npc_or_gm = true; + bool bypass_casting_restrictions = false; + + if (!IsClient()) { + bypass_casting_restrictions = true; + } + + if (IsClient() && CastToClient()->GetGM()) { + bypass_casting_restrictions = true; + Message( + Chat::White, + fmt::format( + "Your GM Flag allows you to bypass zone casting restrictions and cast {} in this zone.", + Saylink::Silent( + fmt::format( + "#castspell {}", + spell_id + ), + GetSpellName(spell_id) + ) + ).c_str() + ); } /* Zone ares that prevent blocked spells from being cast. If on cast iniated then check any mob casting, if on spellfinished only check if is from client. */ - if ((check_on_casting && !ignore_if_npc_or_gm) || (!check_on_casting && IsClient())) { + if ((check_on_casting && !bypass_casting_restrictions) || (!check_on_casting && IsClient())) { if (zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) { if (IsClient()) { if (!CastToClient()->GetGM()) { @@ -707,6 +725,19 @@ bool Mob::DoCastingChecksZoneRestrictions(bool check_on_casting, int32 spell_id) LogSpells("Spell casting canceled [{}] : can not cast in this zone location blocked spell.", spell_id); } else { + Message( + Chat::White, + fmt::format( + "Your GM Flag allows you to bypass zone blocked spells and cast {} in this zone.", + Saylink::Silent( + fmt::format( + "#castspell {}", + spell_id + ), + GetSpellName(spell_id) + ) + ).c_str() + ); LogSpells("GM Cast Blocked Spell: [{}] (ID [{}])", GetSpellName(spell_id), spell_id); } } @@ -716,7 +747,7 @@ bool Mob::DoCastingChecksZoneRestrictions(bool check_on_casting, int32 spell_id) /* Zones where you can not use levitate spells. */ - if (!ignore_if_npc_or_gm && !zone->CanLevitate() && IsEffectInSpell(spell_id, SE_Levitate)) { //check on spellfinished. + if (!bypass_casting_restrictions && !zone->CanLevitate() && IsEffectInSpell(spell_id, SE_Levitate)) { //check on spellfinished. Message(Chat::Red, "You have entered an area where levitation effects do not function."); LogSpells("Spell casting canceled [{}] : can not cast levitation in this zone.", spell_id); return false; @@ -745,11 +776,15 @@ bool Mob::DoCastingChecksZoneRestrictions(bool check_on_casting, int32 spell_id) /* Zones where you can not cast out door only spells. This is only checked when casting is completed. */ - if (!ignore_if_npc_or_gm && spells[spell_id].zone_type == 1 && !zone->CanCastOutdoor()) { - if (IsClient() && !CastToClient()->GetGM()) { - MessageString(Chat::Red, CAST_OUTDOORS); - LogSpells("Spell casting canceled [{}] : can not cast outdoors.", spell_id); - return false; + if (!bypass_casting_restrictions && spells[spell_id].zone_type == 1 && !zone->CanCastOutdoor()) { + if (IsClient()) { + if (!CastToClient()->GetGM()) { + MessageString(Chat::Red, CAST_OUTDOORS); + LogSpells("Spell casting canceled [{}] : can not cast outdoors.", spell_id); + return false; + } else { + Message(Chat::White, "Your GM Flag allows you to cast outdoor spells when indoors."); + } } } } @@ -1066,6 +1101,7 @@ bool Client::CheckFizzle(uint16 spell_id) { // GMs don't fizzle if (GetGM()) { + Message(Chat::White, "Your GM Flag prevents you from fizzling."); return true; } @@ -1648,9 +1684,12 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo if(!HasInstrument) { // if the instrument is missing, log it and interrupt the song LogSpells("Song [{}]: Canceled. Missing required instrument [{}]", spell_id, component); - if(c->GetGM()) - c->Message(Chat::White, "Your GM status allows you to finish casting even though you're missing a required instrument."); - else { + if (c->GetGM()) { + c->Message( + Chat::White, + "Your GM Flag allows you to finish casting even though you're missing a required instrument." + ); + } else { InterruptSpell(); return; } @@ -1686,9 +1725,12 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo } // end reagent loop if (missingreags) { - if(c->GetGM()) - c->Message(Chat::White, "Your GM status allows you to finish casting even though you're missing required components."); - else { + if (c->GetGM()) { + c->Message( + Chat::White, + "Your GM Flag allows you to finish casting even though you're missing required components." + ); + } else { InterruptSpell(); return; } @@ -4050,6 +4092,24 @@ bool Mob::SpellOnTarget( (spelltar != this && spelltar->DivineAura()) || (spelltar == this && spelltar->DivineAura() && !IsCastNotStandingSpell(spell_id)) ) { + if (IsClient()) { + Message( + Chat::White, + fmt::format( + "Your spell {} has failed to land on {} because {} are invulnerable.", + Saylink::Silent( + fmt::format( + "#castspell {}", + spell_id + ), + spells[spell_id].name + ), + GetTargetDescription(spelltar), + spelltar == this ? "you" : "they" + ).c_str() + ); + } + LogSpells("Casting spell [{}] on [{}] aborted: they are invulnerable", spell_id, spelltar->GetName()); safe_delete(action_packet); return false; diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 47bccd0a3..4562497d7 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -123,6 +123,10 @@ bool Client::HasTaskRequestCooldownTimer() task_request_timer.Disable(); } + if (GetGM()) { + Message(Chat::White, "Your GM Flag prevents you from having a task request cooldown."); + } + return (!GetGM() && task_request_timer.Enabled()); } diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 14ea244c5..8b4c35d16 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -1087,7 +1087,17 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { const EQ::ItemData* item = nullptr; - if (((spec->tradeskill==75) || GetGM() || (chance > res)) || zone->random.Roll(aa_chance)) { + if ( + ( + spec->tradeskill == EQ::skills::SkillRemoveTraps || + GetGM() || + (chance > res) + ) || + zone->random.Roll(aa_chance) + ) { + if (GetGM()) { + Message(Chat::White, "Your GM Flag gives you a 100% chance to succeed in combining this tradeskill."); + } success_modifier = 1; diff --git a/zone/trading.cpp b/zone/trading.cpp index 3ec9be522..b932e8a0d 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -814,6 +814,18 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st ((is_pet && (!bagitem->IsQuestItem() || pets_can_take_quest_items) || !is_pet)))) { + if (GetGM()) { + const std::string& item_link = database.CreateItemLink(bagitem->ID); + Message( + Chat::White, + fmt::format( + "Your GM Flag allows you to give {} to {}.", + item_link, + GetTargetDescription(tradingWith) + ).c_str() + ); + } + auto loot_drop_entry = LootdropEntriesRepository::NewNpcEntity(); loot_drop_entry.equip_item = 1; loot_drop_entry.item_charges = static_cast(baginst->GetCharges()); diff --git a/zone/trap.cpp b/zone/trap.cpp index 9f6cd4530..fb72dc38f 100644 --- a/zone/trap.cpp +++ b/zone/trap.cpp @@ -92,10 +92,14 @@ bool Trap::Process() { if (chkarea_timer.Enabled() && chkarea_timer.Check() && !reset_timer.Enabled()) { - Mob* trigger = entity_list.GetTrapTrigger(this); - if (trigger && !(trigger->IsClient() && trigger->CastToClient()->GetGM())) - { - Trigger(trigger); + Mob* m = entity_list.GetTrapTrigger(this); + const bool is_gm_client = m->IsClient() && m->CastToClient()->GetGM(); + if (m && !is_gm_client) { + Trigger(m); + } + + if (is_gm_client) { + m->Message(Chat::White, "Your GM Flag prevents you from triggering a trap."); } } else if (reset_timer.Enabled() && reset_timer.Check()) @@ -315,6 +319,10 @@ Mob* EntityList::GetTrapTrigger(Trap* trap) Log(Logs::General, Logs::Traps, "%s is about to trigger trap %d of chance %d. diff: %0.2f maxdist: %0.2f zdiff: %0.2f maxzdiff: %0.2f", cur->GetName(), trap->trap_id, trap->chance, (diff.x*diff.x + diff.y*diff.y), maxdist, diff.z, trap->maxzdiff); return cur; } + + if (cur->GetGM()) { + cur->Message(Chat::White, "Your GM Flag prevents you from triggering a trap."); + } } else { diff --git a/zone/zoning.cpp b/zone/zoning.cpp index aad72eac4..5de4dba6c 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -373,8 +373,8 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { } if (content_service.GetCurrentExpansion() >= Expansion::Classic && GetGM()) { - LogInfo("[{}] Bypassing Expansion zone checks because GM status is set", GetCleanName()); - Message(Chat::Yellow, "Bypassing Expansion zone checks because GM status is set"); + LogInfo("[{}] Bypassing zone expansion checks because GM Flag is set", GetCleanName()); + Message(Chat::White, "Your GM Flag allows you to bypass zone expansion checks."); } if (zoning_message == ZoningMessage::ZoneSuccess) { @@ -1369,7 +1369,7 @@ bool Client::CanEnterZone(const std::string& zone_short_name, int16 instance_ver return false; } - if (GetLevel() < z->min_level) { + if (!GetGM() && GetLevel() < z->min_level) { LogInfo( "Character [{}] does not meet minimum level requirement ([{}] < [{}])!", GetCleanName(), @@ -1379,7 +1379,7 @@ bool Client::CanEnterZone(const std::string& zone_short_name, int16 instance_ver return false; } - if (GetLevel() > z->max_level) { + if (!GetGM() && GetLevel() > z->max_level) { LogInfo( "Character [{}] does not meet maximum level requirement ([{}] > [{}])!", GetCleanName(), @@ -1399,15 +1399,19 @@ bool Client::CanEnterZone(const std::string& zone_short_name, int16 instance_ver return false; } - if (!z->flag_needed.empty() && Strings::IsNumber(z->flag_needed) && Strings::ToBool(z->flag_needed)) { - if (!GetGM() && !HasZoneFlag(z->zoneidnumber)) { - LogInfo( - "Character [{}] does not have the flag to be in this zone [{}]!", - GetCleanName(), - z->flag_needed - ); - return false; - } + if ( + !GetGM() && + !z->flag_needed.empty() && + Strings::IsNumber(z->flag_needed) && + Strings::ToBool(z->flag_needed) && + !HasZoneFlag(z->zoneidnumber) + ) { + LogInfo( + "Character [{}] does not have the flag to be in this zone [{}]!", + GetCleanName(), + z->flag_needed + ); + return false; } return true;