Construction Set Morrowind-Skriptkurs - Lektion 3

Killfetzer

Super-Moderator
Teammitglied
The Elder Scrolls III: Morrowind - Scriptkurs


3. Objekte verschwinden und wieder erscheinen lassen


Enable/Disable

Für diese Aufgaben stellt Morrowind Script zwei einfache Funktionen zur Verfügung:

Disable
Enable


Mit dem Aufruf von Disable wird das aufrufende Objekt unsichtbar. Es wird im Spiel nicht mehr gesehen, kann nicht mehr aktiviert werden und auch die AI (künstliche Intelligenz) von NPCs oder Kreaturen wird pausiert. Allerdings laufen Scripte auf disableden Objekten nach wie vor weiter! In Innenzellen wird gleichzeitig die Kollisionsabfrage des Objekts deaktiviert. In Außenzellen bleibt die Kollisionsbox des Objekts bis zum nächsten Neuladen der Zelle (normalerweise bis zum nächsten Betreten) erhalten! Auch für dieses Problem gibt es Workarounds, allerdings würde das jetzt zu weit führen. Vielleicht später einmal.

Enable erreicht genau das Gegenteil. Ein zuvor disabeldes Objekt wird dadruch wieder sichtbar, die AI läuft weiter und die Kollisionsabfrage wird wieder aktiviert (auch hier gibt es in Außenzellen wieder das Problem, dass zunächst neu geladen werden muss).

GetDisabled

Diese Funktion liefert eine 1 zurück, wenn das aufrufende Objekt disabled ist. Dies ist also eine Möglichkeit herauszufinden, ob man ein Objekt noch deaktivieren muss oder ob es das bereits ist.

Ein Beispiel für ein Disable-Script findet man auf dem Geisterwall:

Code:
begin GhostfenceScript

if ( GetDisabled == 1 )
  Return
endif				 

;this journal entry gets set when heart is wacked and akul falls apart
if ( GetJournalIndex, "C3_DestroyDagoth" >= 20 )
  Disable
  [...]
endif

end

Die erste if-Abfrage überprüft, ob der Wall bereits disabled wurde. Wenn ja, wird das Script wieder an den Anfang zurück gesetzt (das ist die Wirkung von return) und somit nicht weiter ausgeführt.

Danach wird in der zweiten Abfrage überprüft, ob ein bestimmter Tagebucheintrag vorhanden ist. Wenn dieser vorhanden ist, wird der Wall disabled. Der restliche Teil des Scripts beschäftigt sich mit dem Sound, den der Geisterwall abspielt, deswegen wurde dieser Teil ausgeklammert.


Aufgaben

  1. Statte ein Objekt mit einem Script aus, dass es nach dem Aktivieren verschwinden lässt.
  2. Bastele einen Activator, der anfangs disabled ist und erst nach einem bestimmten Tagebucheintrag erscheint. (Hinweis: Ihr könnt einen Gegenstand nicht im CS als disabled einstellen. Der Gegenstand ist eigentlich enabled, wenn ihr das Spiel ladet. Ihr müsst ein Script verwenden um ihn zu deaktivieren.)
  3. Erschaffe einen (freundlichen?) Geist, der nur nachts erscheint.

Lösungen


-
Wie versprochen war dies diesmal eine sehr kurze Lektion. Die nächsten Lektionen werden sich mit der Bewegung von Objekten beschäftigen. Nächstes Mal geht es zunächst um einfache Bewegung, danach um Rotationen und danach um Teleportation.
 
Zuletzt bearbeitet:
  • Like
Reaktionen: Zeitgeist und J2W
ich mach dann mal den Anfang:
Code:
if ( OnActivate == 1 )
   disable
endif
Code:
if ( GetJournalIndex "XXX" < 20 )
   if ( GetDisabled == 0 )
      disable
   endif
else
   if ( GetDisabled == 1 )
      enable
   endif
endif
Code:
if ( GameHour > 7 )
   if ( GameHour < 22 )
      if ( GetDisabled == 0 )
         disable
      endif
      return
   endif
endif 

if ( GetDisabled == 1 )
   enable
endif

alles jedoch nicht getestet...
 
Meins:
Code:
begin kl_disable_script
 
short state
 
if ( state == 0 )
   if ( OnActivate == 1 )
      disable
      set state to 1
   endif
endif
 
end

Code:
begin kl_disable2_script
 
short state
 
if ( state == 0 )
   if ( GetJournalIndex, "SomethingShallEnable" == 0 )
      disable
   endif
endif
 
if ( state == 0 )
   if ( GetJournalIndex, "SomethinShallEnable" == 50 )
      enable
      set state to 1
   endif
endif
 
end
Das ist beides nur aus dem Kopf geschrieben, ich weiß nicht genau ob es so funktioniert wie es soll.

Nr.3 kommt später.
 
alles jedoch nicht getestet...

Sieht aber passend aus ;)

Meins:
Code:
begin kl_disable_script
 
short state
 
if ( state == 0 )
   if ( OnActivate == 1 )
      disable
      set state to 1
   endif
endif
 
end

Die Variable hier ist zu umständlich. Eine Eigenschaft von disableden Gegenständen ist ja gerade, dass sie nicht aktiviert werden können, also musst du das auch nicht abfangen.

Code:
begin kl_disable2_script
 
short state
 
if ( state == 0 )
   if ( GetJournalIndex, "SomethingShallEnable" == 0 )
      disable
   endif
endif
 
if ( state == 0 )
   if ( GetJournalIndex, "SomethinShallEnable" == 50 )
      enable
      set state to 1
   endif
endif
 
end

Das wird so bestimmt nicht funktionieren. Ich denke, du hast hier im ersten Block vergessen die state-Variable zu setzen. So wie du sie im Moment verwendest, macht sie keinen Sinn.
 
Killfetzer schrieb:
Die Variable hier ist zu umständlich. Eine Eigenschaft von disableden Gegenständen ist ja gerade, dass sie nicht aktiviert werden können, also musst du das auch nicht abfangen.
Aua... einfach denken liegt mir eben manchmal einfach nicht...
Das wird so bestimmt nicht funktionieren. Ich denke, du hast hier im ersten Block vergessen die state-Variable zu setzen. So wie du sie im Moment verwendest, macht sie keinen Sinn.
Nochmal aua... ja, da habe ich etwas vergessen. Wobei sich mir hier gerade die Frage stellt, ist es möglich state im ersten Block ganz wegzulassen?
Also:
Code:
begin kl_disable2_script
 
short state
 
if ( GetJournalIndex, "SomethingShallEnable" == 0 )
      disable
endif
 
if ( state == 0 )
   if ( GetJournalIndex, "SomethinShallEnable" == 50 )
      enable
      set state to 1
   endif
endif
 
end
 
Du solltest ein Disable nur einmal aufrufen. Sonst könnte das zu Problemen führen. Derzeit wird es ja solange aufgerufen, wie du den Tagebucheintrag nicht hast.

Hier solltest du also schon entweder mit einer Variablen oder mit GetDisabled arbeiten.
 
Killfetzer hat recht, permanentes Disablen oder enablen führt ggf. sogar zu Lags.

Mach es lieber so:
Code:
begin kl_disable2_script
short state
 
IF ( state == 0 ) [COLOR="RoyalBlue"][I];bei Spielstart disablen[/I][/COLOR]
  disable
  set state to 1
ElseIF ( state == 1 ) [COLOR="RoyalBlue"][I];drauf warten, bis der Eintrag da ist[/I][/COLOR]
  IF ( GetJournalIndex, "SomethinShallEnable" == 50 )
    enable
    set state to -1 [COLOR="RoyalBlue"][I];enablen und danach nichts mehr machen[/I][/COLOR]
  EndIF
EndIF
 
end
Code:
begin kl_disable2_script
;Die von Killfetzer angesprochene Variante mit "GetDisabled"
 
IF ( GetDisabled == 0 )
  IF ( GetJournalIndex, "SomethinShallEnable" < 50 )
    disable
  EndIF
Else
  IF ( GetJournalIndex, "SomethinShallEnable" >= 50 )
    enable
  EndIF
EndIF
 
end
Wie man sieht, kommt die zweite Variante ohne Variable aus, was Speicher spart. Nachteil ist, dass dafür die Funktion GetJournalIndex jeden Frame ausgeführt werden muß, auch wenn die eigentliche Aufgabe des Scripts lange erfüllt ist. Sie ist damit marginal fps-intensiver. Ich würde Version 1 den Vorzug geben, weil man mit einer State-Variablen noch mehr nette Sachen anstellen könnte ^^
 
Danke, wieder was wichtiges gelernt.

@£exa: So ähnlich habe ich es mir auch gedacht. Oder eben mit GetDisabled.
 
Aufgabe 1 und 2 habe ich auch schon erledigt, aber bei mir dauerts immer etwas länger, daher kommt Aufgabe 3 noch. ;)

Code:
Begin Teri_L3_KingRobe_Script

if ( GetDisabled == 1 )
    return
endif

if ( OnActivate )
    if ( GetDisabled == 0 )
        disable
        MessageBox "Als Ihr des Königs neue Kleider aufnehmen wollt, verschwinden sie vor Euren Augen."
    endif
endif

End
Code:
Begin Teri_L3_TauchAuf_Script

if ( GetJournalIndex, "Teri_Key" => 20 )
    if ( GetDisabled == 1 )
        enable
    endif
else
    if ( GetDisabled == 0 )
        disable
    endif
endif

End

Beide Lösungen funktionieren einwandfrei. Aber wenn Tommys Antwort reicht, ist meine Lösung 1 wohl zu kompliziert. Führt sie auch zu den von £exa erwähnten Lags?
 
Zuletzt bearbeitet:
Beide Lösungen funktionieren einwandfrei. Aber wenn Tommys Antwort reicht, ist meine Lösung 1 wohl zu kompliziert. Führt sie auch zu den von £exa erwähnten Lags?
Nein, du führst den Disable ja nicht immer wieder aus. Dein Script macht halt nur eine unnötige Abfrage, nachdem das Objekt deaktiviert wurde.
 
Ah, ok. Besser einmal zu viel als einmal zu wenig abgefragt, oder? Obwohl unnötiger Scriptmüll ja auch nicht sein muss. ;)

Meine Lösung für Aufgabe 3:
Code:
begin Teri_L3_VerschwindeGeist_Script

if ( GameHour > 6 )
    if ( GetDisabled == 0 )
        disable
    endif
endif

if ( GameHour > 20 )
    if ( GetDisabled == 1 )
        enable
    endif
endif

end Teri_L3_VerschwindeGeist_Script
 
Meine Lösung für Aufgabe 3:
Code:
begin Teri_L3_VerschwindeGeist_Script

if ( GameHour > 6 )
    if ( GetDisabled == 0 )
        disable
    endif
endif

if ( GameHour > 20 )
    if ( GetDisabled == 1 )
        enable
    endif
endif

end Teri_L3_VerschwindeGeist_Script

So und jetzt bin ich gemein ;)

Ich spiele deine Mod und habe die Zelle das letzte Mal um 16 Uhr betreten. Dann verlasse ich sie und komme um 3 Uhr (nachts) zurück. Was passiert?
 
Dann muss ich wohl noch eine Abfrage einbauen, dass der Geist um Mitternacht (wenn der GameHour-Wert resettet wird) auch erscheint. Oder bin ich auf dem falschen Dampfer?
 
Dann muss ich wohl noch eine Abfrage einbauen, dass der Geist um Mitternacht (wenn der GameHour-Wert resettet wird) auch erscheint. Oder bin ich auf dem falschen Dampfer?
so in etwa ... momentan deckt keine deiner Bedingungen den Bereich von 0 bis 6 ab, weshalb es da zu unstimmigkeiten kommen kann, falls der Ausgangszustand nicht der Erwartung entspricht ... für dieses Problems gibts mehrere Lösungsvarianten - eine zusätzliche Bedingung wäre eine mögliche Variante ... für weitere Möglichkeiten siehe auch Lektion 1: Aufgaben-Teil 2, Aufgabe 4
 
Danke. Die Aufgaben Teil 2 der Lektion 1 hatte ich wohl übersehen. Und da versagt mein logisches Verständnis völlig, ich verstehe nur noch Bahnhof. Sollte dieser Teil in den Abschlussprüfungen vorkommen, werde ich wohl nicht besonders abschneiden. ;)
 
Hier ist erstmal 1.

Code:
begin md_disappering_objekt_script

short OnPCEquip
short IsEquiped

if ( OnPCEquip )
    if ( IsEquiped == 0 )
        Disable
        set IsEquiped to 1
    endif
endif

end

2. und 3. kommt gleich,Momentchen ...


Hier ist 2.

Code:
begin md_deactivated_activator_script

if ( GetJournalIndex "XXX" < 20 )
    Disable

elseif ( GetJournalIndex "XXX" >= 20 )
    Enable

endif

end
 
Zuletzt bearbeitet:
Hier ist erstmal 1.

Code:
begin md_disappering_objekt_script

short OnPCEquip
short IsEquiped

if ( OnPCEquip )
    if ( IsEquiped == 0 )
        Disable
        set IsEquiped to 1
    endif
endif

end

Das passt überhaupt nicht. Es geht um einen Activator und nicht um einen Inventargegenstand. Du warst wohl noch in der falschen Lektion ;)


Hier ist 2.

Code:
begin md_deactivated_activator_script

if ( GetJournalIndex "XXX" < 20 )
    Disable

elseif ( GetJournalIndex "XXX" >= 20 )
    Enable

endif

end

Du hast bei diesem Script das gleiche Problem wie Kallor, du rufst Enable und Disable in jedem Scriptdurchgang auf. Das musst du vermeiden.
 
Hm,das kommt raus wenn man mit den Gedanken bei der anderen Lektion ist:lol:

Code:
begin md_disappering_objekt_script

if ( OnActivate == 1 )
    Disable
endif

end

2. überarbeite ich noch,genauso wie 3. ;)


EDIT

Hier ist 2.
Code:
begin md_deactivated_activator_script

short state
 
if ( state == 0 ) 
   disable
   set state to 1

elsif ( state == 1 ) 
      if ( GetJournalIndex, "XXX" == 50 )
          enable
          set state to -1 
      endif
endif

end

Und hier ist 3.
Code:
begin md_nightghost_script


if ( GetDisabled == 0 )
   if ( GameHour > 7 )
       if ( GameHour < 22 )
           Disable
       endif
   endif

else

if ( GetDisabled == 1 )
      if ( GameHour < 7 )
         if ( GameHour > 22 )
            Enable
         endif
      endif
endif

end
 
Zuletzt bearbeitet:
@MadDin:

Dein 2. Script funktioniert so. In deinem 3. Script ist der erste Teil korrekt. Ab dem else klappt es so nicht mehr:

1. Die zweite Abfrage nach GetDisabled ist ja nicht mehr nötig. Du kommst in diesen Block ja nur, wenn der Geist nicht nicht disabled war, also ist die Aussage immer wahr.
Weiterhin funktioniert deine Abfrage nach den Stunden nicht. Es ist unmöglich, dass die Stunde gleichzeitig kleiner als 7 und größer als 22 ist. Das musst du auf zwei getrennte if-Blöcke aufteilen.
 
Sorry für die verspätete Antwort, ich häng gerade ein wenig hinterher:


PHP:
begin merc_disable_Item_activate

if (getdisabled==1)
    return
endif

if (OnActivate==1)
    disable
endif

end

PHP:
begin merc_disabled_object_diary



if (getdisabled==0)
    if(GetJournalIndex "A1_Sleepers_Assi"==0)
    disable
    endif
endif

if (GetDisabled==1)
    if (GetJournalIndex, "A1_Sleepers_Assi">=1)
    enable
    endif
endif
end]

Aufgabe 3 hat mich an meine Anfrage im Scriptthread für mein derzeitiges Ebenherz-Projekt erinnert, allerdings war der Ablauf genau umgekehrt. Damals hab ich mir das für Nachtaktive Sachen herleiten können und es funktioniert:

PHP:
begin "MNC NPC notrade"

short state

if ( state==0 )

   if ( GameHour > 7 )
     if ( GameHour < 21 )
         disable
         set state to 1
     endif
   endif

else
if (state ==1)
   if ( GameHour > 21 )
     Enable
     set state to 0

           elseif ( Gamehour < 7 )
         Enable
         set state to 0
    endif
   endif

endif

end
Allerdings scheint das bei dem Geist irgendwie nicht zu klappen (Vorführeffekt). Ihr wisst net zufällig, woran das liegen könnte?