Larian Banner: Baldur's Gate Patch 9
Previous Thread
Next Thread
Print Thread
#639468 23/12/17 06:22 PM
Joined: Dec 2017
S
stranger
OP Offline
stranger
S
Joined: Dec 2017
Hi Everyone,

First time posting. I've run into a bit of a problem trying to create a stackable status effect. Simply put I want a target skill when used to add an additional stack to the target character. Each further iteration of the stack would add a different effect, such as increased damage, etc ..

I tried using the skillproperties for the skill to get it working and that was not successful. For instance:

Target:IF(HasStatus:STATUS):ADD_STATUS,100,1

As that didn't work I'm thinking that scripting is probably the route I'll need to go, but aside from looking at the Statuses script I'm not sure what direction to go in.

Any feedback/information would be much appreciated!

Joined: May 2017
enthusiast
Offline
enthusiast
Joined: May 2017
The key to having the status stack is to leave the StackID in the statuses' stats data blank.

If you want the status bonus to change (damage, then crit, and so on), you'll either need to handle that with scripting, or with clever skill property conditions like so:

Code
TARGET:IF(HasStatus:STATUS_BUFF1):STATUS_BUFF2,100,2;TARGET:IF(!HasStatus:STATUS_BUFF1&!HasStatus:STATUS_BUFF2):STATUS_BUFF1,100,2;

You may be able to pull this off with only using the skill properties like this, but it may also just apply all the statuses at once, depending on how it evaluates the condition and then applies the statuses. I'm not sure if it would add the first buff and then immediately add the second, which it may do if it applies the first buff before getting to the second condition.

Keep in mind, this is probably way easier to do with a script (depending on how complex you want this to be), and I don't see a huge advantage to doing it with the skillproperties, since it won't add any flavor to the skill description (and you can make the description look nice yourself anyway, with font colors and StatsDescriptionParams).

Joined: Dec 2017
S
stranger
OP Offline
stranger
S
Joined: Dec 2017
LaughingLeader, thanks for the quick reply!

The primary reason I haven't delved into scripting yet is I couldn't find any documentation on how to 'connect it' to a given skill. Is it something I simply write and if those conditions are present it is evaluated? For instance, do I define the script as part of the skill property? Based on what I'm looking to do, do you have any good guidelines, or scripts that you would recommend I review to come up with a stackable status effect?

Thanks again!

Joined: Dec 2017
S
stranger
OP Offline
stranger
S
Joined: Dec 2017
Also, while I'm thinking of it, is there a way to check how many stacks? You mentioned:

"leave the StackID in the statuses' stats data blank"

or do I need to add "status_2" behind the scenes and write a check for that?

Joined: Nov 2017
L
member
Offline
member
L
Joined: Nov 2017
Instead of trying to define these combinations specifically in the skill, it may be better to look into having the status stacking work like e.g. WARM + WARM = BURNING, through the statuses gamescript.

Joined: May 2017
enthusiast
Offline
enthusiast
Joined: May 2017
The most compatible way to script this would be to make it a story script. I wrote a tutorial for that recently: Your First Story Script.

Essentially, your story script could look like this:
So as I was writing up some sample code, the idea was interesting to me, so I went ahead and wrote it like I probably would, using databases, procedures, and queries (which to me, are the fundamentals of reusable, extensible story script code). Here's what I came up with:

INIT:
Code
//DB_MyMod_BuffLevels(_Target, _BuffLevel)
//DB_MyMod_Buffs(_BuffLevel, _Status, _Turns, _Force)
DB_MyMod_Buffs(1, "BUFF1", 2.0, 0);
DB_MyMod_Buffs(2, "BUFF2", 2.0, 0);
DB_MyMod_Buffs(3, "BUFF3", 2.0, 0);
DB_MyMod_Buffs(4, "BUFF4", 2.0, 0);


KB:

//REGION HELPERS
QRY
QRY_MaxBuffLevelReached((INTEGER)_Level)
AND
SysCount("DB_MyMod_Buffs", 4, _Max)
AND
_Level >= _Max
THEN
DB_NOOP(1); // Dummy database to make query evaluate as true

QRY
QRY_BuffActive((CHARACTERGUID)_Target)
AND
DB_MyMod_Buffs(_BuffLevel, _Status, _Turns, _Force)
AND
HasActiveStatus(_Target, _Status, 1)
THEN
DB_NOOP(1);
//END_REGION

//REGION CHARACTER_EVENTS
IF
CharacterUsedSkillOnTarget(_Caster, (CHARACTERGUID)_Target, "Target_MySkillName", _)
THEN
IncrementBuff(_Target);

IF
CharacterStatusRemoved(_Character, _Status, _)
AND
DB_MyMod_BuffLevels(_Character, _BuffLevel)
AND
DB_MyMod_Buffs(_BuffLevel, _Status, _Turns, _Force)
AND
NOT QRY_BuffActive(_Character)
THEN
//Clear character from database since all the buffs are gone.
NOT DB_MyMod_BuffLevels(_Character, _BuffLevel);

//END_REGION

//REGION BUFF_APPLYING
PROC
IncrementBuff((CHARACTERGUID)_Target)
AND
NOT DB_MyMod_BuffLevels(_Target, _)
THEN
DB_MyMod_BuffLevels(_Target, 1);
ApplyBuffStatus(_Target, 1);

PROC
IncrementBuff((CHARACTERGUID)_Target)
AND
DB_MyMod_BuffLevels(_Target, _BuffLevel)
AND
NOT QRY_MaxBuffLevelReached(_BuffLevel)
AND
IntegerSum(_BuffLevel, 1, _NextBuffLevel)
THEN
NOT DB_MyMod_BuffLevels(_Target, _BuffLevel); // Remove previous entry
DB_MyMod_BuffLevels(_Target, _NextBuffLevel);
ApplyBuffStatus(_Target, _NextBuffLevel);

PROC
ApplyBuffStatus((CHARACTERGUID)_Target, (INTEGER)_BuffLevel)
AND
DB_MyMod_Buffs(_BuffLevel, _Status, _Turns, _Force)
THEN
ApplyStatus(_Target, _Status, _Turns, _Force);
//END_REGION

This will probably look pretty complicated as you're just learning how it all works, what the syntax means, and so on, but essentially what I did here was:

Quote
º Create a database that contains all the status information we need: the buff level of the status, the actual status name, the turn duration, and whether or not to force it. We do this so we can reduce the actual code to apply the status to one rule. Makes it way easier to extend, if we decide we want more buffs.

º Create a database to store the target character's current buff level. We use this to cross-reference into our buffs database. Where the previous buffs database is more of our buff "settings", this database is a basically a way for us to temporarily store a variable for a target character, until their buffs are gone.

º Create rules for when that specific skill is cast on a target, and when all of the buff statuses are removed.

º Create some queries/procs to make things easier in the long run.


Now this could be extended, depending on what you want. If you want the buff level to go in reverse (if, say, buff 4 runs out, you want the level to go back a step to level 3), then that would be possible with another rule like this:

Code
IF
CharacterStatusRemoved(_Character, _Status, _)
AND
DB_MyMod_Buffs(_BuffLevel, _Status, _Turns, _Force)
AND
DB_MyMod_BuffLevels(_Character, _BuffLevel)
AND
QRY_BuffActive(_Character)
AND
IntegerSubtract(_BuffLevel, 1, _NextBuffLevel)
THEN
NOT DB_MyMod_BuffLevels(_Character, _BuffLevel);
DB_MyMod_BuffLevels(_Character, _NextBuffLevel);
// No triggering the status applying, since we just want it to apply from the skill being cast.


Looking at it now, this is probably way more information than you wanted, haha. opa

To get a basic idea working, the main methods you'll need are:

Quote
event CharacterUsedSkillOnTarget((CHARACTERGUID)_Character, (GUIDSTRING)_Target, (STRING)_Skill, (STRING)_SkillType)
event CharacterStatusApplied((CHARACTERGUID)_Character, (STRING)_Status, (GUIDSTRING)_Causee)
event CharacterStatusRemoved((CHARACTERGUID)_Character, (STRING)_Status, (GUIDSTRING)_Causee)

query HasActiveStatus([in](GUIDSTRING)_Target, [in](STRING)_Status, [out](INTEGER)_Bool)
call ApplyStatus((GUIDSTRING)_Object, (STRING)_Status, (REAL)_Duration, (INTEGER)_Force, (GUIDSTRING)_Source)

Joined: May 2017
enthusiast
Offline
enthusiast
Joined: May 2017
Originally Posted by Sir Canadian
Also, while I'm thinking of it, is there a way to check how many stacks?
...or do I need to add "status_2" behind the scenes and write a check for that?

You only really need to deal with that StackID stuff if you want the same status to stack more than once (I did this for my Civil Auras mod to stack civil ability point bonuses dynamically through a script).

To my knowledge, tracking those stack numbers has to be done via script, since removing a stack ID is basically saying "ignore this status for any stack-related things". The RemoveStatus call will remove all instances of that same stacking status, but nothing exists to retrieve how many instances of that status exist on an object.

From what you described, it sounds like you want more of a status chain (while internally tracking the number of "stacks"), so you'd have:

Code
First Skill Cast -> (Stack Level 1: Apply A Buff) -> Second Cast (Stack Level 2: Apply B) -> Third Cast (Stack Level 3: Apply C)

And so on.

Joined: Dec 2017
S
stranger
OP Offline
stranger
S
Joined: Dec 2017
@LaughingLeader. Thanks for the detailed information. I'm going to give it a whirl tonight and see how it goes. One more question, do I need to write anything into the skillproperties to 'trigger' a script, or does the nature of what the script is looking for trigger itself?

Joined: May 2017
enthusiast
Offline
enthusiast
Joined: May 2017
Story scripts (called "goals") are part of the "story" of DOS2, which means they always run as long as their parent goal (if they have one) is complete, and they themselves are not complete (called with "GoalComplete"). Nothing is needed from the skill stats to make one run.

Joined: Dec 2017
S
stranger
OP Offline
stranger
S
Joined: Dec 2017
@LaughingLeader. I managed to get my first script working! It's not written as a story script, although that is the next step I would like to take this as your method is cleaner and more organized. This is script works I just had to ensure that I was removing the first Hex cast each IF check until I managed to get through all 5 'stacks'.

EVENTS

EVENT CharacterSetEldritchHex
VARS
CHARACTER:_Character
LIST<STATUS>:_RemoveList
STATUS:_Result
ON
FetchCharacterApplyStatusData(_Character, ELDRITCH_HEX)
ACTIONS
Set(_Result,ELDRITCH_HEX)
ListClear(_RemoveList)
IF "c1"
CharacterHasStatus(_Character, ELDRITCH_HEX_4)
THEN
CharacterApplyStatus(_Character, CRIPPLED,2,1)
ListAdd(_RemoveList,ELDRITCH_HEX)
ELIF "c1"
CharacterHasStatus(_Character, ELDRITCH_HEX_3)
THEN
ListAdd(_RemoveList,ELDRITCH_HEX)
CharacterApplyStatus(_Character,ELDRITCH_HEX_4,2,1)
ELIF "c1"
CharacterHasStatus(_Character, ELDRITCH_HEX_2)
THEN
ListAdd(_RemoveList,ELDRITCH_HEX)
CharacterApplyStatus(_Character,ELDRITCH_HEX_3,3,1)
CharacterApplyStatus(_Character,SUFFOCATING,3,1)
ELIF "c1"
CharacterHasStatus(_Character, ELDRITCH_HEX)
THEN
ListAdd(_RemoveList,ELDRITCH_HEX)
CharacterApplyStatus(_Character,ELDRITCH_HEX_2,3,1)
ENDIF
RETURN(_RemoveList,_Result,_Turns)

I could certainly still clean this up as well. I was trying to look through other scripts as a reference and I couldn't find that many. Do I need to extract a pak file to gain access to more scripts?

Joined: Nov 2017
L
member
Offline
member
L
Joined: Nov 2017
Your solution, Sir Canadian, is precisely what I was suggesting. It's how the game generally handles all other status interactions, and how it expects status interactions to be handled, and I would recommend against duplicating this functionality in Story.

Joined: Dec 2017
S
stranger
OP Offline
stranger
S
Joined: Dec 2017
@LarIlya. The available scripts to look over seems rather limited. Is there a file I need to extract, or something else I need to do to make skill scripts available for review?

Joined: Nov 2017
L
member
Offline
member
L
Joined: Nov 2017
What scripts do you have?
There aren't a lot of Gamescripts to begin with, but there are quite a few behaviour scripts. Do you have the Origins mod loaded as well? A lot of them are in there.

Joined: Dec 2017
S
stranger
OP Offline
stranger
S
Joined: Dec 2017
I do have the Origins mod enables. The two folders aside from my mod are Shared and DOS 2. I really just need to see how a on skill use script is written and I can probably piece it together from there!

Joined: Dec 2017
S
stranger
OP Offline
stranger
S
Joined: Dec 2017
Also, while I'm asking. Any recommendations on how I can make a trap skill in game? I can't find any of the data in the stats editor, so I'm wondering if it might be hidden elsewhere!

Joined: Nov 2017
L
member
Offline
member
L
Joined: Nov 2017
There aren't really any "on skill use scripts", at least not in the sense that I think you mean. The skill functionality is implemented through their stats entry entirely, because that's where all the systems expect that information to be.
For instance, our AI doesn't parse scripts (with a few exceptions, like the FetchCharacterApplyStatusData you used and FetchItemSkillOnDamage), so it wouldn't know what the skill does.

That's not to say you can't do this - there is the OnSkillCast event in Behaviour script and the SkillCast, CharacterUsedSkill, CharacterUsedSkillAtPosition, CharacterUsedSkillOnTarget, CharacterUsedSkillOnZoneWithTarget and CharacterUsedSkillInTrigger events in Story to which you can react, but you're unlikely to find any examples of skill functionality implemented through script because that doesn't fit into the existing systems very well.
If there are things you can't do in stats or by extending generic systems (like your extension to the status interactions above), you may be better off going LaughingLeader's route.

In regards to trap skills: traps typically use normal projectile skills, exploded at the trap's own location with ExplodeAt from item scripts. If you look for "trap" in the Projectile stats table, you'll find a few examples of these.
For example, a mine works by using ExplodeAt(__Me,%MineProjectile,_TrapLevel,__Me), with %MineProjectile, by default, the TrapFireballMine skill. The other parameters are the location -in this case, the location of the mine itself-, the level of the explosion -a level 10 mine will do more damage than a level 2 mine but less than a level 15 mine-, and finally the 'cause' of the explosion, which, simplistically said, tells NPCs if a player caused the explosion.

Joined: Dec 2017
S
stranger
OP Offline
stranger
S
Joined: Dec 2017
@LarIlya. I meant to update my post, I figured out how traps were connected shortly after updating this thread. Although I do appreciate the explanation here as well.

I think I should clarify what I'm trying to do and why I think I need to script part of the functionality. I've created a Skill ( call it skill A) that does X damage and costs 2 AP. If a player uses the spell with AP remaining in their pool the spell would scale in damage. So, for every 1 additional AP over the base cost the damage would increase and the AP would be spent.

Only 2 AP (Base level): 2 AP spent and X dmg
Only 3 AP (BL + 1): 3 AP spent and X + Y dmg
Only 4 AP (BL + 2): 4 AP spent and X + (Y*2) dmg

I haven't gone through all of the API calls yet, so I don't even know if forcing a character to spend additional AP is possible.

Also, I've been able to follow the setup for mostly everything in the stats editor. However, there are certain spells where I can't quite connect all of the dots. Therefore, being unable to use it as a baseline for a new skill I want to create.

For instance, I can see where the tornado skill is setup and where I could apply damage, etc .. However, I cannot find where the effects for the tornado itself are defined (precast and cast animations are located easily enough). So if I wanted to utilize the structure of what the tornado skill does I wouldn't (as far as I know) be able to replace the main effect of the spell with something unique. Any thoughts?

Joined: Mar 2016
Location: Belgium
T
addict
Offline
addict
T
Joined: Mar 2016
Location: Belgium
Originally Posted by LaughingLeader

Keep in mind, this is probably way easier to do with a script (depending on how complex you want this to be), and I don't see a huge advantage to doing it with the skillproperties, since it won't add any flavor to the skill description (and you can make the description look nice yourself anyway, with font colors and StatsDescriptionParams).

The main advantage of using skill properties is that the AI then can reason about it, both when assigning the skill to an NPC and in terms of threat posed by a player having that skill. The AI is completely blind to things done in script.

Joined: Dec 2017
S
stranger
OP Offline
stranger
S
Joined: Dec 2017
I read a few of the threads on setting an animation via a status script or story by utilizing:

CharacterSetAnimationSetOverride

I managed to write a basic script to do this with some of the standard animations listed, however I was unable to use other such animations that I found in the resource manager. More specifically I am trying to loop a 'floating' type animation for a Levitate type skill.

I found Skill_Prepare_Flight_01_Loop in the resource manager and I tried plugging every combination of that animation into CharacterSetAnimationSetOverride to no avail. Is there something I'm missing or can we not use some of the custom animations?


Link Copied to Clipboard
Powered by UBB.threads™ PHP Forum Software 7.7.5