Begin _GRM_CompanionRing
; Version 1.0.
;
; Grant McDorman 14 January 2007
;
; This script provides a generic "companion recall" ring. It can be used
; with any NPC that is in "follow" mode (i.e. that is following you).
;
; There is no associated dialog.
;
; It is "tuned to" (associated with) a companion by putting it on.
; The player is then presented with a list of nearby NPCs who are in
; follow mode (up to 8); the player can select an NPC from the list
; or cancel.
;
; Once "tuned", attempting to equip the ring will not actually equip it,
; but will take various actions:
; * If the NPC cannot be found or is dead, a message will be displayed
; and the player will have the option of resetting the ring.
; * If the NPC is within sight (can see the player), then the player
; is presented with the options of talking to the NPC or resetting the
; ring.
; * Finally, if the NPC is not within sight, the player is presented with
; a message giving the NPC's location, and giving the options of talking,
; teleporting the NPC here, resetting the ring, or doing nothing.
;
; Note that currently, no effect is played when the NPC is "summoned" (since
; this is a ring, and in any event it doesn't seem to be possible to force
; a player cast effect).
;
; Each ring can be associated with a unique NPC. They do not interfere with
; each other.
;
; Inspired by various generic companion teleport/communication rings.
short OnPCEquip
short PCSkipEquip
long newCompanionRef ; Set to ID (internal name) of companion when ring
; is associated with a companion.
short initMessage ; display the initialization message
short initCheck ; check which, if any, companion was selected to attach
short displayMessage ; display the initial communication message
long discard ; temporary variable, for various uses
; ID of associated companion, saved as long
; max 4*16 = 64 characters
long n00
long n01
long n02
long n03
long n04
long n05
long n06
long n07
long n08
long n09
long n10
long n11
long n12
long n13
long n14
long n15
float disp ; disposition, used when "nothing" is selected
float health ; Health. Used to check for dead companion.
short disabled ; Enabled/disabled state.
short canSee ; Companion can see player.
short checkInSight ; Check answer to in sight query ("talk", "reset ring")
short checkReset ; Check answer to reset query when companion can't be detected.
short checkAnswer ; Message is displayed, check for button press.
short button ; Button pressed.
short teleportHere ; Player chose "come here" option.
short forceGreet ; Player chose "talk" option.
short noAction ; Player chose "nothing" option.
short polite ; Respond politely (or not) when player choses "nothing."
long companionID ; ID of companion.
long companionRef ; reference to companion
long companionName ; companion's display name
long cellName ; companion's cell name, if available
; Player's location.
float px
float py
float pz
float pa
; Setup variables; up to 8 companions will be examined.
long companion1 ; References to companions (not IDs or names).
long companion2
long companion3
long companion4
long companion5
long companion6
long companion7
long companion8
long name1 ; Display names of the companions (xGetName).
long name2
long name3
long DoNotUse_long ; long #34, do not use due to MW bugs
long name4
long name5
long name6
long name7
long name8
; If the ring is "tuned", do not equip it.
if (n00 != 0)
set PCSkipEquip to 1
endif
; Don't do anything when a menu is active.
if ( MenuMode != 0)
return
endif
; If equipped, either display the normal message or the tuning message.
if (OnPCEquip != 0)
if (n00 != 0)
set displayMessage to 1
else
set initMessage to 1
endif
set OnPCEquip to 0
endif
; Set up the ring to attach to a companion.
ifx (initMessage)
; Find companions in this cell.
set companion1 to 0
set companion2 to 0
set companion3 to 0
set companion4 to 0
set companion5 to 0
set companion6 to 0
set companion7 to 0
set companion8 to 0
setx companionRef to xFirstNPC
whilex (companionRef)
xSetRef companionRef
set discard to GetCurrentAIPackage
if (discard != 3)
set discard to 0
endif
ifx (discard)
if (companion1 == 0)
set companion1 to companionRef
elseif (companion2 == 0)
set companion2 to companionRef
elseif (companion3 == 0)
set companion3 to companionRef
elseif (companion4 == 0)
set companion4 to companionRef
elseif (companion5 == 0)
set companion5 to companionRef
elseif (companion6 == 0)
set companion6 to companionRef
elseif (companion7 == 0)
set companion7 to companionRef
elseif (companion8 == 0)
set companion8 to companionRef
else
endif
endif
setx companionRef to xNextRef companionRef
endwhile
ifx (companion1)
setx name1 to companion1->xGetName
else
MessageBox "There are no companions nearby to tune the ring to. (The companion has to be following you for the tuning to work.)"
set initMessage to 0
return
endif
ifx (companion2)
setx name2 to companion2->xGetName
else
xMessageFix "Tune to?" name1 "Cancel"
MessageBox "Tune to?" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "Cancel"
set initMessage to 0
set initCheck to 1
return
endif
ifx (companion3)
setx name3 to companion3->xGetName
else
xMessageFix "Tune to?" name1 name2 "Cancel"
MessageBox "Tune to?" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "Cancel"
set initMessage to 0
set initCheck to 1
return
endif
ifx (companion4)
setx name4 to companion4->xGetName
else
xMessageFix "Tune to?" name1 name2 name3 "Cancel"
MessageBox "Tune to?" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "Cancel"
set initMessage to 0
set initCheck to 1
return
endif
ifx (companion5)
setx name5 to companion5->xGetName
else
xMessageFix "Tune to?" name1 name2 name3 name4 "Cancel"
MessageBox "Tune to?" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "Cancel"
set initMessage to 0
set initCheck to 1
return
endif
ifx (companion6)
setx name6 to companion6->xGetName
else
xMessageFix "Tune to?" name1 name2 name3 name4 name5 "Cancel"
MessageBox "Tune to?" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "Cancel"
set initMessage to 0
set initCheck to 1
return
endif
ifx (companion7)
setx name7 to companion7->xGetName
else
xMessageFix "Tune to?" name1 name2 name3 name4 name5 name6 "Cancel"
MessageBox "Tune to?" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "Cancel"
set initMessage to 0
set initCheck to 1
return
endif
ifx (companion8)
setx name8 to companion8->xGetName
else
xMessageFix "Tune to?" name1 name2 name3 name4 name5 name6 name7 "Cancel"
MessageBox "Tune to?" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "Cancel"
set initMessage to 0
set initCheck to 1
return
endif
xMessageFix "Tune to?" name1 name2 name3 name4 name5 name6 name7 name8 "Cancel"
MessageBox "Tune to?" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456" "Cancel"
set initMessage to 0
set initCheck to 1
return
endif
; Check which button was pressed in response to the initial tuning
; message. Note that the "last" button is always cancel, even if there
; are fewer than 8 NPCs found; if there are fewer than 8, then
; the associated variable (companion2-8) will be zero.
ifx (initCheck)
set newCompanionRef to 0
set button to GetButtonPressed
if (button == -1)
return
endif
set initCheck to 0
if (button == 0)
set newCompanionRef to companion1
elseif (button == 1)
set newCompanionRef to companion2
elseif (button == 2)
set newCompanionRef to companion3
elseif (button == 3)
set newCompanionRef to companion4
elseif (button == 4)
set newCompanionRef to companion5
elseif (button == 5)
set newCompanionRef to companion6
elseif (button == 6)
set newCompanionRef to companion7
elseif (button == 7)
set newCompanionRef to companion8
set initCheck to 0
endif
; If a button corresponding to a companion was selected,
; convert the companion's ID into long variables (so it gets stored
; with saves).
ifx (newCompanionRef)
setx companionID to newCompanionRef->xRefID
xFileRewind "grm_companionring_temp"
xFileWriteString "grm_companionring_temp", companionID
; fill with zeros so the xFileReadLong below will read all 16
; values
set discard to 16
whilex (discard)
xFileWriteLong "grm_companionring_temp", 0
set discard to discard-1
endwhile
xFileRewind "grm_companionring_temp"
set n00 to 0
set n01 to 0
set n02 to 0
set n03 to 0
set n04 to 0
set n05 to 0
set n06 to 0
set n07 to 0
set n08 to 0
set n09 to 0
set n10 to 0
set n11 to 0
set n12 to 0
set n13 to 0
set n14 to 0
set n15 to 0
setx discard, n00, n01, n02, n03, n04, n05, n06, n07, n08, n09, n10, n11, n12, n13, n14, n15 to xFileReadLong "grm_companionring_temp" 16
xFileRewind "grm_companionring_temp"
setx companionName to newCompanionRef->xGetName
xMessageFix "The ring is now tuned to %s.", companionName
MessageBox "The ring is now tuned to ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456."
set newCompanionRef to 0
return
else
MessageBox "Cancelled."
endif
endif
; Display the initial communication message.
; This converts the stored form back to a string (thus avoiding
; worries about save/restore; since this is in a dialog/menu,
; performance is not a problem).
ifx (displayMessage)
set displayMessage to 0
; Convert long variables to strings.
xFileRewind "grm_companionring_temp"
xFileWriteLong "grm_companionring_temp", n00
xFileWriteLong "grm_companionring_temp", n01
xFileWriteLong "grm_companionring_temp", n02
xFileWriteLong "grm_companionring_temp", n03
xFileWriteLong "grm_companionring_temp", n04
xFileWriteLong "grm_companionring_temp", n05
xFileWriteLong "grm_companionring_temp", n06
xFileWriteLong "grm_companionring_temp", n07
xFileWriteLong "grm_companionring_temp", n08
xFileWriteLong "grm_companionring_temp", n09
xFileWriteLong "grm_companionring_temp", n10
xFileWriteLong "grm_companionring_temp", n11
xFileWriteLong "grm_companionring_temp", n12
xFileWriteLong "grm_companionring_temp", n13
xFileWriteLong "grm_companionring_temp", n14
xFileWriteLong "grm_companionring_temp", n15
xFileRewind "grm_companionring_temp"
setx companionID to xFileReadString "grm_companionring_temp"
setx companionRef to xGetRef companionID
ifx (companionRef)
; Make sure the companion is alive and enabled.
; (Fatigue is not checked.)
xSetRef companionRef
set health to GetHealthGetRatio
if (health > 0)
xSetRef companionRef
set disabled to GetDisabled
else
set disabled to 1
endif
; Regrettably there is no way of checking local variables
; (to wit, 'companion'). Using AI (GetCurrentAIPackage) is not
; appropriate, since the companion could have been told to wait
; or guard.
setx companionName to companionRef->xGetName
ifx (disabled)
; The companion is disabled or dead.
xMessageFix "There is no response from %s. Reset the ring?", companionName, "Yes", "No"
MessageBox "There is no response from ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456. Reset the ring?", "Yes", "No"
set checkReset to 1
else
; If the companion can see the player just force greeting.
xSetRef companionRef
set canSee to GetLOS Player
ifx (canSee)
xMessageFix "%s is within sight of you. What do you want to do?", companionName, "Talk", "Reset Ring"
MessageBox "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456 is within sight of you. What do you want to do?", "Talk", "Reset Ring"
set checkInSight to 1
return
endif
; Can't see the player.
setx cellName to companionRef->XMyCellID
; Under some circumstances (possibly after 72 hours alone?), the
; cell name of the companion can no longer be retrieved.
ifx (cellName)
xMessageFix "%s: Hi, ^PCName. I'm in %s. What is it?", companionName, cellName, "Come Here", "Talk", "Nothing", "Reset Ring"
MessageBox "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456: Hi, ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456. I'm in ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456. What is it?", "Come Here", "Talk", "Nothing", "Reset Ring"
set checkAnswer to 1
else
xMessageFix "%s: Hi, ^PCName. Um, I'm lost. What is it?", companionName, "Come Here", "Talk", "Nothing", "Reset Ring"
MessageBox "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456: Hi, ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456. Um, I'm lost. What is it?", "Come Here", "Talk", "Nothing", "Reset Ring"
set checkAnswer to 1
endif
endif
else
xLogMessage "_GRM_Companion_Recall_Ring: Couldn't find companion %s.", companionID
ifx (n00)
MessageBox "The ring vibrates but does nothing. Perhaps it can't detect your companion. Should it be reset?", "Yes", "No"
set checkReset to 1
else
MessageBox "The ring vibrates but does nothing. Did you tune it to a companion?", "OK"
endif
endif
return
endif
; Check which button was pressed when the ring can't find the companion,
; or the companion is dead or disabled.
ifx (checkReset)
set button to GetButtonPressed
if (button == -1)
return
endif
set checkReset to 0
if (button == 0)
set n00 to 0
endif
ifx (n00)
else
xMessageFix "The ring is no longer tuned to %s.", companionName
MessageBox "The ring is no longer tuned to ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456."
endif
endif
; Check which button was pressed for in-sight communication.
ifx (checkInSight)
set button to GetButtonPressed
if (button == -1)
return
endif
set checkInSight to 0
ifx (button) ; reset
set n00 to 0
else ; talk
xSetRef companionRef
ForceGreeting
endif
ifx (n00)
else
xMessageFix "The ring is no longer tuned to %s.", companionName
MessageBox "The ring is no longer tuned to ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456."
endif
endif
; Check to see which button was pressed, if any, for distance communication.
ifx (checkAnswer)
set button to GetButtonPressed
if (button == -1)
return
endif
set checkAnswer to 0
if (button == 0)
set teleportHere to 1
elseif (button == 1)
set forceGreet to 1
elseif (button == 2)
set noAction to 1
elseif (button == 3)
set n00 to 0
endif
ifx (n00)
else
xMessageFix "The ring is no longer tuned to %s.", companionName
MessageBox "The ring is no longer tuned to ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456."
endif
endif
; "Come Here" was chosen. Teleport companion here.
ifx (teleportHere)
xMessageFix "You summon %s to you with the ring.", companionName
MessageBox "You summon ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456 to you with the ring."
; Acquired from Improved Teleportation 2.0.
; Get player's current location
set px to player->GetPos X
set py to player->GetPos Y
set pz to player->GetPos Z
set pa to player->GetAngle Z
; Move companion slightly in front of player
; [note: what if player has their nose in a wall? - grm]
set pz to pz - 25
if ( pa < 60 )
if ( pa > -60 )
set py to py + 50
elseif ( pa < -120 )
set py to py - 50
endif
elseif ( pa > 120 )
set py to py - 50
endif
if ( pa > 30 )
if ( pa < 150 )
set px to px + 50
endif
elseif ( pa < -30 )
if ( pa > -150 )
set px to px - 50
endif
endif
set pa to pa + 180
setx cellName to xPCCellId
companionRef->xPositionCell px py pz pa cellName
xSetRef companionRef
Disable
xSetRef companionRef
Enable
set teleportHere to 0
endif
; "Talk" was chosen.
ifx (forceGreet)
xSetRef companionRef
ForceGreeting
set forceGreet to 0
endif
; "Nothing" was chosen.
ifx (noAction)
xSetRef companionRef
set disp to GetDisposition
if (disp > 75)
set polite to 1
else
set polite to 0
endif
ifx (polite)
xMessageFix "%s: Nothing? OK, talk to you later.", companionName
MessageBox "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456: Nothing? OK, talk to you later."
else
xMessageFix "%s: Nothing? Then why did you bother me? *grumble*", companionName
MessageBox "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456: Nothing? Then why did you bother me? *grumble*"
endif
set noAction to 0
endif
end