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

 

 

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

    • Very nice attempt and good luck with your sales.   Don't let the voices in this topic discourage you. Most of those people are just flexing. So if Celestine decides to write a bootkit or hook system calls or whatever and false-feed your anti-cheat fake info just because it sits in user mode just to go play in a private server with 100 people, I'll tell you one thing:   Who the fuck cares ... 😛   The average player won't / can't make these things, the big players like adrenaline won't bother (yet) (they don't have signed drivers do they ? They also run in user mode I believe). And even if they somehow manage to do so, there's a minority of players who would trust putting this thing on their pc. I wouldn't let anyone tamper with my kernel mode even if he had a signed certificate from Microsoft.    Also, in a scene where collusion has been brought out between bot makers and cheat guards, having many options is a blessing regardless of their strength. It makes colusion, conflict of interest and cartel more difficult, it drains from cheating software creators more resources to focus on more cheat guards, and it gives server owners plurality.    This arms race is a numbers game, you stop 90% of the cheaters you add value to all of us.    A couple of senior devs flexing their powers in a forum are not a concern.   If i'd ever make a server, I would definitelly buy from you, not because you have the best anti-cheat, but because you are not well known yet to be the focus of counter measures.     
    • TILL OPENING LEFT LESS THAN - 2 DAYS ! GRAND OPENING FROM - 10/07/2026, FRIDAY, 20:00 +3 GMT !
    • Add me on discord, lets talk   Discord: splicho
    • Add more information about your project, client, rates etc, this can help you find a developer more easily on your needs!
    • Hey guys! I am working on a new l2 private server and lookin for any developers who may be interested in helping me get it up n running. It would require a ton of work at the beginning, but once I am able to handle things on my own, I can do a lot of the work too. I just need someone who is tech savy to help me get things setup, make serious edits, and help me get this project going.   So far I have the project running on Java through eclipse. I have the server up locally, and pushing to GitHub. I just need help with managing edits to quests, skills, and other random functions to get me rolling.   Please reach out if you are interested!   Some current content I am working on: - Class skill edits - NPC skills, stats, and placements - Quest icons for turn in, completion, pick up etc - Quest logic (i.e. kill x mobs, turn in y amount, trigger a mini solo or mini group boss fights) I can handle the quest texts and story - Ability to more quickly upload changes to inventory, crafting, NPC stats/drops, etc... and gear setup. I would like to quickly add new recipes to shops or mob drops so players can start crafting it themselves
  • Topics

×
×
  • Create New...

Important Information

This community uses essential cookies to function properly. Non-essential cookies and third-party services are used only with your consent. Read our Privacy Policy and We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue..