테스트

aion-server 4.8

Gitteol
최고관리자 · 1 · 💬 0 클론/새로받기
 4.8 61f661d · 1 commits 새로받기(Pull)
game-server/src/com/aionemu/gameserver/services/drop/DropRegistrationService.java
package com.aionemu.gameserver.services.drop;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import com.aionemu.commons.utils.Rnd;
import com.aionemu.gameserver.ai.event.AIEventType;
import com.aionemu.gameserver.configs.main.RatesConfig;
import com.aionemu.gameserver.dataholders.DataManager;
import com.aionemu.gameserver.dataholders.GlobalNpcExclusionData;
import com.aionemu.gameserver.model.Chance;
import com.aionemu.gameserver.model.Race;
import com.aionemu.gameserver.model.drop.Drop;
import com.aionemu.gameserver.model.drop.DropItem;
import com.aionemu.gameserver.model.drop.DropModifiers;
import com.aionemu.gameserver.model.drop.NpcDrop;
import com.aionemu.gameserver.model.gameobjects.DropNpc;
import com.aionemu.gameserver.model.gameobjects.Npc;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.model.gameobjects.player.Rates;
import com.aionemu.gameserver.model.items.ItemId;
import com.aionemu.gameserver.model.stats.container.StatEnum;
import com.aionemu.gameserver.model.team.common.legacy.LootGroupRules;
import com.aionemu.gameserver.model.templates.globaldrops.*;
import com.aionemu.gameserver.model.templates.housing.HouseType;
import com.aionemu.gameserver.model.templates.item.ItemTemplate;
import com.aionemu.gameserver.model.templates.npc.AbyssNpcType;
import com.aionemu.gameserver.model.templates.spawns.basespawns.BaseSpawnTemplate;
import com.aionemu.gameserver.model.templates.spawns.siegespawns.SiegeSpawnTemplate;
import com.aionemu.gameserver.network.aion.serverpackets.SM_LOOT_STATUS;
import com.aionemu.gameserver.network.aion.serverpackets.SM_LOOT_STATUS.Status;
import com.aionemu.gameserver.services.QuestService;
import com.aionemu.gameserver.services.event.EventService;
import com.aionemu.gameserver.spawnengine.SpawnHandlerType;
import com.aionemu.gameserver.utils.PacketSendUtility;
import com.aionemu.gameserver.utils.stats.DropRewardEnum;
import com.aionemu.gameserver.world.WorldDropType;
import com.aionemu.gameserver.world.WorldMapType;
import com.aionemu.gameserver.world.zone.ZoneName;

/**
 * @author xTz, Aioncool, Bobobear, Neon
 */
public class DropRegistrationService {

	private Map<Integer, Set<DropItem>> currentDropMap = new ConcurrentHashMap<>();
	private Map<Integer, DropNpc> dropRegistrationMap = new ConcurrentHashMap<>();

	private DropRegistrationService() {
	}

	public void registerDrop(Npc npc, Player player, Collection<Player> groupMembers) {
		registerDrop(npc, player, player.getLevel(), groupMembers);
	}

	/**
	 * After NPC dies, it can register arbitrary drop
	 */
	public void registerDrop(Npc npc, Player player, int highestLevel, Collection<Player> groupMembers) {
		int npcObjId = npc.getObjectId();

		// Getting all possible drops for this Npc
		NpcDrop npcDrop = DataManager.CUSTOM_NPC_DROP.getNpcDrop(npc.getNpcId());

		List<Player> allowedLooters = new ArrayList<>();
		Player looter = player;
		int winnerObj = 0;
		Player teamLooter = initDropNpc(player, npcObjId, allowedLooters, groupMembers);
		if (teamLooter != null) {
			looter = teamLooter;
			winnerObj = teamLooter.getObjectId();
		}

		int index = 1;
		Set<DropItem> droppedItems = new HashSet<>();
		DropModifiers dropModifiers = createDropModifiers(npc, looter, highestLevel);

		if (npcDrop != null) // add custom drops
			index = npcDrop.dropCalculator(droppedItems, index, dropModifiers, groupMembers);

		// Updating current dropMap
		currentDropMap.put(npcObjId, droppedItems);

		index = QuestService.getQuestDrop(droppedItems, index, npc, groupMembers, looter);

		// if npc ai == quest_use_item it will be always excluded from global drops
		boolean isNpcQuest = npc.getAi().getName().equals("quest_use_item");
		if (!isNpcQuest) {
			boolean hasGlobalNpcExclusions = hasGlobalNpcExclusions(npc);
			boolean isAllowedDefaultGlobalDropNpc = isAllowedDefaultGlobalDropNpc(npc, dropModifiers.isDropNpcChest());
			// instances with WorldDropType.NONE must not have global drops (example Arenas)
			if (!hasGlobalNpcExclusions && npc.getWorldDropType() != WorldDropType.NONE) {
				index = addGlobalDrops(index, dropModifiers, looter, npc, isAllowedDefaultGlobalDropNpc, DataManager.GLOBAL_DROP_DATA.getAllRules(),
					droppedItems, groupMembers, winnerObj);
			}
			if (!hasGlobalNpcExclusions || dropModifiers.isDropNpcChest())
				addGlobalDrops(index, dropModifiers, looter, npc, isAllowedDefaultGlobalDropNpc, EventService.getInstance().getActiveEventDropRules(),
					droppedItems, groupMembers, winnerObj);
		}

		npc.getPosition().getWorldMapInstance().getInstanceHandler().onDropRegistered(npc, winnerObj);
		npc.getAi().onGeneralEvent(AIEventType.DROP_REGISTERED);

		for (Player p : allowedLooters) {
			PacketSendUtility.sendPacket(p, new SM_LOOT_STATUS(npcObjId, Status.LOOT_ENABLE));
		}

		DropService.getInstance().scheduleFreeForAll(npcObjId);
	}

	public DropModifiers createDropModifiers(Npc npc, Player player, int highestLevel) {
		DropModifiers dropModifiers = new DropModifiers();
		String dropType = npc.getGroupDrop().name().toLowerCase();
		boolean isChest = npc.getAi().getName().equals("chest") || dropType.startsWith("treasure") || dropType.endsWith("box");
		dropModifiers.setIsDropNpcChest(isChest);
		dropModifiers.setDropRace(player.getRace());
		dropModifiers.setBoostDropRate(calculateBoostDropRate(player, npc));
		dropModifiers.setReductionDropRate(getReductionDropRate(npc, highestLevel));
		return dropModifiers;
	}

	private Player initDropNpc(Player player, int npcObjId, List<Player> allowedLooters, Collection<Player> groupMembers) {
		Player looter = null;
		DropNpc dropNpc = new DropNpc(npcObjId);
		// Distributing drops to players
		var lootingTeam = player.getCurrentTeam();
		if (lootingTeam != null) {
			LootGroupRules lootGroupRules = lootingTeam.getLootGroupRules();

			switch (lootGroupRules.getLootRule()) {
				case ROUNDROBIN:
					int size = groupMembers.size();
					if (size > lootGroupRules.getNrRoundRobin())
						lootGroupRules.setNrRoundRobin(lootGroupRules.getNrRoundRobin() + 1);
					else
						lootGroupRules.setNrRoundRobin(1);

					int i = 0;
					for (Player p : groupMembers) {
						i++;
						if (i == lootGroupRules.getNrRoundRobin()) {
							allowedLooters.add(p);
							looter = p;
							break;
						}
					}
					break;
				case FREEFORALL:
					allowedLooters.addAll(groupMembers);
					break;
				case LEADER:
					Player leader = player.isInGroup() ? player.getPlayerGroup().getLeaderObject() : player.getPlayerAlliance().getLeaderObject();
					allowedLooters.add(leader);
					looter = leader;
					break;
			}
			dropNpc.setInRangePlayers(groupMembers);
			dropNpc.setLootingTeam(lootingTeam);
		} else {
			allowedLooters.add(player);
		}
		allowedLooters.forEach(dropNpc::setAllowedLooter);
		dropRegistrationMap.put(npcObjId, dropNpc);
		return looter;
	}

	public boolean isAllowedDefaultGlobalDropNpc(Npc npc, boolean isChest) {
		// exclude most siege spawns, and inner base spawns
		if (npc.getSpawn() instanceof SiegeSpawnTemplate && npc.getAbyssNpcType() != AbyssNpcType.DEFENDER)
			return false;
		if (npc.getSpawn() instanceof BaseSpawnTemplate && npc.getSpawn().getHandlerType() != SpawnHandlerType.OUTRIDER
			&& npc.getSpawn().getHandlerType() != SpawnHandlerType.OUTRIDER_ENHANCED)
			return false;
		// if npc level == 1 means missing stats, so better exclude it from drops
		if (npc.getLevel() < 2 && !isChest && npc.getWorldId() != WorldMapType.POETA.getId() && npc.getWorldId() != WorldMapType.ISHALGEN.getId())
			return false;
		// if abyss type npc != null or npc is chest, the npc will be excluded from drops
		if (isChest || npc.getAbyssNpcType() != AbyssNpcType.NONE && npc.getAbyssNpcType() != AbyssNpcType.DEFENDER)
			return false;
		return true;
	}

	private int addGlobalDrops(int index, DropModifiers dropModifiers, Player player, Npc npc, boolean isAllowedDefaultGlobalDropNpc,
		List<GlobalRule> rules, Set<DropItem> droppedItems, Collection<Player> groupMembers, int winnerObj) {
		for (GlobalRule rule : rules) {
			// if getGlobalRuleNpcs() != null means drops are for specified npcs (like named drops) so the default restrictions will be ignored
			if (isAllowedDefaultGlobalDropNpc || rule.getGlobalRuleNpcs() != null) {
				float chance = calculateEffectiveChance(rule, npc, dropModifiers);
				if (Rnd.chance() >= chance)
					continue;

				index = addDropItems(index, droppedItems, rule, npc, player, groupMembers, winnerObj, dropModifiers);
			}
		}
		return index;
	}

	private Float getReductionDropRate(Npc npc, int highestLevel) {
		int dropChance = DropRewardEnum.dropRewardFrom(npc.getLevel() - highestLevel); // reduced chance depending on level
		return dropChance == 100 ? null : dropChance / 100f;
	}

	private float calculateBoostDropRate(Player killer, Npc npc) {
		// Drop rate from NPC can be boosted by Spiritmaster Erosion skill
		int boostDropRate = npc.getGameStats().getStat(StatEnum.BOOST_DROP_RATE, 100).getCurrent();
		// can be exploited on duel with Spiritmaster Erosion skill
		boostDropRate = killer.getGameStats().getStat(StatEnum.BOOST_DROP_RATE, boostDropRate).getCurrent();
		// Drop rate can be boosted by player buff too
		boostDropRate = killer.getGameStats().getStat(StatEnum.DR_BOOST, boostDropRate).getCurrent();

		if (killer.getCommonData().getCurrentReposeEnergy() > 0) // EoR 5% Boost drop rate
			boostDropRate += 5;
		if (killer.getCommonData().getCurrentSalvationPercent() > 0) // EoS 5% Boost drop rate
			boostDropRate += 5;
		if (killer.getActiveHouse() != null && killer.getActiveHouse().getHouseType() == HouseType.PALACE) // Deed to Palace 5% Boost drop rate
			boostDropRate += 5;

		return Rates.get(killer, RatesConfig.DROP_RATES) * boostDropRate / 100f;
	}

	public float calculateEffectiveChance(GlobalRule rule, Npc npc, DropModifiers dropModifiers) {
		float chance = rule.getChance();
		// dynamic_chance means mobs will have different base chances based on their rank and rating
		if (rule.isDynamicChance())
			chance *= getRankModifier(npc) * getRatingModifier(npc);
		return dropModifiers.calculateDropChance(chance, rule.isUseLevelBasedChanceReduction());
	}

	private int addDropItems(int index, Set<DropItem> droppedItems, GlobalRule rule, Npc npc, Player player, Collection<Player> groupMembers,
		int winnerObj, DropModifiers dropModifiers) {
		List<GlobalDropItem> drops = collectDrops(rule, npc, dropModifiers);
		if (!drops.isEmpty()) {
			if (rule.getMemberLimit() > 1 && player.isInTeam()) {
				List<Player> members = new ArrayList<>(groupMembers);
				if (rule.getMemberLimit() > members.size())
					Collections.shuffle(members);
				int distributedItems = 0;
				for (Player member : members) {
					for (GlobalDropItem drop : drops) {
						DropItem dropitem = new DropItem(new Drop(drop.getId(), 1, 1, 100));
						dropitem.setCount(getItemCount(drop, npc));
						dropitem.setIndex(index++);
						dropitem.setPlayerObjId(member.getObjectId());
						dropitem.setWinningPlayer(member);
						dropitem.isDistributeItem(true);
						droppedItems.add(dropitem);
					}
					if (++distributedItems >= rule.getMemberLimit())
						break;
				}
			} else {
				for (GlobalDropItem drop : drops) {
					droppedItems.add(regDropItem(index++, winnerObj, npc.getObjectId(), drop.getId(), getItemCount(drop, npc)));
				}
			}
		}
		return index;
	}

	public DropItem regDropItem(int index, int playerObjId, int objId, int itemId, long count) {
		DropItem item = new DropItem(new Drop(itemId, 1, 1, 100));
		item.setPlayerObjId(playerObjId);
		item.setNpcObj(objId);
		item.setCount(count);
		item.setIndex(index);
		return item;
	}

	/**
	 * @return dropRegistrationMap
	 */
	public Map<Integer, DropNpc> getDropRegistrationMap() {
		return dropRegistrationMap;
	}

	/**
	 * @return currentDropMap
	 */
	public Map<Integer, Set<DropItem>> getCurrentDropMap() {
		return currentDropMap;
	}

	public static DropRegistrationService getInstance() {
		return SingletonHolder.instance;
	}

	public boolean hasGlobalNpcExclusions(Npc npc) {
		GlobalNpcExclusionData gde = DataManager.GLOBAL_EXCLUSION_DATA;
		if (!gde.isEmpty()) {
			if (gde.getNpcIds().contains(npc.getNpcId()) || gde.getNpcNames().contains(npc.getName())
				|| gde.getNpcTemplateTypes().contains(npc.getNpcTemplateType()) || npc.getTribe() != null && gde.getNpcTribes().contains(npc.getTribe())
				|| gde.getNpcAbyssTypes().contains(npc.getAbyssNpcType()))
				return true;
		}
		return false;
	}

	private boolean checkRuleRestrictions(GlobalRule rule, Race race, Npc npc) {
		if (!checkRestrictionRace(rule, race))
			return false;
		if (!checkGlobalRuleMaps(rule, npc))
			return false;
		if (!checkGlobalRuleWorlds(rule, npc))
			return false;
		if (!checkGlobalRuleRatings(rule, npc))
			return false;
		if (!checkGlobalRuleRaces(rule, npc))
			return false;
		if (!checkGlobalRuleTribes(rule, npc))
			return false;
		if (!checkGlobalRuleZones(rule, npc))
			return false;
		if (!checkGlobalRuleNpcs(rule, npc))
			return false;
		if (!checkGlobalRuleNpcGroups(rule, npc)) // drop group from npc_templates
			return false;
		if (!checkGlobalRuleExcludedNpcs(rule, npc))
			return false;
		return true;
	}

	private boolean checkRestrictionRace(GlobalRule rule, Race race) {
		if (rule.getRestrictionRace() != null) {
			if (race == Race.ASMODIANS && rule.getRestrictionRace() == GlobalRule.RestrictionRace.ELYOS
				|| race == Race.ELYOS && rule.getRestrictionRace() == GlobalRule.RestrictionRace.ASMODIANS)
				return false;
		}
		return true;
	}

	private boolean checkGlobalRuleMaps(GlobalRule rule, Npc npc) {
		if (rule.getGlobalRuleMaps() != null) {
			for (GlobalDropMap gdMap : rule.getGlobalRuleMaps().getGlobalDropMaps())
				if (gdMap.getMapId() == npc.getPosition().getMapId())
					return true;
			return false;
		}
		return true;
	}

	private boolean checkGlobalRuleWorlds(GlobalRule rule, Npc npc) {
		if (rule.getGlobalRuleWorlds() != null) {
			for (GlobalDropWorld gdWorld : rule.getGlobalRuleWorlds().getGlobalDropWorlds())
				if (gdWorld.getWorldDropType().equals(npc.getWorldDropType()))
					return true;
			return false;
		}
		return true;
	}

	private boolean checkGlobalRuleRatings(GlobalRule rule, Npc npc) {
		if (rule.getGlobalRuleRatings() != null) {
			for (GlobalDropRating gdRating : rule.getGlobalRuleRatings().getGlobalDropRatings())
				if (gdRating.getRating().equals(npc.getRating()))
					return true;
			return false;
		}
		return true;
	}

	private boolean checkGlobalRuleRaces(GlobalRule rule, Npc npc) {
		if (rule.getGlobalRuleRaces() != null) {
			for (GlobalDropRace gdRace : rule.getGlobalRuleRaces().getGlobalDropRaces())
				if (gdRace.getRace().equals(npc.getRace()))
					return true;
			return false;
		}
		return true;
	}

	private boolean checkGlobalRuleTribes(GlobalRule rule, Npc npc) {
		if (rule.getGlobalRuleTribes() != null) {
			for (GlobalDropTribe gdTribe : rule.getGlobalRuleTribes().getGlobalDropTribes())
				if (gdTribe.getTribe().equals(npc.getTribe()))
					return true;
			return false;
		}
		return true;
	}

	private boolean checkGlobalRuleZones(GlobalRule rule, Npc npc) {
		if (rule.getGlobalRuleZones() != null) {
			for (GlobalDropZone gdZone : rule.getGlobalRuleZones().getGlobalDropZones())
				if (npc.isInsideZone(ZoneName.get(gdZone.getZone())))
					return true;
			return false;
		}
		return true;
	}

	private boolean checkGlobalRuleNpcs(GlobalRule rule, Npc npc) {
		if (rule.getGlobalRuleNpcs() != null) {
			for (GlobalDropNpc gdNpc : rule.getGlobalRuleNpcs().getGlobalDropNpcs())
				if (gdNpc.getNpcId() == npc.getNpcId())
					return true;
			return false;
		}
		return true;
	}

	private boolean checkGlobalRuleNpcGroups(GlobalRule rule, Npc npc) {
		if (rule.getGlobalRuleNpcGroups() != null) {
			for (GlobalDropNpcGroup gdGroup : rule.getGlobalRuleNpcGroups().getGlobalDropNpcGroups())
				if (gdGroup.getGroup().equals(npc.getGroupDrop()))
					return true;
			return false;
		}
		return true;
	}

	private boolean checkGlobalRuleExcludedNpcs(GlobalRule rule, Npc npc) {
		if (rule.getGlobalRuleExcludedNpcs() != null)
			return !rule.getGlobalRuleExcludedNpcs().getNpcIds().contains(npc.getNpcId());
		return true;
	}

	public List<GlobalDropItem> collectDrops(GlobalRule rule, Npc npc, DropModifiers dropModifiers) {
		int maxDrops = dropModifiers.getMaxDropsPerGroup() == null ? rule.getMaxDropRule() : dropModifiers.getMaxDropsPerGroup();
		List<GlobalDropItem> drops = collectAllowedDrops(rule, npc, dropModifiers);
		if (drops.size() > maxDrops) {
			List<GlobalDropItem> allowedItems = new ArrayList<>();
			for (int i = 0; i < maxDrops && !drops.isEmpty(); i++) {
				GlobalDropItem item = Chance.selectElement(drops, true);
				if (item != null)
					allowedItems.add(item);
			}
			return allowedItems;
		}
		return drops;
	}

	private List<GlobalDropItem> collectAllowedDrops(GlobalRule rule, Npc npc, DropModifiers dropModifiers) {
		if (!checkRuleRestrictions(rule, dropModifiers.getDropRace(), npc))
			return Collections.emptyList();
		List<GlobalDropItem> tempItems = new ArrayList<>();
		for (GlobalDropItem globalItem : rule.getDropItems()) {
			ItemTemplate itemTemplate = DataManager.ITEM_DATA.getItemTemplate(globalItem.getId());
			if (itemTemplate.getRace() == Race.PC_ALL || itemTemplate.getRace() == dropModifiers.getDropRace()) {
				int diff = npc.getLevel() - itemTemplate.getLevel();
				if (diff >= rule.getMinDiff() && diff <= rule.getMaxDiff())
					tempItems.add(globalItem);
			}
		}
		return tempItems;
	}

	@SuppressWarnings("lossy-conversions")
	private long getItemCount(GlobalDropItem item, Npc npc) {
		long count = Rnd.get(item.getMinCount(), item.getMaxCount());
		if (item.getId() == ItemId.KINAH)
			count *= npc.getLevel() * Math.pow(getRankModifier(npc) * getRatingModifier(npc), 6);
		return count;
	}

	private float getRankModifier(Npc npc) {
		return switch (npc.getRank()) {
			case NOVICE -> 0.9f;
			case DISCIPLINED -> 1f;
			case SEASONED -> 1.05f;
			case EXPERT -> 1.1f;
			case VETERAN -> 1.15f;
			case MASTER -> 1.2f;
		};
	}

	private float getRatingModifier(Npc npc) {
		return switch (npc.getRating()) {
			case JUNK -> 0.5f;
			case NORMAL -> 1f;
			case ELITE -> 1.3f;
			case HERO -> 1.8f;
			case LEGENDARY -> 2f;
		};
	}

	private static class SingletonHolder {

		protected static final DropRegistrationService instance = new DropRegistrationService();
	}

}

📎 첨부파일

댓글 작성 권한이 없습니다.
🏆 포인트 랭킹 TOP 10
순위 닉네임 포인트
1 no_profile 타키야겐지쪽지보내기 자기소개 아이디로 검색 전체게시물 102,949
2 no_profile 동가리쪽지보내기 자기소개 아이디로 검색 전체게시물 63,733
3 no_profile 라프텔쪽지보내기 자기소개 아이디로 검색 전체게시물 51,771
4 no_profile 불멸의행복쪽지보내기 자기소개 아이디로 검색 전체게시물 36,923
5 서번트쪽지보내기 자기소개 아이디로 검색 전체게시물 35,011
6 no_profile 닥터스쪽지보내기 자기소개 아이디로 검색 전체게시물 29,470
7 no_profile 검은고양이쪽지보내기 자기소개 아이디로 검색 전체게시물 29,077
8 no_profile Revolution쪽지보내기 자기소개 아이디로 검색 전체게시물 28,199
9 no_profile 보거스쪽지보내기 자기소개 아이디로 검색 전체게시물 26,731
10 no_profile 호롤롤로쪽지보내기 자기소개 아이디로 검색 전체게시물 17,020
알림 0