diff --git a/common/ruletypes.h b/common/ruletypes.h index b1f1e9551..93992327e 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -218,6 +218,7 @@ RULE_BOOL(Character, ItemExtraSkillDamageCalcAsPercent, false, "If enabled, appl RULE_BOOL(Character, UseForageCommonFood, true, "If enabled, use the common foods specified in the code.") RULE_INT(Character, ClearXTargetDelay, 10, "Seconds between uses of the #clearxtargets command (Set to 0 to disable)") RULE_BOOL(Character, PreventMountsFromZoning, false, "Enable to prevent mounts from zoning - Prior to December 15, 2004 this is enabled.") +RULE_BOOL(Character, GroupInvitesRequireTarget, false, "Enable to require players to have invitee on target (Disables /invite name) - Classic Style") RULE_CATEGORY_END() RULE_CATEGORY(Mercs) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index d06781867..5ae648cfa 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -7321,64 +7321,81 @@ void Client::Handle_OP_GroupInvite2(const EQApplicationPacket *app) } GroupInvite_Struct* gis = (GroupInvite_Struct*)app->pBuffer; + + Mob* invitee = nullptr; - Mob *Invitee = entity_list.GetMob(gis->invitee_name); + if (RuleB(Character, GroupInvitesRequireTarget)) { + // We can only invite the current target. + invitee = GetTarget(); + } else { + invitee = entity_list.GetMob(gis->invitee_name); + } - if (Invitee == this) - { + if (invitee == this) { MessageString(Chat::LightGray, GROUP_INVITEE_SELF); return; } - if (Invitee) - { - if (Invitee->IsClient()) - { - if (Invitee->CastToClient()->MercOnlyOrNoGroup() && !Invitee->IsRaidGrouped()) - { - if (app->GetOpcode() == OP_GroupInvite2) - { + if (invitee) { + if (invitee->IsClient()) { + if (invitee->CastToClient()->MercOnlyOrNoGroup() && !invitee->IsRaidGrouped()) { + if (app->GetOpcode() == OP_GroupInvite2) { //Make a new packet using all the same information but make sure it's a fixed GroupInvite opcode so we //Don't have to deal with GroupFollow2 crap. auto outapp = new EQApplicationPacket(OP_GroupInvite, sizeof(GroupInvite_Struct)); memcpy(outapp->pBuffer, app->pBuffer, outapp->size); - Invitee->CastToClient()->QueuePacket(outapp); + invitee->CastToClient()->QueuePacket(outapp); safe_delete(outapp); return; - } - else - { + } else { //The correct opcode, no reason to bother wasting time reconstructing the packet - Invitee->CastToClient()->QueuePacket(app); + invitee->CastToClient()->QueuePacket(app); } - } - else { + } else if (invitee->IsRaidGrouped()) { + Raid* inviter_raid = GetRaid(); + Raid* invitee_raid = invitee->CastToClient()->GetRaid(); + + bool leader = false; + + if (invitee_raid) { + leader = invitee_raid->IsGroupLeader(invitee->GetName()); + } + + if (inviter_raid != invitee_raid || leader) { + MessageString(Chat::Default, ALREADY_IN_GRP_RAID, invitee->GetCleanName()); + } else { + MessageString(Chat::Default, TARGET_ALREADY_IN_GROUP, invitee->GetCleanName()); + } + + return; + + } else { if (RuleB(Character, OnInviteReceiveAlreadyinGroupMessage)) { - if (!Invitee->CastToClient()->MercOnlyOrNoGroup()) { - Message(Chat::LightGray, "%s is already in another group.", Invitee->GetCleanName()); + if (!invitee->CastToClient()->MercOnlyOrNoGroup()) { + MessageString(Chat::Default, TARGET_ALREADY_IN_GROUP, invitee->GetCleanName()); } } } - } - else if (Invitee->IsBot()) { + } else if (invitee->IsBot()) { Client* inviter = entity_list.GetClientByName(gis->inviter_name); - if (inviter && inviter->IsRaidGrouped() && !Invitee->HasRaid()) { - Bot::ProcessRaidInvite(Invitee->CastToBot(), inviter, true); - } - else if (!Invitee->HasRaid()) { - Bot::ProcessBotGroupInvite(this, std::string(Invitee->GetName())); + if (inviter && inviter->IsRaidGrouped() && !invitee->HasRaid()) { + Bot::ProcessRaidInvite(invitee->CastToBot(), inviter, true); + } else if (!invitee->HasRaid()) { + Bot::ProcessBotGroupInvite(this, std::string(invitee->GetName())); } else { - MessageString(Chat::LightGray, ALREADY_IN_RAID, Invitee->GetCleanName()); + MessageString(Chat::LightGray, ALREADY_IN_RAID, invitee->GetCleanName()); } } - } - else - { - auto pack = new ServerPacket(ServerOP_GroupInvite, sizeof(GroupInvite_Struct)); - memcpy(pack->pBuffer, gis, sizeof(GroupInvite_Struct)); - worldserver.SendPacket(pack); - safe_delete(pack); + } else { + if (RuleB(Character, GroupInvitesRequireTarget)) { + Message(Chat::White, "You must target a player first to invite to join your group."); + } else { + auto pack = new ServerPacket(ServerOP_GroupInvite, sizeof(GroupInvite_Struct)); + memcpy(pack->pBuffer, gis, sizeof(GroupInvite_Struct)); + worldserver.SendPacket(pack); + safe_delete(pack); + } } return; } diff --git a/zone/string_ids.h b/zone/string_ids.h index 2adcc6f92..ca392b320 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -373,6 +373,7 @@ #define ALREADY_IN_YOUR_RAID 5077 //%1 is already in your raid. #define NOT_IN_YOUR_RAID 5082 //%1 is not in your raid. #define GAIN_RAIDEXP 5085 //You gained raid experience! +#define ALREADY_IN_GRP_RAID 5088 //% 1 rejects your invite because they are in a raid and you are not in theirs, or they are a raid group leader #define DUNGEON_SEALED 5141 //The gateway to the dungeon is sealed off to you. Perhaps you would be able to enter if you needed to adventure there. #define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure. #define SUCCOR_FAIL 5169 //The portal collapes before you can escape!