Closed 4Luke4 closed 4 years ago
Yes, I had that in mind since the beginning. It's just that no one asked, and since there's no demand - there's no supply. I didn't use SSL myself, so I'm going to need formal syntax description. Full, with all the words. And some samples. And I don't know what SLB is.
Also, there's a particular issue of nerds being really creative with names:
Stratagems Scripting Language
= .ssl
Star-Trek Scripting Language
= .ssl
I mean, seriously? Although, MultiLanguage Server
= MLS
:roll_eyes: who am I to complain. Will need to add an extra setting or something to distinguish.
I didn't use SSL myself, so I'm going to need formal syntax description. Full, with all the words. And some samples. And I don't know what SLB is.
Well, you could start by looking at the underlying Perl source code here.
The ssl
files are the actual code, whereas the slb
files are "library files" containing common data.
I can give you lots of code snippets but first you'd better get familiar with its syntax (shouldn't be too hard for you....).
Also, the thread linked above contains some introductory examples you might want to look at...
I don't see how perl code would help. I think it's going to be a fool's errand to work on implementing it without having any first hand experience, sorry. So if syntax tree isn't described by someone who works with SSL often, it'll have to wait until I actually use it somewhere.
I don't see how perl code would help. I think it's going to be a fool's errand to work on implementing it without having any first hand experience, sorry. So if syntax tree isn't described by someone who works with SSL often, it'll have to wait until I actually use it somewhere.
I see. If that's the case, then I'll write down the related context-free grammar with the help of some code samples... I've been using it for quite some time, so I'd like to try...
That'd be great. The important thing is to define standard blocks, actual code words don't matter so much.
@burner1024
Since any BAF
block is valid in SSL
(but not in SLB
!), you could start by implementing that....
No point, starting from more specific blocks might lead to throwing it all away later. Need to start from the root.
I see.
So, just to give you an idea (SSL
format):
BEGIN_ACTION_DEFINITION
Name(AttackReevaluate)
TRIGGER
Allegiance(Myself,GOODCUTOFF)
ACTION
RESPONSE #scsprob1
AttackReevaluate(scstarget,scsargument1)
END
IF TRIGGER
TargetBlock(EVILCUTOFFsInOrder)
TriggerBlock(MainhandWeaponUsable)
StateCheck(scstarget,0x9)
!StateCheck(scstarget,STATE_REALLY_DEAD)
Range(scstarget,4)
THEN DO
Action(AttackReevaluate,45)
END
TargetBlock
s and TriggerBlock
s are defined in a separate SLB
file:
TARGET=EVILCUTOFFsInOrder
[EVILCUTOFF]
SecondNearest([EVILCUTOFF])
ThirdNearest([EVILCUTOFF])
FourthNearest([EVILCUTOFF])
FifthNearest([EVILCUTOFF])
SixthNearest([EVILCUTOFF])
TRIGGER=MainhandWeaponUsable
WeaponEffectiveVs(scstarget,MAINHAND)
WeaponCanDamage(scstarget,MAINHAND)
TargetBlock(EVILCUTOFFsInOrder)
will compile as See([EVILCUTOFF])
, See(SecondNearest([EVILCUTOFF]))
, etc. up to See(SixthNearest([EVILCUTOFF]))
.scstarget
will compile as [EVILCUTOFF]
, ... , SixthNearest([EVILCUTOFF])
.scsargument1
will compile as 45
.MainhandWeaponUsable
will compile as
WeaponEffectiveVs(scstarget,MAINHAND)
WeaponCanDamage(scstarget,MAINHAND)
, where scstarget
will compile as before.
So, to sum up, that ssl
block will compile as:
IF
Allegiance(Myself,GOODCUTOFF)
See([EVILCUTOFF])
WeaponEffectiveVs([EVILCUTOFF],MAINHAND)
WeaponCanDamage([EVILCUTOFF],MAINHAND)
StateCheck([EVILCUTOFF],0x9) // 0x9 will compile as 'STATE_SLEEPING | STATE_STUNNED'
!StateCheck([EVILCUTOFF],STATE_REALLY_DEAD)
Range([EVILCUTOFF],4)
THEN
RESPONSE #100
AttackReevaluate([EVILCUTOFF],45)
END
// ........
// Up to:
IF
Allegiance(Myself,GOODCUTOFF)
See(SixthNearest([EVILCUTOFF]))
WeaponEffectiveVs(SixthNearest([EVILCUTOFF]),MAINHAND)
WeaponCanDamage(SixthNearest([EVILCUTOFF]),MAINHAND)
StateCheck(SixthNearest([EVILCUTOFF]),0x9) // 0x9 will compile as 'STATE_SLEEPING | STATE_STUNNED'
!StateCheck(SixthNearest([EVILCUTOFF]),STATE_REALLY_DEAD)
Range(SixthNearest([EVILCUTOFF]),4)
THEN
RESPONSE #100
AttackReevaluate(SixthNearest([EVILCUTOFF]),45)
END
As you can see, that's pretty powerful...
I see. For the purposes of highlighting doesn't really matter how it compiles, though. SSL blocks looks simple enough. The problem with SLB is that I don't see what marker to detect as the end of the block (both TARGET and TRIGGER)
I see. For the purposes of highlighting doesn't really matter how it compiles, though.
Yes, you're right, I specified it for clarification purposes...
The problem with SLB is that I don't see what marker to detect as the end of the block (both TARGET and TRIGGER)
As far as I know, there's no marker to detect the end of both a TARGET
block and a TRIGGER
block...
Next: on top of standard BAF
operators (i.e., OR()
, !
, @
), you need to add &
, |
, and ||
:
TriggerBlock(IsTargetableWithSpells|MR50|SINecromancy|MinorGlobe)
Action(Spell,CLERIC_CURSE|100|50)
IF TRIGGER
ConditionalTargetBlock(EVILCUTOFFsInOrderShort;OR(5)&Class(scstarget,MAGE_ALL)&Class(scstarget,CLERIC_ALL)&Class(scstarget,DRUID_ALL)&Class(scstarget,BARD_ALL)&Class(scstarget,SHAMAN))
TargetBlock(EVILCUTOFFsInOrder)
TriggerBlock(MainhandWeaponUsable)
!StateCheck(scstarget,STATE_REALLY_DEAD)
THEN DO
Action(AttackOneRound)
END
IF TRIGGER
HaveSpell(CLERIC_ZONE_OF_SWEET_AIR)
Global("Cloud","LOCALS",0)
BEGIN LOOP(scsloopvar||;Second;Third;Fourth;Fifth;Sixth)
OR(7)
SpellCast(scsloopvarNearestEnemyOf(Myself),WIZARD_INCENDIARY_CLOUD)
SpellCast(scsloopvarNearestEnemyOf(Myself),WIZARD_STINKING_CLOUD)
SpellCast(scsloopvarNearestEnemyOf(Myself),WIZARD_CLOUDKILL)
SpellCast(scsloopvarNearestEnemyOf(Myself),WIZARD_DEATH_FOG)
SpellCast(scsloopvarNearestEnemyOf(Myself),WIZARD_WRITHING_FOG)
SpellCast(scsloopvarNearestEnemyOf(Myself),WIZARD_SUFFOCATE)
SpellCast(scsloopvarNearestEnemyOf(Myself),CLERIC_CLOUD_OF_PESTILENCE)
END LOOP
THEN DO
SetGlobal("Cloud","LOCALS",1)
SetGlobalTimer("CLTM","LOCALS",TEN_ROUNDS)
Continue()
END
Additional SSL
keywords:
INCLUDE FILE("%path_to_ssl_file%")
Combine() // this one takes no argument...
IgnoreBlock("%name_of_slb_block%") // defined in a separate SLB file
RequireBlock("%name_of_slb_block%") // defined in a separate SLB file
ConditionalTargetBlock("%name_of_target_block%";"%list_of_baf_triggers%")
ActionCondition("%action_name%","%list_of_arguments%") // According to the underlying Perl code, this is identical to standard 'Action()'s – probably it's not been implemented yet...
BEGIN LOOP("%variable_name%"||var1;var2;...) ... END LOOP
VARIABLE()
SSLBoolean(firstBoolean|secondBoolean|etc)
Code snippets:
IF TRIGGER
IgnoreBlock(IsCleric)
RequireBlock(HasL3)
TargetBlock(PCsPreferringWeak)
TriggerBlock(Disabled|MR|ResistElectric|MinorGlobe|Enemy|SIAlteration)
AreaType(OUTDOOR)
IgnoreBlock(Demivrgvs)
THEN DO
Action(Spell,CLERIC_CALL_LIGHTNING|70|30)
END
VARIABLE(cat=dog)
// ..........
SetGlobal("cat","LOCALS",1) // will be replaced by 'SetGlobal("dog","LOCALS",1)'
INCLUDE FILE()
and Combine()
:
INCLUDE FILE(mod_folder/lib/ssl/attack.ssl)
INCLUDE FILE(mod_folder/lib/ssl/spellcasting.ssl)
IF TRIGGER
ConditionalTargetBlock(Protagonist;Allegiance(Myself,EVILCUTOFF))
ConditionalTargetBlock(PCsInOrderShort;Allegiance(Myself,EVILCUTOFF))
TargetBlock(EnemiesInOrder)
TriggerBlock(IsTargetableWithSpells|MR50|SIEnchantment|MinorGlobe)
TriggerBlock(ImmuneToParalysis)
General(scstarget,HUMANOID)
!StateCheck(scstarget,STATE_IMMOBILE)
!StateCheck(scstarget,STATE_REALLY_DEAD)
THEN DO
Combine()
Action(Spell,CLERIC_HOLD_PERSON|100|50)
END
IF
!GlobalTimerNotExpired("castspell","LOCALS")
!GlobalTimerNotExpired("belhifet_jump","LOCALS")
Global("belhifet_jump_loc","GLOBAL",0)
THEN
BEGIN LOOP(scsloopvar||1;2;3;4;5;6)
RESPONSE #100
SetGlobal("belhifet_jump_loc","GLOBAL",scsloopvar)
SetGlobalTimer("castspell","LOCALS",6)
SetGlobalTimer("belhifet_jump_timer","LOCALS",24)
ForceSpellPoint(%belhifet_loc_scsloopvar%,INNATE_INFERNAL_CONVEYANCE)
Wait(4)
FaceObject([PC])
END LOOP
END
Nested LOOP
s:
BEGIN LOOP(!StateCheck\(scstarget,STATE_NOT_TARGETABLE\)|| )
BEGIN LOOP(!StateCheck\(scstarget,STATE_INVISIBLE\) || )
INCLUDE FILE(%scsroot%/caster_shared/caster_definitions.ssl)
END LOOP
END LOOP
SSLBoolean()
:
BEGIN_ACTION_DEFINITION
Name(Spell)
TRIGGER
SSLBoolean(scsargument1)
HaveSpell(scsargument1)
ACTION
RESPONSE #scsprob1
Spell(scstarget,scsargument1)
END
IF TRIGGER
Target(NearestEnemyOf(Myself))
THEN DO
Action(Spell,WIZARD_MAGIC_MISSILE)
Action(Spell,WIZARD_CHROMATIC_ORB)
END
An additional example of BEGIN_ACTION_DEFINITION
:
BEGIN_ACTION_DEFINITION
Name(Spell)
TRIGGER
!GlobalTimerNotExpired("cast_spell","LOCALS")
HaveSpell(scsargument1)
Allegiance(Myself,EVILCUTOFF)
!StateCheck(Myself,STATE_SILENCED)
CheckStatLT(Myself,scsargument2,scsargument3)
!Allegiance(scstarget,EVILCUTOFF)
ACTION
RESPONSE #scsprob1
SetGlobalTimer("cast_spell","LOCALS",ONE_ROUND)
Spell(scstarget,scsargument1)
END
SLB
format: there are four main keywords: TRIGGER
, TRIGGER_REPLACE
, TARGET
, and TARGET_REPLACE
. Basically, each TRIGGER
block is a collection of BAF
triggers, whereas each TARGET
block is a collection of script objects. The additional object scstarget
is a valid script object in TRIGGER
blocks.
TRIGGER=IsSpellcaster
OR(5)
Class(scstarget,MAGE_ALL)
Class(scstarget,CLERIC_ALL)
Class(scstarget,DRUID_ALL)
Class(scstarget,BARD_ALL)
Class(scstarget,SHAMAN)
TRIGGER=Dummy
CheckStatLT(scstarget,100,RESISTMISSILE)
PartyRested()
OR(2)
CheckStatLT(scstarget,75,RESISTMISSILE)
GlobalTimerNotExpired("targetcompromise","LOCALS")
TRIGGER=AoEIceStorm
OR(3)
NextTriggerObject(scstarget)Range(NearestAllyOf(Myself),15)
NumInParty(1)
GlobalTimerNotExpired("targetcompromise","LOCALS")
OR(4)
NextTriggerObject(scstarget)!Range(NearestEnemyOfType([0.HUMANOID]),22)
NextTriggerObject(scstarget)CheckStatGT(NearestEnemyOfType([0.HUMANOID]),59,RESISTCOLD)
NextTriggerObject(scstarget)CheckStatGT(NearestEnemyOfType([0.HUMANOID]),59,RESISTMAGIC)
NextTriggerObject(scstarget)Race(NearestEnemyOfType([0.HUMANOID]),RAKSHASA)
TARGET=Beholders
[0.0.BEHOLDER]
SecondNearest([0.0.BEHOLDER])
ThirdNearest([0.0.BEHOLDER])
FourthNearest([0.0.BEHOLDER])
FifthNearest([0.0.BEHOLDER])
TARGET=EnemySpellcasters
[EVILCUTOFF.0.0.MAGE_ALL]
SecondNearest([EVILCUTOFF.0.0.MAGE_ALL])
ThirdNearest([EVILCUTOFF.0.0.MAGE_ALL])
[EVILCUTOFF.0.0.BARD]
[EVILCUTOFF.0.0.CLERIC_ALL]
[EVILCUTOFF.0.0.DRUID_ALL]
SecondNearest([EVILCUTOFF.0.0.CLERIC_ALL])
SecondNearest([EVILCUTOFF.0.0.DRUID_ALL])
TARGET=EnemiesInReverseOrder
SixthNearestEnemyOf(Myself)
FifthNearestEnemyOf(Myself)
FourthNearestEnemyOf(Myself)
ThirdNearestEnemyOf(Myself)
SecondNearestEnemyOf(Myself)
NearestEnemyOf(Myself)
I added basic hightlighting for both types, you should be able to try them in development mode. I know it's not perfect yet, but the bulk is there.
You might need to set files associations to have .ssl
detected as WeiDU variant:
"files.associations": {
"*.ssl": "weidu-ssl"
}
Feel free to try and report any issues, as usual, with screenshots. Well, you can try to handle them yourself, too. Syntax definition is basically a bunch of regexes including each other, and if you know WeiDU, you should be familiar with regexes as well. (After fixing yml, run scripts/syntaxes_to_json.sh
).
@burner1024
Looks fine, good job!
There's a color conflict with the keyword KIT
: as you know, this is both a script trigger and a STAT. The problem is that it gets highlighted as a STAT when used as a script trigger...
Also, this issue affects standard BAF
files as well...
Without delving too deep, we could make it case sensitive and resolve the conflict that way.
Without delving too deep, we could make it case sensitive and resolve the conflict that way.
Yes, that sounds more robust.
In so doing, you'll be able to distinguish, say, the TriggerBlock MinorGlobe from the STAT MINORGLOBE
... Or, in other words, every TriggerBlock
will be highlighted with same color...
I don't see stat MINORGLOBE. Maybe it's better to use case sensitive names for everything. The reason they were added as case insensitive is because the compiler doesn't care. I don't mind the extension making a gentle push to better code style, though. Anyway, such a change is trivial, so I'll leave it to whoever wants to do that.
I don't see stat
MINORGLOBE
.
It's STAT#64.
Right, it's the other way around, it's in the stat, but not in triggers.
OK then, if there isn't any obvious issue, I guess this may be closed.
@burner1024
Since we're speaking of
BAF
files, I must confess that I rarely use them – I prefer to code scripts in SSL, it's much more compact and versatile.So, my question is: what do you think about adding support for this format?