Support for drop groups.
This commit is contained in:
		| @@ -37,6 +37,7 @@ import org.l2jmobius.gameserver.model.actor.Attackable; | ||||
| import org.l2jmobius.gameserver.model.actor.Creature; | ||||
| import org.l2jmobius.gameserver.model.actor.Npc; | ||||
| import org.l2jmobius.gameserver.model.actor.Player; | ||||
| import org.l2jmobius.gameserver.model.holders.DropGroupHolder; | ||||
| import org.l2jmobius.gameserver.model.holders.DropHolder; | ||||
| import org.l2jmobius.gameserver.model.item.ItemTemplate; | ||||
| import org.l2jmobius.gameserver.model.itemcontainer.Inventory; | ||||
| @@ -326,12 +327,13 @@ public class NpcViewMod implements IBypassHandler | ||||
| 	private static String getDropListButtons(Npc npc) | ||||
| 	{ | ||||
| 		final StringBuilder sb = new StringBuilder(); | ||||
| 		final List<DropGroupHolder> dropListGroups = npc.getTemplate().getDropGroups(); | ||||
| 		final List<DropHolder> dropListDeath = npc.getTemplate().getDropList(); | ||||
| 		final List<DropHolder> dropListSpoil = npc.getTemplate().getSpoilList(); | ||||
| 		if ((dropListDeath != null) || (dropListSpoil != null)) | ||||
| 		if ((dropListGroups != null) || (dropListDeath != null) || (dropListSpoil != null)) | ||||
| 		{ | ||||
| 			sb.append("<table width=275 cellpadding=0 cellspacing=0><tr>"); | ||||
| 			if (dropListDeath != null) | ||||
| 			if ((dropListGroups != null) || (dropListDeath != null)) | ||||
| 			{ | ||||
| 				sb.append("<td align=center><button value=\"Show Drop\" width=100 height=25 action=\"bypass NpcViewMod dropList DROP " + npc.getObjectId() + "\" back=\"L2UI_CT1.Button_DF_Calculator_Down\" fore=\"L2UI_CT1.Button_DF_Calculator\"></td>"); | ||||
| 			} | ||||
| @@ -348,13 +350,40 @@ public class NpcViewMod implements IBypassHandler | ||||
| 	 | ||||
| 	private void sendNpcDropList(Player player, Npc npc, DropType dropType, int pageValue) | ||||
| 	{ | ||||
| 		final List<DropHolder> templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); | ||||
| 		if (templateList == null) | ||||
| 		List<DropHolder> dropList = null; | ||||
| 		if (dropType == DropType.SPOIL) | ||||
| 		{ | ||||
| 			dropList = new ArrayList<>(npc.getTemplate().getSpoilList()); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			final List<DropHolder> drops = npc.getTemplate().getDropList(); | ||||
| 			if (drops != null) | ||||
| 			{ | ||||
| 				dropList = new ArrayList<>(drops); | ||||
| 			} | ||||
| 			final List<DropGroupHolder> dropGroups = npc.getTemplate().getDropGroups(); | ||||
| 			if (dropGroups != null) | ||||
| 			{ | ||||
| 				if (dropList == null) | ||||
| 				{ | ||||
| 					dropList = new ArrayList<>(); | ||||
| 				} | ||||
| 				for (DropGroupHolder dropGroup : dropGroups) | ||||
| 				{ | ||||
| 					final double chance = dropGroup.getChance() / 100; | ||||
| 					for (DropHolder dropHolder : dropGroup.getDropList()) | ||||
| 					{ | ||||
| 						dropList.add(new DropHolder(dropHolder.getDropType(), dropHolder.getItemId(), dropHolder.getMin(), dropHolder.getMax(), dropHolder.getChance() * chance)); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		if (dropList == null) | ||||
| 		{ | ||||
| 			return; | ||||
| 		} | ||||
| 		 | ||||
| 		final List<DropHolder> dropList = new ArrayList<>(templateList); | ||||
| 		Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); | ||||
| 		 | ||||
| 		int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; | ||||
|   | ||||
| @@ -36,6 +36,7 @@ import org.l2jmobius.gameserver.handler.CommunityBoardHandler; | ||||
| import org.l2jmobius.gameserver.handler.IParseBoardHandler; | ||||
| import org.l2jmobius.gameserver.model.actor.Player; | ||||
| import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; | ||||
| import org.l2jmobius.gameserver.model.holders.DropGroupHolder; | ||||
| import org.l2jmobius.gameserver.model.holders.DropHolder; | ||||
| import org.l2jmobius.gameserver.model.item.ItemTemplate; | ||||
| import org.l2jmobius.gameserver.model.itemcontainer.Inventory; | ||||
| @@ -104,6 +105,17 @@ public class DropSearchBoard implements IParseBoardHandler | ||||
| 	 | ||||
| 	private void buildDropIndex() | ||||
| 	{ | ||||
| 		NpcData.getInstance().getTemplates(npc -> npc.getDropGroups() != null).forEach(npcTemplate -> | ||||
| 		{ | ||||
| 			for (DropGroupHolder dropGroup : npcTemplate.getDropGroups()) | ||||
| 			{ | ||||
| 				final double chance = dropGroup.getChance() / 100; | ||||
| 				for (DropHolder dropHolder : dropGroup.getDropList()) | ||||
| 				{ | ||||
| 					addToDropList(npcTemplate, new DropHolder(dropHolder.getDropType(), dropHolder.getItemId(), dropHolder.getMin(), dropHolder.getMax(), dropHolder.getChance() * chance)); | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
| 		NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> | ||||
| 		{ | ||||
| 			for (DropHolder dropHolder : npcTemplate.getDropList()) | ||||
|   | ||||
| @@ -1,16 +1,5 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> | ||||
| 	<xs:complexType name="dropListItem"> | ||||
| 		<xs:attribute name="id" type="xs:positiveInteger" use="required" /> | ||||
| 		<xs:attribute name="min" type="xs:nonNegativeInteger" /> | ||||
| 		<xs:attribute name="max" type="xs:positiveInteger" /> | ||||
| 		<xs:attribute name="chance" type="xs:decimal" /> | ||||
| 	</xs:complexType> | ||||
| 	<xs:complexType name="dropList"> | ||||
| 		<xs:choice minOccurs="1" maxOccurs="unbounded"> | ||||
| 			<xs:element name="item" type="dropListItem" /> | ||||
| 		</xs:choice> | ||||
| 	</xs:complexType> | ||||
| 	<xs:element name="list"> | ||||
| 		<xs:complexType> | ||||
| 			<xs:sequence> | ||||
| @@ -260,11 +249,81 @@ | ||||
| 							</xs:element> | ||||
| 							<xs:element name="dropLists" minOccurs="0" maxOccurs="1"> | ||||
| 								<xs:complexType> | ||||
| 									<xs:all> | ||||
| 										<xs:element name="drop" type="dropList" minOccurs="0" maxOccurs="1" /> | ||||
| 										<xs:element name="spoil" type="dropList" minOccurs="0" maxOccurs="1" /> | ||||
| 										<xs:element name="lucky" type="dropList" minOccurs="0" maxOccurs="1" /> | ||||
| 									</xs:all> | ||||
| 									<xs:sequence> | ||||
| 										<xs:element name="drop" minOccurs="0"> | ||||
| 											<xs:complexType> | ||||
| 												<xs:sequence> | ||||
| 													<xs:element name="item" maxOccurs="unbounded" minOccurs="0"> | ||||
| 														<xs:complexType> | ||||
| 															<xs:simpleContent> | ||||
| 																<xs:extension base="xs:string"> | ||||
| 																	<xs:attribute type="xs:int" name="id" use="required"/> | ||||
| 																	<xs:attribute type="xs:long" name="min" use="required"/> | ||||
| 																	<xs:attribute type="xs:long" name="max" use="required"/> | ||||
| 																	<xs:attribute type="xs:double" name="chance" use="required"/> | ||||
| 																</xs:extension> | ||||
| 															</xs:simpleContent> | ||||
| 														</xs:complexType> | ||||
| 													</xs:element> | ||||
| 													<xs:element name="group" maxOccurs="unbounded" minOccurs="0"> | ||||
| 														<xs:complexType> | ||||
| 															<xs:sequence> | ||||
| 																<xs:element name="item" maxOccurs="unbounded" minOccurs="0"> | ||||
| 																	<xs:complexType> | ||||
| 																		<xs:simpleContent> | ||||
| 																			<xs:extension base="xs:string"> | ||||
| 																				<xs:attribute type="xs:int" name="id" use="required"/> | ||||
| 																				<xs:attribute type="xs:long" name="min" use="required"/> | ||||
| 																				<xs:attribute type="xs:long" name="max" use="required"/> | ||||
| 																				<xs:attribute type="xs:double" name="chance" use="required"/> | ||||
| 																			</xs:extension> | ||||
| 																		</xs:simpleContent> | ||||
| 																	</xs:complexType> | ||||
| 																</xs:element> | ||||
| 															</xs:sequence> | ||||
| 															<xs:attribute type="xs:double" name="chance" use="optional"/> | ||||
| 														</xs:complexType> | ||||
| 													</xs:element> | ||||
| 												</xs:sequence> | ||||
| 											</xs:complexType> | ||||
| 										</xs:element> | ||||
| 										<xs:element name="spoil" minOccurs="0"> | ||||
| 											<xs:complexType> | ||||
| 												<xs:sequence> | ||||
| 													<xs:element name="item" maxOccurs="unbounded" minOccurs="0"> | ||||
| 														<xs:complexType> | ||||
| 															<xs:simpleContent> | ||||
| 																<xs:extension base="xs:string"> | ||||
| 																	<xs:attribute type="xs:int" name="id" use="optional"/> | ||||
| 																	<xs:attribute type="xs:long" name="min" use="optional"/> | ||||
| 																	<xs:attribute type="xs:long" name="max" use="optional"/> | ||||
| 																	<xs:attribute type="xs:double" name="chance" use="optional"/> | ||||
| 																</xs:extension> | ||||
| 															</xs:simpleContent> | ||||
| 														</xs:complexType> | ||||
| 													</xs:element> | ||||
| 												</xs:sequence> | ||||
| 											</xs:complexType> | ||||
| 										</xs:element> | ||||
| 										<xs:element name="lucky" minOccurs="0"> | ||||
| 											<xs:complexType> | ||||
| 												<xs:sequence> | ||||
| 													<xs:element name="item" maxOccurs="unbounded" minOccurs="0"> | ||||
| 														<xs:complexType> | ||||
| 															<xs:simpleContent> | ||||
| 																<xs:extension base="xs:string"> | ||||
| 																	<xs:attribute type="xs:int" name="id" use="optional"/> | ||||
| 																	<xs:attribute type="xs:long" name="min" use="optional"/> | ||||
| 																	<xs:attribute type="xs:long" name="max" use="optional"/> | ||||
| 																	<xs:attribute type="xs:double" name="chance" use="optional"/> | ||||
| 																</xs:extension> | ||||
| 															</xs:simpleContent> | ||||
| 														</xs:complexType> | ||||
| 													</xs:element> | ||||
| 												</xs:sequence> | ||||
| 											</xs:complexType> | ||||
| 										</xs:element> | ||||
| 									</xs:sequence> | ||||
| 								</xs:complexType> | ||||
| 							</xs:element> | ||||
| 							<xs:element name="collision" minOccurs="0" maxOccurs="1"> | ||||
|   | ||||
| @@ -46,6 +46,7 @@ import org.l2jmobius.gameserver.enums.MpRewardType; | ||||
| import org.l2jmobius.gameserver.model.StatSet; | ||||
| import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; | ||||
| import org.l2jmobius.gameserver.model.effects.EffectType; | ||||
| import org.l2jmobius.gameserver.model.holders.DropGroupHolder; | ||||
| import org.l2jmobius.gameserver.model.holders.DropHolder; | ||||
| import org.l2jmobius.gameserver.model.skill.Skill; | ||||
|  | ||||
| @@ -100,6 +101,7 @@ public class NpcData implements IXmlReader | ||||
| 						Set<Integer> clans = null; | ||||
| 						Set<Integer> ignoreClanNpcIds = null; | ||||
| 						List<DropHolder> dropLists = null; | ||||
| 						List<DropGroupHolder> dropGroups = null; | ||||
| 						set.set("id", npcId); | ||||
| 						set.set("displayId", parseInteger(attrs, "displayId")); | ||||
| 						set.set("level", parseByte(attrs, "level")); | ||||
| @@ -436,24 +438,54 @@ public class NpcData implements IXmlReader | ||||
| 										 | ||||
| 										if (dropType != null) | ||||
| 										{ | ||||
| 											if (dropLists == null) | ||||
| 											{ | ||||
| 												dropLists = new ArrayList<>(); | ||||
| 											} | ||||
| 											 | ||||
| 											for (Node dropNode = dropListsNode.getFirstChild(); dropNode != null; dropNode = dropNode.getNextSibling()) | ||||
| 											{ | ||||
| 												final NamedNodeMap dropAttrs = dropNode.getAttributes(); | ||||
| 												if ("item".equalsIgnoreCase(dropNode.getNodeName())) | ||||
| 												final String nodeName = dropNode.getNodeName(); | ||||
| 												if (nodeName.equalsIgnoreCase("group")) | ||||
| 												{ | ||||
| 													final DropHolder dropItem = new DropHolder(dropType, parseInteger(dropAttrs, "id"), parseLong(dropAttrs, "min"), parseLong(dropAttrs, "max"), parseDouble(dropAttrs, "chance")); | ||||
| 													if (ItemTable.getInstance().getTemplate(parseInteger(dropAttrs, "id")) == null) | ||||
| 													if (dropGroups == null) | ||||
| 													{ | ||||
| 														LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(dropAttrs, "id") + "."); | ||||
| 														dropGroups = new ArrayList<>(); | ||||
| 													} | ||||
| 													 | ||||
| 													final DropGroupHolder group = new DropGroupHolder(parseDouble(dropNode.getAttributes(), "chance")); | ||||
| 													for (Node groupNode = dropNode.getFirstChild(); groupNode != null; groupNode = groupNode.getNextSibling()) | ||||
| 													{ | ||||
| 														if (groupNode.getNodeName().equalsIgnoreCase("item")) | ||||
| 														{ | ||||
| 															final NamedNodeMap groupAttrs = groupNode.getAttributes(); | ||||
| 															final int itemId = parseInteger(groupAttrs, "id"); | ||||
| 															 | ||||
| 															if (ItemTable.getInstance().getTemplate(itemId) == null) | ||||
| 															{ | ||||
| 																LOGGER.warning("DropListItem: Could not find item with id " + itemId + "."); | ||||
| 															} | ||||
| 															else | ||||
| 															{ | ||||
| 																group.addDrop(new DropHolder(dropType, itemId, parseLong(groupAttrs, "min"), parseLong(groupAttrs, "max"), parseDouble(groupAttrs, "chance"))); | ||||
| 															} | ||||
| 														} | ||||
| 													} | ||||
| 													 | ||||
| 													dropGroups.add(group); | ||||
| 												} | ||||
| 												else if (nodeName.equalsIgnoreCase("item")) | ||||
| 												{ | ||||
| 													if (dropLists == null) | ||||
| 													{ | ||||
| 														dropLists = new ArrayList<>(); | ||||
| 													} | ||||
| 													 | ||||
| 													final NamedNodeMap dropAttrs = dropNode.getAttributes(); | ||||
| 													final int itemId = parseInteger(dropAttrs, "id"); | ||||
| 													 | ||||
| 													if (ItemTable.getInstance().getTemplate(itemId) == null) | ||||
| 													{ | ||||
| 														LOGGER.warning("DropListItem: Could not find item with id " + itemId + "."); | ||||
| 													} | ||||
| 													else | ||||
| 													{ | ||||
| 														dropLists.add(dropItem); | ||||
| 														dropLists.add(new DropHolder(dropType, itemId, parseLong(dropAttrs, "min"), parseLong(dropAttrs, "max"), parseDouble(dropAttrs, "chance"))); | ||||
| 													} | ||||
| 												} | ||||
| 											} | ||||
| @@ -612,10 +644,21 @@ public class NpcData implements IXmlReader | ||||
| 						template.setClans(clans); | ||||
| 						template.setIgnoreClanNpcIds(ignoreClanNpcIds); | ||||
| 						 | ||||
| 						// Clean old drop groups. | ||||
| 						template.removeDropGroups(); | ||||
| 						 | ||||
| 						// Set new drop groups. | ||||
| 						if (dropGroups != null) | ||||
| 						{ | ||||
| 							template.setDropGroups(dropGroups); | ||||
| 						} | ||||
| 						 | ||||
| 						// Clean old drop lists. | ||||
| 						template.removeDrops(); | ||||
| 						 | ||||
| 						// Set new drop lists. | ||||
| 						if (dropLists != null) | ||||
| 						{ | ||||
| 							template.removeDrops(); | ||||
| 							 | ||||
| 							// Drops are sorted by chance (high to low). | ||||
| 							Collections.sort(dropLists, (d1, d2) -> Double.valueOf(d2.getChance()).compareTo(Double.valueOf(d1.getChance()))); | ||||
| 							for (DropHolder dropHolder : dropLists) | ||||
|   | ||||
| @@ -36,6 +36,7 @@ import org.l2jmobius.gameserver.enums.Race; | ||||
| import org.l2jmobius.gameserver.enums.Sex; | ||||
| import org.l2jmobius.gameserver.model.StatSet; | ||||
| import org.l2jmobius.gameserver.model.actor.Creature; | ||||
| import org.l2jmobius.gameserver.model.holders.DropGroupHolder; | ||||
| import org.l2jmobius.gameserver.model.holders.DropHolder; | ||||
| import org.l2jmobius.gameserver.model.holders.ItemHolder; | ||||
| import org.l2jmobius.gameserver.model.interfaces.IIdentifiable; | ||||
| @@ -107,6 +108,7 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable | ||||
| 	private Map<AISkillScope, List<Skill>> _aiSkillLists; | ||||
| 	private Set<Integer> _clans; | ||||
| 	private Set<Integer> _ignoreClanNpcIds; | ||||
| 	private List<DropGroupHolder> _dropGroups; | ||||
| 	private List<DropHolder> _dropListDeath; | ||||
| 	private List<DropHolder> _dropListSpoil; | ||||
| 	private float _collisionRadiusGrown; | ||||
| @@ -649,12 +651,22 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable | ||||
| 		_ignoreClanNpcIds = ignoreClanNpcIds != null ? Collections.unmodifiableSet(ignoreClanNpcIds) : null; | ||||
| 	} | ||||
| 	 | ||||
| 	public void removeDropGroups() | ||||
| 	{ | ||||
| 		_dropGroups = null; | ||||
| 	} | ||||
| 	 | ||||
| 	public void removeDrops() | ||||
| 	{ | ||||
| 		_dropListDeath = null; | ||||
| 		_dropListSpoil = null; | ||||
| 	} | ||||
| 	 | ||||
| 	public void setDropGroups(List<DropGroupHolder> groups) | ||||
| 	{ | ||||
| 		_dropGroups = groups; | ||||
| 	} | ||||
| 	 | ||||
| 	public void addDrop(DropHolder dropHolder) | ||||
| 	{ | ||||
| 		if (_dropListDeath == null) | ||||
| @@ -673,6 +685,11 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable | ||||
| 		_dropListSpoil.add(dropHolder); | ||||
| 	} | ||||
| 	 | ||||
| 	public List<DropGroupHolder> getDropGroups() | ||||
| 	{ | ||||
| 		return _dropGroups; | ||||
| 	} | ||||
| 	 | ||||
| 	public List<DropHolder> getDropList() | ||||
| 	{ | ||||
| 		return _dropListDeath; | ||||
| @@ -685,11 +702,201 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable | ||||
| 	 | ||||
| 	public List<ItemHolder> calculateDrops(DropType dropType, Creature victim, Creature killer) | ||||
| 	{ | ||||
| 		final List<DropHolder> dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; | ||||
| 		if (dropList == null) | ||||
| 		if (dropType == DropType.DROP) | ||||
| 		{ | ||||
| 			return null; | ||||
| 			// calculate group drops | ||||
| 			List<ItemHolder> groupDrops = null; | ||||
| 			if (_dropGroups != null) | ||||
| 			{ | ||||
| 				groupDrops = calculateGroupDrops(victim, killer); | ||||
| 			} | ||||
| 			 | ||||
| 			// calculate ungrouped drops | ||||
| 			List<ItemHolder> ungroupedDrops = null; | ||||
| 			if (_dropListDeath != null) | ||||
| 			{ | ||||
| 				ungroupedDrops = calculateUngroupedDrops(dropType, victim, killer); | ||||
| 			} | ||||
| 			 | ||||
| 			// return results | ||||
| 			if ((groupDrops != null) && (ungroupedDrops != null)) | ||||
| 			{ | ||||
| 				groupDrops.addAll(ungroupedDrops); | ||||
| 				ungroupedDrops.clear(); | ||||
| 				return groupDrops; | ||||
| 			} | ||||
| 			if (groupDrops != null) | ||||
| 			{ | ||||
| 				return groupDrops; | ||||
| 			} | ||||
| 			if (ungroupedDrops != null) | ||||
| 			{ | ||||
| 				return ungroupedDrops; | ||||
| 			} | ||||
| 		} | ||||
| 		else if ((dropType == DropType.SPOIL) && (_dropListSpoil != null)) | ||||
| 		{ | ||||
| 			return calculateUngroupedDrops(dropType, victim, killer); | ||||
| 		} | ||||
| 		 | ||||
| 		// no drops | ||||
| 		return null; | ||||
| 	} | ||||
| 	 | ||||
| 	private List<ItemHolder> calculateGroupDrops(Creature victim, Creature killer) | ||||
| 	{ | ||||
| 		// level difference calculations | ||||
| 		final int levelDifference = victim.getLevel() - killer.getLevel(); | ||||
| 		final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); | ||||
| 		final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); | ||||
| 		 | ||||
| 		int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; | ||||
| 		List<ItemHolder> calculatedDrops = null; | ||||
| 		for (DropGroupHolder group : _dropGroups) | ||||
| 		{ | ||||
| 			if (dropOccurrenceCounter <= 0) | ||||
| 			{ | ||||
| 				break; | ||||
| 			} | ||||
| 			 | ||||
| 			double groupRate = 1; | ||||
| 			final List<DropHolder> groupDrops = group.getDropList(); | ||||
| 			for (DropHolder dropItem : groupDrops) | ||||
| 			{ | ||||
| 				final int itemId = dropItem.getItemId(); | ||||
| 				final ItemTemplate item = ItemTable.getInstance().getTemplate(itemId); | ||||
| 				final boolean champion = victim.isChampion(); | ||||
| 				 | ||||
| 				// chance | ||||
| 				double rateChance = 1; | ||||
| 				final Float itemChance = Config.RATE_DROP_CHANCE_BY_ID.get(itemId); | ||||
| 				if (itemChance != null) | ||||
| 				{ | ||||
| 					if (itemChance == 0) | ||||
| 					{ | ||||
| 						continue; | ||||
| 					} | ||||
| 					 | ||||
| 					rateChance *= itemChance; | ||||
| 					if (champion && (itemId == Inventory.ADENA_ID)) | ||||
| 					{ | ||||
| 						rateChance *= Config.CHAMPION_ADENAS_REWARDS_CHANCE; | ||||
| 					} | ||||
| 				} | ||||
| 				else if (item.hasExImmediateEffect()) | ||||
| 				{ | ||||
| 					rateChance *= Config.RATE_HERB_DROP_CHANCE_MULTIPLIER; | ||||
| 				} | ||||
| 				else if (victim.isRaid()) | ||||
| 				{ | ||||
| 					rateChance *= Config.RATE_RAID_DROP_CHANCE_MULTIPLIER; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					rateChance *= Config.RATE_DEATH_DROP_CHANCE_MULTIPLIER * (champion ? Config.CHAMPION_REWARDS_CHANCE : 1); | ||||
| 				} | ||||
| 				 | ||||
| 				// premium chance | ||||
| 				if (Config.PREMIUM_SYSTEM_ENABLED && (killer.getActingPlayer() != null) && killer.getActingPlayer().hasPremiumStatus()) | ||||
| 				{ | ||||
| 					if (Config.PREMIUM_RATE_DROP_CHANCE_BY_ID.get(itemId) != null) | ||||
| 					{ | ||||
| 						rateChance *= Config.PREMIUM_RATE_DROP_CHANCE_BY_ID.get(itemId); | ||||
| 					} | ||||
| 					else if (item.hasExImmediateEffect()) | ||||
| 					{ | ||||
| 						// TODO: Premium herb chance? :) | ||||
| 					} | ||||
| 					else if (victim.isRaid()) | ||||
| 					{ | ||||
| 						// TODO: Premium raid chance? :) | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						rateChance *= Config.PREMIUM_RATE_DROP_CHANCE; | ||||
| 					} | ||||
| 				} | ||||
| 				 | ||||
| 				// keep lowest to avoid chance by id configuration conflicts | ||||
| 				groupRate = Math.min(groupRate, rateChance); | ||||
| 			} | ||||
| 			 | ||||
| 			if ((Rnd.nextDouble() * 100) < (group.getChance() * groupRate)) | ||||
| 			{ | ||||
| 				double totalChance = 0; // total group chance is 100 | ||||
| 				final double dropChance = Rnd.nextDouble() * 100; | ||||
| 				GROUP_DROP: for (DropHolder dropItem : groupDrops) | ||||
| 				{ | ||||
| 					if (dropOccurrenceCounter <= 0) | ||||
| 					{ | ||||
| 						break; | ||||
| 					} | ||||
| 					 | ||||
| 					// calculate if item will drop | ||||
| 					totalChance += dropItem.getChance(); | ||||
| 					if (dropChance >= totalChance) | ||||
| 					{ | ||||
| 						continue; | ||||
| 					} | ||||
| 					 | ||||
| 					// check level gap that may prevent to drop item | ||||
| 					if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) | ||||
| 					{ | ||||
| 						continue; | ||||
| 					} | ||||
| 					 | ||||
| 					// create the drop | ||||
| 					final ItemHolder drop = calculateGroupDrop(dropItem, victim, killer); | ||||
| 					 | ||||
| 					// create list | ||||
| 					if (calculatedDrops == null) | ||||
| 					{ | ||||
| 						calculatedDrops = new ArrayList<>(dropOccurrenceCounter); | ||||
| 					} | ||||
| 					 | ||||
| 					// finally | ||||
| 					if (group.getChance() < 100) | ||||
| 					{ | ||||
| 						dropOccurrenceCounter--; | ||||
| 					} | ||||
| 					calculatedDrops.add(drop); | ||||
| 					 | ||||
| 					// no more drops from this group | ||||
| 					break GROUP_DROP; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		// champion extra drop | ||||
| 		if (victim.isChampion()) | ||||
| 		{ | ||||
| 			if ((victim.getLevel() < killer.getLevel()) && (Rnd.get(100) < Config.CHAMPION_REWARD_LOWER_LEVEL_ITEM_CHANCE)) | ||||
| 			{ | ||||
| 				return calculatedDrops; | ||||
| 			} | ||||
| 			if ((victim.getLevel() > killer.getLevel()) && (Rnd.get(100) < Config.CHAMPION_REWARD_HIGHER_LEVEL_ITEM_CHANCE)) | ||||
| 			{ | ||||
| 				return calculatedDrops; | ||||
| 			} | ||||
| 			 | ||||
| 			// create list | ||||
| 			if (calculatedDrops == null) | ||||
| 			{ | ||||
| 				calculatedDrops = new ArrayList<>(); | ||||
| 			} | ||||
| 			 | ||||
| 			if (!calculatedDrops.containsAll(Config.CHAMPION_REWARD_ITEMS)) | ||||
| 			{ | ||||
| 				calculatedDrops.addAll(Config.CHAMPION_REWARD_ITEMS); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		return calculatedDrops; | ||||
| 	} | ||||
| 	 | ||||
| 	private List<ItemHolder> calculateUngroupedDrops(DropType dropType, Creature victim, Creature killer) | ||||
| 	{ | ||||
| 		final List<DropHolder> dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; | ||||
| 		 | ||||
| 		// level difference calculations | ||||
| 		final int levelDifference = victim.getLevel() - killer.getLevel(); | ||||
| @@ -721,7 +928,7 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable | ||||
| 				} | ||||
| 				 | ||||
| 				// calculate chances | ||||
| 				final ItemHolder drop = calculateDrop(dropItem, victim, killer); | ||||
| 				final ItemHolder drop = calculateUngroupedDrop(dropItem, victim, killer); | ||||
| 				if (drop == null) | ||||
| 				{ | ||||
| 					continue; | ||||
| @@ -776,20 +983,82 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable | ||||
| 				calculatedDrops = new ArrayList<>(); | ||||
| 			} | ||||
| 			 | ||||
| 			calculatedDrops.addAll(Config.CHAMPION_REWARD_ITEMS); | ||||
| 			if (!calculatedDrops.containsAll(Config.CHAMPION_REWARD_ITEMS)) | ||||
| 			{ | ||||
| 				calculatedDrops.addAll(Config.CHAMPION_REWARD_ITEMS); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		return calculatedDrops; | ||||
| 	} | ||||
| 	 | ||||
| 	/** | ||||
| 	 * All item drop chance calculations are done by this method. | ||||
| 	 * @param dropItem | ||||
| 	 * @param victim | ||||
| 	 * @param killer | ||||
| 	 * @return ItemHolder | ||||
| 	 */ | ||||
| 	private ItemHolder calculateDrop(DropHolder dropItem, Creature victim, Creature killer) | ||||
| 	private ItemHolder calculateGroupDrop(DropHolder dropItem, Creature victim, Creature killer) | ||||
| 	{ | ||||
| 		final int itemId = dropItem.getItemId(); | ||||
| 		final ItemTemplate item = ItemTable.getInstance().getTemplate(itemId); | ||||
| 		final boolean champion = victim.isChampion(); | ||||
| 		 | ||||
| 		// calculate amount | ||||
| 		double rateAmount = 1; | ||||
| 		if (Config.RATE_DROP_AMOUNT_BY_ID.get(itemId) != null) | ||||
| 		{ | ||||
| 			rateAmount *= Config.RATE_DROP_AMOUNT_BY_ID.get(itemId); | ||||
| 			if (champion && (itemId == Inventory.ADENA_ID)) | ||||
| 			{ | ||||
| 				rateAmount *= Config.CHAMPION_ADENAS_REWARDS_AMOUNT; | ||||
| 			} | ||||
| 		} | ||||
| 		else if (item.hasExImmediateEffect()) | ||||
| 		{ | ||||
| 			rateAmount *= Config.RATE_HERB_DROP_AMOUNT_MULTIPLIER; | ||||
| 		} | ||||
| 		else if (victim.isRaid()) | ||||
| 		{ | ||||
| 			rateAmount *= Config.RATE_RAID_DROP_AMOUNT_MULTIPLIER; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			rateAmount *= Config.RATE_DEATH_DROP_AMOUNT_MULTIPLIER * (champion ? Config.CHAMPION_REWARDS_AMOUNT : 1); | ||||
| 		} | ||||
| 		 | ||||
| 		// premium amount | ||||
| 		if (Config.PREMIUM_SYSTEM_ENABLED && (killer.getActingPlayer() != null) && killer.getActingPlayer().hasPremiumStatus()) | ||||
| 		{ | ||||
| 			if (Config.PREMIUM_RATE_DROP_AMOUNT_BY_ID.get(itemId) != null) | ||||
| 			{ | ||||
| 				rateAmount *= Config.PREMIUM_RATE_DROP_AMOUNT_BY_ID.get(itemId); | ||||
| 			} | ||||
| 			else if (item.hasExImmediateEffect()) | ||||
| 			{ | ||||
| 				// TODO: Premium herb amount? :) | ||||
| 			} | ||||
| 			else if (victim.isRaid()) | ||||
| 			{ | ||||
| 				// TODO: Premium raid amount? :) | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				rateAmount *= Config.PREMIUM_RATE_DROP_AMOUNT; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		// finally | ||||
| 		return new ItemHolder(itemId, (long) (Rnd.get(dropItem.getMin(), dropItem.getMax()) * rateAmount)); | ||||
| 	} | ||||
| 	 | ||||
| 	/** | ||||
| 	 * @param dropItem | ||||
| 	 * @param victim | ||||
| 	 * @param killer | ||||
| 	 * @return ItemHolder | ||||
| 	 */ | ||||
| 	private ItemHolder calculateUngroupedDrop(DropHolder dropItem, Creature victim, Creature killer) | ||||
| 	{ | ||||
| 		switch (dropItem.getDropType()) | ||||
| 		{ | ||||
|   | ||||
| @@ -0,0 +1,49 @@ | ||||
| /* | ||||
|  * This file is part of the L2J Mobius project. | ||||
|  *  | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  *  | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
|  * General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| package org.l2jmobius.gameserver.model.holders; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * @author Mobius | ||||
|  */ | ||||
| public class DropGroupHolder | ||||
| { | ||||
| 	private final List<DropHolder> _dropList = new ArrayList<>(); | ||||
| 	private final double _chance; | ||||
| 	 | ||||
| 	public DropGroupHolder(double chance) | ||||
| 	{ | ||||
| 		_chance = chance; | ||||
| 	} | ||||
| 	 | ||||
| 	public List<DropHolder> getDropList() | ||||
| 	{ | ||||
| 		return _dropList; | ||||
| 	} | ||||
| 	 | ||||
| 	public void addDrop(DropHolder holder) | ||||
| 	{ | ||||
| 		_dropList.add(holder); | ||||
| 	} | ||||
| 	 | ||||
| 	public double getChance() | ||||
| 	{ | ||||
| 		return _chance; | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 MobiusDevelopment
					MobiusDevelopment