Anmelden

Archiv verlassen und diese Seite im Standarddesign anzeigen : Probleme mit einem seeeeehr einfachen Script



Schattenläufer
11.04.2010, 18:28
Hallo Leute, es ist total peinlich, aber ich habe gerade riesige Probleme mit einem eigentlich total einfachen Script.
Die Idee ist folgende: Der Spieler aktiviert einen Activator. Je nachdem, wie die Skills (GetAV, nicht GetBaseAV, das ist wichtig) des Spielercharakter sind, fügt das Spiel ihm eine neue Ability hinzu, welche selbst ebenfalls die Skills verändert.
Wenn der Spieler jetzt ein zweites Mal den Activator aktiviert, würde das Script ja auf Basis der durch seine eigens hinzugefügte Ability veränderten Skills seine Rechnungen durchführen, was in reinem Chaos enden würde - das heißt, die Ability muss vor der Rechnung auf jeden Fall entfernt werden. (Sollte ja kein Problem sein, danach wird sie ja wieder hinzugefügt.)

In der Theorie nicht schwer und sieht auch nicht schwer aus, nur gibt's ein Problem: Es funktioniert nicht. Wenn ich das Script das zweite Mal aktiviere, entfernt es die Ability vollständig, ohne sie am Ende wieder hinzuzufügen. Beim dritten Mal funktioniert es dann wieder. Immer abwechselnd.

Hier das Skript:


scn HFCTEST

short HFCStage

Begin OnActivate

If HFCStage == 0
If ( Player.HasSpell HFCAbility == 1 )
Player.RemoveSpell HFCAbility
EndIf
set HFCStage to 10
endif

if HFCStage == 10
PrintToConsole "We get to the second stage of the script."
; Here is a long calculation which needs to be based on the player's skill WITHOUT the Ability added by this script
set HFCStage to 20
endif

if HFCStage == 20
PrintToConsole "Adding the ability."
Player.AddSpell HFCAbility
set HFCStage to 0
endif

End


Die verschiedenen Stages sind *eigentlich* ja komplett unnötig, aber ich benutze sie momentan als eine Art Versicherung, dass die Zeilen wirklich nacheinander ablaufen. Was sie allem Anschein nach auch tun.
Der Check in Stage 0, ob der Spieler die Ability hat oder nicht, sollte ebenfalls unwichtig sein - wenn er sie nicht hat, entfernt das Script eben eine Ability, die gar nicht da ist. Der steht da allerdings drin, weil ich die Vermutung habe, dass das Script so lange wartet, bis es eine Ability zum Entfernen hat.
Die Vermutung sollte eigentlich durch diesen Check widerlegt sein, aber ich bin mir noch nicht ganz sicher... allerdings wüsste ich in dem Fall auch nicht, was ich tun kann.

Ariantras
11.04.2010, 19:55
Ich hab zwar null Plan vom Modden, aber wohl vom Programmieren - bist du dir sicher, dass das fett markierte nicht doch "HFCstage" heißen soll?



scn HFCTEST

short HFCStage

Begin OnActivate

If HFCStage == 0
If ( Player.HasSpell HFCAbility == 1 )
Player.RemoveSpell HFCAbility
EndIf
set HFCStage to 10
endif

if HFCStage == 10
PrintToConsole "We get to the second stage of the script."
; Here is a long calculation which needs to be based on the player's skill WITHOUT the Ability added by this script
set HFCStage to 20
endif

if HFCTestVar == 20
PrintToConsole "Adding the ability."
Player.AddSpell HFCAbility
set HFCStage to 0
endif

End


Falls das so beabsichtigt und richtig ist, will ich nichts gesagt haben und ziehe mich wieder in mein stilles Eckchen zurück. :)

Schattenläufer
11.04.2010, 20:16
Ugh, doch eins übersehen - ich habe des besseren Verständnisses wegen die Variablen für den Thread hier umbenannt, da in meinem Script gerade alles nur Testbla, Testbla2 etc. heißt.
Ich konnte mein Problem auf dieses Kernproblem einkreisen und habe deswegen ein einfaches Test-Script erstellt - mein eigentliches Script ist etwas länger.

Von daher, gut dass du das gesehen hast, aber es hat nichts mit dem Problem zu tun.

Muecke49
12.04.2010, 11:23
Ich denke dein Problem ist die eigentliche Funktionsweise eines OnActivate Block.

Verstehe ich das richtig, dass Stage 10 und 20 unmittelbar hintereinander ausgeführt werden sollen?

Also erstes Aktivieren: Die Ability wird entfernt.

Zweites Aktivieren: die Kalkulation wird durchgeführt (Stage 10) UND die Ability soll am Ende wieder dazuaddiert (Stage 20) werden.

Dann wäre die Erklärung einfach. Ein onActivate Block läuft nur ein einziges mal durch, in dem Moment wo Du den Activator benutzt. So klappt der erste Schritt, das entfernen der Ability und das setzen auf Stage 10. Beim nächsten Aktivieren merkt das Script, dass Stage 10 gesetzt ist und führt die dort aufgeführten Schritte aus. Und hier kommt nun der Haken, ein onActivate Block registriert nun nicht direkt die Änderung des Stages und nimmt diese mit und führt somit direkt auch den Stage 20 aus. Die Änderungen vom Stage 10 werden gesetzt und da keine weiteren Conditions zutreffen, wird das Script hiermit beendet. Erst beim nächsten Aktivieren (dem 3.Aktivieren), stellt das Script den Stage 20 fest und führt diese Änderungen durch. Wohlgemerkt nur diese Änderungen, die Rechnung bleibt diesmal unbeachtet, denn diese läuft ja unter Stage 10)
Ein OnActivate Block ist sozusagen eine Momentaufnahme. Nur die Conditions, welche beim Aktivieren schon gesetzt waren, werden bemerkt. Alles was innerhalb des Scriptes passiert, wird erst beim erneuten Aktivieren wieder bemerkt. Deswegen ist diese Scriptform auch so Performance schonend, weil sie nur eine Momentaufnahme liefert. Doch es schränkt auch die Möglichkeiten ein, wie Du gerade merkst. :)

Du musst einen GameMode Block einbauen. Anders geht es leider nicht. Probiere es mal mit diesem Script, auf dem Activator.


scn HFCTEST

short HFCStage

Begin OnActivate

If HFCStage == 0
If ( Player.HasSpell HFCAbility == 1 )
Player.RemoveSpell HFCAbility
EndIf
set HFCStage to 10
endif

if HFCStage == 10
PrintToConsole "We get to the second stage of the script."
; Here is a long calculation which needs to be based on the player's skill WITHOUT the Ability added by this script
set HFCStage to 20
endif

End

begin GameMode

if HFCStage != 20
return ;Performace schonen, damit das Script nur durchläuft, wenn der Stage 20 beträgt)
endif

if HFCStage == 20
PrintToConsole "Adding the ability."
Player.AddSpell HFCAbility
set HFCStage to 0
endif

End




Die verschiedenen Stages sind *eigentlich* ja komplett unnötig, aber ich benutze sie momentan als eine Art Versicherung, dass die Zeilen wirklich nacheinander ablaufen. Was sie allem Anschein nach auch tun.


Ich bin mir nicht sicher ob die Rechnung richtig laufen würde, wenn du stage 10 und stage 20 in eine Stage zusammenfügst. Denn dann wäre wieder diese Geschichte mit der Momentaufnahme des onActivateBlock......die Berechnung würde zwar stimmen, doch wenn im selben If-Block die Ability wieder addiert wird, dann könnte es gut sein das diese nicht auf die neue Berechnung addiert wird, sondern auf den Zustand der Ability beim Aktivieren des Activators.

Das ist allerdings nur eine Vermutung und dies liese sich ja austesten, was passiert wenn Du die Stage 20 weglässt und sie einfach in Stage 10 kopierst. ...
Doch ich würde sagen der sichere Weg ist der GameMode Block, der wirklich erst läuft, wenn Stage 10 bereits beendet wurde. :) Bei der Kürze vom Stage 20 und dem Returnbefehl davor, brauchst Du Dir wegen der Performace keine Sorgen machen. :)

Low Post
12.04.2010, 16:17
Meiner Erfahrung nach macht das keine Probleme, ob eine Variable schon vorher einen gewissen Wert, oder den erst im fraglichen Frame erhält, solange er während der Abfrage stimmt.

Bestes Beispiel: Stage 10 läuft durch.


Ich denke das Problem ist hier, dass Oblivion einfach ein Problem damit hat eine Ability im gleichen Frame zu entfernen und wieder hinzuzufügen. Ein einfaches auslagern des hinzufügens in einen Gamemode-Block sollte schon genügen.
Falls du wegen der Performance bedenken hast könntest du den Gamemode-Block ja auch auf einen Activator packen, der in einer Dummy-Zelle seine Zeit absitzt. Um den Gamemode-Block auszulösen veränderst du einfach eine Variable was dazu führt, dass der Gamemode-Block im nächsten Frame ausgeführt wird (siehe Script Processing (http://cs.elderscrolls.com/constwiki/index.php/Script_Processing), Unterpunkt Remote Ref Heartbeat ).
Außer natürlich du deklarierst den Activator auch noch als Quest-Item, IIRC läuft das Script dann immer ab, ganz egal wo das Teil sich befindet.

Alternativ könntest du auch den Effekt der Eigenschaft herausrechnen (GetNthEffectItemMagnitude (file:///D:/Programme/Spiele/Oblivion/obse_command_doc.html#GetNthEffectItemMagnitude) zum ermitteln der Stärke/Stärke schon vorher bekannt & +/- Rechnungen mit einer Variablen).

Schattenläufer
12.04.2010, 16:28
Danke für deine Mühe, aber ich glaube nicht, dass es das ist.

Erst einmal: Das Script soll beim ersten Aktivieren *exakt* dasselbe machen wie beim zweiten Mal. Das war glaube ich ein einfaches Missverständnis, führt aber evtl zu falschen Schlüssen.
Im Grunde ist das hier nichts anderes als ein Activator, der jedes Mal beim Aktivieren die Nachricht "Hallo!" erscheinen lässt. Nur, dass er stattdessen eine Ability entfernen, und dann wieder hinzufügen soll.

Wegen den Stages:
Wie du siehst, habe ich in dem Script zwei Befehle, die mir einen Hinweis darauf geben sollen, dass das Script tatsächlich komplett durchlaufen wird. Beide PrintToConsole-Nachrichten erscheinen beim ersten Durchlauf, und sie erscheinen auch beim zweiten Durchlauf. Also alles so, wie es sein soll.
(Abgesehen davon habe ich die Stages ja erst eingefügt, als ich merkte, dass etwas nicht funktioniert. Vorher bestand das Script praktisch nur aus den beiden Befehlen RemoveSpell und AddSpell, und dazwischen waren keine Punkte, an denen das Script sich hätte entscheiden können, den Rest zu überspringen.)

Wenn ich also nicht gerade auf irgendeiner Leitung stehe, sollte das Script also richtig sein - nur, dass irgendwas in dieser Art zu scripten die beiden Befehle RemoveSpell und AddSpell davon abhält, richtig miteinander zu funktionieren.

So sieht das Script in seiner ursprünglichen, ganz simplen Form aus:



scn HFCTEST

Begin OnActivate

Player.RemoveSpell HFCAbility
PrintToConsole "Spell removed."

Player.AddSpell HFCAbility
PrintToConsole "Spell added."

End


Und es funktioniert nicht. Beide Konsolen-Messages erscheinen.

Schattenläufer
12.04.2010, 23:57
Huch, Low Post! :eek:
Man sollte dich in Secret Post umbenennen, denn deinen Post habe ich vollkommen übersehen. :D Meine Antwort bezog sich auf Muecke49, nicht auf dich.

Das Ganze künstlich zu trennen und in zwei verschiedene Frames zu packen war jedenfalls die Lösung. Vielen, vielen Dank für diesen Hinweis. :)

Nächstes Problem... wenn ich eine Ability mit "Drain Skill" benutze, hat das offensichtlich einen Einfluss darauf, wie schnell der Skill ansteigt. :hehe:nton:
Ich probiere jetzt mal, ob das nicht mehr der Fall ist, wenn ich eine Disease benutze. Laut UESP sollte Drain Skill diesen Effekt nämlich eigentlich nicht haben.
(Edit: Öh, okay, Disease scheidet aus. Message Spam und visueller Effekt, dass man krank ist... dann muss wohl doch ModActorValue ran. *seufz*)