[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 <akkadius1@gmail.com>
This commit is contained in:
Alex King 2024-05-04 19:07:17 -04:00 committed by GitHub
parent 34c27ebb2a
commit aa0e53f5fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 364 additions and 149 deletions

View File

@ -1355,6 +1355,10 @@ bool SharedTaskManager::CanRequestSharedTask(uint32_t task_id, const SharedTaskR
return false; 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 // 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) // 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) { if (task.max_players > 0 && request.members.size() > task.max_players) {

View File

@ -709,23 +709,28 @@ void Client::CompleteConnect()
} }
case SE_Levitate: case SE_Levitate:
{ {
if (!zone->CanLevitate()) if (!zone->CanLevitate()) {
{ if (!GetGM()) {
if (!GetGM())
{
SendAppearancePacket(AppearanceType::FlyMode, 0); SendAppearancePacket(AppearanceType::FlyMode, 0);
BuffFadeByEffect(SE_Levitate); BuffFadeByEffect(SE_Levitate);
Message(Chat::Red, "You can't levitate in this zone."); 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(
SendAppearancePacket(AppearanceType::FlyMode, EQ::constants::GravityBehavior::LevitateWhileRunning, true, true); AppearanceType::FlyMode,
} (
else { spell.limit_value[x1] == 1 ?
SendAppearancePacket(AppearanceType::FlyMode, EQ::constants::GravityBehavior::Levitating, true, true); EQ::constants::GravityBehavior::LevitateWhileRunning :
} EQ::constants::GravityBehavior::Levitating
} ),
true,
true
);
break; break;
} }
case SE_AddMeleeProc: case SE_AddMeleeProc:
@ -2127,8 +2132,19 @@ void Client::Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app)
if (item->MaxCharges != 0) if (item->MaxCharges != 0)
charges = item->MaxCharges; charges = item->MaxCharges;
if (RuleB(Character, EnableDiscoveredItems) && !GetGM() && !IsDiscovered(item->ID)) { if (RuleB(Character, EnableDiscoveredItems) && !IsDiscovered(item->ID)) {
DiscoverItem(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); EQ::ItemInstance *inst = database.CreateItem(item, charges);
@ -2682,8 +2698,19 @@ void Client::Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app)
RecordPlayerEventLog(PlayerEvent::MERCHANT_PURCHASE, e); RecordPlayerEventLog(PlayerEvent::MERCHANT_PURCHASE, e);
} }
if (RuleB(Character, EnableDiscoveredItems) && !GetGM() && !IsDiscovered(item->ID)) { if (RuleB(Character, EnableDiscoveredItems) && !IsDiscovered(item->ID)) {
DiscoverItem(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); EQ::ItemInstance *inst = database.CreateItem(item, charges);
@ -14214,8 +14241,20 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
RecordPlayerEventLog(PlayerEvent::MERCHANT_PURCHASE, e); 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(); t1.stop();

View File

@ -1895,31 +1895,45 @@ void Client::DoManaRegen() {
void Client::DoStaminaHungerUpdate() void Client::DoStaminaHungerUpdate()
{ {
auto outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); 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); LogFood("hunger_level: [{}] thirst_level: [{}] before loss", m_pp.hunger_level, m_pp.thirst_level);
if (zone->GetZoneID() != 151 && !GetGM()) { if (zone->GetZoneID() != Zones::BAZAAR) {
int loss = RuleI(Character, FoodLossPerUpdate); if (!GetGM()) {
if (GetHorseId() != 0) int loss = RuleI(Character, FoodLossPerUpdate);
loss *= 3; if (GetHorseId() != 0) {
loss *= 3;
}
m_pp.hunger_level = EQ::Clamp(m_pp.hunger_level - loss, 0, 6000); 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); 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); if (spellbonuses.hunger) {
m_pp.thirst_level = EQ::ClampLower(m_pp.thirst_level, 3500); 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; } else { // No auto food/drink consumption in the Bazaar
sta->water = m_pp.thirst_level; sta->food = 6000;
} else {
// No auto food/drink consumption in the Bazaar
sta->food = 6000;
sta->water = 6000; sta->water = 6000;
} }
LogFood("Current hunger_level: [{}] = ([{}] minutes left) thirst_level: [{}] = ([{}] minutes left) - after loss", LogFood(
m_pp.hunger_level, m_pp.hunger_level, m_pp.thirst_level, m_pp.thirst_level); "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); FastQueuePacket(&outapp);
} }

View File

@ -1159,10 +1159,10 @@ void Corpse::MakeLootRequestPackets(Client *c, const EQApplicationPacket *app)
if (c->GetGM()) { if (c->GetGM()) {
if (c->Admin() >= AccountStatus::GMAdmin) { if (c->Admin() >= AccountStatus::GMAdmin) {
m_loot_request_type = LootRequestType::GMAllowed; m_loot_request_type = LootRequestType::GMAllowed;
c->Message(Chat::White, "Your GM Status allows you to loot any items on this corpse.");
} } else {
else {
m_loot_request_type = LootRequestType::GMPeek; m_loot_request_type = LootRequestType::GMPeek;
c->Message(Chat::White, "Your GM Flag allows you to look at the items on this corpse.");
} }
} }
else { else {
@ -1615,14 +1615,21 @@ void Corpse::LootCorpseItem(Client *c, const EQApplicationPacket *app)
// safe to ACK now // safe to ACK now
c->QueuePacket(app); c->QueuePacket(app);
if ( if (!IsPlayerCorpse()) {
!IsPlayerCorpse() && if (RuleB(Character, EnableDiscoveredItems) && c && !c->IsDiscovered(inst->GetItem()->ID)) {
RuleB(Character, EnableDiscoveredItems) && if (!c->GetGM()) {
c && c->DiscoverItem(inst->GetItem()->ID);
!c->GetGM() && } else {
!c->IsDiscovered(inst->GetItem()->ID) const std::string& item_link = database.CreateItemLink(inst->GetItem()->ID);
) { c->Message(
c->DiscoverItem(inst->GetItem()->ID); Chat::White,
fmt::format(
"Your GM Flag prevents {} from being added to discovered items.",
item_link
).c_str()
);
}
}
} }
if (zone->adv_data) { 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) // Rez timer has expired, only GMs can rez at this point. (uses rezzable)
if (!IsRezzable()) { if (!IsRezzable()) {
if (caster && caster->IsClient() && !caster->CastToClient()->GetGM()) { if (caster && caster->IsClient()) {
caster->MessageString(Chat::White, REZZ_ALREADY_PENDING); if (!caster->CastToClient()->GetGM()) {
caster->MessageString(Chat::White, CORPSE_TOO_OLD); caster->MessageString(Chat::White, REZZ_ALREADY_PENDING);
return; 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()) { if (c->GetGM()) {
m_rezzed_experience = m_gm_rezzed_experience; m_rezzed_experience = m_gm_rezzed_experience;
m_gm_rezzed_experience = 0; m_gm_rezzed_experience = 0;
c->Message(Chat::White, "Your GM Flag allows you to resurrect this corpse and return experience.");
} }
} }
} }

View File

@ -286,13 +286,17 @@ void Doors::HandleClick(Client *sender, uint8 trigger)
// enforce flags before they hit zoning process // enforce flags before they hit zoning process
auto z = GetZone(m_destination_zone_name, 0); 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 (z && !z->flag_needed.empty() && Strings::IsNumber(z->flag_needed) && Strings::ToInt(z->flag_needed) == 1) {
if (!sender->GetGM() && !sender->HasZoneFlag(z->zoneidnumber)) { if (!sender->HasZoneFlag(z->zoneidnumber)) {
LogInfo( if (!sender->GetGM()) {
"Character [{}] does not have the flag to be in this zone [{}]!", LogInfo(
sender->GetCleanName(), "Character [{}] does not have the flag to be in this zone [{}]!",
z->flag_needed sender->GetCleanName(),
); z->flag_needed
sender->MessageString(Chat::LightBlue, DOORS_LOCKED); );
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 * Guild Doors
*/ */
if ((GetGuildID() > 0) && !sender->GetGM()) { if (GetGuildID() > 0) {
std::string guild_name; 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)) { if (has_guild_name) {
sprintf(door_message, "Only members of the <%s> guild may enter here", guild_name.c_str()); door_message = fmt::format(
} "Only members of the <{}> guild may enter here.",
else { guild_name
strcpy(door_message, "Door is locked by an unknown guild"); );
} } else {
door_message = "Door is locked by an unknown guild.";
}
sender->Message(Chat::LightBlue, door_message); sender->Message(Chat::LightBlue, door_message.c_str());
safe_delete(outapp); safe_delete(outapp);
return; 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 // teleport door
if (((m_open_type == 57) || (m_open_type == 58)) && HasDestinationZone()) { if (EQ::ValueWithin(m_open_type, 57, 58) && HasDestinationZone()) {
bool has_key_required = (required_key_item && ((required_key_item == player_key) || sender->GetGM())); 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 (IsDestinationZoneSame() && (!required_key_item)) {
if (!disable_add_to_key_ring) { if (!disable_add_to_key_ring) {

View File

@ -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 if (inspect_buffs) { // if inspect_buffs is true we're sending a mob's buffs to those with the LAA
Send = clear_target_window; Send = clear_target_window;
if (c->GetGM() || RuleB(Spells, AlwaysSendTargetsBuffs)) { 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; Send = !clear_target_window;
} else if (c->IsRaidGrouped()) { } else if (c->IsRaidGrouped()) {
Raid *raid = c->GetRaid(); Raid *raid = c->GetRaid();

View File

@ -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; return requirements_met;
} }

View File

@ -642,13 +642,19 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object)
sender->SendItemPacket(EQ::invslot::slotCursor, m_inst, ItemPacketTrade); sender->SendItemPacket(EQ::invslot::slotCursor, m_inst, ItemPacketTrade);
// Could be an undiscovered ground_spawn // Could be an undiscovered ground_spawn
if ( if (m_ground_spawn && RuleB(Character, EnableDiscoveredItems) && !sender->IsDiscovered(item->ID)) {
m_ground_spawn && if (!sender->GetGM()) {
RuleB(Character, EnableDiscoveredItems) && sender->DiscoverItem(item->ID);
!sender->GetGM() && } else {
!sender->IsDiscovered(item->ID) const std::string& item_link = database.CreateItemLink(item->ID);
) { sender->Message(
sender->DiscoverItem(item->ID); 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 if (cursor_delete) { // delete the item if it's a duplicate lore. We have to do this because the client expects the item packet

View File

@ -2961,50 +2961,50 @@ bool QuestManager::createBot(const char *name, const char *lastname, uint8 level
auto spawned_bot_count = Bot::SpawnedBotCount(initiator->CharacterID()); auto spawned_bot_count = Bot::SpawnedBotCount(initiator->CharacterID());
if ( if (bot_spawn_limit >= 0 && spawned_bot_count >= bot_spawn_limit) {
bot_spawn_limit >= 0 && if (!initiator->GetGM()) {
spawned_bot_count >= bot_spawn_limit && std::string message;
!initiator->GetGM() if (bot_spawn_limit) {
) { message = fmt::format(
std::string message; "You cannot have more than {} spawned bot{}.",
if (bot_spawn_limit) { bot_spawn_limit,
message = fmt::format( bot_spawn_limit != 1 ? "s" : ""
"You cannot have more than {} spawned bot{}.", );
bot_spawn_limit, } else {
bot_spawn_limit != 1 ? "s" : "" message = "You are not currently allowed to spawn any bots.";
); }
} else {
message = "You are not currently allowed to spawn any bots.";
}
initiator->Message(Chat::White, message.c_str()); initiator->Message(Chat::White, message.c_str());
return false; 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); auto spawned_bot_count_class = Bot::SpawnedBotCount(initiator->CharacterID(), botclass);
if ( if (bot_spawn_limit_class >= 0 && spawned_bot_count_class >= bot_spawn_limit_class) {
bot_spawn_limit_class >= 0 && if (!initiator->GetGM()) {
spawned_bot_count_class >= bot_spawn_limit_class && std::string message;
!initiator->GetGM() if (bot_spawn_limit_class) {
) { message = fmt::format(
std::string message; "You cannot have more than {} spawned {} bot{}.",
if (bot_spawn_limit_class) { bot_spawn_limit_class,
message = fmt::format( GetClassIDName(botclass),
"You cannot have more than {} spawned {} bot{}.", bot_spawn_limit_class != 1 ? "s" : ""
bot_spawn_limit_class, );
GetClassIDName(botclass), } else {
bot_spawn_limit_class != 1 ? "s" : "" message = fmt::format(
); "You are not currently allowed to spawn any {} bots.",
} else { GetClassIDName(botclass)
message = fmt::format( );
"You are not currently allowed to spawn any {} bots.", }
GetClassIDName(botclass)
);
}
initiator->Message(Chat::White, message.c_str()); initiator->Message(Chat::White, message.c_str());
return false; return false;
} else {
initiator->Message(Chat::White, "Your GM Flag allows you to bypass bot class-based spawn limits.");
}
} }
std::string test_name = name; std::string test_name = name;

View File

@ -946,6 +946,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
#endif #endif
if (IsClient()) { if (IsClient()) {
if (CastToClient()->GetGM() || RuleB(Character, BindAnywhere)) { if (CastToClient()->GetGM() || RuleB(Character, BindAnywhere)) {
if (CastToClient()->GetGM()) {
Message(Chat::White, "Your GM Flag allows you to bind anywhere.");
}
auto action_packet = auto action_packet =
new EQApplicationPacket(OP_Action, sizeof(Action_Struct)); new EQApplicationPacket(OP_Action, sizeof(Action_Struct));
Action_Struct* action = (Action_Struct*) action_packet->pBuffer; Action_Struct* action = (Action_Struct*) action_packet->pBuffer;

View File

@ -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. If the spell is a initiated from SpellFinished, then check at start of SpellFinished.
*/ */
bool ignore_if_npc_or_gm = false; bool bypass_casting_restrictions = false;
if (!IsClient() || (IsClient() && CastToClient()->GetGM())) {
ignore_if_npc_or_gm = true; 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. 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 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 (zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) {
if (IsClient()) { if (IsClient()) {
if (!CastToClient()->GetGM()) { 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); LogSpells("Spell casting canceled [{}] : can not cast in this zone location blocked spell.", spell_id);
} }
else { 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); 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. 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."); 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); LogSpells("Spell casting canceled [{}] : can not cast levitation in this zone.", spell_id);
return false; 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. 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 (!bypass_casting_restrictions && spells[spell_id].zone_type == 1 && !zone->CanCastOutdoor()) {
if (IsClient() && !CastToClient()->GetGM()) { if (IsClient()) {
MessageString(Chat::Red, CAST_OUTDOORS); if (!CastToClient()->GetGM()) {
LogSpells("Spell casting canceled [{}] : can not cast outdoors.", spell_id); MessageString(Chat::Red, CAST_OUTDOORS);
return false; 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 // GMs don't fizzle
if (GetGM()) { if (GetGM()) {
Message(Chat::White, "Your GM Flag prevents you from fizzling.");
return true; 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 if(!HasInstrument) { // if the instrument is missing, log it and interrupt the song
LogSpells("Song [{}]: Canceled. Missing required instrument [{}]", spell_id, component); LogSpells("Song [{}]: Canceled. Missing required instrument [{}]", spell_id, component);
if(c->GetGM()) if (c->GetGM()) {
c->Message(Chat::White, "Your GM status allows you to finish casting even though you're missing a required instrument."); c->Message(
else { Chat::White,
"Your GM Flag allows you to finish casting even though you're missing a required instrument."
);
} else {
InterruptSpell(); InterruptSpell();
return; return;
} }
@ -1686,9 +1725,12 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo
} // end reagent loop } // end reagent loop
if (missingreags) { if (missingreags) {
if(c->GetGM()) if (c->GetGM()) {
c->Message(Chat::White, "Your GM status allows you to finish casting even though you're missing required components."); c->Message(
else { Chat::White,
"Your GM Flag allows you to finish casting even though you're missing required components."
);
} else {
InterruptSpell(); InterruptSpell();
return; return;
} }
@ -4050,6 +4092,24 @@ bool Mob::SpellOnTarget(
(spelltar != this && spelltar->DivineAura()) || (spelltar != this && spelltar->DivineAura()) ||
(spelltar == this && spelltar->DivineAura() && !IsCastNotStandingSpell(spell_id)) (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()); LogSpells("Casting spell [{}] on [{}] aborted: they are invulnerable", spell_id, spelltar->GetName());
safe_delete(action_packet); safe_delete(action_packet);
return false; return false;

View File

@ -123,6 +123,10 @@ bool Client::HasTaskRequestCooldownTimer()
task_request_timer.Disable(); 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()); return (!GetGM() && task_request_timer.Enabled());
} }

View File

@ -1087,7 +1087,17 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
const EQ::ItemData* item = nullptr; 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; success_modifier = 1;

View File

@ -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 && (!bagitem->IsQuestItem() || pets_can_take_quest_items) ||
!is_pet)))) { !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(); auto loot_drop_entry = LootdropEntriesRepository::NewNpcEntity();
loot_drop_entry.equip_item = 1; loot_drop_entry.equip_item = 1;
loot_drop_entry.item_charges = static_cast<int8>(baginst->GetCharges()); loot_drop_entry.item_charges = static_cast<int8>(baginst->GetCharges());

View File

@ -92,10 +92,14 @@ bool Trap::Process()
{ {
if (chkarea_timer.Enabled() && chkarea_timer.Check() && !reset_timer.Enabled()) if (chkarea_timer.Enabled() && chkarea_timer.Check() && !reset_timer.Enabled())
{ {
Mob* trigger = entity_list.GetTrapTrigger(this); Mob* m = entity_list.GetTrapTrigger(this);
if (trigger && !(trigger->IsClient() && trigger->CastToClient()->GetGM())) const bool is_gm_client = m->IsClient() && m->CastToClient()->GetGM();
{ if (m && !is_gm_client) {
Trigger(trigger); 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()) 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); 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; return cur;
} }
if (cur->GetGM()) {
cur->Message(Chat::White, "Your GM Flag prevents you from triggering a trap.");
}
} }
else else
{ {

View File

@ -373,8 +373,8 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) {
} }
if (content_service.GetCurrentExpansion() >= Expansion::Classic && GetGM()) { if (content_service.GetCurrentExpansion() >= Expansion::Classic && GetGM()) {
LogInfo("[{}] Bypassing Expansion zone checks because GM status is set", GetCleanName()); LogInfo("[{}] Bypassing zone expansion checks because GM Flag is set", GetCleanName());
Message(Chat::Yellow, "Bypassing Expansion zone checks because GM status is set"); Message(Chat::White, "Your GM Flag allows you to bypass zone expansion checks.");
} }
if (zoning_message == ZoningMessage::ZoneSuccess) { if (zoning_message == ZoningMessage::ZoneSuccess) {
@ -1369,7 +1369,7 @@ bool Client::CanEnterZone(const std::string& zone_short_name, int16 instance_ver
return false; return false;
} }
if (GetLevel() < z->min_level) { if (!GetGM() && GetLevel() < z->min_level) {
LogInfo( LogInfo(
"Character [{}] does not meet minimum level requirement ([{}] < [{}])!", "Character [{}] does not meet minimum level requirement ([{}] < [{}])!",
GetCleanName(), GetCleanName(),
@ -1379,7 +1379,7 @@ bool Client::CanEnterZone(const std::string& zone_short_name, int16 instance_ver
return false; return false;
} }
if (GetLevel() > z->max_level) { if (!GetGM() && GetLevel() > z->max_level) {
LogInfo( LogInfo(
"Character [{}] does not meet maximum level requirement ([{}] > [{}])!", "Character [{}] does not meet maximum level requirement ([{}] > [{}])!",
GetCleanName(), GetCleanName(),
@ -1399,15 +1399,19 @@ bool Client::CanEnterZone(const std::string& zone_short_name, int16 instance_ver
return false; return false;
} }
if (!z->flag_needed.empty() && Strings::IsNumber(z->flag_needed) && Strings::ToBool(z->flag_needed)) { if (
if (!GetGM() && !HasZoneFlag(z->zoneidnumber)) { !GetGM() &&
LogInfo( !z->flag_needed.empty() &&
"Character [{}] does not have the flag to be in this zone [{}]!", Strings::IsNumber(z->flag_needed) &&
GetCleanName(), Strings::ToBool(z->flag_needed) &&
z->flag_needed !HasZoneFlag(z->zoneidnumber)
); ) {
return false; LogInfo(
} "Character [{}] does not have the flag to be in this zone [{}]!",
GetCleanName(),
z->flag_needed
);
return false;
} }
return true; return true;