Jump to content

[BG2] script help please!


Guest Maderez

Recommended Posts

Guest Maderez

Hi there!

I've been sitting all day trying to figure it out

 

I'm trying to make Aerie to keep shotting hold monster on diffrent monster

BUT

once the first enemy is helpless she goes on trying to kill it instead of switching targets and getting them helpless too.

 

this is my script for now

 

IF

Havespell(WIZARD_HOLD_MONSTER)

!See([Enemy.Undead.0.0])

CheckStatLT(LastSeenBy(Myself),50,RESISTMAGIC)

!StateCheck(LastSeenBy(Myself),STATE_HELPLESS)

 

THEN

RESPONSE #100

spell(LastSeenBy(Myself),WIZARD_HOLD_MONSTER)

END

 

can you help me out? what do I need to edit so she will cast the spell on all diffrent targetz and only then go on with the script?

is there a command to switch targets?

 

 

 

Be glad to get some help please..

Link to comment

That snippet doesn't make sense. First, you have to See(); if you have a more comprehensive targeting block, it's still not great, because your !See() will be false if any enemy undead is visible to her (even if it's not her current LastSeenBy) and will then set and screw up LastSeenBy for the rest of the script. Third, there is no iterating through targets; if the conditions are true, the block runs; otherwise, it's skipped. If you want to iterate through enemies, you need additional blocks for each actor you want to look at. Example:

IF
HaveSpell(WIZARD_HOLD_MONSTER)
See(NearestEnemyOf())
!General(LastSeenBy(),UNDEAD)
CheckStatLT(LastSeenBy(),50,RESISTMAGIC)
!StateCheck(LastSeenBy(),STATE_HELPLESS)
THEN
RESPONSE #100
Spell(LastSeenBy(),WIZARD_HOLD_MONSTER)
END

IF
HaveSpell(WIZARD_HOLD_MONSTER)
See(SecondNearestEnemyOf()) // up to TenthNearest is supported
!General(LastSeenBy(),UNDEAD)
CheckStatLT(LastSeenBy(),50,RESISTMAGIC)
!StateCheck(LastSeenBy(),STATE_HELPLESS)
THEN
RESPONSE #100
Spell(LastSeenBy(),WIZARD_HOLD_MONSTER)
END

and so on. There are ways to separate out targeting, but this is the basic code for what you want to do.

Link to comment
Guest Maderez

Fisrt of all let me get started by saying thank you :]

for the clear explenation together with the script exmple.

 

Is there a source you know from which I'll be able to learn build my scripts better?

 

for exmple, I'm try to defein my script so It won't shot domination on undead and monsters, but will on everything else.

won't shoot hold monster on undead . etc.

Is there a specific way to say "except for"?

Like "all but "CrimsonDeath""

can you give me an exemple please?

 

thank you for your help!

 

 

That snippet doesn't make sense. First, you have to See(); if you have a more comprehensive targeting block, it's still not great, because your !See() will be false if any enemy undead is visible to her (even if it's not her current LastSeenBy) and will then set and screw up LastSeenBy for the rest of the script. Third, there is no iterating through targets; if the conditions are true, the block runs; otherwise, it's skipped. If you want to iterate through enemies, you need additional blocks for each actor you want to look at. Example:
IF
HaveSpell(WIZARD_HOLD_MONSTER)
See(NearestEnemyOf())
!General(LastSeenBy(),UNDEAD)
CheckStatLT(LastSeenBy(),50,RESISTMAGIC)
!StateCheck(LastSeenBy(),STATE_HELPLESS)
THEN
RESPONSE #100
Spell(LastSeenBy(),WIZARD_HOLD_MONSTER)
END

IF
HaveSpell(WIZARD_HOLD_MONSTER)
See(SecondNearestEnemyOf()) // up to TenthNearest is supported
!General(LastSeenBy(),UNDEAD)
CheckStatLT(LastSeenBy(),50,RESISTMAGIC)
!StateCheck(LastSeenBy(),STATE_HELPLESS)
THEN
RESPONSE #100
Spell(LastSeenBy(),WIZARD_HOLD_MONSTER)
END

and so on. There are ways to separate out targeting, but this is the basic code for what you want to do.

Link to comment

There should be some tutorials and useful resources in the How-To board.

 

There's generally no way to exclude specific objects, but you can control with varying specificity what you look at. If you just look for a general object (like with NearestEnemyOf(), which is just any visible living actor with opposing allegiance) and then you check an object parameter (e.g., !General(LastSeenBy(),UNDEAD)), if the check is false (the actor seen *is* a general UNDEAD creature), then the entire block is skipped (it doesn't keep you from seeing undead, just prevents you from doing anything if what you already saw is undead).

 

If you try to narrow down what objects are looked at, then you can keep objects of specific types from being selected. Objects are of the type [EA.GENERAL.RACE.CLASS.SPECIFIC.GENDER.ALIGN] (you can look at the same-named IDS files or at various CRE resources with Near Infinity to see the possible choices for each parameter); if you wanted to pick up only general HUMANOID creatures, you could perform See([0.HUMANOID]), or for enemies of the PC only, See([ENEMY.HUMANOID]). This instructs the game to pick up objects whose general param is HUMANOID (these are defined individually on disk in their CRE files), ignoring any other object with different general (no GIANTHUMANOID or UNDEAD or MONSTER).

 

If you wanted to combine more than one check for an individual parameter (say, you wanted general HUMANOID or GIANTHUMANOID), you could do individual blocks (same as with the NearestEnemyOf() example above) or you can use the OR() trigger. Example:

IF
HaveSpell(WIZARD_FEEBLEMIND)
Or(2)
See([ENEMY.GIANTHUMANOID])
See([ENEMY.HUMANOID])
!StateCheck(LastSeenBy(),STATE_HELPLESS)
THEN
RESPONSE #100
Spell(LastSeenBy(),WIZARD_FEEBLEMIND)
END

In an OR(n) trigger, all n triggers are evaluated top-down. So in the above example, LastSeenBy will either be set to an EA ENEMY General HUMANOID actor (because that was the last thing looked for) or an EA ENEMY General GIANTHUMANOID actor (only if no ENEMY HUMANOID is seen by the next check) or the whole thing will be false (neither type of actor was seen) and the block will be skipped. Note that identifying an object by [] spec will not return a dead actor.

Link to comment

Thank you for the quick respons.

I'll go and check the how to section.

Just one last Q..

 

If I build a script like this

 

IF
HaveSpell(CLERIC_HOLD_PERSON)
OR(8)
See(NearestEnemyOf())
See(SecondNearestEnemyOf())
See(ThirdNearestEnemyOf())
See(FourthNearestEnemyOf())
See(FifthNearestEnemyOf())
See(SixthNearestEnemyOf())
See(SeventhNearestEnemyOf())
See(EighthNearestEnemyOf())
General(LastSeenBy(),HUMANOID)
CheckStatLT(LastSeenBy(Myself),50,RESISTMAGIC)
!StateCheck(LastSeenBy(Myself),STATE_HELPLESS)
THEN
RESPONSE #100
Spell(LastSeenBy(Myself),CLERIC_HOLD_PERSON)
END

 

Is there a way to loop it? so it will just keep on going as long as the trigger can give back true?

 

Also, I've read a little about trigger and global,

can you give me a quick explenation about them and how to use them?

after thos things I think I got it all figured out for what I want

 

 

Thank you very much for all your help!

Link to comment
Is there a way to loop it? so it will just keep on going as long as the trigger can give back true?
No, but if a block is executed, script processing restarts from the top of the first script. So the block will repeatedly run if it's true and nothing else before it has been true.

 

That said, this script is likely not going to do what you want. In that block, the only object that will ever be seen is the eighth nearest enemy (which will be the farthest living enemy visible out to eight enemies). So no matter how many times the block is reached (due to executing and having processing restart), it will only ever pick up a single object until that object dies or until other objects move (which can potentially change who's considered the "eighth nearest"), and if any of the subsequent conditions are false (either General() isn't HUMANOID or RESISTMAGIC >= 50 or the object is held), she won't do anything (it won't switch targets simply because the other conditions are false, so until an enemy dies or they all move around, she's going to be checking against the same object every single time).

 

In this case, you'd be better off with eight separate blocks (with one See() per block) so that up to eight nearby enemies can be targeted (again, as long as any single enemy can be seen, all nthNearestEnemyOf() will at least return that object; think of it as FarthestEnemyNotFartherThanEightEnemies(), for example -- it could be the eighth nearest, but it could also be the nearest enemy if there's only one onscreen).

 

Also, I've read a little about trigger and global,
The Trigger() trigger, if that's what you're referring to, isn't something you would use. It's for rudimentary message-passing between objects; ignore it. (For scripting, a trigger is simply the conditions between IF and THEN in a block, and the actions are the calls in the RESPONSE lists. See() is a trigger (from trigger.ids). Spell() is an action (from action.ids). Etc.)

 

Global sets variables that are saved and can be checked. The scope determines where the variable and saved and from where you can check it. A GLOBAL variable exists for all objects in the game; an area variable exists for the specified area (e.g., AR1000 for the government district or AR0020 for the city gates); a LOCALS variable exists only for the actor on whom it's set. For AI scripting, you'd generally use LOCALS; without knowing what you want to use it for, there's not much to show off about it.

IF
Global("MyVariable","LOCALS",0)
THEN
RESPONSE #100
SetGlobal("MyVariable","LOCALS",1)
END

Any variable which has never been set before is assumed to have the value 0 (so "MyVariable","LOCALS",0 will be true even if you've never before set a "MyVariable" global). The value is preserved until it is modified by script, even through save/load (so it will have the value 1 for that specific object as long as that object exists until you explicitly change it). Timers are similar but are special types of variables that translate to game time (so if you wanted something to only happen once per round, you could add a !GlobalTimerNotExpired("MyTimer","LOCALS") trigger and a SetGlobalTimer("MyTimer","LOCALS",6) action), but note that an un-initialized timer will NOT return true in BG2 (which is why we negate GlobalTimerNotExpired() - this trigger returns true only if a timer exists *and* has not yet expired, so !GlobalTimerNotExpired() returns true if a timer does not exist *or* it has already expired).

 

You should be able to browse most of the AI scripts already in the game with Near Infinity to get a feel for how these are generally used.

Link to comment

sry If I posted in the wrong board but Just came to say thanks!

For all the help! :]

 

 

Is there a way to loop it? so it will just keep on going as long as the trigger can give back true?
No, but if a block is executed, script processing restarts from the top of the first script. So the block will repeatedly run if it's true and nothing else before it has been true.

 

That said, this script is likely not going to do what you want. In that block, the only object that will ever be seen is the eighth nearest enemy (which will be the farthest living enemy visible out to eight enemies). So no matter how many times the block is reached (due to executing and having processing restart), it will only ever pick up a single object until that object dies or until other objects move (which can potentially change who's considered the "eighth nearest"), and if any of the subsequent conditions are false (either General() isn't HUMANOID or RESISTMAGIC >= 50 or the object is held), she won't do anything (it won't switch targets simply because the other conditions are false, so until an enemy dies or they all move around, she's going to be checking against the same object every single time).

 

In this case, you'd be better off with eight separate blocks (with one See() per block) so that up to eight nearby enemies can be targeted (again, as long as any single enemy can be seen, all nthNearestEnemyOf() will at least return that object; think of it as FarthestEnemyNotFartherThanEightEnemies(), for example -- it could be the eighth nearest, but it could also be the nearest enemy if there's only one onscreen).

 

Also, I've read a little about trigger and global,
The Trigger() trigger, if that's what you're referring to, isn't something you would use. It's for rudimentary message-passing between objects; ignore it. (For scripting, a trigger is simply the conditions between IF and THEN in a block, and the actions are the calls in the RESPONSE lists. See() is a trigger (from trigger.ids). Spell() is an action (from action.ids). Etc.)

 

Global sets variables that are saved and can be checked. The scope determines where the variable and saved and from where you can check it. A GLOBAL variable exists for all objects in the game; an area variable exists for the specified area (e.g., AR1000 for the government district or AR0020 for the city gates); a LOCALS variable exists only for the actor on whom it's set. For AI scripting, you'd generally use LOCALS; without knowing what you want to use it for, there's not much to show off about it.

IF
Global("MyVariable","LOCALS",0)
THEN
RESPONSE #100
SetGlobal("MyVariable","LOCALS",1)
END

Any variable which has never been set before is assumed to have the value 0 (so "MyVariable","LOCALS",0 will be true even if you've never before set a "MyVariable" global). The value is preserved until it is modified by script, even through save/load (so it will have the value 1 for that specific object as long as that object exists until you explicitly change it). Timers are similar but are special types of variables that translate to game time (so if you wanted something to only happen once per round, you could add a !GlobalTimerNotExpired("MyTimer","LOCALS") trigger and a SetGlobalTimer("MyTimer","LOCALS",6) action), but note that an un-initialized timer will NOT return true in BG2 (which is why we negate GlobalTimerNotExpired() - this trigger returns true only if a timer exists *and* has not yet expired, so !GlobalTimerNotExpired() returns true if a timer does not exist *or* it has already expired).

 

You should be able to browse most of the AI scripts already in the game with Near Infinity to get a feel for how these are generally used.

Link to comment

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...