Not logged inClonkspot Forum
Forum Home Help Search Register Login
Up Topic Deutsch / Hilfestellung / [Gelöst] Regeln durch Auslöser dazuschalten
- - By Serpens66 (More than 200 posts.) Date 21.09.2014 12:28 Edited 21.09.2014 12:35
Hey,

gibt es einen Befehl der z.b so lautet "Wenn gebäude x gebaut/erforscht dann schalte Regel y dazu" ?
Für mein Szenario würde ich nämlich gerne die Regeln Predator und Scorpion Angriff dazuschalten (aus dem Hazard Ships Paket), da man aber noch völlig ohne Zukunftsdinge startet, sollen die erst anfangen sobald ein bestimmtes zukunftsgebäude erforscht/gebaut ist.

Weiterführend wärs cool, wenn man die Regel dann mit bestimmten Bedingungen wieder entfernen könnte. Also z.b wenn x Predator und Scorpions zerstört wurden.

Ich denke mal, man kann einen einfachen "if" Befehl in einer while schleife dafür nehmen, oder? Oder man setzt einfach einen Verweis auf eine Funktion in die "Completion" Funktion eines bestimmten Gebäudes rein, sodass der Bau des Gebäudes das Ereignis auslöst. Wobei die Schleife dann dennoch für das Abschalten der Regel sinnvoll wäre... Oder ich such mir da ebenfalls ein Gebäude aus, welches dafür sorgt, dass die Regel abgeschaltet wird... ja ich denke letztere Variante ist am einfachsten umzusetzen.

Was mir aber fehlt, ist der Befehl, wie man eine Regel dazu schaltet :)
Aber ich denke mal sowas ist machbar, oder?

Was evtl schwieriger wird: kann man irgendwie Tiere durch Bedingungen spawnen?

Edit:
ich sehe gerade aus irgendeinem Pack habe ich bereits eine Regel, die mch Spielregeln/ziele/umweltobjekte im Spiel aussuchen lässt :) Ich schau mir mal an, wie dies funktioniert :)
Parent - - By Clonkonaut (More than 200 posts.) Date 21.09.2014 13:14

> gibt es einen Befehl der z.b so lautet "Wenn gebäude x gebaut/erforscht dann schalte Regel y dazu" ?


Nein, vorgefertigt gibt es das nicht.

> Ich denke mal, man kann einen einfachen "if" Befehl in einer while schleife dafür nehmen, oder?


Nein, wohl nicht. Die kannst mit einer while-Schleife nicht fortlaufend das Spielgeschehen beobachten und Veränderungen registrieren. Dies ist in anderen Sprachen manchmal möglich, wenn ein Befehl existiert, der die Codeausführung warten lässt (häufig "sleep"). Dann würde eine Schleife gestoppt und die restliche Ausführung geht weiter. Das geht in C4Script nicht. Hier stoppt quasi das gesamte Spiel, bis deine Schleife fertig ist. Wenn deine Schleife erst endet, wenn das Gebäude erforscht ist, dann würdest du damit das Spiel komplett lahmlegen (Endlosschleife).

Am einfachsten kannst du das Spielgeschehen überwachen, wenn du dafür ein Hilfsobjekt im Szenario hast, welches mittels einer Timers regelmäßig Abfragen startet. Wenn du nicht gerade ein bestimmtes Szenario bearbeitest und darin über das Szenarioscript Objekte erstellen kannst, bietet sich eine Regel für so etwas an. Alle ausgewählten Regeln werden von der Engine als Objekte an der Position 0,0 erzeugt (gilt übrigens auch für Umweltobjekte). Damit hättest du dein Objekt, welches dann noch etwas tun muss.

Einen Timer kannst du über DefCore setzen:
TimerCall  Zeichenfolge (max. 30)  Regelmäßig aufgerufene Funktion des Objektscripts. Siehe Objektscripte.
Timer  Integer  Zeitabstand der TimerCalls in Frames. Ohne Angabe gilt der Vorgabewert 35.


Das ist einfacher als über Effekte, wenn es nur darum geht, dass eine Funktion im Objektscript immer wieder aufgerufen wird. Du würdest also z.B. "Timer=1" und in die nächste Zeile "TimerCall=MeineFunktion" schreiben. Dann wird in jedem Frame die Funktion MeineFunktion() im Script des Objekts aufgerufen.

In MeineFunktion() (oder der entsprechend von dir gewählte Name) prüfst du dann, ob etwas bestimmtes erforscht wurde. Forschen bedeutet, dass am Ende der forschende Spieler den Bauplan für das Objekt erhält. Welche Baupläne ein Spieler hat, kannst du mit GetPlrKnowledge abfragen. Das kannst du nur pro Spieler machen. Falls du keine Gemeinschaftsbaupläne angeschaltet hast, müsstest du in einer Schleife die Baupläne aller Spieler abfragen und schauen, ob einer das Objekt schon erforscht hat. Ansonsten reicht es, wenn du mit GetPlayerByIndex den ersten Spieler ("GetPlayerByIndex(0)") abfragst.
Sobald du dann den Bauplan findest (also if (GetPlrKnowledge(...)) ) kann deine Timer-Funktion die weitere Regel erstellen (CreateObject(...)). Du solltest aber aufpassen, dass die weitere Regel nicht mehrfach erstellt wird, weil natürlich der Timer weiterläuft und der Bauplan weiterhin exitiert. Also eine zweite Vorbedingung: if ( ! FindObject(...)).

Das Entfernen der Regel ist schwieriger. Wenn du nicht das Script der Gegner modifizieren kannst / willst (könnte auch per #appendto laufen), dann müsste deine Regel ständig die Anzahl der Gegner feststellen (ObjectCount), speichern und im nächsten Timer-Aufruf vergleichen, ob die Zahl kleiner geworden ist. Das ist nicht ganz sauber, weil im gleichen Moment ein Gegner sterben und einer neu erscheinen könnte, außerdem du nicht genau weißt, ob die Gegner getötet wurden oder sonst wie verschwunden sind. Bei der Anzahl x müsstest du dann die zuvor erstellte Regel entfernen.

Wenn du das Script der Gegner änderst, dann müssen diese in ihrer Death()-Funktion deiner Regel Bescheid geben, dass sie getötet wurden. Am einfachsten läuft dies durch "GameCallEx". Das funktioniert wie GameCall mit dem Unterschied, dass auch Regelobjekte den Aufruf erhalten. Dokumentiert ist das nicht, weil es wenn ich mich recht entsinne in System.c4g drin ist. Dann würden deine Gegner also den Aufruf machen und deine Regel in diesem die Zahl getöteter Gegner speichern und bei x wiederum die Regel entfernen.
Parent - - By Serpens66 (More than 200 posts.) Date 21.09.2014 14:12 Edited 21.09.2014 14:15
vielen dank für diese ausführliche Antwort =)

Ich bin ein freund von möglichst einfachen Lösungen :D  Deswegen werde ich für das aktivieren der Regel wohl wirlich einfach nur die Completion Funktion von bestimmten Gebäuden/Gegenständen/Fahrzeugen benutzen, die für das Vorankommen wichtig sind und somit gebaut werden müssen, wodurch dann der Angriff startet. Habe es auch schon getestet und es funktioniert mit CreateObject(idRule) (also die ID der Regel)  und RemoveObject(FindObject(idRule)) einwandfrei :)

Anzahl der Gegner zu nutzen klappt, wie du schon schreibst, nicht unbedingt, da ja immer wieder neue Gegner gespawnt werden, solange die Regel aktiv ist. (da fällt mir ein auf diese weise könnte man doch eig auch Tiere spawnen... ich schau mir diese angriffsregel auch nochmal an, ob ich da einfach die Predatoren durch Feuermonster oderso ersetzen kann ^^).
Jedenfalls ist dein Vorschlag mit der Todesfunktion schon sehr gut und wäre auch vom Spielgeschehen her schöner, als wenn es beim Bau eines Gebäudes aufhört. Zwar zerstören sich diese Gegner häufig selbst, weil sie auf dem Boden aufschlagen und explodieren :D , aber das könnte man ja bei der benötigten Anzahl berücksichtigen. Dadurch würde der angriff dann z.b nach 20 minuten von alleine beendet (weil x Stück dann auf dem boden zerschellt sind), oder evlt auch früher, weil man einige abgeschossen hat.
Also versuchen wir das mal weiter auszuführen:
Mit dem Befehl "GameCallEx", welchen ich in die Death-Funktion der Gegner per appendto schreibe, würde dann also der Regel Bescheid gegeben werden können. Aber welcher Regel genau? Der Regel für den Angriff, oder einer eigenen Regel, die für das zählen zuständig ist und dann bei x die Angriffsregel entfernt?  Oder was genau würde das zählen und auslösen des Angriffs dann übernehmen?

Und würde dieser Zähler dann iwann auf Null gesetzt oder wäre das zu kompliziert?
Also als Beispiel: ich baue eine Hütte und ein Angriff beginnt. Nachdem 10 Stück tot sind, hört der Angriff auf. Nun baue ich noch eine Hütte (oder ein anderes gebäude welches einen Angriff auslöst). Wird dieser Angriff dann automatisch sofort wieder beendet, weil ja zuvor bereits 10 stück gestorben sind?...
Hmmm.. also ich denke es wäre tatsächlich gut, wenn dieser Zähler nicht auf Null gesetzt wird. Denn ich will ja nicht unbedingt, dass bei jedem Hüttenbau dann wieder Gegner auftauchen. Demnach müsste ich das Gebäude und die Anzahl irgendwie koppeln. Soll heißen beim Hüttenbau kommen immer Gegner, aber die Regel wird bei insg. 10 getöteten Feinden sofort wieder entfernt, wodurch dann keine Gegner mehr kommen, auch wenn ich weiterhin Hütten baue.  Dasselbe Prinzip dann zum Beispiel für das Schloss. Nur das hier dann insg. 30 Feinde (also 20 weitere feinde) tot sein müssen, bevor die Regel deaktiviert wird.  Kann man das auf diese Weise irgendwie koppeln?

Falls das nicht zu koppeln geht, wäre es auch nicht tragisch, wenn dann bei jedem Hüttenbau immer wieder Feinde kommen. Man müsste dann aber die Zähler immer wieder auf Null setzen können, sodass dann jedesmal z.b 10 Feinde getötet werden müssen, bis der Angriff aufhört. Also falls man die Anzahl zu tötender Feinde nicht mit dem Auslöser koppeln kann, kann man das auch bei z.b 10 belassen, egal ob der Auslöser nun eine Hütte, oder ein Schloss ist.
Parent - - By Clonkonaut (More than 200 posts.) Date 21.09.2014 14:18

> würde dann also der Regel Bescheid gegeben werden können. Aber welcher Regel genau?


Die Funktion wird bei allen Regelobjekten im Spiel aufgerufen. Aber nur deine selbst geschriebene würde diesen Aufruf verarbeiten, da die anderen die entsprechende Funktion nicht besitzen. Dazu eine eigene Zählregel basteln, die dann die Angriffsregel entfernt und den Zähler auf Null setzt. Beim nächsten Erscheinen der Angriffsregel geht dann alles wieder von vorne los.
Parent - - By Serpens66 (More than 200 posts.) Date 21.09.2014 15:05
hattest du meinen editierten Text bezüglich des Koppelns von Auslöser zu Anzahl der zu tötenen Gegner noch gelesen?

Okay... also ich muss eine eigene Regel schreiben, welche einen Zähler enthält und die Angriffsregel bei x entfernen kann und dann der Zähler auf Null gesetzt wird. 
Wie baue ich so einen Zähler? Ich tippe jetzt mal auf das Zauberwort "Array". Nur ist "Array" für mich noch ein absolutes fremdwort und ich hab noch nicht kapiert, was das genau ist, geschweigedenn wie es funtkioniert :D

Und angenommen ich nenne meine Zählfunktion dann "func Zähler()". Dann muss ich in das Todesscript der gegner reinschreiben:
GameCallEx(Zähler());
?
(ach wie macht man ein appendto für eine Funtkion die bereits im Objekt vorhanden ist, ohne diese zu ersetzen, sondern nur zu erweitern? )

Edit:
ach und das ersetzten der Predator durch Monster oder andere Tiere funktioniert =) Erstelle mir jetzt verschiedene Regeln, die Monster/Feuermonster usw vom Himmel regnen lassen =)
Parent - By Luchs (More than 1000 posts.) Date 21.09.2014 16:28

>(ach wie macht man ein appendto für eine Funtkion die bereits im Objekt vorhanden ist, ohne diese zu ersetzen, sondern nur zu erweitern? )


Du kannst bestehende Funktionen grundsätzlich nur ersetzen, aber du hast die Möglichkeit, die ersetzte Funktion mit `inherited()` aufzurufen.
Parent - - By Clonkonaut (More than 200 posts.) Date 21.09.2014 18:23

> Wie baue ich so einen Zähler?


Eine Variable, die eine Zahl enthält und hochgezählt wird. Kein Array!

> GameCallEx(Zähler());


GameCallEx("Zaehler");
(benutze niemals Umlaute in Funktionsnamen)

> (ach wie macht man ein appendto für eine Funtkion die bereits im Objekt vorhanden ist, ohne diese zu ersetzen, sondern nur zu erweitern? )


Dafür ist inherited() da. Damit rufst du die sonst überladene Funktion auf. In dein appendto würde also kommen:
protected func Death() {
  GameCallEx("Zaehler")
   return _inherited();
}
Parent - - By Serpens66 (More than 200 posts.) Date 21.09.2014 18:36

>Eine Variable, die eine Zahl enthält und hochgezählt wird. Kein Array!


okay.. müsste es ungefähr so klappen?

func Zaehler()
{
var anzahl = 1;
anzahl++;
Entferner();
}

func Entferner()
{
if anzahl >= 10
RemoveObject(FindObejct(idRule));
}


Und, sofern diese 2 Funktoinen richtig sind, muss ich diese ins Script einer neu erstellte Regel einfügen? Muss ich sonst noch was beachten, z.b im DefCore oderso?
Parent - - By Clonkonaut (More than 200 posts.) Date 22.09.2014 12:31
Ungefähr so.

Die Variable muss eine (objekt-)lokale sein, keine funktionslokale. "var" bezeichnet immer eine funktionslokale Variable. D.h. "anzahl" wird erstellt, sobald die Funktion Zaehler() aufgerufen wird und sobald die Funktion fertig abgearbeitet ist, wird "anzahl" wieder vergessen. Eine lokale Variable bleibt dauerhaft in dem Objekt gespeichert, bis das Objekt selbst gelöscht wird. Eine solche Variable wird außerhalb jeder Funktion im Objektscript über das Keyword "local" definiert.
Und weil ich eh schon dabei bin: Darüber hinaus gibt es auch noch globale Variablen. Diese können von jedem Script aus benutzt werden und werden bis zum Ende des Szenarios gespeichert. Das Keyword dafür ist "static", die Variable wird einfach irgendwo (außerhalb von Funktionen) definiert.

Entsprechend dein Script:

local anzahl; // anzahl wird als lokale Variable definiert

func Zaehler()
{
  anzahl++; // anzahl wird um 1 erhöht
  if (anzahl > 10) // Bei if die Bedingung immer in runde Klammern verpacken
  { // Durch diesen Block geschweifter Klammern nach einer If-Abfrage kannst du mehrere Befehle unter dem if zusammenfassen, die nur im Falle von anzahl > 10 ausgeführt werden
    if ( FindObject(idRule) ) // Zur Sicherheit überprüfst du vor dem Entfernen, ob das Zielobjekt vorhanden ist*
      RemoveObject( FindObject(idRule) );
    anzahl = 0; // anzahl zurücksetzen
  }
}


Ich habe deine beiden Funktionen in eine zusammengefasst. idRule ist durch die entsprechende Regel-ID zu ersetzen.

*: Warum ist es wichtig, dass du vor dem Aufruf von RemoveObject noch eine If-Abfrage einfügst, die prüft, ob das Zielobjekt (die andere Regel) vorhanden ist? FindObject(idRule) würde 0 (vielleicht auch false, weiß ich gerade nicht) zurückliefern, wenn das Objekt idRule nicht gefunden wird. Dadurch wäre der Aufruf dann "RemoveObject(0);". Das würde dazu führen, dass das aufrufende Objekt selber gelöscht wird und das wäre deine eigene Regel. Die würde sich also selber löschen, sobald aus irgendeinem Grunde die Angriffsregel schon entfernt ist.

> Muss ich sonst noch was beachten, z.b im DefCore oderso?


Die Kategory sollte C4D_Rule sein.
Parent - - By Serpens66 (More than 200 posts.) Date 22.09.2014 19:29 Edited 24.09.2014 08:17
Das mit den Variablen local und static habe ich zufällig gerade hier https://clonkspot.org/forum/topic_show.pl?tid=308 rausgefunden, aber danke für deine ausführliche erklärung :)

Werde dein Script gleich mal ausprobieren und dann hier editieren, ob es funtkioniert :)

>Dadurch wäre der Aufruf dann "RemoveObject(0);". Das würde dazu führen, dass das aufrufende Objekt selber gelöscht wird und das wäre deine eigene Regel.


ah okay, das wusste ich nicht, danke =)

EDIT:

Im Defcore muss es außerdem ein static Objekt sein, wie ich gemerkt habe ;)
Ich habe nun mal folgendes ins Regelskript geschrieben:

/*-- Angriffsentferner --*/

`#strict`
local anzahl; // anzahl wird als lokale Variable definiert

func Zaehler()
{
  var wert;
  `wert = 5*ObjectCount(_AN_);`
  anzahl++; // anzahl wird um 1 erhöht
  if (anzahl > wert) // Bei if die Bedingung immer in runde Klammern verpacken
  { // Durch diesen Block geschweifter Klammern nach einer If-Abfrage kannst du mehrere Befehle unter dem if zusammenfassen, die nur im Falle von anzahl > wert ausgeführt werden
`   if ( FindObject(PRA_) ) // Zur Sicherheit überprüfst du vor dem Entfernen, ob das Zielobjekt vorhanden ist*`
      RemoveObject( FindObject(PRA_) );
    anzahl = 0; // anzahl zurücksetzen
    Log("Regel entfernt");
  }
}

// Kann mittels des Spielzielauswählers ausgewählt werden
public func IsChooseable() { return(1); }


Die Forenformatierung nervt irgendwie.. bei der Definition von "wert" steht eigentlich:  wert = 5 multipliziert mit ObjectCount([unterstrich]AN[unterstrich]) (habs mit `nun bearbeitet)
Ich teste gerade noch ein wenig, wie gut das genau klappt, und editiere hier dann nochmal mit "edit2".

EDIT2:
okay, also das script funktioniert einwandfrei, wenn ich auf meine eigene "wert"Konstruktion verzichte und stattdessen eine feste Zahl eintrage, z.b "anzahl > 5".
Wenn ich das mit "wert" mache, dann wird es viel zu früh ausgelöst, ich glaube bei bei jedem einzelnen oder jedem zweiten Tod. Weißt du woran das liegen könnte?
Könnte es wieder an "var" bzw. "local" liegen? Eigentlich sollte "var doch funktionieren , da wir ja nur eine Funktion haben.
(fertig mit editieren)
Parent - - By Gecko (More than 500 posts.) Date 23.09.2014 20:38
Mal von den Problemen, die das Script anscheinend macht (nicht näher angeguckt) gibt es doch RemoveAll(ID)?
Das entfernt doch alle Objekte mit der spezischen ID. Das machts doch einfacher.. :)
Parent - - By Serpens66 (More than 200 posts.) Date 23.09.2014 21:14
ja danke stimmt, hatte schon überlegt, ob ich dann einfach 5 mal RemoveObject hinschreibe, aber mit RemoveAll ist das natürlich besser ;)

Ich vermute, dass ich einen Fehler beim definieren von "wert" habe. Vermutlich kann ich da nicht   wert = 5 * ObjectCount(AAN_); schreiben.. warum auch immer...
Parent - - By Zapper (More than 500 posts.) Date 23.09.2014 21:16

> ob ich dann einfach 5 mal RemoveObject hinschreibe, aber mit RemoveAll ist das natürlich besser ;)


Ansonsten könntest du das auch zB so lösen:
var obj;
while (obj = FindObject2(Find_ID(AAN-))) obj->RemoveObject();

oder so
for (var obj in FindObjects(Find_ID(AAN-))) obj->RemoveObject();

> wert = 5 \* ObjectCount(AAN-); schreiben.. warum auch immer...


Müsste gehen. Ich schätze eher, dass du dich sonst irgendwie vertippt hast. Was sagt denn die Fehlermeldung?

Edit: Luchs, wie escape ich die Unterstriche...? Dein markdown springt immer an und \ funktioniert nicht...
Parent - - By Serpens66 (More than 200 posts.) Date 23.09.2014 21:39 Edited 23.09.2014 21:57
ich denk removeAll ist schon ausreichend :)
Ich hab die Regel ID schon von [unterstrich]AN[unterstrich] umbenannt in AAN[unterstrich], weil ich dachte, dass es daran liegt, aber tuts nicht :D

Es kommt keine Fehlermeldung, es ist aber so, dass die Regel schon beim ersten getöteten Monster beendet wird.  Die Regel ist einmal aktiv, also sollte es eigentlich erst ab 5 Monstern beendet werden.   Ich kanns ja mal eben testen, wie es ist, wenn die Regel 2 mal aktiv ist.. ^^

edit:
wenn dir Regel 2 mal aktiv ist, kommt der Log "Regel entfernt" erst nach 6 getöteten Monstern, allerdings kommt die Meldung 2 mal (warum?) und auch schon vorher fielen keine neuen Monster mehr vom himmel, ich hatte 4 monster aufeinmal getötet, dann gewartet, kamen keine monster mehr, dann die restlichen 2 erledigt und es stand 2 mal, dass die REgel entfernt wurde. (auch wenn die Regel nur einmal aktiv ist, kommt die Log Meldung zweimal)
edit2: habs nochmal probiert und nach 5 getöteten monstern, kamen trzd noch neue, also wird die Regel wohl wirklich erst nach 6 monstern entfernt, fragt sich nur, warum gerade 6...
Parent - - By Serpens66 (More than 200 posts.) Date 23.09.2014 22:37 Edited 23.09.2014 22:40
so jetzt noch mal eben eine Korrektur:

Wenn ich mit dem hier stehenden Skript die Regel 1 mal aktiviert habe, passiert folgendes:
Nach einem getötetem Monster wieder einmal "Regel entfernt" ausgegeben.

Wenn ich die Regel 2 mal aktiviert habe, wird nach 6 getöteten Monstern zweimal "REgel entfernt" ausgegeben.

Wenn ich die Regel 3 mal aktiviert habe, wird nach 11 getöteten Monstern dreimal "Regel entfernt" ausgegeben ;)

Finde das Muster :D
Ich frag mich nur, wieso sich dieses Muster ergibt... und wie es richtig sein müsste..
ich möchte, dass bei einer aktivierten Regel 5 Monster,, bei 2mal 10 monster usw. getötet werden müssen.    gut 1  6  11 usw. wäre zur not auch okay.
Und ich möchte natürlich, dass die Regel nur einmal entfernt wird, für den Fall, dass ich doch langsam die Monsterzahl verringern will, anstatt komplett alle aufeinmal wegzunehmen.
Parent - - By Zapper (More than 500 posts.) Date 24.09.2014 08:14

>Finde das Muster


Das klingt danach als ob du bei dem Vergleich mit der Anzahl lieber ein `>=` anstatt eines `>` haben willst
Parent - - By Serpens66 (More than 200 posts.) Date 24.09.2014 08:40
Eigentlich dachte ich, dass wenn die Regel einmal aktiviert es, der objectcount die Zahl 1 ergibt. Dadurch sollten dann 5 Monster getötet werden müssen.  Aber ist auch nicht schlimm, wenn es 2 mal aktivert sein muss, damit es 5 bzw. 6 ergibt.
Was aber stört, ist dass der Log "regel entfernt"  bei 2 mal aktivierter Regel 2 mal auftaucht. Ich schließe daraus, dass auch der Remove Befehl dadurch 2 mal ausgeführt wird.
Kann man das irgendwie ändern, sodass der Remove Befehl nur einmal ausgeführt wird, auch wenn die Regel mehrfach aktiviert ist?

Mir ist beim Erstellen diverser Angriffs und Angriffsentferner Regeln aufgefallen, dass Regeln nicht unter "eigenschaften" als auswählbar angezeigt werden, wenn das Picture im DefCore eine gewisse Größe hat, die womöglich mit der verwendeten grafik zusammenhängt. Kennt ihr da einen Zusammenhang?
Parent - - By Zapper (More than 500 posts.) Date 24.09.2014 09:16

>Eigentlich dachte ich, dass wenn die Regel einmal aktiviert es, der objectcount die Zahl 1 ergibt. Dadurch sollten dann 5 Monster getötet werden


Und dann guckst du ob `zaehler > wert`. Und das ist das erste Mal wahr, wenn `zaehler` 6 ist (weil `wert` ist ja 5).

>Mir ist beim Erstellen diverser Angriffs und Angriffsentferner Regeln aufgefallen, dass Regeln nicht unter "eigenschaften" als auswählbar angezeigt werden, wenn das Picture im DefCore eine gewisse Größe hat, die womöglich mit der verwendeten grafik zusammenhängt. Kennt ihr da einen Zusammenhang?


Ja. Wenn der Picture-Eintrag ungültig ist, wird die Regel nicht angezeigt.
Der Picture-Eintrag muss ein Rechteck in der echten Grafik definieren.

>Kann man das irgendwie ändern, sodass der Remove Befehl nur einmal ausgeführt wird, auch wenn die Regel mehrfach aktiviert ist?


Sag mir nochmal genau was du eigentlich machen willst.
Das Ding oben ist der Script von einer Regel, die irgendwas machen soll wenn eine bestimmte Anzahl von Monstern getötet werden?
Parent - - By Serpens66 (More than 200 posts.) Date 24.09.2014 10:09 Edited 24.09.2014 10:12

>Und dann guckst du ob zaehler > wert. Und das ist das erste Mal wahr, wenn zaehler 6 ist (weil wert ist ja 5).


stimmt, mein Denkfehler. Es muss also heißen zaehler = wert, damit die if Bedingung erst bei 5 get. monstern ausgelöst wird, oder?
hmm... hab es grade mal mit gleichheitszeichen getestet, irgendwas stimmt da nicht..  habe `  wert = 5 * ObjectCount(AAN_);`  mit 2 aktivierten Regeln getestet. Nachdem ich gleichzeitig 2 Monster getötet habe, kam 4 mal die "Regel entfernt" Meldung.
Jetzt habe ich   `wert = 5*ObjectCount(AAN_);` (also die Leerzeichen beim Malzeichen entfernt,hat das auswirkung?) und die Regel einmal aktktiviert, habe jetzt schon 10 monster getötet, aber es ist noch nichts passiert...
Es ist schwierig mit diesen Tests, weil es immer so aussieht, als würde sich alles ändern, obwohl das Script dasselbe ist :D ich teste nochmal weiter und editiere das dann hierhin, ob ich da ein Muster erkennen kann...

>Sag mir nochmal genau was du eigentlich machen willst.


Ich habe ein paar regeln erstellt, welche dafür sorgen, dass Monster gespawnt werden, das sieht so aus:
/*-- Monsterattacken --*/

`#strict`

// Kann mittels des Spielzielauswählers ausgewählt werden
public func IsChooseable() { return(1); }

protected func Initialize()
{
  // Eigene Position festlegen
  SetPosition();
  return(1);
}

/* Eventuell Monster erstellen */
private func DoMonsters()
{
  if (Random(30) || ObjectCount(MONS)>ObjectCount(MOAN)) return(1);
  var pMonster = CreateObject(MONS, Random(LandscapeWidth()), 50, -1);
  SetR(-180, pMonster);
  return(1);
}


Nun wollte ich einen Angriffsentferner, der diese oben genannte Angriffsregel bei Erfüllung der Bedingung wieder entfernt. Die bedingung soll sein, dass x Monster getötet werden müssen, wobei sich diese Anzahl an der Mange der aktivierten "Angriffentferner"-Regel orientiert. Je häufiger der Entferner aktiviert ist, desto mehr Monster müssen getötet werden, bevor die Angriffsregel entfernt wird.
Sieht dann bisher so aus:
/*-- Angriffsentferner --*/

`#strict`
local anzahl; // anzahl wird als lokale Variable definiert

func Zaehler1()  // Monsterangriff entfernen
{
  var wert;
`  wert = 5*ObjectCount(AAN_);`
  anzahl++; // anzahl wird um 1 erhöht
  if (anzahl = wert) // Bei if die Bedingung immer in runde Klammern verpacken
  { // Durch diesen Block geschweifter Klammern nach einer If-Abfrage kannst du mehrere Befehle unter dem if zusammenfassen, die nur im Falle von anzahl > wert ausgeführt werden
  `  if ( FindObject(MOAN) )  // Zur Sicherheit überprüfst du vor dem Entfernen, ob das Zielobjekt vorhanden ist*`
      RemoveAll(MOAN);
    anzahl = 0; // anzahl zurücksetzen
    Log("MonsterRegel entfernt");
  }
}

// Kann mittels des Spielzielauswählers ausgewählt werden
public func IsChooseable() { return(1); }


MONS ist das Tier "Monster".
MOAN ist die ID der Angriffsregel.
`ANN_ ist die ID der Angriffsentfernerregel`
Der GameCallEx Befehl wurde in die Deathfunktion des Monsterscripts geschrieben, sodass es eine Meldung an den Zaehler1 übergibt, wenn ein Monster stirbt.

Noch 2 Fragen, die nicht direkt mit den Regeln zu tun haben:
- Das Tier Schlange hat keine Death Funktion im Script. Es gibt auch kein neues Objekt für die tote Schlange, wie es z.b bei den Monstern der Fall ist. Daraus schließe ich, dass die Schlange einfach nur ihre Aktion zu "Dead" verändert, aber dieselbe ID behält. Meine Regeln überprüfen aber die Anzahl der Schlangen, welche sich aber auch durch ihren Tod nicht ändert. Beim Berserker aus dem Hazardpack ist der Fall ähnlich, das habe ich gelöst, indem ich in die Deathfunktion des Berserkers "removeobject" eingefügt habe, wodurch die Berserker beim Tod einfach verpuffen, anstatt ewig in der Landschaft rumzuliegen. Dadurch klappt meine Regel auch. Bei den Schlangen finde ich aber keinen Befehl im Script, der dafür sorgt, dass sie sterben bzw. ihre Aktion auf "dead" ändern sollen, weshalb ich auch nicht weiß, wo ich da "removeobject" einfügen könnte.
- Der Berserker des Hazardpacks ist brennbar. Im script kann man einen "AddFire" Effekt ausfindig machen, der wohl ausgelöst wird, wenn er Feuerschaden bekommt. Allerdings habe ich diesen Effekt bereits entfernt und der Berserker fängt immernoch feuer, wenn er vor einem brennenden Baum steht, oder man eine brandbombe wirft.
Das ist aus 3 Gründen nicht gut: 1. Beim Brennen ensteht sehr viel Rauch der die Sicht behindert. 2. Der Rauch sorgt dafür, dass das Speil sehr stark hakt.  3. sind die Berserker so viel zu leicht zu töten, da sie so lange brennen, bis sie tot sind.
Deswegen: wie kann ich machen, dass die Berserker kein Feuer mehr fangen? Ich konnte nichts diesbezüglich im Script finden und auch im DefCore steht nichts hilfreiches.
Parent - - By Zapper (More than 500 posts.) Date 24.09.2014 10:28

>hab es grade mal mit gleichheitszeichen getestet, irgendwas stimmt da nicht..


Jo, hatten wir schonmal. Ein einfaches Gleichheitszeichen ist eine Zuweisung und kein Vergleich.
Ansonsten würde ich auch `>=` vorschlagen, weil du sonst Probleme bekommst wenn zB zwei Monster gleichzeitig getötet werden

>Bei den Schlangen finde ich aber keinen Befehl im Script, der dafür sorgt, dass sie sterben bzw. ihre Aktion auf "dead" ändern sollen, weshalb ich auch nicht weiß, wo ich da "removeobject" einfügen könnte.


Das passiert automatisch.
Du könntest einfach den `Death()` Befehl einfügen wenn du magst.
Ansonsten könntest du die lebenden Schlangen auch so zaehlen: `ObjectCount2(Find_ID(SNKE), Find_OCF(OCF_Alive))`

>Deswegen: wie kann ich machen, dass die Berserker kein Feuer mehr fangen? Ich konnte nichts diesbezüglich im Script finden und auch im DefCore steht nichts hilfreiches.


Der DefCore-Eintrag dazu heisst `ContactIncinerate` wenn ich mich grad nicht irre. Setz den einfach auf 0.
Wenn du das nur per `#appendto` machen kannst, könntest du zB so was einfügen:
```
#appendto BLA_
func Incineration() // wird immer aufgerufen wenn das Objekt Feuer fängt
{
    Extinguish(); // gleich wieder löschen
    return _inherited(); // zur Sicherheit; in #appendtos immer eine gute Idee
}
```
Parent - By Serpens66 (More than 200 posts.) Date 24.09.2014 10:50

>Jo, hatten wir schonmal. Ein einfaches Gleichheitszeichen ist eine Zuweisung und kein Vergleich.
>Ansonsten würde ich auch >= vorschlagen, weil du sonst Probleme bekommst wenn zB zwei Monster gleichzeitig getötet werden


ach mist, dieser Anfängerfehler hätte mir nicht mehr passieren dürfen :D  aber naja, in anderen Scripten hab ich das immerhin richtig gemacht :)  Aber stimmt >= ist sinnvoller.

>Das passiert automatisch.
>Du könntest einfach den Death() Befehl einfügen wenn du magst.
>Ansonsten könntest du die lebenden Schlangen auch so zaehlen: ObjectCount2(Find_ID(SNKE), Find_OCF(OCF_Alive))


ah okay, wieder was gelernt, danke :)

>Der DefCore-Eintrag dazu heisst ContactIncinerate wenn ich mich grad nicht irre. Setz den einfach auf 0.


ach stimmt, den habe ich in der DefCore übersehen, danke =)
Auch danke für den Löschbefehl, der ist sicherlich auch noch irgendwann hilfreich :)

ich glaube damit ist in diesem Thread vorerst alles geklärt , danke :)
Parent - - By Serpens66 (More than 200 posts.) Date 24.09.2014 13:34 Edited 24.09.2014 14:04
mal zu der incinerationfunktion, wozu ich auch gleich noch was in meinen Leuchtfeuerthread schreibe:

Was passiert, wenn ich in ein Script, in dem bereits ein Verhalten für incineration defintiert ist, unten ans script dann deine Löschfunktion dranhänge, weil ich übersehen habe, dass es bereits ein incineration funktion im Skript gibt.
Werden durch das _inherited dann die Funnktionen in eine zusammengefügt, oder führt das zu Fehlern?

Letzlich möchte ich wissen, ob ich einfach ohne Bedenken bei jedem Objekt so eine Funktion hinzufügen kann, ohne darauf achten zu müssen, ob so eine funktion schon irgendwo anders im Skript verwendet wird.
Ach: in einem appendto funktioniert das vermutlich auf diese weise gut. aber was wenn ich es direkt ins Skript des Objekts schreibe? Sollte keine Probleme machen, oder?
Und was ist, wenn das Script einen Wert ausgeben muss, weshalb da steht "return (1);"   muss ich das dann durch return _inherited ersetzen, oder kann man in einer Funktion auch mehrere return drin stehen haben?

Und wie ist das bei den Death Funktionen, die automatisch ablaufen. Wenn ich da nun eine eigene Deathfunktion reinschreibe, weil ich z.b will, dass beim tod noch ein bestimmtes geräscuh abgespielt wird. Muss ich dann auch return _inherited(); schreiben, weil sonst alles was eig automatisch beim tod gemacht wird, nicht mehr wirkt?

Edit:
ich habs grad mal getestet. Wenn es die Funktion bereits gibt und ich sie nochmal mit einem anderen Befehl und _inherited dazuschreibe, dann wird diese Funktion nicht beachtet, sondern nur die erste bereits vorher vorhandene.  Demnach wäre es sinnvoll, anstelle es  direkt ins skript zu schreiben, immer ein appendto zu machen?   Möchte nur ungerne für jedes objekt ein eigenes appendto machen, weil die funktionen immer etwas anders sind, die zugefügt werden müssen.  Das würde meinen System Ordner zu voll machen... hmm... ich könnte einen extra obejktordner dafür machen.
Parent - - By Zapper (More than 500 posts.) Date 24.09.2014 17:25
Nochmal was allgemeines zu Funktionen:

Funktionen sind sozusagen Codeschnipsel, denen du Namen geben kannst und die du bei beliebigen Situationen wiederbenutzen kannst.
Dabei können die Funktionen einen Rückgabewert haben. Der Rückgabewert ist sozusagen das Ergebnis der Funktion.

Um die Funktionen beliebigen Situationen anpassen zu können, kannst du ihnen bei Aufrufen benutzerdefinierte Parameter mitgeben.

Beispiel:
```
//die Funktion nimmt zwei Werte entgegen. Im Folgenden Block heissen die a und b
global func Maximum(a, b)
{
     if (a > b) return a;
     return b;
}```

Wenn du den Rückgabewert einer Funktion per `return` festlegst, wird die Funktion an dem Punkt auch verlassen und ist zuende.
Die von oben kannst du zB so benutzen:
```
Log("%d", Maximum(1, 5)); // a ist 1 und b ist 5
Log("%d", Maximum(0, -2)); // a ist 0 und b ist -2
```
In dem Beispiel wird einmal `5` (weil a nicht größer b -> return b) und einmal `0` (weil a > b -> return a) ausgegeben.

`inherited` macht nichts magisches - es ruft einfach nur die nächste Funktion mit dem gleichen Namen auf, wenn es eine gibt.
Und mit `return inherited(...);` gibst du dann einfach das Ergebnis von der Funktion weiter.

Zum Beispiel könnte ich direkt unter meine Funktion von oben noch das hier schreiben:
```global func Maximum(a, b) // gleicher Name!!
{
    if (a == b) return 0;
    return inherited(a, b);
}```

Die Funktion hat als Ergebnis immer `0` wenn der Wert von `a` gleich dem Wert von `b` ist. Ansonsten ist das Ergebnis auch das Ergebnis der oberen `Maximum`-Funktion.

*Das heisst:*
* Immer wenn du die "alte" Funktion auch noch benutzen willst (zB bei deinem Death()-Zeugs), brauchst du irgendwo ein `inherited`. Und immer wenn du das Ergebnis von der Funktion auch weitergeben willst, sollte das ein `return inherited` sein.
* Du kannst ohne Probleme einfach ans Ende von einem Script noch eine Funktion mit gleichem Namen packen. Aber wenn du die Funktion drüber auch noch "benutzen" willst, brauchst du ein inherited!
Parent - - By Serpens66 (More than 200 posts.) Date 25.09.2014 10:05
okay... fast verstanden...
es muss nur noch deutlicher werden, worin der unterschied ziwschen return inherited und nur inherited besteht, anhand eines Beispiels:
Du sagst mit return, wird nur das Ergebnis verwendet... also die bedingungen der Funktion nicht?  Das heißt in deinem Beispiel würde das return inherited einfach nur a und b ausgeben, aber mit der Bedinungen aus deiner zweiten Funktion, demnach andere Ergebnisse, als wenn du nur inherited geschrieben hättest, richtig?

Falls das nicht stimmt, gib mir bitte nochmal ein Beispiel, in dem return inherited gegenüber nur inherited etwas anderes zur Folge hat.
Parent - - By Clonkonaut (More than 200 posts.) Date 25.09.2014 12:08 Edited 25.09.2014 13:27

> worin der unterschied ziwschen return inherited und nur inherited besteht


return und inherited sind zwei unterschiedliche Befehle, die nur häufig kombiniert werden. "return" sorgt einfach dafür, dass die aktuelle Funktion beendet wird (egal, ob danach die Befehle kommen) und ggf. der Wert hinter return als Rückgabewert auf den Aufruf der Funktion geliefert wird. Ob hinter return eine 1 oder inherited() steht, macht dabei für return keinen Unterschied.

Beispiel:
// Irgendwo im Script, wo ist nicht relevant für das Beispiel
var a = 0;
a = SuperFunktion();
Log("%v", a);

func SuperFunktion()
{
  return 1;
  return 2;
}


Ergebnis im Log: "1". Bereits durch return 1; wird die Funktion SuperFunktion beendet und 1 wird als Rückgabewert geliefert, der oben dann in der Variable a gespeichert wird. Entsprechend wird durch Log die 1 ausgegeben.

_inherited(); sorgt bei Überladungen (d.h. in Fällen von #include und #appendto) dafür, dass die überladene Funktion aufgerufen wird, die ansonsten unterdrückt wäre.

Beispiel:
// Objekt 1 mit ID AAAA:

func SuperFunktion()
{
  return 1;
}

------------------
// Objekt 2

`#appendto AAAA // Dieses Script wird also an Objekt AAAA angehängt`

func SuperFunktion() // SuperFunktion() wird überladen
{
  return 2;
}

------------------
// Irgendwo in einem Script

var a = 0;
a = FindObject(AAAA)->SuperFunktion(); // Es wird ein Objekt mit der ID AAAA gesucht und bei diesem dann SuperFunktion() aufgerufen, der Rückgabewert in a gespeichert
Log("%v", a);


Ergebnis im Log: "2". Die Überladung der Funktion hat dafür gesorgt, dass nur return 2; zum Tragen kommt. Die ursprüngliche Funktion SuperFunktion() wird nicht mehr aufgerufen und damit wird auch nicht 1 zurückgegeben, wie die ursprüngliche Funktion es vorgesehen hätte.

Durch return _inherited(); rufe ich die ursprüngliche Funktion auf und falls diese einen Rückgabewert hat, sorgt return dafür, dass wiederum der Wert zurückgegeben wird. Dabei könnte ich das auch so schreiben und hätte das gleiche Ergebnis:

var a = _inherited();
return a;


Beispiel:
// Objekt 1 mit ID AAAA:

func SuperFunktion()
{
  return 1;
}

------------------
// Objekt 2

`#appendto AAAA // Dieses Script wird also an Objekt AAAA angehängt`

func SuperFunktion() // SuperFunktion() wird überladen
{
  return _inherited();
}

------------------
// Irgendwo in einem Script

var a = 0;
a = FindObject(AAAA)->SuperFunktion();
Log("%v", a);


Ergebnis: "1". Da die überladende Funktion nun die überladene Funktion aufruft, wird return 1; 1 als Rückgabewert geben, welcher durch return _inherited(); wiederum weitergegeben wird. Manchmal muss ich aber gar nicht den Rückgabewert der ursprünglichen Funktion verwenden, sondern es nur wichtig, dass ich diese aufrufe.

Beispiel:
`#appendto CLNK // Wird an den Clonk angehängt`

protected func Death() // Wenn der Clonk stirbt
{
  // Ich möchte gerne, dass diese Logzeile geschrieben wird, wenn ein Clonk stirbt
  Log("Oh nein, es wurde ein Clonk getötet");
  // außerdem soll alles mögliche passieren, was passiert, wenn ein Clonk stirbt
  _inherited();
}


Hier wird die Funktion Death des Clonks überladen, es wird durch Log eine Zeile Text ausgegeben und dann rufe ich Death() im usprünglichen Clonkscript auf, weil darin wichtige Sachen passieren (z.B. wird die Action des Clonk auf "Death" oder sowas gesetzt).

Und abschließend noch folgendes Beispiel zur Verdeutlichung: Sagen wir, ich will alle Flints aus Objects.c4d doppelt so groß explodieren lassen. Ich könnte für jeden Flint ein #appendto schreiben, in welchen ich jeweils die Funktion ExplodeSize() überlade und einen doppelt so großen Wert hineinschreibe. Für den Feuerstein wäre das:
`#appendto FLNT`

public func ExplodeSize()
{
  return(36);
}


Usw. - ganz schön viele #appendto-Objekte, die ich dafür brauche! Ich könnte auch einfach immer nur den ursprünglichen Wert von ExplodeSize() verdoppeln und alle Flints in einem Objekt überladen. Und zwar so:
`#appendto FLNT // Feuerstein`
`#appendto SFLN // Super-Flint`
`#appendto TFLN // Tera-Flint`

public func ExplodeSize()
{
  return _inherited() * 2;
}


_inherited() liefert mir die Explosionsstärke, wie sie im jeweiligen Flintscript definiert ist und danach wird diese durch * 2 verdoppelt und dann als Rückgabewert benutzt. Ich muss das nicht in einer Zeile schreiben, so würde genau das gleiche herauskommen:
`#appendto FLNT // Feuerstein`
`#appendto SFLN // Super-Flint`
`#appendto TFLN // Tera-Flint`

public func ExplodeSize()
{
  var size = _inherited();
  size = size * 2;
  return size;
}
Parent - - By Clonkonaut (More than 200 posts.) Date 25.09.2014 12:13
Okaaaay. Dieses Forum macht anscheinend Formatierungskram rein, ohne dass das irgendwo beschrieben ist? Jedenfalls sollten die Zeilen da nicht superfett sein, da ist einfach eine Raute (#) davor.

Aber keine Ahnung, wie ich das richtig mache hier.
Parent - - By Serpens66 (More than 200 posts.) Date 25.09.2014 12:30
du kannst es mit dem ` Zeichen (dem neben dem Fragezeichen auf der tastatur in Großschrift) einkesseln, dann wird die Zeile nicht formatiert. Hab aber noch nicht rausgefunden, wie man damit einen ganzen Textblock machen kann, ohne es in jede Zeile zu schreiben.

Deine Erklärung lese ich mir später durch, bin die Tage erstmal nicht erreichbar und antworte dann vermutlich montag oderso ;) 
Wäre super wenn ihr euch die Tage wenn ihr Zeit habt mal meine anderen noch offenen Threads anschaut, ob ihr Ideen dazu habt =)
Parent - - By Clonkonaut (More than 200 posts.) Date 25.09.2014 13:27
Nervkram da.
Parent - By Serpens66 (More than 200 posts.) Date 29.09.2014 15:45
so habe es mir jetzt durchgelesen, vielen dank für die verständliche Erklärung :)
Jetzt hab ich jedenfalls verstanden, wie man es nutzt und es kommt nun auch in meinen appendtos zum einsatz =)
Parent - By Clonkonaut (More than 200 posts.) Date 25.09.2014 11:45

> ich habs grad mal getestet. Wenn es die Funktion bereits gibt und ich sie nochmal mit einem anderen Befehl und _inherited dazuschreibe, dann wird diese Funktion nicht beachtet, sondern nur die erste bereits vorher vorhandene.


Das wäre auch völliger Quatsch. Wenn du sowieso das Script des Objekts direkt bearbeiten kannst, dann kannst du auch die schon vorhandene Funktion bearbeiten. Jedes Programm mit dem du heutzutage Text bearbeitest bietet dir eine Suchfunktion (in der Regel über die Schnelltasten Strg+F erreichbar), so dass du niemals selber so etwas suchen musst.
Parent - - By Luchs (More than 1000 posts.) Date 24.09.2014 06:47
Am besten für Code die Codeblöcke verwenden mit \```. Wenn ich dazu komme, werden die bald auch Syntax-Highlighting unterstützen..!
Parent - - By Zapper (More than 500 posts.) Date 24.09.2014 06:59
Die gehen aber nicht inline..
Parent - - By Luchs (More than 1000 posts.) Date 24.09.2014 07:00
Inline mit einfachen \`
Parent - - By Zapper (More than 500 posts.) Date 24.09.2014 08:00
Dingdong `Test();`
Parent - By Luchs (More than 1000 posts.) Date 24.09.2014 09:30
/o/
Up Topic Deutsch / Hilfestellung / [Gelöst] Regeln durch Auslöser dazuschalten

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill