// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ // ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ // Magicbane Emulator Project © 2013 - 2022 // www.magicbane.com package engine.gameManager; import engine.Enum; import engine.exception.MsgSendException; import engine.net.Dispatch; import engine.net.DispatchMessage; import engine.net.client.ClientConnection; import engine.net.client.msg.UpdateGoldMsg; import engine.net.client.msg.group.GroupUpdateMsg; import engine.objects.*; import engine.server.MBServerStatics; import org.pmw.tinylog.Logger; import java.util.ArrayList; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public enum GroupManager { GROUPMANAGER; // used for quick lookup of groups by the ID of the group sent in the msg private static final ConcurrentHashMap groupsByID = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_HIGH); // an index for playercharacters to group membership private static final ConcurrentHashMap groupsByAC = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_HIGH); private static int groupCount = 0; /* * Class Implementation */ public static void removeFromGroups(AbstractCharacter ac) { Group gr = null; synchronized (GroupManager.groupsByAC) { gr = GroupManager.groupsByAC.remove(ac); } } public static void LeaveGroup(ClientConnection origin) throws MsgSendException { PlayerCharacter source = SessionManager.getPlayerCharacter(origin); LeaveGroup(source); } public static void LeaveGroup(PlayerCharacter source) throws MsgSendException { if (source == null) return; Group group = GroupManager.groupsByAC.get(source); if (group == null) // source is not in a group return; // Cleanup group window for player quiting GroupUpdateMsg groupUpdateMsg = new GroupUpdateMsg(); groupUpdateMsg.setGroup(group); groupUpdateMsg.setPlayer(source); groupUpdateMsg.setMessageType(3); Set groupMembers = group.getMembers(); for (PlayerCharacter groupMember : groupMembers) { if (groupMember == null) continue; groupUpdateMsg = new GroupUpdateMsg(); groupUpdateMsg.setGroup(group); groupUpdateMsg.setPlayer(source); groupUpdateMsg.setMessageType(3); groupUpdateMsg.setPlayer(groupMember); Dispatch dispatch = Dispatch.borrow(source, groupUpdateMsg); DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); } // Remove from group int size = group.removeGroupMember(source); // remove from the group -> ac mapping list GroupManager.groupsByAC.remove(source); if (size == 0) { GroupManager.deleteGroup(group); return; // group empty so cleanup group and we're done } // set new group lead if needed if (group.getGroupLead() == source) { PlayerCharacter newLead = group.getMembers().iterator().next(); group.setGroupLead(newLead.getObjectUUID()); groupUpdateMsg = new GroupUpdateMsg(); groupUpdateMsg.setGroup(group); groupUpdateMsg.setPlayer(newLead); groupUpdateMsg.addPlayer(source); groupUpdateMsg.setMessageType(2); group.sendUpdate(groupUpdateMsg); // Disable Formation newLead.setFollow(false); groupUpdateMsg = new GroupUpdateMsg(); groupUpdateMsg.setGroup(group); groupUpdateMsg.setPlayer(newLead); groupUpdateMsg.setMessageType(8); group.sendUpdate(groupUpdateMsg); } //send message to group PlayerCharacter pc = group.getGroupLead(); //Fixed String text = source.getFirstName() + " has left the group."; ChatManager.chatGroupInfo(pc, text); // cleanup other group members screens groupUpdateMsg = new GroupUpdateMsg(); groupUpdateMsg.setGroup(group); groupUpdateMsg.setPlayer(source); groupUpdateMsg.setMessageType(3); group.sendUpdate(groupUpdateMsg); } //This updates health/stamina/mana and loc of all players in group public static void RefreshWholeGroupList(PlayerCharacter source, ClientConnection origin, Group gexp) { if (source == null || origin == null) return; Group group = GroupManager.groupsByAC.get(source); if (group == null) return; if (gexp.getObjectUUID() != group.getObjectUUID()) return; Set groupMembers = group.getMembers(); if (groupMembers.size() < 2) return; // Send all group members health/mana/stamina/loc. for (PlayerCharacter groupMember : groupMembers) { if (groupMember == null) continue; GroupUpdateMsg gum = new GroupUpdateMsg(5, 1, groupMembers, group); gum.setPlayerUUID(groupMember.getObjectUUID()); Dispatch dispatch = Dispatch.borrow(groupMember, gum); DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); } } public static void RefreshMyGroupList(PlayerCharacter source, ClientConnection origin) { if (source == null || origin == null) return; Group group = GroupManager.groupsByAC.get(source); if (group == null) return; Set members = group.getMembers(); // Send all group members to player added for (PlayerCharacter groupMember : members) { if (groupMember == null) continue; GroupUpdateMsg gum = new GroupUpdateMsg(); gum.setGroup(group); gum.setMessageType(1); gum.setPlayer(groupMember); Dispatch dispatch = Dispatch.borrow(groupMember, gum); DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); } } public static void RefreshMyGroupListSinglePlayer(PlayerCharacter source, ClientConnection origin, PlayerCharacter playerToRefresh) { // send msg type 1 to the source player on this connection to update the group // list stats for the player that has just been loaded if (source == null || origin == null || playerToRefresh == null) return; Group group = GroupManager.groupsByAC.get(source); if (group == null) return; // only send if the 2 players are in the same group if (group != GroupManager.groupsByAC.get(playerToRefresh)) return; GroupUpdateMsg gum = new GroupUpdateMsg(); gum.setGroup(group); gum.setMessageType(1); gum.setPlayer(playerToRefresh); Dispatch dispatch = Dispatch.borrow(source, gum); DispatchMessage.dispatchMsgDispatch(dispatch, engine.Enum.DispatchChannel.SECONDARY); } public static void RefreshOthersGroupList(PlayerCharacter source) { // refresh my stats on everyone elses group list if (source == null) return; Group group = GroupManager.groupsByAC.get(source); if (group == null) return; //construct message GroupUpdateMsg gim = new GroupUpdateMsg(); gim.setGroup(group); gim.setMessageType(1); gim.setPlayer(source); group.sendUpdate(gim); } public static int incrGroupCount() { GroupManager.groupCount++; return GroupManager.groupCount; } public static boolean deleteGroup(Group g) { // remove all players from the mapping Set members = g.getMembers(); for (PlayerCharacter pc : members) { if (pc != null) { GroupManager.removeFromGroups(pc); } } // remove the group ID from the list GroupManager.groupsByID.remove(g.getObjectUUID()); g.clearMembers(); g.removeUpdateGroupJob(); return true; } public static Group addNewGroup(Group group) { PlayerCharacter pc = group.getGroupLead(); GroupManager.addGroup(group); if (pc != null) { GroupManager.addPlayerGroupMapping(pc, group); return group; } return null; } private static Group addGroup(Group group) { if (GroupManager.groupsByID.containsKey(group.getObjectUUID())) { return null; } GroupManager.groupsByID.put(group.getObjectUUID(), group); return group; } public static Group getGroup(int groupID) { return GroupManager.groupsByID.get(groupID); } public static Group getGroup(PlayerCharacter pc) { return GroupManager.groupsByAC.get(pc); } public static void addPlayerGroupMapping(PlayerCharacter pc, Group grp) { GroupManager.groupsByAC.put(pc, grp); } public static boolean goldSplit(PlayerCharacter pc, Item item, ClientConnection origin, AbstractWorldObject tar) { if (item == null || pc == null || tar == null || item.getItemBase() == null) { Logger.error( "null something"); return false; } if (item.getItemBase().getUUID() != 7) //only split goldItem return false; Group group = getGroup(pc); if (group == null || !group.getSplitGold()) //make sure player is grouped and split is on return false; ArrayList playersSplit = new ArrayList<>(); //get group members for (PlayerCharacter groupMember: group.getMembers()){ if (pc.getLoc().distanceSquared2D(groupMember.getLoc()) > MBServerStatics.CHARACTER_LOAD_RANGE * MBServerStatics.CHARACTER_LOAD_RANGE) continue; if (!groupMember.isAlive()) continue; playersSplit.add(groupMember); } //make sure more then one group member in loot range int size = playersSplit.size(); if (size < 2) return false; int total = item.getNumOfItems(); int amount = total / size; int dif = total - (size * amount); if (AbstractWorldObject.IsAbstractCharacter(tar)) { } else if (tar.getObjectType().equals(Enum.GameObjectType.Corpse)) { Corpse corpse = (Corpse) tar; corpse.getInventory().remove(item); } else { Logger.error("target not corpse or character"); return false; } if (item.getObjectType() == Enum.GameObjectType.MobLoot){ if (tar.getObjectType() == Enum.GameObjectType.Mob){ ((Mob)tar).getCharItemManager().delete(item); }else item.setNumOfItems(0); }else item.setNumOfItems(0); for (PlayerCharacter splitPlayer : playersSplit) { int amt = (group.isGroupLead(splitPlayer)) ? (amount + dif) : amount; if (amt > 0) splitPlayer.getCharItemManager().addGoldToInventory(amt, false); } for (PlayerCharacter splitPlayer : playersSplit) { UpdateGoldMsg ugm = new UpdateGoldMsg(splitPlayer); ugm.configure(); Dispatch dispatch = Dispatch.borrow(splitPlayer, ugm); DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); } UpdateGoldMsg updateTargetGold = new UpdateGoldMsg(tar); updateTargetGold.configure(); DispatchMessage.dispatchMsgToInterestArea(tar, updateTargetGold, Enum.DispatchChannel.SECONDARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); // //TODO send group split message String text = "Group Split: " + amount; ChatManager.chatGroupInfo(pc, text); return true; } }