一个轻量级、可扩展的旧式角色扮演游戏(OSR TRPG)规则引擎,基于B/X D&D规则设计,提供灵活的实体管理、骰子系统和可配置的规则加载机制。
OSR TRPG 规则引擎是一个专为旧式角色扮演游戏(Old School Renaissance)设计的强大灵活框架,提供了构建TRPG系统所需的核心组件。该引擎基于经典的B/X D&D规则设计,同时保持高度的可扩展性,允许开发者轻松实现自定义规则和游戏机制。
最新版本的引擎配备了增强版骰子系统,支持完整的四则运算、括号表达式和复杂表达式计算,为游戏规则的实现提供了更强大的数学基础。无论是简单的属性检定,还是复杂的伤害计算或特殊能力效果,都能通过直观的表达式轻松实现。
- 强大的骰子系统:支持完整四则运算(加、减、乘、除)、括号表达式、百分骰和变量替换,可处理复杂表达式如
(2d6+str_mod)*level - 强大的实体管理:提供属性叠加、来源管理和能力检定功能
- 可配置的规则加载:通过YAML文件定义规则,支持多来源数值叠加
- B/X D&D规则实现:内置经典规则集,包括能力值、职业、种族和等级系统
- 安全性保障:骰子系统内置表达式验证和安全检查,防止潜在风险
- 扩展性:模块化设计,易于扩展和定制
- Python 3.10 或更高版本
- uv 包管理器(推荐)或 pip
- 克隆仓库
git clone https://github.com/yourusername/osr_trpg_engine.git
cd osr_trpg_engine- 创建虚拟环境
python -m venv .venv- 激活虚拟环境
- Windows:
.venv\Scripts\activate
- Linux/macOS:
source .venv/bin/activate
- 安装依赖
使用 uv:
# 安装项目依赖,包括新增的asteval库
uv install或使用 pip:
pip install -e .- asteval: 安全的表达式解析和计算库,用于支持骰子系统的复杂数学表达式计算
以下是一个全面的示例,展示如何使用引擎创建角色、进行检定,以及使用增强版骰子系统进行复杂表达式计算:
from rules.basic1981 import Basic1981Rulebook, create_character
from engine.dice import roll
# 创建规则书实例
rulebook = Basic1981Rulebook()
# 创建角色
character = create_character(
rulebook,
character_class="fighter",
race="human",
name="Aragorn"
)
# 查看角色信息
print(f"角色名称: {character.get('name')}")
print(f"生命值: {character.get('hp')}")
print(f"力量: {character.get('str')} (+{character.get('str_mod')})")
# 进行基本检定
result = character.check("d20+str_mod", difficulty=15)
print(f"力量检定结果: {'通过' if result else '失败'}")
# 使用增强版骰子系统进行复杂表达式计算
# 直接使用roll函数
print("\n=== 高级骰子表达式示例 ===")
print(f"武器伤害计算 (2d6+力量修正): {roll('2d6+3')}")
print(f"复杂表达式: (2d6+4)*2: {roll('(2d6+4)*2')}")
print(f"带变量的表达式: level*d8+con_mod: {roll('3*d8+2', {'level': 3, 'con_mod': 2})}")
# 在角色检定中使用复杂表达式
print("\n=== 角色能力检定示例 ===")
# 设置角色属性以便使用
character.set("level", 5)
character.set("proficiency", 2)
# 使用复杂表达式进行攻击检定
attack_roll = character.get("d20+str_mod+proficiency")
print(f"攻击骰结果: {attack_roll}")
# 计算伤害(使用力量修正、武器加值和等级调整)
damage = character.get("2d6+str_mod+(level/2)")
print(f"伤害计算结果: {damage}")骰子系统允许解析和计算各种骰子表达式,支持基础掷骰、完整四则运算、括号表达式、变量替换和表达式评估:
from engine.dice import roll
# 基本掷骰
print(roll("d20")) # 掷1个20面骰子
print(roll("3d6")) # 掷3个6面骰子并求和
print(roll("d%")) # 掷1个100面骰子(百分骰)
print(roll("2d20")) # 掷2个20面骰子并求和
# 带修饰符的掷骰
print(roll("d20+5")) # 掷1个20面骰子加5
print(roll("3d8+1d4+3")) # 掷3个8面骰子和1个4面骰子,总和加3
print(roll("2d6-2")) # 掷2个6面骰子减2(支持减法)
# 四则运算和括号表达式
print(roll("(d20+4)*2")) # 掷1个20面骰子加4,结果乘以2
print(roll("(2d6/2)+3")) # 掷2个6面骰子除以2(整除),加3
print(roll("2 + 3 * (4 - 1)")) # 常规数学表达式,支持空格
# 使用变量进行计算
variables = {"str_mod": 3, "prof": 2, "level": 5}
print(roll("d20+str_mod+prof", variables)) # 掷1个20面骰子加力量修正和熟练加值
print(roll("level*d8+con_mod", {"level": 3, "con_mod": 2})) # 等级乘以d8加体质修正
print(roll("3d6+str_mod+(level/2)", {"str_mod": 2, "level": 7})) # 复杂变量表达式实体系统是游戏对象(如角色、怪物、物品)的基础:
from engine.entity import Entity
from engine.source import Source
# 创建规则书类(简化示例)
class SimpleRulebook:
pass
# 创建实体
rulebook = SimpleRulebook()
entity = Entity(rulebook)
# 设置属性
entity.set("name", "Goblin")
entity.set("hp", 7)
entity.set("str", 8)
entity.set("str_mod", -1)
# 获取属性
print(entity.get("name")) # 输出: Goblin
print(entity.get("hp")) # 输出: 7
# 创建自定义来源
class MonsterSource(Source):
def priority(self):
return 10
def keys(self):
return ["attack", "damage"]
def get(self, key):
values = {"attack": "d20+0", "damage": "1d6"}
return values[key]
# 附加来源
monster_source = MonsterSource()
entity.attach(monster_source)
# 获取带骰子表达式的值(会自动计算)
print(entity.get("attack")) # 输出: 掷骰结果 (如 12)
print(entity.get("damage")) # 输出: 掷骰结果 (如 4)使用YAML文件定义和加载规则:
from engine.yaml_source import YamlSource, load_yaml_folder
import os
# 从单个YAML文件加载规则
source = YamlSource("rules/yaml/fighter.yml")
print(f"规则名称: {source.name}")
print(f"提供的键: {source.keys()}")
# 从文件夹加载所有规则
yaml_folder = "rules/yaml"
if os.path.exists(yaml_folder):
sources = load_yaml_folder(yaml_folder)
for src in sources:
print(f"加载规则: {src.name}, 优先级: {src.priority()}")YAML规则文件示例:
name: "Fighter"
priority: 10
description: "战士是精通战斗的专家"
attributes:
attack_bonus:
value: "level-1"
mode: "add"
weapon_proficiency:
value: "all"
mode: "override"使用内置的B/X D&D规则创建角色:
from rules.basic1981 import Basic1981Rulebook, create_character
# 创建规则书
rulebook = Basic1981Rulebook()
# 方法1: 创建随机角色
random_character = create_character(rulebook)
# 方法2: 创建指定职业和种族的角色
specific_character = create_character(
rulebook,
character_class="magic-user",
race="elf",
name="Legolas"
)
# 检查种族职业组合是否有效
if rulebook.is_class_available_for_race("thief", "dwarf"):
print("矮人可以选择盗贼职业")
# 获取等级上限
max_level = rulebook.get_max_level("fighter", "elf")
print(f"精灵战士的等级上限: {max_level}")osr_trpg_engine/
├── engine/ # 核心引擎模块
│ ├── __init__.py # 模块初始化
│ ├── dice.py # 增强版骰子解析和计算系统
│ ├── entity.py # 角色实体类
│ ├── source.py # 数值来源抽象
│ └── yaml_source.py # YAML数据驱动
├── rules/ # 规则实现
│ ├── __init__.py # 模块初始化
│ ├── basic1981.py # B/X D&D规则
│ └── yaml/ # YAML规则文件
│ ├── cleric.yml # 牧师职业规则
│ ├── dwarf.yml # 矮人种族规则
│ ├── elf.yml # 精灵种族规则
│ ├── fighter.yml # 战士职业规则
│ ├── halfling.yml # 半身人种族规则
│ ├── human.yml # 人类种族规则
│ ├── magic_user.yml # 法师职业规则
│ └── thief.yml # 盗贼职业规则
├── tests/ # 测试文件
│ ├── test_dice.py # 骰子系统测试
│ ├── test_entity.py # 实体系统测试
│ ├── test_rules.py # 规则系统测试
│ └── ... # 其他测试文件
├── main.py # 主程序入口
├── main_plan.md # 项目计划文档
├── main_plan_v2.md # 增强版计划文档(包含骰子系统更新)
├── pyproject.toml # 项目配置
├── README.md # 项目文档
└── verify.bat # 验证脚本(Windows)
增强版骰子系统是引擎的核心组件,提供了强大而灵活的表达式解析和计算能力:
- 完整的四则运算支持:加(+)、减(-)、乘(*)、除(/)操作
- 括号表达式:支持多层嵌套括号,如
(2d6+4)*(d20-5) - 骰子表达式:支持标准骰子表示法
XdY,其中X是骰子数量,Y是面数 - 百分骰支持:使用
d%表示掷100面骰子 - 变量支持:可以在表达式中使用变量,如
level*d8+con_mod - 安全性保障:内置表达式验证和安全检查,防止潜在风险
骰子系统支持以下表达式语法:
- 基本骰子:
d6,2d20,3d8 - 百分骰:
d%等同于d100 - 四则运算:
d20+5,3d6-2,4*d4,8/2 - 括号表达式:
(d20+4)*2,3d6+(2d8-1) - 带变量的表达式:
d20+str_mod,level*d6+bonus - 复杂组合:
(2d6+str_mod)*(level/2+1),d20+proficiency+(advantage?2:0)
骰子系统包含多层安全措施,确保表达式计算的安全性:
- 表达式长度限制:防止过长表达式导致的性能问题
- 危险字符过滤:禁止使用可能导致安全风险的字符和模式
- 变量验证:确保提供的变量符合安全要求
- 计算结果限制:防止计算结果过大
- 异常处理:捕获并适当地处理各种可能的错误情况,如除零错误
骰子系统经过优化,可以高效处理各种表达式:
- 使用正则表达式高效识别骰子表达式
- 利用asteval库进行安全快速的表达式计算
- 实现了针对骰子表达式的特殊处理逻辑
- 优化了变量替换和表达式验证过程
战斗系统
# 攻击检定
attack_roll = character.get("d20+str_mod+proficiency")
# 伤害计算
weapon_damage = character.get("2d6+str_mod")
# 暴击伤害
critical_damage = character.get("2d6*2+str_mod")技能检定
# 攀爬检定(带困难度调整)
climb_check = character.get("d20+dex_mod+(skill_level/2)")
# 感知检定(带环境修正)
surprise_check = character.get("d20+wis_mod+perception_bonus-2")角色创建
# 能力值生成(4d6取最高3个)
strength = roll("sum(sorted([d6, d6, d6, d6])[1:])")
# 生命值计算(战士职业)
hp = roll("level*d8+con_mod")- 创建新的规则书类,继承或参考
Basic1981Rulebook - 实现自定义的角色创建、属性计算等方法
- 为新规则创建对应的YAML文件
- 创建新类继承
Source抽象基类 - 实现必要的方法:
priority(),keys(),get() - 可选实现:
mode()以支持自定义叠加模式
本节将指导您如何从规则书中提取游戏规则,并将其以Python或YAML形式实现到/rules目录中。
当您要实现一个新规则时,首先需要系统性地分析规则书:
- 识别核心机制:找出规则书中的核心计算方法、属性关系和触发条件
- 分解成数值关系:将复杂规则分解为可计算的数值表达式
- 确定优先级:决定规则相对于其他规则的优先级顺序
- 定义适用条件:确定规则在什么情况下适用
规则描述:"战士每级获得+1攻击加值"
- 核心机制:等级与攻击加值的线性关系
- 数值表达式:
attack_bonus = level - 1 - 优先级:中等(10),确保基础规则不被覆盖
- 适用条件:所有战士职业角色
对于大多数常规规则,使用YAML文件是最简单的方法。这些文件应该放在/rules/yaml/目录下。
name: "规则名称"
priority: 优先级数值
description: "规则描述"
attributes:
属性名1:
value: "属性值或表达式"
mode: "叠加模式(override/add/mul)"
属性名2:
value: "属性值或表达式"
mode: "叠加模式"name: "fighter"
priority: 10
description: "战士是精通战斗的专家"
attributes:
hit_dice: "1d8"
hit_points: "level * d8 + con_mod"
attack_bonus: "level - 1"
weapon_proficiency: "all"
armor_proficiency: "all"
max_level: 14对于包含复杂逻辑、条件判断或特殊计算的规则,应使用Python类实现。
- 在
/rules/目录下创建新的Python模块 - 参考
basic1981.py创建规则书类 - 实现核心方法:
- 规则加载方法
- 角色创建方法
- 能力值生成和修正计算
- 自定义检定或计算方法
class AdvancedRulebook:
def __init__(self):
self.races = {}
self.classes = {}
# 加载YAML规则文件
self._load_rules()
def _load_rules(self):
# 从YAML文件夹加载规则
# ...
def create_character(self, character_class=None, race=None, name=None):
# 实现角色创建逻辑
# 包含复杂的条件判断和计算
# ...
def custom_check(self, entity, check_type, difficulty):
# 实现自定义检定逻辑
# ...规则引擎支持三种主要的属性叠加模式:
- override (覆盖):直接替换现有值
- add (加法):将值加到现有值上(支持数字和骰子表达式)
- mul (乘法):将现有值乘以指定因子
优先级决定了规则应用的顺序:数字越小,优先级越高。
添加新规则后,确保进行充分测试:
- 编写单元测试验证规则计算正确性
- 测试规则与其他规则的交互
- 验证边缘情况和特殊条件
# 测试示例
def test_fighter_rules():
rulebook = Basic1981Rulebook()
fighter = create_character(rulebook, character_class="fighter")
# 验证攻击加值计算正确
assert fighter.get("attack_bonus") == fighter.get("level") - 1
# 验证生命值计算
assert fighter.get("hp") > 0 # 应该有正的生命值- 保持简单:尽可能使用YAML实现简单规则
- 模块化:将复杂规则拆分为逻辑组件
- 文档化:为每个规则添加清晰的描述和注释
- 一致性:遵循现有规则的命名和结构约定
- 可扩展性:设计规则时考虑未来可能的扩展
项目使用pytest进行全面测试,确保所有功能正常工作并保持向后兼容。
运行所有测试:
python -m pytest仅运行骰子系统测试:
python -m pytest tests/test_dice.py -v运行特定测试用例:
python -m pytest tests/test_dice.py::test_basic_operations -v项目测试涵盖了以下关键领域:
- 骰子系统测试:验证基本掷骰、四则运算、括号表达式、变量替换和错误处理
- 实体系统测试:测试属性设置、获取、叠加和来源管理
- 规则系统测试:验证规则加载、属性计算和职业种族组合
使用预配置的验证脚本运行所有检查:
Windows:
verify.bat添加新功能时,请确保同时添加相应的测试用例:
- 在
tests/目录中创建或更新相应的测试文件 - 使用pytest的测试函数格式编写测试
- 确保测试覆盖正常情况、边界情况和错误处理
- 运行测试以验证实现
欢迎贡献代码、报告问题或提供建议!请遵循以下步骤:
- Fork 仓库
- 创建功能分支 (
git checkout -b feature/amazing-feature) - 提交更改 (
git commit -m 'Add some amazing feature') - 推送到分支 (
git push origin feature/amazing-feature) - 开启 Pull Request
本项目使用MIT许可证。详情请参见LICENSE文件。