Skip to content

The guaranteed hit and the guaranteed dodge - #2305

Open
denislauri1999 wants to merge 1 commit into
NosCoreIO:masterfrom
denislauri1999:pr/guaranteed-hit-dodge
Open

The guaranteed hit and the guaranteed dodge#2305
denislauri1999 wants to merge 1 commit into
NosCoreIO:masterfrom
denislauri1999:pr/guaranteed-hit-dodge

Conversation

@denislauri1999

Copy link
Copy Markdown
Contributor

BCard type 16, the two subtypes that override the ordinary dodge roll:

11: There is a %s%% chance that every attack hits.
21: Always dodge the target with a probability of %s%%.

232 of the 253 declarations on skills are subtype 11, and not one of them carries a negative value, so the X2 slots never appear there — and the file gives X1 and X2 the same sentence anyway, so both add rather than cancelling. Subtype 21 is on no skill at all; its 44 declarations live on cards.

Two fields on CombatStats, folded from the active effects the same way as everything else, and read in the dodge phase of DamageCalculator.

One decision is ours, not the file's

When both are present and both roll, one has to win, and nothing in BCard.dat says which. The attacker's guarantee goes first, because its sentence is the unconditional one. Rather than leave that to the order of two ifs and a comment, it has a test.

Left out, with the reason

  • 31, "no penalty for ranged attacks at close range": the penalty it would lift never fires. Chebyshev() in DamageCalculator returns int.MaxValue because CombatStats carries no position, so the close-range branch is unreachable. Removing a penalty that does not apply is not worth the code, and the dead penalty is its own question.
  • 41 (damage rising with distance) and 51 (fairy damage per debuff) need distance and fairy state that are not in CombatStats either.

Checked by breaking it

Disabling the guaranteed hit fails two tests; disabling the guaranteed dodge fails one. One of the tests needed a high roll rather than the class default, because the ordinary dodge chance has a floor of 1% — with a roll of 0 even a defender with no dodge at all still dodges, and there would have been no "would otherwise be hit" to compare against.

BCard type 16, the two subtypes that override the ordinary dodge roll:

    11: There is a %s%% chance that every attack hits.
    21: Always dodge the target with a probability of %s%%.

232 of the 253 declarations on skills are subtype 11, and not one carries a
negative value, so the X2 slots never appear there - and the file gives X1 and
X2 the same sentence anyway, so both add rather than cancelling. Subtype 21 is
on no skill at all; its 44 declarations are on cards.

Two fields on CombatStats, folded from the active effects like everything else,
and read in the dodge phase.

THE ORDER IS OURS. When both are present and both roll, one has to win, and
nothing in BCard.dat says which. The attacker's guarantee goes first, because
its sentence is the unconditional one. That decision has a test rather than only
a comment.

Left out, with the reason: subtype 31 is "no penalty for ranged attacks at close
range", and the penalty it would lift never fires - Chebyshev() in this file
returns int.MaxValue because CombatStats carries no position. Removing a penalty
that does not apply is not worth code, and the dead penalty is a separate
question. 41 and 51 need distance and fairy state that are not here either.

Disabling either half fails the tests: two for the hit, one for the dodge.
@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 29 minutes.

View limit details

Limit details: You’ve used all 2 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 738d6b90-f3bf-400d-aab7-5257e6f84391

📥 Commits

Reviewing files that changed from the base of the PR and between 55dc38c and 70b6681.

📒 Files selected for processing (4)
  • src/NosCore.GameObject/Services/BattleService/BattleStatsProvider.cs
  • src/NosCore.GameObject/Services/BattleService/DamageCalculator.cs
  • src/NosCore.GameObject/Services/BattleService/Model/CombatStats.cs
  • test/NosCore.GameObject.Tests/Services/BattleService/GuaranteedHitAndDodgeTests.cs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

denislauri1999 added a commit to denislauri1999/NosCore that referenced this pull request Aug 24, 2026
NosCoreIO#2305 inserisce il suo caso esattamente allo stesso punto di ancoraggio, e due
inserimenti sulla stessa riga git non li sa fondere: chi li fonde entrambi si
trova un conflitto su BattleStatsProvider senza che ci sia niente da decidere.

Stesso codice, ancoraggio diverso. Nel file il caso resta accanto a Element, che
e' dove ha senso leggerlo: uno e' il tasso elementale dell'attaccante, l'altro la
resistenza di chi para.
denislauri1999 added a commit to denislauri1999/NosCore that referenced this pull request Aug 24, 2026
Restavano la riga delle variabili locali e il blocco 'stats with': NosCoreIO#2305
aggiunge la sua riga esattamente dopo le stesse due, e git non fonde due
inserimenti sullo stesso punto.

Stesso codice, sopra invece che sotto. Le quattro resistenze finiscono anche in
un posto piu' sensato del blocco, accanto alle altre difese.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant