[WIP] Improving companion teplate

Post » Wed Jun 29, 2016 2:35 pm

Hello,


recently I had problem with Fliggerty's slave mod here. It was caused by setting in MCP, but in the meantime I discovered some flaws in the original script, that as far as I know uses Grumpy's companion template. I decided I will try to improve it a bit. Right now, I'm applying the fixes to Fliggerty's slave mod, as that's the one I started to work with, but eventually I hope to create updated version of companion template. Unfortunatelly, I'm not very experienced with companion scripting, so any thoughts and code explanations are highly appreciated.



Original slaveScript from Fliggerty's Slave mod



Spoiler


begin slaveScript

;Slave Status... each slave has a status as follows
;0 = Owned ( default state... does not indicate who they are owned by )
;1 = For Sale ( someone in the game can "sell" this slave via dialogue )
;2 = Owned By the Player ( the player has purchased this slave and the slave follows the player )
;3 = Freed ( the player has freed this slave, the slave eventually disables himself)
;4 = Freed but loyal to Player
;dead = not in slave status, but a state one should check for

short slaveStatus
short NoLore
short loyalty
short OnPCHitMe

float ax
float ay
float bx
float by
float bz
float cx
float cy
float cz
float t1
float t2
float t3
float t4
float coDist
float coDist2
float playz
float timer
float sheatheTimer
float moveTimer
float h_check
short doOnce
short doOnce2
short combatDone
short weapUse
short combatCheck
short soundPlayed
short flyCheck
short wwCheck
short oneTimeMove
short compMove
short c_move
short warp
short c_marksman
short c_speed
short c_athletics
short companion
short nohello
short counter
short addOnce
short p_Check

if ( addOnce == 0 )
AddTopic "-- slave care"
AddTopic "-- slave combat"
AddTopic "-- slave follow"
AddTopic "-- slave commands"
StartScript fs_compLevel
set companion to 1
set addOnce to 1
endif

if ( slaveStatus == 0 )
if ( OnPCHitMe == 1 )
StartCombat player
ModPCCrimeLevel 500
set OnPCHitMe to 0
endif
set companion to 0
return
endif

if ( slaveStatus == 1 )
if ( OnPCHitMe == 1 )
StartCombat player
ModPCCrimeLevel 500
set OnPCHitMe to 0
endif
set companion to 0
return
endif

if ( slaveStatus == 2 )
set companion to 1
endif

if ( slaveStatus == 3 )
if ( GetCurrentAIPackage == 3 )
AIWander 512 0 0 0 0 0 0 0 0 0 0 0
endif
if ( GetItemCount Slave_Bracer_Left > 0 )
Drop Slave_Bracer_Left 1
endif
if ( GetItemCount Slave_Bracer_Right > 0 )
Drop Slave_Bracer_Right 1
endif
if ( CellChanged == 1 )
Disable
endif
set companion to 0
Return
endif

if ( slaveStatus == 4 )
set companion to 1
if ( GetItemCount Slave_Bracer_Left > 0 )
Drop Slave_Bracer_Left 1
endif
if ( GetItemCount Slave_Bracer_Right > 0 )
Drop Slave_Bracer_Right 1
endif
endif

if ( MenuMode == 1 )
if ( GetPCSleep == 0 )
return
endif
endif

if ( weapUse == 1 )
if ( Player->GetWeaponType <= 8 )
setmarksman 0
elseif ( weapUse == 1 )
if ( Player->GetWeaponType >= 9 )
setmarksman 200
endif
endif
endif

if ( OnPCHitMe == 1 )
if ( GetDisposition < 10 )
if ( GetFight < 80 )
MessageBox "That's the last straw!"
SetFight 100
set OnPCHitMe to 0
AIWander 0 0 0 0
endif
else
if ( loyalty > 0 )
set loyalty to ( loyalty -1 )
endif
ModDisposition -5
set OnPCHitMe to 0
endif
endif

if ( combatCheck == 1 )
set sheathetimer to sheatheTimer + GetSecondsPassed
if ( GetSoundPlaying, "Weapon Swish" == 1 )
set sheatheTimer to 0
return
elseif ( GetSoundPlaying, crossbowShoot == 1 )
set sheatheTimer to 0
return
elseif ( GetSoundPlaying, bowShoot == 1 )
set sheatheTimer to 0
return
elseif ( sheatheTimer > 4 )
if ( GetSoundPlaying "Weapon Swish" == 0 )
set sheatheTimer to 0
set soundPlayed to 1
elseif ( sheatheTimer > 4 )
if ( GetSoundPlaying "crossbowShoot" == 0 )
set sheatheTimer to 0
set soundPlayed to 1
elseif ( sheatheTimer > 4 )
if ( GetSoundPlaying "bowShoot" == 0 )
set sheatheTimer to 0
set soundPlayed to 1
endif
endif
endif
endif
endif

if ( soundPlayed == 1 )
equip "silver dagger"
removeitem "silver dagger" 1
; messagebox "Working"
set soundPlayed to 0
set combatCheck to 0
set combatDone to 1
endif

if ( combatDone == 1 )
set timer to timer + GetSecondsPassed
if ( timer > 6 )
set timer to 0
set combatDone to 0
set warp to 0
endif
endif

if ( GetCurrentAIPackage == 3 )
if ( GetPCSleep == 1 )
StartScript fs_compLevel
endif

if ( GetPCSneaking == 1 )
ForceSneak
elseif ( GetPCSneaking == 0 )
ClearForceSneak
endif

if ( Player->GetEffect sEffectLevitate == 1 )
if ( flyCheck == 0 )
cast fs_comp2_lev_bog player
addspell fs_comp2_lev
set flyCheck to 1
endif
endif

if ( Player->GetEffect sEffectLevitate == 0 )
if ( flyCheck == 1 )
removespell fs_comp2_lev
set flyCheck to 0
endif
endif

if ( Player->GetEffect sEffectWaterWalking == 1 )
if ( wwCheck == 0 )
cast fs_comp2_ww_bog player
addspell fs_comp2_ww
set wwCheck to 1
endif
endif

if ( Player->GetEffect sEffectWaterWalking == 0 )
if ( wwCheck == 1 )
removespell fs_comp2_ww
set wwCheck to 0
endif
endif

if ( flyCheck == 1 )
if ( GetWeaponDrawn == 0 )
set playz to ( Player->GetPos z )
SetPos z playz
elseif ( flyCheck == 1 )
if ( GetCurrentAIPackage != 3 )
if ( GetWeaponDrawn == 0 )
set playz to ( GetPos z )
SetPos z playz
endif
endif
endif
endif

if ( GetWeaponDrawn == 1 )
if ( flyCheck == 0 )
set combatCheck to 1
set timer to 0
set warp to 1
return
elseif ( GetWeaponDrawn == 1 )
if ( flyCheck == 1 )
set combatDone to 1
set timer to 0
set warp to 1
return
endif
endif
endif

set ax to ( Player->GetPos x )
set ay to ( Player->GetPos y )

if ( doOnce == 0 )
set bx to ( Player->GetPos x )
set by to ( Player->GetPos y )
set bz to ( Player->GetPos z )
set doOnce 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 doOnce to 0
endif

if ( coDist > 180 )
if ( doOnce2 == 0 )
set cx to ( Player->GetPos x )
set cy to ( Player->GetPos y )
set cz to ( Player->GetPos z )
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

if ( warp == 0 )
if ( coDist > 350 )
if ( GetDistance Player > 680 )
SetPos x bx
SetPos y by
SetPos z bz
AiFollow Player 0 0 0 0
elseif ( warp == 0 )
if ( coDist2 > 350 )
if ( GetDistance Player > 680 )
SetPos x cx
SetPos y cy
SetPos z cz
AiFollow Player 0 0 0 0
endif
endif
endif
endif
endif
endif

;IF ADDITIONS ARE DESIRED, PLEASE ADD THEM BELOW HERE...

if ( GetCurrentAIPackage == 3 )
if ( compMove == 1 )
if ( c_move == 0 )
if ( GetDistance Player < 70 )
AIWander 300 0 0 0 0
set c_move to 1
endif
endif
endif
endif

if ( c_move == 1 )
if ( GetDistance Player > 100 )
AIFollow Player 0 0 0 0
set c_move to 0
endif
endif

if ( oneTimeMove == 1 )
set moveTimer to moveTimer + GetSecondsPassed
if ( moveTimer > 4 )
set moveTimer to 0
AiFollow Player 0 0 0 0
set oneTimeMove to 0
endif
endif

;AND ABOVE HERE.

;ADDITIONS CAN ALSO BE ADDED TO THIS FOLLOWING SECTION, BUT BEWARE OF THE COUNTER.
;Counter can pooch some functions if they need to be checked every frame.

if ( counter < 20 )
set counter to counter + 1
return
endif

set counter to 0

if ( GetCurrentAIPackage == 3 )
if ( GetDistance Player < 300 )
setSpeed 15
setAthletics 15
elseif ( GetDistance Player > 300 )
set c_speed to ( ( Player->GetSpeed ) * 2.25 )
set c_athletics to ( ( Player->GetAthletics ) * 2.25 )
setSpeed c_speed
setAthletics c_athletics
endif

if ( GetItemCount p_restore_health_b >= 1 )
set p_Check to 1
elseif ( GetItemCount p_restore_health_c >= 1 )
set p_Check to 1
elseif ( GetItemCount p_restore_health_e >= 1 )
set p_Check to 1
elseif ( GetItemCount p_restore_health_q >= 1 )
set p_Check to 1
elseif ( GetItemCount p_restore_health_s >= 1 )
set p_Check to 1
else
set p_Check to 0
endif
endif

if ( GetCurrentAIPackage != 3 )
setspeed 40
endif

;ADD ADDITIONAL TOPICS IN THIS SECTION IF REQUIRED.

end slaveScript




Current improved slaveScript:



Spoiler


begin slaveScript

;Slave Status... each slave has a status as follows
;0 = Owned ( default state... does not indicate who they are owned by )
;1 = For Sale ( someone in the game can "sell" this slave via dialogue )
;2 = Owned By the Player ( the player has purchased this slave and the slave follows the player )
;3 = Freed ( the player has freed this slave, the slave eventually disables himself)
;4 = Freed but loyal to Player
;dead = not in slave status, but a state one should check for

short slaveStatus
short NoLore
short loyalty
short OnPCHitMe

float ax
float ay
float bx
float by
float bz
float cx
float cy
float cz
float t1
float t2
float t3
float t4
float coDist
float coDist2
float playz
float timer
float sheatheTimer
float moveTimer
float h_check
float playerDistance
short doOnce
short doOnce2
short combatDone
short weapUse
short combatCheck
short soundPlayed
short flyCheck
short wwCheck
short oneTimeMove
short compMove
short c_move
short warp
short c_marksman
short c_speed
short c_athletics
short companion
short nohello
short counter
short addOnce
short p_Check


;doOnce stuff
if ( addOnce == 0 )
;calculate initial player distance
set playerDistance to GetDistance player
AddTopic "-- slave care"
AddTopic "-- slave combat"
AddTopic "-- slave follow"
AddTopic "-- slave commands"
StartScript fs_compLevel
set companion to 1
set addOnce to 1
endif

;slave status stuff
if ( slaveStatus == 0 )
if ( OnPCHitMe == 1 )
StartCombat player
ModPCCrimeLevel 500
set OnPCHitMe to 0
endif
set companion to 0
return
endif

if ( slaveStatus == 1 )
if ( OnPCHitMe == 1 )
StartCombat player
ModPCCrimeLevel 500
set OnPCHitMe to 0
endif
set companion to 0
return
endif

if ( slaveStatus == 2 )
set companion to 1
endif

if ( slaveStatus == 3 )
if ( GetCurrentAIPackage == 3 )
AIWander 512 0 0 0 0 0 0 0 0 0 0 0
endif
if ( GetItemCount Slave_Bracer_Left > 0 )
Drop Slave_Bracer_Left 1
endif
if ( GetItemCount Slave_Bracer_Right > 0 )
Drop Slave_Bracer_Right 1
endif
if ( CellChanged == 1 )
Disable
endif
set companion to 0
Return
endif

if ( slaveStatus == 4 )
set companion to 1
if ( GetItemCount Slave_Bracer_Left > 0 )
Drop Slave_Bracer_Left 1
endif
if ( GetItemCount Slave_Bracer_Right > 0 )
Drop Slave_Bracer_Right 1
endif
endif

;companion script

;if we are in menu and player does not sleep, return
if ( MenuMode == 1 )
if ( GetPCSleep == 0 )
return
endif
endif

;block for fight settings "use weapon I use"
;sets marksman to 0 if player uses Melee weapon
;sets marksman to 200 if player uses Ranged weapon
;SIMPLIFIED
if ( weapUse == 1 )
if ( Player->GetWeaponType <= 8 )
setmarksman 0
elseif ( Player->GetWeaponType >= 9 )
setmarksman 200
endif
endif

;player hit this slave
;if disposition is too low, slave revolts
;otherwise lower disposition
if ( OnPCHitMe == 1 )
if ( GetDisposition < 10 )
if ( GetFight < 80 )
MessageBox "That's the last straw!"
SetFight 100
set OnPCHitMe to 0
AIWander 0 0 0 0
endif
else
if ( loyalty > 0 )
set loyalty to ( loyalty -1 )
endif
ModDisposition -5
set OnPCHitMe to 0
endif
endif

;combat check - some kind of test whether combat is over?
if ( combatCheck == 1 )
set sheathetimer to sheatheTimer + GetSecondsPassed
if ( GetSoundPlaying, "Weapon Swish" == 1 )
set sheatheTimer to 0
return
elseif ( GetSoundPlaying, "crossbowShoot" == 1 )
set sheatheTimer to 0
return
elseif ( GetSoundPlaying, "bowShoot" == 1 )
set sheatheTimer to 0
return
elseif ( sheatheTimer > 4 )
set sheatheTimer to 0
set soundPlayed to 1
endif
endif

;again something with combat, no idea what exactly is it good for
if ( soundPlayed == 1 )
equip "silver dagger"
removeitem "silver dagger" 1
;messagebox "Working"
set soundPlayed to 0
set combatCheck to 0
set combatDone to 1
endif

;if combat is over, toggle warping
if ( combatDone == 1 )
set timer to timer + GetSecondsPassed
if ( timer > 6 )
set timer to 0
set combatDone to 0
set warp to 0
endif
endif

;if slave is in follow mode
if ( GetCurrentAIPackage == 3 )
;if player sleeps, adjust slave skill values
if ( GetPCSleep == 1 )
StartScript fs_compLevel
endif

;force sneak if player is sneaking
if ( GetPCSneaking == 1 )
ForceSneak
elseif ( GetPCSneaking == 0 )
ClearForceSneak
endif

;if player levitates, also levitate
if ( Player->GetEffect sEffectLevitate == 1 )
if ( flyCheck == 0 )
cast fs_comp2_lev_bog player
addspell fs_comp2_lev
set flyCheck to 1
endif
endif

;levitation stop
if ( Player->GetEffect sEffectLevitate == 0 )
if ( flyCheck == 1 )
removespell fs_comp2_lev
set flyCheck to 0
endif
endif

;if player water walks, also water walk
if ( Player->GetEffect sEffectWaterWalking == 1 )
if ( wwCheck == 0 )
cast fs_comp2_ww_bog player
addspell fs_comp2_ww
set wwCheck to 1
endif
endif

;water walk stop
if ( Player->GetEffect sEffectWaterWalking == 0 )
if ( wwCheck == 1 )
removespell fs_comp2_ww
set wwCheck to 0
endif
endif

;SIMPLIFIED, adjusts z position according to player in levitate mode
if ( flyCheck == 1 )
if ( GetWeaponDrawn == 0 )
set playz to ( Player->GetPos z )
SetPos z playz
endif
endif

;SIMPLIFIED, fight start
if ( GetWeaponDrawn == 1 )
set combatCheck to 1
set timer to 0
set warp to 1
return
endif

set ax to ( Player->GetPos x )
set ay to ( Player->GetPos y )

if ( doOnce == 0 )
set bx to ( Player->GetPos x )
set by to ( Player->GetPos y )
set bz to ( Player->GetPos z )
set doOnce 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 t1

if ( coDist > 129600 ) ;360*360 to avoid using GetSquareRoot
set doOnce to 0
endif

if ( coDist > 32400 ) ;180*180
if ( doOnce2 == 0 )
set cx to ( Player->GetPos x )
set cy to ( Player->GetPos y )
set cz to ( Player->GetPos z )
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 t3

if ( coDist2 > 129600 ) ;360*360
set doOnce2 to 0
endif

; New warp, compatible with the Ultimate Galleon door
if ( warp == 0 )
if ( playerDistance > 680 )
if ( coDist > 122500 ) ;350*350
if ( coDist < 462400 ) ;680*680,this prevents the "warping back" effect
SetPos X, bx
SetPos Y, by
SetPos Z, bz
AiFollow player 0 0 0 0
endif
elseif ( coDist2 > 122500 ) ;350*350
if ( coDist2 < 462400 ) ;680*680,this prevents the "warping back" effect
SetPos X, cx
SetPos Y, cy
SetPos Z, cz
AiFollow player 0 0 0 0
endif
endif
endif
endif

endif

;IF ADDITIONS ARE DESIRED, PLEASE ADD THEM BELOW HERE...

if ( GetCurrentAIPackage == 3 )
if ( compMove == 1 )
if ( c_move == 0 )
if ( playerDistance < 70 )
AIWander 300 0 0 0 0
set c_move to 1
endif
endif
endif
endif

if ( c_move == 1 )
if ( playerDistance > 100 )
AIFollow Player 0 0 0 0
set c_move to 0
endif
endif

if ( oneTimeMove == 1 )
set moveTimer to moveTimer + GetSecondsPassed
if ( moveTimer > 4 )
set moveTimer to 0
AiFollow Player 0 0 0 0
set oneTimeMove to 0
endif
endif

;AND ABOVE HERE.

;ADDITIONS CAN ALSO BE ADDED TO THIS FOLLOWING SECTION, BUT BEWARE OF THE COUNTER.
;Counter can pooch some functions if they need to be checked every frame.

if ( counter < 20 )
set counter to counter + 1
return
endif

set counter to 0

if ( GetCurrentAIPackage == 3 )

;recalculate player distance only every 20 frames and if current AI package is follow (3)
set playerDistance to GetDistance player

if ( playerDistance < 300 )
setSpeed 15
setAthletics 15
elseif ( playerDistance > 300 )
set c_speed to ( ( Player->GetSpeed ) * 2.25 )
set c_athletics to ( ( Player->GetAthletics ) * 2.25 )
setSpeed c_speed
setAthletics c_athletics
endif

if ( GetItemCount p_restore_health_b >= 1 )
set p_Check to 1
elseif ( GetItemCount p_restore_health_c >= 1 )
set p_Check to 1
elseif ( GetItemCount p_restore_health_e >= 1 )
set p_Check to 1
elseif ( GetItemCount p_restore_health_q >= 1 )
set p_Check to 1
elseif ( GetItemCount p_restore_health_s >= 1 )
set p_Check to 1
else
set p_Check to 0
endif
endif

if ( GetCurrentAIPackage != 3 )
setspeed 40
endif

;ADD ADDITIONAL TOPICS IN THIS SECTION IF REQUIRED.

end slaveScript




Current improvements:


- Several blocks of code were shortened and simplified. They are marked as 'SIMPLIFIED'.


- Added new warp compatible with Ultimate Galleon mod.


- Removed calls of 'GetsquareRoot'. Where originally was something like 'GetSquareRoot(x) > y' it is now altered to 'x > y*y'.


- Original script calls 'GetDistance player' about 5 times every frame. Altered so it calls it only once every 20 frames.



Any input is highly appreciated :)

User avatar
Assumptah George
 
Posts: 3373
Joined: Wed Sep 13, 2006 9:43 am

Post » Wed Jun 29, 2016 10:55 pm


I'd try moving the lines from


set ax to ( Player->GetPos x )


... to


AiFollow player 0 0 0 0

endif

endif

endif

endif




after the


set counter to 0


if ( GetCurrentAIPackage == 3 )


section


and see what happens

User avatar
Andrew Lang
 
Posts: 3489
Joined: Thu Oct 11, 2007 8:50 pm

Post » Wed Jun 29, 2016 11:35 am

Okay, I wrote another possible improvement part, but super efficient and very very useful spam protection stopped me from posting it and I do not feel like writing 50 lines of code again, so, just in words... what about the slave script part at the beginning? Some of the parts could be run only once when slavestatus changes. Thoughts?





I just tried it but unfortunatelly this prevented slaves from teleporting when I got a little further. I tried to run around a bit and they never teleported. Instead I managed to lost some of them :D Not sure why though, perhaps I always run further than 680 units? Still seems weird.

User avatar
Amber Hubbard
 
Posts: 3537
Joined: Tue Dec 05, 2006 6:59 pm


Return to III - Morrowind