Construction Set Skript: NPC zu eigene Position teleportieren

Kallreven

Ehrbarer Bürger
Moin,
da ich mit der Suche nix darüber gefunden habe und ich wegen Übersicht kein Freund von Alle-Skript-Probleme-in-einem-Thread-packen bin (Skripthread), schreibe ich hier mein Problem:

Ich bastel grad an einer Erweiterung für Partners DV, ich habe eine alte Heldin von mir als NPC erstellt, und ein paar Dialoge mit Scripten hinzugefügt um das Kampfverhalten zu steuern.
Nun habe ich so die Angewohnheit manchmal mit einem 100Pkt-Sprung-Zauber durch die Landschaft zu hüpfen. Da kommt bloss kein NPC von Partners mit. Also dachte ich an einen Ring, der mir meinen NPC herbeiholt.

Hier das Skript, welches auf dem Ring liegt:
Code:
Begin jm_al_ruf_script

short status
short button
short OnPCEquip

float my_x
float my_y
float my_z

if ( MenuMode == 1 )
    return
endif

if ( OnPCEquip == 1 )
    Set Status to 10
    Set my_x to ( Player -> GetPos, x )
    Set my_y to ( Player -> GetPos, y )
    Set my_z to ( Player -> GetPos, z )
    Set OnPCEquip to 0
Endif

If ( status == 10 ); Menue
    MessageBox "Du steckst den Ring auf", "dran drehen.", "nichts weiteres"
    Set Status to 20
Elseif ( status == 20 ); auf Antwort warten
    Set button to GetButtonPressed

    If ( button == -1 ) ; keine Antwort
        Return
    Elseif ( button == 0 ); dran drehen
        jm_al_alryscha -> Position my_x, my_y, my_z, 0
    Elseif ( button == 1 ); nichts weiteres
        Set status to 0
    Endif
Endif

End
Als ich den Ring benutzte, kam der Dialog und nach einem Klick, war der NPC da weg, wo er vorher war, aber bei mir war er nicht.
Also Console auf und "jm_al_alryscha -> GetPos x" probiert. Ergebnis war 0!
Irgendwie befürchte ich, dass der Befehl Position keine Variablen annimmt.

Gibt es einen anderen Weg oder habe ich da irgendwas übersehen?

Über SetPos habe ich erfahren, dass man damit nur Objekte in der gleichen Zelle beeinflussen kann.
 
Um mal zurück zum Thema zu kommen...

Der SetPos-Befehl akzeptiert Variablen, wenn du mindestens Tribunal und/oder Bloodmoon geladen hast.
Allerdings muss sich das zu teleportierende Objekt maximal eine Exteriorzelle von dir entfernt sein oder sich in der gleichen Interiorzelle befinden.

Wenn es nicht nötig ist, dass du die gleiche Instanz des NPCs benötigst (zB. wegen Inventar), schlage ich vor, dass du einfach die alte Referenz zerstörst und eine neu neben dir erstellst (mit PlaceAtPC).
 
Ich hätte gerne die alte Instanz behalten, halt wegen den ganzen Companion-Geschichtchen.
Und die Begrenzung mit einer Zelle ist auch schlecht.
Mein Char kann mit Zaubern weeeeiiiit hüpfen. *g*

Ich habe weiter recherchiert und habe es nun offiziell (laut Scripting for Dummies): Position und PositionCell akzeptieren KEINE Variablen...
Das stand irgendwo weiter vorne bei Syntax.

Vielleicht werde ich dann wohl das MWSE draufpacken und das mit xPosition (oder so) realisieren.
Mir wäre aber am liebsten eine Lösung ohne Fremde Hilfe.
 
Ich weiß was in Scripting For Dummies steht, aber ich weiß auch, dass mein Talisman-PI funktioniert, denn dort verwende ich den SetPos-Befehl mit Variablen.
Der PositionCell bzw. Position -Befehl funktionieren leider nicht mit Variablen, das stimmt.

Schau dir als Beipsielscript mal die Stadtportalrollen bei meinen Setgegenständen an oder das Ranger-Tent. Ich bin sicher eines der beiden Scripte kannst du mit ein wenig Mühe so umgestalten, dass es mit deinem Problem funktioniert.
 
Ich habs mal mit SetPos probiert. Wenn der NPC in der Nähe ist, tut das auch wunderbar. Aber ab einer best. Distanz geht's nicht. Daher ungeeignet ;) .

Ich guck mir aber mal das deine PIs an.

Dein PI, besonders das FixMe und dieses doppelte Zuweisen hat mich noch mal zum Suchen gebracht und bei einer Suche nach dem FixMe habe ich einen genialen Lösungsweg gefunden. Zwar wieder mal für den Player, aber ev. auch für nen NPC machbar.

Dabei wird erstmal, wie bei dir ein COE 0,0 gemacht und dann die x und y Koordinaten geprüft. Wenn die zu groß sind, werden mehrere Teleports gemacht. Damit könnte man die Begrenzung austricksen.
 
Zuletzt bearbeitet:
Ich mach hier mal nen neuen Post, damit erkennbar ist, dass sich was in diesem Thread getan hat.

Das Script mit mehreren Teleports hat irgendwie nicht hingehauen...
aber bevor ich mich darin verliere habe ich mir mal ein paar Zeilen von Killfetzers PI angeschaut und probiert.
Also folgendes anstelle des Position-Befehls:
Code:
set my_x to ( Player -> GetPos, x )
set my_y to ( Player -> GetPos, y )
set my_z to ( Player -> GetPos, z )
jm_al_alryscha -> COE 0 0
jm_al_alryscha -> SetPos, X, my_x
jm_al_alryscha -> SetPos, Y, my_y
jm_al_alryscha -> SetPos, Z, my_z
FixMe
jm_al_alryscha -> SetPos, X, my_x
jm_al_alryscha -> SetPos, Y, my_y
jm_al_alryscha -> SetPos, Z, my_z

Zuerst musste ich merken, dass COE nur auf den Player wirkt... Ich war also in der Wallachei und mein NPC woanders. Als das COE auskommentiert war, konnte ich folgendes feststellen:

Auf kurze Distanz, also auf Sichtweite etwa, hat es funktioniert. Auf größerer Distanz, also etwa 2 Zellen Distanz, ging es nicht mehr.

Daraus lässt sich vermuten, dass man nur Objekte teleportieren kann, welche sich in den geladenen Zellen befinden.

@Killfetzer: Da der PC immer in den geladenen Zellen ist, scheint dein Script wohl zu funktionieren, da es ja nur den PC teleportiert. Ich möchte aber nen NPC teleportieren... :(
 
Statt COE kannst du den PositionCell-Befehl benutzen. Du suchst dir einen Ort mit eindeutigen Namen im Exteriorbereich aus (z.B. Balmora). Diese Zelle öffnest du im Editor und suchst dir Koordinaten innerhalb dieser Zelle aus.

Dann kannst du deinen NPC mit PositionCell, den ruasgesuchten Koordinaten und "Balmora" als Zelle ins Exterior befördern. Denn das ist der einzige Sinn des COE-Befehls: er soll nur garantieren, dass man sich tatsächlich in einem Exteriorfeld befindet. Denn in einem Interior funktioniert das Ganze nicht.

Deshalb solltest du in dein Script auch noche eine Abfrage einbauen:

if ( GetInterior == 1 )
return
endif
 
  • Like
Reaktionen: Kallreven
Achso...
Daran, dass der NPC auch mal in einem Interior stecken könnte, hab ich gar nicht gedacht. Wahrscheinlich, da ich das Skript auch nur auf Reisen brauche *g*.
Aber solange ich nen NPC nicht beliebig weit an beliebige Exteriorstellen teleportieren kann, bringt mir das Skript nix.
 
Das kannst du doch, wenn du mein Script so wie oben gesagt modifizeirst (und noch ein wenig rumspielst, aber das gehört zum Scripten dazu ;))
 
Hmmm.... Position und PositionCell gehen nicht und mit SetPos funktioniert das mit dem NPC nur auf kurze Distanz.
Das Faden und das Schalten der Menüs fällt bei mir ja flach, da ich nicht den player teleportiere... und das COE bzw festes "Position" ist auch außer Frage, da ich nur Tests innerhalb des Exteriors gemacht habe.
Hast du da noch was anderes verwendet?
(Ich hab mir ein Tor-Rückkehr-Skript angeschaut und da jetzt nur das SetPos als eigentliche Teleportation gefunden...)
 
Doch Du musst Setpos verwenden und kannst den Spieler (nicht andere NPC's!) damit im Exterior-Bereich umplatzieren.

Damit das funktioniert gehe folgendermassen vor:

begin Scriptname

;Schritt 1:
;Setpos akzeptiert nur lokale Float-Variablen, d.h. in das Script müssen ;Deklarationen für

float Xpos
float YPos
float ZPos


; kurzes Auslösen, sag mal das Script hängt an einem Amulett, dafür...

short OnPCEquip
short Button
short Status


;Schritt 2:
; das Ding darf niemals wirklich angelegt werden um nicht in einer Endlos-Schleife zu enden

set PCSkipEquip to 1

if ( Status == 0 )

set Status to OnPCEquip
return

else

;Schritt 3:

;der Spieler muss auf jeden Fall vorher nach draussen ins Freie (Exterior)

if ( Player-> GetInterior )

Player-> Position 0 0 0 0 "Vivec"

; diese Zeile ist eigentlich ein "Fehler", aber gewollt: darauf hin wird der
; Spieler an Position 0,0,0 im Aschland teleportiert, da soll er zwar nicht hin
; aber es ist ein Weg ihn nach draussen ins Exterior zu bekommen
; Du kannst natürlich auch eine gülltige Position z.B. in Balmora verwenden
; verwende dazu Koordinaten aus Balmora (Im Editor ablesen) und den
; POSITION-Befehl - NICHT POSITIONCELL !!!
; Position X Y Z Blickrichtung "Zellenname"

; return
; "return" ist eigentlich überflüssig weil es nach einem Position-Befehl
; sowieso stattfindet, aber der Ordnung halber sei das hier erwähnt.
; ales was unter Position steht wird in diesem frame nicht mehr ausgeführt!

endif

if ( Status == 1 )
messagebox "Wollt Ihr jetzt nach Dingsda reisen?" "Ja" "Nein"
set Status to -1

else

set Button to GetButtonPressed
if ( Button >= 0 )
set Status to 0
if ( Button == 0 )
; jetzt also "Ja" zum teleportieren,
; sagen wir mal XPos, YPos und ZPos enthalten
; bereits eine gültige Position auf Vvardenfell oder Solstheim
; dann geht das so:

Force1stPerson
Player-> SetPos X Xpos
Player-> SetPos Y Ypos
Player-> SetPos Z Zpos
;optional noch Player-> SetAngle Z Blickrichtung
FixMe


endif
endif
endif

; Force1stPerson ist notwendig da sonst FixMe nicht funzt,
; die alte Ansicht zu speichern um zurückzuschalten lohnt nicht
; da der Force3rdPerson-Befehl nicht funktioniert
 
  • Like
Reaktionen: Kallreven
Doch Du musst Setpos verwenden und kannst den Spieler (nicht andere NPC's!) damit im Exterior-Bereich umplatzieren.
Da liegt der Hase begraben. Ich habe nämlich Probleme nen NPC zu teleportieren. ;)
Den Player will ich gar nicht teleportieren. Mit meinen Sprungzaubern kommt ich so oder so im Exterior überall schnell hin.
 
Dann kannst Du (mit Morrowind) nur den NPC an verschiedene vorher im Editor festzulegende Positionen versetzen. Das schliesst natürlich aus das der spieler die Freiheit hätte, selbst zu entscheiden an welchen Ort genau. Aber für manche Fälle mag das ja genügen. Das ginge sowohl per Script als auch ganz ohne Script - eben nur per Dialog. Etwa so,
neues, eigenes Stichwort, z.b. "verreisen"

Text 1:
"OK, Ihr seht mich dann in Vivec"
Fun/Var: Function Choice = 1
Result :
Position xpos ypos zpos zrot "Vivec, Fremdenviertel"
goodbye

Text 2:
"Gut dann bleib ich hier"
Fun/Var : Function Choice = 0
Result: keine

Text 3:
"Wollt Ihr das ich zum Fremdenviertel von Vivec reise?"
fun/var: keine
Result:
Choice "Nein, bleib mal hier" 0 "Ja, ab mit Dir!" 1


Für die Position musst Du noch eine gültige im entsprechenden Exterior (hier "Vivec, Fremdenviertel") auswählen und einsetzen. Ausserdem funktioniert dieses Verfahren nur mit Zellen die einen Eigennamen (wie Balmora, Gnisis, Odai Plateau usw.) haben oder mit Interior-Zellen. Soll das Ziel ein Interior sein dann musst Du aber "PositionCell" statt "Position" verwenden.

Dies hier beschriebene ist natürlich nur ein Beispiel - Du müsstest noch absichern das der Dialog nicht in einer der Zielzellen (wäre für den Spieler dümmlich wenn ein NPC in Vivec ihn fragt ob er nach Vivec verreisen soll) bzw. das der Dialog zu bestimmten von Dir gewünschten Bedingungen stattfindet.
 
Zuletzt bearbeitet:
@Killfetzer und ReneMiner: Danke euch beiden für die Analyse des Problems.
Nach all den Überlegungen komme ich nun zu dem Schluss, dass man mit dem normalen Scriptsystem von Morrowind keinen NPC mit beliebiger Entfernung herbeiholen kann.
Position und PositionCell akzeptieren keine Variablen und SetPos funktioniert nur zuverlässig auf großen Distanzen mit PCs.
Also probier ich mal MWSE aus. Ich erzähl dann hier später, wie es damit aussieht.
 
So, ich habe mir nun mal CompanionTeleportation1.1 für MWSE angeguckt und habe gesehen, dass die es dort mit einem "großen Gepfusche" geschafft haben, NPCs per SetPos durch die ganze Landschaft an variable Positionen zu beamen. Nun habe ich es geschafft, dieses soweit von der MWSE-Abhängigkeit zu befreien, so dass kein MWSE erforderlich ist. (Irgendwie wurde nur MWSE fürs Hinzufügen und Entfernen von NPC aus der Teleportliste genutzt...)

Ich habe den Teleportteil rauskopiert und etwas entschlackt ;) (nur ein wenig *g*).

Hier ist der Code (mit neuen deutschen Kommentaren.. war ja alles englisch...)
Code:
Begin jm_al_ruf_script

short OnPCEquip

float warpx
float warpy
float warpz
float pa

float portTimer
short portWarp


if ( MenuMode == 1 )  ; Nicht waehrend des Menues
    return
endif

if ( OnPCEquip == 1 )
    set portWarp to 2
    set OnPCEquip to 0
endif


if ( portWarp == 2 )        
    
    set portTimer to portTimer + GetSecondsPassed
    
    if ( portTimer < 1 )
        return
    endif
    
    set warpz to ( Player->GetPos z )  ; Position vom player bestimmen
    set warpx to ( Player->GetPos x )
    set warpy to ( Player->GetPos y )
    set pa to ( Player->GetAngle z )
    
    if ( pa < 60 )  ; NPC soll vor Player erscheinen
        if ( pa > -60 )
            set warpy to warpy + 50
        elseif ( pa < -120 )    
            set warpy to warpy - 50
        endif
    elseif ( pa > 120 )
        set warpy to warpy - 50
    endif
    if ( pa > 30 )    
        if ( pa < 150 )
            set warpx to warpx + 50
        endif
    elseif ( pa < -30 )
        if ( pa > -150 )
            set warpx to warpx - 50
        endif
    endif
    set portWarp to 3
    return

elseif ( portWarp == 3 )  ; NPC teleportieren
    set warpz to ( Player->GetPos z )
    set warpz to warpz - 25
    jm_al_alryscha -> SetPos x warpx
    jm_al_alryscha -> SetPos y warpy
    jm_al_alryscha -> SetPos z warpz
    jm_al_alryscha -> Disable  ; NPC stabilisieren durch aus/anschalten
    jm_al_alryscha -> Enable
    set portWarp to 4
    set portTimer to 0
    return
elseif ( portWarp == 4 )        
    set portTimer to portTimer + GetSecondsPassed
    if ( portTimer < 0.25 )
        return
    endif
    jm_al_alryscha -> Disable  ; An und Aus, damit NPC sicher sichtbar
    jm_al_alryscha -> Enable  
    jm_al_alryscha -> AIFollow player 0 0 0 0  ; AI-Package festlegen
    set portWarp to 5
    set portTimer to 0
    return
elseif ( portWarp == 5 )  ; Nochmal wiederholen, sicherheitshalber
    set portTimer to portTimer + GetSecondsPassed
    if ( portTimer < 0.75 )
        return
    endif
    jm_al_alryscha -> Disable
    jm_al_alryscha -> Enable
    jm_al_alryscha -> AIFollow player 0 0 0 0
    set portTimer to 0
    set portWarp to 7
    return

elseif ( portwarp == 7 )
    if ( GetHealth <= 0 )  ; Companion tot
        set portWarp to -1
    elseif ( GetCurrentAIPackage != -1 )  ; erledigt, wenn erlaubtes Package
        set portWarp to 0
    else                                 
        set portWarp to 8  ; steckt vielleicht in Wand? Dann weiter
    endif
    return
elseif ( portWarp == 8 )  ; Reset mit Kampfverhalten an/aus
    StartCombat Player
    set portWarp to 9
    return
elseif ( portWarp == 9 )
    StopCombat
    AIFollow player 0 0 0 0
    set portWarp to 0                        
endif
    
End jm_al_ruf_script
Also da muss ich echt sagen, dass Grumpy und Charles Cooley das letzte aus SetPos rausgeholt haben... *Daumen hoch*