Aller au contenu

Photo

Looking for Script to Pause Time during Conversations so buffs don't fade


  • Veuillez vous connecter pour répondre
14 réponses à ce sujet

#1
ColorsFade

ColorsFade
  • Members
  • 1 270 messages
I could write this script myself *if * I knew enough about the available functions and variables in NWN2, but I don't just yet. I figure there might already be a script like this out there, but so far I have not been able to find it. I've checked the Vault and searched these forums and come up empty. 

#2
kevL

kevL
  • Members
  • 4 070 messages
i Don't think its possible without re-writing spellscripts for buffs. ( To keep track of a decrementing TimeRemaining var etc. and Get-ing then reapplying the effects at dialog end )

So i use clikpause ... clikpause ... IG


if there was an internal GetTimeRemainingForSpellEffects function it'd make things easier, but to do it externally would likely create a lot of overhead. (though workable)

#3
Dann-J

Dann-J
  • Members
  • 3 161 messages
http://nwn2.wikia.co...EffectDurations 

You'd need to cycle through the entire party and store the spell ids of all their effects as local variables first. You'd also have to refresh all the effects several times during the conversation to avoid losing the shorter duration buffs.

It's do-able, but would involve some complex scripting.

#4
ColorsFade

ColorsFade
  • Members
  • 1 270 messages

DannJ wrote...

http://nwn2.wikia.co...EffectDurations 

You'd need to cycle through the entire party and store the spell ids of all their effects as local variables first. You'd also have to refresh all the effects several times during the conversation to avoid losing the shorter duration buffs.

It's do-able, but would involve some complex scripting.


You the man DannJ!

Yep, the short duration ones are the effects I am most concerned about. 

Storing the ID may not be necessary though. 

I was taking a look at the Effect object last night to see if there was a way to do this and I found the script that allows you to print all the effects on a given object. That gave me a way to iterate Effects.

From there, a call can be made to GetEffectSpellId() to get the ID of the Effect. Then we can refresh.

The aspect I was missing was refresh function - I just couldn't find a function to perform that action. But there are hundreds of functions and scripts in the toolset... WIthout intimate knowledge, it can take time to find stuff. And searching the wiki can be hit or miss. Hence why I asked here - this forum is about the closest thing to a StackOverflow I have found. You folks are knowledable and respond really fast, and I appreciate it!

This is just going to be a matter of me understanding the scripting system fully and knowing what functions and scripts are available. I have written some pretty complicated multithreaded code at my day job (particularly this last year)... If I can do that, I should be able to make a spell refresh script :)

#5
kamal_

kamal_
  • Members
  • 5 254 messages
hidden functions
http://nwvault.ign.c...s.Detail&id=312

#6
ColorsFade

ColorsFade
  • Members
  • 1 270 messages
Awesome kamel_!

Now that is what I need!

#7
ColorsFade

ColorsFade
  • Members
  • 1 270 messages
kamal_,

In that Hidden Functions thing - he says to put that in your "mod" folder. I can't find a "mod" folder anywhere in NWN2 in either the base installation directory or the My Documents directory. What is he talking about?

#8
ColorsFade

ColorsFade
  • Members
  • 1 270 messages
Hahah! And it works!!!

I was able to create two scripts (one to start the rebuffing, one to end it) to successfully maintain buffs during a conversation.

I tested it with a level 1 Wizard summoning a wolf. Duration: 18 + 6 seconds per level (perfect for testing). The first time I didn't run the script, and shortly into the conversation the wolf is unsummoned. Then I ran it a second time with the script attached.

I printed debug lines to the chat window so I would know the rebuff code was being called. Every 6 seconds, I refresh the buffs.

I deliberately waited between conversation clicks, watching the updates come across the chat window telling me that the rebuff code was executing. The wolf stayed summoned the entire conversation and remained afterwards, for the ~10 seconds I had left on the buff.

To make it work, all I have to do is add two Actions to my conversation thread. StartRebuff to trigger buffs to refresh and EndRebuff when the conversation is over (because that would cheese dumb to leave it running)

I'd have preferred to use the OnConversation hook for the NPC to start the process, but there's no matching OnConversationEnd hook to turn the script off, and I couldn't find any other way to determine that a conversation had ended other than through the Conversation editor and associated Actions. If anyone has any clues to that, it would make life easier. Otherwise, I will have to remember to set both Actions at the start/end of conversations. In which case, I will probably only use this mechanism in cases where I know the player is going to face a fight when the conversation ends.

At the minimum, this gets me what I wanted, which is players won't have to worry about rushing through dialog during hostile encounters (or pausing between reading) for fear their buffs will run out. This mechanism takes care of that.

Maybe I'm the only player driven batty by this fact, but so be it. It's going to make playing this mod a lot easier on my nerves :-)

#9
painofdungeoneternal

painofdungeoneternal
  • Members
  • 1 799 messages
You have to store the duration, use the spellid to as part of the var name to retrieve it. You have to reapply the spell effect as it was originally cast, and the original caster object might not be available, or at a different level ( or class even ). Assumes you cannot put duplicates of the same spell, but then those get wonky regardless.

Did quite a bit of this for the CSL stuff I did a long time ago, but don't think there is a simple solution short of recompiling all the spells. You might find functions therein which are of use, and that also has the core game functions included, with known bugs and commentary which should be helpful.

The Refresh spell durations function is wonky, make sure you re-apply it. ( and the details of the issue would be noted in the above site )

Modifié par painofdungeoneternal, 25 février 2013 - 06:32 .


#10
ColorsFade

ColorsFade
  • Members
  • 1 270 messages
Darn. I thought RefreshSpellEffectDurations would simply tack on the value to what was remaining, but it doesn't seem to do that. Seems my 6 seconds is exactly how long it lasts.

So yeah, you have to capture the initial duration, which is tough. Too bad there isn't an easy way to get what's remaining off the effect itself.

#11
painofdungeoneternal

painofdungeoneternal
  • Members
  • 1 799 messages
It ( the RefreshSpellEffectDurations ) actually results in the effects messing up too, its borked. You have to recreate how it was originally cast completely, you can't actually use it and have it work as you'd assume. You have to remove the spell and reapply it like it was originally, which means keeping track of class, level, hitdice of damage, metamagic, spellid, caster, etc.

#12
ColorsFade

ColorsFade
  • Members
  • 1 270 messages

painofdungeoneternal wrote...

It ( the RefreshSpellEffectDurations ) actually results in the effects messing up too, its borked. You have to recreate how it was originally cast completely, you can't actually use it and have it work as you'd assume. You have to remove the spell and reapply it like it was originally, which means keeping track of class, level, hitdice of damage, metamagic, spellid, caster, etc.


Yeah, and even then, you wouldn't get it right, because certain spell scripts have modifiers hard-coded into them. I was delving into the spell scripts yesterday to see how they are written and it occurred to me that the only really accurate way to recast a spell would be to call the script that casts that particular spell, that way all modifiers would be applied properly. 

And even then, that's not really what I wanted - because you're casting the spell from scratch every time - so you get the full duration of the spell instead of a refresh. In reality, what needs to happen is simply an extension of the duration remaining on the Effect object.  

I can't see the source code, but I can imagine what they are doing: The Effect object likely has a property on it for the Duration (as it's passed as an argument to ApplyEffectXX methods, I have to imagine the Effect object itself stores this value), and every time the main game engine runs a heartbeat, it checks all active Effectsand decrements the Durations. If the Effect object simply exposed a method like "Refresh(int duration)" then this would be a cakewalk to solve. 

The only other thing I could think to do was disrupt the game's Heartbeat during conversations, but I couldn't see any way to do that as it's probably a core part of the compiled game engine. I did try researching some of the unimplemented Time Stop spells to see if maybe there was a solution in there, but nothng emerged from that effort. 

#13
painofdungeoneternal

painofdungeoneternal
  • Members
  • 1 799 messages
How i did it, is i create an array structure stored as a string on the target, that has all the properties of the spell as it was cast ( i redid all the spells ).

Then i store the ending round of the spell, the heartbeat maintains the current round number ( I prefer this to other methods as the math is simpler and it's easy to visually inspect, but it does require a single hearbeat )

If an effect is removed temporarily, transferred ( some spells can steal an opponents buffs for example ), it can be reapplied with the original settings, and also set to end during the same round number. The spell implementation scripts are all modded so they basically largely bypass the engine. I imagine if you took the CSL stuff, you could use what features it already supports to get it to support what you are talking about - ie dispel all effects during a conversation or like the OC used to do it, but then after the conversation is over, resume them at the original duration ( and a newly set ending round ).

It's extra work of course to do everything I did, but the AI version I setup uses the same spell data and cached values, so it ends up being less actual work overall, and it lets me manually do dispels as well which is the real feature it was originally developed for.

#14
ColorsFade

ColorsFade
  • Members
  • 1 270 messages

painofdungeoneternal wrote...

How i did it, is i create an array structure stored as a string on the target, that has all the properties of the spell as it was cast ( i redid all the spells ).

Then i store the ending round of the spell, the heartbeat maintains the current round number ( I prefer this to other methods as the math is simpler and it's easy to visually inspect, but it does require a single hearbeat )

If an effect is removed temporarily, transferred ( some spells can steal an opponents buffs for example ), it can be reapplied with the original settings, and also set to end during the same round number. The spell implementation scripts are all modded so they basically largely bypass the engine. I imagine if you took the CSL stuff, you could use what features it already supports to get it to support what you are talking about - ie dispel all effects during a conversation or like the OC used to do it, but then after the conversation is over, resume them at the original duration ( and a newly set ending round ).

It's extra work of course to do everything I did, but the AI version I setup uses the same spell data and cached values, so it ends up being less actual work overall, and it lets me manually do dispels as well which is the real feature it was originally developed for.


So Pain, if I wanted to use part of the CSL to accomplish my goal, what would I need to do? Is it just  matter of downloading and installing the proper CSL files, like _HKSpell.nss, or would I need to write additional scripts as well?

I've looked at the CSL site twice now, delving into some of the individual files, but it's quite big and I haven't had time to grok the whole thing yet. 

It sounds like you've accomplished what I want to do. I'd really love to have this feature in my campaign. 

#15
painofdungeoneternal

painofdungeoneternal
  • Members
  • 1 799 messages
The crux of it is in CSLLibrary, you can put that into your hak, or into your module, generally leave it in override though while you develop. It cannot conflict as all names are custom so can be used along side other libraries.

If all you want to do is write your own code, you can use this library instead of the official one and probably never again touch official code. The idea here is you don't need to use the superinclude, since if you need a string function, it's actually in the string include, except I've also put in everything which people actually seem to end up having to add themselves as well.

The folder CSLHookedSpells has the hooked spells. ( hooked meaning it has me hooks set up in the code, and _hkspell include is named for this as well ) It also has renamed spells to prevent conflicts, i actually migrated from normal spells to my spells - so the spell "NW_S0_Fireball" is "sp_fireball", which actually is how i can find spells when they are sorted without having to go refer to spells.2da anyway. I believe there is a spells.2da included. Feats are in the CSLHookedFeats, etc. You'd probably have to review the other folders, but you probably won't need much else, but the more features you use, the more folders end up being required.

The bare events folder has contents which go into the various module events, and there are some object blueprints the overall system uses, as well as the csl_preferences.2da. You need to also set up the spellhook event. These are all areas for you to customize things.

Data is stored via the HkApplyTargetTag function, so you can see how it works. HkTransferTargetTag, HkDeleteTargetTag, HkAOETag

accessing these values is via code like
iCreatorLevel = CSLGetTargetTagInt( SCSPELLTAG_CASTERLEVEL, oTarget, iSpellId);
nDispelDC = CSLGetTargetTagInt( SCSPELLTAG_SPELLDISPELDC, oTarget, iSpellId);
oCreator = IntToObject( CSLGetTargetTagInt( SCSPELLTAG_CASTERPOINTER, oTarget, iSpellId ) );

Transfering ( reaving dispel ) uses in _SCInclude_Abjuration.nss on line 730 or so ( looks like this uses a set duration and not the original duration )
DelayCommand( 0.1f, CSLTransferEffectSpellIdSingle_Void( SC_REMOVE_ALLCREATORS, oCreator, oTarget, oCaster, iSpellId ) );

I actually have it set up to set grease spells on fire, which uses the spell duration. CSLGetTargetTagInt and CSLGetAOETagInt are similar, just different functions depending on the type of target.
if ( iSpellID == SPELL_GREASE )
	{
		iDispelDC = CSLGetAOETagInt( SCSPELLTAG_SPELLDISPELDC, oAOE );
		if ( CSLEnviroConfictContest( oCaster, "You ignited the Grease", iStartingDC, iDispelDC ) )  // 9+21 = 30 vs 50, 30 vs 35
		{
			int iEndingRound = CSLGetAOETagInt( SCSPELLTAG_ENDINGROUND, oAOE );
			
			
			float fRemainingDuration = CSLGetMinf( CSLEnviroGetRemainingDuration( iEndingRound ), 24.0f);
			//string sAOETag =  HkAOETag( oCaster, -SPELL_GREASE, iStartingDC,  fRemainingDuration, FALSE  );
			effect eNewAOE = EffectAreaOfEffect(AOE_PER_RADFIRE, "", "", "", GetTag(oAOE) );
			DelayCommand( 0.1f, ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eNewAOE, GetLocation(oAOE), fRemainingDuration ) );
			DelayCommand( fRemainingDuration, DestroyObject(oAOE) );
			//SendMessageToPC(oCaster, "AOE with duration of"+FloatToString(fRemainingDuration)+" Ending on round "+IntToString(iEndingRound)+" current round "+IntToString(GetLocalInt( GetModule(), "CSL_CURRENT_ROUND" )) );
			ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectNWN2SpecialEffectFile( "fx_blazing_fire_lg" ), GetLocation(oAOE), 2.0f );
			
			return;
		}
	}