[Gelöst] Gegenstand radioaktiv machen

Im StarEmpire Pack gibt es ja einen nuklearen Sprengsatz, der nach Explosion strahlt und je näher Clonks dem Explosionsort kommen, desto mehr schaden bekommen sie.

Hier der Explosionsteil der NuklearBombe:
/* Explosion /

public Incineration:
  // Fragmente
  var i, obj;
  for (i = 1; i <= 10; i++)
  {
    obj = CreateObject(SFRX, Random(50)-25, Random(50)-25, GetOwner());
    ObjectCall(obj, "Settings", i, 25);
  }
  // Druckwelle & Strahlung
  obj = CreateObject(SWAV);
  ObjectCall(obj, "SetSize", 300,GetX(),GetY(), 400+Random(100), 700+Random(300));
  // Explosion
  CreateParticle("Blast", 0,0,0,0, 1500,RGB(0,255,0));
  Explode(160);
  return(1);


Es wird eine Druckwelle SWAV ausgelöst, welche dann widerum für die radioaktivität sorgt, ein teil aus dem SWAV Script:


public func SetSize(newsize, posX, posY, rad_effect, emp_effect)
{
  var obj;
  // Strahlung freisetzen
  if (rad_effect)
  {
    obj = CreateObject(SRAD,0,25,GetOwner());
    obj->SetRadTime(rad_effect);
    SetPosition(posX, posY, obj);
  }


Das "radioaktiv" Objekt SRAD befindet sich unter General/effects/Radiation. Das Skript sieht folgendermaßen aus:

/– Strahlung –/

/#strict

local strength, Actnum;

/
Initialisierungs-Aufruf /

public func SetRadTime(time)
{
  // Timer einstellen, 1000fache Präzision
  strength = time * 1000;
  var obj;
  while (obj = FindObject(SRAD,-50,-50,100,100))
    MergeWith(obj);
  SetAction("Process");
  ChangeDir(SelectDir());
}

public func GetRadTime()
{
  return(strength);
}

/
Action-Call /

protected func Radiation()
{
  ReduceRadiation(994);
  var obj, damage;
  var str = strength/300;
  while (obj = FindObject(0,0,0,0,0, OCF_Alive(),0,0,NoContainer(),obj))
    if (!InLiquid(obj))
      if (!obj->~Radiation())
        if ((damage = (str-ObjectDistance(obj))/120) > 0)
          DoEnergy(-damage, obj);
  // Aktion wechseln
  var num = SelectDir();
  if (num == Actnum) return(1);
  if (Inside(Actnum-num, 1, 2)) SetPhase((Actnum-num)2);
                           else SetPhase(0);
  ChangeDir(num);
}

public func ReduceRadiation(perc)
{
  // Strahlung abschwächen
  strength = strength/1000
perc;
  // löschen bei weniger als 50000
  if (strength < 50000) RemoveObject();
}

private func ChangeDir(dir)
{
  SetDir(dir);
  Actnum = dir;
}

private func SelectDir()
{
  if (strength < 75000)  return(0);
  if (strength < 110000) return(1);
  if (strength < 160000) return(2);
  if (strength < 240000) return(3);
  if (strength < 350000) return(4);
  if (strength < 520000) return(5);
  if (strength < 800000) return(6);
  return(7);
}

/
Strahlungsquellen zusammenfassen */

private func MergeWith(target)
{
  // Strahlungsstärke der anderen Quelle
  var otherstr = target->GetRadTime();
  if (!otherstr) return(0);
  var both = strength + otherstr;
  // neue Position einstellen
  SetPosition(GetX(target) + (GetX()-GetX(target)) * strength / both,
              GetY(target) + (GetY()-GetY(target)) * strength / both);
  strength = both;
  RemoveObject(target);
  return(1);
}


Ich habe nun ein neues Material erstellt, "Plutonium" (knallgrünes Erz), welches als Abfallprodukt im Atomkraftwerk entstehen soll, welches im SpaceLaunch Teil des SE-Packs zu finden ist.  Um diesen mächtigen nuklearen Sprengsatz dann herstellen zu können, soll Plutonium vonnöten sein, also auch zwingend ein Atomkraftwerk.  Das Plutonium selbst soll dabei aber "strahlen".  Es soll nur ein sehr kleiner Radius sein, also vllt eine Länge von 3 clonks nebeneinander ^^.  Auch soll es natürlich nicht sofort töten, wenn ein Clonk es einsammelt, aber schon merklich die Lebenspunkte reduzieren und dann vllt nach 20 sekunden töten.
Um dennoch mit dem Material arbeiten zu können, könnte man in der Rüstung oder dem Schildgenerator des Hazardpacks etwas einprogrammieren, wodurch die Strahlung deutlich weniger Schaden anrichtet (am besten auch die Strahlung der Nuklearexplosion).

Leider leider übersteigt das mal wieder meine programmierkenntnisse… wisst ihr wie ich das mit den vorhandenen Skripten gut umsetzen kann?

>Leider leider übersteigt das mal wieder meine programmierkenntnisse... wisst ihr wie ich das mit den vorhandenen Skripten gut umsetzen kann?


Ich kann ja mal die ultra-Minimalversion für sowas geben:


func Initialize()
{
  // rufe die Effektfunktion in diesem Objekt alle fünf Frames auf
  AddEffect("Radiate", this, 1, 5, this);
}

func FxRadiateTimer(pTarget, iEffectNumber, iTime)
{
  var maximum_distance = 30;
  // suche nach Zielen und mache denen dann Schaden je nachdem wie weit sie weg sind
  for (var obj in FindObjects(Find_Distance(maximum_distance), Find_OCF(OCF_Alive)))
  {
    var distance = Distance(GetX(), GetY(), obj->GetX(), obj->GetY());
    var distance_factor = 100 - (100 \* distance / maximum_distance); // in Prozent!
    var damage = distance_factor * 50;
    DoEnergy(-damage, obj, true);
  }
}


Grafische Effekte musst du selbst basteln - zB per CastParticles :)

Wenn nur die tragbaren Objekte Schaden verursachen sollen, versuch das von Zapper. :slight_smile:

Wenn zusätzlich das Material selbst (also das Zeug, was man minen kann) Schaden verursachen soll, dann würde ich dem Clonk einen Effect geben, der kuckt, wie viele Pixel von dem Material im Umkreis sind, und entsprechend Schaden verursacht.

super vielen dank :slight_smile: klappt einwandfrei :slight_smile:
ja mal schauen, ob und was ich für grafische Effekte da noch machen kann…  vllt nehm ich auch einfach nur einen "aua" sound der Clonks.

danke für den Tipp =)
vllt komme ich darauf später nochmal zurück. Vorerst soll Plutonium aber nur als Brocken im Reaktor entstehen und nicht abbaubar sein.

ach vllt doch noch für die Zukunft ein paar Fragen, wie deine Funktionen funktionieren:

wie kommst du auf:
AddEffect("Radiate", this, 1, 5, this); ?
sowas habe ich bisher noch in keinem Script gesehen. Ich hätte jetzt sowas erwartet wie
func Initialize()
{ FxRadiateTimer(pTarget, iEffectNumber, iTime) }


ach und wie wirken sich die Dinge in Klammern aus (sowohl das aus addeffect, als auch aus FxRadiateTimer)? Sind das automatisch Variablen, oder was ist das? Du verwendest "itime" sonst nirgends, also nehme ich an, dass das vordefintierte Wörter sind? Gibt es da eine Liste für?
Ich glaube den Rest verstehe ich =)

Deine Funktion wird ja alle 5 Frames aufgerufen. Wenn ich nun möchte, dass alle z.b 30 Frames dann der "aua" sound ertönt, wie muss das dann aussehen?
func Initialize()
{
AddEffect("Aua", this, 1, 30, this);
}

func FxAuaTimer(pTarget, iEffectNumber, iTime)
{
Sound ("Aua*");
}

so richtig?
(muss noch schauen, welches der richtige sound ist)

Die [Effekte-Dokumentation](http://crdocs.clonkspot.org/de/sdk/script/Effects.html) erklärt sehr wortreich, was da vor sich geht. Ganz unten ist eine Übersicht der Funktionen und Parameter.

>wie kommst du auf...


Zusätzlich zu dem Link von Luchs nochmal eine kleine Zusammenfassung:
In Clonk gibt es ein "Effekt"-System. "Effekte" sind Dinge, die du an Objekte ranhängen kannst und für die dann in bestimmten Situationen Funktionen aufgerufen werden. Alle Effektfunktionen heissen immer Fx + Effektname + Callbackname. Callbacks gibt es verschiedene: zB Fx\*Timer, der in bestimmten Zeitabständen aufgerufen wird. Oder Fx\*Damage, der aufgerufen wird wenn das Objekt Schaden nimmt. Oder Fx\*Stop wenn der Effekt gelöscht wird. Da gibt es natuerlich noch viel mehr.

Jeder dieser Callbacks hat bestimmte vordefinierte Parameter - das sind die Dinge in den Klammern. Der Fx\*Timer Callback bekommt zum Beispiel wie viel Zeit schon vergangen ist (iTime) und dafür bekommt der Fx\*Damage Callback, wie viel Schaden verursacht wurde.

Jeder Effektcallback bekommt immer das Ziel vom Effekt pTarget. Das habe ich bei dir nicht gebraucht, weil das Ziel vom Effekt gleichzeitig das Objekt ist, in dem die Funktionen stehen.
Du kannst aber auch Effekte an andere Objekte ranhängen.

zB könntest du in die Schleife oben, direkt nach DoEnergy sowas ranhängen:
AddEffect("Sparkle", obj, 1, 30, 0, GetID());

Und dir dann im Script weiter unten sowas definieren:

func FxSparkleTimer(pTarget, iEffectNumber, iTime)
{
  if (iTime > 36 * 10) return -1;
  pTarget->CastParticles("PxSpark", 30, 50, 0, 0, 10, 50, RGB(0, 100, 0), RGB(100, 255, 100));
}

Dann sprühen getroffene Clonks noch ein paar Sekunden lang Funken.

Bemerkungen:
- iTime zählt in Frames. Eine Sekunde hat etwa 36 Frames.
- Wenn die Fx\*Timer Funktion -1 zurückgibt, wird der Effekt entfernt
- Diesmal haben wir in AddEffect als Ort wo der Effekt definiert ist eine Definition GetID angegeben und kein Objekt. Der Unterschied ist etwas feiner und schwerer zu erklären - aber das ist keine schlechte Idee, wenn man den Effekt an ein anderes Objekt ranklatscht.

PS: Dein Aua-Effekt sieht so richtig aus, ja

vielen dank für die Erklärung und auch die Funken  =)
da hab ich ja wieder was absolut geniales gelernt :)  wenn in dem Tempo immer wieder neue Möglichkeiten dazu kommen, wird mein "Projekt" ja nie fertig, weil immer wieder neues gemacht wird :smiley:

Zu dem "aua" sound.  Wenn ich den AddEffect im Initialize schreibe, dann wird es ja dauerhaft für das Objekt abgespielt, so wie es sich bei dem Schaden verhält, den es macht. Also wäre das wohl nicht ganz so richtig.  Vllt wäre in dem Fall die von dir erwähnte Damage Funktion sinnvoller, sodass der Sound nur bei verussachtem Schaden biem Clonk abgespielt wird :slight_smile:

AddEffect("Aua", obj, 1, 10, 0, GetID());

func FxAuaDamage(pTarget, iEffectNumber, iDmgEngy, iCause)
{
  if (iDamage > 10)
  Sound("Aua");
}


hmm… ich bin mir fast sicher, das das so nicht funktioniert… es wird ja nirgends gesagt, dass es einen Clonk treffen soll… Demnach werden evlt auch andere Lebewesen (tiere) die durch den Schadensradius getroffen werden "aua" sagen?  Wobei vermutlich selbst das mit der jetzigen Funktion nicht funktioniert :smiley:
vermutlich brauche ich hier auch eine Schleife, wie du es bei der Schadensverteilung gemacht hast…  aber warum eigentlich? Deine Funktion wird doch alle 5 Frames aufgerufen, also wieso braucht man da noch eine schelife?

>hmm.. ich bin mir fast sicher, das das so nicht funktioniert... es wird ja nirgends gesagt, dass es einen Clonk treffen soll... Demnach werden evlt auch andere Lebewesen (tiere) die durch den Schadensradius getroffen werden "aua" sagen?  Wobei vermutlich selbst das mit >der jetzigen Funktion nicht funktioniert :D


Moment! Fx\*Damage wird aufgerufen, wenn das Objekt Schaden nimmt, nicht wenn es ihn verteilt. Also müsste das dann in deinem Fall auf dem Clonk drauf sein.
Aber viel einfacher wäre es den Sound direkt da abzuspielen wo der Schaden verteilt wird. Z.B. sowas hier einfach unter die Zeile mit dem DoEnergy klatschen:
if (Random(10) == 0) Sound("Aua*");
Random(x) zieht eine Zufallszahl zwischen 0 und x-1. Also hier würde bei so jedem zehnten Mal der Sound abgespielt werden.

>vermutlich brauche ich hier auch eine Schleife, wie du es bei der Schadensverteilung gemacht hast...  aber warum eigentlich? Deine Funktion wird doch alle 5 Frames aufgerufen, also wieso braucht man da noch eine schelife?


Die Schleife macht den Schaden an jedes Lebewesen im Umkreis. Und dieses Schadensverteilen wird dann eben alle 5 Frames gemacht. Ohne Schleife könntest du immer nur einem Lebewesen auf einmal Schaden machen.

Okay, soweit klappt alles =)  habe die Sparkles etwas reduziert "AddEffect("Sparkle", obj, 1, 1, 0, GetID());" und "if (iTime > 1 * 1) return -1;", damit die Funken sofort aufhören, sobald man außerhalb der Reichweite ist und nicht noch ca. 10 sekunden weiter sprühen.

Dennoch sollte man den Aua Sound wohl auf Clonks beschränken, da es doch etwas merkwürdig ist, wenn Fische und Co "Aua" rufen :smiley:
Also muss ich das in einem appendto für clonks regeln? Und da dann einen Damage Effekt hinzufügen?

Wenn wir aber schon bei "Random" sind, es gibt 3 Sounds, Hurt1 bis Hurt3. Durch den Random Befehl könnte man dann ja z.b jedes 30te mal Sound 1 , sound 2 und sound 3 abspielen, super =)

>sofort aufhören


Wenn die Funken nur einmal kommen sollen, kannst du die Zeile mit den CastParticles natuerlich auch wieder in die Schleife übernehmen, die den Schaden macht, und pTarget mit obj tauschen.
Das mit dem Effekt war jetzt eher so das Beispiel wie du einen Effekt an ein anderes Objekt klatschen kannst :)

>Also muss ich das in einem appendto für clonks regeln? Und da dann einen Damage Effekt hinzufügen?


Oder du prüfst einfach ob dein obj ein Clonk ist. Ich glaube Clonks haben den Callback IsClonk().
Also zB in deine Schleife if (obj->~IsClonk()) Sound("Aua*");

>Wenn wir aber schon bei "Random" sind, es gibt 3 Sounds, Hurt1 bis Hurt3. Durch den Random Befehl könnte man dann ja z.b jedes 30te mal Sound 1 , sound 2 und sound 3 abspielen, super =)


Bei Sounds gibt es sogar noch den Trick mit dem Sternchen.
Wenn du die Sounds Hurt1.wav, Hurt2.wav und Hurt3.wav hast, kannst du einen zufällig davon mit Sound("Hurt*") abspielen.

ah danke, da kommt das sternchen also her ^^ :slight_smile:

habe jetzt " if (!Random(5) & obj->~IsClonk()) Sound("Hurt*");"  genommen, damit der sound nur mit einer wahrscheinlichkeit von 1 zu 5 abgespielt wird und es klappt :slight_smile:
zusätzlich leuchtet das Plutonium durch die Hazardlichteffekte nun grün, kann nicht verkauft oder aus dem Rand geworfen werden und wenn es gesprengt wird hinterlässt es einen kleinen radioaktiven Bereich, die Funktion dafür habe ich aus den Isotopen des StarEmpire Packs/General/Items entnommen :slight_smile:

Damit ist es fertig, vielen vielen dank nochmal und auch für all die vielen anderen Fragen die du und die anderen beantwortet =)

Für das logische "und" solltest du &amp;&amp; verwenden. Das einfache &amp; macht ein bitweises "und" auf zwei Binärzahlen, was möglicherweise in manchen Fällen zu seltsamen Ergebnissen führen kann. Außerdem wird bei #strict 2 die rechte Seite von &amp;&amp; nicht ausgeführt, wenn die linke schon false ergibt. Das spart etwa in deinem Beispiel einen Funktionsaufruf.

danke, hab ich gemacht =)

oh man… dieses Licht aus dem Hazardpack … ist sooooo nervig :smiley: ich frage mich echt, warum man das nicht anders gelöst hat bzw. warum man es nicht anders lösen kann :smiley:

Ich meine es wird sowohl als Gebäude und Fahrzeug gewertet, was ja in meinen Threads hier schon für Probleme gesorgt hat. und nun sehe ich, da ich das Licht bei dem Plutonium hinzugefügt habe, dass es auch als einsammelbares Objekt gilt -.- 
Zumindest beim Hazardclonk, welcher mehrere Dinge tragen kann, macht sich das bemerkbar. Nachdem man Plutonium eingesammelt hat, rückt das Licht an erste stelle in seiner "tasche". Man kann es zwar werfen, aber weil es an das Plutonium gebunden ist, wird es aus der Welt entfernt und kehrt zurück in die Tasche :D   Um das Plutonium dann loszuwerden, muss man die Items durchschalten, sodass es an erster Stelle steht.

Das Hazardpack ist ja eig recht "berühmt" und auch zigfach überarbeitet. Wisst ihr warum das Licht all diese nervigen Eigenschaften hat/haben muss?

edit:
oder liegt das eher an meiner Funktion? was müsste ich da ändern? :

func Initialize()
{
  // rufe die Effektfunktion in diesem Objekt alle fünf Frames auf
  AddEffect("Radiate", this, 1, 10, this);
  TurnLightOn();
}
private func TurnLightOn()
{
  var iColor;
  iColor = RGBa(150,235,10,60);
  mlight = AddLight(1000, iColor, this());
  return(1);
}

private func TurnLightOff()
{
  if (mlight) RemoveObject(mlight);
  return(1);
}


Hm… mir kommt gerade der Gedanke, dass man einfach den "TurnLightOff" befehl verwenden könnte, wenn das Objekt eingesammelt wird.
Wie lautet die Funktion dafür, die beim einsammeln aufgerufen wird? (beim fertigstellung eines Ojekts wird ja z.b die Funktion "Completion" aufgerufen. Es gibt da doch bestimmt eine Funktion fürs einsammeln, oder?)

Schreib mal ins script von dem Objekt das eingesammelt wird^^:

func Entrance(object container)
{
    Message("Hui, ein %s hat mich eingesammelt! :D", container->GetName());
}

dann kommt die Fehlermeldung:
ERROR: call to "Message" parameter 2: got "string", but expected "object"!
by: Entrance(Ringo #323) (obj Plutonium #41)


Edit:
aber wenn ich in die Funktion "TurnLightOff();"  schreibe, dann funktioniert das und das Licht geht aus und nervt nicht im inventar des Clonks =)
Jetzt muss es nur noch wieder angehen, wenn es wieder frei rumliegt… wie mache ich das?

>dann kommt die Fehlermeldung:


Das liegt daran, weil Pyrit zu viel OpenClonk codet. Da macht man nämlich obj->Message("Hi", ...) statt Message("Hi", obj, ...).

>Jetzt muss es nur noch wieder angehen, wenn es wieder frei rumliegt... wie mache ich das?


Der Counterpart zu Entrance ist Ejection

Siehe auch hier für viele andere interessante Callbacks: (Runterscrollen zur Tabelle)
http://www.clonk.de/docs/de/sdk/definition/script.html

Offensichtlich kam einfach nie jemand auf die Idee, einem einsammelbaren Objekt ein Licht zu geben. Hazard ist eben primär ein abgeschlossenes Objektpaket - da macht man sich nicht so viele Sorgen über seltsames Verhalten in Verbindung mit anderen Objekten.

Das Einsammelverhalten liegt im Übrigen an der ATTACH-Prozedur, da kann Hazard nicht so viel dafür. Am Besten, du schaltest einfach das Licht ab, wenn das Plutonium eingesammelt wird.