feat(engine): 实现阵营技能树系统

- 新增 FactionSkill、FactionSkillTree、FactionSkills 数据模型
- 创建 faction_skills.py 模块处理技能解锁和效果应用
- 技能解锁条件:power >= cost 且前置技能已解锁
- 技能效果:increase_positive_emotion、reduce_conflict、increase_fear、suppress_others
- 添加 4 个技能树测试用例,全部 30 个测试通过

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
empty
2025-12-30 13:25:20 +08:00
parent 62b4428cf1
commit 3f75eb26c4
5 changed files with 215 additions and 0 deletions

View File

@@ -481,3 +481,63 @@ def test_votes_trigger_faction_event(client):
# 要么触发了事件,要么能量在累积
assert triggered or factions["optimists"]["power"] > 0
# ==================== 技能树系统测试 ====================
def test_faction_skills_exists_in_world_state(client):
"""测试 world_state 包含 faction_skills 字段"""
resp = client.post("/step", json={"events": []})
data = resp.json()
assert "faction_skills" in data["world_state"]
skills = data["world_state"]["faction_skills"]
assert "optimists" in skills
assert "fearful" in skills
def test_faction_skills_has_required_fields(client):
"""测试技能包含必要字段"""
resp = client.post("/step", json={"events": []})
data = resp.json()
skills = data["world_state"]["faction_skills"]
opt_skills = skills["optimists"]["skills"]
assert "festival_boost" in opt_skills
skill = opt_skills["festival_boost"]
assert "unlocked" in skill
assert "cost" in skill
assert "effect" in skill
assert "requires" in skill
def test_skill_unlock_when_power_sufficient(client):
"""测试能量足够时解锁技能"""
# 多轮投票累积能量到 10+
for i in range(6):
events = [
{"type": "vote", "faction": "optimists", "text": "", "user": f"u{i}_a", "ts": i},
{"type": "vote", "faction": "optimists", "text": "", "user": f"u{i}_b", "ts": i+0.1},
]
client.post("/step", json={"events": events})
# 再执行一轮让投票生效
resp = client.post("/step", json={"events": []})
data = resp.json()
skills = data["world_state"]["faction_skills"]["optimists"]["skills"]
# festival_boost cost=10应该已解锁
assert skills["festival_boost"]["unlocked"] is True
def test_skill_requires_prerequisite(client):
"""测试技能需要前置技能"""
resp = client.post("/step", json={"events": []})
data = resp.json()
skills = data["world_state"]["faction_skills"]["optimists"]["skills"]
# unity 需要 festival_boost 作为前置
assert skills["unity"]["requires"] == ["festival_boost"]
# 初始状态下 unity 未解锁
assert skills["unity"]["unlocked"] is False