dnd5e-engine 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/CHANGELOG.md +14 -0
- dnd5e_engine-0.1.2/Makefile +19 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/PKG-INFO +3 -1
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/pyproject.toml +30 -3
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/scripts/_smoke_grid_combat.py +4 -6
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/__init__.py +7 -1
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/formula.py +1 -3
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/orchestrator.py +1068 -774
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/rules/combat.py +2 -8
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/rules/effects.py +5 -5
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/types/intent.py +5 -0
- dnd5e_engine-0.1.2/src/dnd5e_engine/views.py +60 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_apply_changes_to_check.py +3 -5
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_build_party_passives.py +1 -3
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_combat_bucket_keys.py +2 -3
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_concentration_drop_effect_name.py +1 -3
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_concentration_repeat_save_identity.py +3 -9
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_enchantment_sidecar_projection.py +2 -6
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_expire_clears_active_conditions.py +2 -4
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_iter11_fixes.py +9 -29
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_iter13_fixes.py +1 -2
- dnd5e_engine-0.1.2/tests/test_live_combat_view.py +91 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_orchestrator_pc_resolution_typed.py +44 -2
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_passive_stats.py +6 -2
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_phase6_public_surface.py +8 -0
- dnd5e_engine-0.1.2/tests/test_public_api_surface.py +93 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_rage_second_wind_e2e.py +1 -3
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_resolve_check.py +6 -14
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_resolve_check_flag_advantage.py +3 -15
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_start_combat_active_effects.py +39 -35
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_start_combat_seeds_lifecycle.py +1 -2
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_types_smoke.py +5 -9
- dnd5e_engine-0.1.0/Makefile +0 -10
- dnd5e_engine-0.1.0/tests/test_public_api_surface.py +0 -47
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/.gitignore +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/LICENSE +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/NOTICE +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/README.md +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/scripts/smoke_clean_install.sh +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/__init__.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/apply.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/attack.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/build_context.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/cast.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/check.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/context.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/damage.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/dice.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/effects.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/heal.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/mastery.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/monster_actions.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/passive_stats.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/resolver.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/save.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/save_primitive.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/activities/scale.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/build_party.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/build_spec.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/check.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/death_saves.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/dispatch.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/event_dicts.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/events.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/lib_loader.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/outcome.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/py.typed +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/results.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/rules/__init__.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/rules/_class_meta.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/rules/_parsing.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/rules/combat_data.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/rules/combat_helpers.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/rules/conditions.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/rules/dice.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/rules/equipment.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/rules/gambits.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/rules/resolution.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/rules/skills.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/rules/spells.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/spatial.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/specs.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/testing.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/types/__init__.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/types/combat.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/types/conditions.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/types/dice.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/src/dnd5e_engine/types/effects.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/__init__.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/activities/__init__.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/activities/test_build_context.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/activities/test_monster_actions.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_active_effect_schema.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_apply_modifiers.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_build_context_abilities.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_build_party.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_combat_flag_advantage.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_coverage_backfill_death_saves.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_coverage_backfill_seam_errors.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_duration_tick_at_turn_end.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_effects_builder_internals.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_end_combat_snapshot.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_feature_save_dc_no_spell_override.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_get_actor_active_effects.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_grid_topology.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_iter5_fixes.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_lib_loader.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_orchestrator_gating_typed.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_orchestrator_grid_combat.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_orchestrator_monster_typed.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_orchestrator_move_mark_typed.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_resolve_check_specific_buckets.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_rules_smoke.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_scale_resolver.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_smoke.py +0 -0
- {dnd5e_engine-0.1.0 → dnd5e_engine-0.1.2}/tests/test_use_feature_intent.py +0 -0
|
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.2]
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `ActionType.USE_ITEM` parser-facing action member.
|
|
14
|
+
|
|
15
|
+
## [0.1.1]
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- `LiveCombatView` — public read-model projection of live combat state.
|
|
19
|
+
- `get_live(handle) -> LiveCombatView` is now part of the public API (`__all__`);
|
|
20
|
+
it returns a point-in-time snapshot view instead of the private `_LiveCombat`.
|
|
21
|
+
- `roll_dice_str` — the dice-expression evaluator is now a public test seam
|
|
22
|
+
(renamed from the private `_roll_dice_str`).
|
|
23
|
+
|
|
10
24
|
## [0.1.0]
|
|
11
25
|
|
|
12
26
|
First public release.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
.PHONY: check lint format format-check type test coverage security smoke
|
|
2
|
+
check: lint format-check type coverage security
|
|
3
|
+
lint:
|
|
4
|
+
uv run ruff check .
|
|
5
|
+
format:
|
|
6
|
+
uv run ruff format .
|
|
7
|
+
format-check:
|
|
8
|
+
uv run ruff format --check .
|
|
9
|
+
type:
|
|
10
|
+
uv run mypy src/
|
|
11
|
+
test:
|
|
12
|
+
uv run pytest -q
|
|
13
|
+
# Baseline-and-ratchet coverage gate (75% observed today; floor set 2pts under).
|
|
14
|
+
coverage:
|
|
15
|
+
uv run pytest -q --cov=dnd5e_engine --cov-report=term-missing --cov-fail-under=73
|
|
16
|
+
security:
|
|
17
|
+
uv run bandit -r src/ -c pyproject.toml -q
|
|
18
|
+
smoke:
|
|
19
|
+
bash scripts/smoke_clean_install.sh
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dnd5e-engine
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Pure-Python D&D 5e SRD rules engine — combat, checks, effects. Host-agnostic, zero I/O.
|
|
5
5
|
Author: Tapestria contributors
|
|
6
6
|
License: MIT License
|
|
@@ -37,7 +37,9 @@ Requires-Dist: d20>=1.1.2
|
|
|
37
37
|
Requires-Dist: dnd5e-srd-data>=0.1.0
|
|
38
38
|
Requires-Dist: pydantic>=2.10.0
|
|
39
39
|
Provides-Extra: dev
|
|
40
|
+
Requires-Dist: bandit>=1.8.0; extra == 'dev'
|
|
40
41
|
Requires-Dist: mypy>=1.13.0; extra == 'dev'
|
|
42
|
+
Requires-Dist: pytest-cov>=6.0.0; extra == 'dev'
|
|
41
43
|
Requires-Dist: pytest>=8.3.0; extra == 'dev'
|
|
42
44
|
Requires-Dist: ruff>=0.9.0; extra == 'dev'
|
|
43
45
|
Description-Content-Type: text/markdown
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dnd5e-engine"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "Pure-Python D&D 5e SRD rules engine — combat, checks, effects. Host-agnostic, zero I/O."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -30,8 +30,10 @@ dependencies = [
|
|
|
30
30
|
[project.optional-dependencies]
|
|
31
31
|
dev = [
|
|
32
32
|
"pytest>=8.3.0",
|
|
33
|
+
"pytest-cov>=6.0.0",
|
|
33
34
|
"ruff>=0.9.0",
|
|
34
35
|
"mypy>=1.13.0",
|
|
36
|
+
"bandit>=1.8.0",
|
|
35
37
|
]
|
|
36
38
|
|
|
37
39
|
[tool.uv.sources]
|
|
@@ -60,17 +62,42 @@ target-version = "py312"
|
|
|
60
62
|
[tool.ruff.lint]
|
|
61
63
|
select = [
|
|
62
64
|
"E", "F", "I", "N", "UP", "B", "SIM", "T20", "BLE",
|
|
63
|
-
"ASYNC", "PT", "RUF", "TRY",
|
|
65
|
+
"C90", "ASYNC", "PT", "RUF", "TRY",
|
|
64
66
|
]
|
|
65
67
|
ignore = [
|
|
66
68
|
"TRY003",
|
|
67
69
|
"RUF001", "RUF002", "RUF003",
|
|
68
70
|
]
|
|
69
71
|
|
|
72
|
+
# Baseline-and-ratchet: set at the worst function observed today
|
|
73
|
+
# (submit_player_intent=23, in orchestrator.py). Ratcheted down from 56
|
|
74
|
+
# after extracting the orchestrator hotspots (_emit 49→12, start_combat
|
|
75
|
+
# 34→3, _build_hydration_payload 36→6). A PR past this must justify it;
|
|
76
|
+
# keep ratcheting DOWN.
|
|
77
|
+
[tool.ruff.lint.mccabe]
|
|
78
|
+
max-complexity = 23
|
|
79
|
+
|
|
70
80
|
[tool.ruff.lint.per-file-ignores]
|
|
71
|
-
|
|
81
|
+
# Test bodies are allowed to be branchy/repetitive (C901) and print (T201).
|
|
82
|
+
"tests/**/*.py" = ["E501", "F841", "PT006", "PT011", "C901"]
|
|
72
83
|
# Smoke scripts print human-readable progress/result lines (T201) by design.
|
|
73
84
|
"scripts/**/*.py" = ["T201"]
|
|
74
85
|
|
|
75
86
|
[tool.pytest.ini_options]
|
|
76
87
|
testpaths = ["tests"]
|
|
88
|
+
|
|
89
|
+
[tool.coverage.run]
|
|
90
|
+
source = ["dnd5e_engine"]
|
|
91
|
+
|
|
92
|
+
[tool.coverage.report]
|
|
93
|
+
exclude_also = [
|
|
94
|
+
"if TYPE_CHECKING:",
|
|
95
|
+
"@overload",
|
|
96
|
+
"raise NotImplementedError",
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
# Recursive scan of src/ only; tests carry asserts (B101) and the engine
|
|
100
|
+
# rolls dice with the stdlib RNG (B311) — neither is a security concern.
|
|
101
|
+
[tool.bandit]
|
|
102
|
+
exclude_dirs = ["tests"]
|
|
103
|
+
skips = ["B101", "B311"]
|
|
@@ -30,9 +30,7 @@ _BUNDLED_MONSTER_SLUG = "goblin-warrior"
|
|
|
30
30
|
def _check_corpus() -> None:
|
|
31
31
|
loader = get_lib_loader() # BundledAssetLoader → reads bundled canonical/
|
|
32
32
|
monster = loader.get_monster(_BUNDLED_MONSTER_SLUG)
|
|
33
|
-
assert monster is not None,
|
|
34
|
-
f"bundled corpus did not resolve monster {_BUNDLED_MONSTER_SLUG!r}"
|
|
35
|
-
)
|
|
33
|
+
assert monster is not None, f"bundled corpus did not resolve monster {_BUNDLED_MONSTER_SLUG!r}"
|
|
36
34
|
|
|
37
35
|
|
|
38
36
|
async def _run_grid_combat() -> None:
|
|
@@ -63,15 +61,15 @@ async def _run_grid_combat() -> None:
|
|
|
63
61
|
grid_scene=GridScene(width=10, height=10),
|
|
64
62
|
rng_seed=1,
|
|
65
63
|
)
|
|
66
|
-
|
|
67
|
-
assert live.actor_zone["char:hero"] == "0,0"
|
|
64
|
+
assert get_live(start.handle).actor_zone["char:hero"] == "0,0"
|
|
68
65
|
# One legal single-cell move proves the grid MOVE path resolves end-to-end.
|
|
69
66
|
await submit_player_intent(
|
|
70
67
|
start.handle,
|
|
71
68
|
actor_id="char:hero",
|
|
72
69
|
intent=PlayerIntent(intent_type="move", target_zone_id=cell_id(1, 1)),
|
|
73
70
|
)
|
|
74
|
-
|
|
71
|
+
# get_live returns a point-in-time snapshot, so re-fetch after the move.
|
|
72
|
+
assert get_live(start.handle).actor_zone["char:hero"] == "1,1", "grid move did not apply"
|
|
75
73
|
result = await end_combat(start.handle)
|
|
76
74
|
assert result is not None
|
|
77
75
|
print("SMOKE OK: corpus loaded + grid combat ran (hero moved 0,0 -> 1,1)")
|
|
@@ -13,7 +13,7 @@ Deferred for later phases:
|
|
|
13
13
|
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
|
-
__version__ = "0.1.
|
|
16
|
+
__version__ = "0.1.1"
|
|
17
17
|
|
|
18
18
|
from dnd5e_engine.build_party import build_party_member
|
|
19
19
|
from dnd5e_engine.build_spec import (
|
|
@@ -26,10 +26,12 @@ from dnd5e_engine.check import CheckKind, CheckResult, CheckSpec, resolve_check
|
|
|
26
26
|
from dnd5e_engine.events import CombatEvent, IntentType
|
|
27
27
|
from dnd5e_engine.orchestrator import (
|
|
28
28
|
CombatHandle,
|
|
29
|
+
LiveCombatView,
|
|
29
30
|
PlayerIntent,
|
|
30
31
|
advance_monster_turn,
|
|
31
32
|
end_combat,
|
|
32
33
|
get_actor_active_effects,
|
|
34
|
+
get_live,
|
|
33
35
|
narration_events,
|
|
34
36
|
start_combat,
|
|
35
37
|
submit_player_intent,
|
|
@@ -40,6 +42,7 @@ from dnd5e_engine.outcome import (
|
|
|
40
42
|
LootDrop,
|
|
41
43
|
)
|
|
42
44
|
from dnd5e_engine.results import EndCombatResult, StartCombatResult
|
|
45
|
+
from dnd5e_engine.rules.effects import roll_dice_str
|
|
43
46
|
from dnd5e_engine.spatial import cell_id, parse_cell
|
|
44
47
|
from dnd5e_engine.specs import (
|
|
45
48
|
EncounterMemberSpec,
|
|
@@ -74,6 +77,7 @@ __all__ = [
|
|
|
74
77
|
"EndCombatResult",
|
|
75
78
|
"GridScene",
|
|
76
79
|
"IntentType",
|
|
80
|
+
"LiveCombatView",
|
|
77
81
|
"LootDrop",
|
|
78
82
|
"PartyMemberSpec",
|
|
79
83
|
"PlayerIntent",
|
|
@@ -85,10 +89,12 @@ __all__ = [
|
|
|
85
89
|
"cell_id",
|
|
86
90
|
"end_combat",
|
|
87
91
|
"get_actor_active_effects",
|
|
92
|
+
"get_live",
|
|
88
93
|
"make_build_spec",
|
|
89
94
|
"narration_events",
|
|
90
95
|
"parse_cell",
|
|
91
96
|
"resolve_check",
|
|
97
|
+
"roll_dice_str",
|
|
92
98
|
"start_combat",
|
|
93
99
|
"submit_player_intent",
|
|
94
100
|
]
|
|
@@ -123,9 +123,7 @@ def resolve_damage_block(
|
|
|
123
123
|
return block.model_copy(update=updates) if updates else block
|
|
124
124
|
|
|
125
125
|
|
|
126
|
-
def _resolve_token(
|
|
127
|
-
token: str, ctx: ActivityResolutionContext, ability: str | None
|
|
128
|
-
) -> int | str:
|
|
126
|
+
def _resolve_token(token: str, ctx: ActivityResolutionContext, ability: str | None) -> int | str:
|
|
129
127
|
if token == _TOKEN_PROF:
|
|
130
128
|
return ctx.caster_proficiency_bonus
|
|
131
129
|
if token == _TOKEN_MOD:
|