Scripting 9 & 1/2 weeks

Post » Fri Jul 08, 2011 5:03 am

I've been really struggling in testing the scripts for 9 & 1/2 weeks so in an effort to make some progress I could really use some help

Melian very kindly wrote me a light companion script a year ago and I've been trying it out - the script works well however I sometimes want to try to do some things it can't cope with

For instance the companion will come to a certain place and not want to move from there for a few days - I have localdayspassed as a variable - so in dialogue it tests fine - however getting the companion to stay in one spot has proven difficult

I have a global variable called BM_Maidservice this is set to 1 when she enters into companion mode (btw she has no companion share by design) - I was adding to the script a small section that said if she was set to BM_Maidservice 2 then have a stationary AIwander - this would freeze her in place literally as in she would slightly tremble and there would be no idles working at all - however she would stay put for the whole localdayspassed as set in dialogue - however this would create problems with other scripts where i would want her to do some AITravel in a room if she was in freeze mode the scripts just wouldn't move her.

If in dialogue i just add in the results box AIWander 0 0 0 0 - this is not sufficient as i have observed putting her in wait mode and sometimes she will still follow me. Is there someway I can amend this script so that when my Global is set to 2 she still has normal idles but is locked into position until I rest her via dialogue to 1?

begin illyCompanionLSshort followNow  ;simple check for follow mode - if you have a follow variable                ;that's set from dialogue it would be more reliable so replace this if you have oneshort counter  ; added b/c script is filling up a bit now -                ;  stuff that doesn't need to be done every frame can go at end of scriptshort oldCell  ;type of previous cell, interior=1, exterior=0short curCell  ;type of current cell (as above)short matchZ  ;warp needed on z-axis? for levitation/swimmingshort cmbtChk  ;combat checkshort wwChk  ;do-once for taking waterwalking potionshort swimChk  ;does the cell have water? (if so then will check for swimming)short sick  ;check for disease, 0=healthy 1=common disease 2=blight diseaseshort attrDmgshort doorFix  ;state variable for load-door fixshort doOnce1  ;doonce vars for Grumpy's warping code/save warp destination pointsshort doOnce2short tmpS  ;just a temp var (can use in dialogue results calcs)short storeDay  ;simple day change check for medication once-per-day                    ; - remove this if doing it in dialogue instead;potion storage vars;these will need spells set up to apply the effects, except for;  restore health (which is better done in code alone);spells should all be type ability;to use these in dialogue:;if using one of the NPC's potions (ie one stored in these vars):;check that the relevant variable is > 0 (ie that she's actually got some) and if so:;                       set variable to ( variable - 1 );                       AddSpell "spell ID";                       set cureTmr to 1;(that last line will trigger the script to remove the ability once it's done its job);of course replace placeholder ids with the real idsshort pv_lev  ;levitationshort pv_cureC  ;cure common diseaseshort pv_health  ;restore healthshort pv_wwalk  ;water walkingshort pv_dispel  ;dispel (note: beware mischievous players telling her to take a dispel potion while levitating/waterwalking!)short pv_meds  ;daily medicine (2 associated spells needed for this one: cure blight and restore all attributes)float ax  ;position vars for warpingfloat ayfloat bxfloat byfloat bzfloat cxfloat cyfloat czfloat t1  ;temp vars used in warping codefloat t2  ;these "t*" vars can be reused as temp vars in dialogue results if neededfloat t3float t4float coDist  ;distance checks for warpingfloat coDist2float pcDist  ;distance to player - GetDistance is slow, it's cheaper to check once per framefloat warpTmr  ;time out for warpingfloat wndrTmr  ;time out for wander (door fix etc) - can be used in dialogue eg step aside option;to use a step aside option, put in dialogue results:;               set wndrTmr to 3;               AIWander 1000 0 0 0 0 0 0 0 0 0 0 0;               goodbye;the timer should (I hope) put her back in follow mode when it's done (if she was in follow;  mode to start with)float sheathe  ;timer for weapon sheathing after combat (otherwise sheathing would                ;   trigger when combat target is distant or when switching combat targets)float levTmr  ;levitation time-outfloat cureTmr  ;timer for cure/restore abilities to simulate potion usefloat prevX  ;check for change in x-position - more reliable than cellChangedfloat currXfloat swimLvl  ;used to check if swimmingfloat playZfloat myZ  ;current z-positionfloat tmpF  ;just a temp var (can be used for calcs in dialogue results);heal and restore while sleeping, and;check if NPC is dead/unconscious/paralysed -;no point running the rest if they are!if ( GetHealth < 1 )    returnelseif ( GetPCSleep )  ;I put this before menumode since otherwise the PC    ModCurrentHealth 999  ; has to sleep at least 2 hours for it to trigger    ModCurrentMagicka 999    ModCurrentFatigue 999    returnelseif ( GetPCTraveling )        set doorFix to 1 ;wander to get out of pile-up after fast travel        returnelseif ( MenuMode )    returnelseif ( GetFatigue < 1 )    returnelseif ( GetParalysis > 0 )    returnendifif ( wndrTmr < 500 )elseif ( doorFix != 0 )elseif ( GetCurrentAIPackage == 3 )        set followNow to 1elseif ( GetCurrentAIPackage != -1 )  ;if AI is invalid, this assumes the last valid package        set followNow to 0                              ;is still the correct one        set warpTmr to 0endif;check for cell change - need to know if one or both; cells are interior. This triggers the door-fix wander;Problem is that on game load the x-pos is wrong value first time;  so also check for game load via startscriptset tmpS to 0set prevX to currXset currX to ( GetPos x )set tmpF to ( currX - prevX )if ( doorFix == -1 )  ;just warped, false alarm        set doorFix to 0elseif ( doorFix == 0 )        if ( followNow == 0 )        elseif ( mel_reload == 1 )  ;game load = false alarm        elseif ( tmpF > 512 )                set tmpS to 1        elseif ( tmpF < -512 )                set tmpS to 1        endifendifif ( tmpS >= 1 )        set oldCell to curCell        set curCell to ( GetInterior )        set swimChk to 0        if ( oldCell == 1 )  ;if a cell is interior, do the fix                set doorFix to 1        elseif ( curCell == 1 )                set doorFix to 1        elseif ( tmpS == 1 )  ;this will kick in with e.g. scripted teleports                set doorFix to 1        endifendifset pcDist to ( GetDistance player )set warpTmr to ( warpTmr + GetSecondsPassed )if ( wndrTmr < 500 )        set wndrTmr to ( wndrTmr - GetSecondsPassed )        set warpTmr to 0        if ( wndrTmr <= 0 )                set wndrTmr to 512                if ( followNow == 1 )                        AIFollow player 0 0 0 0                else                        AIWander 256 0 0 30 35 5 10 5 10 5 0 0  ;replace idles and distance with your defaults/whatever you want                endif        endifendifif ( doorFix > 0 )        if ( mel_reload != 0 )  ;if it got past the first check, catch it here                set wndrTmr to 0.1  ;this should hopefully put her back in follow mode                set doorFix to 0                return        elseif ( doorFix > 7 )                if ( wndrTmr > 500 )                        set doorFix to 0  ;all done                        return                endif        elseif ( doorFix == 4 )                if ( pcDist < 800 )  ;don't need to warp                        set doorFix to 6                elseif ( ScriptRunning illy_loaddoorfix == 0 )                        set doorFix to 5                        StartScript illy_loaddoorfix  ;warp companion back to player if she's got lost                endif        elseif ( doorFix == 5 )                if ( pcDist > 800 )  ;wait for script to do its job                        return                elseif ( ScriptRunning illy_loaddoorfix == 0 )                        set doorFix to 6                endif        elseif ( doorFix == 6 )                AIWander 512 0 0 0  ;this also helps get companion out of the post-door pile-up                set wndrTmr to 2                set doorFix to 7        else                set doorFix to ( doorFix + 1 )        endif        set warpTmr to 4        returnendif;remove spell abilities when doneif ( cureTmr >= 0 )        set cureTmr to ( cureTmr + GetSecondsPassed )        if ( cureTmr >= 2 )                set cureTmr to -2                RemoveSpell "illy_cureBlight"                RemoveSpell "illy_restoreAttribs"                RemoveSpell "illy_cureCommon"                RemoveSpell "illy_dispel"        endifendif;check health and run or teleport away if necessary;just remove this block if you don't want itif ( GetHealthGetRatio < 0.25 )    MessageBox "Insert flee-by-teleport message here"    PlaySound3d "swallow"  ;pretend to take a potion    PlaySound3d "mysticism hit"    PositionCell 10 -58 131 0 "Caldera, Shenk's Shovel"  ;change destination as you like    returnelseif ( GetHealthGetRatio < 0.5 )    if ( GetFlee < 90 )        SetFlee 100        MessageBox "Insert flee-by-running message here if you want one"  ;or you could use say for a voice file instead    endifelseif ( GetFlee > 90 )    SetFlee 10  ;change to whatever your default isendif;give her a health potion if health is low;change health percent value to whatever you wantif ( GetHealthGetRatio < 0.7 )        if ( pv_health > 0 )     ;are these going to be Exclusive potions??                set pv_health to ( pv_health - 1 )                PlaySound3d "swallow"                PlaySound3d "restoration hit"                ModCurrentHealth 200  ;if not Exclusive, change this value to match potion total health increase        endifendif;this next part checks for combat so they don't warp in the middle of it;and so can force weapon sheathing afterwards if they get stuck like that;removed spellcast sound checks, so now there's room to put hit checks in same blockif ( cmbtChk )        set warpTmr to -2        set sheathe to ( sheathe - GetSecondsPassed )        if ( GetSoundPlaying "Weapon Swish" )                set sheathe to 5        elseif ( GetSoundPlaying "crossbowShoot" )                set sheathe to 5        elseif ( GetSoundPlaying "bowShoot" )                set sheathe to 5        elseif ( GetSoundPlaying "Health Damage" )                set sheathe to 5        elseif ( GetSoundPlaying "destruction hit" )                set sheathe to 5        elseif ( GetSoundPlaying "frost_hit" )                set sheathe to 5        elseif ( GetSoundPlaying "shock hit" )                set sheathe to 5        elseif ( GetSoundPlaying "alteration hit" )                set sheathe to 5        elseif ( GetSoundPlaying "illusion hit" )                set sheathe to 5        elseif ( GetSoundPlaying "Hand To Hand Hit" )                set sheathe to 5        elseif ( GetSoundPlaying "Hand to Hand Hit 2" )                set sheathe to 5        elseif ( sheathe <= 0 )                set cmbtChk to 0        if ( GetSpellReadied )            cast "mark" player        else            equip "chitin dagger"          ;Change this if your NPC actually has one. You            RemoveItem "chitin dagger" 1   ;  should use a 1-handed weapon they *don't* have        endif                                ;  - otherwise it will remove their real weapon!    endifelseif ( GetWeaponDrawn )        set cmbtChk to 1        set sheathe to 5        set warpTmr to -2elseif ( GetSpellReadied )        set cmbtChk to 1        set sheathe to 5        set warpTmr to -2endif;check for attribute damage;note this isn't all that reliable (but she'll get restored each day regardless so not a big deal)if ( GetEffect sEffectRestoreAttribute )        set attrDmg to 0elseif ( GetEffect sEffectDamageAttribute )        set attrDmg to 1elseif ( attrDmg == 1 )        set attrDmg to 2endifif ( followNow == 1 )        if ( GetForceSneak )                if ( cmbtChk )  ;take her out of sneak mode if in combat                        ClearForceSneak                elseif ( player->GetEffect sEffectInvisibility )                elseif ( player->GetEffect sEffectChameleon )                elseif ( GetPCSneaking == 0 )                        ClearForceSneak                endif        elseif ( cmbtChk )        elseif ( GetPCSneaking == 1 )                ForceSneak  ;sneak when the player is sneaking, except in combat        endif                if ( player->GetEffect sEffectWaterWalking )                if ( wwChk == 1 )                elseif ( pv_wwalk > 0 )                        set pv_wwalk to ( pv_wwalk - 1 )  ;take a waterwalking potion                        set wwChk to 1                        PlaySound3d "swallow"                        PlaySound3d "alteration hit"                        AddSpell "efb_bf_wwalkA"                endif        elseif ( wwChk == 1 )                RemoveSpell "efb_bf_wwalkA"                set wwChk to 0        endif                ;this bit will make her sneak when the player uses chameleon or        ;invisiblity spells - just remove it if you don't want it,        ;it shouldn't affect anything else much                if ( cmbtChk )        elseif ( player->GetEffect sEffectInvisibility )                if ( GetForceSneak == 0 )                        ForceSneak                endif        elseif ( player->GetEffect sEffectChameleon )                if ( GetForceSneak == 0 )                        ForceSneak                endif        endif                ;levitation works on a timer, so she has time to get down        ;after player's levitation runs out        ;note: it's all in follow mode block, so if player tells her to wait        ;while levitating she'll just stay there in the air indefinitely!        ;this can be changed if you want (can add a slowfall ability and get        ;down that way)                if ( player->GetEffect sEffectLevitate == 1 )                if ( levTmr > 1 )                        set levTmr to 1                elseif ( levTmr <= 0 )                        if ( pv_lev > 0 )                                set pv_lev to ( pv_lev - 1 )  ;take a levitation potion                                PlaySound3d "swallow"                                PlaySound3d "alteration hit"                                AddSpell "illy_levitateA"                                set levTmr to 1                        elseif ( GetEffect sEffectLevitate == 0 )  ;in case player casts on her or something                                AIWander 0 0 0  ;don't try to follow if can't levitate                                MessageBox "do you want a warning when she's out of levitation potions??"                        endif                endif        elseif ( levTmr > 10 )                RemoveSpell "illy_levitateA"                set levTmr to 0        elseif ( levTmr > 0 )                set levTmr to ( levTmr + GetSecondsPassed )        endif        elseif ( followNow == 0 )        set warpTmr to 0endifif ( swimChk == 0 )  ;we don't know if there's water in this cell or not        if ( curCell == 0 )  ;exterior always has water                set swimChk to 1        elseif ( GetWindSpeed > 0.001 )  ;interior as exterior                set swimChk to 1        elseif ( GetSoundPlaying "FootWaterLeft" == 1 )  ;in true interiors, can only know if                set swimChk to 1                                                        ;there's water when you've stepped in it        elseif ( GetSoundPlaying "FootWaterRight" == 1 )                set swimChk to 1        elseif ( GetSoundPlaying "DefaultLandWater" == 1 )                set swimChk to 1        endifendif;Grumpy's warping calcsset playZ to ( player->GetPos z )set myZ to ( GetPos z )set ax to ( Player->GetPos x )set ay to ( Player->GetPos y )if ( doOnce1 == 0 )        set bx to ( Player->GetPos x )        set by to ( Player->GetPos y )        set bz to playZ        set doOnce1 to 1endifset t1 to ( ax - bx )set t1 to ( t1 * t1 )set t2 to ( ay - by )set t2 to ( t2 * t2 )set t1 to ( t1 + t2 )set coDist to ( GetSquareRoot t1 )if ( coDist > 360 )        set doOnce1 to 0endifif ( coDist > 180 )        if ( doOnce2 == 0 )                set cx to ( Player->GetPos x )                set cy to ( Player->GetPos y )                set cz to playZ                set doOnce2 to 1        endifendifset t3 to ( ax - cx )set t3 to ( t3 * t3 )set t4 to ( ay - cy )set t4 to ( t4 * t4 )set t3 to ( t3 + t4 )set coDist2 to ( GetSquareRoot t3 )if ( coDist2 > 360 )        set doOnce2 to 0endif;the actual warping bitif ( warpTmr > 6 )        if ( pcDist > 680 )                if ( coDist > 350 )                        set doorFix to -1                        set warpTmr to 0                        SetPos x bx                        SetPos y by                        SetPos z bz                        AIFollow Player 0 0 0 0                        return                elseif ( coDist2 > 350 )                        set doorFix to -1                        set warpTmr to 0                        SetPos x cx                        SetPos y cy                        SetPos z cz                        AIFollow Player 0 0 0 0                        return                endif        endifendifif ( cmbtChk != 0 )elseif ( doorFix != 0 )elseif ( followNow == 0 )elseif ( wndrTmr < 500 )elseif ( GetEffect sEffectLevitate )  ;check geteffect again in case the pc casts on her        set matchZ to 1elseif ( swimChk == 1 )        set swimLvl to ( ( GetWaterLevel ) - 119.7 )  ;check if actually *in* the water, not just wading        if ( myZ <= swimLvl )   ;we're swimming                set swimLvl to ( swimLvl - 12 )                set tmpF to ( swimLvl - 20 )                if ( myZ <= swimLvl )   ;companion is underwater                        set matchZ to 1                elseif ( playZ < tmpF ) ;player underwater                        set matchZ to 1                endif        endifendif;this bit moves the companion gradually towards the player's z-axis position;still has same issue as Grumpy's companions in that it can't detect collision;so can end up stuck in stairs etc - but that fixes itself soon enoughif ( matchZ == 1 )        set matchZ to 0        set tmpF to ( myZ - playZ )        if ( tmpF != 0 )                if ( tmpF >= 5 )                        set myZ to ( myZ - 5 )                elseif ( tmpF <= -5 )                        set myZ to ( myZ + 5 )                else                        set myZ to playZ                endif                SetPos z myZ        endifendifif ( cmbtChk == 1 )        set counter to 0        returnelseif ( counter < 20 )            ;  ---------- COUNTER ----------        set counter to ( counter + 1 )        returnendifset counter to 0if ( GetPCCell "cell where you want potions to restock" )  ;need to add id here!; or if you prefer dialogue for this just delete the block; all the if-checks are in case you ever want to set the vars higher; this way they won't be reduced, only increased    if ( pv_lev < 3 )        set pv_lev to 3    endif    if ( pv_cureC < 3 )        set pv_cureC to 3    endif    if ( pv_health < 3 )        set pv_health to 3    endif    if ( pv_wwalk < 3 )        set pv_wwalk to 3    endif    if ( pv_dispel < 3 )        set pv_dispel to 3    endif    if ( pv_meds < 5 )        set pv_meds to 5    endifendif;in theory Day should be less accurate here than DaysPassed; - but Day doesn't require any particular master file (don't know; if this is tribunal-dependent?); - plus the inaccuracy is very unlikely to happen, and the player probably; won't notice even if it does!if ( pv_meds == 0 )    ;don't do anything yet - this will let her take a potion as soon as she gets oneelseif ( storeDay != Day )    set storeDay to Day    set pv_meds to ( pv_meds - 1 )    AddSpell "illy_cureBlight"  ;placeholder ids    AddSpell "illy_restoreAttribs"    set cureTmr to 1    if ( pv_meds == 1 )        MessageBox "insert message warning player there's only 1 dose left"    endifendifif ( GetEffect sEffectCorpus )  ;note that GetBlightDisease will return true before GetEffect can detect corprus    RemoveEffects 132  ;effect ID for corprus - this needs testing btw, might have to use an ability-type spell instead        set sick to 0                   ;   (note: if the above doesn't work, SfD will need updating!)elseif ( sick == 2 )    if ( ( GetJournalIndex "cure quest ID" ) >= value )  ;PLACEHOLDER - assuming here that cure quest (if you do one) will have a journal        AddSpell "illy_cureBlight"  ;placeholder ID, change as you like        set cureTmr to 1    endif   ;assuming nothing should be done here otherwise? (wait for daily medicine dose?)elseif ( sick == 1 )    if ( pv_cureC > 0 )        set pv_cureC to ( pv_cureC - 1 )        PlaySound3d "swallow"        PlaySound3d "restoration hit"        AddSpell "illy_cureCommon"  ;another placeholder ID        set cureTmr to 1            ;   all these spells should all be type ability    endifendif;disease check - can be dealt with in script or dialogue or both as you likeif ( GetBlightDisease )    set sick to 2elseif ( GetCommonDisease )    set sick to 1else    set sick to 0endif;these are in case your NPC goes swimming - NPCs are too;stupid to keep their heads above water :P;acrobatics is mostly for teleport doors - prevents damage if NPC is slowfalling etcif ( GetWaterBreathing != 1 )    SetWaterBreathing 1endifif ( GetAcrobatics < 199 )    SetAcrobatics 200endifModCurrentFatigue 10  ;companions run a lot while in follow mode, this keeps their                                                ;fatigue up (but won't take effect in combat);emergency warp straight to player position;should only happen when there's no valid warp point storedif ( followNow == 0 )elseif ( pcDist > 1024 )        if ( warpTmr > 9 )                set doorFix to -1                SetPos x ax                SetPos y ay                SetPos z playZ                AIFollow player 0 0 0 0                return        endifendifend

User avatar
Jamie Lee
 
Posts: 3415
Joined: Sun Jun 17, 2007 9:15 am

Post » Fri Jul 08, 2011 11:17 am

to enter default wander mode, try
set followNow to 0
to enter back follow mode, try
set followNow to 1

by the way, you should set this section to proper quest id and numeric value as intended or keep it commented else it will not compile/may cause problems
elseif ( sick == 2 )	; commented until ready /abot if ( ( GetJournalIndex "cure quest ID" ) >= value )  ;PLACEHOLDER - assuming here that cure quest (if you do one) will have a journal		; AddSpell "illy_cureBlight"  ;placeholder ID, change as you like		; set cureTmr to 1	; commented until ready /abot endif   ;assuming nothing should be done here otherwise? (wait for daily medicine dose?)

User avatar
jason worrell
 
Posts: 3345
Joined: Sat May 19, 2007 12:26 am

Post » Fri Jul 08, 2011 4:29 am

to enter default wander mode, try
set followNow to 0
to enter back follow mode, try
set followNow to 1

by the way, you should set this section to proper quest id and numeric value as intended or keep it commented else it will not compile/may cause problems
elseif ( sick == 2 )	; commented until ready /abot if ( ( GetJournalIndex "cure quest ID" ) >= value )  ;PLACEHOLDER - assuming here that cure quest (if you do one) will have a journal		; AddSpell "illy_cureBlight"  ;placeholder ID, change as you like		; set cureTmr to 1	; commented until ready /abot endif   ;assuming nothing should be done here otherwise? (wait for daily medicine dose?)



Hello Abot - thank you for dropping by

I have had eactly as you suggested except the variable follownow was changed to the global BM_Maidservice - this got away from issues with other companions and quests that use the follownow variable

This is melian's orginal message to me

Sorry for the late reply - I'm on slow internet for a while

If you use a follow global, that would be good to use in the script too (more reliable than checking AI package). You could just set the global from dialogue results, and replace the followNow variable in the script with the global, since that's what it's trying to work out (whether or not she's supposed to be following).

This bit won't be needed if you use a global:
if ( wndrTmr < 500 )
elseif ( doorFix != 0 )
elseif ( GetCurrentAIPackage == 3 )
set followNow to 1
elseif ( GetCurrentAIPackage != -1 ) ;if AI is invalid, this assumes the last valid package
set followNow to 0 ;is still the correct one
set warpTmr to 0
endif


But you'll need to reset the warp timer if she's not in follow mode, something like this needs to be in the script somewhere instead:
if ( follow_global == 0 )
set warpTmr to 0
endif


Actually, now that I think about it... "followNow" wasn't such a good idea for the variable id, because it's used by nearly all vanilla followers (all except Rabinna, I think?), so mods use it to identify followers for things (which you may not want done).


I use this global for other features such as enabling clones of her at specific times - when a clone is enabled the original companion is sent to a holding cell so I need to make sure she doesn't warp back to me.

So I had added another section which said

If ( BM_Maidservice == 2 )
Aiwander 0 0 0 30 .... (can't remember the actual idles)
endif

This would lock her in place but then i can't do anything else with her

As for the bit of the script at the end - yes that is well cleaned with the right ids - I posted the original script as given to me by Melian
User avatar
Danger Mouse
 
Posts: 3393
Joined: Sat Oct 07, 2006 9:55 am


Return to III - Morrowind