新增战斗/冒险系统
This commit is contained in:
34
Plugins/WPSCombatSystem/__init__.py
Normal file
34
Plugins/WPSCombatSystem/__init__.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""WPS战斗系统 - 包含PVE冒险和PVP对战"""
|
||||
|
||||
from .combat_models import (
|
||||
EquipmentDefinition,
|
||||
SkillDefinition,
|
||||
PlayerStats,
|
||||
BattleState,
|
||||
CombatConfig,
|
||||
EQUIPMENT_REGISTRY,
|
||||
SKILL_REGISTRY,
|
||||
)
|
||||
from .combat_service import CombatService
|
||||
from .combat_plugin_status import WPSCombatStatus
|
||||
from .combat_plugin_equipment import WPSCombatEquipment
|
||||
from .combat_plugin_adventure import WPSCombatAdventure
|
||||
from .combat_plugin_battle import WPSCombatBattle
|
||||
from .combat_plugin_heal import WPSCombatHeal
|
||||
|
||||
__all__ = [
|
||||
"EquipmentDefinition",
|
||||
"SkillDefinition",
|
||||
"PlayerStats",
|
||||
"BattleState",
|
||||
"CombatConfig",
|
||||
"EQUIPMENT_REGISTRY",
|
||||
"SKILL_REGISTRY",
|
||||
"CombatService",
|
||||
"WPSCombatStatus",
|
||||
"WPSCombatEquipment",
|
||||
"WPSCombatAdventure",
|
||||
"WPSCombatBattle",
|
||||
"WPSCombatHeal",
|
||||
]
|
||||
|
||||
700
Plugins/WPSCombatSystem/combat_models.py
Normal file
700
Plugins/WPSCombatSystem/combat_models.py
Normal file
@@ -0,0 +1,700 @@
|
||||
"""战斗系统数据模型和配置定义"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
from enum import Enum
|
||||
|
||||
from PWF.Convention.Runtime.Architecture import Architecture
|
||||
from PWF.Convention.Runtime.GlobalConfig import ProjectConfig
|
||||
from PWF.CoreModules.plugin_interface import DatabaseModel
|
||||
from Plugins.WPSBackpackSystem import BackpackItemTier
|
||||
|
||||
# ============================================================================
|
||||
# 配置管理
|
||||
# ============================================================================
|
||||
|
||||
_config: ProjectConfig = Architecture.Get(ProjectConfig)
|
||||
|
||||
# 基础属性配置
|
||||
COMBAT_CONFIG_DEFAULTS = {
|
||||
# 玩家基础属性
|
||||
"combat_base_hp": 100,
|
||||
"combat_base_atk": 10,
|
||||
"combat_base_def": 5,
|
||||
"combat_base_spd": 10,
|
||||
"combat_base_crit": 5, # 百分比
|
||||
"combat_base_crit_dmg": 150, # 百分比
|
||||
|
||||
# 装备强度权重
|
||||
"combat_weight_atk": 1.0,
|
||||
"combat_weight_def": 0.8,
|
||||
"combat_weight_hp": 0.1,
|
||||
"combat_weight_spd": 0.5,
|
||||
"combat_weight_crit": 2.0,
|
||||
"combat_weight_crit_dmg": 0.01,
|
||||
}
|
||||
|
||||
# 冒险系统配置
|
||||
COMBAT_ADVENTURE_CONFIG = {
|
||||
# 阶段时间配置
|
||||
"combat_adventure_base_time": 15, # 第一阶段基础时间(分钟)
|
||||
"combat_adventure_max_time": 1440, # 最大时间上限(24小时)
|
||||
"combat_food_support_time": 15, # 每个食物支持时间(分钟)
|
||||
|
||||
# 成功率配置
|
||||
"combat_adventure_base_success_rate": 0.80, # 第一阶段基础成功率(80%)
|
||||
"combat_adventure_stage_penalty": 0.05, # 每阶段递减(5%)
|
||||
"combat_adventure_min_success_rate": 0.10, # 最低成功率(10%)
|
||||
"combat_adventure_max_success_rate": 0.95, # 最高成功率(95%)
|
||||
|
||||
# 加成系数配置
|
||||
"combat_adventure_equipment_coeff": 0.01, # 装备强度加成系数(每100强度+1%)
|
||||
"combat_adventure_fortune_coeff": 0.10, # 运势加成系数(运势值*10)
|
||||
|
||||
# 时间缩减配置
|
||||
"combat_time_reduction_divisor": 100, # 时间缩减除数(用于对数函数)
|
||||
|
||||
# 受伤与治疗
|
||||
"combat_heal_cost": 100, # 治疗费用(积分)
|
||||
}
|
||||
|
||||
# PVP战斗配置
|
||||
COMBAT_PVP_CONFIG = {
|
||||
# 挑战配置
|
||||
"combat_challenge_timeout": 15, # 挑战超时时间(分钟)
|
||||
"combat_pvp_reward": 1000, # PVP胜利奖励(积分)
|
||||
"combat_pvp_penalty": 1000, # PVP失败惩罚(积分)
|
||||
"combat_battle_action_timeout": 5, # 战斗操作超时(分钟)
|
||||
|
||||
# 战斗计算配置
|
||||
"combat_damage_def_ratio": 0.5, # 防御减伤系数
|
||||
"combat_damage_random_min": 0.9, # 伤害随机系数最小值
|
||||
"combat_damage_random_max": 1.1, # 伤害随机系数最大值
|
||||
"combat_block_reduction": 0.5, # 格挡减伤比例(50%)
|
||||
|
||||
# 技能配置
|
||||
"combat_default_attack_power": 1.0, # 普通攻击威力倍率
|
||||
"combat_skill_cooldown_default": 3, # 默认技能冷却回合
|
||||
}
|
||||
|
||||
# 果酒Buff配置
|
||||
COMBAT_WINE_BUFFS_CONFIG = {
|
||||
# 普通草药果酒(RARE)
|
||||
"combat_buff_mint_time_reduction": 0.10, # 薄荷:-10%时间
|
||||
"combat_buff_basil_reward_boost": 0.10, # 罗勒:+10%收益
|
||||
"combat_buff_sage_success_rate": 0.05, # 鼠尾草:+5%成功率
|
||||
"combat_buff_rosemary_atk_boost": 0.10, # 迷迭香:+10% ATK
|
||||
|
||||
# 稀有树木果酒(EPIC)
|
||||
"combat_buff_ginkgo_time_reduction": 0.20, # 银杏:-20%时间
|
||||
"combat_buff_sakura_reward_boost": 0.20, # 樱花:+20%收益
|
||||
"combat_buff_sakura_def_boost": 0.10, # 樱花:+10% DEF
|
||||
"combat_buff_maple_success_rate": 0.10, # 红枫:+10%成功率
|
||||
"combat_buff_maple_crit_boost": 0.15, # 红枫:+15% CRIT
|
||||
}
|
||||
|
||||
# 掉落系统配置
|
||||
COMBAT_LOOT_CONFIG = {
|
||||
# 掉落概率权重
|
||||
"combat_loot_weight_points": 40, # 积分掉落权重
|
||||
"combat_loot_weight_equipment": 20, # 装备掉落权重
|
||||
"combat_loot_weight_material": 25, # 材料掉落权重
|
||||
"combat_loot_weight_souvenir": 5, # 纪念品掉落权重
|
||||
"combat_loot_weight_potion": 8, # 药剂掉落权重
|
||||
"combat_loot_weight_seed": 2, # 种子掉落权重
|
||||
|
||||
# 掉落数量配置
|
||||
"combat_loot_points_base": 100, # 基础积分奖励
|
||||
"combat_loot_points_per_stage": 50, # 每阶段额外积分
|
||||
"combat_loot_fortune_multiplier": 0.5, # 运势影响掉落倍率系数
|
||||
}
|
||||
|
||||
# 合并所有配置
|
||||
COMBAT_CONFIG_ALL = {
|
||||
**COMBAT_CONFIG_DEFAULTS,
|
||||
**COMBAT_ADVENTURE_CONFIG,
|
||||
**COMBAT_PVP_CONFIG,
|
||||
**COMBAT_WINE_BUFFS_CONFIG,
|
||||
**COMBAT_LOOT_CONFIG,
|
||||
}
|
||||
|
||||
# 初始化配置(读取或创建默认值)
|
||||
for key, default_value in COMBAT_CONFIG_ALL.items():
|
||||
COMBAT_CONFIG_ALL[key] = _config.FindItem(key, default_value)
|
||||
_config.SaveProperties()
|
||||
|
||||
|
||||
class CombatConfig:
|
||||
"""配置访问类"""
|
||||
|
||||
@staticmethod
|
||||
def get(key: str, default: Any = None) -> Any:
|
||||
"""获取配置项"""
|
||||
return COMBAT_CONFIG_ALL.get(key, default)
|
||||
|
||||
@staticmethod
|
||||
def get_int(key: str, default: int = 0) -> int:
|
||||
"""获取整数配置"""
|
||||
try:
|
||||
return int(COMBAT_CONFIG_ALL.get(key, default))
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
|
||||
@staticmethod
|
||||
def get_float(key: str, default: float = 0.0) -> float:
|
||||
"""获取浮点数配置"""
|
||||
try:
|
||||
return float(COMBAT_CONFIG_ALL.get(key, default))
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# 数据模型定义
|
||||
# ============================================================================
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class EquipmentDefinition:
|
||||
"""装备定义"""
|
||||
item_id: str
|
||||
name: str
|
||||
tier: BackpackItemTier
|
||||
slot: str # weapon/helmet/armor/boots/accessory/virtual
|
||||
attributes: Dict[str, int] # HP, ATK, DEF, SPD, CRIT, CRIT_DMG
|
||||
skill_ids: List[str] = field(default_factory=list)
|
||||
description: str = ""
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SkillDefinition:
|
||||
"""技能定义(DSL格式)"""
|
||||
skill_id: str
|
||||
name: str
|
||||
description: str
|
||||
effects: List[Dict[str, Any]] # DSL效果列表
|
||||
cooldown: int = 0
|
||||
icon: str = "⚔️"
|
||||
|
||||
|
||||
@dataclass
|
||||
class PlayerStats:
|
||||
"""玩家完整属性(基础+装备)"""
|
||||
user_id: int
|
||||
hp: int
|
||||
atk: int
|
||||
def_: int # defense
|
||||
spd: int
|
||||
crit: int
|
||||
crit_dmg: int
|
||||
equipment_strength: float = 0.0
|
||||
available_skills: List[SkillDefinition] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
class BattleState:
|
||||
"""战斗中的玩家状态"""
|
||||
user_id: int
|
||||
name: str
|
||||
current_hp: int
|
||||
max_hp: int
|
||||
atk: int
|
||||
def_: int
|
||||
spd: int
|
||||
crit: int
|
||||
crit_dmg: int
|
||||
buffs: List[Dict[str, Any]] = field(default_factory=list) # [{stat: "ATK", value: 0.2, remaining: 2}]
|
||||
skill_cooldowns: Dict[str, int] = field(default_factory=dict) # {skill_id: remaining_turns}
|
||||
available_skills: List[SkillDefinition] = field(default_factory=list)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""序列化为字典(用于存储到数据库)"""
|
||||
return {
|
||||
"user_id": self.user_id,
|
||||
"name": self.name,
|
||||
"current_hp": self.current_hp,
|
||||
"max_hp": self.max_hp,
|
||||
"atk": self.atk,
|
||||
"def_": self.def_,
|
||||
"spd": self.spd,
|
||||
"crit": self.crit,
|
||||
"crit_dmg": self.crit_dmg,
|
||||
"buffs": self.buffs,
|
||||
"skill_cooldowns": self.skill_cooldowns,
|
||||
"available_skills": [s.skill_id for s in self.available_skills]
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def from_dict(data: Dict[str, Any], skill_registry: Dict[str, SkillDefinition]) -> "BattleState":
|
||||
"""从字典反序列化"""
|
||||
skills = [skill_registry[sid] for sid in data.get("available_skills", []) if sid in skill_registry]
|
||||
return BattleState(
|
||||
user_id=data["user_id"],
|
||||
name=data["name"],
|
||||
current_hp=data["current_hp"],
|
||||
max_hp=data["max_hp"],
|
||||
atk=data["atk"],
|
||||
def_=data["def_"],
|
||||
spd=data["spd"],
|
||||
crit=data["crit"],
|
||||
crit_dmg=data["crit_dmg"],
|
||||
buffs=data.get("buffs", []),
|
||||
skill_cooldowns=data.get("skill_cooldowns", {}),
|
||||
available_skills=skills
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# 预定义内容
|
||||
# ============================================================================
|
||||
|
||||
# 技能注册表
|
||||
SKILL_REGISTRY: Dict[str, SkillDefinition] = {
|
||||
# 默认技能
|
||||
"skill_basic_attack": SkillDefinition(
|
||||
skill_id="skill_basic_attack",
|
||||
name="普通攻击",
|
||||
description="基础攻击,造成100%攻击力的伤害",
|
||||
effects=[
|
||||
{"type": "damage", "power": 1.0, "can_crit": True}
|
||||
],
|
||||
cooldown=0,
|
||||
icon="⚔️"
|
||||
),
|
||||
|
||||
"skill_block": SkillDefinition(
|
||||
skill_id="skill_block",
|
||||
name="格挡",
|
||||
description="防御姿态,本回合受到伤害减少50%",
|
||||
effects=[
|
||||
{"type": "buff", "target": "self", "stat": "block", "value": 0.5, "duration": 1}
|
||||
],
|
||||
cooldown=2,
|
||||
icon="🛡️"
|
||||
),
|
||||
|
||||
# 装备技能
|
||||
"skill_power_strike": SkillDefinition(
|
||||
skill_id="skill_power_strike",
|
||||
name="力劈",
|
||||
description="强力一击,造成130%伤害",
|
||||
effects=[
|
||||
{"type": "damage", "power": 1.3, "can_crit": True}
|
||||
],
|
||||
cooldown=2,
|
||||
icon="💥"
|
||||
),
|
||||
|
||||
"skill_heavy_strike": SkillDefinition(
|
||||
skill_id="skill_heavy_strike",
|
||||
name="重击",
|
||||
description="强力攻击,造成150%伤害,但降低自身10%防御2回合",
|
||||
effects=[
|
||||
{"type": "damage", "power": 1.5, "can_crit": True},
|
||||
{"type": "buff", "target": "self", "stat": "DEF", "value": -0.1, "duration": 2}
|
||||
],
|
||||
cooldown=3,
|
||||
icon="⚡"
|
||||
),
|
||||
|
||||
"skill_devastating_blow": SkillDefinition(
|
||||
skill_id="skill_devastating_blow",
|
||||
name="毁灭打击",
|
||||
description="传说技能,造成200%伤害,必定暴击",
|
||||
effects=[
|
||||
{"type": "damage", "power": 2.0, "can_crit": True, "force_crit": True}
|
||||
],
|
||||
cooldown=5,
|
||||
icon="💀"
|
||||
),
|
||||
|
||||
"skill_magic_bolt": SkillDefinition(
|
||||
skill_id="skill_magic_bolt",
|
||||
name="魔法箭",
|
||||
description="魔法攻击,造成120%伤害,无视20%防御",
|
||||
effects=[
|
||||
{"type": "damage", "power": 1.2, "can_crit": True, "ignore_def": 0.2}
|
||||
],
|
||||
cooldown=2,
|
||||
icon="✨"
|
||||
),
|
||||
|
||||
"skill_iron_wall": SkillDefinition(
|
||||
skill_id="skill_iron_wall",
|
||||
name="铁壁",
|
||||
description="防御姿态,3回合内防御提升30%",
|
||||
effects=[
|
||||
{"type": "buff", "target": "self", "stat": "DEF", "value": 0.3, "duration": 3}
|
||||
],
|
||||
cooldown=4,
|
||||
icon="🏰"
|
||||
),
|
||||
|
||||
"skill_swift_move": SkillDefinition(
|
||||
skill_id="skill_swift_move",
|
||||
name="疾风步",
|
||||
description="快速移动,本回合必定先手,2回合内速度提升20%",
|
||||
effects=[
|
||||
{"type": "special", "effect": "priority"},
|
||||
{"type": "buff", "target": "self", "stat": "SPD", "value": 0.2, "duration": 2}
|
||||
],
|
||||
cooldown=4,
|
||||
icon="💨"
|
||||
),
|
||||
|
||||
"skill_dragon_roar": SkillDefinition(
|
||||
skill_id="skill_dragon_roar",
|
||||
name="龙吼",
|
||||
description="发出龙之咆哮,3回合内攻击和防御各提升15%",
|
||||
effects=[
|
||||
{"type": "buff", "target": "self", "stat": "ATK", "value": 0.15, "duration": 3},
|
||||
{"type": "buff", "target": "self", "stat": "DEF", "value": 0.15, "duration": 3}
|
||||
],
|
||||
cooldown=5,
|
||||
icon="🐉"
|
||||
),
|
||||
|
||||
"skill_protect": SkillDefinition(
|
||||
skill_id="skill_protect",
|
||||
name="守护",
|
||||
description="护符之力,回复30HP并提升10%防御2回合",
|
||||
effects=[
|
||||
{"type": "heal", "value": 30},
|
||||
{"type": "buff", "target": "self", "stat": "DEF", "value": 0.1, "duration": 2}
|
||||
],
|
||||
cooldown=3,
|
||||
icon="✝️"
|
||||
),
|
||||
}
|
||||
|
||||
# 虚拟装备(提供默认技能)
|
||||
VIRTUAL_EQUIPMENT: Dict[str, EquipmentDefinition] = {
|
||||
"virtual_default_skills": EquipmentDefinition(
|
||||
item_id="virtual_default_skills",
|
||||
name="基础战斗技能",
|
||||
tier=BackpackItemTier.COMMON,
|
||||
slot="virtual",
|
||||
attributes={},
|
||||
skill_ids=["skill_basic_attack", "skill_block"],
|
||||
description="所有玩家的默认战斗技能"
|
||||
)
|
||||
}
|
||||
|
||||
# 装备注册表
|
||||
EQUIPMENT_REGISTRY: Dict[str, EquipmentDefinition] = {
|
||||
# ===== 武器 =====
|
||||
"combat_weapon_wood_sword": EquipmentDefinition(
|
||||
item_id="combat_weapon_wood_sword",
|
||||
name="木剑",
|
||||
tier=BackpackItemTier.COMMON,
|
||||
slot="weapon",
|
||||
attributes={"ATK": 15},
|
||||
skill_ids=[],
|
||||
description="最基础的木制武器"
|
||||
),
|
||||
|
||||
"combat_weapon_iron_sword": EquipmentDefinition(
|
||||
item_id="combat_weapon_iron_sword",
|
||||
name="铁剑",
|
||||
tier=BackpackItemTier.RARE,
|
||||
slot="weapon",
|
||||
attributes={"ATK": 35},
|
||||
skill_ids=["skill_power_strike"],
|
||||
description="坚固的铁制剑,附带力劈技能"
|
||||
),
|
||||
|
||||
"combat_weapon_steel_sword": EquipmentDefinition(
|
||||
item_id="combat_weapon_steel_sword",
|
||||
name="钢剑",
|
||||
tier=BackpackItemTier.EPIC,
|
||||
slot="weapon",
|
||||
attributes={"ATK": 60, "CRIT": 5},
|
||||
skill_ids=["skill_heavy_strike"],
|
||||
description="精钢打造,锋利无比"
|
||||
),
|
||||
|
||||
"combat_weapon_legend_sword": EquipmentDefinition(
|
||||
item_id="combat_weapon_legend_sword",
|
||||
name="传说之剑",
|
||||
tier=BackpackItemTier.LEGENDARY,
|
||||
slot="weapon",
|
||||
attributes={"ATK": 100, "CRIT": 10, "CRIT_DMG": 50},
|
||||
skill_ids=["skill_devastating_blow"],
|
||||
description="传说中的神兵利器"
|
||||
),
|
||||
|
||||
"combat_weapon_magic_staff": EquipmentDefinition(
|
||||
item_id="combat_weapon_magic_staff",
|
||||
name="魔法杖",
|
||||
tier=BackpackItemTier.EPIC,
|
||||
slot="weapon",
|
||||
attributes={"ATK": 50, "SPD": 10},
|
||||
skill_ids=["skill_magic_bolt"],
|
||||
description="蕴含魔力的法杖"
|
||||
),
|
||||
|
||||
# ===== 头盔 =====
|
||||
"combat_helmet_leather": EquipmentDefinition(
|
||||
item_id="combat_helmet_leather",
|
||||
name="皮帽",
|
||||
tier=BackpackItemTier.COMMON,
|
||||
slot="helmet",
|
||||
attributes={"HP": 20, "DEF": 5},
|
||||
skill_ids=[],
|
||||
description="简单的皮革头饰"
|
||||
),
|
||||
|
||||
"combat_helmet_iron": EquipmentDefinition(
|
||||
item_id="combat_helmet_iron",
|
||||
name="铁盔",
|
||||
tier=BackpackItemTier.RARE,
|
||||
slot="helmet",
|
||||
attributes={"HP": 50, "DEF": 15},
|
||||
skill_ids=[],
|
||||
description="厚重的铁制头盔"
|
||||
),
|
||||
|
||||
"combat_helmet_dragon": EquipmentDefinition(
|
||||
item_id="combat_helmet_dragon",
|
||||
name="龙鳞头盔",
|
||||
tier=BackpackItemTier.LEGENDARY,
|
||||
slot="helmet",
|
||||
attributes={"HP": 120, "DEF": 40, "ATK": 20},
|
||||
skill_ids=["skill_dragon_roar"],
|
||||
description="龙鳞打造的传奇头盔"
|
||||
),
|
||||
|
||||
# ===== 护甲 =====
|
||||
"combat_armor_cloth": EquipmentDefinition(
|
||||
item_id="combat_armor_cloth",
|
||||
name="布衣",
|
||||
tier=BackpackItemTier.COMMON,
|
||||
slot="armor",
|
||||
attributes={"HP": 30, "DEF": 8},
|
||||
skill_ids=[],
|
||||
description="简单的布质防具"
|
||||
),
|
||||
|
||||
"combat_armor_chain": EquipmentDefinition(
|
||||
item_id="combat_armor_chain",
|
||||
name="锁子甲",
|
||||
tier=BackpackItemTier.RARE,
|
||||
slot="armor",
|
||||
attributes={"HP": 70, "DEF": 25},
|
||||
skill_ids=[],
|
||||
description="环环相扣的金属铠甲"
|
||||
),
|
||||
|
||||
"combat_armor_plate": EquipmentDefinition(
|
||||
item_id="combat_armor_plate",
|
||||
name="板甲",
|
||||
tier=BackpackItemTier.EPIC,
|
||||
slot="armor",
|
||||
attributes={"HP": 120, "DEF": 50},
|
||||
skill_ids=["skill_iron_wall"],
|
||||
description="厚重的全身板甲"
|
||||
),
|
||||
|
||||
# ===== 鞋子 =====
|
||||
"combat_boots_leather": EquipmentDefinition(
|
||||
item_id="combat_boots_leather",
|
||||
name="皮靴",
|
||||
tier=BackpackItemTier.COMMON,
|
||||
slot="boots",
|
||||
attributes={"SPD": 5},
|
||||
skill_ids=[],
|
||||
description="轻便的皮制靴子"
|
||||
),
|
||||
|
||||
"combat_boots_wind": EquipmentDefinition(
|
||||
item_id="combat_boots_wind",
|
||||
name="疾风之靴",
|
||||
tier=BackpackItemTier.EPIC,
|
||||
slot="boots",
|
||||
attributes={"SPD": 20, "DEF": 10},
|
||||
skill_ids=["skill_swift_move"],
|
||||
description="蕴含风之力的靴子"
|
||||
),
|
||||
|
||||
# ===== 饰品 =====
|
||||
"combat_accessory_ring_str": EquipmentDefinition(
|
||||
item_id="combat_accessory_ring_str",
|
||||
name="力量戒指",
|
||||
tier=BackpackItemTier.RARE,
|
||||
slot="accessory",
|
||||
attributes={"ATK": 20, "HP": 30},
|
||||
skill_ids=[],
|
||||
description="增强力量的魔法戒指"
|
||||
),
|
||||
|
||||
"combat_accessory_amulet": EquipmentDefinition(
|
||||
item_id="combat_accessory_amulet",
|
||||
name="守护护符",
|
||||
tier=BackpackItemTier.EPIC,
|
||||
slot="accessory",
|
||||
attributes={"HP": 50, "DEF": 20, "CRIT": 5},
|
||||
skill_ids=["skill_protect"],
|
||||
description="守护佩戴者的神秘护符"
|
||||
),
|
||||
}
|
||||
|
||||
# 果酒buff映射
|
||||
WINE_BUFFS: Dict[str, Dict[str, float]] = {
|
||||
# 普通草药果酒
|
||||
"garden_wine_mint": {
|
||||
"time_reduction": CombatConfig.get_float("combat_buff_mint_time_reduction", 0.10),
|
||||
},
|
||||
"garden_wine_basil": {
|
||||
"reward_boost": CombatConfig.get_float("combat_buff_basil_reward_boost", 0.10),
|
||||
},
|
||||
"garden_wine_sage": {
|
||||
"success_rate": CombatConfig.get_float("combat_buff_sage_success_rate", 0.05),
|
||||
},
|
||||
"garden_wine_rosemary": {
|
||||
"atk_boost": CombatConfig.get_float("combat_buff_rosemary_atk_boost", 0.10),
|
||||
},
|
||||
|
||||
# 稀有树木果酒
|
||||
"garden_wine_ginkgo": {
|
||||
"time_reduction": CombatConfig.get_float("combat_buff_ginkgo_time_reduction", 0.20),
|
||||
},
|
||||
"garden_wine_sakura": {
|
||||
"reward_boost": CombatConfig.get_float("combat_buff_sakura_reward_boost", 0.20),
|
||||
"def_boost": CombatConfig.get_float("combat_buff_sakura_def_boost", 0.10),
|
||||
},
|
||||
"garden_wine_maple": {
|
||||
"success_rate": CombatConfig.get_float("combat_buff_maple_success_rate", 0.10),
|
||||
"crit_boost": CombatConfig.get_float("combat_buff_maple_crit_boost", 0.15),
|
||||
},
|
||||
}
|
||||
|
||||
# 冒险材料(item_id -> (name, tier))
|
||||
ADVENTURE_MATERIALS: Dict[str, Tuple[str, BackpackItemTier]] = {
|
||||
"combat_material_ore": ("矿石", BackpackItemTier.COMMON),
|
||||
"combat_material_gem": ("宝石", BackpackItemTier.RARE),
|
||||
"combat_material_crystal": ("水晶", BackpackItemTier.EPIC),
|
||||
"combat_material_essence": ("精华", BackpackItemTier.LEGENDARY),
|
||||
}
|
||||
|
||||
# 纪念品(item_id -> (name, tier, sell_price))
|
||||
ADVENTURE_SOUVENIRS: Dict[str, Tuple[str, BackpackItemTier, int]] = {
|
||||
"combat_souvenir_medal": ("英雄勋章", BackpackItemTier.RARE, 500),
|
||||
"combat_souvenir_trophy": ("战斗奖杯", BackpackItemTier.EPIC, 1500),
|
||||
"combat_souvenir_relic": ("远古遗物", BackpackItemTier.LEGENDARY, 5000),
|
||||
}
|
||||
|
||||
# 药剂(item_id -> (name, tier, description))
|
||||
COMBAT_POTIONS: Dict[str, Tuple[str, BackpackItemTier, str]] = {
|
||||
"combat_potion_hp_small": ("小型治疗药水", BackpackItemTier.COMMON, "回复50HP"),
|
||||
"combat_potion_hp_medium": ("中型治疗药水", BackpackItemTier.RARE, "回复150HP"),
|
||||
"combat_potion_hp_large": ("大型治疗药水", BackpackItemTier.EPIC, "回复全部HP"),
|
||||
"combat_potion_atk": ("力量药水", BackpackItemTier.RARE, "3回合ATK+20%"),
|
||||
"combat_potion_def": ("防御药水", BackpackItemTier.RARE, "3回合DEF+20%"),
|
||||
}
|
||||
|
||||
# 冒险独有种子(item_id -> (name, tier))
|
||||
ADVENTURE_SEEDS: Dict[str, Tuple[str, BackpackItemTier]] = {
|
||||
"combat_seed_battle_flower": ("战斗之花种子", BackpackItemTier.EPIC),
|
||||
"combat_seed_victory_tree": ("胜利之树种子", BackpackItemTier.LEGENDARY),
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# 数据库模型
|
||||
# ============================================================================
|
||||
|
||||
def get_combat_db_models() -> List[DatabaseModel]:
|
||||
"""返回战斗系统所需的数据库表定义"""
|
||||
return [
|
||||
# 玩家状态表
|
||||
DatabaseModel(
|
||||
table_name="combat_player_status",
|
||||
column_defs={
|
||||
"user_id": "INTEGER PRIMARY KEY",
|
||||
"base_hp": "INTEGER NOT NULL DEFAULT 100",
|
||||
"base_atk": "INTEGER NOT NULL DEFAULT 10",
|
||||
"base_def": "INTEGER NOT NULL DEFAULT 5",
|
||||
"base_spd": "INTEGER NOT NULL DEFAULT 10",
|
||||
"base_crit": "INTEGER NOT NULL DEFAULT 5",
|
||||
"base_crit_dmg": "INTEGER NOT NULL DEFAULT 150",
|
||||
"equipped_weapon": "TEXT",
|
||||
"equipped_helmet": "TEXT",
|
||||
"equipped_armor": "TEXT",
|
||||
"equipped_boots": "TEXT",
|
||||
"equipped_accessory": "TEXT",
|
||||
"is_injured": "INTEGER NOT NULL DEFAULT 0",
|
||||
"current_adventure_id": "INTEGER DEFAULT NULL",
|
||||
"current_battle_id": "INTEGER DEFAULT NULL",
|
||||
},
|
||||
),
|
||||
|
||||
# 装备实例表
|
||||
DatabaseModel(
|
||||
table_name="combat_equipment_instances",
|
||||
column_defs={
|
||||
"instance_id": "INTEGER PRIMARY KEY AUTOINCREMENT",
|
||||
"item_id": "TEXT NOT NULL",
|
||||
"owner_id": "INTEGER NOT NULL",
|
||||
"custom_attributes": "TEXT",
|
||||
"modifications": "TEXT",
|
||||
"created_at": "TEXT NOT NULL",
|
||||
},
|
||||
),
|
||||
|
||||
# 冒险记录表
|
||||
DatabaseModel(
|
||||
table_name="combat_adventure_records",
|
||||
column_defs={
|
||||
"adventure_id": "INTEGER PRIMARY KEY AUTOINCREMENT",
|
||||
"user_id": "INTEGER NOT NULL",
|
||||
"chat_id": "INTEGER NOT NULL",
|
||||
"stage": "INTEGER NOT NULL",
|
||||
"equipment_snapshot": "TEXT NOT NULL",
|
||||
"foods_used": "TEXT NOT NULL",
|
||||
"start_time": "TEXT NOT NULL",
|
||||
"expected_end_time": "TEXT NOT NULL",
|
||||
"status": "TEXT NOT NULL",
|
||||
"rewards": "TEXT",
|
||||
"fortune_value": "REAL",
|
||||
"equipment_strength": "REAL",
|
||||
"success_rate": "REAL",
|
||||
"scheduled_task_id": "INTEGER",
|
||||
},
|
||||
),
|
||||
|
||||
# PVP挑战表
|
||||
DatabaseModel(
|
||||
table_name="combat_pvp_challenges",
|
||||
column_defs={
|
||||
"challenge_id": "INTEGER PRIMARY KEY AUTOINCREMENT",
|
||||
"challenger_id": "INTEGER NOT NULL",
|
||||
"target_id": "INTEGER NOT NULL",
|
||||
"chat_id": "INTEGER NOT NULL",
|
||||
"status": "TEXT NOT NULL",
|
||||
"created_at": "TEXT NOT NULL",
|
||||
"expires_at": "TEXT NOT NULL",
|
||||
},
|
||||
),
|
||||
|
||||
# PVP战斗表
|
||||
DatabaseModel(
|
||||
table_name="combat_pvp_battles",
|
||||
column_defs={
|
||||
"battle_id": "INTEGER PRIMARY KEY AUTOINCREMENT",
|
||||
"challenge_id": "INTEGER NOT NULL",
|
||||
"player1_id": "INTEGER NOT NULL",
|
||||
"player2_id": "INTEGER NOT NULL",
|
||||
"chat_id": "INTEGER NOT NULL",
|
||||
"current_turn_player": "INTEGER NOT NULL",
|
||||
"turn_number": "INTEGER NOT NULL DEFAULT 1",
|
||||
"player1_state": "TEXT NOT NULL",
|
||||
"player2_state": "TEXT NOT NULL",
|
||||
"battle_log": "TEXT NOT NULL",
|
||||
"last_action_time": "TEXT NOT NULL",
|
||||
"action_timeout_minutes": "INTEGER NOT NULL DEFAULT 5",
|
||||
"winner_id": "INTEGER",
|
||||
"status": "TEXT NOT NULL",
|
||||
"created_at": "TEXT NOT NULL",
|
||||
"finished_at": "TEXT",
|
||||
},
|
||||
),
|
||||
]
|
||||
178
Plugins/WPSCombatSystem/combat_plugin_adventure.py
Normal file
178
Plugins/WPSCombatSystem/combat_plugin_adventure.py
Normal file
@@ -0,0 +1,178 @@
|
||||
"""冒险系统插件 - PVE冒险模式"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from PWF.Convention.Runtime.GlobalConfig import ConsoleFrontColor, ProjectConfig
|
||||
|
||||
from .combat_plugin_base import WPSCombatBase
|
||||
|
||||
|
||||
logger: ProjectConfig = ProjectConfig()
|
||||
|
||||
|
||||
class WPSCombatAdventure(WPSCombatBase):
|
||||
"""冒险系统插件"""
|
||||
|
||||
def is_enable_plugin(self) -> bool:
|
||||
return True
|
||||
|
||||
def wake_up(self) -> None:
|
||||
super().wake_up()
|
||||
logger.Log(
|
||||
"Info",
|
||||
f"{ConsoleFrontColor.GREEN}WPSCombatAdventure 插件已加载{ConsoleFrontColor.RESET}"
|
||||
)
|
||||
self.register_plugin("冒险")
|
||||
self.register_plugin("adventure")
|
||||
self.register_plugin("继续冒险")
|
||||
|
||||
# 恢复过期冒险
|
||||
service = self.service()
|
||||
service.recover_overdue_adventures()
|
||||
|
||||
async def callback(self, message: str, chat_id: int, user_id: int) -> Optional[str]:
|
||||
"""
|
||||
处理冒险命令
|
||||
|
||||
命令格式:
|
||||
- 冒险 开始 [食物1] [食物2] ...
|
||||
- 继续冒险 [食物1] [食物2] ...
|
||||
"""
|
||||
message = self.parse_message_after_at(message).strip()
|
||||
|
||||
tokens = message.split()
|
||||
|
||||
if not tokens:
|
||||
# 默认视为继续冒险,支持直接命令 `继续冒险`
|
||||
return await self._handle_continue_adventure(chat_id, user_id, [])
|
||||
|
||||
# 判断是开始新冒险还是继续
|
||||
command = tokens[0].lower()
|
||||
|
||||
if command in ["开始", "start"]:
|
||||
# 开始新冒险(第1阶段)
|
||||
food_items = tokens[1:] if len(tokens) > 1 else []
|
||||
return await self._handle_start_adventure(chat_id, user_id, food_items)
|
||||
elif command in ["继续", "continue"]:
|
||||
food_items = tokens[1:] if len(tokens) > 1 else []
|
||||
return await self._handle_continue_adventure(chat_id, user_id, food_items)
|
||||
else:
|
||||
# 默认视为继续冒险,tokens 即为食物列表
|
||||
food_items = tokens
|
||||
return await self._handle_continue_adventure(chat_id, user_id, food_items)
|
||||
|
||||
async def _handle_start_adventure(
|
||||
self,
|
||||
chat_id: int,
|
||||
user_id: int,
|
||||
food_items: list
|
||||
) -> Optional[str]:
|
||||
"""处理开始新冒险"""
|
||||
service = self.service()
|
||||
|
||||
# 第1阶段
|
||||
stage = 1
|
||||
|
||||
success, msg, adventure_id = service.start_adventure(
|
||||
user_id=user_id,
|
||||
chat_id=chat_id,
|
||||
stage=stage,
|
||||
food_items=food_items,
|
||||
register_callback=self
|
||||
)
|
||||
|
||||
return await self.send_markdown_message(msg, chat_id, user_id)
|
||||
|
||||
async def _handle_continue_adventure(
|
||||
self,
|
||||
chat_id: int,
|
||||
user_id: int,
|
||||
food_items: list
|
||||
) -> Optional[str]:
|
||||
"""处理继续冒险"""
|
||||
service = self.service()
|
||||
|
||||
# 获取当前冒险状态
|
||||
status = service.get_player_status(user_id)
|
||||
current_adventure_id = status.get("current_adventure_id")
|
||||
|
||||
if current_adventure_id:
|
||||
return await self.send_markdown_message(
|
||||
"❌ 你已经在冒险中,请等待当前冒险完成",
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
# 查找最近的成功冒险
|
||||
from PWF.CoreModules.database import get_db
|
||||
db = get_db()
|
||||
cursor = db.conn.cursor()
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT stage FROM combat_adventure_records
|
||||
WHERE user_id = ? AND status = 'success'
|
||||
ORDER BY adventure_id DESC
|
||||
LIMIT 1
|
||||
""",
|
||||
(user_id,)
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
|
||||
if not row:
|
||||
return await self.send_markdown_message(
|
||||
"❌ 你还没有完成任何冒险,请使用 `冒险 开始 [食物...]` 开始第1阶段",
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
# 下一阶段
|
||||
next_stage = row["stage"] + 1
|
||||
|
||||
success, msg, adventure_id = service.start_adventure(
|
||||
user_id=user_id,
|
||||
chat_id=chat_id,
|
||||
stage=next_stage,
|
||||
food_items=food_items,
|
||||
register_callback=self
|
||||
)
|
||||
|
||||
return await self.send_markdown_message(msg, chat_id, user_id)
|
||||
|
||||
async def _settle_adventure_callback(
|
||||
self,
|
||||
adventure_id: int,
|
||||
user_id: int,
|
||||
chat_id: int
|
||||
) -> None:
|
||||
"""冒险结算回调(时钟任务)"""
|
||||
service = self.service()
|
||||
success, msg, rewards = service.settle_adventure(adventure_id)
|
||||
|
||||
# 发送结算消息
|
||||
await self.send_markdown_message(msg, chat_id, user_id)
|
||||
|
||||
def _help_message(self) -> str:
|
||||
"""帮助信息"""
|
||||
return """# 🗺️ 冒险系统
|
||||
**命令格式:**
|
||||
- `冒险 开始 [食物1] [食物2] ...`:开始第1阶段冒险
|
||||
- `继续冒险 [食物1] [食物2] ...`:继续下一阶段
|
||||
|
||||
**说明:**
|
||||
- 每个阶段耗时翻倍(15min → 30min → 60min...)
|
||||
- 食物(果酒)是可选的,可提供buff加成(时间缩减、收益提升等)
|
||||
- 不提供食物也可以冒险,只是没有buff加成
|
||||
- 成功率受装备强度和运势影响
|
||||
- 失败会受伤,需消耗100积分治疗
|
||||
- 成功后可选择继续或停止
|
||||
|
||||
**示例:**
|
||||
- `冒险 开始`:不使用食物开始第1阶段
|
||||
- `冒险 开始 薄荷果酒`:使用1个薄荷果酒(时间缩减10%)
|
||||
- `继续冒险 银杏果酒 银杏果酒`:使用2个银杏果酒(时间缩减20%)
|
||||
"""
|
||||
|
||||
|
||||
__all__ = ["WPSCombatAdventure"]
|
||||
186
Plugins/WPSCombatSystem/combat_plugin_base.py
Normal file
186
Plugins/WPSCombatSystem/combat_plugin_base.py
Normal file
@@ -0,0 +1,186 @@
|
||||
"""战斗系统基础插件类"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Type
|
||||
|
||||
from PWF.Convention.Runtime.Architecture import Architecture
|
||||
from PWF.Convention.Runtime.GlobalConfig import ConsoleFrontColor, ProjectConfig
|
||||
from PWF.CoreModules.plugin_interface import DatabaseModel
|
||||
|
||||
from Plugins.WPSAPI import WPSAPI
|
||||
from Plugins.WPSBackpackSystem import BackpackItemTier, WPSBackpackSystem
|
||||
from Plugins.WPSStoreSystem import WPSStoreSystem
|
||||
from Plugins.WPSConfigSystem import WPSConfigAPI
|
||||
from Plugins.WPSFortuneSystem import WPSFortuneSystem
|
||||
|
||||
from .combat_models import (
|
||||
ADVENTURE_MATERIALS,
|
||||
ADVENTURE_SEEDS,
|
||||
ADVENTURE_SOUVENIRS,
|
||||
COMBAT_POTIONS,
|
||||
EQUIPMENT_REGISTRY,
|
||||
get_combat_db_models,
|
||||
)
|
||||
from .combat_service import CombatService, get_combat_service
|
||||
|
||||
|
||||
logger: ProjectConfig = Architecture.Get(ProjectConfig)
|
||||
|
||||
|
||||
class WPSCombatBase(WPSAPI):
|
||||
"""战斗系统基础插件类"""
|
||||
|
||||
_service: CombatService | None = None
|
||||
_initialized: bool = False
|
||||
|
||||
@classmethod
|
||||
def service(cls) -> CombatService:
|
||||
"""获取共享的战斗服务实例"""
|
||||
if cls._service is None:
|
||||
cls._service = get_combat_service()
|
||||
return cls._service
|
||||
|
||||
def dependencies(self) -> List[Type]:
|
||||
"""声明依赖的插件"""
|
||||
return [
|
||||
WPSAPI,
|
||||
WPSConfigAPI,
|
||||
WPSBackpackSystem,
|
||||
WPSStoreSystem,
|
||||
WPSFortuneSystem,
|
||||
# 注:不强制依赖 WPSGardenSystem,果酒buff配置在 combat_models.py 中
|
||||
]
|
||||
|
||||
def register_db_model(self) -> List[DatabaseModel]:
|
||||
"""注册数据库表"""
|
||||
return get_combat_db_models()
|
||||
|
||||
def wake_up(self) -> None:
|
||||
"""插件初始化(只执行一次)"""
|
||||
if WPSCombatBase._initialized:
|
||||
return
|
||||
WPSCombatBase._initialized = True
|
||||
|
||||
logger.Log(
|
||||
"Info",
|
||||
f"{ConsoleFrontColor.GREEN}WPSCombat 系统开始初始化{ConsoleFrontColor.RESET}"
|
||||
)
|
||||
|
||||
backpack: WPSBackpackSystem = Architecture.Get(WPSBackpackSystem)
|
||||
store: WPSStoreSystem = Architecture.Get(WPSStoreSystem)
|
||||
|
||||
# 1. 注册所有装备
|
||||
for equipment in EQUIPMENT_REGISTRY.values():
|
||||
self._safe_register_item(backpack, equipment.item_id, equipment.name, equipment.tier)
|
||||
# 装备价格根据品质和属性计算
|
||||
price = self._calculate_equipment_price(equipment)
|
||||
self._safe_register_store(store, equipment.item_id, price, limit=3)
|
||||
|
||||
# 2. 注册材料
|
||||
for item_id, (name, tier) in ADVENTURE_MATERIALS.items():
|
||||
self._safe_register_item(backpack, item_id, name, tier)
|
||||
# 材料可以在商店出售(但不购买)
|
||||
|
||||
# 3. 注册纪念品
|
||||
for item_id, (name, tier, sell_price) in ADVENTURE_SOUVENIRS.items():
|
||||
self._safe_register_item(backpack, item_id, name, tier)
|
||||
# 纪念品只能出售
|
||||
|
||||
# 4. 注册药剂
|
||||
for item_id, (name, tier, desc) in COMBAT_POTIONS.items():
|
||||
self._safe_register_item(backpack, item_id, name, tier)
|
||||
# 药剂价格根据品质
|
||||
potion_prices = {
|
||||
BackpackItemTier.COMMON: 50,
|
||||
BackpackItemTier.RARE: 150,
|
||||
BackpackItemTier.EPIC: 500,
|
||||
}
|
||||
price = potion_prices.get(tier, 100)
|
||||
self._safe_register_store(store, item_id, price, limit=10)
|
||||
|
||||
# 5. 注册冒险种子
|
||||
for item_id, (name, tier) in ADVENTURE_SEEDS.items():
|
||||
self._safe_register_item(backpack, item_id, name, tier)
|
||||
# 种子只能通过冒险获得
|
||||
|
||||
# 6. 恢复过期任务和超时战斗
|
||||
try:
|
||||
service = self.service()
|
||||
service.recover_overdue_adventures()
|
||||
service.check_battle_timeout()
|
||||
except Exception as e:
|
||||
logger.Log(
|
||||
"Warning",
|
||||
f"{ConsoleFrontColor.YELLOW}恢复任务时出错: {e}{ConsoleFrontColor.RESET}"
|
||||
)
|
||||
|
||||
logger.Log(
|
||||
"Info",
|
||||
f"{ConsoleFrontColor.GREEN}WPSCombat 系统初始化完成:{len(EQUIPMENT_REGISTRY)}件装备、"
|
||||
f"{len(COMBAT_POTIONS)}种药剂已注册{ConsoleFrontColor.RESET}"
|
||||
)
|
||||
|
||||
# ========================================================================
|
||||
# 辅助方法
|
||||
# ========================================================================
|
||||
|
||||
def _safe_register_item(
|
||||
self,
|
||||
backpack: WPSBackpackSystem,
|
||||
item_id: str,
|
||||
name: str,
|
||||
tier: BackpackItemTier,
|
||||
) -> None:
|
||||
"""安全注册物品到背包系统"""
|
||||
try:
|
||||
backpack.register_item(item_id, name, tier)
|
||||
except Exception as e:
|
||||
logger.Log(
|
||||
"Warning",
|
||||
f"{ConsoleFrontColor.YELLOW}注册物品 {item_id} 时出错: {e}{ConsoleFrontColor.RESET}"
|
||||
)
|
||||
|
||||
def _safe_register_store(
|
||||
self,
|
||||
store: WPSStoreSystem,
|
||||
item_id: str,
|
||||
price: int,
|
||||
*,
|
||||
limit: int = 5,
|
||||
) -> None:
|
||||
"""安全注册物品到商店系统"""
|
||||
try:
|
||||
store.register_mode(
|
||||
item_id=item_id,
|
||||
price=price,
|
||||
limit_amount=limit,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.Log(
|
||||
"Warning",
|
||||
f"{ConsoleFrontColor.YELLOW}注册商店物品 {item_id} 时出错: {e}{ConsoleFrontColor.RESET}"
|
||||
)
|
||||
|
||||
def _calculate_equipment_price(self, equipment) -> int:
|
||||
"""根据装备品质和属性计算价格"""
|
||||
# 基础价格
|
||||
base_prices = {
|
||||
BackpackItemTier.COMMON: 100,
|
||||
BackpackItemTier.RARE: 500,
|
||||
BackpackItemTier.EPIC: 2000,
|
||||
BackpackItemTier.LEGENDARY: 10000,
|
||||
}
|
||||
base_price = base_prices.get(equipment.tier, 100)
|
||||
|
||||
# 属性加成
|
||||
attr_sum = sum(equipment.attributes.values())
|
||||
price = base_price + attr_sum * 5
|
||||
|
||||
# 技能加成
|
||||
skill_bonus = len(equipment.skill_ids) * 200
|
||||
|
||||
return price + skill_bonus
|
||||
|
||||
|
||||
__all__ = ["WPSCombatBase"]
|
||||
251
Plugins/WPSCombatSystem/combat_plugin_battle.py
Normal file
251
Plugins/WPSCombatSystem/combat_plugin_battle.py
Normal file
@@ -0,0 +1,251 @@
|
||||
"""PVP对战插件 - 回合制战斗"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from PWF.Convention.Runtime.GlobalConfig import ConsoleFrontColor, ProjectConfig
|
||||
|
||||
from .combat_plugin_base import WPSCombatBase
|
||||
|
||||
|
||||
logger: ProjectConfig = ProjectConfig()
|
||||
|
||||
|
||||
class WPSCombatBattle(WPSCombatBase):
|
||||
"""PVP对战插件"""
|
||||
|
||||
def is_enable_plugin(self) -> bool:
|
||||
return True
|
||||
|
||||
def wake_up(self) -> None:
|
||||
super().wake_up()
|
||||
logger.Log(
|
||||
"Info",
|
||||
f"{ConsoleFrontColor.GREEN}WPSCombatBattle 插件已加载{ConsoleFrontColor.RESET}"
|
||||
)
|
||||
self.register_plugin("挑战")
|
||||
self.register_plugin("接受挑战")
|
||||
self.register_plugin("拒绝挑战")
|
||||
self.register_plugin("战斗")
|
||||
self.register_plugin("battle")
|
||||
self.register_plugin("投降")
|
||||
|
||||
# 启动超时检查(定期轮询)
|
||||
# TODO: 使用时钟调度器定期检查超时
|
||||
|
||||
async def callback(self, message: str, chat_id: int, user_id: int) -> Optional[str]:
|
||||
"""
|
||||
处理PVP命令
|
||||
|
||||
命令格式:
|
||||
- 挑战 <目标用户ID>
|
||||
- 接受挑战 <挑战ID>
|
||||
- 拒绝挑战 <挑战ID>
|
||||
- 战斗 <战斗ID> <技能名>
|
||||
- 投降 <战斗ID>
|
||||
"""
|
||||
message = self.parse_message_after_at(message).strip()
|
||||
|
||||
tokens = message.split()
|
||||
|
||||
if not tokens:
|
||||
return await self.send_markdown_message(
|
||||
self._help_message(),
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
command = tokens[0].lower()
|
||||
|
||||
if command in ["挑战", "challenge"]:
|
||||
return await self._handle_challenge(chat_id, user_id, tokens[1:])
|
||||
|
||||
elif command in ["接受挑战", "accept"]:
|
||||
return await self._handle_accept(chat_id, user_id, tokens[1:])
|
||||
|
||||
elif command in ["拒绝挑战", "reject"]:
|
||||
return await self._handle_reject(chat_id, user_id, tokens[1:])
|
||||
|
||||
elif command in ["投降", "surrender"]:
|
||||
return await self._handle_surrender(chat_id, user_id, tokens[1:])
|
||||
|
||||
else:
|
||||
# 默认视为战斗动作
|
||||
return await self._handle_battle_action(chat_id, user_id, tokens)
|
||||
|
||||
async def _handle_challenge(
|
||||
self,
|
||||
chat_id: int,
|
||||
user_id: int,
|
||||
args: list
|
||||
) -> Optional[str]:
|
||||
"""处理挑战命令"""
|
||||
if not args:
|
||||
return await self.send_markdown_message(
|
||||
"❌ 请指定目标用户ID\n用法:`挑战 <目标用户ID>`",
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
try:
|
||||
target_id = int(args[0])
|
||||
except ValueError:
|
||||
return await self.send_markdown_message(
|
||||
"❌ 用户ID格式错误",
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
if target_id == user_id:
|
||||
return await self.send_markdown_message(
|
||||
"❌ 不能挑战自己",
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
service = self.service()
|
||||
success, msg, challenge_id = service.create_pvp_challenge(user_id, target_id)
|
||||
|
||||
return await self.send_markdown_message(msg, chat_id, user_id)
|
||||
|
||||
async def _handle_accept(
|
||||
self,
|
||||
chat_id: int,
|
||||
user_id: int,
|
||||
args: list
|
||||
) -> Optional[str]:
|
||||
"""处理接受挑战"""
|
||||
if not args:
|
||||
return await self.send_markdown_message(
|
||||
"❌ 请指定挑战ID\n用法:`接受挑战 <挑战ID>`",
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
try:
|
||||
challenge_id = int(args[0])
|
||||
except ValueError:
|
||||
return await self.send_markdown_message(
|
||||
"❌ 挑战ID格式错误",
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
service = self.service()
|
||||
success, msg, battle_id = service.accept_challenge(challenge_id, user_id)
|
||||
|
||||
return await self.send_markdown_message(msg, chat_id, user_id)
|
||||
|
||||
async def _handle_reject(
|
||||
self,
|
||||
chat_id: int,
|
||||
user_id: int,
|
||||
args: list
|
||||
) -> Optional[str]:
|
||||
"""处理拒绝挑战"""
|
||||
if not args:
|
||||
return await self.send_markdown_message(
|
||||
"❌ 请指定挑战ID\n用法:`拒绝挑战 <挑战ID>`",
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
try:
|
||||
challenge_id = int(args[0])
|
||||
except ValueError:
|
||||
return await self.send_markdown_message(
|
||||
"❌ 挑战ID格式错误",
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
service = self.service()
|
||||
success, msg = service.reject_challenge(challenge_id, user_id)
|
||||
|
||||
return await self.send_markdown_message(msg, chat_id, user_id)
|
||||
|
||||
async def _handle_battle_action(
|
||||
self,
|
||||
chat_id: int,
|
||||
user_id: int,
|
||||
tokens: list
|
||||
) -> Optional[str]:
|
||||
"""处理战斗动作"""
|
||||
if len(tokens) < 2:
|
||||
return await self.send_markdown_message(
|
||||
"❌ 命令格式错误\n用法:`战斗 <战斗ID> <技能名>`",
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
try:
|
||||
battle_id = int(tokens[0])
|
||||
except ValueError:
|
||||
return await self.send_markdown_message(
|
||||
"❌ 战斗ID格式错误",
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
skill_name = " ".join(tokens[1:])
|
||||
|
||||
service = self.service()
|
||||
success, msg = service.execute_battle_action(battle_id, user_id, skill_name)
|
||||
|
||||
return await self.send_markdown_message(msg, chat_id, user_id)
|
||||
|
||||
async def _handle_surrender(
|
||||
self,
|
||||
chat_id: int,
|
||||
user_id: int,
|
||||
args: list
|
||||
) -> Optional[str]:
|
||||
"""处理投降"""
|
||||
if not args:
|
||||
return await self.send_markdown_message(
|
||||
"❌ 请指定战斗ID\n用法:`投降 <战斗ID>`",
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
try:
|
||||
battle_id = int(args[0])
|
||||
except ValueError:
|
||||
return await self.send_markdown_message(
|
||||
"❌ 战斗ID格式错误",
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
service = self.service()
|
||||
success, msg = service.surrender_battle(battle_id, user_id)
|
||||
|
||||
return await self.send_markdown_message(msg, chat_id, user_id)
|
||||
|
||||
def _help_message(self) -> str:
|
||||
"""帮助信息"""
|
||||
return """# ⚔️ PVP对战系统
|
||||
**命令格式:**
|
||||
- `挑战 <目标用户ID>`:发起PVP挑战
|
||||
- `接受挑战 <挑战ID>`:接受挑战
|
||||
- `拒绝挑战 <挑战ID>`:拒绝挑战
|
||||
- `战斗 <战斗ID> <技能名>`:执行战斗动作
|
||||
- `投降 <战斗ID>`:投降
|
||||
|
||||
**说明:**
|
||||
- 挑战有效期15分钟,超时自动失效
|
||||
- 回合制战斗,速度高者先手
|
||||
- 胜者获得1000积分(或失败者全部积分)
|
||||
- 超时未操作视为失败
|
||||
- 可随时投降
|
||||
|
||||
**示例:**
|
||||
- `挑战 12345`:向用户12345发起挑战
|
||||
- `接受挑战 1`:接受挑战ID为1的挑战
|
||||
- `战斗 1 攻击`:在战斗1中使用"攻击"技能
|
||||
- `投降 1`:在战斗1中投降
|
||||
"""
|
||||
|
||||
|
||||
__all__ = ["WPSCombatBattle"]
|
||||
159
Plugins/WPSCombatSystem/combat_plugin_equipment.py
Normal file
159
Plugins/WPSCombatSystem/combat_plugin_equipment.py
Normal file
@@ -0,0 +1,159 @@
|
||||
"""装备管理插件 - 装备和卸下物品"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from PWF.Convention.Runtime.GlobalConfig import ConsoleFrontColor, ProjectConfig
|
||||
|
||||
from .combat_plugin_base import WPSCombatBase
|
||||
|
||||
|
||||
logger: ProjectConfig = ProjectConfig()
|
||||
|
||||
|
||||
class WPSCombatEquipment(WPSCombatBase):
|
||||
"""装备管理插件"""
|
||||
|
||||
def is_enable_plugin(self) -> bool:
|
||||
return True
|
||||
|
||||
def wake_up(self) -> None:
|
||||
super().wake_up()
|
||||
logger.Log(
|
||||
"Info",
|
||||
f"{ConsoleFrontColor.GREEN}WPSCombatEquipment 插件已加载{ConsoleFrontColor.RESET}"
|
||||
)
|
||||
self.register_plugin("装备")
|
||||
self.register_plugin("equip")
|
||||
self.register_plugin("卸下")
|
||||
self.register_plugin("unequip")
|
||||
|
||||
async def callback(self, message: str, chat_id: int, user_id: int) -> Optional[str]:
|
||||
"""
|
||||
处理装备相关命令
|
||||
|
||||
命令格式:
|
||||
- 装备 <物品名>
|
||||
- 卸下 <槽位>
|
||||
"""
|
||||
message = self.parse_message_after_at(message).strip()
|
||||
|
||||
if not message:
|
||||
return await self.send_markdown_message(
|
||||
self._help_message(),
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
tokens = message.split(maxsplit=1)
|
||||
if not tokens:
|
||||
return await self.send_markdown_message(
|
||||
self._help_message(),
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
# 判断是装备还是卸下
|
||||
# 注意:由于命令已经通过register_plugin匹配,这里tokens[0]可能为空
|
||||
# 实际命令词已经被消费了
|
||||
|
||||
# 如果消息为空,说明只输入了命令词
|
||||
if not tokens or len(tokens) == 0:
|
||||
return await self.send_markdown_message(
|
||||
self._help_message(),
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
# tokens[0] 应该是物品名或槽位
|
||||
target = tokens[0] if len(tokens) > 0 else ""
|
||||
|
||||
# 通过检查self被哪个命令触发来判断操作
|
||||
# 但这里我们无法直接知道,所以通过参数判断
|
||||
# 如果是槽位名称(weapon/helmet等),则是卸下操作
|
||||
# 否则尝试装备
|
||||
|
||||
slot_names = ["weapon", "helmet", "armor", "boots", "accessory",
|
||||
"武器", "头盔", "护甲", "鞋子", "饰品"]
|
||||
|
||||
# 简化:检查是否包含"卸下"或"unequip"
|
||||
# 由于命令注册了这些词,我们可以假设如果达到这里,就是对应的操作
|
||||
|
||||
# 实际上,由于我们注册了"装备"和"卸下"两个命令,
|
||||
# message已经去掉了命令词,只剩参数
|
||||
# 所以我们需要重新解析原始消息来判断
|
||||
|
||||
# 更简单的方法:检查target是否是有效槽位
|
||||
slot_map = {
|
||||
"weapon": "weapon", "武器": "weapon",
|
||||
"helmet": "helmet", "头盔": "helmet",
|
||||
"armor": "armor", "护甲": "armor",
|
||||
"boots": "boots", "鞋子": "boots",
|
||||
"accessory": "accessory", "饰品": "accessory",
|
||||
}
|
||||
|
||||
if target.lower() in slot_map:
|
||||
# 卸下操作
|
||||
return await self._handle_unequip(chat_id, user_id, slot_map[target.lower()])
|
||||
else:
|
||||
# 装备操作
|
||||
return await self._handle_equip(chat_id, user_id, target)
|
||||
|
||||
async def _handle_equip(self, chat_id: int, user_id: int, item_name: str) -> Optional[str]:
|
||||
"""处理装备命令"""
|
||||
if not item_name:
|
||||
return await self.send_markdown_message(
|
||||
"❌ 请指定要装备的物品名称\n用法:`装备 <物品名>`",
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
service = self.service()
|
||||
|
||||
# 尝试通过名称查找装备item_id
|
||||
from .combat_models import EQUIPMENT_REGISTRY
|
||||
|
||||
item_id = None
|
||||
for eq_id, eq_def in EQUIPMENT_REGISTRY.items():
|
||||
if eq_def.name.lower() == item_name.lower() or eq_id.lower() == item_name.lower():
|
||||
item_id = eq_id
|
||||
break
|
||||
|
||||
if not item_id:
|
||||
return await self.send_markdown_message(
|
||||
f"❌ 未找到装备:{item_name}",
|
||||
chat_id,
|
||||
user_id
|
||||
)
|
||||
|
||||
success, msg = service.equip_item(user_id, item_id)
|
||||
return await self.send_markdown_message(msg, chat_id, user_id)
|
||||
|
||||
async def _handle_unequip(self, chat_id: int, user_id: int, slot: str) -> Optional[str]:
|
||||
"""处理卸下命令"""
|
||||
service = self.service()
|
||||
success, msg = service.unequip_item(user_id, slot)
|
||||
return await self.send_markdown_message(msg, chat_id, user_id)
|
||||
|
||||
def _help_message(self) -> str:
|
||||
"""帮助信息"""
|
||||
return """# ⚔️ 装备管理
|
||||
**命令格式:**
|
||||
- `装备 <物品名>`:装备指定物品
|
||||
- `卸下 <槽位>`:卸下指定槽位的装备
|
||||
|
||||
**槽位名称:**
|
||||
- weapon/武器
|
||||
- helmet/头盔
|
||||
- armor/护甲
|
||||
- boots/鞋子
|
||||
- accessory/饰品
|
||||
|
||||
**示例:**
|
||||
- `装备 木剑`
|
||||
- `卸下 weapon`
|
||||
"""
|
||||
|
||||
|
||||
__all__ = ["WPSCombatEquipment"]
|
||||
44
Plugins/WPSCombatSystem/combat_plugin_heal.py
Normal file
44
Plugins/WPSCombatSystem/combat_plugin_heal.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""治疗系统插件 - 恢复受伤状态"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from PWF.Convention.Runtime.GlobalConfig import ConsoleFrontColor, ProjectConfig
|
||||
|
||||
from .combat_plugin_base import WPSCombatBase
|
||||
|
||||
|
||||
logger: ProjectConfig = ProjectConfig()
|
||||
|
||||
|
||||
class WPSCombatHeal(WPSCombatBase):
|
||||
"""治疗系统插件"""
|
||||
|
||||
def is_enable_plugin(self) -> bool:
|
||||
return True
|
||||
|
||||
def wake_up(self) -> None:
|
||||
super().wake_up()
|
||||
logger.Log(
|
||||
"Info",
|
||||
f"{ConsoleFrontColor.GREEN}WPSCombatHeal 插件已加载{ConsoleFrontColor.RESET}"
|
||||
)
|
||||
self.register_plugin("治疗")
|
||||
self.register_plugin("heal")
|
||||
self.register_plugin("恢复")
|
||||
|
||||
async def callback(self, message: str, chat_id: int, user_id: int) -> Optional[str]:
|
||||
"""
|
||||
处理治疗命令
|
||||
|
||||
命令格式:
|
||||
- 治疗
|
||||
"""
|
||||
service = self.service()
|
||||
success, msg = service.heal_player(user_id)
|
||||
|
||||
return await self.send_markdown_message(msg, chat_id, user_id)
|
||||
|
||||
|
||||
__all__ = ["WPSCombatHeal"]
|
||||
103
Plugins/WPSCombatSystem/combat_plugin_status.py
Normal file
103
Plugins/WPSCombatSystem/combat_plugin_status.py
Normal file
@@ -0,0 +1,103 @@
|
||||
"""状态查看插件 - 显示玩家属性、装备和技能"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from PWF.Convention.Runtime.GlobalConfig import ConsoleFrontColor, ProjectConfig
|
||||
|
||||
from .combat_plugin_base import WPSCombatBase
|
||||
|
||||
|
||||
logger: ProjectConfig = ProjectConfig()
|
||||
|
||||
|
||||
class WPSCombatStatus(WPSCombatBase):
|
||||
"""状态查看插件"""
|
||||
|
||||
def is_enable_plugin(self) -> bool:
|
||||
return True
|
||||
|
||||
def wake_up(self) -> None:
|
||||
super().wake_up()
|
||||
logger.Log(
|
||||
"Info",
|
||||
f"{ConsoleFrontColor.GREEN}WPSCombatStatus 插件已加载{ConsoleFrontColor.RESET}"
|
||||
)
|
||||
self.register_plugin("战斗属性")
|
||||
self.register_plugin("combat")
|
||||
self.register_plugin("装备栏")
|
||||
self.register_plugin("技能列表")
|
||||
|
||||
async def callback(self, message: str, chat_id: int, user_id: int) -> Optional[str]:
|
||||
"""
|
||||
处理状态查看命令
|
||||
|
||||
命令格式:
|
||||
- 战斗属性:显示完整属性和装备
|
||||
- 装备栏:只显示装备
|
||||
- 技能列表:只显示技能
|
||||
"""
|
||||
message = self.parse_message_after_at(message).strip()
|
||||
|
||||
service = self.service()
|
||||
|
||||
# 根据具体命令显示不同内容
|
||||
# 这里简化为都显示完整状态
|
||||
stats = service.calculate_player_stats(user_id)
|
||||
equipped = service.get_equipped_items(user_id)
|
||||
|
||||
# 格式化输出
|
||||
output = self._format_status(stats, equipped)
|
||||
|
||||
return await self.send_markdown_message(output, chat_id, user_id)
|
||||
|
||||
def _format_status(self, stats, equipped) -> str:
|
||||
"""格式化状态输出"""
|
||||
lines = ["# ⚔️ 战斗属性"]
|
||||
|
||||
# 基础属性
|
||||
lines.append("\n**基础属性:**")
|
||||
lines.append(f"- HP:`{stats.hp}`")
|
||||
lines.append(f"- ATK:`{stats.atk}`")
|
||||
lines.append(f"- DEF:`{stats.def_}`")
|
||||
lines.append(f"- SPD:`{stats.spd}`")
|
||||
lines.append(f"- CRIT:`{stats.crit}%`")
|
||||
lines.append(f"- CRIT_DMG:`{stats.crit_dmg}%`")
|
||||
lines.append(f"- 装备强度:`{stats.equipment_strength:.1f}`")
|
||||
|
||||
# 装备栏
|
||||
lines.append("\n**装备栏:**")
|
||||
slot_names = {
|
||||
"weapon": "武器",
|
||||
"helmet": "头盔",
|
||||
"armor": "护甲",
|
||||
"boots": "鞋子",
|
||||
"accessory": "饰品",
|
||||
}
|
||||
|
||||
for slot, eq_def in equipped.items():
|
||||
slot_display = slot_names.get(slot, slot)
|
||||
if eq_def:
|
||||
# 显示装备属性
|
||||
attrs = ", ".join([f"{k}+{v}" for k, v in eq_def.attributes.items()])
|
||||
tier_label = eq_def.tier.to_markdown_label(eq_def.tier.display_name)
|
||||
lines.append(f"- {slot_display}:{tier_label} {eq_def.name} ({attrs})")
|
||||
else:
|
||||
lines.append(f"- {slot_display}:`未装备`")
|
||||
|
||||
# 可用技能
|
||||
lines.append("\n**可用技能:**")
|
||||
if stats.available_skills:
|
||||
for skill in stats.available_skills:
|
||||
lines.append(f"- {skill.icon} **{skill.name}**")
|
||||
lines.append(f" - {skill.description}")
|
||||
if skill.cooldown > 0:
|
||||
lines.append(f" - 冷却:{skill.cooldown}回合")
|
||||
else:
|
||||
lines.append("- `无可用技能`")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
__all__ = ["WPSCombatStatus"]
|
||||
1515
Plugins/WPSCombatSystem/combat_service.py
Normal file
1515
Plugins/WPSCombatSystem/combat_service.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user