Jump to content

ToB script stuff


devSin

Recommended Posts

Actions:

 

102/103/104/221/222 - Morale actions don't appear to do anything. It's been a long time so I'd double-check, but I'm pretty sure they're broken.

 

112 UseContainer() - tested, non-functional. I even tried inventory containers (bag of holding); no effect.

 

130 RandomTurn() - this could be clarified that it cause the NPC to Face() random directions. Can be useful if you don't want RandomWalk(), but the updated ready animations in BG2 make PC avatars using RandomTurn() look a little crazy paranoid.

 

137 StartDialogue() - should be tested further, but it appears to actually set the dialogue as well (for at least the current session). ActionOverride("Imoen",StartDialogue("whatever",Player1)) will cause interjections to IMOENJ (which should work) to fail.

 

152 ChangeAIType() - amazing, worthless, dangerous. This appears to cat all the Change*() actions into one action. If you pass a script name (or other quoted string), nothing will happen. If you pass a compound object, it will completely set all object parameters (e.g., ChangeAIType([1.2.3.4.5.6.7]) will set the expected fields to the expected value, permanently). All oIDs are required (if you leave any off, they will be set to 0; on a PC, you can then expect errors in CRuleTables.cpp). If you pass a symbolic object, it will clear all identifiers, and fill out the 5 fields below (labeled "Functional spec. n" in NI). So, ChangeAIType(NearestEnemyOf(LastSeenBy(LastTalkedToBy(LastTrigger())))) would zero allegiance, general, race, class, specific, gender, and alignment, set spec 1 to "Myself" (the Object.ids value, 1), 2 to LastTrigger, 3 to LastTalkedToBy, 4 to LastSeenBy, and 5 to NearestEnemyOf. I have no idea how this could ever be useful?

 

Something I want add: The triggers used in the game do not ever exceed 31. So I believe that is the limit for them. Triggers are the conditons that determine if the script actions occur, like InParty() or HPPercentLT.
Say what? Where did this come from?

 

191 SpellNoDec() - It does not require a spell to be known or memorized, and allows for casting failure. It's basically ForceSpell(), without the cheese.

 

219 ChangeAnimationNoEffect() - I'm pretty close to positive that the desc is incorrect. The only difference from ChangeAnimation() is that the normal lighting effects used when swapping the CREs aren't played (this still completely replaces the calling CRE).

 

316 SoundActivate() - I'm not sure why this is marked N/T. It just sets bit 0 for the specified ambient sound struct (by name) to on (1) or off (0).

 

324 SetBeenInPartyFlags() - Despite the plural, this just sets the Been in party flag for the specified object. I suspect the game uses this to determine whether an NPC should be dynamically chosen (from the NPC level 2DAs) or if the attached CRE should be used. Aerie uses this because her CREs have her look like an ogre with no portrait; without this, the game would probably try to use the 2DA and you'd get no-portrait-Aeogrie.

 

330 AddAreaType() - It might be worth mentioning that this value is ORd. 9 would set both bit 0 and bit 3.

 

 

Triggers:

 

AttackedBy() - It should be noted that this trigger doesn't set LastAttackerOf. AttackedBy() returns true for offensive attacks (physical, anything with damage opcode, or spells marked hostile), but only physical attacks set LastAttackerOf. Spell attacks (whether the damage opcode or the flag) will never set LastAttackerOf. Hence, the following isn't always valid.

IF
AttackedBy([30],0)
THEN
RESPONSE #100
AttackOneRound(LastAttakerOf()) // either last physical attacker or null if hit by spell
END

This trigger does set LastTrigger, so substituting LastTrigger() for LastAttackerOf() in the above should always do what you expect.

 

BreakingPoint() - This has nothing to do with morale. Like the soundset, this only tracks happiness. Cleverly, this trigger will only return true when the happiness is checked (you know it's true when the PC runs their breaking point sound). It is false at all other times, even if the happiness is UNHAPPY_BREAKING_POINT (it looks like it's checked fairly often, so it'll eventually return true if happiness is that low).

There is no easy way to script for morale failure. I used a combination of MoraleLT(Myself,MoraleBreakDefaultValue + 1) and CheckStatGT(Myself,MoraleBreakDefaultValue - 1,MORALEBREAK) to hopefully do something useful once during the game. Unlike magic panic, however, morale failure completely blocks a creature's script.

 

NumCreatureVsParty*() - These triggers don't work. They return true sometimes, but definitely not in any sensible fashion, and it doesn't appear to have anything to do with the number of creatures of type that are around.

 

AreaType() - This trigger is ORd. Assuming knowledge of the logic used, multiple calls can be combined (just like StateCheck()).

I was hoping that NotStateCheck() would be a way of combining ANDd values, but it's identical to !StateCheck. I guess Mr. D was too busy with the phoenix warriors.

 

HaveSpellParty() - works fine. HaveSpellPartyRES() does not work. Sucks.

Link to comment
Something I want add: The triggers used in the game do not ever exceed 31. So I believe that is the limit for them. Triggers are the conditons that determine if the script actions occur, like InParty() or HPPercentLT.
Say what? Where did this come from?

I think this is one of those things that somebody spread as a rumour at TeamBG and everyone adopted as solid theory. Most of IESDP is constructed this way. It's total nonsense--I have yet to encounter a trigger limit. (Seriously, how long does it take to test?)

Link to comment
I think this is one of those things that somebody spread as a rumour at TeamBG and everyone adopted as solid theory.

 

I'm pretty sure that was put in by Max, or even Vaskez.

 

(Seriously, how long does it take to test?)

 

Yes, this one proposition would not take long to test (assuming you know it's there to test). So, why the f*ck didn't you do it years ago Sim, and then tell whoever was maintaing the IESDP to update the text, rather than just f*cking complaining and whining about it all the f*cking time, eh?

 

 

Back on topic, thanks for the notes devsin!

Link to comment
I think this is one of those things that somebody spread as a rumour at TeamBG and everyone adopted as solid theory. Most of IESDP is constructed this way. It's total nonsense--I have yet to encounter a trigger limit. (Seriously, how long does it take to test?)
I know there's not that small a limit for action lists. I had a spell stress-test script that cast every priest and wizard spell in the game in 3 blocks (I called it my "superscript," for historical reasons).
Link to comment
I think this is one of those things that somebody spread as a rumour at TeamBG and everyone adopted as solid theory.

 

I'm pretty sure that was put in by Max, or even Vaskez.

 

(Seriously, how long does it take to test?)

 

Yes, this one proposition would not take long to test (assuming you know it's there to test). So, why the f*ck didn't you do it years ago Sim, and then tell whoever was maintaing the IESDP to update the text, rather than just f*cking complaining and whining about it all the f*cking time, eh?

 

 

Back on topic, thanks for the notes devsin!

 

If you're going to cuss, just cuss. Implied cussing is l*me.

Link to comment

I wanted to check this out. The following block seems to work fine.

IF
 HotKey(D)
 See(Player1)
 !StateCheck(Player1,STATE_SLEEPING)
 !StateCheck(Player1,STATE_FROZEN_DEATH)
 !StateCheck(Player1,STATE_STONE_DEATH)
 !StateCheck(Player1,STATE_EXPLODING_DEATH)
 !StateCheck(Player1,STATE_FLAME_DEATH)
 !StateCheck(Player1,STATE_ACID_DEATH)
 !StateCheck(Player1,STATE_DEAD)
 See("JAHEIRA")
 !Dead("JAHEIRA")
 !InParty("JAHEIRA")
 !StateCheck("JAHEIRA",STATE_SLEEPING)
 Global("ALSKDJF","LOCALS",0)
 See("MINSC")
 !Dead("MINSC")
 !InParty("MINSC")
 !StateCheck("MINSC",STATE_SLEEPING)
 Global("ADFSAKL","LOCALS",0)
 AreaCheck("AR0602")
 True()
 OR(3)
   Exists("ULENE")
   Exists("CANIA")
   Exists("ELYME")
 !Dead("DUEGARCLANCHIEF")
 HaveSpell(WIZARD_MAGIC_MISSILE)
 !Detect(NearestEnemyOf(Myself))
 GlobalLT("ALDKJF","LOCALS",1)
 CombatCounter(0)
 Alignment(Myself,NEUTRAL_GOOD)
 Race(Myself,HUMAN)
 !False()
 General(Myself,HUMANOID)
 InParty(Myself)
 Class(Myself,MAGE_ALL)
 !InventoryFull(Player1)
 Class(Myself,THIEF_ALL)
 InMyArea("AATAQAH")
 HasItem("IMOENHP1",Myself) // Imoen's Belt
 CheckStatLT(Myself,1,STONESKINS)
 CheckStatLT(Player1,1,IMPROVEDHASTE)
 HaveAnySpells()
 HappinessGT(Myself,4294967295)
 InActiveArea(Myself)
 Name("Imoen",Myself)
 NumTimesTalkedTo(1)
 NumInPartyGT(1)
 OnScreen(Myself)
 !HasImmunityEffects(Player1)
 !HasBounceEffects(Player1)
THEN
 RESPONSE #100
   DisplayStringHead(Myself,1) // No, I'm sorry, none of them sound familiar.
END

Unless whoever that was is referring to the length of a trigger name, in which case I'm a moron.

 

I know that normal IDS symbols (spells, etc.) that are > 31 chars work from dialogues, but I don't think I've ever done actions/triggers. For scripts, obviously, it makes no difference how long any of the names are, as long as your compiler doesn't suck ass.

Link to comment

I felt so stupid that I tested it. The following worked just fine in a dialogue

0x4043 ThisIsASubstituteInPartyTriggerDesignedToLetMeCheckIfTriggersCanHaveMoreThanThirtyOneChars(O:Actor*)

 

NearestDoor object does work, however it will only return true for closed doors. If the door is open, NearestDoor is the "closest" closed door (however the engine determines object range). If you try to use NearestDoor() or similar, the game will crash.

IF
 HotKey(D)
THEN
 RESPONSE #100
   OpenDoor(NearestDoor)
END

Each time you press D, a new unlocked door is opened (Imoen opened all the unlocked ones in the entire area). If you add a See(NearestDoor), it works only until there aren't any closed doors in visual range.

Link to comment

Off-topic, but it may be where the 31 issue started: the engine can't handle variables of longer than 32 characters (I believe Grim discovered this at some point). This is also why DVs are only allowed 18 characters--those 18 + 14 for SPRITE_IS_DEAD = the maximum 32.

Link to comment

32 is the limit of strings used in the .bcs file.

This is true for all engine types, for example in iwd2 that awkward list for markspellandobject can store only 10 spells.

 

[edit]

Ahh. CamDawg just mentioned it. So it must be true :)

Btw, i know i talked with Max about this.

Link to comment

Most of this ha snow been added to the local copy.

 

One quick question though, the amazing ChangeAIType action - "All oIDs are required (if you leave any off, they will be set to 0; on a PC, you can then expect errors in CRuleTables.cpp)." - can you clarify this?

All id's are required, but ones not supplied end up as 0 - unless it's PC...?

 

 

EDIT: And I even tested most of this stuff! :rolleyes: Makes a change I guess.

Link to comment

Basically, if you leave any identifiers out of the object parameter, the game will destructively set them to 0 in the CRE file.

 

So if Imoen's script tried ChangeAIType([2.5.3]), she would become a PC.GIANT_HUMANOID.HALF_ELF, and her class, specific, gender, and alignment values will all be set to 0 (e.g., it's the same as [2.5.3.0.0.0.0]).

 

The crash in CRulesTables will happen because there are no rules for the invalid "0" values (the game can't process the attribute requirements/restrictions/bonuses/etc.). This would at least happen at level up, and probably won't affect NPCs.

 

I don't think it's important to specify that it may crash, just that any identifiers that are omitted will be set to 0.

Link to comment

Archived

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

×
×
  • Create New...