Ghost creep

I had a game where every tower missed every hit on ghost creeps. 500 waves blood moor with lord Roderic. This did not happen with other hero/wave combinations before. Edit: Replay: https://mazebert.com/g/d1347f29-57c5-4877-b5a4-2b9c38bcd613-35446/ Wave 51, 68, 75

Hitting ghost creeps is likely luck based. Lord Roderic is -50% luck, so you have half the chance to hit the ghost creeps. Depending on how luck works, it might even be worse than that.

Thanks for reporting! Oh yes, it is luck based. But I just noticed that calculating wether a negative ability is triggered is flawed:
public boolean isNegativeAbilityTriggered(float chance) {
  return Sim.context().randomPlugin.getFloatAbs() * luck <= chance;
}
Your example shows the problem very well. Ghost creeps have 50% chance to miss. Roderic gives -50% to luck. The default value for luck is 1, with Roderic luck becomes 1 - 0.5 = 0.5. With the current code, it is not possible to hit at all:
random = 1
luck = 0.5
chance = 0.5

1 * 0.5 <= 0.5 // this is always true, since you will always miss, even with the best possible roll
Thanks for reporting, I'm going to adjust this calculation for the next release.

Update: I fixed it for the next release.
public boolean isNegativeAbilityTriggered(float chance) {
  if (version < Sim.v19) {
    return Sim.context().randomPlugin.getFloatAbs() * luck <= chance;
  } else {
    return !isAbilityTriggered(1.0f - chance);
  }
With Lord Roderic the chance to hit Ghost Creeps is then 25%. That also makes a lot more sense than the previously 0%. Have a look at these examples:
@Test
void negativeAbility_badLuck() {
    randomPluginTrainer.givenFloatAbs(0.0f);
    tower.addLuck(-0.5f);
    assertThat(tower.isNegativeAbilityTriggered(0.5f)).isFalse();
}

@Test
void negativeAbility_badLuck2() {
    randomPluginTrainer.givenFloatAbs(0.24f);
    tower.addLuck(-0.5f);
    assertThat(tower.isNegativeAbilityTriggered(0.5f)).isFalse();
}

@Test
void negativeAbility_badLuck3() {
    randomPluginTrainer.givenFloatAbs(0.25f);
    tower.addLuck(-0.5f);
    assertThat(tower.isNegativeAbilityTriggered(0.5f)).isTrue();
}