Mirror of eathena's sourceforge SVN repository.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

7379 lines
272 KiB

//===== Athena Doc ========================================
//= eAthena Script Commands
//===== Description =======================================
//= A reference manual for the eAthena scripting language.
//= Commands are sorted depending on their functionality.
//===== Version ===========================================
//= 3.50.20120416
//= 1.0 - First release, filled will as much info as I could
//= remember or figure out, most likely there are errors,
//= and things I have missed out [Fredzilla]
//= 1.1 - Added better discription for "getmapxy" (by Terminal Vertex & Z3R0)
//= 1.2b- Added a description for getpartymember (by HappyDenn)
//= (+few spelling mistakes corrected)
//= 2.0 - +79kb extra stuff and numerous corrections by
//= Maeki Rika.
//= 2.1 - Small but important corrections, more proofreading.
//= Some important discoveries in item functions, the
//= secret of making VVS weapons with 'getitem2' and
//= other news. (Rika again) +10kb :)
//= 2.2 - added getItemInfo description [Lupus]
//= 2.3 - added plenty of info for recent (and not so) script commands I added
//= [Skotlex]
//= 2.4 - Explained the upper parameter of jobchange. [Skotlex]
//= 2.5 - Added pow, sqrt and distance. [Lance]
//= 2.6 - Added setd and getd. [Lance]
//= 2.7 - petstat command. [Lance]
//= 2.7a- delitem2, countitems2 commands [Lupus]
//= 2.7b- clone command [Skotlex]
//= 2.7c- disguise / undisguise, query_sql commands [Lupus]
//= 2.8 - Deleted a copy of the nude command. Added axtoi command (needing a
//= clearer explanation of atoi.Gave a better explanation of OnLabels
//= and modified monster explanation due that L_Label isn't working with
//= monster.
//= 2.9.20061230 - Updated getitem and guardian. [FlavioJS]
//= 2.10.20070101 - added sleep,sleep2,awake and updated the variables section.
//= [FlavioJS]
//= 2.11.20070109 - removed the unused flag argument in guildskill, added an
//= optional argument to setcart,setfalcon,setriding and other cleanups
//= [FlavioJS]
//= 2.12.20070201 - Added npcshopitem, npcshopadditem, npcshopdelitem and
//= npcshopattach [Skotlex]
//= 3.00.20070208
//= - Explained Logical Bitwise Operators.
//= Dj-Yhn contributed to AND (&) operator, rest by myself. [erKURITA]
//= - Added a resume of allowed variable and arrays scopes. [erKURITA]
//= - Re-organized the script commands, and grouped them depending
//= on what they do. [erKURITA]
//= - Added a packload of commands that were missing,
//= and corrected some of the wrong ones [Dj-Yhn, erKURITA & Trancid]
//= 3.01.20070209
//= Updated 'cutin' (removed lies, removed outdated bmp list) [ultramage]
//= Removed 'cutincard' since eA no longer implements it
//= 3.02.20070209
//= Corrected/updated info on Xor/setd/getd/callfunc/callsub/return and
//= updated some examples to use "better" code. [FlavioJS]
//= 3.03.20070216
//= Expanded/clarified information on npc timers, added info about the
//= new attach flag for script commands startnpctimer/ stopnpctimer/
//= initnpctimer [Skotlex]
//= 3.03.20070226
//= Updated makeitem and how to include " in strings [Lupus]
//= 3.03.20070228
//= Added info on OnTimerQuit label to npctimer section. [Skotlex]
//= 3.04.20070317
//= Removed all .gat refferences from the examples [Lupus]
//= 3.04.20070330
//= Adjusted the 'itemskill' description due to recent change [ultramage]
//= 3.04.20070409
//= Fixed the incorrect order of parameters in 'makeitem' [ultramage]
//= 3.05.20070423
//= menu/select/prompt produce consistent results for grouped and empty
//= options [FlavioJS]
//= 3.05.20070819
//= Removed the messy 'unitdeadsit' command reference [ultramage]
//= 3.05.20070823
//= Fixed typo in 'areamonster' description (missing argument) [ultramage]
//= 3.06 20070909
//= Added 'gethominfo' description [Skotlex]
//= 3.06.20070910
//= Added info about the new behavior of 'getexp' [ultramage]
//= 3.07.20070915
//= Fixed 'duplicate' missing the target 'name' parameter! [ultramage]
//= 3.08.20071018
//= Clarified how npc names work. [FlavioJS]
//= 3.09.20071103
//= Added script function 'strnpcinfo' [ultramage]
//= 3.10.20071122
//= Added setnpcdisplay. [FlavioJS]
//= 3.10.20071211
//= Added query_logsql. [Skotlex]
//= 3.11.20071215
//= Updated guardianinfo and get-/setcastledata [ultramage]
//= 3.12.20071218
//= Corrected various mistakes mentioned in bugreport:373 [ultramage]
//= 3.12.20071227
//= Corrected description of scope and npc variables. [FlavioJS]
//= 3.13.20080104
//= Updated 'setcell' desc to match latest code changes [ultramage]
//= 3.14.20080211
//= Updated 'input' (new arguments and return value). [FlavioJS]
//= 3.15.20080227
//= Updated 'checkweight' description slightly. [L0ne_W0lf]
//= 3.16.20080229
//= Updated 'Shop' NPC type description to include cashshop. [L0ne_W0lf]
//= Woopth. Fixed spelling. ;P Should be a bit clearer now. [L0ne_W0lf]
//= 3.17.20080312
//= Corrected cashshop description. (#FREEPOINTS->#KAFRAPOINTS) [L0ne_W0lf]
//= 3.18.20080327
//= Added documentation for the 'checkcell' command [ultramage]
//= 3.19.20080407
//= Extended the behaviour of 'guardian'. [FlavioJS]
//= 3.20.20080425
//= Corrected 'getitem', 'getitem2' & 'delitem2' (charid instead of accountid) [Toms]
//= Modified 'delitem' (added optional accountid parameter) [Toms]
//= 3.21.20080612
//= Script commands extended to support skill names: [FlavioJS]
//= skill, addtoskill, guildskill, getskilllv, getgdskilllv, itemskill,
//= petskillattack, petskillattack2, petskillsupport, skilleffect, npcskilleffect,
//= unitskilluseid, unitskillusepos, bonus/bonus2/bonus3/bonus4/bonus5
//= 3.22.20080622
//= Extended 'set' to return the variable reference. [FlavioJS]
//= 3.22.20080901
//= Adjusted the 'getequipname' description to match src [ultramage]
//= 3.23.20080909
//= Added WoE SE related commands. [L0ne_W0lf]
//= 3.24.20081111
//= Changed the error behaviour of delitem/delitem2/Zeny. [FlavioJS]
//= 3.25.20081220
//= Extended the behaviour of duplicates (warps/shops/cashshops). [FlavioJS]
//= 3.26.20090702
//= Replaced 'bonusautoscript' by 'autobonus'. [Inkfish]
//= 3.27.20090725
//= Added Quest Log related commands. [Inkfish]
//= 3.28.20091119
//= Added showevent and searchitem commands [Skotlex]
//= Added info on strcharinfo(3) [Skotlex]
//= 3.29.20101123
//= Added 'pushpc' command. [Ai4rei]
//= 3.30.20101126
//= Added 'setfont', 'mercenary_create', 'mercenary_heal',
//= 'mercenary_sc_start', 'mercenary_get_calls', 'mercenary_set_calls',
//= 'mercenary_get_faith' and 'mercenary_set_faith' commands. [Ai4rei]
//= 3.31.20101130
//= Added 'progressbar' command. [Ai4rei]
//= 3.32.20101208
//= Updated description for commands 'next', 'return', 'attachrid',
//= 'detachrid', 'itemskill', 'openstorage', 'skilleffect', 'donpcevent',
//= 'day', 'night', 'atoi', 'axtoi', 'jump_zero', 'getelementofarray',
//= 'changebase', 'kickwaitingroomall', 'cutin' and 'charcommand'. [Ai4rei]
//= 3.33.20101212
//= Updated description of 'playBGM' and 'playBGMall' to reflect the actual
//= behavior. [Ai4rei]
//= 3.34.20101217
//= Spellcheck. [Ai4rei]
//= 3.35.20110106
//= Removed bug warning from 'deletearray'. [Ai4rei]
//= 3.36.20110219
//= Added 'buyingstore' command. [Ai4rei]
//= 3.37.20110306
//= Added 'searchstores' command. [Ai4rei]
//= 3.38.20110313
//= Added 'cooking' command. [Ai4rei]
//= Fixed item levels in 'produce' command.
//= 3.39.20110322
//= Added 'bg_getareausers' and 'bg_get_data' commands. [Ai4rei]
//= Updated description of 'waitingroom2bg_single' command.
//= Documented optional parameter 'npc name' of 'waitingroom2bg' command.
//= 3.40.20110404
//= Updated description of 'waitingroom' command to include required zeny/lvl. [Kisuka]
//= 3.41.20110427
//= Updated description of 'itemheal' primarily to remove overhead and lies
//= about client effects. [Ai4rei]
//= 3.42.20110508
//= Updated description of all instance commands to reflect actual behavior.
//= [Ai4rei]
//= 3.43.20110529
//= Updated 'npcshopitem', 'npcshopadditem' and 'npcshopdelitem' to support
//= cashshops as well. [Ai4rei]
//= 3.44.20110530
//= Documented special map names recognized by 'warpguild'. [Ai4rei]
//= 3.45.20110709
//= Added 'getmercinfo' command. [Ai4rei]
//= 3.46.20110810
//= Added information on OnTouchNPC and 'unitwarp' special case [Skotlex]
//= 3.47.20111128
//= Added 'showdigit' command. [Ai4rei]
//= 3.48.20120108
//= Updated 'viewpoint' to mention effect of type 0. [Ai4rei]
//= 3.49.20120319
//= Added 'getnpcid' command. [Ai4rei]
//= 3.50.20120416
//= Allow any type of argument in 'escape_sql' command. [FlavioJS]
This document is a reference manual for all the scripting commands and functions
available in current eAthena SVN. It is not a simple tutorial. When people tell
you to "Read The F***ing Manual", they mean this.
The information was mostly acquired through looking up how things actually work
in the source code of the server, which was written by many people over time,
and lots of them don't speak English and never left any notes - or are otherwise
not available for comments. As such, anything written in here might not be
correct, it is only correct to the best of our knowledge, which is limited.
This is not a place to teach you basic programming. This document will not teach
you basic programming by itself. It's more of a reference for those who have at
least a vague idea of what they want to do and want to know what tools they have
available to do it. We've tried to keep it as simple as feasible, but if you
don't understand it, getting a clear book on programming in general will help
better than yelling around the forum for help.
A little learning never caused anyone's head to explode.
The commands and functions are listed in no particular order:
*Name of the command and how to call it.
Descriptive text
Small example if possible. Will usually be incomplete, it's there just to
give you an idea of how it works in practice.
To find a specific command, use Ctrl+F, (or whatever keys call up a search
function in whatever you're reading this with) put an * followed by the command
name, and it should find the command description for you.
If you find anything omitted, please respond. :)
Throughout this document, wherever a command wants an argument, it is given in
<angle brackets>. This doesn't mean you should type the angle brackets. :) If an
argument of a command is optional, it is given in {curly brackets}. You've
doubtlessly seen this convention somewhere, if you didn't, get used to it,
that's how big boys do it. If a command can optionally take an unspecified
number of arguments, you'll see a list like this:
command <argument>{,<argument>...<argument>}
This still means they will want to be separated by commas.
Where a command wants a string, it will be given in "quotes", if it's a number,
it will be given without them. Normally, you can put an expression, like a bunch
of functions or operators returning a value, in (round brackets) instead of most
numbers. Round brackets will not always be required, but they're often a good
Wherever you refer to a map name, it's always 'mapname' or 'mapname.gat'
(Please, don't use .gat suffix anymore. It's useless.)
Script loading structure
Scripts are loaded by the map server as referenced in the 'conf/map_athena.conf'
configuration file, but in the default configuration, it doesn't load any script
files itself. Instead, it loads the file 'npc/scripts_main.conf' which itself
contains references to other files. The actual scripts are loaded from txt
files, which are linked up like this:
npc: <path to a filename>
Any line like this, invoked, ultimately, by 'map_athena.conf' will load up the
script contained in this file, which will make the script available. No file
will get loaded twice, to prevent possible errors.
Another configuration file option of relevance is:
delnpc: <path to a filename>
This will unload a specified script filename from memory, which, while
seemingly useless, may sometimes be required.
Whenever '//' is encountered in a line upon reading, everything beyond this on
that line is considered to be a comment and is ignored. This works wherever you
place it.
Upon loading all the files, the server will execute all the top-level commands
in them. No variables exist yet at this point, no commands can be called other
than those given in this section. These commands set up the basic server script
structure - create NPC objects, spawn monster objects, set map flags, etc. No
code is actually executed at this point except them. The top-level commands the
scripting are pretty confusing, since they aren't structured like you would
expect commands, command name first, but rather, normally start with a map name.
What's more confusing about the top-level commands is that most of them use a
tab symbol to divide their arguments.
To prevent problems and confusion, the tab symbols are written as '%TAB%'
throughout this document, even though this makes the text a bit less readable.
Using an invisible symbol to denote arguments is one of the bad things about
this language, but we're stuck with it for now. :)
Here is a list of valid top-level commands:
** Set a map flag:
<map name>%TAB%mapflag%TAB%<flag>
This will, upon loading, set a specified map flag on a map you like. These are
normally in files inside 'conf/mapflag' and are loaded first, so by the time the
server's up, all the maps have the flags they should have. Map flags determine
the behavior of the map regarding various common problems, for a better
explanation, see 'setmapflag'.
** Create a permanent monster spawn:
<map name>,<x>,<y>,<xs>,<ys>%TAB%monster%TAB%<monster name>%TAB%<mob id>,<amount>,<delay1>,<delay2>,<event>
Map name is the name of the map the monsters will spawn on. x,y are the
coordinates where the mob should spawn. If xs and ys are non-zero, they
specify the 'radius' of a spawn-rectangle area centered at x,y.
Putting zeros instead of these coordinates will spawn the monsters randomly.
Note this is only the initial spawn zone, as mobs random-walk, they are free
to move away from their specified spawn region.
Monster name is the name the monsters will have on screen, and has no relation
whatsoever to their names anywhere else. It's the mob id that counts, which
identifies monster record in 'mob_db.txt' database of monsters. If the mob name
is given as "--ja--", the 'japanese name' field from the monster database is
used, (which, in eAthena, actually contains an english name) if it's "--en--",
it's the 'english name' from the monster database (which contains an uppercase
name used to summon the monster with a GM command).
If you add 20000 to the monster ID, the monster will be spawned in a 'big
version', (monster size class will increase) and if you add 10000, the 'tiny
version' of the monster will be created. However, this method is deprecated
and not recommended, as the values to add can change at a later time (20000
and 10000 actually stand for 2*MAX_MOB_DB and MAX_MOB_DB respectively, which
is defined on mob.h, and can change in the future as more mobs are created).
The recommended way to change a mob's size is to use the event-field (see
Amount is the amount of monsters that will be spawned when this command is
executed, it is affected by spawn rates in 'battle_athena.conf'.
Delay1 and delay2 control monster respawn delays - the first one is the fixed
base respawn time, and the second is random variance on top of the base time.
Both values are given in miliseconds (1000 = 1 second).
Note that the server also enforces a minimum respawn delay of 5 seconds.
You can specify a custom level to use for the mob different from the one of
the database by adjoining the level after the name with a comma. eg:
"Poring,50" for a name will spawn a monster with name Poring and level 50.
Event is a script event to be executed when the mob is killed. The event must
be in the form "NPCName::OnEventName" to execute, and the event name label
should start with "On". As with all events, if the NPC is an on-touch NPC, the
player who triggers the script must be within 'trigger' range for the event to
The Event field can be used alternatively to specify other mob properties. Use
2 to specify that the mob should be small, 4 for big monsters, and 8 for
special ai mobs (which by default attack other monsters instead of players).
You can add these, so using 10 will spawn small monsters that attack other
mobs (if you specify both 2 and 4, the small version takes priority).
** NPC names
/!\ WARNING: this applies to warps, NPCs, duplicates and shops /!\
NPC names are kinda special and are formatted this way:
<Display name>{::<Unique name>}
All NPCs need to have a unique name that is used for identification purposes.
When you have to identify a NPC by it's name, you should use <Unique name>.
If <Unique name> is not provided, use <Display name> instead.
The client has a special feature when displaying names:
if the display name contains a '#' character, it hides that part of the name.
ex: if your NPC is named 'Hunter#hunter1', it will be displayed as 'Hunter'
<Display name> must be at most 24 characters in length.
<Unique name> must be at most 24 characters in length.
** Define a warp point
<from map name>,<fromX>,<fromY>,<facing>%TAB%warp%TAB%<warp name>%TAB%<spanx>,<spany>,<to map name>,<toX>,<toY>
This will define a warp NPC that will warp a player between maps, and while most
arguments of that are obvious, some deserve special mention.
SpanX and SpanY will make the warp sensitive to a character who didn't step
directly on it, but walked into a zone which is centered on the warp from
coordinates and is SpanX in each direction across the X axis and SpanY in each
direction across the Y axis.
Warp NPC objects also have a name, because you can use it to refer to them later
with 'enablenpc'/'disablenpc'
Facing of a warp object is irrelevant, it is not used in the code and all
current scripts have a zero in there.
** Define an NPC object.
<map name>,<x>,<y>,<facing>%TAB%script%TAB%<NPC Name>%TAB%<sprite id>,{<code>}
<map name>,<x>,<y>,<facing>%TAB%script%TAB%<NPC Name>%TAB%<sprite id>,<triggerX>,<triggerY>,{<code>}
This will place an NPC object on a specified map at the specified location, and
is a top-level command you will use the most in your custom scripting. The NPCs
are triggered by clicking on them, and/or by walking in their trigger area, if
defined, see that below.
Facing is a direction the NPC sprite will face in. Not all NPC sprites have
different images depending on the direction you look from, so for some facing
will be meaningless. Facings are counted counterclockwise in increments of 45
degrees, where 0 means facing towards the top of the map. (So to turn the sprite
towards the bottom of the map, you use facing 4, and to make it look southeast
it's facing 5.)
Sprite id is the sprite number used to display this particular NPC. For a full
list of sprite id numbers see http://kalen.s79.xrea.com/npc/npce.shtml You may
also use a monster's ID number instead to display a monster sprite for this NPC.
It is possible to use a job sprite as well, but you must first define it as a
monster sprite in 'mob_avail.txt', a full description on how to do this is not
in the scope of this manual.
A '-1' sprite id will make the NPC invisible (and unclickable).
A '111' sprite id will make an NPC which does not have a sprite, but is still
clickable, which is useful if you want to make a clickable object of the 3D
TriggerX and triggerY, if given, will define an area, centered on NPC and
spanning triggerX cells in every direction across X and triggerY in every
direction across Y. Walking into that area will trigger the NPC. If no
'OnTouch:' special label is present in the NPC code, the execution will start
from the beginning of the script, otherwise, it will start from the 'OnTouch:'
label. Monsters can also trigger the NPC, though the label 'OnTouchNPC:' is
used in this case.
The code part is the script code that will execute whenever the NPC is
triggered. It may contain commands and function calls, descriptions of which
compose most of this document. It has to be in curly brackets, unlike elsewhere
where we use curly brackets, these do NOT signify an optional parameter.
** Define a 'floating' NPC object.
-%TAB%script%TAB%<NPC Name>%TAB%-1,{<code>}
This will define an NPC object not triggerable by normal means. This would
normally mean it's pointless since it can't do anything, but there are
exceptions, mostly related to running scripts at specified time, which is what
these floating NPC objects are for. More on that below.
** Define a shop/cashshop NPC.
-%TAB%shop%TAB%<NPC Name>%TAB%<sprite id>,<itemid>:<price>{,<itemid>:<price>...}
<map name>,<x>,<y>,<facing>%TAB%shop%TAB%<NPC Name>%TAB%<sprite id>,<itemid>:<price>{,<itemid>:<price>...}
This will define a shop NPC, which, when triggered (which can only be done by
clicking) will cause a shop window to come up. No code whatsoever runs in shop
NPCs and you can't change the prices otherwise than by editing the script
itself. (No variables even exist at this point of scripting, so don't even
bother trying to use them.)
The item id is the number of item in the 'item_db.txt' database. If Price is set
to -1, the 'buy price' given in the item database will be used. Otherwise, the
price you gave will be used for this item, which is how you create differing
prices for items in different shops.
Since trunk r12264 you can alternatively use "cashshop" in place of "shop"
to use the Cash Shop interface, allowing you to buy items with special points
(Currently stored as account vars in global_reg #CASHPOINTS and #KAFRAPOINTS.)
This type of shop will not allow you to sell items at it, you may only
purchase items here. The layout used to define sale items still count, and
"<price>" refers to how many points will be spent purchasing the them.
** Define an warp/shop/cashshop/NPC duplicate.
warp: <map name>,<x>,<y>,<facing>%TAB%duplicate(<label>)%TAB%<NPC Name>%TAB%<spanx>,<spany>
shop/cashshop/npc: -%TAB%duplicate(<label>)%TAB%<NPC Name>%TAB%<sprite id>
shop/cashshop/npc: <map name>,<x>,<y>,<facing>%TAB%duplicate(<label>)%TAB%<NPC Name>%TAB%<sprite id>
npc: -%TAB%duplicate(<label>)%TAB%<NPC Name>%TAB%<sprite id>,<triggerX>,<triggerY>
npc: <map name>,<x>,<y>,<facing>%TAB%duplicate(<label>)%TAB%<NPC Name>%TAB%<sprite id>,<triggerX>,<triggerY>
This will duplicate an warp/shop/cashshop/NPC referred to by 'label'.
Warp duplicates inherit the target location.
Shop/cashshop duplicates inherit the item list.
NPC duplicates inherit the script code.
The rest (name, location, facing, sprite ID, span/trigger area)
is obtained from the definition of the duplicate (not inherited).
** Define a function object
function%TAB%script%TAB%<function name>%TAB%{<code>}
This will define a function object, callable with the 'callfunc' command (see
below). This object will load on every map server separately, so you can get at
it from anywhere. It's not possible to call the code in this object by
anything other than the 'callfunc' script command.
The code part is the script code that will execute whenever the function is
called with 'callfunc'. It has to be in curly brackets, unlike elsewhere where
we use curly brackets, these do NOT signify an optional parameter.
Once an object is defined which has a 'code' field to it's definition, it
contains script commands which can actually be triggered and executed.
~ RID? GID? ~
What a RID is and why do you need to know
Most scripting commands and functions will want to request data about a
character, store variables referenced to that character, send stuff to the
client connected to that specific character. Whenever a script is invoked by a
character, it is passed a so-called RID - this is the account ID number of a
character that caused the code to execute by clicking on it, walking into it's
OnTouch zone, or otherwise.
If you are only writing common NPCs, you don't need to bother with it. However,
if you use functions, if you use timers, if you use clock-based script
activation, you need to be aware of all cases when a script execution can be
triggered without a RID attached. This will make a lot of commands and functions
unusable, since they want data from a specific character, want to send stuff to
a specific client, want to store variables specific to that character, and they
would not know what character to work on if there's no RID.
Unless you use 'attachrid' to explicitly attach a character to the script first.
Whenever we say 'invoking character', we mean 'the character who's RID is
attached to the running script. The script function "playerattached" can be
used to check which is the currently attached player to the script (it will
return 0 if the there is no player attached or the attached player no longer
is logged on to the map-server).
But what about GID?
--- ---- ----- ----
GID stands for the Game ID of something, this can either be the GID obtained
through mobspawn (mob control commands) or the account ID of a character.
Another way would be to right click on a mob,
NPC or char as GM sprited char to view the GID.
This is mostly used for the new version of skill and the mob control commmands
implemented (but NEVER documented by Lance. Shame on you...).
Item and pet scripts
Each item in the item database has three special fields - Script , OnEquip_Script
and OnUnequip_Script. The first is script code run every time a character equips the item,
with the RID of the equipping character. Every time they unequip an item, all
temporary bonuses given by the script commands are cleared, and all the scripts
are executed once again to rebuild them. This also happens in several other
situations (like upon login) but the full list is currently unknown.
OnEquip_Script is a piece of script code run whenever the item is used by a character
by doubleclicking on it. OnUnequip_Script runs whenever the
equipment is unequip by a character
Not all script commands work properly in the item scripts. Where commands and
functions are known to be meant specifically for use in item scripts, they are
described as such.
Every pet in the pet database has a PetScript field, which determines pet
behavior. It is invoked wherever a pet of the specified type is spawned.
(hatched from an egg, or loaded from the char server when a character who had
that pet following them connects) This may occur in some other situations as
well. Don't expect anything other than commands definitely marked as usable in
pet scripts to work in there reliably.
Beside the common decimal numbers, which are nothing special whatsoever (though
do not expect to use fractions, since ALL numbers are integer in this language),
the script engine also handles hexadecimal numbers, which are otherwise
identical. Writing a number like '0x<hex digits>' will make it recognized as a
hexadecimal value. Notice that 0x10 is equal to 16. Also notice that if you try
to 'mes 0x10' it will print '16'.
This is not used much, but it pays to know about it.
The meat of every programming language is variables - places where you store
In the eAthena scripting language, variable names are not case sensitive.
Variables are divided into and uniquely identified by the combination of:
prefix - determines the scope and extent (or lifetime) of the variable
name - an identifier consisting of '_' and alphanumeric characters
postfix - determines the type of the variable: integer or string
Scope can be:
global - global to all servers
local - local to the server
account - attached to the account of the character identified by RID
character - attached to the character identified by RID
npc - attached to the NPC
scope - attached to the scope of the instance
Extent can be:
permanent - They still exist when the server resets.
temporary - They cease to exist when the server resets.
Prefix: scope and extent
nothing - A permanent variable attached to the character, the default variable
type. They are stored with all the account data in "save\athena.txt"
in TXT versions and in the SQL versions in the `global_reg_value`
table using type 3.
"@" - A temporary variable attached to the character.
SVN versions before 2094 revision and RC5 version will also treat
'l' as a temporary variable prefix, so beware of having variable
names starting with 'l' if you want full backward compatibility.
"$" - A global permanent variable.
They are stored in "save\mapreg.txt" or database table `mapreg`,
depending on server type.
"$@" - A global temporary variable.
This is important for scripts which are called with no RID
attached, that is, not triggered by a specific character object.
"." - A NPC variable.
They exist in the NPC and disappear when the server restarts or the
NPC is reloaded. Can be accessed from inside the NPC or by calling
'getvariableofnpc'. Function objects can also have .variables which
are accessible from inside the function, however 'getvariableofnpc'
does NOT work on function objects.
".@" - A scope variable.
They are unique to the instance and scope. Each instance has it's
own scope that ends when the script ends. Calling a function with
callsub/callfunc starts a new scope, returning from the function
ends it. When a scope ends, it's variables are converted to values
('return .@var;' returns a value, not a reference).
"#" - A permanent local account variable.
They are stored with all the account data in "save\accreg.txt" in
TXT versions and in the SQL versions in the 'global_reg_value'
table using type 2.
"##" - A permanent global account variable stored by the login server.
They are stored in "save\account.txt" and in the SQL versions in the
'global_reg_value' table, using type 1. The only difference you will
note from normal # variables is when you have multiple char-servers
connected to the same login server. The # variables are unique to
each char-server, while the ## variables are shared by all these
Postfix: integer or string
nothing - integer variable, can store positive and negative numbers, but only
whole numbers (so don't expect to do any fractional math)
'$' - string variable, can store text
name - permanent character integer variable
name$ - permanent character string variable
@name - temporary character integer variable
@name$ - temporary character string variable
$name - permanent global integer variable
$name$ - permanent global string variable
$@name - temporary global integer variable
$@name$ - temporary global string variable
.name - NPC integer variable
.name$ - NPC string variable
.@name - scope integer variable
.@name$ - scope string variable
#name - permanent local account integer variable
#name$ - permanent local account string variable
##name - permanent global account integer variable
##name$ - permanent global account string variable
If a variable was never set, it is considered to equal zero for integer
variables or an empty string ("", nothing between the quotes) for string
variables. Once you set it to that, the variable is as good as forgotten
forever, and no trace remains of it even if it was stored with character or
account data.
Some variables are special, that is, they are already defined for you by the
scripting engine. You can see the full list somewhere in 'db/const.txt', which
is a file you should read, since it also allows you to replace lots of numbered
arguments for many commands with easier to read text. The special variables most
commonly used are all permanent character-based variables:
StatusPoint - Amount of status points remaining.
BaseLevel - Current base level
SkillPoint - Amount of skill points remaining
Class - Current job
Upper - 1 if the character is an advanced job class.
Zeny - Current amount of Zeny
Sex - Character's gender, 0 if female, 1 if male.
Weight - The weight the character currently carries.
MaxWeight - The maximum weight the character can carry.
JobLevel - Character's job level
BaseExp - The amount of base experience points the character has.
Notice that it's zero (or close) if the character just got a level.
JobExp - Same for job levels
NextBaseExp - Amount of experience points needed to reach the next base level.
NextJobExp - Same for job levels.
Hp - Current amount of hit points.
MaxHp - Maximum amount of hit points.
Sp - Current spell points.
MaxSp - Maximum amount of spell points.
BaseJob - This is sneaky, apparently meant for baby class support.
This will supposedly equal Job_Acolyte regardless of whether the
character is an acolyte or a baby acolyte, for example.
Karma - The character's karma. Karma system is not fully functional, but
this doesn't mean this doesn't work at all. Not tested.
Manner - The character's manner rating. Becomes negative if the player
utters words forbidden through the use of 'manner.txt' client-side
While these behave as variables, do not always expect to just set them - it is
not certain whether this will work for all of them. Whenever there is a command
or a function to set something, it's usually preferable to use that instead. The
notable exception is Zeny, which you can and often will address directly -
setting it will make the character own this number of Zeny.
If you try to set Zeny to a negative number, the script will be terminated with an error.
To include symbol '"' in a string you should use prefix '\"'
Arrays (in eAthena at least) are essentially a set of variables going under the
same name. You can tell between the specific variables of an array with an
'array index', a number of a variable in that array:
<variable name>[<array index>]
Variables stored in this way, inside an array, are also called 'array elements'.
Arrays are specifically useful for storing a set of similar data (like several
item IDs for example) and then looping through it. You can address any array
variable as if it was a normal variable:
set @arrayofnumbers[0],1;
You can also do sneaky things like using a variable (or an expression, or even a
value from an another array) to get at an array value:
set @x,100;
set @arrayofnumbers[@x],10;
This will make @arrayofnumbers[100] equal to 10.
Notice that index numbering always starts with 0. Arrays cannot hold more than
128 variables. (So the last one can't have a number higher than 127)
And array indexes probably can't be negative. Nobody tested what happens when
you try to get a negatively numbered variable from an array, but it's not going
to be pretty. :)
Arrays can naturally store strings:
@menulines$[0] is the 0th element of the @menulines$ array of strings. Notice
the '$', normally denoting a string variable, before the square brackets that
denotes an array index.
Resume of the allowed variable and array scopes
------ -- --- ------- -------- --- ----- ------
|VarType | Norm | Array |
|$Str$ | OK! | OK! |
|$@Str$ | OK! | OK! |
|@Str$ | OK! | OK! |
|#Str$ | OK! | FAIL! |
|Str$ | OK! | FAIL! |
|$Int | OK! | OK! |
|$@Int | OK! | OK! |
|@Int | OK! | OK! |
|#Int | OK! | FAIL! |
|Int | OK! | FAIL! |
|.Str$ | OK! | OK! |
|.Int | OK! | OK! |
|.@Str$ | OK! | OK! |
|.@Int | OK! | OK! |
Variable References
Operators are things you can do to variables and numbers. They are either the
common mathematical operations or conditional operators
+ - will add two numbers. If you try to add two strings, the result will be a
string glued together at the +. You can add a number to a string, and the
result will be a string. No other math operators work with strings.
- - will subtract two numbers.
* - will multiply two numbers.
/ - will divide two numbers. Note that this is an integer division, i.e.
7/2 is not equal 3.5, it's equal 3.
% - will give you the remainder of the division. 7%2 is equal to 1.
There are also conditional operators. This has to do with the conditional
command 'if' and they are meant to return either 1 if the condition is satisfied
and 0 if it isn't. (That's what they call 'boolean' variables. 0 means 'False'.
Anything except the zero is 'True' Odd as it is, -1 and -5 and anything below
zero will also be True.)
You can compare numbers to each other and you compare strings to each other, but
you can not compare numbers to strings.
== - Is true if both sides are equal. For strings, it means they are the same.
>= - True if the first value is equal to, or greater than, the second value.
<= - True if the first value is equal to, or less than, the second value
> - True if the first value greater than the second value
< - True if the first value is less than the second value
!= - True if the first value IS NOT equal to the second one
1==1 is True.
1<2 is True while 1>2 is False.
@x>2 is True if @x is equal to 3. But it isn't true if @x is 2.
Only '==' and '!=' have been tested for comparing strings. Since there's no way
to code a seriously complex data structure in this language, trying to sort
strings by alphabet would be pointless anyway.
Comparisons can be stacked in the same condition:
&& - Is True if and only if BOTH sides are true.
('1==1 && 2==2' is true. '2==1 && 1==1' is false.)
|| - Is True if either side of this expression is True.
1==1 && 2==2 is True.
1==1 && 2==1 is False.
1==1 || 2==1 is True.
Logical bitwise operators work only on numbers, and they are the following:
<< - Left shift.
>> - Right shift.
Left shift moves the binary 1(s) of a number n positions to the left,
which is the same as multiplying by 2, n times.
In the other hand, Right shift moves the binary 1(s) of a number n positions
to the right, which is the same as dividing by 2, n times.
set b,2;
set a, b << 3;
mes a;
set a, a >> 2;
mes a;
The first mes command would display 16, which is the same as 2 x (2 x 2 x 2) = 16.
The second mes command would display 4, which is the same as 16 / 2 = 8. 8 / 2 = 4.
& - And.
| - Or.
The bitwise operator AND (&) is used to test two values against each other,
and results in setting bits which are active in both arguments. This can
be used for a few things, but in eAthena this operator is usually used to
create bit-masks in scripts.
The bitwise operator OR (|)sets to 1 a binary position if the binary position
of one of the numbers is 1. This way a variable can hold several values we can check,
known as bit-mask. A variable currently can hold up to 32 bit-masks (from position 0
to position 1). This is a cheap(skate) and easy way to avoid using arrays to store several checks
that a player can have.
A bit-mask basically is (ab)using the variables bits to set various options in
one variable. With the current limit if variables it is possible to store 32
different options in one variable (by using the bits on position 0 to 31).
- Basic example of the & operator, bit example:
10 & 2 = 2
Why? :
10 = 2^1 + 2^3 (2 + 8), so in bits, it would be 1010
2 = 2^1 (2), so in bits (same size) it would be 0010
The & (AND) operator sets bits which are active (1) in both arguments, so in the
example 1010 & 0010, only the 2^1 bit is active (1) in both. Resulting in the bit
0010, which is 2.
- Basic example of creating and using a bit-mask:
set @options,2|4|16; //(note: this is the same as 2+4+16, or 22)
if (@options & 1) mes "Option 1 is activated";
if (@options & 2) mes "Option 2 is activated";
if (@options & 4) mes "Option 3 is activated";
if (@options & 8) mes "Option 4 is activated";
if (@options & 16) mes "Options 5 is activated";
This would return the messages about option 2, 3 and 5 being shown (since we've set
the 2,4 and 16 bit to 1).
^ - Xor.
The bitwise operator XOR (eXclusive OR) sets a binary position to 0 if both
numbers have the same value in the said position. On the other hand, it
sets to 1 if they have different values in the said binary position.
This is another way of setting and unsetting bits in bit-masks.
- First let's set the quests that are currently in progress:
set inProgress,1|8|16; // quest 1,8 and 16 are in progress
- After playing for a bit, the player starts another quest:
if( inProgress&2 == 0 ){
// this will set the bit for quest 2 (inProgress has that bit set to 0)
set inProgress,inProgress^2;
mes "Quest 2: find a newbie and be helpful to him for an hour.";
- After spending some time reading info on Xor's, the player finally completes quest 1:
if( inProgress&1 && isComplete ){
// this will unset the bit for quest 1 (inProgress has that bit set to 1)
set inProgress,inProgress^1;
mes "Quest 1 complete!! You unlocked the secrets of the Xor dynasty, use them wisely.";
Unary operators with only with a single number, which follows the operator, and
are following:
- - Negation.
The sign of the number will be reversed. If the number was positive, it will
become negative and vice versa.
set .@myvar,10;
mes "Negative 10 is "+(-.@myvar);
! - Logical Not.
Reverses the boolean result of an expression. True will become false and
false will become true.
mes "Doing something failed.";
~ - Bitwise Not.
Reverses each bit in a number, also known as one's complement. Cleared bits
are set, and set bits are cleared.
- Ensure, that quest 2 is disabled, while keeping all other active, if they are.
set inProgress,inProgress&(~2); // same as set inProgress,inProgress&0xfffffffd
Ternary operators take three expressions (numbers, strings or boolean), and are
?: - Conditional operator
Very useful e.g. to replace
if(Sex) mes "..."; else mes "...";
clauses with simple
mes "Welcome, " + (Sex?"Mr.":"Mrs.") + " " + strcharinfo(0);
or to replace any other simple if-else clauses. It might be worth
mentioning that ?: has low priority and has to be enclosed with
parenthesis in most (if not all) cases.
Within executable script code, some lines can be labels:
<label name>:
Labels are points of reference in your script, which can be used to route
execution with 'goto', 'menu' and 'jump_zero' commands, invoked with 'doevent'
and 'donpcevent' commands and are otherwise essential. A label's name may not be
longer than 22 characters. (23rd is the ':'.) There is some confusion in the
source about whether it's 22, 23 or 24 all over the place, so keeping labels
under 22 characters could be wise. In addition to labels you name yourself,
there are also some special labels which the script engine will start execution
from if a special event happens:
This will execute when the server clock hits the specified date or time. Hours
and minutes are given in military time. ('0105' will mean 01:05 AM). Weekdays
are Sun,Mon,Tue,Wed,Thu,Fri,Sat. Months are 01 to 12, days are 01 to 31.
Remember the zero. :)
OnInit will execute every time the scripts loading is complete, including when
they are reloaded with @reloadscript command. OnInterIfInit will execute when
the map server connects to a char server, OnInterIfInitOnce will only execute
once and will not execute if the map server reconnects to the char server later.
OnAgitStart will run whenever the server shifts into WoE mode, whether it is
done with @agitstart GM command or with 'AgitStart' script command. OnAgitEnd
will do likewise for the end of WoE. OnAgitInit will run when castle data is
loaded from the char-server by the map server.
No RID will be attached while any of the above mentioned labels are triggered, so
no character or account-based variables will be accessible, until you attach a
RID with 'attachrid' (see below).
The above also applies to, the last three labels, the only difference is that
these labels are used exclusively for WoE SE, and are called independently.
This label will be executed if a trigger area is defined for the NPC object it's
in. If it isn't present, the execution will start from the beginning of the NPC
code. The RID of the triggering character object will be attached.
It's pretty obvious when these four special labels will be invoked. For more
information, see 'doc/sample/PCLoginEvent.txt'
This special label triggers when a player dies. The variable 'killerrid' is
set to the ID of the killer.
This special label triggers when a player kills another player. The variable
'killedrid' is set to the ID of the player killed.
This special label triggers when a player kills a monster. The variable
'killedrid' is set to the Class of the monster killed.
This special label will trigger once a player steps in a map marked with the
'loadevent' mapflag and attach its RID. The fact that this label requires a
mapflag for it to work is because, otherwise, it'd be server-wide and trigger
every time a player would change maps. Imagine the server load with 1,000 players
(oh the pain...)
Only the special labels which are not associated with any script command are
listed here. There are other kinds of labels which may be triggered in a similar
manner, but they are described with their associated commands.
On<label name>:
These special labels are used with Mob scripts mostly, and script commands
that requires you to point/link a command to a mob or another NPC, giving a label
name to start from. The label name can be any of your liking, but must be
monster "prontera",123,42,"Poringz0rd",2341,23,"Master::OnThisMobDeath";
amatsu,13,152,4 script Master 767,{
mes "Hi there";
announce "Hey, "+strcharinfo(0)+" just killed a Poringz0rd!",bc_blue|bc_all;
Each time you kill one, that announce will appear in blue to everyone.
"Global" labels
There's a catch with labels and doevent. If you call a label (using doevent)
and called label is in NPC that has trigger area, that label must end with
"Global" to work globally (i.e. if RID is outside of the trigger area, which
usually happens since otherwise there would be no point calling the label with
doevent, because OnTouch would do the job). For further reference look for
npc_event in npc.c.
Scripting commands and functions
The commands and functions are listed here in no particular order. There's a
difference between commands and functions - commands leave no 'return value'
which might be used in a conditional statement, as a command argument, or stored
in a variable. Calling commands as if they were functions will sometimes work,
but is not advised, as this can lead to some hard to track errors. Calling
functions as if they were commands will mess up the stack, so 'return' command
will not return correctly after this happens in a particular script.
All commands must end with a ';'. Actually, you may expect to have multiple
commands on one line if you properly terminate them with a ';', but it's better
if you don't, since it is not certain just whether the scripting engine will
behave nicely if you do.
From here on, we will have the commands sorted as follow:
1.- Basic commands.
2.- Information-retrieving commands.
3.- Checking commands.
4.- Player-related commands.
5.- Mob / NPC -related commands.
6.- Other commands.
7.- Instance commands.
8.- Quest Log commands.
9.- Battleground commands.
10.- Mercenary commands.
|1.- Basic commands.|
*mes "<string>";
This command will displays a box on the screen for the invoking character, if no
such box is displayed already, and will print the string specified into that
box. There is normally no 'close' or 'next' button on this box, unless you
create one with 'close' or 'next', and while it's open the player can't do much
else, so it's important to create a button later. If the string is empty, it
will show up as an empty line.
mes "Text that will appear in the box";
Inside the string you may put color codes, which will alter the color of the
text printed after them. The color codes are all '^<R><G><B>' and contain three
hexadecimal numbers representing colors as if they were HTML colors - ^FF0000 is
bright red, ^00FF00 is bright green, ^0000FF is bright blue, ^000000 is black.
^FF00FF is a pure magenta, but it's also a color that is considered transparent
whenever the client is drawing windows on screen, so printing text in that color
will have kind of a weird effect. Once you've set a text's color to something,
you have to set it back to black unless you want all the rest of the text be in
that color:
mes "This is ^FF0000 red ^000000 and this is ^00FF00 green, ^000000 so.";
Notice that the text coloring is handled purely by the client. If you use non-
english characters, the color codes might get screwed if they stick to letters
with no intervening space. Separating them with spaces from the letters on
either side solves the problem.
This command will display a 'next' button in the message window for the
invoking character. Clicking on it will cause the window to clear and display
a new one. Used to segment NPC-talking, next is often used in combination with
'mes' and 'close'.
If no window is currently on screen, one will be created, but once the invoking
character clicks on it, a warning is thrown on the server console and the script
will terminate.
mes "[Woman]";
mes "This would appear on the page";
// This is needed cause it is a new page and the top will now be blank
mes "[Woman]";
mes "This would appear on the 2nd page";
This command will create a 'close' button in the message window for the invoking
character. If no window is currently on screen, the script execution will end. This is one
of the ways to end a speech from an NPC. Once the button is clicked, the NPC
script execution will end, and the message box will disappear.
mes "[Woman]";
mes "I am finished talking to you, click the close button";
mes "This command will not run at all, cause the script has ended.";
This command will create a 'close' button in the message window for the invoking
character. WARNING: If no window is currently on screen, the script execution will halt
indefinitely! See 'close'. There is one important difference, though - even though
the message box will have closed, the script execution will not stop, and commands after
'close2' will still run, meaning an 'end' has to be used to stop the script, unless you
make it stop in some other manner.
mes "[Woman]";
mes "I will warp you now";
warp "place",50,50;
Don't expect things to run smoothly if you don't make your scripts 'end'.
This command will stop the execution for this particular script. The two
versions are perfectly equivalent. It is the normal way to end a script which
does not use 'mes'.
if (BaseLevel<=10) goto L_Lvl10;
if (BaseLevel<=20) goto L_Lvl20;
if (BaseLevel<=30) goto L_Lvl30;
if (BaseLevel<=40) goto L_Lvl40;
if (BaseLevel<=50) goto L_Lvl50;
if (BaseLevel<=60) goto L_Lvl60;
if (BaseLevel<=70) goto L_Lvl70;
npctalk "Look at that you are still a n00b";
npctalk "Look at that you are getting better, but still a n00b";
npctalk "Look at that you are getting there, you are almost 2nd profession now right???";
npctalk "Look at that you are almost 2nd profession";
Without the use if 'end' it would travel through the labels until the end of the
script. If you were lvl 10 or less, you would see all the speech lines, the use
of 'end' stops this, and ends the script.
*set <variable>,<expression>;
This command will set a variable to the value that the expression results in.
This is the only way to set a variable directly.
This is the most basic script command and is used a lot whenever you try to do
anything more advanced than just printing text into a message box.
set @x,100;
will make @x equal 100.
set @x,1+5/8+9;
will compute 1+5/8+9 (which is, surprisingly, 10 - remember, all numbers are
integer in this language) and make @x equal it.
Returns the variable reference (since trunk r12870).
*setd "<variable name>",<value>;
Works almost identical as set, just that the variable name is identified as a string,
thus can be constructed dynamically.
This command is equivalent to:
set getd("variable name"),<value>;
set $var$, "Poring";
setd "$var$", "Poporing";
mes $var$; // Will return Poporing
setd "$" + $var$ + "123$", "Poporing is cool";
mes $Poporing123$; // Will return Poporing is cool.
*getd("<variable name>")
Returns a reference to a variable, the name can be constructed dynamically.
Refer to setd for usage.
set getd("$varRefence"), 1;
set @i, getd("$pikachu");
*getvariableofnpc(<variable>,"<npc name>")
Returns a reference to a NPC variable (. prefix) from the target NPC.
This can only be used to get . variables.
//This will return the value of .var, note that this can't be used, since the value isn't caught.
//This will set the .v variable to the value of the TargetNPC's .var variable.
set .v,getvariableofnpc(.var,"TargetNPC");
//This will set the .var variable of TargetNPC to 1.
set getvariableofnpc(.var,"TargetNPC"),1;
Note: even though function objects can have .variables,
getvariableofnpc will not work on them.
*goto <label>;
This command will make the script jump to a label, usually used in conjunction
with other command, such as "if", but often used on it's own.
goto Label;
mes "This will not be seen";
mes "This will be seen";
Note by FlavioJS: goto's are "evil" and should be avoided if possible (ò_ó)
*menu "<option_text>",<target_label>{,"<option_text>",<target_label>,...};
This command will create a selectable menu for the invoking character. Only one
menu can be on screen at the same time.
Depending on what the player picks from the menu, the script execution will
continue from the corresponding label. (it's string-label pairs, not label-
Options can be grouped together, separated by the character ':'.
menu "A:B",L_Wrong,"C",L_Right;
It also sets a special temporary character variable @menu, which contains the
number of option the player picked. (Numbering of options starts at 1.)
This number is consistent with empty options and grouped options.
menu "A::B",L_Wrong,"",L_Impossible,"C",L_Right;
// If they click "A" or "B" they will end up here
// @menu == 1 if "A"
// @menu == 2 will never happen because the option is empty
// @menu == 3 if "B"
// Empty options are not displayed and therefore can't be selected
// this label will never be reached from the menu command
// If they click "C" they will end up here
// @menu == 5
If a label is '-', the script execution will continue right after the menu
command if that option is selected, this can be used to save you time, and
optimize big scripts.
menu "A::B:",-,"C",L_Right;
// If they click "A" or "B" they will end up here
// @menu == 1 if "A"
// @menu == 3 if "B"
// If they click "C" they will end up here
// @menu == 5
Both these examples will perform the exact same task.
If you give an empty string as a menu item, the item will not display. This
can effectively be used to script dynamic menus by using empty string for
entries that should be unavailable at that time.
You can do it by using arrays, but watch carefully - this trick isn't high
wizardry, but minor magic at least. You can't expect to easily duplicate it
until you understand how it works.
Create a temporary array of strings to contain your menu items, and populate it
with the strings that should go into the menu at this execution, making sure not
to leave any gaps. Normally, you do it with a loop and an extra counter, like
setarray @possiblemenuitems$[0],<list of potential menu items>;
set @j,0; // That's the menu lines counter.
// We loop through the list of possible menu items.
// @i is our loop counter.
for( set @i,0; @i<getarraysize(@possiblemenuitems$) ; set @i,@i+1 )
// That 'condition' is whatever condition that determines whether
// a menu item number @i actually goes into the menu or not.
if (<condition>)
// We record the option into the list of options actually available.
set @menulist$[@j],@possiblemenuitems$[@i];
// We just copied the string, we do need it's number for later
// though, so we record it as well.
set @menureference[@j],@i;
// Since we've just added a menu item into the list, we increment
// the menu lines counter.
set @j,@j+1;
// We go on to the next possible menu item.
This will create you an array @menulist$ which contains the text of all items
that should actually go into the menu based on your condition, and an array
@menureference, which contains their numbers in the list of possible menu items.
(Remember, arrays start with 0.) There's less of them than the possible menu
items you've defined, but the menu command can handle the empty lines - only if
they are last in the list, and if it's made this way, they are. Now comes a
dirty trick:
// X is whatever the most menu items you expect to handle.
menu @menulist$[0],-,@menulist$[1],-,....@menulist$[<X>],-;
This calls up a menu of all your items. Since you didn't copy some of the
possible menu items into the list, it's end is empty and so no menu items will
show up past the end. But this menu call doesn't jump anywhere, it just
continues execution right after the menu command. (And it's a good thing it
doesn't, cause you can only explicitly define labels to jump to, and how do you
know which ones to define if you don't know beforehand which options will end up
where in your menu?)
But how do you figure out which option the user picked? Enter the @menu.
@menu contains the number of option that the user selected from the list,
starting with 1 for the first option. You know now which option the user picked
and which number in your real list of possible menu items it translated to:
mes "You selected "+@possiblemenuitems$[@menureference[@menu-1]]+"!";
@menu is the number of option the user picked.
@menu-1 is the array index for the list of actually used menu items that we
@menureference[@menu-1] is the number of the item in the array of possible menu
items that we've saved just for this purpose.
And @possiblemenuitems$[@menureference[@menu-1]] is the string that we used to
display the menu line the user picked. (Yes, it's a handful, but it works.)
You can set up a bunch of 'if (@menureference[@menu-1]==X) goto Y' statements to
route your execution based on the line selected and still generate a different
menu every time, which is handy when you want to, for example, make users select
items in any specific order before proceeding, or make a randomly shuffled menu.
Kafra code bundled with the standard distribution uses a similar array-based
menu technique for teleport lists, but it's much simpler and doesn't use @menu,
probably since that wasn't documented anywhere.
See also 'select', which is probably better in this particular case. Instead of
menu, you could use 'select' like this:
set @dummy,select(@menulist$[0],@menulist$[1],....@menulist$[<X>]);
For the purposes of the technique described above these two statements are
perfectly equivalent.
This function is a handy replacement for 'menu' for some specific cases where
you don't want a complex label structure - like, for example, asking simple yes-
no questions. It will return the number of menu option picked, starting with 1.
Like 'menu', it will also set the variable @menu to contain the option the user
if (select("Yes:No")==1) mes "You said yes, I know.";
And like 'menu', the selected option is consistent with grouped options
and empty options.
prompt works almost the same as select, except that when a character clicks
the Cancel button, this function will return 255 instead.
This command will make an input box pop up on the client connected to the
invoking character, to allow entering of a number or a string. This has many
uses, one example would be a guessing game, also making use of the 'rand'
mes "[Woman]";
mes "Try and guess the number I am thinking of.";
mes "The number will be between 1 and 10.";
set @number, rand(1,10);
input @guess;
mes "[Woman]";
mes "Well done that was the number I was thinking of";
mes "[Woman]";
mes "Sorry, that wasn't the number I was thinking of.";
If you give the input command a string variable to put the input in, it will
allow the player to enter text. Otherwise, only numbers will be allowed.
mes "[Woman]";
mes "Please say HELLO";
input @var$;
mes "[Woman]";
mes "Well done you typed it correctly";
mes "[Woman]";
mes "Sorry you got it wrong";
Normally you may not input a negative number with this command.
This is done to prevent exploits in badly written scripts, which would
let people, for example, put negative amounts of Zeny into a bank script and
receive free Zeny as a result.
Since trunk r12192 the command has two optional arguments and a return value.
The default value of 'min' and 'max' can be set with 'input_min_value' and
'input_max_value' in script_athena.conf.
For numeric inputs the value is capped to the range [min,max]. Returns 1 if
the value was higher than 'max', -1 if lower than 'min' and 0 otherwise.
For string inputs it returns 1 if the string was longer than 'max', -1 is
shorter than 'min' and 0 otherwise.
*callfunc "<function>"{,<argument>,...<argument>};
This command lets you call up a function NPC. A function NPC can be called from
any script on any map server. Using the 'return' command it will come back to
the place that called it.
mes "[Woman]"
mes "Lets see if you win";
callfunc "funcNPC";
mes "Well done you have won";
set @win, rand(2);
if(@win==0) return;
mes "Sorry you lost";
You can pass arguments to your function - values telling it what exactly to do -
which will be available there with getarg() (see 'getarg')
Notice that returning is not mandatory, you can end execution right there.
If you want to return a real value from inside your function NPC, it is better
to write it in the function form, which will also work and will make the script
generally cleaner:
mes "[Man]"
mes "Gimme a number!";
input @number;
if (callfunc("OddFunc",@number)) mes "It's Odd!";
if (getarg(0)%2==0) return 0;// it's even
return 1;// it's odd
*callsub <label>{,<argument>,...<argument>};
This command will go to a specified label within the current script (do NOT use
quotes around it) coming in as if it were a 'callfunc' call, and pass it
arguments given, if any, which can be recovered there with 'getarg'. When done
there, you should use the 'return' command to go back to the point from where
this label was called. This is used when there is a specific thing the script
will do over and over, this lets you use the same bit of code as many times as
you like, to save space and time, without creating extra NPC objects which are
needed with 'callfunc'. A label is not callable in this manner from another
Example 1: callsub for checking (if checks pass, return to script)
callsub S_CheckFull, "guild_vs2",50;
switch( rand(4) ) {
case 0: warp "guild_vs2",9,50; end;
case 1: warp "guild_vs2",49,90; end;
case 2: warp "guild_vs2",90,50; end;
case 3: warp "guild_vs2",49,9; end;
if (getmapusers(getarg(0)) >= getarg(1)) {
mes "I'm sorry, this arena is full. Please try again later.";
Example 2: callsub used repeatedly, with different arguments
// notice how the Zeny check/delete is reused, instead of copy-pasting for every warp
switch(select("Abyss Lake:Amatsu Dungeon:Anthell:Ayothaya Dungeon:Beacon Island, Pharos") {
case 1: callsub S_DunWarp,"hu_fild05",192,207;
case 2: callsub S_DunWarp,"ama_in02",119,181;
case 3: callsub S_DunWarp,"moc_fild20",164,145;
case 4: callsub S_DunWarp,"ayo_fild02",279,150;
case 5: callsub S_DunWarp,"cmd_fild07",132,125;
// etc
// getarg(0) = "mapname"
// getarg(1) = x
// getarg(2) = y
if (Zeny >= 100) {
set Zeny, Zeny-100;
warp getarg(0),getarg(1),getarg(2);
} else {
mes "Dungeon warp costs 100 Zeny.";
This function is used when you use the 'callsub' or 'callfunc' commands. In the
call you can specify variables that will make that call different from another
one. This function will return an argument the function or subroutine was
called with, and is the normal way to get them.
This is another thing that can let you use the same code more than once.
Argument numbering starts with 0, i.e. the first argument you gave is number 0.
If no such argument was given, a zero is returned.
mes "[Woman]";
mes "Lets see if you win";
callfunc "funcNPC",2;
mes "Well done you have won";
mes "[Woman]";
mes "Lets see if you win";
callfunc "funcNPC",5;
mes "Well done you have won";
set @win, rand(getarg(0));
if(@win==0) return;
mes "Sorry you lost";
"woman1" NPC object calls the funcNPC. The argument it gives in this call is
stated as 2, so when the random number is generated by the 'rand' function, it
can only be 0 or 1. Whereas "woman2" gives 5 as the argument number 0 when
calling the function, so the random number could be 0, 1, 2, 3 or 4, this makes
"woman2" less likely to say the player won.
You can pass multiple arguments in a function call:
callfunc "funcNPC",5,4,3;
getarg(0) would be 5, getarg(1) would be 4 and getarg(2) would be 3.
Getarg has an optional argument since trunk r10773 and stable r10958.
If the target argument exists, it is returned.
Otherwise, if <default_value> is present it is returned instead,
if not the script terminates immediately.
in the previous example getarg(2,-1) would be 3 and getarg(3,-1) would be -1
*return {<value>};
This command causes the script execution to leave previously called function
with callfunc or script with callsub and return to the location, where the call
originated from. Optionally a return value can be supplied, when the call was
done using the function form.
Using this command outside of functions or scripts referenced by callsub will
result in error and termination of the script.
callfunc "<your function>";// when nothing is returned
set <variable>,callfunc("<your function>");// when a value is being returned
*function <function name>;
*<function name>;
*function <function name> {
(Skotlex stop being so selfish and give us all the commands T~T! J/k lol :P)
This works like callfunc. It's used for cleaner and fast scripting. Also they
must be inside a script. They're not separated scripts and they work more like
labels with arguments.
Note it looks like the normal declaration
You first Declare the function with function <function name>;.
Put the rest of your code. You can use then <function name>; to call the
function. It can also return a value when used with parentheses.
And at last, but inside the script itself, put the function <function name> {<code>}.
prontera,154,189,4 script Item seller 767,{
function SF_Selling;
mes "I'll open this now if you have more than 50z and you are level 50 or bigger";
if (Zeny > 50) && (BaseLevel > 50) {
mes "Welcome";
} else
set @needed,50-BaseLevel;
mes "You either are Level "+BaseLevel+", thus you need "+@needed+" more levels";
mes "to be able to use this NPC; or you don't have enough Zeny, so get some please";
function SF_Selling {
mes "Would you like to buy a phracon for 50z?";
switch(select("Yes","No, thanks")) {
case 1:
mes "Ok, how many?";
input @quantity;
set @check,Zeny/50;
if (@quantity > @check) {
mes "Sorry but you can only have "+@check+" Phracons with "+Zeny;
} else
mes "here you have";
set Zeny,Zeny-@quantity*50;
getitem 1010,@quantity;
case 2:
mes "Good bye then";
Example with parameters and return value:
prontera,150,150,0 script TestNPC 123,{
function MyAdd;
mes "Enter two numbers.";
input .@a;
input .@b;
mes .@a+" + "+.@b+" = "+MyAdd(.@a,.@b);
function MyAdd
return getarg(0)+getarg(1);
*if (<condition>) <statement>;
This is the basic conditional statement command, and just about the only one
available in this scripting language.
The condition can be any expression. All expressions resulting in a non-zero
value will be considered True, including negative values. All expressions
resulting in a zero are false.
If the expression results in True, the statement will be executed. If it isn't
true, nothing happens and we move on to the next line of the script.
if (1) mes "This will always print.";
if (0) mes "And this will never print.";
if (5) mes "This will also always print.";
if (-1) mes "Funny as it is, this will also print just fine.";
For more information on conditional operators see the operators section above.
Anything that is returned by a function can be used in a condition check without
bothering to store it in a specific variable:
if (strcharinfo(0)=="Daniel Jackson") mes "It is true, you are Daniel!";
More examples of using the 'if' command in the real world:
Example 1:
set @var1,1;
input @var2;
if(@var1==@var2) goto L_Same;
mes "Sorry that is wrong";
Example 2:
set @var1,1;
input @var2;
if(@var1!=@var2) mes "Sorry that is wrong";
(Notice examples 1 and 2 have the same effect.)
Example 3:
set @var1,@var1+1;
mes "[Forgetfull Man]";
if (@var==1) mes "This is the first time you have talked to me";
if (@var==2) mes "This is the second time you have talked to me";
if (@var==3) mes "This is the third time you have talked to me";
if (@var==4) mes "This is the forth time you have talked to me, but I think I am getting amnesia, I have forgotten about you";
if (@var==4) set @var,0;
Example 4:
mes "[Quest Person]";
if(countitem(512)>=1) goto L_GiveApple;
// The number 512 was found from item_db, it is the item number for the Apple.
mes "Can you please bring me an apple?";
mes "Oh an apple, I didn't want it, I just wanted to see one";
Example 5:
mes "[Person Checker]";
if($name$!=null) goto L_Check;
mes "Please tell me someones name";
input $name$;
set $name2$,strcharinfo(0);
mes "[Person Checker]";
mes "Thank you";
if($name$==strcharinfo(0) ) goto L_SameName;
mes "[Person Checker]";
mes "You are not the person that " +$name2$+ " mentioned";
set $name$,null;
set $name2$,null;
mes "[Person Checker]";
mes "You are the person that " +$name2$+ " just mentioned";
mes "nice to meet you";
goto L_End;
See 'strcharinfo' for explanation of what this function does.
Example 6: Using complex conditions.
mes "[Multi Checker]";
if( (@queststarted==1) && (countitem(512)>=5) ) goto L_MultiCheck;
// Only if the quest has been started AND You have 5 apples will it goto "L_MultiCheck"
mes "Please get me 5 apples";
set @queststarted,1;
mes "[Multi Checker]";
mes "Well done you have started the quest of got me 5 apples";
mes "Thank you";
set @queststarted,0;
delitem 512,5;
With the Advanced scripting engine, we got nested if's. That is:
if (<condition>)
If the condition doesn't meet, it'll do the action following the else.
We can also group several actions depending on a condition, the following way:
if (<condition)
} else {
Remember that if you plan to do several actions upon the condition being false, and
you forget to use the curly braces (the { } ), the second action will be executed regardless
the output of the condition, unless of course, you stop the execution of the script if the
condition is true (that is, in the first grouping using a return; , and end; or a close; )
Also, you can have multiple conditions nested or chained, and don't worry about limits as to
how many nested if you can have, there is no spoon ;)
if (<condition 1>)
else if (<condition 2>)
do that;
} else
do this;
*jump_zero (<condition>),<label>;
This command works kinda like an 'if'+'goto' combination in one go. (See 'if').
If the condition is false (equal to zero) this command will immediately jump to
the specified label like in 'goto'. While 'if' is more generally useful, for
some cases this could be an optimization.
The main reason for this command is that other control statements, like
'switch', 'for' or 'while', are disassembled into simple expressions together
with this command when a script is parsed.
*while (<condition>) <statement>;
This is probably the simplest and most frequently used loop structure. The 'while'
statement can be interpreted as "while <condition> is true, perform <statement>".
It is a pretest loop, meaning the conditional expression is tested before any of the
statements in the body of the loop are performed. If the condition evaluates to
false, the statement(s) in the body of the loop is/are never executed. If the
condition evaluates to true, the statement(s) are executed, then control transfers
back to the conditional expression, which is reevaluated and the cycle continues.
Multiple statements can be grouped with { }, curly braces, just like with the 'if' statement.
Example 1:
while (switch(select("Yes:No") == 2 ))
mes "You picked no.";
Example 2: multiple statements
while (switch(select("Yes:No") == 2 )) {
mes "Why did you pick no?";
mes "You should pick yes instead!";
Example 3: counter-controlled loop
set .@i, 1;
while (.@i <= 5) {
mes "This line will print 5 times.";
set .@i, .@i +1;
Example 4: sentinel-controlled loop
mes "Input 0 to stop";
input .@num;
while (.@num != 0) {
mes "You entered " + .@num;
input .@num;
*for (<variable initialization>; <condition>; <variable update>) <statement>;
Another pretest looping structure is the 'for' statement. It is considered a
specialized form of the 'while' statement, and is usually associated with counter-
controlled loops. Here are the steps of the 'for' statement: the initialize
statement is executed first and only once. The condition test is performed.
When the condition evaluates to false, the rest of the for statement is skipped.
When the condition evaluates to true, the body of the loop is executed, then the
update statement is executed (this usually involves incrementing a variable).
Then the condition is reevaluated and the cycle continues.
Example 1:
for( set .@i, 1; .@i <= 5; set .@i, .@i +1 )
mes "This line will print 5 times.";
Example 2:
mes "This will print the numbers 1 - 5.";
for( set .@i, 1; .@i <= 5; set .@i, .@i +1 )
mes .@i;
*do { <statement>; } while (<condition>);
The 'do...while' is the only post-test loop structure available in this script
language. With a post-test, the statements are executed once before the condition
is tested. When the condition is true, the statement(s) are repeated. When the
condition is false, control is transferred to the statement following the
'do...while' loop expression.
Example 1: sentinel-controlled loop
mes "This menu will keep appearing until you pick Cancel";
do {
set .@menu, select("One:Two:Three:Cancel");
} while (.@menu != 4);
Example 2: counter-controlled loop
mes "This will countdown from 10 to 1.";
set .@i, 10;
do {
mes .@i;
set .@i, .@i - 1;
} while (.@i > 0);
*setarray <array name>[<first value>],<value>{,<value>...<value>};
This command will allow you to quickly fill up an array in one go. Check the
Kafra scripts in the distribution to see this used a lot.
setarray @array[0], 100, 200, 300, 400, 500, 600;
First value is the index of the first element of the array to alter. For
setarray @array[0],200,200,200;
setarray @array[1],300,150;
will produce:
*cleararray <array name>[<first value to alter>],<value>,<number of values to set>;
This command will change many array values at the same time to the same value.
setarray @array[0], 100, 200, 300, 400, 500, 600;
// This will make all 6 values 0
cleararray @array[0],0,6;
// This will make array element 0 change to 245
cleararray @array[0],245,1;
// This will make elements 1 and 2 change to 345
cleararray @array[1],345,2;
See 'setarray'.
*copyarray <destination array>[<first value>],<source array>[<first value>],<amount of data to copy>;
This command lets you quickly shuffle a lot of data between arrays, which is in
some cases invaluable.
setarray @array[0], 100, 200, 300, 400, 500, 600;
// So we have made @array[]
copyarray @array2[0],@array[2],2;
// Now, @array2[0] will be equal to @array[2] (300) and
// @array2[1] will be equal to @array[3].
So using the examples above:
@array[0] = 100
@array[1] = 200
@array[2] = 300
@array[3] = 400
@array[4] = 500
@array[5] = 600
New Array:
@array2[0] = 300
@array2[1] = 400
@array2[2] = 0
@array2[3] = 0
Notice that @array[4] and @array[5] won't be copied to the second array, and it will return a
*deletearray <array name>[<first value>],<how much to delete>;
This command will delete a specified number of array elements totally from an
array, shifting all the elements beyond this towards the beginning.
// This will delete array element 0, and move all the other array elements
// up one place.
deletearray @array[0],1
// This would delete array elements numbered 1, 2 and 3, leave element 0 in its
// place, and move the other elements ups, so there are no gaps.
deletearray @array[1],3
|2.- Information-retrieving commands.|
This function will return either the name, party name or guild name for the
invoking character. Whatever it returns is determined by type.
0 - Character's name.
1 - The name of the party they're in if any.
2 - The name of the guild they're in if any.
3 - The name of the map the character is in.
If a character is not a member of any party or guild, an empty string will be
returned when requesting that information.
This function will return the various parts of the name of the calling NPC.
Whatever it returns is determined by type.
0 - The NPC's display name (visible#hidden)
1 - The visible part of the NPC's display name
2 - The hidden part of the NPC's display name
3 - The NPC's unique name (::name)
4 - The name of the map the NPC is in.
*getarraysize(<array name>)
This function returns the number of values that are contained inside the
specified array. Notice that zeros and empty strings at the end of this array
are not counted towards this number.
For example:
setarray @array[0], 100, 200, 300, 400, 500, 600;
set @arraysize,getarraysize(@array);
This will make @arraysize == 6. But if you try this:
setarray @array[0], 100, 200, 300, 400, 500, 600, 0;
set @arraysize,getarraysize(@array);
@arraysize will still equal 6, even though you've set 7 values.
*getelementofarray(<array name>,<index>)
This command retrieves the value of the element of given array at given index.
This is equivalent to using:
<array name>[<index>]
The reason for this is, that this short form is internally converted into a call
to getelementofarray, when the script is loaded.
Also useful when passing arrays to functions or accessing another npc's arrays:
getelementofarray(getvariableofnpc(.var, "testNPC"),<index>)
*readparam(<parameter number>)
This function will return the basic stats of an invoking character, referred to
by the parameter number. Instead of a number, you can use a parameter name if it
is defined in "db/const.txt".
For reference, in there these things are defined:
StatusPoint, BaseLevel, SkillPoint, Class, Upper, Zeny, Sex, Weight, MaxWeight,
JobLevel, BaseExp, JobExp, NextBaseExp, NextJobExp, Hp, MaxHp, Sp, MaxSp,
BaseJob, Karma, Manner, bVit, bDex, bAgi, bStr, bInt, bLuk
All of these also behave as variables, but don't expect to be able to just 'set'
all of them - some will not work for various internal reasons.
// This would return how many status points you haven't spent yet
Using this particular information as a function call is not required. Just
will give you the same result, and some of these parameters work just like
variables (i.e. you can 'set Zeny,100' to make the character have 100 Zeny,
destroying whatever Zeny they had before, or 'set Zeny,Zeny+100' to give them
100 Zeny)
You can also use this command to get stat values:
if(readparam(bVit)<=77) goto L_End;
mes "Only people with over 77 Vit are reading this";
*getcharid(<type>{,"<character name>"})
This function will return a unique ID number of the invoking character, or, if a
character name is specified, of that character.
Type is the kind of associated ID number required:
0 - Character ID number.
1 - Party ID number.
2 - Guild ID number.
3 - Account ID number.
4 - Battle ground ID
For most purposes other than printing it, a number is better to have than a name
(people do horrifying things to their character names).
If the character is not in a party or not in a guild, the function will return 0
if guild or party number is requested. If a name is specified and the character
is not found, 0 is returned.
If getcharid(0) returns a zero, the script got called not by a character and
doesn't have an attached RID. Note that this will cause the map server to
print "player not attached!" error messages, so it is preferred to use
"playerattached" to check for the character attached to the script.
if( getcharid(2) == 0 ) mes "Only members of a guild are allowed here!";
*getnpcid(<type>{,"<npc name>"});
Retrieves IDs of the currently invoked NPC. If a unique npc name is
given, IDs of that NPC are retrieved instead. Type specifies what ID
to retrieve and can be one of the following:
0 - Unit ID (GID)
If an invalid type is given or the NPC does not exist, return value
is 0.
These functions return the characters (child/mother/father) ID
if (getmotherid()) mes "Oh... I know your mother's ID:"+getmotherid();
This function returns 1 if the invoking character's marriage partner is
currently online and 0 if they are not or if the character has no partner.
This function returns the character ID of the invoking character's marriage
partner, if any. If the invoking character is not married, it will return 0,
which is a quick way to see if they are married:
if (getpartnerid()) mes "I'm not going to be your girlfriend!";
if (getpartnerid()) mes "You're married already!";
*getpartyname(<party id>)
This function will return the name of a party that has the specified ID number.
If there is no such party ID, "null" will be returned.
Lets say the ID of a party was saved as a global variable:
// This would return the name of the party from the ID stored in a variable
mes "You're in the '"+getpartyname($@var)"' party, I know!";
*getpartymember <party id>{,<type>};
Thank you to HappyDenn for all this information.
This command will find all members of a specified party and returns their names
(or character id or account id depending on the value of "type") into an array
of temporary global variables. There's actually quite a few commands like this
which will fill a special variable with data upon execution and not do anything
Upon executing this,
$@partymembername$[] is a global temporary string array which contains all the
names of these party members
(only set when type is 0 or not specified)
$@partymembercid[] is a global temporary number array which contains the
character id of these party members.
(only set when type is 1)
$@partymemberaid[] is a global temporary number array which contains the
account id of these party members.
(only set when type is 2)
$@partymembercount is the number of party members that were found.
The party members will (apparently) be found regardless of whether they are
online or offline. Note that the names come in no particular order.
Be sure to use $@partymembercount to go through this array, and not
'getarraysize', because it is not cleared between runs of 'getpartymember'. If
someone with 7 party members invokes this script, the array would have 7
elements. But if another person calls up the NPC, and he has a party of 5, the
server will not clear the array for you, overwriting the values instead. So in
addition to returning the 5 member names, the 6th and 7th elements from the last
call remain, and you will get 5+2 members, of which the last 2 don't belong to
the new guy's party. $@partymembercount will always contain the correct number,
(5) unlike 'getarraysize()' which will return 7 in this case.
// get the character's party ID
// immediately copy $@partymembercount value to a new variable, since
// you don't know when 'getpartymember' will get called again for someone
// else's party, overwriting your global array.
set @partymembercount,$@partymembercount;
// copy $@partymembername array to a new array
copyarray @partymembername$[0],$@partymembername$[0],@partymembercount;
//list the party members in NPC dialog
set @count,0;
if(@count == @partymembercount) goto L_DisplayMemberEnd;
mes (@count + 1) + ". ^0000FF" + @partymembername$[@count] + "^000000";
set @count,@count+1;
goto L_DisplayMember;
*getpartyleader(<party id>{,<type>})
This function returns some information about the given party-id's leader.
When type is omitted, the default information retrieved is the leader's name.
Possible types are:
1: Leader account id
2: Leader character id
3: Leader's class
4: Leader's current map name
5: Leader's current level as stored on the party structure (may not be
current level if leader leveled up recently).
If retrieval fails (leader not found or party does not exist), this function
returns "null" instead of the character name, and -1 for the other types.
This function will return the number for the current character look value
specified by type. See 'setlook' for valid look types.
This can be used to make a certain script behave differently for characters
dressed in black. :)
*getsavepoint(<information type>)
This function will return information about the invoking character's save point.
You can use it to let a character swap between several recorded save points.
Available information types are:
0 - Map name (a string)
1 - X coordinate
2 - Y coordinate
2,2 Item-related commands
*getequipid(<equipment slot>)
This function returns the item ID of the item equipped in the equipment slot
specified on the invoking character. If nothing is equipped there, it returns -1.
Valid equipment slots are:
EQI_HEAD_TOP (1) - Upper head gear
EQI_ARMOR (2) - Armor (Where you keep your Jackets and Robes)
EQI_HAND_L (3) - What is in your Left hand.
EQI_HAND_R (4) - What is in your Right hand.
EQI_GARMENT (5) - The garment slot (Mufflers, Hoods, Manteaus)
EQI_SHOES (6) - What foot gear the player has on.
EQI_ACC_L (7) - Accessory 1.
EQI_ACC_R (8) - Accessory 2.
EQI_HEAD_MID (9) - Middle Headgear (masks and glasses)
EQI_HEAD_LOW (10) - Lower Headgear (beards, some masks)
Notice that a few items occupy several equipment slots, and if the character is
wearing such an item, 'getequipid' will return it's ID number for either slot.
Can be used to check if you have something equipped, or if you haven't got
something equipped:
if(getequipid(EQI_HEAD_TOP)==2234) goto L_WearingTiara;
mes "Come back when you have a Tiara on";
mes "What a lovely Tiara you have on";
You can also use it to make sure people don't pass a point before removing an
item totally from them. Let's say you don't want people to wear Legion Plate
armor, but also don't want them to equip if after the check, you would do this:
if ((getequipid(EQI_ARMOR) == 2341) || (getequipid(EQI_ARMOR) == 2342) goto L_EquipedLegionPlate;
// the || is used as an or argument, there is 2341 and 2342 cause there are
// two different legion plate armors, one with a slot one without.
if ((countitem(2341) > 0) || (countitem(2432) > 0) goto L_InventoryLegionPlate;
mes "I will lets you pass";
warp "place",50,50;
mes "You are wearing some Legion Plate Armor, please drop that in your stash before continuing";
mes "You have some Legion Plate Armor in your inventory, please drop that in your stash before continuing";
*getequipname(<equpment slot>)
Returns the jname of the item equipped in the specified equipment slot on the
invoking character, or an empty string if nothing is equipped in that position.
Does the same thing as getitemname(getequipid()). Useful for an NPC to state
what your are wearing, or maybe saving as a string variable.
See 'getequipid' for a full list of valid equipment slots.
if( getequipname(EQI_HEAD_TOP) != "" )
mes "So you are wearing a "+getequipname(EQI_HEAD_TOP)+" on your head";
mes "You are not wearing any head gear";
*getitemname(<item id>)
Given the database ID number of an item, this function will return the text
stored in the 'japanese name' field (which, in eAthena, stores an english name
the players would normally see on screen.)
This function will search the invoking character's inventory for any broken
items, and will return their item ID numbers. Since the character may have
several broken items, 1 given as an argument will return the first one found, 2
will return the second one, etc. Will return 0 if no such item is found.
// Let's see if they have anything broken:
if (getbrokenid(1)==0) goto Skip;
// They do, so let's print the name of the first broken item:
mes "Oh, I see you have a broken "+getitemname(getbrokenid(1))+" here!";
mes "You don't have anything broken, quit bothering me.";
*getequipisequiped(<equipment slot>)
This functions will return 1 if there is an equipment placed on the specified
equipment slot and 0 otherwise. For a list of equipment slots
see 'getequipid'. Function originally used by the refining NPCs:
if (getequipisequiped(EQI_HEAD_TOP)) goto L_equipped;
mes "[Refiner]";
mes "Do you want me to refine your dumb head?";
mes "[Refiner]";
mes "That's a fine hat you are wearing there...";
*getequipisenableref(<equipment slot>)
Will return 1 if the item equipped on the invoking character in the specified
equipment slot is refinable, and 0 if it isn't. For a list of equipment slots
see 'getequipid'.
if (getequipisenableref(EQI_HEAD_TOP)) goto L_Refine;
mes "[Refiner]";
mes "I can't refine this hat!...";
mes "[Refiner]";
mes "Ok I can refine this";
*getequiprefinerycnt(<equipment slot>)
Returns the current number of pluses for the item in the specified equipment
slot. For a list of equipment slots see 'getequipid'.
Can be used to check if you have reached a maximum refine value, default for
this is +10:
if(getequiprefinerycnt(EQI_HEAD_TOP) < 10) goto L_Refine_HeadGear;
mes "Sorry, it's not possible to refine hats better than +10";
mes "I will now upgrade your "+getequipname(EQI_HEAD_TOP);
*getequipweaponlv(<equipment slot>)
This function returns the weapon level for the weapon equipped in the specified
equipment slot on the invoking character. For a list of equipment slots see
Only EQI_HAND_L and EQI_HAND_R normally make sense, since only weapons
have a weapon level. You can, however, probably, use this field for other
equippable custom items as a flag or something.
If no item is equipped in this slot, or if it doesn't have a weapon level
according to the database, 0 will be returned.
switch (getequipweaponlv(EQI_HAND_R)) {
case 1: mes "You are holding a lvl 1 weapon"; break;
case 2: mes "You are holding a lvl 2 weapon"; break;
case 3: mes "You are holding a lvl 3 weapon"; break;
case 4: mes "You are holding a lvl 4 weapon"; break;
case 5: mes "You are holding a lvl 5 weapon, hm, must be a custom design"; break;
default: mes "Seems you don't have a weapon on"; break;
Or for the left hand, cause it can hold a weapon or a shield:
if(getequipid(EQI_HAND_R)==0) goto L_NothingEquiped;
switch (getequipweaponlv(EQI_HAND_L)) {
case 0: mes "You are holding a shield, so it doesnt have a level"; break;
case 1: mes "You are holding a lvl 1 weapon"; break;
case 2: mes "You are holding a lvl 2 weapon"; break;
case 3: mes "You are holding a lvl 3 weapon"; break;
case 4: mes "You are holding a lvl 4 weapon"; break;
case 5: mes "You are holding a lvl 5 weapon, hm, must be a custom design"; break;
mes "Seems you have nothing equipped";
*getequippercentrefinery(<equipment slot>)
This function calculates and returns the percent value chance to successfully
refine the item found in the specified equipment slot of the invoking character
by +1. There is no actual formula, the success rate for a given weapon level of
a certain refine level is found in the db/refine_db.txt file. For a list of
equipment slots see 'getequipid'.
These values can be displayed for the player to see, or used to calculate the
random change of a refine succeeding or failing and then going through with it
(which is what the official NPC refinery scripts use it for)
// This will find a random number from 0 - 99 and if that is equal to or more
// than the value recoverd by this command it will go to L_Fail
if (getequippercentrefinery(EQI_HAND_L)<=rand(100)) goto L_Fail;
*getareadropitem("<map name>",<x1>,<y1>,<x2>,<y2>,<item>)
This function will count all the items with the specified ID number lying on the
ground on the specified map within the x1/y1-x2/y2 square on it and return that
This is the only function around where a parameter may be either a string or a
number! If it's a number, it means that only the items with that item ID number
will be counted. If it is a string, it is assumed to mean the 'english name'
field from the item database. If you give it an empty string, or something that
isn't found from the item database, it will count items number '512' (apples).
*getequipcardcnt(<equipment slot>)
This function will return the number of cards that have been compounded onto a
specific equipped item for the invoking character. See 'getequipid' for a list
of possible equipment slots.
This command sets a bunch of arrays with a complete list of whatever the
invoking character has in their inventory, including all the data needed to
recreate these items perfectly if they are destroyed. Here's what you get:
@inventorylist_id[] - array of item ids.
@inventorylist_amount[] - their corresponding item amounts.
@inventorylist_equip[] - whether the item is equipped or not.
@inventorylist_refine[] - for how much it is refined.
@inventorylist_identify[] - whether it is identified.
@inventorylist_attribute[] - whether it is broken.
@inventorylist_card1[] - These four arrays contain card data for the items.
@inventorylist_card2[] These data slots are also used to store names
@inventorylist_card3[] inscribed on the items, so you can explicitly check
@inventorylist_card4[] if the character owns an item made by a specific
@inventorylist_expire[] - expire time (Unix time stamp). 0 means never expires.
@inventorylist_count - the number of items in these lists.
This could be handy to save/restore a character's inventory, since no other
command returns such a complete set of data, and could also be the only way to
correctly handle an NPC trader for carded and named items who could resell them
- since NPC objects cannot own items, so they have to store item data in
variables and recreate the items.
Notice that the variables this command generates are all temporary, attached to
the character, and integer.
Be sure to use @inventorylist_count to go through these arrays, and not
'getarraysize', because the arrays are not automatically cleared between runs
of 'getinventorylist'.
This function will return the number of cards inserted into the weapon currently
equipped on the invoking character.
While this function was meant for item scripts, it will work outside them:
if (cardscnt()==4) mes "So you've stuck four cards into that weapon, think you're cool now?";
This function will return the number of pluses the weapon currently equipped on
the invoking character has been refined for.
While this function was meant for item scripts, it will work outside them:
if (getrefine()==10) mes "Wow. That's a murder weapon.";
*getnameditem(<item id>,"<name to inscribe>");
*getnameditem("<item name>","<name to inscribe>");
This function is equivalent to using 'getitem', however, it will not just give
the character an item object, but will also inscribe it with a specified
character's name. You may not inscribe items with arbitrary strings, only with
names of characters that actually exist. While this isn't said anywhere
specifically, apparently, named items may not have cards in them, slots or no -
these data slots are taken by the character ID who's name is inscribed. Only one
remains free and it's not quite clear if a card may be there.
This function will return 1 if an item was successfully created and 0 if it
wasn't for whatever reason. Like 'getitem', this function will also accept an
'english name' from the item database as an item name and will return 0 if no
such item exists.
*getitemslots(<item ID>)
This function will look up the item with the specified ID number in the database
and return the number of slots this kind of items has - 0 if they are not
slotted. It will also be 0 for all non-equippable items, naturally, unless
someone messed up the item database. It will return -1 if there is no such item.
//@slots now has the amount of slots of the item with ID 1205.
set @slots, getItemSlots(1205);
*getiteminfo(<item ID>,<type>)
This function will look up the item with the specified ID number in the database
and return the info set by TYPE argument.
It will return -1 if there is no such item.
Valid types are:
0 - Buy Price; 1 - Sell Price; 2 - Item Type;
3 - maxchance (Max drop chance of this item e.g. 1 = 0.01% , etc..
if = 0, then monsters don't drop it at all (rare or a quest item)
if = 10000, then this item is sold in NPC shops only
4 - sex; 5 - equip; 6 - weight; 7 - atk; 8 - def; 9 - range;
10 - slot; 11 - look; 12 - elv; 13 - wlv; 14 - view id
Check sample in nps\sample\getiteminfo.txt
*getequipcardid(<equipment slot>,<card slot>)
Returns value from equipped item slot in the indicated slot:
num = equip position slot
slot = 0,1,2,3 (Card Slot N)
This func returns CARD ID, 255,254,-255 (for card 0, if the item is produced) it's useful
when you want to check item cards or if it's signed. Useful for such quests as
"Sign this refined item with players name" etc;
Hat[0] +4 -> Player's Hat[0] +4
2,1.- End of item-related commands.
*getmapxy("<variable for map name>",<variable for x>,<variable for y>,<type>{,"<search string>"})
This function will locate a character object, NPC object or pet's coordinates
and place their coordinates into the variables specified when calling it. It
will return 0 if the search was successful, and -1 if the parameters given were
not variables or the search was not successful.
Type is the type of object to search for:
0 - Character object
1 - NPC object
2 - Pet object
3 - Monster object.
While 3 is meant to look for a monster object, no searching will be done if you
specify type 3, and the function will always return -1.
The search string is optional. If it is not specified, the location of the
invoking character will always be returned for types 0 and 2, the location of
the NPC running this function for type 1.
If a search string is specified, for types 0 and 1, the character or NPC with
the specified name will be located. If type is 3, the search will locate the
current pet of the character who's name is given in the search string, it will
NOT locate a pet by name.
What a mess. Example, a working and tested one now:
mes "My name is Meh. I'm here so that Nyah can find me.";
mes "My name is Nyah.";
mes "I will now search for Meh all across the world!";
if (getmapxy(@mapname$,@mapx,@mapy,1,"Meh")!=0) goto Notfound;
mes "And I found him on map "+@mapname$+" at X:"+@mapx+" Y:"+@mapy+" !";
mes "I can't seem to find Meh anywhere!";
Notice that NPC objects disabled with 'disablenpc' will still be located.
This function will return the GM level of the account to which the invoking
character belongs. If this is somehow executed from a console command, 99 will
be returned, and 0 will be returned if the account has no GM level.
This allows you to make NPC's only accessible for certain GM levels, or behave
specially when talked to by GMs.
if (getgmlevel()) mes "What is your command, your godhood?";
if (getgmlevel()) goto Wherever;
*gettimetick(<tick type>)
This function will return the system time in UNIX epoch time (if tick type is 2)
or the time since the start of the current day in seconds if tick type is 1.
Passing 0 will make it return the server's tick, which is a measurement in
milliseconds used by the server's timer system. The server's tick is an
unsigned int which loops every ~50 days.
Just in case you don't know, UNIX epoch time is the number of seconds elapsed
since 1st of January 1970, and is useful to see, for example, for how long the
character has been online with OnPCLoginEvent and OnPCLogoutEvent, which could allow
you to make an 'online time counted for conviction only' jail script.
This function will return specified information about the current system time.
1 - Seconds (of a minute)
2 - Minutes (of an hour)
3 - Hour (of a day)
4 - Week day (0 for Sunday, 6 is Saturday)
5 - Day of the month.
6 - Number of the month.
7 - Year.
8 - Day of the year.
It will only return numbers.
if (gettime(4)==6) mes "It's a Saturday. I don't work on Saturdays.";
*gettimestr(<format string>,<max length>)
This function will return a string containing time data as specified by the
format string.
This uses the C function 'strfmtime', which obeys special format characters. For
a full description see, for example, the description of 'strfmtime' at
All the format characters given in there should properly work.
Max length is the maximum length of a time string to generate.
The example given in eAthena sample scripts works like this:
mes gettimestr("%Y-%m/%d %H:%M:%S",21);
This will print a full date and time like 'YYYY-MM/DD HH:MM:SS'.
This function will return a number of users on a map or the whole server. What
it returns is specified by Type.
Type can be one of the following values, which control what will be returned:
0 - Count of all characters on the map of the invoking character.
1 - Count of all characters in the entire server.
8 - Count of all characters on the map of the NPC the script is
running in.
*getmapusers("<map name>")
This function will return the number of users currently located on the specified
Currently being used in the PVP scripts to check if a PVP room is full of not,
if the number returned it equal to the maximum allowed it will not let you
*getareausers("<map name>",<x1>,<y1>,<x2>,<y2>)
This function will return the count of connected characters which are located
within the specified area - an x1/y1-x2/y2 square on the specified map.
This is useful for maps that are split into many buildings, such as all the
"*_in" maps, due to all the shops and houses.
This command will give the invoking character a list of names of the connected
characters (including themselves) into an NPC script message window (see 'mes')
paging it by 10 names as if with the 'next' command.
You need to put a 'close' after that yourself.
2,2.- Guild-related commands
*getguildname(<guild id>)
This function returns a guild's name given an ID number. If there is no such
guild, "null" will be returned;
// Would print what ever guild 10007 is, in my case this would return "AlcoROhics"
mes "The guild "+GetGuildName(10007)+" are all nice people.";
// This will do the same as above:
set @var,10007;
mes "We have some friends in "+GetGuildName(@var)+", you know.";
This is used all over the WoE controlling scripts. You could also use it for a
guild-based event.
*getguildmaster(<guild id>)
This function return the name of the master of the guild which has the specified
ID number. If there is no such guild, "null" will be returned.
// Would return the guild master of guild 10007, whatever that might be.
// In this example it would return "MissDjax" cause she owns "AlcoROhics" (10007)
mes getguildmaster(10007)+" runs "+getguildname(10007);
Can be used to check if the character is the guild master of the specified guild.
Maybe you want to make a room only guild masters can enter:
set @GID,getcharid(2);
if(@GID==0) goto L_NoGuild;
if(strcharinfo(0)==getguildmaster(@GID)) goto L_GuildMaster;
mes "Sorry you don't own the guild you are in";
mes "Sorry you are not in a guild";
mes "Welcome guild master of "+GetGuildName(@GID);
*getguildmasterid(<guild id>)
This function will return the character ID number of the guild master of the
guild specified by the ID. 0 if the character is not a guild master of any guild.
*getcastlename("<map name>")
This function returns the name of the castle when given the map name for that
castle. The data is read from 'db/castle_db.txt'.
*getcastledata("<map name>",<type of data>)
*setcastledata "<map name>",<type of data>,<value>;
This function returns the castle ownership information for the castle referred
to by it's map name. Castle information stored in 'save\castle.txt' for the TXT
version of the server and in 'guild_castle' table for the SQL version.
Valid types of data are:
0 - Will make the map server request the castle data from the char server, and
always return 0. This, apparently, will also cause indirectly the execution
of an 'OnAgitInit:' event mentioned at the beginning of this document.
1 - Guild ID
2 - Castle Economy score.
3 - Castle Defense score.
4 - Number of times the economy was invested in today.
5 - Number of times the defense was invested in today.
9 - Will return 1 if a Kafra was hired for this castle, 0 otherwise.
10 - Is 1 if the 1st guardian is present (Soldier Guardian)
11 - Is 1 if the 2nd guardian is present (Soldier Guardian)
12 - Is 1 if the 3rd guardian is present (Soldier Guardian)
13 - Is 1 if the 4th guardian is present (Archer Guardian)
14 - Is 1 if the 5th guardian is present (Archer Guardian)
15 - Is 1 if the 6th guardian is present (Knight Guardian)
16 - Is 1 if the 7th guardian is present (Knight Guardian)
17 - Is 1 if the 8th guardian is present (Knight Guardian)
18-25 types of data will return current hit point values for guardians 1-8
The 'setcastledata' command will behave identically, but instead of returning
values for the specified types of accessible data, it will alter them and cause
them to be sent to the char server for storage. Data type of 0 won't do
anything, obviously.
*getgdskilllv(<guild id>,<skill id>)
*getgdskilllv(<guild id>,"<skill name>")
This function returns the level of the skill <skill id> of the guild <guild id>.
If the guild does not have that skill, 0 is returned.
If the guild does not exist, -1 is returned.
Refer to 'db/skill_db.txt' for the full list of skills. (GD_* are guild skills)
*requestguildinfo <guild id>,"<event label>";
This command requests the guild data from the char server and merrily continues
with the execution. Whenever the guild information becomes available (which
happens instantly if the guild information is already in memory, or later, if it
isn't and the map server has to wait for the char server to reply) it will run
the specified event as in a 'doevent' call.
*getmapguildusers(<mapname>,<guild id>)
Returns the amount of persons from the given guild that are on the given map.
//Will set the @persons variable to the amount of persons from the guild
//which ID's = 10 and are at prontera.
set @persons,getMapGuildUsers("prontera",10);
2,2.- End of guild-related commands
*getskilllv(<skill id>)
*getskilllv("<skill name>")
This function returns the level of the specified skill that the invoking
character has. If they don't have the skill, 0 will be returned. The full list
of character skills is available in 'db/skill_db.txt'.
There are two main uses for this function, it can check whether the character
has a skill or not, and it can tell you if the level is high enough.
Example 1:
if (getskilllv(152)) goto L_HasSkillThrowStone;
mes "You don't have Throw Stone";
mes "You have got the skill Throw Stone";
Example 2:
if (getskilllv(28) >= 5) goto L_HasSkillHeallvl5orMore;
if (getskilllv(28) == 10) goto L_HasSkillHealMaxed;
mes "You heal skill is below lvl 5";
mes "Your heal lvl is 5 or more";
mes "Your heal lvl has been maxed";
This command sets a bunch of arrays with a complete list of skills the
invoking character has. Here's what you get:
@skilllist_id[] - skill ids.
@skilllist_lv[] - skill levels.
@skilllist_flag[] - see 'skill' for the meaning of skill flags.
@skilllist_count - number of skills in the above arrays.
While 'getskillv' is probably more useful for most situations, this is the
easiest way to store all the skills and make the character something else for a
while. Advanced job for a day? :) This could also be useful to see how many
skills a character has.
This function will return pet information for the pet the invoking character
currently has active. Valid types are:
0 - Unique pet ID number as stored by the char server and distinguishing it
from all other pets the characters actually have. This value is currently
useless, at most you can use it to tell pets apart reliably.
1 - Pet class number as per 'db/pet_db.txt' - will tell you what kind of a pet it
2 - Pet name. Will return "null" if there's no pet.
3 - Pet friendly level (intimacy score). 1000 is full loyalty.
4 - Pet hungry level. 100 is completely full.
5 - Pet rename flag. 0 means this pet has not been named yet.
This function works as a direct counterpart of 'getpetinfo':
0 - Homunculus unique ID
1 - Homunculus Class
2 - Name
3 - Friendly level (intimacy score). 100000 is full loyalty.
4 - Hungry level. 100 is completely full.
5 - Rename flag. 0 means this homunculus has not been named yet.
6 - Homunculus level
Returns current pet status, all are integers except name.
Returns 0 or "" if the player doesn't have pets.
Flags usable >>
set @i, petstat(PET_CLASS);
*getmonsterinfo(<mob ID>,<type>)
This function will look up the monster with the specified ID number in the
mob database and return the info set by TYPE argument.
It will return -1 if there is no such monster (or the type value is invalid),
or "null" if you requested the monster's name.
Valid types are listed in const.txt:
Check sample in nps\sample\getmonsterinfo.txt
*getmobdrops(<mob id>)
This command will find all drops of the specified mob and return the item IDs
and drop percentages into arrays of temporary global variables.
'getmobdrops' returns 1 if successful and 0 if the mob ID doesn't exist.
Upon executing this,
$@MobDrop_item[] is a global temporary number array which contains the
item IDs of the monster's drops.
$@MobDrop_rate[] is a global temporary number array which contains the
drop percentages of each item. (1 = .01%)
$@MobDrop_count is the number of item drops found.
Be sure to use $@MobDrop_count to go through the arrays, and not
'getarraysize', because the temporary global arrays are not cleared between
runs of 'getmobdrops'. If a mob with 7 item drops is looked up, the arrays would
have 7 elements. But if another mob is looked up and it only has 5 item drops,
the server will not clear the arrays for you, overwriting the values instead. So
in addition to returning the 5 item drops, the 6th and 7th elements from the
last call remain, and you will get 5+2 item drops, of which the last 2 don't
belong to the new mob. $@MobDrop_count will always contain the correct number
(5), unlike 'getarraysize()' which would return 7 in this case.
// get a Mob ID from the user
input .@mob_id;
if (getmobdrops(.@mob_id)) { // 'getmobdrops' returns 1 on success
// immediately copy global temporary variables into scope variables,
// since we don't know when 'getmobdrops' will get called again for
// another mob, overwriting your global temporary variables
set .@count, $@MobDrop_count;
copyarray .@item[0],$@MobDrop_item[0],.@count;
copyarray .@rate[0],$@MobDrop_rate[0],.@count;
mes getmonsterinfo(.@mob_id,MOB_NAME) + " - " + .@count + " drops found:";
for( set .@i,0; .@i < .@count; set .@i,.@i +1 ) {
mes .@item[.@i] + " (" + getitemname(.@item[.@i]) + ") " + .@rate[.@i]/100 + ((.@rate[.@i]%100 < 10) ? ".0":".") + .@rate[.@i]%100 + "%";
} else {
mes "Unknown monster ID.";
*getmapmobs("<map name>")
This function will return the total count of monsters currently located on the
specified map. If the map name is given as "this", the map the invoking
character is on will be used. If the map is not found, or the invoker is not a
character while the map is "this", it will return -1.
This function will return the length of the string given as an argument. It is
useful to check if anything input by the player exceeds name length limits and
other length limits and asking them to try to input something else.
Returns the total amount of skill points a character possesses (SkillPoint+SP's used in skills)
This command can be used to check the currently attached characters total amount of skill points.
This means the skill points used in skill are counted, and added to SkillPoints (number of skill points not used).
//This will set the temp character variable @skillPoints to the amount of skill points,
//and then tell the player the value.
set @skillPoints, skillPointCount();
mes "You have "+@skillPoints+" skill points in total!";
//Self-explanatory... :P
if (skillPointCount() > 20)
mes "Wow, you have more then 20 Skill Points in total!";
*getscrate(<effect type>,<base rate>{,<target ID number>})
This function will return the chance of a status effect affecting the invoking
character, in percent, modified by the their current defense against said
status. The 'base rate' is the base chance of the status effect being inflicted,
in percent.
if (rand(100) > getscrate(Eff_Blind, 50)) goto BlindHimNow;
You can see the full list of available effect types you can possibly inflict in
'db/const.txt' under 'Eff_'.
It is pretty certain that addressing the target by an ID number will not
currently work due to a bug.