aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/examples/plugins
diff options
context:
space:
mode:
author简律纯 <i@jyunko.cn>2025-10-25 00:30:48 +0800
committer简律纯 <i@jyunko.cn>2025-10-25 00:30:48 +0800
commitcbc653ffd0ea9abf4360623dc7a7651e1a49cc61 (patch)
treeea3c396148158077bae3e77eaa9341f8c1990636 /examples/plugins
parent08299b37dfda86e56e4f2b442f68ccd2da7a82e3 (diff)
downloadconventional_role_play-cbc653ffd0ea9abf4360623dc7a7651e1a49cc61.tar.gz
conventional_role_play-cbc653ffd0ea9abf4360623dc7a7651e1a49cc61.zip
feat: Implement plugin system with combat tracker and dice analyzer
- Added `plugin_system_demo.py` to demonstrate basic plugin usage, processing, and analysis. - Created `CombatTrackerPlugin` for tracking combat statistics including damage and healing. - Developed `DiceAnalyzerPlugin` for analyzing dice rolls and calculating success rates. - Introduced `renderer_demo.py` for rendering output in HTML, Markdown, and JSON formats. - Implemented `rule_system_demo.py` to showcase rule engine capabilities with various examples. - Established core rule engine functionality in `rules.py` with support for conditions and actions. - Enhanced base plugin structure in `base.py` to support different plugin types (Processor, Renderer, Analyzer). - Added custom exception handling in `exceptions.py` for better error management. - Configured logging setup in `logging_config.py` for improved logging capabilities. - Created unit tests in `test_rust_core.py` to validate core functionalities and performance.
Diffstat (limited to 'examples/plugins')
-rw-r--r--examples/plugins/combat_tracker_plugin.py96
-rw-r--r--examples/plugins/dice_analyzer_plugin.py90
2 files changed, 186 insertions, 0 deletions
diff --git a/examples/plugins/combat_tracker_plugin.py b/examples/plugins/combat_tracker_plugin.py
new file mode 100644
index 0000000..a12bc9e
--- /dev/null
+++ b/examples/plugins/combat_tracker_plugin.py
@@ -0,0 +1,96 @@
+import sys
+import os
+from pathlib import Path
+import re
+from typing import Any, Dict, List
+
+sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))
+
+from conventionalrp.plugins.base import ProcessorPlugin
+
+
+class CombatTrackerPlugin(ProcessorPlugin):
+ """战斗数据追踪插件"""
+
+ def __init__(self):
+ super().__init__("CombatTracker", "1.0.0")
+ self.damage_pattern = re.compile(r'(\d+)\s*点?(伤害|damage|dmg)', re.IGNORECASE)
+ self.heal_pattern = re.compile(r'(\d+)\s*点?(治疗|healing|heal)', re.IGNORECASE)
+
+ def initialize(self, config: Dict[str, Any] | None = None):
+ self.config = config or {}
+ self.total_damage = 0
+ self.total_healing = 0
+ self.character_stats = {}
+ self.logger.info("CombatTrackerPlugin initialized")
+
+ def process_token(self, token: Dict[str, Any]) -> Dict[str, Any]:
+ content = token.get("content", "")
+ speaker = token.get("speaker", "Unknown")
+
+ damage_match = self.damage_pattern.search(content)
+ if damage_match:
+ damage = int(damage_match.group(1))
+ self.total_damage += damage
+
+ if speaker not in self.character_stats:
+ self.character_stats[speaker] = {"damage_dealt": 0, "healing_done": 0}
+ self.character_stats[speaker]["damage_dealt"] += damage
+
+ token["combat_data"] = {
+ "type": "damage",
+ "amount": damage,
+ "total_damage": self.total_damage,
+ }
+
+ heal_match = self.heal_pattern.search(content)
+ if heal_match:
+ healing = int(heal_match.group(1))
+ self.total_healing += healing
+
+ if speaker not in self.character_stats:
+ self.character_stats[speaker] = {"damage_dealt": 0, "healing_done": 0}
+ self.character_stats[speaker]["healing_done"] += healing
+
+ token["combat_data"] = {
+ "type": "healing",
+ "amount": healing,
+ "total_healing": self.total_healing,
+ }
+
+ return token
+
+ def get_combat_summary(self) -> Dict[str, Any]:
+ return {
+ "total_damage": self.total_damage,
+ "total_healing": self.total_healing,
+ "net_damage": self.total_damage - self.total_healing,
+ "character_stats": self.character_stats,
+ }
+
+ def reset_stats(self):
+ self.total_damage = 0
+ self.total_healing = 0
+ self.character_stats.clear()
+ self.logger.info("Combat stats reset")
+
+
+if __name__ == "__main__":
+ plugin = CombatTrackerPlugin()
+ plugin.initialize()
+
+ test_tokens = [
+ {"type": "dialogue", "speaker": "战士", "content": "我攻击兽人,造成12点伤害"},
+ {"type": "dialogue", "speaker": "法师", "content": "火球术命中,造成28点伤害"},
+ {"type": "dialogue", "speaker": "牧师", "content": "治疗术,恢复15点生命值"},
+ {"type": "dialogue", "speaker": "战士", "content": "再次攻击,造成8点伤害"},
+ ]
+
+ for token in test_tokens:
+ processed = plugin.process_token(token)
+ if "combat_data" in processed:
+ print(f" {processed['speaker']}: {processed['combat_data']}")
+
+ summary = plugin.get_combat_summary()
+ for key, value in summary.items():
+ print(f" {key}: {value}")
diff --git a/examples/plugins/dice_analyzer_plugin.py b/examples/plugins/dice_analyzer_plugin.py
new file mode 100644
index 0000000..3ab2fb9
--- /dev/null
+++ b/examples/plugins/dice_analyzer_plugin.py
@@ -0,0 +1,90 @@
+import sys
+import os
+from pathlib import Path
+import re
+from typing import Any, Dict, List
+
+sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))
+
+from conventionalrp.plugins.base import AnalyzerPlugin
+
+
+class DiceAnalyzerPlugin(AnalyzerPlugin):
+ """骰子投掷数据分析插件"""
+ def __init__(self):
+ super().__init__("DiceAnalyzer", "1.0.0")
+ self.dice_pattern = re.compile(r'd(\d+)')
+
+ def initialize(self, config: Dict[str, Any] | None = None):
+ self.config = config or {}
+ self.logger.info("DiceAnalyzerPlugin initialized")
+
+ def analyze(self, data: Any) -> Dict[str, Any]:
+ if not isinstance(data, list):
+ return {"error": "Input must be a list of tokens"}
+
+ total_rolls = 0
+ dice_types = {}
+ success_count = 0
+ failure_count = 0
+ critical_hits = 0
+ critical_fails = 0
+
+ for token in data:
+ if not isinstance(token, dict):
+ continue
+
+ token_type = token.get("type", "")
+ content = token.get("content", "")
+
+ if token_type == "dice":
+ total_rolls += 1
+
+ match = self.dice_pattern.search(content)
+ if match:
+ dice_type = f"d{match.group(1)}"
+ dice_types[dice_type] = dice_types.get(dice_type, 0) + 1
+
+ if token_type == "success":
+ success_count += 1
+ elif token_type == "failure":
+ failure_count += 1
+
+ if "critical" in content.lower():
+ if "success" in token_type or "成功" in content:
+ critical_hits += 1
+ elif "failure" in token_type or "失败" in content:
+ critical_fails += 1
+
+ result = {
+ "total_rolls": total_rolls,
+ "dice_types": dice_types,
+ "success_count": success_count,
+ "failure_count": failure_count,
+ "critical_hits": critical_hits,
+ "critical_fails": critical_fails,
+ "success_rate": success_count / total_rolls if total_rolls > 0 else 0,
+ "critical_rate": (critical_hits + critical_fails) / total_rolls if total_rolls > 0 else 0,
+ }
+
+ self.logger.info(f"Analyzed {total_rolls} dice rolls")
+ return result
+
+
+if __name__ == "__main__":
+ plugin = DiceAnalyzerPlugin()
+ plugin.initialize()
+
+ test_data = [
+ {"type": "dice", "content": "d20=15"},
+ {"type": "success", "content": "检定成功"},
+ {"type": "dice", "content": "d6=4"},
+ {"type": "dice", "content": "d20=20"},
+ {"type": "success", "content": "大成功!Critical hit!"},
+ {"type": "dice", "content": "d20=1"},
+ {"type": "failure", "content": "大失败..."},
+ ]
+
+ result = plugin.analyze(test_data)
+ for key, value in result.items():
+ print(f" {key}: {value}")