forked from HerculesWS/Hercules
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathscript_commands.txt
11209 lines (8234 loc) · 395 KB
/
script_commands.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//===== Hercules Documentation ===============================
//= Hercules Script Commands
//===== By: ==================================================
//= Hercules Dev Team
//===== Description: =========================================
//= A reference manual for the Hercules scripting language.
//= Commands are sorted depending on their functionality.
//============================================================
This document is a reference manual for all the scripting commands and
functions available in current Hercules GIT. 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.
Structure
---------
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 tell us. :)
Syntax
------
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 idea.
Wherever you refer to a map, use 'mapname' instead of 'mapname.gat'.
Script loading structure
------------------------
Scripts are loaded by the map server as referenced in the
'npc/(pre-)re/scripts_main.conf' configuration file.
The file contains a list of scripts to be loaded (or other configuration files
to be parsed, through the @include directive), in the following format:
npc_global_list: (
"npc/path/to/the/script/to/load.txt",
"npc/other/file.txt",
@include "npc/other_configuration_file.conf"
)
Any script will only get loaded once even if specified twice, to prevent
possible errors.
It's possible to specify scripts to exclude from loading, by inserting them
(using the same syntax) in the list available in npc/scripts_removed.conf:
npc_removed_list: (
"npc/path/to/the/file/to/skip.txt",
)
Script file format
------------------
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.
// This line will be ignored when processing the script.
Block comments can also be used, where you can place /* and */ between any
text you wish Hercules to ignore.
Example:
/* This text,
* no matter which new line you start
* is ignored, until the following
* symbol is encountered: */
The asterisks (*) in front of each line is a personal preference, and is
not required.
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%'
or '<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 'npc/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>{,<mob size>,<mob ai>}
Map name is the name of the map the monsters will spawn on. X,Y are the
coordinates where the mob should spawn. If X's and Y's 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 'db/(pre-)re/mob_db.conf' database of
monsters. If the mob name is given as "--ja--", the 'japanese name' field
from the monster database is used, (which, in Hercules, 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).
Amount is the amount of monsters that will be spawned when this command is
executed, it is affected by spawn rates in 'conf/map/battle/monster.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 milliseconds (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 work.
There are two optional fields for monster size and AI. Size can be 0
(medium), 1 (small), or 2 (big). AI can be 0 (default), 1
(attack/friendly), 2 (sphere), 3 (flora), or 4 (zanzou).
Alternately, a monster spawned using 'boss_monster' instead of 'monster' is able to be
detected on the map with the SC_CASH_BOSS_ALARM status (used by Convex Mirror, item ID# 12214).
A monster spawned using 'miniboss_monster' is spawn monster as mini boss view.
** 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>,{<code>}
<map name>,<x>,<y>,<facing>%TAB%script%TAB%<NPC Name>%TAB%<sprite>,<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 is the sprite identifier used to display this particular NPC. For a
full list of sprite numbers see http://nn.ai4rei.net/dev/npclist/ as
well as doc/constants_(pre-)re.md.
You may also use a monster's ID constant instead to display a monster sprite
for this NPC, in npcs that have view ids of mobs it's encouraged to use
OnTouch events with a 2,2 range and with an 'end' after the header to avoid
bugs (for more info on why see npc_click@map/npc.c). It is possible to use a job
sprite as well, but you must first define it as a monster sprite,
a full description on how to do this is not in the scope of this manual.
A 'FAKE_NPC' sprite will make the NPC invisible (and unclickable).
A 'HIDDEN_NPC' sprite 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 terrain.
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, and using mobattached() will return
monster GID. If player left the area will trigger the label 'OnUnTouch'.
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%FAKE_NPC,{<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>,<itemid>:<price>{,<itemid>:<price>...}
<map name>,<x>,<y>,<facing>%TAB%shop%TAB%<NPC Name>%TAB%<sprite>,<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 'db/(pre-)re/item_db.conf' 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.
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 #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 a trader NPC
<map name>,<x>,<y>,<facing>%TAB%trader%TAB%<NPC Name>%TAB%<sprite>,{<code>}
-%TAB%trader%TAB%<NPC Name>%TAB%FAKE_NPC,{<code>}
All the standards that are valid to script objects are also valid for trader objects
(see ** Define an NPC object for more information).
This will define a trader NPC, which can cause a shop, cashshop or market window
to come up when clicked or called by other means. Unlike shop/cashshop NPCs this
type will run a code and can change the items that are being sold over time without
other NPC objects.
The types that a trader object can have are the following:
- NST_ZENY (0) Normal Zeny Shop (shop)
- NST_CASH (1) Normal Cash Shop (cashshop)
- NST_MARKET (2) Normal NPC Market Shop (where items have limited availability
and need to be refurbished)
- NST_CUSTOM (3) Custom Shop (any currency, item/var/etca, check sample)
- NST_BARTER (4) Barter Shop (each item with own item currency)
- NST_EXPANDED_BARTER (5) Expanded Barter Shop (buy items and spend zeny and items)
Unless otherwise specified via *tradertype an trader object will be defined as
NST_ZENY.
Note: NST_MARKET is only available with PACKETVER 20131223 or newer.
Note: NST_BARTER is only available with PACKETVER 20190116 main or 20190116 RE or 20181226 zero or newer.
Note: NST_EXPANDED_BARTER is only available with PACKETVER 20190904 main or 20190904 RE or 20190828 zero or newer.
See '12 - NPC Trader-Related Commands' and /doc/sample/npc_trader_sample.txt for
more information regarding how to use this NPC type.
** 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>
shop/cashshop/npc: <map name>,<x>,<y>,<facing>%TAB%duplicate(<label>)%TAB%<NPC Name>%TAB%<sprite>
npc: -%TAB%duplicate(<label>)%TAB%<NPC Name>%TAB%<sprite>,<triggerX>,<triggerY>
npc: <map name>,<x>,<y>,<facing>%TAB%duplicate(<label>)%TAB%<NPC Name>%TAB%<sprite>,<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, 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.
Conditional comments
--------------------
An extension of the block comment syntax can be used for conditional comments.
A conditional comment is treated as a block comment when the specified flag is
not defined and as a normal block of code if it is, in a similar way to #ifdef
blocks in the C preprocessor. This can be useful to make
parts of scripts (including top-level commands) optional depending on a server
feature, a setting or a plugin.
A conditional comment begins with /*@CONDITION followed by whitespace or */ and
ends with the first */ sequence encountered on a separate line, but the sequence
//@*/ at the beginning of a line (optionally with indentation) is recommended as
a terminator, since it provides compatibility with older versions of the script
engine that don't support conditional comments. The condition flag can be
negated by prefixing it with an exclamation mark (!). Condition flags consist of
an uppercase letter followed by a sequence of uppercase letters, digits and
underscores and are at least three characters long.
The following block is only parsed if FLAG is defined. If not defined, or when
loaded by an older engine, the block is treated as a block comment:
/*@FLAG
.@x = 1;
//@*/
The following block is only parsed if FLAG is not defined. If defined, or when
loaded by an older engine, the block is treated as a block comment:
/*@!FLAG
.@x = 1;
//@*/
The following block is only parsed if FLAG is defined. If not defined, the block
is treated as a block comment. When loaded by an older engine, the block is also
parsed:
/*@FLAG*/
.@x = 1;
//@*/
The following block is only parsed if FLAG is not defined. If defined, the block
is treated as a block comment. When loaded by an older engine, the block is also
parsed.
/*@!FLAG*/
.@x = 1;
//@*/
The Hercules core provides the following conditional comment flags (plugins may
provide more):
- TRUE - always defined
- HERCULES - always defined
- FALSE - never defined
- RENEWAL - only defined when compiled in renewal mode
- PRERENEWAL - only defined when compiled in pre-renewal mode
- LOADGMSCRIPTS - only defined when script_configuration.load_gm_scripts is true
Note: conditional comments cannot be nested.
~ 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 (see player-related commands).
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 of a
mob obtained through the monster() script command (if only summoned one),
the GID of a NPC obtained through the getnpcid() script command or the
account ID of a character (same as its RID). Another way would be to right
click on a mob, NPC or char as GM sprited char to view its GID.
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 double-clicking on it. OnUnequip_Script runs whenever the
equipment is unequipped 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.
Numbers
-------
The Hercules scripting engine supports 4 types of number literals:
type base syntax
----------------------------------------------
decimal 10 255
hexadecimal 16 0xFF
octal 8 0o377
binary 2 0b11111111
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'. If you wish
to make calculations in base 8, you can also use the octal notation like
'0o<octal digits>'. To make calculations in base 2 (binary), you can use
the binary notation like '0b<binary digits>'.
The following are all equivalent:
255 == 0xFF == 0o377 == 0b11111111
Number values can't exceed the limits of an integer variable: Any number
greater than INT_MAX (2147483647) or smaller than INT_MIN (-2147483648) will
be capped to those values and will cause a warning to be reported.
Underscores can also be used as visual separators for digit grouping purposes:
2_147_483_647
0x7FFF_FFFF
Keep in mind that number literals cannot start or end with a separator and no
more than one separator can be used in a row (so 12_3___456 is illegal).
Variables
---------
The meat of every programming language is variables - places where you
store data.
In Hercules scripting language, variable names are case sensitive. Even though
at the current time the script engine accepts them even with the incorrect
case, it is not advised to rely on this behavior, as it may change at any
time.
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.
"@" - A temporary variable attached to the character.
They disappear when the character logs out.
"$" - A global permanent variable.
They are stored in database table `mapreg`.
"$@" - A global temporary variable.
They are 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 character, script and scope. Each script
execution has its 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, its
variables are converted to values ('return .@var;' returns a
value, not a reference).
"'" - An instance variable.
These are used with the instancing system, and are unique to
each instance.
"#" - A permanent local account variable.
"##" - A permanent global account variable stored by the login server.
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 char-servers.
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.
Examples:
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 - instance integer variable
'name$ - instance 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. The maximum length of variable name including prefix and
suffix is 32. Permanent string variables (name$, $name$, #name$, ##name$)
can store text with a maximum length of 255 characters. All other string
type variables have no such limitation.
Some variables are special, that is, they are already defined for you by
the scripting engine. You can see the full list somewhere in
'doc/constants_(pre-)re.md', 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:
Zeny - Amount of Zeny in the inventory.
BankVault - Amount of Zeny in the bank.
Hp - Current amount of hit points.
MaxHp - Maximum amount of hit points.
Sp - Current spell points.
MaxSp - Maximum amount of spell points.
StatusPoint - Amount of status points remaining.
SkillPoint - Amount of skill points remaining.
BaseLevel - Character's base level.
JobLevel - Character's job level.
BaseExp - Amount of base experience points.
JobExp - Amount of job experience points.
NextBaseExp - Amount of base experience points needed to reach next level.
NextJobExp - Amount of job experience points needed to reach next level.
Weight - Amount of weight the character currently carries.
Display as in Weight/10.
MaxWeight - Maximum weight the character can carry.
Display as in MaxWeight/10.
Sex - Character's gender (SEX_MALE or SEX_FEMALE).
Class - Character's job.
Upper - 0 if the character is normal class, 1 if advanced, 2 if baby.
BaseClass - The character's 1-1 'normal' job, regardless of Upper value.
For example, this will return Job_Acolyte for Acolyte,
Priest/Monk, High Priest/Champion, and Arch Bishop/Sura.
If the character has not reached a 1-1 class, it will return
Job_Novice.
BaseJob - The character's 'normal' job, regardless of Upper value.
For example, this will return Job_Acolyte for Acolyte,
Baby Acolyte, and High Acolyte.
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 file.
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.
Assigning variables
--------- ---------
Variables can be accessed and assigned values directly without the use of
the built-in 'set' function. This means that variables can be accessed and
modified much like other programming languages.
.@x = 100;
.@x = .@y = 100;
Support for modifying variable values using 'set' is still supported (and
required to exist for this method to work) so previous scripts will
continue working. Its usage, though, is deprecated, and it should never be
used in new scripts unless there are special reasons to do so.
When assigning values, all operator methods are supported which exist in
the below 'Operators' section. For instance:
.@x += 100;
.@x -= 100;
.@x *= 2;
.@x /= 2;
.@x %= 5;
.@x >>= 2;
.@x <<= 2;
Will all work. For more information on available operators, see the
Operators section described below. All operators listed there may be
placed in-front of the '=' sign when modifying variables to perform the
action as required.
Increment and decrement operators are also provided, for your convenience.
Pre-increment and pre-decrement operators:
++.@x; // same as .@x = .@x + 1
--.@x; // same as .@x = .@x - 1
Post-increment and post-decrement operators:
.@x++; // similar to .@x = .@x + 1
.@x--; // similar to .@x = .@x - 1
The difference between pre- and post- increment/decrement operators is that,
when used in an expression, the pre- ones will be executed before evaluating
the expression, while the post- ones will be executed after. For example:
.@x = 1;
.@y = ++.@x; // After this line is executed, both .@y and .@x will be 2
.@x = 1;
.@y = .@x++; // After this line is executed, .@y will be 1, .@x will be 2
Note: The pre-increment/pre-decrement operators are, by design, faster (or at
least not slower) than their respective post- equivalent.
Note:
!! Currently the scripting engine does not support directly copying array
!! variables. In order to copy arrays between variables the use of
!! 'copyarray' function is still required.
Strings
-------
Strings are enclosed in "double quotes". To include the literal double
quote symbol (") in a string you need to escape it with a backslash:
.@string$ = "This string contains some \"double quote\" symbols";
Arrays
------
Arrays (in Hercules 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>]
All variable types can be used as arrays.
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:
.@arrayofnumbers[0] = 1;
You can use a variable (or an expression, or even a value from an another
array) as array index:
.@x = 100;
.@arrayofnumbers[.@x] = 10;
This will make .@arrayofnumbers[100] equal to 10.
Index numbering always starts with 0 and arrays can hold over 2 billion
variables. As such, the (guaranteed) allowed values for indices are in the
range 0 ~ 2147483647.
If the array index is omitted, it defaults to zero. Writing
.@arrayofnumbers is perfectly equivalent to writing .@arrayofnumbers[0].
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.
Variable References
-------------------
//##TODO
Hard-coded constants
--------------------
Most of the constants defined by the scripting engine can be found in
'doc/constants_(pre-)re.md' and have the same value independently of settings
that are core related, but there are constants that can be used to
retrieve core information that's set when the server is compiled.
PACKETVER - Server packet version
MAX_LEVEL - Maximum level
MAX_STORAGE - Maximum storage items
MAX_GUILD_STORAGE - Maximum guild storage items
MAX_CART - Maximum cart items
MAX_INVENTORY - Maximum inventory items
MAX_ZENY - Maximum Zeny in the inventory
MAX_BANK_ZENY - Maximum Zeny in the bank
MAX_BG_MEMBERS - Maximum BattleGround members
MAX_CHAT_USERS - Maximum Chat users
MAX_REFINE - Maximum Refine level
MAX_ITEM_GRADE - Maximum Grade level
MAX_ITEM_ID - Maximum Item ID
MAX_MENU_OPTIONS - Maximum NPC menu options
MAX_MENU_LENGTH - Maximum NPC menu string length
MOB_CLONE_START - Clone ID start from this range
MOB_CLONE_END - Clone ID end with this range
Send targets and status options are also hard-coded and can be found
in 'doc/constants_(pre-)re.md'.
Operators
---------
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 raise the first number to the power of the second number.
/ - 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 contain
the same value.
>= - 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.
~= - True if the second value (as regular expression) matches the first
value. Both values must be strings. See the script function pcre_match()
for more details and advanced features.
~! - True if the second value (as regular expression) DOES NOT match the
first value. Both values must be strings. See script function pcre_match()
for more details and advanced features.
Examples:
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.
Example:
b = 2;
a = b << 3;
mes(a);
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 Hercules 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 in variables it is
possible to store 32 different options in one variable (by using the
bits on position 0 to 31).
Example(s):
- 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:
.@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("Option 5 is activated");
This would print the messages about option 2, 3 and 5 (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.
Example:
- First let's set the quests that are currently in progress:
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)
inProgress = inProgress^2;
mes("Quest 2: find a newbie and be helpful to him for an hour.");
close();
}
- 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)
inProgress = inProgress^1;
mes("Quest 1 complete!! You unlocked the secrets of the Xor dynasty, use them wisely.");
close();
}
Unary operators with only with a single number, which follows the
operator, and are the following:
- - Negation.
The sign of the number will be reversed. If the number was positive,
it will become negative and vice versa.
Example:
.@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.
Example:
if (!callfunc("F_dosomething")) {
mes("Doing something failed.");
close();
}
~ - Bitwise Not.
Reverses each bit in a number, also known as one's complement. Cleared
bits are set, and set bits are cleared.
Example:
- Ensure, that quest 2 is disabled, while keeping all other active, if
they are.
inProgress = inProgress&(~2);
// same as set inProgress, inProgress&0xfffffffd
Ternary operators take three expressions (numbers, strings or boolean),
and are the following:
?: - Conditional operator
Very useful e.g. to replace
if (Sex == SEX_MALE)