Jump to content

Proofreading the eSeries


Recommended Posts

Package sent. I currently use GBMAGE and GBSMAGE in BP, for summoned allies. I did a light reworking into a script called BPALLY. This is used for non-party allies, like the chapter6 graveyard teams. Perhaps it is time to rework this script as well, & meld the best of all options together with a few Plaguezone specialties.

 

Was the uScript the one that brought a CPU to a screeching halt, or am I confusing it with "my old pal's" UberScript? Or, was it both of them? :down:

Link to comment

Hey, I just had a thought. You said that Attack() was the only way to get all of your attacks per round.

 

The only quick way I've found to break this mode, like others such as MoveToObject, is with a ClearActions(Myself)/Continue() block. It will break other ways, besides killing your foe--but sluggish is being nice.

 

I can somewhat understand why you could lose some on AttackOneRound. That one-second break might indeed break your rhythm.

 

But I had a thought--perhaps it is worth testing at least. I'm taking a wild guess that most tests on AttackReevaluate were more along the lines of:

 

AttackReevaluate(LastSeenBy(Myself),15) // a split second

 

rather than

 

AttackReevaluate(LastSeenBy(Myself),900) // a full turn

 

You would set your interruptable timer to the same 60 seconds, so at any point throughout the timer it could ring true, and cancel it out. You put the opposite check on the attack blocks, so they don't keep triggering and "re-attacking". Similar to what I have already set up in my draft of the eSeries, just switch the AttackOneRound() line.

 

Just put the action SetGlobal("MyTimer","LOCALS",0) in the bottom of each block. So that after your deviant action (cast a spell, use an tem, etc)--you are able to go right back to the attack blocks if it deems necessary.

 

Now, what if I take the !GlobalTimerNotExpired checks off of the weapon switching blocks...and add the same SetGlobal at the bottom? Were your people switching weapons decently in the old system, using Attack()?

 

It would look something like this:

 

// **********************************************************************
// *           Segment Name: gs_SelectRangedThenMelee_ItemList.baf
// **********************************************************************

// * Notes:  TargetUnreachable seems to happen more often that one might think.  I believe if
// *         the character cannot reach LastSeenBy(), such as in the case of when they are 
// *         surrounded by other NPCs, then that counts as TargetUnreachable()

// * SELECT RANGED_WEAPON, if you have one in a quickslot


IF
 OR(2)
   Global("hlid_UseRangedWeapon","LOCALS",1)                             // or the global, hlid_UseRangedWeapon, is set to 1
   Global("gh_MeleeMissileToggle","LOCALS",0)                               // 1=Fire a Missile, we checked clean against them last block
 !HasItemSlot(Myself,SLOT_SHIELD)                                         //engine limitation--cannot EquipRanged() with shield equipped
 !Range(LastSeenBy(Myself),4)                                             // range is >4 steps away, therefore ranged
 !InWeaponRange(LastSeenBy(Myself))                                       // and my current wepaon won't reach there
 !InParty(LastSeenBy(Myself))                                             // and they're not in our party (double-check)
 !CombatCounter(0)                                                        // and combat is *not* over
 THEN
 RESPONSE #100
   SetGlobal("gh_MeleeMissileToggle","LOCALS",1)                          // 1=Missile
   SetGlobal("gh_ActionIsInterruptable","LOCALS",0)                       // Turns off the timer, and allows us to recommence attack
   EquipRanged()                                                          // Equip ranged weapon
   SmallWait(3)                                                           //a small time bubble (2/10 second), hopefully enough to process the Globals
   Continue()                                                             // and Continue to ranged weapons check block
END


// * SELECT MELEE ATTACK
IF
 OR(2)
   Global("hlid_UseRangedWeapon","LOCALS",0)                             // or the global, hlid_UseRangedWeapon, is set to 1
   Range(LastSeenBy(Myself),4)                                              // range is closer than 4 steps, therefore melee
 !InParty(LastSeenBy(Myself))                                             // and they're not in our party (double-check)
 !Class(Myself,MONK)                                                      // and we're not a monk
THEN
 RESPONSE #100
   SetGlobal("gh_MeleeMissileToggle","LOCALS",0)                          // 0=Melee
   SetGlobal("gh_ActionIsInterruptable","LOCALS",0)                       // Turns off the timer, and allows us to recommence attack
   EquipMostDamagingMelee()                                               // equip nasty Weapon
   SmallWait(3)
   Continue()                                                             // and Continue() to Attack() routine
END

IF
 Global("gh_MeleeMissileToggle","LOCALS",1)                               // 1=Fire a Missile, we checked clean against them last block
 Range(LastSeenBy(Myself),20)                                             //& we can still see them, and within dart range (minimum ranged weapon reach)
 !Range(LastSeenBy(Myself),4)                                             // range is >4 steps away, definitely for ranged attack
 OR(2)
   Global("hlid_UseRangedWeapon","LOCALS",0)                             // either the global, hlid_UseRangedWeapon, is set to 0
   !InWeaponRange(LastSeenBy(Myself))                                       //or they're not in range = we have no ranged weapon
 !CombatCounter(0)                                                        // and combat is *not* over
 THEN
 RESPONSE #100
   SetGlobal("gh_MeleeMissileToggle","LOCALS",0)                          // 1=Missile
   SetGlobal("gh_ActionIsInterruptable","LOCALS",0)                       // Turns off the timer, and allows us to recommence attack
   EquipMostDamagingMelee()                                               // Equip melee weapon instead                                                      // "Missile Weapons"
   SmallWait(3)
   Continue()                                                             // and Continue to Attack() routine
END

IF
 !Range(LastSeenBy(Myself),20)                                             //we're outside of dart range
 !InWeaponRange(LastSeenBy(Myself))                                        //and our weapon isn't "long" enough
 See(NearestEnemyOf(Myself))                                               //and we can see our nearest opponent
THEN
 RESPONSE #100
   Continue()                                                              //take whatever is in hand, and head to the attack block
END

// **********************************************************************
// *           Segment Name: gs_Attack_Stand_Ground.baf
// **********************************************************************

// * ATTACK CLAUSE

// * "Force" Reevaluation Clause of Attack()
// * ----------------------------------------
// * Thanks to Gebhard Blucher & Jochem for the source and inspiration for these bits :)

// * Attack if RangedWeapon Equipped *OR* At Melee Range
IF
 ActionListEmpty()                                                         // not doing a thing...
 !GlobalTimerNotExpired("gh_ActionIsInterruptable","LOCALS")               // if we're still on our timer, & attacking--no need to add more time
 !Allegiance(LastSeenBy(Myself),GOODCUTOFF)                               // and they're not in our party (double-check)
 Detect([EVILCUTOFF])
THEN
 RESPONSE #100
   SetGlobalTimer("gh_ActionIsInterruptable","LOCALS",60)                  // set a 60--sec timer to indicate action is interruptable
   AttackReevaluate(LastSeenBy(Myself),900)                                             // AttackReevaluate()--if time expires, we reevaluate. Most likely, one of the blocks above will stop combatr
END

// * Minimum Catch-All Attack
// * This is invoked if targetting produced no valid targets, and thus the LastSeenBy() is empty
// * but Combat is still going. This is usually when there are only targets left that
// * qualify as "helpless". I'm not so bothered about losing the attacks here, so I continue
// * to use AttackOneRound()

// * MINIMUM ATTACK
IF
 ActionListEmpty()
 !GlobalTimerNotExpired("gh_ActionIsInterruptable","LOCALS")              // if we're still on our timer, & attacking a target--we shouldn't need this
 See(NearestEnemyOf(Myself))                                              // but we can still see AN enemy
 !InParty(LastSeenBy(Myself))                                             // and they're not in our party (double-check)
 !Allegiance(LastSeenBy(Myself),GOODCUTOFF)                               // and not an Ally, Familiar, Minion or Simulacrum
 OR(2)
    Range(LastSeenBy(Myself),6)                                           // and they're at <6 Range
    Global("gh_MeleeMissileToggle","LOCALS",1)                            // or we've a Missile Weapon Equipped
THEN
 RESPONSE #100
   SetGlobalTimer("gh_ActionIsInterruptable","LOCALS",6)                  // set a 6-sec timer to indicate action is interruptable
   AttackOneRound(NearestEnemyOf(Myself))                                 // and attack
END

 

To help things along, at the top of the script we could put a block like:

 

IF
 GlobalTimerExpired("gh_ActionIsInterruptable","LOCALS")
THEN
 RESPONSE #100
   ClearActions(Myself)
   Continue()
END

 

This does a twofold deed. It

1) allows a fresh reset of the ActionListEmpty() trigger, giving this key block (on most spells) a fair chance:

  OR(2)
    ActionListEmpty() 
    GlobalTimerNotExpired("gh_ActionIsInterruptable","LOCALS")

2) It makes a clear break from the (reported buggy) AttackReevaluate. The problem never was with the Attack part, but with the Reevaluate part :devil:

By doing this, we should do its job for it, and hopefully "beat the bug".

 

 

I'm going to test this out in the BPMulti script, at least. I'll let you know how it works out. :down: Any comments/suggestions/warnings you/others may have in the meantime (before I make 1001 line additions!) would be helpful.

 

Thanks,

 

Horred

Link to comment
Hey, I just had a thought. You said that Attack() was the only way to get all of your attacks per round.

 

The only quick way I've found to break this mode, like others such as MoveToObject, is with a ClearActions(Myself)/Continue() block. It will break other ways, besides killing your foe--but sluggish is being nice.

 

I can somewhat understand why you could lose some on AttackOneRound. That one-second break might indeed break your rhythm.

 

But I had a thought--perhaps it is worth testing at least. I'm taking a wild guess that most tests on AttackReevaluate were more along the lines of:

 

AttackReevaluate(LastSeenBy(Myself),15) // a split second

 

rather than

 

AttackReevaluate(LastSeenBy(Myself),900) // a full turn

 

You would set your interruptable timer to the same 60 seconds, so at any point throughout the timer it could ring true, and cancel it out. You put the opposite check on the attack blocks, so they don't keep triggering and "re-attacking". Similar to what I have already set up in my draft of the eSeries, just switch the AttackOneRound() line.

 

Just put the action SetGlobal("MyTimer","LOCALS",0) in the bottom of each block. So that after your deviant action (cast a spell, use an tem, etc)--you are able to go right back to the attack blocks if it deems necessary.

 

Now, what if I take the !GlobalTimerNotExpired checks off of the weapon switching blocks...and add the same SetGlobal at the bottom? Were your people switching weapons decently in the old system, using Attack()?

 

It would look something like this:

 

// **********************************************************************
// *           Segment Name: gs_SelectRangedThenMelee_ItemList.baf
// **********************************************************************

// * Notes:  TargetUnreachable seems to happen more often that one might think.  I believe if
// *         the character cannot reach LastSeenBy(), such as in the case of when they are 
// *         surrounded by other NPCs, then that counts as TargetUnreachable()

// * SELECT RANGED_WEAPON, if you have one in a quickslot


IF
 OR(2)
   Global("hlid_UseRangedWeapon","LOCALS",1)                             // or the global, hlid_UseRangedWeapon, is set to 1
   Global("gh_MeleeMissileToggle","LOCALS",0)                               // 1=Fire a Missile, we checked clean against them last block
 !HasItemSlot(Myself,SLOT_SHIELD)                                         //engine limitation--cannot EquipRanged() with shield equipped
 !Range(LastSeenBy(Myself),4)                                             // range is >4 steps away, therefore ranged
 !InWeaponRange(LastSeenBy(Myself))                                       // and my current wepaon won't reach there
 !InParty(LastSeenBy(Myself))                                             // and they're not in our party (double-check)
 !CombatCounter(0)                                                        // and combat is *not* over
 THEN
 RESPONSE #100
   SetGlobal("gh_MeleeMissileToggle","LOCALS",1)                          // 1=Missile
   SetGlobal("gh_ActionIsInterruptable","LOCALS",0)                       // Turns off the timer, and allows us to recommence attack
   EquipRanged()                                                          // Equip ranged weapon
   SmallWait(3)                                                           //a small time bubble (2/10 second), hopefully enough to process the Globals
   Continue()                                                             // and Continue to ranged weapons check block
END


// * SELECT MELEE ATTACK
IF
 OR(2)
   Global("hlid_UseRangedWeapon","LOCALS",0)                             // or the global, hlid_UseRangedWeapon, is set to 1
   Range(LastSeenBy(Myself),4)                                              // range is closer than 4 steps, therefore melee
 !InParty(LastSeenBy(Myself))                                             // and they're not in our party (double-check)
 !Class(Myself,MONK)                                                      // and we're not a monk
THEN
 RESPONSE #100
   SetGlobal("gh_MeleeMissileToggle","LOCALS",0)                          // 0=Melee
   SetGlobal("gh_ActionIsInterruptable","LOCALS",0)                       // Turns off the timer, and allows us to recommence attack
   EquipMostDamagingMelee()                                               // equip nasty Weapon
   SmallWait(3)
   Continue()                                                             // and Continue() to Attack() routine
END

IF
 Global("gh_MeleeMissileToggle","LOCALS",1)                               // 1=Fire a Missile, we checked clean against them last block
 Range(LastSeenBy(Myself),20)                                             //& we can still see them, and within dart range (minimum ranged weapon reach)
 !Range(LastSeenBy(Myself),4)                                             // range is >4 steps away, definitely for ranged attack
 OR(2)
   Global("hlid_UseRangedWeapon","LOCALS",0)                             // either the global, hlid_UseRangedWeapon, is set to 0
   !InWeaponRange(LastSeenBy(Myself))                                       //or they're not in range = we have no ranged weapon
 !CombatCounter(0)                                                        // and combat is *not* over
 THEN
 RESPONSE #100
   SetGlobal("gh_MeleeMissileToggle","LOCALS",0)                          // 1=Missile
   SetGlobal("gh_ActionIsInterruptable","LOCALS",0)                       // Turns off the timer, and allows us to recommence attack
   EquipMostDamagingMelee()                                               // Equip melee weapon instead                                                      // "Missile Weapons"
   SmallWait(3)
   Continue()                                                             // and Continue to Attack() routine
END

IF
 !Range(LastSeenBy(Myself),20)                                             //we're outside of dart range
 !InWeaponRange(LastSeenBy(Myself))                                        //and our weapon isn't "long" enough
 See(NearestEnemyOf(Myself))                                               //and we can see our nearest opponent
THEN
 RESPONSE #100
   Continue()                                                              //take whatever is in hand, and head to the attack block
END

// **********************************************************************
// *           Segment Name: gs_Attack_Stand_Ground.baf
// **********************************************************************

// * ATTACK CLAUSE

// * "Force" Reevaluation Clause of Attack()
// * ----------------------------------------
// * Thanks to Gebhard Blucher & Jochem for the source and inspiration for these bits :)

// * Attack if RangedWeapon Equipped *OR* At Melee Range
IF
 ActionListEmpty()                                                         // not doing a thing...
 !GlobalTimerNotExpired("gh_ActionIsInterruptable","LOCALS")               // if we're still on our timer, & attacking--no need to add more time
 !Allegiance(LastSeenBy(Myself),GOODCUTOFF)                               // and they're not in our party (double-check)
 Detect([EVILCUTOFF])
THEN
 RESPONSE #100
   SetGlobalTimer("gh_ActionIsInterruptable","LOCALS",60)                  // set a 60--sec timer to indicate action is interruptable
   AttackReevaluate(LastSeenBy(Myself),900)                                             // AttackReevaluate()--if time expires, we reevaluate. Most likely, one of the blocks above will stop combatr
END

// * Minimum Catch-All Attack
// * This is invoked if targetting produced no valid targets, and thus the LastSeenBy() is empty
// * but Combat is still going. This is usually when there are only targets left that
// * qualify as "helpless". I'm not so bothered about losing the attacks here, so I continue
// * to use AttackOneRound()

// * MINIMUM ATTACK
IF
 ActionListEmpty()
 !GlobalTimerNotExpired("gh_ActionIsInterruptable","LOCALS")              // if we're still on our timer, & attacking a target--we shouldn't need this
 See(NearestEnemyOf(Myself))                                              // but we can still see AN enemy
 !InParty(LastSeenBy(Myself))                                             // and they're not in our party (double-check)
 !Allegiance(LastSeenBy(Myself),GOODCUTOFF)                               // and not an Ally, Familiar, Minion or Simulacrum
 OR(2)
    Range(LastSeenBy(Myself),6)                                           // and they're at <6 Range
    Global("gh_MeleeMissileToggle","LOCALS",1)                            // or we've a Missile Weapon Equipped
THEN
 RESPONSE #100
   SetGlobalTimer("gh_ActionIsInterruptable","LOCALS",6)                  // set a 6-sec timer to indicate action is interruptable
   AttackOneRound(NearestEnemyOf(Myself))                                 // and attack
END

 

To help things along, at the top of the script we could put a block like:

 

IF
 GlobalTimerExpired("gh_ActionIsInterruptable","LOCALS")
THEN
 RESPONSE #100
   ClearActions(Myself)
   Continue()
END

 

This does a twofold deed. It

1) allows a fresh reset of the ActionListEmpty() trigger, giving this key block (on most spells) a fair chance:

  OR(2)
    ActionListEmpty() 
    GlobalTimerNotExpired("gh_ActionIsInterruptable","LOCALS")

2) It makes a clear break from the (reported buggy) AttackReevaluate. The problem never was with the Attack part, but with the Reevaluate part  :devil:

By doing this, we should do its job for it, and hopefully "beat the bug".

 

 

I'm going to test this out in the BPMulti script, at least. I'll let you know how it works out. :down: Any comments/suggestions/warnings you/others may have in the meantime (before I make 1001 line additions!) would be helpful.

 

Thanks,

 

Horred

 

No concerns that I can think of Horred. Give it a whirl and let us know how it turns out.

 

Thanks,

Cirerrek

Link to comment

*meekly raises hand*

 

If you're interested in performance, you may want to experiement with shortening variable names. Back when I was frequenting the Interplay boards, asking questions (and getting answers) about IWD2 scripting, I vaguely remember Briereus (BI scripter) commenting that string parsing was something that the script engine did poorly, and long variable names caused a performance hit.

 

Then again, I may be totally off in the woods again, looking for memories that have long since faded away, and without the IPLAY boards, I have no way to confirm the level of sanity I still possess.

 

My latest binge into player scripting was when I finally sat down and played through BGT a few months ago. The difficulty with scripting a game where your level will vary from 1-20+ (I couldn't get into ToB) seemed to be that your spell selections usually vary greatly by player level.

 

As a simple example, at low levels Sleep is an awesome spell, but it's very unlikely that a high-level mage is going to bother carrying it late in the game: leaving "spell residue" in the script that does nothing but waste AI cycles. Similarly, having Time Stop scripted for low level characters is a waste of time, as they aren't going to be capable of casting it.

 

As a result, I started on a ChangeAIScript() user script that would then select a script after sleeping (or relevant hotkey) based on character level, so that I could customize what spells were scripted for the PC based on the PC's level.

 

I was reasonably happy with the potential, using AI Script Sorter to parse things together, but I broke down with laziness at some point. My current plan at some point is to use snips, but put a comment in the script file denoting appropriate spell level for using this snippet, and a little command line utility to take a snip sorter file and generate a group of more restricted ones by level.

 

It would be a way to help manage the size (and therefore performance) of AI scripts with some careful planning. However, whether the complexity of thinking about when this spell would be most appropriate might not be the best approach.

Link to comment
*meekly raises hand*

 

If you're interested in performance, you may want to experiement with shortening variable names.  Back when I was frequenting the Interplay boards, asking questions (and getting answers) about IWD2 scripting, I vaguely remember Briereus (BI scripter) commenting that string parsing was something that the script engine did poorly, and long variable names caused a performance hit.

 

Then again, I may be totally off in the woods again, looking for memories that have long since faded away, and without the IPLAY boards, I have no way to confirm the level of sanity I still possess.

 

My latest binge into player scripting was when I finally sat down and played through BGT a few months ago.  The difficulty with scripting a game where your level will vary from 1-20+ (I couldn't get into ToB) seemed to be that your spell selections usually vary greatly by player level.

 

As a simple example, at low levels Sleep is an awesome spell, but it's very unlikely that a high-level mage is going to bother carrying it late in the game: leaving "spell residue" in the script that does nothing but waste AI cycles.  Similarly, having Time Stop scripted for low level characters is a waste of time, as they aren't going to be capable of casting it.

 

As a result, I started on a ChangeAIScript() user script that would then select a script after sleeping (or relevant hotkey) based on character level, so that I could customize what spells were scripted for the PC based on the PC's level. 

 

I was reasonably happy with the potential, using AI Script Sorter to parse things together, but I broke down with laziness at some point.  My current plan at some point is to use snips, but put a comment in the script file denoting appropriate spell level for using this snippet, and a little command line utility to take a snip sorter file and generate a group of more restricted ones by level.

 

It would be a way to help manage the size (and therefore performance) of AI scripts with some careful planning.  However, whether the complexity of thinking about when this spell would be most appropriate might not be the best approach.

 

http://www.gibberlings3.net/tools/iwd2_scripting_info.txt

 

Something tells me that you might recognize a lot of this information ;)

 

Cheers,

Cirerrek

Link to comment
http://www.gibberlings3.net/tools/iwd2_scripting_info.txt

 

Something tells me that you might recognize a lot of this information ;)

 

Cheers,

Cirerrek

Ah, good to know the detail wasn't lost. Bri was very helpful on the boards, and I took as much advantage of it as I could.

 

IWD2 had a maddeningly powerful (and buggy) scripting language. On the plus side, I got some things to work I never could in any of the other games (most notably weapon switching, so the characters would use blunts against slashing-resistant opponents, for example ). Plus, I preferred the 3E approach used by IWD2 to combat over the BG2 approach.

 

But, since the rest of the world seems to be BG2-centric ( and because of the lack of NPCs in IWD2, I can fully understand why ), I've been wandering back and forth between the two whenever I get back in the mood for scripting.

 

Lots of good information on the boards. Still debating what/how I can do to help out.

Link to comment
http://www.gibberlings3.net/tools/iwd2_scripting_info.txt

 

Something tells me that you might recognize a lot of this information ;)

 

Cheers,

Cirerrek

Ah, good to know the detail wasn't lost. Bri was very helpful on the boards, and I took as much advantage of it as I could.

 

IWD2 had a maddeningly powerful (and buggy) scripting language. On the plus side, I got some things to work I never could in any of the other games (most notably weapon switching, so the characters would use blunts against slashing-resistant opponents, for example ). Plus, I preferred the 3E approach used by IWD2 to combat over the BG2 approach.

 

But, since the rest of the world seems to be BG2-centric ( and because of the lack of NPCs in IWD2, I can fully understand why ), I've been wandering back and forth between the two whenever I get back in the mood for scripting.

 

Lots of good information on the boards. Still debating what/how I can do to help out.

 

So many things didn't work with the IWD2 scripting engine that I finally gave up trying. My iSeries port of the eSeries is as far as I got. I think Yovaneth took his port of the eSeries to IWD2 a little farther, but I think he finally gave up as well.

 

One of the maddening things for me was that all the links to your script snippets were dead by the time I got around to playing around with IWD2. ;)

 

Hopefully if we can add a stat for damage time and enchantment to weapons, then we can script intelligent weapon scripting as well. So, if you've already got a benchmark for that, that would be useful to see.

 

Cheers,

Cirerrek

Link to comment
Hopefully if we can add a stat for damage time and enchantment to weapons, then we can script intelligent weapon scripting as well.  So, if you've already got a benchmark for that, that would be useful to see.
I'm a little confused. are you referring here to BG2 or IWD2? And if BG2, how would I go about learning how you might add stats to weapons? Sorry if I'm thread-hijacking here.

 

I'm a pretty good programmer (career) and so-so at AI scripting, but I really haven't jumped in to learning the entire IE engine yet. Obviously, I've picked up pieces along the way as needed, but there's a lot I just don't know.

Link to comment
I'm a little confused. are you referring here to BG2 or IWD2?  And if BG2, how would I go about learning how you might add stats to weapons?  Sorry if I'm thread-hijacking here.

 

I'm a pretty good programmer (career) and so-so at AI scripting, but I really haven't jumped in to learning the entire IE engine yet.  Obviously, I've picked up pieces along the way as needed, but there's a lot I just don't know.

 

Sorry, I was referring to BG2.

 

You could assign a stat as an equipped effect for either enchantment or damage type.

 

For example

 

SCRIPTINGSTATEX = WEAPON_ENCHANTMENT

 

0 = normal weapon

1 = +1

2 = +2

3 = +3

4 = +4

5 = +5

6 = +6

 

Hmmm, you might have to break these into two

 

SCRIPTINGSTATEXX = PHYSICAL_DAMAGE_TYPE

 

0 = SLASHING

1 = PIERCING

2 = CRUSHING

etc..

 

SCRIPTINGSTATEXXX = ELEMENTAL_DAMAGE_TYPE

 

0 = none

1 = FIRE

2 = ACID

3 = ELECTRICITY

etc.,.

 

Then do something like (forgive the triggers and such since I'm doing this from memory)

 

IF

See([EVILCUTOFF.TROLL])

Or(2)

CheckStat(Myself,1,ELEMENTAL_DAMAGE_TYPE)

CheckStat(Myself,2,ELEMENTAL_DAMAGE_TYPE)

THEN

RESPONSE #100

Attack(LastSeenBy(Myself))

END

Link to comment
So many things didn't work with the IWD2 scripting engine that I finally gave up trying. My iSeries port of the eSeries is as far as I got. I think Yovaneth took his port of the eSeries to IWD2 a little farther, but I think he finally gave up as well.

 

One of the maddening things for me was that all the links to your script snippets were dead by the time I got around to playing around with IWD2. :blush:

 

Hopefully if we can add a stat for damage time and enchantment to weapons, then we can script intelligent weapon scripting as well. So, if you've already got a benchmark for that, that would be useful to see.

 

Cheers,

Cirerrek

Sorry for the (rather delayed) response, I don't know how I missed it. In digging around some really old files this week I ran into some of my old IWD2 scripts, and AISF (which I need to convert BIX to BAF). It got the scripting juices flowing again, and I'm off to try to get the scripts to run and make improvements (as I keep learning new things). And to write some modules so I can parse things in Python.... :hm:

I will warn you that I found the following restrictions in weapon selection (IIRC):

  • I couldn't activate a given slot in IWD2 (I think it was broken), so you have to weapon switch by switching out a single weapon slot and leaving the other three empty ( so that SelectWeapon() works ).
  • Switching out the off-hand weapon was problematic for some reason I cannot remember. IIRC, you can actually do really bad things if you're not careful (something like equipping an off-hand weapon while a two-handed weapon was equipped in the primary slot would crash the game or something ).

Once I have things working again and tested, I'll see if I can post an update on how that stuff works and possibly even some scripts.

Link to comment

Looking over this, I have a couple things to mention:

 

FillSlot() seems to fill the slot with every possible candidate in the inventory, in order; only moving the item origi9nally in the slot back to inventory. So, if you have 5 rings, and are wearing one in the slot you chose, then you would destroy 4 rings, remove the one you had on, and have the one on that was lowest in the inventory. So, to safely FillSlot(), you'd have to have a dropItem() drop every other candidate item for the slot you want to fill but the item you want to put in.

 

I am not sure the engine uses pre-emptive breaks; while it is a common tactic for Optimization, i'd have to test it to see if it actually made a difference in IE. But, it can't hurt, and might be implemented.

 

The Variable names for LOCALS are broken into 3 8-byte groups; so if you only have the first 8 bytes, you will save time. This will increase speed on slow computers, but I don't know how much.

 

Use of locals for blocks of common tests might be a good tactic, something like:

IF
True()
THEN
RESPONSE #100
	SetGlobal("WxCastOK","LOCALS",0)
	Continue()
END

IF
CheckStatLT(Myself,35,SPELLFAILUREMAGE)
// negative any other reasons you might not want to cast, like invisibility
THEN
RESPONSE #100
	SetGlobal("WxCastOK","LOCALS",1)
	Continue()
END

 

And then the script can decide a fireball is a good idea, and have 2 blocks, one which would cast it from memory, the second if WxCastOK is 0 or the mage has no fireball memorized, to use a wand.

 

Likewise, one could have a single block to decide if each spell was a good idea, and then to set a variable to "try it" - then go through the different ways one could actually accomplish casting that, for instance, a fireball would be done with Fireball, a wnd of fire, a necklace of missiles, or a scroll of fireball... and you could do each of those with a block something like:

/// condition for a fireball...
IF
See(TenthNearestEnemyOf(Myself))
!Range(NearestEnemyOf(LastSeenBy(Myself)),15)
THEN
 RESPONSE #100
	SetGlobal("WxSpell","LOCALS",1)
	Continue()
END

IF
Local("WxSpell",1)
HaveSpell(WIZARD_FIREBALL)
Local("WxCastOK",1)
THEN
RESPONSE #100
	Spell(LastSeenBy(Myself),WIZARD_FIREBALL)
END

IF
  Local("WxSpell",1)
  HaveItemEquipped("WAND05")  //Wand of Fire
THEN
RESPONSE #100
	UseItem("Wand05",LastSeenBy(Myself))
END

IF
Local("WxSpell",1)
THEN
RESPONSE #100
	SetGlobal("WxSpell","LOCALS",0)  // wanted to fireball, but couldn't
	Continue()  // so try something else
END

the beauty of this is you don't have to have a separate variable for each spell, you can just go through the script, spell effect by spell effect.

Link to comment

Archived

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

×
×
  • Create New...