diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index d28478a97..c8f724a17 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -120,6 +120,7 @@ namespace Logs { Loot, Expeditions, DynamicZones, + Group, MaxCategoryID /* Don't Remove this */ }; @@ -199,6 +200,7 @@ namespace Logs { "Loot", "Expeditions", "DynamicZones", + "Group", }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index efe25dabe..e32b43b4f 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -161,6 +161,16 @@ OutF(LogSys, Logs::Detail, Logs::Doors, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } 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 {\ if (LogSys.log_settings[Logs::Guilds].is_category_enabled == 1)\ OutF(LogSys, Logs::General, Logs::Guilds, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ @@ -719,6 +729,12 @@ #define LogDoorsDetail(message, ...) do {\ } while (0) +#define LogGroup(message, ...) do {\ +} while (0) + +#define LogGroupDetail(message, ...) do {\ +} while (0) + #define LogGuilds(message, ...) do {\ } while (0) diff --git a/common/servertalk.h b/common/servertalk.h index 40be635ad..816807b56 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -80,6 +80,7 @@ #define ServerOP_UpdateSpawn 0x003f #define ServerOP_SpawnStatusChange 0x0040 #define ServerOP_DropClient 0x0041 // DropClient +#define ServerOP_ChangeGroupLeader 0x0042 #define ServerOP_ReloadTasks 0x0060 #define ServerOP_DepopAllPlayersCorpses 0x0061 #define ServerOP_ReloadTitles 0x0062 @@ -861,6 +862,7 @@ struct ServerGroupLeave_Struct { uint16 instance_id; uint32 gid; char member_name[64]; //kick this member from the group + bool checkleader; }; struct ServerGroupJoin_Struct { @@ -870,10 +872,20 @@ struct ServerGroupJoin_Struct { 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 { uint32 origZoneID; uint16 instance_id; uint32 gid; + char leader_name[64]; + char oldleader_name[64]; }; struct ServerGroupChannelMessage_Struct { diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index f09d08db1..58bd9f177 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -277,6 +277,14 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { break; } + case ServerOP_ChangeGroupLeader: { + if (pack->size != sizeof(ServerGroupLeader_Struct)) { + break; + } + zoneserver_list.SendPacket(pack); //bounce it to all zones + break; + } + case ServerOP_RaidAdd: { if (pack->size != sizeof(ServerRaidGeneralAction_Struct)) break; diff --git a/zone/client.cpp b/zone/client.cpp index 06f04ec1b..726d736e2 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -9988,3 +9988,25 @@ void Client::MovePCDynamicZone(const std::string& zone_name, int zone_version, b auto zone_id = ZoneID(zone_name.c_str()); 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); + } +} \ No newline at end of file diff --git a/zone/client.h b/zone/client.h index f2b1892c9..6829ce303 100644 --- a/zone/client.h +++ b/zone/client.h @@ -794,6 +794,8 @@ public: uint32 GetCharMaxLevelFromQGlobal(); 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 IsSitting() const {return (playeraction == 1);} inline bool IsCrouching() const {return (playeraction == 2);} diff --git a/zone/command.cpp b/zone/command.cpp index ade29ce00..7c98bc6fa 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -206,6 +206,7 @@ int command_init(void) command_add("emotesearch", "Searches NPC Emotes", 80, command_emotesearch) || 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("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("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) || @@ -748,6 +749,19 @@ void command_wwcast(Client *c, const Seperator *sep) else c->Message(Chat::Yellow, "Usage: #wwcast "); } +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){ 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])); diff --git a/zone/command.h b/zone/command.h index cb903a371..65544c510 100644 --- a/zone/command.h +++ b/zone/command.h @@ -99,6 +99,7 @@ void command_emote(Client *c, const Seperator *sep); void command_emotesearch(Client* c, const Seperator *sep); void command_emoteview(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_face(Client *c, const Seperator *sep); void command_faction(Client *c, const Seperator *sep); diff --git a/zone/entity.cpp b/zone/entity.cpp index 659048d35..fa370b389 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -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(); while (it != client_list.end()) { @@ -4225,13 +4225,39 @@ void EntityList::SendGroupLeave(uint32 gid, const char *name) gj->action = groupActLeave; strcpy(gj->yourname, c->GetName()); Mob *Leader = g->GetLeader(); - if (Leader) + if (Leader) { Leader->CastToClient()->GetGroupAAs(&gj->leader_aas); + } c->QueuePacket(outapp); safe_delete(outapp); - g->DelMemberOOZ(name); - if (g->IsLeader(c) && c->IsLFP()) + g->DelMemberOOZ(name, checkleader); + if (g->IsLeader(c) && c->IsLFP()) { 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; strcpy(gj->yourname, it->second->GetName()); Mob *Leader = g->GetLeader(); - if (Leader) + if (Leader) { Leader->CastToClient()->GetGroupAAs(&gj->leader_aas); - + } it->second->QueuePacket(outapp); safe_delete(outapp); } diff --git a/zone/entity.h b/zone/entity.h index 4b0c38e7f..5bb5981fa 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -478,8 +478,9 @@ public: void CameraEffect(uint32 duration, uint32 intensity); Mob* GetClosestMobByBodyType(Mob* sender, bodyType BodyType); 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 SendGroupLeader(uint32 gid, const char *lname, const char *oldlname); void SaveAllClientsTaskState(); void ReloadAllClientsTaskState(int TaskID=0); diff --git a/zone/groups.cpp b/zone/groups.cpp index 4b8e25d09..abc868460 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -59,8 +59,12 @@ Group::Group(uint32 gid) } if(gid != 0) { - if(!LearnMembers()) + if(!LearnMembers()) { SetID(0); + } + if(GetLeader() != nullptr) { + SetOldLeaderName(GetLeaderName()); + } } for(int i = 0; i < MAX_MARKED_NPCS; ++i) MarkedNPCs[i] = 0; @@ -78,6 +82,8 @@ Group::Group(Mob* leader) members[0] = leader; leader->SetGrouped(true); 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; TankTargetID = 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. - // - for(unsigned int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(!strcasecmp(Name, membername[i])) + for (unsigned int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (!strcasecmp(Name, membername[i])) { // This shouldn't be called if the member is in this zone. - if(!members[i]) { - if(!strncmp(GetLeaderName(), Name, 64)) - { + if (!members[i]) { + if (!strncmp(GetLeaderName(), Name, 64)) { //TODO: Transfer leadership if leader disbands OOZ. UpdateGroupAAs(); } - memset(membername[i], 0, 64); - MemberRoles[i] = 0; - if(GroupCount() < 3) - { + if (GroupCount() < 3) { UnDelegateMarkNPC(NPCMarkerName.c_str()); - if (GetLeader() && GetLeader()->IsClient() && GetLeader()->CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoD) { - UnDelegateMainAssist(MainAssistName.c_str()); + if (GetLeader() && GetLeader()->IsClient() && + GetLeader()->CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoD) { + UnDelegateMainAssist(MainAssistName.c_str()); } ClearAllNPCMarks(); } - if (Name == mentoree_name) + if (Name == mentoree_name) { 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) @@ -646,16 +674,6 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender) 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++) { if (members[i] == oldmember) @@ -664,44 +682,36 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender) membername[i][0] = '\0'; memset(membername[i],0,64); MemberRoles[i] = 0; + Log(Logs::Detail, Logs::Group, "DelMember: Removed Member: %s", oldmember->GetCleanName()); break; } } - /* This may seem pointless but the case above does not cover the following situation: - * 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()) + if(GroupCount() < 2) { - SetLeader(nullptr); + DisbandGroup(); + return true; } - //handle leader quitting group gracefully - if (oldmember == GetLeader() && GroupCount() >= 2) + // 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 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++) { - if(members[nl]) + if(members[nl]) { if (members[nl]->IsClient()) { ChangeLeader(members[nl]); + checkleader = false; break; } } } } - - if (!GetLeaderName()) - { - DisbandGroup(); - return true; - } auto pack = new ServerPacket(ServerOP_GroupLeave, sizeof(ServerGroupLeave_Struct)); ServerGroupLeave_Struct* gl = (ServerGroupLeave_Struct*)pack->pBuffer; @@ -709,6 +719,7 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender) gl->zoneid = zone->GetZoneID(); gl->instance_id = zone->GetInstanceID(); strcpy(gl->member_name, oldmember->GetCleanName()); + gl->checkleader = checkleader; worldserver.SendPacket(pack); safe_delete(pack); @@ -800,6 +811,7 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender) Bot::UpdateGroupCastingRoles(this); #endif + safe_delete(outapp); return true; } @@ -2330,17 +2342,16 @@ void Group::ChangeLeader(Mob* newleader) // this changes the current group leader, notifies other members, and updates leadship AA // if the new leader is invalid, do nothing - if (!newleader || !newleader->IsClient()) + if (!newleader) { return; - - Mob* oldleader = GetLeader(); + } auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); GroupJoin_Struct* gu = (GroupJoin_Struct*) outapp->pBuffer; gu->action = groupActMakeLeader; strcpy(gu->membername, newleader->GetName()); - strcpy(gu->yourname, oldleader->GetName()); + strcpy(gu->yourname, GetOldLeaderName()); SetLeader(newleader); database.SetGroupLeaderName(GetID(), newleader->GetName()); UpdateGroupAAs(); @@ -2352,9 +2363,22 @@ void Group::ChangeLeader(Mob* newleader) members[i]->CastToClient()->SendGroupLeaderChangePacket(newleader->GetName()); members[i]->CastToClient()->QueuePacket(outapp); + Log(Logs::Detail, Logs::Group, "ChangeLeader(): Local leader update packet sent to: %s .", members[i]->GetName()); } } 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) diff --git a/zone/groups.h b/zone/groups.h index 57dfdbfb6..c27f9d59a 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -37,6 +37,8 @@ public: GroupIDConsumer() { id = 0; } GroupIDConsumer(uint32 gid) { id = gid; } inline const uint32 GetID() const { return id; } + void SetOldLeaderName(const char* oldleader) { strcpy(oldleadername, oldleader); } + char* GetOldLeaderName() { return oldleadername; } protected: friend class EntityList; @@ -44,6 +46,7 @@ protected: inline void SetID(uint32 set_id) { id = set_id; } private: 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 { @@ -58,6 +61,7 @@ public: void SendLeadershipAAUpdate(); void SendWorldGroup(uint32 zone_id,Mob* zoningmember); bool DelMemberOOZ(const char *Name); + bool DelMemberOOZ(const char *Name, bool checkleader); bool DelMember(Mob* oldmember,bool ignoresender = false); void DisbandGroup(bool joinraid = false); void GetMemberList(std::list& member_list, bool clear_list = true); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index b047197d2..0a052824a 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -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 (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; + } } if (i == EQ::invslot::slotSecondary && inst.IsWeapon() && !CanThisClassDualWield()) { continue; @@ -1169,7 +1194,6 @@ bool Client::AutoPutLootInInventory(EQ::ItemInstance& inst, bool try_worn, bool if (worn_slot_material != EQ::textures::materialInvalid) { SendWearChange(worn_slot_material); } - parse->EventItem(EVENT_EQUIP_ITEM, this, &inst, nullptr, "", i); return true; } diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 30518e520..b0cc62467 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -329,6 +329,21 @@ uint32 Lua_Client::GetBindZoneID(int 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) { Lua_Safe_Call_Void(); 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); } +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() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -1942,6 +1972,9 @@ luabind::scope lua_register_client() { .def("GetBindHeading", (float(Lua_Client::*)(int))&Lua_Client::GetBindHeading) .def("GetBindZoneID", (uint32(Lua_Client::*)(void))&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("SetSecondaryWeaponOrnamentation", (void(Lua_Client::*)(uint32))&Lua_Client::SetSecondaryWeaponOrnamentation) .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::*)(std::string))&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() { diff --git a/zone/lua_client.h b/zone/lua_client.h index 8bfa5bda0..7162bfd6b 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -92,6 +92,9 @@ public: float GetBindHeading(int index); uint32 GetBindZoneID(); uint32 GetBindZoneID(int index); + float GetTargetRingX(); + float GetTargetRingY(); + float GetTargetRingZ(); 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 MoveZone(const char *zone_short_name); @@ -360,6 +363,9 @@ public: void MovePCDynamicZone(std::string zone_name); void MovePCDynamicZone(std::string zone_name, int zone_version); 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 diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 5b8ed1a05..1dc9f2128 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -7051,6 +7051,41 @@ XS(XS_Client_MovePCDynamicZone) { 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 extern "C" #endif @@ -7108,6 +7143,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "Escape"), XS_Client_Escape, file, "$"); newXSproto(strcpy(buf, "ExpeditionMessage"), XS_Client_ExpeditionMessage, 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, "Freeze"), XS_Client_Freeze, file, "$"); newXSproto(strcpy(buf, "GetAAExp"), XS_Client_GetAAExp, file, "$"); diff --git a/zone/trading.cpp b/zone/trading.cpp index 632fd4fb7..1d75bd941 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -2648,6 +2648,11 @@ void Client::SellToBuyer(const EQApplicationPacket *app) { return; } + if(item->IsClassBag()) { + Message(Chat::Red, "That item is a Bag."); + return; + } + if(!item->Stackable) { 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 [{}]", 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"); database.AddBuyLine(CharacterID(), BuySlot, ItemID, ItemName, Quantity, Price); QueuePacket(app); } else { - if(ItemCount > 0) + if(ItemCount > 0) { 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); - - else if(LoreConflict) + } else if(LoreConflict) { 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); - - else if(ToggleOnOff) + } else if(item->IsClassBag()) { + 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); - - else + } else { database.RemoveBuyLine(CharacterID(), BuySlot); + } auto outapp = new EQApplicationPacket(OP_Barter, 936); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 920a3d958..eea45d057 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -877,7 +877,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) if (gl->zoneid == zone->GetZoneID() && gl->instance_id == zone->GetInstanceID()) break; - entity_list.SendGroupLeave(gl->gid, gl->member_name); + entity_list.SendGroupLeave(gl->gid, gl->member_name, gl->checkleader); } break; } @@ -1114,6 +1114,17 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) 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: { ServerGroupChannelMessage_Struct* gcm = (ServerGroupChannelMessage_Struct*)pack->pBuffer; if (zone) {