Jump to content

Recommended Posts

Posted

Hey MxC I'd like to share a simple, but util java modification. It's written for aCis, but you can simple adapt to any chronicle and project by changing the XML parser.

 

This mod will allow you to set specific drops to specific monsters with determined level range, or determined class (RB, GB, Normal Mobs...) with chances.

Why i made this mode? Normally L2J set monster drops like > Monster "X" have Items "Y" as drops. Then, to facilitate servers configuration i did the opposite.

Like: Item "X" will be dropped by a specific range of mobs.

 

There are 2 types of Universal Drops:

1. UniversalDropData.xml


<?xml version="1.0" encoding="utf-8"?>
<list>
	<UniversalDrop itemId="8762" minAmount="1" maxAmount="1" chance="1" premiumApplied="False" monstersIDs="200;201;202"/>
</list>

This mean: Top-Grade Life Stone (8762) will be dropped by monsters with ids (200, 201 and 202) regardless of their levels and class with 1% of chance.

 

2. CategorizedUniversalDropData.xml

 

<?xml version="1.0" encoding="utf-8"?>
<list>
	<CategorizedUniversalDrop itemId="8742" minAmount="1" maxAmount="1" minLevel="40" maxLevel="99" chance="5" premiumApplied="False" dropType="MONSTER"/>
	<CategorizedUniversalDrop itemId="8752" minAmount="1" maxAmount="1" minLevel="40" maxLevel="99" chance="10" premiumApplied="False" dropType="RAIDBOSS"/>
	<CategorizedUniversalDrop itemId="8762" minAmount="1" maxAmount="1" minLevel="40" maxLevel="99" chance="15" premiumApplied="False" dropType="GRANDBOSS"/>
  <CategorizedUniversalDrop itemId="3470" minAmount="1" maxAmount="10" minLevel="40" maxLevel="99" chance="40" premiumApplied="False" dropType="ALL"/>
	
</list>

This mean almost same of uncatecorized universal drops, except by the fact you decide class and level of monsters will drops the specified item.

I think this mod will cover all sorts of monsters in lineage 2 if you know how to use.

 

About

Adaptation:

 

First create a class UniversalDrop.java

 

package dev.universalDrop;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Zaun
 */
public class UniversalDrop
{
	private List<Integer> monsters = new ArrayList<>();
	private int itemId;
	private int chance;
	private int[] amount = new int[2];
	private int[] level = new int[2];
	private boolean premiumApplied;
	private UniversalDropType dropType = UniversalDropType.ALL;
	
	/**
	 * @return the monsters
	 */
	public List<Integer> getMonsters()
	{
		return monsters;
	}
	
	/**
	 * @param monsters the monsters to set
	 */
	public void setMonsters(List<Integer> monsters)
	{
		this.monsters = monsters;
	}
	
	/**
	 * @return the id
	 */
	public int getItemId()
	{
		return itemId;
	}
	
	/**
	 * @param id the id to set
	 */
	public void setItemId(int id)
	{
		this.itemId = id;
	}
	
	/**
	 * @return the amount
	 */
	public int[] getAmount()
	{
		return amount;
	}
	
	/**
	 * @param amount the amount to set
	 */
	public void setAmount(int[] amount)
	{
		this.amount = amount;
	}
	
	/**
	 * @return the chance
	 */
	public int getChance()
	{
		return chance;
	}
	
	/**
	 * @param chance the chance to set
	 */
	public void setChance(int chance)
	{
		this.chance = chance;
	}
	
	/**
	 * @return the premiumApplied
	 */
	public boolean isPremiumApplied()
	{
		return premiumApplied;
	}
	
	/**
	 * @param premiumApplied the premiumApplied to set
	 */
	public void setPremiumApplied(boolean premiumApplied)
	{
		this.premiumApplied = premiumApplied;
	}
	
	/**
	 * @return the dropType
	 */
	public UniversalDropType getDropType()
	{
		return dropType;
	}
	
	/**
	 * @param dropType the dropType to set
	 */
	public void setDropType(UniversalDropType dropType)
	{
		this.dropType = dropType;
	}
	
	/**
	 * @return the level
	 */
	public int[] getLevel()
	{
		return level;
	}
	
	/**
	 * @param level the level to set
	 */
	public void setLevel(int[] level)
	{
		this.level = level;
	}
}

 

 

Then create UniversalDropData.java

 

package dev.universalDrop.data.xml;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.sf.l2j.commons.data.xml.XMLDocument;

import net.sf.l2j.gameserver.data.ItemTable;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import dev.universalDrop.UniversalDrop;
import dev.universalDrop.UniversalDropType;

/**
 * @author Zaun
 */
public class UniversalDropData extends XMLDocument
{
	private Map<Integer, UniversalDrop> universalDrops = new HashMap<>();
	private Map<Integer, UniversalDrop> categorizedUniversalDrops = new HashMap<>();
	
	public UniversalDropData()
	{
		load();
	}
	
	public static UniversalDropData getInstance()
	{
		return SingleTonHolder._instance;
	}
	
	private static class SingleTonHolder
	{
		protected static final UniversalDropData _instance = new UniversalDropData();
	}
	
	public void reload()
	{
		universalDrops.clear();
		categorizedUniversalDrops.clear();
		load();
	}
	
	@Override
	protected void load()
	{
		loadDocument("./data/xml/UniversalDrop/UniversalDropData.xml");
		LOG.info("UniversalDropData: Loaded " + universalDrops.size() + " Universal drops.");
		
		loadDocument("./data/xml/UniversalDrop/CategorizedUniversalDropData.xml");
		LOG.info("UniversalDropData: Loaded " + categorizedUniversalDrops.size() + " Universal drops.");
	}
	
	@Override
	protected void parseDocument(Document doc, File f)
	{
		try
		{
			
			// First element is never read.
			final Node n = doc.getFirstChild();
			
			for (Node o = n.getFirstChild(); o != null; o = o.getNextSibling())
			{
				if (!"UniversalDrop".equalsIgnoreCase(o.getNodeName()))
					continue;
				
				NamedNodeMap attrs = o.getAttributes();
				UniversalDrop universalDrop = null;
				
				int itemId = Integer.parseInt(attrs.getNamedItem("itemId").getNodeValue());
				int minAmount = Integer.parseInt(attrs.getNamedItem("minAmount").getNodeValue());
				int maxAmount = Integer.parseInt(attrs.getNamedItem("maxAmount").getNodeValue());
				int chance = Integer.parseInt(attrs.getNamedItem("chance").getNodeValue());
				String monstersIDs = attrs.getNamedItem("monstersIDs").getNodeValue();
				boolean premiumApplied = Boolean.parseBoolean(attrs.getNamedItem("premiumApplied").getNodeValue());
				if (ItemTable.getInstance().getTemplate(itemId) != null)
				{
					universalDrop = new UniversalDrop();
					
					universalDrop.setItemId(itemId);
					universalDrop.setChance(chance);
					
					int[] amount = new int[2];
					amount[0] = minAmount;
					amount[1] = maxAmount;
					
					universalDrop.setAmount(amount);
					int[] level = new int[2];
					level[0] = -1;
					level[1] = 0;
					
					universalDrop.setLevel(level);
					universalDrop.setDropType(UniversalDropType.ALL);
					List<Integer> monsters = new ArrayList<>();
					for (String monsterId : monstersIDs.split(";"))
					{
						monsters.add(Integer.parseInt(monsterId));
					}
					universalDrop.setMonsters(monsters);
					universalDrop.setPremiumApplied(premiumApplied);
					universalDrops.put(itemId, universalDrop);
				}
				else
				{
					LOG.warning("Item Id: " + itemId + " is an invalid item for Universal drop ID: " + itemId + "(uncategorized).");
				}
				
			}
			
			for (Node o = n.getFirstChild(); o != null; o = o.getNextSibling())
			{
				if (!"CategorizedUniversalDrop".equalsIgnoreCase(o.getNodeName()))
					continue;
				
				NamedNodeMap attrs = o.getAttributes();
				UniversalDrop universalDrop = null;
				
				int itemId = Integer.parseInt(attrs.getNamedItem("itemId").getNodeValue());
				int minAmount = Integer.parseInt(attrs.getNamedItem("minAmount").getNodeValue());
				int maxAmount = Integer.parseInt(attrs.getNamedItem("maxAmount").getNodeValue());
				int minLevel = Integer.parseInt(attrs.getNamedItem("minLevel").getNodeValue());
				int maxLevel = Integer.parseInt(attrs.getNamedItem("maxLevel").getNodeValue());
				int chance = Integer.parseInt(attrs.getNamedItem("chance").getNodeValue());
				UniversalDropType dropType = UniversalDropType.valueOf(attrs.getNamedItem("dropType").getNodeValue());
				boolean premiumApplied = Boolean.parseBoolean(attrs.getNamedItem("premiumApplied").getNodeValue());
				if (ItemTable.getInstance().getTemplate(itemId) != null)
				{
					universalDrop = new UniversalDrop();
					
					universalDrop.setItemId(itemId);
					universalDrop.setChance(chance);
					int[] amount = new int[2];
					amount[0] = minAmount;
					amount[1] = maxAmount;
					
					universalDrop.setAmount(amount);
					
					int[] level = new int[2];
					level[0] = minLevel;
					level[1] = maxLevel;
					universalDrop.setDropType(dropType);
					universalDrop.setLevel(level);
					List<Integer> monsters = new ArrayList<>();
					universalDrop.setMonsters(monsters);
					universalDrop.setPremiumApplied(premiumApplied);
					categorizedUniversalDrops.put(itemId, universalDrop);
					
				}
				else
				{
					LOG.warning("Item Id: " + itemId + " is an invalid item for Universal drop ID: " + itemId + "(categorized).");
				}
				
			}
		}
		catch (Exception e)
		{
			LOG.warning("Universal Drop Data: Error while creating table: " + e);
			e.printStackTrace();
		}
	}
	
	public List<Integer> getUncategorizedUniversalDropItemsIds()
	{
		List<Integer> items = new ArrayList<>();
		
		for (Map.Entry<Integer, UniversalDrop> entry : universalDrops.entrySet())
		{
			items.add(entry.getKey());
		}
		return items;
	}
	
	public List<Integer> monstersWithDropId(int itemId)
	{
		
		for (Map.Entry<Integer, UniversalDrop> entry : universalDrops.entrySet())
		{
			if (entry.getValue().getItemId() == itemId)
			{
				return entry.getValue().getMonsters();
			}
		}
		return new ArrayList<>();
	}
	
	public UniversalDrop getUncategorizedUniversalDropById(int itemId)
	{
		return universalDrops.get(itemId);
	}
	
	public UniversalDrop getCategorizedUniversalDropById(int itemId)
	{
		return categorizedUniversalDrops.get(itemId);
	}
	
	public Collection<UniversalDrop> getAllCategorizedUniversalDrops()
	{
		return categorizedUniversalDrops.values();
	}
}

 

 

Then create this enum

 

package dev.universalDrop;

/**
 * @author Zaun
 */
public enum UniversalDropType
{
	ALL,
	RAIDBOSS,
	GRANDBOSS,
	MONSTER
}

 

 

Then you need to modify your core. If you using aCis find class "Attackable.java" in "...model.actor" package.

 

add this method:

public void universalDropItem(UniversalDrop universalDrop, Player player)
	{
		int chance = universalDrop.getChance();
		int amount = 0;
		IntIntHolder item = null;
		int premiumBonus = 0;
		Premium premium = player.getPremium();
		if (Rnd.get(100) <= chance)
		{
			amount = Rnd.get(universalDrop.getAmount()[0], universalDrop.getAmount()[1]);
			item = new IntIntHolder(universalDrop.getItemId(), amount);
			if (universalDrop.isPremiumApplied() && player.getPremium().getLevel() > 0)
			{
				premiumBonus = (int) (amount * premium.getItemDropRate()) - amount;
				amount *= premium.getItemDropRate();
				item.setPremiumBonus(premiumBonus);
			}
			
			// Check if the autoLoot mode is active
			if ((isRaid() && Config.AUTO_LOOT_RAID) || (!isRaid() && Config.AUTO_LOOT))
				player.doAutoLoot(this, item); // Give this or these Item(s) to the Player that has killed the L2Attackable
			else
				dropItem(player, item); // drop the item on the ground
				
		}
	}

Finde method:

public void doItemDrop(NpcTemplate npcTemplate, Creature mainDamageDealer)

add this code:

// Custom universal drop (uncategorized)
		for (int dropId : UniversalDropData.getInstance().getUncategorizedUniversalDropItemsIds())
		{
			if (UniversalDropData.getInstance().monstersWithDropId(dropId).contains(getNpcId()))
			{
				UniversalDrop universalDrop = UniversalDropData.getInstance().getUncategorizedUniversalDropById(dropId);
				universalDropItem(universalDrop, player);
			}
		}
		
		// Custom universal drop (categorized)
		for (UniversalDrop universalDrop : UniversalDropData.getInstance().getAllCategorizedUniversalDrops())
		{
			UniversalDropType dropType = universalDrop.getDropType();
			// if monster level doesn't correspond to drop level limit, stop execution
			if (!(getLevel() >= universalDrop.getLevel()[0] && getLevel() <= universalDrop.getLevel()[1]))
			{
				continue;
			}
			if (dropType.equals(UniversalDropType.ALL))
			{
				universalDropItem(universalDrop, player);
			}
			else if (dropType.equals(UniversalDropType.RAIDBOSS))
			{
				if (!(this instanceof RaidBoss))
				{
					continue;
				}
				universalDropItem(universalDrop, player);
			}
			else if (dropType.equals(UniversalDropType.GRANDBOSS))
			{
				if (!(this instanceof GrandBoss))
				{
					continue;
				}
				universalDropItem(universalDrop, player);
			}
			else if (dropType.equals(UniversalDropType.MONSTER))
			{
				if (!(this instanceof Monster))
				{
					continue;
				}
				universalDropItem(universalDrop, player);
			}
			
		}

 

Then add this line into your GameServer.java

UniversalDropData.getInstance();

This will call the parser of drops.

 

Now you need to go into your data pack and create a folder inside ./data/ and then create these XML files:

 

"./data/xml/UniversalDrop/UniversalDropData.xml"
./data/xml/UniversalDrop/CategorizedUniversalDropData.xml

 

You can use the post start xml code to create XML files.

 

If you need any help to adapt this code in your source just send me a PM. Enjoy

 

 

  • Like 1
  • Upvote 1
Posted (edited)

Thank you for sharing good job!!!

Edit: the only "flaw" I can note is that your chance is integer but drops use normally 0.001224% for example so it should be a float value for more accurate drops? maybe there is a way for improvement what do you think?

Edited by Nightw0lf
Posted (edited)
  • You can use stream for getUncategorizedUniversalDropItemsIds / monstersWithDropId for oneliners.
  • monstersWithDropId should be properly named, and shouldn't create an empty List (but return Collections.emptyList() instead).

  • IXmlReader use would cut your parser class by alot.

  • equals can be replaced by == for enum comparison. Added to that, you can use NpcTemplate#isType for easy instance comparison (if you don't mind the enum.toString()).

  • getUncategorizedUniversalDropById use should be null checked.

  • You can/should use {} wildcard for LOGGER(s) parameters, cf. :
     

    LOGGER.info("Loaded {} crests.", _crests.size());

     

Edited by Tryskell
  • 4 years later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Posts

    • We've added 5% discounts for bulk purchases of Google accounts for orders of 300 or more, and 10% for orders of 500 or more. The discount is applied automatically when you place your order! The discount is indicated in the product title and description for each category.  
    • 🎄 CHRISTMAS EVENT 🎄   ‼️ Information and details: https://forum.l2harbor.com/threads/rozhdestvenskie-xlopoty-christmas-chores.9430/post-171464
    • METATG.ORG Direct Telegram Service Provider A bonus of +7% on every order! *We add 7% more followers than your ordered amount to proactively cover potential drops and guarantee you an honest result." Telegram Followers - Price per 1000 SUBSCRIBERS Subscribers 3 days - $0.10 ~ 8 RUB Subscribers. Daily Completion: 200,000,000 Subscribers 7 days - $0.17 ~ 13.6 RUB Subscribers. Daily Completion: 200,000,000 Subscribers 14 days - $0.20 ~ 16 RUB Subscribers. Daily Completion: 200,000,000 Subscribers 30 days - $0.30 ~ 24 RUB Subscribers. Daily Completion: 200,000,000 Subscribers 60 days - $0.40 ~ 32 RUB Subscribers, 14-day guarantee. Daily Completion: 200,000,000 Subscribers 90 days (Super Fast) - $0.50 ~ 40 RUB Subscribers, 14-day guarantee. Daily Completion: 200,000,000 Subscribers 120 days (Super Fast) - $0.60 ~ 48 RUB Subscribers, 14-day guarantee. Daily Completion: 200,000,000 Subscribers Lifetime (Super Fast) - $0.70 ~ 56 RUB Lifetime Subscribers. 14-day guarantee. Daily Completion: 200,000,000 Telegram Services - Price per 1000 Post Views - $0.06 ~ 5 RUB Reactions - $0.08 ~ 6.5 RUB Bot Starts - $0.10 ~ 8 RUB Bot Starts with referrals - $0.15 ~ 12 RUB DISCOUNTS and CASHBACK for large volumes Direct Supplier. We work from our own accounts with our own software! High execution speed. Multiple payment methods. We work 24/7! Additional discounts are discussed for volumes starting from $1000 per day. SUPPORT 24/7 - TELEGRAM WEBSITE 24/7 - METATG.ORG
    • Added: a brand-new default dashboard template. You can now add multiple game/login server builds. Full support for running both PTS & L2J servers simultaneously, with switching between them. Payment systems: added OmegaPay and Pally (new PayPal-style API). Account history now stores everything: donations, items delivered to characters, referrals, transfers between game accounts, and coin transfers to another master account. Personal Promo Code System: you can create a promo code and assign it to a user or promoter. When donating, a player can enter this promo code to receive bonus coins, and the promo code owner also receives a bonus — all fully configurable in the admin panel.     Look demo site: demo
  • Topics

×
×
  • Create New...

AdBlock Extension Detected!

Our website is made possible by displaying online advertisements to our members.

Please disable AdBlock browser extension first, to be able to use our community.

I've Disabled AdBlock