fix(models): require a positive range_increment_ft (reject 0 and negatives)
This commit is contained in:
@@ -285,7 +285,8 @@ passe par les conditions (`miss_chance`), pas par la carte.
|
|||||||
```
|
```
|
||||||
|
|
||||||
Champ `range_increment_ft` (optionnel, uniquement pour `kind: ranged` ou
|
Champ `range_increment_ft` (optionnel, uniquement pour `kind: ranged` ou
|
||||||
`kind: touch`) : incrément de portée en pieds. La pénalité de portée vaut −2
|
`kind: touch`) : incrément de portée en pieds, entier **strictement positif**
|
||||||
|
(zéro ou négatif = erreur de données, rejetée au chargement). La pénalité de portée vaut −2
|
||||||
par incrément complet au-delà du premier ; une arme `ranged` reste utilisable
|
par incrément complet au-delà du premier ; une arme `ranged` reste utilisable
|
||||||
jusqu'à 10 incréments, une arme `touch` jusqu'à 5 (jet/firearm), au-delà elle
|
jusqu'à 10 incréments, une arme `touch` jusqu'à 5 (jet/firearm), au-delà elle
|
||||||
n'est pas sélectionnable. `damage_bonus` et `dr` sont optionnels.
|
n'est pas sélectionnable. `damage_bonus` et `dr` sont optionnels.
|
||||||
|
|||||||
@@ -278,7 +278,11 @@ class AttackSpec(_FrozenModel):
|
|||||||
crit_mult: int = Field(default=2, ge=2, le=4)
|
crit_mult: int = Field(default=2, ge=2, le=4)
|
||||||
reach_ft: int = 5
|
reach_ft: int = 5
|
||||||
reach_weapon: bool = False
|
reach_weapon: bool = False
|
||||||
range_increment_ft: int | None = None
|
# Positive or None (None = no range increments: melee, or touch at natural
|
||||||
|
# reach). A zero increment would crash the engine's range_penalty with a
|
||||||
|
# ZeroDivisionError (dist_ft // 0); a negative one silently makes the
|
||||||
|
# weapon unusable -- both are data errors, rejected at the schema.
|
||||||
|
range_increment_ft: int | None = Field(default=None, ge=1)
|
||||||
count: int = Field(default=1, ge=1)
|
count: int = Field(default=1, ge=1)
|
||||||
firearm: bool = False
|
firearm: bool = False
|
||||||
two_handed: bool = False
|
two_handed: bool = False
|
||||||
|
|||||||
@@ -116,6 +116,21 @@ class TestAttackSpec:
|
|||||||
with pytest.raises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
AttackSpec.model_validate(_attack_dict(damage=[]))
|
AttackSpec.model_validate(_attack_dict(damage=[]))
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("increment", [0, -30])
|
||||||
|
def test_rejects_non_positive_range_increment(self, increment: int) -> None:
|
||||||
|
# Given a range_increment_ft of 0 (ZeroDivisionError in range_penalty)
|
||||||
|
# or negative (silently unusable weapon)
|
||||||
|
# When/Then validation rejects it
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
AttackSpec.model_validate(_attack_dict(kind="ranged", range_increment_ft=increment))
|
||||||
|
|
||||||
|
def test_accepts_positive_range_increment(self) -> None:
|
||||||
|
# Given a legal range increment
|
||||||
|
# When validated
|
||||||
|
attack = AttackSpec.model_validate(_attack_dict(kind="ranged", range_increment_ft=30))
|
||||||
|
# Then it is kept
|
||||||
|
assert attack.range_increment_ft == 30
|
||||||
|
|
||||||
def test_rejects_crit_mult_below_2(self) -> None:
|
def test_rejects_crit_mult_below_2(self) -> None:
|
||||||
# Given crit_mult=1 (a weapon that never multiplies is not PF1e)
|
# Given crit_mult=1 (a weapon that never multiplies is not PF1e)
|
||||||
# When/Then validation rejects it
|
# When/Then validation rejects it
|
||||||
|
|||||||
Reference in New Issue
Block a user