begin illyCompanionLS
short 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 one
short 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 script
short oldCell ;type of previous cell, interior=1, exterior=0
short curCell ;type of current cell (as above)
short matchZ ;warp needed on z-axis? for levitation/swimming
short cmbtChk ;combat check
short wwChk ;do-once for taking waterwalking potion
short 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 disease
short attrDmg
short doorFix ;state variable for load-door fix
short doOnce1 ;doonce vars for Grumpy's warping code/save warp destination points
short doOnce2
short 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 ids
short pv_lev ;levitation
short pv_cureC ;cure common disease
short pv_health ;restore health
short pv_wwalk ;water walking
short 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 warping
float ay
float bx
float by
float bz
float cx
float cy
float cz
float t1 ;temp vars used in warping code
float t2 ;these "t*" vars can be reused as temp vars in dialogue results if needed
float t3
float t4
float coDist ;distance checks for warping
float coDist2
float pcDist ;distance to player - GetDistance is slow, it's cheaper to check once per frame
float warpTmr ;time out for warping
float 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-out
float cureTmr ;timer for cure/restore abilities to simulate potion use
float prevX ;check for change in x-position - more reliable than cellChanged
float currX
float swimLvl ;used to check if swimming
float playZ
float myZ ;current z-position
float 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 )
return
elseif ( 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
return
elseif ( GetPCTraveling )
set doorFix to 1 ;wander to get out of pile-up after fast travel
return
elseif ( MenuMode )
return
elseif ( GetFatigue < 1 )
return
elseif ( GetParalysis > 0 )
return
endif
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
;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 startscript
set tmpS to 0
set prevX to currX
set currX to ( GetPos x )
set tmpF to ( currX - prevX )
if ( doorFix == -1 ) ;just warped, false alarm
set doorFix to 0
elseif ( 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
endif
endif
if ( 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
endif
endif
set 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
endif
endif
if ( 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
return
endif
;remove spell abilities when done
if ( 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"
endif
endif
;check health and run or teleport away if necessary
;just remove this block if you don't want it
if ( 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
return
elseif ( 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
endif
elseif ( GetFlee > 90 )
SetFlee 10 ;change to whatever your default is
endif
;give her a health potion if health is low
;change health percent value to whatever you want
if ( 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
endif
endif
;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 block
if ( 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!
endif
elseif ( GetWeaponDrawn )
set cmbtChk to 1
set sheathe to 5
set warpTmr to -2
elseif ( GetSpellReadied )
set cmbtChk to 1
set sheathe to 5
set warpTmr to -2
endif
;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 0
elseif ( GetEffect sEffectDamageAttribute )
set attrDmg to 1
elseif ( attrDmg == 1 )
set attrDmg to 2
endif
if ( 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 0
endif
if ( 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
endif
endif
;Grumpy's warping calcs
set 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 1
endif
set 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 0
endif
if ( 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
endif
endif
set 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 0
endif
;the actual warping bit
if ( 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
endif
endif
if ( 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 1
elseif ( 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
endif
endif
;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 enough
if ( 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
endif
endif
if ( cmbtChk == 1 )
set counter to 0
return
elseif ( counter < 20 ) ; ---------- COUNTER ----------
set counter to ( counter + 1 )
return
endif
set counter to 0
if ( 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
endif
endif
;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 one
elseif ( 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"
endif
endif
if ( 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
endif
endif
;disease check - can be dealt with in script or dialogue or both as you like
if ( GetBlightDisease )
set sick to 2
elseif ( GetCommonDisease )
set sick to 1
else
set sick to 0
endif
;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 etc
if ( GetWaterBreathing != 1 )
SetWaterBreathing 1
endif
if ( GetAcrobatics < 199 )
SetAcrobatics 200
endif
ModCurrentFatigue 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 stored
if ( 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
endif
endif
end[/codebox]
Load door fix/warp (this is called from the companion local script when you go through a loaddoor, use fast travel, teleport with the companion, etc)
begin illy_loaddoorfix;quick warp, used for teleport door fix when companion ends up too far away (disappears);must be targeted script as local script setPos is not reliable here;this script can be used for other followers as well (but if there are many followers at; once I'd have a few different scripts, since they'll have to wait til one's available)short statefloat timerfloat warpxfloat warpyfloat warpzif ( MenuMode == 1 ) returnelseif ( GetHealth < 1 ) ;mostly a check for script losing its target (GetHealth set state to 0 ; will return 0 if that happens) set timer to 0 StopScript illy_loaddoorfix returnelseif ( state == 0 ) if ( timer < 0.2 ) set timer to ( timer + GetSecondsPassed ) return endif set timer to 0 set warpx to ( player->GetPos x ) set warpy to ( player->GetPos y ) set warpz to ( player->GetPos z ) set state to 10elseif ( state == 10 ) SetPos x warpx SetPos y warpy SetPos z warpz disable enable AIFollow player 0 0 0 0 set state to 20 returnelseif ( state == 20 ) if ( timer < 0.1 ) set timer to ( timer + GetSecondsPassed ) return endif set state to 0 set timer to 0 if ( ( GetDistance player ) > 2048 ) ;if still too far away, try again if ( ( GetDistance player ) < 999999 ) ;but give up if actually in a different cell! return endif endif StopScript illy_loaddoorfixendifend
Game load detection (to stop companion wandering on game load as well). For this you need to add a global variable (called "mel_reload" here, you can use that or change it if you want). The variable should just have default settings (short, default 0).
begin mel_checkloadfloat timerif ( timer < 0.5 ) set timer to ( timer + GetSecondsPassed ) if ( mel_reload == 0 ) set mel_reload to 1 endifelse set timer to 0 set mel_reload to 0 StopScript mel_checkloadendifend
So, let me know what you want done with it etc. And good luck with the escort!