Eintragsdetails ansehen

IDProjektKategorieSichtbarkeitZuletzt aktualisiert
0003051EresseaMagieöffentlich2024-10-15 20:13
ReporterSolthar Bearbeitung durchEnno  
PrioritätnormalSchweregradkleinerer FehlerReproduzierbarnicht getestet
Status erledigtLösungerledigt 
Produktversion29.3.1 
Behoben in Version29.4 
Zusammenfassung0003051: Gesang der Versklavung wird doppelt auf Magieresistenz gecheckt
Beschreibung

ZAUBERE "Gesang der Versklavung" einh

Magieresistenz ist komplex und läuft ungefähr so:

  • In verify_targets werden alle Parameter abgeklappert. Parameter sind in der xml-Datei im Attribut parameters definiert, hier zum Beispiel "u" für "eine Einheitnnummer". verify_targets ermittelt nun die Zieleinheit (einh) und checkt ihre Magieresistenz mit target_resists_magic, jedoch nur wenn das Flag NORESISTANCE nicht gesetzt ist (was hier nicht der Fall ist, aber weiter unten relevant wird). Wenn die Einheit widersteht, ist das noch nicht unbedingt das Ende, denn es gibt Zauber, die mehrere Einheiten betreffen und wenn eine davon nicht widersteht, geht es trotzdem weiter. Es wird hier das Flag TARGET_RESISTS gesetzt, das später noch gebraucht wird. In unserem Fall geht es aber nicht weiter, denn es gibt nur eine Zieleinheit. Das ist die erste Chance dem Zauber zu widerstehen.

  • Wenn nicht alle Ziele resistent waren, wird die eigentliche Zauberfunktion aufgerufen, hier sp_charmingsong. Dort wird als eines der ersten Dinge für das Argument (einh) das TARGET_RESISTS-Flag geprüft. Das ist für andere Zauber sinnvoll, hier aber wie gesagt nicht, da dann schon nach der ersten Prüfung Schluss gewesen wäre.

  • sp_charmingsong will seine eigenen Regeln für Magieresistenz definieren, weil hier unter anderem die Personenzahl eingehen soll. Also wird hier nochmal explizit target_resists_magic. Das ist die zweite Chance, dem Zauber zu widerstehen und sie ist so groß wie die erste plus ein besonderer "resist_bonus".

Eine Lösung wäre hier also, den Parameter noresist in der xml-Datei zu setzen. Dann fällt der erste Check weg und das scheint mir das zu sein, was hier gewünscht war.

Der gleich Bug besteht bei sp_enterastral (Astraler Weg), sp_babbler, sp_readmind.

Bei sp_resist_magic fehlt der Check auf TARGET_RESIST, obwohl es mehrere Ziele gibt. Ein Bug. Entweder muss hier noresist rein oder es muss auf TARGET_RESIST geprüft werden, sonst kann eine resistente Einheit durchschlüpfen, wenn die anderen nicht resistent sind.

Bei ein paar Zaubern wird auf TARGET_RESIST geprüft, obwohl sie als noresist gekennzeichnet sind. Kein Fehler aber merkwürdig: sp_transferaura und sp_pump.

Bei sp_movecastle und einigen anderen Zaubern wird TARGET_RESIST nicht geprüft. Vermutlich okay, weil der nur einen einzigen Gebäudeparameter (plus Himmelsrichtung) hat.

sp_treewalk_exit ist komisch: Hier wird TARGET_RESIST geprüft, aber auch, ob die Einheit kontaktiert. Eine kontaktierende Einheit hat aber sowieso keine Magieresistenz. Bei sp_treewalk_enter ist das richtig gemacht.

Bei magic_runes und analysemagic, analyseobject, destroy_magic, break_curse bin ich nicht ganz sicher, was passiert (Parameter "kc"). Ich glaube, hier greift überhaupt keine Resistenz. Kann aber von mir aus so bleiben.

Allerdings gibt es bei analysedream, analysesong, Resistenz. Das sollte einheitlich sein, meine ich?

Ich mache mal Schluss, das ist ein ziemlicher Rant geworden.

TagsKeine Tags zugeordnet.
Parteiii
SpielE2
Report1372

Notizen / Dateien

Enno

Enno

2024-10-04 19:59

Administrator   ~0010248

Soweit ich mich erinnere:

Für Zauber mit expliziten Zielen (Einheiten) im Befehl ist verify_targets immer die richtige Lösung, und es sollte kein weiterer Test nötig sein. Wenn ein Zauber alle Einheiten in einer angegebenen Region betrifft, muss er die Einheiten selber testen (verify_targets testet dann nur gegen die Resistenz der Region an sich).

Ich gucke mir die genannten Zauber mal an, und schaue, ob es evtl. noch mehr gibt.

Enno

Enno

2024-10-04 20:01

Administrator   ~0010249

Du hast hier als Beispiel die Einheit einh der Partei ii in Runde 1372 genannt. Das ist, soweit mir bekannt, nicht deine PArtei, und die Einheitennummer erscheint mir auch unwahrscheinlich. Gehe ich Recht in der Annahme, dass das kein Fall ist, den ich reproduzieren kann, sondern aus purer Lektüre des Codes gefolgert wurde?

Solthar

Solthar

2024-10-04 20:43

Entwickler   ~0010250

Lua-Testfall, Debugging und Lektüre, ja.

Solthar

Solthar

2024-10-04 21:08

Entwickler   ~0010254

Hier ist es diskutierbar, weil sp_charmingsong wie gesagt eigene Regeln festlegt. Ob diese Ausnahme gerechtfertigt ist, ist die Frage.

Bei sp_enterastral (Astraler Weg), sp_babbler, sp_readmind trifft es zu, da tut es der allgemeine Aufruf (es sei denn, man wollte hier eine höhere Chance auf Widerstand.

Es gibt andere Fälle, wo es sinnvoll ist, dass target_resists_magic oder magic_resistance oder is_magic_resistant direkt aufgerufen werden. Zum Beispiel sp_earthquake oder sp_petrify und andere Kampfzauber, weil die keine Parameter haben.

Enno

Enno

2024-10-08 20:48

Administrator   ~0010256

Ich finde den Unterschied zwischen sp_enterastral und sp_treewalkenter spannend. enterastral macht bei jedem Ziel einen Test auf is_magic_resistant, treewalk tut das nicht. Du hast sicher Recht, dass das im Zauber selbst nicht gemacht werden muss, es sei denn, man will besondere Regeln für einen Bonus oder Malus implementieren. Dann sollte man allerdings nicht doppelt testen. Das ist soweit ich sehe, nur bei sp_charmingsong so. Ob das wirklich nötig ist, oder man einfach die Anzahl Personen direkt an die Spruchstärke (force) koppeln sollte, weiß ich nicht. Einfacher wäre es. Klingt mir nach einer nicht sehr gut überlegten Sonderregel, die von jemandem ersonnen wurde, ohne das Framework zu verstehen.

Enno

Enno

2024-10-08 21:01

Administrator   ~0010257

Noch eine Bemerkung:

Es gibt Zauber, die im Falle dass ihr Ziel wegen Magieresistenz verfehlt wird, trotzdem Kosten verursachen (sp_sparkle, sp_destroy_magic, uvm) und solche, die das nicht tun (sp_readmind, sp_babbler, sp_eternizewall usw). Die machen meist sowas:

    /* wenn Ziel gefunden, dieses aber Magieresistent war, Zauber
     * abbrechen aber kosten lassen */
    if (params[0].flag == TARGET_RESISTS)
        return cast_level;
    /* wenn kein Ziel gefunden, Zauber abbrechen */
    if (params[0].flag)
        return 0;

Ich glaube, das sollten alle Zauber tun. Unterschied scheint zu sein, dass ich für die Zauber mit dem TARGET_RESISTS Check vor einem Jahr (Version 28.3: "Zauberkosten sind abhängig von der Zauberstufe") Tests geschrieben habe. Ich hatte das wohl schon einmal in Bearbeitung, bin aber nicht dazu gekommen, das für alle Zauber zu machen, weil es so viel Aufwand ist, Tests für jeden einzelnen zu schreiben, und ich das bei der Gelegenheit gleich auch tun wollte.

Enno

Enno

2024-10-08 21:10

Administrator   ~0010258

Zuletzt bearbeitet: 2024-10-08 21:11

Dann gibt es noch Zauber, die bei einem Ziel mit Resistenz nicht fehlschlagen, sondern nur reduzierte Wirkung haben. sp_babbler ist so einer. Oder sie geben dem Ziel eine Warnung, wie sp_readmind das tut.

Die sollten stattdessen einfach auf TARGET_RESISTS checken, glaube ich? Statt direkt mit if (param->flag) return 0; abzubrechen.

Enno

Enno

2024-10-09 20:30

Administrator   ~0010259

Zuletzt bearbeitet: 2024-10-09 20:32

sp_treewalk_exit ist komisch: Hier wird TARGET_RESIST geprüft, aber auch, ob die Einheit kontaktiert.

Verstehe ich nicht, was da falsch sein soll. Da wird erst geprüft, ob die Magieresistenz gewirkt hat. Wenn nicht, dann wird geguckt, ob die Einheit kontaktiert. Wer nicht kontaktiert, den kann der Zauber nicht teleportieren. Um zu teleportieren (im letzten else), muss die Einheit:

  1. Nicht magieresistent sein,
  2. kontaktiert haben, und
  3. nicht zu schwer sein. Wenn eine Einheit wegen Magieresistenz ausscheidet, wird der Zauber trotzdem bezahlt, in den anderen beiden Fällen wird für sie eine entsprechende Fehlermeldung erzeugt. sp_treewalkenter macht das ähnlich, aber da wird nicht gezahlt, wenn eine Einheit widersteht der nicht gefunden wurde.

Wenn man das einheitlich machen wollte, sollte man in treewalkenter bei TARGET_RESISTS die Kosten setzen, und getrennt auf TARGET_NOTFOUND prüfen.

Update: Ich sehe, dass beide Zauber einen Test haben, und das mit dem Preis bei Resistenz da auch uneinheitlich geregelt ist. Ich repariere.

Enno

Enno

2024-10-09 20:36

Administrator   ~0010260

Was ich nicht so recht verstehe, ist warum TARGET_NOTFOUND und TARGET_RESISTS als bitfield gelöst sind, und nicht als enum. Hmm.

Enno

Enno

2024-10-10 08:24

Administrator   ~0010261

Ich habe mit Änderungen begonnen, siehe https://github.com/eressea/server/pull/1089

Solthar

Solthar

2024-10-10 11:11

Entwickler   ~0010262

Wer kontaktiert ist automatisch nicht magieresistent. Es ist also unsinnig, beides vorauszusetzen.

https://github.com/eressea/server/blob/996ab208d96faa6fa9bb16333d8084defd9287b5/src/magic.c#L1094

Enno

Enno

2024-10-10 12:33

Administrator   ~0010263

Aber nicht umgekehrt. Wenn ich ein Ziel angebe, das mich nicht kontaktiert hat, und das nicht genug Magieresistenz hat, soll das nicht teleportiert werden. Sonst könnte man damit Feinde gegen ihren Willen in den Astralraum schubsen.

Solthar

Solthar

2024-10-10 17:22

Entwickler   ~0010264

Ja das stimmt. Aber was ich sage ist, dass der Test auf Magieresistenz dann überflüssig ist, wenn ich sowieso kontaktiere voraussetze. Nicht unbedingt falsch, aber überflüssig. Und bei treewalk_enter fehlt der Test auf Magieresistenz (deshalb?) auch richtigerweise.

Enno

Enno

2024-10-10 19:43

Administrator   ~0010265

Der Test auf Magieresistenz ist nicht überflüssig, weil er darüber entscheidet, ob der Zauber etwas kosten soll.

Wer resistent ist, wird nicht verzaubert, kostet aber den Zauberer Aura. Wer nicht resistent ist, hat entweder kontaktiert oder nicht. Aus "wer kontaktiert ist nicht resistent" folgt nicht die Umkehrung "wer nicht resistent ist, hat kontaktiert". Im Falle eines Kontaktes wird die Einheit transportiert, andernfalls gibt es eine Fehlermeldung, und der Zauber kostet nichts.

Solthar

Solthar

2024-10-10 20:03

Entwickler   ~0010266

Ja, aber wer resistent ist, hat nicht kontaktiert, also offensichtlich vergessen, zu kontaktieren. Welchen spielerischen Sinn ergibt es, die zaubernde Einheit zusätzlich dafür zu bestrafen? Es ist schlimm genug, dass der Zauber nicht funktioniert.

Dies unterscheidet sich von der Situation, in der ich einen schädlichen Zauber wirke, der vielleicht funktioniert oder nicht, aber in beiden Fällen Aura kosten soll.

Enno

Enno

2024-10-10 20:06

Administrator   ~0010267

Zuletzt bearbeitet: 2024-10-10 20:13

Aber mal zu dem eigentlichen Problem: sp_charmingsong möchte die Resistenz gerne selber prüfen, mit eigenen Modifikatoren. Das ist dann doppelt gemoppelt, weil es schon in verify_targets gemacht wird. Am liebsten möchte man verify_targets für den Zauber selektiv abschalten können. Und wenn man mal guckt, gibt es da einen Check if (((sp->sptyp & NORESISTANCE) == 0) der genau das tut. Ich sehe nur noch nicht, wie das gesetzt wird.

Ah, gefunden. Man muss dafür in spells.xml noresist="true" an den Zauber schreiben. Das ist aktuell nur bei Aushorchen und Auratransfer und zwei Astralraumzaubern gesetzt.

Enno

Enno

2024-10-10 20:09

Administrator   ~0010268

Zuletzt bearbeitet: 2024-10-10 20:14

Oh, ich sehe. Du willst argumentieren, dass der Zauber im Fall von Resistenz nichts kosten soll. Das Argument halte ich auch für vertretbar. Weil man ihn ja nur auf befreundete, also kontaktierende Einheiten spricht, sollte Resistenz nicht vorkommen, es sei denn man macht einen Fehler beim kontaktieren. Stimmt, glaube ich.

Enno

Enno

2024-10-13 16:09

Administrator   ~0010270

Alternative Lösung für sp_charamingsong:

Wenn eine Einheit einen Bonus P_bonus auf die Resistenz kriegt (zu groß oder teure Talente), dann machen wir einen neuen Würfelwurf. Und zwar so, dass die Resistenzchance P_unmodifiziert * P_neu = P_modifiziert ist. P_unmodifiziert ist der Test, den wir schon gemacht haben, P_modifiziert ist P_unmodifiziert + P_bonus, dann ist P_neu = (P_unmodifiziert + P_bonus) / P_unmodifiziert.

Enno

Enno

2024-10-13 18:34

Administrator   ~0010271

Schwere Geburt, aber ich glaube, damit stimmt die Magieresistenz vom Gesang der Versklavung jetzt: https://github.com/eressea/server/blob/fffa33497361d7222c7bf65da20de2302e76d000/src/spells.c#L3502

Enno

Enno

2024-10-13 18:36

Administrator   ~0010272

Zuletzt bearbeitet: 2024-10-13 18:36

Nächster Zauber: sp_pump (Aushorchen) testet das Ziel auch doppelt aus Resistenz. Aber ohne Bonus, das kann man also sicher einfach löschen.

Enno

Enno

2024-10-13 19:06

Administrator   ~0010273

Check. Nächster Fall ist dann sp_enterastral (Astraler Weg). Da kann man sicher auch einfach das is_magic_resistant löschen, aber der Test für den Zauber ist komplizierter zu schreiben (und ich habe mir vorgenommen, das alle Zauber an denen ich ändere bei der Gelegenheit auch Tests kriegen). Das wird heute nicht mehr fertig.

Enno

Enno

2024-10-15 20:12

Administrator   ~0010279

Ich glaube, jetzt habe ich sie alle erwischt. Es wird nur noch an zwei Stellen in spells.c target_resists_magic aufgerufen (in sp_earthquake auf alle Gebäude der Region, und in dc_age für alle Regionen, die unter einem Fluch stehen)

https://github.com/eressea/server/pull/1089

Eintrags-Historie

Änderungsdatum Benutzername Feld Änderung
2024-10-04 13:54 Solthar Neuer Eintrag
2024-10-04 19:56 Enno Bearbeitung durch => Enno
2024-10-04 19:56 Enno Status neu => zugewiesen
2024-10-04 19:59 Enno Notiz hinzugefügt: 0010248
2024-10-04 20:01 Enno Notiz hinzugefügt: 0010249
2024-10-04 20:43 Solthar Notiz hinzugefügt: 0010250
2024-10-04 21:08 Solthar Notiz hinzugefügt: 0010254
2024-10-08 20:48 Enno Notiz hinzugefügt: 0010256
2024-10-08 21:01 Enno Notiz hinzugefügt: 0010257
2024-10-08 21:10 Enno Notiz hinzugefügt: 0010258
2024-10-08 21:11 Enno Notiz bearbeitet: 0010258
2024-10-09 20:30 Enno Notiz hinzugefügt: 0010259
2024-10-09 20:32 Enno Notiz bearbeitet: 0010259
2024-10-09 20:36 Enno Notiz hinzugefügt: 0010260
2024-10-10 08:24 Enno Notiz hinzugefügt: 0010261
2024-10-10 11:11 Solthar Notiz hinzugefügt: 0010262
2024-10-10 12:33 Enno Notiz hinzugefügt: 0010263
2024-10-10 17:22 Solthar Notiz hinzugefügt: 0010264
2024-10-10 19:43 Enno Notiz hinzugefügt: 0010265
2024-10-10 20:03 Solthar Notiz hinzugefügt: 0010266
2024-10-10 20:06 Enno Notiz hinzugefügt: 0010267
2024-10-10 20:09 Enno Notiz hinzugefügt: 0010268
2024-10-10 20:12 Enno Notiz bearbeitet: 0010267
2024-10-10 20:13 Enno Notiz bearbeitet: 0010267
2024-10-10 20:14 Enno Notiz bearbeitet: 0010268
2024-10-13 16:09 Enno Notiz hinzugefügt: 0010270
2024-10-13 18:34 Enno Notiz hinzugefügt: 0010271
2024-10-13 18:36 Enno Notiz hinzugefügt: 0010272
2024-10-13 18:36 Enno Notiz bearbeitet: 0010272
2024-10-13 19:06 Enno Notiz hinzugefügt: 0010273
2024-10-15 20:12 Enno Notiz hinzugefügt: 0010279
2024-10-15 20:13 Enno Status zugewiesen => erledigt
2024-10-15 20:13 Enno Lösung offen => erledigt
2024-10-15 20:13 Enno Behoben in Version => 29.4