summaryrefslogtreecommitdiff
path: root/game.py
diff options
context:
space:
mode:
authorSupremist <sergkarv@gmail.com>2025-10-28 23:47:23 +0200
committerSupremist <sergkarv@gmail.com>2025-10-28 23:47:23 +0200
commitf2c7dbddb602db06ec8e8a8c9a4134451acd52e9 (patch)
treee3d2e0e7900baf8057efc4e0813065ff16990b34 /game.py
parente0911bc676e92ce15b7096b2e85f8b3965a48823 (diff)
downloadpf2e_calc-f2c7dbddb602db06ec8e8a8c9a4134451acd52e9.tar.xz
pf2e_calc-f2c7dbddb602db06ec8e8a8c9a4134451acd52e9.zip
Move common data structs to game.py
Diffstat (limited to 'game.py')
-rw-r--r--game.py87
1 files changed, 87 insertions, 0 deletions
diff --git a/game.py b/game.py
new file mode 100644
index 0000000..fd9aba0
--- /dev/null
+++ b/game.py
@@ -0,0 +1,87 @@
+import re
+from enum import Enum
+from dataclasses import dataclass
+
+from distribution import Distribution
+
+def d(edge_count):
+ res = Distribution()
+ if edge_count == 0:
+ res.insert(0, 1)
+ return res
+ prob = 1/edge_count
+ for val in range(1, edge_count+1):
+ res.insert(val, prob)
+ return res
+
+
+class Roll:
+ Fumble = -1
+ Fail = 0
+ Success = 1
+ Crit = 2
+
+ @staticmethod
+ def get_natural_success_mod(value):
+ if value <= 1:
+ return -1
+ if value >= 20:
+ return 1
+ return 0
+
+ @staticmethod
+ def check(value, dc, bonus=0):
+ res = 0
+ rolled = value + bonus
+ if rolled >= dc + 10:
+ res = Roll.Crit
+ elif rolled >= dc:
+ res = Roll.Success
+ elif rolled <= dc - 10:
+ res = Roll.Fumble
+ res += Roll.get_natural_success_mod(value)
+ return Roll.clamp(res)
+
+ @staticmethod
+ def clamp(value):
+ return max(Roll.Fumble, min(Roll.Crit, value))
+
+
+class Difficulty(Enum):
+ Terrible = -2
+ Low = -1
+ Moderate = 0
+ High = 1
+ Extreme = 2
+
+ @classmethod
+ def from_str(cls, name):
+ name = name.strip().lower()
+ for member in cls:
+ if member.name.lower() == name:
+ return member
+ raise ValueError(f"{name!r} is not a valid {cls.__name__}")
+
+@dataclass
+class RollInfo:
+ die_size: int
+ dice_count: int = 1
+ bonus: int = 0
+
+ @classmethod
+ def parse(cls, str_data):
+ # match strings like 2d6+8 (15)
+ match = re.fullmatch(r"^(\d+)d(\d+)\s*(\+\s*\d+)?\s*(\s*\(\s*\d+\s*\))?",
+ str_data)
+ if not match:
+ raise ValueError(f"{str_data!r} is not a valid RollInfo")
+ res = RollInfo(int(match.group(2).strip()))
+ res.dice_count = int(match.group(1).strip())
+ bonus = match.group(3)
+ if bonus:
+ res.bonus = int(bonus.strip(" +"))
+ return res
+
+ def distribution(self):
+ return self.dice_count * d(self.die_size) + self.bonus
+