Merge branch 'master' into ww

This commit is contained in:
splose 2021-01-03 18:58:00 -05:00 committed by GitHub
commit 02aa4302de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 322 additions and 74 deletions

View File

@ -120,6 +120,7 @@ namespace Logs {
Loot, Loot,
Expeditions, Expeditions,
DynamicZones, DynamicZones,
Group,
MaxCategoryID /* Don't Remove this */ MaxCategoryID /* Don't Remove this */
}; };
@ -199,6 +200,7 @@ namespace Logs {
"Loot", "Loot",
"Expeditions", "Expeditions",
"DynamicZones", "DynamicZones",
"Group",
}; };
} }

View File

@ -161,6 +161,16 @@
OutF(LogSys, Logs::Detail, Logs::Doors, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ OutF(LogSys, Logs::Detail, Logs::Doors, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0) } while (0)
#define LogGroup(message, ...) do {\
if (LogSys.log_settings[Logs::Group].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::Group, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogGroupDetail(message, ...) do {\
if (LogSys.log_settings[Logs::Group].is_category_enabled == 1)\
OutF(LogSys, Logs::Detail, Logs::Group, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogGuilds(message, ...) do {\ #define LogGuilds(message, ...) do {\
if (LogSys.log_settings[Logs::Guilds].is_category_enabled == 1)\ if (LogSys.log_settings[Logs::Guilds].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::Guilds, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ OutF(LogSys, Logs::General, Logs::Guilds, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
@ -719,6 +729,12 @@
#define LogDoorsDetail(message, ...) do {\ #define LogDoorsDetail(message, ...) do {\
} while (0) } while (0)
#define LogGroup(message, ...) do {\
} while (0)
#define LogGroupDetail(message, ...) do {\
} while (0)
#define LogGuilds(message, ...) do {\ #define LogGuilds(message, ...) do {\
} while (0) } while (0)

View File

@ -80,6 +80,7 @@
#define ServerOP_UpdateSpawn 0x003f #define ServerOP_UpdateSpawn 0x003f
#define ServerOP_SpawnStatusChange 0x0040 #define ServerOP_SpawnStatusChange 0x0040
#define ServerOP_DropClient 0x0041 // DropClient #define ServerOP_DropClient 0x0041 // DropClient
#define ServerOP_ChangeGroupLeader 0x0042
#define ServerOP_ReloadTasks 0x0060 #define ServerOP_ReloadTasks 0x0060
#define ServerOP_DepopAllPlayersCorpses 0x0061 #define ServerOP_DepopAllPlayersCorpses 0x0061
#define ServerOP_ReloadTitles 0x0062 #define ServerOP_ReloadTitles 0x0062
@ -861,6 +862,7 @@ struct ServerGroupLeave_Struct {
uint16 instance_id; uint16 instance_id;
uint32 gid; uint32 gid;
char member_name[64]; //kick this member from the group char member_name[64]; //kick this member from the group
bool checkleader;
}; };
struct ServerGroupJoin_Struct { struct ServerGroupJoin_Struct {
@ -870,10 +872,20 @@ struct ServerGroupJoin_Struct {
char member_name[64]; //this person is joining the group char member_name[64]; //this person is joining the group
}; };
struct ServerGroupLeader_Struct {
uint32 zoneid;
uint16 instance_id;
uint32 gid;
char leader_name[64];
char oldleader_name[64];
};
struct ServerForceGroupUpdate_Struct { struct ServerForceGroupUpdate_Struct {
uint32 origZoneID; uint32 origZoneID;
uint16 instance_id; uint16 instance_id;
uint32 gid; uint32 gid;
char leader_name[64];
char oldleader_name[64];
}; };
struct ServerGroupChannelMessage_Struct { struct ServerGroupChannelMessage_Struct {

View File

@ -277,6 +277,14 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
break; break;
} }
case ServerOP_ChangeGroupLeader: {
if (pack->size != sizeof(ServerGroupLeader_Struct)) {
break;
}
zoneserver_list.SendPacket(pack); //bounce it to all zones
break;
}
case ServerOP_RaidAdd: { case ServerOP_RaidAdd: {
if (pack->size != sizeof(ServerRaidGeneralAction_Struct)) if (pack->size != sizeof(ServerRaidGeneralAction_Struct))
break; break;

View File

@ -9988,3 +9988,25 @@ void Client::MovePCDynamicZone(const std::string& zone_name, int zone_version, b
auto zone_id = ZoneID(zone_name.c_str()); auto zone_id = ZoneID(zone_name.c_str());
MovePCDynamicZone(zone_id, zone_version, msg_if_invalid); MovePCDynamicZone(zone_id, zone_version, msg_if_invalid);
} }
void Client::Fling(float value, float target_x, float target_y, float target_z, bool ignore_los, bool clipping) {
BuffFadeByEffect(SE_Levitate);
if (CheckLosFN(target_x, target_y, target_z, 6.0f) || ignore_los) {
auto outapp_fling = new EQApplicationPacket(OP_Fling, sizeof(fling_struct));
fling_struct* flingTo = (fling_struct*)outapp_fling->pBuffer;
if(clipping)
flingTo->collision = 0;
else
flingTo->collision = -1;
flingTo->travel_time = -1;
flingTo->unk3 = 1;
flingTo->disable_fall_damage = 1;
flingTo->speed_z = value;
flingTo->new_y = target_y;
flingTo->new_x = target_x;
flingTo->new_z = target_z;
outapp_fling->priority = 6;
FastQueuePacket(&outapp_fling);
}
}

View File

@ -794,6 +794,8 @@ public:
uint32 GetCharMaxLevelFromQGlobal(); uint32 GetCharMaxLevelFromQGlobal();
uint32 GetCharMaxLevelFromBucket(); uint32 GetCharMaxLevelFromBucket();
void Fling(float value, float target_x, float target_y, float target_z, bool ignore_los = false, bool clipping = false);
inline bool IsStanding() const {return (playeraction == 0);} inline bool IsStanding() const {return (playeraction == 0);}
inline bool IsSitting() const {return (playeraction == 1);} inline bool IsSitting() const {return (playeraction == 1);}
inline bool IsCrouching() const {return (playeraction == 2);} inline bool IsCrouching() const {return (playeraction == 2);}

View File

@ -206,6 +206,7 @@ int command_init(void)
command_add("emotesearch", "Searches NPC Emotes", 80, command_emotesearch) || command_add("emotesearch", "Searches NPC Emotes", 80, command_emotesearch) ||
command_add("emoteview", "Lists all NPC Emotes", 80, command_emoteview) || command_add("emoteview", "Lists all NPC Emotes", 80, command_emoteview) ||
command_add("enablerecipe", "[recipe_id] - Enables a recipe using the recipe id.", 80, command_enablerecipe) || command_add("enablerecipe", "[recipe_id] - Enables a recipe using the recipe id.", 80, command_enablerecipe) ||
command_add("endurance", "Restores you or your target's endurance.", 50, command_endurance) ||
command_add("equipitem", "[slotid(0-21)] - Equip the item on your cursor into the specified slot", 50, command_equipitem) || command_add("equipitem", "[slotid(0-21)] - Equip the item on your cursor into the specified slot", 50, command_equipitem) ||
command_add("face", "- Change the face of your target", 80, command_face) || command_add("face", "- Change the face of your target", 80, command_face) ||
command_add("faction", "[Find (criteria | all ) | Review (criteria | all) | Reset (id)] - Resets Player's Faction", 80, command_faction) || command_add("faction", "[Find (criteria | all ) | Review (criteria | all) | Reset (id)] - Resets Player's Faction", 80, command_faction) ||
@ -748,6 +749,19 @@ void command_wwcast(Client *c, const Seperator *sep)
else else
c->Message(Chat::Yellow, "Usage: #wwcast <spellid>"); c->Message(Chat::Yellow, "Usage: #wwcast <spellid>");
} }
void command_endurance(Client *c, const Seperator *sep)
{
Mob *t;
t = c->GetTarget() ? c->GetTarget() : c;
if (t->IsClient())
t->CastToClient()->SetEndurance(t->CastToClient()->GetMaxEndurance());
else
t->SetEndurance(t->GetMaxEndurance());
t->Message(Chat::White, "Your endurance has been refilled.");
}
void command_setstat(Client* c, const Seperator* sep){ void command_setstat(Client* c, const Seperator* sep){
if(sep->arg[1][0] && sep->arg[2][0] && c->GetTarget()!=0 && c->GetTarget()->IsClient()){ if(sep->arg[1][0] && sep->arg[2][0] && c->GetTarget()!=0 && c->GetTarget()->IsClient()){
c->GetTarget()->CastToClient()->SetStats(atoi(sep->arg[1]),atoi(sep->arg[2])); c->GetTarget()->CastToClient()->SetStats(atoi(sep->arg[1]),atoi(sep->arg[2]));

View File

@ -99,6 +99,7 @@ void command_emote(Client *c, const Seperator *sep);
void command_emotesearch(Client* c, const Seperator *sep); void command_emotesearch(Client* c, const Seperator *sep);
void command_emoteview(Client* c, const Seperator *sep); void command_emoteview(Client* c, const Seperator *sep);
void command_enablerecipe(Client *c, const Seperator *sep); void command_enablerecipe(Client *c, const Seperator *sep);
void command_endurance(Client *c, const Seperator *sep);
void command_equipitem(Client *c, const Seperator *sep); void command_equipitem(Client *c, const Seperator *sep);
void command_face(Client *c, const Seperator *sep); void command_face(Client *c, const Seperator *sep);
void command_faction(Client *c, const Seperator *sep); void command_faction(Client *c, const Seperator *sep);

View File

@ -4209,7 +4209,7 @@ void EntityList::ForceGroupUpdate(uint32 gid)
} }
} }
void EntityList::SendGroupLeave(uint32 gid, const char *name) void EntityList::SendGroupLeave(uint32 gid, const char *name, bool checkleader)
{ {
auto it = client_list.begin(); auto it = client_list.begin();
while (it != client_list.end()) { while (it != client_list.end()) {
@ -4225,13 +4225,39 @@ void EntityList::SendGroupLeave(uint32 gid, const char *name)
gj->action = groupActLeave; gj->action = groupActLeave;
strcpy(gj->yourname, c->GetName()); strcpy(gj->yourname, c->GetName());
Mob *Leader = g->GetLeader(); Mob *Leader = g->GetLeader();
if (Leader) if (Leader) {
Leader->CastToClient()->GetGroupAAs(&gj->leader_aas); Leader->CastToClient()->GetGroupAAs(&gj->leader_aas);
}
c->QueuePacket(outapp); c->QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
g->DelMemberOOZ(name); g->DelMemberOOZ(name, checkleader);
if (g->IsLeader(c) && c->IsLFP()) if (g->IsLeader(c) && c->IsLFP()) {
c->UpdateLFP(); c->UpdateLFP();
}
}
}
}
++it;
}
}
void EntityList::SendGroupLeader(uint32 gid, const char *lname, const char *oldlname)
{
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second){
Group *g = nullptr;
g = it->second->GetGroup();
if (g) {
if (g->GetID() == gid) {
EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct));
GroupJoin_Struct* gj = (GroupJoin_Struct*) outapp->pBuffer;
gj->action = groupActMakeLeader;
strcpy(gj->membername, lname);
strcpy(gj->yourname, oldlname);
it->second->QueuePacket(outapp);
Log(Logs::Detail, Logs::Group, "SendGroupLeader(): Entity loop leader update packet sent to: %s .", it->second->GetName());
safe_delete(outapp);
} }
} }
} }
@ -4254,9 +4280,9 @@ void EntityList::SendGroupJoin(uint32 gid, const char *name)
gj->action = groupActJoin; gj->action = groupActJoin;
strcpy(gj->yourname, it->second->GetName()); strcpy(gj->yourname, it->second->GetName());
Mob *Leader = g->GetLeader(); Mob *Leader = g->GetLeader();
if (Leader) if (Leader) {
Leader->CastToClient()->GetGroupAAs(&gj->leader_aas); Leader->CastToClient()->GetGroupAAs(&gj->leader_aas);
}
it->second->QueuePacket(outapp); it->second->QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
} }

View File

@ -478,8 +478,9 @@ public:
void CameraEffect(uint32 duration, uint32 intensity); void CameraEffect(uint32 duration, uint32 intensity);
Mob* GetClosestMobByBodyType(Mob* sender, bodyType BodyType); Mob* GetClosestMobByBodyType(Mob* sender, bodyType BodyType);
void ForceGroupUpdate(uint32 gid); void ForceGroupUpdate(uint32 gid);
void SendGroupLeave(uint32 gid, const char *name); void SendGroupLeave(uint32 gid, const char *name, bool checkleader);
void SendGroupJoin(uint32 gid, const char *name); void SendGroupJoin(uint32 gid, const char *name);
void SendGroupLeader(uint32 gid, const char *lname, const char *oldlname);
void SaveAllClientsTaskState(); void SaveAllClientsTaskState();
void ReloadAllClientsTaskState(int TaskID=0); void ReloadAllClientsTaskState(int TaskID=0);

View File

@ -59,8 +59,12 @@ Group::Group(uint32 gid)
} }
if(gid != 0) { if(gid != 0) {
if(!LearnMembers()) if(!LearnMembers()) {
SetID(0); SetID(0);
}
if(GetLeader() != nullptr) {
SetOldLeaderName(GetLeaderName());
}
} }
for(int i = 0; i < MAX_MARKED_NPCS; ++i) for(int i = 0; i < MAX_MARKED_NPCS; ++i)
MarkedNPCs[i] = 0; MarkedNPCs[i] = 0;
@ -78,6 +82,8 @@ Group::Group(Mob* leader)
members[0] = leader; members[0] = leader;
leader->SetGrouped(true); leader->SetGrouped(true);
SetLeader(leader); SetLeader(leader);
SetOldLeaderName(leader->GetName());
Log(Logs::Detail, Logs::Group, "Group:Group() Setting OldLeader to: %s and Leader to: %s", GetOldLeaderName(), leader->GetName());
AssistTargetID = 0; AssistTargetID = 0;
TankTargetID = 0; TankTargetID = 0;
PullerTargetID = 0; PullerTargetID = 0;
@ -604,39 +610,61 @@ void Group::SendGroupJoinOOZ(Mob* NewMember) {
} }
bool Group::DelMemberOOZ(const char *Name) { bool Group::DelMemberOOZ(const char *Name, bool checkleader) {
if(!Name) return false; if (!Name) return false;
bool removed = false;
// If a member out of zone has disbanded, clear out their name. // If a member out of zone has disbanded, clear out their name.
// for (unsigned int i = 0; i < MAX_GROUP_MEMBERS; i++) {
for(unsigned int i = 0; i < MAX_GROUP_MEMBERS; i++) { if (!strcasecmp(Name, membername[i])) {
if(!strcasecmp(Name, membername[i]))
// This shouldn't be called if the member is in this zone. // This shouldn't be called if the member is in this zone.
if(!members[i]) { if (!members[i]) {
if(!strncmp(GetLeaderName(), Name, 64)) if (!strncmp(GetLeaderName(), Name, 64)) {
{
//TODO: Transfer leadership if leader disbands OOZ. //TODO: Transfer leadership if leader disbands OOZ.
UpdateGroupAAs(); UpdateGroupAAs();
} }
memset(membername[i], 0, 64); if (GroupCount() < 3) {
MemberRoles[i] = 0;
if(GroupCount() < 3)
{
UnDelegateMarkNPC(NPCMarkerName.c_str()); UnDelegateMarkNPC(NPCMarkerName.c_str());
if (GetLeader() && GetLeader()->IsClient() && GetLeader()->CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoD) { if (GetLeader() && GetLeader()->IsClient() &&
UnDelegateMainAssist(MainAssistName.c_str()); GetLeader()->CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoD) {
UnDelegateMainAssist(MainAssistName.c_str());
} }
ClearAllNPCMarks(); ClearAllNPCMarks();
} }
if (Name == mentoree_name) if (Name == mentoree_name) {
ClearGroupMentor(); ClearGroupMentor();
return true; }
memset(membername[i], 0, 64);
MemberRoles[i] = 0;
removed = true;
Log(Logs::Detail, Logs::Group, "DelMemberOOZ: Removed Member: %s", Name);
break;
} }
}
} }
return false; if (GroupCount() < 2) {
DisbandGroup();
return true;
}
if (checkleader) {
Log(Logs::Detail, Logs::Group, "DelMemberOOZ: Checking leader...");
if (strcmp(GetOldLeaderName(), Name) == 0 && GroupCount() >= 2) {
for (uint32 nl = 0; nl < MAX_GROUP_MEMBERS; nl++) {
if (members[nl]) {
if (members[nl]->IsClient()) {
ChangeLeader(members[nl]);
break;
}
}
}
}
}
return removed;
} }
bool Group::DelMember(Mob* oldmember, bool ignoresender) bool Group::DelMember(Mob* oldmember, bool ignoresender)
@ -646,16 +674,6 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender)
return false; return false;
} }
// TODO: fix this shit
// okay, so there is code below that tries to handle this. It does not.
// So instead of figuring it out now, lets just disband the group so the client doesn't
// sit there with a broken group and there isn't any group leader shuffling going on
// since the code below doesn't work.
if (oldmember == GetLeader()) {
DisbandGroup();
return true;
}
for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++)
{ {
if (members[i] == oldmember) if (members[i] == oldmember)
@ -664,44 +682,36 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender)
membername[i][0] = '\0'; membername[i][0] = '\0';
memset(membername[i],0,64); memset(membername[i],0,64);
MemberRoles[i] = 0; MemberRoles[i] = 0;
Log(Logs::Detail, Logs::Group, "DelMember: Removed Member: %s", oldmember->GetCleanName());
break; break;
} }
} }
/* This may seem pointless but the case above does not cover the following situation: if(GroupCount() < 2)
* Group has Leader a, member b, member c
* b and c are out of zone
* a disconnects/quits
* b or c zone back in and disconnects/quits
* a is still "leader" from GetLeader()'s perspective and will crash the zone when we DelMember(b)
* Ultimately we should think up a better solution to this.
*/
if(oldmember == GetLeader())
{ {
SetLeader(nullptr); DisbandGroup();
return true;
} }
//handle leader quitting group gracefully // If the leader has quit and we have 2 or more players left in group, we want to first check the zone the old leader was in for a new leader.
if (oldmember == GetLeader() && GroupCount() >= 2) // If a suitable replacement cannot be found, we need to go out of zone. If checkleader remains true after this method completes, another
// loop will be run in DelMemberOOZ.
bool checkleader = true;
if (strcmp(GetOldLeaderName(),oldmember->GetCleanName()) == 0 && GroupCount() >= 2)
{ {
for(uint32 nl = 0; nl < MAX_GROUP_MEMBERS; nl++) for(uint32 nl = 0; nl < MAX_GROUP_MEMBERS; nl++)
{ {
if(members[nl]) if(members[nl])
{ {
if (members[nl]->IsClient()) if (members[nl]->IsClient())
{ {
ChangeLeader(members[nl]); ChangeLeader(members[nl]);
checkleader = false;
break; break;
} }
} }
} }
} }
if (!GetLeaderName())
{
DisbandGroup();
return true;
}
auto pack = new ServerPacket(ServerOP_GroupLeave, sizeof(ServerGroupLeave_Struct)); auto pack = new ServerPacket(ServerOP_GroupLeave, sizeof(ServerGroupLeave_Struct));
ServerGroupLeave_Struct* gl = (ServerGroupLeave_Struct*)pack->pBuffer; ServerGroupLeave_Struct* gl = (ServerGroupLeave_Struct*)pack->pBuffer;
@ -709,6 +719,7 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender)
gl->zoneid = zone->GetZoneID(); gl->zoneid = zone->GetZoneID();
gl->instance_id = zone->GetInstanceID(); gl->instance_id = zone->GetInstanceID();
strcpy(gl->member_name, oldmember->GetCleanName()); strcpy(gl->member_name, oldmember->GetCleanName());
gl->checkleader = checkleader;
worldserver.SendPacket(pack); worldserver.SendPacket(pack);
safe_delete(pack); safe_delete(pack);
@ -800,6 +811,7 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender)
Bot::UpdateGroupCastingRoles(this); Bot::UpdateGroupCastingRoles(this);
#endif #endif
safe_delete(outapp);
return true; return true;
} }
@ -2330,17 +2342,16 @@ void Group::ChangeLeader(Mob* newleader)
// this changes the current group leader, notifies other members, and updates leadship AA // this changes the current group leader, notifies other members, and updates leadship AA
// if the new leader is invalid, do nothing // if the new leader is invalid, do nothing
if (!newleader || !newleader->IsClient()) if (!newleader) {
return; return;
}
Mob* oldleader = GetLeader();
auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct));
GroupJoin_Struct* gu = (GroupJoin_Struct*) outapp->pBuffer; GroupJoin_Struct* gu = (GroupJoin_Struct*) outapp->pBuffer;
gu->action = groupActMakeLeader; gu->action = groupActMakeLeader;
strcpy(gu->membername, newleader->GetName()); strcpy(gu->membername, newleader->GetName());
strcpy(gu->yourname, oldleader->GetName()); strcpy(gu->yourname, GetOldLeaderName());
SetLeader(newleader); SetLeader(newleader);
database.SetGroupLeaderName(GetID(), newleader->GetName()); database.SetGroupLeaderName(GetID(), newleader->GetName());
UpdateGroupAAs(); UpdateGroupAAs();
@ -2352,9 +2363,22 @@ void Group::ChangeLeader(Mob* newleader)
members[i]->CastToClient()->SendGroupLeaderChangePacket(newleader->GetName()); members[i]->CastToClient()->SendGroupLeaderChangePacket(newleader->GetName());
members[i]->CastToClient()->QueuePacket(outapp); members[i]->CastToClient()->QueuePacket(outapp);
Log(Logs::Detail, Logs::Group, "ChangeLeader(): Local leader update packet sent to: %s .", members[i]->GetName());
} }
} }
safe_delete(outapp); safe_delete(outapp);
Log(Logs::Detail, Logs::Group, "ChangeLeader(): Old Leader is: %s New leader is: %s", GetOldLeaderName(), newleader->GetName());
ServerPacket* pack = new ServerPacket(ServerOP_ChangeGroupLeader, sizeof(ServerGroupLeader_Struct));
ServerGroupLeader_Struct* fgu = (ServerGroupLeader_Struct*)pack->pBuffer;
fgu->zoneid = zone->GetZoneID();
fgu->gid = GetID();
strcpy(fgu->leader_name, newleader->GetName());
strcpy(fgu->oldleader_name, GetOldLeaderName());
worldserver.SendPacket(pack);
SetOldLeaderName(newleader->GetName());
} }
const char *Group::GetClientNameByIndex(uint8 index) const char *Group::GetClientNameByIndex(uint8 index)

View File

@ -37,6 +37,8 @@ public:
GroupIDConsumer() { id = 0; } GroupIDConsumer() { id = 0; }
GroupIDConsumer(uint32 gid) { id = gid; } GroupIDConsumer(uint32 gid) { id = gid; }
inline const uint32 GetID() const { return id; } inline const uint32 GetID() const { return id; }
void SetOldLeaderName(const char* oldleader) { strcpy(oldleadername, oldleader); }
char* GetOldLeaderName() { return oldleadername; }
protected: protected:
friend class EntityList; friend class EntityList;
@ -44,6 +46,7 @@ protected:
inline void SetID(uint32 set_id) { id = set_id; } inline void SetID(uint32 set_id) { id = set_id; }
private: private:
uint32 id; uint32 id;
char oldleadername[64]; // Keeps the previous leader name, so when the entity is destroyed we can still transfer leadership.
}; };
class Group : public GroupIDConsumer { class Group : public GroupIDConsumer {
@ -58,6 +61,7 @@ public:
void SendLeadershipAAUpdate(); void SendLeadershipAAUpdate();
void SendWorldGroup(uint32 zone_id,Mob* zoningmember); void SendWorldGroup(uint32 zone_id,Mob* zoningmember);
bool DelMemberOOZ(const char *Name); bool DelMemberOOZ(const char *Name);
bool DelMemberOOZ(const char *Name, bool checkleader);
bool DelMember(Mob* oldmember,bool ignoresender = false); bool DelMember(Mob* oldmember,bool ignoresender = false);
void DisbandGroup(bool joinraid = false); void DisbandGroup(bool joinraid = false);
void GetMemberList(std::list<Mob*>& member_list, bool clear_list = true); void GetMemberList(std::list<Mob*>& member_list, bool clear_list = true);

View File

@ -1154,9 +1154,34 @@ bool Client::AutoPutLootInInventory(EQ::ItemInstance& inst, bool try_worn, bool
} }
} }
} }
if( i == EQ::invslot::slotPrimary && m_inv[EQ::invslot::slotSecondary] ) {
uint8 instrument = m_inv[EQ::invslot::slotSecondary]->GetItem()->ItemType;
if(
instrument == EQ::item::ItemTypeWindInstrument ||
instrument == EQ::item::ItemTypeStringedInstrument ||
instrument == EQ::item::ItemTypeBrassInstrument ||
instrument == EQ::item::ItemTypePercussionInstrument
) {
LogInventory("Cannot equip a primary item with [{}] already in the secondary.", m_inv[EQ::invslot::slotSecondary]->GetItem()->Name);
continue; // Do not auto-equip Primary when instrument is in Secondary
}
}
if (i == EQ::invslot::slotSecondary && m_inv[EQ::invslot::slotPrimary]) { // check to see if primary slot is a two hander if (i == EQ::invslot::slotSecondary && m_inv[EQ::invslot::slotPrimary]) { // check to see if primary slot is a two hander
if (m_inv[EQ::invslot::slotPrimary]->GetItem()->IsType2HWeapon()) uint8 instrument = inst.GetItem()->ItemType;
if(
instrument == EQ::item::ItemTypeWindInstrument ||
instrument == EQ::item::ItemTypeStringedInstrument ||
instrument == EQ::item::ItemTypeBrassInstrument ||
instrument == EQ::item::ItemTypePercussionInstrument
) {
LogInventory("Cannot equip a secondary instrument with [{}] already in the primary.", m_inv[EQ::invslot::slotPrimary]->GetItem()->Name);
continue; // Do not auto-equip instrument in Secondary when Primary is equipped.
}
uint8 use = m_inv[EQ::invslot::slotPrimary]->GetItem()->ItemType;
if(use == EQ::item::ItemType2HSlash || use == EQ::item::ItemType2HBlunt || use == EQ::item::ItemType2HPiercing) {
continue; continue;
}
} }
if (i == EQ::invslot::slotSecondary && inst.IsWeapon() && !CanThisClassDualWield()) { if (i == EQ::invslot::slotSecondary && inst.IsWeapon() && !CanThisClassDualWield()) {
continue; continue;
@ -1169,7 +1194,6 @@ bool Client::AutoPutLootInInventory(EQ::ItemInstance& inst, bool try_worn, bool
if (worn_slot_material != EQ::textures::materialInvalid) { if (worn_slot_material != EQ::textures::materialInvalid) {
SendWearChange(worn_slot_material); SendWearChange(worn_slot_material);
} }
parse->EventItem(EVENT_EQUIP_ITEM, this, &inst, nullptr, "", i); parse->EventItem(EVENT_EQUIP_ITEM, this, &inst, nullptr, "", i);
return true; return true;
} }

View File

@ -329,6 +329,21 @@ uint32 Lua_Client::GetBindZoneID(int index) {
return self->GetBindZoneID(index); return self->GetBindZoneID(index);
} }
float Lua_Client::GetTargetRingX() {
Lua_Safe_Call_Real();
return self->GetTargetRingX();
}
float Lua_Client::GetTargetRingY() {
Lua_Safe_Call_Real();
return self->GetTargetRingY();
}
float Lua_Client::GetTargetRingZ() {
Lua_Safe_Call_Real();
return self->GetTargetRingZ();
}
void Lua_Client::MovePC(int zone, float x, float y, float z, float heading) { void Lua_Client::MovePC(int zone, float x, float y, float z, float heading) {
Lua_Safe_Call_Void(); Lua_Safe_Call_Void();
self->MovePC(zone, x, y, z, heading); self->MovePC(zone, x, y, z, heading);
@ -1877,6 +1892,21 @@ void Lua_Client::MovePCDynamicZone(std::string zone_name, int zone_version, bool
return self->MovePCDynamicZone(zone_name, zone_version, msg_if_invalid); return self->MovePCDynamicZone(zone_name, zone_version, msg_if_invalid);
} }
void Lua_Client::Fling(float value, float target_x, float target_y, float target_z) {
Lua_Safe_Call_Void();
self->Fling(value, target_x, target_y, target_z);
}
void Lua_Client::Fling(float value, float target_x, float target_y, float target_z, bool ignore_los) {
Lua_Safe_Call_Void();
self->Fling(value, target_x, target_y, target_z, ignore_los);
}
void Lua_Client::Fling(float value, float target_x, float target_y, float target_z, bool ignore_los, bool clipping) {
Lua_Safe_Call_Void();
self->Fling(value, target_x, target_y, target_z, ignore_los, clipping);
}
luabind::scope lua_register_client() { luabind::scope lua_register_client() {
return luabind::class_<Lua_Client, Lua_Mob>("Client") return luabind::class_<Lua_Client, Lua_Mob>("Client")
.def(luabind::constructor<>()) .def(luabind::constructor<>())
@ -1942,6 +1972,9 @@ luabind::scope lua_register_client() {
.def("GetBindHeading", (float(Lua_Client::*)(int))&Lua_Client::GetBindHeading) .def("GetBindHeading", (float(Lua_Client::*)(int))&Lua_Client::GetBindHeading)
.def("GetBindZoneID", (uint32(Lua_Client::*)(void))&Lua_Client::GetBindZoneID) .def("GetBindZoneID", (uint32(Lua_Client::*)(void))&Lua_Client::GetBindZoneID)
.def("GetBindZoneID", (uint32(Lua_Client::*)(int))&Lua_Client::GetBindZoneID) .def("GetBindZoneID", (uint32(Lua_Client::*)(int))&Lua_Client::GetBindZoneID)
.def("GetTargetRingX", (float(Lua_Client::*)(void))&Lua_Client::GetTargetRingX)
.def("GetTargetRingY", (float(Lua_Client::*)(void))&Lua_Client::GetTargetRingY)
.def("GetTargetRingZ", (float(Lua_Client::*)(void))&Lua_Client::GetTargetRingZ)
.def("SetPrimaryWeaponOrnamentation", (void(Lua_Client::*)(uint32))&Lua_Client::SetPrimaryWeaponOrnamentation) .def("SetPrimaryWeaponOrnamentation", (void(Lua_Client::*)(uint32))&Lua_Client::SetPrimaryWeaponOrnamentation)
.def("SetSecondaryWeaponOrnamentation", (void(Lua_Client::*)(uint32))&Lua_Client::SetSecondaryWeaponOrnamentation) .def("SetSecondaryWeaponOrnamentation", (void(Lua_Client::*)(uint32))&Lua_Client::SetSecondaryWeaponOrnamentation)
.def("MovePC", (void(Lua_Client::*)(int,float,float,float,float))&Lua_Client::MovePC) .def("MovePC", (void(Lua_Client::*)(int,float,float,float,float))&Lua_Client::MovePC)
@ -2204,7 +2237,10 @@ luabind::scope lua_register_client() {
.def("MovePCDynamicZone", (void(Lua_Client::*)(uint32, int, bool))&Lua_Client::MovePCDynamicZone) .def("MovePCDynamicZone", (void(Lua_Client::*)(uint32, int, bool))&Lua_Client::MovePCDynamicZone)
.def("MovePCDynamicZone", (void(Lua_Client::*)(std::string))&Lua_Client::MovePCDynamicZone) .def("MovePCDynamicZone", (void(Lua_Client::*)(std::string))&Lua_Client::MovePCDynamicZone)
.def("MovePCDynamicZone", (void(Lua_Client::*)(std::string, int))&Lua_Client::MovePCDynamicZone) .def("MovePCDynamicZone", (void(Lua_Client::*)(std::string, int))&Lua_Client::MovePCDynamicZone)
.def("MovePCDynamicZone", (void(Lua_Client::*)(std::string, int, bool))&Lua_Client::MovePCDynamicZone); .def("MovePCDynamicZone", (void(Lua_Client::*)(std::string, int, bool))&Lua_Client::MovePCDynamicZone)
.def("Fling", (void(Lua_Client::*)(float,float,float,float))&Lua_Client::Fling)
.def("Fling", (void(Lua_Client::*)(float,float,float,float,bool))&Lua_Client::Fling)
.def("Fling", (void(Lua_Client::*)(float,float,float,float,bool,bool))&Lua_Client::Fling);
} }
luabind::scope lua_register_inventory_where() { luabind::scope lua_register_inventory_where() {

View File

@ -92,6 +92,9 @@ public:
float GetBindHeading(int index); float GetBindHeading(int index);
uint32 GetBindZoneID(); uint32 GetBindZoneID();
uint32 GetBindZoneID(int index); uint32 GetBindZoneID(int index);
float GetTargetRingX();
float GetTargetRingY();
float GetTargetRingZ();
void MovePC(int zone, float x, float y, float z, float heading); void MovePC(int zone, float x, float y, float z, float heading);
void MovePCInstance(int zone, int instance, float x, float y, float z, float heading); void MovePCInstance(int zone, int instance, float x, float y, float z, float heading);
void MoveZone(const char *zone_short_name); void MoveZone(const char *zone_short_name);
@ -360,6 +363,9 @@ public:
void MovePCDynamicZone(std::string zone_name); void MovePCDynamicZone(std::string zone_name);
void MovePCDynamicZone(std::string zone_name, int zone_version); void MovePCDynamicZone(std::string zone_name, int zone_version);
void MovePCDynamicZone(std::string zone_name, int zone_version, bool msg_if_invalid); void MovePCDynamicZone(std::string zone_name, int zone_version, bool msg_if_invalid);
void Fling(float value, float target_x, float target_y, float target_z);
void Fling(float value, float target_x, float target_y, float target_z, bool ignore_los);
void Fling(float value, float target_x, float target_y, float target_z, bool ignore_los, bool clipping);
}; };
#endif #endif

View File

@ -7051,6 +7051,41 @@ XS(XS_Client_MovePCDynamicZone) {
XSRETURN_EMPTY; XSRETURN_EMPTY;
} }
XS(XS_Client_Fling); /* prototype to pass -Wmissing-prototypes */
XS(XS_Client_Fling) {
dXSARGS;
if (items < 5 || items > 7)
Perl_croak(aTHX_ "Usage: Client::Fling(THIS, value, target_x, target_y, target_z, ignore_los, clipping)");
{
Client* THIS;
float value = (float) SvNV(ST(1));
float target_x = (float) SvNV(ST(2));
float target_y = (float) SvNV(ST(3));
float target_z = (float) SvNV(ST(4));
bool ignore_los = false;
bool clipping = false;
if (sv_derived_from(ST(0), "Client")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(Client *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type Client");
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if (items > 5)
ignore_los = (bool) SvTRUE(ST(5));
if (items > 6)
clipping = (bool) SvTRUE(ST(6));
THIS->Fling(value, target_x, target_y, target_z, ignore_los, clipping);
}
XSRETURN_EMPTY;
}
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
#endif #endif
@ -7108,6 +7143,7 @@ XS(boot_Client) {
newXSproto(strcpy(buf, "Escape"), XS_Client_Escape, file, "$"); newXSproto(strcpy(buf, "Escape"), XS_Client_Escape, file, "$");
newXSproto(strcpy(buf, "ExpeditionMessage"), XS_Client_ExpeditionMessage, file, "$$$"); newXSproto(strcpy(buf, "ExpeditionMessage"), XS_Client_ExpeditionMessage, file, "$$$");
newXSproto(strcpy(buf, "FailTask"), XS_Client_FailTask, file, "$$"); newXSproto(strcpy(buf, "FailTask"), XS_Client_FailTask, file, "$$");
newXSproto(strcpy(buf, "Fling"), XS_Client_Fling, file, "$$$$$;$$");
newXSproto(strcpy(buf, "ForageItem"), XS_Client_ForageItem, file, "$"); newXSproto(strcpy(buf, "ForageItem"), XS_Client_ForageItem, file, "$");
newXSproto(strcpy(buf, "Freeze"), XS_Client_Freeze, file, "$"); newXSproto(strcpy(buf, "Freeze"), XS_Client_Freeze, file, "$");
newXSproto(strcpy(buf, "GetAAExp"), XS_Client_GetAAExp, file, "$"); newXSproto(strcpy(buf, "GetAAExp"), XS_Client_GetAAExp, file, "$");

View File

@ -2648,6 +2648,11 @@ void Client::SellToBuyer(const EQApplicationPacket *app) {
return; return;
} }
if(item->IsClassBag()) {
Message(Chat::Red, "That item is a Bag.");
return;
}
if(!item->Stackable) { if(!item->Stackable) {
for(uint32 i = 0; i < Quantity; i++) { for(uint32 i = 0; i < Quantity; i++) {
@ -3001,29 +3006,27 @@ void Client::UpdateBuyLine(const EQApplicationPacket *app) {
LogTrading("UpdateBuyLine: Char: [{}] BuySlot: [{}] ItemID [{}] [{}] Quantity [{}] Toggle: [{}] Price [{}] ItemCount [{}] LoreConflict [{}]", LogTrading("UpdateBuyLine: Char: [{}] BuySlot: [{}] ItemID [{}] [{}] Quantity [{}] Toggle: [{}] Price [{}] ItemCount [{}] LoreConflict [{}]",
GetName(), BuySlot, ItemID, item->Name, Quantity, ToggleOnOff, Price, ItemCount, LoreConflict); GetName(), BuySlot, ItemID, item->Name, Quantity, ToggleOnOff, Price, ItemCount, LoreConflict);
if((item->NoDrop != 0) && !LoreConflict && (Quantity > 0) && HasMoney(Quantity * Price) && ToggleOnOff && (ItemCount == 0)) { if((item->NoDrop != 0) && (!item->IsClassBag())&& !LoreConflict && (Quantity > 0) && HasMoney(Quantity * Price) && ToggleOnOff && (ItemCount == 0)) {
LogTrading("Adding to database"); LogTrading("Adding to database");
database.AddBuyLine(CharacterID(), BuySlot, ItemID, ItemName, Quantity, Price); database.AddBuyLine(CharacterID(), BuySlot, ItemID, ItemName, Quantity, Price);
QueuePacket(app); QueuePacket(app);
} }
else { else {
if(ItemCount > 0) if(ItemCount > 0) {
Message(Chat::Red, "Buy line %s disabled as Item Compensation is not currently supported.", ItemName); Message(Chat::Red, "Buy line %s disabled as Item Compensation is not currently supported.", ItemName);
} else if(Quantity <= 0) {
else if(Quantity <= 0)
Message(Chat::Red, "Buy line %s disabled as the quantity is invalid.", ItemName); Message(Chat::Red, "Buy line %s disabled as the quantity is invalid.", ItemName);
} else if(LoreConflict) {
else if(LoreConflict)
Message(Chat::Red, "Buy line %s disabled as the item is LORE and you have one already.", ItemName); Message(Chat::Red, "Buy line %s disabled as the item is LORE and you have one already.", ItemName);
} else if(item->NoDrop == 0) {
else if(item->NoDrop == 0)
Message(Chat::Red, "Buy line %s disabled as the item is NODROP.", ItemName); Message(Chat::Red, "Buy line %s disabled as the item is NODROP.", ItemName);
} else if(item->IsClassBag()) {
else if(ToggleOnOff) Message(Chat::Red, "Buy line %s disabled as the item is a Bag.", ItemName);
} else if(ToggleOnOff) {
Message(Chat::Red, "Buy line %s disabled due to insufficient funds.", ItemName); Message(Chat::Red, "Buy line %s disabled due to insufficient funds.", ItemName);
} else {
else
database.RemoveBuyLine(CharacterID(), BuySlot); database.RemoveBuyLine(CharacterID(), BuySlot);
}
auto outapp = new EQApplicationPacket(OP_Barter, 936); auto outapp = new EQApplicationPacket(OP_Barter, 936);

View File

@ -877,7 +877,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
if (gl->zoneid == zone->GetZoneID() && gl->instance_id == zone->GetInstanceID()) if (gl->zoneid == zone->GetZoneID() && gl->instance_id == zone->GetInstanceID())
break; break;
entity_list.SendGroupLeave(gl->gid, gl->member_name); entity_list.SendGroupLeave(gl->gid, gl->member_name, gl->checkleader);
} }
break; break;
} }
@ -1114,6 +1114,17 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
break; break;
} }
case ServerOP_ChangeGroupLeader: {
ServerGroupLeader_Struct *fgu = (ServerGroupLeader_Struct *) pack->pBuffer;
if (zone) {
if (fgu->zoneid == zone->GetZoneID()) {
break;
}
entity_list.SendGroupLeader(fgu->gid, fgu->leader_name, fgu->oldleader_name);
}
break;
}
case ServerOP_OOZGroupMessage: { case ServerOP_OOZGroupMessage: {
ServerGroupChannelMessage_Struct* gcm = (ServerGroupChannelMessage_Struct*)pack->pBuffer; ServerGroupChannelMessage_Struct* gcm = (ServerGroupChannelMessage_Struct*)pack->pBuffer;
if (zone) { if (zone) {