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 ;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