|
-
Ein paar Hinweise meinerseits noch:
Als erstes solltest du überprüfen, ob attackerNpc überhaupt ein gültiger NPC (bzw. Pointer) ist (das tritt z.B. auf, wenn jemand Fallschaden bekommt - dann gibt es keinen Angreifer).
Du solltest auch nicht überprüfen, welche Waffe der Angreifer gezogen hat, denn das ist ungenau (z.B.: Pfeil abgeschossen und dann schnell die Waffe gewechselt). Stattdessen verwende den DamageDescriptor, in dem sollte u.a. ein Pointer auf das verwendete Item sein. Das kannst du dann als Angreiferwaffe verwenden.
Die (beispielhafte) Unterscheidung von Schwert/Axt ist ebenfalls unglücklich (u.a. deswegen weil die Dex-Waffen glaube ich DAM_POINT machen). Stattdessen:
Code:
func int CalcMeleeDamage(var C_Item wpn, var C_NPC attacker, var C_NPC victim) {
var int damageType; damageType = wpn.damageType;
var int protectionType; protectionType = DamageTypeToProtIndex(damageType);
var int damage; = MEM_ReadStatArr(wpn.damage, damageType);
var int protection; = MEM_ReadStatArr(victim.protection, protectionType);
return attacker.attribute[ATR_STRENGTH] + damage - protection;
};
func int DamageTypeToProtIndex(var int damageType) {
if damageType == DAM_EDGE {
return PROT_EDGE;
}
else if damageType == DAM_POINT {
return PROT_POINT;
} else if
...
};
Mit der Einschränkung, dass eine Waffe jetzt maximal einen Schadenstyp gleichzeitig machen kann (ich _glaube_ das gilt in G2 für alle Waffen aus dem Hauptspiel).
Und natürlich wird noch ganz viel Fummelarbeit auf dich zukommen, z.B.: Eventuell musst du IMMORTAL-NPCs beachten, der Mindestschaden ist in den Beispielen hier noch nicht einbezogen worden, momentan kann noch negativer Schaden verteilt werden etc etc etc.
Edit: Eventuell nicht wpn.damage[damageType] sondern einfach nur wpn.damageTotal.
Geändert von Lehona (11.10.2018 um 20:12 Uhr)
-
const int DAM_CRITICAL_MULTIPLIER = 2;
Wieso gibt es das eigentlich noch in constants.d von G2 und kann ich das irgendwie nutzen?
edit: Ich möchte mich an dieser Stelle bei euch für die Hilfeversuche bedanken, aber ich komm damit überhaupt nicht zu recht.
Es hat wohl niemand Lust die Sache komplett fertig zu skripten?
Geändert von Moe (21.10.2018 um 20:57 Uhr)
-
Okay, ich bin schon einen Schritt weiter.
Ich hab mal das Skript für den Nahkampfschaden von Teron Gorefiend aus Seite 1 dieses Threads genommen und angepasst.
Code:
func int DMG_OnDmg(var int victimPtr, var int attackerPtr, var int dmg, var int dmgDescriptorPtr) {
var oSDamageDescriptor dmgDesc; dmgDesc = _^(dmgDescriptorPtr);
var c_npc attackerNpc; attackerNpc = _^(attackerptr);
var c_npc victimNpc; victimNpc = _^(victimPtr);
var c_npc slf; slf = _^(attackerptr);
var c_npc oth; oth = _^(victimPtr);
var int meleedamage;
if (Npc_HasReadiedMeleeWeapon(slf)) //////Nahkampf
{
var c_item tmpItm; tmpItm = Npc_GetReadiedWeapon(slf);
var c_item wpn; wpn = Npc_GetReadiedWeapon(slf);
var int wpnDmg; wpnDmg = tmpItm.damageTotal;
var int armRes;
If tmpItm.damagetype == DAM_EDGE { armRes = oth.protection[PROT_EDGE]; }
else if tmpItm.damagetype == DAM_BLUNT { armRes = oth.protection[PROT_BLUNT]; }
else if tmpItm.damagetype == DAM_POINT { armRes = oth.protection[PROT_POINT]; };
if (wpn.flags & ITEM_2HD_AXE || wpn.flags & ITEM_2HD_SWD)
{
meleedamage = (wpnDmg+ slf.attribute[ATR_STRENGTH]);
}
else
{
meleedamage = (wpnDmg+ slf.attribute[ATR_STRENGTH]);
};
meleedamage = (meleedamage -armRes);
if (meleedamage < 0)
{
meleedamage = 0;
};
dmg = meleedamage;
};
return dmg;
};
Das funktioniert super. Jetzt müsste ich das noch irgendwie um kritischen Schaden für Nah- und Fernkampfwaffen ergänzen.
Habt ihr eine Idee wie?
-
Vor der Zeile
Code:
meleedamage = (meleedamage -armRes);
wäre ein guter Zeitpunkt um meleedamage zu verdoppeln, falls ein kritischer Treffer ausgewürfelt wird.
Wie genau du das Gothic 1 System nachbilden willst bleibt dir überlassen. In G1 ist es ja so das man Kampftalente Stufenweise lernt und davon auch die kritische chance abhängt. In g2 lernt man jeden Punkt einzeln und es gibt nicht direkt Stufen.
Eine Idee:
Code:
var int chanceForCrit;
//[hier jetzt chanceForCrit befüllen, zum Beispiel abhängig von der attackerNPC.HitChance[NPC_TALENT_1H] wenn mit einer Einhandwaffe zugeschlagen wurde.]
// Falls Einhandwaffe verwendet && attackerNPC.HitChance[NPC_TALENT_1H] < 30 --> chanceForCrit = 0;
// Falls Einhandwaffe verwendet && attackerNPC.HitChance[NPC_TALENT_1H] < 60 --> chanceForCrit = 5;
// Falls Einhandwaffe verwendet && attackerNPC.HitChance[NPC_TALENT_1H] >= 60 --> chanceForCrit = 10;
if(Hlp_Random(100) < chanceForCrit){
meleedamage = meleedamage * 2;
};
meleedamage = (meleedamage -armRes);
Ich finde deinen code recht seltsam:
- Du definierst dir oth und victimNPC, obwohl beide den gleichen NPC beschreiben.
- Du definierst dir slf und attackerNPC, obwohl beide den gleichen NPC beschreiben.
- Du definierst dir tmpItm und wpn, obwohl beide die gleiche Waffe beschreiben
Auch beachtest du zurzeit nicht das protection[X] auf -1 stehen könnte (signalisiert das der NPC immun gegen diesen Schadenstyp sein soll).
Für Nahkampfwaffen wahrscheinlich irrelevant, spätestens bei Fernkampfwaffen wird das wichtig.
-
Danke,
ich hab die Idee jetzt mal so umgesetzt und das funktioniert soweit, allerdings wird der Doppelte Schaden bei Volltreffern auch berechnet, wenn ich mit einer Einhandwaffe kämpfe, obwohl im Skript aktuell doch nur Zweihandwaffen berücksichtigt werden sollten, oder? Was ist hier falsch?
Code:
func int DMG_OnDmg(var int victimPtr, var int attackerPtr, var int dmg, var int dmgDescriptorPtr) {
var oSDamageDescriptor dmgDesc; dmgDesc = _^(dmgDescriptorPtr);
var c_npc attackerNpc; attackerNpc = _^(attackerptr);
var c_npc victimNpc; victimNpc = _^(victimPtr);
var c_npc slf; slf = _^(attackerptr);
var c_npc oth; oth = _^(victimPtr);
var int meleedamage;
if (Npc_HasReadiedMeleeWeapon(slf)) //////Nahkampf
{
var c_item tmpItm; tmpItm = Npc_GetReadiedWeapon(slf);
var c_item wpn; wpn = Npc_GetReadiedWeapon(slf);
var int wpnDmg; wpnDmg = tmpItm.damageTotal;
var int armRes;
If tmpItm.damagetype == DAM_EDGE { armRes = oth.protection[PROT_EDGE]; }
else if tmpItm.damagetype == DAM_BLUNT { armRes = oth.protection[PROT_BLUNT]; }
else if tmpItm.damagetype == DAM_POINT { armRes = oth.protection[PROT_POINT]; };
if (wpn.flags & ITEM_2HD_AXE || wpn.flags & ITEM_2HD_SWD)
{
meleedamage = (wpnDmg+ slf.attribute[ATR_STRENGTH]);
}
else
{
meleedamage = (wpnDmg+ slf.attribute[ATR_STRENGTH]);
};
var int ChanceForCrit;
if ((wpn.flags & ITEM_2HD_AXE || wpn.flags & ITEM_2HD_SWD) && attackerNPC.HitChance[NPC_TALENT_2H] >= 0)
{
ChanceForCrit = 0;
};
if ((wpn.flags & ITEM_2HD_AXE || wpn.flags & ITEM_2HD_SWD) && attackerNPC.HitChance[NPC_TALENT_2H] >= 10)
{
ChanceForCrit = 10;
};
if ((wpn.flags & ITEM_2HD_AXE || wpn.flags & ITEM_2HD_SWD) && attackerNPC.HitChance[NPC_TALENT_2H] >= 20)
{
ChanceForCrit = 20;
};
if ((wpn.flags & ITEM_2HD_AXE || wpn.flags & ITEM_2HD_SWD) && attackerNPC.HitChance[NPC_TALENT_2H] >= 30)
{
ChanceForCrit = 30;
};
if ((wpn.flags & ITEM_2HD_AXE || wpn.flags & ITEM_2HD_SWD) && attackerNPC.HitChance[NPC_TALENT_2H] >= 40)
{
ChanceForCrit = 40;
};
if ((wpn.flags & ITEM_2HD_AXE || wpn.flags & ITEM_2HD_SWD) && attackerNPC.HitChance[NPC_TALENT_2H] >= 50)
{
ChanceForCrit = 50;
};
if ((wpn.flags & ITEM_2HD_AXE || wpn.flags & ITEM_2HD_SWD) && attackerNPC.HitChance[NPC_TALENT_2H] >= 60)
{
ChanceForCrit = 60;
};
if ((wpn.flags & ITEM_2HD_AXE || wpn.flags & ITEM_2HD_SWD) && attackerNPC.HitChance[NPC_TALENT_2H] >= 70)
{
ChanceForCrit = 70;
};
if ((wpn.flags & ITEM_2HD_AXE || wpn.flags & ITEM_2HD_SWD) && attackerNPC.HitChance[NPC_TALENT_2H] >= 80)
{
ChanceForCrit = 80;
};
if ((wpn.flags & ITEM_2HD_AXE || wpn.flags & ITEM_2HD_SWD) && attackerNPC.HitChance[NPC_TALENT_2H] >= 90)
{
ChanceForCrit = 90;
};
if ((wpn.flags & ITEM_2HD_AXE || wpn.flags & ITEM_2HD_SWD) && attackerNPC.HitChance[NPC_TALENT_2H] >= 100)
{
ChanceForCrit = 100;
};
if(Hlp_Random(100) < chanceForCrit){
meleedamage = meleedamage * 2;
};
meleedamage = (meleedamage -armRes);
if (meleedamage < 0)
{
meleedamage = 0;
};
dmg = meleedamage;
};
return dmg;
};
-
Lokale Variablen in Funktionen werden bei Gothic anders als in anderen Programmiersprachen nicht automatisch "zurückgesetzt". Sprich der Wert von ChanceForCrit ist bei der Deklaration nicht automatisch Null wenn die Funktion merhmals aufgerufen wird.
Deshalb mache das explizit (sollte man in Daedalus übigens so gut wie immer machen):
var int ChanceForCrit; ChanceForCrit = 0;
-
Gibt's eigentlich 'n Grund für diese lange Kaskade von Ifs, anstatt einfach
Code:
if (wpn.flags & ITEM_2HD_AXE || wpn.flags & ITEM_2HD_SWD)
{
ChanceForCrit = attackerNPC.HitChance[NPC_TALENT_2H];
};
zu verwenden?
-
Danke für den Input!
So schaut das Skript jetzt aus:
Code:
func int DMG_OnDmg(var int victimPtr, var int attackerPtr, var int dmg, var int dmgDescriptorPtr) {
var oSDamageDescriptor dmgDesc; dmgDesc = _^(dmgDescriptorPtr);
var c_npc attackerNpc; attackerNpc = _^(attackerptr);
var c_npc victimNpc; victimNpc = _^(victimPtr);
var c_npc slf; slf = _^(attackerptr);
var c_npc oth; oth = _^(victimPtr);
var int damage;
if (Npc_HasReadiedWeapon(slf))
{
var c_item tmpItm; tmpItm = Npc_GetReadiedWeapon(slf);
var c_item wpn; wpn = Npc_GetReadiedWeapon(slf);
var int wpnDmg; wpnDmg = tmpItm.damageTotal;
var int armRes;
If tmpItm.damagetype == DAM_EDGE { armRes = oth.protection[PROT_EDGE]; }
else if tmpItm.damagetype == DAM_BLUNT { armRes = oth.protection[PROT_BLUNT]; }
else if tmpItm.damagetype == DAM_POINT { armRes = oth.protection[PROT_POINT]; };
if (wpn.flags & ITEM_2HD_AXE || wpn.flags & ITEM_2HD_SWD || wpn.flags & ITEM_AXE || wpn.flags & ITEM_SWD)
{
damage = (wpnDmg+ slf.attribute[ATR_STRENGTH]);
}
if (wpn.flags & ITEM_BOW || wpn.flags & ITEM_CROSSBOW)
{
damage = (wpnDmg+ slf.attribute[ATR_DEXTERITY]);
};
var int ChanceForCrit; ChanceForCrit = 0;
if (wpn.flags & ITEM_2HD_AXE || wpn.flags & ITEM_2HD_SWD)
{
ChanceForCrit = attackerNPC.HitChance[NPC_TALENT_2H];
};
if (wpn.flags & ITEM_AXE || wpn.flags & ITEM_SWD)
{
ChanceForCrit = attackerNPC.HitChance[NPC_TALENT_1H];
};
if (wpn.flags & ITEM_BOW)
{
ChanceForCrit = attackerNPC.HitChance[NPC_TALENT_BOW];
};
if (wpn.flags & ITEM_CROSSBOW)
{
ChanceForCrit = attackerNPC.HitChance[NPC_TALENT_CROSSBOW];
};
if(Hlp_Random(100) < chanceForCrit){
damage = damage * 2;
};
damage = (damage -armRes);
if (damage < 0)
{
damage = 0;
};
dmg = damage;
};
return dmg;
};
Funktioniert gut. Habt ihr noch Verbesserungsvorschläge?
-
Meine Punkte sind immer noch aktuell:
https://forum.worldofplayers.de/foru...1#post25957021
Das du manchmal oth und manchmal attackerNPC verwendest ist zwar nicht falsch (im Sinne von: deswegen geht was kaputt) aber macht den code doch eher unleserlich und ist auch einfach unnötig.
-
Zitat von Lehona
Code:
if (wpn.flags & ITEM_2HD_AXE || wpn.flags & ITEM_2HD_SWD)
{
[...]
};
Oder:
Code:
if ((ITEM_2HD_AXE | ITEM_2HD_SWD) & wpn.flags)
{
[...]
};
Dann muss man noch weniger schreiben und die Operatorrangfolge ist sichergestellt
"Unter diesen schwierigen Umständen bin ich mir sicher, daß diese guten Menschen meinen augenblicklichen Bedarf an deren Gold verstehen werden." -- Connor
-
Dankeschön, ich hab das Skript jetzt nochmal angepasst und auch den Faustkampfschaden berücksichtigt.
Passt jetzt so, oder?
Code:
func int DMG_OnDmg(var int victimPtr, var int attackerPtr, var int dmg, var int dmgDescriptorPtr) {
var oSDamageDescriptor dmgDesc; dmgDesc = _^(dmgDescriptorPtr);
var c_npc slf; slf = _^(attackerptr);
var c_npc oth; oth = _^(victimPtr);
var int damage;
if(Npc_IsInFightMode(slf,FMODE_FIST))
{
dmg = (slf.attribute[ATR_STRENGTH] -oth.protection[PROT_BLUNT]);
};
if (Npc_HasReadiedWeapon(slf))
{
var c_item wpn; wpn = Npc_GetReadiedWeapon(slf);
var int wpnDmg; wpnDmg = wpn.damageTotal;
var int armRes;
If wpn.damagetype == DAM_EDGE { armRes = oth.protection[PROT_EDGE]; }
else if wpn.damagetype == DAM_BLUNT { armRes = oth.protection[PROT_BLUNT]; }
else if wpn.damagetype == DAM_POINT { armRes = oth.protection[PROT_POINT]; }
else if wpn.damagetype == DAM_FIRE { armRes = oth.protection[PROT_FIRE]; }
else if wpn.damagetype == DAM_MAGIC { armRes = oth.protection[PROT_MAGIC]; };
if ((ITEM_2HD_AXE || ITEM_2HD_SWD || ITEM_AXE ||ITEM_SWD) & wpn.flags)
{
damage = (wpnDmg+ slf.attribute[ATR_STRENGTH]);
}
if ((ITEM_BOW || ITEM_CROSSBOW) & wpn.flags)
{
damage = (wpnDmg+ slf.attribute[ATR_DEXTERITY]);
};
var int ChanceForCrit; ChanceForCrit = 0;
if ((ITEM_2HD_AXE || ITEM_2HD_SWD) & wpn.flags)
{
ChanceForCrit = slf.HitChance[NPC_TALENT_2H];
};
if ((ITEM_AXE || ITEM_SWD) & wpn.flags)
{
ChanceForCrit = slf.HitChance[NPC_TALENT_1H];
};
if (ITEM_BOW & wpn.flags)
{
ChanceForCrit = slf.HitChance[NPC_TALENT_BOW];
};
if (ITEM_CROSSBOW & wpn.flags)
{
ChanceForCrit = slf.HitChance[NPC_TALENT_CROSSBOW];
};
if(r_MinMax(1,100) <= chanceForCrit){
damage = damage * 2;
};
damage = (damage -armRes);
if (damage < 0)
{
damage = 0;
};
dmg = damage;
};
return dmg;
};
Ich hab auch LeGo Random statt hlp_random eingebaut. Wie genau ist das eigentlich zufälliger/besser?
-
Zitat von Moe
Ich hab auch LeGo Random statt hlp_random eingebaut. Wie genau ist das eigentlich zufälliger/besser?
Irgendjemandem ist mal aufgefallen, dass Hlp_Random() zu den niedrigeren Werten tendiert (d.h. die sind nicht gleichverteilt). Mit LeGo tritt das nicht/weniger auf, außerdem hat man ein bisschen mehr Kontrolle (z.B. kann man den Seed setzen). So großartig wichtig sind die Unterschiede nicht, denn das Random von LeGo ist natürlich nicht kryptographisch sicher (und wissenschaftliche Anwendungen schreibt hoffentlich auch keiner in Daedalus!).
-
Eines möchte ich jetzt noch einbauen und zwar Mindestschaden durch Monster, so wie in G1.
In G1 macht ein Monster beim Angriff immer mindestens einen Punkt Schaden, egal wie hoch der Rüstungsschutz ist.
Ich habe den Mindestschaden auf folgenden Wert gesetzt mit NPC_MINIMAL_DAMAGE = 0;
Was muss ich jetzt machen damit Monster, aber nicht der Spieler, Mindestschaden verursachen?
Zitat von Lehona
Irgendjemandem ist mal aufgefallen, dass Hlp_Random() zu den niedrigeren Werten tendiert (d.h. die sind nicht gleichverteilt). Mit LeGo tritt das nicht/weniger auf, außerdem hat man ein bisschen mehr Kontrolle (z.B. kann man den Seed setzen). So großartig wichtig sind die Unterschiede nicht, denn das Random von LeGo ist natürlich nicht kryptographisch sicher (und wissenschaftliche Anwendungen schreibt hoffentlich auch keiner in Daedalus!).
Danke für die Info!
-
Zitat von Moe
Was muss ich jetzt machen damit Monster, aber nicht der Spieler, Mindestschaden verursachen?
Fies wie ich bin, gebe ich dir mal nur ein Stichwort: Gilde
Zitat von Lehona
(und wissenschaftliche Anwendungen schreibt hoffentlich auch keiner in Daedalus!).
-
Hmm, ich formuliere die Frage anders.
Ich setzte wieder NPC_MINIMAL_DAMAGE = 5;
Dann machen wieder alle NPCs Mindestschaden.
Was muss ich nun tun damit nur der PC_Hero keinen Mindestschaden macht? So wie in G1.
Geändert von Moe (26.10.2018 um 23:31 Uhr)
-
Du kannst zum Beispiel in der damageCalculation prüfen, ob der Schaden vom Helden gemacht wurde und das Opfer einen gültigen Wert hat. Falls ja: Prüfen, ob der Schaden == NPC_MINIMAL_DAMAGE ist. Falls ja: Schaden auf 0 setzen.
Das hätte natürlich zur Folge, dass du entweder 6 Schaden machst, oder gar keinen. Also könntest du diesen Fall noch optimieren, indem du die Schadensberechnung dort manuell nachholst und prüfst, ob der Held nicht doch zwischen 1-5 Schaden anrichten würde. Hilft dir das?
-
Ist es möglich das man beim Blocken Schaden abbekommt?
Ich möchte, dass der geblockte Schaden abhängig vom Waffenskill ist.
-
Wenn ich mich richtig erinnere wird dieses Script hier nicht ausgelöst, falls das Ziel blockt. Da müsste man anders eingreifen (und ich weiß aus dem Stegreif nicht, wie/wo).
-
Zitat von Lehona
Wenn ich mich richtig erinnere wird dieses Script hier nicht ausgelöst, falls das Ziel blockt. Da müsste man anders eingreifen (und ich weiß aus dem Stegreif nicht, wie/wo).
Dieser Thread scheint ganz interessant:
https://forum.worldofplayers.de/foru...en+bei+Blocken
Zitat von Sumpfkrautjunkie
Nicht ganz: AssesDamage registriert nur die "Waffenkontakte":
Als ich an einem eigentständigen Schadensystem mit Berücksichtigung des Blockens gebastelt hab, hab ich das Engineinterne-Berechnungsystem "ausgeschaltet". Und mein Schadnesnsystem über AssesDamage umgesetzt. Leider hatte ich eine Fehlerhafte Blockabfrage eingebaut. Die Folge war: Egal ob man Blockt oder nicht, man bekommt immer den Schaden ab.
Genauso ist es bei der Klaue Beliars, deren Schadensscript auch über Asses.d aufgerufen wird. Beim Treffen ist es egal ob der Gegner blockt oder nicht, der Extra-Schaden entsteht trotzdem.
edit: Der Thread ist auch interessant:
https://forum.worldofplayers.de/foru...ight=BS_Parade
Geändert von Moe (10.03.2019 um 19:53 Uhr)
-
Ich hab mal was getestet und ein Video dazu gemacht:
Code:
func void B_AssessDamage()
{
if(Npc_IsPlayer(self))
{
if (C_BodyStateContains(self,BS_PARADE))
{
B_MagicHurtNpc (other, self, 33);
PrintScreen ("hello", 70, 30, FONT_ScreenSmall, 2);
};
return;
};
if(self.aivar[AIV_ArenaFight] == AF_AFTER)
{
self.aivar[AIV_ArenaFight] = AF_AFTER_PLUS_DAMAGE;
};
if(Npc_IsInState(self,ZS_Attack))
{
if(Npc_IsPlayer(other) && (self.npcType == NPCTYPE_FRIEND))
{
return;
};
if(Npc_IsPlayer(other) && (self.aivar[AIV_PARTYMEMBER] == TRUE))
{
return;
};
if(Hlp_GetInstanceID(other) != self.aivar[AIV_LASTTARGET])
{
if(self.aivar[AIV_HitByOtherNpc] == Hlp_GetInstanceID(other))
{
Npc_SetTarget(self,other);
}
else
{
self.aivar[AIV_HitByOtherNpc] = Hlp_GetInstanceID(other);
};
};
return;
};
if(B_AssessEnemy())
{
return;
};
if(Npc_IsInFightMode(other,FMODE_MELEE) || Npc_IsInFightMode(other,FMODE_FIST) || Npc_IsInFightMode(other,FMODE_NONE))
{
if((Npc_GetAttitude(self,other) == ATT_FRIENDLY) || ((self.npcType == NPCTYPE_FRIEND) && Npc_IsPlayer(other)))
{
if(!Npc_IsInState(self,ZS_ReactToDamage))
{
Npc_ClearAIQueue(self);
B_ClearPerceptions(self);
AI_StartState(self,ZS_ReactToDamage,0,"");
return;
};
};
};
B_Attack(self,other,AR_ReactToDamage,0);
};
https://youtu.be/9SxJkye9wYg
Ich kann nur sagen WTF?!
Es scheint völlig random zu sein, wann der Blockschaden verursacht wird.
Berechtigungen
- Neue Themen erstellen: Nein
- Themen beantworten: Nein
- Anhänge hochladen: Nein
- Beiträge bearbeiten: Nein
|
|