Jump to content

Recommended Posts

Posted

All Credits of this guide to g13h4ck3r:

 

A player will talk to the NPC, select an item (weapon, armor, or jewel), and the NPC will safely (100% chance) enchant this item for some payment. By "enchant" I mean making the item +1 higher than the enchantment it already has.

I will keep this script simple. People can customize it further. I shall explain everything one step at a time, but I will skip parts I believe to be "trivial"...

 

Ok...so let's assume that we have a custom NPC with id 90000 whom we will use for this. Anybody can easily create the default htm for this NPC. At the bottom, instead of the typical "Quest" link, we could do:

Line number On/Off | Expand/Contract | Select all

 

<a action="bypass -h npc_%objectId%_Quest safe_enchant">Enchant an Item</a>

 

When a player clicks on that link, it will directly call the onTalk section of the script that we are about to write.

 

So, let's go to the "data/scripts/custom" folder, create a new subdirectory called "safe_enchant" and create a new file in there, name __init__.py

 

This file will follow the quest prototype, so let's start up with the usual quest stuff. For now, the file should look something like this:

 

import sys
from net.sf.l2j.gameserver.model.quest import State
from net.sf.l2j.gameserver.model.quest import QuestState
from net.sf.l2j.gameserver.model.quest.jython import QuestJython as JQuest

qn = "safe_enchant"

ENCHANT_NPC = 90000

class Quest (JQuest) :

 def __init__(self,id,name,descr): JQuest.__init__(self,id,name,descr)

 def onEvent (self,event,st) :
 return

 def onTalk (self,npc,player):
   htmltext = "<html><body>You are either not on a quest that involves this NPC, or you don't meet this NPC's minimum quest requirements.</body></html>"
   st = player.getQuestState(qn)
   if not st : return htmltext
   return htmltext

QUEST    = Quest(9000,qn,"Custom")

QUEST.addStartNpc(ENCHANT_NPC)
QUEST.addTalkId(ENCHANT_NPC)

 

This has set us up to start working with the script. Now, what we need next is some way for the player to see what items are available for enchanting among the items in the inventory, select the item, and get the enchantment...

 

For the purposes of this simple sample, I will assume that all enchantments cost 1,000,000 adena. You can easily change this...So let's first create an htm where the NPC explains how enchantment works, and what it costs. It is best to not mess with equipped items, just due to some exploits that have existed in the past. So let's tell the player we will only do this for unequipped items. This will do:

 

<html><body>Enchanter:<br>
Select an item you wish to enchant and I will add 1 to its enchantment level!  All enchantments cost 1,000,000 adena.  Please unequip the item that you wish to enchant, if you are currently wearing it.  Are you ready?<br>
<a action="bypass -h Quest safe_enchant choose_item">"I would like to enchant an item."</a></body></html>

 

Let's name this 1.htm and place it in the same folder with the __init__.py file that we just created. This htm will need to be returned as soon as the player clicks on the link from the default script, so we'll put code in onTalk to display this:

 

if npc.getNpcId() == ENCHANT_NPC :
     htmltext = "1.htm"

 

Next, we want to make it such that, when a player clicks on the link, he gets a list of items from his inventory to choose from. So we'll have to add some code in onEvent and handle this...

 

 if event == "choose_item" :
     htmltext = ""
     for Item in st.getPlayer().getInventory().getItems():
       #given an item instance, get the item template to check what type it is
       itemType = Item.getItem().getItemType().toString()
       if itemType in ['None','Light','Heavy','Magic','Shield','Sword','Blunt','Dagger','Bow','Pole','Etc','Fist','Dual Sword','Dual Fist','Big Sword','Big Blunt','Ancient','Crossbow','Rapier'] and item.getItem().getCrystalType() > 0 :
           htmltext += "<a action=\"bypass -h Quest safe_enchant enchantItem_" + str(Item.getObjectId()) +"\">" + Item.getItem().getName() + "+" + str(Item.getEnchantLevel()) + "</a><br>"
     if htmltext == "":
       htmltext = "You have no enchantable items in your inventory"
     htmltext = "<html><body>Enchanter:<br>Please choose which item you wish me to enchant, from the below list:<br>" + htmltext + "</body></html>"

 

Notice that here selecting the enchantable items by item type is a bit of an overkill, but I purposely made it this way so you can more easily exclude jewels (None type), certain types of armor, or certain types of weapon (I can forsee people not wanting to allow safe enchanting of dual swords, for example). You can add similar checks for bodypart too, if you want...In addition, I added a check that the crystalType is not 0, meaning that only D Grade or higher can be enchanted. Each link shows the name of the item as well as the enchantment level, to help the player identify which weapon he/she is selecting. It uses the server-side item name, which actually includes the SA name, if any. However, it will not include info about the augmentations. You can add this info if you wish, but in general it should not be too important...Also, you could change this piece of code a little in order to show prices. That is, if you wish to have a different price for each item, next to each item you could also display the price. Of course, your script will need some way to identify what price to charge and the details of that are all for you to decide.

When a player clicks on a link, the objectId of the selected item is passed back to the script, so we can guarantee that we grab exactly the item the user selected, not just any item of the same name ;)

 

At the next step, we must be careful, we will identify that the player has selected an item, find that item from the inventory, and enchant it. We have to be careful here as players like to try to cheat and exploit by selecting an item and quickly getting rid of it, or such naughty actions ;)

In addition, we must ensure that the player has enough adena (or otehr items) to pay for this exchange.

This again needs to happen in onEvent

 

  elif event.startswith("enchantItem_"):
     # get the object id out of the event string
     objId = int(event.replace("enchantItem_", ""))
     # to avoid exploitation, check if the stored objectId still corresponds to an existing item
     # and if that item is still not equipped
     Item = st.getPlayer().getInventory().getItemByObjectId(objId )
     if Item and not Item.isEquipped() :
       if st.getQuestItemsCount(57) >= 1000000 :
         Item.setEnchantLevel(Item.getEnchantLevel()+1)
         st.takeItems(57, 1000000)
         htmltext = "congratulations.htm"
       else :
         htmltext = "notEnoughItems.htm"
     else :
       htmltext = "cheater.htm"

 

Here, we just checked if a link that started with "enchantItem_" was clicked, then we grabbed the remaining of the link as a number, the object id, found the item, checked if it's still in the inventory and not equipped...If any of this fails, the player is trying to cheat, so we return "cheater.htm". If all is good, we finally also check if the player has enough adena. If no, we return some dialog text to inform the player about "not enough adena" (or other items). If the player does have enough adena, we add 1 to the enchantment and take our 1,000,000 adena fee before returning a "congratulations.htm". Of course, you'll have to create those two htm files, but they can be pretty generic, so I'll leave those up to you.

The line "st.takeItems(57,1000000)" could be changed if you wanted to charge a different amount or a different item than in my script and with some effort you could even make it charge a different amount for each input item...

 

Finally, at your option, you may wish to keep your server's limits for enchantments. That is, you may wish to allow players to enchant their items beyond the server's max, or you may wish to limit them by your server's configs. If you wish not to impose a limit, everything we discussed above can be put together and the script will be done.

 

So just putting together all the parts from above for the __init__.py script , the overall script will be as follows:

 

import sys
from net.sf.l2j.gameserver.model.quest import State
from net.sf.l2j.gameserver.model.quest import QuestState
from net.sf.l2j.gameserver.model.quest.jython import QuestJython as JQuest

qn = "safe_enchant"

ENCHANT_NPC = 90000
acceptableItemTypes = ['None','Light','Heavy','Magic','Shield','Sword','Blunt','Dagger','Bow','Pole','Etc','Fist','Dual Sword','Dual Fist','Big Sword','Big Blunt','Ancient','Crossbow','Rapier']

class Quest (JQuest) :

 def __init__(self,id,name,descr): JQuest.__init__(self,id,name,descr)

 def onEvent (self,event,st) :
   if event == "choose_item" :
     htmltext = ""
     for Item in st.getPlayer().getInventory().getItems():
       #given an item instance, get the item template to check what type it is
       itemType = Item.getItem().getItemType().toString()
       if itemType in acceptableItemTypes and item.getItem().getCrystalType() > 0 :
           htmltext += "<a action=\"bypass -h Quest safe_enchant enchantItem_" + str(Item.getObjectId()) +"\">" + Item.getItem().getName() + "+" + str(Item.getEnchantLevel()) + "</a><br>"
     if htmltext == "":
       htmltext = "You have no enchantable items in your inventory"
     htmltext = "<html><body>Enchanter:<br>Please choose which item you wish me to enchant, from the below list:<br>" + htmltext + "</body></html>"
   elif event.startswith("enchantItem_"):
     # get the object id out of the event string
     objId = int(event.replace("enchantItem_", ""))
     # to avoid exploitation, check if the stored objectId still corresponds to an existing item
     # and if that item is still not equipped
     Item = st.getPlayer().getInventory().getItemByObjectId(objId )
     if Item and not Item.isEquipped() :
       if st.getQuestItemsCount(57) >= 1000000 :
         Item.setEnchantLevel(Item.getEnchantLevel()+1)
         st.takeItems(57, 1000000)
         htmltext = "congratulations.htm"
       else :
         htmltext = "notEnoughItems.htm"
     else :
       htmltext = "cheater.htm"
   return htmltext

 def onTalk (self,npc,player):
   htmltext = "<html><body>You are either not on a quest that involves this NPC, or you don't meet this NPC's minimum quest requirements.</body></html>"
   st = player.getQuestState(qn)
   if not st : return htmltext
   if npc.getNpcId() == ENCHANT_NPC :
      htmltext = "1.htm"
   return htmltext

QUEST    = Quest(9000,qn,"Custom")

QUEST.addStartNpc(ENCHANT_NPC)

 

If you additionally wish to limit the max enchantment based on your server configs, you will need to first import the config class into your script. That is, at the top of the script you will need the line:

Line number On/Off | Expand/Contract | Select all

from from net.sf.l2j import Config

Next, you will need to add some if/else statements where you check the item type, the config option for max-enchantment for that item type, and if the item is already at or above the max. Just before getting the adena, we can add these few lines:

 

itemType = Item.getItem().getItemType().toString()
weapons = ['Sword','Blunt','Dagger','Bow','Pole','Etc','Fist','Dual Sword','Dual Fist','Big Sword','Big Blunt','Ancient','Crossbow','Rapier']
armors = ['Light','Heavy','Magic','Shield']
jewels = ['None']
if (itemType in weapons and Item.getEnchantLevel >= Config.ENCHANT_MAX_WEAPON) or (IitemType in armors and Item.getEnchantLevel >= Config.ENCHANT_MAX_ARMOR) or (itemType in jewels and Item.getEnchantLevel >= Config.ENCHANT_MAX_JEWELRY) :
  htmltext = "reachedMaxEnchant.htm"
else: #... take the adena, give the +1 enchant...

 

So again, putting everything together, we have:

 

import sys
from net.sf.l2j import Config
from net.sf.l2j.gameserver.model.quest import State
from net.sf.l2j.gameserver.model.quest import QuestState
from net.sf.l2j.gameserver.model.quest.jython import QuestJython as JQuest

qn = "safe_enchant"

ENCHANT_NPC = 90000
weapons = ['Sword','Blunt','Dagger','Bow','Pole','Etc','Fist','Dual Sword','Dual Fist','Big Sword','Big Blunt','Ancient','Crossbow','Rapier']
armors = ['Light','Heavy','Magic','Shield']
jewels = ['None']
acceptableItemTypes = weapons+armors+jewels

class Quest (JQuest) :

 def __init__(self,id,name,descr): JQuest.__init__(self,id,name,descr)

 def onEvent (self,event,st) :
   if event == "choose_item" :
     htmltext = ""
     for Item in st.getPlayer().getInventory().getItems():
       # given an item instance, get the item template to check what type it is
       itemType = Item.getItem().getItemType().toString()
       itemGrade = Item.getItem().getCrystalType()
       if itemType in acceptableItemTypes and itemGrade > 0 :
         htmltext += "<a action=\"bypass -h Quest safe_enchant enchantItem_" + str(Item.getObjectId()) +"\">" + Item.getItem().getName() + "+" + str(Item.getEnchantLevel()) + "</a><br>"
     if htmltext == "":
       htmltext = "You have no enchantable items in your inventory"
     htmltext = "<html><body>Enchanter:<br>Please choose which item you wish me to enchant, from the below list:<br>" + htmltext + "</body></html>"
   elif event.startswith("enchantItem_"):
     # get the object id out of the event string
     objId = int(event.replace("enchantItem_", ""))
     # to avoid exploitation, check if the stored objectId still corresponds to an existing item
     # and if that item is still not equipped
     Item = st.getPlayer().getInventory().getItemByObjectId(objId )
     if Item and not Item.isEquipped() :
       itemType = Item.getItem().getItemType().toString()
       itemEnchant = Item.getEnchantLevel()
       if st.getQuestItemsCount(7267) >= 10 :
          if (itemType in weapons and itemEnchant >= Config.ENCHANT_MAX_WEAPON) or (itemType in armors and itemEnchant >= Config.ENCHANT_MAX_ARMOR) or (itemType in jewels and itemEnchant >= Config.ENCHANT_MAX_JEWELRY) :
             htmltext = "reachedMaxEnchant.htm"
          else :
            Item.setEnchantLevel(itemEnchant+1)
            st.takeItems(57, 1000000)
            htmltext = "congratulations.htm"
       else :
         htmltext = "notEnoughItems.htm"
     else :
       htmltext = "cheater.htm"
   return htmltext

 def onTalk (self,npc,player):
   htmltext = "<html><body>You are either not on a quest that involves this NPC, or you don't meet this NPC's minimum quest requirements.</body></html>"
   st = player.getQuestState(qn)
   if not st : return htmltext
   if npc.getNpcId() == ENCHANT_NPC :
      htmltext = "1.htm"
   return htmltext

QUEST    = Quest(9000,qn,"Custom")

QUEST.addStartNpc(ENCHANT_NPC)
QUEST.addTalkId(ENCHANT_NPC)

 

Again, you will need to add the file reachedMaxEnchant.htm in the same directory and put some text to it, but that should be simple. Also, if you prefer, you can limit the max enchantment from this script to any other value (for example if your config allows up to +50 but you want people to get safe enchants for the script only up to +25) simply by replace the Config.ENCHANT_MAX_YYYYYY with the value you like...

 

##############################################################################

 

Well Its Not So DIficul , u just need time  ::)

Posted

:P.....

 

Its very Nice Share and Guide...Thx GrisoM!!

 

Keep Up....

 

 

(I have a small idea! If you can do this npc to spawn after kill one rb(your choice raid)!

And Spawn for only 1 minute!!

But this 1 min you can give +3 free in your weapon.....) :D

 

 

I try to think him how it could become this Script!!

 

Nice Again...Great Guide!

Posted

nah, the credits are WRONG !

 

Rofl how shouold i know if the guy from where i read this from didnt give any credits and start the post saying :

 

Here I will share bla bla bla xD

 

(I have a small idea! If you can do this npc to spawn after kill one rb(your choice raid)!

And Spawn for only 1 minute!!

But this 1 min you can give +3 free in your weapon.....) :D

 

That would be crazy ;P

 

It is a really nice share! I will test it myself and if I will post results ;) Thanks GriSsS

 

Hope it works Cos for what i have seen this could be really cool

  • 5 months later...
  • 2 months 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

    • ============================================================== VIKOS AUTOBOTS — live bots for Lineage 2 servers on Lucera2 Interlude and Classic ============================================================== An empty server loses players. Vikos Autobots fills the world with bots that act like real players: they farm, walk around towns, join clans, fight wars, go to raids, sieges, events and the Olympiad, trade and talk in chat. Installs on top of a Lucera2 build: one .ext.jar + the data/autobots folder + a config. Database tables are created automatically. WHAT THE BOTS DO • Farm zones by level along set routes; they do not steal mobs from players or other bots. Leave town through gatekeepers (custom ones and NPC bypasses too) and come back. • Some bots leave the farm to walk around town while others keep farming, so towns are not empty. They walk, talk to NPCs and sit down to rest (can be turned off for PvP servers). • After a server restart bots carry on where they were: same zone, same route, no mass teleport to town. • Class-based combat AI: fighters, archers, mages, healers, buffers, summoners. Auto parties, healers resurrect the dead. • Real fishing with core skills (cast, Pumping / Reeling, fish fight). Rod by grade, bait and fishing shots are handed out automatically; spots have stands, a bot limit, levels and time. • Gear by class and level, a separate Olympiad set and an "online time" set; custom skills by class and by online time (same idea as time gear). Random enchant, Life Stones, dyes, buffs. Shots, potions, arrows and scrolls restock on their own; bot items never reach the economy. • Clans and alliances with generated crests, clan wars, clan defense, PvP server mode. PvP zones (arena, Coliseum, custom): admin Send or a clock like TvT (14:00;16:00). • Raids and epics (Antharas, Valakas, Baium; more via XML), with a gathering in town or a direct teleport to the boss. Castle sieges, TvT / CTF / DM, Olympiad 1v1 and 3v3, Seven Signs. • Private stores (sell and buy), RU/EN chat, AI chat (Groq or any OpenAI-compatible API), bot replies go through your server chat filter (chatfilters.xml), player PMs forwarded to admin Telegram. • Admin panel: //autobots panel. Online schedule, own AI thread pool built for hundreds of bots online, one commented config and XML files checked at server start. INCLUDED: VIKOS AUTOBOTS CONFIGURATOR (Windows) • A single .exe with Java inside, nothing to install. • Works right on your VPS over SSH/SFTP: edit and save, the files go straight to the server; the old version goes to backup. • World map from server geodata, automatic route building by the core movement rules with checks. • Farm zones, towns, raids, sieges, TvT, PvP zones, fishing spots. • Editors with client icons: equipment (farm / online time / Olympiad), skills (regular and by online time), buffs, Life Stones, supplies, dyes. Finds config mistakes by itself. • Interlude and Classic, RU/EN interface, light and dark theme, F1 help. ============================================================== VIKOS AUTOBOTS — REAL SERVER LOAD Measured ============================================================== Interlude test server on Lucera2, a regular VPS: • 4 vCPU, 8 GB RAM, Debian 13 • Java 17 (Temurin), -Xmx5G • MariaDB -------------------------------------------------------------- RESULTS -------------------------------------------------------------- CPU is a one-minute average for the whole game server process: core, mobs and bots together. 400% = all 4 cores. Bots online Server CPU (of 400%) Share of VPS 1000 ~103% ~26% 1200 ~105% ~26% 1500 ~120-150% ~30-37% • An empty server without bots uses ~40% by itself, so 1000 bots add about one core. • The bot AI itself (all bots together) takes 10-15% of one core for 1000-1500 bots. • Memory is stable, no leaks: - after GC the heap stays at ~2.8-2.9 GB with 800, 1000 and 1200 bots — about the same as the server uses by itself, without bots; - the heap low point does not grow over time: memory does not pile up, it is freed on every garbage collection; - a full night with 1500 bots with no out-of-memory errors (OutOfMemory), the server never crashed; - with -Xmx5G there is more than 2 GB of headroom. • A full night with 1500 bots ran stable. Meanwhile the bots live a full life: farming along routes, walking around towns, fishing, clans, scheduled PvP zones, TvT/CTF, Olympiad and raids. -------------------------------------------------------------- SETTINGS USED FOR THESE NUMBERS -------------------------------------------------------------- • AutobotsIterationDelayMs = 500 A bot thinks twice per second: enough to act like a real player without wasting CPU. • AutobotsMaxBots = 1000-1500 • ai.properties: AiTaskActiveDelay = 500 AggroCheckInterval = 500 Most of the load comes not from the bot code but from the mobs around the bots: bots bring locations to life, and the mobs start checking whom to attack. With these values they check twice per second instead of four times. The load is almost halved, and you can't see the difference: a mob notices its target a fraction of a second later. • MariaDB: innodb_flush_log_at_trx_commit = 2 The database flushes its log to disk once per second instead of after every small query. With 1000+ bots there are many queries, and this removes most of the database load. A standard setting for game servers. • server.properties: AutoDestroyDroppedItemAfter = 120 Drops on the ground disappear after 2 minutes instead of 10. Bots kill a lot, and in 10 minutes tens of thousands of items piled up on the ground: extra memory, extra server work and database deletes. Two minutes is enough for players to pick up their own drops. Rule of thumb: a 4-core VPS handles 1000-1500 bots comfortably. For more, go for 6-8 cores. LICENSE • The license is bound to your VPS IP: the bots run only on the server with that IP. • Send the server IP when you buy. Price: 500$ Contact: Telegram @Vikos177 — https://t.me/Vikos177 Test the bots here: <DOWNLOAD LINK> https://drive.google.com/file/d/1xpOII2X2fsUWekDIJk7POTMWu7ICpHJo/view After purchase you are added to a private Telegram group for clients: news, updates and support. ==============================================================
    • 💻 Supercharge your workflow with Claude Pro & Cursor AI. Clean setups with full warranty included!
    • 📦 Running out of Google Drive space? Upgrade to 5TB + Gemini Pro instantly on your own email.
    • AHAHAHAHAH ? Send it proofs here. I just wanted to sell my stuff, but this crazy Ukrainian just stole my money, and he does this all the time to other people. 
  • 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..