"
# 创建标签
echo "🏷️ 创建版本标签..."
git tag -a "v$NEW_BACKEND_VERSION" -m "Version $NEW_BACKEND_VERSION"
# 推送更改
echo "📤 推送更改到远程仓库..."
git push origin main
git push origin "v$NEW_BACKEND_VERSION"
echo "✅ 推送完成"
echo ""
# 生成发布说明
echo "📝 生成发布说明..."
cat > release_notes.md << EOF
## 🎉 ZSim $NEW_BACKEND_VERSION Release
### 📦 版本信息
- 后端版本: $NEW_BACKEND_VERSION
- 前端版本: $NEW_FRONTEND_VERSION
### 🚀 更新内容
#### ✨ 新功能
- 待添加
#### 🐛 问题修复
- 待添加
#### 🔧 性能优化
- 待添加
### 📋 安装说明
1. 下载对应平台的安装包
2. 运行安装程序
3. 启动 ZSim 应用
### 📁 下载文件
- Windows: \`ZSim-Setup-$NEW_BACKEND_VERSION.exe\`
- macOS: \`ZSim-$NEW_BACKEND_VERSION.dmg\`
- Linux: \`ZSim-$NEW_BACKEND_VERSION.AppImage\`
---
🤖 Generated with [Claude Code](https://claude.ai/code)
EOF
echo "✅ 发布说明已生成: release_notes.md"
echo ""
# 创建 GitHub Release (如果安装了 gh cli)
if command -v gh &> /dev/null; then
echo "🎯 创建 GitHub Release..."
# 准备文件列表
FILES=$(find electron-app/release -type f \( -name "*.exe" -o -name "*.dmg" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.zip" -o -name "*.blockmap" \) | tr '\n' ' ')
# 创建发布
if [[ "$DRAFT" == "true" ]]; then
gh release create "v$NEW_BACKEND_VERSION" $FILES --title "ZSim $NEW_BACKEND_VERSION" --notes-file release_notes.md --draft
elif [[ "$PRERELEASE" == "true" ]]; then
gh release create "v$NEW_BACKEND_VERSION" $FILES --title "ZSim $NEW_BACKEND_VERSION" --notes-file release_notes.md --prerelease
else
gh release create "v$NEW_BACKEND_VERSION" $FILES --title "ZSim $NEW_BACKEND_VERSION" --notes-file release_notes.md
fi
echo "✅ GitHub Release 创建完成"
else
echo "⚠️ GitHub CLI 未安装,跳过自动创建 Release"
echo "请手动创建 Release 并上传文件"
fi
echo ""
echo "🎉 版本发布流程完成!"
echo "📋 后续操作:"
echo " 1. 编辑 release_notes.md 添加具体的更新内容"
echo " 2. 如果未自动创建 Release,请手动创建"
echo " 3. 通知用户更新"
echo ""
# 清理
rm -f release_notes.md
================================================
FILE: tests/__init__.py
================================================
# -*- coding: utf-8 -*-
"""测试模块初始化文件"""
================================================
FILE: tests/api/test_apl.py
================================================
"""
APL API 测试
测试APL相关API端点和数据模型
"""
import pytest
from zsim.api_src.models.apl import (
APLCharacterConfig,
APLCharactersInfo,
APLConfigCreateRequest,
APLConfigUpdateRequest,
APLLogicInfo,
)
def test_apl_character_config_cinema_as_list():
"""测试APL角色配置中cinema字段作为列表的处理"""
# 测试有效的cinema列表
config = APLCharacterConfig(cinema=[0, 1, 2], weapon="TestWeapon", equip_set4="TestSet")
assert config.cinema == [0, 1, 2]
assert config.weapon == "TestWeapon"
assert config.equip_set4 == "TestSet"
# 测试空的cinema列表
config = APLCharacterConfig(cinema=[], weapon="TestWeapon")
assert config.cinema == []
assert config.weapon == "TestWeapon"
# 测试None值
config = APLCharacterConfig(cinema=None, weapon="TestWeapon")
assert config.cinema is None
assert config.weapon == "TestWeapon"
def test_apl_characters_info_dynamic_fields():
"""测试APL角色信息模型的动态字段支持"""
characters_info = APLCharactersInfo(
required=["Character1", "Character2"],
optional=["Character3"],
Character1={"cinema": [0, 1], "weapon": "Weapon1"},
Character2={"cinema": [2, 3], "weapon": "Weapon2"},
Character3={"cinema": [4], "weapon": "Weapon3"},
)
assert characters_info.required == ["Character1", "Character2"]
assert characters_info.optional == ["Character3"]
# 验证动态字段被正确存储
assert hasattr(characters_info, "Character1")
assert hasattr(characters_info, "Character2")
assert hasattr(characters_info, "Character3")
def test_apl_config_create_request():
"""测试APL配置创建请求模型"""
# 创建完整的APL配置请求
characters = APLCharactersInfo(
required=["Character1"],
optional=[],
Character1={"cinema": [0, 1, 2], "weapon": "TestWeapon"},
)
apl_logic = APLLogicInfo(logic="# Test APL logic\n1|action+=|1_S1|condition==True")
request = APLConfigCreateRequest(
title="Test APL Config",
comment="Test comment",
author="Test Author",
characters=characters,
apl_logic=apl_logic,
)
assert request.title == "Test APL Config"
assert request.comment == "Test comment"
assert request.author == "Test Author"
assert request.characters.required == ["Character1"]
assert request.characters.Character1["cinema"] == [0, 1, 2]
assert request.apl_logic.logic == "# Test APL logic\n1|action+=|1_S1|condition==True"
def test_apl_config_update_request():
"""测试APL配置更新请求模型"""
# 创建APL配置更新请求
characters = APLCharactersInfo(required=["Character1", "Character2"], optional=["Character3"])
apl_logic = APLLogicInfo(logic="# Updated APL logic\n2|action+=|2_S1|condition==False")
request = APLConfigUpdateRequest(
title="Updated APL Config",
comment="Updated comment",
characters=characters,
apl_logic=apl_logic,
)
assert request.title == "Updated APL Config"
assert request.comment == "Updated comment"
assert request.characters.required == ["Character1", "Character2"]
assert request.characters.optional == ["Character3"]
assert request.apl_logic.logic == "# Updated APL logic\n2|action+=|2_S1|condition==False"
if __name__ == "__main__":
pytest.main([__file__, "-v"])
================================================
FILE: tests/api/test_apl_database.py
================================================
"""
APL数据库测试
测试APL数据库操作功能
"""
import os
import shutil
import tempfile
import pytest
from zsim.api_src.services.database.apl_db import APLDatabase
from zsim.define import COSTOM_APL_DIR, DEFAULT_APL_DIR, SQLITE_PATH
class TestAPLDatabase:
"""APL数据库测试类"""
@pytest.fixture(autouse=True)
def setup_and_teardown(self):
"""设置和清理测试环境"""
# 保存原始目录
original_default_dir = DEFAULT_APL_DIR
original_custom_dir = COSTOM_APL_DIR
original_sqlite_path = SQLITE_PATH
# 创建临时目录
temp_dir = tempfile.mkdtemp()
test_default_dir = os.path.join(temp_dir, "default")
test_custom_dir = os.path.join(temp_dir, "custom")
# 创建目录
os.makedirs(test_default_dir, exist_ok=True)
os.makedirs(test_custom_dir, exist_ok=True)
# 修改全局变量
import zsim.define
zsim.define.DEFAULT_APL_DIR = test_default_dir
zsim.define.COSTOM_APL_DIR = test_custom_dir
# 重新导入APLDatabase以使用新的目录
import zsim.api_src.services.database.apl_db
zsim.api_src.services.database.apl_db.DEFAULT_APL_DIR = test_default_dir
zsim.api_src.services.database.apl_db.COSTOM_APL_DIR = test_custom_dir
zsim.api_src.services.database.apl_db.SQLITE_PATH = os.path.join(
test_default_dir, "test_zsim.db"
)
yield test_default_dir, test_custom_dir
# 清理
shutil.rmtree(temp_dir)
zsim.define.DEFAULT_APL_DIR = original_default_dir
zsim.define.COSTOM_APL_DIR = original_custom_dir
zsim.api_src.services.database.apl_db.SQLITE_PATH = original_sqlite_path
def test_create_and_get_apl_config(self, setup_and_teardown):
"""测试创建和获取APL配置"""
test_default_dir, test_custom_dir = setup_and_teardown
db = APLDatabase()
# 创建测试配置数据
config_data = {
"title": "Test APL Config",
"author": "Test Author",
"characters": {
"required": ["Character1"],
"optional": ["Character2"],
"Character1": {"cinema": [0, 1, 2], "weapon": "TestWeapon"},
},
"apl_logic": {"logic": "# Test APL logic"},
}
# 创建APL配置
config_id = db.create_apl_config(config_data)
assert isinstance(config_id, str)
assert len(config_id) > 0
# 获取APL配置
retrieved_config = db.get_apl_config(config_id)
assert retrieved_config is not None
assert retrieved_config["title"] == "Test APL Config"
assert retrieved_config["author"] == "Test Author"
assert retrieved_config["characters"]["required"] == ["Character1"]
def test_update_apl_config(self, setup_and_teardown):
"""测试更新APL配置"""
test_default_dir, test_custom_dir = setup_and_teardown
db = APLDatabase()
# 创建初始配置
config_data = {"title": "Original Title", "author": "Original Author"}
config_id = db.create_apl_config(config_data)
# 更新配置
updated_data = {
"title": "Updated Title",
"author": "Updated Author",
"characters": {"required": ["NewCharacter"]},
}
success = db.update_apl_config(config_id, updated_data)
assert success is True
# 验证更新
retrieved_config = db.get_apl_config(config_id)
assert retrieved_config is not None
assert retrieved_config["title"] == "Updated Title"
assert retrieved_config["author"] == "Updated Author"
assert retrieved_config["characters"]["required"] == ["NewCharacter"]
def test_delete_apl_config(self, setup_and_teardown):
"""测试删除APL配置"""
test_default_dir, test_custom_dir = setup_and_teardown
db = APLDatabase()
# 创建配置
config_data = {"title": "Test Config"}
config_id = db.create_apl_config(config_data)
# 验证配置存在
retrieved_config = db.get_apl_config(config_id)
assert retrieved_config is not None
# 删除配置
success = db.delete_apl_config(config_id)
assert success is True
# 验证配置已删除
retrieved_config = db.get_apl_config(config_id)
assert retrieved_config is None
def test_get_apl_templates(self, setup_and_teardown):
"""测试获取APL模板"""
test_default_dir, test_custom_dir = setup_and_teardown
db = APLDatabase()
# 创建测试TOML文件
default_toml_content = """
[general]
title = "Default Template"
author = "Default Author"
"""
custom_toml_content = """
[general]
title = "Custom Template"
author = "Custom Author"
"""
# 写入默认模板文件
with open(
os.path.join(test_default_dir, "default_template.toml"), "w", encoding="utf-8"
) as f:
f.write(default_toml_content)
# 写入自定义模板文件
with open(
os.path.join(test_custom_dir, "custom_template.toml"), "w", encoding="utf-8"
) as f:
f.write(custom_toml_content)
# 获取模板
templates = db.get_apl_templates()
assert len(templates) == 2
# 验证模板信息
titles = [t["title"] for t in templates]
assert "Default Template" in titles
assert "Custom Template" in titles
def test_get_apl_files(self, setup_and_teardown):
"""测试获取APL文件列表"""
test_default_dir, test_custom_dir = setup_and_teardown
db = APLDatabase()
# 创建测试文件
with open(os.path.join(test_default_dir, "file1.toml"), "w", encoding="utf-8") as f:
f.write("# Test file 1")
with open(os.path.join(test_custom_dir, "file2.toml"), "w", encoding="utf-8") as f:
f.write("# Test file 2")
# 获取文件列表
files = db.get_apl_files()
assert len(files) == 2
# 验证文件信息
filenames = [f["name"] for f in files]
assert "file1.toml" in filenames
assert "file2.toml" in filenames
def test_get_apl_file_content(self, setup_and_teardown):
"""测试获取APL文件内容"""
test_default_dir, test_custom_dir = setup_and_teardown
db = APLDatabase()
# 创建测试文件
test_content = "# Test APL Content\n1|action+=|1_S1|condition==True"
file_path = os.path.join(test_custom_dir, "test_file.toml")
with open(file_path, "w", encoding="utf-8") as f:
f.write(test_content)
# 获取文件内容
file_id = "custom_test_file.toml"
content = db.get_apl_file_content(file_id)
assert content is not None
assert content["content"] == test_content
assert content["file_id"] == file_id
def test_create_apl_file(self, setup_and_teardown):
"""测试创建APL文件"""
test_default_dir, test_custom_dir = setup_and_teardown
db = APLDatabase()
# 创建文件数据
file_data = {"name": "new_file.toml", "content": "# New APL File Content"}
# 创建文件
file_id = db.create_apl_file(file_data)
assert file_id == "custom_new_file.toml"
# 验证文件已创建
file_path = os.path.join(test_custom_dir, "new_file.toml")
assert os.path.exists(file_path)
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
assert content == "# New APL File Content"
def test_update_apl_file(self, setup_and_teardown):
"""测试更新APL文件"""
test_default_dir, test_custom_dir = setup_and_teardown
db = APLDatabase()
# 创建初始文件
initial_content = "# Initial Content"
file_path = os.path.join(test_custom_dir, "update_test.toml")
with open(file_path, "w", encoding="utf-8") as f:
f.write(initial_content)
# 更新文件
new_content = "# Updated Content"
success = db.update_apl_file("custom_update_test.toml", new_content)
assert success is True
# 验证更新
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
assert content == new_content
def test_delete_apl_file(self, setup_and_teardown):
"""测试删除APL文件"""
test_default_dir, test_custom_dir = setup_and_teardown
db = APLDatabase()
# 创建文件
file_content = "# Test Content"
file_path = os.path.join(test_custom_dir, "delete_test.toml")
with open(file_path, "w", encoding="utf-8") as f:
f.write(file_content)
# 验证文件存在
assert os.path.exists(file_path)
# 删除文件
success = db.delete_apl_file("custom_delete_test.toml")
assert success is True
# 验证文件已删除
assert not os.path.exists(file_path)
if __name__ == "__main__":
pytest.main([__file__, "-v"])
================================================
FILE: tests/api/test_apl_import_export.py
================================================
"""
APL导入导出测试
测试APL配置的导入和导出功能
"""
import os
import shutil
import tempfile
import tomllib
import pytest
import tomli_w
from zsim.api_src.services.database.apl_db import APLDatabase
from zsim.define import COSTOM_APL_DIR, DEFAULT_APL_DIR
class TestAPLImportExport:
"""APL导入导出测试类"""
@pytest.fixture(autouse=True)
def setup_and_teardown(self):
"""设置和清理测试环境"""
# 保存原始目录
original_default_dir = DEFAULT_APL_DIR
original_custom_dir = COSTOM_APL_DIR
# 创建临时目录
temp_dir = tempfile.mkdtemp()
test_default_dir = os.path.join(temp_dir, "default")
test_custom_dir = os.path.join(temp_dir, "custom")
# 创建目录
os.makedirs(test_default_dir, exist_ok=True)
os.makedirs(test_custom_dir, exist_ok=True)
# 修改全局变量
import zsim.define
zsim.define.DEFAULT_APL_DIR = test_default_dir
zsim.define.COSTOM_APL_DIR = test_custom_dir
# 重新导入APLDatabase以使用新的目录
import zsim.api_src.services.database.apl_db
zsim.api_src.services.database.apl_db.DEFAULT_APL_DIR = test_default_dir
zsim.api_src.services.database.apl_db.COSTOM_APL_DIR = test_custom_dir
zsim.api_src.services.database.apl_db.APL_DATABASE_FILE = os.path.join( # type: ignore
test_default_dir, "apl_configs.db"
)
yield test_default_dir, test_custom_dir
# 清理
shutil.rmtree(temp_dir)
zsim.define.DEFAULT_APL_DIR = original_default_dir
zsim.define.COSTOM_APL_DIR = original_custom_dir
def test_export_apl_config(self, setup_and_teardown):
"""测试导出APL配置"""
test_default_dir, test_custom_dir = setup_and_teardown
db = APLDatabase()
# 创建测试配置数据
config_data = {
"title": "Test Export Config",
"author": "Test Author",
"comment": "Test Comment",
"characters": {
"required": ["Character1"],
"optional": ["Character2"],
"Character1": {"cinema": [0, 1, 2], "weapon": "TestWeapon"},
},
"apl_logic": {"logic": "# Test APL logic"},
}
# 创建APL配置
config_id = db.create_apl_config(config_data)
# 导出配置到文件
export_file_path = os.path.join(test_custom_dir, "exported_config.toml")
success = db.export_apl_config(config_id, export_file_path)
# 验证导出成功
assert success is True
assert os.path.exists(export_file_path)
# 验证导出的文件内容
with open(export_file_path, "rb") as f:
exported_data = tomllib.load(f)
assert exported_data["title"] == "Test Export Config"
assert exported_data["author"] == "Test Author"
assert exported_data["comment"] == "Test Comment"
assert exported_data["characters"]["required"] == ["Character1"]
def test_import_apl_config(self, setup_and_teardown):
"""测试导入APL配置"""
test_default_dir, test_custom_dir = setup_and_teardown
db = APLDatabase()
# 创建TOML文件用于导入
import_data = {
"title": "Test Import Config",
"author": "Import Author",
"comment": "Import Comment",
"characters": {
"required": ["ImportChar1"],
"optional": ["ImportChar2"],
"ImportChar1": {"cinema": [3, 4, 5], "weapon": "ImportWeapon"},
},
"apl_logic": {"logic": "# Import APL logic"},
}
import_file_path = os.path.join(test_custom_dir, "import_config.toml")
with open(import_file_path, "wb") as f:
tomli_w.dump(import_data, f, multiline_strings=True)
# 导入配置
config_id = db.import_apl_config(import_file_path)
# 验证导入成功
assert config_id is not None
assert isinstance(config_id, str)
assert len(config_id) > 0
# 验证导入的配置内容
imported_config = db.get_apl_config(config_id)
assert imported_config is not None
assert imported_config["title"] == "Test Import Config"
assert imported_config["author"] == "Import Author"
assert imported_config["characters"]["required"] == ["ImportChar1"]
def test_import_export_roundtrip(self, setup_and_teardown):
"""测试导入导出往返一致性"""
test_default_dir, test_custom_dir = setup_and_teardown
db = APLDatabase()
# 创建原始配置
original_data = {
"title": "Roundtrip Test Config",
"author": "Roundtrip Author",
"comment": "Roundtrip Comment",
"create_time": "2025-01-01T00:00:00.000+08:00",
"latest_change_time": "2025-01-01T00:00:00.000+08:00",
"characters": {
"required": ["CharA", "CharB"],
"optional": ["CharC"],
"CharA": {"cinema": [0, 1], "weapon": "WeaponA", "equip_set4": "SetA"},
"CharB": {"cinema": [2, 3], "weapon": "WeaponB"},
"CharC": {"cinema": [4]},
},
"apl_logic": {"logic": "# Roundtrip test logic\n1|action+=|1_S1|condition==True"},
}
# 创建配置
config_id = db.create_apl_config(original_data)
# 导出配置
export_file_path = os.path.join(test_custom_dir, "roundtrip_test.toml")
success = db.export_apl_config(config_id, export_file_path)
assert success is True
# 从导出的文件重新导入
new_config_id = db.import_apl_config(export_file_path)
assert new_config_id is not None
# 验证导入的配置与原始配置一致
imported_data = db.get_apl_config(new_config_id)
assert imported_data is not None
# 比较关键字段
assert imported_data["title"] == original_data["title"]
assert imported_data["author"] == original_data["author"]
assert imported_data["comment"] == original_data["comment"]
assert imported_data["characters"]["required"] == original_data["characters"]["required"]
assert imported_data["characters"]["optional"] == original_data["characters"]["optional"]
assert imported_data["apl_logic"]["logic"] == original_data["apl_logic"]["logic"]
# 比较角色配置
for char_name in ["CharA", "CharB", "CharC"]:
if char_name in original_data["characters"]:
assert char_name in imported_data["characters"]
orig_char_config = original_data["characters"][char_name]
imported_char_config = imported_data["characters"][char_name]
for key, value in orig_char_config.items():
assert imported_char_config[key] == value
if __name__ == "__main__":
pytest.main([__file__, "-v"])
================================================
FILE: tests/api/test_character_config.py
================================================
import pytest
from fastapi.testclient import TestClient
from zsim.api import app
from zsim.api_src.services.database.character_db import get_character_db
from zsim.models.character.character_config import CharacterConfig
client = TestClient(app)
@pytest.fixture
def character_config_data():
return {
"config_id": "Hugo_test_config",
"config_name": "test_config",
"name": "Hugo",
"weapon": "音擎A",
"weapon_level": 5,
"cinema": 6,
"crit_balancing": True,
"crit_rate_limit": 0.95,
"scATK_percent": 10,
"scATK": 20,
"scHP_percent": 30,
"scHP": 40,
"scDEF_percent": 50,
"scDEF": 60,
"scAnomalyProficiency": 70,
"scPEN": 80,
"scCRIT": 90,
"scCRIT_DMG": 100,
"drive4": "攻击力%",
"drive5": "暴击率",
"drive6": "暴击伤害",
"equip_style": "4+2",
"equip_set4": "套装A",
}
@pytest.mark.asyncio
async def test_create_character_config(character_config_data):
# 清理之前的测试数据
db = await get_character_db()
await db.delete_character_config("Hugo", "test_config")
# 创建角色配置
response = client.post("/api/characters/Hugo/configs", json=character_config_data)
assert response.status_code == 200
data = response.json()
assert data["name"] == "Hugo"
assert data["config_name"] == "test_config"
assert "config_id" in data
assert "create_time" in data
assert "update_time" in data
@pytest.mark.asyncio
async def test_get_character_config(character_config_data):
# 首先创建一个配置
db = await get_character_db()
await db.delete_character_config("Hugo", "test_config")
config = CharacterConfig(**character_config_data)
await db.add_character_config(config)
# 获取角色配置
response = client.get("/api/characters/Hugo/configs/test_config")
assert response.status_code == 200
data = response.json()
assert data["name"] == "Hugo"
assert data["config_name"] == "test_config"
@pytest.mark.asyncio
async def test_list_character_configs(character_config_data):
# 首先创建一个配置
db = await get_character_db()
await db.delete_character_config("Hugo", "test_config")
config = CharacterConfig(**character_config_data)
await db.add_character_config(config)
# 获取角色的所有配置
response = client.get("/api/characters/Hugo/configs")
assert response.status_code == 200
data = response.json()
assert isinstance(data, list)
assert len(data) > 0
@pytest.mark.asyncio
async def test_update_character_config(character_config_data):
# 首先创建一个配置
db = await get_character_db()
await db.delete_character_config("Hugo", "test_config")
config = CharacterConfig(**character_config_data)
await db.add_character_config(config)
# 更新角色配置
update_data = character_config_data.copy()
update_data["weapon_level"] = 3
response = client.put("/api/characters/Hugo/configs/test_config", json=update_data)
assert response.status_code == 200
data = response.json()
assert data["weapon_level"] == 3
@pytest.mark.asyncio
async def test_delete_character_config(character_config_data):
# 首先创建一个配置
db = await get_character_db()
await db.delete_character_config("Hugo", "test_config")
config = CharacterConfig(**character_config_data)
await db.add_character_config(config)
# 删除角色配置
response = client.delete("/api/characters/Hugo/configs/test_config")
assert response.status_code == 204
# 验证配置已被删除
response = client.get("/api/characters/Hugo/configs/test_config")
assert response.status_code == 404
@pytest.mark.asyncio
async def test_get_characters():
response = client.get("/api/characters/")
assert response.status_code == 200
data = response.json()
assert isinstance(data, list)
assert len(data) > 0
@pytest.mark.asyncio
async def test_get_character_info():
response = client.get("/api/characters/安比/info")
assert response.status_code == 200
data = response.json()
assert "name" in data
assert "element" in data
assert "weapon_type" in data
assert "rarity" in data
assert "base_hp" in data
================================================
FILE: tests/api/test_connection.py
================================================
#!/usr/bin/env python3
"""
简单的前后端连接测试
"""
import json
import os
import subprocess
import sys
def test_backend_connection():
"""测试后端连接"""
print("测试后端连接...")
# 测试UDS连接
uds_path = "/tmp/zsim_api.sock"
if os.path.exists(uds_path):
print(f"✓ UDS socket文件存在: {uds_path}")
try:
# 使用curl测试UDS连接
# 测试健康检查
result = subprocess.run(
["curl", "-s", "--unix-socket", uds_path, "http://localhost/health"],
capture_output=True,
text=True,
timeout=10,
)
if result.returncode == 0:
data = json.loads(result.stdout)
print(f"✓ 健康检查成功: {data}")
else:
print(f"✗ 健康检查失败: {result.stderr}")
return None
# 测试API端点
result = subprocess.run(
["curl", "-s", "--unix-socket", uds_path, "http://localhost/api/sessions/"],
capture_output=True,
text=True,
timeout=10,
)
if result.returncode == 0:
data = json.loads(result.stdout)
print(f"✓ API端点测试成功: {data}")
else:
print(f"✗ API端点测试失败: {result.stderr}")
return None
return True
except Exception as e:
print(f"✗ UDS连接测试失败: {e}")
return None
else:
print(f"✗ UDS socket文件不存在: {uds_path}")
return None
def test_http_fallback():
"""测试HTTP回退连接"""
print("\n测试HTTP回退连接...")
try:
# 尝试使用curl测试常见的端口
for port in [8000, 8001, 8002]:
try:
result = subprocess.run(
["curl", "-s", f"http://127.0.0.1:{port}/health"],
capture_output=True,
text=True,
timeout=3,
)
if result.returncode == 0:
data = json.loads(result.stdout)
print(f"✓ HTTP连接成功 (端口 {port}): {data}")
# 测试API端点
api_result = subprocess.run(
["curl", "-s", f"http://127.0.0.1:{port}/api/sessions/"],
capture_output=True,
text=True,
)
if api_result.returncode == 0:
api_data = json.loads(api_result.stdout)
print(f"✓ API端点测试成功: {api_data}")
return True
else:
print(f"✗ API端点测试失败: {api_result.stderr}")
return False
except Exception:
continue
print("✗ 未找到可用的HTTP端口")
return False
except Exception as e:
print(f"✗ HTTP连接测试失败: {e}")
return False
def main():
"""主函数"""
print("=" * 50)
print("前后端连接测试")
print("=" * 50)
# 测试UDS连接
uds_success = test_backend_connection()
# 如果UDS失败,测试HTTP回退
if not uds_success:
http_success = test_http_fallback()
else:
http_success = False
print("\n" + "=" * 50)
print("测试结果")
print("=" * 50)
print(f"UDS连接: {'✓ 成功' if uds_success else '✗ 失败'}")
print(f"HTTP连接: {'✓ 成功' if http_success else '✗ 失败/未测试'}")
if uds_success or http_success:
print("\n🎉 后端连接正常!")
return 0
else:
print("\n❌ 后端连接失败!")
return 1
if __name__ == "__main__":
sys.exit(main())
================================================
FILE: tests/api/test_enemy_config.py
================================================
import pytest
from fastapi.testclient import TestClient
from zsim.api import app
from zsim.api_src.services.database.enemy_db import get_enemy_db
from zsim.models.enemy.enemy_config import EnemyConfig
client = TestClient(app)
@pytest.fixture
def enemy_config_data():
return {
"config_id": "test_enemy_config",
"enemy_index": 1,
"enemy_adjust": {"def": 0.5, "res": 0.2},
}
@pytest.mark.asyncio
async def test_create_enemy_config(enemy_config_data):
# 清理之前的测试数据
db = await get_enemy_db()
await db.delete_enemy_config("test_enemy_config")
# 创建敌人配置
response = client.post("/api/enemy-configs/", json=enemy_config_data)
assert response.status_code == 200
data = response.json()
assert data["config_id"] == "test_enemy_config"
assert data["enemy_index"] == 1
assert "enemy_adjust" in data
assert "create_time" in data
assert "update_time" in data
@pytest.mark.asyncio
async def test_get_enemy_config(enemy_config_data):
# 首先创建一个配置
db = await get_enemy_db()
await db.delete_enemy_config("test_enemy_config")
config = EnemyConfig(**enemy_config_data)
await db.add_enemy_config(config)
# 获取敌人配置
response = client.get("/api/enemy-configs/test_enemy_config")
assert response.status_code == 200
data = response.json()
assert data["config_id"] == "test_enemy_config"
assert data["enemy_index"] == 1
@pytest.mark.asyncio
async def test_list_enemy_configs(enemy_config_data):
# 首先创建一个配置
db = await get_enemy_db()
await db.delete_enemy_config("test_enemy_config")
config = EnemyConfig(**enemy_config_data)
await db.add_enemy_config(config)
# 获取所有敌人配置
response = client.get("/api/enemy-configs/")
assert response.status_code == 200
data = response.json()
assert isinstance(data, list)
assert len(data) > 0
@pytest.mark.asyncio
async def test_update_enemy_config(enemy_config_data):
# 首先创建一个配置
db = await get_enemy_db()
await db.delete_enemy_config("test_enemy_config")
config = EnemyConfig(**enemy_config_data)
await db.add_enemy_config(config)
# 更新敌人配置
update_data = enemy_config_data.copy()
update_data["enemy_index"] = 2
response = client.put("/api/enemy-configs/test_enemy_config", json=update_data)
assert response.status_code == 200
data = response.json()
assert data["enemy_index"] == 2
@pytest.mark.asyncio
async def test_delete_enemy_config(enemy_config_data):
# 首先创建一个配置
db = await get_enemy_db()
await db.delete_enemy_config("test_enemy_config")
config = EnemyConfig(**enemy_config_data)
await db.add_enemy_config(config)
# 删除敌人配置
response = client.delete("/api/enemy-configs/test_enemy_config")
assert response.status_code == 204
# 验证配置已被删除
response = client.get("/api/enemy-configs/test_enemy_config")
assert response.status_code == 404
@pytest.mark.asyncio
async def test_get_enemies():
response = client.get("/api/enemies/")
assert response.status_code == 200
data = response.json()
assert isinstance(data, list)
assert len(data) > 0
@pytest.mark.asyncio
async def test_get_enemy_info():
response = client.get("/api/enemies/11412/info")
assert response.status_code == 200
data = response.json()
assert "name" in data
assert "sub_id" in data
assert "index_id" in data
================================================
FILE: tests/api/test_session_op.py
================================================
import pytest
from fastapi.testclient import TestClient
from zsim.api import app
from zsim.api_src.services.database.session_db import get_session_db
from zsim.models.session.session_create import Session
client = TestClient(app)
@pytest.fixture
def session_data():
return {
"session_id": "test_session",
"session_name": "Test Session",
"session_type": "test",
"apl_file_path": "data/APL/test_apl.toml",
"character_config_path": "test/path",
"enemy_config_path": "test/path",
"simulation_config_path": "test/path",
}
@pytest.fixture
def session_run_data():
return {
"common_config": {
"session_id": "test_session",
"char_config": [
{"name": "仪玄"},
{"name": "耀嘉音"},
{"name": "扳机"},
],
"enemy_config": {"index_id": 11412, "adjustment_id": 22412},
"apl_path": "zsim/data/APLData/仪玄-耀嘉音-扳机.toml",
},
"mode": "normal",
}
@pytest.mark.asyncio
async def test_create_session(session_data):
db = await get_session_db()
await db.delete_session("test_session")
response = client.post("/api/sessions/", json=session_data)
assert response.status_code == 200
data = response.json()
assert data["session_id"] == "test_session"
@pytest.mark.asyncio
async def test_read_sessions(session_data):
db = await get_session_db()
await db.delete_session("test_session")
session = Session(**session_data)
await db.add_session(session)
response = client.get("/api/sessions/")
assert response.status_code == 200
data = response.json()
assert isinstance(data, list)
assert len(data) > 0
@pytest.mark.asyncio
async def test_read_session(session_data):
db = await get_session_db()
await db.delete_session("test_session")
session = Session(**session_data)
await db.add_session(session)
response = client.get(f"/api/sessions/{session_data['session_id']}")
assert response.status_code == 200
data = response.json()
assert data["session_id"] == session_data["session_id"]
@pytest.mark.asyncio
async def test_get_session_status(session_data):
db = await get_session_db()
await db.delete_session("test_session")
session = Session(**session_data)
await db.add_session(session)
response = client.get(f"/api/sessions/{session_data['session_id']}/status")
assert response.status_code == 200
data = response.json()
assert "status" in data
assert "result" in data
@pytest.mark.asyncio
async def test_run_session(session_data, session_run_data):
db = await get_session_db()
await db.delete_session("test_session")
session = Session(**session_data)
await db.add_session(session)
response = client.post(
f"/api/sessions/{session_data['session_id']}/run?test_mode=true", json=session_run_data
)
assert response.status_code == 200
data = response.json()
assert data["message"] == "Session started successfully"
# Check that the session status is now "completed"
response = client.get(f"/api/sessions/{session_data['session_id']}/status")
assert response.status_code == 200
data = response.json()
assert data["status"] == "completed"
@pytest.mark.asyncio
async def test_stop_session(session_data, session_run_data):
db = await get_session_db()
await db.delete_session("test_session")
session = Session(**session_data)
session.status = "running"
await db.add_session(session)
response = client.post(f"/api/sessions/{session_data['session_id']}/stop")
assert response.status_code == 200
data = response.json()
assert data["status"] == "stopped"
@pytest.mark.asyncio
async def test_update_session(session_data):
db = await get_session_db()
await db.delete_session("test_session")
session = Session(**session_data)
await db.add_session(session)
updated_data = session_data.copy()
updated_data["session_name"] = "Updated Test Session"
response = client.put(f"/api/sessions/{session_data['session_id']}", json=updated_data)
assert response.status_code == 200
data = response.json()
assert data["session_name"] == "Updated Test Session"
@pytest.mark.asyncio
async def test_delete_session(session_data):
db = await get_session_db()
await db.delete_session("test_session")
session = Session(**session_data)
await db.add_session(session)
response = client.delete(f"/api/sessions/{session_data['session_id']}")
assert response.status_code == 204
response = client.get(f"/api/sessions/{session_data['session_id']}")
assert response.status_code == 404
================================================
FILE: tests/api/test_uds.py
================================================
#!/usr/bin/env python3
"""
测试UDS功能的脚本
"""
import os
import platform
import subprocess
import sys
import time
from pathlib import Path
import requests
def test_uds_connection():
"""测试UDS连接功能"""
print("=" * 60)
print("测试UDS连接功能")
print("=" * 60)
# 检查系统平台
current_platform = platform.system()
print(f"当前系统平台: {current_platform}")
if current_platform == "Windows":
print("Windows系统不支持UDS,跳过测试")
return False
uds_path = "/tmp/zsim_api.sock"
print(f"UDS路径: {uds_path}")
# 清理旧的socket文件
if os.path.exists(uds_path):
print("清理旧的socket文件...")
os.unlink(uds_path)
# 启动后端服务器
print("启动后端服务器...")
backend_env = os.environ.copy()
backend_env.update({"ZSIM_IPC_MODE": "uds", "ZSIM_UDS_PATH": uds_path})
# 获取项目根目录
script_dir = Path(__file__).parent
backend_script = script_dir / "zsim" / "api.py"
print(f"后端脚本路径: {backend_script}")
if not backend_script.exists():
print(f"错误: 后端脚本不存在: {backend_script}")
return False
# 启动后端进程
backend_process = subprocess.Popen(
["python", str(backend_script)],
env=backend_env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
try:
# 等待服务器启动
print("等待服务器启动...")
time.sleep(5)
# 检查socket文件是否存在
if os.path.exists(uds_path):
print("✓ UDS socket文件已创建")
else:
print("✗ UDS socket文件未创建")
return False
# 测试健康检查端点
print("测试健康检查端点...")
# 使用requests的Unix Socket适配器
try:
import requests_unixsocket
session = requests_unixsocket.Session()
url = f"http+unix://{uds_path}:/health"
response = session.get(url, timeout=10)
if response.status_code == 200:
print(f"✓ 健康检查成功: {response.json()}")
else:
print(f"✗ 健康检查失败: {response.status_code}")
return False
except ImportError:
print("requests_unixsocket 未安装,使用curl测试...")
# 使用curl测试
import subprocess as sp
try:
result = sp.run(
["curl", "-s", "--unix-socket", uds_path, "http://localhost/health"],
capture_output=True,
text=True,
timeout=10,
)
if result.returncode == 0:
print(f"✓ 健康检查成功: {result.stdout}")
else:
print(f"✗ 健康检查失败: {result.stderr}")
return False
except sp.TimeoutExpired:
print("✗ 健康检查超时")
return False
# 测试API端点
print("测试API端点...")
try:
if "requests_unixsocket" in sys.modules:
url = f"http+unix://{uds_path}:/api/sessions/"
response = session.get(url, timeout=10)
if response.status_code == 200:
print("✓ API端点测试成功")
print(f" 响应: {response.json()}")
else:
print(f"✗ API端点测试失败: {response.status_code}")
return False
else:
# 使用curl测试
result = sp.run(
["curl", "-s", "--unix-socket", uds_path, "http://localhost/api/sessions/"],
capture_output=True,
text=True,
timeout=10,
)
if result.returncode == 0:
print("✓ API端点测试成功")
print(f" 响应: {result.stdout}")
else:
print(f"✗ API端点测试失败: {result.stderr}")
return False
except Exception as e:
print(f"✗ API端点测试异常: {e}")
return False
print("\n" + "=" * 60)
print("✓ 所有UDS测试通过")
print("=" * 60)
return True
except Exception as e:
print(f"✗ 测试失败: {e}")
return False
finally:
# 清理
print("清理进程...")
backend_process.terminate()
backend_process.wait(timeout=5)
if os.path.exists(uds_path):
os.unlink(uds_path)
def test_http_connection():
"""测试HTTP连接功能作为对比"""
print("\n" + "=" * 60)
print("测试HTTP连接功能")
print("=" * 60)
# 启动后端服务器
print("启动后端服务器...")
backend_env = os.environ.copy()
backend_env.update({"ZSIM_IPC_MODE": "http", "ZSIM_API_PORT": "8001"})
# 获取项目根目录
script_dir = Path(__file__).parent
backend_script = script_dir / "zsim" / "api.py"
# 启动后端进程
backend_process = subprocess.Popen(
["python", str(backend_script)],
env=backend_env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
try:
# 等待服务器启动
print("等待服务器启动...")
time.sleep(5)
# 测试健康检查端点
print("测试健康检查端点...")
response = requests.get("http://127.0.0.1:8001/health", timeout=10)
if response.status_code == 200:
print(f"✓ 健康检查成功: {response.json()}")
else:
print(f"✗ 健康检查失败: {response.status_code}")
return False
# 测试API端点
print("测试API端点...")
response = requests.get("http://127.0.0.1:8001/api/sessions/", timeout=10)
if response.status_code == 200:
print("✓ API端点测试成功")
print(f" 响应: {response.json()}")
else:
print(f"✗ API端点测试失败: {response.status_code}")
return False
print("\n" + "=" * 60)
print("✓ 所有HTTP测试通过")
print("=" * 60)
return True
except Exception as e:
print(f"✗ 测试失败: {e}")
return False
finally:
# 清理
print("清理进程...")
backend_process.terminate()
backend_process.wait(timeout=5)
def main():
"""主函数"""
print("ZSim UDS功能测试")
print("=" * 60)
# 测试HTTP连接
http_success = test_http_connection()
# 测试UDS连接
uds_success = test_uds_connection()
print("\n" + "=" * 60)
print("测试总结")
print("=" * 60)
print(f"HTTP测试: {'✓ 通过' if http_success else '✗ 失败'}")
print(f"UDS测试: {'✓ 通过' if uds_success else '✗ 失败'}")
if http_success and uds_success:
print("\n🎉 所有测试通过!")
return 0
else:
print("\n❌ 部分测试失败!")
return 1
if __name__ == "__main__":
sys.exit(main())
================================================
FILE: tests/conftest.py
================================================
import tempfile
from pathlib import Path
import pytest
@pytest.fixture
def temp_config_dir():
"""Create a temporary directory for test configuration files."""
with tempfile.TemporaryDirectory() as temp_dir:
yield Path(temp_dir)
@pytest.fixture
def mock_character_config():
"""Create minimal mock character configuration for testing."""
return {
"艾莲": {
"name": "艾莲",
"weapon": "深海访客",
"weapon_level": 1,
"cinema": 0,
"crit_balancing": False,
"scATK_percent": 10,
"scATK": 0,
"scHP_percent": 0,
"scHP": 0,
"scDEF_percent": 0,
"scDEF": 0,
"scAnomalyProficiency": 0,
"scPEN": 0,
"scCRIT": 10,
"scCRIT_DMG": 10,
"drive4": "攻击力%",
"drive5": "冰属性伤害%",
"drive6": "攻击力%",
"equip_style": "4+2",
"equip_set4": "啄木鸟电音",
"equip_set2_a": "极地重金属",
},
"苍角": {
"name": "苍角",
"weapon": "含羞恶面",
"weapon_level": 5,
"cinema": 2,
"crit_balancing": False,
"scATK_percent": 20,
"scATK": 0,
"scHP_percent": 0,
"scHP": 0,
"scDEF_percent": 0,
"scDEF": 0,
"scAnomalyProficiency": 0,
"scPEN": 0,
"scCRIT": 0,
"scCRIT_DMG": 0,
"drive4": "攻击力%",
"drive5": "攻击力%",
"drive6": "攻击力%",
"equip_style": "4+2",
"equip_set4": "自由蓝调",
"equip_set2_a": "灵魂摇滚",
},
}
@pytest.fixture
def mock_simulation_config():
"""Create minimal mock simulation configuration for testing."""
return {
"debug": {"enabled": False, "level": 4},
"stop_tick": 1000,
"watchdog": {"enabled": False, "level": 4},
"character": {"crit_balancing": True, "back_attack_rate": 1},
"enemy": {"index_ID": 11412, "adjust_ID": 22412, "difficulty": 8.74},
"apl_mode": {
"enabled": True,
"na_order": "./zsim/data/DefaultConfig/NAOrder.json",
"enemy_random_attack": False,
"enemy_regular_attack": False,
"enemy_attack_response": False,
"enemy_attack_method_config": "./zsim/data/enemy_attack_method.csv",
"enemy_attack_action_data": "./zsim/data/enemy_attack_action.csv",
"enemy_attack_report": False,
"player_level": 5,
"default_apl_dir": "./zsim/data/APLData",
"custom_apl_dir": "./zsim/data/APLData/custom",
},
"swap_cancel_mode": {
"enabled": True,
"completion_coefficient": 0.3,
"lag_time": 20,
"debug": False,
},
"database": {
"SQLITE_PATH": "./zsim/data/zsim.db",
"CHARACTER_DATA_PATH": "./zsim/data/character.csv",
"WEAPON_DATA_PATH": "./zsim/data/weapon.csv",
"EQUIP_2PC_DATA_PATH": "./zsim/data/equip_set_2pc.csv",
"SKILL_DATA_PATH": "./zsim/data/skill.csv",
"ENEMY_DATA_PATH": "./zsim/data/enemy.csv",
"ENEMY_ADJUSTMENT_PATH": "./zsim/data/enemy_adjustment.csv",
"DEFAULT_SKILL_PATH": "./zsim/data/default_skill.csv",
"JUDGE_FILE_PATH": "./zsim/data/触发判断.csv",
"EFFECT_FILE_PATH": "./zsim/data/buff_effect.csv",
"EXIST_FILE_PATH": "./zsim/data/激活判断.csv",
"APL_FILE_PATH": "./zsim/data/APLData/薇薇安-柳-耀嘉音.toml",
},
"buff_0_report": {"enabled": False},
"char_report": {
"Vivian": False,
"AstraYao": False,
"Hugo": False,
"Yixuan": False,
"Trigger": False,
"Yuzuha": False,
},
"parallel_mode": {"enabled": False, "adjust_char": 1},
"dev": {"new_sim_boot": True},
}
================================================
FILE: tests/simulator/__init__.py
================================================
# -*- coding: utf-8 -*-
"""模拟器测试模块"""
from .test_basic_simulator import TestBasicSimulator
from .test_parallel_mode import TestParallelMode
from .test_queue_system import TestQueueSystem
__all__ = ["TestBasicSimulator", "TestParallelMode", "TestQueueSystem"]
================================================
FILE: tests/simulator/safe_concurrent_teams.py
================================================
# -*- coding: utf-8 -*-
"""安全的并发队伍测试,通过进程隔离避免环境共享"""
import asyncio
import gc
import os
import uuid
from concurrent.futures import ProcessPoolExecutor, as_completed
from datetime import datetime
from pathlib import Path
import pytest
from zsim.api_src.services.database.session_db import get_session_db
from zsim.models.session.session_create import Session
# 导入团队配置
from ..teams import auto_register_teams
def run_simulation_in_process(common_cfg_dict, session_id, stop_tick=1000):
"""在独立进程中运行模拟
Args:
common_cfg_dict: 配置字典(避免序列化问题)
session_id: 会话ID
stop_tick: 停止tick数
Returns:
dict: 模拟结果
"""
try:
# 导入必要的模块(在子进程中)
from zsim.models.session.session_run import CommonCfg
from zsim.simulator.simulator_class import Simulator
# 重建配置对象
common_cfg = CommonCfg.model_validate(common_cfg_dict)
# 创建模拟器实例
simulator = Simulator()
# 运行模拟
result = simulator.api_run_simulator(common_cfg, None, stop_tick)
# 清理
del simulator
gc.collect()
return {"success": True, "session_id": session_id, "error": None}
except Exception as e:
return {"success": False, "session_id": session_id, "error": str(e)}
class TestSafeConcurrentTeams:
"""安全的并发队伍测试"""
@pytest.fixture(autouse=True)
def setup_test_environment(self):
"""Setup test environment before each test."""
self.original_cwd = os.getcwd()
os.chdir(Path(__file__).parent.parent.parent)
yield
os.chdir(self.original_cwd)
@pytest.mark.asyncio
async def test_teams_with_process_isolation(self):
"""使用进程隔离测试多个队伍"""
# 获取团队配置
team_registry = auto_register_teams()
team_configs = team_registry.get_all_team_configs()
if len(team_configs) < 2:
pytest.skip("需要至少2个队伍配置进行并发测试")
print(f"\n开始测试 {len(team_configs)} 个队伍的进程隔离并发执行...")
db = await get_session_db()
results = []
try:
# 使用进程池执行器
with ProcessPoolExecutor(max_workers=min(len(team_configs), 4)) as executor:
# 提交所有任务
future_to_team = {}
for i, (team_name, common_cfg) in enumerate(team_configs):
session_id = (
f"process-test-{uuid.uuid4().hex[:8]}-{team_name.replace(' ', '-')}"
)
# 创建会话
session = Session(
session_id=session_id,
create_time=datetime.now(),
status="pending",
session_run=common_cfg.model_copy(deep=True),
session_result=None,
)
await db.add_session(session)
print(f"提交队伍 '{team_name}' 到进程池 (session_id: {session_id})")
# 提交任务到进程池
future = executor.submit(
run_simulation_in_process, common_cfg.model_dump(), session_id, 1000
)
future_to_team[future] = (team_name, session_id)
# 等待所有任务完成
for future in as_completed(future_to_team):
team_name, session_id = future_to_team[future]
try:
result = future.result(timeout=60) # 60秒超时
if result["success"]:
print(f"队伍 '{team_name}' 模拟成功")
results.append(
{"team_name": team_name, "session_id": session_id, "success": True}
)
else:
print(f"队伍 '{team_name}' 模拟失败: {result['error']}")
results.append(
{
"team_name": team_name,
"session_id": session_id,
"success": False,
"error": result["error"],
}
)
except Exception as e:
print(f"队伍 '{team_name}' 执行异常: {e}")
results.append(
{
"team_name": team_name,
"session_id": session_id,
"success": False,
"error": str(e),
}
)
except Exception as e:
print(f"进程池执行错误: {e}")
raise
finally:
# 清理数据库
for result in results:
try:
await db.delete_session(result["session_id"])
except:
pass
# 验证结果
successful_teams = [r for r in results if r["success"]]
failed_teams = [r for r in results if not r["success"]]
print("\n=== 测试结果 ===")
print(f"总队伍数: {len(team_configs)}")
print(f"成功: {len(successful_teams)}")
print(f"失败: {len(failed_teams)}")
if failed_teams:
print("\n失败的队伍:")
for team in failed_teams:
print(f" - {team['team_name']}: {team.get('error', '未知错误')}")
# 验证所有队伍都成功
assert len(successful_teams) == len(team_configs), (
f"期望所有 {len(team_configs)} 个队伍都成功,但只有 {len(successful_teams)} 个成功"
)
print(f"\n所有 {len(team_configs)} 个队伍在进程隔离环境下成功完成!")
@pytest.mark.asyncio
async def test_teams_sequential_with_delay(self):
"""测试顺序执行队伍,添加延迟确保资源清理"""
team_registry = auto_register_teams()
team_configs = team_registry.get_all_team_configs()
results = []
for i, (team_name, common_cfg) in enumerate(team_configs):
print(f"\n执行队伍 {i + 1}/{len(team_configs)}: {team_name}")
session_id = f"sequential-delay-{i}-{team_name.replace(' ', '-')}"
try:
# 使用进程隔离运行
with ProcessPoolExecutor(max_workers=1) as executor:
future = executor.submit(
run_simulation_in_process, common_cfg.model_dump(), session_id, 1000
)
result = future.result(timeout=60)
if result["success"]:
print(f"队伍 '{team_name}' 成功")
results.append(team_name)
else:
print(f"队伍 '{team_name}' 失败: {result['error']}")
pytest.fail(f"队伍 '{team_name}' 模拟失败")
except Exception as e:
print(f"队伍 '{team_name}' 执行异常: {e}")
raise
# 添加延迟确保资源清理
await asyncio.sleep(1)
# 强制垃圾回收
gc.collect()
print(f"\n成功顺序执行了 {len(results)} 个队伍")
assert len(results) == len(team_configs)
@pytest.mark.asyncio
async def test_single_team_multiple_times(self):
"""多次运行同一个队伍,验证结果的一致性"""
team_registry = auto_register_teams()
if not team_configs:
pytest.skip("没有可用的团队配置")
team_name, common_cfg = team_configs[0]
print(f"\n多次测试队伍: {team_name}")
results = []
# 多次运行同一个队伍
for i in range(3):
print(f"第 {i + 1} 次运行")
session_id = f"multiple-test-{i}-{team_name.replace(' ', '-')}"
try:
with ProcessPoolExecutor(max_workers=1) as executor:
future = executor.submit(
run_simulation_in_process, common_cfg.model_dump(), session_id, 1000
)
result = future.result(timeout=60)
if result["success"]:
results.append(i + 1)
print(f"第 {i + 1} 次运行成功")
else:
print(f"第 {i + 1} 次运行失败: {result['error']}")
pytest.fail(f"第 {i + 1} 次运行失败")
except Exception as e:
print(f"第 {i + 1} 次运行异常: {e}")
raise
# 延迟
await asyncio.sleep(0.5)
print(f"\n队伍 '{team_name}' 的 {len(results)} 次运行都成功")
assert len(results) == 3
================================================
FILE: tests/simulator/test_basic_simulator.py
================================================
# -*- coding: utf-8 -*-
"""基础模拟器测试"""
from zsim.simulator.simulator_class import Simulator
# 使用标准的相对导入,IDE 可以识别
from ..test_simulator import TestSimulator
class TestBasicSimulator:
"""基础模拟器功能测试"""
def test_init_simulator_without_config(self):
"""Test that simulator can be initialized successfully."""
sim = Simulator()
assert isinstance(sim, Simulator)
assert hasattr(sim, "api_init_simulator")
assert hasattr(sim, "main_loop")
def test_simulator_reset(self):
"""Test that simulator can be reset to initial state."""
test_sim = TestSimulator()
# 使用原有的测试方法创建配置
common_cfg = test_sim.create_test_common_config()
sim = Simulator()
sim.api_init_simulator(common_cfg, sim_cfg=None)
assert sim.init_data is not None
assert sim.tick == 0
assert sim.char_data is not None
assert sim.enemy is not None
================================================
FILE: tests/simulator/test_isolated_teams.py
================================================
# -*- coding: utf-8 -*-
"""隔离的队伍测试,避免环境共享问题"""
import asyncio
import gc
import os
from datetime import datetime
from pathlib import Path
import pytest
from zsim.api_src.services.database.session_db import get_session_db
from zsim.api_src.services.sim_controller.sim_controller import SimController
from zsim.models.session.session_create import Session
from zsim.models.session.session_run import SessionRun
from zsim.simulator.simulator_class import Simulator
# 导入团队配置
from ..teams import auto_register_teams
class TestIsolatedTeams:
"""隔离的队伍测试类,确保每个队伍在独立环境中运行"""
@pytest.fixture(autouse=True)
def setup_test_environment(self):
"""Setup test environment before each test."""
# Store original working directory
self.original_cwd = os.getcwd()
# Change to project root directory
os.chdir(Path(__file__).parent.parent.parent)
yield
# Restore original working directory
os.chdir(self.original_cwd)
# Force garbage collection
gc.collect()
async def run_single_team_simulation(self, team_name: str, common_cfg, session_id: str):
"""运行单个队伍的模拟,确保环境隔离"""
# 创建独立的模拟器实例
simulator = Simulator()
# 初始化模拟器
simulator.api_init_simulator(common_cfg, sim_cfg=None)
# 运行模拟
result = simulator.api_run_simulator(common_cfg, None, 1000)
# 清理资源
del simulator
gc.collect()
return result
@pytest.mark.asyncio
async def test_teams_sequentially(self):
"""按顺序测试各个队伍,避免并发问题"""
# 获取所有团队配置
team_registry = auto_register_teams()
team_configs = team_registry.get_all_team_configs()
db = await get_session_db()
results = []
try:
# 逐个测试队伍
for i, (team_name, common_cfg) in enumerate(team_configs):
print(f"\n=== 开始测试队伍: {team_name} ===")
session_id = f"sequential-test-{i}-{team_name.replace(' ', '-')}"
# 记录开始时间
start_time = datetime.now()
# 运行单个队伍模拟
result = await self.run_single_team_simulation(team_name, common_cfg, session_id)
# 记录结束时间
end_time = datetime.now()
duration = (end_time - start_time).total_seconds()
print(f"队伍 '{team_name}' 模拟完成,耗时: {duration:.2f}秒")
# 验证结果
assert result is not None, f"队伍 '{team_name}' 模拟结果为空"
results.append(
{
"team_name": team_name,
"session_id": session_id,
"duration": duration,
"success": True,
}
)
print(f"=== 队伍 '{team_name}' 测试完成 ===\n")
except Exception as e:
print(f"测试过程中发生错误: {e}")
raise
finally:
# 清理数据库
for result in results:
try:
await db.delete_session(result["session_id"])
except:
pass
# 验证所有队伍都测试成功
assert len(results) == len(team_configs), (
f"期望测试 {len(team_configs)} 个队伍,实际测试了 {len(results)} 个"
)
# 输出测试结果摘要
print("\n=== 测试结果摘要 ===")
for result in results:
status = "成功" if result["success"] else "失败"
print(f"队伍: {result['team_name']}, 耗时: {result['duration']:.2f}秒, 状态: {status}")
print(f"\n所有 {len(team_configs)} 个队伍测试完成,无环境共享问题!")
@pytest.mark.asyncio
async def test_single_team_isolation(self):
"""测试单个队伍的隔离性"""
# 获取第一个团队配置
team_registry = auto_register_teams()
team_configs = team_registry.get_all_team_configs()
if not team_configs:
pytest.skip("没有可用的团队配置")
team_name, common_cfg = team_configs[0]
# 多次运行同一个队伍,确保每次都是独立的
results = []
for i in range(3):
print(f"第 {i + 1} 次运行队伍: {team_name}")
session_id = f"isolation-test-{i}-{team_name.replace(' ', '-')}"
result = await self.run_single_team_simulation(team_name, common_cfg, session_id)
assert result is not None, f"第 {i + 1} 次运行失败"
results.append(result)
# 验证每次运行都是独立的(结果应该相似但不完全相同)
assert len(results) == 3, "应该有3次运行结果"
print(f"队伍 '{team_name}' 的3次独立运行都成功完成")
@pytest.mark.asyncio
async def test_team_with_controller_cleanup(self):
"""使用控制器清理方法测试队伍"""
team_registry = auto_register_teams()
team_configs = team_registry.get_all_team_configs()
if not team_configs:
pytest.skip("没有可用的团队配置")
# 为每个队伍创建新的控制器实例
results = []
for i, (team_name, common_cfg) in enumerate(team_configs):
print(f"\n测试队伍: {team_name}")
# 创建新的控制器实例(避免单例)
controller = SimController()
# 重置控制器的内部状态
controller._queue = asyncio.Queue()
controller._running_tasks.clear()
session_id = f"controller-test-{i}-{team_name.replace(' ', '-')}"
# 创建会话
session_run_config = SessionRun(
stop_tick=1000,
mode="normal",
common_config=common_cfg,
)
session = Session(
session_id=session_id,
create_time=datetime.now(),
status="pending",
session_run=session_run_config,
session_result=None,
)
db = await get_session_db()
await db.add_session(session)
try:
# 放入队列
await controller.put_into_queue(session_id, common_cfg, None)
# 执行单个任务
completed_sessions = await controller.execute_simulation_test(max_tasks=1)
assert len(completed_sessions) == 1, f"队伍 '{team_name}' 应该完成1个任务"
assert completed_sessions[0] == session_id, "完成的会话ID不匹配"
results.append(team_name)
print(f"队伍 '{team_name}' 测试成功")
finally:
# 清理
await db.delete_session(session_id)
# 强制清理控制器
controller._queue = asyncio.Queue()
controller._running_tasks.clear()
del controller
gc.collect()
print(f"\n成功测试了 {len(results)} 个队伍,每个队伍都使用了独立的控制器实例")
assert len(results) == len(team_configs), "所有队伍都应该测试成功"
================================================
FILE: tests/simulator/test_parallel_mode.py
================================================
# -*- coding: utf-8 -*-
"""并行模式测试"""
import pytest
from pydantic import ValidationError
from zsim.api_src.services.sim_controller.sim_controller import SimController
from zsim.models.session.session_create import Session
from zsim.models.session.session_run import (
ExecAttrCurveCfg,
ExecWeaponCfg,
ParallelCfg,
SessionRun,
)
class TestParallelMode:
"""并行模式测试"""
def test_parallel_args_generation_attr_curve(self):
"""Test async generation of attribute curve parallel arguments."""
from ..test_simulator import TestSimulator
test_sim = TestSimulator()
controller = SimController()
session = Session()
session_run_config = test_sim.create_session_run_config("parallel")
# Generate parallel arguments
args_iterator = controller.generate_parallel_args(session, session_run_config)
args_list = list(args_iterator)
# Verify generated arguments
assert len(args_list) == 12 # 2 attributes × 6 values each
for arg in args_list:
assert isinstance(arg, ExecAttrCurveCfg)
assert arg.stop_tick == 1000
assert arg.mode == "parallel"
assert arg.func == "attr_curve"
def test_parallel_args_generation_weapon(self):
"""Test async generation of weapon parallel arguments."""
from ..test_simulator import TestSimulator
test_sim = TestSimulator()
controller = SimController()
session = Session()
# Create weapon parallel configuration
session_run_config = SessionRun(
stop_tick=1000,
mode="parallel",
common_config=test_sim.create_test_common_config(),
parallel_config=ParallelCfg(
enable=True,
adjust_char=2,
func="weapon",
func_config=ParallelCfg.WeaponConfig(
weapon_list=[
ParallelCfg.WeaponConfig.SingleWeapon(name="青溟笼舍", level=5),
ParallelCfg.WeaponConfig.SingleWeapon(name="时流贤者", level=5),
ParallelCfg.WeaponConfig.SingleWeapon(name="飞鸟星梦", level=1),
]
),
),
)
# Generate parallel arguments
args_iterator = controller.generate_parallel_args(session, session_run_config)
args_list = list(args_iterator)
# Verify generated arguments
assert len(args_list) == 3
for arg in args_list:
assert isinstance(arg, ExecWeaponCfg)
assert arg.stop_tick == 1000
assert arg.mode == "parallel"
assert arg.func == "weapon"
def test_parallel_args_generation_edge_cases(self):
"""Test parallel argument generation edge cases."""
from ..test_simulator import TestSimulator
test_sim = TestSimulator()
controller = SimController()
session = Session()
# Test unknown attribute names
session_run_config = SessionRun(
stop_tick=1000,
mode="parallel",
common_config=test_sim.create_test_common_config(),
parallel_config=ParallelCfg(
enable=True,
adjust_char=2,
func="attr_curve",
func_config=ParallelCfg.AttrCurveConfig(
sc_range=(0, 7),
sc_list=["unknown_stat"], # Unknown attribute
remove_equip_list=[],
),
),
)
# Generate parallel arguments, should skip unknown attributes
args_iterator = controller.generate_parallel_args(session, session_run_config)
args_list = list(args_iterator)
assert len(args_list) == 0 # Unknown attributes should be skipped
def test_parallel_args_generation_invalid_mode(self):
"""Test parallel argument generation with invalid mode."""
from ..test_simulator import TestSimulator
test_sim = TestSimulator()
controller = SimController()
session = Session()
session_run_config = test_sim.create_session_run_config("normal") # Normal mode
# Generate parallel arguments, should return empty
args_iterator = controller.generate_parallel_args(session, session_run_config)
args_list = list(args_iterator)
assert len(args_list) == 0
def test_parallel_args_generation_missing_config(self):
"""Test parallel argument generation with missing configuration."""
from ..test_simulator import TestSimulator
test_sim = TestSimulator()
with pytest.raises(ValidationError) as excinfo:
SessionRun(
stop_tick=1000,
mode="parallel",
common_config=test_sim.create_test_common_config(),
# Missing parallel_config
)
assert "并行模式下,parallel_config 不能为空" in str(excinfo.value)
================================================
FILE: tests/simulator/test_queue_system.py
================================================
# -*- coding: utf-8 -*-
"""队列系统测试"""
from datetime import datetime
import pytest
from zsim.api_src.services.sim_controller.sim_controller import SimController
from zsim.models.session.session_create import Session
class TestQueueSystem:
"""队列系统测试"""
@pytest.mark.asyncio
async def test_async_queue_multiple_teams(self):
"""使用队列系统测试多个不同队伍的异步模拟,替代单队伍测试。
注意:此测试可能存在环境共享问题,建议使用 test_isolated_teams.py
中的隔离测试方法进行多队伍测试。
"""
from zsim.api_src.services.database.session_db import get_session_db
from ..teams import auto_register_teams
from ..test_simulator import TestSimulator
test_sim = TestSimulator()
controller = SimController()
db = await get_session_db()
# 使用团队配置注册器获取配置
team_registry = auto_register_teams()
team_configs = team_registry.get_all_team_configs()
completed_sessions = []
try:
# 为每个队伍创建会话并放入队列
for i, (team_name, common_cfg) in enumerate(team_configs):
session_run_config = test_sim.create_session_run_config("normal")
session_run_config.stop_tick = 3000
session = Session(
session_id=f"queue-test-team-{i}-{team_name.replace(' ', '-')}",
create_time=datetime.now(),
status="pending",
session_run=session_run_config,
session_result=None,
)
completed_sessions.append(session.session_id)
await db.add_session(session)
# 将队伍配置放入队列
await controller.put_into_queue(session.session_id, common_cfg, None)
print(f"队伍 '{team_name}' 已添加到队列")
# 执行所有队伍的模拟(注意:这里可能存在环境共享问题)
print(f"开始执行 {len(team_configs)} 个队伍的模拟...")
print("警告:此测试可能存在环境共享问题,建议使用隔离测试方法")
# 由于 execute_simulation_test 方法的限制,我们需要分批处理队伍
# 该方法每次只处理 max_tasks 数量的任务,所以需要多次调用
executed_sessions = []
batch_size = 2 # 每批处理2个队伍以避免环境共享问题
for i in range(0, len(team_configs), batch_size):
batch_executed = await controller.execute_simulation_test(max_tasks=batch_size)
executed_sessions.extend(batch_executed)
# 验证结果
assert len(executed_sessions) == len(team_configs), (
f"期望执行 {len(team_configs)} 个队伍,实际执行了 {len(executed_sessions)} 个"
)
assert set(executed_sessions) == set(completed_sessions), "执行的会话ID与预期不匹配"
# 验证所有会话状态
for session_id in completed_sessions:
updated_session = await db.get_session(session_id)
assert updated_session is not None, f"会话 {session_id} 未找到"
assert updated_session.status == "completed", (
f"会话 {session_id} 状态不是 completed"
)
print(f"所有 {len(team_configs)} 个队伍模拟均已完成")
finally:
# 清理数据库
for session_id in completed_sessions:
await db.delete_session(session_id)
@pytest.mark.asyncio
async def test_async_queue_parallel_mode_execution(self):
"""使用队列系统测试并行模式执行。"""
from zsim.api_src.services.database.session_db import get_session_db
from zsim.models.session.session_run import ParallelCfg
from ..test_simulator import TestSimulator
test_sim = TestSimulator()
common_cfg = test_sim.create_test_common_config()
controller = SimController()
# 创建简化的并行配置以减少任务数量
session_run_config = test_sim.create_session_run_config("parallel")
session_run_config.stop_tick = 100 # 减少执行时间
session_run_config.parallel_config = ParallelCfg(
enable=True,
adjust_char=2,
func="attr_curve",
func_config=ParallelCfg.AttrCurveConfig(
sc_range=(0, 1), # 只测试2个值
sc_list=["攻击力%"], # 单个属性
remove_equip_list=[],
),
)
session = Session(
session_id="queue-test-parallel-session",
create_time=datetime.now(),
status="pending",
session_run=session_run_config,
session_result=None,
)
# 设置数据库
db = await get_session_db()
await db.add_session(session)
parallel_session_ids = [] # 初始化在try块外面
try:
# 生成并行参数并放入队列
args_iterator = controller.generate_parallel_args(session, session_run_config)
args_list = list(args_iterator)
# 为每个并行任务创建单独的会话并放入队列
for i, sim_cfg in enumerate(args_list):
parallel_session_id = f"queue-test-parallel-session-{i}"
parallel_session = Session(
session_id=parallel_session_id,
create_time=datetime.now(),
status="pending",
session_run=session_run_config,
session_result=None,
)
parallel_session_ids.append(parallel_session_id)
await db.add_session(parallel_session)
await controller.put_into_queue(parallel_session_id, common_cfg, sim_cfg)
# 执行并行任务
completed_sessions = await controller.execute_simulation_test_parallel(
session.session_id, parallel_count=len(args_list)
)
# 验证结果 - 应该有2个并行任务完成
assert len(completed_sessions) == 2
assert set(completed_sessions) == set(parallel_session_ids)
# 验证所有并行会话状态
for parallel_session_id in parallel_session_ids:
updated_session = await db.get_session(parallel_session_id)
assert updated_session is not None
assert updated_session.status == "completed"
finally:
# 清理数据库
await db.delete_session(session.session_id)
for parallel_session_id in parallel_session_ids:
await db.delete_session(parallel_session_id)
@pytest.mark.asyncio
async def test_async_queue_empty_handling(self):
"""测试队列为空时的处理。"""
controller = SimController()
# 执行空队列
completed_sessions = await controller.execute_simulation_test(max_tasks=1)
# 应该返回空列表
assert len(completed_sessions) == 0
@pytest.mark.asyncio
async def test_async_queue_error_handling(self):
"""测试队列系统的错误处理。"""
from ..test_simulator import TestSimulator
test_sim = TestSimulator()
controller = SimController()
# 创建一个无效的会话(不存在于数据库中)
common_cfg = test_sim.create_test_common_config()
await controller.put_into_queue("non-existent-session", common_cfg, None)
# 执行应该能处理错误而不崩溃
completed_sessions = await controller.execute_simulation_test(max_tasks=1)
# 应该返回空列表(因为会话不存在)
assert len(completed_sessions) == 0
================================================
FILE: tests/teams/__init__.py
================================================
# -*- coding: utf-8 -*-
"""团队配置模块"""
from .electric_teams import ElectricTeamConfigs
from .fire_teams import FireTeamConfigs
from .ice_teams import IceTeamConfigs
from .physical_teams import PhysicalTeamConfigs
from .team_configs import TeamConfigBase, TeamRegistry, auto_register_teams
__all__ = [
"PhysicalTeamConfigs",
"FireTeamConfigs",
"ElectricTeamConfigs",
"TeamConfigBase",
"TeamRegistry",
"auto_register_teams",
]
================================================
FILE: tests/teams/electric_teams.py
================================================
# -*- coding: utf-8 -*-
"""雷属性队伍配置"""
from zsim.models.session.session_run import CharConfig, CommonCfg, EnemyConfig
from .team_configs import TeamConfigBase, TeamRegistry
class ElectricTeamQingyiConfig(TeamConfigBase):
"""青衣雷属性队配置"""
def __init__(self):
super().__init__(team_name="青衣雷属性队", description="青衣-丽娜-雅雷属性队伍")
def create_config(self) -> CommonCfg:
"""创建青衣雷属性队配置"""
return CommonCfg(
session_id="test-team-qingyi-electric",
char_config=[
CharConfig(
name="青衣",
weapon="玉壶青冰",
weapon_level=5,
cinema=6,
scATK_percent=47,
scCRIT=30,
scCRIT_DMG=50,
equip_style="4+2",
equip_set4="震星迪斯科",
equip_set2_a="啄木鸟电音",
),
CharConfig(
name="丽娜",
weapon="啜泣摇篮",
weapon_level=5,
cinema=6,
scATK_percent=47,
scCRIT=30,
scCRIT_DMG=50,
equip_style="4+2",
equip_set4="静听嘉音",
equip_set2_a="摇摆爵士",
),
CharConfig(
name="雅",
weapon="霰落星殿",
weapon_level=5,
cinema=6,
scATK_percent=47,
scCRIT=30,
scCRIT_DMG=50,
equip_style="4+2",
equip_set4="折枝剑歌",
equip_set2_a="啄木鸟电音",
),
],
enemy_config=EnemyConfig(index_id=11412, adjustment_id=22412, difficulty=8.74),
apl_path="./zsim/data/APLData/青衣-丽娜-雅.toml",
)
def get_expected_characters(self) -> list:
"""获取预期的角色列表"""
return ["青衣", "丽娜", "雅"]
class ElectricTeamSeedZeroAnbiConfig(TeamConfigBase):
"""席德大安比队伍"""
def __init__(self):
super().__init__(team_name="席德大安比队", description="席德-大安比-扳机队伍")
# TODO:扳机的影画目前只支持到1画,不能给高!
def create_config(self) -> CommonCfg:
return CommonCfg(
session_id="test-team-seed-zeroanbi-electric",
char_config=[
CharConfig(
name="席德",
weapon="机巧心种",
weapon_level=5,
cinema=6,
scATK_percent=47,
scCRIT=30,
scCRIT_DMG=50,
equip_style="4+2",
equip_set4="拂晓生花",
equip_set2_a="啄木鸟电音",
),
CharConfig(
name="零号·安比",
weapon="牺牲洁纯",
weapon_level=5,
cinema=6,
scATK_percent=30,
scCRIT=30,
scCRIT_DMG=50,
equip_style="4+2",
equip_set4="如影相随",
equip_set2_a="拂晓生花",
),
CharConfig(
name="扳机",
weapon="索魂影眸",
weapon_level=5,
cinema=1,
scATK_percent=47,
scCRIT=30,
scCRIT_DMG=50,
equip_style="4+2",
equip_set4="如影相随",
equip_set2_a="折枝剑歌",
),
],
enemy_config=EnemyConfig(index_id=11412, adjustment_id=22412, difficulty=8.74),
apl_path="./zsim/data/APLData/席德-大安比-扳机.toml",
)
def get_expected_characters(self) -> list:
"""获取预期的角色列表"""
return ["席德", "零号·安比", "扳机"]
class ElectricTeamConfigs:
"""雷属性队伍配置集合"""
@staticmethod
def register_all():
"""注册所有雷属性队伍配置"""
TeamRegistry.register(ElectricTeamQingyiConfig())
TeamRegistry.register(ElectricTeamSeedZeroAnbiConfig())
@staticmethod
def get_qingyi_team() -> ElectricTeamQingyiConfig:
"""获取青衣雷属性队配置"""
return ElectricTeamQingyiConfig()
@staticmethod
def get_seed_zeroanbi_team() -> ElectricTeamSeedZeroAnbiConfig:
"""获取席德大安比队伍配置"""
return ElectricTeamSeedZeroAnbiConfig()
@staticmethod
def get_all_configs() -> list:
"""获取所有雷属性队伍配置"""
return [ElectricTeamConfigs.get_qingyi_team(), ElectricTeamConfigs.get_seed_zeroanbi_team()]
# 自动注册
ElectricTeamConfigs.register_all()
================================================
FILE: tests/teams/fire_teams.py
================================================
# -*- coding: utf-8 -*-
"""火属性队伍配置"""
from zsim.models.session.session_run import CharConfig, CommonCfg, EnemyConfig
from .team_configs import TeamConfigBase, TeamRegistry
class FireTeamLighterConfig(TeamConfigBase):
"""莱特火属性队配置"""
def __init__(self):
super().__init__(team_name="莱特火属性队", description="莱特-扳机-雨果火属性队伍")
def create_config(self) -> CommonCfg:
"""创建莱特火属性队配置"""
return CommonCfg(
session_id="test-team-lighter-fire",
char_config=[
CharConfig(
name="莱特",
weapon="焰心桂冠",
weapon_level=5,
cinema=0,
scATK_percent=47,
scCRIT=30,
scCRIT_DMG=50,
equip_style="4+2",
equip_set4="震星迪斯科",
equip_set2_a="炎狱重金属",
),
CharConfig(
name="扳机",
weapon="索魂影眸",
weapon_level=5,
cinema=0,
scATK_percent=47,
scCRIT=30,
scCRIT_DMG=50,
equip_style="4+2",
equip_set4="如影相随",
equip_set2_a="啄木鸟电音",
),
CharConfig(
name="雨果",
weapon="千面日陨",
weapon_level=5,
cinema=0,
scATK_percent=47,
scCRIT=30,
scCRIT_DMG=50,
equip_style="4+2",
equip_set4="啄木鸟电音",
equip_set2_a="激素朋克",
),
],
enemy_config=EnemyConfig(index_id=11412, adjustment_id=22412, difficulty=8.74),
apl_path="./zsim/data/APLData/莱特-扳机-雨果.toml",
)
def get_expected_characters(self) -> list:
"""获取预期的角色列表"""
return ["莱特", "扳机", "雨果"]
class FireTeamConfigs:
"""火属性队伍配置集合"""
@staticmethod
def register_all():
"""注册所有火属性队伍配置"""
TeamRegistry.register(FireTeamLighterConfig())
@staticmethod
def get_lighter_team() -> FireTeamLighterConfig:
"""获取莱特火属性队配置"""
return FireTeamLighterConfig()
@staticmethod
def get_all_configs() -> list:
"""获取所有火属性队伍配置"""
return [FireTeamConfigs.get_lighter_team()]
# 自动注册
FireTeamConfigs.register_all()
================================================
FILE: tests/teams/ice_teams.py
================================================
# -*- coding: utf-8 -*-
"""冰属性队伍配置"""
from zsim.models.session.session_run import CharConfig, CommonCfg, EnemyConfig
from .team_configs import TeamConfigBase, TeamRegistry
class IceTeamExampleConfig(TeamConfigBase):
"""示例冰属性队配置"""
def __init__(self):
super().__init__(team_name="示例冰属性队", description="角色1-角色2-角色3冰属性队伍")
def create_config(self) -> CommonCfg:
"""创建示例冰属性队配置"""
return CommonCfg(
session_id="test-team-ice-example",
char_config=[
CharConfig(
name="角色1",
weapon="武器1",
weapon_level=5,
cinema=6,
scATK_percent=47,
scCRIT=30,
scCRIT_DMG=50,
equip_style="4+2",
equip_set4="套装4",
equip_set2_a="套装2",
),
CharConfig(
name="角色2",
weapon="武器2",
weapon_level=5,
cinema=6,
scATK_percent=47,
scCRIT=30,
scCRIT_DMG=50,
equip_style="4+2",
equip_set4="套装4",
equip_set2_a="套装2",
),
CharConfig(
name="角色3",
weapon="武器3",
weapon_level=5,
cinema=6,
scATK_percent=47,
scCRIT=30,
scCRIT_DMG=50,
equip_style="4+2",
equip_set4="套装4",
equip_set2_a="套装2",
),
],
enemy_config=EnemyConfig(index_id=11412, adjustment_id=22412, difficulty=8.74),
apl_path="./zsim/data/APLData/冰属性队伍.toml",
)
def get_expected_characters(self) -> list:
"""获取预期的角色列表"""
return ["角色1", "角色2", "角色3"]
class IceTeamConfigs:
"""冰属性队伍配置集合"""
@staticmethod
def register_all():
"""注册所有冰属性队伍配置"""
TeamRegistry.register(IceTeamExampleConfig())
@staticmethod
def get_example_team() -> IceTeamExampleConfig:
"""获取示例冰属性队配置"""
return IceTeamExampleConfig()
@staticmethod
def get_all_configs() -> list:
"""获取所有冰属性队伍配置"""
return [IceTeamConfigs.get_example_team()]
# # 自动注册
# IceTeamConfigs.register_all()
================================================
FILE: tests/teams/physical_teams.py
================================================
# -*- coding: utf-8 -*-
"""物理队伍配置"""
from zsim.models.session.session_run import CharConfig, CommonCfg, EnemyConfig
from .team_configs import TeamConfigBase, TeamRegistry
class PhysicalTeamVivianConfig(TeamConfigBase):
"""薇薇安物理队配置"""
def __init__(self):
super().__init__(team_name="薇薇安物理队", description="薇薇安-柳-耀嘉音物理属性队伍")
def create_config(self) -> CommonCfg:
"""创建薇薇安物理队配置"""
return CommonCfg(
session_id="test-team-vivian-physical",
char_config=[
CharConfig(
name="薇薇安",
weapon="青溟笼舍",
weapon_level=5,
cinema=6,
scATK_percent=47,
scCRIT=30,
scCRIT_DMG=50,
equip_style="4+2",
equip_set4="自由蓝调",
equip_set2_a="灵魂摇滚",
),
CharConfig(
name="柳",
weapon="时流贤者",
weapon_level=5,
cinema=6,
scATK_percent=47,
scCRIT=30,
scCRIT_DMG=50,
equip_style="4+2",
equip_set4="自由蓝调",
equip_set2_a="灵魂摇滚",
),
CharConfig(
name="耀嘉音",
weapon="飞鸟星梦",
weapon_level=1,
cinema=6,
scATK_percent=47,
scCRIT=30,
scCRIT_DMG=50,
equip_style="4+2",
equip_set4="自由蓝调",
equip_set2_a="灵魂摇滚",
),
],
enemy_config=EnemyConfig(index_id=11412, adjustment_id=22412, difficulty=8.74),
apl_path="./zsim/data/APLData/薇薇安-柳-耀嘉音.toml",
)
def get_expected_characters(self) -> list:
"""获取预期的角色列表"""
return ["薇薇安", "柳", "耀嘉音"]
class PhysicalTeamConfigs:
"""物理队伍配置集合"""
@staticmethod
def register_all():
"""注册所有物理队伍配置"""
TeamRegistry.register(PhysicalTeamVivianConfig())
@staticmethod
def get_vivian_team() -> PhysicalTeamVivianConfig:
"""获取薇薇安物理队配置"""
return PhysicalTeamVivianConfig()
@staticmethod
def get_all_configs() -> list:
"""获取所有物理队伍配置"""
return [PhysicalTeamConfigs.get_vivian_team()]
# 自动注册
PhysicalTeamConfigs.register_all()
================================================
FILE: tests/teams/team_configs.py
================================================
# -*- coding: utf-8 -*-
"""团队配置基础类和注册器"""
from abc import ABC, abstractmethod
from typing import Any, Dict, List, Tuple
from zsim.models.session.session_run import CommonCfg
class TeamConfigBase(ABC):
"""团队配置基类"""
def __init__(self, team_name: str, description: str = ""):
self.team_name = team_name
self.description = description
@abstractmethod
def create_config(self) -> CommonCfg:
"""创建团队配置,子类必须实现此方法"""
pass
@abstractmethod
def get_expected_characters(self) -> List[str]:
"""获取预期的角色列表,子类必须实现此方法"""
pass
def get_team_info(self) -> Dict[str, Any]:
"""获取团队信息"""
return {
"team_name": self.team_name,
"description": self.description,
"characters": self.get_expected_characters(),
}
class TeamRegistry:
"""团队配置注册器"""
_instance = None
_teams: Dict[str, TeamConfigBase] = {}
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
@classmethod
def register(cls, team_config: TeamConfigBase):
"""注册团队配置"""
cls._teams[team_config.team_name] = team_config
@classmethod
def get_team(cls, team_name: str) -> TeamConfigBase:
"""获取团队配置"""
return cls._teams.get(team_name)
@classmethod
def get_all_teams(cls) -> List[TeamConfigBase]:
"""获取所有团队配置"""
return list(cls._teams.values())
@classmethod
def get_all_team_configs(cls) -> List[Tuple[str, CommonCfg]]:
"""获取所有团队的配置元组列表"""
configs = []
for team in cls.get_all_teams():
try:
config = team.create_config()
configs.append((team.team_name, config))
except Exception as e:
print(f"创建团队 '{team.team_name}' 配置失败: {e}")
return configs
@classmethod
def get_teams_by_attribute(cls, attribute: str) -> List[TeamConfigBase]:
"""根据属性获取团队配置"""
return [team for team in cls.get_all_teams() if attribute.lower() in team.team_name.lower()]
@classmethod
def list_team_names(cls) -> List[str]:
"""获取所有团队名称"""
return list(cls._teams.keys())
def auto_register_teams():
"""自动注册所有团队配置"""
# 导入所有团队配置模块以触发注册
# 返回注册器实例
return TeamRegistry()
================================================
FILE: tests/teams/usage_example.py
================================================
# -*- coding: utf-8 -*-
"""团队配置使用示例"""
from .physical_teams import PhysicalTeamConfigs
from .team_configs import TeamRegistry
def example_usage():
"""团队配置使用示例"""
# 1. 获取所有团队配置
all_teams = TeamRegistry.get_all_teams()
print(f"总共注册了 {len(all_teams)} 个队伍")
# 2. 按属性查找队伍
physical_teams = TeamRegistry.get_teams_by_attribute("物理")
fire_teams = TeamRegistry.get_teams_by_attribute("火")
electric_teams = TeamRegistry.get_teams_by_attribute("雷")
print(f"物理队伍: {[team.team_name for team in physical_teams]}")
print(f"火属性队伍: {[team.team_name for team in fire_teams]}")
print(f"雷属性队伍: {[team.team_name for team in electric_teams]}")
# 3. 直接获取特定队伍
vivian_team = PhysicalTeamConfigs.get_vivian_team()
print(f"薇薇安队伍信息: {vivian_team.get_team_info()}")
# 4. 创建新队伍配置示例
class ExampleNewTeamConfig:
"""示例:如何创建新的队伍配置"""
def create_new_team_config(self):
"""创建新队伍配置的示例"""
from zsim.models.session.session_run import CommonCfg
from .team_configs import TeamConfigBase
class NewIceTeamConfig(TeamConfigBase):
def __init__(self):
super().__init__(team_name="冰属性新队伍", description="冰属性队伍示例")
def create_config(self) -> CommonCfg:
# 这里实现具体的配置逻辑
pass
def get_expected_characters(self) -> list:
return ["冰角色1", "冰角色2", "冰角色3"]
# 注册新队伍
TeamRegistry.register(NewIceTeamConfig())
# 5. 批量获取所有配置用于测试
all_team_configs = TeamRegistry.get_all_team_configs()
print(f"获取到 {len(all_team_configs)} 个队伍配置用于测试")
for team_name, config in all_team_configs:
print(f"队伍: {team_name}, 角色: {[char.name for char in config.char_config]}")
if __name__ == "__main__":
example_usage()
================================================
FILE: tests/test_buff.py
================================================
# import pytest
# import pandas as pd
# from zsim.sim_progress.Buff import Buff
# from zsim.define import JUDGE_FILE_PATH, EXIST_FILE_PATH
#
# EXIST_FILE = pd.read_csv(EXIST_FILE_PATH, index_col="BuffName")
# JUDGE_FILE = pd.read_csv(JUDGE_FILE_PATH, index_col="BuffName")
#
#
# @pytest.fixture(
# params=[
# "NonExistentBuff", # 仅测试非法输入
# ]
# )
# def invalid_buff_index(request):
# return request.param
#
#
# @pytest.fixture(
# params=[
# "Buff-武器-精1街头巨星-终结技增伤", # 仅测试合法输入
# ]
# )
# def valid_buff_index(request):
# return request.param
#
#
# @pytest.fixture(
# params=[
# "Buff-武器-精1街头巨星-终结技增伤", # 仅测试合法输入
# ]
# )
# def complex_buff_index(request):
# """待测试的复杂Buff名"""
# return request.param
#
#
# @pytest.fixture
# def buff(valid_buff_index):
# dict_1 = EXIST_FILE.loc[valid_buff_index].to_dict()
# if "BuffName" not in dict_1:
# dict_1["BuffName"] = valid_buff_index
# dict_2 = JUDGE_FILE.loc[valid_buff_index].to_dict()
# return Buff(dict_1, dict_2)
#
#
# class TestBuff:
# def test_invalid_buff(self, invalid_buff_index):
# """测试非法不存在的Buff名能否抛出异常"""
# with pytest.raises(KeyError):
# EXIST_FILE.loc[invalid_buff_index]
#
# def test_valid_buff(self, buff, valid_buff_index):
# """测试合法的Buff名能否让Buff类实例化成功"""
# assert buff.ft.index == valid_buff_index
#
# def test_xjudge_init(self, buff):
# if buff.ft.simple_judge_logic:
# assert buff.logic.xjudge is None
# else:
# assert buff.logic.xjudge is not None
#
# def test_xjudge_judge(self, xjudge_buff, test_node):
# pass
================================================
FILE: tests/test_simulator.py
================================================
import asyncio
import gc
import os
from pathlib import Path
import pytest
from pydantic import ValidationError
from zsim.api_src.services.sim_controller.sim_controller import SimController
from zsim.models.session.session_create import Session
from zsim.models.session.session_run import (
CharConfig,
CommonCfg,
EnemyConfig,
ExecAttrCurveCfg,
ExecWeaponCfg,
ParallelCfg,
SessionRun,
)
from zsim.simulator.simulator_class import Simulator
# 导入团队配置
from .teams import auto_register_teams
class TestSimulator:
"""Comprehensive test suite for simulator functionality."""
@pytest.fixture(autouse=True)
def setup_test_environment(self):
"""Setup test environment before each test."""
# Store original working directory
self.original_cwd = os.getcwd()
# Change to project root directory
os.chdir(Path(__file__).parent.parent)
yield
# Restore original working directory
os.chdir(self.original_cwd)
def create_test_common_config(self) -> CommonCfg:
"""Create a test common configuration."""
return CommonCfg(
session_id="test-session-001",
char_config=[
CharConfig(
name="薇薇安",
weapon="青溟笼舍",
weapon_level=5,
cinema=6,
scATK_percent=47,
scCRIT=30,
scCRIT_DMG=50,
equip_style="4+2",
equip_set4="自由蓝调",
equip_set2_a="灵魂摇滚",
),
CharConfig(
name="柳",
weapon="时流贤者",
weapon_level=5,
cinema=6,
scATK_percent=47,
scCRIT=30,
scCRIT_DMG=50,
equip_style="4+2",
equip_set4="自由蓝调",
equip_set2_a="灵魂摇滚",
),
CharConfig(
name="耀嘉音",
weapon="飞鸟星梦",
weapon_level=1,
cinema=6,
scATK_percent=47,
scCRIT=30,
scCRIT_DMG=50,
equip_style="4+2",
equip_set4="自由蓝调",
equip_set2_a="灵魂摇滚",
),
],
enemy_config=EnemyConfig(index_id=11412, adjustment_id=22412, difficulty=8.74),
apl_path="./zsim/data/APLData/薇薇安-柳-耀嘉音.toml",
)
def create_session_run_config(self, mode: str = "normal") -> SessionRun:
"""Create session run configuration."""
if mode == "parallel":
return SessionRun(
stop_tick=1000,
mode="parallel",
common_config=self.create_test_common_config(),
parallel_config=ParallelCfg(
enable=True,
adjust_char=2,
func="attr_curve",
func_config=ParallelCfg.AttrCurveConfig(
sc_range=(0, 5),
sc_list=["攻击力%", "暴击率"],
remove_equip_list=[],
),
),
)
else:
return SessionRun(
stop_tick=1000,
mode="normal",
common_config=self.create_test_common_config(),
)
def create_multiple_team_configs(self) -> list[tuple[str, CommonCfg]]:
"""创建多个测试队伍配置,使用团队配置注册器。
Returns:
list[tuple[str, CommonCfg]]: 包含队伍名称和配置的元组列表
"""
# 初始化团队配置注册器
team_registry = auto_register_teams()
# 获取所有团队配置
return team_registry.get_all_team_configs()
# Basic Simulator Tests
def test_init_simulator_without_config(self):
"""Test that simulator can be initialized successfully."""
sim = Simulator()
assert isinstance(sim, Simulator)
assert hasattr(sim, "api_init_simulator")
assert hasattr(sim, "main_loop")
def test_simulator_reset(self):
"""Test that simulator can be reset to initial state."""
common_cfg = self.create_test_common_config()
sim = Simulator()
sim.api_init_simulator(common_cfg, sim_cfg=None)
assert sim.init_data is not None
assert sim.tick == 0
assert sim.char_data is not None
assert sim.enemy is not None
# Async Tests
@pytest.mark.asyncio
async def test_async_simulator_initialization(self):
"""Test async simulator initialization."""
controller = SimController()
assert isinstance(controller, SimController)
# Parallel Mode Tests
@pytest.mark.asyncio
async def test_parallel_args_generation_attr_curve(self):
"""Test async generation of attribute curve parallel arguments."""
controller = SimController()
session = Session()
session_run_config = self.create_session_run_config("parallel")
# Generate parallel arguments
args_iterator = controller.generate_parallel_args(session, session_run_config)
args_list = list(args_iterator)
# Verify generated arguments
assert len(args_list) == 12 # 2 attributes × 6 values each
for arg in args_list:
assert isinstance(arg, ExecAttrCurveCfg)
assert arg.stop_tick == 1000
assert arg.mode == "parallel"
assert arg.func == "attr_curve"
@pytest.mark.asyncio
async def test_parallel_args_generation_weapon(self):
"""Test async generation of weapon parallel arguments."""
controller = SimController()
session = Session()
# Create weapon parallel configuration
session_run_config = SessionRun(
stop_tick=1000,
mode="parallel",
common_config=self.create_test_common_config(),
parallel_config=ParallelCfg(
enable=True,
adjust_char=2,
func="weapon",
func_config=ParallelCfg.WeaponConfig(
weapon_list=[
ParallelCfg.WeaponConfig.SingleWeapon(name="青溟笼舍", level=5),
ParallelCfg.WeaponConfig.SingleWeapon(name="时流贤者", level=5),
ParallelCfg.WeaponConfig.SingleWeapon(name="飞鸟星梦", level=1),
]
),
),
)
# Generate parallel arguments
args_iterator = controller.generate_parallel_args(session, session_run_config)
args_list = list(args_iterator)
# Verify generated arguments
assert len(args_list) == 3
for arg in args_list:
assert isinstance(arg, ExecWeaponCfg)
assert arg.stop_tick == 1000
assert arg.mode == "parallel"
assert arg.func == "weapon"
# Configuration Validation Tests
def test_session_run_config_validation(self):
"""Test session run configuration validation."""
# Test normal mode
config = self.create_session_run_config("normal")
assert config.mode == "normal"
assert config.stop_tick == 1000
# Test parallel mode
config = self.create_session_run_config("parallel")
assert config.mode == "parallel"
assert config.parallel_config is not None
assert config.parallel_config.func == "attr_curve"
def test_character_config_validation(self):
"""Test character configuration validation."""
# Test valid configuration (3 characters)
valid_config = CommonCfg(
session_id="test-valid-config",
char_config=[
CharConfig(name="薇薇安"),
CharConfig(name="柳"),
CharConfig(name="耀嘉音"),
],
enemy_config=EnemyConfig(index_id=11412, adjustment_id=22412, difficulty=8.74),
apl_path="./zsim/data/APLData/薇薇安-柳-耀嘉音.toml",
)
sim = Simulator()
sim.api_init_simulator(valid_config, sim_cfg=None)
assert sim.init_data is not None
# Test invalid configuration (2 characters)
with pytest.raises(Exception):
invalid_config = CommonCfg(
session_id="test-invalid-config",
char_config=[
CharConfig(name="薇薇安"),
CharConfig(name="柳"),
],
enemy_config=EnemyConfig(index_id=11412, adjustment_id=22412, difficulty=8.74),
apl_path="./zsim/data/APLData/薇薇安-柳-耀嘉音.toml",
)
sim_invalid = Simulator()
sim_invalid.api_init_simulator(invalid_config, sim_cfg=None)
# Edge Cases and Error Handling
def test_parallel_args_generation_edge_cases(self):
"""Test parallel argument generation edge cases."""
controller = SimController()
session = Session()
# Test unknown attribute names
session_run_config = SessionRun(
stop_tick=1000,
mode="parallel",
common_config=self.create_test_common_config(),
parallel_config=ParallelCfg(
enable=True,
adjust_char=2,
func="attr_curve",
func_config=ParallelCfg.AttrCurveConfig(
sc_range=(0, 7),
sc_list=["unknown_stat"], # Unknown attribute
remove_equip_list=[],
),
),
)
# Generate parallel arguments, should skip unknown attributes
args_iterator = controller.generate_parallel_args(session, session_run_config)
args_list = list(args_iterator)
assert len(args_list) == 0 # Unknown attributes should be skipped
def test_parallel_args_generation_invalid_mode(self):
"""Test parallel argument generation with invalid mode."""
controller = SimController()
session = Session()
session_run_config = self.create_session_run_config("normal") # Normal mode
# Generate parallel arguments, should return empty
args_iterator = controller.generate_parallel_args(session, session_run_config)
args_list = list(args_iterator)
assert len(args_list) == 0
def test_parallel_args_generation_missing_config(self):
"""Test parallel argument generation with missing configuration."""
with pytest.raises(ValidationError) as excinfo:
SessionRun(
stop_tick=1000,
mode="parallel",
common_config=self.create_test_common_config(),
# Missing parallel_config
)
assert "并行模式下,parallel_config 不能为空" in str(excinfo.value)
# Data Transmission Tests
def test_data_transmission_correctness(self):
"""Test that data is correctly transmitted between components."""
common_cfg = self.create_test_common_config()
sim = Simulator()
sim.api_init_simulator(common_cfg, sim_cfg=None)
# Verify character data transmission
assert len(sim.init_data.name_box) == 3
assert sim.init_data.name_box == ["薇薇安", "柳", "耀嘉音"]
# Verify character configuration transmission
for i, char_config in enumerate(common_cfg.char_config):
char_dict = getattr(sim.init_data, f"char_{i}")
assert char_dict["name"] == char_config.name
assert char_dict["weapon"] == char_config.weapon
assert char_dict["weapon_level"] == char_config.weapon_level
# Verify enemy configuration transmission
assert sim.enemy.index_ID == common_cfg.enemy_config.index_id
assert sim.enemy.adjustment_id == int(common_cfg.enemy_config.adjustment_id)
assert sim.enemy.difficulty == common_cfg.enemy_config.difficulty
# Verify APL path transmission
assert sim.preload.apl_path == common_cfg.apl_path
def test_weapon_adjustment_with_sim_cfg(self):
"""Test that weapon adjustment works correctly with simulation configuration."""
# Create configuration using Pydantic models
common_cfg = self.create_test_common_config()
# Create weapon adjustment configuration
sim_cfg = ExecWeaponCfg(
stop_tick=1000,
mode="parallel",
func="weapon",
adjust_char=1, # Adjust first character (Vivian)
weapon_name="青溟笼舍",
weapon_level=3,
run_turn_uuid="test-weapon-adjustment",
)
sim = Simulator()
sim.api_init_simulator(common_cfg, sim_cfg)
# Verify weapon adjustment
# First character's weapon should be adjusted
char_0_dict = sim.init_data.char_0
assert char_0_dict["weapon"] == "青溟笼舍"
assert char_0_dict["weapon_level"] == 3
# Other characters' weapons should remain unchanged
char_1_dict = sim.init_data.char_1
assert char_1_dict["weapon"] == "时流贤者"
assert char_1_dict["weapon_level"] == 5
# SimController Singleton Test
def test_sim_controller_singleton(self):
"""Test SimController singleton pattern."""
controller1 = SimController()
controller2 = SimController()
assert controller1 is controller2
# 队列基础的异步测试
@pytest.mark.asyncio
async def test_async_queue_multiple_teams(self):
"""使用队列系统测试多个不同队伍的异步模拟,替代单队伍测试。"""
from datetime import datetime
from zsim.api_src.services.database.session_db import get_session_db
from zsim.models.session.session_create import Session
controller = SimController()
db = await get_session_db()
# 获取所有队伍配置
team_configs = self.create_multiple_team_configs()
completed_sessions = []
try:
# 为每个队伍创建会话并放入队列
for i, (team_name, common_cfg) in enumerate(team_configs):
session_run_config = self.create_session_run_config("normal")
session_run_config.stop_tick = 3000
session = Session(
session_id=f"queue-test-team-{i}-{team_name.replace(' ', '-')}",
create_time=datetime.now(),
status="pending",
session_run=session_run_config,
session_result=None,
)
completed_sessions.append(session.session_id)
await db.add_session(session)
# 将队伍配置放入队列
await controller.put_into_queue(session.session_id, common_cfg, None)
print(f"队伍 '{team_name}' 已添加到队列")
# 执行所有队伍的模拟
print(f"开始执行 {len(team_configs)} 个队伍的模拟...")
executed_sessions = await controller.execute_simulation_test(
max_tasks=len(team_configs)
)
# 验证结果
assert len(executed_sessions) == len(team_configs), (
f"期望执行 {len(team_configs)} 个队伍,实际执行了 {len(executed_sessions)} 个"
)
assert set(executed_sessions) == set(completed_sessions), "执行的会话ID与预期不匹配"
# 验证所有会话状态
for session_id in completed_sessions:
updated_session = await db.get_session(session_id)
assert updated_session is not None, f"会话 {session_id} 未找到"
assert updated_session.status == "completed", (
f"会话 {session_id} 状态不是 completed"
)
print(f"所有 {len(team_configs)} 个队伍模拟均已完成")
finally:
# 清理数据库
for session_id in completed_sessions:
await db.delete_session(session_id)
@pytest.mark.asyncio
async def test_async_queue_parallel_mode_execution(self):
"""使用队列系统测试并行模式执行。"""
from datetime import datetime
from zsim.api_src.services.database.session_db import get_session_db
from zsim.models.session.session_create import Session
common_cfg = self.create_test_common_config()
controller = SimController()
# 创建简化的并行配置以减少任务数量
session_run_config = SessionRun(
stop_tick=100, # 减少执行时间
mode="parallel",
common_config=common_cfg,
parallel_config=ParallelCfg(
enable=True,
adjust_char=2,
func="attr_curve",
func_config=ParallelCfg.AttrCurveConfig(
sc_range=(0, 1), # 只测试2个值
sc_list=["攻击力%"], # 单个属性
remove_equip_list=[],
),
),
)
session = Session(
session_id="queue-test-parallel-session",
create_time=datetime.now(),
status="pending",
session_run=session_run_config,
session_result=None,
)
# 设置数据库
db = await get_session_db()
await db.add_session(session)
parallel_session_ids = [] # 初始化在try块外面
try:
# 生成并行参数并放入队列
args_iterator = controller.generate_parallel_args(session, session_run_config)
args_list = list(args_iterator)
# 为每个并行任务创建单独的会话并放入队列
for i, sim_cfg in enumerate(args_list):
parallel_session_id = f"queue-test-parallel-session-{i}"
parallel_session = Session(
session_id=parallel_session_id,
create_time=datetime.now(),
status="pending",
session_run=session_run_config,
session_result=None,
)
parallel_session_ids.append(parallel_session_id)
await db.add_session(parallel_session)
await controller.put_into_queue(parallel_session_id, common_cfg, sim_cfg)
# 执行并行任务
completed_sessions = await controller.execute_simulation_test_parallel(
session.session_id, parallel_count=len(args_list)
)
# 验证结果 - 应该有2个并行任务完成
assert len(completed_sessions) == 2
assert set(completed_sessions) == set(parallel_session_ids)
# 验证所有并行会话状态
for parallel_session_id in parallel_session_ids:
updated_session = await db.get_session(parallel_session_id)
assert updated_session is not None
assert updated_session.status == "completed"
finally:
# 清理数据库
await db.delete_session(session.session_id)
for parallel_session_id in parallel_session_ids:
await db.delete_session(parallel_session_id)
@pytest.mark.asyncio
async def test_async_queue_empty_handling(self):
"""测试队列为空时的处理。"""
controller = SimController()
# 执行空队列
completed_sessions = await controller.execute_simulation_test(max_tasks=1)
# 应该返回空列表
assert len(completed_sessions) == 0
@pytest.mark.asyncio
async def test_async_queue_error_handling(self):
"""测试队列系统的错误处理。"""
controller = SimController()
# 创建一个无效的会话(不存在于数据库中)
common_cfg = self.create_test_common_config()
await controller.put_into_queue("non-existent-session", common_cfg, None)
# 执行应该能处理错误而不崩溃
completed_sessions = await controller.execute_simulation_test(max_tasks=1)
# 应该返回空列表(因为会话不存在)
assert len(completed_sessions) == 0
@pytest.mark.skip(reason="Known memory leak in pandas and simulator core.")
@pytest.mark.asyncio
async def test_async_queue_memory_leak(self):
"""Checks for memory leaks by running a simulation multiple times."""
import tracemalloc
from datetime import datetime
from zsim.api_src.services.database.session_db import get_session_db
from zsim.models.session.session_create import Session
common_cfg = self.create_test_common_config()
controller = SimController()
db = await get_session_db()
# Reset controller state for a clean test environment
controller._queue = asyncio.Queue()
controller._running_tasks.clear()
tracemalloc.start(10)
gc.collect()
async def run_one_sim(run_id: str):
"""Runs a single simulation in an isolated environment."""
session_id = f"memory-leak-test-{run_id}"
session_run_config = self.create_session_run_config("normal")
session_run_config.stop_tick = 200
session = Session(
session_id=session_id,
create_time=datetime.now(),
status="pending",
session_run=session_run_config,
)
await db.add_session(session)
# Create and destroy simulator within the run
local_controller = SimController()
await local_controller.put_into_queue(session.session_id, common_cfg, None)
completed_sessions = await local_controller.execute_simulation_test(max_tasks=1)
assert len(completed_sessions) == 1
await db.delete_session(session.session_id)
# Explicit cleanup
del local_controller
gc.collect()
# Warm-up run
await run_one_sim("warmup")
# Take snapshot after warm-up
snapshot1 = tracemalloc.take_snapshot()
# Run multiple times to detect leaks
for i in range(5):
await run_one_sim(f"run-{i}")
# Final snapshot
snapshot2 = tracemalloc.take_snapshot()
tracemalloc.stop()
top_stats = snapshot2.compare_to(snapshot1, "lineno")
total_growth = sum(stat.size_diff for stat in top_stats)
# Final threshold adjustment to 1024 KB
threshold_kb = 1024
assert total_growth < threshold_kb * 1024, (
f"Potential memory leak detected. "
f"Memory grew by {total_growth / 1024:.2f} KB after 5 runs.\n"
f"Top 10 differences:\n" + "\n".join([str(s) for s in top_stats[:10]])
)
@pytest.mark.asyncio
async def test_async_simulation_memory_usage(self):
"""Test async simulation memory usage."""
common_cfg = self.create_test_common_config()
# Force garbage collection
gc.collect()
initial_objects = len(gc.get_objects())
# Use SimController for async execution
controller = SimController()
result = await controller.run_single_simulation(common_cfg, None, 1000) # noqa: F841
gc.collect()
final_objects = len(gc.get_objects())
object_growth = final_objects - initial_objects
assert object_growth < 10000 # Reasonable threshold
================================================
FILE: zsim/__init__.py
================================================
================================================
FILE: zsim/api.py
================================================
"""
此处为api入口文件,负责启动FastAPI应用,不要在这里定义路由或写其他业务逻辑。
所有路由应在api_src/routes目录下定义。
业务逻辑应在api_src/service目录下实现。
请确保在运行此文件时,FastAPI能够正确加载所有路由和服务。
"""
import os
import platform
import dotenv
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from zsim.define import __version__
dotenv.load_dotenv()
app = FastAPI(
title="ZSim API",
description="ZSim API for simulation management and control",
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
if os.getenv("ZSIM_DISABLE_ROUTES") != "1":
from zsim.api_src.routes import (
router as api_router,
) # defer import to avoid side effects in tests
app.include_router(api_router, prefix="/api", tags=["ZSim API"])
@app.get("/health")
async def health_check():
"""
Health check endpoint for the ZSim API.
Returns:
dict: A simple message indicating the API is running.
"""
return {"message": "ZSim API is running!"}
@app.get("/version")
async def get_version():
"""
Get the current version of the ZSim API.
Returns:
dict: A dictionary containing the version string.
"""
return {"version": __version__}
if __name__ == "__main__":
import logging
import multiprocessing
import socket
import sys
import uvicorn
multiprocessing.freeze_support()
# 添加调试信息
logging.info(f"API version: {__version__}")
def get_free_port():
"""获取一个可用的端口号"""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(("", 0))
s.listen(1)
port = s.getsockname()[1]
return port
# 获取IPC模式,支持 http 和 uds
ipc_mode = os.getenv("ZSIM_IPC_MODE", "auto").lower()
# 在Unix类系统上默认使用uds,Windows上使用http
if ipc_mode == "auto":
ipc_mode = "uds" if platform.system() != "Windows" else "http"
if ipc_mode == "uds" and platform.system() != "Windows":
# UDS模式
uds_path = os.getenv("ZSIM_UDS_PATH", "/tmp/zsim_api.sock")
# 清理旧的socket文件
if os.path.exists(uds_path):
os.unlink(uds_path)
if getattr(sys, "frozen", False):
uvicorn.run(
app,
uds=uds_path,
log_level="info",
access_log=True,
workers=1,
)
else:
uvicorn.run(
"zsim.api:app",
uds=uds_path,
log_level="info",
reload=True,
access_log=True,
)
else:
# HTTP模式
try:
port = int(os.getenv("ZSIM_API_PORT", 0))
except ValueError:
logging.error("Invalid port number in ZSIM_API_PORT environment variable.")
port = 0
if port == 0:
port = get_free_port()
host = os.getenv("ZSIM_API_HOST", "127.0.0.1")
if getattr(sys, "frozen", False):
uvicorn.run(
app,
host=host,
port=port,
log_level="info",
access_log=True,
workers=1,
)
else:
uvicorn.run(
"zsim.api:app",
host=host,
port=port,
log_level="info",
reload=True,
access_log=True,
)
================================================
FILE: zsim/api_src/models/__init__.py
================================================
"""
API模型包初始化文件
"""
================================================
FILE: zsim/api_src/models/apl.py
================================================
"""
APL相关Pydantic模型
定义APL API请求和响应的数据模型
"""
from typing import Generic, TypeVar
from pydantic import BaseModel, Field
class APLGeneralInfo(BaseModel):
"""APL通用信息模型"""
title: str = Field(..., description="APL标题")
comment: str | None = Field(None, description="APL注释")
author: str | None = Field(None, description="APL作者")
create_time: str | None = Field(None, description="创建时间")
latest_change_time: str | None = Field(None, description="最后修改时间")
class APLCharacterConfig(BaseModel):
"""APL角色配置模型"""
cinema: list[int] | None = Field(None, description="角色影画等级")
weapon: str | None = Field(None, description="角色武器")
equip_set4: str | None = Field(None, description="四件套装备")
class APLCharactersInfo(BaseModel):
"""APL角色信息模型"""
required: list[str] | None = Field(None, description="必须角色列表")
optional: list[str] | None = Field(None, description="可选角色列表")
class Config:
extra = "allow" # 允许动态字段用于存储各个角色的具体配置
class APLLogicInfo(BaseModel):
"""APL逻辑信息模型"""
logic: str = Field(..., description="APL逻辑代码")
class APLTemplateInfo(BaseModel):
"""APL模板信息模型"""
id: str = Field(..., description="模板ID")
title: str = Field(..., description="模板标题")
author: str | None = Field(None, description="模板作者")
comment: str | None = Field(None, description="模板注释")
create_time: str | None = Field(None, description="创建时间")
latest_change_time: str | None = Field(None, description="最后修改时间")
source: str = Field(..., description="模板来源 (default/custom)")
file_path: str = Field(..., description="文件路径")
class APLFileInfo(BaseModel):
"""APL文件信息模型"""
id: str = Field(..., description="文件ID")
name: str = Field(..., description="文件名")
path: str = Field(..., description="相对路径")
source: str = Field(..., description="文件来源 (default/custom)")
full_path: str = Field(..., description="完整文件路径")
class APLFileContent(BaseModel):
"""APL文件内容模型"""
file_id: str = Field(..., description="文件ID")
content: str = Field(..., description="文件内容")
file_path: str = Field(..., description="文件路径")
class APLConfigCreateRequest(BaseModel):
"""创建APL配置请求模型"""
title: str = Field(..., description="APL标题")
comment: str | None = Field(None, description="APL注释")
author: str | None = Field(None, description="APL作者")
characters: APLCharactersInfo | None = Field(None, description="角色配置")
apl_logic: APLLogicInfo | None = Field(None, description="APL逻辑")
class APLConfigUpdateRequest(BaseModel):
"""更新APL配置请求模型"""
title: str | None = Field(None, description="APL标题")
comment: str | None = Field(None, description="APL注释")
author: str | None = Field(None, description="APL作者")
characters: APLCharactersInfo | None = Field(None, description="角色配置")
apl_logic: APLLogicInfo | None = Field(None, description="APL逻辑")
class APLFileCreateRequest(BaseModel):
"""创建APL文件请求模型"""
name: str = Field(..., description="文件名")
content: str = Field(..., description="文件内容")
class APLFileUpdateRequest(BaseModel):
"""更新APL文件请求模型"""
content: str = Field(..., description="文件内容")
class APLValidateRequest(BaseModel):
"""APL语法验证请求模型"""
apl_code: str = Field(..., description="APL代码")
class APLParseRequest(BaseModel):
"""APL代码解析请求模型"""
apl_code: str = Field(..., description="APL代码")
class APLValidateResponse(BaseModel):
"""APL语法验证响应模型"""
valid: bool = Field(..., description="语法是否有效")
message: str | None = Field(None, description="验证消息")
errors: list[str] | None = Field(None, description="错误列表")
class APLParseAction(BaseModel):
"""APL解析动作模型"""
line: int = Field(..., description="行号")
character: str = Field(..., description="角色")
action_type: str = Field(..., description="动作类型")
action_id: str = Field(..., description="动作ID")
conditions: list[str] = Field(..., description="条件列表")
class APLParseResponse(BaseModel):
"""APL代码解析响应模型"""
parsed: bool = Field(..., description="是否解析成功")
actions: list[APLParseAction] | None = Field(None, description="解析的动作列表")
error: str | None = Field(None, description="错误信息")
T = TypeVar("T")
class APIResponse(BaseModel, Generic[T]):
"""通用API响应模型"""
code: int = Field(..., description="响应码")
message: str = Field(..., description="响应消息")
data: T | None = Field(None, description="响应数据")
================================================
FILE: zsim/api_src/services/apl_service.py
================================================
"""
APL业务逻辑服务
负责APL相关业务逻辑处理
"""
from typing import Any
from ..models.apl import (
APLConfigCreateRequest,
APLConfigUpdateRequest,
APLFileContent,
APLFileCreateRequest,
APLFileInfo,
APLFileUpdateRequest,
APLParseResponse,
APLTemplateInfo,
APLValidateResponse,
)
from .database.apl_db import APLDatabase
class APLService:
"""APL业务逻辑服务类"""
def __init__(self):
"""初始化APL服务"""
self.db = APLDatabase()
def get_apl_templates(self) -> list[APLTemplateInfo]:
"""获取APL模板列表"""
templates = self.db.get_apl_templates()
return [APLTemplateInfo(**template) for template in templates]
def get_apl_config(self, config_id: str) -> dict[str, Any] | None:
"""获取特定APL配置"""
return self.db.get_apl_config(config_id)
def create_apl_config(self, config_data: APLConfigCreateRequest) -> dict[str, Any]:
"""创建新的APL配置"""
config_dict = config_data.model_dump()
# 验证配置数据
if not self._validate_apl_config(config_dict):
raise ValueError("Invalid APL configuration data")
config_id = self.db.create_apl_config(config_dict)
return {"config_id": config_id, "message": "APL configuration created successfully"}
def update_apl_config(
self, config_id: str, config_data: APLConfigUpdateRequest
) -> dict[str, Any]:
"""更新APL配置"""
config_dict = config_data.model_dump()
# 验证配置数据
if not self._validate_apl_config(config_dict):
raise ValueError("Invalid APL configuration data")
success = self.db.update_apl_config(config_id, config_dict)
if success:
return {"config_id": config_id, "message": "APL configuration updated successfully"}
else:
raise ValueError("Failed to update APL configuration")
def delete_apl_config(self, config_id: str) -> dict[str, Any]:
"""删除APL配置"""
success = self.db.delete_apl_config(config_id)
if success:
return {"config_id": config_id, "message": "APL configuration deleted successfully"}
else:
raise ValueError("Failed to delete APL configuration")
def get_apl_files(self) -> list[APLFileInfo]:
"""获取所有APL文件列表"""
files = self.db.get_apl_files()
return [APLFileInfo(**file) for file in files]
def get_apl_file_content(self, file_id: str) -> APLFileContent:
"""获取APL文件内容"""
content = self.db.get_apl_file_content(file_id)
if content is not None:
return APLFileContent(**content)
else:
raise ValueError("APL file not found")
def create_apl_file(self, file_data: APLFileCreateRequest) -> dict[str, Any]:
"""创建新的APL文件"""
# APL文件创建不需要验证APL配置数据,因为这是创建文件而不是配置
file_id = self.db.create_apl_file(file_data.model_dump())
return {"file_id": file_id, "message": "APL file created successfully"}
def update_apl_file(self, file_id: str, content: str) -> dict[str, Any]:
"""更新APL文件内容"""
file_data = APLFileUpdateRequest(content=content)
success = self.db.update_apl_file(file_id, file_data.content)
if success:
return {"file_id": file_id, "message": "APL file updated successfully"}
else:
raise ValueError("Failed to update APL file")
def delete_apl_file(self, file_id: str) -> dict[str, Any]:
"""删除APL文件"""
success = self.db.delete_apl_file(file_id)
if success:
return {"file_id": file_id, "message": "APL file deleted successfully"}
else:
raise ValueError("Failed to delete APL file")
def validate_apl_syntax(self, apl_code: str) -> APLValidateResponse:
"""验证APL语法"""
# 实现APL语法验证逻辑
# 这里需要根据APL的具体语法规则来实现
try:
# 简单的语法检查示例
lines = apl_code.strip().split("\n")
errors = []
for i, line in enumerate(lines, 1):
line = line.rstrip() # 只去掉右边的空白字符,保留左边的缩进
# 跳过空行和注释行
if not line or line.lstrip().startswith("#"):
continue
# 检查基本格式:动作角色|动作类型|动作ID|条件...
parts = line.split("|")
if len(parts) < 3:
errors.append(f"Line {i}: Invalid APL format, expected at least 3 parts")
continue
# 验证各部分不为空
character, action_type, action_id = parts[0], parts[1], parts[2]
if not character.strip():
errors.append(f"Line {i}: Character name cannot be empty")
if not action_type.strip():
errors.append(f"Line {i}: Action type cannot be empty")
if not action_id.strip():
errors.append(f"Line {i}: Action ID cannot be empty")
if errors:
return APLValidateResponse(valid=False, message=None, errors=errors)
else:
return APLValidateResponse(valid=True, message="APL syntax is valid", errors=None)
except Exception as e:
return APLValidateResponse(
valid=False, message=None, errors=[f"Syntax validation error: {str(e)}"]
)
def parse_apl_code(self, apl_code: str) -> APLParseResponse:
"""解析APL代码"""
# 实现APL代码解析逻辑
try:
# 简单的解析示例
lines = apl_code.strip().split("\n")
parsed_actions = []
for i, line in enumerate(lines, 1):
line = line.rstrip() # 只去掉右边的空白字符,保留左边的缩进
# 跳过空行和注释行
if not line or line.lstrip().startswith("#"):
continue
# 解析基本格式:动作角色|动作类型|动作ID|条件...
parts = line.split("|")
if len(parts) >= 3:
action = {
"line": i,
"character": parts[0].strip(),
"action_type": parts[1].strip(),
"action_id": parts[2].strip(),
"conditions": [part.strip() for part in parts[3:]]
if len(parts) > 3
else [],
}
parsed_actions.append(action)
return APLParseResponse(parsed=True, actions=parsed_actions, error=None)
except Exception as e:
return APLParseResponse(
parsed=False, actions=None, error=f"APL parsing error: {str(e)}"
)
def _validate_apl_config(self, config_data: dict[str, Any]) -> bool:
"""验证APL配置数据"""
# 实现APL配置数据验证逻辑
# 检查必需字段 - title必须存在,即使为空字符串
if "title" not in config_data:
return False
# 验证通用信息字段,如果缺失则设为空字符串
general_fields = ["title", "comment", "author"]
for field in general_fields:
if field not in config_data or config_data[field] is None:
config_data[field] = ""
# 检查角色配置
if "characters" in config_data:
characters = config_data["characters"]
if not isinstance(characters, dict):
return False
# 验证角色配置中的cinema字段格式
# 根据模板,cinema可以是int或list[int]
for char_name, char_config in characters.items():
if char_name in ["required", "optional"]:
continue # 跳过required和optional字段
if isinstance(char_config, dict) and "cinema" in char_config:
cinema = char_config["cinema"]
# cinema可以是None, int, 或list[int]
if cinema is not None:
# 如果是单个整数,转换为列表
if isinstance(cinema, int):
char_config["cinema"] = [cinema]
# 如果是列表,检查所有元素都是整数且在有效范围内(0-6)
elif isinstance(cinema, list):
for c in cinema:
if not isinstance(c, int) or c < 0 or c > 6:
return False
else:
# 不是int也不是list,无效格式
return False
# 检查APL逻辑
if "apl_logic" in config_data:
apl_logic = config_data["apl_logic"]
if not isinstance(apl_logic, dict) or "logic" not in apl_logic:
return False
return True
def export_apl_config(self, config_id: str, file_path: str) -> bool:
"""导出APL配置到TOML文件"""
return self.db.export_apl_config(config_id, file_path)
def import_apl_config(self, file_path: str) -> str | None:
"""从TOML文件导入APL配置"""
return self.db.import_apl_config(file_path)
================================================
FILE: zsim/api_src/services/database/apl_db.py
================================================
"""
APL数据库服务
负责APL相关数据的数据库操作
"""
from __future__ import annotations
import asyncio
import os
import tomllib
import uuid
from datetime import datetime
from typing import Any
import tomli_w
from sqlalchemy import String, Text, delete, select
from sqlalchemy.orm import Mapped, mapped_column
from zsim.api_src.services.database.orm import Base, get_async_engine, get_async_session
from zsim.define import COSTOM_APL_DIR, DEFAULT_APL_DIR
class APLConfigORM(Base):
__tablename__ = "apl_configs"
id: Mapped[str] = mapped_column(String(64), primary_key=True)
title: Mapped[str] = mapped_column(String(255), nullable=False)
author: Mapped[str | None] = mapped_column(String(255), nullable=True)
comment: Mapped[str | None] = mapped_column(Text, nullable=True)
create_time: Mapped[str] = mapped_column(String(32), nullable=False)
latest_change_time: Mapped[str] = mapped_column(String(32), nullable=False)
content: Mapped[str] = mapped_column(Text, nullable=False)
class APLDatabase:
"""APL数据库操作类"""
def __init__(self) -> None:
"""初始化APL数据库实例"""
self._initialized = False
async def _ensure_initialized(self) -> None:
"""确保数据库元数据已创建"""
if self._initialized:
return
async with get_async_engine().begin() as conn:
await conn.run_sync(Base.metadata.create_all)
self._initialized = True
def get_apl_templates(self) -> list[dict[str, Any]]:
"""获取所有APL模板。
Returns:
list[dict[str, Any]]: 模板信息列表。
"""
templates = []
templates.extend(self._get_apl_from_dir(DEFAULT_APL_DIR, "default"))
templates.extend(self._get_apl_from_dir(COSTOM_APL_DIR, "custom"))
return templates
def get_apl_config(self, config_id: str) -> dict[str, Any] | None:
"""获取特定APL配置。
Args:
config_id (str): APL配置ID。
Returns:
dict[str, Any] | None: APL配置内容,未找到时返回None。
"""
if not config_id or not isinstance(config_id, str):
return None
return asyncio.get_event_loop().run_until_complete(self._get_apl_config_async(config_id))
async def _get_apl_config_async(self, config_id: str) -> dict[str, Any] | None:
"""异步获取特定APL配置。
Args:
config_id (str): APL配置ID。
Returns:
dict[str, Any] | None: APL配置内容,未找到时返回None。
"""
await self._ensure_initialized()
async with get_async_session() as session:
result = await session.execute(select(APLConfigORM).where(APLConfigORM.id == config_id))
record = result.scalar_one_or_none()
if record is None:
return None
content = tomllib.loads(record.content)
return {
"title": record.title,
"author": record.author,
"comment": record.comment,
"create_time": record.create_time,
"latest_change_time": record.latest_change_time,
**content,
}
def create_apl_config(self, config_data: dict[str, Any]) -> str:
"""创建新的APL配置。
Args:
config_data (dict[str, Any]): APL配置数据。
Returns:
str: 新建配置的ID。
Raises:
Exception: 当写入数据库失败时抛出。
"""
if not config_data or not isinstance(config_data, dict):
raise ValueError("配置数据不能为空且必须是字典类型")
config_id = str(uuid.uuid4())
asyncio.get_event_loop().run_until_complete(
self._create_apl_config_async(config_id, config_data)
)
return config_id
async def _create_apl_config_async(self, config_id: str, config_data: dict[str, Any]) -> None:
"""异步创建APL配置。
Args:
config_id (str): 新配置ID。
config_data (dict[str, Any]): APL配置数据。
"""
await self._ensure_initialized()
current_time = datetime.now().isoformat()
title = config_data.get("title", "")
author = config_data.get("author", "")
comment = config_data.get("comment", "")
content_data = config_data.copy()
content_data.pop("title", None)
content_data.pop("author", None)
content_data.pop("comment", None)
content_data.pop("create_time", None)
content_data.pop("latest_change_time", None)
content = tomli_w.dumps(content_data)
async with get_async_session() as session:
session.add(
APLConfigORM(
id=config_id,
title=title,
author=author,
comment=comment,
create_time=current_time,
latest_change_time=current_time,
content=content,
)
)
await session.commit()
def update_apl_config(self, config_id: str, config_data: dict[str, Any]) -> bool:
"""更新APL配置。
Args:
config_id (str): APL配置ID。
config_data (dict[str, Any]): 更新后的数据。
Returns:
bool: 更新成功返回True,否则False。
"""
if not config_id or not isinstance(config_id, str):
return False
if not config_data or not isinstance(config_data, dict):
return False
return asyncio.get_event_loop().run_until_complete(
self._update_apl_config_async(config_id, config_data)
)
async def _update_apl_config_async(self, config_id: str, config_data: dict[str, Any]) -> bool:
"""异步更新APL配置。
Args:
config_id (str): APL配置ID。
config_data (dict[str, Any]): 更新后的数据。
Returns:
bool: 更新成功返回True,否则False。
"""
await self._ensure_initialized()
async with get_async_session() as session:
result = await session.execute(select(APLConfigORM).where(APLConfigORM.id == config_id))
record = result.scalar_one_or_none()
if record is None:
return False
latest_change_time = datetime.now().isoformat()
record.title = config_data.get("title", "")
record.author = config_data.get("author", "")
record.comment = config_data.get("comment", "")
record.latest_change_time = latest_change_time
content_data = config_data.copy()
content_data.pop("title", None)
content_data.pop("author", None)
content_data.pop("comment", None)
content_data.pop("create_time", None)
content_data.pop("latest_change_time", None)
record.content = tomli_w.dumps(content_data)
await session.flush()
await session.commit()
return True
def delete_apl_config(self, config_id: str) -> bool:
"""删除APL配置。
Args:
config_id (str): APL配置ID。
Returns:
bool: 删除成功返回True,否则False。
"""
if not config_id or not isinstance(config_id, str):
return False
return asyncio.get_event_loop().run_until_complete(self._delete_apl_config_async(config_id))
async def _delete_apl_config_async(self, config_id: str) -> bool:
"""异步删除APL配置。
Args:
config_id (str): APL配置ID。
Returns:
bool: 删除成功返回True,否则False。
"""
await self._ensure_initialized()
async with get_async_session() as session:
result = await session.execute(delete(APLConfigORM).where(APLConfigORM.id == config_id))
if result.rowcount == 0:
await session.rollback()
return False
await session.commit()
return True
def export_apl_config(self, config_id: str, file_path: str) -> bool:
"""导出APL配置到TOML文件。
Args:
config_id (str): APL配置ID。
file_path (str): 导出文件路径。
Returns:
bool: 导出成功返回True,否则False。
"""
if not config_id or not isinstance(config_id, str):
return False
if not file_path or not isinstance(file_path, str):
return False
config = self.get_apl_config(config_id)
if config is None:
return False
export_data = config.copy()
export_data.pop("create_time", None)
export_data.pop("latest_change_time", None)
# 确保目标目录存在
os.makedirs(os.path.dirname(file_path), exist_ok=True)
# 使用tomli_w.dump写入文件对象
with open(file_path, "wb") as file:
tomli_w.dump(export_data, file)
return True
def import_apl_config(self, file_path: str) -> str | None:
"""从TOML文件导入APL配置。
Args:
file_path (str): APL文件路径。
Returns:
str | None: 导入成功时返回新配置ID,否则None。
"""
if not file_path or not isinstance(file_path, str):
return None
if not os.path.exists(file_path):
return None
with open(file_path, "rb") as file:
config_data = tomllib.load(file)
config_id = str(uuid.uuid4())
asyncio.get_event_loop().run_until_complete(
self._create_apl_config_async(config_id, config_data)
)
return config_id
def get_apl_files(self) -> list[dict[str, Any]]:
"""获取所有APL文件列表。
Returns:
list[dict[str, Any]]: APL文件信息列表。
"""
files = []
files.extend(self._get_apl_files_from_dir(DEFAULT_APL_DIR, "default"))
files.extend(self._get_apl_files_from_dir(COSTOM_APL_DIR, "custom"))
return files
def get_apl_file_content(self, file_id: str) -> dict[str, Any] | None:
"""获取APL文件内容。
Args:
file_id (str): APL文件标识。
Returns:
dict[str, Any] | None: 文件内容信息,未找到时返回None。
"""
if not file_id or not isinstance(file_id, str):
return None
if file_id.startswith("default_"):
rel_path = file_id[len("default_") :]
base_dir = DEFAULT_APL_DIR
elif file_id.startswith("custom_"):
rel_path = file_id[len("custom_") :]
base_dir = COSTOM_APL_DIR
else:
return None
file_path = os.path.join(base_dir, rel_path)
if not os.path.exists(file_path):
return None
with open(file_path, "r", encoding="utf-8") as file:
content = file.read()
return {"file_id": file_id, "content": content, "file_path": file_path}
def create_apl_file(self, file_data: dict[str, Any]) -> str:
"""创建新的APL文件。
Args:
file_data (dict[str, Any]): APL文件数据。
Returns:
str: 新建APL文件的标识。
Raises:
Exception: 当写入文件失败时抛出。
"""
if not file_data or not isinstance(file_data, dict):
raise ValueError("文件数据不能为空且必须是字典类型")
name = file_data.get("name", "new_apl.toml")
content = file_data.get("content", "")
if not name.endswith(".toml"):
name += ".toml"
file_path = os.path.join(COSTOM_APL_DIR, name)
os.makedirs(COSTOM_APL_DIR, exist_ok=True)
with open(file_path, "w", encoding="utf-8") as file:
file.write(content)
return f"custom_{name}"
def update_apl_file(self, file_id: str, content: str) -> bool:
"""更新APL文件内容。
Args:
file_id (str): APL文件标识。
content (str): 文件内容。
Returns:
bool: 更新成功返回True,否则False。
"""
if not file_id or not isinstance(file_id, str):
return False
if content is None or not isinstance(content, str):
return False
if file_id.startswith("default_"):
return False
if not file_id.startswith("custom_"):
return False
rel_path = file_id[len("custom_") :]
file_path = os.path.join(COSTOM_APL_DIR, rel_path)
if not os.path.exists(file_path):
return False
with open(file_path, "w", encoding="utf-8") as file:
file.write(content)
return True
def delete_apl_file(self, file_id: str) -> bool:
"""删除APL文件。
Args:
file_id (str): APL文件标识。
Returns:
bool: 删除成功返回True,否则False。
"""
if not file_id or not isinstance(file_id, str):
return False
if file_id.startswith("default_"):
return False
if not file_id.startswith("custom_"):
return False
rel_path = file_id[len("custom_") :]
file_path = os.path.join(COSTOM_APL_DIR, rel_path)
if not os.path.exists(file_path):
return False
os.remove(file_path)
return True
def _get_apl_from_dir(self, apl_dir: str, source_type: str) -> list[dict[str, Any]]:
"""从指定目录获取APL模板。
Args:
apl_dir (str): 目录路径。
source_type (str): 模板来源标识。
Returns:
list[dict[str, Any]]: 模板列表。
"""
apl_list: list[dict[str, Any]] = []
if not os.path.exists(apl_dir):
return apl_list
for root, _, files in os.walk(apl_dir):
for file_name in files:
if not file_name.endswith(".toml"):
continue
file_path = os.path.join(root, file_name)
with open(file_path, "rb") as file:
apl_data = tomllib.load(file)
general_info = apl_data.get("general", {})
apl_list.append(
{
"id": f"{source_type}_{os.path.relpath(file_path, apl_dir).replace(os.sep, '_')}",
"title": general_info.get("title", ""),
"author": general_info.get("author", ""),
"comment": general_info.get("comment", ""),
"create_time": general_info.get("create_time", ""),
"latest_change_time": general_info.get("latest_change_time", ""),
"source": source_type,
"file_path": file_path,
}
)
return apl_list
def _get_apl_files_from_dir(self, apl_dir: str, source_type: str) -> list[dict[str, Any]]:
"""从指定目录获取APL文件列表。
Args:
apl_dir (str): 目录路径。
source_type (str): 模板来源标识。
Returns:
list[dict[str, Any]]: 文件信息列表。
"""
file_list: list[dict[str, Any]] = []
if not os.path.exists(apl_dir):
return file_list
for root, _, files in os.walk(apl_dir):
for file_name in files:
if not file_name.endswith(".toml"):
continue
file_path = os.path.join(root, file_name)
rel_path = os.path.relpath(file_path, apl_dir)
file_list.append(
{
"id": f"{source_type}_{rel_path.replace(os.sep, '_')}",
"name": file_name,
"path": rel_path,
"source": source_type,
"full_path": file_path,
}
)
return file_list
================================================
FILE: zsim/api_src/services/database/character_db.py
================================================
"""角色配置数据库访问层"""
from __future__ import annotations
from datetime import datetime
from typing import Any
from sqlalchemy import Boolean, Float, Integer, String, Text, delete, select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Mapped, mapped_column
from zsim.api_src.services.database.orm import Base, get_async_engine, get_async_session
from zsim.models.character.character_config import CharacterConfig
_character_db: "CharacterDB | None" = None
class CharacterConfigORM(Base):
__tablename__ = "character_configs"
config_id: Mapped[str] = mapped_column(String(128), primary_key=True)
name: Mapped[str] = mapped_column(String(255), nullable=False)
config_name: Mapped[str] = mapped_column(String(255), nullable=False)
weapon: Mapped[str] = mapped_column(String(255), nullable=False)
weapon_level: Mapped[int] = mapped_column(Integer, nullable=False)
cinema: Mapped[int] = mapped_column(Integer, nullable=False)
crit_balancing: Mapped[bool] = mapped_column(Boolean, nullable=False)
crit_rate_limit: Mapped[float] = mapped_column(Float, nullable=False)
scATK_percent: Mapped[int] = mapped_column(Integer, nullable=False)
scATK: Mapped[int] = mapped_column(Integer, nullable=False)
scHP_percent: Mapped[int] = mapped_column(Integer, nullable=False)
scHP: Mapped[int] = mapped_column(Integer, nullable=False)
scDEF_percent: Mapped[int] = mapped_column(Integer, nullable=False)
scDEF: Mapped[int] = mapped_column(Integer, nullable=False)
scAnomalyProficiency: Mapped[int] = mapped_column(Integer, nullable=False)
scPEN: Mapped[int] = mapped_column(Integer, nullable=False)
scCRIT: Mapped[int] = mapped_column(Integer, nullable=False)
scCRIT_DMG: Mapped[int] = mapped_column(Integer, nullable=False)
drive4: Mapped[str] = mapped_column(Text, nullable=False)
drive5: Mapped[str] = mapped_column(Text, nullable=False)
drive6: Mapped[str] = mapped_column(Text, nullable=False)
equip_style: Mapped[str] = mapped_column(String(255), nullable=False)
equip_set4: Mapped[str | None] = mapped_column(String(255), nullable=True)
equip_set2_a: Mapped[str | None] = mapped_column(String(255), nullable=True)
equip_set2_b: Mapped[str | None] = mapped_column(String(255), nullable=True)
equip_set2_c: Mapped[str | None] = mapped_column(String(255), nullable=True)
create_time: Mapped[str] = mapped_column(String(32), nullable=False)
update_time: Mapped[str] = mapped_column(String(32), nullable=False)
class CharacterDB:
"""角色配置数据库访问对象"""
def __init__(self) -> None:
"""初始化数据库访问对象"""
self._cache: dict[str, Any] = {}
self._db_init = False
async def _init_db(self) -> None:
"""确保数据库表结构已建立"""
if self._db_init:
return
async with get_async_engine().begin() as conn:
await conn.run_sync(Base.metadata.create_all)
self._db_init = True
async def add_character_config(self, config: CharacterConfig) -> None:
"""添加一个新的角色配置。
Args:
config (CharacterConfig): 角色配置数据。
Raises:
SQLAlchemyError: 当数据库写入失败时抛出。
"""
await self._init_db()
if not config.config_id:
config.config_id = f"{config.name}_{config.config_name}"
config.update_time = datetime.now()
async with get_async_session() as session:
session.add(
CharacterConfigORM(
config_id=config.config_id,
name=config.name,
config_name=config.config_name,
weapon=config.weapon,
weapon_level=config.weapon_level,
cinema=config.cinema,
crit_balancing=config.crit_balancing,
crit_rate_limit=config.crit_rate_limit,
scATK_percent=config.scATK_percent,
scATK=config.scATK,
scHP_percent=config.scHP_percent,
scHP=config.scHP,
scDEF_percent=config.scDEF_percent,
scDEF=config.scDEF,
scAnomalyProficiency=config.scAnomalyProficiency,
scPEN=config.scPEN,
scCRIT=config.scCRIT,
scCRIT_DMG=config.scCRIT_DMG,
drive4=config.drive4,
drive5=config.drive5,
drive6=config.drive6,
equip_style=config.equip_style,
equip_set4=config.equip_set4,
equip_set2_a=config.equip_set2_a,
equip_set2_b=config.equip_set2_b,
equip_set2_c=config.equip_set2_c,
create_time=config.create_time.isoformat(),
update_time=config.update_time.isoformat(),
)
)
try:
await session.commit()
except SQLAlchemyError as exc: # noqa: BLE001
await session.rollback()
raise exc
async def get_character_config(self, name: str, config_name: str) -> CharacterConfig | None:
"""根据角色名称和配置名称获取角色配置。
Args:
name (str): 角色名称。
config_name (str): 配置名称。
Returns:
CharacterConfig | None: 匹配的角色配置,未找到时返回None。
"""
await self._init_db()
config_id = f"{name}_{config_name}"
async with get_async_session() as session:
result = await session.execute(
select(CharacterConfigORM).where(CharacterConfigORM.config_id == config_id)
)
record = result.scalar_one_or_none()
if record is None:
return None
return CharacterConfig(
config_id=record.config_id,
name=record.name,
config_name=record.config_name,
weapon=record.weapon,
weapon_level=record.weapon_level,
cinema=record.cinema,
crit_balancing=record.crit_balancing,
crit_rate_limit=record.crit_rate_limit,
scATK_percent=record.scATK_percent,
scATK=record.scATK,
scHP_percent=record.scHP_percent,
scHP=record.scHP,
scDEF_percent=record.scDEF_percent,
scDEF=record.scDEF,
scAnomalyProficiency=record.scAnomalyProficiency,
scPEN=record.scPEN,
scCRIT=record.scCRIT,
scCRIT_DMG=record.scCRIT_DMG,
drive4=record.drive4,
drive5=record.drive5,
drive6=record.drive6,
equip_style=record.equip_style,
equip_set4=record.equip_set4,
equip_set2_a=record.equip_set2_a,
equip_set2_b=record.equip_set2_b,
equip_set2_c=record.equip_set2_c,
create_time=datetime.fromisoformat(record.create_time),
update_time=datetime.fromisoformat(record.update_time),
)
async def update_character_config(self, config: CharacterConfig) -> None:
"""更新数据库中的角色配置。
Args:
config (CharacterConfig): 新的角色配置信息。
Raises:
SQLAlchemyError: 当数据库写入失败时抛出。
"""
await self._init_db()
config.update_time = datetime.now()
async with get_async_session() as session:
result = await session.execute(
select(CharacterConfigORM).where(CharacterConfigORM.config_id == config.config_id)
)
record = result.scalar_one_or_none()
if record is None:
return
record.name = config.name
record.config_name = config.config_name
record.weapon = config.weapon
record.weapon_level = config.weapon_level
record.cinema = config.cinema
record.crit_balancing = config.crit_balancing
record.crit_rate_limit = config.crit_rate_limit
record.scATK_percent = config.scATK_percent
record.scATK = config.scATK
record.scHP_percent = config.scHP_percent
record.scHP = config.scHP
record.scDEF_percent = config.scDEF_percent
record.scDEF = config.scDEF
record.scAnomalyProficiency = config.scAnomalyProficiency
record.scPEN = config.scPEN
record.scCRIT = config.scCRIT
record.scCRIT_DMG = config.scCRIT_DMG
record.drive4 = config.drive4
record.drive5 = config.drive5
record.drive6 = config.drive6
record.equip_style = config.equip_style
record.equip_set4 = config.equip_set4
record.equip_set2_a = config.equip_set2_a
record.equip_set2_b = config.equip_set2_b
record.equip_set2_c = config.equip_set2_c
record.update_time = config.update_time.isoformat()
await session.flush()
try:
await session.commit()
except SQLAlchemyError as exc: # noqa: BLE001
await session.rollback()
raise exc
async def delete_character_config(self, name: str, config_name: str) -> None:
"""删除指定角色的配置。
Args:
name (str): 角色名称。
config_name (str): 配置名称。
"""
await self._init_db()
config_id = f"{name}_{config_name}"
async with get_async_session() as session:
await session.execute(
delete(CharacterConfigORM).where(CharacterConfigORM.config_id == config_id)
)
await session.commit()
async def list_character_configs(self, name: str) -> list[CharacterConfig]:
"""获取指定角色的所有配置列表。
Args:
name (str): 角色名称。
Returns:
list[CharacterConfig]: 角色配置列表。
"""
await self._init_db()
async with get_async_session() as session:
result = await session.execute(
select(CharacterConfigORM)
.where(CharacterConfigORM.name == name)
.order_by(CharacterConfigORM.config_name)
)
records = result.scalars().all()
return [
CharacterConfig(
config_id=record.config_id,
name=record.name,
config_name=record.config_name,
weapon=record.weapon,
weapon_level=record.weapon_level,
cinema=record.cinema,
crit_balancing=record.crit_balancing,
crit_rate_limit=record.crit_rate_limit,
scATK_percent=record.scATK_percent,
scATK=record.scATK,
scHP_percent=record.scHP_percent,
scHP=record.scHP,
scDEF_percent=record.scDEF_percent,
scDEF=record.scDEF,
scAnomalyProficiency=record.scAnomalyProficiency,
scPEN=record.scPEN,
scCRIT=record.scCRIT,
scCRIT_DMG=record.scCRIT_DMG,
drive4=record.drive4,
drive5=record.drive5,
drive6=record.drive6,
equip_style=record.equip_style,
equip_set4=record.equip_set4,
equip_set2_a=record.equip_set2_a,
equip_set2_b=record.equip_set2_b,
equip_set2_c=record.equip_set2_c,
create_time=datetime.fromisoformat(record.create_time),
update_time=datetime.fromisoformat(record.update_time),
)
for record in records
]
async def get_character_db() -> CharacterDB:
"""获取CharacterDB单例。
Returns:
CharacterDB: 单例数据库访问对象。
"""
global _character_db
if _character_db is None:
_character_db = CharacterDB()
return _character_db
================================================
FILE: zsim/api_src/services/database/enemy_db.py
================================================
"""敌人配置数据库访问层"""
from __future__ import annotations
import json
from datetime import datetime
from sqlalchemy import Integer, String, Text, delete, select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Mapped, mapped_column
from zsim.api_src.services.database.orm import Base, get_async_engine, get_async_session
from zsim.models.enemy.enemy_config import EnemyConfig
_enemy_db: "EnemyDB | None" = None
class EnemyConfigORM(Base):
__tablename__ = "enemy_configs"
config_id: Mapped[str] = mapped_column(String(128), primary_key=True)
enemy_index: Mapped[int] = mapped_column(Integer, nullable=False)
enemy_adjust: Mapped[str] = mapped_column(Text, nullable=False)
create_time: Mapped[str] = mapped_column(String(32), nullable=False)
update_time: Mapped[str] = mapped_column(String(32), nullable=False)
class EnemyDB:
"""敌人配置数据库访问对象"""
def __init__(self) -> None:
"""初始化数据库访问对象"""
self._db_init = False
async def _init_db(self) -> None:
"""确保数据库表结构已建立"""
if self._db_init:
return
async with get_async_engine().begin() as conn:
await conn.run_sync(Base.metadata.create_all)
self._db_init = True
async def add_enemy_config(self, config: EnemyConfig) -> None:
"""添加敌人配置。
Args:
config (EnemyConfig): 敌人配置数据。
Raises:
SQLAlchemyError: 当数据库写入失败时抛出。
"""
await self._init_db()
config.update_time = datetime.now()
async with get_async_session() as session:
session.add(
EnemyConfigORM(
config_id=config.config_id,
enemy_index=config.enemy_index,
enemy_adjust=json.dumps(config.enemy_adjust),
create_time=config.create_time.isoformat(),
update_time=config.update_time.isoformat(),
)
)
try:
await session.commit()
except SQLAlchemyError as exc: # noqa: BLE001
await session.rollback()
raise exc
async def get_enemy_config(self, config_id: str) -> EnemyConfig | None:
"""根据配置ID获取敌人配置。
Args:
config_id (str): 敌人配置ID。
Returns:
EnemyConfig | None: 匹配的敌人配置,未找到时返回None。
"""
await self._init_db()
async with get_async_session() as session:
result = await session.execute(
select(EnemyConfigORM).where(EnemyConfigORM.config_id == config_id)
)
record = result.scalar_one_or_none()
if record is None:
return None
return EnemyConfig(
config_id=record.config_id,
enemy_index=record.enemy_index,
enemy_adjust=json.loads(record.enemy_adjust),
create_time=datetime.fromisoformat(record.create_time),
update_time=datetime.fromisoformat(record.update_time),
)
async def update_enemy_config(self, config: EnemyConfig) -> None:
"""更新敌人配置。
Args:
config (EnemyConfig): 敌人配置数据。
Raises:
SQLAlchemyError: 当数据库写入失败时抛出。
"""
await self._init_db()
config.update_time = datetime.now()
async with get_async_session() as session:
result = await session.execute(
select(EnemyConfigORM).where(EnemyConfigORM.config_id == config.config_id)
)
record = result.scalar_one_or_none()
if record is None:
return
record.enemy_index = config.enemy_index
record.enemy_adjust = json.dumps(config.enemy_adjust)
record.update_time = config.update_time.isoformat()
await session.flush()
try:
await session.commit()
except SQLAlchemyError as exc: # noqa: BLE001
await session.rollback()
raise exc
async def delete_enemy_config(self, config_id: str) -> None:
"""删除敌人配置。
Args:
config_id (str): 敌人配置ID。
"""
await self._init_db()
async with get_async_session() as session:
await session.execute(
delete(EnemyConfigORM).where(EnemyConfigORM.config_id == config_id)
)
await session.commit()
async def list_enemy_configs(self) -> list[EnemyConfig]:
"""列出所有敌人配置。
Returns:
list[EnemyConfig]: 敌人配置列表。
"""
await self._init_db()
async with get_async_session() as session:
result = await session.execute(
select(EnemyConfigORM).order_by(EnemyConfigORM.config_id)
)
records = result.scalars().all()
return [
EnemyConfig(
config_id=record.config_id,
enemy_index=record.enemy_index,
enemy_adjust=json.loads(record.enemy_adjust),
create_time=datetime.fromisoformat(record.create_time),
update_time=datetime.fromisoformat(record.update_time),
)
for record in records
]
async def get_enemy_db() -> EnemyDB:
"""获取EnemyDB单例。
Returns:
EnemyDB: 敌人数据库访问对象。
"""
global _enemy_db
if _enemy_db is None:
_enemy_db = EnemyDB()
return _enemy_db
================================================
FILE: zsim/api_src/services/database/orm.py
================================================
"""SQLAlchemy基础设施定义"""
from __future__ import annotations
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from pathlib import Path
from sqlalchemy.ext.asyncio import (
AsyncEngine,
AsyncSession,
async_sessionmaker,
create_async_engine,
)
from sqlalchemy.orm import DeclarativeBase
from zsim.define import SQLITE_PATH
class Base(DeclarativeBase):
"""声明式基类"""
def _database_path() -> Path:
"""返回SQLite数据库路径。
Returns:
Path: 数据库文件的绝对路径。
"""
path = Path(SQLITE_PATH).expanduser()
path.parent.mkdir(parents=True, exist_ok=True)
return path.resolve()
def get_async_database_url() -> str:
"""获取异步模式下的数据库URL。
Returns:
str: 适用于异步SQLAlchemy引擎的数据库URL。
"""
return f"sqlite+aiosqlite:///{_database_path().as_posix()}"
def get_sync_database_url() -> str:
"""获取同步模式下的数据库URL。
Returns:
str: 适用于同步SQLAlchemy引擎(如Alembic)的数据库URL。
"""
return f"sqlite:///{_database_path().as_posix()}"
_async_engine: AsyncEngine = create_async_engine(get_async_database_url(), future=True)
_async_session_factory = async_sessionmaker(_async_engine, expire_on_commit=False)
def get_async_engine() -> AsyncEngine:
"""返回复用的异步SQLAlchemy引擎实例。
Returns:
AsyncEngine: 进程范围内复用的异步引擎。
"""
return _async_engine
@asynccontextmanager
async def get_async_session() -> AsyncIterator[AsyncSession]:
"""获取一个SQLAlchemy异步会话。
Returns:
AsyncIterator[AsyncSession]: SQLAlchemy异步会话上下文管理器。
Raises:
RuntimeError: 当执行过程中出现数据库错误时抛出。
"""
session = _async_session_factory()
try:
yield session
except Exception as exc:
await session.rollback()
raise RuntimeError("异步数据库会话执行失败") from exc
finally:
await session.close()
__all__ = [
"Base",
"get_async_engine",
"get_async_session",
"get_async_database_url",
"get_sync_database_url",
]
================================================
FILE: zsim/api_src/services/database/session_db.py
================================================
"""模拟会话数据库访问层"""
from __future__ import annotations
import json
from datetime import datetime
from typing import Any
from sqlalchemy import String, Text, delete, select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Mapped, mapped_column
from zsim.api_src.services.database.orm import Base, get_async_engine, get_async_session
from zsim.models.session.session_create import Session
_session_db: "SessionDB | None" = None
class SessionORM(Base):
"""模拟会话ORM模型"""
__tablename__ = "sessions"
session_id: Mapped[str] = mapped_column(String(128), primary_key=True)
session_name: Mapped[str] = mapped_column(String(255), nullable=False, default="")
create_time: Mapped[str] = mapped_column(String(32), nullable=False)
status: Mapped[str] = mapped_column(String(32), nullable=False)
session_run: Mapped[str | None] = mapped_column(Text, nullable=True)
session_result: Mapped[str | None] = mapped_column(Text, nullable=True)
class SessionDB:
"""会话数据库访问对象"""
def __init__(self) -> None:
"""初始化数据库访问对象"""
self._cache: dict[str, Any] = {}
self._db_init = False
async def _init_db(self) -> None:
"""确保数据库表结构已建立"""
if self._db_init:
return
async with get_async_engine().begin() as conn:
await conn.run_sync(Base.metadata.create_all)
self._db_init = True
async def add_session(self, session_data: Session) -> None:
"""添加一个新的模拟会话。
Args:
session_data (Session): 会话数据。
Raises:
SQLAlchemyError: 当数据库写入失败时抛出。
"""
await self._init_db()
async with get_async_session() as session:
session.add(
SessionORM(
session_id=session_data.session_id,
session_name=session_data.session_name,
create_time=session_data.create_time.isoformat(),
status=session_data.status,
session_run=(
session_data.session_run.model_dump_json(indent=4)
if session_data.session_run
else None
),
session_result=(
json.dumps([result.model_dump() for result in session_data.session_result])
if session_data.session_result
else None
),
)
)
try:
await session.commit()
except SQLAlchemyError as exc: # noqa: BLE001
await session.rollback()
raise exc
async def get_session(self, session_id: str) -> Session | None:
"""根据ID获取模拟会话。
Args:
session_id (str): 会话ID。
Returns:
Session | None: 匹配的会话数据,未找到时返回None。
"""
await self._init_db()
async with get_async_session() as session:
result = await session.execute(
select(SessionORM).where(SessionORM.session_id == session_id)
)
record = result.scalar_one_or_none()
if record is None:
return None
return Session(
session_id=record.session_id,
session_name=record.session_name,
create_time=datetime.fromisoformat(record.create_time),
status=record.status,
session_run=(json.loads(record.session_run) if record.session_run else None),
session_result=(
json.loads(record.session_result) if record.session_result else None
),
)
async def update_session(self, session_data: Session) -> None:
"""更新模拟会话。
Args:
session_data (Session): 会话数据。
Raises:
SQLAlchemyError: 当数据库写入失败时抛出。
"""
await self._init_db()
async with get_async_session() as session:
result = await session.execute(
select(SessionORM).where(SessionORM.session_id == session_data.session_id)
)
record = result.scalar_one_or_none()
if record is None:
return
record.session_name = session_data.session_name
record.create_time = session_data.create_time.isoformat()
record.status = session_data.status
record.session_run = (
session_data.session_run.model_dump_json(indent=4)
if session_data.session_run
else None
)
record.session_result = (
json.dumps([result.model_dump() for result in session_data.session_result])
if session_data.session_result
else None
)
await session.flush()
try:
await session.commit()
except SQLAlchemyError as exc: # noqa: BLE001
await session.rollback()
raise exc
async def delete_session(self, session_id: str) -> None:
"""删除模拟会话。
Args:
session_id (str): 会话ID。
"""
await self._init_db()
async with get_async_session() as session:
await session.execute(delete(SessionORM).where(SessionORM.session_id == session_id))
await session.commit()
async def list_sessions(self) -> list[Session]:
"""列出所有模拟会话。
Returns:
list[Session]: 会话数据列表。
"""
await self._init_db()
async with get_async_session() as session:
result = await session.execute(
select(SessionORM).order_by(SessionORM.create_time.desc())
)
records = result.scalars().all()
return [
Session(
session_id=record.session_id,
session_name=record.session_name,
create_time=datetime.fromisoformat(record.create_time),
status=record.status,
session_run=(json.loads(record.session_run) if record.session_run else None),
session_result=(
json.loads(record.session_result) if record.session_result else None
),
)
for record in records
]
async def get_session_db() -> SessionDB:
"""获取SessionDB单例。
Returns:
SessionDB: 会话数据库访问对象。
"""
global _session_db
if _session_db is None:
_session_db = SessionDB()
return _session_db
================================================
FILE: zsim/api_src/services/sim_controller/__init__.py
================================================
from .sim_controller import SimController
__all__ = ["SimController"]
================================================
FILE: zsim/api_src/services/sim_controller/sim_controller.py
================================================
import asyncio
import logging
import threading
from concurrent.futures import ProcessPoolExecutor
from typing import TYPE_CHECKING, Any, Iterator, Literal
from zsim.api_src.services.database.session_db import get_session_db
from zsim.models.session.session_create import Session
from zsim.models.session.session_result import (
AttrCurvePayload,
BuffResult,
DmgResult,
NormalModeResult,
NormalResultPayload,
ParallelAttrCurveResultPayload,
ParallelModeResult,
ParallelResultPayload,
ParallelWeaponResultPayload,
WeaponPayload,
)
from zsim.models.session.session_run import (
CommonCfg,
ExecAttrCurveCfg,
ExecWeaponCfg,
ParallelCfg,
SessionRun,
)
from zsim.models.session.session_run import (
SimulationConfig as SimCfg,
)
from zsim.simulator import Simulator
from zsim.utils.constants import stats_trans_mapping
from zsim.utils.process_buff_result import (
prepare_buff_data_and_cache as process_buff,
)
from zsim.utils.process_dmg_result import (
prepare_dmg_data_and_cache as process_dmg,
)
from zsim.utils.process_parallel_data import (
judge_parallel_result,
merge_parallel_dmg_data,
)
from zsim.utils.process_parallel_data import (
prepare_parallel_data_and_cache as prepare_parallel_cache,
)
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Confirmation
logger = logging.getLogger(__name__)
class SimController:
_instance = None
_lock = threading.Lock()
def __new__(cls, *args, **kwargs):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super(SimController, cls).__new__(cls)
return cls._instance
"""
模拟控制器,负责管理和执行模拟任务。
该类提供异步队列管理、进程池执行和并行参数生成功能。
"""
def __init__(self):
if hasattr(self, "_initialized") and self._initialized:
return
self._initialized = True
"""初始化模拟控制器"""
self._executor: ProcessPoolExecutor | None = None
self._queue: asyncio.Queue = asyncio.Queue()
self._running_tasks: set[asyncio.Future[Any]] = set()
self._loop: asyncio.AbstractEventLoop | None = None
@property
def executor(self) -> ProcessPoolExecutor:
"""获取进程池执行器,延迟初始化。"""
with self._lock:
if self._executor is None:
self._executor = ProcessPoolExecutor()
return self._executor
def __del__(self):
"""析构函数,确保资源清理。"""
self._shutdown_executor()
def _shutdown_executor(self):
"""关闭进程池执行器。"""
if self._executor is not None:
try:
self._executor.shutdown(wait=True)
logger.info("进程池已关闭")
except Exception as e:
logger.error(f"关闭进程池时发生错误: {e}")
finally:
self._executor = None
async def put_into_queue(
self, session_id: str, common_cfg: CommonCfg, sim_cfg: SimCfg | None
) -> None:
"""
将模拟任务放入队列。
Args:
common_cfg: 通用配置对象
sim_cfg: 模拟配置对象,可以为None
"""
await self._queue.put((session_id, common_cfg, sim_cfg))
async def get_from_queue(self) -> tuple[str, CommonCfg, SimCfg | None]:
"""
从队列中获取模拟任务。
Returns:
包含通用配置和模拟配置的元组
"""
return await self._queue.get()
async def execute_simulation(self) -> None:
"""
执行模拟任务的主循环。
从队列中持续获取任务并在进程池中执行,包含错误处理和资源管理。
"""
event_loop = asyncio.get_event_loop()
db = await get_session_db()
while True:
try:
session_id, common_cfg, sim_cfg = await self.get_from_queue()
session = await db.get_session(session_id)
if not session or not session.session_run:
logger.error(f"无法获取会话 {session_id} 或其运行配置")
continue
stop_tick = (
sim_cfg.stop_tick
if sim_cfg and sim_cfg.stop_tick is not None
else session.session_run.stop_tick
)
if stop_tick is None:
logger.warning(f"会话 {session_id} 未设置 stop_tick,使用默认值 3600")
stop_tick = 3600
def run_simulator(
_common_cfg: CommonCfg, _sim_cfg: SimCfg | None, _stop_tick: int
) -> "Confirmation":
simulator = Simulator()
return simulator.api_run_simulator(_common_cfg, _sim_cfg, _stop_tick)
# 创建模拟器实例并提交任务
future: asyncio.Future["Confirmation"] = event_loop.run_in_executor(
self.executor, run_simulator, common_cfg, sim_cfg, stop_tick
)
self._running_tasks.add(future)
future.add_done_callback(lambda f: self._task_done_callback(f, session_id))
# 让出控制权给其他协程
await asyncio.sleep(0)
except Exception as e:
logger.error(f"执行模拟任务时发生错误: {e}", exc_info=True)
await asyncio.sleep(1) # 错误后短暂延迟
async def execute_simulation_test(self, max_tasks: int = 1) -> list[str]:
"""
执行模拟任务的测试版本,处理有限数量的任务。
Args:
max_tasks: 最大处理任务数,默认为1
Returns:
list[str]: 完成的任务的 session_id 列表
"""
from concurrent.futures import ThreadPoolExecutor
event_loop = asyncio.get_event_loop()
db = await get_session_db()
completed_sessions = []
for _ in range(max_tasks):
session_id = None
try:
# 如果队列为空,跳出循环
if self._queue.empty():
break
session_id, common_cfg, sim_cfg = await self.get_from_queue()
session = await db.get_session(session_id)
if not session or not session.session_run:
logger.error(f"无法获取会话 {session_id} 或其运行配置")
continue
stop_tick = (
sim_cfg.stop_tick
if sim_cfg and sim_cfg.stop_tick is not None
else session.session_run.stop_tick
)
if stop_tick is None:
logger.warning(f"会话 {session_id} 未设置 stop_tick,使用默认值 3600")
stop_tick = 3600
def run_simulator(
_common_cfg: CommonCfg, _sim_cfg: SimCfg | None, _stop_tick: int
) -> "Confirmation":
simulator = Simulator()
return simulator.api_run_simulator(_common_cfg, _sim_cfg, _stop_tick)
# 使用 ThreadPoolExecutor 避免序列化问题
with ThreadPoolExecutor() as thread_executor:
future: asyncio.Future["Confirmation"] = event_loop.run_in_executor(
thread_executor, run_simulator, common_cfg, sim_cfg, stop_tick
)
result = await future # noqa: F841
logger.info(f"测试模拟任务 {session_id} 完成")
# 更新会话状态
session.status = "completed"
await db.update_session(session)
completed_sessions.append(session_id)
except Exception as e:
logger.error(f"执行测试模拟任务时发生错误: {e}", exc_info=True)
if session_id:
session = await db.get_session(session_id)
if session:
session.status = "failed"
await db.update_session(session)
return completed_sessions
async def execute_simulation_test_parallel(
self, session_id: str, parallel_count: int = 2
) -> list[str]:
"""
执行并行模拟任务的测试版本。
Args:
session_id: 会话ID
parallel_count: 并行任务数量
Returns:
list[str]: 完成的任务的 session_id 列表
"""
from concurrent.futures import ThreadPoolExecutor
event_loop = asyncio.get_event_loop()
db = await get_session_db()
completed_sessions = []
# 等待队列中有足够的任务
tasks_to_process = []
for _ in range(parallel_count):
if self._queue.empty():
break
task = await self.get_from_queue()
tasks_to_process.append(task)
if not tasks_to_process:
return completed_sessions
def run_simulator(
session_id_inner: str,
common_cfg: CommonCfg,
sim_cfg: SimCfg | None,
stop_tick: int,
) -> tuple[str, "Confirmation"]:
simulator = Simulator()
result = simulator.api_run_simulator(common_cfg, sim_cfg, stop_tick)
return session_id_inner, result
# 并行执行所有任务
with ThreadPoolExecutor(max_workers=parallel_count) as thread_executor:
futures = []
for session_id_inner, common_cfg, sim_cfg in tasks_to_process:
session = await db.get_session(session_id_inner)
if not session or not session.session_run:
logger.error(f"无法获取会话 {session_id_inner} 或其运行配置")
continue
stop_tick = (
sim_cfg.stop_tick
if sim_cfg and sim_cfg.stop_tick is not None
else session.session_run.stop_tick
)
if stop_tick is None:
stop_tick = 1000
future = event_loop.run_in_executor(
thread_executor,
run_simulator,
session_id_inner,
common_cfg,
sim_cfg,
stop_tick,
)
futures.append(future)
# 等待所有任务完成
if futures:
results = await asyncio.gather(*futures, return_exceptions=True)
for result in results:
if isinstance(result, BaseException):
logger.error(f"并行任务执行失败: {result}")
continue
if not isinstance(result, tuple) or len(result) != 2:
logger.error(f"并行任务结果格式错误: {result}")
continue
session_id_inner, confirmation = result
# 更新会话状态
session = await db.get_session(session_id_inner)
if session:
session.status = "completed"
await db.update_session(session)
completed_sessions.append(session_id_inner)
logger.info(f"并行测试模拟任务 {session_id_inner} 完成")
return completed_sessions
async def run_single_simulation(
self, common_cfg: CommonCfg, sim_cfg: SimCfg | None, stop_tick: int
) -> "Confirmation":
"""
运行单个模拟任务的异步方法,用于测试。
Args:
common_cfg: 通用配置
sim_cfg: 模拟配置
stop_tick: 停止帧数
Returns:
Confirmation: 模拟结果确认信息
"""
loop = asyncio.get_event_loop()
# Use ThreadPoolExecutor instead of ProcessPoolExecutor for compatibility
from concurrent.futures import ThreadPoolExecutor
def _run_simulator() -> "Confirmation":
simulator = Simulator()
return simulator.api_run_simulator(common_cfg, sim_cfg, stop_tick)
if isinstance(self.executor, ProcessPoolExecutor):
# For testing, use thread executor to avoid serialization issues
with ThreadPoolExecutor() as thread_executor:
return await loop.run_in_executor(thread_executor, _run_simulator)
else:
return await loop.run_in_executor(self.executor, _run_simulator)
def _task_done_callback(self, future: asyncio.Future["Confirmation"], session_id: str) -> None:
"""
任务完成时的回调函数。
Args:
future: 完成的Future对象
"""
self._running_tasks.discard(future)
asyncio.run_coroutine_threadsafe(
self._update_session_status(future, session_id), asyncio.get_running_loop()
)
async def _update_session_status(
self, future: asyncio.Future["Confirmation"], session_id: str
) -> None:
db = await get_session_db()
session = await db.get_session(session_id)
if not session:
logger.error(f"会话 {session_id} 未找到,无法更新状态")
return
try:
result = future.result()
logger.info(f"模拟任务 {session_id} 完成")
session.status = "completed"
# 处理模拟结果确认信息
if isinstance(result, dict) and "run_turn_uuid" in result:
processed_result: (
NormalModeResult | ParallelModeResult
) = await self._process_simulation_result(result)
try:
session.session_result = [processed_result]
except Exception as e:
logger.error(
f"TODO: 模拟任务 {session_id} 结果处理: {repr(e)}",
exc_info=True,
)
except Exception as e:
logger.error(f"模拟任务 {session_id} 执行失败: {e}", exc_info=True)
session.status = "failed"
await db.update_session(session)
async def _process_simulation_result(
self, confirmation: "Confirmation"
) -> NormalModeResult | ParallelModeResult:
"""
处理模拟结果确认信息的函数stub。
Args:
confirmation: 包含运行确认信息的字典
- run_turn_uuid: 运行轮次的UUID
- status: 运行状态
- timestamp: 完成时间戳
- sim_cfg: 模拟配置(包含并行配置信息)
"""
rid = confirmation.session_id # compatible to webui rid logic
logger.info(f"开始处理模拟结果: session_id={rid}, status={confirmation.status}")
if judge_parallel_result(rid):
await prepare_parallel_cache(rid)
parallel_dmg_data = await merge_parallel_dmg_data(rid)
if parallel_dmg_data is None:
raise ValueError("并行模式下合并结果失败")
func: Literal["attr_curve", "weapon"] = parallel_dmg_data[0] # type: ignore
result_data = parallel_dmg_data[1]
payload: ParallelAttrCurveResultPayload | ParallelWeaponResultPayload
if func == "attr_curve":
payload = ParallelAttrCurveResultPayload(
func=func, result=AttrCurvePayload(root=result_data)
)
elif func == "weapon":
payload = ParallelWeaponResultPayload(
func=func, result=WeaponPayload(root=result_data)
)
else:
raise NotImplementedError(f"未知的func类型: {func}")
result = ParallelModeResult(
mode="parallel", func=func, result=ParallelResultPayload(root=payload)
)
else:
# Single run.
dmg_result = process_dmg(rid)
buff_result = await process_buff(rid)
result = NormalModeResult(
mode="normal",
result=NormalResultPayload(
dmg_result=DmgResult(root=dmg_result),
buff_result=BuffResult(root=buff_result), # type: ignore
),
)
logger.info(f"模拟结果处理完成: run_turn_uuid={rid}")
return result
async def shutdown(self) -> None:
"""优雅关闭控制器,等待所有任务完成并清理资源。"""
logger.info("开始关闭模拟控制器...")
# 等待所有运行中的任务完成
if self._running_tasks:
logger.info(f"等待 {len(self._running_tasks)} 个任务完成...")
await asyncio.gather(*list(self._running_tasks), return_exceptions=True)
# 关闭进程池
if self._executor is not None:
self._executor.shutdown(wait=True)
logger.info("进程池已关闭")
logger.info("模拟控制器已关闭")
def generate_parallel_args(
self,
session: Session,
session_run_config: SessionRun,
) -> Iterator[SimCfg]:
"""
生成用于并行模拟的参数。WebUI版本有一个类似方法,但是他们的数据结构并不相同。
Args:
session: 会话配置
session_run_config: 会话运行配置
Yields:
SimCfg: 为每个模拟任务生成的参数对象
Raises:
ValueError: 当模式不是parallel或func类型未知时
"""
mode = session_run_config.mode
session_id = session.session_id
if mode != "parallel":
logger.warning(f"会话模式不是parallel,当前模式: {mode}")
return
stop_tick = session_run_config.stop_tick
if stop_tick is None:
logger.error("并行模式下 stop_tick 不能为空")
raise ValueError("并行模式下 stop_tick 不能为空")
parallel_cfg = session_run_config.parallel_config
if parallel_cfg is None:
logger.error("并行配置为空")
raise ValueError("并行配置不能为空")
# 根据启用的标志确定功能
func = parallel_cfg.func
func_cfg = parallel_cfg.func_config
if func == "attr_curve" and isinstance(func_cfg, ParallelCfg.AttrCurveConfig):
yield from self._generate_attr_curve_args(func_cfg, parallel_cfg, stop_tick, session_id)
elif func == "weapon" and isinstance(func_cfg, ParallelCfg.WeaponConfig):
yield from self._generate_weapon_args(func_cfg, parallel_cfg, stop_tick, session_id)
else:
error_msg = f"未知的func类型: {func}, 完整配置: {parallel_cfg}"
logger.error(error_msg)
raise ValueError(error_msg)
@staticmethod
def _generate_attr_curve_args(
func_cfg: ParallelCfg.AttrCurveConfig,
parallel_cfg: ParallelCfg,
stop_tick: int,
session_id: str,
) -> Iterator[ExecAttrCurveCfg]:
"""
生成属性曲线参数。
Args:
func_cfg: 属性曲线配置
parallel_cfg: 并行配置
stop_tick: 停止帧数
session_id: 会话ID
Yields:
ExecAttrCurveCfg: 单个子进程在属性收益曲线模式下的执行配置
"""
sc_list = func_cfg.sc_list
sc_range_start, sc_range_end = func_cfg.sc_range
remove_equip_list = func_cfg.remove_equip_list
for sc_name in sc_list:
if sc_name not in stats_trans_mapping:
logger.warning(f"未知的属性名称: {sc_name},跳过")
continue
for sc_value in range(sc_range_start, sc_range_end + 1):
args = ExecAttrCurveCfg(
stop_tick=stop_tick,
mode="parallel",
func="attr_curve",
adjust_char=parallel_cfg.adjust_char,
sc_name=stats_trans_mapping[sc_name],
sc_value=sc_value,
run_turn_uuid=session_id,
remove_equip=sc_name in remove_equip_list,
)
yield args
@staticmethod
def _generate_weapon_args(
func_cfg: ParallelCfg.WeaponConfig,
parallel_cfg: ParallelCfg,
stop_tick: int,
session_id: str,
) -> Iterator[ExecWeaponCfg]:
"""
生成武器参数。
Args:
func_cfg: 武器配置
parallel_cfg: 并行配置
stop_tick: 停止帧数
session_id: 会话ID
Yields:
ExecWeaponCfg: 单个子进程在武器伤害期望模式的执行配置
"""
weapon_list: list[ParallelCfg.WeaponConfig.SingleWeapon] = func_cfg.weapon_list
for weapon in weapon_list:
args = ExecWeaponCfg(
stop_tick=stop_tick,
mode="parallel",
func="weapon",
adjust_char=parallel_cfg.adjust_char,
weapon_name=weapon.name,
weapon_level=weapon.level,
run_turn_uuid=session_id,
)
yield args
================================================
FILE: zsim/config_example.json
================================================
{
"debug": {
"enabled": true,
"level": 4,
"check_skill_mul": false,
"check_skill_mul_tag": ["1401_Cinema_6"]
},
"stop_tick": 10800,
"watchdog": {
"enabled": false,
"level": 4
},
"character": {
"crit_balancing": true,
"back_attack_rate": 1
},
"enemy": {
"index_ID": 11412,
"adjust_ID": 22412,
"difficulty": 8.74
},
"apl_mode": {
"enabled": true,
"na_order": "./zsim/data/DefaultConfig/NAOrder.json",
"enemy_random_attack": false,
"enemy_regular_attack": false,
"enemy_attack_response": false,
"enemy_attack_method_config": "./zsim/data/enemy_attack_method.csv",
"enemy_attack_action_data": "./zsim/data/enemy_attack_action.csv",
"enemy_attack_report": true,
"player_level": 5,
"default_apl_dir": "./zsim/data/APLData",
"custom_apl_dir": "./zsim/data/APLData/custom",
"Yanagi": "./zsim/data/DefaultConfig/1221.json",
"Hugo": "./zsim/data/DefaultConfig/1291.json",
"Alice": "./zsim/data/DefaultConfig/1401.json",
"Seed": "./zsim/data/DefaultConfig/1461.json"
},
"swap_cancel_mode": {
"enabled": true,
"completion_coefficient": 0.3,
"lag_time": 20,
"debug": false,
"debug_target_skill": "1371_CA"
},
"database": {
"SQLITE_PATH": "./zsim/data/zsim.db",
"CHARACTER_DATA_PATH": "./zsim/data/character.csv",
"WEAPON_DATA_PATH": "./zsim/data/weapon.csv",
"EQUIP_2PC_DATA_PATH": "./zsim/data/equip_set_2pc.csv",
"SKILL_DATA_PATH": "./zsim/data/skill.csv",
"ENEMY_DATA_PATH": "./zsim/data/enemy.csv",
"ENEMY_ADJUSTMENT_PATH": "./zsim/data/enemy_adjustment.csv",
"DEFAULT_SKILL_PATH": "./zsim/data/default_skill.csv",
"JUDGE_FILE_PATH": "./zsim/data/\u89e6\u53d1\u5224\u65ad.csv",
"EFFECT_FILE_PATH": "./zsim/data/buff_effect.csv",
"EXIST_FILE_PATH": "./zsim/data/\u6fc0\u6d3b\u5224\u65ad.csv",
"APL_FILE_PATH": "./zsim/data/APLData/\u8587\u8587\u5b89-\u67f3-\u8000\u5609\u97f3.toml"
},
"translate": {
"id": "skill_tag",
"From": "char_name",
"OfficialName": "CN_skill_tag",
"SpConsumption": "sp_consume",
"SpRecovery_hit": "sp_recovery",
"Sp_Threshold": "sp_threshold",
"FeverRecovery": "fever_recovery",
"ElementAbnormalAccumulation": "anomaly_accumulation",
"SkillType": "skill_type",
"TriggerBuffLevel": "trigger_buff_level",
"ElementType": "element_type",
"TimeCost": "ticks",
"HitNumber": "hit_times",
"DmgRelated_Attributes": "diff_multiplier",
"StunRelated_Attributes": "",
"Interruption_Resistance": "interruption_resistance"
},
"buff_0_report": {
"enabled": false
},
"char_report": {
"Vivian": false,
"AstraYao": false,
"Hugo": false,
"Yixuan": false,
"Trigger": false,
"Jufufu": false,
"Yuzuha": false,
"Alice": false,
"Seed": false
},
"na_mode_level": {
"Hugo": 3
},
"parallel_mode": {
"enabled": false,
"adjust_char": 1,
"adjust_sc": {
"enabled": true,
"sc_range": [
0,
5
],
"sc_list": [
"\u66b4\u51fb\u7387"
],
"remove_equip_list": [
"\u66b4\u51fb\u7387"
]
},
"adjust_weapon": {
"enabled": false,
"weapon_list": []
},
"char_report": {
"Vivian": false,
"AstraYao": false,
"Hugo": false,
"Yixuan": false,
"Trigger": false,
"Seed": false
}
},
"dev": {
"new_sim_boot": true
}
}
================================================
FILE: zsim/data/APLData/APL template.toml
================================================
[general]
title = "APL配置示例"
comment = "这是一个APL配置示例,你可以据此新建新模板"
author = "Yuki Aro"
create_time = "2025-04-15T23:00:00.000+08:00"
latest_change_time = "2025-04-15T23:00:00.000+08:00"
[characters]
required = [ "零号·安比", "扳机",]
optional = [ "丽娜",]
[apl_logic]
logic = """
# 扳机逻辑
# 扳机补充决意值逻辑:
# 连击逻辑:
1361|action+=|1361_SNA_1|attribute.1361:special_state→狙击姿态==True|attribute.1361:special_resource<100|status.enemy:stun_pct<=0.7
# 启动逻辑
1361|action+=|1361_SNA_0|attribute.1361:special_resource<5|status.enemy:stun==False|status.enemy:stun==False
# 失衡期逻辑:
#连携技释放逻辑
1361|action+=|1361_QTE|status.enemy:QTE_triggered_times==0|status.enemy:single_qte!=None|special.preload_data:operating_char!=1361
1381|action+=|1381_QTE|status.enemy:single_qte!=None|special.preload_data:operating_char!=1381
# 3E启动逻辑
1381|action+=|1381_E_A|attribute.1381:special_state→满层==True|status.enemy:stun==True
# 维持E连续释放,直至白雷耗尽
1381|action+=|1381_E_A|attribute.1381:special_resource>=1|attribute.1381:special_state→E连击>0|status.enemy:stun==True
# 大招逻辑
1381|action+=|1381_Q|attribute.1381:decibel>=3000|attribute.1381:special_resource<0.01|status.enemy:stun==True|status.1381:lasting_node_tag!=1381_E_A
# 泄能逻辑
1381|action+=|1381_E_EX|attribute.1381:energy>=60|attribute.1381:special_resource<0.01|status.enemy:stun==True
# 零号·安比站场逻辑(临近爆发前)
# 临近失衡时,一直持续平A直到连携技激发
1381|action+=|auto_NA|status.enemy:stun_pct>=0.8|status.enemy:stun==False
# 零号·安比站场逻辑(平时)
# 3E启动逻辑
1381|action+=|1381_E_A|attribute.1381:special_state→满层==True
# 维持E连续释放,直至白雷耗尽
1381|action+=|1381_E_A|attribute.1381:special_resource>=1|attribute.1381:special_state→E连击>0
1361|action+=|1361_Q|attribute.1361:decibel>=3000|status.enemy:stun==False|status.enemy:stun_pct<0.7|status.1361:char_available==True
1361|action+=|1361_E_EX|attribute.1361:energy>=60|status.enemy:stun==False
# 平稳期,如果能量快要溢出且距离怪物失衡还有一段时间,那就可以放强化E
1381|action+=|1381_E_EX|attribute.1381:energy>=100|attribute.1381:special_resource<0.01|status.enemy:stun_pct<=0.5
# 第四段普攻重复最多5次
1381|action+=|1381_NA_4|action.1381:strict_linked_after==1381_NA_4|status.1381:lasting_node_tag==1381_NA_4|status.1381:repeat_times<5
1361|action+=|1361_SNA_3|action.1361:strict_linked_after==1361_SNA_1
1381|action+=|auto_NA
"""
[characters."零号·安比"]
cinema = [ 0, 1, 2, 3, 4, 5, 6,]
weapon = ""
equip_set4 = ""
[characters."扳机"]
cinema = 0
weapon = ""
[characters."丽娜"]
cinema = [ 0,]
weapon = ""
================================================
FILE: zsim/data/APLData/default_APL/1251.txt
================================================
#status.1251:lasting_node_tag==1251_NA_3_NFC|status.1251:lasting_node_tick>=180|!action.after:skill_tag==1251_NA_3_NFC;status.1251:lasting_node_tag==1251_NA_3_NFC|status.1251:lasting_node_tick<180|!action.after:skill_tag==1251_NA_3_NFC
================================================
FILE: zsim/data/APLData/default_APL/1331.txt
================================================
# 1331|action+=|1331_SNA_2|action.1331:strict_linked_after:1331_CoAttack_A|action.1331:second_last_node==1331_SNA_1
================================================
FILE: zsim/data/APLData/仪玄-耀嘉音-扳机.toml
================================================
[general]
title = "仪玄-耀嘉音-扳机"
comment = "仪玄、耀嘉音、扳机的默认APL"
author = "虎皮"
create_time = "2025-06-03T16:44:15.343+08:00"
latest_change_time = "2025-07-02T23:28:16.135+08:00"
[characters]
required = [ "仪玄", "扳机", "耀嘉音",]
optional = []
[apl_logic]
logic = """
# 进攻响应逻辑
# 仪玄 尽快 在闪避后衔接闪避反击
1371|action+=|1371_CA|action.1371:positive_linked_after==1371_dodge
# 仪玄 在招架后 释放支援突击
1371|action+=|assault_after_parry
# 扳机 在招架后 释放支援突击
1361|action+=|assault_after_parry
# 仪玄 尽量延后招架敌人攻击
1371|action.atk_response_balance+=|parry|special.preload_data:operating_char!=1371
# 扳机 尽量延后招架敌人攻击
1361|action.atk_response_balance+=|parry|special.preload_data:operating_char!=1361
# 仪玄 尽量延后闪避敌人攻击(受上面APL影响,此条APL永不执行)
1371|action.atk_response_balance+=|1371_dodge|special.preload_data:operating_char==1371
# 仪玄在前台扳机进行 招架交互 时保持等待
# 防止仪玄在扳机在【招架、被击退、突击支援】过程中用快速支援抢队,最终导致扳机的突击支援打不出来。
1371|action+=|wait|action.1361:during_parry==True
1371|action+=|wait|action.1361:assault_aid_enable==True
# 耀嘉音开场E
1311|action+=|1311_E_A|action.1311:first_action==True
# 扳机补充决意值逻辑:
1361|action+=|1361_BH_Aid|status.1361:quick_assist_available==True|attribute.1361:special_resource<20|status.enemy:stun==False
# 连击逻辑:
1361|action+=|1361_SNA_1|attribute.1361:special_state→狙击姿态==True|attribute.1361:special_resource<100|status.enemy:stun_pct<=0.7
# 启动逻辑
1361|action+=|1361_SNA_0|attribute.1361:special_resource<5|status.enemy:stun==False
# QTE逻辑
1361|action+=|1361_QTE|status.enemy:single_qte!=None|special.preload_data:operating_char!=1361|status.enemy:QTE_triggered_times<1
1371|action+=|1371_QTE|status.enemy:single_qte!=None|special.preload_data:operating_char!=1371|status.enemy:QTE_triggered_times<2
1311|action+=|1311_QTE|status.enemy:single_qte!=None|special.preload_data:operating_char!=1311|status.enemy:QTE_triggered_times<3
# 仪玄响应快支手法
1371|action+=|1371_BH_Aid|status.1371:quick_assist_available==True|status.enemy:single_qte==None|status.enemy:QTE_activation_available==False
# 2画逻辑
1371|action+=|1371_Cinema_2|action.1371:strict_linked_after==1371_SNA_B_2|attribute.1371:cinema>=2|attribute.1371:special_state→聚墨点数>=1
1371|action+=|1371_Cinema_2|action.1371:strict_linked_after==1371_E_EX_B_3|attribute.1371:cinema>=2|attribute.1371:special_state→聚墨点数>=1
# 强化E连段释放逻辑
1371|action+=|1371_E_EX_A_3|action.1371:strict_linked_after==1371_E_EX_A_2|attribute.1371:special_resource>=20
1371|action+=|1371_E_EX_A_2|action.1371:strict_linked_after==1371_E_EX_A_1_Add
1371|action+=|1371_E_EX_A_2|action.1371:strict_linked_after==1371_E_EX_A_1_NFC
# 扳机快支逻辑
1361|action+=|1361_BH_Aid|attribute.1361:energy>=60|status.1361:quick_assist_available==True|status.enemy:stun==False
# 扳机EQ逻辑
1361|action+=|1361_Q|attribute.1361:decibel>=3000|status.enemy:stun==False|status.enemy:stun_pct<0.7|status.1361:char_available==True
1361|action+=|1361_E_EX|attribute.1361:energy>=60|status.enemy:stun==False
# 耀嘉音开大逻辑
1311|action+=|1311_Q|attribute.1311:decibel>=3000|status.enemy:stun==False
# 玄墨极阵释放逻辑
1371|action+=|1371_SNA_B_1|attribute.1371:special_state→玄墨值==1
# 术法大招释放逻辑
1371|action+=|1371_Q_A|attribute.1371:special_state→调息层数==True|attribute.1371:special_state→玄墨值==0|status.enemy:stun==True
1371|action+=|1371_Q_A|attribute.1371:special_state→术法值==120|status.enemy:stun==True
1371|action+=|1371_Q|attribute.1371:decibel==3000|status.enemy:stun==True
# 第一段强化E释放逻辑
# 1371|action+=|1371_E_EX_A_1_FC|attribute.1371:special_resource>=60
# 第一段强化E(测试加速版)释放逻辑
# 1371|action+=|1371_E_EX_A_1_FCT|attribute.1371:special_resource>=60
# 凝云术释放逻辑(失衡期)
1371|action+=|1371_E_EX_B_1|attribute.1371:special_resource>=60|status.enemy:stun==True
# 在敌人远未失衡时开大,避免溢出
1371|action+=|1371_Q_A|status.enemy:stun_pct<=0.2|attribute.1371:special_state→术法值==120
# 在能量快要满时放凝云术
1371|action+=|1371_E_EX_B_1|attribute.1371:special_resource>=110
# 仪玄测试专用APL
1371|action+=|auto_NA
"""
[characters."仪玄"]
cinema = [ 0, 1, 2,]
weapon = ""
equip_set4 = ""
[characters."扳机"]
cinema = [ 0,]
weapon = ""
equip_set4 = ""
[characters."耀嘉音"]
cinema = [ 0,]
weapon = ""
equip_set4 = ""
================================================
FILE: zsim/data/APLData/大安比扳机双人组.toml
================================================
[general]
title = "大安比扳机双人组"
comment = "开发组为 大安比、扳机配队 提供的默认APL"
author = "虎皮"
create_time = "2025-04-15T23:00:00.000+08:00"
latest_change_time = "2025-07-02T22:19:28.732+08:00"
[characters]
required = [ "零号·安比", "扳机",]
optional = [ "丽娜",]
[apl_logic]
logic = """
# 扳机逻辑
# 扳机补充决意值逻辑:
# 连击逻辑:
1361|action+=|1361_SNA_1|attribute.1361:special_state→狙击姿态==True|attribute.1361:special_resource<100|status.enemy:stun_pct<=0.7
# 启动逻辑
1361|action+=|1361_SNA_0|attribute.1361:special_resource<5|status.enemy:stun==False|status.enemy:stun==False
# 失衡期逻辑:
#连携技释放逻辑
1361|action+=|1361_QTE|status.enemy:QTE_triggered_times==0|status.enemy:single_qte!=None|special.preload_data:operating_char!=1361
1381|action+=|1381_QTE|status.enemy:single_qte!=None|special.preload_data:operating_char!=1381
# 3E启动逻辑
1381|action+=|1381_E_A|attribute.1381:special_state→满层==True|status.enemy:stun==True
# 维持E连续释放,直至白雷耗尽
1381|action+=|1381_E_A|attribute.1381:special_resource>=1|attribute.1381:special_state→E连击>0|status.enemy:stun==True
# 大招逻辑
1381|action+=|1381_Q|attribute.1381:decibel>=3000|attribute.1381:special_resource<0.01|status.enemy:stun==True|status.1381:lasting_node_tag!=1381_E_A
# 泄能逻辑
1381|action+=|1381_E_EX|attribute.1381:energy>=60|attribute.1381:special_resource<0.01|status.enemy:stun==True
# 零号·安比站场逻辑(临近爆发前)
# 临近失衡时,一直持续平A直到连携技激发
1381|action+=|auto_NA|status.enemy:stun_pct>=0.8|status.enemy:stun==False
# 零号·安比站场逻辑(平时)
# 3E启动逻辑
1381|action+=|1381_E_A|attribute.1381:special_state→满层==True
# 维持E连续释放,直至白雷耗尽
1381|action+=|1381_E_A|attribute.1381:special_resource>=1|attribute.1381:special_state→E连击>0
1361|action+=|1361_Q|attribute.1361:decibel>=3000|status.enemy:stun==False|status.enemy:stun_pct<0.7|status.1361:char_available==True
1361|action+=|1361_E_EX|attribute.1361:energy>=60|status.enemy:stun==False
# 平稳期,如果能量快要溢出且距离怪物失衡还有一段时间,那就可以放强化E
1381|action+=|1381_E_EX|attribute.1381:energy>=100|attribute.1381:special_resource<0.01|status.enemy:stun_pct<=0.5
# 第四段普攻重复最多5次
1381|action+=|1381_NA_4|action.1381:strict_linked_after==1381_NA_4|status.1381:lasting_node_tag==1381_NA_4|status.1381:repeat_times<5
1361|action+=|1361_SNA_3|action.1361:strict_linked_after==1361_SNA_1
1381|action+=|auto_NA
"""
[characters."零号·安比"]
cinema = [ 0, 1, 2, 3, 4, 5, 6,]
weapon = ""
equip_set4 = ""
[characters."扳机"]
cinema = [ 0,]
weapon = ""
equip_set4 = ""
[characters."丽娜"]
cinema = [ 0,]
weapon = ""
equip_set4 = ""
================================================
FILE: zsim/data/APLData/席德-大安比-扳机.toml
================================================
[general]
title = "席德-大安比-扳机"
comment = "开发组为席德、大安比、扳机 提供的默认APL"
author = "虎皮"
create_time = "2025-04-15T23:00:00.000+08:00"
latest_change_time = "2025-09-20T14:33:15.900+08:00"
[characters]
required = [
"席德",
"零号·安比",
"扳机",
]
optional = []
[characters."零号·安比"]
cinema = [
0,
1,
2,
3,
4,
5,
6,
]
weapon = ""
equip_set4 = ""
[characters."扳机"]
cinema = [
0,
]
weapon = ""
equip_set4 = ""
[characters."席德"]
cinema = [
0,
]
weapon = ""
equip_set4 = ""
[apl_logic]
logic = """
# -------------------------最高优先级----------------------------
# APL安排了尽量让扳机的强化E激发连携技的手法,所以另外两个角色在此期间需要保持静默。这个保持静默的优先级是非常高的,要提到最前面来。
1381|action+=|wait|(status.enemy:stun_pct>=0.95 or status.enemy:stun==True)|action.1361:positive_linked_after==1361_E_EX
1461|action+=|wait|(status.enemy:stun_pct>=0.95 or status.enemy:stun==True)|action.1361:positive_linked_after==1361_E_EX
# 零号·安比一旦3E开始,就必须持续E连击
1381|action+=|1381_E_A|attribute.1381:special_resource>=1|attribute.1381:special_state→E连击>0
# 席德一旦在SNA_1释放后发现钢能值足够,就必须打完三连。
1461|action+=|auto_NA|action.1461:positive_linked_after==1461_SNA_1|attribute.1461:special_state→钢能值足够==True
# -------------------------爆发期---------------------------- 底层逻辑是:enemy.stun==True
# 首先是QTE手法,优先放扳机的,然后放零号安比的,然后放席德的,保证尽量让席德收尾。
1381|action+=|1381_QTE|status.enemy:single_qte!=None|special.preload_data:operating_char!=1381
1461|action+=|1461_QTE|status.enemy:single_qte!=None|special.preload_data:operating_char!=1461
# 合轴逻辑。双C相互合轴是提高爆发质量的底层逻辑,这里必须前置。
# 零号·安比尝试在席德的SNA_2后合轴打3E,这要求在自身条件满足的同时,还满足合轴条件(席德已经释放了SNA_2,同时席德处于前台)
1381|action+=|1381_E_A|status.enemy:stun==True|attribute.1381:special_state→满层==True|action.1461:positive_linked_after==1461_SNA_2|special.preload_data:operating_char==1461
# 安比在席德释放SNA_2时候,尝试使用Q、强化E合轴。由于安比不是爆发主力,所以对于银星的审查就不那么严格了,尽量把技能甩出来才是第一目标。
1381|action+=|1381_Q|status.enemy:stun==True|special.preload_data:operating_char==1461|attribute.1381:decibel>=3000|attribute.1381:special_resource<=2|action.1461:positive_linked_after==1461_SNA_2
1381|action+=|1381_E_EX|status.enemy:stun==True|special.preload_data:operating_char==1461|attribute.1381:energy>=60|attribute.1381:special_resource<=2|action.1461:positive_linked_after==1461_SNA_2
# 席德在大安比释放强化E时,尝试使用大招合轴。但是需要避免自己吞掉自己的SNA_1、SNA_2、SNA_3
1461|action+=|1461_Q|status.enemy:stun==True|special.preload_data:operating_char==1381|attribute.1461:decibel>=3000|attribute.1461:special_resource<=70|action.1381:positive_linked_after==1381_E_EX|(action.1461:positive_linked_after!=1461_SNA_1 and action.1461:positive_linked_after!=1461_SNA_2 and action.1461:positive_linked_after!=1461_SNA_3)
# 席德在大安比释放强化E时,尝试使用SNA_1进行合轴(如果钢能值满,且快速释放机会还在)
1461|action+=|1461_SNA_1|status.enemy:stun==True|special.preload_data:operating_char==1381|attribute.1461:special_state→钢能值足够==True|attribute.1461:special_state→sna快速释放==True|action.1381:positive_linked_after==1381_E_EX
# 席德输出阶段,先检测重击,然后检测大招释放。需要考虑大招不要提前在三连期间释放,防止顶替亏损伤害。
1461|action+=|1461_SNA_1|status.enemy:stun==True|attribute.1461:special_state→钢能值足够==True|attribute.1461:special_state→sna快速释放==True
1461|action+=|1461_Q|status.enemy:stun==True|attribute.1461:special_resource<=70|attribute.1461:decibel>=3000|(action.1461:positive_linked_after!=1461_SNA_1 and action.1461:positive_linked_after!=1461_SNA_2 and action.1461:positive_linked_after!=1461_SNA_3)
# 大安比输出阶段,先检测3E,然后再检测大招、强化E,此处需要严格把控强化E和大招不要溢出银星
1381|action+=|1381_E_A|status.enemy:stun==True|attribute.1381:special_state→满层==True
1381|action+=|1381_Q|status.enemy:stun==True|attribute.1381:decibel>=3000|attribute.1381:special_resource<=0.5
1381|action+=|1381_E_EX|status.enemy:stun==True|attribute.1381:energy>=60|attribute.1381:special_resource<=0.5
# 收尾阶段,当整个爆发期没有别的可以做的事情的时候,那就只能让席德释放强化E或是平A了,相当于爆发期的止损手法。
# 席德强化E连续释放与启动逻辑
1461|action+=|1461_E_EX|status.enemy:stun==True|attribute.1461:energy>=10|attribute.1461:special_state→强化E连续释放==True
1461|action+=|1461_E_EX|status.enemy:stun==True|attribute.1461:energy>=60|attribute.1461:special_resource<=80
1461|action+=|auto_NA|status.enemy:stun==True
# -------------------------非失衡期---------------------------- 底层逻辑是:status.enemy:stun==False
# 非失衡期的头等大事就是扳机的决意值补充
1361|action+=|1361_SNA_1|status.enemy:stun==False|attribute.1361:special_state→狙击姿态==True|attribute.1361:special_resource<100
1361|action+=|1361_SNA_0|attribute.1361:special_resource<5|status.enemy:stun==False|status.enemy:stun==False
# ==========(临近失衡)========== 底层逻辑是:status.enemy:stun_pct>=0.8
# 首先,如果怪物的失衡百分比已经接近99%,则尽可能尝试让扳机用强化E激发连携。同时,其他两名角色需要保持静默,不要合轴,以免影响连携技激发。
1361|action+=|1361_E_EX|status.enemy:stun==False|status.enemy:stun_pct>=0.95|attribute.1361:energy>=60
# 其他情况,在席德钢能值不足80点时,由席德通过普攻站场,席德钢能值一旦达到80点,就切下去让大安比战场,以免席德误触SNA_1影响爆发。
1461|action+=|auto_NA|status.enemy:stun==False|status.enemy:stun_pct>=0.8|attribute.1461:special_resource<80
1381|action+=|auto_NA|status.enemy:stun==False|status.enemy:stun_pct>=0.8
# ==========(非临近失衡)========== 底层逻辑:status.enemy:stun==False
# 应该保证大安比、席德的SNA_1和3E在非临近失衡阶段的启动处于高优先级
1381|action+=|1381_E_A|status.enemy:stun==False|attribute.1381:special_state→满层==True
1461|action+=|1461_SNA_1|status.enemy:stun==False|attribute.1461:special_state→钢能值足够==True|attribute.1461:special_state→sna快速释放==True
# 应保证扳机大招在非临近失衡期、低失衡百分比时的高优先级,然后保证其强化E不要溢出,为临近失衡期留好能量。
1361|action+=|1361_Q|attribute.1361:decibel>=3000|status.enemy:stun==False|status.enemy:stun_pct<=0.6|status.1361:char_available==True
1361|action+=|1361_E_EX|attribute.1361:energy>=110|status.enemy:stun==False|status.enemy:stun_pct<=0.7
# 考虑大安比的能量、喧响值严重溢出的情况,如果喧响值发生了溢出,则只允许在 较低的失衡百分比下释放Q,席德大招同理。
1381|action+=|1381_E_EX|status.enemy:stun==False|attribute.1381:energy>=110|attribute.1381:special_resource<=0.5
1381|action+=|1381_Q|status.enemy:stun==False|status.enemy:stun_pct<=0.2|attribute.1381:decibel>=3000|attribute.1381:special_resource<=0.5
1461|action+=|1461_Q|status.enemy:stun==False|status.enemy:stun_pct<=0.2|attribute.1461:decibel>=3000|attribute.1461:special_resource<=70
# 席德泄能
1461|action+=|1461_E_EX|status.enemy:stun==False|attribute.1461:energy>=10|attribute.1461:special_state→强化E连续释放==True
1461|action+=|1461_E_EX|status.enemy:stun==False|attribute.1461:energy>=60|attribute.1461:special_resource<=80
# 席德平A作为底层逻辑。
1461|action+=|auto_NA|status.enemy:stun==False
"""
================================================
FILE: zsim/data/APLData/柚叶-雅-薇薇安.toml
================================================
[general]
title = "柚叶-雅-薇薇安"
comment = "开发组为 柚叶、雅、薇薇安 提供的默认APL"
author = "虎皮"
create_time = "2025-07-15T14:06:38.457+08:00"
latest_change_time = "2025-07-27T03:23:03.891+08:00"
[characters]
required = [ "柚叶", "薇薇安", "雅",]
optional = []
[apl_logic]
logic = """
#------------------------------最高优先级------------------------------
# 三名角色在招架后总是打支援突击
1411|action+=|assault_after_parry
1091|action+=|assault_after_parry
1331|action+=|assault_after_parry
# 所有角色在其他角色招架过程中保持静默,确保招架流程完整,突击支援能顺利释放。
1091|action+=|wait|action.1411:during_parry==True or action.1331:during_parry==True
1331|action+=|wait|action.1411:during_parry==True or action.1091:during_parry==True
1411|action+=|wait|action.1331:during_parry==True or action.1091:during_parry==True
1091|action+=|wait|action.1411:assault_aid_enable==True or action.1331:assault_aid_enable==True
1331|action+=|wait|action.1411:assault_aid_enable==True or action.1091:assault_aid_enable==True
1411|action+=|wait|action.1331:assault_aid_enable==True or action.1091:assault_aid_enable==True
# 柚叶在支援突击后总、需要补充Buff时是衔接强化E(含6画)
1411|action+=|1411_E_EX_B|attribute.1411:energy>=60|action.1411:positive_linked_after==1411_Assault_Aid_B|attribute.1411:cinema==6|buff.1411:duration→Buff-角色-柚叶-核心被动-狸之愿-攻击力<=300
1411|action+=|1411_E_EX_B|attribute.1411:energy>=60|action.1411:positive_linked_after==1411_Assault_Aid_A|buff.1411:duration→Buff-角色-柚叶-核心被动-狸之愿-攻击力<=300
# 6画情况下,没Buff时柚叶弹刀
1411|action.atk_response_balance+=|parry|special.preload_data:operating_char!=1411|attribute.1411:cinema==6|buff.1411:count→Buff-角色-柚叶-6画-紊乱伤害倍率提升==0
# 在需要补核心被动Buff时且能量足够时优先柚叶弹刀
1411|action.atk_response_balance+=|parry|special.preload_data:operating_char!=1411|buff.1411:duration→Buff-角色-柚叶-核心被动-狸之愿-攻击力<=300|attribute.1411:energy>=60
# 雅 尽快 在闪避后衔接闪避反击
1091|action+=|1091_CA|action.1091:positive_linked_after==1091_dodge
# 快速支援逻辑(必须在非彩色失衡阶段执行)
1331|action+=|1331_BH_Aid|status.1331:quick_assist_available==True|status.enemy:QTE_activation_available==False
1091|action+=|1091_BH_Aid|status.1091:quick_assist_available==True|status.enemy:QTE_activation_available==False
# 其他角色在柚叶释放强化E期间等待,确保快速支援的触发
1091|action+=|wait|action.1411:is_performing==1411_E_EX_A or action.1411:is_performing==1411_E_EX_B
1331|action+=|wait|action.1411:is_performing==1411_E_EX_A or action.1411:is_performing==1411_E_EX_B
# 补Buff逻辑:能量不够且没Buff时平A;在需要补Buff的场合,优先开大;
1411|action+=|1411_Q|buff.1411:exist→Buff-角色-柚叶-核心被动-狸之愿-攻击力==False|attribute.1411:decibel>=3000
1411|action+=|1411_E_EX_A|buff.1411:exist→Buff-角色-柚叶-核心被动-狸之愿-攻击力==False|attribute.1411:energy>=60
1411|action+=|auto_NA|buff.1411:exist→Buff-角色-柚叶-核心被动-狸之愿-攻击力==False|attribute.1411:energy<60
# 薇薇安开伞状态下打SNA1
1331|action+=|1331_SNA_1|attribute.1331:special_state→淑女仪态==True
#雅6豆蓄力——傻瓜版,6豆有了就放
1091|action+=|1091_SNA_3|attribute.1091:special_resource==6
# 柚叶无甜度点时候强化E
1411|action+=|1411_E_EX_A|attribute.1411:special_resource<=0|attribute.1411:energy>=60
#------------------------------无异常阶段------------------------------
# 启动阶段,只有当VVA没有护羽但是有飞羽时让VVA招架、在柚叶有能量切需要补Buff时候招架、其余情况都是雅闪避
1331|action.atk_response_balance+=|parry|special.preload_data:operating_char!=1331|status.enemy:is_under_anomaly==False|attribute.1331:energy<60|attribute.1331:special_resource==0|attribute.1331:special_state→飞羽数量!=0
1411|action.atk_response_balance+=|parry|special.preload_data:operating_char!=1411|status.enemy:is_under_anomaly==False|attribute.1411:energy>=60|buff.1411:duration→Buff-角色-柚叶-核心被动-狸之愿-攻击力<=300
1091|action.atk_response_balance+=|1091_dodge|special.preload_data:operating_char==1091|status.enemy:is_under_anomaly==False
# QTE逻辑——当敌人不处于异常状态时(含2画)
1091|action+=|1091_QTE_A|status.enemy:is_under_anomaly==False|status.enemy:single_qte!=None|special.preload_data:operating_char!=1091
1331|action+=|1331_QTE|status.enemy:is_under_anomaly==False|status.enemy:single_qte!=None|special.preload_data:operating_char!=1331
# 雅大招快速启动(在保证积蓄值和豆子都不会大量溢出的情况下)
1091|action+=|1091_Q|status.enemy:is_under_anomaly==False|attribute.1091:decibel>=3000|status.enemy:anomaly_pct_5<0.2|attribute.1091:special_resource<=3|action.1091:is_performing!=1091_SNA_3
# 雅强化E(在保证豆子不溢出的情况下)
1091|action+=|1091_E_EX_A_1|status.enemy:is_under_anomaly==False|attribute.1091:energy>=40|attribute.1091:special_resource<=4
# 雅平A(无异常阶段的底层逻辑)
1091|action+=|auto_NA|status.enemy:is_under_anomaly==False
#------------------------------侵蚀分支------------------------------
# 侵蚀分支时,薇薇安只在能量不够、没有豆子时候招架;或是在Buff快断时切柚叶招架;其余情况下都是雅打闪反
1331|action.atk_response_balance+=|parry|special.preload_data:operating_char!=1331|status.enemy:is_corruption==True|attribute.1331:energy<60|attribute.1331:special_resource==0|attribute.1331:special_state→飞羽数量!=0
1411|action.atk_response_balance+=|parry|special.preload_data:operating_char!=1411|status.enemy:is_corruption==True|attribute.1411:energy>=60|buff.1411:duration→Buff-角色-柚叶-核心被动-狸之愿-攻击力<=300
1091|action.atk_response_balance+=|1091_dodge|special.preload_data:operating_char==1091|status.enemy:is_corruption==True
# 雅QTE(侵蚀分支的第一优先级依旧是雅的QTE)
1091|action+=|1091_QTE_A|status.enemy:is_corruption==True|status.enemy:single_qte!=None|special.preload_data:operating_char!=1091
# 柚叶QTE
1411|action+=|1411_QTE|status.enemy:is_corruption==True|status.enemy:single_qte!=None|special.preload_data:operating_char!=1411
1331|action+=|1331_QTE|status.enemy:is_corruption==True|status.enemy:single_qte!=None|special.preload_data:operating_char!=1331
# 雅大招(比启动阶段更严格的积蓄条、豆子审查条件)
1091|action+=|1091_Q|status.enemy:is_corruption==True|attribute.1091:decibel>=3000|status.enemy:anomaly_pct_5<=0.6|attribute.1091:special_resource<=2|action.1091:is_performing!=1091_SNA_3
# 雅强化E(较为宽松的豆子审查)
1091|action+=|1091_E_EX_A_1|status.enemy:is_corruption==True|attribute.1091:energy>=40|attribute.1091:special_resource<=3
# 柚叶在Q、E能确保触发强击时释放Q、E,并且优先检测E
1411|action+=|1411_E_EX_A|status.enemy:is_corruption==True|attribute.1411:energy>=60|status.enemy:anomaly_pct_0>=0.95
1411|action+=|1411_Q|status.enemy:is_corruption==True|attribute.1411:decibel>=3000|status.enemy:anomaly_pct_0>=0.85
# 雅在能量快要溢出时强制强化E
1091|action+=|1091_E_EX_A_1|status.enemy:is_corruption==True|attribute.1091:energy>=115
# 雅持续平A(普攻逻辑)
1091|action+=|auto_NA|status.enemy:is_corruption==True
#------------------------------烈霜分支------------------------------
# 烈霜分支的第一要务是尽快触发以太或是强击侵蚀,所以这个分支其实是没有任何雅的逻辑的
# 如果敌人正处于烈霜分支下,那么招架应优先留给VVA,VVA接不住的招架再留给柚叶
1331|action.atk_response_balance+=|parry|special.preload_data:operating_char!=1331|status.enemy:is_frost_frostbite==True
1411|action.atk_response_balance+=|parry|special.preload_data:operating_char!=1411|status.enemy:is_frost_frostbite==True
# 柚叶和薇薇安QTE
1411|action+=|1411_QTE|status.enemy:is_frost_frostbite==True|status.enemy:single_qte!=None|special.preload_data:operating_char!=1411
1331|action+=|1331_QTE|status.enemy:is_frost_frostbite==True|status.enemy:single_qte!=None|special.preload_data:operating_char!=1331
1091|action+=|1091_QTE_A|status.enemy:is_frost_frostbite==True|status.enemy:single_qte!=None|special.preload_data:operating_char!=1091
# 薇薇安大招(确保飞羽、护羽、以太积蓄不严重溢出)
1331|action+=|1331_Q|status.enemy:is_frost_frostbite==True|attribute.1331:decibel>=3000|status.enemy:anomaly_pct_4<=0.5|attribute.1331:special_state→飞羽数量<=1|attribute.1331:special_resource<=3
# 薇薇安强化E(确保飞羽不严重溢出)
1331|action+=|1331_E_EX|status.enemy:is_frost_frostbite==True|attribute.1331:energy>=60|attribute.1331:special_state→飞羽数量<=2
# 柚叶的Q、E检测(只在快要触发强击时释放,优先开大)
1411|action+=|1411_Q|status.enemy:is_frost_frostbite==True|attribute.1411:decibel>=3000|status.enemy:anomaly_pct_0>=0.85
1411|action+=|1411_E_EX_A|status.enemy:is_frost_frostbite==True|attribute.1411:energy>=60|status.enemy:anomaly_pct_0>=0.95
# 烈霜分支的底层逻辑总体上是雅的站场,所以在这里要补一个雅的闪反(可以理解为雅的闪反并不具备较高优先级。)
1091|action.atk_response_balance+=|1091_dodge|special.preload_data:operating_char==1091|status.enemy:is_frost_frostbite==True
# 哪怕处于烈霜分支,如果后台的以太积蓄马上就要满了,可以使用强化E来触发VVA的协同来触发以太异常;(要确保自己豆子不会溢出、VVA有豆子)
1091|action+=|1091_E_EX_A_1|status.enemy:is_frost_frostbite==True|status.enemy:anomaly_pct_4>=0.97|status.enemy:buildup_pct_delta_4_5>=0.3|attribute.1331:special_resource>=1|attribute.1091:energy>=40|attribute.1091:special_resource<=3
# 在烈霜分支,只要烈霜积蓄还没有达到阈值线(70%),豆子也不严重溢出,就可以在豆子允许的范围内释放强化E避免能量溢出
1091|action+=|1091_E_EX_A_1|status.enemy:is_frost_frostbite==True|status.enemy:anomaly_pct_5<=0.5|attribute.1091:special_resource<=4|attribute.1091:energy>=115
# 在烈霜分支时,还是要保持雅的持续平A;
1091|action+=|auto_NA|status.enemy:is_frost_frostbite==True
#------------------------------强击分支------------------------------
# 强击分支是特殊情况,逻辑类似于启动
# 进攻交互部分,在强击分支中的逻辑类似于启动逻辑
1331|action.atk_response_balance+=|parry|special.preload_data:operating_char!=1331|status.enemy:is_assault==True|attribute.1331:energy<60|attribute.1331:special_resource==0|attribute.1331:special_state→飞羽数量!=0
1411|action.atk_response_balance+=|parry|special.preload_data:operating_char!=1411|status.enemy:is_assault==True|attribute.1411:energy>=60|buff.1411:duration→Buff-角色-柚叶-核心被动-狸之愿-攻击力<=300
1091|action.atk_response_balance+=|1091_dodge|special.preload_data:operating_char==1091|status.enemy:is_assault==True
# QTE逻辑
1091|action+=|1091_QTE_A|status.enemy:is_assault==True|status.enemy:single_qte!=None|special.preload_data:operating_char!=1091
1331|action+=|1331_QTE|status.enemy:is_assault==True|status.enemy:single_qte!=None|special.preload_data:operating_char!=1331
1411|action+=|1411_QTE|status.enemy:is_assault==True|status.enemy:single_qte!=None|special.preload_data:operating_char!=1411
# 雅大招逻辑(只用防止豆子溢出)
1091|action+=|1091_Q|status.enemy:is_assault==True|attribute.1091:decibel>=3000|attribute.1091:special_resource<=2|action.1091:is_performing!=1091_SNA_3
# 薇薇安大招(确保飞羽、护羽、以太积蓄不严重溢出)
1331|action+=|1331_Q|status.enemy:is_assault==True|attribute.1331:decibel>=3000|status.enemy:anomaly_pct_4<=0.5|attribute.1331:special_state→飞羽数量<=1|attribute.1331:special_resource<=3
# 雅强化E(较为宽松的豆子审查)
1091|action+=|1091_E_EX_A_1|status.enemy:is_assault==True|attribute.1091:energy>=40|attribute.1091:special_resource<=3
# 薇薇安强化E(确保飞羽不严重溢出)
1331|action+=|1331_E_EX|status.enemy:is_assault==True|attribute.1331:energy>=60|attribute.1331:special_state→飞羽数量<=2
# 底层逻辑——雅平A
1091|action+=|auto_NA|status.enemy:is_assault==True"""
[characters."柚叶"]
cinema = []
weapon = ""
equip_set4 = ""
[characters."薇薇安"]
cinema = []
weapon = ""
equip_set4 = ""
[characters."雅"]
cinema = []
weapon = ""
equip_set4 = ""
================================================
FILE: zsim/data/APLData/爱丽丝-柚叶-简.toml
================================================
[general]
title = "爱丽丝-柚叶-简"
comment = "开发组为爱丽丝-柚叶-简 提供的默认APL"
author = "虎皮"
create_time = "2025-08-20T16:24:38.457+08:00"
latest_change_time = "2025-08-28T23:31:02.313+08:00"
[characters]
required = [ "爱丽丝", "柚叶", "简",]
optional = []
[apl_logic]
logic = """
#------------------------------最高优先级------------------------------
# 三名角色在招架后总是打支援突击
1411|action+=|assault_after_parry
1261|action+=|assault_after_parry
1401|action+=|assault_after_parry
# 所有角色在其他角色招架过程中保持静默,确保招架流程完整,突击支援能顺利释放。
1261|action+=|wait|action.1411:during_parry==True or action.1401:during_parry==True
1401|action+=|wait|action.1411:during_parry==True or action.1261:during_parry==True
1411|action+=|wait|action.1401:during_parry==True or action.1261:during_parry==True
1261|action+=|wait|action.1411:assault_aid_enable==True or action.1401:assault_aid_enable==True
1401|action+=|wait|action.1411:assault_aid_enable==True or action.1261:assault_aid_enable==True
1411|action+=|wait|action.1401:assault_aid_enable==True or action.1261:assault_aid_enable==True
# 柚叶弹刀逻辑(要补Buff时优先让柚叶弹刀弹刀)
1411|action.atk_response_balance+=|parry|special.preload_data:operating_char!=1411|attribute.1411:energy>=60|buff.1411:duration→Buff-角色-柚叶-核心被动-狸之愿-攻击力<=300
# 快速支援逻辑(在非彩色失衡阶段)
1261|action+=|1261_BH_Aid|status.1261:quick_assist_available==True|status.enemy:QTE_activation_available==False
1401|action+=|1401_BH_Aid|status.1401:quick_assist_available==True|status.enemy:QTE_activation_available==False
# 补Buff逻辑:能量不够且没Buff时平A;在需要补Buff的场合,优先开大;
1411|action+=|1411_Q|buff.1411:exist→Buff-角色-柚叶-核心被动-狸之愿-攻击力==False|attribute.1411:decibel>=3000
1411|action+=|1411_E_EX_A|buff.1411:exist→Buff-角色-柚叶-核心被动-狸之愿-攻击力==False|attribute.1411:energy>=60
1411|action+=|auto_NA|buff.1411:exist→Buff-角色-柚叶-核心被动-狸之愿-攻击力==False|attribute.1411:energy<60
# 柚叶无甜度点时候强化E
1411|action+=|1411_E_EX_A|attribute.1411:special_resource<=0|attribute.1411:energy>=60
# 简 尽快 在闪避后衔接闪避反击
1261|action+=|1261_CA_1|action.1261:positive_linked_after==1261_dodge
# 爱丽丝3蓄
1401|action+=|1401_SNA_3|attribute.1401:special_resource>=3
#------------------------------爱丽丝和简的输出逻辑------------------------------
# 进攻交互逻辑,这支队伍常规情况下是让简闪反的,实在没办法再让爱丽丝弹刀
1261|action.atk_response_balance+=|1261_dodge|special.preload_data:operating_char==1261
1401|action.atk_response_balance+=|parry|special.preload_data:operating_char!=1401
# QTE
1261|action+=|1261_QTE|status.enemy:is_frost_frostbite==True|status.enemy:single_qte!=None|special.preload_data:operating_char!=1261
1401|action+=|1401_QTE|status.enemy:is_frost_frostbite==True|status.enemy:single_qte!=None|special.preload_data:operating_char!=1401
1411|action+=|1411_QTE|status.enemy:is_frost_frostbite==True|status.enemy:single_qte!=None|special.preload_data:operating_char!=1411
#爱丽丝和简的泄能、大招逻辑其实并没有严格的先后顺序,在这份APL中我们选择让爱丽丝优先泄能、打大招。
# 爱丽丝在不严重溢出剑仪值时用强化E(南十字)
1401|action+=|1401_E_EX_1|attribute.1401:energy>=40|attribute.1401:special_resource<=2
# 爱丽丝在不严重溢出剑仪值时开大
1401|action+=|1401_Q|attribute.1401:decibel>=3000|attribute.1401:special_resource<=1
# 简仅在没有狂热状态、或是狂热值较低时候开大
1261|action+=|1261_Q|((attribute.1261:special_state→狂热状态==True and attribute.1261:special_resource<=40) or attribute.1261:special_state→狂热状态==False)|attribute.1261:decibel>=3000
# 简仅在有狂热状态且能量足够时开强化E
1261|action+=|1261_E_EX|attribute.1261:energy>=60|attribute.1261:special_state→狂热状态==True
# 爱丽丝在剑仪值快满且没资源时平A
1401|action+=|auto_NA|attribute.1401:energy<40|attribute.1401:decibel<3000|attribute.1401:special_resource>=2.5|attribute.1401:special_resource<3
# 底层逻辑:简平A
1261|action+=|auto_NA
"""
[characters."爱丽丝"]
cinema = []
weapon = ""
equip_set4 = ""
[characters."柚叶"]
cinema = []
weapon = ""
equip_set4 = ""
[characters."简"]
cinema = []
weapon = ""
equip_set4 = ""
================================================
FILE: zsim/data/APLData/莱特-扳机-雨果.toml
================================================
[general]
title = "莱特-扳机-雨果测试APL"
comment = "这是开发组为莱特、扳机、雨果队提供的默认APL"
author = "虎皮"
create_time = "2025-05-23T04:18:04.155+08:00"
latest_change_time = "2025-05-23T04:19:23.815+08:00"
[characters]
required = [ "扳机", "莱特", "雨果",]
optional = []
[apl_logic]
logic = """
# 扳机补充决意值逻辑:
# 连击逻辑:
1361|action+=|1361_SNA_1|attribute.1361:special_state→狙击姿态==True|attribute.1361:special_resource<100|status.enemy:stun_pct<=0.7
# 启动逻辑
1361|action+=|1361_SNA_0|attribute.1361:special_resource<5|status.enemy:stun==False
# QTE逻辑
1291|action+=|1291_QTE|status.enemy:single_qte!=None|special.preload_data:operating_char!=1291|status.enemy:QTE_triggered_times<1
1161|action+=|1161_E_EX_1|status.enemy:single_qte==None|status.enemy:stun==True|status.enemy:QTE_activation_available==True|attribute.1161:energy>=40|status.enemy:QTE_triggered_times<1
1161|action+=|1161_E|status.enemy:single_qte==None|status.enemy:stun==True|status.enemy:QTE_activation_available==True|attribute.1161:energy<40|status.enemy:QTE_triggered_times<1
# 莱特消耗士气的逻辑
1161|action.no_swap_cancel+=|1161_NA_5_EnEndH_EX|action.1161:strict_linked_after==1161_NA_5_CoH_EX
1161|action.no_swap_cancel+=|1161_NA_5_CoH_EX|action.1161:strict_linked_after==1161_NA_5_SH_EX
1161|action.no_swap_cancel+=|1161_NA_5_SH_EX|action.1161:strict_linked_after==1161_BH_Aid|status.enemy:stun==False
# 非失衡期泄能逻辑
1291|action+=|1291_E_EX_1|attribute.1291:energy>=85|status.enemy:stun==False
# 莱特EQ逻辑
1161|action+=|1161_Q|attribute.1161:decibel>=3000|buff.1161:count→Buff-角色-莱特-核心被动-冲击力提升>=75|status.enemy:stun_pct<0.5
1161|action+=|1161_E_EX_2|attribute.1161:energy>=80|buff.1161:count→Buff-角色-莱特-核心被动-冲击力提升>=75|action.1161:strict_linked_after==1161_E_EX_1|status.enemy:stun_pct<0.5
1161|action.no_swap_cancel+=|1161_E_EX_1|attribute.1161:energy>=100|buff.1161:count→Buff-角色-莱特-核心被动-冲击力提升>=75|status.enemy:stun_pct<0.5
# 扳机EQ逻辑
1361|action+=|1361_Q|attribute.1361:decibel>=3000|status.enemy:stun==False|status.enemy:stun_pct<0.7|status.1361:char_available==True
1361|action+=|1361_E_EX|attribute.1361:energy>=60|status.enemy:stun==False
# 莱特士气足够时,上场释放快支
1161|action.no_swap_cancel+=|1161_BH_Aid|attribute.1161:special_resource>=75|status.1161:on_field==False|status.enemy:stun==False
# 莱特站场
# 1161|action+=|auto_NA|status.enemy:stun==False
# 失衡期爆发逻辑
# 1291|action+=|auto_NA|buff.1291:exist→Buff-角色-雨果-4画-蓄力射击减冰抗==False|attribute.1291:cinema>=4
# 4画专用逻辑
# 1291|action+=|1291_Q|attribute.1291:decibel>=3000|status.enemy:stun==True|status.enemy:QTE_triggered_times>=1|status.1291:char_available==True|buff.1291:exist→Buff-角色-雨果-4画-蓄力射击减冰抗==True
# 1291|action+=|1291_E_EX_1|attribute.1291:energy>=40|status.enemy:stun==True|status.enemy:QTE_triggered_times>=1|buff.1291:exist→Buff-角色-雨果-4画-蓄力射击减冰抗==True
1291|action+=|1291_Q|attribute.1291:decibel>=3000|status.enemy:stun==True|status.enemy:QTE_triggered_times>=1|status.1291:char_available==True
1291|action+=|1291_E_EX_1|attribute.1291:energy>=40|status.enemy:stun==True|status.enemy:QTE_triggered_times>=1
# 雨果自动平A测试
1291|action+=|auto_NA
"""
[characters."扳机"]
cinema = [ 0,]
weapon = ""
equip_set4 = ""
[characters."莱特"]
cinema = [ 0,]
weapon = ""
equip_set4 = ""
[characters."雨果"]
cinema = [ 0, 1, 2, 3, 4, 5, 6,]
weapon = ""
equip_set4 = ""
================================================
FILE: zsim/data/APLData/薇薇安-柳-耀嘉音.toml
================================================
[general]
title = "薇薇安-柳-耀嘉音"
comment = "开发组为 薇薇安、柳、耀嘉音队伍提供的默认APL"
author = "虎皮"
create_time = "2025-04-27T22:14:15.235+08:00"
latest_change_time = "2025-05-22T01:23:35.784+08:00"
[characters]
required = [ "薇薇安", "耀嘉音", "柳",]
optional = []
[apl_logic]
logic = "# 耀嘉音开场E\r\n1311|action+=|1311_E_A|action.1311:first_action==True\r\n1331|action+=|1331_SNA_2|status.1331:on_field==False|attribute.1331:special_state→裙裾浮游==True\r\n\r\n# 柳硬等快支触发,不提前上场抢队。\r\n1221|action+=|wait|status.1221:assist_waiting_for_anwser==True\r\n1221|action+=|1221_BH_Aid|status.1221:quick_assist_available==True|status.enemy:is_shock==False\r\n1331|action+=|1331_BH_Aid|status.1331:quick_assist_available==True|status.enemy:is_corruption==False\r\n\r\n# 柳开场强化E\r\n1221|action+=|1221_E_EX_1|attribute.1221:energy>=40|action.1221:first_action==True\r\n\r\n\r\n# VVA测试\r\n1331|action+=|1331_Q|status.enemy:is_corruption==False|attribute.1331:decibel==3000\r\n1331|action+=|1331_E_EX|attribute.1331:energy>=60|status.enemy:is_corruption==False\r\n\r\n# 强化E飘浮后,长按闪避回到开伞状态\r\n# 1331|action+=|1331_SNA_0|attribute.1331:special_state→裙裾浮游==True|action.1331:strict_linked_after==1331_E_EX||status.enemy:is_corruption==False\r\n# 开伞状态下打SNA1\r\n1331|action+=|1331_SNA_1|attribute.1331:special_state→淑女仪态==True\r\n\r\n\r\n# 柳切换架势逻辑\r\n1221|action+=|1221_E_A|buff.1221:duration→Buff-角色-柳-架势-下弦<=120|action.1221:strict_linked_after==1221_NA_5\r\n1221|action+=|1221_E_A|buff.1221:duration→Buff-角色-柳-架势-上弦<=120|action.1221:strict_linked_after==1221_SNA_5\r\n# 柳平A逻辑——不是感电就A\r\n1221|action+=|auto_NA|status.enemy:is_shock==False\r\n\r\n# 柳强化E释放逻辑\r\n# 强化E终结一击逻辑(2~5命),1穿刺达到2次上限后再接2\r\n1221|action+=|1221_E_EX_2|action.1221:strict_linked_after==1221_E_EX_1|attribute.1221:cinema>=2|attribute.1221:cinema<6|status.1221:lasting_node_tag==1221_E_EX_1|status.1221:repeat_times>1\r\n# 强化E终结一击逻辑(6命),1穿刺达到4次上限后再接2\r\n1221|action+=|1221_E_EX_2|action.1221:strict_linked_after==1221_E_EX_1|attribute.1221:cinema<=6|status.1221:lasting_node_tag==1221_E_EX_1|status.1221:repeat_times>3\r\n\r\n# 连击逻辑\r\n1221|action+=|1221_E_EX_1|attribute.1221:cinema>1|attribute.1221:energy>=40|action.1221:strict_linked_after==1221_E_EX_1|status.enemy:is_under_anomaly==True\r\n# 启动逻辑\r\n1221|action+=|1221_Q|attribute.1221:decibel==3000|status.enemy:is_under_anomaly==True\r\n1221|action+=|1221_E_EX_1|attribute.1221:energy>=50|status.enemy:is_under_anomaly==True\r\n\r\n# 手动释放SNA2——VVA的SNA_2大多会强制在技能后面自动释放,所以基本没有手动释放的时候。\r\n1331|action+=|1331_SNA_2|attribute.1331:special_state→裙裾浮游==True\r\n\r\n1221|action+=|auto_NA\r\n\r\n"
[characters."薇薇安"]
cinema = []
weapon = ""
equip_set4 = ""
[characters."耀嘉音"]
cinema = []
weapon = ""
equip_set4 = ""
[characters."柳"]
cinema = []
weapon = ""
equip_set4 = ""
================================================
FILE: zsim/data/APLData/青衣-丽娜-雅.toml
================================================
[general]
title = "青衣-丽娜-雅默认手法"
comment = "这是开发组为青衣、丽娜、雅队伍提供的默认APL"
author = "虎皮"
create_time = "2025-05-23T04:16:54.482+08:00"
latest_change_time = "2025-07-02T23:08:55.042+08:00"
[characters]
required = [ "青衣", "雅", "丽娜",]
optional = []
[apl_logic]
logic = """
# status.1251:lasting_node_tag==1251_NA_3_NFC|status.1251:lasting_node_tick>=180|status.1251:on_field==False
#-------------------失衡期逻辑-------------------
#若在连续激发SNA_1的过程中怪物进入了失衡状态,则需要提前打出SNA_2
1251|action+=|1251_SNA_2|action.1251:strict_linked_after==1251_SNA_1|status.enemy:stun==True|attribute.1251:special_state→醉花月云转可用次数>=0
#连携技释放逻辑
1251|action+=|1251_QTE|status.enemy:QTE_triggerable_times==3|status.enemy:QTE_triggered_times==1|status.enemy:single_qte!=None|special.preload_data:operating_char!=1251
1211|action+=|1211_QTE|status.enemy:single_qte!=None|special.preload_data:operating_char!=1211
1091|action+=|1091_QTE_A|status.enemy:single_qte!=None|special.preload_data:operating_char!=1091
#失衡期间丽娜要满覆盖buff
1211|action+=|1211_NA_1|status.enemy:stun==True|!buff.1091:exist→Buff-角色-丽娜-核心被动-穿透率==True|status.enemy:QTE_activation_available==False
#满豆自动放满蓄力普攻
1091|action+=|1091_SNA_3|attribute.1091:special_resource==6|buff.1091:exist→Buff-角色-丽娜-核心被动-穿透率==True|status.enemy:stun==True
#能量不够时应优先大招
1091|action+=|1091_Q|attribute.1091:special_resource>3|attribute.1091:decibel==3000|status.enemy:stun==True|attribute.1091:energy<40
#豆子相差很远时,也优先开大
1091|action+=|1091_Q|attribute.1091:special_resource<4|attribute.1091:decibel==3000|status.enemy:stun==True
#有能量、有大时,根据豆子数量判断大招如何释放。
1091|action+=|1091_E_EX_A_1|status.enemy:stun==True|attribute.1091:special_resource<6|attribute.1091:special_resource>4|attribute.1091:decibel==3000
1091|action+=|1091_Q|status.enemy:stun==True|attribute.1091:special_resource<3|attribute.1091:decibel==3000
#泄能逻辑
1091|action+=|1091_E_EX_B_1|status.enemy:stun==True|attribute.1091:energy>=40|attribute.1091:special_resource<6|action.1091:strict_linked_after==1091_E_EX_A_2
1091|action+=|1091_E_EX_A_1|status.enemy:stun==True|attribute.1091:energy>=40|attribute.1091:special_resource<6
#剩余情况都是后置开大
1091|action+=|1091_Q|attribute.1091:special_resource<4|attribute.1091:decibel==3000|status.enemy:stun==True
#-------------------非失衡期逻辑-------------------
# 雅快支罗炯
1091|action+=|1091_BH_Aid|status.1091:quick_assist_available==True|status.enemy:stun==False
#后续SNA_1的释放逻辑:#青衣泄电压逻辑:
#SNA_2正常结束的逻辑
1251|action+=|1251_SNA_2|action.1251:strict_linked_after==1251_SNA_1|attribute.1251:special_state→醉花月云转可用次数==0
1251|action+=|1251_SNA_1|action.1251:strict_linked_after==1251_SNA_1|status.enemy:stun==False|attribute.1251:special_state→醉花月云转可用次数>0
#启动逻辑,主要是看何时打出第一个SNA_1
1251|action+=|1251_SNA_1|attribute.1251:special_resource>=75|status.enemy:stun==False|attribute.1251:special_state→醉花月云转可用次数==5
#大招逻辑:
1211|action+=|1211_Q|attribute.1211:decibel==3000|status.enemy:stun==False
1251|action+=|1251_Q|attribute.1251:decibel==3000|attribute.1251:special_resource<75|status.enemy:stun==False
#泄能逻辑:
1211|action+=|1211_E_EX|attribute.1211:energy>=60
1251|action+=|1251_E_EX_FC|attribute.1251:energy>=80|status.enemy:stun==False|attribute.1251:special_resource<75
1251|action+=|1251_E_EX_NFC|attribute.1251:energy>=60|status.enemy:stun==False|attribute.1251:special_resource<75
#青衣普攻逻辑:
#A3循环的逻辑:检测到Switch或者是衔接在自己后;前提是没有失衡,且电能没满;
1251|action+=|1251_NA_3_NFC|attribute.1251:special_resource<100|status.enemy:stun==False|action.1251:strict_linked_after==1251_NA_Switch
1251|action+=|1251_NA_3_NFC|attribute.1251:special_resource<100|status.enemy:stun==False|action.1251:strict_linked_after==1251_NA_3_NFC
1091|action+=|auto_NA|status.enemy:anomaly_pct_5<=0.7|status.enemy:stun==False
1251|action+=|auto_NA|status.enemy:stun==False
#雅的快速支援逻辑:
1091|action+=|1091_BH_Aid|action.1251:strict_linked_after==1211_E_EX|attribute.1091:special_resource==6
#丽娜的补buff逻辑:只争取覆盖雅的满蓄力普攻。有能量放E,每能量A1
1211|action+=|1211_E_EX|attribute.1091:special_resource==6|!buff.1211:exist→Buff-角色-丽娜-核心被动-穿透率==True|attribute.1211:energy>=60
1211|action+=|1211_NA_1|attribute.1091:special_resource==6|!buff.1211:exist→Buff-角色-丽娜-核心被动-穿透率==True
#满豆自动放满蓄力普攻
1091|action+=|1091_SNA_3|attribute.1091:special_resource==6|buff.1091:exist→Buff-角色-丽娜-核心被动-穿透率==True
#满喧响自动开大(雅的傻瓜大招逻辑)
1091|action+=|1091_Q|attribute.1091:special_resource<=3|attribute.1091:decibel==3000
#豆子小于6且能量足够时,在强化E第一段后自动衔接强化E第二段,
1091|action+=|1091_E_EX_B_1|action.1251:strict_linked_after==1091_E_EX_A_2|attribute.1091:energy>=40|attribute.1091:special_resource<6
#豆子小于6时,能量够就强化E(雅的傻瓜泄能逻辑)
1091|action+=|1091_E_EX_B_1|attribute.1091:energy>=40|attribute.1091:special_resource<6|action.1251:strict_linked_after==1091_E_EX_A_1
1091|action+=|1091_E_EX_A_1|attribute.1091:energy>=40|attribute.1091:special_resource<6
#底层逻辑:没事干时平A
1091|action+=|auto_NA
"""
[characters."青衣"]
cinema = []
weapon = ""
equip_set4 = ""
[characters."雅"]
cinema = []
weapon = ""
equip_set4 = ""
[characters."丽娜"]
cinema = []
weapon = ""
equip_set4 = ""
================================================
FILE: zsim/data/DefaultConfig/1221.json
================================================
{
"default": {
"1221_NA_1": "1221_NA_2",
"1221_NA_2": "1221_NA_3",
"1221_NA_3": "1221_NA_4",
"1221_NA_4": "1221_NA_5",
"1221_NA_5": "1221_NA_1",
"1221_E": "1221_NA_3",
"1221_E_A": "1221_NA_3",
"1221_E_EX_2": "1221_NA_3",
"1221_CA": "1221_NA_3",
"1221_BH_Aid": "1221_NA_3",
"1221_Assault_Aid": "1221_NA_3"
},
"normal_kagen": {
"1221_SNA_1": "1221_SNA_2",
"1221_SNA_2": "1221_SNA_3",
"1221_SNA_3": "1221_SNA_4",
"1221_SNA_4": "1221_SNA_5",
"1221_SNA_5": "1221_SNA_1",
"1221_E": "1221_SNA_3",
"1221_E_A": "1221_SNA_3",
"1221_E_EX_2": "1221_SNA_3",
"1221_CA": "1221_SNA_3",
"1221_BH_Aid": "1221_SNA_3",
"1221_Assault_Aid": "1221_SNA_3"
},
"shinra_kagen": {
"1221_SNA_1": "1221_SNA_2",
"1221_SNA_2": "1221_SNA_3",
"1221_SNA_3": "1221_SNA_4",
"1221_SNA_4": "1221_SNA_5",
"1221_SNA_5": "1221_SNA_3",
"1221_E": "1221_SNA_3",
"1221_E_A": "1221_SNA_3",
"1221_E_EX_2": "1221_SNA_3",
"1221_RA": "1221_SNA_3",
"1221_CA": "1221_SNA_3",
"1221_QTE": "1221_SNA_3",
"1221_Q": "1221_SNA_3",
"1221_BH_Aid": "1221_SNA_3",
"1221_Assault_Aid": "1221_SNA_3"
},
"shinra_jougen": {
"1221_NA_1": "1221_NA_2",
"1221_NA_2": "1221_NA_3",
"1221_NA_3": "1221_NA_4",
"1221_NA_4": "1221_NA_5",
"1221_NA_5": "1221_NA_3",
"1221_E": "1221_NA_3",
"1221_E_A": "1221_NA_3",
"1221_E_EX_2": "1221_NA_3",
"1221_RA": "1221_NA_3",
"1221_CA": "1221_NA_3",
"1221_QTE": "1221_NA_3",
"1221_Q": "1221_NA_3",
"1221_BH_Aid": "1221_NA_3",
"1221_Assault_Aid": "1221_NA_3"
}
}
================================================
FILE: zsim/data/DefaultConfig/1291.json
================================================
{
"default": {
"_comment": "这是默认的普攻逻辑,是最摆烂的策略。不进行蓄力,也不会在技能后面都进行衔接普攻的操作",
"1291_NA_1": "1291_NA_2",
"1291_NA_2": "1291_NA_3",
"1291_NA_3": "1291_SNA_1",
"1291_SNA_1": "1291_SNA_2_NFC"
},
"balanced_mode": {
"_comment": "这是正常的普攻逻辑,虽然不会打蓄力,但是会在技能后面都进行衔接普攻的操作",
"1291_NA_1": "1291_NA_2",
"1291_NA_2": "1291_NA_3",
"1291_NA_3": "1291_SNA_1",
"1291_RA": "1291_NA_3",
"1291_Q": "1291_SNA_1",
"1291_Assault_Aid": "1291_NA_1_ALT",
"1291_NA_1_ALT": "1291_NA_A",
"1291_BH_Aid": "1291_BH_Aid_A",
"1291_CA": "1291_SCA",
"1291_SNA_1": "1291_SNA_2_NFC",
"1291_E_EX_2": "1291_SNA_1"
},
"perfection_mode": {
"_comment": "这是打满普攻的逻辑,雨果现在会尝试在所有技能后面衔接特殊普攻,并且打满蓄力",
"1291_NA_1": "1291_NA_2",
"1291_NA_2": "1291_NA_3",
"1291_NA_3": "1291_SNA_1",
"1291_RA": "1291_NA_3",
"1291_Q": "1291_SNA_1",
"1291_Assault_Aid": "1291_NA_1_ALT",
"1291_NA_1_ALT": "1291_NA_A_FC",
"1291_BH_Aid": "1291_BH_Aid_A_FC",
"1291_CA": "1291_SCA_FC",
"1291_SNA_1": "1291_SNA_2_FC",
"1291_E_EX_2": "1291_SNA_1"
},
"only_full_charge_na": {
"_comment": "这是4画的额逻,只打普攻的满蓄力",
"1291_NA_1": "1291_NA_2",
"1291_NA_2": "1291_NA_3",
"1291_NA_3": "1291_SNA_1",
"1291_SNA_1": "1291_SNA_2_FC"
}
}
================================================
FILE: zsim/data/DefaultConfig/1331.json
================================================
{
"default": {
"1331_NA_1": "1331_NA_2",
"1331_NA_2": "1331_NA_3",
"1331_NA_3": "1331_NA_4"
}
}
================================================
FILE: zsim/data/DefaultConfig/1401.json
================================================
{"default": {
"1401_NA_1": "1401_NA_2",
"1401_NA_2": "1401_NA_3",
"1401_NA_3": "1401_NA_4",
"1401_NA_4": "1401_NA_5",
"1401_NA_5": "1401_NA_1",
"1401_E_EX_1": "1401_NA_5",
"1401_E_EX_2": "1401_NA_5",
"1401_Assault_Aid": "1401_NA_5",
"1401_QTE": "1401_NA_5",
"1401_BH_Aid": "1401_NA_5",
"1401_RA": "1401_NA_2",
"1401_SNA_3": "1401_NA_5"
},
"EnhancementState": {
"1401_NA_1": "1401_NA_2",
"1401_NA_2": "1401_NA_3",
"1401_NA_3": "1401_NA_4",
"1401_NA_4": "1401_NA_5",
"1401_NA_5": "1401_NA_1",
"1401_E_EX_1": "1401_NA_5",
"1401_E_EX_2": "1401_NA_5",
"1401_Assault_Aid": "1401_NA_5",
"1401_QTE": "1401_NA_5",
"1401_BH_Aid": "1401_NA_5",
"1401_RA": "1401_NA_2",
"1401_SNA_3": "1401_NA_5"
}
}
================================================
FILE: zsim/data/DefaultConfig/1461.json
================================================
{
"default": {
"1461_NA_1": "1461_NA_2",
"1461_NA_2": "1461_NA_3",
"1461_NA_3": "1461_NA_4",
"1461_NA_4": "1461_SNA_1",
"1461_RA": "1461_NA_2"
},
"steel_charge_enough": {
"1461_NA_1": "1461_NA_2",
"1461_NA_2": "1461_NA_3",
"1461_NA_3": "1461_NA_4",
"1461_NA_4": "1461_SNA_1",
"1461_SNA_1": "1461_SNA_2",
"1461_RA": "1461_NA_2",
"1461_Q": "1461_SNA_2"
}
}
================================================
FILE: zsim/data/DefaultConfig/NAOrder.json
================================================
{
"1091": {
"1091_NA_1": "1091_NA_2",
"1091_NA_2": "1091_NA_3",
"1091_NA_3": "1091_NA_4",
"1091_NA_4": "1091_NA_5",
"1091_NA_5": "1091_NA_2",
"1091_CA": "1091_NA_4",
"1091_E_EX_A_2": "1091_NA_3",
"1091_E_EX_B_2": "1091_NA_3",
"1091_Q": "1091_NA_3"
},
"1161": {
"1161_NA_1": "1161_NA_2",
"1161_NA_2": "1161_NA_3",
"1161_NA_3": "1161_NA_4",
"1161_NA_4": "1161_NA_5_SH",
"1161_NA_5_SH": "1161_NA_5_CoH",
"1161_NA_5_CoH": "1161_NA_5_EndH",
"1161_NA_5_EndH": "1161_NA_1",
"1091_E_EX_1": "1161_NA_3",
"1091_E_EX_2": "1161_NA_3"
},
"1141": {
"1141_SNA_1": "1141_SNA_2",
"1141_SNA_2": "1141_SNA_3",
"1141_SNA_3": "1141_SNA_4",
"1141_SNA_4": "1141_SNA_5_FC"
},
"1251": {
"1251_NA_1": "1251_NA_2",
"1251_NA_2": "1251_NA_Switch",
"1251_E_EX_NFC": "1251_NA_Switch",
"1251_E_EX_FC": "1251_NA_Switch",
"1251_CA": "1251_NA_Switch"
},
"1381": {
"1381_NA_1": "1381_NA_2",
"1381_NA_2": "1381_NA_3",
"1381_NA_3": "1381_NA_4",
"1381_NA_4": "1381_NA_5",
"1381_NA_5": "1381_NA_1",
"1381_E_EX": "1381_NA_3"
},
"1261": {
"1261_NA_1": "1261_NA_2",
"1261_NA_2": "1261_NA_3",
"1261_NA_3": "1261_NA_4",
"1261_NA_4": "1261_NA_5",
"1261_NA_5": "1261_NA_3"
},
"1331": {
"1331_NA_1": "1331_NA_2",
"1331_NA_2": "1331_NA_3",
"1331_NA_3": "1331_NA_4"
},
"1371": {
"1371_NA_1": "1371_NA_2",
"1371_NA_2": "1371_NA_3",
"1371_NA_3": "1371_NA_4",
"1371_NA_4": "1371_NA_5",
"1371_NA_5": "1371_RA",
"1371_SNA_A": "1371_NA_5",
"1371_RA": "1371_NA_2"
},
"1411": {
"1411_NA_1": "1411_NA_2",
"1411_NA_2": "1411_NA_3",
"1411_NA_3": "1411_NA_4",
"1411_NA_4": "1411_NA_5",
"1411_RA": "1411_NA_3",
"1411_CA": "1411_NA_3",
"1411_E_EX_A": "1411_NA_2",
"1411_E_EX_B": "1411_NA_2",
"1411_Assault_Aid": "1411_NA_2",
"1411_Assault_Aid_A": "1411_NA_2"
},
"1401": {
"1401_NA_1": "1401_NA_2",
"1401_NA_2": "1401_NA_3",
"1401_NA_3": "1401_NA_4",
"1401_NA_4": "1401_NA_5",
"1401_NA_5": "1401_NA_1",
"1401_E_EX_1": "1401_NA_5",
"1401_E_EX_2": "1401_NA_5",
"1401_Assault_Aid": "1401_NA_5",
"1401_QTE": "1401_NA_5",
"1401_BH_Aid": "1401_NA_5",
"1401_RA": "1401_NA_2",
"1401_SNA_3": "1401_NA_5"
}
}
================================================
FILE: zsim/data/__init__.py
================================================
================================================
FILE: zsim/data/apl_test.txt
================================================
# 扳机补充决意值逻辑:
# 连击逻辑:
1361|action+=|1361_SNA_1|attribute.1361:special_state→狙击姿态==True|attribute.1361:special_resource<100|status.enemy:stun_pct<=0.7
# 启动逻辑
1361|action+=|1361_SNA_0|attribute.1361:special_resource<5|status.enemy:stun==False
# QTE逻辑
1291|action+=|1291_QTE|status.enemy:single_qte!=None|special.preload_data:operating_char!=1291|status.enemy:QTE_triggered_times<1
1161|action+=|1161_E_EX_1|status.enemy:single_qte==None|status.enemy:stun==True|status.enemy:QTE_activation_available==True|attribute.1161:energy>=40|status.enemy:QTE_triggered_times<1
1161|action+=|1161_E|status.enemy:single_qte==None|status.enemy:stun==True|status.enemy:QTE_activation_available==True|attribute.1161:energy<40|status.enemy:QTE_triggered_times<1
# 莱特消耗士气的逻辑
1161|action.no_swap_cancel+=|1161_NA_5_EnEndH_EX|action.1161:strict_linked_after==1161_NA_5_CoH_EX
1161|action.no_swap_cancel+=|1161_NA_5_CoH_EX|action.1161:strict_linked_after==1161_NA_5_SH_EX
1161|action.no_swap_cancel+=|1161_NA_5_SH_EX|action.1161:strict_linked_after==1161_BH_Aid|status.enemy:stun==False
# 非失衡期泄能逻辑
1291|action+=|1291_E_EX_1|attribute.1291:energy>=85|status.enemy:stun==False
# 莱特EQ逻辑
1161|action+=|1161_Q|attribute.1161:decibel>=3000|buff.1161:count→Buff-角色-莱特-核心被动-冲击力提升>=75|status.enemy:stun_pct<0.5
1161|action+=|1161_E_EX_2|attribute.1161:energy>=80|buff.1161:count→Buff-角色-莱特-核心被动-冲击力提升>=75|action.1161:strict_linked_after==1161_E_EX_1|status.enemy:stun_pct<0.5
1161|action.no_swap_cancel+=|1161_E_EX_1|attribute.1161:energy>=100|buff.1161:count→Buff-角色-莱特-核心被动-冲击力提升>=75|status.enemy:stun_pct<0.5
# 扳机EQ逻辑
1361|action+=|1361_Q|attribute.1361:decibel>=3000|status.enemy:stun==False|status.enemy:stun_pct<0.7|status.1361:char_available==True
1361|action+=|1361_E_EX|attribute.1361:energy>=60|status.enemy:stun==False
# 莱特士气足够时,上场释放快支
1161|action.no_swap_cancel+=|1161_BH_Aid|attribute.1161:special_resource>=75|status.1161:on_field==False|status.enemy:stun==False
# 莱特站场
# 1161|action+=|auto_NA|status.enemy:stun==False
# 失衡期爆发逻辑
# 1291|action+=|auto_NA|buff.1291:exist→Buff-角色-雨果-4画-蓄力射击减冰抗==False|attribute.1291:cinema>=4
# 4画专用逻辑
# 1291|action+=|1291_Q|attribute.1291:decibel>=3000|status.enemy:stun==True|status.enemy:QTE_triggered_times>=1|status.1291:char_available==True|buff.1291:exist→Buff-角色-雨果-4画-蓄力射击减冰抗==True
# 1291|action+=|1291_E_EX_1|attribute.1291:energy>=40|status.enemy:stun==True|status.enemy:QTE_triggered_times>=1|buff.1291:exist→Buff-角色-雨果-4画-蓄力射击减冰抗==True
1291|action+=|1291_Q|attribute.1291:decibel>=3000|status.enemy:stun==True|status.enemy:QTE_triggered_times>=1|status.1291:char_available==True
1291|action+=|1291_E_EX_1|attribute.1291:energy>=40|status.enemy:stun==True|status.enemy:QTE_triggered_times>=1
# 雨果自动平A测试
1291|action+=|auto_NA
================================================
FILE: zsim/data/buff_effect.csv
================================================
名称,key1,value1,key2,value2,key3,value3,key4,value4
Buff-角色-艾莲-核心被动,固定暴击伤害,1,,,,,,
Buff-角色-艾莲-额外能力,冰属性伤害,0.03,,,,,,
Buff-武器-精1深海访客-冰伤,冰属性伤害,0.25,,,,,,
Buff-武器-精1深海访客-暴击率-1,固定暴击率,0.1,,,,,,
Buff-武器-精1深海访客-暴击率-2,固定暴击率,0.1,,,,,,
Buff-武器-精2深海访客-冰伤,冰属性伤害,0.315,,,,,,
Buff-武器-精2深海访客-暴击率-1,固定暴击率,0.125,,,,,,
Buff-武器-精2深海访客-暴击率-2,固定暴击率,0.125,,,,,,
Buff-武器-精3深海访客-冰伤,冰属性伤害,0.38,,,,,,
Buff-武器-精3深海访客-暴击率-1,固定暴击率,0.15,,,,,,
Buff-武器-精3深海访客-暴击率-2,固定暴击率,0.15,,,,,,
Buff-武器-精4深海访客-冰伤,冰属性伤害,0.445,,,,,,
Buff-武器-精4深海访客-暴击率-1,固定暴击率,0.175,,,,,,
Buff-武器-精4深海访客-暴击率-2,固定暴击率,0.175,,,,,,
Buff-武器-精5深海访客-冰伤,冰属性伤害,0.5,,,,,,
Buff-武器-精5深海访客-暴击率-1,固定暴击率,0.2,,,,,,
Buff-武器-精5深海访客-暴击率-2,固定暴击率,0.2,,,,,,
Buff-驱动盘-极地重金属-冲刺攻击增伤,冲刺攻击增伤,0.2,,,,,,
Buff-驱动盘-极地重金属-普攻增伤,普攻增伤,0.2,,,,,,
Buff-驱动盘-极地重金属-冲刺与普攻增伤-有条件,普攻增伤,0.2,冲刺攻击增伤,0.2,,,,
Buff-驱动盘-震星迪斯科,普攻失衡值增加,0.2,冲刺攻击失衡值增加,0.2,闪避反击失衡值增加,0.2,,
Buff-驱动盘-啄木鸟电音-普攻,局内攻击力%,0.09,,,,,,
Buff-驱动盘-啄木鸟电音-闪避反击,局内攻击力%,0.09,,,,,,
Buff-驱动盘-啄木鸟电音-强化特殊技,局内攻击力%,0.09,,,,,,
Buff-角色-莱特-核心被动-冲击力提升,局内冲击力%,0.002,,,,,,
Buff-角色-莱特-核心被动-冰火双抗,火伤害抗性降低,0.15,冰伤害抗性降低,0.15,,,,
Buff-角色-莱特-核心被动-失衡时间延长,失衡延长,180,,,,,,
Buff-角色-莱特-额外能力-冰火增伤,冰属性伤害,0.0025,火属性伤害,0.0025,,,,
Buff-角色-莱卡恩-核心被动-失衡值提升,普攻失衡值增加,0.8,火属性伤害,0.025,,,,
Buff-角色-莱卡恩-核心被动-减冰抗,冰伤害抗性降低,0.25,,,,,,
Buff-角色-莱卡恩-额外能力-失衡易伤倍率,失衡易伤增加,0.35,,,,,,
Buff-异常-霜寒,受暴击伤害增加,0.1,,,,,,
Buff-异常-畏缩,受失衡增加,0.075,,,,,,
Buff-角色-苍角-核心被动-1,固定攻击力,1,,,,,,
Buff-角色-苍角-核心被动-2,固定攻击力,1,,,,,,
Buff-武器-精1含羞恶面-冰伤,冰属性伤害,0.15,,,,,,
Buff-武器-精2含羞恶面-冰伤,冰属性伤害,0.175,,,,,,
Buff-武器-精3含羞恶面-冰伤,冰属性伤害,0.2,,,,,,
Buff-武器-精4含羞恶面-冰伤,冰属性伤害,0.225,,,,,,
Buff-武器-精5含羞恶面-冰伤,冰属性伤害,0.25,,,,,,
Buff-武器-精1含羞恶面-叠层攻击力,局内攻击力%,0.02,,,,,,
Buff-武器-精2含羞恶面-叠层攻击力,局内攻击力%,0.023,,,,,,
Buff-武器-精3含羞恶面-叠层攻击力,局内攻击力%,0.026,,,,,,
Buff-武器-精4含羞恶面-叠层攻击力,局内攻击力%,0.029,,,,,,
Buff-武器-精5含羞恶面-叠层攻击力,局内攻击力%,0.032,,,,,,
Buff-武器-精1燃狱齿轮-后台能量自动回复,能量自动恢复,0.6,,,,,,
Buff-武器-精2燃狱齿轮-后台能量自动回复,能量自动恢复,0.75,,,,,,
Buff-武器-精3燃狱齿轮-后台能量自动回复,能量自动恢复,0.9,,,,,,
Buff-武器-精4燃狱齿轮-后台能量自动回复,能量自动恢复,1.05,,,,,,
Buff-武器-精5燃狱齿轮-后台能量自动回复,能量自动恢复,1.2,,,,,,
Buff-武器-精1燃狱齿轮-叠层冲击力,局内冲击力%,0.1,,,,,,
Buff-武器-精2燃狱齿轮-叠层冲击力,局内冲击力%,0.125,,,,,,
Buff-武器-精3燃狱齿轮-叠层冲击力,局内冲击力%,0.15,,,,,,
Buff-武器-精4燃狱齿轮-叠层冲击力,局内冲击力%,0.175,,,,,,
Buff-武器-精5燃狱齿轮-叠层冲击力,局内冲击力%,0.2,,,,,,
Buff-武器-精1拘缚者,普攻失衡值增加,0.06,普攻增伤,0.06,,,,
Buff-武器-精2拘缚者,普攻失衡值增加,0.075,普攻增伤,0.075,,,,
Buff-武器-精3拘缚者,普攻失衡值增加,0.09,普攻增伤,0.09,,,,
Buff-武器-精4拘缚者,普攻失衡值增加,0.105,普攻增伤,0.105,,,,
Buff-武器-精5拘缚者,普攻失衡值增加,0.12,普攻增伤,0.12,,,,
Buff-武器-精1焰心桂冠-冲击力提升,局内冲击力%,0.25,,,,,,
Buff-武器-精2焰心桂冠-冲击力提升,局内冲击力%,0.2875,,,,,,
Buff-武器-精3焰心桂冠-冲击力提升,局内冲击力%,0.325,,,,,,
Buff-武器-精4焰心桂冠-冲击力提升,局内冲击力%,0.3625,,,,,,
Buff-武器-精5焰心桂冠-冲击力提升,局内冲击力%,0.4,,,,,,
Buff-武器-精1焰心桂冠-受暴伤提升,受暴击伤害增加,0.015,,,,,,
Buff-武器-精2焰心桂冠-受暴伤提升,受暴击伤害增加,0.0172,,,,,,
Buff-武器-精3焰心桂冠-受暴伤提升,受暴击伤害增加,0.0195,,,,,,
Buff-武器-精4焰心桂冠-受暴伤提升,受暴击伤害增加,0.0217,,,,,,
Buff-武器-精5焰心桂冠-受暴伤提升,受暴击伤害增加,0.024,,,,,,
Buff-武器-精1玉壶青冰-普攻加冲击,局内冲击力%,0.007,,,,,,
Buff-武器-精2玉壶青冰-普攻加冲击,局内冲击力%,0.0088,,,,,,
Buff-武器-精3玉壶青冰-普攻加冲击,局内冲击力%,0.0105,,,,,,
Buff-武器-精4玉壶青冰-普攻加冲击,局内冲击力%,0.0122,,,,,,
Buff-武器-精5玉壶青冰-普攻加冲击,局内冲击力%,0.014,,,,,,
Buff-武器-精1玉壶青冰-15层后增伤,全增伤,0.2,,,,,,
Buff-武器-精2玉壶青冰-15层后增伤,全增伤,0.23,,,,,,
Buff-武器-精3玉壶青冰-15层后增伤,全增伤,0.26,,,,,,
Buff-武器-精4玉壶青冰-15层后增伤,全增伤,0.29,,,,,,
Buff-武器-精5玉壶青冰-15层后增伤,全增伤,0.32,,,,,,
Buff-武器-精1贵重骨核-75%以上,失衡增幅,0.1,,,,,,
Buff-武器-精2贵重骨核-75%以上,失衡增幅,0.115,,,,,,
Buff-武器-精3贵重骨核-75%以上,失衡增幅,0.13,,,,,,
Buff-武器-精4贵重骨核-75%以上,失衡增幅,0.145,,,,,,
Buff-武器-精5贵重骨核-75%以上,失衡增幅,0.16,,,,,,
Buff-武器-精1贵重骨核-50%以上,失衡增幅,0.1,,,,,,
Buff-武器-精2贵重骨核-50%以上,失衡增幅,0.115,,,,,,
Buff-武器-精3贵重骨核-50%以上,失衡增幅,0.13,,,,,,
Buff-武器-精4贵重骨核-50%以上,失衡增幅,0.145,,,,,,
Buff-武器-精5贵重骨核-50%以上,失衡增幅,0.16,,,,,,
Buff-武器-精1人为刀俎,局内冲击力%,0.02,,,,,,
Buff-武器-精2人为刀俎,局内冲击力%,0.023,,,,,,
Buff-武器-精3人为刀俎,局内冲击力%,0.026,,,,,,
Buff-武器-精4人为刀俎,局内冲击力%,0.029,,,,,,
Buff-武器-精5人为刀俎,局内冲击力%,0.032,,,,,,
Buff-武器-精1德玛拉电池II型-电伤,电属性伤害,0.15,,,,,,
Buff-武器-精2德玛拉电池II型-电伤,电属性伤害,0.175,,,,,,
Buff-武器-精3德玛拉电池II型-电伤,电属性伤害,0.2,,,,,,
Buff-武器-精4德玛拉电池II型-电伤,电属性伤害,0.22,,,,,,
Buff-武器-精5德玛拉电池II型-电伤,电属性伤害,0.24,,,,,,
Buff-武器-精1德玛拉电池II型-能量获得效率,能量获得效率,0.18,,,,,,
Buff-武器-精2德玛拉电池II型-能量获得效率,能量获得效率,0.205,,,,,,
Buff-武器-精3德玛拉电池II型-能量获得效率,能量获得效率,0.23,,,,,,
Buff-武器-精4德玛拉电池II型-能量获得效率,能量获得效率,0.255,,,,,,
Buff-武器-精5德玛拉电池II型-能量获得效率,能量获得效率,0.28,,,,,,
Buff-武器-精1硫磺石,局内攻击力%,0.035,,,,,,
Buff-武器-精2硫磺石,局内攻击力%,0.044,,,,,,
Buff-武器-精3硫磺石,局内攻击力%,0.052,,,,,,
Buff-武器-精4硫磺石,局内攻击力%,0.06,,,,,,
Buff-武器-精5硫磺石,局内攻击力%,0.07,,,,,,
Buff-角色-苍角-额外能力,冰属性伤害,0.25,,,,,,
Buff-角色-青衣-核心被动-失衡易伤,失衡易伤增加,0.04,连携技增伤,0.03,,,,
Buff-角色-青衣-额外能力-失衡效率,普攻失衡值增加,0.2,,,,,,
Buff-角色-青衣-额外能力-冲击转攻击,固定攻击力,1,,,,,,
Buff-角色-11号-核心被动,普攻增伤,0.7,,,,,,
Buff-角色-11号-组队被动-常驻,火属性伤害,0.1,,,,,,
Buff-角色-11号-组队被动-失衡,火属性伤害,0.225,,,,,,
Buff-角色-雅-终结技-冰伤,冰属性伤害,0.3,,,,,,
Buff-角色-雅-核心被动-冰焰,烈霜积蓄效率增加,0.01,,,,,,
Buff-角色-雅-核心被动-霜灼,全积蓄效率增加,0.2,,,,,,
Buff-角色-雅-组队被动-普攻增伤,普攻增伤,0.6,,,,,,
Buff-角色-雅-组队被动-无视冰抗,冰抗性穿透,0.3,,,,,,
Buff-角色-露西-特殊技-攻击力,,,,,,,,
Buff-角色-露西-长按特殊技-攻击力,,,,,,,,
Buff-角色-露西-连携技-攻击力,,,,,,,,
Buff-角色-露西-终结技-攻击力,,,,,,,,
Buff-角色-派派-组队被动-积蓄效率,,,,,,,,
Buff-角色-派派-组队被动-全队增伤,,,,,,,,
Buff-角色-柏妮思-组队被动-延长灼烧,灼烧时间延长,180,,,,,,
Buff-角色-丽娜-核心被动-穿透率,穿透率,0.01,,,,,,
Buff-角色-丽娜-组队被动-增伤,电属性伤害,0.1,,,,,,
Buff-角色-丽娜-组队被动-延长感电,感电时间延长,180,,,,,,
Buff-音擎-精1霰落星殿-暴伤,固定暴击伤害,0.5,,,,,,
Buff-音擎-精2霰落星殿-暴伤,固定暴击伤害,0.57,,,,,,
Buff-音擎-精3霰落星殿-暴伤,固定暴击伤害,0.65,,,,,,
Buff-音擎-精4霰落星殿-暴伤,固定暴击伤害,0.72,,,,,,
Buff-音擎-精5霰落星殿-暴伤,固定暴击伤害,0.8,,,,,,
Buff-音擎-精1霰落星殿-叠层冰伤,冰属性伤害,0.2,,,,,,
Buff-音擎-精2霰落星殿-叠层冰伤,冰属性伤害,0.23,,,,,,
Buff-音擎-精3霰落星殿-叠层冰伤,冰属性伤害,0.26,,,,,,
Buff-音擎-精4霰落星殿-叠层冰伤,冰属性伤害,0.29,,,,,,
Buff-音擎-精5霰落星殿-叠层冰伤,冰属性伤害,0.32,,,,,,
Buff-驱动盘-折枝剑歌-暴伤,固定暴击伤害,0.3,,,,,,
Buff-驱动盘-折枝剑歌-暴击率,固定暴击率,0.12,,,,,,
Buff-异常-烈霜霜寒,受暴击伤害增加,0.1,,,,,,
Buff-角色-青衣-核心被动-额外电压补偿,普攻失衡值增加,0.01,普攻增伤,0.005,,,,
Buff-驱动盘-自由蓝调-物理,物理异常抗性降低,0.2,,,,,,
Buff-驱动盘-自由蓝调-火,火异常抗性降低,0.2,,,,,,
Buff-驱动盘-自由蓝调-冰,冰伤害抗性降低,0.2,,,,,,
Buff-驱动盘-自由蓝调-电,电伤害抗性降低,0.2,,,,,,
Buff-驱动盘-自由蓝调-以太,以太伤害抗性降低,0.2,,,,,,
Buff-驱动盘-自由蓝调-烈霜,冰异常抗性降低,0.2,,,,,,
Buff-驱动盘-河豚电音-终结技伤害提升,终结技增伤,0.2,,,,,,
Buff-驱动盘-河豚电音-攻击力提升,局内攻击力%,0.15,,,,,,
Buff-驱动盘-静听嘉音-嘉音,,,,,,,,
Buff-驱动盘-静听嘉音-增伤,全增伤,0.08,,,,,,
Buff-驱动盘-摇摆爵士-全队增伤,全增伤,0.15,,,,,,
Buff-驱动盘-激素朋克-全局攻击力,局内攻击力%,0.25,,,,,,
Buff-驱动盘-混沌爵士-火电伤,火属性伤害,0.15,电属性伤害,0.15,,,,
Buff-驱动盘-混沌爵士-前台增伤,强化特殊技增伤,0.2,支援突击增伤,0.2,,,,
Buff-驱动盘-原始朋克-全队增伤,全增伤,0.15,,,,,,
Buff-驱动盘-獠牙重金属-增伤,全增伤,0.35,,,,,,
Buff-武器-精1啜泣摇篮-后台回能,能量自动恢复,0.6,,,,,,
Buff-武器-精1啜泣摇篮-全队增伤,全增伤,0.1,,,,,,
Buff-武器-精1啜泣摇篮-全队增伤自增长,全增伤,0.017,,,,,,
Buff-武器-精2啜泣摇篮-后台回能,能量自动恢复,0.75,,,,,,
Buff-武器-精2啜泣摇篮-全队增伤,全增伤,0.125,,,,,,
Buff-武器-精2啜泣摇篮-全队增伤自增长,全增伤,0.02,,,,,,
Buff-武器-精3啜泣摇篮-后台回能,能量自动恢复,0.9,,,,,,
Buff-武器-精3啜泣摇篮-全队增伤,全增伤,0.15,,,,,,
Buff-武器-精3啜泣摇篮-全队增伤自增长,全增伤,0.025,,,,,,
Buff-武器-精4啜泣摇篮-后台回能,能量自动恢复,1.05,,,,,,
Buff-武器-精4啜泣摇篮-全队增伤,全增伤,0.175,,,,,,
Buff-武器-精4啜泣摇篮-全队增伤自增长,全增伤,0.03,,,,,,
Buff-武器-精5啜泣摇篮-后台回能,能量自动恢复,1.2,,,,,,
Buff-武器-精5啜泣摇篮-全队增伤,全增伤,0.2,,,,,,
Buff-武器-精5啜泣摇篮-全队增伤自增长,全增伤,0.033,,,,,,
Buff-武器-精1时光切片-回能回喧响,,,,,,,,
Buff-武器-精2时光切片-回能回喧响,,,,,,,,
Buff-武器-精3时光切片-回能回喧响,,,,,,,,
Buff-武器-精4时光切片-回能回喧响,,,,,,,,
Buff-武器-精5时光切片-回能回喧响,,,,,,,,
Buff-武器-精1聚宝箱-回能,局内能量自动恢复,0.5,,,,,,
Buff-武器-精1聚宝箱-全队增伤,全增伤,0.15,,,,,,
Buff-武器-精2聚宝箱-回能,局内能量自动恢复,0.58,,,,,,
Buff-武器-精2聚宝箱-全队增伤,全增伤,0.175,,,,,,
Buff-武器-精3聚宝箱-回能,局内能量自动恢复,0.65,,,,,,
Buff-武器-精3聚宝箱-全队增伤,全增伤,0.2,,,,,,
Buff-武器-精4聚宝箱-回能,局内能量自动恢复,0.72,,,,,,
Buff-武器-精4聚宝箱-全队增伤,全增伤,0.22,,,,,,
Buff-武器-精5聚宝箱-回能,局内能量自动恢复,0.8,,,,,,
Buff-武器-精5聚宝箱-全队增伤,全增伤,0.24,,,,,,
Buff-武器-精1好斗的阿炮-全局攻击力,局内攻击力%,0.025,,,,,,
Buff-武器-精2好斗的阿炮-全局攻击力,局内攻击力%,0.028,,,,,,
Buff-武器-精3好斗的阿炮-全局攻击力,局内攻击力%,0.032,,,,,,
Buff-武器-精4好斗的阿炮-全局攻击力,局内攻击力%,0.036,,,,,,
Buff-武器-精5好斗的阿炮-全局攻击力,局内攻击力%,0.04,,,,,,
Buff-武器-精1逍遥游球-全队暴击率,,,,,,,,
Buff-武器-精2逍遥游球-全队暴击率,,,,,,,,
Buff-武器-精3逍遥游球-全队暴击率,,,,,,,,
Buff-武器-精4逍遥游球-全队暴击率,,,,,,,,
Buff-武器-精5逍遥游球-全队暴击率,,,,,,,,
Buff-武器-精1残响Ⅰ型-全队冲击力,局内冲击力%,0.08,,,,,,
Buff-武器-精2残响Ⅰ型-全队冲击力,局内冲击力%,0.09,,,,,,
Buff-武器-精3残响Ⅰ型-全队冲击力,局内冲击力%,0.1,,,,,,
Buff-武器-精4残响Ⅰ型-全队冲击力,局内冲击力%,0.11,,,,,,
Buff-武器-精5残响Ⅰ型-全队冲击力,局内冲击力%,0.12,,,,,,
Buff-武器-精1残响II型-全队掌控精通,局内异常掌控,10,局内异常精通,10,,,,
Buff-武器-精2残响II型-全队掌控精通,局内异常掌控,11.5,局内异常精通,11.5,,,,
Buff-武器-精3残响II型-全队掌控精通,局内异常掌控,13,局内异常精通,13,,,,
Buff-武器-精4残响II型-全队掌控精通,局内异常掌控,14.5,局内异常精通,14.5,,,,
Buff-武器-精5残响II型-全队掌控精通,局内异常掌控,16,局内异常精通,16,,,,
Buff-武器-精1残响III型-全队攻击力,局内攻击力%,0.08,,,,,,
Buff-武器-精2残响III型-全队攻击力,局内攻击力%,0.09,,,,,,
Buff-武器-精3残响III型-全队攻击力,局内攻击力%,0.1,,,,,,
Buff-武器-精4残响III型-全队攻击力,局内攻击力%,0.11,,,,,,
Buff-武器-精5残响III型-全队攻击力,局内攻击力%,0.12,,,,,,
Buff-角色-妮可-核心被动-减防,百分比减防,0.4,,,,,,
Buff-角色-妮可-组队被动以太-增伤,以太属性伤害,0.25,,,,,,
Buff-角色-凯撒-大招-命中护盾增加失衡值,终结技失衡值增加,1,,,,,,
Buff-角色-凯撒-核心被动-攻击力,固定攻击力,1000,,,,,,
Buff-角色-凯撒-组队被动-增伤,全增伤,0.25,,,,,,
Buff-角色-耀佳音-咏叹华彩,全增伤,0.2,固定暴击伤害,0.25,,,,
Buff-角色-耀佳音-核心被动-攻击力,固定攻击力,1,,,,,,
Buff-角色-耀佳音-组队被动-触发器,,,,,,,,
Buff-角色-耀佳音-快支管理器-触发器,,,,,,,,
Buff-角色-耀佳音-震音管理器-触发器,,,,,,,,
Buff-角色-耀佳音-1画-减防,,,,,,,,
Buff-角色-耀佳音-1画-无敌效果,,,,,,,,
Buff-角色-耀佳音-2画-额外攻击力,,,,,,,,
Buff-角色-耀佳音-4画-强攻特效触发器,,,,,,,,
Buff-角色-耀佳音-4画-异常特效,,,,,,,,
Buff-角色-耀佳音-4画-击破特效,,,,,,,,
Buff-角色-耀佳音-6画-震音音簇暴击率提升,,,,,,,,
Buff-角色-耀佳音-6画-重击暴击率提升,,,,,,,,
Buff-角色-柏妮思-核心被动-燃油特调触发器,,,,,,,,
Buff-角色-柏妮思-核心被动-余烬增伤,火积蓄效率增加,0.65,,,,,,
Buff-角色-柏妮思-影画2-热意洞穿,穿透率,0.04,,,,,,
Buff-角色-柏妮思-影画4-招式暴击率,固定暴击率,0.3,,,,,,
Buff-武器-精1灼心摇壶-回能,局内能量自动恢复,0.6,,,,,,
Buff-武器-精1灼心摇壶-增伤,全增伤,0.035,,,,,,
Buff-武器-精1灼心摇壶-精通,固定异常精通,50,,,,,,
Buff-武器-精2灼心摇壶-回能,局内能量自动恢复,0.75,,,,,,
Buff-武器-精2灼心摇壶-增伤,全增伤,0.044,,,,,,
Buff-武器-精2灼心摇壶-精通,固定异常精通,62,,,,,,
Buff-武器-精3灼心摇壶-回能,局内能量自动恢复,0.9,,,,,,
Buff-武器-精3灼心摇壶-增伤,全增伤,0.052,,,,,,
Buff-武器-精3灼心摇壶-精通,固定异常精通,75,,,,,,
Buff-武器-精4灼心摇壶-回能,局内能量自动恢复,1.05,,,,,,
Buff-武器-精4灼心摇壶-增伤,全增伤,0.061,,,,,,
Buff-武器-精4灼心摇壶-精通,固定异常精通,87,,,,,,
Buff-武器-精5灼心摇壶-回能,局内能量自动恢复,1.2,,,,,,
Buff-武器-精5灼心摇壶-增伤,全增伤,0.07,,,,,,
Buff-武器-精5灼心摇壶-精通,固定异常精通,100,,,,,,
Buff-角色-格莉丝-核心被动-电能,,,,,,,,
Buff-角色-格莉丝-组队被动-感电伤害,,,,,,,,
Buff-角色-格莉丝-影画2-双抗降低,,,,,,,,
Buff-武器-精1嵌合编译器-攻击力,局内攻击力%,0.12,,,,,,
Buff-武器-精1嵌合编译器-精通,固定异常精通,25,,,,,,
Buff-武器-精2嵌合编译器-攻击力,局内攻击力%,0.15,,,,,,
Buff-武器-精2嵌合编译器-精通,固定异常精通,31,,,,,,
Buff-武器-精3嵌合编译器-攻击力,局内攻击力%,0.18,,,,,,
Buff-武器-精3嵌合编译器-精通,固定异常精通,37,,,,,,
Buff-武器-精4嵌合编译器-攻击力,局内攻击力%,0.21,,,,,,
Buff-武器-精4嵌合编译器-精通,固定异常精通,43,,,,,,
Buff-武器-精5嵌合编译器-攻击力,局内攻击力%,0.24,,,,,,
Buff-武器-精5嵌合编译器-精通,固定异常精通,50,,,,,,
Buff-武器-精1防暴者Ⅵ型-暴击率,固定暴击率,0.15,,,,,,
Buff-武器-精2防暴者Ⅵ型-暴击率,固定暴击率,0.188,,,,,,
Buff-武器-精3防暴者Ⅵ型-暴击率,固定暴击率,0.226,,,,,,
Buff-武器-精4防暴者Ⅵ型-暴击率,固定暴击率,0.264,,,,,,
Buff-武器-精5防暴者Ⅵ型-暴击率,固定暴击率,0.3,,,,,,
Buff-武器-精1防暴者Ⅵ型-普攻增伤,普攻增伤,0.35,,,,,,
Buff-武器-精2防暴者Ⅵ型-普攻增伤,普攻增伤,0.435,,,,,,
Buff-武器-精3防暴者Ⅵ型-普攻增伤,普攻增伤,0.52,,,,,,
Buff-武器-精4防暴者Ⅵ型-普攻增伤,普攻增伤,0.605,,,,,,
Buff-武器-精5防暴者Ⅵ型-普攻增伤,普攻增伤,0.7,,,,,,
Buff-角色-朱鸢-核心被动-强化普攻增伤,普攻增伤,0.4,,,,,,
Buff-角色-朱鸢-核心被动-失衡普攻增伤,普攻增伤,0.4,,,,,,
Buff-角色-朱鸢-额外能力-暴击率,局内暴击率,0.3,,,,,,
Buff-角色-朱鸢-2画-强化普攻增伤,普攻增伤,0.1,,,,,,
Buff-角色-朱鸢-4画-无视以太抗,以太伤害抗性降低,0.25,,,,,,
Buff-角色-朱鸢-6画-降低能耗,,,,,,,,
Buff-角色-格丽斯-4画-能量获取效率,,,,,,,,
Buff-角色-格丽斯-6画-特殊技增伤,,,,,,,,
Buff-角色-伊芙琳-核心被动-暴击率提升,局内暴击率,0.25,,,,,,
Buff-角色-伊芙琳-组队被动-连携技大招增伤,连携技增伤,0.3,终结技增伤,0.3,,,,
Buff-角色-伊芙琳-组队被动-连携技大招倍率增加,,,,,,,,
Buff-角色-伊芙琳-1画-无视防御力,百分比减防,0.12,,,,,,
Buff-角色-伊芙琳-1画-无视防御力扩散,百分比减防,0.12,,,,,,
Buff-角色-伊芙琳-1画-禁锢触发器,,,,,,,,
Buff-角色-伊芙琳-2画-局内大攻击,局内攻击力%,0.15,,,,,,
Buff-角色-伊芙琳-2画-返还撩火触发器,,,,,,,,
Buff-角色-伊芙琳-2画-连携技打断等级提升,,,,,,,,
Buff-角色-伊芙琳-4画-护盾给暴伤,局内暴击伤害,0.4,,,,,,
Buff-角色-伊芙琳-4画-护盾,,,,,,,,
Buff-角色-伊芙琳-6画-额外追击触发器,,,,,,,,
Buff-武器-精1心弦夜响-暴伤,固定暴击伤害,0.5,,,,,,
Buff-武器-精2心弦夜响-暴伤,固定暴击伤害,0.575,,,,,,
Buff-武器-精3心弦夜响-暴伤,固定暴击伤害,0.65,,,,,,
Buff-武器-精4心弦夜响-暴伤,固定暴击伤害,0.725,,,,,,
Buff-武器-精5心弦夜响-暴伤,固定暴击伤害,0.8,,,,,,
Buff-武器-精1心弦夜响-无视火抗,火抗性穿透,0.125,,,,,,
Buff-武器-精2心弦夜响-无视火抗,火抗性穿透,0.145,,,,,,
Buff-武器-精3心弦夜响-无视火抗,火抗性穿透,0.165,,,,,,
Buff-武器-精4心弦夜响-无视火抗,火抗性穿透,0.185,,,,,,
Buff-武器-精5心弦夜响-无视火抗,火抗性穿透,0.2,,,,,,
Buff-武器-精1心弦夜响-心弦触发器,,,,,,,,
Buff-武器-精2心弦夜响-心弦触发器,,,,,,,,
Buff-武器-精3心弦夜响-心弦触发器,,,,,,,,
Buff-武器-精4心弦夜响-心弦触发器,,,,,,,,
Buff-武器-精5心弦夜响-心弦触发器,,,,,,,,
Buff-角色-悠真-核心被动-特殊冲刺攻击暴击率,局内暴击率,0.25,,,,,,
Buff-角色-悠真-核心被动-特殊冲刺攻击暴伤,局内暴击伤害,0.12,,,,,,
Buff-角色-悠真-组队被动,全增伤,0.4,,,,,,
Buff-角色-悠真-2画-特殊冲刺攻击增伤,冲刺攻击增伤,0.5,,,,,,
Buff-角色-悠真-6画-无视电抗,电抗性穿透,0.15,,,,,,
Buff-武器-精1残心青囊-暴击率,固定暴击率,0.1,,,,,,
Buff-武器-精2残心青囊-暴击率,固定暴击率,0.115,,,,,,
Buff-武器-精3残心青囊-暴击率,固定暴击率,0.13,,,,,,
Buff-武器-精4残心青囊-暴击率,固定暴击率,0.145,,,,,,
Buff-武器-精5残心青囊-暴击率,固定暴击率,0.16,,,,,,
Buff-武器-精1残心青囊-电属性伤害,电属性伤害,0.4,,,,,,
Buff-武器-精2残心青囊-电属性伤害,电属性伤害,0.46,,,,,,
Buff-武器-精3残心青囊-电属性伤害,电属性伤害,0.52,,,,,,
Buff-武器-精4残心青囊-电属性伤害,电属性伤害,0.58,,,,,,
Buff-武器-精5残心青囊-电属性伤害,电属性伤害,0.64,,,,,,
Buff-武器-精1残心青囊-条件暴击率,固定暴击率,0.1,,,,,,
Buff-武器-精2残心青囊-条件暴击率,固定暴击率,0.115,,,,,,
Buff-武器-精3残心青囊-条件暴击率,固定暴击率,0.13,,,,,,
Buff-武器-精4残心青囊-条件暴击率,固定暴击率,0.145,,,,,,
Buff-武器-精5残心青囊-条件暴击率,固定暴击率,0.16,,,,,,
Buff-角色-艾莲-1画-暴击率,局内暴击率,0.02,,,,,,
Buff-角色-艾莲-2画-强化特殊技额外爆伤,局内暴击伤害,0.2,,,,,,
Buff-角色-艾莲-4画-能量回复,,,,,,,,
Buff-角色-艾莲-6画-穿透率,穿透率,0.2,,,,,,
Buff-角色-艾莲-6画-冲刺蓄力剪增伤,冲刺攻击增伤,2.5,,,,,,
Buff-角色-11号-1画-回能,,,,,,,,
Buff-角色-11号-2画-火力镇压增伤,普攻增伤,0.03,冲刺攻击增伤,0.03,闪避反击增伤,0.03,,
Buff-角色-11号-6画-火力镇压无视火抗,,,,,,,,
Buff-角色-艾莲-快蓄触发器,,,,,,,,
Buff-角色-柏妮思-组队被动-火积蓄加成,,,,,,,,
Buff-角色-柏妮思-1画-余烬倍率提升,,,,,,,,
Buff-角色-柏妮思-1画-余烬火积蓄加成,,,,,,,,
Buff-角色-柏妮思-4画-双喷延时触发器,,,,,,,,
Buff-武器-精1家政员-后场时能量回复,局内能量自动恢复,0.45,,,,,,
Buff-武器-精2家政员-后场时能量回复,局内能量自动恢复,0.52,,,,,,
Buff-武器-精3家政员-后场时能量回复,局内能量自动恢复,0.58,,,,,,
Buff-武器-精4家政员-后场时能量回复,局内能量自动恢复,0.65,,,,,,
Buff-武器-精5家政员-后场时能量回复,局内能量自动恢复,0.72,,,,,,
Buff-武器-精1家政员-物理增伤,物理属性伤害,0.03,,,,,,
Buff-武器-精2家政员-物理增伤,物理属性伤害,0.035,,,,,,
Buff-武器-精3家政员-物理增伤,物理属性伤害,0.04,,,,,,
Buff-武器-精4家政员-物理增伤,物理属性伤害,0.044,,,,,,
Buff-武器-精5家政员-物理增伤,物理属性伤害,0.048,,,,,,
Buff-武器-精1旋钻机-赤轴-电属性增伤,普攻增伤,0.5,冲刺攻击增伤,0.5,,,,
Buff-武器-精2旋钻机-赤轴-电属性增伤,普攻增伤,0.575,冲刺攻击增伤,0.575,,,,
Buff-武器-精3旋钻机-赤轴-电属性增伤,普攻增伤,0.65,冲刺攻击增伤,0.65,,,,
Buff-武器-精4旋钻机-赤轴-电属性增伤,普攻增伤,0.725,冲刺攻击增伤,0.725,,,,
Buff-武器-精5旋钻机-赤轴-电属性增伤,普攻增伤,0.8,冲刺攻击增伤,0.8,,,,
Buff-武器-精1星徽引擎-攻击力提升,局内攻击力%,0.12,,,,,,
Buff-武器-精2星徽引擎-攻击力提升,局内攻击力%,0.138,,,,,,
Buff-武器-精3星徽引擎-攻击力提升,局内攻击力%,0.156,,,,,,
Buff-武器-精4星徽引擎-攻击力提升,局内攻击力%,0.174,,,,,,
Buff-武器-精5星徽引擎-攻击力提升,局内攻击力%,0.192,,,,,,
Buff-武器-精1星鎏金花信-攻击力提升,局内攻击力%,0.06,,,,,,
Buff-武器-精2星鎏金花信-攻击力提升,局内攻击力%,0.069,,,,,,
Buff-武器-精3星鎏金花信-攻击力提升,局内攻击力%,0.078,,,,,,
Buff-武器-精4星鎏金花信-攻击力提升,局内攻击力%,0.087,,,,,,
Buff-武器-精5星鎏金花信-攻击力提升,局内攻击力%,0.096,,,,,,
Buff-武器-精1星鎏金花信-强化特殊技增伤,强化特殊技增伤,0.15,,,,,,
Buff-武器-精2星鎏金花信-强化特殊技增伤,强化特殊技增伤,0.172,,,,,,
Buff-武器-精3星鎏金花信-强化特殊技增伤,强化特殊技增伤,0.195,,,,,,
Buff-武器-精4星鎏金花信-强化特殊技增伤,强化特殊技增伤,0.218,,,,,,
Buff-武器-精5星鎏金花信-强化特殊技增伤,强化特殊技增伤,0.24,,,,,,
Buff-武器-精1星强音热望-攻击力提升,局内攻击力%,0.06,,,,,,
Buff-武器-精2星强音热望-攻击力提升,局内攻击力%,0.069,,,,,,
Buff-武器-精3星强音热望-攻击力提升,局内攻击力%,0.078,,,,,,
Buff-武器-精4星强音热望-攻击力提升,局内攻击力%,0.087,,,,,,
Buff-武器-精5星强音热望-攻击力提升,局内攻击力%,0.096,,,,,,
Buff-武器-精1星强音热望-额外攻击力提升,局内攻击力%,0.06,,,,,,
Buff-武器-精2星强音热望-额外攻击力提升,局内攻击力%,0.069,,,,,,
Buff-武器-精3星强音热望-额外攻击力提升,局内攻击力%,0.078,,,,,,
Buff-武器-精4星强音热望-额外攻击力提升,局内攻击力%,0.087,,,,,,
Buff-武器-精5星强音热望-额外攻击力提升,局内攻击力%,0.096,,,,,,
Buff-角色-扳机-核心被动-失衡易伤,全时段失衡易伤增加,0.35,,,,,,
Buff-角色-扳机-额外能力-追加攻击失衡值提升,追加攻击失衡值增加,0.01,,,,,,
Buff-角色-扳机-协同攻击-触发器,,,,,,,,
Buff-角色-扳机-协战状态-触发器,,,,,,,,
Buff-角色-扳机-1画-失衡易伤提升,全时段失衡易伤增加,0.2,,,,,,
Buff-角色-扳机-1画-决意值提升触发器,,,,,,,,
Buff-角色-扳机-2画-猎眸,,,,,,,,
Buff-角色-扳机-4画-断离触发器,,,,,,,,
Buff-角色-扳机-6画-破甲凶弹触发器,,,,,,,,
Buff-角色-零号·安比-银星触发器,,,,,,,,
Buff-角色-零号·安比-核心被动-增伤,全增伤,0.25,,,,,,
Buff-角色-零号·安比-核心被动-受暴伤增加,追加攻击暴伤,0.01,,,,,,
Buff-角色-零号·安比-组队被动-暴击率提升,局内暴击率,0.1,,,,,,
Buff-角色-零号·安比-组队被动-全队对银星目标增伤,追加攻击增伤,0.25,,,,,,
Buff-角色-零号·安比-2画-暴击率提升,局内暴击率,0.12,,,,,,
Buff-角色-零号·安比-4画-无视电抗,电抗性穿透,0.12,,,,,,
Buff-武器-精1牺牲洁纯-常驻暴伤,局内暴击伤害,0.3,,,,,,
Buff-武器-精2牺牲洁纯-常驻暴伤,局内暴击伤害,0.345,,,,,,
Buff-武器-精3牺牲洁纯-常驻暴伤,局内暴击伤害,0.39,,,,,,
Buff-武器-精4牺牲洁纯-常驻暴伤,局内暴击伤害,0.435,,,,,,
Buff-武器-精5牺牲洁纯-常驻暴伤,局内暴击伤害,0.48,,,,,,
Buff-武器-精1牺牲洁纯-触发暴伤,局内暴击伤害,0.1,,,,,,
Buff-武器-精2牺牲洁纯-触发暴伤,局内暴击伤害,0.115,,,,,,
Buff-武器-精3牺牲洁纯-触发暴伤,局内暴击伤害,0.13,,,,,,
Buff-武器-精4牺牲洁纯-触发暴伤,局内暴击伤害,0.145,,,,,,
Buff-武器-精5牺牲洁纯-触发暴伤,局内暴击伤害,0.16,,,,,,
Buff-武器-精1牺牲洁纯-满层电伤,电属性伤害,0.2,,,,,,
Buff-武器-精2牺牲洁纯-满层电伤,电属性伤害,0.23,,,,,,
Buff-武器-精3牺牲洁纯-满层电伤,电属性伤害,0.26,,,,,,
Buff-武器-精4牺牲洁纯-满层电伤,电属性伤害,0.29,,,,,,
Buff-武器-精5牺牲洁纯-满层电伤,电属性伤害,0.32,,,,,,
Buff-驱动盘-如影相随-二件套,追加攻击增伤,0.15,冲刺攻击增伤,0.15,,,,
Buff-驱动盘-如影相随-四件套,局内攻击力%,0.04,局内暴击率,0.04,,,,
Buff-武器-精1索魂影眸-减防,百分比减防,0.25,,,,,,
Buff-武器-精2索魂影眸-减防,百分比减防,0.2875,,,,,,
Buff-武器-精3索魂影眸-减防,百分比减防,0.325,,,,,,
Buff-武器-精4索魂影眸-减防,百分比减防,0.3625,,,,,,
Buff-武器-精5索魂影眸-减防,百分比减防,0.4,,,,,,
Buff-武器-精1索魂影眸-魂锁,局内冲击力%,0.04,,,,,,
Buff-武器-精2索魂影眸-魂锁,局内冲击力%,0.046,,,,,,
Buff-武器-精3索魂影眸-魂锁,局内冲击力%,0.052,,,,,,
Buff-武器-精4索魂影眸-魂锁,局内冲击力%,0.058,,,,,,
Buff-武器-精5索魂影眸-魂锁,局内冲击力%,0.064,,,,,,
Buff-武器-精1索魂影眸-冲击力,局内冲击力%,0.08,,,,,,
Buff-武器-精2索魂影眸-冲击力,局内冲击力%,0.092,,,,,,
Buff-武器-精3索魂影眸-冲击力,局内冲击力%,0.104,,,,,,
Buff-武器-精4索魂影眸-冲击力,局内冲击力%,0.116,,,,,,
Buff-武器-精5索魂影眸-冲击力,局内冲击力%,0.128,,,,,,
Buff-角色-柳-架势-上弦,电属性伤害,0.1,,,,,,
Buff-角色-柳-架势-下弦,局内穿透率,0.1,,,,,,
Buff-角色-柳-森罗万象,,,,,,,,
Buff-角色-柳-核心被动-紊乱倍率提升,紊乱倍率增加,2.5,,,,,,
Buff-角色-柳-核心被动-电伤增幅,电属性伤害,0.2,,,,,,
Buff-角色-柳-额外能力-积蓄效率,普攻积蓄效率增加,0.45,,,,,,
Buff-角色-柳-极性紊乱触发器,,,,,,,,
Buff-角色-柳-1画-洞悉,,,,,,,,
Buff-角色-柳-1画-精通增幅,固定异常精通,80,,,,,,
Buff-角色-柳-2画-积蓄效率,电积蓄效率增加,0.2,,,,,,
Buff-角色-柳-4画-识破,局内穿透率,0.16,,,,,,
Buff-角色-柳-6画-特殊技伤害提升,强化特殊技增伤,0.2,,,,,,
Buff-角色-简-狂热状态触发器,,,,,,,,
Buff-角色-简-狂热-物理积蓄效率提升,物理积蓄效率增加,0.25,,,,,,
Buff-角色-简-狂热-额外精通转攻击力,固定攻击力,2,,,,,,
Buff-角色-简-核心被动-啮咬触发器,畏缩时间延长,300,,,,,,
Buff-角色-简-核心被动-啮咬-强击暴击率提升,强击暴击率增加,0.01,,,,,,
Buff-角色-简-核心被动-啮咬-强击暴击伤害提升,强击暴击伤害增加,0.5,,,,,,
Buff-角色-简-额外能力-物理异常积蓄效率提升,物理积蓄效率增加,0.2,,,,,,
Buff-角色-简-额外能力-物理异常积蓄效率额外提升,物理积蓄效率增加,0.15,,,,,,
Buff-角色-简-1画-狂热物理异常积蓄效率额外提升,物理积蓄效率增加,0.15,,,,,,
Buff-角色-简-1画-精通转增伤,全增伤,0.01,,,,,,
Buff-角色-简-2画-啮咬-强击无视防御与暴击伤害提升,强击无视防御,0.15,强击暴击伤害增加,0.5,,,,
Buff-角色-简-2画-啮咬-攻击无视防御,百分比减防,0.15,,,,,,
Buff-角色-简-4画-全队异常伤害提升,,,,,,,,
Buff-角色-简-6画-双暴提升,局内暴击率,0.2,局内暴击伤害,0.4,,,,
Buff-角色-简-6画-额外攻击触发器,,,,,,,,
Buff-角色-薇薇安-协同攻击触发器,,,,,,,,
Buff-角色-薇薇安-羽毛结算触发器,,,,,,,,
Buff-角色-薇薇安-羽毛结算触发器,,,,,,,,
Buff-角色-薇薇安-核心被动触发器,,,,,,,,
Buff-角色-薇薇安-预言触发器,,,,,,,,
Buff-角色-薇薇安-额外能力-协同攻击触发器,,,,,,,,
Buff-角色-薇薇安-额外能力-全队侵蚀伤害增加,侵蚀额外伤害增幅,0.12,,,,,,
Buff-角色-薇薇安-额外能力-侵蚀紊乱伤害提升,紊乱额外伤害增幅,0.12,,,,,,
Buff-角色-薇薇安-1画-全属性异常和紊乱伤害提升,全属性异常额外伤害增幅,0.16,紊乱额外伤害增幅,0.16,,,,
Buff-角色-薇薇安-2画-以太积蓄效率提升,以太积蓄效率增加,0.25,,,,,,
Buff-角色-薇薇安-2画-异放全属性抗性穿透,全属性抗性穿透,0.15,,,,,,
Buff-角色-薇薇安-4画-悬落与落羽生花必暴,局内暴击率,1,,,,,,
Buff-角色-薇薇安-4画-局内攻击力增幅,局内攻击力%,0.12,,,,,,
Buff-角色-薇薇安-6画-以太伤害增加,以太属性伤害,0.4,,,,,,
Buff-驱动盘-法厄同之歌-四件套-以太伤害提高,以太属性伤害,0.25,,,,,,
Buff-驱动盘-法厄同之歌-四件套-精通增幅,固定异常精通,45,,,,,,
Buff-武器-精1飞鸟星梦-属性异常积蓄效率,全积蓄效率增加,0.4,,,,,,
Buff-武器-精2飞鸟星梦-属性异常积蓄效率,全积蓄效率增加,0.46,,,,,,
Buff-武器-精3飞鸟星梦-属性异常积蓄效率,全积蓄效率增加,0.52,,,,,,
Buff-武器-精4飞鸟星梦-属性异常积蓄效率,全积蓄效率增加,0.58,,,,,,
Buff-武器-精5飞鸟星梦-属性异常积蓄效率,全积蓄效率增加,0.64,,,,,,
Buff-武器-精1飞鸟星梦-精通增幅,固定异常精通,20,,,,,,
Buff-武器-精2飞鸟星梦-精通增幅,固定异常精通,23,,,,,,
Buff-武器-精3飞鸟星梦-精通增幅,固定异常精通,26,,,,,,
Buff-武器-精4飞鸟星梦-精通增幅,固定异常精通,29,,,,,,
Buff-武器-精5飞鸟星梦-精通增幅,固定异常精通,32,,,,,,
Buff-武器-精1时流贤者-电积蓄效率提升,电积蓄效率增加,0.3,,,,,,
Buff-武器-精2时流贤者-电积蓄效率提升,电积蓄效率增加,0.35,,,,,,
Buff-武器-精3时流贤者-电积蓄效率提升,电积蓄效率增加,0.4,,,,,,
Buff-武器-精4时流贤者-电积蓄效率提升,电积蓄效率增加,0.45,,,,,,
Buff-武器-精5时流贤者-电积蓄效率提升,电积蓄效率增加,0.5,,,,,,
Buff-武器-精1时流贤者-精通提升,固定异常精通,75,,,,,,
Buff-武器-精2时流贤者-精通提升,固定异常精通,85,,,,,,
Buff-武器-精3时流贤者-精通提升,固定异常精通,95,,,,,,
Buff-武器-精4时流贤者-精通提升,固定异常精通,105,,,,,,
Buff-武器-精5时流贤者-精通提升,固定异常精通,115,,,,,,
Buff-武器-精1时流贤者-装备者紊乱伤害提升,紊乱额外伤害增幅,0.25,,,,,,
Buff-武器-精2时流贤者-装备者紊乱伤害提升,紊乱额外伤害增幅,0.275,,,,,,
Buff-武器-精3时流贤者-装备者紊乱伤害提升,紊乱额外伤害增幅,0.3,,,,,,
Buff-武器-精4时流贤者-装备者紊乱伤害提升,紊乱额外伤害增幅,0.325,,,,,,
Buff-武器-精5时流贤者-装备者紊乱伤害提升,紊乱额外伤害增幅,0.35,,,,,,
Buff-武器-精1淬锋钳刺-猎意,物理属性伤害,0.12,,,,,,
Buff-武器-精2淬锋钳刺-猎意,物理属性伤害,0.15,,,,,,
Buff-武器-精3淬锋钳刺-猎意,物理属性伤害,0.18,,,,,,
Buff-武器-精4淬锋钳刺-猎意,物理属性伤害,0.21,,,,,,
Buff-武器-精5淬锋钳刺-猎意,物理属性伤害,0.24,,,,,,
Buff-武器-精1淬锋钳刺-属性异常积蓄效率提升,全积蓄效率增加,0.4,,,,,,
Buff-武器-精2淬锋钳刺-属性异常积蓄效率提升,全积蓄效率增加,0.5,,,,,,
Buff-武器-精3淬锋钳刺-属性异常积蓄效率提升,全积蓄效率增加,0.6,,,,,,
Buff-武器-精4淬锋钳刺-属性异常积蓄效率提升,全积蓄效率增加,0.7,,,,,,
Buff-武器-精5淬锋钳刺-属性异常积蓄效率提升,全积蓄效率增加,0.8,,,,,,
Buff-武器-精1玲珑妆匣-回能,,,,,,,,
Buff-武器-精2玲珑妆匣-回能,,,,,,,,
Buff-武器-精3玲珑妆匣-回能,,,,,,,,
Buff-武器-精4玲珑妆匣-回能,,,,,,,,
Buff-武器-精5玲珑妆匣-回能,,,,,,,,
Buff-武器-精1玲珑妆匣-全队增伤,全增伤,0.1,,,,,,
Buff-武器-精2玲珑妆匣-全队增伤,全增伤,0.115,,,,,,
Buff-武器-精3玲珑妆匣-全队增伤,全增伤,0.13,,,,,,
Buff-武器-精4玲珑妆匣-全队增伤,全增伤,0.145,,,,,,
Buff-武器-精5玲珑妆匣-全队增伤,全增伤,0.16,,,,,,
Buff-武器-精1雨林饕客-局内攻击力,局内攻击力%,0.025,,,,,,
Buff-武器-精2雨林饕客-局内攻击力,局内攻击力%,0.028,,,,,,
Buff-武器-精3雨林饕客-局内攻击力,局内攻击力%,0.032,,,,,,
Buff-武器-精4雨林饕客-局内攻击力,局内攻击力%,0.036,,,,,,
Buff-武器-精5雨林饕客-局内攻击力,局内攻击力%,0.04,,,,,,
Buff-武器-精1双生泣星-精通增幅,固定异常精通,30,,,,,,
Buff-武器-精2双生泣星-精通增幅,固定异常精通,34,,,,,,
Buff-武器-精3双生泣星-精通增幅,固定异常精通,38,,,,,,
Buff-武器-精4双生泣星-精通增幅,固定异常精通,42,,,,,,
Buff-武器-精5双生泣星-精通增幅,固定异常精通,48,,,,,,
Buff-武器-精1触电唇彩-攻击力与增伤,全增伤,0.15,局内攻击力%,0.1,,,,
Buff-武器-精2触电唇彩-攻击力与增伤,全增伤,0.175,局内攻击力%,0.115,,,,
Buff-武器-精3触电唇彩-攻击力与增伤,全增伤,0.2,局内攻击力%,0.13,,,,
Buff-武器-精4触电唇彩-攻击力与增伤,全增伤,0.225,局内攻击力%,0.145,,,,
Buff-武器-精5触电唇彩-攻击力与增伤,全增伤,0.25,局内攻击力%,0.16,,,,
Buff-武器-精1轰鸣座驾-触发器,,,,,,,,
Buff-武器-精2轰鸣座驾-触发器,,,,,,,,
Buff-武器-精3轰鸣座驾-触发器,,,,,,,,
Buff-武器-精4轰鸣座驾-触发器,,,,,,,,
Buff-武器-精5轰鸣座驾-触发器,,,,,,,,
Buff-武器-精1轰鸣座驾-攻击力,局内攻击力%,0.08,,,,,,
Buff-武器-精2轰鸣座驾-攻击力,局内攻击力%,0.092,,,,,,
Buff-武器-精3轰鸣座驾-攻击力,局内攻击力%,0.104,,,,,,
Buff-武器-精4轰鸣座驾-攻击力,局内攻击力%,0.116,,,,,,
Buff-武器-精5轰鸣座驾-攻击力,局内攻击力%,0.128,,,,,,
Buff-武器-精1轰鸣座驾-精通提升,固定异常精通,40,,,,,,
Buff-武器-精2轰鸣座驾-精通提升,固定异常精通,46,,,,,,
Buff-武器-精3轰鸣座驾-精通提升,固定异常精通,52,,,,,,
Buff-武器-精4轰鸣座驾-精通提升,固定异常精通,58,,,,,,
Buff-武器-精5轰鸣座驾-精通提升,固定异常精通,64,,,,,,
Buff-武器-精1轰鸣座驾-属性异常积蓄,全积蓄效率增加,0.25,,,,,,
Buff-武器-精2轰鸣座驾-属性异常积蓄,全积蓄效率增加,0.28,,,,,,
Buff-武器-精3轰鸣座驾-属性异常积蓄,全积蓄效率增加,0.32,,,,,,
Buff-武器-精4轰鸣座驾-属性异常积蓄,全积蓄效率增加,0.36,,,,,,
Buff-武器-精5轰鸣座驾-属性异常积蓄,全积蓄效率增加,0.4,,,,,,
Buff-武器-精1「电磁暴」-壹式-异常掌控,固定异常掌控,25,,,,,,
Buff-武器-精2「电磁暴」-壹式-异常掌控,固定异常掌控,28,,,,,,
Buff-武器-精3「电磁暴」-壹式-异常掌控,固定异常掌控,32,,,,,,
Buff-武器-精4「电磁暴」-壹式-异常掌控,固定异常掌控,36,,,,,,
Buff-武器-精5「电磁暴」-壹式-异常掌控,固定异常掌控,40,,,,,,
Buff-武器-精1「电磁暴」-贰式-异常精通,固定异常精通,25,,,,,,
Buff-武器-精2「电磁暴」-贰式-异常精通,固定异常精通,28,,,,,,
Buff-武器-精3「电磁暴」-贰式-异常精通,固定异常精通,32,,,,,,
Buff-武器-精4「电磁暴」-贰式-异常精通,固定异常精通,36,,,,,,
Buff-武器-精5「电磁暴」-贰式-异常精通,固定异常精通,40,,,,,,
Buff-武器-精1「电磁暴」-叁式-回能,,,,,,,,
Buff-武器-精2「电磁暴」-叁式-回能,,,,,,,,
Buff-武器-精3「电磁暴」-叁式-回能,,,,,,,,
Buff-武器-精4「电磁暴」-叁式-回能,,,,,,,,
Buff-武器-精5「电磁暴」-叁式-回能,,,,,,,,
Buff-角色-雨果-核心被动-暗渊回响,固定暴击率,0.12,固定暴击伤害,0.25,,,,
Buff-角色-雨果-核心被动-单击破攻击力,固定攻击力,300,,,,,,
Buff-角色-雨果-核心被动-双击破攻击力,固定攻击力,600,,,,,,
Buff-角色-雨果-决算触发器,,,,,,,,
Buff-角色-雨果-决算倍率增幅,额外伤害倍率,0.01,,,,,,
Buff-角色-雨果-核心被动-强化E失衡值提升,强化特殊技失衡值增加,0.2,,,,,,
Buff-角色-雨果-额外能力-连携技伤害提升,连携技增伤,0.15,,,,,,
Buff-角色-雨果-额外能力-连携技对普通敌人伤害提升,连携技增伤,0.35,,,,,,
Buff-角色-雨果-额外能力-决算招式增伤,全增伤,0.4,,,,,,
Buff-角色-雨果-额外能力-强化E回能触发器,,,,,,,,
Buff-角色-雨果-1画-决算招式双暴增幅,固定暴击率,0.12,固定暴击伤害,0.3,,,,
Buff-角色-雨果-2画-决算招式无视防御力,百分比减防,0.15,,,,,,
Buff-角色-雨果-4画-蓄力射击减冰抗,冰抗性穿透,0.12,,,,,,
Buff-角色-雨果-6画-决算招式增伤,全增伤,0.6,,,,,,
Buff-武器-精1千面日陨-常驻暴伤,固定暴击伤害,0.45,,,,,,
Buff-武器-精2千面日陨-常驻暴伤,固定暴击伤害,0.5175,,,,,,
Buff-武器-精3千面日陨-常驻暴伤,固定暴击伤害,0.585,,,,,,
Buff-武器-精4千面日陨-常驻暴伤,固定暴击伤害,0.6525,,,,,,
Buff-武器-精5千面日陨-常驻暴伤,固定暴击伤害,0.72,,,,,,
Buff-武器-精1千面日陨-零度处刑,百分比减防,0.25,,,,,,
Buff-武器-精2千面日陨-零度处刑,百分比减防,0.2875,,,,,,
Buff-武器-精3千面日陨-零度处刑,百分比减防,0.325,,,,,,
Buff-武器-精4千面日陨-零度处刑,百分比减防,0.3625,,,,,,
Buff-武器-精5千面日陨-零度处刑,百分比减防,0.4,,,,,,
Buff-武器-精1钢铁肉垫-常驻物理伤,物理属性伤害,0.2,,,,,,
Buff-武器-精2钢铁肉垫-常驻物理伤,物理属性伤害,0.25,,,,,,
Buff-武器-精3钢铁肉垫-常驻物理伤,物理属性伤害,0.3,,,,,,
Buff-武器-精4钢铁肉垫-常驻物理伤,物理属性伤害,0.35,,,,,,
Buff-武器-精5钢铁肉垫-常驻物理伤,物理属性伤害,0.4,,,,,,
Buff-武器-精1钢铁肉垫-背击增伤,全增伤,0.25,,,,,,
Buff-武器-精2钢铁肉垫-背击增伤,全增伤,0.315,,,,,,
Buff-武器-精3钢铁肉垫-背击增伤,全增伤,0.38,,,,,,
Buff-武器-精4钢铁肉垫-背击增伤,全增伤,0.44,,,,,,
Buff-武器-精5钢铁肉垫-背击增伤,全增伤,0.5,,,,,,
Buff-武器-精1街头巨星-终结技增伤,终结技增伤,0.15,,,,,,
Buff-武器-精2街头巨星-终结技增伤,终结技增伤,0.172,,,,,,
Buff-武器-精3街头巨星-终结技增伤,终结技增伤,0.195,,,,,,
Buff-武器-精4街头巨星-终结技增伤,终结技增伤,0.217,,,,,,
Buff-武器-精5街头巨星-终结技增伤,终结技增伤,0.24,,,,,,
Buff-武器-精1鎏金花信-局内攻击和强化E增伤,局内攻击力%,0.06,强化特殊技增伤,0.15,,,,
Buff-武器-精2鎏金花信-局内攻击和强化E增伤,局内攻击力%,0.069,强化特殊技增伤,0.172,,,,
Buff-武器-精3鎏金花信-局内攻击和强化E增伤,局内攻击力%,0.078,强化特殊技增伤,0.195,,,,
Buff-武器-精4鎏金花信-局内攻击和强化E增伤,局内攻击力%,0.087,强化特殊技增伤,0.218,,,,
Buff-武器-精5鎏金花信-局内攻击和强化E增伤,局内攻击力%,0.096,强化特殊技增伤,0.24,,,,
Buff-武器-精1强音热望-攻击力加成,局内攻击力%,0.06,,,,,,
Buff-武器-精2强音热望-攻击力加成,局内攻击力%,0.069,,,,,,
Buff-武器-精3强音热望-攻击力加成,局内攻击力%,0.078,,,,,,
Buff-武器-精4强音热望-攻击力加成,局内攻击力%,0.087,,,,,,
Buff-武器-精5强音热望-攻击力加成,局内攻击力%,0.096,,,,,,
Buff-武器-精1强音热望-额外攻击力加成,局内攻击力%,0.06,,,,,,
Buff-武器-精2强音热望-额外攻击力加成,局内攻击力%,0.069,,,,,,
Buff-武器-精3强音热望-额外攻击力加成,局内攻击力%,0.078,,,,,,
Buff-武器-精4强音热望-额外攻击力加成,局内攻击力%,0.087,,,,,,
Buff-武器-精5强音热望-额外攻击力加成,局内攻击力%,0.096,,,,,,
Buff-武器-精1加农转子-常驻攻击力,局内攻击力%,0.075,,,,,,
Buff-武器-精2加农转子-常驻攻击力,局内攻击力%,0.086,,,,,,
Buff-武器-精3加农转子-常驻攻击力,局内攻击力%,0.097,,,,,,
Buff-武器-精4加农转子-常驻攻击力,局内攻击力%,0.108,,,,,,
Buff-武器-精5加农转子-常驻攻击力,局内攻击力%,0.12,,,,,,
Buff-武器-精1加农转子-附加伤害触发器,,,,,,,,
Buff-武器-精2加农转子-附加伤害触发器,,,,,,,,
Buff-武器-精3加农转子-附加伤害触发器,,,,,,,,
Buff-武器-精4加农转子-附加伤害触发器,,,,,,,,
Buff-武器-精5加农转子-附加伤害触发器,,,,,,,,
Buff-武器-精1「月相」-望-增伤,普攻增伤,0.12,冲刺攻击增伤,0.12,闪避反击增伤,0.12,,
Buff-武器-精2「月相」-望-增伤,普攻增伤,0.14,冲刺攻击增伤,0.14,闪避反击增伤,0.14,,
Buff-武器-精3「月相」-望-增伤,普攻增伤,0.16,冲刺攻击增伤,0.16,闪避反击增伤,0.16,,
Buff-武器-精4「月相」-望-增伤,普攻增伤,0.18,冲刺攻击增伤,0.18,闪避反击增伤,0.18,,
Buff-武器-精5「月相」-望-增伤,普攻增伤,0.2,冲刺攻击增伤,0.2,闪避反击增伤,0.2,,
Buff-武器-精1「月相」-晦-增伤,全增伤,0.15,,,,,,
Buff-武器-精2「月相」-晦-增伤,全增伤,0.175,,,,,,
Buff-武器-精3「月相」-晦-增伤,全增伤,0.2,,,,,,
Buff-武器-精4「月相」-晦-增伤,全增伤,0.225,,,,,,
Buff-武器-精5「月相」-晦-增伤,全增伤,0.25,,,,,,
Buff-武器-精1「月相」-朔-回能触发器,,,,,,,,
Buff-武器-精2「月相」-朔-回能触发器,,,,,,,,
Buff-武器-精3「月相」-朔-回能触发器,,,,,,,,
Buff-武器-精4「月相」-朔-回能触发器,,,,,,,,
Buff-武器-精5「月相」-朔-回能触发器,,,,,,,,
Buff-角色-仪玄-回能事件组触发器,,,,,,,,
Buff-角色-仪玄-核心被动-技能增伤,全增伤,0.6,,,,,,
Buff-角色-仪玄-额外能力-对失衡敌人增伤,全增伤,0.3,,,,,,
Buff-角色-仪玄-额外能力-暴伤提升,固定暴击伤害,0.4,,,,,,
Buff-角色-仪玄-1画-暴击率提升,固定暴击率,0.1,,,,,,
Buff-角色-仪玄-1画-落雷触发器,,,,,,,,
Buff-角色-仪玄-2画-强化E与终结技无视以太抗,以太抗性穿透,0.15,,,,,,
Buff-角色-仪玄-2画-失衡时间提升,失衡延长,180,,,,,,
Buff-角色-仪玄-4画-静心,全增伤,0.3,,,,,,
Buff-角色-仪玄-6画-贯穿伤害提高,贯穿伤害增加,0.2,,,,,,
Buff-武器-精1青溟笼舍-暴击率提升,固定暴击率,0.2,,,,,,
Buff-武器-精2青溟笼舍-暴击率提升,固定暴击率,0.23,,,,,,
Buff-武器-精3青溟笼舍-暴击率提升,固定暴击率,0.26,,,,,,
Buff-武器-精4青溟笼舍-暴击率提升,固定暴击率,0.29,,,,,,
Buff-武器-精5青溟笼舍-暴击率提升,固定暴击率,0.32,,,,,,
Buff-武器-精1青溟笼舍-以太伤害提升,以太属性伤害,0.08,,,,,,
Buff-武器-精2青溟笼舍-以太伤害提升,以太属性伤害,0.092,,,,,,
Buff-武器-精3青溟笼舍-以太伤害提升,以太属性伤害,0.104,,,,,,
Buff-武器-精4青溟笼舍-以太伤害提升,以太属性伤害,0.116,,,,,,
Buff-武器-精5青溟笼舍-以太伤害提升,以太属性伤害,0.128,,,,,,
Buff-武器-精1青溟笼舍-贯穿伤害提升,贯穿伤害增加,0.1,,,,,,
Buff-武器-精2青溟笼舍-贯穿伤害提升,贯穿伤害增加,0.115,,,,,,
Buff-武器-精3青溟笼舍-贯穿伤害提升,贯穿伤害增加,0.13,,,,,,
Buff-武器-精4青溟笼舍-贯穿伤害提升,贯穿伤害增加,0.145,,,,,,
Buff-武器-精5青溟笼舍-贯穿伤害提升,贯穿伤害增加,0.16,,,,,,
Buff-驱动盘-云岿如我-四件套-暴击率提升,固定暴击率,0.04,,,,,,
Buff-驱动盘-云岿如我-四件套-贯穿伤害提升,贯穿伤害增加,0.1,,,,,,
Buff-武器-精1幻变魔方-爆伤提升,固定暴击伤害,0.16,,,,,,
Buff-武器-精2幻变魔方-爆伤提升,固定暴击伤害,0.184,,,,,,
Buff-武器-精3幻变魔方-爆伤提升,固定暴击伤害,0.208,,,,,,
Buff-武器-精4幻变魔方-爆伤提升,固定暴击伤害,0.232,,,,,,
Buff-武器-精5幻变魔方-爆伤提升,固定暴击伤害,0.256,,,,,,
Buff-武器-精1幻变魔方-强化E增伤,强化特殊技增伤,0.2,,,,,,
Buff-武器-精2幻变魔方-强化E增伤,强化特殊技增伤,0.23,,,,,,
Buff-武器-精3幻变魔方-强化E增伤,强化特殊技增伤,0.26,,,,,,
Buff-武器-精4幻变魔方-强化E增伤,强化特殊技增伤,0.29,,,,,,
Buff-武器-精5幻变魔方-强化E增伤,强化特殊技增伤,0.32,,,,,,
Buff-武器-精1电波漫步-贯穿力提升,固定贯穿力,80,,,,,,
Buff-武器-精2电波漫步-贯穿力提升,固定贯穿力,92,,,,,,
Buff-武器-精3电波漫步-贯穿力提升,固定贯穿力,104,,,,,,
Buff-武器-精4电波漫步-贯穿力提升,固定贯穿力,116,,,,,,
Buff-武器-精5电波漫步-贯穿力提升,固定贯穿力,128,,,,,,
Buff-武器-精1「灰烬」-钴蓝-攻击力提升,局内攻击力%,0.072,,,,,,
Buff-武器-精2「灰烬」-钴蓝-攻击力提升,局内攻击力%,0.082,,,,,,
Buff-武器-精3「灰烬」-钴蓝-攻击力提升,局内攻击力%,0.093,,,,,,
Buff-武器-精4「灰烬」-钴蓝-攻击力提升,局内攻击力%,0.104,,,,,,
Buff-武器-精5「灰烬」-钴蓝-攻击力提升,局内攻击力%,0.115,,,,,,
Buff-角色-柚叶-甜蜜惊吓,,,,,,,,
Buff-角色-柚叶-硬糖射击触发器,,,,,,,,
Buff-角色-柚叶-彩糖花火积蓄值增加,全积蓄效率增加,0.01,,,,,,
Buff-角色-柚叶-彩糖花火·极积蓄值增加,全积蓄效率增加,0.01,,,,,,
Buff-角色-柚叶-核心被动-狸之愿-攻击力,固定攻击力,1,,,,,,
Buff-角色-柚叶-核心被动-狸之愿-增伤,全增伤,0.15,,,,,,
Buff-角色-柚叶-组队被动-积蓄值增幅,全积蓄效率增加,0.002,,,,,,
Buff-角色-柚叶-组队被动-属性异常与紊乱伤害增幅,全增伤,0.002,,,,,,
Buff-角色-柚叶-1画-全属性伤害抗性降低,全属性伤害抗性降低,0.1,,,,,,
Buff-角色-柚叶-2画-全队增伤与积蓄效率增幅,全增伤,0.15,全积蓄效率增加,0.15,,,,
Buff-角色-柚叶-2画-连携技触发器,,,,,,,,
Buff-角色-柚叶-4画-支援突击增幅,全增伤,0.3,全积蓄效率增加,0.2,,,,
Buff-角色-柚叶-4画-快支触发器,,,,,,,,
Buff-角色-柚叶-6画-炮弹触发器,,,,,,,,
Buff-角色-柚叶-6画-彩糖花火极触发器,,,,,,,,
Buff-角色-柚叶-6画-紊乱伤害倍率提升,紊乱倍率增加,1.05,,,,,,
Buff-武器-精1狸法七变化-异常掌控,固定异常掌控,30,,,,,,
Buff-武器-精2狸法七变化-异常掌控,固定异常掌控,34.5,,,,,,
Buff-武器-精3狸法七变化-异常掌控,固定异常掌控,39,,,,,,
Buff-武器-精4狸法七变化-异常掌控,固定异常掌控,43.5,,,,,,
Buff-武器-精5狸法七变化-异常掌控,固定异常掌控,48,,,,,,
Buff-武器-精1狸法七变化-全队异常精通,固定异常精通,60,,,,,,
Buff-武器-精2狸法七变化-全队异常精通,固定异常精通,69,,,,,,
Buff-武器-精3狸法七变化-全队异常精通,固定异常精通,78,,,,,,
Buff-武器-精4狸法七变化-全队异常精通,固定异常精通,87,,,,,,
Buff-武器-精5狸法七变化-全队异常精通,固定异常精通,96,,,,,,
Buff-角色-薇薇安-6画-触发器,,,,,,,,
Buff-角色-爱丽丝-核心被动-紊乱基础倍率增加,紊乱倍率增加,0.18,,,,,,
Buff-角色-爱丽丝-核心被动-物理异常积蓄效率提升,物理积蓄效率增加,0.25,,,,,,
Buff-角色-爱丽丝-额外能力-异常掌控转精通,固定异常精通,1,,,,,,
Buff-角色-爱丽丝-影画-1画-减防,百分比减防,0.2,,,,,,
Buff-角色-爱丽丝-影画-2画-全队强击伤害提升,强击额外伤害增幅,0.15,,,,,,
Buff-角色-爱丽丝-影画-2画-紊乱伤害提升,紊乱额外伤害增幅,0.15,,,,,,
Buff-角色-爱丽丝-影画-4画-无视物理伤害抗性,物理抗性穿透,0.1,,,,,,
Buff-角色-爱丽丝-影画-4画-普攻积蓄效率增幅,普攻积蓄效率增加,0.25,,,,,,
Buff-角色-爱丽丝-影画-6画-额外攻击触发器,,,,,,,,
Buff-角色-爱丽丝-影画-6画-额外攻击必暴,固定暴击率,1,,,,,,
Buff-武器-精1十方锻星-异常掌控提升,固定异常掌控,60,,,,,,
Buff-武器-精2十方锻星-异常掌控提升,固定异常掌控,69,,,,,,
Buff-武器-精3十方锻星-异常掌控提升,固定异常掌控,78,,,,,,
Buff-武器-精4十方锻星-异常掌控提升,固定异常掌控,87,,,,,,
Buff-武器-精5十方锻星-异常掌控提升,固定异常掌控,96,,,,,,
Buff-武器-精1十方锻星-物理伤害增加,物理属性伤害,0.2,,,,,,
Buff-武器-精2十方锻星-物理伤害增加,物理属性伤害,0.23,,,,,,
Buff-武器-精3十方锻星-物理伤害增加,物理属性伤害,0.26,,,,,,
Buff-武器-精4十方锻星-物理伤害增加,物理属性伤害,0.29,,,,,,
Buff-武器-精5十方锻星-物理伤害增加,物理属性伤害,0.32,,,,,,
Buff-角色-爱丽丝-极性强击触发器,,,,,,,,
Buff-角色-席德-强袭,固定攻击力,1000,固定暴击伤害,0.3,,,,
Buff-角色-席德-明攻,固定攻击力,1000,固定暴击伤害,0.3,,,,
Buff-角色-席德-围杀,全增伤,0.25,,,,,,
Buff-角色-席德-额外能力-重击大招增伤无视电抗,全增伤,0.3,电抗性穿透,0.25,,,,
Buff-角色-席德-影画-1画-崩坠暴伤增加,固定暴击伤害,0.3,,,,,,
Buff-角色-席德-影画-2画-围杀无视防御力,百分比减防,0.2,,,,,,
Buff-角色-席德-影画-2画-耗能转化增伤,全增伤,0.05,,,,,,
Buff-角色-席德-影画-4画-喧响效率与大招增伤,喧响获得效率,0.1,终结技增伤,0.2,,,,
Buff-角色-席德-影画-6画-常驻暴伤,固定暴击伤害,0.5,,,,,,
Buff-角色-席德-影画-6画-触发器,,,,,,,,
Buff-武器-精1机巧心种-常驻暴击,固定暴击率,0.15,,,,,,
Buff-武器-精2机巧心种-常驻暴击,固定暴击率,0.17,,,,,,
Buff-武器-精3机巧心种-常驻暴击,固定暴击率,0.19,,,,,,
Buff-武器-精4机巧心种-常驻暴击,固定暴击率,0.21,,,,,,
Buff-武器-精5机巧心种-常驻暴击,固定暴击率,0.23,,,,,,
Buff-武器-精1机巧心种-电属性增伤,电属性伤害,0.125,,,,,,
Buff-武器-精2机巧心种-电属性增伤,电属性伤害,0.145,,,,,,
Buff-武器-精3机巧心种-电属性增伤,电属性伤害,0.165,,,,,,
Buff-武器-精4机巧心种-电属性增伤,电属性伤害,0.185,,,,,,
Buff-武器-精5机巧心种-电属性增伤,电属性伤害,0.205,,,,,,
Buff-武器-精1机巧心种-普攻大招无视防御,百分比减防,0.2,,,,,,
Buff-武器-精2机巧心种-普攻大招无视防御,百分比减防,0.23,,,,,,
Buff-武器-精3机巧心种-普攻大招无视防御,百分比减防,0.26,,,,,,
Buff-武器-精4机巧心种-普攻大招无视防御,百分比减防,0.29,,,,,,
Buff-武器-精5机巧心种-普攻大招无视防御,百分比减防,0.32,,,,,,
Buff-驱动盘-拂晓生花-二件套-普攻增伤,普攻增伤,0.15,,,,,,
Buff-驱动盘-拂晓生花-四件套-常驻普攻增伤,普攻增伤,0.2,,,,,,
Buff-驱动盘-拂晓生花-四件套-触发普攻增伤,普攻增伤,0.2,,,,,,
Buff-驱动盘-月光骑士颂-全队增伤,全增伤,0.18,,,,,,
Buff-角色-席德-明攻触发器,,,,,,,,
Buff-角色-席德-影画-2画-无视防御触发器,,,,,,,,
Buff-角色-席德-围杀触发器,,,,,,,,
Buff-角色-席德-影画-4画-触发器,,,,,,,,
================================================
FILE: zsim/data/character.csv
================================================
CID,name,角色属性-中文,角色属性,基础生命值,基础攻击力,基础防御力,基础暴击率,基础暴击伤害,基础暴击分数,基础异常掌控,基础异常精通,基础穿透率,基础穿透值,基础能量自动回复,基础冲击力,角色特性,角色阵营,支援类型,组队被动条件,动作建模,Buff支持,影画支持,精细测帧
1011,安比,电,3,7498.2,658.957,618.3,0.05,0.5,60,94,93,0,0,1.2,136,击破,狡兔屋,招架,同属性|同阵营,-0.5,-1,-1,-1
1021,猫又,物理,0,7561.5,910.5958,586.6,0.194,0.5,88.8,97,96,0,0,1.2,92,强攻,狡兔屋,招架,同属性|同阵营,-0.5,-1,-1,-1
1031,妮可,以太,4,8147.1,649.1691,623.2,0.05,0.5,60,90,93,0,0,1.56,88,支援,狡兔屋,招架,同属性|同阵营,-0.5,0.5,-0.5,-1
1041,11号,火,1,7672.3,888.5686,613.3,0.194,0.5,88.8,94,93,0,0,1.2,93,强攻,奥伯勒斯小队,招架,同属性|同阵营,-0.5,0.5,-1,-1
1061,可琳,物理,0,6975.9,806.9574074,605.4,0.05,0.788,88.8,93,94,0,0,1.2,93,强攻,维多利亚家政,招架,同属性|同阵营,-0.5,-1,-1,-1
1081,比利,物理,0,6909.6,786.938889,605.4,0.194,0.5,88.8,92,91,0,0,1.2,91,强攻,狡兔屋,回避,同属性|同阵营,-0.5,-1,-1,-1
1101,珂蕾妲,火,1,8128.3,735.8413,595.5,0.05,0.5,60,97,96,0,0,1.2,134,击破,白祇重工,招架,同属性|同阵营,-0.5,-1,-1,-1
1111,安东,电,3,7221.2,791.6483,623.2,0.194,0.5,88.8,86,90,0,0,1.2,95,强攻,白祇重工,招架,同属性|同阵营,-0.5,-1,-1,-1
1121,本,火,1,8578.4,866.9388889,724.1,0.05,0.5,60,86,90,0,0,1.2,95,防护,白祇重工,招架,同属性|同阵营,-0.5,-1,-1,-1
1131,苍角,冰,2,8027.4,664.9719,596.5,0.05,0.5,60,93,96,0,0,1.2,86,支援,对空洞特别行动部第六课,招架,同属性|同阵营,-0.5,1,-0.5,-1
1141,莱卡恩,冰,2,8415.2,728.5833,605.4,0.05,0.5,60,91,90,0,0,1.2,137,击破,维多利亚家政,招架,同属性|同阵营,0.5,1,-1,-1
1181,格莉丝,电,3,7673.7042,880.6952,606.5977,0.05,0.5,60,152,116,0,0,1.2,83,异常,白祇重工,回避,同属性|同阵营,-0.5,-1,-1,-1
1191,艾莲,冰,2,7672.3,937.9683,605.4,0.194,0.5,88.8,94,93,0,0,1.2,93,强攻,维多利亚家政,招架,同属性|同阵营,0.9,1,-0.5,-1
1211,丽娜,电,3,8607.1,716.9833329,603.4,0.05,0.5,60,93,92,0.144,0,1.2,83,支援,维多利亚家政,回避,同属性|同阵营,0.5,0.75,-1,-1
1241,朱鸢,以太,4,7482,918.9333335,600,0.05,0.788,88.8,93,102,0,0,1.2,90,强攻,刑侦特勤组,回避,支援|同阵营,0.9,1,-0.5,-1
1151,露西,火,1,7974.05,658.957,613.3684,0.05,0.5,60,94,93,0,0,1.56,86,支援,卡吕冬之子,招架,同属性|同阵营,0.25,0.75,-1,-1
1281,派派,物理,0,6976,758.4172,612,0.05,0.5,60,118,118,0,0,1.2,86,异常,卡吕冬之子,招架,同属性|同阵营,-0.5,-1,-1,-1
1251,青衣,电,3,8251,758.2048,613,0.05,0.5,60,93,94,0,0,1.2,136,击破,刑侦特勤组,招架,强攻|同阵营,0.9,1,-0.5,-1
1261,简,物理,0,7789,880.6952,607,0.05,0.5,60,150,112,0,0,1.2,86,异常,刑侦特勤组,招架,异常|同阵营,-0.5,-0.5,-1,-1
1271,赛斯,电,3,8701,643.2987,746,0.05,0.5,60,90,86,0,0,1.56,94,防护,刑侦特勤组,招架,同属性|同阵营,-0.5,-1,-1,-1
1071,凯撒,物理,0,9526,711.6899,754,0.05,0.5,60,87,90,0,0,1.2,123,防护,卡吕冬之子,招架,招架|同阵营,0.5,1,-1,-1
1171,柏妮思,火,1,7368.1929,863.4528,600.5916,0.05,0.5,60,118,120,0,0,1.56,83,异常,卡吕冬之子,招架,异常|同阵营,-0.5,-1,-1,-1
1221,柳,电,3,7789,872.574,613,0.05,0.5,60,148,114,0,0,1.2,86,异常,对空洞特别行动部第六课,招架,异常|同属性,0.9,1,1,-1
1161,莱特,火,1,8253.2915,797.9569,612.6038,0.05,0.5,60,91,90,0,0,1.2,137,击破,卡吕冬之子,招架,强攻|同阵营,0.75,1,-0.5,-1
1091,雅,烈霜,5,7673.7042,880.6952,606.5977,0.05,0.5,60,116,238,0,0,1.2,86,异常,对空洞特别行动部第六课,招架,支援|同阵营,0.9,1,-0.5,-1
1201,悠真,电,3,7405.6956,915.593,600.5916,0.194,0.5,88.8,95,95,0,0,1.2,90,强攻,对空洞特别行动部第六课,招架,击破|异常,-0.5,-1,-1,-1
1311,耀嘉音,以太,4,8609.2122,715.7699,600.5916,0.05,0.5,60,93,92,0,0,1.56,83,支援,天琴座,回避,强攻|异常,0.75,1,-0.5,0.5
1321,伊芙琳,火,1,7788.6961,929.7586,612.6038,0.194,0.5,88.8,92,90,0,0,1.2,93,强攻,天琴座,招架,击破|支援,-0.5,-1,-1,-1
1381,零号·安比,电,3,7673.7042,929.7586,612.6038,0.194,0.5,88.8,94,93,0,0,1.2,93,强攻,新艾利都防卫军,招架,击破|支援,0.9,1,1,-1
1361,扳机,电,3,7923.1783,750.7503,600.5916,0.05,0.5,60,96,95,0,0,1.2,131,击破,新艾利都防卫军,招架,强攻|同属性,0.75,1,1,-1
1331,薇薇安,以太,4,7673.7042,880.6952,606.5977,0.05,0.5,60,144,118,0,0,1.2,86,异常,反舌鸟,招架,异常|同属性,0.9,1,1,1
1351,波可娜,物理,0,7612.7878,665.8333,606.5977,0.05,0.5,60,92,90,0,0,1.2,136,击破,卡吕冬之子,招架,强攻|同阵营,-0.5,-1,-1,-1
1291,雨果,冰,2,7940.71,919.3011,616.6098,0.194,0.5,88.8,86,90,0,0,1.2,95,强攻,反舌鸟,招架,击破|同属性,0.9,1,1,1
1371,仪玄,玄墨,6,8373.8621,872.5748,441.1145,0.194,0.5,88.8,92,90,0,0,1.2,93,命破,云岿山,招架,击破|支援|防护,0.9,1,1,1
1391,橘福福,火,1,8250.59,765.6593,597.5915,0.194,0.5,88.8,93,96,0,0,1.2,118,击破,云岿山,招架,强攻|命破,-1,-1,-1,-1
1421,潘引壶,物理,0,8453.79,661.9158,712.9433,0.05,0.5,60,91,90,0,0,1.2,94,防护,云岿山,招架,命破|同阵营,-1,-1,-1,-1
1401,爱丽丝,物理,0,7673.7,805.6952,606.5977,0.05,0.5,60,142,118,0,0,1.2,86,异常,怪啖屋,招架,异常|支援,1,1,1,1
1411,柚叶,物理,0,8829.47,683.2048,612.6038,0.05,0.5,60,124,93,0,0,1.2,86,支援,怪啖屋,招架,异常|同阵营,0.75,1,1,1
1461,席德,电,3,7673.7,854.7586,612.6038,0.05,0.788,88.8,94,93,0,0,1.2,93,强攻,新艾利都防卫军,招架,强攻,1,1,1,1
================================================
FILE: zsim/data/character_config_example.toml
================================================
name_box = ["艾莲", "苍角", "莱卡恩"]
["露西"]
name = "露西"
weapon = "好斗的阿炮"
weapon_level = 5
cinema = 6
crit_balancing = false
scATK_percent = 5
scATK = 0
scHP_percent = 0
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 0
scPEN = 0
scCRIT = 6
scCRIT_DMG = 10
drive4 = "攻击力%"
drive5 = "攻击力%"
drive6 = "能量自动回复%"
equip_style = "4+2"
equip_set4 = "摇摆爵士"
equip_set2_a = "自由蓝调"
["丽娜"]
name = "丽娜"
weapon = "啜泣摇篮"
weapon_level = 1
cinema = 0
crit_balancing = false
crit_rate_limit = 0.95
scATK_percent = 6
scATK = 0
scHP_percent = 0
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 15
scPEN = 0
scCRIT = 4
scCRIT_DMG = 3
drive4 = "异常精通"
drive5 = "穿透率"
drive6 = "攻击力%"
equip_style = "4+2"
equip_set4 = "静听嘉音"
equip_set2_a = "摇摆爵士"
["零号·安比"]
name = "零号·安比"
weapon = "牺牲洁纯"
weapon_level = 1
cinema = 0
crit_balancing = false
crit_rate_limit = 0.95
scATK_percent = 8
scATK = 0
scHP_percent = 0
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 0
scPEN = 5
scCRIT = 9
scCRIT_DMG = 13
drive4 = "暴击率%"
drive5 = "电属性伤害%"
drive6 = "攻击力%"
equip_style = "4+2"
equip_set4 = "如影相随"
equip_set2_a = "啄木鸟电音"
["扳机"]
name = "扳机"
weapon = "索魂影眸"
weapon_level = 1
cinema = 0
crit_balancing = false
crit_rate_limit = 0.95
scATK_percent = 8
scATK = 0
scHP_percent = 0
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 0
scPEN = 0
scCRIT = 9
scCRIT_DMG = 13
drive4 = "暴击率%"
drive5 = "电属性伤害%"
drive6 = "攻击力%"
equip_style = "4+2"
equip_set4 = "如影相随"
equip_set2_a = "啄木鸟电音"
["简"]
name = "简"
weapon = "淬锋钳刺"
weapon_level = 1
cinema = 2
crit_balancing = false
scATK_percent = 9
scATK = 0
scHP_percent = 0
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 15
scPEN = 0
scCRIT = 0
scCRIT_DMG = 0
drive4 = "异常精通"
drive5 = "物理属性伤害%"
drive6 = "异常掌控"
equip_style = "4+2"
equip_set4 = "自由蓝调"
equip_set2_a = "激素朋克"
["雅"]
name = "雅"
weapon = "霰落星殿"
weapon_level = 1
cinema = 0
crit_balancing = false
crit_rate_limit = 0.95
scATK_percent = 6
scATK = 0
scHP_percent = 0
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 0
scPEN = 0
scCRIT = 7
scCRIT_DMG = 11
drive4 = "暴击率%"
drive5 = "冰属性伤害%"
drive6 = "攻击力%"
equip_style = "4+2"
equip_set4 = "折枝剑歌"
equip_set2_a = "啄木鸟电音"
["柳"]
name = "柳"
weapon = "时流贤者"
weapon_level = 1
cinema = 0
crit_balancing = true
scATK_percent = 5
scATK = 0
scHP_percent = 0
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 13
scPEN = 0
scCRIT = 0
scCRIT_DMG = 0
drive4 = "异常精通"
drive5 = "电属性伤害%"
drive6 = "异常掌控"
equip_style = "4+2"
equip_set4 = "自由蓝调"
equip_set2_a = "激素朋克"
["薇薇安"]
name = "薇薇安"
weapon = "飞鸟星梦"
weapon_level = 1
cinema = 0
crit_balancing = true
scATK_percent = 5
scATK = 0
scHP_percent = 0
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 15
scPEN = 0
scCRIT = 0
scCRIT_DMG = 0
drive4 = "异常精通"
drive5 = "以太属性伤害%"
drive6 = "异常掌控"
equip_style = "4+2"
equip_set4 = "法厄同之歌"
equip_set2_a = "自由蓝调"
["耀嘉音"]
name = "耀嘉音"
weapon = "玲珑妆匣"
weapon_level = 1
cinema = 0
crit_balancing = false
crit_rate_limit = 0.95
scATK_percent = 15
scATK = 0
scHP_percent = 0
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 10
scPEN = 0
scCRIT = 0
scCRIT_DMG = 0
drive4 = "攻击力%"
drive5 = "攻击力%"
drive6 = "攻击力%"
equip_style = "4+2"
equip_set4 = "静听嘉音"
equip_set2_a = "激素朋克"
["雨果"]
name = "雨果"
weapon = "千面日陨"
weapon_level = 1
cinema = 0
crit_balancing = true
crit_rate_limit = 0.95
scATK_percent = 8
scATK = 0
scHP_percent = 0
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 0
scPEN = 0
scCRIT = 12
scCRIT_DMG = 24
drive4 = "暴击率%"
drive5 = "冰属性伤害%"
drive6 = "攻击力%"
equip_style = "4+2"
equip_set4 = "啄木鸟电音"
equip_set2_a = "激素朋克"
["莱特"]
name = "莱特"
weapon = "焰心桂冠"
weapon_level = 1
cinema = 0
crit_balancing = false
crit_rate_limit = 0.95
scATK_percent = 0
scATK = 0
scHP_percent = 0
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 0
scPEN = 0
scCRIT = 0
scCRIT_DMG = 0
drive4 = "暴击率%"
drive5 = "火属性伤害%"
drive6 = "冲击力%"
equip_style = "4+2"
equip_set4 = "震星迪斯科"
equip_set2_a = "炎狱重金属"
["艾莲"]
name = "艾莲"
weapon = "深海访客"
weapon_level = 1
cinema = 0
crit_balancing = false
crit_rate_limit = 0.95
scATK_percent = 10
scATK = 0
scHP_percent = 0
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 0
scPEN = 0
scCRIT = 10
scCRIT_DMG = 10
drive4 = "暴击率%"
drive5 = "冰属性伤害%"
drive6 = "攻击力%"
equip_style = "4+2"
equip_set4 = "啄木鸟电音"
equip_set2_a = "极地重金属"
["伊芙琳"]
name = "伊芙琳"
weapon = "飞鸟星梦"
weapon_level = 1
cinema = 0
crit_balancing = false
scATK_percent = 0
scATK = 0
scHP_percent = 0
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 0
scPEN = 0
scCRIT = 0
scCRIT_DMG = 0
drive4 = "攻击力%"
drive5 = "攻击力%"
drive6 = "攻击力%"
equip_style = "4+2"
equip_set4 = "自由蓝调"
equip_set2_a = "摇摆爵士"
["苍角"]
name = "苍角"
weapon = "含羞恶面"
weapon_level = 5
cinema = 2
crit_balancing = false
crit_rate_limit = 0.95
scATK_percent = 20
scATK = 0
scHP_percent = 0
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 0
scPEN = 0
scCRIT = 0
scCRIT_DMG = 0
drive4 = "攻击力%"
drive5 = "攻击力%"
drive6 = "攻击力%"
equip_style = "4+2"
equip_set4 = "自由蓝调"
equip_set2_a = "灵魂摇滚"
["仪玄"]
name = "仪玄"
weapon = "青溟笼舍"
weapon_level = 1
cinema = 0
crit_balancing = true
crit_rate_limit = 0.68
scATK_percent = 0
scATK = 0
scHP_percent = 10
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 0
scPEN = 0
scCRIT = 15
scCRIT_DMG = 15
drive4 = "暴击率%"
drive5 = "以太属性伤害%"
drive6 = "攻击力%"
equip_style = "4+2"
equip_set4 = "云岿如我"
equip_set2_a = "折枝剑歌"
["青衣"]
name = "青衣"
weapon = "玉壶青冰"
weapon_level = 1
cinema = 0
crit_balancing = true
crit_rate_limit = 0.95
scATK_percent = 0
scATK = 0
scHP_percent = 0
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 0
scPEN = 0
scCRIT = 10
scCRIT_DMG = 10
drive4 = "暴击率%"
drive5 = "电属性伤害%"
drive6 = "冲击力%"
equip_style = "4+2"
equip_set4 = "震星迪斯科"
equip_set2_a = "啄木鸟电音"
["莱卡恩"]
name = "莱卡恩"
weapon = "拘缚者"
weapon_level = 1
cinema = 0
crit_balancing = false
crit_rate_limit = 0.95
scATK_percent = 10
scATK = 0
scHP_percent = 0
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 0
scPEN = 0
scCRIT = 10
scCRIT_DMG = 10
drive4 = "攻击力%"
drive5 = "攻击力%"
drive6 = "冲击力%"
equip_style = "4+2"
equip_set4 = "震星迪斯科"
equip_set2_a = "啄木鸟电音"
["柚叶"]
name = "柚叶"
weapon = "狸法七变化"
weapon_level = 1
cinema = 0
crit_balancing = false
crit_rate_limit = 0.95
scATK_percent = 10
scATK = 0
scHP_percent = 0
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 0
scPEN = 0
scCRIT = 0
scCRIT_DMG = 0
drive4 = "攻击力%"
drive5 = "攻击力%"
drive6 = "异常掌控"
equip_style = "4+2"
equip_set4 = "摇摆爵士"
equip_set2_a = "法厄同之歌"
["席德"]
name = "席德"
weapon = "机巧心种"
weapon_level = 3
cinema = 6
crit_balancing = true
crit_rate_limit = 0.75
scATK_percent = 10
scATK = 0
scHP_percent = 0
scHP = 0
scDEF_percent = 0
scDEF = 0
scAnomalyProficiency = 0
scPEN = 0
scCRIT = 15
scCRIT_DMG = 20
drive4 = "暴击率%"
drive5 = "电属性伤害%"
drive6 = "攻击力%"
equip_style = "4+2"
equip_set4 = "拂晓生花"
equip_set2_a = "折枝剑歌"
================================================
FILE: zsim/data/csv_excel_sync.py
================================================
import os
import sys
import pandas as pd
def import_csv_to_excel(csv_file, sheet_name, writer):
"""将CSV文件导入到Excel工作表中"""
try:
df = pd.read_csv(csv_file)
df.to_excel(writer, sheet_name=sheet_name, index=False)
print(f"成功导入 {csv_file} 到工作表 {sheet_name}")
return True
except Exception as e:
print(f"导入 {csv_file} 时出错: {str(e)}")
return False
def export_excel_to_csv(excel_file, sheet_name, csv_file):
"""将Excel工作表导出到CSV文件"""
try:
df = pd.read_excel(excel_file, sheet_name=sheet_name)
df.to_csv(csv_file, index=False)
print(f"成功导出工作表 {sheet_name} 到 {csv_file}")
return True
except Exception as e:
print(f"导出工作表 {sheet_name} 时出错: {str(e)}")
return False
# 定义文件和工作表的映射关系
FILE_SHEET_MAPPING = {
"character.csv": "character",
"skill.csv": "skill",
"default_skill.csv": "default_skill",
"weapon.csv": "weapon",
"enemy.csv": "enemy",
"enemy_adjustment.csv": "enemy_adjustment",
"equip_set_2pc.csv": "equip_set_2pc",
"buff_effect.csv": "buff_effect",
"触发判断.csv": "触发判断",
"激活判断.csv": "激活判断",
"enemy_attack_action.csv": "enemy_attack_action",
"enemy_attack_method.csv": "enemy_attack_method",
}
def csv_to_excel():
"""将所有CSV文件导入到Excel中"""
# 定义Excel文件路径
excel_file = "./zsim/data/game_data.xlsx"
# 获取当前脚本所在目录
current_dir = os.path.dirname(os.path.abspath(__file__))
# 创建ExcelWriter对象
with pd.ExcelWriter(excel_file, engine="openpyxl") as writer:
success_count = 0
total_count = len(FILE_SHEET_MAPPING)
# 导入每个CSV文件到对应的工作表
for csv_file, sheet_name in FILE_SHEET_MAPPING.items():
csv_path = os.path.join(current_dir, csv_file)
if os.path.exists(csv_path):
if import_csv_to_excel(csv_path, sheet_name, writer):
success_count += 1
else:
print(f"未找到CSV文件: {csv_path}")
print(f"Excel文件创建完成,成功导入 {success_count}/{total_count} 个文件")
def excel_to_csv():
"""将Excel中的所有工作表导出到CSV文件"""
# 定义Excel文件路径
excel_file = "./zsim/data/game_data.xlsx"
# 获取当前脚本所在目录
current_dir = os.path.dirname(os.path.abspath(__file__))
# 检查Excel文件是否存在
if not os.path.exists(excel_file):
print(f"Excel文件不存在: {excel_file}")
return
success_count = 0
total_count = len(FILE_SHEET_MAPPING)
# 导出每个工作表到对应的CSV文件
for csv_file, sheet_name in FILE_SHEET_MAPPING.items():
csv_path = os.path.join(current_dir, csv_file)
if export_excel_to_csv(excel_file, sheet_name, csv_path):
success_count += 1
print(f"CSV文件导出完成,成功导出 {success_count}/{total_count} 个工作表")
def print_help():
"""打印帮助信息"""
print("CSV和Excel双向同步工具")
print("用法:")
print(" python csv_excel_sync.py [选项]")
print("选项:")
print(" -h, --help 显示此帮助信息")
print(" -i, --import 从CSV导入到Excel")
print(" -e, --export 从Excel导出到CSV")
print("如果不提供选项,将进入交互模式")
def main():
# 处理命令行参数
if len(sys.argv) > 1:
if sys.argv[1] in ["-h", "--help"]:
print_help()
return
elif sys.argv[1] in ["-i", "--import"]:
csv_to_excel()
return
elif sys.argv[1] in ["-e", "--export"]:
excel_to_csv()
return
else:
print(f"未知选项: {sys.argv[1]}")
print_help()
return
# 交互模式
while True:
print("\nCSV和Excel双向同步工具")
print("\033[91m1. 从CSV导入到Excel,会导致Excel的格式配置、表内公式全部丢失\033[0m")
print("2. 从Excel导出到CSV")
print("3. 退出")
choice = input("请选择操作 [1-3]: ")
if choice == "1":
confirm = input(
"\033[91m警告: 此操作会导致Excel的格式配置、表内公式全部丢失,是否继续?[y/n]: \033[0m"
).lower()
if confirm == "y":
csv_to_excel()
elif choice == "2":
confirm = input("确认要从Excel导出到CSV吗?[y/n]: ").lower()
if confirm == "y":
excel_to_csv()
elif choice == "3":
print("程序已退出")
break
else:
print("无效选择,请重试")
if __name__ == "__main__":
main()
================================================
FILE: zsim/data/default_skill.csv
================================================
CID,name,CN_TriggerLevel,skill_tag,CN_skill_tag,skill_text,INSTRUCTION,comment,damage_ratio,damage_ratio_growth,D_LEVEL12,D_LEVEL14,D_LEVEL16,stun_ratio,stun_ratio_growth,S_LEVEL12,S_LEVEL14,S_LEVEL16,sp_threshold,sp_consume,sp_recovery,fever_recovery,self_fever_re,distance_attenuation,initial_level,anomaly_accumulation,skill_type,trigger_buff_level,element_type,element_damage_percent,diff_multiplier,ticks,hit_times,on_field,anomaly_attack,interruption_resistance,swap_cancel_ticks,labels,follow_up,follow_by,aid_direction,aid_lag_ticks,tick_list,force_add_condition_APL,heavy_attack,max_repeat_times,do_immediately,anomaly_update_list,adrenaline_recovery,adrenaline_threshold,adrenaline_consume
0,0,0,dodge,闪避,默认技能,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,0,FALSE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,,0,0,0
0,0,0,switch,向前切人,默认技能,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,FALSE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,,0,0,0
0,0,0,bwswitch,向后切人,默认技能,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,FALSE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,,0,0,0
0,0,0,interrupted,被打断,默认技能,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,0,FALSE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,,0,0,0
0,0,0,sleep,发呆,默认技能,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,FALSE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,,0,0,0
0,0,0,CannonRotorAdditionalDamage,加农转子附加伤害,默认技能,0,0,2,0,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,1,0,FALSE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,,0,0,0
0,0,0,knock_back_cause_parry,招架后被击退,默认技能,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,FALSE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,,0,0,0
================================================
FILE: zsim/data/enemy.csv
================================================
CN_enemy_ID,SubID,IndexID,生命值,攻击力,防御力,暴击伤害,失衡值上限,能否失衡,失衡值自动回复,失衡值自动回复时限,失衡恢复速度,失衡恢复时间,失衡易伤值,可连携次数,抗打断等级,冻结抵抗,冰伤害抗性,火伤害抗性,电伤害抗性,物理伤害抗性,以太伤害抗性,冰异常抗性,火异常抗性,电异常抗性,物理异常抗性,以太异常抗性,冰失衡抗性,火失衡抗性,电失衡抗性,物理失衡抗性,以太失衡抗性,70级最大生命值,70级最大攻击力,70级最大失衡值上限,60级及以上防御力,能否异常,进攻策略
搜捕巡查员,900011011,11011,1123,60,40,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,278447.85,1956.37,1410.0,635.2,True,0
搜捕巡查员,900011012,11012,1373,60,40,0.5,900,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,340435.35,1956.37,2115.0,635.2,True,0
武装巡查员,900011021,11021,923,54,40,0.5,360,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,228857.85,1760.73,846.0,635.2,True,0
武装巡查员,900011022,11022,1073,54,40,0.5,540,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,266050.35,1760.73,1269.0,635.2,True,0
防暴巡查员,900011031,11031,1123,60,46,0.5,600,True,0.0,0,0.1538,6.5,0.25,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,278447.85,1956.37,1410.0,730.48,True,0
防暴巡查员,900011032,11032,1373,60,46,0.5,900,True,0.0,0,0.1538,6.5,0.25,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,340435.35,1956.37,2115.0,730.48,True,0
自律辅助单位·「清扫者」,900011041,11041,6759,84,54,0.5,3335,True,0.0,0,0.1,10.0,0.5,2,3,0.5,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,1675894.05,2738.91,7837.25,857.52,True,0
自律辅助单位·「清扫者」,900011044,11044,7001,96,54,0.5,3783,True,0.0,0,0.1,10.0,0.5,2,5,0.5,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,1735897.95,3130.19,8890.05,857.52,True,0
非法辅助单位·「怒汉」,900011045,11045,7001,96,54,0.5,3783,True,0.0,0,0.1,10.0,0.5,2,5,0.5,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,1735897.95,3130.19,8890.05,857.52,True,0
非法辅助单位·「怒汉」,900011046,11046,6759,84,54,0.5,3335,True,0.0,0,0.1,10.0,0.5,2,3,0.5,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,1675894.05,2738.91,7837.25,857.52,True,0
自律辅助单位·「卫士Ⅱ型」,900011051,11051,8417,92,54,0.5,4161,True,0.0,0,0.0769,13.0,0.5,2,5,0.5,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,2086995.15,2999.76,9778.35,857.52,True,0
自律辅助单位·「卫士Ⅱ型」,900011052,11052,8736,106,54,0.5,4753,True,0.0,0,0.0769,13.0,0.5,2,5,0.5,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,2166091.2,3456.25,11169.55,857.52,True,0
自律辅助单位·「卫士Ⅱ型」,900011054,11054,8417,92,54,0.5,4161,True,0.0,0,0.0769,13.0,0.5,2,5,0.5,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,2086995.15,2999.76,9778.35,857.52,True,0
自律辅助单位·「卫士Ⅱ型」,900011056,11056,8417,92,54,0.5,4161,True,0.0,0,0.0769,13.0,0.5,2,5,0.5,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,2086995.15,2999.76,9778.35,857.52,True,0
自律强袭单位·「卫士Ⅲ型」,900011057,11057,17218,120,60,0.5,5991,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,4269203.1,3912.73,14078.85,952.8,True,0
自律辅助单位·「卫士」,900011058,11058,8417,92,54,0.5,4161,True,0.0,0,0.0769,13.0,0.5,2,5,0.5,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,2086995.15,2999.76,9778.35,857.52,True,0
自律辅助单位·「卫士」,900011059,11059,8736,106,54,0.5,4753,True,0.0,0,0.0769,13.0,0.5,2,5,0.5,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,2166091.2,3456.25,11169.55,857.52,True,0
自律战术单位·「提丰·重击者型」,900011061,11061,16778,132,60,0.5,5543,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,4160105.1,4304.01,13026.05,952.8,True,0
自律战术单位·「提丰·重击者型」,900011062,11062,16778,132,60,0.5,5543,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,4160105.1,4304.01,13026.05,952.8,True,0
自律战术单位·「提丰·挑战者型」,900011063,11063,16778,132,60,0.5,5543,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,4160105.1,4304.01,13026.05,952.8,True,0
自律战术单位·「提丰·挑战者型」,900011064,11064,16778,132,60,0.5,5543,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,4160105.1,4304.01,13026.05,952.8,True,0
雷蛛,900011083,11083,462,44,43,0.5,180,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,114552.9,1434.67,423.0,682.84,True,0
雷蛛,900011084,11084,537,44,43,0.5,270,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,133149.15,1434.67,634.5,682.84,True,0
雷蛛·蓄能型,900011085,11085,462,44,43,0.5,180,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,114552.9,1434.67,423.0,682.84,True,0
雷蛛·蓄能型,900011086,11086,537,44,43,0.5,270,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,133149.15,1434.67,634.5,682.84,True,0
提尔锋,900011096,11096,1123,66,36,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,278447.85,2152.0,1410.0,571.68,True,0
提尔锋,900011097,11097,1373,66,36,0.5,900,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,340435.35,2152.0,2115.0,571.68,True,0
提尔锋·蓄能型,900011098,11098,1123,66,36,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,278447.85,2152.0,1410.0,571.68,True,0
提尔锋·蓄能型,900011099,11099,1373,66,36,0.5,900,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,340435.35,2152.0,2115.0,571.68,True,0
阿佩卡,900011103,11103,1123,54,36,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,278447.85,1760.73,1410.0,571.68,True,0
阿佩卡,900011104,11104,1373,54,36,0.5,900,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,340435.35,1760.73,2115.0,571.68,True,0
阿佩卡·蓄能型,900011105,11105,1123,54,36,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,278447.85,1760.73,1410.0,571.68,True,0
阿佩卡·蓄能型,900011106,11106,1373,54,36,0.5,900,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,340435.35,1760.73,2115.0,571.68,True,0
法布提,900011114,11114,8417,92,54,0.5,4161,True,0.0,0,0.0769,13.0,0.5,2,5,0.5,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,2086995.15,2999.76,9778.35,857.52,True,0
法布提,900011115,11115,8736,106,54,0.5,4753,True,0.0,0,0.0769,13.0,0.5,2,5,0.5,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,2166091.2,3456.25,11169.55,857.52,True,0
法布提·蓄能型,900011116,11116,8417,92,54,0.5,4161,True,0.0,0,0.0769,13.0,0.5,2,5,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,2086995.15,2999.76,9778.35,857.52,True,0
法布提·蓄能型,900011117,11117,8736,106,54,0.5,4753,True,0.0,0,0.0769,13.0,0.5,2,5,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,2166091.2,3456.25,11169.55,857.52,True,0
哈提,900011123,11123,5890,92,54,0.5,2168,True,0.0,0,0.1,10.0,1.0,2,3,0.5,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,1460425.5,2999.76,5094.8,857.52,True,0
哈提,900011124,11124,6059,106,54,0.5,2459,True,0.0,0,0.1,10.0,1.0,2,3,0.5,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,1502329.05,3456.25,5778.65,857.52,True,0
哈提·蓄能型,900011125,11125,5890,92,54,0.5,2168,True,0.0,0,0.1,10.0,1.0,2,3,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,1460425.5,2999.76,5094.8,857.52,True,0
哈提·蓄能型,900011126,11126,6059,106,54,0.5,2459,True,0.0,0,0.1,10.0,1.0,2,3,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,1502329.05,3456.25,5778.65,857.52,True,0
哈提头犬·蓄能型,900011127,11127,20548,132,60,0.5,5543,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,5094876.6,4304.01,13026.05,952.8,True,0
萨提尔,900011141,11141,1123,66,36,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,278447.85,2152.0,1410.0,571.68,True,0
萨提尔,900011142,11142,1373,66,36,0.5,900,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,340435.35,2152.0,2115.0,571.68,True,0
萨提尔·蓄能型,900011143,11143,1123,66,36,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,278447.85,2152.0,1410.0,571.68,True,0
萨提尔·蓄能型,900011144,11144,1373,66,36,0.5,900,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,340435.35,2152.0,2115.0,571.68,True,0
初生杜拉罕,900011152,11152,16778,120,60,0.5,5543,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,4160105.1,3912.73,13026.05,952.8,True,0
杜拉罕,900011154,11154,7097,97,58,0.5,3502,True,0.0,0,0.1,10.0,0.5,2,3,0.5,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,1759701.15,3162.79,8229.7,921.04,True,0
杜拉罕,900011155,11155,7351,111,58,0.5,3972,True,0.0,0,0.1,10.0,0.5,2,5,0.5,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,1822680.45,3619.28,9334.2,921.04,True,0
恶名·杜拉罕,900011156,11156,16778,120,60,0.5,5543,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,4160105.1,3912.73,13026.05,952.8,True,0
杜拉罕·蓄能型,900011157,11157,7097,97,58,0.5,3502,True,0.0,0,0.1,10.0,0.5,2,3,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,1759701.15,3162.79,8229.7,921.04,True,0
杜拉罕·蓄能型,900011158,11158,7351,111,58,0.5,3972,True,0.0,0,0.1,10.0,0.5,2,5,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,1822680.45,3619.28,9334.2,921.04,True,0
袭扰猎兵,900011161,11161,1240,66,40,0.5,600,True,0.0,0,0.5,2.0,1.0,1,1,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,307458.0,2152.0,1410.0,635.2,True,0
袭扰猎兵,900011162,11162,1615,66,40,0.5,900,True,0.0,0,0.5,2.0,1.0,1,1,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,400439.25,2152.0,2115.0,635.2,True,0
掠袭猎兵,900011163,11163,1240,66,40,0.5,600,True,0.0,0,0.5,2.0,1.0,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,307458.0,2152.0,1410.0,635.2,True,0
掠袭猎兵,900011164,11164,1615,66,40,0.5,900,True,0.0,0,0.5,2.0,1.0,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,400439.25,2152.0,2115.0,635.2,True,0
恶名·塔纳托斯,900011181,11181,18493,120,60,0.5,3880,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,4585339.35,3912.73,9118.0,952.8,True,0
塔纳托斯,900011184,11184,7951,97,45,0.5,2451,True,0.0,0,0.1,10.0,1.0,2,3,0.5,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,1971450.45,3162.79,5759.85,714.6,True,0
塔纳托斯,900011185,11185,8179,101,54,0.5,2781,True,0.0,0,0.1,10.0,1.0,2,5,0.5,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,2027983.05,3293.22,6535.35,857.52,True,0
塔纳托斯·蓄能型,900011186,11186,7951,88,54,0.5,2451,True,0.0,0,0.1,10.0,1.0,2,3,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,1971450.45,2869.34,5759.85,857.52,True,0
塔纳托斯·蓄能型,900011187,11187,8179,101,54,0.5,2781,True,0.0,0,0.1,10.0,1.0,2,5,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,2027983.05,3293.22,6535.35,857.52,True,0
装甲哈提,900011192,11192,6491,116,54,0.5,2648,True,0.0,0,0.1,10.0,1.0,2,3,0.5,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,1609443.45,3782.31,6222.8,857.52,True,0
装甲哈提,900011194,11194,6491,116,54,0.5,2648,True,0.0,0,0.1,10.0,1.0,2,5,0.5,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,1609443.45,3782.31,6222.8,857.52,True,0
恶名·装甲哈提,900011195,11195,15411,120,60,0.5,3880,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,3821157.45,3912.73,9118.0,952.8,True,0
装甲哈提·蓄能型,900011196,11196,6311,102,54,0.5,2335,True,0.0,0,0.1,10.0,1.0,2,3,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,1564812.45,3325.82,5487.25,857.52,True,0
装甲哈提·蓄能型,900011197,11197,6491,116,54,0.5,2648,True,0.0,0,0.1,10.0,1.0,2,5,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,1609443.45,3782.31,6222.8,857.52,True,0
霍普利泰,900011203,11203,1123,60,46,0.5,600,True,0.0,0,0.1538,6.5,0.25,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,278447.85,1956.37,1410.0,730.48,True,0
霍普利泰,900011204,11204,1373,60,46,0.5,900,True,0.0,0,0.1538,6.5,0.25,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,340435.35,1956.37,2115.0,730.48,True,0
霍普利泰·蓄能型,900011205,11205,1123,60,46,0.5,600,True,0.0,0,0.1538,6.5,0.25,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,278447.85,1956.37,1410.0,730.48,True,0
霍普利泰·蓄能型,900011206,11206,1373,60,46,0.5,900,True,0.0,0,0.1538,6.5,0.25,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,340435.35,1956.37,2115.0,730.48,True,0
阿劳恩,900011213,11213,4905,53,45,0.5,2335,True,0.0,0,0.1428,7.0,1.0,2,3,0.5,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,1216194.75,1728.12,5487.25,714.6,True,0
阿劳恩,900011214,11214,5062,60,45,0.5,2648,True,0.0,0,0.1428,7.0,1.0,2,5,0.5,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,1255122.9,1956.37,6222.8,714.6,True,0
阿劳恩·蓄能型,900011215,11215,4905,53,45,0.5,2335,True,0.0,0,0.1428,7.0,1.0,2,3,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,1216194.75,1728.12,5487.25,714.6,True,0
阿劳恩·蓄能型,900011216,11216,5062,60,45,0.5,2648,True,0.0,0,0.1428,7.0,1.0,2,5,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,1255122.9,1956.37,6222.8,714.6,True,0
铁道地精,900011222,11222,7097,97,45,0.5,3502,True,0.0,0,0.1,10.0,0.5,2,3,0.5,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,1759701.15,3162.79,8229.7,714.6,True,0
铁道地精,900011224,11224,7351,111,45,0.5,3972,True,0.0,0,0.1,10.0,0.5,2,5,0.5,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,1822680.45,3619.28,9334.2,714.6,True,0
地精,900011225,11225,7097,97,45,0.5,3502,True,0.0,0,0.1,10.0,0.5,2,3,0.5,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,1759701.15,3162.79,8229.7,714.6,True,0
地精,900011226,11226,7351,111,45,0.5,3972,True,0.0,0,0.1,10.0,0.5,2,5,0.5,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,1822680.45,3619.28,9334.2,714.6,True,0
初生死路屠夫,900011233,11233,10086,120,60,0.5,5991,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,2500823.7,3912.73,14078.85,952.8,True,0
初生死路屠夫,900011234,11234,16778,120,60,0.5,5543,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,4160105.1,3912.73,13026.05,952.8,True,0
死路屠夫,900011235,11235,10086,120,60,0.5,5991,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,2500823.7,3912.73,14078.85,952.8,True,0
死路屠夫,900011236,11236,16778,120,60,0.5,5543,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,4160105.1,3912.73,13026.05,952.8,True,0
初生死路屠夫,900011237,11237,17218,120,60,0.5,5991,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,4269203.1,3912.73,14078.85,952.8,True,0
死路屠夫,900011238,11238,17218,120,60,0.5,5991,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,4269203.1,3912.73,14078.85,952.8,True,0
黄金邦布,900011241,11241,2704,34,45,0.5,1334,True,0.0,0,0.1,10.0,0.5,2,1,0.5,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,670456.8,1108.61,3134.9,714.6,True,0
黄金邦布,900011242,11242,2704,34,45,0.5,1334,True,0.0,0,0.1,10.0,0.5,2,5,0.5,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,670456.8,1108.61,3134.9,714.6,True,0
白金邦布,900011245,11245,2704,34,45,0.5,1334,True,0.0,0,0.1,10.0,0.5,2,1,0.5,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,670456.8,1108.61,3134.9,714.6,True,0
先锋猎兵,900011251,11251,1123,66,40,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,278447.85,2152.0,1410.0,635.2,True,0
先锋猎兵,900011252,11252,1373,66,40,0.5,900,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,340435.35,2152.0,2115.0,635.2,True,0
轻装猎兵,900011253,11253,1123,66,40,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,278447.85,2152.0,1410.0,635.2,True,0
轻装猎兵,900011254,11254,1373,66,40,0.5,900,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,340435.35,2152.0,2115.0,635.2,True,0
突击炮手,900011261,11261,6640,93,54,0.5,2653,True,0.01,0,0.1,10.0,0.5,2,3,0.5,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,1646388.0,3032.37,6234.55,857.52,True,0
突击炮手,900011262,11262,6843,106,54,0.5,3030,True,0.01,0,0.1,10.0,0.5,2,5,0.5,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,1696721.85,3456.25,7120.5,857.52,True,0
重装炮手,900011263,11263,6640,93,54,0.5,2653,True,0.01,0,0.1,10.0,0.5,2,3,0.5,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,1646388.0,3032.37,6234.55,857.52,True,0
重装炮手,900011264,11264,6843,106,54,0.5,3030,True,0.01,0,0.1,10.0,0.5,2,5,0.5,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,1696721.85,3456.25,7120.5,857.52,True,0
整训猎兵,900011271,11271,1123,66,40,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,278447.85,2152.0,1410.0,635.2,True,0
整训猎兵,900011272,11272,1373,66,40,0.5,900,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,340435.35,2152.0,2115.0,635.2,True,0
新晋猎兵,900011273,11273,1123,66,40,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,278447.85,2152.0,1410.0,635.2,True,0
新晋猎兵,900011274,11274,1373,66,40,0.5,900,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,340435.35,2152.0,2115.0,635.2,True,0
巡防猎兵,900011281,11281,1123,60,46,0.5,600,True,0.0,0,0.1538,6.5,0.25,1,1,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,278447.85,1956.37,1410.0,730.48,True,0
巡防猎兵,900011282,11282,1373,60,46,0.5,900,True,0.0,0,0.1538,6.5,0.25,1,1,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,340435.35,1956.37,2115.0,730.48,True,0
戍卫猎兵,900011283,11283,1123,60,46,0.5,600,True,0.0,0,0.1538,6.5,0.25,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,278447.85,1956.37,1410.0,730.48,True,0
戍卫猎兵,900011284,11284,1373,60,46,0.5,900,True,0.0,0,0.1538,6.5,0.25,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,340435.35,1956.37,2115.0,730.48,True,0
曼德拉,900011291,11291,884,63,36,0.5,630,False,0.0,0,0.1538,6.5,0.5,1,99,0.0,-0.2,0.0,0.0,0.0,-0.2,2.8,3.0,3.0,3.0,2.8,-0.2,0.0,0.0,0.0,-0.2,219187.8,2054.18,1480.5,571.68,False,0
曼德拉,900011292,11292,884,63,36,0.5,630,False,0.0,0,0.1538,6.5,0.5,1,99,0.0,-0.2,0.0,0.0,0.0,-0.2,2.8,3.0,3.0,3.0,2.8,-0.2,0.0,0.0,0.0,-0.2,219187.8,2054.18,1480.5,571.68,False,0
曼德拉,900011293,11293,1153,63,36,0.5,945,False,0.0,0,0.1538,6.5,0.5,1,99,0.0,-0.2,0.0,0.0,0.0,-0.2,2.8,3.0,3.0,3.0,2.8,-0.2,0.0,0.0,0.0,-0.2,285886.35,2054.18,2220.75,571.68,False,0
曼德拉,900011294,11294,1153,63,36,0.5,945,False,0.0,0,0.1538,6.5,0.5,1,99,0.0,-0.2,0.0,0.0,0.0,-0.2,2.8,3.0,3.0,3.0,2.8,-0.2,0.0,0.0,0.0,-0.2,285886.35,2054.18,2220.75,571.68,False,0
曼德拉·蓄能型,900011295,11295,1081,63,36,0.5,945,False,0.0,0,0.1538,6.5,0.5,1,99,0.0,0.0,-0.2,0.0,0.0,0.0,3.0,2.8,3.0,3.0,3.0,0.0,-0.2,0.0,0.0,0.0,268033.95,2054.18,2220.75,571.68,False,0
曼德拉·蓄能型,900011296,11296,1081,63,36,0.5,945,False,0.0,0,0.1538,6.5,0.5,1,99,0.0,0.0,-0.2,0.0,0.0,0.0,3.0,2.8,3.0,3.0,3.0,0.0,-0.2,0.0,0.0,0.0,268033.95,2054.18,2220.75,571.68,False,0
曼德拉·蓄能型,900011297,11297,1153,63,36,0.5,945,False,0.0,0,0.1538,6.5,0.5,1,99,0.0,0.0,-0.2,0.0,0.0,0.0,3.0,2.8,3.0,3.0,3.0,0.0,-0.2,0.0,0.0,0.0,285886.35,2054.18,2220.75,571.68,False,0
曼德拉·蓄能型,900011298,11298,1153,63,36,0.5,945,False,0.0,0,0.1538,6.5,0.5,1,99,0.0,0.0,-0.2,0.0,0.0,0.0,3.0,2.8,3.0,3.0,3.0,0.0,-0.2,0.0,0.0,0.0,285886.35,2054.18,2220.75,571.68,False,0
格莱特,900011301,11301,21523,120,60,0.5,7189,True,0.0,0,0.0833,12.0,0.5,3,99,0.75,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,5336627.85,3912.73,16894.15,952.8,True,0
格莱特·蓄能型,900011302,11302,21523,120,60,0.5,7189,True,0.0,0,0.0833,12.0,0.5,3,99,0.75,0.0,-0.2,0.0,0.2,0.0,0.0,-0.2,0.0,0.2,0.0,0.0,-0.2,0.0,0.2,0.0,5336627.85,3912.73,16894.15,952.8,True,0
格莱特·超频型,900011303,11303,21523,120,60,0.5,7189,True,0.0,0,0.0833,12.0,0.5,3,99,0.75,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,5336627.85,3912.73,16894.15,952.8,True,0
盗洞暴徒·偷袭者,900011311,11311,1123,63,40,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,278447.85,2054.18,1410.0,635.2,True,0
盗洞暴徒·偷袭者,900011312,11312,1373,63,40,0.5,900,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,340435.35,2054.18,2115.0,635.2,True,0
盗洞暴徒·袭击者,900011313,11313,1123,63,40,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,278447.85,2054.18,1410.0,635.2,True,0
盗洞暴徒·袭击者,900011314,11314,1373,63,40,0.5,900,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,340435.35,2054.18,2115.0,635.2,True,0
盗洞暴徒·盗猎客,900011321,11321,1123,54,40,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,278447.85,1760.73,1410.0,635.2,True,0
盗洞暴徒·盗猎客,900011322,11322,1373,54,40,0.5,900,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,340435.35,1760.73,2115.0,635.2,True,0
盗洞暴徒·偷猎者,900011323,11323,1123,54,40,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,278447.85,1760.73,1410.0,635.2,True,0
盗洞暴徒·偷猎者,900011324,11324,1373,54,40,0.5,900,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,340435.35,1760.73,2115.0,635.2,True,0
盗洞暴徒·焚毁狂,900011331,11331,1123,54,40,0.5,600,True,0.05,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,278447.85,1760.73,1410.0,635.2,True,0
盗洞暴徒·焚毁狂,900011332,11332,1373,54,40,0.5,900,True,0.05,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,340435.35,1760.73,2115.0,635.2,True,0
盗洞暴徒·纵火犯,900011333,11333,1123,54,40,0.5,600,True,0.05,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,278447.85,1760.73,1410.0,635.2,True,0
盗洞暴徒·纵火犯,900011334,11334,1373,54,40,0.5,900,True,0.05,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,340435.35,1760.73,2115.0,635.2,True,0
盗洞暴徒·劫掠者,900011341,11341,1240,63,40,0.5,600,True,0.0,0,0.5,2.0,1.0,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,307458.0,2054.18,1410.0,635.2,True,0
盗洞暴徒·劫掠者,900011342,11342,1615,63,40,0.5,900,True,0.0,0,0.5,2.0,1.0,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,400439.25,2054.18,2115.0,635.2,True,0
盗洞暴徒·掠夺者,900011343,11343,1240,63,40,0.5,600,True,0.0,0,0.5,2.0,1.0,1,1,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,307458.0,2054.18,1410.0,635.2,True,0
盗洞暴徒·掠夺者,900011344,11344,1615,63,40,0.5,900,True,0.0,0,0.5,2.0,1.0,1,1,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,400439.25,2054.18,2115.0,635.2,True,0
盗洞暴徒·通缉打手,900011351,11351,8414,84,50,0.5,3335,True,0.0,0,0.1,10.0,1.0,2,3,0.5,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,2086251.3,2738.91,7837.25,794.0,True,0
盗洞暴徒·通缉打手,900011352,11352,8655,96,50,0.5,3783,True,0.0,0,0.1,10.0,1.0,2,5,0.5,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,2146007.25,3130.19,8890.05,794.0,True,0
祸首·通缉打手,900011353,11353,20548,120,60,0.5,5543,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,5094876.6,3912.73,13026.05,952.8,True,0
盗洞暴徒·魁梧打手,900011354,11354,8414,84,50,0.5,3335,True,0.0,0,0.1,10.0,1.0,2,3,0.5,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,2086251.3,2738.91,7837.25,794.0,True,0
盗洞暴徒·魁梧打手,900011355,11355,8655,96,50,0.5,3783,True,0.0,0,0.1,10.0,1.0,2,5,0.5,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,2146007.25,3130.19,8890.05,794.0,True,0
盗洞暴徒·通缉虐待狂,900011361,11361,6324,76,50,0.5,2527,True,0.01,0,0.1,10.0,0.5,2,3,0.5,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,1568035.8,2478.06,5938.45,794.0,True,0
盗洞暴徒·通缉虐待狂,900011362,11362,6517,86,50,0.5,2886,True,0.01,0,0.1,10.0,0.5,2,5,0.5,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,1615890.15,2804.12,6782.1,794.0,True,0
盗洞暴徒·魁梧施虐者,900011363,11363,6324,76,50,0.5,2527,True,0.01,0,0.1,10.0,0.5,2,3,0.5,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,1568035.8,2478.06,5938.45,794.0,True,0
盗洞暴徒·魁梧施虐者,900011364,11364,6517,86,50,0.5,2886,True,0.01,0,0.1,10.0,0.5,2,5,0.5,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,1615890.15,2804.12,6782.1,794.0,True,0
眼魔引擎,900011371,11371,1123,54,43,0.5,600,True,0.05,0,0.1538,6.5,0.5,1,1,0.0,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,278447.85,1760.73,1410.0,682.84,True,0
眼魔引擎,900011372,11372,1373,54,43,0.5,900,True,0.05,0,0.1538,6.5,0.5,1,1,0.0,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,340435.35,1760.73,2115.0,682.84,True,0
泰拉斯奎祸车,900011381,11381,8835,88,54,0.5,3502,True,0.0,0,0.1,10.0,1.0,2,3,0.5,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,2190638.25,2869.34,8229.7,857.52,True,0
泰拉斯奎祸车,900011382,11382,9088,101,54,0.5,3972,True,0.0,0,0.1,10.0,1.0,2,5,0.5,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,2253369.6,3293.22,9334.2,857.52,True,0
星期五,900011391,11391,21523,120,60,0.5,7189,True,0.0,0,0.0833,12.0,0.5,3,99,0.75,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,5336627.85,3912.73,16894.15,952.8,True,0
星期五·蓄能型,900011392,11392,21523,120,60,0.5,7189,True,0.0,0,0.0833,12.0,0.5,3,99,0.75,0.0,-0.2,0.0,0.2,0.0,0.0,-0.2,0.0,0.2,0.0,0.0,-0.2,0.0,0.2,0.0,5336627.85,3912.73,16894.15,952.8,True,0
星期五·超频型,900011393,11393,21523,120,60,0.5,7189,True,0.0,0,0.0833,12.0,0.5,3,99,0.75,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,5336627.85,3912.73,16894.15,952.8,True,0
汉斯,900011401,11401,21523,120,60,0.5,7189,True,0.0,0,0.0833,12.0,0.5,3,99,0.75,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,5336627.85,3912.73,16894.15,952.8,True,0
汉斯·蓄能型,900011402,11402,21523,120,60,0.5,7189,True,0.0,0,0.0833,12.0,0.5,3,99,0.75,0.0,-0.2,0.0,0.2,0.0,0.0,-0.2,0.0,0.2,0.0,0.0,-0.2,0.0,0.2,0.0,5336627.85,3912.73,16894.15,952.8,True,0
汉斯·超频型,900011403,11403,21523,120,60,0.5,7189,True,0.0,0,0.0833,12.0,0.5,3,99,0.75,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,5336627.85,3912.73,16894.15,952.8,True,0
未知复合侵蚀体,900011411,11411,12103,120,60,0.5,7189,True,0.0,0,0.0833,12.0,0.5,3,99,0.75,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,3000938.85,3912.73,16894.15,952.8,True,0
未知复合侵蚀体,900011412,11412,22383,120,60,0.5,7189,True,0.0,0,0.0833,12.0,0.5,3,99,0.75,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,5549864.85,3912.73,16894.15,952.8,True,0
未知复合侵蚀体,900011413,11413,22861,120,60,0.5,7189,True,0.0,0,0.0833,12.0,0.5,3,99,0.75,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,5668384.95,3912.73,16894.15,952.8,True,0
未知复合侵蚀体,900011414,11414,22383,120,60,0.5,7189,True,0.0,0,0.0833,12.0,0.5,3,99,0.75,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,5549864.85,3912.73,16894.15,952.8,True,0
冥宁芙·黑纱,900011421,11421,17918,120,60,0.5,5543,True,0.0,0,0.1428,7.0,1.0,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,4442768.1,3912.73,13026.05,952.8,True,0
冥宁芙·黑纱,900011422,11422,17918,120,60,0.5,5543,True,0.0,0,0.1428,7.0,1.0,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,4442768.1,3912.73,13026.05,952.8,True,0
冥宁芙·灰纱,900011431,11431,17918,120,60,0.5,5543,True,0.0,0,0.1428,7.0,1.0,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,4442768.1,3912.73,13026.05,952.8,True,0
冥宁芙·灰纱,900011432,11432,17918,120,60,0.5,5543,True,0.0,0,0.1428,7.0,1.0,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,4442768.1,3912.73,13026.05,952.8,True,0
自律辅助单位·「捷足巡游者Ⅱ型」,900011441,11441,7097,79,54,0.5,3502,True,0.0,0,0.1,10.0,0.5,2,3,0.5,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,1759701.15,2575.88,8229.7,857.52,True,0
自律辅助单位·「捷足巡游者Ⅱ型」,900011442,11442,7351,91,54,0.5,3972,True,0.0,0,0.1,10.0,0.5,2,5,0.5,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,1822680.45,2967.16,9334.2,857.52,True,0
自律辅助单位·「捷足巡游者」,900011443,11443,7097,79,54,0.5,3502,True,0.0,0,0.1,10.0,0.5,2,3,0.5,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,1759701.15,2575.88,8229.7,857.52,True,0
自律辅助单位·「捷足巡游者」,900011444,11444,7351,91,54,0.5,3972,True,0.0,0,0.1,10.0,0.5,2,5,0.5,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,1822680.45,2967.16,9334.2,857.52,True,0
自律辅助单位·「重装侵袭者Ⅱ型」,900011451,11451,8835,97,54,0.5,3502,True,0.0,0,0.1,10.0,1.0,2,3,0.5,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,2190638.25,3162.79,8229.7,857.52,True,0
自律辅助单位·「重装侵袭者Ⅱ型」,900011452,11452,9088,111,54,0.5,3972,True,0.0,0,0.1,10.0,1.0,2,5,0.5,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,2253369.6,3619.28,9334.2,857.52,True,0
自律战术单位·「护戍盾卫Ω型」,900011453,11453,20548,120,60,0.5,5543,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,5094876.6,3912.73,13026.05,952.8,True,0
自律辅助单位·「重装侵袭者」,900011454,11454,8414,92,54,0.5,3335,True,0.0,0,0.1,10.0,1.0,2,3,0.5,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,2086251.3,2999.76,7837.25,857.52,True,0
自律辅助单位·「重装侵袭者」,900011455,11455,8655,106,54,0.5,3783,True,0.0,0,0.1,10.0,1.0,2,5,0.5,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,2146007.25,3456.25,8890.05,857.52,True,0
自律战术单位·「护戍盾卫」,900011456,11456,20548,120,60,0.5,5543,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,0.0,-0.2,0.0,-0.2,0.2,5094876.6,3912.73,13026.05,952.8,True,0
恶灵,900011461,11461,1240,54,36,0.5,600,True,0.0,0,0.5,2.0,1.0,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,307458.0,1760.73,1410.0,571.68,True,0
恶灵,900011462,11462,1615,54,36,0.5,900,True,0.0,0,0.5,2.0,1.0,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,400439.25,1760.73,2115.0,571.68,True,0
恶灵·蓄能型,900011463,11463,1240,54,36,0.5,600,True,0.0,0,0.5,2.0,1.0,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,307458.0,1760.73,1410.0,571.68,True,0
恶灵·蓄能型,900011464,11464,1615,54,36,0.5,900,True,0.0,0,0.5,2.0,1.0,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,400439.25,1760.73,2115.0,571.68,True,0
游魂,900011471,11471,1123,66,40,0.5,600,True,0.05,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,278447.85,2152.0,1410.0,635.2,True,0
游魂,900011472,11472,1373,66,40,0.5,900,True,0.05,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,340435.35,2152.0,2115.0,635.2,True,0
游魂·蓄能型,900011473,11473,1123,66,40,0.5,600,True,0.05,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,278447.85,2152.0,1410.0,635.2,True,0
游魂·蓄能型,900011474,11474,1373,66,40,0.5,900,True,0.05,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,340435.35,2152.0,2115.0,635.2,True,0
匪祸侵蚀体·恶毒打手,900011481,11481,1123,54,40,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,278447.85,1760.73,1410.0,635.2,True,0
匪祸侵蚀体·恶毒打手,900011482,11482,1373,54,40,0.5,900,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,340435.35,1760.73,2115.0,635.2,True,0
匪祸侵蚀体·贪婪射手,900011491,11491,1123,66,40,0.5,600,True,0.05,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,278447.85,2152.0,1410.0,635.2,True,0
匪祸侵蚀体·贪婪射手,900011492,11492,1373,66,40,0.5,900,True,0.05,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,340435.35,2152.0,2115.0,635.2,True,0
匪祸侵蚀体·狂乱暴徒,900011501,11501,6083,92,50,0.5,2668,True,0.0,0,0.1,10.0,0.5,2,3,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,1508279.85,2999.76,6269.8,794.0,True,0
匪祸侵蚀体·狂乱暴徒,900011502,11502,6301,106,50,0.5,3026,True,0.0,0,0.1,10.0,0.5,2,5,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,1562332.95,3456.25,7111.1,794.0,True,0
匪祸侵蚀体·盛怒恶霸,900011511,11511,8835,88,50,0.5,3502,True,0.0,0,0.1,10.0,1.0,2,3,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,2190638.25,2869.34,8229.7,794.0,True,0
匪祸侵蚀体·盛怒恶霸,900011512,11512,9088,101,50,0.5,3972,True,0.0,0,0.1,10.0,1.0,2,5,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,2253369.6,3293.22,9334.2,794.0,True,0
匪祸侵蚀体·凶心疯汉,900011521,11521,8417,83,45,0.5,4161,True,0.0,0,0.0769,13.0,0.5,2,5,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,2086995.15,2706.31,9778.35,714.6,True,0
匪祸侵蚀体·凶心疯汉,900011522,11522,8736,95,45,0.5,4753,True,0.0,0,0.0769,13.0,0.5,2,5,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,2166091.2,3097.58,11169.55,714.6,True,0
互利型共生以骸群·代号:尼尼微,900011531,11531,21539,120,60,0.5,10028,True,0.0,0,0.0833,12.0,0.5,3,99,0.75,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,5340595.05,3912.73,23565.8,952.8,True,0
偏利型共生以骸群·代号:杰佩托,900011541,11541,26924,144,60,0.5,11031,True,0.0,0,0.0833,12.0,0.5,3,99,0.75,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,6675805.8,4695.28,25922.85,952.8,True,0
骸蜂,900011561,11561,1123,60,43,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,278447.85,1956.37,1410.0,682.84,True,0
骸蜂,900011562,11562,923,60,43,0.5,360,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,228857.85,1956.37,846.0,682.84,True,0
莫尔斯,900011601,11601,9904,83,50,0.5,5253,True,0.0,0,0.1428,7.0,1.0,2,3,0.5,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,2455696.8,2706.31,12344.55,794.0,True,0
离子体·多佩冈亚·莫尔斯,900011602,11602,9904,83,50,0.5,5253,True,0.0,0,0.1428,7.0,1.0,2,3,0.5,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,2455696.8,2706.31,12344.55,794.0,True,0
离子体·多佩冈亚·莫尔斯,900011603,11603,10220,95,50,0.5,5958,True,0.0,0,0.1428,7.0,1.0,2,4,0.5,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,2534049.0,3097.58,14001.3,794.0,True,0
巴罗姆,900011611,11611,10545,102,50,0.5,6256,True,0.0,0,0.0769,13.0,0.5,2,3,0.5,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,2614632.75,3325.82,14701.6,794.0,True,0
离子体·多佩冈亚·巴罗姆,900011612,11612,10545,102,50,0.5,6256,True,0.0,0,0.0769,13.0,0.5,2,3,0.5,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,2614632.75,3325.82,14701.6,794.0,True,0
离子体·多佩冈亚·巴罗姆,900011613,11613,10945,116,50,0.5,7146,True,0.0,0,0.0769,13.0,0.5,2,4,0.5,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,2713812.75,3782.31,16793.1,794.0,True,0
波可娜,900011621,11621,9904,102,50,0.5,5253,True,0.0,0,0.1428,7.0,1.0,2,3,0.5,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,2455696.8,3325.82,12344.55,794.0,True,0
离子体·多佩冈亚·波可娜,900011622,11622,9904,102,50,0.5,5253,True,0.0,0,0.1428,7.0,1.0,2,3,0.5,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,2455696.8,3325.82,12344.55,794.0,True,0
离子体·多佩冈亚·波可娜,900011623,11623,10220,116,50,0.5,5958,True,0.0,0,0.1428,7.0,1.0,2,4,0.5,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,2534049.0,3782.31,14001.3,794.0,True,0
简·杜,900011641,11641,17918,120,60,0.5,5543,True,0.0,0,0.1428,7.0,1.0,3,1,0.75,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,4442768.1,3912.73,13026.05,952.8,True,0
离子体·多佩冈亚·简,900011642,11642,17918,120,60,0.5,5543,True,0.0,0,0.1428,7.0,1.0,3,1,0.75,0.2,0.0,0.0,-0.2,0.0,0.2,0.0,0.0,-0.2,0.0,0.2,0.0,0.0,-0.2,0.0,4442768.1,3912.73,13026.05,952.8,True,0
绞杀藤,900011651,11651,472,63,36,0.5,630,False,0.0,0,0.1538,6.5,0.5,1,99,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,117032.4,2054.18,1480.5,571.68,True,0
绞杀藤,900011652,11652,472,63,36,0.5,630,False,0.0,0,0.1538,6.5,0.5,1,99,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,117032.4,2054.18,1480.5,571.68,True,0
绞杀藤,900011653,11653,472,63,36,0.5,630,False,0.0,0,0.1538,6.5,0.5,1,99,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,117032.4,2054.18,1480.5,571.68,True,0
绞杀藤,900011654,11654,472,63,36,0.5,630,False,0.0,0,0.1538,6.5,0.5,1,99,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,117032.4,2054.18,1480.5,571.68,True,0
绞杀藤,900011661,11661,472,63,36,0.5,630,False,0.0,0,0.1538,6.5,0.5,1,99,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,117032.4,2054.18,1480.5,571.68,True,0
盗洞暴徒·蛮横力士,900011671,11671,21811,132,60,0.5,5543,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,5408037.45,4304.01,13026.05,952.8,True,0
弗瑟尔,900011681,11681,1238,76,40,0.5,662,True,0.0,0,0.1538,6.5,0.25,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,306962.1,2478.06,1555.7,635.2,True,0
弗瑟尔,900011682,11682,1514,76,40,0.5,992,True,0.0,0,0.1538,6.5,0.25,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,375396.3,2478.06,2331.2,635.2,True,0
弗瑟尔·蓄能型,900011683,11683,1238,76,40,0.5,662,True,0.0,0,0.1538,6.5,0.25,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,306962.1,2478.06,1555.7,635.2,True,0
弗瑟尔·蓄能型,900011684,11684,1514,76,40,0.5,992,True,0.0,0,0.1538,6.5,0.25,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,375396.3,2478.06,2331.2,635.2,True,0
索迪代斯,900011691,11691,1238,76,40,0.5,662,True,0.05,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,306962.1,2478.06,1555.7,635.2,True,0
索迪代斯,900011692,11692,1514,69,40,0.5,992,True,0.05,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,375396.3,2249.82,2331.2,635.2,True,0
索迪代斯·蓄能型,900011693,11693,1238,69,40,0.5,662,True,0.05,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,306962.1,2249.82,1555.7,635.2,True,0
索迪代斯·蓄能型,900011694,11694,1514,69,40,0.5,992,True,0.05,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,375396.3,2249.82,2331.2,635.2,True,0
「霸主侵蚀体·庞培」,900011701,11701,11129,132,60,0.5,6762,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,0.0,-0.2,0.4,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,2759435.55,4304.01,15890.7,952.8,True,0
「霸主侵蚀体·庞培」,900011702,11702,18925,158,60,0.5,6762,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,0.0,-0.2,0.4,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,4692453.75,5151.76,15890.7,952.8,True,0
「霸主侵蚀体·庞培」,900011703,11703,20728,173,60,0.5,7084,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,0.0,-0.2,0.4,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,5139507.6,5640.86,16647.4,952.8,True,0
色雷斯人,900011711,11711,18456,132,60,0.5,5820,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,4576165.2,4304.01,13677.0,952.8,True,0
地精·蓄能型,900011721,11721,6759,92,45,0.5,3335,True,0.0,0,0.1,10.0,0.5,2,3,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,1675894.05,2999.76,7837.25,714.6,True,0
地精·蓄能型,900011722,11722,7001,106,45,0.5,3783,True,0.0,0,0.1,10.0,0.5,2,5,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,1735897.95,3456.25,8890.05,714.6,True,0
铁道地精·蓄能型,900011723,11723,6759,92,45,0.5,3335,True,0.0,0,0.1,10.0,0.5,2,3,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,1675894.05,2999.76,7837.25,714.6,True,0
铁道地精·蓄能型,900011724,11724,7001,106,45,0.5,3783,True,0.0,0,0.1,10.0,0.5,2,5,0.5,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,1735897.95,3456.25,8890.05,714.6,True,0
离子体·法布提,900011731,11731,8417,92,45,0.5,4161,True,0.0,0,0.0769,13.0,0.5,2,5,0.5,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,2086995.15,2999.76,9778.35,714.6,True,0
离子体·法布提,900011732,11732,8736,106,45,0.5,4753,True,0.0,0,0.0769,13.0,0.5,2,5,0.5,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,2166091.2,3456.25,11169.55,714.6,True,0
离子体·塔纳托斯,900011741,11741,7951,97,45,0.5,2451,True,0.0,0,0.1,10.0,1.0,2,3,0.5,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,1971450.45,3162.79,5759.85,714.6,True,0
离子体·塔纳托斯,900011742,11742,8179,111,45,0.5,2781,True,0.0,0,0.1,10.0,1.0,2,5,0.5,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,2027983.05,3619.28,6535.35,714.6,True,0
离子体·杜拉罕,900011751,11751,7097,97,58,0.5,3502,True,0.0,0,0.1,10.0,0.5,2,3,0.5,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,1759701.15,3162.79,8229.7,921.04,True,0
离子体·杜拉罕,900011752,11752,7351,111,58,0.5,3972,True,0.0,0,0.1,10.0,0.5,2,5,0.5,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,1822680.45,3619.28,9334.2,921.04,True,0
轰击猎兵,900011761,11761,1235,59,40,0.5,630,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,306218.25,1923.76,1480.5,635.2,True,0
轰击猎兵,900011762,11762,1510,59,40,0.5,945,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,374404.5,1923.76,2220.75,635.2,True,0
掷弹猎兵,900011763,11763,1235,59,40,0.5,630,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,306218.25,1923.76,1480.5,635.2,True,0
掷弹猎兵,900011764,11764,1510,59,40,0.5,945,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,374404.5,1923.76,2220.75,635.2,True,0
离子体·瑟托迪亚,900011771,11771,1123,60,36,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,278447.85,1956.37,1410.0,571.68,True,0
离子体·瑟托迪亚,900011772,11772,1373,60,36,0.5,900,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,340435.35,1956.37,2115.0,571.68,True,0
离子体·纳塞勒亚,900011781,11781,1123,60,36,0.5,600,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,278447.85,1956.37,1410.0,571.68,True,0
离子体·纳塞勒亚,900011782,11782,1373,60,36,0.5,900,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,340435.35,1956.37,2115.0,571.68,True,0
伐木机,900011791,11791,20134,144,60,0.5,6097,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,0.2,0.0,-0.2,0.0,0.0,0.2,0.0,-0.2,0.0,0.0,0.2,0.0,-0.2,0.0,0.0,4992225.3,4695.28,14327.95,952.8,True,0
牲鬼·布林格,900011811,11811,12189,144,60,0.5,7084,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,-0.2,0.0,0.0,0.2,0.0,-0.2,0.0,0.0,0.2,0.0,-0.2,0.0,0.0,0.2,0.0,3022262.55,4695.28,16647.4,952.8,True,0
牲鬼·布林格,900011812,11812,20728,173,60,0.5,7084,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,-0.2,0.0,0.0,0.2,0.0,-0.2,0.0,0.0,0.2,0.0,-0.2,0.0,0.0,0.2,0.0,5139507.6,5640.86,16647.4,952.8,True,0
绽壳虫,900011814,11814,562,42,36,0.5,300,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,139347.9,1369.46,705.0,571.68,True,0
绽壳虫,900011815,11815,562,42,36,0.5,300,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,139347.9,1369.46,705.0,571.68,True,0
绽壳虫,900011816,11816,562,42,36,0.5,300,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,139347.9,1369.46,705.0,571.68,True,0
牲鬼·布林格,900011818,11818,20728,173,60,0.5,7084,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,-0.2,0.0,0.0,0.2,0.0,-0.2,0.0,0.0,0.2,0.0,-0.2,0.0,0.0,0.2,0.0,5139507.6,5640.86,16647.4,952.8,True,0
牲鬼·布林格,900011819,11819,12189,144,60,0.5,7084,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,-0.2,0.0,0.0,0.2,0.0,-0.2,0.0,0.0,0.2,0.0,-0.2,0.0,0.0,0.2,0.0,3022262.55,4695.28,16647.4,952.8,True,0
阿佩卡(寄生态),900011821,11821,1550,79,36,0.5,660,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,384322.5,2575.88,1551.0,571.68,True,0
阿佩卡(寄生态),900011822,11822,1895,79,36,0.5,990,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,469865.25,2575.88,2326.5,571.68,True,0
提尔锋(寄生态),900011831,11831,1550,79,36,0.5,660,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,384322.5,2575.88,1551.0,571.68,True,0
提尔锋(寄生态),900011832,11832,1895,79,36,0.5,990,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,469865.25,2575.88,2326.5,571.68,True,0
刺椎原虫,900011851,11851,387,72,36,0.5,660,False,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,95956.65,2347.64,1551.0,571.68,True,0
刺椎原虫,900011852,11852,474,72,36,0.5,990,False,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,0.0,0.0,0.0,0.0,-0.2,117528.3,2347.64,2326.5,571.68,True,0
恶名·冥宁芙,900011861,11861,26787,144,60,0.5,6097,True,0.0,0,0.1428,7.0,1.0,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,6641836.65,4695.28,14327.95,952.8,True,0
恶名·庞培,900011881,11881,20728,173,60,0.5,7084,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,0.0,-0.2,0.4,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,0.0,-0.2,0.2,0.0,0.0,5139507.6,5640.86,16647.4,952.8,True,0
自律强袭单位·「提丰·破坏者型」,900011891,11891,23630,144,60,0.5,6097,True,0.0,0,0.0833,12.0,1.0,3,5,0.75,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,5859058.5,4695.28,14327.95,952.8,True,0
恶名·死路屠夫,900011901,11901,20662,144,60,0.5,6590,True,0.0,0,0.0833,12.0,0.5,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,5123142.9,4695.28,15486.5,952.8,True,0
特勤护卫,900011911,11911,1348,76,36,0.5,660,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,334236.6,2478.06,1551.0,571.68,True,0
特勤护卫,900011912,11912,1348,76,36,0.5,660,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,334236.6,2478.06,1551.0,571.68,True,0
特勤护卫,900011913,11913,1348,76,36,0.5,660,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,334236.6,2478.06,1551.0,571.68,True,0
特勤护卫,900011914,11914,1348,76,36,0.5,660,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,334236.6,2478.06,1551.0,571.68,True,0
特勤护卫,900011915,11915,1648,76,36,0.5,990,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,408621.6,2478.06,2326.5,571.68,True,0
特勤护卫,900011916,11916,1648,76,36,0.5,990,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,408621.6,2478.06,2326.5,571.68,True,0
特勤护卫,900011917,11917,1648,76,36,0.5,990,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,408621.6,2478.06,2326.5,571.68,True,0
特勤护卫,900011918,11918,1648,76,36,0.5,990,True,0.0,0,0.1538,6.5,0.5,1,1,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,0.0,-0.2,0.0,-0.2,0.0,408621.6,2478.06,2326.5,571.68,True,0
骇鸟,900011941,11941,21502,144,60,0.5,6097,True,0.0,0,0.1428,7.0,1.0,3,5,0.75,0.2,0.0,-0.2,0.0,0.0,0.2,0.0,-0.2,0.0,0.0,0.2,0.0,-0.2,0.0,0.0,5331420.9,4695.28,14327.95,952.8,True,0
帕里库斯,900011951,11951,20548,144,60,0.5,4268,True,0.0,0,0.0833,12.0,1.0,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,5094876.6,4695.28,10029.8,952.8,True,0
帕里库斯,900011952,11952,20548,144,60,0.5,4268,True,0.0,0,0.0833,12.0,1.0,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,5094876.6,4695.28,10029.8,952.8,True,0
特佩什,900011961,11961,21297,144,60,0.5,7926,True,0.0,0,0.0833,12.0,0.25,3,5,0.75,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,-0.2,0.0,0.0,0.0,-0.2,5280591.15,4695.28,18626.1,952.8,True,0
特战强袭轰击者,900011971,11971,18616,144,60,0.5,6097,True,0.0,0,0.0833,12.0,0.25,3,5,0.75,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,0.0,0.2,-0.2,0.0,0.0,4615837.2,4695.28,14327.95,952.8,True,0
暗渊惩戒者,900011981,11981,22398,144,60,0.5,6097,True,0.0,0,0.1428,7.0,1.0,3,1,0.75,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,5553584.1,4695.28,14327.95,952.8,True,0
离子体·多佩冈亚·暗渊惩戒者,900011982,11982,22398,144,60,0.5,6097,True,0.0,0,0.1428,7.0,1.0,3,1,0.75,0.4,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,0.2,-0.2,0.0,-0.2,0.0,5553584.1,4695.28,14327.95,952.8,True,0
暗渊惩戒者,900011983,11983,22398,144,60,0.5,6097,True,0.0,0,0.1428,7.0,1.0,3,1,0.75,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,5553584.1,4695.28,14327.95,952.8,True,0
================================================
FILE: zsim/data/enemy_adjustment.csv
================================================
ID,生命值,攻击力,失衡值上限,防御力,异常积蓄值上限,SubID
20101,-0.2,-0.5,-0.2,0,-0.2,2010101
20101,-0.2,-0.5,-0.2,0,-0.2,2010102
20101,-0.2,-0.5,-0.2,0,-0.2,2010103
20102,-0.35,-0.65,-0.35,0,-0.35,2010201
20102,-0.35,-0.65,-0.35,0,-0.35,2010202
20102,-0.35,-0.65,-0.35,0,-0.35,2010203
20103,-0.5,-0.8,-0.5,0,-0.5,2010301
20103,-0.5,-0.8,-0.5,0,-0.5,2010302
20103,-0.5,-0.8,-0.5,0,-0.5,2010303
20110,0.3,0.15,0.15,0,0.3,2011003
20111,0.0,-0.3,0.0,0,0.0,2011101
20111,0.0,-0.25,0.0,0,0.0,2011102
20111,0.0,-0.2,0.0,0,0.0,2011103
20112,0.6,0.2,0.0,0,0.0,2011202
20113,0.25,0.0,0.1,0,0.0,2011303
20114,0.0,-0.3,0.0,0,0.25,2011401
20114,0.0,-0.3,0.0,0,0.25,2011402
20114,0.0,-0.3,0.0,0,0.25,2011403
20115,0.25,0.0,0.1,0,0.0,2011502
20116,-0.2,-0.15,-0.4,0,0.0,2011601
20116,-0.2,-0.15,-0.35,0,0.0,2011602
20116,-0.45,-0.15,-0.5,0,0.0,2011603
20117,-0.7,0.0,-0.5,0,0.0,2011701
20118,-0.1,-0.1,0.0,0,0.0,2011801
20118,-0.1,-0.1,0.0,0,0.0,2011802
20118,-0.1,-0.1,0.0,0,0.0,2011803
20119,-0.1,-0.1,0.0,0,0.0,2011903
20120,-0.35,0.0,0.0,0,0.0,2012003
20121,0.0,0.0,-0.35,0,0.0,2012103
20501,0.8,0.0,-0.35,0,0.0,2050101
20502,0.0,0.0,0.0,0,0.0,2050201
20502,0.0,0.0,0.0,0,0.0,2050202
20502,0.6,0.33,0.06,0,0.0,2050203
20521,0.0,-0.2,0.0,0,0.0,2050211
20522,0.07,-0.1,0.05,0,0.1,2050212
20523,0.07,-0.1,0.05,0,0.1,2050213
20524,1.28,0.08,0.12,0,0.24,2050214
20503,1.0,0.4,0.11,0,0.0,2050301
20503,1.0,0.4,0.11,0,0.0,2050302
20503,1.35,0.43,0.15,0,0.0,2050303
20531,0.0,-0.1,0.0,0,0.0,2050311
20532,0.21,0.18,0.05,0,0.1,2050312
20533,0.21,0.18,0.05,0,0.1,2050313
20534,1.39,0.37,0.12,0,0.24,2050314
20504,1.35,0.43,0.15,0,0.0,2050401
20504,1.35,0.43,0.15,0,0.0,2050402
20504,3.7,0.71,0.27,0,0.0,2050403
20541,0.0,0.0,0.0,0,0.0,2050411
20542,0.39,0.2,0.04,0,0.08,2050412
20543,0.39,0.2,0.04,0,0.08,2050413
20544,1.44,0.41,0.12,0,0.24,2050414
20545,1.44,0.41,0.12,0,0.24,2050415
20546,3.0,0.6,0.19,0,0.38,2050416
20551,0.0,0.0,0.0,0,0.0,2050511
20552,0.42,0.21,0.05,0,0.1,2050512
20553,0.42,0.21,0.05,0,0.1,2050513
20554,1.45,0.45,0.11,0,0.22,2050514
20555,1.45,0.45,0.11,0,0.22,2050515
20556,3.15,0.75,0.18,0,0.36,2050516
20561,0.0,0.0,0.0,0,0.0,2050611
20562,0.54,0.4,0.04,0,0.08,2050612
20563,0.54,0.4,0.04,0,0.08,2050613
20564,1.54,0.59,0.1,0,0.2,2050614
20565,1.54,0.59,0.1,0,0.2,2050615
20566,3.51,0.79,0.17,0,0.34,2050616
20571,0.0,0.0,0.0,0,0.0,2050701
20572,0.2,0.1,0.05,0,0.05,2050702
20573,0.0,0.0,0.0,0,0.0,2050703
20574,0.1,0.0,0.04,0,0.04,2050704
20575,0.39,0.1,0.04,0,0.04,2050705
20576,0.72,0.21,0.12,0,0.12,2050706
20581,0.0,0.0,0.0,0,0.0,2050801
20582,0.39,0.1,0.04,0,0.04,2050802
20583,0.39,0.1,0.04,0,0.04,2050803
20584,0.72,0.21,0.12,0,0.12,2050804
20585,0.72,0.21,0.12,0,0.12,2050805
20586,1.8,0.3,0.19,0,0.19,2050806
20601,0.3,0.0,-0.5,0,0.0,2060101
20601,0.2,0.1,-0.5,0,0.0,2060102
20601,0.2,0.1,-0.5,0,0.0,2060103
20602,0.4,0.0,-0.5,0,0.0,2060201
20602,0.3,0.0,-0.5,0,0.0,2060202
20602,0.3,0.15,-0.5,0,0.0,2060203
20603,-0.2,0.0,-0.5,0,0.0,2060301
20603,-0.25,0.1,-0.5,0,0.0,2060302
20603,-0.25,0.1,-0.5,0,0.0,2060303
20604,0.0,0.0,-0.5,0,0.0,2060401
20604,-0.1,0.0,-0.5,0,0.0,2060402
20604,-0.1,0.0,-0.5,0,0.0,2060403
20605,0.15,0.0,-0.3,0,0.0,2060501
20605,0.05,0.1,-0.3,0,0.0,2060502
20605,0.05,0.1,-0.3,0,0.0,2060503
20606,0.5,0.0,-0.3,0,0.0,2060601
20606,0.4,0.0,-0.3,0,0.0,2060602
20606,0.4,0.0,-0.3,0,0.0,2060603
20607,0.3,0.0,-0.5,0,0.0,2060701
20607,0.3,0.1,-0.5,0,0.0,2060702
20607,0.2,0.1,-0.5,0,0.0,2060703
20671,0.98,0.45,0.02,0,0.04,2060711
20672,1.95,0.89,0.04,0,0.08,2060712
20673,1.95,0.89,0.04,0,0.08,2060713
20674,3.9,1.0,0.1,0,0.2,2060714
20675,3.9,1.0,0.1,0,0.2,2060715
20676,9.1,1.26,0.17,0,0.34,2060716
20608,0.25,0.0,-0.5,0,0.0,2060801
20608,0.2,0.0,-0.6,0,0.0,2060802
20608,0.2,0.0,-0.6,0,0.0,2060803
20609,-0.25,0.0,-0.5,0,0.0,2060901
20609,-0.7,0.0,-0.5,0,0.0,2060902
20609,-0.7,0.0,-0.5,0,0.0,2060903
20610,0.0,0.0,-0.5,0,0.0,2061001
20610,-0.45,0.0,-0.5,0,0.0,2061002
20610,-0.45,0.0,-0.5,0,0.0,2061003
20611,0.15,0.0,-0.6,0,0.0,2061101
20611,0.05,0.0,-0.6,0,0.0,2061102
20611,0.0,0.0,-0.6,0,0.0,2061103
20612,0.3,0.0,-0.6,0,0.0,2061201
20612,0.2,-0.15,-0.6,0,0.0,2061202
20612,0.2,-0.2,-0.6,0,0.0,2061203
20613,0.2,0.0,-0.5,0,0.0,2061301
20613,0.0,-0.1,-0.5,0,0.0,2061302
20613,0.0,-0.15,-0.5,0,0.0,2061303
20614,0.2,0.0,-0.5,0,0.0,2061401
20614,0.0,-0.1,-0.5,0,0.0,2061402
20614,0.0,-0.2,-0.5,0,0.0,2061403
20615,0.0,0.0,-0.5,0,0.0,2061501
20615,-0.4,-0.15,-0.7,0,0.0,2061502
20615,-0.55,-0.3,-0.7,0,0.0,2061503
20616,0.0,-0.2,-0.5,0,0.0,2061601
20616,-0.35,-0.35,-0.7,0,0.0,2061602
20616,-0.35,-0.45,-0.7,0,0.0,2061603
20701,-0.3,0.4,-0.15,0,0.0,2070101
20701,-0.3,0.4,-0.15,0,0.0,2070102
20701,0.0,0.4,0.0,0,0.0,2070103
20702,-0.1,0.4,0.0,0,0.0,2070201
20702,-0.1,0.4,0.0,0,0.0,2070202
20702,0.0,0.4,0.0,0,0.0,2070203
20704,1.2,0.0,-0.5,0,0.0,2070401
20704,1.2,0.0,-0.5,0,0.0,2070402
20901,0.3,0.0,0.0,0,0.0,2090101
20901,0.3,0.0,0.0,0,0.0,2090102
20901,0.3,0.0,0.0,0,0.0,2090103
20902,0.3,0.0,0.0,0,0.0,2090201
20902,0.3,0.0,0.0,0,0.0,2090202
20902,0.3,0.0,0.0,0,0.0,2090203
20904,0.07,-0.07,-0.03,0,0.0,2090401
20904,0.07,-0.07,-0.03,0,0.0,2090402
20904,0.07,-0.07,-0.03,0,0.0,2090403
20905,0.3,0.0,0.3,0,0.0,2090501
20905,0.3,0.0,0.3,0,0.0,2090502
20905,0.3,0.0,0.3,0,0.0,2090503
20906,0.3,0.0,0.0,0,0.0,2090601
20906,0.3,0.0,0.0,0,0.0,2090602
20906,0.3,0.0,0.0,0,0.0,2090603
20907,0.45,0.0,0.0,0,0.0,2090701
20907,0.45,0.0,0.0,0,0.0,2090702
20907,0.45,0.0,0.0,0,0.0,2090703
20908,0.35,0.0,0.0,0,0.0,2090801
20909,0.5,0.0,0.0,0,0.0,2090901
20910,0.4,0.0,0.0,0,0.0,2091001
20910,0.4,0.0,0.0,0,0.0,2091002
20910,0.4,0.0,0.0,0,0.0,2091003
20912,0.3,0.0,0.15,0,0.0,2091201
20912,0.3,0.0,0.15,0,0.0,2091202
20912,0.3,0.0,0.15,0,0.0,2091203
20913,0.4,0.0,0.15,0,0.0,2091301
20913,0.4,0.0,0.15,0,0.0,2091302
20913,0.4,0.0,0.15,0,0.0,2091303
20914,0.3,0.0,0.0,0,0.0,2091401
20914,0.3,0.0,0.0,0,0.0,2091402
20914,0.3,0.0,0.0,0,0.0,2091403
20915,0.0,0.0,0.0,0,0.0,2091501
20915,0.0,0.0,0.0,0,0.0,2091502
20915,0.0,0.0,0.0,0,0.0,2091503
20916,0.3,0.0,0.0,0,0.0,2091601
20916,0.3,0.0,0.0,0,0.0,2091602
20916,0.3,0.0,0.0,0,0.0,2091603
20917,0.3,0.0,0.0,0,0.0,2091701
20917,0.3,0.0,0.0,0,0.0,2091702
20917,0.3,0.0,0.0,0,0.0,2091703
20918,0.3,0.0,0.2,0,0.0,2091801
20918,0.3,0.0,0.2,0,0.0,2091802
20918,0.3,0.0,0.2,0,0.0,2091803
20919,0.5,0.0,0.0,0,0.0,2091901
20919,0.5,0.0,0.0,0,0.0,2091902
20919,0.5,0.0,0.0,0,0.0,2091903
20920,0.5,0.0,0.2,0,0.0,2092001
20920,0.5,0.0,0.2,0,0.0,2092002
20920,0.5,0.0,0.2,0,0.0,2092003
20921,0.6,0.0,0.0,0,0.0,2092101
20921,0.6,0.0,0.0,0,0.0,2092102
20921,0.6,0.0,0.0,0,0.0,2092103
20922,1.0,0.0,0.0,0,0.0,2092201
20923,1.2,0.0,0.0,0,0.0,2092301
20924,0.45,0.0,0.0,0,0.0,2092401
20925,1.0,0.0,0.0,0,0.0,2092501
20926,1.1,0.0,0.0,0,0.0,2092601
20927,1.15,0.0,0.0,0,0.0,2092701
20928,0.4,0.0,0.0,0,0.0,2092801
20929,0.7,0.0,0.0,0,0.0,2092901
20930,1.3,0.0,0.0,0,0.0,2093001
21401,0.33,0.0,0.9,0,0.85,2140101
21401,0.33,0.0,0.9,0,0.85,2140102
21401,0.33,0.0,0.9,0,0.85,2140103
21402,1.23,0.0,1.48,0,1.55,2140201
21402,1.23,0.0,1.48,0,1.55,2140202
21402,1.23,0.0,1.48,0,1.55,2140203
21901,0.7,0.0,0.0,0,0.0,2190101
21901,0.5,0.0,0.0,0,0.0,2190102
21901,0.35,0.0,0.0,0,0.0,2190103
21902,0.7,-0.3,0.0,0,0.0,2190201
21902,0.5,-0.3,0.0,0,0.0,2190202
21902,0.35,-0.3,0.0,0,0.0,2190203
22101,0.8,0.0,0.8,0,0.0,2210101
22101,0.8,0.0,0.4,0,0.0,2210102
22101,0.8,0.0,0.4,0,0.0,2210103
22301,0.1,0.0,0.05,0,0.0,2230101
22301,0.1,0.0,0.05,0,0.0,2230102
22301,0.1,0.0,0.05,0,0.0,2230103
22302,0.2,0.0,0.1,0,0.0,2230201
22302,0.2,0.0,0.1,0,0.0,2230202
22302,0.2,0.0,0.1,0,0.0,2230203
22303,0.3,0.0,0.15,0,0.0,2230301
22303,0.3,0.0,0.15,0,0.0,2230302
22303,0.3,0.0,0.15,0,0.0,2230303
22304,0.4,0.0,0.2,0,0.0,2230401
22304,0.4,0.0,0.2,0,0.0,2230402
22304,0.4,0.0,0.2,0,0.0,2230403
22305,0.5,0.0,0.25,0,0.0,2230501
22305,0.5,0.0,0.25,0,0.0,2230502
22305,0.5,0.0,0.25,0,0.0,2230503
22306,0.5,0.25,0.25,0,0.25,2230601
22306,0.5,0.25,0.25,0,0.25,2230602
22306,0.5,0.25,0.25,0,0.25,2230603
22307,0.75,0.5,0.35,0,0.35,2230701
22307,0.75,0.5,0.35,0,0.35,2230702
22307,0.75,0.5,0.35,0,0.35,2230703
22308,1.0,0.5,0.5,0,0.5,2230801
22308,1.0,0.5,0.5,0,0.5,2230802
22308,1.0,0.5,0.5,0,0.5,2230803
22310,0.0,-0.25,0.0,0,0.0,2231000
22311,0.25,0.0,0.15,0,0.15,2231001
22312,0.5,0.15,0.25,0,0.25,2231002
22313,0.6,0.15,0.25,0,0.25,2231003
22314,0.75,0.25,0.25,0,0.25,2231004
22315,1.0,0.5,0.5,0,0.5,2231005
22316,1.25,0.5,0.5,0,0.5,2231006
22317,1.5,0.6,0.5,0,0.5,2231007
22318,1.75,0.7,0.5,0,0.5,2231008
22319,2.0,0.8,0.5,0,0.5,2231009
22320,2.25,0.9,0.5,0,0.5,2231010
22321,2.5,1.0,0.5,0,0.5,2231011
22322,2.75,1.0,0.5,0,0.5,2231012
22323,3.0,1.0,0.5,0,0.5,2231013
22324,3.5,1.0,0.5,0,0.5,2231014
22325,4.0,1.0,0.5,0,0.5,2231015
22326,4.0,1.0,1.0,0,1.0,2231016
22401,0.6,-0.25,0.0,0,0.0,2240101
22402,0.3,-0.3,0.0,0,0.0,2240102
22403,0.15,-0.35,0.0,0,0.0,2240103
22404,0.0,-0.35,0.0,0,0.0,2240104
22405,0.7,-0.3,0.0,0,0.0,2240105
22406,0.92,-0.25,0.0,0,0.0,2240106
22407,0.56,-0.3,0.0,0,0.0,2240107
22408,0.26,-0.35,0.0,0,0.0,2240108
22409,0.2,-0.35,0.0,0,0.0,2240109
22410,1.0,-0.3,0.0,0,0.0,2240110
22411,0.92,-0.35,0.0,0,0.0,2240111
22412,0.56,-0.35,0.0,0,0.0,2240112
22413,0.56,-0.3,0.0,0,0.0,2240113
22414,0.72,-0.3,0.0,0,0.0,2240114
22415,0.56,-0.35,0.0,0,0.0,2240115
22416,0.75,-0.3,0.0,0,0.0,2240116
22417,0.55,-0.35,0.0,0,0.0,2240117
22418,0.35,-0.35,0.0,0,0.0,2240118
22419,0.75,-0.3,0.0,0,0.0,2240119
22420,0.75,-0.3,0.0,0,0.0,2240120
22421,1.3,-0.3,0.0,0,0.0,2240121
22422,1.46,-0.3,0.0,0,0.0,2240122
22423,0.75,-0.35,0.0,0,0.0,2240123
22424,0.75,-0.3,0.0,0,0.0,2240124
22425,0.45,-0.35,0.0,0,0.0,2240125
22426,1.0,-0.3,0.0,0,0.0,2240126
22427,0.61,-0.3,0.0,0,0.0,2240127
22428,0.885,-0.3,0.0,0,0.0,2240128
22801,0.07,0.05,0.0,0,0.0,2280101
22802,0.24,0.06,0.06,0,0.0,2280201
22803,0.32,0.07,0.07,0,0.02,2280301
22804,0.65,0.07,0.07,0,0.05,2280401
22805,0.85,0.08,0.08,0,0.07,2280501
22806,0.95,0.08,0.08,0,0.13,2280601
22807,-0.5,-0.33,-0.12,0,-0.31,2280701
22808,1.07,0.1,0.08,0,0.15,2280801
23001,0.3,0.0,0.5,0,0.0,2300101
23002,0.15,0.0,0.5,0,0.0,2300201
23003,1.0,0.0,0.5,0,-0.3,2300301
23003,-0.25,0.0,0.5,0,-0.3,2300302
23003,0.0,0.0,0.5,0,-0.3,2300303
23004,-0.3,0.0,-0.15,0,0.0,2300401
23005,0.0,0.0,-0.2,0,0.0,2300501
23005,0.0,0.0,0.25,0,0.0,2300502
23005,0.0,0.0,0.25,0,0.0,2300503
23006,-0.3,0.0,0.5,0,0.0,2300601
23101,0.75,0.0,0.0,0,0.0,2310101
23101,0.75,0.0,0.0,0,0.0,2310102
23101,0.75,0.0,0.0,0,0.0,2310103
23102,1.0,0.0,0.0,0,0.0,2310201
23102,1.0,0.0,0.0,0,0.0,2310202
23102,1.0,0.0,0.0,0,0.0,2310203
23103,1.5,0.0,0.0,0,0.0,2310301
23103,1.5,0.0,0.0,0,0.0,2310302
23103,1.5,0.0,0.0,0,0.0,2310303
23104,0.5,0.0,0.0,0,0.0,2310401
23104,0.75,0.0,0.0,0,0.0,2310402
23104,0.75,0.0,0.0,0,0.0,2310403
23105,0.0,-0.75,-0.5,0,0.0,2310501
23105,0.0,-0.8,-0.6,0,0.0,2310502
23105,0.0,-0.8,-0.6,0,0.0,2310503
23106,0.25,-0.5,-0.5,0,0.0,2310601
23106,0.25,-0.6,-0.6,0,0.0,2310602
23106,0.25,-0.6,-0.6,0,0.0,2310603
23107,0.5,-0.3,-0.5,0,0.0,2310701
23107,0.6,-0.5,-0.6,0,0.0,2310702
23107,0.6,-0.5,-0.6,0,0.0,2310703
23108,0.25,-0.3,-0.5,0,0.0,2310801
23108,0.6,-0.5,-0.6,0,0.0,2310802
23108,0.6,-0.5,-0.6,0,0.0,2310803
29101,0.4,0.0,0.0,0,0.0,2910101
29101,0.2,0.0,0.0,0,0.0,2910102
29101,0.0,0.0,0.0,0,0.0,2910103
29301,-0.15,0.0,0.33,0,0.33,2930101
29302,0.07,0.0,0.67,0,0.33,2930201
29303,0.27,0.0,1.0,0,0.33,2930301
29304,0.38,-0.1,1.0,0,0.33,2930401
29305,0.48,-0.5,1.0,0,0.33,2930501
29306,0.3,0.0,1.0,0,0.33,2930601
29312,0.17,0.0,0.33,0,0.33,2931201
29313,0.37,0.0,0.67,0,0.33,2931301
29314,0.48,0.0,1.0,0,0.33,2931401
29315,0.58,-0.2,1.0,0,0.33,2931501
29316,0.4,0.0,1.0,0,0.33,2931601
205101,0.0,0.0,0.0,0,0.0,20510101
205102,0.3,0.086,0.03,0,0.06,20510102
205103,0.3,0.086,0.03,0,0.06,20510103
205104,1.78,0.36,0.08,0,0.16,20510104
205105,1.78,0.36,0.08,0,0.16,20510105
205106,5.2,0.4,0.13,0,0.26,20510106
205111,2.59,0.345,0.13,0,0.26,20511101
205121,0.0,0.0,0.0,0,0.0,20512101
205122,0.49,0.2,0.03,0,0.06,20512102
205123,0.49,0.2,0.03,0,0.06,20512103
205124,2.1,0.36,0.08,0,0.16,20512104
205125,2.1,0.36,0.08,0,0.16,20512105
205126,6.0,0.5,0.13,0,0.26,20512106
205131,0.0,0.0,0.0,0,0.0,20513101
205132,0.89,0.08,0.03,0,0.06,20513102
205133,0.89,0.08,0.03,0,0.06,20513103
205134,2.95,0.24,0.08,0,0.16,20513104
205135,2.95,0.24,0.08,0,0.16,20513105
205136,7.03,0.39,0.13,0,0.26,20513106
================================================
FILE: zsim/data/enemy_attack_action.csv
================================================
ID,tag,description,hit,duration,cd,hit_list,blockable,interruption_level_list,effect_radius_list,stoppable,hit_type
0,攻击的tag,攻击的描述,命中次数,动作时长,动作内置CD,各次攻击时间点,各次攻击能否被格挡,各次攻击的打断系数,各次攻击的作用范围,是否能被打断,攻击类型
1,default_enemy_attack_mode_a,默认平A模式:1次轻攻击,1,60,300,[30],True,,,True,Light
2,default_enemy_attack_mode_b,默认平A模式:2次攻击,2,90,500,"[30,70]",True,,,True,Chain
3,default_enemy_attack_mode_c,默认平A模式:3次攻击,3,150,700,"[30, 70, 110]",True,,,True,Chain
4,default_enemy_attack_mode_d,默认平A模式:1次重攻击,1,60,300,[30],True,,,True,Heavy
5,default_enemy_attack_mode_e,默认平A模式:5次连续攻击,5,250,900,"[30,80,130,180,230]",True,,,True,Chain
================================================
FILE: zsim/data/enemy_attack_method.csv
================================================
ID,method_name,discription,action_set,action_rate,rest_tick
0,default_method_0,默认攻击策略(固定间隔),1,1,600
1,default_method_1,默认攻击策略(随机),1|2|3,0.3|0.2|0.1,300
2,default_method_2,默认共计策略(固定间隔抛出重攻击),4,1,600
3,default_method_3,默认共计策略(固定时间抛出连续攻击),5,1,1200
================================================
FILE: zsim/data/equip_set_2pc.csv
================================================
set_ID,HP%,ATK%,DEF%,IMP%,oHP%,oATK%,oDEF%,oIMP%,Crit_Rate,Crit_DMG,Regen%,Regen,pen%,Get_ratio,Anomaly_Mastery,Anomaly_Proficiency,ICE_DMG_bonus,FIRE_DMG_bonus,ELECTRIC_DMG_bonus,PHY_DMG_bonus,ETHER_DMG_bonus
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
极地重金属,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,0,0,0,0
獠牙重金属,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,0
雷暴重金属,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,0,0
混沌重金属,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1
炎狱重金属,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,0,0,0
原始朋克,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
混沌爵士,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,0,0,0,0,0
摇摆爵士,0,0,0,0,0,0,0,0,0,0,0.2,0,0,0,0,0,0,0,0,0,0
灵魂摇滚,0,0,0.16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
激素朋克,0,0.1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
自由蓝调,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,0,0,0,0,0
震星迪斯科,0,0,0,0.06,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
河豚电音,0,0,0,0,0,0,0,0,0,0,0,0,0.08,0,0,0,0,0,0,0,0
啄木鸟电音,0,0,0,0,0,0,0,0,0.08,0,0,0,0,0,0,0,0,0,0,0,0
折枝剑歌,0,0,0,0,0,0,0,0,0,0.16,0,0,0,0,0,0,0,0,0,0,0
静听嘉音,0,0.1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
如影相随,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
法厄同之歌,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.08,0,0,0,0,0,0
云岿如我,0.1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
月光骑士颂,0,0,0,0,0,0,0,0,0,0,0.2,0,0,0,0,0,0,0,0,0,0
拂晓生花,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
================================================
FILE: zsim/data/skill.csv
================================================
CID,name,CN_TriggerLevel,skill_tag,CN_skill_tag,skill_text,INSTRUCTION,comment,damage_ratio,damage_ratio_growth,D_LEVEL12,D_LEVEL14,D_LEVEL16,stun_ratio,stun_ratio_growth,S_LEVEL12,S_LEVEL14,S_LEVEL16,sp_threshold,sp_consume,sp_recovery,adrenaline_threshold,adrenaline_consume,adrenaline_recovery,fever_recovery,self_fever_re,miasmic_shield_break,distance_attenuation,initial_level,anomaly_accumulation,skill_type,trigger_buff_level,element_type,element_damage_percent,diff_multiplier,ticks,hit_times,on_field,anomaly_attack,interruption_resistance,swap_cancel_ticks,labels,follow_up,follow_by,aid_direction,aid_lag_ticks,tick_list,force_add_condition_APL,heavy_attack,max_repeat_times,do_immediately,anomaly_update_list
1241,朱鸢,普攻,1241_NA_1,第1段普攻,普通攻击:不许动!,物理,一段,0.431,0.04,0.871,0.951,1.031,0.216,0.01,0.326,0.346,0.366,0,0,0.705,0,0,0,4.9,5.39,19.59,0,0,0,0,0,0,0,0,29,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,普攻,1241_NA_2,第2段普攻,普通攻击:不许动!,物理,二段,1.264,0.115,2.529,2.759,2.989,0.973,0.045,1.468,1.558,1.648,0,0,3.182,0,0,0,22.1,24.31,88.38,0,0,10477,0,0,0,1,0,59,4,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,普攻,1241_NA_3,第3段普攻,普通攻击:不许动!,物理,三段,1.373,0.125,2.748,2.998,3.248,0.947,0.044,1.431,1.519,1.607,0,0,3.097,0,0,0,21.53,23.6775,86.01,0,0,0,0,0,0,0,0,50,5,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,普攻,1241_NA_4,第4段普攻,普通攻击:不许动!,物理,四段,1.51,0.138,3.028,3.304,3.58,1.243,0.057,1.87,1.984,2.098,0,0,4.066,0,0,0,28.25,31.075,112.94,0,0,5912,0,0,0,1,0,66,4,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,普攻,1241_NA_5,第5段普攻,普通攻击:不许动!,物理,五段,1.622,0.148,3.25,3.546,3.842,1.392,0.064,2.096,2.224,2.352,0,0,4.554,0,0,0,32.48,34.7875,126.5,0,0,6005,0,0,4,0.690627202,0,55,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
0,朱鸢,强化特殊技,1241_E_EX_B,强化E形态B,废弃,废弃,废弃,0.431,0.04,0.871,0.951,1.031,0.216,0.01,0.326,0.346,0.366,60,60,0.705,0,0,0,154.8,5.39,19.59,0,0,0,1,2,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1241,朱鸢,普攻,1241_SNA_1,第1段特殊普攻,普通攻击:请勿抵抗,物理,一段(物理),0.537,0.049,1.076,1.174,1.272,0.596,0.028,0.904,0.96,1.016,0,0,0.991,0,0,0,6.9,7.59,54.16,0,0,0,0,0,0,0,0,35,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,普攻,1241_SNA_1_α,第1段特殊普攻(α衔接),普通攻击:请勿抵抗,物理,一段(物理),0.537,0.049,1.076,1.174,1.272,0.596,0.028,0.904,0.96,1.016,0,0,0.991,0,0,0,17.93,7.59,54.16,0,0,0,0,0,0,0,0,49,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,普攻,1241_SNA_1_β,第1段特殊普攻(β衔接),普通攻击:请勿抵抗,物理,一段(物理),0.537,0.049,1.076,1.174,1.272,0.596,0.028,0.904,0.96,1.016,0,0,0.991,0,0,0,17.93,7.59,54.16,0,0,0,0,0,0,0,0,47,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,普攻,1241_SNA_2,第2段特殊普攻,普通攻击:请勿抵抗,物理,二段(物理),0.537,0.049,1.076,1.174,1.272,0.596,0.028,0.904,0.96,1.016,0,0,1.835,0,0,0,13.18,14.025,54.16,0,0,0,0,0,0,0,0,28,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,普攻,1241_SNA_3,第3段特殊普攻,普通攻击:请勿抵抗,物理,三段(物理)-第1枪,0.5363,0.049,1.0753,1.1733,1.2713,0.5957,0.0273,0.8963,0.951,1.0057,0,0,1.635333333,0,0,0,34.18,12.49416667,54.15333333,0,0,0,0,0,0,0,0,22,3,TRUE,FALSE,0,0,,1241_SNA_4,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,普攻,1241_SNA_4,第3段特殊普攻,普通攻击:请勿抵抗,物理,三段(物理)-第2枪,0.5363,0.049,1.0753,1.1733,1.2713,0.5957,0.0273,0.8963,0.951,1.0057,0,0,1.635333333,0,0,0,27.6,12.49416667,54.15333333,0,0,0,0,0,0,0,0,15,3,TRUE,FALSE,0,0,,1241_SNA_5,1241_SNA_3,0,0,,,FALSE,1,FALSE,
1241,朱鸢,普攻,1241_SNA_5,第5段特殊普攻,普通攻击:请勿抵抗,物理,三段(物理)-第3枪,0.5363,0.049,1.0753,1.1733,1.2713,0.5957,0.0273,0.8963,0.951,1.0057,0,0,1.635333333,0,0,0,6.9,12.49416667,54.15333333,0,0,0,0,0,0,0,0,43,3,TRUE,FALSE,0,0,,,1241_SNA_4,0,0,,,TRUE,1,FALSE,
1241,朱鸢,普攻,1241_SNA_1_A,第1段特殊普攻(形态A),普通攻击:请勿抵抗,以太,一段(以太),1.359,0.124,2.723,2.971,3.219,0.596,0.028,0.904,0.96,1.016,0,0,0.991,0,0,0,6.9,7.59,54.16,0,0,5415,0,0,4,1,0,35,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,普攻,1241_SNA_1_A_α,第1段特殊普攻(形态A-α衔接),普通攻击:请勿抵抗,以太,一段(以太),1.359,0.124,2.723,2.971,3.219,0.596,0.028,0.904,0.96,1.016,0,0,0.991,0,0,0,6.9,7.59,54.16,0,0,5415,0,0,4,1,0,49,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,普攻,1241_SNA_1_A_β,第1段特殊普攻(形态A-β衔接),普通攻击:请勿抵抗,以太,一段(以太),1.359,0.124,2.723,2.971,3.219,0.596,0.028,0.904,0.96,1.016,0,0,0.991,0,0,0,6.9,7.59,54.16,0,0,5415,0,0,4,1,0,47,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,普攻,1241_SNA_2_A,第2段特殊普攻(形态A),普通攻击:请勿抵抗,以太,二段(以太),1.359,0.124,2.723,2.971,3.219,0.596,0.028,0.904,0.96,1.016,0,0,1.835,0,0,0,13.18,14.025,54.16,0,0,5415,0,0,4,1,0,28,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,普攻,1241_SNA_3_A,第3段特殊普攻(形态A),普通攻击:请勿抵抗,以太,三段(以太)-第1枪,1.359,0.1237,2.7193,2.9667,3.214,0.5957,0.0273,0.8963,0.951,1.0057,0,0,1.635333333,0,0,0,11.39,12.49416667,54.15333333,0,0,5415,0,0,4,1,0,22,3,TRUE,TRUE,0,0,,1241_SNA_4_A,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,普攻,1241_SNA_4_A,第4段特殊普攻(形态A),普通攻击:请勿抵抗,以太,三段(以太)-第2枪,1.359,0.1237,2.7193,2.9667,3.214,0.5957,0.0273,0.8963,0.951,1.0057,0,0,1.635333333,0,0,0,11.39,12.49416667,54.15333333,0,0,5415,0,0,4,1,0,15,3,TRUE,TRUE,0,0,,1241_SNA_5_A,1241_SNA_3_A,0,0,,,FALSE,1,FALSE,
1241,朱鸢,普攻,1241_SNA_5_A,第5段特殊普攻(形态A),普通攻击:请勿抵抗,以太,三段(以太)-第3枪,1.359,0.1237,2.7193,2.9667,3.214,0.5957,0.0273,0.8963,0.951,1.0057,0,0,1.635333333,0,0,0,11.39,12.49416667,54.15333333,0,0,5415,0,0,4,1,0,43,3,TRUE,TRUE,0,0,,,1241_SNA_4_A,0,0,,,TRUE,1,FALSE,
1241,朱鸢,特殊技,1241_E,特殊技,特殊技:鹿弹射击,,直接点按,0.184,0.017,0.371,0.405,0.439,0.184,0.009,0.283,0.301,0.319,0,0,0,0,0,0,4.18,4.5925,16.66,0,0,1665,1,1,4,1,0,44,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1241,朱鸢,特殊技,1241_E_A,特殊技形态A,特殊技:鹿弹射击,,后闪,0.184,0.017,0.371,0.405,0.439,0.184,0.009,0.283,0.301,0.319,0,0,0,0,0,0,4.18,4.5925,16.66,0,0,1665,1,1,4,1,0,40,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1241,朱鸢,特殊技,1241_E_B,特殊技形态B,特殊技:鹿弹射击,,前闪,0.184,0.017,0.371,0.405,0.439,0.184,0.009,0.283,0.301,0.319,0,0,0,0,0,0,4.18,4.5925,16.66,0,0,1665,1,1,4,1,0,22,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1241,朱鸢,特殊技,1241_E_C,特殊技形态C,特殊技:鹿弹射击,,侧闪,0.184,0.017,0.371,0.405,0.439,0.184,0.009,0.283,0.301,0.319,0,0,0,0,0,0,4.18,4.5925,16.66,0,0,1665,1,1,4,1,0,25,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1241,朱鸢,强化特殊技,1241_E_EX,强化特殊技,强化特殊技:全弹连射,,直接点按,5.874,0.534,11.748,12.816,13.884,4.8,0.219,7.209,7.647,8.085,60,60,0,0,0,0,154.8,173.7175,143.3,0,0,48512,1,2,4,1,0,86,7,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1241,朱鸢,强化特殊技,1241_E_EX_A,强化E形态A,强化特殊技:全弹连射,,侧闪,5.874,0.534,11.748,12.816,13.884,4.8,0.219,7.209,7.647,8.085,60,60,0,0,0,0,154.8,173.7175,143.3,0,0,48512,1,2,4,1,0,91,7,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1241,朱鸢,连携技,1241_QTE,连携技,连携技:歼灭模式,,,5.875,0.535,11.76,12.83,13.9,1.486,0.068,2.234,2.37,2.506,0,0,0,0,0,0,153.48,220.0275,135.01,0,0,33450,3,5,4,1,0,91,9,TRUE,TRUE,0,55,,,,0,0,,,TRUE,1,TRUE,
0,朱鸢,连携技,0_QTE_PRE,连携技僵直,弃用,废弃,废弃,废弃,4.077,0.371,8.158,8.9,9.642,1.787,0.082,2.689,2.853,3.017,0,0,4.906,0,0,0,153.48,37.4825,162.46,0,0,16245,3,5,4,1,0,55,9,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,朱鸢,冲刺攻击,0_RA,和前冲刺攻击重复,弃用,废弃,废弃,废弃,4.077,0.371,8.158,8.9,9.642,1.787,0.082,2.689,2.853,3.017,0,0,4.906,0,0,0,6.28,37.4825,162.46,0,0,16245,2,3,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,冲刺攻击,1241_RA_F,前冲刺攻击,冲刺攻击:火力奇袭,,前向,0.551,0.051,1.112,1.214,1.316,0.276,0.013,0.419,0.445,0.471,0,0,0.901,0,0,0,6.28,6.9025,25.01,0,0,5000,2,3,0,1,0,30,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,冲刺攻击,1241_RA_B,后冲刺攻击,冲刺攻击:火力奇袭,,后向,0.551,0.051,1.112,1.214,1.316,0.276,0.013,0.419,0.445,0.471,0,0,0.901,0,0,0,8.37,6.9025,25.01,0,0,5000,2,3,0,1,0,53,4,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,冲刺攻击,1241_RA_S,侧冲刺攻击,冲刺攻击:火力奇袭,,侧向,0.551,0.051,1.112,1.214,1.316,0.276,0.013,0.419,0.445,0.471,0,0,0.901,0,0,0,6.28,6.9025,25.01,0,0,5000,2,3,0,1,0,31,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,冲刺攻击,1241_SRA_F,前特殊冲刺攻击,冲刺攻击:火力压制,物理,前向,0.537,0.049,1.076,1.174,1.272,0.596,0.028,0.904,0.96,1.016,0,0,1.171,0,0,0,0.82,8.965,32.51,0,0,0,2,3,0,0,0,50,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,冲刺攻击,1241_SRA_F_ET,前特殊冲刺攻击(以太),冲刺攻击:火力压制,以太,前向,1.359,0.124,2.723,2.971,3.219,0.596,0.028,0.904,0.96,1.016,0,0,2.341,0,0,0,2.76,17.9025,65.01,0,0,5415,2,3,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,冲刺攻击,1241_SRA_B,后特殊冲刺攻击,冲刺攻击:火力压制,物理,后向,0.537,0.049,1.076,1.174,1.272,0.596,0.028,0.904,0.96,1.016,0,0,1.171,0,0,0,0.82,8.965,32.51,0,0,0,2,3,0,0,0,49,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,冲刺攻击,1241_SRA_B_ET,后特殊冲刺攻击(以太),冲刺攻击:火力压制,以太,后向,1.359,0.124,2.723,2.971,3.219,0.596,0.028,0.904,0.96,1.016,0,0,2.341,0,0,0,2.76,17.9025,65.01,0,0,5415,2,3,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,冲刺攻击,1241_SRA_S,侧特殊冲刺攻击,冲刺攻击:火力压制,物理,侧向,0.537,0.049,1.076,1.174,1.272,0.596,0.028,0.904,0.96,1.016,0,0,1.171,0,0,0,0.82,8.965,32.51,0,0,0,2,3,0,0,0,27,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,冲刺攻击,1241_SRA_S_ET,侧特殊冲刺攻击(以太),冲刺攻击:火力压制,以太,侧向,1.359,0.124,2.723,2.971,3.219,0.596,0.028,0.904,0.96,1.016,0,0,2.341,0,0,0,2.76,17.9025,65.01,0,0,5415,2,3,4,1,0,27,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1241,朱鸢,终结技,1241_Q,终结技,终结技:歼灭模式MAX,,,19.776,1.798,39.554,43.15,46.746,1.251,0.0627,1.9407,2.0661,2.1915,0,0,0,0,0,0,0,0,603.34,0,0,10333,3,6,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1241,朱鸢,受击支援,1241_BH_Aid,受击支援,快速支援:掩护射击,,,0.514,0.047,1.031,1.125,1.219,0.514,0.024,0.778,0.826,0.874,0,0,1.682,0,0,0,11.7,12.87,23.36,0,0,4670,5,7,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1241,朱鸢,突击支援,1241_Assault_Aid,突击支援,支援突击:自卫还击,,,3.558,0.324,7.122,7.77,8.418,3.086,0.141,4.637,4.919,5.201,0,0,0,0,0,0,91.6,100.76,151.67,0,0,30197,5,9,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1241,朱鸢,闪避反击,1241_CA,闪避反击,闪避反击:火力震爆,,,1.768,0.161,3.539,3.861,4.183,1.614,0.074,2.428,2.576,2.724,0,0,1.682,0,0,0,11.7,12.87,196.71,0,0,4670,2,4,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
0,朱鸢,普攻,0_NA_Switch,普攻状态切换,废弃,废弃,废弃,0.431,0.04,0.871,0.951,1.031,0.216,0.01,0.326,0.346,0.366,0,0,0.705,0,0,0,27.6,5.39,19.59,0,0,0,0,0,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1011,安比,普攻,1011_NA_1,第1段普攻,普通攻击:伏特速攻,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,3.9,4.29,15.59,0,0,0,0,0,0,0,0,22,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1011,安比,普攻,1011_NA_2,第2段普攻,普通攻击:伏特速攻,,,0.337,0.031,0.678,0.74,0.802,0.287,0.014,0.441,0.469,0.497,0,0,1.032,0,0,0,7.18,7.8925,28.65,0,0,0,0,0,0,0,0,21,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1011,安比,普攻,1011_NA_3,第3段普攻,普通攻击:伏特速攻,,,1.136,0.104,2.28,2.488,2.696,0.896,0.041,1.347,1.429,1.511,0,0,3.226,0,0,0,22.4,24.64,89.59,0,0,0,0,0,0,0,0,54,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1011,安比,普攻,1011_NA_4,第4段普攻,普通攻击:伏特速攻,,,2.391,0.218,4.789,5.225,5.661,1.874,0.086,2.82,2.992,3.164,0,0,6.745,0,0,0,46.85,51.535,187.35,0,0,17247,0,0,3,1,0,94,5,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1011,安比,普攻,1011_SNA_4,第4段特殊普攻,普通攻击:落雷,,,3.286,0.299,6.575,7.173,7.771,1.424,0.065,2.139,2.269,2.399,0,0,5.126,0,0,0,35.6,39.16,142.38,0,0,12750,0,0,3,1,0,70,5,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1011,安比,特殊技,1011_E,特殊技,特殊技:电光挥击,,,0.934,0.085,1.869,2.039,2.209,0.934,0.043,1.407,1.493,1.579,0,0,0,0,0,0,23.35,25.685,46.66,0,0,9330,1,1,3,1,0,62,3,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1011,安比,强化特殊技,1011_E_EX,强化特殊技,强化特殊技:苍雷斩,,,5.83,0.53,11.66,12.72,13.78,4.818,0.219,7.227,7.665,8.103,60,60,0,0,0,0,171.05,188.155,178.3,0,0,53237,1,2,3,1,0,105,6,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1011,安比,强化特殊技,1011_E_EX_A,强化E形态A,强化特殊技:苍雷斩,,快速释放,5.83,0.53,11.66,12.72,13.78,4.818,0.219,7.227,7.665,8.103,60,60,0,0,0,0,171.05,188.155,178.3,0,0,53237,1,2,3,1,0,88,6,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1011,安比,特殊技,1011_E_A,特殊技形态A,特殊技:电光挥击,,快速释放,0.934,0.085,1.869,2.039,2.209,0.934,0.043,1.407,1.493,1.579,0,0,0,0,0,0,23.35,25.685,46.66,0,0,9330,1,1,3,1,0,48,3,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1011,安比,连携技,1011_QTE,连携技,连携技:电磁引擎,,,5.424,0.494,10.858,11.846,12.834,1.434,0.066,2.16,2.292,2.424,0,0,0,0,0,0,152.23,222.31,143.34,0,0,34283,3,5,3,1,0,97,4,TRUE,TRUE,0,97,,,,0,0,,,TRUE,1,TRUE,
1011,安比,冲刺攻击,1011_RA,冲刺攻击,冲刺攻击:电弧斩,,,0.567,0.052,1.139,1.243,1.347,0.284,0.013,0.427,0.453,0.479,0,0,1.02,0,0,0,7.1,7.81,28.32,0,0,0,2,3,0,0,0,35,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1011,安比,闪避反击,1011_CA,闪避反击,闪避反击:迅雷,,,1.802,0.164,3.606,3.934,4.262,1.617,0.074,2.431,2.579,2.727,0,0,2.219,0,0,0,15.43,16.9675,211.64,0,0,6163,2,4,3,1,0,40,3,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1011,安比,终结技,1011_Q,终结技,终结技:过载引擎,,,15.126,1.376,30.262,33.014,35.766,9.916,0.451,14.877,15.779,16.681,0,0,0,0,0,0,0,0,710.04,0,0,21003,3,6,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1011,安比,受击支援,1011_BH_Aid,受击支援,快速支援:降雷,,,0.617,0.057,1.244,1.358,1.472,0.617,0.029,0.936,0.994,1.052,0,0,2.219,0,0,0,15.43,16.9675,30.82,0,0,6163,5,7,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1011,安比,招架/回避支援,1011_Light_parry_Aid,轻招架,招架支援:电光一闪,,,0,0,0,0,0,2.467,0.113,3.71,3.936,4.162,0,0,0,0,0,0,16.68,0,366.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1011,安比,招架/回避支援,1011_Heavy_parry_Aid,重招架,招架支援:电光一闪,,,0,0,0,0,0,3.117,0.142,4.679,4.963,5.247,0,0,0,0,0,0,29.18,0,416.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1011,安比,招架/回避支援,1011_Chain_parry_Aid,连续招架,招架支援:电光一闪,,,0,0,0,0,0,1.517,0.069,2.276,2.414,2.552,0,0,0,0,0,0,29.18,0,116.64,0,0,0,5,8,0,1,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1011,安比,突击支援,1011_Assault_Aid,突击支援,支援突击:回旋闪电,,,3.352,0.305,6.707,7.317,7.927,2.914,0.133,4.377,4.643,4.909,0,0,0,0,0,0,94.73,104.1975,160,0,0,31322,5,9,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1141,莱卡恩,普攻,1141_NA_1,第1段普攻,普通攻击:狩月舞步,点按,一段,0.292,0.027,0.589,0.643,0.697,0.146,0.007,0.223,0.237,0.251,0,0,0.501,0,0,0,3.48,3.8225,13.9,0,0,0,0,0,0,0,0,27,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1141,莱卡恩,普攻,1141_SNA_1,第1段特殊普攻,普通攻击:狩月舞步,长按,一段蓄力,0.371,0.034,0.745,0.813,0.881,0.121,0.006,0.187,0.199,0.211,0,0,0.414,0,0,0,2.88,3.1625,11.49,0,0,1148,0,0,2,1,0,29,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1141,莱卡恩,普攻,1141_NA_2,第2段普攻,普通攻击:狩月舞步,点按,二段,0.349,0.032,0.701,0.765,0.829,0.303,0.014,0.457,0.485,0.513,0,0,1.039,0,0,0,7.23,7.9475,28.85,0,0,0,0,0,0,0,0,21,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1141,莱卡恩,普攻,1141_SNA_2,第2段特殊普攻,普通攻击:狩月舞步,长按,二段蓄力,0.564,0.052,1.136,1.24,1.344,0.329,0.015,0.494,0.524,0.554,0,0,1.127,0,0,0,7.83,8.6075,31.29,0,0,3128,0,0,2,1,0,17,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1141,莱卡恩,普攻,1141_NA_3,第3段普攻,普通攻击:狩月舞步,点按,三段,0.584,0.054,1.178,1.286,1.394,0.456,0.021,0.687,0.729,0.771,0,0,1.563,0,0,0,10.88,11.9625,43.41,0,0,0,0,0,0,0,0,31,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1141,莱卡恩,普攻,1141_SNA_3,第3段特殊普攻,普通攻击:狩月舞步,长按,三段蓄力,0.995,0.091,1.996,2.178,2.36,0.537,0.025,0.812,0.862,0.912,0,0,1.841,0,0,0,12.8,14.08,51.14,0,0,5113,0,0,2,1,0,33,2,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1141,莱卡恩,普攻,1141_NA_4,第4段普攻,普通攻击:狩月舞步,点按,四段,1.52,0.139,3.049,3.327,3.605,1.12,0.051,1.681,1.783,1.885,0,0,3.838,0,0,0,26.65,29.315,106.6,0,0,0,0,0,0,0,0,67,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1141,莱卡恩,普攻,1141_SNA_4,第4段特殊普攻,普通攻击:狩月舞步,长按,四段蓄力,2.109,0.192,4.221,4.605,4.989,1.071,0.049,1.61,1.708,1.806,0,0,3.672,0,0,0,25.5,28.05,101.99,0,0,10198,0,0,2,1,0,68,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1141,莱卡恩,普攻,1141_NA_5,第5段普攻,普通攻击:狩月舞步,点按,五段,1.807,0.165,3.622,3.952,4.282,1.477,0.068,2.225,2.361,2.497,0,0,5.062,0,0,0,35.15,38.665,140.59,0,0,0,0,0,0,0,0,68,3,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1141,莱卡恩,普攻,1141_SNA_5_NFC,第5段特殊普攻(不满),普通攻击:狩月舞步,长按,五段一级蓄力,2.776,0.253,5.559,6.065,6.571,1.631,0.075,2.456,2.606,2.756,0,0,5.589,0,0,0,38.83,42.7075,155.25,0,0,15524,0,0,2,1,0,77,4,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1141,莱卡恩,普攻,1141_SNA_5_FC,第5段特殊普攻(满),普通攻击:狩月舞步,长按,五段二级蓄力,3.557,0.324,7.121,7.769,8.417,2.056,0.094,3.09,3.278,3.466,0,0,7.048,0,0,0,48.95,53.845,195.76,0,0,19575,0,0,2,1,0,109,7,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1141,莱卡恩,特殊技,1141_E,特殊技,特殊技:追猎时刻,,点按,5.343,0.487,10.7,11.674,12.648,4.504,0.206,6.77,7.182,7.594,0,0,0,0,0,0,18.35,38.47126802,0,0,0,7416,1,1,2,1,0,63,3,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1141,莱卡恩,特殊技,1141_E_A,特殊技形态A,特殊技:追猎时刻,,蓄力,7.895,0.719,15.804,17.242,18.68,6.644,0.303,9.977,10.583,11.189,0,0,0,0,0,0,31.7,162.440985,0,0,0,42618,1,1,2,1,0,87,4,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1141,莱卡恩,强化特殊技,1141_E_EX,强化特殊技,强化特殊技:狂猎时刻,,点按,0.473,0.043,0.946,1.032,1.118,0.237,0.011,0.358,0.38,0.402,60,40,0,0,0,0,133.85,240.2694525,0,0,0,62924,1,2,2,1,0,103,5,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1141,莱卡恩,强化特殊技,1141_E_EX_A,强化E形态A,强化特殊技:狂猎时刻,,蓄力,0.473,0.043,0.946,1.032,1.118,0.237,0.011,0.358,0.38,0.402,20,20,0.81,0,0,0,197.98,6.856866383,0,0,0,0,1,2,2,1,0,163,8,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1141,莱卡恩,冲刺攻击,1141_RA,冲刺攻击,冲刺攻击:保持清洁,,,0.473,0.043,0.946,1.032,1.118,0.237,0.011,0.358,0.38,0.402,0,0,0.811,0,0,0,5.65,6.215,22.51,0,0,0,2,3,0,0,0,27,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1141,莱卡恩,闪避反击,1141_CA,闪避反击,闪避反击:礼仪教导,,,1.87,0.17,3.74,4.08,4.42,1.681,0.077,2.528,2.682,2.836,0,0,2.162,0,0,0,17.1,16.5275,210.04,0,0,6003,2,4,2,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1141,莱卡恩,连携技,1141_QTE,连携技,连携技:遵命,,,6.378,0.58,12.758,13.918,15.078,2.188,0.1,3.288,3.488,3.688,0,0,0,0,0,0,168.48,240.185,208.37,0,0,40786,3,5,2,1,0,127,8,TRUE,TRUE,0,127,,,,0,0,,,TRUE,1,TRUE,
1141,莱卡恩,终结技,1141_Q,终结技,终结技:不辱使命,,,16.941,1.541,33.892,36.974,40.056,10.966,0.499,16.455,17.453,18.451,0,0,0,0,0,0,0,0,673.37,0,0,17336,3,6,2,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1141,莱卡恩,受击支援,1141_BH_Aid,受击支援,快速支援:狼群,,,0.631,0.058,1.269,1.385,1.501,0.631,0.029,0.95,1.008,1.066,0,0,2.162,0,0,0,17.1,16.5275,30.02,0,0,6003,5,7,2,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1141,莱卡恩,招架/回避支援,1141_Light_parry_Aid,轻招架,招架支援:狩猎干预,,,0,0,0,0,0,2.59,0.118,3.888,4.124,4.36,0,0,0,0,0,0,16.68,0,366.64,0,0,0,5,8,0,0,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1141,莱卡恩,招架/回避支援,1141_Heavy_parry_Aid,重招架,招架支援:狩猎干预,,,0,0,0,0,0,3.273,0.149,4.912,5.21,5.508,0,0,0,0,0,0,29.18,0,416.64,0,0,0,5,8,0,0,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1141,莱卡恩,招架/回避支援,1141_Chain_parry_Aid,连续招架,招架支援:狩猎干预,,,0,0,0,0,0,1.593,0.073,2.396,2.542,2.688,0,0,0,0,0,0,29.18,0,116.64,0,0,0,5,8,0,0,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1141,莱卡恩,突击支援,1141_Assault_Aid,突击支援,支援突击:复仇反扑,,,2.883,0.263,5.776,6.302,6.828,2.468,0.113,3.711,3.937,4.163,0,0,0,0,0,0,78.5,86.35,116.71,0,0,25476,5,9,2,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1101,珂蕾妲,普攻,1101_NA_1,第1段普攻,普通攻击:砸扁,粉碎,,,0.636,0.058,1.274,1.39,1.506,0.318,0.015,0.483,0.513,0.543,0,0,1.09,0,0,0,7.58,8.3325,30.27,0,0,0,0,0,0,0,0,41,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1101,珂蕾妲,普攻,1101_NA_2,第2段普攻,普通攻击:砸扁,粉碎,,,0.792,0.072,1.584,1.728,1.872,0.655,0.03,0.985,1.045,1.105,0,0,2.246,0,0,0,15.6,17.16,62.38,0,0,0,0,0,0,0,0,37,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1101,珂蕾妲,普攻,1101_NA_3,第3段普攻,普通攻击:砸扁,粉碎,,,1.261,0.115,2.526,2.756,2.986,1.043,0.048,1.571,1.667,1.763,0,0,3.574,0,0,0,24.83,27.3075,99.27,0,0,0,0,0,0,0,0,57,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1101,珂蕾妲,普攻,1101_NA_4,第4段普攻,普通攻击:砸扁,粉碎,,,3.174,0.289,6.353,6.931,7.509,2.493,0.114,3.747,3.975,4.203,0,0,8.547,0,0,0,59.38,65.3125,237.41,0,0,0,0,0,0,0,0,108,5,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1101,珂蕾妲,普攻,1101_SNA_1,第1段特殊普攻,普通攻击:砸扁,粉碎,强化普攻,单人熔炉升温,强化普攻1,1.608,0.147,3.225,3.519,3.813,0.614,0.028,0.922,0.978,1.034,0,0,2.105,0,0,0,14.63,16.0875,58.46,0,0,4435,0,0,1,1,0,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1101,珂蕾妲,普攻,1101_SNA_2,第2段特殊普攻,普通攻击:砸扁,粉碎,强化普攻,单人熔炉升温,强化普攻2,4.049,0.369,8.108,8.846,9.584,1.566,0.072,2.358,2.502,2.646,0,0,5.972,0,0,0,41.48,45.6225,165.88,0,0,14908,0,0,1,1,0,80,3,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1101,珂蕾妲,普攻,1101_SNA_1_A,第1段特殊普攻(形态A),普通攻击:砸扁,粉碎,协同强化普攻,协同后强化普攻1,1.608,0.147,3.225,3.519,3.813,0.614,0.028,0.922,0.978,1.034,0,0,2.105,0,0,0,14.63,16.0875,58.46,0,0,4435,0,0,1,1,0,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1101,珂蕾妲,普攻,1101_SNA_2_A,第2段特殊普攻(形态A),普通攻击:砸扁,粉碎,协同强化普攻,协同后强化普攻2,5.013,0.456,10.029,10.941,11.853,2.346,0.107,3.523,3.737,3.951,0,0,7.311,0,0,0,50.78,55.8525,203.08,0,0,18629,0,0,1,1,0,98,4,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1101,珂蕾妲,特殊技,1101_E_1,第1段特殊技,特殊技:爆破!铁锤时间,,打击,0.519,0.048,1.047,1.143,1.239,0.519,0.024,0.783,0.831,0.879,0,0,0,0,0,0,11.35,13.585,49.35,0,0,6418,1,1,0,1,0,59,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1101,珂蕾妲,特殊技,1101_E_2,第2段特殊技,特殊技:爆破!铁锤时间,,引爆,0.778,0.071,1.559,1.701,1.843,0.778,0.036,1.174,1.246,1.318,0,0,0,0,0,0,17.03,20.3775,74.02,0,0,5918,1,1,1,1,0,28,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1101,珂蕾妲,特殊技,1101_E_A,特殊技形态A,特殊技:爆破!铁锤时间,,引爆(协同),0.855,0.078,1.713,1.869,2.025,0.778,0.036,1.174,1.246,1.318,0,0,0,0,0,0,17.03,20.3775,74.02,0,0,5918,1,1,1,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1101,珂蕾妲,强化特殊技,1101_E_EX_1,第1段强化特殊技,强化特殊技:沸腾熔炉,,打击,1.523,0.139,3.052,3.33,3.608,1.523,0.07,2.293,2.433,2.573,60,60,0,0,0,0,36.25,39.875,144.97,0,0,7248,1,2,0,1,0,60,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1101,珂蕾妲,强化特殊技,1101_E_EX_2,第2段强化特殊技,强化特殊技:沸腾熔炉,,引爆,6.06,0.551,12.121,13.223,14.325,4.94,0.225,7.415,7.865,8.315,60,60,0,0,0,0,155.4,170.9675,136.64,0,0,47612,1,2,1,1,0,40,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1101,珂蕾妲,强化特殊技,1101_E_EX_A,强化E形态A,强化特殊技:沸腾熔炉,,引爆(协同),6.666,0.606,13.332,14.544,15.756,4.94,0.225,7.415,7.865,8.315,60,60,0,0,0,0,155.4,170.9675,136.64,0,0,47612,1,2,1,1,0,60,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1101,珂蕾妲,冲刺攻击,1101_RA,冲刺攻击,冲刺攻击:给我颤抖,,,0.561,0.051,1.122,1.224,1.326,0.281,0.013,0.424,0.45,0.476,0,0,0.961,0,0,0,6.68,7.3425,26.67,0,0,0,2,3,0,1,0,32,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1101,珂蕾妲,闪避反击,1101_CA,闪避反击,闪避反击:别小看我,,,3.439,0.313,6.882,7.508,8.134,2.888,0.132,4.34,4.604,4.868,0,0,6.299,0,0,0,47.1,48.125,324.97,0,0,17496,2,4,0,1,0,83,4,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1101,珂蕾妲,连携技,1101_QTE,连携技,连携技:天崩-地裂,,,6.36,0.579,12.729,13.887,15.045,2.17,0.099,3.259,3.457,3.655,0,0,0,0,0,0,168.1,239.7175,206.64,0,0,40613,3,5,1,1,0,119,2,TRUE,TRUE,0,119,,,,0,0,,,TRUE,1,TRUE,
1101,珂蕾妲,终结技,1101_Q,终结技,终结技:锤进地心,,单人大招,15.488,1.408,30.976,33.792,36.608,10.049,0.457,15.076,15.99,16.904,0,0,0,0,0,0,0,0,680.04,0,0,18003,3,6,1,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1101,珂蕾妲,终结技,1101_Q_A,终结技(形态A),终结技:锤进地心,,协同状态下的大招,16.94,1.54,33.88,36.96,40.04,10.965,0.499,16.454,17.452,18.45,0,0,0,0,0,0,0,0,673.34,0,0,19066,3,6,1,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1101,珂蕾妲,受击支援,1101_BH_Aid,受击支援,快速支援:让我来,,,1.838,0.168,3.686,4.022,4.358,1.838,0.084,2.762,2.93,3.098,0,0,6.299,0,0,0,47.1,48.125,87.49,0,0,17496,5,7,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1101,珂蕾妲,招架/回避支援,1101_Light_parry_Aid,轻招架,招架支援:护身锤,,,0,0,0,0,0,2.59,0.118,3.888,4.124,4.36,0,0,0,0,0,0,16.68,0,366.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1101,珂蕾妲,招架/回避支援,1101_Heavy_parry_Aid,重招架,招架支援:护身锤,,,0,0,0,0,0,3.273,0.149,4.912,5.21,5.508,0,0,0,0,0,0,29.18,0,416.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1101,珂蕾妲,招架/回避支援,1101_Chain_parry_Aid,连续招架,招架支援:护身锤,,,0,0,0,0,0,1.593,0.073,2.396,2.542,2.688,0,0,0,0,0,0,29.18,0,116.64,0,0,0,5,8,0,1,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1101,珂蕾妲,突击支援,1101_Assault_Aid,突击支援,支援突击:锤钟,,,3.592,0.327,7.189,7.843,8.497,3.127,0.143,4.7,4.986,5.272,0,0,0,0,0,0,96.6,106.26,164.97,0,0,31992,5,9,1,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1191,艾莲,普攻,1191_NA_1,第1段普攻,普通攻击:利齿修剪法,,,0.488,0.045,0.983,1.073,1.163,0.244,0.012,0.376,0.4,0.424,0,0,0.679,0,0,0,5.55,6.105,22.18,0,0,0,0,0,0,0,0,30,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1191,艾莲,普攻,1191_NA_2,第2段普攻,普通攻击:利齿修剪法,,,1.111,0.101,2.222,2.424,2.626,0.868,0.04,1.308,1.388,1.468,0,0,2.415,0,0,0,19.73,21.6975,78.9,0,0,0,0,0,0,0,0,46,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1191,艾莲,普攻,1191_NA_3_FC,第3段普攻(满),普通攻击:利齿修剪法,,,2.973,0.271,5.954,6.496,7.038,2.404,0.11,3.614,3.834,4.054,0,0,6.688,0,0,0,46.48,60.115,218.54,0,0,0,0,0,0,0,0,104,10,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1191,艾莲,普攻,1191_SNA_1,第1段特殊普攻,普通攻击:急冻修剪法,,,0.996,0.091,1.997,2.179,2.361,0.488,0.023,0.741,0.787,0.833,0,0,1.358,0,0,0,11.1,12.21,44.36,0,0,4435,0,0,2,1,0,30,2,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1191,艾莲,普攻,1191_SNA_2,第2段特殊普攻,普通攻击:急冻修剪法,,,1.84,0.168,3.688,4.024,4.36,0.902,0.041,1.353,1.435,1.517,0,0,2.509,0,0,0,20.5,22.55,82,0,0,8199,0,0,2,1,0,46,2,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1191,艾莲,普攻,1191_SNA_3_FC,第3段特殊普攻(满),普通攻击:急冻修剪法,,,4.962,0.452,9.934,10.838,11.742,2.455,0.112,3.687,3.911,4.135,0,0,6.428,0,0,0,51.58,61.38,223.18,0,0,22317,0,0,2,1,0,113,14,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1191,艾莲,普攻,1191_RA_1,冲刺攻击第1段,冲刺攻击:冰渊潜袭,,这一段无法单独打出,所以未不进行单独的测帧。,0.623,0.057,1.25,1.364,1.478,0.623,0.029,0.942,1,1.058,0,0,2.038,0,0,0,14.15,15.565,56.6,0,0,5659,0,0,2,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1191,艾莲,普攻,1191_RA_NFC,冲刺攻击(不满),冲刺攻击:冰渊潜袭,,实际的倍率数据为:回旋斩击+快速剪击,在公测中,快速剪击数据被砍,1.276,0.116,2.552,2.784,3.016,0.982,0.045,1.477,1.567,1.657,0,0,3.211,0,0,0,37.75,24.53,89.2,0,0,8919,0,0,2,1,0,64,4,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1191,艾莲,普攻,1191_RA_FC,冲刺攻击(满),冲刺攻击:冰渊潜袭,,实际的倍率数据为:回旋斩击+蓄力剪击,,1.582,0.144,3.166,3.454,3.742,1.217,0.056,1.833,1.945,2.057,0,0,3.981,0,0,0,41.8,30.415,110.57,0,0,11056,0,0,2,1,0,121,6,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1191,艾莲,特殊技,1191_E,特殊技,特殊技:摆尾,,,0.505,0.046,1.011,1.103,1.195,0.505,0.023,0.758,0.804,0.85,0,0,0,0,0,0,11.78,12.6225,45.85,0,0,4584,1,1,2,1,0,66,3,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1191,艾莲,强化特殊技,1191_E_EX,强化特殊技,强化特殊技:横扫,,,3.772,0.343,7.545,8.231,8.917,4.051,0.185,6.086,6.456,6.826,40,40,0,0,0,0,127.6,140.36,155.04,0,0,40373,1,2,2,1,0,88,8,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1191,艾莲,强化特殊技,1191_E_EX_A,强化E形态A,强化特殊技:鲨卷风,,,5.533,0.503,11.066,12.072,13.078,3.717,0.169,5.576,5.914,6.252,40,40,0,0,0,0,118.83,130.7075,131.67,0,0,37219,1,2,2,1,0,80,9,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1191,艾莲,冲刺攻击,1191_RA,冲刺攻击,冲刺攻击:骇浪,,普通冲刺攻击,0.77,0.07,1.54,1.68,1.82,0.385,0.018,0.583,0.619,0.655,0,0,1.26,0,0,0,8.75,9.625,34.99,0,0,0,2,3,0,0,0,44,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1191,艾莲,冲刺攻击,1191_RA_A,冲刺攻击形态A,冲刺攻击:寒潮,,冲刺攻击冰附魔,1.457,0.133,2.92,3.186,3.452,0.788,0.036,1.184,1.256,1.328,0,0,2.579,0,0,0,17.93,19.7175,35.82,0,0,7163,2,3,2,1,0,44,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1191,艾莲,闪避反击,1191_CA,闪避反击,闪避反击:暗礁,,,1.526,0.139,3.055,3.333,3.611,2.274,0.104,3.418,3.626,3.834,0,0,3.842,0,0,0,26.7,29.37,256.71,0,0,10670,2,4,0,1,0,64,4,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1191,艾莲,连携技,1191_QTE,连携技,连携技:雪崩,,,7.946,0.723,15.899,17.345,18.791,3.557,0.162,5.339,5.663,5.987,0,0,0,0,0,0,197.23,271.81,323.34,0,0,52283,3,5,2,1,0,211,13,TRUE,TRUE,0,211,,,,0,0,,,TRUE,1,TRUE,
1191,艾莲,终结技,1191_Q,终结技,终结技:永冬狂宴,,,18.908,1.719,37.817,41.255,44.693,1.852,0.085,2.787,2.957,3.127,0,0,0,0,0,0,0,0,668.34,0,0,16833,3,6,2,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1191,艾莲,受击支援,1191_BH_Aid,受击支援,快速支援:护卫鲛,,,1.211,0.111,2.432,2.654,2.876,1.211,0.056,1.827,1.939,2.051,0,0,3.962,0,0,0,27.53,30.2775,55.02,0,0,11003,5,7,2,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1191,艾莲,招架/回避支援,1191_Light_parry_Aid,轻招架,招架支援:迎头浪,,,0,0,0,0,0,2.713,0.124,4.077,4.325,4.573,0,0,0,0,0,0,16.68,0,366.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1191,艾莲,招架/回避支援,1191_Heavy_parry_Aid,重招架,招架支援:迎头浪,,,0,0,0,0,0,3.428,0.156,5.144,5.456,5.768,0,0,0,0,0,0,29.18,0,416.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1191,艾莲,招架/回避支援,1191_Chain_parry_Aid,连续招架,招架支援:迎头浪,,,0,0,0,0,0,1.668,0.076,2.504,2.656,2.808,0,0,0,0,0,0,29.18,0,116.64,0,0,0,5,8,0,1,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1191,艾莲,突击支援,1191_Assault_Aid,突击支援,支援突击:巡洋鲨,,,4.379,0.399,8.768,9.566,10.364,3.848,0.175,5.773,6.123,6.473,0,0,0,0,0,0,102.23,122.76,204.97,0,0,37392,5,9,2,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1041,11号,普攻,1041_NA_1,第1段普攻,普通攻击:热身火花,,一段,0.344,0.032,0.696,0.76,0.824,0.172,0.008,0.26,0.276,0.292,0,0,0.589,0,0,0,4,4.51,16.35,0,0,0,0,0,0,0,0,22,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1041,11号,普攻,1041_SNA_1,第1段特殊普攻,普通攻击:火力镇压,,一段,0.551,0.051,1.112,1.214,1.316,0.18,0.009,0.279,0.297,0.315,0,0,0.615,0,0,0,3.98,4.7025,17.06,0,0,1705,0,0,1,1,0,22,2,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1041,11号,普攻,1041_NA_2,第2段普攻,普通攻击:热身火花,,二段,0.412,0.038,0.83,0.906,0.982,0.344,0.016,0.52,0.552,0.584,0,0,1.177,0,0,0,7.9,8.9925,32.7,0,0,0,0,0,0,0,0,22,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1041,11号,普攻,1041_SNA_2,第2段特殊普攻,普通攻击:火力镇压,,二段,0.572,0.052,1.144,1.248,1.352,0.336,0.016,0.512,0.544,0.576,0,0,1.149,0,0,0,7.13,8.8,31.92,0,0,3191,0,0,1,1,0,22,4,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1041,11号,普攻,1041_NA_3,第3段普攻,普通攻击:热身火花,,三段,1.028,0.094,2.062,2.25,2.438,0.823,0.038,1.241,1.317,1.393,0,0,2.82,0,0,0,19.3,21.56,78.32,0,0,0,0,0,0,0,0,48,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1041,11号,普攻,1041_SNA_3,第3段特殊普攻,普通攻击:火力镇压,,三段,1.32,0.12,2.64,2.88,3.12,0.752,0.035,1.137,1.207,1.277,0,0,2.577,0,0,0,16.63,19.69,71.58,0,0,7157,0,0,1,1,0,42,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1041,11号,普攻,1041_NA_4,第4段普攻,普通攻击:热身火花,,四段,2.134,0.194,4.268,4.656,5.044,1.676,0.077,2.523,2.677,2.831,0,0,5.744,0,0,0,38.2,43.89,159.56,0,0,0,0,0,0,0,0,77,5,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1041,11号,普攻,1041_SNA_4,第4段特殊普攻,普通攻击:火力镇压,,四段,3.407,0.31,6.817,7.437,8.057,1.92,0.088,2.888,3.064,3.24,0,0,6.581,0,0,0,43.33,50.2975,182.81,0,0,18280,0,0,1,1,0,87,10,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1041,11号,特殊技,1041_E,特殊技,特殊技:烈火,,,0.526,0.048,1.054,1.15,1.246,0.526,0.024,0.79,0.838,0.886,0,0,0,0,0,0,12.3,13.7775,50.02,0,0,5001,1,1,1,1,0,72,4,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1041,11号,强化特殊技,1041_E_EX,强化特殊技,强化特殊技:盛燃烈火,,,6.75,0.614,13.504,14.732,15.96,5.435,0.248,8.163,8.659,9.155,80,80,0,0,0,0,192.05,211.255,141.7,0,0,58018,1,2,1,1,0,102,6,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1041,11号,冲刺攻击,1041_RA,冲刺攻击,冲刺攻击:炽火,,,0.683,0.063,1.376,1.502,1.628,0.342,0.016,0.518,0.55,0.582,0,0,1.171,0,0,0,7.83,8.965,32.51,0,0,0,2,3,0,0,0,37,2,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1041,11号,冲刺攻击,1041_SRA,特殊冲刺攻击,冲刺攻击:火力镇压,,,0.788,0.072,1.58,1.724,1.868,0.788,0.036,1.184,1.256,1.328,0,0,2.701,0,0,0,17.3,20.6525,37.51,0,0,7500,2,3,1,1,0,37,4,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1041,11号,闪避反击,1041_CA,闪避反击,闪避反击:逆火,,,2.62,0.239,5.249,5.727,6.205,2.258,0.103,3.391,3.597,3.803,0,0,4.14,0,0,0,28.35,31.625,265,0,0,11499,2,4,1,1,0,68,6,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1041,11号,连携技,1041_QTE,连携技,连携技:昂扬烈焰,,,6.325,0.575,12.65,13.8,14.95,2.136,0.098,3.214,3.41,3.606,0,0,0,0,0,0,166.8,238.81,203.37,0,0,40286,3,5,1,1,0,131,8,TRUE,TRUE,0,131,,,,0,0,,,TRUE,1,TRUE,
1041,11号,终结技,1041_Q,终结技,终结技:轰鸣烈焰,,,21.03,1.912,42.062,45.886,49.71,2.85,0.13,4.28,4.54,4.8,0,0,0,0,0,0,0,0,746.71,0,0,24670,3,6,1,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1041,11号,受击支援,1041_BH_Aid,受击支援,快速支援:火力掩护,,,1.208,0.11,2.418,2.638,2.858,1.208,0.055,1.813,1.923,2.033,0,0,4.14,0,0,0,28.35,31.625,57.5,0,0,11499,5,7,1,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1041,11号,招架/回避支援,1041_Light_parry_Aid,轻招架,招架支援:巩固防线,,,0,0,0,0,0,2.59,0.118,3.888,4.124,4.36,0,0,0,0,0,0,16.68,0,366.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1041,11号,招架/回避支援,1041_Heavy_parry_Aid,重招架,招架支援:巩固防线,,,0,0,0,0,0,3.273,0.149,4.912,5.21,5.508,0,0,0,0,0,0,29.18,0,416.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1041,11号,招架/回避支援,1041_Chain_parry_Aid,连续招架,招架支援:巩固防线,,,0,0,0,0,0,1.593,0.073,2.396,2.542,2.688,0,0,0,0,0,0,29.18,0,116.64,0,0,0,5,8,0,1,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1041,11号,突击支援,1041_Assault_Aid,突击支援,支援突击:重燃,,,3.837,0.349,7.676,8.374,9.072,3.355,0.153,5.038,5.344,5.65,0,0,0,0,0,0,102.85,113.135,181.64,0,0,34242,5,9,1,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1131,苍角,普攻,1131_NA_1,第1段普攻,普通攻击:打年糕,,,0.662,0.061,1.333,1.455,1.577,0.331,0.016,0.507,0.539,0.571,0,0,1.192,0,0,0,8.13,9.1025,33.1,0,0,0,0,0,0,0,0,46,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1131,苍角,普攻,1131_NA_2,第2段普攻,普通攻击:打年糕,,,2.163,0.197,4.33,4.724,5.118,1.88,0.086,2.826,2.998,3.17,0,0,6.767,0,0,0,39.95,44.4125,187.96,0,0,0,0,0,0,0,0,88,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1131,苍角,普攻,1131_NA_3,第3段普攻,普通攻击:打年糕,,,2.931,0.267,5.868,6.402,6.936,2.217,0.101,3.328,3.53,3.732,0,0,7.98,0,0,0,51.88,54.2025,221.65,0,0,0,0,0,0,0,0,120,3,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1131,苍角,普攻,1131_SNA_1,第1段特殊普攻,普通攻击:打年糕(霜染刃旗),,,0.766,0.07,1.536,1.676,1.816,0.466,0.022,0.708,0.752,0.796,0,0,1.677,0,0,0,11.83,12.815,46.58,0,0,4657,0,0,2,1,0,32,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1131,苍角,普攻,1131_SNA_2,第2段特殊普攻,普通攻击:打年糕(霜染刃旗),,,2.285,0.208,4.573,4.989,5.405,1.275,0.058,1.913,2.029,2.145,0,0,4.588,0,0,0,31.38,35.0625,127.44,0,0,12743,0,0,2,1,0,68,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1131,苍角,普攻,1131_SNA_3,第3段特殊普攻,普通攻击:打年糕(霜染刃旗),,,5.114,0.465,10.229,11.159,12.089,2.633,0.12,3.953,4.193,4.433,0,0,9.476,0,0,0,65.83,72.4075,263.22,0,0,26321,0,0,2,1,0,138,5,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1131,苍角,特殊技,1131_E_1,第1段特殊技,特殊技:吹凉便当,,一段,0.284,0.026,0.57,0.622,0.674,0.284,0.013,0.427,0.453,0.479,0,0,0,0,0,0,7.1,7.81,28.35,0,0,2834,1,1,2,1,0,76,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1131,苍角,特殊技,1131_E_2,第2段特殊技,特殊技:吹凉便当,,终结段,1.001,0.091,2.002,2.184,2.366,1.001,0.046,1.507,1.599,1.691,0,0,0,0,0,0,25.03,27.5275,50.02,0,0,5001,1,1,2,1,0,58,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1131,苍角,强化特殊技,1131_E_A,特殊技形态A,特殊技:集合啦!,,展旗,2.501,0.228,5.009,5.465,5.921,2.751,0.126,4.137,4.389,4.641,0,0,0,0,0,0,61.88,68.7775,125.01,0,0,12500,1,2,2,1,0,160,8,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1131,苍角,强化特殊技,1131_E_B,特殊技形态B,特殊技:集合啦!,,快速展旗,1.401,0.128,2.809,3.065,3.321,1.401,0.064,2.105,2.233,2.361,0,0,0,0,0,0,31.05,38.5275,70.02,0,0,7001,1,2,2,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1131,苍角,强化特殊技,1131_E_C,特殊技形态C,特殊技:集合啦!,,收旗攻击,2.45,0.223,4.903,5.349,5.795,2.45,0.112,3.682,3.906,4.13,0,0,0,0,0,0,61.25,67.375,122.5,0,0,12250,1,2,2,1,0,148,11,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1131,苍角,强化特殊技,1131_E_EX_1,第1段强化特殊技,强化特殊技:扇走蚊虫,,连续“扇巴掌”的第一段,要先激活旗子,所以会长一些,1.312,0.1195,2.6265,2.8655,3.1045,1.129,0.0515,1.6955,1.7985,1.9015,60,30,0,0,0,0,24.85,41.1125,57.99,0,0,12203.5,1,2,2,1,0,80,4,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1131,苍角,强化特殊技,1131_E_EX_2,第2段强化特殊技,强化特殊技:扇走蚊虫,,,1.312,0.1195,2.6265,2.8655,3.1045,1.129,0.0515,1.6955,1.7985,1.9015,30,30,0,0,0,0,24.85,41.1125,57.99,0,0,12203.5,1,2,2,1,0,64,4,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1131,苍角,强化特殊技,1131_E_EX_3,第3段强化特殊技,强化特殊技:扇走蚊虫,,第三段竖劈,位于强化特殊技连续“扇巴掌”结束后自动衔接,倍率为特殊技第2段,该段并未标注在强化特殊技文本中,1.001,0.091,2.002,2.184,2.366,1.001,0.046,1.507,1.599,1.691,30,30,0,0,0,0,25.03,27.5275,50.02,0,0,5001,1,2,2,1,0,56,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1131,苍角,强化特殊技,1131_E_EX_A,强化E形态A,特殊技:集合啦!,,风场,1.021,0.093,2.044,2.23,2.416,0.879,0.04,1.319,1.399,1.479,0,0,0,0,0,0,42.6,31.9825,27.07,0,0,9491,1,2,2,1,0,0,1,TRUE,TRUE,0,0,,,,1,15,,,TRUE,1,FALSE,
1131,苍角,冲刺攻击,1131_RA,冲刺攻击,冲刺攻击:对半分,,,0.767,0.07,1.537,1.677,1.817,0.384,0.018,0.582,0.618,0.654,0,0,1.38,0,0,0,9.8,10.56,38.32,0,0,0,2,3,0,0,0,46,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1131,苍角,冲刺攻击,1131_SRA,特殊冲刺攻击,冲刺攻击:对半分(霜染刃旗),,,1.315,0.12,2.635,2.875,3.115,0.801,0.037,1.208,1.282,1.356,0,0,2.882,0,0,0,19.6,22.0275,40.02,0,0,8003,2,3,2,1,0,60,2,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1131,苍角,闪避反击,1131_CA,闪避反击,闪避反击:别抢零食,,,2.473,0.225,4.948,5.398,5.848,2.134,0.097,3.201,3.395,3.589,0,0,4.079,0,0,0,29.85,31.185,263.31,0,0,11330,2,4,2,1,0,48,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1131,苍角,连携技,1131_QTE,连携技,连携技:鹅鸡斩,,,7.458,0.678,14.916,16.272,17.628,3.468,0.158,5.206,5.522,5.838,0,0,0,0,0,0,204.33,278.245,346.71,0,0,54620,3,5,2,1,0,256,18,TRUE,TRUE,0,256,,,,1,0,,,TRUE,1,TRUE,
1131,苍角,终结技,1131_Q,终结技,终结技:大份鹅鸡斩,,,19.898,1.809,39.797,43.415,47.033,3.768,0.172,5.66,6.004,6.348,0,0,0,0,0,0,0,0,876.71,0,0,37670,3,6,2,1,0,0,1,TRUE,TRUE,0,0,,,,1,0,,,TRUE,1,TRUE,
1131,苍角,受击支援,1131_BH_Aid,受击支援,快速支援:双人套餐,,,1.134,0.104,2.278,2.486,2.694,1.134,0.052,1.706,1.81,1.914,0,0,4.079,0,0,0,29.85,31.185,56.66,0,0,11330,5,7,2,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1131,苍角,招架/回避支援,1131_Light_parry_Aid,轻招架,招架支援:防守战术,,,0,0,0,0,0,2.467,0.113,3.71,3.936,4.162,0,0,0,0,0,0,16.68,0,366.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1131,苍角,招架/回避支援,1131_Heavy_parry_Aid,重招架,招架支援:防守战术,,,0,0,0,0,0,3.117,0.142,4.679,4.963,5.247,0,0,0,0,0,0,29.18,0,416.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1131,苍角,招架/回避支援,1131_Chain_parry_Aid,连续招架,招架支援:防守战术,,,0,0,0,0,0,1.517,0.069,2.276,2.414,2.552,0,0,0,0,0,0,29.18,0,116.64,0,0,0,5,8,0,1,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1131,苍角,突击支援,1131_Assault_Aid_1,突击支援,支援突击:席卷打击,,A,2.64,0.24,5.28,5.76,6.24,2.313,0.106,3.479,3.691,3.903,0,0,0,0,0,0,107.85,81.5925,132.98,0,0,24757,5,9,2,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1131,苍角,突击支援,1131_Assault_Aid_2,突击支援,支援突击:席卷打击,,B,1.132,0.103,2.265,2.471,2.677,0.991,0.046,1.497,1.589,1.681,0,0,0,0,0,0,107.85,34.98,56.99,0,0,10610,5,9,2,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1061,可琳,普攻,1061_NA_1,第1段普攻,,,,0.82,0.075,1.645,1.795,1.945,0.41,0.019,0.619,0.657,0.695,0,0,1.476,0,0,0,9.93,11.275,41,0,0,4099,0,0,0,1,0,51,4,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1061,可琳,普攻,1061_NA_2,第2段普攻,,,,0.766,0.07,1.536,1.676,1.816,0.702,0.032,1.054,1.118,1.182,0,0,2.526,0,0,0,16.93,19.305,70.17,0,0,7016,0,0,0,1,0,32,4,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1061,可琳,普攻,1061_NA_3,第3段普攻,,,按打满计算,1.792,0.163,3.585,3.911,4.237,1.238,0.057,1.865,1.979,2.093,0,0,4.456,0,0,0,29.75,34.045,123.77,0,0,12376,0,0,0,1,0,75,4,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1061,可琳,普攻,1061_NA_4,第4段普攻,,,0.0,2.334,0.213,4.677,5.103,5.529,1.864,0.085,2.799,2.969,3.139,0,0,6.709,0,0,0,45.85,51.26,186.34,0,0,18633,0,0,0,1,0,90,8,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,可琳,普攻,0_NA_3_NFC,第3段普攻(不满),废弃,,,2.334,0.213,4.677,5.103,5.529,1.864,0.085,2.799,2.969,3.139,0,0,6.709,0,0,0,0,51.26,186.34,0,0,18633,0,0,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1061,可琳,普攻,1061_NA_5,第5段普攻,,,按打满计算,4.212,0.383,8.425,9.191,9.957,3.42,0.156,5.136,5.448,5.76,0,0,12.31,0,0,0,84.38,94.05,341.95,0,0,34194,0,0,0,1,0,103,7,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
0,可琳,普攻,0_NA_5_NFC,第5段普攻(不满),废弃,,,4.212,0.383,8.425,9.191,9.957,3.42,0.156,5.136,5.448,5.76,0,0,12.31,0,0,0,0,94.05,341.95,0,0,34194,0,0,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1061,可琳,特殊技,1061_E_1,第1段特殊技,回旋斩击,,0.0,0.667,0.061,1.338,1.46,1.582,0.667,0.031,1.008,1.07,1.132,0,0,0,0,0,0,16.48,18.3425,66.69,0,0,6668,1,1,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1061,可琳,特殊技,1061_E_2,第2段特殊技,持续斩击最大,,0.0,0.375,0.035,0.76,0.83,0.9,0.375,0.018,0.573,0.609,0.645,0,0,0,0,0,0,9.38,10.3125,37.5,0,0,3750,1,1,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,可琳,特殊技,0_E_A,特殊技形态A,废弃,,第2段持续斩击蓄力不满,倍率并未明示,只能通过对比技能跳数来反推。蓄满12跳,不蓄满8跳,已知第一段4跳,最后一段1跳,那么中间段跳数比例应该是7:3,等比缩小倍率,0.25,0.023,0.503,0.549,0.595,0.25,0.012,0.382,0.406,0.43,0,0,0,0,0,0,4.02,6.875,25,0,0,2499,1,1,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1061,可琳,特殊技,1061_E_3,第3段特殊技,爆炸,,0.0,0.25,0.023,0.503,0.549,0.595,0.25,0.012,0.382,0.406,0.43,0,0,0,0,0,0,6.13,6.875,25,0,0,2499,1,1,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,可琳,特殊技,0_E_NFC,特殊技(不满),废弃,,该动作包含了特殊技1段、2段不完整版、爆炸,0.25,0.023,0.503,0.549,0.595,0.25,0.012,0.382,0.406,0.43,0,0,0,0,0,0,26.62,6.875,25,0,0,2499,1,1,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
0,可琳,特殊技,0_E_FC,特殊技(满),废弃,,该动作包含了特殊技1段、2段完整版、爆炸,0.25,0.023,0.503,0.549,0.595,0.25,0.012,0.382,0.406,0.43,0,0,0,0,0,0,31.98,6.875,25,0,0,2499,1,1,0,1,0,139,12,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1061,可琳,强化特殊技,1061_E_EX_1,第1段强化特殊技,回旋斩击,,0.0,3.451,0.314,6.905,7.533,8.161,2.064,0.094,3.098,3.286,3.474,80,20,0,0,0,0,62.65,68.9425,93.01,0,0,20333,1,2,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1061,可琳,强化特殊技,1061_E_EX_2,第2段强化特殊技,持续斩击最大,,0.0,10.352,0.942,20.714,22.598,24.482,6.19,0.282,9.292,9.856,10.42,60,40,0,0,0,0,187.9,206.7725,279.03,0,0,61001,1,2,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,可琳,强化特殊技,0_E_EX_A,强化E形态A,废弃,,第2段持续斩击蓄力不满,倍率并未明示,只能通过对比技能跳数来反推。蓄满32跳,不蓄满8跳,已知第一段4跳,最后一段1跳,那么中间段跳数比例应该是9:1,等比缩小倍率,3.451,0.314,6.905,7.533,8.161,2.064,0.094,3.098,3.286,3.474,5,5,0,0,0,0,20.88,68.9425,93.01,0,0,20333,1,2,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1061,可琳,强化特殊技,1061_E_EX_3,第3段强化特殊技,爆炸,,0.0,3.451,0.314,6.905,7.533,8.161,2.064,0.094,3.098,3.286,3.474,20,20,0,0,0,0,62.65,68.9425,93.01,0,0,20333,1,2,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,可琳,强化特殊技,0_E_EX_NFC,强化特殊技(不满),废弃,,该动作包含了强化特殊技1段、2段不完整版、爆炸,3.451,0.314,6.905,7.533,8.161,2.064,0.094,3.098,3.286,3.474,80,45,0,0,0,0,146.18,68.9425,93.01,0,0,20333,1,2,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
0,可琳,强化特殊技,0_E_EX_FC,强化特殊技(满),废弃,,该动作包含了强化特殊技1段、2段完整版、爆炸,3.451,0.314,6.905,7.533,8.161,2.064,0.094,3.098,3.286,3.474,80,80,0,0,0,0,313.2,68.9425,93.01,0,0,20333,1,2,0,1,0,262,42,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1061,可琳,冲刺攻击,1061_RA,冲刺攻击,,,可琳的闪避反击是可以长按的,此处只记录了按满的总倍率,0.967,0.088,1.935,2.111,2.287,0.484,0.022,0.726,0.77,0.814,0,0,1.741,0,0,0,11.73,13.31,48.35,0,0,4834,2,3,0,1,0,86,8,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1061,可琳,闪避反击,1061_CA,闪避反击,A,,可琳的冲刺攻击是可以长按的,此处只记录了按满的总倍率,喧响值实验,打了5次,414点,1.356,0.124,2.72,2.968,3.216,0.659,0.03,0.989,1.049,1.109,0,0,2.37,0,0,0,82.8,18.1225,140.82,0,0,6581,2,4,0,1,0,64,10,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1061,可琳,连携技,1061_QTE,连携技,,,,6.873,0.625,13.748,14.998,16.248,2.883,0.132,4.335,4.599,4.863,0,0,0,0,0,0,188.45,262.1575,288.3,0,0,48779,3,5,0,1,0,155,14,TRUE,TRUE,0,155,,,,0,0,,,TRUE,1,TRUE,
1061,可琳,终结技,1061_Q,终结技,,,,20.288,1.845,40.583,44.273,47.963,4.068,0.185,6.103,6.473,6.843,0,0,0,0,0,0,0,0,906.71,0,0,40670,3,6,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1061,可琳,普攻,1061_BH_Aid,受击支援,A,,,1.317,0.12,2.637,2.877,3.117,1.317,0.06,1.977,2.097,2.217,0,0,4.739,0,0,0,20.03,36.2175,65.82,0,0,13163,0,0,0,0,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1061,可琳,普攻,1061_Light_parry_Aid,轻招架,,,,0,0,0,0,0,2.467,0.113,3.71,3.936,4.162,0,0,0,0,0,0,16.68,0,366.64,0,0,0,0,0,0,0,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1061,可琳,普攻,1061_Heavy_parry_Aid,重招架,,,,0,0,0,0,0,3.117,0.142,4.679,4.963,5.247,0,0,0,0,0,0,291.75,0,416.64,0,0,0,0,0,0,0,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1061,可琳,普攻,1061_Chain_parry_Aid,连续招架,,,,0,0,0,0,0,1.517,0.069,2.276,2.414,2.552,0,0,0,0,0,0,291.75,0,116.64,0,0,0,0,0,0,0,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1061,可琳,普攻,1061_Assault_Aid,突击支援,,,,5.475,0.498,10.953,11.949,12.945,4.886,0.223,7.339,7.785,8.231,0,0,0,0,0,0,151.63,166.7875,311.71,0,0,51801,0,0,0,0,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1021,猫又,普攻,1021_NA_1,第1段普攻,,,,0.552,0.051,1.113,1.215,1.317,0.18,0.009,0.279,0.297,0.315,0,0,0.616,0,0,0,4.1,4.7025,17.09,0,0,1708,0,0,0,1,0,23,2,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1021,猫又,普攻,1021_NA_2,第2段普攻,,,,0.626,0.057,1.253,1.367,1.481,0.371,0.017,0.558,0.592,0.626,0,0,1.269,0,0,0,8.45,9.7075,35.25,0,0,3524,0,0,0,1,0,25,2,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1021,猫又,普攻,1021_NA_3,第3段普攻,,,,0.727,0.067,1.464,1.598,1.732,0.467,0.022,0.709,0.753,0.797,0,0,1.599,0,0,0,10.65,12.2375,44.41,0,0,4440,0,0,0,1,0,29,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1021,猫又,普攻,1021_NA_4,第4段普攻,,,,1.702,0.155,3.407,3.717,4.027,1.037,0.048,1.565,1.661,1.757,0,0,3.555,0,0,0,24.33,27.17,98.74,0,0,9873,0,0,0,1,0,55,4,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1021,猫又,普攻,1021_NA_5,第5段普攻,,,,1.236,0.113,2.479,2.705,2.931,0.589,0.027,0.886,0.94,0.994,0,0,2.017,0,0,0,14.03,15.4275,58.98,0,0,5602,0,0,0,1,0,28,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1021,猫又,普攻,1021_SNA,特殊普攻,,,,0.718,0.066,1.444,1.576,1.708,0.589,0.027,0.886,0.94,0.994,0,0,2.017,0,0,0,14.03,15.4275,58.98,0,0,5602,0,0,0,1,0,36,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1021,猫又,特殊技,1021_E,特殊技,,,,0.473,0.043,0.946,1.032,1.118,0.473,0.022,0.715,0.759,0.803,0,0,0,0,0,0,11.05,12.4025,45.02,0,0,4501,1,1,0,1,0,75,5,TRUE,TRUE,0,116,,,,0,0,,,TRUE,1,FALSE,
1021,猫又,强化特殊技,1021_E_EX,强化特殊技,,,,5.397,0.491,10.798,11.78,12.762,4.554,0.207,6.831,7.245,7.659,40,40,0,0,0,0,134.45,148.61,175.04,0,0,43073,1,2,0,1,0,116,7,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1021,猫又,冲刺攻击,1021_RA,冲刺攻击,,,,0.351,0.032,0.703,0.767,0.831,0.176,0.008,0.264,0.28,0.296,0,0,0.601,0,0,0,14.6,16.06,16.69,0,0,1668,2,3,0,1,0,20,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1021,猫又,闪避反击,1021_CA,闪避反击,,,,2.279,0.208,4.567,4.983,5.399,1.995,0.091,2.996,3.178,3.36,0,0,3.239,0,0,0,22.1,24.75,239.97,0,0,8996,2,4,0,1,0,74,8,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1021,猫又,连携技,1021_QTE,连携技,,,,5.362,0.488,10.73,11.706,12.682,1.592,0.073,2.395,2.541,2.687,0,0,0,0,0,0,158.05,229.185,168.37,0,0,36786,3,5,0,1,0,111,9,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1021,猫又,终结技,1021_Q,终结技,,,,15.711,1.429,31.43,34.288,37.146,1.181,0.054,1.775,1.883,1.991,0,0,0,0,0,0,0,0,624.97,0,0,12496,3,6,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1021,猫又,受击支援,1021_BH_Aid,受击支援,,,,0.945,0.086,1.891,2.063,2.235,0.945,0.043,1.418,1.504,1.59,0,0,3.239,0,0,0,22.1,24.75,44.99,0,0,8996,5,7,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1021,猫又,招架/回避支援,1021_Light_parry_Aid,轻招架,,,,0,0,0,0,0,2.59,0.118,3.888,4.124,4.36,0,0,0,0,0,0,16.68,0,366.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1021,猫又,招架/回避支援,1021_Heavy_parry_Aid,重招架,,,,0,0,0,0,0,3.273,0.149,4.912,5.21,5.508,0,0,0,0,0,0,29.18,0,416.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1021,猫又,招架/回避支援,1021_Chain_parry_Aid,连续招架,,,,0,0,0,0,0,1.593,0.073,2.396,2.542,2.688,0,0,0,0,0,0,29.18,0,116.64,0,0,0,5,8,0,1,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1021,猫又,突击支援,1021_Assault_Aid,突击支援,,,,3.004,0.274,6.018,6.566,7.114,2.581,0.118,3.879,4.115,4.351,0,0,0,0,0,0,81.6,89.76,124.97,0,0,26592,5,9,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1121,本,普攻,1121_NA_1,第1段普攻,普通攻击:对账,,一段,0.659,0.06,1.319,1.439,1.559,0.471,0.022,0.713,0.757,0.801,0,0,1.694,0,0,0,11.78,12.9525,47.06,0,0,0,0,0,0,0,0,60,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1121,本,普攻,1121_NA_2,第2段普攻,普通攻击:对账,,二段,1.89,0.172,3.782,4.126,4.47,1.569,0.072,2.361,2.505,2.649,0,0,5.646,0,0,0,39.23,43.1475,156.81,0,0,0,0,0,0,0,0,84,4,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1121,本,普攻,1121_NA_3,第3段普攻,普通攻击:对账,,三段,3.483,0.317,6.97,7.604,8.238,2.601,0.119,3.91,4.148,4.386,0,0,9.363,0,0,0,65.03,71.5275,260.08,0,0,0,0,0,0,0,0,105,3,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1121,本,特殊技,1121_E_1,第1段特殊技,特殊技:拳债统计,格挡,主动攻击,0.417,0.038,0.835,0.911,0.987,0.417,0.019,0.626,0.664,0.702,0,0,0,0,0,0,10.43,11.4675,41.66,0,0,0,1,1,0,1,0,25,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1121,本,特殊技,1121_E_2,第2段特殊技,特殊技:拳债统计,反击,也就是“挠一爪子”,在和柯蕾妲的联动中会出现,格挡反击,2.334,0.213,4.677,5.103,5.529,2.234,0.102,3.356,3.56,3.764,0,0,0,0,0,0,20.85,22.935,83.31,0,0,0,1,1,0,1,0,51,2,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1121,本,强化特殊技,1121_E_EX_1,第1段强化特殊技,强化特殊技:到期还拳,强化格挡,主动攻击,4.385,0.399,8.774,9.572,10.37,2.471,0.113,3.714,3.94,4.166,60,30,0,0,0,0,85.7,94.27,103.52,0,0,27099,1,2,1,1,0,90,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1121,本,强化特殊技,1121_E_EX_2,第2段强化特殊技,强化特殊技:到期还拳,强化反击,追加攻击,4.385,0.399,8.774,9.572,10.37,2.471,0.113,3.714,3.94,4.166,60,30,0,0,0,0,85.7,94.27,103.52,0,0,27099,1,2,1,1,0,72,3,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1121,本,强化特殊技,1121_E_EX_A_1,强化E形态A第1段,强化特殊技:到期还拳,强化完美格挡,格挡反击,5.005,0.455,10.01,10.92,11.83,3.676,0.168,5.524,5.86,6.196,30,30,0,0,0,0,102.3,112.53,147.76,0,0,33071,1,2,1,1,0,77,2,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1121,本,强化特殊技,1121_E_EX_A_2,强化E形态A第2段,强化特殊技:到期还拳,强化完美反击,格挡追击,5.512,0.502,11.034,12.038,13.042,3.676,0.168,5.524,5.86,6.196,30,30,0,0,0,0,102.3,112.53,147.76,0,0,33071,1,2,1,1,0,117,6,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1121,本,冲刺攻击,1121_RA,冲刺攻击,冲刺攻击:前来报销,,,1.384,0.126,2.77,3.022,3.274,0.692,0.032,1.044,1.108,1.172,0,0,2.491,0,0,0,17.3,19.03,69.17,0,0,0,2,3,0,0,0,72,5,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1121,本,闪避反击,1121_CA,闪避反击,闪避反击:清算,,,2.257,0.206,4.523,4.935,5.347,1.967,0.09,2.957,3.137,3.317,0,0,3.481,0,0,0,24.18,26.5925,246.67,0,0,9666,2,4,1,1,0,59,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1121,本,连携技,1121_QTE,连携技,连携技:盖章,结算,,,6.273,0.571,12.554,13.696,14.838,3.283,0.15,4.933,5.233,5.533,0,0,0,0,0,0,173.45,245.6575,228.3,0,0,42779,3,5,1,1,0,128,10,TRUE,TRUE,0,128,,,,0,0,,,TRUE,1,TRUE,
1121,本,终结技,1121_Q,终结技,终结技:拳债,全面清偿,,,16.43,1.494,32.864,35.852,38.84,1.1,0.05,1.65,1.75,1.85,0,0,0,0,0,0,0,0,610,0,0,11000,3,6,1,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1121,本,受击支援,1121_BH_Aid,受击支援,快速支援:联合追债,,,0.967,0.088,1.935,2.111,2.287,0.967,0.044,1.451,1.539,1.627,0,0,3.481,0,0,0,24.18,26.5925,48.34,0,0,9666,5,7,1,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1121,本,招架/回避支援,1121_Light_parry_Aid,轻招架,招架支援:分摊风险,,轻招架,0,0,0,0,0,2.251,0.103,3.384,3.59,3.796,0,0,0,0,0,0,12.53,0,350.04,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1121,本,招架/回避支援,1121_Heavy_parry_Aid,重招架,招架支援:分摊风险,,重招架,0,0,0,0,0,2.684,0.122,4.026,4.27,4.514,0,0,0,0,0,0,20.85,0,383.34,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1121,本,招架/回避支援,1121_Chain_parry_Aid,连续招架,招架支援:分摊风险,,连续招架,0,0,0,0,0,1.084,0.05,1.634,1.734,1.834,0,0,0,0,0,0,20.85,0,83.34,0,0,0,5,8,0,1,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1121,本,突击支援,1121_Assault_Aid,突击支援,支援突击:违约惩罚,,,3.259,0.297,6.526,7.12,7.714,2.828,0.129,4.247,4.505,4.763,0,0,0,0,0,0,92.25,101.475,153.37,0,0,30426,5,9,1,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1211,丽娜,普攻,1211_NA_1,第1段普攻,普通攻击:痛打呆子,,一段,0.44,0.04,0.88,0.96,1.04,0.245,0.012,0.377,0.401,0.425,0,0,0.838,0,0,0,8.75,6.4075,23.28,0,0,0,0,0,0,0,0,38,6,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1211,丽娜,普攻,1211_NA_2,第2段普攻,普通攻击:痛打呆子,,二段,1.114,0.102,2.236,2.44,2.644,0.884,0.041,1.335,1.417,1.499,0,0,3.029,0,0,0,15.03,16.5275,67.84,0,0,0,0,0,0,0,0,39,4,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1211,丽娜,普攻,1211_NA_3,第3段普攻,普通攻击:痛打呆子,,三段,1.171,0.107,2.348,2.562,2.776,0.966,0.044,1.45,1.538,1.626,0,0,3.31,0,0,0,35.1,38.61,91.94,0,0,8363,0,0,3,1,0,47,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1211,丽娜,普攻,1211_NA_4,第4段普攻,普通攻击:痛打呆子,,四段,1.839,0.168,3.687,4.023,4.359,1.468,0.067,2.205,2.339,2.473,0,0,5.59,0,0,0,31.08,34.1825,155.28,0,0,10474,0,0,3,1,0,136,24,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1211,丽娜,普攻,1211_SNA_1,第1段特殊普攻,普通攻击:赶走傻瓜,,,3.151,0.287,6.308,6.882,7.456,3.151,0.144,4.735,5.023,5.311,0,0,10.802,0,0,0,75.03,82.5275,300.04,0,0,30003,0,0,0,0,0,136,20,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1211,丽娜,特殊技,1211_E,特殊技,特殊技:砸扁笨蛋,,,0.613,0.056,1.229,1.341,1.453,0.613,0.028,0.921,0.977,1.033,0,0,0,0,0,0,14.6,16.06,58.36,0,0,5835,1,1,3,1,0,90,6,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1211,丽娜,强化特殊技,1211_E_EX,强化特殊技,强化特殊技:笨蛋消失魔法,,,5.46,0.497,10.927,11.921,12.915,4.445,0.203,6.678,7.084,7.49,60,60,0,0,0,0,104.18,114.5925,133.3,0,0,47162,1,2,3,1,0,81,15,TRUE,TRUE,0,0,,,,1,15,,,TRUE,1,FALSE,
1211,丽娜,连携技,1211_QTE,连携技,连携技:侍者守则,,,10.13,0.921,20.261,22.103,23.945,1.751,0.08,2.631,2.791,2.951,0,0,0,0,0,0,158.08,228.745,166.71,0,0,36620,3,5,3,1,0,93,16,TRUE,TRUE,0,93,,,,1,15,,,TRUE,1,TRUE,
1211,丽娜,终结技,1211_Q,终结技,终结技:女王的侍从们,,,21.167,1.925,42.342,46.192,50.042,1.138,0.052,1.71,1.814,1.918,0,0,0,0,0,0,0,0,733.38,0,0,10837,3,6,3,1,0,0,1,TRUE,TRUE,0,0,,,,1,15,,,TRUE,1,TRUE,
1211,丽娜,冲刺攻击,1211_RA,冲刺攻击,冲刺攻击:突然惊吓,,,1.05,0.096,2.106,2.298,2.49,0.525,0.024,0.789,0.837,0.885,0,0,1.8,0,0,0,12.5,13.75,50,0,0,0,2,3,0,0,0,65,6,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1211,丽娜,闪避反击,1211_CA,闪避反击,闪避反击:邦布回魂,,,2.276,0.207,4.553,4.967,5.381,2.276,0.104,3.42,3.628,3.836,0,0,4.202,0,0,0,27.53,32.12,266.71,0,0,11670,2,4,3,1,0,72,6,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1211,丽娜,受击支援,1211_BH_Aid,受击支援,快速支援:二拍的阿勒芒德,,,1.226,0.112,2.458,2.682,2.906,1.226,0.056,1.842,1.954,2.066,0,0,4.202,0,0,0,27.53,32.12,58.36,0,0,11670,5,7,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1211,丽娜,突击支援,1211_Parry_Aid,回避支援,支援突击:四拍的加沃特,,,3.494,0.318,6.992,7.628,8.264,3.036,0.138,4.554,4.83,5.106,0,0,0,0,0,0,94.1,103.51,158.3,0,0,31092,5,9,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1181,格莉丝,普攻,1181_NA_1,第1段普攻,普通攻击:高压射钉,,一段,0.551,0.051,1.112,1.214,1.316,0.18,0.009,0.279,0.297,0.315,0,0,0.615,0,0,0,4.28,4.7025,17.08,0,0,0,0,0,0,0,0,25,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1181,格莉丝,普攻,1181_NA_2,第2段普攻,普通攻击:高压射钉,,二段,0.597,0.055,1.202,1.312,1.422,0.347,0.016,0.523,0.555,0.587,0,0,1.189,0,0,0,8.28,9.1025,33.02,0,0,0,0,0,0,0,0,24,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1181,格莉丝,普攻,1181_NA_3,第3段普攻,普通攻击:高压射钉,,三段,1.248,0.114,2.502,2.73,2.958,0.716,0.033,1.079,1.145,1.211,0,0,2.454,0,0,0,17.05,18.755,68.17,0,0,6460,0,0,3,0,0,37,5,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1181,格莉丝,普攻,1181_NA_4,第4段普攻,普通攻击:高压射钉,,四段,1.863,0.17,3.733,4.073,4.413,1.072,0.049,1.611,1.709,1.807,0,0,4.081,0,0,0,28.35,31.185,113.35,0,0,0,0,0,0,0,0,59,5,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1181,格莉丝,普攻,1181_SNA_1,第1段特殊普攻,普通攻击:高压射钉,,垫步射击,0.403,0.037,0.81,0.884,0.958,0.403,0.019,0.612,0.65,0.688,0,0,1.38,0,0,0,1.04,10.56,38.34,0,0,0,0,0,0,0,0,23,5,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1181,格莉丝,特殊技,1181_E,特殊技,特殊技:工程清障,,,0.421,0.039,0.85,0.928,1.006,0.421,0.02,0.641,0.681,0.721,0,0,0,0,0,0,10.03,11.0275,20.02,0,0,7003,1,1,3,0,0,35,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1181,格莉丝,强化特殊技,1181_E_EX,强化特殊技,强化特殊技:超规工程清障,,此处是双倍的倍率和成长,因为一次性有两颗手雷,角色说明面板里面也是这么标注的,1.669,0.152,3.341,3.645,3.949,1.342,0.061,2.013,2.135,2.257,40,40,0,0,0,0,47.55,52.305,34.17,0,0,14334,1,2,3,0,0,49,4,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1181,格莉丝,特殊技,1181_R+E,闪E,特殊技:工程清障,,,0.421,0.039,0.85,0.928,1.006,0.421,0.02,0.641,0.681,0.721,0,0,0,0,0,0,10.03,11.0275,20.02,0,0,7003,1,1,3,0,0,14,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1181,格莉丝,连携技,1181_QTE,连携技,连携技:协作施工,,,5.713,0.52,11.433,12.473,13.513,1.523,0.07,2.293,2.433,2.573,0,0,0,0,0,0,152.65,222.7775,145.01,0,0,34450,3,5,3,0,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1181,格莉丝,终结技,1181_Q,终结技,终结技:工程爆破请勿接近,,,14.788,1.345,29.583,32.273,34.963,1.331,0.061,2.002,2.124,2.246,0,0,0,0,0,0,0,0,626.7,0,0,89570,3,6,3,0,0,83,9,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1181,格莉丝,冲刺攻击,1181_RA,冲刺攻击,冲刺攻击:突击检查,,,0.333,0.031,0.674,0.736,0.798,0.167,0.008,0.255,0.271,0.287,0,0,0.571,0,0,0,3.98,4.3725,15.86,0,0,0,2,3,0,0,0,18,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1181,格莉丝,闪避反击,1181_CA,闪避反击,闪避反击:违章处罚,,,1.642,0.15,3.292,3.592,3.892,1.505,0.069,2.264,2.402,2.54,0,0,1.56,0,0,0,10.03,11.935,193.34,0,0,4333,2,4,3,0,0,33,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1181,格莉丝,受击支援,1181_BH_Aid,受击支援,快速支援:事故解决方案,,,0.455,0.042,0.917,1.001,1.085,0.455,0.021,0.686,0.728,0.77,0,0,1.56,0,0,0,10.03,11.935,21.67,0,0,4333,5,7,3,0,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1181,格莉丝,突击支援,1181_Parry_Aid,回避支援,支援突击:反击电针,,,3.593,0.327,7.19,7.844,8.498,3.128,0.143,4.701,4.987,5.273,0,0,0,0,0,0,96.6,106.2875,165.04,0,0,32001,5,9,3,0,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1111,安东,普攻,1111_NA_1,第1段普攻,普通攻击:热血上工操,,一段,0.678,0.062,1.36,1.484,1.608,0.339,0.016,0.515,0.547,0.579,0,0,1.22,0,0,0,8.28,9.3225,33.89,0,0,0,0,0,0,0,0,43,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1111,安东,普攻,1111_NA_2,第2段普攻,普通攻击:热血上工操,,二段,0.923,0.084,1.847,2.015,2.183,0.753,0.035,1.138,1.208,1.278,0,0,2.709,0,0,0,18.43,20.7075,75.23,0,0,0,0,0,0,0,0,41,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1111,安东,普攻,1111_NA_3,第3段普攻,普通攻击:热血上工操,,三段,1.098,0.1,2.198,2.398,2.598,0.933,0.043,1.406,1.492,1.578,0,0,3.357,0,0,0,22.93,25.6575,93.25,0,0,0,0,0,0,0,0,50,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1111,安东,普攻,1111_NA_4,第4段普攻,普通攻击:热血上工操,,四段,2.291,0.209,4.59,5.008,5.426,1.814,0.083,2.727,2.893,3.059,0,0,6.53,0,0,0,44.4,49.885,181.38,0,0,0,0,0,0,0,0,88,3,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1111,安东,普攻,1111_NA_1_A,第1段普攻(形态A),普通攻击:热血上工操,这一段倍率和普攻第一段完全一样,只是动画时间不同。,,0.678,0.062,1.36,1.484,1.608,0.339,0.016,0.515,0.547,0.579,0,0,1.22,0,0,0,8.28,9.3225,33.89,0,0,0,0,0,0,0,0,43,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1111,安东,普攻,1111_SNA_1,第1段特殊普攻,普通攻击:热血上工操(爆发状态),,一段,2.409,0.219,4.818,5.256,5.694,1.66,0.076,2.496,2.648,2.8,0,5,0,0,0,0,53.05,58.355,96.67,0,0,17749,0,0,3,1,0,60,4,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,安东,普攻,0_SNA_2_NFC,第2段特殊普攻(不满),废弃,,,4.692,0.427,9.389,10.243,11.097,3.233,0.147,4.85,5.144,5.438,0,0,0,0,0,0,0,113.6575,188.31,0,0,34573,0,0,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1111,安东,普攻,1111_SNA_2_FC,第2段特殊普攻(满),普通攻击:热血上工操(爆发状态),,二段,4.692,0.427,9.389,10.243,11.097,3.233,0.147,4.85,5.144,5.438,0,12,0,0,0,0,102.05,113.6575,188.31,0,0,34573,0,0,3,1,0,100,28,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,安东,普攻,0_SNA_3_NFC,第3段特殊普攻(不满),废弃,,,4.569,0.416,9.145,9.977,10.809,3.148,0.144,4.732,5.02,5.308,0,0,0,0,0,0,0,110.66,183.37,0,0,33668,0,0,0,1,0,82,8,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1111,安东,普攻,1111_SNA_3_FC,第3段特殊普攻(满),普通攻击:热血上工操(爆发状态),,三段,4.569,0.416,9.145,9.977,10.809,3.148,0.144,4.732,5.02,5.308,0,13,0,0,0,0,100.6,110.66,183.37,0,0,33668,0,0,3,1,0,120,16,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1111,安东,冲刺攻击,1111_RA,冲刺攻击,冲刺攻击:硬碰硬,,,1.951,0.178,3.909,4.265,4.621,1.951,0.089,2.93,3.108,3.286,0,0,0,0,0,0,0,53.6525,195.04,0,0,19503,2,3,0,0,0,40,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1111,安东,冲刺攻击,1111_SRA,特殊冲刺攻击,冲刺攻击:硬碰硬,,,2.409,0.219,4.818,5.256,5.694,1.66,0.076,2.496,2.648,2.8,0,0,0,0,0,0,0,58.355,96.67,0,0,17749,2,3,0,1,0,60,4,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1111,安东,闪避反击,1111_CA_1,闪避反击第1段,闪避反击:回敬拳击,,一段,1.356,0.1235,2.7145,2.9615,3.2085,1.1585,0.053,1.7415,1.8475,1.9535,0,0,2.3705,0,0,0,0,18.10875,140.835,0,0,0,2,4,0,0,0,80,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1111,安东,闪避反击,1111_CA_2,闪避反击第2段,闪避反击:回敬拳击,,二段,1.356,0.1235,2.7145,2.9615,3.2085,1.1585,0.053,1.7415,1.8475,1.9535,0,0,2.3705,0,0,0,0,18.10875,140.835,0,0,0,2,4,0,0,0,86,2,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1111,安东,闪避反击,1111_SCA,特殊闪避反击,闪避反击:过载钻击(爆发状态),,,4.654,0.424,9.318,10.166,11.014,3.518,0.16,5.278,5.598,5.918,0,12,0,0,0,0,0,88.495,296.64,0,0,26923,2,4,0,1,0,88,20,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1111,安东,连携技,1111_QTE,连携技,连携技:转转转!,,,6.407,0.583,12.82,13.986,15.152,2.417,0.11,3.627,3.847,4.067,0,0,0,0,0,0,0,249.3425,241.64,0,0,44113,3,5,0,1,0,138,6,TRUE,FALSE,0,138,,,,0,0,,,TRUE,1,TRUE,
1111,安东,终结技,1111_Q,终结技,终结技:转转转转转!,,,18.164,1.652,36.336,39.64,42.944,2.434,0.111,3.655,3.877,4.099,0,0,0,0,0,0,0,0,743.37,0,0,24336,3,6,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,TRUE,
1111,安东,特殊技,1111_E,特殊技,特殊技:兄弟,转起来!,,,0.442,0.041,0.893,0.975,1.057,0.442,0.021,0.673,0.715,0.757,0,0,0,0,0,0,0,12.155,44.19,0,0,4418,1,1,0,1,0,56,2,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1111,安东,强化特殊技,1111_E_EX,强化特殊技,强化特殊技:兄弟,突破天际!,,,1.951,0.178,3.909,4.265,4.621,1.951,0.089,2.93,3.108,3.286,40,0,0,0,0,0,0,53.6525,195.04,0,0,19503,1,2,0,1,0,76,4,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1111,安东,受击支援,1111_BH_Aid,受击支援,快速支援:援护钻击(爆发状态),,,3.654,0.333,7.317,7.983,8.649,2.518,0.115,3.783,4.013,4.243,0,0,0,0,0,0,0,88.495,73.32,0,0,26923,5,7,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1111,安东,招架/回避支援,1111_Light_parry_Aid,轻招架,招架支援:护身锤,,轻招架,0,0,0,0,0,2.467,0.113,3.71,3.936,4.162,0,0,0,0,0,0,0,0,366.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1111,安东,招架/回避支援,1111_Heavy_parry_Aid,重招架,招架支援:护身锤,,重招架,0,0,0,0,0,3.117,0.142,4.679,4.963,5.247,0,0,0,0,0,0,0,0,416.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1111,安东,招架/回避支援,1111_Chain_parry_Aid,连续招架,招架支援:护身锤,,连续招架,0,0,0,0,0,1.517,0.069,2.276,2.414,2.552,0,0,0,0,0,0,0,0,116.64,0,0,0,5,8,0,1,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1111,安东,突击支援,1111_Assault_Aid,突击支援,支援突击:极限突进,,,3.258,0.297,6.525,7.119,7.713,2.827,0.129,4.246,4.504,4.762,0,0,0,0,0,0,0,101.4475,153.34,0,0,30422,5,9,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1081,比利,普攻,1081_NA_1,第1段普攻,普通攻击:火力全开,,站姿开火,0.68,0.062,1.362,1.486,1.61,0.544,0.025,0.819,0.869,0.919,0,0,1.632,0,0,0,0,9.9825,67.99,0,0,5439,0,0,0,1,0,42,2,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1081,比利,普攻,1081_NA_2,第2段普攻,普通攻击:火力全开,,站姿子弹,0.076,0.007,0.153,0.167,0.181,0.061,0.003,0.094,0.1,0.106,0,0,0.544,0,0,0,0,3.3275,7.56,0,0,604,0,0,0,1,0,21,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1081,比利,普攻,1081_NA_3,第3段普攻,普通攻击:火力全开,,蹲姿开火,0.618,0.057,1.245,1.359,1.473,0.547,0.025,0.822,0.872,0.922,0,0,1.969,0,0,0,0,15.0425,53.67,0,0,2903,0,0,0,1,0,53,2,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1081,比利,普攻,1081_NA_4,第4段普攻,普通攻击:火力全开,,蹲姿子弹,0.127,0.012,0.259,0.283,0.307,0.089,0.005,0.144,0.154,0.164,0,0,0.321,0,0,0,0,2.4475,8.9,0,0,889,0,0,0,1,0,11,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1081,比利,普攻,1081_NA_5,第5段普攻,普通攻击:火力全开,,终结射击,0.495,0.045,0.99,1.08,1.17,0.552,0.026,0.838,0.89,0.942,0,0,3.971,0,0,0,0,15.18,38.05,0,0,8079,0,0,0,1,0,26,4,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1081,比利,特殊技,1081_E_1,第1段特殊技,特殊技:乖乖站好,,一段,0.242,0.022,0.484,0.528,0.572,0.242,0.011,0.363,0.385,0.407,0,0,0,0,0,0,0,6.655,24.15,0,0,2414,1,1,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1081,比利,特殊技,1081_E_2,第2段特殊技,特殊技:乖乖站好,,二段,0.517,0.047,1.034,1.128,1.222,0.517,0.024,0.781,0.829,0.877,0,0,0,0,0,0,0,14.2175,25.85,0,0,2584,1,1,0,1,0,32,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1081,比利,特殊技,1081_E_3,第3段特殊技,特殊技:乖乖站好,,三段,0.501,0.046,1.007,1.099,1.191,0.501,0.023,0.754,0.8,0.846,0,0,0,0,0,0,0,13.7775,25.01,0,0,2500,1,1,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1081,比利,强化特殊技,1081_E_EX,强化特殊技,强化特殊技:清场时间,,,5.438,0.495,10.883,11.873,12.863,4.395,0.2,6.595,6.995,7.395,60,60,0,0,0,0,0,162.03,114.97,0,0,44687,1,2,0,1,0,69,4,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1081,比利,连携技,1081_QTE,连携技,连携技:星徽荣耀幻影,,,7.352,0.669,14.711,16.049,17.387,1.966,0.09,2.956,3.136,3.316,0,0,0,0,0,0,0,242.935,218.34,0,0,41783,3,5,0,1,0,128,9,TRUE,FALSE,0,128,,,,0,0,,,TRUE,1,TRUE,
0,比利,普攻,0_QTE_PRE,连携技僵直,废弃,,废弃,7.352,0.669,14.711,16.049,17.387,1.966,0.09,2.956,3.136,3.316,0,0,0,0,0,0,0,242.935,218.34,0,0,41783,0,0,0,1,0,67,9,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1081,比利,普攻,1081_RA_F,前冲刺攻击,冲刺攻击:星-徽-制-裁,,直线射击,0.39,0.036,0.786,0.858,0.93,0.195,0.009,0.294,0.312,0.33,0,0,0.78,0,0,0,0,5.9675,21.65,0,0,4329,0,0,0,1,0,42,7,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1081,比利,普攻,1081_RA_B,后冲刺攻击,冲刺攻击:星-徽-制-裁,,周身射击,0.63,0.058,1.268,1.384,1.5,0.63,0.029,0.949,1.007,1.065,0,0,2.519,0,0,0,0,19.25,34.99,0,0,3498,0,0,0,1,0,26,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1081,比利,普攻,1081_CA,闪避反击,闪避反击:公平决斗,,,2.214,0.202,4.436,4.84,5.244,1.934,0.088,2.902,3.078,3.254,0,0,3.362,0,0,0,0,25.685,243.37,0,0,9336,0,0,0,1,0,62,3,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1081,比利,普攻,1081_BH_Aid,受击支援,快速支援:星徽-同伴之力,,,0.934,0.085,1.869,2.039,2.209,0.934,0.043,1.407,1.493,1.579,0,0,3.362,0,0,0,0,25.685,46.69,0,0,9336,0,0,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
0,比利,普攻,0_Light_parry_Aid,轻招架,废弃,,,0.934,0.085,1.869,2.039,2.209,0.934,0.043,1.407,1.493,1.579,0,0,3.362,0,0,0,0,25.685,46.69,0,0,9336,0,0,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
0,比利,普攻,0_Heavy_parry_Aid,重招架,废弃,,,0.934,0.085,1.869,2.039,2.209,0.934,0.043,1.407,1.493,1.579,0,0,3.362,0,0,0,0,25.685,46.69,0,0,9336,0,0,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
0,比利,普攻,0_Chain_parry_Aid,连续招架,废弃,,,0.934,0.085,1.869,2.039,2.209,0.934,0.043,1.407,1.493,1.579,0,0,3.362,0,0,0,0,25.685,46.69,0,0,9336,0,0,0,1,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1081,比利,普攻,0_Assault_Aid,突击支援,支援突击:要害射击,,,3.888,0.354,7.782,8.49,9.198,3.412,0.156,5.128,5.44,5.752,0,0,0,0,0,0,0,120.01,198.34,0,0,36497,0,0,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
0,妮可(弃),普攻,0_NA_1,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,0,4.29,15.59,0,0,0,0,0,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,妮可(弃),普攻,0_NA_2,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,0,4.29,15.59,0,0,0,0,0,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,妮可(弃),普攻,0_NA_3,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,0,4.29,15.59,0,0,0,0,0,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,妮可(弃),特殊技,0_E,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,13.15,4.29,15.59,0,0,0,1,1,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,妮可(弃),强化特殊技,0_E_EX_1,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,60,5,0.562,0,0,0,62.325,4.29,15.59,0,0,0,1,2,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,妮可(弃),强化特殊技,0_E_EX_2,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,60,37,0.562,0,0,0,62.33,4.29,15.59,0,0,0,1,2,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,妮可(弃),强化特殊技,0_E_EX_B,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,20,0.562,0,0,0,124.65,4.29,15.59,0,0,0,1,2,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,妮可(弃),强化特殊技,0_E_EX_A,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,0,4.29,15.59,0,0,0,1,2,0,0,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,妮可(弃),强化特殊技,0_E_EX_FC,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,0,4.29,15.59,0,0,0,1,2,0,0,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,妮可(弃),强化特殊技,0_E_EX_A_1,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,0,4.29,15.59,0,0,0,1,2,0,0,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,妮可(弃),强化特殊技,0_E_EX_B_1,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,0,4.29,15.59,0,0,0,1,2,0,0,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,妮可(弃),强化特殊技,0_E_EX_B_2,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,0,4.29,15.59,0,0,0,1,2,0,0,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,妮可(弃),冲刺攻击,0_RA,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,0,4.29,15.59,0,0,0,2,3,0,0,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,妮可(弃),闪避反击,0_CA,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,0,4.29,15.59,0,0,0,2,4,0,0,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,妮可(弃),连携技,0_QTE_1,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,58.8,4.29,15.59,0,0,0,3,5,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,妮可(弃),连携技,0_QTE_2,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,88.2,4.29,15.59,0,0,0,3,5,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,妮可(弃),连携技,0_QTE,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,147,4.29,15.59,0,0,0,3,5,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,妮可(弃),受击支援,0_BH_Aid,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,0,4.29,15.59,0,0,0,5,7,0,0,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
0,妮可(弃),招架/回避支援,0_Light_parry_Aid,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,0,4.29,15.59,0,0,0,5,8,0,0,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
0,妮可(弃),招架/回避支援,0_Heavy_parry_Aid,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,0,4.29,15.59,0,0,0,5,8,0,0,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
0,妮可(弃),招架/回避支援,0_Chain_parry_Aid,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,0,4.29,15.59,0,0,0,5,8,0,0,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
0,妮可(弃),突击支援,0_Assault_Aid,老数据不再使用,,,,0.312,0.029,0.631,0.689,0.747,0.156,0.008,0.244,0.26,0.276,0,0,0.562,0,0,0,0,4.29,15.59,0,0,0,5,9,0,0,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1281,派派,普攻,1281_NA_1,第1段普攻,普通攻击:准备发车,,一段,0.59,0.054,1.184,1.292,1.4,0.295,0.014,0.449,0.477,0.505,0,0,1.062,0,0,0,6.313,8.1125,29.49,0,0,2948,0,0,0,1,0,39,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1281,派派,普攻,1281_NA_2,第2段普攻,普通攻击:准备发车,,二段,0.788,0.072,1.58,1.724,1.868,0.645,0.03,0.975,1.035,1.095,0,0,2.319,0,0,0,13.803,17.7375,64.41,0,0,6440,0,0,0,1,0,40,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1281,派派,普攻,1281_NA_3,第3段普攻,普通攻击:准备发车,,三段,1.501,0.137,3.008,3.282,3.556,1.219,0.056,1.835,1.947,2.059,0,0,4.389,0,0,0,26.0866,33.5225,121.9,0,0,12189,0,0,0,1,0,81,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1281,派派,普攻,1281_NA_4,第4段普攻,普通攻击:准备发车,,四段,3.206,0.292,6.418,7.002,7.586,2.522,0.115,3.787,4.017,4.247,0,0,9.077,0,0,0,53.9708,69.355,252.12,0,0,25211,0,0,0,1,0,80,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1281,派派,特殊技,1281_E,特殊技,特殊技:有亿点重,,一级蓄力,0.934,0.085,1.869,2.039,2.209,0.934,0.043,1.407,1.493,1.579,0,0,0,0,0,0,19.9876,25.685,93.34,0,0,9333,1,1,0,1,0,83,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1281,派派,特殊技,1281_E_NFC,特殊技(不满),特殊技:有亿点重,,二级蓄力,1.034,0.094,2.068,2.256,2.444,1.034,0.047,1.551,1.645,1.739,0,0,0,0,0,0,22.1276,28.435,103.37,0,0,10336,1,1,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1281,派派,特殊技,1281_E_FC,特殊技(满),特殊技:有亿点重,,三级蓄力,2.35,0.214,4.704,5.132,5.56,2.35,0.107,3.527,3.741,3.955,0,0,0,0,0,0,50.29,64.625,235,0,0,23500,1,1,0,1,0,88,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1281,派派,强化特殊技,1281_E_EX,强化特殊技,强化特殊技:非常重,,,6.129,0.558,12.267,13.383,14.499,3.968,0.181,5.959,6.321,6.683,0,0,0,0,0,0,98.2046,126.1975,213.34,0,0,53255,1,2,0,1,0,84,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1281,派派,强化特殊技,1281_E_EX_A,强化E形态A,强化特殊技:引擎转,,每圈 *2,0.9355,0.0855,1.876,2.047,2.218,0.548,0.025,0.823,0.873,0.923,0,0,0,0,0,0,14.4343,18.54875,23.335,0,0,8062.5,1,2,0,1,0,63,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1281,派派,冲刺攻击,1281_RA,冲刺攻击,冲刺攻击:一脚油门,,,0.901,0.082,1.803,1.967,2.131,0.451,0.021,0.682,0.724,0.766,0,0,1.621,0,0,0,9.6514,12.4025,45.02,0,0,4501,2,3,0,1,0,56,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1281,派派,闪避反击,1281_CA,闪避反击,闪避反击:动力漂移,,,2.69,0.245,5.385,5.875,6.365,2.3,0.105,3.455,3.665,3.875,0,0,4.679,0,0,0,27.82,35.75,279.97,0,0,12996,2,4,0,1,0,101,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1281,派派,连携技,1281_QTE,连携技,连携技:系好安全带,,,6.523,0.593,13.046,14.232,15.418,2.533,0.116,3.809,4.041,4.273,0,0,0,0,0,0,196.5162,252.5325,253.3,0,0,45279,3,5,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1281,派派,终结技,1281_Q,终结技,终结技:坐~稳~啦~,,,16.604,1.51,33.214,36.234,39.254,3.283,0.15,4.933,5.233,5.533,0,0,0,0,0,0,0,0,828.3,0,0,112754,3,6,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1281,派派,受击支援,1281_BH_Aid,受击支援,快速支援:点刹,,,1.3,0.119,2.609,2.847,3.085,1.3,0.06,1.96,2.08,2.2,0,0,4.679,0,0,0,27.82,35.75,64.99,0,0,12996,5,7,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1281,派派,招架/回避支援,1281_Light_parry_Aid,轻招架,招架支援:极限刹车,,轻招架,0,0,0,0,0,2.467,0.113,3.71,3.936,4.162,0,0,0,0,0,0,14.2738,0,366.64,0,0,0,5,8,0,1,0,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1281,派派,招架/回避支援,1281_Heavy_parry_Aid,重招架,招架支援:极限刹车,,重招架,0,0,0,0,0,3.117,0.142,4.679,4.963,5.247,0,0,0,0,0,0,24.9738,0,416.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1281,派派,招架/回避支援,1281_Chain_parry_Aid,连续招架,招架支援:极限刹车,,连续招架,0,0,0,0,0,1.517,0.069,2.276,2.414,2.552,0,0,0,0,0,0,24.9738,0,116.64,0,0,0,5,8,0,1,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1281,派派,突击支援,1281_Assault_Aid,突击支援,支援突击:弯道超车,,,4.005,0.365,8.02,8.75,9.48,3.52,0.16,5.28,5.6,5.92,0,0,0,0,0,0,96.0646,123.4475,206.67,0,0,37622,5,9,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1251,青衣,普攻,1251_NA_1,第1段普攻,普通攻击:一煞,,,0.472,0.043,0.945,1.031,1.117,0.236,0.011,0.357,0.379,0.401,0,0,0.771,0,0,0,4.601,5.9125,21.42,0,0,0,0,0,3,0,0,27,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1251,青衣,普攻,1251_NA_1_A,第1段普攻(形态A),普通攻击:一煞,,,1.103,0.101,2.214,2.416,2.618,0.552,0.026,0.838,0.89,0.942,0,0,1.805,0,0,0,10.7428,13.805,50.14,0,0,0,0,0,0,0,0,58,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1251,青衣,普攻,1251_NA_2,第2段普攻,普通攻击:一煞,,,1.221,0.111,2.442,2.664,2.886,0.822,0.038,1.24,1.316,1.392,0,0,2.689,0,0,0,18.618,23.925,74.69,0,0,0,0,0,0,0,0,51,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1251,青衣,普攻,1251_NA_Switch,普攻状态切换,普通攻击:一煞,,,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1251,青衣,普攻,1251_NA_3_NFC,第3段普攻(不满),普通攻击:一煞,,,0.176,0.016,0.352,0.384,0.416,0.191,0.009,0.29,0.308,0.326,0,0,0.48,0,0,0,2.8676,3.685,13.34,0,0,1333,0,0,3,1,0,16,4,TRUE,TRUE,0,0,,1251_NA_4,,0,0,,status.1251:lasting_node_tag==1251_NA_3_NFC|status.1251:lasting_node_tick>=180|status.1251:on_field==False,FALSE,1,FALSE,
1251,青衣,普攻,1251_NA_3_FC,第3段普攻(满),普通攻击:一煞,,,2.816,0.256,5.632,6.144,6.656,3.056,0.144,4.64,4.928,5.216,0,0,7.68,0,0,0,20.6724,58.96,213.44,0,0,21328,0,0,3,1,0,260,64,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1251,青衣,普攻,1251_NA_4,第4段普攻,普通攻击:一煞,,,2.344,0.214,4.698,5.126,5.554,2.047,0.094,3.081,3.269,3.457,0,0,6.698,0,0,0,39.8254,51.1775,186.03,0,0,15399,0,0,3,1,0,128,10,TRUE,TRUE,0,0,,,1251_NA_3_NFC,0,0,,,TRUE,1,FALSE,
1251,青衣,普攻,1251_SNA,特殊普攻,普通攻击:醉花云,,,0.856,0.078,1.714,1.87,2.026,0.856,0.039,1.285,1.363,1.441,0,0,2.799,0,0,0,16.6492,21.395,77.75,0,0,7774,0,0,3,1,0,48,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1251,青衣,普攻,1251_SNA_1,第1段特殊普攻,普通攻击:醉花月云转,,突进攻击,0.8974,0.0816,1.795,1.9582,2.1214,0.5438,0.0248,0.8166,0.8662,0.9158,0,0,1.369,0,0,0,8.14056,10.461,38.028,0,0,3802.6,0,0,3,1,0,20,3,TRUE,TRUE,0,0,,1251_SNA_2,,0,0,,special.preload_data:operating_char!=1251|action.1251:strict_linked_after==1251_SNA_1,FALSE,1,FALSE,
1251,青衣,普攻,1251_SNA_2,第2段特殊普攻,普通攻击:醉花月云转,,终结一击,3.944,0.359,7.893,8.611,9.329,2.176,0.099,3.265,3.463,3.661,0,0,5.478,0,0,0,32.5708,41.855,152.15,0,0,15214,0,0,3,1,0,82,5,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1251,青衣,特殊技,1251_E,特殊技,特殊技:昼锦堂,,,0.624,0.057,1.251,1.365,1.479,0.624,0.029,0.943,1.001,1.059,0,0,0,0,0,0,12.1338,15.5925,56.69,0,0,5668,1,1,3,1,0,82,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1251,青衣,强化特殊技,1251_E_EX_NFC,强化特殊技(不满),强化特殊技:月上海棠,,A,0.495,0.045,0.99,1.08,1.17,0.248,0.012,0.38,0.404,0.428,0,60,0,0,0,0,76.3124,100.3265135,0,0,0,32409,1,2,3,1,0,98,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1251,青衣,强化特殊技,1251_E_EX_FC,强化特殊技(满),强化特殊技:月上海棠,,B,0.495,0.045,0.99,1.08,1.17,0.248,0.012,0.38,0.404,0.428,0,80,0.81,0,0,0,82.6682,5.843506484,0,0,0,0,1,2,3,1,0,110,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1251,青衣,冲刺攻击,1251_RA,冲刺攻击,冲刺攻击:入破,,,0.495,0.045,0.99,1.08,1.17,0.248,0.012,0.38,0.404,0.428,0,0,0.81,0,0,0,4.815,6.1875,22.49,0,0,0,2,3,0,1,0,23,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1251,青衣,闪避反击,1251_CA,闪避反击,闪避反击:意不尽,,,2.84,0.259,5.689,6.207,6.725,1.904,0.087,2.861,3.035,3.209,0,0,4.381,0,0,0,26.0438,33.4675,271.67,0,0,12166,2,4,3,1,0,82,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1251,青衣,连携技,1251_QTE,连携技,连携技:太平令,,,6.479,0.589,12.958,14.136,15.314,2.09,0.095,3.135,3.325,3.515,0,0,0,0,0,0,182.97,235.125,189.97,0,0,38946,3,5,3,1,0,120,1,TRUE,TRUE,0,120,,,,0,0,,,TRUE,1,TRUE,
1251,青衣,终结技,1251_Q,终结技,终结技:八声甘州,,,16.707,1.519,33.416,36.454,39.492,10.971,0.499,16.46,17.458,18.456,0,0,0,0,0,0,0,0,715.04,0,0,21503,3,6,3,1,0,90,1,TRUE,TRUE,0,90,,,,0,0,,,TRUE,1,TRUE,
1251,青衣,受击支援,1251_BH_Aid,受击支援,快速支援:风入松,,,1.339,0.122,2.681,2.925,3.169,1.339,0.061,2.01,2.132,2.254,0,0,4.381,0,0,0,26.0438,33.4675,60.84,0,0,12166,5,7,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1251,青衣,招架/回避支援,1251_Light_parry_Aid,轻招架,招架支援:锦上花,,轻招架,0,0,0,0,0,2.493,0.114,3.747,3.975,4.203,0,0,0,0,0,0,14.2738,0,366.64,0,0,0,5,8,0,0,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1251,青衣,招架/回避支援,1251_Heavy_parry_Aid,重招架,招架支援:锦上花,,重招架,0,0,0,0,0,3.043,0.139,4.572,4.85,5.128,0,0,0,0,0,0,24.9738,0,416.64,0,0,0,5,8,0,0,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1251,青衣,招架/回避支援,1251_Chain_parry_Aid,连续招架,招架支援:锦上花,,连续招架,0,0,0,0,0,1.283,0.059,1.932,2.05,2.168,0,0,0,0,0,0,24.9738,0,116.64,0,0,0,5,8,0,0,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1251,青衣,突击支援,1251_Assault_Aid,突击支援,支援突击:清江引,,,3.764,0.343,7.537,8.223,8.909,2.79,0.127,4.187,4.441,4.695,0,0,0,0,0,0,35.3314,45.4025,165.04,0,0,32001,5,9,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1261,简,普攻,1261_NA_1,第1段普攻,普通攻击:跳步刃舞,,一段,0.361,0.033,0.724,0.79,0.856,0.153,0.007,0.23,0.244,0.258,0,0,0.501,0,0,0,,3.8225,13.9,0,0,2134,0,0,0,1,0,20,1,TRUE,TRUE,0,0,"{'passion_get': 4.1695, 'passion_consume': 3.3356}",,,0,0,,,FALSE,1,FALSE,
1261,简,普攻,1261_NA_2,第2段普攻,普通攻击:跳步刃舞,,二段,0.623,0.057,1.25,1.364,1.478,0.443,0.021,0.674,0.716,0.758,0,0,1.448,0,0,0,,11.055,40.2,0,0,5206,0,0,0,1,0,30,2,TRUE,TRUE,0,0,"{'passion_get': 6.6493, 'passion_consume': 5.3194}",,,0,0,,,FALSE,1,FALSE,
1261,简,普攻,1261_NA_3,第3段普攻,普通攻击:跳步刃舞,,三段,0.835,0.076,1.671,1.823,1.975,0.595,0.028,0.903,0.959,1.015,0,0,1.946,0,0,0,,14.8775,54.04,0,0,6878,0,0,0,1,0,30,3,TRUE,TRUE,0,0,"{'passion_get': 8.2623, 'passion_consume': 6.6098}",,,0,0,,,FALSE,1,FALSE,
1261,简,普攻,1261_NA_4,第4段普攻,普通攻击:跳步刃舞,,四段,1.634,0.149,3.273,3.571,3.869,1.097,0.05,1.647,1.747,1.847,0,0,3.589,0,0,0,,27.4175,99.69,0,0,12745,0,0,0,1,0,60,6,TRUE,TRUE,0,0,"{'passion_get': 15.5541, 'passion_consume': 12.4433}",,,0,0,,,FALSE,1,FALSE,
1261,简,普攻,1261_NA_5,第5段普攻,普通攻击:跳步刃舞,,五段,0.988,0.09,1.978,2.158,2.338,0.687,0.032,1.039,1.103,1.167,0,0,2.246,0,0,0,,17.16,62.38,0,0,7671,0,0,0,1,0,40,3,TRUE,TRUE,0,0,"{'passion_get': 8.0264, 'passion_consume': 6.4211}",,,0,0,,,FALSE,1,FALSE,
1261,简,普攻,1261_NA_6,第6段普攻,普通攻击:跳步刃舞,,六段,2.913,0.265,5.828,6.358,6.888,2,0.091,3.001,3.183,3.365,0,0,6.543,0,0,0,,49.995,181.75,0,0,22662,0,0,0,1,0,86,6,TRUE,TRUE,0,0,"{'passion_get': 25.1293, 'passion_consume': 20.1034}",,,0,0,,,TRUE,1,FALSE,
1261,简,普攻,1261_SNA_1,第1段特殊普攻,普通攻击:萨霍夫跳,,连续攻击,3.008,0.274,6.022,6.57,7.118,2.292,0.105,3.447,3.657,3.867,0,0,7.499,0,0,0,,57.2825,208.3,0,0,27340,0,0,0,1,0,150,17,TRUE,TRUE,0,0,"{'passion_get': 0.0, 'passion_consume': 0.0, 'passion_direct_add': 24.996}",1261_SNA_2,,0,0,,,FALSE,1,FALSE,
1261,简,普攻,1261_SNA_2,第2段特殊普攻,普通攻击:萨霍夫跳,,终结一击,1.613,0.147,3.23,3.524,3.818,1.229,0.056,1.845,1.957,2.069,0,0,4.02,0,0,0,,30.7175,111.67,0,0,14656,0,0,0,1,0,42,2,TRUE,TRUE,0,0,"{'passion_get': 0.0, 'passion_consume': 0.0, 'passion_direct_add': 13.4}",,1261_SNA_1,0,0,,,TRUE,1,FALSE,
1261,简,特殊技,1261_E,特殊技,特殊技:掠空,,,0.578,0.053,1.161,1.267,1.373,0.578,0.027,0.875,0.929,0.983,0,0,0,0,0,0,,14.4375,52.5,0,0,5250,1,1,0,1,0,74,4,TRUE,TRUE,0,0,"{'passion_get': 7.875, 'passion_consume': 12.6}",,,0,0,,,TRUE,1,FALSE,
1261,简,强化特殊技,1261_E_EX,强化特殊技,强化特殊技:掠空-横扫,,,5.747,0.523,11.5,12.546,13.592,4.681,0.213,7.024,7.45,7.876,60,60,0,0,0,0,,170.3075,135.04,0,0,47396,1,2,0,1,0,90,6,TRUE,TRUE,0,0,"{'passion_get': 42.5544, 'passion_consume': 21.2772}",,,0,0,,,TRUE,1,FALSE,
1261,简,冲刺攻击,1261_RA_1,冲刺攻击第1段,冲刺攻击:刀刃跳,,一段,0.715,0.065,1.43,1.56,1.69,0.358,0.017,0.545,0.579,0.613,0,0,1.17,0,0,0,,8.9375,32.5,0,0,3250,2,3,0,1,0,42,2,TRUE,TRUE,0,0,"{'passion_get': 9.75, 'passion_consume': 0.0}",,,0,0,,,FALSE,1,FALSE,
1261,简,冲刺攻击,1261_RA_2,冲刺攻击第2段,冲刺攻击:刀刃跳,,二段,0.715,0.065,1.43,1.56,1.69,0.358,0.017,0.545,0.579,0.613,0,0,1.17,0,0,0,,8.9375,32.5,0,0,3250,2,3,0,1,0,42,2,TRUE,TRUE,0,0,"{'passion_get': 9.75, 'passion_consume': 0.0}",,,0,0,,,FALSE,1,FALSE,
1261,简,冲刺攻击,1261_RA,冲刺攻击,冲刺攻击:虚像突刺,,,1.045,0.095,2.09,2.28,2.47,0.523,0.024,0.787,0.835,0.883,0,0,1.71,0,0,0,,13.0625,47.49,0,0,4748,2,3,0,1,0,54,4,TRUE,TRUE,0,0,"{'passion_get': 0.0, 'passion_consume': 11.3961}",,,0,0,,,FALSE,1,FALSE,
1261,简,闪避反击,1261_CA_1,闪避反击第1段,闪避反击:疾影,,一段,3.412,0.311,6.833,7.455,8.077,2.292,0.105,3.447,3.657,3.867,0,0,3.9,0,0,0,,29.81,258.34,0,0,17767,2,4,0,1,0,76,5,TRUE,TRUE,0,0,"{'passion_get': 5.4167, 'passion_consume': 0.0, 'passion_direct_add': 20.0}",,,0,0,,,TRUE,1,FALSE,
1261,简,闪避反击,1261_CA_2,闪避反击第2段,闪避反击:疾影,,二段,3.412,0.311,6.833,7.455,8.077,2.292,0.105,3.447,3.657,3.867,0,0,3.9,0,0,0,,29.81,258.34,0,0,17767,2,4,0,1,0,76,5,TRUE,TRUE,0,0,"{'passion_get': 5.4167, 'passion_consume': 0.0, 'passion_direct_add': 20.0}",,,0,0,,,TRUE,1,FALSE,
1261,简,闪避反击,1261_CA,闪避反击,闪避反击:疾影连舞,,,3.87,0.352,7.742,8.446,9.15,2.475,0.113,3.718,3.944,4.17,0,0,4.499,0,0,0,,34.375,274.97,0,0,19431,2,4,0,1,0,76,5,TRUE,TRUE,0,0,"{'passion_get': 0.0, 'passion_consume': 10.0, 'passion_direct_add': 20.0}",,,0,0,,,TRUE,1,FALSE,
1261,简,连携技,1261_QTE,连携技,连携技:罪孽生花,,,6.326,0.576,12.662,13.814,14.966,2.376,0.108,3.564,3.78,3.996,0,0,0,0,0,0,,248.875,239.97,0,0,43946,3,5,0,1,0,62,15,TRUE,TRUE,0,62,"{'passion_get': 0.0, 'passion_consume': 35.0, 'passion_direct_add': 100.0, 'direct_passion': True}",,,0,0,,,TRUE,1,TRUE,
1261,简,终结技,1261_Q,终结技,终结技:终幕演出,,,14.706,1.337,29.413,32.087,34.761,1.865,0.085,2.8,2.97,3.14,0,0,0,0,0,0,,0,688.34,0,0,96658,3,6,0,1,0,90,1,TRUE,TRUE,0,90,"{'passion_get': 0.0, 'passion_consume': 35.0, 'passion_direct_add': 100.0, 'direct_passion': True}",,,0,0,,,TRUE,1,TRUE,
1261,简,受击支援,1261_BH_Aid,受击支援,快速支援:乌刺,,,1.192,0.109,2.391,2.609,2.827,1.192,0.055,1.797,1.907,2.017,0,0,3.9,0,0,0,,29.81,54.17,0,0,10833,5,7,0,1,0,0,1,TRUE,TRUE,0,0,"{'passion_get': 15.8334, 'passion_consume': 0.0}",,,0,0,,,TRUE,1,FALSE,
1261,简,受击支援,1261_BH_Aid_A,受击支援_A,快速支援:勾手跳,,,1.375,0.125,2.75,3,3.25,1.375,0.063,2.068,2.194,2.32,0,0,4.499,0,0,0,,34.375,62.49,0,0,12496,5,7,0,1,0,0,1,TRUE,TRUE,0,0,"{'passion_get': 0.0, 'passion_consume': 14.996}",,,0,0,,,TRUE,1,FALSE,
1261,简,招架/回避支援,1261_Light_parry_Aid,轻招架,招架支援:最后防线,,轻招架,0,0,0,0,0,2.713,0.124,4.077,4.325,4.573,0,0,0,0,0,0,,0,366.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,"{'passion_get': 0.0, 'passion_consume': 0.0}",,,0,0,,,FALSE,1,TRUE,
1261,简,招架/回避支援,1261_Heavy_parry_Aid,重招架,招架支援:最后防线,,重招架,0,0,0,0,0,3.428,0.156,5.144,5.456,5.768,0,0,0,0,0,0,,0,416.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,"{'passion_get': 0.0, 'passion_consume': 0.0}",,,0,0,,,FALSE,1,TRUE,
1261,简,招架/回避支援,1261_Chain_parry_Aid,连续招架,招架支援:最后防线,,连续招架,0,0,0,0,0,1.668,0.076,2.504,2.656,2.808,0,0,0,0,0,0,,0,116.64,0,0,0,5,8,0,1,0,10,1,TRUE,FALSE,0,0,"{'passion_get': 0.0, 'passion_consume': 0.0}",,,0,0,,,FALSE,1,TRUE,
1261,简,突击支援,1261_Assault_Aid,突击支援,支援突击:疾风扫,,,3.455,0.315,6.92,7.55,8.18,2.99,0.136,4.486,4.758,5.03,0,0,0,0,0,0,,98.01,144.97,0,0,29292,5,9,0,1,0,0,1,TRUE,TRUE,0,0,"{'passion_get': 40.7686, 'passion_consume': 17.3961}",,,0,0,,,TRUE,1,FALSE,
1271,塞斯,普攻,1271_NA_1,第1段普攻,普通攻击:雷霆击,,一段,0.362,0.033,0.725,0.791,0.857,0.181,0.009,0.28,0.298,0.316,0,0,0.652,0,0,0,3.8734,4.9775,18.09,0,0,0,0,0,0,0,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1271,塞斯,普攻,1271_NA_2,第2段普攻,普通攻击:雷霆击,,二段,0.565,0.052,1.137,1.241,1.345,0.451,0.021,0.682,0.724,0.766,0,0,1.624,0,0,0,9.6514,12.4025,45.1,0,0,0,0,0,0,0,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1271,塞斯,普攻,1271_NA_3,第3段普攻,普通攻击:雷霆击,,三段,1.933,0.176,3.869,4.221,4.573,1.513,0.069,2.272,2.41,2.548,0,0,5.445,0,0,0,32.3782,41.6075,151.24,0,0,0,0,0,0,0,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1271,塞斯,普攻,1271_NA_4,第4段普攻,普通攻击:雷霆击,,四段,0.974,0.089,1.953,2.131,2.309,0.805,0.037,1.212,1.286,1.36,0,0,2.895,0,0,0,17.227,22.1375,80.42,0,0,8041,0,0,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1271,塞斯,普攻,1271_SNA_1,第1段特殊普攻,普通攻击:雷霆击-感电,,连续攻击,3.828,0.348,7.656,8.352,9.048,1.617,0.074,2.431,2.579,2.727,0,0,5.821,0,0,0,34.6038,44.4675,143.61,0,0,16168,0,0,3,1,0,0,1,TRUE,TRUE,0,0,,1271_SNA_2,,0,0,,,FALSE,1,FALSE,
1271,塞斯,普攻,1271_SNA_2,第2段特殊普攻,普通攻击:雷霆击-感电,,终结一击,4.242,0.386,8.488,9.26,10.032,1.455,0.067,2.192,2.326,2.46,0,0,4.76,0,0,0,28.3122,36.3825,132.21,0,0,13220,0,0,3,1,0,0,1,TRUE,TRUE,0,0,,,1271_SNA_1,-1,15,,,TRUE,1,FALSE,
1271,塞斯,特殊技,1271_E,特殊技,特殊技:电光盾冲,,,0.692,0.063,1.385,1.511,1.637,0.692,0.032,1.044,1.108,1.172,0,0,0,0,0,0,14.8088,19.03,69.19,0,0,6918,1,1,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1271,塞斯,强化特殊技,1271_E_EX,强化特殊技,强化特殊技:电光盾冲-高伏特,,,6.46,0.588,12.928,14.104,15.28,5.404,0.246,8.11,8.602,9.094,0,80,0,0,0,0,160.8638,206.7175,223.34,0,0,59316,1,2,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1271,塞斯,强化特殊技,1271_E_EX_A,强化E形态A,强化特殊技:电光盾冲-高伏特,,(蓄力),9.998,0.909,19.997,21.815,23.633,8.49,0.386,12.736,13.508,14.28,0,80,0,0,0,0,246.2284,316.415,396.67,0,0,92438,1,2,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1271,塞斯,冲刺攻击,1271_RA,冲刺攻击,冲刺攻击:电光突袭,,,1.1,0.1,2.2,2.4,2.6,0.55,0.025,0.825,0.875,0.925,0,0,1.98,0,0,0,11.77,15.125,54.99,0,0,0,2,3,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1271,塞斯,闪避反击,1271_CA,闪避反击,闪避反击:以退为进,,,2.3,0.21,4.61,5.03,5.45,2,0.091,3.001,3.183,3.365,0,0,3.6,0,0,0,21.4,27.5,250,0,0,10000,2,4,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1271,塞斯,连携技,1271_QTE,连携技,连携技:最终制裁,,,7.041,0.641,14.092,15.374,16.656,3.051,0.139,4.58,4.858,5.136,0,0,0,0,0,0,207.6014,266.7775,305.04,0,0,50453,3,5,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1271,塞斯,终结技,1271_Q,终结技,终结技:正义必胜,,,20.243,1.841,40.494,44.176,47.858,4.033,0.184,6.057,6.425,6.793,0,0,0,0,0,0,0,0,903.3,0,0,40329,3,6,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1271,塞斯,受击支援,1271_BH_Aid,受击支援,快速支援:武力支援,,,1,0.091,2.001,2.183,2.365,1,0.046,1.506,1.598,1.69,0,0,3.6,0,0,0,21.4,27.5,50,0,0,10000,5,7,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1271,塞斯,招架/回避支援,1271_Light_parry_Aid,轻招架,招架支援:迅雷盾,,轻招架,0,0,0,0,0,2.467,0.113,3.71,3.936,4.162,0,0,0,0,0,0,14.2738,0,366.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1271,塞斯,招架/回避支援,1271_Heavy_parry_Aid,重招架,招架支援:迅雷盾,,重招架,0,0,0,0,0,3.117,0.142,4.679,4.963,5.247,0,0,0,0,0,0,24.9738,0,416.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1271,塞斯,招架/回避支援,1271_Chain_parry_Aid,连续招架,招架支援:迅雷盾,,连续招架,0,0,0,0,0,1.517,0.069,2.276,2.414,2.552,0,0,0,0,0,0,24.9738,0,116.64,0,0,0,5,8,0,1,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1271,塞斯,突击支援,1271_Assault_Aid,突击支援,支援突击:治安裁决,,,4.285,0.39,8.575,9.355,10.135,3.78,0.172,5.672,6.016,6.36,0,0,0,0,0,0,102.4846,131.6975,226.67,0,0,40322,5,9,3,1,0,0,1,TRUE,TRUE,0,0,,,,-1,15,,,TRUE,1,FALSE,
1071,凯撒,普攻,1071_NA_1,第1段普攻,普通攻击:横行斩打,,一段,0.472,0.043,0.945,1.031,1.117,0.213,0.01,0.323,0.343,0.363,0,0,0.772,0,0,0,4.601,5.9125,21.43,0,0,2142,0,0,0,1,0,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1071,凯撒,普攻,1071_NA_2,第2段普攻,普通攻击:横行斩打,,二段,0.409,0.038,0.827,0.903,0.979,0.338,0.016,0.514,0.546,0.578,0,0,1.228,0,0,0,7.2974,9.3775,34.1,0,0,3409,0,0,0,1,0,15,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1071,凯撒,普攻,1071_NA_3,第3段普攻,普通攻击:横行斩打,,三段,1.483,0.135,2.968,3.238,3.508,1.1,0.05,1.65,1.75,1.85,0,0,3.998,0,0,0,23.7754,30.5525,111.05,0,0,11104,0,0,0,1,0,66,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1071,凯撒,普攻,1071_NA_Switch,普攻状态切换,普通攻击:横行斩打,,三段(派生),1.184,0.108,2.372,2.588,2.804,0.736,0.034,1.11,1.178,1.246,0,0,2.676,0,0,0,15.9216,20.46,74.33,0,0,7432,0,0,0,1,0,42,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1071,凯撒,普攻,1071_NA_4,第4段普攻,普通攻击:横行斩打,,四段,0.788,0.072,1.58,1.724,1.868,0.675,0.031,1.016,1.078,1.14,0,0,2.453,0,0,0,14.5948,18.755,68.13,0,0,6812,0,0,0,1,0,32,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1071,凯撒,普攻,1071_NA_5,第5段普攻,普通攻击:横行斩打,,五段,1.986,0.181,3.977,4.339,4.701,1.375,0.063,2.068,2.194,2.32,0,0,4.997,0,0,0,29.7032,38.17,138.8,0,0,13879,0,0,0,1,0,77,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1071,凯撒,普攻,1071_NA_6,第6段普攻,普通攻击:横行斩打,,六段,3.999,0.364,8.003,8.731,9.459,2.626,0.12,3.946,4.186,4.426,0,0,9.549,0,0,0,56.7742,72.9575,265.25,0,0,26524,0,0,0,1,0,132,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1071,凯撒,普攻,1071_SNA,特殊普攻,普通攻击:此路不通!,,,1.263,0.115,2.528,2.758,2.988,0.932,0.043,1.405,1.491,1.577,0,0,3.388,0,0,0,34.7322,25.905,94.11,0,0,9410,0,0,0,1,0,57,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1071,凯撒,特殊技,1071_E,特殊技,特殊技:震荡盾击,,,0.533,0.049,1.072,1.17,1.268,0.24,0.011,0.361,0.383,0.405,0,0,0,0,0,0,0,6.655,24.19,0,0,2418,1,1,0,1,0,31,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1071,凯撒,特殊技,1071_E_A,特殊技形态A,特殊技:震荡盾击,,[精准格挡],0,0,0,0,0,0.77,0.035,1.155,1.225,1.295,0,0,0,0,0,0,0,11.0275,0,0,0,4000,1,1,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1071,凯撒,特殊技,1071_E_B,特殊技形态B,特殊技:喧嚣直刺,,,0.588,0.054,1.182,1.29,1.398,0.265,0.013,0.408,0.434,0.46,0,0,0,0,0,0,0,7.3425,26.69,0,0,2668,1,1,0,0,0,60,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1071,凯撒,强化特殊技,1071_E_EX,强化特殊技,强化特殊技:招架反击,,,3.872,0.352,7.744,8.448,9.152,2.526,0.115,3.791,4.021,4.251,0,0,0,0,0,0,0,84.6175,123.34,0,0,24911,1,2,0,1,0,75,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1071,凯撒,强化特殊技,1071_E_EX_A,强化E形态A,强化特殊技:招架反击,,[精准格挡],0,0,0,0,0,1.54,0.07,2.31,2.45,2.59,0,0,0,0,0,0,0,27.5,0,0,0,10000,1,2,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1071,凯撒,强化特殊技,1071_E_EX_B,强化E形态B,强化特殊技:超强力盾击,,,4.257,0.387,8.514,9.288,10.062,2.884,0.132,4.336,4.6,4.864,0,0,0,0,0,0,0,94.93,126.7,0,0,28287,1,2,0,1,0,75,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1071,凯撒,冲刺攻击,1071_RA,冲刺攻击,冲刺攻击:猪突猛进,,,0.623,0.057,1.25,1.364,1.478,0.312,0.015,0.477,0.507,0.537,0,0,1.02,0,0,0,0,27.28,28.32,0,0,2831,2,3,0,1,0,35,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1071,凯撒,闪避反击,1071_CA,闪避反击,闪避反击:以牙还牙,,,1.935,0.176,3.871,4.223,4.575,1.742,0.08,2.622,2.782,2.942,0,0,2.101,0,0,0,0,16.06,208.34,0,0,5833,2,4,0,1,0,35,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1071,凯撒,连携技,1071_QTE,连携技,连携技:路怒震打,,,6.388,0.581,12.779,13.941,15.103,1.999,0.091,3,3.182,3.364,0,0,0,0,0,0,0,232.8425,181.67,0,0,38116,3,5,0,1,0,66,1,TRUE,TRUE,0,66,,,,0,0,,,TRUE,1,TRUE,
1071,凯撒,终结技,1071_Q,终结技,终结技:暴君猛击,,,20.123,1.83,40.253,43.913,47.573,2.787,0.127,4.184,4.438,4.692,0,0,0,0,0,0,0,0,753.3,0,0,25329,3,6,0,1,0,90,1,TRUE,TRUE,0,90,,,,0,0,,,TRUE,1,TRUE,
1071,凯撒,受击支援,1071_BH_Aid,受击支援,快速支援:变道支援,,,0.642,0.059,1.291,1.409,1.527,0.642,0.03,0.972,1.032,1.092,0,0,2.101,0,0,0,0,16.06,58.34,0,0,5833,5,7,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1071,凯撒,招架/回避支援,1071_Light_parry_Aid,轻招架,招架支援:守御之盾,,轻招架,0,0,0,0,0,2.713,0.124,4.077,4.325,4.573,0,0,0,0,0,0,0,0,366.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1071,凯撒,招架/回避支援,1071_Heavy_parry_Aid,重招架,招架支援:守御之盾,,重招架,0,0,0,0,0,3.453,0.157,5.18,5.494,5.808,0,0,0,0,0,0,0,0,418.34,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1071,凯撒,招架/回避支援,1071_Chain_parry_Aid,连续招架,招架支援:守御之盾,,连续招架,0,0,0,0,0,1.693,0.077,2.54,2.694,2.848,0,0,0,0,0,0,0,0,118.34,0,0,0,5,8,0,1,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1071,凯撒,突击支援,1071_Assault_Aid,突击支援,支援突击:支援之锋,,,4.072,0.371,8.153,8.895,9.637,3.563,0.162,5.345,5.669,5.993,0,0,0,0,0,0,0,114.51,185,0,0,34697,5,9,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1171,柏妮思,普攻,1171_NA_1,第1段普攻,普通攻击:炽焰直调式,,一段,0.448,0.041,0.899,0.981,1.063,0.187,0.009,0.286,0.304,0.322,0,0,0.61,0,0,0,3.638,4.675,16.95,0,0,1694,0,0,1,0.833333333,0,44,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1171,柏妮思,普攻,1171_NA_2,第2段普攻,普通攻击:炽焰直调式,,二段,0.435,0.04,0.875,0.955,1.035,0.308,0.014,0.462,0.49,0.518,0,0,1.007,0,0,0,5.992,7.7,27.95,0,0,0,0,0,0,0,0,46,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1171,柏妮思,普攻,1171_NA_3,第3段普攻,普通攻击:炽焰直调式,,三段,0.717,0.066,1.443,1.575,1.707,0.478,0.022,0.72,0.764,0.808,0,0,1.562,0,0,0,9.2876,11.935,43.38,0,0,4337,0,0,1,1,0,46,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1171,柏妮思,普攻,1171_NA_4,第4段普攻,普通攻击:炽焰直调式,,四段,0.666,0.061,1.337,1.459,1.581,0.304,0.014,0.458,0.486,0.514,0,0,0.992,0,0,0,5.9064,7.59,27.55,0,0,2754,0,0,1,1,0,38,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1171,柏妮思,普攻,1171_NA_5,第5段普攻,普通攻击:炽焰直调式,,五段,0.963,0.088,1.931,2.107,2.283,0.52,0.024,0.784,0.832,0.88,0,0,1.699,0,0,0,10.1008,12.98,47.2,0,0,4719,0,0,1,1,0,47,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1171,柏妮思,普攻,1171_SNA_1,第1段特殊普攻,普通攻击:炽焰搅拌式,,持续喷射,1.254,0.114,2.508,2.736,2.964,0.965,0.044,1.449,1.537,1.625,0,0,1.578,0,0,0,18.7678,24.1175,87.66,0,0,9598,0,0,1,1,0,90,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1171,柏妮思,普攻,1171_SNA_2,第2段特殊普攻,普通攻击:炽焰搅拌式,,终结一击,2.328,0.212,4.66,5.084,5.508,1.791,0.082,2.693,2.857,3.021,0,0,2.931,0,0,0,34.8392,44.77,162.79,0,0,17111,0,0,1,1,0,68,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1171,柏妮思,特殊技,1171_E,特殊技,特殊技:灼热熟成法,,,0.578,0.053,1.161,1.267,1.373,0.578,0.027,0.875,0.929,0.983,0,0,0,0,0,0,11.2564,14.465,52.52,0,0,5251,1,1,1,1,0,69,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1171,柏妮思,特殊技,1171_E_FC,特殊技(满),特殊技:灼热熟成法,,蓄力,0.624,0.057,1.251,1.365,1.479,0.624,0.029,0.943,1.001,1.059,0,0,0,0,0,0,12.1338,15.5925,56.69,0,0,5668,1,1,1,1,0,82,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1171,柏妮思,强化特殊技,1171_E_EX_1,第1段强化特殊技,强化特殊技:灼热摇荡法,,持续喷射,5.438,0.495,10.883,11.873,12.863,3.786,0.173,5.689,6.035,6.381,0,40,0,0,0,0,106.4222,136.7575,189,0,0,41110,1,2,1,1,0,120,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1171,柏妮思,强化特殊技,1171_E_EX_2,第2段强化特殊技,强化特殊技:灼热摇荡法,,火焰冲击,0.967,0.088,1.935,2.111,2.287,0.657,0.03,0.987,1.047,1.107,0,5,0,0,0,0,18.6822,24.0075,31.5,0,0,7155,1,2,1,1,0,54,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1171,柏妮思,强化特殊技,1171_E_EX_A_1,强化E形态A第1段,强化特殊技:灼热摇荡法·双份,,持续喷射,9.581,0.871,19.162,20.904,22.646,5.857,0.267,8.794,9.328,9.862,0,0,0,0,0,0,143.166,183.975,227.37,0,0,53024,1,2,1,1,0,120,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1171,柏妮思,强化特殊技,1171_E_EX_A_2,强化E形态A第2段,强化特殊技:灼热摇荡法·双份,,火焰冲击,2.871,0.261,5.742,6.264,6.786,2.078,0.095,3.123,3.313,3.503,0,0,0,0,0,0,46.973,60.3625,110.04,0,0,18365,1,2,1,1,0,86,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1171,柏妮思,冲刺攻击,1171_RA,冲刺攻击,冲刺攻击:危险发酵式,,,0.679,0.062,1.361,1.485,1.609,0.34,0.016,0.516,0.548,0.58,0,0,1.11,0,0,0,6.6126,8.4975,30.84,0,0,3083,2,3,1,1,0,39,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1171,柏妮思,闪避反击,1171_CA,闪避反击,闪避反击:摇荡闪,,,2.197,0.2,4.397,4.797,5.197,1.944,0.089,2.923,3.101,3.279,0,0,1.38,0,0,0,16.4138,21.0925,226.67,0,0,7666,2,4,1,0.8,0,58,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1171,柏妮思,连携技,1171_QTE,连携技,连携技:燃油熔焰,,,6.809,0.619,13.618,14.856,16.094,2.42,0.11,3.63,3.85,4.07,0,0,0,0,0,0,189.39,243.375,219.97,0,0,100296,3,5,1,1,0,106,1,TRUE,TRUE,0,106,,,,0,0,,,TRUE,1,TRUE,
1171,柏妮思,终结技,1171_Q,终结技,终结技:纵享盛焰,,最大,20.122,1.83,40.252,43.912,47.572,1.064,0.049,1.603,1.701,1.799,0,0,0,0,0,0,0,0,596.67,0,0,86116,3,6,1,1,0,90,1,TRUE,TRUE,0,90,,,,1,15,,,TRUE,1,TRUE,
1171,柏妮思,受击支援,1171_BH_Aid,受击支援,快速支援:提神特饮,,,0.844,0.077,1.691,1.845,1.999,0.844,0.039,1.273,1.351,1.429,0,0,1.38,0,0,0,16.4138,21.0925,38.34,0,0,7666,5,7,1,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1171,柏妮思,招架/回避支援,1171_Light_parry_Aid,轻招架,招架支援:烟熏油盅,,轻招架,0,0,0,0,0,2.713,0.124,4.077,4.325,4.573,0,0,0,0,0,0,14.2738,0,366.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1171,柏妮思,招架/回避支援,1171_Heavy_parry_Aid,重招架,招架支援:烟熏油盅,,重招架,0,0,0,0,0,3.428,0.156,5.144,5.456,5.768,0,0,0,0,0,0,24.9738,0,416.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1171,柏妮思,招架/回避支援,1171_Chain_parry_Aid,连续招架,招架支援:烟熏油盅,,连续招架,0,0,0,0,0,1.668,0.076,2.504,2.656,2.808,0,0,0,0,0,0,24.9738,0,116.64,0,0,0,5,8,0,1,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1171,柏妮思,突击支援,1171_Assault_Aid,突击支援,支援突击:灼焰甘露,,,3.276,0.298,6.554,7.15,7.746,2.824,0.129,4.243,4.501,4.759,0,0,0,0,0,0,72.5246,93.1975,133.34,0,0,27722,5,9,1,1,0,134,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1171,柏妮思,突击支援,1171_Core_Passive,核心被动,核心被动:燃油特调,,余烬,1.75,0,1.75,1.75,1.75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6000,5,9,1,1,0,0,1,FALSE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1221,柳,普攻,1221_NA_1,上弦-第1段,架势:上弦,,一段,0.566,0.052,1.138,1.242,1.346,0.278,0.013,0.421,0.447,0.473,0,0,1.009,0,0,0,5.4142,6.9575,28.01,0,0,0,0,0,0,0,0,21,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1221,柳,普攻,1221_NA_2,上弦-第2段,架势:上弦,,二段,0.997,0.091,1.998,2.18,2.362,0.681,0.031,1.022,1.084,1.146,0,0,2.474,0,0,0,13.2466,17.0225,68.71,0,0,0,0,0,0,0,0,40,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1221,柳,普攻,1221_NA_3,上弦-第3段,架势:上弦,,三段,1.131,0.103,2.264,2.47,2.676,0.694,0.032,1.046,1.11,1.174,0,0,2.522,0,0,0,13.5034,17.3525,70.04,0,0,7991,0,0,3,1,0,42,2,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1221,柳,普攻,1221_NA_4,上弦-第4段,架势:上弦,,四段,1.266,0.116,2.542,2.774,3.006,0.776,0.036,1.172,1.244,1.316,0,0,2.822,0,0,0,15.1084,19.415,78.37,0,0,8942,0,0,3,1,0,49,5,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1221,柳,普攻,1221_NA_5,上弦-第5段,架势:上弦,,五段,2.369,0.216,4.745,5.177,5.609,1.452,0.066,2.178,2.31,2.442,0,0,5.28,0,0,0,28.248,36.3,146.67,0,0,16735,0,0,3,1,0,98,7,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1221,柳,普攻,1221_NA_Switch,普攻状态切换,,,,1.131,0.103,2.264,2.47,2.676,0.555,0.026,0.841,0.893,0.945,0,0,2.017,0,0,0,0,13.11542566,0,0,0,0,0,0,0,0,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1221,柳,普攻,1221_SNA_1,下弦-第1段,架势:下弦,,一段,1.131,0.103,2.264,2.47,2.676,0.555,0.026,0.841,0.893,0.945,0,0,2.017,0,0,0,10.807,13.8875,56.03,0,0,0,0,0,0,0,0,46,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1221,柳,普攻,1221_SNA_2,下弦-第2段,架势:下弦,,二段,1.292,0.118,2.59,2.826,3.062,0.931,0.043,1.404,1.49,1.576,0,0,3.385,0,0,0,18.1258,23.2925,94.01,0,0,0,0,0,0,0,0,46,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1221,柳,普攻,1221_SNA_3,下弦-第3段,架势:下弦,,三段,0.728,0.067,1.465,1.599,1.733,0.446,0.021,0.677,0.719,0.761,0,0,1.622,0,0,0,8.6884,11.165,45.04,0,0,5138,0,0,3,1,0,35,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1221,柳,普攻,1221_SNA_4,下弦-第4段,架势:下弦,,四段,1.077,0.098,2.155,2.351,2.547,0.661,0.031,1.002,1.064,1.126,0,0,2.401,0,0,0,12.8614,16.5275,66.67,0,0,7607,0,0,3,1,0,34,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1221,柳,普攻,1221_SNA_5,下弦-第5段,架势:下弦,,五段,2.718,0.248,5.446,5.942,6.438,1.667,0.076,2.503,2.655,2.807,0,0,6.059,0,0,0,32.421,41.6625,168.31,0,0,19204,0,0,3,1,0,104,8,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1221,柳,特殊技,1221_E,特殊技,特殊技:流转,,直接放E,1.174,0.107,2.351,2.565,2.779,1.057,0.049,1.596,1.694,1.792,0,0,0,0,0,0,20.5654,26.4275,53.34,0,0,9600,1,1,3,1,0,66,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1221,柳,特殊技,1221_E_A,特殊技形态A,特殊技:流转,,快速衔接版的E,1.174,0.107,2.351,2.565,2.779,1.057,0.049,1.596,1.694,1.792,0,0,0,0,0,0,20.5654,26.4275,53.34,0,0,9600,1,1,3,1,0,56,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
0,柳,强化特殊技,0_E_EX,强化特殊技,废弃,,废弃,1.638,0.149,3.277,3.575,3.873,1.271,0.058,1.909,2.025,2.141,40,40,0,0,0,0,92.0414,41.9375,66.67,0,0,14350,1,2,3,1,0,118,7,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1221,柳,强化特殊技,1221_E_EX_1,第1段强化特殊技,强化特殊技:月华流转,,强化E的穿刺攻击,2命以上可重复释放,1.638,0.149,3.277,3.575,3.873,1.271,0.058,1.909,2.025,2.141,40,10,0,0,0,0,32.635,41.9375,66.67,0,0,14350,1,2,3,1,0,40,3,TRUE,TRUE,0,0,,1221_E_EX_2,,0,0,,attribute.1221:cinema<2,FALSE,1,FALSE,
1221,柳,强化特殊技,1221_E_EX_2,第2段强化特殊技,强化特殊技:月华流转,,强化E的下落攻击,最后1跳触发极性紊乱,3.778,0.344,7.562,8.25,8.938,1.096,0.05,1.646,1.746,1.846,0,30,0,0,0,0,59.4064,76.34,66.7,0,0,26854,1,2,3,1,0,78,4,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1221,柳,冲刺攻击,1221_RA,冲刺攻击,冲刺攻击:飞掠,,,0.504,0.046,1.01,1.102,1.194,0.454,0.021,0.685,0.727,0.769,0,0,1.65,0,0,0,8.8382,11.3575,45.82,0,0,0,2,3,0,0,0,55,4,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1221,柳,闪避反击,1221_CA,闪避反击,闪避反击:疾反,,,2.316,0.211,4.637,5.059,5.481,1.832,0.084,2.756,2.924,3.092,0,0,3.062,0,0,0,16.3924,21.065,235.04,0,0,7652,2,4,3,1,0,51,3,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1221,柳,连携技,1221_QTE,连携技,连携技:星月相随,,,5.931,0.54,11.871,12.951,14.031,1.783,0.082,2.685,2.849,3.013,0,0,0,0,0,0,166.6204,214.115,200.04,0,0,35958,3,5,3,1,0,66,10,TRUE,TRUE,0,66,,,,0,0,,,TRUE,1,TRUE,
1221,柳,终结技,1221_Q,终结技,终结技:雷影天华,,,15.118,1.375,30.243,32.993,35.743,0.975,0.045,1.47,1.56,1.65,0,0,0,0,0,0,0,0,721.64,0,0,90439,3,6,3,1,0,90,10,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1221,柳,受击支援,1221_BH_Aid,受击支援,快速支援:风华斩,,,0.936,0.086,1.882,2.054,2.226,0.842,0.039,1.271,1.349,1.427,0,0,3.062,0,0,0,18.2114,21.065,42.52,0,0,7652,5,7,3,1,0,60,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1221,柳,招架/回避支援,1221_Light_parry_Aid,轻招架,招架支援:流光反,,,0,0,0,0,0,2.442,0.111,3.663,3.885,4.107,0,0,0,0,0,0,0,0,366.64,0,0,0,5,8,0,0,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1221,柳,招架/回避支援,1221_Heavy_parry_Aid,重招架,招架支援:流光反,,,0,0,0,0,0,3.086,0.141,4.637,4.919,5.201,0,0,0,0,0,0,0,0,416.64,0,0,0,5,8,0,0,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1221,柳,招架/回避支援,1221_Chain_parry_Aid,连续招架,招架支援:流光反,,,0,0,0,0,0,1.502,0.069,2.261,2.399,2.537,0,0,0,0,0,0,0,0,116.64,0,0,0,5,8,0,0,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1221,柳,突击支援,1221_Assault_Aid,突击支援,支援突击:飞絮刺,,,4.071,0.371,8.152,8.894,9.636,3.206,0.146,4.812,5.104,5.396,0,0,0,0,0,0,89.1096,103.07,184.97,0,0,31223,5,9,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1161,莱特,普攻,1161_NA_1,第1段普攻,普通攻击:L式轰鸣拳,,一段,0.392,0.036,0.788,0.86,0.932,0.196,0.009,0.295,0.313,0.331,0,0,0.641,0,0,0,3.8306,4.9225,17.81,0,0,0,0,0,0,0,0,25,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1161,莱特,普攻,1161_NA_2,第2段普攻,普通攻击:L式轰鸣拳,,二段,0.48,0.044,0.964,1.052,1.14,0.329,0.015,0.494,0.524,0.554,0,0,1.075,0,0,0,6.3986,8.2225,29.86,0,0,0,0,0,0,0,0,29,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1161,莱特,普攻,1161_NA_3,第3段普攻,普通攻击:L式轰鸣拳,,三段,0.553,0.051,1.114,1.216,1.318,0.467,0.022,0.709,0.753,0.797,0,0,1.526,0,0,0,9.0736,11.66,42.38,0,0,0,0,0,0,0,0,25,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1161,莱特,普攻,1161_SNA_1,第1段特殊普攻,普通攻击:L式轰鸣拳,,追击一段,0.869,0.079,1.738,1.896,2.054,0.605,0.028,0.913,0.969,1.025,0,0,1.978,0,0,0,11.77,15.125,54.95,0,0,0,0,0,0,0,0,30,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1161,莱特,普攻,1161_SNA_2,第2段特殊普攻,普通攻击:L式轰鸣拳,,追击二段,0.477,0.044,0.961,1.049,1.137,0.332,0.016,0.508,0.54,0.572,0,0,1.085,0,0,0,6.4628,8.305,30.13,0,0,0,0,0,0,0,0,40,4,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1161,莱特,普攻,1161_SNA_3,第3段特殊普攻,普通攻击:L式轰鸣拳,,追击三段,0.581,0.053,1.164,1.27,1.376,0.404,0.019,0.613,0.651,0.689,0,0,1.321,0,0,0,7.8538,10.0925,36.69,0,0,0,0,0,0,0,0,58,4,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1161,莱特,普攻,1161_SNA_4,第4段特殊普攻,普通攻击:L式轰鸣拳,,追击四段,0.328,0.03,0.658,0.718,0.778,0.228,0.011,0.349,0.371,0.393,0,0,0.745,0,0,0,4.4298,5.6925,20.69,0,0,0,0,0,0,0,0,20,2,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1161,莱特,普攻,1161_SNA_5,第5段特殊普攻,普通攻击:L式轰鸣拳,,追击五段,0.89,0.081,1.781,1.943,2.105,0.619,0.029,0.938,0.996,1.054,0,0,2.025,0,0,0,12.0482,15.4825,56.23,0,0,0,0,0,0,0,0,40,3,TRUE,FALSE,0,0,,,,0,0,,,TRUE,1,FALSE,
1161,莱特,普攻,1161_NA_4,第4段普攻,普通攻击:L式轰鸣拳,,四段,0.789,0.072,1.581,1.725,1.869,0.761,0.035,1.146,1.216,1.286,0,0,2.491,0,0,0,14.8088,19.03,69.18,0,0,5505,0,0,1,1,0,33,2,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1161,莱特,普攻,1161_NA_5_SH,五段起攻,普通攻击:L式轰鸣拳,,五段起攻,1.636,0.149,3.275,3.573,3.871,1.295,0.059,1.944,2.062,2.18,0,0,4.762,0,0,0,28.3122,36.3825,132.26,0,0,12387,0,0,1,1,0,41,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1161,莱特,普攻,1161_NA_5_CoH,五段连击,普通攻击:L式轰鸣拳,,五段连击,1.229,0.112,2.461,2.685,2.909,0.813,0.037,1.22,1.294,1.368,0,0,2.798,0,0,0,16.6492,21.395,77.71,0,0,7770,0,0,1,1,0,80,18,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1161,莱特,普攻,1161_NA_5_EndH,五段终结,普通攻击:L式轰鸣拳,,五段终结,1.046,0.096,2.102,2.294,2.486,0.795,0.037,1.202,1.276,1.35,0,0,2.738,0,0,0,16.2854,20.9275,76.06,0,0,7605,0,0,1,1,0,46,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1161,莱特,普攻,1161_NA_5_EnEndH_EX,五段强力终结(士气喷发状态),普通攻击:L式轰鸣拳,士气喷发,五段强力终结(士气喷发状态),4.354,0.396,8.71,9.502,10.294,1.324,0.061,1.995,2.117,2.239,0,0,3.609,0,0,0,21.4642,215,100.24,0,0,10023,0,0,1,1,0,52,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1161,莱特,普攻,1161_NA_5_SH_EX,五段起攻(士气喷发状态),普通攻击:L式轰鸣拳,士气喷发,五段起攻(士气喷发状态),1.772,0.162,3.554,3.878,4.202,1.363,0.062,2.045,2.169,2.293,0,0,4.46,0,0,0,26.5146,215,123.88,0,0,12387,0,0,1,1,0,41,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1161,莱特,普攻,1161_NA_5_CoH_EX,五段连击(士气喷发状态),普通攻击:L式轰鸣拳,士气喷发,五段连击(士气喷发状态),3.643,0.332,7.295,7.959,8.623,0.855,0.039,1.284,1.362,1.44,0,0,2.798,0,0,0,16.6492,215,77.71,0,0,7770,0,0,1,1,0,136,24,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1161,莱特,普攻,1161_NA_5_EndH_EX,五段终结(士气喷发状态),普通攻击:L式轰鸣拳,士气喷发,五段终结(士气喷发状态),1.197,0.109,2.396,2.614,2.832,0.921,0.042,1.383,1.467,1.551,0,0,3.012,0,0,0,17.9118,23.0175,83.67,0,0,8366,0,0,1,1,0,62,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1161,莱特,特殊技,1161_E,特殊技,特殊技:V式日轮升拳,,,0.363,0.033,0.726,0.792,0.858,0.363,0.017,0.55,0.584,0.618,0,0,0,0,0,0,6.42,8.25,30,0,0,3000,1,1,1,1,0,38,3,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1161,莱特,强化特殊技,1161_E_EX_1,第1段强化特殊技,强化特殊技:V式日轮升拳-全冲程,,,4.863,0.443,9.736,10.622,11.508,4.035,0.184,6.059,6.427,6.795,40,40,0,0,0,0,100.6442,129.3325,128.31,0,0,36764,1,2,1,1,0,52,3,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1161,莱特,强化特殊技,1161_E_EX_2,第2段强化特殊技,强化特殊技:V式日轮升拳-全冲程,,追击,2.927,0.267,5.864,6.398,6.932,2.477,0.113,3.72,3.946,4.172,20,20,0,0,0,0,59.706,76.725,93.37,0,0,22326,1,2,1,1,0,76,3,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1161,莱特,冲刺攻击,1161_RA,冲刺攻击,冲刺攻击:骸突,,,0.899,0.082,1.801,1.965,2.129,0.45,0.021,0.681,0.723,0.765,0,0,1.471,0,0,0,8.7526,11.2475,40.85,0,0,0,2,3,0,0,0,80,3,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1161,莱特,闪避反击,1161_CA,闪避反击,闪避反击:烈闪,,,1.863,0.17,3.733,4.073,4.413,1.687,0.077,2.534,2.688,2.842,0,0,1.921,0,0,0,11.4276,14.685,203.34,0,0,5333,2,4,1,1,0,40,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1161,莱特,连携技,1161_QTE,连携技,连携技:V式灼日炎,,,7.121,0.648,14.249,15.545,16.841,2.732,0.125,4.107,4.357,4.607,0,0,0,0,0,0,195.4676,251.185,248.34,0,0,44783,3,5,1,1,0,67,10,TRUE,TRUE,0,67,,,,0,0,,,TRUE,1,TRUE,
1161,莱特,终结技,1161_Q,终结技,终结技:W式桂冠终火,,,15.08,1.371,30.161,32.903,35.645,9.474,0.431,14.215,15.077,15.939,0,0,0,0,0,0,0,0,596.7,0,0,9669,3,6,1,1,0,4,3,TRUE,TRUE,0,4,,,,0,0,,,TRUE,1,TRUE,
1161,莱特,受击支援,1161_BH_Aid,受击支援,快速支援:烈闪-守,,,0.697,0.064,1.401,1.529,1.657,0.697,0.032,1.049,1.113,1.177,0,0,2.28,0,0,0,13.5676,17.435,31.67,0,0,6333,5,7,1,1,0,40,3,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1161,莱特,招架/回避支援,1161_Light_parry_Aid,轻招架,招架支援:瞬破,,轻招架,0,0,0,0,0,2.713,0.124,4.077,4.325,4.573,0,0,0,0,0,0,0,0,366.64,0,0,0,5,8,0,0,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1161,莱特,招架/回避支援,1161_Heavy_parry_Aid,重招架,招架支援:瞬破,,重招架,0,0,0,0,0,3.428,0.156,5.144,5.456,5.768,0,0,0,0,0,0,0,0,416.64,0,0,0,5,8,0,0,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1161,莱特,招架/回避支援,1161_Chain_parry_Aid,连续招架,招架支援:瞬破,,连续招架,0,0,0,0,0,1.668,0.076,2.504,2.656,2.808,0,0,0,0,0,0,0,0,116.64,0,0,0,5,8,0,0,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1161,莱特,突击支援,1161_Assault_Aid,突击支援,支援突击:骸突-刺,,,2.198,0.2,4.398,4.798,5.198,1.823,0.083,2.736,2.902,3.068,0,0,0,0,0,0,50.0546,64.3225,63.34,0,0,18272,5,9,1,1,0,44,3,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1091,雅,普攻,1091_NA_1,第1段普攻,普通攻击:风花,,一段,0.269,0.025,0.544,0.594,0.644,0.135,0.007,0.212,0.226,0.24,0,0,0.44,0,0,0,2.6322,3.3825,12.21,0,0,0,0,0,0,0,0,24,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1091,雅,普攻,1091_NA_2,第2段普攻,普通攻击:风花,,二段,0.296,0.027,0.593,0.647,0.701,0.269,0.013,0.412,0.438,0.464,0,0,0.879,0,0,0,5.243,6.7375,24.42,0,0,0,0,0,0,0,0,21,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1091,雅,普攻,1091_NA_3,第3段普攻,普通攻击:风花,,三段,0.628,0.058,1.266,1.382,1.498,0.464,0.022,0.706,0.75,0.794,0,0,1.517,0,0,0,9.0308,11.605,42.14,0,0,6290,0,0,5,1,0,30,2,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1091,雅,普攻,1091_NA_4,第4段普攻,普通攻击:风花,,四段,0.965,0.088,1.933,2.109,2.285,0.821,0.038,1.239,1.315,1.391,0,0,2.686,0,0,0,15.9644,20.515,74.6,0,0,9121,0,0,5,1,0,49,4,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1091,雅,普攻,1091_NA_5,第5段普攻,普通攻击:风花,,五段,1.29,0.118,2.588,2.824,3.06,1.31,0.06,1.97,2.09,2.21,0,0,4.285,0,0,0,25.4874,32.7525,119.01,0,0,12927,0,0,5,1,0,58,8,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1091,雅,特殊技,1091_E,特殊技,特殊技:深雪,,,0.358,0.033,0.721,0.787,0.853,0.358,0.017,0.545,0.579,0.613,0,0,0,0,0,0,6.4414,8.965,32.52,0,0,3251,1,1,5,1,0,45,3,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1091,雅,强化特殊技,1091_E_EX_A_1,强化E形态A第1段,强化特殊技:飞雪,,斩击A,1.574,0.144,3.158,3.446,3.734,1.287,0.059,1.936,2.054,2.172,40,20,0,0,0,0,36.6154,46.53,38.67,0,0,15775,1,2,5,1,0,31,4,TRUE,TRUE,0,0,,1091_E_EX_A_2,,0,0,,,FALSE,1,FALSE,
1091,雅,强化特殊技,1091_E_EX_B_1,强化E形态B第1段,强化特殊技:飞雪,,斩击B,2.36,0.215,4.725,5.155,5.585,1.93,0.088,2.898,3.074,3.25,40,20,0,0,0,0,54.9338,69.7675,58.01,0,0,25052,1,2,5,1,0,36,3,TRUE,TRUE,0,0,,1091_E_EX_B_2,,0,0,,,FALSE,1,FALSE,
1091,雅,强化特殊技,1091_E_EX_A_2,强化E形态A第2段,强化特殊技:飞雪,,追击A,1.933,0.176,3.869,4.221,4.573,1.62,0.074,2.434,2.582,2.73,20,20,0,0,0,0,42.8214,56.155,62,0,0,18925,1,2,5,1,0,30,3,TRUE,TRUE,0,0,,,1091_E_EX_A_1,0,0,,,TRUE,1,FALSE,
1091,雅,强化特殊技,1091_E_EX_B_2,强化E形态B第2段,强化特殊技:飞雪,,追击B,2.899,0.264,5.803,6.331,6.859,2.43,0.111,3.651,3.873,4.095,20,20,0,0,0,0,64.2428,84.205,93,0,0,29777,1,2,5,1,0,66,9,TRUE,TRUE,0,0,,,1091_E_EX_B_1,0,0,,,TRUE,1,FALSE,
1091,雅,冲刺攻击,1091_RA,冲刺攻击,冲刺攻击:冬蜂,,,0.258,0.024,0.522,0.57,0.618,0.129,0.006,0.195,0.207,0.219,0,0,0.421,0,0,0,4.6438,3.2175,11.69,0,0,0,2,3,0,0,0,13,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1091,雅,闪避反击,1091_CA,闪避反击,闪避反击:寒雀,,,2.459,0.224,4.923,5.371,5.819,2.145,0.098,3.223,3.419,3.615,0,0,3.42,0,0,0,20.33,26.125,245,0,0,9499,2,4,5,1,0,64,5,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1091,雅,连携技,1091_QTE_A,连携技_A,连携技:春临,,A,1.884,0.172,3.776,4.12,4.464,0.567,0.026,0.853,0.905,0.957,0,0,0,0,0,0,51.253,69.0525,51.52,0,0,11136,3,5,5,1,0,38,2,TRUE,TRUE,0,38,,1091_QTE_B,,0,0,,,FALSE,1,TRUE,
1091,雅,连携技,1091_QTE_B,连携技_B,连携技:春临,,B,1.884,0.172,3.776,4.12,4.464,0.567,0.026,0.853,0.905,0.957,0,0,0,0,0,0,51.253,69.0525,51.52,0,0,11136,3,5,5,1,0,38,2,TRUE,TRUE,0,38,,1091_QTE_C,1091_QTE_A,0,0,,,FALSE,1,TRUE,
1091,雅,连携技,1091_QTE_C,连携技_C,连携技:春临,,C,2.512,0.229,5.031,5.489,5.947,0.756,0.035,1.141,1.211,1.281,0,0,0,0,0,0,68.3516,92.0425,68.69,0,0,14848,3,5,5,1,0,38,2,TRUE,TRUE,0,38,,,1091_QTE_B,0,0,,,TRUE,1,TRUE,
1091,雅,终结技,1091_Q,终结技,终结技:名残雪,,,23.88,2.171,47.761,52.103,56.445,3.704,0.169,5.563,5.901,6.239,0,0,0,0,0,0,0,0,836.67,0,0,113716,3,6,5,1,0,205,6,TRUE,TRUE,0,205,,,,0,0,,,TRUE,1,TRUE,
1091,雅,受击支援,1091_BH_Aid,受击支援,快速支援:花信风,,,1.045,0.095,2.09,2.28,2.47,1.045,0.048,1.573,1.669,1.765,0,0,3.42,0,0,0,20.33,26.125,47.5,0,0,9499,5,7,5,1,0,64,5,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1091,雅,招架/回避支援,1091_Light_parry_Aid,轻招架,招架支援:花筏,,轻招架,0,0,0,0,0,2.713,0.124,4.077,4.325,4.573,0,0,0,0,0,0,0,0,366.64,0,0,0,5,8,0,0,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1091,雅,招架/回避支援,1091_Heavy_parry_Aid,重招架,招架支援:花筏,,重招架,0,0,0,0,0,3.428,0.156,5.144,5.456,5.768,0,0,0,0,0,0,0,0,416.64,0,0,0,5,8,0,0,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1091,雅,招架/回避支援,1091_Chain_parry_Aid,连续招架,招架支援:花筏,,连续招架,0,0,0,0,0,1.283,0.059,1.932,2.05,2.168,0,0,0,0,0,0,0,0,116.64,0,0,0,5,8,0,0,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1091,雅,突击支援,1091_Assault_Aid,突击支援,支援突击:花辞,,,3.378,0.308,6.766,7.382,7.998,2.919,0.133,4.382,4.648,4.914,0,0,0,0,0,0,74.6646,95.9475,139.97,0,0,28617,5,9,5,1,0,81,10,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1091,雅,普攻,1091_SNA_1,第1段特殊普攻,普通攻击:霜月,,一段蓄力斩击,4.547,0.414,9.101,9.929,10.757,0.44,0.02,0.66,0.7,0.74,0,0,1.44,0,0,0,18.5538,11,39.99,0,0,3998,0,0,5,1,0,61,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1091,雅,普攻,1091_SNA_2,第2段特殊普攻,普通攻击:霜月,,二段蓄力斩击,8.581,0.781,17.172,18.734,20.296,0.624,0.029,0.943,1.001,1.059,0,0,2.041,0,0,0,32.8276,15.5925,56.69,0,0,5668,0,0,5,1,0,72,2,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1091,雅,普攻,1091_SNA_3,第3段特殊普攻,普通攻击:霜月,,三段蓄力斩击,21.411,1.947,42.828,46.722,50.616,3.778,0.172,5.67,6.014,6.358,0,0,6.181,0,0,0,89.1738,94.435,343.37,0,0,34336,0,0,5,1,0,209,16,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1091,雅,突击支援,1091_Core_Passive,核心被动,核心技:霜灼破,,,0.42,0.04,0.85,0.93,1.01,0.265,0.013,0.408,0.434,0.46,0,0,0.867,0,0,0,0,6.6275,0,0,0,0,5,9,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1201,悠真,普攻,1201_NA_1,第1段普攻,普通攻击:穿云,,一段,0.424,0.039,0.853,0.931,1.009,0.265,0.013,0.408,0.434,0.46,0,0,0.867,0,0,0,4.970625,6.6275,24.07,0,0,0,0,0,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1201,悠真,普攻,1201_NA_2,第2段普攻,普通攻击:穿云,,二段,0.398,0.037,0.805,0.879,0.953,0.443,0.021,0.674,0.716,0.758,0,0,1.45,0,0,0,8.311875,11.0825,40.28,0,0,0,0,0,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1201,悠真,普攻,1201_NA_3,第3段普攻,普通攻击:穿云,,三段,0.709,0.065,1.424,1.554,1.684,0.661,0.031,1.002,1.064,1.126,0,0,2.164,0,0,0,12.395625,16.5275,60.09,0,0,0,0,0,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1201,悠真,普攻,1201_NA_4,第4段普攻,普通攻击:穿云,,四段,0.901,0.082,1.803,1.967,2.131,0.896,0.041,1.347,1.429,1.511,0,0,2.93,0,0,0,16.78875,22.385,81.38,0,0,4917,0,0,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1201,悠真,普攻,1201_NA_5,第5段普攻,普通攻击:穿云,,五段,1.329,0.121,2.66,2.902,3.144,1.154,0.053,1.737,1.843,1.949,0,0,3.777,0,0,0,21.65625,28.875,104.91,0,0,6584,0,0,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1201,悠真,普攻,1201_NA_Switch,普攻状态切换,普通攻击:穿云·移形,,,0.695,0.064,1.399,1.527,1.655,0.487,0.023,0.74,0.786,0.832,0,0,1.593,0,0,0,9.136875,12.1825,44.23,0,0,0,0,0,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1201,悠真,普攻,1201_SNA,特殊普攻,普通攻击:落羽,,,1.054,0.096,2.11,2.302,2.494,0.527,0.024,0.791,0.839,0.887,0,0,1.724,0,0,0,9.879375,13.1725,47.88,0,0,3351,0,0,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1201,悠真,普攻,1201_CoAttack,协同攻击,普通攻击:甲乙矢,,协同攻击,0.159,0.015,0.324,0.354,0.384,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,FALSE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1201,悠真,特殊技,1201_E,特殊技,特殊技:天罗,,,0.523,0.048,1.051,1.147,1.243,0.523,0.024,0.787,0.835,0.883,0,0,0,0,0,0,9.8175,13.09,47.52,0,0,3326,1,1,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1201,悠真,强化特殊技,1201_E_EX,强化特殊技,强化特殊技:地网,,,4.493,0.409,8.992,9.81,10.628,4.49,0.205,6.745,7.155,7.565,60,60,0,0,0,0,123.585,164.78,121.64,0,0,31911,1,2,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1201,悠真,冲刺攻击,1201_RA,冲刺攻击,冲刺攻击:飞弦,,,0.807,0.074,1.621,1.769,1.917,0.404,0.019,0.613,0.651,0.689,0,0,1.321,0,0,0,7.569375,10.0925,36.67,0,0,0,2,3,0,1,0,0,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1201,悠真,闪避反击,1201_CA,闪避反击,闪避反击:藏锋,,,2.196,0.2,4.396,4.796,5.196,1.943,0.089,2.922,3.1,3.278,0,0,2.759,0,0,0,15.819375,21.0925,226.64,0,0,5364,2,4,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1201,悠真,连携技,1201_QTE,连携技,连携技:会·离,,,5.176,0.471,10.357,11.299,12.241,1.834,0.084,2.758,2.926,3.094,0,0,0,0,0,0,171.538125,228.7175,166.7,0,0,25633,3,5,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1201,悠真,终结技,1201_Q,终结技,终结技:残心,,,19.539,1.777,39.086,42.64,46.194,1.069,0.049,1.608,1.706,1.804,0,0,0,0,0,0,0,0,588.31,0,0,6181,3,6,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,TRUE,
1201,悠真,受击支援,1201_BH_Aid,受击支援,快速支援:穿弦,,,0.843,0.077,1.69,1.844,1.998,0.843,0.039,1.272,1.35,1.428,0,0,2.759,0,0,0,15.819375,21.0925,38.32,0,0,5364,5,7,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1201,悠真,招架/回避支援,1201_Light_parry_Aid,轻招架,招架支援:构身,,轻招架,0,0,0,0,0,2.476,0.113,3.719,3.945,4.171,0,0,0,0,0,0,0,0,350.04,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1201,悠真,招架/回避支援,1201_Heavy_parry_Aid,重招架,招架支援:构身,,重招架,0,0,0,0,0,2.952,0.135,4.437,4.707,4.977,0,0,0,0,0,0,0,0,383.34,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1201,悠真,招架/回避支援,1201_Chain_parry_Aid,连续招架,招架支援:构身,,连续招架,0,0,0,0,0,1.192,0.055,1.797,1.907,2.017,0,0,0,0,0,0,0,0,83.34,0,0,0,5,8,0,1,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1201,悠真,招架/回避支援,1201_Assault_Aid,突击支援,支援突击:构身·斩,,,3.071,0.28,6.151,6.711,7.271,2.633,0.12,3.953,4.193,4.433,0,0,0,0,0,0,65.773125,87.6975,120.01,0,0,18145,5,8,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1201,悠真,冲刺攻击,1201_SRA_1,第一段特殊冲刺攻击,冲刺攻击:飞弦·斩,,一段,1.623,0.148,3.251,3.547,3.843,0.66,0.03,0.99,1.05,1.11,0,0,1.08,0,0,0,12.375,16.5,29.99,0,0,4197,2,3,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1201,悠真,冲刺攻击,1201_SRA_2,第二段特殊冲刺攻击,冲刺攻击:飞弦·斩,,二段,1.666,0.152,3.338,3.642,3.946,0.459,0.021,0.69,0.732,0.774,0,0,0.751,0,0,0,8.600625,11.4675,41.67,0,0,2916,2,3,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1201,悠真,冲刺攻击,1201_SRA_3,第三段特殊冲刺攻击,冲刺攻击:飞弦·斩,,三段,1.896,0.173,3.799,4.145,4.491,0.495,0.023,0.748,0.794,0.84,0,0,0.81,0,0,0,9.28125,12.375,44.97,0,0,3147,2,3,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1151,露西,普攻,1151_NA_1,第1段普攻,普通攻击:淑女的球棍,,一段,0.566,0.052,1.138,1.242,1.346,0.283,0.013,0.426,0.452,0.478,0,0,1.019,0,0,0,5.836875,7.7825,28.3,0,0,0,0,0,0,1,0,39,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1151,露西,普攻,1151_NA_2,第2段普攻,普通攻击:淑女的球棍,,二段,0.778,0.071,1.559,1.701,1.843,0.65,0.03,0.98,1.04,1.1,0,0,2.34,0,0,0,13.40625,17.875,64.98,0,0,0,0,0,0,1,0,40,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1151,露西,普攻,1151_NA_3,第3段普攻,普通攻击:淑女的球棍,,三段(派生),2.115,0.193,4.238,4.624,5.01,1.623,0.074,2.437,2.585,2.733,0,0,5.842,0,0,0,33.474375,44.6325,162.27,0,0,0,0,0,0,1,0,81,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1151,露西,普攻,1151_NA_3_ALT,第3段派生普攻,普通攻击:淑女的球棍,,三段,1.889,0.172,3.781,4.125,4.469,1.574,0.072,2.366,2.51,2.654,0,0,5.666,0,0,0,32.46375,43.285,157.38,0,0,13401,0,0,1,1,0,96,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1151,露西,普攻,1151_NA_4,第4段普攻,普通攻击:淑女的球棍,,四段,2.726,0.248,5.454,5.95,6.446,2.08,0.095,3.125,3.315,3.505,0,0,7.488,0,0,0,42.9,57.2,207.99,0,0,18625,0,0,1,1,0,80,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1151,露西,特殊技,1151_E_A,特殊技形态A,特殊技:安打!,,平直球,0.617,0.057,1.244,1.358,1.472,0.617,0.029,0.936,0.994,1.052,0,0,0,0,0,0,12.725625,16.9675,61.66,0,0,6165,1,1,1,1,0,83,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1151,露西,特殊技,1151_E_B,特殊技形态B,特殊技:安打!,,高飞球,0.692,0.063,1.385,1.511,1.637,0.692,0.032,1.044,1.108,1.172,0,0,0,0,0,0,14.2725,19.03,69.15,0,0,6915,1,1,1,1,0,88,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1151,露西,强化特殊技,1151_E_EX_A,强化E形态A,强化特殊技:全垒打!,,平直球,5.084,0.463,10.177,11.103,12.029,3.646,0.166,5.472,5.804,6.136,60,60,0,0,0,0,124.61625,166.155,125.01,0,0,33541,1,2,1,1,0,84,1,TRUE,TRUE,0,0,,,,1,15,,,TRUE,1,FALSE,
1151,露西,强化特殊技,1151_E_EX_B,强化E形态B,强化特殊技:全垒打!,,高飞球,5.364,0.488,10.732,11.708,12.684,3.896,0.178,5.854,6.21,6.566,60,60,0,0,0,0,130.80375,174.405,145,0,0,34241,1,2,1,1,0,91,1,TRUE,TRUE,0,0,,,,1,15,,,TRUE,1,FALSE,
1151,露西,冲刺攻击,1151_RA,冲刺攻击,冲刺攻击:豪勇猪突!,,,0.784,0.072,1.576,1.72,1.864,0.392,0.018,0.59,0.626,0.662,0,0,1.411,0,0,0,8.085,10.78,39.19,0,0,0,2,3,0,1,0,56,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1151,露西,闪避反击,1151_CA,闪避反击,闪避反击:獠牙折转!,,,3.08,0.28,6.16,6.72,7.28,2.6,0.119,3.909,4.147,4.385,0,0,5.759,0,0,0,33,44,309.97,0,0,15996,2,4,1,1,0,101,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1151,露西,连携技,1151_QTE,连携技,连携技:大满贯!,,*3,4.924,0.448,9.852,10.748,11.644,0.934,0.043,1.407,1.493,1.579,0,0,0,0,0,0,156.42,208.56,93.31,0,0,29280,3,5,1,1,0,67,1,TRUE,TRUE,0,67,,,,1,15,,,TRUE,1,TRUE,
1151,露西,终结技,1151_Q,终结技,终结技:再见全垒打!,,,17.186,1.563,34.379,37.505,40.631,2.835,0.129,4.254,4.512,4.77,0,0,0,0,0,0,0,0,815,0,0,31500,3,6,1,1,0,90,1,TRUE,TRUE,0,90,,,,1,15,,,TRUE,1,TRUE,
1151,露西,受击支援,1151_BH_Aid,受击支援,快速支援:触身球!,,,1.6,0.146,3.206,3.498,3.79,1.6,0.073,2.403,2.549,2.695,0,0,5.759,0,0,0,33,44,79.99,0,0,15996,5,7,1,1,0,101,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1151,露西,招架/回避支援,1151_Light_parry_Aid,轻招架,招架支援:安全上垒!,,轻招架,0,0,0,0,0,2.077,0.095,3.122,3.312,3.502,0,0,0,0,0,0,0,0,336.64,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1151,露西,招架/回避支援,1151_Heavy_parry_Aid,重招架,招架支援:安全上垒!,,重招架,0,0,0,0,0,2.294,0.105,3.449,3.659,3.869,0,0,0,0,0,0,0,0,353.34,0,0,0,5,8,0,1,0,30,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1151,露西,招架/回避支援,1151_Chain_parry_Aid,连续招架,招架支援:安全上垒!,,连续招架,0,0,0,0,0,0.694,0.032,1.046,1.11,1.174,0,0,0,0,0,0,0,0,53.34,0,0,0,5,8,0,1,0,10,1,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,TRUE,
1151,露西,突击支援,1151_Assault_Aid,突击支援,支援突击:触垒得分!,,,3.491,0.318,6.989,7.625,8.261,3.043,0.139,4.572,4.85,5.128,0,0,0,0,0,0,81.241875,108.3225,169.97,0,0,32667,5,9,1,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1151,露西,普攻,1151_CoAttack_1,第一段协同攻击,亲卫队小猪:抄家伙!,,棒球棍,0.925,0.085,1.86,2.03,2.2,0.155,0.008,0.243,0.259,0.275,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,205,1,FALSE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1151,露西,普攻,1151_CoAttack_2,第二段协同攻击,亲卫队小猪:抄家伙!,,拳套,1.275,0.116,2.551,2.783,3.015,0.213,0.01,0.323,0.343,0.363,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,180,1,FALSE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1151,露西,普攻,1151_CoAttack_3,第三段协同攻击,亲卫队小猪:抄家伙!,,弹弓,1.75,0.16,3.51,3.83,4.15,0.292,0.014,0.446,0.474,0.502,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,220,1,FALSE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1151,露西,普攻,1151_CoAttack,协同攻击,亲卫队小猪:回旋挥击!,,回旋挥击,2.5,0.228,5.008,5.464,5.92,0.2,0.01,0.31,0.33,0.35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,190,1,FALSE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1151,露西,强化特殊技,1151_Cinema_6,影画6,獠牙堪烈火,,影画6,3,0,3,3,3,0.417,0,0.417,0.417,0.417,0,0,0,0,0,0,0,0,0,0,0,4166,1,2,1,1,0,0,1,FALSE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1311,耀嘉音,普攻,1311_NA_1,第1段普攻,普通攻击:《随想曲》,,一段,0.438,0.04,0.878,0.958,1.038,0.413,0.019,0.622,0.66,0.698,0,0,0.675,0,0,0,9.343125,10.3125,37.46,2,0,3745,0,0,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1311,耀嘉音,普攻,1311_NA_2,第2段普攻,普通攻击:《随想曲》,,二段,0.591,0.054,1.185,1.293,1.401,0.564,0.026,0.85,0.902,0.954,0,0,0.923,0,0,0,10.8075,14.1075,51.26,2,0,5125,0,0,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1311,耀嘉音,普攻,1311_NA_3_NFC,第3段普攻(不满),普通攻击:《随想曲》,,三段最小,1.209,0.11,2.419,2.639,2.859,1.148,0.053,1.731,1.837,1.943,0,0,1.878,0,0,0,21.82125,28.71,104.32,2,0,10431,0,0,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1311,耀嘉音,普攻,1311_NA_3_FC,第3段普攻(满),普通攻击:《随想曲》,,三段最大,2.707,0.247,5.424,5.918,6.412,2.194,0.1,3.294,3.494,3.694,0,0,3.59,0,0,0,48.38625,54.835,199.4,2,0,19939,0,0,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1311,耀嘉音,普攻,1311_SNA_1,第1段特殊普攻,普通攻击:间奏,,一段,0.55,0.05,1.1,1.2,1.3,0.484,0.022,0.726,0.77,0.814,0,0,0.634,0,0,0,3.63,4.84,43.98,2,0,1759,0,0,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1311,耀嘉音,普攻,1311_SNA_2,第2段特殊普攻,普通攻击:间奏,,二段,0.55,0.05,1.1,1.2,1.3,0.484,0.022,0.726,0.77,0.814,0,0,0.634,0,0,0,3.63,4.84,43.98,2,0,1759,0,0,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1311,耀嘉音,普攻,1311_SNA_3,第3段特殊普攻,普通攻击:间奏,,三段(单段,未 * 2),0.55,0.05,1.1,1.2,1.3,0.484,0.022,0.726,0.77,0.814,0,0,0.634,0,0,0,3.63,4.84,43.98,2,0,1759,0,0,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1311,耀嘉音,普攻,1311_SCA,特殊闪避反击,普通攻击:副歌,,(单段,未 * 4),0.55,0.05,1.1,1.2,1.3,0.484,0.022,0.726,0.77,0.814,0,0,0.634,0,0,0,3.63,4.84,43.98,2,0,1759,0,0,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1311,耀嘉音,普攻,1311_NA_Switch,普攻状态切换,普通攻击:终曲,,,0.55,0.05,1.1,1.2,1.3,0.484,0.022,0.726,0.77,0.814,0,0,0.634,0,0,0,3.63,4.84,43.98,2,0,1759,0,0,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1311,耀嘉音,特殊技,1311_E_A,特殊技形态A,特殊技:《风铃与旧约》,,最小,0.55,0.05,1.1,1.2,1.3,0.484,0.022,0.726,0.77,0.814,0,0,0,0,0,0,3.63,0,43.98,2,0,1759,1,1,4,1,0,85,1,TRUE,TRUE,0,56,,,,0,0,[55],,TRUE,1,FALSE,
1311,耀嘉音,特殊技,1311_E_B,特殊技形态B,特殊技:《风铃与旧约》,,额外攻击,单段,未*4,0.55,0.05,1.1,1.2,1.3,0.484,0.022,0.726,0.77,0.814,0,0,0,0,0,0,3.63,0,43.98,2,0,1759,1,1,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1311,耀嘉音,强化特殊技,1311_E_EX_A,强化E形态A,和弦,,首段震音;耀佳音的快速支援不是通过某个技能触发的,而是通过Buff触发的,所以震音中没有填写触发快速支援的对应参数。,0.46,0.042,0.922,1.006,1.09,0,0,0,0,0,25,25,0,0,0,0,0,0,0,0,0,2977,1,2,4,1,0,35,1,FALSE,TRUE,0,0,{'additional_damage': 1},,,0,0,[21],,FALSE,1,FALSE,
1311,耀嘉音,强化特殊技,1311_E_EX_A_FREE,强化E形态A(后续追加),和弦,,追加震音,后续的震音触发是没有能耗也没有强化E标签的,所以写一个单独的技能。,0.46,0.042,0.922,1.006,1.09,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2977,1,1,4,1,0,35,1,FALSE,TRUE,0,0,{'additional_damage': 1},,,0,0,[21],,FALSE,1,FALSE,
1311,耀嘉音,强化特殊技,1311_E_EX_B,强化E形态B(单段),和弦,,追加音簇(单段,未 * 3),0.24,0.022,0.482,0.526,0.57,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1032,1,2,4,1,0,20,1,FALSE,TRUE,0,0,{'additional_damage': 1},,,0,0,[14],,FALSE,1,FALSE,
1311,耀嘉音,强化特殊技,1311_E_EX_C,强化E形态B(三连),和弦,,追加音簇(单段, * 3),0.72,0.066,1.446,1.578,1.71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3096,1,2,4,1,0,50,3,FALSE,TRUE,0,0,{'additional_damage': 1},,,0,0,"[21, 34, 45]",,FALSE,1,FALSE,
1311,耀嘉音,冲刺攻击,1311_RA,冲刺攻击,冲刺攻击:《蚀月奏》,,,0.807,0.074,1.621,1.769,1.917,0.404,0.019,0.613,0.651,0.689,0,0,1.32,0,0,0,7.569375,10.0925,36.65,2,0,3664,2,3,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1311,耀嘉音,闪避反击,1311_CA,闪避反击,闪避反击:《折伞华尔兹》,,,1.1,0.1,2.2,2.4,2.6,0.968,0.044,1.452,1.54,1.628,0,0,1.267,0,0,0,7.26,9.68,87.96,2,0,3518,2,4,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1311,耀嘉音,连携技,1311_QTE,连携技,连携技:《微醺协奏》,,,6.718,0.611,13.439,14.661,15.883,2.329,0.106,3.495,3.707,3.919,0,0,0,0,0,0,180.819375,241.0925,211.67,0,0,41116,3,5,4,1,0,113,1,TRUE,TRUE,0,113,,,,0,0,[46],,TRUE,1,TRUE,
1311,耀嘉音,终结技,1311_Q,终结技,终结技:《幻想式奏鸣》,,,19.598,1.782,39.2,42.764,46.328,2.383,0.109,3.582,3.8,4.018,0,0,0,0,0,0,0,0,716.64,0,0,21663,3,6,4,1,0,116,4,TRUE,TRUE,0,116,,,,0,0,"[7, 22, 31, 46]",,TRUE,1,FALSE,
1311,耀嘉音,受击支援,1311_BH_Aid,受击支援,快速支援:《一川烟火》,,,0.55,0.05,1.1,1.2,1.3,0.484,0.022,0.726,0.77,0.814,0,0,0.634,0,0,0,3.63,4.84,43.98,2,0,1759,5,7,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1311,耀嘉音,突击支援,1311_Assault_Aid,突击支援,支援突击:《三生初见》,,,3.046,0.277,6.093,6.647,7.201,2.61,0.119,3.919,4.157,4.395,0,0,0,0,0,0,65.278125,87.0375,118.37,2,0,25701,5,9,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1311,耀嘉音,普攻,1311_Cinema_4,影画4,,,,0.51,0.047,1.029,1.123,1.217,0.26,0.01,0.388,0.412,0.436,0,0,0.838,0,0,0,0,6.4075,0,0,0,0,0,0,4,1,0,0,1,FALSE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1321,伊芙琳,普攻,1321_NA_1,第1段普攻,普通攻击:割弦,,一段,0.512,0.047,1.029,1.123,1.217,0.256,0.012,0.388,0.412,0.436,0,0,0.838,0,0,0,4.805625,6.4075,23.28,0,0,0,0,0,0,1,0,32,2,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1321,伊芙琳,普攻,1321_NA_2,第2段普攻,普通攻击:割弦,,二段,0.621,0.057,1.248,1.362,1.476,0.538,0.025,0.813,0.863,0.913,0,0,1.761,0,0,0,10.085625,13.4475,48.9,0,0,0,0,0,0,1,0,229,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1321,伊芙琳,普攻,1321_NA_3,第3段普攻,普通攻击:割弦,,三段,0.782,0.072,1.574,1.718,1.862,0.621,0.029,0.94,0.998,1.056,0,0,2.031,0,0,0,11.653125,15.5375,56.42,0,0,0,0,0,0,1,0,36,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1321,伊芙琳,普攻,1321_NA_4,第4段普攻,普通攻击:割弦,,四段,1.867,0.17,3.737,4.077,4.417,1.53,0.07,2.3,2.44,2.58,0,0,5.006,0,0,0,28.689375,38.2525,139.04,0,0,13903,0,0,1,1,0,76,5,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1321,伊芙琳,普攻,1321_NA_5,第5段普攻,普通攻击:割弦,,五段,2.234,0.204,4.478,4.886,5.294,1.683,0.077,2.53,2.684,2.838,0,0,5.505,0,0,0,31.55625,42.075,152.92,0,0,15291,0,0,1,1,0,74,8,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1321,伊芙琳,普攻,1321_SNA_1,第1段特殊普攻,普通攻击:绞勒式·I型,,,2.264,0.206,4.53,4.942,5.354,1.211,0.056,1.827,1.939,2.051,0,0,3.962,0,0,0,22.708125,30.2775,110.04,0,0,11003,0,0,1,1,0,67,4,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1321,伊芙琳,普攻,1321_SNA_2,第2段特殊普攻,普通攻击:绞勒式·II型,,,2.452,0.223,4.905,5.351,5.797,1.248,0.057,1.875,1.989,2.103,0,0,4.082,0,0,0,23.38875,31.185,113.37,0,0,11336,0,0,1,1,0,79,4,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1321,伊芙琳,特殊技,1321_E_1,第1段特殊技,特殊技:锁系控位,,一段,0.523,0.048,1.051,1.147,1.243,0.523,0.024,0.787,0.835,0.883,0,0,0,0,0,0,19.59375,26.125,47.49,0,0,4748,1,1,1,1,0,96,6,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1321,伊芙琳,特殊技,1321_E_2,第2段特殊技,特殊技:束裂式·I型,,缠绕,0.404,0.037,0.811,0.885,0.959,0.404,0.019,0.613,0.651,0.689,0,0,0,0,0,0,15.118125,20.1575,36.65,0,0,3664,1,1,1,1,0,149,9,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1321,伊芙琳,特殊技,1321_E_A,特殊技形态A,特殊技:束裂式·I型,,引爆,0.34,0.031,0.681,0.743,0.805,0.34,0.016,0.516,0.548,0.58,0,0,0,0,0,0,12.725625,16.9675,30.85,0,0,3084,1,1,1,1,0,129,9,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1321,伊芙琳,强化特殊技,1321_E_EX,强化特殊技,强化特殊技:束裂式·终型,,缠绕,5.412,0.492,10.824,11.808,12.792,4.371,0.199,6.56,6.958,7.356,0,0,0,0,0,0,121.006875,161.3425,113.31,0,0,44462,1,2,1,1,0,100,6,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1321,伊芙琳,强化特殊技,1321_E_EX_A,强化E形态A,强化特殊技:束裂式·终型,,引爆,0.596,0.055,1.201,1.311,1.421,0.596,0.028,0.904,0.96,1.016,0,0,0,0,0,0,12.890625,17.1875,41.64,0,0,5620,1,2,1,1,0,71,9,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1321,伊芙琳,冲刺攻击,1321_RA,冲刺攻击,冲刺攻击:穿梭潜袭,,,0.605,0.055,1.21,1.32,1.43,0.303,0.014,0.457,0.485,0.513,0,0,0.99,0,0,0,5.671875,7.5625,27.49,0,0,0,2,3,0,1,0,34,2,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1321,伊芙琳,闪避反击,1321_CA,闪避反击,闪避反击:绞缢反制,,,2.102,0.192,4.214,4.598,4.982,1.871,0.086,2.817,2.989,3.161,0,0,2.522,0,0,0,14.458125,19.2775,220.04,0,0,7003,2,4,1,1,0,47,3,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1321,伊芙琳,连携技,1321_QTE,连携技,连携技:月辉丝·绊,,,8.293,0.754,16.587,18.095,19.603,2.366,0.108,3.554,3.77,3.986,0,0,0,0,0,0,127.070625,169.4275,215.04,0,0,29017,3,5,1,1,0,137,14,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1321,伊芙琳,终结技,1321_Q,终结技,终结技:月辉丝·弦音,,,19.885,1.808,39.773,43.389,47.005,2.604,0.119,3.913,4.151,4.389,0,0,0,0,0,0,0,0,736.71,0,0,23670,3,6,1,1,0,132,15,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1321,伊芙琳,受击支援,1321_BH_Aid,受击支援,快速支援:烈锋,,,0.771,0.071,1.552,1.694,1.836,0.771,0.036,1.167,1.239,1.311,0,0,2.522,0,0,0,14.458125,19.2775,35.02,0,0,7003,5,7,1,1,0,46,3,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1321,伊芙琳,招架/回避支援,1321_Light_parry_Aid,轻招架,招架支援:静默掩护,,轻招架,0,0,0,0,0,2.713,0.124,4.077,4.325,4.573,0,0,0,0,0,0,0,0,366.64,0,0,0,5,8,0,1,0,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1321,伊芙琳,招架/回避支援,1321_Heavy_parry_Aid,重招架,招架支援:静默掩护,,重招架,0,0,0,0,0,3.428,0.156,5.144,5.456,5.768,0,0,0,0,0,0,0,0,416.64,0,0,0,5,8,0,1,0,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1321,伊芙琳,招架/回避支援,1321_Chain_parry_Aid,连续招架,招架支援:静默掩护,,连续招架,0,0,0,0,0,1.668,0.076,2.504,2.656,2.808,0,0,0,0,0,0,0,0,116.64,0,0,0,5,8,0,1,0,10,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1321,伊芙琳,突击支援,1321_Assault_Aid,突击支援,支援突击:轨迹干涉,,,2.916,0.266,5.842,6.374,6.906,2.49,0.114,3.744,3.972,4.2,0,0,0,0,0,0,62.679375,83.5725,109.97,0,0,24567,5,9,1,1,0,47,3,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1031,妮可,普攻,1031_NA_1,第1段普攻,普通攻击:狡兔连打,,一段A,0.389,0.036,0.785,0.857,0.929,0.195,0.009,0.294,0.312,0.33,0,0,0.7,0,0,0,,5.3625,19.43,0,0,0,0,0,0,1,0,46,4,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1031,妮可,普攻,1031_NA_1_A,第1段普攻(形态A),普通攻击:狡兔连打,,一段B*3,0.271,0.025,0.546,0.596,0.646,0.209,0.01,0.319,0.339,0.359,0,0,0.7,0,0,0,,5.3625,20.82,0,0,0,0,0,0,1,0,46,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1031,妮可,普攻,1031_NA_2,第2段普攻,普通攻击:为所欲为,,一段A,0.389,0.036,0.785,0.857,0.929,0.195,0.009,0.294,0.312,0.33,0,0,0.7,0,0,0,,5.3625,19.43,0,0,0,0,0,0,1,0,37,5,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1031,妮可,普攻,1031_NA_2_A,第2段普攻(形态A),普通攻击:为所欲为,,一段B*3,0.494,0.045,0.989,1.079,1.169,0.209,0.01,0.319,0.339,0.359,0,0,0.7,0,0,0,,5.3625,20.82,0,0,0,0,0,0,1,0,37,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1031,妮可,普攻,1031_NA_3,第3段普攻,普通攻击:狡兔连打,,二段A,0.353,0.033,0.716,0.782,0.848,0.29,0.014,0.444,0.472,0.5,0,0,1.041,0,0,0,,7.975,28.92,0,0,0,0,0,0,1,0,105,16,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1031,妮可,普攻,1031_NA_3_A,第3段普攻(形态A),普通攻击:狡兔连打,,二段B*4,0.361,0.033,0.724,0.79,0.856,0.278,0.013,0.421,0.447,0.473,0,0,1.041,0,0,0,,7.975,27.75,0,0,0,0,0,0,1,0,105,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1031,妮可,普攻,1031_SNA_1,第1段特殊普攻,普通攻击:为所欲为,,二段A,0.353,0.033,0.716,0.782,0.848,0.29,0.014,0.444,0.472,0.5,0,0,1.041,0,0,0,,7.975,28.92,0,0,0,0,0,0,1,0,46,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1031,妮可,普攻,1031_SNA_1_A,第1段特殊普攻(形态A),普通攻击:为所欲为,,二段B*4,0.658,0.06,1.318,1.438,1.558,0.278,0.013,0.421,0.447,0.473,0,0,1.041,0,0,0,,7.975,27.75,0,0,0,0,0,0,1,0,46,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1031,妮可,普攻,1031_SNA_2,第2段特殊普攻,普通攻击:狡兔连打,,三段A,1.243,0.113,2.486,2.712,2.938,1.043,0.048,1.571,1.667,1.763,0,0,3.754,0,0,0,,28.6825,104.27,0,0,0,0,0,0,1,0,37,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1031,妮可,普攻,1031_SNA_2_A,第2段特殊普攻(形态A),普通攻击:狡兔连打,,三段B*20,1.804,0.164,3.608,3.936,4.264,1.388,0.064,2.092,2.22,2.348,0,0,3.754,0,0,0,,28.6825,104.06,0,0,0,0,0,0,1,0,37,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1031,妮可,普攻,1031_SNA_3,第3段特殊普攻,普通攻击:狡兔连打,,三段A,1.243,0.113,2.486,2.712,2.938,1.043,0.048,1.571,1.667,1.763,0,0,3.754,0,0,0,,28.6825,104.27,0,0,0,0,0,0,1,0,105,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1031,妮可,普攻,1031_SNA_3_A,第3段特殊普攻(形态A),普通攻击:狡兔连打,,三段B*20,3.29,0.3,6.59,7.19,7.79,1.388,0.064,2.092,2.22,2.348,0,0,3.754,0,0,0,,28.6825,104.06,0,0,0,0,0,0,1,0,105,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1031,妮可,特殊技,1031_E_A,特殊技形态A,特殊技:糖衣炮弹,,A,0.263,0.024,0.527,0.575,0.623,0.25,0.012,0.382,0.406,0.43,0,0,0,0,0,0,,7.2325,26.25,0,0,2624,1,1,4,1,0,37,1,TRUE,TRUE,0,0,,1031_E_B,,0,0,,,FALSE,1,FALSE,
1031,妮可,特殊技,1031_E_B,特殊技形态B,特殊技:糖衣炮弹,,B,0.263,0.024,0.527,0.575,0.623,0.25,0.012,0.382,0.406,0.43,0,0,0,0,0,0,,7.2325,26.25,0,0,2624,1,1,4,1,0,37,1,TRUE,TRUE,0,0,,,1031_E_A,0,0,,,TRUE,1,FALSE,
1031,妮可,强化特殊技,1031_E_EX_FC,强化特殊技(满),强化特殊技:夹心糖衣炮弹,,蓄力,2.15,0.196,4.306,4.698,5.09,1.798,0.082,2.7,2.864,3.028,60,60,0,0,0,0,,68.805,74.18,0,0,19735,1,2,4,1,0,179,4,TRUE,TRUE,0,179,,1031_E_EX_B,,0,0,,,FALSE,1,FALSE,
1031,妮可,强化特殊技,1031_E_EX_A_1,强化E形态A第1段,强化特殊技:夹心糖衣炮弹,,炮击A,1.075,0.098,2.153,2.349,2.545,0.899,0.041,1.35,1.432,1.514,60,40,0,0,0,0,,34.4025,37.09,0,0,9867,1,2,4,1,0,35,12,TRUE,TRUE,0,35,,1031_E_EX_A_2,,0,0,,,FALSE,1,FALSE,
1031,妮可,强化特殊技,1031_E_EX_A_2,强化E形态A第2段,强化特殊技:夹心糖衣炮弹,,炮击B,1.075,0.098,2.153,2.349,2.545,0.899,0.041,1.35,1.432,1.514,0,0,0,0,0,0,,34.4025,37.09,0,0,9867,1,2,4,1,0,35,1,TRUE,TRUE,0,35,,,1031_E_EX_A_1,0,0,,,TRUE,1,FALSE,
1031,妮可,强化特殊技,1031_E_EX_B,强化E形态B,强化特殊技:夹心糖衣炮弹,,能量场,3.87,0.352,7.742,8.446,9.15,3.236,0.148,4.864,5.16,5.456,0,0,0,0,0,0,,137.61,148.35,0,0,39471,1,2,4,1,0,0,1,TRUE,TRUE,0,0,,,1031_E_EX_A_1|1031_E_EX_FC,1,15,,,FALSE,1,FALSE,-1
1031,妮可,冲刺攻击,1031_RA_F,前冲刺攻击,冲刺攻击:惊喜开箱,,前闪攻击A,0.412,0.038,0.83,0.906,0.982,0.29,0.014,0.444,0.472,0.5,0,0,1.02,0,0,0,,7.81,28.91,0,0,0,2,3,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1031,妮可,冲刺攻击,1031_SRA,特殊冲刺攻击,冲刺攻击:惊喜开箱,,前闪攻击B*13,1.173,0.107,2.35,2.564,2.778,0.451,0.021,0.682,0.724,0.766,0,0,1.02,0,0,0,,7.81,45.09,0,0,0,2,3,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1031,妮可,冲刺攻击,1031_RA_B,后冲刺攻击,冲刺攻击:惊喜开箱,,后闪攻击,0.6,0.055,1.205,1.315,1.425,0.6,0.028,0.908,0.964,1.02,0,0,2.159,0,0,0,,8.25,29.99,0,0,0,2,3,0,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1031,妮可,闪避反击,1031_CA_1,闪避反击第1段,闪避反击:牵制炮击,,A,0.912,0.083,1.825,1.991,2.157,0.817,0.038,1.235,1.311,1.387,0,0,2.279,0,0,0,,8.7175,106.65,0,0,3164,2,4,4,1,0,0,1,TRUE,TRUE,0,0,,1031_CA_2,,0,0,,,FALSE,1,FALSE,
1031,妮可,闪避反击,1031_CA_2,闪避反击第2段,闪避反击:牵制炮击,,B,0.912,0.083,1.825,1.991,2.157,0.817,0.038,1.235,1.311,1.387,0,0,1.919,0,0,0,,8.7175,106.65,0,0,3164,2,4,4,1,0,0,1,TRUE,TRUE,0,0,,,1031_CA_1,0,0,,,TRUE,1,FALSE,
1031,妮可,连携技,1031_QTE_A_1,连携技形态A第1段,连携技:高价以太爆弹,,炮击A,1.048,0.096,2.104,2.296,2.488,0.25,0.012,0.382,0.406,0.43,0,0,0,0,0,0,,43.45,25,0,0,6489,3,5,4,1,0,0,1,TRUE,TRUE,0,0,,1031_QTE_A_2,,0,0,,,FALSE,1,TRUE,
1031,妮可,连携技,1031_QTE_A_2,连携技形态A第2段,连携技:高价以太爆弹,,炮击B,1.048,0.096,2.104,2.296,2.488,0.25,0.012,0.382,0.406,0.43,0,0,0,0,0,0,,43.45,25,0,0,6489,3,5,4,1,0,0,1,TRUE,TRUE,0,0,,1031_QTE_B,1031_QTE_A_1,0,0,,,TRUE,1,TRUE,
1031,妮可,连携技,1031_QTE_B,连携技_B,连携技:高价以太爆弹,,能量场,2.83,0.258,5.668,6.184,6.7,0.675,0.031,1.016,1.078,1.14,0,0,0,0,0,0,,130.35,75,0,0,19469,3,5,4,1,0,0,1,TRUE,TRUE,0,0,,,1031_QTE_A_2,1,15,,,FALSE,1,TRUE,-1
1031,妮可,终结技,1031_Q_1,终结技第一段,终结技:特制以太榴弹,,炮击,6.468,0.588,12.936,14.112,15.288,0.36,0.017,0.547,0.581,0.615,0,0,0,0,0,0,,0,236,0,0,3599,3,6,4,1,0,0,1,TRUE,TRUE,0,0,,1031_Q_2,,0,0,,,TRUE,1,TRUE,
1031,妮可,终结技,1031_Q_2,终结技第二段,终结技:特制以太榴弹,,能量场,8.732,0.794,17.466,19.054,20.642,0.486,0.023,0.739,0.785,0.831,0,0,0,0,0,0,,0,354,0,0,5399,3,6,4,1,0,0,1,TRUE,TRUE,0,0,,,1031_Q_1,1,15,,,FALSE,1,TRUE,-1
1031,妮可,受击支援,1031_BH_Aid_1,受击支援第1段,快速支援:救急炮击,,A,0.317,0.029,0.636,0.694,0.752,0.317,0.015,0.482,0.512,0.542,0,0,1.14,0,0,0,,8.7175,15.83,0,0,3164,5,7,4,1,0,0,1,TRUE,TRUE,0,0,,1031_BH_Aid_2,,0,0,,,FALSE,1,FALSE,
1031,妮可,受击支援,1031_BH_Aid_2,受击支援第2段,快速支援:救急炮击,,B,0.317,0.029,0.636,0.694,0.752,0.317,0.015,0.482,0.512,0.542,0,0,1.14,0,0,0,,8.7175,15.83,0,0,3164,5,7,4,1,0,0,1,TRUE,TRUE,0,0,,,1031_BH_Aid_1,0,0,,,TRUE,1,FALSE,
1031,妮可,招架/回避支援,1031_Light_parry_Aid,轻招架,招架支援:狡兔出手!,,轻招架,0,0,0,0,0,2.467,0.113,3.71,3.936,4.162,0,0,0,0,0,0,,0,366.64,0,0,0,5,8,0,1,0,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1031,妮可,招架/回避支援,1031_Heavy_parry_Aid,重招架,招架支援:狡兔出手!,,重招架,0,0,0,0,0,3.117,0.142,4.679,4.963,5.247,0,0,0,0,0,0,,0,416.64,0,0,0,5,8,0,1,0,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1031,妮可,招架/回避支援,1031_Chain_parry_Aid,连续招架,招架支援:狡兔出手!,,连续招架,0,0,0,0,0,1.517,0.069,2.276,2.414,2.552,0,0,0,0,0,0,,0,116.64,0,0,0,5,8,0,1,0,10,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1031,妮可,突击支援,1031_Assault_Aid,突击支援,支援突击:趁虚而入,,,3.771,0.343,7.544,8.23,8.916,3.303,0.151,4.964,5.266,5.568,0,0,0,0,0,0,,116.5725,189.97,0,0,35367,5,9,4,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1381,零号·安比,普攻,1381_NA_1,第1段普攻,普通攻击:电击穿,,一段,0.327,0.03,0.657,0.717,0.777,0.279,0.013,0.422,0.448,0.474,0,0,0.912,0,0,0,,6.985,25.33,0,0,2532,0,0,3,1,0,28,2,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1381,零号·安比,普攻,1381_NA_2,第2段普攻,普通攻击:电击穿,,二段,0.622,0.057,1.249,1.363,1.477,0.545,0.025,0.82,0.87,0.92,0,0,1.784,0,0,0,,13.64,49.55,0,0,4954,0,0,3,1,0,34,2,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1381,零号·安比,普攻,1381_NA_3,第3段普攻,普通攻击:电击穿,,三段,0.801,0.073,1.604,1.75,1.896,0.719,0.033,1.082,1.148,1.214,0,0,2.35,0,0,0,,17.9575,65.28,0,0,6527,0,0,3,1,0,37,8,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1381,零号·安比,普攻,1381_NA_3_A,第3段普攻(形态A),普通攻击:电击穿,,三段终结,在连续普攻中,这一段会被吞掉,所以一般不会触发和使用。,0.399,0.037,0.806,0.88,0.954,0.621,0.029,0.94,0.998,1.056,0,0,2.031,0,0,0,,15.5375,56.41,0,0,5640,0,0,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1381,零号·安比,普攻,1381_NA_4,第4段普攻,普通攻击:电击穿,,四段(x1),1.545,0.141,3.096,3.378,3.66,1.377,0.063,2.07,2.196,2.322,0,0,4.506,0,0,0,0,34.43,125.17,0,0,12516,0,0,3,1,0,11,1,TRUE,TRUE,0,0,,1381_NA_5|1381_NA_5,,0,0,,status.1381:on_field==False;status.1381:lasting_node_tag==1381_NA_4|status.1381:repeat_times==5,FALSE,5,FALSE,
1381,零号·安比,普攻,1381_NA_5,第5段普攻,普通攻击:电击穿,,五段,1.485,0.135,2.97,3.24,3.51,1.309,0.06,1.969,2.089,2.209,0,0,4.283,0,0,0,,32.725,118.96,0,0,11895,0,0,3,1,0,74,8,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1381,零号·安比,特殊技,1381_CoAttack,协同攻击,特殊技:苍光,,[白雷],1.672,0.152,3.344,3.648,3.952,0,0,0,0,0,0,0,0,0,0,0,,0,0,0,0,0,1,1,3,1,0,0,1,FALSE,TRUE,0,0,"{'aftershock_attack': 1, 'additional_damage': 1}",1381_E_Aid|1381_CoAttack,1831_E_A|1831_CoAttack|1831_E_EX,0,0,,attribute.1381:special_state→雷殛==True;attribute.1381:cinema>=1|attribute.1381:special_state→1画状态==True,FALSE,1,FALSE,
1381,零号·安比,特殊技,1381_E_Aid,3连E后的追加攻击,特殊技:雷殛,,[雷殛],1.881,0.171,3.762,4.104,4.446,0,0,0,0,0,0,0,0,0,0,0,,0,0,0,0,0,1,1,3,1,0,0,1,FALSE,TRUE,0,0,"{'aftershock_attack': 1, 'additional_damage': 1}",1381_Cinema_6,1831_CoAttack,0,0,,attribute.1381:cinema==6|attribute.1381:special_state→6画状态==True,FALSE,1,FALSE,
1381,零号·安比,特殊技,1381_E_A,特殊技形态A,特殊技:苍光,,,0.43,0.04,0.87,0.95,1.03,0.331,0.016,0.507,0.539,0.571,0,0,0,0,0,0,,8.2775,30.04,0,0,3003,1,1,3,1,0,27,1,TRUE,TRUE,0,0,,1381_CoAttack,,0,0,,attribute.1381:special_state→白雷==True,FALSE,1,FALSE,
1381,零号·安比,特殊技,1381_E_B,特殊技形态B,特殊技:星雷,,,0.596,0.055,1.201,1.311,1.421,0.596,0.028,0.904,0.96,1.016,0,0,0,0,0,0,,14.905,54.17,0,0,5416,1,1,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1381,零号·安比,强化特殊技,1381_E_EX,强化特殊技,强化特殊技:极雷断空,,,3.796,0.346,7.602,8.294,8.986,4.586,0.209,6.885,7.303,7.721,60,60,0,0,0,0,,167.5575,128.37,0,0,46496,1,2,3,1,0,80,1,TRUE,TRUE,0,0,,1381_CoAttack,,0,0,,attribute.1381:cinema>=1|attribute.1381:special_state→1画状态==True,TRUE,1,FALSE,
1381,零号·安比,冲刺攻击,1381_RA,冲刺攻击,冲刺攻击:奔流,,,0.825,0.075,1.65,1.8,1.95,0.413,0.019,0.622,0.66,0.698,0,0,1.35,0,0,0,,10.3125,37.49,0,0,3748,2,3,3,1,0,49,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1381,零号·安比,闪避反击,1381_CA,闪避反击,闪避反击:地闪回击,,,2.506,0.228,5.014,5.47,5.926,2.182,0.1,3.282,3.482,3.682,0,0,3.539,0,0,0,,27.06,248.31,0,0,9830,2,4,3,1,0,72,6,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1381,零号·安比,连携技,1381_QTE,连携技,连携技:疾跃落雷,,,5.638,0.513,11.281,12.307,13.333,1.981,0.091,2.982,3.164,3.346,0,0,0,0,0,0,,232.4025,180.01,0,0,37950,3,5,3,1,0,82,10,TRUE,TRUE,0,82,,,,0,0,,,TRUE,1,TRUE,
1381,零号·安比,终结技,1381_Q,终结技,终结技:斩空掠电,,,17.349,1.578,34.707,37.863,41.019,2.769,0.126,4.155,4.407,4.659,0,0,0,0,0,0,,0,751.67,0,0,25166,3,6,3,1,0,147,13,TRUE,TRUE,0,147,,,,0,0,,,TRUE,1,TRUE,
1381,零号·安比,受击支援,1381_BH_Aid,受击支援,快速支援:云闪,,,1.082,0.099,2.171,2.369,2.567,1.082,0.05,1.632,1.732,1.832,0,0,1.77,0,0,0,,27.06,49.16,0,0,9830,5,7,3,1,0,62,6,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1381,零号·安比,招架/回避支援,1381_Light_parry_Aid,轻招架,招架支援:逆极反袭,,轻招架,0,0,0,0,0,2.713,0.124,4.077,4.325,4.573,0,0,0,0,0,0,,0,366.64,0,0,0,5,8,0,1,0,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1381,零号·安比,招架/回避支援,1381_Heavy_parry_Aid,重招架,招架支援:逆极反袭,,重招架,0,0,0,0,0,3.428,0.156,5.144,5.456,5.768,0,0,0,0,0,0,,0,416.64,0,0,0,5,8,0,1,0,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1381,零号·安比,招架/回避支援,1381_Chain_parry_Aid,连续招架,招架支援:逆极反袭,,连续招架,0,0,0,0,0,1.668,0.076,2.504,2.656,2.808,0,0,0,0,0,0,,0,116.64,0,0,0,5,8,0,1,0,10,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1381,零号·安比,突击支援,1381_Assault_Aid,突击支援,支援突击:直击先导,,,4.071,0.371,8.152,8.894,9.636,3.562,0.162,5.344,5.668,5.992,0,0,0,0,0,0,,114.51,184.97,0,0,34692,5,9,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1381,零号·安比,招架/回避支援,1381_Cinema_6,影画6,影画6,,,0.345,0.032,0.697,0.761,0.825,0.173,0.008,0.261,0.277,0.293,0,0,0.987,0,0,0,,7.5625,0,0,0,0,5,8,3,1,0,0,1,FALSE,FALSE,0,0,"{'aftershock_attack': 1, 'additional_damage': 1}",,1381_E_Aid,0,0,,,TRUE,1,FALSE,
1361,扳机,普攻,1361_NA_1,第1段普攻,普通攻击:冷膛射击,,一段,0.345,0.032,0.697,0.761,0.825,0.173,0.008,0.261,0.277,0.293,0,0,0.987,0,0,0,,7.5625,19.59,0,0,0,0,0,0,1,0,26,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1361,扳机,普攻,1361_NA_2,第2段普攻,普通攻击:冷膛射击,,二段,0.569,0.052,1.141,1.245,1.349,0.285,0.013,0.428,0.454,0.48,0,0,2.002,0,0,0,,15.3175,58.25,0,0,0,0,0,0,1,0,44,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1361,扳机,普攻,1361_NA_3,第3段普攻,普通攻击:冷膛射击,,三段,1.109,0.101,2.22,2.422,2.624,0.608,0.028,0.916,0.972,1.028,0,0,3.281,0,0,0,,25.08,96.31,0,0,0,0,0,0,1,0,55,4,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1361,扳机,普攻,1361_NA_4,第4段普攻,普通攻击:冷膛射击,,四段,2.43,0.221,4.861,5.303,5.745,1.769,0.081,2.66,2.822,2.984,0,0,6.976,0,0,0,,53.295,193.77,0,0,17441,0,0,3,1,0,91,6,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1361,扳机,普攻,1361_SNA_1,第1段特殊普攻,普通攻击:无音狙杀,,射击,0.476,0.044,0.96,1.048,1.136,0.238,0.011,0.359,0.381,0.403,0,0,1.362,0,0,0,,10.4425,27.01,0,0,2700,0,0,3,1,0,32,3,TRUE,TRUE,0,0,,,1361_SNA_0,0,0,,,FALSE,1,FALSE,
1361,扳机,普攻,1361_SNA_2,第2段特殊普攻,普通攻击:无音狙杀,,反击,2.339,0.213,4.682,5.108,5.534,1.488,0.068,2.236,2.372,2.508,0,0,6.957,0,0,0,,53.1575,193.23,0,0,19322,0,0,3,1,0,93,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1361,扳机,普攻,1361_SNA_3,第3段特殊普攻,普通攻击:无音狙杀,,终结,1.312,0.12,2.632,2.872,3.112,0.656,0.03,0.986,1.046,1.106,0,0,4.489,0,0,0,,34.2925,74.55,0,0,11926,0,0,3,1,0,87,4,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1361,扳机,普攻,1361_CoAttack_A,协同攻击A,普通攻击:协奏狙杀,,单段,未 * 2,0.479,0.044,0.963,1.051,1.139,0.359,0.017,0.546,0.58,0.614,0,0,0,0,0,0,,0,0,0,0,1086,0,0,3,1,0,20,3,FALSE,FALSE,0,0,{'aftershock_attack': 1},1361_CoAttack_A,,0,0,,status.1361:lasting_node_tag==1361_CoAttack_A|status.1361:repeat_times<2,FALSE,1,FALSE,
1361,扳机,特殊技,1361_E,特殊技,特殊技:幽闪,,,0.715,0.065,1.43,1.56,1.69,0.715,0.033,1.078,1.144,1.21,0,0,0,0,0,0,,17.875,65,0,0,6500,1,1,3,1,0,82,4,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1361,扳机,强化特殊技,1361_E_EX,强化特殊技,强化特殊技:幽闪花葬,,,6.344,0.577,12.691,13.845,14.999,4.327,0.197,6.494,6.888,7.282,60,60,0,0,0,0,,210.8425,233.34,0,0,35666,1,2,3,1,0,143,7,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1361,扳机,冲刺攻击,1361_RA,冲刺攻击,冲刺攻击:怨魂返,,,0.514,0.047,1.031,1.125,1.219,0.257,0.012,0.389,0.413,0.437,0,0,0.841,0,0,0,,6.435,23.36,0,0,0,2,3,0,1,0,29,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1361,扳机,闪避反击,1361_CA,闪避反击,闪避反击:极魂罚,,,2.197,0.2,4.397,4.797,5.197,1.944,0.089,2.923,3.101,3.279,0,0,2.76,0,0,0,,21.0925,226.67,0,0,7666,2,4,3,1,0,65,3,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1361,扳机,连携技,1361_QTE,连携技,连携技:冥河之引,,,5.747,0.523,11.5,12.546,13.592,1.358,0.062,2.04,2.164,2.288,0,0,0,0,0,0,,216.81,123.37,0,0,32286,3,5,3,1,0,51,5,TRUE,TRUE,0,51,,,,0,0,,,TRUE,1,TRUE,
1361,扳机,终结技,1361_Q,终结技,终结技:冥府挽歌,,,14.805,1.346,29.611,32.303,34.995,9.22,0.42,13.84,14.68,15.52,0,0,0,0,0,0,,0,576.67,0,0,7666,3,6,3,1,0,37,5,TRUE,TRUE,0,37,,,,0,0,,,TRUE,1,TRUE,
1361,扳机,受击支援,1361_BH_Aid,受击支援,快速支援:冷枪援护,,,0.844,0.077,1.691,1.845,1.999,0.844,0.039,1.273,1.351,1.429,0,0,2.76,0,0,0,,21.0925,38.34,0,0,7666,5,7,3,1,0,50,3,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1361,扳机,招架/回避支援,1361_Light_parry_Aid,轻招架,招架支援:死线偏移,,轻招架,0,0,0,0,0,2.713,0.124,4.077,4.325,4.573,0,0,0,0,0,0,,0,366.64,0,0,0,5,8,0,1,0,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1361,扳机,招架/回避支援,1361_Heavy_parry_Aid,重招架,招架支援:死线偏移,,重招架,0,0,0,0,0,3.428,0.156,5.144,5.456,5.768,0,0,0,0,0,0,,0,416.64,0,0,0,5,8,0,1,0,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1361,扳机,招架/回避支援,1361_Chain_parry_Aid,连续招架,招架支援:死线偏移,,连续招架,0,0,0,0,0,1.668,0.076,2.504,2.656,2.808,0,0,0,0,0,0,,0,116.64,0,0,0,5,8,0,1,0,10,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1361,扳机,招架/回避支援,1361_Assault_Aid,突击支援,支援突击:殛雷穿心,,,4.329,0.394,8.663,9.451,10.239,3.274,0.149,4.913,5.211,5.509,0,0,0,0,0,0,,121.4125,201.7,0,0,36951,5,8,3,1,0,0,1,TRUE,TRUE,0,0,,,,0,0,,,TRUE,1,FALSE,
1361,扳机,普攻,1361_CoAttack_1,第一段强化协同攻击,普通攻击:协奏狙杀·冥狱,,连射(单段,未 * 3),0.226,0.021,0.457,0.499,0.541,0.17,0.008,0.258,0.274,0.29,0,0,0,0,0,0,,0,0,0,0,2049,0,0,3,1,0,20,3,FALSE,FALSE,0,0,{'aftershock_attack': 1},1361_CoAttack_1|1361_CoAttack_2,1361_CoAttack_1,0,0,,status.1361:lasting_node_tag==1361_CoAttack_1|status.1361:repeat_times<3;status.1361:lasting_node_tag==1361_CoAttack_1|status.1361:repeat_times>=3,FALSE,1,FALSE,
1361,扳机,普攻,1361_CoAttack_2,第二段强化协同攻击,普通攻击:协奏狙杀·冥狱,,终结,0.452,0.042,0.914,0.998,1.082,0.34,0.016,0.516,0.548,0.58,0,0,0,0,0,0,,0,0,0,0,4098,0,0,3,1,0,75,3,FALSE,FALSE,0,0,{'aftershock_attack': 1},,1361_CoAttack_1,0,0,,,FALSE,1,FALSE,
1361,扳机,普攻,1361_Cinema_4,影画4,匿息隐踪,,,2,0,2,2,2,1.2,0,1.2,1.2,1.2,0,0,0,0,0,0,,0,0,0,0,0,0,0,0,1,0,0,1,FALSE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1361,扳机,普攻,1361_SNA_0,第1段特殊普攻前摇,普通攻击:无音狙杀,,前摇,0.341,0.031,0.682,0.744,0.806,0.424,0.02,0.644,0.684,0.724,0,0,1.387,0,0,0,0,10.615,0,0,0,0,0,0,0,0,0,26,0,TRUE,FALSE,0,0,,1361_SNA_1,,0,0,,,FALSE,1,FALSE,
1331,薇薇安,普攻,1331_NA_1,第1段普攻,普通攻击:翎羽拂击,,一段,0.341,0.031,0.682,0.744,0.806,0.424,0.02,0.644,0.684,0.724,0,0,1.387,0,0,0,,10.615,38.51,0,0,0,0,0,0,1,0,32,1,TRUE,FALSE,0,0,,,,0,0,[22],,FALSE,1,FALSE,
1331,薇薇安,普攻,1331_NA_2,第2段普攻,普通攻击:翎羽拂击,,二段,0.242,0.022,0.484,0.528,0.572,0.281,0.013,0.424,0.45,0.476,0,0,0.92,0,0,0,,7.04,25.55,0,0,0,0,0,0,1,0,16,1,TRUE,FALSE,0,0,,,,0,0,[5],,FALSE,1,FALSE,
1331,薇薇安,普攻,1331_NA_3,第3段普攻,普通攻击:翎羽拂击,,三段,0.769,0.07,1.539,1.679,1.819,0.818,0.038,1.236,1.312,1.388,0,0,2.675,0,0,0,,20.46,74.31,0,0,0,0,0,0,1,0,42,3,TRUE,FALSE,0,0,,,,0,0,"[11, 23, 38]",,FALSE,1,FALSE,
1331,薇薇安,普攻,1331_NA_4,第4段普攻,普通攻击:翎羽拂击,,四段,1.284,0.117,2.571,2.805,3.039,1.272,0.058,1.91,2.026,2.142,0,0,4.16,0,0,0,,31.79,115.55,0,0,8661,0,0,4,1,0,67,2,TRUE,TRUE,0,0,,,,0,0,"[26, 33]",,FALSE,1,FALSE,
1331,薇薇安,普攻,1331_SNA_1,特殊普攻1,普通攻击:淑女礼仪 · 舞步,,,0.478,0.044,0.962,1.05,1.138,0.372,0.017,0.559,0.593,0.627,0,0,1.215,0,0,0,,9.295,33.75,0,0,3375,0,0,4,1,0,71,7,TRUE,TRUE,0,0,{'flight_feather': 1},1331_SNA_2,,0,0,"[11, 16, 21, 26, 31, 37, 41]",,FALSE,1,FALSE,
1331,薇薇安,普攻,1331_SNA_2,特殊普攻2,普通攻击:裙裾浮游 · 悬落,,,0.802,0.073,1.605,1.751,1.897,0.624,0.029,0.943,1.001,1.059,0,0,2.04,0,0,0,,15.5925,56.66,0,0,5665,0,0,4,1,0,130,14,FALSE,TRUE,0,0,{'only_buffed': 'Buff-角色-薇薇安-4画-悬落与落羽生花必暴'},,1331_SNA_1,0,0,"[8, 15, 21, 27, 33, 63, 66, 69, 72, 79, 90, 99, 108, 117]",,FALSE,1,FALSE,
1331,薇薇安,普攻,1331_CoAttack_A,协同攻击A,普通攻击:落羽生花,,,2.2,0.2,4.4,4.8,5.2,0,0,0,0,0,0,0,0,0,0,0,,0,0,0,0,12600,0,0,4,1,0,0,1,FALSE,TRUE,0,0,"{'only_buffed': 'Buff-角色-薇薇安-4画-悬落与落羽生花必暴', 'additional_damage': 1}",,,0,0,,,FALSE,1,FALSE,
1331,薇薇安,特殊技,1331_E,特殊技,特殊技:银羽咏叹,,,0.569,0.052,1.141,1.245,1.349,0.569,0.026,0.855,0.907,0.959,0,0,0,0,0,0,,14.2175,51.69,0,0,4134,1,1,4,1,0,68,2,TRUE,TRUE,0,0,,,,0,0,"[13, 35]",,FALSE,1,FALSE,
1331,薇薇安,强化特殊技,1331_E_EX,强化特殊技,强化特殊技:堇花悼亡,,,5.926,0.539,11.855,12.933,14.011,4.848,0.221,7.279,7.721,8.163,60,60,0,0,0,0,,175.12,146.71,0,0,35256,1,2,4,1,0,127,9,TRUE,TRUE,0,0,"{'flight_feather': 3, 'c6_feather': 1}",1331_SNA_2,,0,0,"[12, 19, 25, 31, 46, 50, 54, 64, 72]",status.1331:on_field==False|attribute.1331:special_state→裙裾浮游==True,FALSE,1,FALSE,
1331,薇薇安,冲刺攻击,1331_RA,冲刺攻击,冲刺攻击:银刺舞曲,,,0.496,0.046,1.002,1.094,1.186,0.248,0.012,0.38,0.404,0.428,0,0,0.811,0,0,0,,6.215,22.51,0,0,0,2,3,0,1,0,27,2,TRUE,TRUE,0,0,,,,0,0,"[10, 17]",,FALSE,1,FALSE,
1331,薇薇安,闪避反击,1331_CA,闪避反击,闪避反击:羽刃反振,,,2.364,0.215,4.729,5.159,5.589,2.073,0.095,3.118,3.308,3.498,0,0,3.182,0,0,0,,24.31,238.37,0,0,7069,2,4,4,1,0,62,2,TRUE,TRUE,0,0,,,,0,0,"[5, 37]",,FALSE,1,FALSE,
1331,薇薇安,连携技,1331_QTE,连携技,连携技:星羽和声,,,6.589,0.599,13.178,14.376,15.574,2.2,0.1,3.3,3.5,3.7,0,0,0,0,0,0,,237.875,199.97,0,0,31957,3,5,4,1,0,107,12,TRUE,TRUE,0,107,{'flight_feather': 2},1331_SNA_2,,0,0,"[7, 13, 18, 23, 28, 33, 62, 88, 92, 94, 100, 104]",status.1331:on_field==False|attribute.1331:special_state→裙裾浮游==True,FALSE,1,TRUE,
1331,薇薇安,终结技,1331_Q,终结技,终结技:飞鸟鸣颂,,,16.868,1.534,33.742,36.81,39.878,2.494,0.114,3.748,3.976,4.204,0,0,0,0,0,0,,0,726.71,0,0,80856,3,6,4,1,0,123,16,TRUE,TRUE,0,123,{'flight_feather': 5},1331_SNA_2,,0,0,"[1, 3, 6, 11, 16, 21, 26, 31, 36, 41, 46, 94, 109, 115, 121, 122]",status.1331:on_field==False|attribute.1331:special_state→裙裾浮游==True,FALSE,1,TRUE,
1331,薇薇安,快速支援,1331_BH_Aid,受击支援,快速支援:凛羽之护,,,0.973,0.089,1.952,2.13,2.308,0.487,0.023,0.74,0.786,0.832,0,0,1.591,0,0,0,,12.155,44.19,0,0,3534,5,7,4,1,0,73,2,TRUE,TRUE,0,0,,,,0,0,"[7, 39]",,FALSE,1,FALSE,
1331,薇薇安,招架/回避支援,1331_Light_parry_Aid,轻招架,招架支援:银伞列阵,,轻招架,0,0,0,0,0,2.713,0.124,4.077,4.325,4.573,0,0,0,0,0,0,,0,366.64,0,0,0,5,8,0,1,0,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1331,薇薇安,招架/回避支援,1331_Heavy_parry_Aid,重招架,招架支援:银伞列阵,,重招架,0,0,0,0,0,3.428,0.156,5.144,5.456,5.768,0,0,0,0,0,0,,0,416.64,0,0,0,5,8,0,1,0,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1331,薇薇安,招架/回避支援,1331_Chain_parry_Aid,连续招架,招架支援:银伞列阵,,连续招架,0,0,0,0,0,1.668,0.076,2.504,2.656,2.808,0,0,0,0,0,0,,0,116.64,0,0,0,5,8,0,1,0,10,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1331,薇薇安,突击支援,1331_Assault_Aid,突击支援,支援突击:裁决羽刃,,,3.739,0.34,7.479,8.159,8.839,3.253,0.148,4.881,5.177,5.473,0,0,0,0,0,0,,105.6,163.37,0,0,23656,5,9,4,1,0,120,7,TRUE,TRUE,0,0,{'flight_feather': 2},1331_SNA_2,,0,0,"[16, 20, 24, 28, 47, 57, 65]",status.1331:on_field==False|attribute.1331:special_state→裙裾浮游==True,FALSE,1,FALSE,
1331,薇薇安,招架/回避支援,1331_Core_Passive,核心被动,核心被动:命运悲歌,,[薇薇安的预言],0.55,0,0.55,0.55,0.55,0,0,0,0,0,0,0,0,0,0,0,,0,0,0,0,0,5,8,4,1,0,0,1,FALSE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1331,薇薇安,普攻,1331_SNA_0,强化E接开伞衔接,衔接动作,,强化E中途长按闪避可退回开伞状态,0.431,0.04,0.871,0.951,1.031,0.216,0.01,0.326,0.346,0.366,0,0,0.705,0,0,0,0,5.39,0,0,0,0,0,0,0,0,0,50,0,TRUE,FALSE,0,0,,,,0,0,,,FALSE,1,FALSE,
1291,雨果,普攻,1291_NA_1,第1段普攻,普通攻击:暗渊四重奏,,一段,0.431,0.04,0.871,0.951,1.031,0.216,0.01,0.326,0.346,0.366,0,0,0.705,0,0,0,,5.39,19.59,0,0,0,0,0,0,1,0,28,2,TRUE,TRUE,0,0,,,,0,0,"[11, 19]",,FALSE,1,FALSE,
1291,雨果,普攻,1291_NA_2,第2段普攻,普通攻击:暗渊四重奏,,二段,0.741,0.068,1.489,1.625,1.761,0.549,0.025,0.824,0.874,0.924,0,0,1.796,0,0,0,,13.7225,49.88,0,0,0,0,0,0,1,0,32,2,TRUE,TRUE,0,0,,,,0,0,"[8, 20]",,FALSE,1,FALSE,
1291,雨果,普攻,1291_NA_3,第3段普攻,普通攻击:暗渊四重奏,,三段,1.696,0.155,3.401,3.711,4.021,1.281,0.059,1.93,2.048,2.166,0,0,4.191,0,0,0,,32.0375,116.41,0,0,0,0,0,0,1,0,67,9,TRUE,TRUE,0,0,,,,0,0,"[5, 13, 19, 23, 29, 33, 39, 43, 57]",,FALSE,1,FALSE,
1291,雨果,普攻,1291_SNA_1,第1段特殊普攻,普通攻击:暗渊四重奏,,斩击,1.491,0.136,2.987,3.259,3.531,1.258,0.058,1.896,2.012,2.128,0,0,4.115,0,0,0,,31.46,114.31,0,0,9581,0,0,2,1,0,84,7,TRUE,TRUE,0,0,,,,0,0,"[12, 18, 24, 30, 36, 60, 65]",,FALSE,1,FALSE,
1291,雨果,普攻,1291_SNA_2_NFC,第2段特殊普攻(不满),普通攻击:暗渊四重奏,,射击,0.586,0.054,1.18,1.288,1.396,0.501,0.023,0.754,0.8,0.846,0,0,1.637,0,0,0,,12.5125,45.46,0,0,3617,0,0,2,1,0,110,5,TRUE,TRUE,0,0,,,,0,0,"[27, 38, 44, 68, 75]",,TRUE,1,FALSE,
1291,雨果,普攻,1291_SNA_2_FC,第2段特殊普攻(满),普通攻击:暗渊四重奏,,蓄力射击,1.18,0.108,2.368,2.584,2.8,0.957,0.044,1.441,1.529,1.617,0,0,3.132,0,0,0,,23.925,87,0,0,7770,0,0,2,1,0,130,10,TRUE,TRUE,0,0,,,,0,0,"[11, 14, 18, 23, 28, 35, 80, 85, 90, 94]",,TRUE,1,FALSE,
1291,雨果,特殊技,1291_E,特殊技,特殊技:魂狩·断罪,,,0.853,0.078,1.711,1.867,2.023,0.853,0.039,1.282,1.36,1.438,0,0,0,0,0,0,,21.34,77.52,0,0,7751,1,1,2,1,0,92,5,TRUE,TRUE,0,0,,,,0,0,"[30, 35, 40, 54, 63]",,FALSE,1,FALSE,
1291,雨果,强化特殊技,1291_E_EX_1,第1段强化特殊技,强化特殊技:魂狩·惩戒,,A,0.187,0.017,0.374,0.408,0.442,4.324,0.197,6.491,6.885,7.279,40,40,0,0,0,0,,7.755,9.51,0,0,2254,1,2,2,1,0,68,6,TRUE,TRUE,0,0,,1291_E_EX_2,,0,0,"[30, 34, 37, 41, 45, 49]",,FALSE,1,FALSE,
1291,雨果,强化特殊技,1291_E_EX_2,第2段强化特殊技,强化特殊技:魂狩·惩戒,,B,3.545,0.323,7.098,7.744,8.39,0.228,0.011,0.349,0.371,0.393,0,0,0,0,0,0,,147.0425,180.54,0,0,42843,1,2,2,1,0,45,1,TRUE,TRUE,0,0,,,1291_E_EX_1,0,0,[24],,FALSE,1,FALSE,
1291,雨果,冲刺攻击,1291_RA,冲刺攻击,冲刺攻击:诡影·破,,,0.935,0.085,1.87,2.04,2.21,0.468,0.022,0.71,0.754,0.798,0,0,1.53,0,0,0,,11.6875,42.49,0,0,0,2,3,0,1,0,50,2,TRUE,TRUE,0,0,,,,0,0,"[8, 32]",,FALSE,1,FALSE,
1291,雨果,闪避反击,1291_CA,闪避反击,闪避反击:诡影·斩,,,2.459,0.224,4.923,5.371,5.819,2.145,0.098,3.223,3.419,3.615,0,0,3.42,0,0,0,,26.125,245,0,0,9500,2,4,2,1,0,69,4,TRUE,TRUE,0,0,,,,0,0,"[17, 29, 38, 42]",,FALSE,1,FALSE,
1291,雨果,普攻,1291_SCA,特殊闪避反击,闪避反击:诡影·斩,,派生射击,1.621,0.148,3.249,3.545,3.841,1.247,0.057,1.874,1.988,2.102,0,0,4.08,0,0,0,,31.185,113.34,0,0,11333,2,0,2,1,0,84,7,TRUE,TRUE,0,0,,,,0,0,"[12, 18, 24, 30, 36, 60, 65]",,FALSE,1,FALSE,
1291,雨果,普攻,1291_SCA_FC,蓄力特殊闪避反击,闪避反击:诡影·斩,,派生蓄力射击,2.121,0.193,4.244,4.63,5.016,1.632,0.075,2.457,2.607,2.757,0,0,5.339,0,0,0,,40.81,148.31,0,0,14830,2,0,2,1,0,130,10,TRUE,TRUE,0,0,,,,0,0,"[11, 14, 18, 23, 28, 35, 80, 85, 90, 94]",,TRUE,1,FALSE,
1291,雨果,连携技,1291_QTE,连携技,连携技:命运戏法,,斩击,7.011,0.638,14.029,15.305,16.581,2.622,0.12,3.942,4.182,4.422,0,0,0,0,0,0,,248.435,238.34,0,0,43783,3,5,2,1,0,117,16,TRUE,TRUE,0,117,,,,0,0,"[2, 3, 6, 9, 18, 21, 24, 27, 30, 33, 36, 39, 49, 56, 88, 93]",,FALSE,1,TRUE,
1291,雨果,终结技,1291_Q,终结技,终结技:渎神者,,,15.271,1.389,30.55,33.328,36.106,2.642,0.121,3.973,4.215,4.457,0,0,0,0,0,0,,0,718.34,0,0,21833,3,6,2,1,0,131,15,TRUE,TRUE,0,131,,,,0,0,"[18, 19, 23, 28, 33, 38, 43, 48, 53, 58, 63, 68, 89, 94, 98]",,FALSE,1,TRUE,
1291,雨果,受击支援,1291_BH_Aid,受击支援,快速支援:葬歌,,,1.045,0.095,2.09,2.28,2.47,0.523,0.024,0.787,0.835,0.883,0,0,1.71,0,0,0,,13.0625,47.5,0,0,4750,6,7,2,1,0,58,4,TRUE,TRUE,0,58,,,,0,0,"[12, 18, 26, 31]",,TRUE,1,FALSE,
1291,雨果,招架/回避支援,1291_Light_parry_Aid,轻招架,招架支援:死期未至,,轻招架,0,0,0,0,0,2.713,0.124,4.077,4.325,4.573,0,0,0,0,0,0,,0,366.64,0,0,0,6,8,0,1,0,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1291,雨果,招架/回避支援,1291_Heavy_parry_Aid,重招架,招架支援:死期未至,,重招架,0,0,0,0,0,3.428,0.156,5.144,5.456,5.768,0,0,0,0,0,0,,0,416.64,0,0,0,6,8,0,1,0,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1291,雨果,招架/回避支援,1291_Chain_parry_Aid,连续招架,招架支援:死期未至,,连续招架,0,0,0,0,0,1.668,0.076,2.504,2.656,2.808,0,0,0,0,0,0,,0,116.64,0,0,0,6,8,0,1,0,10,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1291,雨果,突击支援,1291_Assault_Aid,突击支援,支援突击:王牌反转,,,2.917,0.266,5.843,6.375,6.907,2.491,0.114,3.745,3.973,4.201,0,0,0,0,0,0,,83.6,110.04,0,0,24576,6,9,2,1,0,66,4,TRUE,TRUE,0,66,,,,0,0,"[11, 18, 37, 44]",,FALSE,1,FALSE,
1291,雨果,普攻,1291_NA_1_ALT,第1段派生普攻,普通攻击:暗渊协奏曲,,斩击,1.434,0.131,2.875,3.137,3.399,1.103,0.051,1.664,1.766,1.868,0,0,3.609,0,0,0,,27.5825,100.24,0,0,10023,0,0,2,1,0,84,7,TRUE,TRUE,0,0,,,,0,0,"[12, 18, 24, 30, 36, 60, 65]",,FALSE,1,FALSE,
1291,雨果,普攻,1291_NA_A,特殊派生普攻,普通攻击:暗渊协奏曲,,射击,0.867,0.079,1.736,1.894,2.052,0.667,0.031,1.008,1.07,1.132,0,0,2.181,0,0,0,,16.665,60.57,0,0,6056,0,0,2,1,0,110,5,TRUE,TRUE,0,0,,,,0,0,"[27, 38, 44, 68, 75]",,TRUE,1,FALSE,
1291,雨果,普攻,1291_NA_A_FC,蓄力特殊派生普攻,普通攻击:暗渊协奏曲,,蓄力射击,1.706,0.156,3.422,3.734,4.046,1.312,0.06,1.972,2.092,2.212,0,0,4.293,0,0,0,,32.8075,119.25,0,0,11924,0,0,2,1,0,130,10,TRUE,TRUE,0,0,,,,0,0,"[11, 14, 18, 23, 28, 35, 80, 85, 90, 94]",,TRUE,1,FALSE,
1291,雨果,普攻,1291_BH_Aid_A,特殊受击支援,快速支援:葬歌,,派生射击,1.247,0.114,2.501,2.729,2.957,0.624,0.029,0.943,1.001,1.059,0,0,2.04,0,0,0,,15.5925,56.67,0,0,5666,6,0,2,1,0,84,7,TRUE,TRUE,0,0,,,,0,0,"[12, 18, 24, 30, 36, 60, 65]",,TRUE,1,FALSE,
1291,雨果,普攻,1291_BH_Aid_A_FC,蓄力特殊受击支援,快速支援:葬歌,,派生蓄力射击,1.632,0.149,3.271,3.569,3.867,0.816,0.038,1.234,1.31,1.386,0,0,2.67,0,0,0,,20.405,74.16,0,0,7415,6,0,2,1,0,95,3,TRUE,TRUE,0,0,,,,0,0,"[45, 54, 60]",,TRUE,1,FALSE,
1291,雨果,强化特殊技,1291_CorePassive_E_EX,核心被动-强化E标签,核心技:决算,,强化E触发,0.458,0.042,0.92,1.004,1.088,0.286,0.013,0.429,0.455,0.481,0,0,0,0,0,0,,0,0,0,0,0,1,2,2,1,0,1,0,TRUE,TRUE,0,0,{'totalized': 1},,,0,0,,,FALSE,1,FALSE,
1291,雨果,终结技,1291_CorePassive_Q,核心被动-终结技标签,核心技:决算,,大招触发,0.458,0.042,0.92,1.004,1.088,0.286,0.013,0.429,0.455,0.481,0,0,0,0,0,1,,7.15,25.97,0,0,2596,3,6,2,1,0,1,0,TRUE,TRUE,0,0,{'totalized': 1},,,0,0,,,FALSE,1,FALSE,
1371,仪玄,普攻,1371_NA_1,第1段普攻,普通攻击:霄云劲,,普通,0.458,0.042,0.92,1.004,1.088,0.286,0.013,0.429,0.455,0.481,0,0,0,0,0,1,,7.15,25.97,0,0,2596,0,0,6,1,4,33,3,TRUE,TRUE,0,0,,,,0,0,"[8, 15, 25]",,FALSE,1,FALSE,
1371,仪玄,普攻,1371_NA_2,第2段普攻,普通攻击:霄云劲,,二段,0.655,0.06,1.315,1.435,1.555,0.689,0.032,1.041,1.105,1.169,0,0,0,0,0,1,,17.2425,62.61,0,0,6260,0,0,6,1,4,40,4,TRUE,TRUE,0,0,,,,0,0,"[12, 20, 27, 32]",,FALSE,1,FALSE,
1371,仪玄,普攻,1371_NA_3,第3段普攻,普通攻击:霄云劲,,三段,0.871,0.08,1.751,1.911,2.071,0.858,0.039,1.287,1.365,1.443,0,0,0,0,0,2,,21.45,77.92,0,0,7791,0,0,6,1,4,51,5,TRUE,TRUE,0,0,,,,0,0,"[7, 13, 19, 25, 31]",,FALSE,1,FALSE,
1371,仪玄,普攻,1371_NA_4,第4段普攻,普通攻击:霄云劲,,四段,1.048,0.096,2.104,2.296,2.488,1.118,0.051,1.679,1.781,1.883,0,0,0,0,0,2,,27.94,101.6,0,0,10159,0,0,6,1,4,49,5,TRUE,TRUE,0,0,,,,0,0,"[5, 16, 21, 24, 28]",,FALSE,1,FALSE,
1371,仪玄,普攻,1371_SNA,特殊普攻,普通攻击:墨影凝云,,无玄墨值时长按闪避,是玄墨极阵的弱化版,2.347,0.214,4.701,5.129,5.557,2.934,0.134,4.408,4.676,4.944,0,0,0,0,0,5,,73.37,266.71,0,0,26670,0,0,6,1,4,159,24,TRUE,TRUE,0,0,,,,0,0,"[28, 33, 37, 42, 51, 54, 56, 59, 65, 71, 77, 83, 89, 97, 103, 109, 114, 120, 126, 132, 138, 144, 150, 156]",,FALSE,1,FALSE,
1371,仪玄,普攻,1371_NA_5,第5段普攻,普通攻击:霄云劲,,五段,1.137,0.104,2.281,2.489,2.697,1.057,0.049,1.596,1.694,1.792,0,0,0,0,0,2,,26.4275,96.07,0,0,9606,0,0,6,1,4,35,2,TRUE,TRUE,0,0,,,,0,0,"[18, 27]",,TRUE,1,FALSE,
1371,仪玄,普攻,1371_SNA_B_2,普攻重击,普通攻击:青溟震击,,自动衔接在玄墨极阵后,1.106,0.101,2.217,2.419,2.621,1.063,0.049,1.602,1.7,1.798,0,0,0,0,0,0,,26.5925,96.64,0,0,9663,0,0,6,1,4,57,12,TRUE,TRUE,0,0,,,1371_SNA_B_1,0,0,"[1, 7, 14, 16, 20, 26, 31, 33, 38, 43, 45, 50]",,TRUE,1,FALSE,
1371,仪玄,特殊技,1371_E,特殊技,特殊技:烬影诀,,,0.521,0.048,1.049,1.145,1.241,0.651,0.03,0.981,1.041,1.101,0,0,0,0,0,0,,16.28,59.17,0,0,5916,1,1,6,1,4,77,2,TRUE,TRUE,0,0,,,,0,0,"[50, 57]",,TRUE,1,FALSE,
1371,仪玄,强化特殊技,1371_E_EX_A_1_NFC,强化特殊技,强化特殊技:墨痕化形,,点按强化E第一段,3.003,0.273,6.006,6.552,7.098,1.729,0.079,2.598,2.756,2.914,0,0,0,40,40,0,,54.4225,108.31,0,0,19787,1,2,6,1,4,66,3,TRUE,TRUE,0,0,,,,0,0,"[26, 32, 36]",,TRUE,1,FALSE,
1371,仪玄,强化特殊技,1371_E_EX_A_1_FC,强化特殊技,强化特殊技:墨痕化形,,满蓄力强化E第一段,3.003,0.273,6.006,6.552,7.098,1.729,0.079,2.598,2.756,2.914,0,0,0,40,40,0,,54.4225,108.31,0,0,19787,1,2,6,1,4,95,5,TRUE,TRUE,0,0,,1371_E_EX_A_1_Add,,0,0,"[61, 67, 72, 81, 87]",,TRUE,1,FALSE,
1371,仪玄,强化特殊技,1371_E_EX_A_1_FCT,强化特殊技,强化特殊技:墨痕化形,,满蓄力强化E第一段(测试),3.003,0.273,6.006,6.552,7.098,1.729,0.079,2.598,2.756,2.914,0,0,0,40,40,0,,54.4225,108.31,0,0,19787,1,2,6,1,4,45,5,TRUE,TRUE,0,0,,1371_E_EX_A_1_Add,,0,0,,,TRUE,1,FALSE,
1371,仪玄,冲刺攻击,1371_RA,冲刺攻击,冲刺攻击:凌云破,,,0.499,0.046,1.005,1.097,1.189,0.312,0.015,0.477,0.507,0.537,0,0,0,0,0,1,,7.81,28.34,0,0,2833,2,3,6,1,4,34,3,TRUE,TRUE,0,0,,,,0,0,"[9, 16, 22]",,TRUE,1,FALSE,
1371,仪玄,闪避反击,1371_CA,闪避反击,闪避反击:除祟一击,,,2.196,0.2,4.396,4.796,5.196,2.365,0.108,3.553,3.769,3.985,0,0,0,0,0,2,,31.625,264.97,0,0,11496,2,4,6,1,4,80,6,TRUE,TRUE,0,0,,,,0,0,"[18, 31, 36, 41, 45, 51]",,TRUE,1,FALSE,
1371,仪玄,连携技,1371_QTE,连携技,连携技:玄墨迅击,,,5.331,0.485,10.666,11.636,12.606,2.274,0.104,3.418,3.626,3.834,0,0,0,0,0,0,,239.745,206.71,0,0,40620,3,5,6,1,4,125,13,TRUE,TRUE,0,125,,,,0,0,"[39, 42, 45, 47, 50, 53, 56, 59, 62, 70, 75, 79, 84]",,TRUE,1,TRUE,
1371,仪玄,终结技,1371_Q,终结技,终结技:青溟云影,,,18.534,1.685,37.069,40.439,43.809,4.071,0.186,6.117,6.489,6.861,0,0,0,0,0,0,,0,870.04,0,0,37003,3,6,6,1,4,208,30,TRUE,TRUE,0,208,,,,0,0,"[1, 2, 4, 10, 15, 20, 25, 29, 35, 40, 39, 45, 49, 54, 119, 124, 129, 134, 139, 144, 149, 154, 159, 164, 168, 173, 178, 187, 192, 196]",,TRUE,1,TRUE,
1371,仪玄,受击支援,1371_BH_Aid,受击支援,快速支援:流云影身,,,1.012,0.092,2.024,2.208,2.392,0.633,0.029,0.952,1.01,1.068,0,0,0,0,0,1,,15.8125,57.49,0,0,5748,6,7,6,1,4,69,7,TRUE,TRUE,0,0,,,,0,0,"[16, 21, 26, 31, 35, 41, 49]",,TRUE,1,FALSE,
1371,仪玄,招架/回避支援,1371_Light_parry_Aid,轻招架,招架支援:清霄劲,,轻招架,0,0,0,0,0,2.713,0.124,4.077,4.325,4.573,0,0,0,0,0,0,,0,366.64,0,0,0,6,8,0,1,4,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1371,仪玄,招架/回避支援,1371_Heavy_parry_Aid,重招架,招架支援:清霄劲,,重招架,0,0,0,0,0,3.428,0.156,5.144,5.456,5.768,0,0,0,0,0,0,,0,416.64,0,0,0,6,8,0,1,4,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1371,仪玄,招架/回避支援,1371_Chain_parry_Aid,连续招架,招架支援:清霄劲,,连续招架,0,0,0,0,0,1.668,0.076,2.504,2.656,2.808,0,0,0,0,0,0,,0,116.64,0,0,0,6,8,0,1,4,10,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1371,仪玄,突击支援,1371_Assault_Aid,突击支援,支援突击:霄云迅击,,,2.97,0.27,5.94,6.48,7.02,3.229,0.147,4.846,5.14,5.434,0,0,0,0,0,0,,104.9125,161.71,0,0,31551,6,9,6,1,4,99,13,TRUE,TRUE,0,0,,,,0,0,"[13, 16, 19, 22, 25, 27, 31, 36, 40, 63, 70, 75, 80]",,TRUE,1,FALSE,
1371,仪玄,终结技,1371_Q_A,终结技(形态A),终结技:符法千重,,,14.662,1.333,29.325,31.991,34.657,2.494,0.114,3.748,3.976,4.204,0,0,0,0,0,0,,0,726.71,0,0,22670,3,6,6,1,4,114,13,TRUE,TRUE,0,114,,,,0,0,"[36, 41, 45, 50, 55, 60, 65, 68, 73, 78, 81, 92, 100]",,TRUE,1,TRUE,
1371,仪玄,普攻,1371_SNA_B_1,强化特殊普攻,普通攻击:玄墨极阵,,有豆子时长按闪避,开阵。,3.052,0.278,6.11,6.666,7.222,2.934,0.134,4.408,4.676,4.944,0,0,0,0,0,0,,73.37,266.71,0,0,26670,0,0,6,1,4,150,22,TRUE,TRUE,0,0,,1371_SNA_B_2,,0,0,"[30, 35, 41, 46, 52, 56, 61, 63, 68, 74, 80, 86, 92, 99, 105, 112, 117, 123, 129, 135, 141, 147]",,TRUE,1,FALSE,
1371,仪玄,强化特殊技,1371_E_EX_B_2,(普攻)蓄力强化特殊技-蓄力段(满蓄力),强化特殊技:凝云术,,(普攻)蓄力时间达到最长时的技能数据,6.718,0.611,13.439,14.661,15.883,4.8,0.219,7.209,7.647,8.085,0,0,0,40,40,0,,174.185,200,0,0,50333,1,2,6,1,4,122,18,TRUE,TRUE,0,0,,1371_E_EX_B_3,1371_E_EX_B_1,0,0,"[12, 17, 22, 27, 34, 40, 45, 51, 57, 63, 69, 75, 81, 87, 93, 101, 107, 112]",,FALSE,1,FALSE,
1371,仪玄,强化特殊技,1371_E_EX_A_2,强化特殊技-第二段(点按),强化特殊技:霄云迅击-破,,,3.706,0.337,7.413,8.087,8.761,2.944,0.134,4.418,4.686,4.954,0,0,0,0,0,0,,99.0275,156.71,0,0,29904,1,2,6,1,4,94,15,TRUE,TRUE,0,0,,,,0,0,"[12, 15, 17, 20, 23, 26, 29, 32, 35, 38, 62, 69, 74, 78, 83]",,FALSE,1,FALSE,
1371,仪玄,强化特殊技,1371_E_EX_A_1_Add,强化E蓄力追加,强化特殊技:墨痕化形,,强化E蓄力完成后自动追加的一段伤害,1.496,0.136,2.992,3.264,3.536,0.758,0.035,1.143,1.213,1.283,0,0,0,0,0,0,,30.14,20,0,0,5583,1,2,6,1,4,8,3,TRUE,TRUE,0,0,,,1371_E_EX_A_1_FC,0,0,"[1, 3, 7]",,FALSE,1,FALSE,
1371,仪玄,强化特殊技,1371_E_EX_A_3,强化特殊技-第三段(点按),强化特殊技:青溟震击-破,,,4.264,0.388,8.532,9.308,10.084,2.853,0.13,4.283,4.543,4.803,0,0,0,20,20,0,,108.625,96.64,0,0,30545,1,2,6,1,4,61,8,TRUE,TRUE,0,0,,,,0,0,"[17, 27, 31, 35, 39, 43, 47, 51]",,TRUE,1,FALSE,
1371,仪玄,强化特殊技,1371_E_EX_B_1,(普攻)蓄力强化特殊技-开头,强化特殊技:墨烬影消,,强化E蓄力开始时会触发一次墨烬影消,也是能量的第一次扣除点,2.343,0.213,4.686,5.112,5.538,1.421,0.065,2.136,2.266,2.396,0,0,0,60,20,0,,58.2175,30.01,0,0,15716,1,2,6,1,4,44,3,TRUE,TRUE,0,0,,1371_E_EX_B_2,,0,0,"[33, 36, 43]",,FALSE,1,FALSE,
1371,仪玄,强化特殊技,1371_E_EX_B_3,(普攻)蓄力强化特殊技-结尾,强化特殊技:墨烬影消,,强化E蓄力结束时会触发一次墨烬影消,这次没有能量消耗,2.343,0.213,4.686,5.112,5.538,1.421,0.065,2.136,2.266,2.396,0,0,0,0,0,0,,58.2175,30.01,0,0,15716,1,2,6,1,4,69,1,TRUE,TRUE,0,0,,,,0,0,[8],,TRUE,1,FALSE,
1371,仪玄,强化特殊技,1371_Cinema_2,2画追加强化E,强化特殊技:符法千重-破,2画,2画解锁:消耗一层2画的Buff,并且在强化E后再追加发动一段攻击(需要主动点按),12,0,12,12,12,3.741,0,3.741,3.741,3.741,0,0,0,0,0,0,,62.37,226.71,0,0,22670,1,2,6,1,4,144,13,TRUE,TRUE,0,0,,,,0,0,"[49, 55, 59, 64, 69, 73, 78, 82, 87, 90, 96, 105, 114]",,TRUE,1,FALSE,
1371,仪玄,附加伤害,1371_Cinema_1,1画追加落雷,1画追加落雷,,,0.43,0.04,0.85,0.932,1.01,0.485,0.023,0.738,0.784,0.83,0,0,1.587,0,0,0,,12.1275,44.07,0,0,0,0,10,6,1,4,0,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,FALSE,
1391,橘福福,普攻,1391_NA_1,第1段普攻,普通攻击:恶虎七式·燎身爪,,一段,0.425,0.039,0.854,0.932,1.01,0.485,0.023,0.738,0.784,0.83,0,0,1.587,0,0,0,,12.1275,44.07,0,0,0,0,0,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1391,橘福福,普攻,1391_NA_2,第2段普攻,普通攻击:恶虎七式·燎身爪,,二段,0.452,0.042,0.914,0.998,1.082,0.391,0.018,0.589,0.625,0.661,0,0,1.279,0,0,0,,9.79,35.52,0,0,0,0,0,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1391,橘福福,普攻,1391_NA_3,第3段普攻,普通攻击:恶虎七式·燎身爪,,三段,0.315,0.029,0.634,0.692,0.75,0.485,0.023,0.738,0.784,0.83,0,0,1.586,0,0,0,,12.1275,44.05,0,0,4404,0,0,1,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1391,橘福福,普攻,1391_NA_4,第4段普攻,普通攻击:恶虎七式·燎身爪,,四段,2.184,0.199,4.373,4.771,5.169,1.339,0.061,2.01,2.132,2.254,0,0,4.382,0,0,0,,33.495,121.72,0,0,12171,0,0,1,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1391,橘福福,普攻,1391_SNA,特殊普攻,普通攻击:「虎威」,,,0.924,0.084,1.848,2.016,2.184,0.693,0.032,1.045,1.109,1.173,0,0,0,0,0,0,,0,0,0,0,0,0,0,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1391,橘福福,特殊技,1391_E,特殊技,特殊技:恶虎七式·下山虎,,,0.661,0.061,1.332,1.454,1.576,0.661,0.031,1.002,1.064,1.126,0,0,0,0,0,0,,16.5275,60.02,0,0,6001,1,1,1,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1391,橘福福,强化特殊技,1391_E_EX,强化特殊技,强化特殊技:恶虎七式改·下山猛虎,,,5.265,0.479,10.534,11.492,12.45,3.441,0.157,5.168,5.482,5.796,0,0,0,0,0,0,,181.9675,163.31,0,0,51212,1,2,1,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1391,橘福福,冲刺攻击,1391_CA_1,冲刺攻击第1段,冲刺攻击:恶虎七式·虎奔,,,0.368,0.034,0.742,0.81,0.878,0.184,0.009,0.283,0.301,0.319,0,0,0.601,0,0,0,,4.5925,16.69,0,0,0,2,3,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1391,橘福福,冲刺攻击,1391_CA_2,冲刺攻击第2段,冲刺攻击:恶虎七式·山君鼎戏,,无威势时,0.184,0.017,0.371,0.405,0.439,0.184,0.009,0.283,0.301,0.319,0,0,0.601,0,0,0,,4.5925,0,0,0,0,2,3,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1391,橘福福,冲刺攻击,1391_CA_3,冲刺攻击第3段,冲刺攻击:恶虎七式·山君鼎戏·威势,,有威势时,0.192,0.018,0.39,0.426,0.462,0.087,0.004,0.131,0.139,0.147,0,0,0,0,0,0,,0,0,0,0,0,2,3,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1391,橘福福,闪避反击,1391_RA,冲刺攻击,闪避反击:恶虎七式·离火回峰,,,2.078,0.189,4.157,4.535,4.913,1.853,0.085,2.788,2.958,3.128,0,0,2.462,0,0,0,,18.81,218.37,0,0,6836,2,4,1,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1391,橘福福,连携技,1391_QTE_A,连携技_A,连携技:虎釜崩,,,6.681,0.608,13.369,14.585,15.801,1.605,0.073,2.408,2.554,2.7,0,0,0,0,0,0,,148.7475,208.34,0,0,30808,3,5,1,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1391,橘福福,连携技,1391_QTE_B,连携技_B,连携技:虎釜震煞,,,3.545,0.323,7.098,7.744,8.39,2.216,0.101,3.327,3.529,3.731,0,0,0,0,0,0,,45.4025,0,0,0,6041,3,5,1,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1391,橘福福,终结技,1391_Q,终结技,终结技:恶虎七式·猛虎炸开花,,,16.638,1.513,33.281,36.307,39.333,10.907,0.496,16.363,17.355,18.347,0,0,0,0,0,0,,0,710.04,0,0,21003,3,6,1,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1391,橘福福,受击支援,1391_BH_Aid,受击支援,快速支援:怒决蹯,,,0.753,0.069,1.512,1.65,1.788,0.377,0.018,0.575,0.611,0.647,0,0,1.231,0,0,0,,9.405,34.19,0,0,3418,6,7,1,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1391,橘福福,招架/回避支援,1391_Light_parry_Aid,轻招架,招架支援:岿然虎踞,,轻招架,0,0,0,0,0,2.013,0.092,3.025,3.209,3.393,0,0,0,0,0,0,,0,366.64,0,0,0,6,8,0,1,0,30,1,TRUE,TRUE,0,0,,,,,,,,FALSE,1,TRUE,
1391,橘福福,招架/回避支援,1391_Heavy_parry_Aid,重招架,招架支援:岿然虎踞,,重招架,0,0,0,0,0,2.728,0.124,4.092,4.34,4.588,0,0,0,0,0,0,,0,416.64,0,0,0,6,8,0,1,0,30,1,TRUE,TRUE,0,0,,,,,,,,FALSE,1,TRUE,
1391,橘福福,招架/回避支援,1391_Chain_parry_Aid,连续招架,招架支援:岿然虎踞,,连续招架,0,0,0,0,0,1.368,0.063,2.061,2.187,2.313,0,0,0,0,0,0,,0,116.64,0,0,0,6,8,0,1,0,10,1,TRUE,TRUE,0,0,,,,,,,,FALSE,1,TRUE,
1391,橘福福,突击支援,1391_Assault_Aid,突击支援,支援突击:彪形焰颌,,,4.072,0.371,8.153,8.895,9.637,2.563,0.117,3.85,4.084,4.318,0,0,0,0,0,0,,114.51,185.01,0,0,34697,6,9,1,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1421,潘引壶,普攻,1421_NA_1,第1段普攻,普通攻击:极意连打,,一段,0.497,0.046,1.003,1.095,1.187,0.249,0.012,0.381,0.405,0.429,0,0,0.894,0,0,0,,6.8475,24.81,0,0,2480,0,0,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1421,潘引壶,普攻,1421_NA_2,第2段普攻,普通攻击:极意连打,,二段,0.498,0.046,1.004,1.096,1.188,0.475,0.022,0.717,0.761,0.805,0,0,1.708,0,0,0,,13.0625,47.45,0,0,4744,0,0,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1421,潘引壶,普攻,1421_NA_3,第3段普攻,普通攻击:极意连打,,三段,1.094,0.1,2.194,2.394,2.594,0.843,0.039,1.272,1.35,1.428,0,0,3.035,0,0,0,,23.1825,84.3,0,0,8429,0,0,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1421,潘引壶,普攻,1421_NA_4,第4段普攻,普通攻击:极意连打,,四段,2.113,0.193,4.236,4.622,5.008,1.665,0.076,2.501,2.653,2.805,0,0,5.994,0,0,0,,45.7875,166.49,0,0,16648,0,0,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1421,潘引壶,特殊技,1421_E_A,特殊技形态A,特殊技:爆音点穴指,,,0.625,0.057,1.252,1.366,1.48,0.625,0.029,0.944,1.002,1.06,0,0,0,0,0,0,,17.1875,62.49,0,0,6248,1,1,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1421,潘引壶,强化特殊技,1421_E_B,特殊技形态B,特殊技:断脉破穴手,,,3.841,0.35,7.691,8.391,9.091,2.573,0.117,3.86,4.094,4.328,0,0,0,0,0,0,,99.2475,101.7,0,0,28312,1,2,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1421,潘引壶,特殊技,1421_E_EX_1,第1段强化特殊技,强化特殊技:贴山震脉靠,,一段,0.973,0.089,1.952,2.13,2.308,0.417,0.019,0.626,0.664,0.702,0,0,0,0,0,0,,19.1125,0,0,0,4861,1,1,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1421,潘引壶,特殊技,1421_E_EX_2,第2段强化特殊技,强化特殊技:贴山震脉靠,,二段,0.973,0.089,1.952,2.13,2.308,0.417,0.019,0.626,0.664,0.702,0,0,0,0,0,0,,19.1125,0,0,0,4861,1,1,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1421,潘引壶,特殊技,1421_E_EX_3,第3段强化特殊技,强化特殊技:贴山震脉靠,,三段,0.973,0.089,1.952,2.13,2.308,0.417,0.019,0.626,0.664,0.702,0,0,0,0,0,0,,19.1125,0,0,0,4861,1,1,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1421,潘引壶,冲刺攻击,1421_RA,冲刺攻击,冲刺攻击:热油鼎盛,,,0.834,0.076,1.67,1.822,1.974,0.417,0.019,0.626,0.664,0.702,0,0,1.501,0,0,0,,11.4675,41.67,0,0,4166,2,3,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1421,潘引壶,闪避反击,1421_CA,闪避反击,闪避反击:四两拨千斤,,,2.192,0.2,4.392,4.792,5.192,1.917,0.088,2.885,3.061,3.237,0,0,3.3,0,0,0,,25.2175,241.67,0,0,9166,2,4,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1421,潘引壶,连携技,1421_QTE,连携技,连携技:锅气灌顶,,,6.323,0.575,12.648,13.798,14.948,2.333,0.107,3.51,3.724,3.938,0,0,0,0,0,0,,247.0325,233.3,0,0,43279,3,5,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1421,潘引壶,终结技,1421_Q,终结技,终结技:满汉全席!,,,16.279,1.48,32.559,35.519,38.479,0.984,0.045,1.479,1.569,1.659,0,0,0,0,0,0,,0,598.34,0,0,9833,3,6,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1421,潘引壶,受击支援,1421_BH_Aid,受击支援,快速支援:抬头见喜,,,0.917,0.084,1.841,2.009,2.177,0.459,0.021,0.69,0.732,0.774,0,0,1.65,0,0,0,,12.6225,45.84,0,0,4583,6,7,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1421,潘引壶,招架/回避支援,1421_Light_parry_Aid,轻招架,招架支援:见敌卸甲,,轻招架,0,0,0,0,0,2.251,0.103,3.384,3.59,3.796,0,0,0,0,0,0,,0,350.04,0,0,0,6,8,0,1,0,30,1,TRUE,TRUE,0,0,,,,,,,,FALSE,1,TRUE,
1421,潘引壶,招架/回避支援,1421_Heavy_parry_Aid,重招架,招架支援:见敌卸甲,,重招架,0,0,0,0,0,2.684,0.122,4.026,4.27,4.514,0,0,0,0,0,0,,0,383.34,0,0,0,6,8,0,1,0,30,1,TRUE,TRUE,0,0,,,,,,,,FALSE,1,TRUE,
1421,潘引壶,招架/回避支援,1421_Chain_parry_Aid,连续招架,招架支援:见敌卸甲,,连续招架,0,0,0,0,0,1.084,0.05,1.634,1.734,1.834,0,0,0,0,0,0,,0,83.34,0,0,0,6,8,0,1,0,10,1,TRUE,TRUE,0,0,,,,,,,,FALSE,1,TRUE,
1421,潘引壶,招架/回避支援,1421_Assault_Aid,突击支援,支援突击:借势打势,,,3.374,0.307,6.751,7.365,7.979,2.935,0.134,4.409,4.677,4.945,0,0,0,0,0,0,,104.885,161.64,0,0,31542,6,8,0,1,0,60,1,TRUE,TRUE,0,0,,,,,,,,,,,
1401,爱丽丝,普攻,1401_NA_1,第1段普攻,普通攻击:星仪序曲,,一段,0.606,0.056,1.222,1.334,1.446,0.436,0.02,0.656,0.696,0.736,0,0,1.585,0,0,0,,8.4975,44.01,0,0,3272,0,0,0,1,0,30,2,TRUE,TRUE,0,0,{'blade_etiquette': 9.8177},,,0,0,"[10, 21]",,FALSE,1,FALSE,
1401,爱丽丝,普攻,1401_NA_2,第2段普攻,普通攻击:星仪序曲,,二段,0.825,0.075,1.65,1.8,1.95,0.659,0.03,0.989,1.049,1.109,0,0,2.393,0,0,0,,12.815,66.48,0,0,4915,0,0,0,1,0,43,3,TRUE,TRUE,0,0,{'blade_etiquette': 15.6912},,,0,0,"[7, 21, 32]",,FALSE,1,FALSE,
1401,爱丽丝,普攻,1401_NA_3,第3段普攻,普通攻击:星仪序曲,,三段,0.587,0.054,1.181,1.289,1.397,0.54,0.025,0.815,0.865,0.915,0,0,1.962,0,0,0,,10.505,54.5,0,0,4001,0,0,0,1,0,28,2,TRUE,TRUE,0,0,{'blade_etiquette': 11.7733},,,0,0,"[2, 19]",,FALSE,1,FALSE,
1401,爱丽丝,普攻,1401_NA_4,第4段普攻,普通攻击:星仪序曲,,四段,1.284,0.117,2.571,2.805,3.039,1.188,0.054,1.782,1.89,1.998,0,0,4.32,0,0,0,,23.1,120,0,0,8808,0,0,0,1,0,70,6,TRUE,TRUE,0,0,{'blade_etiquette': 19.9492},,,0,0,"[14, 24, 32, 39, 47, 59]",,FALSE,1,FALSE,
1401,爱丽丝,普攻,1401_NA_5,第5段普攻,普通攻击:星仪序曲,,五段,1.303,0.119,2.612,2.85,3.088,1.321,0.061,1.992,2.114,2.236,0,0,4.802,0,0,0,,25.685,133.37,0,0,9749,0,0,0,1,0,70,4,TRUE,TRUE,0,0,{'blade_etiquette': 23.2261},,,0,0,"[8, 17, 24, 42]",,TRUE,1,FALSE,
1401,爱丽丝,普攻,1401_NA_5_PLUS,第5段强化普攻,普通攻击:星仪序曲,,五段(强化),2.164,0.197,4.331,4.725,5.119,2.096,0.096,3.152,3.344,3.536,0,0,7.622,0,0,0,,40.755,211.7,0,0,15507,0,0,0,1,0,118,6,TRUE,TRUE,0,0,{'blade_etiquette': 38.9238},,,0,0,"[8, 14, 23, 38, 81, 93]",,TRUE,1,FALSE,
1401,爱丽丝,强化特殊技,1401_E_EX_1,强化特殊技(后退),强化特殊技:极光突刺·南十字,,,5.32,0.484,10.644,11.612,12.58,4.053,0.185,6.088,6.458,6.828,40,40,0,0,0,0,,107.3875,186.67,0,0,36446,1,2,0,1,0,114,7,TRUE,TRUE,0,0,{'blade_etiquette': 106.4254},,,0,0,"[7, 13, 19, 25, 31, 38, 83]",,TRUE,1,FALSE,
1401,爱丽丝,特殊技,1401_E,特殊技,特殊技:破晓突刺,,,0.624,0.057,1.251,1.365,1.479,0.561,0.026,0.847,0.899,0.951,0,0,0,0,0,0,,21.835,56.66,0,0,3965,1,1,0,1,0,69,6,TRUE,TRUE,0,0,{'blade_etiquette': 9.558},,,0,0,"[16, 21, 27, 31, 37, 41]",,FALSE,1,FALSE,
1401,爱丽丝,强化特殊技,1401_E_EX_2,强化特殊技(突进),强化特殊技:极光突刺·北十字,,,4.6,0.419,9.209,10.047,10.885,3.452,0.157,5.179,5.493,5.807,40,40,0,0,0,0,,93.9125,139.97,0,0,31706,1,2,0,1,0,88,3,TRUE,TRUE,0,0,{'blade_etiquette': 100.3584},,,0,0,"[47, 45, 67]",,TRUE,1,FALSE,
1401,爱丽丝,普攻,1401_SNA_1,蓄力1段普攻,普通攻击:星芒圆舞曲,,一段蓄力,1.889,0.172,3.781,4.125,4.469,0.553,0.026,0.839,0.891,0.943,0,0,0,0,0,0,,21.505,111.67,0,0,7816,0,0,0,1,0,78,3,TRUE,TRUE,0,32,{'blade_etiquette': -100},,,0,0,"[39, 45, 53]",,TRUE,1,FALSE,
1401,爱丽丝,普攻,1401_SNA_2,蓄力2段普攻,普通攻击:星芒圆舞曲,,二段蓄力,3.044,0.277,6.091,6.645,7.199,0.776,0.036,1.172,1.244,1.316,0,0,0,0,0,0,,30.1675,156.67,0,0,10966,0,0,0,1,0,121,5,TRUE,TRUE,0,47,{'blade_etiquette': -200},,,0,0,"[52, 58, 65, 71, 92]",,TRUE,1,FALSE,
1401,爱丽丝,普攻,1401_SNA_3,蓄力3段普攻,普通攻击:星芒圆舞曲,,三段蓄力,9.657,0.878,19.315,21.071,22.827,1.972,0.09,2.962,3.142,3.322,0,0,0,0,0,0,,76.6975,398.34,0,0,46783,0,0,0,1,0,243,20,TRUE,TRUE,0,54,{'blade_etiquette': -300},,,0,0,"[59, 65, 70, 79, 84, 89, 94, 101, 105, 136, 145, 155, 161, 166, 171, 172, 174, 177, 181, 210]",,TRUE,1,FALSE,
1401,爱丽丝,冲刺攻击,1401_RA,冲刺攻击,冲刺攻击:剑舞之风,,,0.734,0.067,1.471,1.605,1.739,0.331,0.016,0.507,0.539,0.571,0,0,1.201,0,0,0,,6.435,33.36,0,0,2334,2,3,0,1,0,42,3,TRUE,TRUE,0,0,{'blade_etiquette': 5.7675},,,0,0,"[5, 21, 33]",,TRUE,1,FALSE,
1401,爱丽丝,闪避反击,1401_CA,闪避反击,闪避反击:剑闪之仪,,,2.84,0.259,5.689,6.207,6.725,2.195,0.1,3.295,3.495,3.695,0,0,4.38,0,0,0,,23.43,271.67,0,0,8516,2,4,0,1,0,83,3,TRUE,TRUE,0,0,{'blade_etiquette': 23.8736},,,0,0,"[8, 26, 47]",,TRUE,1,FALSE,
1401,爱丽丝,连携技,1401_QTE,连携技,连携技:星落间章,,,6.663,0.606,13.329,14.541,15.753,2.047,0.094,3.081,3.269,3.457,0,0,0,0,0,0,,167.805,206.71,0,0,28434,3,5,0,1,0,113,7,TRUE,TRUE,0,113,{'blade_etiquette': 40.559},,,0,0,"[17, 30, 31, 45, 46, 59, 69]",,TRUE,1,TRUE,
1401,爱丽丝,终结技,1401_Q,终结技,终结技:星芒终章,,,22.62,2.057,45.247,49.361,53.475,2.426,0.111,3.647,3.869,4.091,0,0,0,0,0,0,,0,745.04,0,0,72225,3,6,0,1,0,159,14,TRUE,TRUE,0,159,{'blade_etiquette': 200},,,0,0,"[4, 17, 28, 40, 50, 59, 65, 71, 79, 83, 91, 99, 132, 138]",,TRUE,1,TRUE,
1401,爱丽丝,快速支援,1401_BH_Aid,快速支援,快速支援:交替穿刺,,,1.633,0.149,3.272,3.57,3.868,1.469,0.067,2.206,2.34,2.474,0,0,2.671,0,0,0,,28.5725,74.19,0,0,10385,6,7,0,1,0,75,3,TRUE,TRUE,0,0,{'blade_etiquette': 12.5163},,,0,0,"[3, 16, 38]",,TRUE,1,FALSE,
1401,爱丽丝,招架/回避支援,1401_Light_parry_Aid,轻招架,招架支援:对抗防守,,轻招架,0,0,0,0,0,2.442,0.111,3.663,3.885,4.107,0,0,0,0,0,0,,0,366.64,0,0,0,6,8,0,1,0,0,1,TRUE,TRUE,0,0,{'blade_etiquette': 0},,,0,0,,,FALSE,1,TRUE,
1401,爱丽丝,招架/回避支援,1401_Heavy_parry_Aid,重招架,招架支援:对抗防守,,重招架,0,0,0,0,0,3.086,0.141,4.637,4.919,5.201,0,0,0,0,0,0,,0,416.64,0,0,0,6,8,0,1,0,0,1,TRUE,TRUE,0,0,{'blade_etiquette': 0},,,0,0,,,FALSE,1,TRUE,
1401,爱丽丝,招架/回避支援,1401_Chain_parry_Aid,连续招架,招架支援:对抗防守,,连续招架,0,0,0,0,0,1.502,0.069,2.261,2.399,2.537,0,0,0,0,0,0,,0,116.64,0,0,0,6,8,0,1,0,0,1,TRUE,TRUE,0,0,{'blade_etiquette': 0},,,0,0,,,FALSE,1,TRUE,
1401,爱丽丝,支援突击,1401_Assault_Aid,支援突击,支援突击:交叉还击,,,3.327,0.303,6.66,7.266,7.872,2.584,0.118,3.882,4.118,4.354,0,0,0,0,0,0,,66.1925,136.64,0,0,19717,6,9,0,1,0,91,6,TRUE,TRUE,0,0,{'blade_etiquette': 81.3164},,,0,0,"[4, 18, 35, 63, 63, 75]",,TRUE,1,FALSE,
1401,爱丽丝,附加伤害,1401_Cinema_6,6画附加伤害,6画附加伤害,,,33,0,33,33,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,1,3,0,1,FALSE,FALSE,0,0,"{'additional_damage': 1, 'blade_etiquette': 0}",,,0,0,,,FALSE,1,FALSE,
1411,柚叶,普攻,1411_NA_1,第1段普攻,普通攻击:狸之爪,,一段,0.377,0.035,0.762,0.832,0.902,0.44,0.02,0.66,0.7,0.74,0,0,1.44,0,0,0,0,11,39.98,0,0,1618,0,0,0,1,0,33,2,TRUE,TRUE,0,0,,,,0,0,"[7, 23]",,FALSE,1,FALSE,
1411,柚叶,普攻,1411_NA_2,第2段普攻,普通攻击:狸之爪,,二段,0.577,0.053,1.16,1.266,1.372,0.631,0.029,0.95,1.008,1.066,0,0,2.064,0,0,0,0,15.785,57.34,0,0,2682,0,0,0,1,0,41,5,TRUE,TRUE,0,0,,,,0,0,"[3, 13, 19, 25, 32]",,FALSE,1,FALSE,
1411,柚叶,普攻,1411_NA_3,第3段普攻,普通攻击:狸之爪,,三段,0.66,0.06,1.32,1.44,1.56,0.66,0.03,0.99,1.05,1.11,0,0,2.157,0,0,0,0,16.5,59.92,0,0,3164,0,0,0,1,0,41,3,TRUE,TRUE,0,0,,,,0,0,"[14, 23, 32]",,FALSE,1,FALSE,
1411,柚叶,普攻,1411_NA_4,第4段普攻,普通攻击:狸之爪,,四段,0.981,0.09,1.971,2.151,2.331,1.127,0.052,1.699,1.803,1.907,0,0,3.686,0,0,0,0,28.16,102.39,0,0,6296,0,0,0,1,0,52,4,TRUE,TRUE,0,0,,,,0,0,"[24, 31, 36, 43]",,FALSE,1,FALSE,
1411,柚叶,普攻,1411_NA_5,第5段普攻,普通攻击:狸之爪,,五段,1.641,0.15,3.291,3.591,3.891,1.636,0.075,2.461,2.611,2.761,0,0,5.352,0,0,0,0,40.8925,148.67,0,0,8839,0,0,0,1,0,48,1,TRUE,TRUE,0,0,,,,0,0,[14],,TRUE,1,FALSE,
1411,柚叶,特殊技,1411_E,特殊技,特殊技:软糖轰击,,,0.505,0.046,1.011,1.103,1.195,0.505,0.023,0.758,0.804,0.85,0,0,0,0,0,0,0,12.6225,45.84,0,0,4583,1,1,0,1,0,47,2,TRUE,TRUE,0,0,,,,0,0,"[17, 18]",,TRUE,1,FALSE,
1411,柚叶,强化特殊技,1411_E_EX_A,强化特殊技,强化特殊技:小心蛀牙,,,4.21,0.383,8.423,9.189,9.955,4.21,0.192,6.322,6.706,7.09,60,60,0,0,0,0,0,188.8425,180,0,0,48208,1,2,0,1,0,109,5,TRUE,TRUE,0,0,{'sugar_points': 2},,,1,67,"[16, 25, 34, 42, 67]",,TRUE,1,FALSE,
1411,柚叶,强化特殊技,1411_E_EX_B,强化特殊技加速,强化特殊技:小心蛀牙,就是现在!,,,2.414,0.22,4.834,5.274,5.714,2.67,0.122,4.012,4.256,4.5,60,60,0,0,0,0,0,140.745,63.37,0,0,32462,1,2,0,1,0,26,1,TRUE,TRUE,0,0,{'sugar_points': 2},,,1,21,[21],,TRUE,1,FALSE,
1411,柚叶,冲刺攻击,1411_RA,冲刺攻击,冲刺攻击:你要倒霉了!,,,0.698,0.064,1.402,1.53,1.658,0.349,0.016,0.525,0.557,0.589,0,0,1.141,0,0,0,0,8.7175,31.69,0,0,3168,2,3,0,1,0,56,4,TRUE,TRUE,0,0,,,,0,0,"[11, 19, 24, 30]",,TRUE,1,FALSE,
1411,柚叶,闪避反击,1411_CA,闪避反击,闪避反击:报复开始~,,,2.769,0.252,5.541,6.045,6.549,2.384,0.109,3.583,3.801,4.019,0,0,4.202,0,0,0,0,32.0925,266.7,0,0,11669,2,4,0,1,0,88,5,TRUE,TRUE,0,0,,,,0,0,"[17, 31, 37, 43, 62]",,TRUE,1,FALSE,
1411,柚叶,连携技,1411_QTE,连携技,连携技:恶作剧合战,,,5.987,0.545,11.982,13.072,14.162,2.329,0.106,3.495,3.707,3.919,0,0,0,0,0,0,0,241.0925,211.7,0,0,34469,3,5,0,1,0,65,5,TRUE,TRUE,0,65,{'sugar_points': 1},,,1,52,"[7, 15, 21, 27, 52]",,TRUE,1,TRUE,
1411,柚叶,终结技,1411_Q,终结技,终结技:不投降就捣乱,,,14.432,1.312,28.864,31.488,34.112,2.86,0.13,4.29,4.55,4.81,0,0,0,0,0,0,0,0,759.97,0,0,25996,3,6,0,1,0,105,10,TRUE,TRUE,0,105,{'sugar_points': 2},,,1,99,"[0, 1, 3, 7, 11, 15, 18, 41, 65, 99]",,TRUE,1,TRUE,
1411,柚叶,快速支援,1411_BH_Aid,快速支援,快速支援:甜点时间,,,1.229,0.112,2.461,2.685,2.909,0.615,0.028,0.923,0.979,1.035,0,0,2.011,0,0,0,0,15.3725,55.85,0,0,5584,6,7,0,1,0,69,6,TRUE,TRUE,0,0,,,,0,0,"[3, 9, 14, 18, 23, 43]",,TRUE,1,FALSE,
1411,柚叶,招架/回避支援,1411_Light_parry_Aid,轻招架,招架支援:糖分补充,,轻招架,0,0,0,0,0,2.952,0.135,4.437,4.707,4.977,0,0,0,0,0,0,0,0,383.34,0,0,0,6,8,0,1,0,30,1,TRUE,TRUE,0,0,{'sugar_points': 1},,,0,0,,,FALSE,1,TRUE,
1411,柚叶,招架/回避支援,1411_Heavy_parry_Aid,重招架,招架支援:糖分补充,,重招架,0,0,0,0,0,3.428,0.156,5.144,5.456,5.768,0,0,0,0,0,0,0,0,416.64,0,0,0,6,8,0,1,0,30,1,TRUE,TRUE,0,0,,,,0,0,,,FALSE,1,TRUE,
1411,柚叶,招架/回避支援,1411_Chain_parry_Aid,连续招架,招架支援:糖分补充,,连续招架,0,0,0,0,0,1.668,0.076,2.504,2.656,2.808,0,0,0,0,0,0,0,0,116.64,0,0,0,6,8,0,1,0,10,1,TRUE,TRUE,0,0,{'sugar_points': 1},,,0,0,,,FALSE,1,TRUE,
1411,柚叶,突击支援,1411_Assault_Aid,突击支援,支援突击:来块曲奇,,,2.943,0.268,5.891,6.427,6.963,2.514,0.115,3.779,4.009,4.239,0,0,0,0,0,0,0,84.2875,111.7,0,0,24801,6,9,0,1,0,81,4,TRUE,TRUE,0,0,,,,0,0,"[16, 25, 31, 48]",,TRUE,1,FALSE,
1411,柚叶,普攻,1411_CoAttack_A,协同攻击(转圈),普通攻击:硬糖射击,,,1.32,0.12,2.64,2.88,3.12,0.88,0.04,1.32,1.4,1.48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,162,7,FALSE,TRUE,0,0,"{'aftershock_attack': 1, 'sugar_points': -1}",,,0,0,"[38, 45, 54, 58, 61, 117, 118]",,FALSE,1,FALSE,
1411,柚叶,普攻,1411_SNA_C,特殊普攻(开伞),普通攻击:狸之帐,,招架,0,0,0,0,0,1.668,0.076,2.504,2.656,2.808,0,0,0,0,0,0,0,0,116.64,0,0,0,0,8,0,1,0,96,0,TRUE,TRUE,0,0,{'sugar_points': 1},,,0,0,,,FALSE,1,FALSE,
1411,柚叶,普攻,1411_SNA_A,特殊普攻(Dot),普通攻击:彩糖花火,,,0.275,0.025,0.55,0.6,0.65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1766,0,0,0,1,0,0,1,FALSE,FALSE,0,0,"{'ineffective_anomaly_buildup': 1, 'additional_damage': 1}",,,0,0,,,FALSE,1,FALSE,
1411,柚叶,普攻,1411_SNA_B,特殊普攻(Dot强化),普通攻击:彩糖花火·极,,,3.08,0.28,6.16,6.72,7.28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12000,0,0,0,1,0,0,1,FALSE,FALSE,0,0,"{'ineffective_anomaly_buildup': 1, 'additional_damage': 1}",,,0,0,,,FALSE,1,FALSE,
1411,柚叶,普攻,1411_CoAttack_B,协同攻击(狸猫和人协同),普通攻击:狸之助,,狸猫阿釜协同柚叶攻击,1.1,0.1,2.2,2.4,2.6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,85,8,FALSE,TRUE,0,0,,,,0,0,"[15, 20, 24, 33, 41, 60, 63, 68]",,FALSE,1,FALSE,
1411,柚叶,普攻,1411_CoAttack_C,协同攻击(狸猫),普通攻击:狸之助,,狸猫阿釜自主攻击,1.1,0.1,2.2,2.4,2.6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,85,8,FALSE,TRUE,0,0,,,,0,0,"[15, 20, 24, 33, 41, 60, 63, 68]",,FALSE,1,FALSE,
1411,柚叶,支援突击,1411_Assault_Aid_A,突击支援(特殊),支援突击:夹心硬糖射击,,,4.636,0.422,9.278,10.122,10.966,4.087,0.186,6.133,6.505,6.877,0,0,0,0,0,0,0,129.635,221.64,0,0,39642,6,9,0,1,0,137,6,TRUE,TRUE,0,0,,,,0,0,"[28, 35, 42, 51, 98, 113]",,TRUE,1,FALSE,
1411,柚叶,附加伤害,1411_Cinema_6,6画炮弹,6画附加伤害:强力炮弹,,,0.3,0,0.3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,FALSE,FALSE,0,0,"{'additional_damage': 1, 'sugar_points': -1}",,,0,0,,,FALSE,1,FALSE,
1411,柚叶,支援突击,1411_Assault_Aid_B,突击支援(6画专属),支援突击:夹心硬糖射击,,6画专属蓄力版支援突击,4.636,0.422,9.278,10.122,10.966,4.087,0.186,6.133,6.505,6.877,0,0,0,0,0,0,0,129.635,221.64,0,0,39642,6,9,0,1,0,137,6,TRUE,TRUE,0,0,,,,0,0,"[28, 35, 42, 51, 122, 136]",,TRUE,1,FALSE,
1461,席德,普攻,1461_NA_1,第一段普攻,普通攻击:霜蕊轮舞,,一段,0.465,0.043,0.938,1.024,1.11,0.294,0.014,0.448,0.476,0.504,0,0,0.667,0,0,0,0,7.3425,26.67,0,0,0,0,0,0,1,0,27,5,TRUE,TRUE,0,0,{'steel_charge': 2.9334},,,0,0,"[7, 12, 15, 19, 20]",,FALSE,1,FALSE,
1461,席德,普攻,1461_NA_2,第二段普攻,普通攻击:霜蕊轮舞,,二段,1.241,0.113,2.484,2.71,2.936,0.829,0.038,1.247,1.323,1.399,0,0,1.884,0,0,0,0,20.735,75.34,0,0,0,0,0,0,1,0,33,6,TRUE,TRUE,0,0,{'steel_charge': 8.2867},,,0,0,"[10, 11, 16, 17, 23, 24]",,FALSE,1,FALSE,
1461,席德,普攻,1461_NA_3,第三段普攻,普通攻击:霜蕊轮舞,,三段,0.718,0.066,1.444,1.576,1.708,0.446,0.021,0.677,0.719,0.761,0,0,1.014,0,0,0,0,11.165,40.54,0,0,4053,0,0,3,1,0,69,11,TRUE,TRUE,0,0,{'steel_charge': 4.4585},,,0,0,"[13, 16, 19, 24, 31, 37, 43, 49, 56, 62, 68]",,FALSE,1,FALSE,
1461,席德,普攻,1461_NA_4,第四段普攻,普通攻击:霜蕊轮舞,,四段,3.586,0.326,7.172,7.824,8.476,2.265,0.103,3.398,3.604,3.81,0,0,5.146,0,0,0,0,56.6225,205.84,0,0,20583,0,0,3,1,0,21,3,TRUE,TRUE,0,0,{'steel_charge': 22.6419},,,0,0,"[1, 11, 20]",,TRUE,1,FALSE,
1461,席德,特殊技,1461_E,特殊技,特殊技:苍霜零落,,,0.477,0.044,0.961,1.049,1.137,0.477,0.022,0.719,0.763,0.807,0,0,0,0,0,0,0,11.935,43.35,0,0,4334,1,0,3,1,0,62,4,TRUE,TRUE,0,0,{'steel_charge': 4.7685},,,0,0,"[40, 44, 49, 54]",,TRUE,1,FALSE,
1461,席德,普攻,1461_SNA_1,第一段重击,普通攻击:落华·重戮,,,1.666,0.152,3.338,3.642,3.946,1.448,0.066,2.174,2.306,2.438,0,0,3.291,0,0,0,0,36.2175,131.64,0,0,13163,0,1,3,1,0,85,2,TRUE,TRUE,0,0,{'steel_charge': 0},,,0,0,"[50, 65]",,TRUE,1,FALSE,
1461,席德,普攻,1461_SNA_2,第二段重击,普通攻击:落华·崩坠一式,,,5.283,0.481,10.574,11.536,12.498,0.679,0.031,1.02,1.082,1.144,0,0,1.543,0,0,0,0,16.9675,61.7,0,0,6169,0,1,3,1,0,35,6,FALSE,TRUE,0,0,{'steel_charge': 0},1461_SNA_3,,0,0,"[1, 7, 12, 17, 22, 27]",,FALSE,1,FALSE,
1461,席德,普攻,1461_SNA_3,第三段重击,普通攻击:落华·崩坠二式,,,9.894,0.9,19.794,21.594,23.394,1.688,0.077,2.535,2.689,2.843,0,0,3.835,0,0,0,0,42.185,153.37,0,0,15336,0,1,3,1,0,118,3,FALSE,TRUE,0,0,{'steel_charge': 0},,,0,0,"[45, 58, 68]",,TRUE,1,FALSE,
1461,席德,强化特殊技,1461_E_EX_1,强化特殊技-导弹(单段),强化特殊技:铁萼雨幕,,,0.6834,0.0622,1.3676,1.492,1.6164,0.5868,0.0267,0.8805,0.9339,0.9873,5,5,0,0,0,0,0,19.50025,26.997,0,0,5773.5,1,2,3,1,0,10,2,TRUE,TRUE,0,0,{'steel_charge': 2.96964},1461_SNA_1|1461_E_EX_2,,0,0,"[1, 5]",attribute.1461:special_state→强化E达到最大次数==True|action.1461:strict_linked_after==1461_E_EX_1|status.1461:on_field==False;attribute.1461:special_state→强化E达到最大次数==False|action.1461:strict_linked_after==1461_E_EX_1|status.1461:on_field==False,FALSE,1,FALSE,
1461,席德,冲刺攻击,1461_RA,冲刺攻击,冲刺攻击:磁陨轮舞,,,0.642,0.059,1.291,1.409,1.527,0.321,0.015,0.486,0.516,0.546,0,0,0.73,0,0,0,0,8.03,29.17,0,0,0,2,3,0,1,0,42,3,TRUE,TRUE,0,0,{'steel_charge': 3.2084},,,0,0,"[19, 24, 27]",,FALSE,1,FALSE,
1461,席德,闪避反击,1461_CA,闪避反击,闪避反击:裂萼纷华,,,3.201,0.291,6.402,6.984,7.566,2.549,0.116,3.825,4.057,4.289,0,0,3.293,0,0,0,0,36.2175,281.7,0,0,13169,2,4,3,1,0,78,12,TRUE,TRUE,0,0,{'steel_charge': 24.487},,,0,0,"[6, 21, 21, 31, 56, 57, 62, 63, 69, 70, 74, 75]",,TRUE,1,FALSE,
1461,席德,连携技,1461_QTE,连携技,连携技:落霰风暴,,,7.342,0.668,14.69,16.026,17.362,2.953,0.135,4.438,4.708,4.978,0,0,0,0,0,0,0,256.685,268.37,0,0,46786,3,5,3,1,0,148,14,TRUE,TRUE,0,148,{'steel_charge': 29.5204},,,0,0,"[19, 25, 27, 48, 55, 56, 113, 119, 120, 125, 126, 131, 135, 137]",,TRUE,1,TRUE,
1461,席德,终结技,1461_Q,终结技,终结技:机芯花园·绽放!,,,32.497,2.955,65.002,70.912,76.822,5.959,0.271,8.94,9.482,10.024,0,0,0,0,0,0,0,0,1041.71,0,0,54170,3,6,3,1,0,260,27,TRUE,TRUE,0,260,{'steel_charge': 0},,,0,0,"[52, 55, 56, 65, 67, 77, 78, 86, 87, 97, 99, 107, 108, 114, 116, 128, 129, 134, 138, 146, 148, 155, 158, 217, 218, 229, 240]",,TRUE,1,TRUE,
1461,席德,快速支援,1461_BH_Aid,快速支援,快速支援:花雨齐射,,,1.54,0.14,3.08,3.36,3.64,0.77,0.035,1.155,1.225,1.295,0,0,1.75,0,0,0,0,19.25,70,0,0,7000,6,7,3,1,0,72,10,TRUE,TRUE,0,0,{'steel_charge': 7.7},,,0,0,"[6, 14, 18, 26, 51, 52, 57, 58, 69, 70]",,TRUE,1,FALSE,
1461,席德,招架/回避支援,1461_Light_parry_Aid,轻招架,招架支援:雏华屏障,,轻招架,0,0,0,0,0,2.713,0.124,4.077,4.325,4.573,0,0,0,0,0,0,0,0,366.64,0,0,0,6,8,0,1,0,30,1,TRUE,TRUE,0,0,{'steel_charge': 0},,,0,0,,,FALSE,1,TRUE,
1461,席德,招架/回避支援,1461_Heavy_parry_Aid,重招架,招架支援:雏华屏障,,重招架,0,0,0,0,0,3.428,0.156,5.144,5.456,5.768,0,0,0,0,0,0,0,0,416.64,0,0,0,6,8,0,1,0,30,1,TRUE,TRUE,0,0,{'steel_charge': 0},,,0,0,,,FALSE,1,TRUE,
1461,席德,招架/回避支援,1461_Chain_parry_Aid,连续招架,招架支援:雏华屏障,,连续招架,0,0,0,0,0,1.668,0.076,2.504,2.656,2.808,0,0,0,0,0,0,0,0,116.64,0,0,0,6,8,0,1,0,10,1,TRUE,TRUE,0,0,{'steel_charge': 0},,,0,0,,,FALSE,1,TRUE,
1461,席德,突击支援,1461_Assault_Aid,突击支援,支援突击:绯芯爆裂,,,4.56,0.415,9.125,9.955,10.785,4.016,0.183,6.029,6.395,6.761,0,0,0,0,0,0,0,127.6,216.71,0,0,38976,6,9,3,1,0,122,19,TRUE,TRUE,0,0,{'steel_charge': 23.8371},,,0,0,"[1, 8, 11, 29, 33, 36, 42, 49, 72, 73, 80, 81, 88, 89, 95, 96, 107, 113, 116]",,TRUE,1,FALSE,
1461,席德,强化特殊技,1461_E_EX_2,强化特殊技-收尾,强化特殊技:铁萼雨幕·离,,,0.184,0.017,0.371,0.405,0.439,0.092,0.005,0.147,0.157,0.167,0,0,0,0,0,0,0,2.31,8.34,0,0,833,1,2,3,1,0,53,5,TRUE,TRUE,0,0,{'steel_charge': 0},,,0,0,"[10, 11, 18, 21, 26]",,TRUE,1,FALSE,
1461,席德,强化特殊技,1461_E_EX_0,强化特殊技-启动,强化特殊技-启动,,,0,0,0,0,0,0,0,0,0,0,60,10,0,0,0,0,0,0,0,0,0,0,1,2,0,1,0,35,1,TRUE,TRUE,0,0,{'steel_charge': 0},,,0,0,,,FALSE,1,FALSE,
1461,席德,附加伤害,1461_Cinema_6,6画炮击,6画炮击,,,1.65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,3,1,0,0,1,FALSE,FALSE,0,0,"{'steel_charge': 0, 'additional_damage': 1}",,,0,0,,,FALSE,1,FALSE,
================================================
FILE: zsim/data/str_to_num.py
================================================
import csv
import os
from decimal import Decimal
from tqdm import tqdm
"""
用于将./data 目录下的csv重整
"""
def is_percentage(value):
"""检查字符串是否为百分比形式"""
return isinstance(value, str) and "%" in value
def convert_percentage(value):
"""将百分比字符串转换为浮点数"""
return float(Decimal(value.strip("%")) / 100)
# noinspection PyBroadException
def process_cell(value):
"""处理单个单元格的值"""
if is_percentage(value):
try:
return convert_percentage(value)
except Exception:
return value
try:
return eval(value)
except Exception:
return value
def process_csv_file(file_path):
"""处理单个 CSV 文件"""
with open(file_path, mode="r", newline="", encoding="utf-8") as file:
reader = csv.reader(file)
rows = list(reader)
# 处理除首行首列外的数据
for row_index in tqdm(range(1, len(rows))):
# for row_index in range(1, len(rows)):
if row_index == "add_buff_to":
continue
for col_index in range(1, len(rows[row_index])):
if col_index == "add_buff_to":
continue
rows[row_index][col_index] = process_cell(rows[row_index][col_index])
# 将处理后的数据写回文件
with open(file_path, mode="w", newline="", encoding="utf-8") as file:
writer = csv.writer(file)
writer.writerows(rows)
def process_all_csv_files(directory):
"""处理指定目录下的所有 CSV 文件"""
for filename in os.listdir(directory):
if filename.endswith(".csv"):
file_path = os.path.join(directory, filename)
process_csv_file(file_path)
if __name__ == "__main__":
# path = './data'
# process_all_csv_files(path)
# 老配置
process_csv_file("./zsim/data/skill.csv")
# process_csv_file("./zsim/data/character.csv")
# process_csv_file("./zsim/data/enemy.csv")
# process_csv_file("./zsim/data/enemy_adjustment.csv")
# process_csv_file("./zsim/data/weapon.csv")
# # 新配置
# process_csv_file("skill.csv")
# process_csv_file("character.csv")
# process_csv_file("enemy.csv")
# process_csv_file("enemy_adjustment.csv")
# process_csv_file("weapon.csv")
================================================
FILE: zsim/data/weapon.csv
================================================
名称,ID,稀有度,职业,0级基础攻击力,60级基础攻击力,高级属性,0级高级属性值,60级高级属性值
机巧心种,14146,S,强攻,48,713.76,暴击率,0.1,0.24
铸梦炉歌,14145,S,支援,48,713.76,生命值,0.12,0.3
狸法七变化,14141,S,支援,48,713.76,能量自动回复,0.24,0.6
十方锻星,14140,S,异常,48,713.76,攻击力,0.12,0.3
福虓炉炉,14139,S,击破,48,713.76,攻击力,0.12,0.3
牺牲洁纯,14138,S,强攻,48,713.76,暴击伤害,0.19,0.48
青溟笼舍,14137,S,命破,50,743.5,生命值,0.12,0.3
索魂影眸,14136,S,击破,48,713.76,暴击率,0.1,0.24
飞鸟星梦,14133,S,异常,48,713.76,异常精通,36.0,90.0
心弦夜响,14132,S,强攻,48,713.76,暴击率,0.1,0.24
玲珑妆匣,14131,S,支援,48,713.76,攻击力,0.12,0.3
嚣枪喧焰,14130,S,强攻,48,713.76,能量自动回复,0.24,0.6
千面日陨,14129,S,强攻,48,713.76,暴击率,0.1,0.24
淬锋钳刺,14126,S,异常,48,713.76,异常精通,36.0,90.0
玉壶青冰,14125,S,击破,48,713.76,冲击力,0.07,0.18
防暴者Ⅵ型,14124,S,强攻,48,713.76,暴击伤害,0.19,0.48
时流贤者,14122,S,异常,48,713.76,攻击力,0.12,0.3
啜泣摇篮,14121,S,支援,46,684.02,穿透率,0.1,0.24
残心青囊,14120,S,强攻,48,713.76,暴击伤害,0.19,0.48
深海访客,14119,S,强攻,48,713.76,暴击率,0.1,0.24
嵌合编译器,14118,S,异常,46,684.02,穿透率,0.1,0.24
灼心摇壶,14117,S,异常,48,713.76,攻击力,0.12,0.3
焰心桂冠,14116,S,击破,48,713.76,冲击力,0.07,0.18
拘缚者,14114,S,击破,46,684.02,冲击力,0.07,0.18
燃狱齿轮,14110,S,击破,46,684.02,冲击力,0.07,0.18
霰落星殿,14109,S,异常,50,743.5,暴击率,0.1,0.24
奔袭獠牙,14107,S,防护,48,713.76,冲击力,0.07,0.18
海妖摇篮,14105,S,命破,48,713.76,生命值,0.12,0.3
硫磺石,14104,S,强攻,46,684.02,攻击力,0.12,0.3
钢铁肉垫,14102,S,强攻,46,684.02,暴击率,0.1,0.24
左轮转子,14003,A,击破,40,594.8,冲击力,0.06,0.15
逍遥游球,14002,A,支援,40,594.8,能量自动回复,0.2,0.5
加农转子,14001,A,强攻,40,594.8,暴击率,0.08,0.2
燔火胧夜,13144,A,命破,42,624.54,生命值,0.1,0.25
震元奇枢,13142,A,防护,42,624.54,攻击力,0.1,0.25
裁纸刀,13135,A,击破,42,624.54,冲击力,0.06,0.15
轰鸣座驾,13128,A,异常,42,624.54,攻击力,0.1,0.25
维序者-特化型,13127,A,防护,42,624.54,攻击力,0.1,0.25
好斗的阿炮,13115,A,支援,42,624.54,能量自动回复,0.2,0.5
含羞恶面,13113,A,支援,42,624.54,攻击力,0.1,0.25
比格气缸,13112,A,防护,42,624.54,防御力,0.16,0.4
旋钻机-赤轴,13111,A,强攻,42,624.54,能量自动回复,0.2,0.5
仿制星徽引擎,13108,A,强攻,42,624.54,攻击力,0.1,0.25
家政员,13106,A,强攻,42,624.54,攻击力,0.1,0.25
聚宝箱,13103,A,支援,42,624.54,能量自动回复,0.2,0.5
德玛拉电池Ⅱ型,13101,A,击破,42,624.54,冲击力,0.06,0.15
光影刻刀,13016,A,防护,40,594.8,冲击力,0.06,0.15
强音热望,13015,A,强攻,40,594.8,暴击率,0.08,0.2
电波漫步,13014,A,命破,40,594.8,生命值,0.1,0.25
鎏金花信,13013,A,强攻,40,594.8,攻击力,0.1,0.25
幻变魔方,13012,A,命破,40,594.8,攻击力,0.1,0.25
春日融融,13011,A,防护,40,594.8,攻击力,0.1,0.25
兔能环,13010,A,防护,40,594.8,防御力,0.16,0.4
触电唇彩,13009,A,异常,40,594.8,异常精通,30.0,75.0
双生泣星,13008,A,异常,40,594.8,攻击力,0.1,0.25
正版变身器,13007,A,防护,40,594.8,生命值,0.1,0.25
贵重骨核,13006,A,击破,40,594.8,冲击力,0.06,0.15
人为刀俎,13005,A,击破,40,594.8,能量自动回复,0.2,0.5
星徽引擎,13004,A,强攻,40,594.8,攻击力,0.1,0.25
雨林饕客,13003,A,异常,40,594.8,异常精通,30.0,75.0
时光切片,13002,A,支援,40,594.8,穿透率,0.08,0.2
街头巨星,13001,A,强攻,40,594.8,攻击力,0.1,0.25
「灰烬」-钴蓝,12015,B,命破,32,475.84,生命值,0.08,0.2
「恒等式」-变格,12014,B,防护,32,475.84,防御力,0.13,0.32
「恒等式」-本格,12013,B,防护,32,475.84,防御力,0.13,0.32
「电磁暴」-叁式,12012,B,异常,32,475.84,穿透率,0.06,0.16
「电磁暴」-贰式,12011,B,异常,32,475.84,异常精通,24.0,60.0
「电磁暴」-壹式,12010,B,异常,32,475.84,攻击力,0.08,0.2
「湍流」-斧型,12009,B,击破,32,475.84,能量自动回复,0.16,0.4
「湍流」-矢型,12008,B,击破,32,475.84,冲击力,0.05,0.12
「湍流」-铳型,12007,B,击破,32,475.84,攻击力,0.08,0.2
「残响」-Ⅲ型,12006,B,支援,32,475.84,生命值,0.08,0.2
「残响」-Ⅱ型,12005,B,支援,32,475.84,能量自动回复,0.16,0.4
「残响」-Ⅰ型,12004,B,支援,32,475.84,攻击力,0.08,0.2
「月相」-朔,12003,B,强攻,32,475.84,暴击率,0.06,0.16
「月相」-晦,12002,B,强攻,32,475.84,攻击力,0.08,0.2
「月相」-望,12001,B,强攻,32,475.84,攻击力,0.08,0.2
「月相」-望,12001,B,强攻,32,475.84,攻击力,0.08,0.2
================================================
FILE: zsim/data/激活判断.csv
================================================
BuffName,is_weapon,is_debuff,is_additional_ability,is_cinema,from,exist,description,durationtype,maxduration,maxcount,incrementalstep,prejudge,endjudge,freshtype,alltime,hitincrease,increaseCD,readyto_increase,simple_judge_logic,simple_start_logic,simple_end_logic,simple_hit_logic,simple_effect_logic,simple_exit_logic,refinement,add_buff_to,schedule_judge,individual_settled,backend_acitve,label,label_effect_rule,listener_id
Buff-角色-艾莲-核心被动,FALSE,FALSE,FALSE,FALSE,艾莲,FALSE,冲刺攻击和冰普攻的爆伤+100%,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-艾莲-额外能力,FALSE,FALSE,TRUE,FALSE,艾莲,FALSE,造成冰伤时使后续冰伤+3%,TRUE,600,10,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1深海访客-冰伤,TRUE,FALSE,FALSE,FALSE,深海访客,FALSE,常驻冰伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1深海访客-暴击率-1,TRUE,FALSE,FALSE,FALSE,深海访客,FALSE,普攻命中加暴击,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1深海访客-暴击率-2,TRUE,FALSE,FALSE,FALSE,深海访客,FALSE,冲刺攻击造成冰伤加暴击,TRUE,900,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2深海访客-冰伤,TRUE,FALSE,FALSE,FALSE,深海访客,FALSE,常驻冰伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2深海访客-暴击率-1,TRUE,FALSE,FALSE,FALSE,深海访客,FALSE,普攻命中加暴击,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2深海访客-暴击率-2,TRUE,FALSE,FALSE,FALSE,深海访客,FALSE,冲刺攻击造成冰伤加暴击,TRUE,900,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3深海访客-冰伤,TRUE,FALSE,FALSE,FALSE,深海访客,FALSE,常驻冰伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3深海访客-暴击率-1,TRUE,FALSE,FALSE,FALSE,深海访客,FALSE,普攻命中加暴击,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3深海访客-暴击率-2,TRUE,FALSE,FALSE,FALSE,深海访客,FALSE,冲刺攻击造成冰伤加暴击,TRUE,900,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4深海访客-冰伤,TRUE,FALSE,FALSE,FALSE,深海访客,FALSE,常驻冰伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4深海访客-暴击率-1,TRUE,FALSE,FALSE,FALSE,深海访客,FALSE,普攻命中加暴击,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4深海访客-暴击率-2,TRUE,FALSE,FALSE,FALSE,深海访客,FALSE,冲刺攻击造成冰伤加暴击,TRUE,900,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5深海访客-冰伤,TRUE,FALSE,FALSE,FALSE,深海访客,FALSE,常驻冰伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5深海访客-暴击率-1,TRUE,FALSE,FALSE,FALSE,深海访客,FALSE,普攻命中加暴击,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5深海访客-暴击率-2,TRUE,FALSE,FALSE,FALSE,深海访客,FALSE,冲刺攻击造成冰伤加暴击,TRUE,900,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-驱动盘-极地重金属-冲刺攻击增伤,FALSE,FALSE,FALSE,FALSE,极地重金属,FALSE,冲刺攻击增伤20%,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-驱动盘-极地重金属-普攻增伤,FALSE,FALSE,FALSE,FALSE,极地重金属,FALSE,普攻增伤20%,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-驱动盘-极地重金属-冲刺与普攻增伤-有条件,FALSE,FALSE,FALSE,FALSE,极地重金属,FALSE,施加冻结或触发碎冰使冲刺攻击和普攻伤害提高40%,TRUE,720,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,TRUE,FALSE,FALSE,,,
Buff-驱动盘-震星迪斯科,FALSE,FALSE,FALSE,FALSE,震星迪斯科,FALSE,普攻、冲刺攻击、闪避反击的失衡值提升20%,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,TRUE,,,
Buff-驱动盘-啄木鸟电音-普攻,FALSE,FALSE,FALSE,FALSE,啄木鸟电音,FALSE,普攻E暴击时,+9%攻击力,TRUE,360,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,TRUE,FALSE,FALSE,,,
Buff-驱动盘-啄木鸟电音-闪避反击,FALSE,FALSE,FALSE,FALSE,啄木鸟电音,FALSE,闪反暴击时,+9%攻击力,TRUE,360,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,TRUE,FALSE,FALSE,,,
Buff-驱动盘-啄木鸟电音-强化特殊技,FALSE,FALSE,FALSE,FALSE,啄木鸟电音,FALSE,强化E暴击时,+9%攻击力,TRUE,360,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,TRUE,FALSE,FALSE,,,
Buff-角色-莱特-核心被动-冲击力提升,FALSE,FALSE,FALSE,FALSE,莱特,FALSE,每消耗1点士气,冲击力提升0.2%,TRUE,360,100,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-莱特-核心被动-冰火双抗,FALSE,TRUE,FALSE,FALSE,莱特,FALSE,轻拳起攻或是刺拳连击命中减目标双抗,TRUE,1800,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-莱特-核心被动-失衡时间延长,FALSE,TRUE,FALSE,FALSE,莱特,FALSE,终结一击命中敌人时使失衡时间延长,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-莱特-额外能力-冰火增伤,FALSE,FALSE,TRUE,FALSE,莱特,FALSE,士气喷发状态下的A5给全队加冰火伤,TRUE,1800,300,5,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE,TRUE,0,1110,FALSE,FALSE,FALSE,,,
Buff-角色-莱卡恩-核心被动-失衡值提升,FALSE,FALSE,FALSE,FALSE,莱卡恩,FALSE,蓄力普攻的失衡值提升80%,TRUE,0,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-莱卡恩-核心被动-减冰抗,FALSE,TRUE,FALSE,FALSE,莱卡恩,FALSE,强化E或支援突击命中时降低目标25%冰抗,TRUE,1800,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-莱卡恩-额外能力-失衡易伤倍率,FALSE,TRUE,TRUE,FALSE,莱卡恩,FALSE,莱卡恩攻击命中处于失衡状态下的敌人时,目标失衡易伤倍率提高35%,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1,TRUE,FALSE,FALSE,,,
Buff-异常-霜寒,FALSE,TRUE,FALSE,FALSE,enemy,FALSE,受到暴击伤害+10%,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1,FALSE,FALSE,FALSE,,,
Buff-异常-畏缩,FALSE,TRUE,FALSE,FALSE,enemy,FALSE,受到的失衡值+7.5%,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-苍角-核心被动-1,FALSE,FALSE,FALSE,FALSE,苍角,FALSE,500点攻击力,TRUE,1800,500,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,0,100,FALSE,FALSE,FALSE,,,
Buff-角色-苍角-核心被动-2,FALSE,FALSE,FALSE,FALSE,苍角,FALSE,三豆给额外500攻击力,TRUE,1800,500,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,0,100,FALSE,FALSE,FALSE,,,
Buff-武器-精1含羞恶面-冰伤,TRUE,FALSE,FALSE,FALSE,含羞恶面,FALSE,常驻冰伤,FALSE,0,1,0,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2含羞恶面-冰伤,TRUE,FALSE,FALSE,FALSE,含羞恶面,FALSE,常驻冰伤,FALSE,0,1,0,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3含羞恶面-冰伤,TRUE,FALSE,FALSE,FALSE,含羞恶面,FALSE,常驻冰伤,FALSE,0,1,0,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4含羞恶面-冰伤,TRUE,FALSE,FALSE,FALSE,含羞恶面,FALSE,常驻冰伤,FALSE,0,1,0,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5含羞恶面-冰伤,TRUE,FALSE,FALSE,FALSE,含羞恶面,FALSE,常驻冰伤,FALSE,0,1,0,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1含羞恶面-叠层攻击力,TRUE,FALSE,FALSE,FALSE,含羞恶面,FALSE,发动强化E时全队攻击力提升,TRUE,720,4,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精2含羞恶面-叠层攻击力,TRUE,FALSE,FALSE,FALSE,含羞恶面,FALSE,发动强化E时全队攻击力提升,TRUE,720,4,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精3含羞恶面-叠层攻击力,TRUE,FALSE,FALSE,FALSE,含羞恶面,FALSE,发动强化E时全队攻击力提升,TRUE,720,4,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精4含羞恶面-叠层攻击力,TRUE,FALSE,FALSE,FALSE,含羞恶面,FALSE,发动强化E时全队攻击力提升,TRUE,720,4,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精5含羞恶面-叠层攻击力,TRUE,FALSE,FALSE,FALSE,含羞恶面,FALSE,发动强化E时全队攻击力提升,TRUE,720,4,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精1燃狱齿轮-后台能量自动回复,TRUE,FALSE,FALSE,FALSE,燃狱齿轮,FALSE,位于后台时能量自动回复提升,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,1,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精2燃狱齿轮-后台能量自动回复,TRUE,FALSE,FALSE,FALSE,燃狱齿轮,FALSE,位于后台时能量自动回复提升,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,2,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精3燃狱齿轮-后台能量自动回复,TRUE,FALSE,FALSE,FALSE,燃狱齿轮,FALSE,位于后台时能量自动回复提升,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,3,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精4燃狱齿轮-后台能量自动回复,TRUE,FALSE,FALSE,FALSE,燃狱齿轮,FALSE,位于后台时能量自动回复提升,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,4,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精5燃狱齿轮-后台能量自动回复,TRUE,FALSE,FALSE,FALSE,燃狱齿轮,FALSE,位于后台时能量自动回复提升,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,5,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精1燃狱齿轮-叠层冲击力,TRUE,FALSE,FALSE,FALSE,燃狱齿轮,FALSE,发动强化特殊技时冲击力提升,TRUE,600,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精2燃狱齿轮-叠层冲击力,TRUE,FALSE,FALSE,FALSE,燃狱齿轮,FALSE,发动强化特殊技时冲击力提升,TRUE,600,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精3燃狱齿轮-叠层冲击力,TRUE,FALSE,FALSE,FALSE,燃狱齿轮,FALSE,发动强化特殊技时冲击力提升,TRUE,600,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精4燃狱齿轮-叠层冲击力,TRUE,FALSE,FALSE,FALSE,燃狱齿轮,FALSE,发动强化特殊技时冲击力提升,TRUE,600,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精5燃狱齿轮-叠层冲击力,TRUE,FALSE,FALSE,FALSE,燃狱齿轮,FALSE,发动强化特殊技时冲击力提升,TRUE,600,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精1拘缚者,TRUE,FALSE,FALSE,FALSE,拘缚者,FALSE,攻击命中敌人时,普攻造成的伤害和失衡值提升,TRUE,480,5,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精2拘缚者,TRUE,FALSE,FALSE,FALSE,拘缚者,FALSE,攻击命中敌人时,普攻造成的伤害和失衡值提升,TRUE,480,5,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精3拘缚者,TRUE,FALSE,FALSE,FALSE,拘缚者,FALSE,攻击命中敌人时,普攻造成的伤害和失衡值提升,TRUE,480,5,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精4拘缚者,TRUE,FALSE,FALSE,FALSE,拘缚者,FALSE,攻击命中敌人时,普攻造成的伤害和失衡值提升,TRUE,480,5,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精5拘缚者,TRUE,FALSE,FALSE,FALSE,拘缚者,FALSE,攻击命中敌人时,普攻造成的伤害和失衡值提升,TRUE,480,5,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精1焰心桂冠-冲击力提升,TRUE,FALSE,FALSE,FALSE,焰心桂冠,FALSE,发动快速支援或极限支援时冲击力提升,TRUE,480,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2焰心桂冠-冲击力提升,TRUE,FALSE,FALSE,FALSE,焰心桂冠,FALSE,发动快速支援或极限支援时冲击力提升,TRUE,480,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3焰心桂冠-冲击力提升,TRUE,FALSE,FALSE,FALSE,焰心桂冠,FALSE,发动快速支援或极限支援时冲击力提升,TRUE,480,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4焰心桂冠-冲击力提升,TRUE,FALSE,FALSE,FALSE,焰心桂冠,FALSE,发动快速支援或极限支援时冲击力提升,TRUE,480,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5焰心桂冠-冲击力提升,TRUE,FALSE,FALSE,FALSE,焰心桂冠,FALSE,发动快速支援或极限支援时冲击力提升,TRUE,480,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1焰心桂冠-受暴伤提升,TRUE,TRUE,FALSE,FALSE,焰心桂冠,FALSE,普攻命中时施加爆伤提升,TRUE,1800,20,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1,FALSE,FALSE,FALSE,,,
Buff-武器-精2焰心桂冠-受暴伤提升,TRUE,TRUE,FALSE,FALSE,焰心桂冠,FALSE,普攻命中时施加爆伤提升,TRUE,1800,20,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1,FALSE,FALSE,FALSE,,,
Buff-武器-精3焰心桂冠-受暴伤提升,TRUE,TRUE,FALSE,FALSE,焰心桂冠,FALSE,普攻命中时施加爆伤提升,TRUE,1800,20,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1,FALSE,FALSE,FALSE,,,
Buff-武器-精4焰心桂冠-受暴伤提升,TRUE,TRUE,FALSE,FALSE,焰心桂冠,FALSE,普攻命中时施加爆伤提升,TRUE,1800,20,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1,FALSE,FALSE,FALSE,,,
Buff-武器-精5焰心桂冠-受暴伤提升,TRUE,TRUE,FALSE,FALSE,焰心桂冠,FALSE,普攻命中时施加爆伤提升,TRUE,1800,20,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1,FALSE,FALSE,FALSE,,,
Buff-武器-精1玉壶青冰-普攻加冲击,TRUE,FALSE,FALSE,FALSE,玉壶青冰,FALSE,普攻命中时冲击力提升,TRUE,480,30,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2玉壶青冰-普攻加冲击,TRUE,FALSE,FALSE,FALSE,玉壶青冰,FALSE,普攻命中时冲击力提升,TRUE,480,30,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3玉壶青冰-普攻加冲击,TRUE,FALSE,FALSE,FALSE,玉壶青冰,FALSE,普攻命中时冲击力提升,TRUE,480,30,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4玉壶青冰-普攻加冲击,TRUE,FALSE,FALSE,FALSE,玉壶青冰,FALSE,普攻命中时冲击力提升,TRUE,480,30,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5玉壶青冰-普攻加冲击,TRUE,FALSE,FALSE,FALSE,玉壶青冰,FALSE,普攻命中时冲击力提升,TRUE,480,30,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1玉壶青冰-15层后增伤,TRUE,FALSE,FALSE,FALSE,玉壶青冰,FALSE,普攻命中时若茶劲>=15层则全队增伤,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精2玉壶青冰-15层后增伤,TRUE,FALSE,FALSE,FALSE,玉壶青冰,FALSE,普攻命中时若茶劲>=15层则全队增伤,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精3玉壶青冰-15层后增伤,TRUE,FALSE,FALSE,FALSE,玉壶青冰,FALSE,普攻命中时若茶劲>=15层则全队增伤,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精4玉壶青冰-15层后增伤,TRUE,FALSE,FALSE,FALSE,玉壶青冰,FALSE,普攻命中时若茶劲>=15层则全队增伤,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精5玉壶青冰-15层后增伤,TRUE,FALSE,FALSE,FALSE,玉壶青冰,FALSE,普攻命中时若茶劲>=15层则全队增伤,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精1贵重骨核-75%以上,TRUE,FALSE,FALSE,FALSE,贵重骨核,FALSE,敌方生命值大于75%时增加失衡值,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2贵重骨核-75%以上,TRUE,FALSE,FALSE,FALSE,贵重骨核,FALSE,敌方生命值大于75%时增加失衡值,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3贵重骨核-75%以上,TRUE,FALSE,FALSE,FALSE,贵重骨核,FALSE,敌方生命值大于75%时增加失衡值,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4贵重骨核-75%以上,TRUE,FALSE,FALSE,FALSE,贵重骨核,FALSE,敌方生命值大于75%时增加失衡值,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5贵重骨核-75%以上,TRUE,FALSE,FALSE,FALSE,贵重骨核,FALSE,敌方生命值大于75%时增加失衡值,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1贵重骨核-50%以上,TRUE,FALSE,FALSE,FALSE,贵重骨核,FALSE,敌方生命之大于50%时增加失衡值,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2贵重骨核-50%以上,TRUE,FALSE,FALSE,FALSE,贵重骨核,FALSE,敌方生命之大于50%时增加失衡值,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3贵重骨核-50%以上,TRUE,FALSE,FALSE,FALSE,贵重骨核,FALSE,敌方生命之大于50%时增加失衡值,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4贵重骨核-50%以上,TRUE,FALSE,FALSE,FALSE,贵重骨核,FALSE,敌方生命之大于50%时增加失衡值,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5贵重骨核-50%以上,TRUE,FALSE,FALSE,FALSE,贵重骨核,FALSE,敌方生命之大于50%时增加失衡值,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1人为刀俎,TRUE,FALSE,FALSE,FALSE,人为刀俎,FALSE,每拥有10点能量叠层加冲击力,TRUE,480,8,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2人为刀俎,TRUE,FALSE,FALSE,FALSE,人为刀俎,FALSE,每拥有10点能量叠层加冲击力,TRUE,480,8,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3人为刀俎,TRUE,FALSE,FALSE,FALSE,人为刀俎,FALSE,每拥有10点能量叠层加冲击力,TRUE,480,8,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4人为刀俎,TRUE,FALSE,FALSE,FALSE,人为刀俎,FALSE,每拥有10点能量叠层加冲击力,TRUE,480,8,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5人为刀俎,TRUE,FALSE,FALSE,FALSE,人为刀俎,FALSE,每拥有10点能量叠层加冲击力,TRUE,480,8,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1德玛拉电池II型-电伤,TRUE,FALSE,FALSE,FALSE,德玛拉电池Ⅱ型,FALSE,电伤提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2德玛拉电池II型-电伤,TRUE,FALSE,FALSE,FALSE,德玛拉电池Ⅱ型,FALSE,电伤提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3德玛拉电池II型-电伤,TRUE,FALSE,FALSE,FALSE,德玛拉电池Ⅱ型,FALSE,电伤提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4德玛拉电池II型-电伤,TRUE,FALSE,FALSE,FALSE,德玛拉电池Ⅱ型,FALSE,电伤提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5德玛拉电池II型-电伤,TRUE,FALSE,FALSE,FALSE,德玛拉电池Ⅱ型,FALSE,电伤提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1德玛拉电池II型-能量获得效率,TRUE,FALSE,FALSE,FALSE,德玛拉电池Ⅱ型,FALSE,闪避反击或支援攻击命中时,能量获得效率提升,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2德玛拉电池II型-能量获得效率,TRUE,FALSE,FALSE,FALSE,德玛拉电池Ⅱ型,FALSE,闪避反击或支援攻击命中时,能量获得效率提升,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3德玛拉电池II型-能量获得效率,TRUE,FALSE,FALSE,FALSE,德玛拉电池Ⅱ型,FALSE,闪避反击或支援攻击命中时,能量获得效率提升,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4德玛拉电池II型-能量获得效率,TRUE,FALSE,FALSE,FALSE,德玛拉电池Ⅱ型,FALSE,闪避反击或支援攻击命中时,能量获得效率提升,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5德玛拉电池II型-能量获得效率,TRUE,FALSE,FALSE,FALSE,德玛拉电池Ⅱ型,FALSE,闪避反击或支援攻击命中时,能量获得效率提升,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1硫磺石,TRUE,FALSE,FALSE,FALSE,硫磺石,FALSE,普攻、冲刺攻击、闪反命中时攻击力提升,TRUE,480,8,1,FALSE,FALSE,TRUE,FALSE,TRUE,30,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精2硫磺石,TRUE,FALSE,FALSE,FALSE,硫磺石,FALSE,普攻、冲刺攻击、闪反命中时攻击力提升,TRUE,480,8,1,FALSE,FALSE,TRUE,FALSE,TRUE,30,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精3硫磺石,TRUE,FALSE,FALSE,FALSE,硫磺石,FALSE,普攻、冲刺攻击、闪反命中时攻击力提升,TRUE,480,8,1,FALSE,FALSE,TRUE,FALSE,TRUE,30,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精4硫磺石,TRUE,FALSE,FALSE,FALSE,硫磺石,FALSE,普攻、冲刺攻击、闪反命中时攻击力提升,TRUE,480,8,1,FALSE,FALSE,TRUE,FALSE,TRUE,30,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精5硫磺石,TRUE,FALSE,FALSE,FALSE,硫磺石,FALSE,普攻、冲刺攻击、闪反命中时攻击力提升,TRUE,480,8,1,FALSE,FALSE,TRUE,FALSE,TRUE,30,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,TRUE,FALSE,,,
Buff-角色-苍角-额外能力,FALSE,FALSE,TRUE,FALSE,苍角,FALSE,消耗涡流发动展旗时,全队造成冰伤提升20%,TRUE,1320,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1110,FALSE,FALSE,FALSE,,,
Buff-角色-青衣-核心被动-失衡易伤,FALSE,TRUE,FALSE,FALSE,青衣,FALSE,增加失衡易伤,TRUE,9999999,20,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-青衣-额外能力-失衡效率,FALSE,FALSE,TRUE,FALSE,青衣,FALSE,普攻失衡值增加,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-青衣-额外能力-冲击转攻击,FALSE,FALSE,TRUE,FALSE,青衣,FALSE,冲击力转攻击力,TRUE,9999999,600,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-11号-核心被动,FALSE,FALSE,FALSE,FALSE,11号,FALSE,火力镇压伤害增加,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-11号-组队被动-常驻,FALSE,FALSE,TRUE,FALSE,11号,FALSE,火伤增加,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-11号-组队被动-失衡,FALSE,FALSE,TRUE,FALSE,11号,FALSE,失衡期火伤增加,TRUE,9999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-雅-终结技-冰伤,FALSE,FALSE,FALSE,FALSE,雅,FALSE,大招后30%冰伤加成,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-雅-核心被动-冰焰,FALSE,TRUE,FALSE,FALSE,雅,FALSE,暴击率转烈霜积蓄效率,TRUE,1800,80,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,FALSE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-雅-核心被动-霜灼,FALSE,TRUE,FALSE,FALSE,雅,FALSE,全队全属性异常积蓄效率提升,TRUE,9999999,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-雅-组队被动-普攻增伤,FALSE,FALSE,TRUE,FALSE,雅,FALSE,蓄力普攻伤害增加,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-雅-组队被动-无视冰抗,FALSE,FALSE,TRUE,FALSE,雅,FALSE,紊乱后的蓄力普攻无视冰抗,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-露西-特殊技-攻击力,FALSE,FALSE,FALSE,FALSE,露西,FALSE,,TRUE,600,600,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,0,1110,FALSE,FALSE,FALSE,,,
Buff-角色-露西-长按特殊技-攻击力,FALSE,FALSE,FALSE,FALSE,露西,FALSE,,TRUE,900,600,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,0,1110,FALSE,FALSE,FALSE,,,
Buff-角色-露西-连携技-攻击力,FALSE,FALSE,FALSE,TRUE,露西,FALSE,2画解锁攻击力Buff,TRUE,600,600,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,2,1110,FALSE,FALSE,FALSE,,,
Buff-角色-露西-终结技-攻击力,FALSE,FALSE,FALSE,TRUE,露西,FALSE,2画解锁攻击力Buff,TRUE,600,600,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,2,1110,FALSE,FALSE,FALSE,,,
Buff-角色-派派-组队被动-积蓄效率,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-派派-组队被动-全队增伤,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-柏妮思-组队被动-延长灼烧,FALSE,TRUE,TRUE,FALSE,柏妮思,FALSE,延长灼烧(触发器),无实际效果,不会进循环,TRUE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-丽娜-核心被动-穿透率,FALSE,FALSE,FALSE,FALSE,丽娜,FALSE,全队穿透率提升,TRUE,300,30,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,FALSE,0,1110,FALSE,FALSE,FALSE,,,
Buff-角色-丽娜-组队被动-增伤,FALSE,FALSE,TRUE,FALSE,丽娜,FALSE,全队电伤提升,TRUE,9999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1110,FALSE,FALSE,FALSE,,,
Buff-角色-丽娜-组队被动-延长感电,FALSE,TRUE,TRUE,FALSE,丽娜,FALSE,延长感电(触发器),无实际效果,不会进循环,TRUE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1,FALSE,FALSE,FALSE,,,
Buff-音擎-精1霰落星殿-暴伤,TRUE,FALSE,FALSE,FALSE,霰落星殿,FALSE,暴伤提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-音擎-精2霰落星殿-暴伤,TRUE,FALSE,FALSE,FALSE,霰落星殿,FALSE,暴伤提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-音擎-精3霰落星殿-暴伤,TRUE,FALSE,FALSE,FALSE,霰落星殿,FALSE,暴伤提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-音擎-精4霰落星殿-暴伤,TRUE,FALSE,FALSE,FALSE,霰落星殿,FALSE,暴伤提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-音擎-精5霰落星殿-暴伤,TRUE,FALSE,FALSE,FALSE,霰落星殿,FALSE,暴伤提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-音擎-精1霰落星殿-叠层冰伤,TRUE,FALSE,FALSE,FALSE,霰落星殿,FALSE,强化E或是异常触发叠层,TRUE,900,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,TRUE,FALSE,,,
Buff-音擎-精2霰落星殿-叠层冰伤,TRUE,FALSE,FALSE,FALSE,霰落星殿,FALSE,强化E或是异常触发叠层,TRUE,900,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,TRUE,FALSE,,,
Buff-音擎-精3霰落星殿-叠层冰伤,TRUE,FALSE,FALSE,FALSE,霰落星殿,FALSE,强化E或是异常触发叠层,TRUE,900,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,TRUE,FALSE,,,
Buff-音擎-精4霰落星殿-叠层冰伤,TRUE,FALSE,FALSE,FALSE,霰落星殿,FALSE,强化E或是异常触发叠层,TRUE,900,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,TRUE,FALSE,,,
Buff-音擎-精5霰落星殿-叠层冰伤,TRUE,FALSE,FALSE,FALSE,霰落星殿,FALSE,强化E或是异常触发叠层,TRUE,900,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,TRUE,FALSE,,,
Buff-驱动盘-折枝剑歌-暴伤,FALSE,FALSE,FALSE,FALSE,折枝剑歌,FALSE,异常掌控>115时加暴伤,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-驱动盘-折枝剑歌-暴击率,FALSE,FALSE,FALSE,FALSE,折枝剑歌,FALSE,冻结或碎冰提高暴击率,TRUE,900,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,TRUE,FALSE,FALSE,,,
Buff-异常-烈霜霜寒,FALSE,TRUE,FALSE,FALSE,enemy,FALSE,受到暴击伤害+10%,TRUE,1200,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-青衣-核心被动-额外电压补偿,FALSE,FALSE,FALSE,FALSE,青衣,FALSE,溢出电压补偿失衡值,FALSE,0,25,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-驱动盘-自由蓝调-物理,FALSE,TRUE,FALSE,FALSE,自由蓝调,FALSE,物理积蓄抗性降低,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1,FALSE,FALSE,FALSE,,,
Buff-驱动盘-自由蓝调-火,FALSE,TRUE,FALSE,FALSE,自由蓝调,FALSE,火积蓄抗性降低,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1,FALSE,FALSE,FALSE,,,
Buff-驱动盘-自由蓝调-冰,FALSE,TRUE,FALSE,FALSE,自由蓝调,FALSE,冰积蓄抗性降低,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1,FALSE,FALSE,FALSE,,,
Buff-驱动盘-自由蓝调-电,FALSE,TRUE,FALSE,FALSE,自由蓝调,FALSE,电积蓄抗性降低,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1,FALSE,FALSE,FALSE,,,
Buff-驱动盘-自由蓝调-以太,FALSE,TRUE,FALSE,FALSE,自由蓝调,FALSE,以太积蓄抗性降低,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1,FALSE,FALSE,FALSE,,,
Buff-驱动盘-自由蓝调-烈霜,FALSE,TRUE,FALSE,FALSE,自由蓝调,FALSE,烈霜积蓄抗性降低,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1,FALSE,FALSE,FALSE,,,
Buff-驱动盘-河豚电音-终结技伤害提升,FALSE,FALSE,FALSE,FALSE,河豚电音,FALSE,终结技增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-驱动盘-河豚电音-攻击力提升,FALSE,FALSE,FALSE,FALSE,河豚电音,FALSE,发动大招攻击力提升,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-驱动盘-静听嘉音-嘉音,FALSE,FALSE,FALSE,FALSE,静听嘉音,FALSE,支援攻击叠层,TRUE,900,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1110,FALSE,FALSE,TRUE,,,
Buff-驱动盘-静听嘉音-增伤,FALSE,FALSE,FALSE,FALSE,静听嘉音,FALSE,支援攻击入场后增伤,TRUE,900,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,0,1110,TRUE,FALSE,TRUE,,,
Buff-驱动盘-摇摆爵士-全队增伤,FALSE,FALSE,FALSE,FALSE,摇摆爵士,FALSE,连携技或大招全队增伤,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1110,FALSE,FALSE,FALSE,,,
Buff-驱动盘-激素朋克-全局攻击力,FALSE,FALSE,FALSE,FALSE,激素朋克,FALSE,全局攻击力加成,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,Hormone_Punk_1
Buff-驱动盘-混沌爵士-火电伤,FALSE,FALSE,FALSE,FALSE,混沌爵士,FALSE,火电伤加成,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-驱动盘-混沌爵士-前台增伤,FALSE,FALSE,FALSE,FALSE,混沌爵士,FALSE,换入前台增伤保持,TRUE,300,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,450,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-驱动盘-原始朋克-全队增伤,FALSE,FALSE,FALSE,FALSE,原始朋克,FALSE,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-驱动盘-獠牙重金属-增伤,FALSE,FALSE,FALSE,FALSE,獠牙重金属,FALSE,队伍中任意角色对敌人施加强击效果时,装备者对目标增伤35%,该Buff效果完全由监听器控制,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,Fanged_Metal_1
Buff-武器-精1啜泣摇篮-后台回能,TRUE,FALSE,FALSE,FALSE,啜泣摇篮,FALSE,后台回能,TRUE,9999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,1,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精1啜泣摇篮-全队增伤,TRUE,FALSE,FALSE,FALSE,啜泣摇篮,FALSE,攻击命中后全队增伤,TRUE,180,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精1啜泣摇篮-全队增伤自增长,TRUE,FALSE,FALSE,FALSE,啜泣摇篮,FALSE,全队增伤自增长,TRUE,180,6,1,TRUE,FALSE,TRUE,FALSE,FALSE,30,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,1,1110,TRUE,FALSE,TRUE,,,
Buff-武器-精2啜泣摇篮-后台回能,TRUE,FALSE,FALSE,FALSE,啜泣摇篮,FALSE,后台回能,TRUE,9999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,2,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精2啜泣摇篮-全队增伤,TRUE,FALSE,FALSE,FALSE,啜泣摇篮,FALSE,攻击命中后全队增伤,TRUE,180,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精2啜泣摇篮-全队增伤自增长,TRUE,FALSE,FALSE,FALSE,啜泣摇篮,FALSE,全队增伤自增长,TRUE,180,6,1,TRUE,FALSE,TRUE,FALSE,FALSE,30,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,2,1110,TRUE,FALSE,TRUE,,,
Buff-武器-精3啜泣摇篮-后台回能,TRUE,FALSE,FALSE,FALSE,啜泣摇篮,FALSE,后台回能,TRUE,9999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,3,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精3啜泣摇篮-全队增伤,TRUE,FALSE,FALSE,FALSE,啜泣摇篮,FALSE,攻击命中后全队增伤,TRUE,180,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精3啜泣摇篮-全队增伤自增长,TRUE,FALSE,FALSE,FALSE,啜泣摇篮,FALSE,全队增伤自增长,TRUE,180,6,1,TRUE,FALSE,TRUE,FALSE,FALSE,30,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,3,1110,TRUE,FALSE,TRUE,,,
Buff-武器-精4啜泣摇篮-后台回能,TRUE,FALSE,FALSE,FALSE,啜泣摇篮,FALSE,后台回能,TRUE,9999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,4,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精4啜泣摇篮-全队增伤,TRUE,FALSE,FALSE,FALSE,啜泣摇篮,FALSE,攻击命中后全队增伤,TRUE,180,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精4啜泣摇篮-全队增伤自增长,TRUE,FALSE,FALSE,FALSE,啜泣摇篮,FALSE,全队增伤自增长,TRUE,180,6,1,TRUE,FALSE,TRUE,FALSE,FALSE,30,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,4,1110,TRUE,FALSE,TRUE,,,
Buff-武器-精5啜泣摇篮-后台回能,TRUE,FALSE,FALSE,FALSE,啜泣摇篮,FALSE,后台回能,TRUE,9999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,5,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精5啜泣摇篮-全队增伤,TRUE,FALSE,FALSE,FALSE,啜泣摇篮,FALSE,攻击命中后全队增伤,TRUE,180,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精5啜泣摇篮-全队增伤自增长,TRUE,FALSE,FALSE,FALSE,啜泣摇篮,FALSE,全队增伤自增长,TRUE,180,6,1,TRUE,FALSE,TRUE,FALSE,FALSE,30,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,5,1110,TRUE,FALSE,TRUE,,,
Buff-武器-精1时光切片-回能回喧响,TRUE,FALSE,FALSE,FALSE,时光切片,FALSE,回能回喧响,TRUE,5,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,720,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精2时光切片-回能回喧响,TRUE,FALSE,FALSE,FALSE,时光切片,FALSE,回能回喧响,TRUE,5,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,720,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精3时光切片-回能回喧响,TRUE,FALSE,FALSE,FALSE,时光切片,FALSE,回能回喧响,TRUE,5,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,720,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精4时光切片-回能回喧响,TRUE,FALSE,FALSE,FALSE,时光切片,FALSE,回能回喧响,TRUE,5,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,720,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精5时光切片-回能回喧响,TRUE,FALSE,FALSE,FALSE,时光切片,FALSE,回能回喧响,TRUE,5,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,720,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精1聚宝箱-回能,TRUE,FALSE,FALSE,FALSE,聚宝箱,FALSE,强化E、连携大招造成以太伤害后增加回能,TRUE,120,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精1聚宝箱-全队增伤,TRUE,FALSE,FALSE,FALSE,聚宝箱,FALSE,强化E、连携大招造成以太伤害后全队增伤,TRUE,120,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1110,FALSE,FALSE,TRUE,,,
Buff-武器-精2聚宝箱-回能,TRUE,FALSE,FALSE,FALSE,聚宝箱,FALSE,强化E、连携大招造成以太伤害后增加回能,TRUE,120,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精2聚宝箱-全队增伤,TRUE,FALSE,FALSE,FALSE,聚宝箱,FALSE,强化E、连携大招造成以太伤害后全队增伤,TRUE,120,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1110,FALSE,FALSE,TRUE,,,
Buff-武器-精3聚宝箱-回能,TRUE,FALSE,FALSE,FALSE,聚宝箱,FALSE,强化E、连携大招造成以太伤害后增加回能,TRUE,120,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精3聚宝箱-全队增伤,TRUE,FALSE,FALSE,FALSE,聚宝箱,FALSE,强化E、连携大招造成以太伤害后全队增伤,TRUE,120,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1110,FALSE,FALSE,TRUE,,,
Buff-武器-精4聚宝箱-回能,TRUE,FALSE,FALSE,FALSE,聚宝箱,FALSE,强化E、连携大招造成以太伤害后增加回能,TRUE,120,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精4聚宝箱-全队增伤,TRUE,FALSE,FALSE,FALSE,聚宝箱,FALSE,强化E、连携大招造成以太伤害后全队增伤,TRUE,120,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1110,FALSE,FALSE,TRUE,,,
Buff-武器-精5聚宝箱-回能,TRUE,FALSE,FALSE,FALSE,聚宝箱,FALSE,强化E、连携大招造成以太伤害后增加回能,TRUE,120,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精5聚宝箱-全队增伤,TRUE,FALSE,FALSE,FALSE,聚宝箱,FALSE,强化E、连携大招造成以太伤害后全队增伤,TRUE,120,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1110,FALSE,FALSE,TRUE,,,
Buff-武器-精1好斗的阿炮-全局攻击力,TRUE,FALSE,FALSE,FALSE,好斗的阿炮,FALSE,任意命中全队攻击力提升,TRUE,480,4,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE,TRUE,1,1110,FALSE,TRUE,TRUE,,,
Buff-武器-精2好斗的阿炮-全局攻击力,TRUE,FALSE,FALSE,FALSE,好斗的阿炮,FALSE,任意命中全队攻击力提升,TRUE,480,4,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE,TRUE,2,1110,FALSE,TRUE,TRUE,,,
Buff-武器-精3好斗的阿炮-全局攻击力,TRUE,FALSE,FALSE,FALSE,好斗的阿炮,FALSE,任意命中全队攻击力提升,TRUE,480,4,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE,TRUE,3,1110,FALSE,TRUE,TRUE,,,
Buff-武器-精4好斗的阿炮-全局攻击力,TRUE,FALSE,FALSE,FALSE,好斗的阿炮,FALSE,任意命中全队攻击力提升,TRUE,480,4,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE,TRUE,4,1110,FALSE,TRUE,TRUE,,,
Buff-武器-精5好斗的阿炮-全局攻击力,TRUE,FALSE,FALSE,FALSE,好斗的阿炮,FALSE,任意命中全队攻击力提升,TRUE,480,4,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE,TRUE,5,1110,FALSE,TRUE,TRUE,,,
Buff-武器-精1逍遥游球-全队暴击率,TRUE,TRUE,FALSE,FALSE,逍遥游球,FALSE,触发属性克制时,全队对目标暴击率提升,TRUE,720,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1,FALSE,FALSE,FALSE,,,
Buff-武器-精2逍遥游球-全队暴击率,TRUE,TRUE,FALSE,FALSE,逍遥游球,FALSE,触发属性克制时,全队对目标暴击率提升,TRUE,720,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1,FALSE,FALSE,FALSE,,,
Buff-武器-精3逍遥游球-全队暴击率,TRUE,TRUE,FALSE,FALSE,逍遥游球,FALSE,触发属性克制时,全队对目标暴击率提升,TRUE,720,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1,FALSE,FALSE,FALSE,,,
Buff-武器-精4逍遥游球-全队暴击率,TRUE,TRUE,FALSE,FALSE,逍遥游球,FALSE,触发属性克制时,全队对目标暴击率提升,TRUE,720,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1,FALSE,FALSE,FALSE,,,
Buff-武器-精5逍遥游球-全队暴击率,TRUE,TRUE,FALSE,FALSE,逍遥游球,FALSE,触发属性克制时,全队对目标暴击率提升,TRUE,720,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1,FALSE,FALSE,FALSE,,,
Buff-武器-精1残响Ⅰ型-全队冲击力,TRUE,FALSE,FALSE,FALSE,残响I型,FALSE,发动强化E时全队冲击力提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精2残响Ⅰ型-全队冲击力,TRUE,FALSE,FALSE,FALSE,残响I型,FALSE,发动强化E时全队冲击力提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精3残响Ⅰ型-全队冲击力,TRUE,FALSE,FALSE,FALSE,残响I型,FALSE,发动强化E时全队冲击力提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精4残响Ⅰ型-全队冲击力,TRUE,FALSE,FALSE,FALSE,残响I型,FALSE,发动强化E时全队冲击力提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精5残响Ⅰ型-全队冲击力,TRUE,FALSE,FALSE,FALSE,残响I型,FALSE,发动强化E时全队冲击力提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精1残响II型-全队掌控精通,TRUE,FALSE,FALSE,FALSE,残响II型,FALSE,发动强化E或连携时,全队掌控和精通提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精2残响II型-全队掌控精通,TRUE,FALSE,FALSE,FALSE,残响II型,FALSE,发动强化E或连携时,全队掌控和精通提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精3残响II型-全队掌控精通,TRUE,FALSE,FALSE,FALSE,残响II型,FALSE,发动强化E或连携时,全队掌控和精通提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精4残响II型-全队掌控精通,TRUE,FALSE,FALSE,FALSE,残响II型,FALSE,发动强化E或连携时,全队掌控和精通提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精5残响II型-全队掌控精通,TRUE,FALSE,FALSE,FALSE,残响II型,FALSE,发动强化E或连携时,全队掌控和精通提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精1残响III型-全队攻击力,TRUE,FALSE,FALSE,FALSE,残响III型,FALSE,发动连携或大招时,全队攻击力提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精2残响III型-全队攻击力,TRUE,FALSE,FALSE,FALSE,残响III型,FALSE,发动连携或大招时,全队攻击力提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精3残响III型-全队攻击力,TRUE,FALSE,FALSE,FALSE,残响III型,FALSE,发动连携或大招时,全队攻击力提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精4残响III型-全队攻击力,TRUE,FALSE,FALSE,FALSE,残响III型,FALSE,发动连携或大招时,全队攻击力提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1110,FALSE,FALSE,FALSE,,,
Buff-武器-精5残响III型-全队攻击力,TRUE,FALSE,FALSE,FALSE,残响III型,FALSE,发动连携或大招时,全队攻击力提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1110,FALSE,FALSE,FALSE,,,
Buff-角色-妮可-核心被动-减防,FALSE,TRUE,FALSE,FALSE,妮可,FALSE,强化普攻或能量场命中敌人时减防,TRUE,210,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1,FALSE,FALSE,TRUE,,,
Buff-角色-妮可-组队被动以太-增伤,FALSE,FALSE,TRUE,FALSE,妮可,FALSE,强化普攻或能量场命中敌人时以太增伤,TRUE,210,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1110,FALSE,FALSE,TRUE,,,
Buff-角色-凯撒-大招-命中护盾增加失衡值,FALSE,FALSE,FALSE,FALSE,凯撒,FALSE,大招命中护盾增加失衡值,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-凯撒-核心被动-攻击力,,,,,,FALSE,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-凯撒-组队被动-增伤,FALSE,TRUE,TRUE,FALSE,凯撒,FALSE,,TRUE,1800,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-耀佳音-咏叹华彩,FALSE,FALSE,FALSE,FALSE,耀嘉音,FALSE,咏叹华彩状态下,全队增伤、暴伤提高,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1110,FALSE,FALSE,TRUE,,,
Buff-角色-耀佳音-核心被动-攻击力,FALSE,FALSE,FALSE,FALSE,耀嘉音,FALSE,快支、连携、回避支援、招架支援入场的角色以及耀嘉音获得攻击力加成(通过特殊资源模块强制执行添加,所以不需要管怎么触发),TRUE,1800,1200,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,0,1110,FALSE,FALSE,TRUE,,,
Buff-角色-耀佳音-组队被动-触发器,FALSE,FALSE,TRUE,FALSE,耀嘉音,FALSE,组队被动的效果已经内置在了耀嘉音的特殊资源中,所以这是个无效果的空Buff,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-耀佳音-快支管理器-触发器,FALSE,FALSE,FALSE,FALSE,耀嘉音,FALSE,快支管理器的触发器,负责调用快支管理器尝试触发快支,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,0,1000,TRUE,FALSE,TRUE,,,
Buff-角色-耀佳音-震音管理器-触发器,FALSE,FALSE,FALSE,FALSE,耀嘉音,FALSE,震音管理器的触发器,负责调用震音管理器触发协同攻击。,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,TRUE,,,
Buff-角色-耀佳音-1画-减防,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-1画-无敌效果,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-2画-额外攻击力,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-4画-强攻特效触发器,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-4画-异常特效,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-4画-击破特效,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-6画-震音音簇暴击率提升,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-6画-重击暴击率提升,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-柏妮思-核心被动-燃油特调触发器,FALSE,FALSE,FALSE,FALSE,柏妮思,FALSE,燃点超过50点时进入燃油特调状态,用于余烬Dot的触发,TRUE,999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,FALSE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-柏妮思-核心被动-余烬增伤,FALSE,FALSE,TRUE,FALSE,柏妮思,FALSE,精通转余烬增伤,FALSE,5,30,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,TRUE,,,
Buff-角色-柏妮思-影画2-热意洞穿,FALSE,TRUE,FALSE,TRUE,柏妮思,FALSE,我方攻击时,本次攻击的穿透率提升,TRUE,360,5,1,,,,,,,,,,,,,,,,,,,,,
Buff-角色-柏妮思-影画4-招式暴击率,FALSE,FALSE,FALSE,TRUE,柏妮思,FALSE,"""强化特殊技""或""支援攻击""命中敌人时,暴击率提升",FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1灼心摇壶-回能,TRUE,FALSE,FALSE,FALSE,灼心摇壶,FALSE,位于后场时,装备者的能量自动回复提升,TRUE,999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,1,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精1灼心摇壶-增伤,TRUE,FALSE,FALSE,FALSE,灼心摇壶,FALSE,"""强化特殊技""或""支援攻击""命中敌人时增伤",TRUE,360,10,1,FALSE,FALSE,TRUE,FALSE,TRUE,18,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,1,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精1灼心摇壶-精通,TRUE,FALSE,FALSE,FALSE,灼心摇壶,FALSE,获得伤害提升效果时,若叠加层数大于等于5层,则装备者的异常精通额外提升,TRUE,360,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精2灼心摇壶-回能,TRUE,FALSE,FALSE,FALSE,灼心摇壶,FALSE,位于后场时,装备者的能量自动回复提升,TRUE,999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,2,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精2灼心摇壶-增伤,TRUE,FALSE,FALSE,FALSE,灼心摇壶,FALSE,"""强化特殊技""或""支援攻击""命中敌人时增伤",TRUE,360,10,1,FALSE,FALSE,TRUE,FALSE,TRUE,18,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,2,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精2灼心摇壶-精通,TRUE,FALSE,FALSE,FALSE,灼心摇壶,FALSE,获得伤害提升效果时,若叠加层数大于等于5层,则装备者的异常精通额外提升,TRUE,360,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精3灼心摇壶-回能,TRUE,FALSE,FALSE,FALSE,灼心摇壶,FALSE,位于后场时,装备者的能量自动回复提升,TRUE,999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,3,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精3灼心摇壶-增伤,TRUE,FALSE,FALSE,FALSE,灼心摇壶,FALSE,"""强化特殊技""或""支援攻击""命中敌人时增伤",TRUE,360,10,1,FALSE,FALSE,TRUE,FALSE,TRUE,18,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,3,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精3灼心摇壶-精通,TRUE,FALSE,FALSE,FALSE,灼心摇壶,FALSE,获得伤害提升效果时,若叠加层数大于等于5层,则装备者的异常精通额外提升,TRUE,360,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精4灼心摇壶-回能,TRUE,FALSE,FALSE,FALSE,灼心摇壶,FALSE,位于后场时,装备者的能量自动回复提升,TRUE,999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,4,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精4灼心摇壶-增伤,TRUE,FALSE,FALSE,FALSE,灼心摇壶,FALSE,"""强化特殊技""或""支援攻击""命中敌人时增伤",TRUE,360,10,1,FALSE,FALSE,TRUE,FALSE,TRUE,18,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,4,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精4灼心摇壶-精通,TRUE,FALSE,FALSE,FALSE,灼心摇壶,FALSE,获得伤害提升效果时,若叠加层数大于等于5层,则装备者的异常精通额外提升,TRUE,360,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精5灼心摇壶-回能,TRUE,FALSE,FALSE,FALSE,灼心摇壶,FALSE,位于后场时,装备者的能量自动回复提升,TRUE,999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,5,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精5灼心摇壶-增伤,TRUE,FALSE,FALSE,FALSE,灼心摇壶,FALSE,"""强化特殊技""或""支援攻击""命中敌人时增伤",TRUE,360,10,1,FALSE,FALSE,TRUE,FALSE,TRUE,18,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,5,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精5灼心摇壶-精通,TRUE,FALSE,FALSE,FALSE,灼心摇壶,FALSE,获得伤害提升效果时,若叠加层数大于等于5层,则装备者的异常精通额外提升,TRUE,360,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,TRUE,,,
Buff-角色-格莉丝-核心被动-电能,FALSE,FALSE,FALSE,FALSE,格莉丝,FALSE,造成物理伤害时,获得“电能”;到达上限后,发动“特殊技”或“强化特殊技“时,消耗使电属性异常积蓄值提升,FALSE,,8,1,FALSE,FALSE,TRUE,FALSE,TRUE,,TRUE,FALSE,,,,,FALSE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-格莉丝-组队被动-感电伤害,FALSE,TRUE,TRUE,FALSE,格莉丝,FALSE,“强化特殊技”命中敌人时,目标下次被施加”感电”效果时,该次”感电“伤害提升,FALSE,,2,1,FALSE,FALSE,TRUE,FALSE,TRUE,,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-格莉丝-影画2-双抗降低,FALSE,TRUE,FALSE,TRUE,格莉丝,FALSE,投掷手雷命中敌人时,目标的电属性伤害抗性降低、电属性异常积蓄抗性降低,FALSE,,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1,FALSE,FALSE,FALSE,,,
Buff-武器-精1嵌合编译器-攻击力,TRUE,FALSE,FALSE,FALSE,嵌合编译器,FALSE,攻击力提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1嵌合编译器-精通,TRUE,FALSE,FALSE,FALSE,嵌合编译器,FALSE,"发动“特殊技”或""强化特殊技""时,异常精通提升",TRUE,480,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精2嵌合编译器-攻击力,TRUE,FALSE,FALSE,FALSE,嵌合编译器,FALSE,攻击力提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2嵌合编译器-精通,TRUE,FALSE,FALSE,FALSE,嵌合编译器,FALSE,"发动“特殊技”或""强化特殊技""时,异常精通提升",TRUE,480,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精3嵌合编译器-攻击力,TRUE,FALSE,FALSE,FALSE,嵌合编译器,FALSE,攻击力提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3嵌合编译器-精通,TRUE,FALSE,FALSE,FALSE,嵌合编译器,FALSE,"发动“特殊技”或""强化特殊技""时,异常精通提升",TRUE,480,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精4嵌合编译器-攻击力,TRUE,FALSE,FALSE,FALSE,嵌合编译器,FALSE,攻击力提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4嵌合编译器-精通,TRUE,FALSE,FALSE,FALSE,嵌合编译器,FALSE,"发动“特殊技”或""强化特殊技""时,异常精通提升",TRUE,480,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精5嵌合编译器-攻击力,TRUE,FALSE,FALSE,FALSE,嵌合编译器,FALSE,攻击力提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5嵌合编译器-精通,TRUE,FALSE,FALSE,FALSE,嵌合编译器,FALSE,"发动“特殊技”或""强化特殊技""时,异常精通提升",TRUE,480,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精1防暴者Ⅵ型-暴击率,TRUE,FALSE,FALSE,FALSE,防暴者Ⅵ型,FALSE,暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2防暴者Ⅵ型-暴击率,TRUE,FALSE,FALSE,FALSE,防暴者Ⅵ型,FALSE,暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3防暴者Ⅵ型-暴击率,TRUE,FALSE,FALSE,FALSE,防暴者Ⅵ型,FALSE,暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4防暴者Ⅵ型-暴击率,TRUE,FALSE,FALSE,FALSE,防暴者Ⅵ型,FALSE,暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5防暴者Ⅵ型-暴击率,TRUE,FALSE,FALSE,FALSE,防暴者Ⅵ型,FALSE,暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1防暴者Ⅵ型-普攻增伤,TRUE,FALSE,FALSE,FALSE,防暴者Ⅵ型,FALSE,为装备者提供8层充能效果,最多叠加8层;[普通攻击]造成以太伤害时,消耗1层充能,使当前招式造成的伤害提升,TRUE,9999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,FALSE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2防暴者Ⅵ型-普攻增伤,TRUE,FALSE,FALSE,FALSE,防暴者Ⅵ型,FALSE,为装备者提供8层充能效果,最多叠加8层;[普通攻击]造成以太伤害时,消耗2层充能,使当前招式造成的伤害提升,TRUE,9999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,FALSE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3防暴者Ⅵ型-普攻增伤,TRUE,FALSE,FALSE,FALSE,防暴者Ⅵ型,FALSE,为装备者提供8层充能效果,最多叠加8层;[普通攻击]造成以太伤害时,消耗3层充能,使当前招式造成的伤害提升,TRUE,9999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,FALSE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4防暴者Ⅵ型-普攻增伤,TRUE,FALSE,FALSE,FALSE,防暴者Ⅵ型,FALSE,为装备者提供8层充能效果,最多叠加8层;[普通攻击]造成以太伤害时,消耗4层充能,使当前招式造成的伤害提升,TRUE,9999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,FALSE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5防暴者Ⅵ型-普攻增伤,TRUE,FALSE,FALSE,FALSE,防暴者Ⅵ型,FALSE,为装备者提供8层充能效果,最多叠加8层;[普通攻击]造成以太伤害时,消耗5层充能,使当前招式造成的伤害提升,TRUE,9999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,FALSE,5,1000,FALSE,FALSE,FALSE,,,
Buff-角色-朱鸢-核心被动-强化普攻增伤,FALSE,FALSE,FALSE,FALSE,朱鸢,FALSE,强化普攻增伤,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-朱鸢-核心被动-失衡普攻增伤,FALSE,FALSE,FALSE,FALSE,朱鸢,FALSE,强化普攻攻击失衡敌人时额外增伤,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-朱鸢-额外能力-暴击率,FALSE,FALSE,TRUE,FALSE,朱鸢,FALSE,发动[强化特殊技]、[连携技]或[终结技]时,自身暴击率提升,TRUE,6000,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-朱鸢-2画-强化普攻增伤,FALSE,FALSE,FALSE,TRUE,朱鸢,FALSE,强化普攻增伤,TRUE,300,5,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-角色-朱鸢-4画-无视以太抗,FALSE,FALSE,FALSE,TRUE,朱鸢,FALSE,强化普攻无视以太抗性,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-角色-朱鸢-6画-降低能耗,FALSE,FALSE,FALSE,TRUE,朱鸢,FALSE,强化特殊技降低能耗,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,6,1000,FALSE,FALSE,FALSE,,,
Buff-角色-格丽斯-4画-能量获取效率,FALSE,FALSE,FALSE,TRUE,格莉丝,FALSE,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-格丽斯-6画-特殊技增伤,FALSE,FALSE,FALSE,TRUE,格莉丝,FALSE,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-伊芙琳-核心被动-暴击率提升,FALSE,FALSE,FALSE,FALSE,伊芙琳,FALSE,拉线时加暴击率,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-伊芙琳-组队被动-连携技大招增伤,FALSE,FALSE,TRUE,FALSE,伊芙琳,FALSE,连携技、大招增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-伊芙琳-组队被动-连携技大招倍率增加,FALSE,FALSE,TRUE,FALSE,伊芙琳,FALSE,连携技、大招倍率提高,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-伊芙琳-1画-无视防御力,FALSE,FALSE,FALSE,TRUE,伊芙琳,FALSE,无视防御力-主目标,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-角色-伊芙琳-1画-无视防御力扩散,FALSE,FALSE,FALSE,TRUE,伊芙琳,FALSE,无视防御力-扩散,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1,FALSE,FALSE,FALSE,,,
Buff-角色-伊芙琳-1画-禁锢触发器,FALSE,TRUE,FALSE,TRUE,伊芙琳,FALSE,“禁锢”效果触发器,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,1,1,FALSE,FALSE,FALSE,,,
Buff-角色-伊芙琳-2画-局内大攻击,FALSE,FALSE,FALSE,TRUE,伊芙琳,FALSE,局内大攻击,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-角色-伊芙琳-2画-返还撩火触发器,FALSE,FALSE,FALSE,TRUE,伊芙琳,FALSE,25秒一次返还怒气,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1500,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-角色-伊芙琳-2画-连携技打断等级提升,FALSE,FALSE,FALSE,TRUE,伊芙琳,FALSE,重击的连携技打断等级提高,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-角色-伊芙琳-4画-护盾给暴伤,FALSE,FALSE,FALSE,TRUE,伊芙琳,FALSE,护盾存在时给暴伤,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,4,1000,FALSE,FALSE,FALSE,,,
Buff-角色-伊芙琳-4画-护盾,FALSE,FALSE,FALSE,TRUE,伊芙琳,FALSE,连携技、终结技给盾,TRUE,999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-角色-伊芙琳-6画-额外追击触发器,FALSE,FALSE,FALSE,TRUE,伊芙琳,FALSE,连携技、大招激活协同攻击触发器,TRUE,1200,16,16,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,6,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1心弦夜响-暴伤,TRUE,FALSE,FALSE,FALSE,心弦夜响,FALSE,提升爆伤,FALSE,0,1,2,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2心弦夜响-暴伤,TRUE,FALSE,FALSE,FALSE,心弦夜响,FALSE,提升爆伤,FALSE,0,1,2,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3心弦夜响-暴伤,TRUE,FALSE,FALSE,FALSE,心弦夜响,FALSE,提升爆伤,FALSE,0,1,2,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4心弦夜响-暴伤,TRUE,FALSE,FALSE,FALSE,心弦夜响,FALSE,提升爆伤,FALSE,0,1,2,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5心弦夜响-暴伤,TRUE,FALSE,FALSE,FALSE,心弦夜响,FALSE,提升爆伤,FALSE,0,1,2,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1心弦夜响-无视火抗,TRUE,FALSE,FALSE,FALSE,心弦夜响,FALSE,每层[心弦]会使装备者的[连携技]和[终结技]无视火抗,最多两层,FALSE,1800,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,"{""only_trigger_buff_level"":[5,6]}",,Heartstring_Nocturne_1
Buff-武器-精2心弦夜响-无视火抗,TRUE,FALSE,FALSE,FALSE,心弦夜响,FALSE,每层[心弦]会使装备者的[连携技]和[终结技]无视火抗,最多两层,FALSE,1800,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,"{""only_trigger_buff_level"":[5,6]}",,Heartstring_Nocturne_1
Buff-武器-精3心弦夜响-无视火抗,TRUE,FALSE,FALSE,FALSE,心弦夜响,FALSE,每层[心弦]会使装备者的[连携技]和[终结技]无视火抗,最多两层,FALSE,1800,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,"{""only_trigger_buff_level"":[5,6]}",,Heartstring_Nocturne_1
Buff-武器-精4心弦夜响-无视火抗,TRUE,FALSE,FALSE,FALSE,心弦夜响,FALSE,每层[心弦]会使装备者的[连携技]和[终结技]无视火抗,最多两层,FALSE,1800,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,"{""only_trigger_buff_level"":[5,6]}",,Heartstring_Nocturne_1
Buff-武器-精5心弦夜响-无视火抗,TRUE,FALSE,FALSE,FALSE,心弦夜响,FALSE,每层[心弦]会使装备者的[连携技]和[终结技]无视火抗,最多两层,FALSE,1800,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,"{""only_trigger_buff_level"":[5,6]}",,Heartstring_Nocturne_1
Buff-武器-精1心弦夜响-心弦触发器,TRUE,FALSE,FALSE,FALSE,废弃,FALSE,装备者进入接战状态、发动[连携技]、[终结技]时,获得1层[心弦],,TRUE,1800,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,Heartstring_Nocturne_1
Buff-武器-精2心弦夜响-心弦触发器,TRUE,FALSE,FALSE,FALSE,废弃,FALSE,装备者进入接战状态、发动[连携技]、[终结技]时,获得1层[心弦],,TRUE,1800,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,Heartstring_Nocturne_1
Buff-武器-精3心弦夜响-心弦触发器,TRUE,FALSE,FALSE,FALSE,废弃,FALSE,装备者进入接战状态、发动[连携技]、[终结技]时,获得1层[心弦],,TRUE,1800,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,Heartstring_Nocturne_1
Buff-武器-精4心弦夜响-心弦触发器,TRUE,FALSE,FALSE,FALSE,废弃,FALSE,装备者进入接战状态、发动[连携技]、[终结技]时,获得1层[心弦],,TRUE,1800,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,Heartstring_Nocturne_1
Buff-武器-精5心弦夜响-心弦触发器,TRUE,FALSE,FALSE,FALSE,废弃,FALSE,装备者进入接战状态、发动[连携技]、[终结技]时,获得1层[心弦],,TRUE,1800,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,Heartstring_Nocturne_1
Buff-角色-悠真-核心被动-特殊冲刺攻击暴击率,FALSE,FALSE,FALSE,FALSE,悠真,FALSE,特殊冲刺攻击暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-悠真-核心被动-特殊冲刺攻击暴伤,FALSE,FALSE,FALSE,FALSE,悠真,FALSE,特殊冲刺攻击命中且暴击时叠层,提升特殊冲刺攻击暴伤,TRUE,300,6,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE,TRUE,0,1000,TRUE,FALSE,FALSE,,,
Buff-角色-悠真-组队被动,FALSE,FALSE,TRUE,FALSE,悠真,FALSE,攻击失衡/属性异常敌人自身增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-悠真-2画-特殊冲刺攻击增伤,FALSE,FALSE,FALSE,TRUE,悠真,FALSE,发动连携/终结技时叠层,特殊冲刺攻击增伤,FALSE,0,7,7,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-悠真-6画-无视电抗,FALSE,FALSE,FALSE,TRUE,悠真,FALSE,攻击失衡/属性异常敌人无视电抗,TRUE,720,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1残心青囊-暴击率,TRUE,FALSE,FALSE,FALSE,残心青囊,FALSE,暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2残心青囊-暴击率,TRUE,FALSE,FALSE,FALSE,残心青囊,FALSE,暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3残心青囊-暴击率,TRUE,FALSE,FALSE,FALSE,残心青囊,FALSE,暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4残心青囊-暴击率,TRUE,FALSE,FALSE,FALSE,残心青囊,FALSE,暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5残心青囊-暴击率,TRUE,FALSE,FALSE,FALSE,残心青囊,FALSE,暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1残心青囊-电属性伤害,TRUE,FALSE,FALSE,FALSE,残心青囊,FALSE,冲刺攻击造成的电属性伤害提升,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2残心青囊-电属性伤害,TRUE,FALSE,FALSE,FALSE,残心青囊,FALSE,冲刺攻击造成的电属性伤害提升,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3残心青囊-电属性伤害,TRUE,FALSE,FALSE,FALSE,残心青囊,FALSE,冲刺攻击造成的电属性伤害提升,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4残心青囊-电属性伤害,TRUE,FALSE,FALSE,FALSE,残心青囊,FALSE,冲刺攻击造成的电属性伤害提升,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5残心青囊-电属性伤害,TRUE,FALSE,FALSE,FALSE,残心青囊,FALSE,冲刺攻击造成的电属性伤害提升,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1残心青囊-条件暴击率,TRUE,FALSE,FALSE,FALSE,残心青囊,FALSE,自身/队友施加属性异常效果或造成失衡时,提升暴击率,TRUE,900,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,Zanshin_Herb_Case_1
Buff-武器-精2残心青囊-条件暴击率,TRUE,FALSE,FALSE,FALSE,残心青囊,FALSE,自身/队友施加属性异常效果或造成失衡时,提升暴击率,TRUE,900,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,Zanshin_Herb_Case_1
Buff-武器-精3残心青囊-条件暴击率,TRUE,FALSE,FALSE,FALSE,残心青囊,FALSE,自身/队友施加属性异常效果或造成失衡时,提升暴击率,TRUE,900,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,Zanshin_Herb_Case_1
Buff-武器-精4残心青囊-条件暴击率,TRUE,FALSE,FALSE,FALSE,残心青囊,FALSE,自身/队友施加属性异常效果或造成失衡时,提升暴击率,TRUE,900,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,Zanshin_Herb_Case_1
Buff-武器-精5残心青囊-条件暴击率,TRUE,FALSE,FALSE,FALSE,残心青囊,FALSE,自身/队友施加属性异常效果或造成失衡时,提升暴击率,TRUE,900,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,Zanshin_Herb_Case_1
Buff-角色-艾莲-1画-暴击率,FALSE,FALSE,FALSE,TRUE,艾莲,FALSE,消耗冰豆提升暴击率,TRUE,900,6,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,TRUE,FALSE,,,
Buff-角色-艾莲-2画-强化特殊技额外爆伤,FALSE,FALSE,FALSE,TRUE,艾莲,FALSE,强化特殊技的爆伤提升,FALSE,0,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,2,1000,FALSE,FALSE,FALSE,,,
Buff-角色-艾莲-4画-能量回复,FALSE,FALSE,FALSE,TRUE,艾莲,FALSE,敌人冻结或失衡时回复能量,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,600,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,4,1000,FALSE,FALSE,TRUE,,,
Buff-角色-艾莲-6画-穿透率,FALSE,FALSE,FALSE,TRUE,艾莲,FALSE,发动[强化特殊技]、[连携技]或获得[快蓄]时,穿透率提升,TRUE,360,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,6,1000,FALSE,FALSE,FALSE,,,
Buff-角色-艾莲-6画-冲刺蓄力剪增伤,FALSE,FALSE,FALSE,TRUE,艾莲,FALSE,蓄力剪击命中敌人时,使当前招式造成的伤害提升,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,FALSE,6,1000,FALSE,FALSE,FALSE,,,
Buff-角色-11号-1画-回能,FALSE,FALSE,FALSE,TRUE,11号,FALSE,进入接战状态或换入前场时,能量不足则回复能量,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,1,1000,FALSE,FALSE,FALSE,,,
Buff-角色-11号-2画-火力镇压增伤,FALSE,FALSE,FALSE,TRUE,11号,FALSE,触发火力镇压时,普攻冲攻闪反增伤,TRUE,900,12,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-角色-11号-6画-火力镇压无视火抗,FALSE,FALSE,FALSE,TRUE,11号,FALSE,火力镇压无视火抗,FALSE,999999,8,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,6,1000,FALSE,FALSE,FALSE,,,
Buff-角色-艾莲-快蓄触发器,FALSE,FALSE,FALSE,FALSE,艾莲(测试),FALSE,艾莲获得快蓄效果,,,,,,,,,,0,TRUE,,,,,,,,,,,,,,
Buff-角色-柏妮思-组队被动-火积蓄加成,FALSE,FALSE,TRUE,FALSE,柏妮思,FALSE,重击、强化E、余烬的火积蓄效率提升,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-柏妮思-1画-余烬倍率提升,FALSE,FALSE,FALSE,TRUE,柏妮思,FALSE,根据攻击力,余烬倍率提升,FALSE,,,,,,,,,0,,,,,,,,1,1000,FALSE,FALSE,FALSE,,,
Buff-角色-柏妮思-1画-余烬火积蓄加成,FALSE,FALSE,FALSE,TRUE,柏妮思,FALSE,余烬的火属性异常积蓄提升25%,FALSE,,,,,,,,,0,,,,,,,,1,1000,FALSE,FALSE,FALSE,,,
Buff-角色-柏妮思-4画-双喷延时触发器,FALSE,FALSE,FALSE,TRUE,柏妮思,FALSE,双喷时间延长,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1家政员-后场时能量回复,TRUE,FALSE,FALSE,FALSE,家政员,FALSE,位于后场时自动回能,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2家政员-后场时能量回复,TRUE,FALSE,FALSE,FALSE,家政员,FALSE,位于后场时自动回能,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3家政员-后场时能量回复,TRUE,FALSE,FALSE,FALSE,家政员,FALSE,位于后场时自动回能,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4家政员-后场时能量回复,TRUE,FALSE,FALSE,FALSE,家政员,FALSE,位于后场时自动回能,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5家政员-后场时能量回复,TRUE,FALSE,FALSE,FALSE,家政员,FALSE,位于后场时自动回能,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1家政员-物理增伤,TRUE,FALSE,FALSE,FALSE,家政员,FALSE,强化特殊技命中时物理增伤,TRUE,60,15,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2家政员-物理增伤,TRUE,FALSE,FALSE,FALSE,家政员,FALSE,强化特殊技命中时物理增伤,TRUE,60,15,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3家政员-物理增伤,TRUE,FALSE,FALSE,FALSE,家政员,FALSE,强化特殊技命中时物理增伤,TRUE,60,15,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4家政员-物理增伤,TRUE,FALSE,FALSE,FALSE,家政员,FALSE,强化特殊技命中时物理增伤,TRUE,60,15,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5家政员-物理增伤,TRUE,FALSE,FALSE,FALSE,家政员,FALSE,强化特殊技命中时物理增伤,TRUE,60,15,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1旋钻机-赤轴-电属性增伤,TRUE,FALSE,FALSE,FALSE,旋钻机-赤轴,FALSE,发动[强化特殊技]或[连携技]时,[普通攻击]和[冲刺攻击]造成的电属性伤害提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,900,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2旋钻机-赤轴-电属性增伤,TRUE,FALSE,FALSE,FALSE,旋钻机-赤轴,FALSE,发动[强化特殊技]或[连携技]时,[普通攻击]和[冲刺攻击]造成的电属性伤害提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,900,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3旋钻机-赤轴-电属性增伤,TRUE,FALSE,FALSE,FALSE,旋钻机-赤轴,FALSE,发动[强化特殊技]或[连携技]时,[普通攻击]和[冲刺攻击]造成的电属性伤害提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,900,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4旋钻机-赤轴-电属性增伤,TRUE,FALSE,FALSE,FALSE,旋钻机-赤轴,FALSE,发动[强化特殊技]或[连携技]时,[普通攻击]和[冲刺攻击]造成的电属性伤害提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,900,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5旋钻机-赤轴-电属性增伤,TRUE,FALSE,FALSE,FALSE,旋钻机-赤轴,FALSE,发动[强化特殊技]或[连携技]时,[普通攻击]和[冲刺攻击]造成的电属性伤害提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,900,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1星徽引擎-攻击力提升,TRUE,FALSE,FALSE,FALSE,星徽引擎,FALSE,发动[闪避反击]或[快速支援]时,装备者的攻击力提升,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2星徽引擎-攻击力提升,TRUE,FALSE,FALSE,FALSE,星徽引擎,FALSE,发动[闪避反击]或[快速支援]时,装备者的攻击力提升,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3星徽引擎-攻击力提升,TRUE,FALSE,FALSE,FALSE,星徽引擎,FALSE,发动[闪避反击]或[快速支援]时,装备者的攻击力提升,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4星徽引擎-攻击力提升,TRUE,FALSE,FALSE,FALSE,星徽引擎,FALSE,发动[闪避反击]或[快速支援]时,装备者的攻击力提升,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5星徽引擎-攻击力提升,TRUE,FALSE,FALSE,FALSE,星徽引擎,FALSE,发动[闪避反击]或[快速支援]时,装备者的攻击力提升,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1星鎏金花信-攻击力提升,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2星鎏金花信-攻击力提升,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3星鎏金花信-攻击力提升,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4星鎏金花信-攻击力提升,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5星鎏金花信-攻击力提升,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1星鎏金花信-强化特殊技增伤,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2星鎏金花信-强化特殊技增伤,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3星鎏金花信-强化特殊技增伤,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4星鎏金花信-强化特殊技增伤,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5星鎏金花信-强化特殊技增伤,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1星强音热望-攻击力提升,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2星强音热望-攻击力提升,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3星强音热望-攻击力提升,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4星强音热望-攻击力提升,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5星强音热望-攻击力提升,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1星强音热望-额外攻击力提升,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2星强音热望-额外攻击力提升,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3星强音热望-额外攻击力提升,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4星强音热望-额外攻击力提升,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5星强音热望-额外攻击力提升,TRUE,FALSE,FALSE,FALSE,弃用,FALSE,弃用,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,5,1000,FALSE,FALSE,FALSE,,,
Buff-角色-扳机-核心被动-失衡易伤,FALSE,TRUE,FALSE,FALSE,扳机,FALSE,扳机的追加攻击命中敌人时提升失衡易伤,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1,FALSE,FALSE,TRUE,,,
Buff-角色-扳机-额外能力-追加攻击失衡值提升,FALSE,FALSE,TRUE,FALSE,扳机,FALSE,扳机追加攻击的失衡值提升,TRUE,999999,75,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,0,1000,FALSE,FALSE,TRUE,"{""only_label"": [""aftershock_attack""]}",,
Buff-角色-扳机-协同攻击-触发器,FALSE,FALSE,FALSE,FALSE,扳机,FALSE,触发普通协同、强化协同的触发器,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,0,1000,FALSE,FALSE,TRUE,,,
Buff-角色-扳机-协战状态-触发器,,,,,,,已废弃,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-扳机-1画-失衡易伤提升,FALSE,TRUE,FALSE,TRUE,扳机,FALSE,核心被动的失衡易伤进一步提升,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1,FALSE,FALSE,TRUE,,,
Buff-角色-扳机-1画-决意值提升触发器,,,,,,,已废弃,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-扳机-2画-猎眸,FALSE,FALSE,FALSE,TRUE,扳机,FALSE,发动追加攻击时触发,全队暴伤,TRUE,600,4,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1110,FALSE,FALSE,TRUE,,,
Buff-角色-扳机-4画-断离触发器,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-扳机-6画-破甲凶弹触发器,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-零号·安比-银星触发器,FALSE,TRUE,FALSE,FALSE,零号·安比,FALSE,安比的攻击会增加银星,,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-零号·安比-核心被动-增伤,FALSE,FALSE,FALSE,FALSE,零号·安比,FALSE,有银星时候增伤,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,TRUE,,,
Buff-角色-零号·安比-核心被动-受暴伤增加,FALSE,TRUE,FALSE,FALSE,零号·安比,FALSE,有银星时受到的追加攻击的暴击伤害增加,TRUE,999999,999999,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,0,1,FALSE,FALSE,TRUE,,,
Buff-角色-零号·安比-组队被动-暴击率提升,FALSE,FALSE,TRUE,FALSE,零号·安比,FALSE,暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-零号·安比-组队被动-全队对银星目标增伤,FALSE,FALSE,TRUE,FALSE,零号·安比,FALSE,全队追击对有银星的敌人增伤25%,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1110,FALSE,FALSE,FALSE,"{""only_label"": [""aftershock_attack""]}",,
Buff-角色-零号·安比-2画-暴击率提升,FALSE,FALSE,FALSE,TRUE,零号·安比,FALSE,暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-角色-零号·安比-4画-无视电抗,FALSE,FALSE,FALSE,TRUE,零号·安比,FALSE,命中有银星单位时无视电抗,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1牺牲洁纯-常驻暴伤,TRUE,FALSE,FALSE,FALSE,牺牲洁纯,FALSE,常驻暴伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2牺牲洁纯-常驻暴伤,TRUE,FALSE,FALSE,FALSE,牺牲洁纯,FALSE,常驻暴伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3牺牲洁纯-常驻暴伤,TRUE,FALSE,FALSE,FALSE,牺牲洁纯,FALSE,常驻暴伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4牺牲洁纯-常驻暴伤,TRUE,FALSE,FALSE,FALSE,牺牲洁纯,FALSE,常驻暴伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5牺牲洁纯-常驻暴伤,TRUE,FALSE,FALSE,FALSE,牺牲洁纯,FALSE,常驻暴伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1牺牲洁纯-触发暴伤,TRUE,FALSE,FALSE,FALSE,牺牲洁纯,FALSE,普攻、特殊技以及追加攻击触发独立叠层暴伤,FALSE,1800,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精2牺牲洁纯-触发暴伤,TRUE,FALSE,FALSE,FALSE,牺牲洁纯,FALSE,普攻、特殊技以及追加攻击触发独立叠层暴伤,FALSE,1800,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精3牺牲洁纯-触发暴伤,TRUE,FALSE,FALSE,FALSE,牺牲洁纯,FALSE,普攻、特殊技以及追加攻击触发独立叠层暴伤,FALSE,1800,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精4牺牲洁纯-触发暴伤,TRUE,FALSE,FALSE,FALSE,牺牲洁纯,FALSE,普攻、特殊技以及追加攻击触发独立叠层暴伤,FALSE,1800,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精5牺牲洁纯-触发暴伤,TRUE,FALSE,FALSE,FALSE,牺牲洁纯,FALSE,普攻、特殊技以及追加攻击触发独立叠层暴伤,FALSE,1800,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精1牺牲洁纯-满层电伤,TRUE,FALSE,FALSE,FALSE,牺牲洁纯,FALSE,触发暴伤Buff3层时,增加电伤,TRUE,999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2牺牲洁纯-满层电伤,TRUE,FALSE,FALSE,FALSE,牺牲洁纯,FALSE,触发暴伤Buff3层时,增加电伤,TRUE,999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3牺牲洁纯-满层电伤,TRUE,FALSE,FALSE,FALSE,牺牲洁纯,FALSE,触发暴伤Buff3层时,增加电伤,TRUE,999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4牺牲洁纯-满层电伤,TRUE,FALSE,FALSE,FALSE,牺牲洁纯,FALSE,触发暴伤Buff3层时,增加电伤,TRUE,999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5牺牲洁纯-满层电伤,TRUE,FALSE,FALSE,FALSE,牺牲洁纯,FALSE,触发暴伤Buff3层时,增加电伤,TRUE,999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,5,1000,FALSE,FALSE,FALSE,,,
Buff-驱动盘-如影相随-二件套,FALSE,FALSE,FALSE,FALSE,如影相随,FALSE,追加攻击和冲刺攻击造成伤害提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,TRUE,,,
Buff-驱动盘-如影相随-四件套,FALSE,FALSE,FALSE,FALSE,如影相随,FALSE,追加攻击、冲刺攻击命中敌人时提升局内攻击力,TRUE,900,3,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精1索魂影眸-减防,TRUE,TRUE,FALSE,FALSE,索魂影眸,FALSE,电伤追加攻击 使敌人减防,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1,FALSE,FALSE,TRUE,,,
Buff-武器-精2索魂影眸-减防,TRUE,TRUE,FALSE,FALSE,索魂影眸,FALSE,电伤追加攻击 使敌人减防,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1,FALSE,FALSE,TRUE,,,
Buff-武器-精3索魂影眸-减防,TRUE,TRUE,FALSE,FALSE,索魂影眸,FALSE,电伤追加攻击 使敌人减防,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1,FALSE,FALSE,TRUE,,,
Buff-武器-精4索魂影眸-减防,TRUE,TRUE,FALSE,FALSE,索魂影眸,FALSE,电伤追加攻击 使敌人减防,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1,FALSE,FALSE,TRUE,,,
Buff-武器-精5索魂影眸-减防,TRUE,TRUE,FALSE,FALSE,索魂影眸,FALSE,电伤追加攻击 使敌人减防,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1,FALSE,FALSE,TRUE,,,
Buff-武器-精1索魂影眸-魂锁,TRUE,FALSE,FALSE,FALSE,索魂影眸,FALSE,电伤追加攻击 使装备获得魂锁,提升局内冲击力,TRUE,720,3,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,TRUE,TRUE,,,
Buff-武器-精2索魂影眸-魂锁,TRUE,FALSE,FALSE,FALSE,索魂影眸,FALSE,电伤追加攻击 使装备获得魂锁,提升局内冲击力,TRUE,720,3,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,TRUE,TRUE,,,
Buff-武器-精3索魂影眸-魂锁,TRUE,FALSE,FALSE,FALSE,索魂影眸,FALSE,电伤追加攻击 使装备获得魂锁,提升局内冲击力,TRUE,720,3,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,TRUE,TRUE,,,
Buff-武器-精4索魂影眸-魂锁,TRUE,FALSE,FALSE,FALSE,索魂影眸,FALSE,电伤追加攻击 使装备获得魂锁,提升局内冲击力,TRUE,720,3,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,TRUE,TRUE,,,
Buff-武器-精5索魂影眸-魂锁,TRUE,FALSE,FALSE,FALSE,索魂影眸,FALSE,电伤追加攻击 使装备获得魂锁,提升局内冲击力,TRUE,720,3,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,TRUE,TRUE,,,
Buff-武器-精1索魂影眸-冲击力,TRUE,FALSE,FALSE,FALSE,索魂影眸,FALSE,魂锁满层时额外提升冲击力,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,1,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精2索魂影眸-冲击力,TRUE,FALSE,FALSE,FALSE,索魂影眸,FALSE,魂锁满层时额外提升冲击力,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,2,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精3索魂影眸-冲击力,TRUE,FALSE,FALSE,FALSE,索魂影眸,FALSE,魂锁满层时额外提升冲击力,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,3,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精4索魂影眸-冲击力,TRUE,FALSE,FALSE,FALSE,索魂影眸,FALSE,魂锁满层时额外提升冲击力,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,4,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精5索魂影眸-冲击力,TRUE,FALSE,FALSE,FALSE,索魂影眸,FALSE,魂锁满层时额外提升冲击力,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,5,1000,FALSE,FALSE,TRUE,,,
Buff-角色-柳-架势-上弦,FALSE,FALSE,FALSE,FALSE,柳,FALSE,电伤提升,抗打断等级提升,TRUE,480,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-柳-架势-下弦,FALSE,FALSE,FALSE,FALSE,柳,FALSE,穿透率提升,普攻打断等级提升,TRUE,480,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-柳-森罗万象,,,,,,,已废弃,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-柳-核心被动-紊乱倍率提升,FALSE,TRUE,FALSE,FALSE,柳,FALSE,发动强化E后,全队紊乱倍率提高250%,TRUE,900,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-柳-核心被动-电伤增幅,FALSE,FALSE,FALSE,FALSE,柳,FALSE,强化E命中时,电伤提高20%,TRUE,900,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-柳-额外能力-积蓄效率,FALSE,FALSE,TRUE,FALSE,柳,FALSE,架势切换后,普攻的积蓄值提高,TRUE,480,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-柳-极性紊乱触发器,FALSE,FALSE,FALSE,FALSE,柳,FALSE,柳极性紊乱的触发器,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,0,1000,TRUE,FALSE,TRUE,,,
Buff-角色-柳-1画-洞悉,FALSE,FALSE,FALSE,TRUE,柳,FALSE,队伍中任意角色触发属性异常时叠层,TRUE,900,3,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-角色-柳-1画-精通增幅,FALSE,FALSE,FALSE,TRUE,柳,FALSE,1层洞悉触发,精通提升,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,1,1000,FALSE,FALSE,FALSE,,,
Buff-角色-柳-2画-积蓄效率,FALSE,FALSE,FALSE,TRUE,柳,FALSE,快速突刺的积蓄值提升20%,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-角色-柳-4画-识破,FALSE,TRUE,FALSE,TRUE,柳,FALSE,月城柳造成属性异常伤害时触发,提升穿透率,TRUE,900,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1,FALSE,FALSE,FALSE,,,
Buff-角色-柳-6画-特殊技伤害提升,FALSE,FALSE,FALSE,TRUE,柳,FALSE,森罗万象期间强化E伤害提升,,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,6,1000,FALSE,FALSE,FALSE,,,
Buff-角色-简-狂热状态触发器,FALSE,FALSE,FALSE,FALSE,简,FALSE,狂热状态触发器,本身无效果,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-简-狂热-物理积蓄效率提升,FALSE,FALSE,FALSE,FALSE,简,FALSE,狂热状态下的积蓄效率,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-简-狂热-额外精通转攻击力,FALSE,FALSE,FALSE,FALSE,简,FALSE,狂热状态下的精通转攻击力,TRUE,999999,300,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,FALSE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-简-核心被动-啮咬触发器,FALSE,TRUE,FALSE,FALSE,简,FALSE,核心被动啮咬触发器,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-简-核心被动-啮咬-强击暴击率提升,FALSE,TRUE,FALSE,FALSE,简,FALSE,啮咬激活期间,强击暴击率提升,TRUE,600,100,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,FALSE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-简-核心被动-啮咬-强击暴击伤害提升,FALSE,TRUE,FALSE,FALSE,简,FALSE,啮咬激活期间,强击暴伤提升,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-简-额外能力-物理异常积蓄效率提升,FALSE,FALSE,TRUE,FALSE,简,FALSE,物理异常积蓄效率提升20%,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-简-额外能力-物理异常积蓄效率额外提升,FALSE,FALSE,TRUE,FALSE,简,FALSE,简对处于异常状态下的敌人的积蓄效率额外提升15%,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-简-1画-狂热物理异常积蓄效率额外提升,FALSE,FALSE,FALSE,TRUE,简,FALSE,狂热状态下的积蓄效率进一步提升,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,1,1000,FALSE,FALSE,FALSE,,,
Buff-角色-简-1画-精通转增伤,FALSE,TRUE,FALSE,TRUE,简,FALSE,狂热状态下的精通转攻击力,TRUE,999999,30,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,FALSE,1,1,FALSE,FALSE,FALSE,,,
Buff-角色-简-2画-啮咬-强击无视防御与暴击伤害提升,FALSE,TRUE,FALSE,TRUE,简,FALSE,啮咬激活期间,强击无视防御力,且暴伤提升,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,2,1,FALSE,FALSE,FALSE,,,
Buff-角色-简-2画-啮咬-攻击无视防御,FALSE,FALSE,FALSE,TRUE,简,FALSE,啮咬激活期间,简的攻击无视目标防御力,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,2,1000,FALSE,FALSE,FALSE,,,
Buff-角色-简-4画-全队异常伤害提升,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-简-6画-双暴提升,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-简-6画-双暴提升,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-简-6画-额外攻击触发器,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Buff-角色-薇薇安-协同攻击触发器,FALSE,FALSE,FALSE,FALSE,薇薇安,FALSE,控制落羽生花的触发器,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,0,1000,TRUE,FALSE,TRUE,,,
Buff-角色-薇薇安-羽毛结算触发器,FALSE,FALSE,FALSE,FALSE,薇薇安,FALSE,控制羽毛数量增减的触发器,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,0,1000,FALSE,FALSE,TRUE,,,
Buff-角色-薇薇安-核心被动触发器,FALSE,FALSE,FALSE,FALSE,薇薇安,FALSE,核心被动之复制异常效果,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,0,1000,FALSE,FALSE,TRUE,,,
Buff-角色-薇薇安-预言触发器,FALSE,FALSE,FALSE,FALSE,薇薇安,FALSE,添加核心被动Dot的触发器,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,0,1000,FALSE,FALSE,TRUE,,,
Buff-角色-薇薇安-额外能力-协同攻击触发器,FALSE,FALSE,TRUE,FALSE,薇薇安,FALSE,队友触发属性异常时,协同释放一次落雨生花,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,0,1000,TRUE,FALSE,TRUE,,,
Buff-角色-薇薇安-额外能力-全队侵蚀伤害增加,FALSE,TRUE,TRUE,FALSE,薇薇安,FALSE,全队造成的侵蚀伤害提高12%,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-薇薇安-额外能力-侵蚀紊乱伤害提升,FALSE,TRUE,TRUE,FALSE,薇薇安,FALSE,侵蚀状态被结算的紊乱伤害提升12%,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1,FALSE,FALSE,FALSE,"{""specified_element_type"":[4,6]}",,
Buff-角色-薇薇安-1画-全属性异常和紊乱伤害提升,FALSE,TRUE,FALSE,TRUE,薇薇安,FALSE,处于薇薇安预言下的目标受到的所有属性异常伤害和紊乱伤害提高16%,FALSE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1,FALSE,FALSE,TRUE,,,
Buff-角色-薇薇安-2画-以太积蓄效率提升,FALSE,FALSE,FALSE,TRUE,薇薇安,FALSE,以太异常积蓄效率提升25%,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-角色-薇薇安-2画-异放全属性抗性穿透,FALSE,TRUE,FALSE,TRUE,薇薇安,FALSE,专属于异放的15%全属性伤害抗性穿透,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1,FALSE,FALSE,TRUE,"{""only_anomaly"":[""Abloom""]}",,
Buff-角色-薇薇安-4画-悬落与落羽生花必暴,FALSE,FALSE,FALSE,TRUE,薇薇安,FALSE,普通攻击:悬落、落羽生花必暴,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,"{""only_skill"":[""1331_SNA_2"",""1331_CoAttack_A""]}",,
Buff-角色-薇薇安-4画-局内攻击力增幅,FALSE,FALSE,FALSE,TRUE,薇薇安,FALSE,悬落、落雨生花命中后增加12%局内攻击力,TRUE,720,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,TRUE,,,
Buff-角色-薇薇安-6画-以太伤害增加,FALSE,FALSE,FALSE,TRUE,薇薇安,FALSE,以太伤害增加40%,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,6,1000,FALSE,FALSE,FALSE,,,
Buff-驱动盘-法厄同之歌-四件套-以太伤害提高,FALSE,FALSE,FALSE,FALSE,法厄同之歌,FALSE,队友发动强化E,使装备者以太伤害增加25%,TRUE,480,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,TRUE,,,
Buff-驱动盘-法厄同之歌-四件套-精通增幅,FALSE,FALSE,FALSE,FALSE,法厄同之歌,FALSE,队伍中任意角色发动强化E,装备者获得45精通,TRUE,480,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精1飞鸟星梦-属性异常积蓄效率,TRUE,FALSE,FALSE,FALSE,飞鸟星梦,FALSE,属性异常积蓄效率增加,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2飞鸟星梦-属性异常积蓄效率,TRUE,FALSE,FALSE,FALSE,飞鸟星梦,FALSE,属性异常积蓄效率增加,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3飞鸟星梦-属性异常积蓄效率,TRUE,FALSE,FALSE,FALSE,飞鸟星梦,FALSE,属性异常积蓄效率增加,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4飞鸟星梦-属性异常积蓄效率,TRUE,FALSE,FALSE,FALSE,飞鸟星梦,FALSE,属性异常积蓄效率增加,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5飞鸟星梦-属性异常积蓄效率,TRUE,FALSE,FALSE,FALSE,飞鸟星梦,FALSE,属性异常积蓄效率增加,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1飞鸟星梦-精通增幅,TRUE,FALSE,FALSE,FALSE,飞鸟星梦,FALSE,装备者造成以太伤害时获得精通增幅,TRUE,300,6,1,FALSE,FALSE,TRUE,FALSE,TRUE,30,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,TRUE,FALSE,TRUE,,,
Buff-武器-精2飞鸟星梦-精通增幅,TRUE,FALSE,FALSE,FALSE,飞鸟星梦,FALSE,装备者造成以太伤害时获得精通增幅,TRUE,300,6,1,FALSE,FALSE,TRUE,FALSE,TRUE,30,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,TRUE,FALSE,TRUE,,,
Buff-武器-精3飞鸟星梦-精通增幅,TRUE,FALSE,FALSE,FALSE,飞鸟星梦,FALSE,装备者造成以太伤害时获得精通增幅,TRUE,300,6,1,FALSE,FALSE,TRUE,FALSE,TRUE,30,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,TRUE,FALSE,TRUE,,,
Buff-武器-精4飞鸟星梦-精通增幅,TRUE,FALSE,FALSE,FALSE,飞鸟星梦,FALSE,装备者造成以太伤害时获得精通增幅,TRUE,300,6,1,FALSE,FALSE,TRUE,FALSE,TRUE,30,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,TRUE,FALSE,TRUE,,,
Buff-武器-精5飞鸟星梦-精通增幅,TRUE,FALSE,FALSE,FALSE,飞鸟星梦,FALSE,装备者造成以太伤害时获得精通增幅,TRUE,300,6,1,FALSE,FALSE,TRUE,FALSE,TRUE,30,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,TRUE,FALSE,TRUE,,,
Buff-武器-精1时流贤者-电积蓄效率提升,TRUE,FALSE,FALSE,FALSE,时流贤者,FALSE,装备者电属性异常积蓄提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2时流贤者-电积蓄效率提升,TRUE,FALSE,FALSE,FALSE,时流贤者,FALSE,装备者电属性异常积蓄提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3时流贤者-电积蓄效率提升,TRUE,FALSE,FALSE,FALSE,时流贤者,FALSE,装备者电属性异常积蓄提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4时流贤者-电积蓄效率提升,TRUE,FALSE,FALSE,FALSE,时流贤者,FALSE,装备者电属性异常积蓄提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5时流贤者-电积蓄效率提升,TRUE,FALSE,FALSE,FALSE,时流贤者,FALSE,装备者电属性异常积蓄提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1时流贤者-精通提升,TRUE,FALSE,FALSE,FALSE,时流贤者,FALSE,E或强化E命中处于属性异常状态下的敌人时,精通提升,TRUE,900,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2时流贤者-精通提升,TRUE,FALSE,FALSE,FALSE,时流贤者,FALSE,E或强化E命中处于属性异常状态下的敌人时,精通提升,TRUE,900,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3时流贤者-精通提升,TRUE,FALSE,FALSE,FALSE,时流贤者,FALSE,E或强化E命中处于属性异常状态下的敌人时,精通提升,TRUE,900,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4时流贤者-精通提升,TRUE,FALSE,FALSE,FALSE,时流贤者,FALSE,E或强化E命中处于属性异常状态下的敌人时,精通提升,TRUE,900,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5时流贤者-精通提升,TRUE,FALSE,FALSE,FALSE,时流贤者,FALSE,E或强化E命中处于属性异常状态下的敌人时,精通提升,TRUE,900,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1时流贤者-装备者紊乱伤害提升,TRUE,FALSE,FALSE,FALSE,时流贤者,FALSE,装备者精通大于375时,装备者造成的紊乱伤害提升,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,1,1000,FALSE,FALSE,FALSE,"{""only_active_by"":[""self""], ""only_anomaly"":[""Disorder"", ""PolarityDisorder""]}",,
Buff-武器-精2时流贤者-装备者紊乱伤害提升,TRUE,FALSE,FALSE,FALSE,时流贤者,FALSE,装备者精通大于375时,装备者造成的紊乱伤害提升,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,2,1000,FALSE,FALSE,FALSE,"{""only_active_by"":[""self""], ""only_anomaly"":[""Disorder"", ""PolarityDisorder""]}",,
Buff-武器-精3时流贤者-装备者紊乱伤害提升,TRUE,FALSE,FALSE,FALSE,时流贤者,FALSE,装备者精通大于375时,装备者造成的紊乱伤害提升,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,3,1000,FALSE,FALSE,FALSE,"{""only_active_by"":[""self""], ""only_anomaly"":[""Disorder"", ""PolarityDisorder""]}",,
Buff-武器-精4时流贤者-装备者紊乱伤害提升,TRUE,FALSE,FALSE,FALSE,时流贤者,FALSE,装备者精通大于375时,装备者造成的紊乱伤害提升,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,4,1000,FALSE,FALSE,FALSE,"{""only_active_by"":[""self""], ""only_anomaly"":[""Disorder"", ""PolarityDisorder""]}",,
Buff-武器-精5时流贤者-装备者紊乱伤害提升,TRUE,FALSE,FALSE,FALSE,时流贤者,FALSE,装备者精通大于375时,装备者造成的紊乱伤害提升,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,5,1000,FALSE,FALSE,FALSE,"{""only_active_by"":[""self""], ""only_anomaly"":[""Disorder"", ""PolarityDisorder""]}",,
Buff-武器-精1淬锋钳刺-猎意,TRUE,FALSE,FALSE,FALSE,淬锋钳刺,FALSE,冲刺攻击使物理伤害提高,进场或极限闪避直接满层,TRUE,600,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,30,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2淬锋钳刺-猎意,TRUE,FALSE,FALSE,FALSE,淬锋钳刺,FALSE,冲刺攻击使物理伤害提高,进场或极限闪避直接满层,TRUE,600,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,30,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3淬锋钳刺-猎意,TRUE,FALSE,FALSE,FALSE,淬锋钳刺,FALSE,冲刺攻击使物理伤害提高,进场或极限闪避直接满层,TRUE,600,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,30,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4淬锋钳刺-猎意,TRUE,FALSE,FALSE,FALSE,淬锋钳刺,FALSE,冲刺攻击使物理伤害提高,进场或极限闪避直接满层,TRUE,600,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,30,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5淬锋钳刺-猎意,TRUE,FALSE,FALSE,FALSE,淬锋钳刺,FALSE,冲刺攻击使物理伤害提高,进场或极限闪避直接满层,TRUE,600,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,30,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1淬锋钳刺-属性异常积蓄效率提升,TRUE,FALSE,FALSE,FALSE,淬锋钳刺,FALSE,猎意叠满时,装备者属性异常积蓄效率提升,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2淬锋钳刺-属性异常积蓄效率提升,TRUE,FALSE,FALSE,FALSE,淬锋钳刺,FALSE,猎意叠满时,装备者属性异常积蓄效率提升,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3淬锋钳刺-属性异常积蓄效率提升,TRUE,FALSE,FALSE,FALSE,淬锋钳刺,FALSE,猎意叠满时,装备者属性异常积蓄效率提升,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4淬锋钳刺-属性异常积蓄效率提升,TRUE,FALSE,FALSE,FALSE,淬锋钳刺,FALSE,猎意叠满时,装备者属性异常积蓄效率提升,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5淬锋钳刺-属性异常积蓄效率提升,TRUE,FALSE,FALSE,FALSE,淬锋钳刺,FALSE,猎意叠满时,装备者属性异常积蓄效率提升,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1玲珑妆匣-回能,TRUE,FALSE,FALSE,FALSE,玲珑妆匣,FALSE,任意角色通过切人技入场时给装备者回能,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,300,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精2玲珑妆匣-回能,TRUE,FALSE,FALSE,FALSE,玲珑妆匣,FALSE,任意角色通过切人技入场时给装备者回能,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,300,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精3玲珑妆匣-回能,TRUE,FALSE,FALSE,FALSE,玲珑妆匣,FALSE,任意角色通过切人技入场时给装备者回能,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,300,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精4玲珑妆匣-回能,TRUE,FALSE,FALSE,FALSE,玲珑妆匣,FALSE,任意角色通过切人技入场时给装备者回能,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,300,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精5玲珑妆匣-回能,TRUE,FALSE,FALSE,FALSE,玲珑妆匣,FALSE,任意角色通过切人技入场时给装备者回能,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,300,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精1玲珑妆匣-全队增伤,TRUE,FALSE,FALSE,FALSE,玲珑妆匣,FALSE,装备者消耗25点或以上能量时触发全队增伤,TRUE,1200,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1110,FALSE,FALSE,TRUE,,,
Buff-武器-精2玲珑妆匣-全队增伤,TRUE,FALSE,FALSE,FALSE,玲珑妆匣,FALSE,装备者消耗25点或以上能量时触发全队增伤,TRUE,1200,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1110,FALSE,FALSE,TRUE,,,
Buff-武器-精3玲珑妆匣-全队增伤,TRUE,FALSE,FALSE,FALSE,玲珑妆匣,FALSE,装备者消耗25点或以上能量时触发全队增伤,TRUE,1200,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1110,FALSE,FALSE,TRUE,,,
Buff-武器-精4玲珑妆匣-全队增伤,TRUE,FALSE,FALSE,FALSE,玲珑妆匣,FALSE,装备者消耗25点或以上能量时触发全队增伤,TRUE,1200,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1110,FALSE,FALSE,TRUE,,,
Buff-武器-精5玲珑妆匣-全队增伤,TRUE,FALSE,FALSE,FALSE,玲珑妆匣,FALSE,装备者消耗25点或以上能量时触发全队增伤,TRUE,1200,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1110,FALSE,FALSE,TRUE,,,
Buff-武器-精1雨林饕客-局内攻击力,TRUE,FALSE,FALSE,FALSE,雨林饕客,FALSE,每消耗10点能量,获得1层局内攻击,TRUE,600,10,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精2雨林饕客-局内攻击力,TRUE,FALSE,FALSE,FALSE,雨林饕客,FALSE,每消耗10点能量,获得1层局内攻击,TRUE,600,10,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精3雨林饕客-局内攻击力,TRUE,FALSE,FALSE,FALSE,雨林饕客,FALSE,每消耗10点能量,获得1层局内攻击,TRUE,600,10,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精4雨林饕客-局内攻击力,TRUE,FALSE,FALSE,FALSE,雨林饕客,FALSE,每消耗10点能量,获得1层局内攻击,TRUE,600,10,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精5雨林饕客-局内攻击力,TRUE,FALSE,FALSE,FALSE,雨林饕客,FALSE,每消耗10点能量,获得1层局内攻击,TRUE,600,10,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精1双生泣星-精通增幅,TRUE,FALSE,FALSE,FALSE,双生泣星,FALSE,意角色对敌人施加属性异常效果时叠层,怪物从失衡状态下恢复时清空,TRUE,999999,4,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,FALSE,1,1000,TRUE,TRUE,TRUE,,,
Buff-武器-精2双生泣星-精通增幅,TRUE,FALSE,FALSE,FALSE,双生泣星,FALSE,意角色对敌人施加属性异常效果时叠层,怪物从失衡状态下恢复时清空,TRUE,999999,4,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,FALSE,2,1000,TRUE,TRUE,TRUE,,,
Buff-武器-精3双生泣星-精通增幅,TRUE,FALSE,FALSE,FALSE,双生泣星,FALSE,意角色对敌人施加属性异常效果时叠层,怪物从失衡状态下恢复时清空,TRUE,999999,4,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,FALSE,3,1000,TRUE,TRUE,TRUE,,,
Buff-武器-精4双生泣星-精通增幅,TRUE,FALSE,FALSE,FALSE,双生泣星,FALSE,意角色对敌人施加属性异常效果时叠层,怪物从失衡状态下恢复时清空,TRUE,999999,4,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,FALSE,4,1000,TRUE,TRUE,TRUE,,,
Buff-武器-精5双生泣星-精通增幅,TRUE,FALSE,FALSE,FALSE,双生泣星,FALSE,意角色对敌人施加属性异常效果时叠层,怪物从失衡状态下恢复时清空,TRUE,999999,4,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,FALSE,5,1000,TRUE,TRUE,TRUE,,,
Buff-武器-精1触电唇彩-攻击力与增伤,TRUE,FALSE,FALSE,FALSE,触电唇彩,FALSE,当场上存在异常状态敌人时触发,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,1,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精2触电唇彩-攻击力与增伤,TRUE,FALSE,FALSE,FALSE,触电唇彩,FALSE,当场上存在异常状态敌人时触发,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,2,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精3触电唇彩-攻击力与增伤,TRUE,FALSE,FALSE,FALSE,触电唇彩,FALSE,当场上存在异常状态敌人时触发,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,3,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精4触电唇彩-攻击力与增伤,TRUE,FALSE,FALSE,FALSE,触电唇彩,FALSE,当场上存在异常状态敌人时触发,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,4,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精5触电唇彩-攻击力与增伤,TRUE,FALSE,FALSE,FALSE,触电唇彩,FALSE,当场上存在异常状态敌人时触发,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,5,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精1轰鸣座驾-触发器,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾触发器,FALSE,1,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,18,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2轰鸣座驾-触发器,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾触发器,FALSE,1,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,18,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3轰鸣座驾-触发器,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾触发器,FALSE,1,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,18,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4轰鸣座驾-触发器,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾触发器,FALSE,1,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,18,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5轰鸣座驾-触发器,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾触发器,FALSE,1,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,18,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1轰鸣座驾-攻击力,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾特效1:攻击力增幅,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2轰鸣座驾-攻击力,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾特效1:攻击力增幅,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3轰鸣座驾-攻击力,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾特效1:攻击力增幅,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4轰鸣座驾-攻击力,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾特效1:攻击力增幅,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5轰鸣座驾-攻击力,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾特效1:攻击力增幅,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1轰鸣座驾-精通提升,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾特效2:精通提升,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2轰鸣座驾-精通提升,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾特效2:精通提升,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3轰鸣座驾-精通提升,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾特效2:精通提升,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4轰鸣座驾-精通提升,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾特效2:精通提升,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5轰鸣座驾-精通提升,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾特效2:精通提升,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1轰鸣座驾-属性异常积蓄,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾特效3:积蓄效率提升,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2轰鸣座驾-属性异常积蓄,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾特效3:积蓄效率提升,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3轰鸣座驾-属性异常积蓄,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾特效3:积蓄效率提升,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4轰鸣座驾-属性异常积蓄,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾特效3:积蓄效率提升,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5轰鸣座驾-属性异常积蓄,TRUE,FALSE,FALSE,FALSE,轰鸣座驾,FALSE,轰鸣座驾特效3:积蓄效率提升,TRUE,300,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1「电磁暴」-壹式-异常掌控,TRUE,FALSE,FALSE,FALSE,「电磁暴」-壹式,FALSE,电磁暴一式:异常掌控,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,1200,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精2「电磁暴」-壹式-异常掌控,TRUE,FALSE,FALSE,FALSE,「电磁暴」-壹式,FALSE,电磁暴一式:异常掌控,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,1200,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精3「电磁暴」-壹式-异常掌控,TRUE,FALSE,FALSE,FALSE,「电磁暴」-壹式,FALSE,电磁暴一式:异常掌控,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,1200,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精4「电磁暴」-壹式-异常掌控,TRUE,FALSE,FALSE,FALSE,「电磁暴」-壹式,FALSE,电磁暴一式:异常掌控,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,1200,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精5「电磁暴」-壹式-异常掌控,TRUE,FALSE,FALSE,FALSE,「电磁暴」-壹式,FALSE,电磁暴一式:异常掌控,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,1200,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精1「电磁暴」-贰式-异常精通,TRUE,FALSE,FALSE,FALSE,「电磁暴」-贰式,FALSE,电磁暴二式:异常精通,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,1200,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精2「电磁暴」-贰式-异常精通,TRUE,FALSE,FALSE,FALSE,「电磁暴」-贰式,FALSE,电磁暴二式:异常精通,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,1200,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精3「电磁暴」-贰式-异常精通,TRUE,FALSE,FALSE,FALSE,「电磁暴」-贰式,FALSE,电磁暴二式:异常精通,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,1200,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精4「电磁暴」-贰式-异常精通,TRUE,FALSE,FALSE,FALSE,「电磁暴」-贰式,FALSE,电磁暴二式:异常精通,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,1200,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精5「电磁暴」-贰式-异常精通,TRUE,FALSE,FALSE,FALSE,「电磁暴」-贰式,FALSE,电磁暴二式:异常精通,TRUE,600,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,1200,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精1「电磁暴」-叁式-回能,TRUE,FALSE,FALSE,FALSE,「电磁暴」-叁式,FALSE,电磁暴三式:回能,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,720,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,1,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精2「电磁暴」-叁式-回能,TRUE,FALSE,FALSE,FALSE,「电磁暴」-叁式,FALSE,电磁暴三式:回能,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,720,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,2,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精3「电磁暴」-叁式-回能,TRUE,FALSE,FALSE,FALSE,「电磁暴」-叁式,FALSE,电磁暴三式:回能,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,720,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,3,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精4「电磁暴」-叁式-回能,TRUE,FALSE,FALSE,FALSE,「电磁暴」-叁式,FALSE,电磁暴三式:回能,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,720,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,4,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精5「电磁暴」-叁式-回能,TRUE,FALSE,FALSE,FALSE,「电磁暴」-叁式,FALSE,电磁暴三式:回能,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,720,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,5,1000,FALSE,FALSE,TRUE,,,
Buff-角色-雨果-核心被动-暗渊回响,FALSE,FALSE,FALSE,FALSE,雨果,FALSE,雨果核心被动:双暴,TRUE,360,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-雨果-核心被动-单击破攻击力,FALSE,FALSE,FALSE,FALSE,雨果,FALSE,雨果核心被动:单击破加攻击力,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-雨果-核心被动-双击破攻击力,FALSE,FALSE,FALSE,FALSE,雨果,FALSE,雨果核心被动:双击破加攻击力,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-雨果-决算触发器,FALSE,FALSE,FALSE,FALSE,雨果,FALSE,抛出决算、结算失衡值的核心触发器,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-雨果-决算倍率增幅,FALSE,FALSE,FALSE,FALSE,雨果,FALSE,决定决算倍率的Buff载体,TRUE,1,5000,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,"{""only_label"":[""totalized""]}",,
Buff-角色-雨果-核心被动-强化E失衡值提升,FALSE,FALSE,FALSE,FALSE,雨果,FALSE,雨果强化E对非失衡目标的失衡值提升,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-雨果-额外能力-连携技伤害提升,FALSE,FALSE,TRUE,FALSE,雨果,FALSE,额外能力:常驻连携技增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-雨果-额外能力-连携技对普通敌人伤害提升,FALSE,FALSE,TRUE,FALSE,雨果,FALSE,额外能力:连携技对普通敌人额外增伤,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-雨果-额外能力-决算招式增伤,FALSE,FALSE,TRUE,FALSE,雨果,FALSE,额外能力:触发决算的招式额外增伤40%,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,"{""only_label"":[""totalized""]}",,
Buff-角色-雨果-额外能力-强化E回能触发器,FALSE,FALSE,TRUE,FALSE,雨果,FALSE,额外能力:强化E命中普通敌人回能20点,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,1800,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-雨果-1画-决算招式双暴增幅,FALSE,FALSE,FALSE,TRUE,雨果,FALSE,暗渊回响状态下,触发决算效果时招式的双暴提升,TRUE,1,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,"{""only_label"":[""totalized""]}",,
Buff-角色-雨果-2画-决算招式无视防御力,FALSE,FALSE,FALSE,TRUE,雨果,FALSE,触发决算效果时招式无视敌人防御力,TRUE,1,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,"{""only_label"":[""totalized""]}",,
Buff-角色-雨果-4画-蓄力射击减冰抗,FALSE,FALSE,FALSE,TRUE,雨果,FALSE,蓄力射击使敌人冰抗降低12%,TRUE,900,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-角色-雨果-6画-决算招式增伤,FALSE,FALSE,FALSE,TRUE,雨果,FALSE,决算触发时,招式增伤60%,TRUE,1,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,6,1000,FALSE,FALSE,FALSE,"{""only_label"":[""totalized""]}",,
Buff-武器-精1千面日陨-常驻暴伤,TRUE,FALSE,FALSE,FALSE,千面日陨,FALSE,暴击伤害提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2千面日陨-常驻暴伤,TRUE,FALSE,FALSE,FALSE,千面日陨,FALSE,暴击伤害提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3千面日陨-常驻暴伤,TRUE,FALSE,FALSE,FALSE,千面日陨,FALSE,暴击伤害提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4千面日陨-常驻暴伤,TRUE,FALSE,FALSE,FALSE,千面日陨,FALSE,暴击伤害提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5千面日陨-常驻暴伤,TRUE,FALSE,FALSE,FALSE,千面日陨,FALSE,暴击伤害提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1千面日陨-零度处刑,TRUE,FALSE,FALSE,FALSE,千面日陨,FALSE,角色攻击无视防御力,FALSE,180,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2千面日陨-零度处刑,TRUE,FALSE,FALSE,FALSE,千面日陨,FALSE,角色攻击无视防御力,FALSE,180,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3千面日陨-零度处刑,TRUE,FALSE,FALSE,FALSE,千面日陨,FALSE,角色攻击无视防御力,FALSE,180,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4千面日陨-零度处刑,TRUE,FALSE,FALSE,FALSE,千面日陨,FALSE,角色攻击无视防御力,FALSE,180,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5千面日陨-零度处刑,TRUE,FALSE,FALSE,FALSE,千面日陨,FALSE,角色攻击无视防御力,FALSE,180,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1钢铁肉垫-常驻物理伤,TRUE,FALSE,FALSE,FALSE,钢铁肉垫,FALSE,常驻物理增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2钢铁肉垫-常驻物理伤,TRUE,FALSE,FALSE,FALSE,钢铁肉垫,FALSE,常驻物理增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3钢铁肉垫-常驻物理伤,TRUE,FALSE,FALSE,FALSE,钢铁肉垫,FALSE,常驻物理增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4钢铁肉垫-常驻物理伤,TRUE,FALSE,FALSE,FALSE,钢铁肉垫,FALSE,常驻物理增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5钢铁肉垫-常驻物理伤,TRUE,FALSE,FALSE,FALSE,钢铁肉垫,FALSE,常驻物理增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1钢铁肉垫-背击增伤,TRUE,FALSE,FALSE,FALSE,钢铁肉垫,FALSE,常驻物理增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,"{""only_back_attack"":[1]}",,
Buff-武器-精2钢铁肉垫-背击增伤,TRUE,FALSE,FALSE,FALSE,钢铁肉垫,FALSE,常驻物理增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,"{""only_back_attack"":[1]}",,
Buff-武器-精3钢铁肉垫-背击增伤,TRUE,FALSE,FALSE,FALSE,钢铁肉垫,FALSE,常驻物理增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,"{""only_back_attack"":[1]}",,
Buff-武器-精4钢铁肉垫-背击增伤,TRUE,FALSE,FALSE,FALSE,钢铁肉垫,FALSE,常驻物理增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,"{""only_back_attack"":[1]}",,
Buff-武器-精5钢铁肉垫-背击增伤,TRUE,FALSE,FALSE,FALSE,钢铁肉垫,FALSE,常驻物理增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,"{""only_back_attack"":[1]}",,
Buff-武器-精1街头巨星-终结技增伤,TRUE,FALSE,FALSE,FALSE,街头巨星,FALSE,连携技叠层,终结技增伤,TRUE,999999,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精2街头巨星-终结技增伤,TRUE,FALSE,FALSE,FALSE,街头巨星,FALSE,连携技叠层,终结技增伤,TRUE,999999,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精3街头巨星-终结技增伤,TRUE,FALSE,FALSE,FALSE,街头巨星,FALSE,连携技叠层,终结技增伤,TRUE,999999,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精4街头巨星-终结技增伤,TRUE,FALSE,FALSE,FALSE,街头巨星,FALSE,连携技叠层,终结技增伤,TRUE,999999,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精5街头巨星-终结技增伤,TRUE,FALSE,FALSE,FALSE,街头巨星,FALSE,连携技叠层,终结技增伤,TRUE,999999,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精1鎏金花信-局内攻击和强化E增伤,TRUE,FALSE,FALSE,FALSE,鎏金花信,FALSE,局内攻击以及强化E增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2鎏金花信-局内攻击和强化E增伤,TRUE,FALSE,FALSE,FALSE,鎏金花信,FALSE,局内攻击以及强化E增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3鎏金花信-局内攻击和强化E增伤,TRUE,FALSE,FALSE,FALSE,鎏金花信,FALSE,局内攻击以及强化E增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4鎏金花信-局内攻击和强化E增伤,TRUE,FALSE,FALSE,FALSE,鎏金花信,FALSE,局内攻击以及强化E增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5鎏金花信-局内攻击和强化E增伤,TRUE,FALSE,FALSE,FALSE,鎏金花信,FALSE,局内攻击以及强化E增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1强音热望-攻击力加成,TRUE,FALSE,FALSE,FALSE,强音热望,FALSE,强化E、连携技使攻击力增加,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2强音热望-攻击力加成,TRUE,FALSE,FALSE,FALSE,强音热望,FALSE,强化E、连携技使攻击力增加,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3强音热望-攻击力加成,TRUE,FALSE,FALSE,FALSE,强音热望,FALSE,强化E、连携技使攻击力增加,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4强音热望-攻击力加成,TRUE,FALSE,FALSE,FALSE,强音热望,FALSE,强化E、连携技使攻击力增加,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5强音热望-攻击力加成,TRUE,FALSE,FALSE,FALSE,强音热望,FALSE,强化E、连携技使攻击力增加,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1强音热望-额外攻击力加成,TRUE,FALSE,FALSE,FALSE,强音热望,FALSE,强化E、连携技使攻击力增加,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2强音热望-额外攻击力加成,TRUE,FALSE,FALSE,FALSE,强音热望,FALSE,强化E、连携技使攻击力增加,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3强音热望-额外攻击力加成,TRUE,FALSE,FALSE,FALSE,强音热望,FALSE,强化E、连携技使攻击力增加,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4强音热望-额外攻击力加成,TRUE,FALSE,FALSE,FALSE,强音热望,FALSE,强化E、连携技使攻击力增加,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5强音热望-额外攻击力加成,TRUE,FALSE,FALSE,FALSE,强音热望,FALSE,强化E、连携技使攻击力增加,TRUE,480,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1加农转子-常驻攻击力,TRUE,FALSE,FALSE,FALSE,加农转子,FALSE,常驻攻击力加成,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2加农转子-常驻攻击力,TRUE,FALSE,FALSE,FALSE,加农转子,FALSE,常驻攻击力加成,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3加农转子-常驻攻击力,TRUE,FALSE,FALSE,FALSE,加农转子,FALSE,常驻攻击力加成,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4加农转子-常驻攻击力,TRUE,FALSE,FALSE,FALSE,加农转子,FALSE,常驻攻击力加成,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5加农转子-常驻攻击力,TRUE,FALSE,FALSE,FALSE,加农转子,FALSE,常驻攻击力加成,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1加农转子-附加伤害触发器,TRUE,FALSE,FALSE,FALSE,加农转子,FALSE,攻击命中敌人并发生暴击时,触发额外攻击,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,480,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2加农转子-附加伤害触发器,TRUE,FALSE,FALSE,FALSE,加农转子,FALSE,攻击命中敌人并发生暴击时,触发额外攻击,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,450,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3加农转子-附加伤害触发器,TRUE,FALSE,FALSE,FALSE,加农转子,FALSE,攻击命中敌人并发生暴击时,触发额外攻击,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,420,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4加农转子-附加伤害触发器,TRUE,FALSE,FALSE,FALSE,加农转子,FALSE,攻击命中敌人并发生暴击时,触发额外攻击,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,390,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5加农转子-附加伤害触发器,TRUE,FALSE,FALSE,FALSE,加农转子,FALSE,攻击命中敌人并发生暴击时,触发额外攻击,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,360,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1「月相」-望-增伤,TRUE,FALSE,FALSE,FALSE,「月相」-望,FALSE,普攻、冲刺攻击、闪反增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2「月相」-望-增伤,TRUE,FALSE,FALSE,FALSE,「月相」-望,FALSE,普攻、冲刺攻击、闪反增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3「月相」-望-增伤,TRUE,FALSE,FALSE,FALSE,「月相」-望,FALSE,普攻、冲刺攻击、闪反增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4「月相」-望-增伤,TRUE,FALSE,FALSE,FALSE,「月相」-望,FALSE,普攻、冲刺攻击、闪反增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5「月相」-望-增伤,TRUE,FALSE,FALSE,FALSE,「月相」-望,FALSE,普攻、冲刺攻击、闪反增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1「月相」-晦-增伤,TRUE,FALSE,FALSE,FALSE,「月相」-晦,FALSE,连携技、大招增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2「月相」-晦-增伤,TRUE,FALSE,FALSE,FALSE,「月相」-晦,FALSE,连携技、大招增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3「月相」-晦-增伤,TRUE,FALSE,FALSE,FALSE,「月相」-晦,FALSE,连携技、大招增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4「月相」-晦-增伤,TRUE,FALSE,FALSE,FALSE,「月相」-晦,FALSE,连携技、大招增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5「月相」-晦-增伤,TRUE,FALSE,FALSE,FALSE,「月相」-晦,FALSE,连携技、大招增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1「月相」-朔-回能触发器,TRUE,FALSE,FALSE,FALSE,「月相」-朔,FALSE,强化E回能,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,720,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2「月相」-朔-回能触发器,TRUE,FALSE,FALSE,FALSE,「月相」-朔,FALSE,强化E回能,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,720,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3「月相」-朔-回能触发器,TRUE,FALSE,FALSE,FALSE,「月相」-朔,FALSE,强化E回能,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,720,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4「月相」-朔-回能触发器,TRUE,FALSE,FALSE,FALSE,「月相」-朔,FALSE,强化E回能,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,720,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5「月相」-朔-回能触发器,TRUE,FALSE,FALSE,FALSE,「月相」-朔,FALSE,强化E回能,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,720,TRUE,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-角色-仪玄-回能事件组触发器,FALSE,FALSE,FALSE,FALSE,废弃,FALSE,废弃,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE,0,1000,FALSE,FALSE,TRUE,,,
Buff-角色-仪玄-核心被动-技能增伤,FALSE,FALSE,FALSE,FALSE,仪玄,FALSE,玄墨极阵、青溟震击、强化E、突击支援、QTE、大招增伤,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-仪玄-额外能力-对失衡敌人增伤,FALSE,FALSE,TRUE,FALSE,仪玄,FALSE,凝云术和墨烬影消命中处于失衡状态下的敌人时增伤30%,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,"{""only_skill"":[""1371_E_EX_B_1"",""1371_E_EX_B_2"",""1371_E_EX_B_3""]}",,
Buff-角色-仪玄-额外能力-暴伤提升,FALSE,FALSE,TRUE,FALSE,仪玄,FALSE,发动终结技后提升暴伤,TRUE,900,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-仪玄-1画-暴击率提升,FALSE,FALSE,FALSE,TRUE,仪玄,FALSE,常驻暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-角色-仪玄-1画-落雷触发器,FALSE,FALSE,FALSE,TRUE,仪玄,FALSE,队友攻击触发落雷,并且回复闪能,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,360,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,1,1000,FALSE,FALSE,TRUE,,,
Buff-角色-仪玄-2画-强化E与终结技无视以太抗,FALSE,FALSE,FALSE,TRUE,仪玄,FALSE,终结技和强化E造成伤害时无视目标 以太抗性,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,"{""only_trigger_buff_level"":[2,6]}",,
Buff-角色-仪玄-2画-失衡时间提升,FALSE,TRUE,FALSE,TRUE,仪玄,FALSE,仪玄发动喧响值大招时,可以使敌人的失衡时间延长3秒,TRUE,999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,2,1,FALSE,FALSE,FALSE,,,
Buff-角色-仪玄-4画-静心,FALSE,FALSE,FALSE,TRUE,仪玄,FALSE,仪玄发动终结技时叠层,每层使得下一次墨烬影消和凝云术的伤害提升30%,TRUE,999999,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,FALSE,4,1000,FALSE,FALSE,FALSE,"{""only_skill"":[""1371_E_EX_B_1"",""1371_E_EX_B_2"",""1371_E_EX_B_3""]}",,
Buff-角色-仪玄-6画-贯穿伤害提高,FALSE,FALSE,FALSE,TRUE,仪玄,FALSE,凝神状态下,贯穿伤害提高20%,TRUE,900,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,6,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1青溟笼舍-暴击率提升,TRUE,FALSE,FALSE,FALSE,青溟笼舍,FALSE,常驻暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2青溟笼舍-暴击率提升,TRUE,FALSE,FALSE,FALSE,青溟笼舍,FALSE,常驻暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3青溟笼舍-暴击率提升,TRUE,FALSE,FALSE,FALSE,青溟笼舍,FALSE,常驻暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4青溟笼舍-暴击率提升,TRUE,FALSE,FALSE,FALSE,青溟笼舍,FALSE,常驻暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5青溟笼舍-暴击率提升,TRUE,FALSE,FALSE,FALSE,青溟笼舍,FALSE,常驻暴击率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1青溟笼舍-以太伤害提升,TRUE,FALSE,FALSE,FALSE,青溟笼舍,FALSE,释放强化E时叠层,每层提升以太伤害,TRUE,900,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2青溟笼舍-以太伤害提升,TRUE,FALSE,FALSE,FALSE,青溟笼舍,FALSE,释放强化E时叠层,每层提升以太伤害,TRUE,900,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3青溟笼舍-以太伤害提升,TRUE,FALSE,FALSE,FALSE,青溟笼舍,FALSE,释放强化E时叠层,每层提升以太伤害,TRUE,900,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4青溟笼舍-以太伤害提升,TRUE,FALSE,FALSE,FALSE,青溟笼舍,FALSE,释放强化E时叠层,每层提升以太伤害,TRUE,900,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5青溟笼舍-以太伤害提升,TRUE,FALSE,FALSE,FALSE,青溟笼舍,FALSE,释放强化E时叠层,每层提升以太伤害,TRUE,900,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1青溟笼舍-贯穿伤害提升,TRUE,FALSE,FALSE,FALSE,青溟笼舍,FALSE,释放强化E时叠层,每层提升以太贯穿伤害,TRUE,900,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,"{""only_element"":[4]}",,
Buff-武器-精2青溟笼舍-贯穿伤害提升,TRUE,FALSE,FALSE,FALSE,青溟笼舍,FALSE,释放强化E时叠层,每层提升以太贯穿伤害,TRUE,900,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,"{""only_element"":[4]}",,
Buff-武器-精3青溟笼舍-贯穿伤害提升,TRUE,FALSE,FALSE,FALSE,青溟笼舍,FALSE,释放强化E时叠层,每层提升以太贯穿伤害,TRUE,900,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,"{""only_element"":[4]}",,
Buff-武器-精4青溟笼舍-贯穿伤害提升,TRUE,FALSE,FALSE,FALSE,青溟笼舍,FALSE,释放强化E时叠层,每层提升以太贯穿伤害,TRUE,900,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,"{""only_element"":[4]}",,
Buff-武器-精5青溟笼舍-贯穿伤害提升,TRUE,FALSE,FALSE,FALSE,青溟笼舍,FALSE,释放强化E时叠层,每层提升以太贯穿伤害,TRUE,900,2,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,"{""only_element"":[4]}",,
Buff-驱动盘-云岿如我-四件套-暴击率提升,FALSE,FALSE,FALSE,FALSE,云岿如我,FALSE,发动强化E、连携技、大招时叠层,每层提升暴击率,TRUE,900,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-驱动盘-云岿如我-四件套-贯穿伤害提升,FALSE,FALSE,FALSE,FALSE,云岿如我,FALSE,满层时,造成的贯穿伤害增加,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1幻变魔方-爆伤提升,TRUE,FALSE,FALSE,FALSE,幻变魔方,FALSE,发动强化E时暴伤提升,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2幻变魔方-爆伤提升,TRUE,FALSE,FALSE,FALSE,幻变魔方,FALSE,发动强化E时暴伤提升,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3幻变魔方-爆伤提升,TRUE,FALSE,FALSE,FALSE,幻变魔方,FALSE,发动强化E时暴伤提升,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4幻变魔方-爆伤提升,TRUE,FALSE,FALSE,FALSE,幻变魔方,FALSE,发动强化E时暴伤提升,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5幻变魔方-爆伤提升,TRUE,FALSE,FALSE,FALSE,幻变魔方,FALSE,发动强化E时暴伤提升,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1幻变魔方-强化E增伤,TRUE,FALSE,FALSE,FALSE,幻变魔方,FALSE,发动强化E时、若敌人血量低于50%,强化E伤害提升,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2幻变魔方-强化E增伤,TRUE,FALSE,FALSE,FALSE,幻变魔方,FALSE,发动强化E时、若敌人血量低于50%,强化E伤害提升,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3幻变魔方-强化E增伤,TRUE,FALSE,FALSE,FALSE,幻变魔方,FALSE,发动强化E时、若敌人血量低于50%,强化E伤害提升,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4幻变魔方-强化E增伤,TRUE,FALSE,FALSE,FALSE,幻变魔方,FALSE,发动强化E时、若敌人血量低于50%,强化E伤害提升,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5幻变魔方-强化E增伤,TRUE,FALSE,FALSE,FALSE,幻变魔方,FALSE,发动强化E时、若敌人血量低于50%,强化E伤害提升,TRUE,720,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1电波漫步-贯穿力提升,TRUE,FALSE,FALSE,FALSE,电波漫步,FALSE,发动连携技或大招时叠层(独立),每层提升固定贯穿力,TRUE,720,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精2电波漫步-贯穿力提升,TRUE,FALSE,FALSE,FALSE,电波漫步,FALSE,发动连携技或大招时叠层(独立),每层提升固定贯穿力,TRUE,720,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精3电波漫步-贯穿力提升,TRUE,FALSE,FALSE,FALSE,电波漫步,FALSE,发动连携技或大招时叠层(独立),每层提升固定贯穿力,TRUE,720,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精4电波漫步-贯穿力提升,TRUE,FALSE,FALSE,FALSE,电波漫步,FALSE,发动连携技或大招时叠层(独立),每层提升固定贯穿力,TRUE,720,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精5电波漫步-贯穿力提升,TRUE,FALSE,FALSE,FALSE,电波漫步,FALSE,发动连携技或大招时叠层(独立),每层提升固定贯穿力,TRUE,720,3,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,TRUE,FALSE,,,
Buff-武器-精1「灰烬」-钴蓝-攻击力提升,TRUE,FALSE,FALSE,FALSE,「灰烬」-钴蓝,FALSE,进入接站状态或前场时,装备者攻击力提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,CinderCobalt_1
Buff-武器-精2「灰烬」-钴蓝-攻击力提升,TRUE,FALSE,FALSE,FALSE,「灰烬」-钴蓝,FALSE,进入接站状态或前场时,装备者攻击力提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,CinderCobalt_1
Buff-武器-精3「灰烬」-钴蓝-攻击力提升,TRUE,FALSE,FALSE,FALSE,「灰烬」-钴蓝,FALSE,进入接站状态或前场时,装备者攻击力提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,CinderCobalt_1
Buff-武器-精4「灰烬」-钴蓝-攻击力提升,TRUE,FALSE,FALSE,FALSE,「灰烬」-钴蓝,FALSE,进入接站状态或前场时,装备者攻击力提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,CinderCobalt_1
Buff-武器-精5「灰烬」-钴蓝-攻击力提升,TRUE,FALSE,FALSE,FALSE,「灰烬」-钴蓝,FALSE,进入接站状态或前场时,装备者攻击力提升,TRUE,600,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,1200,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,CinderCobalt_1
Buff-角色-柚叶-甜蜜惊吓,FALSE,TRUE,FALSE,FALSE,柚叶,FALSE,甜蜜惊吓debuff (不含触发逻辑,仅作为标志使用),TRUE,2400,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-柚叶-硬糖射击触发器,FALSE,FALSE,FALSE,FALSE,柚叶,FALSE,硬糖射击触发器,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,0,1000,FALSE,FALSE,TRUE,,,
Buff-角色-柚叶-彩糖花火积蓄值增加,FALSE,FALSE,FALSE,FALSE,柚叶,FALSE,彩糖花火的积蓄值增加,FALSE,2,31,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,"{""only_skill_1"":[""1411_SNA_A""]}",,
Buff-角色-柚叶-彩糖花火·极积蓄值增加,FALSE,FALSE,FALSE,FALSE,柚叶,FALSE,彩糖花火·极积蓄值增加,FALSE,2,31,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,"{""only_skill_1"":[""1411_SNA_B""]}",,
Buff-角色-柚叶-核心被动-狸之愿-攻击力,FALSE,FALSE,FALSE,FALSE,柚叶,FALSE,攻击力增幅,TRUE,2400,1200,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE,TRUE,0,1110,FALSE,FALSE,FALSE,,,
Buff-角色-柚叶-核心被动-狸之愿-增伤,FALSE,FALSE,FALSE,FALSE,柚叶,FALSE,增伤,TRUE,2400,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1110,FALSE,FALSE,FALSE,,,
Buff-角色-柚叶-组队被动-积蓄值增幅,FALSE,FALSE,TRUE,FALSE,柚叶,FALSE,积蓄效率和紊乱、属性异常伤害提升,TRUE,2400,130,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE,TRUE,0,1110,FALSE,FALSE,FALSE,,,
Buff-角色-柚叶-组队被动-属性异常与紊乱伤害增幅,FALSE,FALSE,TRUE,FALSE,柚叶,FALSE,积蓄效率和紊乱、属性异常伤害提升,TRUE,2400,130,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE,TRUE,0,1110,FALSE,FALSE,FALSE,"{""only_anomaly"":[""Disorder"",""PolarityDisorder"",""AllAnomaly""]}",,
Buff-角色-柚叶-1画-全属性伤害抗性降低,FALSE,TRUE,FALSE,TRUE,柚叶,FALSE,甜蜜惊吓使敌人全属性抗性降低,TRUE,2400,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1,FALSE,FALSE,FALSE,,,
Buff-角色-柚叶-2画-全队增伤与积蓄效率增幅,FALSE,FALSE,FALSE,TRUE,柚叶,FALSE,柚叶强化E、大招命中时为全队提供积蓄效率与增伤Buff,TRUE,2400,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1110,FALSE,FALSE,FALSE,,,
Buff-角色-柚叶-2画-连携技触发器,FALSE,FALSE,FALSE,TRUE,柚叶,FALSE,柚叶的强化E和大招在命中非失衡期敌人时会激发连携,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-角色-柚叶-4画-支援突击增幅,FALSE,FALSE,FALSE,TRUE,柚叶,FALSE,柚叶的两个支援突击额外增伤、积蓄效率提升,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,"{""only_skill"":[""1411_Assault_Aid_A"",""1411_Assault_Aid"",""1411_Assault_Aid_B""]}",,
Buff-角色-柚叶-4画-快支触发器,FALSE,FALSE,FALSE,TRUE,柚叶,FALSE,柚叶的两个支援突击在命中时会激发快支,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-角色-柚叶-6画-炮弹触发器,FALSE,FALSE,FALSE,TRUE,柚叶,FALSE,柚叶6画的蓄力突击支援会催生强力炮弹,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,6,1000,FALSE,FALSE,FALSE,,,
Buff-角色-柚叶-6画-彩糖花火极触发器,FALSE,FALSE,FALSE,TRUE,柚叶,FALSE,柚叶6画的炮弹命中会触发彩糖花火极,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,6,1000,FALSE,FALSE,TRUE,,,
Buff-角色-柚叶-6画-紊乱伤害倍率提升,FALSE,FALSE,FALSE,TRUE,柚叶,FALSE,柚叶6画的炮弹命中会叠加增加紊乱倍率的Buff,TRUE,2400,3,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,6,1110,FALSE,TRUE,TRUE,,,
Buff-武器-精1狸法七变化-异常掌控,TRUE,FALSE,FALSE,FALSE,狸法七变化,FALSE,装备者的强化E或终结技造成物理伤害时提升自身异常掌控,TRUE,2400,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2狸法七变化-异常掌控,TRUE,FALSE,FALSE,FALSE,狸法七变化,FALSE,装备者的强化E或终结技造成物理伤害时提升自身异常掌控,TRUE,2400,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3狸法七变化-异常掌控,TRUE,FALSE,FALSE,FALSE,狸法七变化,FALSE,装备者的强化E或终结技造成物理伤害时提升自身异常掌控,TRUE,2400,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4狸法七变化-异常掌控,TRUE,FALSE,FALSE,FALSE,狸法七变化,FALSE,装备者的强化E或终结技造成物理伤害时提升自身异常掌控,TRUE,2400,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5狸法七变化-异常掌控,TRUE,FALSE,FALSE,FALSE,狸法七变化,FALSE,装备者的强化E或终结技造成物理伤害时提升自身异常掌控,TRUE,2400,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1狸法七变化-全队异常精通,TRUE,FALSE,FALSE,FALSE,狸法七变化,FALSE,装备者追加攻击命中敌人时提升全队精通,TRUE,2400,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1110,FALSE,FALSE,TRUE,,,
Buff-武器-精2狸法七变化-全队异常精通,TRUE,FALSE,FALSE,FALSE,狸法七变化,FALSE,装备者追加攻击命中敌人时提升全队精通,TRUE,2400,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1110,FALSE,FALSE,TRUE,,,
Buff-武器-精3狸法七变化-全队异常精通,TRUE,FALSE,FALSE,FALSE,狸法七变化,FALSE,装备者追加攻击命中敌人时提升全队精通,TRUE,2400,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1110,FALSE,FALSE,TRUE,,,
Buff-武器-精4狸法七变化-全队异常精通,TRUE,FALSE,FALSE,FALSE,狸法七变化,FALSE,装备者追加攻击命中敌人时提升全队精通,TRUE,2400,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1110,FALSE,FALSE,TRUE,,,
Buff-武器-精5狸法七变化-全队异常精通,TRUE,FALSE,FALSE,FALSE,狸法七变化,FALSE,装备者追加攻击命中敌人时提升全队精通,TRUE,2400,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1110,FALSE,FALSE,TRUE,,,
Buff-角色-薇薇安-6画-触发器,FALSE,FALSE,FALSE,TRUE,薇薇安,FALSE,发动悬落时消耗额外护羽,触发特殊异放,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,6,1000,FALSE,FALSE,FALSE,,,
Buff-角色-爱丽丝-核心被动-紊乱基础倍率增加,FALSE,TRUE,FALSE,FALSE,爱丽丝,FALSE,畏缩状态下的敌人结算紊乱时,按畏缩状态剩余时间提高紊乱的基础倍率,其触发完全由监听器负责,TRUE,1,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1,FALSE,FALSE,FALSE,,,
Buff-角色-爱丽丝-核心被动-物理异常积蓄效率提升,FALSE,FALSE,FALSE,FALSE,爱丽丝,FALSE,爱丽丝触发强击时,物理积蓄效率提升25%,该Buff触发完全由监听器控制,TRUE,1800,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-爱丽丝-额外能力-异常掌控转精通,FALSE,FALSE,TRUE,FALSE,爱丽丝,FALSE,爱丽丝的异常掌控超过140点的部分,以1:1.6的比例转化为精通,TRUE,999999,999,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-角色-爱丽丝-影画-1画-减防,FALSE,TRUE,FALSE,TRUE,爱丽丝,FALSE,爱丽丝触发强击时,目标防御力降低,该Buff的触发完全由监听器控制,TRUE,1800,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1,FALSE,FALSE,FALSE,,,
Buff-角色-爱丽丝-影画-2画-全队强击伤害提升,FALSE,FALSE,FALSE,TRUE,爱丽丝,FALSE,全队角色强击伤害提升,常驻buff,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1110,FALSE,FALSE,FALSE,,,
Buff-角色-爱丽丝-影画-2画-紊乱伤害提升,FALSE,TRUE,FALSE,TRUE,爱丽丝,FALSE,物理异常状态下的敌人被结算紊的紊乱伤害提升,该Buff的触发完全由监听器控制,TRUE,1,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1,FALSE,FALSE,FALSE,,,
Buff-角色-爱丽丝-影画-4画-无视物理伤害抗性,FALSE,FALSE,FALSE,TRUE,爱丽丝,FALSE,爱丽丝无视目标10%物理抗性,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-角色-爱丽丝-影画-4画-普攻积蓄效率增幅,FALSE,FALSE,FALSE,TRUE,爱丽丝,FALSE,爱丽丝的强化A5的物理积蓄值提升25%,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-角色-爱丽丝-影画-6画-额外攻击触发器,FALSE,FALSE,FALSE,TRUE,爱丽丝,FALSE,在决胜状态激活时,任意其他角色攻击命中都会触发一次额外攻击,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,60,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,6,1000,FALSE,FALSE,TRUE,,,
Buff-角色-爱丽丝-影画-6画-额外攻击必暴,FALSE,FALSE,FALSE,TRUE,爱丽丝,FALSE,6画的额外攻击必暴,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,6,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1十方锻星-异常掌控提升,TRUE,FALSE,FALSE,FALSE,十方锻星,FALSE,异常掌控提升,常驻,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2十方锻星-异常掌控提升,TRUE,FALSE,FALSE,FALSE,十方锻星,FALSE,异常掌控提升,常驻,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3十方锻星-异常掌控提升,TRUE,FALSE,FALSE,FALSE,十方锻星,FALSE,异常掌控提升,常驻,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4十方锻星-异常掌控提升,TRUE,FALSE,FALSE,FALSE,十方锻星,FALSE,异常掌控提升,常驻,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5十方锻星-异常掌控提升,TRUE,FALSE,FALSE,FALSE,十方锻星,FALSE,异常掌控提升,常驻,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1十方锻星-物理伤害增加,TRUE,FALSE,FALSE,FALSE,十方锻星,FALSE,触发强击时,装备者造成的伤害提高20%,最多两层;入场立即获得2层效果,此Buff的触发完全受监听器控制,TRUE,1200,2,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,PracticedPerfection_1
Buff-武器-精2十方锻星-物理伤害增加,TRUE,FALSE,FALSE,FALSE,十方锻星,FALSE,触发强击时,装备者造成的伤害提高20%,最多两层;入场立即获得2层效果,此Buff的触发完全受监听器控制,TRUE,1200,2,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,PracticedPerfection_1
Buff-武器-精3十方锻星-物理伤害增加,TRUE,FALSE,FALSE,FALSE,十方锻星,FALSE,触发强击时,装备者造成的伤害提高20%,最多两层;入场立即获得2层效果,此Buff的触发完全受监听器控制,TRUE,1200,2,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,PracticedPerfection_1
Buff-武器-精4十方锻星-物理伤害增加,TRUE,FALSE,FALSE,FALSE,十方锻星,FALSE,触发强击时,装备者造成的伤害提高20%,最多两层;入场立即获得2层效果,此Buff的触发完全受监听器控制,TRUE,1200,2,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,PracticedPerfection_1
Buff-武器-精5十方锻星-物理伤害增加,TRUE,FALSE,FALSE,FALSE,十方锻星,FALSE,触发强击时,装备者造成的伤害提高20%,最多两层;入场立即获得2层效果,此Buff的触发完全受监听器控制,TRUE,1200,2,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,PracticedPerfection_1
Buff-角色-爱丽丝-极性强击触发器,FALSE,TRUE,FALSE,FALSE,爱丽丝,FALSE,爱丽丝的极性强击触发器,FALSE,0,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,FALSE,TRUE,0,1000,FALSE,FALSE,TRUE,,,
Buff-角色-席德-强袭,FALSE,FALSE,FALSE,FALSE,席德,FALSE,正兵释放强化E时席德获得强袭,TRUE,2400,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1000,FALSE,FALSE,TRUE,,,
Buff-角色-席德-明攻,FALSE,FALSE,FALSE,FALSE,席德,FALSE,席德释放强化E时正兵获得明攻,该Buff的触发完全由监听器控制,TRUE,2400,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1110,FALSE,FALSE,FALSE,,,
Buff-角色-席德-围杀,FALSE,FALSE,FALSE,FALSE,席德,FALSE,当正兵、强袭都存在时,围杀激活,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,FALSE,0,1110,FALSE,FALSE,TRUE,,,
Buff-角色-席德-额外能力-重击大招增伤无视电抗,FALSE,FALSE,TRUE,FALSE,席德,FALSE,席德的特殊普攻与大招增伤30%且无视敌人25%电抗,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,"{""only_skill"":[""1461_SNA_1"",""1461_SNA_2"",""1461_SNA_3"",""1461_Q""]}",,
Buff-角色-席德-影画-1画-崩坠暴伤增加,FALSE,FALSE,FALSE,TRUE,席德,FALSE,席德崩坠的暴伤增加,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,"{""only_skill"":[""1461_SNA_2"",""1461_SNA_3""]}",,
Buff-角色-席德-影画-2画-围杀无视防御力,FALSE,FALSE,FALSE,TRUE,席德,FALSE,围杀生效时,额外无视敌人防御力,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,FALSE,2,1110,FALSE,FALSE,FALSE,,,
Buff-角色-席德-影画-2画-耗能转化增伤,FALSE,FALSE,FALSE,TRUE,席德,FALSE,消耗能量使重戮伤害增加,TRUE,86,24,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,"{""only_skill"":[""1461_SNA_1""]}",,
Buff-角色-席德-影画-4画-喧响效率与大招增伤,FALSE,FALSE,FALSE,TRUE,席德,FALSE,围杀激活时,喧响值获取效率增加,并且大招增伤,TRUE,999999,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,FALSE,4,1110,FALSE,FALSE,TRUE,,,
Buff-角色-席德-影画-6画-常驻暴伤,FALSE,FALSE,FALSE,TRUE,席德,FALSE,暴击伤害提高50%,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,6,1000,FALSE,FALSE,FALSE,,,
Buff-角色-席德-影画-6画-触发器,FALSE,FALSE,FALSE,TRUE,席德,FALSE,激光触发器,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,FALSE,TRUE,TRUE,TRUE,TRUE,6,1000,FALSE,FALSE,TRUE,,,
Buff-武器-精1机巧心种-常驻暴击,TRUE,FALSE,FALSE,FALSE,机巧心种,FALSE,常驻暴击,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精2机巧心种-常驻暴击,TRUE,FALSE,FALSE,FALSE,机巧心种,FALSE,常驻暴击,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精3机巧心种-常驻暴击,TRUE,FALSE,FALSE,FALSE,机巧心种,FALSE,常驻暴击,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精4机巧心种-常驻暴击,TRUE,FALSE,FALSE,FALSE,机巧心种,FALSE,常驻暴击,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精5机巧心种-常驻暴击,TRUE,FALSE,FALSE,FALSE,机巧心种,FALSE,常驻暴击,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,FALSE,FALSE,,,
Buff-武器-精1机巧心种-电属性增伤,TRUE,FALSE,FALSE,FALSE,机巧心种,FALSE,装备者通过普攻和强化E造成伤害时分别获得一层,每个招式最多触发一次,TRUE,2400,2,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,1,1000,FALSE,TRUE,TRUE,,,
Buff-武器-精2机巧心种-电属性增伤,TRUE,FALSE,FALSE,FALSE,机巧心种,FALSE,装备者通过普攻和强化E造成伤害时分别获得一层,每个招式最多触发一次,TRUE,2400,2,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,2,1000,FALSE,TRUE,TRUE,,,
Buff-武器-精3机巧心种-电属性增伤,TRUE,FALSE,FALSE,FALSE,机巧心种,FALSE,装备者通过普攻和强化E造成伤害时分别获得一层,每个招式最多触发一次,TRUE,2400,2,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,3,1000,FALSE,TRUE,TRUE,,,
Buff-武器-精4机巧心种-电属性增伤,TRUE,FALSE,FALSE,FALSE,机巧心种,FALSE,装备者通过普攻和强化E造成伤害时分别获得一层,每个招式最多触发一次,TRUE,2400,2,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,4,1000,FALSE,TRUE,TRUE,,,
Buff-武器-精5机巧心种-电属性增伤,TRUE,FALSE,FALSE,FALSE,机巧心种,FALSE,装备者通过普攻和强化E造成伤害时分别获得一层,每个招式最多触发一次,TRUE,2400,2,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,5,1000,FALSE,TRUE,TRUE,,,
Buff-武器-精1机巧心种-普攻大招无视防御,TRUE,FALSE,FALSE,FALSE,机巧心种,FALSE,第二特效2层时触发,普攻和大招无视敌人防御,FALSE,999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,1,1000,FALSE,FALSE,TRUE,"{""only_trigger_buff_level"":[0,6]}",,
Buff-武器-精2机巧心种-普攻大招无视防御,TRUE,FALSE,FALSE,FALSE,机巧心种,FALSE,第二特效2层时触发,普攻和大招无视敌人防御,FALSE,999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,2,1000,FALSE,FALSE,TRUE,"{""only_trigger_buff_level"":[0,6]}",,
Buff-武器-精3机巧心种-普攻大招无视防御,TRUE,FALSE,FALSE,FALSE,机巧心种,FALSE,第二特效2层时触发,普攻和大招无视敌人防御,FALSE,999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,3,1000,FALSE,FALSE,TRUE,"{""only_trigger_buff_level"":[0,6]}",,
Buff-武器-精4机巧心种-普攻大招无视防御,TRUE,FALSE,FALSE,FALSE,机巧心种,FALSE,第二特效2层时触发,普攻和大招无视敌人防御,FALSE,999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,4,1000,FALSE,FALSE,TRUE,"{""only_trigger_buff_level"":[0,6]}",,
Buff-武器-精5机巧心种-普攻大招无视防御,TRUE,FALSE,FALSE,FALSE,机巧心种,FALSE,第二特效2层时触发,普攻和大招无视敌人防御,FALSE,999999,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,FALSE,5,1000,FALSE,FALSE,TRUE,"{""only_trigger_buff_level"":[0,6]}",,
Buff-驱动盘-拂晓生花-二件套-普攻增伤,FALSE,FALSE,FALSE,FALSE,拂晓生花,FALSE,常驻普攻增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-驱动盘-拂晓生花-四件套-常驻普攻增伤,FALSE,FALSE,FALSE,FALSE,拂晓生花,FALSE,常驻普攻增伤,FALSE,0,1,1,FALSE,FALSE,TRUE,TRUE,FALSE,0,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-驱动盘-拂晓生花-四件套-触发普攻增伤,FALSE,FALSE,FALSE,FALSE,拂晓生花,FALSE,装备者为强攻角色时,发动强化E和终结技触发普攻增伤,TRUE,1500,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1000,FALSE,FALSE,FALSE,,,
Buff-驱动盘-月光骑士颂-全队增伤,FALSE,FALSE,FALSE,FALSE,月光骑士颂,FALSE,装备者为支援角色时,发动强化E和终结技触发全队增伤,TRUE,1500,1,1,TRUE,FALSE,TRUE,FALSE,FALSE,0,TRUE,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,0,1110,FALSE,FALSE,FALSE,,,
Buff-角色-席德-明攻触发器,FALSE,FALSE,FALSE,FALSE,席德,FALSE,席德的明攻Buff的触发器,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,0,1000,FALSE,FALSE,TRUE,,,
Buff-角色-席德-影画-2画-无视防御触发器,FALSE,FALSE,FALSE,TRUE,席德,FALSE,席德2画减防触发器,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,2,1000,FALSE,FALSE,TRUE,,,
Buff-角色-席德-围杀触发器,FALSE,FALSE,FALSE,FALSE,席德,FALSE,席德围杀的触发器,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,0,1000,FALSE,FALSE,TRUE,,,
Buff-角色-席德-影画-4画-触发器,FALSE,FALSE,FALSE,TRUE,席德,FALSE,席德4画减防触发器,FALSE,0,1,1,FALSE,FALSE,TRUE,FALSE,TRUE,0,TRUE,FALSE,TRUE,TRUE,FALSE,TRUE,TRUE,4,1000,FALSE,FALSE,TRUE,,,
================================================
FILE: zsim/data/触发判断.csv
================================================
BuffName,id,OfficialName,From,SpConsumption,SpRecovery_hit,Sp_Threshold,FeverRecovery,ElementAbnormalAccumulation,SkillType,TriggerBuffLevel,ElementType,TimeCost,HitNumber,DmgRelated_Attributes,StunRelated_Attributes,Interruption_Resistance
Buff-角色-艾莲-核心被动,,,,,,,,,,,2,,,,,
Buff-角色-艾莲-额外能力,,,,,,,,,,0,2,,,,,
Buff-武器-精1深海访客-冰伤,,,,,,,,,,,,,,,,
Buff-武器-精1深海访客-暴击率-1,,,,,,,,,,0,,,,,,
Buff-武器-精1深海访客-暴击率-2,,,,,,,,,,3,2|5,,,,,
Buff-武器-精2深海访客-冰伤,,,,,,,,,,,,,,,,
Buff-武器-精2深海访客-暴击率-1,,,,,,,,,,0,,,,,,
Buff-武器-精2深海访客-暴击率-2,,,,,,,,,,3,2|5,,,,,
Buff-武器-精3深海访客-冰伤,,,,,,,,,,,,,,,,
Buff-武器-精3深海访客-暴击率-1,,,,,,,,,,0,,,,,,
Buff-武器-精3深海访客-暴击率-2,,,,,,,,,,3,2|5,,,,,
Buff-武器-精4深海访客-冰伤,,,,,,,,,,,,,,,,
Buff-武器-精4深海访客-暴击率-1,,,,,,,,,,0,,,,,,
Buff-武器-精4深海访客-暴击率-2,,,,,,,,,,3,2|5,,,,,
Buff-武器-精5深海访客-冰伤,,,,,,,,,,,,,,,,
Buff-武器-精5深海访客-暴击率-1,,,,,,,,,,0,,,,,,
Buff-武器-精5深海访客-暴击率-2,,,,,,,,,,3,2|5,,,,,
Buff-驱动盘-极地重金属-冲刺攻击增伤,,,,,,,,,,3,,,,,,
Buff-驱动盘-极地重金属-普攻增伤,,,,,,,,,,0,,,,,,
Buff-驱动盘-极地重金属-冲刺与普攻增伤-有条件,,,,,,,,,,,,,,,,
Buff-驱动盘-震星迪斯科,,,,,,,,,,0|3|4,,,,,,
Buff-驱动盘-啄木鸟电音-普攻,,,,,,,,,,0,,,,,,
Buff-驱动盘-啄木鸟电音-闪避反击,,,,,,,,,,4,,,,,,
Buff-驱动盘-啄木鸟电音-强化特殊技,,,,,,,,,,2,,,,,,
Buff-角色-莱特-核心被动-冲击力提升,,,,,,,,,,,,,,,,
Buff-角色-莱特-核心被动-冰火双抗,1161_NA_5_SH_EX|1161_NA_5_CoH_EX,,,,,,,,,,,,,,,
Buff-角色-莱特-核心被动-失衡时间延长,1161_NA_5_EndH_EX|1161_NA_5_EnEndH_EX,,,,,,,,,,,,,,,
Buff-角色-莱特-额外能力-冰火增伤,1161_SNA_5|1161_NA_5_SH|1161_NA_5_CoH|1161_NA_5_EndH|1161_NA_5_EnEndH_EX|1161_NA_5_SH_EX|1161_NA_5_CoH_EX|1161_NA_5_EndH_EX,,,,,,,,,,,,,,,
Buff-角色-莱卡恩-核心被动-失衡值提升,1141_SNA_1|1141_SNA_2|1141_SNA_3|1141_SNA_4|1141_SNA_5_FC|1141_SNA_5_NFC,,,,,,,,,,,,,,,
Buff-角色-莱卡恩-核心被动-减冰抗,,,,,,,,,,2|9,,,,,,
Buff-角色-莱卡恩-额外能力-失衡易伤倍率,,,,,,,,,,,,,,,,
Buff-异常-霜寒,,,,,,,,,,,,,,,,
Buff-异常-畏缩,,,,,,,,,,,,,,,,
Buff-角色-苍角-核心被动-1,1131_E_EX_A,,,,,,,,,,,,,,,
Buff-角色-苍角-核心被动-2,,,,,,,,,,,,,,,,
Buff-武器-精1含羞恶面-冰伤,,,,,,,,,,,,,,,,
Buff-武器-精2含羞恶面-冰伤,,,,,,,,,,,,,,,,
Buff-武器-精3含羞恶面-冰伤,,,,,,,,,,,,,,,,
Buff-武器-精4含羞恶面-冰伤,,,,,,,,,,,,,,,,
Buff-武器-精5含羞恶面-冰伤,,,,,,,,,,,,,,,,
Buff-武器-精1含羞恶面-叠层攻击力,,,,,,,,,,2,,,,,,
Buff-武器-精2含羞恶面-叠层攻击力,,,,,,,,,,2,,,,,,
Buff-武器-精3含羞恶面-叠层攻击力,,,,,,,,,,2,,,,,,
Buff-武器-精4含羞恶面-叠层攻击力,,,,,,,,,,2,,,,,,
Buff-武器-精5含羞恶面-叠层攻击力,,,,,,,,,,2,,,,,,
Buff-武器-精1燃狱齿轮-后台能量自动回复,,,,,,,,,,,,,,,,
Buff-武器-精2燃狱齿轮-后台能量自动回复,,,,,,,,,,,,,,,,
Buff-武器-精3燃狱齿轮-后台能量自动回复,,,,,,,,,,,,,,,,
Buff-武器-精4燃狱齿轮-后台能量自动回复,,,,,,,,,,,,,,,,
Buff-武器-精5燃狱齿轮-后台能量自动回复,,,,,,,,,,,,,,,,
Buff-武器-精1燃狱齿轮-叠层冲击力,,,,,,,,,,2,,,,,,
Buff-武器-精2燃狱齿轮-叠层冲击力,,,,,,,,,,2,,,,,,
Buff-武器-精3燃狱齿轮-叠层冲击力,,,,,,,,,,2,,,,,,
Buff-武器-精4燃狱齿轮-叠层冲击力,,,,,,,,,,2,,,,,,
Buff-武器-精5燃狱齿轮-叠层冲击力,,,,,,,,,,2,,,,,,
Buff-武器-精1拘缚者,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精2拘缚者,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精3拘缚者,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精4拘缚者,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精5拘缚者,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精1焰心桂冠-冲击力提升,,,,,,,,,,7|9,,,,,,
Buff-武器-精2焰心桂冠-冲击力提升,,,,,,,,,,7|9,,,,,,
Buff-武器-精3焰心桂冠-冲击力提升,,,,,,,,,,7|9,,,,,,
Buff-武器-精4焰心桂冠-冲击力提升,,,,,,,,,,7|9,,,,,,
Buff-武器-精5焰心桂冠-冲击力提升,,,,,,,,,,7|9,,,,,,
Buff-武器-精1焰心桂冠-受暴伤提升,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精2焰心桂冠-受暴伤提升,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精3焰心桂冠-受暴伤提升,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精4焰心桂冠-受暴伤提升,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精5焰心桂冠-受暴伤提升,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精1玉壶青冰-普攻加冲击,,,,,,,,,,0,,,,,,
Buff-武器-精2玉壶青冰-普攻加冲击,,,,,,,,,,0,,,,,,
Buff-武器-精3玉壶青冰-普攻加冲击,,,,,,,,,,0,,,,,,
Buff-武器-精4玉壶青冰-普攻加冲击,,,,,,,,,,0,,,,,,
Buff-武器-精5玉壶青冰-普攻加冲击,,,,,,,,,,0,,,,,,
Buff-武器-精1玉壶青冰-15层后增伤,,,,,,,,,,0,,,,,,
Buff-武器-精2玉壶青冰-15层后增伤,,,,,,,,,,0,,,,,,
Buff-武器-精3玉壶青冰-15层后增伤,,,,,,,,,,0,,,,,,
Buff-武器-精4玉壶青冰-15层后增伤,,,,,,,,,,0,,,,,,
Buff-武器-精5玉壶青冰-15层后增伤,,,,,,,,,,0,,,,,,
Buff-武器-精1贵重骨核-75%以上,,,,,,,,,,,,,,,,
Buff-武器-精2贵重骨核-75%以上,,,,,,,,,,,,,,,,
Buff-武器-精3贵重骨核-75%以上,,,,,,,,,,,,,,,,
Buff-武器-精4贵重骨核-75%以上,,,,,,,,,,,,,,,,
Buff-武器-精5贵重骨核-75%以上,,,,,,,,,,,,,,,,
Buff-武器-精1贵重骨核-50%以上,,,,,,,,,,,,,,,,
Buff-武器-精2贵重骨核-50%以上,,,,,,,,,,,,,,,,
Buff-武器-精3贵重骨核-50%以上,,,,,,,,,,,,,,,,
Buff-武器-精4贵重骨核-50%以上,,,,,,,,,,,,,,,,
Buff-武器-精5贵重骨核-50%以上,,,,,,,,,,,,,,,,
Buff-武器-精1人为刀俎,,,,,,,,,,,,,,,,
Buff-武器-精2人为刀俎,,,,,,,,,,,,,,,,
Buff-武器-精3人为刀俎,,,,,,,,,,,,,,,,
Buff-武器-精4人为刀俎,,,,,,,,,,,,,,,,
Buff-武器-精5人为刀俎,,,,,,,,,,,,,,,,
Buff-武器-精1德玛拉电池II型-电伤,,,,,,,,,,,,,,,,
Buff-武器-精2德玛拉电池II型-电伤,,,,,,,,,,,,,,,,
Buff-武器-精3德玛拉电池II型-电伤,,,,,,,,,,,,,,,,
Buff-武器-精4德玛拉电池II型-电伤,,,,,,,,,,,,,,,,
Buff-武器-精5德玛拉电池II型-电伤,,,,,,,,,,,,,,,,
Buff-武器-精1德玛拉电池II型-能量获得效率,,,,,,,,,,4|7|8|9,,,,,,
Buff-武器-精2德玛拉电池II型-能量获得效率,,,,,,,,,,4|7|8|9,,,,,,
Buff-武器-精3德玛拉电池II型-能量获得效率,,,,,,,,,,4|7|8|9,,,,,,
Buff-武器-精4德玛拉电池II型-能量获得效率,,,,,,,,,,4|7|8|9,,,,,,
Buff-武器-精5德玛拉电池II型-能量获得效率,,,,,,,,,,4|7|8|9,,,,,,
Buff-武器-精1硫磺石,,,,,,,,,,0|3|4,,,,,,
Buff-武器-精2硫磺石,,,,,,,,,,0|3|4,,,,,,
Buff-武器-精3硫磺石,,,,,,,,,,0|3|4,,,,,,
Buff-武器-精4硫磺石,,,,,,,,,,0|3|4,,,,,,
Buff-武器-精5硫磺石,,,,,,,,,,0|3|4,,,,,,
Buff-角色-苍角-额外能力,,,,,,,,,,,,,,,,
Buff-角色-青衣-核心被动-失衡易伤,1251_SNA_1|1251_SNA_2,,,,,,,,,,,,,,,
Buff-角色-青衣-额外能力-失衡效率,,,,,,,,,,0,,,,,,
Buff-角色-青衣-额外能力-冲击转攻击,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-角色-11号-核心被动,,,,,,,,,,0|3,1,,,,,
Buff-角色-11号-组队被动-常驻,,,,,,,,,,,,,,,,
Buff-角色-11号-组队被动-失衡,,,,,,,,,,,,,,,,
Buff-角色-雅-终结技-冰伤,,,,,,,,,,6,,,,,,
Buff-角色-雅-核心被动-冰焰,,,,,,,,5,,,,,,,,
Buff-角色-雅-核心被动-霜灼,,,,,,,,,,,,,,,,
Buff-角色-雅-组队被动-普攻增伤,1091_SNA_1|1091_SNA_2|1091_SNA_3,,,,,,,,,,,,,,,
Buff-角色-雅-组队被动-无视冰抗,,,,,,,,,,,,,,,,
Buff-角色-露西-特殊技-攻击力,,,,,,,,,,,,,,,,
Buff-角色-露西-长按特殊技-攻击力,,,,,,,,,,,,,,,,
Buff-角色-露西-连携技-攻击力,,,,,,,,,,,,,,,,
Buff-角色-露西-终结技-攻击力,,,,,,,,,,,,,,,,
Buff-角色-派派-组队被动-积蓄效率,,,,,,,,,,,,,,,,
Buff-角色-派派-组队被动-全队增伤,,,,,,,,,,,,,,,,
Buff-角色-柏妮思-组队被动-延长灼烧,,,,,,,,,,,,,,,,
Buff-角色-丽娜-核心被动-穿透率,,,,,,,,,,,,,,,,
Buff-角色-丽娜-组队被动-增伤,,,,,,,,,,,,,,,,
Buff-角色-丽娜-组队被动-延长感电,,,,,,,,,,,,,,,,
Buff-音擎-精1霰落星殿-暴伤,,,,,,,,,,,,,,,,
Buff-音擎-精2霰落星殿-暴伤,,,,,,,,,,,,,,,,
Buff-音擎-精3霰落星殿-暴伤,,,,,,,,,,,,,,,,
Buff-音擎-精4霰落星殿-暴伤,,,,,,,,,,,,,,,,
Buff-音擎-精5霰落星殿-暴伤,,,,,,,,,,,,,,,,
Buff-音擎-精1霰落星殿-叠层冰伤,,,,,,,,,,,,,,,,
Buff-音擎-精2霰落星殿-叠层冰伤,,,,,,,,,,,,,,,,
Buff-音擎-精3霰落星殿-叠层冰伤,,,,,,,,,,,,,,,,
Buff-音擎-精4霰落星殿-叠层冰伤,,,,,,,,,,,,,,,,
Buff-音擎-精5霰落星殿-叠层冰伤,,,,,,,,,,,,,,,,
Buff-驱动盘-折枝剑歌-暴伤,,,,,,,,,,,,,,,,
Buff-驱动盘-折枝剑歌-暴击率,,,,,,,,,,,,,,,,
Buff-异常-烈霜霜寒,,,,,,,,,,,,,,,,
Buff-角色-青衣-核心被动-额外电压补偿,,,,,,,,,,,,,,,,
Buff-驱动盘-自由蓝调-物理,,,,,,,,,,,,,,,,
Buff-驱动盘-自由蓝调-火,,,,,,,,,,,,,,,,
Buff-驱动盘-自由蓝调-冰,,,,,,,,,,,,,,,,
Buff-驱动盘-自由蓝调-电,,,,,,,,,,,,,,,,
Buff-驱动盘-自由蓝调-以太,,,,,,,,,,,,,,,,
Buff-驱动盘-自由蓝调-烈霜,,,,,,,,,,,,,,,,
Buff-驱动盘-河豚电音-终结技伤害提升,,,,,,,,,,,,,,,,
Buff-驱动盘-河豚电音-攻击力提升,,,,,,,,,,6,,,,,,
Buff-驱动盘-静听嘉音-嘉音,,,,,,,,,,7,,,,,,
Buff-驱动盘-静听嘉音-增伤,,,,,,,,,,,,,,,,
Buff-驱动盘-摇摆爵士-全队增伤,,,,,,,,,,,,,,,,
Buff-驱动盘-激素朋克-全局攻击力,,,,,,,,,,,,,,,,
Buff-驱动盘-混沌爵士-火电伤,,,,,,,,,,,,,,,,
Buff-驱动盘-混沌爵士-前台增伤,,,,,,,,,,,,,,,,
Buff-驱动盘-原始朋克-全队增伤,,,,,,,,,,,,,,,,
Buff-驱动盘-獠牙重金属-增伤,,,,,,,,,,,,,,,,
Buff-武器-精1啜泣摇篮-后台回能,,,,,,,,,,,,,,,,
Buff-武器-精1啜泣摇篮-全队增伤,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精1啜泣摇篮-全队增伤自增长,,,,,,,,,,,,,,,,
Buff-武器-精2啜泣摇篮-后台回能,,,,,,,,,,,,,,,,
Buff-武器-精2啜泣摇篮-全队增伤,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精2啜泣摇篮-全队增伤自增长,,,,,,,,,,,,,,,,
Buff-武器-精3啜泣摇篮-后台回能,,,,,,,,,,,,,,,,
Buff-武器-精3啜泣摇篮-全队增伤,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精3啜泣摇篮-全队增伤自增长,,,,,,,,,,,,,,,,
Buff-武器-精4啜泣摇篮-后台回能,,,,,,,,,,,,,,,,
Buff-武器-精4啜泣摇篮-全队增伤,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精4啜泣摇篮-全队增伤自增长,,,,,,,,,,,,,,,,
Buff-武器-精5啜泣摇篮-后台回能,,,,,,,,,,,,,,,,
Buff-武器-精5啜泣摇篮-全队增伤,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精5啜泣摇篮-全队增伤自增长,,,,,,,,,,,,,,,,
Buff-武器-精1时光切片-回能回喧响,,,,,,,,,,,,,,,,
Buff-武器-精2时光切片-回能回喧响,,,,,,,,,,,,,,,,
Buff-武器-精3时光切片-回能回喧响,,,,,,,,,,,,,,,,
Buff-武器-精4时光切片-回能回喧响,,,,,,,,,,,,,,,,
Buff-武器-精5时光切片-回能回喧响,,,,,,,,,,,,,,,,
Buff-武器-精1聚宝箱-回能,,,,,,,,,,2|5|6,4,,,,,
Buff-武器-精1聚宝箱-全队增伤,,,,,,,,,,2|5|6,4,,,,,
Buff-武器-精2聚宝箱-回能,,,,,,,,,,2|5|6,4,,,,,
Buff-武器-精2聚宝箱-全队增伤,,,,,,,,,,2|5|6,4,,,,,
Buff-武器-精3聚宝箱-回能,,,,,,,,,,2|5|6,4,,,,,
Buff-武器-精3聚宝箱-全队增伤,,,,,,,,,,2|5|6,4,,,,,
Buff-武器-精4聚宝箱-回能,,,,,,,,,,2|5|6,4,,,,,
Buff-武器-精4聚宝箱-全队增伤,,,,,,,,,,2|5|6,4,,,,,
Buff-武器-精5聚宝箱-回能,,,,,,,,,,2|5|6,4,,,,,
Buff-武器-精5聚宝箱-全队增伤,,,,,,,,,,2|5|6,4,,,,,
Buff-武器-精1好斗的阿炮-全局攻击力,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精2好斗的阿炮-全局攻击力,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精3好斗的阿炮-全局攻击力,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精4好斗的阿炮-全局攻击力,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精5好斗的阿炮-全局攻击力,,,,,,,,,,,0|1|2|3|4|5,,,,,
Buff-武器-精1逍遥游球-全队暴击率,,,,,,,,,,,,,,,,
Buff-武器-精2逍遥游球-全队暴击率,,,,,,,,,,,,,,,,
Buff-武器-精3逍遥游球-全队暴击率,,,,,,,,,,,,,,,,
Buff-武器-精4逍遥游球-全队暴击率,,,,,,,,,,,,,,,,
Buff-武器-精5逍遥游球-全队暴击率,,,,,,,,,,,,,,,,
Buff-武器-精1残响Ⅰ型-全队冲击力,,,,,,,,,,2,,,,,,
Buff-武器-精2残响Ⅰ型-全队冲击力,,,,,,,,,,2,,,,,,
Buff-武器-精3残响Ⅰ型-全队冲击力,,,,,,,,,,2,,,,,,
Buff-武器-精4残响Ⅰ型-全队冲击力,,,,,,,,,,2,,,,,,
Buff-武器-精5残响Ⅰ型-全队冲击力,,,,,,,,,,2,,,,,,
Buff-武器-精1残响II型-全队掌控精通,,,,,,,,,,2|5,,,,,,
Buff-武器-精2残响II型-全队掌控精通,,,,,,,,,,2|5,,,,,,
Buff-武器-精3残响II型-全队掌控精通,,,,,,,,,,2|5,,,,,,
Buff-武器-精4残响II型-全队掌控精通,,,,,,,,,,2|5,,,,,,
Buff-武器-精5残响II型-全队掌控精通,,,,,,,,,,2|5,,,,,,
Buff-武器-精1残响III型-全队攻击力,,,,,,,,,,5|6,,,,,,
Buff-武器-精2残响III型-全队攻击力,,,,,,,,,,5|6,,,,,,
Buff-武器-精3残响III型-全队攻击力,,,,,,,,,,5|6,,,,,,
Buff-武器-精4残响III型-全队攻击力,,,,,,,,,,5|6,,,,,,
Buff-武器-精5残响III型-全队攻击力,,,,,,,,,,5|6,,,,,,
Buff-角色-妮可-核心被动-减防,,,,,,,,,,,,,,,,
Buff-角色-妮可-组队被动以太-增伤,,,,,,,,,,,,,,,,
Buff-角色-凯撒-大招-命中护盾增加失衡值,,,,,,,,,,,,,,,,
Buff-角色-凯撒-核心被动-攻击力,,,,,,,,,,,,,,,,
Buff-角色-凯撒-组队被动-增伤,1071_E_A|1071_E_B|1071_E_EX_A|1071_E_EX_B|1071_SNA,,,,,,,,,,,,,,,
Buff-角色-耀佳音-咏叹华彩,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-核心被动-攻击力,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-组队被动-触发器,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-快支管理器-触发器,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-震音管理器-触发器,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-1画-减防,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-1画-无敌效果,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-2画-额外攻击力,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-4画-强攻特效触发器,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-4画-异常特效,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-4画-击破特效,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-6画-震音音簇暴击率提升,,,,,,,,,,,,,,,,
Buff-角色-耀佳音-6画-重击暴击率提升,,,,,,,,,,,,,,,,
Buff-角色-柏妮思-核心被动-燃油特调触发器,,,,,,,,,,,,,,,,
Buff-角色-柏妮思-核心被动-余烬增伤,,,,,,,,,,,,,,,,
Buff-角色-柏妮思-影画2-热意洞穿,,,,,,,,,,,,,,,,
Buff-角色-柏妮思-影画4-招式暴击率,,,,,,,,,,2|9,,,,,,
Buff-武器-精1灼心摇壶-回能,,,,,,,,,,,,,,,,
Buff-武器-精1灼心摇壶-增伤,,,,,,,,,,,,,,,,
Buff-武器-精1灼心摇壶-精通,,,,,,,,,,,,,,,,
Buff-武器-精2灼心摇壶-回能,,,,,,,,,,,,,,,,
Buff-武器-精2灼心摇壶-增伤,,,,,,,,,,,,,,,,
Buff-武器-精2灼心摇壶-精通,,,,,,,,,,,,,,,,
Buff-武器-精3灼心摇壶-回能,,,,,,,,,,,,,,,,
Buff-武器-精3灼心摇壶-增伤,,,,,,,,,,,,,,,,
Buff-武器-精3灼心摇壶-精通,,,,,,,,,,,,,,,,
Buff-武器-精4灼心摇壶-回能,,,,,,,,,,,,,,,,
Buff-武器-精4灼心摇壶-增伤,,,,,,,,,,,,,,,,
Buff-武器-精4灼心摇壶-精通,,,,,,,,,,,,,,,,
Buff-武器-精5灼心摇壶-回能,,,,,,,,,,,,,,,,
Buff-武器-精5灼心摇壶-增伤,,,,,,,,,,,,,,,,
Buff-武器-精5灼心摇壶-精通,,,,,,,,,,,,,,,,
Buff-角色-格莉丝-核心被动-电能,,,,,,,,,,,,,,,,
Buff-角色-格莉丝-组队被动-感电伤害,,,,,,,,,,,,,,,,
Buff-角色-格莉丝-影画2-双抗降低,,,,,,,,,,,,,,,,
Buff-武器-精1嵌合编译器-攻击力,,,,,,,,,,,,,,,,
Buff-武器-精1嵌合编译器-精通,,,,,,,,,,1|2,,,,,,
Buff-武器-精2嵌合编译器-攻击力,,,,,,,,,,,,,,,,
Buff-武器-精2嵌合编译器-精通,,,,,,,,,,1|2,,,,,,
Buff-武器-精3嵌合编译器-攻击力,,,,,,,,,,,,,,,,
Buff-武器-精3嵌合编译器-精通,,,,,,,,,,1|2,,,,,,
Buff-武器-精4嵌合编译器-攻击力,,,,,,,,,,,,,,,,
Buff-武器-精4嵌合编译器-精通,,,,,,,,,,1|2,,,,,,
Buff-武器-精5嵌合编译器-攻击力,,,,,,,,,,,,,,,,
Buff-武器-精5嵌合编译器-精通,,,,,,,,,,1|2,,,,,,
Buff-武器-精1防暴者Ⅵ型-暴击率,,,,,,,,,,,,,,,,
Buff-武器-精2防暴者Ⅵ型-暴击率,,,,,,,,,,,,,,,,
Buff-武器-精3防暴者Ⅵ型-暴击率,,,,,,,,,,,,,,,,
Buff-武器-精4防暴者Ⅵ型-暴击率,,,,,,,,,,,,,,,,
Buff-武器-精5防暴者Ⅵ型-暴击率,,,,,,,,,,,,,,,,
Buff-武器-精1防暴者Ⅵ型-普攻增伤,,,,,,,,,,,,,,,,
Buff-武器-精2防暴者Ⅵ型-普攻增伤,,,,,,,,,,,,,,,,
Buff-武器-精3防暴者Ⅵ型-普攻增伤,,,,,,,,,,,,,,,,
Buff-武器-精4防暴者Ⅵ型-普攻增伤,,,,,,,,,,,,,,,,
Buff-武器-精5防暴者Ⅵ型-普攻增伤,,,,,,,,,,,,,,,,
Buff-角色-朱鸢-核心被动-强化普攻增伤,"
1241_SRA_F_ET|1241_SRA_B_ET|1241_SRA_S_ET|1241_SNA_1_A|1241_SNA_1_A_α|1241_SNA_1_A_β|1241_SNA_2_A|1241_SNA_3_A|1241_SNA_4_A|1241_SNA_10_A",,,,,,,,,,,,,,,
Buff-角色-朱鸢-核心被动-失衡普攻增伤,,,,,,,,,,,,,,,,
Buff-角色-朱鸢-额外能力-暴击率,,,,,,,,,,2|5|6,,,,,,
Buff-角色-朱鸢-2画-强化普攻增伤,"
1241_SRA_F_ET|1241_SRA_B_ET|1241_SRA_S_ET|1241_SNA_1_A|1241_SNA_1_A_α|1241_SNA_1_A_β|1241_SNA_2_A|1241_SNA_3_A|1241_SNA_4_A|1241_SNA_10_A",,,,,,,,,,,,,,,
Buff-角色-朱鸢-4画-无视以太抗,"
1241_SRA_F_ET|1241_SRA_B_ET|1241_SRA_S_ET|1241_SNA_1_A|1241_SNA_1_A_α|1241_SNA_1_A_β|1241_SNA_2_A|1241_SNA_3_A|1241_SNA_4_A|1241_SNA_10_A",,,,,,,,,,,,,,,
Buff-角色-朱鸢-6画-降低能耗,,,,,,,,,,,,,,,,
Buff-角色-格丽斯-4画-能量获取效率,,,,,,,,,,,,,,,,
Buff-角色-格丽斯-6画-特殊技增伤,,,,,,,,,,,,,,,,
Buff-角色-伊芙琳-核心被动-暴击率提升,,,,,,,,,,,,,,,,
Buff-角色-伊芙琳-组队被动-连携技大招增伤,,,,,,,,,,5|6,,,,,,
Buff-角色-伊芙琳-组队被动-连携技大招倍率增加,,,,,,,,,,,,,,,,
Buff-角色-伊芙琳-1画-无视防御力,,,,,,,,,,,,,,,,
Buff-角色-伊芙琳-1画-无视防御力扩散,1321_E_2|1321_E_EX|1321_E_EX_A,,,,,,,,,,,,,,,
Buff-角色-伊芙琳-1画-禁锢触发器,,,,,,,,,,,,,,,,
Buff-角色-伊芙琳-2画-局内大攻击,,,,,,,,,,,,,,,,
Buff-角色-伊芙琳-2画-返还撩火触发器,1321_SNA_1|1321_SNA_2,,,,,,,,,,,,,,,
Buff-角色-伊芙琳-2画-连携技打断等级提升,,,,,,,,,,,,,,,,
Buff-角色-伊芙琳-4画-护盾给暴伤,,,,,,,,,,,,,,,,
Buff-角色-伊芙琳-4画-护盾,,,,,,,,,,5|6,,,,,,
Buff-角色-伊芙琳-6画-额外追击触发器,,,,,,,,,,5|6,,,,,,
Buff-武器-精1心弦夜响-暴伤,,,,,,,,,,,,,,,,
Buff-武器-精2心弦夜响-暴伤,,,,,,,,,,,,,,,,
Buff-武器-精3心弦夜响-暴伤,,,,,,,,,,,,,,,,
Buff-武器-精4心弦夜响-暴伤,,,,,,,,,,,,,,,,
Buff-武器-精5心弦夜响-暴伤,,,,,,,,,,,,,,,,
Buff-武器-精1心弦夜响-无视火抗,,,,,,,,,,,,,,,,
Buff-武器-精2心弦夜响-无视火抗,,,,,,,,,,,,,,,,
Buff-武器-精3心弦夜响-无视火抗,,,,,,,,,,,,,,,,
Buff-武器-精4心弦夜响-无视火抗,,,,,,,,,,,,,,,,
Buff-武器-精5心弦夜响-无视火抗,,,,,,,,,,,,,,,,
Buff-武器-精1心弦夜响-心弦触发器,,,,,,,,,,,,,,,,
Buff-武器-精2心弦夜响-心弦触发器,,,,,,,,,,,,,,,,
Buff-武器-精3心弦夜响-心弦触发器,,,,,,,,,,,,,,,,
Buff-武器-精4心弦夜响-心弦触发器,,,,,,,,,,,,,,,,
Buff-武器-精5心弦夜响-心弦触发器,,,,,,,,,,,,,,,,
Buff-角色-悠真-核心被动-特殊冲刺攻击暴击率,1201_SRA_1|1201_SRA_2|1201_SRA_3,,,,,,,,,,,,,,,
Buff-角色-悠真-核心被动-特殊冲刺攻击暴伤,1201_SRA_1|1201_SRA_2|1201_SRA_3,,,,,,,,,,,,,,,
Buff-角色-悠真-组队被动,,,,,,,,,,,,,,,,
Buff-角色-悠真-2画-特殊冲刺攻击增伤,,,,,,,,,,5|6,,,,,,
Buff-角色-悠真-6画-无视电抗,,,,,,,,,,,,,,,,
Buff-武器-精1残心青囊-暴击率,,,,,,,,,,,,,,,,
Buff-武器-精2残心青囊-暴击率,,,,,,,,,,,,,,,,
Buff-武器-精3残心青囊-暴击率,,,,,,,,,,,,,,,,
Buff-武器-精4残心青囊-暴击率,,,,,,,,,,,,,,,,
Buff-武器-精5残心青囊-暴击率,,,,,,,,,,,,,,,,
Buff-武器-精1残心青囊-电属性伤害,,,,,,,,,,3,,,,,,
Buff-武器-精2残心青囊-电属性伤害,,,,,,,,,,3,,,,,,
Buff-武器-精3残心青囊-电属性伤害,,,,,,,,,,3,,,,,,
Buff-武器-精4残心青囊-电属性伤害,,,,,,,,,,3,,,,,,
Buff-武器-精5残心青囊-电属性伤害,,,,,,,,,,3,,,,,,
Buff-武器-精1残心青囊-条件暴击率,,,,,,,,,,,,,,,,
Buff-武器-精2残心青囊-条件暴击率,,,,,,,,,,,,,,,,
Buff-武器-精3残心青囊-条件暴击率,,,,,,,,,,,,,,,,
Buff-武器-精4残心青囊-条件暴击率,,,,,,,,,,,,,,,,
Buff-武器-精5残心青囊-条件暴击率,,,,,,,,,,,,,,,,
Buff-角色-艾莲-1画-暴击率,,,,,,,,,,,,,,,,
Buff-角色-艾莲-2画-强化特殊技额外爆伤,,,,,,,,,,,,,,,,
Buff-角色-艾莲-4画-能量回复,,,,,,,,,,,,,,,,
Buff-角色-艾莲-6画-穿透率,,,,,,,,,,,,,,,,
Buff-角色-艾莲-6画-冲刺蓄力剪增伤,,,,,,,,,,,,,,,,
Buff-角色-11号-1画-回能,,,,,,,,,,,,,,,,
Buff-角色-11号-2画-火力镇压增伤,1041_SNA_1|1041_SNA_2|1041_SNA_3|1041_SNA_4|1041_SRA,,,,,,,,,,,,,,,
Buff-角色-11号-6画-火力镇压无视火抗,,,,,,,,,,,,,,,,
Buff-角色-艾莲-快蓄触发器,,,,,,,,,,,,,,,,
Buff-角色-柏妮思-组队被动-火积蓄加成,1171_SNA_1|1171_SNA_2|1171_E_EX_1|1171_E_EX_2|1171_E_EX_A_1|1171_E_EX_A_2|1171_Core_Passive,,,,,,,,,,,,,,,
Buff-角色-柏妮思-1画-余烬倍率提升,,,,,,,,,,,,,,,,
Buff-角色-柏妮思-1画-余烬火积蓄加成,,,,,,,,,,,,,,,,
Buff-角色-柏妮思-4画-双喷延时触发器,,,,,,,,,,,,,,,,
Buff-武器-精1家政员-后场时能量回复,,,,,,,,,,,,,,,,
Buff-武器-精2家政员-后场时能量回复,,,,,,,,,,,,,,,,
Buff-武器-精3家政员-后场时能量回复,,,,,,,,,,,,,,,,
Buff-武器-精4家政员-后场时能量回复,,,,,,,,,,,,,,,,
Buff-武器-精5家政员-后场时能量回复,,,,,,,,,,,,,,,,
Buff-武器-精1家政员-物理增伤,,,,,,,,,,2,,,,,,
Buff-武器-精2家政员-物理增伤,,,,,,,,,,2,,,,,,
Buff-武器-精3家政员-物理增伤,,,,,,,,,,2,,,,,,
Buff-武器-精4家政员-物理增伤,,,,,,,,,,2,,,,,,
Buff-武器-精5家政员-物理增伤,,,,,,,,,,2,,,,,,
Buff-武器-精1旋钻机-赤轴-电属性增伤,,,,,,,,,,2|5,,,,,,
Buff-武器-精2旋钻机-赤轴-电属性增伤,,,,,,,,,,2|5,,,,,,
Buff-武器-精3旋钻机-赤轴-电属性增伤,,,,,,,,,,2|5,,,,,,
Buff-武器-精4旋钻机-赤轴-电属性增伤,,,,,,,,,,2|5,,,,,,
Buff-武器-精5旋钻机-赤轴-电属性增伤,,,,,,,,,,2|5,,,,,,
Buff-武器-精1星徽引擎-攻击力提升,,,,,,,,,,4|7,,,,,,
Buff-武器-精2星徽引擎-攻击力提升,,,,,,,,,,4|7,,,,,,
Buff-武器-精3星徽引擎-攻击力提升,,,,,,,,,,4|7,,,,,,
Buff-武器-精4星徽引擎-攻击力提升,,,,,,,,,,4|7,,,,,,
Buff-武器-精5星徽引擎-攻击力提升,,,,,,,,,,4|7,,,,,,
Buff-武器-精1星鎏金花信-攻击力提升,,,,,,,,,,,,,,,,
Buff-武器-精2星鎏金花信-攻击力提升,,,,,,,,,,,,,,,,
Buff-武器-精3星鎏金花信-攻击力提升,,,,,,,,,,,,,,,,
Buff-武器-精4星鎏金花信-攻击力提升,,,,,,,,,,,,,,,,
Buff-武器-精5星鎏金花信-攻击力提升,,,,,,,,,,,,,,,,
Buff-武器-精1星鎏金花信-强化特殊技增伤,,,,,,,,,,,,,,,,
Buff-武器-精2星鎏金花信-强化特殊技增伤,,,,,,,,,,,,,,,,
Buff-武器-精3星鎏金花信-强化特殊技增伤,,,,,,,,,,,,,,,,
Buff-武器-精4星鎏金花信-强化特殊技增伤,,,,,,,,,,,,,,,,
Buff-武器-精5星鎏金花信-强化特殊技增伤,,,,,,,,,,,,,,,,
Buff-武器-精1星强音热望-攻击力提升,,,,,,,,,,2|5,,,,,,
Buff-武器-精2星强音热望-攻击力提升,,,,,,,,,,2|5,,,,,,
Buff-武器-精3星强音热望-攻击力提升,,,,,,,,,,2|5,,,,,,
Buff-武器-精4星强音热望-攻击力提升,,,,,,,,,,2|5,,,,,,
Buff-武器-精5星强音热望-攻击力提升,,,,,,,,,,2|5,,,,,,
Buff-武器-精1星强音热望-额外攻击力提升,,,,,,,,,,,,,,,,
Buff-武器-精2星强音热望-额外攻击力提升,,,,,,,,,,,,,,,,
Buff-武器-精3星强音热望-额外攻击力提升,,,,,,,,,,,,,,,,
Buff-武器-精4星强音热望-额外攻击力提升,,,,,,,,,,,,,,,,
Buff-武器-精5星强音热望-额外攻击力提升,,,,,,,,,,,,,,,,
Buff-角色-扳机-核心被动-失衡易伤,,,,,,,,,,,,,,,,
Buff-角色-扳机-额外能力-追加攻击失衡值提升,,,,,,,,,,,,,,,,
Buff-角色-扳机-协同攻击-触发器,,,,,,,,,,,,,,,,
Buff-角色-扳机-协战状态-触发器,,,,,,,,,,,,,,,,
Buff-角色-扳机-1画-失衡易伤提升,,,,,,,,,,,,,,,,
Buff-角色-扳机-1画-决意值提升触发器,,,,,,,,,,,,,,,,
Buff-角色-扳机-2画-猎眸,,,,,,,,,,,,,,,,
Buff-角色-扳机-4画-断离触发器,,,,,,,,,,,,,,,,
Buff-角色-扳机-6画-破甲凶弹触发器,,,,,,,,,,,,,,,,
Buff-角色-零号·安比-银星触发器,,,,,,,,,,,3,,,,,
Buff-角色-零号·安比-核心被动-增伤,,,,,,,,,,,,,,,,
Buff-角色-零号·安比-核心被动-受暴伤增加,,,,,,,,,,,,,,,,
Buff-角色-零号·安比-组队被动-暴击率提升,,,,,,,,,,,,,,,,
Buff-角色-零号·安比-组队被动-全队对银星目标增伤,,,,,,,,,,,,,,,,
Buff-角色-零号·安比-2画-暴击率提升,,,,,,,,,,,,,,,,
Buff-角色-零号·安比-4画-无视电抗,,,,,,,,,,,,,,,,
Buff-武器-精1牺牲洁纯-常驻暴伤,,,,,,,,,,,,,,,,
Buff-武器-精2牺牲洁纯-常驻暴伤,,,,,,,,,,,,,,,,
Buff-武器-精3牺牲洁纯-常驻暴伤,,,,,,,,,,,,,,,,
Buff-武器-精4牺牲洁纯-常驻暴伤,,,,,,,,,,,,,,,,
Buff-武器-精5牺牲洁纯-常驻暴伤,,,,,,,,,,,,,,,,
Buff-武器-精1牺牲洁纯-触发暴伤,,,,,,,,,,,,,,,,
Buff-武器-精2牺牲洁纯-触发暴伤,,,,,,,,,,,,,,,,
Buff-武器-精3牺牲洁纯-触发暴伤,,,,,,,,,,,,,,,,
Buff-武器-精4牺牲洁纯-触发暴伤,,,,,,,,,,,,,,,,
Buff-武器-精5牺牲洁纯-触发暴伤,,,,,,,,,,,,,,,,
Buff-武器-精1牺牲洁纯-满层电伤,,,,,,,,,,,,,,,,
Buff-武器-精2牺牲洁纯-满层电伤,,,,,,,,,,,,,,,,
Buff-武器-精3牺牲洁纯-满层电伤,,,,,,,,,,,,,,,,
Buff-武器-精4牺牲洁纯-满层电伤,,,,,,,,,,,,,,,,
Buff-武器-精5牺牲洁纯-满层电伤,,,,,,,,,,,,,,,,
Buff-驱动盘-如影相随-二件套,,,,,,,,,,,,,,,,
Buff-驱动盘-如影相随-四件套,,,,,,,,,,,,,,,,
Buff-武器-精1索魂影眸-减防,,,,,,,,,,,,,,,,
Buff-武器-精2索魂影眸-减防,,,,,,,,,,,,,,,,
Buff-武器-精3索魂影眸-减防,,,,,,,,,,,,,,,,
Buff-武器-精4索魂影眸-减防,,,,,,,,,,,,,,,,
Buff-武器-精5索魂影眸-减防,,,,,,,,,,,,,,,,
Buff-武器-精1索魂影眸-魂锁,,,,,,,,,,,,,,,,
Buff-武器-精2索魂影眸-魂锁,,,,,,,,,,,,,,,,
Buff-武器-精3索魂影眸-魂锁,,,,,,,,,,,,,,,,
Buff-武器-精4索魂影眸-魂锁,,,,,,,,,,,,,,,,
Buff-武器-精5索魂影眸-魂锁,,,,,,,,,,,,,,,,
Buff-武器-精1索魂影眸-冲击力,,,,,,,,,,,,,,,,
Buff-武器-精2索魂影眸-冲击力,,,,,,,,,,,,,,,,
Buff-武器-精3索魂影眸-冲击力,,,,,,,,,,,,,,,,
Buff-武器-精4索魂影眸-冲击力,,,,,,,,,,,,,,,,
Buff-武器-精5索魂影眸-冲击力,,,,,,,,,,,,,,,,
Buff-角色-柳-架势-上弦,,,,,,,,,,,,,,,,
Buff-角色-柳-架势-下弦,,,,,,,,,,,,,,,,
Buff-角色-柳-森罗万象,,,,,,,,,,,,,,,,
Buff-角色-柳-核心被动-紊乱倍率提升,,,,,,,,,,2,,,,,,
Buff-角色-柳-核心被动-电伤增幅,,,,,,,,,,2,,,,,,
Buff-角色-柳-额外能力-积蓄效率,,,,,,,,,,,,,,,,
Buff-角色-柳-极性紊乱触发器,,,,,,,,,,,,,,,,
Buff-角色-柳-1画-洞悉,,,,,,,,,,,,,,,,
Buff-角色-柳-1画-精通增幅,,,,,,,,,,,,,,,,
Buff-角色-柳-2画-积蓄效率,1221_E_EX_1,,,,,,,,,,,,,,,
Buff-角色-柳-4画-识破,,,,,,,,,,,,,,,,
Buff-角色-柳-6画-特殊技伤害提升,,,,,,,,,,,,,,,,
Buff-角色-简-狂热状态触发器,,,,,,,,,,,,,,,,
Buff-角色-简-狂热-物理积蓄效率提升,,,,,,,,,,,,,,,,
Buff-角色-简-狂热-额外精通转攻击力,,,,,,,,,,,,,,,,
Buff-角色-简-核心被动-啮咬触发器,,,,,,,,,,,0,,,,,
Buff-角色-简-核心被动-啮咬-强击暴击率提升,,,,,,,,,,,,,,,,
Buff-角色-简-核心被动-啮咬-强击暴击伤害提升,,,,,,,,,,,,,,,,
Buff-角色-简-额外能力-物理异常积蓄效率提升,,,,,,,,,,,,,,,,
Buff-角色-简-额外能力-物理异常积蓄效率额外提升,,,,,,,,,,,,,,,,
Buff-角色-简-1画-狂热物理异常积蓄效率额外提升,,,,,,,,,,,,,,,,
Buff-角色-简-1画-精通转增伤,,,,,,,,,,,,,,,,
Buff-角色-简-2画-啮咬-强击无视防御与暴击伤害提升,,,,,,,,,,,,,,,,
Buff-角色-简-2画-啮咬-攻击无视防御,,,,,,,,,,,,,,,,
Buff-角色-简-4画-全队异常伤害提升,,,,,,,,,,,,,,,,
Buff-角色-简-6画-双暴提升,,,,,,,,,,,,,,,,
Buff-角色-简-6画-额外攻击触发器,,,,,,,,,,,,,,,,
Buff-角色-简-6画-额外攻击触发器,,,,,,,,,,,,,,,,
Buff-角色-薇薇安-协同攻击触发器,,,,,,,,,,,,,,,,
Buff-角色-薇薇安-羽毛结算触发器,,,,,,,,,,,,,,,,
Buff-角色-薇薇安-核心被动触发器,,,,,,,,,,,,,,,,
Buff-角色-薇薇安-预言触发器,,,,,,,,,,,,,,,,
Buff-角色-薇薇安-额外能力-协同攻击触发器,,,,,,,,,,,,,,,,
Buff-角色-薇薇安-额外能力-全队侵蚀伤害增加,,,,,,,,,,,,,,,,
Buff-角色-薇薇安-额外能力-侵蚀紊乱伤害提升,,,,,,,,,,,,,,,,
Buff-角色-薇薇安-1画-全属性异常和紊乱伤害提升,,,,,,,,,,,,,,,,
Buff-角色-薇薇安-2画-以太积蓄效率提升,,,,,,,,,,,,,,,,
Buff-角色-薇薇安-2画-异放全属性抗性穿透,,,,,,,,,,,,,,,,
Buff-角色-薇薇安-4画-悬落与落羽生花必暴,,,,,,,,,,,,,,,,
Buff-角色-薇薇安-4画-局内攻击力增幅,1331_SNA_2|1331_CoAttack_A,,,,,,,,,,,,,,,
Buff-角色-薇薇安-6画-以太伤害增加,,,,,,,,,,,,,,,,
Buff-驱动盘-法厄同之歌-四件套-以太伤害提高,,,,,,,,,,,,,,,,
Buff-驱动盘-法厄同之歌-四件套-精通增幅,,,,,,,,,,2,,,,,,
Buff-武器-精1飞鸟星梦-属性异常积蓄效率,,,,,,,,,,,,,,,,
Buff-武器-精2飞鸟星梦-属性异常积蓄效率,,,,,,,,,,,,,,,,
Buff-武器-精3飞鸟星梦-属性异常积蓄效率,,,,,,,,,,,,,,,,
Buff-武器-精4飞鸟星梦-属性异常积蓄效率,,,,,,,,,,,,,,,,
Buff-武器-精5飞鸟星梦-属性异常积蓄效率,,,,,,,,,,,,,,,,
Buff-武器-精1飞鸟星梦-精通增幅,,,,,,,,,,,,,,,,
Buff-武器-精2飞鸟星梦-精通增幅,,,,,,,,,,,,,,,,
Buff-武器-精3飞鸟星梦-精通增幅,,,,,,,,,,,,,,,,
Buff-武器-精4飞鸟星梦-精通增幅,,,,,,,,,,,,,,,,
Buff-武器-精5飞鸟星梦-精通增幅,,,,,,,,,,,,,,,,
Buff-武器-精1时流贤者-电积蓄效率提升,,,,,,,,,,,,,,,,
Buff-武器-精2时流贤者-电积蓄效率提升,,,,,,,,,,,,,,,,
Buff-武器-精3时流贤者-电积蓄效率提升,,,,,,,,,,,,,,,,
Buff-武器-精4时流贤者-电积蓄效率提升,,,,,,,,,,,,,,,,
Buff-武器-精5时流贤者-电积蓄效率提升,,,,,,,,,,,,,,,,
Buff-武器-精1时流贤者-精通提升,,,,,,,,,,,,,,,,
Buff-武器-精2时流贤者-精通提升,,,,,,,,,,,,,,,,
Buff-武器-精3时流贤者-精通提升,,,,,,,,,,,,,,,,
Buff-武器-精4时流贤者-精通提升,,,,,,,,,,,,,,,,
Buff-武器-精5时流贤者-精通提升,,,,,,,,,,,,,,,,
Buff-武器-精1时流贤者-装备者紊乱伤害提升,,,,,,,,,,,,,,,,
Buff-武器-精2时流贤者-装备者紊乱伤害提升,,,,,,,,,,,,,,,,
Buff-武器-精3时流贤者-装备者紊乱伤害提升,,,,,,,,,,,,,,,,
Buff-武器-精4时流贤者-装备者紊乱伤害提升,,,,,,,,,,,,,,,,
Buff-武器-精5时流贤者-装备者紊乱伤害提升,,,,,,,,,,,,,,,,
Buff-武器-精1淬锋钳刺-猎意,,,,,,,,,,,,,,,,
Buff-武器-精2淬锋钳刺-猎意,,,,,,,,,,,,,,,,
Buff-武器-精3淬锋钳刺-猎意,,,,,,,,,,,,,,,,
Buff-武器-精4淬锋钳刺-猎意,,,,,,,,,,,,,,,,
Buff-武器-精5淬锋钳刺-猎意,,,,,,,,,,,,,,,,
Buff-武器-精1淬锋钳刺-属性异常积蓄效率提升,,,,,,,,,,,,,,,,
Buff-武器-精2淬锋钳刺-属性异常积蓄效率提升,,,,,,,,,,,,,,,,
Buff-武器-精3淬锋钳刺-属性异常积蓄效率提升,,,,,,,,,,,,,,,,
Buff-武器-精4淬锋钳刺-属性异常积蓄效率提升,,,,,,,,,,,,,,,,
Buff-武器-精5淬锋钳刺-属性异常积蓄效率提升,,,,,,,,,,,,,,,,
Buff-武器-精1玲珑妆匣-回能,,,,,,,,,,5|7|8,,,,,,
Buff-武器-精2玲珑妆匣-回能,,,,,,,,,,5|7|8,,,,,,
Buff-武器-精3玲珑妆匣-回能,,,,,,,,,,5|7|8,,,,,,
Buff-武器-精4玲珑妆匣-回能,,,,,,,,,,5|7|8,,,,,,
Buff-武器-精5玲珑妆匣-回能,,,,,,,,,,5|7|8,,,,,,
Buff-武器-精1玲珑妆匣-全队增伤,,,,,,,,,,,,,,,,
Buff-武器-精2玲珑妆匣-全队增伤,,,,,,,,,,,,,,,,
Buff-武器-精3玲珑妆匣-全队增伤,,,,,,,,,,,,,,,,
Buff-武器-精4玲珑妆匣-全队增伤,,,,,,,,,,,,,,,,
Buff-武器-精5玲珑妆匣-全队增伤,,,,,,,,,,,,,,,,
Buff-武器-精1雨林饕客-局内攻击力,,,,,,,,,,,,,,,,
Buff-武器-精2雨林饕客-局内攻击力,,,,,,,,,,,,,,,,
Buff-武器-精3雨林饕客-局内攻击力,,,,,,,,,,,,,,,,
Buff-武器-精4雨林饕客-局内攻击力,,,,,,,,,,,,,,,,
Buff-武器-精5雨林饕客-局内攻击力,,,,,,,,,,,,,,,,
Buff-武器-精1双生泣星-精通增幅,,,,,,,,,,,,,,,,
Buff-武器-精2双生泣星-精通增幅,,,,,,,,,,,,,,,,
Buff-武器-精3双生泣星-精通增幅,,,,,,,,,,,,,,,,
Buff-武器-精4双生泣星-精通增幅,,,,,,,,,,,,,,,,
Buff-武器-精5双生泣星-精通增幅,,,,,,,,,,,,,,,,
Buff-武器-精1触电唇彩-攻击力与增伤,,,,,,,,,,,,,,,,
Buff-武器-精2触电唇彩-攻击力与增伤,,,,,,,,,,,,,,,,
Buff-武器-精3触电唇彩-攻击力与增伤,,,,,,,,,,,,,,,,
Buff-武器-精4触电唇彩-攻击力与增伤,,,,,,,,,,,,,,,,
Buff-武器-精5触电唇彩-攻击力与增伤,,,,,,,,,,,,,,,,
Buff-武器-精1轰鸣座驾-触发器,,,,,,,,,,2,,,,,,
Buff-武器-精2轰鸣座驾-触发器,,,,,,,,,,2,,,,,,
Buff-武器-精3轰鸣座驾-触发器,,,,,,,,,,2,,,,,,
Buff-武器-精4轰鸣座驾-触发器,,,,,,,,,,2,,,,,,
Buff-武器-精5轰鸣座驾-触发器,,,,,,,,,,2,,,,,,
Buff-武器-精1轰鸣座驾-攻击力,,,,,,,,,,,,,,,,
Buff-武器-精2轰鸣座驾-攻击力,,,,,,,,,,,,,,,,
Buff-武器-精3轰鸣座驾-攻击力,,,,,,,,,,,,,,,,
Buff-武器-精4轰鸣座驾-攻击力,,,,,,,,,,,,,,,,
Buff-武器-精5轰鸣座驾-攻击力,,,,,,,,,,,,,,,,
Buff-武器-精1轰鸣座驾-精通提升,,,,,,,,,,,,,,,,
Buff-武器-精2轰鸣座驾-精通提升,,,,,,,,,,,,,,,,
Buff-武器-精3轰鸣座驾-精通提升,,,,,,,,,,,,,,,,
Buff-武器-精4轰鸣座驾-精通提升,,,,,,,,,,,,,,,,
Buff-武器-精5轰鸣座驾-精通提升,,,,,,,,,,,,,,,,
Buff-武器-精1轰鸣座驾-属性异常积蓄,,,,,,,,,,,,,,,,
Buff-武器-精2轰鸣座驾-属性异常积蓄,,,,,,,,,,,,,,,,
Buff-武器-精3轰鸣座驾-属性异常积蓄,,,,,,,,,,,,,,,,
Buff-武器-精4轰鸣座驾-属性异常积蓄,,,,,,,,,,,,,,,,
Buff-武器-精5轰鸣座驾-属性异常积蓄,,,,,,,,,,,,,,,,
Buff-武器-精1「电磁暴」-壹式-异常掌控,,,,,,,,,,,,,,,,
Buff-武器-精2「电磁暴」-壹式-异常掌控,,,,,,,,,,,,,,,,
Buff-武器-精3「电磁暴」-壹式-异常掌控,,,,,,,,,,,,,,,,
Buff-武器-精4「电磁暴」-壹式-异常掌控,,,,,,,,,,,,,,,,
Buff-武器-精5「电磁暴」-壹式-异常掌控,,,,,,,,,,,,,,,,
Buff-武器-精1「电磁暴」-贰式-异常精通,,,,,,,,,,,,,,,,
Buff-武器-精2「电磁暴」-贰式-异常精通,,,,,,,,,,,,,,,,
Buff-武器-精3「电磁暴」-贰式-异常精通,,,,,,,,,,,,,,,,
Buff-武器-精4「电磁暴」-贰式-异常精通,,,,,,,,,,,,,,,,
Buff-武器-精5「电磁暴」-贰式-异常精通,,,,,,,,,,,,,,,,
Buff-武器-精1「电磁暴」-叁式-回能,,,,,,,,,,,,,,,,
Buff-武器-精2「电磁暴」-叁式-回能,,,,,,,,,,,,,,,,
Buff-武器-精3「电磁暴」-叁式-回能,,,,,,,,,,,,,,,,
Buff-武器-精4「电磁暴」-叁式-回能,,,,,,,,,,,,,,,,
Buff-武器-精5「电磁暴」-叁式-回能,,,,,,,,,,,,,,,,
Buff-角色-雨果-核心被动-暗渊回响,,,,,,,,,,5,,,,,,
Buff-角色-雨果-核心被动-单击破攻击力,,,,,,,,,,,,,,,,
Buff-角色-雨果-核心被动-双击破攻击力,,,,,,,,,,,,,,,,
Buff-角色-雨果-决算触发器,,,,,,,,,,,,,,,,
Buff-角色-雨果-决算倍率增幅,,,,,,,,,,,,,,,,
Buff-角色-雨果-核心被动-强化E失衡值提升,,,,,,,,,,,,,,,,
Buff-角色-雨果-额外能力-连携技伤害提升,,,,,,,,,,,,,,,,
Buff-角色-雨果-额外能力-连携技对普通敌人伤害提升,,,,,,,,,,,,,,,,
Buff-角色-雨果-额外能力-决算招式增伤,,,,,,,,,,,,,,,,
Buff-角色-雨果-额外能力-强化E回能触发器,,,,,,,,,,,,,,,,
Buff-角色-雨果-1画-决算招式双暴增幅,,,,,,,,,,,,,,,,
Buff-角色-雨果-2画-决算招式无视防御力,,,,,,,,,,,,,,,,
Buff-角色-雨果-4画-蓄力射击减冰抗,1291_SNA_2_FC|1291_SCA_FC|1291_NA_A_FC|1291_BH_Aid_A_FC|1291_SNA_2_NFC|1291_SCA|1291_NA_A|1291_BH_Aid_A,,,,,,,,,,,,,,,
Buff-角色-雨果-6画-决算招式增伤,,,,,,,,,,,,,,,,
Buff-武器-精1千面日陨-常驻暴伤,,,,,,,,,,,,,,,,
Buff-武器-精2千面日陨-常驻暴伤,,,,,,,,,,,,,,,,
Buff-武器-精3千面日陨-常驻暴伤,,,,,,,,,,,,,,,,
Buff-武器-精4千面日陨-常驻暴伤,,,,,,,,,,,,,,,,
Buff-武器-精5千面日陨-常驻暴伤,,,,,,,,,,,,,,,,
Buff-武器-精1千面日陨-零度处刑,,,,,,,,,,2|5|6,2,,,,,
Buff-武器-精2千面日陨-零度处刑,,,,,,,,,,2|5|6,2,,,,,
Buff-武器-精3千面日陨-零度处刑,,,,,,,,,,2|5|6,2,,,,,
Buff-武器-精4千面日陨-零度处刑,,,,,,,,,,2|5|6,2,,,,,
Buff-武器-精5千面日陨-零度处刑,,,,,,,,,,2|5|6,2,,,,,
Buff-武器-精1钢铁肉垫-常驻物理伤,,,,,,,,,,,,,,,,
Buff-武器-精2钢铁肉垫-常驻物理伤,,,,,,,,,,,,,,,,
Buff-武器-精3钢铁肉垫-常驻物理伤,,,,,,,,,,,,,,,,
Buff-武器-精4钢铁肉垫-常驻物理伤,,,,,,,,,,,,,,,,
Buff-武器-精5钢铁肉垫-常驻物理伤,,,,,,,,,,,,,,,,
Buff-武器-精1钢铁肉垫-背击增伤,,,,,,,,,,,,,,,,
Buff-武器-精2钢铁肉垫-背击增伤,,,,,,,,,,,,,,,,
Buff-武器-精3钢铁肉垫-背击增伤,,,,,,,,,,,,,,,,
Buff-武器-精4钢铁肉垫-背击增伤,,,,,,,,,,,,,,,,
Buff-武器-精5钢铁肉垫-背击增伤,,,,,,,,,,,,,,,,
Buff-武器-精1街头巨星-终结技增伤,,,,,,,,,,,,,,,,
Buff-武器-精2街头巨星-终结技增伤,,,,,,,,,,,,,,,,
Buff-武器-精3街头巨星-终结技增伤,,,,,,,,,,,,,,,,
Buff-武器-精4街头巨星-终结技增伤,,,,,,,,,,,,,,,,
Buff-武器-精5街头巨星-终结技增伤,,,,,,,,,,,,,,,,
Buff-武器-精1鎏金花信-局内攻击和强化E增伤,,,,,,,,,,,,,,,,
Buff-武器-精2鎏金花信-局内攻击和强化E增伤,,,,,,,,,,,,,,,,
Buff-武器-精3鎏金花信-局内攻击和强化E增伤,,,,,,,,,,,,,,,,
Buff-武器-精4鎏金花信-局内攻击和强化E增伤,,,,,,,,,,,,,,,,
Buff-武器-精5鎏金花信-局内攻击和强化E增伤,,,,,,,,,,,,,,,,
Buff-武器-精1强音热望-攻击力加成,,,,,,,,,,2|5,,,,,,
Buff-武器-精2强音热望-攻击力加成,,,,,,,,,,2|5,,,,,,
Buff-武器-精3强音热望-攻击力加成,,,,,,,,,,2|5,,,,,,
Buff-武器-精4强音热望-攻击力加成,,,,,,,,,,2|5,,,,,,
Buff-武器-精5强音热望-攻击力加成,,,,,,,,,,2|5,,,,,,
Buff-武器-精1强音热望-额外攻击力加成,,,,,,,,,,,,,,,,
Buff-武器-精2强音热望-额外攻击力加成,,,,,,,,,,,,,,,,
Buff-武器-精3强音热望-额外攻击力加成,,,,,,,,,,,,,,,,
Buff-武器-精4强音热望-额外攻击力加成,,,,,,,,,,,,,,,,
Buff-武器-精5强音热望-额外攻击力加成,,,,,,,,,,,,,,,,
Buff-武器-精1加农转子-常驻攻击力,,,,,,,,,,,,,,,,
Buff-武器-精2加农转子-常驻攻击力,,,,,,,,,,,,,,,,
Buff-武器-精3加农转子-常驻攻击力,,,,,,,,,,,,,,,,
Buff-武器-精4加农转子-常驻攻击力,,,,,,,,,,,,,,,,
Buff-武器-精5加农转子-常驻攻击力,,,,,,,,,,,,,,,,
Buff-武器-精1加农转子-附加伤害触发器,,,,,,,,,,,,,,,,
Buff-武器-精2加农转子-附加伤害触发器,,,,,,,,,,,,,,,,
Buff-武器-精3加农转子-附加伤害触发器,,,,,,,,,,,,,,,,
Buff-武器-精4加农转子-附加伤害触发器,,,,,,,,,,,,,,,,
Buff-武器-精5加农转子-附加伤害触发器,,,,,,,,,,,,,,,,
Buff-武器-精1「月相」-望-增伤,,,,,,,,,,,,,,,,
Buff-武器-精2「月相」-望-增伤,,,,,,,,,,,,,,,,
Buff-武器-精3「月相」-望-增伤,,,,,,,,,,,,,,,,
Buff-武器-精4「月相」-望-增伤,,,,,,,,,,,,,,,,
Buff-武器-精5「月相」-望-增伤,,,,,,,,,,,,,,,,
Buff-武器-精1「月相」-晦-增伤,,,,,,WD,,,,,,,,,,
Buff-武器-精2「月相」-晦-增伤,,,,,,,,,,,,,,,,
Buff-武器-精3「月相」-晦-增伤,,,,,,,,,,,,,,,,
Buff-武器-精4「月相」-晦-增伤,,,,,,,,,,,,,,,,
Buff-武器-精5「月相」-晦-增伤,,,,,,,,,,,,,,,,
Buff-武器-精1「月相」-朔-回能触发器,,,,,,,,,,2,,,,,,
Buff-武器-精2「月相」-朔-回能触发器,,,,,,,,,,2,,,,,,
Buff-武器-精3「月相」-朔-回能触发器,,,,,,,,,,2,,,,,,
Buff-武器-精4「月相」-朔-回能触发器,,,,,,,,,,2,,,,,,
Buff-武器-精5「月相」-朔-回能触发器,,,,,,,,,,2,,,,,,
Buff-角色-仪玄-回能事件组触发器,,,,,,,,,,,,,,,,
Buff-角色-仪玄-核心被动-技能增伤,1371_SNA_B_1|1371_SNA_B_2|1371_E_EX_A_1_NFC|1371_E_EX_A_1_FC|1371_E_EX_B_2|1371_E_EX_A_2|1371_E_EX_A_1_Add|1371_E_EX_A_3|1371_E_EX_B_1|1371_E_EX_B_3|1371_Assault_Aid|1371_QTE|1371_Q|1371_Q_A|1371_E_EX_A_1_FCT,,,,,,,,,,,,,,,
Buff-角色-仪玄-额外能力-对失衡敌人增伤,,,,,,,,,,,,,,,,
Buff-角色-仪玄-额外能力-暴伤提升,,,,,,,,,,6,,,,,,
Buff-角色-仪玄-1画-暴击率提升,,,,,,,,,,,,,,,,
Buff-角色-仪玄-1画-落雷触发器,,,,,,,,,,,,,,,,
Buff-角色-仪玄-2画-强化E与终结技无视以太抗,,,,,,,,,,2|6,,,,,,
Buff-角色-仪玄-2画-失衡时间提升,,,,,,,,,,,,,,,,
Buff-角色-仪玄-4画-静心,,,,,,,,,,,,,,,,
Buff-角色-仪玄-6画-贯穿伤害提高,,,,,,,,,,6,,,,,,
Buff-武器-精1青溟笼舍-暴击率提升,,,,,,,,,,,,,,,,
Buff-武器-精2青溟笼舍-暴击率提升,,,,,,,,,,,,,,,,
Buff-武器-精3青溟笼舍-暴击率提升,,,,,,,,,,,,,,,,
Buff-武器-精4青溟笼舍-暴击率提升,,,,,,,,,,,,,,,,
Buff-武器-精5青溟笼舍-暴击率提升,,,,,,,,,,,,,,,,
Buff-武器-精1青溟笼舍-以太伤害提升,,,,,,,,,,,,,,,,
Buff-武器-精2青溟笼舍-以太伤害提升,,,,,,,,,,,,,,,,
Buff-武器-精3青溟笼舍-以太伤害提升,,,,,,,,,,,,,,,,
Buff-武器-精4青溟笼舍-以太伤害提升,,,,,,,,,,,,,,,,
Buff-武器-精5青溟笼舍-以太伤害提升,,,,,,,,,,,,,,,,
Buff-武器-精1青溟笼舍-贯穿伤害提升,,,,,,,,,,,,,,,,
Buff-武器-精2青溟笼舍-贯穿伤害提升,,,,,,,,,,,,,,,,
Buff-武器-精3青溟笼舍-贯穿伤害提升,,,,,,,,,,,,,,,,
Buff-武器-精4青溟笼舍-贯穿伤害提升,,,,,,,,,,,,,,,,
Buff-武器-精5青溟笼舍-贯穿伤害提升,,,,,,,,,,,,,,,,
Buff-驱动盘-云岿如我-四件套-暴击率提升,,,,,,,,,,2|5|6,,,,,,
Buff-驱动盘-云岿如我-四件套-贯穿伤害提升,,,,,,,,,,,,,,,,
Buff-武器-精1幻变魔方-爆伤提升,,,,,,,,,,2,,,,,,
Buff-武器-精2幻变魔方-爆伤提升,,,,,,,,,,2,,,,,,
Buff-武器-精3幻变魔方-爆伤提升,,,,,,,,,,2,,,,,,
Buff-武器-精4幻变魔方-爆伤提升,,,,,,,,,,2,,,,,,
Buff-武器-精5幻变魔方-爆伤提升,,,,,,,,,,2,,,,,,
Buff-武器-精1幻变魔方-强化E增伤,,,,,,,,,,,,,,,,
Buff-武器-精2幻变魔方-强化E增伤,,,,,,,,,,,,,,,,
Buff-武器-精3幻变魔方-强化E增伤,,,,,,,,,,,,,,,,
Buff-武器-精4幻变魔方-强化E增伤,,,,,,,,,,,,,,,,
Buff-武器-精5幻变魔方-强化E增伤,,,,,,,,,,,,,,,,
Buff-武器-精1电波漫步-贯穿力提升,,,,,,,,,,5|6,,,,,,
Buff-武器-精2电波漫步-贯穿力提升,,,,,,,,,,5|6,,,,,,
Buff-武器-精3电波漫步-贯穿力提升,,,,,,,,,,5|6,,,,,,
Buff-武器-精4电波漫步-贯穿力提升,,,,,,,,,,5|6,,,,,,
Buff-武器-精5电波漫步-贯穿力提升,,,,,,,,,,5|6,,,,,,
Buff-武器-精1「灰烬」-钴蓝-攻击力提升,,,,,,,,,,,,,,,,
Buff-武器-精2「灰烬」-钴蓝-攻击力提升,,,,,,,,,,,,,,,,
Buff-武器-精3「灰烬」-钴蓝-攻击力提升,,,,,,,,,,,,,,,,
Buff-武器-精4「灰烬」-钴蓝-攻击力提升,,,,,,,,,,,,,,,,
Buff-武器-精5「灰烬」-钴蓝-攻击力提升,,,,,,,,,,,,,,,,
Buff-角色-柚叶-甜蜜惊吓,,,,,,,,,,,,,,,,
Buff-角色-柚叶-硬糖射击触发器,,,,,,,,,,,,,,,,
Buff-角色-柚叶-彩糖花火积蓄值增加,,,,,,,,,,,,,,,,
Buff-角色-柚叶-彩糖花火·极积蓄值增加,,,,,,,,,,,,,,,,
Buff-角色-柚叶-核心被动-狸之愿-攻击力,1411_E_EX_A|1411_E_EX_B|1411_Q,,,,,,,,,,,,,,,
Buff-角色-柚叶-核心被动-狸之愿-增伤,1411_E_EX_A|1411_E_EX_B|1411_Q,,,,,,,,,,,,,,,
Buff-角色-柚叶-组队被动-积蓄值增幅,1411_E_EX_A|1411_E_EX_B|1411_Q,,,,,,,,,,,,,,,
Buff-角色-柚叶-组队被动-属性异常与紊乱伤害增幅,1411_E_EX_A|1411_E_EX_B|1411_Q,,,,,,,,,,,,,,,
Buff-角色-柚叶-1画-全属性伤害抗性降低,,,,,,,,,,,,,,,,
Buff-角色-柚叶-2画-全队增伤与积蓄效率增幅,1411_E_EX_A|1411_E_EX_B|1411_Q,,,,,,,,,,,,,,,
Buff-角色-柚叶-2画-连携技触发器,,,,,,,,,,,,,,,,
Buff-角色-柚叶-4画-支援突击增幅,,,,,,,,,,,,,,,,
Buff-角色-柚叶-4画-快支触发器,,,,,,,,,,,,,,,,
Buff-角色-柚叶-6画-炮弹触发器,,,,,,,,,,,,,,,,
Buff-角色-柚叶-6画-彩糖花火极触发器,,,,,,,,,,,,,,,,
Buff-角色-柚叶-6画-紊乱伤害倍率提升,1411_Cinema_6,,,,,,,,,,,,,,,
Buff-武器-精1狸法七变化-异常掌控,,,,,,,,,,2|6,0,,,,,
Buff-武器-精2狸法七变化-异常掌控,,,,,,,,,,2|6,0,,,,,
Buff-武器-精3狸法七变化-异常掌控,,,,,,,,,,2|6,0,,,,,
Buff-武器-精4狸法七变化-异常掌控,,,,,,,,,,2|6,0,,,,,
Buff-武器-精5狸法七变化-异常掌控,,,,,,,,,,2|6,0,,,,,
Buff-武器-精1狸法七变化-全队异常精通,,,,,,,,,,,,,,,,
Buff-武器-精2狸法七变化-全队异常精通,,,,,,,,,,,,,,,,
Buff-武器-精3狸法七变化-全队异常精通,,,,,,,,,,,,,,,,
Buff-武器-精4狸法七变化-全队异常精通,,,,,,,,,,,,,,,,
Buff-武器-精5狸法七变化-全队异常精通,,,,,,,,,,,,,,,,
Buff-角色-薇薇安-6画-触发器,,,,,,,,,,,,,,,,
Buff-角色-爱丽丝-核心被动-紊乱基础倍率增加,,,,,,,,,,,,,,,,
Buff-角色-爱丽丝-核心被动-物理异常积蓄效率提升,,,,,,,,,,,,,,,,
Buff-角色-爱丽丝-额外能力-异常掌控转精通,,,,,,,,,,,,,,,,
Buff-角色-爱丽丝-影画-1画-减防,,,,,,,,,,,,,,,,
Buff-角色-爱丽丝-影画-2画-全队强击伤害提升,,,,,,,,,,,,,,,,
Buff-角色-爱丽丝-影画-2画-紊乱伤害提升,,,,,,,,,,,,,,,,
Buff-角色-爱丽丝-影画-4画-无视物理伤害抗性,,,,,,,,,,,,,,,,
Buff-角色-爱丽丝-影画-4画-普攻积蓄效率增幅,1401_NA_5_PLUS,,,,,,,,,,,,,,,
Buff-角色-爱丽丝-影画-6画-额外攻击触发器,,,,,,,,,,,,,,,,
Buff-角色-爱丽丝-影画-6画-额外攻击必暴,,,,,,,,,,,,,,,,
Buff-武器-精1十方锻星-异常掌控提升,,,,,,,,,,,,,,,,
Buff-武器-精2十方锻星-异常掌控提升,,,,,,,,,,,,,,,,
Buff-武器-精3十方锻星-异常掌控提升,,,,,,,,,,,,,,,,
Buff-武器-精4十方锻星-异常掌控提升,,,,,,,,,,,,,,,,
Buff-武器-精5十方锻星-异常掌控提升,,,,,,,,,,,,,,,,
Buff-武器-精1十方锻星-物理伤害增加,,,,,,,,,,,,,,,,
Buff-武器-精2十方锻星-物理伤害增加,,,,,,,,,,,,,,,,
Buff-武器-精3十方锻星-物理伤害增加,,,,,,,,,,,,,,,,
Buff-武器-精4十方锻星-物理伤害增加,,,,,,,,,,,,,,,,
Buff-武器-精5十方锻星-物理伤害增加,,,,,,,,,,,,,,,,
Buff-角色-爱丽丝-极性强击触发器,,,,,,,,,,,,,,,,
Buff-角色-席德-强袭,,,,,,,,,,,,,,,,
Buff-角色-席德-明攻,,,,,,,,,,,,,,,,
Buff-角色-席德-围杀,,,,,,,,,,,,,,,,
Buff-角色-席德-额外能力-重击大招增伤无视电抗,,,,,,,,,,,,,,,,
Buff-角色-席德-影画-1画-崩坠暴伤增加,,,,,,,,,,,,,,,,
Buff-角色-席德-影画-2画-围杀无视防御力,,,,,,,,,,,,,,,,
Buff-角色-席德-影画-2画-耗能转化增伤,,,,,,,,,,,,,,,,
Buff-角色-席德-影画-4画-喧响效率与大招增伤,,,,,,,,,,,,,,,,
Buff-角色-席德-影画-6画-常驻暴伤,,,,,,,,,,,,,,,,
Buff-角色-席德-影画-6画-触发器,,,,,,,,,,,,,,,,
Buff-武器-精1机巧心种-常驻暴击,,,,,,,,,,,,,,,,
Buff-武器-精2机巧心种-常驻暴击,,,,,,,,,,,,,,,,
Buff-武器-精3机巧心种-常驻暴击,,,,,,,,,,,,,,,,
Buff-武器-精4机巧心种-常驻暴击,,,,,,,,,,,,,,,,
Buff-武器-精5机巧心种-常驻暴击,,,,,,,,,,,,,,,,
Buff-武器-精1机巧心种-电属性增伤,,,,,,,,,,,,,,,,
Buff-武器-精2机巧心种-电属性增伤,,,,,,,,,,,,,,,,
Buff-武器-精3机巧心种-电属性增伤,,,,,,,,,,,,,,,,
Buff-武器-精4机巧心种-电属性增伤,,,,,,,,,,,,,,,,
Buff-武器-精5机巧心种-电属性增伤,,,,,,,,,,,,,,,,
Buff-武器-精1机巧心种-普攻大招无视防御,,,,,,,,,,,,,,,,
Buff-武器-精2机巧心种-普攻大招无视防御,,,,,,,,,,,,,,,,
Buff-武器-精3机巧心种-普攻大招无视防御,,,,,,,,,,,,,,,,
Buff-武器-精4机巧心种-普攻大招无视防御,,,,,,,,,,,,,,,,
Buff-武器-精5机巧心种-普攻大招无视防御,,,,,,,,,,,,,,,,
Buff-驱动盘-拂晓生花-二件套-普攻增伤,,,,,,,,,,,,,,,,
Buff-驱动盘-拂晓生花-四件套-常驻普攻增伤,,,,,,,,,,,,,,,,
Buff-驱动盘-拂晓生花-四件套-触发普攻增伤,,,,,,,,,,,,,,,,
Buff-驱动盘-月光骑士颂-全队增伤,,,,,,,,,,,,,,,,
Buff-角色-席德-明攻触发器,,,,,,,,,,,,,,,,
Buff-角色-席德-影画-2画-无视防御触发器,,,,,,,,,,,,,,,,
Buff-角色-席德-围杀触发器,,,,,,,,,,,,,,,,
Buff-角色-席德-影画-4画-触发器,,,,,,,,,,,,,,,,
================================================
FILE: zsim/define.py
================================================
import json
import shutil
import sys
import tomllib
from pathlib import Path
from typing import Any, Callable, ClassVar, Literal, cast
from pydantic import BaseModel, ConfigDict
from pydantic.alias_generators import to_pascal
from pydantic_settings import (
BaseSettings,
JsonConfigSettingsSource,
PydanticBaseSettingsSource,
SettingsConfigDict,
)
# 属性类型:
ElementType = Literal[0, 1, 2, 3, 4, 5, 6]
Number = int | float
INVALID_ELEMENT_ERROR = "Invalid element type"
NORMAL_MODE_ID_JSON = "results/id_cache.json"
results_dir = "results/"
data_dir = Path("./zsim/data")
config_path = Path("./zsim/config.json")
class DebugConfig(BaseModel):
enabled: bool = True
level: int = 4
check_skill_mul: bool = False
check_skill_mul_tag: list[str] = []
class WatchdogConfig(BaseModel):
enabled: bool = False
level: int = 4
class CharacterConfig(BaseModel):
crit_balancing: bool = True
back_attack_rate: float = 1.0
class EnemyConfig(BaseModel):
index_id: int
adjust_id: int
difficulty: float
model_config = ConfigDict(alias_generator=lambda x: x.replace("id", "ID"))
class AplModeConfig(BaseModel):
enabled: bool = True
na_order: str
enemy_random_attack: bool = False
enemy_regular_attack: bool = False
enemy_attack_response: bool = False
enemy_attack_method_config: str
enemy_attack_action_data: str
enemy_attack_report: bool = True
player_level: int = 5
default_apl_dir: str
custom_apl_dir: str
yanagi: str
hugo: str
alice: str
seed: str
perfect_player: bool = True
apl_thought_check: bool = False
apl_thought_check_window: list[int] = [0, 1]
model_config = ConfigDict(populate_by_name=True, alias_generator=to_pascal)
class SwapCancelModeConfig(BaseModel):
enabled: bool = True
completion_coefficient: float
lag_time: float
debug: bool = False
debug_target_skill: str
class DatabaseConfig(BaseModel):
sqlite_path: str
character_data_path: str
weapon_data_path: str
equip_2pc_data_path: str
skill_data_path: str
enemy_data_path: str
enemy_adjustment_path: str
default_skill_path: str
judge_file_path: str
effect_file_path: str
exist_file_path: str
apl_file_path: str
model_config = ConfigDict(populate_by_name=True, alias_generator=str.upper)
class Buff0ReportConfig(BaseModel):
enabled: bool = False
class CharReportConfig(BaseModel):
vivian: bool
astra_yao: bool
hugo: bool
yixuan: bool
trigger: bool
jufufu: bool
yuzuha: bool
alice: bool
seed: bool
model_config = ConfigDict(populate_by_name=True, alias_generator=to_pascal)
class NaModeLevelConfig(BaseModel):
hugo: int
model_config = ConfigDict(populate_by_name=True, alias_generator=to_pascal)
class DevConfig(BaseModel):
new_sim_boot: bool = True
class Config(BaseSettings):
model_config: ClassVar[SettingsConfigDict] = SettingsConfigDict(
json_file=config_path,
json_file_encoding="utf-8",
env_file=".env",
env_ignore_empty=True,
env_file_encoding="utf-8",
env_nested_delimiter="__",
env_prefix="ZSIM_",
populate_by_name=True,
)
debug: DebugConfig
stop_tick: int = 10800
watchdog: WatchdogConfig
character: CharacterConfig
enemy: EnemyConfig
apl_mode: AplModeConfig
swap_cancel_mode: SwapCancelModeConfig
database: DatabaseConfig
translate: dict[str, str] = {}
buff_0_report: Buff0ReportConfig
char_report: CharReportConfig
na_mode_level: NaModeLevelConfig
parallel_mode: dict[str, Any] = {}
dev: DevConfig = DevConfig()
@classmethod
def settings_customise_sources(
cls,
settings_cls: type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> tuple[PydanticBaseSettingsSource, ...]:
return (
init_settings,
JsonConfigSettingsSource(settings_cls),
env_settings,
dotenv_settings,
file_secret_settings,
)
# 确保数据目录存在
data_dir.mkdir(exist_ok=True, parents=True)
char_config_file = data_dir / "character_config.toml"
saved_char_config = {}
# 修复:将char_config_file作为参数传递给initialize_config_files
def initialize_config_files_with_paths(char_file: Path, data_dir: Path, config_path: Path):
"""
初始化配置文件。
如果配置文件不存在,则从 _example 文件复制生成。
如果存在,则检查并使用模板更新现有配置。
"""
char_config_example_path = Path("zsim/data/character_config_example.toml")
config_example_path = Path("zsim/config_example.json")
# TOML config
if not char_file.exists():
shutil.copy(char_config_example_path, char_file)
print(f"已生成配置文件:{char_file}")
# JSON config
def update_json_config(template: dict[str, Any], user: dict[str, Any]) -> bool:
"""递归更新用户配置,返回是否被更新"""
updated = False
for key, value in template.items():
if key not in user:
user[key] = value
updated = True
elif isinstance(value, dict) and isinstance(user.get(key), dict):
if update_json_config(cast(dict[str, Any], value), user[key]):
updated = True
return updated
if not Path(config_path).exists():
shutil.copy(config_example_path, config_path)
print(f"已生成配置文件:{config_path}")
else:
with open(config_example_path, "r", encoding="utf-8") as f:
template_config = json.load(f)
with open(config_path, "r", encoding="utf-8") as f:
user_config = json.load(f)
if update_json_config(template_config, user_config):
with open(config_path, "w", encoding="utf-8") as f:
json.dump(user_config, f, indent=4, ensure_ascii=False)
print(f"配置文件 {config_path} 已更新。")
# 使用新的函数
initialize_config_files_with_paths(char_config_file, data_dir, config_path)
if char_config_file.exists():
with open(char_config_file, "rb") as f:
saved_char_config = tomllib.load(f)
else:
raise FileNotFoundError(f"Character config file {char_config_file} not found.")
# 确保配置文件目录存在
config_path.parent.mkdir(exist_ok=True, parents=True)
config = Config() # type:ignore
# 敌人配置
ENEMY_INDEX_ID: int = config.enemy.index_id
ENEMY_ADJUST_ID: int = config.enemy.adjust_id
ENEMY_DIFFICULTY: float = config.enemy.difficulty
# APL模式配置
APL_MODE: bool = config.apl_mode.enabled
SWAP_CANCEL: bool = config.swap_cancel_mode.enabled
APL_PATH: str = config.database.apl_file_path
APL_NA_ORDER_PATH: str = config.apl_mode.na_order
ENEMY_RANDOM_ATTACK: bool = config.apl_mode.enemy_random_attack
ENEMY_REGULAR_ATTACK: bool = config.apl_mode.enemy_regular_attack
if ENEMY_RANDOM_ATTACK and ENEMY_REGULAR_ATTACK:
raise ValueError("不能同时开启“敌人随机进攻”与“敌人规律进攻”参数。")
ENEMY_ATTACK_RESPONSE: bool = config.apl_mode.enemy_attack_response
ENEMY_ATTACK_METHOD_CONFIG: str = config.apl_mode.enemy_attack_method_config
ENEMY_ATTACK_ACTION: str = config.apl_mode.enemy_attack_action_data
ENEMY_ATTACK_REPORT: bool = config.apl_mode.enemy_attack_report
ENEMY_ATK_PARAMETER_DICT: dict[str, int | float | bool] = {
"Taction": 30, # 角色弹刀与闪避动作的持续时间,不开放给用户更改。
"Tbase": 273, # 人类反应时间大数据中位数,单位ms,不可更改!
"PlayerLevel": config.apl_mode.player_level, # 玩家水平系数,由用户自己填写。
"PerfectPlayer": config.apl_mode.perfect_player, # 是否是完美玩家(默认是)
"theta": 90, # θ,人类胜利最小反应时间(神经传导极限),为90ms,不可更改!
"c": 0.5, # 波动调节系数,暂取0.5,不开放给用户更改。
"delta": 30, # 玩家水平系数所导致的中位数波动单位,暂时取30ms,不开放给用户更改。
}
PARRY_BASE_PARAMETERS: dict[str, int | float] = {
"ChainParryActionTimeCost": 10, # 连续招架动作的时间消耗
}
# 招架策略,有些角色的拥有不同的突击支援(比如柚叶),所以在这里用字典进行映射。
# 该字典的key为CID,value为招架动作的skill_tag
# 注意,不同的招架策略有时候存在着影画或是其他的限制条件,
# 所以若是在不满足这些条件的情况下强行使用这些招架策略,那么character中的审查函数会报错而中断程序运行。
CHAR_PARRY_STRATEGY_MAP: dict[int, str] = {1411: "1411_Assault_Aid_A"}
# debug参数,用于检查APL在窗口期间的想法
APL_THOUGHT_CHECK: bool = config.apl_mode.apl_thought_check
APL_THOUGHT_CHECK_WINDOW: list[int] = config.apl_mode.apl_thought_check_window
DEFAULT_APL_DIR: str = config.apl_mode.default_apl_dir
COSTOM_APL_DIR: str = config.apl_mode.custom_apl_dir
YANAGI_NA_ORDER: str = config.apl_mode.yanagi
HUGO_NA_ORDER: str = config.apl_mode.hugo
HUGO_NA_MODE_LEVEL: int = config.na_mode_level.hugo
ALICE_NA_ORDER: str = config.apl_mode.alice
SEED_NA_ORDER: str = config.apl_mode.seed
#: 合轴操作完成度系数->根据前一个技能帧数的某个比例来延后合轴
SWAP_CANCEL_MODE_COMPLETION_COEFFICIENT: float = config.swap_cancel_mode.completion_coefficient
#: 操作滞后系数->合轴操作延后的另一种迟滞方案,即固定值延后。
SWAP_CANCEL_MODE_LAG_TIME: float = config.swap_cancel_mode.lag_time
SWAP_CANCEL_MODE_DEBUG: bool = config.swap_cancel_mode.debug
SWAP_CANCEL_DEBUG_TARGET_SKILL: str = config.swap_cancel_mode.debug_target_skill
# 数据库配置
SQLITE_PATH: str = config.database.sqlite_path
CHARACTER_DATA_PATH: str = config.database.character_data_path
WEAPON_DATA_PATH: str = config.database.weapon_data_path
EQUIP_2PC_DATA_PATH: str = config.database.equip_2pc_data_path
SKILL_DATA_PATH: str = config.database.skill_data_path
ENEMY_DATA_PATH: str = config.database.enemy_data_path
ENEMY_ADJUSTMENT_PATH: str = config.database.enemy_adjustment_path
DEFAULT_SKILL_PATH: str = config.database.default_skill_path
CRIT_BALANCING: bool = config.character.crit_balancing
BACK_ATTACK_RATE: float = config.character.back_attack_rate
# FIXME:背击暂时用几率控制。
DEBUG: bool = config.debug.enabled
DEBUG_LEVEL: int = config.debug.level
JUDGE_FILE_PATH: str = config.database.judge_file_path
EFFECT_FILE_PATH: str = config.database.effect_file_path
EXIST_FILE_PATH: str = config.database.exist_file_path
BUFF_LOADING_CONDITION_TRANSLATION_DICT = config.translate
ENABLE_WATCHDOG: bool = config.watchdog.enabled
WATCHDOG_LEVEL: int = config.watchdog.level
INPUT_ACTION_LIST = "" # 半废弃
# 初始化Buff的报告:
BUFF_0_REPORT: bool = config.buff_0_report.enabled
# 角色特殊机制报告:
VIVIAN_REPORT: bool = config.char_report.vivian
ASTRAYAO_REPORT: bool = config.char_report.astra_yao
HUGO_REPORT: bool = config.char_report.hugo
YIXUAN_REPORT: bool = config.char_report.yixuan
TRIGGER_REPORT: bool = config.char_report.trigger
YUZUHA_REPORT: bool = config.char_report.yuzuha
ALICE_REPORT: bool = config.char_report.alice
SEED_REPORT: bool = config.char_report.seed
# Cal计算debug
CHECK_SKILL_MUL: bool = config.debug.check_skill_mul
CHECK_SKILL_MUL_TAG: list[str] = config.debug.check_skill_mul_tag
# 开发变量
NEW_SIM_BOOT: bool = config.dev.new_sim_boot
compare_methods_mapping: dict[str, Callable[[float | int, float | int], bool]] = {
"<": lambda a, b: a < b,
"<=": lambda a, b: a <= b,
">": lambda a, b: a > b,
">=": lambda a, b: a >= b,
"==": lambda a, b: a == b,
"!=": lambda a, b: a != b,
}
ANOMALY_MAPPING: dict[ElementType, str] = {
0: "强击",
1: "灼烧",
2: "碎冰",
3: "感电",
4: "侵蚀",
5: "烈霜碎冰",
6: "玄墨侵蚀",
}
ELEMENT_TYPE_MAPPING: dict[ElementType, str] = {
0: "物理",
1: "火",
2: "冰",
3: "电",
4: "以太",
5: "烈霜",
6: "玄墨",
}
# 属性类型等价映射字典
ELEMENT_EQUIVALENCE_MAP: dict[ElementType, list[ElementType]] = {
0: [0],
1: [1],
2: [2, 5], # 烈霜也能享受到冰属性加成
3: [3],
4: [4, 6], # 玄墨也能享受到以太属性加成
5: [5],
6: [6],
}
SUB_STATS_MAPPING: dict[
Literal[
"scATK_percent",
"scATK",
"scHP_percent",
"scHP",
"scDEF_percent",
"scDEF",
"scAnomalyProficiency",
"scPEN",
"scCRIT",
"scCRIT_DMG",
"DMG_BONUS",
"PEN_RATIO",
"ANOMALY_MASTERY",
"SP_REGEN",
],
Number,
] = {
"scATK_percent": 0.03,
"scATK": 19,
"scHP_percent": 0.03,
"scHP": 112,
"scDEF_percent": 0.048,
"scDEF": 15,
"scAnomalyProficiency": 9,
"scPEN": 9,
"scCRIT": 0.024,
"scCRIT_DMG": 0.048,
"DMG_BONUS": 0.03,
"PEN_RATIO": 0.024,
"ANOMALY_MASTERY": 0.03,
"SP_REGEN": 0.06,
}
DOCS_DIR = "docs"
# Version Check
GITHUB_REPO_OWNER = "ZZZSimulator"
GITHUB_REPO_NAME = "ZSim"
# 版本号处理
if getattr(sys, "frozen", False):
# 打包环境:版本号由PyInstaller在打包时注入
__version__ = "1.0.0" # 默认值,打包时会被替换
else:
# 开发环境:从 pyproject.toml 读取
try:
with open("pyproject.toml", "rb") as f:
pyproject_config = tomllib.load(f)
__version__ = pyproject_config.get("project", {}).get("version", "0.0.0")
except FileNotFoundError:
__version__ = "1.0.0"
if __name__ == "__main__":
# 打印全部CONSTANT变量名
def print_constant_names_and_values():
# 获取当前全局命名空间
global_vars = globals()
# 筛选出所有全大写的变量名及其值
constant_names_and_values = {
name: value for name, value in global_vars.items() if name.isupper()
}
# 打印这些变量名及其值
for name, value in constant_names_and_values.items():
print(f"{name}: {value}")
print_constant_names_and_values()
print(config.model_dump_json(indent=2, by_alias=True))
================================================
FILE: zsim/lib_webui/__init__.py
================================================
================================================
FILE: zsim/lib_webui/clean_results_cache.py
================================================
import json
import os
try:
from zsim.define import NORMAL_MODE_ID_JSON
except ModuleNotFoundError:
pass
from .constants import IDDuplicateError, results_dir
# 获取合法的结果缓存
def get_all_results(
*, id_cache_path=NORMAL_MODE_ID_JSON, results_dir=results_dir
) -> dict[str, str | int | None]:
# 读取id_cache.json文件
# 如果文件不存在则创建空的id_cache
if not os.path.exists(id_cache_path):
os.makedirs(os.path.dirname(id_cache_path), exist_ok=True)
with open(id_cache_path, "w") as f:
json.dump({}, f)
with open(id_cache_path, "r") as f:
id_cache = json.load(f)
# 获取results文件夹内的所有文件夹名
folder_names = [
name for name in os.listdir(results_dir) if os.path.isdir(os.path.join(results_dir, name))
]
# 找出需要删除的key
keys_to_delete = []
for key in list(id_cache.keys()):
if key not in folder_names:
keys_to_delete.append(key)
# 删除无对应文件夹的key
for key in keys_to_delete:
del id_cache[key]
# 找出需要新建的key
for folder_name in folder_names:
if folder_name not in id_cache.keys():
id_cache[folder_name] = "Unkown results"
# 将更新后的id_cache写回id_cache.json文件
with open(id_cache_path, "w") as f:
json.dump(id_cache, f, indent=4)
return id_cache
def rename_result(
former_name: str,
new_name: str,
new_comment: str | None = None,
*,
id_cache_path=NORMAL_MODE_ID_JSON,
results_dir=results_dir,
):
"""
重命名结果文件夹并更新id_cache.json文件中的对应条目。
参数:
former_name (str): 原文件夹名称
new_name (str): 新文件夹名称
new_comment (str | None): 新的备注信息,默认为None表示保留原备注
id_cache_path (str, optional, keyword only): id_cache.json文件路径,默认为ID_CACHE_JSON
results_dir (str, optional, keyword only): 结果文件夹路径,默认为results_dir
返回:
None
异常:
FileNotFoundError: 当原文件夹不存在时抛出
IDDuplicateError: 当新文件夹已存在时抛出
JSONDecodeError: 当id_cache.json文件格式错误时抛出
示例:
>>> rename_result("old_result", "new_result", "测试结果")
# 将old_result重命名为new_result,并更新备注为"测试结果"
"""
# 读取id_cache.json文件
with open(id_cache_path, "r") as f:
id_cache = json.load(f)
# 检查新名称是否已存在且与旧名称不同
if former_name != new_name:
new_path = os.path.join(results_dir, new_name)
if os.path.exists(new_path):
raise IDDuplicateError(f"新名称 {new_name} 已存在,请使用其他名称。")
# 重命名文件夹
former_path = os.path.join(results_dir, former_name)
os.rename(former_path, new_path)
# 更新id_cache字典
id_cache[new_name] = id_cache[former_name]
del id_cache[former_name]
if new_comment is not None:
id_cache[new_name] = new_comment
# 将更新后的id_cache写回id_cache.json文件
with open(id_cache_path, "w") as f:
json.dump(id_cache, f, indent=4)
def delete_result(former_name: str, *, id_cache_path=NORMAL_MODE_ID_JSON, results_dir=results_dir):
"""
删除结果文件夹并更新id_cache.json文件中的对应条目。
参数:
former_name (str): 需要删除的结果文件夹名称
id_cache_path (str, optional, keyword only): id_cache.json文件路径,默认为ID_CACHE_JSON
results_dir (str, optional, keyword only): 结果文件夹路径,默认为results_dir
返回:
None
异常:
FileNotFoundError: 当目标文件夹不存在时抛出
JSONDecodeError: 当id_cache.json文件格式错误时抛出
"""
import shutil
# 删除文件夹
folder_path = os.path.join(results_dir, former_name)
if not os.path.exists(folder_path):
raise FileNotFoundError(f"目标文件夹 {former_name} 不存在。")
shutil.rmtree(folder_path)
# 更新id_cache.json
with open(id_cache_path, "r") as f:
id_cache = json.load(f)
if former_name in id_cache:
del id_cache[former_name]
with open(id_cache_path, "w") as f:
json.dump(id_cache, f, indent=4)
if __name__ == "__main__":
get_all_results()
================================================
FILE: zsim/lib_webui/constants.py
================================================
import polars as pl
import streamlit as st
from zsim.define import ElementType
@st.cache_data
def _init_buff_effect_mapping() -> dict[str, str]:
"""初始化BUFF效果映射关系"""
try:
df = pl.scan_csv("./zsim/data/buff_effect.csv")
mapping = df.collect().to_dict(as_series=False)
buff_effect_map: dict[str, str] = {}
for i in range(len(mapping["名称"])):
name = mapping["名称"][i]
effect_str = ""
# 动态的找键值对数量
max_key_index = 0
for col_name in mapping.keys():
if col_name.startswith("key"):
try:
index = int(col_name[3:])
if index > max_key_index:
max_key_index = index
except ValueError:
# 忽略不符合 keyN 格式的列名
pass
for j in range(1, max_key_index + 1):
key_col = f"key{j}"
value_col = f"value{j}"
if key_col in mapping and value_col in mapping:
key = mapping[key_col][i]
value = mapping[value_col][i]
if key and value is not None:
try:
effect_str += f"{key}: {float(value)}; "
except ValueError:
# Handle cases where value is not a valid float
print(
f"Warning: Could not convert value '{value}' to float for buff '{name}', key '{key}'. Skipping this effect."
)
continue
if effect_str:
# Remove trailing semicolon and space if present
buff_effect_map[name] = effect_str.rstrip("; ")
return buff_effect_map
except Exception as e:
print(f"Warning: Failed to load buff effect mapping: {e}")
return {}
BUFF_EFFECT_MAPPING: dict[str, str] = _init_buff_effect_mapping()
@st.cache_data
def _init_skill_tag_mapping() -> dict[str, str]:
"""初始化技能标签映射关系"""
try:
df = pl.scan_csv(
"./zsim/data/skill.csv",
schema_overrides={
"skill_tag": pl.String,
"skill_text": pl.String,
"INSTRUCTION": pl.String,
},
)
mapping = (
df.select("skill_tag", "skill_text", "INSTRUCTION").collect().to_dict(as_series=False)
)
return {
_tag: f"{_text if _text else ''}{f' - {_instruction}' if _instruction else ''}"
for _tag, _text, _instruction in zip(
mapping["skill_tag"], mapping["skill_text"], mapping["INSTRUCTION"], strict=False
)
}
except Exception as e:
print(f"Warning: Failed to load skill mapping: {e}")
return {}
SKILL_TAG_MAPPING: dict[str, str] = _init_skill_tag_mapping()
@st.cache_data
def _init_char_mapping() -> dict[str, str]:
"""初始化角色CID和名称的映射关系"""
try:
df = pl.scan_csv("./zsim/data/character.csv")
mapping = df.select(["name", "CID"]).collect().to_dict(as_series=False)
return {name: str(cid) for name, cid in zip(mapping["name"], mapping["CID"], strict=False)}
except Exception as e:
print(f"Warning: Failed to load character mapping: {e}")
return {}
# 角色CID和名称的映射关系
CHAR_CID_MAPPING: dict[str, str] = _init_char_mapping()
# 角色配置常量
default_chars = [
"扳机",
"丽娜",
"零号·安比",
] # 这个值其实没啥意义,但是必须是三个角色,否则可能会报错
__lf_character = pl.scan_csv("./zsim/data/character.csv")
char_options = __lf_character.select("name").unique().collect().to_series().to_list()
# 角色名称->职业特性
char_profession_map = {
row["name"]: row["角色特性"] for row in __lf_character.collect().iter_rows(named=True)
}
# 职业特性->角色名称列表
profession_chars_map = {}
for char_name, profession in char_profession_map.items():
if profession not in profession_chars_map:
profession_chars_map[profession] = []
profession_chars_map[profession].append(char_name)
profession_chars_map["不限特性"] = char_options
# 武器选项
__lf_weapon = pl.scan_csv("./zsim/data/weapon.csv")
weapon_options = __lf_weapon.select("名称").unique().collect().to_series().to_list()
# 音擎名称->职业
weapon_profession_map = {
row["名称"]: row["职业"] for row in __lf_weapon.collect().iter_rows(named=True)
}
# 音擎名称->稀有度
weapon_rarity_map = {
row["名称"]: row["稀有度"] for row in __lf_weapon.collect().iter_rows(named=True)
}
# 音擎名称->角色名称 (仅限部分 S 级和 A 级)
weapon_char_map: dict[str, str] = {}
for row in __lf_weapon.collect().iter_rows(named=True):
cid = row["ID"] % 1000 * 10 + 1
names = (
__lf_character.filter(pl.col("CID") == cid).select("name").collect().to_series().to_list()
)
# 如果没有对应角色,默认用空字符串
weapon_char_map[row["名称"]] = names[0] if names else ""
# 驱动盘套装选项
__lf_equip = pl.scan_csv("./zsim/data/equip_set_2pc.csv")
equip_set_ids = (
__lf_equip.select("set_ID")
.filter(pl.col("set_ID").is_not_null())
.unique()
.collect()
.to_series()
.to_list()
)
equip_set4_options = equip_set2_options = equip_set_ids
# 主词条选项
main_stat4_options = [
"攻击力%",
"生命值%",
"防御力%",
"暴击率%",
"暴击伤害%",
"异常精通",
"-",
]
main_stat5_options = [
"攻击力%",
"生命值%",
"防御力%",
"穿透率",
"物理属性伤害%",
"火属性伤害%",
"冰属性伤害%",
"电属性伤害%",
"以太属性伤害%",
"-",
]
main_stat6_options = [
"攻击力%",
"生命值%",
"防御力%",
"异常掌控",
"冲击力%",
"能量自动回复%",
"-",
]
stats_trans_mapping = {
"攻击力%": "scATK_percent",
"攻击力": "scATK",
"生命值%": "scHP_percent",
"生命值": "scHP",
"防御力%": "scDEF_percent",
"防御力": "scDEF",
"异常精通": "scAnomalyProficiency",
"穿透值": "scPEN",
"暴击率": "scCRIT",
"暴击伤害": "scCRIT_DMG",
"属性伤害加成": "DMG_BONUS",
"穿透率": "PEN_RATIO",
"异常掌控": "ANOMALY_MASTERY",
"能量自动回复": "SP_REGEN",
}
SC_DATA_DISCRIPTION_MAPPING = {
"scATK_percent": "3%/词条",
"scATK": "19点/词条",
"scHP_percent": "3%/词条",
"scHP": "112/词条",
"scDEF_percent": "4.8%/词条",
"scDEF": "15点/词条",
"scAnomalyProficiency": "9点/词条",
"scPEN": "9点/词条",
"scCRIT": "2.4%暴击率或4.8%暴击伤害/词条",
"scCRIT_DMG": "2.4%暴击率或4.8%暴击伤害/词条",
"DMG_BONUS": "3%/词条",
"PEN_RATIO": "2.4%/词条",
"ANOMALY_MASTERY": "3%/词条",
"SP_REGEN": "6%/词条",
}
# 副词条最大值
sc_max_value = 40
# 计算结果缓存文件路径
ID_CACHE_JSON = "./results/id_cache.json"
results_dir = "./results"
# 六元素翻译对应表
element_mapping: dict[ElementType, str] = {
0: "物理",
1: "火",
2: "冰",
3: "电",
4: "以太",
5: "烈霜",
6: "玄墨",
}
# ID重复时抛出的自定义异常类
class IDDuplicateError(Exception):
"""当检测到重复ID时抛出此异常"""
pass
# 确保在文件末尾删除临时变量
del __lf_character
del __lf_weapon
del __lf_equip
================================================
FILE: zsim/lib_webui/doc_pages/page_apl_doc.py
================================================
import os
import streamlit as st
from zsim.define import DOCS_DIR
def show_apl_doc():
apl_doc_path = os.path.abspath(os.path.join(DOCS_DIR, "ZZZSim_APL功能技术文档.md"))
with open(apl_doc_path, "r", encoding="utf-8") as f:
apl_doc_content = f.read()
st.markdown(apl_doc_content, unsafe_allow_html=True)
show_apl_doc()
================================================
FILE: zsim/lib_webui/doc_pages/page_char_support.py
================================================
from pathlib import Path
import polars as pl
import streamlit as st
from zsim.define import DOCS_DIR
@st.dialog("关于:角色支持列表", width="large")
def dialog_about_char_support() -> None:
docs_path = Path(DOCS_DIR)
about_doc_path = docs_path / "角色支持介绍.md"
with open(about_doc_path, "r", encoding="utf-8") as f:
md_content = f.read()
st.markdown(md_content, unsafe_allow_html=True)
# 显示角色与CID对应表
def show_char_cid_mapping() -> None:
"""
显示角色与CID对应表。
该函数会从 CSV 文件构建一个 Polars DataFrame,计算“角色支持度”,
并将“角色支持度”、“技能测帧”、“Buff支持”、“影画支持”列的数值转换为图标,
然后按照指定的列顺序(角色名, CID, 角色支持度, 技能测帧, Buff支持, 影画支持)
在 Streamlit 界面上展示该表格。
计算角色支持度
规则:
-1: 不支持
0: 不完全支持
1: 完全支持
详细逻辑:
1. 如果“动作建模”>=1、“Buff支持”>=1、“影画支持”>=1,则“角色支持度”为 1。
2. 否则,如果“动作建模”>=0 且“Buff支持”>=0,则“角色支持度”为 0。
3. 其他所有情况,“角色支持度”为 -1。
"""
st.markdown("### 角色支持列表")
st.caption("角色与CID的对应关系仅与本模拟器内部功能有关")
# 从 character.csv 加载数据
lf = pl.scan_csv("./zsim/data/character.csv")
def map_support_to_icon(value: float | int) -> str:
"""将支持度数值转换为图标"""
if value >= 1:
return "✅ 完全"
elif value <= -1:
return "❌ 不支持"
else:
return "⚠️ 不完全"
lf_with_support_level = lf.with_columns(
pl.when((pl.col("动作建模") >= 1) & (pl.col("Buff支持") >= 1) & (pl.col("影画支持") >= 1))
.then(pl.lit(1))
.when((pl.col("动作建模") >= 0) & (pl.col("Buff支持") >= 0))
.then(pl.lit(0))
.otherwise(pl.lit(-1))
.alias("角色支持度")
)
# 将支持度相关列的数值转换为图标
lf_with_icons = lf_with_support_level.with_columns(
pl.col("name").alias("角色名称"),
pl.col("角色支持度")
.map_elements(map_support_to_icon, return_dtype=pl.String)
.alias("角色支持度"),
pl.col("动作建模")
.map_elements(map_support_to_icon, return_dtype=pl.String)
.alias("动作建模"),
pl.col("精细测帧")
.map_elements(map_support_to_icon, return_dtype=pl.String)
.alias("精细测帧"),
pl.col("Buff支持")
.map_elements(map_support_to_icon, return_dtype=pl.String)
.alias("Buff支持"),
pl.col("影画支持")
.map_elements(map_support_to_icon, return_dtype=pl.String)
.alias("影画支持"),
)
# 列顺序: 角色名 (name), CID, 角色支持度, 动作建模, 精细测帧, Buff支持, 影画支持
selected_columns = [
"角色名称",
"CID",
"角色支持度",
"动作建模",
"精细测帧",
"Buff支持",
"影画支持",
]
selected_lf = lf_with_icons.select(selected_columns)
st.dataframe(selected_lf)
if st.button("关于"):
dialog_about_char_support()
show_char_cid_mapping()
================================================
FILE: zsim/lib_webui/doc_pages/page_contribution.py
================================================
import os
import streamlit as st
from zsim.define import DOCS_DIR
def show_apl_doc():
apl_doc_path = os.path.abspath(os.path.join(DOCS_DIR, "ReadMe.md"))
with open(apl_doc_path, "r", encoding="utf-8") as f:
apl_doc_content = f.read()
st.markdown(apl_doc_content, unsafe_allow_html=True)
show_apl_doc()
================================================
FILE: zsim/lib_webui/doc_pages/page_equip_support.py
================================================
================================================
FILE: zsim/lib_webui/doc_pages/page_start_up.py
================================================
================================================
FILE: zsim/lib_webui/doc_pages/page_weapon_support.py
================================================
================================================
FILE: zsim/lib_webui/multiprocess_wrapper.py
================================================
import io
from contextlib import redirect_stdout
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from zsim.simulator.config_classes import (
SimulationConfig as SimCfg,
)
def run_single_simulation(stop_tick: int) -> str:
"""运行单次模拟的包装函数
这个函数在模块级别定义,可以被pickle序列化
Args:
stop_tick: 模拟停止的tick数
Returns:
模拟结果字符串
"""
from zsim.simulator import Simulator # 真正启动模拟再导入,以优化启动速度
f = io.StringIO()
with redirect_stdout(f):
print("启动子进程")
sim_ins = Simulator()
sim_ins.main_loop(stop_tick)
return f.getvalue()
def run_parallel_simulation(sim_cfg: "SimCfg") -> str:
"""运行并行模拟的包装函数
这个函数在模块级别定义,可以被pickle序列化
Args:
stop_tick: 模拟停止的tick数
sim_cfg: 模拟配置
Returns:
模拟结果字符串
"""
from zsim.simulator import Simulator # 真正启动模拟再导入,以优化启动速度
f = io.StringIO()
with redirect_stdout(f):
print("启动子进程")
sim_ins = Simulator()
sim_ins.main_loop(
stop_tick=sim_cfg.stop_tick if sim_cfg.stop_tick is not None else 1000, sim_cfg=sim_cfg
)
return f.getvalue()
================================================
FILE: zsim/lib_webui/process_apl_editor.py
================================================
import copy
import os
import time
import tomllib
from typing import Any, Sequence
import pandas as pd
import streamlit as st
import tomli_w
from streamlit_ace import st_ace
from zsim.define import (
CHARACTER_DATA_PATH,
COSTOM_APL_DIR,
DEFAULT_APL_DIR,
saved_char_config,
)
from .constants import CHAR_CID_MAPPING
class APLArchive:
default_apl_map: dict[str, dict]
custom_apl_map: dict[str, dict]
options: Sequence[str]
title_apl_map: dict[str, dict]
title_file_name_map: dict[str, str]
def __init__(self):
self.refresh()
def refresh(self):
self.default_apl_map = self.__get_apl_toml(DEFAULT_APL_DIR)
self.custom_apl_map = self.__get_apl_toml(COSTOM_APL_DIR)
all_apl_list: list[dict] = list(self.default_apl_map.values()) + list(
self.custom_apl_map.values()
)
all_apl_map: dict[str, dict] = self.default_apl_map | self.custom_apl_map
self.title_apl_map = {
apl.get("general", {}).get("title", None): apl for apl in all_apl_list
}
self.title_file_name_map = {
apl.get("general", {}).get("title", None): relative_path
for relative_path, apl in all_apl_map.items()
}
self.options = [title for title in self.title_apl_map.keys() if title is not None]
def save_apl_data(self, title: str, edited_data: dict[str, Any]):
"""保存编辑后的APL数据到对应的TOML文件。
Args:
title (str): 要保存的APL的标题。
edited_data (dict[str, Any]): 包含编辑后APL信息的字典。
Raises:
ValueError: 如果找不到标题对应的文件路径或保存失败。
"""
if self.title_file_name_map is not None:
relative_path = self.title_file_name_map.get(title)
if not relative_path:
raise ValueError(f"错误:找不到标题 '{title}' 对应的文件路径。")
# 确定绝对路径
if relative_path in self.default_apl_map:
base_dir = DEFAULT_APL_DIR
elif relative_path in self.custom_apl_map:
base_dir = COSTOM_APL_DIR
else:
raise ValueError(f"内部错误:无法确定文件 '{relative_path}' 的所属目录。")
absolute_path = os.path.abspath(os.path.join(base_dir, relative_path))
try:
# 深拷贝以避免修改传入的字典
data_to_save = copy.deepcopy(edited_data)
# 注意:角色列表现在直接由 multiselect 提供,无需解析字符串
# if 'characters' in data_to_save:
# chars_info = data_to_save['characters']
# # 移除旧的字符串解析逻辑
# chars_info.pop('required_str_temp', None)
# chars_info.pop('optional_str_temp', None)
# 更新最后修改时间
if "general" in data_to_save:
from datetime import datetime
now_str = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "+08:00"
data_to_save["general"]["latest_change_time"] = now_str
else:
# 如果没有 general 部分,也尝试添加时间戳
from datetime import datetime
now_str = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "+08:00"
data_to_save["general"] = {"latest_change_time": now_str}
# 保存到文件
with open(absolute_path, "wb") as f:
tomli_w.dump(data_to_save, f, multiline_strings=True)
# 刷新内部缓存
self.refresh()
except FileNotFoundError:
raise ValueError(f"错误:找不到文件 '{absolute_path}'。")
except Exception as e:
raise ValueError(f"保存APL文件时出错:{e}")
def get_general(self, title: str):
"""获取指定标题的APL的一般信息。"""
assert self.title_apl_map is not None
return self.title_apl_map.get(title, {}).get("general", {})
def get_apl_data(self, title: str) -> dict[str, Any] | None:
"""获取指定标题的完整APL数据"""
assert self.title_apl_map is not None
return self.title_apl_map.get(title)
def get_title_from_path(self, path: str) -> str | None:
"""根据路径获取对应的标题"""
assert self.title_file_name_map is not None
for title, apl_path in self.title_file_name_map.items():
if apl_path in path:
return title
return None
def get_origin_relative_path(self, title: str) -> str | None:
"""根据标题获取其在项目中的相对文件路径。
Args:
title (str): APL的标题。
Returns:
str | None: APL文件相对于项目根目录的相对路径,如果找不到则返回 None。
"""
# 从 title_file_name_map 获取相对于 APL 基础目录的路径
relative_path_in_apl_dir = self.title_file_name_map.get(title)
if relative_path_in_apl_dir is None:
# st.error(f"错误:找不到标题 '{title}' 对应的文件路径。")
return None
# 确定文件属于哪个基础目录 (default 或 custom)
assert self.default_apl_map is not None and self.custom_apl_map is not None, (
"APL映射未初始化。请先调用 refresh() 方法。"
)
if relative_path_in_apl_dir in self.default_apl_map:
base_dir_relative_to_project = DEFAULT_APL_DIR
elif relative_path_in_apl_dir in self.custom_apl_map:
base_dir_relative_to_project = COSTOM_APL_DIR
else:
# st.error(f"内部错误:无法确定文件 '{relative_path_in_apl_dir}' 的所属目录。")
return None # 或者可以抛出异常
# 组合基础目录的项目相对路径和文件在基础目录内的相对路径
# 使用 os.path.join 来正确处理路径分隔符
# 替换反斜杠为正斜杠以保持一致性
full_relative_path = os.path.join(
base_dir_relative_to_project, relative_path_in_apl_dir
).replace("\\", "/")
return full_relative_path
def change_title(self, former_title: str, new_title: str, new_comment: str | None = None):
# Step 1: Check if the former title exists
if former_title not in self.title_apl_map.keys():
st.error(f"错误:原标题 '{former_title}' 不存在。")
return
# Step 2: Check if the new title already exists (and is not the same as the former title)
if new_title != former_title and new_title in self.title_apl_map.keys():
st.error(f"错误:新标题 '{new_title}' 已被其他APL使用。")
return
# Step 3: Check if the new title is the same as the former title
if new_title == former_title and new_comment == self.title_apl_map.get(
former_title, {}
).get("general", {}).get("comment", None):
st.warning("新旧标题相同,且未提供新注释,无需更改。")
return
# Step 4: Get the relative path for the former title
relative_path = self.title_file_name_map.get(former_title)
if not relative_path:
st.error(
f"内部错误:找不到标题 '{former_title}' 对应的文件路径。"
) # Should not happen if step 1 passed
return
# Determine the absolute path
assert self.default_apl_map is not None and self.custom_apl_map is not None, (
"APL映射未初始化。请先调用 refresh() 方法。"
)
if relative_path in self.default_apl_map:
base_dir = DEFAULT_APL_DIR
elif relative_path in self.custom_apl_map:
base_dir = COSTOM_APL_DIR
else:
st.error(f"内部错误:无法确定文件 '{relative_path}' 的所属目录。") # Should not happen
return
absolute_path = os.path.abspath(os.path.join(base_dir, relative_path))
# Step 5 & 6: Update the title and comment in the TOML file and save
try:
with open(absolute_path, "rb") as f:
apl_data = tomllib.load(f)
if "general" not in apl_data:
apl_data["general"] = {}
apl_data["general"]["title"] = new_title
if new_comment is not None:
apl_data["general"]["comment"] = new_comment
with open(absolute_path, "wb") as f:
tomli_w.dump(apl_data, f, multiline_strings=True)
st.success("正在保存...")
time.sleep(1)
# Step 7: Refresh the APL archive
self.refresh()
except FileNotFoundError:
st.error(f"错误:找不到文件 '{absolute_path}'。")
except Exception as e:
st.error(f"保存APL文件时出错:{e}")
def __get_apl_toml(self, apl_path: str) -> dict[str, dict]:
"""根据APL地址获取APL toml的内容
:param apl_path: APL文件或目录路径
:return: {relative_path: toml_content} 字典,如果路径无效则返回空字典
"""
toml_dict_map = {}
# 将输入路径转换为绝对路径
base_path = os.path.abspath(apl_path)
try:
if os.path.isfile(base_path):
# 如果是文件,直接处理
if base_path.endswith(".toml"):
try:
with open(base_path, "rb") as f:
toml_data: dict = tomllib.load(f)
if toml_data.get("apl_logic", {}).get("logic") is not None:
relative_path = os.path.basename(base_path)
toml_dict_map[relative_path] = toml_data
except Exception as e:
st.exception(Exception(f"Error loading TOML file {base_path}: {e}"))
elif os.path.isdir(base_path):
# 如果是目录,遍历所有toml文件
for root, _, files in os.walk(base_path):
for file in files:
if file.endswith(".toml"):
file_path = os.path.join(root, file)
try:
with open(file_path, "rb") as f:
toml_data: dict = tomllib.load(f)
if toml_data.get("apl_logic", {}).get("logic") is not None:
relative_path = os.path.relpath(file_path, base_path)
toml_dict_map[relative_path] = toml_data
except Exception as e:
st.exception(Exception(f"Error loading TOML file {file_path}: {e}"))
else:
# 如果路径既不是文件也不是目录,则记录警告或错误
st.warning(f"APL path does not exist or is not a file/directory: {apl_path}")
return toml_dict_map
except Exception as e:
raise ValueError(f"读取APL文件失败:{str(e)}")
class APLJudgeTool:
def __init__(self, raw_apl: dict) -> None:
self.raw_apl: dict = raw_apl
self.characters: dict = raw_apl.get("characters", {})
self.required_chars: list[str] = [
self._convert_to_name(char) for char in self.characters.get("required", [])
]
self.optional_chars: list[str] = [
self._convert_to_name(char) for char in self.characters.get("optional", [])
]
self.char_configs: dict[str, dict] = {
self._convert_to_name(k): v
for k, v in self.characters.items()
if k not in ["required", "optional"]
} # {name: {config}}
self.apl_logic: str = raw_apl.get("apl_logic", {}).get("logic", "")
self.saved_char_config: dict = saved_char_config
def _convert_to_name(self, char_identifier: str | int) -> str:
"""将任何角色标识(名称或CID)统一转换为角色名称"""
# 如果输入的是CID,通过反向查找获取名称
for name, cid in CHAR_CID_MAPPING.items():
if cid == char_identifier:
return name
# 如果输入的是名称或未知标识,直接返回
return str(char_identifier)
def judge_requried_chars(self) -> tuple[bool, list[str]]:
"""判断是否满足所有必须角色"""
missing_chars = []
for char in self.required_chars:
if char not in self.saved_char_config.get("name_box", []):
missing_chars.append(char)
return len(missing_chars) == 0, missing_chars
def judge_optional_chars(self) -> tuple[bool, list[str]]:
"""判断是否满足所有可选角色"""
missing_chars = []
for char in self.optional_chars:
if char not in self.saved_char_config.get("name_box", []):
missing_chars.append(char)
return len(missing_chars) == 0, missing_chars
def judge_char_config(self) -> tuple[bool, dict[str, str | int]]:
"""判断是否满足所有角色配置"""
missing_configs = {}
char_name: str # 角色名称
config: dict # 角色配置字典
key: str # 配置项名称
value: str | int # 配置项值
saved_value: str | int | list[str | int] # 保存的配置项值
for char_name, config in self.char_configs.items():
for key, value in config.items():
saved_value = self.saved_char_config.get(char_name, {}).get(key)
target_value = str(value)
pass_through_values = ["", "None", "-1", "[]"]
# 如果目标值在pass_through中,直接跳过后续判断
if target_value in pass_through_values:
continue
# 判断saved_value是否为列表
if isinstance(saved_value, list):
# 如果是列表,检查保存值是否在列表中
if any(v in target_value for v in [str(v) for v in saved_value]):
missing_configs[char_name] = missing_configs.get(char_name, {})
missing_configs[char_name][key] = value
else:
# 如果不是列表,按相等判断
if str(saved_value) not in target_value:
missing_configs[char_name] = missing_configs.get(char_name, {})
missing_configs[char_name][key] = value
return len(missing_configs) == 0, missing_configs
def display_apl_details(
apl_data: dict[str, Any], apl_title: str, apl_archive: APLArchive
): # <-- 添加 apl_archive 参数
"""使用Streamlit组件显示和编辑APL的详细信息。
Args:
apl_data (dict[str, Any]): 包含APL信息的字典。
apl_title (str): 当前编辑的APL标题,用于session_state键。
"""
if not apl_data:
st.warning("未找到选定的APL数据。")
return
st.divider()
st.subheader(f"编辑 APL:{apl_title}") # Use title in subheader
# Initialize session state for edited data if not present
session_key = f"edited_apl_{apl_title}"
if session_key not in st.session_state:
# Deep copy to avoid modifying the original dict directly
st.session_state[session_key] = copy.deepcopy(apl_data)
edited_data: dict = st.session_state[session_key]
# --- General 信息编辑 ---
general_info = edited_data.get("general", {})
cols_general = st.columns(2)
# Title editing might need special handling due to its use as an identifier
# For now, make it read-only or handle rename separately as per roadmap
cols_general[0].markdown(
f"**标题:** (重命名请使用上方按钮)**{general_info.get('title', 'N/A')}** ",
unsafe_allow_html=True,
)
general_info["author"] = cols_general[1].text_input(
"作者", value=general_info.get("author", "")
)
# Display create/change times - typically read-only
general_info["comment"] = st.text_area("注释", value=general_info.get("comment", ""))
edited_data["general"] = general_info # Update the edited data
# --- Characters 信息编辑 (Basic Framework) ---
st.markdown("**角色信息**")
characters_info: dict = edited_data.setdefault("characters", {}) # 使用 setdefault 确保存在
# --- 读取角色列表 ---
try:
if os.path.exists(CHARACTER_DATA_PATH):
df_char = pd.read_csv(CHARACTER_DATA_PATH)
all_character_names = df_char["name"].unique().tolist()
else:
st.error(f"角色数据文件未找到: {CHARACTER_DATA_PATH}")
all_character_names = [] # 提供空列表以避免后续错误
except Exception as e:
st.error(f"读取角色数据时出错: {e}")
all_character_names = []
# --- 使用多选框编辑必须/可选角色 ---
required_list = characters_info.get("required", [])
optional_list = characters_info.get("optional", [])
# 过滤掉不在可选列表中的已选角色(可能来自旧数据或手动修改)
valid_required = [char for char in required_list if char in all_character_names]
valid_optional = [char for char in optional_list if char in all_character_names]
# 初始化 session_state
if f"{session_key}_required_chars" not in st.session_state:
st.session_state[f"{session_key}_required_chars"] = valid_required
if f"{session_key}_optional_chars" not in st.session_state:
st.session_state[f"{session_key}_optional_chars"] = valid_optional
col1, col2 = st.columns(2)
with col1:
# 更新 characters_info 中的列表为过滤后的有效列表
st.multiselect(
"必须角色",
options=all_character_names,
key=f"{session_key}_required_chars", # 添加唯一 key
max_selections=3,
)
# 用 session_state 结果同步到 characters_info
characters_info["required"] = st.session_state[f"{session_key}_required_chars"]
with col2:
st.multiselect(
"可选角色",
options=all_character_names,
key=f"{session_key}_optional_chars",
)
characters_info["optional"] = st.session_state[f"{session_key}_optional_chars"]
# 清理掉不在 selected_chars 中的角色配置
# 需要在这里重新获取最新的 selected_chars 列表
_selected_chars_for_cleanup = characters_info.get("required", []) + characters_info.get(
"optional", []
)
_current_config_keys = list(characters_info.keys())
for _key in _current_config_keys:
if _key not in _selected_chars_for_cleanup and _key not in [
"required",
"optional",
]:
# 确保 key 存在再删除,避免潜在错误
if _key in characters_info:
del characters_info[_key]
# --- 编辑角色配置 ---
st.markdown("**角色配置编辑**")
selected_chars = characters_info.get("required", []) + characters_info.get("optional", [])
if not selected_chars:
st.markdown("- 请先在上方选择“必须角色”或“可选角色”。")
else:
# 确保 characters_info 中存在所有选定角色的条目
cols = st.columns(len(selected_chars))
i = 0
for char_name in selected_chars:
if char_name not in characters_info:
characters_info[char_name] = {} # 如果不存在则初始化为空字典
# 为每个选定的角色显示编辑界面
for char_name in selected_chars:
with cols[i]:
i += 1
if char_name not in all_character_names: # 跳过无效的角色名
continue
# 获取或初始化该角色的配置
char_config = characters_info.setdefault(char_name, {})
with st.expander(f"编辑角色配置需求: {char_name}", expanded=False):
# cinema 编辑 (使用多选框)
cinema_options = list(range(7)) # 选项为 0 到 6
current_cinema_val = char_config.get("cinema", [])
# 确保 current_cinema_val 是列表,并且元素是整数
if isinstance(current_cinema_val, int):
default_cinema = (
[current_cinema_val] if current_cinema_val in cinema_options else []
)
elif isinstance(current_cinema_val, list):
# 过滤掉无效值或非整数值
default_cinema = [
int(v)
for v in current_cinema_val
if isinstance(v, (int, str))
and str(v).isdigit()
and int(v) in cinema_options
]
elif isinstance(current_cinema_val, str) and current_cinema_val.isdigit():
default_cinema = (
[int(current_cinema_val)]
if int(current_cinema_val) in cinema_options
else []
)
else:
default_cinema = [] # 如果是其他类型或空字符串,默认为空列表
# 使用 st.multiselect 控件
selected_cinema = st.multiselect(
"影画等级 (可多选)",
options=cinema_options,
default=default_cinema,
key=f"{session_key}_{char_name}_cinema",
)
# 直接将选择的列表(整数)保存到 char_config
# 如果用户没有选择任何项,则保存空列表
char_config["cinema"] = selected_cinema
# weapon 编辑
char_config["weapon"] = st.text_input(
"音擎",
value=str(char_config.get("weapon", "")),
key=f"{session_key}_{char_name}_weapon",
)
# equip_set4 编辑
char_config["equip_set4"] = st.text_input(
"四件套",
value=str(char_config.get("equip_set4", "")),
key=f"{session_key}_{char_name}_equip_set4",
)
# 更新 session state 中的 characters 数据
edited_data["characters"] = characters_info
# --- APL Logic 编辑 ---
st.markdown("**APL 逻辑**")
st.page_link("lib_webui/doc_pages/page_apl_doc.py", icon="📖", label="APL设计书")
apl_logic_info = edited_data.get("apl_logic", {})
st.write("逻辑编写:")
# 使用 st_ace 替换 st.text_area 以获得更好的代码编辑体验
apl_logic_info["logic"] = st_ace(
value=apl_logic_info.get("logic", ""),
language="python",
theme="github", # 选择一个主题
keybinding="vscode", # 可选:设置键位绑定
height=800, # 设置编辑器高度
auto_update=True, # 自动更新内容
key=f"{session_key}_apl_logic_editor", # 添加唯一 key
)
edited_data["apl_logic"] = apl_logic_info # Update the edited data
# --- 在保存按钮前添加警告 ---
relative_path = apl_archive.title_file_name_map.get(apl_title)
if relative_path and relative_path in apl_archive.default_apl_map:
st.warning(
"警告:正在修改非自建APL,这可能会在更新时被覆盖。请考虑复制后修改。",
icon="⚠️",
)
# --- 保存按钮 ---
st.divider()
if st.button("保存对 APL 的修改"):
st.session_state[session_key] = edited_data
try:
# 调用保存方法 (edited_data 中的 characters 已包含正确的列表)
apl_archive.save_apl_data(apl_title, edited_data)
st.success(f"APL '{apl_title}' 已成功保存!")
# 清理 session state 并刷新页面
del st.session_state[session_key]
time.sleep(1) # 短暂显示成功消息
st.rerun()
except ValueError as e:
st.error(f"保存失败:{e}")
except Exception as e:
st.error(f"保存过程中发生意外错误:{e}")
def go_apl_editor():
apl_archive = APLArchive()
st.write("选择一个APL")
col1, col2, col3, col4 = st.columns([3, 1, 1, 1])
with col1:
selected_title = st.selectbox(
"APL选项",
apl_archive.options,
key="selected_apl_title",
label_visibility="collapsed",
)
with col2:
@st.dialog("APL详情")
def show_apl_detail():
general = apl_archive.get_general(selected_title)
st.markdown(
f"""
{general.get("title", "无标题")}
👤 作者:{general.get("author", "佚名")}
📅 创建时间:{general.get("create_time", "无")}
🔄 上次修改:{general.get("latest_change_time", "无")}
""",
unsafe_allow_html=True,
)
st.write("")
if st.button("确定", use_container_width=True):
st.rerun()
if st.button("更多", use_container_width=True):
show_apl_detail()
with col3:
@st.dialog("APL重命名")
def rename_apl():
relative_path = apl_archive.title_file_name_map.get(selected_title)
if relative_path in apl_archive.default_apl_map:
st.warning("警告:正在修改非自建APL,你需要知道自己在做什么", icon="⚠️")
new_title = st.text_input("新标题", value=selected_title)
new_comment = st.text_input(
"新注释",
value=apl_archive.get_general(selected_title).get("comment", ""),
)
if st.button("确定", use_container_width=True):
apl_archive.change_title(selected_title, new_title, new_comment)
# 刷新 APL 列表并切换到新的标题
apl_archive.refresh()
st.session_state["selected_apl_title"] = new_title
st.rerun()
if st.button("重命名", use_container_width=True):
rename_apl()
with col4:
# 新建 APL 对话框
@st.dialog("新建APL")
def create_new_apl():
st.write("基于模板创建新的APL")
# 读取模板文件内容
template_path = os.path.abspath(os.path.join(DEFAULT_APL_DIR, "APL template.toml"))
try:
with open(template_path, "rb") as f:
template_data = tomllib.load(f)
except FileNotFoundError:
st.error(f"错误:找不到模板文件 '{template_path}'")
return
except Exception as e:
st.error(f"读取模板文件时出错:{e}")
return
new_title = st.text_input("新标题", placeholder="请输入新APL的标题")
new_author = st.text_input("作者 (可选)", placeholder="请输入作者名称")
new_comment = st.text_input("注释 (可选)", placeholder="请输入注释信息")
if st.button("创建", use_container_width=True):
if not new_title:
st.warning("请输入新标题。")
return
if new_title in apl_archive.title_apl_map:
st.error(f"错误:标题 '{new_title}' 已存在。")
return
# 准备新的 APL 数据
new_apl_data = template_data.copy()
if "general" not in new_apl_data:
new_apl_data["general"] = {}
from datetime import datetime
now_str = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "+08:00"
new_apl_data["general"]["title"] = new_title
if new_author:
new_apl_data["general"]["author"] = new_author
else:
# 如果用户未输入作者,可以保留模板中的或设为默认值
new_apl_data["general"]["author"] = template_data.get("general", {}).get(
"author", "未知作者"
)
if new_comment:
new_apl_data["general"]["comment"] = new_comment
else:
new_apl_data["general"]["comment"] = template_data.get("general", {}).get(
"comment", ""
)
new_apl_data["general"]["create_time"] = now_str
new_apl_data["general"]["latest_change_time"] = now_str
# 生成文件名 (简单处理,替换空格和特殊字符)
safe_filename = "".join(c for c in new_title if c.isalnum() or c in "-_ ").rstrip()
safe_filename = safe_filename.replace(" ", "_") + ".toml"
new_file_path = os.path.join(COSTOM_APL_DIR, safe_filename)
try:
# 确保目录存在
os.makedirs(COSTOM_APL_DIR, exist_ok=True)
# 保存新文件
with open(new_file_path, "wb") as f:
tomli_w.dump(new_apl_data, f, multiline_strings=True)
st.success(f"APL '{new_title}' 已成功创建并保存至 '{safe_filename}'")
time.sleep(1)
# 刷新 APL 列表
apl_archive.refresh()
st.session_state["selected_apl_title"] = new_title
st.rerun()
except Exception as e:
st.error(f"创建或保存新APL文件时出错:{e}")
# 绑定新建按钮到对话框
if st.button("新建", use_container_width=True):
create_new_apl()
# 在选择框下方显示选定APL的详细信息
if selected_title:
selected_apl_data = apl_archive.get_apl_data(selected_title)
# 传递 apl_archive 实例
if selected_apl_data is None:
st.error(f"未找到标题为 '{selected_title}' 的APL数据。")
else:
# 调用显示函数
display_apl_details(selected_apl_data, selected_title, apl_archive)
================================================
FILE: zsim/lib_webui/process_buff_result.py
================================================
import asyncio
import json
import os
from typing import Any
import aiofiles
import aiofiles.os
import plotly.graph_objects as go
import polars as pl
import streamlit as st
from zsim.define import results_dir
from .constants import BUFF_EFFECT_MAPPING
def _prepare_buff_timeline_data(df: pl.DataFrame) -> list[dict[str, Any]]:
"""将包含时间序列BUFF数据的Polars DataFrame转换为适用于Plotly时间线的格式。
Args:
df (pl.DataFrame): 输入的Polars DataFrame,应包含 'time_tick' 列,
其余列为各个BUFF的状态,列名为BUFF名称。
单元格中的值代表该BUFF在对应time_tick的状态值,
null 值表示BUFF在该tick不生效。
Returns:
list[dict[str, Any]]: 转换后的数据列表,每个字典代表一个BUFF生效的时间段,
包含 'Task' (BUFF名称), 'Start' (开始tick),
'Finish' (结束tick), 'Value' (BUFF值)。
"""
timeline_data: list[dict[str, Any]] = []
buff_columns = [col for col in df.columns if col != "time_tick"]
for buff_name in buff_columns:
# 将空值填充为0.0并筛选出来
buff_df = df.select(["time_tick", buff_name]).with_columns(pl.col(buff_name).fill_null(0.0))
if buff_df.height == 0:
continue
# 尝试将 BUFF 值列转换为数值类型,无法转换的设为 null
buff_df = buff_df.with_columns(pl.col(buff_name).cast(pl.Float32, strict=False))
# 计算值变化的点
buff_df = buff_df.with_columns(pl.col(buff_name).diff().alias("value_diff"))
# 标记每个连续段的开始
# 条件:第一行,或者值发生变化
buff_df = buff_df.with_columns(
((pl.arange(0, pl.count()) == 0) | (pl.col("value_diff") != 0)).alias("is_start")
)
# 为每个连续段分配一个ID
# 将布尔值转换为整数,以便进行累加
buff_df = buff_df.with_columns(
pl.col("is_start").cast(pl.Int32).cum_sum().alias("group_id")
)
# 按段聚合,找到起始tick、结束tick和对应的值
grouped = buff_df.group_by("group_id").agg(
pl.first("time_tick").alias("Start"),
pl.last("time_tick").alias("last_valid_tick"),
pl.first(buff_name).alias("Value"),
)
# 计算结束 tick (Finish)
# 使用当前段的最后一个有效tick作为结束点
grouped = grouped.with_columns(pl.col("last_valid_tick").alias("Finish"))
# 转换结果为字典列表
for row in grouped.select(["Start", "Finish", "Value"]).iter_rows(named=True):
# 过滤掉 Value 为 null 的行
if row["Value"]:
timeline_data.append(
{
"Task": buff_name,
"Start": int(row["Start"]),
"Finish": int(row["Finish"]),
"Value": row["Value"],
}
)
return timeline_data
def _draw_buff_timeline_charts(all_buff_data: dict[str, list[dict[str, Any]]]) -> None:
"""根据处理后的BUFF数据绘制多个时间线图表。
Args:
all_buff_data (dict[str, list[dict[str, Any]]]): 包含BUFF时间线数据的字典,
键为文件标识符,值为时间线数据列表。
"""
if not all_buff_data:
st.warning("没有可用于绘制图表的BUFF数据。")
return
st.subheader("BUFF时间线: ")
for file_key, buff_data in all_buff_data.items():
if not buff_data:
continue
with st.expander(f"{file_key}"):
# Plotly 加载时直接获取 buff 效果映射关系
df_timeline = pl.DataFrame(buff_data).with_columns(
pl.col("Task").replace(BUFF_EFFECT_MAPPING, default=None).alias("Effect")
)
# 准备悬停文本 - 包含Value、Start、Finish 以及 Effect 信息
df_timeline = df_timeline.with_columns(
pl.format(
"层数: {} ({}~{})\n每层效果: {}",
pl.col("Value"),
pl.col("Start"),
pl.col("Finish"),
pl.col("Effect"),
).alias("hover_text")
)
fig = go.Figure(
data=[
go.Bar(
name=row["Task"],
x=[(row["Finish"] - row["Start"])],
base=[row["Start"]],
y=[row["Task"]],
orientation="h",
text=f"{row['Value']}",
hoverinfo="text",
hovertext=row["hover_text"],
marker=dict(opacity=0.7),
)
for row in df_timeline.iter_rows(named=True)
]
)
fig.update_layout(
title=f"{file_key} BUFF 时间线",
xaxis_title="时间 (帧)",
yaxis_title="BUFF名称",
barmode="stack",
yaxis=dict(autorange="reversed"), # 反转Y轴
height=max(400, len(df_timeline["Task"].unique()) * 30), # 动态调整高度
hovermode="closest",
showlegend=False, # 隐藏图例
)
st.plotly_chart(fig, use_container_width=True)
def _load_cached_buff_data(rid: int | str) -> dict[str, list[dict[str, Any]]] | None:
"""尝试从JSON缓存文件加载BUFF时间线数据。"""
buff_log_path = os.path.join(results_dir, str(rid), "buff_log")
json_file_path = os.path.join(buff_log_path, "buff_timeline_data.json")
if os.path.exists(json_file_path):
try:
with open(json_file_path, "r", encoding="utf-8") as f:
return json.load(f)
except Exception:
# 加载失败,将视为缓存不存在
return None
return None
async def prepare_buff_data_and_cache(
rid: int | str,
) -> dict[str, list[dict[str, Any]]] | None:
"""异步处理BUFF日志CSV文件,生成时间线数据,并缓存到JSON文件。
此函数不处理UI反馈,仅负责数据处理和文件操作。
Args:
rid (int | str): 运行ID。
Returns:
dict[str, list[dict[str, Any]]] | None: 处理后的BUFF时间线数据字典,
如果处理失败或无CSV文件则返回None。
如果找到CSV但处理后无数据,返回空字典 {}。
"""
buff_log_path = os.path.join(results_dir, str(rid), "buff_log")
json_file_path = os.path.join(buff_log_path, "buff_timeline_data.json")
if not await aiofiles.os.path.exists(buff_log_path):
# 日志目录不存在,无法处理
return None
try:
all_files = await aiofiles.os.listdir(buff_log_path)
csv_files = [f for f in all_files if f.endswith(".csv")]
except FileNotFoundError:
# listdir 可能在目录刚创建时失败,或者权限问题
return None
except Exception as e:
print(f"列出目录 {buff_log_path} 时发生错误: {e}")
return None
if not csv_files:
# 没有CSV文件,无需处理,但也无需创建JSON。返回空字典表示成功但无数据。
return {}
all_buff_data: dict[str, list[dict[str, Any]]] = {}
processed_csv_files: list[str] = []
tasks = []
async def process_csv(filename: str):
nonlocal all_buff_data, processed_csv_files
csv_file_path = os.path.join(buff_log_path, filename)
try:
# 使用 asyncio.to_thread 在单独的线程中运行同步的 polars 操作
# 注意:Polars 的 read_csv 默认是多线程的,但为了与 aiofiles 配合,仍使用 to_thread
df = await asyncio.to_thread(pl.read_csv, csv_file_path)
file_key = filename.replace(".csv", "")
# _prepare_buff_timeline_data 本身是同步的,可以在这里直接调用
buff_data = _prepare_buff_timeline_data(df)
all_buff_data[file_key] = buff_data
processed_csv_files.append(csv_file_path)
except Exception as e:
print(f"处理文件 {csv_file_path} 时发生错误: {e}")
# 可以选择在这里标记错误,或者让 gather 捕获
raise # 重新抛出异常,让 gather 知道有错误
# 为每个CSV文件创建一个处理任务
for filename in csv_files:
tasks.append(process_csv(filename))
# 并发执行所有CSV处理任务
results = await asyncio.gather(*tasks, return_exceptions=True)
# 检查是否有处理错误
has_processing_error = any(isinstance(res, Exception) for res in results)
if has_processing_error:
print("处理CSV文件时至少发生一个错误。")
return None
# 如果没有处理错误或者决定即使有错误也要继续
if all_buff_data: # 确保有数据才写入
try:
# 异步写入JSON缓存文件
async with aiofiles.open(json_file_path, "w", encoding="utf-8") as f:
await f.write(json.dumps(all_buff_data, indent=4, ensure_ascii=False))
except Exception as e:
print(f"写入JSON文件 {json_file_path} 时发生错误: {e}")
has_processing_error = True # 标记写入错误
# 异步删除原始CSV文件
if processed_csv_files:
delete_tasks = [aiofiles.os.remove(csv_path) for csv_path in processed_csv_files]
delete_results = await asyncio.gather(*delete_tasks, return_exceptions=True)
for i, res in enumerate(delete_results):
if isinstance(res, Exception):
print(f"删除文件 {processed_csv_files[i]} 时发生错误: {res}")
# 删除失败通常不认为是关键错误,只打印日志
# 如果在处理或写入JSON时发生错误,返回None
if has_processing_error:
return None
return all_buff_data
def show_buff_result(rid: int | str) -> None:
"""显示指定运行ID的BUFF结果,优先从缓存加载,否则处理CSV并缓存。"""
st.subheader(f"{rid} 的 BUFF 数据分析")
# 尝试加载缓存数据
cached_data = _load_cached_buff_data(rid)
if cached_data is not None:
st.info("从缓存加载BUFF数据。")
all_buff_data = cached_data
else:
st.info("未找到缓存,正在处理BUFF日志文件...")
all_buff_data = asyncio.run(prepare_buff_data_and_cache(rid))
if all_buff_data is None:
st.error("处理BUFF日志文件失败。")
return
elif not all_buff_data:
st.warning("在日志目录中未找到BUFF相关的CSV文件。")
# 即使没有数据,也绘制一个空状态或提示
_draw_buff_timeline_charts({}) # 传递空字典以显示无数据消息
return
else:
st.success("BUFF日志处理完成并已缓存。")
# 绘制图表
_draw_buff_timeline_charts(all_buff_data)
================================================
FILE: zsim/lib_webui/process_char_config.py
================================================
import streamlit as st
from zsim.define import saved_char_config
from zsim.models.session.session_run import CharConfig
from zsim.sim_progress.Character import character_factory
def display_character_panels(name_box: list[str], use_columns: bool = True) -> None:
"""显示角色面板。
Args:
name_box: 包含角色名称的列表。
use_columns: 是否将角色面板分列显示,默认为 True。
"""
all_char_configs: list[dict] = [
saved_char_config.get(name) for name in name_box if name in saved_char_config
]
characters = []
for config in all_char_configs:
if config:
char_config = CharConfig(**config)
character = character_factory(char_config)
characters.append(character)
st.subheader("角色局外面板")
def _display_panel(character):
"""内部函数,用于显示单个角色面板。"""
with st.expander(character.NAME, expanded=False):
statement = character.statement.statement
col_left, col_right = st.columns(2)
with col_left:
st.markdown(
f"攻击力: {statement.get('ATK', 0):.2f}
",
unsafe_allow_html=True,
)
st.markdown(
f"暴击率: {statement.get('CRIT_rate', 0):.2%}
",
unsafe_allow_html=True,
)
st.markdown(
f"暴击伤害: {statement.get('CRIT_damage', 0):.2%}
",
unsafe_allow_html=True,
)
st.markdown(
f"生命值: {statement.get('HP', 0):.2f}
",
unsafe_allow_html=True,
)
st.markdown(
f"防御力: {statement.get('DEF', 0):.2f}
",
unsafe_allow_html=True,
)
st.markdown(
f"穿透率: {statement.get('PEN_ratio', 0):.2%}
",
unsafe_allow_html=True,
)
st.markdown(
f"穿透值: {statement.get('PEN_numeric', 0):.2f}
",
unsafe_allow_html=True,
)
st.markdown(
f"能量自动回复: {statement.get('sp_regen', 0):.2f}
",
unsafe_allow_html=True,
)
st.markdown(
f"冲击力: {statement.get('IMP', 0):.2f}
",
unsafe_allow_html=True,
)
with col_right:
st.markdown(
f"冰属性伤害: {statement.get('ICE_DMG_bonus', 0):.2%}
",
unsafe_allow_html=True,
)
st.markdown(
f"火属性伤害: {statement.get('FIRE_DMG_bonus', 0):.2%}
",
unsafe_allow_html=True,
)
st.markdown(
f"物理属性伤害: {statement.get('PHY_DMG_bonus', 0):.2%}
",
unsafe_allow_html=True,
)
st.markdown(
f"以太属性伤害: {statement.get('ETHER_DMG_bonus', 0):.2%}
",
unsafe_allow_html=True,
)
st.markdown(
f"电属性伤害: {statement.get('ELECTRIC_DMG_bonus', 0):.2%}
",
unsafe_allow_html=True,
)
st.markdown(
f"异常精通: {statement.get('AP', 0):.2f}
",
unsafe_allow_html=True,
)
st.markdown(
f"异常掌控: {statement.get('AM', 0):.2f}
",
unsafe_allow_html=True,
)
if use_columns:
cols = st.columns(len(characters))
for i, character in enumerate(characters):
with cols[i]:
_display_panel(character)
else:
for character in characters:
_display_panel(character)
@st.dialog("角色面板")
def dialog_character_panels(name_box: list[str]) -> None:
# 默认使用分列显示
display_character_panels(name_box, use_columns=False)
================================================
FILE: zsim/lib_webui/process_dmg_result.py
================================================
import json
import os
import plotly.express as px
import polars as pl
import streamlit as st
from zsim.define import ANOMALY_MAPPING
from zsim.sim_progress.Character.skill_class import lookup_name_or_cid
from .constants import SKILL_TAG_MAPPING, element_mapping, results_dir
def _load_dmg_data(rid: int | str) -> pl.DataFrame | None:
"""加载指定运行ID的伤害数据CSV文件。
Args:
rid (int): 运行ID。
Returns:
Optional[pd.DataFrame]: 加载的伤害数据DataFrame,如果文件未找到则返回None。
"""
csv_file_path = os.path.join(results_dir, str(rid), "damage.csv")
try:
lf = pl.scan_csv(csv_file_path)
# 去除列名中的特殊字符
schema_names = lf.collect_schema().names()
lf = lf.rename(
{col: col.replace("\r", "").replace("\n", "").strip() for col in schema_names}
)
return lf.collect()
except FileNotFoundError:
st.error(f"未找到文件:{csv_file_path}")
return None
def prepare_line_chart_data(dmg_result_df: pl.DataFrame) -> dict[str, pl.DataFrame]:
"""准备用于绘制伤害与失衡曲线图的数据。
Args:
dmg_result_df (pl.DataFrame): 原始伤害数据。
Returns:
dict[str, Any]: 包含处理后数据的字典,用于绘制折线图。
- 'line_chart_df': 包含时间、伤害、DPS、失衡值、失衡效率的DataFrame。
"""
processed_df = dmg_result_df.clone()
# 计算DPS
processed_df = processed_df.with_columns(
(pl.col("dmg_expect").cum_sum() / pl.col("tick") * 60).alias("dps")
)
# 处理失衡值
if "失衡状态" in processed_df.columns:
processed_df = processed_df.with_columns(
pl.when(pl.col("失衡状态")).then(0).otherwise(pl.col("stun")).alias("stun")
)
# 计算失衡效率
first_stun_row = processed_df.filter(pl.col("失衡状态") == True).head(1) # noqa: E712
if len(first_stun_row) > 0:
first_stun_tick = first_stun_row["tick"][0]
before_stun = processed_df.filter(pl.col("tick") <= first_stun_tick)
after_stun = processed_df.filter(pl.col("tick") > first_stun_tick)
before_stun = before_stun.with_columns(
(pl.col("stun").cum_sum() / pl.col("tick") * 60).alias("stun_efficiency")
)
after_stun = after_stun.with_columns(pl.lit(None).alias("stun_efficiency"))
processed_df = pl.concat([before_stun, after_stun])
else:
processed_df = processed_df.with_columns(
(pl.col("stun").cum_sum() / pl.col("tick") * 60).alias("stun_efficiency")
)
return {"line_chart_df": processed_df}
def draw_line_chart(chart_data: dict[str, pl.DataFrame]) -> None:
"""绘制伤害与失衡曲线图。
Args:
chart_data (Dict[str, pl.DataFrame]): 包含绘制图表所需数据的字典。
"""
df = chart_data["line_chart_df"]
with st.expander("伤害与失衡曲线:"):
# 时间-伤害分布
st.subheader("时间-伤害分布")
fig_dmg = px.line(
df,
x="tick",
y=["dmg_expect", "dmg_crit"],
labels={
"tick": "时间(帧数)",
"value": "伤害值",
"variable": "数据类型",
"dmg_expect": "期望伤害",
"dmg_crit": "暴击伤害",
},
)
st.plotly_chart(fig_dmg)
# 时间-DPS分布
st.subheader("时间-DPS分布")
fig_dps = px.line(
df,
x="tick",
y="dps",
labels={"tick": "时间(帧数)", "dps": "DPS"},
)
st.plotly_chart(fig_dps)
# 时间-失衡值分布
st.subheader("时间-失衡值分布")
fig_stun = px.line(
df,
x="tick",
y="stun",
labels={"tick": "时间(帧数)", "stun": "失衡值"},
)
st.plotly_chart(fig_stun)
# 时间-失衡效率分布
st.subheader("时间-失衡效率分布")
fig_stun_eff = px.line(
df,
x="tick",
y="stun_efficiency",
labels={"tick": "时间(帧数)", "stun_efficiency": "失衡效率(每秒)"},
)
st.plotly_chart(fig_stun_eff)
def _get_cn_skill_tag(skill_tag: str) -> str:
"""根据技能标签获取技能中文名。
Args:
skill_tag (str): 技能标签。
Returns:
str: 技能中文名。
"""
return SKILL_TAG_MAPPING.get(skill_tag, skill_tag)
def sort_df_by_UUID(dmg_result_df: pl.DataFrame) -> pl.DataFrame:
"""按UUID对伤害数据进行分组和聚合。
Args:
dmg_result_df (pl.DataFrame): 原始伤害数据。
Returns:
pl.DataFrame: 按UUID聚合后的数据,包含每个UUID的总伤害、总失衡、总积蓄等信息。
Raises:
ValueError: 如果DataFrame缺少必要的列。
"""
required_columns = [
"skill_tag",
"dmg_expect",
"stun",
"buildup",
"UUID",
"is_anomaly",
]
for col in required_columns:
if col not in dmg_result_df.columns or dmg_result_df[col].is_null().all():
raise ValueError(f"DataFrame 中缺少有效的列: {col}")
result_data = []
all_UUID = dmg_result_df["UUID"].unique().to_list()
for UUID in all_UUID:
same_UUID_rows = dmg_result_df.filter(pl.col("UUID") == UUID)
dmg_expect_sum = same_UUID_rows["dmg_expect"].fill_null(0).sum()
stun_sum = same_UUID_rows["stun"].fill_null(0).sum()
buildup_sum = same_UUID_rows["buildup"].fill_null(0).sum()
skill_tags = same_UUID_rows["skill_tag"].drop_nulls()
skill_tag = skill_tags[0] if len(skill_tags) > 0 else None
is_anomaly = same_UUID_rows["is_anomaly"][0]
element_types = same_UUID_rows["element_type"].drop_nulls()
element_type = element_types[0] if len(element_types) > 0 else None
cid: int | str | None = None
name: str | None = None
skill_cn_name: str | None = None
if skill_tag:
cid_str = skill_tag[0:4]
skill_cn_name = _get_cn_skill_tag(skill_tag) # 获取技能中文名
try:
name, cid_lookup = lookup_name_or_cid(cid=cid_str)
cid = cid_lookup
except ValueError:
name = skill_tag # 如果查找失败,使用skill_tag作为名字
cid = None
else:
skill_cn_name = "Unknown" # 如果没有skill_tag,则设为Unknown
result_data.append(
{
"UUID": UUID,
"name": name,
"element_type": element_type,
"is_anomaly": is_anomaly,
"cid": cid,
"skill_tag": skill_tag,
"skill_cn_name": skill_cn_name, # 添加技能中文名
"dmg_expect_sum": dmg_expect_sum,
"stun_sum": stun_sum,
"buildup_sum": buildup_sum,
}
)
return pl.DataFrame(result_data)
def prepare_char_chart_data(uuid_df: pl.DataFrame) -> dict[str, pl.DataFrame]:
"""准备用于绘制角色参与度分布图的数据。
Args:
uuid_df (pl.DataFrame): 按UUID聚合后的伤害数据。
Returns:
Dict[str, Any]: 包含绘制饼图所需数据的字典。
- 'char_dmg_df': 按角色分组的伤害总和。
- 'char_stun_df': 按角色分组的失衡总和。
- 'char_skill_dmg_df': 按角色和技能标签分组的伤害总和。
- 'char_element_df': 按角色和元素类型分组的积蓄总和。
"""
# 各伤害来源占比
char_dmg_df = (
uuid_df.filter(pl.col("dmg_expect_sum") > 0)
.group_by(["name", "is_anomaly"])
.agg(pl.col("dmg_expect_sum").sum())
)
# 角色失衡占比
char_stun_df = (
uuid_df.filter(pl.col("stun_sum") > 0).group_by("name").agg(pl.col("stun_sum").sum())
)
# 角色技能输出占比
filtered_skill_df = uuid_df.filter(pl.col("cid").is_not_null())
char_skill_dmg_df = filtered_skill_df.group_by(["name", "skill_cn_name"]).agg(
[
pl.col("dmg_expect_sum").sum(),
pl.col("buildup_sum").sum(),
pl.col("stun_sum").sum(),
]
)
# 角色属性积蓄占比
filtered_buildup_df = uuid_df.filter(pl.col("buildup_sum") > 0)
char_element_df = filtered_buildup_df.group_by(["name", "element_type"]).agg(
pl.col("buildup_sum").sum()
)
return {
"char_dmg_df": char_dmg_df,
"char_stun_df": char_stun_df,
"char_skill_dmg_df": char_skill_dmg_df,
"char_element_df": char_element_df,
}
def draw_char_chart(chart_data: dict[str, pl.DataFrame]) -> None:
"""绘制角色参与度分布图。
Args:
chart_data (Dict[str, Any]): 包含绘制图表所需数据的字典。
"""
char_dmg_df = chart_data["char_dmg_df"]
char_stun_df = chart_data["char_stun_df"]
char_skill_dmg_df = chart_data["char_skill_dmg_df"]
char_element_df = chart_data["char_element_df"]
with st.expander("角色参与度分布情况:"):
cols1 = st.columns(2)
# 角色伤害占比分布
with cols1[0]:
st.subheader("队伍伤害来源占比")
if len(char_dmg_df) > 0:
fig_dmg_pie = px.pie(
char_dmg_df,
names="name",
values="dmg_expect_sum",
labels={"name": "来源", "dmg_expect_sum": "期望伤害总和"},
)
st.plotly_chart(fig_dmg_pie)
else:
st.info("没有非零伤害数据可供显示")
# 角色失衡占比分布
with cols1[1]:
st.subheader("队伍失衡来源占比")
if len(char_stun_df) > 0:
fig_stun_pie = px.pie(
char_stun_df,
names="name",
values="stun_sum",
labels={"name": "来源", "stun_sum": "失衡值总和"},
)
st.plotly_chart(fig_stun_pie)
else:
st.info("没有非零失衡值数据可供显示")
# 每个角色的各技能输出占比分布
st.subheader("各角色技能输出占比")
unique_names = char_skill_dmg_df["name"].unique()
if len(unique_names) > 0:
cols2 = st.columns(len(unique_names))
col_index = 0
for name in char_skill_dmg_df["name"].unique().to_list():
group = char_skill_dmg_df.filter(pl.col("name") == name)
with cols2[col_index]:
st.caption(f"{name}")
fig_skill_pie = px.pie(
group,
names="skill_cn_name",
values="dmg_expect_sum",
labels={
"skill_cn_name": "技能名称",
"dmg_expect_sum": "期望伤害总和",
},
)
st.plotly_chart(fig_skill_pie)
col_index += 1
else:
st.info("没有角色技能伤害数据可供显示")
# 每个角色各技能的异常积蓄占比
st.subheader("各角色技能异常积蓄占比")
unique_names = char_skill_dmg_df["name"].unique()
if len(unique_names) > 0:
cols2 = st.columns(len(unique_names))
col_index = 0
for name in char_skill_dmg_df["name"].unique().to_list():
group = char_skill_dmg_df.filter(pl.col("name") == name)
with cols2[col_index]:
st.caption(f"{name}")
fig_skill_ano_pie = px.pie(
group,
names="skill_cn_name",
values="buildup_sum",
labels={
"skill_cn_name": "技能名称",
"buildup_sum": "异常值总和",
},
)
st.plotly_chart(fig_skill_ano_pie)
col_index += 1
else:
st.info("没有角色技能异常数据可供显示")
# 每个角色各技能的失衡值占比
st.subheader("各角色技能失衡值占比")
unique_names = char_skill_dmg_df["name"].unique()
if len(unique_names) > 0:
cols2 = st.columns(len(unique_names))
col_index = 0
for name in char_skill_dmg_df["name"].unique().to_list():
group = char_skill_dmg_df.filter(pl.col("name") == name)
with cols2[col_index]:
st.caption(f"{name}")
fig_skill_stun_pie = px.pie(
group,
names="skill_cn_name",
values="stun_sum",
labels={
"skill_cn_name": "技能名称",
"stun_sum": "失衡值总和",
},
)
st.plotly_chart(fig_skill_stun_pie)
col_index += 1
else:
st.info("没有角色技能失衡数据可供显示")
# 每个角色各属性的积蓄占比
st.subheader("各属性积蓄来源占比")
unique_elements = char_element_df["element_type"].unique()
if len(unique_elements) > 0:
cols3 = st.columns(len(unique_elements))
col_index = 0
for element in unique_elements:
element_df = char_element_df.filter(pl.col("element_type") == element)
element_name = element_mapping.get(element, element) # 获取元素中文名
with cols3[col_index]:
st.caption(f"{element_name}")
fig_buildup_pie = px.pie(
element_df,
names="name",
values="buildup_sum",
labels={"name": "角色", "buildup_sum": "积蓄值总和"},
)
st.plotly_chart(fig_buildup_pie)
col_index += 1
else:
st.info("没有属性积蓄数据可供显示")
def _find_consecutive_true_ranges(df: pl.DataFrame, column: str) -> list[tuple[int, int]]:
"""查找DataFrame列中连续为True的范围。
Args:
df (pl.DataFrame): 输入的DataFrame,需要包含 'tick' 列。
column (str): 要查找的布尔列名。
Returns:
list[tuple[int, int]]: 一个包含 (开始tick, 结束tick) 元组的列表。
"""
ranges = []
start = None
# 获取tick列和指定列的值
ticks = df["tick"].to_list()
values = df[column].to_list()
for i, (tick, value) in enumerate(zip(ticks, values, strict=False)):
if value:
if start is None:
start = tick
else:
if start is not None:
# 结束tick应该是上一个为True的tick
prev_tick = ticks[i - 1] if i > 0 else start
ranges.append((start, prev_tick))
start = None
# 处理最后一个区间(如果存在)
if start is not None:
ranges.append((start, ticks[-1]))
return ranges
def prepare_timeline_data(dmg_result_df: pl.DataFrame) -> pl.DataFrame | None:
"""准备用于绘制异常状态时间线的数据。
Args:
dmg_result_df (pl.DataFrame): 原始伤害数据。
Returns:
Optional[pl.DataFrame]: 用于绘制Gantt图的DataFrame,如果缺少列或无数据则返回None。
"""
required_columns = [
"冻结",
"霜寒",
"畏缩",
"感电",
"灼烧",
"侵蚀",
"烈霜霜寒",
"tick",
]
missing_cols = [col for col in required_columns if col not in dmg_result_df.columns]
if missing_cols:
st.error(f"输入数据缺少必要的列: {missing_cols}")
return None
columns_to_check = ["冻结", "霜寒", "畏缩", "感电", "灼烧", "侵蚀", "烈霜霜寒"]
gantt_data = []
for col in columns_to_check:
if col in dmg_result_df.columns:
ranges = _find_consecutive_true_ranges(dmg_result_df, col)
for start, end in ranges:
gantt_data.append({"Task": col, "Start": start, "Finish": end})
if not gantt_data:
return None
gantt_df = pl.DataFrame(gantt_data)
gantt_df = gantt_df.with_columns(
(pl.col("Finish") - pl.col("Start") + 1).alias("Duration")
) # 持续时间包含首尾
return gantt_df
def draw_char_timeline(gantt_df: pl.DataFrame | None) -> None:
"""绘制异常状态时间线(Gantt图)。
Args:
gantt_df: 用于绘制Gantt图的数据,如果为None则不绘制。
"""
with st.expander("异常时间线:"):
if gantt_df is not None and len(gantt_df) > 0:
fig_timeline = px.bar(
gantt_df,
x="Duration",
y="Task",
base="Start",
orientation="h",
labels={
"Start": "开始时间(帧)",
"Duration": "持续时间(帧)",
"Task": "状态类型",
},
height=250,
)
st.plotly_chart(fig_timeline)
else:
st.warning("没有找到任何连续的状态数据")
def calculate_and_save_anomaly_attribution(
rid: int | str, char_dmg_df: pl.DataFrame, char_element_df: pl.DataFrame
) -> None:
"""计算并保存异常伤害归因。
Args:
rid (int): 运行ID。
char_dmg_df (pd.DataFrame): 角色直接伤害数据。
char_element_df (pd.DataFrame): 角色元素积蓄数据。
"""
output_path = f"{results_dir}/{rid}/damage_attribution.json"
# 检查文件是否已存在
if os.path.exists(output_path):
return
# 计算每种元素类型的异常总伤害
anomaly_name_list = list(ANOMALY_MAPPING.values()) + ["极性紊乱", "异放"]
anomaly_damage_totals = {element: 0 for element in anomaly_name_list}
for anomaly_name in anomaly_name_list:
if anomaly_name in char_dmg_df["name"].to_list():
filtered_df = char_dmg_df.filter(pl.col("name") == anomaly_name)
for row in filtered_df.iter_rows(named=True):
anomaly_damage_totals[anomaly_name] += row["dmg_expect_sum"]
# 初始化一个包含所有角色的字典
all_characters = set(char_dmg_df.filter(~pl.col("is_anomaly"))["name"].to_list()).union(
set(char_element_df["name"])
)
# 初始化角色伤害数据
attribution_data: dict[str, dict[str, float]] = {
name: {"direct_damage": 0, "anomaly_damage": 0} for name in all_characters
}
# 处理只打出直伤的角色
for row in char_dmg_df.filter(~pl.col("is_anomaly")).iter_rows(named=True):
name = row["name"]
direct_damage = row["dmg_expect_sum"]
attribution_data[name]["direct_damage"] = direct_damage
# 分配异常伤害到角色
for row in char_element_df.iter_rows(named=True):
name = row["name"]
element_type = row["element_type"]
buildup_sum = row["buildup_sum"]
anomaly_name = ANOMALY_MAPPING[element_type]
total_anomaly_damage = anomaly_damage_totals[anomaly_name]
# 计算角色的异常伤害归因
if total_anomaly_damage > 0:
element_total = char_element_df.filter(pl.col("element_type") == element_type)[
"buildup_sum"
].sum()
anomaly_damage_attribution = (buildup_sum / element_total) * total_anomaly_damage
else:
anomaly_damage_attribution = 0
# 更新角色的异常伤害
attribution_data[name]["anomaly_damage"] += anomaly_damage_attribution
# 处理极性紊乱和异放
for anomaly_name in ["极性紊乱", "异放"]:
total_anomaly_damage = anomaly_damage_totals.get(anomaly_name, 0)
if total_anomaly_damage > 0:
if anomaly_name == "极性紊乱":
for key in attribution_data:
if key == "柳":
attribution_data[key]["anomaly_damage"] += total_anomaly_damage
if anomaly_name == "异放":
for key in attribution_data:
if key == "薇薇安":
attribution_data[key]["anomaly_damage"] += total_anomaly_damage
with open(output_path, "w", encoding="utf-8") as f:
json.dump(attribution_data, f, ensure_ascii=False, indent=4)
def prepare_dmg_data_and_cache(
rid: int | str,
) -> dict[str, pl.DataFrame | dict[str, pl.DataFrame]] | None:
"""准备并缓存伤害分析所需的数据。
Args:
rid (int): 运行ID。
Returns:
Optional[dict[str, pl.DataFrame]]: 包含预处理后的数据的字典,
如果没有数据则返回None。
"""
dmg_result_df = _load_dmg_data(rid)
if dmg_result_df is None:
return None
uuid_df = sort_df_by_UUID(dmg_result_df)
char_chart_data = prepare_char_chart_data(uuid_df)
# st.write(char_chart_data)
calculate_and_save_anomaly_attribution(
int(rid) if isinstance(rid, int) else rid,
char_chart_data["char_dmg_df"],
char_chart_data["char_element_df"],
)
return {
"dmg_result_df": dmg_result_df,
"char_dmg_df": char_chart_data["char_dmg_df"],
"uuid_df": uuid_df,
"char_chart_data": char_chart_data,
}
def show_dmg_result(rid: int | str) -> None:
"""处理并显示指定运行ID的伤害分析结果。
Args:
rid (int): 运行ID。
"""
st.subheader(f"{rid} 的伤害数据分析")
prepared_data_dict = prepare_dmg_data_and_cache(rid)
if prepared_data_dict is None:
return
uuid_df = prepared_data_dict["uuid_df"]
char_chart_data = prepared_data_dict["char_chart_data"]
dmg_result_df = prepared_data_dict["dmg_result_df"]
if dmg_result_df is None:
return
with st.expander("原始数据:"):
st.dataframe(dmg_result_df)
with st.expander("按UUID排序后的数据:"):
st.dataframe(uuid_df)
# 准备并绘制折线图
line_chart_data = prepare_line_chart_data(dmg_result_df) # type: ignore
draw_line_chart(line_chart_data)
# 准备并绘制角色分布图
draw_char_chart(char_chart_data) # type: ignore
# 准备并绘制时间线图
timeline_data = prepare_timeline_data(dmg_result_df) # type: ignore
draw_char_timeline(timeline_data)
================================================
FILE: zsim/lib_webui/process_parallel_data.py
================================================
"""
这个模块应该在WebUI被启用后依然存在,可以转移到api_src中。
"""
import asyncio
import json
import os
from typing import Any
import aiofiles
import plotly.graph_objects as go
import streamlit as st
from zsim.define import results_dir
from zsim.lib_webui.process_buff_result import show_buff_result
from zsim.lib_webui.process_dmg_result import show_dmg_result
from zsim.utils.process_buff_result import prepare_buff_data_and_cache
from zsim.utils.process_dmg_result import prepare_dmg_data_and_cache
from .constants import stats_trans_mapping
reversed_stats_trans_mapping = {v: k for k, v in stats_trans_mapping.items()}
def judge_parallel_result(rid: int | str) -> bool:
"""判断对应的rid是否为并行模式。
Args:
rid (int): 运行ID。
Returns:
bool: 如果是并行模式,则返回True;否则返回False。
"""
result_dir = os.path.join(results_dir, str(rid))
if not os.path.isdir(result_dir):
return False
parallel_config_path = os.path.join(result_dir, ".parallel_config.json")
if not os.path.exists(parallel_config_path):
return False
try:
with open(parallel_config_path, "r", encoding="utf-8") as f:
parallel_config: dict = json.load(f)
if not parallel_config.get("enabled", False):
return False
except (json.JSONDecodeError, IOError):
# 如果文件读取或解析失败,也视为非并行模式
return False
# 检查是否存在至少一个包含 sub.parallel_config.json 的子目录
for item in os.listdir(result_dir):
sub_dir_path = os.path.join(result_dir, item)
if os.path.isdir(sub_dir_path):
sub_config_path = os.path.join(sub_dir_path, "sub.parallel_config.json")
if os.path.exists(sub_config_path):
return True
return False
async def _process_sub_damage(sub_rid: str) -> None:
"""异步处理单个子目录的数据。
Args:
sub_rid (str): 子运行ID。
"""
# prepare_dmg_data_and_cache 不是异步函数,使用 to_thread
await asyncio.to_thread(prepare_dmg_data_and_cache, sub_rid)
async def _process_sub_buff(sub_rid: str) -> None:
"""异步处理单个子目录的数据。
Args:
sub_rid (str): 子运行ID。
"""
await prepare_buff_data_and_cache(sub_rid)
async def prepare_parallel_data_and_cache(rid: int | str) -> None:
"""对并行模式的每一份报告进行数据预处理,并将结果缓存到本地(异步执行)。
Args:
rid (int | str): 运行ID。
"""
result_dir = os.path.join(results_dir, str(rid))
parallel_config_path = os.path.join(result_dir, ".parallel_config.json")
try:
with open(parallel_config_path, "r", encoding="utf-8") as f:
parallel_config: dict = json.load(f)
except (json.JSONDecodeError, IOError) as e:
st.error(f"读取或解析并行配置文件 {parallel_config_path} 失败: {e}")
return
if parallel_config.get("adjust_sc", {}).get("enabled", False):
merged_sc_file_path = os.path.join(result_dir, "merged_sc_data.json")
if os.path.exists(merged_sc_file_path):
return
tasks = []
for item in os.listdir(result_dir):
sub_dir_path = os.path.join(result_dir, item)
if os.path.isdir(sub_dir_path):
sub_config_path = os.path.join(sub_dir_path, "sub.parallel_config.json")
if os.path.exists(sub_config_path):
sub_rid: str = os.path.join(str(rid), item) # 子进程rid
# 创建异步任务
tasks.append(_process_sub_damage(sub_rid))
# 并发执行所有任务
if tasks:
await asyncio.gather(*tasks)
# 统计并行模式的个子进程伤害归并结果
async def merge_parallel_dmg_data(
rid: int | str,
) -> tuple[str, dict[str, Any]] | None:
"""对并行模式的每一份报告进行数据预处理,并将结果缓存到本地。
Args:
rid (int): 运行ID。
"""
result_dir = os.path.join(results_dir, str(rid))
parallel_config_path = os.path.join(result_dir, ".parallel_config.json")
async with aiofiles.open(parallel_config_path, "r", encoding="utf-8") as f:
parallel_config: dict = json.loads(await f.read())
if parallel_config.get("adjust_sc", {}).get("enabled", False):
# 属性收益曲线功能
func = "attr_curve"
merged_sc_file_path = os.path.join(result_dir, "merged_sc_data.json")
sc_merged_data = {}
if os.path.exists(merged_sc_file_path):
async with aiofiles.open(merged_sc_file_path, "r", encoding="utf-8") as f:
sc_merged_data = json.loads(await f.read())
else:
try:
st.info("首次处理读取属性收益曲线数据,请稍等...")
sc_merged_data = await _merge_attr_curve_data(rid)
st.success("属性收益曲线数据合并完成!")
# 将合并后的数据保存到 JSON 文件
try:
async with aiofiles.open(merged_sc_file_path, "w", encoding="utf-8") as f:
await f.write(json.dumps(sc_merged_data, indent=4, ensure_ascii=False))
st.success(f"合并的属性收益曲线数据已保存至 {merged_sc_file_path}")
except IOError as e:
st.error(f"保存合并的属性收益曲线数据失败: {e}")
except Exception as e:
st.error(f"合并属性收益曲线数据时出错: {e}")
return func, sc_merged_data
elif parallel_config.get("adjust_weapon", {}).get("enabled", False):
# 武器切换功能
func = "weapon"
merged_weapon_file_path = os.path.join(result_dir, "merged_weapon_data.json")
weapon_merged_data = {}
if os.path.exists(merged_weapon_file_path):
async with aiofiles.open(merged_weapon_file_path, "r", encoding="utf-8") as f:
weapon_merged_data = json.loads(await f.read())
else:
try:
st.info("首次处理读取武器切换数据,请稍等...")
weapon_merged_data = await _merge_weapon_data(rid)
st.success("武器切换数据合并完成!")
# 将合并后的数据保存到 JSON 文件
try:
async with aiofiles.open(merged_weapon_file_path, "w", encoding="utf-8") as f:
await f.write(json.dumps(weapon_merged_data, indent=4, ensure_ascii=False))
st.success(f"合并的武器切换数据已保存至 {merged_weapon_file_path}")
except IOError as e:
st.error(f"保存合并的武器切换数据失败: {e}")
except Exception as e:
st.error(f"合并武器切换数据时出错: {e}")
return func, weapon_merged_data
else:
return None
def __draw_attr_curve(
sc_merged_data: dict[str, dict[str, dict[int | float, dict[str, float | None]]]],
) -> None:
"""绘制属性收益曲线折线图"""
if sc_merged_data:
for char_name, char_data in sc_merged_data.items():
fig = go.Figure()
has_data = False # 标记是否有数据添加到图表中
x_values = [] # 初始化x_values
for sc_name, sc_values_results in char_data.items():
# sc_values_results 的结构现在是 {sc_value: {"result": float, "rate": float | None}}
# 数据在 merge_parallel_sc_data 中已经按 sc_value 排序
if not sc_values_results:
st.warning(f"角色 '{char_name}' 的词条 '{sc_name}' 没有数据,跳过绘制。")
continue
# 提取 x 值 (词条值) 和 y 值 (收益率)
x_values_raw = list(sc_values_results.keys())
# 提取预计算的收益率,跳过第一个点(收益率通常为None)
y_values_rate = [data.get("rate") for data in sc_values_results.values()]
# 尝试将 x 值转换为浮点数
try:
x_values = [float(x) for x in x_values_raw]
except ValueError:
st.warning(
f"角色 '{char_name}' 的词条 '{sc_name}' 包含非数值的 x 值,跳过绘制。"
)
continue
# 确保有足够的数据点来绘制收益率(至少需要两个原始点才能计算一个收益率点)
if len(x_values) < 2:
st.warning(
f"角色 '{char_name}' 的词条 '{sc_name}' 数据点不足 (<2),无法绘制收益率曲线。"
)
continue
# 过滤掉第一个点的 x 值和 y 值(因为第一个点没有收益率)
# 同时处理 y_values_rate 中可能存在的 None 值
plot_x_values = []
plot_y_values = []
for i in range(1, len(x_values)):
if y_values_rate[i] is not None:
plot_x_values.append(x_values[i])
plot_y_values.append(y_values_rate[i])
if not plot_x_values:
st.warning(
f"角色 '{char_name}' 的词条 '{sc_name}' 没有有效的收益率数据点,跳过绘制。"
)
continue
fig.add_trace(
go.Scatter(
x=plot_x_values, # 使用过滤后的 x 值
y=plot_y_values, # 使用过滤后的 y 值 (收益率)
mode="lines+markers",
name=reversed_stats_trans_mapping.get(sc_name, sc_name),
connectgaps=False, # 不连接 None 值造成的断点
)
)
has_data = True
if has_data:
# 计算整数刻度 (基于原始的所有 x_values)
try:
# 确保只使用数值类型的 x 值
numeric_x_values = [x for x in x_values if isinstance(x, (int, float))]
if not numeric_x_values:
raise ValueError("No numeric x values found")
min_x = min(numeric_x_values)
max_x = max(numeric_x_values)
# 生成从最小整数到最大整数的所有整数刻度
integer_ticks = list(
range(
int(min_x) if min_x == int(min_x) else int(min_x) + 1,
int(max_x) + 1,
)
)
# 如果最小值本身是整数,也包含它
if isinstance(min_x, int) or (isinstance(min_x, float) and min_x.is_integer()):
if int(min_x) not in integer_ticks:
integer_ticks.insert(0, int(min_x))
integer_ticks.sort() # 确保刻度排序
except ValueError: # 如果 x_values 为空或不包含数字
integer_ticks = []
# fmt: off
fig.update_layout(
title=f"{char_name} - 属性收益曲线",
xaxis_title="词条数",
yaxis_title="收益率", # 更新 Y 轴标题
hovermode="x unified",
yaxis=dict(tickformat=".2%"), # 将 Y 轴格式化为百分比
xaxis=dict(
tickmode="array" if integer_ticks else "auto", # 如果有计算出的整数刻度则使用array模式
tickvals=integer_ticks if integer_ticks else None, # 设置刻度值为整数
tickformat="d", # 强制显示为整数
),
)
st.plotly_chart(fig, use_container_width=True)
else:
st.warning(f"角色 '{char_name}' 没有足够的数据来绘制组合图表。")
# fmt: on
else:
st.warning("没有可用于绘制属性收益曲线的数据。")
def __draw_weapon_data(
weapon_merged_data: dict[str, dict[str, dict[str, dict[str, Any]]]],
) -> None:
"""绘制武器对比柱状图"""
if weapon_merged_data:
for char_name, char_data in weapon_merged_data.items():
fig = go.Figure()
has_data = False # 标记是否有数据添加到图表中
# 收集所有武器和精炼等级的数据
weapons_data = {}
for weapon_name, weapon_levels in char_data.items():
if not weapon_levels:
st.warning(f"角色 '{char_name}' 的武器 '{weapon_name}' 没有数据,跳过绘制。")
continue
# 为每个精炼等级收集伤害数据
for level, level_data in weapon_levels.items():
damage = level_data.get("damage", 0.0)
if weapon_name not in weapons_data:
weapons_data[weapon_name] = {}
weapons_data[weapon_name][level] = damage
# 如果没有收集到数据,跳过此角色
if not weapons_data:
st.warning(f"角色 '{char_name}' 没有可用的武器数据,跳过绘制。")
continue
# 收集所有独特的精炼等级
all_levels = sorted(
list(
set(
level
for levels_data in weapons_data.values()
for level in levels_data.keys()
)
)
)
all_weapon_names = list(weapons_data.keys())
# 为每个精炼等级创建柱状图系列
for level in all_levels:
level_damages = []
for weapon_name in all_weapon_names:
# 获取该武器在该精炼等级的伤害,如果不存在则为0
damage = weapons_data.get(weapon_name, {}).get(level, 0.0)
level_damages.append(damage)
if any(d > 0 for d in level_damages): # 只添加有数据的精炼等级系列
fig.add_trace(
go.Bar(
x=all_weapon_names, # 武器名称作为 X 轴
y=level_damages,
name=f"精炼 {level}", # 精炼等级作为系列名称
text=[f"{damage:.2f}" for damage in level_damages],
textposition="auto",
)
)
has_data = True
if has_data:
# 更新图表布局
fig.update_layout(
title=f"{char_name} - 武器伤害对比",
xaxis_title="武器名称", # 更新 X 轴标题
yaxis_title="总伤害",
barmode="group", # 分组模式,按 X 轴(武器名称)分组
hovermode="x unified",
)
st.plotly_chart(fig, use_container_width=True)
else:
st.warning(f"角色 '{char_name}' 没有足够的数据来绘制武器对比图表。")
else:
st.warning("没有可用于绘制武器对比图表的数据。")
async def _read_json_file(file_path: str) -> dict[str, Any]:
"""异步读取JSON文件。
Args:
file_path (str): JSON文件路径。
Returns:
dict[str, Any]: 读取到的JSON内容,如果失败则返回空字典。
"""
try:
async with aiofiles.open(file_path, mode="r", encoding="utf-8") as f:
content = await f.read()
return json.loads(content)
except (FileNotFoundError, json.JSONDecodeError, IOError) as e:
# TODO: 使用更健壮的日志记录
print(f"Error reading JSON file {file_path}: {e}")
return {}
async def _collect_sub_parallel_data(
rid: int | str,
) -> list[dict[str, Any]]:
"""异步收集所有子进程的并行配置和伤害数据。
Args:
rid (int | str): 运行ID。
Returns:
list[dict[str, Any]]: 包含每个子进程配置和伤害数据的列表。
每个字典包含 'sub_config', 'sc_data', 'sub_dir_path'。
"""
result_dir: str = os.path.join(results_dir, str(rid))
tasks = []
sub_dir_paths_map: dict[int, str] = {} # 存储 task index 到 sub_dir_path 的映射
collected_data: list[dict[str, Any]] = []
# 收集需要读取的文件路径
task_index = 0
for item in os.listdir(result_dir):
sub_dir_path = os.path.join(result_dir, item)
if os.path.isdir(sub_dir_path):
sub_config_path = os.path.join(sub_dir_path, "sub.parallel_config.json")
dmg_attribution_path = os.path.join(sub_dir_path, "damage_attribution.json")
if os.path.exists(sub_config_path) and os.path.exists(dmg_attribution_path):
# 添加读取配置文件的任务
tasks.append(_read_json_file(sub_config_path))
sub_dir_paths_map[task_index] = sub_dir_path # 记录config对应的目录
task_index += 1
# 添加读取伤害数据的任务
tasks.append(_read_json_file(dmg_attribution_path))
sub_dir_paths_map[task_index] = sub_dir_path # 记录dmg对应的目录
task_index += 1
# 并发执行所有文件读取任务
if not tasks:
print(f"在 {result_dir} 中未找到有效的子进程结果目录。")
return []
results = await asyncio.gather(*tasks)
# 处理读取结果
i = 0
while i < len(results):
sub_config: dict[str, Any] = results[i]
sc_data: dict[str, Any] = results[i + 1]
current_sub_dir = sub_dir_paths_map.get(i, "未知子目录") # 获取对应的子目录路径
i += 2
if not sub_config:
print(
f"警告:跳过子目录 {current_sub_dir},因为 sub.parallel_config.json 读取失败或为空。"
)
continue
if not sc_data:
print(
f"警告:跳过子目录 {current_sub_dir},因为 damage_attribution.json 读取失败或为空。"
)
continue
collected_data.append(
{
"sub_config": sub_config,
"sc_data": sc_data,
"sub_dir_path": current_sub_dir,
}
)
return collected_data
async def _merge_attr_curve_data(
rid: int | str,
) -> dict[str, dict[str, dict[int | float, dict[str, float | None]]]]:
"""读取所有子进程的属性收益曲线数据,合并并计算收益率。
Args:
rid (int | str): 运行ID。
Returns:
dict[str, dict[str, dict[int | float, dict[str, float | None]]]]: {
角色名(adjust_char): {
词条名(sc_name): {
词条值(sc_value): {
"result": 原始结果(sc_result: float),
"rate": 收益率(rate_of_return: float | None)
}
}
}
}
"""
all_sc_data: dict[str, dict[str, dict[int | float | None, float | None]]] = {}
collected_data = await _collect_sub_parallel_data(rid)
for item in collected_data:
sub_config = item["sub_config"]
sc_data = item["sc_data"]
current_sub_dir = item["sub_dir_path"]
adjust_char: str | None = sub_config.get("adjust_char")
sc_name: str | None = sub_config.get("sc_name")
# sc_value 可能是 int 或 float
sc_value_raw: Any = sub_config.get("sc_value")
sc_value: int | float | None = None
if isinstance(sc_value_raw, (int, float)):
sc_value = sc_value_raw
if adjust_char is None or sc_name is None or sc_value is None:
print(
f"警告:跳过子目录 {current_sub_dir},缺少必要的配置信息 (adjust_char, sc_name, sc_value)。"
)
continue
# damage_attribution.json 处理
char_dmg_data: dict[str, Any] | None = sc_data.get(adjust_char)
if char_dmg_data is None:
print(
f"警告:跳过子目录 {current_sub_dir},在 damage_attribution.json 中未找到角色 '{adjust_char}' 的数据。"
)
continue
# 伤害数据包含 direct_damage 和 anomaly_damage
direct_damage: float = char_dmg_data.get("direct_damage", 0.0)
anomaly_damage: float = char_dmg_data.get("anomaly_damage", 0.0)
sc_result: float = direct_damage + anomaly_damage
# 填充结果字典
if adjust_char not in all_sc_data:
all_sc_data[adjust_char] = {}
if sc_name not in all_sc_data[adjust_char]:
all_sc_data[adjust_char][sc_name] = {}
# 检查 sc_value 是否已存在,如果存在则打印警告(理论上并行配置不应重复)
if sc_value in all_sc_data[adjust_char][sc_name]:
print(
f"警告:在角色 '{adjust_char}' 的词条 '{sc_name}' 中,词条值 '{sc_value}' 重复出现。来自子目录: {current_sub_dir}"
)
# 存储原始结果
all_sc_data[adjust_char][sc_name][sc_value] = { # type: ignore
"result": sc_result,
"rate": None,
}
# 对每个词条的值按 sc_value 排序并计算收益率
for char_name, char_data in all_sc_data.items():
for sc_name_key, sc_values_data in char_data.items():
# 按 sc_value 排序
try:
# 尝试将键转换为浮点数进行排序
# 过滤掉 sc_value 为 None 的项再排序
filtered_items = [(k, v) for k, v in sc_values_data.items() if k is not None]
sorted_items = sorted(filtered_items, key=lambda item: float(item[0]))
except ValueError:
# 如果转换失败,按原始键(字符串)排序
sorted_items = [(k, v) for k, v in sc_values_data.items() if k is not None]
sorted_items = sorted(sorted_items, key=lambda item: str(item[0]))
# 更新排序后的字典,并计算收益率
sorted_sc_data: dict[int | float, dict[str, float | None]] = {}
previous_result: float | None = None
for i, (sc_val, data) in enumerate(sorted_items):
current_result = data["result"] # type: ignore
rate = None
if i > 0 and previous_result is not None and previous_result != 0:
rate = (current_result / previous_result) - 1
sorted_sc_data[sc_val] = {"result": current_result, "rate": rate}
previous_result = current_result
# 用包含收益率的排序后字典替换原来的字典
all_sc_data[char_name][sc_name_key] = sorted_sc_data # type: ignore
return all_sc_data # type: ignore
async def _merge_weapon_data(
rid: int | str,
) -> dict[str, dict[str, dict[str, dict[str, Any]]]]:
"""读取所有子进程的武器切换数据,合并并计算平均伤害。
Args:
rid (int | str): 运行ID。
Returns:
dict[str, dict[str, dict[str, dict[str, Any]]]]: {
角色名(adjust_char): {
武器名(weapon_name): {
精炼等级{weapon_level}: {
"damage": 总伤害加权,
}
}
}
}
"""
all_weapon_data: dict[str, dict[str, dict[str, dict[str, Any]]]] = {}
collected_data = await _collect_sub_parallel_data(rid)
for item in collected_data:
sub_config = item["sub_config"]
sc_data = item["sc_data"]
current_sub_dir = item["sub_dir_path"]
adjust_char: str | None = sub_config.get("adjust_char")
weapon_name: str | None = sub_config.get("weapon_name")
weapon_level: str | None = sub_config.get("weapon_level")
if adjust_char is None or weapon_name is None or weapon_level is None:
print(
f"警告:跳过子目录 {current_sub_dir},缺少必要的配置信息 (adjust_char, weapon_name, weapon_level)。"
)
continue
char_dmg_data: dict[str, Any] | None = sc_data.get(adjust_char)
if char_dmg_data is None:
print(
f"警告:跳过子目录 {current_sub_dir},在 damage_attribution.json 中未找到角色 '{adjust_char}' 的数据。"
)
continue
# 伤害数据包含 direct_damage 和 anomaly_damage
direct_damage: float = char_dmg_data.get("direct_damage", 0.0)
anomaly_damage: float = char_dmg_data.get("anomaly_damage", 0.0)
total_damage: float = direct_damage + anomaly_damage
# 填充结果字典
if adjust_char not in all_weapon_data:
all_weapon_data[adjust_char] = {}
if weapon_name not in all_weapon_data[adjust_char]:
all_weapon_data[adjust_char][weapon_name] = {}
# 检查 weapon_level 是否已存在,如果存在则打印警告(理论上并行配置不应重复)
if weapon_level in all_weapon_data[adjust_char][weapon_name]:
print(
f"警告:在角色 '{adjust_char}' 的武器 '{weapon_name}' 中,精炼等级 '{weapon_level}' 重复出现。来自子目录: {current_sub_dir}"
)
# 存储总伤害
all_weapon_data[adjust_char][weapon_name][weapon_level] = {
"damage": total_damage,
}
return all_weapon_data
def process_parallel_result(rid: int | str) -> None:
"""处理并行模式的结果。
Args:
rid (int): 运行ID。
"""
result_dir = os.path.join(results_dir, str(rid))
# 1. 预处理每个子目录的数据(伤害、Buff等)
with st.spinner("开始预处理并行子目录数据,初次处理会持续一段时间...", show_time=True):
asyncio.run(prepare_parallel_data_and_cache(rid))
# 2. 合并需要聚合的数据(例如属性收益曲线或武器对比)
result = asyncio.run(merge_parallel_dmg_data(rid))
# 3. 绘制图表
if result:
func, merged_data = result
if func == "attr_curve":
__draw_attr_curve(merged_data)
elif func == "weapon":
__draw_weapon_data(merged_data)
# 4. 获取有效的子目录列表
sub_dirs = []
if os.path.isdir(result_dir):
for item in os.listdir(result_dir):
sub_dir_path = os.path.join(result_dir, item)
if os.path.isdir(sub_dir_path):
sub_config_path = os.path.join(sub_dir_path, "sub.parallel_config.json")
if os.path.exists(sub_config_path):
sub_dirs.append(item) # 添加子目录名称
st.markdown("--- ")
st.write("选择要查看的子进程报告")
col1, col2 = st.columns(2)
selected_key = sub_dirs[0]
with col1:
# 5. 添加下拉选择框以选择子进程报告
if sub_dirs:
selected_sub_dir = st.selectbox(
"选择要查看的子进程报告",
options=sub_dirs,
index=0,
key=f"selectbox_sub_dir_{rid}",
label_visibility="collapsed",
)
selected_key = f"{rid}/{selected_sub_dir}"
with col2:
# 6. 提供按钮处理全部buff结果以节约储存
if st.button(
"处理全部BUFF结果",
key="toggle_buff_all",
help="处理所有buff结果可以节约大量储存空间,但耗时较长",
):
with st.spinner("开始处理所有子进程BUFF结果...", show_time=True):
async def process_all_sub_buff():
"""处理所有子进程的BUFF结果。"""
tasks = []
for sub_dir in sub_dirs:
sub_rid = f"{rid}/{sub_dir}"
tasks.append(_process_sub_buff(sub_rid))
await asyncio.gather(*tasks)
asyncio.run(process_all_sub_buff())
if st.button("显示子进程伤害结果", key="toggle_dmg_all"):
show_dmg_result(selected_key)
show_buff_result(selected_key)
else:
st.info("未找到有效的子进程结果目录。")
# TODO: 添加其他并行结果的处理逻辑,例如生成聚合报告、绘制对比图表等。
st.warning("并行模式的结果合并与展示功能仍在开发中。", icon="⚠️")
================================================
FILE: zsim/lib_webui/process_simulator.py
================================================
import json
import os
import shutil
from typing import Any, Iterator
import polars as pl
import streamlit as st
from zsim.define import config_path
from zsim.lib_webui.process_apl_editor import APLArchive, APLJudgeTool
from zsim.simulator.config_classes import (
ExecAttrCurveCfg,
ExecWeaponCfg,
)
from zsim.simulator.config_classes import SimulationConfig as SimCfg
from .constants import stats_trans_mapping
def generate_parallel_args(
stop_tick: int,
parallel_cfg: dict,
run_turn_uuid: str,
) -> Iterator[SimCfg]:
"""生成用于并行模拟的参数。
Args:
stop_tick: 模拟停止的 tick 数。
parallel_cfg: 并行模式的配置字典。
run_turn_uuid: 当前运行轮次的 UUID。
Yields:
MainArgs: 为每个模拟任务生成的参数对象。
"""
# Determine the function based on enabled flags
func = None
if parallel_cfg.get("adjust_sc", {}).get("enabled", False):
func = "attr_curve"
elif parallel_cfg.get("adjust_weapon", {}).get("enabled", False):
func = "weapon"
if func == "attr_curve":
adjust_sc_cfg = parallel_cfg["adjust_sc"]
sc_list = adjust_sc_cfg["sc_list"]
sc_range_start, sc_range_end = adjust_sc_cfg["sc_range"]
remove_equip_list = adjust_sc_cfg.get(
"remove_equip_list", []
) # 获取需要移除装备的词条列表,如果不存在则为空列表
for sc_name in sc_list:
for sc_value in range(sc_range_start, sc_range_end + 1):
args = ExecAttrCurveCfg(
stop_tick=stop_tick,
mode="parallel",
func=func,
adjust_char=parallel_cfg["adjust_char"],
sc_name=stats_trans_mapping[sc_name],
sc_value=sc_value,
run_turn_uuid=run_turn_uuid,
remove_equip=sc_name in remove_equip_list,
)
yield args
elif func == "weapon":
adjust_weapon_cfg = parallel_cfg["adjust_weapon"]
weapon_list = adjust_weapon_cfg["weapon_list"]
for weapon in weapon_list:
args = ExecWeaponCfg(
stop_tick=stop_tick,
mode="parallel",
func=func,
adjust_char=parallel_cfg["adjust_char"],
weapon_name=weapon["name"],
weapon_level=weapon["level"],
run_turn_uuid=run_turn_uuid,
)
yield args
else:
raise ValueError(f"Unknown func: {func}, full cfg: {parallel_cfg}")
def apl_selecter():
with open(config_path, "r", encoding="utf-8") as f:
config = json.load(f)
default_apl_path = config["database"]["APL_FILE_PATH"]
apl_archive = APLArchive()
default_apl_titile = apl_archive.get_title_from_path(default_apl_path)
options_list = list(apl_archive.options or [])
# 检查 default_apl_titile 是否在选项列表中
if default_apl_titile in options_list:
default_index = options_list.index(default_apl_titile)
else:
default_index = 0 # 如果不在,则默认选择第一个选项
selected_title = st.selectbox(
"APL选项",
options_list,
label_visibility="collapsed",
index=default_index,
)
return selected_title
def save_apl_selection(selected_title: str):
"""保存APL选择。
Args:
selected_title: 选中的APL标题。
"""
apl_archive = APLArchive()
original_path = apl_archive.get_origin_relative_path(selected_title)
with open(config_path, "r", encoding="utf-8") as f:
config = json.load(f)
config["database"]["APL_FILE_PATH"] = original_path
with open(config_path, "w", encoding="utf-8") as f:
json.dump(config, f, indent=4)
def get_default_apl_tile() -> str | None:
"""获取默认APL的标题。
Returns:
str: 默认APL的标题。
"""
with open(config_path, "r", encoding="utf-8") as f:
config = json.load(f)
default_apl_path = config["database"]["APL_FILE_PATH"]
apl_archive = APLArchive()
return apl_archive.get_title_from_path(default_apl_path)
def show_apl_judge_result(selected_title: str | None = None) -> bool:
"""显示并返回判断结果APL的判断结果。
Args:
selected_title (str): 选中的APL标题。
Returns:
bool: 判断结果APL的判断结果。
"""
if selected_title is None:
selected_title = get_default_apl_tile()
if selected_title is None:
st.error("未找到默认APL,请先选择一个APL。")
return False
apl_archive = APLArchive()
apl_data: dict[str, Any] | None = apl_archive.get_apl_data(selected_title)
if apl_data is None:
st.error("未找到APL数据,请检查APL文件是否正确。")
return False
apl_judge_tool = APLJudgeTool(apl_data)
required_chars_result: tuple[bool, list[str]] = apl_judge_tool.judge_requried_chars()
option_result_result: tuple[bool, list[str]] = apl_judge_tool.judge_optional_chars()
char_config_result: tuple[bool, dict[str, str | int]] = apl_judge_tool.judge_char_config()
if required_chars_result[0]:
st.success("必选角色满足要求")
else:
st.error(f"必选角色缺少:{required_chars_result[1]}")
if option_result_result[0]:
st.success("可选角色满足要求")
else:
st.error(f"可选角色缺少:{option_result_result[1]}")
if char_config_result[0]:
st.success("角色配置满足要求")
else:
st.error(f"角色配置缺少:{char_config_result[1]}")
return required_chars_result[0] and char_config_result[0]
def enemy_selector() -> None:
"""敌人配置选择器界面。"""
# 从enemy.csv获取所有唯一的IndexID和CN_enemy_ID,并按IndexID排序
with open(config_path, "r", encoding="utf-8") as f:
config = json.load(f)
saved_index = config["enemy"]["index_ID"]
saved_adjust = config["enemy"]["adjust_ID"]
# 只在首次加载时初始化session_state
if "enemy_index" not in st.session_state:
st.session_state["enemy_index"] = saved_index
if "enemy_adjust" not in st.session_state:
st.session_state["enemy_adjust"] = saved_adjust
# 获取所有可选项
enemy_lf = pl.scan_csv("zsim/data/enemy.csv")
enemy_data: pl.DataFrame = (
enemy_lf.select(["IndexID", "CN_enemy_ID"])
.unique(subset=["IndexID"])
.sort(by="IndexID", descending=True)
.collect()
)
enemy_options = []
enemy_values = []
for index_id, cn_enemy_id in enemy_data.iter_rows():
display_text = f"{index_id} - {cn_enemy_id}"
enemy_options.append(display_text)
enemy_values.append(index_id)
adjust_df = pl.scan_csv("zsim/data/enemy_adjustment.csv")
adjust_options: list[int] = sorted(
adjust_df.select("ID").unique().collect().to_series().to_list()
)
col_enemy1, col_enemy2 = st.columns(2)
with col_enemy1:
# 找到当前IndexID对应的显示选项索引
try:
current_index_pos = enemy_values.index(st.session_state["enemy_index"])
except ValueError:
current_index_pos = 0
selected_display = st.selectbox(
"选择敌人",
enemy_options,
index=current_index_pos,
help="数值为IndexID,同一个名字的怪物可能有不同的IndexID,他们的各项属性不同,选择时请注意",
key="enemy_index_selectbox",
)
selected_index = enemy_values[enemy_options.index(selected_display)]
with col_enemy2:
try:
current_adjust_pos = adjust_options.index(st.session_state["enemy_adjust"])
except ValueError:
current_adjust_pos = 0
selected_adjust = st.selectbox(
"敌人属性调整ID",
adjust_options,
index=current_adjust_pos,
help="一般每个关卡对应一个调整ID,不知道是什么的话就不改",
key="enemy_adjust_selectbox",
)
# 更新session_state为当前选择
st.session_state["enemy_index"] = selected_index
st.session_state["enemy_adjust"] = selected_adjust
# 检查是否有未保存的更改
if (
st.session_state["enemy_index"] != saved_index
or st.session_state["enemy_adjust"] != saved_adjust
):
st.session_state["enemy_config_unsaved"] = True
else:
st.session_state["enemy_config_unsaved"] = False
def save_enemy_selection(index_id: int, adjust_id: int):
"""保存敌人配置选择。
Args:
index_id: 选中的敌人基础ID
adjust_id: 选中的敌人调整ID
"""
# 创建配置文件临时备份
backup_path = config_path.parent / "config.json.bak"
shutil.copy(config_path, backup_path)
try:
# 部分更新配置文件
with open(config_path, "r+", encoding="utf-8") as f:
config = json.load(f)
# 只更新需要的部分
config["enemy"]["index_ID"] = index_id
config["enemy"]["adjust_ID"] = adjust_id
# 写回文件
f.seek(0)
json.dump(config, f, indent=4)
f.truncate()
except Exception as e:
# 出错时恢复备份
print(f"保存配置出错: {e}")
shutil.move(backup_path, config_path)
raise
finally:
# 清理备份
if os.path.exists(backup_path):
os.remove(backup_path)
#
# with open(CONFIG_PATH, "r", encoding="utf-8") as f:
# config = json.load(f)
#
# config["enemy"]["index_ID"] = index_id
# config["enemy"]["adjust_ID"] = adjust_id
#
# with open(CONFIG_PATH, "w", encoding="utf-8") as f:
# json.dump(config, f, indent=4)
================================================
FILE: zsim/lib_webui/version_checker.py
================================================
import json
import re
import webbrowser
from typing import Any
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
import streamlit as st
from zsim.define import GITHUB_REPO_NAME, GITHUB_REPO_OWNER, __version__
class GitHubVersionChecker:
"""GitHub版本检查器"""
def __init__(self, repo_owner: str, repo_name: str, current_version: str):
"""
初始化版本检查器
Args:
repo_owner: GitHub仓库所有者
repo_name: GitHub仓库名称
current_version: 当前版本号
"""
self.repo_owner = repo_owner
self.repo_name = repo_name
self.current_version = current_version
self.api_url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/releases/latest"
self.repo_url = f"https://github.com/{repo_owner}/{repo_name}"
def _parse_version(self, version: str) -> tuple[list, str, int]:
"""
解析版本号,支持预发布版本
Args:
version: 版本号字符串,如 "1.2.3a1" 或 "1.2.3"
Returns:
tuple: (主版本号列表, 预发布类型, 预发布版本号)
例如: ([1, 2, 3], "a", 1) 或 ([1, 2, 3], "", 0)
"""
# 移除版本号前的 'v' 前缀
clean_version = version.lstrip("v")
# 使用正则表达式匹配版本号格式
# 匹配格式: 数字.数字.数字[预发布标识符数字]
pattern = r"^(\d+(?:\.\d+)*?)([a-zA-Z]+)?(\d+)?$"
match = re.match(pattern, clean_version)
if not match:
# 如果不匹配,尝试简单的数字版本
try:
main_parts = [int(x) for x in clean_version.split(".")]
return main_parts, "", 0
except ValueError:
# 如果解析失败,返回默认值
return [0], "", 0
main_version = match.group(1)
prerelease_type = match.group(2) or ""
prerelease_num = int(match.group(3)) if match.group(3) else 0
# 解析主版本号
try:
main_parts = [int(x) for x in main_version.split(".")]
except ValueError:
main_parts = [0]
return main_parts, prerelease_type, prerelease_num
def _compare_versions(self, version1: str, version2: str) -> int:
"""
比较两个版本号,支持预发布版本
Args:
version1: 版本号1
version2: 版本号2
Returns:
-1: version1 < version2
0: version1 == version2
1: version1 > version2
"""
# 解析版本号
v1_main, v1_pre_type, v1_pre_num = self._parse_version(version1)
v2_main, v2_pre_type, v2_pre_num = self._parse_version(version2)
# 补齐主版本号长度
max_len = max(len(v1_main), len(v2_main))
v1_main.extend([0] * (max_len - len(v1_main)))
v2_main.extend([0] * (max_len - len(v2_main)))
# 首先比较主版本号
for i in range(max_len):
if v1_main[i] < v2_main[i]:
return -1
elif v1_main[i] > v2_main[i]:
return 1
# 主版本号相同,比较预发布版本
# 预发布版本的优先级:无预发布 > rc > b > a
prerelease_priority = {"": 4, "rc": 3, "b": 2, "a": 1}
v1_priority = prerelease_priority.get(v1_pre_type.lower(), 0)
v2_priority = prerelease_priority.get(v2_pre_type.lower(), 0)
if v1_priority != v2_priority:
return -1 if v1_priority < v2_priority else 1
# 预发布类型相同,比较预发布版本号
if v1_pre_type and v2_pre_type: # 都是预发布版本
if v1_pre_num < v2_pre_num:
return -1
elif v1_pre_num > v2_pre_num:
return 1
return 0
def check_for_updates(self, timeout: int = 10) -> dict[str, Any] | None:
"""
检查是否有新版本
Args:
timeout: 请求超时时间(秒)
Returns:
如果有新版本,返回包含版本信息的字典;否则返回None
"""
try:
# 创建请求
request = Request(
self.api_url,
headers={
"User-Agent": "ZZZ-Simulator-Version-Checker",
"Accept": "application/vnd.github.v3+json",
},
)
# 发送请求
with urlopen(request, timeout=timeout) as response:
if response.status == 200:
data = json.loads(response.read().decode("utf-8"))
latest_version = data.get("tag_name", "")
if not latest_version:
return None
# 比较版本
if self._compare_versions(self.current_version, latest_version) < 0:
return {
"latest_version": latest_version,
"current_version": self.current_version,
"release_url": data.get("html_url", self.repo_url),
"release_name": data.get("name", latest_version),
"release_body": data.get("body", ""),
"published_at": data.get("published_at", ""),
"download_url": data.get("zipball_url", ""),
}
return None
else:
print(f"GitHub API请求失败,状态码: {response.status}")
return None
except (URLError, HTTPError, json.JSONDecodeError, ValueError) as e:
print(f"检查更新时发生错误: {e}")
return None
@st.dialog("发现新版本")
def show_update_dialog(self, update_info: dict[str, Any]) -> None:
"""
显示更新对话框
Args:
update_info: 更新信息字典
"""
# 使用容器来确保对话框显示在顶部
with st.container():
st.success(f"🎉 发现新版本: {update_info['latest_version']}")
with st.expander("📋 查看更新详情", expanded=False):
col_info1, col_info2 = st.columns(2)
with col_info1:
st.markdown(f"**当前版本:** `v{update_info['current_version']}`")
if update_info.get("published_at"):
st.markdown(f"**发布时间:** {update_info['published_at'][:10]}")
with col_info2:
st.markdown(f"**最新版本:** `{update_info['latest_version']}`")
if update_info.get("release_name"):
st.markdown(f"**发布标题:** {update_info['release_name']}")
if update_info.get("release_body"):
st.markdown("**更新说明:**")
# 限制更新说明的长度,避免界面过长
release_body = update_info["release_body"]
if len(release_body) > 500:
release_body = release_body[:500] + "..."
st.markdown(release_body)
# 按钮布局
col1, col2 = st.columns(2)
with col1:
if st.button(
"🔗 前往发布页",
type="primary",
use_container_width=True,
key="download_btn",
):
webbrowser.open(update_info["release_url"])
st.success("已在浏览器中打开下载页面")
st.session_state.update_dismissed = True
with col2:
if st.button("❌ 暂不更新", use_container_width=True, key="dismiss_btn"):
st.session_state.update_dismissed = True
st.rerun()
def check_github_updates() -> None:
"""
检查GitHub更新的主函数
从pyproject.toml读取当前版本,检查GitHub仓库是否有新版本
"""
# 避免重复检查
if st.session_state.get("update_checked", False) or st.session_state.get(
"update_dismissed", False
):
return
try:
current_version = __version__
# 创建版本检查器
checker = GitHubVersionChecker(
repo_owner=GITHUB_REPO_OWNER,
repo_name=GITHUB_REPO_NAME,
current_version=current_version,
)
# 检查更新
update_info = checker.check_for_updates()
if update_info:
checker.show_update_dialog(update_info)
# 标记已检查
st.session_state.update_checked = True
except Exception as e:
print(f"检查更新时发生错误: {e}")
st.session_state.update_checked = True
================================================
FILE: zsim/main.py
================================================
import argparse
import timeit
from zsim.simulator.config_classes import (
ExecAttrCurveCfg,
ExecWeaponCfg,
)
from zsim.simulator.simulator_class import Simulator
if __name__ == "__main__":
# 创建命令行参数解析器
parser = argparse.ArgumentParser(description="ZZZ模拟器")
parser.add_argument("--stop-tick", type=int, default=None, help="指定模拟的tick数量 int")
parser.add_argument(
"--mode",
type=str,
default="normal",
choices=["normal", "parallel"],
help="运行模式",
)
parser.add_argument(
"--func",
type=str,
default=None,
choices=["attr_curve", "weapon"],
help="功能选择",
)
parser.add_argument(
"--adjust-char",
type=int,
default=None,
choices=[1, 2, 3],
help="调整的角色相对位置",
)
parser.add_argument("--sc-name", type=str, default=None, help="要调整的副词条名称 str")
parser.add_argument("--sc-value", type=int, default=None, help="要调整的副词条数量 int")
parser.add_argument("--run-turn-uuid", type=str, default=None, help="运行的uuid str")
parser.add_argument(
"--remove-equip",
action="store_true",
default=False,
help="移除装备 (存在此标志时移除)",
)
parser.add_argument(
"--weapon-name",
type=str,
default=None,
help="要调整的武器名称 str",
)
parser.add_argument(
"--weapon-level",
type=int,
default=None,
help="要调整的武器精炼等级 int",
)
# 解析命令行参数
args = parser.parse_args()
print(args)
if args.mode == "normal":
print("常规模式")
# 常规模式,作为单进程运行,读取全部的配置
simulator_instance = Simulator()
if args.stop_tick is not None:
print(
f"\n主循环耗时: {timeit.timeit(lambda: simulator_instance.main_loop(args.stop_tick), globals=globals(), number=1):.2f} s"
)
else:
print(
f"\n主循环耗时: {timeit.timeit(simulator_instance.main_loop, globals=globals(), number=1):.2f} s"
)
print("\n正在等待IO结束···")
elif args.mode == "parallel":
print("并行模式")
print(args)
simulator_instance = Simulator()
# 并行模式,作为子进程运行,角色的指定副词条将被设为传入值,并根据是否移除其他主副词条进行模拟
if func := args.func == "attr_curve":
sim_cfg: ExecAttrCurveCfg = ExecAttrCurveCfg(
stop_tick=args.stop_tick,
mode=args.mode,
adjust_char=args.adjust_char,
sc_name=args.sc_name,
sc_value=args.sc_value,
run_turn_uuid=args.run_turn_uuid,
remove_equip=args.remove_equip,
)
elif func := args.func == "weapon":
sim_cfg: ExecWeaponCfg = ExecWeaponCfg(
stop_tick=args.stop_tick,
mode=args.mode,
adjust_char=args.adjust_char,
weapon_name=args.weapon_name,
weapon_level=args.weapon_level,
run_turn_uuid=args.run_turn_uuid,
)
else:
raise ValueError("func参数错误")
if args.stop_tick is not None:
print(
f"\n主循环耗时: {timeit.timeit(lambda: simulator_instance.main_loop(args.stop_tick, sim_cfg=sim_cfg), globals=globals(), number=1):.2f} s"
)
else:
print(
f"\n主循环耗时: {timeit.timeit(lambda: simulator_instance.main_loop(sim_cfg=sim_cfg), globals=globals(), number=1):.2f} s"
)
================================================
FILE: zsim/models/character/__init__.py
================================================
================================================
FILE: zsim/models/character/character_config.py
================================================
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, Field
class CharacterConfig(BaseModel):
"""角色配置数据模型"""
config_id: str = Field(description="角色配置ID,格式为 {name}_{config_name}")
name: str = Field(description="角色名称")
config_name: str = Field(description="配置名称")
weapon: str
weapon_level: int
cinema: int
crit_balancing: bool
crit_rate_limit: float
scATK_percent: int
scATK: int
scHP_percent: int
scHP: int
scDEF_percent: int
scDEF: int
scAnomalyProficiency: int
scPEN: int
scCRIT: int
scCRIT_DMG: int
drive4: str
drive5: str
drive6: str
equip_style: str
equip_set4: Optional[str] = None
equip_set2_a: Optional[str] = None
equip_set2_b: Optional[str] = None
equip_set2_c: Optional[str] = None
create_time: datetime = Field(default_factory=datetime.now, description="配置创建时间")
update_time: datetime = Field(default_factory=datetime.now, description="配置更新时间")
================================================
FILE: zsim/models/enemy/__init__.py
================================================
================================================
FILE: zsim/models/enemy/enemy_config.py
================================================
from datetime import datetime
from typing import Any, Dict
from pydantic import BaseModel, Field
class EnemyConfig(BaseModel):
"""敌人配置数据模型"""
config_id: str = Field(description="敌人配置ID")
enemy_index: int
enemy_adjust: Dict[str, Any]
create_time: datetime = Field(default_factory=datetime.now, description="配置创建时间")
update_time: datetime = Field(default_factory=datetime.now, description="配置更新时间")
================================================
FILE: zsim/models/event_enums.py
================================================
# 此文件记录了所有的和事件广播以及后置初始化有关的枚举类
from enum import Enum
class SpecialStateUpdateSignal(Enum):
"""特殊状态管理器的更新信号类"""
"""在Preload之后,广播点位位于Preload末尾,ConfirmEngine中与外部数据交互时。"""
BEFORE_PRELOAD = "BeforePreload"
"""在角色接收技能的点位广播"""
CHARACTER = "Character"
"""广播点位位于Enemy接受到攻击时候"""
RECEIVE_HIT = "ReceiveHit"
SSUS = SpecialStateUpdateSignal
class PostInitObjectType(Enum):
"""记录了所有需要后置初始化的数据的大类型,以及它们在各自的管理器中传入工厂函数所对应的参数"""
SweetScare = ("SweetScare", [SSUS.RECEIVE_HIT, SSUS.BEFORE_PRELOAD, SSUS.CHARACTER])
class ListenerBroadcastSignal(Enum):
"""监听器广播函数所涉及到的更新信号"""
SWITCHING_IN = "switching_in_event" # 角色切入前场
ENTER_BATTLE = "enter_battle_event" # 角色进入战斗
ANOMALY = "anomaly_event" # 属性异常事件
STUN = "stun_event" # 失衡事件
PARRY = "parry_event" # 招架事件
BLOCK = "block_event" # 格挡事件(其他具备格挡功能的技能响应进攻事件)
DISORDER_SPAWN = "disorder_event_spawn" # 紊乱事件产生
DISORDER_SETTLED = "disorder_event_settled" # 紊乱事件结算
ASSAULT_STATE_ON = (
"assistant_state_on" # 畏缩状态上升沿或者刷新——等价于“队伍中任意角色对敌人施加物理异常状态”
)
ASSAULT_SPAWN = "assault_spawn" # 强击触发
POLARIZED_ASSAULT_SPAWN = "polarized_assault" # 极性强击触发
================================================
FILE: zsim/models/session/__init__.py
================================================
================================================
FILE: zsim/models/session/session_create.py
================================================
from datetime import datetime
from typing import Literal
from uuid import uuid4
from pydantic import BaseModel, Field
from .session_result import NormalModeResult, ParallelModeResult
from .session_run import SessionRun
def generate_session_id() -> str:
"""Generate a unique session ID: YYYYMMDD + first 8 chars of uuid4."""
date_str = datetime.now().strftime("%Y%m%d")
uuid_part = str(uuid4())[:8]
return f"{date_str}-{uuid_part}"
class Session(BaseModel):
"""Session configuration model."""
session_id: str = Field(
default_factory=generate_session_id, description="随机生成的会话ID,为本日日期+8位UUID前缀"
)
session_name: str = Field(default="", description="会话名称")
create_time: datetime = Field(
default_factory=datetime.now, description="会话创建时间,默认当前时间"
)
session_run: SessionRun | None = None
session_result: list[NormalModeResult | ParallelModeResult] | None = None
status: Literal["pending", "running", "completed", "stopped", "failed"] = Field(
default="pending", description="会话状态"
)
if __name__ == "__main__":
print(Session())
================================================
FILE: zsim/models/session/session_result.py
================================================
from typing import Any, Literal, Self, Union
from pydantic import BaseModel, Field, RootModel
# --- Payloads for different result types ---
# --- Normal Mode ---
class DmgResult(RootModel[dict[str, Any] | None]):
"""
Represents the damage calculation results.
The root is a dictionary containing various dataframes (as list of dicts)
for detailed damage analysis. The structure is preserved from the webui
processing functions for compatibility.
"""
pass
class BuffTimelineBarValue(BaseModel):
task: str = Field(description="Buff name", alias="Task")
start: int = Field(description="Start tick of the buff", alias="Start")
finish: int = Field(description="End tick of the buff", alias="Finish")
value: float = Field(description="Buff value/stack", alias="Value")
class BuffResult(RootModel[dict[str, list[BuffTimelineBarValue]] | None]):
"""
Represents the buff timeline results.
The root is a dictionary where keys are source identifiers (e.g., file keys)
and values are lists of buff timeline points.
"""
pass
class NormalResultPayload(BaseModel):
dmg_result: DmgResult | None
buff_result: BuffResult | None
# --- Parallel Mode ---
class AttrCurvePoint(BaseModel):
result: float = Field(description="Total damage for this data point")
rate: float | None = Field(description="Rate of return compared to the previous point")
class AttrCurvePayload(RootModel[dict[str, dict[str, dict[str, AttrCurvePoint]]]]):
"""
Represents the attribute curve results.
Structure: {char_name: {sc_name: {sc_value: point_data}}}
"""
pass
class WeaponResultPoint(BaseModel):
damage: float = Field(description="Total damage for this weapon configuration")
class WeaponPayload(RootModel[dict[str, dict[str, dict[str, WeaponResultPoint]]]]):
"""
Represents the weapon comparison results.
Structure: {char_name: {weapon_name: {weapon_level: point_data}}}
"""
pass
class ParallelAttrCurveResultPayload(BaseModel):
func: Literal["attr_curve"]
result: AttrCurvePayload
class ParallelWeaponResultPayload(BaseModel):
func: Literal["weapon"]
result: WeaponPayload
class ParallelResultPayload(
RootModel[Union[ParallelAttrCurveResultPayload, ParallelWeaponResultPayload]]
):
root: Union[ParallelAttrCurveResultPayload, ParallelWeaponResultPayload] = Field(
..., discriminator="func"
)
# --- Discriminated Union Models ---
class NormalModeResult(BaseModel):
mode: Literal["normal"]
result: NormalResultPayload
class ParallelModeResult(BaseModel):
mode: Literal["parallel"]
func: Literal["attr_curve", "weapon"]
result: ParallelResultPayload
# --- Top-level SessionResult Factory Class ---
class SessionResult:
"""
This class acts as a factory for creating specific result models
(NormalModeResult or ParallelModeResult) based on the 'mode' field.
It allows instantiation like `SessionResult(mode='normal', result=...)`,
and the returned object will be a validated instance of the correct model.
This is not a Pydantic model itself, but a dispatcher.
"""
def __new__(cls, **kwargs: Any) -> Self | NormalModeResult | ParallelModeResult:
# This is not a standard Pydantic model, but a factory that returns one.
# It's designed to match the instantiation pattern in the controller.
if cls is not SessionResult:
# This allows subclasses to be instantiated normally if needed.
return super().__new__(cls)
mode = kwargs.get("mode")
if mode == "normal":
return NormalModeResult(**kwargs)
elif mode == "parallel":
return ParallelModeResult(**kwargs)
else:
raise ValueError(f"Invalid 'mode' for SessionResult: {mode}")
================================================
FILE: zsim/models/session/session_run.py
================================================
"""
单个会话的启动配置参数
初始化json样例:
session_config = SessionRun(
**{
"stop_tick": 3600,
"mode": "parallel",
"common_config": {
"session_id": "123",
"char_config": [
{"name": "角色名", "CID": 1},
{"name": "角色名", "CID": 2},
{"name": "角色名", "CID": 3},
],
"enemy_config": {
"index_id": 11451,
"adjustment_id": 22041,
"difficulty": 8.74,
},
"apl_path": "path/to/apl.toml",
},
"parallel_config": {
"enable": "true",
"adjust_char": 2,
"func": "attr_curve", # 可选值功能,后续会拓展
"func_config": { # 根据 func 的值,自动将 func_config 字典转换为正确的模型实例
"sc_range": [0, 40],
"sc_list": ["scATK_percent", "scCRIT", "scCRIT_DMG"],
"remove_equip_list": [],
},
},
}
)
"""
from typing import Literal, Self
from pydantic import (
BaseModel,
Field,
NonNegativeFloat,
NonNegativeInt,
ValidationError,
model_validator,
)
class CharConfig(BaseModel):
"""角色配置参数"""
name: str
CID: int | None = None
weapon: str | None = None
weapon_level: Literal[1, 2, 3, 4, 5] = 1
equip_style: Literal["4+2", "2+2+2"] = "4+2"
equip_set4: str | None = None
equip_set2_a: str | None = None
equip_set2_b: str | None = None
equip_set2_c: str | None = None
drive4: str | None = None
drive5: str | None = None
drive6: str | None = None
scATK_percent: NonNegativeInt = 0
scATK: NonNegativeInt = 0
scHP_percent: NonNegativeInt = 0
scHP: NonNegativeInt = 0
scDEF_percent: NonNegativeInt = 0
scDEF: NonNegativeInt = 0
scAnomalyProficiency: NonNegativeInt = 0
scPEN: NonNegativeInt = 0
scCRIT: NonNegativeInt = 0
scCRIT_DMG: NonNegativeInt = 0
sp_limit: NonNegativeInt | NonNegativeFloat = 120
cinema: Literal[0, 1, 2, 3, 4, 5, 6] = 0
crit_balancing: bool = True
crit_rate_limit: NonNegativeFloat = 0.95
@model_validator(mode="after")
def validate_stats(self) -> Self:
"""验证属性值是否合法"""
# 验证暴击率上限
if not 0.05 <= self.crit_rate_limit <= 1:
raise ValidationError("暴击率上限必须在0.05到1之间")
# 验证所有sc属性必须大于等于0
return self
class EnemyConfig(BaseModel):
"""敌人配置参数"""
index_id: int
adjustment_id: int | str
difficulty: int | float = 8.74
class SimulationConfig(BaseModel):
"""模拟配置参数"""
# all mode common:
stop_tick: int | None = Field(None, description="指定模拟的tick数量")
mode: Literal["normal", "parallel"] | None = Field(None, description="运行模式")
func: Literal["attr_curve", "weapon"] | None = Field(None, description="功能选择")
adjust_char: Literal[1, 2, 3] | None = Field(None, description="调整的角色相对位置")
run_turn_uuid: str | None = Field(None, description="本轮次并行运行的uuid")
class ExecAttrCurveCfg(SimulationConfig):
"""调整副词条配置参数"""
func: Literal["attr_curve", "weapon"] | None = "attr_curve"
sc_name: str
sc_value: int
remove_equip: bool = False
class ExecWeaponCfg(SimulationConfig):
"""调整武器配置参数"""
func: Literal["attr_curve", "weapon"] | None = "weapon"
weapon_name: str
weapon_level: Literal[1, 2, 3, 4, 5]
class CommonCfg(BaseModel):
"""通用配置参数"""
session_id: str
char_config: list[CharConfig] = []
enemy_config: EnemyConfig
apl_path: str = ""
@model_validator(mode="after")
def validate_char_config(self) -> Self:
"""验证角色配置参数"""
# 角色配置参数不能为空
if len(self.char_config) != 3:
raise ValidationError("角色配置参数必须为3个")
return self
class ParallelCfg(BaseModel):
enable: bool = False
adjust_char: Literal[1, 2, 3]
func: Literal["attr_curve", "weapon"] | None = None
func_config: "AttrCurveConfig | WeaponConfig | None" = None
class AttrCurveConfig(BaseModel):
"""调整属性曲线配置参数"""
sc_range: tuple[int, int] = (0, 40)
sc_list: list[str]
remove_equip_list: list[str] = []
class WeaponConfig(BaseModel):
"""调整武器配置参数"""
weapon_list: list["SingleWeapon"] = []
class SingleWeapon(BaseModel):
name: str
level: Literal[1, 2, 3, 4, 5] = 1
ParallelCfg.model_rebuild()
ParallelCfg.WeaponConfig.model_rebuild()
class SessionRun(BaseModel):
"""模拟器会话配置参数,启动会话的全部数据"""
# all mode common:
stop_tick: int | None = Field(None, description="指定模拟的tick数量")
mode: Literal["normal", "parallel"] | None = Field(None, description="运行模式")
common_config: CommonCfg
parallel_config: ParallelCfg | None = None
@model_validator(mode="after")
def validate_common_config(self) -> Self:
"""验证通用配置参数"""
if self.mode == "parallel" and self.parallel_config is None:
raise ValueError("并行模式下,parallel_config 不能为空")
return self
if __name__ == "__main__":
config = ParallelCfg(
enable=True,
adjust_char=2,
func="attr_curve",
func_config={
"sc_range": (0, 40),
"sc_list": ["scATK", "scDEF"],
"remove_equip_list": [],
}, # type: ignore
)
print(config)
try:
session_config = SessionRun(
stop_tick=1000,
mode="parallel",
common_config={
"session_id": "123",
"char_config": [{"name": ""}, {"name": ""}, {"name": ""}],
"enemy_config": {"index_id": 1, "adjustment_id": "s"},
"apl_path": "",
}, # type: ignore
parallel_config=config,
)
print(session_config.model_dump_json(indent=4))
except ValidationError as e:
print(e)
try:
session_config = SessionRun(
**{
"stop_tick": 3600,
"mode": "parallel",
"common_config": {
"session_id": "123",
"char_config": [
{"name": "角色名", "CID": 1},
{"name": "角色名", "CID": 2},
{"name": "角色名", "CID": 3},
],
"enemy_config": {
"index_id": 11451,
"adjustment_id": 22041,
"difficulty": 8.74,
},
"apl_path": "path/to/apl.toml",
},
"parallel_config": {
"enable": "true",
"adjust_char": 2,
"func": "attr_curve", # 可选值功能,后续会拓展
"func_config": { # 根据 func 的值,自动将 func_config 字典转换为正确的模型实例
"sc_range": [0, 40],
"sc_list": ["scATK_percent", "scCRIT", "scCRIT_DMG"],
"remove_equip_list": [],
},
},
}
)
except ValidationError as e:
print(e)
================================================
FILE: zsim/page_apl_editor.py
================================================
import streamlit as st
def page_apl_editor():
st.title("ZZZ Simulator - APL编辑器")
from zsim.lib_webui.process_apl_editor import go_apl_editor
go_apl_editor()
page_apl_editor()
================================================
FILE: zsim/page_character_config.py
================================================
import streamlit as st
import tomli_w
def page_character_config():
st.title("ZZZ Simulator - 角色配置")
from zsim.define import saved_char_config
from zsim.lib_webui.constants import default_chars
if "name_box" in saved_char_config:
default_chars = saved_char_config["name_box"]
from zsim.lib_webui.constants import (
char_profession_map,
equip_set2_options,
equip_set4_options,
profession_chars_map,
weapon_char_map,
weapon_options,
weapon_profession_map,
weapon_rarity_map,
)
col0, col1, col2, col3, col4, col5, col6, col7 = st.columns([1, 1, 1, 1, 1, 1, 1, 1])
with col0:
profession_0 = st.selectbox(
"角色1特性",
list(profession_chars_map.keys()),
index=list(profession_chars_map.keys()).index("不限特性"),
key="profession_select_0",
)
with col1:
name_box_0 = [
st.selectbox(
"角色1",
profession_chars_map[profession_0],
index=profession_chars_map[profession_0].index(default_chars[0])
if len(default_chars) > 0 and default_chars[0] in profession_chars_map[profession_0]
else 0,
key="char_select_0",
)
]
with col3:
profession_1 = st.selectbox(
"角色2特性",
list(profession_chars_map.keys()),
index=list(profession_chars_map.keys()).index("不限特性"),
key="profession_select_1",
)
with col4:
name_box_1 = [
st.selectbox(
"角色2",
profession_chars_map[profession_1],
index=profession_chars_map[profession_1].index(default_chars[1])
if len(default_chars) > 1 and default_chars[1] in profession_chars_map[profession_1]
else 0,
key="char_select_1",
)
]
with col6:
profession_2 = st.selectbox(
"角色3特性",
list(profession_chars_map.keys()),
index=list(profession_chars_map.keys()).index("不限特性"),
key="profession_select_2",
)
with col7:
name_box_2 = [
st.selectbox(
"角色3",
profession_chars_map[profession_2],
index=profession_chars_map[profession_2].index(default_chars[2])
if len(default_chars) > 2 and default_chars[2] in profession_chars_map[profession_2]
else 0,
key="char_select_2",
)
]
name_box = name_box_0 + name_box_1 + name_box_2
if len(name_box) != 3:
st.stop()
if len(name_box) != len(set(name_box)):
st.error("请选择三个不同的角色")
st.stop()
for name in name_box:
with st.expander(f"{name}的配置"):
col_weapon, col_level, col_cinema = st.columns(3)
with col_weapon:
show_adapted_weapon = st.session_state.get(f"{name}_show_adapted_weapon", True)
show_rarity_s = st.session_state.get(f"{name}_show_rarity_s", True)
show_rarity_a = st.session_state.get(f"{name}_show_rarity_a", True)
show_rarity_b = st.session_state.get(f"{name}_show_rarity_b", False)
char_profession = char_profession_map.get(name)
if show_adapted_weapon and char_profession:
filtered_weapon_options = [
w for w in weapon_options if weapon_profession_map.get(w) == char_profession
]
else:
filtered_weapon_options = list(weapon_options)
# 根据稀有度筛选
filtered_weapon_options = [
w
for w in filtered_weapon_options
if (show_rarity_s and weapon_rarity_map.get(w) == "S")
or (show_rarity_a and weapon_rarity_map.get(w) == "A")
or (show_rarity_b and weapon_rarity_map.get(w) == "B")
]
rarity_order = {"S": 0, "A": 1, "B": 2}
filtered_weapon_options = sorted(
filtered_weapon_options,
key=lambda w: (rarity_order.get(weapon_rarity_map.get(w), 3), w),
)
if name in saved_char_config:
current_weapon = saved_char_config[name].get("weapon")
else:
current_weapon = None
# 如果当前音擎不在可选列表,或未设置,则默认选第一个
if not filtered_weapon_options:
st.selectbox(
"音擎",
[],
key=f"{name}_weapon",
)
else:
if current_weapon not in filtered_weapon_options:
current_weapon = filtered_weapon_options[0]
st.selectbox(
"音擎",
filtered_weapon_options,
index=filtered_weapon_options.index(current_weapon),
key=f"{name}_weapon",
format_func=lambda x: (
f"({weapon_rarity_map.get(x, '未知')}"
f"{' ' + weapon_char_map.get(x) if weapon_char_map.get(x) else ''}) {x}"
),
)
col_rarity = st.columns(4)
with col_rarity[0]:
show_adapted_weapon = st.checkbox(
"只显示适配音擎",
value=show_adapted_weapon,
key=f"{name}_show_adapted_weapon",
)
with col_rarity[1]:
show_rarity_s = st.checkbox(
"S",
value=show_rarity_s,
key=f"{name}_show_rarity_s",
)
with col_rarity[2]:
show_rarity_a = st.checkbox(
"A",
value=show_rarity_a,
key=f"{name}_show_rarity_a",
)
with col_rarity[3]:
show_rarity_b = st.checkbox(
"B",
value=show_rarity_b,
key=f"{name}_show_rarity_b",
)
with col_level:
st.number_input(
"音擎精炼等级",
min_value=1,
max_value=5,
value=saved_char_config[name].get("weapon_level", 1)
if name in saved_char_config
else 1,
key=f"{name}_weapon_level",
)
with col_cinema:
st.number_input(
"影画等级",
min_value=0,
max_value=6,
value=saved_char_config[name].get("cinema", 0)
if name in saved_char_config
else 0,
key=f"{name}_cinema_level",
)
equip_style = st.radio(
"驱动盘搭配方式",
["4+2", "2+2+2"],
index=0
if name not in saved_char_config or "equip_style" not in saved_char_config[name]
else (0 if saved_char_config[name]["equip_style"] == "4+2" else 1),
key=f"{name}_equip_style",
)
col1, col2 = st.columns(2)
with col1:
if equip_style == "4+2":
st.selectbox(
"四件套",
equip_set4_options,
index=equip_set4_options.index(saved_char_config[name]["equip_set4"])
if name in saved_char_config and "equip_set4" in saved_char_config[name]
else 0,
key=f"{name}_equip_set4",
)
st.selectbox(
"二件套",
equip_set2_options,
index=equip_set2_options.index(
saved_char_config[name].get("equip_set2_a", "啄木鸟电音")
)
if name in saved_char_config
else 0,
key=f"{name}_equip_set2",
)
else:
st.selectbox(
"二件套A",
equip_set2_options,
index=equip_set2_options.index(
saved_char_config[name].get("equip_set2_a", "啄木鸟电音")
)
if name in saved_char_config
else 0,
key=f"{name}_equip_set2A",
)
st.selectbox(
"二件套B",
equip_set2_options,
index=equip_set2_options.index(
saved_char_config[name].get("equip_set2_b", "啄木鸟电音")
)
if name in saved_char_config and "equip_set2_b" in saved_char_config[name]
else 0,
key=f"{name}_equip_set2B",
)
st.selectbox(
"二件套C",
equip_set2_options,
index=equip_set2_options.index(
saved_char_config[name].get("equip_set2_c", "啄木鸟电音")
)
if name in saved_char_config and "equip_set2_c" in saved_char_config[name]
else 0,
key=f"{name}_equip_set2C",
)
with col2:
from zsim.lib_webui.constants import (
main_stat4_options,
main_stat5_options,
main_stat6_options,
)
st.selectbox(
"四号位主词条",
main_stat4_options,
index=main_stat4_options.index(saved_char_config[name].get("drive4", "攻击力%"))
if name in saved_char_config
else 0,
key=f"{name}_main_stat4",
)
st.selectbox(
"五号位主词条",
main_stat5_options,
index=main_stat5_options.index(saved_char_config[name].get("drive5", "攻击力%"))
if name in saved_char_config
else 0,
key=f"{name}_main_stat5",
)
st.selectbox(
"六号位主词条",
main_stat6_options,
index=main_stat6_options.index(
saved_char_config[name].get("drive6 ", "攻击力%")
)
if name in saved_char_config
else 0,
key=f"{name}_main_stat6",
)
from zsim.lib_webui.constants import sc_max_value
st.text("副词条数量:")
col1, col2, col3, col4, col5 = st.columns(5)
with col1:
st.number_input(
"攻击力%",
min_value=0,
max_value=sc_max_value,
value=saved_char_config[name].get("scATK_percent", 0)
if name in saved_char_config
else 0,
key=f"{name}_scATK_percent",
)
st.number_input(
"攻击力",
min_value=0,
max_value=sc_max_value,
value=saved_char_config[name].get("scATK", 0)
if name in saved_char_config
else 0,
key=f"{name}_scATK",
)
with col2:
st.number_input(
"生命值%",
min_value=0,
max_value=sc_max_value,
value=saved_char_config[name].get("scHP_percent", 0)
if name in saved_char_config
else 0,
key=f"{name}_scHP_percent",
)
st.number_input(
"生命值",
min_value=0,
max_value=sc_max_value,
value=saved_char_config[name].get("scHP", 0)
if name in saved_char_config
else 0,
key=f"{name}_scHP",
)
with col3:
st.number_input(
"防御力%",
min_value=0,
max_value=sc_max_value,
value=saved_char_config[name].get("scDEF_percent", 0)
if name in saved_char_config
else 0,
key=f"{name}_scDEF_percent",
)
st.number_input(
"防御力",
min_value=0,
max_value=sc_max_value,
value=saved_char_config[name].get("scDEF", 0)
if name in saved_char_config
else 0,
key=f"{name}_scDEF",
)
with col4:
st.number_input(
"暴击率",
min_value=0,
max_value=sc_max_value,
value=saved_char_config[name].get("scCRIT", 0)
if name in saved_char_config
else 0,
key=f"{name}_scCRIT",
)
st.number_input(
"暴击伤害",
min_value=0,
max_value=sc_max_value,
value=saved_char_config[name].get("scCRIT_DMG", 0)
if name in saved_char_config
else 0,
key=f"{name}_scCRIT_DMG",
)
with col5:
st.number_input(
"异常精通",
min_value=0,
max_value=sc_max_value,
value=saved_char_config[name].get("scAnomalyProficiency", 0)
if name in saved_char_config
else 0,
key=f"{name}_scAnomalyProficiency",
)
st.number_input(
"穿透值",
min_value=0,
max_value=sc_max_value,
value=saved_char_config[name].get("scPEN", 0)
if name in saved_char_config
else 0,
key=f"{name}_scPEN",
)
col1, col2 = st.columns(2)
with col1:
if name not in saved_char_config:
saved_char_config[name] = {}
crit_balancing: bool = st.checkbox(
"使用暴击配平算法",
value=saved_char_config[name].get("crit_balancing", False),
key=f"{name}_crit_balancing",
)
if st.session_state.get(f"{name}_crit_rate_limit") is None:
st.session_state[f"{name}_crit_rate_limit"] = saved_char_config[name].get(
"crit_rate_limit", 0.95
)
if crit_balancing:
st.number_input(
"暴击率上限",
min_value=0.0,
max_value=1.0,
value=st.session_state[f"{name}_crit_rate_limit"],
key=f"{name}_crit_rate_limit",
help="配平算法会将角色局外面板的暴击率限制在此值以下,适用于会吃到暴击率buff的情况,以防止溢出",
)
char_config = {
"name": name,
"weapon": st.session_state[f"{name}_weapon"],
"weapon_level": st.session_state[f"{name}_weapon_level"],
"cinema": st.session_state[f"{name}_cinema_level"],
"crit_balancing": st.session_state[f"{name}_crit_balancing"],
"crit_rate_limit": st.session_state[f"{name}_crit_rate_limit"],
"scATK_percent": st.session_state[f"{name}_scATK_percent"],
"scATK": st.session_state[f"{name}_scATK"],
"scHP_percent": st.session_state[f"{name}_scHP_percent"],
"scHP": st.session_state[f"{name}_scHP"],
"scDEF_percent": st.session_state[f"{name}_scDEF_percent"],
"scDEF": st.session_state[f"{name}_scDEF"],
"scAnomalyProficiency": st.session_state[f"{name}_scAnomalyProficiency"],
"scPEN": st.session_state[f"{name}_scPEN"],
"scCRIT": st.session_state[f"{name}_scCRIT"],
"scCRIT_DMG": st.session_state[f"{name}_scCRIT_DMG"],
"drive4": st.session_state[f"{name}_main_stat4"],
"drive5": st.session_state[f"{name}_main_stat5"],
"drive6": st.session_state[f"{name}_main_stat6"],
"equip_style": st.session_state[f"{name}_equip_style"],
}
if st.session_state[f"{name}_equip_style"] == "4+2":
char_config["equip_set4"] = st.session_state[f"{name}_equip_set4"]
char_config["equip_set2_a"] = st.session_state[f"{name}_equip_set2"]
else:
char_config["equip_set2_a"] = st.session_state[f"{name}_equip_set2A"]
char_config["equip_set2_b"] = st.session_state[f"{name}_equip_set2B"]
char_config["equip_set2_c"] = st.session_state[f"{name}_equip_set2C"]
st.session_state[f"{name}_config"] = char_config
if not st.button("提交并保存角色配置"):
st.stop()
_config_to_save = {"name_box": name_box}
for name in name_box:
_config_to_save[name] = st.session_state[f"{name}_config"]
saved_char_config.update(_config_to_save)
from zsim.define import char_config_file
with open(char_config_file, "wb") as f:
tomli_w.dump(saved_char_config, f)
from zsim.lib_webui.process_char_config import display_character_panels
display_character_panels(name_box)
page_character_config()
================================================
FILE: zsim/page_data_analysis.py
================================================
import streamlit as st
from zsim.lib_webui.clean_results_cache import (
delete_result,
get_all_results,
rename_result,
)
from zsim.lib_webui.constants import IDDuplicateError
@st.fragment
def _result_manager():
id_cache = get_all_results()
options = list(id_cache.keys())[::-1]
if not options:
st.warning("没有找到任何结果缓存。请先运行模拟器生成结果。", icon="⚠️")
st.stop()
st.markdown("选择一个结果:")
col1, col2, col3, col4 = st.columns(4)
with col1:
selected_key = st.selectbox("选择你要查看的结果", options, label_visibility="collapsed")
with col2:
st.markdown(
f'备注:
{id_cache.get(selected_key, "N/A")}',
unsafe_allow_html=True,
)
with col3:
@st.dialog("重命名结果")
def rename_dialog(current_key, current_comment):
new_name = st.text_input("请输入新的ID", value=current_key)
new_comment = st.text_input("请输入新的备注信息", value=current_comment)
col1_dialog, col2_dialog = st.columns(2)
with col1_dialog:
if st.button("保存", key="save_rename", use_container_width=True):
try:
rename_result(current_key, new_name or "", new_comment or "")
st.rerun()
except FileNotFoundError:
st.warning("请检查文件是否存在或ID是否正确。", icon="⚠️")
except IDDuplicateError as e:
st.warning(e, icon="⚠️")
with col2_dialog:
if st.button("取消", key="cancel_rename", use_container_width=True):
st.rerun()
if st.button("重命名", use_container_width=True):
rename_dialog(selected_key, id_cache.get(selected_key, ""))
with col4:
@st.dialog("删除结果")
def delete_dialog(key_to_delete):
st.warning(f"你确定要删除 {key_to_delete} 吗?", icon="⚠️")
col1_dialog, col2_dialog = st.columns(2)
with col1_dialog:
if st.button("确定", key="confirm_del_result", use_container_width=True):
delete_result(key_to_delete)
st.rerun()
with col2_dialog:
if st.button("取消", key="cancel", use_container_width=True):
st.rerun()
if st.button("删除", use_container_width=True):
delete_dialog(selected_key)
return selected_key
def page_data_analysis():
from zsim.lib_webui.process_buff_result import show_buff_result
from zsim.lib_webui.process_dmg_result import show_dmg_result
from zsim.lib_webui.process_parallel_data import (
judge_parallel_result,
process_parallel_result,
)
st.title("ZZZ Simulator - 数据分析")
selected_key = _result_manager()
if not st.toggle("开启数据分析"):
st.stop()
# Ensure selected_key is valid before proceeding
if not selected_key:
st.error("无法获取选定的结果键。")
st.stop()
if judge_parallel_result(selected_key):
st.write("这是一个并行模式(多进程)的结果。")
process_parallel_result(selected_key)
else:
st.write("这是一个普通模式(单进程)的结果。")
show_dmg_result(selected_key)
show_buff_result(selected_key)
page_data_analysis()
================================================
FILE: zsim/page_simulator.py
================================================
import concurrent.futures
import json
import os
import uuid
from typing import Literal
import psutil
import streamlit as st
from zsim.define import NEW_SIM_BOOT, saved_char_config
from zsim.lib_webui.constants import stats_trans_mapping, weapon_options
from zsim.lib_webui.multiprocess_wrapper import (
run_parallel_simulation,
run_single_simulation,
)
from zsim.lib_webui.process_char_config import dialog_character_panels
from zsim.lib_webui.process_simulator import (
apl_selecter,
enemy_selector,
generate_parallel_args,
save_apl_selection,
save_enemy_selection,
show_apl_judge_result,
)
from zsim.run import go_parallel_subprocess, go_single_subprocess
apl_legal = False
# --- 常量定义 ---
# 模拟器配置相关
RUN_MODES: list[Literal["普通模式(单进程)", "并行模式(多进程)"]] = [
"普通模式(单进程)",
"并行模式(多进程)",
]
ADJUST_CHAR_OPTIONS = ["1号", "2号", "3号"]
SIMULATION_FUNCTIONS = [
"属性收益曲线",
"音擎伤害期望对比",
"驱动盘四件套对比",
"APL对比",
"单次失衡爆发对比",
"失衡效率对比",
"异常积蓄效率对比",
]
SC_RANGE_MIN = 0
SC_RANGE_MAX = 75
SC_RANGE_STEP = 1
DEFAULT_SC_RANGE = (0, 75) # 默认模拟词条数范围
DEFAULT_SC_LIST = [] # 默认模拟词条种类
DEFAULT_WEAPON_LIST = [] # 默认武器列表
# UI 相关
TEXT_AREA_HEIGHT = 400
# 结果存储相关
PARALLEL_RUN_PREFIX = "parallel_"
RESULTS_DIR_PREFIX = "./results/"
PARALLEL_CONFIG_SUFFIX = "/.parallel_config.json"
# --- 页面函数 ---
def page_simulator():
"""模拟器页面函数"""
st.title("ZZZ Simulator - 模拟器")
from zsim.define import config_path
# 获取当前计算机的物理核心数量
MAX_WORKERS = psutil.cpu_count(logical=False)
@st.cache_resource
def get_executor():
"""获取进程池执行器"""
return concurrent.futures.ProcessPoolExecutor(max_workers=MAX_WORKERS)
with open(config_path, "r", encoding="utf-8") as f:
config = json.load(f)
default_stop_tick = config["stop_tick"]
@st.fragment
def go_simulator():
"""启动模拟器UI及逻辑"""
# 初始化状态
if "simulation_running" not in st.session_state:
st.session_state["simulation_running"] = False
st.write(
'模拟时长(tick):
',
unsafe_allow_html=True,
)
st.write("")
col1, col2, col3, col4 = st.columns(4)
with col1:
stop_tick = st.number_input(
"模拟时长",
min_value=1,
max_value=65535,
value=default_stop_tick,
key="stop_tick",
help="单位为 tick(帧),1秒 = 60 ticks",
disabled=st.session_state["simulation_running"],
label_visibility="collapsed",
)
if stop_tick != default_stop_tick:
with open(config_path, "r+", encoding="utf-8") as f:
config = json.load(f)
config["stop_tick"] = stop_tick
f.seek(0)
json.dump(config, f, indent=4)
f.truncate()
# 新增:敌人选择器
st.write("")
st.markdown("**敌人配置**")
# 读取当前保存的敌人配置
enemy_selector()
if st.button("保存敌人配置", disabled=st.session_state["simulation_running"]):
save_enemy_selection(st.session_state["enemy_index"], st.session_state["enemy_adjust"])
st.session_state["enemy_config_unsaved"] = False
with col2:
if st.button(
"查看角色配置",
use_container_width=True,
disabled=st.session_state["simulation_running"],
):
name_box = saved_char_config["name_box"]
dialog_character_panels(name_box)
with col3:
@st.dialog("APL选择", width="large")
def go_apl_select():
"""APL选择对话框"""
selected_title: str = apl_selecter()
show_apl_judge_result(selected_title)
if st.button(
"保存APL选择",
use_container_width=True,
):
save_apl_selection(selected_title)
st.rerun()
if st.button(
"APL选择",
use_container_width=True,
disabled=st.session_state["simulation_running"],
):
go_apl_select()
with col4:
@st.dialog("模拟器配置", width="large")
def go_config():
"""模拟器配置对话框"""
with open(config_path, "r", encoding="utf-8") as f:
config = json.load(f)
parallel_cfg = config["parallel_mode"]
default_mode = parallel_cfg["enabled"]
# st.write(parallel_cfg)
mode = st.radio(
"运行模式",
RUN_MODES,
index=default_mode,
horizontal=True,
)
if mode == RUN_MODES[0]: # 单进程
st.write(
'单进程模式下,模拟器将进行单次模拟。角色配置、模拟时长、APL都有专门的配置项管理,你不需要对此模式进行进行额外配置
',
unsafe_allow_html=True,
)
elif mode == RUN_MODES[1]: # 多进程
st.write(
'多进程模式下,模拟器将按照一定规律执行多次模拟,最大可利用的核心数为你的物理核心数。
',
unsafe_allow_html=True,
)
# 调整角色的相对位置
default_adjust_char = int(parallel_cfg["adjust_char"])
adjust_char = st.radio(
"调整角色",
ADJUST_CHAR_OPTIONS,
index=default_adjust_char - 1,
help="以队伍配置中的顺序进行选择",
horizontal=True,
)
# 模拟功能
# TODO 添加额外功能后这里需要default_function
# Determine the default selected function based on config
default_function_index = 0 # Default to the first function
if parallel_cfg["adjust_sc"]["enabled"]:
default_function_index = SIMULATION_FUNCTIONS.index("属性收益曲线")
elif parallel_cfg["adjust_weapon"]["enabled"]:
default_function_index = SIMULATION_FUNCTIONS.index("音擎伤害期望对比")
selected_func = st.radio(
"模拟功能",
SIMULATION_FUNCTIONS,
index=default_function_index,
help="选择模拟功能",
horizontal=True,
)
if selected_func == SIMULATION_FUNCTIONS[0]: # 属性收益曲线
st.write(
'将指定种类的属性按照单个副词条的模型量进行步进增加,分别计算伤害期望
',
unsafe_allow_html=True,
)
# 模拟范围
default_sc_range_cfg = parallel_cfg["adjust_sc"]["sc_range"]
sc_range: tuple[int] = st.slider(
"模拟词条数范围",
min_value=SC_RANGE_MIN,
max_value=SC_RANGE_MAX,
value=default_sc_range_cfg,
step=SC_RANGE_STEP,
)
col_sc_select = st.columns([2, 1])
with col_sc_select[0]:
# 模拟词条种类
default_sc_list_cfg = parallel_cfg["adjust_sc"]["sc_list"] # type: ignore
sc_list = st.multiselect(
"模拟词条种类",
stats_trans_mapping.keys(),
default=default_sc_list_cfg,
help="选择要模拟的词条种类",
)
st.write(
'右侧选择框的解释:勾选:避免稀释或转模产生影响;不勾选:避免异常/直伤占比变化产生影响;你做不到两全其美一般来说,异常角色带精通主词条就去掉掌控的勾,反之亦然
',
unsafe_allow_html=True,
)
with col_sc_select[1]:
# 控制对应词条是否需要移除其他主副词条
st.write(
'清除主副词条开关:
',
unsafe_allow_html=True,
)
tmp_dict = {}
for sc in sc_list:
tmp_dict[sc] = st.checkbox(
f"{sc}",
value=True if sc != "异常掌控" else False,
help="勾选后,将在模拟时移除主副词条,不适用于移除装备后会导致词条本身影响输出占比产生变化的情况,一般情况下,你只需要关注异常角色的异常掌控/异常精通",
)
remove_equip_list = list(
key for key, value in tmp_dict.items() if value
)
if "暴击率" in sc_list or "暴击伤害" in sc_list:
st.warning(
"模拟暴击率/暴击伤害时,建议勾选角色配置中的“使用暴击配平算法”选项",
icon="⚠️",
)
elif selected_func == SIMULATION_FUNCTIONS[1]: # 音擎伤害期望对比
st.write(
'对比不同音擎的伤害期望
',
unsafe_allow_html=True,
)
# 模拟武器列表
default_weapon_list_cfg = parallel_cfg["adjust_weapon"]["weapon_list"]
# Use a list of selectboxes and number inputs for weapon and level
weapon_configs = []
st.write("模拟音擎及等级")
num_weapons = st.number_input(
"音擎数量",
min_value=0,
value=len(default_weapon_list_cfg),
step=1,
)
for i in range(num_weapons):
col_weapon, col_level = st.columns([3, 1])
with col_weapon:
default_weapon_name = (
default_weapon_list_cfg[i]["name"]
if i < len(default_weapon_list_cfg)
and "name" in default_weapon_list_cfg[i]
else weapon_options[0]
)
selected_weapon = st.selectbox(
f"音擎 {i + 1}",
weapon_options,
index=(
weapon_options.index(default_weapon_name)
if default_weapon_name in weapon_options
else 0
),
key=f"weapon_select_{i}",
label_visibility="collapsed",
)
with col_level:
default_weapon_level = (
default_weapon_list_cfg[i]["level"]
if i < len(default_weapon_list_cfg)
and "level" in default_weapon_list_cfg[i]
else 1
)
selected_level = st.number_input(
f"等级 {i + 1}",
min_value=1,
max_value=5,
value=default_weapon_level,
step=1,
key=f"weapon_level_{i}",
label_visibility="collapsed",
)
weapon_configs.append(
{"name": selected_weapon, "level": selected_level}
)
else:
st.error("此功能暂未实现")
if st.button("保存配置"):
mode_bool = True if mode == RUN_MODES[1] else False # 多进程
if mode_bool:
with open(config_path, "r+", encoding="utf-8") as f:
config = json.load(f)
config["parallel_mode"]["enabled"] = mode_bool
config["parallel_mode"]["adjust_char"] = int(adjust_char.split("号")[0])
if selected_func == SIMULATION_FUNCTIONS[0]: # 属性收益曲线
config["parallel_mode"]["adjust_sc"] = {
"enabled": True,
"sc_range": sc_range,
"sc_list": sc_list,
"remove_equip_list": remove_equip_list,
}
config["parallel_mode"]["adjust_weapon"] = {
"enabled": False,
"weapon_list": DEFAULT_WEAPON_LIST,
}
elif selected_func == SIMULATION_FUNCTIONS[1]: # 音擎伤害期望对比
config["parallel_mode"]["adjust_sc"] = {
"enabled": False,
"sc_range": list(DEFAULT_SC_RANGE),
"sc_list": DEFAULT_SC_LIST,
}
config["parallel_mode"]["adjust_weapon"] = {
"enabled": True,
"weapon_list": weapon_configs, # Save the list of dictionaries
}
else:
config["parallel_mode"]["adjust_sc"] = {
"enabled": False,
"sc_range": list(DEFAULT_SC_RANGE),
"sc_list": DEFAULT_SC_LIST,
}
config["parallel_mode"]["adjust_weapon"] = {
"enabled": False,
"weapon_list": DEFAULT_WEAPON_LIST,
}
f.seek(0)
json.dump(config, f, indent=4)
f.truncate()
else:
# 单进程模式
with open(config_path, "r+", encoding="utf-8") as f:
config = json.load(f)
config["parallel_mode"]["enabled"] = mode_bool
f.seek(0)
json.dump(config, f, indent=4)
f.truncate()
st.rerun()
if st.button(
"模拟器配置",
use_container_width=True,
disabled=st.session_state["simulation_running"],
):
go_config()
# 启动模拟后自锁
col1, col2 = st.columns([8, 1])
# 加载config
with open(config_path, "r", encoding="utf-8") as f:
config = json.load(f)
parallel_cfg = config["parallel_mode"]
if not parallel_cfg["enabled"]:
# 单进程模式
with col1:
if (
st.button(
"开始模拟-单进程",
disabled=st.session_state["simulation_running"]
or st.session_state.get("enemy_config_unsaved", False),
type="primary",
)
and not st.session_state["simulation_running"]
):
allow_simulation = show_apl_judge_result()
if not allow_simulation:
st.error("请先完成APL选择和角色配置")
st.stop()
st.session_state["simulation_running"] = True
st.rerun(scope="fragment")
elif not st.session_state["simulation_running"]:
st.stop()
with st.spinner("单进程模拟中,这可能会持续数十秒,请稍候...", show_time=True):
if not NEW_SIM_BOOT:
future = get_executor().submit(go_single_subprocess, stop_tick)
else:
# 使用包装器函数来避免pickle错误
future = get_executor().submit(run_single_simulation, stop_tick)
result = future.result()
st.text_area(
"模拟完成,请前往数据分析查看结果,进程输出:",
result,
height=TEXT_AREA_HEIGHT,
)
st.session_state["simulation_running"] = False
elif parallel_cfg["enabled"]:
# 多进程模式
st.write(
f"多进程模式:模拟{parallel_cfg['adjust_sc']['enabled'] and SIMULATION_FUNCTIONS[0] or '未选择功能'}"
)
with col1:
if st.button(
"开始模拟-多进程",
disabled=st.session_state["simulation_running"]
or st.session_state.get("enemy_config_unsaved", False),
type="primary",
):
allow_simulation = show_apl_judge_result()
if not allow_simulation:
st.error("请先完成APL选择和角色配置")
st.stop()
st.session_state["simulation_running"] = True
st.rerun(scope="fragment")
elif not st.session_state["simulation_running"]:
st.stop()
with st.spinner("多进程模拟中,这可能会持续数十秒,请稍候...", show_time=True):
# 每个进程的参数
run_turn_uuid = PARALLEL_RUN_PREFIX + str(
uuid.uuid4()
) # 为本轮并行运行生成统一的UUID
cfg_dump_dir = RESULTS_DIR_PREFIX + run_turn_uuid + PARALLEL_CONFIG_SUFFIX
os.makedirs(os.path.dirname(cfg_dump_dir), exist_ok=True) # 创建结果文件夹
# 将配置存入结果文件根目录
with open(cfg_dump_dir, "w", encoding="utf-8") as f:
json.dump(parallel_cfg, f, indent=4)
# 启动多进程
if not NEW_SIM_BOOT:
futures = {
get_executor().submit(go_parallel_subprocess, args): i + 1
for i, args in enumerate(
generate_parallel_args(
stop_tick,
parallel_cfg,
run_turn_uuid,
)
)
}
else:
futures = {
get_executor().submit(run_parallel_simulation, args): i + 1
for i, args in enumerate(
generate_parallel_args(
stop_tick,
parallel_cfg,
run_turn_uuid,
)
)
}
# 创建结果容器
result_container = st.container()
# 实时处理完成的任务
for future in concurrent.futures.as_completed(futures):
task_num = futures[future]
try:
result = future.result()
with result_container:
with st.expander(f"进程 {task_num} 输出结果"):
st.code(
result,
language="",
height=TEXT_AREA_HEIGHT,
)
except Exception as e:
with result_container:
st.error(f"进程 {task_num} 执行出错: {str(e)}")
st.session_state["simulation_running"] = False
with col2:
if st.button("重置模拟器", type="primary", use_container_width=True):
st.rerun(scope="fragment")
go_simulator()
page_simulator()
================================================
FILE: zsim/run.py
================================================
import argparse
import subprocess
import sys
from zsim.simulator.config_classes import SimulationConfig as SimCfg
def go_api():
"""启动 FastAPI 服务"""
try:
command = [
sys.executable,
"-m",
"zsim.api",
]
subprocess.run(command)
except Exception as e:
print(f"错误:启动FastAPI失败 - {str(e)}")
sys.exit(1)
def go_webui():
"""启动 Streamlit 服务"""
try:
subprocess.run([sys.executable, "-m", "streamlit", "run", "zsim/webui.py"])
except Exception as e:
print(f"错误:启动Streamlit失败 - {str(e)}")
sys.exit(1)
def go_webview_app():
"""启动 pywebview 应用。"""
try:
# 直接执行 webview_app.py 脚本
subprocess.run([sys.executable, "zsim/webview_app.py"], check=True)
except subprocess.CalledProcessError as e:
print(f"错误:启动 Webview 应用失败 - {e}")
sys.exit(1)
except FileNotFoundError:
print("错误:找不到 zsim/webview_app.py。请确保文件存在。")
sys.exit(1)
except Exception as e:
print(f"错误:启动 Webview 应用时发生未知错误 - {str(e)}")
sys.exit(1)
def go_cli():
"""启动命令行模式,不传递任何参数,用作调试或测试。
Args:
`args (MainArgs)`: 包含传递给 main.py 的参数的对象。
默认为一个空的 MainArgs 对象,表示不传递额外参数。
"""
try:
command = [sys.executable, "zsim/main.py"]
subprocess.run(command)
except Exception as e:
print(f"错误:启动命令行界面失败 - {str(e)}")
sys.exit(1)
def go_single_subprocess(stop_tick: int):
"""启动单个子进程"""
try:
results = []
command = [sys.executable, "zsim/main.py", "--stop-tick", str(stop_tick)]
proc = subprocess.run(command, capture_output=True, text=True)
results.append(proc.stdout.strip())
return "\n".join(results)
except Exception as e:
return f"错误:启动子进程失败 - {str(e)}"
def go_parallel_subprocess(sim_cfg: SimCfg):
"""根据提供的 SimCfg 对象启动并行模式子进程。
注意:此函数会强制将 `mode` 参数设置为 `parallel`。
Args:
`sim_cfg (SimCfg)`: 包含传递给 main.py 的参数的对象。
其中的 `mode` 参数会被忽略并强制设为 `parallel`。
"""
try:
command = [sys.executable, "zsim/main.py"]
# 强制设置 mode 为 parallel,即使 args 中有其他值
args_dict = sim_cfg.model_dump(exclude_none=True)
args_dict["mode"] = "parallel" # 确保 mode 是 parallel
for field, value in args_dict.items():
if field == "mode" and value != "parallel": # 跳过非 parallel 的 mode
continue
if isinstance(value, bool):
if value:
command.append(f"--{field.replace('_', '-')}")
else:
command.extend([f"--{field.replace('_', '-')}", str(value)])
# 注意:并行模式可能需要更复杂的处理
proc = subprocess.run(command, capture_output=True, text=True, check=True)
return proc.stdout.strip()
except subprocess.CalledProcessError as e:
return f"错误:启动子进程失败 - {e.stderr}"
except Exception as e:
return f"错误:启动子进程失败 - {str(e)}"
def go_help():
"""显示帮助信息"""
print("ZZZ模拟器")
print("命令列表:")
print(" run: 启动 Streamlit WebUI (浏览器)")
print(" app: 启动桌面应用 (Webview)")
print(" c: 使用 main.py 运行命令行模拟")
confirm_launch()
def confirm_launch():
"""交互式确认启动"""
CHOICES = {
"run": go_webui,
"app": go_webview_app,
"c": go_cli,
"api": go_api,
"help": go_help,
}
choice = input(
"输入 run 启动 WebUI, 输入 app 启动桌面应用, 输入 api 启动API服务, 输入 c 运行命令行:"
).lower()
if choice in CHOICES.keys():
CHOICES[choice]()
else:
print("操作已取消")
sys.exit(0)
def main():
parser = argparse.ArgumentParser(description="ZZZ Simulator")
parser.add_argument(
"command",
nargs="?",
default=None,
help="子命令(例如:run, app, c, api)",
choices=["run", "app", "c", "api", None],
)
args = parser.parse_args()
if args.command == "run":
go_webui()
elif args.command == "app":
go_webview_app()
elif args.command == "c":
go_cli()
elif args.command == "api":
go_api()
else:
print("ZZZ模拟器\n")
confirm_launch()
if __name__ == "__main__":
main()
================================================
FILE: zsim/script/APLSpawner/APLDesigner.py
================================================
import gradio as gr
from components.SortableRow import SortableRow
def create_next_step_interface():
with gr.TabItem("下一步操作", id=1, visible=False) as tab:
with gr.Column():
gr.Markdown("## 动态横条配置")
# 存储横条数据
rows_state = gr.State(value=[])
# 横条容器
rows_container = gr.HTML()
# 控制按钮
with gr.Row():
add_btn = gr.Button("➕ 添加新横条")
save_btn = gr.Button("💾 保存配置")
# 添加初始化横条
initial_row = SortableRow(1, ["选项A", "选项B"])
rows_container = initial_row.row
def add_row(rows):
new_id = len(rows) + 1
new_row = SortableRow(new_id, ["选项A", "选项B"])
return new_row.row
add_btn.click(fn=add_row, inputs=rows_state, outputs=rows_container)
return tab, add_btn, save_btn, rows_state, rows_container
================================================
FILE: zsim/script/APLSpawner/Spawner.py
================================================
import dash
import dash_bootstrap_components as dbc
import pandas as pd
from dash import Dash, Input, Output, dcc, html
from zsim.define import CHARACTER_DATA_PATH
# 初始化角色数据
char_data = pd.read_csv(CHARACTER_DATA_PATH)
full_char_name_list = char_data["name"].tolist()
full_char_cid_list = char_data["CID"].tolist()
full_char_dict = {
name: cid for name, cid in zip(full_char_name_list, full_char_cid_list, strict=False)
}
class Spawner:
def __init__(self):
self.full_char_dict = full_char_dict
spawner_data = Spawner()
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = html.Div(
[
dcc.Tabs(
id="tbas-container",
value="char-select-tab",
children=[
dcc.Tab(
label="角色选择",
value="char-select-tab",
children=[
dbc.Row(
[
dbc.Col(
dcc.Dropdown(
id="char-select-box-1",
options=[
{"label": t[0], "value": t[1]}
for t in spawner_data.full_char_dict.items()
],
),
md=4, # 每列占4格(Bootstrap共12格)
),
dbc.Col(
dcc.Dropdown(
id="char-select-box-2",
options=[
{"label": t[0], "value": t[1]}
for t in spawner_data.full_char_dict.items()
],
),
md=4, # 每列占4格(Bootstrap共12格)
),
dbc.Col(
dcc.Dropdown(
id="char-select-box-3",
options=[
{"label": t[0], "value": t[1]}
for t in spawner_data.full_char_dict.items()
],
),
md=4,
),
]
),
# 第一行 三个角色选择框结束,第二行开始
dbc.Button(),
],
)
],
)
]
)
@app.callback(
[
Output(component_id="char-select-box-1", component_property="options"),
Output(component_id="char-select-box-2", component_property="options"),
Output(component_id="char-select-box-3", component_property="options"),
],
[
Input(component_id="char-select-box-1", component_property="value"),
Input(component_id="char-select-box-2", component_property="value"),
Input(component_id="char-select-box-3", component_property="value"),
],
prevent_initial_call=True,
)
def update_dropdown(char_1, char_2, char_3):
"""
回调1:角色选择界面的下拉菜单回调。
根据在下拉菜单中选择的角色,修改其他菜单的内容。
"""
"""第一步:锁定触发会调函数的源头"""
ctx = dash.callback_context
"""callback_context解释:
dash.callback_context输出的内容如下,根据AI的介绍,它输出的应该是JSON格式:
{
"triggered": [
{
"prop_id": "char-select-box_1.value", // 触发源组件ID及属性
"value": "1001", // 触发时的属性值
"triggered": true // 是否实际触发
},
{
"prop_id": ".", // 其他可能存在的非主动触发项
"value": null,
"triggered": false
}]
}
所以才需要下面代码的格式整理,最终获取triggered_id
"""
if not ctx.triggered:
raise dash.exceptions.PreventUpdate
selected = {val for val in [char_1, char_2, char_3] if val is not None}
triggered_id = ctx.triggered[0]["prop_id"].split(".")[0]
filtered_options = []
for name, cid in spawner_data.full_char_dict.items():
if cid not in selected:
filtered_options.append({"label": name, "value": cid})
else:
filtered_options.append({"label": f"{name} (已选)", "value": cid, "disabled": True})
# 仅更新触发源的下拉框
if triggered_id == "char-select-box-1":
return filtered_options, dash.no_update, dash.no_update
elif triggered_id == "char-select-box-2":
return dash.no_update, filtered_options, dash.no_update
elif triggered_id == "char-select-box-3":
return dash.no_update, dash.no_update, filtered_options
else:
raise ValueError("触发源ID错误!")
@app.callback()
def select_char_complete():
"""
回调2:锁定选择按钮回调函数,
主要是判定角色是否完成选择,如果完成选择,那么就开放新的标签页。
"""
pass
if __name__ == "__main__":
app.run_server(debug=True)
================================================
FILE: zsim/script/APLSpawner/__init__.py
================================================
================================================
FILE: zsim/script/APLSpawner/components/SortableRow.py
================================================
# components/SortableRow.py
import gradio as gr
class SortableRow:
def __init__(self, row_id, options):
self.id = row_id
with gr.Row(elem_classes="sortable-row") as self.row:
self.handle = gr.HTML('☰
')
self.dropdown = gr.Dropdown(options, label=f"选项 {row_id}")
self.delete_btn = gr.Button("❌", elem_classes="delete-btn")
# 删除事件
self.delete_btn.click(fn=lambda: None, inputs=None, outputs=None)
================================================
FILE: zsim/script/code_line.py
================================================
import os
def count_effective_lines(directory):
total_lines = 0
for root, dirs, files in os.walk(directory):
# 排除 .venv 目录
if ".venv" in dirs:
dirs.remove(".venv")
for file in files:
if file.endswith((".py", ".txt", ".c", ".cpp", ".h")):
file_path = os.path.join(root, file)
with open(file_path, "r", encoding="utf-8") as f:
lines = f.readlines()
# 根据文件类型过滤掉空行和注释行
effective_lines: list[str] = []
if file.endswith(".py") or file.endswith(".txt"):
effective_lines = [
line
for line in lines
if line.strip() and not line.strip().startswith("#")
]
elif file.endswith(".c") or file.endswith(".cpp") or file.endswith(".h"):
effective_lines = [
line
for line in lines
if line.strip()
and not line.strip().startswith("//")
and not line.strip().startswith("/*")
]
if effective_lines:
total_lines += len(effective_lines)
return total_lines
def count_total_lines(directory):
total_lines = 0
for root, dirs, files in os.walk(directory):
# 排除 .venv 目录
if ".venv" in dirs:
dirs.remove(".venv")
for file in files:
if file.endswith((".py", ".txt", ".c", ".cpp", ".h")):
file_path = os.path.join(root, file)
with open(file_path, "r", encoding="utf-8") as f:
lines = f.readlines()
total_lines += len(lines)
return total_lines
if __name__ == "__main__":
# 指定目录
directory = "./"
# 计算有效代码行数
effective_lines = count_effective_lines(directory)
print(f"有效代码行数: {effective_lines}")
# 计算总代码行数
total_lines = count_total_lines(directory)
print(f"总代码行数: {total_lines}")
================================================
FILE: zsim/script/del_all_pycache.py
================================================
# 删除指定目录与所有子目录下的__pycache__文件夹
import os
import shutil
def remove_pycache(directory):
for root, dirs, files in os.walk(directory):
print(f"Scanning: {root}")
for dir_name in dirs:
if dir_name == "__pycache__":
pycache_path = os.path.join(root, dir_name)
shutil.rmtree(pycache_path)
print(f"Removed: {pycache_path}")
if __name__ == "__main__":
directory_to_clean = "./"
remove_pycache(directory_to_clean)
================================================
FILE: zsim/script/draw_anomaly_timeline.py
================================================
#!/usr/bin/env python3
"""
绘制异常轴图的脚本。
该脚本读取本地results目录下的damage.csv文件,并根据其中的信息绘制一个透明背景的异常轴图。
启动命令:python zsim/script/draw_anomaly_timeline.py results/359
"""
import argparse
import os
import sys
try:
import pandas as pd
import plotly.express as px
PLOTLY_AVAILABLE = True
except ImportError:
PLOTLY_AVAILABLE = False
print("错误: 缺少必要的依赖包。请安装 pandas 和 plotly:")
print(" pip install pandas plotly")
sys.exit(1)
def find_consecutive_true_ranges(df, column):
"""查找DataFrame列中连续为True的范围。
Args:
df (pd.DataFrame): 输入的DataFrame,需要包含 'tick' 列。
column (str): 要查找的布尔列名。
Returns:
list[tuple[int, int]]: 一个包含 (开始tick, 结束tick) 元组的列表。
"""
ranges = []
start = None
# 获取tick列和指定列的值
ticks = df["tick"].tolist()
values = df[column].tolist()
for i, (tick, value) in enumerate(zip(ticks, values, strict=False)):
if value:
if start is None:
start = tick
else:
if start is not None:
# 结束tick应该是上一个为True的tick
prev_tick = ticks[i - 1] if i > 0 else start
ranges.append((start, prev_tick))
start = None
# 处理最后一个区间(如果存在)
if start is not None:
ranges.append((start, ticks[-1]))
return ranges
def prepare_timeline_data(df):
"""准备用于绘制异常状态时间线的数据。
Args:
df (pd.DataFrame): 原始伤害数据。
Returns:
tuple: (用于绘制Gantt图的DataFrame, 每种异常状态的平均持续时间字典)
"""
required_columns = [
"冻结",
"霜寒",
"畏缩",
"感电",
"灼烧",
"侵蚀",
"烈霜霜寒",
"tick",
]
missing_cols = [col for col in required_columns if col not in df.columns]
if missing_cols:
raise ValueError(f"输入数据缺少必要的列: {missing_cols}")
columns_to_check = ["冻结", "霜寒", "畏缩", "感电", "灼烧", "侵蚀", "烈霜霜寒"]
gantt_data = []
duration_stats = {}
for col in columns_to_check:
if col in df.columns:
ranges = find_consecutive_true_ranges(df, col)
durations = [end - start + 1 for start, end in ranges] # 持续时间包含首尾
duration_stats[col] = durations
for start, end in ranges:
gantt_data.append({"Task": col, "Start": start, "Finish": end})
if not gantt_data:
return pd.DataFrame(), {}
gantt_df = pd.DataFrame(gantt_data)
gantt_df["Duration"] = gantt_df["Finish"] - gantt_df["Start"] + 1 # 持续时间包含首尾
# 计算每种异常状态的平均持续时间
avg_durations = {}
for col, durations in duration_stats.items():
if durations:
avg_durations[col] = sum(durations) / len(durations)
else:
avg_durations[col] = 0
return gantt_df, avg_durations
def draw_anomaly_timeline(gantt_df, output_path=None):
"""绘制异常状态时间线(Gantt图)。
Args:
gantt_df (pd.DataFrame): 用于绘制Gantt图的数据。
output_path (str, optional): 输出图片文件路径(例如 output.png)。
"""
if gantt_df is not None and len(gantt_df) > 0:
fig = px.bar(
gantt_df,
x="Duration",
y="Task",
base="Start",
orientation="h",
labels={
"Start": "开始时间(帧)",
"Duration": "持续时间(帧)",
"Task": "状态类型",
},
height=350,
)
# 设置透明背景
fig.update_layout(
plot_bgcolor="rgba(0,0,0,0)",
paper_bgcolor="rgba(0,0,0,0)",
)
# 设置网格线颜色
fig.update_xaxes(showgrid=True, gridwidth=3, gridcolor="rgba(128,128,128,0.2)")
fig.update_yaxes(showgrid=True, gridwidth=3, gridcolor="rgba(128,128,128,0.2)")
if output_path:
# 保存为PNG图片
try:
fig.write_image(output_path, width=1200, height=300)
print(f"异常轴图已保存至: {output_path}")
except ValueError as e:
if "kaleido" in str(e).lower():
print("错误: 缺少kaleido包,无法保存图片。请安装kaleido:")
print(" pip install kaleido")
print("或者只在浏览器中查看图表,不使用 -o 参数")
sys.exit(1)
else:
raise e
else:
# 在浏览器中显示
fig.show()
else:
print("没有找到任何连续的状态数据")
def main():
parser = argparse.ArgumentParser(description="绘制异常轴图")
parser.add_argument("result_dir", help="战斗日志所在的结果目录,例如 results/123")
parser.add_argument("-o", "--output", help="输出图片文件路径(例如 output.png)")
args = parser.parse_args()
# 如果指定了输出路径,检查kaleido是否可用
if args.output:
try:
import plotly.io as pio
# 尝试导入kaleido
pio.kaleido.scope
except ImportError:
print("警告: 如果要保存图片,请安装kaleido:")
print(" pip install kaleido")
print("否则请只在浏览器中查看图表,不使用 -o 参数")
# 构建damage.csv文件路径
damage_csv_path = os.path.join(args.result_dir, "damage.csv")
# 检查文件是否存在
if not os.path.exists(damage_csv_path):
print(f"错误: 文件 {damage_csv_path} 不存在")
sys.exit(1)
# 读取damage.csv文件
try:
df = pd.read_csv(damage_csv_path)
print(f"成功读取文件: {damage_csv_path}")
except Exception as e:
print(f"读取文件时出错: {e}")
sys.exit(1)
# 准备数据
try:
gantt_df, avg_durations = prepare_timeline_data(df)
print("数据准备完成")
except Exception as e:
print(f"准备数据时出错: {e}")
sys.exit(1)
# 输出每种异常状态的平均持续时间
print("\n各异常状态的平均持续时间:")
print("-" * 30)
for anomaly, avg_duration in avg_durations.items():
if avg_duration == 0:
continue
print(f"{anomaly}: {avg_duration / 60:.2f} 秒")
print("-" * 30)
# 绘制图表
try:
draw_anomaly_timeline(gantt_df, args.output)
print("异常轴图绘制完成")
except Exception as e:
print(f"绘制图表时出错: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
================================================
FILE: zsim/setup.py
================================================
from setuptools import Extension, setup
module_LinkedList = Extension(
"sim_progress.data_struct.LinkedList",
sources=[r"./sim_progress/data_struct/LinkedList.c"],
)
module_ActionStack = Extension(
"sim_progress.data_struct.ActionStack",
sources=[r"./sim_progress/data_struct/ActionStack.cpp"],
)
setup(
name="LinkedList",
version="1.0",
description="A simple linked list extension module",
ext_modules=[module_LinkedList],
)
setup(
name="ActionStack",
version="1.0",
description="A simple action stack extension module",
ext_modules=[module_ActionStack],
)
================================================
FILE: zsim/sim_progress/Buff/Buff0Manager/Buff0ManagerClass.py
================================================
import copy
import itertools
from collections import defaultdict
from typing import TYPE_CHECKING
import pandas as pd
from zsim.define import (
BUFF_0_REPORT,
CHARACTER_DATA_PATH,
EXIST_FILE_PATH,
JUDGE_FILE_PATH,
saved_char_config,
)
from .. import JudgeTools
from ..buff_class import Buff
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
class Buff0Manager:
def __init__(
self,
name_box: list[str],
judge_list_set: list[list[str]],
weapon_dict: dict[str, list],
cinema_dict: dict,
char_obj_dict: dict | None,
sim_instance: "Simulator | None",
):
# 加载文件
self.EXIST_FILE = pd.read_csv(EXIST_FILE_PATH, index_col="BuffName")
self.JUDGE_FILE = pd.read_csv(JUDGE_FILE_PATH, index_col="BuffName")
self.CHARACTER_FILE = pd.read_csv(CHARACTER_DATA_PATH, index_col="name")
self.sim_instance: "Simulator" = sim_instance
self.judge_list_set = judge_list_set
self.weapon_dict = weapon_dict
self.cinema_dict = cinema_dict
self.char_name_box = name_box # 角色名列表
self.name_order_box = self.change_name_box() # 角色名顺序字典
self.char_obj_dict = char_obj_dict
self.buff_info_inventory: dict[str, dict[str, tuple[dict, dict]]] = defaultdict(dict)
# 设置初始值和数据预处理
self.allbuff_list = self.EXIST_FILE.index.tolist() # 将索引列转为列表
self.buff_name_box: dict[str, list[str]] = {}
# 初始化exist_buff_dict
self.exist_buff_dict: dict[str, dict[str, Buff]] = {"enemy": {}}
for _char_name in self.char_name_box:
self.exist_buff_dict[_char_name] = {}
# 把处理judge_list_set
self.__equip_set2_box = []
self.char_equip_info: dict[str, dict[str, str]] = {}
self.__process_judge_list_set()
self.total_judge_condition_list = (
list(itertools.chain.from_iterable(self.judge_list_set)) + self.__equip_set2_box
)
self.__selector = self.__selector(self)
self.__selector.select_buff_into_exist_buff_dict()
self.__passively_updating_change()
# self.__process_label()
"""
由于deepcopy存在问题,导致buff_0这里初始化好的only_active_by参数在后续复制的过程中可能丢失,
所以这里索性不做处理,直接到data_struct中现场处理。
"""
self.__process_additional_ability_data()
# self.initialize_buff_listener()
if BUFF_0_REPORT:
print(self)
def __str__(self):
output = ""
for _char_name, _sub_dict in self.exist_buff_dict.items():
output += f"本次模拟中{_char_name}可能吃到的Buff为:\n"
for _i in _sub_dict.values():
output += f" {_i.ft.index}\n"
return output
def initialize_buff_listener(self):
"""处理buff监听器的初始化"""
for _char_name, _sub_dict in self.exist_buff_dict.items():
for _buff_0 in _sub_dict.values():
if not isinstance(_buff_0, Buff):
raise TypeError(f"存在非Buff类型的对象:{_buff_0}")
if _buff_0.ft.listener_id is not None:
_obj = (
self.char_obj_dict[_buff_0.ft.operator]
if _buff_0.ft.operator != "enemy"
else self.sim_instance.schedule_data.enemy
)
self.sim_instance.listener_manager.listener_factory(
listener_owner=_obj,
initiate_signal=_buff_0.ft.listener_id,
sim_instance=self.sim_instance,
)
def __process_label(self):
"""处理label类型的内容"""
for _char_name, _dict in self.exist_buff_dict.items():
for _index, _buff_0 in _dict.items():
if not _buff_0.ft.label:
continue
if (
"only_active_by" in _buff_0.ft.label
and _buff_0.ft.label["only_active_by"][0] == "self"
):
char_obj = JudgeTools.find_char_from_name(
_buff_0.ft.operator, sim_instance=self.sim_instance
)
_buff_0.ft.label["only_active_by"] = [char_obj.CID]
def __process_judge_list_set(self):
"""将judge_list_set中的信息全部处理到self.char_equip_info 中"""
for _sub_list in self.judge_list_set:
_name = _sub_list[0]
_weapon = _sub_list[1]
_equip_set4 = _sub_list[2]
_equip_set2_a = _sub_list[3]
if saved_char_config[_name]["equip_style"] == "4+2":
self.char_equip_info[_name] = {
"weapon": _weapon,
"equip_set4": _equip_set4,
"equip_set2_a": _equip_set2_a,
"equip_style": "4+2",
}
elif saved_char_config[_name]["equip_style"] == "2+2+2":
_equip_set2_b = saved_char_config[_name]["equip_set2_b"]
_equip_set2_c = saved_char_config[_name]["equip_set2_c"]
self.char_equip_info[_name] = {
"weapon": _weapon,
"equip_set4": None,
"equip_set2_a": _equip_set2_a,
"equip_set2_b": _equip_set2_b,
"equip_set2_c": _equip_set2_c,
"equip_style": "2+2+2",
}
self.__equip_set2_box.append(_equip_set2_b)
self.__equip_set2_box.append(_equip_set2_c)
else:
raise ValueError("无法解析的驱动盘装备策略!")
def __process_additional_ability_data(self):
"""修改角色对象的组队被动激活参数"""
for _char_name, sub_exist_dict in self.exist_buff_dict.items():
if _char_name == "enemy":
continue
char_instance = self.char_obj_dict[_char_name]
for _buff_index, _buff_instance in sub_exist_dict.items():
if (
_buff_instance.ft.is_additional_ability
and _char_name == _buff_instance.ft.bufffrom
):
char_instance.additional_abililty_active = True
break
else:
char_instance.additional_abililty_active = False
# print(char_instance.NAME, char_instance.additional_abililty_active)
def change_name_box(self):
"""
生成每个角色对应的namebox列表,以自己作为第0位角色。
举例:
name_box = [苍角,艾莲,莱卡恩]
name_order_dict = {
苍角:[苍角,艾莲,莱卡恩,enemy],
艾莲:[艾莲,莱卡恩,苍角,enemy],
莱卡恩:[莱卡恩,苍角,艾莲,enemy]
}
"""
name_box = self.char_name_box
output_name_dict = {}
for i in range(len(name_box)):
new_name_box = name_box[i:] + name_box[:i]
output_name_dict[name_box[i]] = new_name_box + ["enemy"]
return output_name_dict
def __passively_updating_change(self):
"""完成初始化之前,进行最后的参数调整。"""
for char_name, sub_buff_dict in self.exist_buff_dict.items():
for _buff_0 in sub_buff_dict.values():
if not isinstance(_buff_0, Buff):
raise TypeError
if char_name == "enemy":
_buff_0.ft.passively_updating = False
else:
if _buff_0.ft.operator != char_name:
_buff_0.ft.passively_updating = True
else:
_buff_0.ft.passively_updating = False
def search_equipper(self, equipment: str) -> str | None:
"""
根据输入的装备名称寻找装备者
临时使用,该函数只能返回找到的第一个,
buff系统重构后将重写此逻辑
"""
for _char_name, sub_list in self.weapon_dict.items():
if sub_list[0] == equipment:
return _char_name
else:
return None
class __selector:
def __init__(self, buff_0_manager_instance):
self.buff_0_manager: Buff0Manager = buff_0_manager_instance
self.__select_strategy_map = {
"char_handler": None,
"weapon_handler": None,
"drive_handler": None,
}
self.__special_set2_dict = {"如影相随": ["Buff-驱动盘-如影相随-二件套"]}
self.__buff_0_pool: dict[str, tuple] = {}
self.__additional_ability_data = self.__additional_ability_data(self.buff_0_manager)
self.__get_buff_0_pool()
class __additional_ability_data:
"""组队被动数据,主要负责组队被动激活相关"""
def __init__(self, buff_0_manager_instance):
self.buff_0_manager: Buff0Manager = buff_0_manager_instance
self.condition_list = [
"角色属性-中文",
"角色阵营",
"角色特性",
"支援类型",
]
self.additional_ability_judge_info = {}
self.__get_additional_ability_judge_info()
# print(self.additional_ability_judge_info)
def __get_additional_ability_judge_info(self):
for _char_name in self.buff_0_manager.char_name_box:
sub_info_list = []
self.additional_ability_judge_info[_char_name] = {}
self.additional_ability_judge_info[_char_name]["required_condition"] = (
self.buff_0_manager.CHARACTER_FILE.loc[_char_name, "组队被动条件"]
)
for condition in self.condition_list:
sub_info_list.append(
self.buff_0_manager.CHARACTER_FILE.loc[_char_name, condition]
)
self.additional_ability_judge_info[_char_name]["config_info"] = sub_info_list
def addition_skill_info_trans(self, buff_from: str):
"""
前置函数的初始化会将组队被动的激活条件原封不动地放入字典中(包含|分隔符的字符串)
此函数是将字符串根据分隔符分割成list,并且根据具体内容进行翻译,
最后输出的是翻译后的list。
例如:苍角的组队被动激活条件是‘同属性|同阵营’
那么翻译过后就会输出:
['冰', '对空6课']
"""
addition_skill_info = self.additional_ability_judge_info[buff_from]
required_condition_list = addition_skill_info["required_condition"].split("|")
condition_list_after_trans = []
for conditions in required_condition_list:
if conditions == "同阵营":
condition_list_after_trans.append(addition_skill_info["config_info"][1])
elif conditions == "同属性":
condition_list_after_trans.append(addition_skill_info["config_info"][0])
elif conditions in ["异常", "强攻", "支援", "击破", "防护", "命破"]:
condition_list_after_trans.append(conditions)
elif conditions in ["招架", "回避"]:
condition_list_after_trans.append(conditions)
else:
raise ValueError(f"无法解析的组队被动激活条件:{conditions}")
return condition_list_after_trans
def __get_buff_0_pool(self):
"""筛选出总的buff_0池子。"""
# 遍历 EXIST_FILE,按条件筛选
for buff_name, row_data in self.buff_0_manager.EXIST_FILE.iterrows():
# 判断是否符合所有筛选条件
buff_from = row_data["from"]
if row_data["is_weapon"]:
"""武器Buff"""
for charname in self.buff_0_manager.weapon_dict:
if (
buff_from == self.buff_0_manager.weapon_dict[charname][0]
and row_data["refinement"]
== self.buff_0_manager.weapon_dict[charname][1]
):
self.select_buffs(buff_name, row_data)
elif row_data["is_cinema"] and buff_from in self.buff_0_manager.cinema_dict.keys():
"""影画Buff"""
if row_data["refinement"] <= self.buff_0_manager.cinema_dict[buff_from]:
self.select_buffs(buff_name, row_data)
else:
"""角色Buff 和 Enemy"""
if buff_from in self.buff_0_manager.weapon_dict:
"""组队被动"""
if row_data["is_additional_ability"]:
"""获得【足以使组队被动激活的条件集合】"""
condition_list_after_trans = (
self.__additional_ability_data.addition_skill_info_trans(buff_from)
)
partner_condition_list = []
for (
other_key
) in self.__additional_ability_data.additional_ability_judge_info:
if other_key != buff_from:
partner_condition_list.extend(
self.__additional_ability_data.additional_ability_judge_info[
other_key
]["config_info"]
)
# print(buff_name, condition_list_after_trans, partner_condition_list)
for conditions in condition_list_after_trans:
if conditions in partner_condition_list:
self.select_buffs(buff_name, row_data)
break
else:
if buff_from in self.buff_0_manager.char_name_box:
self.select_buffs(buff_name, row_data)
elif row_data["from"] == "enemy":
self.select_buffs(buff_name, row_data)
else:
if buff_from in self.buff_0_manager.total_judge_condition_list:
self.select_buffs(buff_name, row_data)
def select_buffs(self, buff_name, row_data):
"""
根据buffname为索引,去dataframe中寻找对应的judge,并且和输入的rowdata打包进入selected buffs
"""
judge_row_data = self.buff_0_manager.JUDGE_FILE.loc[buff_name]
row_data["BuffName"] = buff_name
self.__buff_0_pool[buff_name] = (row_data, judge_row_data)
def select_buff_into_exist_buff_dict(self):
for buff_name, buff_info_tuple in self.__buff_0_pool.items():
buff_from = buff_info_tuple[0]["from"]
try:
adding_code = str(int(buff_info_tuple[0]["add_buff_to"])).zfill(4)
except ValueError:
raise ValueError(f"{buff_name}的 add_buff_to 参数填写不完整!")
if buff_from in self.buff_0_manager.char_name_box:
"""如果buff来自于角色,那么buff_from就一定指向这个buff的真正来源,也就是buff的拥有者(并非buff的受益者)"""
current_name_box = self.buff_0_manager.name_order_box[buff_from]
selected_characters = [
current_name_box[i]
for i in range(len(current_name_box))
if adding_code[i] == "1"
]
if buff_from not in selected_characters:
selected_characters.append(buff_from)
for _name in selected_characters:
self.initiate_buff(buff_info_tuple, buff_name, _name, buff_from)
elif buff_from == "enemy":
"""
进入这一分支的所有buff实际上都是环境或是其他原因而强加给enemy的,
由于buffload函数并不会以“enemy”为主视角来判定buff,
所有添加给enemy的buff都是在buffload遍历其他角色时产生、或是其他阶段强行添加的,
所以,此处的buff_orner参数传入并不严格,因为用不到。
"""
self.initiate_buff(buff_info_tuple, buff_name, "enemy", "enemy")
elif buff_from in self.buff_0_manager.total_judge_condition_list:
"""
如果buff不属于角色和enemy,那么buff肯定来自装备。
"""
for (
char_name,
_sub_equip_info_dict,
) in self.buff_0_manager.char_equip_info.items():
if buff_from in _sub_equip_info_dict.values():
self.processor_equipment_buff(
adding_code, buff_info_tuple, buff_name, char_name
)
def initiate_buff(self, buff_info_tuple, buff_name, benifiter, buff_orner):
"""
参数中的benifiter和orner不是一个名字。benifiter是buff的受益者,但并不一定是触发buff的角色。
而buff_orner是触发buff者,哪怕这个buff是加给别人的,作为触发者,它的exist_buff_dict中也应该保留这个buff,
这样,在BuffLoad函数对buff_0进行判断时,就可以通过buff.ft.passively_updating参数来避开不必要的判断了。
"""
dict_1 = copy.deepcopy(buff_info_tuple[0]) # 创建 dict_1 的副本
dict_2 = copy.deepcopy(buff_info_tuple[1]) # 创建 dict_2 的副本
dict_1["operator"] = buff_orner
if benifiter == buff_orner:
dict_1["passively_updating"] = False
else:
dict_1["passively_updating"] = True
buff_new = Buff(dict_1, dict_2, sim_instance=self.buff_0_manager.sim_instance)
buff_new.ft.beneficiary = benifiter
self.buff_0_manager.exist_buff_dict[benifiter][buff_name] = buff_new
# self.buff_0_manager.buff_info_inventory[benifiter][buff_name] = (dict_1, dict_2)
def processor_equipment_buff(
self, adding_code, buff_info_tuple, buff_name, equipment_carrier
):
"""处理来源于装备的Buff"""
""" 为了防止只佩戴了二件套的情况下,筛选出了拥有相同buff_from的四件套效果,之类需要进行额外的判断。"""
current_name_box = self.buff_0_manager.name_order_box[equipment_carrier]
personal_equip_dict = self.buff_0_manager.char_equip_info[equipment_carrier]
buff_from = buff_info_tuple[0]["from"]
if personal_equip_dict["equip_style"] == "4+2":
if (
personal_equip_dict["equip_set2_a"] in buff_name
and personal_equip_dict["equip_set2_a"] not in self.__special_set2_dict
):
"""
如果检测到装备者选择配装风格是4+2,并且装备者二件套的名字出现在Buff名中,
就说明是出现了“只佩戴了二件套但是错误地筛选出了四件套的情况”,
此时能够进入这一分支的情况有两种:
1、二件套确实附带了一个Buff——如影2,那么这个Buff就一定在__selector的豁免名单中。
2、二件套并未附带Buff,但是因为和四件套拥有相同的buff_from,所以错误地进入了这个分支,需要处理的,也正是这个分支。
"""
return
elif personal_equip_dict["equip_style"] == "2+2+2":
"""当配装方案是2+2+2时,只要当前任意的二件套处于豁免范围,"""
if "四件套" in buff_name:
return
"""筛除所有和豁免名单无关的套装,无论4、2件套;然后再筛除不在豁免名单上的二件套Buff"""
if not (
buff_from in self.__special_set2_dict
and buff_name in self.__special_set2_dict[buff_from]
):
return
selected_characters = [
current_name_box[i] for i in range(len(current_name_box)) if adding_code[i] == "1"
]
if equipment_carrier not in selected_characters:
selected_characters.append(equipment_carrier)
for _name in selected_characters:
self.initiate_buff(buff_info_tuple, buff_name, _name, equipment_carrier)
def change_name_box(name_box):
"""
生成每个角色对应的namebox列表,以自己作为第0位角色
"""
output_name_dict = {}
for i in range(len(name_box)):
new_name_box = name_box[i:] + name_box[:i]
output_name_dict[name_box[i]] = new_name_box + ["enemy"]
return output_name_dict
================================================
FILE: zsim/sim_progress/Buff/Buff0Manager/__init__.py
================================================
from .Buff0ManagerClass import Buff0Manager, change_name_box
__all__ = ["Buff0Manager", "change_name_box"]
================================================
FILE: zsim/sim_progress/Buff/BuffAdd.py
================================================
from .buff_class import Buff
def buff_add(timenow: float, LOADING_BUFF_DICT: dict, DYNAMIC_BUFF_DICT: dict, enemy):
"""
该函数是Buff三部曲中的最后一步。
它负责把LOADING_BUFF_DICT中的待加载的buff添加到对应角色的Dynamic_Buff_List中,
由于load阶段存在一个小漏洞,导致当前动作中,一些因hit激活的buff,会在start节点漏进来:
前提:LOAD阶段的这个漏洞系底层逻辑缺陷,无法在LOAD阶段自己解决,只能后面的程序进行找补。
Bug成因:
原因是在start那个ticks的BuffLoadLoop函数会因为主要参数对比全部通过,放行这个本不该在start标签处触发的buff。
举例:
这个Buff是普攻触发,那么普攻的第一帧(start),虽然还没有hit,但确实是“正在进行普攻”状态,那么这个buff就是会通过判断。
该buff会顺利进入LOADING_BUFF_DICT从而被ADD函数无脑执行,添加进DYNAMIC_BUFF_DICT中。
但是每个buff在添加进DYNAMIC之前,都要执行buff.update方法来更新自身的动态信息(幸亏我当时很菜,把buff实例化和更新信息分开写了!!)
而该buff会因为当前子任务是start而被归入start分支,从而执行update_cause_start函数,
但是该buff本身是hit更新类型,所以无法进入start函数的任意一个分支,也就无法获得时间等动态信息的有效更新。
也就是说,BuffLoadLoop会放行一些buff,这些buff会被实例化,但是时间、层数等完全不会更新,全部是0,actrive状态也是False。
在下一个tick,这些buff会因为自身endticks小于当前tick,而被Update_Buff函数扫出DYNAMIC_BUFF_DICT
我记得之前遇到过的“灵异事件”,就是在tick=1的时候,会发现出现了一些没有信息更新的buff,它们漏进了Dynamic_buff_dict,但是在tick=2时消失,也正是因为这个原因。
虽然这些漏进去的Buff只会存在一个Ticks,但是也很有可能影响当前tick的计算。
所以,在后续处理DynamicBuffList时,需要schedule阶段多判断一个buff.dy.active 是否是True,如果不是True就不要执行。
或者,在Buff_Add阶段处理,判断一下active或是buff.dy下面的任意属性是否为0即可。
所以,LOADING_BUFF_DICT中只会出现本tick该被添加的buff,将所有buff全部add,将容器清空是这个阶段的核心任务。
所有的buff都会被添加到对应的DYNAMIC_BUFF_DICT中,这样一来,“加buff”这一动作被彻底实现。
"""
for char in LOADING_BUFF_DICT:
if not LOADING_BUFF_DICT[char]:
continue
while LOADING_BUFF_DICT[char]:
buff = LOADING_BUFF_DICT[char].pop()
# for buff in LOADING_BUFF_DICT[char]:
if not isinstance(buff, Buff):
raise ValueError(f"loading_buff_dict中的{buff}元素不是Buff类")
if (
not buff.dy.active
or (buff.dy.startticks == 0 and buff.dy.endticks == 0)
or buff.dy.count == 0
):
# if buff.ft.index == 'Buff-武器-精1燃狱齿轮-叠层冲击力':
# print(f'{buff.dy.active, buff.dy.startticks, buff.dy.endticks, buff.dy.count}')
continue
buff_existing_check = next(
(
existing_buff
for existing_buff in DYNAMIC_BUFF_DICT[char]
if existing_buff.ft.index == buff.ft.index
),
None,
)
# 这个语句的作用是,检查buff是否已经存在。检查的索引是buff.ft.index。
if buff_existing_check:
if buff.ft.alltime:
continue
DYNAMIC_BUFF_DICT[char].remove(buff_existing_check)
# report_to_log(f'[Buff ADD]:{timenow}:{buff_existing_check.ft.name}刷新了')
DYNAMIC_BUFF_DICT[char].append(buff)
add_debuff_to_enemy(buff, char, enemy)
return DYNAMIC_BUFF_DICT
def add_debuff_to_enemy(buff, char, enemy):
if char == "enemy":
debuff_existing_check = next(
(
existing_buff
for existing_buff in enemy.dynamic.dynamic_debuff_list
if existing_buff.ft.index == buff.ft.index
),
None,
)
if debuff_existing_check:
enemy.dynamic.dynamic_debuff_list.remove(debuff_existing_check)
enemy.dynamic.dynamic_debuff_list.append(buff)
# 只有在处理enemy的buff时,需要将改动同时同步到buff中。
# report_to_log(f'[Buff ADD]:{timenow}:{buff.ft.name}第{buff.history.active_times}次触发:endticks:{buff.dy.endticks}')
================================================
FILE: zsim/sim_progress/Buff/BuffAddStrategy.py
================================================
from typing import TYPE_CHECKING
from .buff_class import Buff
if TYPE_CHECKING:
from zsim.sim_progress.Enemy import Enemy
from zsim.simulator.simulator_class import Simulator
def _buff_filter(*args, **kwargs):
buff_name_list: list[str] = []
for arg in args:
if isinstance(arg, str):
buff_name_list.append(arg)
elif isinstance(arg, Buff):
buff_name_list.append(arg.ft.index)
for value in kwargs.values():
if isinstance(value, str):
buff_name_list.append(value)
if isinstance(value, Buff):
buff_name_list.append(value.ft.index)
return buff_name_list
def buff_add_strategy(
*added_buffs: str | Buff,
benifit_list: list[str] | None = None,
specified_count: int | float | None = None,
sim_instance: "Simulator" = None,
):
"""
这个函数是暴力添加buff用的,比如霜寒、畏缩等debuff,
又比如核心被动强行添加buff的行为,都可以通过这个函数来实现。
Args:
added_buffs: str: 需要添加的Buff的index
benifit_list: list[str]: 受益者名单
specified_count: int | float | None: 指定层数,非必要参数
sim_instance: Simulator: 模拟器实例
"""
if sim_instance is None:
raise ValueError("调用buff_add_strategy函数时,sim_instance是None")
buff_name_list: list[str] = _buff_filter(*added_buffs)
all_name_order_box = sim_instance.load_data.all_name_order_box
# name_box = main_module.load_data.name_box
# name_box_now = name_box + ['enemy']
enemy = sim_instance.schedule_data.enemy
exist_buff_dict = sim_instance.load_data.exist_buff_dict
tick = sim_instance.tick
DYNAMIC_BUFF_DICT = sim_instance.global_stats.DYNAMIC_BUFF_DICT
"""
将Buff名称、Buff实例转化为对应的Buff并且添加到DYNAMIC_BUFF_DICT或者其他地方。
是在Load阶段以外暴力互动DYNAMIC_BUFF_DICT的通用方式。
"""
# 对于buff_name_list中的每个Buff都执行一次
for buff_name in buff_name_list:
# FIXME: 这里可能存在Bug,指定受益人(benifit_list)可能与自动查找的逻辑冲突。
selected_characters = confirm_selected_character(
exist_buff_dict, buff_name, all_name_order_box, benifit_list
)
if selected_characters is None:
print(
f"【BuffAddStrategy警告】并未找到适用于{buff_name}的受益人!本次Buff添加将被跳过!"
)
continue
# 针对每位受益人,都执行一次Buff添加
for names in selected_characters:
let_buff_start(
DYNAMIC_BUFF_DICT, buff_name, enemy, exist_buff_dict, names, specified_count, tick
)
# __check_buff_add_result(buff_name, selected_characters, exist_buff_dict, DYNAMIC_BUFF_DICT, sim_instance)
def let_buff_start(
DYNAMIC_BUFF_DICT: dict[str, list[Buff]],
buff_name: str,
enemy: "Enemy",
exist_buff_dict: dict[str, dict[str, Buff]],
names: str,
specified_count: int,
tick: int,
):
"""
这个函数是buff_add_strategy函数的添加Buff的核心业务函数。
Args:
DYNAMIC_BUFF_DICT: dict: 动态Buff字典
buff_name: str: Buff名称
enemy: Character: 敌人
exist_buff_dict: dict: 存在的Buff字典
names: str: 受益者名称
specified_count: int | float | None: 指定层数,非必要参数
tick: int: 当前时间
"""
from copy import deepcopy
# 对于不同的Buff受益人,sub_exist_buff_dict是不同的,需要重新获取
sub_exist_buff_dict = exist_buff_dict[names]
copyed_buff = sub_exist_buff_dict[buff_name]
buff_new = deepcopy(copyed_buff)
buff_new.ft.operator = copyed_buff.ft.operator
buff_new.ft.passively_updating = copyed_buff.ft.passively_updating
buff_new.ft.beneficiary = copyed_buff.ft.beneficiary
# buff_new = Buff.create_new_from_existing(copyed_buff)
if copyed_buff.ft.simple_start_logic and buff_new.ft.simple_effect_logic:
if specified_count is not None:
buff_new.simple_start(
tick,
sub_exist_buff_dict,
specified_count=specified_count,
)
else:
buff_new.simple_start(tick, sub_exist_buff_dict)
elif not copyed_buff.ft.simple_start_logic:
# print(buff_new.ft.index)
buff_new.logic.xstart(benifit=names)
elif not copyed_buff.ft.simple_effect_logic:
# print(buff_new.ft.index)
buff_new.logic.xeffect()
# 更新 DYNAMIC_BUFF_DICT
dynamic_buff_list = DYNAMIC_BUFF_DICT.get(names, [])
buff_existing_check = next(
(
existing_buff
for existing_buff in dynamic_buff_list
if existing_buff.ft.index == buff_new.ft.index
),
None,
)
if buff_existing_check:
dynamic_buff_list.remove(buff_existing_check)
# print(f'强制添加Buff函数执行,本次为 {names} 添加的Buff为:{buff_new.ft.index},激活状态为:{buff_new.dy.active},开始时间为:{buff_new.dy.startticks},结束时间为:{buff_new.dy.endticks},层数:{buff_new.dy.count}')
dynamic_buff_list.append(buff_new)
# 如果是敌人,更新动态 Debuff 列表
if names == "enemy":
enemy_dynamic_debuff_list = enemy.dynamic.dynamic_debuff_list
debuff_existing_check = next(
(
existing_buff
for existing_buff in enemy_dynamic_debuff_list
if existing_buff.ft.index == buff_new.ft.index
),
None,
)
if debuff_existing_check:
enemy_dynamic_debuff_list.remove(debuff_existing_check)
enemy_dynamic_debuff_list.append(buff_new)
def get_selected_character(adding_buff_code, all_name_order_box, copyed_buff):
if copyed_buff.ft.add_buff_to == "0001" or copyed_buff.ft.operator == "enemy":
selected_characters = ["enemy"]
else:
name_box_now = all_name_order_box[copyed_buff.ft.operator]
selected_characters = [
name_box_now[i] for i in range(len(name_box_now)) if adding_buff_code[i] == "1"
]
return selected_characters
def confirm_selected_character(
exist_buff_dict: dict[str, dict[str, Buff]],
buff_name: str,
all_name_order_box: dict[str, list[str]],
benifit_list: list[str] = None,
) -> list[str] | None:
"""
确认选中的角色是否存在。
Args:
exist_buff_dict: dict[str,dict[str,Buff]]: 存在Buff字典
buff_name: str: 即将执行强行添加的Buff名称
all_name_order_box: dict[str, list[str]]: 所有角色的名称列表
benifit_list: list[str]: 外部制定的受益者名单
"""
for char_name, sub_dict in exist_buff_dict.items():
# 首先判断Buff是否在当前检查角色(char_name)的收益列表中
if buff_name not in sub_dict:
continue
selected_buff = sub_dict[buff_name]
# assert isinstance(selected_buff, Buff), "buff_add_strategy函数中,buff_name_list中的元素必须是Buff类"
# 确定本次Buff添加的受益人
adding_buff_code = str(int(selected_buff.ft.add_buff_to)).zfill(4)
selected_characters = (
get_selected_character(adding_buff_code, all_name_order_box, selected_buff)
if benifit_list is None
else benifit_list
)
return selected_characters
else:
return None
def __check_buff_add_result(
buff_name: str,
selected_characters: list[str],
exist_buff_dict: dict[str, dict[str, Buff]],
DYNAMIC_BUFF_DICT: dict[str, list[Buff]],
sim_instance: "Simulator",
):
"""
检查Buff添加结果是否符合预期。
Args:
buff_name: str: 即将执行强行添加的Buff名称
selected_characters: list[str]: 选中的角色列表
exist_buff_dict: dict[str,dict[str,Buff]]: 存在Buff字典
DYNAMIC_BUFF_DICT: dict[str, list[Buff]: 动态Buff字典
"""
tick = sim_instance.tick
for char_name in selected_characters:
sub_list = DYNAMIC_BUFF_DICT[char_name]
for buffs in sub_list:
assert isinstance(buffs, Buff)
buff_0 = exist_buff_dict[char_name][buffs.ft.index]
if buffs.ft.index == buff_name:
if all(
[
buffs.dy.startticks == buff_0.dy.startticks,
buffs.dy.endticks == buff_0.dy.endticks,
buffs.dy.count == buff_0.dy.count,
]
):
print(
f"【BuffAddStrategy检查】{tick}tick:{char_name}成功添加了{buff_name}, 其层数为{buffs.dy.count},{buffs.dy.startticks} - {buffs.dy.endticks}"
)
return
print(f"【BuffAddStrategy检查】{tick}tick:{char_name}未添加{buff_name}")
================================================
FILE: zsim/sim_progress/Buff/BuffLoad.py
================================================
from __future__ import annotations
from typing import TYPE_CHECKING
import numpy as np
import pandas as pd
from zsim.define import (
BUFF_LOADING_CONDITION_TRANSLATION_DICT,
EXIST_FILE_PATH,
JUDGE_FILE_PATH,
)
from zsim.sim_progress.Character.skill_class import Skill
from .buff_class import Buff
if TYPE_CHECKING:
from zsim.sim_progress.Load import LoadingMission
from zsim.simulator.simulator_class import Simulator
EXIST_FILE = pd.read_csv(EXIST_FILE_PATH, index_col="BuffName")
JUDGE_FILE = pd.read_csv(JUDGE_FILE_PATH, index_col="BuffName")
JUDGE_FILE = JUDGE_FILE.replace({np.nan: None})
EXIST_FILE = EXIST_FILE.replace({np.nan: None})
class BuffInitCache:
def __init__(self):
self.cache = {}
def get(self, key):
return self.cache.get(key)
def add(self, key, value):
self.cache[key] = value
max_cache = 128
while len(self.cache) > max_cache:
self.cache.popitem()
def __getitem__(self, key):
return self.cache[key]
class BuffJudgeCache(BuffInitCache):
def __init__(self):
super().__init__()
def process_buff(
buff_0,
sub_exist_buff_dict,
mission,
time_now,
selected_characters,
LOADING_BUFF_DICT,
exist_buff_dict: dict,
sim_instance: "Simulator",
):
"""
该函数是公用的buff逻辑处理函数,主要是通过BuffJudge来判断Buff是否应该触发。
注意,此处的buff_0是operator的buff_0,哪怕buff是要加给别的角色,这里也是operator的buff_0
"""
all_match, judge_condition_dict, active_condition_dict = BuffInitialize(
buff_0.ft.index, sub_exist_buff_dict
)
all_match = BuffJudge(buff_0, judge_condition_dict, mission)
if not all_match:
return
# if not buff_0.ft.is_debuff:
"""
在20241114的更新中,我删除了debuff分支。因为buff的add_buff_to被拓展成了4字段,所以就没有必要判断是否是debuff了
如果一个buff是debuff,那么它的add_buff_to字段的最后一位肯定是1,比如0001,
这样,它就一定会在buff_go_to函数中导致'enemy'字段进入selected_characters列表,这样一来,enemy会被当成正常角色来执行正常的buff添加和update。
"""
for char in selected_characters:
# if buff_0.ft.simple_judge_logic:
if buff_0.ft.simple_effect_logic:
for sub_mission_start_tick, sub_mission in mission.mission_dict.items():
if time_now - 1 < sub_mission_start_tick <= time_now:
"""
筛选出正在发生的子任务,如果子任务正在发生就直接执行update,把子任务的str传进buff.update()函数
并且触发对应的分支(start、hit、end),完成符合buff属性的时间、层数更新。
"""
buff_new = Buff(
active_condition_dict,
judge_condition_dict,
sim_instance=sim_instance,
)
buff_new.update(
char,
time_now,
mission.mission_node.skill.ticks,
sub_exist_buff_dict,
sub_mission,
)
buff_new.ft.operator = buff_0.ft.operator
buff_new.ft.passively_updating = buff_0.ft.passively_updating
buff_new.ft.beneficiary = buff_0.ft.beneficiary
if buff_new.dy.is_changed:
LOADING_BUFF_DICT[char].append(buff_new)
"""
这里要注意:process_buff函数中传入的buff_0,只会来自于角色,
所以,如果有上个Enemy的debuff,那么角色自身作为触发源头,正常更新以外,
需要向Enemy的buff_0同步广播。否则,record就无法记录enemy身上Buff的正常层数。
"""
if char == "enemy":
enemy_buff_0 = exist_buff_dict["enemy"][buff_0.ft.index]
buff_new.update_to_buff_0(enemy_buff_0)
else:
"""
这个分支主要是为了处理复杂的effect类的buff的
此类buff的更新往往不依赖start、hit、end三大子标签进行,
所以单独进行处理
"""
buff_new = Buff(active_condition_dict, judge_condition_dict, sim_instance=sim_instance)
buff_new.logic.xeffect()
if buff_new.dy.is_changed:
buff_new.ft.operator = buff_0.ft.operator
buff_new.ft.passively_updating = buff_0.ft.passively_updating
buff_new.ft.beneficiary = buff_0.ft.beneficiary
LOADING_BUFF_DICT[char].append(buff_new)
if char == "enemy":
enemy_buff_0 = exist_buff_dict["enemy"][buff_0.ft.index]
buff_new.update_to_buff_0(enemy_buff_0)
def BuffLoadLoop(
time_now: int,
load_mission_dict: dict,
existbuff_dict: dict,
character_name_box: list,
LOADING_BUFF_DICT: dict,
all_name_order_box: dict,
sim_instance: "Simulator",
):
"""
这是buff修改三部曲的第二步,也是最核心的一个步骤,
该函数会向外抛出LOADING_BUFF_DICT——本tick触发了多少BUFF/DEBUFF,并且移交给BuffAdd函数,执行buff的添加。
本函数的核心调用函数是ProcessBuff函数。
"""
# 初始化LOADING_BUFF_DICT
from zsim.sim_progress.Load import LoadingMission
all_name_box = character_name_box + ["enemy"]
for character in all_name_box:
LOADING_BUFF_DICT[character] = []
# 遍历load_mission_dict中的任务
for mission in load_mission_dict.values():
if not isinstance(mission, LoadingMission):
raise TypeError(f"当前{mission}不是LoadingMission类!")
actor_name = mission.mission_character
if actor_name not in existbuff_dict:
raise ValueError("当前角色的Buff源并未创建!")
# 提取当前角色的 Buff 列表
# sub_exist_debuff_dict = existbuff_dict['enemy']
for char_name in character_name_box:
sub_exist_buff_dict = existbuff_dict[char_name]
if char_name == actor_name:
process_on_field_buff(
sub_exist_buff_dict,
mission,
time_now,
LOADING_BUFF_DICT,
all_name_order_box,
existbuff_dict,
sim_instance=sim_instance,
)
else:
process_backend_buff(
sub_exist_buff_dict,
all_name_order_box,
mission,
time_now,
LOADING_BUFF_DICT,
existbuff_dict,
sim_instance=sim_instance,
)
return LOADING_BUFF_DICT
def process_on_field_buff(
sub_exist_buff_dict: dict,
mission: "LoadingMission",
time_now: int,
LOADING_BUFF_DICT: dict,
all_name_order_box: dict,
exist_buff_dict: dict,
sim_instance: "Simulator",
):
"""
处理前台Buff的逻辑模块
注意,这部分的分支,指的是以当前的前台角色为第一视角来给自己或是其他人添加Buff。
由于这个循环的前置参数——character_name是从mission里面拿来的,所以“前台角色”不可能是enemy
这意味enemy的所有buff必须是别人添加给它的,目前enemy没有主动更新buff的逻辑。
"""
for buff_key, buff_0 in sub_exist_buff_dict.items():
if not isinstance(buff_0, Buff):
raise TypeError(f"当前{buff_key}不是Buff类!")
if buff_0.ft.schedule_judge:
# 跳过schedule阶段处理的buff
continue
if buff_0.ft.passively_updating:
# 目前正是前台角色触发前台buff,而passively_updating为True时,
# 意味着“当前buff的触发我说了不算,别人说了算”,那么本函数自然无法处理,要直接跳过。
continue
# 提前计算添加Buff的角色列表
main_char = buff_0.ft.operator
all_name_box = all_name_order_box[main_char]
selected_characters = buff_go_to(buff_0, all_name_box)
process_buff(
buff_0,
sub_exist_buff_dict,
mission,
time_now,
selected_characters,
LOADING_BUFF_DICT,
exist_buff_dict,
sim_instance=sim_instance,
)
def process_backend_buff(
sub_exist_buff_dict: dict,
all_name_order_box: dict,
mission: "LoadingMission",
time_now: int,
LOADING_BUFF_DICT: dict,
exist_buff_dict: dict,
sim_instance: "Simulator",
):
"""
处理后台Buff的逻辑,
尽管当前的动作是别的角色(actor ≠ char_name),但是,两位后台角色身上,依旧存在着可能发生更新的Buff
这些Buff都拥有backend_active标签。但并非所有拥有这一标签的buff都应该执行更新。
比如,后台角色A会给所有人叠层,当前台动作满足该Buff的触发条件时,
应只执行该buff所有者(operator)的buff更新,而不执行受益者(beneficiary)的更新,
这样就可以避免buff的重复更新。
以 静听佳音4件套 为例:套装佩戴者位于后台时,如果前台角色使用了快速支援,
那么,在process_on_field_buff函数中,前台角色的嘉音层数不会被更新;
而在此函数中,该buff属于耀佳音的那个buff_0会触发更新,从而实现全队层数+1
"""
for other_buff_key, other_buff_0 in sub_exist_buff_dict.items():
if not isinstance(other_buff_0, Buff):
raise TypeError(f"当前{other_buff_key}不是Buff类!")
if other_buff_0.ft.schedule_judge:
continue
if not other_buff_0.ft.backend_acitve:
continue
if other_buff_0.ft.passively_updating:
continue
main_char = other_buff_0.ft.operator
name_order_box = all_name_order_box[main_char]
selected_characters_back = buff_go_to(other_buff_0, name_order_box)
process_buff(
other_buff_0,
sub_exist_buff_dict,
mission,
time_now,
selected_characters_back,
LOADING_BUFF_DICT,
exist_buff_dict,
sim_instance=sim_instance,
)
def buff_go_to(buff_0, all_name_box):
"""
运行函数前,总有:
all_name_box = character_name_box + ['enemy']
该函数是用来处理buff该加给什么角色的
比如这个buff的add_buff_to字段的内容是1100(加给自己和下一位),那么新的这个selected_characters就会输出[艾莲,莱卡恩]
如果字段内容是1010(加给自己和上一位),那么新的selected_characters就会输出[艾莲,苍角]
"""
adding_code = str(int(buff_0.ft.add_buff_to)).zfill(4)
selected_characters = [
all_name_box[i] for i in range(len(all_name_box)) if adding_code[i] == "1"
]
return selected_characters
def BuffInitialize(
buff_name: str, existbuff_dict: dict, *, cache=BuffInitCache()
) -> tuple[bool, dict, dict]:
cache_key = (buff_name, tuple(existbuff_dict.items()))
if cache_key in cache.cache:
return cache.get(cache_key)
# 对单个buff进行初始化,抛出一个触发状态参数,两个参数序列。
all_match = False
buff_now = existbuff_dict[buff_name]
if not isinstance(buff_now, Buff):
raise ValueError(f"当前正在检索的Buff:{buff_name}并不是Buff类!")
if buff_name not in JUDGE_FILE.index:
raise ValueError(f"Buff{buff_name}不在JUDGE_FILE中!")
judge_condition_dict = dict(JUDGE_FILE.loc[buff_name])
active_condition_dict = dict(EXIST_FILE.loc[buff_name])
active_condition_dict["BuffName"] = buff_name
# 根据buff名称,直接把判断信息从JUDGE_FILE中提出来并且转化成dict。
results = (all_match, judge_condition_dict, active_condition_dict)
cache.add(cache_key, results)
return results
def BuffJudge(
buff_now: Buff,
judge_condition_dict: dict,
mission: "LoadingMission",
*,
cache=BuffJudgeCache(),
) -> bool:
"""
如果judge_condition_dict的全部内容是None,同时buff还是简单判断逻辑
说明是环境或是战斗系统自带的debuff,则直接返回False,跳过判断。
"""
# 以下为缓存逻辑
simple_logic: bool = buff_now.ft.simple_judge_logic
all_simple = [
buff_now.ft.simple_judge_logic,
buff_now.ft.simple_start_logic,
buff_now.ft.simple_hit_logic,
buff_now.ft.simple_end_logic,
buff_now.ft.simple_effect_logic,
buff_now.ft.simple_exit_logic,
]
if all(all_simple):
cache_key = hash((id(buff_now), tuple(judge_condition_dict.items()), id(mission)))
if cache_key in cache.cache:
return cache[cache_key]
result: bool
def save_cache_and_return(result: bool, *, cache=cache):
"""由于本函数有多个return中断,所以写了个这玩意,把直接return换成return这个函数就行"""
if all(all_simple):
cache.add(cache_key, result)
return result
# ——————缓存逻辑结束————————
if buff_now.ft.alltime:
result = True
return save_cache_and_return(result)
if (
not any(value if value is None else True for value in judge_condition_dict.values())
and buff_now.ft.simple_judge_logic
):
# EXPLAIN:全部数据都是None并且是简单判断逻辑
# 这通常意味着Buff的判断不在Load阶段,而是通过某种方式在其他阶段暴力添加。
# 但是部分alltime的buff也会进入这一分支,所以需要在判断alltime之后再进行全空判断。
result = False
return save_cache_and_return(result)
"""
正常buff的判断逻辑
"""
skill_now = mission.mission_node.skill
if not isinstance(skill_now, Skill.InitSkill):
raise TypeError(f"{skill_now}并非Skill类!")
if simple_logic:
all_match = simple_string_judge(judge_condition_dict, skill_now)
else:
try:
all_match = buff_now.logic.xjudge(
loading_mission=mission, skill_node=mission.mission_node
)
except TypeError:
raise TypeError(f"{buff_now.ft.index}的xjudge方法参数错误!")
result = all_match
return save_cache_and_return(result)
def simple_string_judge(judge_condition_dict: dict, skill_now) -> bool:
all_match = True
"""
先假定all_match是True,一会儿循环过程中一旦有不符合的项,就改成False。
只有全部通过才能继续维持All_match的值。
"""
for condition, judge_condition in BUFF_LOADING_CONDITION_TRANSLATION_DICT.items():
"""
由于SkillNode中的属性名和judge_condition_dict中的键值名不同,
所以需要BUFF_LOADING_CONDITION_TRANSLATION_DICT进行翻译。
"""
csv_judge_condition = judge_condition_dict[condition]
if csv_judge_condition is not None:
"""
如果键值下面是None则直接跳过。
"""
final_condition = process_string(csv_judge_condition)
if getattr(skill_now, judge_condition) not in final_condition:
all_match = False
return all_match
def process_string(source: str) -> list[int | float | str]:
"""
在2024.11.13的更新中,从csv中读取的数据从单个数值变成了字符串,但是数据类型有点复杂。
如果单元格内没有分隔符,那么就会被转化为单元素列表,且会自动转换其中的数字为python数字,
如果有分隔符,则会根据分隔符打散成列表,并且将其中的数字转化成python数字。
由于getattr方法获得的技能属性的数值永远是单个的,所以用 技能属性 in list 的判定逻辑,
这样就可以实现“或”逻辑。
"""
if isinstance(source, str):
if "|" in source:
split_list = source.split("|")
return [eval(item) if item.isdigit() else item for item in split_list]
else:
return [eval(source) if source.isdigit() else source]
else:
return [source]
def process_buff_for_test(buff_0, sub_exist_buff_dict, mission):
"""
本函数截取了process_buff函数的头部,专为Pytest服务,正常程序请勿调用!
"""
all_match, judge_condition_dict, active_condition_dict = BuffInitialize(
buff_0.ft.index, sub_exist_buff_dict
)
all_match = BuffJudge(buff_0, judge_condition_dict, mission)
return all_match
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/AliceAdditionalAbilityApBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
from ._buff_record_base_class import BuffRecordBaseClass as BRBC
class AliceAdditionalAbilityApBonusRecord(BRBC):
def __init__(self):
super().__init__()
self.trans_ratio: float = 1.6 # 转化比率
class AliceAdditionalAbilityApBonus(Buff.BuffLogic):
def __init__(self, buff_instance: Buff):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xhit = self.special_hit_logic
self.buff_0: "Buff | None" = None
self.record: BRBC | None = None
def get_prepared(self, **kwargs):
assert self.buff_0 is not None, "buff_0未初始化"
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["爱丽丝"][self.buff_instance.ft.index]
assert self.buff_0 is not None, "buff_0未初始化"
if self.buff_0.history.record is None:
self.buff_0.history.record = AliceAdditionalAbilityApBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""根据触发时的异常掌控,计算转化的Buff层数"""
self.check_record_module()
self.get_prepared(char_CID=1401, sub_exist_buff_dict=1, enemy=1, dynamic_buff_list=1)
assert self.record is not None, "记录模块未初始化"
assert self.record.enemy is not None, "敌人未初始化"
assert self.record.dynamic_buff_list is not None, "动态Buff列表未初始化"
assert self.record.sub_exist_buff_dict is not None, "子存在Buff字典未初始化"
from zsim.sim_progress.ScheduledEvent.Calculator import Calculator, MultiplierData
mul_data = MultiplierData(
enemy_obj=self.record.enemy,
dynamic_buff=self.record.dynamic_buff_list,
character_obj=self.record.char,
)
am = Calculator.AnomalyMul.cal_am(mul_data)
if am < 140:
return
count = (am - 140) * self.record.trans_ratio
tick = self.buff_instance.sim_instance.tick
self.buff_instance.simple_start(
timenow=tick, sub_exist_buff_dict=self.record.sub_exist_buff_dict, no_count=1
)
self.buff_instance.dy.count = count
self.buff_instance.update_to_buff_0(buff_0=self.buff_0)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/AliceCinema6Trigger.py
================================================
from typing import TYPE_CHECKING
from .. import Buff, JudgeTools, check_preparation
from ._buff_record_base_class import BuffRecordBaseClass as BRBC
if TYPE_CHECKING:
pass
class AliceCinema6TriggerRecord(BRBC):
def __init__(self):
super().__init__()
self.additional_attack_skill_tag = "1401_Cinema_6" # 6画额外攻击的技能tag
class AliceCinema6Trigger(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.buff_0: "Buff | None" = None
self.record: BRBC | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["爱丽丝"][self.buff_instance.ft.index]
assert self.buff_0 is not None, "buff_0不能为空"
if self.buff_0.history.record is None:
self.buff_0.history.record = AliceCinema6TriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""爱丽丝的6画额外攻击逻辑判定函数,只会在决胜状态可用时放行队友技能的命中事件"""
self.check_record_module()
self.get_prepared(char_CID=1401)
assert self.record is not None, "记录模块未正确初始化,请检查函数"
assert self.record.char is not None, "角色未正确初始化,请检查函数"
from zsim.sim_progress.Character.Alice import Alice
if not isinstance(self.record.char, Alice):
raise TypeError("【爱丽丝6画触发器警告】初始化时获取的角色并非爱丽丝,请检查初始化逻辑")
skill_node = kwargs.get("skill_node")
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
assert isinstance(skill_node, SkillNode), "skill_node必须为SkillNode类型"
# 首先过滤掉自己的技能
if skill_node.char_name == self.record.char.NAME:
return False
# 当爱丽丝不处于决胜状态时,不触发
if not self.record.char.victory_state:
# print(self.record.char.victory_state_update_tick, self.record.char.victory_state_attack_counter)
return False
# 过滤掉并非处于命中帧的技能
if not skill_node.is_hit_now(tick=self.buff_instance.sim_instance.tick):
return False
else:
if self.record.check_cd(tick_now=self.buff_instance.sim_instance.tick):
return True
else:
return False
def special_hit_logic(self, **kwargs):
"""爱丽丝6画额外攻击触发器的业务函数,主要负责调用角色方法,添加额外攻击的Preload事件"""
self.check_record_module()
self.get_prepared(char_CID=1401)
assert self.record is not None, "记录模块未正确初始化,请检查函数"
assert self.record.char is not None, "角色未正确初始化,请检查函数"
from zsim.sim_progress.Character.Alice import Alice
if not isinstance(self.record.char, Alice):
raise TypeError("【爱丽丝6画触发器警告】初始化时获取的角色并非爱丽丝,请检查初始化逻辑")
self.record.char.spawn_extra_attack()
self.record.last_active_tick = self.buff_instance.sim_instance.tick
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/AlicePolarizedAssaultTrigger.py
================================================
from copy import deepcopy
from define import ALICE_REPORT
from zsim.sim_progress.Preload import SkillNode
from .. import Buff, JudgeTools, check_preparation
class AlicePolarizedAssaultTriggerRecord:
def __init__(self):
self.char = None
self.allowed_skill_tag_list: list[str] = ["1401_SNA_3", "1401_Q"] # 合法的极性强击触发源
self.trigger_origin: "SkillNode | None" = None
class AlicePolarizedAssaultTrigger(Buff.BuffLogic):
"""爱丽丝的极性强击触发器"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xeffect = self.special_effect_logic
self.buff_0 = None
self.record: AlicePolarizedAssaultTriggerRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["爱丽丝"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = AlicePolarizedAssaultTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs) -> bool:
"""极性强击的判断函数,放行三蓄和大招"""
self.check_record_module()
self.get_prepared(char_CID=1401)
skill_node = kwargs.get("skill_node")
if skill_node is None:
return False
if not isinstance(skill_node, SkillNode):
return False
if skill_node.skill_tag not in self.record.allowed_skill_tag_list:
return False
tick = self.buff_instance.sim_instance.tick
if not skill_node.is_last_hit(tick=tick):
return False
# 小于2画时,大招无法触发极性强击
if skill_node.skill_tag == "1401_Q" and self.record.char.cinema < 2:
return False
if self.record.trigger_origin is not None:
raise ValueError(
f"【极性强击触发器警告】存在尚未处理的触发源{self.record.trigger_origin.skill_tag}"
)
self.record.trigger_origin = skill_node
# print(f"【测试】当前时间{tick},{skill_node.skill_tag}即将通过判定。preload_tick: {skill_node.preload_tick}, end_tick: {skill_node.end_tick},tick_list: {skill_node.tick_list}")
return True
def special_effect_logic(self, **kwargs):
"""极性强击触发器的执行函数,构造一个极性强击事件并且将其添加进event_list中,同时置空自己的触发信号"""
self.check_record_module()
self.get_prepared(char_CID=1401)
from zsim.sim_progress.data_struct import PolarizedAssaultEvent
sim_instance = self.buff_instance.sim_instance
tick = sim_instance.tick
enemy = sim_instance.schedule_data.enemy
copyed_anomaly_bar = deepcopy(enemy.anomaly_bars_dict[0])
copyed_anomaly_bar.activated_by = self.record.trigger_origin
event = PolarizedAssaultEvent(
execute_tick=tick,
anomlay_bar=copyed_anomaly_bar,
char_instance=self.record.char,
skill_node=self.record.trigger_origin,
)
event_list = sim_instance.schedule_data.event_list
event_list.append(event)
if ALICE_REPORT:
sim_instance.schedule_data.change_process_state()
print(
f"【爱丽丝事件】{self.record.trigger_origin.skill.skill_text} 最后一Hit命中,创建了一个极性强击事件!"
)
self.record.trigger_origin = None
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/AnomalyDebuffExitJudge.py
================================================
from .. import Buff, JudgeTools
anomaly_statement_dict = {
"Buff-异常-霜寒": "frostbite",
"Buff-异常-畏缩": "assault",
"Buff-异常-烈霜霜寒": "frost_frostbite",
}
class AnomalyDebuffExitJudge(Buff.BuffLogic):
"""
理论上所有属性异常导致的debuff都适用这退出特效。
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xexit = self.special_exit_logic
self.last_frostbite = False
self.last_frost_frostbite = False
self.last_assault = False
self.last_shock = False
self.last_burn = False
self.last_corruption = False
self.enemy = None
def special_exit_logic(self, **kwargs):
"""
特殊属性异常退出机制
即:属性异常结束(检测到下降沿)就结束
"""
if self.enemy is None:
self.enemy = JudgeTools.find_enemy(sim_instance=self.buff_instance.sim_instance)
anomaly_name = anomaly_statement_dict[self.buff_instance.ft.index]
anomaly_now = getattr(self.enemy.dynamic, anomaly_name)
anomaly_statement = [
getattr(self.buff_instance.logic, f"last_{anomaly_name}"),
anomaly_now,
]
def mode_func(a, b):
return a is True and b is False
result = JudgeTools.detect_edge(anomaly_statement, mode_func)
setattr(self.buff_instance.logic, f"last_{anomaly_name}", anomaly_now)
return result
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/AstraYaoChordManagerTrigger.py
================================================
from zsim.define import ASTRAYAO_REPORT
from .. import Buff, JudgeTools, check_preparation, find_tick
class AstraYaoChordManagerTriggerRecord:
def __init__(self):
self.char = None
self.last_update_node = None
class AstraYaoChordManagerTrigger(Buff.BuffLogic):
def __init__(self, buff_instance):
"""耀嘉音震音管理器触发器,负责调用震音管理器并尝试添加协同攻击。"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xstart = self.special_start_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["耀嘉音"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = AstraYaoChordManagerTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""放行所有的符合条件的技能"""
self.check_record_module()
self.get_prepared(char_CID=1311)
skill_node = kwargs["skill_node"]
if skill_node.skill.trigger_buff_level in [5, 7, 8]:
if find_tick(sim_instance=self.buff_instance.sim_instance) == skill_node.preload_tick:
self.record.last_update_node = skill_node
return True
return False
def special_start_logic(self, **kwargs):
"""special_start函数只会在动作开始时执行,控制了执行的次数,防止重复触发。"""
self.check_record_module()
self.get_prepared(char_CID=1311)
char = self.record.char
skill_node = self.record.last_update_node
from zsim.sim_progress.Character.AstraYao import AstraYao
if not isinstance(char, AstraYao):
raise TypeError("record.char is not AstraYao")
char.chord_manager.chord_trigger.try_spawn_chord_coattack(
find_tick(sim_instance=self.buff_instance.sim_instance),
skill_node=skill_node,
)
if ASTRAYAO_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(f"检测到入场动作{skill_node.skill_tag},尝试调用震音管理器,触发协同攻击!")
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/AstraYaoCorePassiveAtkBonus.py
================================================
from zsim.define import ASTRAYAO_REPORT
from .. import Buff, JudgeTools, check_preparation, find_tick
class AstraYaoCorePassiveAtkBonusRecord:
def __init__(self):
self.char = None
self.core_passive_ratio = 0.35
self.duration_added_per_active = 1200
self.update_info_box = {}
self.sub_exist_buff_dict = None
class AstraYaoCorePassiveAtkBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""耀嘉音核心被动攻击力的xstart方法"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0: Buff | None = None
self.record = None
self.xstart = self.special_start_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["耀嘉音"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = AstraYaoCorePassiveAtkBonusRecord()
self.record = self.buff_0.history.record
def special_start_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1311, sub_exist_buff_dict=1)
from zsim.sim_progress.Character import Character
if not isinstance(self.record.char, Character):
raise TypeError
benifit = kwargs.get("benifit", None)
if benifit is None:
raise ValueError(f"{self.buff_instance.ft.index}的xstart函数并未获取到benifit参数")
static_atk = self.record.char.statement.ATK
count = min(static_atk * self.record.core_passive_ratio, self.buff_instance.ft.maxcount)
tick = find_tick(sim_instance=self.buff_instance.sim_instance)
if self.buff_0.dy.active and benifit in self.record.update_info_box:
last_update_tick = self.record.update_info_box[benifit]["startticks"]
if last_update_tick == find_tick(sim_instance=self.buff_instance.sim_instance):
# print(f'已经检测到{benifit}角色在当前tick有过buff更新,所以不做重复更新!!!')
return
# last_update_duration = self.record.update_info_box[benifit]["endticks"] - last_update_tick
last_update_end_tick = self.record.update_info_box[benifit]["endticks"]
"""如果本次buff更新的受益者曾在很久之前被加过buff,但是buff早就掉了,那么就当成第一次触发处理。"""
if last_update_end_tick < tick:
last_update_end_tick = tick
self.buff_instance.simple_start(
tick, self.record.sub_exist_buff_dict, no_start=1, no_count=1, no_end=1
)
self.buff_instance.dy.startticks = tick
# self.buff_instance.dy.endticks = min(last_update_duration - tick + last_update_tick + 1200, self.buff_instance.ft.maxduration+tick)
self.buff_instance.dy.endticks = min(
last_update_end_tick + 1200, self.buff_instance.ft.maxduration + tick
)
# if self.buff_instance.dy.startticks > self.buff_instance.dy.endticks:
# print(self.buff_instance.dy.startticks, self.buff_instance.dy.endticks, benifit)
# print(self.record.update_info_box[benifit])
else:
self.buff_instance.simple_start(
tick, self.record.sub_exist_buff_dict, no_count=1, no_end=1
)
self.buff_instance.dy.endticks = tick + self.record.duration_added_per_active
self.buff_instance.dy.count = count
# if self.buff_instance.dy.startticks > self.buff_instance.dy.endticks:
# print(self.buff_instance.dy.startticks, self.buff_instance.dy.endticks, benifit)
self.record.update_info_box[benifit] = {
"startticks": tick,
"endticks": self.buff_instance.dy.endticks,
"count": count,
}
self.buff_instance.update_to_buff_0(self.buff_0)
if ASTRAYAO_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(
f"核心被动触发器激活!成功为{benifit}角色添加攻击力buff({count}点)!Buff的时间节点为:{self.buff_instance.dy.startticks}--{self.buff_instance.dy.endticks}"
)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/AstraYaoIdyllicCadenza.py
================================================
from .. import Buff, JudgeTools, check_preparation
class AstraYaoIdyllicCadenzaRecord:
def __init__(self):
self.char = None
class AstraYaoIdyllicCadenza(Buff.BuffLogic):
def __init__(self, buff_instance):
"""耀嘉音咏叹华彩的加成效果的判定逻辑"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["耀嘉音"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = AstraYaoIdyllicCadenzaRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""检测咏叹华彩状态"""
self.check_record_module()
self.get_prepared(char_CID=1311)
if self.record.char.get_resources()[1]:
return True
else:
return False
def special_exit_logic(self, **kwargs):
return not self.special_judge_logic(**kwargs)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/AstraYaoQuickAssistManagerTrigger.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class AstraYaoQuickAssistManagerTriggerRecord:
def __init__(self):
self.char = None
class AstraYaoQuickAssistManagerTrigger(Buff.BuffLogic):
def __init__(self, buff_instance):
"""耀嘉音快支管理器的触发器,该触发器只负责把skill_node或者loading_mission扔给特殊资源模块。"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xeffect = self.special_effect_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["耀嘉音"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = AstraYaoQuickAssistManagerTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
return True
def special_effect_logic(self, **kwargs):
"""通过简单判定之后,执行special_effect_logic"""
self.check_record_module()
self.get_prepared(char_CID=1311)
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return
self.record.char.chord_manager.quick_assist_trigger_manager.update_myself(
find_tick(sim_instance=self.buff_instance.sim_instance), skill_node
)
# if ASTRAYAO_REPORT:
# print(f'检测到攻击动作命中,尝试调用快支管理器!')
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/AstralVoice.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class AstralVoiceRecord:
def __init__(self):
self.equipper = None
self.char = None
self.trigger_buff_0 = None
self.sub_exist_buff_dict = None
self.action_stack = None
class AstralVoice(Buff.BuffLogic):
"""
这是静听佳音四件套的生效逻辑。该Buff有一个“触发器”,
该触发器由简单逻辑控制,会根据支援突击触发、叠层和刷新;
而触发器本身并无效果,真正实现增伤和复杂判定的是本buff的逻辑模块。
本模块由 复杂判定(xjudge) 和 复杂生效(xstart) 两个部分构成
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xeffect = self.special_effect_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"静听嘉音", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
"""
这里的初始化,找到的buff_0实际上是佩戴者的buff_0,
即使是在受益者的buff.history.record中存储的,也是装备佩戴者的buff_0。
"""
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = AstralVoiceRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
复杂判定逻辑:首先要检测触发器Buff的激活情况;
即:trigger_buff_0.dy.active
然后是对比trigger_buff_level,对比通过才能输出True
"""
self.check_record_module()
self.get_prepared(
equipper="静听嘉音",
trigger_buff_0=(
self.buff_instance.ft.operator,
"Buff-驱动盘-静听嘉音-嘉音",
),
action_stack=1,
)
tick_now = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Load import LoadingMission
from zsim.sim_progress.Preload import SkillNode
if isinstance(skill_node, SkillNode):
pass
elif isinstance(skill_node, LoadingMission):
skill_node = skill_node.mission_node
if self.record.trigger_buff_0.dy.active and skill_node.skill.trigger_buff_level == 7:
if skill_node.loading_mission.mission_dict.get(tick_now, None) == "start":
return True
else:
return False
else:
return False
def special_effect_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(
equipper="静听嘉音",
trigger_buff_0=(
self.buff_instance.ft.operator,
"Buff-驱动盘-静听嘉音-嘉音",
),
sub_exist_buff_dict=1,
)
tick_now = find_tick(sim_instance=self.buff_instance.sim_instance)
self.buff_instance.simple_start(tick_now, self.record.sub_exist_buff_dict)
self.buff_instance.dy.count = self.record.trigger_buff_0.dy.count
self.buff_instance.update_to_buff_0(self.buff_0)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/BackendJudge.py
================================================
from .. import Buff, JudgeTools
class BackendJudge(Buff.BuffLogic):
"""
后台判定的通用逻辑模块
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
self.equipper = None
def special_judge_logic(self, **kwargs):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
self.buff_instance.ft.bufffrom,
sim_instance=self.buff_instance.sim_instance,
)
name_box = JudgeTools.find_init_data(sim_instance=self.buff_instance.sim_instance).name_box
if name_box[0] != self.equipper:
return True
else:
return False
def special_exit_logic(self, **kwargs):
result = self.xjudge()
if result:
return False
else:
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/BasicComplexBuffClass.py
================================================
from .. import Buff, JudgeTools, check_preparation
class BaseBuffRecord:
"""基础记录Class"""
def __init__(self):
self.char = None
self.buff_0 = None
self.exist_buff_dict = None
self.sub_exist_buff_dict = None
self.preload_data = None
self.trigger_buff_0 = None
self.equipper = None
class BasicComplexBuffClass(Buff.BuffLogic):
RECORD_CLASS = BaseBuffRecord
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
self.xhit = self.special_hit_logic
self.xeffect = self.special_effect_logic
def get_prepared(self, **kwargs):
"""通用准备检查方法"""
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self, **kwargs):
"""通用记录模块检查"""
char_name = kwargs.get("char_name", None)
if char_name is None:
raise ValueError(
f"{self.buff_instance.ft.index}在进行初始化时,复杂Buff逻辑中的check_record_module函数中并未传入有效的char_name参数!"
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[char_name][self.buff_instance.ft.index]
if not hasattr(self.buff_0.history, "record") or self.buff_0.history.record is None:
self.buff_0.history.record = self.RECORD_CLASS()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
pass
def special_exit_logic(self, **kwargs):
pass
def special_start_logic(self, **kwargs):
pass
def special_hit_logic(self, **kwargs):
pass
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/BranchBladeSongCritDamageBonus.py
================================================
from zsim.sim_progress.ScheduledEvent.Calculator import Calculator, MultiplierData
from .. import Buff, JudgeTools, check_preparation
class BranchBladeSongRecord:
def __init__(self):
self.equipper = None
self.enemy = None
self.dynamic_buff_list = None
self.char = None
class BranchBladeSongCritDamageBonus(Buff.BuffLogic):
"""
该buff是新冰4件套中的第一特效:异常掌控>=115就会触发。
由于不能实现“异常掌控>=115时候,将buff.ft.alltime修改为True的操作,
所以只能让该buff在每个动作都检测,然后每个动作都触发,用来平替alltime。
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"折枝剑歌", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = BranchBladeSongRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="折枝剑歌", enemy=1, dynamic_buff_list=1)
mul_data = MultiplierData(
self.record.enemy, self.record.dynamic_buff_list, self.record.char
)
am = Calculator.AnomalyMul.cal_am(mul_data)
if am >= 115:
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/BranchBladeSongCritRateBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class BranchBladeSongCritRateBonusRecord:
def __init__(self):
self.enemy = None
self.equipper = None
self.main_module = None
self.char = None
self.last_tick_freez_statement = 0, False
class BranchBladeSongCritRateBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""
该buff是新冰4的第二特效,需要检测冻结和碎冰效果。
也就是enemy.dynamic.frozen的状态,只要发生改变,就可以触发。
"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
# 初始化特定逻辑
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"折枝剑歌", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = BranchBladeSongCritRateBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="折枝剑歌", enemy=1)
tick = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
if self.record.enemy.dynamic.frozen is None:
this_tick_freez_statement = False
else:
this_tick_freez_statement = self.record.enemy.dynamic.frozen
if this_tick_freez_statement != self.record.last_tick_freez_statement[1]:
self.record.last_tick_freez_statement = tick, this_tick_freez_statement
return True
else:
self.record.last_tick_freez_statement = tick, this_tick_freez_statement
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/CannonRotor.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class CannonRotorRecord:
def __init__(self):
self.equipper = None
self.char = None
self.enemy = None
self.dynamic_buff_list = None
self.skill_tag = "CannonRotorAdditionalDamage"
self.preload_data = None
self.sub_exist_buff_dict = None
class CannonRotor(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"加农转子", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = CannonRotorRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="加农转子", enemy=1, dynamic_buff_list=1, sub_exist_buff_dict=1)
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise ValueError(
f"{self.buff_instance.ft.index}的Xjudge函数获取的skill_node不是SkillNode类型!"
)
if skill_node.char_name != self.record.char.NAME:
return False
if not skill_node.is_hit_now(find_tick(sim_instance=self.buff_instance.sim_instance)):
return False
from zsim.sim_progress.RandomNumberGenerator import RNG
from zsim.sim_progress.ScheduledEvent.Calculator import Calculator, MultiplierData
mul_data = MultiplierData(
self.record.enemy, self.record.dynamic_buff_list, self.record.char
)
rng: RNG = self.buff_instance.sim_instance.rng_instance
normalized_value = rng.random_float()
cric_rate = Calculator.RegularMul.cal_crit_rate(mul_data)
if normalized_value <= cric_rate:
return True
return False
def special_hit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="加农转子", enemy=1, dynamic_buff_list=1, preload_data=1)
event_list = JudgeTools.find_event_list(sim_instance=self.buff_instance.sim_instance)
from zsim.sim_progress.Preload.SkillsQueue import spawn_node
whole_skill_tag = str(self.record.char.CID) + "_" + self.record.skill_tag
node = spawn_node(
whole_skill_tag,
find_tick(sim_instance=self.buff_instance.sim_instance),
self.record.preload_data.skills,
)
from zsim.sim_progress.Load import LoadingMission
mission = LoadingMission(node)
mission.mission_start(find_tick(sim_instance=self.buff_instance.sim_instance))
node.loading_mission = mission
event_list.append(node)
self.buff_instance.simple_start(
find_tick(sim_instance=self.buff_instance.sim_instance),
self.record.sub_exist_buff_dict,
)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/CinderCobaltAtkBonus.py
================================================
from zsim.sim_progress.Buff import Buff, JudgeTools, check_preparation, find_tick
class CinderCobaltAtkBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
self.listener = None
class CinderCobaltAtkBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"「灰烬」-钴蓝", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = CinderCobaltAtkBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="「灰烬」-钴蓝")
if self.record.listener is None:
self.record.listener = self.buff_instance.sim_instance.listener_manager.get_listener(
listener_owner=self.record.char,
listener_id=self.buff_instance.ft.listener_id,
)
active_signal = self.record.listener.active_signal
if active_signal is None:
return False
if active_signal[0].NAME != self.record.char.NAME:
return False
else:
self.record.listener.active_signal = None
if self.buff_0.is_ready(find_tick(sim_instance=self.buff_instance.sim_instance)):
# print(
# f"{self.buff_instance.ft.index}接收到了匹配的更新信号(佩戴者为{active_signal[0].NAME})"
# )
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/CordisGerminaCritRateBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class CordisGerminaCritRateBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
class CordisGerminaCritRateBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record: CordisGerminaCritRateBonusRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"机巧心种", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = CordisGerminaCritRateBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="机巧心种")
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/CordisGerminaEleDmgBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
from ._buff_record_base_class import BuffRecordBaseClass as Brbc
class CordisGerminaEleDmgBonusRecord(Brbc):
def __init__(self):
super().__init__()
class CordisGerminaEleDmgBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""这是机巧心种电属性增伤Buff的脚本"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record: CordisGerminaEleDmgBonusRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"机巧心种", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = CordisGerminaEleDmgBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""机巧心种的电属性增伤Buff触发器,由于在后台也需要监听,所以这里需要用脚本进行判断"""
self.check_record_module()
self.get_prepared(equipper="机巧心种")
assert self.record is not None
skill_node = kwargs.get("skill_node", None)
from zsim.sim_progress.Preload import SkillNode
assert isinstance(skill_node, SkillNode)
# 首先筛选掉没有佩戴机巧心种的角色的技能
if skill_node.char_name != self.record.char.NAME:
return False
# 筛选掉不是强化E和普攻的技能。
if skill_node.skill.trigger_buff_level not in [0, 2]:
return False
# 筛选掉非命中帧
tick = self.buff_instance.sim_instance.tick
if not skill_node.is_hit_now(tick=tick):
return False
else:
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/CordisGerminaSNAAndQIgnoreDefense.py
================================================
from .. import Buff, JudgeTools, check_preparation
from ._buff_record_base_class import BuffRecordBaseClass as Brbc
class CordisGerminaSNAAndQIgnoreDefenseRecord(Brbc):
def __init__(self):
super().__init__()
class CordisGerminaSNAAndQIgnoreDefense(Buff.BuffLogic):
def __init__(self, buff_instance):
"""这是机巧心种普攻大招无视防御Buff的脚本"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
self.equipper = None
self.buff_0 = None
self.record: CordisGerminaSNAAndQIgnoreDefenseRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"机巧心种", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = CordisGerminaSNAAndQIgnoreDefenseRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="机巧心种", trigger_buff_0=("equipper", "机巧心种-电属性增伤"))
assert self.record is not None
assert self.record.trigger_buff_0 is not None
result = len(self.record.trigger_buff_0.dy.built_in_buff_box) == 2
return result
def special_exit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="机巧心种", trigger_buff_0=("equipper", "机巧心种-电属性增伤"))
return not self.xjudge
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/DawnsBloom4SetTriggerNADmgBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
from ._buff_record_base_class import BuffRecordBaseClass as Brbc
class DawnsBloom4SetTriggerNADmgBonusRecord(Brbc):
def __init__(self):
super().__init__()
class DawnsBloom4SetTriggerNADmgBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""这是拂晓生花四件套触发普攻增伤Buff的脚本"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record: DawnsBloom4SetTriggerNADmgBonusRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"拂晓生花", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = DawnsBloom4SetTriggerNADmgBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""拂晓生花4件套触发普攻增伤,仅对强攻角色生效"""
self.check_record_module()
self.get_prepared(equipper="拂晓生花")
assert self.record is not None
# 对于非强攻角色,永远不触发
if self.record.char.specialty != "强攻":
return False
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
assert isinstance(skill_node, SkillNode)
# 筛选掉不是强化E和大招的技能
if skill_node.skill.trigger_buff_level not in [2, 6]:
return False
tick = self.buff_instance.sim_instance.tick
if skill_node.preload_tick != tick:
return False
else:
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/ElectroLipGlossAtkAndDmgBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class ElectroLipGlossAtkAndDmgBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
self.enemy = None
class ElectroLipGlossAtkAndDmgBonus(Buff.BuffLogic):
"""触电唇彩判定逻辑"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"触电唇彩", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = ElectroLipGlossAtkAndDmgBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""检测到目标处于异常状态就放行。"""
self.check_record_module()
self.get_prepared(equipper="触电唇彩", enemy=1)
if self.record.enemy.dynamic.is_under_anomaly():
return True
else:
return False
def special_exit_logic(self, **kwargs):
return not self.special_judge_logic()
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/ElegantVanityDmgBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class ElegantVanityDmgBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
self.last_update_tick_node = None
class ElegantVanityDmgBonus(Buff.BuffLogic):
"""玲珑妆匣的全队增伤Buff逻辑。"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"玲珑妆匣", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = ElegantVanityDmgBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
判断传入的skill_node的能耗是否>=25,如果是则放行
"""
self.check_record_module()
self.get_prepared(equipper="玲珑妆匣")
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
raise ValueError(f"{self.buff_instance.ft.index}的Xjudge函数获取到的skill_node为None!")
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise TypeError(
f"{self.buff_instance.ft.index}的Xjudge函数获取到的skill_node类型错误!"
)
# 过滤不是自己的skill_node
if skill_node.char_name != self.record.char.NAME:
return False
if skill_node.preload_tick < JudgeTools.find_tick(
sim_instance=self.buff_instance.sim_instance
):
return False
if skill_node.skill.sp_consume >= 25:
if self.record.last_update_tick_node is None:
self.record.last_update_tick_node = skill_node
# print(f'增伤Buff因{skill_node.skill_tag}触发!')
return True
else:
if self.record.last_update_tick_node.UUID != skill_node.UUID:
self.record.last_update_tick_node = skill_node
# print(f'增伤Buff因{skill_node.skill_tag}触发!')
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/ElegantVanitySpRecover.py
================================================
from .. import Buff, JudgeTools, check_preparation
class ElegantVanitySpRecoverRecord:
def __init__(self):
self.equipper = None
self.char = None
self.sub_exist_buff_dict = None
self.energy_value_dict = {1: 5, 2: 5.5, 3: 6, 4: 6.5, 5: 7}
class ElegantVanitySpRecover(Buff.BuffLogic):
"""玲珑妆匣的回能Buff逻辑。"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xstart = self.special_start_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"玲珑妆匣", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = ElegantVanitySpRecoverRecord()
self.record = self.buff_0.history.record
def special_start_logic(self, **kwargs):
"""
这部分的代码主要是负责构建一个ScheduleRefreshData实例的,
而simple_start只是为了启动一次,让Log记录到这个buff。
Buff自身没有效果。
"""
self.check_record_module()
self.get_prepared(equipper="玲珑妆匣", sub_exist_buff_dict=1)
tick_now = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
self.buff_instance.simple_start(tick_now, self.record.sub_exist_buff_dict)
energy_value = self.record.energy_value_dict[int(self.buff_instance.ft.refinement)]
event_list = JudgeTools.find_event_list(sim_instance=self.buff_instance.sim_instance)
from zsim.sim_progress.data_struct import ScheduleRefreshData
refresh_data = ScheduleRefreshData(
sp_target=(self.record.char.NAME,),
sp_value=energy_value,
)
event_list.append(refresh_data)
# print(f'玲珑妆匣回能触发!')
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/FlamemakerShakerApBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class FlamemakerShakerApBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
self.trigger_buff_0 = None
class FlamemakerShakerApBonus(Buff.BuffLogic):
"""灼心摇壶的精通增幅判定"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"灼心摇壶", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = FlamemakerShakerApBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""检测到目标buff层数>=5时候放行"""
self.check_record_module()
self.get_prepared(equipper="灼心摇壶", trigger_buff_0=("equipper", "灼心摇壶-增伤"))
if not self.record.trigger_buff_0.dy.active:
return False
if self.record.trigger_buff_0.dy.count < 5:
return False
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/FlamemakerShakerDmgBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class FlamemakerShakerDmgBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
self.preload_data = None
self.sub_exist_buff_dict = None
class FlamemakerShakerDmgBonus(Buff.BuffLogic):
"""灼心摇壶的增伤逻辑判定"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"灼心摇壶", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = FlamemakerShakerDmgBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""检测到强化E标签或是支援攻击标签,则放行。如果角色处于前台则更新1层,若角色处于后台则更新两层。"""
self.check_record_module()
self.get_prepared(equipper="灼心摇壶")
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Load import LoadingMission
from zsim.sim_progress.Preload import SkillNode
if isinstance(skill_node, SkillNode):
pass
elif isinstance(skill_node, LoadingMission):
skill_node = skill_node.mission_node
else:
return False
# 滤去不是自己的技能
if self.record is not None and self.record.equipper != skill_node.char_name:
return False
if skill_node.skill.trigger_buff_level == 2:
return True
else:
if skill_node.skill.labels is not None:
# 若技能有标签,则判断是否是支援攻击标签。
if "Assist_Attack" in skill_node.skill.labels:
return True
else:
return False
def special_hit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="灼心摇壶", preload_data=1, sub_exist_buff_dict=1)
if self.record.preload_data.operating_now != self.record.char.CID:
# 说明此时角色正位于后台,更新两层。
self.buff_instance.simple_start(
find_tick(sim_instance=self.buff_instance.sim_instance),
self.record.sub_exist_buff_dict,
no_count=1,
)
self.buff_instance.dy.count = min(
self.buff_instance.dy.count + 2, self.buff_instance.ft.maxcount
)
else:
self.buff_instance.simple_start(
find_tick(sim_instance=self.buff_instance.sim_instance),
self.record.sub_exist_buff_dict,
)
self.buff_instance.update_to_buff_0(self.buff_0)
# print(f'灼心摇壶更新了{update_count}层,当前层数为:{self.buff_0.dy.count}')
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/FlightOfFancy.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class FlightOfFancyRecord:
def __init__(self):
self.equipper = None
self.char = None
class FlightOfFancy(Buff.BuffLogic):
"""飞鸟星梦的复杂逻辑,监测到装备者造成以太伤害时叠层。"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"飞鸟星梦", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = FlightOfFancyRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""检测到装备者的以太伤害技能,并且处于Hit节点。"""
self.check_record_module()
self.get_prepared(equipper="飞鸟星梦")
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Load import LoadingMission
from zsim.sim_progress.Preload import SkillNode
if isinstance(skill_node, SkillNode):
pass
elif isinstance(skill_node, LoadingMission):
skill_node = skill_node.mission_node
else:
return False
# 滤去不是自己的技能
if self.record.equipper != skill_node.char_name:
return False
# 滤去非以太伤害的技能
if skill_node.element_type != 4:
return False
tick = find_tick(sim_instance=self.buff_instance.sim_instance)
if skill_node.loading_mission.is_hit_now(tick):
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/FreedomBlues.py
================================================
from .. import Buff, JudgeTools, check_preparation
class FreedomBluesRecord:
def __init__(self):
self.equipper = None
self.char = None
self.action_stack = None
class FreedomBlues(Buff.BuffLogic):
"""
这是自由蓝调的复杂判定逻辑。
自由蓝调被分为6个buff,但是共用这一个逻辑模块。
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"自由蓝调", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = FreedomBluesRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
只有装备者位于前台,并且当前的动作是强化E才会进入下一轮判断
只有当强化E 属性与buff自身的refinement想同,才会输出True。
这里,refinement被借用来记录buff对应的属性种类。
"""
self.check_record_module()
self.get_prepared(equipper="自由蓝调", action_stack=1)
action_now = self.record.action_stack.peek()
element_type_trigger = self.buff_instance.ft.refinement
if (
str(self.record.char.CID) in action_now.mission_tag
and action_now.mission_node.skill.trigger_buff_level == 2
):
if action_now.mission_node.element_type == element_type_trigger:
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/HailstormShrineIceBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
anomaly_name_list = ["frostbite", "assault", "shock", "burn", "corruption"]
class HailstormShrineIceBonusRecord:
def __init__(self):
self.anomaly_state = {name: False for name in anomaly_name_list}
self.equipper = None
self.action_stack = None
self.enemy = None
self.char = None
class HailstormShrineIceBonus(Buff.BuffLogic):
"""
该buff为雅专武的冰伤判定模块。
它需要检测所有的属性异常,找它们的上升沿。
或者是当前动作的trigger_buff_level为强化特殊技
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"霰落星殿", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = HailstormShrineIceBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="霰落星殿", enemy=1, action_stack=1)
action_now = self.record.action_stack.peek()
current_anomalies = {
name: getattr(self.record.enemy.dynamic, name) for name in anomaly_name_list
}
# 判断总异常数量是否 >= 2
if sum(current_anomalies.values()) >= 2 or sum(self.record.anomaly_state.values()) >= 2:
raise ValueError("当前ticks总异常数量为2!")
# 检查是否有状态变化或满足特殊技触发条件
has_change = any(
current_anomalies[name] != self.record.anomaly_state[name] for name in anomaly_name_list
)
if has_change or (
action_now.mission_node.skill.trigger_buff_level == 2
and str(self.record.char.CID) in action_now.mission_tag
):
self.record.anomaly_state.update(current_anomalies)
return True
# 更新状态并返回
self.record.anomaly_state.update(current_anomalies)
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/HeartstringNocturne.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class HeartstringNocturneRecord:
def __init__(self):
self.equipper = None
self.char = None
self.listener_exist = False
self.listener = None
class HeartstringNocturne(Buff.BuffLogic):
"""心弦夜响的复杂逻辑:进入战斗或是释放连携技、大招时触发。"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"心弦夜响", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = HeartstringNocturneRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="心弦夜响")
if not self.record.listener_exist:
self.record.listener = self.buff_instance.sim_instance.listener_manager.get_listener(
listener_owner=self.record.char,
listener_id="Heartstring_Nocturne_1",
)
# self.record.listener = self.buff_instance.sim_instance.listener_manager.listener_factory(
# initiate_signal="Heartstring_Nocturne_1", sim_instance=self.buff_instance.sim_instance
# )
self.record.listener_exist = True
# print(f"为{self.record.char.NAME}创建了一个心弦夜响的监听器!")
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise ValueError(
f"{self.buff_instance.ft.index}的Xjudge函数获取的skill_node不是SkillNode类型!"
)
active_signal = self.record.listener.active_signal
if active_signal is not None:
event_obj: SkillNode = active_signal[0]
if event_obj.char_name == self.record.char.NAME:
return True
else:
if skill_node.char_name == self.record.char.NAME:
if skill_node.preload_tick == find_tick(
sim_instance=self.buff_instance.sim_instance
) and skill_node.skill.trigger_buff_level in [5, 6]:
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/HellfireGearsSpRBonus.py
================================================
from .. import Buff, JudgeTools
class HellfireGearsSpRBonus(Buff.BuffLogic):
"""
燃狱齿轮的后台回能。需要在初始化的时候就获取角色和武器配置列表
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
self.equipper = None
def special_judge_logic(self, **kwargs):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"燃狱齿轮", sim_instance=self.buff_instance.sim_instance
)
name_box = JudgeTools.find_init_data(sim_instance=self.buff_instance.sim_instance).name_box
if name_box[0] != self.equipper:
return True
else:
return False
def special_exit_logic(self, **kwargs):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"燃狱齿轮", sim_instance=self.buff_instance.sim_instance
)
name_box = JudgeTools.find_init_data(sim_instance=self.buff_instance.sim_instance).name_box
if name_box[0] == self.equipper:
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/HormonePunkAtkBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class HormonePunkAtkBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
self.listener_exist = False
self.listener = None
class HormonePunkAtkBonus(Buff.BuffLogic):
"""激素朋克的复杂逻辑模块,检测到监听器更新信号时更新。"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"激素朋克", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = HormonePunkAtkBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""检测到更新信号时,返回True,并且置空监听器的active_signal。"""
self.check_record_module()
self.get_prepared(equipper="激素朋克")
if not self.record.listener_exist:
self.record.listener = self.buff_instance.sim_instance.listener_manager.get_listener(
listener_owner=self.record.char, listener_id="Hormone_Punk_1"
)
self.record.listener_exist = True
# print(f"为{self.record.char.NAME}创建了一个激素朋克的监听器!")
active_signal = self.record.listener.active_signal
if active_signal is None:
return False
if active_signal[0].NAME != self.record.char.NAME:
return False
else:
self.record.listener.active_signal = None
if self.buff_0.is_ready(find_tick(sim_instance=self.buff_instance.sim_instance)):
# print(
# f"{self.buff_instance.ft.index}接收到了匹配的更新信号(佩戴者为{active_signal[0].NAME}),buff更新时间{self.buff_0.dy.startticks}, buff结束时间为{self.buff_0.dy.endticks}"
# )
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/HugoAdditionalAbilityExtraQTEDmgBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class HugoAdditionalAbilityExtraQTEDmgBonusRecord:
def __init__(self):
self.char = None
self.enemy = None
class HugoAdditionalAbilityExtraQTEDmgBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""雨果组队被动,连携技对普通敌人增伤"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0: Buff | None = None
self.record = None
self.xjudge = self.special_judge_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["雨果"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = HugoAdditionalAbilityExtraQTEDmgBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""连携技对普通敌人增伤"""
self.check_record_module()
self.get_prepared(char_CID=1291, enemy=1)
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise TypeError(
f"{self.buff_instance.ft.index}的xjudge函数获取到的skill_node不是SkillNode类型"
)
"""过滤不是自己的技能"""
if "1291" not in skill_node.skill_tag:
return False
"""过滤不是连携技的技能"""
if skill_node.skill.trigger_buff_level != 5:
return False
"""普通敌人的筛选是通过可连携次数来判断的"""
if self.record.enemy.QTE_triggerable_times <= 1:
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/HugoCorePassiveDoubleStunAtkBonus.py
================================================
from zsim.define import HUGO_REPORT
from .. import Buff, JudgeTools, check_preparation
class HugoCorePassiveDoubleStunAtkBonusRecord:
def __init__(self):
self.char = None
self.stun_char_count = None
self.char_obj_list = None
class HugoCorePassiveDoubleStunAtkBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""雨果核心被动,双击破角色的攻击力加成"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0: Buff | None = None
self.record = None
self.xjudge = self.special_judge_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["雨果"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = HugoCorePassiveDoubleStunAtkBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""当队伍里存在2名被击角色时,触发该效果"""
self.check_record_module()
self.get_prepared(char_CID=1291, char_obj_list=1)
if self.record.stun_char_count is None:
self.record.stun_char_count = 0
for char_obj in self.record.char_obj_list:
from zsim.sim_progress.Character import Character
if not isinstance(char_obj, Character):
raise TypeError("char_obj_list中的对象不是Character类的实例")
if char_obj.specialty == "击破":
self.record.stun_char_count += 1
if HUGO_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(
f"雨果的双击破角色攻击力Buff在初始化阶段检测到了队伍中有{self.record.stun_char_count}名击破角色"
)
stun_char_count = self.record.stun_char_count
if stun_char_count >= 2:
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/HugoCorePassiveEXStunBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class HugoCorePassiveEXStunBonusRecord:
def __init__(self):
self.char = None
self.enemy = None
class HugoCorePassiveEXStunBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""雨果核心被动,E对非失衡状态的敌人造成的失衡值提升"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0: Buff | None = None
self.record = None
self.xjudge = self.special_judge_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["雨果"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = HugoCorePassiveEXStunBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""强化E命中非失衡状态的敌人时触发"""
self.check_record_module()
self.get_prepared(char_CID=1291, enemy=1)
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise TypeError(
f"{self.buff_instance.ft.index}的xjudge函数获取到的skill_node不是SkillNode类型"
)
"""过滤不是自己的技能"""
if "1291" not in skill_node.skill_tag:
return False
"""过滤不是强化E的技能"""
if skill_node.skill.trigger_buff_level != 2:
return False
if self.record.enemy.dynamic.stun:
return False
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/HugoCorePassiveSingleStunAtkBonus.py
================================================
from zsim.define import HUGO_REPORT
from zsim.sim_progress.Buff import Buff, JudgeTools, check_preparation
class HugoCorePassiveSingleStunAtkBonusRecord:
def __init__(self):
self.char = None
self.stun_char_count = None
self.char_obj_list = None
class HugoCorePassiveSingleStunAtkBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""雨果核心被动,单击破角色的攻击力加成"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0: Buff | None = None
self.record = None
self.xjudge = self.special_judge_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["雨果"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = HugoCorePassiveSingleStunAtkBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""当队伍里存在一名被击角色时,触发该效果"""
self.check_record_module()
self.get_prepared(char_CID=1291, char_obj_list=1)
if self.record.stun_char_count is None:
self.record.stun_char_count = 0
for char_obj in self.record.char_obj_list:
from zsim.sim_progress.Character import Character
if not isinstance(char_obj, Character):
raise TypeError("char_obj_list中的对象不是Character类的实例")
if char_obj.specialty == "击破":
self.record.stun_char_count += 1
if HUGO_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(
f"雨果的单击破角色攻击力Buff在初始化阶段检测到了队伍中有{self.record.stun_char_count}名击破角色"
)
stun_char_count = self.record.stun_char_count
if stun_char_count >= 1:
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/HugoCorePassiveTotalizeTrigger.py
================================================
from zsim.define import HUGO_REPORT
from zsim.sim_progress.Enemy import Enemy
from .. import Buff, JudgeTools, check_preparation, find_tick
class HugoCorePassiveTotalizeTriggerRecord:
def __init__(self):
self.char = None
self.enemy: Enemy | None = None
self.active_signal = None
self.E_totalize_tag = "1291_CorePassive_E_EX"
self.Q_totalize_tag = "1291_CorePassive_Q"
self.totalize_buff_index = "Buff-角色-雨果-决算倍率增幅"
self.abyss_reverb_buff_index = "Buff-角色-雨果-核心被动-暗渊回响"
self.cinema_1_buff_index = "Buff-角色-雨果-1画-决算招式双暴增幅"
self.cinema_2_buff_index = "Buff-角色-雨果-2画-决算招式无视防御力"
self.cinema_6_buff_index = "Buff-角色-雨果-6画-决算招式增伤"
self.preload_data = None
self.shot_attack_list = [
"1291_SNA_2_NFC",
"1291_SNA_2_FC",
"1291_SCA",
"1291_SCA_FC",
"1291_NA_A",
"1291_NA_A_FC",
"1291_BH_Aid_A",
"1291_BH_Aid_A_FC",
]
# self.fc_shot_attack_list = [
# "1291_SNA_2_FC",
# "1291_SCA_FC",
# "1291_NA_A_FC",
# "1291_BH_Aid_A_FC",
# ]
class HugoCorePassiveTotalizeTrigger(Buff.BuffLogic):
def __init__(self, buff_instance):
"""雨果核心被动,决算触发器"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0: Buff | None = None
self.record: HugoCorePassiveTotalizeTriggerRecord | None = None
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["雨果"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = HugoCorePassiveTotalizeTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""敌人处于失衡状态时,强化E、大招触发"""
self.check_record_module()
self.get_prepared(char_CID=1291, enemy=1, preload_data=1)
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise TypeError(
f"{self.buff_instance.ft.index}的xjudge函数获取到的skill_node不是SkillNode类型"
)
# 过滤不是自己的技能
if "1291" not in skill_node.skill_tag:
return False
# 过滤不是E、大招的技能
if skill_node.skill.trigger_buff_level not in [2, 6]:
"""6画时,任何射击攻击都可以借由本触发器来执行暗渊回响Buff的触发"""
if (
self.record.char.cinema == 6
and skill_node.skill_tag in self.record.shot_attack_list
):
if skill_node.loading_mission is None:
raise ValueError(f"{skill_node.skill_tag}本应该有loading_mission,但是没有")
if not skill_node.loading_mission.is_last_hit(
find_tick(sim_instance=self.buff_instance.sim_instance)
):
return False
else:
self.record.active_signal = skill_node.skill.trigger_buff_level
return True
else:
return False
# 过滤掉无法触发决算的第一段强化E
if skill_node.skill_tag == "1291_E_EX_1":
return False
# 过滤掉可能进入Buff循环的决算??大概率不可能吧,决算能进BuffLoad那真的是见鬼了
if skill_node.skill.labels is not None:
if "totalize" in skill_node.skill.labels:
return False
# 过滤不是最后一次命中的技能
if skill_node.loading_mission is None:
return False
if not skill_node.loading_mission.is_last_hit(
find_tick(sim_instance=self.buff_instance.sim_instance)
):
return False
if self.record.active_signal is not None:
raise ValueError(
f"雨果的决算触发器在运行时候发现存在就一个未被结算的信号{self.record.active_signal},这是不允许的"
)
# 如果是6命,则无条件放行强化E
if self.record.char.cinema == 6 and skill_node.skill.trigger_buff_level == 2:
self.record.active_signal = skill_node.skill.trigger_buff_level
return True
else:
# 否则,检测敌人是否处于失衡状态
if self.record.enemy.dynamic.stun:
self.record.active_signal = skill_node.skill.trigger_buff_level
return True
else:
return False
def special_hit_logic(self, **kwargs):
"""结算E、大招"""
self.check_record_module()
self.get_prepared(char_CID=1291, enemy=1, preload_data=1)
if self.record.active_signal is None:
raise ValueError("雨果的决算触发器的Xjudge函数放行了,但是Xhit函数却没有获取到触发信号")
if self.record.active_signal not in [0, 2, 6]:
raise ValueError(
f"雨果的决算触发器的Xjudge函数放行了,但是给出了非法信号!触发信号:{self.record.active_signal}"
)
elif self.record.active_signal == 0 and self.record.char.cinema != 6:
raise ValueError(f"在非6画的情况下检测到了非法的触发信号:{self.record.active_signal}")
"""准备数据"""
event_list = JudgeTools.find_event_list(sim_instance=self.buff_instance.sim_instance)
rest_tick = self.record.enemy.get_stun_rest_tick()
ratio = 1000 + min(300, rest_tick) / 60 * 280 + min(600, max(rest_tick - 300, 0)) / 60 * 100
if self.record.active_signal in [2, 6]:
if HUGO_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(
f"雨果使用{'大招' if self.record.active_signal == 6 else '强化E'}触发了决算!本次决算结算的失衡时间为{rest_tick / 60:.2f}秒,结算倍率为{ratio:.2f}%,"
)
else:
if HUGO_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print("6画触发:检测到射击攻击命中!为雨果触发一次暗渊回响Buff!")
"""先处理Buff"""
from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy
if self.record.active_signal == 0:
abyss_reverb_buff_index = self.record.abyss_reverb_buff_index
buff_add_strategy(
abyss_reverb_buff_index,
benifit_list=["雨果"],
sim_instance=self.buff_instance.sim_instance,
)
self.record.active_signal = None
"""触发信号为0时,只添加Buff,不执行后面的逻辑。"""
return
else:
buff_index = self.record.totalize_buff_index
buff_add_strategy(
buff_index,
specified_count=ratio,
benifit_list=["雨果"],
sim_instance=self.buff_instance.sim_instance,
)
stun_value_feed_back_ratio = min(rest_tick / 60, 5) * 0.05
if self.record.char.cinema >= 1:
cinema_1_buff_index = self.record.cinema_1_buff_index
buff_add_strategy(
cinema_1_buff_index,
benifit_list=["雨果"],
sim_instance=self.buff_instance.sim_instance,
)
if self.record.char.cinema >= 2:
cinema_2_buff_index = self.record.cinema_2_buff_index
buff_add_strategy(
cinema_2_buff_index,
benifit_list=["雨果"],
sim_instance=self.buff_instance.sim_instance,
)
if self.record.char.cinema == 6:
cinema_6_buff_index = self.record.cinema_6_buff_index
buff_add_strategy(
cinema_6_buff_index,
benifit_list=["雨果"],
sim_instance=self.buff_instance.sim_instance,
)
"""再生成决算的skill_node"""
from zsim.sim_progress.Load import LoadingMission
from zsim.sim_progress.Preload.SkillsQueue import spawn_node
if self.record.active_signal == 2:
node_tag = self.record.E_totalize_tag
elif self.record.active_signal == 6:
node_tag = self.record.Q_totalize_tag
else:
raise ValueError("雨果的决算触发器的Xjudge函数放行了,但是给出的信号不是强化E、大招")
totalize_node = spawn_node(
node_tag,
find_tick(sim_instance=self.buff_instance.sim_instance),
self.record.preload_data.skills,
)
"""给予技能节点一个loading_mission"""
totalize_node.loading_mission = LoadingMission(totalize_node)
totalize_node.loading_mission.mission_start(
find_tick(sim_instance=self.buff_instance.sim_instance)
)
event_list.append(totalize_node)
"""失衡状态强制结算事件"""
from zsim.sim_progress.data_struct import StunForcedTerminationEvent
if self.record.enemy.dynamic.stun:
if self.record.char.cinema >= 2 and self.record.active_signal == 6:
if HUGO_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print("2画触发:检测到雨果释放大招,根据2画效果,本次决算不终结失衡状态!")
stun_event = None
else:
stun_event = StunForcedTerminationEvent(
self.record.enemy,
stun_value_feed_back_ratio,
execute_tick=find_tick(sim_instance=self.buff_instance.sim_instance),
event_source="雨果",
)
else:
if self.record.char.cinema != 6:
raise ValueError(
"雨果的决算触发器的Xjudge函数放行了,但是敌人并未处于失衡状态,这对于非6画雨果来说是不允许的。"
)
elif self.record.active_signal != 2:
raise ValueError(
f"触发信号为{self.record.active_signal},这意味着6画雨果的大招在非失衡期触发了决算,这是不允许的"
)
else:
stun_event = None
"""2画以上的大招触发决算时,不结算失衡状态。"""
if stun_event is not None:
event_list.append(stun_event)
"""重置信号"""
self.record.active_signal = None
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/IceJadeTeaPotExtraDMGBonus.py
================================================
from .. import Buff, JudgeTools
class IceJadeTeaPotExtraDMGBonus(Buff.BuffLogic):
"""
青衣专武>=15层时的额外增伤触发判定。
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
def special_judge_logic(self, **kwargs):
equipper = JudgeTools.find_equipper(
"玉壶青冰", sim_instance=self.buff_instance.sim_instance
)
dynamic_buff_list = JudgeTools.find_dynamic_buff_list(
sim_instance=self.buff_instance.sim_instance
)
for buffs in dynamic_buff_list[equipper]:
if "玉壶青冰-普攻加冲击" not in buffs.ft.index:
continue
if buffs.dy.count >= 15:
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/JaneAdditionalAbilityPhyBuildupBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class JaneAdditionalAbilityPhyBuildupBonusRecord:
def __init__(self):
self.char = None
self.trigger_buff_0 = None
self.dynamic_buff_list = None
self.enemy = None
self.sub_exist_buff_dict = None
class JaneAdditionalAbilityPhyBuildupBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""简组队被动中第二特效的复杂逻辑"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["简"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = JaneAdditionalAbilityPhyBuildupBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""简组队被动的第二特效是:只要有敌人处于异常状态即可触发,所以只要有任意一种异常处于激活状态,就可以放行。"""
self.check_record_module()
self.get_prepared(char_CID=1261, enemy=1)
return self.record.enemy.dynamic.is_under_anomaly()
def special_exit_logic(self, **kwargs):
"""此Buff退出逻辑和触发逻辑相反"""
return not self.special_judge_logic()
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/JaneCinema1APTransToDmgBonus.py
================================================
from zsim.sim_progress.ScheduledEvent.Calculator import (
Calculator as Cal,
)
from zsim.sim_progress.ScheduledEvent.Calculator import (
MultiplierData as Mul,
)
from .. import Buff, JudgeTools, check_preparation, find_tick
class JaneCinema1APTransToDmgBonusRecord:
def __init__(self):
self.char = None
self.trigger_buff_0 = None
self.dynamic_buff_list = None
self.enemy = None
self.sub_exist_buff_dict = None
class JaneCinema1APTransToDmgBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""1画的狂热状态下的精通转模增伤buff的复杂逻辑"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.xexit = self.special_exit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["简"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = JaneCinema1APTransToDmgBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""简的1画精通转增伤部分,触发逻辑和狂热触发器挂钩;"""
self.check_record_module()
self.get_prepared(char_CID=1261, trigger_buff_0=("简", "Buff-角色-简-狂热状态触发器"))
if self.record.trigger_buff_0.dy.active:
return True
else:
return False
def special_hit_logic(self, **kwargs):
"""当触发器激活时,执行self.xhit,计算实时精通,转化为增伤层数。"""
self.check_record_module()
self.get_prepared(
char_CID=1261,
trigger_buff_0=("简", "Buff-角色-简-狂热状态触发器"),
dynamic_buff_list=1,
enemy=1,
sub_exist_buff_dict=1,
)
mul_data = Mul(self.record.enemy, self.record.dynamic_buff_list, self.record.char)
ap = Cal.AnomalyMul.cal_ap(mul_data)
count = min(ap * 0.1, self.buff_instance.ft.maxcount)
tick = find_tick(sim_instance=self.buff_instance.sim_instance)
self.buff_instance.simple_start(tick, self.record.sub_exist_buff_dict, no_count=1)
self.buff_instance.dy.count = count
self.buff_instance.update_to_buff_0(self.buff_0)
def special_exit_logic(self, **kwargs):
"""精通转增伤Buff的退出逻辑与触发器相反"""
return not self.special_judge_logic()
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/JaneCoreSkillStrikeCritDmgBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class JaneCoreSkillStrikeCritDmgBonusRecord:
def __init__(self):
self.char = None
self.trigger_buff_0 = None
self.dynamic_buff_list = None
self.enemy = None
self.sub_exist_buff_dict = None
class JaneCoreSkillStrikeCritDmgBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""简核心被动中,强击暴击伤害的复杂逻辑"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["简"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = JaneCoreSkillStrikeCritDmgBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""强击的暴伤Debuff情况是和啮咬绑定的。"""
self.check_record_module()
self.get_prepared(
char_CID=1261, trigger_buff_0=("enemy", "Buff-角色-简-核心被动-啮咬触发器")
)
if self.record.trigger_buff_0.dy.active:
return True
else:
return False
def special_exit_logic(self, **kwargs):
"""此Buff退出逻辑和触发逻辑相反"""
return not self.special_judge_logic()
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/JaneCoreSkillStrikeCritRateBonus.py
================================================
from zsim.sim_progress.ScheduledEvent.Calculator import (
Calculator as Cal,
)
from zsim.sim_progress.ScheduledEvent.Calculator import (
MultiplierData as Mul,
)
from .. import Buff, JudgeTools, check_preparation, find_tick
class JaneCoreSkillStrikeCritRateBonusRecord:
def __init__(self):
self.char = None
self.trigger_buff_0 = None
self.dynamic_buff_list = None
self.enemy = None
self.sub_exist_buff_dict = None
class JaneCoreSkillStrikeCritRateBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""简核心被动中,强击暴击率的复杂逻辑"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.xexit = self.special_exit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["简"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = JaneCoreSkillStrikeCritRateBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""强击暴击率的Debuff情况是和啮咬绑定的。"""
self.check_record_module()
self.get_prepared(
char_CID=1261, trigger_buff_0=("enemy", "Buff-角色-简-核心被动-啮咬触发器")
)
if self.record.trigger_buff_0.dy.active:
return True
else:
return False
def special_hit_logic(self, **kwargs):
"""当触发器激活时,执行self.xhit,计算实时精通,转化成暴击率层数。"""
self.check_record_module()
self.get_prepared(
char_CID=1261,
trigger_buff_0=("enemy", "Buff-角色-简-核心被动-啮咬触发器"),
dynamic_buff_list=1,
enemy=1,
sub_exist_buff_dict=1,
)
mul_data = Mul(self.record.enemy, self.record.dynamic_buff_list, self.record.char)
ap = Cal.AnomalyMul.cal_ap(mul_data)
count = min(40 + ap * 0.16, 100)
tick = find_tick(sim_instance=self.buff_instance.sim_instance)
self.buff_instance.simple_start(tick, self.record.sub_exist_buff_dict, no_count=1)
self.buff_instance.dy.count = count
self.buff_instance.update_to_buff_0(self.buff_0)
def special_exit_logic(self, **kwargs):
"""此Buff退出逻辑和触发逻辑相反"""
return not self.special_judge_logic()
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/JanePassionStateAPTransToATK.py
================================================
from math import floor
from zsim.sim_progress.ScheduledEvent.Calculator import (
Calculator as Cal,
)
from zsim.sim_progress.ScheduledEvent.Calculator import (
MultiplierData as Mul,
)
from .. import Buff, JudgeTools, check_preparation, find_tick
class JanePassionStateAPTransToATKRecord:
def __init__(self):
self.char = None
self.trigger_buff_0 = None
self.dynamic_buff_list = None
self.enemy = None
self.sub_exist_buff_dict = None
class JanePassionStateAPTransToATK(Buff.BuffLogic):
def __init__(self, buff_instance):
"""狂热状态下的精通转攻击力"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.xexit = self.special_exit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["简"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = JanePassionStateAPTransToATKRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""精通转攻击力部分的触发行为与触发器对齐;"""
self.check_record_module()
self.get_prepared(char_CID=1261, trigger_buff_0=("简", "Buff-角色-简-狂热状态触发器"))
if self.record.trigger_buff_0.dy.active:
return True
else:
return False
def special_hit_logic(self, **kwargs):
"""当触发器激活时,执行self.xhit,计算实时精通,激活自身状态并且更新层数。"""
self.check_record_module()
self.get_prepared(
char_CID=1261,
trigger_buff_0=("简", "Buff-角色-简-狂热状态触发器"),
dynamic_buff_list=1,
enemy=1,
sub_exist_buff_dict=1,
)
mul_data = Mul(self.record.enemy, self.record.dynamic_buff_list, self.record.char)
ap = Cal.AnomalyMul.cal_ap(mul_data)
count = floor(
max(ap - 120, 0)
) # 超过120点的部分,每1点叠1层,这里应该是向下取证,比如120.1,那就不叠层。
tick = find_tick(sim_instance=self.buff_instance.sim_instance)
self.buff_instance.simple_start(tick, self.record.sub_exist_buff_dict, no_count=1)
self.buff_instance.dy.count = count
self.buff_instance.update_to_buff_0(self.buff_0)
def special_exit_logic(self, **kwargs):
"""精通转攻击力Buff的退出逻辑与触发器相反"""
return not self.special_judge_logic()
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/JanePassionStatePhyBuildupBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class JanePassionStatePhyBuildupBonusRecord:
def __init__(self):
self.char = None
self.trigger_buff_0 = None
class JanePassionStatePhyBuildupBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""狂热状态下的积蓄效率的判定逻辑"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["简"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = JanePassionStatePhyBuildupBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""积蓄效率Buff的判定和触发器有关,其状态和触发器相同"""
self.check_record_module()
self.get_prepared(char_CID=1261, trigger_buff_0=("简", "Buff-角色-简-狂热状态触发器"))
if self.record.trigger_buff_0.dy.active:
return True
else:
return False
def special_exit_logic(self, **kwargs):
"""积蓄效率的退出逻辑与触发器相反"""
return not self.special_judge_logic()
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/JanePassionStateTrigger.py
================================================
from .. import Buff, JudgeTools, check_preparation
class JanePassionStateTriggerRecord:
def __init__(self):
self.char = None
class JanePassionStateTrigger(Buff.BuffLogic):
def __init__(self, buff_instance):
"""简单的狂热状态触发器"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["简"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = JanePassionStateTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""简的狂热状态触发器,其取值狂热状态同步"""
self.check_record_module()
self.get_prepared(char_CID=1261)
passion_state = self.record.char.get_special_stats().get("狂热状态")
if passion_state is None:
raise ValueError(f"{self.buff_instance.ft.index} 的xjudge模块并未获取到简的狂热状态!")
if passion_state:
return True
else:
return False
def special_exit_logic(self, **kwargs):
"""简的狂热状态触发器的退出逻辑,和触发函数持相反逻辑"""
return not self.special_judge_logic()
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/KaboomTheCannon.py
================================================
from .. import Buff, JudgeTools, check_preparation
class KaboomTheCannonRecord:
def __init__(self):
self.equipper = None
self.char = None
self.action_stack = None
self.active_char_dict = {}
self.sub_exist_buff_dict = None
class KaboomTheCannon(Buff.BuffLogic):
"""
好斗的阿炮的复杂逻辑模块。主要是“1人只能提供1层”这个部分的约束
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xhit = self.special_hit_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"好斗的阿炮", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
"""
这里的初始化,找到的buff_0实际上是佩戴者的buff_0
"""
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = KaboomTheCannonRecord()
self.record = self.buff_0.history.record
def special_hit_logic(self, **kwargs):
"""主要归档触发源。"""
# TODO: 等三只小猪加入了,可能还得重新弄。
self.check_record_module()
self.get_prepared(equipper="好斗的阿炮", action_stack=1, sub_exist_buff_dict=1)
action_now = self.record.action_stack.peek()
tick_now = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
self.record.active_char_dict[action_now.mission_character] = [
tick_now,
tick_now + self.buff_instance.ft.maxduration,
]
for names, tick_list in self.record.active_char_dict.copy().items():
if tick_list[1] <= tick_now:
del self.record.active_char_dict[names]
self.buff_instance.simple_start(tick_now, self.record.sub_exist_buff_dict, not_count=True)
input_list = list(self.record.active_char_dict.values())
self.buff_instance.dy.built_in_buff_box = input_list
self.buff_instance.update_to_buff_0(self.buff_0)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/LighterAdditionalAbility_IceFireBonus.py
================================================
from zsim.sim_progress.ScheduledEvent.Calculator import Calculator, MultiplierData
from .. import Buff, JudgeTools, check_preparation
class LighterExtraSkillRecord:
def __init__(self):
self.char = None
self.enemy = None
self.dynamic_buff_list = None
self.sub_exist_buff_dict = None
self.real_count = 0
class LighterExtraSkill_IceFireBonus(Buff.BuffLogic):
"""
这个buff的特性是:能叠20层,每层起码有1.25%冰火增伤。但是,每次层数更新时,都会检测冲击力,
冲击力超过170的部分,都会以0.025%的数值增幅到这个1.25%的基础数值上。
模型转换:
以0.25%为一层,本buff最多300层。
buff本身层数分为两种,一种为虚层(fake_count),另一种为实层(real_count)。
实层根据命中次数,每次5层(共1.25%)稳定增加,每次实层更新后(self.update_to_buff0,该函数无法调用,需要手动)
那么有效叠加次数:hit_count = real_count / 5,而且这个一定是int。
根据当前冲击力超过170的部分,算出每个effect_count所享受的虚层增幅fake_count_delta : (当前冲击力-170)/10,该数值可以是个小数
实层传回buff_0的专用字段 buff.history.real_count 保存,下一次再拿。
本tick的实际生效层数,也就是最后记录到self.dy.count 的算法是:
self.dy.count = min(real_count + hit_count * fake_count_delta, 300)
下一个ticks,虚层清空,重新计算,实层重新从buff_0拿过来\
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
# 初始化特定逻辑
self.xhit = self.special_hit_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["莱特"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = LighterExtraSkillRecord()
self.record = self.buff_0.history.record
def special_hit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1161, enemy=1, dynamic_buff_list=1, sub_exist_buff_dict=1)
tick_now = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
buff_i = self.buff_instance
buff_i.simple_start(tick_now, self.record.sub_exist_buff_dict)
# 由于buff.dy.count的最终修改逻辑是直接赋值,不涉及累加。所以这里还原simple_start的步骤应该是多余的。
# buff_i.dy.count -= buff_i.ft.step
"""
先处理real_count的逻辑,最多100层(叠加20次)
只要该模块执行了,那就说明又命中了一次,自然要+5层。
但是这里用作计算的层数,不能来自simple_start之后的buff.dy.count,
而应该是来自于record.real_count。
这一步实现了命中叠加最基本层数,并且实时更新到realcount中。
"""
real_count = min(self.record.real_count + buff_i.ft.step, 100)
self.record.real_count = real_count
# 计算实时冲击力
mul_data = MultiplierData(
self.record.enemy, self.record.dynamic_buff_list, self.record.char
)
stun_value = Calculator.StunMul.cal_imp(mul_data)
# 计算虚层
fake_count_delta = max((stun_value - 170) / 10, 0)
sum_fake_count = real_count / 5 * fake_count_delta
# 计算等效的实际生效层数
buff_i.dy.count = min(real_count + sum_fake_count, 300)
buff_i.update_to_buff_0(self.buff_0)
# print('buff_i:', main_module.tick, buff_i.dy.active, buff_i.dy.startticks, buff_i.dy.endticks, real_count, sum_fake_count)
# print('buff_0:', buff_0.dy.active, buff_0.dy.startticks, buff_0.dy.endticks, buff_0.history.real_count)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/LighterUniqueSkillStunBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class LighterUniqueSkillStunBonusRecord:
def __init__(self):
self.last_morale = 0
self.last_morale_delta = 0
self.buff_count = 0
self.sub_exist_buff_dict = None
self.char = None
class LighterUniqueSkillStunBonus(Buff.BuffLogic):
"""
该buff是复杂判断 + 复杂生效双代码控制。
检测莱特士气的变化。如果发生了变化,则返回True
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xeffect = self.special_effect_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["莱特"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = LighterUniqueSkillStunBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
调用这个方法的位置,应该是buff_0的xjudge,所以,有效的self.buff_count也是存在buff_0里面的。
"""
self.check_record_module()
self.get_prepared(char_CID=1161)
if self.record.last_morale > self.record.char.morale:
# 检测到士气的下降之后,计算士气差,并且转化为层数预存起来。
self.record.last_morale_delta = (
self.record.last_morale - self.record.char.morale
) / 100
self.record.buff_count = self.record.last_morale_delta
self.record.last_morale = self.record.char.morale
# 暂时假设不向下取整。
return True
else:
self.record.last_morale = self.record.char.morale
return False
def special_effect_logic(self):
"""
这个方法需要在xjudge通过之后调用,此时调用的是buff_new的xeffect。
所以这里需要向buff_0获取它的的层数。
"""
self.check_record_module()
self.get_prepared(char_CID=1161, sub_exist_buff_dict=1)
buff_i = self.buff_instance
tick = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
buff_i.simple_start(tick, self.record.sub_exist_buff_dict)
buff_i.dy.count -= buff_i.ft.step
buff_i.dy.count = min(buff_i.dy.count + self.record.buff_count, buff_i.ft.maxcount)
buff_i.update_to_buff_0(self.buff_0)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/LighterUniqueSkillStunTimeLimitBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class LighterUniqueSkillStunTimeRecord:
def __init__(self):
self.last_stun_statement = False
self.enemy = None
class LighterUniqueSkillStunTimeLimitBonus(Buff.BuffLogic):
"""
该buff的退出逻辑特殊,失衡结束就会直接退出。
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xexit = self.special_exit_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["莱特"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = LighterUniqueSkillStunTimeRecord()
self.record = self.buff_0.history.record
def special_exit_logic(self, **kwargs):
"""
获取当前失衡值,和上一次失衡值对比。
"""
self.check_record_module()
self.get_prepared(enemy=1)
if self.record.last_stun_statement and not self.record.enemy.dynamic.stun:
self.record.last_stun_statement = self.record.enemy.dynamic.stun
return True
else:
self.record.last_stun_statement = self.record.enemy.dynamic.stun
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/LinaAdditionalSkillEleDMGBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class LinaAdditionalSkillRecord:
def __init__(self):
self.enemy = None
class LinaAdditionalSkillEleDMGBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""
丽娜组队被动:感电增加全队电伤
"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["丽娜"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = LinaAdditionalSkillRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(enemy=1)
if self.record.enemy.dynamic.shock:
return True
else:
return False
def special_exit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(enemy=1)
if not self.record.enemy.dynamic.shock:
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/LinaCoreSkillPenRatioBonus.py
================================================
from zsim.sim_progress.ScheduledEvent.Calculator import (
Calculator as Cal,
)
from zsim.sim_progress.ScheduledEvent.Calculator import (
MultiplierData as Mul,
)
from .. import Buff, JudgeTools, check_preparation
class LinaCoreSkillRecord:
def __init__(self):
self.action_stack = None
self.char = None
self.enemy = None
self.dynamic_buff_list = None
self.sub_exist_buff_dict = None
class LinaCoreSkillPenRatioBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""
丽娜核心被动,穿透率增幅。
"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xstart = self.special_start_logic
self.xexit = self.special_exit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["丽娜"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = LinaCoreSkillRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
只要不是重击,就都触发。
"""
self.check_record_module()
self.get_prepared(action_stack=1)
if self.record.action_stack.peek().mission_tag == "1211_SNA_1":
return False
else:
return True
def special_start_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(
action_stack=1,
char_CID=1211,
dynamic_buff_list=1,
enemy=1,
sub_exist_buff_dict=1,
)
tick_now = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
self.buff_instance.simple_start(tick_now, self.record.sub_exist_buff_dict)
self.buff_0.dy.count -= self.buff_0.ft.step
mul_data = Mul(self.record.enemy, self.record.dynamic_buff_list, self.record.char)
pen_ratio = Cal.RegularMul.cal_pen_ratio(mul_data)
count = min(pen_ratio * 0.2 * 100 + 12, self.buff_instance.ft.maxcount)
self.buff_instance.dy.count = count
self.buff_instance.update_to_buff_0(self.buff_0)
def special_exit_logic(self, **kwargs):
"""
只要检测到重击,就立刻终止。
"""
self.check_record_module()
self.get_prepared(action_stack=1)
if self.record.action_stack.peek().mission_tag != "1211_SNA_1":
tick = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
if self.buff_instance.dy.endticks <= tick:
return True
return False
else:
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/LunarNoviluna.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class LunarNovilunaRecord:
def __init__(self):
self.equipper = None
self.char = None
self.enegy_value_map = {1: 3, 2: 3.5, 3: 4, 4: 4.5, 5: 5}
self.sub_exist_buff_dict = None
class LunarNoviluna(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xstart = self.special_start_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"「月相」-朔", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = LunarNovilunaRecord()
self.record = self.buff_0.history.record
def special_start_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="「月相」-朔", sub_exist_buff_dict=1)
from zsim.sim_progress.data_struct import ScheduleRefreshData
event_list = JudgeTools.find_event_list(sim_instance=self.buff_instance.sim_instance)
energy_value = self.record.enegy_value_map[self.buff_instance.ft.refinement]
refresh_data = ScheduleRefreshData(
sp_target=(self.record.char.NAME,),
sp_value=energy_value,
)
event_list.append(refresh_data)
self.buff_instance.simple_start(
find_tick(sim_instance=self.buff_instance.sim_instance),
self.record.sub_exist_buff_dict,
)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/LyconAdditionalAbilityStunVulnerability.py
================================================
from .. import Buff, JudgeTools, check_preparation
class LyconAdditionalAbility:
def __init__(self):
self.last_stun_statement = False
self.action_stack = None
self.enemy = None
self.info_dict = None
class LyconAdditionalAbilityStunVulnerability(Buff.BuffLogic):
"""
该buff的退出逻辑特殊,失衡结束就会直接退出。
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["莱卡恩"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = LyconAdditionalAbility()
self.record = self.buff_0.history.record
def special_exit_logic(self, **kwargs):
"""
该buff和莱特的核心被动失衡时间延长是一样的,都是要在失衡期消失的时候检测退出。
获取当前失衡值,和上一次失衡值对比。
"""
self.check_record_module()
self.get_prepared(enemy=1)
if self.record.last_stun_statement and not self.record.enemy.dynamic.stun:
self.record.last_stun_statement = self.record.enemy.dynamic.stun
return True
else:
self.record.last_stun_statement = self.record.enemy.dynamic.stun
return False
def special_judge_logic(self, **kwargs):
"""
进入机制。获取当前skillNode并且检测当前怪物的失衡状态,两者都符合才触发。
"""
self.check_record_module()
self.get_prepared(enemy=1, action_stack=1)
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
if "1141" in skill_node.skill_tag and self.record.enemy.dynamic.stun:
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/MagneticStormAlphaAMBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class MagneticStormAlphaAMBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
self.sub_exist_buff_dict = None
class MagneticStormAlphaAMBonus(Buff.BuffLogic):
"""电磁暴1式判定逻辑"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"「电磁暴」-壹式", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = MagneticStormAlphaAMBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""只要造成了积蓄值,就放行"""
self.check_record_module()
self.get_prepared(equipper="「电磁暴」-壹式")
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Load import LoadingMission
from zsim.sim_progress.Preload import SkillNode
if isinstance(skill_node, SkillNode):
pass
elif isinstance(skill_node, LoadingMission):
skill_node = skill_node.mission_node
else:
return False
# 滤去不是自己的技能
if self.record.equipper != skill_node.char_name:
return False
if (
skill_node.skill.anomaly_accumulation != 0
and skill_node.skill.element_damage_percent > 0
):
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/MagneticStormBravoApBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class MagneticStormBravoApBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
self.sub_exist_buff_dict = None
class MagneticStormBravoApBonus(Buff.BuffLogic):
"""电磁暴2式判定逻辑"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"「电磁暴」-贰式", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = MagneticStormBravoApBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""只要造成了积蓄值,就放行"""
self.check_record_module()
self.get_prepared(equipper="「电磁暴」-贰式")
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Load import LoadingMission
from zsim.sim_progress.Preload import SkillNode
if isinstance(skill_node, SkillNode):
pass
elif isinstance(skill_node, LoadingMission):
skill_node = skill_node.mission_node
else:
return False
# 滤去不是自己的技能
if self.record.equipper != skill_node.char_name:
return False
if (
skill_node.skill.anomaly_accumulation != 0
and skill_node.skill.element_damage_percent > 0
):
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/MagneticStormCharlieSpRecover.py
================================================
from .. import Buff, JudgeTools, check_preparation
class MagneticStormCharlieSpRecoverRecord:
def __init__(self):
self.equipper = None
self.char = None
self.sub_exist_buff_dict = None
self.energy_value_dict = {1: 3.5, 2: 4, 3: 4.5, 4: 5, 5: 5.5}
class MagneticStormCharlieSpRecover(Buff.BuffLogic):
"""电磁暴3式判定逻辑"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"「电磁暴」-叁式", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = MagneticStormCharlieSpRecoverRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""只要造成了积蓄值,就放行"""
self.check_record_module()
self.get_prepared(equipper="「电磁暴」-叁式")
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Load import LoadingMission
from zsim.sim_progress.Preload import SkillNode
if isinstance(skill_node, SkillNode):
pass
elif isinstance(skill_node, LoadingMission):
skill_node = skill_node.mission_node
else:
return False
# 滤去不是自己的技能
if self.record.equipper != skill_node.char_name:
return False
if (
skill_node.skill.anomaly_accumulation != 0
and skill_node.skill.element_damage_percent > 0
):
return True
return False
def special_hit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="「电磁暴」-叁式", sub_exist_buff_dict=1)
tick_now = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
self.buff_instance.simple_start(tick_now, self.record.sub_exist_buff_dict)
energy_value = self.record.energy_value_dict[int(self.buff_instance.ft.refinement)]
event_list = JudgeTools.find_event_list(sim_instance=self.buff_instance.sim_instance)
from zsim.sim_progress.data_struct import ScheduleRefreshData
refresh_data = ScheduleRefreshData(
sp_target=(self.record.char.NAME,),
sp_value=energy_value,
)
event_list.append(refresh_data)
print("电磁暴3式回能触发!")
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/MarcatoDesireAtkBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class MarcatoDesireRecord:
def __init__(self):
self.equipper = None
self.char = None
self.enemy = None
class MarcatoDesireAtkBonus(Buff.BuffLogic):
"""强音热望的复杂逻辑:连携技或强化E命中属性异常状态下敌人时触发"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"强音热望", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = MarcatoDesireRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="强音热望", enemy=1)
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise ValueError(
f"{self.buff_instance.ft.index}的Xjudge函数获取的skill_node不是SkillNode类型!"
)
if skill_node.char_name != self.record.char.NAME:
return False
if not skill_node.is_hit_now(find_tick(sim_instance=self.buff_instance.sim_instance)):
return False
if skill_node.skill.trigger_buff_level in [2, 5]:
if self.record.enemy.dynamic.is_under_anomaly():
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/MetanukiMorphosisAPBonus.py
================================================
from typing import TYPE_CHECKING
from .. import Buff, JudgeTools, check_preparation
if TYPE_CHECKING:
from zsim.sim_progress.Preload import SkillNode
class MetanukiMorphosisAPBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
class MetanukiMorphosisAPBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record: MetanukiMorphosisAPBonusRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"狸法七变化", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = MetanukiMorphosisAPBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""检测到装备者的追加攻击时放行,但是需要注意此效果只能生效一个"""
self.check_record_module()
self.get_prepared(equipper="狸法七变化")
skill_node: "SkillNode" = kwargs.get("skill_node")
if skill_node is None:
return False
if skill_node.char_name != self.record.char.NAME:
return False
if not skill_node.have_label(label_key="aftershock_attack"):
return False
if skill_node.is_hit_now(tick=self.buff_instance.sim_instance.tick):
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/MiyabiAdditionalAbility_IgnoreIceRes.py
================================================
from .. import Buff, JudgeTools, check_preparation
anomaly_name_list = ["frostbite", "assault", "shock", "burn", "corruption"]
class MiyabiAdditionalAbility:
def __init__(self):
self.anomaly_state = {name: False for name in anomaly_name_list}
self.disorder = False
self.effect_count = 0
self.action_stack = None
self.enemy = None
class MiyabiAdditionalAbility_IgnoreIceRes(Buff.BuffLogic):
"""
该buff为雅的组队被动中,紊乱后蓄力重击无视冰抗,
该Buff需要检测紊乱,这一判定必须对比:当前tick下enemy的状态 和 本buff逻辑模块内记录的上一次判定过程中的enemy状态,
重点在于,必须保证当前tick和上一次记录的tick只相差1,这样才不会误判紊乱的发生。
主要需要规避的情况:
1、第n个ticks检测到霜寒,
2、n+1ticks 时,角色被切到后台了,
3、第n+x个ticks的时候,角色重新回到前台,并且检测到灼烧,
那么这里就会自然而然判定为“disorder”,这是误判。
由于紊乱的产生只有1个tick,要准确检测到紊乱,必须每个tick实时获取enemy的状态,
所以,该buff必须每个tick都被执行一次判定逻辑,
我修改了buff的backend_active参数,这样,雅在前台时,它能通过Load的主逻辑进行判断
而角色在后台时,也可以通过Load的副逻辑,passively_updating == True且backend_active == True的分支,来执行判断。
检测到紊乱发生后,buff的生效次数 effect_count 自增1,最多1层,
只有当effect_count 的层数是1,且当前的动作(action_stack.peek()获取)
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["雅"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = MiyabiAdditionalAbility()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(enemy=1, action_stack=1)
action_now = self.record.action_stack.peek()
enemy = self.record.enemy
current_anomalies = {name: getattr(enemy.dynamic, name) for name in anomaly_name_list}
# 获取当前状态
current_count = sum(current_anomalies.values())
last_count = sum(self.record.anomaly_state.values())
# 计算两个list中的True的数量
if last_count >= 2 or current_count >= 2:
raise ValueError("当前ticks总异常数量为2!")
self.record.disorder = (
current_count == 1
and last_count == 1
and any(
current_anomalies[name] != self.record.anomaly_state[name]
for name in anomaly_name_list
)
)
# 当前后的True的数量都是1(意味着上一个Ticks有异常,这个Ticks也有异常),判断二者是否是同一个异常。如果不是,就修改Disorder为True
self.record.anomaly_state.update(current_anomalies)
if self.record.disorder:
self.record.effect_count = min(self.record.effect_count + 1, 1)
if self.record.effect_count > 0 and action_now.mission_tag in [
"1091_SNA_1",
"1091_SNA_2",
"1091_SNA_3",
]:
self.record.effect_count = 0
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/MiyabiCoreSkill_FrostBurn.py
================================================
from .. import Buff, JudgeTools, check_preparation
class MiyabiCoreSkillFB:
def __init__(self):
self.last_frostbite = False
self.enemy = None
class MiyabiCoreSkill_FrostBurn(Buff.BuffLogic):
"""
该buff是雅的核心被动中的【霜灼】,【霜灼】的进入机制是,随着烈霜属性异常触发,同步触发。
执行这一步的是:update_anomaly函数,该函数会在烈霜属性积蓄条满的时候,
根据bar.accompany_debuff中记录的str,去添加同名debuff。
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xexit = self.special_exit_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["雅"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = MiyabiCoreSkillFB()
self.record = self.buff_0.history.record
def special_exit_logic(self, **kwargs):
"""
霜灼buff的退出机制是检测到霜寒的下降沿就退出
"""
self.check_record_module()
self.get_prepared(enemy=1)
frostbite_now = self.record.enemy.dynamic.frost_frostbite
frostbite_statement = [self.record.last_frostbite, frostbite_now]
def mode_func(a, b):
return a is True and b is False
result = JudgeTools.detect_edge(frostbite_statement, mode_func)
self.record.last_frostbite = frostbite_now
return result
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/MiyabiCoreSkill_IceFire.py
================================================
from typing import TYPE_CHECKING
from zsim.sim_progress import Preload
from zsim.sim_progress.ScheduledEvent.Calculator import Calculator, MultiplierData
from .. import Buff, JudgeTools, check_preparation
if TYPE_CHECKING:
from zsim.sim_progress.Preload import SkillNode
class MiyabiCoreSkillIF:
def __init__(self):
self.char = None
self.sub_exist_buff_dict = None
self.dynamic_buff_list = None
self.last_frostbite = False
self.enemy = None
self.action_stack = None
class MiyabiCoreSkill_IceFire(Buff.BuffLogic):
"""
该buff是雅的核心被动中的【冰焰】,冰焰在判断TrigerBuffLevel的同时,
还需要检索当前enemy_debuff_list中是否含有【霜灼】,如果有就返回False
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.xexit = self.special_exit_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["雅"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = MiyabiCoreSkillIF()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
这个复杂判断逻辑需要同时检索当前技能的element_type,
以及enemy的debuff_list有没有霜灼,
两者都通过,才会return True
"""
self.check_record_module()
self.get_prepared(char_CID=1091, enemy=1, action_stack=1)
enemy = self.record.enemy
debuff_list = enemy.dynamic.dynamic_debuff_list
skill_node: "SkillNode" = kwargs.get("skill_node")
if skill_node is None:
return False
if skill_node.char_name != self.record.char.NAME:
return False
if skill_node.skill.element_type != 5:
return False
else:
for debuff in debuff_list:
if not isinstance(debuff, Buff):
raise TypeError(f"{debuff}不是Buff类!")
if debuff.ft.index == "Buff-角色-雅-核心被动-霜灼":
return False
else:
return True
def special_exit_logic(self, **kwargs):
"""
冰焰buff的退出机制是检测到霜寒的上升沿就退出
"""
self.check_record_module()
self.get_prepared(char_CID=1091, enemy=1)
enemy = self.record.enemy
frostbite_now = enemy.dynamic.frost_frostbite
if frostbite_now is None:
frostbite_now = False
frostbite_statement = [self.record.last_frostbite, frostbite_now]
def mode_func(a, b):
return a is False and b is True
result = JudgeTools.detect_edge(frostbite_statement, mode_func)
self.record.last_frostbite = frostbite_now
# print(f'当前tick,冰焰退出情况:{result}')
if result:
event_list = JudgeTools.find_event_list(sim_instance=self.buff_instance.sim_instance)
skill_obj = self.record.char.skills_dict["1091_Core_Passive"]
skill_node = Preload.SkillNode(skill_obj, 0)
event_list.append(skill_node)
self.record.char.special_resources(skill_node)
return result
def special_hit_logic(self, **kwargs):
"""
冰焰的生效机制是:根据当前的暴击率,得出当前的Buff层数。
这个效果本应该是随动的,不需要buff判定通过才改变层数,
但是如果buff判定不通过,那么烈霜伤害,该buff层数的变动就没有实际意义,
"""
self.check_record_module()
self.get_prepared(char_CID=1091, enemy=1, dynamic_buff_list=1, sub_exist_buff_dict=1)
enemy = self.record.enemy
dynamic_buff = self.record.dynamic_buff_list
tick_now = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
buff_i = self.buff_instance
buff_i.simple_start(tick_now, self.record.sub_exist_buff_dict)
buff_i.dy.count -= buff_i.ft.step
mul_data = MultiplierData(enemy, dynamic_buff, self.record.char)
crit_rate = Calculator.RegularMul.cal_crit_rate(mul_data)
count = min(crit_rate, 0.8) * 100
# print(crit_rate, count)
buff_i.dy.count = min(count, self.buff_0.ft.maxcount)
buff_i.update_to_buff_0(self.buff_0)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/MoonlightLullabyAllTeamDmgBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class MoonlightLullabyAllTeamDmgBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
class MoonlightLullabyAllTeamDmgBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""这是月光骑士颂全队增伤Buff的脚本"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record: MoonlightLullabyAllTeamDmgBonusRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"月光骑士颂", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = MoonlightLullabyAllTeamDmgBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="月光骑士颂")
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/NikoleCoreSkillDefReduction.py
================================================
from .. import Buff, JudgeTools, check_preparation
class NicoleCoreSkillRecord:
def __init__(self):
self.action_stack = None
self.char = None
self.enemy = None
self.dynamic_buff_list = None
self.sub_exist_buff_dict = None
class NicoleCoreSkillDefReduction(Buff.BuffLogic):
def __init__(self, buff_instance):
"""
妮可的核心被动,减防。
"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["妮可"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = NicoleCoreSkillRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
目前这个buff的触发条件是简化过的。本来应该是检测“强化子弹”
"""
self.check_record_module()
self.get_prepared(action_stack=1)
if self.record.action_stack.peek().mission_tag == "1211_SNA_1":
return False
else:
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/PhaethonsMelody.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class PhaethonsMelodyRecord:
def __init__(self):
self.equipper = None
self.char = None
class PhaethonsMelody(Buff.BuffLogic):
"""法厄同之歌的复杂逻辑,以太增伤部分。"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"法厄同之歌", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = PhaethonsMelodyRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""法厄同之歌的复杂逻辑,监测到非装备者的强化E发动时放行。"""
self.check_record_module()
self.get_prepared(equipper="法厄同之歌")
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise TypeError(
f"{self.buff_instance.ft.index}的xjudge函数获取的skill_node不是SkillNode类!"
)
# 滤去自己的技能
if self.record.equipper == skill_node.char_name:
return False
# 过滤非强化E的技能
if skill_node.skill.trigger_buff_level != 2:
return False
tick = find_tick(sim_instance=self.buff_instance.sim_instance)
if skill_node.preload_tick == tick:
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/PolarMetalFreezeBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class PolarMetalRecord:
def __init__(self):
self.last_tick_freez_statement = 0, False
self.equipper = None
self.enemy = None
self.char = None
class PolarMetalFreezeBonus(Buff.BuffLogic):
"""
这是极地重金属的复杂逻辑判定。
主要检测的是碎冰的变化状态,如果碎冰状态变了,就返回True
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
# 初始化特定逻辑
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"极地重金属", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = PolarMetalRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(enemy=1)
enemy = self.record.enemy
tick = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
if enemy.dynamic.frozen is None:
output = False
else:
output = enemy.dynamic.frozen
this_tick_freez_statement = output
if this_tick_freez_statement != self.record.last_tick_freez_statement[1]:
self.record.last_tick_freez_statement = tick, this_tick_freez_statement
return True
else:
self.record.last_tick_freez_statement = tick, this_tick_freez_statement
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/PreciousFossilizedCoreStunBonusOver50Hp.py
================================================
from .. import Buff, JudgeTools
class PreciousFossilizedCoreStunBonusOver50Hp(Buff.BuffLogic):
"""
这段代码是贵重骨核的复杂判断逻辑。
敌人生命大于50%时生效。
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
# 初始化特定逻辑
self.xjudge = self.special_judge_logic
def special_judge_logic(self, **kwargs):
enemy = JudgeTools.find_enemy(sim_instance=self.buff_instance.sim_instance)
hp_pct = enemy.get_total_hp_percentage()
if hp_pct >= 0.5:
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/PreciousFossilizedCoreStunBonusOver75Hp.py
================================================
from .. import Buff, JudgeTools
class PreciousFossilizedCoreStunBonusOver75Hp(Buff.BuffLogic):
"""
这段代码是贵重骨核的复杂判断逻辑,
敌人生命值大于等于75%时返回True
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
# 初始化特定逻辑
self.xjudge = self.special_judge_logic
def special_judge_logic(self, **kwargs):
enemy = JudgeTools.find_enemy(sim_instance=self.buff_instance.sim_instance)
hp_pct = enemy.get_total_hp_percentage()
if hp_pct >= 0.75:
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/PuzzleSphereExDmgBonus.py
================================================
from typing import TYPE_CHECKING
from .. import Buff, JudgeTools, check_preparation
if TYPE_CHECKING:
from zsim.sim_progress.Preload import SkillNode
class PuzzleSphereExDmgBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
self.enemy = None
class PuzzleSphereExDmgBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"幻变魔方", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = PuzzleSphereExDmgBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""幻变魔方的特殊判定逻辑,强化E发动时,若敌人的血量高于50%,则放行。"""
self.check_record_module()
self.get_prepared(equipper="幻变魔方", enemy=1)
skill_node: "SkillNode | None" = kwargs.get("skill_node", None)
if skill_node is None:
return False
if skill_node.char_name != self.record.char.NAME:
return False
if skill_node.skill.trigger_buff_level != 2:
return False
if self.record.enemy.get_current_hp_percentage() < 0.5:
return False
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/QingYiAdditionalAbilityStunConvertToATK.py
================================================
from zsim.sim_progress.ScheduledEvent.Calculator import Calculator, MultiplierData
from .. import Buff, JudgeTools, check_preparation
class QingYiAdditionalSkillRecord:
def __init__(self):
self.char = None
self.enemy = None
self.sub_exist_buff_dict = None
self.dynamic_buff_list = None
class QingYiAdditionalAbilityStunConvertToATK(Buff.BuffLogic):
def __init__(self, buff_instance):
"""
青衣的组队被动之冲击力转模部分。
"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["青衣"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = QingYiAdditionalSkillRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
return True
def special_hit_logic(self, **kwargs):
"""
找冲击力,并且构建mul现场算。算完出层数即可。
"""
self.check_record_module()
self.get_prepared(char_CID=1251, enemy=1, dynamic_buff_list=1, sub_exist_buff_dict=1)
tick_now = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
self.buff_instance.simple_start(tick_now, self.record.sub_exist_buff_dict)
self.buff_0.dy.count -= self.buff_0.ft.step
mul_data = MultiplierData(
self.record.enemy, self.record.dynamic_buff_list, self.record.char
)
stun_value = Calculator.StunMul.cal_imp(mul_data)
count = min((stun_value - 120) * 6, self.buff_instance.ft.maxcount)
self.buff_instance.dy.count = count
self.buff_instance.update_to_buff_0(self.buff_0)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/QingYiCoreSkillExtraStunBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class QintYiCoreSkillExtraStunRecord:
"""
记录信息的类。从青衣开始,这些类要统一管理。
"""
def __init__(self):
self.last_update_voltage = 0
self.sub_exist_buff_dict = None
self.action_stack = None
self.count = 0
self.char = None
class QingYiCoreSkillExtraStunBonus(Buff.BuffLogic):
"""
青衣的核心被动:消耗电压时,
溢出的电压,每1%都会转化为一层额外的失衡值提升作为补偿;
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xstart = self.special_start_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["青衣"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = QintYiCoreSkillExtraStunRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
检测到SNA_1就为True,否则为False。
这个模块优先于Buff的Start逻辑,所以可以前置更新电压。
并且将计算出的触发情况,放到record里面预存起来。
SNA_1分支,会直接return True,不更新电压,但是直接计算层数。
SNA_2分支,会return True,因为也能吃到这个补偿Buff,且更新电压为0
其他分支,更新电压后,直接返回False
"""
self.check_record_module()
self.get_prepared(char_CID=1251, action_stack=1)
if self.record.action_stack.peek().mission_tag == "1251_SNA_1":
# 这个count哪怕每次SNA_1都计算也不要紧,因为SNA_1分支不会清空电压记录,
# 所以每次算出来都是一样的。
self.record.count = max(self.record.last_update_voltage - 75, 0)
return True
elif self.record.action_stack.peek().mission_tag == "1251_SNA_2":
self.record.last_update_voltage = 0
return True
else:
self.record.last_update_voltage = self.record.char.get_resources()[1]
return False
def special_start_logic(self, **kwargs):
"""
这里是启动逻辑。进入这一逻辑说明是SNA_1或者SNA_2的start标签。
此时,应该从record获取层数,并且激活buff。
"""
self.check_record_module()
self.get_prepared(char_CID=1251, sub_exist_buff_dict=1, action_stack=1)
tick_now = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
self.buff_instance.simple_start(tick_now, self.record.sub_exist_buff_dict)
self.buff_0.dy.count -= self.buff_0.ft.step
count = min(self.record.count, self.buff_instance.ft.maxcount)
self.buff_instance.dy.count = count - 1
self.buff_instance.update_to_buff_0(self.buff_0)
if self.record.action_stack.peek().mission_tag == "1251_SNA_2":
# 在增幅完SNA_2后,本轮次的record.count使命完成,进行重置。
self.record.count = 0
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/QingYiCoreSkillStunDMGBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class QintYiCoreSkillRecord:
"""
记录信息的类。从青衣开始,这些类要统一管理。
"""
def __init__(self):
self.pre_saved_counts = 0
self.last_update_stun = False
self.last_update_skill_tag = None
self.char = None
self.enemy = None
self.sub_exist_buff_dict = None
class QingYiCoreSkillStunDMGBonus(Buff.BuffLogic):
"""
青衣的核心被动:[羁服]——失衡易伤以及连携技增伤;
该buff有两个模块,分别是:XHit以及Xexit
Xhit根据当前的Tag来控制层数的变化;
而Xexit则在检测到失衡状态的下降沿时执行,输出True
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xstart = self.special_start_logic
self.xexit = self.special_exit_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["青衣"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = QintYiCoreSkillRecord()
self.record = self.buff_0.history.record
def special_start_logic(self, **kwargs):
"""
检测到当前的action_stack,判断它们的mission_tag
SNA_1叠1层, 且预叠1层;
SNA_2叠5层,且追加叠加所有的预叠层数。
"""
self.check_record_module()
self.get_prepared(char_CID=1251, sub_exist_buff_dict=1, enemy=1)
action_stack = JudgeTools.find_stack(sim_instance=self.buff_instance.sim_instance)
action_now = action_stack.peek()
last_action = action_stack.peek_bottom()
tick_now = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
self.buff_instance.simple_start(tick_now, self.record.sub_exist_buff_dict)
self.buff_0.dy.count -= 1
self.buff_instance.dy.count = self.buff_0.dy.count
if action_now.mission_tag == "1251_SNA_1":
if last_action.mission_tag != "1251_SNA_1":
"""上一个动作不是1251_SNA_1时,强制清空现有层数。"""
self.record.pre_saved_counts = 0
self.buff_instance.dy.count += 1
self.record.pre_saved_counts += 1
if self.record.pre_saved_counts > 5:
raise ValueError(
f"1251_SNA_1提供的预叠层数已经超过了5,\n"
f"当前为:{self.record.pre_saved_counts},\n"
f"应该是APL代码的逻辑有问题,请检查1251_SNA_2的释放逻辑!"
)
elif action_now.mission_tag == "1251_SNA_2":
self.buff_instance.dy.count += 5
self.buff_instance.dy.count += self.record.pre_saved_counts
self.record.pre_saved_counts = 0
self.buff_instance.dy.count = min(self.buff_instance.dy.count, 20)
self.buff_instance.update_to_buff_0(self.buff_0)
def special_exit_logic(self, **kwargs):
"""
退出逻辑:检测到失衡的下降沿。
"""
self.check_record_module()
self.get_prepared(char_CID=1251, sub_exist_buff_dict=1, enemy=1)
def mode_func(a, b):
return a is True and b is False
stun_statement_tuple = (
self.record.last_update_stun,
self.record.enemy.dynamic.stun,
)
if JudgeTools.detect_edge(stun_statement_tuple, mode_func):
self.record.last_update_stun = self.record.enemy.dynamic.stun
return True
self.record.last_update_stun = self.record.enemy.dynamic.stun
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/QingmingBirdcageCompanionEthDmgBonus.py
================================================
from typing import TYPE_CHECKING
from .. import Buff, JudgeTools, check_preparation
if TYPE_CHECKING:
from zsim.sim_progress.Character import Character
from zsim.sim_progress.Preload import SkillNode
from zsim.sim_progress.Preload.PreloadDataClass import PreloadData
from zsim.simulator.simulator_class import Simulator
class QingmingBirdcageCompanionEthDmgBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
self.sub_exist_buff_dict = None
self.preload_data = None
self.update_signal = None
class QingmingBirdcageCompanionEthDmgBonus(Buff.BuffLogic):
"""青溟笼舍的清明同行的复杂判定,这把武器拥有 以太增伤 以及 贯穿伤害两部分效果,这两部分效果共享同一个判定逻辑"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xstart = self.special_start_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"青溟笼舍", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = QingmingBirdcageCompanionEthDmgBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""这里有两种放行条件。第一种是装备者的强化E,第二种是刚刚进场时放行。"""
self.check_record_module()
self.get_prepared(equipper="青溟笼舍", preload_data=1)
preload_data: "PreloadData" = self.record.preload_data
char: "Character" = self.record.char
skill_node: "SkillNode | None" = kwargs.get("skill_node", None)
sim: "Simulator" = self.buff_instance.sim_instance
if skill_node is None:
return False
# 检测到第一个动作时放行
if skill_node.preload_tick != sim.tick:
return False
if skill_node.char_name != char.NAME:
return False
if len(preload_data.personal_node_stack[char.CID]) == 1:
if self.record.update_signal is not None:
raise ValueError(
f"{self.buff_instance.ft.index}的Xjudge函数检验到有尚未处理的更新信号{self.record.update_signal},本次更新请求来自于{skill_node.skill_tag}请检查XStart函数"
)
self.record.update_signal = 0
return True
else:
if skill_node.skill.trigger_buff_level == 2:
if self.record.update_signal is not None:
raise ValueError(
f"{self.buff_instance.ft.index}的Xjudge函数检验到有尚未处理的更新信号{self.record.update_signal},本次更新请求来自于{skill_node.skill_tag}请检查XStart函数"
)
self.record.update_signal = 1
return True
return False
def special_start_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="青溟笼舍", sub_exist_buff_dict=1)
sim: "Simulator" = self.buff_instance.sim_instance
if self.record.update_signal is None:
raise ValueError(
f"{self.buff_instance.ft.index}的XStart函数并未检测到有效的更新信号,请检查Xjudge函数!"
)
if self.record.update_signal == 0:
self.buff_instance.simple_start(
timenow=sim.tick,
sub_exist_buff_dict=self.record.sub_exist_buff_dict,
no_count=1,
)
self.buff_instance.dy.count = 2
self.buff_instance.update_to_buff_0(self.buff_0)
self.record.update_signal = None
elif self.record.update_signal == 1:
self.buff_instance.simple_start(
timenow=sim.tick, sub_exist_buff_dict=self.record.sub_exist_buff_dict
)
self.record.update_signal = None
else:
raise ValueError(f"无法解析的更新信号:{self.record.update_signal}")
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/QingmingBirdcageCompanionSheerAtkBonus.py
================================================
from typing import TYPE_CHECKING
from .. import Buff, JudgeTools, check_preparation
if TYPE_CHECKING:
from zsim.sim_progress.Character import Character
from zsim.sim_progress.Preload import SkillNode
from zsim.sim_progress.Preload.PreloadDataClass import PreloadData
from zsim.simulator.simulator_class import Simulator
class QingmingBirdcageCompanionSheerAtkBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
self.sub_exist_buff_dict = None
self.preload_data = None
self.update_signal = None
class QingmingBirdcageCompanionSheerAtkBonus(Buff.BuffLogic):
"""青溟笼舍的清明同行的复杂判定,这把武器拥有 以太增伤 以及 贯穿伤害两部分效果,这两部分效果共享同一个判定逻辑"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xstart = self.special_start_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"青溟笼舍", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = QingmingBirdcageCompanionSheerAtkBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""这里有两种放行条件。第一种是装备者的强化E,第二种是刚刚进场时放行。"""
self.check_record_module()
self.get_prepared(equipper="青溟笼舍", preload_data=1)
preload_data: "PreloadData" = self.record.preload_data
char: "Character" = self.record.char
skill_node: "SkillNode | None" = kwargs.get("skill_node", None)
sim: "Simulator" = self.buff_instance.sim_instance
if skill_node is None:
return False
# 检测到第一个动作时放行
if skill_node.char_name != char.NAME:
return False
if skill_node.preload_tick != sim.tick:
return False
if len(preload_data.personal_node_stack[char.CID]) == 1:
if self.record.update_signal is not None:
raise ValueError(
f"{self.buff_instance.ft.index}的Xjudge函数检验到有尚未处理的更新信号,请检查XStart函数"
)
self.record.update_signal = 0
return True
if skill_node.skill.trigger_buff_level == 2:
if self.record.update_signal is not None:
raise ValueError(
f"{self.buff_instance.ft.index}的Xjudge函数检验到有尚未处理的更新信号,请检查XStart函数"
)
self.record.update_signal = 1
return True
return False
def special_start_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="青溟笼舍", sub_exist_buff_dict=1)
sim: "Simulator" = self.buff_instance.sim_instance
if self.record.update_signal is None:
raise ValueError(
f"{self.buff_instance.ft.index}的XStart函数并未检测到有效的更新信号,请检查Xjudge函数!"
)
if self.record.update_signal == 0:
self.buff_instance.simple_start(
timenow=sim.tick,
sub_exist_buff_dict=self.record.sub_exist_buff_dict,
no_count=1,
)
self.buff_instance.dy.count = 2
self.buff_instance.update_to_buff_0(self.buff_0)
self.record.update_signal = None
elif self.record.update_signal == 1:
self.buff_instance.simple_start(
timenow=sim.tick, sub_exist_buff_dict=self.record.sub_exist_buff_dict
)
self.record.update_signal = None
else:
raise ValueError(f"无法解析的更新信号:{self.record.update_signal}")
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/RainforestGourmetATKBonus.py
================================================
import math
from zsim.sim_progress.Buff import Buff, JudgeTools, check_preparation, find_tick
class RainforestGourmetATKBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
self.sub_exist_buff_dict = None
self.last_update_node = None
class RainforestGourmetATKBonus(Buff.BuffLogic):
"""雨林饕客的局内攻击"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xstart = self.special_start_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"雨林饕客", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = RainforestGourmetATKBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""检测到强化E标签或是支援攻击标签,则放行。如果角色处于前台则更新1层,若角色处于后台则更新两层。"""
self.check_record_module()
self.get_prepared(equipper="雨林饕客")
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise TypeError
if skill_node.char_name != self.record.char.NAME:
return False
if skill_node.preload_tick != find_tick(sim_instance=self.buff_instance.sim_instance):
return False
if skill_node.skill.sp_consume == 0:
return False
self.record.last_update_node = skill_node
return True
def special_start_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="雨林饕客", sub_exist_buff_dict=1)
sp_consume = self.record.last_update_node.skill.sp_consume
count = math.floor(sp_consume / 10)
self.buff_instance.simple_start(
find_tick(sim_instance=self.buff_instance.sim_instance),
self.record.sub_exist_buff_dict,
individule_settled_count=count,
)
# print(f'雨林饕客的buff触发了!当前层数{self.buff_instance.dy.count}')
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/RiotSuppressorMarkVI.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class RiotSuppressorMarkVIRecord:
def __init__(self):
self.equipper = None
self.char = None
self.max_effect_times = 8
self.available_effect_times = 0
self.active_signal = None
self.sub_exist_buff_dict = None
class RiotSuppressorMarkVI(Buff.BuffLogic):
"""防暴者Ⅵ型的复杂逻辑"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xeffect = self.special_effect_logic
self.xexit = self.special_exit_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"防暴者Ⅵ型", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = RiotSuppressorMarkVIRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""检测到强化E和普攻都放行。强化E叠层,普攻消层。"""
self.check_record_module()
self.get_prepared(equipper="防暴者Ⅵ型")
if self.buff_0.dy.active and self.record.available_effect_times < 1:
raise ValueError(f"{self.buff_instance.ft.index}在可用层数耗尽的情况下仍保持激活状态!")
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise ValueError(
f"{self.buff_instance.ft.index}的xjudge函数获取的skill_node不是SkillNode类型!"
)
if self.record.char.NAME != skill_node.char_name:
return False
if skill_node.skill.trigger_buff_level not in [2, 0]:
return False
"""Buff的触发,还有生效次数的消耗,都只有在技能释放时才会执行。"""
if skill_node.preload_tick == find_tick(sim_instance=self.buff_instance.sim_instance):
signal = skill_node.skill.trigger_buff_level
if skill_node.skill.trigger_buff_level == 0:
if not self.buff_0.dy.active:
signal = None
else:
signal = None
if signal is not None:
if self.record.active_signal is not None:
raise ValueError(
f"{self.buff_instance.ft.index}的Xjudge函数检测到尚未结算的更新信号{self.record.active_signal}!"
)
self.record.active_signal = signal
return True
else:
return False
def special_effect_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="防暴者Ⅵ型", sub_exist_buff_dict=1)
if self.record.active_signal == 2:
"""更新信号为2是时,刷新Buff,叠加生效层数。"""
self.record.available_effect_times = min(
self.record.available_effect_times + self.record.max_effect_times,
self.record.max_effect_times,
)
self.buff_instance.simple_start(
find_tick(sim_instance=self.buff_instance.sim_instance),
self.record.sub_exist_buff_dict,
)
# print(
# f"防暴者VI型Buff触发了!当前可用次数为{self.record.available_effect_times}!"
# )
elif self.record.active_signal == 0:
"""更新信号为0时,消耗层数。"""
if self.record.available_effect_times < 1:
raise ValueError(f"{self.buff_instance.ft.index}的剩余层数不足,无法消耗层数!")
self.record.available_effect_times -= 1
# print(
# f"检测到普攻发动!消耗1层!当前层数为:{self.record.available_effect_times}!"
# )
else:
raise ValueError(
f"{self.buff_instance.ft.index}的Xeffect函数获取到了无法解析的信号{self.record.active_signal}!"
)
self.record.active_signal = None
def special_exit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="防暴者Ⅵ型")
if self.record.available_effect_times < 1:
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/RoaringRideBuffTrigger.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class RoaringRideBuffTriggerRecord:
def __init__(self):
self.equipper = None
self.char = None
self.buff_map = None
self.sub_exist_buff_dict = None
class RoaringRideBuffTrigger(Buff.BuffLogic):
"""轰鸣座驾触发器"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xhit = self.special_hit_logic
self.equipper = None
self.buff_0 = None
self.record: RoaringRideBuffTriggerRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"轰鸣座驾", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = RoaringRideBuffTriggerRecord()
self.record = self.buff_0.history.record
def special_hit_logic(self, **kwargs):
"""
轰鸣座驾的判定是简单逻辑,只要是强化E命中即可。
命中后,会执行special_hit函数,该函数会抽取随机数,并且为自己添加对应的Buff
"""
self.check_record_module()
self.get_prepared(equipper="轰鸣座驾", sub_exist_buff_dict=1)
if self.record.buff_map is None:
self.record.buff_map = {
0: f"Buff-武器-精{int(self.buff_instance.ft.refinement)}轰鸣座驾-攻击力",
1: f"Buff-武器-精{int(self.buff_instance.ft.refinement)}轰鸣座驾-精通提升",
2: f"Buff-武器-精{int(self.buff_instance.ft.refinement)}轰鸣座驾-属性异常积蓄",
}
from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy
from zsim.sim_progress.RandomNumberGenerator import RNG
rng: RNG = self.buff_instance.sim_instance.rng_instance
normalized_value = rng.random_float()
if 0 <= normalized_value < 1 / 3:
buff_add_strategy(self.record.buff_map[0], sim_instance=self.buff_instance.sim_instance)
# print(f'轰鸣座驾触发了攻击力Buff')
elif 1 / 3 <= normalized_value < 2 / 3:
buff_add_strategy(self.record.buff_map[1], sim_instance=self.buff_instance.sim_instance)
# print(f'轰鸣座驾触发了精通Buff')
else:
# print(f'轰鸣座驾触发了积蓄效率Buff')
buff_add_strategy(self.record.buff_map[2], sim_instance=self.buff_instance.sim_instance)
self.buff_instance.simple_start(
find_tick(sim_instance=self.buff_instance.sim_instance),
self.record.sub_exist_buff_dict,
)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SeedAdditionalAbilityTrigger.py
================================================
# 这是席德额外能力重击大招增伤无视电抗Buff的脚本
from define import SEED_REPORT
from .. import Buff, JudgeTools, check_preparation
from ._buff_record_base_class import BuffRecordBaseClass as BRBC
class SeedAdditionalAbilityTriggerRecord(BRBC):
def __init__(self):
super().__init__()
self.cd = 60
self.energy_value = 2 # 回能值,2点能量。
class SeedAdditionalAbilityTrigger(Buff.BuffLogic):
def __init__(self, buff_instance):
"""这是席德额外能力给正兵回能的触发器"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.buff_0: "Buff | None" = None
self.record: BRBC | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["席德"][self.buff_instance.ft.index]
assert self.buff_0 is not None, (
"【Buff初始化警告】席德的复杂逻辑模块未正确初始化,请检查函数"
)
if self.buff_0.history.record is None:
self.buff_0.history.record = SeedAdditionalAbilityTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1461)
assert self.record is not None, (
f"【Buff初始化警告】{self.buff_instance.ft.index}的复杂逻辑模块未正确初始化,请检查函数"
)
skill_node = kwargs.get("skill_node")
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
assert isinstance(skill_node, SkillNode)
preload_data = self.buff_instance.sim_instance.preload.preload_data
# 当前操作角色不是席德时,直接返回False
if preload_data.operating_now != 1461:
return False
# 过滤掉不是席德的技能
if skill_node.char_name != self.record.char.NAME:
return False
# 过滤掉所有非命中帧
tick = self.buff_instance.sim_instance.tick
if not skill_node.is_hit_now(tick=tick):
return False
# 检查内置CD
if not self.record.check_cd(tick_now=tick):
return False
return True
def special_hit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1461)
assert isinstance(self.record, SeedAdditionalAbilityTriggerRecord), (
f"【Buff初始化警告】{self.buff_instance.ft.index}的复杂逻辑模块未正确初始化,请检查函数"
)
assert self.record.char.vanguard is not None, (
"席德在激活了组队被动的情况下没有指定正兵,请检查"
)
vanguard = self.record.char.vanguard
from zsim.sim_progress.data_struct.sp_update_data import ScheduleRefreshData
energy_value = self.record.energy_value
refresh_data = ScheduleRefreshData(
sp_target=(vanguard.NAME,),
sp_value=energy_value,
)
event_list = self.buff_instance.sim_instance.schedule_data.event_list
event_list.append(refresh_data)
self.record.last_active_tick = self.buff_instance.sim_instance.tick
if SEED_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(f"【席德事件】额外能力触发,为{vanguard}回恢 {energy_value} 点能量")
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SeedBesiegeBonus.py
================================================
# 这是席德围杀Buff的脚本
from .. import Buff, JudgeTools, check_preparation
from ._buff_record_base_class import BuffRecordBaseClass as BRBC
class SeedBesiegeBonusRecord(BRBC):
def __init__(self):
super().__init__()
class SeedBesiegeBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""这是席德围杀Buff的脚本"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xexit = self.special_exit_logic
self.buff_0: "Buff | None" = None
self.record: BRBC | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["席德"][self.buff_instance.ft.index]
assert self.buff_0 is not None, (
"【Buff初始化警告】席德的复杂逻辑模块未正确初始化,请检查函数"
)
if self.buff_0.history.record is None:
self.buff_0.history.record = SeedBesiegeBonusRecord()
self.record = self.buff_0.history.record
def special_exit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1461)
assert self.record is not None, (
f"【Buff初始化警告】{self.buff_instance.ft.index}的复杂逻辑模块未正确初始化,请检查函数"
)
seed = self.record.char
from zsim.sim_progress.Character.Seed import Seed
assert isinstance(seed, Seed), (
f"【Buff初始化警告】{self.buff_instance.ft.index}的复杂逻辑模块未正确初始化,请检查函数"
)
besiege_tuple = seed.besiege_active_check()
beneficiary = kwargs.get("beneficiary", None)
if beneficiary is None:
print(
f"【Buff退出警告】{self.buff_instance.ft.index} 的复杂逻辑模块未正确识别到输入参数“beneficiary”,遂终止Buff。请检查函数"
)
return True
# 如果席德都没有指定正兵,那么肯定也不可能有围杀Buff
if seed.vanguard is None:
return True
if beneficiary == "席德":
return not besiege_tuple[0]
elif beneficiary == seed.vanguard.NAME:
return not besiege_tuple[1]
else:
# 别的角色不可能保留围杀Buff
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SeedBesiegeBonusTrigger.py
================================================
# 这是席德围杀Buff的脚本
from .. import Buff, JudgeTools, check_preparation
from ._buff_record_base_class import BuffRecordBaseClass as BRBC
class SeedBesiegeBonusTriggerRecord(BRBC):
def __init__(self):
super().__init__()
self.buff_index = "Buff-角色-席德-围杀"
class SeedBesiegeBonusTrigger(Buff.BuffLogic):
def __init__(self, buff_instance):
"""这是席德围杀Buff的脚本"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.buff_0: "Buff | None" = None
self.record: BRBC | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["席德"][self.buff_instance.ft.index]
assert self.buff_0 is not None, (
"【Buff初始化警告】席德的复杂逻辑模块未正确初始化,请检查函数"
)
if self.buff_0.history.record is None:
self.buff_0.history.record = SeedBesiegeBonusTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1461)
assert self.record is not None, (
f"【Buff初始化警告】{self.buff_instance.ft.index}的复杂逻辑模块未正确初始化,请检查函数"
)
from zsim.sim_progress.Character.Seed import Seed
seed = self.record.char
assert isinstance(seed, Seed)
besiege_state_tuple = seed.besiege_active_check()
if any(besiege_state_tuple):
return True
else:
return False
def special_hit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1461)
assert self.record is not None
from zsim.sim_progress.Character.Seed import Seed
seed = self.record.char
assert isinstance(seed, Seed)
besiege_state_tuple = seed.besiege_active_check()
from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy
benefit_list = []
if besiege_state_tuple[0]:
benefit_list.append("席德")
if besiege_state_tuple[1]:
benefit_list.append(seed.vanguard.NAME) if seed.vanguard is not None else None
if benefit_list:
buff_add_strategy(
self.record.buff_index,
benifit_list=benefit_list,
sim_instance=self.buff_instance.sim_instance,
)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SeedCinema2BesiegeIgnoreDefenceTrigger.py
================================================
from .. import Buff, JudgeTools, check_preparation
from ._buff_record_base_class import BuffRecordBaseClass as BRBC
class SeedCinema2BesiegeIgnoreDefenceTriggerRecord(BRBC):
def __init__(self):
super().__init__()
self.buff_index = "Buff-角色-席德-影画-2画-围杀无视防御力"
class SeedCinema2BesiegeIgnoreDefenceTrigger(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.buff_0: "Buff | None" = None
self.record: BRBC | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["席德"][self.buff_instance.ft.index]
assert self.buff_0 is not None, (
"【Buff初始化警告】角色名字的复杂逻辑模块未正确初始化,请检查函数"
)
if self.buff_0.history.record is None:
self.buff_0.history.record = SeedCinema2BesiegeIgnoreDefenceTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1461)
assert self.record is not None, (
f"【Buff初始化警告】{self.buff_instance.ft.index}的复杂逻辑模块未正确初始化,请检查函数"
)
from zsim.sim_progress.Character.Seed import Seed
seed = self.record.char
assert isinstance(seed, Seed)
besiege_state_tuple = seed.besiege_active_check()
if any(besiege_state_tuple):
return True
else:
return False
def special_hit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1461)
assert self.record is not None
from zsim.sim_progress.Character.Seed import Seed
seed = self.record.char
assert isinstance(seed, Seed)
besiege_state_tuple = seed.besiege_active_check()
from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy
benefit_list = []
if besiege_state_tuple[0]:
benefit_list.append("席德")
if besiege_state_tuple[1]:
benefit_list.append(seed.vanguard.NAME) if seed.vanguard is not None else None
if benefit_list:
buff_add_strategy(
self.record.buff_index,
benifit_list=benefit_list,
sim_instance=self.buff_instance.sim_instance,
)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SeedCinema2BesiegeIgnoreDefense.py
================================================
# 这是席德2画围杀无视防御力Buff的脚本
from .. import Buff, JudgeTools, check_preparation
from ._buff_record_base_class import BuffRecordBaseClass as BRBC
class SeedCinema2BesiegeIgnoreDefenseRecord(BRBC):
def __init__(self):
super().__init__()
class SeedCinema2BesiegeIgnoreDefense(Buff.BuffLogic):
def __init__(self, buff_instance):
"""这是席德2画围杀无视防御力Buff的脚本"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xexit = self.special_exit_logic
self.buff_0: "Buff | None" = None
self.record: BRBC | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["席德"][self.buff_instance.ft.index]
assert self.buff_0 is not None, (
"【Buff初始化警告】席德的复杂逻辑模块未正确初始化,请检查函数"
)
if self.buff_0.history.record is None:
self.buff_0.history.record = SeedCinema2BesiegeIgnoreDefenseRecord()
self.record = self.buff_0.history.record
def special_exit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1461)
assert self.record is not None, (
f"【Buff初始化警告】{self.buff_instance.ft.index}的复杂逻辑模块未正确初始化,请检查函数"
)
seed = self.record.char
from zsim.sim_progress.Character.Seed import Seed
assert isinstance(seed, Seed), (
f"【Buff初始化警告】{self.buff_instance.ft.index}的复杂逻辑模块未正确初始化,请检查函数"
)
besiege_tuple = seed.besiege_active_check()
beneficiary = kwargs.get("beneficiary", None)
if beneficiary is None:
print(
f"【Buff退出警告】{self.buff_instance.ft.index} 的复杂逻辑模块未正确识别到输入参数“beneficiary”,遂终止Buff。请检查函数"
)
return True
# 如果席德都没有指定正兵,那么肯定也不可能有围杀Buff
if seed.vanguard is None:
return True
if beneficiary == "席德":
return not besiege_tuple[0]
elif beneficiary == seed.vanguard.NAME:
return not besiege_tuple[1]
else:
# 别的角色不可能保留围杀Buff
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SeedCinema4Bonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
from ._buff_record_base_class import BuffRecordBaseClass as BRBC
class SeedCinema4BonusRecord(BRBC):
def __init__(self):
super().__init__()
self.buff_index = "Buff-角色-席德-影画-4画-喧响效率与大招增伤"
class SeedCinema4Bonus(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xexit = self.special_exit_logic
self.buff_0: "Buff | None" = None
self.record: BRBC | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["席德"][self.buff_instance.ft.index]
assert self.buff_0 is not None, (
"【Buff初始化警告】角色名字的复杂逻辑模块未正确初始化,请检查函数"
)
if self.buff_0.history.record is None:
self.buff_0.history.record = SeedCinema4BonusRecord()
self.record = self.buff_0.history.record
def special_exit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1461)
assert self.record is not None, (
f"【Buff初始化警告】{self.buff_instance.ft.index}的复杂逻辑模块未正确初始化,请检查函数"
)
seed = self.record.char
from zsim.sim_progress.Character.Seed import Seed
assert isinstance(seed, Seed), (
f"【Buff初始化警告】{self.buff_instance.ft.index}的复杂逻辑模块未正确初始化,请检查函数"
)
besiege_tuple = seed.besiege_active_check()
beneficiary = kwargs.get("beneficiary", None)
if beneficiary is None:
print(
f"【Buff退出警告】{self.buff_instance.ft.index} 的复杂逻辑模块未正确识别到输入参数“beneficiary”,遂终止Buff。请检查函数"
)
return True
# 如果席德都没有指定正兵,那么肯定也不可能有围杀Buff
if seed.vanguard is None:
return True
if beneficiary == "席德":
return not besiege_tuple[0]
elif beneficiary == seed.vanguard.NAME:
return not besiege_tuple[1]
else:
# 别的角色不可能保留围杀Buff
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SeedCinema4Trigger.py
================================================
from .. import Buff, JudgeTools, check_preparation
from ._buff_record_base_class import BuffRecordBaseClass as BRBC
class SeedCinema4TriggerRecord(BRBC):
def __init__(self):
super().__init__()
self.buff_index = "Buff-角色-席德-影画-4画-喧响效率与大招增伤"
class SeedCinema4Trigger(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.buff_0: "Buff | None" = None
self.record: BRBC | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["席德"][self.buff_instance.ft.index]
assert self.buff_0 is not None, (
"【Buff初始化警告】角色名字的复杂逻辑模块未正确初始化,请检查函数"
)
if self.buff_0.history.record is None:
self.buff_0.history.record = SeedCinema4TriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1461)
assert self.record is not None, (
f"【Buff初始化警告】{self.buff_instance.ft.index}的复杂逻辑模块未正确初始化,请检查函数"
)
from zsim.sim_progress.Character.Seed import Seed
seed = self.record.char
assert isinstance(seed, Seed)
besiege_state_tuple = seed.besiege_active_check()
if any(besiege_state_tuple):
return True
else:
return False
def special_hit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1461)
assert self.record is not None
from zsim.sim_progress.Character.Seed import Seed
seed = self.record.char
assert isinstance(seed, Seed)
besiege_state_tuple = seed.besiege_active_check()
from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy
benefit_list = []
if besiege_state_tuple[0]:
benefit_list.append("席德")
if besiege_state_tuple[1]:
benefit_list.append(seed.vanguard.NAME) if seed.vanguard is not None else None
if benefit_list:
buff_add_strategy(
self.record.buff_index,
benifit_list=benefit_list,
sim_instance=self.buff_instance.sim_instance,
)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SeedCinema6Trigger.py
================================================
from zsim.define import SEED_REPORT
from .. import Buff, JudgeTools, check_preparation
from ._buff_record_base_class import BuffRecordBaseClass as BRBC
class SeedCinema6TriggerRecord(BRBC):
def __init__(self):
super().__init__()
self.cd = 180
self.additional_damage_skill_tag = "1461_Cinema_6"
self.trigger_skill_tag = "1461_SNA_1"
class SeedCinema6Trigger(Buff.BuffLogic):
def __init__(self, buff_instance):
"""这是席德6画触发器Buff的脚本"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xstart = self.special_start_logic
self.buff_0: "Buff | None" = None
self.record: BRBC | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["席德"][self.buff_instance.ft.index]
assert self.buff_0 is not None, (
"【Buff初始化警告】席德的复杂逻辑模块未正确初始化,请检查函数"
)
if self.buff_0.history.record is None:
self.buff_0.history.record = SeedCinema6TriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1461)
assert self.record is not None, (
f"【Buff初始化警告】{self.buff_instance.ft.index}的复杂逻辑模块未正确初始化,请检查函数"
)
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
assert isinstance(skill_node, SkillNode)
if skill_node.skill_tag != self.record.trigger_skill_tag:
return False
tick = self.buff_instance.sim_instance.tick
if tick != skill_node.preload_tick:
return False
if not self.record.check_cd(tick_now=tick):
return False
return True
def special_start_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1461)
assert self.record is not None
from zsim.sim_progress.data_struct.SchedulePreload import schedule_preload_event_factory
tick = self.buff_instance.sim_instance.tick
preload_tick_list = [tick, tick, tick]
skill_tag_list = [self.record.additional_damage_skill_tag] * 3
preload_data = self.buff_instance.sim_instance.preload.preload_data
schedule_preload_event_factory(
preload_tick_list=preload_tick_list,
skill_tag_list=skill_tag_list,
preload_data=preload_data,
sim_instance=self.buff_instance.sim_instance,
)
self.record.last_active_tick = tick
if SEED_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print("【席德6画】检测到席德发动了 落华·重戮,添加三次协同攻击!")
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SeedDirectStrikeBonus.py
================================================
# 这是席德明攻Buff的脚本
from .. import Buff, JudgeTools, check_preparation
from ._buff_record_base_class import BuffRecordBaseClass as BRBC
class SeedDirectStrikeBonusRecord(BRBC):
def __init__(self):
super().__init__()
self.buff_index = "Buff-角色-席德-明攻"
class SeedDirectStrikeBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""这是席德明攻Buff的脚本"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xexit = self.special_exit_logic
self.buff_0: "Buff | None" = None
self.record: BRBC | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["席德"][self.buff_instance.ft.index]
assert self.buff_0 is not None, (
"【Buff初始化警告】席德的复杂逻辑模块未正确初始化,请检查函数"
)
if self.buff_0.history.record is None:
self.buff_0.history.record = SeedDirectStrikeBonusRecord()
self.record = self.buff_0.history.record
def special_exit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1461)
assert self.record is not None, (
f"【Buff初始化警告】{self.buff_instance.ft.index}的复杂逻辑模块未正确初始化,请检查函数"
)
from zsim.sim_progress.Character.Seed import Seed
seed: Seed = self.record.char
if seed.vanguard is None:
# 当席德的没有队友被指定为“正兵”时,明攻永远不可能触发。
return False
# 直接运行席德的围攻状态判断函数
direct_strike = seed.direct_strike_active
return not direct_strike
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SeedDirectStrikeTrigger.py
================================================
# 这是席德明攻Buff的脚本
from .. import Buff, JudgeTools, check_preparation
from ._buff_record_base_class import BuffRecordBaseClass as BRBC
class SeedDirectStrikeTriggerRecord(BRBC):
def __init__(self):
super().__init__()
self.buff_index = "Buff-角色-席德-明攻"
class SeedDirectStrikeTrigger(Buff.BuffLogic):
def __init__(self, buff_instance):
"""这是席德明攻Buff的脚本"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.buff_0: "Buff | None" = None
self.record: BRBC | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["席德"][self.buff_instance.ft.index]
assert self.buff_0 is not None, (
"【Buff初始化警告】席德的复杂逻辑模块未正确初始化,请检查函数"
)
if self.buff_0.history.record is None:
self.buff_0.history.record = SeedDirectStrikeTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""判断席德的明攻Buff生效情况"""
self.check_record_module()
self.get_prepared(char_CID=1461)
assert self.record is not None, (
f"【Buff初始化警告】{self.buff_instance.ft.index}的复杂逻辑模块未正确初始化,请检查函数"
)
from zsim.sim_progress.Character.Seed import Seed
seed: Seed = self.record.char
if seed.vanguard is None:
# 当席德的没有队友被指定为“正兵”时,明攻永远不可能触发。
return False
direct_strike = seed.direct_strike_active
# 直接运行席德的围攻状态判断函数
return direct_strike
def special_hit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1461, sub_exist_buff_dict=1)
assert self.record is not None
seed = self.record.char
from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy
buff_add_strategy(
self.record.buff_index,
benifit_list=[seed.vanguard.NAME],
sim_instance=self.buff_instance.sim_instance,
)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SeedOnslaughtBonus.py
================================================
from zsim.sim_progress.Character.Seed import Seed
from .. import Buff, JudgeTools, check_preparation
from ._buff_record_base_class import BuffRecordBaseClass as BRBC
class SeedOnslaughtBonusRecord(BRBC):
def __init__(self):
super().__init__()
class SeedOnslaughtBonus(Buff.BuffLogic):
"""席德的强袭Buff复杂逻辑"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
self.buff_0: "Buff | None" = None
self.record: BRBC | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["席德"][self.buff_instance.ft.index]
assert self.buff_0 is not None, (
"【Buff初始化警告】席德的复杂逻辑模块未正确初始化,请检查函数"
)
if self.buff_0.history.record is None:
self.buff_0.history.record = SeedOnslaughtBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""席德的强袭状态早就已经记录在席德的特殊资源中了,所以这里不需要重复判断,只需要直接调用方法判断是否生效即可"""
self.check_record_module()
self.get_prepared(char_CID=1461)
assert self.record is not None, (
f"【Buff初始化警告】{self.buff_instance.ft.index}的复杂逻辑模块未正确初始化,请检查函数"
)
assert type(self.record.char) is Seed, (
f"当前record中的角色不是席德,而是{type(self.record.char).__name__}, CID为:{self.record.char.CID, self.record.char.NAME}"
)
return self.record.char.onslaught_active
def special_exit_logic(self, **kwargs):
"""强袭Buff的退出逻辑和生效逻辑相反,所以这里需要调用席德的方法检测是否退出强袭状态"""
self.check_record_module()
self.get_prepared(char_CID=1461)
assert self.record is not None, (
f"【Buff初始化警告】{self.buff_instance.ft.index}的复杂逻辑模块未正确初始化,请检查函数"
)
return not self.xjudge
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SeveredInnocencELEDMGBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class SeveredInnocencELEDMGBonusRecord:
def __init__(self):
self.char = None
self.equipper = None
self.trigger_buff_0 = None
class SeveredInnocencELEDMGBonus(Buff.BuffLogic):
"""
牺牲洁纯的电伤判定
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.equipper = None
self.record = None
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"牺牲洁纯", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = SeveredInnocencELEDMGBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""查装备者身上的触发暴伤的Buff是否为3层"""
self.check_record_module()
self.get_prepared(
char_CID=1381,
equipper="牺牲洁纯",
trigger_buff_0=("equipper", "牺牲洁纯-触发暴伤"),
)
if self.record.trigger_buff_0.dy.count == 3:
if not self.record.trigger_buff_0.dy.active:
raise ValueError(f"{self.record.trigger_buff_0.ft.index}有层数但是未激活!")
return True
return False
def special_exit_logic(self, **kwargs):
"""xjudge的反逻辑"""
if self.xjudge:
return False
else:
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SeveredInnocenceCritDMGBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class SeveredInnocenceCritDMGBonusRecord:
def __init__(self):
self.char = None
self.equipper = None
self.update_signal = []
self.active_tick_box = {
0: {"start": 0, "end": 0},
1: {"start": 0, "end": 0},
2: {"start": 0, "end": 0},
}
self.sub_exist_buff_dict = None
class SeveredInnocenceCritDMGBonus(Buff.BuffLogic):
"""
牺牲洁纯的层数判定
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.equipper = None
self.record = None
self.xjudge = self.special_judge_logic
self.xstart = self.special_start_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"牺牲洁纯", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = SeveredInnocenceCritDMGBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
在判断函数阶段,就根据传入的skill_node,进行CID、mission子标签的筛选,
将属于安比的、start子标签的 普攻、特殊技以及追加攻击记录下来,
并且更新进record中的update_signal中
"""
self.check_record_module()
self.get_prepared(char_CID=1381, equipper="牺牲洁纯")
_skill_node = kwargs.get("skill_node", None)
_loading_mission = kwargs.get("loading_mission", None)
if _skill_node is None:
raise ValueError(f"{self.buff_instance.ft.index}的xjudge函数并未获取到skill_node")
if _loading_mission is None:
raise ValueError(f"{self.buff_instance.ft.index}的xjudge函数并未获取到loading_mission")
from zsim.sim_progress.Preload import SkillNode
if not isinstance(_skill_node, SkillNode):
raise TypeError(f"{_skill_node}不是SkillNode类")
tick = find_tick(sim_instance=self.buff_instance.sim_instance)
if str(self.record.char.CID) not in _skill_node.skill_tag:
return False
if not tick - 1 < _skill_node.preload_tick <= tick:
return False
if _skill_node.skill.labels is not None:
if "aftershock_attack" in _skill_node.skill.labels.keys():
self.record.update_signal.append(2)
return True
elif _skill_node.skill.trigger_buff_level == 0:
self.record.update_signal.append(0)
return True
elif _skill_node.skill.trigger_buff_level in [1, 2]:
self.record.update_signal.append(1)
return True
else:
return False
def special_start_logic(self, **kwargs):
"""
在正式更新阶段,对update_signal进行pop遍历,把每个signal挨个拿出来进行处理,
并且按照individual_settled类Buff的更新规则,将其转换成built_in_buff_box,并且替换原有的。
最终实现不同Buff层数的管理。
"""
self.check_record_module()
self.get_prepared(char_CID=1381, equipper="牺牲洁纯", sub_exist_buff_dict=1)
tick = find_tick(sim_instance=self.buff_instance.sim_instance)
if not self.record.update_signal:
return
reset_list = list(set(self.record.update_signal))
while reset_list:
update_signal = reset_list.pop()
self.record.active_tick_box[update_signal]["start"] = tick
self.record.active_tick_box[update_signal]["end"] = (
tick + self.buff_instance.ft.maxduration
)
self.buff_instance.simple_start(tick, self.record.sub_exist_buff_dict, no_count=1)
self.buff_instance.dy.built_in_buff_box = []
for _mode_index, _sub_dict in self.record.active_tick_box.items():
if self.record.active_tick_box[_mode_index]["end"] > tick:
self.buff_instance.dy.built_in_buff_box.append(list(_sub_dict.values()))
self.buff_instance.dy.count = len(self.buff_instance.dy.built_in_buff_box)
self.buff_instance.update_to_buff_0(self.buff_0)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/ShadowHarmony4.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class ShadowHarmony4Record:
def __init__(self):
self.equipper = None
self.char = None
class ShadowHarmony4(Buff.BuffLogic):
"""
这是极地重金属的复杂逻辑判定。
主要检测的是碎冰的变化状态,如果碎冰状态变了,就返回True
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
# 初始化特定逻辑
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"如影相随", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = ShadowHarmony4Record()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="如影相随")
loading_mission = kwargs.get("loading_mission", None)
if loading_mission is None:
raise ValueError(f"{self.buff_instance.ft.index}的xjuge函数中,获取loading_mission失败")
from zsim.sim_progress.Load import LoadingMission
if not isinstance(loading_mission, LoadingMission):
raise TypeError
skill_node = loading_mission.mission_node
tick = find_tick(sim_instance=self.buff_instance.sim_instance)
if not tick - 1 < loading_mission.get_first_hit() <= tick:
"""由于单个技能只能更新本buff一次,而本函数又只能在hit节点执行,
所以这里需要过滤到第一个hit的节点"""
return False
"""是冲刺攻击或是追加攻击标签时,检测技能属性是否与四件套佩戴者属性相同,如果不同则不予触发!"""
if skill_node.element_type != self.record.char.element_type:
return False
if not skill_node.skill.labels:
if skill_node.skill.trigger_buff_level == 3:
return True
else:
if "aftershock_attack" in skill_node.skill.labels.keys():
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SharpenedStingerAnomalyBuildupBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class SharpenedStingerAnomalyBuildupBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
self.update_signal = None
self.preload_data = None
self.sub_exist_buff_dict = None
self.trigger_buff_0 = None
class SharpenedStingerAnomalyBuildupBonus(Buff.BuffLogic):
"""淬锋钳刺第二个特效的判断逻辑"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"淬锋钳刺", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = SharpenedStingerAnomalyBuildupBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""淬锋钳刺的第二特效触发逻辑:触发器Buff为3层时触发。"""
self.check_record_module()
self.get_prepared(
equipper="淬锋钳刺",
preload_data=1,
trigger_buff_0=("equipper", "淬锋钳刺-猎意"),
)
if self.record.trigger_buff_0.dy.count == 3:
return True
else:
return False
def special_exit_logic(self, **kwargs):
return not self.special_judge_logic(**kwargs)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SharpenedStingerPhyDmgBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class SharpenedStingerPhyDmgBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
self.update_signal = None
self.preload_data = None
self.sub_exist_buff_dict = None
class SharpenedStingerPhyDmgBonus(Buff.BuffLogic):
"""淬锋钳刺的 猎意复杂逻辑"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xstart = self.special_start_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"淬锋钳刺", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = SharpenedStingerPhyDmgBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""淬锋钳刺的猎意触发逻辑。"""
self.check_record_module()
self.get_prepared(equipper="淬锋钳刺", preload_data=1)
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise TypeError(
f"{self.buff_instance.ft.index}的xjudge函数获取的skill_node不是SkillNode类!"
)
# 过滤不是自己的skill_node
if self.record.char.NAME != skill_node.char_name:
return False
self.buff_0.ready_judge(find_tick(sim_instance=self.buff_instance.sim_instance))
if not self.buff_0.dy.ready:
return False
if self.record.preload_data.personal_node_stack[self.record.char.CID].__len__() <= 1:
self.record.update_signal = 1
return True
# 判断是否为冲刺攻击或闪避反击
if skill_node.skill.trigger_buff_level not in [3, 4]:
return False
if skill_node.skill.trigger_buff_level in [3]:
self.record.update_signal = 0
elif skill_node.skill.trigger_buff_level == 4:
self.record.update_signal = 1
return True
def special_start_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="淬锋钳刺", preload_data=1, sub_exist_buff_dict=1)
if self.record.update_signal is None:
return
if self.record.update_signal == 0:
self.buff_instance.simple_start(
find_tick(sim_instance=self.buff_instance.sim_instance),
self.record.sub_exist_buff_dict,
)
elif self.record.update_signal == 1:
self.buff_instance.simple_start(
find_tick(sim_instance=self.buff_instance.sim_instance),
self.record.sub_exist_buff_dict,
no_count=1,
)
self.buff_instance.dy.count = self.buff_instance.ft.maxcount
self.buff_instance.update_to_buff_0(self.buff_0)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SliceofTimeExtraResources.py
================================================
from .. import Buff, JudgeTools, check_preparation
class SliceofTimeExtraResourcesRecord:
def __init__(self):
self.equipper = None
self.char = None
self.action_stack = None
self.sub_exist_buff_dict = None
self.decibel_value_dict = {
1: {4: 20, 2: 25, 7: 30, 8: 30, 9: 30, 5: 35},
2: {4: 23, 2: 28.5, 7: 34.5, 8: 34.5, 9: 34.5, 5: 40},
3: {4: 26, 2: 32, 7: 39, 8: 39, 9: 39, 5: 45},
4: {4: 29, 2: 35.5, 7: 43.5, 8: 43.5, 9: 43.5, 5: 50},
5: {4: 32, 2: 40, 7: 48, 8: 48, 9: 48, 5: 55},
}
self.energy_value_dict = {1: 0.7, 2: 0.8, 3: 0.9, 4: 1.0, 5: 1.1}
self.last_update_tick_box = {"E_EX": 0, "Sup": 0, "QTE": 0, "CA": 0}
self.update_key_dict = {
2: "E_EX",
4: "CA",
5: "QTE",
7: "Sup",
8: "Sup",
9: "Sup",
}
class SliceofTimeExtraResources(Buff.BuffLogic):
"""
这是时光切片的复杂效果逻辑。
虽然该buff的buff effect为空,但是在special_start逻辑中,内置了恢复能量和喧响值的方法。
通过构建Schedule Refresh Data的实例,并向event list中添加,
就可以实现角色的喧响值和能量值的修改。
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xstart = self.special_start_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"时光切片", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = SliceofTimeExtraResourcesRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
第一层判定是trigger_buff_level的判定
通过第一层判定后,再过内置CD检测。
"""
self.check_record_module()
self.get_prepared(equipper="时光切片", action_stack=1)
action_now = self.record.action_stack.peek()
trigger_buff_level = action_now.mission_node.skill.trigger_buff_level
tick_now = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
if trigger_buff_level in [4, 2, 7, 8, 9, 5]:
ready = self.check_update_cd(trigger_buff_level, tick_now)
if ready:
return True
else:
return False
else:
return False
def special_start_logic(self, **kwargs):
"""
这部分的代码主要是负责构建一个ScheduleRefreshData实例的,
而simple_start只是为了启动一次,让Log记录到这个buff。
Buff自身没有效果。
"""
self.check_record_module()
self.get_prepared(equipper="时光切片", action_stack=1, sub_exist_buff_dict=1)
tick_now = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
self.buff_instance.simple_start(tick_now, self.record.sub_exist_buff_dict)
action_now = self.record.action_stack.peek()
trigger_buff_level = action_now.mission_node.skill.trigger_buff_level
decibel_value = self.record.decibel_value_dict[self.buff_instance.ft.refinement][
trigger_buff_level
]
energy_value = self.record.energy_value_dict[self.buff_instance.ft.refinement]
actor_name = action_now.mission_character
event_list = JudgeTools.find_event_list(sim_instance=self.buff_instance.sim_instance)
from zsim.sim_progress.data_struct import ScheduleRefreshData
refresh_data = ScheduleRefreshData(
sp_target=(self.record.char.NAME,),
sp_value=energy_value,
decibel_target=(actor_name,),
decibel_value=decibel_value,
)
event_list.append(refresh_data)
def check_update_cd(self, tbl: int, tick_now: int):
"""
检测内置CD!由于闪避反击、强化E、支援技、QTE的触发CD是分开计算的,
所以,这里也要根据trigger buff level进行分流,分别检测各自的CD。
"""
if tbl not in self.record.update_key_dict:
raise ValueError(f"传入的Trigger Buff Level为{tbl},不在检测范围内!")
key = self.record.update_key_dict[tbl]
last_update_tick = self.record.last_update_tick_box[key]
if last_update_tick == 0:
self.record.last_update_tick_box[key] = tick_now
return True
if tick_now - last_update_tick > self.buff_instance.ft.cd:
self.record.last_update_tick_box[key] = tick_now
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SokakuAdditionalAbilityICEBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class SokakuAdditionalAbilityIBRecord:
def __init__(self):
self.char = None
self.action_stack = None
self.last_update_resource = 0
class SokakuAdditionalAbilityICEBonus(Buff.BuffLogic):
"""
苍角组队被动:
消耗涡流发动展旗时激活
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
# 初始化特定逻辑
self.xjudge = self.special_judge_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["苍角"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = SokakuAdditionalAbilityIBRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1131, action_stack=1)
action_now = self.record.action_stack.peek()
resource_now = self.record.char.get_resources()[1]
if action_now.mission_tag != "1131_E_EX_A":
return False
if self.record.last_update_resource <= resource_now:
self.record.last_update_resource = resource_now
return False
else:
self.record.last_update_resource = resource_now
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SokakuUniqueSkillMajorATKBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class SokakuAdditionalAbilityATKRecord:
def __init__(self):
self.dynamic_buff_list = None
self.char = None
self.action_stack = None
self.sub_exist_buff_dict = None
self.last_update_rescource = 0
class SokakuUniqueSkillMajorATKBonus(Buff.BuffLogic):
"""
苍角的核心被动2:消耗涡流的展旗会叠加双倍的攻击力。
程序对苍角的两个Buff是剥离处理的。
Buff1是简单判定逻辑,只要有展旗,就一定触发。
Buff2作为额外的层数,在判定出涡流下降沿时再触发。
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xstart = self.special_start_logic
self.xjudge = self.special_judge_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["苍角"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = SokakuAdditionalAbilityATKRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
检测到展旗的TAG后,去查当前的资源数量。
由于执行本代码的阶段是Load阶段,而资源消耗事件是发生在Preload阶段的。
所以,本阶段理论上能够检测到资源的下降沿。
但是由于每次Buff都会新建,所以,这里的self是不能存历史资源的。
必须存放在Buff_0.history.last_update_resource里面。
"""
self.check_record_module()
self.get_prepared(char_CID=1131, action_stack=1)
action_now = self.record.action_stack.peek()
resource_now = self.record.char.get_resources()[1]
if action_now.mission_tag == "1131_E_EX_A":
def match_code(a, b):
return a < b
if JudgeTools.detect_edge(
(resource_now, self.record.last_update_rescource), match_code
):
self.record.last_update_rescource = resource_now
return True
self.record.last_update_rescource = resource_now
return False
def special_start_logic(self, **kwargs):
"""
展旗发动时,应该检索当前角色的面板攻击力。
如果能顺利执行这个模块,那么意味着已经检测到下降沿。
直接调取攻击力并按单倍计算即可。
——————————————————————————
注意,这里不能按照技能说明,用双倍算,
因为这个Buff2只是攻击力Buff的一半,另外一半的层数,在Buff1身上。
"""
self.check_record_module()
self.get_prepared(char_CID=1131, sub_exist_buff_dict=1)
atk_now = self.record.char.statement.ATK
count = min(atk_now * 0.2, 500)
tick_now = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
self.buff_instance.simple_start(tick_now, self.record.sub_exist_buff_dict)
# 先用simple_start把buff开起来。然后再修改层数。
self.buff_instance.dy.count = count
self.buff_instance.update_to_buff_0(self.buff_0)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SokakuUniqueSkillMinorATKBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class SokakuUniqueSkillMinorATKRecord:
def __init__(self):
self.char = None
self.sub_exist_buff_dict = None
class SokakuUniqueSkillMinorATKBonus(Buff.BuffLogic):
"""
这里是苍角的核心被动 1,核心被动1的触发无需复杂代码控制,
只要释放了展旗,就会判定通过。
但是,具体的层数,却是要根据苍角的面板攻击力实时调取的。
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xstart = self.special_start_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["苍角"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = SokakuUniqueSkillMinorATKRecord()
self.record = self.buff_0.history.record
def special_start_logic(self, **kwargs):
"""
展旗发动时,应该检索当前角色的面板攻击力。
"""
self.check_record_module()
self.get_prepared(char_CID=1131, sub_exist_buff_dict=1)
atk_now = self.record.char.statement.ATK
count = min(atk_now * 0.2, 500)
tick_now = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
self.buff_instance.simple_start(tick_now, self.record.sub_exist_buff_dict)
self.buff_instance.dy.count = count
self.buff_instance.update_to_buff_0(self.buff_0)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/Soldier0AnbyAdditionalSkillDMGBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class Soldier0AnbyAdditionalSkillDMGBonusRecord:
def __init__(self):
self.char = None
self.trigger_buff_0 = None
self.preload_data = None
class Soldier0AnbyAdditionalSkillDMGBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""
零号·安比的组队被动,操作角色为安比,并且目标有银星时候,全队增伤。
"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["零号·安比"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = Soldier0AnbyAdditionalSkillDMGBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
只要是检测到有银星,且正在操作安比,就返回True
"""
self.check_record_module()
self.get_prepared(
char_CID=1381,
trigger_buff_0=("零号·安比", "Buff-角色-零号·安比-银星触发器"),
preload_data=1,
)
if self.record.trigger_buff_0.dy.active:
if self.record.preload_data.operating_now == 1381:
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/Soldier0AnbyCinema4EleResReduce.py
================================================
from .. import Buff, JudgeTools, check_preparation
class Soldier0AnbyCinema4EleResReduceRecord:
def __init__(self):
self.char = None
self.trigger_buff_0 = None
class Soldier0AnbyCinema4EleResReduce(Buff.BuffLogic):
def __init__(self, buff_instance):
"""
零号·安比的组队被动,操作角色为安比,并且目标有银星时候,全队增伤。
"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["零号·安比"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = Soldier0AnbyCinema4EleResReduceRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
只要是检测到有银星,就返回True
"""
self.check_record_module()
self.get_prepared(
char_CID=1381,
trigger_buff_0=("零号·安比", "Buff-角色-零号·安比-银星触发器"),
)
if self.record.trigger_buff_0.dy.active:
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/Soldier0AnbyCoreSkillCritDMGBonus.py
================================================
from zsim.sim_progress.ScheduledEvent.Calculator import (
Calculator as Cal,
)
from zsim.sim_progress.ScheduledEvent.Calculator import (
MultiplierData as Mul,
)
from .. import Buff, JudgeTools, check_preparation
class Soldier0AnbyCoreSkillCritDMGBonusRecord:
def __init__(self):
self.char = None
self.dynamic_buff_list = None
self.enemy = None
self.sub_exist_buff_dict = None
self.trigger_buff_0 = None
class Soldier0AnbyCoreSkillCritDMGBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""
零号·安比的核心被动,银星有层数就触发增伤。
"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.xexit = self.special_exit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["零号·安比"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = Soldier0AnbyCoreSkillCritDMGBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
只要是检测到有银星,就返回True
"""
self.check_record_module()
self.get_prepared(
char_CID=1381,
trigger_buff_0=("零号·安比", "Buff-角色-零号·安比-银星触发器"),
)
if self.record.trigger_buff_0.dy.active:
return True
else:
return False
def special_hit_logic(self, **kwargs):
"""在Buff触发时,读取安比的暴伤,计算当前的层数"""
self.check_record_module()
self.get_prepared(char_CID=1381, dynamic_buff_list=1, enemy=1, sub_exist_buff_dict=1)
tick_now = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
self.buff_instance.simple_start(tick_now, self.record.sub_exist_buff_dict, no_count=1)
mul_data = Mul(self.record.enemy, self.record.dynamic_buff_list, self.record.char)
crit_dmg = Cal.RegularMul.cal_personal_crit_dmg(mul_data)
count = crit_dmg * 0.3 * 100
self.buff_instance.dy.count = count
self.buff_instance.update_to_buff_0(self.buff_0)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/Soldier0AnbyCoreSkillDMGBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class Soldier0AnbyCoreSkillDMGBonusRecord:
def __init__(self):
self.char = None
self.dynamic_buff_list = None
self.enemy = None
self.sub_exist_buff_dict = None
self.trigger_buff_0 = None
class Soldier0AnbyCoreSkillDMGBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""
零号·安比的核心被动,银星有层数就触发增伤。
"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["零号·安比"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = Soldier0AnbyCoreSkillDMGBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
只要是检测到有银星,就返回True
"""
self.check_record_module()
self.get_prepared(char_CID=1381)
if self.record.char.get_resources()[1] > 0:
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/Soldier0AnbySilverStarTrigger.py
================================================
from .. import Buff, JudgeTools, check_preparation
class Soldier0AnbySilverStarTriggerRecord:
def __init__(self):
self.char = None
class Soldier0AnbySilverStarTrigger(Buff.BuffLogic):
def __init__(self, buff_instance):
"""
零号·安比的核心被动,银星有层数就触发增伤。
"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xexit = self.special_exit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["零号·安比"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = Soldier0AnbySilverStarTriggerRecord()
self.record = self.buff_0.history.record
def special_exit_logic(self, **kwargs):
"""
只要是检测到银星清0,就返回True
"""
self.check_record_module()
self.get_prepared(char_CID=1381)
if self.record.char.get_resources()[1] == 0:
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/Soldier11AdditionalSkillExtraFireDMGBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class Slodier11AdditionalSkillRecord:
def __init__(self):
self.enemy = None
class Soldier11AdditionalSkillExtraFireDMGBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""
11号组队被动:失衡期间额外火伤。
"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["11号"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = Slodier11AdditionalSkillRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(enemy=1)
if self.record.enemy.dynamic.stun:
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SpectralGazeDefReduce.py
================================================
from .. import Buff, JudgeTools, check_preparation
class SpectralGazeDefReduceRecord:
def __init__(self):
self.equipper = None
self.char = None
class SpectralGazeDefReduce(Buff.BuffLogic):
"""扳机专武索魂影眸的减防效果判定"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"索魂影眸", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
"""
这里的初始化,找到的buff_0实际上是佩戴者的buff_0
"""
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = SpectralGazeDefReduceRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""装备者的[追加攻击]命中敌人并造成电属性伤害时触发"""
self.check_record_module()
self.get_prepared(equipper="索魂影眸")
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
raise ValueError(f"{self.buff_instance.ft.index}的xjudge中缺少skill_node参数")
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise TypeError
if str(self.record.char.CID) not in skill_node.skill_tag or not skill_node.skill.labels:
return False
if skill_node.element_type == 3 and "aftershock_attack" in skill_node.skill.labels:
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SpectralGazeImpactBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class SpectralGazeImpactBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
self.trigger_buff_0 = None
class SpectralGazeImpactBonus(Buff.BuffLogic):
"""扳机专武索魂影眸的第3特效——魂锁满层时,获得冲击力增幅,"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"索魂影眸", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
"""
这里的初始化,找到的buff_0实际上是佩戴者的buff_0
"""
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = SpectralGazeImpactBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""检查触发器buff是否是3层"""
self.check_record_module()
self.get_prepared(equipper="索魂影眸", trigger_buff_0=("equipper", "索魂影眸-魂锁"))
if self.record.trigger_buff_0.dy.active:
if self.record.trigger_buff_0.dy.count == 3:
return True
return False
def special_exit_logic(self, **kwargs):
if not self.xjudge:
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SpectralGazeSpiritLock.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class SpectralGazeSpiritLockRecord:
def __init__(self):
self.equipper = None
self.char = None
self.preload_data = None
self.last_update_node_id = None
class SpectralGazeSpiritLock(Buff.BuffLogic):
"""扳机专武索魂影眸第二特效——魂锁,"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"索魂影眸", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
"""
这里的初始化,找到的buff_0实际上是佩戴者的buff_0
"""
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = SpectralGazeSpiritLockRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
装备者的[追加攻击]命中敌人并造成电属性伤害时,
若当前角色处于后台(并非主操角色),返回True,
同一招式内最多触发一次(这里利用了skill_node 的一个新增功能:独立ID)
"""
self.check_record_module()
self.get_prepared(equipper="索魂影眸", preload_data=1)
tick = find_tick(sim_instance=self.buff_instance.sim_instance)
skill_node = kwargs.get("skill_node")
loading_mission = kwargs.get("loading_mission")
"""逻辑外壳和专武的第一特效没有区别"""
if skill_node is None:
raise ValueError(f"{self.buff_instance.ft.index}的xjudge中缺少skill_node参数")
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise TypeError
if not loading_mission.is_hit_now(tick):
return False
if str(self.record.char.CID) not in skill_node.skill_tag:
return False
if not skill_node.skill.labels:
return False
if skill_node.element_type == 3 and "aftershock_attack" in skill_node.skill.labels:
if self.record.preload_data.operating_now != self.record.char.CID:
node_id = skill_node.get_total_instances()
if node_id != self.record.last_update_node_id:
self.record.last_update_node_id = node_id
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/SteamOven.py
================================================
from .. import Buff, JudgeTools, check_preparation
class SteamOvenRecord:
def __init__(self):
self.equipper = None
self.char = None
self.sub_exist_buff_dict = None
self.last_update_count = 0
self.last_update_tick = 0
self.action_stack = None
self.E_EX_started = False
self.E_EX_endtick = 0
class SteamOven(Buff.BuffLogic):
"""
这段代码是人为刀俎的生效逻辑。根据能量返回层数。
由于该效果要求在能量扣除后还能继续存在8秒,且每一层效果单独结算持续时间,
应在每次更新时,都实时检测当前能量,并更新buff.dy.built_in_buff_box中的所有list。
更新方式不是替换,而是类似于栈。
这里不需要管过期tuples的移除,只需要管溢出tuples的移除即可。
注意,这个Buff的复杂判断逻辑永远是False,但是只输出True。
不能用Alltime参数来平替,因为这样会导致在Update_Buff函数中,本Buff会提前被送出循环,而跳过清理过期tuples的步骤。
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
# 初始化特定逻辑
self.xjudge = self.special_judge_logic
self.xeffect = self.special_effect_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"人为刀俎", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = SteamOvenRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
由于人为刀俎的Buff实际上是按照all_time的规格在生效的,
但是因为要用复杂逻辑来更新它的实际层数,所以不能用alltime来粗暴处理,
否则,在UpdateBuff阶段,这个Buff会因为all_time参数是True而被筛掉,
从而丧失自我更新、去除过期层数的能力。
"""
return True
def special_effect_logic(self):
"""
真正的逻辑模块,首先是初始化,比如找出char、找到装备使用者等;
然后是检查当前激活情况,如果当前buff尚未被激活,那么需要用simple_start激活一下。
然后检查能量, 确定需要循环的次数(除以10并向下取整)
确定这一次加入的tuple子单元的两个时间点(在单次函数执行过程中所添加的子层数的tuple都是相同的)
"""
self.check_record_module()
self.get_prepared(equipper="人为刀俎", sub_exist_buff_dict=1, action_stack=1)
tick_now = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
action_now = self.record.action_stack.peek()
char_energy = self.record.char.sp
"""
以下部分的逻辑判定主要是为了规避
第一个动作是强化E时,由于先在Preload阶段扣除了能量,所以在这里无法读取到真正应生效的能量数值,
所以加了这些代码,让这种情况的强化E也能正确享受到人为刀俎的Buff层数。
"""
if "E_EX" in action_now.mission_tag and not self.record.E_EX_started:
self.record.E_EX_endtick = tick_now + action_now.mission_node.skill.ticks
char_energy += action_now.mission_node.skill.sp_consume
self.record.E_EX_started = True
if tick_now >= self.record.E_EX_endtick:
self.record.E_EX_started = False
new_count = char_energy // 10
"""
层数无变化 或 有增长,返回新层数,更新信息;
层数负增长,判断时间,如果大于8秒,则用新层数,更新信息,
否则用老层数,不更新信息。
"""
if new_count >= self.record.last_update_count:
self.record.last_update_count = new_count
self.record.last_update_tick = tick_now
output = new_count
else:
if tick_now - self.record.last_update_tick > 480:
self.record.last_update_count = new_count
self.record.last_update_tick = tick_now
output = new_count
else:
output = self.record.last_update_count
output = min(output, self.buff_instance.ft.maxcount)
# print(new_count, output, (self.last_update_count, self.last_update_tick))
self.buff_instance.simple_start(tick_now, self.record.sub_exist_buff_dict)
self.buff_instance.dy.count = output
self.buff_instance.update_to_buff_0(self.buff_0)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/StreetSuperstar.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class StreetSuperstarRecord:
def __init__(self):
self.equipper = None
self.char = None
self.sub_exist_buff_dict = None
self.qte_counter = 0
self.max_qte = 3
self.active_signal = None
class StreetSuperstar(Buff.BuffLogic):
"""街头巨星的逻辑核心:任意角色释放QTE叠层、装备者释放大招触发。"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xstart = self.special_start_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"街头巨星", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = StreetSuperstarRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="街头巨星")
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise TypeError(
f"{self.buff_instance.ft.index}的xjudge函数获取到的skill_node不是SkillNode类型"
)
if not skill_node.preload_tick == find_tick(sim_instance=self.buff_instance.sim_instance):
return False
if skill_node.skill.trigger_buff_level == 5:
self.record.qte_counter = min(self.record.qte_counter + 1, self.record.max_qte)
elif skill_node.skill.trigger_buff_level == 6:
if skill_node.char_name == self.record.char.NAME:
self.record.active_signal = skill_node
return True
return False
def special_start_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="街头巨星", sub_exist_buff_dict=1)
if self.record.qte_counter == 0:
return
self.buff_instance.simple_start(
find_tick(sim_instance=self.buff_instance.sim_instance),
self.record.sub_exist_buff_dict,
specified_count=self.record.qte_counter,
no_end=1,
)
self.buff_instance.dy.endticks = (
find_tick(sim_instance=self.buff_instance.sim_instance)
+ self.record.active_signal.skill.ticks
)
self.buff_instance.update_to_buff_0(self.buff_0)
self.record.qte_counter = 0
self.active_signal = None
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/TheVault.py
================================================
from .. import Buff, JudgeTools, check_preparation
class TheVaultRecord:
def __init__(self):
self.equipper = None
self.char = None
self.action_stack = None
class TheVault(Buff.BuffLogic):
"""
聚宝箱的复杂逻辑模块,回能和增伤的判定逻辑都是一样的,
所以它们共用这一个逻辑模块。
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"聚宝箱", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
"""
这里的初始化,找到的buff_0实际上是佩戴者的buff_0
"""
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = TheVaultRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
聚宝箱的触发条件:自己的技能、技能命中帧、[强化E、大招、连携技]
"""
self.check_record_module()
self.get_prepared(equipper="聚宝箱", action_stack=1)
from zsim.sim_progress.Preload import SkillNode
skill_node: SkillNode | None = kwargs.get("skill_node", None)
if skill_node is None:
return False
if skill_node.char_name != self.record.char.NAME:
return False
if skill_node.skill.trigger_buff_level not in [2, 5, 6]:
return False
tick = self.buff_instance.sim_instance.tick
if skill_node.is_hit_now(tick):
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/TimeweaverApBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class TimeweaverApBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
self.enemy = None
class TimeweaverApBonus(Buff.BuffLogic):
"""时流贤者的电属性积蓄相关Buff逻辑。"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"时流贤者", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = TimeweaverApBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""时流贤者的电属性积蓄相关Buff的核心逻辑。"""
self.check_record_module()
self.get_prepared(equipper="时流贤者", enemy=1)
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise TypeError(
f"{self.buff_instance.ft.index}的xjudge函数获取的skill_node不是SkillNode类!"
)
# 过滤不是自己的skill_node
if self.record.char.NAME != skill_node.char_name:
return False
# 判断skill node的trigger_buff_level是否为1或2
if skill_node.skill.trigger_buff_level not in [1, 2]:
return False
# 判断当前是否是hit节点
if not skill_node.loading_mission.is_hit_now(
find_tick(sim_instance=self.buff_instance.sim_instance)
):
return False
# 判断敌人是否处于异常状态
if not self.record.enemy.dynamic.is_under_anomaly():
return False
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/TimeweaverDisorderDmgMul.py
================================================
from .. import Buff, JudgeTools, check_preparation
class TimeweaverDisorderDmgMulRecord:
def __init__(self):
self.equipper = None
self.char = None
self.preload_data = None
self.dynamic_buff_list = None
self.enemy = None
class TimeweaverDisorderDmgMul(Buff.BuffLogic):
"""时流贤者的精通AP检查相关Buff逻辑。"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"时流贤者", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = TimeweaverDisorderDmgMulRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""时流贤者的精通AP检查相关Buff的核心逻辑。"""
self.check_record_module()
self.get_prepared(equipper="时流贤者", preload_data=1, dynamic_buff_list=1, enemy=1)
from zsim.sim_progress.ScheduledEvent.Calculator import (
Calculator as Cal,
)
from zsim.sim_progress.ScheduledEvent.Calculator import (
MultiplierData as Mul,
)
mul_data = Mul(self.record.enemy, self.record.dynamic_buff_list, self.record.char)
ap = Cal.AnomalyMul.cal_ap(mul_data)
return ap >= 375
def special_exit_logic(self, **kwargs):
return not self.special_judge_logic(**kwargs)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/TriggerAdditionalAbilityStunBonus.py
================================================
from zsim.sim_progress.ScheduledEvent.Calculator import Calculator, MultiplierData
from .. import Buff, JudgeTools, check_preparation, find_tick
class TriggerAdditionalAbilityStunBonusRecord:
def __init__(self):
self.char = None
self.sub_exist_buff_dict = None
self.enemy = None
self.dynamic_buff_list = None
class TriggerAdditionalAbilityStunBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""扳机的组队被动,根据暴击率提升失衡值。"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["扳机"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = TriggerAdditionalAbilityStunBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""首先,扳机组队被动的判断逻辑和核心被动没有区别"""
self.check_record_module()
self.get_prepared(char_CID=1361)
from zsim.sim_progress.Preload import SkillNode
skill_node: SkillNode | None
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
raise ValueError(f"{self.buff_instance.ft.index}的xjudge中缺少skill_node参数")
if "1361" not in skill_node.skill_tag or not skill_node.skill.labels:
return False
if "aftershock_attack" in skill_node.skill.labels.keys():
return True
return False
def special_hit_logic(self, **kwargs):
"""判定通过后,执行Buff激活,计算实时暴击率,替换当前层数。"""
self.check_record_module()
self.get_prepared(char_CID=1361, sub_exist_buff_dict=1, enemy=1, dynamic_buff_list=1)
tick = find_tick(sim_instance=self.buff_instance.sim_instance)
mul_data = MultiplierData(
self.record.enemy, self.record.dynamic_buff_list, self.record.char
)
crit_rate = Calculator.RegularMul.cal_personal_crit_rate(mul_data)
"""「扳机」的暴击率高于40%时,每超过1%暴击率会使自身发动[追加攻击]造成的失衡值提升1.5%,最多提升75%。"""
count = min(max(crit_rate - 0.4, 0) / 0.01 * 1.5, 75)
# print(f'当前暴击率:{crit_rate}, 层数:{count}')
self.buff_instance.simple_start(tick, self.record.sub_exist_buff_dict, no_count=1)
self.buff_instance.dy.count = count
self.buff_instance.update_to_buff_0(self.buff_0)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/TriggerAfterShockTrigger.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class TriggerAfterShockTriggerRecord:
def __init__(self):
self.char = None
self.preload_data = None
self.active_signal_mission = None
self.after_shock_manager = None
class TriggerAfterShockTrigger(Buff.BuffLogic):
def __init__(self, buff_instance):
"""扳机的协同攻击触发器"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["扳机"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = TriggerAfterShockTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
触发器的xjudge函数,负责判断当前攻击是否能够触发协同攻击
"""
self.check_record_module()
self.get_prepared(char_CID=1361, preload_data=1)
loading_mission = kwargs.get("loading_mission", None)
if loading_mission is None:
raise ValueError(
f"{self.buff_instance.ft.index}的xjudge函数中,传入的loading_mission为None!"
)
from zsim.sim_progress.Load import LoadingMission
if not isinstance(loading_mission, LoadingMission):
raise TypeError(
f"{self.buff_instance.ft.index}的xjudge函数中,传入的loading_mission类型错误!"
)
tick = find_tick(sim_instance=self.buff_instance.sim_instance)
"""如果当前mission不是hit,则不触发"""
if not loading_mission.is_hit_now(tick):
return False
"""如果当前mission是扳机自己的动作,也不触发"""
if "1361" in loading_mission.mission_tag:
return False
"""
剩余情况汇总:队友的、正在命中的skill_node,即为可能触发协同攻击的skill_node,
但是,这里是不包含 扳机 决意值 的判断逻辑的,
因为在after_shock管理器中,决意值不够时会直接返回None。
"""
self.record.active_signal_mission = loading_mission
return True
def special_hit_logic(self, **kwargs):
"""
扳机的协同攻击触发的核心函数,负责抛出对应的协同攻击给Preload,
同时更新角色的决意值、协战状态管理器数据
"""
self.check_record_module()
self.get_prepared(char_CID=1361, preload_data=1)
if self.record.active_signal_mission is None:
raise ValueError(
f"{self.buff_instance.ft.index}的xjudge函数在本tick通过判定,但是并未将通过判定的skill_node传入自身的record中"
)
tick = find_tick(sim_instance=self.buff_instance.sim_instance)
after_shock_tag = self.record.char.after_shock_manager.spawn_after_shock(
tick, self.record.active_signal_mission
)
if after_shock_tag is not None:
insert_tuple = (after_shock_tag, False, 0)
self.record.preload_data.preload_action_list_before_confirm.append(insert_tuple)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/TriggerCoreSkillStunDMGBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class TriggerCoreSkillStunDMGBonusRecord:
def __init__(self):
self.char = None
class TriggerCoreSkillStunDMGBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
"""
扳机的核心被动,扳机发动的追加攻击能增加失衡易伤
"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["扳机"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = TriggerCoreSkillStunDMGBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""只要是检测到扳机释放的协同攻击,就返回True"""
self.check_record_module()
self.get_prepared(char_CID=1361)
from zsim.sim_progress.Preload import SkillNode
skill_node: SkillNode | None
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
raise ValueError(f"{self.buff_instance.ft.index}的xjudge并未成功获取到skill_node!")
if "1361" not in skill_node.skill_tag or not skill_node.skill.labels:
return False
if "aftershock_attack" in skill_node.skill.labels.keys():
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/VivianAdditionalAbilityCoAttackTrigger.py
================================================
from zsim.define import VIVIAN_REPORT
from .. import Buff, JudgeTools, check_preparation, find_tick
class VivianAdditionalAbilityCoAttackTriggerRecord:
def __init__(self):
self.char = None
self.last_update_anomaly = None # 上次更新的异常。
self.cd = 30 # 内置CD0.5秒
self.last_update_tick = 0 # 上次更新时间
self.preload_data = None
class VivianAdditionalAbilityCoAttackTrigger(Buff.BuffLogic):
def __init__(self, buff_instance):
"""薇薇安组队被动中的协同攻击触发器"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xeffect = self.special_effect_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["薇薇安"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = VivianAdditionalAbilityCoAttackTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""检测到属性异常传入后,进行判定。如果新的异常,则放行。"""
self.check_record_module()
self.get_prepared(char_CID=1331, preload_data=1)
anomaly_bar = kwargs.get("anomaly_bar", None)
if anomaly_bar is None:
return False
from zsim.sim_progress.anomaly_bar import AnomalyBar
if not isinstance(anomaly_bar, AnomalyBar):
raise TypeError(
f"{self.buff_instance.ft.index}的xjudge函数获取的{anomaly_bar}不是AnomalyBar类!"
)
# 如果是VVA自己触发的异常,则不放行。
if anomaly_bar.activated_by:
if "1331" in anomaly_bar.activated_by.skill_tag:
if VIVIAN_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print("组队被动:检测到薇薇安触发的属性异常,不放行!")
return False
# 如果是首次传入的属性异常类,则直接放行。
tick = find_tick(sim_instance=self.buff_instance.sim_instance)
if self.record.last_update_anomaly is None:
self.record.last_update_anomaly = anomaly_bar
self.record.last_update_tick = tick
return True
# 如果是同一异常,则不放行。
if id(anomaly_bar) == id(self.record.last_update_anomaly):
return False
# CD没转好,不触发。
if tick - self.record.last_update_tick < self.record.cd:
return False
self.record.last_update_anomaly = anomaly_bar
self.record.last_update_tick = tick
return True
def special_effect_logic(self, **kwargs):
"""一旦Xjudge放行,那么就执行本函数,试图生成一次生花。"""
self.check_record_module()
self.get_prepared(char_CID=1361, preload_data=1)
coattack_skill_tag = self.record.char.feather_manager.spawn_coattack()
if coattack_skill_tag is None:
if VIVIAN_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(
f"组队被动:虽然有{self.record.last_update_anomaly.element_type}类型的新异常触发!但是豆子不够!当前资源情况为:{self.record.char.get_special_stats()}"
)
return
input_tuple = (coattack_skill_tag, False, 0)
self.record.preload_data.external_add_skill(input_tuple)
if VIVIAN_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(
f"组队被动:监听到队友的技能{self.record.last_update_anomaly.activate_by}触发了新的异常,薇薇安触发了一次落雨生花!"
)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/VivianCinema1Debuff.py
================================================
from .. import Buff, JudgeTools, check_preparation
class VVivianCinema1DebuffRecord:
def __init__(self):
self.char = None
self.enemy = None
class VivianCinema1Debuff(Buff.BuffLogic):
def __init__(self, buff_instance):
"""薇薇安1画的负面效果判定逻辑"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["薇薇安"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = VVivianCinema1DebuffRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""检测到敌人身上有薇薇安的预言Dot就放行"""
self.check_record_module()
self.get_prepared(char_CID=1331, enemy=1)
if self.record.enemy.find_dot("ViviansProphecy"):
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/VivianCinema6Trigger.py
================================================
import math
from zsim.define import VIVIAN_REPORT
from zsim.sim_progress.ScheduledEvent.Calculator import (
Calculator as Cal,
)
from zsim.sim_progress.ScheduledEvent.Calculator import (
MultiplierData as Mul,
)
from .. import Buff, JudgeTools, check_preparation
class VivianCinema6TriggerRecord:
def __init__(self):
self.char = None
self.preload_data = None
self.last_update_node = None
self.enemy = None
self.dynamic_buff_list = None
self.sub_exist_buff_dict = None
self.cinema_ratio = None
self.guard_feather = None
@property
def c6_ratio(self):
return self.guard_feather * 0.8
class VivianCinema6Trigger(Buff.BuffLogic):
def __init__(self, buff_instance):
"""薇薇安的核心被动触发器"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xeffect = self.special_effect_logic
self.ANOMALY_RATIO_MUL = {
0: 0.0075,
1: 0.08,
2: 0.0108,
3: 0.032,
4: 0.0615,
5: 0.0108,
}
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["薇薇安"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = VivianCinema6TriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
薇薇安的核心被动触发器:
触发机制为:全队任意角色触发属性异常的第一跳时,构造一个新的属性异常放到Evenlist中
"""
self.check_record_module()
self.get_prepared(char_CID=1331, enemy=1)
skill_node = kwargs.get("skill_node", None)
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise TypeError(
f"{self.buff_instance.ft.index}的xjudge函数获取到的skill_node不是SkillNode类型!"
)
if skill_node.skill_tag != "1331_SNA_2":
return False
if not self.record.enemy.dynamic.is_under_anomaly:
if VIVIAN_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(" APL警告:怪物没异常你打什么SNA_2!豆子全没了吧傻子!")
if self.record.last_update_node is None:
self.c6_pre_active(skill_node)
return True
else:
if skill_node.UUID != self.record.last_update_node.UUID:
self.c6_pre_active(skill_node)
return True
return False
def c6_pre_active(self, skill_node):
self.record.last_update_node = skill_node
guard_feather_cost = min(self.record.char.feather_manager.guard_feather, 5)
if VIVIAN_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(
f"6画触发器:检测到【悬落】,即将消耗全部护羽!消耗前的资源情况为:{self.record.char.get_special_stats()}"
)
self.record.guard_feather = guard_feather_cost
self.record.char.feather_manager.guard_feather = 0
self.record.char.feather_manager.c1_counter += guard_feather_cost
while self.record.char.feather_manager.c1_counter >= 4:
self.record.char.feather_manager.c1_counter -= 4
self.record.char.feather_manager.flight_feather = min(
self.record.char.feather_manager.flight_feather + 1, 5
)
if VIVIAN_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(
f"6画触发器:因6画触发、联动1画,恢复一点飞羽!当前资源情况为:{self.record.char.get_special_stats()}"
)
def special_effect_logic(self, **kwargs):
"""当Xjudge检测到AnomalyBar传入时通过判定,并且执行xeffect"""
self.check_record_module()
self.get_prepared(
char_CID=1361,
preload_data=1,
dynamic_buff_list=1,
enemy=1,
sub_exist_buff_dict=1,
)
from zsim.sim_progress.anomaly_bar import AnomalyBar
get_result = self.record.enemy.dynamic.get_active_anomaly()
if not get_result:
self.record.char.feather_manager.update_myself(c6_signal=True)
if VIVIAN_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(
"6画触发器:在怪物没有异常的情况下打了【悬落】,虽然不能触发额外的异放,但是依然可以进行羽毛转化!"
)
else:
active_anomaly_bar = get_result[0]
copyed_anomaly = AnomalyBar.create_new_from_existing(active_anomaly_bar)
if not copyed_anomaly.settled:
copyed_anomaly.anomaly_settled()
# copyed_anomaly = self.record.last_update_anomaly
event_list = JudgeTools.find_event_list(sim_instance=self.buff_instance.sim_instance)
mul_data = Mul(self.record.enemy, self.record.dynamic_buff_list, self.record.char)
ap = Cal.AnomalyMul.cal_ap(mul_data)
from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import (
DirgeOfDestinyAnomaly,
)
dirge_of_destiny_anomaly = DirgeOfDestinyAnomaly(
copyed_anomaly,
active_by="1331",
sim_instance=self.buff_instance.sim_instance,
)
ratio = self.ANOMALY_RATIO_MUL.get(copyed_anomaly.element_type)
if self.record.cinema_ratio is None:
self.record.cinema_ratio = 1 if self.record.char.cinema < 2 else 1.3
final_ratio = (
math.floor(ap / 10) * ratio * self.record.cinema_ratio * self.record.c6_ratio
)
dirge_of_destiny_anomaly.anomaly_dmg_ratio = final_ratio
# 在柚叶版本更新后,异常计算的逻辑改变了。current_ndarray不再动态变更,而是在属性异常触发后集中计算。
# 所以,这里获取到的current_ndarray是已经计算好的,所以这里不需要除以当前异常值
# dirge_of_destiny_anomaly.current_ndarray = (
# dirge_of_destiny_anomaly.current_ndarray
# / dirge_of_destiny_anomaly.current_anomaly
# )
event_list.append(dirge_of_destiny_anomaly)
if VIVIAN_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(
f"6画触发器:触发额外异放!本次触发消耗额外护羽数量为:{self.record.guard_feather},当前资源情况为:{self.record.char.get_special_stats()}"
)
self.record.guard_feather = 0
self.record.char.feather_manager.update_myself(c6_signal=True)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/VivianCoattackTrigger.py
================================================
from zsim.define import VIVIAN_REPORT
from .. import Buff, JudgeTools, check_preparation, find_tick
class VivianCoattackTriggerRecord:
def __init__(self):
self.char = None
self.preload_data = None
self.last_update_node = None
self.JUDGE_MAP = {
"1221_E_EX_1": lambda: self.last_update_node.end_tick
>= find_tick(sim_instance=self.char.sim_instance),
"1221_E_EX_2": lambda: False,
}
class VivianCoattackTrigger(Buff.BuffLogic):
def __init__(self, buff_instance):
"""薇薇安的协同攻击(落雨生花)触发器"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xeffect = self.special_effect_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["薇薇安"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = VivianCoattackTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""检测到队友释放强化E并且第一跳命中时放行。"""
self.check_record_module()
self.get_prepared(char_CID=1331, preload_data=1)
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return
from zsim.sim_progress.Load import LoadingMission
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode | LoadingMission):
return
if isinstance(skill_node, LoadingMission):
skill_node = skill_node.mission_node
# 首先过滤所有非强化E标签的技能
if skill_node.skill.trigger_buff_level != 2:
return False
# 过滤所有并非第一跳的技能
tick = find_tick(sim_instance=self.buff_instance.sim_instance)
if not skill_node.loading_mission.is_first_hit(tick):
return False
# 如果是首次传入,直接放行
if self.record.last_update_node is None:
self.record.last_update_node = skill_node
return True
else:
# 并非首次传入时,判断是否是同一个技能
if skill_node.UUID == self.record.last_update_node.UUID:
return False
else:
# 若是不同技能,进入最后一个判断分支
if skill_node.skill_tag in self.record.JUDGE_MAP:
result = self.record.JUDGE_MAP[skill_node.skill_tag]()
if result:
self.record.last_update_node = skill_node
else:
return False
else:
self.record.last_update_node = skill_node
return True
def special_effect_logic(self, **kwargs):
"""执行后直接添加一次落雨生花到eventlist——该动作没有动画,所以直接进event_list即可"""
self.check_record_module()
self.get_prepared(char_CID=1361, preload_data=1)
coattack_skill_tag = self.record.char.feather_manager.spawn_coattack()
if coattack_skill_tag is None:
if VIVIAN_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(
f"【落雨生花】触发器:虽然监听到了队友的强化E:{self.record.last_update_node.skill_tag},但是豆子不够!当前资源情况为:{self.record.char.get_special_stats()}"
)
return
input_tuple = (coattack_skill_tag, False, 0)
self.record.preload_data.external_add_skill(input_tuple)
if VIVIAN_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(
f"【落雨生花】触发器:监测到强化特殊技{self.record.last_update_node.skill_tag},薇薇安成功触发了一次落雨生花!(迟滞1tick)"
)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/VivianCorePassiveTrigger.py
================================================
from zsim.define import VIVIAN_REPORT
from zsim.sim_progress.ScheduledEvent.Calculator import (
Calculator as Cal,
)
from zsim.sim_progress.ScheduledEvent.Calculator import (
MultiplierData as Mul,
)
from .. import Buff, JudgeTools, check_preparation
class VivianCorePassiveTriggerRecord:
def __init__(self):
self.char = None
self.preload_data = None
self.last_update_node = None
self.enemy = None
self.dynamic_buff_list = None
self.sub_exist_buff_dict = None
self.cinema_ratio = None
class VivianCorePassiveTrigger(Buff.BuffLogic):
def __init__(self, buff_instance):
"""薇薇安的核心被动触发器"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xeffect = self.special_effect_logic
self.ANOMALY_RATIO_MUL = {
0: 0.0075,
1: 0.08,
2: 0.0108,
3: 0.032,
4: 0.0615,
5: 0.0108,
}
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["薇薇安"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = VivianCorePassiveTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
薇薇安的核心被动触发器:
触发机制为:落羽生花命中处于异常状态的目标时,构造一个新的属性异常放到Evenlist中
"""
self.check_record_module()
self.get_prepared(char_CID=1331, enemy=1)
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise TypeError(
f"{self.buff_instance.ft.index}的xjudge函数获取到的skill_node 不是SkillNode类型"
)
if skill_node.skill_tag != "1331_CoAttack_A":
return False
if not self.record.enemy.dynamic.is_under_anomaly():
return False
if self.record.last_update_node is None:
self.record.last_update_node = skill_node
return True
else:
if skill_node.UUID != self.record.last_update_node.UUID:
self.record.last_update_node = skill_node
return True
else:
return False
def special_effect_logic(self, **kwargs):
"""当Xjudge检测到AnomalyBar传入时通过判定,并且执行xeffect"""
self.check_record_module()
self.get_prepared(
char_CID=1361,
preload_data=1,
dynamic_buff_list=1,
enemy=1,
sub_exist_buff_dict=1,
)
from zsim.sim_progress.anomaly_bar import AnomalyBar
get_result = self.record.enemy.dynamic.get_active_anomaly()
if not get_result:
raise ValueError(
f"{self.buff_instance.ft.index}的xeffect函数中,enemy.get_active_anomlay函数返回空列表,说明此时没有异常。但是xjudge函数却放行了。"
)
active_anomaly_bar = get_result[0]
copyed_anomaly = AnomalyBar.create_new_from_existing(active_anomaly_bar)
if not copyed_anomaly.settled:
copyed_anomaly.anomaly_settled()
event_list = JudgeTools.find_event_list(sim_instance=self.buff_instance.sim_instance)
mul_data = Mul(self.record.enemy, self.record.dynamic_buff_list, self.record.char)
ap = Cal.AnomalyMul.cal_ap(mul_data)
from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import (
DirgeOfDestinyAnomaly,
)
dirge_of_destiny_anomaly = DirgeOfDestinyAnomaly(
copyed_anomaly,
active_by="1331",
sim_instance=self.buff_instance.sim_instance,
)
ratio = self.ANOMALY_RATIO_MUL.get(copyed_anomaly.element_type)
if self.record.cinema_ratio is None:
self.record.cinema_ratio = 1 if self.record.char.cinema < 2 else 1.3
"""20250424参考波波獭视频,该倍率是每一点精通平滑收益,并非向下取整,故此调整模型,去掉floor。"""
"""final_ratio = math.floor(ap/10) * ratio * self.record.cinema_ratio"""
final_ratio = ap / 10 * ratio * self.record.cinema_ratio
dirge_of_destiny_anomaly.anomaly_dmg_ratio = final_ratio
# dirge_of_destiny_anomaly.current_ndarray = (
# dirge_of_destiny_anomaly.current_ndarray
# / dirge_of_destiny_anomaly.current_anomaly
# )
event_list.append(dirge_of_destiny_anomaly)
if VIVIAN_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print("核心被动:检测到【落羽生花】命中异常状态下的敌人,触发一次异放!!!")
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/VivianDotTrigger.py
================================================
from zsim.define import VIVIAN_REPORT
from .. import Buff, JudgeTools, check_preparation, find_tick
class VivianDotTriggerRecord:
def __init__(self):
self.char = None
self.enemy = None
class VivianDotTrigger(Buff.BuffLogic):
def __init__(self, buff_instance):
"""薇薇安的Dot(薇薇安的预言)触发器"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["薇薇安"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = VivianDotTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""检测到敌人处于属性异常状态,并且是SNA2或者是协同攻击时,放行"""
self.check_record_module()
self.get_prepared(char_CID=1331, enemy=1)
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise TypeError(
f"{self.buff_instance.ft.index}的Xjudge函数获取到的skill_node不是SkillNode类型,请检查!"
)
# 筛选出能够触发dot的SNA_2和生花
if skill_node.skill_tag not in ["1331_SNA_2", "1331_CoAttack_A"]:
return False
# 检测到当前tick有命中时,放行。
tick = find_tick(sim_instance=self.buff_instance.sim_instance)
if not skill_node.loading_mission.is_hit_now(tick):
return False
# 如果敌人不处于异常状态,不放行
if not self.record.enemy.dynamic.is_under_anomaly():
return False
return True
def special_hit_logic(self, **kwargs):
"""xjudge放行后,直接生成dot。但是如果dot已经存在,就不重复生成。"""
self.check_record_module()
self.get_prepared(char_CID=1361, enemy=1)
# 如果敌人身上已经存在这个dot,直接不执行
if self.record.enemy.find_dot("ViviansProphecy") is not None:
return
from zsim.sim_progress.Load import LoadingMission
from zsim.sim_progress.Update.UpdateAnomaly import spawn_normal_dot
dot = spawn_normal_dot("ViviansProphecy", sim_instance=self.buff_instance.sim_instance)
dot.start(find_tick(sim_instance=self.buff_instance.sim_instance))
event_list = JudgeTools.find_event_list(sim_instance=self.buff_instance.sim_instance)
dot.skill_node_data.loading_mission = LoadingMission(dot.skill_node_data)
dot.skill_node_data.loading_mission.mission_start(
find_tick(sim_instance=self.buff_instance.sim_instance)
)
self.record.enemy.dynamic.dynamic_dot_list.append(dot)
event_list.append(dot.skill_node_data)
if VIVIAN_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print("核心被动:薇薇安对敌人施加Dot——薇薇安的预言")
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/VivianFeatherTrigger.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class VivianFeatherTriggerRecord:
def __init__(self):
self.char = None
self.last_update_node = None
class VivianFeatherTrigger(Buff.BuffLogic):
def __init__(self, buff_instance):
"""管理薇薇安羽毛更新的触发器"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["薇薇安"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = VivianFeatherTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""检测到最后一跳时放行"""
self.check_record_module()
self.get_prepared(char_CID=1331)
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Preload import SkillNode
if not isinstance(skill_node, SkillNode):
raise TypeError(
f"{self.buff_instance.ft.index}的xjudge函数获取的skill_node不是SkillNode类型"
)
# 过滤掉不是自己的skill_node
if "1331" not in skill_node.skill_tag:
return False
# 放行所有正处于最后一跳的skill_node
tick = find_tick(sim_instance=self.buff_instance.sim_instance)
if skill_node.loading_mission.is_last_hit(tick):
self.record.last_update_node = skill_node
return True
else:
return False
def special_hit_logic(self, **kwargs):
"""只要触发器放行了,那么special_hit就一定会执行,执行一次后,把record清空即可。"""
self.check_record_module()
self.get_prepared(char_CID=1331)
self.record.char.feather_manager.update_myself(self.record.last_update_node)
self.record.last_update_node = None
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/WeepingCradleDMGBonusIncrease.py
================================================
from .. import Buff, JudgeTools, check_preparation
class WeepingCradleDMGBRecord:
def __init__(self):
self.equipper = None
self.char = None
self.sub_exist_buff_dict = None
self.trigger_buff_0 = None
self.sub_exist_buff_dict = None
self.last_update_tick = 0
class WeepingCradleDMGBonusIncrease(Buff.BuffLogic):
"""
这是啜泣摇篮的自增伤模组。
它需要检测触发器buff [Buff-武器-啜泣摇篮-全队增伤]的 状态来判定自身的状态,这里还涉及一个字符串拼接问题,保证该逻辑能在各个buff中通用。
同时,它还需要判定自身更新的CD,来规避绝无效运算。
判定通过后,它通过special effect来启动,并且进行自叠层。
启动阶段,它的起止时间是触发器buff的时间,这点比较特殊,所以在simple start之后,要修改起止时间并且重新update
而在自叠层阶段,它只修改层数,不修改起止时间。
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
# 初始化特定逻辑
self.xjudge = self.special_judge_logic
self.xeffect = self.special_effect_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"啜泣摇篮", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None: # 这里的初始化,找到的buff_0实际上是佩戴者的buff_0,
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = WeepingCradleDMGBRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
trigger_index = f"Buff-武器-精{int(self.buff_instance.ft.refinement)}啜泣摇篮-全队增伤"
self.get_prepared(
equipper="啜泣摇篮",
trigger_buff_0=(self.buff_instance.ft.operator, trigger_index),
)
if self.record.trigger_buff_0.dy.active:
result = self.increase_cd_judge()
if result:
return True
else:
return False
else:
return False
def special_effect_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="啜泣摇篮", sub_exist_buff_dict=1)
tick_now = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
if not self.buff_0.dy.active:
self.buff_instance.simple_start(tick_now, self.record.sub_exist_buff_dict)
self.buff_instance.dy.startticks = self.record.trigger_buff_0.dy.startticks
self.buff_instance.dy.endticks = self.record.trigger_buff_0.dy.endticks
self.buff_instance.update_to_buff_0(self.buff_0)
else:
self.buff_instance.simple_start(
tick_now, self.record.sub_exist_buff_dict, no_start=True, no_end=True
)
def increase_cd_judge(self):
tick_now = JudgeTools.find_tick(sim_instance=self.buff_instance.sim_instance)
if tick_now - self.record.last_update_tick >= self.buff_instance.ft.cd:
self.record.last_update_tick = tick_now
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/WeepingGeminiApBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation, find_tick
class WeepingGeminiApBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
self.last_update_anomaly = None
self.enemy = None
self.last_update_stun = False
self.sub_exist_buff_dict = None
class WeepingGeminiApBonus(Buff.BuffLogic):
"""双生泣星的精通增幅判定。"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xeffect = self.special_effect_logic
self.xexit = self.special_exit_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"双生泣星", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = WeepingGeminiApBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""检测到新属性异常触发,直接放行。"""
self.check_record_module()
self.get_prepared(equipper="双生泣星")
anomaly_bar = kwargs.get("anomaly_bar", None)
if anomaly_bar is None:
return False
from zsim.sim_progress.anomaly_bar import AnomalyBar
if not isinstance(anomaly_bar, AnomalyBar):
raise TypeError(
f"{self.buff_instance.ft.index}的xjudge函数获取的{anomaly_bar}不是AnomalyBar类!"
)
if anomaly_bar.activated_by:
if self.record.equipper != anomaly_bar.activated_by.char_name:
return False
if self.record.last_update_anomaly is None:
self.record.last_update_anomaly = anomaly_bar
return True
# 如果是同一异常,则不放行。
if id(anomaly_bar) == id(self.record.last_update_anomaly):
return False
self.record.last_update_anomaly = anomaly_bar
return True
def special_effect_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="双生泣星", enemy=1, sub_exist_buff_dict=1)
self.buff_instance.simple_start(
find_tick(sim_instance=self.buff_instance.sim_instance),
self.record.sub_exist_buff_dict,
)
# print(f'检测到新的异常状态!层数更新!当前层数:{self.buff_instance.dy.built_in_buff_box}')
def special_exit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="双生泣星", enemy=1)
enemy = self.record.enemy
if self.record.last_update_stun:
if not enemy.dynamic.stun:
self.record.last_update_stun = enemy.dynamic.stun
# print(f'检测到敌人失衡状态的下降沿,Buff清空!')
return True
self.record.last_update_stun = enemy.dynamic.stun
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/WoodpeckerElectroSet4_CA.py
================================================
from __future__ import annotations
from typing import TYPE_CHECKING
from .. import Buff, JudgeTools, check_preparation
if TYPE_CHECKING:
from zsim.sim_progress.RandomNumberGenerator import RNG
class WoodpeckerElectroCARecord:
def __init__(self):
self.equipper = None
self.char = None
self.dynamic_buff_list = None
self.enemy = None
self.action_stack = None
class WoodpeckerElectroSet4_CA(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
# 初始化特定逻辑
self.xjudge = self.special_judge_logic
self.buff_0 = None
self.record = None
self.equipper = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"啄木鸟电音", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = WoodpeckerElectroCARecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="啄木鸟电音", enemy=1, dynamic_buff_list=1, action_stack=1)
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Load import LoadingMission
from zsim.sim_progress.Preload import SkillNode
if isinstance(skill_node, SkillNode):
pass
elif isinstance(skill_node, LoadingMission):
skill_node = skill_node.mission_node
else:
return False
if str(self.record.char.CID) not in skill_node.skill_tag:
return False
from zsim.sim_progress.ScheduledEvent.Calculator import Calculator, MultiplierData
mul_data = MultiplierData(
self.record.enemy, self.record.dynamic_buff_list, self.record.char
)
if skill_node.skill.trigger_buff_level == 4:
rng: RNG = self.buff_instance.sim_instance.rng_instance
normalized_value = rng.random_float()
cric_rate = Calculator.RegularMul.cal_crit_rate(mul_data)
if normalized_value <= cric_rate:
return True
else:
return False
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/WoodpeckerElectroSet4_E_EX.py
================================================
from zsim.sim_progress.RandomNumberGenerator import RNG
from zsim.sim_progress.ScheduledEvent.Calculator import Calculator, MultiplierData
from .. import Buff, JudgeTools, check_preparation
class WoodpeckerElectroEXRecord:
def __init__(self):
self.equipper = None
self.char = None
self.dynamic_buff_list = None
self.enemy = None
self.action_stack = None
class WoodpeckerElectroSet4_E_EX(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
# 初始化特定逻辑
self.xjudge = self.special_judge_logic
self.buff_0 = None
self.record = None
self.equipper = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"啄木鸟电音", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = WoodpeckerElectroEXRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="啄木鸟电音", enemy=1, dynamic_buff_list=1, action_stack=1)
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Load import LoadingMission
from zsim.sim_progress.Preload import SkillNode
if isinstance(skill_node, SkillNode):
pass
elif isinstance(skill_node, LoadingMission):
skill_node = skill_node.mission_node
else:
return False
if str(self.record.char.CID) not in skill_node.skill_tag:
return False
mul_data = MultiplierData(
self.record.enemy, self.record.dynamic_buff_list, self.record.char
)
if skill_node.skill.trigger_buff_level == 2:
cric_rate = Calculator.RegularMul.cal_crit_rate(mul_data)
rng: RNG = self.buff_instance.sim_instance.rng_instance
normalized_value = rng.random_float()
if normalized_value <= cric_rate:
return True
else:
return False
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/WoodpeckerElectroSet4_NA.py
================================================
from zsim.sim_progress.RandomNumberGenerator import RNG
from zsim.sim_progress.ScheduledEvent.Calculator import Calculator, MultiplierData
from .. import Buff, JudgeTools, check_preparation
class WoodpeckerElectroNARecord:
def __init__(self):
self.equipper = None
self.char = None
self.dynamic_buff_list = None
self.enemy = None
self.action_stack = None
class WoodpeckerElectroSet4_NA(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
# 初始化特定逻辑
self.xjudge = self.special_judge_logic
self.buff_0 = None
self.record = None
self.equipper = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"啄木鸟电音", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = WoodpeckerElectroNARecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="啄木鸟电音", enemy=1, dynamic_buff_list=1, action_stack=1)
skill_node = kwargs.get("skill_node", None)
if skill_node is None:
return False
from zsim.sim_progress.Load import LoadingMission
from zsim.sim_progress.Preload import SkillNode
if isinstance(skill_node, SkillNode):
pass
elif isinstance(skill_node, LoadingMission):
skill_node = skill_node.mission_node
else:
return False
if str(self.record.char.CID) not in skill_node.skill_tag:
return False
mul_data = MultiplierData(
self.record.enemy, self.record.dynamic_buff_list, self.record.char
)
if skill_node.skill.trigger_buff_level == 0:
cric_rate = Calculator.RegularMul.cal_crit_rate(mul_data)
rng: RNG = self.buff_instance.sim_instance.rng_instance
normalized_value = rng.random_float()
if normalized_value <= cric_rate:
return True
else:
return False
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YanagiCinema6EXDmgBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class YanagiCinema6EXDmgBonusRecord:
def __init__(self):
self.char = None
class YanagiCinema6EXDmgBonus(Buff.BuffLogic):
"""
柳的6画,森罗万象激活时,通过判定。
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["柳"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YanagiCinema6EXDmgBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""检测当前的森罗万象状态是否开启,若开启则通过判定。"""
self.check_record_module()
self.get_prepared(char_CID=1221)
if self.record.char.get_special_stats()["森罗万象状态"]:
return True
else:
return False
def special_exit_logic(self, **kwargs):
return not self.special_judge_logic(**kwargs)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YanagiPolarityDisorderTrigger.py
================================================
from copy import deepcopy
from .. import Buff, JudgeTools, check_preparation, find_tick
class YanagiPolarityDisorderTriggerRecord:
def __init__(self):
self.char = None
self.enemy = None
self.polarity_disorder_update_signal = (
False # 极性紊乱更新信号:理论上生命周期只有0个tick,本tick放行,本tick处理后重置
)
self.e_counter = {"update_from": "", "count": 0} # 突刺攻击的计数器
self.e_max_count = None # 突刺攻击的最大次数
self.polarity_disorder_basic_dmg_ratio = None # 极性紊乱的基础倍率
self.polarity_disorder_ap_ratio = 32 # 固定的3200%精通倍率
class YanagiPolarityDisorderTrigger(Buff.BuffLogic):
"""
柳的极性紊乱的触发器。
极性紊乱会在强化E下落攻击和Q的最后一个Hit触发,
若是一个招式内同时触发了感电和极性紊乱,则应该先结算感电,再结算极性紊乱;
根据目前ZSim的结构,属性异常检测、属性异常更新、Buff判断循环启动这几个步骤的顺序应该为:
Buff判断循环启动 ——> 触发器启动 ——> 属性异常更新——> 技能伤害计算——> 异常条更新
所以,如果在极性紊乱更新的Tick,同时触发了新的属性异常,
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xeffect = self.special_effect_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["柳"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YanagiPolarityDisorderTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs) -> bool:
"""
柳的极性紊乱的触发器判断机制,即Enemy身上存在属性异常,就放行,并且向record释放更新信号。
"""
self.check_record_module()
self.get_prepared(char_CID=1221, enemy=1)
obj_input = kwargs.get("skill_node", None)
# 筛选出能够和极性紊乱系统互动的三种技能
if obj_input is None:
return False
from zsim.sim_progress.Load import LoadingMission
from zsim.sim_progress.Preload import SkillNode
if not isinstance(obj_input, SkillNode | LoadingMission):
raise TypeError(
f"{self.buff_instance.ft.index}的xjudge模块中获取到的{obj_input}不是SkillNode类也不是LoadingMission类!"
)
skill_node = obj_input if isinstance(obj_input, SkillNode) else obj_input.mission_node
if skill_node.skill_tag not in ["1221_E_EX_1", "1221_E_EX_2", "1221_Q"]:
return False
# 正确性判断
if self.record.polarity_disorder_update_signal:
raise ValueError("上一次极性紊乱的更新信号仍旧存在,请检查代码")
# 如果检测到穿刺攻击,则进入对应分支——更新连击次数,但是最后要返回False——因为穿刺攻击无法结算极性紊乱;
if skill_node.skill_tag == "1221_E_EX_1":
# 如果上一次更新的UUID是空,则说明是第一个动作,或者是一个全新的强化E的开始,则直接跳过第一轮分支,进入连击次数更新环节。
if self.record.e_counter["update_from"] == "":
pass
else:
# 如果UUID相同,说明是同一个技能的不同hit,直接返回False
if skill_node.UUID == self.record.e_counter["update_from"]:
return False
if self.record.char.cinema >= 2:
if self.record.e_max_count is None:
self.record.e_max_count = 2 if self.record.char.cinema < 6 else 4
self.record.e_counter["count"] += 1
if self.record.e_counter["count"] >= self.record.e_max_count:
self.record.e_counter["count"] = self.record.e_max_count
self.record.e_counter["update_from"] = skill_node.UUID
return False
# 若是另外两个攻击,则应该检查是否是最后一跳,放行前,打开更新信号。
else:
tick = find_tick(sim_instance=self.buff_instance.sim_instance)
if tick - 1 < skill_node.loading_mission.get_last_hit() <= tick: # 此时就是最后一跳
if self.record.enemy.dynamic.is_under_anomaly(): # 并且存在激活的属性异常
self.record.polarity_disorder_update_signal = True
return True
self.record.e_counter = {"update_from": "", "count": 0}
return False
def special_effect_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1221, enemy=1)
if not self.record.polarity_disorder_update_signal:
raise ValueError("在极性紊乱触发信号未激活时,执行了触发函数!")
# 根据角色命座,初始化基础倍率
if self.record.polarity_disorder_basic_dmg_ratio is None:
self.record.polarity_disorder_basic_dmg_ratio = (
0.15 if self.record.char.cinema < 2 else 0.2
)
# 根据连击次数,计算最终缩放倍率
final_ratio = (
self.record.polarity_disorder_basic_dmg_ratio + 0.15 * self.record.e_counter["count"]
)
# 获取当前正在激活的属性异常条
active_anomaly_bar = self.record.enemy.get_active_anomaly_bar()
active_bar_deep_copy = deepcopy(active_anomaly_bar)
if not active_bar_deep_copy.settled:
active_bar_deep_copy.anomaly_settled()
# 构造极性紊乱对象
from zsim.sim_progress.Update import spawn_output
polarity_disorder_output = spawn_output(
active_bar_deep_copy,
mode_number=2,
polarity_ratio=final_ratio,
skill_node=kwargs["skill_node"],
sim_instance=self.buff_instance.sim_instance,
)
# polarity_disorder_output = spawn_output(active_anomaly_bar, mode_number=1)
# 置入event_list
event_list = JudgeTools.find_event_list(sim_instance=self.buff_instance.sim_instance)
event_list.append(polarity_disorder_output)
# 清空记录,回收更新信号
self.record.e_counter = {"update_from": "", "count": 0}
self.record.polarity_disorder_update_signal = False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YanagiStanceJougen.py
================================================
from .. import Buff, JudgeTools, check_preparation
class YanagiStanceJougenRecord:
def __init__(self):
self.char = None
class YanagiStanceJougen(Buff.BuffLogic):
"""
柳的上弦增幅,检测到上弦状态就通过判定
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["柳"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YanagiStanceJougenRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
检测柳的当前状态,如果当前状态为上弦就通过判定。
"""
self.check_record_module()
self.get_prepared(char_CID=1221)
if self.record.char.stance_manager.stance_now:
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YanagiStanceKagen.py
================================================
from .. import Buff, JudgeTools, check_preparation
class YanagiStanceKagenRecord:
def __init__(self):
self.char = None
class YanagiStanceKagen(Buff.BuffLogic):
"""
柳的下弦增幅,检测到下弦状态就通过判定
"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["柳"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YanagiStanceKagenRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
检测柳的当前状态,如果当前状态为下弦就通过判定。
"""
self.check_record_module()
self.get_prepared(char_CID=1221)
if self.record.char.stance_manager.stance_now:
return False
else:
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YangiCinema1ApBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class YangiCinema1ApBonusRecord:
def __init__(self):
self.char = None
self.trigger_buff_0 = None
class YangiCinema1ApBonus(Buff.BuffLogic):
"""柳1画的精通增幅"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["柳"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YangiCinema1ApBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""
检测触发器Buff洞悉的层数,层数>= 1 就触发!
"""
self.check_record_module()
self.get_prepared(char_CID=1221, trigger_buff_0=("柳", "Buff-角色-柳-1画-洞悉"))
if self.record.trigger_buff_0.dy.active:
if self.record.trigger_buff_0.dy.count >= 1:
return True
return False
def special_exit_logic(self, **kwargs):
"""退出逻辑和触发逻辑相反!"""
return not self.special_judge_logic(**kwargs)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YixuanAdditionalAbilityDmgBonus.py
================================================
from typing import TYPE_CHECKING
from zsim.define import YIXUAN_REPORT
from .. import Buff, JudgeTools, check_preparation
if TYPE_CHECKING:
from zsim.sim_progress.Preload import SkillNode
class YixuanAdditionalAbilityDmgBonusRecord:
def __init__(self):
self.char = None
self.trigger_buff_0 = None
class YixuanAdditionalAbilityDmgBonus(Buff.BuffLogic):
"""仪玄组队被动的增伤效果:触发条件是:凝云术和墨烬影消命中失衡状态下的敌人时触发。"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["仪玄"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YixuanAdditionalAbilityDmgBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1371)
skill_node: "SkillNode | None" = kwargs.get("skill_node", None)
if skill_node is None:
return False
enemy = self.buff_instance.sim_instance.schedule_data.enemy
if not enemy.dynamic.stun:
return False
if "1371_E_EX_B_" not in skill_node.skill_tag:
return False
if skill_node.preload_tick == self.buff_instance.sim_instance.tick:
if YIXUAN_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(
f"仪玄的{skill_node.skill.skill_text}命中了失衡状态下的敌人,触发了组队被动的增伤效果!"
)
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YixuanCinema1Trigger.py
================================================
from typing import TYPE_CHECKING
from zsim.define import YIXUAN_REPORT
from .. import Buff, JudgeTools, check_preparation
if TYPE_CHECKING:
from zsim.sim_progress.Character.Yixuan import Yixuan
from zsim.sim_progress.Preload import SkillNode
from zsim.sim_progress.Preload.PreloadDataClass import PreloadData
class YixuanCinema1TriggerRecord:
def __init__(self):
self.char = None
self.lighting_strike_skill_tag = "1371_Cinema_1"
self.preload_data = None
self.adrenaline_value = 5
self.sub_exist_buff_dict = None
class YixuanCinema1Trigger(Buff.BuffLogic):
"""仪玄1画的触发器"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["仪玄"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YixuanCinema1TriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""仪玄1画的触发器逻辑,当任意技能命中时放行。"""
self.check_record_module()
self.get_prepared(char_CID=1371)
skill_node: "SkillNode | None" = kwargs.get("skill_node", None)
if skill_node is None:
return False
tick = self.buff_instance.sim_instance.tick
if skill_node.char_name == "仪玄":
return False
if skill_node.is_hit_now(tick):
return True
return False
def special_hit_logic(self, **kwargs):
"""向event_list抛出一个落雷以及恢复角色5点闪能值"""
self.check_record_module()
self.get_prepared(char_CID=1221, preload_data=1, sub_exist_buff_dict=1)
preload_data: "PreloadData" = self.record.preload_data
char: "Yixuan" = self.record.char
simulator = self.buff_instance.sim_instance
event_list = simulator.schedule_data.event_list
tick = simulator.tick
# 处理落雷
from zsim.sim_progress.Load import LoadingMission
from zsim.sim_progress.Preload.SkillsQueue import spawn_node
lightning_strick_node: "SkillNode" = spawn_node(
tag=self.record.lighting_strike_skill_tag,
preload_tick=tick,
skills=preload_data.skills,
)
loading_mission = LoadingMission(mission=lightning_strick_node)
loading_mission.mission_start(tick)
lightning_strick_node.loading_mission = loading_mission
event_list.append(lightning_strick_node)
char.update_adrenaline(sp_value=self.record.adrenaline_value)
self.buff_instance.simple_start(
timenow=tick, sub_exist_buff_dict=self.record.sub_exist_buff_dict
)
if YIXUAN_REPORT:
print(
f"1画:生成一道落雷,并且为仪玄回复5点闪能值,仪玄当前闪能值:{char.adrenaline: .2f}"
)
self.buff_instance.sim_instance.schedule_data.change_process_state()
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YixuanCinema2StunTimeLimitBonus.py
================================================
from typing import TYPE_CHECKING
from zsim.define import YIXUAN_REPORT
from .. import Buff, JudgeTools, check_preparation
if TYPE_CHECKING:
from zsim.sim_progress.Preload import SkillNode
class YixuanCinema2StunTimeLimitBonusRecord:
def __init__(self):
self.char = None
self.enemy = None
self.required_skill_tag = "1371_Q"
class YixuanCinema2StunTimeLimitBonus(Buff.BuffLogic):
"""仪玄2画效果:增加怪物失衡时间"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xexit = self.special_exit_logic
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["仪玄"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YixuanCinema2StunTimeLimitBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1371, enemy=1)
skill_node: "SkillNode | None" = kwargs.get("skill_node", None)
if skill_node is None:
return False
if skill_node.skill_tag != self.record.required_skill_tag:
return False
if not self.record.enemy.dynamic.stun:
return False
if skill_node.preload_tick != self.buff_instance.sim_instance.tick:
return False
if YIXUAN_REPORT:
print(
"2画:检测到仪玄释放喧响值大招!敌人正处于失衡状态,2画效果生效,延长敌人3秒失衡时间!"
)
self.buff_instance.sim_instance.schedule_data.change_process_state()
return True
def special_exit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1371, enemy=1)
if not self.record.enemy.dynamic.stun:
if YIXUAN_REPORT:
print("2画:检测到敌人从失衡状态中恢复,仪玄2画的失衡时间延长效果结束!")
self.buff_instance.sim_instance.schedule_data.change_process_state()
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YixuanCinema4Tranquility.py
================================================
from typing import TYPE_CHECKING
from zsim.define import YIXUAN_REPORT
from .. import Buff, JudgeTools, check_preparation
if TYPE_CHECKING:
from zsim.sim_progress.Preload import SkillNode
class YixuanCinema4TranquilityRecord:
def __init__(self):
self.char = None
self.update_signal = None
self.sub_exist_buff_dict = None
self.c4_counter = 0 # 静心层数
self.max_c4_count = 2
class YixuanCinema4Tranquility(Buff.BuffLogic):
"""仪玄4画的静心判定逻辑"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xeffect = self.special_effect_logic
self.xexit = self.special_exit_logic
self.buff_0 = None
self.record: YixuanCinema4TranquilityRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["仪玄"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YixuanCinema4TranquilityRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""仪玄4画的复杂逻辑,当传入技能是仪玄的大招时,叠层;当传入的技能是凝云术时,消耗层数。"""
self.check_record_module()
self.get_prepared(char_CID=1371)
skill_node: "SkillNode | None" = kwargs.get("skill_node", None)
if skill_node is None:
return False
tick = self.buff_instance.sim_instance.tick
if skill_node.char_name != "仪玄":
return False
if skill_node.skill.trigger_buff_level == 6:
# 发动大招时叠层
if skill_node.preload_tick != tick:
return False
if self.record.update_signal is not None:
raise ValueError(
f"{self.buff_instance.ft.index}的Xjudge函数的【大招分支】发现record中存在尚未处理的更新信号:{self.record.update_signal}!"
)
self.record.update_signal = 0
return True
else:
# 检测到第二次墨烬影结束消层
if skill_node.skill_tag == "1371_E_EX_B_3":
if skill_node.end_tick != tick:
return False
if self.record.update_signal is not None:
raise ValueError(
f"{self.buff_instance.ft.index}的Xjudge函数的【凝云术分支】发现record中存在尚未处理的更新信号:{self.record.update_signal}!"
)
self.record.update_signal = 1
return True
return False
def special_effect_logic(self, **kwargs):
"""4画的特殊生效逻辑,它根据record中的更新信号(update_signal),有两种模式,一种是叠层,每一种则是消层"""
self.check_record_module()
self.get_prepared(char_CID=1371, sub_exist_buff_dict=1)
if self.record.update_signal is None:
raise ValueError(
f"{self.buff_instance.ft.index}的Xeffect函数执行时,并未检测到有效的更新信号!"
)
sim = self.buff_instance.sim_instance
self.buff_instance.simple_start(
timenow=sim.tick,
sub_exist_buff_dict=self.record.sub_exist_buff_dict,
no_count=1,
)
if self.record.update_signal == 0:
self.record.c4_counter = min(self.record.c4_counter + 1, self.record.max_c4_count)
self.buff_instance.dy.count = self.record.c4_counter
if YIXUAN_REPORT:
print(
f"4画:检测到仪玄释放大招,为仪玄叠加一层静心,当前的静心层数为:{self.record.c4_counter}"
)
self.buff_instance.sim_instance.schedule_data.change_process_state()
elif self.record.update_signal == 1:
# 经过实测,4画在消耗时会一次性消耗全部层数。
if self.record.c4_counter != 0:
if YIXUAN_REPORT:
print(
f"4画:检测到仪玄释放凝云术,本次凝云术消耗{self.record.c4_counter}层静心!"
)
self.buff_instance.sim_instance.schedule_data.change_process_state()
self.record.c4_counter = 0
self.buff_instance.dy.count = self.record.c4_counter
else:
raise ValueError(f"无法解析的更新信号!{self.record.update_signal}")
self.buff_instance.update_to_buff_0(self.buff_0)
self.record.update_signal = None
def special_exit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1371)
if self.record.c4_counter == 0:
if YIXUAN_REPORT:
print("4画:静心层数耗尽!Buff消退!")
self.buff_instance.sim_instance.schedule_data.change_process_state()
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YunkuiTalesSheerAtkBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class YunkuiTalesSheerAtkBonusRecord:
def __init__(self):
self.equipper = None
self.char = None
self.trigger_buff_0 = None
class YunkuiTalesSheerAtkBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"云岿如我", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YunkuiTalesSheerAtkBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(
equipper="云岿如我",
trigger_buff_0=(self.equipper, "Buff-驱动盘-云岿如我-四件套-暴击率提升"),
)
trigger_buff_0: Buff = self.record.trigger_buff_0
if trigger_buff_0.dy.active:
if trigger_buff_0.dy.count == 3:
return True
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YuzuhaAdditionalAbilityAnomalyBuildupBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class YuzuhaAdditionalAbilityAnomalyBuildupBonusRecord:
def __init__(self):
self.char = None
self.sub_exist_buff_dict = None
self.dynamic_buff_list = None
self.enemy = None
self.cinema_1_ratio = None
class YuzuhaAdditionalAbilityAnomalyBuildupBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xhit = self.special_hit_logic
self.buff_0 = None
self.record: YuzuhaAdditionalAbilityAnomalyBuildupBonusRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["柚叶"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YuzuhaAdditionalAbilityAnomalyBuildupBonusRecord()
self.record = self.buff_0.history.record
def special_hit_logic(self, **kwargs):
"""buff激活时,根据柚叶的异常掌控计算层数"""
self.check_record_module()
self.get_prepared(char_CID=1411, sub_exist_buff_dict=1, enemy=1, dynamic_buff_list=1)
if self.record.cinema_1_ratio is None:
self.record.cinema_1_ratio = 1 if self.record.char.cinema < 1 else 1.3
from zsim.sim_progress.ScheduledEvent.Calculator import Calculator, MultiplierData
mul_data = MultiplierData(
self.record.enemy, self.record.dynamic_buff_list, self.record.char
)
am = Calculator.AnomalyMul.cal_am(mul_data)
if am < 100:
return
count = min(am - 100, 100) * self.record.cinema_1_ratio
tick = self.buff_instance.sim_instance.tick
self.buff_instance.simple_start(
timenow=tick, sub_exist_buff_dict=self.record.sub_exist_buff_dict, no_count=1
)
self.buff_instance.dy.count = count
self.buff_instance.update_to_buff_0(buff_0=self.buff_0)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YuzuhaAdditionalAbilityAnomalyDmgBonus.py
================================================
from ....define import YUZUHA_REPORT
from .. import Buff, JudgeTools, check_preparation
class YuzuhaAdditionalAbilityAnomalyDmgBonusRecord:
def __init__(self):
self.char = None
self.sub_exist_buff_dict = None
self.dynamic_buff_list = None
self.enemy = None
self.cinema_1_ratio = None
class YuzuhaAdditionalAbilityAnomalyDmgBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xhit = self.special_hit_logic
self.buff_0 = None
self.record: YuzuhaAdditionalAbilityAnomalyDmgBonusRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["柚叶"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YuzuhaAdditionalAbilityAnomalyDmgBonusRecord()
self.record = self.buff_0.history.record
def special_hit_logic(self, **kwargs):
"""buff激活时,根据柚叶的异常掌控计算层数"""
self.check_record_module()
self.get_prepared(char_CID=1411, sub_exist_buff_dict=1, enemy=1, dynamic_buff_list=1)
if self.record.cinema_1_ratio is None:
self.record.cinema_1_ratio = 1 if self.record.char.cinema < 1 else 1.3
from zsim.sim_progress.ScheduledEvent.Calculator import Calculator, MultiplierData
mul_data = MultiplierData(
self.record.enemy, self.record.dynamic_buff_list, self.record.char
)
am = Calculator.AnomalyMul.cal_am(mul_data)
if am < 100:
return
count = min(am - 100, 100) * self.record.cinema_1_ratio
tick = self.buff_instance.sim_instance.tick
self.buff_instance.simple_start(
timenow=tick, sub_exist_buff_dict=self.record.sub_exist_buff_dict, no_count=1
)
self.buff_instance.dy.count = count
self.buff_instance.update_to_buff_0(buff_0=self.buff_0)
if YUZUHA_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(
f"【柚叶组队被动】检测到【狸之愿】激活,当前柚叶的异常掌控为{am:.2f}点,共计提供{count * 0.2:.2f}%的异常积蓄效率以及属性异常/紊乱增伤"
)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinem1EleResReduce.py
================================================
from typing import TYPE_CHECKING
from .. import Buff, JudgeTools, check_preparation
if TYPE_CHECKING:
from ...Preload import SkillNode
class YuzuhaCinem1EleResReduceRecord:
def __init__(self):
self.char = None
self.enemy = None
class YuzuhaCinem1EleResReduce(Buff.BuffLogic):
def __init__(self, buff_instance):
"""柚叶的1画,一样是甜蜜惊吓判定逻辑"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["柚叶"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YuzuhaCinem1EleResReduceRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""只有两种强化E和大招的重攻击才能触发甜蜜惊吓效果"""
self.check_record_module()
self.get_prepared(char_CID=1411, enemy=1)
skill_node: "SkillNode" = kwargs.get("skill_node")
if skill_node is None:
return False
if skill_node.skill_tag not in ["1411_E_EX_A", "1411_E_EX_B", "1411_Q"]:
return False
if not skill_node.is_last_hit(tick=self.buff_instance.sim_instance.tick):
return False
else:
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema2Trigger.py
================================================
from typing import TYPE_CHECKING
from zsim.define import YUZUHA_REPORT
from .. import Buff, JudgeTools, check_preparation
if TYPE_CHECKING:
from zsim.sim_progress.Preload import SkillNode
class YuzuhaCinema2TriggerRecord:
def __init__(self):
self.char = None
self.allowed_skill_tag_list = ["1411_E_EX_A", "1411_E_EX_B", "1411_Q"]
self.skill_node_be_changed = None
self.cd = 1200
self.last_update_tick = None
self.enemy = None
class YuzuhaCinema2Trigger(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.buff_0 = None
self.record: YuzuhaCinema2TriggerRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["柚叶"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YuzuhaCinema2TriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1411, enemy=1)
if self.record.skill_node_be_changed is not None:
raise ValueError("【浮波柚叶2画触发器】存在尚未处理的更新信号!!")
if self.record.enemy.dynamic.stun:
return False
skill_node: "SkillNode" = kwargs.get("skill_node")
if skill_node is None:
return False
if skill_node.skill_tag not in self.record.allowed_skill_tag_list:
return False
if not self.ready:
return False
tick = self.buff_instance.sim_instance.tick
if not skill_node.is_last_hit(tick=tick):
return False
else:
self.record.skill_node_be_changed = skill_node
return True
@property
def ready(self):
if self.record.last_update_tick is None:
return True
tick = self.buff_instance.sim_instance.tick
if tick - self.record.last_update_tick > self.record.cd:
return True
else:
return False
def special_hit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1411)
if self.record.skill_node_be_changed is None:
raise ValueError("【浮波柚叶2画触发器】未发现更新信号!!")
skill_node: "SkillNode" = self.record.skill_node_be_changed
skill_node.force_qte_trigger = True
if YUZUHA_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(
f"【柚叶2画】检测到{skill_node.skill_tag}的重攻击即将命中非失衡敌人!修改参数使其能够触发QTE!"
)
self.record.skill_node_be_changed = None
self.record.last_update_tick = self.buff_instance.sim_instance.tick
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema4QuickAssistTrigger.py
================================================
from typing import TYPE_CHECKING
from zsim.define import YUZUHA_REPORT
from .. import Buff, JudgeTools, check_preparation
if TYPE_CHECKING:
from zsim.sim_progress.data_struct.QuickAssistSystem import QuickAssistSystem
from zsim.sim_progress.Preload import SkillNode
class YuzuhaCinema4QuickAssistTriggerRecord:
def __init__(self):
self.char = None
self.allowed_skill_tag_list = [
"1411_Assault_Aid",
"1411_Assault_Aid_A",
"1411_Assault_Aid_B",
]
self.trigger_skill_node = None
class YuzuhaCinema4QuickAssistTrigger(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.buff_0 = None
self.record: YuzuhaCinema4QuickAssistTriggerRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["柚叶"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YuzuhaCinema4QuickAssistTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""浮波柚叶的4画触发器——支援突击触发快速支援"""
self.check_record_module()
self.get_prepared(char_CID=1411)
if self.record.trigger_skill_node is not None:
raise ValueError("【柚叶4画触发器】存在尚未处理的快支触发事件!")
skill_node: "SkillNode" = kwargs.get("skill_node")
if skill_node is None:
return False
if skill_node.skill_tag not in self.record.allowed_skill_tag_list:
return False
tick = self.buff_instance.sim_instance.tick
if not skill_node.is_last_hit(tick=tick):
return False
self.record.trigger_skill_node = skill_node
return True
def special_hit_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1411)
sim_instance = self.buff_instance.sim_instance
QAS: "QuickAssistSystem" = sim_instance.preload.preload_data.quick_assist_system
target_char_obj = sim_instance.char_data.find_next_char_obj(char_now=1411, direction=1)
QAS.force_active_quick_assist(
tick_now=sim_instance.tick,
skill_node=self.record.trigger_skill_node,
char_name=target_char_obj.NAME,
)
if YUZUHA_REPORT:
sim_instance.schedule_data.change_process_state()
print(
f"【柚叶4画】技能 {self.record.trigger_skill_node.skill_tag} 最后一击命中,并且成功激活了 {target_char_obj.NAME} 的快速支援!"
)
self.record.trigger_skill_node = None
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema6SheelTrigger.py
================================================
from typing import TYPE_CHECKING
from zsim.define import YUZUHA_REPORT
from .. import Buff, JudgeTools, check_preparation
if TYPE_CHECKING:
from zsim.sim_progress.Character.Yuzuha import Yuzuha
from zsim.sim_progress.Preload import SkillNode
class YuzuhaCinema6SheelTriggerRecord:
def __init__(self):
self.char = None
self.allowed_skill_tag = "1411_Assault_Aid_B"
self.charging_start = False
self.charging_tick = 0
self.sheel_counter = 0
class YuzuhaCinema6SheelTrigger(Buff.BuffLogic):
"""6画的炮弹触发逻辑"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xeffect = self.special_effect_logic
self.buff_0 = None
self.record: YuzuhaCinema6SheelTriggerRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["柚叶"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YuzuhaCinema6SheelTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""模拟蓄力时间"""
self.check_record_module()
self.get_prepared(char_CID=1411)
skill_node: "SkillNode" = kwargs.get("skill_node")
if skill_node is None:
return False
if skill_node.skill_tag != self.record.allowed_skill_tag:
return False
tick = self.buff_instance.sim_instance.tick
lasting_tick = tick - skill_node.preload_tick
if 0 <= lasting_tick < 24:
return False
else:
self.record.charging_start = True
if self.record.charging_tick >= 24:
char: "Yuzuha" = self.record.char
if char.get_resources()[1] < 1:
return False
else:
return True
else:
if tick == skill_node.end_tick:
self.record.charging_start = False
self.record.sheel_counter = 0
self.record.charging_tick = 0
return False
self.record.charging_tick += 1
return False
def special_effect_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1411)
from zsim.sim_progress.data_struct.SchedulePreload import schedule_preload_event_factory
sim_instance = self.buff_instance.sim_instance
preload_tick_list = [sim_instance.tick]
skill_tag_list = ["1411_Cinema_6"]
preload_data = sim_instance.preload.preload_data
schedule_preload_event_factory(
preload_tick_list=preload_tick_list,
skill_tag_list=skill_tag_list,
preload_data=preload_data,
sim_instance=sim_instance,
)
self.record.sheel_counter += 1
self.record.charging_tick = 0
if YUZUHA_REPORT:
sim_instance.schedule_data.change_process_state()
print(
f"【柚叶6画】检测到正在›蓄力支援突击,将发射1枚炮弹,这是本次蓄力的第{self.record.sheel_counter}枚"
)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema6SugarBurstMaxTrigger.py
================================================
from .. import Buff, JudgeTools, check_preparation
class YuzuhaCinema6SugarBurstMaxTriggerRecord:
def __init__(self):
self.char = None
self.enemy = None
class YuzuhaCinema6SugarBurstMaxTrigger(Buff.BuffLogic):
"""炮弹命中甜蜜惊吓状态的敌人时,会触发一次彩糖花火·极"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.buff_0 = None
self.record: YuzuhaCinema6SugarBurstMaxTriggerRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["柚叶"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YuzuhaCinema6SugarBurstMaxTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=1411, enemy=1)
skill_node = kwargs.get("skill_node")
if skill_node is None:
return False
if skill_node.skill_tag != "1411_Cinema_6":
return False
if self.record.enemy.special_state_manager:
pass
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YuzuhaCorePassiveSweetScare.py
================================================
from typing import TYPE_CHECKING
from .. import Buff, JudgeTools, check_preparation
if TYPE_CHECKING:
from ...Preload import SkillNode
class YuzuhaCorePassiveSweetScareRecord:
def __init__(self):
self.char = None
self.enemy = None
class YuzuhaCorePassiveSweetScare(Buff.BuffLogic):
def __init__(self, buff_instance):
"""柚叶的甜蜜惊吓判定逻辑(该buff只作为标志物使用!不含任何业务逻辑和实际效果)"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["柚叶"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YuzuhaCorePassiveSweetScareRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""只有两种强化E和大招的重攻击才能触发甜蜜惊吓效果"""
self.check_record_module()
self.get_prepared(char_CID=1411, enemy=1)
skill_node: "SkillNode" = kwargs.get("skill_node")
if skill_node is None:
return False
if skill_node.skill_tag not in ["1411_E_EX_A", "1411_E_EX_B", "1411_Q"]:
return False
if not skill_node.is_last_hit(tick=self.buff_instance.sim_instance.tick):
return False
else:
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YuzuhaHardCandyShotTrigger.py
================================================
from typing import TYPE_CHECKING
from .. import Buff, JudgeTools, check_preparation
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
from ...Character.Yuzuha import Yuzuha
from ...Preload import SkillNode
class YuzuhaHardCandyShotTriggerRecord:
def __init__(self):
self.char = None
self.sub_exist_buff_dict = None
self.cd = None
self.last_update_tick = None
self.update_signal = None
class YuzuhaHardCandyShotTrigger(Buff.BuffLogic):
def __init__(self, buff_instance):
"""硬糖射击触发器"""
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.buff_0 = None
self.record = None
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["柚叶"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YuzuhaHardCandyShotTriggerRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""队友的攻击命中时放行"""
self.check_record_module()
self.get_prepared(char_CID=1411)
skill_node: "SkillNode" = kwargs.get("skill_node")
# 筛选出队友的攻击命中
if skill_node is None:
return False
if skill_node.char_name == self.record.char.NAME:
return False
tick = self.buff_instance.sim_instance.tick
if not skill_node.is_hit_now(tick=tick):
return False
char: "Yuzuha" = self.record.char
# 先保证角色有空
sim_instance: "Simulator" = self.buff_instance.sim_instance
if sim_instance.preload.preload_data.char_occupied_check(char_cid=char.CID, tick=tick):
return False
# 再保证甜度点足够
if char.get_resources()[1] < 1:
return False
else:
if self.ready:
self.record.update_signal = skill_node
return True
return False
def special_hit_logic(self, **kwargs):
"""触发硬糖射击,首先要进行一次simple_start保证触发内置CD,然后再执行业务逻辑"""
self.check_record_module()
self.get_prepared(char_CID=1411, sub_exist_buff_dict=1)
char: "Yuzuha" = self.record.char
tick = self.buff_instance.sim_instance.tick
self.buff_instance.simple_start(
timenow=tick, sub_exist_buff_dict=self.record.sub_exist_buff_dict
)
char.spawn_hard_candy_shot(update_signal=self.record.update_signal)
self.record.last_update_tick = tick
self.record.update_signal = None
@property
def ready(self):
if self.record.cd is None:
self.record.cd = 480 if self.record.char.cinema < 2 else 360
if self.record.last_update_tick is None:
return True
if self.buff_instance.sim_instance.tick - self.record.last_update_tick > self.record.cd:
return True
else:
return False
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YuzuhaSugarBurstAnomalyBuildupBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class YuzuhaSugarBurstAnomalyBuildupBonusRecord:
def __init__(self):
self.char = None
self.na_skill_level = None
self.skill_tag = "1411_SNA_A"
self.basic_count = 6
self.count_growth_per_level = 1.5
self.sub_exist_buff_dict = None
class YuzuhaSugarBurstAnomalyBuildupBonus(Buff.BuffLogic):
"""柚叶自带Buff——彩糖花火积蓄值提升。由于数值和buff等级挂钩所以需要在这里控制层数;"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.buff_0 = None
self.record: YuzuhaSugarBurstAnomalyBuildupBonusRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["柚叶"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YuzuhaSugarBurstAnomalyBuildupBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""只放行彩糖花火"""
self.check_record_module()
self.get_prepared(char_CID=1411)
skill_node = kwargs.get("skill_node")
if skill_node is None:
return False
if skill_node.skill_tag != self.record.skill_tag:
return False
if skill_node.preload_tick != self.buff_instance.sim_instance.tick:
return False
else:
return True
def special_hit_logic(self, **kwargs):
"""根据技能等级生成对应层数"""
self.check_record_module()
self.get_prepared(char_CID=1411, na_skill_level=1, sub_exist_buff_dict=1)
self.buff_instance.simple_start(
timenow=self.buff_instance.sim_instance.tick,
sub_exist_buff_dict=self.record.sub_exist_buff_dict,
no_count=1,
)
count = (
self.record.basic_count
+ self.record.na_skill_level * self.record.count_growth_per_level
)
self.buff_instance.dy.count = count
self.buff_instance.update_to_buff_0(buff_0=self.buff_0)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YuzuhaSugarBurstMaxAnomalyBuildupBonus.py
================================================
from .. import Buff, JudgeTools, check_preparation
class YuzuhaSugarBurstMaxAnomalyBuildupBonusRecord:
def __init__(self):
self.char = None
self.na_skill_level = None
self.skill_tag = "1411_SNA_B"
self.basic_count = 6
self.count_growth_per_level = 1.5
self.sub_exist_buff_dict = None
class YuzuhaSugarBurstMaxAnomalyBuildupBonus(Buff.BuffLogic):
"""柚叶自带Buff——彩糖花火·极积蓄值提升。由于数值和buff等级挂钩所以需要在这里控制层数;"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.xhit = self.special_hit_logic
self.buff_0 = None
self.record: YuzuhaSugarBurstMaxAnomalyBuildupBonusRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["柚叶"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YuzuhaSugarBurstMaxAnomalyBuildupBonusRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""只放行彩糖花火·极"""
self.check_record_module()
self.get_prepared(char_CID=1411)
skill_node = kwargs.get("skill_node")
if skill_node is None:
return False
if skill_node.skill_tag != self.record.skill_tag:
return False
if skill_node.preload_tick != self.buff_instance.sim_instance.tick:
return False
else:
return True
def special_hit_logic(self, **kwargs):
"""根据技能等级生成对应层数"""
self.check_record_module()
self.get_prepared(char_CID=1411, na_skill_level=1, sub_exist_buff_dict=1)
self.buff_instance.simple_start(
timenow=self.buff_instance.sim_instance.tick,
sub_exist_buff_dict=self.record.sub_exist_buff_dict,
no_count=1,
)
count = (
self.record.basic_count
+ self.record.na_skill_level * self.record.count_growth_per_level
)
self.buff_instance.dy.count = count
self.buff_instance.update_to_buff_0(buff_0=self.buff_0)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/YuzuhaTanukiWishAtkBonus.py
================================================
from ....define import YUZUHA_REPORT
from .. import Buff, JudgeTools, check_preparation
class YuzuhaTanukiWishAtkBonusRecord:
def __init__(self):
self.char = None
self.core_passive_ratio = 0.4
self.sub_exist_buff_dict = None
class YuzuhaTanukiWishAtkBonus(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xhit = self.special_hit_logic
self.buff_0 = None
self.record: YuzuhaTanukiWishAtkBonusRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["柚叶"][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = YuzuhaTanukiWishAtkBonusRecord()
self.record = self.buff_0.history.record
def special_hit_logic(self, **kwargs):
"""buff激活时,根据柚叶的场外攻击力计算层数,"""
self.check_record_module()
self.get_prepared(char_CID=1411, sub_exist_buff_dict=1)
tick = self.buff_instance.sim_instance.tick
static_atk = self.record.char.statement.ATK
count = min(static_atk * self.record.core_passive_ratio, self.buff_instance.ft.maxcount)
self.buff_instance.simple_start(
timenow=tick, sub_exist_buff_dict=self.record.sub_exist_buff_dict, no_count=1
)
self.buff_instance.dy.count = count
self.buff_instance.update_to_buff_0(buff_0=self.buff_0)
if YUZUHA_REPORT:
self.buff_instance.sim_instance.schedule_data.change_process_state()
print(
f"【狸之愿】柚叶核心被动触发!柚叶场外站街攻击力为{static_atk:.2f}点,【狸之愿】为队友提供{count:.2f}点攻击力!"
)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/ZanshinHerbCase.py
================================================
from .. import Buff, JudgeTools, check_preparation
class ZanshinHerbCaseRecord:
def __init__(self):
self.equipper = None
self.char = None
self.listener_exist = False
self.listener = None
class ZanshinHerbCase(Buff.BuffLogic):
"""激素朋克的复杂逻辑模块,检测到监听器更新信号时更新。"""
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"残心青囊", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = ZanshinHerbCaseRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
"""检测到更新信号时,返回True,并且置空监听器的active_signal。"""
self.check_record_module()
self.get_prepared(equipper="残心青囊")
if not self.record.listener_exist:
self.record.listener = self.buff_instance.sim_instance.listener_manager.get_listener(
listener_owner=self.record.char, listener_id="Zanshin_Herb_Case_1"
)
self.record.listener_exist = True
# print(f"为{self.record.char.NAME}创建了一个残心青囊的监听器!")
active_signal = self.record.listener.active_signal
if active_signal is None:
return False
else:
"""置空信号"""
self.record.listener.listener_active()
print("检测到残心青囊 的触发信号!触发第三特效!")
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/__init__.py
================================================
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/_buff_record_base_class.py
================================================
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from ...Character.Alice import Alice
from ...Character.character import Character
from ...Character.Seed import Seed
from ...Character.Yixuan import Yixuan
from ...data_struct.ActionStack import ActionStack
from ...Enemy import Enemy
from ...Preload.PreloadDataClass import PreloadData
from ..buff_class import Buff
class BuffRecordBaseClass:
def __init__(self):
self.char: "Character | None | Alice | Yixuan | Seed" = None
self.sub_exist_buff_dict: dict[str, "Buff"] | None = None
self.dynamic_buff_list: dict[str, list] | None = None
self.enemy: "Enemy | None" = None
self.equipper: "str | None" = None
self.action_stack: "ActionStack | None" = None
self.event_list: list | None = None
self.preload_data: "PreloadData | None" = None
self.char_obj_list: "list[Character] | None" = None
self.na_skill_level: "int | None" = None
self.trans_ratio: float = 0
self.cd: int = 60 # 内置CD:1秒一次
self.last_active_tick: int = 0 # 上次触发的时间点
self.buff_index: str | None = None
self.trigger_buff_0: "Buff | None" = None
self.additional_damage_skill_tag: str | None = None
self.trigger_skill_tag: str | None = None
def check_cd(self, tick_now: int):
"""检查内置CD是否就绪"""
if self.cd == 0:
return True
if tick_now - self.last_active_tick < self.cd:
return False
else:
return True
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/_char_buff_mod.py
================================================
from .. import Buff, JudgeTools, check_preparation
from ._buff_record_base_class import BuffRecordBaseClass as BRBC
class CharBuffXLogicNameRecord(BRBC):
def __init__(self):
super().__init__()
class CharBuffXLogicName(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.buff_0: "Buff | None" = None
self.record: BRBC | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)["角色名字"][self.buff_instance.ft.index]
assert self.buff_0 is not None, (
"【Buff初始化警告】角色名字的复杂逻辑模块未正确初始化,请检查函数"
)
if self.buff_0.history.record is None:
self.buff_0.history.record = CharBuffXLogicNameRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(char_CID=0000)
assert self.record is not None, (
f"【Buff初始化警告】{self.buff_instance.ft.index}的复杂逻辑模块未正确初始化,请检查函数"
)
================================================
FILE: zsim/sim_progress/Buff/BuffXLogic/_euipment_buff_mod.py
================================================
from .. import Buff, JudgeTools, check_preparation
class BuffXLogicNameRecord:
def __init__(self):
self.equipper = None
self.char = None
class BuffXLogicName(Buff.BuffLogic):
def __init__(self, buff_instance):
super().__init__(buff_instance)
self.buff_instance: Buff = buff_instance
self.xjudge = self.special_judge_logic
self.equipper = None
self.buff_0 = None
self.record: BuffXLogicNameRecord | None = None
def get_prepared(self, **kwargs):
return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs)
def check_record_module(self):
if self.equipper is None:
self.equipper = JudgeTools.find_equipper(
"装备名字", sim_instance=self.buff_instance.sim_instance
)
if self.buff_0 is None:
self.buff_0 = JudgeTools.find_exist_buff_dict(
sim_instance=self.buff_instance.sim_instance
)[self.equipper][self.buff_instance.ft.index]
if self.buff_0.history.record is None:
self.buff_0.history.record = BuffXLogicNameRecord()
self.record = self.buff_0.history.record
def special_judge_logic(self, **kwargs):
self.check_record_module()
self.get_prepared(equipper="装备名字")
================================================
FILE: zsim/sim_progress/Buff/JudgeTools/DetectEdges.py
================================================
def detect_edge(pair, mode_func):
# 使用自定义的mode_func函数来判断是否为上升沿或下降沿
return mode_func(pair[0], pair[1])
================================================
FILE: zsim/sim_progress/Buff/JudgeTools/FindCharFromCID.py
================================================
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
def find_char_from_CID(CID: int, sim_instance: "Simulator"):
char_list = sim_instance.char_data.char_obj_list
for char in char_list:
if char.CID == CID:
return char
else:
raise ValueError(f"并未找到CID为{CID}的角色!")
================================================
FILE: zsim/sim_progress/Buff/JudgeTools/FindCharFromName.py
================================================
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
def find_char_from_name(NAME: str, sim_instance: "Simulator | None" = None):
assert sim_instance is not None, "sim_instance不能为空"
char_list = sim_instance.char_data.char_obj_list
for _ in char_list:
if _.NAME == NAME:
return _
else:
raise ValueError(f"未找到名为{NAME}的角色")
================================================
FILE: zsim/sim_progress/Buff/JudgeTools/FindEquipper.py
================================================
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
def find_equipper(item_name: str, sim_instance: "Simulator" = None):
"""
该函数用来找佩戴装备的人,但是需要注意,这个函数处理不了多个人同时佩戴同一件装备的情况。
"""
Judge_list_set = sim_instance.init_data.Judge_list_set
if "二件套" not in item_name:
"""默认找的是四件套 和武器"""
for sub_list in Judge_list_set:
for i in sub_list:
if i == item_name and i != sub_list[3]:
return sub_list[0]
else:
"""找的是二件套"""
for sub_list in Judge_list_set:
for i in sub_list:
if i == item_name and i == sub_list[3]:
return sub_list[0]
================================================
FILE: zsim/sim_progress/Buff/JudgeTools/FindMain.py
================================================
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
def find_enemy(sim_instance: "Simulator" = None):
enemy = sim_instance.schedule_data.enemy
return enemy
def find_init_data(sim_instance: "Simulator" = None):
init_data = sim_instance.init_data
return init_data
def find_char_list(sim_instance: "Simulator" = None):
char_list = sim_instance.char_data.char_obj_list
return char_list
def find_dynamic_buff_list(sim_instance: "Simulator" = None):
dynamic_buff_list = sim_instance.global_stats.DYNAMIC_BUFF_DICT
return dynamic_buff_list
def find_tick(sim_instance: "Simulator" = None):
tick = sim_instance.tick
return tick
def find_exist_buff_dict(sim_instance: "Simulator" = None):
exist_buff_dict = sim_instance.load_data.exist_buff_dict
return exist_buff_dict
def find_event_list(sim_instance: "Simulator" = None):
event_list = sim_instance.schedule_data.event_list
return event_list
def find_stack(sim_instance: "Simulator" = None):
stack = sim_instance.load_data.action_stack
return stack
def find_load_data(sim_instance: "Simulator" = None):
load_data = sim_instance.load_data
return load_data
def find_schedule_data(sim_instance: "Simulator" = None):
schedule_data = sim_instance.schedule_data
return schedule_data
def find_preload_data(sim_instance: "Simulator" = None):
preload_data = sim_instance.preload.preload_data
return preload_data
def find_name_box(sim_instance: "Simulator" = None):
name_box = sim_instance.load_data.name_box
return name_box
def find_all_name_order_box(sim_instance: "Simulator" = None):
all_name_order_box = sim_instance.load_data.all_name_order_box
return all_name_order_box
================================================
FILE: zsim/sim_progress/Buff/JudgeTools/__init__.py
================================================
from typing import TYPE_CHECKING
from .DetectEdges import detect_edge # noqa: F401
from .FindCharFromCID import find_char_from_CID
from .FindCharFromName import find_char_from_name
from .FindEquipper import find_equipper
from .FindMain import (
find_all_name_order_box, # noqa: F401
find_char_list,
find_dynamic_buff_list,
find_enemy,
find_event_list,
find_exist_buff_dict,
find_init_data, # noqa: F401,
find_preload_data,
find_stack,
find_tick, # noqa: F401
)
if TYPE_CHECKING:
from .. import Buff
def check_preparation(
buff_0: "Buff | None",
buff_instance: "Buff",
equipper: str | None = None,
char_CID: int | None = None,
char_NAME: str | None = None,
**kwargs,
):
"""
这是一个综合函数。根据传入的参数,来执行不同的内容。
"""
# 先决条件检查
assert buff_0 is not None, "buff_0不能为空"
if buff_0.history.record is None:
raise ValueError("buff_0的record模块尚未初始化!!!")
record = buff_0.history.record
# 参数获取
enemy = kwargs.get("enemy")
sub_exist_buff_dict = kwargs.get("sub_exist_buff_dict")
dynamic_buff_list = kwargs.get("dynamic_buff_list")
action_stack = kwargs.get("action_stack")
event_list = kwargs.get("event_list")
trigger_buff_0 = kwargs.get("trigger_buff_0")
preload_data = kwargs.get("preload_data")
char_obj_list = kwargs.get("char_obj_list")
na_skill_level = kwargs.get("na_skill_level")
# 参数正确性检查
if sub_exist_buff_dict and char_NAME is None and char_CID is None and equipper is None:
raise ValueError(
"在查询sub_exist_buff_dict的同时,应保证传入char_CID/char_NAME/equipper中的一个参数"
)
if trigger_buff_0 and trigger_buff_0[0] == "enemy" and not any([char_CID, char_NAME, equipper]):
raise ValueError(
"在查询来自于enemy的trigger_buff_0的同时,应保证传入char_CID/char_NAME/equipper中的一个参数"
)
# 函数主体部分
if equipper:
if record.equipper is None:
record.equipper = find_equipper(equipper, sim_instance=buff_instance.sim_instance)
if record.char is None:
assert record.equipper is not None, "equipper不能为空"
record.char = find_char_from_name(
NAME=record.equipper, sim_instance=buff_instance.sim_instance
)
if char_CID:
if record.char is None:
record.char = find_char_from_CID(char_CID, sim_instance=buff_instance.sim_instance)
if char_NAME:
if record.char is None:
record.char = find_char_from_name(char_NAME, sim_instance=buff_instance.sim_instance)
if sub_exist_buff_dict:
if record.char is None:
raise ValueError("在buff_0.history.record 中并未读取到对应的char")
if record.sub_exist_buff_dict is None:
record.sub_exist_buff_dict = find_exist_buff_dict(
sim_instance=buff_instance.sim_instance
)[record.char.NAME]
if enemy:
if record.enemy is None:
record.enemy = find_enemy(sim_instance=buff_instance.sim_instance)
if dynamic_buff_list:
if record.dynamic_buff_list is None:
record.dynamic_buff_list = find_dynamic_buff_list(
sim_instance=buff_instance.sim_instance
)
if action_stack:
if record.action_stack is None:
record.action_stack = find_stack(sim_instance=buff_instance.sim_instance)
if event_list:
# print('event_list放在record中很有可能不会随动!!注意!')
if record.event_list is None:
record.event_list = find_event_list(sim_instance=buff_instance.sim_instance)
if trigger_buff_0:
trigger_buff_0_handler(record, trigger_buff_0, buff_instance=buff_instance)
if preload_data:
if record.preload_data is None:
record.preload_data = find_preload_data(sim_instance=buff_instance.sim_instance)
if char_obj_list:
if record.char_obj_list is None:
record.char_obj_list = find_char_list(sim_instance=buff_instance.sim_instance)
if na_skill_level:
if record.char is None:
raise ValueError("在buff_0.history.record 中并未读取到对应的char")
record.na_skill_level = record.char.skill_object.skill_level_dict.get("normal")
def trigger_buff_0_handler(record, trigger_buff_0, buff_instance: "Buff"):
"""
该函数用于寻找trigger_buff_0,在搜索不同的触发器Buff‘时,程序所面临的情况往往是复杂的。
1、触发器的操作者(operator)和受益者(beneficiary)都是本人的,那么传入的数据直接可以使用;
2、触发器Buff来自于装备者的,其操作者不是一个固定人选,所以需要先找到equipper,再替换操作者;
3、触发器的操作者和受益者不同的(比如目标Buff是一个debuff,存在于Enemy身上),此时,应该传入Operator
——原因是,Buff只有在自身是主视角的时候,才会执行触发,由于模拟器内没有Enemy的主视角,所以,Enemy所有的buff都是需要别的角色来添加的,
所以,应该直接找到活跃的Buff源——也就是Buff 的Operator的源头。
"""
if not isinstance(trigger_buff_0, tuple):
raise TypeError("输入的参数必须是tuple!")
if record.trigger_buff_0 is None:
operator = trigger_buff_0[0]
buff_index = trigger_buff_0[1]
if operator == "equipper":
if record.equipper is None:
record.equipper = find_equipper(operator, sim_instance=buff_instance.sim_instance)
# FIXME:这里要解决传入的operator 是“equipper”字符串的问题!!!!虽然该分支不会被执行,所以从未出错(obsidian笔记详解一下)
operator = record.equipper
elif operator == "enemy":
operator = record.char.NAME
sub_exist_buff_dict = find_exist_buff_dict(sim_instance=buff_instance.sim_instance)[
operator
]
founded_list = []
for _buff_founded in sub_exist_buff_dict.values():
if buff_index in _buff_founded.ft.index:
founded_list.append(_buff_founded)
if len(founded_list) != 1:
"""说明提供的关键词筛选出了多个Buff,此时需要进一步筛选出正确结果"""
founded_buff_index_list = [founded_buff.ft.index for founded_buff in founded_list]
"""验错环节"""
if len(set(founded_buff_index_list)) != len(founded_list):
raise ValueError(f"在{operator}的sub_exist_buff_dict中找到了2个以上的同名buff!")
trigger_index_length = len(buff_index)
for _buffs in founded_list:
if _buffs.ft.index[-trigger_index_length:] == buff_index:
record.trigger_buff_0 = _buffs
break
else:
raise ValueError(
f"并未找到Buff名后缀为{buff_index}的触发器Buff,说明提供的用于寻找trigger_buff_0的关键词无法有效筛选出触发器,请调整关键词或者数据库Buff Index"
)
else:
record.trigger_buff_0 = founded_list[0]
================================================
FILE: zsim/sim_progress/Buff/ScheduleBuffSettle.py
================================================
from __future__ import annotations
from typing import TYPE_CHECKING
from . import JudgeTools
from .buff_class import Buff
from .BuffAdd import add_debuff_to_enemy
if TYPE_CHECKING:
from zsim.sim_progress.Load import LoadingMission
from zsim.simulator.simulator_class import Simulator
def ScheduleBuffSettle(
time_tick: int,
exist_buff_dict: dict,
enemy,
DYNAMIC_BUFF_DICT: dict,
action_stack,
sim_instance: Simulator,
**kwargs,
):
"""
专门用于处理Schedule阶段才能处理的Buff(buff.ft.schedule_judge = True)
此类Buff往往需要当前Tick的结果出来之后再判定触发与否;
"""
preload_data = JudgeTools.find_preload_data(sim_instance=sim_instance)
action_result = None
if "anomaly_bar" in kwargs:
anomaly_bar = kwargs["anomaly_bar"]
if anomaly_bar.activated_by is not None:
action_result = anomaly_bar.activated_by
elif "skill_node" in kwargs:
from zsim.sim_progress.Load import LoadingMission
from zsim.sim_progress.Preload import SkillNode
skill_node = kwargs["skill_node"]
if isinstance(skill_node, SkillNode):
action_result = skill_node
elif isinstance(skill_node, LoadingMission):
action_result = skill_node.mission_node
else:
print(f"ScheduleBuffSettle函数接收到了无法识别的event类型{type(skill_node).__name__}")
return
if action_result is None:
action_now = preload_data.get_on_field_node(time_tick)
else:
action_now = action_result
if action_now is None:
print("Warnning!!!ScheduleBuffSettle函数没有找到action_now!")
# FIXME: 修复这个问题!!!
return
char_on_field = action_now.char_name
all_name_order_box = JudgeTools.find_all_name_order_box(sim_instance=sim_instance)
name_box_on_field = all_name_order_box[char_on_field]
for char_name in name_box_on_field:
sub_exist_buff_dict = exist_buff_dict[char_name]
if char_name == "enemy":
continue
elif char_name == char_on_field:
process_schedule_on_field_buff(
sub_exist_buff_dict,
name_box_on_field,
time_tick,
DYNAMIC_BUFF_DICT,
enemy,
**kwargs,
)
else:
process_schedule_backend_buff(
sub_exist_buff_dict,
all_name_order_box,
time_tick,
DYNAMIC_BUFF_DICT,
enemy,
**kwargs,
)
def process_schedule_on_field_buff(
sub_exist_buff_dict: dict,
name_box_now: list,
time_tick: int,
DYNAMIC_BUFF_DICT: dict,
enemy,
**kwargs,
):
"""
用来处理schedule阶段的前台buff。这里特殊说明一下name_box_now的情况
这个list取自当前的init_data下的namebox,是经过顺序变化的、只适用于前台角色第一视角的name_box
所以,这个box只能传入本函数, 为前台角色处理前台buff使用,在处理后台buff的函数中,
应该获取的是all_name_order_box,并从中提取出对应的name_box
"""
for buff in sub_exist_buff_dict.values():
ArgumentCheck(buff=buff)
if not buff.ft.schedule_judge:
continue
if buff.ft.passively_updating:
continue
# Buff判定
all_match = buff.logic.xjudge(**kwargs)
if not all_match:
continue
# Buff 激活
adding_buff_code = str(int(buff.ft.add_buff_to)).zfill(4)
selected_characters = [
name_box_now[i] for i in range(len(name_box_now)) if adding_buff_code[i] == "1"
]
# if buff.ft.index == 'Buff-武器-精1啜泣摇篮-全队增伤自增长':
# print(f'onfield函数处理了这个buff!当前的namebox是:{name_box_now}')
add_schedule_buff(
selected_characters,
buff,
time_tick,
sub_exist_buff_dict,
DYNAMIC_BUFF_DICT,
enemy,
**kwargs,
)
def process_schedule_backend_buff(
sub_exist_buff_dict: dict,
all_name_order_box: dict,
time_tick: int,
DYNAMIC_BUFF_DICT: dict,
enemy,
**kwargs,
):
"""
用来处理schedule阶段的后台buff。
"""
for buff in sub_exist_buff_dict.values():
ArgumentCheck(buff=buff)
if not buff.ft.schedule_judge:
continue
if not buff.ft.backend_acitve:
continue
if buff.ft.passively_updating:
continue
all_match = buff.logic.xjudge(**kwargs)
if not all_match:
continue
main_char = buff.ft.operator
name_box_now = all_name_order_box[main_char]
adding_buff_code = str(int(buff.ft.add_buff_to)).zfill(4)
selected_characters = [
name_box_now[i] for i in range(len(name_box_now)) if adding_buff_code[i] == "1"
]
# if buff.ft.index == 'Buff-武器-精1啜泣摇篮-全队增伤自增长':
# print(f'backend函数处理了这个buff!当前的namebox是:{name_box_now}')
add_schedule_buff(
selected_characters,
buff,
time_tick,
sub_exist_buff_dict,
DYNAMIC_BUFF_DICT,
enemy,
**kwargs,
)
def add_schedule_buff(
selected_characters: list,
buff: Buff,
time_tick: int,
sub_exist_buff_dict: dict,
DYNAMIC_BUFF_DICT: dict,
enemy,
**kwargs,
):
"""
Schedule阶段用来直接添加buff的函数
"""
if not buff.ft.schedule_judge:
raise ValueError(f"{buff.ft.index}不是schedule阶段buff!")
for characters in selected_characters:
buff_new = Buff.create_new_from_existing(buff)
buff_new.ft.operator = buff.ft.operator
buff_new.ft.passively_updating = buff.ft.passively_updating
# if buff.ft.index == 'Buff-武器-精1啜泣摇篮-全队增伤自增长':
# print(f'buff_0情况:{buff.dy.startticks, buff.dy.endticks}')
# print(f'新buff情况:{buff_new.dy.startticks, buff_new.dy.endticks}')
# if not buff.ft.operator == characters:
# continue
if buff.ft.simple_effect_logic:
buff_new.simple_start(time_tick, sub_exist_buff_dict)
else:
buff_new.logic.xeffect(**kwargs)
# Buff加载
buff_existing_check = next(
(
existing_buff
for existing_buff in DYNAMIC_BUFF_DICT[characters]
if existing_buff.ft.index == buff.ft.index
),
None,
)
if buff_existing_check:
DYNAMIC_BUFF_DICT[characters].remove(buff_existing_check)
DYNAMIC_BUFF_DICT[characters].append(buff_new)
if characters == "enemy":
buff_existing_check = next(
(
existing_buff
for existing_buff in enemy.dynamic.dynamic_debuff_list
if existing_buff.ft.index == buff.ft.index
),
None,
)
if buff_existing_check:
enemy.dynamic.dynamic_debuff_list.remove(buff_existing_check)
add_debuff_to_enemy(buff_new, characters, enemy)
def ArgumentCheck(**kwargs):
action_now = kwargs.get("action_now", None)
buff = kwargs.get("buff", None)
if action_now:
if not isinstance(action_now, LoadingMission):
raise TypeError(f"{action_now}不是LoadingMission类!")
if buff:
if not isinstance(buff, Buff):
raise TypeError(f"{buff}不是Buff类!")
if __name__ == "__main__":
pass
================================================
FILE: zsim/sim_progress/Buff/__init__.py
================================================
from .Buff0Manager import Buff0Manager # noqa: F401
from .buff_class import Buff, spawn_buff_from_index # noqa: F401
from .BuffAdd import buff_add # noqa: F401
from .BuffLoad import BuffInitialize, BuffLoadLoop # noqa: F401
from .JudgeTools import * # noqa: F403
from .ScheduleBuffSettle import ScheduleBuffSettle # noqa: F401
# TODO:
# buff.ft.label = {"only_CoAttack": 1, "only_技能skill_tag": 1}
# skill.label = {"CoAttack": 1, "accept_buff_Buff名字": 1}
# 按照如上格式,进行Buff的数据库拓展,并且写好构造函数的对应接口。
================================================
FILE: zsim/sim_progress/Buff/buff_class.py
================================================
import ast
import importlib
import json
from functools import lru_cache
from typing import TYPE_CHECKING
import numpy as np
import pandas as pd
from zsim.define import EFFECT_FILE_PATH, EXIST_FILE_PATH, JUDGE_FILE_PATH, config_path
from zsim.sim_progress.Report import report_to_log
from .BuffXLogic._buff_record_base_class import BuffRecordBaseClass as BRBC
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
with open(config_path, "r", encoding="utf-8") as file:
config = json.load(file)
debug = config.get("debug")
with open("./zsim/sim_progress/Buff/buff_config.json", "r", encoding="utf-8") as f:
_buff_load_config = json.load(f)
# 如果禁用缓存,每次都创建新的实例
# 这个index列表里面装的是乘区类型中所有的项目,也是buff效果作用的范围.
# 这个列表中的内容:在Buff效果.csv 中作为索引存在;而在 Event父类中,它们又包含了 info子类的部分内容 和 multiplication子类的全部内容,
# 在文件中,这个list被用在最后的buffchagne()函数中,作为中转字典的keylist存在
# 在重构本程序的过程中,我思考过是否要把这个巨大的indexlist按照乘区划分拆成若干,这意味着Event中的Multi子类或许可以独立出来成为一个单独的父类存在.
# 这样做的好处是:庞大复杂的Multi可以不用蜗居在Event下,结构更加清晰.但是坏处就是,Event和Multi必须1对1实例化,否则容易出现多个动作共用同一份乘区实例的情况,就很容易发生错误NTR(bushi
class Buff:
"""
config字典的键值来自:触发判断.csv
judge_config字典的键值来自:激活判断.csv
"""
@staticmethod
def create_new_from_existing(existing_instance):
"""
已经弃用。
通过复制已有实例的状态来创建新实例
该方法主要用于BuffAddStrategy函数。
"""
new_instance = Buff.__new__(Buff) # 不调用构造函数
new_instance.__dict__ = existing_instance.__dict__.copy() # 复制原实例的属性
return new_instance
def __init__(self, config: pd.Series, judge_config: pd.Series, sim_instance: "Simulator"):
if not hasattr(self, "ft"):
self.ft = self.BuffFeature(config)
self.dy = self.BuffDynamic()
self.sjc = self.BuffSimpleJudgeCondition(judge_config)
self.logic = self.BuffLogic(self)
self.history = self.BuffHistory()
self.effect_dct = self.__lookup_buff_effect(self.ft.index)
self.feature_config = config
self.judge_config = judge_config
else:
self.history.active_times += 1
# 调用特殊的逻辑加载函数
self.buff_config = self.load_config()
self.load_special_judge_config()
self.sim_instance = sim_instance
@staticmethod
def load_config():
"""
加载 Buff 配置文件
"""
return _buff_load_config
def load_special_judge_config(self):
"""
根据Buff的特性选择是否加载特殊的逻辑模块。
动态加载适应于当前Buff实例的复杂逻辑模块。
"""
try:
index = self.ft.index
config = self.buff_config.get(index)
if config:
module_name = config["module"]
class_name = config["class"]
# 动态加载模块
module = importlib.import_module(module_name, package="zsim.sim_progress.Buff")
logic_class = getattr(module, class_name)
self.logic = logic_class(self)
else:
# 默认逻辑
pass
except ModuleNotFoundError:
# 处理模块找不到的情况
print(f"Module for {self.ft.index} not found. Falling back to default logic.")
pass
class BuffFeature:
bf_instance_cache: dict[int, "Buff.BuffFeature"] = {}
max_cache_size = 256
def __new__(cls, config):
cache_key = hash(tuple(sorted(config.items())))
if cache_key in cls.bf_instance_cache:
return cls.bf_instance_cache[cache_key]
instance = super(Buff.BuffFeature, cls).__new__(cls)
if len(cls.bf_instance_cache) >= cls.max_cache_size:
cls.bf_instance_cache.popitem()
cls.bf_instance_cache[cache_key] = instance
return instance
def __init__(self, meta_config: pd.Series):
if not hasattr(self, "name"):
try:
config_dict: dict = dict(meta_config)
except TypeError:
raise TypeError(f"{meta_config} is not a mapping")
self.buff = None
self.simple_judge_logic = config_dict["simple_judge_logic"] # 复杂判断逻辑,
self.simple_start_logic = config_dict[
"simple_start_logic"
] # 复杂开始逻辑,指的是buff的start方式比较特殊,需要代码控制
self.simple_end_logic = config_dict[
"simple_end_logic"
] # 复杂结束逻辑,指的是buff的结束不以常规buff的结束条件为约束的,比如消耗完层数才消失的,比如受击导致持续时间发生跳变的,
self.simple_hit_logic = config_dict["simple_hit_logic"] # 复杂的命中判定逻辑
self.simple_effect_logic = config_dict[
"simple_effect_logic"
] # 复杂的生效逻辑,和simple_start对应
"""
注意,此处的xeffect往往与xjudge进行配合。因为xjudge会导致buff在BuffLoad函数中进入else分支,
如果某buff既有复杂的judge_logic,又有复杂的start/hit/end_logic,
那么后者就应该使用xeffect来写,要不然就会进入simple_start分支而导致代码块无法执行。
"""
self.simple_exit_logic = config_dict["simple_exit_logic"] # 复杂退出逻辑
self.index = config_dict["BuffName"] # buff的英文名,也是buff的索引
self.is_weapon = config_dict["is_weapon"] # buff是否是武器特效
self.is_additional_ability = config_dict[
"is_additional_ability"
] # Buff是否是组队被动Buff
self.refinement = config_dict["refinement"] # 武器特效的精炼等级
self.bufffrom = config_dict[
"from"
] # buff的来源,可以是角色名,也可以是其他,这个字段用来和配置文件比对,比对成功则证明这个buff是可以触发的;
self.description = config_dict[
"description"
] # buff的中文名字,包括一些buff效果的拆分,这里的中文名写的会比较细
self.exist = config_dict["exist"] # buff是否参与了计算,即是否允许被激活
self.durationtype = config_dict[
"durationtype"
] # buff的持续时间类型,如果是True,就是有持续时间的,如果是False,就没有持续时间类型,属于瞬时buff.
self.maxduration = int(config_dict["maxduration"]) # buff最大持续时间
self.maxcount = int(config_dict["maxcount"]) # buff允许被叠加的最大层数
self.step = int(
config_dict["incrementalstep"]
) # buff的自增步长,也可以理解为叠层事件发生时的叠层效率.
self.prejudge = config_dict[
"prejudge"
] # buff的抬手判定类型,True是攻击抬手时会产生判定;False则是不会产生判定
self.endjudge = config_dict[
"endjudge"
] # buff的结束判定类型,True是攻击或动作结束时会产生判定,False则不会产生判定。
self.fresh = config_dict[
"freshtype"
] # buff的刷新类型,True是刷新层数时,刷新时间,False是刷新层数是,不影响时间.
self.alltime = config_dict[
"alltime"
] # buff的永远生效类型,True是无条件永远生效,False是有条件
self.hitincrease = config_dict[
"hitincrease"
] # buff的层数增长类型,True就增长层数 = 命中次数,而False是增长层数为固定值,取决于step数据;
self.cd = int(config_dict["increaseCD"]) # buff的叠层内置CD
self.add_buff_to = config_dict["add_buff_to"] # 记录了buff会被添加给谁?
self.is_debuff = config_dict["is_debuff"] # 记录了这个buff是否是个debuff
self.schedule_judge = config_dict[
"schedule_judge"
] # 记录了这个buff是否需要在schedule阶段处理。
self.backend_acitve = config_dict[
"backend_acitve"
] # 记录了这个buff是否需要在后台才能触发
self.individual_settled = config_dict[
"individual_settled"
] # 记录了这个buff的叠层是否是独立结算
"""
在20241116的更新中,更新了新的buff结算逻辑,针对“层数独立结算”的buff,
在BuffFeature下新增了一个参数:individual_settled
buff在更新或者新建实例时,应该检测该参数是否为True,
如果是True,则应该检索当前DYNAMIC_BUFF_DICT中的buff是否存在,
如果存在,则应该直接更新self.dy.built_in_buff_box。
"""
self.operator = config_dict.get("operator", None)
self.passively_updating = config_dict.get("passively_updating", None)
"""
在20241130的更新中,新增了passively_updating这一参数。
在初始化阶段、生成exist_buff_dict以及一众buff_0时,
会根据对应的添加逻辑,修改这一参数。这一参数可以标志出该buff是否应该由当前角色的行为触发。
这样就可以避免“艾莲的强化E会意外触发苍角核心被动的攻击力buff”
"""
self._beneficiary = None
"""
在20250102的更新中,我们为buff.feature新增了beneficiary这个属性。并且在buff_exist_judge函数中做了对应的初始化逻辑。
该属性是为了标注buff的受益者,至此,operator、beneficiary 与passively_updating三个参数构成了一套相对完整的逻辑。
当operator与beneficiary不同时,passively_updating为True,反之则为False;
但是,以上逻辑暂时还未实装。
编写beneficiary属性的原因:
目前,在Buff循环逻辑中,find_buff_0函数返回的结果只能是equipper的buff_0,这对于大部分buff来说是没有影响的,
但是如果一个Buff加给自己的和加给他人的buff情况不同、而我们有需要去找受益者获取buff_0时,beneficiary属性就派上用场了。
——启发自 Buff 静听嘉音
"""
"""Buff标签"""
self.label: dict[str, list[str] | str] | None = self.__process_label_str(
config_dict
)
"""
标签生效规则:
None: 无标签时,该参数为None
0:所有标签都通过时,才生效,
n(0以外的任意int):通过n个标签时,就生效。
"""
self.label_effect_rule: int | None = self.__process_label_rule(config_dict)
self.buff0_id = None
__listener_id_str = config_dict.get("listener_id") # 与Buff的伴生的监听器的ID
if __listener_id_str is None or __listener_id_str is np.nan:
self.listener_id = None
else:
self.listener_id = str(__listener_id_str).strip()
@property
def beneficiary(self):
return self._beneficiary
@beneficiary.setter
def beneficiary(self, value):
# if self.index == "Buff-角色-席德-明攻":
# print(11111111, f"席德明攻的受益人变更为{value},当前buff的操作者是:{self.operator}")
self._beneficiary = value
def __process_label_rule(self, config_dict: dict) -> int | None:
label_rule = config_dict.get("label_effect_rule", 0)
if pd.isna(label_rule) or label_rule is None:
if self.label:
return 0
else:
return None
else:
label_rule = int(label_rule)
assert self.label is not None, (
f"在初始化{self.index}时,label_rule为{label_rule},但label为None"
)
if label_rule != 0 and label_rule > len(self.label.keys()):
raise ValueError(
f"{self.index}在初始化时填入的label_rule为{label_rule},大于其label中填入的参数数量!self.ft.label = {self.label}"
)
return label_rule
def __process_label_str(self, config_dict: dict):
"""处理label的初始化!"""
label_str = config_dict.get("label", None)
if label_str is None:
return None
elif isinstance(label_str, str):
if label_str.strip() is None or pd.isna(label_str):
return None
else:
_dict = ast.literal_eval(str(label_str).strip())
return _dict
class BuffDynamic:
def __init__(self):
self.exist = False # buff是否参与了计算,即是否允许被激活
self.active = False # buff当前的激活状态
self.count: int | float = 0 # buff当前层数
self.ready = True # buff的可叠层状态,如果是True,就意味着是内置CD结束了,可以叠层,如果不是True,就不能叠层.
self.startticks = 0 # buff上一次触发的时间(tick)
self.endticks = 0 # buff计划课中,buff的结束时间
self.settle_times = 0 # buff目前已经被结算过的次数
self.buff_from = None # debuff的专用属性,用于记录debuff的来源。
self.built_in_buff_box = [] # 如果self.ft.single_deal是True,则需要创建这个list。
"""
在20241117的更新中,我们新增了built_in_buff_box属性,该属性需要和self.ft.individual_settled进行配合。
如果该参数为True,则在单独结算Buff时,将buff的startticks和endticks以[start,end]的格式存入列表,然后写入这个box。
"""
self.is_changed = False
"""
在20241115的更新中,新增了buff.dy.is_change属性。
该字段记录了当前buff是否已经成功被变更属性。通过该字段就可以区分进入update函数的buff是否真实地改变了数据,
而update_to_buff_0的函数也需要严格根据这个参数的情况来执行。
这能够避免某些本应在hit处更新的buff,于start处执行了update函数,被分入start分支后,
该buff虽然没有改变属性,但是无条件执行了update_to_buff_0,
从而污染了源头数据。导致部分叠层、以及其他属性变更出现问题。
"""
self.effect_available_times = 0 # 剩余的生效次数
def reset_myself(self):
"""更新Buff.dynamic"""
self.active = False
self.count = 0
self.ready = True
self.startticks = 0
self.endticks = 0
self.settle_times = 0
self.built_in_buff_box = []
self.is_changed = False
class BuffLogic:
"""
这是记录所有的复杂逻辑的子类。由于所有的复杂逻辑的调用,都必须从BuffLoadLoop开始。
所以,再复杂的Buff也需要在CSV数据库中输入对应的index以及其他基本属性,使其能够顺利进入BuffLoadLoop。
而真正调用Xlogic模块的地方实际上有两个。Xjudge会在BuffLoadLoop中调用的BuffJudge函数的一个分支中被执行,最终抛出的是布尔值。
第二处则是在Buff.update()函数。为了保证所有的Xlogic都有方式被正确调用,所以,我将复杂的触发逻辑分为了Start、Hit和End三类。
它们会在各自的分支中被调用。
"""
def __init__(self, buff_instance: "Buff"):
self.buff: "Buff" = buff_instance
self.xjudge = None # 判断逻辑
self.xstart = None # 复杂的开始逻辑
self.xhit = None # 复杂的命中更新逻辑
self.xend = None # 结束逻辑
self.xeffect = None # 生效逻辑
self.xexit = None # 退出逻辑
def special_judge_logic(self, **kwargs) -> bool | None:
pass
def special_start_logic(self, **kwargs):
# 这里可以实现特定的开始逻辑
pass
def special_hit_logic(self, **kwargs):
# 这里可以实现特定的命中逻辑
pass
def special_end_logic(self, **kwargs):
# 这里可以实现特定的结束逻辑
pass
def special_effect_logic(self, **kwargs):
pass
def special_exit_logic(self, **kwargs):
pass
class BuffSimpleJudgeCondition:
sjc_instance_cache: dict[int, "Buff.BuffSimpleJudgeCondition"] = {}
max_size = 128
def __new__(cls, judgeconfig):
cache_key = hash(tuple(sorted(judgeconfig.items())))
if cache_key in cls.sjc_instance_cache:
return cls.sjc_instance_cache[cache_key]
instance = object.__new__(cls)
if len(cls.sjc_instance_cache) >= cls.max_size:
cls.sjc_instance_cache.popitem()
cls.sjc_instance_cache[cache_key] = instance
def __init__(self, judgeconfig):
self.buff = None
self.id = judgeconfig["id"]
self.oname = judgeconfig["OfficialName"]
self.sp = judgeconfig["SpConsumption"]
self.spr = judgeconfig["SpRecovery_hit"]
self.fev = judgeconfig["FeverRecovery"]
self.eaa = judgeconfig["ElementAbnormalAccumulation"]
self.st = judgeconfig["SkillType"]
self.tbl = judgeconfig["TriggerBuffLevel"]
self.et = judgeconfig["ElementType"]
self.tc = judgeconfig["TimeCost"]
self.hn = judgeconfig["HitNumber"]
self.da = judgeconfig["DmgRelated_Attributes"]
self.sa = judgeconfig["StunRelated_Attributes"]
class BuffHistory:
def __init__(self):
"""
History是Buff的一个子类,主要记录了buff的触发历史,\n
包括buff的上次结束时间,上次持续时间,激活次数以及结束次数.\n
"""
self.last_end = 0 # buff上一次结束的时间
self.active_times = 0 # buff迄今为止激活过的次数
self.last_duration = 0 # buff上一次的持续时间
self.end_times = 0 # buff结束过的次数
self.real_count = 0 # 莱特组队被动专用的字段,用于记录实层。
self.last_update_tick = 0 # 部分复杂buff需要的上一次更新时间
self.last_update_resource = 0 # 部分复杂buff需要的上一次更新时的资源数量
self.record: "BRBC | None" = None
def reset_myself(self):
"""重置Buff.history"""
self.last_end = 0
self.active_times = 0
self.last_duration = 0
self.end_times = 0
self.real_count = 0
self.last_update_tick = 0
self.last_update_resource = 0
self.record = None
@property
def durtation(self):
if not self.dy.active:
return 0
return self.dy.endticks - self.dy.startticks
def __lookup_buff_effect(self, index: str) -> dict:
"""
根据索引获取buff效果字典。
该方法从json文件中尝试读取所有buff效果数据
找到后,将数据转换为字典并返回。
参数:
- index: buff索引。
返回:
- buff_effect: 包含buff效果的字典。
"""
# 初始化一个空的字典来存储buff效果
# 读取包含所有buff效果的CSV文件
all_buff_js = self.__convert_buff_js(EFFECT_FILE_PATH)
try:
buff = all_buff_js[index]
except KeyError as e:
buff = {}
report_to_log(f"[WARNING] {e}: 索引{index}没有找到,或buff效果json结构错误", level=4)
return buff
@staticmethod
@lru_cache(maxsize=64)
def __convert_buff_js(csv_file):
df = pd.read_csv(csv_file)
width = int(np.ceil(df.shape[1] / 2))
# 初始化结果字典
result = {}
# 遍历 DataFrame 的每一行
for index, row in df.iterrows():
name = row["名称"]
value = {}
# 处理 key-value 对
for i in range(1, width):
try:
key = row[f"key{i}"]
val = row[f"value{i}"]
if pd.notna(key) and pd.notna(val):
value[key] = float(val)
except KeyError:
continue
result[name] = value
return result
def reset_myself(self):
"""Buff的重置函数"""
self.dy.reset_myself()
self.history.reset_myself()
def ready_judge(self, timenow):
"""
用来判断内置CD是否就绪
"""
if not self.dy.ready:
if timenow - self.dy.startticks >= self.ft.cd:
self.dy.ready = True
def is_ready(self, tick: int) -> bool:
"""
用来判断buff是否可以被触发
"""
if self.ft.cd == 0:
return True
else:
if self.dy.startticks == 0:
return True
if tick - self.dy.startticks >= self.ft.cd:
return True
else:
return False
def end(self, timenow, exist_buff_dict: dict):
"""
用来执行buff的结束
"""
buff_0 = exist_buff_dict[self.ft.index]
# buff_0就是buff的源头。位于exsist_buff_dict中。
if not isinstance(buff_0, Buff):
raise TypeError(f"{buff_0}不是Buff类!")
# 在修改buff的状态时,对buff_0进行相同的修改。以保证状态同步。
self.dy.active = False
self.dy.count = 0
self.dy.built_in_buff_box = []
buff_0.dy.active = False
buff_0.dy.count = 0
# 同时,更新buff_0的触发历史记录。
buff_0.history.last_end = timenow
buff_0.history.end_times += 1
buff_0.history.last_duration = max(timenow - self.dy.startticks, 0)
buff_0.dy.built_in_buff_box = []
# 再把当前buff的实例化的history和buff源对齐
self.history.last_end = buff_0.history.last_end
self.history.end_times = buff_0.history.end_times
self.history.last_duration = buff_0.history.last_duration
# report_to_log(
# f'[Buff INFO]:{timenow}:{self.ft.name}第{buff_0.history.end_times}次结束;duration:{buff_0.history.last_duration}', level=3)
def simple_start(self, timenow: int, sub_exist_buff_dict: dict[str, "Buff"], **kwargs):
"""
sub_exist_buff_dict = exist_buff_dict[角色名]
角色名指的是当前的前台角色。
该方法是buff的默认激活方法。它会以最常见的策略激活buff。
让buff具有最基本的start、end两个时间点,以及最基本的层数,
并且更新好Buff的内置CD,最后将所有的信息改动回传给buff0
"""
no_start = kwargs.get("no_start", False)
no_end = kwargs.get("no_end", False)
no_count = kwargs.get("no_count", False)
specified_count = kwargs.get("specified_count", None) # 外部定制层数——层数不独立结算的Buff
_simple_start_buff_0: "Buff" = sub_exist_buff_dict[self.ft.index]
individule_settled_count = kwargs.get("individule_settled_count", 0)
if no_count and any([individule_settled_count, specified_count]):
raise ValueError("在传入no_count参数时,同时传入了其他控制层数的参数。")
if specified_count and self.ft.individual_settled:
raise ValueError("企图使用specified_count参数来控制层数,但该buff不是层数独立结算的。")
if individule_settled_count != 0 and not self.ft.individual_settled:
raise ValueError(
f"对于层数不独立结算的{self.ft.index},在调用simple_start函数时,不应传入individule_settled_count参数。"
)
if individule_settled_count and specified_count:
raise ValueError("同时传入了individule_settled_count和specified_count参数。")
if individule_settled_count == 0:
individule_settled_count = 1
self.dy.active = True
if not no_start:
self.dy.startticks = timenow
if not no_end:
self.dy.endticks = timenow + self.ft.maxduration
if not no_count:
if self.ft.individual_settled:
for i in range(0, individule_settled_count):
self.dy.built_in_buff_box.append((self.dy.startticks, self.dy.endticks))
while len(self.dy.built_in_buff_box) > self.ft.maxcount:
self.dy.built_in_buff_box.pop(0)
self.dy.count = len(self.dy.built_in_buff_box)
else:
if specified_count:
self.dy.count = specified_count
# print(f"{self.ft.index}的层数被设定为{specified_count}")
else:
self.dy.count = min(
_simple_start_buff_0.dy.count + self.ft.step, self.ft.maxcount
)
self.dy.is_changed = True
self.dy.ready = False
self.update_to_buff_0(_simple_start_buff_0)
# if (
# self.ft.index == "Buff-角色-雨果-1画-决算招式双暴增幅"
# or self.ft.index == "Buff-角色-雨果-2画-决算招式无视防御力"
# ):
# print(f"{self.ft.index}触发了,层数为:{self.dy.count}")
def individual_setteled_update(self, duration, timenow):
"""
各层数单独结算类的buff的更新函数
会首先检查内置的box的容量情况,如果box满了会先进行pop(0),然后append
最后,更新自身的count
"""
start = timenow
end = timenow + duration
if len(self.dy.built_in_buff_box) == self.ft.maxcount:
if len(self.dy.built_in_buff_box) > self.ft.maxcount:
# 溢出报错
raise ValueError(
f"box的当前大小是{len(self.dy.built_in_buff_box)},超过了最大容量{self.ft.index}"
)
# 说明此时box的含量已经满了,所以把最老的pop出来。
self.dy.built_in_buff_box.pop(0)
# 不管是否pop,都需要append
self.dy.built_in_buff_box.append((start, end))
self.dy.count = len(self.dy.built_in_buff_box)
self.dy.startticks = start
self.dy.endticks = end
self.dy.active = True
self.dy.ready = False
self.dy.is_changed = True
def update(
self,
char_name: str,
timenow,
timecost,
sub_exist_buff_dict: dict,
sub_mission: str,
):
"""
该函数只负责buff的时间更新。buff该不该进行更新,并不是该函数的负责范围。
往往是在外部函数判断出某个buff需要触发后(通常是新建一个Buff实例)
根据Buff的更新特性(比如fresh、Prejudge等参数)以及对应正在发生的子任务节点,对应的处理buff的dynamic属性。
"""
if self.ft.index not in sub_exist_buff_dict:
raise TypeError(f"{self.ft.index}并不存在于{char_name}的exist_buff_dict中!")
buff_0 = sub_exist_buff_dict[self.ft.index]
if self.ft.alltime:
self.dy.active = True
self.dy.count = 1
self.dy.is_changed = True
self.dy.startticks = timenow
self.update_to_buff_0(buff_0)
return
if buff_0.dy.active:
"""
如果update函数运行时,检测到Buff0已经active,则意味着我们需要更新一个已经被激活的buff。
首先,应该将自身的主要数据与Buff0对齐,这是前提条件。这所有的东西主要是为了叠层服务的。
当然那,不用担心这一步会污染startticks和endticks,因为后面该对这两个东西做出改动的函数,都会改动它们。
不该做出改动的,那自然不需要改,也是符合需求的。
"""
self.download_from_buff_0(buff_0)
if not isinstance(buff_0, Buff):
raise TypeError(f"{buff_0}不是Buff类!")
if buff_0.ft.operator != char_name and char_name != "enemy":
"""
由于传入update函数的sub_exist_buff_dict永远只会来自于buff的实际更新来源(operator),
所以,对于select_character是多个角色的buff,必须在这里进行筛选。
如果查询到buff源的operator 不等于 buff的受益者(也就是这里传入的char_name)
则意味着buff只执行添加,而不执行更新。
只有操作者才有资格更新buff。而在外部,更新、轮询char的顺序,来自于select_character,
该列表是按照操作者作为第一视角的,所以,我们总能保证操作者的更新buff在前,而受益者的被动添加在后。
但是,如果char_name是enemy,则意味着这是要给敌人添加buff。这是单向行为,所以不在这一层屏蔽的范围内。
"""
self.dy.is_changed = True
return
buff_0.ready_judge(timenow)
if not buff_0.dy.ready:
report_to_log(
f"[Buff INFO]:{timenow}:{buff_0.ft.description}内置CD没就绪,并未成功触发",
level=3,
)
return
"""
在执行所有分支之前,自然要判断buff的就绪情况。如果Buff没有就绪,那么一切都是白扯。
因为self自身是刚刚实例化的Buff,肯定是新鲜的,所以,ready的判断要去exist buff dict中的buff0执行,
那里记录着buff的最新情况。
ready检测不通过直接return——cd没转好,所以就算能够触发,也是不会触发的。
"""
if sub_mission == "start":
self.update_cause_start(timenow, timecost, sub_exist_buff_dict, beneficiary=char_name)
elif sub_mission == "end":
if self.ft.endjudge:
self.update_cause_end(timenow, sub_exist_buff_dict, beneficiary=char_name)
elif sub_mission == "hit":
self.update_cause_hit(timenow, sub_exist_buff_dict, timecost, beneficiary=char_name)
def update_to_buff_0(self, buff_0):
"""
该方法往往衔接在buff更新后使用。
由于在buff判定逻辑中,buff的层数、时间的刷新被视为重新激活了一个新的buff,
所以,这个方法需要向exist_buff_dict中的buff源头,也就是buff_0传递一些当前buff的参数
"""
if not isinstance(buff_0, Buff):
raise TypeError(f"{buff_0}不是Buff类!")
buff_0.dy.active = self.dy.active
buff_0.dy.ready = self.dy.ready
buff_0.dy.startticks = self.dy.startticks
buff_0.dy.endticks = self.dy.endticks
buff_0.dy.built_in_buff_box = self.dy.built_in_buff_box
buff_0.history.active_times += 1
if buff_0.ft.individual_settled:
buff_0.dy.count = min(len(self.dy.built_in_buff_box), self.ft.maxcount)
else:
# if buff_0.ft.index == 'Buff-武器-精1啜泣摇篮-全队增伤自增长':
# print(f'buff_0更新前层数:{buff_0.dy.count}, buff自身更新前层数:{self.dy.count}')
buff_0.dy.count = self.dy.count
# if buff_0.ft.index == 'Buff-武器-精1啜泣摇篮-全队增伤自增长':
# print(f'buff_0更新后层数:{buff_0.dy.count}, buff自身更新后层数:{self.dy.count}')
# report_to_log(f'[Buff INFO]:{timenow}:{buff_0.ft.index}第{buff_0.history.active_times}次触发')
def download_from_buff_0(self, buff_0):
if not isinstance(buff_0, Buff):
raise TypeError(f"{buff_0}不是Buff类!")
self.dy.active = buff_0.dy.active
self.dy.ready = buff_0.dy.ready
self.dy.is_changed = buff_0.dy.is_changed
self.dy.startticks = buff_0.dy.startticks
self.dy.endticks = buff_0.dy.endticks
self.dy.built_in_buff_box = buff_0.dy.built_in_buff_box
self.dy.count = buff_0.dy.count
def update_cause_start(self, timenow, timecost, exist_buff_dict: dict, beneficiary: str):
buff_0 = exist_buff_dict[self.ft.index]
if not isinstance(buff_0, Buff):
raise TypeError(f"{buff_0}不是Buff类!")
if not self.ft.simple_start_logic:
# EXAMPLE:Buff触发时,随机获得层数。
assert self.logic.xstart is not None, (
f"{self.ft.index} 的simple_start_logic参数不为True时,其logic.xstart不能为空"
)
self.logic.xstart(beneficiary=beneficiary)
self.update_to_buff_0(buff_0)
return
if self.ft.maxduration == 0: # 瞬时buff
if not self.ft.hitincrease: # 命中不叠层
"""
所有瞬时buff(maxduration=0)中,非命中触发的那部分,
本质上是把瞬时buff看做是一个持续时间为招式持续时间的buff。
所有的“普攻伤害增加”类型的Buff,都是这个逻辑处理的。在出招时候(start)就已经加上了,而在End之后自动结束。
确保该招式内的所有Hit都能享受到加成。
但是,这个函数处理不了在招式内叠层的Buff,在逻辑上绕开了“强化E伤害提高5%,且每命中一次再提高5%”这类buff
就以这个Buff例子为例,这个Buff是Hit事件才会触发的,在Start函数中应该毫无作为,所以必须绕开。
"""
self.dy.active = True
if self.ft.individual_settled:
"""
单独结算的Buff处理逻辑。
"""
# EXAMPLE:发动普攻时,使当前招式伤害增加X%,每层效果独立结算。
self.individual_setteled_update(timecost, timenow)
else:
# EXAMPLE:发动普攻时,使当前招式伤害增加X%,
self.dy.startticks = timenow
self.dy.endticks = timenow + timecost
self.dy.count = min(buff_0.dy.count + self.ft.step, self.ft.maxcount)
self.dy.ready = False
self.dy.is_changed = True
else:
if self.ft.prejudge:
"""
所有具有持续时间的buff中,只有抬手就触发的这一类,会在start标签处更新。
再次强调:但凡是Hit触发的buff,在start标签处都不会做任何改变。
并且,本质上prejudge和hit_increase两个参数在数据库中就是互斥的,
除非,某个Buff在技能抬手就触发,同时还会因为命中而叠层(那也太傻逼了,虽然我觉得这不远了。到时候出问题了记得踢我。)
"""
# FIXME:某个Buff在技能抬手就触发,同时还会因为命中而叠层 的逻辑等待拓展
if self.ft.individual_settled:
# EXAMPLE:发动普攻时,使攻击力提升,每次触发独立结算持续时间。
self.individual_setteled_update(self.ft.maxduration, timenow)
else:
# EXAMPLE:发动普攻时,使攻击力提升,重复触发刷新持续时间
self.dy.active = True
self.dy.startticks = timenow
self.dy.endticks = timenow + self.ft.maxduration
if not self.ft.hitincrease:
self.dy.count = min(buff_0.dy.count + self.ft.step, self.ft.maxcount)
self.dy.ready = False
self.dy.is_changed = True
"""
所有因start标签而更新的buff,它们的底层逻辑往往和Hit更新互斥,
它们的层数计算往往非常直接,就是当前层数 + 步长;
而想要做到所谓的“层数叠加了”,那么当前层数应该从buff_0处获取(这是通用步骤,其他类型的层数更新也是这个流程)
总体层数又被min函数掐死,不用担心移除。
"""
if self.dy.is_changed:
self.update_to_buff_0(buff_0)
# report_to_log(f"[Buff INFO]:{timenow}:{buff_0.ft.index}第{buff_0.history.active_times}次触发", level=3)
def update_cause_end(self, timenow, exist_buff_dict, beneficiary: str):
"""
这个函数一般不会用到,因为不会有动作结束才触发的傻逼buff逻辑。
“艾莲踩了你一脚,你当场不敢发作但是等艾莲转身离开,你触发硬度+3,持续30秒?
还是那句话,因动作结束而触发的Buff就是傻逼,好在这里不需要判断是不是Prejudge了。
"""
buff_0 = exist_buff_dict[self.ft.index]
if self.ft.simple_end_logic:
if not isinstance(buff_0, Buff):
raise TypeError(f"{buff_0}不是Buff类!")
# 至于buff.end()并非在这个环节做出修改,而是应该在主循环开头,遍历DynamicBuffList的时候进行修改。
if self.ft.endjudge:
if self.ft.individual_settled:
self.individual_setteled_update(self.ft.maxduration, timenow)
else:
# EXAMPLE:普攻结束后,伤害增加X%,重复触发刷新持续时间。
self.dy.active = True
self.dy.startticks = timenow
self.dy.endticks = timenow + self.ft.maxduration
self.dy.count = min(buff_0.dy.count + self.ft.step, self.ft.maxcount)
self.dy.ready = False
self.dy.is_changed = True
# report_to_log(f"[Buff INFO]:{timenow}:{buff_0.ft.index}第{buff_0.history.active_times}次触发", level=3)
else:
# EXAMPLE:普攻结束后,随机获得1~10层的攻击力Buff。
assert self.logic.xend is not None, f"{self.ft.index}的buff没有初始化xend方法"
self.logic.xend(beneficiary=beneficiary)
self.dy.is_changed = True
if self.dy.is_changed:
self.update_to_buff_0(buff_0)
def update_cause_hit(self, timenow, exist_buff_dict: dict, timecost, beneficiary: str):
"""
这里是最常用的代码,大部分的buff都是hit标签更新。
当然,第一层就要过hitincrease筛选,但凡不满足的,我hit一万次你也触发不了。
然后就是那些沟槽的 重复触发但不刷新时间的buff(fresh == False)
在处理这些Buff的时候必须忍住不要更新startticks,要不然就全丸辣!
"""
buff_0 = exist_buff_dict[self.ft.index]
if not isinstance(buff_0, Buff):
raise TypeError(f"{buff_0}不是Buff类!")
if not buff_0.dy.active:
# 新触发的buff
if buff_0.ft.maxduration == 0:
endticks = timenow + timecost
else:
endticks = timenow + buff_0.ft.maxduration
else:
# 已经触发了buff
endticks = self.dy.endticks
if not self.ft.simple_hit_logic:
assert self.logic.xhit is not None, f"{self.ft.index}的buff没有初始化xhit方法"
self.logic.xhit(beneficiary=beneficiary)
self.dy.is_changed = True
self.update_to_buff_0(buff_0)
return
if not self.ft.hitincrease:
# EXPLAIN:如果hitincrease是False,则意味着在本函数内完全没有更新的可能,直接return就行。
return
if self.ft.fresh: # 处理可更新的buff(fresh = True)
# EXPLAIN:fresh参数和individual_settled是否等价?不,前者是命中时间完全不修改endticks,而后者则是独立结算机制。
# 在判定逻辑的优先级上,fresh和individual_settled包含关系,如果fresh为FALSE,那么无论层数是否独立结算,都会表现为相同的结果。
# 所以,只有fresh为True的buff,才有被区分是否独立结算的意义。
if self.ft.individual_settled:
# EXAMPLE:普攻命中时,攻击力提高3%,层数之间独立结算。
self.individual_setteled_update(self.ft.maxduration, timenow)
if self.ft.maxduration == 0:
# EXAMPLE:普攻期间命中时,攻击力提高3%,层数之间独立结算。
# 如果maxduration是0,那么endticks是不能变的。要还原回来。
self.dy.endticks = endticks
self.dy.active = True
self.dy.is_changed = True
self.dy.ready = False
else:
# EXAMPLE: 命中可叠层,且持续时间刷新。
# EXAMPLE:所有的具有复杂判断逻辑但是光环类的Debuff会在这里被处理。
self.dy.startticks = timenow
"""
这里还没完呢,startticks虽然更新了,但是endticks要不要更新还得看buff是否是瞬时buff。
瞬时buff到点结束,那就不能改变,只能照抄buff_0,
只有非瞬时buff,才会因hit刷新了持续时间而更新endticks。
"""
self.dy.endticks = (
timenow + self.ft.maxduration if self.ft.maxduration != 0 else endticks
)
self.dy.count = min(buff_0.dy.count + self.ft.step, self.ft.maxcount)
self.dy.active = True
self.dy.is_changed = True
self.dy.ready = False
else:
"""
处理剩下的其他buff逻辑(fresh = False 或瞬时 buff)
这些buff都是startticks不允许更新的,endticks也是如此。
"""
if not self.ft.individual_settled:
# EXAMPLE:强化E持续期间,命中一次叠层一次。
self.dy.count = min(buff_0.dy.count + self.ft.step, self.ft.maxcount)
self.dy.active = True
self.dy.is_changed = True
self.dy.ready = False
# report_to_log(f"[Buff INFO]:{timenow}:{buff_0.ft.index}第{buff_0.history.active_times}次触发", level=3)
if self.dy.is_changed:
self.update_to_buff_0(buff_0)
def __str__(self) -> str:
return f"Buff名: {self.ft.index}→{self.ft.description}"
def __deepcopy__(self, memo):
new_obj = Buff(self.feature_config, self.judge_config, sim_instance=self.sim_instance)
memo[id(self)] = new_obj
return new_obj
def spawn_buff_from_index(index: str, sim_instance: "Simulator"):
"""
注意:本函数基本上是为了Pytest服务的,所以涉及反复打开CSV,基本没有任何性能优化可言
正常的主程序运行不要调用本函数!!!!
"""
def find_row_as_dict(_index: str, csv_path: str):
"""根据index查找对应行并转换为字典"""
try:
df = pd.read_csv(csv_path)
# 查找匹配行(假设索引列名为'BuffName')
matched = df[df["BuffName"] == _index]
return matched.iloc[0].copy()
except FileNotFoundError:
raise FileNotFoundError(f"CSV文件 {csv_path} 不存在")
trigger_dict = find_row_as_dict(index, EXIST_FILE_PATH)
judge_dict = find_row_as_dict(index, JUDGE_FILE_PATH)
# 创建Buff实例
return Buff(trigger_dict, judge_dict, sim_instance=sim_instance)
================================================
FILE: zsim/sim_progress/Buff/buff_config.json
================================================
{
"Buff-角色-莱特-额外能力-冰火增伤": {
"module": ".BuffXLogic.LighterAdditionalAbility_IceFireBonus",
"class": "LighterExtraSkill_IceFireBonus"
},
"Buff-驱动盘-极地重金属-冲刺与普攻增伤-有条件": {
"module": ".BuffXLogic.PolarMetalFreezeBonus",
"class": "PolarMetalFreezeBonus"
},
"Buff-驱动盘-啄木鸟电音-普攻": {
"module": ".BuffXLogic.WoodpeckerElectroSet4_NA",
"class": "WoodpeckerElectroSet4_NA"
},
"Buff-驱动盘-啄木鸟电音-闪避反击": {
"module": ".BuffXLogic.WoodpeckerElectroSet4_CA",
"class": "WoodpeckerElectroSet4_CA"
},
"Buff-驱动盘-啄木鸟电音-强化特殊技": {
"module": ".BuffXLogic.WoodpeckerElectroSet4_E_EX",
"class": "WoodpeckerElectroSet4_E_EX"
},
"Buff-角色-莱特-核心被动-冲击力提升": {
"module": ".BuffXLogic.LighterUniqueSkillStunBonus",
"class": "LighterUniqueSkillStunBonus"
},
"Buff-角色-莱特-核心被动-失衡时间延长": {
"module": ".BuffXLogic.LighterUniqueSkillStunTimeLimitBonus",
"class": "LighterUniqueSkillStunTimeLimitBonus"
},
"Buff-角色-莱卡恩-额外能力-失衡易伤倍率": {
"module": ".BuffXLogic.LyconAdditionalAbilityStunVulnerability",
"class": "LyconAdditionalAbilityStunVulnerability"
},
"Buff-武器-精1燃狱齿轮-后台能量自动回复": {
"module": ".BuffXLogic.HellfireGearsSpRBonus",
"class": "HellfireGearsSpRBonus"
},
"Buff-武器-精2燃狱齿轮-后台能量自动回复": {
"module": ".BuffXLogic.HellfireGearsSpRBonus",
"class": "HellfireGearsSpRBonus"
},
"Buff-武器-精3燃狱齿轮-后台能量自动回复": {
"module": ".BuffXLogic.HellfireGearsSpRBonus",
"class": "HellfireGearsSpRBonus"
},
"Buff-武器-精4燃狱齿轮-后台能量自动回复": {
"module": ".BuffXLogic.HellfireGearsSpRBonus",
"class": "HellfireGearsSpRBonus"
},
"Buff-武器-精5燃狱齿轮-后台能量自动回复": {
"module": ".BuffXLogic.HellfireGearsSpRBonus",
"class": "HellfireGearsSpRBonus"
},
"Buff-武器-精1玉壶青冰-15层后增伤": {
"module": ".BuffXLogic.IceJadeTeaPotExtraDMGBonus",
"class": "IceJadeTeaPotExtraDMGBonus"
},
"Buff-武器-精2玉壶青冰-15层后增伤": {
"module": ".BuffXLogic.IceJadeTeaPotExtraDMGBonus",
"class": "IceJadeTeaPotExtraDMGBonus"
},
"Buff-武器-精3玉壶青冰-15层后增伤": {
"module": ".BuffXLogic.IceJadeTeaPotExtraDMGBonus",
"class": "IceJadeTeaPotExtraDMGBonus"
},
"Buff-武器-精4玉壶青冰-15层后增伤": {
"module": ".BuffXLogic.IceJadeTeaPotExtraDMGBonus",
"class": "IceJadeTeaPotExtraDMGBonus"
},
"Buff-武器-精5玉壶青冰-15层后增伤": {
"module": ".BuffXLogic.IceJadeTeaPotExtraDMGBonus",
"class": "IceJadeTeaPotExtraDMGBonus"
},
"Buff-异常-霜寒": {
"module": ".BuffXLogic.AnomalyDebuffExitJudge",
"class": "AnomalyDebuffExitJudge"
},
"Buff-异常-畏缩": {
"module": ".BuffXLogic.AnomalyDebuffExitJudge",
"class": "AnomalyDebuffExitJudge"
},
"Buff-异常-烈霜霜寒": {
"module": ".BuffXLogic.AnomalyDebuffExitJudge",
"class": "AnomalyDebuffExitJudge"
},
"Buff-角色-雅-核心被动-冰焰": {
"module": ".BuffXLogic.MiyabiCoreSkill_IceFire",
"class": "MiyabiCoreSkill_IceFire"
},
"Buff-角色-雅-核心被动-霜灼": {
"module": ".BuffXLogic.MiyabiCoreSkill_FrostBurn",
"class": "MiyabiCoreSkill_FrostBurn"
},
"Buff-角色-雅-组队被动-无视冰抗": {
"module": ".BuffXLogic.MiyabiAdditionalAbility_IgnoreIceRes",
"class": "MiyabiAdditionalAbility_IgnoreIceRes"
},
"Buff-音擎-精1霰落星殿-叠层冰伤": {
"module": ".BuffXLogic.HailstormShrineIceBonus",
"class": "HailstormShrineIceBonus"
},
"Buff-音擎-精2霰落星殿-叠层冰伤": {
"module": ".BuffXLogic.HailstormShrineIceBonus",
"class": "HailstormShrineIceBonus"
},
"Buff-音擎-精3霰落星殿-叠层冰伤": {
"module": ".BuffXLogic.HailstormShrineIceBonus",
"class": "HailstormShrineIceBonus"
},
"Buff-音擎-精4霰落星殿-叠层冰伤": {
"module": ".BuffXLogic.HailstormShrineIceBonus",
"class": "HailstormShrineIceBonus"
},
"Buff-音擎-精5霰落星殿-叠层冰伤": {
"module": ".BuffXLogic.HailstormShrineIceBonus",
"class": "HailstormShrineIceBonus"
},
"Buff-驱动盘-折枝剑歌-暴伤": {
"module": ".BuffXLogic.BranchBladeSongCritDamageBonus",
"class": "BranchBladeSongCritDamageBonus"
},
"Buff-驱动盘-折枝剑歌-暴击率": {
"module": ".BuffXLogic.BranchBladeSongCritRateBonus",
"class": "BranchBladeSongCritRateBonus"
},
"Buff-武器-精1贵重骨核-75%以上": {
"module": ".BuffXLogic.PreciousFossilizedCoreStunBonusOver75Hp",
"class": "PreciousFossilizedCoreStunBonusOver75Hp"
},
"Buff-武器-精2贵重骨核-75%以上": {
"module": ".BuffXLogic.PreciousFossilizedCoreStunBonusOver75Hp",
"class": "PreciousFossilizedCoreStunBonusOver75Hp"
},
"Buff-武器-精3贵重骨核-75%以上": {
"module": ".BuffXLogic.PreciousFossilizedCoreStunBonusOver75Hp",
"class": "PreciousFossilizedCoreStunBonusOver75Hp"
},
"Buff-武器-精4贵重骨核-75%以上": {
"module": ".BuffXLogic.PreciousFossilizedCoreStunBonusOver75Hp",
"class": "PreciousFossilizedCoreStunBonusOver75Hp"
},
"Buff-武器-精5贵重骨核-75%以上": {
"module": ".BuffXLogic.PreciousFossilizedCoreStunBonusOver75Hp",
"class": "PreciousFossilizedCoreStunBonusOver75Hp"
},
"Buff-武器-精1贵重骨核-50%以上": {
"module": ".BuffXLogic.PreciousFossilizedCoreStunBonusOver50Hp",
"class": "PreciousFossilizedCoreStunBonusOver50Hp"
},
"Buff-武器-精2贵重骨核-50%以上": {
"module": ".BuffXLogic.PreciousFossilizedCoreStunBonusOver50Hp",
"class": "PreciousFossilizedCoreStunBonusOver50Hp"
},
"Buff-武器-精3贵重骨核-50%以上": {
"module": ".BuffXLogic.PreciousFossilizedCoreStunBonusOver50Hp",
"class": "PreciousFossilizedCoreStunBonusOver50Hp"
},
"Buff-武器-精4贵重骨核-50%以上": {
"module": ".BuffXLogic.PreciousFossilizedCoreStunBonusOver50Hp",
"class": "PreciousFossilizedCoreStunBonusOver50Hp"
},
"Buff-武器-精5贵重骨核-50%以上": {
"module": ".BuffXLogic.PreciousFossilizedCoreStunBonusOver50Hp",
"class": "PreciousFossilizedCoreStunBonusOver50Hp"
},
"Buff-武器-精1人为刀俎": {
"module": ".BuffXLogic.SteamOven",
"class": "SteamOven"
},
"Buff-武器-精2人为刀俎": {
"module": ".BuffXLogic.SteamOven",
"class": "SteamOven"
},
"Buff-武器-精3人为刀俎": {
"module": ".BuffXLogic.SteamOven",
"class": "SteamOven"
},
"Buff-武器-精4人为刀俎": {
"module": ".BuffXLogic.SteamOven",
"class": "SteamOven"
},
"Buff-武器-精5人为刀俎": {
"module": ".BuffXLogic.SteamOven",
"class": "SteamOven"
},
"Buff-角色-苍角-额外能力": {
"module": ".BuffXLogic.SokakuAdditionalAbilityICEBonus",
"class": "SokakuAdditionalAbilityICEBonus"
},
"Buff-角色-苍角-核心被动-1": {
"module": ".BuffXLogic.SokakuUniqueSkillMinorATKBonus",
"class": "SokakuUniqueSkillMinorATKBonus"
},
"Buff-角色-苍角-核心被动-2": {
"module": ".BuffXLogic.SokakuUniqueSkillMajorATKBonus",
"class": "SokakuUniqueSkillMajorATKBonus"
},
"Buff-角色-青衣-核心被动-失衡易伤": {
"module": ".BuffXLogic.QingYiCoreSkillStunDMGBonus",
"class": "QingYiCoreSkillStunDMGBonus"
},
"Buff-角色-青衣-核心被动-额外电压补偿": {
"module": ".BuffXLogic.QingYiCoreSkillExtraStunBonus",
"class": "QingYiCoreSkillExtraStunBonus"
},
"Buff-角色-青衣-额外能力-冲击转攻击": {
"module": ".BuffXLogic.QingYiAdditionalAbilityStunConvertToATK",
"class": "QingYiAdditionalAbilityStunConvertToATK"
},
"Buff-角色-丽娜-核心被动-穿透率": {
"module": ".BuffXLogic.LinaCoreSkillPenRatioBonus",
"class": "LinaCoreSkillPenRatioBonus"
},
"Buff-角色-丽娜-组队被动-增伤": {
"module": ".BuffXLogic.LinaAdditionalSkillEleDMGBonus",
"class": "LinaAdditionalSkillEleDMGBonus"
},
"Buff-驱动盘-自由蓝调-物理": {
"module": ".BuffXLogic.FreedomBlues",
"class": "FreedomBlues"
},
"Buff-驱动盘-自由蓝调-火": {
"module": ".BuffXLogic.FreedomBlues",
"class": "FreedomBlues"
},
"Buff-驱动盘-自由蓝调-冰": {
"module": ".BuffXLogic.FreedomBlues",
"class": "FreedomBlues"
},
"Buff-驱动盘-自由蓝调-电": {
"module": ".BuffXLogic.FreedomBlues",
"class": "FreedomBlues"
},
"Buff-驱动盘-自由蓝调-以太": {
"module": ".BuffXLogic.FreedomBlues",
"class": "FreedomBlues"
},
"Buff-驱动盘-自由蓝调-烈霜": {
"module": ".BuffXLogic.FreedomBlues",
"class": "FreedomBlues"
},
"Buff-驱动盘-静听嘉音-增伤": {
"module": ".BuffXLogic.AstralVoice",
"class": "AstralVoice"
},
"Buff-武器-精1啜泣摇篮-全队增伤自增长": {
"module": ".BuffXLogic.WeepingCradleDMGBonusIncrease",
"class": "WeepingCradleDMGBonusIncrease"
},
"Buff-武器-精1啜泣摇篮-后台回能": {
"module": ".BuffXLogic.BackendJudge",
"class": "BackendJudge"
},
"Buff-武器-精2啜泣摇篮-全队增伤自增长": {
"module": ".BuffXLogic.WeepingCradleDMGBonusIncrease",
"class": "WeepingCradleDMGBonusIncrease"
},
"Buff-武器-精2啜泣摇篮-后台回能": {
"module": ".BuffXLogic.BackendJudge",
"class": "BackendJudge"
},
"Buff-武器-精3啜泣摇篮-全队增伤自增长": {
"module": ".BuffXLogic.WeepingCradleDMGBonusIncrease",
"class": "WeepingCradleDMGBonusIncrease"
},
"Buff-武器-精3啜泣摇篮-后台回能": {
"module": ".BuffXLogic.BackendJudge",
"class": "BackendJudge"
},
"Buff-武器-精4啜泣摇篮-全队增伤自增长": {
"module": ".BuffXLogic.WeepingCradleDMGBonusIncrease",
"class": "WeepingCradleDMGBonusIncrease"
},
"Buff-武器-精4啜泣摇篮-后台回能": {
"module": ".BuffXLogic.BackendJudge",
"class": "BackendJudge"
},
"Buff-武器-精5啜泣摇篮-全队增伤自增长": {
"module": ".BuffXLogic.WeepingCradleDMGBonusIncrease",
"class": "WeepingCradleDMGBonusIncrease"
},
"Buff-武器-精5啜泣摇篮-后台回能": {
"module": ".BuffXLogic.BackendJudge",
"class": "BackendJudge"
},
"Buff-武器-精1时光切片-回能回喧响": {
"module": ".BuffXLogic.SliceofTimeExtraResources",
"class": "SliceofTimeExtraResources"
},
"Buff-武器-精2时光切片-回能回喧响": {
"module": ".BuffXLogic.SliceofTimeExtraResources",
"class": "SliceofTimeExtraResources"
},
"Buff-武器-精3时光切片-回能回喧响": {
"module": ".BuffXLogic.SliceofTimeExtraResources",
"class": "SliceofTimeExtraResources"
},
"Buff-武器-精4时光切片-回能回喧响": {
"module": ".BuffXLogic.SliceofTimeExtraResources",
"class": "SliceofTimeExtraResources"
},
"Buff-武器-精5时光切片-回能回喧响": {
"module": ".BuffXLogic.SliceofTimeExtraResources",
"class": "SliceofTimeExtraResources"
},
"Buff-武器-精1聚宝箱-回能": {
"module": ".BuffXLogic.TheVault",
"class": "TheVault"
},
"Buff-武器-精2聚宝箱-回能": {
"module": ".BuffXLogic.TheVault",
"class": "TheVault"
},
"Buff-武器-精3聚宝箱-回能": {
"module": ".BuffXLogic.TheVault",
"class": "TheVault"
},
"Buff-武器-精4聚宝箱-回能": {
"module": ".BuffXLogic.TheVault",
"class": "TheVault"
},
"Buff-武器-精5聚宝箱-回能": {
"module": ".BuffXLogic.TheVault",
"class": "TheVault"
},
"Buff-武器-精1聚宝箱-全队增伤": {
"module": ".BuffXLogic.TheVault",
"class": "TheVault"
},
"Buff-武器-精2聚宝箱-全队增伤": {
"module": ".BuffXLogic.TheVault",
"class": "TheVault"
},
"Buff-武器-精3聚宝箱-全队增伤": {
"module": ".BuffXLogic.TheVault",
"class": "TheVault"
},
"Buff-武器-精4聚宝箱-全队增伤": {
"module": ".BuffXLogic.TheVault",
"class": "TheVault"
},
"Buff-武器-精5聚宝箱-全队增伤": {
"module": ".BuffXLogic.TheVault",
"class": "TheVault"
},
"Buff-武器-精1好斗的阿炮-全局攻击力": {
"module": ".BuffXLogic.KaboomTheCannon",
"class": "KaboomTheCannon"
},
"Buff-武器-精2好斗的阿炮-全局攻击力": {
"module": ".BuffXLogic.KaboomTheCannon",
"class": "KaboomTheCannon"
},
"Buff-武器-精3好斗的阿炮-全局攻击力": {
"module": ".BuffXLogic.KaboomTheCannon",
"class": "KaboomTheCannon"
},
"Buff-武器-精4好斗的阿炮-全局攻击力": {
"module": ".BuffXLogic.KaboomTheCannon",
"class": "KaboomTheCannon"
},
"Buff-武器-精5好斗的阿炮-全局攻击力": {
"module": ".BuffXLogic.KaboomTheCannon",
"class": "KaboomTheCannon"
},
"Buff-角色-零号·安比-银星触发器": {
"module": ".BuffXLogic.Soldier0AnbySilverStarTrigger",
"class": "Soldier0AnbySilverStarTrigger"
},
"Buff-角色-零号·安比-核心被动-增伤": {
"module": ".BuffXLogic.Soldier0AnbyCoreSkillDMGBonus",
"class": "Soldier0AnbyCoreSkillDMGBonus"
},
"Buff-角色-零号·安比-核心被动-受暴伤增加": {
"module": ".BuffXLogic.Soldier0AnbyCoreSkillCritDMGBonus",
"class": "Soldier0AnbyCoreSkillCritDMGBonus"
},
"Buff-角色-零号·安比-组队被动-全队对银星目标增伤": {
"module": ".BuffXLogic.Soldier0AnbyAdditionalSkillDMGBonus",
"class": "Soldier0AnbyAdditionalSkillDMGBonus"
},
"Buff-角色-零号·安比-4画-无视电抗": {
"module": ".BuffXLogic.Soldier0AnbyCinema4EleResReduce",
"class": "Soldier0AnbyCinema4EleResReduce"
},
"Buff-武器-精1牺牲洁纯-触发暴伤": {
"module": ".BuffXLogic.SeveredInnocenceCritDMGBonus",
"class": "SeveredInnocenceCritDMGBonus"
},
"Buff-武器-精2牺牲洁纯-触发暴伤": {
"module": ".BuffXLogic.SeveredInnocenceCritDMGBonus",
"class": "SeveredInnocenceCritDMGBonus"
},
"Buff-武器-精3牺牲洁纯-触发暴伤": {
"module": ".BuffXLogic.SeveredInnocenceCritDMGBonus",
"class": "SeveredInnocenceCritDMGBonus"
},
"Buff-武器-精4牺牲洁纯-触发暴伤": {
"module": ".BuffXLogic.SeveredInnocenceCritDMGBonus",
"class": "SeveredInnocenceCritDMGBonus"
},
"Buff-武器-精5牺牲洁纯-触发暴伤": {
"module": ".BuffXLogic.SeveredInnocenceCritDMGBonus",
"class": "SeveredInnocenceCritDMGBonus"
},
"Buff-武器-精1牺牲洁纯-满层电伤": {
"module": ".BuffXLogic.SeveredInnocencELEDMGBonus",
"class": "SeveredInnocencELEDMGBonus"
},
"Buff-武器-精2牺牲洁纯-满层电伤": {
"module": ".BuffXLogic.SeveredInnocencELEDMGBonus",
"class": "SeveredInnocencELEDMGBonus"
},
"Buff-武器-精3牺牲洁纯-满层电伤": {
"module": ".BuffXLogic.SeveredInnocencELEDMGBonus",
"class": "SeveredInnocencELEDMGBonus"
},
"Buff-武器-精4牺牲洁纯-满层电伤": {
"module": ".BuffXLogic.SeveredInnocencELEDMGBonus",
"class": "SeveredInnocencELEDMGBonus"
},
"Buff-武器-精5牺牲洁纯-满层电伤": {
"module": ".BuffXLogic.SeveredInnocencELEDMGBonus",
"class": "SeveredInnocencELEDMGBonus"
},
"Buff-驱动盘-如影相随-四件套": {
"module": ".BuffXLogic.ShadowHarmony4",
"class": "ShadowHarmony4"
},
"Buff-角色-扳机-协同攻击-触发器": {
"module": ".BuffXLogic.TriggerAfterShockTrigger",
"class": "TriggerAfterShockTrigger"
},
"Buff-角色-扳机-核心被动-失衡易伤": {
"module": ".BuffXLogic.TriggerCoreSkillStunDMGBonus",
"class": "TriggerCoreSkillStunDMGBonus"
},
"Buff-角色-扳机-额外能力-追加攻击失衡值提升": {
"module": ".BuffXLogic.TriggerAdditionalAbilityStunBonus",
"class": "TriggerAdditionalAbilityStunBonus"
},
"Buff-角色-扳机-1画-失衡易伤提升": {
"module": ".BuffXLogic.TriggerCoreSkillStunDMGBonus",
"class": "TriggerCoreSkillStunDMGBonus"
},
"Buff-武器-精1索魂影眸-减防": {
"module": ".BuffXLogic.SpectralGazeDefReduce",
"class": "SpectralGazeDefReduce"
},
"Buff-武器-精2索魂影眸-减防": {
"module": ".BuffXLogic.SpectralGazeDefReduce",
"class": "SpectralGazeDefReduce"
},
"Buff-武器-精3索魂影眸-减防": {
"module": ".BuffXLogic.SpectralGazeDefReduce",
"class": "SpectralGazeDefReduce"
},
"Buff-武器-精4索魂影眸-减防": {
"module": ".BuffXLogic.SpectralGazeDefReduce",
"class": "SpectralGazeDefReduce"
},
"Buff-武器-精5索魂影眸-减防": {
"module": ".BuffXLogic.SpectralGazeDefReduce",
"class": "SpectralGazeDefReduce"
},
"Buff-武器-精1索魂影眸-魂锁": {
"module": ".BuffXLogic.SpectralGazeSpiritLock",
"class": "SpectralGazeSpiritLock"
},
"Buff-武器-精2索魂影眸-魂锁": {
"module": ".BuffXLogic.SpectralGazeSpiritLock",
"class": "SpectralGazeSpiritLock"
},
"Buff-武器-精3索魂影眸-魂锁": {
"module": ".BuffXLogic.SpectralGazeSpiritLock",
"class": "SpectralGazeSpiritLock"
},
"Buff-武器-精4索魂影眸-魂锁": {
"module": ".BuffXLogic.SpectralGazeSpiritLock",
"class": "SpectralGazeSpiritLock"
},
"Buff-武器-精5索魂影眸-魂锁": {
"module": ".BuffXLogic.SpectralGazeSpiritLock",
"class": "SpectralGazeSpiritLock"
},
"Buff-武器-精1索魂影眸-冲击力": {
"module": ".BuffXLogic.SpectralGazeImpactBonus",
"class": "SpectralGazeImpactBonus"
},
"Buff-武器-精2索魂影眸-冲击力": {
"module": ".BuffXLogic.SpectralGazeImpactBonus",
"class": "SpectralGazeImpactBonus"
},
"Buff-武器-精3索魂影眸-冲击力": {
"module": ".BuffXLogic.SpectralGazeImpactBonus",
"class": "SpectralGazeImpactBonus"
},
"Buff-武器-精4索魂影眸-冲击力": {
"module": ".BuffXLogic.SpectralGazeImpactBonus",
"class": "SpectralGazeImpactBonus"
},
"Buff-武器-精5索魂影眸-冲击力": {
"module": ".BuffXLogic.SpectralGazeImpactBonus",
"class": "SpectralGazeImpactBonus"
},
"Buff-角色-柳-架势-上弦": {
"module": ".BuffXLogic.YanagiStanceJougen",
"class": "YanagiStanceJougen"
},
"Buff-角色-柳-架势-下弦": {
"module": ".BuffXLogic.YanagiStanceKagen",
"class": "YanagiStanceKagen"
},
"Buff-角色-柳-极性紊乱触发器": {
"module": ".BuffXLogic.YanagiPolarityDisorderTrigger",
"class": "YanagiPolarityDisorderTrigger"
},
"Buff-角色-简-狂热状态触发器": {
"module": ".BuffXLogic.JanePassionStateTrigger",
"class": "JanePassionStateTrigger"
},
"Buff-角色-简-狂热-物理积蓄效率提升": {
"module": ".BuffXLogic.JanePassionStatePhyBuildupBonus",
"class": "JanePassionStatePhyBuildupBonus"
},
"Buff-角色-简-狂热-额外精通转攻击力": {
"module": ".BuffXLogic.JanePassionStateAPTransToATK",
"class": "JanePassionStateAPTransToATK"
},
"Buff-角色-简-核心被动-啮咬-强击暴击率提升": {
"module": ".BuffXLogic.JaneCoreSkillStrikeCritRateBonus",
"class": "JaneCoreSkillStrikeCritRateBonus"
},
"Buff-角色-简-核心被动-啮咬-强击暴击伤害提升": {
"module": ".BuffXLogic.JaneCoreSkillStrikeCritDmgBonus",
"class": "JaneCoreSkillStrikeCritDmgBonus"
},
"Buff-角色-简-额外能力-物理异常积蓄效率额外提升": {
"module": ".BuffXLogic.JaneAdditionalAbilityPhyBuildupBonus",
"class": "JaneAdditionalAbilityPhyBuildupBonus"
},
"Buff-角色-简-1画-狂热物理异常积蓄效率额外提升": {
"module": ".BuffXLogic.JanePassionStatePhyBuildupBonus",
"class": "JanePassionStatePhyBuildupBonus"
},
"Buff-角色-简-1画-精通转增伤": {
"module": ".BuffXLogic.JaneCinema1APTransToDmgBonus",
"class": "JaneCinema1APTransToDmgBonus"
},
"Buff-角色-简-2画-啮咬-强击无视防御与暴击伤害提升" : {
"module": ".BuffXLogic.JaneCoreSkillStrikeCritDmgBonus",
"class": "JaneCoreSkillStrikeCritDmgBonus"
},
"Buff-角色-简-2画-啮咬-攻击无视防御" : {
"module": ".BuffXLogic.JaneCoreSkillStrikeCritDmgBonus",
"class": "JaneCoreSkillStrikeCritDmgBonus"
},
"Buff-角色-柳-1画-精通增幅": {
"module": ".BuffXLogic.YangiCinema1ApBonus",
"class": "YangiCinema1ApBonus"
},
"Buff-角色-柳-6画-特殊技伤害提升": {
"module": ".BuffXLogic.YanagiCinema6EXDmgBonus",
"class": "YanagiCinema6EXDmgBonus"
},
"Buff-角色-薇薇安-协同攻击触发器": {
"module": ".BuffXLogic.VivianCoattackTrigger",
"class": "VivianCoattackTrigger"
},
"Buff-角色-薇薇安-羽毛结算触发器": {
"module": ".BuffXLogic.VivianFeatherTrigger",
"class": "VivianFeatherTrigger"
},
"Buff-角色-薇薇安-核心被动触发器": {
"module": ".BuffXLogic.VivianCorePassiveTrigger",
"class": "VivianCorePassiveTrigger"
},
"Buff-角色-薇薇安-预言触发器": {
"module": ".BuffXLogic.VivianDotTrigger",
"class": "VivianDotTrigger"
},
"Buff-角色-薇薇安-额外能力-协同攻击触发器": {
"module": ".BuffXLogic.VivianAdditionalAbilityCoAttackTrigger",
"class": "VivianAdditionalAbilityCoAttackTrigger"
},
"Buff-角色-薇薇安-1画-全属性异常和紊乱伤害提升": {
"module": ".BuffXLogic.VivianCinema1Debuff",
"class": "VivianCinema1Debuff"
},
"Buff-角色-薇薇安-6画-触发器": {
"module": ".BuffXLogic.VivianCinema6Trigger",
"class": "VivianCinema6Trigger"
},
"Buff-驱动盘-法厄同之歌-四件套-以太伤害提高": {
"module": ".BuffXLogic.PhaethonsMelody",
"class": "PhaethonsMelody"
},
"Buff-武器-精1飞鸟星梦-精通增幅": {
"module": ".BuffXLogic.FlightOfFancy",
"class": "FlightOfFancy"
},
"Buff-武器-精2飞鸟星梦-精通增幅": {
"module": ".BuffXLogic.FlightOfFancy",
"class": "FlightOfFancy"
},
"Buff-武器-精3飞鸟星梦-精通增幅": {
"module": ".BuffXLogic.FlightOfFancy",
"class": "FlightOfFancy"
},
"Buff-武器-精4飞鸟星梦-精通增幅": {
"module": ".BuffXLogic.FlightOfFancy",
"class": "FlightOfFancy"
},
"Buff-武器-精5飞鸟星梦-精通增幅": {
"module": ".BuffXLogic.FlightOfFancy",
"class": "FlightOfFancy"
},
"Buff-武器-精1时流贤者-精通提升": {
"module": ".BuffXLogic.TimeweaverApBonus",
"class": "TimeweaverApBonus"
},
"Buff-武器-精2时流贤者-精通提升": {
"module": ".BuffXLogic.TimeweaverApBonus",
"class": "TimeweaverApBonus"
},
"Buff-武器-精3时流贤者-精通提升": {
"module": ".BuffXLogic.TimeweaverApBonus",
"class": "TimeweaverApBonus"
},
"Buff-武器-精4时流贤者-精通提升": {
"module": ".BuffXLogic.TimeweaverApBonus",
"class": "TimeweaverApBonus"
},
"Buff-武器-精5时流贤者-精通提升": {
"module": ".BuffXLogic.TimeweaverApBonus",
"class": "TimeweaverApBonus"
},
"Buff-武器-精1时流贤者-装备者紊乱伤害提升": {
"module": ".BuffXLogic.TimeweaverDisorderDmgMul",
"class": "TimeweaverDisorderDmgMul"
},
"Buff-武器-精2时流贤者-装备者紊乱伤害提升": {
"module": ".BuffXLogic.TimeweaverDisorderDmgMul",
"class": "TimeweaverDisorderDmgMul"
},
"Buff-武器-精3时流贤者-装备者紊乱伤害提升": {
"module": ".BuffXLogic.TimeweaverDisorderDmgMul",
"class": "TimeweaverDisorderDmgMul"
},
"Buff-武器-精4时流贤者-装备者紊乱伤害提升": {
"module": ".BuffXLogic.TimeweaverDisorderDmgMul",
"class": "TimeweaverDisorderDmgMul"
},
"Buff-武器-精5时流贤者-装备者紊乱伤害提升": {
"module": ".BuffXLogic.TimeweaverDisorderDmgMul",
"class": "TimeweaverDisorderDmgMul"
},
"Buff-武器-精1淬锋钳刺-猎意": {
"module": ".BuffXLogic.SharpenedStingerPhyDmgBonus",
"class": "SharpenedStingerPhyDmgBonus"
},
"Buff-武器-精2淬锋钳刺-猎意": {
"module": ".BuffXLogic.SharpenedStingerPhyDmgBonus",
"class": "SharpenedStingerPhyDmgBonus"
},
"Buff-武器-精3淬锋钳刺-猎意": {
"module": ".BuffXLogic.SharpenedStingerPhyDmgBonus",
"class": "SharpenedStingerPhyDmgBonus"
},
"Buff-武器-精4淬锋钳刺-猎意": {
"module": ".BuffXLogic.SharpenedStingerPhyDmgBonus",
"class": "SharpenedStingerPhyDmgBonus"
},
"Buff-武器-精5淬锋钳刺-猎意": {
"module": ".BuffXLogic.SharpenedStingerPhyDmgBonus",
"class": "SharpenedStingerPhyDmgBonus"
},
"Buff-武器-精1淬锋钳刺-属性异常积蓄效率提升" : {
"module": ".BuffXLogic.SharpenedStingerAnomalyBuildupBonus",
"class": "SharpenedStingerAnomalyBuildupBonus"
},
"Buff-武器-精2淬锋钳刺-属性异常积蓄效率提升" : {
"module": ".BuffXLogic.SharpenedStingerAnomalyBuildupBonus",
"class": "SharpenedStingerAnomalyBuildupBonus"
},
"Buff-武器-精3淬锋钳刺-属性异常积蓄效率提升" : {
"module": ".BuffXLogic.SharpenedStingerAnomalyBuildupBonus",
"class": "SharpenedStingerAnomalyBuildupBonus"
},
"Buff-武器-精4淬锋钳刺-属性异常积蓄效率提升" : {
"module": ".BuffXLogic.SharpenedStingerAnomalyBuildupBonus",
"class": "SharpenedStingerAnomalyBuildupBonus"
},
"Buff-武器-精5淬锋钳刺-属性异常积蓄效率提升" : {
"module": ".BuffXLogic.SharpenedStingerAnomalyBuildupBonus",
"class": "SharpenedStingerAnomalyBuildupBonus"
},
"Buff-角色-耀佳音-咏叹华彩": {
"module": ".BuffXLogic.AstraYaoIdyllicCadenza",
"class": "AstraYaoIdyllicCadenza"
},
"Buff-角色-耀佳音-核心被动-攻击力": {
"module": ".BuffXLogic.AstraYaoCorePassiveAtkBonus",
"class": "AstraYaoCorePassiveAtkBonus"
},
"Buff-角色-耀佳音-快支管理器-触发器": {
"module": ".BuffXLogic.AstraYaoQuickAssistManagerTrigger",
"class": "AstraYaoQuickAssistManagerTrigger"
},
"Buff-角色-耀佳音-震音管理器-触发器": {
"module": ".BuffXLogic.AstraYaoChordManagerTrigger",
"class": "AstraYaoChordManagerTrigger"
},
"Buff-武器-精1玲珑妆匣-回能": {
"module": ".BuffXLogic.ElegantVanitySpRecover",
"class": "ElegantVanitySpRecover"
},
"Buff-武器-精2玲珑妆匣-回能": {
"module": ".BuffXLogic.ElegantVanitySpRecover",
"class": "ElegantVanitySpRecover"
},
"Buff-武器-精3玲珑妆匣-回能": {
"module": ".BuffXLogic.ElegantVanitySpRecover",
"class": "ElegantVanitySpRecover"
},
"Buff-武器-精4玲珑妆匣-回能": {
"module": ".BuffXLogic.ElegantVanitySpRecover",
"class": "ElegantVanitySpRecover"
},
"Buff-武器-精5玲珑妆匣-回能": {
"module": ".BuffXLogic.ElegantVanitySpRecover",
"class": "ElegantVanitySpRecover"
},
"Buff-武器-精1玲珑妆匣-全队增伤": {
"module": ".BuffXLogic.ElegantVanityDmgBonus",
"class": "ElegantVanityDmgBonus"
},
"Buff-武器-精2玲珑妆匣-全队增伤": {
"module": ".BuffXLogic.ElegantVanityDmgBonus",
"class": "ElegantVanityDmgBonus"
},
"Buff-武器-精3玲珑妆匣-全队增伤": {
"module": ".BuffXLogic.ElegantVanityDmgBonus",
"class": "ElegantVanityDmgBonus"
},
"Buff-武器-精4玲珑妆匣-全队增伤": {
"module": ".BuffXLogic.ElegantVanityDmgBonus",
"class": "ElegantVanityDmgBonus"
},
"Buff-武器-精5玲珑妆匣-全队增伤": {
"module": ".BuffXLogic.ElegantVanityDmgBonus",
"class": "ElegantVanityDmgBonus"
},
"Buff-武器-精1灼心摇壶-回能": {
"module": ".BuffXLogic.BackendJudge",
"class": "BackendJudge"
},
"Buff-武器-精2灼心摇壶-回能": {
"module": ".BuffXLogic.BackendJudge",
"class": "BackendJudge"
},
"Buff-武器-精3灼心摇壶-回能": {
"module": ".BuffXLogic.BackendJudge",
"class": "BackendJudge"
},
"Buff-武器-精4灼心摇壶-回能": {
"module": ".BuffXLogic.BackendJudge",
"class": "BackendJudge"
},
"Buff-武器-精5灼心摇壶-回能": {
"module": ".BuffXLogic.BackendJudge",
"class": "BackendJudge"
},
"Buff-武器-精1灼心摇壶-增伤": {
"module": ".BuffXLogic.FlamemakerShakerDmgBonus",
"class": "FlamemakerShakerDmgBonus"
},
"Buff-武器-精2灼心摇壶-增伤": {
"module": ".BuffXLogic.FlamemakerShakerDmgBonus",
"class": "FlamemakerShakerDmgBonus"
},
"Buff-武器-精3灼心摇壶-增伤": {
"module": ".BuffXLogic.FlamemakerShakerDmgBonus",
"class": "FlamemakerShakerDmgBonus"
},
"Buff-武器-精4灼心摇壶-增伤": {
"module": ".BuffXLogic.FlamemakerShakerDmgBonus",
"class": "FlamemakerShakerDmgBonus"
},
"Buff-武器-精5灼心摇壶-增伤": {
"module": ".BuffXLogic.FlamemakerShakerDmgBonus",
"class": "FlamemakerShakerDmgBonus"
},
"Buff-武器-精1灼心摇壶-精通": {
"module": ".BuffXLogic.FlamemakerShakerApBonus",
"class": "FlamemakerShakerApBonus"
},
"Buff-武器-精2灼心摇壶-精通": {
"module": ".BuffXLogic.FlamemakerShakerApBonus",
"class": "FlamemakerShakerApBonus"
},
"Buff-武器-精3灼心摇壶-精通": {
"module": ".BuffXLogic.FlamemakerShakerApBonus",
"class": "FlamemakerShakerApBonus"
},
"Buff-武器-精4灼心摇壶-精通": {
"module": ".BuffXLogic.FlamemakerShakerApBonus",
"class": "FlamemakerShakerApBonus"
},
"Buff-武器-精5灼心摇壶-精通": {
"module": ".BuffXLogic.FlamemakerShakerApBonus",
"class": "FlamemakerShakerApBonus"
},
"Buff-武器-精1雨林饕客-局内攻击力": {
"module": ".BuffXLogic.RainforestGourmetATKBonus",
"class": "RainforestGourmetATKBonus"
},
"Buff-武器-精2雨林饕客-局内攻击力": {
"module": ".BuffXLogic.RainforestGourmetATKBonus",
"class": "RainforestGourmetATKBonus"
},
"Buff-武器-精3雨林饕客-局内攻击力": {
"module": ".BuffXLogic.RainforestGourmetATKBonus",
"class": "RainforestGourmetATKBonus"
},
"Buff-武器-精4雨林饕客-局内攻击力": {
"module": ".BuffXLogic.RainforestGourmetATKBonus",
"class": "RainforestGourmetATKBonus"
},
"Buff-武器-精5雨林饕客-局内攻击力": {
"module": ".BuffXLogic.RainforestGourmetATKBonus",
"class": "RainforestGourmetATKBonus"
},
"Buff-武器-精1双生泣星-精通增幅": {
"module": ".BuffXLogic.WeepingGeminiApBonus",
"class": "WeepingGeminiApBonus"
},
"Buff-武器-精2双生泣星-精通增幅": {
"module": ".BuffXLogic.WeepingGeminiApBonus",
"class": "WeepingGeminiApBonus"
},
"Buff-武器-精3双生泣星-精通增幅": {
"module": ".BuffXLogic.WeepingGeminiApBonus",
"class": "WeepingGeminiApBonus"
},
"Buff-武器-精4双生泣星-精通增幅": {
"module": ".BuffXLogic.WeepingGeminiApBonus",
"class": "WeepingGeminiApBonus"
},
"Buff-武器-精5双生泣星-精通增幅": {
"module": ".BuffXLogic.WeepingGeminiApBonus",
"class": "WeepingGeminiApBonus"
},
"Buff-武器-精1触电唇彩-攻击力与增伤": {
"module": ".BuffXLogic.ElectroLipGlossAtkAndDmgBonus",
"class": "ElectroLipGlossAtkAndDmgBonus"
},
"Buff-武器-精2触电唇彩-攻击力与增伤": {
"module": ".BuffXLogic.ElectroLipGlossAtkAndDmgBonus",
"class": "ElectroLipGlossAtkAndDmgBonus"
},
"Buff-武器-精3触电唇彩-攻击力与增伤": {
"module": ".BuffXLogic.ElectroLipGlossAtkAndDmgBonus",
"class": "ElectroLipGlossAtkAndDmgBonus"
},
"Buff-武器-精4触电唇彩-攻击力与增伤": {
"module": ".BuffXLogic.ElectroLipGlossAtkAndDmgBonus",
"class": "ElectroLipGlossAtkAndDmgBonus"
},
"Buff-武器-精5触电唇彩-攻击力与增伤": {
"module": ".BuffXLogic.ElectroLipGlossAtkAndDmgBonus",
"class": "ElectroLipGlossAtkAndDmgBonus"
},
"Buff-武器-精1轰鸣座驾-触发器": {
"module": ".BuffXLogic.RoaringRideBuffTrigger",
"class": "RoaringRideBuffTrigger"
},
"Buff-武器-精2轰鸣座驾-触发器": {
"module": ".BuffXLogic.RoaringRideBuffTrigger",
"class": "RoaringRideBuffTrigger"
},
"Buff-武器-精3轰鸣座驾-触发器": {
"module": ".BuffXLogic.RoaringRideBuffTrigger",
"class": "RoaringRideBuffTrigger"
},
"Buff-武器-精4轰鸣座驾-触发器": {
"module": ".BuffXLogic.RoaringRideBuffTrigger",
"class": "RoaringRideBuffTrigger"
},
"Buff-武器-精5轰鸣座驾-触发器": {
"module": ".BuffXLogic.RoaringRideBuffTrigger",
"class": "RoaringRideBuffTrigger"
},
"Buff-武器-精1「电磁暴」-壹式-异常掌控": {
"module": ".BuffXLogic.MagneticStormAlphaAMBonus",
"class": "MagneticStormAlphaAMBonus"
},
"Buff-武器-精2「电磁暴」-壹式-异常掌控": {
"module": ".BuffXLogic.MagneticStormAlphaAMBonus",
"class": "MagneticStormAlphaAMBonus"
},
"Buff-武器-精3「电磁暴」-壹式-异常掌控": {
"module": ".BuffXLogic.MagneticStormAlphaAMBonus",
"class": "MagneticStormAlphaAMBonus"
},
"Buff-武器-精4「电磁暴」-壹式-异常掌控": {
"module": ".BuffXLogic.MagneticStormAlphaAMBonus",
"class": "MagneticStormAlphaAMBonus"
},
"Buff-武器-精5「电磁暴」-壹式-异常掌控": {
"module": ".BuffXLogic.MagneticStormAlphaAMBonus",
"class": "MagneticStormAlphaAMBonus"
},
"Buff-武器-精2「电磁暴」-贰式-异常精通": {
"module": ".BuffXLogic.MagneticStormBravoApBonus",
"class": "MagneticStormBravoApBonus"
},
"Buff-武器-精3「电磁暴」-贰式-异常精通": {
"module": ".BuffXLogic.MagneticStormBravoApBonus",
"class": "MagneticStormBravoApBonus"
},
"Buff-武器-精4「电磁暴」-贰式-异常精通": {
"module": ".BuffXLogic.MagneticStormBravoApBonus",
"class": "MagneticStormBravoApBonus"
},
"Buff-武器-精5「电磁暴」-贰式-异常精通": {
"module": ".BuffXLogic.MagneticStormBravoApBonus",
"class": "MagneticStormBravoApBonus"
},
"Buff-武器-精1「电磁暴」-叁式-回能": {
"module": ".BuffXLogic.MagneticStormCharlieSpRecover",
"class": "MagneticStormCharlieSpRecover"
},
"Buff-武器-精2「电磁暴」-叁式-回能": {
"module": ".BuffXLogic.MagneticStormCharlieSpRecover",
"class": "MagneticStormCharlieSpRecover"
},
"Buff-武器-精3「电磁暴」-叁式-回能": {
"module": ".BuffXLogic.MagneticStormCharlieSpRecover",
"class": "MagneticStormCharlieSpRecover"
},
"Buff-武器-精4「电磁暴」-叁式-回能": {
"module": ".BuffXLogic.MagneticStormCharlieSpRecover",
"class": "MagneticStormCharlieSpRecover"
},
"Buff-武器-精5「电磁暴」-叁式-回能": {
"module": ".BuffXLogic.MagneticStormCharlieSpRecover",
"class": "MagneticStormCharlieSpRecover"
},
"Buff-角色-雨果-核心被动-单击破攻击力": {
"module": ".BuffXLogic.HugoCorePassiveSingleStunAtkBonus",
"class": "HugoCorePassiveSingleStunAtkBonus"
},
"Buff-角色-雨果-核心被动-双击破攻击力": {
"module": ".BuffXLogic.HugoCorePassiveDoubleStunAtkBonus",
"class": "HugoCorePassiveDoubleStunAtkBonus"
},
"Buff-角色-雨果-决算触发器": {
"module": ".BuffXLogic.HugoCorePassiveTotalizeTrigger",
"class": "HugoCorePassiveTotalizeTrigger"
},
"Buff-角色-雨果-核心被动-强化E失衡值提升": {
"module": ".BuffXLogic.HugoCorePassiveEXStunBonus",
"class": "HugoCorePassiveEXStunBonus"
},
"Buff-角色-雨果-额外能力-连携技对普通敌人伤害提升": {
"module": ".BuffXLogic.HugoAdditionalAbilityExtraQTEDmgBonus",
"class": "HugoAdditionalAbilityExtraQTEDmgBonus"
},
"Buff-驱动盘-激素朋克-全局攻击力": {
"module": ".BuffXLogic.HormonePunkAtkBonus",
"class": "HormonePunkAtkBonus"
},
"Buff-武器-精1防暴者Ⅵ型-普攻增伤": {
"module": ".BuffXLogic.RiotSuppressorMarkVI",
"class": "RiotSuppressorMarkVI"
},
"Buff-武器-精2防暴者Ⅵ型-普攻增伤": {
"module": ".BuffXLogic.RiotSuppressorMarkVI",
"class": "RiotSuppressorMarkVI"
},
"Buff-武器-精3防暴者Ⅵ型-普攻增伤": {
"module": ".BuffXLogic.RiotSuppressorMarkVI",
"class": "RiotSuppressorMarkVI"
},
"Buff-武器-精4防暴者Ⅵ型-普攻增伤": {
"module": ".BuffXLogic.RiotSuppressorMarkVI",
"class": "RiotSuppressorMarkVI"
},
"Buff-武器-精5防暴者Ⅵ型-普攻增伤": {
"module": ".BuffXLogic.RiotSuppressorMarkVI",
"class": "RiotSuppressorMarkVI"
},
"Buff-武器-精1残心青囊-条件暴击率": {
"module": ".BuffXLogic.ZanshinHerbCase",
"class": "ZanshinHerbCase"
},
"Buff-武器-精2残心青囊-条件暴击率": {
"module": ".BuffXLogic.ZanshinHerbCase",
"class": "ZanshinHerbCase"
},
"Buff-武器-精3残心青囊-条件暴击率": {
"module": ".BuffXLogic.ZanshinHerbCase",
"class": "ZanshinHerbCase"
},
"Buff-武器-精4残心青囊-条件暴击率": {
"module": ".BuffXLogic.ZanshinHerbCase",
"class": "ZanshinHerbCase"
},
"Buff-武器-精5残心青囊-条件暴击率": {
"module": ".BuffXLogic.ZanshinHerbCase",
"class": "ZanshinHerbCase"
},
"Buff-武器-精1心弦夜响-无视火抗": {
"module": ".BuffXLogic.HeartstringNocturne",
"class": "HeartstringNocturne"
},
"Buff-武器-精2心弦夜响-无视火抗": {
"module": ".BuffXLogic.HeartstringNocturne",
"class": "HeartstringNocturne"
},
"Buff-武器-精3心弦夜响-无视火抗": {
"module": ".BuffXLogic.HeartstringNocturne",
"class": "HeartstringNocturne"
},
"Buff-武器-精4心弦夜响-无视火抗": {
"module": ".BuffXLogic.HeartstringNocturne",
"class": "HeartstringNocturne"
},
"Buff-武器-精5心弦夜响-无视火抗": {
"module": ".BuffXLogic.HeartstringNocturne",
"class": "HeartstringNocturne"
},
"Buff-武器-精1街头巨星-终结技增伤": {
"module": ".BuffXLogic.StreetSuperstar",
"class": "StreetSuperstar"
},
"Buff-武器-精2街头巨星-终结技增伤": {
"module": ".BuffXLogic.StreetSuperstar",
"class": "StreetSuperstar"
},
"Buff-武器-精3街头巨星-终结技增伤": {
"module": ".BuffXLogic.StreetSuperstar",
"class": "StreetSuperstar"
},
"Buff-武器-精4街头巨星-终结技增伤": {
"module": ".BuffXLogic.StreetSuperstar",
"class": "StreetSuperstar"
},
"Buff-武器-精5街头巨星-终结技增伤": {
"module": ".BuffXLogic.StreetSuperstar",
"class": "StreetSuperstar"
},
"Buff-武器-精1强音热望-额外攻击力加成": {
"module": ".BuffXLogic.MarcatoDesireAtkBonus",
"class": "MarcatoDesireAtkBonus"
},
"Buff-武器-精2强音热望-额外攻击力加成": {
"module": ".BuffXLogic.MarcatoDesireAtkBonus",
"class": "MarcatoDesireAtkBonus"
},
"Buff-武器-精3强音热望-额外攻击力加成": {
"module": ".BuffXLogic.MarcatoDesireAtkBonus",
"class": "MarcatoDesireAtkBonus"
},
"Buff-武器-精4强音热望-额外攻击力加成": {
"module": ".BuffXLogic.MarcatoDesireAtkBonus",
"class": "MarcatoDesireAtkBonus"
},
"Buff-武器-精5强音热望-额外攻击力加成": {
"module": ".BuffXLogic.MarcatoDesireAtkBonus",
"class": "MarcatoDesireAtkBonus"
},
"Buff-武器-精1加农转子-附加伤害触发器": {
"module": ".BuffXLogic.CannonRotor",
"class": "CannonRotor"
},
"Buff-武器-精2加农转子-附加伤害触发器": {
"module": ".BuffXLogic.CannonRotor",
"class": "CannonRotor"
},
"Buff-武器-精3加农转子-附加伤害触发器": {
"module": ".BuffXLogic.CannonRotor",
"class": "CannonRotor"
},
"Buff-武器-精4加农转子-附加伤害触发器": {
"module": ".BuffXLogic.CannonRotor",
"class": "CannonRotor"
},
"Buff-武器-精5加农转子-附加伤害触发器": {
"module": ".BuffXLogic.CannonRotor",
"class": "CannonRotor"
},
"Buff-武器-精1「月相」-朔-回能触发器": {
"module": ".BuffXLogic.LunarNoviluna",
"class": "LunarNoviluna"
},
"Buff-武器-精2「月相」-朔-回能触发器": {
"module": ".BuffXLogic.LunarNoviluna",
"class": "LunarNoviluna"
},
"Buff-武器-精3「月相」-朔-回能触发器": {
"module": ".BuffXLogic.LunarNoviluna",
"class": "LunarNoviluna"
},
"Buff-武器-精4「月相」-朔-回能触发器": {
"module": ".BuffXLogic.LunarNoviluna",
"class": "LunarNoviluna"
},
"Buff-武器-精5「月相」-朔-回能触发器": {
"module": ".BuffXLogic.LunarNoviluna",
"class": "LunarNoviluna"
},
"Buff-角色-仪玄-额外能力-对失衡敌人增伤": {
"module": ".BuffXLogic.YixuanAdditionalAbilityDmgBonus",
"class": "YixuanAdditionalAbilityDmgBonus"
},
"Buff-角色-仪玄-1画-落雷触发器": {
"module": ".BuffXLogic.YixuanCinema1Trigger",
"class": "YixuanCinema1Trigger"
},
"Buff-角色-仪玄-2画-失衡时间提升": {
"module": ".BuffXLogic.YixuanCinema2StunTimeLimitBonus",
"class": "YixuanCinema2StunTimeLimitBonus"
},
"Buff-武器-精1青溟笼舍-以太伤害提升": {
"module": ".BuffXLogic.QingmingBirdcageCompanionEthDmgBonus",
"class": "QingmingBirdcageCompanionEthDmgBonus"
},
"Buff-武器-精2青溟笼舍-以太伤害提升": {
"module": ".BuffXLogic.QingmingBirdcageCompanionEthDmgBonus",
"class": "QingmingBirdcageCompanionEthDmgBonus"
},
"Buff-角色-仪玄-4画-静心": {
"module": ".BuffXLogic.YixuanCinema4Tranquility",
"class": "YixuanCinema4Tranquility"
},
"Buff-武器-精3青溟笼舍-以太伤害提升": {
"module": ".BuffXLogic.QingmingBirdcageCompanionEthDmgBonus",
"class": "QingmingBirdcageCompanionEthDmgBonus"
},
"Buff-武器-精4青溟笼舍-以太伤害提升": {
"module": ".BuffXLogic.QingmingBirdcageCompanionEthDmgBonus",
"class": "QingmingBirdcageCompanionEthDmgBonus"
},
"Buff-武器-精5青溟笼舍-以太伤害提升": {
"module": ".BuffXLogic.QingmingBirdcageCompanionEthDmgBonus",
"class": "QingmingBirdcageCompanionEthDmgBonus"
},
"Buff-武器-精1青溟笼舍-贯穿伤害提升": {
"module": ".BuffXLogic.QingmingBirdcageCompanionSheerAtkBonus",
"class": "QingmingBirdcageCompanionSheerAtkBonus"
},
"Buff-武器-精2青溟笼舍-贯穿伤害提升": {
"module": ".BuffXLogic.QingmingBirdcageCompanionSheerAtkBonus",
"class": "QingmingBirdcageCompanionSheerAtkBonus"
},
"Buff-武器-精3青溟笼舍-贯穿伤害提升": {
"module": ".BuffXLogic.QingmingBirdcageCompanionSheerAtkBonus",
"class": "QingmingBirdcageCompanionSheerAtkBonus"
},
"Buff-武器-精4青溟笼舍-贯穿伤害提升": {
"module": ".BuffXLogic.QingmingBirdcageCompanionSheerAtkBonus",
"class": "QingmingBirdcageCompanionSheerAtkBonus"
},
"Buff-武器-精5青溟笼舍-贯穿伤害提升": {
"module": ".BuffXLogic.QingmingBirdcageCompanionSheerAtkBonus",
"class": "QingmingBirdcageCompanionSheerAtkBonus"
},
"Buff-驱动盘-云岿如我-四件套-贯穿伤害提升": {
"module": ".BuffXLogic.YunkuiTalesSheerAtkBonus",
"class": "YunkuiTalesSheerAtkBonus"
},
"Buff-武器-精1幻变魔方-强化E增伤": {
"module": ".BuffXLogic.PuzzleSphereExDmgBonus",
"class": "PuzzleSphereExDmgBonus"
},
"Buff-武器-精2幻变魔方-强化E增伤": {
"module": ".BuffXLogic.PuzzleSphereExDmgBonus",
"class": "PuzzleSphereExDmgBonus"
},
"Buff-武器-精3幻变魔方-强化E增伤": {
"module": ".BuffXLogic.PuzzleSphereExDmgBonus",
"class": "PuzzleSphereExDmgBonus"
},
"Buff-武器-精4幻变魔方-强化E增伤": {
"module": ".BuffXLogic.PuzzleSphereExDmgBonus",
"class": "PuzzleSphereExDmgBonus"
},
"Buff-武器-精5幻变魔方-强化E增伤": {
"module": ".BuffXLogic.PuzzleSphereExDmgBonus",
"class": "PuzzleSphereExDmgBonus"
},
"Buff-武器-精1「灰烬」-钴蓝-攻击力提升": {
"module": ".BuffXLogic.CinderCobaltAtkBonus",
"class": "CinderCobaltAtkBonus"
},
"Buff-武器-精2「灰烬」-钴蓝-攻击力提升": {
"module": ".BuffXLogic.CinderCobaltAtkBonus",
"class": "CinderCobaltAtkBonus"
},
"Buff-武器-精3「灰烬」-钴蓝-攻击力提升": {
"module": ".BuffXLogic.CinderCobaltAtkBonus",
"class": "CinderCobaltAtkBonus"
},
"Buff-武器-精4「灰烬」-钴蓝-攻击力提升": {
"module": ".BuffXLogic.CinderCobaltAtkBonus",
"class": "CinderCobaltAtkBonus"
},
"Buff-武器-精5「灰烬」-钴蓝-攻击力提升": {
"module": ".BuffXLogic.CinderCobaltAtkBonus",
"class": "CinderCobaltAtkBonus"
},
"Buff-角色-柚叶-甜蜜惊吓": {
"module": ".BuffXLogic.YuzuhaCorePassiveSweetScare",
"class": "YuzuhaCorePassiveSweetScare"
},
"Buff-角色-柚叶-硬糖射击触发器": {
"module": ".BuffXLogic.YuzuhaHardCandyShotTrigger",
"class": "YuzuhaHardCandyShotTrigger"
},
"Buff-角色-柚叶-彩糖花火积蓄值增加": {
"module": ".BuffXLogic.YuzuhaSugarBurstAnomalyBuildupBonus",
"class": "YuzuhaSugarBurstAnomalyBuildupBonus"
},
"Buff-角色-柚叶-彩糖花火·极积蓄值增加": {
"module": ".BuffXLogic.YuzuhaSugarBurstMaxAnomalyBuildupBonus",
"class": "YuzuhaSugarBurstMaxAnomalyBuildupBonus"
},
"Buff-角色-柚叶-核心被动-狸之愿-攻击力": {
"module": ".BuffXLogic.YuzuhaTanukiWishAtkBonus",
"class": "YuzuhaTanukiWishAtkBonus"
},
"Buff-角色-柚叶-组队被动-积蓄值增幅": {
"module": ".BuffXLogic.YuzuhaAdditionalAbilityAnomalyBuildupBonus",
"class": "YuzuhaAdditionalAbilityAnomalyBuildupBonus"
},
"Buff-角色-柚叶-组队被动-属性异常与紊乱伤害增幅": {
"module": ".BuffXLogic.YuzuhaAdditionalAbilityAnomalyDmgBonus",
"class": "YuzuhaAdditionalAbilityAnomalyDmgBonus"
},
"Buff-角色-柚叶-1画-全属性伤害抗性降低": {
"module": ".BuffXLogic.YuzuhaCinem1EleResReduce",
"class": "YuzuhaCinem1EleResReduce"
},
"Buff-角色-柚叶-2画-连携技触发器": {
"module": ".BuffXLogic.YuzuhaCinema2Trigger",
"class": "YuzuhaCinema2Trigger"
},
"Buff-角色-柚叶-4画-快支触发器": {
"module": ".BuffXLogic.YuzuhaCinema4QuickAssistTrigger",
"class": "YuzuhaCinema4QuickAssistTrigger"
},
"Buff-角色-柚叶-6画-炮弹触发器": {
"module": ".BuffXLogic.YuzuhaCinema6SheelTrigger",
"class": "YuzuhaCinema6SheelTrigger"
},
"Buff-角色-柚叶-6画-彩糖花火极触发器": {
"module": ".BuffXLogic.YuzuhaCinema6SugarBurstMaxTrigger",
"class": "YuzuhaCinema6SugarBurstMaxTrigger"
},
"Buff-武器-精1狸法七变化-全队异常精通": {
"module": ".BuffXLogic.MetanukiMorphosisAPBonus",
"class": "MetanukiMorphosisAPBonus"
},
"Buff-武器-精2狸法七变化-全队异常精通": {
"module": ".BuffXLogic.MetanukiMorphosisAPBonus",
"class": "MetanukiMorphosisAPBonus"
},
"Buff-武器-精3狸法七变化-全队异常精通": {
"module": ".BuffXLogic.MetanukiMorphosisAPBonus",
"class": "MetanukiMorphosisAPBonus"
},
"Buff-武器-精4狸法七变化-全队异常精通": {
"module": ".BuffXLogic.MetanukiMorphosisAPBonus",
"class": "MetanukiMorphosisAPBonus"
},
"Buff-武器-精5狸法七变化-全队异常精通": {
"module": ".BuffXLogic.MetanukiMorphosisAPBonus",
"class": "MetanukiMorphosisAPBonus"
},
"Buff-角色-爱丽丝-额外能力-异常掌控转精通": {
"module": ".BuffXLogic.AliceAdditionalAbilityApBonus",
"class": "AliceAdditionalAbilityApBonus"
},
"Buff-角色-爱丽丝-极性强击触发器": {
"module": ".BuffXLogic.AlicePolarizedAssaultTrigger",
"class": "AlicePolarizedAssaultTrigger"
},
"Buff-角色-爱丽丝-影画-6画-额外攻击触发器": {
"module": ".BuffXLogic.AliceCinema6Trigger",
"class": "AliceCinema6Trigger"
},
"Buff-角色-席德-强袭": {
"module": ".BuffXLogic.SeedOnslaughtBonus",
"class": "SeedOnslaughtBonus"
},
"Buff-角色-席德-明攻": {
"module": ".BuffXLogic.SeedDirectStrikeBonus",
"class": "SeedDirectStrikeBonus"
},
"Buff-角色-席德-明攻触发器": {
"module": ".BuffXLogic.SeedDirectStrikeTrigger",
"class": "SeedDirectStrikeTrigger"
},
"Buff-角色-席德-围杀": {
"module": ".BuffXLogic.SeedBesiegeBonus",
"class": "SeedBesiegeBonus"
},
"Buff-角色-席德-额外能力-重击大招增伤无视电抗": {
"module": ".BuffXLogic.SeedAdditionalAbilityTrigger",
"class": "SeedAdditionalAbilityTrigger"
},
"Buff-角色-席德-影画-2画-围杀无视防御力": {
"module": ".BuffXLogic.SeedCinema2BesiegeIgnoreDefense",
"class": "SeedCinema2BesiegeIgnoreDefense"
},
"Buff-角色-席德-影画-4画-喧响效率与大招增伤": {
"module": ".BuffXLogic.SeedCinema4Bonus",
"class": "SeedCinema4Bonus"
},
"Buff-角色-席德-影画-6画-触发器": {
"module": ".BuffXLogic.SeedCinema6Trigger",
"class": "SeedCinema6Trigger"
},
"Buff-武器-精1机巧心种-常驻暴击": {
"module": ".BuffXLogic.CordisGerminaCritRateBonus",
"class": "CordisGerminaCritRateBonus"
},
"Buff-武器-精2机巧心种-常驻暴击": {
"module": ".BuffXLogic.CordisGerminaCritRateBonus",
"class": "CordisGerminaCritRateBonus"
},
"Buff-武器-精3机巧心种-常驻暴击": {
"module": ".BuffXLogic.CordisGerminaCritRateBonus",
"class": "CordisGerminaCritRateBonus"
},
"Buff-武器-精4机巧心种-常驻暴击": {
"module": ".BuffXLogic.CordisGerminaCritRateBonus",
"class": "CordisGerminaCritRateBonus"
},
"Buff-武器-精5机巧心种-常驻暴击": {
"module": ".BuffXLogic.CordisGerminaCritRateBonus",
"class": "CordisGerminaCritRateBonus"
},
"Buff-武器-精1机巧心种-电属性增伤": {
"module": ".BuffXLogic.CordisGerminaEleDmgBonus",
"class": "CordisGerminaEleDmgBonus"
},
"Buff-武器-精2机巧心种-电属性增伤": {
"module": ".BuffXLogic.CordisGerminaEleDmgBonus",
"class": "CordisGerminaEleDmgBonus"
},
"Buff-武器-精3机巧心种-电属性增伤": {
"module": ".BuffXLogic.CordisGerminaEleDmgBonus",
"class": "CordisGerminaEleDmgBonus"
},
"Buff-武器-精4机巧心种-电属性增伤": {
"module": ".BuffXLogic.CordisGerminaEleDmgBonus",
"class": "CordisGerminaEleDmgBonus"
},
"Buff-武器-精5机巧心种-电属性增伤": {
"module": ".BuffXLogic.CordisGerminaEleDmgBonus",
"class": "CordisGerminaEleDmgBonus"
},
"Buff-武器-精1机巧心种-普攻大招无视防御": {
"module": ".BuffXLogic.CordisGerminaSNAAndQIgnoreDefense",
"class": "CordisGerminaSNAAndQIgnoreDefense"
},
"Buff-武器-精2机巧心种-普攻大招无视防御": {
"module": ".BuffXLogic.CordisGerminaSNAAndQIgnoreDefense",
"class": "CordisGerminaSNAAndQIgnoreDefense"
},
"Buff-武器-精3机巧心种-普攻大招无视防御": {
"module": ".BuffXLogic.CordisGerminaSNAAndQIgnoreDefense",
"class": "CordisGerminaSNAAndQIgnoreDefense"
},
"Buff-武器-精4机巧心种-普攻大招无视防御": {
"module": ".BuffXLogic.CordisGerminaSNAAndQIgnoreDefense",
"class": "CordisGerminaSNAAndQIgnoreDefense"
},
"Buff-武器-精5机巧心种-普攻大招无视防御": {
"module": ".BuffXLogic.CordisGerminaSNAAndQIgnoreDefense",
"class": "CordisGerminaSNAAndQIgnoreDefense"
},
"Buff-驱动盘-拂晓生花-四件套-触发普攻增伤": {
"module": ".BuffXLogic.DawnsBloom4SetTriggerNADmgBonus",
"class": "DawnsBloom4SetTriggerNADmgBonus"
},
"Buff-驱动盘-月光骑士颂-全队增伤": {
"module": ".BuffXLogic.MoonlightLullabyAllTeamDmgBonus",
"class": "MoonlightLullabyAllTeamDmgBonus"
},
"Buff-角色-席德-影画-2画-无视防御触发器": {
"module": ".BuffXLogic.SeedCinema2BesiegeIgnoreDefenceTrigger",
"class": "SeedCinema2BesiegeIgnoreDefenceTrigger"
},
"Buff-角色-席德-围杀触发器": {
"module": ".BuffXLogic.SeedBesiegeBonusTrigger",
"class": "SeedBesiegeBonusTrigger"
},
"Buff-角色-席德-影画-4画-触发器": {
"module": ".BuffXLogic.SeedCinema4Trigger",
"class": "SeedCinema4Trigger"
}
}
================================================
FILE: zsim/sim_progress/Character/Alice.py
================================================
from math import floor
from typing import TYPE_CHECKING
from zsim.define import ALICE_REPORT
from .character import Character
from .utils.filters import _skill_node_filter
if TYPE_CHECKING:
from zsim.sim_progress.Preload import SkillNode
from zsim.simulator.simulator_class import Simulator
class Alice(Character):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.blade_etiquette: float = 300.0 # 剑仪
self.max_blade_etiquette: float = 300.0 # 最大剑仪值
self.victory_state_update_tick: int = 0 # 六画的决胜状态的更新时间
self.victory_state_duration: int = 1800 # 决胜状态持续时间
self.victory_state_activation_origin: list[str] = ["1401_SNA_3", "1401_Q"] # 决胜状态激活源
self.victory_state_attack_counter: int = 0 # 六画的决胜状态的剩余攻击次数
self.victory_state_max_attack_count: int = 6 # 六画的决胜状态的最大攻击次数
self.cinema_6_additional_attack_skill_tag: str = "1401_Cinema_6" # 6画额外攻击的技能tag
self._na_enhancement_state: bool = False # 强化平A可用状态
self.decibel = 1000.0 if self.cinema < 2 else 2000.0 # 爱丽丝喧响值
@property
def na_enhancement_state(self) -> bool:
"""强化平A是否可用"""
return self._na_enhancement_state
@na_enhancement_state.setter
def na_enhancement_state(self, value: bool) -> None:
"""强化平A状态的赋值函数"""
if not self._na_enhancement_state and not value:
raise ValueError(
"【爱丽丝时间警告】企图将强化平A状态从False切换到False,这意味着Preload在强化平A不可用的情况下放行了强化A5"
)
self._na_enhancement_state = value
@property
def victory_state(self) -> bool:
"""决胜状态是否处于激活状态"""
from zsim.simulator.simulator_class import Simulator
assert isinstance(self.sim_instance, Simulator), "角色未正确初始化,请检查函数"
# 攻击次数尚未耗尽,或是时间为0tick(未发生过更新),此时的决胜状态都判定为False
if self.victory_state_update_tick == 0 or self.victory_state_attack_counter == 0:
return False
else:
# 层数没耗尽时,才检查时间条件
return (
self.sim_instance.tick - self.victory_state_update_tick
< self.victory_state_duration
)
@property
def blade_etquitte_bar(self) -> int:
# 剑仪条格子数(向下取整)
return floor(self.blade_etiquette / 100)
def reset_myself(self):
# 重置能量、喧响值
self.sp: float = 40.0
self.decibel: float = 1000.0 if self.cinema < 2 else 2000.0
# 重置动态属性
self.dynamic.reset()
def special_resources(self, *args, **kwargs) -> None:
"""爱丽丝的特殊资源模块"""
from zsim.simulator.simulator_class import Simulator
assert isinstance(self.sim_instance, Simulator), "角色未正确初始化,请检查函数"
# 输入类型检查
skill_nodes: list[SkillNode] = _skill_node_filter(*args, **kwargs)
for node in skill_nodes:
if node.char_name == self.NAME:
# 6画情况下,优先更新决胜状态的相关参数
if self.cinema == 6:
if node.skill_tag in self.victory_state_activation_origin:
self.victory_state_update_tick = self.sim_instance.tick
self.victory_state_attack_counter = self.victory_state_max_attack_count
if ALICE_REPORT:
self.sim_instance.schedule_data.change_process_state()
print(
f"【爱丽丝事件】【6画】检测到爱丽丝释放了{node.skill.skill_text},激活了决胜状态"
)
# 更新强化A5状态
if node.skill_tag == "1401_NA_5_PLUS":
self.na_enhancement_state = False
if ALICE_REPORT:
self.sim_instance.schedule_data.change_process_state()
print(
f"【爱丽丝事件】爱丽丝成功释放了一次强化A5:{node.skill.skill_text},强化A5状态关闭"
)
# 更新剑仪值
self.update_blade_etiquette(update_obj=node)
else:
# 队友的skill_node判断;
pass
def update_blade_etiquette(self, update_obj: "SkillNode | float | int") -> None:
# 更新剑仪值的函数
from zsim.sim_progress.Preload import SkillNode
from zsim.simulator.simulator_class import Simulator
assert isinstance(self.sim_instance, Simulator), "角色未正确初始化,请检查函数"
if update_obj is None:
raise ValueError("【剑仪值更新警告】在调用剑仪值更新函数时,必须传入update_obj参数")
if isinstance(update_obj, SkillNode):
if update_obj.labels is None:
raise ValueError(
f"【剑仪值更新警告】传入的{update_obj.skill_tag}没有初始化label参数,请检查数据库"
)
if "blade_etiquette" not in update_obj.labels:
print(
f"【剑仪值更新警告】技能{update_obj.skill_tag}的label中不包含剑仪值,请检查数据库"
)
return
blade_etiquette = update_obj.labels.get("blade_etiquette")
elif isinstance(update_obj, float | int):
blade_etiquette = update_obj
assert isinstance(blade_etiquette, float | int), "剑仪值更新函数传入的参数类型错误"
self.blade_etiquette = min(self.max_blade_etiquette, self.blade_etiquette + blade_etiquette)
if blade_etiquette == 0:
return
if ALICE_REPORT:
self.sim_instance.schedule_data.change_process_state()
print(
f"【爱丽丝事件】爱丽丝 {'恢复' if blade_etiquette > 0 else '消耗'} 了 {abs(blade_etiquette):.2f} 点剑仪值,当前剑仪值为 {self.blade_etiquette:.2f}"
)
def POST_INIT_DATA(self, sim_instance: "Simulator"):
"""初始化爱丽丝的监听器组"""
listener_manager = sim_instance.listener_manager
# 组队被动激活时,初始化紊乱回剑仪值的监听器
if self.additional_abililty_active:
listener_manager.listener_factory(
listener_owner=self, initiate_signal="Alice_1", sim_instance=sim_instance
)
# 初始化本体固有监听器(紊乱倍率、物理积蓄效率)
for listener_id in ["Alice_2", "Alice_3", "Alice_4", "Alice_5"]:
listener_manager.listener_factory(
listener_owner=self, initiate_signal=listener_id, sim_instance=sim_instance
)
# 1画激活时,初始化1画监听器(减防Buff)
if self.cinema >= 1:
listener_manager.listener_factory(
listener_owner=self, initiate_signal="Alice_Cinema_1_A", sim_instance=sim_instance
)
listener_manager.listener_factory(
listener_owner=self, initiate_signal="Alice_Cinema_1_B", sim_instance=sim_instance
)
# 2画激活时,初始化2画监听器(紊乱伤害提升)
if self.cinema >= 2:
listener_manager.listener_factory(
listener_owner=self, initiate_signal="Alice_Cinema_2_A", sim_instance=sim_instance
)
def spawn_extra_attack(self) -> None:
"""6画额外攻击的接口,向Preload添加一次额外攻击事件,同时扣除一次使用次数"""
assert self.victory_state, "6画额外攻击接口调用时,决胜状态未激活,请检查前置判断逻辑"
assert self.sim_instance is not None, "角色未正确初始化,请检查函数"
from zsim.sim_progress.data_struct.SchedulePreload import schedule_preload_event_factory
preload_tick_list = [self.sim_instance.tick]
skill_tag_list = [self.cinema_6_additional_attack_skill_tag]
preload_data = self.sim_instance.preload.preload_data
schedule_preload_event_factory(
preload_tick_list=preload_tick_list,
skill_tag_list=skill_tag_list,
preload_data=preload_data,
sim_instance=self.sim_instance,
)
if ALICE_REPORT:
self.sim_instance.schedule_data.change_process_state()
print(
f"【爱丽丝事件】【6画】队友攻击命中,爱丽丝触发额外攻击!当前剩余额外攻击次数:{self.victory_state_attack_counter}"
)
# 保险起见,计数器在最后更新
self.victory_state_attack_counter -= 1
def personal_action_replace_strategy(self, action: str):
"""爱丽丝的个人动作替换策略,其核心是:尝试把NA_5替换为它的强化版本"""
if action == "1401_NA_5":
if self.na_enhancement_state:
return "1401_NA_5_PLUS"
return action
def get_resources(self, *args, **kwargs) -> tuple[str, int]:
return "剑仪格", self.blade_etquitte_bar
def get_special_stats(self, *args, **kwargs) -> dict[str | None, object | None]:
return {"剑仪值": self.blade_etiquette, "强化A5状态": self.na_enhancement_state}
================================================
FILE: zsim/sim_progress/Character/AstraYao.py
================================================
import math
from typing import TYPE_CHECKING
from zsim.define import ASTRAYAO_REPORT
from zsim.sim_progress.Buff import JudgeTools
from zsim.sim_progress.data_struct import schedule_preload_event_factory
from zsim.sim_progress.Preload.PreloadDataClass import PreloadData
from .character import Character
from .utils.filters import _skill_node_filter
if TYPE_CHECKING:
from zsim.sim_progress.Preload import SkillNode
class AstraYao(Character):
"""耀佳音的特殊资源模块"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.idyllic_cadenza = False # 咏叹华彩状态
self.chord_manager = ChordCoattackManager(self)
@property
def chord(self) -> int:
"""每拥有25点能量,耀嘉音将拥有1点[和弦]"""
return math.floor(self.sp / 25)
class Dynamic(Character.Dynamic):
"""
这里的所有修改,是为了让on_field属性能够完美适配咏叹华彩状态,
当咏叹华彩状态为True时,on_field属性永远返回True,
而当它为False时,on_field属性返回存储的值。
"""
def __init__(self, char_instantce: Character):
super().__init__(char_instantce)
self.character: "AstraYao" = char_instantce # type: ignore
self._on_field = False # 初始化父类的普通属性
@property
def on_field(self):
"""重写on_field属性,根据咏叹华彩状态返回不同值"""
if self.character.idyllic_cadenza: # 如果咏叹华彩状态为True
return True
return self._on_field # 否则返回存储的值
@on_field.setter
def on_field(self, value):
"""保留设置功能"""
self._on_field = value
def __update_idyllic_cadenza(self, skill_node: "SkillNode") -> None:
"""更新咏叹华彩状态"""
if skill_node.skill_tag in ["1311_E_A", "1311_QTE", "1311_Q"]:
self.idyllic_cadenza = True
elif "1311_NA_3" in skill_node.skill_tag:
self.idyllic_cadenza = False
def special_resources(self, *args, **kwargs) -> None:
# 输入类型检查
skill_nodes: list["SkillNode"] = _skill_node_filter(*args, **kwargs)
for node in skill_nodes:
self.__update_idyllic_cadenza(node)
pass
def get_resources(self) -> tuple[str, int]:
return "咏叹华彩", self.idyllic_cadenza
def get_special_stats(self, *args, **kwargs) -> dict[str | None, object | None]:
return {"和弦": self.chord}
"""================================分割线=================================="""
class ChordCoattackManager:
def __init__(self, char_instance: AstraYao):
self.char = char_instance
self.quick_assist_trigger_manager = self.QuickAssistTriggerManager(self.char)
self.chord_trigger = self.ChordTrigger(self)
class QuickAssistTriggerManager:
"""快速支援管理器"""
def __init__(self, char_instance: AstraYao):
self.char = char_instance
self.light_attack_trigger = self.BaseSingleTrigger(
self, cd=180 if self.char.cinema < 4 else 60
)
self.heavy_attack_trigger = self.BaseSingleTrigger(self, cd=60)
self.found_char_dict: dict[str, Character] = {}
self.preload_data: PreloadData | None = None
def update_myself(self, tick: int, event):
"""这个函数的作用是更新自身状态,并且尝试触发轻、重两种快速支援触发器。"""
from zsim.sim_progress.Load import LoadingMission
from zsim.sim_progress.Preload import SkillNode
if isinstance(event, LoadingMission):
skill_node = event.mission_node
elif isinstance(event, SkillNode):
skill_node = event
else:
return
# 处理loading_mission为None的情况
if skill_node.loading_mission is None:
skill_node.loading_mission = LoadingMission(skill_node)
skill_node.loading_mission.mission_start(tick, report=False)
# # 检查当前tick是否为命中tick
# if not skill_node.loading_mission.is_hit_now(tick):
# raise ValueError(
# f"在非命中tick {tick} 上调用了耀嘉音快支管理器中的update_myself方法,当前的命中tick列表为:{skill_node.loading_mission.mission_dict}"
# )
# 根据轻重攻击情况触发快速支援
if skill_node.loading_mission.is_heavy_hit(tick):
self.heavy_attack_trigger.try_active(tick, skill_node)
else:
self.light_attack_trigger.try_active(tick, skill_node)
class BaseSingleTrigger:
"""单个触发器类"""
def __init__(self, manager_instance, cd: int):
self.manager: ChordCoattackManager.QuickAssistTriggerManager = manager_instance
self.cd = cd
self.last_update_tick = 0
def is_ready(self, tick: int):
if self.last_update_tick == 0:
return True
if tick - self.last_update_tick >= self.cd:
return True
else:
return False
def determine_target_char(self, tick: int):
"""
根据当前角色和角色顺序确定目标角色,这里需要分两种情况讨论:
1、下一个角色是耀嘉音——跳过耀嘉音,直接触发下下位角色的快速支援,
2、下一个角色不是耀嘉音——正常触发下一位角色的快速支援。
"""
assert self.manager.preload_data is not None, "preload_data is not initialized"
_operating_node = self.manager.preload_data.get_on_field_node(tick)
all_name_order_box = self.manager.preload_data.load_data.all_name_order_box
if _operating_node is None:
raise ValueError("想要触发耀嘉音的快速支援,则当前场上必须存在角色!")
current_name_order = all_name_order_box[_operating_node.char_name]
if current_name_order[1] == "耀嘉音":
target = current_name_order[2]
else:
target = current_name_order[1]
# print(_operating_node.skill_tag, current_name_order, target)
return target
def __active(self, tick: int, skill_node):
"""触发快速支援!不包含CD判断,只包含触发逻辑。"""
if self.manager.preload_data is None:
if self.manager.char.sim_instance is None:
raise ValueError("sim_instance is None, cannot find preload_data")
self.manager.preload_data = JudgeTools.find_preload_data(
sim_instance=self.manager.char.sim_instance
)
if not isinstance(self.manager.preload_data, PreloadData):
raise TypeError("快速支援管理器无法找到PreloadData实例!")
self.last_update_tick = tick
target_char = self.determine_target_char(tick)
assert self.manager.preload_data.quick_assist_system is not None
self.manager.preload_data.quick_assist_system.force_active_quick_assist(
tick, skill_node, target_char
)
def try_active(self, tick: int, skill_node):
"""尝试触发快速支援!这是给外部调用的接口。"""
if self.manager.char.chord < 1 or not self.manager.char.idyllic_cadenza:
"""当耀嘉音的和弦数量不足、或不处于唱歌状态时,不予触发!"""
return False
if not self.is_ready(tick):
return
self.__active(tick, skill_node)
class ChordTrigger:
def __init__(self, manager_instance):
self.manager: ChordCoattackManager = manager_instance
self.preload_data: PreloadData | None = None
# 震音:Tremolo;音簇:Tone Clusters
self.tremolo_tick = 35 # 震音的总时长
self.tone_clusters_tick = 50 # 音簇的总时长
self.coattack_base_count = (
1 if not self.manager.char.additional_abililty_active else 2
) # 震音的基础轮次
self.c2_update_tick = 0
self.c2_trigger_cd = 180
self.core_passive_buff_index = "Buff-角色-耀佳音-核心被动-攻击力"
self.last_chord_update_tick = 0 # 上一次调用和弦构造函数的时间点!
self.tremolo_tag = "1311_E_EX_A"
self.free_tremolo_tag = "1311_E_EX_A_FREE"
self.tone_clusters_tag = "1311_E_EX_C"
def c2_ready(self, tick: int):
return tick - self.c2_update_tick >= self.c2_trigger_cd
def try_spawn_chord_coattack(self, tick: int, skill_node: "SkillNode | None"):
"""给外部的接口,尝试执行!"""
if self.manager.char.sp < 25:
return
self.coattack_active(tick, skill_node)
def coattack_active(self, tick: int, skill_node: "SkillNode | None"):
"""
给外部函数调用的接口,用于生成协同攻击。
如果有顺带传入skill_node参数,则意味着调用来自Buff系统的触发器,
此时,协同攻击预载行为往往伴随着耀嘉音核心被动攻击力Buff的刷新。
"""
c2_count = 1 if self.manager.char.cinema >= 2 else 0
loop_times = self.coattack_base_count + c2_count
self.__chord_group_spawn_loop(tick, loop_times)
if skill_node is not None:
self.__add_core_passive_buff(skill_node)
def __chord_group_spawn_loop(self, tick: int, loop_times: int):
"""
用于抛出成组的和弦攻击,每组动作包含1次震音、3次音簇。
并且可以进行重复执行,模仿耀嘉音技能模组中,多次触发的情况。
"""
if self.preload_data is None:
if self.manager.char.sim_instance is None:
raise ValueError("sim_instance is None, cannot find preload_data")
self.preload_data = JudgeTools.find_preload_data(
sim_instance=self.manager.char.sim_instance
)
if not isinstance(self.preload_data, PreloadData):
raise TypeError("和弦管理器无法找到PreloadData实例!")
priority_list = [-1, -1]
preload_tick = tick
for i in range(loop_times):
if i == 2:
if self.c2_ready(tick):
self.c2_update_tick = tick
else:
"""针对2画,这里需要注意内置CD的判断"""
continue
if i == 0:
skill_tag_list = [self.tremolo_tag, self.tone_clusters_tag]
else:
skill_tag_list = [self.free_tremolo_tag, self.tone_clusters_tag]
skill_preload_tick_list = [
preload_tick,
preload_tick + self.tremolo_tick,
]
preload_tick += self.tremolo_tick + self.tone_clusters_tick
if self.manager.char.sim_instance is None:
raise ValueError("sim_instance is None, cannot schedule event")
schedule_preload_event_factory(
skill_tag_list=skill_tag_list,
preload_tick_list=skill_preload_tick_list,
preload_data=self.preload_data,
apl_priority_list=priority_list,
sim_instance=self.manager.char.sim_instance,
)
def __add_core_passive_buff(self, skill_node: "SkillNode"):
"""在触发第一次震音的时刻,也会给角色上Buff"""
add_buff_list = [self.manager.char.NAME] + [skill_node.char_name]
benifit_list = list(set(add_buff_list))
from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy
if self.manager.char.sim_instance is None:
raise ValueError("sim_instance is None, cannot add buff")
buff_add_strategy(
self.core_passive_buff_index,
benifit_list=benifit_list,
sim_instance=self.manager.char.sim_instance,
)
if ASTRAYAO_REPORT:
assert self.manager.char.sim_instance is not None
self.manager.char.sim_instance.schedule_data.change_process_state()
print(f"核心被动触发器激活!为{benifit_list}添加了{self.core_passive_buff_index}!")
================================================
FILE: zsim/sim_progress/Character/Ellen.py
================================================
from typing import TYPE_CHECKING
from zsim.sim_progress.Report import report_to_log
from .character import Character
from .utils.filters import _skill_node_filter
if TYPE_CHECKING:
from zsim.sim_progress.Preload import SkillNode
class Ellen(Character):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.flash_freeze: int = 0
def special_resources(self, *args, **kwargs) -> None:
"""模拟艾莲的急冻充能"""
# 输入类型检查
skill_nodes: list[SkillNode] = _skill_node_filter(*args, **kwargs)
for node in skill_nodes:
if "1191" not in node.skill_tag:
continue
if node.skill_tag in ["1191_SNA_1", "1191_SNA_2", "1191_SNA_3"]:
self.flash_freeze -= 1
if self.flash_freeze < 0:
report_to_log(
f"[Character] 释放 {node.skill_tag} 时,{self.NAME}的急冻充能不足,请检查技能树"
)
if self.flash_freeze < 3:
if node.skill_tag in ["1191_E_EX", "1191_E_EX_A", "1191_RA_NFC"]:
self.flash_freeze += 1
report_to_log(f"[Character] {self.NAME}的急冻充能被更新为:{self.flash_freeze}")
if node.skill_tag == "1191_RA_FC":
self.flash_freeze += 3
report_to_log(f"[Character] {self.NAME}的急冻充能被更新为:{self.flash_freeze}")
self.flash_freeze = max(self.flash_freeze, 0)
self.flash_freeze = min(self.flash_freeze, 3)
def get_resources(self, *args, **kwargs) -> tuple[str | None, int | float | None]:
return "急冻充能", self.flash_freeze
================================================
FILE: zsim/sim_progress/Character/Hugo.py
================================================
from .character import Character
class Hugo(Character):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# 虽然雨果自身没有特殊资源,但是需要创建他的专属监听器
self.listener_creat = False
def special_resources(self, *args, **kwargs) -> None:
"""雨果的特殊资源模块"""
if not self.listener_creat:
assert self.sim_instance is not None
self.sim_instance.listener_manager.listener_factory(
listener_owner=self,
initiate_signal="Hugo",
sim_instance=self.sim_instance,
)
return
def get_resources(self) -> tuple[str | None, int | float | bool | None]:
return "特殊资源", 0.0
def get_special_stats(self, *args, **kwargs) -> dict[str | None, object | None]:
return {}
================================================
FILE: zsim/sim_progress/Character/Jane.py
================================================
from zsim.sim_progress.Preload import SkillNode
from .character import Character
from .utils.filters import _skill_node_filter
class Jane(Character):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.passion_stream: float = 0.0 # 狂热心流,0.0 ~ 100.0
self.passion_state: bool = False # 狂热状态
self.salchow_jump: int = 0 # 萨霍夫跳剩余次数
def __check_salchow_jump(self) -> None:
"""检查萨霍夫跳次数"""
max_jumps = 1 if self.cinema == 0 else 2
if self.salchow_jump > max_jumps:
self.salchow_jump = max_jumps
elif self.salchow_jump < 0:
raise RuntimeError("萨霍夫跳次数不能为负数")
def __reset_passion(self) -> None:
"""重置狂热"""
self.passion_stream = 0.0
self.passion_state = False
self.__check_salchow_jump()
def __get_into_passion_state(self) -> None:
"""进入狂热状态"""
self.passion_stream = 100
self.passion_state = True
self.salchow_jump += 1
self.__check_salchow_jump()
def __passion_core(
self, passion_get: float, passion_consume: float, passion_direct_add: float
) -> None:
"""狂热计算逻辑核心"""
self.passion_stream += passion_direct_add # 直接添加的狂热值,闪反、QTE、大招、萨霍夫跳等
if not self.passion_state:
# 非狂热心流状态下,结算狂热获得
self.passion_stream += passion_get
if self.passion_stream >= 100 - 1e-6:
self.__get_into_passion_state()
else:
# 狂热心流状态下,结算狂热消耗
self.passion_stream -= passion_consume
if self.passion_stream <= 1e-6:
self.__reset_passion()
def special_resources(self, *args, **kwargs) -> None:
"""模拟简的狂热心流"""
# 输入类型检查
skill_nodes: list[SkillNode] = _skill_node_filter(*args, **kwargs)
for node in skill_nodes:
if node.char_name != "简":
continue
if node.skill_tag == "1261_SNA_1":
self.salchow_jump -= 1
self.__check_salchow_jump()
labels: dict[str, list[str] | str | int | float] = (
node.labels if node.labels is not None else {}
)
passion_get: float = float(labels.get("passion_get", 0)) # type: ignore
passion_consume: float = float(labels.get("passion_consume", 0)) # type: ignore
passion_direct_add: float = float(labels.get("passion_direct_add", 0)) # type: ignore
self.__passion_core(passion_get, passion_consume, passion_direct_add)
# TODO 关于萨霍夫跳的第一段(1261_SNA_1)的拆分问题:
# 这一段攻击动作是可以提前停止的,
# 所以我在考虑要不要在数据库中对该动作进行拆分——就像青衣的 NA_3 一样,只记录最小单位。
# 因为开大抢队、或是即将到来的怪物进攻以及角色交互模块(估计在5月份我就一定会写),一定会涉及到各种动作的提前终止,
# 虽然就目前的结构来说,提前终止在伤害、积蓄、失衡端都是已经可以满足的(我给Load阶段留了接口,可以调用函数暴力删除某个正在进行的动作),
# 但是在特殊资源这一块,技能一旦传入就会拿到100%份额的心流恢复,这里和实战中提前打断萨霍夫跳的情况会有一定出入——这会干扰到后续APL对整个循环的递推。
# 当然,现阶段可以不做这个,让简每次都打完完整的萨霍夫跳
# 但是,类似于这种可重复的蓄力类动作的拆分,可以说是通用需求。在简这里碰到的问题,在其他角色那里一样会碰到,
# ---分割线---
# 如果要拆的话,显然就要涉及一个判断“1261_SNA_1的首次传入”的需求,
# 那可以看看char下面的lasting_node,那里的数据结构可以帮助你快速判断这个“首次传入”,就不需要在本地写一堆轮子了。
# if self.cinema == 6:
# pass
# TODO 六命后强击立刻进入狂热心流——由外部模块控制,调用接口强制启动心流即可,不需要在本函数中留接口。
def external_passion_change(self):
"""
外部强制开启心流状态的接口——主要为6画服务,尚未debug!!!这里只是留一个接口。
至于为何要单写一个函数,因为要调用的是“__”开头的私有方法,直接调用IDE会报错,按照格式调用又不美观,索性单写一个函数
"""
self.__get_into_passion_state()
def get_resources(self) -> tuple[str, float]:
return "狂热心流", self.passion_stream
def get_special_stats(self, *args, **kwargs) -> dict[str | None, object | None]:
"""获取简的特殊状态"""
return {
"狂热心流": self.passion_stream,
"狂热状态": self.passion_state,
"萨霍夫跳剩余次数": self.salchow_jump,
}
================================================
FILE: zsim/sim_progress/Character/Lighter.py
================================================
from zsim.sim_progress.Preload import SkillNode
from zsim.sim_progress.Report import report_to_log
from .character import Character
from .utils.filters import _skill_node_filter
class Lighter(Character):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.morale: int | float = 4000 # 士气初始40 整形为4000
self.last_tick: int = 0
def special_resources(self, *args, **kwargs) -> None:
"""
模拟莱特的士气机制
判断目前的时间,与上一次激活时做差,并更新士气值
确保士气值不超过100
将传入的skill_node消耗的能量转为士气值
需要消耗士气时对应扣除
"""
# 输入类型检查
skill_nodes: list[SkillNode] = _skill_node_filter(*args, **kwargs)
# 对输入的skill_node进行遍历
for node in skill_nodes:
# 累加逻辑
if self.morale < 10000:
# 消耗能量及时更新
sp_consume = node.skill.sp_consume
if sp_consume > 0:
self.morale += sp_consume * 26
# print(
# f"检测到队友强化E{node.skill_tag}:当前士气:{self.morale / 100:.2f}"
# )
if "1161" not in node.skill_tag:
continue
# 递减逻辑
if node.skill_tag == "1161_NA_5_SH_EX":
self.morale -= 1000
report_to_log(f"[Character] 莱特的士气消耗至 {self.morale / 100:.2f}")
elif node.skill_tag == "1161_NA_5_CoH_EX":
self.morale -= 9000
# print(f'检测到士气消耗动作,当前士气(处理前):{self.morale}(处理后):{self.morale / 100:.2f}')
# TODO:这里需要一个函数来控制“夹断”技能的数据。思路:\n
# 1、根据当前SkillNode类复制一个出来(__dict__),\n
# 2、根据资源消耗量算出缩放比例,\n
# 3、根据缩放比例修改新的复制SkillNode的所有数据。\n
# 4、传给下一个环节。
# FIXME: 20241208:
# 观察到莱特的士气貌似只有首轮具有阈值,次轮开始就失效了
report_to_log(f"[Character] 莱特的士气消耗至 {self.morale / 100:.2f}")
if self.morale < 0:
report_to_log(f"[Character] 莱特的士气消耗至 {self.morale / 100:.2f}, 请检查")
self.morale = 0
# 时间每 6 ticks 更新
assert self.sim_instance is not None
tick: int = self.sim_instance.tick
if tick is not None:
if (minus := tick - self.last_tick) >= 6:
self.morale += minus // 6 * 29 # 地板除保证整形对齐
self.last_tick = tick - minus % 6 # 求余以保证余数不计入本次计算
self.morale = min(self.morale, 10000)
def get_resources(self) -> tuple[str, float]:
return "士气", self.morale / 100
================================================
FILE: zsim/sim_progress/Character/Miyabi.py
================================================
from typing import Literal
from zsim.sim_progress.anomaly_bar import Disorder
from zsim.sim_progress.Preload import SkillNode
from zsim.sim_progress.Report import report_to_log
from .character import Character
from .utils.filters import _skill_node_filter
def _disorder_counter(*args, **kwargs) -> int:
"""用于计算输入中紊乱的次数"""
counter: int = 0
for arg in args:
if isinstance(arg, Disorder):
counter += 1
for value in kwargs.values():
if isinstance(value, Disorder):
counter += 1
return counter
class Miyabi(Character):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.last_tick: int | None = None
self.frosty: int = 3
def special_resources(self, *args, **kwargs) -> None:
"""模拟雅的落霜机制"""
# 输入类型检查
skill_nodes: list[SkillNode] = _skill_node_filter(*args, **kwargs)
for node in skill_nodes:
if "1091" not in node.skill_tag:
continue
if self.frosty <= 6:
if node.skill_tag in ["1091_E_EX_A_1", "1091_E_EX_B_1"]:
self.frosty += 2
elif node.skill_tag == "1091_Core_Passive" and not self._shatter_internal_cd():
"""
霜灼·破的产生,在冰焰buff的exist逻辑里。
在exist逻辑返回True之前,会把霜灼破扔给special_resources以及eventlist
"""
self.frosty += 1
elif node.skill_tag == "1091_Q":
self.frosty += 3
else:
self.frosty = 6
if node.skill_tag == "1091_SNA_1":
self.frosty -= 2
elif node.skill_tag == "1091_SNA_2":
self.frosty -= 4
elif node.skill_tag == "1091_SNA_3":
self.frosty -= 6
if self.frosty < 0:
log = f"[Character] {self.NAME}的落霜不足,被消耗至{self.frosty}点,已重置,请检查技能树"
print(log)
report_to_log(log)
self.frosty = 0
if self.frosty <= 6:
disorder_times = _disorder_counter(*args, **kwargs)
self.frosty += disorder_times * 2
self.frosty = min(self.frosty, 6)
def _shatter_internal_cd(self) -> bool:
"""判断落霜叠层是否处于CD"""
assert self.sim_instance is not None
tick: int = self.sim_instance.tick
if self.last_tick is None:
self.last_tick = tick
return False
if tick - self.last_tick < 60:
return True
else:
self.last_tick = tick
return False
def get_resources(self) -> tuple[Literal["落霜"], int]:
return "落霜", self.frosty
================================================
FILE: zsim/sim_progress/Character/Qingyi.py
================================================
from zsim.sim_progress.Preload import SkillNode
from .character import Character
from .utils.filters import _skill_node_filter
class Qingyi(Character):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.__MAX_VOLTAGE: int = 10000
self.__QUAN_VOLTAGE: float = self.__MAX_VOLTAGE / 100 * (1.3 if self.cinema >= 1 else 1)
self.__FLASH_THRESHOLD: float = self.__MAX_VOLTAGE * 0.75
self.VOLTAGE_MAP: dict = {
"1251_NA_3_NFC": self.__QUAN_VOLTAGE * 4.6875,
"1251_NA_3_FC": self.__QUAN_VOLTAGE * 4.6875 * 16,
"1251_SNA": self.__QUAN_VOLTAGE * 1.94,
"1251_NA_4": self.__QUAN_VOLTAGE * 7.7,
"1251_CA": self.__QUAN_VOLTAGE * 16.08,
"1251_BH_Aid": self.__QUAN_VOLTAGE * 6.08,
"1251_Assault_Aid": self.__QUAN_VOLTAGE * 14.89,
"1251_E": self.__QUAN_VOLTAGE * 2.83,
"1251_E_EX_NFC": self.__QUAN_VOLTAGE * 22.29,
"1251_E_EX_FC": self.__QUAN_VOLTAGE * 30,
"1251_QTE": self.__QUAN_VOLTAGE * 25,
"1251_Q": self.__QUAN_VOLTAGE * 80,
}
self.flash_connect_voltage: float = (
0 if self.cinema == 0 else self.__MAX_VOLTAGE
) # 闪络电压,初始化为0
self.flash_connect: bool = False if self.cinema == 0 else True # 闪络状态
self.rush_attack_available_times: int = 5 # 醉花月云转-突进攻击可用次数
def special_resources(self, *args, **kwargs) -> None:
"""模拟青衣的闪络电压机制"""
skill_nodes: list[SkillNode] = _skill_node_filter(*args, **kwargs)
for node in skill_nodes:
# 闪络电压增加逻辑
if self.flash_connect_voltage < self.__MAX_VOLTAGE:
skill_tag = node.skill_tag
self.flash_connect_voltage += self.VOLTAGE_MAP.get(skill_tag, 0)
# 闪络电压不能超过最大值
self.flash_connect_voltage = min(self.flash_connect_voltage, self.__MAX_VOLTAGE)
# 闪络电压超过75%时,进入闪络状态
if self.flash_connect_voltage - self.__FLASH_THRESHOLD >= 1e-5:
self.flash_connect = True
self.rush_attack_available_times = 5
if self.flash_connect:
# 闪络状态执行逻辑
if node.skill_tag == "1251_SNA_1":
self.flash_connect_voltage = 0
self.rush_attack_available_times -= 1
self.flash_connect = False
else:
# 非闪络状态执行逻辑
if node.skill_tag == "1251_SNA_1":
# 醉花月云转-突进攻击可用次数减一
if self.rush_attack_available_times in [0, 5]:
print(
f"WTF APL is doing? rush_attack_available_times is {self.rush_attack_available_times}"
)
self.rush_attack_available_times -= 1
def get_resources(self, *args, **kwargs) -> tuple[str, float]:
result = self.flash_connect_voltage / self.__MAX_VOLTAGE * 100
return "闪络电压", result
def get_special_stats(self, *args, **kwargs) -> dict[str | None, object | None]:
return {
"闪络电压": self.flash_connect_voltage / self.__MAX_VOLTAGE, # 返回一个比例
"闪络状态": self.flash_connect,
"醉花月云转可用次数": self.rush_attack_available_times,
}
================================================
FILE: zsim/sim_progress/Character/Seed/ExStateManager.py
================================================
from typing import TYPE_CHECKING
from ....define import SEED_REPORT
from ..character import Character
if TYPE_CHECKING:
from zsim.sim_progress.Preload import SkillNode
class SeedEXState:
"""席德强化E释放的**当前**状态"""
IDLE = "idle" # 未开始强化E
FIRST_CAST = "first" # 第一段 (E_EX_0)
LOOPING = "looping" # 循环段 (E_EX_1)
INTRUPTED = "interrupted" # 中途打断 (E_EX_2)
FINISH = "finish" # 完整结束(SNA_1)
class SeedEXStateManager:
def __init__(self, char_instance: Character):
self.char = char_instance
assert self.char.NAME == "席德", (
f"SeedEXStateManager 仅支持席德, 当前角色为 {self.char.NAME}"
)
from . import Seed
assert type(self.char) is Seed
self.e_ex_max_repeat_times: int = 10 if self.char.cinema < 2 else 20
self.allowed_list = ["1461_E_EX_0", "1461_E_EX_1", "1461_E_EX_2", "1461_SNA_1"]
self._e_ex_state = SeedEXState.IDLE
self.repeat_count = 0 # 当前E_EX_1的重复次数
self.state_mapping = {
SeedEXState.IDLE: "1461_E_EX_0",
SeedEXState.FIRST_CAST: "1461_E_EX_1",
SeedEXState.LOOPING: "1461_E_EX_1",
SeedEXState.INTRUPTED: "1461_E_EX_0",
SeedEXState.FINISH: "1461_SNA_1",
}
self.cinema_2_buff_index = "Buff-角色-席德-影画-2画-耗能转化增伤"
@property
def e_ex_state(self) -> str:
return self._e_ex_state
@e_ex_state.setter
def e_ex_state(self, value: SeedEXState):
"""设置强化E释放的当前状态"""
self._e_ex_state = value
def update_ex_state(self, skill_node: "SkillNode"):
"""根据当前状态更新EX状态"""
# 由于非主动动作永远不可能在在强化E期间插入,但是额外伤害类技能是可以的,所以这里我们排除一下,避免干扰误触断言。
if skill_node.is_additional_damage:
return
if skill_node.skill_tag in self.allowed_list:
if skill_node.skill_tag == "1461_E_EX_0":
assert self.e_ex_state not in [SeedEXState.LOOPING, SeedEXState.FIRST_CAST], (
f"席德的强化E释放状态状态错误, 当前状态为 {self.e_ex_state}"
)
self.e_ex_state = SeedEXState.FIRST_CAST
self.repeat_count = 0 # 在检测到起手式时重置重复次数
elif skill_node.skill_tag == "1461_E_EX_1":
assert self.e_ex_state not in [
SeedEXState.IDLE,
SeedEXState.INTRUPTED,
SeedEXState.FINISH,
], f"席德的强化E释放状态状态错误, 当前状态为 {self.e_ex_state}"
assert self.repeat_count < self.e_ex_max_repeat_times, (
f"席德的强化E释放状态状态错误, 重复次数超过最大次数 {self.e_ex_max_repeat_times}"
)
self.repeat_count += 1
self.e_ex_state = (
SeedEXState.LOOPING
if self.repeat_count < self.e_ex_max_repeat_times
else SeedEXState.FINISH
)
elif skill_node.skill_tag == "1461_E_EX_2":
assert self.e_ex_state in [SeedEXState.LOOPING, SeedEXState.FIRST_CAST], (
f"席德的强化E释放状态状态错误, 当前状态为 {self.e_ex_state}"
)
assert self.repeat_count < self.e_ex_max_repeat_times, (
f"席德的强化E释放状态状态错误, 既然打出了E_EX_2就说明提前打断了强化E释放,此时释放次数({self.repeat_count}次)应小于最大次数{self.e_ex_max_repeat_times}"
)
# print(22222, f"{self.char.sim_instance.tick}tick:检测到1461_E_EX_2,强化E状态从{self.e_ex_state}切换到{SeedEXState.INTRUPTED}")
self.e_ex_state = SeedEXState.INTRUPTED
elif skill_node.skill_tag == "1461_SNA_1":
if self.e_ex_state == SeedEXState.IDLE:
# 若是传入了第一段重击,同时强化E状态为IDLE,说明此次SNA_1和强化E连段无关,直接返回
return
else:
assert self.e_ex_state in [SeedEXState.FINISH, SeedEXState.INTRUPTED], (
f"席德的强化E释放状态状态错误, 当前状态为 {self.e_ex_state}"
)
self.e_ex_state = SeedEXState.IDLE
if self.char.cinema >= 2:
if SEED_REPORT:
self.char.sim_instance.schedule_data.change_process_state()
print(
f"【席德2画报告】检测到强化E结束,本次强化E共耗能{self.repeat_count * 5:.0f}点,将为本次自动衔接的 {skill_node.skill.skill_text} 提供{self.repeat_count * 5:.0f}%的增伤!"
)
from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy
benefit_list = ["席德"]
buff_add_strategy(
self.cinema_2_buff_index,
benifit_list=benefit_list,
specified_count=self.repeat_count,
sim_instance=self.char.sim_instance,
)
else:
assert self.e_ex_state not in [SeedEXState.LOOPING, SeedEXState.FIRST_CAST], (
f"在传入其他无关技能时,席德的强化E状态处于未结算的情况,当前状态为{self.e_ex_state}"
)
if self.e_ex_state in [SeedEXState.FINISH, SeedEXState.INTRUPTED]:
self.e_ex_state = SeedEXState.IDLE
def action_replacement_handler(self, action: str):
"""根据当前强化E状态,转换为对应强化E技能index"""
state = self.e_ex_state
# 对于不是席德的动作,直接返回
if "1461" not in action:
return action
if action in ["1461_E_EX_0", "1461_E_EX_1", "1461_E_EX_2"]:
action = "1461_E_EX"
if action == "1461_E_EX":
result = self.state_mapping[state]
# print(f"{self.char.sim_instance.tick}tick:强化E状态为{state},指令{action}被替换为了{result}")
return result
else:
if state in [SeedEXState.LOOPING, SeedEXState.FIRST_CAST]:
# print(1111, f"在{self.char.sim_instance.tick}tick 指令{action}被替换为了1461_E_EX_2")
return "1461_E_EX_2"
else:
return action
================================================
FILE: zsim/sim_progress/Character/Seed/__init__.py
================================================
from typing import TYPE_CHECKING
from zsim.define import SEED_REPORT
from ..character import Character
from ..utils.filters import _skill_node_filter
from .ExStateManager import SeedEXStateManager
if TYPE_CHECKING:
from zsim.sim_progress.Preload import SkillNode
from zsim.simulator.simulator_class import Simulator
class Seed(Character):
def __init__(self, **kwargs):
"""
Seed的特殊机制
"""
super().__init__(**kwargs)
self._steel_charge: float = 60.0 if self.cinema < 1 else 100.0 # 钢能值
self.max_steel_charge: int = 150 # 最大钢能值
self.sna_steel_charge_cost: int = (
60 if self.cinema < 1 else 50
) # 落华·崩坠一式、二式消耗的钢能值
self.sp_to_steel_ratio: float = 0.5 # 技能能耗转换为钢能值的比例
self.Q_steel_charge_get: int = 60 if self.cinema < 1 else 80 # Q技能获得的钢能值
self.vanguard: Character | None = None # 正兵
self.sna_quick_release: bool = False # sna的快速释放状态
# 特殊状态组
self._onslaught: bool = False # 强袭状态
self._onslaught_active_tick: int = 0 # 强袭状态激活的tick
self._direct_strike: bool = False # 明攻状态
self._direct_strike_active_tick: int = 0 # 明攻状态激活的tick
self.special_state_duration: int = 2400 # 特殊状态持续时间`
self.state_keep_tick: int = 180 # 特殊状态在后台保持的tick
"""
注意,我并未设计某函数来赋予明攻、强袭状态为False值,因为整个模拟器的结构不太方便每个tick都来判断一次是否状态是否过期。
所以,这两个状态的赋值只会是赋予True值,而不会是赋予False值。
但是我设计了一个属性来记录这两个状态的激活tick,所以我可以在需要判断状态是否过期时,用当前tick减去激活tick,
如果大于特殊状态持续时间,那么就认为状态过期。
"""
self.sesm = SeedEXStateManager(char_instance=self)
@property
def onslaught(self) -> bool:
"""强袭状态"""
assert self.sim_instance is not None
if not self._onslaught:
return False
else:
tick = self.sim_instance.tick
if tick - self._onslaught_active_tick > self.special_state_duration:
return False
else:
return True
@property
def direct_strike(self) -> bool:
"""明攻状态"""
assert self.sim_instance is not None
if not self._direct_strike:
return False
else:
tick = self.sim_instance.tick
if tick - self._direct_strike_active_tick > self.special_state_duration:
return False
else:
return True
@onslaught.setter
def onslaught(self, value: bool) -> None:
"""强袭状态在检测到赋予True值时记录当前tick"""
assert self.sim_instance is not None
self._onslaught = value
if value:
self._onslaught_active_tick = self.sim_instance.tick
@direct_strike.setter
def direct_strike(self, value: bool) -> None:
"""明攻状态在检测到赋予True值时记录当前tick"""
assert self.sim_instance is not None
self._direct_strike = value
if value:
self._direct_strike_active_tick = self.sim_instance.tick
@property
def direct_strike_active(self) -> bool:
"""明攻状态是否生效:哪怕明攻状态激活,也会因为退场时间超过3秒而失效,所以必须单独判断"""
# 作为正兵的专属状态,若正兵本不存在,则直接返回False
if self.vanguard is None:
return False
# 当明攻状态为False时,直接返回False
if not self.direct_strike:
return False
# 当明攻状态为激活时,则需要判断正兵是否处于前台,或是退场的3秒以内。
# 若正兵处于前台,则直接返回True
if self.vanguard.dynamic.on_field:
return True
# 若正兵处于后台,则需要判断退场时间
# 当正兵退场时间小于等于3秒时,返回True
if self.vanguard.dynamic.is_off_field_within(max_ticks=self.state_keep_tick):
return True
# 当正兵退场时间大于3秒时,返回False
else:
return False
@property
def onslaught_active(self) -> bool:
"""强袭状态是否生效:哪怕强袭状态激活,也会因为退场时间超过3秒而失效,所以必须单独判断"""
# 当强袭状态为False时,直接返回False
if not self.onslaught:
return False
# 当强袭状态为激活时,则需要判断自己是否处于前台,或是退场的3秒以内。
# 若自己处于前台,则直接返回True
if self.dynamic.on_field:
return True
# 若自己处于后台,则需要判断退场时间
# 当自己退场时间小于等于3秒时,返回True
if self.dynamic.is_off_field_within(max_ticks=self.state_keep_tick):
return True
# 当自己退场时间大于3秒时,返回False
else:
return False
def besiege_active_check(self) -> tuple[bool, bool]:
"""
围杀状态是否生效:需要强袭状态和明攻状态同时生效,返回的是席德以及正兵的围杀状态,
围杀状态需要强袭状态和明攻状态同时生效,对于席德和正兵来说,围杀的生效判定条件不同。
对于席德来说,需要自身的强袭Buff处于生效,且正兵身上的明攻状态存在,就可以通过判定
对于正兵来说,需要自身的明攻Buff生效,且席德身上的强袭状态存在,就可以通过判定
"""
seed_besiege = self.onslaught_active and self.direct_strike
vanguard_besiege = self.direct_strike_active and self.onslaught
return seed_besiege, vanguard_besiege
@property
def besiege(self) -> bool:
"""围杀状态,当强袭状态和明攻状态同时为True时,为True"""
return self.onslaught and self.direct_strike
@property
def e_ex_repeat_limit_reached(self) -> bool:
"""强化E第一段的释放次数是否达到最大次数"""
if not self.dynamic.lasting_node.is_spamming:
return False
if self.dynamic.lasting_node.node.skill_tag != "1461_E_EX_1":
return False
result = self.dynamic.lasting_node.repeat_times == self.sesm.e_ex_max_repeat_times
return result
def special_resources(self, *args, **kwargs) -> None:
"""模拟Seed的特殊资源机制"""
assert self.sim_instance is not None
# 输入类型检查
skill_nodes: list[SkillNode] = _skill_node_filter(*args, **kwargs)
# 对输入的skill_node进行遍历
for node in skill_nodes:
# 更新能耗转化的钢能值
self.update_steel_charge_from_sp_cost(skill_node=node)
# 更新特殊状态
self.update_special_state(node)
if node.char_name == self.NAME:
# 更新强化E状态
self.sesm.update_ex_state(skill_node=node)
# SNA2和SNA3技能消耗钢能值
if node.skill_tag in ["1461_SNA_2", "1461_SNA_3"]:
self.update_steel_charge(
value=self.sna_steel_charge_cost * -1, update_origin=node.skill_tag
)
# Q技能获得钢能值
elif node.skill_tag == "1461_Q":
self.update_steel_charge(
value=self.Q_steel_charge_get, update_origin=node.skill_tag
)
if SEED_REPORT:
self.sim_instance.schedule_data.change_process_state()
print(
f"【席德事件】席德释放了 {node.skill_tag} 获得了{self.Q_steel_charge_get}点钢能值"
)
elif node.skill_tag == "1461_SNA_1":
# SNA_1处理
active_generation_node_stack = self.sim_instance.preload.preload_data.personal_active_generation_node_stack[
1461
]
latest_active_generation_node = active_generation_node_stack.peek()
# 首先确认是否存在刚好结束的强化E第一段,因为衔接在强化E后自动释放的SNA_1是不会消耗快速释放标记的
e_ex_is_just_end = False
if latest_active_generation_node is not None:
if all(
[
latest_active_generation_node.skill_tag == "1461_E_EX_1",
latest_active_generation_node.end_tick == self.sim_instance.tick,
]
):
e_ex_is_just_end = True
if not e_ex_is_just_end:
if self.sna_quick_release:
self.sna_quick_release = False
else:
print(
"【席德测试】检测到位于强化E后自动衔接释放的SNA_1,本次释放不会消耗快速释放标记!"
)
@property
def steel_charge(self) -> float:
return self._steel_charge
@steel_charge.setter
def steel_charge(self, value: int | float) -> None:
assert self.sim_instance is not None
# 检查钢能值足够的上升沿(支持1画和0画的不同门槛)
threshold = self.sna_steel_charge_cost * 2
if self._steel_charge < threshold <= self._steel_charge + value:
# 在检测到钢能值足够的上升沿时,打开sna的快速释放标记。
self.sna_quick_release = True
# if SEED_REPORT:
# self.sim_instance.schedule_data.change_process_state()
# print(f"【席德事件】钢能值达到门槛({threshold}点),开启SNA快速释放")
value = min(value, self.max_steel_charge)
self._steel_charge = value
def update_steel_charge(self, value: int | float, update_origin: str) -> None:
"""更新钢能值"""
if value < 0:
assert abs(value) <= self.steel_charge, (
f"{update_origin}企图消耗{abs(value):.2f}点钢能值,目前钢能值为{self.steel_charge:.2f}点,钢能值不足!"
)
self.steel_charge += value
@property
def steel_charge_enough(self) -> bool:
"""判断钢能值是否足够"""
result = self.steel_charge >= self.sna_steel_charge_cost * 2
return result
def update_special_resource(self, skill_node: "SkillNode"):
"""
从命中中更新钢能值,该函数的调用时机为命中后,
所以不在Character的special_resource内进行更新,而是在Schedule阶段调用。
"""
if skill_node.char_name != self.NAME:
return
if skill_node.labels is None:
return
if "steel_charge" in skill_node.labels:
total_value = skill_node.labels["steel_charge"]
assert isinstance(total_value, int | float)
value = total_value / skill_node.skill.hit_times
self.update_steel_charge(value=value, update_origin=skill_node.skill_tag)
def update_steel_charge_from_sp_cost(self, skill_node: "SkillNode"):
"""
因技能能耗而更新钢能值,但注意,只有正兵和席德本身的能耗才能转化为钢能值
"""
assert self.sim_instance is not None
# 筛选出正兵和席德本身的技能
if self.vanguard is not None:
if skill_node.char_name not in [self.NAME, self.vanguard.NAME]:
return
else:
if skill_node.char_name not in [self.NAME]:
return
sp_consume = skill_node.skill.sp_consume
if sp_consume == 0:
return
value = sp_consume * self.sp_to_steel_ratio
self.update_steel_charge(value=value, update_origin=f"{skill_node.skill_tag}能耗转化")
if SEED_REPORT:
self.sim_instance.schedule_data.change_process_state()
print(
f"【席德事件】{skill_node.skill_tag}消耗了{sp_consume:.2f}点能量,转化为{value:.2f}点钢能值"
)
def update_special_state(self, skill_node: "SkillNode"):
"""更新席德的特殊状态,这个函数有两个任务:
1、在正兵释放强化E时,开启强袭状态;
2、在席德释放强化E时,开启明攻状态;"""
if skill_node.skill.trigger_buff_level != 2:
return
if self.vanguard is None:
return
if skill_node.char_name == self.NAME:
self.direct_strike = True
else:
if skill_node.char_name == self.vanguard.NAME:
self.onslaught = True
def POST_INIT_DATA(self, sim_instance: "Simulator"):
"""在初始化阶段,席德需要通过基础攻击力来确认“正兵”人选,只有强攻类型的角色才能成为正兵"""
atk_box = []
vanguard = None
for char_obj in sim_instance.char_data.char_obj_list:
if not char_obj.specialty == "强攻":
continue
char_atk_outside = char_obj.statement.ATK
atk_box.append(char_atk_outside)
if char_atk_outside == max(atk_box):
vanguard = char_obj
else:
self.vanguard = vanguard
assert self.vanguard is not None
# 席德无法指定正兵为自己
if self.vanguard.NAME == self.NAME:
self.vanguard = None
else:
if SEED_REPORT:
sim_instance.schedule_data.change_process_state()
print(f"【席德事件】本次模拟中,席德找到的正兵为:{self.vanguard.NAME}!")
self.onslaught = True
self.direct_strike = True
if self.vanguard is None:
if SEED_REPORT:
sim_instance.schedule_data.change_process_state()
print("【席德事件】注意!席德并未在当前队伍里找到正兵!")
def reset_myself(self):
# 重置能量、喧响值
self.sp: float = 40.0
self.decibel: float = 1000.0
# 重置动态属性
self.dynamic.reset()
# 重置特殊状态
self._onslaught = False
self._onslaught_active_tick = 0
self._direct_strike = False
self._direct_strike_active_tick = 0
def personal_action_replace_strategy(self, action: str):
"""
席德的个人动作替换策略,根据强化E连段情况的不同,将传入的动作指令替换成不同的skill_tag
"""
return self.sesm.action_replacement_handler(action=action)
def get_resources(self, *args, **kwargs) -> tuple[str | None, int | float | None]:
return "钢能值", self.steel_charge
def get_special_stats(self, *args, **kwargs) -> dict[str | None, object | None]:
from .ExStateManager import SeedEXState
return {
"钢能值足够": self.steel_charge_enough,
"sna快速释放": self.sna_quick_release,
"强袭状态": self.onslaught,
"明攻状态": self.direct_strike,
"围杀状态": self.besiege,
"强袭状态生效": self.onslaught_active,
"明攻状态生效": self.direct_strike_active,
"正兵": self.vanguard.NAME if self.vanguard else None,
"强化E达到最大次数": self.e_ex_repeat_limit_reached,
"强化E连续释放": self.sesm.e_ex_state in [SeedEXState.LOOPING, SeedEXState.FIRST_CAST],
}
================================================
FILE: zsim/sim_progress/Character/Soldier0_Anby.py
================================================
from zsim.sim_progress.Preload import SkillNode
from .character import Character
from .utils.filters import _skill_node_filter
class Soldier0_Anby(Character):
"""大安比的银星机制"""
# TODO:把银星的逻辑写在Char里面是偷懒的做法!!!这本应该是一个debuff
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.silver_star: float = 0.0 # 银星
self.white_thunder_update_tick: int = 0 # 白雷更新时间
self.continuing_white_thunder_counter: int = 0 # 连续白雷的计数器
self.white_thunder_answer: bool = False # 白雷的触发响应器
self.thunder_smite_answer: bool = False # 雷殛的触发响应器
self.continuing_e: bool = True # E连击的触发响应器
self.c1_answer: bool = False # 1画的强化E白雷的触发响应器
self.c1_counter: int = 0 # 1画的额外白雷结算次数。
self.c2_counter: int = 0 # 2画的层数计数器
self.c6_counter: int = 0 # 6画计数器
self.c6_answer: bool = False # 6画的触发响应器
self.silver_star_basic_cost = 33.333333 # 单E的银星基本消耗
self.max_silver_star: float = 100.1 # 银星上限
self.silver_star_gain_dict: dict[str, float] = {
"1381_NA_1": 4.6875,
"1381_NA_2": 7.53472222,
"1381_NA_3": 15.12152778,
"1381_NA_4": 2.34375,
"1381_NA_5": 18.75,
"1381_E_B": 11.11,
"1381_E_EX": 100.1,
"1381_CA": 50.1,
"1381_QTE": 21.09375,
"1381_Q": 100.1,
"1381_BH_Aid": 6.25,
"1381_Assault_Aid": 6.25,
}
@property
def full(self):
if self.silver_star >= self.max_silver_star:
return True
else:
return False
def special_resources(self, *args, **kwargs) -> None:
skill_nodes: list[SkillNode] = _skill_node_filter(*args, **kwargs)
tick = kwargs.get("tick", 0)
for nodes in skill_nodes:
_skill_tag = nodes.skill_tag
# 1、筛去不是本角色的技能
if "1381" not in _skill_tag:
continue
# 2、处理传入的白雷、雷殛、6命
if _skill_tag == "1381_CoAttack":
self.__white_thunder_processor(tick)
elif _skill_tag == "1381_E_Aid":
self.__thunder_smite_processor()
elif _skill_tag == "1381_Cinema_6":
self.__cinema_6_filter()
# 3、处理消层的E
elif _skill_tag == "1381_E_A":
self.__azure_flash_processor()
# 4、处理其他技能——叠印记
else:
self.continuing_e = False
if _skill_tag == "1381_E_EX" and self.cinema >= 1:
self.c1_answer = True
if _skill_tag == "1381_Q" and self.cinema >= 2:
self.c2_counter += 6
if self.c2_counter > 6:
self.c2_counter = 6
self.silver_star += self.silver_star_gain_dict.get(nodes.skill_tag, 0.0)
# print(f'{_skill_tag}使银星层数增长了,当前层数为{self.silver_star:.2f}')
"""最大值检查"""
if self.silver_star > self.max_silver_star:
self.silver_star = self.max_silver_star
def __azure_flash_processor(self):
"""处理层数逻辑"""
self.__check_myself()
if self.full:
self.continuing_e = True
"""在解锁2画,并且有预存层数的时候,优先使用层数,并且照常激活白雷。"""
if self.cinema >= 2 and self.c2_counter > 0:
self.c2_counter -= 1
else:
self.silver_star -= self.silver_star_basic_cost
self.white_thunder_answer = True
if self.silver_star < 0:
print("银星扣过头了!")
self.silver_star = 0
def __thunder_smite_processor(self):
"""处理雷殛的函数。"""
if not self.thunder_smite_answer:
print("非法雷殛!在雷殛响应状态未开启的情况下,触发了雷殛!")
self.thunder_smite_answer = False
def __white_thunder_processor(self, tick):
"""针对白雷进行更新,包括来源判断、计数器更新、以及响应器更新。"""
if not self.white_thunder_answer and not self.c1_answer:
print(
"非法白雷!在零号·安比白雷响应状态未开启、1画的强化E状态也未开启 的情况下,触发了白雷!"
)
if self.cinema >= 1 and self.c1_answer:
self.c1_filter()
else:
self.c0_filter(tick)
# 白雷计数器层数更新结束后,对雷殛进行更新。
self.c6_updater()
self.__thunder_smite_active()
def c6_updater(self):
"""
更新6画逻辑
从文字描述上看,6画的添加行为应该属于协同攻击「白雷」的后置,但如果利用白雷的follow_up来进行c6的添加,那么就会导致以下问题:
c6添加的时间点为白雷6 结束时,在这个Tick,白雷6会在Preload阶段的ForceAddEngine中被识别到结束信号,
此时,ForceAddEngine会尝试读取白雷6的follow up以及对应的force_add_APL,
由于ForceAddEngine每个Tick只能进行一次ForceAdd添加,这就会导致C6的协同攻击和雷殛相互抢队,谁排在前面,就执行谁。
所以,在之前的debug中,我总能观察到C6的协同攻击被大幅度延后,以至于C6计数器都出现了问题。
在后来的更新中,我将C6等价为雷殛的后置技能,从而摆脱了抢队问题。
经验:同一个技能的多个follow up必须是互斥的,如果存在一个tick通过多个follow up 判定的可能,就要做特殊处理。否则一定会因为抢队出问题。
"""
if self.cinema == 6:
self.c6_counter += 1
if self.c6_counter >= 6:
self.c6_answer = True
self.c6_counter = 0
def c0_filter(self, tick):
"""通用逻辑"""
self.white_thunder_answer = False
"""给了3帧的时间宽限"""
if self.white_thunder_update_tick == 0:
self.continuing_white_thunder_counter += 1
elif tick - self.white_thunder_update_tick <= 30:
self.continuing_white_thunder_counter += 1
else:
"""超时则清空——这个分支就是“连续”的体现。"""
self.continuing_white_thunder_counter = 1
self.white_thunder_update_tick = tick
def c1_filter(self):
"""解锁1画时,检测到c1_answer开启时,无视白雷响应器状态直接更新白雷计数器,计数器到3,把1画的强化响应器关闭。"""
self.continuing_white_thunder_counter += 1
self.c1_counter += 1
if self.c1_counter >= 3:
self.c1_answer = False
self.c1_counter = 0
def __thunder_smite_active(self):
"""在白雷计数器更新后,尝试对雷殛激活状态进行更新。"""
if self.continuing_white_thunder_counter >= 3:
if self.thunder_smite_answer:
print("在未结算上一次雷殛的情况下,再次触发了雷殛!")
self.thunder_smite_answer = True
self.continuing_white_thunder_counter = 0
def __check_myself(self):
"""自检"""
if self.silver_star < self.silver_star_basic_cost and self.c2_counter == 0:
print("当前可用的银星层数不够,传入的操作企图触发白雷,请检查APL!")
if self.white_thunder_answer:
print("白雷响应状态仍保持开启的情况下,再次企图触发了白雷! 当前存在未结算的白雷!!")
def __cinema_6_filter(self):
if self.cinema != 6:
raise ValueError("在未激活6画的情况下,触发了6画的追加攻击!")
if not self.c6_answer:
print("在6画的追加攻击响应器未开启的情况下,触发了6画的追加攻击!")
self.c6_answer = False
def get_resources(self) -> tuple[str | None, int | float | bool | None]:
return (
"银星层数",
self.silver_star / self.silver_star_basic_cost + self.c2_counter,
)
def get_special_stats(self, *args, **kwargs) -> dict[str | None, object | None]:
return {
"白雷": self.white_thunder_answer,
"雷殛": self.thunder_smite_answer,
"6画状态": self.c6_answer,
"1画状态": self.c1_answer,
"白雷连击次数": self.continuing_white_thunder_counter,
"E连击": self.continuing_e,
"6画_白雷次数": self.c6_counter,
"1画_白雷次数": self.c1_counter,
"2画_电鸣": self.c2_counter,
"满层": self.full,
}
================================================
FILE: zsim/sim_progress/Character/Soldier11.py
================================================
from zsim.sim_progress.Preload import SkillNode
from .character import Character
from .utils.filters import _skill_node_filter
class Soldier11(Character):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.fire_suppression: int = 0 # 强制的火力镇压
self.settle_tick: int | None = None
def special_resources(self, *args, **kwargs) -> None:
"""模拟11号的火力镇压机制"""
# 输入类型检查
skill_nodes: list[SkillNode] = _skill_node_filter(*args, **kwargs)
assert self.sim_instance is not None
tick: int = self.sim_instance.tick
for node in skill_nodes:
if self.settle_tick is not None:
# 超时重置
if tick - self.settle_tick >= 480:
self.fire_suppression = 0
self.settle_tick = None
# 过滤非11号技能
if "1041" not in node.skill_tag:
continue
# 获取层数逻辑
if node.skill_tag in ["1041_E_EX", "1041_QTE", "1041_Q"]:
self.fire_suppression = 8
self.settle_tick = tick
# 消耗层数逻辑
if "SNA" in node.skill_tag and self.fire_suppression > 0:
self.fire_suppression -= 1
def get_resources(self, *args, **kwargs) -> tuple[str | None, int | float | None]:
return "火力镇压", self.fire_suppression
================================================
FILE: zsim/sim_progress/Character/Soukaku.py
================================================
from zsim.sim_progress.Preload import SkillNode
from zsim.sim_progress.Report import report_to_log
from .character import Character
from .utils.filters import _skill_node_filter
class Soukaku(Character):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.vortex: int = 0 # 涡流初始0点
def special_resources(self, *args, **kwargs) -> None:
"""模拟苍角的涡流机制"""
# 输入类型检查
skill_nodes: list[SkillNode] = _skill_node_filter(*args, **kwargs)
# 对输入的skill_node进行遍历
for node in skill_nodes:
if "1131" not in node.skill_tag:
continue
if self.vortex <= 3:
if node.skill_tag in [
"1131_E_EX_1",
"1131_E_EX_2",
"1131_E_EX_3",
"1131_QTE",
]:
self.vortex += 1
report_to_log(f"[Character] 苍角的涡流被更新为 {self.vortex}")
elif node.skill_tag == "1131_Q":
self.vortex = 3
report_to_log(f"[Character] 苍角的涡流被更新为 {self.vortex}")
# 这里不能 elif
if self.vortex >= 3:
if node.skill_tag in ["1131_E_EX_A"]:
"""
在20241227的更新中,由于APL中补全了展旗的逻辑,
现在展旗会正确衔接了,具体会触发衔接的场合有:
1、能量不够(<30)的1、2段强化E
2、QTE
3、Q
"""
self.vortex = 0
# BuffAddStrategy('Buff-角色-苍角-核心被动-2')
report_to_log(f"[Character] 苍角的涡流被更新为 {self.vortex}")
def get_resources(self, *args, **kwargs) -> tuple[str | None, int | float | None]:
return "涡流", self.vortex
================================================
FILE: zsim/sim_progress/Character/Trigger/AfterShockManager.py
================================================
from .TriggerCoordinatedSupportTrigger import CoordinatedSupportManager
class AfterShock:
"""协同攻击基类"""
def __init__(self, skill_tag: str, cd: int, mode: int = 0):
self.update_tick = 0
self.cd = cd
self.active_result = skill_tag
if mode == 0:
self.complex_cd_manager = None
else:
self.complex_cd_manager = self.ComplexCDManager()
def is_ready(self, skill_node, tick: int):
# TODO:先写一个临时的
if self.complex_cd_manager is None:
if self.update_tick == 0:
return True
if tick - self.update_tick >= self.cd:
return True
return False
else:
return self.complex_cd_manager.is_available(skill_node, tick)
def after_shock_happend(self, tick: int):
"""抛出aftershock的skill_tag,并且更新自身信息"""
self.update_tick = tick
return self.active_result
class ComplexCDManager:
"""这是强化协同攻击的复杂CD管理器"""
def __init__(self):
self.cdm_e = BasicCDManager()
self.cdm_q = BasicCDManager()
self.cdm_aid = BasicCDManager()
self.cdm_map = {2: self.cdm_e, 6: self.cdm_q, 9: self.cdm_aid}
def is_available(self, skill_node, tick: int) -> bool:
"""整个CD管理器的对外接口,用来更新、判断此次触发信号能否成功激发一次协同攻击。"""
cdm = self.cdm_map.get(skill_node.skill.trigger_buff_level, None)
if cdm is None:
raise ValueError("传入的skill_node与complex_cd_manager不匹配")
__available_result = cdm.update(tick)
return __available_result
class BasicCDManager:
def __init__(self):
self.cd = 1200
self.max_count = 2
self.count = 2
self.start_tick = 0
self.refresh_tick = 0
self.active = False
def refresh_myself(self, tick: int):
self.count = self.max_count
self.active = True
self.start_tick = tick
self.refresh_tick = tick + self.cd
def start_myself(self, tick: int):
self.refresh_myself(tick)
self.count -= 1
def update(self, tick: int) -> bool:
"""根据扳机的强化协同攻击的CD管理机制,写的等效逻辑。"""
if self.count == 2:
self.start_myself(tick)
return True
elif self.count == 1:
if tick >= self.refresh_tick:
self.start_myself(tick)
return True
else:
self.count -= 1
return True
elif self.count == 0:
if tick >= self.refresh_tick:
self.start_myself(tick)
return True
else:
pass
# print(
# "由于层数耗尽且尚未到达刷新时间,扳机并未成功触发本次强化协同!!"
# )
return False
class AfterShockManager:
"""协同攻击管理器, 服务于角色——扳机"""
def __init__(self, char_instance):
self.char = char_instance
normal_after_shock_cd = 180 if self.char.cinema < 1 else 120
self.normal_after_shock = AfterShock("1361_CoAttack_A", normal_after_shock_cd, mode=0)
self.strong_after_shock = AfterShock("1361_CoAttack_1", 300, mode=1)
self.coordinated_support_manager = CoordinatedSupportManager()
def spawn_after_shock(self, tick: int, loading_mission) -> str | None:
"""
根据传入的skill_node抛出对应的协同攻击,并且更新自身数据;
这个函数接口是为Buff阶段服务的!请不要在Preload以及char的special_resource阶段调用本函数
"""
if not self.char.is_available(tick):
return None
skill_node = loading_mission.mission_node
"""优先判断强协同攻击: 只有重击的最后一跳才能触发!"""
if tick - 1 < loading_mission.get_last_hit() <= tick and skill_node.skill.heavy_attack:
if skill_node.skill.trigger_buff_level in [2, 6, 9]:
if self.strong_after_shock.is_ready(skill_node, tick):
if self.char.get_resources()[1] >= 5:
if self.strong_after_shock.complex_cd_manager.is_available(
skill_node, tick
):
strong_after_shock_tag = self.strong_after_shock.after_shock_happend(
tick
)
self.char.update_purge(strong_after_shock_tag)
return strong_after_shock_tag
# else:
# print(f'决意值为{self.char.get_resources()[1]},无法触发强化协同攻击!')
"""其次判断协战状态的免费协同"""
free_after_shock = self.coordinated_support_manager.spawn_after_shock(tick)
if free_after_shock is not None:
return free_after_shock
"""最后,判断消耗决意值的普通协同"""
if skill_node.skill.trigger_buff_level in [0, 1, 3, 4, 7]:
if self.normal_after_shock.is_ready(skill_node, tick):
if self.char.get_resources()[1] >= 3:
normal_after_shock_tag = self.normal_after_shock.after_shock_happend(tick)
self.char.update_purge(normal_after_shock_tag)
return normal_after_shock_tag
# else:
# print(f'决意值为{self.char.get_resources()[1]},无法触发普通协同攻击!')
return None
================================================
FILE: zsim/sim_progress/Character/Trigger/TriggerCoordinatedSupportTrigger.py
================================================
class CoordinatedSupportManager:
"""协战状态管理器"""
def __init__(self):
self.coordinated_support = False
self.update_tick = 0
self.end_tick = 0
self.max_duration = 1200
self.max_count = 10
self.after_shock_tag = "1361_CoAttack_A"
self.count = 0
self.update_count_box = {2: 4, 6: 6}
self.update_tick_box = {2: 480, 6: 720}
def update_myself(self, tick: int, skill_node):
"""传入skill_node,更新自身状态,该函数只负责刷新协战状态,不负责层数减少。"""
tbl = skill_node.skill.trigger_buff_level
if tbl in [2, 6]:
self.refresh_myself(tbl, tick)
def refresh_myself(self, tbl, tick):
"""更新协战状态的函数
关于协战状态的结束时间(end_tick)的更新逻辑,这个比较复杂。
由于协战状态的池子中的剩余时间是可以叠加的,但又存在最大值。
如果用最无脑的写法,那应该写一个函数,每个tick更新一次所谓的“剩余时间”,但是这样做实在是太浪费计算资源了。
所以这里考虑了一个等效的算法,这个算法会在每次激活协战状态时,无脑更新start_tick和end_tick,
其中,end_tick的更新逻辑较为复杂,但是无论是哪一种情况,都可以表达为:
min(max(原结束时间 - 当期时间, 0) + 新增加的时间, 当前时间 + 最大持续时间) + 当前时间
图形理解:
情况1:新增时间∆t并未使Buff时间溢出
|---------|---------------|------------------------|-------------|
情况说明: tick_now 老end_tick ∆t 新end_tick 最大时间
情况2:新增时间∆t使得时间溢出
|---------|---------------|------------------------|-------------|
情况说明: tick_now 老end_tick ∆t 最大时间 新end_tick
情况3:新增发生前,状态早已结束
|---------|---------------|------------------------|---------------------------|
情况说明: 老end_tick tick_now ∆t 新end_tick 最大时间
纵观全部的触发情况,无外乎上面三种。而无论哪一种情况,公式都可以准确算出新end_tick的位置。"""
self.coordinated_support = True
self.update_tick = tick
end_tick_new = (
min(
max(self.end_tick - tick, 0) + self.update_tick_box[tbl],
tick + self.max_duration,
)
+ tick
)
self.end_tick = end_tick_new
self.count += self.update_count_box[tbl]
self.count = min(self.count, self.max_count)
# print(f'协战状态更新!当前层数:{self.count},结束时间{self.end_tick}, 当前剩余时间:{self.end_tick - tick}')
def is_active(self, tick: int):
"""检查自身Buff状态是否存在"""
if self.end_tick > tick:
if self.count > 0:
return True
return False
def end(self, tick: int):
"""Buff结束"""
# print(f'协战状态结束了,当前层数:{self.count},当前剩余时间:{self.end_tick - tick}')
self.coordinated_support = False
self.count = 0
self.update_tick = tick
def spawn_after_shock(self, tick: int) -> str | None:
"""生成协同攻击的函数"""
if self.is_active(tick):
self.count -= 1
# print(f'协战状态触发了一次协同攻击,剩余层数:{self.count},当前剩余时间:{self.end_tick - tick}')
if self.count <= 0:
self.end(tick)
return self.after_shock_tag
return None
================================================
FILE: zsim/sim_progress/Character/Trigger/__init__.py
================================================
from typing import TYPE_CHECKING
from ..character import Character
from ..utils.filters import _skill_node_filter
from .AfterShockManager import AfterShockManager
if TYPE_CHECKING:
from zsim.sim_progress.Preload import SkillNode
class Trigger(Character):
"""扳机的特殊资源"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.after_shock_manager = AfterShockManager(self)
self.purge = 0 # 决意值
self.max_purge = 100 if self.cinema < 1 else 125
self.purge_gain_ratio = 1 if self.cinema < 1 else 1.25
self.sniper_stance = False # 狙击姿态
def special_resources(self, *args, **kwargs) -> None:
"""
这个函数在preload阶段被调用,主要用于更新协战状态、以及自身的决意值;
而这里的更新主要是状态的刷新、持续时间的增加以及层数的增加,
至于抛出协同攻击、层数减少以及决意值消耗,则在Buff阶段进行执行,这边只要留好接口即可;
接口:应该是char.spawn_after_shock,对内调用self.after_shock_manager的相应方法。
"""
skill_nodes: list["SkillNode"] = _skill_node_filter(*args, **kwargs)
tick = kwargs.get("tick", 0)
for nodes in skill_nodes:
_skill_tag = nodes.skill_tag
# 1、筛去不是本角色的技能
if "1361" not in _skill_tag:
if nodes.active_generation:
self.sniper_stance = False
continue
# 2、处理传入的强化E、Q,更新协战状态
self.after_shock_manager.coordinated_support_manager.update_myself(tick, nodes)
if nodes.skill_tag in ["1361_SNA_1", "1361_SNA_2"]:
if not self.sniper_stance:
raise ValueError(f"在非狙击姿态的情况下传入了{nodes.skill_tag}")
purge_delta = 25 * self.purge_gain_ratio
self.purge += purge_delta
if self.purge > self.max_purge:
self.purge = self.max_purge
elif nodes.skill_tag == "1361_SNA_0":
if self.sniper_stance:
raise ValueError("在狙击姿态已经开启的情况下传入了1361_SNA_0")
self.sniper_stance = True
elif nodes.skill_tag == "1361_SNA_3":
if not self.sniper_stance:
raise ValueError("在狙击姿态已经关闭的情况下传入了1361_SNA_3")
self.sniper_stance = False
def update_purge(self, skill_tag):
"""在Buff阶段更新决意值的函数!"""
if skill_tag == "1361_CoAttack_A":
if self.purge < 3:
print(f"现有决意值不足以触发{skill_tag}!请检查函数逻辑!")
self.purge = self.purge - 3 if self.purge >= 3 else 0
elif skill_tag == "1361_CoAttack_1":
if self.purge < 5:
print(f"现有决意值不足以触发{skill_tag}!请检查函数逻辑!")
self.purge = self.purge - 5 if self.purge >= 5 else 0
def get_resources(self) -> tuple[str | None, int | float | bool | None]:
return "决意值", self.purge
def get_special_stats(self, *args, **kwargs) -> dict[str | None, object | None]:
return {"狙击姿态": self.sniper_stance}
================================================
FILE: zsim/sim_progress/Character/Vivian/FeatherManager.py
================================================
from zsim.define import VIVIAN_REPORT
from zsim.sim_progress.Preload import SkillNode
from ..character import Character
class FeatherManager:
"""薇薇安的羽毛管理器,飞羽、护羽存储、转化、消耗。"""
def __init__(self, char_instance: Character):
self.char = char_instance
self.flight_feather = 2 # 飞羽,进场初始化为4层
self.guard_feather = 0 if self.char.cinema < 4 else 5 # 护羽,初始化为0层
self.feather_max_count = 5 # 最大飞羽/护羽层数,默认为6层
self.co_attack_index = "1331_CoAttack_A"
self.c1_counter = 0 # 1 画计数器
def update_myself(self, skill_node: SkillNode = None, c6_signal: bool = None):
"""
用来更新羽毛的方法,被薇薇安的羽毛触发器调用,
该函数内部没有任何类型判断,所有的判断全部交给羽毛触发器的xjudge
"""
if skill_node is None and c6_signal is None:
raise ValueError(
"薇薇安羽毛管理器的update_myself函数必须传入skill_node或c6_singal参数中的一个!"
)
if c6_signal or skill_node.skill_tag == "1331_SNA_2":
self.trans_feather()
else:
self.gain_feather(skill_node)
def trans_feather(self):
"""将现有的飞羽全部转化成护羽:注意,飞羽转化为护羽的时间点为SNA_2的最后一跳,所以这里不能走特殊资源,只能从触发器走。"""
trans_count = self.flight_feather
self.guard_feather = min(self.guard_feather + trans_count, self.feather_max_count)
self.flight_feather = 0
if VIVIAN_REPORT:
self.char.sim_instance.schedule_data.change_process_state()
print(
f"羽毛管理器:羽毛转化!当前的护羽、飞羽数量为:{self.guard_feather, self.flight_feather}"
)
def gain_feather(self, skill_node: SkillNode):
"""
获得飞羽,飞羽的获得结算大多为技能的最后一跳,所以也需要从触发器走。
该函数内部没有任何类型判断,所有的判断全部交给羽毛触发器的xjudge
"""
if not skill_node.skill.labels:
return
if "flight_feather" not in skill_node.skill.labels.keys():
return
flight_feather_count = skill_node.labels["flight_feather"]
c6_feather = skill_node.labels.get("c6_feather", 0)
if self.char.cinema == 6:
flight_feather_count += c6_feather
self.flight_feather = min(
self.flight_feather + flight_feather_count, self.feather_max_count
)
if VIVIAN_REPORT:
self.char.sim_instance.schedule_data.change_process_state()
print(
f"羽毛管理器:获得{flight_feather_count}点羽毛!当前护、飞羽数量为:{self.guard_feather, self.flight_feather}"
)
def spawn_coattack(self) -> str | None:
"""尝试生成一次生花"""
if self.guard_feather > 0:
self.guard_feather -= 1
if self.char.cinema >= 1:
self.c1_counter += 1
if self.c1_counter >= 4:
self.flight_feather = min(self.flight_feather + 1, self.feather_max_count)
self.c1_counter -= 4
if VIVIAN_REPORT:
self.char.sim_instance.schedule_data.change_process_state()
print(
f"羽毛管理器:落羽生花完成结算!当前的护羽、飞羽数量为:{self.guard_feather, self.flight_feather}"
)
return self.co_attack_index
else:
return None
================================================
FILE: zsim/sim_progress/Character/Vivian/__init__.py
================================================
from typing import TYPE_CHECKING
from ..character import Character
from ..utils.filters import _skill_node_filter
from .FeatherManager import FeatherManager
if TYPE_CHECKING:
from zsim.sim_progress.Preload import SkillNode
class Vivian(Character):
"""薇薇安的特殊资源模块"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.feather_manager = FeatherManager(self) # 羽毛管理器(飞羽、护羽的获取、切换)
self.state_level = 0 # 状态等级,0是无状态,1是开伞,2是飘浮
@property
def noblewoman_state(self) -> bool: # 判定当前是否为开伞状态(淑女仪态)
return self.state_level == 1
@property
def fluttering_frock_state(self) -> bool: # 判定当前是否为飘浮状态(裙裾浮游)
return self.state_level == 2
def __check_node(self, skill_node: "SkillNode") -> None:
"""检查传入的SkillNode,是否符合当前的状态。"""
skill_tag = skill_node.skill_tag
if skill_tag not in ["1331_SNA_0", "1331_SNA_1", "1331_SNA_2"]:
return
if skill_tag == "1331_SNA_0":
if not self.fluttering_frock_state:
raise ValueError(
f"薇薇安的SNA_0的作用是从飘浮状态退回开伞状态,而当前状态等级为{self.state_level},无法释放{skill_tag}"
)
elif skill_tag == "1331_SNA_1":
if not self.noblewoman_state:
raise ValueError(
f"薇薇安的SNA_1只能在开伞状态下释放,而当前状态等级为{self.state_level},无法释放{skill_tag}"
)
elif skill_tag == "1331_SNA_2":
if not self.fluttering_frock_state:
raise ValueError(
f"薇薇安的SNA_2只能在飘浮状态下释放,而当前状态等级为{self.state_level},无法释放{skill_tag}"
)
def special_resources(self, *args, **kwargs) -> None:
# 输入类型检查
skill_nodes: list[SkillNode] = _skill_node_filter(*args, **kwargs)
for node in skill_nodes:
if "1331" not in node.skill_tag:
continue
self.__check_node(node)
if node.skill_tag in ["1331_SNA_0", "1331_BH_Aid", "1331_NA_4"]:
# 进入开伞状态
self.state_level = 1
elif node.skill_tag == "1331_SNA_2":
# 退回最初状态
self.state_level = 0
elif node.skill_tag in [
"1331_SNA_1",
"1331_E_EX",
"1331_CA",
"1331_QTE",
"1331_Q",
"1331_Assault_Aid",
]:
# 直接飘浮
self.state_level = 2
else:
return
def get_resources(self) -> tuple[str, int]:
return "护羽", self.feather_manager.guard_feather
def get_special_stats(self, *args, **kwargs) -> dict[str, int | float | bool]:
return {
"护羽数量": self.feather_manager.guard_feather,
"飞羽数量": self.feather_manager.flight_feather,
"裙裾浮游": self.fluttering_frock_state,
"淑女仪态": self.noblewoman_state,
}
================================================
FILE: zsim/sim_progress/Character/Yanagi/StanceManager.py
================================================
from zsim.sim_progress.Buff import find_tick
from zsim.sim_progress.Preload import SkillNode
class Shinrabanshou:
def __init__(self, cinema: int, char_instance):
self.max_duration = 900 if cinema < 6 else 1800
self.update_tick = 0
self.char = char_instance
def statement(self, tick: int):
"""查询 柳的森罗万象状态的方法"""
if self.update_tick == 0:
return False
if tick - self.update_tick >= self.max_duration:
return False
return True
@property
def active(self):
"""更新森罗万象的时间!"""
tick = find_tick(sim_instance=self.char.sim_instance)
return tick < self.update_tick + self.max_duration
class StanceManager:
"""柳的架势管理器"""
def __init__(self, char_instance):
self.char = char_instance
self.stance_jougen = True # 上弦状态,初始化时就是上弦
self.stance_kagen = False # 下弦状态
self.last_update_node = None # 上次导致架势管理器的数据发生更新的skill_node
self.shinrabanshou = Shinrabanshou(self.char.cinema, self.char) # 森罗万象管理器
self.ex_chain = False # 突刺连段状态,也可以理解为'是否正在释放强化E'
self.stance_changing_buff_index = "Buff-角色-柳-额外能力-积蓄效率"
def update_myself(self, skill_node: SkillNode):
"""接收skill_node,并且判断自身架势是否要改变;"""
skill_tag = skill_node.skill_tag
"""首先筛掉不是自己的skill_node"""
if "1221" not in skill_tag:
return
"""若传入的skill_node不能触发任何架势直接返回!"""
if skill_tag not in [
"1221_E",
"1221_E_A",
"1221_QTE",
"1221_Assault_Aid",
"1221_E_EX_1",
"1221_E_EX_2",
]:
return
"""若检测到强化E 突刺攻击,则需要分类讨论"""
if skill_tag == "1221_E_EX_1":
if (
self.last_update_node is not None
and self.last_update_node.skill_tag == "1221_E_EX_1"
):
"""当检测到上一个使架势管理器发生更新的技能是穿刺攻击时,直接返回"""
if not self.ex_chain:
raise ValueError(
"检测到中间段强化E的突刺攻击时,架势管理器的ex_chain未处于打开状态!"
)
return
else:
"""其余情况,穿刺攻击的上一个技能都不会是穿刺攻击,所以可以放行。改变架势 + 启动森罗万象"""
if self.ex_chain:
# raise ValueError(f'检测到首段强化E的突刺攻击时,架势管理器的ex_chain正处于打开状态!')
print("检测到首段强化E的突刺攻击时,架势管理器的ex_chain正处于打开状态!")
self.ex_chain = True
# print(f'强化E连段开始')
tick = find_tick(sim_instance=self.char.sim_instance)
self.shinrabanshou.update_tick = tick
self.last_update_node = skill_node
self.change_stance()
elif skill_tag == "1221_E_EX_2":
"""检测到强化E的下落攻击分支"""
if not self.ex_chain:
raise ValueError(
"检测到强化E下落攻击传入,但是架势管理器的ex_chain未处于打开状态!"
)
self.ex_chain = False
self.last_update_node = skill_node
# print(f'强化E连段结束')
else:
"""其余情况全部都执行一次架势切换"""
self.change_stance()
self.last_update_node = skill_node
def change_stance(self):
"""更新架势"""
if self.stance_jougen == self.stance_kagen:
raise ValueError("上弦、下弦状态不能同时为True或False!")
if self.stance_jougen:
self.stance_jougen = False
self.stance_kagen = True
else:
self.stance_jougen = True
self.stance_kagen = False
from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy
buff_add_strategy(self.stance_changing_buff_index, sim_instance=self.char.sim_instance)
@property
def stance_now(self):
"""返回当前的架势状态,True是上弦,False是下弦"""
if self.stance_jougen:
return True
elif self.stance_kagen:
return False
else:
raise ValueError("上弦、下弦状态不能同时为True或False!")
================================================
FILE: zsim/sim_progress/Character/Yanagi/__init__.py
================================================
from typing import TYPE_CHECKING
from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import NewAnomaly
from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy
from zsim.sim_progress.Preload import SkillNode
from ..character import Character
from ..utils.filters import (
_anomaly_filter,
_skill_node_filter,
)
from .StanceManager import StanceManager
if TYPE_CHECKING:
pass
class Yanagi(Character):
"""柳的特殊资源系统"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.stance_manager = StanceManager(self)
self.cinme_1_buff_index = "Buff-角色-柳-1画-洞悉"
self.cinema_4_buff_index = "Buff-角色-柳-4画-识破"
def special_resources(self, *args, **kwargs) -> None:
skill_nodes: list["SkillNode"] = _skill_node_filter(*args, **kwargs)
anomalies: list[NewAnomaly] = _anomaly_filter(*args, **kwargs)
# tick = kwargs.get('tick', 0)
for nodes in skill_nodes:
self.stance_manager.update_myself(nodes)
if self.cinema >= 1 and anomalies:
if self.sim_instance is not None:
buff_add_strategy(self.cinme_1_buff_index, sim_instance=self.sim_instance)
if self.cinema >= 4:
for _anomaly in anomalies:
if isinstance(_anomaly.activate_by, SkillNode):
if str(self.CID) in _anomaly.activate_by.skill_tag:
if self.sim_instance is not None:
buff_add_strategy(
self.cinema_4_buff_index, sim_instance=self.sim_instance
)
break
def update_sp_and_decibel(self, *args, **kwargs):
"""自然更新能量和喧响的方法"""
# Preload Skill
skill_nodes = _skill_node_filter(*args, **kwargs)
for node in skill_nodes:
# SP
if node.char_name == self.NAME:
if node.skill_tag == "1221_E_EX_1" and self.cinema == 6:
sp_consume = node.skill.sp_consume / 2
else:
sp_consume = node.skill.sp_consume
sp_threshold = node.skill.sp_threshold
sp_recovery = node.skill.sp_recovery
if self.sp < sp_threshold:
print(
f"{node.skill_tag}需要{sp_threshold:.2f}点能量,目前{self.NAME}仅有{self.sp:.2f}点,需求无法满足,请检查技能树"
)
sp_change = sp_recovery - sp_consume
self.update_sp(sp_change)
# Decibel
self.process_single_node_decibel(node)
# SP recovery over time
self.update_sp_overtime(args, kwargs)
def get_resources(self) -> tuple[str | None, int | float | bool | None]:
"""柳的get_resource不返回内容!因为柳没有特殊资源,只有特殊状态"""
return None, None
def get_special_stats(self, *args, **kwargs) -> dict[str | None, object | None]:
return {
"当前架势": self.stance_manager.stance_now,
"森罗万象状态": self.stance_manager.shinrabanshou.active,
}
================================================
FILE: zsim/sim_progress/Character/Yixuan/AdrenalineEventClass.py
================================================
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
from zsim.define import YIXUAN_REPORT
if TYPE_CHECKING:
from zsim.sim_progress.Character.Yixuan import Yixuan
from zsim.sim_progress.Preload import SkillNode
from zsim.simulator.simulator_class import Simulator
class BaseAdrenalineEvent(ABC):
"""管理单个闪能事件的基类"""
@abstractmethod
def __init__(self, char_instance: "Yixuan", comment: str = None):
self.char = char_instance
self.comment = comment
self.active: bool = False
self.max_duration: int = 0
self.last_active_tick: int = 0
self.regenerate_value_sum: float = 0
self.index = ""
self.active_times: int = 0
@abstractmethod
def update_status(self, skill_node: "SkillNode"):
"""更新自身状态,但不包含生效逻辑——char接收Preload的skill_node时调用"""
pass
def check_myself(self):
"""
在Buff阶段调用,检查自身是否处于激活状态,若自身激活,则调用effect_apply
"""
simulator: "Simulator" = self.char.sim_instance
if self.active:
self.apply_effect()
if simulator.tick >= self.last_active_tick + self.max_duration:
self.active = False
if YIXUAN_REPORT:
print(
f"第{self.active_times}次【{self.index}】结束了!总计为仪玄恢复了{self.regenerate_value_sum: .2f}点闪能!"
)
self.char.sim_instance.schedule_data.change_process_state()
self.regenerate_value_sum = 0
@abstractmethod
def apply_effect(self):
"""事件生效一次的方法"""
pass
class AuricArray(BaseAdrenalineEvent):
"""来自仪玄玄墨极阵释放过程中的回能效果的管理器对象"""
def __init__(self, char_instance: "Yixuan", comment: str = None):
super().__init__(char_instance, comment)
self.index = "玄墨极阵回能效果"
self.comment = (
"来自玄墨极阵的回能Buff,【普通攻击:玄墨极阵】期间会持续回复闪能,每秒7点,持续3秒"
)
self.active = False
self.max_duration: int = 180
self.last_active_tick: int = 0
self.regenerate_value = 7 / 60
self.active_times: int = 0
self.regenerate_value_sum = 0
def update_status(self, skill_node: "SkillNode"):
"""当检测到玄墨极阵的skill_node时,更新自身状态,"""
simulator: "Simulator" = self.char.sim_instance
if skill_node:
if skill_node.char_name != self.char.NAME:
return
if skill_node.skill_tag == "1371_SNA_B_1":
if self.active:
raise ValueError(
f"仪玄在展开玄墨极阵时,检测到上一个开始于{self.last_active_tick}tick的玄墨极阵的回能Buff尚未结束,仪玄不可能在短时间内打出两次玄墨极阵,请检查逻辑!"
)
self.active = True
self.active_times += 1
self.last_active_tick = simulator.tick
"""激活的当前tick也需要恢复闪能,但是并不是在本方法内部执行的,而是通过Buff触发器统一在Load阶段执行。"""
if YIXUAN_REPORT:
print(f"检测到技能{skill_node.skill_tag}(玄墨极阵)!【{self.index}】激活")
self.char.sim_instance.schedule_data.change_process_state()
def apply_effect(self):
"""事件生效,恢复一次闪能值"""
self.char.update_adrenaline(self.regenerate_value)
self.regenerate_value_sum += self.regenerate_value
class AuricInkUndercurrent(BaseAdrenalineEvent):
"""仪玄组队被动中,队友释放大招时的回能事件的管理器对象"""
def __init__(self, char_instance: "Yixuan", comment: str = None):
super().__init__(char_instance, comment)
self.index = "组队被动回能效果"
self.comment = "来自组队被动的回能Buff,队友释放大招后,仪玄会持续每秒恢复2点闪能,持续10秒"
self.active = False
self.max_duration: int = 600
self.last_active_tick: int = 0
self.regenerate_value_per_tick = 2 / 60
self.active_times: int = 0
self.regenerate_value_sum = 0
def update_status(self, skill_node: "SkillNode"):
"""当检测到队友大招时,更新自身状态,"""
simulator: "Simulator" = self.char.sim_instance
if skill_node:
# 过滤自己的技能
if skill_node.char_name == self.char.NAME:
return
if skill_node.skill.trigger_buff_level == 6:
self.active = True
self.active_times += 1
self.last_active_tick = simulator.tick
if YIXUAN_REPORT:
print(f"检测到队友释放大招:{skill_node.skill_tag}!【{self.index}】激活")
self.char.sim_instance.schedule_data.change_process_state()
def apply_effect(self):
"""事件生效,恢复一次闪能值"""
self.char.update_adrenaline(self.regenerate_value_per_tick)
self.regenerate_value_sum += self.regenerate_value_per_tick
================================================
FILE: zsim/sim_progress/Character/Yixuan/AdrenalineManagerClass.py
================================================
from typing import TYPE_CHECKING
from .AdrenalineEventClass import AuricArray, AuricInkUndercurrent, BaseAdrenalineEvent
if TYPE_CHECKING:
from zsim.sim_progress.Character.Yixuan import Yixuan
from zsim.sim_progress.Preload import SkillNode
ADRENALINE_EVENT_LIST = [AuricArray, AuricInkUndercurrent]
def adrenaline_event_factory(char_instance: "Yixuan") -> list:
event_list = []
for event in ADRENALINE_EVENT_LIST:
if event == AuricInkUndercurrent:
if not char_instance.additional_abililty_active:
continue
event_list.append(event(char_instance=char_instance))
return event_list
class AdrenalineManager:
"""仪玄有各种回复闪能的事件,所以统一写一个Manger来管理它们。"""
def __init__(self, char_instance: "Yixuan"):
self.char = char_instance
self.adrenaline_recover_event_group: list[BaseAdrenalineEvent] | None = None
def broadcast(self, skill_node: "SkillNode"):
"""向所有回能事件进行广播"""
if self.adrenaline_recover_event_group is None:
self.adrenaline_recover_event_group = adrenaline_event_factory(char_instance=self.char)
for event in self.adrenaline_recover_event_group:
event.update_status(skill_node=skill_node)
def refresh(self):
for event in self.adrenaline_recover_event_group:
event.check_myself()
================================================
FILE: zsim/sim_progress/Character/Yixuan/__init__.py
================================================
from typing import TYPE_CHECKING
from zsim.define import YIXUAN_REPORT
from zsim.sim_progress.Character import Character
from zsim.simulator.simulator_class import Simulator
from ..utils.filters import (
_skill_node_filter,
_sp_update_data_filter,
)
from .AdrenalineManagerClass import AdrenalineManager
if TYPE_CHECKING:
from zsim.sim_progress.Preload import SkillNode
class Yixuan(Character):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.sheer_attack_conversion_rate = {
0: 0.3,
1: 0.1,
2: 0,
3: 0,
} # 贯穿力转化字典{属性值(攻击力0,生命值1,防御力2,精通3): 倍率}
self.adrenaline_limit = 120 # 闪能最大值
self.max_technique_points = 120 # 最大术法值
self.adrenaline = self.adrenaline_limit # 入场时,获得满闪能
self.technique_points: float = 0.0 if self.cinema < 1 else 120.0 # 术法值
self.adrenaline_manager = AdrenalineManager(char_instance=self)
self.listener_build = False
self.__adrenaline_recover_overtime_update_tick = 0 # 上次更新闪能自然恢复的时间
self.__technique_points_trans_ratio = 0.667 # 闪能转化成术法值的比例
self.auricink_point: int = 0 # 玄墨值
self.condensed_ink: int = 0 # 聚墨(2画效果)
self.regulated_breathing: bool = False # 调息(6画效果)
self.regulated_breathing_last_update_tick: int = 0
self.cinema_6_cd = 1800 # 6画获得调息的CD
# TODO: 队友极限支援监听
# TODO: 极限闪避监听
def special_resources(self, *args, **kwargs) -> None:
# 输入类型检查
if not self.listener_build:
if not isinstance(self.sim_instance, Simulator):
raise TypeError("仪玄对象中的sim_instance不是Simulator类")
self.sim_instance.listener_manager.listener_factory(
listener_owner=self,
initiate_signal="Yixuan_1",
sim_instance=self.sim_instance,
)
self.listener_build = True
skill_nodes: list["SkillNode"] = _skill_node_filter(*args, **kwargs)
tick = self.sim_instance.tick
for __nodes in skill_nodes:
"""向闪能回复事件管理器广播当前skill_node"""
self.adrenaline_manager.broadcast(skill_node=__nodes)
if __nodes.char_name != self.NAME:
continue
if __nodes.skill_tag == "1371_Q_A":
if self.auricink_point != 0:
print(
f"Warning:仪玄在玄墨值大于0的情况下再次释放了【{__nodes.skill.skill_text}】,这造成了玄墨值的溢出,请检查APL代码!"
)
if self.regulated_breathing:
if self.cinema < 6:
raise ValueError("仪玄在非6画状态下开启了调息状态,请检查代码!")
self.regulated_breathing = False
self.regulated_breathing_last_update_tick = tick
if YIXUAN_REPORT:
print(f"6画:仪玄释放【{__nodes.skill.skill_text}】,消耗一层调息!")
self.sim_instance.schedule_data.change_process_state()
else:
if self.technique_points < 120:
raise ValueError("仪玄的术法值不足!请检查APL!")
self.technique_points = 0
self.auricink_point = min(1, self.auricink_point + 1)
elif __nodes.skill_tag == "1371_Q":
if self.cinema >= 2:
self.condensed_ink = min(1, self.condensed_ink + 1)
if YIXUAN_REPORT:
print(
f"2画:检测到仪玄释放喧响大招【{__nodes.skill.skill_text}】,获得1点聚墨值!"
)
self.sim_instance.schedule_data.change_process_state()
if self.cinema == 6:
if (
tick - self.regulated_breathing_last_update_tick >= self.cinema_6_cd
) or self.regulated_breathing_last_update_tick == 0:
self.regulated_breathing = True
if YIXUAN_REPORT:
print(
f"6画:检测到技能【{__nodes.skill.skill_text}】,仪玄获得一层调息"
)
self.sim_instance.schedule_data.change_process_state()
else:
print(
f"6画:检测到技能【{__nodes.skill.skill_text}】,但是仪玄调息的内置CD尚未转好,所以无法获得调息"
)
elif __nodes.skill_tag == "1371_SNA_B_1":
if self.auricink_point <= 0:
raise ValueError("仪玄的玄墨值不足!请检查APL!")
self.auricink_point -= 1
elif __nodes.skill_tag == "1371_Cinema_2":
if self.cinema < 2:
raise ValueError(
f"仪玄当前影画为{self.cinema},未解锁2画,APL却抛出了2画专属的SkillNode,请检查APL"
)
if self.condensed_ink < 1:
raise ValueError("仪玄当前的聚墨点数不足!请检查APL")
self.condensed_ink -= 1
if YIXUAN_REPORT:
print(f"2画:仪玄追加释放【{__nodes.skill.skill_text}】,消耗1点聚墨值")
self.sim_instance.schedule_data.change_process_state()
# 更新术法值和闪能值
self.__update_adrenaline(skill_node=__nodes)
def update_sp(self, sp_value: float):
"""仪玄没有能量值,所以这里update_sp直接return置空"""
return
def update_adrenaline(self, sp_value: int | float):
"""可全局强制更新能量的方法——仪玄特化版"""
if sp_value < 0:
# 当检测到闪能消耗时候,进行术法值的转化
technique_points_delta = abs(sp_value) * self.__technique_points_trans_ratio
self.technique_points = min(
self.technique_points + technique_points_delta,
self.max_technique_points,
)
if YIXUAN_REPORT:
print(
f"仪玄消耗了{abs(sp_value):.2f}点闪能值,转化为{technique_points_delta:.2f}点术法值!当前术法值为:{self.technique_points:.2f}"
)
self.sim_instance.schedule_data.change_process_state()
self.adrenaline += sp_value
self.adrenaline = max(0.0, min(self.adrenaline, self.adrenaline_limit))
# if abs(sp_value) >= 0.1:
# print(f"仪玄的闪能改变了{sp_value:.2f}点,当前为:{self.adrenaline:.2f}")
def __update_adrenaline(self, skill_node: "SkillNode"):
"""char对象内部更新闪能的方法"""
if skill_node.char_name != self.NAME:
raise ValueError(f"{self.NAME}的更新闪能的方法接收到了非仪玄的SkillNode")
adrenaline_delta = (
skill_node.skill.adrenaline_recovery - skill_node.skill.adrenaline_consume
)
if adrenaline_delta <= 0 and abs(adrenaline_delta) > self.adrenaline:
raise ValueError(
f"检测到技能{skill_node.skill_tag}【{skill_node.skill.skill_text}】企图消耗{skill_node.skill.adrenaline_consume}点闪能,但是仪玄当前的闪能不足:{self.adrenaline},请检查APL"
)
self.update_adrenaline(adrenaline_delta)
def update_sp_overtime(self, args, kwargs):
"""
该函数会在Preload以及Schedule阶段被调用两次,当在Preload阶段调用时,sp_regen_data为空,
只有在Schedule阶段被调用时,函数才会被执行。
所以sp_regen_data虽然和闪能回复无关,但确是保证函数只被运行一次的关键。
"""
sp_regen_data = _sp_update_data_filter(*args, **kwargs)
if sp_regen_data:
if (
self.sim_instance.tick == self.__adrenaline_recover_overtime_update_tick
and self.__adrenaline_recover_overtime_update_tick != 0
):
raise ValueError(
"检测到仪玄闪能的自然恢复逻辑在同一个tick被调用了两次!请检查函数!"
)
sp_change_per_tick = 2 / 60
self.update_adrenaline(sp_change_per_tick)
self.__adrenaline_recover_overtime_update_tick = self.sim_instance.tick
def refresh_myself(self):
"""回能更新的几个管理器需要每个tick更新一次,所以用这个接口进行更新。"""
self.adrenaline_manager.refresh()
def get_resources(self) -> tuple[str, float]:
return "闪能", self.adrenaline
def get_special_stats(self, *args, **kwargs) -> dict[str, int | float | bool]:
"""获取简仪玄特殊状态"""
return {
"术法值": self.technique_points,
"玄墨值": self.auricink_point,
"聚墨点数": self.condensed_ink,
"调息层数": self.regulated_breathing,
}
================================================
FILE: zsim/sim_progress/Character/Yuzuha/__init__.py
================================================
from typing import TYPE_CHECKING
from zsim.define import YUZUHA_REPORT
from zsim.models.event_enums import PostInitObjectType as PIOT
from zsim.models.event_enums import SpecialStateUpdateSignal as SSUS
from zsim.sim_progress.Preload import SkillNode
from ...data_struct.SchedulePreload import schedule_preload_event_factory
from ..character import Character
from ..utils.filters import _skill_node_filter
if TYPE_CHECKING:
from zsim.sim_progress.data_struct.enemy_special_state_manager.special_classes import SweetScare
from zsim.simulator.simulator_class import Simulator
class Yuzuha(Character):
def __init__(self, **kwargs):
"""柚叶的特殊资源"""
super().__init__(**kwargs)
self.sweet_scare: SweetScare | None = None
self.sugar_points: int = 3 # 甜度点
self.max_sugar_points: int = 6
self.hard_candy_shot_tag = "1411_CoAttack_A"
def special_resources(self, *args, **kwargs) -> None:
skill_nodes: list[SkillNode] = _skill_node_filter(*args, **kwargs)
for node in skill_nodes:
sim_instance: "Simulator" = self.sim_instance
sim_instance.schedule_data.enemy.special_state_manager.broadcast_and_update(
signal=SSUS.CHARACTER, skill_node=node
)
if node.skill_tag == "1411_Assault_Aid_B" and self.cinema < 6:
raise ValueError(
"企图在非6画状态下对支援突击进行蓄力!请检查define中的招架支援配置!"
)
if node.char_name != self.NAME:
continue
if node.skill.labels is not None and "sugar_points" in node.skill.labels:
sugar_points = node.skill.labels["sugar_points"]
self.update_sugar_points(value=sugar_points)
if YUZUHA_REPORT:
self.sim_instance.schedule_data.change_process_state()
print(
f"{self.NAME}释放了技能{node.skill_tag},{'获得' if sugar_points > 0 else '消耗'}了{abs(sugar_points)}甜度点!当前甜度点为{self.sugar_points}"
)
if node.skill.trigger_buff_level == 6:
from zsim.sim_progress.data_struct.sp_update_data import ScheduleRefreshData
report_namelist = []
for char_obj in sim_instance.char_data.char_obj_list:
if char_obj.NAME == self.NAME:
continue
report_namelist.append(char_obj.NAME)
schedule_refresh_event = ScheduleRefreshData(
sp_target=(char_obj.NAME,),
sp_value=25,
)
event_list = sim_instance.schedule_data.event_list
event_list.append(schedule_refresh_event)
else:
if YUZUHA_REPORT:
sim_instance.schedule_data.change_process_state()
print(
f"【柚叶回能】:柚叶发动大招,为{[_name for _name in report_namelist]}恢复25点能量值"
)
def update_sugar_points(self, value: int):
"""更新甜度点"""
if value < 0 and abs(self.sugar_points) < abs(value):
if YUZUHA_REPORT:
sim_instance: "Simulator" = self.sim_instance
sim_instance.schedule_data.change_process_state()
print(
f"【甜度点警告】:甜度点不足!当前甜度点为{self.sugar_points}, 甜度点消耗值为:{abs(value)}"
)
self.sugar_points = 0
return
self.sugar_points += value
if self.sugar_points > self.max_sugar_points:
self.sugar_points = self.max_sugar_points
def spawn_hard_candy_shot(self, update_signal: "SkillNode" = None):
"""生成一次硬糖射击"""
# self.update_sugar_points(value=-1)
skill_tag_list = [self.hard_candy_shot_tag]
preload_tick_list = [self.sim_instance.tick]
schedule_preload_event_factory(
skill_tag_list=skill_tag_list,
preload_tick_list=preload_tick_list,
preload_data=self.sim_instance.preload.preload_data,
apl_priority_list=[-1],
sim_instance=self.sim_instance,
)
if YUZUHA_REPORT:
self.sim_instance.schedule_data.change_process_state()
print(
f"【硬糖射击】{update_signal.skill_tag if update_signal is not None else None}触发了一次硬糖射击!"
)
def POST_INIT_DATA(self, sim_instance: "Simulator"):
"""柚叶的后置初始化函数,用于后置创建甜蜜惊吓特殊状态"""
enemy = sim_instance.schedule_data.enemy
self.sweet_scare = enemy.special_state_manager.special_state_factory(
state_type=PIOT.SweetScare
)
self.sp = 40.00 if self.cinema < 1 else 70.00 # 初始化能量值
if self.cinema >= 2:
sim_instance.listener_manager.listener_factory(
listener_owner=self, initiate_signal="Yuzuha_1", sim_instance=sim_instance
)
if self.cinema >= 6:
sim_instance.listener_manager.listener_factory(
listener_owner=self, initiate_signal="Yuzuha_2", sim_instance=sim_instance
)
def get_resources(self, *args, **kwargs) -> tuple[str | None, int | float | None]:
return "甜度点", self.sugar_points
def get_special_stats(self, *args, **kwargs) -> dict[str | None, object | None]:
pass
================================================
FILE: zsim/sim_progress/Character/Zhuyuan.py
================================================
from ..Preload import SkillNode
from ..Report import report_to_log
from .character import Character
from .utils.filters import _skill_node_filter
class Zhuyuan(Character):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.shotshells = 0 # 霰弹个数
if self.cinema >= 1: # 影画1的额外子弹逻辑
self.allow_restore = True
self.QTE_STORED = 6
self.Q_STORED = 9
self.shotshells_warehouse: list = []
else:
self.allow_restore = False
def special_resources(self, *args, **kwargs):
# 输入类型检查
skill_nodes: list[SkillNode] = _skill_node_filter(*args, **kwargs)
for node in skill_nodes:
# 攒子弹逻辑
if node.skill_tag in [
"1241_E_EX",
"1241_E_EX_A",
"1241_QTE",
"1241_Q",
"1241_Assault_Aid",
]:
self.shotshells = min(self.shotshells + 3, 9)
if self.allow_restore and node.skill_tag in ["1241_QTE", "1241_Q"]:
self.shotshells_warehouse.append(node.skill_tag)
# 消耗子弹逻辑
if "1241_S" in node.skill_tag:
if self.shotshells <= 0:
report_to_log("[Zhuyuan]: 弹夹为空, 无法使用")
print("[Zhuyuan]:弹夹为空, 无法使用")
self.shotshells = max(self.shotshells - 1, 0)
if self.shotshells == 0 and self.allow_restore:
if self.shotshells_warehouse:
popping_shotshells = self.shotshells_warehouse.pop()
for shell in self.shotshells_warehouse[:]:
if shell == popping_shotshells:
self.shotshells_warehouse.remove(shell)
if popping_shotshells == "1241_QTE":
self.shotshells += self.QTE_STORED
elif popping_shotshells == "1241_Q":
self.shotshells += self.Q_STORED
def get_resources(self, *args, **kwargs) -> tuple[str | None, int | float | bool | None]:
return "强化霰弹", self.shotshells
def get_special_stats(self, *args, **kwargs) -> dict[str | None, object | None]:
if self.allow_restore:
stored_shotshells = (6 if "1241_QTE" in self.shotshells_warehouse else 0) + (
9 if "1241_Q" in self.shotshells_warehouse else 0
)
else:
stored_shotshells = 0
return {
"强化霰弹": self.shotshells,
"缓存霰弹": stored_shotshells,
"强化霰弹(含缓存)": self.shotshells + stored_shotshells,
}
================================================
FILE: zsim/sim_progress/Character/__init__.py
================================================
import importlib
from typing import TYPE_CHECKING
from .character import Character
from .skill_class import lookup_name_or_cid
if TYPE_CHECKING:
from zsim.models.session.session_run import CharConfig, ExecAttrCurveCfg, ExecWeaponCfg
__char_module_map = {
"苍角": "Soukaku",
"莱特": "Lighter",
"艾莲": "Ellen",
"雅": "Miyabi",
"11号": "Soldier11",
"青衣": "Qingyi",
"朱鸢": "Zhuyuan",
"伊芙琳": "Evelyn",
"零号·安比": "Soldier0_Anby",
"扳机": "Trigger",
"柳": "Yanagi",
"简": "Jane",
"薇薇安": "Vivian",
"耀嘉音": "AstraYao",
"雨果": "Hugo",
"仪玄": "Yixuan",
"柚叶": "Yuzuha",
"爱丽丝": "Alice",
"席德": "Seed",
}
def character_factory(
char_config: "CharConfig",
*,
sim_cfg: "ExecAttrCurveCfg | ExecWeaponCfg | None" = None,
) -> Character:
"""
角色工厂函数,用于创建角色实例
参数:
- char_config: CharConfig对象,包含角色的所有配置信息
- sim_cfg: 模拟配置对象,可选参数,用于特殊模拟模式
返回:
- Character: 角色实例
"""
name, CID = lookup_name_or_cid(char_config.name, char_config.CID)
if name in __char_module_map:
try:
module_name = __char_module_map[name]
module = importlib.import_module(f".{module_name}", package=__name__)
character_class: type[Character] = getattr(module, module_name)
return character_class(char_config=char_config, sim_cfg=sim_cfg)
except ModuleNotFoundError:
return Character(char_config=char_config, sim_cfg=sim_cfg)
else:
return Character(char_config=char_config, sim_cfg=sim_cfg)
================================================
FILE: zsim/sim_progress/Character/character.py
================================================
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
import polars as pl
from zsim.define import (
CHARACTER_DATA_PATH,
EQUIP_2PC_DATA_PATH,
SUB_STATS_MAPPING,
WEAPON_DATA_PATH,
)
from zsim.models.session.session_run import CharConfig, ExecAttrCurveCfg, ExecWeaponCfg
from zsim.sim_progress.Report import report_to_log
from .skill_class import Skill, lookup_name_or_cid
from .utils.filters import _skill_node_filter, _sp_update_data_filter
if TYPE_CHECKING:
from zsim.sim_progress.Buff.buff_class import Buff
from zsim.sim_progress.data_struct.sp_update_data import SPUpdateData
from zsim.sim_progress.Preload.SkillsQueue import SkillNode
from zsim.simulator.simulator_class import Simulator
class Character:
def __init__(
self,
*,
char_config: CharConfig,
sim_cfg: ExecAttrCurveCfg | ExecWeaponCfg | None = None,
):
"""
调用时,会生成包含全部角色基础信息的对象,自动从数据库中查找全部信息
参数:
- char_config: CharConfig对象,包含角色的所有配置信息
- sim_cfg: 模拟配置对象,可选参数,用于特殊模拟模式
自生成参数:
-self.level = 60 默认角色等级,传给防御区用
-记录每个基础属性来源的各参数,主要来自查表
-包含角色全部技能信息的 Skill 对象,以及来自 Skill 的 action_list, skills_dict
暴击非配平逻辑(直接读取):
scCRIT: 暴击率副词条
scCRIT_DMG: 暴击伤害副词条
是否配平由传入参数 char_config.crit_balancing 控制
配平逻辑下,暴伤与暴击率词条将会被重新分配,且没有基于整数词条数量的自动重整
"""
# 从CharConfig对象中提取参数
name = char_config.name
CID = char_config.CID
weapon = char_config.weapon
weapon_level = char_config.weapon_level
equip_style = char_config.equip_style
equip_set4 = char_config.equip_set4
equip_set2_a = char_config.equip_set2_a
equip_set2_b = char_config.equip_set2_b
equip_set2_c = char_config.equip_set2_c
drive4 = char_config.drive4
drive5 = char_config.drive5
drive6 = char_config.drive6
scATK_percent = char_config.scATK_percent
scATK = char_config.scATK
scHP_percent = char_config.scHP_percent
scHP = char_config.scHP
scDEF_percent = char_config.scDEF_percent
scDEF = char_config.scDEF
scAnomalyProficiency = char_config.scAnomalyProficiency
scPEN = char_config.scPEN
scCRIT = char_config.scCRIT
scCRIT_DMG = char_config.scCRIT_DMG
sp_limit = char_config.sp_limit
cinema = char_config.cinema
crit_balancing = char_config.crit_balancing
crit_rate_limit = char_config.crit_rate_limit
# 从数据库中查找角色信息,并核对必填项
self.NAME, self.CID = lookup_name_or_cid(name, CID)
# 初始化为0的各属性
self.baseATK = 0.0
self.ATK_percent = 0.0
self.ATK_numeric = 0.0
self.overall_ATK_percent = 0.0
self.overall_ATK_numeric = 0.0
self.baseHP = 0.0
self.HP_percent = 0.0
self.HP_numeric = 0.0
self.overall_HP_percent = 0.0
self.overall_HP_numeric = 0.0
self.baseDEF = 0.0
self.DEF_percent = 0.0
self.DEF_numeric = 0.0
self.overall_DEF_percent = 0.0
self.overall_DEF_numeric = 0.0
self.baseIMP = 0.0
self.IMP_percent = 0.0
self.IMP_numeric = 0.0
self.overall_IMP_percent = 0.0
self.overall_IMP_numeric = 0.0
self.baseAP = 0.0
self.AP_percent = 0.0
self.AP_numeric = 0.0
self.overall_AP_percent = 0.0
self.overall_AP_numeric = 0.0
self.baseAM = 0.0
self.AM_percent = 0.0
self.AM_numeric = 0.0
self.overall_AM_percent = 0.0
self.overall_AM_numeric = 0.0
self.CRIT_rate_numeric = 0.0 # 暴击率(非配平逻辑使用)
self.CRIT_damage_numeric = 0.0 # 暴击伤害(非配平逻辑使用)
self.base_sp_regen = 0.0
self.sp_regen_percent = 0.0
self.sp_regen_numeric = 0.0
self.ICE_DMG_bonus = 0.0
self.FIRE_DMG_bonus = 0.0
self.PHY_DMG_bonus = 0.0
self.ETHER_DMG_bonus = 0.0
self.ELECTRIC_DMG_bonus = 0.0
self.ALL_DMG_bonus = 0.0
self.Trigger_DMG_bonus = 0.0
self.PEN_ratio = 0.0
self.PEN_numeric = 0.0
# 单独初始化的各组件
self.level: int = 60
self.weapon_ID: str | None = weapon
self.weapon_level: int = weapon_level
self.cinema: int = cinema
self.baseCRIT_score: float = 60
self.sp_get_ratio: float = 1 # 能量获得效率
self.sp_limit: int = int(sp_limit)
self.sp: float = 40.0
self.decibel: float = 1000.0
self.specialty: str | None
self.element_type: int
self.crit_balancing: bool = crit_balancing
self.crit_rate_limit: float = crit_rate_limit
self.sheer_attack_conversion_rate: dict[int, float] | None = None
# 初始化角色基础属性 .\data\character.csv
self._init_base_attribute(name)
# fmt: off
# 如果并行配置没有移除套装,就初始化套装效果和主副词条
if sim_cfg is not None:
if isinstance(sim_cfg, ExecAttrCurveCfg):
if not sim_cfg.remove_equip:
self.__init_all_equip_static(drive4, drive5, drive6,
equip_set2_a, equip_set2_b, equip_set2_c, equip_set4, equip_style,
scATK, scATK_percent, scAnomalyProficiency, scCRIT,
scCRIT_DMG, scDEF, scDEF_percent, scHP, scHP_percent, scPEN)
self.__init_attr_curve_config(sim_cfg)
self._init_weapon_primitive(weapon, weapon_level)
elif isinstance(sim_cfg, ExecWeaponCfg):
self.__init_all_equip_static(drive4, drive5, drive6,
equip_set2_a, equip_set2_b, equip_set2_c, equip_set4, equip_style,
scATK, scATK_percent, scAnomalyProficiency, scCRIT,
scCRIT_DMG, scDEF, scDEF_percent, scHP, scHP_percent, scPEN)
self._init_weapon_primitive(sim_cfg.weapon_name, sim_cfg.weapon_level) # 覆盖武器基础属性
else:
self.__init_all_equip_static(drive4, drive5, drive6,
equip_set2_a, equip_set2_b, equip_set2_c, equip_set4, equip_style,
scATK, scATK_percent, scAnomalyProficiency, scCRIT,
scCRIT_DMG, scDEF, scDEF_percent, scHP, scHP_percent, scPEN)
# 初始化武器基础属性 .\data\weapon.csv
self._init_weapon_primitive(weapon, weapon_level)
self.additional_abililty_active: bool | None = None # 角色是否激活组队被动,该参数将在Buff模块初始化完成后进行赋值
skill_level_addon = 4 if self.cinema >=5 else (2 if self.cinema >= 3 else 0)
skills_level: dict[str, int] = {
"normal_level": 12 + skill_level_addon,
"special_level": 12 + skill_level_addon,
"dodge_level": 12 + skill_level_addon,
"chain_level": 12 + skill_level_addon,
"assist_level": 12 + skill_level_addon,
}
self.statement = Character.Statement(self, crit_balancing=crit_balancing)
self.skill_object: Skill = Skill(name=self.NAME, CID=self.CID, **skills_level, char_obj=self)
self.action_list = self.skill_object.action_list
self.skills_dict = self.skill_object.skills_dict
self.dynamic = self.Dynamic(self)
self.sim_instance: "Simulator | None" = None # 模拟器实例
self.equip_buff_map: dict[int, "Buff"] = {} # 来自装备的Buff0的指针
# fmt: off
def __init_all_equip_static(self, drive4, drive5, drive6,
equip_set2_a, equip_set2_b, equip_set2_c, equip_set4, equip_style,
scATK, scATK_percent, scAnomalyProficiency, scCRIT,
scCRIT_DMG, scDEF, scDEF_percent, scHP, scHP_percent, scPEN):
# fmt: on
# 初始化套装效果 .\data\equip_set_2pc.csv
self._init_equip_set(
equip_style, equip_set4, equip_set2_a, equip_set2_b, equip_set2_c
)
# 初始化主词条
self._init_main_stats(drive4, drive5, drive6)
# 初始化副词条
self._init_sub_stats(
scATK_percent, scATK, scHP_percent,scHP, scDEF_percent, scDEF, scAnomalyProficiency, scPEN, scCRIT, scCRIT_DMG,
)
# fmt: on
class Statement:
def __init__(self, char: "Character", crit_balancing: bool):
"""
-char_class : 已实例化的角色
用于计算角色面板属性:
-传入已经实例化的 Character 对象,计算出目前的角色面板
-如果和 Character 对象同时调用,那么本对象会储存角色的局外面板属性
-可在调用本类前对一个 Character 对象内的值进行更改,以实现手动调整面板的功能
调用格式为:
char_dynamic = Character.Statement(char) # 需要传入一个角色对象
获取面板数值:
-使用属性名调用,格式为 char_dynamic.ATK
-使用内置字典调用,格式为 char_dynamic.statement['ATK'] # 谁会想用这个方法呢,这个字典不过是方便输出 log 罢了
值得注意的是,这个类有许多属性直接继承自 Character,但是防止混淆没有写成子类,但你依然可以直接调用 NAME、CID 等静态参数
包含的方法:
-还是没有!这个类是被动的,不应该自己变化,需要的时候重新生成,你要强行写函数改也行(乐
"""
self.NAME = char.NAME
self.CID = char.CID
self.ATK = (char.baseATK * (1 + char.ATK_percent) + char.ATK_numeric) * (
1 + char.overall_ATK_percent
) + char.overall_ATK_numeric
self.HP = (char.baseHP * (1 + char.HP_percent) + char.HP_numeric) * (
1 + char.overall_HP_percent
) + char.overall_HP_numeric
self.DEF = (char.baseDEF * (1 + char.DEF_percent) + char.DEF_numeric) * (
1 + char.overall_DEF_percent
) + char.overall_DEF_numeric
self.IMP = (char.baseIMP * (1 + char.IMP_percent) + char.IMP_numeric) * (
1 + char.overall_IMP_percent
) + char.overall_IMP_numeric
self.AP = (char.baseAP * (1 + char.AP_percent) + char.AP_numeric) * (
1 + char.overall_AP_percent
) + char.overall_AP_numeric
self.AM = (char.baseAM * (1 + char.AM_percent) + char.AM_numeric) * (
1 + char.overall_AM_percent
) + char.overall_AM_numeric
# 更换balancing参数可实现不同的逻辑,默认为True,即配平逻辑
self.CRIT_damage, self.CRIT_rate = self._func_statement_CRIT(
char.baseCRIT_score,
char.CRIT_rate_numeric,
char.CRIT_damage_numeric,
char.crit_rate_limit,
balancing=crit_balancing,
)
self.sp_regen = char.base_sp_regen * (1 + char.sp_regen_percent) + char.sp_regen_numeric
self.sp_get_ratio = char.sp_get_ratio
self.sp_limit = char.sp_limit
# 储存目前能量与喧响的参数
self.PEN_ratio = char.PEN_ratio
self.PEN_numeric = char.PEN_numeric
self.ICE_DMG_bonus = char.ICE_DMG_bonus
self.FIRE_DMG_bonus = char.FIRE_DMG_bonus
self.PHY_DMG_bonus = char.PHY_DMG_bonus
self.ETHER_DMG_bonus = char.ETHER_DMG_bonus
self.ELECTRIC_DMG_bonus = char.ELECTRIC_DMG_bonus
# 将当前对象 (self) 的所有非可调用(即不是方法或函数)的属性收集到一个字典中
self.statement = {
attr: getattr(self, attr)
for attr in dir(self)
if not callable(getattr(self, attr)) and not attr.startswith("__")
}
report_to_log(f"[CHAR STATUS]:{self.NAME}:{str(self.statement)}")
@staticmethod
def _func_statement_CRIT(
CRIT_score: float,
CRIT_rate_numeric: float,
CRIT_damage_numeric: float,
CRIT_rate_limit: float,
balancing: bool,
) -> tuple[float, float]:
"""
双暴状态函数
balancing : 是否使用配平逻辑
CRIT_score : 暴击评分
CRIT_rate_numeric : 暴击率数值
CRIT_damage_numeric : 暴击伤害数值
返回:
CRIT_damage : 暴击伤害
CRIT_rate : 暴击率
默认为True,即配平逻辑,会使用暴击评分、暴击暴伤输出,集中计算暴击率与暴击伤害
若为False,则忽略传入的暴击评分,直接返回给定的数值
"""
# 参数有效性验证
if not (0 <= CRIT_score):
raise ValueError("CRIT_score must be above 0")
if not (0 <= CRIT_rate_numeric):
raise ValueError("CRIT_rate_numeric must be above 0")
if not (0 <= CRIT_damage_numeric):
raise ValueError("CRIT_damage_numeric must be above 0")
if not (0 <= CRIT_rate_limit <= 1):
raise ValueError("CRIT_rate_limit must be between 0 and 1")
if balancing:
limit_score: float = CRIT_rate_limit * 400
if CRIT_score >= limit_score:
CRIT_rate = CRIT_rate_limit
CRIT_damage = (CRIT_score - CRIT_rate * 200) / 100
else:
CRIT_damage = max(0.5, CRIT_score / 200)
CRIT_rate = (CRIT_score / 100 - CRIT_damage) / 2
else:
CRIT_damage = CRIT_damage_numeric
CRIT_rate = CRIT_rate_numeric
return min(5.0, CRIT_damage), min(1.0, CRIT_rate)
def __str__(self) -> str:
return f"角色静态面板:{self.NAME}"
class Dynamic:
"""用于记录角色各种动态信息的类,主要和APL模块进行互动。"""
def __init__(self, char_instantce: Character):
self.character = char_instantce
self.lasting_node = LastingNode(self.character)
from zsim.sim_progress.data_struct.QuickAssistSystem.quick_assist_manager import (
QuickAssistManager,
)
self.quick_assist_manager = QuickAssistManager(self.character)
self._on_field = False # 角色是否在前台
self._switching_in_tick = 0 # 角色切到前台状态的时间点
self._switching_out_tick = 0 # 角色切到后台状态的时间点
def reset(self):
self.lasting_node.reset()
self.on_field = False
@property
def on_field(self) -> bool:
return self._on_field
@on_field.setter
def on_field(self, value: bool):
assert self.character.sim_instance is not None
tick = self.character.sim_instance.tick
if self.on_field and not value:
# 角色on_field状态的下降沿,即角色从前台切换到后台
self._switching_out_tick = tick
elif not self.on_field and value:
# 角色on_field状态的上升沿,即角色从后台切换到前台
self._switching_in_tick = tick
self._on_field = value
def is_off_field_within(self, max_ticks: int) -> bool:
"""判断角色切到后台的时间是否小于等于指定时间"""
assert self.character.sim_instance is not None
if self.on_field:
return False
current_tick = self.character.sim_instance.tick
return current_tick - self._switching_out_tick <= max_ticks
def is_on_field_within(self, max_ticks: int) -> bool:
"""判断角色切到前台的时间是否小于等于指定时间"""
assert self.character.sim_instance is not None
if not self.on_field:
return False
current_tick = self.character.sim_instance.tick
return current_tick - self._switching_in_tick <= max_ticks
def is_available(self, tick: int):
"""查询角色当前tick是否有空"""
lasting_node = self.dynamic.lasting_node
if lasting_node is None:
return ValueError("角色没有LastingNode")
node = lasting_node.node
if node is None:
return True
if node.end_tick >= tick:
return False
return True
def __mapping_csv_to_attr(self, row: dict):
self.baseATK += float(row.get("base_ATK", 0))
self.ATK_percent += float(row.get("ATK%", 0))
self.DEF_percent += float(row.get("DEF%", 0))
self.HP_percent += float(row.get("HP%", 0))
self.IMP_percent += float(row.get("IMP%", 0))
self.overall_ATK_percent += float(row.get("oATK%", 0))
self.overall_DEF_percent += float(row.get("oDEF%", 0))
self.overall_HP_percent += float(row.get("oHP%", 0))
self.overall_IMP_percent += float(row.get("oIMP%", 0))
self.AM_percent += float(row.get("Anomaly_Mastery", 0))
self.AP_numeric += float(row.get("Anomaly_Proficiency", 0))
self.sp_regen_percent += float(row.get("Regen%", 0))
self.sp_regen_numeric += float(row.get("Regen", 0))
self.sp_get_ratio += float(row.get("Get_ratio", 0))
self.PEN_ratio += float(row.get("pen%", 0))
self.ICE_DMG_bonus += float(row.get("ICE_DMG_bonus", 0))
self.FIRE_DMG_bonus += float(row.get("FIRE_DMG_bonus", 0))
self.ELECTRIC_DMG_bonus += float(row.get("ELECTRIC_DMG_bonus", 0))
self.PHY_DMG_bonus += float(row.get("PHY_DMG_bonus", 0))
self.ETHER_DMG_bonus += float(row.get("ETHER_DMG_bonus", 0))
if self.crit_balancing:
crit_score_delta = 100 * (
float(row.get("Crit_Rate", 0)) * 2 + float(row.get("Crit_DMG", 0))
)
self.baseCRIT_score += crit_score_delta
else:
self.CRIT_rate_numeric += float(row.get("Crit_Rate", 0))
self.CRIT_damage_numeric += float(row.get("Crit_DMG", 0))
def _init_base_attribute(self, char_name: str):
"""
初始化角色基础属性。
根据角色名称,从CSV文件中读取角色的基础属性数据,并将其赋值给角色对象。
参数:
char_name(str): 角色的名称。
"""
if not isinstance(char_name, str) or not char_name.strip():
raise ValueError("角色名称必须是非空字符串")
try:
row = (
pl.scan_csv(CHARACTER_DATA_PATH)
.filter(pl.col("name") == char_name)
.collect()
.to_dicts()
)
if row:
# 将对应记录提取出来,并赋值给角色对象
row_0: dict = row[0]
self.baseATK = float(row_0.get("基础攻击力", 0))
self.baseHP = float(row_0.get("基础生命值", 0))
self.baseDEF = float(row_0.get("基础防御力", 0))
self.baseIMP = float(row_0.get("基础冲击力", 0))
self.baseAP = float(row_0.get("基础异常精通", 0))
self.baseAM = float(row_0.get("基础异常掌控", 0))
# self.baseCRIT_score = float(row_0.get("基础暴击分数", 60))
self.CRIT_rate_numeric = float(
row_0.get("基础暴击率", 0)
) # 此处不需要根据暴击配平区分
self.CRIT_damage_numeric = float(row_0.get("基础暴击伤害", 1))
self.baseCRIT_score = 100 * (self.CRIT_rate_numeric * 2 + self.CRIT_damage_numeric)
# print(f'{self.NAME}的核心被动初始化完成!当前暴击分数为:{self.baseCRIT_score}')
self.PEN_ratio = float(row_0.get("基础穿透率", 0))
self.PEN_numeric = float(row_0.get("基础穿透值", 0))
self.base_sp_regen = float(row_0.get("基础能量自动回复", 0))
self.base_sp_get_ratio = float(row_0.get("基础能量获取效率", 1))
self.specialty = row_0.get("角色特性", None) # 角色特性,强攻、击破等
self.aid_type = row_0.get("支援类型", None)
self.element_type = row_0.get("角色属性", 0)
if self.element_type is None or self.element_type < 0:
raise NotImplementedError(f"角色{char_name}的属性类型未定义")
# CID特殊处理,避免不必要的类型转换
cid_value: int | None = row_0.get("CID", None)
self.CID = int(cid_value) if cid_value is not None else -1
else:
raise ValueError(f"角色{char_name}不存在")
except FileNotFoundError:
logging.error("找不到角色数据文件,请检查路径是否正确。")
raise
except Exception as e:
logging.error(f"初始化角色属性时发生未知错误:{e}")
raise
def _init_weapon_primitive(self, weapon: str | None, weapon_level: int) -> None:
"""初始化武器主属性(适配新版 weapon.csv)"""
if weapon is None:
return
df = pl.read_csv(WEAPON_DATA_PATH)
row = df.filter(pl.col("名称") == weapon)
if row.height > 0:
row_0 = row.row(0, named=True)
base_atk = float(row_0["60级基础攻击力"])
attr_value = row_0["60级高级属性值"]
self.baseATK += base_atk
# 处理高级属性
attr_type = row_0["高级属性"]
attr_value = float(attr_value)
if attr_type in ["攻击力"]:
self.ATK_percent += attr_value if attr_value < 1 else 0
elif attr_type in ["暴击率"]:
if self.crit_balancing:
self.baseCRIT_score += attr_value * 200 # 1%暴击率=2分 -> 1暴击率=200分
else:
self.CRIT_rate_numeric += attr_value
elif attr_type in ["暴击伤害"]:
if self.crit_balancing:
self.baseCRIT_score += attr_value * 100 # 1暴击伤害=100分
else:
self.CRIT_damage_numeric += attr_value
elif attr_type in ["异常精通"]:
self.AP_numeric += attr_value
elif attr_type in ["冲击力"]:
self.IMP_percent += attr_value
elif attr_type in ["防御力"]:
self.DEF_percent += attr_value
elif attr_type in ["生命值"]:
self.HP_percent += attr_value
elif attr_type in ["穿透率"]:
self.PEN_ratio += attr_value
elif attr_type in ["能量自动回复"]:
self.sp_regen_percent += attr_value
else:
raise ValueError(f"未知的武器高级属性类型:{attr_type}")
else:
raise ValueError(f"请输入正确的武器名称,{weapon} 不存在!")
def _init_equip_set(
self,
equip_style: str,
equip_set4: str | None,
equip_set2_a: str | None,
equip_set2_b: str | None,
equip_set2_c: str | None,
):
"""初始化套装效果, Character类仅计算二件套"""
if equip_style not in ["4+2", "2+2+2"]:
raise ValueError("请输入正确的套装格式")
# 将自身套装效果抄录
equip_set_all = [equip_set4, equip_set2_a, equip_set2_b, equip_set2_c]
# 检查四件套与三个二件套是否有相同的套装
used_sets = []
if equip_set4:
used_sets.append(equip_set4)
two_piece_sets = [equip_set2_a, equip_set2_b, equip_set2_c]
for set_name in two_piece_sets:
if set_name:
if set_name in used_sets:
raise ValueError("四件套与二件套中请勿输入重复的套装名称")
del used_sets, two_piece_sets
self.equip_set4, self.equip_set2_a, self.equip_set2_b, self.equip_set2_c = equip_set_all
# 4+2格式则移出2b、2c
if equip_style == "4+2": # 非空判断
if equip_set2_b in equip_set_all: # 别删这个if,否则输入None会报错
equip_set_all.remove(equip_set2_b)
if equip_set2_c in equip_set_all: # 别删这个if,否则输入None会报错
equip_set_all.remove(equip_set2_c)
else:
if equip_set4 in equip_set_all: # 别删这个if,否则输入None会报错
equip_set_all.remove(equip_set4)
if equip_set_all is not None: # 全空则跳过
lf = pl.scan_csv(EQUIP_2PC_DATA_PATH)
for equip_2pc in equip_set_all:
if bool(equip_2pc): # 若二件套非空,则继续
row: list[dict] = lf.filter(pl.col("set_ID") == equip_2pc).collect().to_dicts()
if row:
row_0 = row[0]
self.__mapping_csv_to_attr(row_0)
else:
raise ValueError(f"套装 {equip_2pc} 不存在")
def _init_main_stats(self, drive4: str | None, drive5: str | None, drive6: str | None):
"""初始化主词条"""
drive_parts = [drive4, drive5, drive6]
# 初始化1-3号位
self.HP_numeric += 2200
self.ATK_numeric += 316
self.DEF_numeric += 184
# 匹配4-6号位
for drive in drive_parts:
match drive:
case "生命值%" | "生命值":
self.HP_percent += 0.3
case "攻击力%" | "攻击力":
self.ATK_percent += 0.3
case "防御力%" | "防御力":
self.DEF_percent += 0.48
case "暴击率%" | "暴击率":
if self.crit_balancing:
self.baseCRIT_score += 48
else:
self.CRIT_rate_numeric += 0.24
case "暴击伤害%" | "暴击伤害":
if self.crit_balancing:
self.baseCRIT_score += 48
else:
self.CRIT_damage_numeric += 0.48
case "异常精通":
self.AP_numeric += 92
case "穿透率%" | "穿透率":
self.PEN_ratio += 0.24
case "冰属性伤害%" | "冰属性伤害":
self.ICE_DMG_bonus += 0.3
case "火属性伤害%" | "火属性伤害":
self.FIRE_DMG_bonus += 0.3
case "电属性伤害%" | "电属性伤害":
self.ELECTRIC_DMG_bonus += 0.3
case "以太属性伤害%" | "以太属性伤害":
self.ETHER_DMG_bonus += 0.3
case "物理属性伤害%" | "物理属性伤害":
self.PHY_DMG_bonus += 0.3
case "异常掌控":
self.AM_percent += 0.3
case "冲击力%" | "冲击力":
self.IMP_percent += 0.18
case "能量自动回复%" | "能量自动回复":
self.sp_regen_percent += 0.6
case None:
continue
case "None" | "-" | "" | "0":
continue
case _:
raise ValueError(f"提供的主词条名称 {drive} 不存在")
def _init_sub_stats(
self,
scATK_percent: int | float = 0,
scATK: int | float = 0,
scHP_percent: int | float = 0,
scHP: int | float = 0,
scDEF_percent: int | float = 0,
scDEF: int | float = 0,
scAnomalyProficiency: int | float = 0,
scPEN: int | float = 0,
scCRIT: int | float = 0,
scCRIT_DMG: int | float = 0,
*,
DMG_BONUS: int | float = 0,
PEN_RATIO: int | float = 0,
ANOMALY_MASTERY: int | float = 0,
SP_REGEN: int | float = 0,
):
"""初始化副词条"""
self.ATK_percent += scATK_percent * SUB_STATS_MAPPING["scATK_percent"]
self.ATK_numeric += scATK * SUB_STATS_MAPPING["scATK"]
self.HP_percent += scHP_percent * SUB_STATS_MAPPING["scHP_percent"]
self.HP_numeric += scHP * SUB_STATS_MAPPING["scHP"]
self.DEF_percent += scDEF_percent * SUB_STATS_MAPPING["scDEF_percent"]
self.DEF_numeric += scDEF * SUB_STATS_MAPPING["scDEF"]
self.AP_numeric += scAnomalyProficiency * SUB_STATS_MAPPING["scAnomalyProficiency"]
self.PEN_numeric += scPEN * SUB_STATS_MAPPING["scPEN"]
if self.crit_balancing:
self.baseCRIT_score += (
(scCRIT * SUB_STATS_MAPPING["scCRIT"]) * 2
+ (scCRIT_DMG * SUB_STATS_MAPPING["scCRIT_DMG"])
) * 100
else:
self.CRIT_rate_numeric += scCRIT * SUB_STATS_MAPPING["scCRIT"]
self.CRIT_damage_numeric += scCRIT_DMG * SUB_STATS_MAPPING["scCRIT_DMG"]
# Only for parallel
element_dmg_mapping = {
0: self.PHY_DMG_bonus,
1: self.FIRE_DMG_bonus,
2: self.ICE_DMG_bonus,
3: self.ELECTRIC_DMG_bonus,
4: self.ETHER_DMG_bonus,
5: self.ICE_DMG_bonus, # 烈霜也是冰
6: self.ETHER_DMG_bonus,
}
element_dmg_mapping[self.element_type] += DMG_BONUS * SUB_STATS_MAPPING["DMG_BONUS"]
self.PEN_ratio += PEN_RATIO * SUB_STATS_MAPPING["PEN_RATIO"]
self.AM_percent += ANOMALY_MASTERY * SUB_STATS_MAPPING["ANOMALY_MASTERY"]
self.sp_regen_percent += SP_REGEN * SUB_STATS_MAPPING["SP_REGEN"]
def hardset_sub_stats(
self,
scATK_percent: int | float | None = None,
scATK: int | float | None = None,
scHP_percent: int | float | None = None,
scHP: int | float | None = None,
scDEF_percent: int | float | None = None,
scDEF: int | float | None = None,
scAnomalyProficiency: int | float | None = None,
scPEN: int | float | None = None,
scCRIT: int | float | None = None,
scCRIT_DMG: int | float | None = None,
*,
DMG_BONUS: int | float | None = None,
PEN_RATIO: int | float | None = None,
ANOMALY_MASTERY: int | float | None = None,
SP_REGEN: int | float | None = None,
):
"""硬设置副词条,仅修改传入的参数对应的属性"""
if scATK_percent is not None:
self.ATK_percent = scATK_percent * SUB_STATS_MAPPING["scATK_percent"]
if scATK is not None:
self.ATK_numeric = scATK * SUB_STATS_MAPPING["scATK"]
if scHP_percent is not None:
self.HP_percent = scHP_percent * SUB_STATS_MAPPING["scHP_percent"]
if scHP is not None:
self.HP_numeric = scHP * SUB_STATS_MAPPING["scHP"]
if scDEF_percent is not None:
self.DEF_percent = scDEF_percent * SUB_STATS_MAPPING["scDEF_percent"]
if scDEF is not None:
self.DEF_numeric = scDEF * SUB_STATS_MAPPING["scDEF"]
if scAnomalyProficiency is not None:
self.AP_numeric = scAnomalyProficiency * SUB_STATS_MAPPING["scAnomalyProficiency"]
if scPEN is not None:
self.PEN_numeric = scPEN * SUB_STATS_MAPPING["scPEN"]
if self.crit_balancing:
if scCRIT is not None or scCRIT_DMG is not None:
current_score = self.baseCRIT_score
if scCRIT is not None:
current_score += scCRIT * SUB_STATS_MAPPING["scCRIT"] * 2 * 100
if scCRIT_DMG is not None:
current_score += scCRIT_DMG * SUB_STATS_MAPPING["scCRIT_DMG"] * 100
self.baseCRIT_score = current_score
else:
if scCRIT is not None:
self.CRIT_rate_numeric = scCRIT * SUB_STATS_MAPPING["scCRIT"]
if scCRIT_DMG is not None:
self.CRIT_damage_numeric = scCRIT_DMG * SUB_STATS_MAPPING["scCRIT_DMG"]
# Only for parallel
if DMG_BONUS is not None:
element_dmg_mapping = {
0: "PHY_DMG_bonus",
1: "FIRE_DMG_bonus",
2: "ICE_DMG_bonus",
3: "ELECTRIC_DMG_bonus",
4: "ETHER_DMG_bonus",
5: "ICE_DMG_bonus", # 烈霜也是冰
6: "ETHER_DMG_bonus", # 玄墨也是以太
}
setattr(
self,
element_dmg_mapping[self.element_type],
DMG_BONUS * SUB_STATS_MAPPING["DMG_BONUS"],
)
if PEN_RATIO is not None:
self.PEN_ratio = PEN_RATIO * SUB_STATS_MAPPING["PEN_RATIO"]
if ANOMALY_MASTERY is not None:
self.AM_percent = ANOMALY_MASTERY * SUB_STATS_MAPPING["ANOMALY_MASTERY"]
if SP_REGEN is not None:
self.sp_regen_percent = SP_REGEN * SUB_STATS_MAPPING["SP_REGEN"]
def __init_attr_curve_config(self, parallel_config: ExecAttrCurveCfg):
if not isinstance(parallel_config, ExecAttrCurveCfg):
return
ALLOW_SC_LIST: list[str] = list(SUB_STATS_MAPPING.keys())
sc_name, sc_value = parallel_config.sc_name, parallel_config.sc_value
if sc_name in ALLOW_SC_LIST:
adjust_pair = {sc_name: sc_value}
else:
raise RuntimeError(f"Parallel Config Segfault: sc_name: {sc_name} do not exist")
self.hardset_sub_stats(**adjust_pair)
def update_sp_and_decibel(self, *args, **kwargs):
"""自然更新能量和喧响的方法"""
# Preload Skill
skill_nodes: list[SkillNode] = _skill_node_filter(*args, **kwargs)
for node in skill_nodes:
# SP
self.update_single_node_sp(node)
# SP recovery over time
self.update_sp_overtime(args, kwargs)
def update_sp_overtime(self, args, kwargs):
"""处理当前tick的自然回能"""
sp_regen_data: list[SPUpdateData] = _sp_update_data_filter(*args, **kwargs)
for mul in sp_regen_data:
if mul.char_name == self.NAME:
sp_change_2 = mul.get_sp_regen() / 60 # 每秒回能转化为每帧回能
self.update_sp(sp_change_2)
def update_single_node_sp(self, node):
"""处理单个skill_node的回能"""
if node.char_name == self.NAME:
sp_consume = node.skill.sp_consume
sp_threshold = node.skill.sp_threshold
sp_recovery = node.skill.sp_recovery
if self.sp < sp_threshold:
print(
f"{node.skill_tag}需要{sp_threshold:.2f}点能量,目前{self.NAME}仅有{self.sp:.2f}点,需求无法满足,请检查技能树"
)
sp_change = sp_recovery - sp_consume
self.update_sp(sp_change)
# Decibel
self.process_single_node_decibel(node)
def process_single_node_decibel(self, node):
allowed_list = ["1371_Q_A"]
if (
self.NAME == node.char_name
and node.skill_tag.split("_")[1] == "Q"
and node.skill_tag not in allowed_list
):
if self.decibel - 3000 <= -1e-5:
print(
f"{self.NAME} 释放大招时喧响值不足3000,目前为{self.decibel:.2f}点,请检查技能树"
)
self.decibel = 0
else:
# 计算喧响变化值
decibel_change = node.skill.self_fever_re
# 如果喧响变化值大于0,则更新喧响值
if decibel_change > 0:
# 如果不是自身技能,倍率折半
if node.char_name != self.NAME:
decibel_change *= 0.5
# 更新喧响值
self.update_decibel(decibel_change)
def update_sp(self, sp_value: int | float):
"""可全局强制更新能量的方法"""
self.sp += sp_value
self.sp = max(0.0, min(self.sp, self.sp_limit))
def update_decibel(self, decibel_value: int | float):
"""可外部强制更新喧响的方法"""
# if self.decibel == 3000 and self.NAME == '仪玄':
# print(f"{self.NAME} 释放技能时喧响值已满3000点!")
from zsim.sim_progress.ScheduledEvent.Calculator import cal_buff_total_bonus
dynamic_buff = self.sim_instance.global_stats.DYNAMIC_BUFF_DICT
enabled_buff = tuple(dynamic_buff[self.NAME])
buff_bonus_dict = cal_buff_total_bonus(
enabled_buff=enabled_buff, judge_obj=None, sim_instance=self.sim_instance
)
decibel_get_ratio = buff_bonus_dict.get("喧响获得效率", 0)
final_decibel_change_value = decibel_value * (1 + decibel_get_ratio)
self.decibel += final_decibel_change_value
# print(final_decibel_change_value, decibel_value, decibel_get_ratio)
self.decibel = max(0.0, min(self.decibel, 3000))
def special_resources(self, *args, **kwargs) -> None:
"""父类中不包含默认特殊资源"""
return None
def get_resources(self) -> tuple[str | None, int | float | bool | None]:
"""获取特殊资源的属性名称与数量"""
return None, None
def get_special_stats(self, *args, **kwargs) -> dict[str | None, object | None]:
"""获取全部特殊属性的名称与数值"""
result: dict[str | None, object | None] = {}
return result
def __str__(self) -> str:
return f"{self.NAME} {self.level}级,能量{self.sp:.2f},喧响{self.decibel:.2f}"
def reset_myself(self):
# 重置能量、喧响值
self.sp: float = 40.0
self.decibel: float = 1000.0
# 重置动态属性
self.dynamic.reset()
def refresh_myself(self):
"""部分角色身上存在每个tick更新一次的数据结构,所以这里提供一个统一的对外调用接口。
目前这个接口是被Schedule阶段调用的。"""
return None
def __deepcopy__(self, memo):
return self
def personal_action_replace_strategy(self, action: str):
return action
def POST_INIT_DATA(self, sim_instance: "Simulator"):
pass
class LastingNode:
def __init__(self, char_instance: Character):
"""用于记录和管理角色持续释放技能的状态节点
该类负责追踪角色的技能释放状态,包括连续释放同一技能的情况和技能被打断的处理。
属性:
char_instance (Character): 关联的角色实例
node (SkillNode): 当前正在执行的技能节点,初始为None
start_tick (int): 开始释放技能的时间点
update_tick (int): 最近一次更新状态的时间点
is_spamming (bool): 是否处于连续释放同一技能的状态
repeat_times (int): 连续释放同一技能的次数
"""
self.char_instance = char_instance
self.node = None
self.start_tick = 0
self.update_tick = 0
self.is_spamming = False # 是否处于连续释放技能的状态
self.repeat_times = 0
def reset(self):
"""重置所有状态参数到初始值
在需要清除当前技能状态时调用,比如切换角色或战斗结束时
"""
self.node = None
self.start_tick = 0
self.update_tick = 0
self.is_spamming = False
self.repeat_times = 0
def update_node(self, node, tick: int):
"""更新技能节点状态
处理技能节点的更新逻辑,包括:
1. 处理与其他角色节点的交互
2. 处理技能被打断的情况
3. 处理连续释放同一技能的状态更新
4. 处理技能切换的逻辑
参数:
node (SkillNode): 新的技能节点
tick (int): 当前时间点
异常:
ValueError: 当尝试过早更新节点时抛出
"""
# 若传入动作不是自己的技能
# from zsim.sim_progress.Preload import SkillNode
# assert isinstance(node, SkillNode)
if node.is_additional_damage and node.skill.ticks == 0:
# 若传入的动作是0帧的附加伤害,由于这些技能很明显是不需要角色通过某些动画动作来释放的,
# 所以这里就不更新lasting_node,以保证不会因为0帧技能而导致lasting_node的数据被污染。
return
if node.char_name != self.char_instance.NAME:
if self.node is None:
# 若此时自己没有技能,则直接返回
return
if self.is_spamming and self.node.end_tick <= tick:
# 若此时自己正在持续释放某技能但是该技能已经结束,则结束技能释放状态、清空技能节点,重置参数;
self.is_spamming = False
self.node = None
self.update_tick = tick
self.repeat_times = 0
return
else:
# 若传入动作是自己的技能
if self.node is None:
# 若此时自己没有登记中的技能,那么就登记当前技能,并且更新参数
self.node = node
self.start_tick = tick
self.update_tick = tick
self.repeat_times = 1
return
# 若此时自己有正在进行中的技能
if node.skill_tag in ["被打断", "发呆"]:
# 若此时技能是“被打断”或是“发呆”,则进行参数更新,并且关闭spamming参数;
self.is_spamming = False
self.node = node
self.start_tick = tick
self.update_tick = tick
self.repeat_times = 0
return
else:
# 若此时传入技能是其他正常技能,则需要进行判断
if self.node.end_tick > tick and node.active_generation:
# 若已经登记的技能尚未结束,且新传入技能是主动释放,那需要进行验错——理论上,APL不会在角色当前尚还有动作时放行一个新技能。
if not self.node.skill.do_immediately and node.skill.do_immediately:
# 若已登记技能并非高优先级,而传入技能为高优先级,则说明是发生了技能顶替(比如大招顶替自己的平A),这是一个正常情况,所以不进入报错分支;
pass
else:
if "dodge" in self.node.skill_tag:
# 若已经登记技能为闪避,那么此时无论传入什么技能,都不进入报错分支——因为闪避是可以被任意取消的
pass
else:
# 其他的情况则说明APL模块确实出现了错误,报错。
raise ValueError(
f"过早传入了node{node.skill_tag},当前node{self.node.skill_tag}为{self.node.preload_tick}开始 {self.node.end_tick}结束,\n"
f"但是{node.skill_tag}的企图在{tick}tick进行更新,它预计从{node.preload_tick}开始 {node.end_tick}结束!"
)
# 在验错环节结束后,正式进行技能信息的更新、替换;
if self.node.skill_tag == node.skill_tag:
# 若传入技能和已登记技能一致,则直接更新参数
self.is_spamming = True
self.repeat_times += 1
else:
# 若传入技能和已登记技能不一致,则关闭spamming参数,并更新技能信息
self.is_spamming = False
self.start_tick = tick
self.repeat_times = 1
# 无论如何,node以及update_tick参数都会更新
self.node = node
self.update_tick = tick
def spamming_info(self, tick: int):
"""获取当前技能持续释放的状态信息
参数:
tick (int): 当前时间点
返回:
tuple: (是否连续释放中, 技能标签, 持续时间, 重复次数)
"""
lasting_tick = tick - self.start_tick
if self.node is None:
skill_tag = None
else:
skill_tag = self.node.skill_tag
return self.is_spamming, skill_tag, lasting_tick, self.repeat_times
if __name__ == "__main__":
pass
================================================
FILE: zsim/sim_progress/Character/skill_class.py
================================================
import ast
from functools import lru_cache
import polars as pl
from zsim.define import (
CHARACTER_DATA_PATH,
DEFAULT_SKILL_PATH,
SKILL_DATA_PATH,
ElementType,
)
from zsim.sim_progress import Report
try:
# 读取角色数据
char_lf = pl.scan_csv(CHARACTER_DATA_PATH)
except Exception as e:
raise IOError(f"无法读取文件 {CHARACTER_DATA_PATH}: {e}")
@lru_cache(maxsize=64)
def lookup_name_or_cid(name: str = "", cid: int | str | None = None) -> tuple[str, int]:
"""
初始化角色名称和CID(角色ID)。
这个方法用于验证和确定角色的名称和CID。它可以根据提供的名称或CID来查找
对应的角色信息,并确保提供的名称和CID匹配。如果只提供了名称或CID,它将
尝试从 ./data/character.csv 中查找对应的CID或名称。
参数:
- name:str 角色的名称。
- CID:int 角色的ID。
示例:
self.NAME, self.CID = lookup_name_or_cid(name, cid)
返回:
- 一个包含角色名称和CID的元组。
异常:
- ValueError: 提供的名称和CID不匹配,或者角色不存在。
- IOError: 角色数据库常量 CHARACTER_DATA_PATH 有误
- SystemError: 无法处理提供的参数。
"""
global char_lf
# 查找角色信息
if name != "":
result = char_lf.filter(pl.col("name") == name).collect().to_dicts()
elif cid is not None:
# 确保cid是整数
cid_int = int(cid) if cid is not None else None
result = char_lf.filter(pl.col("CID") == cid_int).collect().to_dicts()
else:
raise ValueError("角色名称与ID必须至少提供一个")
if not result:
raise ValueError("角色不存在")
character_info = result[0]
# 检查传入的name与CID是否匹配
if name is not None and cid is not None:
if int(character_info["CID"]) != int(cid):
raise ValueError("传入的name与CID不匹配")
return character_info["name"], int(character_info["CID"])
class Skill:
def __init__(
self,
name: str = "",
CID: int | str | None = None,
normal_level=12,
special_level=12,
dodge_level=12,
chain_level=12,
assist_level=12,
core_level=6,
char_obj=None,
):
"""
根据提供的角色、各技能等级,创建一个角色的技能对象。
成功创建的对象会包含角色的名称、ID、核心技等级、包含全部技能的字典
skills_dict:
-keys: 该角色的全部技能标签(skill_tag)
-values: 包含全部属性的 InitSkill 对象,可使用getattr()方法调用
方法 __create_action_list():
-检查 self.skill_dict 中是否包含闪避、正向切人、反向切人、被打断、发呆
-有,则使用自身的技能
-没有,则使用自带 module,初始化这些动作
-返回仅包含动作名称的列表
方法 get_skill_info():
-在仅输入技能标签(skill_tag)时,返回该技能的 InitSkill 对象
-在同时输入技能标签(skill_tag)和所需属性时(attr_info)时,返回该技能对象的指定属性
以下两个标识符必须提供至少一个:\n
name:str 角色名称\n
CID:int 角色的ID
调用示例:
test_object = Skill(name='艾莲')
action_list = test_object.action_list # 获取动作列表
skills_dict = test_object.skills_dict # 获取技能字典
skill_0: Skill.InitSkill = test_object.skills_dict[action_list[0]] # 获取第一个动作对应的技能对象
skill_0.damage_ratio # 获取第一个动作的伤害倍率—方式1
test_object.get_skill_info(skill_tag=action_list[0], attr_info='damage_ratio') # 获取第一个动作的伤害倍率-方式2
"""
# 初始化时确保CID被转换为整数或None
cid_int = int(CID) if CID is not None else None
# 初始化角色名称和CID
self.name, self.CID = lookup_name_or_cid(name, cid_int)
# 核心技等级需要可读
self.core_level = core_level
self.skill_level_dict = {
"normal": normal_level,
"special": special_level,
"dodge": dodge_level,
"chain": chain_level,
"assist": assist_level,
"core": core_level,
} # 技能等级字典
# 最晚在这里创建DataFrame,优化不一点点,这玩意可大了
schema_dict = {
"CID": int,
"name": str,
"CN_TriggerLevel": str,
"skill_tag": str,
"CN_skill_tag": str,
"skill_text": str,
"INSTRUCTION": str,
"damage_ratio": float,
"damage_ratio_growth": float,
"D_LEVEL12": float,
"D_LEVEL14": float,
"D_LEVEL16": float,
"stun_ratio": float,
"stun_ratio_growth": float,
"S_LEVEL12": float,
"S_LEVEL14": float,
"S_LEVEL16": float,
"sp_threshold": int,
"sp_consume": int,
"sp_recovery": float,
"adrenaline_recovery": float,
"adrenaline_threshold": float,
"adrenaline_consume": float,
"fever_recovery": float,
"self_fever_re": float,
"distance_attenuation": int,
"initial_level": int,
"anomaly_accumulation": float,
"skill_type": int,
"trigger_buff_level": int,
"element_type": int,
"element_damage_percent": float,
"diff_multiplier": float,
"ticks": int,
"hit_times": int,
"on_field": bool,
"anomaly_attack": bool,
"interruption_resistance": int,
"swap_cancel_ticks": int,
"labels": str,
"follow_up": str,
"follow_by": str,
"aid_direction": int,
"aid_lag_ticks": int,
"tick_list": str,
"force_add_condition_APL": str,
"heavy_attack": bool,
"max_repeat_times": int,
"do_immediately": bool,
"anomaly_update_list": str,
}
all_skills_lf = pl.scan_csv(SKILL_DATA_PATH, schema_overrides=schema_dict)
# 根据CID提取角色的技能数据
try:
self.skill_df = all_skills_lf.filter(pl.col("CID") == self.CID).collect()
# 如果没有找到对应CID,则报错
if self.skill_df.is_empty():
raise ValueError(f"找不到CID为 {self.CID} 的角色信息")
# 提取dataframe中,每个索引为skill_tag的值,保存为keys
else:
__keys = self.skill_df["skill_tag"].unique()
except KeyError:
print(f"{SKILL_DATA_PATH} 中缺少 'skill_tag' 列") # 虽然不可能
return
except ValueError as e:
print(e)
return
# 创建技能字典与技能列表 self.skills_dict 与 self.action_list
self.skills_dict = {} # {技能名str:技能参数object:InitSkill}
self.char_obj = char_obj
for key in __keys:
skill = self.InitSkill(
skill_dataframe=self.skill_df,
key=key,
normal_level=normal_level,
special_level=special_level,
dodge_level=dodge_level,
chain_level=chain_level,
assist_level=assist_level,
core_level=core_level,
CID=self.CID,
char_name=self.name,
char_obj=self.char_obj,
)
self.skills_dict[key] = skill
self.action_list = self.__create_action_list()
def get_skill_info(self, skill_tag: str, attr_info: str | None = None):
"""
-在仅输入技能标签(skill_tag)时,返回该技能的 InitSkill 对象\n
-在同时输入技能标签(skill_tag)和所需属性时(attr_info)时,返回该技能对象的指定属性
"""
skill_info: Skill.InitSkill = self.skills_dict[skill_tag]
if attr_info is None:
return skill_info
else:
value = getattr(skill_info, attr_info)
if value:
return value
else:
return None
def __create_action_list(self):
"""
创建动作列表并检查初始化状态
此函数旨在为角色或实体创建一个动作列表,并检查这些动作是否已经初始化。
它通过检查技能字典(skills_dict)中的键来确定哪些动作已经存在,如果不存在(即未初始化),
则会创建这些动作的默认实例。
"""
# 定义需要检查是否初始化的动作列表
default_actions_dataframe = pl.read_csv(DEFAULT_SKILL_PATH)
by_default_actions = default_actions_dataframe["skill_tag"].unique()
# 初始化每个动作的状态为 True
init_actions = {action: True for action in by_default_actions}
# 遍历 skills_dict 的键
for key in self.skills_dict.keys():
# 检查键中是否包含某个动作
for action in by_default_actions:
if action in key:
# 如果包含,则将对应动作的状态设为 False
init_actions[action] = False
# 遍历每个动作及其初始化状态
for action, init in init_actions.items():
# 如果某个动作未被初始化,则创建对应的 Skill 对象并添加到 skills_dict
if init:
self.skills_dict[f"{self.CID}_{action}"] = Skill.InitSkill(
default_actions_dataframe,
key=action,
char_name=self.name,
CID=self.CID,
char_obj=self.char_obj,
)
return list(self.skills_dict.keys())
class InitSkill:
def __init__(
self,
skill_dataframe: pl.DataFrame,
key,
char_name: str,
normal_level=12,
special_level=12,
dodge_level=12,
chain_level=12,
assist_level=12,
core_level=6,
CID=0,
char_obj=None,
):
"""
初始化角色的单个技能。
会在执行class Skill的时候自动调用,不用手动创建此类的对象
继承自此类的对象会包含输入的技能(key)的全部属性
"""
self.char_obj = char_obj
# 提取数据库内,该技能的数据
_raw_skill_data = skill_dataframe.filter(pl.col("skill_tag") == key).to_dicts()
if not _raw_skill_data:
raise ValueError("未找到技能")
else:
_raw_skill_data = _raw_skill_data[0]
# 如果不是 攻击力/生命值/防御力/精通 倍率,报错,未来可接复杂逻辑
self.diff_multiplier = int(_raw_skill_data["diff_multiplier"])
if _raw_skill_data["diff_multiplier"] not in [0, 1, 2, 3, 4]:
raise ValueError("目前只支持 攻击力/生命值/防御力/精通/贯穿力 倍率")
self.char_name: str = char_name
# 储存技能Tag
self.cid = CID
self.skill_tag = f"{CID}_{key}" if str(CID) not in key else key
self.CN_skill_tag: str = _raw_skill_data["CN_skill_tag"]
self.skill_text: str = _raw_skill_data["skill_text"]
# 确定使用的技能等级
self.skill_type: int = int(_raw_skill_data["skill_type"])
self.skill_level: int = self.__init_skill_level(
self.skill_type,
normal_level,
special_level,
dodge_level,
chain_level,
assist_level,
core_level,
)
# 确定伤害倍率
damage_ratio = float(_raw_skill_data["damage_ratio"])
damage_ratio_growth = float(_raw_skill_data["damage_ratio_growth"])
self.damage_ratio: float = damage_ratio + damage_ratio_growth * (self.skill_level - 1)
# 确定失衡倍率
stun_ratio = float(_raw_skill_data["stun_ratio"])
stun_ratio_growth = float(_raw_skill_data["stun_ratio_growth"])
self.stun_ratio: float = stun_ratio + stun_ratio_growth * (self.skill_level - 1)
# 能量相关属性
self.sp_threshold: float = float(_raw_skill_data["sp_threshold"])
self.sp_consume: float = float(_raw_skill_data["sp_consume"])
self.sp_recovery: float = float(_raw_skill_data["sp_recovery"])
# 闪能相关——仪玄专属
self.adrenaline_threshold: float = float(_raw_skill_data["adrenaline_threshold"])
self.adrenaline_consume: float = float(_raw_skill_data["adrenaline_consume"])
self.adrenaline_recovery: float = float(_raw_skill_data["adrenaline_recovery"])
# 喧响值
self.self_fever_re: float = float(_raw_skill_data["self_fever_re"])
# 距离衰减,不知道有啥用
self.distance_attenuation: int = int(_raw_skill_data["distance_attenuation"])
# 属性异常蓄积值,直接转化为浮点
self.anomaly_accumulation: float = float(_raw_skill_data["anomaly_accumulation"]) / 100
# TriggerBuffLevel
self.trigger_buff_level: int = int(_raw_skill_data["trigger_buff_level"])
# 元素相关
self.element_type: ElementType = _raw_skill_data["element_type"]
self.element_damage_percent: float = float(_raw_skill_data["element_damage_percent"])
# 动画相关
ticks_str = _raw_skill_data["ticks"]
if ticks_str is None or ticks_str == "test":
print(f"检测到技能 {self.skill_tag}的ticks参数不正确,已设置为默认值60")
self.ticks = 60
else:
self.ticks: int = int(_raw_skill_data["ticks"])
temp_hit_times = int(_raw_skill_data["hit_times"])
self.hit_times: int = temp_hit_times if temp_hit_times > 0 else 1
self.on_field: bool = bool(_raw_skill_data["on_field"])
self.anomaly_attack: bool = bool(_raw_skill_data["anomaly_attack"])
# 特殊标签
labels_str = _raw_skill_data["labels"]
if labels_str is None or not str(labels_str).strip(): # 判断空值或空字符串
labels = None
else:
# 去除首尾空格后尝试解析字典
labels = ast.literal_eval(str(labels_str).strip())
self.labels: dict | None = labels # 技能特殊标签
# if self.labels:
# pass
# TODO:抗打断标签;无敌标签
# 技能链相关
__swap_cancel_ticks_value = _raw_skill_data["swap_cancel_ticks"]
if __swap_cancel_ticks_value is None:
self.swap_cancel_ticks = 0
else:
self.swap_cancel_ticks: int = int(
_raw_skill_data["swap_cancel_ticks"]
) # 可执行合轴操作的最短时间
follow_up = _raw_skill_data["follow_up"]
if follow_up is None:
self.follow_up: list = []
else:
self.follow_up: list = _raw_skill_data["follow_up"].split(
"|"
) # 技能发动后强制衔接的技能标签
follow_by = _raw_skill_data["follow_by"]
if follow_by is None:
self.follow_by: list = []
else:
self.follow_by: list = _raw_skill_data["follow_by"].split(
"|"
) # 发动技能必须的前置技能标签
self.aid_direction: int = _raw_skill_data["aid_direction"] # 触发快速支援的方向
aid_lag_ticks_value = _raw_skill_data["aid_lag_ticks"]
if aid_lag_ticks_value == "inf":
self.aid_lag_ticks = self.ticks - 1
elif aid_lag_ticks_value is None:
self.aid_lag_ticks = 0
else:
self.aid_lag_ticks: int = int(
_raw_skill_data["aid_lag_ticks"]
) # 技能激活快速支援的滞后时间
tick_value = _raw_skill_data["tick_list"]
if tick_value is None:
self.tick_list = None
elif isinstance(tick_value, str):
# 处理空字符串或纯空格
if not tick_value.strip():
self.tick_list = None
else:
try:
# 转换并去除首尾空格
self.tick_list = ast.literal_eval(str(tick_value).strip())
# self.tick_list = [int(v.strip()) for v in split_values]
except ValueError as e:
raise ValueError(f"{self.skill_tag} 的 tick_list 包含无效整数: {e}")
else:
# 处理非字符串类型(如意外数值)
self.tick_list = None
if self.tick_list:
if max(self.tick_list) >= self.ticks:
raise ValueError(
f"{self.skill_tag}的精确帧数分布的最大值超过技能总帧数!请检查数据正确性,{self.tick_list, self.ticks}"
)
if len(self.tick_list) != self.hit_times:
raise ValueError(
f"{self.skill_tag}的精确帧数分布所包含的命中数与技能的命中总数不符!请检查数据正确性,{self.tick_list, self.hit_times}"
)
self.ratio_distribution: list | None = None # 技能的精确倍率分布
# _raw_skill_data['ratio_distribution'].split(':') if _raw_skill_data['ratio_distribution'] else None
self.force_add_condition_APL = []
condition_value = _raw_skill_data["force_add_condition_APL"]
if condition_value is None:
self.force_add_condition_APL = []
else:
from zsim.sim_progress.Preload.apl_unit.APLUnit import (
SimpleUnitForForceAdd,
)
condition_list = condition_value.strip().split(";")
for _cond_str in condition_list:
_cond_list = _cond_str.strip().split("|")
simple_apl_unit_for_force_add = SimpleUnitForForceAdd(condition_list=_cond_list)
self.force_add_condition_APL.append(simple_apl_unit_for_force_add)
if (
len(self.follow_up) != len(self.force_add_condition_APL)
and self.force_add_condition_APL
):
raise ValueError(
f"ID为{self.skill_tag}的技能的follow_up与force_add_condition_APL长度不一致!请检查数据正确性"
)
self.skill_attr_dict = {
attr: getattr(self, attr)
for attr in dir(self)
if not attr.startswith("__") and not callable(getattr(self, attr))
}
self.heavy_attack: bool = bool(_raw_skill_data["heavy_attack"])
__max_repeat_times_value = _raw_skill_data["max_repeat_times"]
if __max_repeat_times_value is None:
self.max_repeat_times = 1
else:
self.max_repeat_times: int = int(
_raw_skill_data["max_repeat_times"]
) # 最大重复释放次数。
"""
技能是否立刻执行,大部分技能都是False,目前只有QTE和大招具有这种属性。
该属性会在APL部分的SwapCancelEngine中被用到,用于检测角色已有的动作是否会被新动作打断。
"""
self.do_immediately: bool = bool(_raw_skill_data["do_immediately"])
self.anomaly_update_rule: (
list[int] | int | None
) = [] # 更新异常的模式,如果不填,那就是最后一跳,如果有填写,那就按照填写的跳数来更新。
anomaly_update_list_str = _raw_skill_data["anomaly_update_list"]
self._process_anomaly_update_rule(anomaly_update_list_str)
Report.report_to_log(f"[Skill INFO]:{self.skill_tag}:{str(self.skill_attr_dict)}")
def _process_anomaly_update_rule(self, anomaly_update_list_str):
"""
初始化 异常更新规则 :
1、不填,则就返回[],那就按照最后一跳处理;
2、-1,则返回-1,那就按照每一跳处理;
3、a&b&c&d, 则返回[a, b, c, d],那就按照这些跳数处理
"""
if anomaly_update_list_str is None:
self.anomaly_update_rule = [] # None代表更新节点是最后一跳
else:
try:
anomaly_update_mode = int(anomaly_update_list_str)
if anomaly_update_mode == -1:
self.anomaly_update_rule = anomaly_update_mode
else:
if anomaly_update_mode > self.hit_times:
raise ValueError(
f"{self.skill_tag}的更新节点大于技能总帧数!请检查数据正确性"
)
self.anomaly_update_rule = [anomaly_update_mode]
except ValueError:
self.anomaly_update_rule = anomaly_update_list_str.split("&")
if (
isinstance(self.anomaly_update_rule, list)
and len(self.anomaly_update_rule) > self.hit_times
):
raise ValueError(f"{self.skill_tag}的更新节点总数大于技能总帧数!请检查数据正确性")
@staticmethod
def __init_skill_level(
skill_type: int,
normal_level: int,
special_level: int,
dodge_level: int,
chain_level: int,
assist_level: int,
core_level: int,
) -> int:
"""
根据 skill_type 选择对应的技能等级
参数:
- skill_type (int): 技能类型标签
- normal_level (int): 普攻等级
- special_level (int): 特殊技等级
- dodge_level (int): 闪避等级
- chain_level (int): 连携技等级
- assist_level (int): 支援技等级
- core_level (int): 核心被动等级
"""
skill_levels = {
0: normal_level,
1: special_level,
2: dodge_level,
3: chain_level,
4: core_level,
5: assist_level,
6: assist_level, # 暂时过度一下,防止报错
}
# FIXME:修复数据库中支援技skill_type的问题!
if skill_type in skill_levels:
return skill_levels[skill_type]
else:
raise ValueError(f"非法的技能种类(skill_type):{skill_type}")
def __str__(self) -> str:
return self.skill_tag
def __str__(self) -> str:
return self.name + "Skills"
if __name__ == "__main__":
test_object = Skill(name="艾莲")
test_object2 = Skill(CID=1221)
action_list = test_object.action_list # 获取动作列表
skills_dict = test_object.skills_dict # 获取技能字典
skill_0: Skill.InitSkill = test_object.skills_dict[
action_list[0]
] # 获取第一个动作对应的技能对象
print(skill_0.damage_ratio) # 获取第一个动作的伤害倍率
print(
test_object.get_skill_info(skill_tag=action_list[0], attr_info="damage_ratio")
) # 获取第一个动作的伤害倍率
================================================
FILE: zsim/sim_progress/Character/utils/__init__.py
================================================
================================================
FILE: zsim/sim_progress/Character/utils/filters.py
================================================
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import NewAnomaly
from zsim.sim_progress.data_struct import SPUpdateData
from zsim.sim_progress.Preload import SkillNode
from zsim.sim_progress.ScheduledEvent.Calculator import Calculator
def _skill_node_filter(*args, **kwargs) -> list["SkillNode"]:
"""过滤出输入的 SKillNode,并作为列表返回"""
from zsim.sim_progress.Preload import SkillNode
skill_nodes: list[SkillNode] = []
for arg in args:
if isinstance(arg, SkillNode):
skill_nodes.append(arg)
for value in kwargs.values():
if isinstance(value, SkillNode):
skill_nodes.append(value)
return skill_nodes
def _multiplier_filter(*args, **kwargs) -> list[Calculator.MultiplierData]:
"""过滤出输入的 乘区数据,并作为列表返回"""
from zsim.sim_progress.ScheduledEvent.Calculator import Calculator
multiplier_data: list[Calculator.MultiplierData] = []
for arg in args:
if isinstance(arg, Calculator.MultiplierData):
multiplier_data.append(arg)
for value in kwargs.values():
if isinstance(value, Calculator.MultiplierData):
multiplier_data.append(value)
return multiplier_data
def _sp_update_data_filter(*args, **kwargs) -> list["SPUpdateData"]:
"""过滤出输入的 SPUpdateData,并作为列表返回"""
from zsim.sim_progress.data_struct import SPUpdateData
sp_update_data: list[SPUpdateData] = []
for arg in args:
if isinstance(arg, SPUpdateData):
sp_update_data.append(arg)
for value in kwargs.values():
if isinstance(value, SPUpdateData):
sp_update_data.append(value)
return sp_update_data
def _anomaly_filter(*args, **kwargs) -> list["NewAnomaly"]:
"""过滤出输入的异常类!并作为列表返回"""
from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import NewAnomaly
anomaly_bar_list: list[NewAnomaly] = []
for arg in args:
if isinstance(arg, NewAnomaly):
anomaly_bar_list.append(arg)
for value in kwargs.values():
if isinstance(value, NewAnomaly):
anomaly_bar_list.append(value)
return anomaly_bar_list
================================================
FILE: zsim/sim_progress/Dot/BaseDot.py
================================================
from dataclasses import dataclass
from typing import TYPE_CHECKING
from zsim.sim_progress.anomaly_bar import AnomalyBar
if TYPE_CHECKING:
from zsim.sim_progress.Preload import SkillNode
from zsim.simulator.simulator_class import Simulator
class Dot:
def __init__(
self,
bar: "AnomalyBar | None",
skill_tag: str | None = None,
sim_instance: "Simulator | None" = None,
):
self.sim_instance = sim_instance
self.ft = self.DotFeature(sim_instance=self.sim_instance)
self.dy = self.DotDynamic()
self.history = self.DotHistory()
# 默认情况下不创建anomlay_data。
self.anomaly_data = None
self.skill_node_data: "SkillNode | None" = None
if bar is not None and skill_tag is not None:
raise ValueError("Dot的构造函数不可以同时传入bar和skill_tag")
if bar:
self.anomaly_data = bar
if skill_tag:
from zsim.sim_progress.Buff import JudgeTools
from zsim.sim_progress.Preload.SkillsQueue import spawn_node
if self.sim_instance is None:
raise ValueError("sim_instance is None, but it should not be.")
preload_data = JudgeTools.find_preload_data(sim_instance=self.sim_instance)
tick = JudgeTools.find_tick(sim_instance=self.sim_instance)
self.skill_node_data = spawn_node(skill_tag, tick, preload_data.skills)
@dataclass
class DotFeature:
"""
这里记录了Dot的固定属性。
effect_rules属性记录了dot的更新规则。
更新指的是:更新自身时间、层数、内置CD等属性,同时也有可能是造成伤害。
0:无更新——只有效果,没有更新机制。
1:根据时间更新——完全依赖内置CD
2:命中时更新——依赖内置CD,同时需要外部进行“hit”判断,外部函数或许需要联动LoadingMission和TimeTick
3:缓存式更新——依赖内置CD,以及Dot.Dynamic中的动态记录模块,来记录伤害积累。
4:碎冰——只有含有重攻击的技能在end标签处才能触发。
"""
sim_instance: "Simulator | None"
update_cd: int | float = 0
index: str | None = None
name: str | None = None
dot_from: str | None = None
effect_rules: int | None = None
max_count: int | None = None
max_duration: int | None = None
incremental_step: int | None = None
max_effect_times: int = 30
count_as_skill_hit: bool = (
False # dot生效时的伤害能否视作技能的一次命中(从而参与其他的命中类dot的触发)
)
complex_exit_logic = False # 复杂的结束判定
def __str__(self):
return str(self.__dict__)
@dataclass
class DotDynamic:
start_ticks: int = 0
end_ticks: int = 0
last_effect_ticks: int = 0
active: bool | None = None
count: int = 0
ready: bool | None = None
effect_times: int = 0
@dataclass
class DotHistory:
start_times: int = 0
end_times: int = 0
last_start_ticks: int = 0
last_end_ticks: int = 0
last_duration: int = 0
def ready_judge(self, timenow: int):
if not self.dy.ready:
if timenow - self.dy.last_effect_ticks >= self.ft.update_cd:
self.dy.ready = True
def end(self, timenow: int):
self.dy.active = False
self.dy.count = 0
self.history.last_end_ticks = timenow
self.history.last_duration = timenow - self.dy.start_ticks
self.history.end_times += 1
def start(self, timenow: int):
self.dy.active = True
self.dy.start_ticks = timenow
self.dy.last_effect_ticks = timenow
if self.ft.max_duration is None:
raise ValueError(f"{self.ft.index}的最大持续时间为None,请检查初始化!")
self.dy.end_ticks = self.dy.start_ticks + self.ft.max_duration
self.history.start_times += 1
self.history.last_start_ticks = timenow
self.dy.count = 1
self.dy.effect_times = 1
self.dy.ready = False
def exit_judge(self, **kwargs) -> bool:
pass
================================================
FILE: zsim/sim_progress/Dot/Dots/AliceCoreSkillAssaultDot.py
================================================
from dataclasses import dataclass
from typing import TYPE_CHECKING
from .. import Dot
if TYPE_CHECKING:
from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar
from zsim.simulator.simulator_class import Simulator
class AliceCoreSkillAssaultDot(Dot):
def __init__(self, bar: "AnomalyBar | None" = None, sim_instance: "Simulator | None" = None):
super().__init__(bar=bar, sim_instance=sim_instance) # 调用父类Dot的初始化方法
self.ft = self.DotFeature(sim_instance=sim_instance)
if sim_instance is None:
raise ValueError("构造dot实例时必须传入有效的sim_instance实例")
from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar
# 正确性验证
if any(
[
bar is None,
bar is not None and not isinstance(bar, AnomalyBar),
bar.element_type != 0,
]
):
raise ValueError(
"构造爱丽丝的核心被动Dot实例时,必须传入有效的 物理属性 anomlay_bar 实例"
)
self.anomaly_data = bar
self.anomaly_data.rename_tag = "爱丽丝强击Dot"
self.anomaly_data.scaling_factor = 0.025 # 缩放比例
@dataclass
class DotFeature(Dot.DotFeature):
sim_instance: "Simulator | None"
update_cd: int | float = 57 # dot的内置CD是0.95秒,换算为57帧
index: str | None = "AliceCoreSkillAssaultDot"
name: str | None = "爱丽丝物理异常Dot"
dot_from: str | None = "爱丽丝"
effect_rules: int | None = 1
max_count: int | None = 999999
incremental_step: int | None = 1
max_duration: int | None = 999999
complex_exit_logic = True # 该dot为复杂退出逻辑
def exit_judge(self, **kwargs):
"""爱丽丝物理异常Dot 的退出逻辑:敌人的只要不处于畏缩状态,就退出。"""
enemy = kwargs.get("enemy", None)
from zsim.sim_progress.Enemy import Enemy
if not isinstance(enemy, Enemy):
raise TypeError("enemy参数必须是Enemy类的实例")
if not enemy.dynamic.assault:
self.dy.active = False
return True
return False
================================================
FILE: zsim/sim_progress/Dot/Dots/AuricInkCorruption.py
================================================
from dataclasses import dataclass, field
from typing import TYPE_CHECKING
from .. import Dot
if TYPE_CHECKING:
from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar
from zsim.simulator.simulator_class import Simulator
class AuricInkCorruption(Dot):
def __init__(self, bar: "AnomalyBar | None", sim_instance: "Simulator | None"):
super().__init__(bar, sim_instance=sim_instance) # 调用父类Dot的初始化方法
self.ft = self.DotFeature(sim_instance=sim_instance)
@dataclass
class DotFeature(Dot.DotFeature):
sim_instance: "Simulator | None"
char_name_box: list[str] = field(init=False)
update_cd: int | float = 30
index: str | None = "AuricInkCorruption"
name: str | None = "玄墨侵蚀"
dot_from: str | None = "enemy"
effect_rules: int | None = 2
max_count: int | None = 1
incremental_step: int | None = 1
max_duration: int | None = 600
max_effect_times = 30
def __post_init__(self):
if self.sim_instance is None:
raise ValueError("sim_instance is None, but it should not be.")
self.char_name_box = self.sim_instance.init_data.name_box
"""
如果某角色在角色列表里,侵蚀和最大生效次数就要发生变化。
"""
if "某角色" in self.char_name_box:
self.max_duration = 600 + 180
================================================
FILE: zsim/sim_progress/Dot/Dots/Corruption.py
================================================
from dataclasses import dataclass, field
from typing import TYPE_CHECKING
from .. import Dot
if TYPE_CHECKING:
from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar
from zsim.simulator.simulator_class import Simulator
class Corruption(Dot):
def __init__(self, bar: "AnomalyBar | None" = None, sim_instance: "Simulator | None" = None):
super().__init__(bar, sim_instance=sim_instance) # 调用父类Dot的初始化方法
self.ft = self.DotFeature(sim_instance=sim_instance)
@dataclass
class DotFeature(Dot.DotFeature):
sim_instance: "Simulator | None"
char_name_box: list[str] = field(init=False)
update_cd: int | float = 30
index: str | None = "Corruption"
name: str | None = "侵蚀"
dot_from: str | None = "enemy"
effect_rules: int | None = 2
max_count: int | None = 1
incremental_step: int | None = 1
max_duration: int | None = None
max_effect_times: int = 30
def __post_init__(self):
if self.sim_instance is None:
raise ValueError("sim_instance is None, but it should not be.")
self.char_name_box = self.sim_instance.init_data.name_box
self.max_duration = 600
"""
如果某角色在角色列表里,侵蚀和最大生效次数就要发生变化。
"""
if "某角色" in self.char_name_box:
self.max_duration = 600 + 180
================================================
FILE: zsim/sim_progress/Dot/Dots/Freez.py
================================================
from dataclasses import dataclass
from typing import TYPE_CHECKING
import numpy as np
from .. import Dot
if TYPE_CHECKING:
from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar
from zsim.simulator.simulator_class import Simulator
class Freez(Dot):
def __init__(self, bar: "AnomalyBar | None" = None, sim_instance: "Simulator | None" = None):
super().__init__(bar, sim_instance=sim_instance) # 调用父类Dot的初始化方法
self.ft = self.DotFeature(sim_instance=sim_instance)
@dataclass
class DotFeature(Dot.DotFeature):
sim_instance: "Simulator | None"
enemy = None
update_cd: int | float = np.inf
index: str | None = "Freez"
name: str | None = "冻结"
dot_from: str | None = "enemy"
effect_rules: int | None = 4
max_count: int | None = 1
incremental_step: int | None = 1
max_duration: int | None = None
max_effect_times: int = 1
def __post_init__(self):
if self.sim_instance is None:
raise ValueError("sim_instance is None, but it should not be.")
self.enemy = self.sim_instance.schedule_data.enemy
self.max_duration = int(240 * (1 + self.enemy.freeze_resistance))
def start(self, timenow: int):
self.dy.active = True
self.dy.start_ticks = timenow
self.dy.last_effect_ticks = timenow
if self.ft.max_duration is not None:
self.dy.end_ticks = self.dy.start_ticks + self.ft.max_duration
self.history.start_times += 1
self.history.last_start_ticks = timenow
self.dy.count = 1
self.dy.effect_times = 1
self.dy.ready = True
================================================
FILE: zsim/sim_progress/Dot/Dots/Ignite.py
================================================
from dataclasses import dataclass, field
from typing import TYPE_CHECKING
from .. import Dot
if TYPE_CHECKING:
from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar
from zsim.simulator.simulator_class import Simulator
class Ignite(Dot):
"""
灼烧dot,固定时间,内置CD0.5s
"""
def __init__(self, bar: "AnomalyBar | None" = None, sim_instance: "Simulator | None" = None):
super().__init__(bar, sim_instance=sim_instance) # 调用父类Dot的初始化方法
self.ft = self.DotFeature(
sim_instance=sim_instance
) # 用Ignite的DotFeature替代默认的DotFeature
# 你可以在这里添加特定于Ignite的行为或方法
@dataclass
class DotFeature(Dot.DotFeature):
sim_instance: "Simulator | None"
char_name_box: list[str] = field(init=False)
update_cd: int | float = 30
index: str | None = "Ignite"
name: str | None = "灼烧"
dot_from: str | None = "enemy"
effect_rules: int | None = 1
max_count: int | None = 1
incremental_step: int | None = 1
max_duration: int | None = None
max_effect_times: int = 30
def __post_init__(self):
if self.sim_instance is None:
raise ValueError("sim_instance is None, but it should not be.")
self.char_name_box = self.sim_instance.global_stats.name_box
"""
如果柏妮思在角色列表里,灼烧和最大生效次数就要发生变化。
"""
if "柏妮思" in self.char_name_box:
self.max_duration = 600 + 180
else:
self.max_duration = 600
================================================
FILE: zsim/sim_progress/Dot/Dots/Shock.py
================================================
from dataclasses import dataclass, field
from typing import TYPE_CHECKING
from .. import Dot
if TYPE_CHECKING:
from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar
from zsim.simulator.simulator_class import Simulator
class Shock(Dot):
def __init__(self, bar: "AnomalyBar | None" = None, sim_instance: "Simulator | None" = None):
super().__init__(bar, sim_instance=sim_instance) # 调用父类Dot的初始化方法
self.ft = self.DotFeature(sim_instance=sim_instance)
@dataclass
class DotFeature(Dot.DotFeature):
sim_instance: "Simulator | None"
char_name_box: list[str] = field(init=False)
exist_buff_dict: dict[str, dict[str, object]] = field(init=False)
update_cd: int | float = 60
index: str | None = "Shock"
name: str | None = "感电"
dot_from: str | None = "enemy"
effect_rules: int | None = 2
max_count: int | None = 1
incremental_step: int | None = 1
"""
如果丽娜在角色列表里,灼烧和最大生效次数就要发生变化。
"""
max_duration: int | None = None
max_effect_times: int = 30
def __post_init__(self):
if self.sim_instance is None:
raise ValueError("sim_instance is None, but it should not be.")
self.char_name_box = self.sim_instance.init_data.name_box
self.exist_buff_dict = self.sim_instance.load_data.exist_buff_dict
if "丽娜" in self.char_name_box:
if "Buff-角色-丽娜-组队被动-延长感电" in self.exist_buff_dict["丽娜"]:
self.max_duration = 600 + 180
else:
self.max_duration = 600
else:
self.max_duration = 600
================================================
FILE: zsim/sim_progress/Dot/Dots/ViviansProphecy.py
================================================
from dataclasses import dataclass
from typing import TYPE_CHECKING
from zsim.sim_progress.Buff import JudgeTools
from zsim.sim_progress.Preload.SkillsQueue import spawn_node
from .. import Dot
if TYPE_CHECKING:
from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar
from zsim.simulator.simulator_class import Simulator
class ViviansProphecy(Dot):
def __init__(self, bar: "AnomalyBar | None" = None, sim_instance: "Simulator | None" = None):
super().__init__(bar=bar, sim_instance=sim_instance) # 调用父类Dot的初始化方法
self.ft = self.DotFeature(sim_instance=sim_instance)
if sim_instance is None:
raise ValueError("构造dot实例时必须传入有效的sim_instance实例")
self.preload_data = JudgeTools.find_preload_data(sim_instance=sim_instance)
tick = JudgeTools.find_tick(sim_instance=sim_instance)
self.skill_node_data = spawn_node("1331_Core_Passive", tick, self.preload_data.skills)
@dataclass
class DotFeature(Dot.DotFeature):
sim_instance: "Simulator | None"
update_cd: int | float = 33
index: str | None = "ViviansProphecy"
name: str | None = "薇薇安的预言"
dot_from: str | None = "薇薇安"
effect_rules: int | None = 1
max_count: int | None = 999999
incremental_step: int | None = 1
max_duration: int | None = 999999
complex_exit_logic = True # 该dot为复杂退出逻辑
def exit_judge(self, **kwargs):
"""薇薇安的预言 dot的退出逻辑:敌人只要处于异常状态,就不会退出。"""
enemy = kwargs.get("enemy", None)
from zsim.sim_progress.Enemy import Enemy
if not isinstance(enemy, Enemy):
raise TypeError("enemy参数必须是Enemy类的实例")
if not enemy.dynamic.is_under_anomaly():
self.dy.active = False
return True
return False
================================================
FILE: zsim/sim_progress/Dot/Dots/__init__.py
================================================
================================================
FILE: zsim/sim_progress/Dot/__init__.py
================================================
from .BaseDot import Dot
class DotNode:
def __init__(self):
pass
__all__ = ["Dot", "DotNode"]
================================================
FILE: zsim/sim_progress/Enemy/EnemyAttack/EnemyAttackClass.py
================================================
import ast
from collections import defaultdict
from typing import TYPE_CHECKING
import numpy as np
import pandas as pd
from zsim.define import (
ENEMY_ATK_PARAMETER_DICT,
ENEMY_ATTACK_ACTION,
ENEMY_ATTACK_METHOD_CONFIG,
ENEMY_ATTACK_REPORT,
ENEMY_RANDOM_ATTACK,
ENEMY_REGULAR_ATTACK,
)
from zsim.sim_progress.RandomNumberGenerator import RNG
if TYPE_CHECKING:
from zsim.sim_progress.Enemy import Enemy
"""
EnemyAttack模块相关的数据结构以及程序逻辑设计如下(2025.1.30):
1、新建一个数据库(暂时用CSV来执行),里面记录了Enemy的各种进攻动作,由独立的ID作为索引。(包含了默认动作)
2、新建一个Json,用来记录不同敌人的进攻策略,其中包含了各种数据库中的动作ID,以及它们对应的几率。(包含了默认的策略)
Json的键值是一个独立的ID,可以是数字,也可以是字符串。
3、在Enemy类下,更新一个新的字段(包括EnemyClass以及对应的Enemy.csv),用来存放不同敌人的策略ID,
4、初始化时,根据Enemy.attack.策略ID→对应的攻击策略→对应的攻击动作ID(多个)→构造EnemyAttack实例(多个)→存放到Enemy.attack.attack_method下
4.1、每个攻击动作都会构造一个单独的EnemyAttackAction实例,这些实例会被存到EnemyAttackMethod实例下,并且有各自的几率。
5、调用时,利用EnemyAttack.attack_event_spawn函数,生成本次发生的攻击事件,并且抛出,被Preload获取。
"""
method_file = pd.read_csv(ENEMY_ATTACK_METHOD_CONFIG, index_col="ID")
action_file = pd.read_csv(ENEMY_ATTACK_ACTION, index_col="ID")
class EnemyAttackMethod:
"""含有若干个进攻动作的进攻策略"""
def __init__(self, ID: int = 0, enemy_instance: "Enemy" = None):
self.action_set: dict[float | int, EnemyAttackAction] = defaultdict()
self.enemy = enemy_instance
self.active = True
if ENEMY_RANDOM_ATTACK:
self.random_attack: bool = True
self.attack_skill_tag = None
elif ENEMY_REGULAR_ATTACK:
self.random_attack = False
self.attack_skill_tag = EnemyAttackAction(ID=int(method_file.loc[ID]["action_set"])).tag
else:
self.random_attack = False
self.attack_skill_tag = None
self.active = False
self.last_start_tick = 0
self.last_end_tick = 0
self.ready = False
rate_list = method_file.loc[ID]["action_rate"].split("|")
if sum(float(i) for i in rate_list) > 1:
raise ValueError("动作总权重超过1,请检查配置")
single_action_id_list = method_file.loc[ID]["action_set"].split("|")
if len(rate_list) != len(single_action_id_list):
raise ValueError("动作总数与概率总数不符,请检查配置")
self.rest_tick = method_file.loc[ID]["rest_tick"]
self.description = method_file.loc[ID]["discription"] # FIXME
self.name = method_file.loc[ID]["method_name"]
for i in range(len(single_action_id_list)):
action_id = single_action_id_list[i]
action_rate = float(rate_list[i])
enemy_attack_action = EnemyAttackAction(int(action_id))
self.action_set[action_rate] = enemy_attack_action
if ENEMY_ATTACK_REPORT:
print(f"【进攻交互系统初始化】:为敌人添加进攻动作:{enemy_attack_action}")
if ENEMY_ATTACK_REPORT:
print("【进攻交互系统初始化】:敌人进攻动作初始化完毕!")
print(
f"【进攻交互系统初始化】:敌人({self.enemy.name})共拥有{len(self.action_set)}个进攻动作,每次进攻决策的冷却时间为:{self.rest_tick}tick!"
)
if not self.active and ENEMY_ATTACK_REPORT:
print(
"【进攻交互系统初始化】:由于在配置文件中并未开启任意一种进攻策略,所以在本次模拟中敌人不会进攻!"
)
def ready_check(self, current_tick: int) -> bool:
"""判断敌人进攻的内置CD——进攻动作结束后,进攻决策才会进入冷却时间。"""
if not self.ready:
if current_tick - self.last_end_tick >= self.rest_tick:
self.ready = True
return self.ready
def probablity_driven_action_selection(self, current_tick: int) -> "EnemyAttackAction | None":
"""根据概率选择一个进攻动作"""
cumulative_probability = (
0 # 累积概率,这个数字没有实际意义,只是为了方便计算,每次函数运行时都初始化为0
)
rng: RNG = self.enemy.sim_instance.rng_instance
normalized_value = rng.random_float()
if not self.ready_check(current_tick):
return None
for _rate, _action in self.action_set.items():
cumulative_probability += _rate
if cumulative_probability >= normalized_value:
self.last_start_tick = current_tick
self.last_end_tick = current_tick + _action.duration
self.ready = False
return _action
else:
"""如果循环结束,还没有选中任何一个动作,说明无事发生,返回None"""
return None
def time_anchored_action_selection(self, current_tick: int) -> "EnemyAttackAction | None":
"""以固定的时间间隔选择固定的进攻动作"""
if self.ready_check(current_tick=current_tick):
self.last_start_tick = current_tick
self.last_end_tick = current_tick + self.action_set[1].duration
self.ready = False
if ENEMY_ATTACK_REPORT:
self.enemy.sim_instance.schedule_data.change_process_state()
print(
f"{self.enemy.name}(ID:{self.enemy.index_ID})抛出进攻动作{self.action_set[1].tag}"
)
return self.action_set[1]
else:
return None
def reset_myself(self):
"""重构EnemyAttack方法!"""
self.last_start_tick = 0
self.last_end_tick = 0
self.ready = False
class EnemyAttackAction:
"""敌人的单个进攻动作,它不记录任何动态数据,只是一个静态的动作数据结构,"""
def __init__(self, ID: int):
if ID == 0:
raise ValueError("EnemyAttackAction实例化所用的ID为0,请检查配置信息!")
self.id = ID
self.action_dict = action_file.loc[ID].to_dict()
self.tag = self.action_dict.get("tag", "")
self.description = self.action_dict.get("description", "")
self.hit = int(self.action_dict.get("hit", 0))
if self.hit <= 0:
raise ValueError("hit参数必须大于0,请检查配置信息!")
self.duration = float(self.action_dict.get("duration", 0))
if self.duration <= 0:
raise ValueError("duration参数必须大于0,请检查配置信息!")
self.cd = int(self.action_dict.get("cd", 0))
hit_list_str = self.action_dict.get("hit_list", None)
if hit_list_str is None or hit_list_str is np.nan:
# 在未提供hit_list的情况下,默认hit均匀分布,所以直接根据hit和duration来产生hit_list,
self.hit_list = list(
(self.duration / (self.hit + 1)) * (i + 1) for i in range(int(self.hit))
)
else:
self.hit_list = ast.literal_eval(hit_list_str)
if len(self.hit_list) != self.hit:
raise ValueError(f"{self.tag}的命中数量与命中时间列表长度不符,请检查配置信息!")
self.parryable = bool(self.action_dict.get("blockable", True)) # 是否可以招架
self.interruption_level_list = self.action_dict.get("interruption_level_list", None)
if self.interruption_level_list is None or self.interruption_level_list is np.nan:
self.interruption_level_list = [1] * self.hit
else:
self.interruption_level_list = self.interruption_level_list.split("|")
self.effect_radius_list = self.action_dict.get("effect_radius_list", None)
# TODO:暂时不考虑由技能范围不同而对命中率造成的影响,统一按照100%命中来处理,
self.stoppable = self.action_dict.get("stoppable", True)
self.hit_type = self.action_dict.get("hit_type", "Light")
if self.hit_type == "Chain" and self.hit <= 1:
raise ValueError(
f"{self.tag}为连续进攻动作,但是其命中数量为{self.hit},请检查配置信息!"
)
if self.hit_type in ["Light", "Heavy"] and self.hit > 1:
raise ValueError(
f"{self.tag}为{self.hit_type}攻击,但是其命中数量为{self.hit},请检查配置信息!"
)
def get_hit_tick(self, another_ta: int = None, hit_count: int = 1) -> int:
"""获取命中时间,"""
if not self.hit_list:
raise ValueError("hit_list为空,无法获取命中点!")
hit_tick = self.hit_list[hit_count - 1]
Ta = ENEMY_ATK_PARAMETER_DICT.get("Taction") if another_ta is None else another_ta
if hit_tick < Ta:
raise ValueError(
f"{self.tag}的第一个命中点({hit_tick})小于响应动作持续时间({Ta}),请检查数据库!"
)
return self.hit_list[0]
def get_first_hit(self) -> int:
"""获取第一个命中点"""
if not self.hit_list:
raise ValueError("hit_list为空,无法获取第一个命中点!")
first_hit_tick = self.hit_list[0]
Ta = ENEMY_ATK_PARAMETER_DICT.get("Taction")
if first_hit_tick < Ta:
raise ValueError(
f"{self.tag}的第一个命中点({first_hit_tick})小于相应动作持续时间({Ta}),请检查数据库!"
)
return self.hit_list[0]
def __str__(self):
return f"进攻动作ID:{self.id}, 技能Tag:{self.tag},动作耗时:{self.duration},单次动作的冷却时间:{self.cd}"
if __name__ == "__main__":
method = EnemyAttackMethod()
print(f"{method.name},{method.description}")
count = 0
for rate, action in method.action_set.items():
print(
f"事件{count + 1},几率{rate},动作集{action.hit_list},是否可被格挡:{action.blockable_list},\n打断等级{action.interruption_level_list},效果半径{action.effect_radius_list},是否可被打断{action.stoppable}"
)
count += 1
================================================
FILE: zsim/sim_progress/Enemy/EnemyAttack/__init__.py
================================================
from .EnemyAttackClass import EnemyAttackMethod
__all__ = ["EnemyAttackMethod"]
================================================
FILE: zsim/sim_progress/Enemy/EnemyUniqueMechanic/BaseUniqueMechanic.py
================================================
from abc import ABC, abstractmethod
class BaseUniqueMechanic(ABC):
@abstractmethod
def __init__(self, enemy_instance):
self.enemy = enemy_instance
@abstractmethod
def update_myself(self, *args, **kwargs):
pass
@abstractmethod
def event_active(self, *args, **kwargs):
pass
================================================
FILE: zsim/sim_progress/Enemy/EnemyUniqueMechanic/BreakingLegManager.py
================================================
from typing import TYPE_CHECKING
from zsim.sim_progress.data_struct import SingleHit
from zsim.sim_progress.Report import report_dmg_result
from .BaseUniqueMechanic import BaseUniqueMechanic
if TYPE_CHECKING:
from zsim.sim_progress.Enemy import Enemy
"""
FOCUS_RATIO_MAP 的存在,是为了模拟角色在破腿的过程中,
伤害溢散到别的腿上,导致单腿的破腿效率低于预期的情况。
但是,这种伤害的溢散,本质上和角色自身的技能模组有关。
这里的数据往往来自于对实战视频的观察,并非准确数值,并且可能经常需要调整。
"""
class BreakingLegManager:
def __init__(self, enemy_instance):
self.leg_group = {
0: SingleLeg(enemy_instance, self),
1: SingleLeg(enemy_instance, self),
2: SingleLeg(enemy_instance, self),
3: SingleLeg(enemy_instance, self),
4: SingleLeg(enemy_instance, self),
5: SingleLeg(enemy_instance, self),
}
self.major_target = 0
self.FOCUS_RATIO_MAP = {1361: 0.6, 1381: 0.6}
self.enemy = enemy_instance
def update_myself(self, single_hit: SingleHit, tick: int):
"""这是整个manager的对外总接口,负责接收SingleHit,并且分配伤害到对应的腿上"""
leg_index_tuple = self.select_target()
char_cid = int(single_hit.skill_tag.strip().split("_")[0])
major_ratio = self.FOCUS_RATIO_MAP.get(char_cid, 0.7)
minor_ratio = (1 - major_ratio) / 2
ratio_tuple = (minor_ratio, major_ratio, minor_ratio)
for i in range(len(leg_index_tuple)):
self.leg_group[leg_index_tuple[i]].update_myself(single_hit, tick, ratio_tuple[i])
def select_target(self) -> tuple[int, int, int]:
"""选腿!"""
major_target = self.major_target
minor_target_left = self.major_target - 1
if minor_target_left < 0:
minor_target_left = 5
minor_target_right = self.major_target + 1
if minor_target_right > 5:
minor_target_right = 0
return minor_target_left, major_target, minor_target_right
def change_major_leg(self):
"""换一条腿!"""
self.major_target += 1
if self.major_target > 5:
self.major_target = 0
# print(f'换腿!当前主目标为{self.major_target}!')
def report_all_legs(self):
print("------------------")
for index, legs in self.leg_group.items():
print(f"腿{index}的已损失HP为{legs.lost_leg_hp}")
print("------------------")
def reset_myself(self):
self.major_target = 0
for index, leg in self.leg_group.items():
leg.reset_single_leg()
class SingleLeg(BaseUniqueMechanic):
"""
这是一个关于未知侵蚀复合体的破腿机制的模拟结构——
这只怪拥有六条腿,每条腿都有自己的独立血量,
它们的对外接口是:update_myself()函数,需要传入single_hit,
而它们的事件触发核心是:event_active()函数
"""
def __init__(self, enemy_instance, manager_instance):
super().__init__(enemy_instance)
self.leg_hp_ratio = 0.08 # 腿的倍率
self.max_leg_hp = self.enemy.max_HP * self.leg_hp_ratio
self.lost_leg_hp = 0 # 已经损失的腿的HP
self.cd = 180 # 给了破腿的CD,暂定3秒
self.last_broken = 0 # 上一次破腿的时间
self.event = BreakingEvent(enemy_instance)
self.manager = manager_instance
def update_myself(self, single_hit: SingleHit, tick: int, ratio: float):
"""对外接口,接受主动技能的SingleHit,并且积累伤害。但是这里有几种情况是不积累伤害的,"""
if self.enemy.dynamic.stun:
"""
敌人如果正处于失衡状态下,那么游戏会强制锁定你的目标为敌人本体,而非腿,
所以在失衡期,是不太容易破腿的,
在模拟器中,我们对这种情况进行了直接返回的处理——即模拟战斗中,失衡期不会破腿。
不尽如此,腿还会因为失衡期而恢复。
"""
if self.lost_leg_hp != 0:
self.restore_leg()
return
if self.last_broken != 0:
if tick - self.last_broken <= self.cd:
"""腿还没冷却好!"""
return
"""更新腿的生命值"""
self.update_leg_hp(single_hit, tick, ratio)
def event_active(self, single_hit: SingleHit, tick: int):
self.event.active(single_hit, tick)
def broken_leg_judge(self, tick: int) -> bool:
"""检测腿有没有爆"""
if self.lost_leg_hp >= self.max_leg_hp:
self.last_broken = tick
self.restore_leg()
print("腿破了!!!")
return True
else:
return False
def restore_leg(self):
self.lost_leg_hp = 0
self.manager.change_major_leg()
def update_leg_hp(self, single_hit: SingleHit, tick: int, ratio):
"""更新腿的生命值"""
self.lost_leg_hp += single_hit.dmg_expect * ratio
if self.broken_leg_judge(tick):
self.event_active(single_hit, tick)
self.enemy.sim_instance.decibel_manager.update(single_hit=single_hit, key="part_break")
def reset_single_leg(self):
"""重置单条腿"""
self.lost_leg_hp = 0
self.last_broken = 0
class BreakingEvent:
def __init__(self, enemy_instance):
self.enemy: "Enemy" = enemy_instance
self.decibel_rewards = 1000 # 奖励喧响值
self.stun_ratio = 0.15 # 失衡比例
self.damage_ratio = 0.055 # 破腿的直伤倍率
self.game_state = None
self.found_char_dict: dict[int:object] = {}
def active(self, single_hit: SingleHit, tick: int):
"""破腿进行时!"""
if self.game_state is None:
from zsim.sim_progress.Preload import get_game_state
self.game_state = get_game_state()
# 1、更新喧响值
self.update_decibel(single_hit)
# 2、更新失衡
stun_value = self.enemy.max_stun * self.stun_ratio
self.enemy.update_stun(stun_value)
self.enemy.stun_judge(tick, single_hit=single_hit)
# 3、更新伤害
dmg_value = self.enemy.max_HP * self.damage_ratio
self.enemy._Enemy__HP_update(dmg_value)
report_dmg_result(
tick=tick,
element_type=0,
skill_tag="破腿",
dmg_expect=round(dmg_value, 2),
dmg_crit=round(dmg_value, 2),
stun=round(stun_value, 2),
buildup=0,
**self.enemy.dynamic.get_status(),
)
def update_decibel(self, single_hit: SingleHit):
"""向破腿的角色里更新喧响值"""
char_cid = int(single_hit.skill_tag.strip().split("_")[0])
if char_cid not in self.found_char_dict:
from zsim.sim_progress.Buff import find_char_from_CID
self.found_char_dict[char_cid] = find_char_from_CID(char_cid, self.enemy.sim_instance)
char_obj = self.found_char_dict[char_cid]
char_name = char_obj.NAME
from zsim.sim_progress.data_struct import ScheduleRefreshData
refresh_data = ScheduleRefreshData(
sp_target=(char_name,),
decibel_target=(char_name,),
decibel_value=self.decibel_rewards,
)
self.game_state["schedule_data"].event_list.append(refresh_data)
================================================
FILE: zsim/sim_progress/Enemy/EnemyUniqueMechanic/__init__.py
================================================
from .BreakingLegManager import BreakingLegManager
UNIQUE_MECHANIC_MAP = {
11411: BreakingLegManager,
# 11412: BreakingLegManager,
11413: BreakingLegManager,
# 11414: BreakingLegManager,
}
def unique_mechanic_factory(enemy_instance):
"""构造特殊敌人事件的工厂函数"""
mechanic_class = UNIQUE_MECHANIC_MAP.get(enemy_instance.index_ID, None)
if mechanic_class:
return mechanic_class(enemy_instance)
else:
return None
================================================
FILE: zsim/sim_progress/Enemy/QTEManager/QTEData.py
================================================
from typing import TYPE_CHECKING
from zsim.sim_progress.data_struct import SingleHit
if TYPE_CHECKING:
from zsim.sim_progress.Enemy import Enemy
class QETDataUpdater:
@classmethod
def apply(cls, qte_data, single_qte, attr_name):
raise NotImplementedError
class SumStrategy(QETDataUpdater):
"""数值累加策略"""
@classmethod
def apply(cls, qte_data, single_qte, attr_name):
single_qte_value = getattr(single_qte, attr_name, 0) or 0
qte_data_value = getattr(qte_data, attr_name, 0) or 0
setattr(qte_data, attr_name, single_qte_value + qte_data_value)
class ListMergeStrategy(QETDataUpdater):
"""列表合并策略"""
@classmethod
def apply(cls, qte_data, single_qte, attr_name):
single_qte_value = getattr(single_qte, attr_name, []) or []
qte_data_value = getattr(qte_data, attr_name, []) or []
setattr(qte_data, attr_name, qte_data_value + single_qte_value)
class QTEData:
def __init__(self, enemy_instance):
"""这个数据结构是管理怪物的QTE的总体数据的,它会随着Enemy类的初始化而一同初始化。
其中的动态数据(比如qte_received_box qte_triggered_times等,会在每次进入失衡期之前进行重置。"""
self.enemy_instance: "Enemy" = enemy_instance # 在初始化时,传入Enemy实例;
self.qte_received_box: list[str] = [] # 用于接受QTE阶段输入的QTE skill_tag
self.qte_triggered_times: int = 0 # 已经触发过几次QTE了
self.qte_triggerable_times: int | None = (
enemy_instance.QTE_triggerable_times
) # 最多可以触发几次QTE
self.qte_activation_available = False # 彩色失衡阶段——在StunJudge中被打开,在SingeQTE的merge方法中被关闭,当然,失衡阶段的结束也会关闭该参数(依旧是StunJudge)。
self.single_qte = None # 单次QTE的实例
self.__single_hit_check = lambda hit: all(
[
hit.hitted_count == 1
or hit.heavy_hit # 第一跳、重击(通常为重攻击标签的最后一跳)均能通过判定,
# hit.proactive or (not hit.proactive and 'QTE' in hit.skill_tag), # 筛选出主动技能,所有的被动释放的技能都不能和QTE的激活行为进行互动。
]
)
self.strategies_map = {
"qte_received_box": ListMergeStrategy,
"qte_triggered_times": SumStrategy,
}
self.preload_data = None
self.qte_answered_box: list[str | None] = [] # 响应过QTE的角色
def check_myself(self, single_hit: SingleHit | None = None) -> bool:
"""该函数用于检查自身目前的状态,即当前是彩色失衡还是灰色失衡;"""
if self.qte_triggerable_times is None:
return False
if self.qte_triggered_times > self.qte_triggerable_times:
raise ValueError(
f"QTE的实际响应总次数为{self.qte_triggered_times}次,大于其最大次数{self.qte_triggerable_times}次!"
)
if len(self.qte_received_box) > self.qte_triggered_times:
raise ValueError(
f"QTE总计包含了{len(self.qte_received_box)}个skill_tag,而实际的响应次数为{self.qte_triggered_times}次!"
)
if self.enemy_instance.dynamic.stun:
if self.qte_triggerable_times is None:
return False
if self.qte_triggered_times < self.qte_triggerable_times:
return True
else:
self.qte_activation_available = False
return False
else:
"""
v0.3.1b2更新:
为了复现柚叶2画在非失衡期也能激发一次连携技的机制,
所以为single_hit以及skill_node添加了一个新参数——force_qte_trigger(强制激发QTE)
该参数可以让QTE管理器在非失衡期放行这个single_hit,并且使其顺利进入后续的判定。
后续更新:
QTE结构仅需要针对两种情况进行特殊放行:
1、在非失衡期能够激发连携的skill_node——force_qte_trigger==True
2、在非失衡期激发连携后,进行响应的QTE技能;
"""
if self.single_qte is None:
if single_hit is not None:
if single_hit.force_qte_trigger:
return True
else:
if single_hit is not None:
if (
single_hit.skill_node
and single_hit.skill_node.skill.trigger_buff_level == 5
):
return True
return False
def try_qte(self, hit: SingleHit) -> None:
"""
该函数是QTEData的最外层接口,是核心调用函数。
其核心作用为:用传入的SingleHit来尝试激发QTE。
"""
# 0、 如果是非失衡状态或是灰色失衡状态,那么直接返回
if not self.check_myself(single_hit=hit):
return
# 1、可行性审查,这里,只有主动动作的第一跳、以及含有重击标签的、主动动作的最后一跳能够通过判定。
if not self.single_hit_filter(hit):
return
# 2、是否存在已经激活的SingleQTE实例——连携阶段已经因重攻击而激发、SingleQTE实例也已经创建,但是尚未传入SingleHit的状态
if self.single_qte is not None:
if not isinstance(self.single_qte, SingleQTE):
raise TypeError(
f"QTEData的single_qte属性不是SingleQTE类!你往里放入了{type(self.single_qte)}!"
)
# 2.1、尝试将已经通过可行性检查的SingleHit传入到SingleQTE中,进行数据更新,
self.single_qte.receive_hit(hit)
# print(f'{hit.skill_tag}响应了本次连携技触发!')
else:
# 3、如果SingleQTE实例不存在,那么要对传入的SingHit进行判断;
if self.qte_active_selector(hit):
# 3.1、如果是能够激发连携的hit,而此时又没有SingleHit存在,那么就是激活了新的QTE阶段,进入下一步判断。
self.single_qte = SingleQTE(self, single_hit=hit)
self.enemy_instance.sim_instance.schedule_data.change_process_state()
assert hit.skill_node is not None
print(
f"{hit.skill_node.char_name} 的 {hit.skill_node.skill.skill_text} 激发了连携技!当前已经激发过{self.qte_triggered_times + 1}次连携技!"
)
else:
"""
如果不是重攻击,那就只能是某技能的第一跳。
很明显,在SingleHit并未被创建的时候,技能的第一跳并不能激发连携状态。所以这个分支什么都不做(暂时)
这个分枝往往发生在:怪物已经失衡,但是重攻击标签还未传入时,可能是前台切人合轴了,也可能是角色的APL本来就不打连携技。
此时,下一个循环,函数就会触发隔壁分枝,因为有一个SingleQTE是待传入状态,
"""
# print(f'虽然是彩色失衡状态,但是没有进行响应')
return
def single_hit_filter(self, hit: SingleHit):
"""
该函数用于对传入的SingleHit进行筛选,并返回筛选结果。
"""
return self.__single_hit_check(hit)
def reset(self):
"""该函数用于在失衡期开始时的重置QTEdata中的动态数据"""
self.qte_received_box = []
self.qte_triggered_times = 0
self.qte_activation_available = True
self.single_qte = None
self.qte_answered_box = []
def restore(self):
self.qte_received_box = []
self.qte_triggered_times = 0
self.qte_activation_available = False
self.single_qte = None
def spawn_single_qte(self):
pass
def qte_active_selector(self, _hit: SingleHit) -> bool:
"""
这个函数筛选的是真正能激发QTE的技能,这种技能有两个条件:
1、重攻击Hit——含有重攻击标签(“heavy_attack”)的技能的最后一跳
2、函数接收到这个hit信号的同时,角色正处于被操作状态下
(解释一下,这里为什么不用“前台”属性来进行判断:因为绝区零已经存在多名角色同时处于前台的情况,
所以这个“前台”的说法并不准确,但是无论如何,玩家操纵的角色始终只有一名,
所以“角色正处于被操作状态下”才是更准确的描述。)
"""
if not _hit.heavy_hit:
return False
if self.preload_data is None:
self.preload_data = self.enemy_instance.sim_instance.preload.preload_data
from zsim.sim_progress.Preload.PreloadDataClass import PreloadData
if not isinstance(self.preload_data, PreloadData):
raise TypeError("QTEData的preload_data属性不是PreloadData类!")
if not self.enemy_instance.dynamic.stun:
assert _hit.skill_node is not None
if _hit.skill_node.force_qte_trigger:
# FIXME: 临时解决方案,理论上2画触发连携技需要柚叶在前台,合轴会导致激发失败;
# 但是这涉及到的底层逻辑较为复杂,APL也不太好改,所以这里暂时先这么临时解决一下
return True
if self.preload_data.operating_now is None:
"""说明目前没有任何角色在前台"""
return False
else:
return int(_hit.skill_tag.split("_")[0]) == self.preload_data.operating_now
def check_qte_legality(self, qte_skill_tag: str):
"""
检查QTE是否合法,即是否已经被响应过了。
"""
CID = int(qte_skill_tag.split("_")[0])
return CID not in self.qte_answered_box
class SingleQTE:
def __init__(self, qte_data: QTEData, single_hit: SingleHit):
self.qte_data = qte_data
self.qte_received_box: list[str] = [] # 用于接受QTE阶段输入的QTE skill_tag
self.qte_triggerable_times: int | None = (
self.qte_data.qte_triggerable_times
) # 最多可以触发几次QTE
self.qte_triggered_times: int = 0 # 已经响应了几次QTE
self.qte_activation_available = False # 彩色失衡阶段
self.__is_hitted = (
False # 每个SingleHit都只会被响应一次,所以这里用一个bool变量来标记是否已经被响应过。
)
self.active_by: SingleHit = single_hit
def receive_hit(self, _single_hit: SingleHit):
"""SingleQTE接收SingleHit的方法"""
if self.__is_hitted:
raise ValueError(
"SingleQTE实例已经响应过一次了!请检查函数逻辑,查找为何处会多次调用同一个SingleQTE的_receive_hit函数!"
)
if not isinstance(_single_hit, SingleHit):
raise TypeError(
f"SingleQTE实例的_receive_hit函数被调用时,传入的single_hit参数不是SingleHit类!而是{type(_single_hit)}!"
)
if _single_hit.hitted_count != 1: # 如果传进来的不是第一跳,那直接return
return
if not _single_hit.proactive: # 如果传进来的是一个非主动动作,也直接return
return
if self.qte_data.enemy_instance.dynamic.stun:
"""正常的QTE接收逻辑"""
self.receive_hit_while_stun(_single_hit)
else:
"""非失衡阶段的QTE接收逻辑(强制触发的QTE)"""
self.receive_qte_without_stun(_single_hit)
def receive_hit_while_stun(self, _single_hit: SingleHit):
"""失衡期接收QTE的业务逻辑"""
self.qte_triggered_times += 1
assert _single_hit.skill_node is not None
if "QTE" not in _single_hit.skill_tag:
"""说明QTE被取消了"""
self.qte_data.enemy_instance.sim_instance.schedule_data.change_process_state()
print(
f"【QTE事件】{_single_hit.skill_node.char_name} 取消第{self.qte_data.qte_triggered_times + 1}次QTE,并释放了 {_single_hit.skill_node.skill.skill_text} "
)
# 当取消QTE时,连续响应QTE的角色列表需要清空
self.qte_data.qte_answered_box = []
else:
"""角色响应了QTE,释放连携技"""
assert self.active_by.skill_node is not None
if _single_hit.skill_node.char_name == self.active_by.skill_node.char_name:
raise ValueError(f"{_single_hit.skill_node.char_name} 企图响应自己激发的QTE!")
self.qte_received_box.append(_single_hit.skill_tag)
"""QTE成功响应之后,返还1秒QTE时间"""
self.qte_data.enemy_instance.sim_instance.schedule_data.change_process_state()
self.qte_data.enemy_instance.dynamic.stun_tick_feed_back_from_QTE += 60
print(
f"【QTE事件】 {_single_hit.skill_node.char_name} 响应了连携,释放连携技(skill_tag为{_single_hit.skill_tag}),返还1秒失衡时间!"
)
# 当角色响应了QTE时,需要将角色的skill_tag添加到qte_answered_box中
CID = _single_hit.skill_node.skill.char_obj.CID
if CID in self.qte_data.qte_answered_box:
raise ValueError(
f"一轮连续地QTE中,每名角色只允许响应一次QTE!当前轮次QTE中,已经有{self.qte_data.qte_answered_box}响应过了QTE,{CID} 企图响应QTE两次!"
)
self.qte_data.qte_answered_box.append(_single_hit.skill_node.skill.char_obj.CID)
self.__is_hitted = True
self.merge_single_qte()
def merge_single_qte(self):
"""SingleQTE向QTEData更新数据的方法"""
for attr_name, strategy in self.qte_data.strategies_map.items():
if hasattr(self, attr_name):
strategy.apply(self.qte_data, self, attr_name)
self.qte_data.single_qte = None
self.qte_data.check_myself()
def receive_qte_without_stun(self, _single_hit: SingleHit):
"""在非失衡阶段接收hit"""
assert _single_hit.skill_node is not None
if "QTE" not in _single_hit.skill_tag:
"""说明QTE被取消了"""
self.qte_data.enemy_instance.sim_instance.schedule_data.change_process_state()
print(
f"【QTE事件】{_single_hit.skill_node.char_name} 取消了强制触发的QTE,并释放了 {_single_hit.skill_node.skill.skill_text} "
)
else:
"""角色响应了QTE,释放连携技"""
assert self.active_by.skill_node is not None
if (
_single_hit.skill_node.char_name == self.active_by.skill_node.char_name
and not self.active_by.skill_node.force_qte_trigger
):
raise ValueError(f"{_single_hit.skill_node.char_name} 企图响应自己激发的QTE!")
# FIXME:由于柚叶2画有强行在非失衡期触发连携技的特性,为了系统稳定暂时让这个激发在后台也能生效。
# 所以这里的验错也需要避开这个情况,否则就会报“自己响应自己QTE”的错误
self.qte_data.enemy_instance.sim_instance.schedule_data.change_process_state()
print(
f"【QTE事件】 {_single_hit.skill_node.char_name} 响应了强制触发的连携,释放连携技 {_single_hit.skill_node.skill.skill_text}(skill_tag为{_single_hit.skill_tag})"
)
self.__is_hitted = True
self.merge_single_qte()
================================================
FILE: zsim/sim_progress/Enemy/QTEManager/__init__.py
================================================
from .QTEData import QTEData
class QTEManager:
def __init__(self, enemy_instance):
self.qte_data: QTEData = QTEData(enemy_instance)
def receive_hit(self, hit):
self.qte_data.try_qte(hit)
def reset_myself(self):
self.qte_data.reset()
def check_qte_legality(self, qte_skill_tag: str):
return self.qte_data.check_qte_legality(qte_skill_tag)
================================================
FILE: zsim/sim_progress/Enemy/__init__.py
================================================
from typing import TYPE_CHECKING, Literal
import numpy as np
import pandas as pd
from zsim.define import ENEMY_ADJUSTMENT_PATH, ENEMY_DATA_PATH
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from zsim.models.event_enums import SpecialStateUpdateSignal as SSUS
from zsim.sim_progress.anomaly_bar import (
AuricInkAnomaly,
ElectricAnomaly,
EtherAnomaly,
FireAnomaly,
FrostAnomaly,
IceAnomaly,
PhysicalAnomaly,
)
from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar
from zsim.sim_progress.data_struct import SingleHit
from zsim.sim_progress.data_struct.enemy_special_state_manager import SpecialStateManager
from zsim.sim_progress.Report import report_to_log
from .EnemyAttack import EnemyAttackMethod
from .EnemyUniqueMechanic import unique_mechanic_factory
from .QTEManager import QTEManager
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
class EnemySettings:
def __init__(self):
self.enemy_info_overwrite = False # 是否强制覆盖怪物数据
self.forced_no_stun = False
self.forced_no_anomaly = False
self.forced_stun_DMG_take_ratio: float = 1.5
self.forced_anomaly: int = 0
class Enemy:
def __init__(
self,
*,
name: str | None = None,
index_id: int | None = None,
sub_ID: int | None = None,
adjustment_id: int | None = None,
difficulty: float = 1,
sim_instance: "Simulator | None" = None,
):
"""
根据数据库信息创建怪物属性对象。
三选一参数:(你不填也行,默认创建尼尼微作为木桩怪,因为她全部0抗性)
- enemy_name (str): 敌人的中文名称
- enemy_index_ID (int): 敌人的索引ID
- enemy_sub_ID (int): 敌人的子ID,格式为9000+索引ID
!!!注意!!!因为可能存在重名敌人的问题,使用中文名称查找怪物时,只会查找ID最靠前的那一个
更新参数接口:
hit_received(single_hit)
update_stun(float)
获取临时参数接口:
get_hp_percentage()
get_stun_percentage()
"""
# 读取敌人数据文件,初始化敌人信息
assert sim_instance is not None
self.sim_instance: "Simulator" = sim_instance
self.__last_stun_increase_tick: int | None = None
_raw_enemy_dataframe = pd.read_csv(ENEMY_DATA_PATH)
_raw_enemy_adjustment_dataframe = pd.read_csv(ENEMY_ADJUSTMENT_PATH)
# !!!注意!!!因为可能存在重名敌人的问题,使用中文名称查找怪物时,只会返回ID更靠前的
enemy_info = self.__lookup_enemy(_raw_enemy_dataframe, name, index_id, sub_ID)
self.name, self.index_ID, self.sub_ID, self.data_dict = enemy_info
self.adjustment_id = adjustment_id
# 获取调整倍率
self.enemy_adjust: dict[
Literal["生命值", "攻击力", "失衡值上限", "防御力", "异常积蓄值上限"], float
] = self.__lookup_enemy_adjustment(_raw_enemy_adjustment_dataframe, adjustment_id)
# 难度
self.difficulty: float = difficulty
# 初始化动态属性
self.dynamic = self.EnemyDynamic(self)
# 初始化敌人基础属性
self.base_max_HP: float = float(self.data_dict["70级最大生命值"])
self.max_HP: float = (
float(self.data_dict["70级最大生命值"]) * (1 + self.enemy_adjust["生命值"]) * difficulty
)
self.max_ATK: float = float(self.data_dict["70级最大攻击力"]) * (
1 + self.enemy_adjust["攻击力"]
)
self.max_stun: float = float(self.data_dict["70级最大失衡值上限"]) * (
1 + self.enemy_adjust["失衡值上限"]
)
self.max_DEF: float = float(self.data_dict["60级及以上防御力"]) * (
1 + self.enemy_adjust["防御力"]
)
self.CRIT_damage: float = float(self.data_dict["暴击伤害"])
self.able_to_be_stunned: bool = bool(self.data_dict["能否失衡"])
self.able_to_get_anomaly: bool = bool(self.data_dict["能否异常"])
self.stun_recovery_rate: float = float(self.data_dict["失衡恢复速度"]) / 60
self.stun_recovery_time: float = 0.0
self.qte_manager: QTEManager | None = None
self.stun_DMG_take_ratio: float = float(self.data_dict["失衡易伤值"])
self.QTE_triggerable_times: int = int(self.data_dict["可连携次数"])
# 初始化敌人异常状态抗性
max_element_anomaly, self.max_anomaly_PHY = self.__init_enemy_anomaly(
self.able_to_get_anomaly,
self.QTE_triggerable_times,
self.enemy_adjust["异常积蓄值上限"],
)
self.max_anomaly_ICE = self.max_anomaly_FIRE = self.max_anomaly_ETHER = (
self.max_anomaly_ELECTRIC
) = self.max_anomaly_FIREICE = self.max_anomaly_AURICINK = max_element_anomaly
# 初始化敌人其他防御属性
self.interruption_resistance_level: int = int(self.data_dict["抗打断等级"])
self.freeze_resistance: float = float(self.data_dict["冻结抵抗"])
# 伤害抗性
self.ICE_damage_resistance: float = float(self.data_dict["冰伤害抗性"])
self.FIRE_damage_resistance: float = float(self.data_dict["火伤害抗性"])
self.ELECTRIC_damage_resistance: float = float(self.data_dict["电伤害抗性"])
self.ETHER_damage_resistance: float = float(self.data_dict["以太伤害抗性"])
self.PHY_damage_resistance: float = float(self.data_dict["物理伤害抗性"])
# 异常抗性
self.ICE_anomaly_resistance: float = float(self.data_dict["冰异常抗性"])
self.FIRE_anomaly_resistance: float = float(self.data_dict["火异常抗性"])
self.ELECTRIC_anomaly_resistance: float = float(self.data_dict["电异常抗性"])
self.ETHER_anomaly_resistance: float = float(self.data_dict["以太异常抗性"])
self.PHY_anomaly_resistance: float = float(self.data_dict["物理异常抗性"])
# 失衡抗性
self.ICE_stun_resistance: float = float(self.data_dict["冰失衡抗性"])
self.FIRE_stun_resistance: float = float(self.data_dict["火失衡抗性"])
self.ELECTRIC_stun_resistance: float = float(self.data_dict["电失衡抗性"])
self.ETHER_stun_resistance: float = float(self.data_dict["以太失衡抗性"])
self.PHY_stun_resistance: float = float(self.data_dict["物理失衡抗性"])
# 各抗性对应字典
self.damage_resistance_dict: dict[int, float] = {
0: self.PHY_damage_resistance,
1: self.FIRE_damage_resistance,
2: self.ICE_damage_resistance,
3: self.ELECTRIC_damage_resistance,
4: self.ETHER_damage_resistance,
5: self.ICE_damage_resistance,
6: self.ETHER_damage_resistance,
}
self.anomaly_resistance_dict: dict[int, float] = {
0: self.PHY_anomaly_resistance,
1: self.FIRE_anomaly_resistance,
2: self.ICE_anomaly_resistance,
3: self.ELECTRIC_anomaly_resistance,
4: self.ETHER_anomaly_resistance,
5: self.ICE_anomaly_resistance,
6: self.ETHER_anomaly_resistance,
}
self.stun_resistance_dict: dict[int, float] = {
0: self.PHY_stun_resistance,
1: self.FIRE_stun_resistance,
2: self.ICE_stun_resistance,
3: self.ELECTRIC_stun_resistance,
4: self.ETHER_stun_resistance,
5: self.ICE_stun_resistance,
6: self.ETHER_stun_resistance,
}
# 初始化敌人设置
self.settings = EnemySettings()
self.__apply_settings(self.settings)
# 下面的两个dict本来写在外面的,但是别的程序也要用这两个dict,所以索性写进来了。我是天才。
self.trans_element_number_to_str = {
0: "PHY",
1: "FIRE",
2: "ICE",
3: "ELECTRIC",
4: "ETHER",
5: "FIREICE",
6: "AURICINK",
}
self.trans_anomaly_effect_to_str = {
0: "assault",
1: "burn",
2: "frostbite",
3: "shock",
4: "corruption",
5: "frost_frostbite",
6: "auricink_corruption",
}
# enemy实例化的时候,6种异常积蓄条也随着一起实例化
self.frost_anomaly_bar = FrostAnomaly(sim_instance=self.sim_instance)
self.ice_anomaly_bar = IceAnomaly(sim_instance=self.sim_instance)
self.fire_anomaly_bar = FireAnomaly(sim_instance=self.sim_instance)
self.physical_anomaly_bar = PhysicalAnomaly(sim_instance=self.sim_instance)
self.ether_anomaly_bar = EtherAnomaly(sim_instance=self.sim_instance)
self.electric_anomaly_bar = ElectricAnomaly(sim_instance=self.sim_instance)
self.auricink_anomaly_bar = AuricInkAnomaly(sim_instance=self.sim_instance)
"""
由于在AnomalyBar的init中有一个update_anomaly函数,
该函数可以根据传入new_snap_shot: tuple 的第0位的属性标号,
找到对应的anomaly_bar的实例,并且执行它的update_snap_shot 函数。
以更新对应的积蓄快照。
本来,这个dict应该建立在update_anomaly函数中,但是考虑到该函数会反复调用,频繁地创建这个dict会导致性能的浪费。
所以将其挪到Enemy的init中,这样,这个dict只在Enemy实例化时被创建一次,
然后update_anomaly函数将通过enemy.anomaly_bars_dict来调出对应的anomaly_bars实例。
"""
self.anomaly_bars_dict: dict[int, AnomalyBar] = {
0: self.physical_anomaly_bar,
1: self.fire_anomaly_bar,
2: self.ice_anomaly_bar,
3: self.electric_anomaly_bar,
4: self.ether_anomaly_bar,
5: self.frost_anomaly_bar,
6: self.auricink_anomaly_bar,
}
# 在初始化阶段更新属性异常条最大值。
for element_type in self.anomaly_bars_dict:
anomaly_bar = self.anomaly_bars_dict[element_type]
max_value = getattr(
self, f"max_anomaly_{self.trans_element_number_to_str[element_type]}"
)
anomaly_bar.max_anomaly = max_value
if self.data_dict["进攻策略"] is None or self.data_dict["进攻策略"] is np.nan:
attack_method_code = 0
else:
attack_method_code = int(self.data_dict["进攻策略"])
self.attack_method = EnemyAttackMethod(ID=attack_method_code, enemy_instance=self)
self.restore_stun()
self.unique_machanic_manager = unique_mechanic_factory(self) # 特殊机制管理器
self.special_state_manager = SpecialStateManager(enemy_instance=self)
report_to_log(f"[ENEMY]: 怪物对象 {self.name} 已创建,怪物ID {self.index_ID}", level=4)
def __restore_stun_recovery_time(self):
self.stun_recovery_time = float(self.data_dict["失衡恢复时间"]) * 60
def restore_stun(self):
"""还原 Enemy 本身的失衡恢复时间,与QTE计数"""
self.dynamic.stun = False
self.dynamic.stun_bar = 0
self.dynamic.stun_tick = 0
self.dynamic.stun_tick_feed_back_from_QTE = 0
self.__restore_stun_recovery_time()
if self.qte_manager is None:
self.qte_manager = QTEManager(self)
self.qte_manager.qte_data.restore()
def increase_stun_recovery_time(self, increase_tick: int):
"""更新失衡延长的时间,负责接收 Calculator 的 buff"""
if self.__last_stun_increase_tick is None:
self.__last_stun_increase_tick = increase_tick
self.stun_recovery_time += increase_tick
else:
if increase_tick >= self.__last_stun_increase_tick:
self.__last_stun_increase_tick = increase_tick
self.__restore_stun_recovery_time()
self.stun_recovery_time += increase_tick
def get_active_anomaly_bar(self) -> type[AnomalyBar]:
"""用于外部获取当前正在激活的属性异常条对象"""
output_list = []
for element_type, anomaly_bar in self.anomaly_bars_dict.items():
if anomaly_bar.active:
output_list.append(self.dynamic.active_anomaly_bar_dict[element_type])
if len(output_list) == 0 or len(output_list) > 1:
raise ValueError(f"状态错误!找到了{len(output_list)}种正在激活的属性异常条!")
return output_list[0]
@staticmethod
def __lookup_enemy(
enemy_df: pd.DataFrame,
enemy_name: str | None = None,
enemy_index_ID: int | None = None,
enemy_sub_ID: int | None = None,
) -> tuple[str, int, int, dict]:
"""
根据敌人名称或ID查找敌人信息,并返回敌人名称、IndexID和SubID。
若输入多个参数,此函数会检测这些参数是否一一对应
!!!注意!!!因为可能存在重名敌人的问题,使用中文名称查找怪物时,只会返回ID更靠前的
因此,在已经输入了ID的情况下,函数不会优先根据中文名查找
参数:
- enemy_df: pd.DataFrame, 敌人数据 DataFrame,包含敌人信息。
- enemy_name: str, 可选,敌人名称。
- enemy_index_ID: int, 可选,敌人IndexID。
- enemy_sub_ID: int, 可选,敌人SubID。
返回:
- 有传入参数时,返回对应怪物的数据
- 无传入参数时,返回尼尼微的数据
"""
# fmt: off
try:
if enemy_index_ID is not None:
row = enemy_df[enemy_df["IndexID"] == enemy_index_ID].to_dict("records")
elif enemy_sub_ID is not None:
row = enemy_df[enemy_df["SubID"] == enemy_sub_ID].to_dict("records")
elif enemy_name is not None:
row = enemy_df[enemy_df["CN_enemy_ID"] == enemy_name].to_dict("records")
else:
row = enemy_df[enemy_df["IndexID"] == 11531].to_dict("records") # 默认打尼尼微(因为全部0抗)
if not row:
raise ValueError(f"找不到对应的敌人,请检查输入参数:name={enemy_name}, index_id={enemy_index_ID}, sub_id={enemy_sub_ID}")
except IndexError:
raise ValueError("找不到对应的敌人")
# fmt: on
row_0: dict = row[0]
name: str = row_0["CN_enemy_ID"]
index_ID: int = int(row_0["IndexID"])
sub_ID: int = int(row_0["SubID"])
# 检查输入的变量与查到的变量是否一致
if enemy_name is not None:
if name != enemy_name:
raise ValueError("传入的name与ID不匹配")
if enemy_index_ID is not None:
if index_ID != enemy_index_ID:
raise ValueError("传入的name与ID不匹配")
if enemy_sub_ID is not None:
if sub_ID != enemy_sub_ID:
raise ValueError("传入的name与ID不匹配")
return name, index_ID, sub_ID, row_0
@staticmethod
def __lookup_enemy_adjustment(
adjust_df: pd.DataFrame, adjust_ID: int | None = None
) -> dict[Literal["生命值", "攻击力", "失衡值上限", "防御力", "异常积蓄值上限"], float]: # type: ignore
"""根据调整ID查找敌人调整数据,并返回调整数据字典。"""
if adjust_ID is not None:
try:
row = adjust_df[adjust_df["ID"] == adjust_ID].to_dict("records")
except IndexError:
raise ValueError(f"找不到属性调整ID:{adjust_ID}")
row_0: dict[
Literal["生命值", "攻击力", "失衡值上限", "防御力", "异常积蓄值上限"], float
] = dict(row[0]) # type: ignore
return row_0
else:
return {
"生命值": 0,
"攻击力": 0,
"失衡值上限": 0,
"防御力": 0,
"异常积蓄值上限": 0,
}
@staticmethod
def __init_enemy_anomaly(
able_to_get_anomaly: bool, QTE_triggerable_times: int, adjust: float
) -> tuple[int | float, int | float]:
"""
根据敌人的异常能力和QTE触发次数(怪物等阶)初始化敌人的异常值。
参数:
able_to_get_anomaly (bool): 敌人是否能获得异常值。
QTE_triggerable_times (int): QTE可触发的次数。
返回:
tuple: 包含两个值,分别为基础异常值和物理异常值。
"""
if able_to_get_anomaly:
# 定义基础异常值
base_anomaly = 150 * (1 + adjust)
# 定义物理异常值的乘数
physical_anomaly_mul = 1.2
# 计算物理异常值
base_anomaly_physical = base_anomaly * physical_anomaly_mul
# 根据QTE触发次数返回相应的异常值
if QTE_triggerable_times == 1:
return base_anomaly * 4, base_anomaly_physical * 4
elif QTE_triggerable_times == 2:
return base_anomaly * 15, base_anomaly_physical * 15
elif QTE_triggerable_times == 3:
return base_anomaly * 20, base_anomaly_physical * 20
else:
# 如果QTE触发次数不符合已定义的条件,返回默认异常值
return 3000, 3600 # 默认异常值
else:
return 0, 0
def __apply_settings(self, settings: EnemySettings):
if settings.enemy_info_overwrite:
if settings.forced_no_stun:
self.able_to_be_stunned = False
if settings.forced_no_anomaly:
self.able_to_get_anomaly = False
self.stun_DMG_take_ratio = settings.forced_stun_DMG_take_ratio
else:
pass
def update_max_anomaly(self, element: str | int = "ALL", *, times: int = 1) -> None:
"""更新怪物异常值,触发一次异常后调用。"""
# 参数类型检查
if not isinstance(element, (str, int)):
raise TypeError(f"element参数类型错误,必须是整数或字符串,实际类型为{type(element)}")
if not isinstance(times, int):
raise TypeError(f"times参数必须是整数,实际类型为{type(times)}")
if times <= 0:
raise ValueError(f"times参数必须大于0,实际值为{times}")
# 属性类型映射表
element_mapping: dict[str, tuple] = {
"PHY": ("物理", 0),
"FIRE": ("火", 1),
"ICE": ("冰", 2),
"ELECTRIC": ("电", 3),
"ETHER": ("以太", 4),
"FROST": ("烈霜", "FIREICE", 5),
"AURICINK": ("玄墨", 6),
"ALL": ("全部", "所有"),
}
# 检查并标准化元素
if isinstance(element, str):
element = element.upper()
for key, values in element_mapping.items():
if element in (key, *values):
element = key
break
else:
raise ValueError(f"输入了不支持的元素种类:{element}")
elif isinstance(element, int):
for key, values in element_mapping.items():
if element in values:
element = key
break
else:
raise ValueError(f"输入了不支持的元素种类:{element}")
else:
raise ValueError(f"无法识别的元素种类:{element}")
# 更新比例
update_ratio = 1.02 # 每次异常增加 2% 对应属性异常值
# 确保 times 在合理范围内
if times > 1e6: # 防止极端值导致性能问题
raise ValueError(f"times参数过大,可能导致性能问题,实际值为{times}")
# 计算最终更新比例
multiplier = update_ratio**times
# 批量更新异常值
if element == "ALL":
self.max_anomaly_ICE *= multiplier
self.max_anomaly_FIRE *= multiplier
self.max_anomaly_ETHER *= multiplier
self.max_anomaly_ELECTRIC *= multiplier
self.max_anomaly_PHY *= multiplier
self.max_anomaly_FIREICE *= multiplier
self.max_anomaly_AURICINK *= multiplier
else:
# 单个元素更新
if element == "ICE":
self.max_anomaly_ICE *= multiplier
elif element == "FIRE":
self.max_anomaly_FIRE *= multiplier
elif element == "ETHER":
self.max_anomaly_ETHER *= multiplier
elif element == "ELECTRIC":
self.max_anomaly_ELECTRIC *= multiplier
elif element == "PHY":
self.max_anomaly_PHY *= multiplier
elif element == "FROST":
self.max_anomaly_FIREICE *= multiplier
elif element == "AURICINK":
self.max_anomaly_AURICINK *= multiplier
def update_stun(self, stun: np.float64) -> None:
self.dynamic.stun_bar += stun
def hit_received(self, single_hit: SingleHit, tick: int) -> None:
"""实现怪物的QTE次数计算、扣血计算等受击时的对象结算,与伤害计算器对接"""
# 更新失衡,为减少函数调用
self.dynamic.stun_bar += single_hit.stun
self.stun_judge(tick, single_hit=single_hit)
# 怪物的扣血逻辑。
self.__HP_update(single_hit.dmg_expect)
# 更新异常值
self.__anomaly_prod(single_hit.snapshot, single_hit=single_hit)
if self.unique_machanic_manager is not None:
self.unique_machanic_manager.update_myself(single_hit, tick)
# 更新连携管理器
assert (
self.qte_manager is not None
and self.sim_instance.preload.preload_data.atk_manager is not None
)
self.qte_manager.receive_hit(single_hit)
self.sim_instance.preload.preload_data.atk_manager.receive_single_hit(
single_hit=single_hit, tick=tick
)
# 在接收hit的时,向所有特殊状态进行广播,执行更新自检!
self.special_state_manager.broadcast_and_update(
signal=SSUS.RECEIVE_HIT, single_hit=single_hit
)
# 遥远的需求:
# TODO:实时DPS的计算,以及预估战斗结束时间,用于进一步优化APL。(例:若目标预计死亡时间<5秒,则不补buff)
def get_total_hp_percentage(self) -> float:
"""获取当前生命值百分比的方法(总量百分比)"""
return 1 - self.dynamic.lost_hp / self.max_HP
def get_current_hp_percentage(self) -> float:
"""获取当前生命值百分比的方法(小血条百分比)"""
return 1 - self.dynamic.lost_hp % self.base_max_HP / self.base_max_HP
def get_stun_percentage(self) -> float:
"""获取当前失衡值百分比的方法"""
return self.dynamic.stun_bar / self.max_stun
def get_stun_rest_tick(self) -> float:
"""获取当前剩余失衡时间的方法"""
# TODO:未完全实现!连携技返还失衡时间部分尚未完成。
if not self.dynamic.stun:
return 0
return (
self.stun_recovery_time
- self.dynamic.stun_tick
+ self.dynamic.stun_tick_feed_back_from_QTE
)
def stun_judge(self, _tick: int, **kwargs) -> bool:
"""判断敌人是否处于 失衡 状态,并更新 失衡 状态"""
single_hit = kwargs.get("single_hit", None)
if not self.able_to_be_stunned:
self.dynamic.stun_update_tick = _tick
return False
if self.dynamic.stun:
# 如果已经是失衡状态,则判断是否恢复
if (
self.stun_recovery_time + self.dynamic.stun_tick_feed_back_from_QTE
<= self.dynamic.stun_tick
):
self.dynamic.stun_update_tick = _tick
self.restore_stun()
else:
if _tick - self.dynamic.stun_update_tick > 1:
raise ValueError("状态更新间隔大于1!存在多个tick都未更新stun的情况!")
self.dynamic.stun_bar = 0 # 避免 log 差错
self.dynamic.stun_update_tick = _tick
# 若怪物当前处于冻结状态,则不增加stun_tick
if not self.dynamic.frozen:
self.dynamic.stun_tick += 1
# else:
# print("检测到怪物当前处于冻结状态,所以不会增加stun_tick!!")
elif self.dynamic.stun_bar >= self.max_stun:
# 若是检测到失衡状态的上升沿,则应该开启彩色失衡状态。
assert self.qte_manager is not None
self.qte_manager.qte_data.reset()
print("怪物陷入失衡了!")
self.dynamic.stun = True
self.dynamic.stun_bar = 0 # 避免 log 差错
self.dynamic.stun_update_tick = _tick
if single_hit:
self.sim_instance.decibel_manager.update(single_hit=single_hit, key="stun")
self.sim_instance.listener_manager.broadcast_event(
event=single_hit, signal=LBS.STUN
)
assert self.sim_instance.preload.preload_data.atk_manager is not None
if self.sim_instance.preload.preload_data.atk_manager.attacking:
self.sim_instance.preload.preload_data.atk_manager.interrupted(
tick=_tick, reason="被打进失衡"
)
return self.dynamic.stun
def __HP_update(self, dmg_expect: np.float64) -> None:
"""用于更新敌人已损生命值"""
self.dynamic.lost_hp += dmg_expect
if (minus := self.max_HP - self.dynamic.lost_hp) <= 0:
self.dynamic.lost_hp = -1 * minus
report_to_log(f"怪物{self.name}死亡!")
def __anomaly_prod(
self, snapshot: tuple[int, np.float64, np.ndarray], single_hit: SingleHit
) -> None:
"""用于更新异常条的角色面板快照"""
if snapshot[1] >= 1e-6: # 确保非零异常值才更新
element_type_code = snapshot[0]
updated_bar = self.anomaly_bars_dict[element_type_code]
updated_bar.update_snap_shot(snapshot, single_hit=single_hit)
def reset_myself(self):
self.dynamic.reset_myself()
self.reset_anomaly_bars()
assert self.qte_manager is not None
self.qte_manager.reset_myself()
self.attack_method.reset_myself()
if self.unique_machanic_manager is not None:
self.unique_machanic_manager.reset_myself()
def reset_anomaly_bars(self):
"""重置异常条!"""
max_element_anomaly, self.max_anomaly_PHY = self.__init_enemy_anomaly(
self.able_to_get_anomaly,
self.QTE_triggerable_times,
self.enemy_adjust["异常积蓄值上限"],
)
self.max_anomaly_ICE = self.max_anomaly_FIRE = self.max_anomaly_ETHER = (
self.max_anomaly_ELECTRIC
) = self.max_anomaly_FIREICE = max_element_anomaly
for element_type, anomaly_bar in self.anomaly_bars_dict.items():
anomaly_bar.reset_myself()
max_value = getattr(
self, f"max_anomaly_{self.trans_element_number_to_str[element_type]}"
)
anomaly_bar.max_anomaly = max_value
def find_dot(self, dot_tag: str) -> object | None:
"""通过dot名,查找enemy身上是否存在此dot"""
for dots in self.dynamic.dynamic_dot_list:
if dots.ft.index == dot_tag and dots.dy.active:
return dots
else:
continue
else:
return None
class EnemyDynamic:
def __init__(self, enemy_instance):
self.enemy: Enemy = enemy_instance
self.stun = False # 失衡状态
self.stun_update_tick = 0 # 上次更新失衡状态的时间
self.frozen = False # 冻结状态
self.frostbite = False # 霜寒状态
self.frost_frostbite = False # 烈霜霜寒状态
self._assault = False # 畏缩状态
self.shock = False # 感电状态
self.burn = False # 灼烧状态
self.corruption = False # 侵蚀状态
self.auricink_corruption = False # 玄墨侵蚀状态
self.dynamic_debuff_list = [] # 用来装debuff的list
# from zsim.sim_progress.data_struct.monitor_list_class import MonitoredList
# self.dynamic_dot_list = MonitoredList() # 用来装dot的list
self.dynamic_dot_list = [] # 用来装dot的list
self.active_anomaly_bar_dict: dict[int, type[AnomalyBar] | None] = {
number: AnomalyBar for number in range(6)
} # 用来装激活属性异常的字典。
self.stun_bar = 0 # 累计失衡条
self.lost_hp = 0 # 已损生命值
self.stun_tick = 0 # 失衡已进行时间
self.stun_tick_feed_back_from_QTE = 0 # 从QTE中返还的失衡时间
self.frozen_tick = 0
self.frostbite_tick = 0
self.assault_tick = 0
self.shock_tick = 0
self.burn_tick = 0
self.corruption_tick = 0
@property
def assault(self) -> bool:
return self._assault
@assault.setter
def assault(self, value: bool):
# 由于监听器可能需要更新,所以这里要先赋值,再广播;
self._assault = value
if value:
# 检查更新值并且广播给各监听器(目前只为爱丽丝核心被动Dot触发器服务)
sim_instance = self.enemy.sim_instance
sim_instance.listener_manager.broadcast_event(
signal=LBS.ASSAULT_STATE_ON, event=self.enemy.anomaly_bars_dict[0]
)
def __str__(self):
return f"失衡: {self.stun}, 失衡条: {self.stun_bar:.2f}, 冻结: {self.frozen}, 霜寒: {self.frostbite}, 畏缩: {self.assault}, 感电: {self.shock}, 灼烧: {self.burn}, 侵蚀:{self.corruption}, 烈霜霜寒:{self.frost_frostbite}"
def get_status(self) -> dict:
return {
"失衡状态": self.stun,
"失衡条": self.stun_bar,
"已损生命值": self.lost_hp,
"冻结": self.frozen,
"霜寒": self.frostbite,
"畏缩": self.assault,
"感电": self.shock,
"灼烧": self.burn,
"侵蚀": self.corruption,
"烈霜霜寒": self.frost_frostbite,
"玄墨侵蚀": self.auricink_corruption,
}
def reset_myself(self):
self.stun: bool = False
self.stun_update_tick: int = 0
self.frozen: bool = False
self.frostbite: bool = False
self.frost_frostbite: bool = False
self.assault: bool = False
self.shock: bool = False
self.burn: bool = False
self.corruption: bool = False
self.dynamic_debuff_list: list = []
self.dynamic_dot_list: list = []
self.active_anomaly_bar_dict = {number: None for number in range(6)}
self.stun_bar: float = 0
self.lost_hp: float = 0
self.stun_tick: int = 0
self.frozen_tick: int = 0
self.frostbite_tick: int = 0
self.assault_tick: int = 0
self.shock_tick: int = 0
self.burn_tick: int = 0
self.corruption_tick: int = 0
self.stun_tick_feed_back_from_QTE: int = 0
def is_under_anomaly(self) -> bool:
"""若敌人正处于任意一种异常状态下,都会返回True"""
return any(
[
self.frostbite,
self.frost_frostbite,
self.assault,
self.burn,
self.corruption,
self.shock,
self.auricink_corruption,
]
)
def get_active_anomaly(self) -> list[type[AnomalyBar] | None]:
if self.is_under_anomaly():
return [
_anomaly_bar
for _anomaly_bar in self.active_anomaly_bar_dict.values()
if _anomaly_bar is not None and _anomaly_bar.active
]
else:
return []
def __str__(self):
return f"{self.name}: {self.dynamic.__str__()}"
def __deepcopy__(self, memo):
return self
if __name__ == "__main__":
test = Enemy(index_id=11432, sub_ID=900011432)
print(test.ice_anomaly_bar.max_anomaly)
================================================
FILE: zsim/sim_progress/Load/LoadDamageEvent.py
================================================
from zsim.sim_progress.Preload import SkillNode
# import Enemy
from zsim.sim_progress.Report import report_to_log
from .. import Dot
from .loading_mission import LoadingMission
def SpawnDamageEvent(mission: LoadingMission | Dot.Dot, event_list: list):
"""
负责往event_list中添加伤害生成事件,添加的内容是实例:
要么是SkillNode的实例,要么是Dot的实例。
"""
if isinstance(mission, LoadingMission):
if mission.hitted_count > mission.mission_node.hit_times:
raise ValueError(
f"{mission.mission_tag}目前是第{mission.hitted_count},最多{mission.mission_node.hit_times}"
)
mission.hitted_count += 1
event_list.append(mission)
elif isinstance(mission, Dot.Dot):
if (
mission.dy.effect_times > mission.ft.max_effect_times
and not mission.ft.complex_exit_logic
):
raise ValueError("该Dot任务已经完成,应当被删除!")
if mission.anomaly_data is not None:
event_list.append(mission.anomaly_data)
else:
event_list.append(mission.skill_node_data)
def ProcessTimeUpdateDots(timetick: int, dot_list: list, event_list: list):
"""
处理effect_rules == 1的Dot对象,始终检查是否应触发。
"""
for dot in dot_list:
if not isinstance(dot, Dot.Dot):
raise TypeError(f"{dot}不是Dot类!")
# 只处理 effect_rules == 1 的 Dot
if dot.ft.effect_rules == 1:
dot.ready_judge(timetick)
if dot.dy.ready:
dot.dy.last_effect_ticks = timetick
dot.dy.ready = False
dot.dy.effect_times += 1
SpawnDamageEvent(dot, event_list)
def ProcessHitUpdateDots(timetick: int, dot_list: list, event_list: list):
"""
处理effect_rules == 2的Dot对象,只在Mission触发或是Schedule进行检查。
"""
for dot in dot_list:
if not isinstance(dot, Dot.Dot):
raise TypeError(f"{dot}不是Dot类!")
# 只处理 effect_rules == 2 的 Dot
if dot.ft.effect_rules == 2:
dot.ready_judge(timetick)
if dot.dy.ready:
SpawnDamageEvent(dot, event_list)
dot.dy.ready = False
dot.dy.last_effect_ticks = timetick
dot.dy.effect_times += 1
def ProcessFreezLikeDots(timetick: int, enemy, event_list: list, event):
"""
所有碎冰类逻辑的dot都用此函数结算。
"""
dot_list = enemy.dynamic.dynamic_dot_list
skill_tag: str
is_heavy_attack: bool
if isinstance(event, LoadingMission):
skill_tag = event.mission_tag
if not event.is_heavy_hit(timetick):
is_heavy_attack = False
else:
is_heavy_attack = True
elif isinstance(event, SkillNode):
skill_tag = event.skill_tag
if not event.is_heavy_hit(timetick):
is_heavy_attack = False
else:
is_heavy_attack = True
else:
raise TypeError(
f"ProcessFreezLikeDots函数接收到的{event}不是LoadingMission或是SkillNode类!"
)
if not is_heavy_attack:
if "1291_CorePassive" not in skill_tag:
return False
for dot in dot_list[:]:
if not isinstance(dot, Dot.Dot):
raise TypeError(f"{dot}不是Dot类!")
if dot.ft.effect_rules != 4:
continue
dot.ready_judge(timetick)
if dot.dy.ready:
print(f"{skill_tag}结算了碎冰!")
SpawnDamageEvent(dot, event_list)
dot.dy.ready = False
dot.dy.last_effect_ticks = timetick
dot.dy.effect_times += 1
dot_list.remove(dot)
enemy.dynamic.frozen = False
return True
def DamageEventJudge(
timetick: int,
load_mission_dict: dict,
enemy,
event_list: list,
char_obj_list: list,
**kwargs,
):
"""
DamageEvent的Judge函数:轮询load_mission_dict以及enemy.dynamic_dot_list,判断是否应生成Hit事件。
并且当Hit时间生成时,将对应的实例添加到event_list中。
当前可能产生Hit的mission类型共有两种,第一种是动作类,第二种是Dot类。
1-动作类:
首先应该查询mission.mission_dict,并且查询所有的键值,检查是否有键值需要在本tick处理。
如果有,则应该将mission.mission_node传递给Schedule Event List。
2-Dot类:
首先应明确是固定随时间变化的Dot,还是命中后才产生伤害的Dot。这一条件以Dot.effect_rules来区分。
如果effect_rules = 1,则表明是仅根据时间和内置CD来产生伤害的,则应该每个Tick都随着本函数执行一次判断;
如果effect_rules = 2,则表明是根据命中来产生伤害的,则应该和动作类mission一起判断。
同时,本函数还会在子任务是end的时候检查enemy的积蓄值。如果积蓄值满,则会触发异常(update_anomaly函数)
"""
# 处理 Load.Mission 任务
# dynamic_buff_dict = kwargs.get("dynamic_buff_dict", None)
process_overtime_mission(timetick, load_mission_dict)
for mission in load_mission_dict.values():
if not isinstance(mission, LoadingMission):
raise TypeError(f"{mission}不是LoadingMission类!")
if mission.is_hit_now(timetick):
SpawnDamageEvent(mission, event_list)
# 当Mission触发时,检查 effect_rules == 2 的 Dot
# ProcessHitUpdateDots(timetick, enemy.dynamic.dynamic_dot_list, event_list)
# 始终检查 effect_rules == 1 的 Dot
ProcessTimeUpdateDots(timetick, enemy.dynamic.dynamic_dot_list, event_list)
# TODO:预留接口:处理effect_rules == 3 的buff(但是涉及快照)
def process_overtime_mission(tick: int, Load_mission_dict: dict):
"""去除过期任务!"""
to_remove = []
for key, mission in Load_mission_dict.items():
if not isinstance(mission, LoadingMission):
continue
mission.check_myself(tick)
if not mission.mission_active_state:
if key not in to_remove:
to_remove.append(key)
for key in to_remove:
report_to_log(
f"[Skill LOAD]:{tick}:{Load_mission_dict[key].mission_tag}已经结束,已从Load中移除",
level=2,
)
Load_mission_dict.pop(key)
# for mission_key, mission in Load_mission_dict.items():
# if mission_key == '1331_CoAttack_A':
# print(mission_key, mission.mission_node.preload_tick, mission.mission_node.end_tick)
================================================
FILE: zsim/sim_progress/Load/SkillEventSplit.py
================================================
from zsim.sim_progress import Load, Preload
from zsim.sim_progress.data_struct import ActionStack
def SkillEventSplit(
preloaded_action_list: list,
Load_mission_dict: dict,
name_dict: dict,
timenow,
action_stack: ActionStack,
):
# 新增新的loading mission
for i in range(len(preloaded_action_list)):
skill = preloaded_action_list.pop()
if not isinstance(skill, Preload.SkillNode):
raise ValueError(f"本次拆分的{type(skill)}不是SkillNode类!")
# print(f"{timenow}tick:技能{skill.skill_tag}正式生效")
this_mission = Load.LoadingMission(skill)
this_mission.mission_start(timenow)
action_stack.push(this_mission)
if skill.skill_tag in name_dict:
name_dict[skill.skill_tag] += 1
else:
name_dict[skill.skill_tag] = 1
key = skill.skill_tag + f"[{name_dict[skill.skill_tag]}]"
Load_mission_dict[key] = this_mission
return Load_mission_dict
if __name__ == "__main__": # 测试
import tqdm
timelimit = 3600
load_mission_dict = {}
p = Preload.Preload()
name_dict = {}
for tick in tqdm.trange(timelimit):
p.do_preload(tick)
preload_action_list = p.preload_data.preloaded_action
if not preload_action_list:
continue
SkillEventSplit(preload_action_list, load_mission_dict, name_dict)
for item in load_mission_dict:
print(f"{item}, {load_mission_dict[item].mission_character_number}")
# TODO: 将SkillEventSplit中,对于load_mission_dict的维护部分单独拆分,并且每个ticks查询,如果当前有hit子任务,则直接向schedule_event_list添加对应的SkillNode
================================================
FILE: zsim/sim_progress/Load/__init__.py
================================================
from .LoadDamageEvent import DamageEventJudge
from .loading_mission import LoadingMission
from .SkillEventSplit import SkillEventSplit
__all__ = [
"DamageEventJudge",
"LoadingMission",
"SkillEventSplit",
]
================================================
FILE: zsim/sim_progress/Load/loading_mission.py
================================================
from zsim.sim_progress.Preload import SkillNode
from zsim.sim_progress.Report import report_to_log
class LoadingMission:
def __init__(self, mission: SkillNode):
self.mission_active_state = False
self.mission_node = mission
self.mission_dict = {}
self.mission_start_tick = mission.preload_tick
self.hitted_count = 0 # 已经结算的hit数量
self.mission_tag = mission.skill_tag
self.mission_end_tick = mission.end_tick
self.mission_character = mission.char_name
self.preload_tick = mission.preload_tick
self.mission_node.loading_mission = self # type: ignore
def mission_start(self, timenow: int, **kwargs) -> None:
report = kwargs.get("report", True)
self.mission_active_state = True
timecost = self.mission_node.skill.ticks
if timecost:
time_step = (timecost - 1) / (self.mission_node.hit_times + 1)
self.mission_dict[float(self.mission_node.preload_tick)] = "start"
# if self.mission_node.hit_times == 1:
# self.mission_dict[float(self.mission_node.preload_tick+timecost - 1)/2] = "hit"
# else:
if self.mission_node.skill.tick_list:
for hit_tick in self.mission_node.skill.tick_list:
tick_key = self.mission_node.preload_tick + hit_tick
self.mission_dict[tick_key] = "hit"
else:
for i in range(self.mission_node.hit_times):
tick_key = self.mission_node.preload_tick + time_step * (i + 1)
# 由于timetick在循环中的自增量是整数,所以为了保证能和键值准确匹配,
# 这里的键值也要向上取整,注意,这里产生的是一个int,所以要转化为float
self.mission_dict[tick_key] = "hit"
self.mission_dict[float(self.mission_node.preload_tick + timecost)] = "end"
report_to_log(
f"[Skill LOAD]:{timenow}:{self.mission_tag}开始并拆分子任务。", level=4
) if report else None
else:
self.mission_dict[timenow] = "hit"
def mission_end(self) -> None:
self.mission_active_state = False
self.hitted_count = 0
self.mission_dict = {}
def check_myself(self, timenow: int) -> None:
if self.mission_end_tick < timenow:
self.mission_end()
return
def get_first_hit(self) -> int | None:
"""返回首次命中的时间"""
tick_list = list(self.mission_dict.keys())
while tick_list:
tick = min(tick_list)
if self.mission_dict[tick] == "hit":
return tick
else:
tick_list.remove(tick)
def is_hit_now(self, tick_now: int) -> bool:
"""检测当前tick是否有hit事件。"""
for _tick in self.mission_dict.keys():
if tick_now - 1 < _tick <= tick_now:
if self.mission_dict[_tick] == "hit":
return True
else:
return False
def get_last_hit(self) -> int | None:
"""返回最后一次命中的时间"""
tick_list = list(self.mission_dict.keys())
while tick_list:
tick = max(tick_list)
if self.mission_dict[tick] == "hit":
return tick
else:
tick_list.remove(tick)
def is_first_hit(self, tick: int) -> bool:
first_hit = self.get_first_hit()
if first_hit is None:
return False
return tick - 1 < first_hit <= tick
def is_last_hit(self, tick: int) -> bool:
last_hit = self.get_last_hit()
if last_hit is None:
return False
return tick - 1 < last_hit <= tick
def is_heavy_hit(self, tick: int) -> bool:
if not self.is_last_hit(tick):
return False
else:
if self.mission_node.skill.heavy_attack:
return True
else:
return False
================================================
FILE: zsim/sim_progress/Preload/APLModule/APLClass.py
================================================
import json
from typing import TYPE_CHECKING
from zsim.define import APL_NA_ORDER_PATH, CHAR_PARRY_STRATEGY_MAP
from zsim.sim_progress.data_struct.NormalAttackManager import (
BaseNAManager,
na_manager_factory,
)
from zsim.sim_progress.Preload.APLModule.ActionReplaceManager import (
ActionReplaceManager,
)
from ..apl_unit.ActionAPLUnit import ActionAPLUnit
from .APLOperator import APLOperator
if TYPE_CHECKING:
from zsim.sim_progress.Character.character import Character
from zsim.sim_progress.Preload import PreloadData
from zsim.simulator.simulator_class import Simulator
class APLClass:
"""
APL代码的执行部分。它会调用apl_condition并且轮询所有的APL代码,
找出第一个符合条件的动作并且执行。
"""
def __init__(
self,
all_apl_unit_list: list,
preload_data: "PreloadData | None" = None,
sim_instance: "Simulator | None" = None,
):
self.game_state: dict | None = None
self.sim_instance: "Simulator | None" = sim_instance
self.preload_data = preload_data
self.actions_list = all_apl_unit_list
self.na_manager_dict: dict[int, BaseNAManager] = {}
try:
json_path = APL_NA_ORDER_PATH
with open(json_path, "r", encoding="utf-8") as file:
self.NA_action_dict = json.load(file)
except (FileNotFoundError, json.JSONDecodeError) as e:
print(f"初始化时发生错误: {e}")
self.NA_action_dict = {}
self.apl_operator: APLOperator | None = None
self.repeat_action: tuple[str, int] = ("", 0)
self.action_replace_manager = None
self.char_obj_dict: dict[int, "Character"] = {}
def execute(self, tick, mode: int) -> tuple[str, int, ActionAPLUnit]:
if self.game_state is None:
self.get_game_state()
if self.apl_operator is None:
assert self.preload_data is not None, "Preload_data is not initialized"
assert self.sim_instance is not None, "Simulator instance is not initialized"
assert self.game_state is not None, "Game state is not initialized"
self.apl_operator = APLOperator(
self.actions_list,
self.game_state,
simulator_instance=self.sim_instance,
preload_data=self.preload_data,
)
assert self.preload_data is not None
assert self.apl_operator is not None
assert self.preload_data.atk_manager is not None
cid, skill_tag, apl_priority, apl_unit = (
self.apl_operator.spawn_next_action_in_common_mode(tick)
if not self.preload_data.atk_manager.attacking
else self.apl_operator.spawn_next_action_in_atk_response_mode(tick)
)
final_result = self.perform_action(cid, skill_tag, tick)
# FIXME: 这里的优先级修改可能存在问题,需要重新考虑一下。
# if final_result != skill_tag:
# apl_priority = 0
return final_result, apl_priority, apl_unit
def get_game_state(self) -> dict | None:
if self.game_state is None:
try:
# 延迟从 sys.modules 获取字典A,假设 main 模块中已定义字典 A
# main_module = sys.modules["simulator.main_loop"]
# if main_module is None:
# raise ImportError("Main module not found.")
# self.game_state = main_module.game_state # 获取 main 中的 A
if self.preload_data and self.preload_data.sim_instance:
self.game_state = self.preload_data.sim_instance.game_state
except Exception as e:
print(f"Error loading dictionary A: {e}")
return self.game_state
def perform_action(self, CID: int, action: str, tick: int) -> str:
"""APL逻辑判定通过,执行动作!"""
if self.game_state is None:
self.game_state = self.get_game_state()
if self.preload_data is None:
assert self.game_state is not None, "Game state is not initialized"
preload_from_game_state = self.game_state.get("preload")
assert preload_from_game_state is not None, "Preload object not in game_state"
self.preload_data = preload_from_game_state.preload_data
output = self.action_processor(CID, action, tick)
return output
def action_processor(self, CID, action, tick) -> str:
"""用于生成动作,以及模拟游戏内的部分动作替换逻辑"""
if self.action_replace_manager is None:
self.action_replace_manager = ActionReplaceManager(self.preload_data)
result_tupe = self.action_replace_manager.action_replace_factory(CID, action, tick)
if result_tupe[0]:
output = result_tupe[1]
else:
output = self.spawn_action_directly(CID, action)
return output
def spawn_action_directly(self, CID: int, action: str):
"""在没有被快速支援、或是其他技能拦截的情况下,直接生成动作"""
assert self.preload_data is not None
if CID not in self.char_obj_dict:
assert self.sim_instance.char_data is not None, "Preload_data is not initialized"
for _char_obj in self.sim_instance.char_data.char_obj_list:
if _char_obj.CID == CID:
self.char_obj_dict[CID] = _char_obj
break
else:
raise ValueError(f"在构造普攻管理器时,未找到CID为{CID}的角色!")
if action == "auto_NA":
if CID not in self.na_manager_dict:
assert self.preload_data.char_data is not None
for _char_obj in self.preload_data.char_data.char_obj_list:
if _char_obj.CID == CID:
self.na_manager_dict[CID] = na_manager_factory(_char_obj)
break
else:
raise ValueError(f"在构造普攻管理器时,未找到CID为{CID}的角色!")
current_na_manager = self.na_manager_dict[CID]
# stack = self.preload_data.personal_node_stack.get(CID, None)
stack = self.preload_data.personal_active_generation_node_stack.get(CID, None)
if stack is None:
last_action = None
else:
last_action = stack.peek()
if last_action is None or CID != self.preload_data.operating_now:
"""没有第一个动作时,直接派生第一段普攻"""
output = current_na_manager.first_hit
else:
output = current_na_manager.spawn_out_na(last_action)
elif action == "assault_after_parry":
if CID in CHAR_PARRY_STRATEGY_MAP:
output = CHAR_PARRY_STRATEGY_MAP[CID]
else:
output = f"{CID}_Assault_Aid"
else:
output = action
char_obj = self.char_obj_dict[CID]
final_output = char_obj.personal_action_replace_strategy(action=output)
return final_output
================================================
FILE: zsim/sim_progress/Preload/APLModule/APLJudgeTools/CheckCID.py
================================================
import re
def check_cid(check_target):
if len(check_target) != 4 or not bool(re.match(r"^-?\d+$", check_target)):
"""检测self.check_target是否是4位int"""
raise ValueError(f"子条件中的CID格式不对!{check_target}")
# TODO: 应该在判断通过的同时输出CID!
================================================
FILE: zsim/sim_progress/Preload/APLModule/APLJudgeTools/CheckNumberType.py
================================================
def check_number_type(text):
"""
将文本的整型、浮点数、布尔值、元组转化为正常格式,纯文本不变、若是类型不是文本则直接输出。
:param text: 输入的参数,可能是文本或已经正确的类型
:return: 转换后的值
"""
if isinstance(text, str):
# 尝试转换为整型
try:
return int(text)
except ValueError:
pass
# 尝试转换为浮点数
try:
return float(text)
except ValueError:
pass
# 尝试转化None
if text.lower() == "none":
return None
# 尝试转换为布尔值
if text.lower() in ["true", "false"]:
return text.lower() == "true"
# 尝试转换为元组
if text.startswith("(") and text.endswith(")"):
try:
# 去除括号并分割元素
elements = text[1:-1].split(",")
# 递归处理每个元素
return tuple(check_number_type(elem.strip()) for elem in elements)[1]
except ValueError:
pass
# 如果以上转换都失败,返回原文本
return text
elif isinstance(text, tuple):
# 如果是元组,递归处理每个元素
return tuple(check_number_type(elem) for elem in text)[1]
else:
# 如果不是文本类型,直接返回
return text
================================================
FILE: zsim/sim_progress/Preload/APLModule/APLJudgeTools/FindBuff.py
================================================
def find_buff(game_state: dict, char, buff_index):
"""
根据buff的index来找到buff
通常用于判断“当前是否有该Buff激活”
"""
for buffs in game_state["global_stats"].DYNAMIC_BUFF_DICT[char.NAME]:
if buffs.ft.index == buff_index:
return buffs
else:
return None
# elif hasattr(data, key): # 处理类对象
# return get_nested_value(getattr(data, key), key_list[1:])
# else:
# raise ValueError(f'无法解析的数据类型!{data, type(data)}')
================================================
FILE: zsim/sim_progress/Preload/APLModule/APLJudgeTools/FindBuff_0.py
================================================
def find_buff_0(game_state: dict, char, buff_index):
"""
根据buff的index来找到buff
通常用于判断“当前是否有该Buff激活”
"""
for _buff_index, _buff in game_state["load_data"].exist_buff_dict[char.NAME].items():
if _buff_index == buff_index:
return _buff
else:
return None
# elif hasattr(data, key): # 处理类对象
# return get_nested_value(getattr(data, key), key_list[1:])
# else:
# raise ValueError(f'无法解析的数据类型!{data, type(data)}')
================================================
FILE: zsim/sim_progress/Preload/APLModule/APLJudgeTools/FindCharacter.py
================================================
def find_char(found_char_dict: dict, game_state: dict, CID: int):
"""
根据提供的CID,找到对应的char,并且返回、保存在self.found_char_dict中。
每次调用时,会先检查是否在self.found_char_dict中。如果找不到,再扔出去。
"""
if CID in found_char_dict.keys():
return found_char_dict[CID]
for char in game_state["char_data"].char_obj_list:
if char.CID == int(CID):
found_char_dict[char.CID] = char
return char
else:
raise ValueError(f"未找到CID为{CID}的角色!")
================================================
FILE: zsim/sim_progress/Preload/APLModule/APLJudgeTools/GetGameState.py
================================================
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
def get_game_state(sim_instance: "Simulator"):
"""获取game_state"""
return sim_instance.game_state
================================================
FILE: zsim/sim_progress/Preload/APLModule/APLJudgeTools/GetLastAction.py
================================================
from zsim.define import SWAP_CANCEL
def get_last_action(game_state: dict):
"""
注意,这个函数获取的应该是最新的主动动作的名称,
"""
if SWAP_CANCEL:
if game_state is None or game_state["preload"] is None:
return False
return game_state["preload"].preload_data.latest_active_generation_node
else:
last_node = getattr(game_state["preload"].preload_data, "last_node", None)
if last_node is None:
return None
output = last_node.skill_tag
return output
================================================
FILE: zsim/sim_progress/Preload/APLModule/APLJudgeTools/GetNestedValue.py
================================================
def get_nested_value(key_list: list, data):
"""递归获取嵌套结构中的值,一挖到底"""
if not key_list:
return data
if len(key_list) > 1:
key = key_list[0]
if isinstance(data, dict) and key in data:
return get_nested_value(data[key], key_list[1:])
elif isinstance(data, list | tuple) and isinstance(key, int) and 0 <= key < len(data):
return get_nested_value(data[key], key_list[1:])
elif len(key_list) == 1:
key = key_list[0]
if isinstance(data, dict) and key in data:
return data[key]
elif isinstance(data, list | tuple) and isinstance(key, int) and 0 <= key < len(data):
return data[key]
else:
raise ValueError(f"无法解析的数据类型!{data, type(data)}")
================================================
FILE: zsim/sim_progress/Preload/APLModule/APLJudgeTools/GetPersonalNodeStack.py
================================================
def get_personal_node_stack(game_state):
return game_state["preload"].preload_data.personal_node_stack
================================================
FILE: zsim/sim_progress/Preload/APLModule/APLJudgeTools/__init__.py
================================================
from .CheckCID import check_cid
from .CheckNumberType import check_number_type
from .FindBuff import find_buff
from .FindBuff_0 import find_buff_0
from .FindCharacter import find_char
from .GetGameState import get_game_state
from .GetLastAction import get_last_action
from .GetNestedValue import get_nested_value
from .GetPersonalNodeStack import get_personal_node_stack
__all__ = [
"check_number_type",
"find_char",
"get_nested_value",
"find_buff",
"get_last_action",
"get_game_state",
"find_buff_0",
"check_cid",
"get_personal_node_stack",
]
================================================
FILE: zsim/sim_progress/Preload/APLModule/APLManager.py
================================================
import os
from typing import TYPE_CHECKING, Optional
from zsim.define import COSTOM_APL_DIR, DEFAULT_APL_DIR
from .APLClass import APLClass
from .APLParser import APLParser
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
from .. import PreloadData
class APLManager:
"""APL管理器,用于管理和加载APL代码文件"""
def __init__(self, sim_instance: "Simulator | None" = None):
self.default_apl_dir = DEFAULT_APL_DIR
self.custom_apl_dir = COSTOM_APL_DIR
self._ensure_directories()
self.sim_instance = sim_instance
def _ensure_directories(self):
"""确保必要的目录存在"""
os.makedirs(self.default_apl_dir, exist_ok=True)
os.makedirs(self.custom_apl_dir, exist_ok=True)
def get_apl_path(self, name: str) -> Optional[str]:
"""
获取APL文件的完整路径
:param name: APL文件名(可以带或不带.txt后缀)
:return: 完整的文件路径,如果文件不存在则返回None
"""
if not name.endswith(".txt"):
name = f"{name}.txt"
# 按照优先级依次查找
search_paths = [
os.path.join(self.custom_apl_dir, name),
os.path.join(self.default_apl_dir, name),
]
for path in search_paths:
if os.path.exists(path):
return path
return None
def load_apl(self, path: str, mode: int, preload_data: "PreloadData") -> APLClass:
"""
加载并解析APL文件
:param path: APL文件路径
:param mode: 解析模式(0为普通模式,1为默认配置模式)
:param preload_data: 外部传入的Preload_data
:return: 已初始化的APLClass实例
"""
return APLClass(
APLParser(file_path=path).parse(mode=mode),
preload_data=preload_data,
sim_instance=self.sim_instance,
)
def list_available_apls(self) -> list[str]:
"""
列出所有可用的APL文件
:return: APL文件名列表
"""
apls = []
for directory in [self.default_apl_dir, self.custom_apl_dir]:
if os.path.exists(directory):
apls.extend([f for f in os.listdir(directory) if f.endswith(".txt")])
return list(set(apls)) # 去重
================================================
FILE: zsim/sim_progress/Preload/APLModule/APLOperator.py
================================================
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
from ..apl_unit.ActionAPLUnit import ActionAPLUnit
from ..apl_unit.APLUnit import APLUnit
from ..PreloadDataClass import PreloadData
class APLOperator:
"""APL执行器,负责运行对象化的APL代码,并返回布尔值。"""
def __init__(
self,
all_apl_unit_list,
game_state: dict,
preload_data: "PreloadData",
simulator_instance: "Simulator" = None,
):
self.game_state = game_state
self.preload_data = preload_data
self.found_char_dict = {} # 用于装角色实例,键值是CID
self.leagal_apl_type_list = [
"action+=",
"action.no_swap_cancel+=",
"action.atk_response_positive+=",
"action.atk_response_balance+=",
]
self.sim_instance = simulator_instance
from zsim.sim_progress.Preload.apl_unit.APLUnit import APLUnit
self.apl_unit_inventory: dict[int, APLUnit] = {} # 用于装已经解析过的apl子条件实例。
for unit_dict in all_apl_unit_list:
self.apl_unit_inventory[unit_dict["priority"]] = self.apl_unit_factory(unit_dict)
# print(unit_dict["priority"], unit_dict)
def spawn_next_action_in_common_mode(self, tick) -> tuple[int, str, int, "ActionAPLUnit"]:
"""APL执行器的核心功能函数——筛选出优先级最高的下一个动作(普通模式)"""
atk_response_mode = self.preload_data.atk_manager.attacking
if atk_response_mode:
raise ValueError("在进攻响应模式下,不能调用spawn_next_action_in_common_mode方法!")
for priority, apl_unit in self.apl_unit_inventory.items():
from zsim.sim_progress.Preload.apl_unit.ActionAPLUnit import ActionAPLUnit
from zsim.sim_progress.Preload.apl_unit.AtkResponseAPLUnit import (
AtkResponseAPLUnit,
)
apl_unit: ActionAPLUnit | AtkResponseAPLUnit
if isinstance(apl_unit, AtkResponseAPLUnit):
continue
result, result_box = apl_unit.check_all_sub_units(
self.found_char_dict,
self.game_state,
tick=tick,
sim_instance=self.sim_instance,
preload_data=self.preload_data,
)
if not result:
# if priority in [1] and tick <= 1500:
# print(
# f"这次不通过的APL优先级为{priority},内容为{apl_unit.result} 判定结果为:{result_box}"
# )
continue
else:
if apl_unit.break_when_found_action:
# print(
# f"APL找到了新的最高优先级的动作!优先级为:{apl_unit.priority},输出动作:{apl_unit.result}"
# )
return (
int(apl_unit.char_CID),
apl_unit.result,
apl_unit.priority,
apl_unit,
)
else:
continue
else:
raise ValueError("没有找到符合要求的APL!")
def spawn_next_action_in_atk_response_mode(self, tick) -> tuple[int, str, int, "ActionAPLUnit"]:
"""APL执行器的核心功能函数——筛选出优先级最高的下一个动作(进攻响应模式)"""
if not self.preload_data.atk_manager.attacking:
raise ValueError(
"在非进攻响应模式下,不能调用spawn_next_action_in_atk_response_mode方法!"
)
from zsim.sim_progress.Preload.apl_unit.ActionAPLUnit import ActionAPLUnit
from zsim.sim_progress.Preload.apl_unit.AtkResponseAPLUnit import (
AtkResponseAPLUnit,
)
for priority, apl_unit in self.apl_unit_inventory.items():
if isinstance(apl_unit, ActionAPLUnit | AtkResponseAPLUnit):
result, result_box = apl_unit.check_all_sub_units(
self.found_char_dict,
self.game_state,
tick=tick,
sim_instance=self.sim_instance,
preload_data=self.preload_data,
)
if not result:
continue
else:
return (
int(apl_unit.char_CID),
apl_unit.result,
apl_unit.priority,
apl_unit,
)
else:
raise ValueError("没有找到符合要求的APL!")
def apl_unit_factory(self, apl_unit_dict) -> "APLUnit":
"""构造APL子单元的工厂函数"""
from zsim.sim_progress.Preload.apl_unit.ActionAPLUnit import ActionAPLUnit
from zsim.sim_progress.Preload.apl_unit.AtkResponseAPLUnit import (
AtkResponseAPLUnit,
)
if apl_unit_dict["type"] in ["action+=", "action.no_swap_cancel+="]:
return ActionAPLUnit(apl_unit_dict, sim_instance=self.sim_instance)
elif "action.atk_response" in apl_unit_dict["type"]:
return AtkResponseAPLUnit(apl_unit_dict=apl_unit_dict, sim_instance=self.sim_instance)
elif all(code_str in apl_unit_dict["type"] for code_str in ["a", "c", "t", "i", "o", "n"]):
raise ValueError(f"貌似是拼写错误,当前输入的APL类型为:{apl_unit_dict['type']}")
else:
raise ValueError(f"无法识别的APL类型:{apl_unit_dict['type']}")
# # Optimized Code:
#
# if "enemy" not in judge_code:
# return False
# path, operator, value = self._judge_code_spliter(judge_code)
# accessor = self._access_cache(path) # 缓存访问器
#
# # 获取实际数值
# target_value = accessor(self.game_state["schedule_data"])
# if target_value is None:
# return False
#
# # 执行比较操作(可扩展更多运算符)
# print("Debugging 1st", target_value, '2nd', value)
# return compare_methods_mapping[operator](target_value, type(target_value)(value)) # 保持类型一致
================================================
FILE: zsim/sim_progress/Preload/APLModule/APLParser.py
================================================
import os.path
import re
from typing import Sequence
class APLParser:
def __init__(self, apl_code: str | None = None, file_path: str | None = None):
# 如果传入APL代码,使用它;如果传入文件路径,则从文件中读取
if apl_code is not None:
self.apl_code = apl_code
elif file_path is not None:
self.apl_code = self._read_apl_from_file(file_path)
else:
raise ValueError("Either apl_code or file_path must be provided.")
@staticmethod
def _read_apl_from_file(file_path: str) -> str:
"""从文件中读取APL代码。"""
try:
# 检查文件扩展名
if file_path.endswith(".toml"):
import tomllib
with open(file_path, "rb") as f:
toml_dict: dict = tomllib.load(f)
# 如果存在apl_logic表的logic,返回其内容,否则返回空字符串
return toml_dict.get("apl_logic", {}).get("logic", "")
else:
# 非toml文件按原有方式处理
with open(file_path, "r", encoding="utf-8") as f:
return f.read()
except FileNotFoundError as e:
print(f"Error reading file {file_path}: {e}")
return ""
def parse(self, mode: int) -> list[dict[str, Sequence[str]]]:
"""
apl_code本来是一大串str,现在要通过这个函数,将其变为列表内含多字典的模式。
action下面存的应该是技能ID或是Skill的Triggle_Buff_Level,
而conditions下面存的则是发动动作的条件。
这一步应该在初始化的时候执行。
"""
actions = []
priority = 0
if mode == 0:
priority = 1
selected_char_cid = []
for line in self.apl_code.splitlines():
# 去除空白字符并清理行内注释
line = line.split("#", 1)[0].strip()
# 忽略空行
if not line:
continue
try:
if mode == 0:
# 0. 更新CID,
if int(line[:4]) not in selected_char_cid:
selected_char_cid.append(int(line[:4]))
# 1. 按 '|' 分割字符串
parts = line.split("|")
if len(parts) < 3:
raise ValueError(f"Invalid format: {line}")
# 2. 提取 CID
CID = parts[0]
apl_type = parts[1]
# 3. 提取 action_name 和条件部分
action_name = parts[2]
conditions = parts[3:] # 从第4个元素开始作为条件列表
# 兼容|作为与门表达式符号的逻辑,转为一整条表达式字符串,统一解析出子条件列表
condition_expression = " and ".join(conditions).strip()
conditions, logic_tree = parse_logical_expression(condition_expression)
# 4. 记录解析后的数据
actions.append(
{
"CID": CID,
"type": apl_type.strip(),
"action": action_name.strip(),
"conditions": [cond.strip() for cond in conditions if cond.strip()],
"conditions_tree": logic_tree, # dict表示的逻辑树结构
"priority": priority,
"whole_line": line,
}
)
if mode == 0:
priority += 1
except Exception as e:
print(f"Error parsing line: {line}, Error: {e}")
continue
if mode == 0:
"""
这个if分枝的功能是:部分角色可能因角色特性而存在一些默认优先级最高的行为,
在APL代码进行解析和拆分时,这些优先级最高的代码会被安插在所有APL的最前端。
如果某角色存在着优先级永远最高的默认手法,则可以用这个功能实现,把对应的APL逻辑写到DefaultConfig中即可。
但是注意,DefaultConfig中的所有APL代码均会以最高优先级进行执行,
所以一般情况下还是推荐对APL进行全面定制
"""
for cid in selected_char_cid:
dir_path = "./data/APLData/default_APL"
default_file_name = f"{cid}.txt"
full_path = dir_path + "/" + default_file_name
if not os.path.isfile(full_path):
continue
else:
default_action = APLParser(file_path=full_path).parse(mode=1)
actions[:0] = default_action
return renumber_priorities(actions)
def renumber_priorities(data_list):
seen = set() # 记录已使用的优先级值
current_max = -1 # 跟踪当前最大有效优先级
for item in data_list:
original = item["priority"]
# 策略1:优先保持原有数值(如果未被占用)
if original not in seen:
seen.add(original)
current_max = max(current_max, original)
# 策略2:分配当前最大+1(当原值已被占用时)
else:
current_max += 1
item["priority"] = current_max
seen.add(current_max)
return data_list
def tokenize(expression):
# 括号、and、or 分割,保留分隔符
return re.findall(r"\(|\)|\band\b|\bor\b|[^()\s]+", expression)
def extract_conditions(tokens):
# 提取子条件单元(非运算符和括号)
return sorted(set(t for t in tokens if t not in ("and", "or", "(", ")")))
def parse_expression(tokens):
"""解析逻辑表达式, 返回逻辑树结构, 优先级为()>and>or"""
if not tokens:
return None
def parse_factor(index):
"""解析基本因子:括号或单个条件"""
token = tokens[index]
if token == "(":
subtree, index = parse_or(index + 1)
if tokens[index] != ")":
raise ValueError("缺失右括号")
return subtree, index + 1
else:
return token, index + 1
def parse_and(index):
"""解析 and 级别表达式"""
left, index = parse_factor(index)
items = [left]
while index < len(tokens) and tokens[index] == "and":
right, index = parse_factor(index + 1)
items.append(right)
return {"and": items} if len(items) > 1 else items[0], index
def parse_or(index):
"""解析 or 级别表达式"""
left, index = parse_and(index)
items = [left]
while index < len(tokens) and tokens[index] == "or":
right, index = parse_and(index + 1)
items.append(right)
return {"or": items} if len(items) > 1 else items[0], index
tree, final_index = parse_or(0) # 从最低优先级开始解析
if final_index != len(tokens):
raise ValueError("无法解析整个表达式")
return tree
def parse_logical_expression(expr):
tokens = tokenize(expr)
conditions = extract_conditions(tokens)
logic_tree = parse_expression(tokens)
return conditions, logic_tree
if __name__ == "__main__":
code = "1211|action+=|1211_NA_1|status.enemy:stun==True and !buff.1091:exist→Buff-角色-丽娜-核心被动-穿透率==True"
# actions_list = APLParser(file_path=APL_PATH).parse(mode=0)
actions_list = APLParser(apl_code=code).parse(mode=0)
for sub_dict in actions_list:
print(sub_dict)
================================================
FILE: zsim/sim_progress/Preload/APLModule/ActionReplaceManager.py
================================================
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
from zsim.define import ENEMY_ATTACK_REPORT
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from zsim.sim_progress.data_struct.QuickAssistSystem import QuickAssistManager
if TYPE_CHECKING:
from zsim.sim_progress.data_struct.EnemyAttackEvent import EnemyAttackEventManager
from zsim.sim_progress.Preload import SkillNode
from zsim.sim_progress.Preload.PreloadDataClass import PreloadData
class ActionReplaceManager:
"""
该对象主要用于阻塞、改写APL运行结果。
由于是非常粗暴的接在APL的对外输出函数上进行拦截、修正,
所以该对象的使用必须谨慎,以免大幅度影响APL手法的实现。
"""
def __init__(self, preload_data):
self.preload_data: "PreloadData" = preload_data
self.quick_assist_strategy = self.QuickAssistStrategy(self.preload_data)
self.parry_aid_strategy = self.ParryAidStrategy(self.preload_data)
def action_replace_factory(self, CID: int, action: str, tick: int) -> tuple[bool, str]:
"""该函数主要用于拦截APL的主动动作,使其被其他动作替代,用来模拟各种特殊情况"""
"""如果目前正处于黄光阶段(窗口期),那么此时的所有切人动作都会被无条件换成格挡,哪怕此时快支正处于激活状态"""
assert self.preload_data.sim_instance is not None
if "耀嘉音" in self.preload_data.sim_instance.init_data.name_box:
"""有耀嘉音时,优先检测快支替换"""
if self.quick_assist_strategy.condition_judge(CID=CID, action=action, tick=tick):
quick_assist_strategy_replace_result = self.quick_assist_strategy.spawn_new_action(
CID, action
)
if quick_assist_strategy_replace_result not in ["parry", "wait"]:
return True, quick_assist_strategy_replace_result
parry_replace_result = self.parry_aid_strategy.spawn_new_action(
CID=CID, action=action, tick=tick
)
if parry_replace_result != action:
return True, parry_replace_result
return False, action
else:
"""没有耀嘉音时,优先检测招架替换"""
parry_replace_result = self.parry_aid_strategy.spawn_new_action(
CID=CID, action=action, tick=tick
)
if parry_replace_result != action:
return True, parry_replace_result
if self.quick_assist_strategy.condition_judge(CID=CID, action=action, tick=tick):
quick_assist_strategy_replace_result = self.quick_assist_strategy.spawn_new_action(
CID, action
)
return True, quick_assist_strategy_replace_result
return False, action
class __BaseStrategy(ABC):
def __init__(self, preload_data: "PreloadData"):
self.preload_data: "PreloadData" = preload_data
@abstractmethod
def condition_judge(self, *args, **kwargs) -> bool | str:
pass
@abstractmethod
def spawn_new_action(self, *args, **kwargs) -> str:
pass
class QuickAssistStrategy(__BaseStrategy):
def __init__(self, preload_data):
super().__init__(preload_data)
self.manager_box: dict[int, "QuickAssistManager"] = {}
def condition_judge(self, CID: int, tick: int, action: str, *args, **kwargs) -> bool:
"""
该函数用于判定当前tick的动作是否需要被替换成快速支援:条件如下:
1、当前角色快速支援亮起
2、当前角色企图从后台切到前台
"""
if CID is None or action is None:
raise ValueError("CID或action为空!")
if self.preload_data.quick_assist_system is None:
"""如果快速支援系统的对象还未建立,那么说明此时
根本不可能有导致快速支援替换APL动作的情况发生,直接返回False即可。"""
return False
if CID not in self.manager_box:
for (
manager
) in self.preload_data.quick_assist_system.quick_assist_manager_group.values():
if manager.char.CID == CID:
self.manager_box[CID] = manager
break
else:
raise ValueError(f"没有找到{CID}角色的快速支援管理器!")
current_manager = self.manager_box[CID]
node_on_field = self.preload_data.get_on_field_node(tick - 1)
"""注意,这里传入tick-1的作用:当某些技能不能被合轴与终止时(比如QTE和Q),新动作会被SwapCancelEngine一直拦截,
此时,就会出现1帧时间场上没有任何动作,这会导致调用该函数的一些判断出错。所以将时间提前了1帧,规避这些错误。"""
if node_on_field is None:
# FIXME:这里还是有问题,程序中有时会出现的current_node_on_field 为None的情况,一定会干扰模拟的进行,必须找时间解决掉!
return False
"""当前角色的快速支援正处于激活状态,并且角色企图上场释放技能,则执行替换。"""
if current_manager.quick_assist_available and str(CID) not in node_on_field.skill_tag:
return True
else:
return False
def spawn_new_action(self, CID: int, action: str) -> str:
if action == "wait":
return action
for _obj in self.preload_data.skills:
if _obj.CID != CID:
continue
if "parry" in action:
do_immediately_info = True
elif action == "auto_NA":
do_immediately_info = False
else:
do_immediately_info = _obj.get_skill_info(
skill_tag=action, attr_info="do_immediately"
)
if do_immediately_info:
return action
else:
manager = self.manager_box[CID]
# print(f'执行快速支援!技能{action}替换成了{manager.quick_assist_skill}!')
return manager.quick_assist_skill
else:
raise ValueError(f"没有找到CID为{CID}的技能对象!无法执行快速支援替换!")
class ParryAidStrategy(__BaseStrategy):
"""负责将技能tag替换成各类招架支援的结构"""
def __init__(self, preload_data):
super().__init__(preload_data)
self.consecutive_parry_mode: bool = False # 连续招架模式
self.consecutive_parry_node: "SkillNode | None" = None # 连续招架的技能节点
self.parry_interaction_in_progress: bool = False # 当前轮次招架交互正在进行
self.parry_tag: str | None = None # 当前轮次招架交互的招架技能标签。
from zsim.define import PARRY_BASE_PARAMETERS
self.chain_parry_tick = PARRY_BASE_PARAMETERS[
"ChainParryActionTimeCost"
] # 系统默认的连续招架时间消耗
self._knock_back_signal = False # 击退信号,在末次招架结算时,会由外部数据结构操作接口函数打开,并且在抛出击退信号后置为False
self.final_parry_node: "SkillNode | None" = None
"""每次招架后,角色都会获得一次突击支援的机会,但是衔接突击支援的时间是有要求的,必须在突击支援失效之前进行(角色击退时间)"""
self.assault_aid_disable_tick: int = 0
self.assault_aid_enable: bool = False
@property
def knock_back_signal(self) -> bool:
return self._knock_back_signal
@knock_back_signal.setter
def knock_back_signal(self, value: bool):
# print(f"【ActionReplaceManager】knock_back_signal被重新赋值为{value}")
self._knock_back_signal = value
def condition_judge(self, CID: int, tick: int, action: str, *args, **kwargs) -> bool | str:
"""
用来判断当前情景下,APL抛出的技能tag是否需要被强制替换为对应的招架类Tag。
"""
if self.knock_back_signal:
return "knock_back"
atk_manager = self.preload_data.atk_manager
if atk_manager is None:
return False
char_on_field_cid = self.preload_data.operating_now
if not atk_manager.attacking and not self.knock_back_signal:
return False
# 对于命中次数为0的进攻事件,应该将其送入【首次招架分支】:
if atk_manager.hitted_count == 0:
# 审查招架角色是否拥有招架的能力,是否满足招架的基本条件,同时时间窗口又是否允许?
if self.__first_parry_condition_judge(
atk_manager=atk_manager,
CID=CID,
tick=tick,
action=action,
char_on_field_cid=char_on_field_cid,
):
return "first_parry"
else:
# 命中次数不为0的情况,主要分为两种:【连续招架】以及【单次招架的后续】。
if self.consecutive_parry_mode:
# 若连续招架的开关打开,那么无需任何函数判定,都放行并且返回【连续招架】的更新信号
assert atk_manager.action is not None
if atk_manager.hitted_count == atk_manager.action.hit - 1:
return "final_parry"
else:
return "consecutive_parry"
# else:
# # 若连续招架开关关闭,那么则是【单次招架后续】分支,这里需要考虑是否要抛出“KnockBack”技能,模拟角色被击退。
# if self.knock_back_signal:
# return "knock_back"
return False
def __first_parry_condition_judge(
self,
atk_manager: "EnemyAttackEventManager",
CID: int,
tick: int,
action: str,
char_on_field_cid: int | None,
) -> bool:
"""
判断当前情况是否满足第一次招架。条件有:
0、当前有激活的进攻事件并且结算次数为0(已前置)
1、有角色正尝试切换到前台
2、该角色拥有招架能力
3、时间窗口合法
"""
# 条件1检查——角色尝试切换到前台
if char_on_field_cid is not None:
if char_on_field_cid == CID:
return False
# 条件2检查——角色有招架能力
if self.preload_data.sim_instance is None:
return False
char_obj = self.preload_data.sim_instance.char_data.find_char_obj(CID=CID)
if char_obj is None:
return False
if char_obj.aid_type != "招架":
if "parry" in action.lower():
raise ValueError(
f"APL尝试让 {char_obj.NAME} 进行招架操作,但是该角色没有招架能力!"
)
return False
# 条件3检查——时间窗口合法
if not atk_manager.is_in_response_window(tick=tick):
return False
else:
return True
def spawn_new_action(self, CID: int, action: str, tick: int, *args, **kwargs) -> str:
"""根据当前的状态,执行招架支援tag的替换。"""
replace_signal: bool | str = self.condition_judge(CID=CID, action=action, tick=tick)
# 若条件判断返回的是False,则说明不执行替换,返回原始tag。
if not replace_signal:
return action
atk_manager = self.preload_data.atk_manager
assert atk_manager is not None
if replace_signal == "first_parry":
return self.__first_parry_replace_handler(
atk_manager=atk_manager, CID=CID, action=action, tick=tick
)
elif replace_signal == "consecutive_parry":
return self.__consecutive_parry_replace_handler(
atk_manager=atk_manager, CID=CID, action=action, tick=tick
)
elif replace_signal == "knock_back":
return self.__knock_back_replace_handler(CID=CID)
elif replace_signal == "final_parry":
return self.__final_parry_replace_handler(atk_manager=atk_manager, CID=CID)
else:
raise ValueError(f"无法识别的替换信号:{replace_signal}!")
def spawn_parry_aid_tag(self, CID: int, mode: int) -> str:
if mode == 0:
return f"{CID}_Light_parry_Aid"
elif mode == 1:
assert self.consecutive_parry_node is not None
return (
f"{self.consecutive_parry_node.skill_tag.strip().split('_')[0]}_Chain_parry_Aid"
)
elif mode == 2:
assert self.consecutive_parry_node is not None
return (
f"{self.consecutive_parry_node.skill_tag.strip().split('_')[0]}_Heavy_parry_Aid"
)
elif mode == 3:
"""这是衔接在招架支援后的后退动作,无法取消。"""
assert self.final_parry_node is not None
return f"{self.final_parry_node.skill_tag.strip().split('_')[0]}_knock_back_cause_parry"
else:
raise ValueError(f"不支持的招架模式:{mode}!")
def __first_parry_replace_handler(
self,
atk_manager: "EnemyAttackEventManager",
CID: int,
action: str,
tick: int,
) -> str:
"""负责处理“首次招架”分支的tag替换业务"""
# 注意,执行本函数的情况,正常情况下总是符合时间窗口的(APL和前方的信号函数都已经处理过这个逻辑了
if not atk_manager.is_in_response_window(tick=tick):
raise ValueError("首次招架的技能标签替换失败,因为当前时间窗口已经过期!")
if atk_manager.action is None:
raise ValueError("atk_manager.action is None, but it should not be.")
if atk_manager.action.hit_type in ["Light", "Chain"]:
return self.spawn_parry_aid_tag(CID=CID, mode=0)
elif atk_manager.action.hit_type == "Heavy":
return self.spawn_parry_aid_tag(CID=CID, mode=2)
else:
raise ValueError(f"不支持的招架技能类型:{atk_manager.action.hit_type}!")
def __consecutive_parry_replace_handler(
self,
atk_manager: "EnemyAttackEventManager",
CID: int,
action: str,
tick: int,
) -> str:
"""
负责处理“连续招架”分支的tag替换业务
当处于连续招架的情况下,应该在窗口合法时,第一时间抛出连续招架,
若时间窗口尚未到来,则抛出wait,
"""
settled_tick = tick + self.chain_parry_tick
if atk_manager.hit_check(int(settled_tick)):
return self.spawn_parry_aid_tag(CID=CID, mode=1)
else:
return "wait"
def __knock_back_replace_handler(self, CID: int) -> str:
"""
负责处理“KnockBack”分支的tag替换业务
该分支的职能是:准确识别“击退信号”并且抛出“击退”动作。
首先,在检测到被击退信号之前,应当保持输出wait,
而在识别到被击退信号之后,输出knock_back
"""
if self.knock_back_signal:
return self.spawn_parry_aid_tag(CID=CID, mode=3)
else:
raise ValueError("并未检测到击退信号!请检查函数逻辑!")
def __final_parry_replace_handler(self, CID: int, atk_manager: "EnemyAttackEventManager"):
"""
负责处理“final_parry”分支的tag替换业务
该分支的职能是:复核验证最后一击的判定结果,
同时根据本次进攻信号的数量,抛出对应的tag
"""
assert atk_manager.action is not None
if atk_manager.hitted_count > atk_manager.action.hit - 1:
raise ValueError(
"当前已结算次数与进攻信号的命中次数不匹配!这种情况理应不该送入“final_parry”分支,请检查函数逻辑。"
)
if atk_manager.action.hit < 3:
return self.spawn_parry_aid_tag(CID=CID, mode=1)
else:
return self.spawn_parry_aid_tag(CID=CID, mode=2)
def update_myself(self, skill_node: "SkillNode", tick: int):
"""开放给外部的更新窗口"""
if skill_node.skill.trigger_buff_level not in [7, 8, 9]:
if "knock_back" not in skill_node.skill_tag:
return
if "Light_parry" in skill_node.skill_tag:
self.parry_interaction_in_progress = True
if ENEMY_ATTACK_REPORT:
if self.preload_data.sim_instance:
self.preload_data.sim_instance.schedule_data.change_process_state()
print(
f"检测到来自于{skill_node.char_name}的招架技能{skill_node.skill_tag}!招架交互开始!"
)
assert self.preload_data.atk_manager is not None
assert self.preload_data.atk_manager.action is not None
if self.preload_data.atk_manager.action.hit > 1:
self.consecutive_parry_mode = True
self.consecutive_parry_node = skill_node
print("当前进攻事件是多次命中的,所以开启连续招架模式!")
elif "knock_back" in skill_node.skill_tag:
if ENEMY_ATTACK_REPORT:
if self.preload_data.sim_instance:
self.preload_data.sim_instance.schedule_data.change_process_state()
print(f"角色 {skill_node.char_name} 因招架而被击退!")
self.knock_back_signal = False
self.final_parry_node = None
self.parry_interaction_in_progress = False
self.assault_aid_enable = True
self.assault_aid_disable_tick = tick + 60
if self.preload_data.sim_instance:
listener_manager = self.preload_data.sim_instance.listener_manager
listener_manager.broadcast_event(event=skill_node, signal=LBS.PARRY)
elif "Assault_Aid" in skill_node.skill_tag:
if tick > self.assault_aid_disable_tick:
raise ValueError(
f"{skill_node.char_name} 企图释放支援突击,但支援突击早就在{self.assault_aid_disable_tick}tick失效!请检查函数逻辑!"
)
self.assault_aid_enable = False
self.assault_aid_disable_tick = tick
if ENEMY_ATTACK_REPORT:
if self.preload_data.sim_instance:
self.preload_data.sim_instance.schedule_data.change_process_state()
print(f"角色 {skill_node.char_name} 在招架完成后释放支援突击!")
elif any(
[_sub_tag in skill_node.skill_tag for _sub_tag in ["Chain_parry", "Heavy_parry"]]
):
# 在检测连续招架或是重招架时,必须检测当前的命中次数,确保正确关闭连续招架状态。
assert self.preload_data.atk_manager is not None
assert self.preload_data.atk_manager.action is not None
if (
self.preload_data.atk_manager.hitted_count
== self.preload_data.atk_manager.action.hit
):
self.consecutive_parry_mode = False
================================================
FILE: zsim/sim_progress/Preload/APLModule/SubConditionUnit/ActionSubUnit.py
================================================
from typing import TYPE_CHECKING
from zsim.sim_progress.Preload.APLModule.APLJudgeTools import (
check_cid,
get_personal_node_stack,
)
from zsim.sim_progress.Preload.APLModule.SubConditionUnit import BaseSubConditionUnit
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
from ... import PreloadClass
from ...APLModule.ActionReplaceManager import ActionReplaceManager
from ...PreloadDataClass import PreloadData
class ActionSubUnit(BaseSubConditionUnit):
def __init__(self, priority: int, sub_condition_dict: dict = None, mode=0):
super().__init__(priority=priority, sub_condition_dict=sub_condition_dict, mode=mode)
class ActionCheckHandler:
@classmethod
def handler(cls, *args, **kwargs):
raise NotImplementedError
class LatestActionTagHandler(ActionCheckHandler):
@classmethod
def handler(cls, char_cid: int, game_state, tick: int) -> str | None:
preload_data: "PreloadData" = game_state["preload"].preload_data
lastest_node = preload_data.latest_active_generation_node
if lastest_node is None:
return None
if lastest_node.end_tick >= tick:
return lastest_node.skill_tag
else:
return None
class StrictLinkedHandler(ActionCheckHandler):
"""强衔接判定,技能skill_tag符合的同时,还需要上一个动作刚好结束。"""
@classmethod
def handler(cls, char_cid: int, game_state, tick: int) -> str | None:
char_stack = get_personal_node_stack(game_state).get(char_cid, None)
if char_stack is None:
return None
else:
for i in range(char_stack.length):
current_node = char_stack.peek_index(i + 1)
if current_node is None:
return None
if (
current_node.skill.labels is not None
and "additional_damage" in current_node.skill.labels
):
continue
if current_node.end_tick != tick:
return None
return current_node.skill_tag
else:
return None
class LenientLinkedHandler(ActionCheckHandler):
@classmethod
def handler(cls, char_cid: int, game_state, tick: int) -> str | None:
char_stack = get_personal_node_stack(game_state).get(char_cid, None)
if char_stack is None:
return None
for i in range(char_stack.length):
current_node = char_stack.peek_index(i)
if current_node is None:
return None
if (
current_node.skill.labels is not None
and "additional_damage" in current_node.skill.labels
):
continue
else:
return current_node.skill_tag
else:
return None
class PositiveLinkedHander(ActionCheckHandler):
@classmethod
def handler(cls, char_cid: int, game_state, tick: int) -> str | None:
"""
积极衔接判定,技能skill_tag符合的同时,
只要上一个动作没有结束,就尝试进行衔接!
一般用于上一个节能可以被自己技能打断的情况,比如闪避。
"""
char_stack = get_personal_node_stack(game_state).get(char_cid, None)
if char_stack is None:
return None
for i in range(char_stack.length):
current_node = char_stack.peek_index(i + 1)
if current_node is None:
return None
if (
current_node.skill.labels is not None
and "additional_damage" in current_node.skill.labels
):
continue
if current_node.end_tick >= tick:
return current_node.skill_tag
else:
return None
class FirstActionHandler(ActionCheckHandler):
@classmethod
def handler(cls, char_cid: int, game_state, tick: int) -> bool:
char_stack = get_personal_node_stack(game_state).get(char_cid, None)
if char_stack is None:
return True
else:
current_node = char_stack.peek()
if current_node is None:
return True
return False
class IsPerformingHandler(ActionCheckHandler):
@classmethod
def handler(cls, char_cid: int, game_state, tick: int) -> None | str:
"""该函数的主要作用是尝试获取角色正在释放的某个技能的skill_tag。如果角色现在有空,则直接返回None"""
char_stack = get_personal_node_stack(game_state).get(char_cid, None)
if char_stack is None:
return None
last_node = char_stack.peek()
if last_node is None:
return None
else:
if last_node.end_tick >= tick:
return last_node.skill_tag
else:
return None
class DuringParryHandler(ActionCheckHandler):
@classmethod
def handler(cls, char_cid: int, game_state, tick: int) -> bool:
"""该函数主要作用是判断当前角色是否正处于进攻交互阶段(招架、或者被击退)"""
preload: "PreloadClass" = game_state["preload"]
action_replace_manager: "ActionReplaceManager" = (
preload.strategy.apl_engine.apl.action_replace_manager
)
if action_replace_manager is None:
return False
parry_strategy = action_replace_manager.parry_aid_strategy
return parry_strategy.parry_interaction_in_progress
class AssaultAidEnableHandler(ActionCheckHandler):
@classmethod
def handler(cls, char_cid: int, game_state, tick: int) -> bool:
"""该函数主要用于检查,当前角色的“支援突击”是否处于激活可用状态"""
preload: "PreloadClass" = game_state["preload"]
action_replace_manager: "ActionReplaceManager" = (
preload.strategy.apl_engine.apl.action_replace_manager
)
if action_replace_manager is None:
return False
assault_aid_enable = action_replace_manager.parry_aid_strategy.assault_aid_enable
return assault_aid_enable
ActionHandlerMap = {
"skill_tag": LatestActionTagHandler,
"strict_linked_after": StrictLinkedHandler,
"lenient_linked_after": LenientLinkedHandler,
"positive_linked_after": PositiveLinkedHander,
"first_action": FirstActionHandler,
"is_performing": IsPerformingHandler,
"during_parry": DuringParryHandler,
"assault_aid_enable": AssaultAidEnableHandler,
}
def check_myself(
self,
found_char_dict,
game_state,
sim_instance: "Simulator" = None,
*args,
**kwargs,
):
"""处理 动作判定类 的子条件"""
handler_cls = self.ActionHandlerMap.get(self.check_stat)
handler = handler_cls() if handler_cls else None
if not handler:
raise ValueError(
f"当前检查的check_stat为:{self.check_stat},优先级为{self.priority},暂无处理该属性的逻辑模块!"
)
if self.check_target in ["after", "team"]:
return self.spawn_result(handler.handler(game_state))
else:
"""check_target 不是 after(其实已经弃用了),就是CID"""
check_cid(self.check_target)
char_cid = int(self.check_target)
tick = sim_instance.tick
handler_result = handler.handler(char_cid, game_state, tick)
# if handler_result is not None:
# print(tick, f"APL优先级为:{self.priority}", self.check_value, handler_result)
result = self.spawn_result(handler_result)
return result
# if self.check_stat == 'skill_tag':
# checked_value = get_last_action(game_state)
# return self.spawn_result(checked_value)
# else:
# raise ValueError(f'子条件中的check_stat为:{self.check_stat},优先级为{self.priority},暂无处理该属性的逻辑模块!')
# else:
# raise ValueError(f'子条件中的check_target为:{self.check_target},优先级为{self.priority},暂无处理该目标类型的逻辑模块!')
================================================
FILE: zsim/sim_progress/Preload/APLModule/SubConditionUnit/AttributeSubUnit.py
================================================
from ...APLModule.APLJudgeTools import check_cid, get_nested_value
from ...APLModule.SubConditionUnit import BaseSubConditionUnit
class AttributeSubUnit(BaseSubConditionUnit):
def __init__(self, priority: int, sub_condition_dict: dict = None, mode=0):
super().__init__(priority=priority, sub_condition_dict=sub_condition_dict, mode=mode)
self.char = None
class AttributeCheckHandler:
@classmethod
def handler(cls, *args, **kwargs):
raise NotImplementedError
class EnergyHandler(AttributeCheckHandler):
@classmethod
def handler(cls, char, **kwargs):
return char.sp
class DecibelHandler(AttributeCheckHandler):
@classmethod
def handler(cls, char, **kwargs):
return char.decibel
class SpecialResourceValueHandler(AttributeCheckHandler):
@classmethod
def handler(cls, char, **kwargs):
return char.get_resources()[1]
class SpecialResourceTypeHandler(AttributeCheckHandler):
@classmethod
def handler(cls, char, **kwargs):
return char.get_resources()[0]
class SpecialStateHandler(AttributeCheckHandler):
@classmethod
def handler(cls, char, nested_stat_key_list: list = None, **kwargs):
if nested_stat_key_list:
return get_nested_value(nested_stat_key_list, char.get_special_stats())
else:
return char.get_special_stats()
class CinemaHandler(AttributeCheckHandler):
@classmethod
def handler(cls, char, **kwargs):
return char.cinema
class AdrenalineHandler(AttributeCheckHandler):
@classmethod
def handler(cls, char, **kwargs):
if not hasattr(char, "adrenaline"):
raise AttributeError(f"尝试在角色{char.NAME}中访问闪能!")
return char.adrenaline
AttributeHandlerMap = {
"energy": EnergyHandler,
"decibel": DecibelHandler,
"special_resource": SpecialResourceValueHandler,
"special_resource_type": SpecialResourceTypeHandler,
"special_state": SpecialStateHandler,
"cinema": CinemaHandler,
"adrenaline": AdrenalineHandler,
}
def check_myself(self, found_char_dict, game_state: dict, *args, **kwargs):
"""处理 属性判定类 的子条件"""
tick = kwargs.get("tick", None)
check_cid(self.check_target)
if self.char is None:
from zsim.sim_progress.Preload import find_char
self.char = find_char(found_char_dict, game_state, int(self.check_target))
handler_cls = self.AttributeHandlerMap.get(self.check_stat)
handler = handler_cls() if handler_cls else None
if not handler:
raise ValueError(
f"当前检查的check_stat为:{self.check_stat},优先级为{self.priority},暂无处理该属性的逻辑模块!"
)
if self.check_stat != "special_state":
return self.spawn_result(handler.handler(self.char, tick=tick))
else:
return self.spawn_result(
handler.handler(self.char, self.nested_stat_key_list, tick=tick)
)
================================================
FILE: zsim/sim_progress/Preload/APLModule/SubConditionUnit/BaseSubConditionUnit.py
================================================
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
from ...APLModule.APLJudgeTools import check_number_type
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
class BaseSubConditionUnit(ABC):
def __init__(self, priority: int, sub_condition_dict: dict = None, mode=0):
"""单个APL判断条件的对象基类。"""
self.logic_mode = mode # mode 为1 时为反逻辑
self.priority = priority # 优先级
if sub_condition_dict is None:
self.no_condition = True # 无条件
else:
self.no_condition = False
self.check_target = sub_condition_dict["target"] # 检查目标 比如enemy,self,或者角色ID
self.check_stat = None
self.nested_stat_key_list = []
"""
在check_stat难免会碰到多层的嵌套结构,在APL中,统一用'→'来表示嵌套结构,这个list就是用来装这些嵌套的key的。
目前,nested_stat_key_list只为char.get_special_states函数服务
"""
if "→" in sub_condition_dict["stat"]: # 被检查的参数,这里有很多,不一一列举。
self.check_stat = sub_condition_dict["stat"].split("→")[0]
self.nested_stat_key_list = sub_condition_dict["stat"].split("→")[1:]
else:
self.check_stat = sub_condition_dict["stat"]
self.operation_type = sub_condition_dict["operation_type"] # 计算类型,主要是比较符和调用符
self.check_value = check_number_type(
sub_condition_dict["value"]
) # 参与计算的值 或者调用的函数名
@abstractmethod
def check_myself(
self,
found_char_dict,
game_state,
sim_instance: "Simulator" = None,
*args,
**kwargs,
):
pass
def spawn_result(self, value=None, **kwargs):
"""根据self.operation_type中的匿名函数来输出结果的函数"""
# value = check_number_type(value)
# assert value is not None, f"优先级为 {self.priority} 的子条件单元检查结果为 None, {self.check_stat}"
result = self.operation_type(value, self.check_value)
# if self.priority == 11 and result:
# import inspect
# print(f'{self.priority}_result:{value, self.check_value, inspect.getsource(self.operation_type).strip(), result}')
return self.translate_result(result)
def translate_result(self, result):
if self.logic_mode == 0:
return result
else:
return not result
================================================
FILE: zsim/sim_progress/Preload/APLModule/SubConditionUnit/BuffSubUnit.py
================================================
from zsim.sim_progress.Preload.APLModule.APLJudgeTools import (
check_cid,
find_buff,
find_buff_0,
)
from zsim.sim_progress.Preload.APLModule.SubConditionUnit import BaseSubConditionUnit
class BuffSubUnit(BaseSubConditionUnit):
def __init__(self, priority: int, sub_condition_dict: dict = None, mode=0):
super().__init__(priority=priority, sub_condition_dict=sub_condition_dict, mode=mode)
self.buff_0 = None
self.char = None
self.apl_warnning_dict = {}
class BuffCheckHandler:
@classmethod
def handler(cls, *args, **kwargs):
raise NotImplementedError
class BuffExistHandler(BuffCheckHandler):
@classmethod
def handler(cls, game_state, char, buff_0):
search_result = find_buff(game_state, char, buff_0.ft.index)
if search_result is not None:
return True
else:
return False
class BuffCountHandler(BuffCheckHandler):
@classmethod
def handler(cls, game_state, char, buff_0):
search_result = find_buff(game_state, char, buff_0.ft.index)
if search_result is not None:
return search_result.dy.count
else:
return 0
class BuffDurationHandler(BuffCheckHandler):
@classmethod
def handler(cls, game_state, char, buff_0):
search_result = find_buff(game_state, char, buff_0.ft.index)
if search_result is None:
return 0
tick = char.sim_instance.tick
return max(search_result.dy.endticks - tick, 0)
BuffHandlerMap = {
"exist": BuffExistHandler,
"count": BuffCountHandler,
"duration": BuffDurationHandler,
}
def check_myself(self, found_char_dict, game_state, *args, **kwargs):
check_cid(self.check_target)
if self.char is None:
from zsim.sim_progress.Preload import find_char
self.char = find_char(found_char_dict, game_state, int(self.check_target))
if self.buff_0 is None:
buff_index = self.nested_stat_key_list[0]
search_resurt = find_buff_0(game_state, self.char, buff_index)
if search_resurt is not None:
self.buff_0 = search_resurt
else:
if self.priority not in self.apl_warnning_dict:
print(
f"【非法APL警告】优先级为{self.priority}的APL似乎与当前模拟条件不匹配!原因:在{self.char.NAME}身上并未找到名为{buff_index}的Buff!"
)
self.apl_warnning_dict[self.priority] = True
return False
handler_cls = self.BuffHandlerMap[self.check_stat]
handler = handler_cls() if handler_cls else None
if not handler:
raise ValueError(
f"当前检查的check_stat为:{self.check_stat},优先级为{self.priority},暂无处理该属性的逻辑模块!"
)
return self.spawn_result(handler.handler(game_state, self.char, self.buff_0))
================================================
FILE: zsim/sim_progress/Preload/APLModule/SubConditionUnit/SpecialSubUnit.py
================================================
from typing import TYPE_CHECKING
from .BaseSubConditionUnit import BaseSubConditionUnit
if TYPE_CHECKING:
from ...PreloadDataClass import PreloadData
class SpecialSubUnit(BaseSubConditionUnit):
def __init__(self, priority: int, sub_condition_dict: dict = None, mode=0):
super().__init__(priority=priority, sub_condition_dict=sub_condition_dict, mode=mode)
self.preload_data = None
class SpecialHandler:
@classmethod
def handler(cls, *args, **kwargs):
raise NotImplementedError
class OperatingCharacterHandler(SpecialHandler):
@classmethod
def handler(cls, preload_data):
cid = preload_data.operating_now
# print(f'调用了特殊检查,当前正在操作的CID为:{cid},当前被检测的技能为:{preload_data.latest_active_generation_node.skill_tag}')
return cid
class IsAttackingHandler(SpecialHandler):
@classmethod
def handler(cls, preload_data: "PreloadData"):
atk_manager = preload_data.atk_manager
if atk_manager is None:
return False
return atk_manager.attacking
SpecialHandlerMap = {
"operating_char": OperatingCharacterHandler,
"is_attacking": IsAttackingHandler,
}
def check_myself(self, found_char_dict, game_state, *args, **kwargs):
if self.preload_data is None:
preload = game_state.get("preload", None)
if preload is None:
raise ValueError(
"为从gamestate中获取到preload数据,请检查game_state的preload数据是否正常!"
)
self.preload_data = preload.preload_data
handler_cls = self.SpecialHandlerMap.get(self.check_stat)
handler = handler_cls() if handler_cls else None
if not handler:
raise ValueError(
f"当前检查的check_stat为:{self.check_stat},优先级为{self.priority},暂无处理该属性的逻辑模块!"
)
__result = self.spawn_result(handler.handler(self.preload_data))
# if __result and self.priority == 6:
# print(handler.handler(self.preload_data)), print(self.preload_data.latest_active_generation_node.skill_tag)
return __result
================================================
FILE: zsim/sim_progress/Preload/APLModule/SubConditionUnit/StatusSubUnit.py
================================================
from typing import TYPE_CHECKING
from zsim.sim_progress.Buff.JudgeTools import find_tick
from zsim.sim_progress.Preload.APLModule.APLJudgeTools.FindCharacter import find_char
from .BaseSubConditionUnit import BaseSubConditionUnit
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
class StatusSubUnit(BaseSubConditionUnit):
def __init__(self, priority: int, sub_condition_dict: dict = None, mode=0):
super().__init__(priority=priority, sub_condition_dict=sub_condition_dict, mode=mode)
self.enemy = None
class CheckHandler:
@classmethod
def handler(cls, *args, **kwargs):
raise NotImplementedError
class StunHandler(CheckHandler):
@classmethod
def handler(cls, enemy):
return enemy.dynamic.stun
class QTETriggerableHandler(CheckHandler):
@classmethod
def handler(cls, enemy):
return enemy.qte_manager.qte_data.qte_triggerable_times
class QTETriggeredHandler(CheckHandler):
@classmethod
def handler(cls, enemy):
return enemy.qte_manager.qte_data.qte_triggered_times
class QTEActivationAvailableHandler(CheckHandler):
@classmethod
def handler(cls, enemy):
return enemy.qte_manager.qte_data.qte_activation_available
class AnomalyPctHandler(CheckHandler):
def __init__(self, anomaly_number):
self.anomaly_number = anomaly_number
def handler(self, enemy):
return enemy.anomaly_bars_dict[self.anomaly_number].get_buildup_pct()
class BuildupPctHandler(CheckHandler):
def __init__(self, element_type_1: int, element_type_2: int):
self.element_type_1 = element_type_1
self.element_type_2 = element_type_2
def handler(self, enemy):
result = (
enemy.anomaly_bars_dict[self.element_type_1].get_buildup_pct()
- enemy.anomaly_bars_dict[self.element_type_2].get_buildup_pct()
)
return result
class StunPctHandler(CheckHandler):
@classmethod
def handler(cls, enemy):
return enemy.get_stun_percentage()
class CharLastingNodeTagHandler(CheckHandler):
@classmethod
def handler(cls, char_cid, found_char_dict, game_state, sim_instance):
tick = find_tick(sim_instance=sim_instance)
char = find_char(found_char_dict, game_state, char_cid)
return char.dynamic.lasting_node.spamming_info(tick)[1]
class CharLastingNodeTickHandler(CheckHandler):
@classmethod
def handler(cls, char_cid, found_char_dict, game_state, sim_instance):
tick = find_tick(sim_instance=sim_instance)
char = find_char(found_char_dict, game_state, char_cid)
return char.dynamic.lasting_node.spamming_info(tick)[2]
class CharRepeatTimesHandler(CheckHandler):
@classmethod
def handler(cls, char_cid, found_char_dict, game_state, sim_instance):
tick = find_tick(sim_instance=sim_instance)
char = find_char(found_char_dict, game_state, char_cid)
return char.dynamic.lasting_node.spamming_info(tick)[3]
class CharOnFieldHandler(CheckHandler):
@classmethod
def handler(cls, char_cid, found_char_dict, game_state, sim_instance):
char = find_char(found_char_dict, game_state, char_cid)
result = char.dynamic.on_field
return result
class SingleQTEHandler(CheckHandler):
@classmethod
def handler(cls, enemy):
return enemy.qte_manager.qte_data.single_qte
class CharAvailableHandler(CheckHandler):
@classmethod
def handler(cls, char_cid, found_char_dict, game_state, sim_instance):
char = find_char(found_char_dict, game_state, char_cid)
return char.is_available(find_tick(sim_instance=sim_instance))
class QuickAssistHandler(CheckHandler):
@classmethod
def handler(cls, char_cid, found_char_dict, game_state, sim_instance):
char = find_char(found_char_dict, game_state, char_cid)
quick_assist_available = char.dynamic.quick_assist_manager.quick_assist_available
from zsim.sim_progress.Character.character import Character
from zsim.simulator.simulator_class import Simulator
assert isinstance(char, Character)
assert isinstance(sim_instance, Simulator)
tick = sim_instance.tick
if not char.is_available(tick=tick):
return False
# 这里需要进一步审查,如果当前角色快速支援亮起但是角色本身有动作,那么这里应该返回False,即“快速支援激活但不能被响应”
preload_data = sim_instance.preload.preload_data
char_node_stack = preload_data.personal_node_stack.get(char_cid, None)
if char_node_stack is None:
return quick_assist_available
else:
for nodes in char_node_stack.stack:
if not nodes.active_generation or nodes.is_additional_damage:
continue
if tick <= nodes.end_tick:
return False
return quick_assist_available
class WaitingAssistHandler(CheckHandler):
@classmethod
def handler(cls, char_cid, found_char_dict, game_state, sim_instance):
char = find_char(found_char_dict, game_state, char_cid)
return char.dynamic.quick_assist_manager.assist_waiting_for_anwser(
find_tick(sim_instance=sim_instance)
)
class ActiveAnomalyHandler(CheckHandler):
@classmethod
def handler(cls, enemy, *args, **kwargs):
return enemy.dynamic.is_under_anomaly()
class ShockHandler(CheckHandler):
@classmethod
def handler(cls, enemy):
return enemy.dynamic.shock
class BurnHandler(CheckHandler):
@classmethod
def handler(cls, enemy):
return enemy.dynamic.burn
class AssultHandler(CheckHandler):
@classmethod
def handler(cls, enemy):
return enemy.dynamic.assault
class FrostbiteHandler(CheckHandler):
@classmethod
def handler(cls, enemy):
return enemy.dynamic.frostbite
class FrostFrostbiteHandler(CheckHandler):
@classmethod
def handler(cls, enemy):
return enemy.dynamic.frost_frostbite
class CorruptionHandler(CheckHandler):
@classmethod
def handler(cls, enemy):
return enemy.dynamic.corruption
HANDLE_MAP = {
"stun": StunHandler,
"QTE_triggerable_times": QTETriggerableHandler, # 可连携次数
"QTE_triggered_times": QTETriggeredHandler, # 已连携次数
"anomaly_pct": AnomalyPctHandler,
"lasting_node_tag": CharLastingNodeTagHandler,
"lasting_node_tick": CharLastingNodeTickHandler,
"on_field": CharOnFieldHandler,
"QTE_activation_available": QTEActivationAvailableHandler, # 彩色失衡状态
"single_qte": SingleQTEHandler,
"repeat_times": CharRepeatTimesHandler,
"stun_pct": StunPctHandler,
"char_available": CharAvailableHandler,
"is_under_anomaly": ActiveAnomalyHandler,
"is_shock": ShockHandler,
"is_burn": BurnHandler,
"is_assault": AssultHandler,
"is_frostbite": FrostbiteHandler,
"is_frost_frostbite": FrostFrostbiteHandler,
"is_corruption": CorruptionHandler,
"quick_assist_available": QuickAssistHandler,
"assist_waiting_for_anwser": WaitingAssistHandler,
"buildup_pct_delta": BuildupPctHandler,
}
def check_myself(
self,
found_char_dict,
game_state,
sim_instance: "Simulator" = None,
*args,
**kwargs,
):
if self.check_target == "enemy":
if self.enemy is None:
self.enemy = game_state["schedule_data"].enemy
if "anomaly_pct" in self.check_stat:
anomaly_number = int(self.check_stat[-1])
handler = self.HANDLE_MAP["anomaly_pct"](anomaly_number)
elif "buildup_pct_delta" in self.check_stat:
stat_str = self.check_stat.strip().split("_")
anomaly_number_1 = int(stat_str[-1])
anomaly_number_2 = int(stat_str[-2])
handler = self.HANDLE_MAP["buildup_pct_delta"](anomaly_number_1, anomaly_number_2)
else:
handler_cls = self.HANDLE_MAP.get(self.check_stat)
handler = handler_cls() if handler_cls else None
if not handler:
raise ValueError(
f"当前检查的check_stat为:{self.check_stat},优先级为{self.priority},暂无处理该属性的逻辑模块!"
)
return self.spawn_result(handler.handler(self.enemy))
else:
"""既然check_target不是Enemy,那么一定是char的CID"""
handler_cls = self.HANDLE_MAP.get(self.check_stat)
handler = handler_cls() if handler_cls else None
if not handler:
raise ValueError(
f"当前检查的check_stat为:{self.check_stat},优先级为{self.priority},暂无处理该属性的逻辑模块!"
)
return self.spawn_result(
handler.handler(int(self.check_target), found_char_dict, game_state, sim_instance)
)
================================================
FILE: zsim/sim_progress/Preload/APLModule/SubConditionUnit/__init__.py
================================================
from .BaseSubConditionUnit import BaseSubConditionUnit # noqa: I001
from .ActionSubUnit import ActionSubUnit
from .AttributeSubUnit import AttributeSubUnit
from .BuffSubUnit import BuffSubUnit
from .SpecialSubUnit import SpecialSubUnit
from .StatusSubUnit import StatusSubUnit
__all__ = [
"BaseSubConditionUnit",
"StatusSubUnit",
"AttributeSubUnit",
"BuffSubUnit",
"ActionSubUnit",
"SpecialSubUnit",
]
================================================
FILE: zsim/sim_progress/Preload/APLModule/__init__.py
================================================
from .APLClass import APLClass
from .APLManager import APLManager
from .APLOperator import APLOperator
from .APLParser import APLParser
__all__ = [
"APLOperator",
"APLParser",
"APLClass",
"APLManager",
]
================================================
FILE: zsim/sim_progress/Preload/PreloadClass.py
================================================
from typing import TYPE_CHECKING, Iterable
from .PreloadDataClass import PreloadData
from .PreloadStrategy import SwapCancelStrategy
if TYPE_CHECKING:
from zsim.sim_progress.Character.skill_class import Skill
from zsim.simulator.dataclasses import LoadData
from zsim.simulator.simulator_class import Simulator
class PreloadClass:
def __init__(
self,
skills: Iterable["Skill"],
*,
load_data: "LoadData",
apl_path: str | None = None,
sim_instance: "Simulator | None" = None,
**kwargs,
):
self.preload_data: "PreloadData" = PreloadData(
skills, load_data=load_data, sim_instance=sim_instance
)
self.apl_path = apl_path
self.strategy = SwapCancelStrategy(self.preload_data, apl_path)
def do_preload(self, tick, enemy, name_box, char_data):
if self.preload_data.name_box is None:
self.preload_data.name_box = name_box
if self.preload_data.char_data is None:
self.preload_data.char_data = char_data
self.strategy.generate_actions(enemy, tick)
def reset_myself(self, namebox):
self.preload_data.reset_myself(namebox)
================================================
FILE: zsim/sim_progress/Preload/PreloadDataClass.py
================================================
from typing import TYPE_CHECKING, Iterable
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from .SkillsQueue import SkillNode
if TYPE_CHECKING:
from zsim.sim_progress.Character.skill_class import Skill
from zsim.sim_progress.data_struct import EnemyAttackEventManager, NodeStack, QuickAssistSystem
from zsim.sim_progress.Load.loading_mission import LoadingMission
from zsim.simulator.dataclasses import CharacterData, LoadData
from zsim.simulator.simulator_class import Simulator
class PreloadData:
"""循环于Preload阶段内部的数据"""
def __init__(
self,
skills: Iterable["Skill"],
sim_instance: "Simulator | None",
load_data: "LoadData",
**kwargs,
):
self.sim_instance: "Simulator | None" = sim_instance
self.preload_action: list[SkillNode] = [] # 最终return返回给外部申请的数据结构
self.skills: Iterable["Skill"] = (
skills # 用于创建SkillNode,是SkillNode构造函数的必要参数。
)
from zsim.sim_progress.data_struct import NodeStack
self.personal_node_stack: dict[
int, "NodeStack[SkillNode]"
] = {} # 个人的技能栈(包括主动生成的和被动生成的)
self.personal_active_generation_node_stack: dict[
int, "NodeStack[SkillNode]"
] = {} # 个人的主动生成的技能栈
self.current_node_stack: "NodeStack[SkillNode]" = NodeStack(
length=5
) # Preload阶段的总技能栈
self.latest_active_generation_node: SkillNode | None = (
None # 最近一次主动生成的skillnode,#TODO:可能是无用参数!
)
self.preload_action_list_before_confirm: list[
tuple[str, bool, int]
] = [] # 当前tick需要执行preload的SkillTag列表,列表中的元素是(skill_tag, active_generation),其中,active_generation指的是动作是否是主动生成。
self.name_box: list[str] | None = None
self.char_data: "CharacterData | None" = None
self.load_data: "LoadData" = load_data
self.load_mission_dict: dict[str, "LoadingMission"] = load_data.load_mission_dict
self.quick_assist_system: "QuickAssistSystem | None" = None
self.atk_manager: "EnemyAttackEventManager | None" = None
@property
def operating_now(self) -> int | None:
"""返回正在操作的角色"""
if self.latest_active_generation_node is None:
return None
_cid = int(self.latest_active_generation_node.skill_tag.split("_")[0])
return _cid
def push_node_in_swap_cancel(self, node: SkillNode, tick: int):
"""合轴模式中的内部数据更新函数。将构造好的SkillNode加入preload_action中,同时更新Preload板块的内部数据。"""
assert self.sim_instance is not None
self.check_myself_before_push_node()
self.preload_action.append(node)
char_cid = int(node.skill_tag.split("_")[0])
self.current_node_stack.push(node)
if char_cid not in self.personal_node_stack:
from zsim.sim_progress.data_struct import NodeStack
self.personal_node_stack[char_cid] = NodeStack(length=3)
self.personal_active_generation_node_stack[char_cid] = NodeStack(length=3)
if not self.personal_node_stack[char_cid].last_node_is_end(tick):
"""若检测到当前stack中的最新node还未结束,但是SwapCancel还是放行了,那么就说明可能发生了node的顶替,
此时应该排除是附加伤害的可能性,因为附加伤害是可以被swapcancel轻易放行的,但是并不具备打断的效果。"""
if not (
node.skill.labels is not None
and "additional_damage" in node.skill.labels # 技能拥有附加标签
):
self.force_change_action(node)
if self.personal_node_stack[char_cid].is_empty():
"""检测角色的第一个动作抛出。"""
self.sim_instance.listener_manager.broadcast_event(event=node, signal=LBS.ENTER_BATTLE)
self.personal_node_stack[char_cid].push(node)
if node.active_generation:
self.latest_active_generation_node = node
self.personal_active_generation_node_stack[char_cid].push(node)
if self.quick_assist_system is None:
assert self.char_data is not None
from zsim.sim_progress.data_struct import QuickAssistSystem
self.quick_assist_system = QuickAssistSystem(
self.char_data.char_obj_list, sim_instance=self.sim_instance
)
self.quick_assist_system.update(tick, node, self.load_data.all_name_order_box)
if self.atk_manager is not None and self.atk_manager.attacking:
self.atk_manager.answered_action.append(node)
from zsim.sim_progress.Preload.APLModule.ActionReplaceManager import ActionReplaceManager
action_replace_manager: "ActionReplaceManager" = (
self.sim_instance.preload.strategy.apl_engine.apl.action_replace_manager
)
if action_replace_manager is not None:
action_replace_manager.parry_aid_strategy.update_myself(skill_node=node, tick=tick)
def check_myself_before_push_node(self):
"""Confirm阶段自检"""
_active_generation_node_list = []
for _node in self.preload_action:
if _node.active_generation:
_active_generation_node_list.append(_node)
if len(_active_generation_node_list) > 1:
raise ValueError(
f"在一个Tick中检测到了多个主动技能!共有:{_active_generation_node_list}"
)
def get_on_field_node(self, tick: int) -> SkillNode | None:
"""获取当前的前台技能"""
return self.current_node_stack.get_on_field_node(tick)
def chek_myself_before_start_preload(self, enemy, tick):
"""Preload阶段自检"""
if self.preload_action:
print(f"尚未被Load阶段处理的技能:{self.preload_action}")
enemy.stun_judge(tick)
def external_add_skill(self, skill_tuple: tuple[str, bool, int]):
"""外部Buff向下一个Tick添加技能的接口,通常用于协同攻击"""
skill_tag, active_generation, apl_priority = skill_tuple
self.preload_action_list_before_confirm.append(skill_tuple)
def reset_myself(self, name_box):
"""重置preload_data"""
self.preload_action = [] # 最终return返回给外部申请的数据结构
for cid, stack in self.personal_node_stack.items():
stack.reset()
self.current_node_stack.reset()
self.latest_active_generation_node = None
self.preload_action_list_before_confirm = []
self.name_box = name_box
def force_change_action(self, skill_node: SkillNode):
"""强制更新动作,用于技能强制顶替、被打断或是类似场合"""
char_cid = int(skill_node.skill_tag.strip().split("_")[0])
node_be_changed: "SkillNode | None" = self.personal_node_stack[char_cid].peek()
if node_be_changed is None:
raise ValueError
if node_be_changed.end_tick <= skill_node.preload_tick:
raise ValueError(
f"尝试用{skill_node.skill_tag}来强制替换{node_be_changed.skill_tag},但是后者已经于{node_be_changed.end_tick}结束,这种情况不用调用强制替换方法。请检查调用逻辑。"
)
self.delete_mission_in_preload_data(node_be_changed)
if node_be_changed.skill.do_immediately and "dodge" not in node_be_changed.skill_tag:
raise ValueError(
f"{skill_node.skill_tag}正在尝试顶替一个最高优先级的技能:{node_be_changed.skill_tag}"
)
def delete_mission_in_preload_data(self, node_be_changed: "SkillNode"):
"""在PreloadData中强制干涉Load阶段,并且执行特定任务的删除。"""
mission_key_to_remove: list[str] = []
for mission_key, mission in self.load_mission_dict.items():
from zsim.sim_progress.Load import LoadingMission
if not isinstance(mission, LoadingMission):
continue
if mission.mission_tag == node_be_changed.skill_tag:
mission.mission_end()
mission_key_to_remove.append(mission_key)
for key in mission_key_to_remove:
self.load_mission_dict.pop(key)
def char_occupied_check(self, char_cid: int, tick: int):
"""检查角色当前是否存在动作(无论主动、被动)"""
char_stack = self.personal_node_stack.get(char_cid, None)
if char_stack is None:
return True
latest_node = char_stack.get_effective_node()
if latest_node is None:
return True
if latest_node.end_tick > tick:
return True
return False
================================================
FILE: zsim/sim_progress/Preload/PreloadEngine/APLEngine.py
================================================
from typing import TYPE_CHECKING
from zsim.define import APL_PATH, APL_THOUGHT_CHECK
from zsim.define import APL_THOUGHT_CHECK_WINDOW as ATCW
from ..APLModule import APLManager
from ..SkillsQueue import SkillNode, spawn_node
from .BasePreloadEngine import BasePreloadEngine
if TYPE_CHECKING:
from ..PreloadDataClass import PreloadData
class APLEngine(BasePreloadEngine):
"""用于调动APL模块的Preload引擎"""
def __init__(self, data: "PreloadData", apl_path: str | None = None):
super().__init__(data)
self.preload_data = data
self.sim_instance = self.preload_data.sim_instance
if self.sim_instance is None:
raise ValueError("APLEngine requires a sim_instance to be provided.")
self.apl_manager = APLManager(sim_instance=self.sim_instance)
if apl_path is None:
apl_path = APL_PATH
elif not apl_path.endswith(".txt") or not apl_path.endswith(".toml"):
# 如果提供的是APL名称而不是完整路径
found_path = self.apl_manager.get_apl_path(apl_path)
if found_path:
apl_path = found_path
self.apl = self.apl_manager.load_apl(apl_path, mode=0, preload_data=self.preload_data)
self.latest_node: SkillNode | None = None
self._apl_want: tuple | None = None # APL引擎的想法
@property
def apl_want(self) -> tuple | None:
return self._apl_want
@apl_want.setter
def apl_want(self, value: tuple | None) -> None:
skill_tag, apl_priority, apl_unit = value if value else (None, None, None)
if APL_THOUGHT_CHECK:
tick = self.sim_instance.tick
if tick in range(ATCW[0], ATCW[1]):
if value != self.apl_want:
print(
f"{tick}tick:APL引擎的想法变化,{self.apl_want[0] if self.apl_want else None} → {skill_tag},来自于优先级 {apl_priority} 的单元,详细内容:{apl_unit.whole_line}"
) if self.apl_want is not None else print(
f"{tick}tick:APL引擎产生了第一个想法:{skill_tag}"
)
self._apl_want = value
def run_myself(self, tick) -> SkillNode | None:
"""APL模块运行的最终结果:技能名、最终通过的APL代码优先级"""
skill_tag, apl_priority, apl_unit = self.apl.execute(tick, mode=0)
self.apl_want = (skill_tag, apl_priority, apl_unit)
if skill_tag == "wait":
return None
node = spawn_node(
skill_tag,
tick,
self.data.skills,
active_generation=True,
apl_priority=apl_priority,
apl_unit=apl_unit,
)
return node
def reset_myself(self):
"""APL模块暂时没有任何需要Reset的地方!"""
self.latest_node = None
def get_available_apls(self) -> list[str]:
"""获取所有可用的APL文件列表"""
return self.apl_manager.list_available_apls()
================================================
FILE: zsim/sim_progress/Preload/PreloadEngine/AttackAnswerEngine.py
================================================
from typing import TYPE_CHECKING
from .BasePreloadEngine import BasePreloadEngine
if TYPE_CHECKING:
from zsim.sim_progress.Character import Character
from zsim.sim_progress.Enemy import Enemy
from zsim.sim_progress.Enemy.EnemyAttack.EnemyAttackClass import EnemyAttackAction
from zsim.simulator.simulator_class import Simulator
from ..PreloadDataClass import PreloadData
class AttackResponseEngine(BasePreloadEngine):
"""进攻响应引擎,主要负责敌人进攻动作抛出,以及角色动作响应相关的内容;"""
def __init__(self, data: "PreloadData", sim_instance: "Simulator | None" = None):
assert sim_instance is not None
super().__init__(data)
self.data: "PreloadData" = data
self.game_state = None
self.found_char_dict: dict[int, "Character"] = {}
self.enemy: "Enemy | None" = None
self.sim_instance: "Simulator" = sim_instance
def run_myself(self, tick: int, *args, **kwargs) -> bool:
if self.data.atk_manager is None:
from zsim.sim_progress.data_struct import EnemyAttackEventManager
self.data.atk_manager = EnemyAttackEventManager(
enemy_instance=self.sim_instance.schedule_data.enemy
)
self.data.atk_manager.end_check(tick=tick)
enemy_attack_action: "EnemyAttackAction | None" = self.try_spawn_enemy_attack()
if enemy_attack_action is not None:
# 将进攻信号发送给PreloadData。
self.data.atk_manager.event_start(
action=enemy_attack_action, start_tick=self.sim_instance.tick
)
"""每次运行,都要让atk_manager自检一次,以更新状态。"""
self.data.atk_manager.check_myself(tick=tick)
return True
def try_spawn_enemy_attack(self) -> "EnemyAttackAction | None":
"""调用Enemy对象下的进攻模组,并且生成一次攻击,同时打包成事件存入本地"""
if self.sim_instance is None and self.data.sim_instance is not None:
self.sim_instance = self.data.sim_instance
if self.enemy is None:
self.enemy = self.sim_instance.schedule_data.enemy
if (
not self.enemy.attack_method.active
or self.enemy.dynamic.stun
or (
self.data.atk_manager is not None
and self.data.atk_manager.interruption_recovery_check(tick=self.sim_instance.tick)
)
):
return None
if self.enemy.attack_method.random_attack:
enemy_attack_action = self.enemy.attack_method.probablity_driven_action_selection(
current_tick=self.sim_instance.tick
)
else:
enemy_attack_action = self.enemy.attack_method.time_anchored_action_selection(
current_tick=self.sim_instance.tick
)
return enemy_attack_action
================================================
FILE: zsim/sim_progress/Preload/PreloadEngine/BasePreloadEngine.py
================================================
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from .. import PreloadData
class BasePreloadEngine(ABC):
@abstractmethod
def __init__(self, data: "PreloadData"):
self.data = data
self.active_signal = False # 用于记录当前引擎在当前tick是否运行过。
@abstractmethod
def run_myself(self, *args, **kwargs) -> Any:
return False
__all__ = ["BasePreloadEngine"]
================================================
FILE: zsim/sim_progress/Preload/PreloadEngine/ConfirmEngine.py
================================================
from typing import TYPE_CHECKING
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from zsim.sim_progress.Report import report_to_log
from ..PreloadEngine import BasePreloadEngine
from ..SkillsQueue import SkillNode, spawn_node
if TYPE_CHECKING:
from zsim.sim_progress.Character import Character
from zsim.sim_progress.Preload.PreloadDataClass import PreloadData
class ConfirmEngine(BasePreloadEngine):
def __init__(self, data: "PreloadData"):
"""
这个引擎的主要功能有:
1、将各环节产生的需要进行Preload的skill_tag,构造成SkillNode,
2、可行性验证
3、内部数据交互、更新
4、外部数据交互、更新
"""
super().__init__(data)
self.external_update_signal = False
self.external_add_skill_list = []
self.validators = [self._validate_timing]
self.name_box_first_change = True # 首次更改name_box的标志
def run_myself(self, tick: int, **kwargs) -> bool:
"""依次执行 Node构造、验证、内外部数据交互"""
apl_skill_node: SkillNode | None = kwargs.get("apl_skill_node", None)
apl_skill_tag = kwargs.get("apl_skill_tag", None)
if apl_skill_node is None and apl_skill_tag != "wait":
raise ValueError("ConfirmEngine 并未获取到 APL Skill Node,请检查输入")
for i in range(len(self.data.preload_action_list_before_confirm)):
tuples = self.data.preload_action_list_before_confirm.pop()
# 1、创建node
node = self.spawn_node_from_tag(tick, tuples, template_node_from_apl=apl_skill_node)
# 2、可行性验证
if self.validate_node_execution(node, tick):
# 3、内部数据交互
self.data.push_node_in_swap_cancel(node, tick)
report_to_log(f"[PRELOAD]:In tick: {tick}, {node.skill_tag} has been preloaded")
# 4、外部数据交互
self.update_external_data(node, tick)
# print(f'{node.skill_tag}通过了可行性验证,该主动动作来自于优先级为{node.apl_priority}的APL代码')
# if any(
# [_subtags in node.skill_tag for _subtags in ["knock_back", "parry"]]
# ):
# print(
# f"{node.skill_tag}被ConfirmEngine接收,它将从{node.preload_tick}开始,于{node.end_tick}结束。"
# )
else:
pass
return True
def spawn_node_from_tag(
self,
tick: int,
tuples: tuple[str, bool, int],
template_node_from_apl: SkillNode | None = None,
):
"""通过skill_tag构造Node"""
skill_tag = tuples[0]
active_generation = tuples[1] if tuples[1] else False
if template_node_from_apl and skill_tag == template_node_from_apl.skill_tag:
apl_unit = template_node_from_apl.apl_unit
else:
apl_unit = None
node = spawn_node(
skill_tag,
tick,
self.data.skills,
active_generation=active_generation,
apl_priority=tuples[2],
apl_unit=apl_unit,
)
return node
def update_external_data(self, node: SkillNode, tick: int):
"""与外部数据交互,主要是和char进行交互。"""
if self.data.char_data:
for char in self.data.char_data.char_obj_list:
char.update_sp_and_decibel(node)
char.special_resources(node, tick=tick)
char.dynamic.lasting_node.update_node(node, tick)
# 切人逻辑
name_box = self.data.name_box
if (
isinstance(name_box, list)
and all(isinstance(name, str) for name in name_box)
and node.active_generation
and self.data.char_data is not None
):
self.switch_char(node, self.data.char_data)
if self.data.sim_instance:
self.data.sim_instance.decibel_manager.update(skill_node=node)
def switch_char(self, this_node: SkillNode, char_data) -> None:
name_box = self.data.name_box
if name_box is None:
return
old_name_box = name_box.copy()
name_index = name_box.index(this_node.char_name)
# 更改前台角色(切人逻辑)
if name_index == 1:
name_switch = name_box.pop(0)
name_box.append(name_switch)
elif name_index == 2:
name_switch = name_box.pop(0)
name_box.append(name_switch)
name_switch = name_box.pop(0)
name_box.append(name_switch)
for char in char_data.char_obj_list:
char: "Character"
if name_box[0] == char.NAME:
if name_box[0] != old_name_box[0]:
"""在更新name_box的时候,将切人事件对所有监听器进行广播。"""
if self.data.sim_instance:
self.data.sim_instance.listener_manager.broadcast_event(
event=char, signal=LBS.SWITCHING_IN, skill_node=this_node
)
char.dynamic.on_field = True
else:
# 首次切人逻辑
if self.name_box_first_change:
if self.data.sim_instance:
self.data.sim_instance.listener_manager.broadcast_event(
event=char, signal=LBS.SWITCHING_IN, skill_node=this_node
)
char.dynamic.on_field = True
self.name_box_first_change = False
else:
char.dynamic.on_field = False
def validate_node_execution(self, node: SkillNode, tick: int) -> bool:
"""集中验证节点可执行性"""
# watchdog.watch_reverse_order(node, self.data.personal_node_stack.peek())
result = all(validator(node, tick) for validator in self.validators)
return result
@staticmethod
def _validate_timing(node: SkillNode, tick: int) -> bool:
"""检验preload_tick的封装是否有问题,"""
results = node.preload_tick <= tick
if not results:
print(
f"Preload Tick的可行性验证未通过!应在{node.preload_tick}tick preload的{node.skill_tag}技能过早的在confirm引擎中出现!"
)
return node.preload_tick <= tick
================================================
FILE: zsim/sim_progress/Preload/PreloadEngine/ForceAddEngine.py
================================================
from typing import TYPE_CHECKING
from ..APLModule.APLJudgeTools import get_game_state
from ..PreloadEngine import BasePreloadEngine
from ..SkillsQueue import SkillNode
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
class ForceAddEngine(BasePreloadEngine):
"""该引擎的主要作用是:在技能结束时,检索它们的后置技能,并且执行添加"""
def __init__(self, data):
super().__init__(data)
self.game_state = None
self.found_char_dict = {}
def run_myself(self, tick: int) -> bool:
"""当每个node结束时,都应该调用这个函数来判断强制添加。"""
self.active_signal = False
if not self.data.personal_node_stack:
return True
for _, stack in self.data.personal_node_stack.items():
node: SkillNode | None
node = stack.peek()
if node is None:
continue
if node.end_tick > tick:
"""如果当前node并未结束,则不符合“node”结束的条件,则应该直接跳过。"""
continue
follow_up: list[str] = node.skill.follow_up
if not follow_up:
continue
conditions_unit: list = node.skill.force_add_condition_APL
should_force_add, index = self.prcoess_force_add_apl(
conditions_unit,
skill_tag=node.skill_tag,
tick=tick,
sim_instance=self.data.sim_instance,
)
if should_force_add:
# print(f'强制添加判定通过!该强制添加来自于{node.skill_tag},将要添加:{follow_up[index]}')
self.check_char(follow_up, index, node) # 检验数据的正确性
self.data.preload_action_list_before_confirm.append((follow_up[index], False, 0))
self.active_signal = True
return True
def check_char(self, follow_up: list, index: int, node: SkillNode):
"""
该函数的作用:确保:B角色技能在强制预载时,并没有动作存在即可。
如果程序流程合理,这个函数是不会被执行的。
"""
follow_up_skill_CID = int(follow_up[index][:4])
follow_up_skill_add_tick = node.end_tick
if self.data.personal_node_stack is None:
return
followed_char_stack = self.data.personal_node_stack.get(follow_up_skill_CID, None)
if followed_char_stack is not None:
latest_node: SkillNode | None = followed_char_stack.peek()
if latest_node is not None and node.end_tick < latest_node.end_tick:
raise ValueError(
f"出现了不应该出现的情况!技能{follow_up[index]}理应在{node.skill_tag}之后、于{follow_up_skill_add_tick}执行,但是此时角色{follow_up_skill_CID}尚有动作存在。"
)
def prcoess_force_add_apl(
self, conditions_unit, sim_instance: "Simulator | None", **kwargs
) -> tuple[bool, int]:
"""强制添加动作的前置判定,有APL模块则运行模块,无APL模块则直接通过。"""
should_force_add = True
index = 0
tick = kwargs.get("tick", None)
if conditions_unit and self.game_state is None:
if sim_instance:
self.game_state = get_game_state(sim_instance=sim_instance)
if conditions_unit:
"""存在条件类APL判定"""
for unit in conditions_unit:
_apl_result, result_box = unit.check_all_sub_units(
self.found_char_dict,
self.game_state,
tick=tick,
sim_instance=sim_instance,
)
if not _apl_result:
"""当apl单元的自运行结果为False时,意味着该条APL的条件中存在着不满足的项,所以应该continue,赶紧去检查下个Box"""
should_force_add = False
index += 1
else:
should_force_add = True
return should_force_add, index
else:
"""
for循环执行完毕后,还是没有return,那么就意味着,当前所有的APL单元的自运行都没通过,
也就是说当前该动作组中的所有衔接动作都不满足自动衔接的条件,
此时,返回False, index(这里的index其实已经无所谓了)
"""
return should_force_add, index
else:
return should_force_add, index
================================================
FILE: zsim/sim_progress/Preload/PreloadEngine/SwapCancelValidateEngine.py
================================================
import math
from zsim.define import (
SWAP_CANCEL_DEBUG_TARGET_SKILL,
SWAP_CANCEL_MODE_DEBUG,
)
from zsim.define import (
SWAP_CANCEL_MODE_COMPLETION_COEFFICIENT as SCK,
)
from zsim.define import (
SWAP_CANCEL_MODE_LAG_TIME as SCLT,
)
from ..SkillsQueue import SkillNode
from .BasePreloadEngine import BasePreloadEngine
# EXPLAIN:关于SCK和LT的作用:
"""
以上两个系数分别是:
①合轴操作完成度系数 SWAP_CANCEL_MODE_COMPLETION_COEFFICIENT (程序中通常引用为SCK)
②操作滞后系数 SWAP_CANCEL_MODE_LAG_TIME (程序中通常引用为SCLT),它们共同用于模拟玩家的合轴操作。
因为不可能任意操作都具有完美的完成度(在第2帧就完美切人+下一招出手),
人体机能限制、注意力不集中、可能存在的操作习惯以及其他因素,都会导致合轴操作的延后实施,
所以,这里通过设置一个系数来模拟玩家的操作滞后程度,在计算时,我会取用skill_node的时长(skill.ticks),并且乘以SCK,
所计算出的结果与SCLT参数相比较,取较小值作为最终的滞后时间(防止较长的技能滞后严重,导致模拟失真)。
后续的升级方向:
在引入随机数生成器后,可以进一步基于两个参数的基本值,对这两个参数进行随机处理,从而真正模拟玩家在操作端的浮动。
"""
class SwapCancelValidateEngine(BasePreloadEngine):
"""该引擎的作用是:判断当前传入的APL运行结果是否满足合轴的需求"""
def __init__(self, data):
super().__init__(data)
self.validators = [
self._validate_char_avaliable,
self._validate_char_task_conflict,
self._validate_swap_tick,
self._validate_qte_activation,
self._validate_wait_event,
self._validate_swap_state_check,
self._validate_swap_strategy_check,
]
self.__report_tag = None
@property
def external_update_signal(self):
return True if self.data.preload_action_list_before_confirm else False
def run_myself(
self,
skill_tag: str,
tick: int,
apl_priority: int = 0,
apl_skill_node: SkillNode | None = None,
**kwargs,
) -> bool:
"""合轴可行性分析基本分为以下几个步骤:
1、当前涉及角色是否有空
2、合轴时间是否符合
3、确认合轴后,将skill_tag和主动参数 打包成tuple"""
self.active_signal = False
"""若当前APL动作为等待,那么直接返回False,不做任何操作。"""
if self._validate_wait_event(apl_skill_tag=skill_tag):
self._swap_cancel_debug_print(mode=0, skill_tag=skill_tag)
return False
"""检测对应角色是否有空——当前tick是否存在未完成动作"""
if not self._validate_char_avaliable(
skill_tag=skill_tag, tick=tick, apl_skill_node=apl_skill_node
):
self._swap_cancel_debug_print(mode=1, skill_tag=skill_tag)
return False
"""检测当前tick的APL输出是否与角色自身的任务冲突——动作的顶替判定"""
if not self._validate_char_task_conflict(
skill_tag=skill_tag, apl_skill_node=apl_skill_node, tick=tick
):
self._swap_cancel_debug_print(mode=2, skill_tag=skill_tag)
return False
"""QTE状态过滤器——QTE阶段不支持任何合轴"""
if self._validate_qte_activation(tick=tick, skill_node=apl_skill_node):
return False
"""检测当前tick的角色状态是否支持合轴——切人CD检测、高优先级动作判定"""
if not self._validate_swap_state_check(
tick=tick, skill_tag=skill_tag, apl_skill_node=apl_skill_node
):
self._swap_cancel_debug_print(mode=3, skill_tag=skill_tag)
return False
"""检测当前的tick是否满足合轴操作的需求"""
if not self._validate_swap_tick(skill_tag=skill_tag, tick=tick):
self._swap_cancel_debug_print(mode=4, skill_tag=skill_tag)
return False
"""检测当前的前台动作是否允许进行合轴——合轴策略过滤"""
if not self._validate_swap_strategy_check(tick=tick, skill_tag=skill_tag):
return False
self.data.preload_action_list_before_confirm.append((skill_tag, True, apl_priority))
self.active_signal = True
return True
def _validate_char_avaliable(
self, skill_tag: str, apl_skill_node: SkillNode | None, tick: int
) -> bool:
"""角色是否可以获取的判定"""
cid = int(skill_tag.split("_")[0])
char_stack = self.data.personal_node_stack.get(cid, None)
if char_stack is None:
"""角色的动作栈都尚未创建,说明角色当前没有任何动作,角色有空。"""
return True
"""获取上一个非附加伤害的技能Node"""
"""
在v0.3.4的开发过程中,我发现在一些极端情况下,当角色的personal_node_stack被数量较多的附加伤害技能填充时,
会发生一个无法有效读取角色当前正在进行的技能的问题,
这个问题将直接导致 合轴条件检测机制 会认为 “当前角色有空”而直接放行,从而导致“自己合轴自己”的情况发生。
比如爱丽丝6画会在三蓄发动时生效,队友的一些Hit会高频触发这个6画的附加伤害,
大量的"1401_Cinema_6"的skill_node涌入personal_node_stack,并且迅速超过上限,于是本该正在进行的技能"1401_SNA_3"因溢出而被pop移除了。
这样一来,get_effective_node()发现当前node_stack中全部都是附加伤害技能,就直接返回None,导致函数直接放行,
虽然整个SwapCancelValidateEngine内置多个校验器,但是关于角色是否“有空”的校验只有这一个,
这导致一旦本函数放行,后续的校验器将完全默认“角色有空”,最终导致某些场景下的错误判断。
更新后,我在get_effective_node()的返回结果为None的情况下,进一步检查角色的dynamic.lasting_node.node是否为None。
如果dynamic.lasting_node.node也为None,那么说明角色当前确实没有任何动作,角色才是真正“有空的”。
"""
try_get_char_latest_node = char_stack.get_effective_node()
if try_get_char_latest_node is None:
from zsim.sim_progress.Character.character import Character
char = self.data.char_data.find_char_obj(CID=cid)
assert isinstance(char, Character)
if char.dynamic.lasting_node.node is not None:
char_latest_node = char.dynamic.lasting_node.node
else:
char_latest_node = None
else:
char_latest_node = try_get_char_latest_node
# char_latest_active_node = char_stack.get_on_field_node(tick)
# if char_latest_active_node is not None:
# print(char_latest_node.skill_tag, char_latest_active_node.skill_tag)
if char_latest_node is None:
"""角色栈已经创建但是上一个动作为空,说明本动作是角色的第一个动作,角色有空。"""
return True
# print([_stacknode.skill_tag for _stacknode in char_stack.stack])
# print(f'APL:{apl_skill_node.skill_tag, apl_skill_node.apl_priority}, 上个技能:{char_latest_node.skill_tag, char_latest_node.apl_priority, char_latest_node.end_tick}')
"""角色当前有一个正在发生的Node"""
if char_latest_node.end_tick > tick:
"""如果该node是闪避,则直接放行——闪避是可以被自己的技能合轴、顶替的。"""
if "dodge" in char_latest_node.skill_tag:
# print(
# f"{apl_skill_node.char_name}的技能{apl_skill_node.skill_tag}企图取消自己的闪避技能!"
# ) if SWAP_CANCEL_MODE_DEBUG else None
return True
elif "parry" in char_latest_node.skill_tag and "knock_back_cause_parry" in skill_tag:
"""对于衔接于招架之后的击退,要立即放行"""
return True
"""正在进行的技能并非立即执行类型,而新的技能是立即执行类型,则放行"""
if (
apl_skill_node is not None
and apl_skill_node.skill.do_immediately
and not char_latest_node.skill.do_immediately
):
return True
else:
return False
else:
"""角色上一个动作已经结束,说明角色有空。"""
return True
@staticmethod
def spawn_lag_time(node: SkillNode) -> int:
"""
生成滞后时间,关于函数中两个参数SCK和SCLT的含义,请参考本文件开头的注释。
这里返回的lag_time是经过向上取整的。
"""
lag_time = math.ceil(min(node.skill.ticks * SCK, SCLT))
return lag_time
def _validate_swap_tick(self, skill_tag: str, tick: int, **kwargs):
"""针对当前技能的合轴时间的检测"""
current_node_on_field = self.data.get_on_field_node(tick)
if current_node_on_field is None:
return True
# 放行所有的附加伤害——附加伤害通常都没有动作,所以无需合轴
if (
current_node_on_field.skill.labels is not None
and "additional_damage" in current_node_on_field.skill.labels
):
return True
# 放行特别豁免清单中的技能,比如被击退等特殊动作;
if any([_sub_tag in skill_tag for _sub_tag in ["knock_back"]]):
return True
swap_lag_tick = self.spawn_lag_time(current_node_on_field)
if (
swap_lag_tick
+ current_node_on_field.skill.swap_cancel_ticks
+ current_node_on_field.preload_tick
> tick
):
return False
else:
if SWAP_CANCEL_MODE_DEBUG and SWAP_CANCEL_DEBUG_TARGET_SKILL:
if SWAP_CANCEL_DEBUG_TARGET_SKILL == skill_tag:
print(
f"监听的技能{skill_tag}满足合轴时间要求!合轴放行!上一个技能{current_node_on_field.skill_tag}因本次合轴而提前结束。"
f"本次合轴延迟时间为{swap_lag_tick + current_node_on_field.skill.swap_cancel_ticks}ticks,被合轴技能时间为{current_node_on_field.skill.ticks}ticks。"
)
return True
def _validate_qte_activation(self, tick: int, skill_node: SkillNode | None) -> bool:
"""针对当前技能的QTE是否处于激活状态的检测,当检查到有角色正在释放QTE时,返回True"""
# enemy = self.data.sim_instance.schedule_data.enemy
# if enemy.qte_manager.qte_data.single_qte is not None:
# if skill_node.skill.trigger_buff_level != 5:
# return True
for _cid, stack in self.data.personal_node_stack.items():
if stack.peek() is None:
continue
node_now = stack.peek()
if node_now is not None and node_now.end_tick > tick:
if "QTE" in node_now.skill_tag:
# FIXME: 由于伊芙琳的QTE是可以进行合轴的,这里一定会遇到Bug。
return True
continue
else:
return False
def _validate_wait_event(self, apl_skill_tag: str | None = None) -> bool:
"""用于检测传入的apl动作是否为wait。"""
if apl_skill_tag == "wait":
return True
else:
return False
def _validate_char_task_conflict(
self, skill_tag: str, apl_skill_node: SkillNode | None, tick: int
) -> bool:
"""
针对角色自身的任务冲突的检测——尽管角色当前tick有空
但并不意味着apl抛出的动作就可以直接执行。
APL抛出的动作还需要和角色自身的任务进行冲突检测,相互竞争和覆盖。
"""
if apl_skill_node is None:
return True
cid = int(skill_tag.split("_")[0])
for _tuples in self.data.preload_action_list_before_confirm:
_tuples: tuple[str, bool, int]
"""
preload_data中,preload_action_list_before_confirm是一个列表,
其中记录了当前tick要被抛出的动作,其中,每个元素是一个元组,
元组的第一个元素是技能的tag,第二个元素是技能的主动类型,第三个元素是APL的优先级。
对于当前函数来说,APL抛出的动作apl_skill_node尚未进入preload_action_list_before_confirm列表,
此时该列表中的所有技能都来自于ForceAddEngine强行添加。
"""
_tag = _tuples[0]
if cid == int(_tag.split("_")[0]):
"""如果角色在当前tick有forceadd的任务,并且APL抛出的动作并非do_immediately,则返回False"""
if not apl_skill_node.skill.do_immediately:
return False
for obj in self.data.skills:
if not obj.CID == cid:
continue
if obj.get_skill_info(skill_tag=_tag, attr_info="do_immediately"):
"""如果当前tick被force_add添加的skill_tag本来就是do_immediately类型,那么就没法抢队了"""
return False
else:
"""附加伤害additional_damage(类似于“白雷”)由于不需要占用角色,所以可以免于被挤掉的命运"""
skill_info = obj.get_skill_info(skill_tag=_tag, attr_info="labels")
if skill_info is None:
skill_info = {}
if (
not isinstance(skill_info, dict)
or "additional_damage" not in skill_info
):
"""但若当前tick被force_add 添加的skill_tag只是个普通技能,那么就要执行顶替。"""
(
print(f"即将添加的衔接技能:{_tuples}被{skill_tag}顶替!")
if SWAP_CANCEL_MODE_DEBUG
else None
)
self.data.preload_action_list_before_confirm.remove(_tuples)
return True
break
else:
raise ValueError(f"没找到{cid}对应的角色!")
else:
continue
else:
return True
def _validate_swap_state_check(
self, tick: int, skill_tag: str, apl_skill_node: SkillNode | None
):
"""检查角色当前的状态是否允许当前技能进行合轴"""
cid = int(skill_tag.split("_")[0])
node_on_field: SkillNode | None = self.data.get_on_field_node(tick)
char_node_stack = self.data.personal_node_stack.get(cid, None)
char_latest_node: SkillNode | None = char_node_stack.peek() if char_node_stack else None
char_change_cd: bool
last_actively_generated_node: SkillNode | None = self.data.latest_active_generation_node
if node_on_field and not node_on_field.active_generation:
"""
由于get_on_field_node函数只会尽量返回台前的主动技能,
而当场上仅存在一个被动技能时,该技能也会被函数获取并且返回。
这里需要检测返回结果的主动生成状态,若为False,则说明当前前台技能是被动技能,
此时,node_on_field等同于None
"""
node_on_field = None
if char_latest_node is None:
char_change_cd = True
else:
tick_delta = tick - char_latest_node.end_tick
char_change_cd = tick_delta >= 60
"""当前台存在一个高优先级技能时,合轴操作都是不可用的"""
if (
node_on_field is not None
and node_on_field.skill.do_immediately
and "dodge" not in node_on_field.skill_tag
):
return False
"""第一个主动动作时,直接放行"""
if last_actively_generated_node is None:
return True
"""当前台不存在技能或是前台技能并非高优先级时,那么可以进行合轴的进一步判定"""
if str(cid) not in last_actively_generated_node.skill_tag:
"""当动作涉及切人时,需要进行切人CD的检测"""
if char_change_cd:
"""当前角色的切人CD已经冷却完毕,则直接放行。"""
return True
else:
if apl_skill_node is None:
return False
if (
any([_sub_tag in skill_tag for _sub_tag in ["QTE", "Aid", "knock_back"]])
or apl_skill_node.skill.do_immediately
):
"""如果是支援类和连携技这种无视切人CD的技能,那么此时角色可以切出"""
return True
else:
return False
else:
"""当动作不涉及切人时,直接放行"""
return True
def _validate_swap_strategy_check(self, tick: int, skill_tag: str):
"""该函数用于检测当前技能的合轴策略是否允许合轴——在场的主动动作是否允许合轴。"""
cid = skill_tag.split("_")[0]
last_actively_generated_node: SkillNode | None = self.data.latest_active_generation_node
if last_actively_generated_node is None or last_actively_generated_node.end_tick < tick:
return True
if cid in last_actively_generated_node.skill_tag:
return True
else:
if last_actively_generated_node.apl_unit is None:
raise ValueError(
f"{last_actively_generated_node.skill_tag}作为主动动作但是却没有APLUnit!"
)
if last_actively_generated_node.apl_unit.apl_unit_type == "action.no_swap_cancel+=":
"""
当前台技能的apl_unit非空(意味着前台技能来自于APL模块),
并且apl_unit的种类为“action.no_swap_cancel+=”,即合轴禁止类型,则不可切人。
"""
self._swap_cancel_debug_print(
mode=5,
skill_tag=skill_tag,
last_actively_generated_node=last_actively_generated_node,
)
return False
return True
def check_myself(self):
if self.data.preload_action_list_before_confirm:
self.active_signal = True
def _swap_cancel_debug_print(
self,
mode: int,
skill_tag: str,
last_actively_generated_node: SkillNode | None = None,
):
if not SWAP_CANCEL_MODE_DEBUG:
return
"""由于APL的SwapCancelEngine基本会看每个tick都调用,所以这里需要避免重复播报。"""
if self.__report_tag == skill_tag:
return
self.__report_tag = skill_tag
skill_compare = True if SWAP_CANCEL_DEBUG_TARGET_SKILL else False
if mode == 0:
print("APL返回的结果是wait!")
elif mode == 1:
(
print(f"{skill_tag}所涉及角色当前没空!")
if not skill_compare
else (
print(f"{skill_tag}所涉及角色当前没空!")
if skill_tag == SWAP_CANCEL_DEBUG_TARGET_SKILL
else None
)
)
elif mode == 2:
(
print(f"{skill_tag}所涉及角色当前tick存在任务冲突,合轴失败!")
if not skill_compare
else (
print(
f"{skill_tag}所涉所涉及角色当前tick存在任务冲突,合轴失败!及角色当前没空!"
)
if skill_tag == SWAP_CANCEL_DEBUG_TARGET_SKILL
else None
)
)
elif mode == 3:
(
print(f"{skill_tag}所涉及角色切人CD未就绪 或是 技能优先级低于前台技能,合轴失败!")
if not skill_compare
else (
print(
f"{skill_tag}所涉及角色切人CD未就绪 或是 技能优先级低于前台技能,合轴失败!"
)
if skill_tag == SWAP_CANCEL_DEBUG_TARGET_SKILL
else None
)
)
elif mode == 4:
(
print(f"当前tick不满足{skill_tag}合轴所需的时间!")
if not skill_compare
else (
print(f"当前tick不满足{skill_tag}合轴所需的时间!")
if skill_tag == SWAP_CANCEL_DEBUG_TARGET_SKILL
else None
)
)
elif mode == 5:
if last_actively_generated_node:
(
print(
f"{skill_tag}的上一个主动动作{last_actively_generated_node.skill.skill_tag}的APL策略为不要合轴!"
)
if not skill_compare
else (
print(
f"{skill_tag}的上一个主动动作{last_actively_generated_node.skill.skill_tag}的APL策略为不要合轴!"
)
if skill_tag == SWAP_CANCEL_DEBUG_TARGET_SKILL
else None
)
)
else:
raise ValueError("mode参数错误!")
================================================
FILE: zsim/sim_progress/Preload/PreloadEngine/__init__.py
================================================
from .APLEngine import APLEngine
from .AttackAnswerEngine import AttackResponseEngine
from .BasePreloadEngine import BasePreloadEngine
from .ConfirmEngine import ConfirmEngine
from .ForceAddEngine import ForceAddEngine
from .SwapCancelValidateEngine import SwapCancelValidateEngine
__all__ = [
"APLEngine",
"BasePreloadEngine",
"ForceAddEngine",
"ConfirmEngine",
"SwapCancelValidateEngine",
"AttackResponseEngine",
]
================================================
FILE: zsim/sim_progress/Preload/PreloadStrategy.py
================================================
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
from zsim.models.event_enums import SpecialStateUpdateSignal as SSUS
from zsim.sim_progress.Preload.PreloadEngine import (
APLEngine,
AttackResponseEngine,
ConfirmEngine,
ForceAddEngine,
SwapCancelValidateEngine,
)
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
from .PreloadDataClass import PreloadData
class BasePreloadStrategy(ABC):
"""基础策略,无论是什么策略,都会包含 APL、强制添加技能以及最终技能确认三个引擎。"""
def __init__(self, data, apl_path):
self.data: "PreloadData" = data
self.apl_engine = APLEngine(data, apl_path=apl_path)
self.force_add_engine = ForceAddEngine(data)
self.confirm_engine = ConfirmEngine(data)
self.finish_post_init: bool = False # 是否完成了后置初始化
@abstractmethod
def generate_actions(self, *args, **kwargs):
pass
@abstractmethod
def check_myself(self, *args, **kwargs):
pass
@abstractmethod
def reset_myself(self):
pass
class SwapCancelStrategy(BasePreloadStrategy):
def __init__(self, data, apl_path: str | None):
super().__init__(data, apl_path=apl_path)
self.swap_cancel_engine = SwapCancelValidateEngine(data)
self.attack_response_engine = AttackResponseEngine(
data=data, sim_instance=self.data.sim_instance
)
self.tick = 0
def generate_actions(self, enemy, tick: int) -> None:
"""合轴逻辑"""
# 0、自检
self.check_myself(enemy, tick)
assert self.data.sim_instance is not None
self.data.sim_instance.schedule_data.enemy.special_state_manager.broadcast_and_update(
signal=SSUS.BEFORE_PRELOAD
)
# 0.5、 EnemyAttack结构运行一次
self.attack_response_engine.run_myself(tick=tick)
# 1、APL引擎抛出本tick的主动动作
apl_skill_node = self.apl_engine.run_myself(tick)
if apl_skill_node is not None:
apl_skill_tag = apl_skill_node.skill_tag
priority = apl_skill_node.apl_priority
else:
apl_skill_tag = "wait"
apl_skill_node = None
priority = 0
# print(apl_skill_tag, priority)
# TODO:新增功能:Enemy进攻模块的反馈接口,即招架后Enemy动作被打断;或是角色动作被Enemy打断的功能;
# TODO:“破招”事件需通过decibel manager向角色发放对应的喧响值奖励;
# 2、ForceAdd引擎处理旧有的强制添加逻辑;
self.force_add_engine.run_myself(tick)
# 3、SwapCancel引擎 判定当前tick和技能是否能够成功合轴
self.swap_cancel_engine.run_myself(
apl_skill_tag, tick, apl_priority=priority, apl_skill_node=apl_skill_node
)
if (
self.swap_cancel_engine.active_signal
or self.force_add_engine.active_signal
or self.swap_cancel_engine.external_update_signal
):
# 4、Confirm引擎 清理data.preload_action_list_before_confirm,
self.confirm_engine.run_myself(
tick, apl_skill_node=apl_skill_node, apl_skill_tag=apl_skill_tag
)
def check_myself(self, enemy, tick, *args, **kwargs):
"""准备工作"""
if not self.finish_post_init:
self.post_init_all_object()
self.finish_post_init = True
self.data.chek_myself_before_start_preload(enemy, tick)
def reset_myself(self):
pass
def post_init_all_object(self):
"""后置初始化所有数据"""
assert self.data.sim_instance is not None
sim_instance: Simulator = self.data.sim_instance
for char_obj in sim_instance.char_data.char_obj_list:
char_obj.POST_INIT_DATA(sim_instance=sim_instance)
class SequenceStrategy:
def generate_actions(self):
# 封装顺序生成逻辑
pass
================================================
FILE: zsim/sim_progress/Preload/SkillsQueue.py
================================================
import threading
import uuid
from typing import TYPE_CHECKING, Iterable
import pandas as pd
from zsim.define import ELEMENT_TYPE_MAPPING as ETM
from zsim.define import ElementType, config
from zsim.sim_progress.Character.skill_class import Skill
from zsim.sim_progress.data_struct.LinkedList import LinkedList
from zsim.sim_progress.Report import report_to_log
if TYPE_CHECKING:
from zsim.sim_progress.Load import LoadingMission
class SkillNode:
_instance_counter = 0
_counter_lock = threading.Lock()
def __init__(
self,
skill: Skill.InitSkill,
preload_tick: int,
active_generation: bool = False,
apl_unit=None,
**kwargs,
):
"""
预加载技能节点
包含:
1、部分需要立即调用的信息;
2、整个 Skill.InitSkill 对象,包含了技能的全部信息,用于计算器调用
"""
with SkillNode._counter_lock:
self.apl_priority: int = kwargs.get("apl_priority", 0)
self.apl_unit = apl_unit
self.skill_tag: str = skill.skill_tag
self.char_name: str = skill.char_name
self.preload_tick: int = preload_tick
self.hit_times: int = skill.hit_times
self.labels: dict[str, list[str] | str | int | float] | None = skill.labels
self.skill: Skill.InitSkill = skill
self.end_tick: int = self.preload_tick + self.skill.ticks
self.active_generation: bool = active_generation # 构造函数的调用来源是否是主动动作
# TODO:后续需用UUID替换skill_node实例ID
self.instance_id = SkillNode._instance_counter
SkillNode._instance_counter += 1
# 生成 UUID
self.UUID = uuid.uuid4()
tick_list = []
if self.skill.tick_list:
for hit_tick in self.skill.tick_list:
tick_key = self.preload_tick + hit_tick
tick_list.append(tick_key)
else:
time_step = (self.skill.ticks - 1) / (self.hit_times + 1)
for i in range(self.hit_times):
tick_key = self.preload_tick + time_step * (i + 1)
tick_list.append(tick_key)
self.tick_list = tick_list
self.loading_mission: "LoadingMission | None" = None
self._effective_anomaly_buildup: bool = True
self._element_type_change: ElementType | None = None
self.force_qte_trigger: bool = False
@property
def is_additional_damage(self) -> bool:
"""判断当前技能是否为额外伤害"""
if self.skill.labels is None:
return False
else:
if "additional_damage" in self.skill.labels:
return True
else:
return False
@property
def element_type(self) -> ElementType:
"""返回当前的属性种类!(考虑染色)"""
if self._element_type_change is None:
return self.skill.element_type
else:
return self._element_type_change
@property
def element_type_change(self) -> ElementType | None:
"""技能的染色"""
return self._element_type_change
@element_type_change.setter
def element_type_change(self, value: ElementType | None):
if self._element_type_change is not None:
raise ValueError(
f"技能{self.skill_tag}已经被染色为【{ETM.get(self._element_type_change)}】属性!不能被重复染色!"
)
self._element_type_change = value
@property
def effective_anomaly_buildup(self) -> bool:
"""判断技能是否为异常技能"""
return self._effective_anomaly_buildup
@effective_anomaly_buildup.setter
def effective_anomaly_buildup(self, value: bool):
self._effective_anomaly_buildup = value
def __str__(self) -> str:
return f"SkillNode: {self.skill_tag}"
@classmethod
def get_total_instances(cls) -> int:
"""获取当前skill_node的唯一ID,该ID在skill_node被构造时就已经确定"""
return cls._instance_counter
def have_label(self, label_key: str):
"""判断当前skill_node是否拥有传入数值的skill_label"""
if self.skill.labels is None:
return False
if self.labels is not None and label_key in self.labels.keys():
return True
else:
return False
def is_heavy_hit(self, tick: int) -> bool:
"""判断当前技能是否为重击"""
if not self.skill.heavy_attack:
return False
last_hit = self.tick_list[-1]
if tick - 1 < last_hit <= tick:
return True
else:
return False
def is_hit_now(self, tick: int) -> bool:
"""判断当前技能是否命中"""
for tick_key in self.tick_list:
if tick - 1 < tick_key <= tick:
return True
continue
else:
return False
def is_last_hit(self, tick: int):
"""判断当前tick是否存在最后一击"""
if not self.is_hit_now(tick):
return False
else:
return tick - 1 < self.tick_list[-1] <= tick
def spawn_node(tag: str, preload_tick: int, skills: Iterable[Skill], **kwargs) -> SkillNode:
"""
通过输入的tag和preload_tick,直接创建SkillNode。
"""
active_generation = kwargs.get("active_generation", False)
apl_priority = kwargs.get("apl_priority", 0)
apl_unit = kwargs.get("apl_unit", None)
for obj in skills:
if tag in obj.skills_dict.keys():
node = SkillNode(
obj.skills_dict[tag],
preload_tick,
active_generation,
apl_priority=apl_priority,
apl_unit=apl_unit,
)
return node
else:
raise ValueError(
f"预加载技能 {tag} 不存在于输入的 Skill 类中,请检查输入, "
f"当前技能列表为:{[(skill.name, skill.skills_dict.keys()) for skill in skills]}"
)
def get_skills_queue(
preload_table: pd.DataFrame,
*skills: Skill,
) -> tuple[int, LinkedList]:
"""
提取dataframe中,‘skill_tag’列的信息
并将其与输入的 Skill 类比对
可输入任意数量的 Skill 类比对。
示例:
get_skills_queue(dataframe,
skills = Skill( name='艾莲'),
skills_2 = Skill(name = '苍角'),
skills_3 = Skill(name = '莱卡恩'))
返回:一个链表,包含全部可被预加载的 SkillNode
"""
# 输入类型检查
if not isinstance(preload_table, pd.DataFrame):
raise TypeError("预加载序列表必须是 pandas.DataFrame 类型")
if not all(isinstance(x, Skill) for x in skills):
raise TypeError("输入的技能必须是 Skill 类")
skills_queue = LinkedList() # 用于储存技能节点
preload_skills: pd.Series = pd.Series()
try:
preload_skills: pd.Series = preload_table["skill_tag"] # 传入的数据必须包含 skill_tag 列
except KeyError:
print("提供错误的预加载序列表,请检查输入")
# 确保技能列表不为空
if not preload_skills.empty:
preload_skills_list: list[str] = preload_skills.tolist()
else:
raise ValueError("预加载序技能列表为空")
preload_tick_stamps = {skill.CID: 0 for skill in skills}
if not config.apl_mode.enabled:
for tag in preload_skills_list:
cid = int(tag[:4]) # 提取tag的前四个字符作为key
if cid not in preload_tick_stamps:
raise ValueError(f"技能 {tag} 的CID不在输入的技能列表中,请检查输入")
try:
# 在__main__中寻找tick变量,并与preload_tick_stamp比较
tick = globals().get("tick", 0)
preload_tick_stamp = max(
preload_tick_stamps[cid], tick
) # 取最大值作为preload_tick_stamp
node = spawn_node(tag, preload_tick_stamp, skills)
skills_queue.add(node)
preload_tick_stamps[cid] = (
preload_tick_stamp + node.skill.ticks
) # 更新preload_tick_stamp
report_to_log(
f"[PRELOAD]:预加载节点 {tag} 已创建,将在 {preload_tick_stamps[cid]} 执行",
level=2,
)
except ValueError as e:
raise ValueError(str(e))
return max(preload_tick_stamps.values()), skills_queue
if __name__ == "__main__":
pass
================================================
FILE: zsim/sim_progress/Preload/__init__.py
================================================
from . import SkillsQueue, watchdog
from .APLModule.APLClass import APLClass
from .APLModule.APLJudgeTools import find_char, get_game_state
from .APLModule.APLParser import APLParser
from .PreloadClass import PreloadClass
from .PreloadDataClass import PreloadData
from .SkillsQueue import SkillNode
__all__ = [
"watchdog",
"SkillsQueue",
"SkillNode",
"APLParser",
"APLClass",
"find_char",
"get_game_state",
"PreloadClass",
"PreloadData",
]
================================================
FILE: zsim/sim_progress/Preload/apl_unit/APLUnit.py
================================================
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
from zsim.define import compare_methods_mapping
from ..APLModule.SubConditionUnit import (
ActionSubUnit,
AttributeSubUnit,
BaseSubConditionUnit,
BuffSubUnit,
SpecialSubUnit,
StatusSubUnit,
)
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
class APLUnit(ABC):
def __init__(self, sim_instance: "Simulator"):
"""一行APL就是一个APLUnit,它是所有APLUnit的基类。"""
self.priority = 0
self.char_CID = None
self.break_when_found_action = True
self.result = None
self.sub_conditions_unit_list = []
self.sub_conditions_ast = None
self.apl_unit_type = None
self.sim_instance = sim_instance
@abstractmethod
def check_all_sub_units(self, found_char_dict, game_state, sim_instance: "Simulator", **kwargs):
pass
def evaluate_condition_ast(
self, node: "ExprNode", found_char_dict, game_state, sim_instance, tick, result_box
):
"""递归地评估逻辑树的表达式节点"""
if node.is_leaf():
if not isinstance(node.sub_condition, BaseSubConditionUnit):
raise TypeError("逻辑树中包含非 BaseSubConditionUnit 类型的叶子节点")
result = node.sub_condition.check_myself(
found_char_dict, game_state, tick=tick, sim_instance=sim_instance
)
result_box.append(result)
return result
else:
left_result = self.evaluate_condition_ast(
node.left, found_char_dict, game_state, sim_instance, tick, result_box
)
right_result = self.evaluate_condition_ast(
node.right, found_char_dict, game_state, sim_instance, tick, result_box
)
if node.operator == "and":
return left_result and right_result
elif node.operator == "or":
return left_result or right_result
else:
raise ValueError(f"未知逻辑运算符: {node.operator}")
def spawn_sub_condition(
priority: int, sub_condition_code: str = None
) -> ActionSubUnit | BuffSubUnit | StatusSubUnit | AttributeSubUnit | ActionSubUnit:
"""解构apl子条件字符串,并且组建出构建sub_condition类需要的构造字典"""
logic_mode = 0
sub_condition_dict = {}
code_head = sub_condition_code.split(":")[0]
if "special" not in code_head and "." not in code_head:
raise ValueError(f"不正确的条件代码!{sub_condition_code}")
if code_head.startswith("!"):
code_head = code_head[1:]
logic_mode = 1
sub_condition_dict["type"] = code_head.split(".")[0]
sub_condition_dict["target"] = code_head.split(".")[1]
code_body = sub_condition_code.split(":")[1]
for _operator in [">=", "<=", "==", ">", "<", "!="]:
if _operator in code_body:
sub_condition_dict["operation_type"] = compare_methods_mapping[_operator]
sub_condition_dict["stat"] = code_body.split(_operator)[0]
sub_condition_dict["value"] = code_body.split(_operator)[1]
break
else:
raise ValueError(f"不正确的计算符!{code_body}")
sub_condition_output = sub_condition_unit_factory(priority, sub_condition_dict, mode=logic_mode)
return sub_condition_output
def sub_condition_unit_factory(priority: int, sub_condition_dict: dict = None, mode=0):
"""根据传入的dict,来构建不同的子条件单元"""
condition_type = sub_condition_dict["type"]
if condition_type not in ["status", "attribute", "buff", "action", "special"]:
raise ValueError(f"不正确的条件类型!{sub_condition_dict['type']}")
if condition_type == "status":
return StatusSubUnit(priority, sub_condition_dict, mode)
elif condition_type == "attribute":
return AttributeSubUnit(priority, sub_condition_dict, mode)
elif condition_type == "buff":
return BuffSubUnit(priority, sub_condition_dict, mode)
elif condition_type == "action":
return ActionSubUnit(priority, sub_condition_dict, mode)
elif condition_type == "special":
return SpecialSubUnit(priority, sub_condition_dict, mode)
else:
raise ValueError(f"special类的APL解析,是当前尚未开发的功能!优先级为{priority},")
class SimpleUnitForForceAdd(APLUnit):
def __init__(self, condition_list, sim_instance: "Simulator" = None):
super().__init__(sim_instance=sim_instance)
self.whole_line = condition_list
for condition_str in condition_list:
self.sub_conditions_unit_list.append(spawn_sub_condition(self.priority, condition_str))
def check_all_sub_units(self, found_char_dict, game_state, sim_instance: "Simulator", **kwargs):
if self.sim_instance is None:
self.sim_instance = sim_instance
result_box = []
tick = kwargs.get("tick", None)
if not self.sub_conditions_unit_list:
return True, result_box
for sub_units in self.sub_conditions_unit_list:
if not isinstance(sub_units, BaseSubConditionUnit):
raise TypeError("ActionAPLUnit类的sub_conditions_unit_list中的对象构建不正确!")
result = sub_units.check_myself(
found_char_dict, game_state, tick=tick, sim_instance=sim_instance
)
result_box.append(result)
if not result:
return False, result_box
else:
return True, result_box
class ExprNode:
def __init__(
self, operator=None, left=None, right=None, sub_condition: "BaseSubConditionUnit" = None
):
"""
- operator: "and", "or"(逻辑运算符)
- left/right: ExprNode 对象
- sub_condition: 原子条件(BaseSubConditionUnit 的实例),只在叶子节点设置
"""
self.operator = operator
self.left = left
self.right = right
self.sub_condition = sub_condition
def is_leaf(self):
return self.operator is None
def logic_tree_to_expr_node(priority: int, logic_tree: dict | str | None) -> ExprNode | None:
if logic_tree is None:
return None
# 如果是字符串(最小条件单元),构造叶子节点
if isinstance(logic_tree, str):
return ExprNode(sub_condition=spawn_sub_condition(priority, logic_tree))
# 应该只有一个操作符键
assert isinstance(logic_tree, dict) and len(logic_tree) == 1
operator = list(logic_tree.keys())[0]
children = logic_tree[operator]
# 如果只有两个元素,直接构造左右节点
if len(children) == 2:
left_node = logic_tree_to_expr_node(priority, children[0])
right_node = logic_tree_to_expr_node(priority, children[1])
return ExprNode(operator=operator, left=left_node, right=right_node)
# 如果超过两个,需要递归构造嵌套结构(左结合)
current = logic_tree_to_expr_node(priority, children[0])
for i in range(1, len(children)):
right = logic_tree_to_expr_node(priority, children[i])
current = ExprNode(operator=operator, left=current, right=right)
return current
================================================
FILE: zsim/sim_progress/Preload/apl_unit/ActionAPLUnit.py
================================================
from typing import TYPE_CHECKING
from .APLUnit import APLUnit
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
class ActionAPLUnit(APLUnit):
def __init__(self, apl_unit_dict: dict, sim_instance: "Simulator" = None):
"""动作类APL"""
super().__init__(sim_instance=sim_instance)
self.char_CID = apl_unit_dict["CID"]
self.priority = apl_unit_dict["priority"]
self.apl_unit_type = apl_unit_dict["type"]
self.break_when_found_action = True
self.result = apl_unit_dict["action"]
self.whole_line = apl_unit_dict.get("whole_line", None)
from zsim.sim_progress.Preload.apl_unit.APLUnit import (
logic_tree_to_expr_node,
spawn_sub_condition,
)
for condition_str in apl_unit_dict["conditions"]:
self.sub_conditions_unit_list.append(spawn_sub_condition(self.priority, condition_str))
self.sub_conditions_ast = logic_tree_to_expr_node(
self.priority, apl_unit_dict.get("conditions_tree", None)
)
self.builtin_percond_list: list = []
if self.result == "assault_after_parry": # 对于突击支援,需要添加一项内置的条件检查。
"""APL脚本代码:action.CID:positive_linked_after==CID_knock_back_cause_parry"""
precond_str_1 = f"action.{self.char_CID}:strict_linked_after=={self.char_CID}_knock_back_cause_parry"
precond_str_2 = f"special.preload_data:operating_char=={self.char_CID}"
for precond in [precond_str_1, precond_str_2]:
self.builtin_percond_list.append(spawn_sub_condition(self.priority, precond))
def check_all_sub_units(self, found_char_dict, game_state, sim_instance: "Simulator", **kwargs):
"""单行APL的逻辑函数:检查所有子条件并且输出结果"""
result_box = []
tick = kwargs.get("tick", None)
if self.builtin_percond_list:
for precond_unit in self.builtin_percond_list:
if not precond_unit.check_myself(
found_char_dict, game_state, tick=tick, sim_instance=sim_instance
):
return False, result_box
if not self.sub_conditions_unit_list:
"""无条件直接输出True"""
return True, result_box
if self.sub_conditions_ast is None:
return True, result_box
final_result = self.evaluate_condition_ast(
self.sub_conditions_ast, found_char_dict, game_state, sim_instance, tick, result_box
)
# 下列代码块是用于检查QTE是否在重复释放上符合规则(即QTE是否已经被响应过了)
if "QTE" in self.result:
enemy = self.sim_instance.schedule_data.enemy
qte_manager = enemy.qte_manager
qte_leagal = qte_manager.check_qte_legality(qte_skill_tag=self.result)
else:
qte_leagal = True
final_final_result = final_result and qte_leagal
return final_final_result, result_box
================================================
FILE: zsim/sim_progress/Preload/apl_unit/AtkResponseAPLUnit.py
================================================
from typing import TYPE_CHECKING
from zsim.sim_progress.Preload.apl_unit.APLUnit import APLUnit
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
class AtkResponseAPLUnit(APLUnit):
def __init__(self, apl_unit_dict: dict, sim_instance: "Simulator" = None):
"""动作响应类APL"""
super().__init__(sim_instance=sim_instance)
self.char_CID = apl_unit_dict["CID"]
self.priority = apl_unit_dict["priority"]
self.apl_unit_type = apl_unit_dict["type"]
self.whole_line = apl_unit_dict.get("whole_line", None)
self.response_proactive_level = None # 进攻响应APL的主动响应等级
if self.apl_unit_type.split("_")[-1] == "positive+=":
self.response_proactive_level = 1
elif self.apl_unit_type.split("_")[-1] == "balance+=":
self.response_proactive_level = 0
else:
raise ValueError(
f"不正确的进攻响应APL类型:{self.apl_unit_type},只能是positive或balance!"
)
if "atk_response" not in self.apl_unit_type:
raise ValueError("企图对非进攻响应APL构造AtkResponseAPLUnit类!")
self.break_when_found_action = True
self.result = apl_unit_dict["action"]
from zsim.sim_progress.Preload.apl_unit.APLUnit import (
logic_tree_to_expr_node,
spawn_sub_condition,
)
for condition_str in apl_unit_dict["conditions"]:
self.sub_conditions_unit_list.append(spawn_sub_condition(self.priority, condition_str))
self.sub_conditions_ast = logic_tree_to_expr_node(
self.priority, apl_unit_dict.get("conditions_tree", None)
)
self.common_response_tag_list = ["parry", "dodge"]
def check_all_sub_units(self, found_char_dict, game_state, sim_instance: "Simulator", **kwargs):
"""仅供模式下的单行APL的逻辑函数:检查所有子条件并且输出结果"""
result_box = []
tick = kwargs.get("tick", None)
if tick is None:
tick = self.sim_instance.tick
if not self.check_atk_response_conditions(tick):
"""如果进攻响应的前置条件不满足,直接返回False"""
return False, result_box
"""在进行附加条件的检查之前,先检查当前时间是否符合响应策略积极度"""
if not self.check_response_tick(tick):
return False, result_box
if self.sub_conditions_ast is None:
return True, result_box
final_result = self.evaluate_condition_ast(
self.sub_conditions_ast, found_char_dict, game_state, sim_instance, tick, result_box
)
return final_result, result_box
def check_atk_response_conditions(self, tick: int) -> bool:
"""检查进攻响应的前置条件是否满足"""
atk_manager = self.sim_instance.preload.preload_data.atk_manager
if not atk_manager.attacking:
# print("当前没有正在进行的进攻事件,无法响应!")
return False
if atk_manager.is_answered:
# print("当前进攻事件已经被响应,无法再次响应!")
return False
rt_tick = atk_manager.get_rt()
can_be_answered_result_tuple: tuple = atk_manager.can_be_answered(rt_tick=rt_tick)
if not can_be_answered_result_tuple[0]:
print(
f"当前进攻事件无法在第{tick}tick被响应,当前的响应窗口为{can_be_answered_result_tuple}"
)
return False
return True
def check_response_tick(self, tick: int) -> bool:
"""检查当前tick是否符合响应策略积极度"""
proactive_level = self.response_proactive_level
atk_manager = self.sim_instance.preload.preload_data.atk_manager
response_window: tuple[int, int]
skill_tick: int = 0
if any([common_tag in self.result for common_tag in self.common_response_tag_list]):
response_window = (
atk_manager.interaction_window_open_tick,
atk_manager.interaction_window_close_tick,
)
else:
"""如果技能并非常规响应动作(如强化E、大招等),则需要获取技能的具体ticks,来计算新的响应窗口。"""
cid: int = int(self.char_CID)
skill_obj_list = self.sim_instance.preload.preload_data.skills
for _skill_obj in skill_obj_list:
if cid == _skill_obj.CID:
skill_obj = _skill_obj
break
else:
raise ValueError(f"没有找到CID为{cid}的技能对象!")
skill_tick: int = skill_obj.get_skill_info(skill_tag=self.result, attr_info="ticks")
response_window = atk_manager.get_uncommon_response_window(another_ta=skill_tick)
if proactive_level == 0:
"""在平衡策略下,响应动作需要尽量晚一些执行,所以检测右边界
但是又不能完全和右边界重合,因为那样太晚了,所以我们就让它提早一帧放行。"""
if tick + 1 == response_window[1]:
return True
elif proactive_level == 1:
from zsim.define import ENEMY_ATK_PARAMETER_DICT
if skill_tick != 0 and skill_tick < ENEMY_ATK_PARAMETER_DICT["Taction"]:
print(
f"Warning: 技能 {self.result} 的ticks小于基础参数Taction,这会导致响应窗口的左边界不正确!所以直接于左边界处拦截,直接返回False。"
)
"""
但若是这个响应动作的时间小于30tick,则一定会引起响应失败。
在EnemyAttackAction中的get_first_hit的方法中,这一定会引起报错。
为了保证程序的运行,我需要提前拦截这种错误,让本函数返回False并且输出错误信息。
"""
return False
"""在积极策略下,响应动作需要尽量早一些执行,所以检测左边界"""
if tick == response_window[0]:
return True
else:
raise ValueError(
f"不正确的进攻响应APL的主动响应等级:{self.response_proactive_level}!"
)
return False
================================================
FILE: zsim/sim_progress/Preload/apl_unit/__init__.py
================================================
================================================
FILE: zsim/sim_progress/Preload/watchdog.py
================================================
from typing import TYPE_CHECKING
from zsim.define import ENABLE_WATCHDOG, WATCHDOG_LEVEL
from zsim.sim_progress.Report import report_to_log
if TYPE_CHECKING:
from zsim.sim_progress.Character.skill_class import Skill
from zsim.sim_progress.Preload.SkillsQueue import SkillNode
if ENABLE_WATCHDOG:
report_to_log("[INFO] Watchdog is enabled.", level=4)
def watch_reverse_order(
current_node: "SkillNode | Skill.InitSkill",
last_node: "SkillNode | Skill.InitSkill | None",
) -> bool | None:
"""
监控技能队列中的技能加载顺序,如果发现逆序加载则发出警告。
该函数检查当前节点和上一个节点的技能标签,判断是否存在逆序加载的情况。
逆序加载指的是技能队列中本应先加载的技能后于其他技能加载,这可能是数据顺序错误或技能依赖关系处理不当导致的。
参数:
current_node: 当前正在检查的技能节点,可以是SkillsQueue.SkillNode或Skill_Class.Skill.InitSkill类型。
last_node: 上一个已加载的技能节点,作为参考与当前节点比较。
返回值:
无。如果检测到逆序加载,会打印警告信息。
"""
if not ENABLE_WATCHDOG:
return None
if WATCHDOG_LEVEL <= 0:
return None
if last_node is None:
return None
if not (isinstance(current_node, SkillNode) or isinstance(current_node, Skill.InitSkill)):
return None
if not (isinstance(last_node, SkillNode) or isinstance(last_node, Skill.InitSkill)):
return None
current_tag = current_node.skill_tag
last_tag = last_node.skill_tag
if current_tag[:-1] == last_tag[:-1]:
if current_tag[-1] < last_tag[-1]:
feedback = (
f"[WARNING] Watchdog detected a reverse order preload event:"
f"Is {current_tag} really behind of {last_tag}?"
)
print(feedback)
report_to_log(feedback, level=0)
return False
return True
class WatchDog:
def __init__(self, **kwargs):
try:
watch_reverse_order(**kwargs)
except AttributeError:
pass
================================================
FILE: zsim/sim_progress/RandomNumberGenerator/__init__.py
================================================
import random
import threading
import time
from functools import lru_cache
from typing import TYPE_CHECKING
import numpy as np
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
MAX_SIGNED_INT64: int = 2**63 - 1
class RNG:
_instances = {}
_lock = threading.Lock()
def __new__(cls, sim_instance: "Simulator"):
"""为了RNG增加进程锁"""
with cls._lock:
if sim_instance not in cls._instances:
instance = super().__new__(cls)
cls._instances[sim_instance] = instance
return cls._instances[sim_instance]
def __init__(self, sim_instance: "Simulator"):
"""RNG的构造函数,每个进程只执行一次,反复调用构造函数会报错。"""
if not hasattr(self, "_initialized"):
self.seed: int | None = None
self.r: int | None = None
self.sim_instance = sim_instance
self.reseed()
self._initialized = True
self.NORMAL_TABLE_SIZE = 10000
self.normal_table = None
def get_seed(self) -> int:
assert self.seed is not None
return self.seed
def reseed(self, new_seed: int | None = None):
if self.sim_instance is None:
raise ValueError("RNG模块在初始化时,并未传入Simulator对象")
if self.sim_instance.in_parallel_mode:
# 当多进程模式时,seed的创造应该基于进程的UUID
assert self.sim_instance.sim_cfg is not None
run_turn_uuid: str | None = self.sim_instance.sim_cfg.run_turn_uuid
if run_turn_uuid is None:
raise ValueError("多进程模式下,sim_cfg中必须存在有效的run_turn_uuid")
hashed_uuid = abs(hash(run_turn_uuid)) % (2**63)
tick = self.sim_instance.tick
new_seed = (hashed_uuid + tick) if new_seed is None else (new_seed + tick)
else:
# 当单进程模式时,seed的创造应该基于当前的time()返回的结果
tick = self.sim_instance.tick
if new_seed is None:
new_seed = int(time.time() * 1000000) + tick
else:
new_seed = int(new_seed) + tick
(self.seed, self.r) = self.generate_random_number(new_seed)
random.seed(self.seed)
def random_float(self) -> float:
return random.uniform(0.0, 1.0)
@staticmethod
@lru_cache(maxsize=4)
def generate_random_number(seed: int) -> tuple[int, int]:
random.seed(seed)
random_number = random.randint(a=-MAX_SIGNED_INT64, b=MAX_SIGNED_INT64)
return seed, random_number
def generate_and_judge(self, possibility: float) -> bool:
self.seed, self.r = self.generate_random_number(self.seed)
return np.abs(self.r) < possibility * MAX_SIGNED_INT64
def normal_from_table(self) -> float:
"""生成正态分布的随机数,使用预先生成的正态分布表"""
if not hasattr(self, "normal_table") or self.normal_table is None:
self.normal_table = np.random.normal(loc=0, scale=1, size=self.NORMAL_TABLE_SIZE)
rng_float = self.random_float()
idx = int(rng_float * self.NORMAL_TABLE_SIZE)
idx = min(idx, self.NORMAL_TABLE_SIZE - 1)
value = self.normal_table[idx]
return float(value)
def __deepcopy__(self, memo): # pylint: disable=unused-argument
return self # 始终返回现有实例
def __copy__(self):
return self
================================================
FILE: zsim/sim_progress/Report/__init__.py
================================================
import asyncio
import json
import logging
import os
import threading
from datetime import datetime
from typing import TYPE_CHECKING
from zsim.define import NORMAL_MODE_ID_JSON
from .buff_handler import dump_buff_csv, report_buff_to_queue
from .log_handler import async_log_writer, log_queue, report_to_log
from .result_handler import (
async_result_writer,
report_dmg_result,
result_queue,
)
__all__ = [
"report_buff_to_queue",
"report_to_log",
"report_dmg_result",
"start_report_threads",
"stop_report_threads",
]
__result_id: str = "Unknown"
__event_loop: asyncio.AbstractEventLoop | None = None # 存储事件循环的引用
if TYPE_CHECKING:
from zsim.models.session.session_run import ExecAttrCurveCfg, ExecWeaponCfg
def regen_result_id(sim_cfg: "ExecAttrCurveCfg | ExecWeaponCfg | None", *, session_id=None) -> None:
"""
根据运行模式生成结果ID并处理相关文件。
如果 `sim_cfg` 不为 None(并行模式),则结果ID由 `run_turn_uuid`, `sc_name` 和 `sc_value` 组合而成,
格式为 "./results/{run_turn_uuid}/{sc_name}_{sc_value}"。
此模式下会创建对应的结果目录,并将 `parallel_config` 对象序列化为 JSON 文件(parallel_config.json)保存在该目录中。
如果 `sim_cfg` 为 None(普通模式),则从ID缓存文件中读取现有ID,
找到最大的有效ID,生成一个新的递增ID,并将新ID和时间戳写入缓存文件。
结果ID格式为 "./results/{current_id}"。
Args:
sim_cfg: 并行配置对象,或 None。
session_id: 会话ID,在API启动的普通模式下用作传递本次运行的id。
Returns:
None. 全局变量 `__result_id` 会被更新。
"""
global __result_id
if sim_cfg is not None:
# 并行模式:session_id(API模式)/随机生成的uuid(WebUI模式) + 配置列表作为id
if sim_cfg.func == "attr_curve":
__result_id = f"./results/{sim_cfg.run_turn_uuid}/{sim_cfg.func}_{sim_cfg.sc_name}_{sim_cfg.sc_value}" # type: ignore
elif sim_cfg.func == "weapon":
__result_id = f"./results/{sim_cfg.run_turn_uuid}/{sim_cfg.func}_{sim_cfg.weapon_name}_{sim_cfg.weapon_level}" # type: ignore
# 创建结果目录
os.makedirs(__result_id, exist_ok=True)
# 将 parallel_config 保存为 JSON 文件
config_path = os.path.join(__result_id, "sub.parallel_config.json")
try:
# 尝试将 dataclass 对象转换为字典以便序列化
config_dict = sim_cfg.model_dump()
# 更换角色相对位置为角色名
index = config_dict["adjust_char"]
from zsim.define import saved_char_config
config_dict["adjust_char"] = saved_char_config["name_box"][index - 1]
with open(config_path, "w", encoding="utf-8") as f:
json.dump(config_dict, f, indent=4, ensure_ascii=False)
except TypeError as e:
# 如果转换或序列化失败,记录错误日志
raise TypeError(f"无法将 parallel_config 转换为字典: {e}") from e
elif session_id is not None:
# API启动的普通模式:使用session_id作为id
cache_path = NORMAL_MODE_ID_JSON
# 检查缓存文件是否存在,如果不存在则创建
if not os.path.exists(cache_path):
os.makedirs(os.path.dirname(cache_path), exist_ok=True)
with open(cache_path, "w") as f:
json.dump({}, f, indent=4)
with open(cache_path, "r+", encoding="utf-8") as f:
id_cache_dict = json.load(f)
if session_id in id_cache_dict.keys():
logging.warning(f"session_id {session_id} 已存在,将使用该id")
else:
id_cache_dict[session_id] = datetime.now().strftime("%Y-%m-%d_%H%M")
f.seek(0)
json.dump(id_cache_dict, f, indent=4)
f.truncate()
__result_id = f"./results/{session_id}"
else:
# CLI或WebUI启动的普通模式:使用缓存文件中的最大ID+1作为id
cache_path = NORMAL_MODE_ID_JSON
# 检查缓存文件是否存在,如果不存在则创建
if not os.path.exists(cache_path):
os.makedirs(os.path.dirname(cache_path), exist_ok=True)
with open(cache_path, "w") as f:
json.dump({}, f, indent=4)
# 读取缓存文件
with open(cache_path, "r+") as f:
try:
id_cache_dict = json.load(f)
except json.decoder.JSONDecodeError:
id_cache_dict = {}
valid_ids = []
# 筛选出有效的整数ID
for key in id_cache_dict.keys():
try:
valid_ids.append(int(key))
except ValueError:
continue
# 确定新的ID
if valid_ids:
current_id = max(valid_ids) + 1
else:
current_id = 0
# 将新ID和当前时间戳添加到缓存字典中
id_cache_dict[str(current_id)] = datetime.now().strftime("%Y-%m-%d_%H%M")
f.seek(0)
# 将更新后的缓存字典写回文件
json.dump(id_cache_dict, f, indent=4)
# 截断文件到当前写入位置
f.truncate()
# 更新全局结果ID
__result_id = f"./results/{current_id}"
def start_async_tasks():
"""启动异步任务处理日志和结果写入"""
# 在新线程中运行事件循环
def run_event_loop():
global __event_loop
# 如果已有事件循环在运行,则不再创建新的
if __event_loop is not None:
return
# 创建新的事件循环
__event_loop = asyncio.new_event_loop()
asyncio.set_event_loop(__event_loop)
__event_loop.create_task(async_log_writer(__result_id))
__event_loop.create_task(async_result_writer(__result_id))
__event_loop.run_forever()
loop_thread = threading.Thread(target=run_event_loop, daemon=True)
loop_thread.start()
def start_report_threads(sim_cfg, *, session_id=None):
"""用于在开始模拟时启动线程以处理日志和结果写入。"""
regen_result_id(sim_cfg, session_id=session_id)
start_async_tasks()
def stop_report_threads():
dump_buff_csv(__result_id)
log_queue.join()
result_queue.join()
================================================
FILE: zsim/sim_progress/Report/buff_handler.py
================================================
import os
from collections import defaultdict
import polars as pl
from zsim.define import DEBUG, DEBUG_LEVEL
buffered_data: dict[str, dict[int, dict[str, int]]] = defaultdict(
lambda: defaultdict(lambda: defaultdict(int))
)
def report_buff_to_queue(
character_name: str, time_tick, buff_name: str, buff_count, all_match: bool, level=4
):
if DEBUG and DEBUG_LEVEL <= level:
if all_match:
# 由于Buff的log录入总是在下个tick的开头,所以这里的time_tick要-1
buffered_data[character_name][time_tick - 1][buff_name] += buff_count
def dump_buff_csv(result_id: str):
# Check if buffered_data has any content
if not buffered_data:
return
for char_name, char_data in buffered_data.items():
if not char_data:
continue
# 收集所有可能的buff名称
all_buff_names = set()
for buffs in char_data.values():
all_buff_names.update(buffs.keys())
# 构建行数据,确保所有行都有相同的列
rows = []
for tick, buffs in char_data.items():
row = {"time_tick": tick}
# 确保所有可能的 buff 列都存在于每行中
for buff_name in all_buff_names:
row[buff_name] = buffs.get(buff_name, 0)
rows.append(row)
if not rows:
continue
buff_report_file_path = f"{result_id}/buff_log/{char_name}.csv"
# Ensure the directory exists
try:
os.makedirs(os.path.dirname(buff_report_file_path), exist_ok=True)
except Exception:
continue
# Create DataFrame and sort columns
try:
df = pl.DataFrame(rows)
if df.is_empty():
continue
# Sort columns: time_tick first, then buff names alphabetically for deterministic output.
buff_columns = sorted([col for col in df.columns if col != "time_tick"])
df = df.sort("time_tick").select(["time_tick"] + buff_columns)
# Write CSV file
df.write_csv(buff_report_file_path, include_bom=True)
except Exception:
pass
================================================
FILE: zsim/sim_progress/Report/log_handler.py
================================================
import asyncio
import os
import queue
import aiofiles
from zsim.define import DEBUG, DEBUG_LEVEL
log_queue: queue.Queue = queue.Queue()
def report_to_log(content: str | None = None, level=4) -> None:
if not DEBUG or content is None:
return
if DEBUG and DEBUG_LEVEL <= level:
log_queue.put(content)
async def async_log_writer(result_id: str):
report_file_path = f"./logs/{result_id}.log".replace("./results/", "")
os.makedirs(os.path.dirname(report_file_path), exist_ok=True)
while True:
try:
content = log_queue.get_nowait()
async with aiofiles.open(report_file_path, "a", encoding="utf-8") as file:
await file.write(f"{content}\n")
log_queue.task_done()
except queue.Empty:
await asyncio.sleep(0.01)
================================================
FILE: zsim/sim_progress/ScheduledEvent/CalAnomaly.py
================================================
from typing import TYPE_CHECKING, Literal
import numpy as np
from zsim.define import ELEMENT_TYPE_MAPPING as ETM
from zsim.define import ElementType
from zsim.sim_progress.anomaly_bar import AnomalyBar
from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import (
DirgeOfDestinyAnomaly as Abloom,
)
from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import (
Disorder,
PolarityDisorder,
)
from zsim.sim_progress.Character.character import Character
from zsim.sim_progress.Character.Yanagi import Yanagi
from zsim.sim_progress.Enemy import Enemy
from zsim.sim_progress.Report import report_to_log
from .Calculator import Calculator as Cal
from .Calculator import MultiplierData as MulData
if TYPE_CHECKING:
from zsim.sim_progress.Buff import Buff
from zsim.sim_progress.Character import Character
from zsim.simulator.simulator_class import Simulator
class CalAnomaly:
def __init__(
self,
anomaly_obj: AnomalyBar,
enemy_obj: Enemy,
dynamic_buff: dict[str, list["Buff"]],
sim_instance: "Simulator",
):
"""
Schedule 节点对于异常伤害的分支逻辑,用于计算异常伤害
调用方法 cal_anomaly_dmg() 输出.伤害期望
异常伤害快照以 array 形式储存,顺序为:
[基础伤害区、增伤区、异常精通区、等级、异常增伤区、异常暴击区、穿透率、穿透值、抗性穿透]
"""
self.sim_instance = sim_instance
self.enemy_obj = enemy_obj
self.anomaly_obj: AnomalyBar = anomaly_obj
if not self.anomaly_obj.settled:
raise ValueError(
f"即将被计算的 {ETM[self.anomaly_obj.element_type]} 异常条对象尚未结算快照,请检查前置业务逻辑"
)
self.dynamic_buff = dynamic_buff
snapshot: tuple[ElementType, np.ndarray] = (
self.anomaly_obj.element_type,
self.anomaly_obj.current_ndarray,
)
self.element_type: ElementType = snapshot[0]
# self.dmg_sp 以 array 形式储存,顺序为:基础伤害区、增伤区、异常精通区、等级、异常增伤区、异常暴击区、穿透率、穿透值、抗性穿透、冲击力、失衡值增幅
self.dmg_sp: np.ndarray = snapshot[1]
assert self.dmg_sp.shape == (1, 11), (
f"tick: {self.sim_instance.tick} 异常伤害快照形状错误,期望(1, 11),实际{self.dmg_sp}\n"
f"其他信息:名字:{type(self.anomaly_obj).__name__}\n"
f"属性:{self.anomaly_obj.element_type}\n"
f"是否是紊乱:{self.anomaly_obj.is_disorder}\n"
f"是否已经被结算:{self.anomaly_obj.settled}"
)
if anomaly_obj.activated_by is None:
print(
f"【CalAnomaly Warnning】:检测到异常实例(属性类型:{anomaly_obj.element_type})的激活源为空,改异常实例将无法享受Buff加成。"
)
raise NotImplementedError
else:
char_obj: "Character | None" = anomaly_obj.activated_by.skill.char_obj
# 根据动态buff读取怪物面板
self.data: MulData = MulData(
enemy_obj=self.enemy_obj,
dynamic_buff=self.dynamic_buff,
judge_node=anomaly_obj,
character_obj=char_obj,
)
# 虚拟角色等级
v_char_level: int = int(
np.floor(self.dmg_sp[0, 3] + 0.0000001)
) # 加一个极小的数避免精度向下丢失导致的误差
self.v_char_level = v_char_level
# 等级系数
k_level = self.cal_k_level(v_char_level)
# 激活型暴击区(目前仅简的核心被动)
active_crit: float = self.cal_active_crit(self.data)
# 防御区
def_mul: np.float64 = self.cal_def_mul(self.data, v_char_level)
# 抗性区
res_mul: float = Cal.RegularMul.cal_res_mul(
self.data,
element_type=self.element_type,
snapshot_res_pen=self.dmg_sp[0, 8],
)
# 减易伤区
vulnerability_mul: float = Cal.RegularMul.cal_dmg_vulnerability(
self.data, element_type=self.element_type
)
# 失衡易伤区
stun_vulnerability: float = Cal.RegularMul.cal_stun_vulnerability(self.data)
# 特殊乘区
special_mul: float = Cal.RegularMul.cal_special_mul(self.data)
imp_mul = self.dmg_sp[0, 9]
stun_mul = self.dmg_sp[0, 10]
self.final_multipliers: np.ndarray = self.set_final_multipliers(
k_level,
active_crit,
def_mul,
res_mul,
vulnerability_mul,
stun_vulnerability,
special_mul,
imp_mul,
stun_mul,
)
@staticmethod
def cal_k_level(v_char_level: int) -> np.float64:
"""等级区 = trunc(1+ 1/59* (等级 - 1), 4)"""
# 定义域检查
if v_char_level < 0:
report_to_log(f"角色等级{v_char_level}过低,将被设置为0")
v_char_level = 0
elif v_char_level > 60:
report_to_log(f"角色等级{v_char_level}过高,将被设置为60")
v_char_level = 60
# 查表
# fmt: off
values: list[float] = [
0, 1.0000, 1.0169, 1.0338, 1.0508, 1.0677, 1.0847, 1.1016, 1.1186, 1.1355, 1.1525,
1.1694, 1.1864, 1.2033, 1.2203, 1.2372, 1.2542, 1.2711, 1.2881, 1.3050, 1.3220,
1.3389, 1.3559, 1.3728, 1.3898, 1.4067, 1.4237, 1.4406, 1.4576, 1.4745, 1.4915,
1.5084, 1.5254, 1.5423, 1.5593, 1.5762, 1.5932, 1.6101, 1.6271, 1.6440, 1.6610,
1.6779, 1.6949, 1.7118, 1.7288, 1.7457, 1.7627, 1.7796, 1.7966, 1.8135, 1.8305,
1.8474, 1.8644, 1.8813, 1.8983, 1.9152, 1.9322, 1.9491, 1.9661, 1.9830, 2.0000
]
# fmt: on
return np.float64(values[v_char_level])
def cal_active_crit(self, data: MulData) -> float:
"""激活型异常暴击区
目前仅简的核心被动
"""
if self.element_type == 0:
crit_rate = data.dynamic.strike_crit_rate_increase
crit_dmg = data.dynamic.strike_crit_dmg_increase
return 1 + crit_rate * crit_dmg
else:
return 1
def cal_def_mul(self, data: MulData, v_char_level) -> np.float64:
"""防御区 = 攻击方等级基数 / (受击方有效防御 + 攻击方等级基数)"""
# 攻击方等级系数
k_attacker: int = Cal.RegularMul.cal_k_attacker(v_char_level)
# 计算属性/类型的穿透
if self.element_type == 0:
# 穿透率
addon_pen_ratio = float(self.dmg_sp[0, 6]) + self.data.dynamic.strike_ignore_defense
# 受击方有效防御
else:
addon_pen_ratio = float(self.dmg_sp[0, 6])
# 受击方有效防御
recipient_def: float = Cal.RegularMul.cal_recipient_def(
data,
Cal.RegularMul.cal_pen_ratio(data),
addon_pen_ratio=addon_pen_ratio,
addon_pen_numeric=float(self.dmg_sp[0, 7]),
)
# 计算防御区
defense_mul = k_attacker / (recipient_def + k_attacker)
return np.float64(defense_mul)
def set_final_multipliers(
self,
k_level,
active_crit,
def_mul,
res_mul,
vulnerability_mul,
stun_vulnerability,
special_mul,
imp_mul,
stun_mul,
) -> np.ndarray:
"""将计算结果写入 self.final_multipliers"""
# self.dmg_sp 以 array 形式储存,顺序为:基础伤害区、增伤区、异常精通区、等级、异常增伤区、异常暴击区、穿透率、穿透值、抗性穿透、冲击力、失衡值增幅
base_dmg = self.dmg_sp[0, 0]
dmg_bonus = self.dmg_sp[0, 1]
am_mul = self.dmg_sp[0, 2]
anomaly_bonus = self.dmg_sp[0, 4]
active_crit = active_crit
# 将所有乘数放入一个数组
results = np.array(
[
base_dmg,
dmg_bonus,
am_mul,
k_level,
anomaly_bonus,
active_crit,
def_mul,
res_mul,
vulnerability_mul,
imp_mul,
stun_mul,
stun_vulnerability,
special_mul,
],
dtype=np.float64,
)
return results
def cal_anomaly_dmg(self) -> np.float64:
"""计算异常伤害期望"""
"""
在v0.3.5a1中,由于爱丽丝的核心被动Dot会以固定比例造成属性异常伤害,
所以我们为属性异常伤害期望计算添加了缩放比例的乘算逻辑
"""
return np.float64(
np.prod(self.final_multipliers)
/ (self.dmg_sp[0, 9] * self.dmg_sp[0, 10])
* self.anomaly_obj.scaling_factor
)
class CalDisorder(CalAnomaly):
def __init__(
self,
disorder_obj: Disorder,
enemy_obj: Enemy,
dynamic_buff: dict[str, list["Buff"]],
sim_instance: "Simulator",
):
"""
异常伤害快照以 array 形式储存,顺序为:
[基础伤害区、增伤区、异常精通区、等级、异常增伤区、异常暴击区、穿透率、穿透值、抗性穿透]
"""
super().__init__(disorder_obj, enemy_obj, dynamic_buff, sim_instance=sim_instance)
self.final_multipliers[0] = self.cal_disorder_base_dmg(
np.float64(self.final_multipliers[0])
)
self.final_multipliers[4] = self.cal_disorder_extra_mul()
def cal_disorder_base_dmg(self, base_mul: np.float64) -> np.float64:
"""
计算紊乱的基础伤害
紊乱基础伤害 = (各属性异常剩余倍率 + 各属性紊乱基础倍率) * (1 + 紊乱基础倍率增幅)
"""
t_s = np.float64(self.anomaly_obj.remaining_tick() / 60)
disorder_base_dmg: np.float64
# 计算紊乱基础倍率增幅
disorder_basic_mul_map = self.data.dynamic.disorder_basic_mul_map
disorder_base_ratio_increase = (
disorder_basic_mul_map[self.element_type] + disorder_basic_mul_map["all"]
)
# 计算紊乱基础伤害
match self.element_type:
case 0: # 强击紊乱
_atk = base_mul / 7.13
_ratio = np.floor(t_s) * 0.075 + 4.5 + disorder_base_ratio_increase
case 1: # 灼烧紊乱
_atk = base_mul / 0.5
_ratio = np.floor(t_s / 0.5) * 0.5 + 4.5 + disorder_base_ratio_increase
case 2: # 霜寒紊乱
_atk = base_mul / 5
_ratio = np.floor(t_s) * 0.075 + 4.5 + disorder_base_ratio_increase
case 3: # 感电紊乱
_atk = base_mul / 1.25
_ratio = np.floor(t_s) * 1.25 + 4.5 + disorder_base_ratio_increase
case 4: # 侵蚀紊乱
_atk = base_mul / 0.625
_ratio = np.floor(t_s / 0.5) * 0.625 + 4.5 + disorder_base_ratio_increase
case 5: # 烈霜紊乱
_atk = base_mul / 5
_ratio = np.floor(t_s) * 0.75 + 6 + disorder_base_ratio_increase
case 6: # 玄墨侵蚀紊乱
_atk = base_mul / 0.625
_ratio = np.floor(t_s / 0.5) * 0.625 + 4.5 + disorder_base_ratio_increase
case _:
raise AssertionError(f"Invalid Element Type {self.element_type}")
disorder_base_dmg = _atk * _ratio
# from zsim.define import ELEMENT_TYPE_MAPPING as ETM
# print(f"111111,计算紊乱!{ETM[self.element_type]}属性的紊乱,攻击力为:{_atk:.2f},倍率为:{_ratio:.2f}, 紊乱倍率加成为:{disorder_base_ratio_increase}")
return np.float64(disorder_base_dmg)
def cal_disorder_extra_mul(self) -> np.float64:
"""
计算紊乱的异常额外增伤区,即紊乱的异常增伤区。
异常额外增伤区 = 1 + 对应属性异常额外增伤
紊乱的额外增伤本身只有一个词条:紊乱额外伤害增幅(disorder_dmg_mul),该词条本身是对所有紊乱通用的,
所以若是要实现“X属性紊乱伤害增幅”,则必须通过buff label的"specified_disorder_element_type"加以限制。
"""
map: dict[ElementType | Literal["all", -1], float] = self.data.dynamic.ano_extra_bonus
return np.float64(1 + map[-1])
def cal_disorder_stun(self) -> np.float64:
imp = self.final_multipliers[9]
stun_ratio = 2
stun_res = Cal.StunMul.cal_stun_res(self.data, self.element_type)
stun_bonus = self.final_multipliers[10]
stun_received = Cal.StunMul.cal_stun_received(self.data)
k_level_for_stun = 1 + self.v_char_level * 0.0075
return np.float64(
np.prod([imp, stun_ratio, stun_res, stun_bonus, stun_received, k_level_for_stun])
)
class CalPolarityDisorder(CalDisorder):
def __init__(
self,
disorder_obj: PolarityDisorder,
enemy_obj: Enemy,
dynamic_buff: dict[str, list["Buff"]],
sim_instance: "Simulator",
):
super().__init__(disorder_obj, enemy_obj, dynamic_buff, sim_instance=sim_instance)
yanagi_obj = self.__find_yanagi()
yanagi_mul = MulData(
enemy_obj=enemy_obj, dynamic_buff=dynamic_buff, character_obj=yanagi_obj
)
ap = Cal.AnomalyMul.cal_ap(yanagi_mul)
self.final_multipliers[0] = (
self.final_multipliers[0] * disorder_obj.polarity_disorder_ratio
) + (ap * disorder_obj.additional_dmg_ap_ratio)
def __find_yanagi(self) -> Yanagi | None:
yanagi_obj: Character | None = self.sim_instance.char_data.char_obj_dict.get("柳", None)
if yanagi_obj is None or not isinstance(yanagi_obj, Yanagi):
raise AssertionError("没柳你哪来的极性紊乱")
return yanagi_obj
class CalAbloom(CalAnomaly):
def __init__(
self,
abloom_obj: Abloom,
enemy_obj: Enemy,
dynamic_buff: dict[str, list["Buff"]],
sim_instance: "Simulator",
):
super().__init__(abloom_obj, enemy_obj, dynamic_buff, sim_instance=sim_instance)
self.final_multipliers[0] *= abloom_obj.anomaly_dmg_ratio
================================================
FILE: zsim/sim_progress/ScheduledEvent/Calculator.py
================================================
import json
from functools import lru_cache
from typing import Any, Literal
import numpy as np
from zsim.define import CHECK_SKILL_MUL, CHECK_SKILL_MUL_TAG, INVALID_ELEMENT_ERROR, ElementType
from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar
from zsim.sim_progress.Character import Character
from zsim.sim_progress.data_struct import cal_buff_total_bonus
from zsim.sim_progress.Enemy import Enemy
from zsim.sim_progress.Preload import SkillNode
from zsim.sim_progress.Report import report_to_log
from .constants import EventConstants
with open(
file="./zsim/sim_progress/ScheduledEvent/buff_effect_trans.json",
mode="r",
encoding="utf-8-sig",
) as f:
buff_effect_trans: dict = json.load(f)
class MultiplierData:
"""
乘数数据缓存管理类
使用缓存机制来存储和重用乘数计算结果,提高性能。
采用 LRU (Least Recently Used) 缓存策略来自动管理缓存大小。
"""
mul_data_cache: dict[tuple, "MultiplierData"] = {}
MAX_CACHE_SIZE = EventConstants.MAX_CACHE_SIZE
def __new__(
cls,
enemy_obj: Enemy,
dynamic_buff: dict[str, list],
character_obj: Character | None = None,
judge_node: SkillNode | AnomalyBar | None = None,
):
hashable_dynamic_buff = tuple((key, tuple(value)) for key, value in dynamic_buff.items())
enemy_hashable = (
tuple(enemy_obj.dynamic.dynamic_debuff_list),
tuple(enemy_obj.dynamic.dynamic_dot_list),
)
node_id = id(judge_node)
if isinstance(judge_node, AnomalyBar):
node_id = judge_node.UUID
# 使用更稳定的唯一标识符,避免垃圾回收后的问题
character_id = (
getattr(character_obj, "UUID", None)
or getattr(character_obj, "CID", None)
or f"{character_obj.__class__.__name__}_{id(character_obj)}"
)
cache_key = tuple((enemy_hashable, hashable_dynamic_buff, character_id, node_id))
if cache_key in cls.mul_data_cache:
return cls.mul_data_cache[cache_key]
else:
instance = super().__new__(cls)
if len(cls.mul_data_cache) >= cls.MAX_CACHE_SIZE:
cls.mul_data_cache.popitem()
cls.mul_data_cache[cache_key] = instance
return instance
def __init__(
self,
enemy_obj: Enemy,
dynamic_buff: dict | None = None,
character_obj: Character | None = None,
judge_node: SkillNode | AnomalyBar | None = None,
):
"""
初始化乘数数据实例
Args:
enemy_obj: 敌人对象
dynamic_buff: 动态buff字典
character_obj: 角色对象
judge_node: 判断节点(技能节点或异常条)
"""
if dynamic_buff is None:
dynamic_buff = {}
if not hasattr(self, "char_name"):
self.judge_node: SkillNode | AnomalyBar | None = judge_node
self.enemy_instance = enemy_obj
if character_obj is None:
self.char_name = None
self.char_level = None
self.cid = None
self.char_instance = None
else:
self.char_name = character_obj.NAME
self.char_level = character_obj.level
self.cid = character_obj.CID
self.char_instance = character_obj
# 获取角色局外面板数据
static_statement: Character.Statement | None = getattr(character_obj, "statement", None)
self.static = self.StaticStatement(static_statement)
# 获取敌人数据
self.enemy_obj = enemy_obj
# 获取buff动态加成
dynamic_statement: dict = self.get_buff_bonus(dynamic_buff, self.judge_node)
self.dynamic = self.DynamicStatement(dynamic_statement)
def get_buff_bonus(self, dynamic_buff: dict, node: SkillNode | AnomalyBar | None) -> dict:
"""
获取buff加成数据
Args:
dynamic_buff: 动态buff字典
node: 判断节点
Returns:
dict: 包含所有buff加成的字典
"""
if self.char_name is None:
char_buff: list = []
else:
try:
char_buff = dynamic_buff[self.char_name]
except KeyError:
char_buff = []
report_to_log(f"[WARNING] 动态Buff列表内没有角色 {self.char_name}", level=4)
try:
enemy_buff: list = self.enemy_obj.dynamic.dynamic_debuff_list
except AttributeError:
report_to_log("[WARNING] self.enemy_obj 中找不到动态buff列表", level=4)
try:
enemy_buff = dynamic_buff["enemy"]
except KeyError:
report_to_log("[WARNING] dynamic_buff 中依然找不到动态buff列表", level=4)
enemy_buff = []
enabled_buff: tuple = tuple(char_buff + enemy_buff)
try:
dynamic_statement: dict = cal_buff_total_bonus(
enabled_buff=enabled_buff,
judge_obj=node,
sim_instance=self.enemy_obj.sim_instance,
char_name=self.char_name,
)
except TypeError as err:
raise TypeError(
f"参数错误!enabled_buff为{type(enabled_buff)},node为{type(node)}"
) from err
return dynamic_statement
class StaticStatement:
_instance_cache: dict[tuple | None, Any] = {}
_max_cache_size = 128
def __new__(cls, static_statement: Character.Statement | None):
if static_statement is None:
cache_key = None
else:
cache_key = tuple(sorted(static_statement.statement.items()))
if cache_key in cls._instance_cache:
return cls._instance_cache[cache_key]
else:
instance = super().__new__(cls)
if len(cls._instance_cache) >= cls._max_cache_size:
cls._instance_cache.popitem()
cls._instance_cache[cache_key] = instance
return instance
def __init__(self, static_statement: Character.Statement | None):
"""将角色面板抄下来!!!!!如果没有角色传入,那就生成屎!!!"""
self.atk: float = 0.0
self.hp: float = 0.0
self.defense: float = 0.0
self.imp: float = 0.0
self.ap: float = 0.0
self.am: float = 0.0
self.crit_rate: float = 0.0
self.crit_damage: float = 0.0
self.sp_regen: float = 0.0
self.sp_get_ratio: float = 0.0
self.sp_limit: float = 0.0
self.pen_ratio: float = 0.0
self.pen_numeric: float = 0.0
self.phy_dmg_bonus: float = 0.0
self.ice_dmg_bonus: float = 0.0
self.fire_dmg_bonus: float = 0.0
self.ether_dmg_bonus: float = 0.0
self.electric_dmg_bonus: float = 0.0
attribute_map = {
"atk": "ATK",
"hp": "HP",
"defense": "DEF",
"imp": "IMP",
"ap": "AP",
"am": "AM",
"crit_rate": "CRIT_rate",
"crit_damage": "CRIT_damage",
"sp_regen": "sp_regen",
"sp_get_ratio": "sp_get_ratio",
"sp_limit": "sp_limit",
"pen_ratio": "PEN_ratio",
"pen_numeric": "PEN_numeric",
"phy_dmg_bonus": "PHY_DMG_bonus",
"ice_dmg_bonus": "ICE_DMG_bonus",
"fire_dmg_bonus": "FIRE_DMG_bonus",
"ether_dmg_bonus": "ETHER_DMG_bonus",
"electric_dmg_bonus": "ELECTRIC_DMG_bonus",
}
if static_statement is None:
pass
else:
for attr, static_attr in attribute_map.items():
setattr(self, attr, getattr(static_statement, static_attr, 0.0))
class DynamicStatement:
def __init__(self, dynamic_statement):
"""
buff动态加成的初始化蟑螂桶,这一百多行不是屎山,是为了IDE能认识这些傻逼玩意
"""
self.buff_name: float = 0.0
self.hp: float = 0.0
self.atk: float = 0.0
self.defense: float = 0.0
self.imp: float = 0.0
self.crit_rate: float = 0.0
self.crit_dmg: float = 0.0
self.anomaly_proficiency: float = 0.0
self.anomaly_mastery: float = 0.0
self.pen_ratio: float = 0.0
self.pen_numeric: float = 0.0
self.sp_regen: float = 0.0
self.sp_get_ratio: float = 0.0
self.sp_limit: float = 0.0
self.phy_dmg_bonus: float = 0.0
self.fire_dmg_bonus: float = 0.0
self.ice_dmg_bonus: float = 0.0
self.electric_dmg_bonus: float = 0.0
self.ether_dmg_bonus: float = 0.0
self.field_hp_percentage: float = 0.0
self.field_atk_percentage: float = 0.0
self.field_def_percentage: float = 0.0
self.field_imp_percentage: float = 0.0
self.field_crit_rate: float = 0.0
self.field_crit_dmg: float = 0.0
self.field_anomaly_proficiency: float = 0.0
self.field_anomaly_mastery: float = 0.0
self.field_pen_ratio: float = 0.0
self.field_pen_numeric: float = 0.0
self.field_sp_regen: float = 0.0
self.field_sp_get_ratio: float = 0.0
self.field_sp_limit: float = 0.0
self.extra_damage_ratio: float = 0.0 # 基础伤害倍率
self.decibel_get_ratio: float = 0.0 # 喧响获得效率
self.phy_crit_dmg_bonus: float = 0.0
self.fire_crit_dmg_bonus: float = 0.0
self.ice_crit_dmg_bonus: float = 0.0
self.electric_crit_dmg_bonus: float = 0.0
self.ether_crit_dmg_bonus: float = 0.0
self.phy_crit_rate_bonus: float = 0.0
self.fire_crit_rate_bonus: float = 0.0
self.ice_crit_rate_bonus: float = 0.0
self.electric_crit_rate_bonus: float = 0.0
self.ether_crit_rate_bonus: float = 0.0
self.attack_type_dmg_bonus: float = 0.0
self.normal_attack_dmg_bonus: float = 0.0
self.special_skill_dmg_bonus: float = 0.0
self.ex_special_skill_dmg_bonus: float = 0.0
self.dash_attack_dmg_bonus: float = 0.0
self.counter_attack_dmg_bonus: float = 0.0
self.qte_dmg_bonus: float = 0.0
self.ultimate_dmg_bonus: float = 0.0
self.quick_aid_dmg_bonus: float = 0.0
self.defensive_aid_dmg_bonus: float = 0.0
self.assault_aid_dmg_bonus: float = 0.0
self.anomaly_dmg_bonus: float = 0.0
self.all_dmg_bonus: float = 0.0
self.percentage_def_reduction: float = 0.0
self.def_reduction: float = 0.0
self.all_dmg_res_decrease: float = 0.0
self.physical_dmg_res_decrease: float = 0.0
self.fire_dmg_res_decrease: float = 0.0
self.ice_dmg_res_decrease: float = 0.0
self.electric_dmg_res_decrease: float = 0.0
self.ether_dmg_res_decrease: float = 0.0
self.all_res_pen_increase: float = 0.0
self.physical_res_pen_increase: float = 0.0
self.fire_res_pen_increase: float = 0.0
self.ice_res_pen_increase: float = 0.0
self.electric_res_pen_increase: float = 0.0
self.ether_res_pen_increase: float = 0.0
self.all_anomaly_res_decrease: float = 0.0
self.physical_anomaly_res_decrease: float = 0.0
self.fire_anomaly_res_decrease: float = 0.0
self.ice_anomaly_res_decrease: float = 0.0
self.electric_anomaly_res_decrease: float = 0.0
self.ether_anomaly_res_decrease: float = 0.0
self.received_crit_dmg_bonus: float = 0.0
self.crit_rate_received_increase: float = 0.0
self.physical_vulnerability: float = 0.0
self.fire_vulnerability: float = 0.0
self.ice_vulnerability: float = 0.0
self.electric_vulnerability: float = 0.0
self.ether_vulnerability: float = 0.0
self.anomaly_vulnerability: float = 0.0
self.all_vulnerability: float = 0.0
self.stun_res: float = 0.0
self.stun_bonus: float = 0.0
self.received_stun_increase: float = 0.0
self.stun_vulnerability_increase: float = 0.0
self.stun_vulnerability_increase_all_time: float = 0.0
self.normal_attack_stun_bonus: float = 0.0
self.special_skill_stun_bonus: float = 0.0
self.ex_special_skill_stun_bonus: float = 0.0
self.dash_attack_stun_bonus: float = 0.0
self.counter_attack_stun_bonus: float = 0.0
self.qte_stun_bonus: float = 0.0
self.ultimate_stun_bonus: float = 0.0
self.quick_aid_stun_bonus: float = 0.0
self.defensive_aid_stun_bonus: float = 0.0
self.assault_aid_stun_bonus: float = 0.0
self.physical_anomaly_buildup_bonus: float = 0.0
self.fire_anomaly_buildup_bonus: float = 0.0
self.ice_anomaly_buildup_bonus: float = 0.0
self.electric_anomaly_buildup_bonus: float = 0.0
self.ether_anomaly_buildup_bonus: float = 0.0
self.frost_anomaly_buildup_bonus: float = 0.0
self.all_anomaly_buildup_bonus: float = 0.0
self.normal_attack_anomaly_buildup_bonus: float = 0.0
self.special_skill_anomaly_buildup_bonus: float = 0.0
self.ex_special_skill_anomaly_buildup_bonus: float = 0.0
self.dash_attack_anomaly_buildup_bonus: float = 0.0
self.counter_attack_anomaly_buildup_bonus: float = 0.0
self.qte_anomaly_buildup_bonus: float = 0.0
self.ultimate_anomaly_buildup_bonus: float = 0.0
self.quick_aid_anomaly_buildup_bonus: float = 0.0
self.defensive_aid_anomaly_buildup_bonus: float = 0.0
self.assault_aid_anomaly_buildup_bonus: float = 0.0
self.assault_dmg_mul: float = 0.0
self.burn_dmg_mul: float = 0.0
self.freeze_dmg_mul: float = 0.0
self.shock_dmg_mul: float = 0.0
self.chaos_dmg_mul: float = 0.0
self.disorder_dmg_mul: float = 0.0
self.all_anomaly_dmg_mul: float = 0.0
self.special_multiplier_zone: float = 0.0
self.stun_tick_increase: int = 0
self.base_dmg_increase: float = 0.0
self.base_dmg_increase_percentage: float = 0.0
self.aftershock_attack_dmg_bonus: float = 0.0
self.aftershock_attack_crit_dmg_bonus: float = 0.0
self.aftershock_attack_stun_bonus: float = 0.0
self.assault_time_increase: float = 0.0
self.assault_time_increase_percentage: float = 0.0
self.burn_time_increase: float = 0.0
self.burn_time_increase_percentage: float = 0.0
self.shock_time_increase: float = 0.0
self.shock_time_increase_percentage: float = 0.0
self.corruption_time_increase: float = 0.0
self.corruption_time_increase_percentage: float = 0.0
self.frostbite_time_increase: float = 0.0
self.frostbite_time_increase_percentage: float = 0.0
self.frost_frostbite_time_increase: float = 0.0
self.frost_frostbite_time_increase_percentage: float = 0.0
self.all_anomaly_time_increase: float = 0.0
self.all_anomaly_time_increase_percentage: float = 0.0
self.strike_crit_rate_increase: float = 0.0
self.strike_crit_dmg_increase: float = 0.0
self.strike_ignore_defense: float = 0.0
# 异常其他属性
self.strike_crit_rate_increase: float = 0.0
self.strike_crit_dmg_increase: float = 0.0
self.strike_ignore_defense: float = 0.0
self.all_disorder_basic_mul: float = 0.0
self.strike_disorder_basic_mul: float = 0.0
self.burn_disorder_basic_mul: float = 0.0
self.frostbite_disorder_basic_mul: float = 0.0
self.shock_disorder_basic_mul: float = 0.0
self.chaos_disorder_basic_mul: float = 0.0
self.sheer_atk: float = 0.0 # 固定贯穿力增幅
self.field_sheer_atk_percentage: float = 0.0 # 局内百分比贯穿力增幅
self.sheer_dmg_bonus: float = 0.0 # 贯穿伤害增加
self.__read_dynamic_statement(dynamic_statement)
"""在更新完全部Buff效果后,再组成字典(提前组成字典会导致字典内容和后置的赋值脱钩)"""
self.ano_extra_bonus: dict[ElementType | Literal["all", -1], float] = {
0: self.assault_dmg_mul,
1: self.burn_dmg_mul,
2: self.freeze_dmg_mul,
3: self.shock_dmg_mul,
4: self.chaos_dmg_mul,
5: self.freeze_dmg_mul,
-1: self.disorder_dmg_mul,
"all": self.all_anomaly_dmg_mul,
}
self.anomaly_time_increase: dict[ElementType | Literal["all"], float] = {
0: self.assault_time_increase,
1: self.burn_time_increase,
2: self.shock_time_increase,
3: self.frostbite_time_increase,
4: self.corruption_time_increase,
5: self.frost_frostbite_time_increase,
"all": self.all_anomaly_time_increase,
}
self.anomaly_time_increase_percentage: dict[ElementType | Literal["all"], float] = {
0: self.assault_time_increase_percentage,
1: self.burn_time_increase_percentage,
2: self.shock_time_increase_percentage,
3: self.frostbite_time_increase_percentage,
4: self.corruption_time_increase_percentage,
5: self.frost_frostbite_time_increase_percentage,
"all": self.all_anomaly_time_increase_percentage,
}
self.disorder_basic_mul_map: dict[ElementType | Literal["all"], float] = {
0: self.strike_disorder_basic_mul,
1: self.burn_disorder_basic_mul,
2: self.frostbite_disorder_basic_mul,
3: self.shock_disorder_basic_mul,
4: self.chaos_disorder_basic_mul,
5: self.frostbite_disorder_basic_mul,
6: self.chaos_disorder_basic_mul,
"all": self.all_disorder_basic_mul,
}
def __read_dynamic_statement(self, dynamic_statement: dict) -> None:
"""使用翻译json初始化动态面板"""
# 打开buff_effect_trans.json
# 确保所有的属性都有默认值
# for value in buff_effect_trans.values():
# if not hasattr(self, value):
# setattr(self, value, 0.0)
# 遍历dynamic_statement,根据json翻译,设置对应的属性值
for CNkey, value in dynamic_statement.items():
if CNkey in buff_effect_trans:
attr_name = buff_effect_trans[CNkey]
setattr(self, attr_name, getattr(self, attr_name) + value)
else:
raise KeyError(f"Invalid buff multiplier key: {CNkey}")
class Calculator:
def __init__(
self,
skill_node: SkillNode,
character_obj: Character,
enemy_obj: Enemy,
dynamic_buff: dict | None = None,
):
"""
Calculator 是 Schedule 阶段获得 SkillNode 后的计算处理逻辑
当计划事件读取到 SkillNode 时,Calculator 会根据目前的角色的面板、enemy 对象、角色的动态buff,
计算出角色的直伤、异常、失衡的各乘区,并根据需求计算出输出、异常值、异常快照、失衡值
"""
if dynamic_buff is None:
dynamic_buff = {}
if not isinstance(skill_node, SkillNode):
raise ValueError("错误的参数类型,应该为SkillNode")
if not isinstance(character_obj, Character):
raise ValueError("错误的参数类型,应该为Character")
if not isinstance(enemy_obj, Enemy):
raise ValueError("错误的参数类型,应该为Enemy")
if not isinstance(dynamic_buff, dict):
raise ValueError("错误的参数类型,应该为dict")
# 创建MultiplierData对象,用于计算各种战斗中的乘区数据
data = MultiplierData(enemy_obj, dynamic_buff, character_obj, skill_node)
# 初始化角色名称和角色ID
self.char_name: str | None = data.char_name
self.cid: int | None = data.cid
self.skill_node = skill_node
assert isinstance(data.judge_node, SkillNode)
self.element_type = data.judge_node.element_type
self.skill_tag = data.judge_node.skill_tag
# 初始化各种乘区
self.regular_multipliers = self.RegularMul(data)
self.anomaly_multipliers = self.AnomalyMul(data)
self.stun_multipliers = self.StunMul(data)
# 处理失衡时间增加
self.update_stun_tick(enemy_obj, data)
class RegularMul:
"""
负责计算与储存与常规直伤有关的属性
常规直伤 = 基础伤害区 * 增伤区 * 暴击区 * 防御区 * 抗性区 * 减易伤区 * 失衡易伤区 * 特殊乘区
"""
def __init__(self, data: MultiplierData):
self.base_dmg = self.cal_base_dmg(data)
self.dmg_bonus = self.cal_dmg_bonus(data)
self.crit_rate = self.cal_crit_rate(data)
self.crit_dmg = self.cal_crit_dmg(data)
self.crit_expect = self.cal_crit_expect(data)
self.defense_mul = self.cal_defense_mul(data)
self.res_mul = self.cal_res_mul(data)
self.dmg_vulnerability = self.cal_dmg_vulnerability(data)
self.stun_vulnerability = self.cal_stun_vulnerability(data)
self.special_multiplier_zone = self.cal_special_mul(data)
self.sheer_dmg_bonus = self.cal_sheer_dmg_bonus(data)
# 常规伤害
self.regular_dmg_multipliers = {
"基础伤害区": self.base_dmg,
"增伤区": self.dmg_bonus,
"暴击率": self.crit_rate,
"暴击伤害": self.crit_dmg,
"暴击期望": self.crit_expect,
"防御区": self.defense_mul,
"抗性区": self.res_mul,
"减易伤区": self.dmg_vulnerability,
"失衡易伤区": self.stun_vulnerability,
"特殊倍率区": self.special_multiplier_zone,
"贯穿伤害区": self.sheer_dmg_bonus,
}
def get_array_expect(self) -> np.ndarray:
array_expect: np.ndarray = np.array(
[
self.base_dmg,
self.dmg_bonus,
self.crit_expect,
self.defense_mul,
self.res_mul,
self.dmg_vulnerability,
self.stun_vulnerability,
self.special_multiplier_zone,
self.sheer_dmg_bonus,
],
dtype=np.float64,
)
return array_expect
def get_array_crit(self) -> np.ndarray:
when_crit_mul = 1 + self.crit_dmg
array_crit: np.ndarray = np.array(
[
self.base_dmg,
self.dmg_bonus,
when_crit_mul,
self.defense_mul,
self.res_mul,
self.dmg_vulnerability,
self.stun_vulnerability,
self.special_multiplier_zone,
self.sheer_dmg_bonus,
],
dtype=np.float64,
)
return array_crit
def get_array_not_crit(self) -> np.ndarray:
array_no_crit: np.ndarray = np.array(
[
self.base_dmg,
self.dmg_bonus,
1,
self.defense_mul,
self.res_mul,
self.dmg_vulnerability,
self.stun_vulnerability,
self.special_multiplier_zone,
self.sheer_dmg_bonus,
],
dtype=np.float64,
)
return array_no_crit
def cal_base_dmg(self, data: MultiplierData) -> float:
"""
基础伤害区 = 伤害倍率 * 对应属性
如非特殊注明,代理人技能的伤害倍率都是基于自身攻击力的,在代理人的技能页面可以轻易查阅技能的伤害倍率,
玩家也可以在战斗中于暂停菜单中看到技能的伤害倍率。
而代理人的攻击力也可以在代理人页面或战斗中的暂停菜单中查阅,当然考虑到战斗中可能存在的各种Buff,
实时的伤害计算请关注战斗中实际的攻击力数值。
"""
assert isinstance(data.judge_node, SkillNode), "非法的调用,没有获取到skill node"
# 伤害倍率 = 技能伤害倍率 / 攻击次数
dmg_ratio = data.judge_node.skill.damage_ratio / data.judge_node.hit_times
# 获取伤害对应属性
base_attr = data.judge_node.skill.diff_multiplier
# 属性为攻击力
attr = self.cal_base_attr(base_attr, data)
base_dmg = ((dmg_ratio + data.dynamic.extra_damage_ratio) * attr) * (
1 + data.dynamic.base_dmg_increase_percentage
) + data.dynamic.base_dmg_increase
# if data.judge_node.char_name == "雅":
# print(f"雅的基础乘区为:{dmg_ratio:.2f}, 基础攻击力{data.static.atk:.2f} 局内百分比 {data.dynamic.field_atk_percentage:.2f}固定攻击力{data.dynamic.atk:.2f}")
return base_dmg
def cal_base_attr(self, base_attr: int, data: MultiplierData):
"""根据base_attr来计算对应属性的值"""
if base_attr == 0:
# 攻击力 = 局外攻击力 * 局内百分比攻击力 + 局内固定攻击力
attr = data.static.atk * (1 + data.dynamic.field_atk_percentage) + data.dynamic.atk
# 属性为生命值
elif base_attr == 1:
attr = data.static.hp * (1 + data.dynamic.field_hp_percentage) + data.dynamic.hp
# 属性为防御力
elif base_attr == 2:
attr = (
data.static.defense * (1 + data.dynamic.field_def_percentage)
+ data.dynamic.defense
)
# 属性为精通
elif base_attr == 3:
attr = (
data.static.ap * (1 + data.dynamic.field_anomaly_proficiency)
+ data.dynamic.anomaly_proficiency
)
elif base_attr == 4:
assert data.char_instance is not None
# 贯穿力属性的实时计算
if not hasattr(data.char_instance, "sheer_attack_conversion_rate"):
raise AttributeError(
f"{data.char_instance.NAME}作为命破属性代理人,必须拥有贯穿力转化字典!"
)
base_sheer_atk = 0
assert data.char_instance.sheer_attack_conversion_rate is not None
for (
key,
value,
) in data.char_instance.sheer_attack_conversion_rate.items():
if key not in [0, 1, 2, 3]:
raise ValueError(f"无法解析的贯穿力转化率key:{key}")
if value <= 0:
continue
base_sheer_atk += self.cal_base_attr(base_attr=key, data=data) * value
else:
if data.dynamic.field_sheer_atk_percentage != 0:
raise ValueError(
"警告!检测到非0的“局内贯穿力%Buff”,该效果目前还无法处理,请注意检查buff_effect"
)
current_sheer_atk = base_sheer_atk + data.dynamic.sheer_atk
attr = current_sheer_atk
# if data.dynamic.sheer_atk != 0:
# print(f"检测到 {data.char_instance.NAME} 的局内固定贯穿力Buff:{data.dynamic.sheer_atk}, 基础贯穿力:{base_sheer_atk}")
else:
raise AssertionError(INVALID_ELEMENT_ERROR)
return attr
@staticmethod
def cal_dmg_bonus(data: MultiplierData) -> float:
"""
增伤区 = 100% + 属性增伤 + 伤害类型增伤 + 进攻类型增伤 + 全类型增伤
增伤区包含游戏中各种百分比形式的伤害提升/加成,造成伤害降低同样作用于该乘区,理解为负的增伤即可。
属性增伤即针对游戏中5种伤害属性(火(Fire)、电(Electric)、冰(Ice)、物理(Physical)和以太(Ether))的伤害加成。属性增伤常见于驱动盘位的主属性和音擎效果。
伤害类型增伤包括针对于各类技能(如普通攻击,强化特殊技,终结技等)的增伤。常见于音擎效果和鸣徽效果中。
进攻类型增伤即针对于角色进攻类型(斩击(Slash)、打击(Strike)和穿透(Pierce))的增伤。全类型增伤就是未作类型限定的增伤。
"""
assert isinstance(data.judge_node, SkillNode)
element_type = data.judge_node.element_type
# 获取属性伤害加成,初始化为1.0
if element_type == 0:
element_dmg_bonus = data.static.phy_dmg_bonus + data.dynamic.phy_dmg_bonus
elif element_type == 1:
element_dmg_bonus = data.static.fire_dmg_bonus + data.dynamic.fire_dmg_bonus
elif element_type == 3:
element_dmg_bonus = data.static.electric_dmg_bonus + data.dynamic.electric_dmg_bonus
elif element_type == 2 or element_type == 5:
element_dmg_bonus = data.static.ice_dmg_bonus + data.dynamic.ice_dmg_bonus
elif element_type in [4, 6]:
element_dmg_bonus = data.static.ether_dmg_bonus + data.dynamic.ether_dmg_bonus
else:
raise ValueError(f"Invalid element type: {element_type}, must be a integer in 0~6")
# 获取指定Tag增伤
trigger_buff_level = data.judge_node.skill.trigger_buff_level
if trigger_buff_level == 0:
trigger_dmg_bonus = data.dynamic.normal_attack_dmg_bonus
elif trigger_buff_level == 1:
trigger_dmg_bonus = data.dynamic.special_skill_dmg_bonus
elif trigger_buff_level == 2:
trigger_dmg_bonus = data.dynamic.ex_special_skill_dmg_bonus
elif trigger_buff_level == 3:
trigger_dmg_bonus = data.dynamic.dash_attack_dmg_bonus
elif trigger_buff_level == 4:
trigger_dmg_bonus = data.dynamic.counter_attack_dmg_bonus
elif trigger_buff_level == 5:
trigger_dmg_bonus = data.dynamic.qte_dmg_bonus
elif trigger_buff_level == 6:
trigger_dmg_bonus = data.dynamic.ultimate_dmg_bonus
elif trigger_buff_level == 7:
trigger_dmg_bonus = data.dynamic.quick_aid_dmg_bonus
elif trigger_buff_level == 8:
trigger_dmg_bonus = data.dynamic.defensive_aid_dmg_bonus
elif trigger_buff_level == 9:
trigger_dmg_bonus = data.dynamic.assault_aid_dmg_bonus
elif trigger_buff_level == 10:
trigger_dmg_bonus = 0
else:
raise AssertionError("Invalid trigger_level")
# 获取指定label增伤
if (
data.judge_node.skill.labels is not None
and data.judge_node.skill.labels.get("aftershock_attack") == 1
):
label_dmg_bonus = data.dynamic.aftershock_attack_dmg_bonus
else:
label_dmg_bonus = 0
dmg_bonus = (
1
+ element_dmg_bonus
+ trigger_dmg_bonus
+ label_dmg_bonus
+ data.dynamic.all_dmg_bonus
)
# if "Cinema_1" in data.judge_node.skill_tag:
# print(element_dmg_bonus, trigger_dmg_bonus, label_dmg_bonus, data.dynamic.all_dmg_bonus)
# if "1291_CorePassive" in data.judge_node.skill_tag:
# print(
# f"元素类增伤:{element_dmg_bonus}, 技能类型增伤:{trigger_dmg_bonus}, 标签增伤:{label_dmg_bonus}, 全类型增伤:{data.dynamic.all_dmg_bonus}",
# )
return dmg_bonus
@staticmethod
def cal_crit_rate(data: MultiplierData) -> float:
"""暴击率 = 面板暴击率 + buff暴击率 + 受暴击概率增加"""
crit_rate = (
data.static.crit_rate
+ data.dynamic.crit_rate
+ data.dynamic.field_crit_rate
+ data.dynamic.crit_rate_received_increase
)
return crit_rate
@staticmethod
def cal_personal_crit_rate(data: MultiplierData) -> float:
"""个人实时暴击率 = 面板暴击率 + buff暴击率"""
crit_rate = (
data.static.crit_rate + data.dynamic.crit_rate + data.dynamic.field_crit_rate
)
return crit_rate
@staticmethod
def cal_crit_dmg(data: MultiplierData) -> float:
"""暴击伤害 = 静态面板暴击伤害 + buff暴击伤害 + 受暴击伤害增加"""
# 获取指定label暴伤
assert isinstance(data.judge_node, SkillNode)
if (
data.judge_node.skill.labels is not None
and data.judge_node.skill.labels.get("aftershock_attack") == 1
):
label_crit_dmg_bonus = data.dynamic.aftershock_attack_crit_dmg_bonus
else:
label_crit_dmg_bonus = 0
buff_crit_dmg_bonus = (
data.dynamic.crit_dmg + data.dynamic.field_crit_dmg + label_crit_dmg_bonus
)
crit_dmg = (
data.static.crit_damage + buff_crit_dmg_bonus + data.dynamic.received_crit_dmg_bonus
)
return min(5, crit_dmg)
def cal_crit_expect(self, data: MultiplierData) -> float:
"""暴击期望 = 1 + 暴击率 * 暴击伤害"""
if (
data.char_instance is not None
and data.char_instance.crit_balancing
and self.crit_rate > 1
):
# 目前不使用溢出补偿
return 1 + min(1, self.crit_rate) * self.crit_dmg
# 配平算法下的暴击溢出补偿,为了解决配平仅能适配静态面板的问题
# return 1 + ((self.crit_rate - 1) * 2 + self.crit_dmg)
else:
return 1 + min(1, self.crit_rate) * self.crit_dmg
@staticmethod
def cal_personal_crit_dmg(data: MultiplierData) -> float:
"""面板暴击伤害 = 静态面板暴击伤害 + buff暴击伤害"""
personal_crit_dmg = (
data.static.crit_damage + data.dynamic.crit_dmg + data.dynamic.field_crit_dmg
)
return personal_crit_dmg
def cal_defense_mul(self, data: MultiplierData) -> float:
"""
防御区 = 攻击方等级基数 / (受击方有效防御 + 攻击方等级基数)
当检测到攻击属性为4时,说明是贯穿伤害,无视防御区,所以直接返回1
受击方有效防御 = 受击方防御 * (1 - 攻击方穿透率%) - 攻击方穿透值 ≥ 0
受击方防御 = (基础防御 * (1 + 战斗外防御%) + 战斗外固定防御) * (1 + 防御加成% - 防御降低%) + 固定防御
"""
assert isinstance(data.judge_node, SkillNode)
base_attr = data.judge_node.skill.diff_multiplier
if base_attr != 4:
attacker_level: int = data.char_level if data.char_level is not None else 1
# 攻击方等级系数
k_attacker = self.cal_k_attacker(attacker_level)
# 穿透率
pen_ratio = self.cal_pen_ratio(data)
# 受击方有效防御
effective_def = self.cal_recipient_def(data, pen_ratio)
# 防御区
defense_mul = k_attacker / (effective_def + k_attacker)
else:
defense_mul = 1
return defense_mul
@staticmethod
def cal_recipient_def(
data: MultiplierData,
pen_ratio: float,
*,
addon_pen_ratio: float = 0.0,
addon_pen_numeric: float = 0.0,
) -> float:
# 受击方防御
recipient_def = (
data.enemy_obj.max_DEF * (1 - data.dynamic.percentage_def_reduction)
- data.dynamic.def_reduction
)
# 穿透值
pen_numeric: float = (
data.static.pen_numeric + data.dynamic.pen_numeric + addon_pen_numeric
)
# 受击方有效防御
effective_def: float = max(
0.0, recipient_def * (1 - pen_ratio - addon_pen_ratio) - pen_numeric
)
return effective_def
@staticmethod
def cal_pen_ratio(data: MultiplierData, *, addon_pen_ratio=0.0):
return data.static.pen_ratio + data.dynamic.pen_ratio + addon_pen_ratio
@staticmethod
def cal_k_attacker(attacker_level: int) -> int:
# 定义域检查
if attacker_level < 0:
report_to_log(f"角色等级{attacker_level}过低,将被设置为0")
attacker_level = 0
elif attacker_level > 60:
report_to_log(f"角色等级{attacker_level}过高,将被设置为60")
attacker_level = 60
# 攻击方等级系数
# fmt: off
values: list[int] = [
0, 50, 54, 58, 62, 66, 71, 76, 82, 88, 94,
100, 107, 114, 121, 129, 137, 145, 153, 162,
172, 181, 191, 201, 211, 222, 233, 245, 258,
268, 281, 293, 306, 319, 333, 347, 362, 377,
393, 409, 421, 436, 452, 469, 485, 502, 519,
537, 556, 573, 592, 612, 629, 649, 669, 689,
709, 730, 751, 772, 794,
]
# fmt: on
k_attacker = values[attacker_level]
return k_attacker
@staticmethod
def cal_res_mul(
data: MultiplierData,
*,
element_type: ElementType | None = None,
snapshot_res_pen=0,
) -> float:
"""抗性区 = 1 - 受击方抗性 + 受击方抗性降低 + 攻击方抗性穿透"""
if element_type is None:
assert isinstance(data.judge_node, SkillNode)
element_type = data.judge_node.element_type
# 获取抗性区,初始化为0
if element_type == 0:
element_res = (
data.enemy_obj.PHY_damage_resistance
- data.dynamic.physical_dmg_res_decrease
- data.dynamic.physical_res_pen_increase
)
elif element_type == 1:
element_res = (
data.enemy_obj.FIRE_damage_resistance
- data.dynamic.fire_dmg_res_decrease
- data.dynamic.fire_res_pen_increase
)
elif element_type == 2 or element_type == 5:
element_res = (
data.enemy_obj.ICE_damage_resistance
- data.dynamic.ice_dmg_res_decrease
- data.dynamic.ice_res_pen_increase
)
elif element_type == 3:
element_res = (
data.enemy_obj.ELECTRIC_damage_resistance
- data.dynamic.electric_dmg_res_decrease
- data.dynamic.electric_res_pen_increase
)
elif element_type in [4, 6]:
element_res = (
data.enemy_obj.ETHER_damage_resistance
- data.dynamic.ether_dmg_res_decrease
- data.dynamic.ether_res_pen_increase
)
else:
raise AssertionError(INVALID_ELEMENT_ERROR)
res_mul = (
1
- element_res
+ data.dynamic.all_dmg_res_decrease
+ data.dynamic.all_res_pen_increase
+ snapshot_res_pen
)
# if snapshot_res_pen == 0:
# if isinstance(data.judge_node, SkillNode) and data.judge_node.char_name == "仪玄" and data.judge_node.skill.trigger_buff_level in [2, 6]:
# print(element_res, data.dynamic.all_dmg_res_decrease, data.dynamic.all_res_pen_increase)
return res_mul
@staticmethod
def cal_dmg_vulnerability(
data: MultiplierData, *, element_type: ElementType | None = None
) -> float:
"""
减易伤区 = 1 + 减易伤
"""
if element_type is None:
assert isinstance(data.judge_node, SkillNode)
element_type = data.judge_node.element_type
# 获取抗性区,初始化为0
if element_type == 0:
element_vulnerability = data.dynamic.physical_vulnerability
elif element_type == 1:
element_vulnerability = data.dynamic.fire_vulnerability
elif element_type == 2 or element_type == 5:
element_vulnerability = data.dynamic.ice_vulnerability
elif element_type == 3:
element_vulnerability = data.dynamic.electric_vulnerability
elif element_type in [4, 6]:
element_vulnerability = data.dynamic.ether_vulnerability
else:
raise AssertionError(INVALID_ELEMENT_ERROR)
dmg_vulnerability = 1 + element_vulnerability + data.dynamic.all_vulnerability
return dmg_vulnerability
@staticmethod
def cal_stun_vulnerability(data: MultiplierData) -> float:
"""
失衡时:失衡易伤区 = 1 + 怪物失衡易伤 + 失衡易伤增幅 + 全时段失衡易伤(扳机核心被动那种)
非失衡时:失衡易伤区 = 1 + 全时段失衡易伤(扳机核心被动那种)
"""
stun_status: bool = data.enemy_obj.dynamic.stun
if stun_status:
stun_vulnerability = (
1
+ data.enemy_obj.stun_DMG_take_ratio
+ data.dynamic.stun_vulnerability_increase
+ data.dynamic.stun_vulnerability_increase_all_time
)
else:
stun_vulnerability = 1 + data.dynamic.stun_vulnerability_increase_all_time
return stun_vulnerability
@staticmethod
def cal_special_mul(data: MultiplierData) -> float:
return 1 + data.dynamic.special_multiplier_zone
@staticmethod
def cal_sheer_dmg_bonus(data: MultiplierData) -> float:
"""计算贯穿伤害增幅区——贯穿伤害增加是一个独立乘区!"""
assert isinstance(data.judge_node, SkillNode)
if data.judge_node.skill.diff_multiplier != 4:
return 1.0
else:
return 1 + data.dynamic.sheer_dmg_bonus
class AnomalyMul:
"""
负责计算与储存与异常伤害有关的属性
异常伤害快照以 array 形式储存,顺序为:
[基础伤害区、增伤区、异常精通区、等级、异常增伤区、异常暴击区、穿透率、穿透值、抗性穿透]
异常积蓄值 = 基础积蓄值 * 异常掌控/100 * (1 + 属性异常积蓄效率提升) * (1 - 属性异常积蓄抗性)
基础伤害区 = 攻击力 * 对应属性的异常伤害倍率
增伤区 = 1 + 属性增伤 + 全增伤
异常精通区 = 异常精通 / 100
等级 = 角色等级
异常增伤区 = 单独异常增伤
异常暴击区 单独考虑简一个角色
"""
def __init__(self, data: MultiplierData):
assert isinstance(data.judge_node, SkillNode)
self.element_type: ElementType = data.judge_node.element_type
self.anomaly_buildup: np.float64 = self.cal_anomaly_buildup(data)
self.base_damage: float = self.cal_base_damage(data)
self.dmg_bonus: float = self.cal_dmg_bonus(data)
self.ap_mul: float = self.cal_ap_mul(data)
self.level: int = data.char_level if data.char_level is not None else 0
self.anomaly_bonus: float = self.cal_ano_extra_mul(data)
self.anomaly_crit: float = self.cal_anomaly_crit(data)
self.pen_ratio: float = data.static.pen_ratio + data.dynamic.pen_ratio
self.pen_numeric: float = data.static.pen_numeric + data.dynamic.pen_numeric
self.res_pen: float = self.cal_res_pen(data)
self.anomaly_snapshot = np.array(
[
self.base_damage,
self.dmg_bonus,
self.ap_mul,
self.level,
self.anomaly_bonus,
self.anomaly_crit,
self.pen_ratio,
self.pen_numeric,
self.res_pen,
],
dtype=np.float64,
)
@staticmethod
def cal_am(data: MultiplierData) -> np.float64:
am = np.float64(
data.static.am * (1 + data.dynamic.field_anomaly_mastery)
+ data.dynamic.anomaly_mastery
)
return am
@staticmethod
def cal_anomaly_buildup(data: MultiplierData) -> np.float64:
"""异常积蓄值 = 基础积蓄值 * 异常掌控/100 * (1 + 属性异常积蓄效率提升) * (1 - 属性异常积蓄抗性)"""
# 基础蓄积值
assert isinstance(data.judge_node, SkillNode)
accumulation = data.judge_node.skill.anomaly_accumulation
# 异常掌控
am = Calculator.AnomalyMul.cal_am(data)
# 属性异常积蓄效率提升、属性异常积蓄抗性
element_type = data.judge_node.element_type
enemy_buildup_res = data.enemy_obj.anomaly_resistance_dict.get(element_type, 0.0)
if element_type == 0:
element_buildup_bonus = (
data.dynamic.physical_anomaly_buildup_bonus
+ data.dynamic.all_anomaly_buildup_bonus
)
buildup_res = 1 - data.dynamic.physical_anomaly_res_decrease - enemy_buildup_res
elif element_type == 1:
element_buildup_bonus = (
data.dynamic.fire_anomaly_buildup_bonus + data.dynamic.all_anomaly_buildup_bonus
)
buildup_res = 1 - data.dynamic.fire_anomaly_res_decrease - enemy_buildup_res
elif element_type == 2:
element_buildup_bonus = (
data.dynamic.ice_anomaly_buildup_bonus + data.dynamic.all_anomaly_buildup_bonus
)
buildup_res = 1 - data.dynamic.ice_anomaly_res_decrease - enemy_buildup_res
elif element_type == 3:
element_buildup_bonus = (
data.dynamic.electric_anomaly_buildup_bonus
+ data.dynamic.all_anomaly_buildup_bonus
)
buildup_res = 1 - data.dynamic.electric_anomaly_res_decrease - enemy_buildup_res
elif element_type in [4, 6]:
element_buildup_bonus = (
data.dynamic.ether_anomaly_buildup_bonus
+ data.dynamic.all_anomaly_buildup_bonus
)
buildup_res = 1 - data.dynamic.ether_anomaly_res_decrease - enemy_buildup_res
elif element_type == 5:
element_buildup_bonus = (
data.dynamic.frost_anomaly_buildup_bonus
+ data.dynamic.all_anomaly_buildup_bonus
)
buildup_res = 1 - data.dynamic.ice_anomaly_res_decrease - enemy_buildup_res
else:
raise AssertionError(INVALID_ELEMENT_ERROR)
trigger_buff_level = data.judge_node.skill.trigger_buff_level
if trigger_buff_level == 0:
trigger_buildup_bonus = data.dynamic.normal_attack_anomaly_buildup_bonus
elif trigger_buff_level == 1:
trigger_buildup_bonus = data.dynamic.special_skill_anomaly_buildup_bonus
elif trigger_buff_level == 2:
trigger_buildup_bonus = data.dynamic.ex_special_skill_anomaly_buildup_bonus
elif trigger_buff_level == 3:
trigger_buildup_bonus = data.dynamic.dash_attack_anomaly_buildup_bonus
elif trigger_buff_level == 4:
trigger_buildup_bonus = data.dynamic.counter_attack_anomaly_buildup_bonus
elif trigger_buff_level == 5:
trigger_buildup_bonus = data.dynamic.qte_anomaly_buildup_bonus
elif trigger_buff_level == 6:
trigger_buildup_bonus = data.dynamic.ultimate_anomaly_buildup_bonus
elif trigger_buff_level == 7:
trigger_buildup_bonus = data.dynamic.quick_aid_anomaly_buildup_bonus
elif trigger_buff_level == 8:
trigger_buildup_bonus = data.dynamic.defensive_aid_anomaly_buildup_bonus
elif trigger_buff_level == 9:
trigger_buildup_bonus = data.dynamic.assault_aid_anomaly_buildup_bonus
elif trigger_buff_level == 10:
trigger_buildup_bonus = 0
else:
raise AssertionError(INVALID_ELEMENT_ERROR)
element_dmg_percentage = data.judge_node.skill.element_damage_percent
hit_times = data.judge_node.hit_times
anomaly_buildup = (
accumulation
* (am / 100)
* (1 + element_buildup_bonus + trigger_buildup_bonus)
* buildup_res
* element_dmg_percentage
/ hit_times
)
return np.float64(anomaly_buildup)
@staticmethod
def cal_base_damage(data: MultiplierData) -> float:
"""基础伤害区 = 攻击力 * 对应属性的异常伤害倍率"""
base_attr = data.static.atk * (1 + data.dynamic.field_atk_percentage) + data.dynamic.atk
assert isinstance(data.judge_node, SkillNode)
element_type = data.judge_node.element_type
if element_type == 0:
base_damage = 7.13 * base_attr
elif element_type == 1:
base_damage = 0.5 * base_attr
elif element_type == 2 or element_type == 5:
base_damage = 5 * base_attr
elif element_type == 3:
base_damage = 1.25 * base_attr
elif element_type in [4, 6]:
base_damage = 0.625 * base_attr
else:
raise AssertionError(INVALID_ELEMENT_ERROR)
return base_damage
@staticmethod
def cal_dmg_bonus(data: MultiplierData) -> float:
"""增伤区 = 1 + 属性增伤 + 全增伤"""
assert isinstance(data.judge_node, SkillNode)
element_type = data.judge_node.element_type
if element_type == 0:
element_dmg_bonus = data.static.phy_dmg_bonus + data.dynamic.phy_dmg_bonus
elif element_type == 1:
element_dmg_bonus = data.static.fire_dmg_bonus + data.dynamic.fire_dmg_bonus
elif element_type == 2 or element_type == 5:
element_dmg_bonus = data.static.ice_dmg_bonus + data.dynamic.ice_dmg_bonus
elif element_type == 3:
element_dmg_bonus = data.static.electric_dmg_bonus + data.dynamic.electric_dmg_bonus
elif element_type in [4, 6]:
element_dmg_bonus = data.static.ether_dmg_bonus + data.dynamic.ether_dmg_bonus
else:
raise AssertionError(INVALID_ELEMENT_ERROR)
dmg_bonus = (
1 + element_dmg_bonus + data.dynamic.all_dmg_bonus + data.dynamic.anomaly_dmg_bonus
)
return dmg_bonus
def cal_ap_mul(self, data: MultiplierData) -> float:
"""异常精通区 = 异常精通 / 100"""
ap = self.cal_ap(data)
ap_mul = ap / 100
return ap_mul
@staticmethod
@lru_cache(maxsize=16)
def cal_ap(data: MultiplierData):
ap = (
data.static.ap * (1 + data.dynamic.field_anomaly_proficiency)
+ data.dynamic.anomaly_proficiency
)
return ap
@staticmethod
def cal_ano_extra_mul(data: MultiplierData) -> float:
"""异常额外增伤区 = 1 + 对应属性异常额外增伤"""
assert isinstance(data.judge_node, SkillNode)
element_type = data.judge_node.element_type
map = data.dynamic.ano_extra_bonus
ano_dmg_mul = 1 + map.get(element_type, 0) + map["all"]
return ano_dmg_mul
def cal_anomaly_crit(self, data: MultiplierData) -> float:
"""已弃用,避免大范围重构数据类型,保留一个1"""
return 1
def cal_res_pen(self, data: MultiplierData) -> float:
assert isinstance(data.judge_node, SkillNode)
element_type = data.judge_node.element_type
if element_type == 0:
element_res_pen = data.dynamic.physical_res_pen_increase
elif element_type == 1:
element_res_pen = data.dynamic.fire_res_pen_increase
elif element_type == 2 or element_type == 5:
element_res_pen = data.dynamic.ice_res_pen_increase
elif element_type == 3:
element_res_pen = data.dynamic.electric_res_pen_increase
elif element_type in [4, 6]:
element_res_pen = data.dynamic.ether_res_pen_increase
else:
raise AssertionError(INVALID_ELEMENT_ERROR)
return element_res_pen
class StunMul:
"""
负责计算与储存与失衡值有关的属性,并负责与enemy互相
失衡值累积 = 冲击力 * 失衡倍率 * (1 - 失衡抗性) * (1 + 失衡值提升 - 失衡值降低) * (1+ 受到失衡值提升 - 受到失衡值降低)
"""
def __init__(self, data: MultiplierData):
assert isinstance(data.judge_node, SkillNode)
self.element_type: ElementType = data.judge_node.element_type
self.imp = self.cal_imp(data)
self.stun_ratio = self.cal_stun_ratio(data)
self.stun_res = self.cal_stun_res(data, self.element_type)
self.stun_bonus = self.cal_stun_bonus(data)
self.stun_received = self.cal_stun_received(data)
def get_stun_array(self) -> np.ndarray:
stun_array = np.array(
[
self.imp,
self.stun_ratio,
self.stun_res,
self.stun_bonus,
self.stun_received,
],
dtype=np.float64,
)
return stun_array
@staticmethod
def cal_imp(data: MultiplierData) -> float:
imp = data.static.imp * (1 + data.dynamic.field_imp_percentage) + data.dynamic.imp
return imp
@staticmethod
def cal_stun_ratio(data: MultiplierData) -> float:
assert isinstance(data.judge_node, SkillNode)
stun_ratio = data.judge_node.skill.stun_ratio / data.judge_node.hit_times
return stun_ratio
@staticmethod
def cal_stun_res(
data: MultiplierData, element_type: ElementType, *, over_stun_res: float = 0
) -> float:
enemy_stun_res = data.enemy_obj.stun_resistance_dict.get(element_type, 0.0)
stun_res = 1 - data.dynamic.stun_res - over_stun_res - enemy_stun_res
return stun_res
@staticmethod
def cal_stun_bonus(data: MultiplierData) -> float:
# 获取指定label失衡值增加
assert isinstance(data.judge_node, SkillNode)
if (
data.judge_node.skill.labels is not None
and data.judge_node.skill.labels.get("aftershock_attack") == 1
):
label_stun_bonus = data.dynamic.aftershock_attack_stun_bonus
else:
label_stun_bonus = 0
# 接下来计算标签类失衡值
tbl = data.judge_node.skill.trigger_buff_level
if tbl == 0:
stun_bonus_tbl = data.dynamic.normal_attack_stun_bonus
elif tbl == 1:
stun_bonus_tbl = data.dynamic.special_skill_stun_bonus
elif tbl == 2:
stun_bonus_tbl = data.dynamic.ex_special_skill_stun_bonus
elif tbl == 3:
stun_bonus_tbl = data.dynamic.dash_attack_stun_bonus
elif tbl == 4:
stun_bonus_tbl = data.dynamic.counter_attack_stun_bonus
elif tbl == 5:
stun_bonus_tbl = data.dynamic.qte_stun_bonus
elif tbl == 6:
stun_bonus_tbl = data.dynamic.ultimate_stun_bonus
elif tbl == 7:
stun_bonus_tbl = data.dynamic.quick_aid_stun_bonus
elif tbl == 8:
stun_bonus_tbl = data.dynamic.defensive_aid_stun_bonus
elif tbl == 9:
stun_bonus_tbl = data.dynamic.assault_aid_stun_bonus
elif tbl == 10:
stun_bonus_tbl = 0
else:
raise ValueError(
f"{data.judge_node.skill_tag}的trigger_buff_level为{tbl},无法解析!"
)
all_stun_bonus = data.dynamic.stun_bonus # 全品类失衡增幅
stun_bonus = 1 + stun_bonus_tbl + all_stun_bonus + label_stun_bonus
return stun_bonus
@staticmethod
def cal_stun_received(data: MultiplierData, over_stun_received: float = 0) -> float:
stun_received = 1 + data.dynamic.received_stun_increase + over_stun_received
return stun_received
def cal_dmg_expect(self) -> np.float64:
"""计算伤害期望"""
multipliers: np.ndarray = self.regular_multipliers.get_array_expect()
dmg_expect = np.prod(multipliers)
self.check_skill_node_mul(multipliers)
return np.float64(dmg_expect)
def check_skill_node_mul(self, multipliers):
"""检查技能节点的乘区"""
if not CHECK_SKILL_MUL:
return
if any([__tag in self.skill_tag for __tag in CHECK_SKILL_MUL_TAG]):
tag_list = [
"基础乘区",
"增伤区",
"双暴区",
"防御区",
"抗性区",
"易伤区",
"失衡易伤区",
"特殊乘区",
"贯穿伤害区",
]
print(
self.skill_node.skill.skill_text,
f"第{self.skill_node.loading_mission.hitted_count if self.skill_node.loading_mission else 1}次命中",
":",
[
f"{__tag} : {__value:.2f}"
for __tag, __value in zip(tag_list, multipliers, strict=True)
],
)
def cal_dmg_crit(self) -> np.float64:
"""计算暴击伤害"""
multipliers: np.ndarray = self.regular_multipliers.get_array_crit()
dmg_crit = np.prod(multipliers)
return np.float64(dmg_crit)
def cal_dmg_not_crit(self) -> np.float64:
"""计算非暴击伤害"""
multipliers: np.ndarray = self.regular_multipliers.get_array_not_crit()
dmg_not_crit = np.prod(multipliers)
return np.float64(dmg_not_crit)
def cal_snapshot(self) -> tuple[int, np.float64, np.ndarray]:
"""计算异常值与失衡值快照,返回一个一维数组,用于计算异常伤害的虚拟角色,鬼知道为什么那么麻烦"""
element_type: int = self.element_type
build_up: np.float64 = self.anomaly_multipliers.anomaly_buildup
anomaly_snapshot: np.ndarray = self.anomaly_multipliers.anomaly_snapshot
stun_snapshot: np.ndarray = np.array(
[self.stun_multipliers.imp, self.stun_multipliers.stun_bonus]
)
snapshot = np.concatenate((anomaly_snapshot, stun_snapshot))
return element_type, build_up, snapshot
def cal_stun(self) -> np.float64:
"""计算失衡值"""
multipliers: np.ndarray = self.stun_multipliers.get_stun_array()
stun = np.prod(multipliers)
return np.float64(stun)
@staticmethod
def update_stun_tick(enemy_obj: Enemy, data: MultiplierData):
"""专门更新延长失衡时间的 buff"""
if data.dynamic.stun_tick_increase >= 1:
enemy_obj.increase_stun_recovery_time(data.dynamic.stun_tick_increase)
# TODO:当前动作是否能够被打断的计算。
# 技能自身有抗打断系数,但是考虑到凯撒之类的抗打断Buff的存在,所以需要在Schedule阶段进行一个全局的计算,
# 在检测到Preload阶段抛出的“怪物进攻”信息后(当前Preload还没有这个功能,应该在这里留好接口),Schedule调取当前动作对应的基础抗打断值,
# 并且从buff系统中读取抗打断加成,最后返回bool值,表示当前动作是否能够被打断。
# TODO:Preload与Schedule阶段在打断功能上的交互与后续设计:
# ①将打断模块放在Schedule的原因:
# 在同一个tick中,理论上说,打断事件在Preload阶段处理或是Schedule阶段处理是一样的,
# 在Schedule阶段处理的好处更多,因为当前tick触发的buff也会被纳入考量。
# 这样可以避免诸多类似于“某添加抗打断buff、但自身不抗打断的技能,在start标签附近被打断”的特殊情况发生
# ②后续设计:
# Schedule阶段在完成判定后,应该第一时间更新一个全局的、或是Preload内部能够读取到的一个指定数据结构
# (该数据结构粗看下来,布尔值应该就能满足要求,但是后续如果还有精确到tick的要求,那可能会变成字典。)
# 而在下一个Tick,Preload会根据这个数据结构中的内容来判断“刚刚那个tick的动作是否被打断了”,
# 这将影响到Preload抛出的是正常主动动作,还是“被打断”的空动作。
if __name__ == "__main__":
pass
================================================
FILE: zsim/sim_progress/ScheduledEvent/__init__.py
================================================
from __future__ import annotations
import logging
from typing import TYPE_CHECKING, Any
from zsim.sim_progress import Buff
from zsim.sim_progress.Character import Character
from zsim.sim_progress.data_struct import (
ActionStack,
PolarizedAssaultEvent,
QuickAssistEvent,
SchedulePreload,
SPUpdateData,
)
from zsim.sim_progress.Load.loading_mission import LoadingMission
from zsim.sim_progress.Preload import SkillNode
from zsim.sim_progress.Update import update_anomaly
from .event_handlers import EventContext, event_handler_factory, register_all_handlers
if TYPE_CHECKING:
from zsim.simulator.dataclasses import ScheduleData
from zsim.simulator.simulator_class import Simulator
class ScConditionData:
"""
用于记录在本tick可能的判断 buff 数据,以方便后续计算伤害
"""
def __init__(self):
self.buff_list: list = []
self.when_crit: bool = False
class ScheduledEvent:
"""
计划事件方法类
主逻辑链 self.event_start():
1、读取计划事件列表,将其中所有的buff示例排到列表最靠前的位置。self.sort_events()
2、遍历事件列表,从开始到结束,将每一个事件派发到分支逻辑链内进行处理
"""
def __init__(
self,
dynamic_buff: dict,
data,
tick: int,
exist_buff_dict: dict,
action_stack: ActionStack,
*,
loading_buff: dict | None = None,
sim_instance: Simulator,
):
self.data: "ScheduleData" = data
self.data.dynamic_buff = dynamic_buff
self.data.processed_times = 0
# self.judge_required_info_dict = data.judge_required_info_dict
self.action_stack = action_stack
if loading_buff is None:
loading_buff = {}
elif not isinstance(loading_buff, dict):
raise ValueError(f"loading_buff参数必须为字典,但你输入了{loading_buff}")
if not isinstance(tick, int):
raise ValueError(f"tick参数必须为整数,但你输入了{tick}")
# 更新Data
self.tick = tick
self.data.loading_buff = loading_buff
self.exist_buff_dict = exist_buff_dict
self.enemy = self.data.enemy
self.execute_tick_key_map = {
SkillNode: "preload_tick",
QuickAssistEvent: "execute_tick",
SchedulePreload: "execute_tick",
PolarizedAssaultEvent: "execute_tick",
}
self.sim_instance: Simulator = sim_instance
# 确保事件处理器已注册
self._ensure_handlers_registered()
def _ensure_handlers_registered(self) -> None:
"""确保所有事件处理器已注册"""
if not event_handler_factory.list_handlers():
register_all_handlers()
logging.info("事件处理器注册完成")
else:
logging.debug("事件处理器已经注册")
def _create_event_context(self) -> EventContext:
"""
创建事件处理上下文
Returns:
EventContext: 包含事件处理所需数据的上下文对象
"""
return EventContext(
data=self.data,
tick=self.tick,
enemy=self.enemy,
dynamic_buff=self.data.dynamic_buff,
exist_buff_dict=self.exist_buff_dict,
action_stack=self.action_stack,
sim_instance=self.sim_instance,
)
def event_start(self):
"""Schedule主逻辑"""
# 更新角色面板
for char in self.data.char_obj_list:
char: Character
sp_update_data = SPUpdateData(char_obj=char, dynamic_buff=self.data.dynamic_buff)
char.update_sp_and_decibel(sp_update_data)
if hasattr(char, "refresh_myself"):
char.refresh_myself()
self.process_event()
def process_event(self):
"""
处理当前所有事件
使用事件处理器模式来处理各种类型的事件,替代原有的大型if-elif链。
提高代码的可读性和可维护性。
"""
if not self.data.event_list:
return
# 先处理优先级高的buff
self.solve_buff()
# 筛选出可处理的事件,并按照优先级排序
processable_events = self.select_processable_event()
# 使用事件处理器处理事件
for event in processable_events:
try:
self._process_single_event(event)
# 事件处理完毕,从列表中移除
self.data.event_list.remove(event)
self.data.processed_times += 1
except Exception as e:
raise RuntimeError(f"处理事件 {type(event)} 时发生错误: {e}") from e
# 如果计算过程中又有新的事件生成,则继续循环
if self.data.event_list and not self.check_all_event():
self.process_event()
def _process_single_event(self, event: Any) -> None:
"""
处理单个事件
Args:
event: 待处理的事件对象
Raises:
NotImplementedError: 当事件类型不被支持时
RuntimeError: 当事件处理器无法找到时
Exception: 事件处理过程中的其他错误
"""
# 特殊处理Buff事件
if isinstance(event, Buff.Buff):
raise NotImplementedError(f"{type(event)},目前不应存在于 event_list")
# 事件处理上下文
context = self._create_event_context()
# 获取事件处理器
handler = event_handler_factory.get_handler(event)
if handler is None:
error_msg = f"无法找到适合处理事件类型 {type(event)} 的处理器"
logging.error(error_msg)
logging.debug(f"可用的事件处理器: {event_handler_factory.list_handlers()}")
raise RuntimeError(error_msg)
# 处理事件
try:
handler.handle(event, context)
except Exception as e:
logging.error(f"处理事件 {type(event).__name__} 时发生错误: {e}", exc_info=True)
raise
def check_all_event(self):
"""检查所有残留事件是否到期,只要有一个残留事件已经到期,直接返回False,激活递归。"""
for event in self.data.event_list:
# 获取事件类型对应的tick属性名
execute_tick = self.get_execute_tick(event)
if execute_tick is None:
return False
if execute_tick > self.tick: # 严格大于当前tick才视为未到期
continue
else:
return False
return True
def get_execute_tick(self, event) -> int | None:
"""获取事件的执行tick,获取不到则返回None"""
tick_attr = self.execute_tick_key_map.get(type(event), None)
if tick_attr is None:
"""获取不到属性时,说明该event并不具备计划事件的需求,所以这种事件是必须在当前tick被清空的,直接返回None"""
return None
execute_tick = getattr(event, tick_attr, None)
if execute_tick is None:
raise AttributeError(f"{type(event)} 没有属性 {tick_attr}")
return execute_tick
def update_anomaly_bar_after_skill_event(self, event):
"""在Schedule阶段,处理完一个SkillEvent后,都要进行一次异常条更新。"""
"""
将异常值更新移动到Schedule阶段的主要原因:原有的Buff更新、异常/紊乱结算的顺序不合理;
原有顺序:
Preload -> Load -> update_anomaly() -> Buff(第一轮) -> Schedule -> Buff(第二轮)
现有顺序:
Preload -> Load -> Buff(第一轮) -> Schedule -> update_anomaly() -> Buff(第二轮)
由于update_anomaly()函数是根据现有积蓄值来判断是否触发属性异常的,
所以在运行过程中,只有先把积蓄值打满的下一次update_anomaly()才会触发属性异常,
无论是哪种结构,enemy的receive_hit()函数都会在Schedule阶段执行,
故任何早于Schedule阶段的update_anomaly都只能更新到上个tick的属性异常,
所以,原有结构中,第Ntick打满的异常条,会在第N+1 tick被激活,
一般情况下,这种迟滞1tick的激活行为不会对模拟的结果造成影响,
(长难句警告!!)--但若是某个Buff事件的激活 依赖于发生在 技能last_hit标签处的属性异常更新--
那么在老的结构下,事件的更新顺序为
--(第N Tick)--
-> update_anomaly(此时的异常条还没打满[来自于上个tick]所以第Ntick的运行无结果)
-> Buff事件触发器检测(异常条更新状态没有改变,所以触发器不触发)
-> Schedule,异常条满,
--(第N+1 Tick)--
-> update_anomaly(异常条满,更新异常)
-> Buff事件触发器检测(已经错过了触发窗口,所以触发器不触发)
而在新的结构下,事件更新顺序为:
--(第N Tick)--
-> Schedule,异常条满,
-> update_anomaly(异常条满,更新异常)
-> Buff事件触发器检测(将该Buff改为Schedule处理类型)
上述结构的改变就能够彻底规避来自于结构的触发误差——来自柳极性紊乱触发器的启发
"""
if isinstance(event, SkillNode):
_node = event
elif isinstance(event, LoadingMission):
_node = event.mission_node
else:
raise TypeError("无法解析的事件类型")
"""接下来要通过技能的异常更新特性,判断当前Tick的技能是否能够更新异常
由于调用函数的位置是ScheduleEvent,所以一定是Hit事件发生时,
所以,直接调用loading_mission.hitted_count数量就可以获得当前正在被结算的Hit次数。"""
should_update = False
if not _node.skill.anomaly_update_rule:
if _node.loading_mission is None:
_loading_mission = LoadingMission(_node)
_loading_mission.mission_start(timenow=self.sim_instance.tick)
_node.loading_mission = _loading_mission
last_hit = _node.loading_mission.get_last_hit()
if last_hit is not None and self.tick - 1 < last_hit <= self.tick:
should_update = True
else:
if _node.skill.anomaly_update_rule == -1:
should_update = True
else:
if (
_node.loading_mission is not None
and _node.skill.anomaly_update_rule is not None
and (
isinstance(_node.skill.anomaly_update_rule, list)
and _node.loading_mission.hitted_count in _node.skill.anomaly_update_rule
or isinstance(_node.skill.anomaly_update_rule, int)
and _node.loading_mission.hitted_count == _node.skill.anomaly_update_rule
)
):
should_update = True
if should_update:
update_anomaly(
_node.element_type,
self.enemy,
self.tick,
self.data.event_list,
self.data.char_obj_list,
skill_node=_node,
dynamic_buff_dict=self.data.dynamic_buff,
sim_instance=self.sim_instance,
)
def solve_buff(self) -> None:
"""提前处理Buff实例"""
# Buff.buff_add(
# self.tick, self.data.loading_buff, self.data.dynamic_buff, self.data.enemy
# )
buff_events = []
other_events = []
for event in self.data.event_list[:]:
if isinstance(event, Buff.Buff):
buff_events.append(event)
else:
other_events.append(event)
self.data.event_list = buff_events + other_events
def select_processable_event(self):
"""筛选当前可执行的事件,并且按照优先级排序,获取不到优先级的默认为0,"""
_output_event_list = []
for _event in self.data.event_list:
execute_tick = self.get_execute_tick(_event)
if execute_tick is None or execute_tick <= self.tick:
"""说明事件不存在execute_tick或已到期,需要被立刻执行。"""
schedule_priority = getattr(_event, "schedule_priority", 0)
# 使用bisect模块进行高效插入
import bisect
priorities = [getattr(e, "schedule_priority", 0) for e in _output_event_list]
insert_pos = bisect.bisect_right(priorities, schedule_priority)
_output_event_list.insert(insert_pos, _event)
return _output_event_list
if __name__ == "__main__":
pass
================================================
FILE: zsim/sim_progress/ScheduledEvent/buff_effect_trans.json
================================================
{
"固定生命值": "hp",
"固定攻击力": "atk",
"固定防御力": "defense",
"固定冲击力": "imp",
"固定暴击率": "crit_rate",
"固定暴击伤害": "crit_dmg",
"固定异常精通": "anomaly_proficiency",
"固定异常掌控": "anomaly_mastery",
"穿透率": "pen_ratio",
"穿透值": "pen_numeric",
"能量自动恢复": "sp_regen",
"能量获得效率": "sp_get_ratio",
"能量上限": "sp_limit",
"物理属性伤害": "phy_dmg_bonus",
"火属性伤害": "fire_dmg_bonus",
"冰属性伤害": "ice_dmg_bonus",
"电属性伤害": "electric_dmg_bonus",
"以太属性伤害": "ether_dmg_bonus",
"局内生命值%": "field_hp_percentage",
"局内攻击力%": "field_atk_percentage",
"局内防御力%": "field_def_percentage",
"局内冲击力%": "field_imp_percentage",
"局内暴击率": "field_crit_rate",
"局内暴击伤害": "field_crit_dmg",
"局内异常精通": "field_anomaly_proficiency",
"局内异常掌控": "field_anomaly_mastery",
"局内穿透率": "field_pen_ratio",
"局内穿透值": "field_pen_numeric",
"局内能量自动恢复": "sp_regen",
"局内能量获得效率": "sp_get_ratio",
"局内能量上限": "field_sp_limit",
"额外伤害倍率": "extra_damage_ratio",
"固定贯穿力": "sheer_atk",
"局内贯穿力%": "field_sheer_atk_percentage",
"喧响获得效率": "decibel_get_ratio",
"物理属性暴击伤害": "phy_crit_dmg_bonus",
"火属性暴击伤害": "fire_crit_dmg_bonus",
"冰属性暴击伤害": "ice_crit_dmg_bonus",
"电属性暴击伤害": "electric_crit_dmg_bonus",
"以太属性暴击伤害": "ether_crit_dmg_bonus",
"物理属性暴击率": "phy_crit_rate_bonus",
"火属性暴击率": "fire_crit_rate_bonus",
"冰属性暴击率": "ice_crit_rate_bonus",
"电属性暴击率": "electric_crit_rate_bonus",
"以太属性暴击率": "ether_crit_rate_bonus",
"攻击类型增伤": "attack_type_dmg_bonus",
"普攻增伤": "normal_attack_dmg_bonus",
"特殊技增伤": "special_skill_dmg_bonus",
"强化特殊技增伤": "ex_special_skill_dmg_bonus",
"冲刺攻击增伤": "dash_attack_dmg_bonus",
"闪避反击增伤": "counter_attack_dmg_bonus",
"连携技增伤": "qte_dmg_bonus",
"终结技增伤": "ultimate_dmg_bonus",
"快速支援增伤": "quick_aid_dmg_bonus",
"招架支援增伤": "defensive_aid_dmg_bonus",
"支援突击增伤": "assault_aid_dmg_bonus",
"属性异常增伤": "anomaly_dmg_bonus",
"全增伤": "all_dmg_bonus",
"百分比减防": "percentage_def_reduction",
"固定减防": "def_reduction",
"全属性伤害抗性降低": "all_dmg_res_decrease",
"物理伤害抗性降低": "physical_dmg_res_decrease",
"火伤害抗性降低": "fire_dmg_res_decrease",
"冰伤害抗性降低": "ice_dmg_res_decrease",
"电伤害抗性降低": "electric_dmg_res_decrease",
"以太伤害抗性降低": "ether_dmg_res_decrease",
"全属性抗性穿透": "all_res_pen_increase",
"物理抗性穿透": "physical_res_pen_increase",
"火抗性穿透": "fire_res_pen_increase",
"冰抗性穿透": "ice_res_pen_increase",
"电抗性穿透": "electric_res_pen_increase",
"以太抗性穿透": "ether_res_pen_increase",
"全属性异常抗性降低": "all_anomaly_res_decrease",
"物理异常抗性降低": "physical_anomaly_res_decrease",
"火异常抗性降低": "fire_anomaly_res_decrease",
"冰异常抗性降低": "ice_anomaly_res_decrease",
"电异常抗性降低": "electric_anomaly_res_decrease",
"以太异常抗性降低": "ether_anomaly_res_decrease",
"受暴击伤害增加": "received_crit_dmg_bonus",
"被暴击几率增加": "crit_rate_received_increase",
"物理易伤": "physical_vulnerability",
"火易伤": "fire_vulnerability",
"冰易伤": "ice_vulnerability",
"电易伤": "electric_vulnerability",
"以太易伤": "ether_vulnerability",
"异常易伤": "anomaly_vulnerability",
"全易伤": "all_vulnerability",
"失衡抗性": "stun_res",
"失衡增幅": "stun_bonus",
"受失衡增加": "received_stun_increase",
"失衡易伤增加": "stun_vulnerability_increase",
"全时段失衡易伤增加": "stun_vulnerability_increase_all_time",
"普攻失衡值增加": "normal_attack_stun_bonus",
"特殊技失衡值增加": "special_skill_stun_bonus",
"强化特殊技失衡值增加": "ex_special_skill_stun_bonus",
"冲刺攻击失衡值增加": "dash_attack_stun_bonus",
"闪避反击失衡值增加": "counter_attack_stun_bonus",
"连携技失衡值增加": "qte_stun_bonus",
"终结技失衡值增加": "ultimate_stun_bonus",
"快速支援失衡值增加": "quick_aid_stun_bonus",
"招架支援失衡值增加": "defensive_aid_stun_bonus",
"支援突击失衡值增加": "assault_aid_stun_bonus",
"物理积蓄效率增加": "physical_anomaly_buildup_bonus",
"火积蓄效率增加": "fire_anomaly_buildup_bonus",
"冰积蓄效率增加": "ice_anomaly_buildup_bonus",
"电积蓄效率增加": "electric_anomaly_buildup_bonus",
"以太积蓄效率增加": "ether_anomaly_buildup_bonus",
"烈霜积蓄效率增加": "frost_anomaly_buildup_bonus",
"全积蓄效率增加": "all_anomaly_buildup_bonus",
"普攻积蓄效率增加": "normal_attack_anomaly_buildup_bonus",
"特殊技积蓄效率增加": "special_skill_anomaly_buildup_bonus",
"强化特殊技积蓄效率增加": "ex_special_skill_anomaly_buildup_bonus",
"冲刺攻击积蓄效率增加": "dash_attack_anomaly_buildup_bonus",
"闪避反击积蓄效率增加": "counter_attack_anomaly_buildup_bonus",
"连携技积蓄效率增加": "qte_anomaly_buildup_bonus",
"终结技积蓄效率增加": "ultimate_anomaly_buildup_bonus",
"快速支援积蓄效率增加": "quick_aid_anomaly_buildup_bonus",
"招架支援积蓄效率增加": "defensive_aid_anomaly_buildup_bonus",
"支援突击积蓄效率增加": "assault_aid_anomaly_buildup_bonus",
"强击额外伤害增幅": "assault_dmg_mul",
"灼烧额外伤害增幅": "burn_dmg_mul",
"冻结额外伤害增幅": "freeze_dmg_mul",
"感电额外伤害增幅": "shock_dmg_mul",
"侵蚀额外伤害增幅": "chaos_dmg_mul",
"紊乱额外伤害增幅": "disorder_dmg_mul",
"全属性异常额外伤害增幅": "all_anomaly_dmg_mul",
"特殊乘区": "special_multiplier_zone",
"失衡延长": "stun_tick_increase",
"固定基础伤害增加": "base_dmg_increase",
"基础伤害增加%": "base_dmg_increase_percentage",
"追加攻击增伤": "aftershock_attack_dmg_bonus",
"追加攻击暴伤": "aftershock_attack_crit_dmg_bonus",
"追加攻击失衡值增加": "aftershock_attack_stun_bonus",
"畏缩时间延长": "assault_time_increase",
"畏缩时间延长百分比": "assault_time_increase_percentage",
"灼烧时间延长": "burn_time_increase",
"灼烧时间延长百分比": "burn_time_increase_percentage",
"感电时间延长": "shock_time_increase",
"感电时间延长百分比": "shock_time_increase_percentage",
"侵蚀时间延长": "corruption_time_increase",
"侵蚀时间延长百分比": "corruption_time_increase_percentage",
"霜寒时间延长": "frostbite_time_increase",
"霜寒时间延长百分比": "frostbite_time_increase_percentage",
"烈霜霜寒时间延长": "frost_frostbite_time_increase",
"烈霜霜寒时间延长百分比": "frost_frostbite_time_increase_percentage",
"所有异常时间延长": "all_anomaly_time_increase",
"所有异常时间延长百分比": "all_anomaly_time_increase_percentage",
"强击暴击率增加": "strike_crit_rate_increase",
"强击暴击伤害增加": "strike_crit_dmg_increase",
"强击无视防御": "strike_ignore_defense",
"紊乱倍率增加": "all_disorder_basic_mul",
"强击紊乱倍率增加": "strike_disorder_basic_mul",
"灼烧紊乱倍率增加": "burn_disorder_basic_mul",
"霜寒紊乱倍率增加": "frostbite_disorder_basic_mul",
"感电紊乱倍率增加": "shock_disorder_basic_mul",
"侵蚀紊乱倍率增加": "chaos_disorder_basic_mul",
"贯穿伤害增加": "sheer_dmg_bonus"
}
================================================
FILE: zsim/sim_progress/ScheduledEvent/constants.py
================================================
"""
事件常量定义
该模块定义了 ScheduledEvent 模块中使用的各种常量。
"""
class EventConstants:
"""事件相关常量"""
# 默认优先级
DEFAULT_PRIORITY = 0
# 缓存相关
MAX_CACHE_SIZE = 128
# 时间精度
TICK_PRECISION = 0.0000001
# 二分查找阈值
BINARY_SEARCH_THRESHOLD = 10
# 事件列表最大大小
EVENT_LIST_MAX_SIZE = 1000
@classmethod
def validate_constants(cls) -> None:
"""验证常量值的合理性"""
assert cls.MAX_CACHE_SIZE > 0, "缓存大小必须大于0"
assert cls.TICK_PRECISION > 0, "时间精度必须大于0"
assert cls.BINARY_SEARCH_THRESHOLD > 0, "二分查找阈值必须大于0"
assert cls.EVENT_LIST_MAX_SIZE > 0, "事件列表最大大小必须大于0"
assert cls.DEFAULT_PRIORITY >= 0, "默认优先级必须大于等于0"
# 事件类型名称
EVENT_TYPES = {
"skill": "技能事件",
"anomaly": "异常事件",
"disorder": "紊乱事件",
"polarity_disorder": "极性紊乱事件",
"abloom": "薇薇安异放事件",
"refresh": "数据刷新事件",
"quick_assist": "快速支援事件",
"preload": "预加载事件",
"stun_forced_termination": "眩晕强制终止事件",
"polarized_assault": "极性强击事件",
}
# 执行时间键映射
EXECUTE_TICK_KEYS = {
"SkillNode": "preload_tick",
"QuickAssistEvent": "execute_tick",
"SchedulePreload": "execute_tick",
"PolarizedAssaultEvent": "execute_tick",
}
# 优先级定义(数字越大优先级越低)
PRIORITY = {
"HIGH": 0, # 高优先级,数字最小
"MEDIUM": 500, # 中等优先级
"LOW": 800, # 低优先级
"VERY_LOW": 999, # 极低优先级,数字最大
}
# 异常更新规则
ANOMALY_UPDATE_RULES = {
"ALWAYS": -1,
"NEVER": 0,
}
class ErrorMessages:
"""错误消息常量"""
# 参数验证错误
INVALID_LOADING_BUFF_TYPE = "loading_buff参数必须为字典,但你输入了{}"
INVALID_TICK_TYPE = "tick参数必须为整数,但你输入了{}"
TARGET_NOT_FOUND = "[Schedule] target: {} not found in char_obj_list, check the alloc."
CHARACTER_NOT_FOUND = "{} not found in char_obj_list"
INVALID_EVENT_TYPE = "{},目前不应存在于 event_list"
# 事件处理错误
FUTURE_EVENT_PROCESSING = "event_start主循环正在尝试处理一个名为{}的未来事件"
ATTRIBUTE_NOT_FOUND = "{} 没有属性 {}"
INVALID_EVENT_HANDLER = "无法找到适合处理事件类型 {} 的处理器"
# 缓存错误
CACHE_SIZE_EXCEEDED = "缓存大小超过限制: {}"
CACHE_KEY_NOT_FOUND = "缓存键未找到: {}"
class WarningMessages:
"""警告消息常量"""
# Buff相关警告
DYNAMIC_BUFF_NOT_FOUND = "[WARNING] 动态Buff列表内没有角色 {}"
BUFF_LIST_EMPTY = "[WARNING] Buff列表为空"
# 事件处理警告
EVENT_NOT_HANDLED = "[WARNING] 事件未被处理: {}"
HANDLER_NOT_FOUND = "[WARNING] 未找到事件处理器: {}"
# 性能警告
LARGE_EVENT_LIST = "[WARNING] 事件列表过大: {}"
SLOW_EVENT_PROCESSING = "[WARNING] 事件处理耗时过长: {}s"
================================================
FILE: zsim/sim_progress/ScheduledEvent/event_handlers/__init__.py
================================================
"""
事件处理器模块
该模块定义了事件处理的工厂类,用于管理各种类型的事件处理器。
"""
from .base import EventHandlerABC
from .context import EventContext
from .handlers import event_handler_factory, register_all_handlers
__all__ = ["EventHandlerABC", "event_handler_factory", "register_all_handlers", "EventContext"]
================================================
FILE: zsim/sim_progress/ScheduledEvent/event_handlers/base.py
================================================
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Any
from .context import EventContext
if TYPE_CHECKING:
from zsim.sim_progress.Enemy import Enemy
from zsim.simulator.dataclasses import ScheduleData
class EventHandlerABC(ABC):
"""事件处理器抽象基类"""
@abstractmethod
def can_handle(self, event: Any) -> bool:
"""
判断是否可以处理指定类型的事件
Args:
event: 待处理的事件对象
Returns:
bool: 如果可以处理该类型事件则返回True,否则返回False
"""
pass
@abstractmethod
def handle(self, event: Any, context: EventContext) -> None:
"""
处理事件
Args:
event: 待处理的事件对象
context: 事件处理上下文,包含所需的数据和环境信息
Raises:
NotImplementedError: 如果子类未实现此方法
"""
pass
@property
@abstractmethod
def event_type(self) -> str:
"""
返回处理器支持的事件类型名称
Returns:
str: 事件类型名称
"""
pass
class BaseEventHandler(EventHandlerABC):
"""基础事件处理器,提供通用功能"""
def __init__(self, event_type: str):
self._event_type = event_type
@property
def event_type(self) -> str:
return self._event_type
def _get_context_data(self, context: EventContext) -> ScheduleData:
"""从上下文中获取ScheduleData"""
return context.get_data()
def _get_context_tick(self, context: EventContext) -> int:
"""从上下文中获取当前tick"""
return context.get_tick()
def _get_context_enemy(self, context: EventContext) -> Enemy:
"""从上下文中获取敌人对象"""
return context.get_enemy()
def _get_context_dynamic_buff(self, context: EventContext):
"""从上下文中获取动态buff"""
return context.get_dynamic_buff()
def _get_context_exist_buff_dict(self, context: EventContext):
"""从上下文中获取已存在buff字典"""
return context.get_exist_buff_dict()
def _get_context_action_stack(self, context: EventContext):
"""从上下文中获取动作栈"""
return context.get_action_stack()
def _get_context_sim_instance(self, context: EventContext):
"""从上下文中获取模拟器实例"""
return context.get_sim_instance()
def _validate_event(
self, event: Any, expected_type: type | tuple[type, ...] | None = None
) -> None:
"""
验证事件参数
Args:
event: 待验证的事件对象
expected_type: 期望的事件类型,如果为None则只验证非None
Raises:
TypeError: 当事件类型不符合期望时
ValueError: 当事件为None时
"""
if event is None:
raise ValueError("事件对象不能为None")
if expected_type is not None and not isinstance(event, expected_type):
if isinstance(expected_type, tuple):
expected_names = [t.__name__ for t in expected_type]
raise TypeError(
f"期望事件类型为 {expected_names} 之一,实际得到 {type(event).__name__}"
)
else:
raise TypeError(
f"期望事件类型为 {expected_type.__name__},实际得到 {type(event).__name__}"
)
def _validate_context(self, context: EventContext) -> None:
"""
验证上下文数据
Args:
context: 待验证的上下文对象
Raises:
ValueError: 当上下文无效时
"""
if not isinstance(context, EventContext): # type: ignore
raise TypeError("上下文必须是EventContext类型")
# Pydantic模型已经确保了数据的完整性和有效性
def _handle_error(self, error: Exception, operation: str, event: Any = None) -> None:
"""
统一错误处理方法
Args:
error: 发生的异常
operation: 操作描述
event: 相关事件对象(可选)
Raises:
RuntimeError: 包装后的异常信息
"""
error_msg = f"在 {operation} 时发生错误: {error}"
if event is not None:
error_msg = f"在 {operation} 事件 {type(event)} 时发生错误: {error}"
raise RuntimeError(error_msg) from error
================================================
FILE: zsim/sim_progress/ScheduledEvent/event_handlers/context.py
================================================
"""
事件处理上下文模型
该模块定义了事件处理上下文的dataclass,用于替代字典形式的上下文数据。
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from zsim.sim_progress.Buff import Buff
from zsim.sim_progress.data_struct import ActionStack
from zsim.sim_progress.Enemy import Enemy
from zsim.sim_progress.Preload.SkillsQueue import SkillNode
from zsim.simulator.dataclasses import ScheduleData
from zsim.simulator.simulator_class import Simulator
@dataclass
class EventContext:
"""
事件处理上下文模型
包含事件处理所需的全部数据和对象,使用dataclass提供类型安全和简洁的语法。
"""
data: ScheduleData
tick: int
enemy: Enemy
dynamic_buff: dict[str, list[Buff]]
exist_buff_dict: dict[str, dict[str, Buff]]
action_stack: ActionStack[SkillNode]
sim_instance: Simulator
def get_data(self) -> ScheduleData:
"""获取调度数据对象"""
return self.data
def get_tick(self) -> int:
"""获取当前时间刻"""
return self.tick
def get_enemy(self) -> Enemy:
"""获取敌人对象"""
return self.enemy
def get_dynamic_buff(self) -> dict[str, list[Buff]]:
"""获取动态buff字典"""
return self.dynamic_buff
def get_exist_buff_dict(self) -> dict[str, dict[str, Buff]]:
"""获取已存在buff字典"""
return self.exist_buff_dict
def get_action_stack(self) -> ActionStack[SkillNode]:
"""获取动作栈"""
return self.action_stack
def get_sim_instance(self) -> Simulator:
"""获取模拟器实例"""
return self.sim_instance
================================================
FILE: zsim/sim_progress/ScheduledEvent/event_handlers/handlers/__init__.py
================================================
"""事件处理器模块
该模块包含所有具体的事件处理器实现和工厂类。
"""
from .abloom import AbloomEventHandler
from .anomaly import AnomalyEventHandler
from .disorder import DisorderEventHandler
from .factory import event_handler_factory
from .polarity_disorder import PolarityDisorderEventHandler
from .polarized_assault import PolarizedAssaultEventHandler
from .preload import PreloadEventHandler
from .quick_assist import QuickAssistEventHandler
from .refresh import RefreshEventHandler
from .skill import SkillEventHandler
from .stun_forced_termination import StunForcedTerminationEventHandler
def register_all_handlers() -> None:
"""注册所有事件处理器"""
handlers = [
SkillEventHandler(),
AnomalyEventHandler(),
DisorderEventHandler(),
PolarityDisorderEventHandler(),
AbloomEventHandler(),
RefreshEventHandler(),
QuickAssistEventHandler(),
PreloadEventHandler(),
StunForcedTerminationEventHandler(),
PolarizedAssaultEventHandler(),
]
for handler in handlers:
event_handler_factory.register_handler(handler)
__all__ = [
"SkillEventHandler",
"AnomalyEventHandler",
"DisorderEventHandler",
"PolarityDisorderEventHandler",
"AbloomEventHandler",
"RefreshEventHandler",
"QuickAssistEventHandler",
"PreloadEventHandler",
"StunForcedTerminationEventHandler",
"PolarizedAssaultEventHandler",
"event_handler_factory",
"register_all_handlers",
]
================================================
FILE: zsim/sim_progress/ScheduledEvent/event_handlers/handlers/abloom.py
================================================
"""薇薇安异放事件处理器"""
from typing import Any
from zsim.sim_progress import Report
from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import DirgeOfDestinyAnomaly as Abloom
from ...CalAnomaly import CalAbloom
from ..base import BaseEventHandler
from ..context import EventContext
class AbloomEventHandler(BaseEventHandler):
"""薇薇安异放事件处理器"""
def __init__(self):
super().__init__("abloom")
def can_handle(self, event: Any) -> bool:
return type(event) is Abloom
def handle(self, event: Abloom, context: EventContext) -> None:
"""处理薇薇安异放事件"""
enemy = self._get_context_enemy(context)
dynamic_buff = self._get_context_dynamic_buff(context)
sim_instance = self._get_context_sim_instance(context)
tick = self._get_context_tick(context)
# 计算异放伤害
calculator = CalAbloom(
abloom_obj=event,
enemy_obj=enemy,
dynamic_buff=dynamic_buff,
sim_instance=sim_instance,
)
damage_anomaly = calculator.cal_anomaly_dmg()
Report.report_dmg_result(
tick=tick,
element_type=event.element_type,
skill_tag="异放",
dmg_expect=round(damage_anomaly, 2),
is_anomaly=True,
dmg_crit=round(damage_anomaly, 2),
stun=0,
buildup=0,
**enemy.dynamic.get_status(),
UUID=event.UUID if event.UUID is not None else "",
)
================================================
FILE: zsim/sim_progress/ScheduledEvent/event_handlers/handlers/anomaly.py
================================================
"""异常事件处理器"""
from typing import Any
from zsim.sim_progress import Report
from zsim.sim_progress.anomaly_bar import AnomalyBar as AnB
from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import (
NewAnomaly,
)
from zsim.sim_progress.Buff import ScheduleBuffSettle
from ...CalAnomaly import CalAnomaly
from ..base import BaseEventHandler
from ..context import EventContext
class AnomalyEventHandler(BaseEventHandler):
"""异常事件处理器"""
def __init__(self):
super().__init__("anomaly")
def can_handle(self, event: Any) -> bool:
return type(event) is AnB or type(event) is NewAnomaly
def handle(self, event: AnB | NewAnomaly, context: EventContext) -> None:
"""处理异常事件
处理 AnomalyBar 和 NewAnomaly 两种类型的异常事件,包括:
- 计算异常伤害
- 报告伤害结果
- 处理相关buff结算
"""
# 验证输入
self._validate_event(event, (AnB, NewAnomaly))
self._validate_context(context)
enemy = self._get_context_enemy(context)
dynamic_buff = self._get_context_dynamic_buff(context)
exist_buff_dict = self._get_context_exist_buff_dict(context)
action_stack = self._get_context_action_stack(context)
sim_instance = self._get_context_sim_instance(context)
tick = self._get_context_tick(context)
# 计算异常伤害
calculator = CalAnomaly(
anomaly_obj=event,
enemy_obj=enemy,
dynamic_buff=dynamic_buff,
sim_instance=sim_instance,
)
damage_anomaly = calculator.cal_anomaly_dmg()
Report.report_dmg_result(
tick=tick,
skill_tag=event.rename_tag if event.rename else None,
element_type=event.element_type,
dmg_expect=round(damage_anomaly, 2),
is_anomaly=True,
dmg_crit=round(damage_anomaly, 2),
stun=0,
buildup=0,
**enemy.dynamic.get_status(),
UUID=event.UUID if event.UUID is not None else "",
)
# 处理buff结算
ScheduleBuffSettle(
tick,
exist_buff_dict,
enemy,
dynamic_buff,
action_stack,
anomaly_bar=event,
sim_instance=sim_instance,
)
================================================
FILE: zsim/sim_progress/ScheduledEvent/event_handlers/handlers/disorder.py
================================================
"""紊乱事件处理器"""
from typing import Any
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from zsim.sim_progress import Report
from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import Disorder
from ...CalAnomaly import CalDisorder
from ..base import BaseEventHandler
from ..context import EventContext
class DisorderEventHandler(BaseEventHandler):
"""紊乱事件处理器"""
def __init__(self):
super().__init__("disorder")
def can_handle(self, event: Any) -> bool:
return type(event) is Disorder
def handle(self, event: Disorder, context: EventContext) -> None:
"""处理紊乱事件"""
enemy = self._get_context_enemy(context)
dynamic_buff = self._get_context_dynamic_buff(context)
sim_instance = self._get_context_sim_instance(context)
tick = self._get_context_tick(context)
# 广播紊乱事件
sim_instance.listener_manager.broadcast_event(event=event, signal=LBS.DISORDER_SETTLED)
# 计算紊乱伤害
calculator = CalDisorder(
disorder_obj=event,
enemy_obj=enemy,
dynamic_buff=dynamic_buff,
sim_instance=sim_instance,
)
damage_disorder = calculator.cal_anomaly_dmg()
stun = calculator.cal_disorder_stun()
# 更新敌人眩晕值
enemy.update_stun(stun)
Report.report_dmg_result(
tick=tick,
element_type=event.element_type,
dmg_expect=round(damage_disorder, 2),
dmg_crit=round(damage_disorder, 2),
is_anomaly=True,
is_disorder=True,
stun=round(stun, 2),
buildup=0,
**enemy.dynamic.get_status(),
UUID=event.UUID if event.UUID is not None else "",
)
================================================
FILE: zsim/sim_progress/ScheduledEvent/event_handlers/handlers/factory.py
================================================
"""
事件处理器工厂模块
该模块定义了事件处理的工厂类,用于管理各种类型的事件处理器。
"""
from typing import Any
from ..base import EventHandlerABC
class EventHandlerFactory:
"""事件处理器工厂类"""
def __init__(self):
self._handlers: dict[str, EventHandlerABC] = {}
self._handler_cache: dict[type, EventHandlerABC] = {}
self._cache_hits = 0
self._cache_misses = 0
def register_handler(self, handler: EventHandlerABC) -> None:
"""
注册事件处理器
Args:
handler: 事件处理器实例
Raises:
ValueError: 如果已存在相同事件类型的处理器
"""
event_type = handler.event_type
if event_type in self._handlers:
raise ValueError(f"事件类型 '{event_type}' 的处理器已存在")
self._handlers[event_type] = handler
def get_handler(self, event: Any) -> EventHandlerABC | None:
"""
获取适合处理指定事件的处理器(带缓存)
Args:
event: 待处理的事件对象
Returns:
EventHandler | None: 如果找到合适的处理器则返回,否则返回None
"""
# 检查缓存
event_type = type(event)
if event_type in self._handler_cache:
self._cache_hits += 1
return self._handler_cache[event_type]
# 缓存未命中,查找处理器
for handler in self._handlers.values():
if handler.can_handle(event):
self._handler_cache[event_type] = handler
self._cache_misses += 1
return handler
self._cache_misses += 1
return None
def get_handler_by_type(self, event_type: str) -> EventHandlerABC | None:
"""
根据事件类型获取处理器
Args:
event_type: 事件类型名称
Returns:
EventHandler | None: 如果找到处理器则返回,否则返回None
"""
return self._handlers.get(event_type)
def list_handlers(self) -> list[str]:
"""
获取所有已注册的处理器类型列表
Returns:
list[str]: 处理器类型名称列表
"""
return list(self._handlers.keys())
def clear_handlers(self) -> None:
"""清除所有已注册的处理器"""
self._handlers.clear()
self._handler_cache.clear()
def get_cache_stats(self) -> dict[str, int | float]:
"""
获取缓存统计信息
Returns:
dict[str, int]: 包含缓存命中率和统计信息的字典
"""
total_requests = self._cache_hits + self._cache_misses
hit_rate = (self._cache_hits / total_requests * 100) if total_requests > 0 else 0
return {
"cache_hits": self._cache_hits,
"cache_misses": self._cache_misses,
"total_requests": total_requests,
"hit_rate_percent": round(hit_rate, 2),
}
def reset_cache_stats(self) -> None:
"""重置缓存统计信息"""
self._cache_hits = 0
self._cache_misses = 0
def clear_cache(self) -> None:
"""清除处理器缓存"""
self._handler_cache.clear()
self.reset_cache_stats()
# 全局处理器工厂实例
event_handler_factory = EventHandlerFactory()
__all__ = ["event_handler_factory"]
================================================
FILE: zsim/sim_progress/ScheduledEvent/event_handlers/handlers/polarity_disorder.py
================================================
"""极性紊乱事件处理器"""
from typing import Any
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from zsim.sim_progress import Report
from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import PolarityDisorder
from ...CalAnomaly import CalPolarityDisorder
from ..base import BaseEventHandler
from ..context import EventContext
class PolarityDisorderEventHandler(BaseEventHandler):
"""极性紊乱事件处理器"""
def __init__(self):
super().__init__("polarity_disorder")
def can_handle(self, event: Any) -> bool:
return type(event) is PolarityDisorder
def handle(self, event: PolarityDisorder, context: EventContext) -> None:
"""处理极性紊乱事件"""
enemy = self._get_context_enemy(context)
dynamic_buff = self._get_context_dynamic_buff(context)
sim_instance = self._get_context_sim_instance(context)
tick = self._get_context_tick(context)
# 广播极性紊乱事件
sim_instance.listener_manager.broadcast_event(event=event, signal=LBS.DISORDER_SETTLED)
# 计算极性紊乱伤害
calculator = CalPolarityDisorder(
disorder_obj=event,
enemy_obj=enemy,
dynamic_buff=dynamic_buff,
sim_instance=sim_instance,
)
damage_disorder = calculator.cal_anomaly_dmg()
Report.report_dmg_result(
tick=tick,
element_type=event.element_type,
skill_tag="极性紊乱",
dmg_expect=round(damage_disorder, 2),
dmg_crit=round(damage_disorder, 2),
is_anomaly=True,
is_disorder=True,
stun=0,
buildup=0,
**enemy.dynamic.get_status(),
UUID=event.UUID if event.UUID is not None else "",
)
================================================
FILE: zsim/sim_progress/ScheduledEvent/event_handlers/handlers/polarized_assault.py
================================================
"""极性强击事件处理器"""
from typing import Any
from zsim.sim_progress.data_struct import PolarizedAssaultEvent
from ..base import BaseEventHandler
from ..context import EventContext
class PolarizedAssaultEventHandler(BaseEventHandler):
"""极性强击事件处理器"""
def __init__(self):
super().__init__("polarized_assault")
def can_handle(self, event: Any) -> bool:
return type(event) is PolarizedAssaultEvent
def handle(self, event: PolarizedAssaultEvent, context: EventContext) -> None:
"""处理极性强击事件"""
data = self._get_context_data(context)
tick = self._get_context_tick(context)
# 检查是否到达执行时间
if tick < event.execute_tick:
# 时间未到,将事件重新加入列表
data.event_list.append(event)
return
# 执行事件
event.execute()
================================================
FILE: zsim/sim_progress/ScheduledEvent/event_handlers/handlers/preload.py
================================================
"""预加载事件处理器"""
from typing import Any
from zsim.sim_progress.data_struct import SchedulePreload
from ..base import BaseEventHandler
from ..context import EventContext
class PreloadEventHandler(BaseEventHandler):
"""预加载事件处理器"""
def __init__(self):
super().__init__("preload")
def can_handle(self, event: Any) -> bool:
return isinstance(event, SchedulePreload)
def handle(self, event: SchedulePreload, context: EventContext) -> None:
"""处理预加载事件"""
data = self._get_context_data(context)
tick = self._get_context_tick(context)
# 检查是否到达执行时间
if tick < event.execute_tick:
# 时间未到,将事件重新加入列表
data.event_list.append(event)
return
# 执行事件
event.execute_myself()
================================================
FILE: zsim/sim_progress/ScheduledEvent/event_handlers/handlers/quick_assist.py
================================================
"""快速支援事件处理器"""
from typing import Any
from zsim.sim_progress.data_struct import QuickAssistEvent
from ..base import BaseEventHandler
from ..context import EventContext
class QuickAssistEventHandler(BaseEventHandler):
"""快速支援事件处理器"""
def __init__(self):
super().__init__("quick_assist")
def can_handle(self, event: Any) -> bool:
return isinstance(event, QuickAssistEvent)
def handle(self, event: QuickAssistEvent, context: EventContext) -> None:
"""处理快速支援事件"""
data = self._get_context_data(context)
tick = self._get_context_tick(context)
# 检查是否到达执行时间
if tick < event.execute_tick:
# 时间未到,将事件重新加入列表
data.event_list.append(event)
return
# 执行事件
event.execute_update(tick)
================================================
FILE: zsim/sim_progress/ScheduledEvent/event_handlers/handlers/refresh.py
================================================
"""数据刷新事件处理器"""
from typing import Any
from zsim.sim_progress.data_struct import ScheduleRefreshData
from ..base import BaseEventHandler
from ..context import EventContext
class RefreshEventHandler(BaseEventHandler):
"""数据刷新事件处理器"""
def __init__(self):
super().__init__("refresh")
def can_handle(self, event: Any) -> bool:
return isinstance(event, ScheduleRefreshData)
def handle(self, event: ScheduleRefreshData, context: EventContext) -> None:
"""处理数据刷新事件"""
try:
data = self._get_context_data(context)
# 创建角色映射
char_mapping = {character.NAME: character for character in data.char_obj_list}
# 更新能量
for target in event.sp_target:
if target != "":
if target not in char_mapping:
raise KeyError(f"目标角色 {target} 未找到")
char_mapping[target].update_sp(event.sp_value)
# 更新喧响
for target in event.decibel_target:
if target != "":
if target not in char_mapping:
raise KeyError(f"目标角色 {target} 未找到")
char_mapping[target].update_decibel(event.decibel_value)
except Exception as e:
self._handle_error(e, "数据刷新事件处理", event)
================================================
FILE: zsim/sim_progress/ScheduledEvent/event_handlers/handlers/skill.py
================================================
"""技能事件处理器"""
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from zsim.sim_progress import Report
from zsim.sim_progress.Buff import ScheduleBuffSettle
from zsim.sim_progress.Character import Character
from zsim.sim_progress.data_struct import (
ActionStack,
SingleHit,
)
from zsim.sim_progress.Load.LoadDamageEvent import (
ProcessFreezLikeDots,
ProcessHitUpdateDots,
)
from zsim.sim_progress.Load.loading_mission import LoadingMission
from zsim.sim_progress.Preload import SkillNode
from zsim.sim_progress.Update import update_anomaly
from ...Calculator import Calculator
from ..base import BaseEventHandler
from ..context import EventContext
if TYPE_CHECKING:
from zsim.sim_progress.Enemy import Enemy
from zsim.simulator.dataclasses import ScheduleData
from zsim.simulator.simulator_class import Simulator
class SkillEventHandler(BaseEventHandler):
"""技能事件处理器"""
def __init__(self):
super().__init__("skill")
def can_handle(self, event: Any) -> bool:
return isinstance(event, SkillNode | LoadingMission)
def handle(self, event: SkillNode | LoadingMission, context: EventContext) -> None:
"""处理技能事件"""
# 验证输入
self._validate_event(event, (SkillNode, LoadingMission))
self._validate_context(context)
data = self._get_context_data(context)
tick = self._get_context_tick(context)
enemy = self._get_context_enemy(context)
dynamic_buff = self._get_context_dynamic_buff(context)
exist_buff_dict = self._get_context_exist_buff_dict(context)
action_stack = self._get_context_action_stack(context)
sim_instance = self._get_context_sim_instance(context)
# 检查是否到达执行时间
execute_tick = self._get_execute_tick(event, context)
if execute_tick is None or execute_tick > tick:
return
self._process_skill_event(
event=event,
data=data,
tick=tick,
enemy=enemy,
dynamic_buff=dynamic_buff,
exist_buff_dict=exist_buff_dict,
action_stack=action_stack,
sim_instance=sim_instance,
)
def _get_execute_tick(
self, event: SkillNode | LoadingMission, context: EventContext
) -> int | None:
"""获取事件的执行tick"""
if isinstance(event, SkillNode):
return event.preload_tick
elif isinstance(event, LoadingMission):
return event.mission_node.preload_tick
return None
def _process_skill_event(
self,
event: SkillNode | LoadingMission,
data: ScheduleData,
tick: int,
enemy: Enemy,
dynamic_buff: dict,
exist_buff_dict: dict,
action_stack: ActionStack,
sim_instance: Simulator,
) -> None:
"""处理技能事件的具体逻辑"""
# 获取技能节点和命中次数
skill_node, hit_count = self._extract_skill_info(event)
# 查找角色对象
char_obj = self._find_character(skill_node.skill.char_name, data.char_obj_list)
# 计算伤害
self._calculate_damage(skill_node, char_obj, enemy, dynamic_buff, hit_count, event, tick)
# 更新异常条
self._update_anomaly_bar_after_skill_event(
skill_node, enemy, tick, data, dynamic_buff, sim_instance
)
# 处理buff结算
self._settle_buffs(
tick, exist_buff_dict, enemy, dynamic_buff, action_stack, skill_node, sim_instance
)
# 处理伤害更新
self._update_damage_effects(tick, enemy, data, event)
self._broadcast_skill_event_to_char(event=event, sim_instance=sim_instance)
def _broadcast_skill_event_to_char(
self, event: SkillNode | LoadingMission, sim_instance: Simulator
) -> None:
"""广播技能事件到所有角色以触发特殊资源更新
Args:
event: 当前技能事件
sim_instance: 模拟器实例
"""
event_to_broadcast = event if isinstance(event, SkillNode) else event.mission_node
for char_obj in sim_instance.char_data.char_obj_list:
if hasattr(char_obj, "update_special_resource"):
char_obj.update_special_resource(event_to_broadcast)
def _extract_skill_info(self, event: SkillNode | LoadingMission) -> tuple[SkillNode, int]:
"""提取技能节点和命中次数信息
Args:
event: 技能事件对象
Returns:
tuple[SkillNode, int]: (技能节点, 命中次数)
"""
if isinstance(event, LoadingMission):
return event.mission_node, event.hitted_count
else:
return event, 0
def _find_character(self, char_name: str, char_obj_list: list[Character]) -> Character:
"""查找角色对象"""
for character in char_obj_list:
if character.NAME == char_name:
return character
raise ValueError(f"角色 {char_name} 未找到")
def _calculate_damage(
self,
skill_node: SkillNode,
char_obj: Character,
enemy: Enemy,
dynamic_buff: dict,
hit_count: int,
event: SkillNode | LoadingMission,
tick: int,
) -> None:
"""计算伤害"""
calculator = Calculator(
skill_node=skill_node,
character_obj=char_obj,
enemy_obj=enemy,
dynamic_buff=dynamic_buff,
)
snapshot = calculator.cal_snapshot()
stun = calculator.cal_stun()
damage_expect = calculator.cal_dmg_expect()
damage_crit = calculator.cal_dmg_crit()
# 获取实际的active_generation值
if isinstance(event, SkillNode):
proactive = event.active_generation
else:
proactive = event.mission_node.active_generation
hit_result = SingleHit(
skill_tag=skill_node.skill_tag,
snapshot=snapshot,
stun=stun,
dmg_expect=damage_expect,
dmg_crit=damage_crit,
hitted_count=hit_count,
proactive=proactive,
)
hit_result.skill_node = skill_node
if skill_node.skill.follow_by:
hit_result.proactive = False
if skill_node.hit_times == hit_count and skill_node.skill.heavy_attack:
hit_result.heavy_hit = True
enemy.hit_received(hit_result, tick) # 使用实际的tick值
Report.report_dmg_result(
tick=tick, # 使用实际的tick值
element_type=skill_node.element_type,
skill_tag=skill_node.skill_tag,
dmg_expect=round(damage_expect, 2),
dmg_crit=round(damage_crit, 2),
stun=round(stun, 2),
buildup=round(snapshot[1], 2),
**enemy.dynamic.get_status(),
UUID=skill_node.UUID if skill_node.UUID is not None else "",
crit_rate=calculator.regular_multipliers.crit_rate,
crit_dmg=calculator.regular_multipliers.crit_dmg,
)
def _update_anomaly_bar_after_skill_event(
self,
skill_node: SkillNode,
enemy: Enemy,
tick: int,
data: ScheduleData,
dynamic_buff: dict,
sim_instance: Simulator,
) -> None:
"""
在技能事件后更新异常条
Args:
skill_node: 技能节点
enemy: 敌人对象
tick: 当前时间刻
data: 调度数据
dynamic_buff: 动态buff
sim_instance: 模拟器实例
"""
# 复制原始 __init__.py 中的 update_anomaly_bar_after_skill_event 逻辑
_node = skill_node
# 判断当前Tick的技能是否能够更新异常
should_update = False
if not _node.skill.anomaly_update_rule:
if _node.loading_mission is None:
_loading_mission = LoadingMission(_node)
_loading_mission.mission_start(timenow=sim_instance.tick)
_node.loading_mission = _loading_mission
last_hit = _node.loading_mission.get_last_hit()
if last_hit is not None and tick - 1 < last_hit <= tick:
should_update = True
else:
if _node.skill.anomaly_update_rule == -1:
should_update = True
else:
if (
_node.loading_mission is not None
and _node.skill.anomaly_update_rule is not None
and (
isinstance(_node.skill.anomaly_update_rule, list)
and _node.loading_mission.hitted_count in _node.skill.anomaly_update_rule
or isinstance(_node.skill.anomaly_update_rule, int)
and _node.loading_mission.hitted_count == _node.skill.anomaly_update_rule
)
):
should_update = True
if should_update:
update_anomaly(
_node.element_type,
enemy,
tick,
data.event_list,
data.char_obj_list,
skill_node=_node,
dynamic_buff_dict=dynamic_buff,
sim_instance=sim_instance,
)
def _settle_buffs(
self,
tick: int,
exist_buff_dict: dict,
enemy: Enemy,
dynamic_buff: dict,
action_stack: ActionStack,
skill_node: SkillNode,
sim_instance: Simulator,
) -> None:
"""处理buff结算
Args:
tick: 当前tick
exist_buff_dict: 已存在的buff字典
enemy: 敌人对象
dynamic_buff: 动态buff字典
action_stack: 动作栈
skill_node: 技能节点
sim_instance: 模拟器实例
"""
ScheduleBuffSettle(
tick,
exist_buff_dict,
enemy,
dynamic_buff,
action_stack,
skill_node=skill_node,
sim_instance=sim_instance,
)
def _update_damage_effects(
self,
tick: int,
enemy: Enemy,
data: ScheduleData,
event: SkillNode | LoadingMission,
) -> None:
"""处理伤害更新效果
Args:
tick: 当前tick
enemy: 敌人对象
data: 调度数据
event: 技能事件对象
"""
ProcessHitUpdateDots(tick, enemy.dynamic.dynamic_dot_list, data.event_list)
ProcessFreezLikeDots(timetick=tick, enemy=enemy, event_list=data.event_list, event=event)
================================================
FILE: zsim/sim_progress/ScheduledEvent/event_handlers/handlers/stun_forced_termination.py
================================================
"""眩晕强制终止事件处理器"""
from typing import Any
from zsim.sim_progress.data_struct import StunForcedTerminationEvent
from ..base import BaseEventHandler
from ..context import EventContext
class StunForcedTerminationEventHandler(BaseEventHandler):
"""眩晕强制终止事件处理器"""
def __init__(self):
super().__init__("stun_forced_termination")
def can_handle(self, event: Any) -> bool:
return type(event) is StunForcedTerminationEvent
def handle(self, event: StunForcedTerminationEvent, context: EventContext) -> None:
"""处理眩晕强制终止事件"""
data = self._get_context_data(context)
tick = self._get_context_tick(context)
# 检查是否到达执行时间
if tick < event.execute_tick:
# 时间未到,将事件重新加入列表
data.event_list.append(event)
return
# 执行事件
event.execute_myself()
================================================
FILE: zsim/sim_progress/Update/UpdateAnomaly.py
================================================
import importlib
from copy import deepcopy
from typing import TYPE_CHECKING
from zsim.define import ELEMENT_TYPE_MAPPING
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from zsim.sim_progress.anomaly_bar import AnomalyBar
from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import (
Disorder,
NewAnomaly,
PolarityDisorder,
)
from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy
from zsim.sim_progress.Dot.BaseDot import Dot
if TYPE_CHECKING:
from zsim.sim_progress.Buff import Buff
from zsim.sim_progress.Preload import SkillNode
from zsim.simulator.simulator_class import Simulator
anomlay_dot_dict = {
0: "Assault",
1: "Ignite",
2: "Freez",
3: "Shock",
4: "Corruption",
5: "Freez",
6: "AuricInkCorruption",
}
def spawn_output(anomaly_bar, mode_number, sim_instance: "Simulator", **kwargs):
"""
该函数用于抛出一个新的属性异常类
"""
if not isinstance(anomaly_bar, AnomalyBar):
raise TypeError(f"{anomaly_bar}不是AnomalyBar类!")
skill_node = kwargs.get("skill_node", None)
# 先处理快照,使其除以总权值。
anomaly_bar.anomaly_settled() if mode_number in [0] else None
# 老逻辑
# anomaly_bar.current_ndarray = (
# anomaly_bar.current_ndarray / anomaly_bar.current_anomaly
# )
# output = anomaly_bar.element_type, anomaly_bar.current_ndarray
output: "AnomalyBar | None" = None
if mode_number == 0:
output = NewAnomaly(anomaly_bar, active_by=skill_node, sim_instance=sim_instance)
elif mode_number == 1:
output = Disorder(anomaly_bar, active_by=skill_node, sim_instance=sim_instance)
elif mode_number == 2:
polarity_ratio = kwargs.get("polarity_ratio", None)
if polarity_ratio is None:
raise ValueError(
"在调用spawn_output函数的模式二(mode_number=2)、企图生成一个极性紊乱对象时,并未传入必须的参数polarity_ratio!"
)
output = PolarityDisorder(
anomaly_bar, polarity_ratio, active_by=skill_node, sim_instance=sim_instance
)
if output is None:
raise ValueError("在调用spawn_output函数时,未正确生成一个AnomalyBar实例!")
# 广播事件
if mode_number in [1, 2]:
sim_instance.listener_manager.broadcast_event(event=output, signal=LBS.DISORDER_SPAWN)
return output
def anomaly_effect_active(
bar: AnomalyBar,
timenow: int,
enemy,
new_anomaly,
element_type,
sim_instance: "Simulator",
):
"""
该函数的作用是创建属性异常附带的debuff和dot,
debuff与dot的index写在了Anomaly.accompany_debuff和Anomaly.accompany_dot里。
这里通过Buff的BuffInitialize函数来根据Buff名,直接提取对应的双字典,
并且直接放进Buff的构造函数内,对新的Buff进行实例化。
然后,回传给exist_buff_dict中的Buff0。
Args:
bar: AnomalyBar: 样本异常条实例,仅用于获取静态信息(多为字符串),不用于业务和运算
timenow: int: 当前时间
enemy: Enemy: 敌人实例
new_anomaly: AnomalyBar: 新触发的异常实例,通常为参与业务的主体,是样本异常条实例的深拷贝
element_type: int: 属性类型(0~6)
sim_instance: Simulator: 模拟器实例
"""
if bar.accompany_debuff:
for debuff in bar.accompany_debuff:
buff_add_strategy(debuff, sim_instance=sim_instance)
if bar.accompany_dot:
new_dot = spawn_anomaly_dot(
element_type, timenow, bar=new_anomaly, sim_instance=sim_instance
)
if new_dot:
for dots in enemy.dynamic.dynamic_dot_list[:]:
if dots.ft.index == new_dot.ft.index:
dots.end(timenow)
enemy.dynamic.dynamic_dot_list.remove(dots)
enemy.dynamic.dynamic_dot_list.append(new_dot)
# event_list.append(new_dot)
def update_anomaly(
element_type: int,
enemy,
time_now: int,
event_list: list,
char_obj_list: list,
sim_instance: "Simulator",
skill_node: "SkillNode",
dynamic_buff_dict: dict[str, list["Buff"]],
**kwargs,
):
"""
该函数需要在Schedule阶段的SkillEvent分支内运行。
用于判断该次属性异常触发应该是新建、替换还是触发紊乱。
第一个参数是属性种类,第二个参数是Enemy类的实例,第三个参数是当前时间
如果判断通过触发,则会立刻实例化一个对应的属性异常实例(自带复制父类的状态与属性),
"""
bar: AnomalyBar = enemy.anomaly_bars_dict[skill_node.element_type]
if not isinstance(bar, AnomalyBar):
raise TypeError(f"{type(bar)}不是Anomaly类!")
active_anomaly_check, active_anomaly_list, last_anomaly_element_type = check_anomaly_bar(enemy)
# 获取当前最大值。修改最大值的操作在确认内置CD转好后再执行。
bar.max_anomaly = getattr(
enemy, f"max_anomaly_{enemy.trans_element_number_to_str[element_type]}"
)
assert bar.max_anomaly is not None and bar.current_anomaly is not None, (
"当前异常值或最大异常值为None!"
)
if bar.current_anomaly >= bar.max_anomaly:
# 积蓄值蓄满了,但是属性异常不一定触发,还需要验证一下内置CD
bar.ready_judge(time_now)
if bar.ready:
# 内置CD检测也通过之后,属性异常正式触发。现将需要更新的信息更新一下。
sim_instance.decibel_manager.update(skill_node=skill_node, key="anomaly")
bar.change_info_cause_active(
time_now, dynamic_buff_dict=dynamic_buff_dict, skill_node=skill_node
)
enemy.update_max_anomaly(element_type)
active_bar = deepcopy(bar)
enemy.dynamic.active_anomaly_bar_dict[element_type] = active_bar
# 异常事件监听器广播
sim_instance.listener_manager.broadcast_event(event=active_bar, signal=LBS.ANOMALY)
if active_bar.element_type in [0]:
sim_instance.listener_manager.broadcast_event(
event=active_bar, signal=LBS.ASSAULT_SPAWN
)
"""
更新完毕,现在正式进入分支判断——触发同类异常 & 触发异类异常(紊乱)。
无论是哪个分支,都需要涉及enemy下的两大容器:enemy_debuff_list以及enemy_dot_list的修改,
同时,也可能需要修改exist_buff_dict以及DYNAMIC_BUFF_DICT
"""
if element_type in active_anomaly_list or active_anomaly_check == 0:
"""
这个分支意味着:新触发了某异常或是同类异常覆盖,此时应该执行的策略是“更新”,模式编码是0
该策略下,只需要抛出一个新的属性异常给dot,不需要改变enemy的信息,只需要更新enemy的dot和debuff 两个list即可。
"""
mode_number = 0
new_anomaly = spawn_output(
active_bar, mode_number, skill_node=skill_node, sim_instance=sim_instance
)
for _char in char_obj_list:
_char.special_resources(new_anomaly)
anomaly_effect_active(
active_bar,
time_now,
enemy,
new_anomaly,
element_type,
sim_instance=sim_instance,
)
if element_type in [2, 5]:
"""
当前分支是冰异常和烈霜异常分支,触发异常后,不向eventlist里面添加事件。
但是如果有老的异常,那么就要立刻去掉老的,换上新的。
最后,frozen的状态参数被打开。
"""
if enemy.dynamic.frozen:
event_list.append(new_anomaly)
# print("新的冰异常触发导致老碎冰直接结算")
enemy.dynamic.frozen = True
# print("触发了新的冰异常!")
else:
"""
只要不是冰和烈霜异常,就直接向eventlist里面添加即可。
"""
event_list.append(new_anomaly)
setattr(enemy.dynamic, enemy.trans_anomaly_effect_to_str[element_type], True)
enemy.dynamic.active_anomaly_bar_dict[element_type] = active_bar
elif element_type not in active_anomaly_list and len(active_anomaly_list) > 0:
"""
这个分支意味着:要结算紊乱。那么需要复制的就不应该是新的这个属性异常,而应该是老的属性异常的bar实例。
为了区分好用于计算的异常积蓄条,
"""
mode_number = 1
last_anomaly_bar = enemy.dynamic.active_anomaly_bar_dict[last_anomaly_element_type]
setattr(
enemy.dynamic,
enemy.trans_anomaly_effect_to_str[last_anomaly_element_type],
False,
)
setattr(enemy.dynamic, enemy.trans_anomaly_effect_to_str[element_type], True)
if element_type in [2, 5]:
# if enemy.dynamic.frozen:
# event_list.append(last_anomaly_bar)
enemy.dynamic.frozen = True
# print("触发了新的冰异常!")
# 旧的激活异常拿出来复制,变成disorder后,从enemy身上清空。
disorder = spawn_output(
last_anomaly_bar,
mode_number,
skill_node=skill_node,
sim_instance=sim_instance,
)
enemy.dynamic.active_anomaly_bar_dict[last_anomaly_element_type] = None
enemy.anomaly_bars_dict[last_anomaly_element_type].active = False
remove_dots_cause_disorder(disorder, enemy, event_list, time_now)
# 新的激活异常根据原来的Bar进行复制,并且添加到enemy身上。
new_anomaly = spawn_output(
active_bar, 0, skill_node=skill_node, sim_instance=sim_instance
)
anomaly_effect_active(
active_bar,
time_now,
enemy,
new_anomaly,
element_type,
sim_instance=sim_instance,
)
enemy.dynamic.active_anomaly_bar_dict[element_type] = active_bar
# 向eventlist中添加事件。主要包括非烈霜、冰属性的新异常,以及紊乱。
if element_type not in [2, 5]:
event_list.append(new_anomaly)
for obj in char_obj_list:
obj.special_resources(disorder)
event_list.append(disorder)
sim_instance.decibel_manager.update(skill_node=skill_node, key="disorder")
enemy.sim_instance.schedule_data.change_process_state()
if disorder.activated_by:
print(
f"由【{disorder.activated_by.char_name}】的【{disorder.activated_by.skill_tag}】技能触发了紊乱!【{ELEMENT_TYPE_MAPPING[last_anomaly_bar.element_type]}】属性的异常状态提前结束!"
)
# 在异常与紊乱两个分支的最后,清空bar的异常积蓄和快照。
else:
raise ValueError("无法解析的异常/紊乱分支")
bar.reset_current_info_cause_output()
def remove_dots_cause_disorder(disorder, enemy, event_list, time_now):
"""
该函数只负责移除dot。
"""
remove_dots_list = []
for dots in enemy.dynamic.dynamic_dot_list:
if not isinstance(dots, Dot):
raise TypeError(f"{dots}不是DOT类!")
if dots.ft.index in ["Freez", "Freezdot"] or dots.ft.index == disorder.accompany_dot:
if dots.dy.effect_times > dots.ft.max_effect_times:
raise ValueError("该Dot任务已经完成,应当被删除!")
remove_dots_list.append(dots)
else:
sim_instance = enemy.sim_instance
for _dot in remove_dots_list:
if _dot.ft.index in ["Freez", "Freezdot"]:
event_list.append(_dot.anomaly_data)
_dot.dy.ready = False
_dot.dy.last_effect_ticks = time_now
_dot.dy.effect_times += 1
_dot.end(time_now)
enemy.dynamic.dynamic_dot_list.remove(_dot)
enemy.dynamic.frozen = False
enemy.dynamic.frostbite = False
else:
_dot.end(time_now)
enemy.dynamic.dynamic_dot_list.remove(_dot)
sim_instance.schedule_data.change_process_state()
print(f"因紊乱而强行移除Dot {_dot.ft.index}")
def check_anomaly_bar(enemy):
"""
自检函数:
1、检查当前激活的属性异常数量是否>2,如果是直接报错。
2、由于冰与烈霜异常会导致2,5同时进入active_anomaly_check列表,所以这里要进行筛选
"""
active_anomaly_check = 0
active_anomaly_list = []
anomaly_name_list = []
for (
element_number,
element_anomaly_effect,
) in enemy.trans_anomaly_effect_to_str.items():
if getattr(enemy.dynamic, element_anomaly_effect):
anomaly_name_list.append(element_anomaly_effect)
anomaly_name_list_unique = list(set(anomaly_name_list))
active_anomaly_check = len(anomaly_name_list_unique)
active_anomaly_list.append(element_number)
if active_anomaly_check >= 2:
raise ValueError("当前同时存在两种以上的异常状态!!!")
last_anomaly_element_type: int | None = None
if len(active_anomaly_list) == 1:
last_anomaly_element_type = active_anomaly_list[0]
elif len(active_anomaly_list) == 2:
if active_anomaly_list == [2, 5]:
for number in [2, 5]:
if enemy.anomaly_bars_dict[number].active:
last_anomaly_element_type = number
break
else:
raise TypeError(f"当前激活的异常类型列表为{active_anomaly_list},是预期之外的值。")
else:
last_anomaly_element_type = None
return active_anomaly_check, active_anomaly_list, last_anomaly_element_type
def spawn_anomaly_dot(
element_type, timenow, bar=None, skill_tag=None, sim_instance: "Simulator | None" = None
):
if element_type in anomlay_dot_dict:
class_name = anomlay_dot_dict[element_type]
new_dot = create_dot_instance(class_name, sim_instance=sim_instance, bar=bar)
if isinstance(new_dot, Dot):
new_dot.start(timenow)
return new_dot
else:
return False
def spawn_normal_dot(dot_index, sim_instance: "Simulator", bar=None):
if sim_instance is None:
raise ValueError("sim_instance不能为空!")
new_dot = create_dot_instance(dot_index, sim_instance=sim_instance, bar=bar)
return new_dot
def create_dot_instance(class_name: str, sim_instance: "Simulator | None" = None, bar=None):
# 动态导入相应模块
module_name = f"zsim.sim_progress.Dot.Dots.{class_name}" # 假设你的类都在dot.DOTS模块中
try:
module = importlib.import_module(module_name) # 导入模块
class_obj = getattr(module, class_name) # 获取类对象
if bar:
dot_obj: Dot = class_obj(bar=bar, sim_instance=sim_instance)
else:
dot_obj: Dot = class_obj(sim_instance=sim_instance)
return dot_obj # 创建并返回类实例
except (ModuleNotFoundError, AttributeError) as e:
raise ValueError(f"Error loading class {class_name}: {e}")
================================================
FILE: zsim/sim_progress/Update/Update_Buff.py
================================================
from zsim.sim_progress.Buff import Buff
from zsim.sim_progress.Dot import BaseDot
from zsim.sim_progress.Enemy import Enemy
from zsim.sim_progress.Report import report_buff_to_queue, report_to_log
def update_time_related_effect(
DYNAMIC_BUFF_DICT: dict, timetick, exist_buff_dict: dict, enemy: Enemy
):
"""
更新一些和时间相关的效果,异常条、Buff、Dot
"""
update_anomaly_bar(timetick, enemy)
update_buff(DYNAMIC_BUFF_DICT, enemy, exist_buff_dict, timetick)
update_dot(enemy, timetick)
return DYNAMIC_BUFF_DICT
def update_buff(DYNAMIC_BUFF_DICT, enemy, exist_buff_dict, timetick):
"""
该函数用于更新当前正处于活跃状态的Buff,
并且根据时间或是其他规则判断这些Buff是否应该结束。
结束的Buff会被移除。
注意,该函数的运行位置会导致所有Buff于Ntick末尾消失的Buff在N+1tick的开头处理,
当然这大部分情况下不会影响正确性。
"""
for charname, sub_dynamic_buff_list in DYNAMIC_BUFF_DICT.items():
remove_buff_list = []
for _ in sub_dynamic_buff_list:
CheckBuff(_, charname)
# 首先根据Buff的结束行为是否复杂进行分流
if not _.ft.simple_exit_logic:
# 结束行为复杂的Buff,其结束逻辑由xexit()控制
try:
shoud_exit = _.logic.xexit(beneficiary=charname)
except TypeError:
raise TypeError(f"{_.ft.index}的xexit方法参数错误!") # noqa: B904
if not shoud_exit:
# 如果buff的xexit()函数认为buff不应该结束,则再记录一次层数。
report_buff_to_queue(charname, timetick, _.ft.index, _.dy.count, True, level=4)
else:
# 若buff的xexit()函数认为buff应该结束,则移除buff
remove_buff_list.append(_)
else:
# 处理完了结束行为较为复杂的Buff,现在来处理结束行为简单的Buff
# 对于alltime的buff,自然是每个tick都存在,所以每个tick都记录。
if _.ft.alltime:
report_buff_to_queue(charname, timetick, _.ft.index, _.dy.count, True, level=4)
continue
# 对于层数独立结算的buff,需要特殊判断;
if _.ft.individual_settled:
if len(_.dy.built_in_buff_box) <= 0:
# 层数为0时候结束
remove_buff_list.append(_)
continue
else:
process_individual_buff(_, timetick)
# 先更新层数,再report。
report_buff_to_queue(
charname, timetick, _.ft.index, _.dy.count, True, level=4
)
# 接下来处理的是层数不独立结算的buff
else:
# 层数不独立的buff,时间到点了就要结束。
if timetick > _.dy.endticks:
remove_buff_list.append(_)
continue
# 没结束的buffreport一下层数。
else:
report_buff_to_queue(
charname, timetick, _.ft.index, _.dy.count, True, level=4
)
else:
# 统一执行KickOut函数,移除buff
sub_exist_buff_dict = exist_buff_dict[charname]
for removed_buff in remove_buff_list:
KickOutBuff(
DYNAMIC_BUFF_DICT,
removed_buff,
charname,
enemy,
sub_exist_buff_dict,
timetick,
)
def process_individual_buff(_, timetick):
"""
针对层数独立结算的buff的tuple的独立结算。去除过期的tuple
"""
end_tuples_list = []
for tuples in _.dy.built_in_buff_box:
if tuples[1] < timetick:
end_tuples_list.append(tuples)
else:
for _end_tuples in end_tuples_list:
_.dy.built_in_buff_box.remove(_end_tuples)
_.dy.count = len(_.dy.built_in_buff_box)
def KickOutBuff(
DYNAMIC_BUFF_DICT: dict,
buff: Buff,
charname: str,
enemy,
sub_exist_buff_dict: dict,
timetick: int,
):
buff.end(timetick, sub_exist_buff_dict)
DYNAMIC_BUFF_DICT[charname].remove(buff)
report_to_log(
f"[Buff END]:{timetick}:{charname} 的 {buff.ft.index} 结束,已从动态列表移除", level=4
)
if buff.ft.is_debuff:
enemy.dynamic.dynamic_debuff_list.remove(buff)
def CheckBuff(_, charname):
"""
检查buff的参数情况。
"""
if not isinstance(_, Buff):
raise TypeError(f"{_}不是Buff类!")
if _.ft.is_debuff and charname != "enemy":
raise ValueError(f"{_.ft.index}是debuff但是却进入了{charname}的buff池!")
if (not _.ft.is_debuff) and charname == "enemy":
raise ValueError(f"{_.ft.index}是buff但是却在enemy的debuff池中!")
def update_dot(enemy: Enemy, timetick):
for _ in enemy.dynamic.dynamic_dot_list[:]:
if not isinstance(_, BaseDot.Dot):
raise TypeError(f"Enemy的dot列表中的{_}不是Dot类!")
if not _.ft.complex_exit_logic:
if timetick >= _.dy.end_ticks:
_.end(timetick)
enemy.dynamic.dynamic_dot_list.remove(_)
report_to_log(f"[Dot END]:{timetick}:{_.ft.index}结束,已从动态列表移除", level=4)
else:
exit_result = _.exit_judge(enemy=enemy)
# 不是所有的dot的退出函数都有返回,这里必须处理退出函数不返回内容的情况
if exit_result is None:
raise ValueError("复杂退出逻辑Dot的退出函数必须返回有效布尔值")
if exit_result:
_.end(timetick)
enemy.dynamic.dynamic_dot_list.remove(_)
report_to_log(f"[Dot END]:{timetick}:{_.ft.index}结束,已从动态列表移除", level=4)
def update_anomaly_bar(time_now: int, enemy: Enemy):
for element_type, bar in enemy.anomaly_bars_dict.items():
result = bar.check_myself(time_now)
if result:
setattr(
enemy.dynamic,
enemy.trans_anomaly_effect_to_str[element_type],
bar.active,
)
enemy.dynamic.active_anomaly_bar_dict[element_type] = None
================================================
FILE: zsim/sim_progress/Update/__init__.py
================================================
from .UpdateAnomaly import spawn_output, update_anomaly
__all__ = [
"spawn_output",
"update_anomaly",
]
================================================
FILE: zsim/sim_progress/__init__.py
================================================
================================================
FILE: zsim/sim_progress/anomaly_bar/Anomalies.py
================================================
from dataclasses import dataclass
from .AnomalyBarClass import AnomalyBar
@dataclass
class PhysicalAnomaly(AnomalyBar):
def __post_init__(self):
super().__post_init__()
self.element_type = 0
self.accompany_debuff = ["Buff-异常-畏缩"]
self.max_duration = 0
self.duration_buff_list = ["Buff-角色-简-核心被动-啮咬触发器"]
self.basic_max_duration = 600
self.duration_buff_key_list = [
"畏缩时间延长",
"所有异常时间延长",
"畏缩时间延长百分比",
"所有异常时间延长百分比",
]
def __hash__(self):
return hash(self.UUID)
@dataclass
class FireAnomaly(AnomalyBar):
def __post_init__(self):
super().__post_init__() # 调用父类的初始化方法
self.accompany_dot = "Ignite"
self.element_type = 1 # 火属性
self.basic_max_duration = 600
self.duration_buff_list = ["Buff-角色-柏妮思-组队被动-延长灼烧"]
self.max_duration = 0
self.duration_buff_key_list = [
"灼烧时间延长",
"所有异常时间延长",
"灼烧时间延长百分比",
"所有异常时间延长百分比",
]
@dataclass
class IceAnomaly(AnomalyBar):
def __post_init__(self):
super().__post_init__() # 调用父类的初始化方法
self.element_type = 2 # 冰属性
self.accompany_debuff = ["Buff-异常-霜寒"]
self.accompany_dot = "Freez"
self.basic_max_duration = 600
self.max_duration = 0
self.duration_buff_key_list = [
"霜寒时间延长",
"所有异常时间延长",
"霜寒时间延长百分比",
"所有异常时间延长百分比",
]
# 冻结时间可以延长失衡
@dataclass
class ElectricAnomaly(AnomalyBar):
def __post_init__(self):
super().__post_init__() # 调用父类的初始化方法
self.element_type = 3 # 电属性
self.accompany_dot = "Shock"
self.basic_max_duration = 600
self.duration_buff_list = ["Buff-角色-丽娜-组队被动-延长感电"]
self.max_duration = 0
self.duration_buff_key_list = [
"感电时间延长",
"所有异常时间延长",
"感电时间延长百分比",
"所有异常时间延长百分比",
]
@dataclass
class EtherAnomaly(AnomalyBar):
def __post_init__(self):
super().__post_init__() # 调用父类的初始化方法
self.element_type = 4 # 以太属性
self.accompany_dot = "Corruption"
self.basic_max_duration = 600
self.max_duration = 0
self.duration_buff_key_list = [
"侵蚀时间延长",
"所有异常时间延长",
"侵蚀时间延长百分比",
"所有异常时间延长百分比",
]
@dataclass
class FrostAnomaly(AnomalyBar):
def __post_init__(self):
super().__post_init__() # 调用父类的初始化方法
self.element_type = 5 # 烈霜属性(星见雅专属)
self.accompany_dot = "Freez"
self.basic_max_duration = 1200
self.accompany_debuff = ["Buff-异常-烈霜霜寒", "Buff-角色-雅-核心被动-霜灼"]
self.max_duration = 0
self.duration_buff_key_list = [
"烈霜霜寒时间延长",
"所有异常时间延长",
"烈霜霜寒时间延长百分比",
"所有异常时间延长百分比",
]
@dataclass
class AuricInkAnomaly(AnomalyBar):
def __post_init__(self):
super().__post_init__() # 调用父类的初始化方法
self.element_type = 6 # 玄墨侵蚀的属性也是以太
self.accompany_dot = "AuricInkCorruption"
self.basic_max_duration = 600
self.max_duration = 0
self.duration_buff_key_list = [
"玄墨侵蚀时间延长",
"所有异常时间延长",
"玄墨侵蚀时间延长百分比",
"所有异常时间延长百分比",
]
================================================
FILE: zsim/sim_progress/anomaly_bar/AnomalyBarClass.py
================================================
import uuid
from dataclasses import dataclass, field
from typing import TYPE_CHECKING
import numpy as np
from zsim.define import ElementType
if TYPE_CHECKING:
from zsim.sim_progress.Buff import Buff
from zsim.sim_progress.data_struct.single_hit import SingleHit
from zsim.sim_progress.Preload import SkillNode
from zsim.simulator.simulator_class import Simulator
@dataclass
class AnomalyBar:
"""
这是属性异常类的基类。其中包含了属性异常的基本属性,以及几个基本方法。
"""
sim_instance: "Simulator"
element_type: ElementType = 0 # 属性种类编号(1~5)
is_disorder: bool = False # 是否是紊乱实例
current_ndarray: np.ndarray = field(
default_factory=lambda: np.zeros((1, 1), dtype=np.float64)
) # 当前快照总和
current_anomaly: np.float64 = field(
default_factory=lambda: np.float64(0)
) # 当前已经累计的积蓄值
current_effective_anomaly: np.float64 = field(
default_factory=lambda: np.float64(0)
) # 有效积蓄值(参与快照的)
anomaly_times: int = 0 # 迄今为止触发过的异常次数
cd: int = 180 # 属性异常的内置CD,
last_active: int = 0 # 上一次属性异常的时间
max_anomaly: int | None = None # 最大积蓄值
ready: bool = True # 内置CD状态
accompany_debuff: list | None = None # 是否在激活时伴生debuff的index
accompany_dot: str | None = None # 是否在激活时伴生dot的index
active: bool | None = None # 当前异常条是否激活,这一属性和enemy下面的异常开关同步。
max_duration: int | None = None
duration_buff_list: list | None = None # 影响当前异常状态最大时长的buff名
duration_buff_key_list: list | None = None # 影响当前异常状态最大时长的buff效果关键字
basic_max_duration: int = 0 # 基础最大时间
UUID: uuid.UUID | None = None
activated_by: "SkillNode | None" = None
ndarray_box: list[tuple] | None = None
scaling_factor: float = 1.0 # 缩放比例,在计算伤害时会乘以该比例
settled: bool = False # 快照是否被结算过
rename_tag: str | None = None # 重命名标签
schedule_priority: int = 999 # 默认情况下,异常条的处理优先级为999,位于当前tick的最后。
@property
def rename(self) -> bool:
return self.rename_tag is not None
def __post_init__(self):
self.UUID = uuid.uuid4()
def __hash__(self):
return hash(self.UUID)
@property
def is_full(self):
assert self.max_anomaly is not None
return self.current_anomaly >= self.max_anomaly
def remaining_tick(self):
timetick = self.sim_instance.tick
assert self.max_duration is not None
remaining_tick = max(self.max_duration - self.duration(timetick), 0)
return remaining_tick
def duration(self, timetick: int):
duration = timetick - self.last_active
if self.max_duration is not None:
assert duration <= self.max_duration, "该异常早就结束了!不应该触发紊乱!"
else:
raise AssertionError("该异常的max_duration为None,无法判断是否过期!")
return duration
def update_snap_shot(self, new_snap_shot: tuple, single_hit: "SingleHit"):
"""
该函数是更新快照的核心函数。但是并不具备识别属性种类的功能。
所以需要在外部嵌套一个总函数,根据属性种类来执行不同属性的update函数。
"""
if not isinstance(new_snap_shot[2], np.ndarray):
raise TypeError("所传入的快照元组的第3个元素应该是np.ndarray!")
#
# new_ndarray = new_snap_shot[2].reshape(1, -1) # 将数据重塑为一行多列的形式
build_up_value = new_snap_shot[1] # 获取积蓄值
#
# assert self.current_ndarray is not None, "当前快照数组为None!"
# if self.current_ndarray.shape[1] != new_ndarray.shape[1]:
# # 扩展 current_ndarray 列数,保持已有数据,新增的部分会填充为零
# if self.current_ndarray.shape[1] < new_ndarray.shape[1]:
# # 扩展 current_ndarray 列数,增加零列
# new_shape = (1, new_ndarray.shape[1])
# extended_ndarray = np.zeros(new_shape, dtype=np.float64)
# # 将已有的数据复制到新的 ndarray 中
# extended_ndarray[:, : self.current_ndarray.shape[1]] = (
# self.current_ndarray
# )
# self.current_ndarray = extended_ndarray
# else:
# # 如果 current_ndarray 列数大于 new_ndarray 列数,直接裁剪 current_ndarray
# raise ValueError(
# f"传入的快照数组列数为{new_ndarray.shape[1]},小于快照缓存的列数!"
# )
#
# cal_result_1 = build_up_value * new_ndarray
# self.current_ndarray += cal_result_1
self.current_anomaly += build_up_value
# print(f"测试:{self.sim_instance.tick}tick:{single_hit.skill_tag}命中!积蓄了{build_up_value}点{ELEMENT_TYPE_MAPPING[new_snap_shot[0]]}属性积蓄!当前积蓄值为:{self.current_anomaly}")
if single_hit.effective_anomlay_buildup():
# 只有有效积蓄才会累计快照
if self.ndarray_box is None:
self.ndarray_box = []
self.ndarray_box.append(new_snap_shot)
def ready_judge(self, timenow):
if timenow - self.last_active >= self.cd:
self.ready = True
def check_myself(self, timenow: int):
assert self.max_duration is not None, "该异常的max_duration为None,无法判断是否过期!"
if self.active and (self.last_active + self.max_duration < timenow):
self.active = False
return True
return False
def change_info_cause_active(
self,
timenow: int,
skill_node: "SkillNode",
dynamic_buff_dict: dict[str, list["Buff"]],
):
"""
属性异常激活时,必要的信息更新
"""
char_cid = int(skill_node.skill_tag.strip().split("_")[0])
self.ready = False
self.anomaly_times += 1
self.last_active = timenow
self.active = True
self.activated_by = skill_node
self.__get_max_duration(dynamic_buff_dict, char_cid)
# self.sim_instance.schedule_data.change_process_state()
# print(
# f"{skill_node.char_name}的技能【{self.activated_by.skill_tag}】激活了【{ELEMENT_TYPE_MAPPING[self.element_type]}】属性的异常状态!\n技能为{skill_node.skill_tag}, preload_tick为{skill_node.preload_tick}, end_tick为{skill_node.end_tick},tick_list为{skill_node.tick_list}"
# )
def reset_current_info_cause_output(self):
"""
重置和属性积蓄条以及快照相关的信息。
该函数通常位于抛出异常实例之前调用,
"""
self.current_effective_anomaly = np.float64(0)
self.current_anomaly = np.float64(0)
self.current_ndarray = np.zeros((1, self.current_ndarray.shape[0]), dtype=np.float64)
self.ndarray_box = []
self.settled = False
def get_buildup_pct(self):
if self.max_anomaly is None:
return 0
if self.is_full:
return 1
pct = self.current_anomaly / self.max_anomaly
return pct
def reset_myself(self):
self.current_ndarray = np.zeros((1, 1), dtype=np.float64)
self.current_anomaly = np.float64(0)
self.anomaly_times = 0
self.last_active = 0
self.ready = True
self.active = False
self.max_anomaly = None
self.ndarray_box = []
def __get_max_duration(self, dynamic_buff_list, anomaly_from: int | str) -> None:
"""通过Buff计算当前异常的最大持续时间"""
if self.duration_buff_list is None:
self.max_duration = self.basic_max_duration
# print(f'属性类型为{self.element_type}的异常不存在影响持续时间的Buff,所以直接使用基础值{self.basic_max_duration}')
return
max_duration_delta_fix = 0
max_duration_delta_pct = 0
for _buff_index in self.duration_buff_list:
enemy_buff_list = dynamic_buff_list.get("enemy")
for buffs in enemy_buff_list:
if _buff_index == buffs.ft.index and buffs.dy.active:
for keys in self.duration_buff_key_list: # type: ignore
if keys in buffs.effect_dct.keys():
if "百分比" in keys:
max_duration_delta_pct += buffs.dy.count * buffs.effect_dct.get(
keys
)
else:
max_duration_delta_fix += buffs.dy.count * buffs.effect_dct.get(
keys
)
self.max_duration = max(
self.basic_max_duration * (1 + max_duration_delta_pct) + max_duration_delta_fix,
0,
)
# print(f'属性类型为{self.element_type}的异常激活了,本次激活的最大时长为{self.max_duration}')
@staticmethod
def create_new_from_existing(existing_instance):
"""
通过复制已有实例的状态来创建新实例
"""
new_instance = AnomalyBar.__new__(AnomalyBar) # 不调用构造函数
new_instance.__dict__ = existing_instance.__dict__.copy() # 复制原实例的属性
return new_instance
def __deepcopy__(self, memo):
"""AnomalyBar的deepcopy方法,需要绕开Buff"""
import copy
cls = self.__class__
new_anomaly_bar = cls.__new__(cls)
memo[id(self)] = new_anomaly_bar
# 安全复制属性
for key, value in self.__dict__.items():
if key == "sim_instance":
# 直接复制 Simulator 引用(避免深拷贝 Buff)
setattr(new_anomaly_bar, key, value)
elif key == "activated_by" and hasattr(value, "skill"):
# 复制 SkillNode 但不深拷贝其内部技能对象
new_skill_node = copy.copy(value)
setattr(new_anomaly_bar, key, new_skill_node)
elif key == "current_ndarray" and value is not None:
# 使用 numpy 的安全复制方法
setattr(new_anomaly_bar, key, value.copy())
elif key == "current_anomaly" and value is not None:
# 安全复制 np.float64
setattr(new_anomaly_bar, key, copy.copy(value))
elif key == "UUID":
# 生成新的 UUID
setattr(new_anomaly_bar, key, uuid.uuid4())
else:
try:
# 尝试标准深拷贝
setattr(new_anomaly_bar, key, copy.deepcopy(value, memo))
except TypeError:
# 无法深拷贝时回退到浅拷贝
setattr(new_anomaly_bar, key, value)
return new_anomaly_bar
def anomaly_settled(self):
"""结算快照!"""
if self.settled:
raise RuntimeError(
"【异常条结算警告】当前异常条快照已经被结算过一次了,请检查业务逻辑,找出重复结算的时间点!"
)
total_array = np.zeros((1, 1), dtype=np.float64)
effective_buildup: np.float64 = np.float64(0)
while self.ndarray_box:
_tuples = self.ndarray_box.pop()
_element_type = _tuples[0]
_array = _tuples[2].reshape(1, -1)
_build_up = _tuples[1]
if total_array.shape[1] != _array.shape[1]:
if total_array.shape[1] < _array.shape[1]:
new_shape = (1, _array.shape[1])
extended_ndarray = np.zeros(new_shape, dtype=np.float64)
# 将已有的数据复制到新的 ndarray 中
extended_ndarray[:, : total_array.shape[1]] = total_array
total_array = extended_ndarray
else:
raise ValueError(f"传入的快照数组列数为{_array.shape[1]},小于快照缓存的列数!")
total_array += _array * _build_up
effective_buildup += _build_up
self.current_effective_anomaly = effective_buildup
self.current_ndarray = total_array / self.current_effective_anomaly
self.settled = True
================================================
FILE: zsim/sim_progress/anomaly_bar/CopyAnomalyForOutput.py
================================================
import uuid
from typing import TYPE_CHECKING
from .AnomalyBarClass import AnomalyBar
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
class Disorder(AnomalyBar):
"""
紊乱类,当这个类被创建,将会在__init__方法中自动调用__dict__方法,立刻复制父类的所有状态。
注意,语法上,在创建Disorder实例时,需要在括号里传入需要复制的父类实例。
Disorder会打开自身的is_disorder
"""
def __init__(self, Output_bar: AnomalyBar, sim_instance: "Simulator", **kwargs):
super().__init__(sim_instance=sim_instance)
self.__dict__.update(Output_bar.__dict__)
self.sim_instance = sim_instance
self.is_disorder = True
activate_by = kwargs.get("active_by", None)
self.activate_by = activate_by
# 复制父类的所有属性,主要是快照、积蓄总值、属性类型。
self.source_uuid = self.UUID
self.UUID = uuid.uuid4()
def __hash__(self):
"""使对象可哈希"""
return hash(self.UUID)
class NewAnomaly(AnomalyBar):
"""
普通的异常类,仅用于非紊乱的属性异常更新。
"""
def __init__(self, Output_bar: AnomalyBar, active_by, sim_instance: "Simulator"):
super().__init__(sim_instance=sim_instance)
self.__dict__.update(Output_bar.__dict__)
self.sim_instance = sim_instance
self.activate_by = active_by
self.source_uuid = self.UUID
self.UUID = uuid.uuid4()
def __hash__(self):
"""使对象可哈希"""
return hash(self.UUID)
class PolarityDisorder(Disorder):
"""
柳的极性紊乱(不含核心被动的紊乱基础倍率增加)
极性紊乱的计算公式为:
极性紊乱伤害 = 紊乱伤害 * 本次极性紊乱倍率(解锁2命后可变)+ 附加3200% * 精通的伤害
构造时,不仅需要提供被复制的异常条,还需要提供连击次数(用来计算极性紊乱比例),还需要提供触发者ID(CID或者enemy)
"""
def __init__(
self,
Output_bar: AnomalyBar,
_polarity_disorder_ratio,
active_by,
sim_instance: "Simulator",
):
super().__init__(Output_bar, active_by=active_by, sim_instance=sim_instance)
self.__dict__.update(Output_bar.__dict__)
self.sim_instance = sim_instance
self.is_disorder = True
self.polarity_disorder_ratio = (
_polarity_disorder_ratio # 极性紊乱对比紊乱的缩放比例(已经考虑连击次数)
)
self.additional_dmg_ap_ratio = 32 # 精通附加伤害的倍率!
self.activate_by = active_by
self.source_uuid = self.UUID
self.UUID = uuid.uuid4()
def __hash__(self):
"""使对象可哈希"""
return hash(self.UUID)
class DirgeOfDestinyAnomaly(AnomalyBar):
"""薇薇安的核心被动「命运悲歌」会重复触发一次异常伤害,
该伤害具有属性异常的全部相同参数,同时具有一个缩放倍率。"""
def __init__(self, Output_bar: AnomalyBar, active_by, sim_instance: "Simulator"):
super().__init__(sim_instance=sim_instance)
self.__dict__.update(Output_bar.__dict__)
self.sim_instance = sim_instance
self.activate_by = active_by
self.anomaly_dmg_ratio = 0 # 属性异常伤害的缩放倍率
self.source_uuid = self.UUID
self.UUID = uuid.uuid4()
def __hash__(self):
"""使对象可哈希"""
return hash(self.UUID)
================================================
FILE: zsim/sim_progress/anomaly_bar/__init__.py
================================================
from .Anomalies import (
AuricInkAnomaly,
ElectricAnomaly,
EtherAnomaly,
FireAnomaly,
FrostAnomaly,
IceAnomaly,
PhysicalAnomaly,
)
from .AnomalyBarClass import AnomalyBar
from .CopyAnomalyForOutput import Disorder
__all__ = [
"AnomalyBar",
"PhysicalAnomaly",
"FireAnomaly",
"IceAnomaly",
"ElectricAnomaly",
"EtherAnomaly",
"FrostAnomaly",
"Disorder",
"AuricInkAnomaly",
]
================================================
FILE: zsim/sim_progress/data_struct/ActionStack.cpp
================================================
#include "ActionStack.h"
// 构造函数
static int PyActionStack_init(PyActionStack* self, PyObject* args, PyObject* kwargs) {
self->action_stack = new ActionStack();
return 0;
}
// 析构函数
static void PyActionStack_dealloc(PyActionStack* self) {
delete self->action_stack;
Py_TYPE(self)->tp_free((PyObject*)self);
}
// 迭代器构造函数
static int PyActionStackIterator_init(PyActionStackIterator* self, PyObject* args, PyObject* kwargs) {
PyObject* action_stack_obj;
if (!PyArg_ParseTuple(args, "O!", &PyActionStackType, &action_stack_obj)) {
return -1;
}
self->iterator = new ActionStack::ActionStackIterator(((PyActionStack*)action_stack_obj)->action_stack);
return 0;
}
// 迭代器析构函数
static void PyActionStackIterator_dealloc(PyActionStackIterator* self) {
delete self->iterator;
Py_TYPE(self)->tp_free((PyObject*)self);
}
// push 方法
static PyObject* PyActionStack_push(PyActionStack* self, PyObject* args) {
PyObject* item;
if (!PyArg_ParseTuple(args, "O", &item)) {
return NULL;
}
self->action_stack->push(item);
Py_RETURN_NONE;
}
// pop 方法
static PyObject* PyActionStack_pop(PyActionStack* self, PyObject* args) {
try {
PyObject* result = self->action_stack->pop();
Py_INCREF(result);
return result;
} catch (const std::out_of_range& e) {
PyErr_SetString(PyExc_IndexError, e.what());
return NULL;
}
}
// peek 方法
static PyObject* PyActionStack_peek(PyActionStack* self, PyObject* args) {
try {
PyObject* result = self->action_stack->peek();
Py_INCREF(result);
return result;
} catch (const std::out_of_range& e) {
PyErr_SetString(PyExc_IndexError, e.what());
return NULL;
}
}
// is_empty 方法
static PyObject* PyActionStack_is_empty(PyActionStack* self, PyObject* args) {
return Py_BuildValue("b", self->action_stack->is_empty());
}
// peek_bottom 方法
static PyObject* PyActionStack_peek_bottom(PyActionStack* self, PyObject* args) {
try {
PyObject* result = self->action_stack->peek_bottom();
Py_INCREF(result);
return result;
} catch (const std::out_of_range& e) {
PyErr_SetString(PyExc_IndexError, e.what());
return NULL;
}
}
// size 方法
static PyObject* PyActionStack_size(PyActionStack* self, PyObject* args) {
return Py_BuildValue("n", self->action_stack->size());
}
// to_string 方法
static PyObject* PyActionStack_to_string(PyActionStack* self, PyObject* args) {
std::string result = self->action_stack->to_string();
return Py_BuildValue("s", result.c_str());
}
// __getitem__ 方法
static PyObject* PyActionStack_getitem(PyActionStack* self, PyObject* args) {
Py_ssize_t index;
if (!PyArg_ParseTuple(args, "n", &index)) {
return NULL;
}
try {
PyObject* result = self->action_stack->operator[](index);
Py_INCREF(result);
return result;
} catch (const std::out_of_range& e) {
PyErr_SetString(PyExc_IndexError, e.what());
return NULL;
}
}
// __eq__ 方法
static PyObject* PyActionStack_eq(PyActionStack* self, PyObject* other) {
if (!PyObject_TypeCheck(other, &PyActionStackType)) {
Py_RETURN_FALSE;
}
PyActionStack* other_stack = (PyActionStack*)other;
return Py_BuildValue("b", *self->action_stack == *other_stack->action_stack);
}
// __ne__ 方法
static PyObject* PyActionStack_ne(PyActionStack* self, PyObject* other) {
if (!PyObject_TypeCheck(other, &PyActionStackType)) {
Py_RETURN_TRUE;
}
PyActionStack* other_stack = (PyActionStack*)other;
return Py_BuildValue("b", *self->action_stack != *other_stack->action_stack);
}
// __iter__ 方法
static PyObject* PyActionStack_iter(PyActionStack* self) {
PyActionStackIterator* iterator = (PyActionStackIterator*)PyObject_New(PyActionStackIterator, &PyActionStackIteratorType);
if (iterator == NULL) {
return NULL;
}
if (PyActionStackIterator_init(iterator, (PyObject*)self, NULL) < 0) {
Py_DECREF(iterator);
return NULL;
}
return (PyObject*)iterator;
}
// 迭代器 __next__ 方法
static PyObject* PyActionStackIterator_next(PyActionStackIterator* self) {
return self->iterator->next();
}
// 类型定义
PyTypeObject PyActionStackType = {
PyVarObject_HEAD_INIT(NULL, 0)
"ActionStack.ActionStack", // tp_name
sizeof(PyActionStack), // tp_basicsize
0, // tp_itemsize
(destructor)PyActionStack_dealloc, // tp_dealloc
0, // tp_print
0, // tp_getattr
0, // tp_setattr
0, // tp_reserved
0, // tp_repr
0, // tp_as_number
0, // tp_as_sequence
0, // tp_as_mapping
0, // tp_hash
0, // tp_call
0, // tp_str
0, // tp_getattro
0, // tp_setattro
0, // tp_as_buffer
Py_TPFLAGS_DEFAULT, // tp_flags
"A simple action stack.", // tp_doc
0, // tp_traverse
0, // tp_clear
0, // tp_richcompare
0, // tp_weaklistoffset
0, // tp_iter
0, // tp_iternext
PyActionStack_methods, // tp_methods
0, // tp_members
0, // tp_getset
0, // tp_base
0, // tp_dict
0, // tp_descr_get
0, // tp_descr_set
0, // tp_dictoffset
(initproc)PyActionStack_init, // tp_init
0, // tp_alloc
PyType_GenericNew, // tp_new
};
// 迭代器类型定义
PyTypeObject PyActionStackIteratorType = {
PyVarObject_HEAD_INIT(NULL, 0)
"ActionStack.ActionStackIterator", // tp_name
sizeof(PyActionStackIterator), // tp_basicsize
0, // tp_itemsize
(destructor)PyActionStackIterator_dealloc, // tp_dealloc
0, // tp_print
0, // tp_getattr
0, // tp_setattr
0, // tp_reserved
0, // tp_repr
0, // tp_as_number
0, // tp_as_sequence
0, // tp_as_mapping
0, // tp_hash
0, // tp_call
0, // tp_str
0, // tp_getattro
0, // tp_setattro
0, // tp_as_buffer
Py_TPFLAGS_DEFAULT, // tp_flags
"ActionStack iterator object", // tp_doc
0, // tp_traverse
0, // tp_clear
0, // tp_richcompare
0, // tp_weaklistoffset
PyObject_SelfIter, // tp_iter
(iternextfunc)PyActionStackIterator_next, // tp_iternext
PyActionStackIterator_methods, // tp_methods
0, // tp_members
0, // tp_getset
0, // tp_base
0, // tp_dict
0, // tp_descr_get
0, // tp_descr_set
0, // tp_dictoffset
(initproc)PyActionStackIterator_init, // tp_init
0, // tp_alloc
PyType_GenericNew, // tp_new
};
// 模块初始化
static PyModuleDef ActionStackModule = {
PyModuleDef_HEAD_INIT,
"ActionStack",
"A simple action stack module.",
-1,
NULL, NULL, NULL, NULL, NULL
};
PyMODINIT_FUNC PyInit_ActionStack(void) {
PyObject* m;
if (PyType_Ready(&PyActionStackType) < 0) {
return NULL;
}
if (PyType_Ready(&PyActionStackIteratorType) < 0) {
return NULL;
}
m = PyModule_Create(&ActionStackModule);
if (m == NULL) {
return NULL;
}
Py_INCREF(&PyActionStackType);
if (PyModule_AddObject(m, "ActionStack", (PyObject*)&PyActionStackType) < 0) {
Py_DECREF(&PyActionStackType);
Py_DECREF(m);
return NULL;
}
return m;
}
================================================
FILE: zsim/sim_progress/data_struct/ActionStack.h
================================================
#ifndef ACTIONSTACK_H
#define ACTIONSTACK_H
#define PY_SSIZE_T_CLEAN
#include
#include
#include
// 定义 ActionStack 类
class ActionStack {
public:
ActionStack() {}
~ActionStack() {
for (PyObject* item : stack) {
Py_DECREF(item);
}
}
void push(PyObject* item) {
Py_INCREF(item);
stack.push_back(item);
if (stack.size() > 2) {
Py_DECREF(stack.front());
stack.erase(stack.begin());
}
}
PyObject* pop() {
if (is_empty()) {
throw std::out_of_range("Stack is empty");
}
PyObject* item = stack.back();
stack.pop_back();
return item;
}
PyObject* peek() const {
if (is_empty()) {
throw std::out_of_range("Stack is empty");
}
return stack.back();
}
bool is_empty() const {
return stack.empty();
}
PyObject* peek_bottom() const {
if (is_empty()) {
throw std::out_of_range("Stack is empty");
}
return stack.front();
}
size_t size() const {
return stack.size();
}
std::string to_string() const {
std::string result = "[";
for (size_t i = 0; i < stack.size(); ++i) {
PyObject* item = stack[i];
PyObject* str = PyObject_Str(item);
if (str == NULL) {
return "Error converting to string";
}
result += PyUnicode_AsUTF8(str);
Py_DECREF(str);
if (i < stack.size() - 1) {
result += ", ";
}
}
result += "]";
return result;
}
PyObject* operator[](size_t index) const {
if (index >= stack.size()) {
throw std::out_of_range("Index out of range");
}
return stack[index];
}
bool operator==(const ActionStack& other) const {
if (stack.size() != other.stack.size()) {
return false;
}
for (size_t i = 0; i < stack.size(); ++i) {
if (stack[i] != other.stack[i]) {
return false;
}
}
return true;
}
bool operator!=(const ActionStack& other) const {
return !(*this == other);
}
// 迭代器类
class ActionStackIterator {
public:
ActionStackIterator(ActionStack* stack) : stack_(stack), index_(0) {}
PyObject* next() {
if (index_ >= stack_->size()) {
PyErr_SetString(PyExc_StopIteration, "No more items");
return NULL;
}
PyObject* item = stack_->operator[](index_);
Py_INCREF(item);
index_++;
return item;
}
private:
ActionStack* stack_;
size_t index_;
};
private:
std::vector stack;
};
// 定义 ActionStack 对象类型
typedef struct {
PyObject_HEAD
ActionStack* action_stack;
} PyActionStack;
// 定义 ActionStack 迭代器对象类型
typedef struct {
PyObject_HEAD
ActionStack::ActionStackIterator* iterator;
} PyActionStackIterator;
// 方法声明
static int PyActionStack_init(PyActionStack* self, PyObject* args, PyObject* kwargs);
static void PyActionStack_dealloc(PyActionStack* self);
static int PyActionStackIterator_init(PyActionStackIterator* self, PyObject* args, PyObject* kwargs);
static void PyActionStackIterator_dealloc(PyActionStackIterator* self);
static PyObject* PyActionStack_push(PyActionStack* self, PyObject* args);
static PyObject* PyActionStack_pop(PyActionStack* self, PyObject* args);
static PyObject* PyActionStack_peek(PyActionStack* self, PyObject* args);
static PyObject* PyActionStack_is_empty(PyActionStack* self, PyObject* args);
static PyObject* PyActionStack_peek_bottom(PyActionStack* self, PyObject* args);
static PyObject* PyActionStack_size(PyActionStack* self, PyObject* args);
static PyObject* PyActionStack_to_string(PyActionStack* self, PyObject* args);
static PyObject* PyActionStack_getitem(PyActionStack* self, PyObject* args);
static PyObject* PyActionStack_eq(PyActionStack* self, PyObject* other);
static PyObject* PyActionStack_ne(PyActionStack* self, PyObject* other);
static PyObject* PyActionStack_iter(PyActionStack* self);
static PyObject* PyActionStackIterator_next(PyActionStackIterator* self);
// 方法定义
static PyMethodDef PyActionStack_methods[] = {
{"push", (PyCFunction)PyActionStack_push, METH_VARARGS, "Push an item onto the stack."},
{"pop", (PyCFunction)PyActionStack_pop, METH_NOARGS, "Pop an item from the stack."},
{"peek", (PyCFunction)PyActionStack_peek, METH_NOARGS, "Peek at the top item of the stack."},
{"is_empty", (PyCFunction)PyActionStack_is_empty, METH_NOARGS, "Check if the stack is empty."},
{"peek_bottom", (PyCFunction)PyActionStack_peek_bottom, METH_NOARGS, "Peek at the bottom item of the stack."},
{"size", (PyCFunction)PyActionStack_size, METH_NOARGS, "Get the size of the stack."},
{"to_string", (PyCFunction)PyActionStack_to_string, METH_NOARGS, "Get the string representation of the stack."},
{"__getitem__", (PyCFunction)PyActionStack_getitem, METH_VARARGS, "Get item by index."},
{"__eq__", (PyCFunction)PyActionStack_eq, METH_O, "Check equality with another ActionStack."},
{"__ne__", (PyCFunction)PyActionStack_ne, METH_O, "Check inequality with another ActionStack."},
{"__iter__", (PyCFunction)PyActionStack_iter, METH_NOARGS, "Return an iterator object."},
{NULL} // Sentinel
};
// 迭代器方法定义
static PyMethodDef PyActionStackIterator_methods[] = {
{"__next__", (PyCFunction)PyActionStackIterator_next, METH_NOARGS, "Return the next item from the iterator."},
{NULL} // Sentinel
};
// 类型定义
extern PyTypeObject PyActionStackType;
extern PyTypeObject PyActionStackIteratorType;
#endif // ACTIONSTACK_H
================================================
FILE: zsim/sim_progress/data_struct/ActionStack.py
================================================
from collections import defaultdict
from typing import TYPE_CHECKING, Generic, TypeVar
from zsim.define import SWAP_CANCEL
if TYPE_CHECKING:
from zsim.sim_progress.Preload import SkillNode
NODE_T = TypeVar("NODE_T", bound=SkillNode)
else:
NODE_T = TypeVar("NODE_T", bound="SkillNode")
class BaseStack(Generic[NODE_T]):
"""通用栈结构的基类"""
def __init__(self, length: int):
self.length = length
self.stack: list[NODE_T] = []
def push(self, item: NODE_T):
self.stack.append(item)
if len(self.stack) > self.length:
self.stack.pop(0)
def pop(self) -> NODE_T | None:
if self.is_empty():
return None
return self.stack.pop()
def peek(self) -> NODE_T | None:
if self.is_empty():
return None
return self.stack[-1]
def is_empty(self) -> bool:
return len(self.stack) == 0
def peek_bottom(self) -> NODE_T | None:
if self.is_empty():
return None
return self.stack[0]
def reset(self):
self.stack = []
def __len__(self):
return len(self.stack)
def __str__(self):
return str(self.stack)
def __iter__(self):
return iter(self.stack)
def __getitem__(self, index) -> NODE_T:
return self.stack[index]
def __eq__(self, value: object) -> bool:
return self.stack == value or self.stack == getattr(value, "stack", None)
def __ne__(self, value: object) -> bool:
return not self.__eq__(value)
class ActionStack(BaseStack[NODE_T]):
"""
这个动作栈无法记录所有的动作,只能记录所有的前台角色的主动技能。
功能类似于wow的插件TrufigGCD。但是长度只有2,因为只需要记录“上一个动作”和“当前动作”
"""
def __init__(self, length: int = 2):
# 初始化一个空的栈,用列表作为基础
"""
关于动作栈的更新:
在更新了合轴模式后,部分依赖检测ActionStack的状态来判断的函数会出现错误。
因为旧版本的ActionStack只能记录“全队上一个动作”,但是如果同一个tickPreload阶段抛出了多个动作,
那么ActionStack只能记录最后一个动作,导致部分该触发的buff无法触发。
因此,我们更新了ActionStack的结构,为其增加了personal_stack属性,使其可以记录“每个角色上一个动作”。
并且,我们还更新了ActionStack的各个方法,为它们增加了key参数,当我们打开合轴模式,并且传入Key参数时,
pop、peek方法会返回对应角色的上一个动作,
但是相应的,如果我们没有开启合轴模式,那么传入Key参数时就会报错。
"""
super().__init__(length)
if SWAP_CANCEL:
self.personal_stack: defaultdict[str, list[NODE_T]] = defaultdict(list)
self._swap_cancel_warning_printed = False # 标志变量,用于控制警告信息只打印一次
def push(self, item: NODE_T):
"""向栈中压入一个元素,如果栈内元素超过2个,移除最早的元素"""
if SWAP_CANCEL:
key = item.mission_character # type: ignore
if key:
self.personal_stack[key].append(item)
if len(self.personal_stack[key]) > self.length:
self.personal_stack[key].pop(0)
# 调用父类的push方法
super().push(item)
def pop(self, /, key: str | None = None) -> NODE_T | None:
"""从栈顶弹出一个元素"""
if key:
if not SWAP_CANCEL:
raise ValueError("往ActionStack的pop方法中传入key参数时,合轴模式必须开启!")
if key not in self.personal_stack or len(self.personal_stack[key]) == 0:
return None
pop_item = self.personal_stack[key].pop()
return pop_item
else:
return super().pop()
def peek(self, /, key: str | None = None) -> NODE_T | None:
"""查看栈顶元素"""
if key:
if not SWAP_CANCEL:
raise ValueError("往ActionStack的peek方法中传入key参数时,合轴模式必须开启!")
if key not in self.personal_stack or len(self.personal_stack[key]) == 0:
return None
return self.personal_stack[key][-1]
else:
if SWAP_CANCEL and not self._swap_cancel_warning_printed:
print(
"Warning: 在开启合轴模式的情况下,在调用ActionStack的peek方法时并未传入key参数!\n这会导致在含有多个动作的tick,peek方法只会返回最后一个动作,从而让部分buff无法正常触发!"
)
self._swap_cancel_warning_printed = True # 标记为已打印
return super().peek()
def peek_bottom(self, /, key: str | None = None) -> NODE_T | None:
"""查看栈底元素"""
if key:
if not SWAP_CANCEL:
raise ValueError(
"往ActionStack的peek_bottom方法中传入key参数时,合轴模式必须开启!"
)
if key not in self.personal_stack or len(self.personal_stack[key]) == 0:
return None
return self.personal_stack[key][0]
else:
return super().peek_bottom()
def reset_myself(self):
if SWAP_CANCEL:
self.personal_stack = defaultdict(list)
self._swap_cancel_warning_printed = False # 标志变量,用于控制警告信息只打印一次
self.reset()
class NodeStack(BaseStack[NODE_T]):
def __init__(self, length: int = 3):
super().__init__(length)
def peek_index(self, index: int) -> NODE_T | None:
if self.is_empty():
return None
if index > len(self.stack):
# print(f"index out of range, 当前stack长度为{len(self.stack)}")
return None
return self.stack[-index]
def get_effective_node(self) -> NODE_T | None:
"""排除附加伤害技能,来获取有效的技能。"""
if self.is_empty():
return None
else:
i = 1
while i <= len(self.stack):
node = self.stack[-i]
if node.is_additional_damage:
i += 1
continue
return node
return None
def get_on_field_node(self, tick_now: int) -> NODE_T | None:
"""
这个函数是NodeStack的内置方法,用来获取当前Stack中的前台技能
鉴于合轴模式中,各角色的技能情况比较复杂,所以这个函数返回的结果并不一定准确。
1、当目前场上没有node时,返回None
2、当目前场上只有1个ndoe时,无论这个node是什么类型,都返回该node
3、当目前场上存在多个node时,应返回最新的那个主动动作的SkillNode
"""
_exist_node_list: list[NODE_T] = []
_active_node_now = False
for _node in self.stack:
if _node.end_tick >= tick_now:
if _node.active_generation:
_active_node_now = True
_exist_node_list.append(_node)
# print([nodes.skill_tag for nodes in _exist_node_list])
if len(_exist_node_list) == 0:
return None
elif len(_exist_node_list) == 1:
return _exist_node_list[0]
elif len(_exist_node_list) > 1:
if _active_node_now:
return max(
(x for x in _exist_node_list if x.active_generation),
key=lambda x: x.preload_tick,
)
else:
"""当场上的node全部都是被动动作时,只取其中最新的那个。"""
return max((x for x in _exist_node_list), key=lambda x: x.preload_tick)
return None
def last_node_is_end(self, tick) -> bool:
"""判断上一个skillnode是否结束"""
last_node = self.peek()
if last_node is None:
return True
return last_node.end_tick <= tick
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/AliceCinema1BladeEtquitteRecoverListener.py
================================================
from typing import TYPE_CHECKING
from zsim.define import ALICE_REPORT
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.sim_progress.Character.Alice import Alice
from zsim.simulator.simulator_class import Simulator
class AliceCinema1BladeEtquitteRecoverListener(BaseListener):
"""该监听器是爱丽丝第一影画的剑仪值回复监听器"""
def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None):
super().__init__(listener_id, sim_instance=sim_instance)
self.char: "Alice | None" = None
self.blade_etquitte_value = 25
def listening_event(self, event, signal: LBS, **kwargs):
"""监听到极性强击信号时,激活"""
if self.char is None:
char_obj = self.sim_instance.char_data.find_char_obj(CID=1401)
from zsim.sim_progress.Character.Alice import Alice
if not isinstance(char_obj, Alice):
raise TypeError(
f"【爱丽丝1画监听器警告】获取的角色不是Alice类型,而是{type(char_obj)}"
)
self.char = char_obj
if self.char.cinema < 1:
raise ValueError(
f"【爱丽丝1画监听器警告】检测到{self.char.cinema}画的爱丽丝企图创建1画相关监听器,请检查初始化函数。"
)
if signal != LBS.POLARIZED_ASSAULT_SPAWN:
return
self.listener_active()
def listener_active(self, **kwargs):
if self.char is not None:
if ALICE_REPORT:
self.sim_instance.schedule_data.change_process_state()
print(
f"【爱丽丝事件】【1画】监听到极性强击信号,即将为爱丽丝回复{self.blade_etquitte_value}点剑仪值!"
)
self.char.update_blade_etiquette(update_obj=self.blade_etquitte_value)
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/AliceCinema1DefReduceListener.py
================================================
from typing import TYPE_CHECKING
from zsim.define import ALICE_REPORT
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.sim_progress.anomaly_bar import AnomalyBar
from zsim.sim_progress.Character.Alice import Alice
from zsim.sim_progress.Character.character import Character
from zsim.simulator.simulator_class import Simulator
class AliceCinema1DefReduceListener(BaseListener):
"""该监听器是爱丽丝第一影画的强击Buff的监听器,当监听到强击生成信号时,给敌人挂上减防debuff"""
def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None):
super().__init__(listener_id, sim_instance=sim_instance)
self.char: "Character | None | Alice" = None
self.buff_index = "Buff-角色-爱丽丝-影画-1画-减防"
def listening_event(self, event: "AnomalyBar", signal: LBS, **kwargs):
"""监听到紊乱信号时,激活"""
if self.char is None:
char_obj = self.sim_instance.char_data.find_char_obj(CID=1401)
assert char_obj is not None, (
"【爱丽丝1画监听器警告】检测到爱丽丝1画相关监听器初始化时,无法找到爱丽丝对象,请检查初始化函数。"
)
self.char = char_obj
if self.char.cinema < 1:
raise ValueError(
f"【爱丽丝1画监听器警告】检测到{self.char.cinema}画的爱丽丝企图创建1画相关监听器,请检查初始化函数。"
)
# 过滤掉非爱丽丝激活的强击事件
if signal not in [LBS.ASSAULT_SPAWN, LBS.POLARIZED_ASSAULT_SPAWN]:
return
else:
from zsim.sim_progress.Preload import SkillNode
assert isinstance(event.activated_by, SkillNode), (
"【爱丽丝1画监听器警告】检测到爱丽丝1画相关监听器激活时,传入的异常条的Activated_by属性为None"
)
if event.activated_by.char_name != "爱丽丝":
return
self.listener_active()
def listener_active(self, **kwargs):
from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy
buff_add_strategy(self.buff_index, benifit_list=["enemy"], sim_instance=self.sim_instance)
if ALICE_REPORT:
self.sim_instance.schedule_data.change_process_state()
print("【爱丽丝事件】【1画】检测到爱丽丝触发强击,目标防御力在接下来的30秒内降低20%")
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/AliceCinema2DisorderDmgBonus.py
================================================
from typing import TYPE_CHECKING
from zsim.define import ALICE_REPORT
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from ...anomaly_bar.CopyAnomalyForOutput import Disorder, PolarityDisorder
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.sim_progress.Character.Alice import Alice
from zsim.sim_progress.Character.character import Character
from zsim.simulator.simulator_class import Simulator
class AliceCinema2DisorderDmgBonus(BaseListener):
"""这个监听器的作用是监听紊乱事件来触发2画紊乱伤害提升Buff"""
def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None):
super().__init__(listener_id, sim_instance=sim_instance)
self.char: "Character | None | Alice" = None
self.buff_index = "Buff-角色-爱丽丝-影画-2画-紊乱伤害提升"
def listening_event(self, event, signal: LBS, **kwargs):
"""监听到紊乱生成信号时,激活"""
if self.char is None:
char_obj = self.sim_instance.char_data.find_char_obj(CID=1401)
assert char_obj is not None, (
"【爱丽丝2画监听器警告】检测到爱丽丝2画相关监听器初始化时,无法找到爱丽丝对象,请检查初始化函数。"
)
self.char = char_obj
if self.char.cinema < 2:
raise ValueError(
f"【爱丽丝2画监听器警告】检测到{self.char.cinema}画的爱丽丝企图创建2画相关监听器,请检查初始化函数。"
)
if signal != LBS.DISORDER_SPAWN:
return
if not isinstance(event, Disorder | PolarityDisorder):
print(
f"【爱丽丝2画监听器警告】检测到紊乱生成信号(DISORDER_SPAWN),但是与之匹配传入的不是紊乱或是极性紊乱类型,而是{type(event)}类型"
)
return
self.listener_active()
def listener_active(self, **kwargs):
"""监听器的激活函数,为敌人添加紊乱伤害提升Buff"""
from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy
buff_add_strategy(self.buff_index, benifit_list=["enemy"], sim_instance=self.sim_instance)
if ALICE_REPORT:
self.sim_instance.schedule_data.change_process_state()
print(f"【爱丽丝事件】【2画】监听到紊乱生成信号,为敌人添加了{self.buff_index}Buff!")
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/AliceCoreSkillDisorderBasicMulBonusListener.py
================================================
from typing import TYPE_CHECKING
from zsim.define import ALICE_REPORT
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from ...anomaly_bar.CopyAnomalyForOutput import Disorder, PolarityDisorder
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.sim_progress.Character.Alice import Alice
from zsim.sim_progress.Character.character import Character
from zsim.simulator.simulator_class import Simulator
class AliceCoreSkillDisorderBasicMulBonusListener(BaseListener):
"""这个监听器的作用是监听紊乱事件来触发Buff,并且根据当前物理异常的剩余时间,设定Buff的层数"""
def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None):
super().__init__(listener_id, sim_instance=sim_instance)
self.char: "Character | None | Alice" = None
self.buff_index = "Buff-角色-爱丽丝-核心被动-紊乱基础倍率增加"
def listening_event(self, event, signal: LBS, **kwargs):
"""监听到紊乱触发信号时,激活"""
if self.char is None:
char_obj = self.sim_instance.char_data.find_char_obj(CID=1401)
self.char = char_obj
if signal not in [LBS.DISORDER_SPAWN]:
return
if not isinstance(event, Disorder | PolarityDisorder):
print(
f"【爱丽丝紊乱监听器警告】检测到紊乱触发信号(DISORDER_SPAWN),但是与之匹配传入的不是紊乱或是极性紊乱类型,而是{type(event)}类型"
)
return
# 当传入的紊乱不是物理属性时直接返回。
if event.element_type != 0:
return
self.listener_active(event_obj=event)
def listener_active(self, **kwargs):
"""监听器的激活函数,根据当前紊乱的剩余时间,设定Buff层数"""
from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy
assert "event_obj" in kwargs, (
"【爱丽丝紊乱监听器警告】监听器函数激活时,并未传入对应的event_obj参数!"
)
event_obj: Disorder | PolarityDisorder = kwargs["event_obj"]
rest_tick = event_obj.remaining_tick()
count = min(rest_tick / 60, 10) # 最大层数10
buff_add_strategy(
self.buff_index,
benifit_list=["enemy"],
specified_count=count,
sim_instance=self.sim_instance,
)
if ALICE_REPORT:
self.sim_instance.schedule_data.change_process_state()
print(
f"【爱丽丝事件】检测到物理属性的紊乱发生,物理异常的剩余时间为{rest_tick:.1f}tick,使本次紊乱的基础倍率提升 {count * 18:.1f} %!"
)
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/AliceCoreSkillPhyBuildupBonusListener.py
================================================
from typing import TYPE_CHECKING
from zsim.define import ALICE_REPORT
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.sim_progress.anomaly_bar import AnomalyBar
from zsim.sim_progress.Character.Alice import Alice
from zsim.sim_progress.Character.character import Character
from zsim.simulator.simulator_class import Simulator
class AliceCoreSkillPhyBuildupBonusListener(BaseListener):
"""这个监听器的作用是监听强击事件,并且为爱丽丝添加Buff"""
def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None):
super().__init__(listener_id, sim_instance=sim_instance)
self.char: "Character | None | Alice" = None
self.buff_index = "Buff-角色-爱丽丝-核心被动-物理异常积蓄效率提升"
def listening_event(self, event: "AnomalyBar", signal: LBS, **kwargs):
"""监听到强击触发信号时激活"""
if self.char is None:
char_obj = self.sim_instance.char_data.find_char_obj(CID=1401)
self.char = char_obj
if signal in [LBS.ASSAULT_SPAWN, LBS.POLARIZED_ASSAULT_SPAWN]:
if signal == LBS.ASSAULT_SPAWN:
# 过滤不是爱丽丝自己触发的普通强击
from zsim.sim_progress.Preload import SkillNode
assert isinstance(event.activated_by, SkillNode), (
"【爱丽丝物理积蓄效率监听器警告】检测到监听器激活时,传入的异常条的Activated_by属性为None"
)
if event.activated_by.char_name != "爱丽丝":
return
self.listener_active(signal=signal)
def listener_active(self, **kwargs):
"""监听器的激活函数,为爱丽丝添加积蓄效率Buff"""
signal = kwargs.get("signal")
from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy
buff_add_strategy(self.buff_index, benifit_list=["爱丽丝"], sim_instance=self.sim_instance)
if ALICE_REPORT:
self.sim_instance.schedule_data.change_process_state()
print(
f"【爱丽丝事件】检测到爱丽丝触发{'强击' if signal == LBS.ASSAULT_SPAWN else '极性强击'},为爱丽丝添加 物理积蓄效率提高 的Buff"
)
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/AliceDisorderListener.py
================================================
from typing import TYPE_CHECKING
from zsim.define import ALICE_REPORT
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.sim_progress.Character.Alice import Alice
from zsim.sim_progress.Character.character import Character
from zsim.simulator.simulator_class import Simulator
class AliceDisorderListener(BaseListener):
"""这个监听器的作用是监听紊乱的触发"""
def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None):
super().__init__(listener_id, sim_instance=sim_instance)
self.char: "Character | None | Alice" = None
self.update_value = 30
def listening_event(self, event, signal: LBS, **kwargs):
"""监听到紊乱信号时,激活"""
if self.char is None:
char_obj = self.sim_instance.char_data.find_char_obj(CID=1401)
self.char = char_obj
if signal not in [LBS.DISORDER_SETTLED]:
return
from ...anomaly_bar.CopyAnomalyForOutput import Disorder, PolarityDisorder
if not isinstance(event, Disorder | PolarityDisorder):
print(
f"【爱丽丝紊乱监听器警告】检测到紊乱结算信号(DISORDER_SETTLED),但是与之匹配传入的不是紊乱或是极性紊乱类型,而是{type(event)}类型"
)
return
self.listener_active()
def listener_active(self, **kwargs):
if ALICE_REPORT:
self.sim_instance.schedule_data.change_process_state()
print(
f"【爱丽丝事件】紊乱监听器监听到紊乱结算,即将为爱丽丝回复{self.update_value}点剑仪值!"
)
from zsim.sim_progress.Character.Alice import Alice
assert isinstance(self.char, Alice)
self.char.update_blade_etiquette(update_obj=self.update_value)
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/AliceDotTriggerListener.py
================================================
from typing import TYPE_CHECKING
from zsim.define import ALICE_REPORT
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.sim_progress.Character.Alice import Alice
from zsim.sim_progress.Character.character import Character
from zsim.simulator.simulator_class import Simulator
class AliceDotTriggerListener(BaseListener):
"""这个监听器的作用是监听畏缩的激活与刷新"""
def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None):
super().__init__(listener_id, sim_instance=sim_instance)
self.char: "Character | None | Alice" = None
def listening_event(self, event, signal: LBS, **kwargs):
"""监听到紊乱信号时,激活"""
if self.char is None:
char_obj = self.sim_instance.char_data.find_char_obj(CID=1401)
self.char = char_obj
if signal not in [LBS.ASSAULT_STATE_ON]:
return
self.listener_active()
def listener_active(self, **kwargs):
"""核心被动激活,给敌人添加Dot"""
enemy = self.sim_instance.schedule_data.enemy
# 验证
if not enemy.dynamic.assault:
raise ValueError(
"【爱丽丝核心被动Dot监听器警告】敌人当前的状态不符合核心被动激活条件,请检查!"
)
from copy import deepcopy
from zsim.sim_progress.Update.UpdateAnomaly import spawn_normal_dot
"""
解释:deepcopy的对象为何来自enemy.anomaly_bars_dict而非enemy.dynamic.active_anomaly_bar_dicts?
监听器的激活时间点位于enemy.dynamic.assault被赋值为True的时间点,
该时间点比enemy.dynamic.active_anomaly_bar_dicts的更新更早,所以此时从enemy.dynamic.active_anomaly_bar_dicts中是获取不到我们想要的异常条的,
此时刚激活的异常条的最新状态还处于enemy.anomaly_bars_dict中,所以要从这里获取。
"""
phy_anomaly_bar = deepcopy(enemy.anomaly_bars_dict[0])
phy_anomaly_bar.anomaly_settled()
dot = spawn_normal_dot(
dot_index="AliceCoreSkillAssaultDot",
sim_instance=self.sim_instance,
bar=phy_anomaly_bar,
)
dot.start(timenow=self.sim_instance.tick)
event_list = self.sim_instance.schedule_data.event_list
from zsim.sim_progress.Dot.BaseDot import Dot
for dots in enemy.dynamic.dynamic_dot_list:
assert isinstance(dots, Dot)
if dots.ft.index == dot.ft.index:
dots.end(timenow=self.sim_instance.tick)
enemy.dynamic.dynamic_dot_list.remove(dots)
break
enemy.dynamic.dynamic_dot_list.append(dot)
event_list.append(dot.anomaly_data)
if ALICE_REPORT:
self.sim_instance.schedule_data.change_process_state()
print("【爱丽丝事件】检测到畏缩状态更新,核心被动Dot激活!")
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/AliceNAEnhancementListener.py
================================================
from typing import TYPE_CHECKING
from zsim.define import ALICE_REPORT
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.sim_progress.Character.Alice import Alice
from zsim.sim_progress.Character.character import Character
from zsim.simulator.simulator_class import Simulator
class AliceNAEnhancementListener(BaseListener):
"""这个监听器的作用是监听强击的触发,触发后打开爱丽丝的强化平A状态"""
def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None):
super().__init__(listener_id, sim_instance=sim_instance)
self.char: "Character | None | Alice" = None
def listening_event(self, event, signal: LBS, **kwargs):
"""监听到紊乱信号时,激活"""
if self.char is None:
char_obj = self.sim_instance.char_data.find_char_obj(CID=1401)
self.char = char_obj
if signal not in [LBS.ASSAULT_SPAWN]:
return
self.listener_active()
def listener_active(self, **kwargs):
if ALICE_REPORT:
self.sim_instance.schedule_data.change_process_state()
print("【爱丽丝事件】监听到强击事件触发!爱丽丝获得1次强化A5次数")
from zsim.sim_progress.Character.Alice import Alice
assert isinstance(self.char, Alice)
self.char.na_enhancement_state = True
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/BaseListenerClass.py
================================================
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
if TYPE_CHECKING:
from zsim.sim_progress.Character.character import Character
from zsim.sim_progress.Enemy import Enemy
from zsim.simulator.simulator_class import Simulator
class BaseListener(ABC):
@abstractmethod
def __init__(
self,
listener_id: str | None = None,
sim_instance: "Simulator | None" = None,
owner: "Character | Enemy | None" = None,
):
assert sim_instance is not None
self.sim_instance: "Simulator" = sim_instance
self.listener_id: str | None = listener_id
self.schedule = None
self.owner: "Character | Enemy | None" = owner
@abstractmethod
def listening_event(self, event, signal: LBS, **kwargs):
"""监听事件的函数"""
pass
@abstractmethod
def listener_active(self, **kwargs):
"""当监听到预期事件时,监听器的激活函数"""
pass
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/CinderCobaltListener.py
================================================
from typing import TYPE_CHECKING
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
class CinderCobaltListener(BaseListener):
"""这个监听器的作用是监听佩戴者的进场。"""
def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None):
super().__init__(listener_id, sim_instance=sim_instance)
self.active_signal: tuple[object, bool] | None = None
def listening_event(self, event, signal: LBS, **kwargs):
"""监听到佩戴者的进场后,记录更新信号"""
if signal not in [LBS.SWITCHING_IN, LBS.ENTER_BATTLE]:
return
from zsim.sim_progress.Character.character import Character
if not isinstance(event, Character):
return
self.active_signal = (event, True)
def listener_active(self, **kwargs):
pass
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/FangedMetalListener.py
================================================
from typing import TYPE_CHECKING
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
class FangedMetalListener(BaseListener):
"""这个监听器的作用是监听所有强击事件的触发,獠牙重金属4"""
def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None):
super().__init__(listener_id, sim_instance=sim_instance)
self.buff_index = "Buff-驱动盘-獠牙重金属-增伤"
def listening_event(self, event, signal: LBS, **kwargs):
"""监听到强击事件后,激活监听器"""
if signal not in [LBS.ASSAULT_STATE_ON]:
return
self.listener_active(target=self.owner)
def listener_active(self, **kwargs):
"""獠牙重金属4的监听器激活时,为佩戴者添加增伤buff"""
from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy
from zsim.sim_progress.Character.character import Character
target = kwargs.get("target")
assert isinstance(target, Character), (
"獠牙重金属4的监听器激活时,传入激活函数的target参数必须是Character类"
)
benifit_list = [target.NAME]
buff_add_strategy(
self.buff_index, benifit_list=benifit_list, sim_instance=self.sim_instance
)
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/HeartstringNocturneListener.py
================================================
from typing import TYPE_CHECKING
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
class HeartstringNocturneListener(BaseListener):
"""监听入场事件,并且直接添加心弦夜响Buff"""
def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None):
super().__init__(listener_id, sim_instance=sim_instance)
self.active_signal = None
def listening_event(self, event, signal: LBS, **kwargs):
"""监听到角色入场事件,传递入场信号。"""
if signal != LBS.ENTER_BATTLE:
return
from zsim.sim_progress.Preload import SkillNode
if not isinstance(event, SkillNode):
raise ValueError("entr_battle_event的事件对象必须是SkillNode类型!")
self.active_signal = (event, True)
def listener_active(self, **kwargs):
self.active_signal = None
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/HormonePunkListener.py
================================================
from typing import TYPE_CHECKING
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
class HormonePunkListener(BaseListener):
"""这个监听器的作用是监听佩戴者的进场。"""
def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None):
super().__init__(listener_id, sim_instance=sim_instance)
self.active_signal: tuple[object, bool] | None = None
def listening_event(self, event, signal: LBS, **kwargs):
"""监听到佩戴者的进场后,记录更新信号"""
if signal not in [LBS.SWITCHING_IN, LBS.ENTER_BATTLE]:
return
from zsim.sim_progress.Character.character import Character
if not isinstance(event, Character):
return
self.active_signal = (event, True)
def listener_active(self, **kwargs):
pass
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/HugoCorePassiveBuffListener.py
================================================
from typing import TYPE_CHECKING
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
class HugoCorePassiveBuffListener(BaseListener):
"""这个监听器的作用是,尝试监听雨果致使怪物失衡的事件,并且触发一次核心被动Buff"""
def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None):
super().__init__(listener_id, sim_instance=sim_instance)
self.buff_index = "Buff-角色-雨果-核心被动-暗渊回响"
def listening_event(self, event, signal: LBS, **kwargs):
"""监听到雨果的single_hit后,直接添加Buff"""
if signal != LBS.STUN:
return
from zsim.sim_progress.data_struct import SingleHit
if not isinstance(event, SingleHit):
return
if "1291" not in event.skill_tag:
return
self.listener_active()
from zsim.define import HUGO_REPORT
if HUGO_REPORT:
self.sim_instance.schedule_data.change_process_state()
if event.skill_node is None:
return
print(
f"雨果的失衡事件监听器监听到了雨果的技能{event.skill_tag}({event.skill_node.skill.skill_text})使怪物陷入失衡状态,根据核心被动,触发一次【暗渊回响】Buff"
)
def listener_active(self, **kwargs):
"""触发核心被动Buff,通过BuffAddStrategy来暴力添加Buff"""
from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy
buff_add_strategy(self.buff_index, benifit_list=["雨果"], sim_instance=self.sim_instance)
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/PracticedPerfectionPhyDmgBonusListener.py
================================================
from typing import TYPE_CHECKING
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
class PracticedPerfectionPhyDmgBonusListener(BaseListener):
"""十方锻星的物理增伤监听器,监听入场信号和强击信号"""
def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None):
super().__init__(listener_id, sim_instance=sim_instance)
self.buff_index: str | None = None # 音擎增益的Buff index
def listening_event(self, event, signal: LBS, **kwargs):
"""监听到角色入场事件,传递入场信号。"""
if signal not in [LBS.ASSAULT_SPAWN, LBS.ENTER_BATTLE]:
return
self.listener_active(signal=signal)
def listener_active(self, **kwargs):
"""监听器激活,根据信号类型进行不同的处理"""
from zsim.sim_progress.Character.character import Character
if self.buff_index is None:
assert self.owner is not None, (
"【十方锻星物理增伤监听器警告】监听器未绑定角色,请检查初始化"
)
assert isinstance(self.owner, Character), (
"【十方锻星物理增伤监听器警告】监听器绑定的角色不是Character类型,请检查初始化"
)
assert self.owner.weapon_ID == "十方锻星", (
f"【十方锻星物理增伤监听器警告】监听器绑定的武器是{self.owner.weapon_ID},并非十方锻星,请检查初始化"
)
assert int(self.owner.weapon_level) in [1, 2, 3, 4, 5], (
f"【十方锻星物理增伤监听器警告】监听器绑定的角色武器精炼等级为{self.owner.weapon_level},不是合法的精炼等级,请检查初始化"
)
self.buff_index = f"Buff-武器-精{int(self.owner.weapon_level)}十方锻星-物理伤害增加"
assert "signal" in kwargs, "【十方锻星物理增伤监听器警告】监听器激活时,未传入信号类型"
signal: LBS = kwargs["signal"]
from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy
if signal == LBS.ENTER_BATTLE:
assert isinstance(self.owner, Character), (
"【十方锻星物理增伤监听器警告】监听器绑定的角色不是Character类型,请检查初始化"
)
benifit_list = [self.owner.NAME]
buff_add_strategy(
self.buff_index,
benifit_list=benifit_list,
specified_count=2,
sim_instance=self.sim_instance,
)
buff_add_strategy(
self.buff_index, benifit_list=benifit_list, sim_instance=self.sim_instance
)
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/YixuanAnomalyListener.py
================================================
from typing import TYPE_CHECKING
from zsim.define import YIXUAN_REPORT
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.sim_progress.Character.Yixuan import Yixuan
from zsim.simulator.simulator_class import Simulator
class YixuanAnomalyListener(BaseListener):
"""这个监听器的作用是,尝试监听仪玄的玄墨异常触发事件,并且恢复自身闪能,10点(内置CD10秒)。"""
def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None):
super().__init__(listener_id, sim_instance=sim_instance)
self.char: "Yixuan | None" = None
self.last_active_tick: int = 0
self.cd: int = 600 # 内置CD
self.recover_value: int = 10 # 监听器激活时为仪玄恢复的闪能值
@property
def ready(self) -> bool:
return (
True
if self.last_active_tick == 0
else self.last_active_tick + self.cd <= self.sim_instance.tick
)
def listening_event(self, event, signal: LBS, **kwargs):
"""监听到新的anomlay创建后,检查属性类型,通过判定则恢复闪能。"""
if self.char is None:
from zsim.sim_progress.Character.Yixuan import Yixuan
char_obj = self.sim_instance.char_data.find_char_obj(CID=1371)
if not isinstance(char_obj, Yixuan):
return
self.char = char_obj
if signal != LBS.ANOMALY:
return
from zsim.sim_progress.anomaly_bar import AnomalyBar
if not isinstance(event, AnomalyBar):
raise TypeError(
f"仪玄的属性异常监听器接收到了anomlay_event的信号,但是传入的event_obj却为{type(event)}类型,请检查监听器广播函数调用!"
)
if event:
from zsim.define import ANOMALY_MAPPING
if YIXUAN_REPORT:
print(
f"监听到新的属性异常:{ANOMALY_MAPPING[event.element_type]}!尝试激活监听事件——仪玄闪能恢复!"
)
self.sim_instance.schedule_data.change_process_state()
self.listener_active()
def listener_active(self, **kwargs):
"""监听事件激活,检测内置Cd,通过后为仪玄恢复闪能值。"""
if not self.ready:
if YIXUAN_REPORT:
print(
f"仪玄在{self.last_active_tick}tick时已经通过该效果恢复过一次闪能值了,所以此时该效果的内置CD尚未就绪!"
)
self.sim_instance.schedule_data.change_process_state()
return
else:
from zsim.sim_progress.Character.Yixuan import Yixuan
if not isinstance(self.char, Yixuan):
raise TypeError
self.char.update_adrenaline(self.recover_value)
if YIXUAN_REPORT:
print(f"玄墨监听器事件激活!成功为仪玄恢复{self.recover_value}点闪能!")
self.sim_instance.schedule_data.change_process_state()
self.last_active_tick = self.sim_instance.tick
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/YuzuhaC2QTEListener.py
================================================
from typing import TYPE_CHECKING
from zsim.define import YUZUHA_REPORT
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.sim_progress.Character.Yuzuha import Yuzuha
from zsim.sim_progress.Preload import SkillNode
from zsim.simulator.simulator_class import Simulator
class YuzuhaC2QTEListener(BaseListener):
"""这个监听器的作用是,监听其他角色通过连携技入场。"""
def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None):
super().__init__(listener_id, sim_instance=sim_instance)
self.char: "Yuzuha | None" = None
def listening_event(self, event, signal: LBS, skill_node: "SkillNode | None" = None, **kwargs):
""""""
if self.char is None:
from zsim.sim_progress.Character.Yuzuha import Yuzuha
char_obj = self.sim_instance.char_data.find_char_obj(CID=1411)
if not isinstance(char_obj, Yuzuha):
return
self.char = char_obj
if (
signal != LBS.SWITCHING_IN
or not isinstance(skill_node, SkillNode)
or skill_node.char_name == self.char.NAME
or skill_node.skill.trigger_buff_level != 5
):
return
else:
self.listener_active()
if YUZUHA_REPORT:
self.sim_instance.schedule_data.change_process_state()
print(
f"【柚叶2画】检测到队友 {skill_node.char_name} 通过连携技 {skill_node.skill_tag} 入场,为柚叶恢复1点甜度点"
)
def listener_active(self, **kwargs):
assert self.char is not None
self.char.update_sugar_points(value=1)
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/YuzuhaC6ParryListener.py
================================================
from typing import TYPE_CHECKING
from zsim.define import YUZUHA_REPORT
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.sim_progress.Character.Yuzuha import Yuzuha
from zsim.simulator.simulator_class import Simulator
class YuzuhaC6ParryListener(BaseListener):
"""这个监听器的作用是,监听自身的招架事件"""
def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None):
super().__init__(listener_id, sim_instance=sim_instance)
self.char: "Yuzuha | None" = None
def listening_event(self, event, signal: LBS, **kwargs):
"""获取“招架”类的广播信号。"""
if self.char is None:
from zsim.sim_progress.Character.Yuzuha import Yuzuha
char_obj = self.sim_instance.char_data.find_char_obj(CID=1411)
if not isinstance(char_obj, Yuzuha):
return
self.char = char_obj
if signal != LBS.PARRY:
return
from zsim.sim_progress.Preload import SkillNode
if not isinstance(event, SkillNode) or event.skill_tag not in [
"1411_knock_back_cause_parry",
"1411_SNA_3",
]:
return
else:
self.listener_active()
if YUZUHA_REPORT:
self.sim_instance.schedule_data.change_process_state()
print(
f"【柚叶6画】检测到 柚叶 通过技能 {event.skill_tag} 招架/格挡了敌人的攻击,为 柚叶 恢复1点甜度点"
)
def listener_active(self, **kwargs):
assert self.char is not None
self.char.update_sugar_points(value=1)
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/ZanshinHerbCaseListener.py
================================================
from typing import TYPE_CHECKING
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
class ZanshinHerbCaseListener(BaseListener):
"""这个监听器的作用是记录残心青囊的触发信号"""
def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None):
super().__init__(listener_id, sim_instance=sim_instance)
self.active_signal: tuple[object, bool] | None = None
def listening_event(self, event, signal: LBS, **kwargs):
"""监听到失衡事件或是触发了新的异常事件时,记录这个信号。"""
if signal not in [LBS.STUN, LBS.ANOMALY]:
return
self.active_signal = (event, True)
def listener_active(self, **kwargs):
"""置空信号"""
self.active_signal = None
================================================
FILE: zsim/sim_progress/data_struct/BattleEventListener/__init__.py
================================================
import importlib
from collections import defaultdict
from typing import TYPE_CHECKING
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
from .BaseListenerClass import BaseListener
if TYPE_CHECKING:
from zsim.sim_progress.Character.character import Character
from zsim.sim_progress.Enemy import Enemy
from zsim.simulator.simulator_class import Simulator
class ListenerManger:
"""监听器组"""
def __init__(self, sim_instance: "Simulator"):
self.sim_instance = sim_instance
self._listeners_group: defaultdict[str | int, dict[str, BaseListener]] = defaultdict(
dict
) # 监听器组 的ID 可能是角色的CID(int),也可能是文本“enemy”
self.__listener_map: dict[str, str] = {
"Hugo_1": "HugoCorePassiveBuffListener",
"Hormone_Punk_1": "HormonePunkListener",
"Zanshin_Herb_Case_1": "ZanshinHerbCaseListener",
"Heartstring_Nocturne_1": "HeartstringNocturneListener",
"Yixuan_1": "YixuanAnomalyListener",
"CinderCobalt_1": "CinderCobaltListener",
"Yuzuha_1": "YuzuhaC2QTEListener",
"Yuzuha_2": "YuzuhaC6ParryListener",
"Alice_1": "AliceDisorderListener",
"Alice_2": "AliceCoreSkillDisorderBasicMulBonusListener",
"Alice_3": "AliceCoreSkillPhyBuildupBonusListener",
"Alice_4": "AliceNAEnhancementListener",
"Alice_5": "AliceDotTriggerListener",
"Alice_Cinema_1_A": "AliceCinema1DefReduceListener",
"Alice_Cinema_1_B": "AliceCinema1BladeEtquitteRecoverListener",
"Alice_Cinema_2_A": "AliceCinema2DisorderDmgBonus",
"PracticedPerfection_1": "PracticedPerfectionPhyDmgBonusListener",
"Fanged_Metal_1": "FangedMetalListener",
}
def add_listener(self, listener_owner: "Character | Enemy | None", listener: BaseListener):
"""添加一个监听器"""
if listener_owner is None or listener.listener_id is None:
raise TypeError("监听器所有者或监听器ID不能为空")
from zsim.sim_progress.Character.character import Character
if isinstance(listener_owner, Character):
self._listeners_group[listener_owner.CID][listener.listener_id] = listener
elif isinstance(listener_owner, Enemy):
self._listeners_group["enemy"][listener.listener_id] = listener
else:
raise TypeError(f"无法解析的监听器所有者类型: {type(listener_owner)}")
def remove_listener(self, listener_owner: "Character | Enemy | None", listener: BaseListener):
"""移除一个监听器"""
if listener_owner is None or listener.listener_id is None:
raise TypeError("监听器所有者或监听器ID不能为空")
if isinstance(listener_owner, Character):
listeners_group = self._listeners_group[listener_owner.CID]
elif isinstance(listener_owner, Enemy):
listeners_group = self._listeners_group["enemy"]
else:
raise TypeError(f"无法解析的监听器所有者类型: {type(listener_owner)}")
listeners_group.pop(listener.listener_id)
def broadcast_event(self, event, signal: LBS, **kwargs):
"""广播事件,kwargs参数中记录了事件类型"""
for owner_id, owner_dict in self._listeners_group.items():
for __listener in owner_dict.values():
__listener: BaseListener
__listener.listening_event(event=event, signal=signal, **kwargs)
def listener_factory(
self,
listener_owner: "Character | Enemy | None",
initiate_signal: str | None = None,
sim_instance: "Simulator | None" = None,
):
"""初始化监听器的工厂函数"""
if initiate_signal is None:
raise ValueError(
"在初始化阶段调用监听器工厂函数时,必须传入有效的initiate_signal参数!"
)
if listener_owner is None:
raise ValueError("调用监听器工厂函数时,listener_onwner参数不能为空!")
for listener_id, listener_class_name in self.__listener_map.items():
if initiate_signal in listener_id:
module_name = listener_class_name
try:
module = importlib.import_module(f".{module_name}", package=__name__)
listener_obj = getattr(module, listener_class_name)(
listener_id, sim_instance=sim_instance
)
if listener_obj.owner is None:
listener_obj.owner = listener_owner
self.add_listener(listener_owner=listener_owner, listener=listener_obj)
return listener_obj
except ModuleNotFoundError:
raise ValueError("在初始化阶段调用监听器工厂函数时,找不到对应的监听器模块!")
else:
raise ValueError(
f"在初始化阶段调用监听器工厂函数时,未找到ID为 {initiate_signal} 的监听器类!"
)
def get_listener(
self, listener_owner: "Character | Enemy | None", listener_id: str
) -> BaseListener | None:
"""获取指定监听器"""
from zsim.sim_progress.Character.character import Character
if listener_owner is None:
raise TypeError("监听器所有者不能为空")
if isinstance(listener_owner, Character):
listener = self._listeners_group[listener_owner.CID].get(listener_id, None)
elif isinstance(listener_owner, Enemy):
listener = self._listeners_group["enemy"].get(listener_id, None)
else:
raise TypeError(f"无法解析的监听器所有者类型: {type(listener_owner)}")
if listener is None:
raise ValueError(
f"在获取监听器时,未找到对应的监听器 ID: {listener_id},所有者: {listener_owner}"
)
return listener
def __str__(self) -> str:
output = "==========监听器组的现状如下==========\n"
for owner_id, owner_dict in self._listeners_group.items():
output += f"监听器组子集 ID: {owner_id}\n"
output += f"{['监听器' + __key + ' | ' for __key in owner_dict.keys()]}\n"
output += "==================================="
return output
================================================
FILE: zsim/sim_progress/data_struct/DecibelManager/DecibelManagerClass.py
================================================
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from zsim.sim_progress.Character.character import Character
from zsim.sim_progress.data_struct.single_hit import SingleHit
from zsim.sim_progress.Enemy import Enemy
from zsim.sim_progress.Load.loading_mission import LoadingMission
from zsim.sim_progress.Preload.SkillsQueue import SkillNode
from zsim.simulator.dataclasses import ScheduleData
from zsim.simulator.simulator_class import Simulator
class Decibelmanager:
def __init__(self, sim_instance: "Simulator"):
# 原类属性改为实例属性
self.sim_instance = sim_instance
self.DECIBEL_EVENT_MAP: dict[str | int, list[int]] = {
"interrupt_enemy": [10],
4: [20],
"part_break": [20],
"stun": [20],
"anomaly": [35, 125, 170],
"disorder": [15, 65, 85],
5: [10],
8: [200],
"BH_Aid": [20],
"BH_Aid_after_attacked": [30],
}
self.REPORT_MAP: dict[str | int, str] = {
"interrupt_enemy": "打断敌人进攻",
4: "极限闪避",
"part_break": "部位破坏",
"stun": "使敌人失衡",
"anomaly": "使敌人触发属性异常",
"disorder": "使敌人触发紊乱",
5: "释放连携技",
8: "释放招架支援",
"BH_Aid": "支援角色触发的快速支援",
"BH_Aid_after_attacked": "受击后触发的快速支援",
}
self.char_obj_list: list["Character"] = []
self.enemy: "Enemy | None" = None
self.game_state: dict[str, Any] = {}
def update(self, **kwargs):
decibel_value, node, output_key = self.get_decibel_value(**kwargs)
if decibel_value == 0:
return
char_dict = self.split_char_list_by_cid(node)
for char_kind, char_list in char_dict.items():
if char_kind == "major":
value_input = decibel_value * 1
self.add_decibel_to_char(value_input, char_list[0], output_key)
elif char_kind == "minor":
value_input = decibel_value * 0.5
for minor_char_name in char_list:
self.add_decibel_to_char(value_input, minor_char_name, output_key)
else:
raise ValueError(f"{char_kind}不是major或minor!")
def add_decibel_to_char(self, decibel_value, char_name, output_key):
from zsim.sim_progress.data_struct import ScheduleRefreshData
refresh_data = ScheduleRefreshData(decibel_target=(char_name,), decibel_value=decibel_value)
schedule_data: "ScheduleData" = self.sim_instance.game_state["schedule_data"]
schedule_data.event_list.append(refresh_data)
# print(f"{char_name}因{self.REPORT_MAP[output_key]}获得了{decibel_value}点喧响值!")
def get_decibel_value(
self,
key: str | None = None,
skill_node: "SkillNode | None" = None,
single_hit: "SingleHit | None" = None,
loading_mission: "LoadingMission | None" = None,
**kwargs,
):
"""根据程序的输入进行参数的初始化检查!并且返回本次运行所需要增加的喧响值"""
if not any([skill_node, single_hit, loading_mission]):
raise ValueError(
"DecibelManager的update函数中,必须传入skill_node、single_hit、loading_mission中的一个!"
)
node: "SkillNode | None" = None
if skill_node:
node = skill_node
elif single_hit is not None:
node = single_hit.skill_node
elif loading_mission is not None:
node = loading_mission.mission_node
if key is None:
if node is None:
raise ValueError("DecibelManager的get_decibel_value函数中,node不能为空!")
if node.skill.trigger_buff_level not in self.DECIBEL_EVENT_MAP:
decibel_value = 0
output_key = 0
else:
if node.active_generation:
# EXPLAIN: 这里要筛选重攻击标签——因为像雅这种角色的连携技分3段,如果不筛选主动动作,那么雅就会多次吃到连携技的喧响值奖励
# 风险:暂未发现该筛选存在Bug风险。
decibel_value = self.DECIBEL_EVENT_MAP[node.skill.trigger_buff_level][0]
output_key = node.skill.trigger_buff_level
else:
decibel_value = 0
output_key = 0
else:
if key not in self.DECIBEL_EVENT_MAP:
decibel_value = 0
output_key = 0
else:
if key in ["anomaly", "disorder"]:
if self.enemy is None:
self.enemy = self.sim_instance.game_state["schedule_data"].enemy
assert self.enemy is not None
decibel_value = self.DECIBEL_EVENT_MAP[key][
self.enemy.QTE_triggerable_times - 1
]
else:
decibel_value = self.DECIBEL_EVENT_MAP[key][0]
output_key = key
return decibel_value, node, output_key
def split_char_list_by_cid(self, node: "SkillNode | None"):
if node is None:
raise ValueError("DecibelManager的split_char_list_by_cid函数中,node不能为空!")
char_id = int(node.skill_tag.strip().split("_")[0])
char_dict = {"major": [], "minor": []}
if not self.char_obj_list:
from zsim.sim_progress.Buff import find_char_list
self.char_obj_list = find_char_list(sim_instance=self.sim_instance)
for obj in self.char_obj_list:
if obj.CID == char_id:
char_dict["major"].append(obj.NAME)
else:
char_dict["minor"].append(obj.NAME)
if len(char_dict["major"]) == 0:
raise ValueError(f"并未找到CID为{char_id}的角色!")
elif len(char_dict["major"]) > 1:
raise ValueError(f"找到多个CID为{char_id}的角色!")
else:
return char_dict
================================================
FILE: zsim/sim_progress/data_struct/DecibelManager/__init__.py
================================================
================================================
FILE: zsim/sim_progress/data_struct/EnemyAttackEvent.py
================================================
import math
from typing import TYPE_CHECKING, cast
from zsim.define import ENEMY_ATK_PARAMETER_DICT, ENEMY_ATTACK_REPORT
if TYPE_CHECKING:
from zsim.sim_progress.data_struct import SingleHit
from zsim.sim_progress.Enemy import Enemy
from zsim.sim_progress.Enemy.EnemyAttack.EnemyAttackClass import EnemyAttackAction
from zsim.sim_progress.Preload import SkillNode
class EnemyAttackEventManager:
def __init__(self, enemy_instance: "Enemy"):
"""进攻事件对象,负责管理敌人进攻的相关动态信息。"""
self.enemy: "Enemy" = enemy_instance
self.action: "None | EnemyAttackAction" = None
self.last_start_tick: int = 0 # 进攻事件的开始时刻,也是进攻意图的展露时刻。
self.last_end_tick: int = 0
self.answered_action: list["SkillNode"] = []
self.interaction_window_open_tick: int | None = (
None # 交互窗口开启的tick,即游戏中红黄光亮起的tick
)
self.interaction_window_close_tick: int | None = (
None # 交互窗口关闭的tick,即游戏中动作命中的时间点
)
self.hitted_count = 0 # 交互期间的命中计数
self.answered_count = 0 # 交互期间的成功响应次数
self.interrupted_skill_type = [2, 5, 6]
self.interruption_recovery_frames = 60 # 每次敌人被打断的硬直时间
self._interruption_update_tick = 0
@property
def interruption_update_tick(self) -> int:
"""上一次敌人进入打断硬直的更新时间,可以直接访问,也可以赋值"""
return self._interruption_update_tick
@interruption_update_tick.setter
def interruption_update_tick(self, value: int):
"""赋值功能"""
self._interruption_update_tick = value
# print(
# f"敌人的打断硬直更新了!新的状态将从{value}tick开始,于{value + self.interruption_recovery_frames}tick结束。"
# )
def event_start(self, action: "EnemyAttackAction", start_tick: int):
"""开始一个进攻事件"""
self.action = action
self.last_start_tick = start_tick
self.last_end_tick = start_tick + round(action.duration)
response_window: tuple[int, int] = self.get_response_window()
self.interaction_window_open_tick = response_window[0]
self.interaction_window_close_tick = response_window[1]
if ENEMY_ATTACK_REPORT:
self.enemy.sim_instance.schedule_data.change_process_state()
print(
f"敌人({self.enemy.name})开始了进攻事件:{action.tag},持续时间为{action.duration}tick"
)
def event_end(self, tick: int | None = None):
"""结束一个进攻事件"""
if self.action is None:
raise ValueError("没有正在进行的进攻事件,无法结束!")
# self.response_result_settlement()
if tick is not None:
self.last_end_tick = tick
self.action = None
self.answered_action = []
self.hitted_count = 0
self.answered_count = 0
def interrupted(self, tick: int, reason: str | None = None):
"""中断当前进攻事件"""
if self.action is None:
raise ValueError("没有正在进行的进攻事件,无法中断!")
if ENEMY_ATTACK_REPORT:
self.enemy.sim_instance.schedule_data.change_process_state()
print(
f"敌人({self.enemy.name})的进攻事件:{self.action.tag}在第{tick}tick被中断!打断源:{reason}"
)
self.event_end(tick=tick)
self.interruption_update_tick = tick
def interruption_recovery_check(self, tick: int) -> bool:
"""检测当前tick是否处于硬直状态"""
if self.interruption_update_tick == 0:
return False
if tick - self.interruption_update_tick > self.interruption_recovery_frames:
return False
else:
return True
def end_check(self, tick: int):
"""检测当前进攻事件是否已经结束"""
if not self.action:
return
if tick >= self.last_end_tick:
if ENEMY_ATTACK_REPORT:
self.enemy.sim_instance.schedule_data.change_process_state()
print(
f"敌人({self.enemy.name})的进攻事件:{self.action.tag}在第{tick}tick自然结束!该次动作共计命中{self.hitted_count}次,被有效响应{self.answered_count}次"
)
self.event_end()
@property
def attacking(self) -> bool:
"""当前是否正在进行进攻事件"""
return self.action is not None
@property
def is_answered(self) -> bool:
"""当前进攻事件是否已经被响应"""
if not self.answered_action:
return False
else:
for action in self.answered_action:
"""仅在检测到招架支援时,才返回True。只要不招架,那么单次进攻信号就可能被多次利用。"""
if "parry_Aid" in action.skill_tag:
return True
else:
return False
def is_in_response_window(self, tick: int) -> bool:
"""判断当前tick是否处于首次响应窗口内"""
if not self.attacking:
return False
if (
self.interaction_window_open_tick is not None
and self.interaction_window_close_tick is not None
and self.interaction_window_open_tick <= tick <= self.interaction_window_close_tick
):
return True
return False
def get_rt(self) -> int:
"""获取玩家反应时间(RT),即玩家从看到敌人进攻到做出反应的时间。"""
theta = ENEMY_ATK_PARAMETER_DICT.get("theta", None)
if theta is None:
raise ValueError("ENEMY_ATK_PARAMETER_DICT中没有theta参数,请检查配置!")
perfect_player: bool = bool(ENEMY_ATK_PARAMETER_DICT.get("perfect_player"))
if perfect_player:
"""若是完美玩家将直接应用最短反应时间"""
return round(theta / 1000 * 60)
Lp_val = ENEMY_ATK_PARAMETER_DICT.get("PlayerLevel", None)
if Lp_val is None:
raise ValueError("ENEMY_ATK_PARAMETER_DICT中没有PlayerLevel参数,请检查配置!")
Lp = cast(int, Lp_val)
c = ENEMY_ATK_PARAMETER_DICT.get("c", None)
if c is None:
raise ValueError("ENEMY_ATK_PARAMETER_DICT中没有c参数,请检查配置!")
Tbase = ENEMY_ATK_PARAMETER_DICT.get("Tbase", None)
if Tbase is None:
raise ValueError("ENEMY_ATK_PARAMETER_DICT中没有Tbase参数,请检查配置!")
delta = ENEMY_ATK_PARAMETER_DICT.get("delta", None)
if delta is None:
raise ValueError("ENEMY_ATK_PARAMETER_DICT中没有delta参数,请检查配置!")
sigma = c / (Lp**0.3) # 计算方差
Ta = Tbase + delta * (3 - Lp) # 根据玩家水平计算对应中位数
mu = math.log(Ta - theta) - sigma**2 / 2 # 计算均值
Z = abs(
self.enemy.sim_instance.rng_instance.normal_from_table()
) # 从RNG模块按正态分布获取一个0~1的随机数。
RT = theta + math.e ** (mu + sigma * Z)
rt_tick = round(RT / 1000 * 60) # 将毫秒转化为帧(tick)
return rt_tick
def get_response_window(self) -> tuple[int, int]:
"""获取红黄光亮起的时间点"""
if not self.action:
raise ValueError("No action in progress")
first_hit_tick = self.action.get_hit_tick() + self.last_start_tick
Ta_val = ENEMY_ATK_PARAMETER_DICT.get("Taction")
if not isinstance(Ta_val, (int, float)):
raise ValueError("Taction not configured in ENEMY_ATK_PARAMETER_DICT")
Ta = int(Ta_val)
left_bound = max(
self.last_start_tick, first_hit_tick - Ta
) # 如果怪物前摇很短,动作时间也很短,那么怪物攻击动作开始的时间就是黄光亮起的时间。
right_bound = first_hit_tick
return left_bound, right_bound
def get_uncommon_response_window(self, another_ta: int) -> tuple[int, int]:
"""获取红黄光亮起的时间点,适用于非标准的进攻动作"""
if not self.action:
raise ValueError("No action in progress")
first_hit_tick = self.action.get_hit_tick(another_ta=another_ta) + self.last_start_tick
Ta = another_ta
left_bonud = max(self.last_start_tick, first_hit_tick - Ta)
right_bound = first_hit_tick
return left_bonud, right_bound
def can_be_answered(self, rt_tick: int) -> tuple[bool, int, int]:
"""该函数用于判断当前进攻事件是否具有响应的可能,主要是时间判断。"""
if not self.action:
raise ValueError("调用can_be_answered函数时请确保存在进攻事件")
if self.is_answered:
print(
f"当前动作:{self.action.tag}已经被{[_action.skill_tag for _action in self.answered_action]}响应过了!"
)
return False, 0, 0
Lp_val = ENEMY_ATK_PARAMETER_DICT.get("PlayerLevel")
if not isinstance(Lp_val, int):
raise ValueError("PlayerLevel not configured in ENEMY_ATK_PARAMETER_DICT")
Lp = Lp_val
if self.interaction_window_close_tick is None or self.interaction_window_open_tick is None:
return False, 0, 0
Td = self.interaction_window_close_tick - self.interaction_window_open_tick
first_hit_tick = self.action.get_hit_tick()
if Lp <= 2:
return rt_tick <= Td, rt_tick, Td
else:
return (
rt_tick <= first_hit_tick,
rt_tick,
first_hit_tick,
)
def receive_response_node(self, skill_node: "SkillNode"):
"""统一接口,用于接收响应技能(preload阶段)。"""
if not self.attacking:
raise ValueError("企图在没有进攻事件的时候调用进攻事件接收接口。")
self.answered_action.append(skill_node)
self.answered_count += 1
def check_myself(self, tick: int) -> None:
"""检查当前tick的命中结算、结束结算、打断等情况;"""
if self.action is None:
return
"""筛除过期的响应技能"""
nodes_to_remove = []
for nodes in self.answered_action:
if nodes.end_tick < tick:
nodes_to_remove.append(nodes)
else:
for _nodes in nodes_to_remove:
self.answered_action.remove(_nodes)
"""检查是否命中,如果有命中则执行单次命中的结果检测"""
if self.hit_check(tick=tick):
self.single_hit_settlement(tick=tick)
def receive_single_hit(self, single_hit: "SingleHit", tick: int):
"""
在Enemy接收Hit的时候,需要这个函数来把SingleHit传进AtkEventManager,
来更新敌人的打断状态。
"""
skill_node: "SkillNode | None" = single_hit.skill_node
if skill_node is None:
return
if not self.__check_skill_interrupt_capability(skill_node=skill_node):
return
self.interruption_update_tick = tick
if self.action is not None:
self.interrupted(tick=tick, reason=f"技能:{skill_node.skill_tag}")
def hit_check(self, tick: int) -> bool:
"""检查输入的tick是否存在命中节点"""
if not self.action:
return False
if any([_hit_tick + self.last_start_tick == tick for _hit_tick in self.action.hit_list]):
return True
return False
def single_hit_settlement(self, tick: int) -> None:
"""单次命中结算函数,用于结算当前tick的命中结果。"""
sim_instance = self.enemy.sim_instance
char_on_field_val = sim_instance.preload.preload_data.operating_now
if char_on_field_val is None:
return
char_on_field = char_on_field_val
char_stack = sim_instance.preload.preload_data.personal_node_stack[char_on_field]
self.hitted_count += 1
if char_stack is None:
if ENEMY_ATTACK_REPORT:
self.enemy.sim_instance.schedule_data.change_process_state()
print(f"当前前台角色{char_on_field}并未进行有效交互(没有技能栈),将被打断!")
return
nodes = char_stack.get_effective_node()
if nodes is None:
if ENEMY_ATTACK_REPORT:
self.enemy.sim_instance.schedule_data.change_process_state()
print(f"当前前台角色{char_on_field}并未进行有效交互(尚未行动过),将被打断!")
return
if "interrupted" in nodes.skill_tag:
print(f"角色{char_on_field}正处于被打断后摇中,打断后摇不刷新!")
return
if nodes.end_tick < tick and not (
(apl := self.enemy.sim_instance.preload.strategy.apl_engine.apl)
and (arm := apl.action_replace_manager)
and (pas := arm.parry_aid_strategy)
and pas.consecutive_parry_mode
):
if ENEMY_ATTACK_REPORT:
self.enemy.sim_instance.schedule_data.change_process_state()
print(
f"当前前台角色{char_on_field}并未进行有效交互(没有正在进行的动作,上一个动作{nodes.skill_tag}已经在{nodes.end_tick}结束),将被打断!"
)
return
if any([_sub_tag in nodes.skill_tag for _sub_tag in ["parry", "dodge"]]):
# 直接交互
if ENEMY_ATTACK_REPORT:
self.enemy.sim_instance.schedule_data.change_process_state()
print(
f"角色{char_on_field}成功通过{nodes.skill_tag}与敌方的进攻进行交互。该技能从{nodes.preload_tick}开始,{nodes.end_tick}结束。"
)
self.answered_count += 1
if "parry" in nodes.skill_tag:
from zsim.sim_progress.Preload.APLModule.ActionReplaceManager import (
ActionReplaceManager,
)
action_replace_manager: ActionReplaceManager | None = (
sim_instance.preload.strategy.apl_engine.apl.action_replace_manager
)
if not action_replace_manager:
return
action = self.action
if not action:
return
if action.hit == 1:
self.enemy.sim_instance.schedule_data.change_process_state()
print(
f"【AtkEventManager】检测到来自角色{char_on_field}的招架技能{nodes.skill_tag},进攻交互式时间提前结束,角色即将被击退!"
)
action_replace_manager.parry_aid_strategy.knock_back_signal = True
action_replace_manager.parry_aid_strategy.final_parry_node = nodes
self.event_end(tick=tick)
else:
if action.hit == self.hitted_count:
action_replace_manager.parry_aid_strategy.knock_back_signal = True
action_replace_manager.parry_aid_strategy.final_parry_node = nodes
self.event_end(tick=tick)
print(
f"检测到来自角色{char_on_field}的招架技能{nodes.skill_tag},进攻交互式时间提前结束,角色即将被击退!"
)
else:
# 非直接交互
if nodes.skill.trigger_buff_level in [2, 4, 5, 6, 7, 8, 9]:
if ENEMY_ATTACK_REPORT:
self.enemy.sim_instance.schedule_data.change_process_state()
print(f"角色{char_on_field}选择释放{nodes.skill_tag}进行交互,交互成功。")
self.answered_count += 1
else:
if ENEMY_ATTACK_REPORT:
self.enemy.sim_instance.schedule_data.change_process_state()
print(f"角色被打断!{nodes.skill_tag}技能被迫取消!")
"""取消当前正在进行的技能,同时添加一次被打断技能,模拟角色动作被打断。"""
sim_instance.preload.preload_data.delete_mission_in_preload_data(
node_be_changed=nodes
)
sim_instance.preload.preload_data.external_add_skill(
skill_tuple=(f"{char_on_field}_interrupted", True, tick)
)
def __check_skill_interrupt_capability(self, skill_node: "SkillNode") -> bool:
"""检查技能是否能够被打断,这涉及到更加详细的打断参数比对,所以这里先用其他的逻辑代替。"""
if skill_node.skill.trigger_buff_level in self.interrupted_skill_type:
if skill_node.active_generation:
return True
return False
================================================
FILE: zsim/sim_progress/data_struct/LinkedList.c
================================================
#define PY_SSIZE_T_CLEAN
#include
#include
#include
#pragma execution_character_set("utf-8")
// 节点结构定义
typedef struct Node {
PyObject* data; // 存储 Python 对象
struct Node* next;
} Node;
// 链表结构定义
typedef struct {
PyObject_HEAD
Node* head;
int length;
} LinkedList;
// 定义迭代器结构
typedef struct {
PyObject_HEAD
Node* current;
LinkedList* list;
} LinkedListIterator;
// 迭代器的 next 方法
static PyObject* LinkedList_iternext(LinkedListIterator* iter) {
if (iter->current == NULL) {
PyErr_SetNone(PyExc_StopIteration);
return NULL;
}
PyObject* data = iter->current->data;
Py_INCREF(data);
iter->current = iter->current->next;
return data;
}
// 迭代器的析构函数
static void LinkedListIterator_dealloc(LinkedListIterator* self) {
Py_XDECREF(self->list);
PyObject_GC_UnTrack(self);
PyObject_GC_Del(self);
}
// 迭代器的遍历函数
static int LinkedListIterator_traverse(LinkedListIterator *self, visitproc visit, void *arg) {
Py_VISIT(self->list);
return 0;
}
// 定义迭代器类型
static PyTypeObject LinkedListIteratorType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "LinkedList.LinkedListIterator",
.tp_basicsize = sizeof(LinkedListIterator),
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_iternext = (iternextfunc)LinkedList_iternext,
.tp_dealloc = (destructor)LinkedListIterator_dealloc,
.tp_traverse = (traverseproc)LinkedListIterator_traverse,
};
// 创建新的链表对象
static PyObject* LinkedList_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
LinkedList* self = (LinkedList*)type->tp_alloc(type, 0);
if (self) {
self->head = NULL;
self->length = 0;
}
return (PyObject*)self;
}
// 销毁链表对象
static void LinkedList_dealloc(LinkedList* self) {
Node* current = self->head;
while (current) {
Node* temp = current;
Py_XDECREF(temp->data);
current = current->next;
free(temp);
}
Py_TYPE(self)->tp_free((PyObject*)self);
}
// 添加节点到尾部
static PyObject* LinkedList_add(LinkedList* self, PyObject* args) {
PyObject* data;
if (!PyArg_ParseTuple(args, "O", &data)) {
return NULL;
}
Py_INCREF(data);
Node* new_node = (Node*)malloc(sizeof(Node));
if (!new_node) {
Py_DECREF(data);
return PyErr_NoMemory();
}
new_node->data = data;
new_node->next = NULL;
if (!self->head) {
self->head = new_node;
} else {
Node* current = self->head;
while (current->next) {
current = current->next;
}
current->next = new_node;
}
self->length++;
Py_RETURN_NONE;
}
static PyObject* LinkedList_insert(LinkedList* self, PyObject* args) {
PyObject* data;
if (!PyArg_ParseTuple(args, "O", &data)) {
return NULL; // 如果参数解析失败,返回 NULL
}
Py_INCREF(data); // 增加引用计数,确保数据不会被回收
Node* new_node = (Node*)malloc(sizeof(Node));
if (!new_node) {
Py_DECREF(data);
return PyErr_NoMemory(); // 如果分配失败,返回内存错误
}
new_node->data = data;
new_node->next = self->head; // 新节点的 next 指向当前链表的头节点
self->head = new_node; // 更新链表头指针为新节点
self->length++; // 链表长度加 1
Py_RETURN_NONE; // 返回 None
}
static PyObject* LinkedList_pop_head(LinkedList* self, PyObject* Py_UNUSED(ignored)) {
// 检查链表是否为空
if (self->head == NULL) {
Py_RETURN_NONE; // 如果链表为空,返回 None
}
// 保存当前头节点
Node* old_head = self->head;
// 获取头节点的数据
PyObject* removed_data = old_head->data;
// 更新头节点为下一个节点
self->head = old_head->next;
// 释放旧头节点内存
free(old_head);
// 减少链表长度
self->length--;
// 增加返回数据的引用计数,防止被垃圾回收
Py_INCREF(removed_data);
return removed_data; // 返回移除的数据
}
static PyObject* LinkedList_remove(LinkedList* self, PyObject* data) {
if (self->head == NULL) {
PyErr_SetString(PyExc_ValueError, "Cannot remove from empty list.");
return NULL;
}
Node* current = self->head;
Node* prev = NULL;
while (current) {
int comparison_result = PyObject_RichCompareBool(current->data, data, Py_EQ);
if (comparison_result == -1) {
// 处理比较失败的情况
PyErr_SetString(PyExc_TypeError, "Error comparing data types.");
return NULL;
}
if (comparison_result) {
// 找到匹配项,进行删除操作
Py_XDECREF(current->data); // 安全地减少引用计数
if (prev == NULL) {
self->head = current->next;
} else {
prev->next = current->next;
}
free(current);
Py_RETURN_TRUE;
} else {
prev = current;
current = current->next;
}
}
Py_RETURN_FALSE;
}
// 创建新的迭代器对象
static PyObject* LinkedList_iter(LinkedList* self) {
LinkedListIterator* iter = (LinkedListIterator*)PyObject_GC_New(LinkedListIterator, &LinkedListIteratorType);
if (iter == NULL) {
return NULL;
}
iter->current = self->head;
iter->list = self;
Py_INCREF(self);
PyObject_GC_Track(iter);
return (PyObject*)iter;
}
static PyObject* LinkedList_getitem(LinkedList* self, Py_ssize_t index) {
if (index < 0 || index >= self->length) {
PyErr_SetString(PyExc_IndexError, "Index out of range.");
return NULL;
}
Node* current = self->head;
for (Py_ssize_t i = 0; i < index; i++) {
current = current->next;
}
Py_INCREF(current->data);
return current->data;
}
// 获取链表长度
static PyObject* LinkedList_length(LinkedList* self, void* closure) {
return self->length;
}
// 链表字符串表示
static PyObject* LinkedList_str(LinkedList* self) {
PyObject* result = PyList_New(0);
Node* current = self->head;
while (current) {
PyList_Append(result, current->data);
current = current->next;
}
PyObject* str_result = PyObject_Str(result);
Py_DECREF(result);
return str_result;
}
// 定义获取链表长度的函数
static Py_ssize_t LinkedList_sq_length(LinkedList* self) {
return self->length;
}
static PyObject* LinkedList_gethead(LinkedList* self, void* closure) {
if (self->head) {
Py_INCREF(self->head->data);
return self->head->data;
}
Py_RETURN_NONE;
};
static PyObject* LinkedList_getnext(LinkedList* self, void* closure) {
if (self->head) {
Py_INCREF(self->head->next);
return self->head->next;
}
Py_RETURN_NONE;
}
static int LinkedList_is_empty(LinkedList* self) {
return self->head == NULL;
}
static int LinkedList_bool(LinkedList* self) {
return !LinkedList_is_empty(self);
}
// 定义链表的方法
static PyMethodDef LinkedList_methods[] = {
{"add", (PyCFunction)LinkedList_add, METH_VARARGS, "Add an element to the linked list."},
{"insert", (PyCFunction)LinkedList_insert, METH_VARARGS, "Insert an element to the linked list."},
{"pop_head", (PyCFunction)LinkedList_pop_head, METH_NOARGS, "Remove and return the head element of the linked list."},
{"remove", (PyCFunction)LinkedList_remove, METH_VARARGS, "Remove an element from the linked list."},
{NULL} // Sentinel
};
// 定义链表的成员
static PyGetSetDef LinkedList_getset[] = {
{"head", (getter)LinkedList_gethead, NULL, "Get the head element of the linked list.", NULL},
{"next", (getter)LinkedList_getnext, NULL, "Get the next element of the linked list.", NULL},
{"length", (getter)LinkedList_length, NULL, "Get the length of the linked list.", NULL},
{NULL} // Sentinel
};
// 链表类型定义
static PyTypeObject LinkedListType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "LinkedList",
.tp_doc = "A simple linked list.",
.tp_basicsize = sizeof(LinkedList),
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_new = LinkedList_new,
.tp_dealloc = (destructor)LinkedList_dealloc,
.tp_methods = LinkedList_methods,
.tp_getset = LinkedList_getset,
.tp_str = (reprfunc)LinkedList_str,
.tp_iter = (getiterfunc)LinkedList_iter,
// .tp_bool = (inquiry)LinkedList_bool,
.tp_as_sequence = &(PySequenceMethods){
.sq_item = (ssizeargfunc)LinkedList_getitem,
.sq_length = (lenfunc)LinkedList_sq_length,
},
.tp_as_mapping = &(PyMappingMethods){
.mp_length = (lenfunc)LinkedList_sq_length,
},
};
// 模块初始化
static PyModuleDef LinkedListmodule = {
PyModuleDef_HEAD_INIT,
"LinkedList",
"A simple linked list module.",
-1,
};
PyMODINIT_FUNC PyInit_LinkedList(void) {
PyObject* m;
if (PyType_Ready(&LinkedListType) < 0) {
return NULL;
}
m = PyModule_Create(&LinkedListmodule);
if (!m) {
return NULL;
}
Py_INCREF(&LinkedListType);
PyModule_AddObject(m, "LinkedList", (PyObject*)&LinkedListType);
return m;
}
================================================
FILE: zsim/sim_progress/data_struct/LinkedList.py
================================================
from typing import Generic, TypeVar
T = TypeVar("T")
class Node(Generic[T]):
def __init__(self, data: T | None = None):
self.data: T | None = data
self.next: "Node[T] | None" = None
class NodeIterator(Generic[T]):
def __init__(self, head: Node[T] | None):
self.current: Node[T] | None = head
def __next__(self) -> T:
if self.current is None:
raise StopIteration
data = self.current.data
self.current = self.current.next
if data is None:
raise StopIteration
return data
def __iter__(self):
return self
class LinkedList(Generic[T]):
def __init__(self):
self.head: Node[T] | None = None
def add(self, data: T) -> None:
"""在链表尾部添加"""
new_node = Node(data)
if self.head is None:
self.head = new_node
else:
current = self.head
while current.next:
current = current.next
current.next = new_node
def insert(self, data: T) -> None:
"""在链表头部插入"""
new_node = Node(data)
new_node.next = self.head
self.head = new_node
def __iter__(self):
return NodeIterator(self.head)
def __str__(self) -> str:
elements = []
current = self.head
while current:
elements.append(current.data)
current = current.next
return str(elements)
def __len__(self) -> int:
count = 0
current = self.head
while current:
count += 1
current = current.next
return count
def __getitem__(self, index: int) -> Node[T]:
current = self.head
if current is None:
raise IndexError("Index out of range")
for _ in range(index):
current = current.next
if current is None:
raise IndexError("Index out of range")
return current
def print_list(self) -> None:
current = self.head
while current:
print(f"{current.data} -> ", end="")
current = current.next
print("None")
def pop_head(self) -> Node[T] | None:
if self.head is not None:
removed_node = self.head
self.head = self.head.next
return removed_node
else:
return None
def remove(self, data: T) -> bool:
current = self.head
previous = None
while current:
if current.data == data:
if previous:
previous.next = current.next
else:
self.head = current.next
return True
previous = current
current = current.next
return False
================================================
FILE: zsim/sim_progress/data_struct/NormalAttackManager/BaseNAManager.py
================================================
from abc import ABC
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from zsim.sim_progress.Character import Character
from zsim.sim_progress.Preload import SkillNode
class BaseNAManager(ABC):
def __init__(self, char_obj: "Character", rule_inventory_dict: dict):
self.char: "Character" = char_obj
self.na_rule_inventory: dict = rule_inventory_dict
self.RULE_MAP: dict = {"default": lambda: True}
self.special_first_hit_list = [1141]
@property
def first_hit(self) -> str:
"""首次普攻"""
return (
str(self.char.CID) + "_NA_1"
if self.char.CID not in self.special_first_hit_list
else str(self.char.CID) + "_SNA_1"
)
def na_rule_selector(self) -> dict[str, str]:
"""选择普攻策略!"""
for rule_name, check_func in self.RULE_MAP.items():
if check_func():
return self.na_rule_inventory[rule_name]
else:
return self.na_rule_inventory["default"]
def spawn_out_na(self, skill_node: "SkillNode") -> str:
"""生成普攻"""
_na_dict = self.na_rule_selector()
if skill_node.skill_tag in _na_dict:
return _na_dict[skill_node.skill_tag]
else:
return self.first_hit
================================================
FILE: zsim/sim_progress/data_struct/NormalAttackManager/NAManagerClasses.py
================================================
from typing import TYPE_CHECKING
from .BaseNAManager import BaseNAManager
if TYPE_CHECKING:
from zsim.sim_progress.Character.character import Character
from zsim.sim_progress.Character.Seed import Seed
class YanagiNAManager(BaseNAManager):
def __init__(self, char_obj, rule_inventory_dict: dict):
super().__init__(char_obj, rule_inventory_dict)
self.char: "Character" = char_obj
self.na_rule_inventory = rule_inventory_dict
self.RULE_MAP = {
"default": lambda: self.char.get_special_stats()["当前架势"]
and not self.char.get_special_stats()["森罗万象状态"],
"normal_kagen": lambda: (not self.char.get_special_stats()["当前架势"])
and not self.char.get_special_stats()["森罗万象状态"],
"shinra_jougen": lambda: self.char.get_special_stats()["当前架势"]
and self.char.get_special_stats()["森罗万象状态"],
"shinra_kagen": lambda: (not self.char.get_special_stats()["当前架势"])
and self.char.get_special_stats()["森罗万象状态"],
}
@property
def first_hit(self) -> str:
return "1221_NA_1" if self.char.get_special_stats()["当前架势"] else "1221_SNA_1"
class HugoNAManager(BaseNAManager):
def __init__(self, char_obj: "Character", rule_inventory_dict: dict):
super().__init__(char_obj, rule_inventory_dict)
self.char = char_obj
self.na_rule_inventory = rule_inventory_dict
from zsim.define import HUGO_NA_MODE_LEVEL
self.RULE_MAP = {
"default": lambda: HUGO_NA_MODE_LEVEL == 0,
"balanced_mode": lambda: HUGO_NA_MODE_LEVEL == 1,
"perfection_mode": lambda: HUGO_NA_MODE_LEVEL == 2,
"only_full_charge_na": lambda: HUGO_NA_MODE_LEVEL == 3,
}
class SeedNAManager(BaseNAManager):
def __init__(self, char_obj: "Character | Seed", rule_inventory_dict: dict):
super().__init__(char_obj, rule_inventory_dict)
from zsim.sim_progress.Character.Seed import Seed
self.char: Seed = char_obj
self.na_rule_inventory = rule_inventory_dict
self.RULE_MAP = {
"default": lambda: not self.char.steel_charge_enough,
"steel_charge_enough": lambda: self.char.steel_charge_enough,
}
================================================
FILE: zsim/sim_progress/data_struct/NormalAttackManager/__init__.py
================================================
import json
from zsim.define import APL_NA_ORDER_PATH, HUGO_NA_ORDER, SEED_NA_ORDER, YANAGI_NA_ORDER
from .BaseNAManager import BaseNAManager
from .NAManagerClasses import HugoNAManager, SeedNAManager, YanagiNAManager
NA_RULE_INVENTORY_PATH = {1221: YANAGI_NA_ORDER, 1291: HUGO_NA_ORDER, 1461: SEED_NA_ORDER}
NA_MANAGER_MAP = {1221: YanagiNAManager, 1291: HugoNAManager, 1461: SeedNAManager}
def na_manager_factory(char_obj) -> BaseNAManager:
char_cid = char_obj.CID
if char_cid in NA_RULE_INVENTORY_PATH:
path = NA_RULE_INVENTORY_PATH.get(char_cid)
else:
path = APL_NA_ORDER_PATH
if char_cid in NA_MANAGER_MAP:
with open(path, "r", encoding="utf-8") as file:
na_dict = json.load(file)
return NA_MANAGER_MAP.get(char_cid)(char_obj, na_dict)
else:
with open(path, "r", encoding="utf-8") as file:
all_default_na_dict = json.load(file)
char_na_dict = all_default_na_dict.get(str(char_cid))
dict_input = {"default": char_na_dict}
return BaseNAManager(char_obj, dict_input)
================================================
FILE: zsim/sim_progress/data_struct/PolarizedAssaultEventClass.py
================================================
from copy import deepcopy
from typing import TYPE_CHECKING
from zsim.define import ALICE_REPORT
from zsim.define import ELEMENT_TYPE_MAPPING as ETM
from zsim.models.event_enums import ListenerBroadcastSignal as LBS
if TYPE_CHECKING:
from zsim.sim_progress.anomaly_bar import AnomalyBar
from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import Disorder
from zsim.sim_progress.Character.character import Character
from zsim.sim_progress.Preload import SkillNode
class PolarizedAssaultEvent:
def __init__(
self,
execute_tick: int,
anomlay_bar: "AnomalyBar",
char_instance: "Character",
skill_node: "SkillNode",
):
"""这是爱丽丝的极性强击事件,该事件拥有最低的优先级,保证自己能够在本tick的最后才被递归执行
Args:
execute_tick: int: 该事件在Schedule阶段,被执行的tick
anomlay_bar: AnomalyBar: 极性强击事件基于当前强击异常条的状态,所以构造时必须传入强击异常条的深拷贝
char_instance: Character: 爱丽丝的char实例
skill_node: SkillNode: 触发极性强击事件的触发源(应该是大招或是三蓄力普攻)
"""
self.execute_tick = execute_tick # 被执行时间
self.schedule_priority = 998 # 该事件永远在被执行tick的末轮递归中执行
self.anomaly_bar: "AnomalyBar" = anomlay_bar # 强击异常条的深拷贝
assert not self.anomaly_bar.settled, (
"【极性强击事件警告】构造极性强击事件时,传入的异常条必须是未结算的异常条!"
)
self.anomaly_bar.anomaly_settled()
self.anomaly_bar.rename_tag = "极性强击"
self.char: "Character" = char_instance
if self.char.NAME != "爱丽丝":
raise ValueError(
f"【极性强击事件警告】构造极性强击事件时,传入的Char实例并非爱丽丝,而是{self.char.NAME}"
)
self.skill_node: "SkillNode" = skill_node # 极性强击触发源头
self.allowed_skill_tag_list: list[str] = ["1401_SNA_3", "1401_Q"] # 合法的极性强击触发源
if self.skill_node.skill_tag not in self.allowed_skill_tag_list:
raise ValueError(
f"【极性强击事件警告】检测到非法的极性强击触发源:{skill_node.skill_tag}"
)
else:
if skill_node.skill_tag == "1401_Q" and self.char.cinema < 2:
raise ValueError(
"【极性强击事件警告】检测到低于2画的爱丽丝企图用 大招 触发极性强击"
)
if self.anomaly_bar.element_type != 0:
raise ValueError(
f"【极性强击事件警告】构造极性强击事件时,必须传入物理异常条的深拷贝!当前传入的异常条属性为:{ETM[self.anomaly_bar.element_type]}"
)
self.sim_instance = self.anomaly_bar.sim_instance
def execute(self):
"""执行极性强击事件,向EventList添加强击、紊乱事件"""
# 先添加一次极性强击;
event_list = self.sim_instance.schedule_data.event_list
enemy = self.sim_instance.enemy
self.sim_instance.listener_manager.broadcast_event(
event=self.anomaly_bar, signal=LBS.POLARIZED_ASSAULT_SPAWN
)
# if self.anomaly_bar.settled:
event_list.append(self.anomaly_bar)
if ALICE_REPORT:
self.sim_instance.schedule_data.change_process_state()
print(
f"【爱丽丝事件】{self.skill_node.skill.skill_text} 触发的极性强击事件结算!向事件列表添加一个强击异常!"
)
# 更新畏缩状态
from zsim.sim_progress.Update.UpdateAnomaly import anomaly_effect_active
anomaly_effect_active(
bar=self.anomaly_bar,
timenow=self.sim_instance.tick,
enemy=enemy,
new_anomaly=self.anomaly_bar,
element_type=0,
sim_instance=self.sim_instance,
)
# 再检测敌人是否处于异常状态下,如果敌人当前存在异常状态则立刻触发一次紊乱
active_anomaly_list = enemy.dynamic.get_active_anomaly()
if not active_anomaly_list:
return
anomaly_bar = active_anomaly_list[0]
anomaly_bar_new = deepcopy(anomaly_bar)
if not anomaly_bar_new.settled:
anomaly_bar_new.anomaly_settled()
"""
由于爱丽丝的极性强击不影响原有的异常条状态,
所以这里必须用深拷贝规避结算紊乱函数对于异常条的破坏性修改
"""
from zsim.sim_progress.Update.UpdateAnomaly import spawn_output
disorder: "Disorder" = spawn_output(
anomaly_bar=anomaly_bar_new,
skill_node=self.skill_node,
mode_number=1,
sim_instance=self.sim_instance,
)
event_list.append(disorder)
if ALICE_REPORT:
self.sim_instance.schedule_data.change_process_state()
print(
f"【爱丽丝事件】同时,极性强击事件结算了一次【{ETM[disorder.element_type]}】属性的紊乱!"
)
================================================
FILE: zsim/sim_progress/data_struct/QuickAssistSystem/__init__.py
================================================
from __future__ import annotations
from typing import TYPE_CHECKING
from zsim.sim_progress.Buff import JudgeTools
from .quick_assist_manager import QuickAssistManager
if TYPE_CHECKING:
from zsim.sim_progress.Character.character import Character
from zsim.sim_progress.Preload import SkillNode
from zsim.simulator.simulator_class import Simulator
class QuickAssistSystem:
"""管理整个小队的系统,需要延迟创建。"""
def __init__(self, char_obj_list: list, sim_instance: Simulator):
self.sim_instance = sim_instance
self.char_obj_list: list["Character"] = char_obj_list
self.quick_assist_manager_group: dict[str, QuickAssistManager] = {}
for char_obj in self.char_obj_list:
self.quick_assist_manager_group[char_obj.NAME] = char_obj.dynamic.quick_assist_manager
def update(self, tick: int, skill_node: "SkillNode", all_name_order_box: dict[str, list[str]]):
"""外部接口,通过传入的skill_node来判断如何激活快速支援。"""
current_name_order_dict = all_name_order_box[skill_node.char_name]
if skill_node.skill.aid_direction == 0:
"""skill_node不影响快速支援状态"""
pass
elif skill_node.skill.aid_direction == 1:
"""skill_node会触发下一位角色的快速支援"""
active_char = current_name_order_dict[1]
active_manager = self.quick_assist_manager_group[active_char]
self.spawn_event_group(tick, skill_node, active_manager)
elif skill_node.skill.aid_direction == 2:
"""skill_node会触发上一位角色的快速支援"""
active_char = current_name_order_dict[2]
active_manager = self.quick_assist_manager_group[active_char]
self.spawn_event_group(tick, skill_node, active_manager)
else:
raise ValueError(f"无法解析的快速支援方向参数!{skill_node.skill.aid_direction}")
if skill_node.skill.trigger_buff_level == 7:
if not self.quick_assist_manager_group[skill_node.char_name].quick_assist_available:
if skill_node.char_name != "莱特":
"""这里需要放行莱特,因为莱特会自己触发快速支援。"""
raise ValueError(
f"在{skill_node.char_name}的快速支援没有亮起的情况下,打出了快速支援!"
)
self.answer_assist(tick, skill_node)
def answer_assist(self, tick: int, skill_node: "SkillNode"):
"""该函数用于在检测到角色响应了快速支援时,向Eventlist提前抛出结束事件。"""
char_name = skill_node.char_name
manager = self.quick_assist_manager_group[char_name]
end_event = QuickAssistEvent(
update_tick=tick,
updated_by=skill_node,
operation=False,
manager=manager,
answer=True,
)
event_list = JudgeTools.find_event_list(sim_instance=self.sim_instance)
event_list.append(end_event)
# print(f'{skill_node.char_name}响应了快速支援!')
def spawn_event_group(
self, tick_now: int, skill_node: "SkillNode", active_manager: QuickAssistManager
):
"""创建一个事件对,包含开始事件和结束事件,并将他们添加到event_list里面去。"""
start_event = QuickAssistEvent(
update_tick=tick_now,
updated_by=skill_node,
operation=True,
manager=active_manager,
)
end_event = QuickAssistEvent(
update_tick=tick_now,
updated_by=skill_node,
operation=False,
manager=active_manager,
)
start_event.manager.assist_event_update_tick = tick_now
start_event.manager.last_update_node = skill_node
end_event.manager.assist_event_update_tick = tick_now
event_list = JudgeTools.find_event_list(sim_instance=self.sim_instance)
event_list.append(start_event)
event_list.append(end_event)
def force_active_quick_assist(self, tick_now: int, skill_node: "SkillNode", char_name: str):
"""强制激活快速支援,主要是服务于外部调用。"""
self.spawn_event_group(tick_now, skill_node, self.quick_assist_manager_group[char_name])
class QuickAssistEvent:
"""快速支援事件"""
def __init__(
self,
update_tick: int,
updated_by: "SkillNode",
operation: bool,
manager: QuickAssistManager,
answer: bool = False,
):
self.operation = operation
self.updated_by = updated_by
self.manager = manager
self.exit_mode = answer
if self.operation:
self.execute_tick = update_tick + self.updated_by.skill.aid_lag_ticks
else:
if self.exit_mode:
self.execute_tick = update_tick
else:
self.execute_tick = (
update_tick + self.updated_by.skill.aid_lag_ticks + self.manager.max_duration
)
def execute_update(self, tick_now: int, answer: bool = False):
"""事件的自执行方法"""
if tick_now != self.execute_tick and self.operation:
raise ValueError(
f"{self.manager.char.NAME}的本次快速支援的激活更新理应在{self.execute_tick}tick执行,但实际执行于{tick_now}tick"
)
if self.operation:
self.manager.state_change(tick_now, operation="turn_on")
else:
self.manager.state_change(tick_now, operation="turn_off", answer=self.exit_mode)
================================================
FILE: zsim/sim_progress/data_struct/QuickAssistSystem/quick_assist_manager.py
================================================
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from zsim.sim_progress.Character import Character
from zsim.sim_progress.Preload import SkillNode
class QuickAssistManager:
"""角色个人的快支管理器"""
def __init__(self, char: "Character"):
self.char = char
self.start_tick = 0
self.max_duration = 60 # 快速支援亮起的最大持续时间
self.end_tick = 0
self.quick_assist_available = False # 快速支援是否亮起
self.quick_assist_skill = f"{self.char.CID}_BH_Aid" # 快速支援技能名
self.assist_event_update_tick = 0 # 快速支援事件上报给eventlist的tick
self.last_update_node: "SkillNode | None" = None # 上一次导致快速支援激活的技能。
def assist_waiting_for_anwser(self, tick: int) -> bool:
"""检查当前是否处于“快速支援即将触发但还未触发”的状态"""
checked_node = self.last_update_node
if checked_node is None:
# 如果last_update_node是None,那说明当前根本没有技能尝试触发过快速支援,直接返回False
return False
if checked_node.preload_tick + checked_node.skill.aid_lag_ticks > tick:
return True
else:
return False
def state_change(self, tick: int, **kwargs) -> None:
"""改变自身状态。"""
operation = kwargs.get("operation", None)
answer = kwargs.get("answer", False) # noqa: F841
if operation == "turn_on":
self.start_tick = tick
self.quick_assist_available = True
self.end_tick = tick + self.max_duration
# print(f'{self.char.NAME}的快速支援亮起了!')
elif operation == "turn_off":
if not self.quick_assist_available:
"""这个分支意味着,快速支援早就因角色提前响应而被关闭,此时不需要更新,直接return"""
return
self.quick_assist_available = False
self.end_tick = tick
# if answer:
# print(f'{self.char.NAME}响应了快速支援,使其提前结束!')
# else:
# print(f'{self.char.NAME}忽略了快速支援,使其到期结束!')
else:
raise ValueError("传入了快支管理器无法解析的参数!")
================================================
FILE: zsim/sim_progress/data_struct/SchedulePreload.py
================================================
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from zsim.sim_progress.Preload.PreloadDataClass import PreloadData
from zsim.simulator.simulator_class import Simulator
class SchedulePreload:
def __init__(
self,
preload_tick: int,
skill_tag: str,
preload_data=None,
apl_priority: int = 0,
active_generation: bool = False,
sim_instance: "Simulator | None" = None,
) -> None:
"""计划Preload事件,用于在指定的tick执行Preload的添加,通常用于协同攻击、附加伤害、延后追加攻击事件的创建。
Args:
preload_tick (int): 事件执行的tick
skill_tag (str): 事件的标签,用于识别事件
preload_data (PreloadData, optional): 事件的Preload数据,用于添加Preload。Defaults to None.
apl_priority (int, optional): 事件的APL优先级,用于排序。Defaults to 0.
active_generation (bool, optional): 事件是否为主动生成的,用于判断是否需要添加到事件列表中。Defaults to False.
sim_instance (Simulator | None, optional): 事件所在的模拟器实例,用于获取当前的tick和事件列表。Defaults to None.
"""
self.execute_tick: int = preload_tick
self.skill_tag: str = skill_tag
self.preload_data: "PreloadData | None" = preload_data
self.apl_priority: int = apl_priority
self.active_generation: bool = active_generation
self.sim_instance = sim_instance
def execute_myself(self):
if self.preload_data is None:
from zsim.sim_progress.Buff import JudgeTools
self.preload_data = JudgeTools.find_preload_data(sim_instance=self.sim_instance)
info_tuple = (self.skill_tag, self.active_generation, self.apl_priority)
self.preload_data.external_add_skill(info_tuple)
def schedule_preload_event_factory(
preload_tick_list: list[int],
skill_tag_list: list[str],
preload_data,
sim_instance: "Simulator",
apl_priority_list: list[int] | None = None,
active_generation_list: list[bool] | None = None,
) -> None:
"""根据传入的参数,生成SchedulePreload事件;通常情况下我们不通过构造函数直接创建SchedulePreload事件,而是通过调用此工厂函数来创建事件。
Args:
preload_tick_list (list[int]): 事件执行的tick列表
skill_tag_list (list[str]): 事件的标签列表,用于识别事件
preload_data (_type_): 事件的Preload数据,用于添加Preload
sim_instance (Simulator): 事件所在的模拟器实例,用于获取当前的tick和事件列表
apl_priority_list (list[int] | None, optional): 事件的APL优先级列表,用于排序。Defaults to None.
active_generation_list (list[bool] | None, optional): 事件是否为主动生成的列表,用于判断是否需要添加到事件列表中。Defaults to None.
"""
event_count = len(skill_tag_list)
from zsim.sim_progress.Buff import JudgeTools
tick_now = JudgeTools.find_tick(sim_instance=sim_instance)
event_list = JudgeTools.find_event_list(sim_instance=sim_instance)
if len(preload_tick_list) != event_count:
raise ValueError("preload_tick_list和skill_tag_list的长度不一致")
if apl_priority_list is not None and len(apl_priority_list) != event_count:
raise ValueError("apl_priority_list和skill_tag_list的长度不一致")
if active_generation_list is not None and len(active_generation_list) != event_count:
raise ValueError("active_generation_list和skill_tag_list的长度不一致")
for i in range(event_count):
preload_tick = preload_tick_list[i]
if preload_tick < tick_now:
raise ValueError("不能添加过去的Preload计划事件")
skill_tag = skill_tag_list[i]
apl_priority = apl_priority_list[i] if apl_priority_list is not None else 0
active_generation = (
active_generation_list[i] if active_generation_list is not None else False
)
schedule_event = SchedulePreload(
preload_tick, skill_tag, preload_data, apl_priority, active_generation
)
event_list.append(schedule_event)
================================================
FILE: zsim/sim_progress/data_struct/StunForcedTerminationEvent.py
================================================
from zsim.define import HUGO_REPORT
from zsim.sim_progress.Enemy import Enemy
class StunForcedTerminationEvent:
"""
强制结束失衡事件,该事件会强制结束怪物当前的失衡状态,并且返还一定比例的失衡值
目前只服务于雨果的决算机制。
"""
def __init__(
self,
enemy: Enemy,
stun_feed_back_ratio: float,
execute_tick: int,
event_source: str = "雨果",
):
self.enemy = enemy
self.feed_back_ratio = stun_feed_back_ratio
self.execute_tick = execute_tick # 执行时间
self.schedule_priority = 999
self.source = event_source
def execute_myself(self):
"""执行事件"""
if not self.enemy.dynamic.stun:
raise ValueError(f"执行强制结束失衡状态事件时,怪物{self.enemy.name}未处于失衡状态")
self.enemy.restore_stun()
self.enemy.dynamic.stun_bar += self.enemy.max_stun * self.feed_back_ratio
if HUGO_REPORT:
self.enemy.sim_instance.schedule_data.change_process_state()
print(f"失衡状态重置已经执行!成功返还{self.feed_back_ratio * 100}%的失衡值!")
================================================
FILE: zsim/sim_progress/data_struct/__init__.py
================================================
from .ActionStack import ActionStack, NodeStack
from .BattleEventListener import ListenerManger
from .data_analyzer import cal_buff_total_bonus
from .DecibelManager.DecibelManagerClass import Decibelmanager
from .EnemyAttackEvent import EnemyAttackEventManager
from .LinkedList import LinkedList
from .PolarizedAssaultEventClass import PolarizedAssaultEvent
from .QuickAssistSystem import QuickAssistEvent, QuickAssistSystem
from .SchedulePreload import SchedulePreload, schedule_preload_event_factory
from .single_hit import SingleHit
from .sp_update_data import ScheduleRefreshData, SPUpdateData
from .StunForcedTerminationEvent import StunForcedTerminationEvent
__all__ = [
"ActionStack",
"NodeStack",
"ListenerManger",
"cal_buff_total_bonus",
"Decibelmanager",
"EnemyAttackEventManager",
"LinkedList",
"QuickAssistSystem",
"QuickAssistEvent",
"SchedulePreload",
"schedule_preload_event_factory",
"SingleHit",
"SPUpdateData",
"ScheduleRefreshData",
"StunForcedTerminationEvent",
"PolarizedAssaultEvent",
]
================================================
FILE: zsim/sim_progress/data_struct/data_analyzer.py
================================================
from __future__ import annotations
from functools import lru_cache
from typing import TYPE_CHECKING, Any, Sequence
# from charset_normalizer.md import is_arabic_isolated_form
from zsim.define import BACK_ATTACK_RATE
from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import NewAnomaly
from zsim.sim_progress.Report import report_to_log
if TYPE_CHECKING:
from zsim.sim_progress.anomaly_bar import AnomalyBar
from zsim.sim_progress.Buff import Buff
from zsim.sim_progress.Preload.SkillsQueue import SkillNode
from zsim.simulator.simulator_class import Simulator
@lru_cache(maxsize=128)
def cal_buff_total_bonus(
enabled_buff: Sequence["Buff"],
judge_obj: "SkillNode | AnomalyBar | None" = None,
sim_instance: "Simulator" = None,
char_name: str | None = None,
) -> dict[str, float]:
"""过滤并计算buff总加成。
该方法首先读取buff效果的键值对,然后遍历提供列表的所有buff(一般为特定角色+怪物,具体参考调用方式)
对于每个buff,检查其是否为Buff类型,然后根据buff的计数(count)来计算总加成。
参数:
- enabled_buff: 包含需要处理的buff的列表。
- judge_obj: 可选的技能节点或异常状态,用于过滤buff。
返回:
- dict[str, float]: 包含所有buff总加成的键值对。
"""
# 初始化动态语句字典,用于累加buff效果的值
dynamic_statement: dict[str, float] = {}
# effect_buff_list: list[str] = []
# 遍历角色身上的所有buff
from zsim.sim_progress.anomaly_bar import AnomalyBar
from zsim.sim_progress.Buff import Buff
from zsim.sim_progress.Preload.SkillsQueue import SkillNode
# FIXME:
# 已知bug:武器【时流贤者】的“对佩戴者造成的紊乱伤害增幅”的效果在部分紊乱、极性紊乱上会失效,原因未知,等待继续排查。
buff_obj: Buff
for buff_obj in enabled_buff:
# 确保buff是Buff类的实例
if not isinstance(buff_obj, Buff):
raise TypeError(f"{buff_obj} 不是Buff类型,无法计算!")
else:
# 检查buff是否激活
if not buff_obj.dy.active:
report_to_log(f"[Warning] 动态buff列表中混入了未激活buff: {str(buff_obj)},已跳过")
continue
# 检查buff的标签是否与技能节点匹配
if judge_obj is not None:
"""
下面几个continue作用:筛选掉无法对当前judge_obj生效的buff。
一般来说,buff都是默认对所有的judge_obj产生效果的,但是有一类buff不是。
这类的buff通常带有标签,比如only_skill或者only_anomaly,或者only_label……
这些buff只有在特定条件被满足的情况下才会对当前技能生效——__check_skill_node() 和 __check_special_anomlay()这几个函数就是用来检查这个的。
总之,被continue跳过的Buff一定自带label或是其他的特殊判定条件,并且和当前的检查对象——judge_obj不相符,导致它对当前检测对象无法生效。
"""
if not __check_activation_origin(
buff_obj=buff_obj,
judge_obj=judge_obj,
sim_instance=sim_instance,
char_name=char_name,
):
continue
if isinstance(judge_obj, SkillNode) and not __check_skill_node(buff_obj, judge_obj):
continue
if isinstance(judge_obj, AnomalyBar) and not __check_special_anomly(
buff_obj, judge_obj
):
continue
# 获取buff的层数
count = buff_obj.dy.count
count = count if count > 0 else 0
# 遍历buff的每个效果和对应的值,并将其累加
# if buff_obj.ft.label and judge_obj is not None:
# if 'only_label' in buff_obj.ft.label.keys():
# print(f'{buff_obj.ft.index}通过了判定,享受该buff加成的对象为:{judge_obj}')
for key, value in buff_obj.effect_dct.items():
# 如果键值对在动态语句字典中,则累加值,否则初始化并赋值
try:
dynamic_statement[key] = dynamic_statement.get(key, 0) + value * count
except TypeError:
continue
# effect_buff_list.append(buff_obj)
# if judge_obj is not None and isinstance(judge_obj, SkillNode):
# if "1291_CorePassive" in judge_obj.skill_tag:
# print(f"检测到决算{judge_obj.skill_tag}, 其享受的buff列表为:")
# for _buff in effect_buff_list:
# print(f"{_buff.ft.index}: {_buff.effect_dct}")
return dynamic_statement
def __check_skill_node(buff: "Buff", skill_node: "SkillNode") -> bool:
"""
检查 buff 的标签是否与 skill node 匹配。
该方法用于验证 buff 的标签限制条件是否满足技能节点的要求。检查标签类型:
1. only_skill: buff仅对特定技能标签生效
2. only_label: buff仅对带有特定标签的技能生效
注意:不同的label之间是AND关系,单个label内的列表元素是OR关系。
参数:
buff (Buff): 需要检查的buff对象
skill_node (SkillNode): 技能节点对象
返回:
bool: 如果buff标签与技能节点匹配则返回True,否则返回False
"""
# 定义允许的标签类型
ALLOWED_LABELS = [
"only_skill",
"only_label",
"only_trigger_buff_level",
"only_back_attack",
"only_element",
"only_skill_type",
]
# 获取buff的标签列表
buff_labels: dict[str, list[str] | str] = buff.ft.label
# 如果buff没有标签限制,则直接返回True
if not buff_labels:
return True
# 获取技能节点的标签信息
skill_tag: str = skill_node.skill_tag
skill_labels: dict[str, Any] = skill_node.labels
# 用于记录是否检查了相关的label类型
has_relevant_labels = False
# 用于记录所有相关label是否都满足条件
all_labels_satisfied = True
# 遍历buff的所有标签进行检查
for label_key, label_value in buff_labels.items():
"""注意,在Buff端,label总是以 {str, list[str]}的形式存在的,这里要针对这一特性进行处理。"""
if not label_value:
continue
if not isinstance(label_value, list):
raise TypeError(
f"Buff {buff} 的标签 {label_key} 的值存在,对应Value为:{label_value} ,但不是列表类型,请检查初始化或者数据库。"
)
# 检查是否为允许的标签类型,不在ALLOWED_LABELS中的label,直接跳过
if any(
[
__check_label_key(label_key=label_key, target_label_key=_tlk)
for _tlk in ALLOWED_LABELS
]
):
has_relevant_labels = True
label_satisfied = False
# 对于合法label,要进行分类讨论
# 检查是否为特定技能限制
if __check_label_key(label_key=label_key, target_label_key="only_skill"):
if skill_tag in label_value:
label_satisfied = True
# print(f"{buff.ft.index}对技能{skill_tag}成功生效!")
# 检查是否为特定标签限制
elif __check_label_key(label_key=label_key, target_label_key="only_label"):
"""
当被检查技能完全不存在label属性时,说明该技能是无标签的普通技能。
而当前分支检查的是"技能是否具有Buff指定的标签",所以这里无需继续遍历,直接continue。
"""
if skill_labels is None:
# 如果技能没有标签属性,那么只有标签限制无法满足
label_satisfied = False
elif any(_sub_label in skill_labels.keys() for _sub_label in label_value):
# print(f'在技能 {skill_tag} 中,成功找到标签 {label_value},')
label_satisfied = True
elif __check_label_key(label_key=label_key, target_label_key="only_trigger_buff_level"):
if skill_node.skill.trigger_buff_level in label_value:
# print(f"{buff.ft.index}对技能{skill_tag}成功生效!")
label_satisfied = True
elif __check_label_key(label_key=label_key, target_label_key="only_back_attack"):
from zsim.sim_progress.RandomNumberGenerator import RNG
rng: RNG = buff.sim_instance.rng_instance
normalized_value = rng.random_float()
if normalized_value <= BACK_ATTACK_RATE:
label_satisfied = True
elif __check_label_key(label_key=label_key, target_label_key="only_element"):
from zsim.define import ELEMENT_EQUIVALENCE_MAP
if not isinstance(label_value, list):
raise TypeError(
f"Buff {buff} 的标签 {label_key} 的值存在,对应Value为:{label_value} ,但不是列表类型,请检查初始化或者数据库。"
)
for _ele_type in label_value:
if skill_node.element_type in ELEMENT_EQUIVALENCE_MAP[_ele_type]:
# 只要找到一种符合要求的元素,就满足这个label
label_satisfied = True
break
elif __check_label_key(label_key=label_key, target_label_key="only_skill_type"):
if skill_node.skill.skill_type in label_value:
label_satisfied = True
# 如果当前label不满足条件,那么整个AND条件就不满足
if not label_satisfied:
all_labels_satisfied = False
# 如果没有任何相关的label类型,说明buff没有限制条件
if not has_relevant_labels:
return True
# 只有所有相关的label都满足条件,才返回True
return all_labels_satisfied
def __check_label_key(label_key: str, target_label_key: str):
"""用于筛选出对应的label"""
pattern = r"_\d{1,2}$" # 匹配结尾是_加1-2位数字
import re
if bool(re.search(pattern, label_key)):
base_key = label_key.rsplit("_", 1)[0]
else:
base_key = label_key
return base_key == target_label_key
def __check_special_anomly(buff: "Buff", anomaly_node: "AnomalyBar") -> bool:
"""
检查 buff 的标签是否与异常匹配。
检查标签类型:
1. only_anomly: buff仅对特定异常状态生效
- Disorder: 紊乱
- Abloom: 异放
- PolarityDisorder: 极性紊乱
参数:
buff (Buff): 需要检查的buff对象
anomly_node (AnomalyBar的各种子类): 异常状态对象
返回:
bool: 如果buff标签与异常状态节点匹配则返回True,否则返回False
"""
# 导入异常状态相关的类
from zsim.sim_progress.anomaly_bar.Anomalies import (
AuricInkAnomaly,
ElectricAnomaly,
EtherAnomaly,
FireAnomaly,
FrostAnomaly,
IceAnomaly,
PhysicalAnomaly,
)
from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import (
DirgeOfDestinyAnomaly as Abloom,
)
from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import (
Disorder,
PolarityDisorder,
)
# 定义允许的标签类型
ALLOW_LABELS = ["only_anomaly", "specified_disorder_element_type"]
# 定义异常状态类型映射字典
SELECT_ANOMALY_MAP = {
"Disorder": [Disorder],
"Abloom": [Abloom],
"PolarityDisorder": [PolarityDisorder],
"AllAnomaly": [
IceAnomaly,
PhysicalAnomaly,
FireAnomaly,
FrostAnomaly,
EtherAnomaly,
ElectricAnomaly,
AuricInkAnomaly,
NewAnomaly,
],
}
# 获取buff的标签列表
buff_labels: dict[str, list[str] | str] = buff.ft.label
# 如果buff没有标签限制,则直接返回True
if not buff_labels:
return True
# 遍历buff的所有标签进行检查
for label_key, label_value in buff_labels.items():
# 跳过空值标签
if not label_value:
continue
# 检查是否为允许的标签类型
if label_key in ALLOW_LABELS:
if label_key == "only_anomaly": # 特定异常类型的判断
# 输入为单个字符串
if isinstance(label_value, str):
if label_value in SELECT_ANOMALY_MAP.keys():
for sig_value in SELECT_ANOMALY_MAP[label_value]:
if isinstance(anomaly_node, sig_value):
# buff.sim_instance.schedule_data.change_process_state()
# print(f"{ELEMENT_TYPE_MAPPING[anomaly_node.element_type]}属性的{type(anomaly_node).__name__}对象成功与{buff.ft.index}匹配")
return True
# 输入为列表
if isinstance(label_value, list):
for checked_value in label_value:
if checked_value in SELECT_ANOMALY_MAP.keys():
for sig_value in SELECT_ANOMALY_MAP[checked_value]:
if isinstance(anomaly_node, sig_value):
# buff.sim_instance.schedule_data.change_process_state()
# print(f"{ELEMENT_TYPE_MAPPING[anomaly_node.element_type]}属性的{type(anomaly_node).__name__}对象成功与{buff.ft.index}匹配")
return True
elif label_key == "specified_disorder_element_type": # 制定元素类型的紊乱判断
# 首先,它得是个紊乱或是极性紊乱
if not isinstance(anomaly_node, Disorder | PolarityDisorder):
continue
if isinstance(label_value, int):
if anomaly_node.element_type == label_value:
# buff.sim_instance.schedule_data.change_process_state()
# print(f"【测试】{ELEMENT_TYPE_MAPPING[anomaly_node.element_type]}属性的{type(anomaly_node).__name__}对象成功与{buff.ft.index}匹配")
return True
if isinstance(label_value, list):
for checked_value in label_value:
if checked_value == anomaly_node.element_type:
# buff.sim_instance.schedule_data.change_process_state()
# print(f"【测试】{ELEMENT_TYPE_MAPPING[anomaly_node.element_type]}属性的{type(anomaly_node).__name__}对象成功与{buff.ft.index}匹配")
return True
else:
buff.sim_instance.schedule_data.change_process_state()
print(
f"【data_analyzer警告】识别到暂无处理逻辑的标签类型:{label_key},故当前buff{buff.ft.index}无法对对象{type(anomaly_node).__name__}生效"
)
return False
return False
def __check_activation_origin(
buff_obj: "Buff", judge_obj: "SkillNode | AnomalyBar", sim_instance: "Simulator", char_name: str
):
"""检查buff的label是否存在“only_active_by”,然后再检查当前被检项目与源头是否匹配。
- buff_obj: 被检查的buff
- judge_obj: 被检查的对象,可能是SkillNode或者异常"""
if buff_obj.ft.label is None:
return True
if "only_active_by" not in buff_obj.ft.label.keys():
return True
from zsim.sim_progress.anomaly_bar import AnomalyBar
from zsim.sim_progress.Preload import SkillNode
CID_list = buff_obj.ft.label.get("only_active_by")
# FIXME: 当队伍中同时存在两把“时流贤者”时,Buff源检验可能会出错,暂不确定。
if CID_list[0] == "self":
"""
注:当Buff的only_active_by的值为self时,
其语义为:“只有自己激活/释放的对象(技能、异常伤害)才能享受到这个buff的加成”
这里的“自己”,指的应该是Buff的受益者(beneficiary),而不是Buff的实际操作者(operator)
举例:如果某把武器会给三名队友上一个“自己触发的属性异常的伤害提升”的Buff,那么这里对比的就是触发源的角色以及当前buff的受益者。
所以这里的self指的是beneficiary
"""
beneficiary = buff_obj.ft.beneficiary # Buff的实际受益者
if isinstance(judge_obj, SkillNode):
skill_result = beneficiary == judge_obj.char_name
return skill_result
elif isinstance(judge_obj, AnomalyBar):
if judge_obj.activated_by is None:
print(f"未检测到异常对象{judge_obj.element_type}的激活源!")
return False
anomaly_result = judge_obj.activated_by.char_name == beneficiary
return anomaly_result
else:
print(f"judge_obj的类型未定义!{type(judge_obj)}")
return False
else:
print(f"尚未定义的“only_active_by参数{CID_list}")
return False
if __name__ == "__main__":
base_key = "only_skill"
key_1 = "only_skill_1"
key_2 = "only_skill_trigger_buff_level"
key_3 = "only_skill_trigger_buff_level_1"
list1 = [key_1, key_2, key_3]
for _key in list1:
print(__check_label_key(label_key=_key, target_label_key=base_key))
================================================
FILE: zsim/sim_progress/data_struct/enemy_special_state_manager/__init__.py
================================================
from .special_state_class import EnemySpecialState
from .special_state_manager_class import SpecialStateManager
__all__ = ["SpecialStateManager", "EnemySpecialState"]
================================================
FILE: zsim/sim_progress/data_struct/enemy_special_state_manager/special_classes.py
================================================
from typing import TYPE_CHECKING
from zsim.define import ELEMENT_TYPE_MAPPING as ETM
from zsim.define import YUZUHA_REPORT, ElementType
from zsim.models.event_enums import SpecialStateUpdateSignal as SSUS
from .special_state_class import EnemySpecialState
if TYPE_CHECKING:
from zsim.sim_progress.data_struct import SingleHit
from zsim.sim_progress.Enemy import Enemy
from zsim.sim_progress.Preload import SkillNode
from .special_state_manager_class import SpecialStateManager
class SweetScare(EnemySpecialState):
"""柚叶的甜蜜惊吓特殊状态"""
def __init__(self, enemy_instance: "Enemy", manager_instance: "SpecialStateManager"):
super().__init__(enemy_instance=enemy_instance, manager_instance=manager_instance)
self.flavor_match_element: ElementType | None = None # 染色种类
self.leagle_skill_tag: list[str] = ["1411_SNA_A", "1411_SNA_B"]
self.active_origin_skill_tag: list[str] = ["1411_E_EX_A", "1411_E_EX_B", "1411_Q"]
self.max_duration: int = 2400 # 持续时间
self.description = "甜蜜惊吓"
self.sim_instance = self.enemy.sim_instance
self.sugarburst_sparkless_max_trigger_origin_skill_tag: list[str] = [
"1411_Assault_Aid_A",
"1411_CoAttack_A",
]
self.sugarburst_sparkless_max: str = "1411_SNA_B"
self.sugarburst_sparkless: str = "1411_SNA_A"
self.sugarburst_sparkless_update_tick: int = 0 # 彩糖花火上次更新的时间
self.sugarburst_sparkless_cd: int = 60 # 彩糖花火CD
@property
def flavor_match(self) -> bool:
"""【十人十色】状态(是否被染色)"""
if self.active and self.flavor_match_element is not None:
return True
else:
return False
def start(self):
"""甜蜜惊吓的启动逻辑"""
self.active = True
self.last_update_tick = self.enemy.sim_instance.tick
self.flavor_match_element = None
if YUZUHA_REPORT:
sim_instance = self.enemy.sim_instance
sim_instance.schedule_data.change_process_state()
print(
f"【甜蜜惊吓】状态激活!本次状态预计于{sim_instance.tick + self.max_duration}tick结束"
)
@property
def sugarburst_sparkless_ready(self):
if self.sugarburst_sparkless_update_tick == 0:
return True
if (
self.sim_instance.tick - self.sugarburst_sparkless_update_tick
>= self.sugarburst_sparkless_cd
):
return True
return False
def end(self):
"""甜蜜惊吓的结束逻辑"""
self.active = False
self.flavor_match_element = None
def update(self, update_signal: SSUS, **kwargs):
"""甜蜜惊吓的自更新函数,该函数需要在两个被广播式地调用,所以需要传入更新信号"""
if update_signal == SSUS.RECEIVE_HIT:
self.__update_when_receive_hit(single_hit=kwargs.get("single_hit"))
elif update_signal == SSUS.BEFORE_PRELOAD:
self.__update_when_after_preload()
elif update_signal == SSUS.CHARACTER:
self.__update_when_in_character(**kwargs)
else:
self.enemy.sim_instance.schedule_data.change_process_state()
print(
f"【特殊状态:甜蜜惊吓 警告】接收到了与自己分组无关的信号{update_signal.value},请检查逻辑以及数据库填写"
)
return
def try_change_attribute(self, skill_node: "SkillNode"):
"""外部调用接口,尝试进行染色"""
if not self.flavor_match:
return
if skill_node.skill_tag in self.leagle_skill_tag:
self.attribute_changing(skill_node)
def attribute_changing(self, skill_node: "SkillNode"):
"""染色的业务逻辑"""
if skill_node.skill_tag not in self.leagle_skill_tag:
self.enemy.sim_instance.schedule_data.change_process_state()
print(
f"【特殊状态:甜蜜惊吓 警告】自检函数放行了一个不能被染色的技能:{skill_node.skill_tag}"
)
return
skill_node.effective_anomaly_buildup = False # 将其改为无效积蓄
skill_node.element_type_change = self.flavor_match_element # 染色
# if YUZUHA_REPORT:
# self.enemy.sim_instance.schedule_data.change_process_state()
# print(f"【甜蜜惊吓染色】技能{skill_node.skill_tag}被染色为{ETM.get(skill_node.element_type)}属性")
def flavor_match_update(self, skill_node: "SkillNode"):
"""【甜蜜惊吓】状态正式被染色"""
self.flavor_match_element = skill_node.element_type
if YUZUHA_REPORT:
self.enemy.sim_instance.schedule_data.change_process_state()
print(
f"【甜蜜惊吓】状态被技能{skill_node.skill_tag}染为 {ETM.get(skill_node.element_type)} 属性!这将在接下来的2400tick内改变所有【彩糖花火】的属性!"
)
def __update_when_receive_hit(self, single_hit: "SingleHit | None", **kwargs):
"""在受到攻击时执行,主要负责:彩糖花火·极、甜蜜惊吓状态的开启、染色状态的开启"""
if single_hit is None:
self.enemy.sim_instance.schedule_data.change_process_state()
print("【特殊状态:甜蜜惊吓 警告】在receive_hit的节点没有接收到预期中的SingleHit")
return
"""首先要检查的是彩糖花火·极的触发"""
if single_hit.skill_tag in self.sugarburst_sparkless_max_trigger_origin_skill_tag:
if single_hit.heavy_hit:
from zsim.sim_progress.data_struct import schedule_preload_event_factory
skill_tag_list = [self.sugarburst_sparkless_max]
preload_tick_list = [self.sim_instance.tick]
schedule_preload_event_factory(
skill_tag_list=skill_tag_list,
preload_tick_list=preload_tick_list,
preload_data=self.sim_instance.preload.preload_data,
apl_priority_list=[-1],
sim_instance=self.sim_instance,
)
if YUZUHA_REPORT:
self.sim_instance.schedule_data.change_process_state()
assert single_hit.skill_node is not None
print(
f"【甜蜜惊吓触发】由 {single_hit.skill_node.skill.skill_text} 触发了一次【彩糖花火·极】"
)
"""若single_hit是那几个会刷新甜蜜惊吓状态的技能,那么优先执行重启判定"""
if single_hit.skill_tag in self.active_origin_skill_tag:
if single_hit.heavy_hit:
if self.active:
self.end()
self.start()
return
"""剩下的所有hit情况,才会进入染色判定逻辑"""
if self.active:
if not self.flavor_match:
if (
int(single_hit.skill_tag.strip().split("_")[0])
== self.sim_instance.preload.preload_data.operating_now
):
if single_hit.skill_node is None:
self.sim_instance.schedule_data.change_process_state()
print(
"【特殊状态:甜蜜惊吓警告】在试图更新染色状态时,检测到SingleHit并未关联SkillNode!染色失败!"
)
return
self.flavor_match_update(skill_node=single_hit.skill_node)
def __update_when_after_preload(self, **kwargs):
"""在Preload的最开始阶段执行,主要负责彩糖花火的触发"""
if self.active:
if self.sim_instance.tick - self.last_update_tick >= self.max_duration:
self.end()
return
if self.sugarburst_sparkless_ready:
from zsim.sim_progress.data_struct import schedule_preload_event_factory
skill_tag_list = [self.sugarburst_sparkless]
preload_tick_list = [self.sim_instance.tick]
schedule_preload_event_factory(
skill_tag_list=skill_tag_list,
preload_tick_list=preload_tick_list,
preload_data=self.sim_instance.preload.preload_data,
apl_priority_list=[-1],
sim_instance=self.sim_instance,
)
self.sugarburst_sparkless_update_tick = self.sim_instance.tick
def __update_when_in_character(self, skill_node: "SkillNode", **kwargs):
"""在Character内部执行,主要负责染色"""
self.try_change_attribute(skill_node=skill_node)
================================================
FILE: zsim/sim_progress/data_struct/enemy_special_state_manager/special_state_class.py
================================================
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
from zsim.models.event_enums import SpecialStateUpdateSignal as SSUS
if TYPE_CHECKING:
from zsim.sim_progress.Enemy import Enemy
from .special_state_manager_class import SpecialStateManager
class EnemySpecialState(ABC):
"""管理敌人特殊状态的数据结构基类"""
@abstractmethod
def __init__(self, enemy_instance: "Enemy", manager_instance: "SpecialStateManager", **kwargs):
self.enemy = enemy_instance
self.manager = manager_instance
self.active: bool = False # 激活状态
self.last_update_tick: int = 0 # 最近更新时间
self.max_duration: int = 0 # 持续时间
self.description: str | None = None # 说明
@abstractmethod
def start(self):
"""开始函数"""
pass
@abstractmethod
def update(self, update_signal: SSUS, **kwargs):
"""
状态更新函数!无论是在Preload内部传给Enemy,还是在Enemy的receive hit阶段,
都需要调用此函数并且使用不同的信号来区分业务逻辑;
"""
pass
@abstractmethod
def end(self):
"""结束函数!"""
pass
================================================
FILE: zsim/sim_progress/data_struct/enemy_special_state_manager/special_state_manager_class.py
================================================
import importlib
from typing import TYPE_CHECKING
from zsim.models.event_enums import PostInitObjectType as PIOT
from zsim.models.event_enums import SpecialStateUpdateSignal as SSUS
from .special_state_class import EnemySpecialState
if TYPE_CHECKING:
from zsim.sim_progress.Enemy import Enemy
class SpecialStateManager:
def __init__(self, enemy_instance: "Enemy"):
"""管理敌人特殊状态的管理器"""
self.enemy = enemy_instance
self.observers: dict[SSUS, list[EnemySpecialState]] = {}
for signal in SSUS:
self.observers[signal] = []
def register(self, state: EnemySpecialState, signals: list[SSUS]):
"""注册对象到特定信号组"""
for signal in signals:
if state not in self.observers[signal]:
self.observers[signal].append(state)
self.enemy.sim_instance.schedule_data.change_process_state()
print(f"【特殊状态管理器】已完成特殊状态【{state}】的注册!")
def broadcast_and_update(self, signal: SSUS, **kwargs):
"""向所有的事件组广播事件,并执行自检和业务逻辑函数"""
for state in self.observers[signal]:
# if state.active:
try:
state.update(signal, **kwargs)
except Exception as e:
print(f"广播错误 ({signal.name}): {e}")
def special_state_factory(self, state_type: PIOT, **kwargs):
"""工厂函数"""
state_info = state_type.value
class_name = state_info[0]
signal_list = state_info[1]
module = importlib.import_module(".special_classes", package=__package__)
state_class = getattr(module, class_name)
state_instance: EnemySpecialState = state_class(
enemy_instance=self.enemy, manager_instance=self, **kwargs
)
self.register(state=state_instance, signals=signal_list)
return state_instance
================================================
FILE: zsim/sim_progress/data_struct/monitor_list_class.py
================================================
def monitor_list_operation(func):
"""装饰器:监视列表操作并打印变化信息"""
def wrapper(self, *args, **kwargs):
# 操作前状态
# print(f"Before {func.__name__}: {self}")
# 执行原始操作
result = func(self, *args, **kwargs)
# 操作后状态
# print(f"After {func.__name__}: {self}")
return result
return wrapper
class MonitoredList(list):
"""自定义列表类,监视 append/remove 操作"""
@monitor_list_operation
def append(self, item):
# print(f"添加了{item.ft.index}dot")
from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar
if isinstance(item, AnomalyBar):
if not item.settled:
raise ValueError("不能添加未结算的异常条")
super().append(item) # 调用父类方法
@monitor_list_operation
def remove(self, item):
# print(f"移除了{item.ft.index}dot")
super().remove(item) # 调用父类方法
================================================
FILE: zsim/sim_progress/data_struct/single_hit.py
================================================
from dataclasses import dataclass
from typing import TYPE_CHECKING
import numpy as np
if TYPE_CHECKING:
from zsim.sim_progress.Preload import SkillNode
@dataclass
class SingleHit:
"""Feedback to enemy for a single hit."""
skill_tag: str
snapshot: tuple[int, np.float64, np.ndarray]
stun: np.float64
dmg_expect: np.float64
dmg_crit: np.float64
hitted_count: int
proactive: bool # 该动作是否为主动技能(主要依靠检测skill_node的follow_by参数)
heavy_hit = False # 重攻击标签——默认重攻击是 heavy_attack为True的技能的最后一个Hit
skill_node: "SkillNode | None" = None
def effective_anomlay_buildup(self) -> bool:
"""是否是有效积蓄"""
if self.skill_node is None:
return False
return self.skill_node.effective_anomaly_buildup
@property
def force_qte_trigger(self) -> bool:
if self.skill_node is None:
return False
skill_node: "SkillNode" = self.skill_node
return skill_node.force_qte_trigger
@dataclass
class AnomalyHit:
"""Feedback to enemy for a single anomaly hit."""
skill_tag: str
snapshot: tuple[int, np.float64, np.ndarray]
================================================
FILE: zsim/sim_progress/data_struct/sp_update_data.py
================================================
from typing import TYPE_CHECKING, Generator
from .data_analyzer import cal_buff_total_bonus
if TYPE_CHECKING:
from zsim.sim_progress.Character import Character
class SPUpdateData:
def __init__(self, char_obj: "Character", dynamic_buff: dict):
"""更新角色SP时的专用数据结构,仅用于传递角色的静态与动态的能量自动回复效率"""
self.char_name = char_obj.NAME
self.static_sp_regen: float = char_obj.statement.sp_regen
enabled_buff: Generator = (buff for buff in dynamic_buff[self.char_name])
self.dynamic_sp_regen: tuple[float, float] = self.__cal_dynamic_sp_regen(enabled_buff)
@staticmethod
def __cal_dynamic_sp_regen(enabled_buff: Generator):
buff_bonus: dict = cal_buff_total_bonus(enabled_buff)
dynamic_sp_regen = buff_bonus.get("能量自动恢复", 0) + buff_bonus.get("局内能量自动恢复", 0)
dynamic_sp_gain_ratio = buff_bonus.get("局内能量获得效率", 0)
return dynamic_sp_regen, dynamic_sp_gain_ratio
def get_sp_regen(self) -> float:
sp_regen = (self.static_sp_regen + self.dynamic_sp_regen[0]) * (
self.dynamic_sp_regen[1] + 1
)
return sp_regen
class ScheduleRefreshData:
def __init__(
self,
*,
sp_target: tuple[str] | None = None,
sp_value: float | int = 0,
decibel_target: tuple[str] | None = None,
decibel_value: float | int = 0,
**kwargs,
):
# 避免可变默认参数
self.sp_target: tuple[str] = sp_target if sp_target is not None else ("",)
self.decibel_target: tuple[str] = decibel_target if decibel_target is not None else ("",)
# 类型检查和异常处理
if not isinstance(sp_value, (float, int)):
raise TypeError("sp_value must be a number")
if not isinstance(decibel_value, (float, int)):
raise TypeError("decibel_value must be a number")
self.sp_value = sp_value
self.decibel_value = decibel_value
# 输入验证
if not self.sp_target or not all(isinstance(item, str) for item in self.sp_target):
raise ValueError("sp_target must be a non-empty tuple of strings")
if not self.decibel_target or not all(
isinstance(item, str) for item in self.decibel_target
):
raise ValueError("decibel_target must be a non-empty tuple of strings")
allowed_kwargs = {
"additional_param1",
"additional_param2",
} # 根据实际情况定义允许的额外参数
for key, value in kwargs.items():
if key in allowed_kwargs:
setattr(self, key, value)
else:
raise ValueError(f"Unexpected keyword argument: {key}")
================================================
FILE: zsim/sim_progress/data_struct/summons_event/__init__.py
================================================
from .summons_event_class import SummonsEvent
__all__ = ["SummonsEvent"]
================================================
FILE: zsim/sim_progress/data_struct/summons_event/summons_event_class.py
================================================
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
from zsim.sim_progress.summons.summons_class import Summons
if TYPE_CHECKING:
from zsim.simulator.simulator_class import Simulator
class SummonsEvent(ABC):
@abstractmethod
def __init__(self, summons_obj: Summons, execute_tick: int, event: object | None = None):
self.summons = summons_obj
self.description: str | None = None
self.event = event
self.execute_tick: int = execute_tick
self._has_executed: bool = False
self._change_state: bool = False # 状态锁定标志
@property
def has_executed(self):
"""是否被处理过"""
return self._has_executed
@has_executed.setter
def has_executed(self, value: bool):
if self._change_state:
raise RuntimeError("执行状态不允许被反复修改!")
self._has_executed = value
self._change_state = True
def execute_myself(self):
"""业务逻辑接口"""
self._execute_myself()
self._post_execute_check()
def _post_execute_check(self):
"""后置检查"""
if not self.has_executed:
sim_instance: "Simulator" = self.summons.sim_instance
sim_instance.schedule_data.change_process_state()
print("【SummonsEvent警告】:在运行业务逻辑后,自身执行状态未被更改,请检查代码!")
@abstractmethod
def _execute_myself(self):
"""实际业务逻辑"""
pass
================================================
FILE: zsim/sim_progress/summons/__init__.py
================================================
from .summons_class import Summons
__all__ = ["Summons"]
================================================
FILE: zsim/sim_progress/summons/summons_class.py
================================================
from abc import ABC, abstractmethod
from zsim.sim_progress.Character.character import Character
class Summons(ABC):
@abstractmethod
def __init__(self, ownner_obj: Character):
"""召唤物基类"""
self.owner: Character = ownner_obj
self.sim_instance = None
@abstractmethod
def check_myself(self):
pass
@abstractmethod
def active(self):
pass
================================================
FILE: zsim/simulator/__init__.py
================================================
from .simulator_class import Simulator
__all__ = ["Simulator"]
================================================
FILE: zsim/simulator/config_classes.py
================================================
from zsim.models.session.session_run import (
ExecAttrCurveCfg,
ExecWeaponCfg,
SimulationConfig,
)
__all__ = ["SimulationConfig", "ExecAttrCurveCfg", "ExecWeaponCfg"]
================================================
FILE: zsim/simulator/dataclasses.py
================================================
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any, Literal
from zsim.define import saved_char_config
from zsim.models.session.session_run import CharConfig, CommonCfg
from zsim.sim_progress.Buff import Buff
from zsim.sim_progress.Buff.Buff0Manager import Buff0ManagerClass, change_name_box
from zsim.sim_progress.Character import Character, character_factory
from zsim.sim_progress.data_struct import ActionStack
from zsim.sim_progress.Enemy import Enemy
from .config_classes import SimulationConfig as SimCfg
if TYPE_CHECKING:
from .simulator_class import Simulator
@dataclass
class InitData:
def __init__(
self,
*,
common_cfg: CommonCfg | None = None,
sim_cfg: SimCfg | None = None,
sim_instance: "Simulator | None" = None,
):
"""
初始化角色配置数据。
- CLI或WebUI模式下从配置文件中加载角色配置信息
- API模式下,通过传入的配置信息初始化角色配置
并初始化相关数据结构。
如果配置文件/配置信息不存在或配置信息不完整,将抛出异常。
"""
self.sim_cfg = sim_cfg
if common_cfg is None:
self.__direct_read_init()
elif isinstance(common_cfg, CommonCfg):
self.__api_init(common_cfg)
else:
raise ValueError("Invalid configuration type.")
self.sim_instance = sim_instance
def __direct_read_init(self):
"""CLI/WebUI方法不传入常规配置,直接读取文件"""
config: dict = saved_char_config
if not config:
raise AssertionError("No character init configuration found.")
try:
self.name_box: list[str] = config["name_box"]
self.Judge_list_set: list[
list[str | None]
] = [] # [[角色名, 武器名, 四件套, 二件套], ...]
self.char_0 = config[self.name_box[0]]
self.char_1 = config[self.name_box[1]]
self.char_2 = config[self.name_box[2]]
except KeyError as e:
raise AssertionError(f"Missing key in character init configuration: {e}") from e
self.__adjust_weapon_with_sim_cfg()
for name in self.name_box:
char = getattr(self, f"char_{self.name_box.index(name)}")
self.Judge_list_set.append(
[
name,
char["weapon"],
char.get("equip_set4", ""),
char.get("equip_set2_a", ""),
]
)
self.weapon_dict: dict[str, list[str | Literal[1, 2, 3, 4, 5] | None]] = {
name: [
getattr(self, f"char_{self.name_box.index(name)}")["weapon"],
getattr(self, f"char_{self.name_box.index(name)}")["weapon_level"],
]
for name in self.name_box
} # {角色名: [武器名, 武器精炼等级], ...}
self.cinema_dict = {
name: getattr(self, f"char_{self.name_box.index(name)}")["cinema"]
for name in self.name_box
} # {角色名: 影画等级, ...}
def __api_init(self, common_cfg: CommonCfg):
"""API方法传入常规配置"""
self.name_box: list[str] = [char.name for char in common_cfg.char_config]
assert len(self.name_box) == 3, "Character configuration must be 3."
# 创建 char_0, char_1, char_2 属性,将 CharConfig 对象转换为字典
for i, char_config in enumerate(common_cfg.char_config):
char_dict = char_config.model_dump()
setattr(self, f"char_{i}", char_dict)
self.Judge_list_set: list[list[str | None]] = [
[char.name, char.weapon, char.equip_set4, char.equip_set2_a]
for char in common_cfg.char_config
]
self.weapon_dict: dict[str, list[str | Literal[1, 2, 3, 4, 5] | None]] = {
char.name: [char.weapon, char.weapon_level] for char in common_cfg.char_config
}
self.cinema_dict: dict[str, Literal[0, 1, 2, 3, 4, 5, 6]] = {
char.name: char.cinema for char in common_cfg.char_config
}
self.__adjust_weapon_with_sim_cfg()
def __adjust_weapon_with_sim_cfg(self):
# 根据sim_cfg调整武器配置
from zsim.models.session.session_run import ExecWeaponCfg
if self.sim_cfg is not None and isinstance(self.sim_cfg, ExecWeaponCfg):
if self.sim_cfg.adjust_char is None:
return
adjust_char_index = (
self.sim_cfg.adjust_char - 1
) # UI从1开始计数,这里需要转换为0开始的索引
if 0 <= adjust_char_index < len(self.name_box):
char_dict_to_adjust = getattr(self, f"char_{adjust_char_index}")
# 更新武器名称和精炼等级
char_dict_to_adjust["weapon"] = self.sim_cfg.weapon_name
char_dict_to_adjust["weapon_level"] = self.sim_cfg.weapon_level
@dataclass
class CharacterData:
char_obj_list: list[Character] = field(init=False)
init_data: InitData
sim_cfg: SimCfg | None
sim_instance: "Simulator"
def __post_init__(self):
self.char_obj_list = []
if self.init_data.name_box:
i = 0
for _ in self.init_data.name_box:
char_dict = getattr(self.init_data, f"char_{i}")
# 提取sim_cfg参数
sim_cfg = None
if self.sim_cfg is not None and self.sim_cfg.adjust_char == i + 1:
# UI那边不是从0开始数数的
from zsim.models.session.session_run import ExecAttrCurveCfg, ExecWeaponCfg
if isinstance(self.sim_cfg, (ExecAttrCurveCfg, ExecWeaponCfg)):
sim_cfg = self.sim_cfg
else:
# 如果类型不匹配,使用None
sim_cfg = None
# 创建CharConfig对象
char_config = CharConfig(**char_dict)
char_obj: Character = character_factory(char_config, sim_cfg=sim_cfg)
if char_obj.sim_instance is None:
char_obj.sim_instance = self.sim_instance
self.char_obj_list.append(char_obj)
i += 1
self.char_obj_dict = {char_obj.NAME: char_obj for char_obj in self.char_obj_list}
def find_next_char_obj(self, char_now: int, direction: int = 1) -> Character:
"""输入查找起点(CID),以及查找方向,返回下一位角色"""
__index = 0
for char_obj in self.char_obj_list:
__index += 1
if char_now != char_obj.CID:
continue
if direction == 1:
"""顺向查找"""
if __index + 1 == len(self.char_obj_list):
return self.char_obj_list[0]
else:
return self.char_obj_list[__index]
elif direction == -1:
"""逆向查找"""
if __index == 0:
return self.char_obj_list[-1]
else:
return self.char_obj_list[__index - 1]
else:
raise ValueError("direction参数错误!")
else:
raise ValueError(f"未找到CID为{char_now}的角色!")
def reset_myself(self):
for obj in self.char_obj_list:
obj.reset_myself()
def find_char_obj(
self, CID: int | None = None, char_name: str | None = None
) -> Character | None:
if not CID and not char_name:
raise ValueError("查找角色时,必须提供CID或是char_name中的一个!")
for char_obj in self.char_obj_list:
if CID == char_obj.CID or char_name == char_obj.NAME:
return char_obj
else:
if CID:
raise ValueError(f"未找到CID为{CID}的角色!")
elif char_name:
raise ValueError(f"未找到名称为{char_name}的角色!")
@dataclass
class LoadData:
name_box: list
Judge_list_set: list
weapon_dict: dict
action_stack: ActionStack
cinema_dict: dict
exist_buff_dict: dict = field(init=False)
load_mission_dict: dict = field(default_factory=dict)
LOADING_BUFF_DICT: dict = field(default_factory=dict)
name_dict: dict = field(default_factory=dict)
all_name_order_box: dict = field(default_factory=dict)
preload_tick_stamp: dict = field(default_factory=dict)
char_obj_dict: dict | None = None
sim_instance: "Simulator | None" = None
def __post_init__(self):
self.buff_0_manager = Buff0ManagerClass.Buff0Manager(
self.name_box,
self.Judge_list_set,
self.weapon_dict,
self.cinema_dict,
self.char_obj_dict,
sim_instance=self.sim_instance,
)
self.exist_buff_dict = self.buff_0_manager.exist_buff_dict
self.all_name_order_box = change_name_box(self.name_box)
# self.all_name_order_box = Buff.Buff0Manager.change_name_box()
def reset_exist_buff_dict(self):
"""重置buff_exist_dict"""
for _char_name, sub_exist_buff_dict in self.exist_buff_dict.items():
for _buff_name, buff in sub_exist_buff_dict.items():
buff.reset_myself()
def reset_myself(self, name_box, Judge_list_set, weapon_dict, cinema_dict):
self.name_box = name_box
self.Judge_list_set = Judge_list_set
self.weapon_dict = weapon_dict
self.cinema_dict = cinema_dict
self.action_stack.reset_myself()
self.reset_exist_buff_dict()
self.load_mission_dict = {}
self.LOADING_BUFF_DICT = {}
self.name_dict = {}
self.all_name_order_box = change_name_box(self.name_box)
self.preload_tick_stamp = {}
@dataclass
class ScheduleData:
enemy: Enemy
char_obj_list: list[Character]
event_list: list[Any] = field(default_factory=list)
# judge_required_info_dict = {"skill_node": None}
loading_buff: dict[str, list[Buff]] = field(default_factory=dict)
dynamic_buff: dict[str, list[Buff]] = field(default_factory=dict)
sim_instance: "Simulator | None" = None
processed_event: bool = False
# 记录已处理的事件次数, 给外部判断是否有事件发生, 便于前端跳过没有 event 的帧的 log
# 实际执行时, 当 event 是 Preload.SkillNode | LoadingMission 时, 大多数情况是没有 log 输出的, 所以仍然会输出大量空帧.
# 10800 帧的情况目前可以只打印 1500 条左右的 log. 但是打印的帧数字不规律, 可能看起来有点怪.
processed_times: int = field(default=0)
processe_state_update_tick: int = field(default=0) # process_state的更新时间
def reset_myself(self):
"""重置ScheduleData的动态数据!"""
self.enemy.reset_myself()
self.event_list = []
# self.judge_required_info_dict = {"skill_node": None}
for char_name in self.loading_buff:
self.loading_buff[char_name] = []
self.dynamic_buff[char_name] = []
self.processed_times = 0
@property
def processed_state_this_tick(self):
"""当前tick是否有新事件发生"""
return self.processed_event
def change_process_state(self):
"""有新事件发生时调用,保证终端print"""
self.processed_event = True
def reset_processed_event(self):
"""重置processed_event"""
self.processed_event = False
@dataclass
class GlobalStats:
name_box: list[str]
DYNAMIC_BUFF_DICT: dict[str, list[Buff]] = field(default_factory=dict)
sim_instance: "Simulator | None" = None
def __post_init__(self):
for name in self.name_box + ["enemy"]:
self.DYNAMIC_BUFF_DICT[name] = []
def reset_myself(self, name_box):
for name in self.name_box + ["enemy"]:
self.DYNAMIC_BUFF_DICT[name] = []
================================================
FILE: zsim/simulator/simulator_class.py
================================================
import gc
import time
from typing import TYPE_CHECKING, Any
from pydantic import BaseModel
from zsim.define import config
from zsim.sim_progress.Buff import (
BuffLoadLoop,
buff_add,
)
from zsim.sim_progress.Character.skill_class import Skill
from zsim.sim_progress.data_struct import ActionStack, Decibelmanager, ListenerManger
from zsim.sim_progress.Enemy import Enemy
from zsim.sim_progress.Load import DamageEventJudge, SkillEventSplit
from zsim.sim_progress.Preload import PreloadClass
from zsim.sim_progress.RandomNumberGenerator import RNG
from zsim.sim_progress.Report import start_report_threads, stop_report_threads
from zsim.sim_progress.ScheduledEvent import ScheduledEvent as ScE
from zsim.sim_progress.Update.Update_Buff import update_time_related_effect
from zsim.simulator.dataclasses import (
CharacterData,
GlobalStats,
InitData,
LoadData,
ScheduleData,
SimCfg,
)
if TYPE_CHECKING:
from zsim.models.session.session_run import CommonCfg
class Confirmation(BaseModel):
session_id: str
status: str
timestamp: int
sim_cfg: SimCfg | None = None
class Simulator:
"""模拟器类。
## 模拟器的初始状态,包括但不限于:
### 常规变量
- 模拟器时间刻度(tick)每秒为60ticks
- 暴击种子(crit_seed)为RNG模块使用,未来接入随机功能时用于复现测试
- 初始化数据(init_data)包含数据库读到的大部分数据
- 角色数据(char_data)包含角色的实例
### 参与tick逻辑的内部对象
- 加载数据(load_data)
- 调度数据(schedule_data)
- 全局统计数据(global_stats)
- 技能列表(skills)
- 预加载类(preload)
- 游戏状态(game_state)包含前面的大多数数据
- 喧响管理器(decibel_manager)
- 监听器管理器(listener_manager)
### 其他实例
- 随机数生成器实例(rng_instance)
- 并行模式标志(in_parallel_mode)
- 模拟配置,用于控制并行模式下,模拟器作为子进程的参数(sim_cfg)
"""
tick: int
crit_seed: int
init_data: InitData
enemy: Enemy
char_data: CharacterData
load_data: LoadData
schedule_data: ScheduleData
global_stats: GlobalStats
skills: list[Skill]
preload: PreloadClass
game_state: dict[str, Any]
decibel_manager: Decibelmanager
listener_manager: ListenerManger
rng_instance: RNG
in_parallel_mode: bool
sim_cfg: SimCfg | None
def cli_init_simulator(self, sim_cfg: SimCfg | None):
"""CLI和WebUI的旧方法,重置模拟器实例为初始状态。"""
self.__detect_parallel_mode(sim_cfg)
self.init_data = InitData(common_cfg=None, sim_cfg=sim_cfg)
self.enemy = Enemy(
index_id=config.enemy.index_id,
adjustment_id=config.enemy.adjust_id,
difficulty=config.enemy.difficulty,
sim_instance=self,
)
self.__init_data_struct(sim_cfg)
start_report_threads(sim_cfg) # 启动线程以处理日志和结果写入
def api_init_simulator(self, common_cfg: "CommonCfg", sim_cfg: SimCfg | None):
"""api初始化模拟器实例的接口。"""
self.__detect_parallel_mode(sim_cfg)
self.init_data = InitData(common_cfg=common_cfg, sim_cfg=sim_cfg)
self.enemy = Enemy(
index_id=common_cfg.enemy_config.index_id,
adjustment_id=int(common_cfg.enemy_config.adjustment_id),
difficulty=common_cfg.enemy_config.difficulty,
sim_instance=self,
)
self.__init_data_struct(sim_cfg, api_apl_path=common_cfg.apl_path)
start_report_threads(
sim_cfg, session_id=common_cfg.session_id
) # 启动线程以处理日志和结果写入
def api_run_simulator(
self, common_cfg: "CommonCfg", sim_cfg: SimCfg | None, stop_tick: int | None = None
) -> Confirmation:
"""api运行模拟器实例的接口。
Args:
common_cfg: 通用配置对象,包含角色和敌人配置
sim_cfg: 模拟配置对象,包含模拟的详细参数
stop_tick: 停止模拟的帧数,默认为10800帧(3分钟)
Returns:
包含运行确认信息的字典
"""
if stop_tick is None:
stop_tick = 10800
self.api_init_simulator(common_cfg, sim_cfg)
self.main_loop(stop_tick=stop_tick, sim_cfg=sim_cfg, use_api=True)
# 返回确认信息
confirmation = Confirmation(
session_id=common_cfg.session_id,
status="completed",
timestamp=int(time.time()),
sim_cfg=sim_cfg,
)
return confirmation
def __detect_parallel_mode(self, sim_cfg):
if sim_cfg is not None:
self.in_parallel_mode = True
self.sim_cfg = sim_cfg
else:
self.in_parallel_mode = False
self.sim_cfg = None
def __init_data_struct(self, sim_cfg, *, api_apl_path: str | None = None):
self.tick = 0
self.crit_seed = 0
self.char_data = CharacterData(self.init_data, sim_cfg, sim_instance=self)
self.load_data = LoadData(
name_box=self.init_data.name_box,
Judge_list_set=self.init_data.Judge_list_set,
weapon_dict=self.init_data.weapon_dict,
cinema_dict=self.init_data.cinema_dict,
action_stack=ActionStack(),
char_obj_dict=self.char_data.char_obj_dict,
sim_instance=self,
)
self.schedule_data = ScheduleData(
enemy=self.enemy,
char_obj_list=self.char_data.char_obj_list,
sim_instance=self,
)
if self.schedule_data.enemy.sim_instance is None:
self.schedule_data.enemy.sim_instance = self
self.global_stats = GlobalStats(name_box=self.init_data.name_box, sim_instance=self)
skills = [char.skill_object for char in self.char_data.char_obj_list]
self.preload = PreloadClass(
skills,
load_data=self.load_data,
apl_path=config.database.apl_file_path if api_apl_path is None else api_apl_path,
sim_instance=self,
)
self.game_state: dict[str, Any] = {
"tick": self.tick,
"init_data": self.init_data,
"char_data": self.char_data,
"load_data": self.load_data,
"schedule_data": self.schedule_data,
"global_stats": self.global_stats,
"preload": self.preload,
}
self.decibel_manager = Decibelmanager(self)
self.listener_manager = ListenerManger(self)
self.rng_instance = RNG(sim_instance=self)
# 监听器的初始化需要整个Simulator实例,因此在这里进行初始化
self.load_data.buff_0_manager.initialize_buff_listener()
def main_loop(
self, stop_tick: int = 10800, *, sim_cfg: SimCfg | None = None, use_api: bool = False
):
"""
CLI和WebUI使用此方法直接从文件读取数据,运行模拟器。
传入的值仅为stop_tick和并行模拟配置。
"""
if not use_api:
self.cli_init_simulator(sim_cfg)
while True:
# Tick Update
# report_to_log(f"[Update] Tick step to {tick}")
update_time_related_effect(
self.global_stats.DYNAMIC_BUFF_DICT,
self.tick,
self.load_data.exist_buff_dict,
self.schedule_data.enemy,
)
# Preload
self.preload.do_preload(
self.tick,
self.schedule_data.enemy,
self.init_data.name_box,
self.char_data,
)
preload_list = self.preload.preload_data.preload_action
if stop_tick is None:
if (
not config.apl_mode.enabled
and self.preload.preload_data.skills_queue.head is None
):
# Old Sequence mode left, not compatible with APL mode now
stop_tick = self.tick + 120
elif self.tick >= stop_tick:
break
# Load
if preload_list:
SkillEventSplit(
preload_list,
self.load_data.load_mission_dict,
self.load_data.name_dict,
self.tick,
self.load_data.action_stack,
)
DamageEventJudge(
self.tick,
self.load_data.load_mission_dict,
self.schedule_data.enemy,
self.schedule_data.event_list,
self.char_data.char_obj_list,
)
BuffLoadLoop(
self.tick,
self.load_data.load_mission_dict,
self.load_data.exist_buff_dict,
self.init_data.name_box,
self.load_data.LOADING_BUFF_DICT,
self.load_data.all_name_order_box,
sim_instance=self,
)
buff_add(
self.tick,
self.load_data.LOADING_BUFF_DICT,
self.global_stats.DYNAMIC_BUFF_DICT,
self.schedule_data.enemy,
)
# Load.DamageEventJudge(tick, load_data.load_mission_dict, schedule_data.enemy, schedule_data.event_list, char_data.char_obj_list)
# ScheduledEvent
sce = ScE(
self.global_stats.DYNAMIC_BUFF_DICT,
self.schedule_data,
self.tick,
self.load_data.exist_buff_dict,
self.load_data.action_stack,
sim_instance=self,
)
sce.event_start()
# self.tick += 1
# if sce.data.processed_times > 0:
# print(f"\r{self.tick}", end="")
if self.schedule_data.processed_state_this_tick and self.tick != 0:
minutes = self.tick // 3600
rest_seconds = self.tick % 3600 / 60
if rest_seconds == 60:
rest_seconds = 0
minutes += 1
print()
print(
f"▲ ▲ ▲第{self.tick}帧({minutes:.0f}分 {rest_seconds:02.0f}秒)发生的事件如上▲ ▲ ▲\n ",
end="",
)
print("---------------------------------------------")
self.tick += 1
self.schedule_data.reset_processed_event()
if self.tick % 500 == 0 and self.tick != 0:
gc.collect()
stop_report_threads()
def __deepcopy__(self, memo):
return self
================================================
FILE: zsim/utils/constants.py
================================================
import polars as pl
from zsim.define import ElementType
def _init_buff_effect_mapping() -> dict[str, str]:
"""初始化BUFF效果映射关系"""
try:
df = pl.scan_csv("./zsim/data/buff_effect.csv")
mapping = df.collect().to_dict(as_series=False)
buff_effect_map: dict[str, str] = {}
for i in range(len(mapping["名称"])):
name = mapping["名称"][i]
effect_str = ""
# 动态的找键值对数量
max_key_index = 0
for col_name in mapping.keys():
if col_name.startswith("key"):
try:
index = int(col_name[3:])
if index > max_key_index:
max_key_index = index
except ValueError:
# 忽略不符合 keyN 格式的列名
pass
for j in range(1, max_key_index + 1):
key_col = f"key{j}"
value_col = f"value{j}"
if key_col in mapping and value_col in mapping:
key = mapping[key_col][i]
value = mapping[value_col][i]
if key and value is not None:
try:
effect_str += f"{key}: {float(value)}; "
except ValueError:
# Handle cases where value is not a valid float
print(
f"Warning: Could not convert value '{value}' to float for buff '{name}', key '{key}'. Skipping this effect."
)
continue
if effect_str:
# Remove trailing semicolon and space if present
buff_effect_map[name] = effect_str.rstrip("; ")
return buff_effect_map
except Exception as e:
print(f"Warning: Failed to load buff effect mapping: {e}")
return {}
BUFF_EFFECT_MAPPING: dict[str, str] = _init_buff_effect_mapping()
def _init_skill_tag_mapping() -> dict[str, str]:
"""初始化技能标签映射关系"""
try:
df = pl.scan_csv(
"./zsim/data/skill.csv",
schema_overrides={
"skill_tag": pl.String,
"skill_text": pl.String,
"INSTRUCTION": pl.String,
},
)
mapping = (
df.select("skill_tag", "skill_text", "INSTRUCTION").collect().to_dict(as_series=False)
)
return {
_tag: f"{_text if _text else ''}{f' - {_instruction}' if _instruction else ''}"
for _tag, _text, _instruction in zip(
mapping["skill_tag"], mapping["skill_text"], mapping["INSTRUCTION"], strict=False
)
}
except Exception as e:
print(f"Warning: Failed to load skill mapping: {e}")
return {}
SKILL_TAG_MAPPING: dict[str, str] = _init_skill_tag_mapping()
def _init_char_mapping() -> dict[str, str]:
"""初始化角色CID和名称的映射关系"""
try:
df = pl.scan_csv("./zsim/data/character.csv")
mapping = df.select(["name", "CID"]).collect().to_dict(as_series=False)
return {name: str(cid) for name, cid in zip(mapping["name"], mapping["CID"], strict=False)}
except Exception as e:
print(f"Warning: Failed to load character mapping: {e}")
return {}
# 角色CID和名称的映射关系
CHAR_CID_MAPPING: dict[str, str] = _init_char_mapping()
# 角色配置常量
default_chars = [
"扳机",
"丽娜",
"零号·安比",
] # 这个值其实没啥意义,但是必须是三个角色,否则可能会报错
__lf = pl.scan_csv("./zsim/data/character.csv")
char_options = __lf.select("name").unique().collect().to_series().to_list()
# 角色名称->职业特性
char_profession_map = {row["name"]: row["角色特性"] for row in __lf.collect().iter_rows(named=True)}
# 武器选项
__lf = pl.scan_csv("./zsim/data/weapon.csv")
weapon_options = __lf.select("名称").unique().collect().to_series().to_list()
# 音擎名称->职业
weapon_profession_map = {row["名称"]: row["职业"] for row in __lf.collect().iter_rows(named=True)}
# 驱动盘套装选项
__lf = pl.scan_csv("./zsim/data/equip_set_2pc.csv")
equip_set_ids = (
__lf.select("set_ID")
.filter(pl.col("set_ID").is_not_null())
.unique()
.collect()
.to_series()
.to_list()
)
equip_set4_options = equip_set2_options = equip_set_ids
# 主词条选项
main_stat4_options = [
"攻击力%",
"生命值%",
"防御力%",
"暴击率%",
"暴击伤害%",
"异常精通",
"-",
]
main_stat5_options = [
"攻击力%",
"生命值%",
"防御力%",
"穿透率",
"物理属性伤害%",
"火属性伤害%",
"冰属性伤害%",
"电属性伤害%",
"以太属性伤害%",
"-",
]
main_stat6_options = [
"攻击力%",
"生命值%",
"防御力%",
"异常掌控",
"冲击力%",
"能量自动回复%",
"-",
]
stats_trans_mapping = {
"攻击力%": "scATK_percent",
"攻击力": "scATK",
"生命值%": "scHP_percent",
"生命值": "scHP",
"防御力%": "scDEF_percent",
"防御力": "scDEF",
"异常精通": "scAnomalyProficiency",
"穿透值": "scPEN",
"暴击率": "scCRIT",
"暴击伤害": "scCRIT_DMG",
"属性伤害加成": "DMG_BONUS",
"穿透率": "PEN_RATIO",
"异常掌控": "ANOMALY_MASTERY",
"能量自动回复": "SP_REGEN",
}
SC_DATA_DISCRIPTION_MAPPING = {
"scATK_percent": "3%/词条",
"scATK": "19点/词条",
"scHP_percent": "3%/词条",
"scHP": "112/词条",
"scDEF_percent": "4.8%/词条",
"scDEF": "15点/词条",
"scAnomalyProficiency": "9点/词条",
"scPEN": "9点/词条",
"scCRIT": "2.4%暴击率或4.8%暴击伤害/词条",
"scCRIT_DMG": "2.4%暴击率或4.8%暴击伤害/词条",
"DMG_BONUS": "3%/词条",
"PEN_RATIO": "2.4%/词条",
"ANOMALY_MASTERY": "3%/词条",
"SP_REGEN": "6%/词条",
}
# 副词条最大值
sc_max_value = 40
# 计算结果缓存文件路径
ID_CACHE_JSON = "./results/id_cache.json"
results_dir = "./results"
# 六元素翻译对应表
element_mapping: dict[ElementType, str] = {
0: "物理",
1: "火",
2: "冰",
3: "电",
4: "以太",
5: "烈霜",
6: "玄墨",
}
# ID重复时抛出的自定义异常类
class IDDuplicateError(Exception):
"""当检测到重复ID时抛出此异常"""
pass
del __lf # 确保在文件末尾删除临时变量
================================================
FILE: zsim/utils/process_buff_result.py
================================================
import asyncio
import json
import os
from typing import Any
import aiofiles
import aiofiles.os
import polars as pl
from zsim.define import results_dir
def _prepare_buff_timeline_data(df: pl.DataFrame) -> list[dict[str, Any]]:
"""将包含时间序列BUFF数据的Polars DataFrame转换为适用于Plotly时间线的格式。
Args:
df (pl.DataFrame): 输入的Polars DataFrame,应包含 'time_tick' 列,
其余列为各个BUFF的状态,列名为BUFF名称。
单元格中的值代表该BUFF在对应time_tick的状态值,
null 值表示BUFF在该tick不生效。
Returns:
list[dict[str, Any]]: 转换后的数据列表,每个字典代表一个BUFF生效的时间段,
包含 'Task' (BUFF名称), 'Start' (开始tick),
'Finish' (结束tick), 'Value' (BUFF值)。
"""
timeline_data: list[dict[str, Any]] = []
buff_columns = [col for col in df.columns if col != "time_tick"]
for buff_name in buff_columns:
# 将空值填充为0.0并筛选出来
buff_df = df.select(["time_tick", buff_name]).with_columns(pl.col(buff_name).fill_null(0.0))
if buff_df.height == 0:
continue
# 尝试将 BUFF 值列转换为数值类型,无法转换的设为 null
buff_df = buff_df.with_columns(pl.col(buff_name).cast(pl.Float32, strict=False))
# 计算值变化的点
buff_df = buff_df.with_columns(pl.col(buff_name).diff().alias("value_diff"))
# 标记每个连续段的开始
# 条件:第一行,或者值发生变化
buff_df = buff_df.with_columns(
((pl.arange(0, pl.count()) == 0) | (pl.col("value_diff") != 0)).alias("is_start")
)
# 为每个连续段分配一个ID
# 将布尔值转换为整数,以便进行累加
buff_df = buff_df.with_columns(
pl.col("is_start").cast(pl.Int32).cum_sum().alias("group_id")
)
# 按段聚合,找到起始tick、结束tick和对应的值
grouped = buff_df.group_by("group_id").agg(
pl.first("time_tick").alias("Start"),
pl.last("time_tick").alias("last_valid_tick"),
pl.first(buff_name).alias("Value"),
)
# 计算结束 tick (Finish)
# 使用当前段的最后一个有效tick作为结束点
grouped = grouped.with_columns(pl.col("last_valid_tick").alias("Finish"))
# 转换结果为字典列表
for row in grouped.select(["Start", "Finish", "Value"]).iter_rows(named=True):
# 过滤掉 Value 为 null 的行
if row["Value"]:
timeline_data.append(
{
"Task": buff_name,
"Start": int(row["Start"]),
"Finish": int(row["Finish"]),
"Value": row["Value"],
}
)
return timeline_data
def _load_cached_buff_data(rid: int | str) -> dict[str, list[dict[str, Any]]] | None:
"""尝试从JSON缓存文件加载BUFF时间线数据。"""
buff_log_path = os.path.join(results_dir, str(rid), "buff_log")
json_file_path = os.path.join(buff_log_path, "buff_timeline_data.json")
if os.path.exists(json_file_path):
try:
with open(json_file_path, "r", encoding="utf-8") as f:
return json.load(f)
except Exception:
# 加载失败,将视为缓存不存在
return None
return None
async def prepare_buff_data_and_cache(
rid: int | str,
) -> dict[str, list[dict[str, Any]]] | None:
"""异步处理BUFF日志CSV文件,生成时间线数据,并缓存到JSON文件。
此函数不处理UI反馈,仅负责数据处理和文件操作。
Args:
rid (int | str): 运行ID。
Returns:
dict[str, list[dict[str, Any]]] | None: 处理后的BUFF时间线数据字典,
如果处理失败或无CSV文件则返回None。
如果找到CSV但处理后无数据,返回空字典 {}。
"""
buff_log_path = os.path.join(results_dir, str(rid), "buff_log")
json_file_path = os.path.join(buff_log_path, "buff_timeline_data.json")
if not await aiofiles.os.path.exists(buff_log_path):
# 日志目录不存在,无法处理
return None
try:
all_files = await aiofiles.os.listdir(buff_log_path)
csv_files = [f for f in all_files if f.endswith(".csv")]
except FileNotFoundError:
# listdir 可能在目录刚创建时失败,或者权限问题
return None
except Exception as e:
print(f"列出目录 {buff_log_path} 时发生错误: {e}")
return None
if not csv_files:
# 没有CSV文件,无需处理,但也无需创建JSON。返回空字典表示成功但无数据。
return {}
all_buff_data: dict[str, list[dict[str, Any]]] = {}
processed_csv_files: list[str] = []
tasks = []
async def process_csv(filename: str):
nonlocal all_buff_data, processed_csv_files
csv_file_path = os.path.join(buff_log_path, filename)
try:
# 使用 asyncio.to_thread 在单独的线程中运行同步的 polars 操作
# 注意:Polars 的 read_csv 默认是多线程的,但为了与 aiofiles 配合,仍使用 to_thread
df = await asyncio.to_thread(pl.read_csv, csv_file_path)
file_key = filename.replace(".csv", "")
# _prepare_buff_timeline_data 本身是同步的,可以在这里直接调用
buff_data = _prepare_buff_timeline_data(df)
all_buff_data[file_key] = buff_data
processed_csv_files.append(csv_file_path)
except Exception as e:
print(f"处理文件 {csv_file_path} 时发生错误: {e}")
# 可以选择在这里标记错误,或者让 gather 捕获
raise # 重新抛出异常,让 gather 知道有错误
# 为每个CSV文件创建一个处理任务
for filename in csv_files:
tasks.append(process_csv(filename))
# 并发执行所有CSV处理任务
results = await asyncio.gather(*tasks, return_exceptions=True)
# 检查是否有处理错误
has_processing_error = any(isinstance(res, Exception) for res in results)
if has_processing_error:
print("处理CSV文件时至少发生一个错误。")
return None
# 如果没有处理错误或者决定即使有错误也要继续
if all_buff_data: # 确保有数据才写入
try:
# 异步写入JSON缓存文件
async with aiofiles.open(json_file_path, "w", encoding="utf-8") as f:
await f.write(json.dumps(all_buff_data, indent=4, ensure_ascii=False))
except Exception as e:
print(f"写入JSON文件 {json_file_path} 时发生错误: {e}")
has_processing_error = True # 标记写入错误
# 异步删除原始CSV文件
if processed_csv_files:
delete_tasks = [aiofiles.os.remove(csv_path) for csv_path in processed_csv_files]
delete_results = await asyncio.gather(*delete_tasks, return_exceptions=True)
for i, res in enumerate(delete_results):
if isinstance(res, Exception):
print(f"删除文件 {processed_csv_files[i]} 时发生错误: {res}")
# 删除失败通常不认为是关键错误,只打印日志
# 如果在处理或写入JSON时发生错误,返回None
if has_processing_error:
return None
return all_buff_data
================================================
FILE: zsim/utils/process_dmg_result.py
================================================
import json
import os
import polars as pl
from zsim.define import ANOMALY_MAPPING
from zsim.sim_progress.Character.skill_class import lookup_name_or_cid
from .constants import SKILL_TAG_MAPPING, results_dir
def _load_dmg_data(rid: int | str) -> pl.DataFrame | None:
"""加载指定运行ID的伤害数据CSV文件。
Args:
rid (int): 运行ID。
Returns:
Optional[pd.DataFrame]: 加载的伤害数据DataFrame,如果文件未找到则返回None。
"""
csv_file_path = os.path.join(results_dir, str(rid), "damage.csv")
try:
lf = pl.scan_csv(csv_file_path)
# 去除列名中的特殊字符
schema_names = lf.collect_schema().names()
lf = lf.rename(
{col: col.replace("\r", "").replace("\n", "").strip() for col in schema_names}
)
return lf.collect()
except FileNotFoundError:
print(f"未找到文件:{csv_file_path}")
return None
def prepare_line_chart_data(dmg_result_df: pl.DataFrame) -> dict[str, pl.DataFrame]:
"""准备用于绘制伤害与失衡曲线图的数据。
Args:
dmg_result_df (pl.DataFrame): 原始伤害数据。
Returns:
dict[str, Any]: 包含处理后数据的字典,用于绘制折线图。
- 'line_chart_df': 包含时间、伤害、DPS、失衡值、失衡效率的DataFrame。
"""
processed_df = dmg_result_df.clone()
# 计算DPS
processed_df = processed_df.with_columns(
(pl.col("dmg_expect").cum_sum() / pl.col("tick") * 60).alias("dps")
)
# 处理失衡值
if "失衡状态" in processed_df.columns:
processed_df = processed_df.with_columns(
pl.when(pl.col("失衡状态")).then(0).otherwise(pl.col("stun")).alias("stun")
)
# 计算失衡效率
first_stun_row = processed_df.filter(pl.col("失衡状态") == True).head(1) # noqa: E712
if len(first_stun_row) > 0:
first_stun_tick = first_stun_row["tick"][0]
before_stun = processed_df.filter(pl.col("tick") <= first_stun_tick)
after_stun = processed_df.filter(pl.col("tick") > first_stun_tick)
before_stun = before_stun.with_columns(
(pl.col("stun").cum_sum() / pl.col("tick") * 60).alias("stun_efficiency")
)
after_stun = after_stun.with_columns(pl.lit(None).alias("stun_efficiency"))
processed_df = pl.concat([before_stun, after_stun])
else:
processed_df = processed_df.with_columns(
(pl.col("stun").cum_sum() / pl.col("tick") * 60).alias("stun_efficiency")
)
return {"line_chart_df": processed_df}
def _get_cn_skill_tag(skill_tag: str) -> str:
"""根据技能标签获取技能中文名。
Args:
skill_tag (str): 技能标签。
Returns:
str: 技能中文名。
"""
return SKILL_TAG_MAPPING.get(skill_tag, skill_tag)
def sort_df_by_UUID(dmg_result_df: pl.DataFrame) -> pl.DataFrame:
"""按UUID对伤害数据进行分组和聚合。
Args:
dmg_result_df (pl.DataFrame): 原始伤害数据。
Returns:
pl.DataFrame: 按UUID聚合后的数据,包含每个UUID的总伤害、总失衡、总积蓄等信息。
Raises:
ValueError: 如果DataFrame缺少必要的列。
"""
required_columns = [
"skill_tag",
"dmg_expect",
"stun",
"buildup",
"UUID",
"is_anomaly",
]
for col in required_columns:
if col not in dmg_result_df.columns or dmg_result_df[col].is_null().all():
raise ValueError(f"DataFrame 中缺少有效的列: {col}")
result_data = []
all_UUID = dmg_result_df["UUID"].unique().to_list()
for UUID in all_UUID:
same_UUID_rows = dmg_result_df.filter(pl.col("UUID") == UUID)
dmg_expect_sum = same_UUID_rows["dmg_expect"].fill_null(0).sum()
stun_sum = same_UUID_rows["stun"].fill_null(0).sum()
buildup_sum = same_UUID_rows["buildup"].fill_null(0).sum()
skill_tags = same_UUID_rows["skill_tag"].drop_nulls()
skill_tag = skill_tags[0] if len(skill_tags) > 0 else None
is_anomaly = same_UUID_rows["is_anomaly"][0]
element_types = same_UUID_rows["element_type"].drop_nulls()
element_type = element_types[0] if len(element_types) > 0 else None
cid: int | str | None = None
name: str | None = None
skill_cn_name: str | None = None
if skill_tag:
cid_str = skill_tag[0:4]
skill_cn_name = _get_cn_skill_tag(skill_tag) # 获取技能中文名
try:
name, cid_lookup = lookup_name_or_cid(cid=cid_str)
cid = cid_lookup
except ValueError:
name = skill_tag # 如果查找失败,使用skill_tag作为名字
cid = None
else:
skill_cn_name = "Unknown" # 如果没有skill_tag,则设为Unknown
result_data.append(
{
"UUID": UUID,
"name": name,
"element_type": element_type,
"is_anomaly": is_anomaly,
"cid": cid,
"skill_tag": skill_tag,
"skill_cn_name": skill_cn_name, # 添加技能中文名
"dmg_expect_sum": dmg_expect_sum,
"stun_sum": stun_sum,
"buildup_sum": buildup_sum,
}
)
return pl.DataFrame(result_data)
def prepare_char_chart_data(uuid_df: pl.DataFrame) -> dict[str, pl.DataFrame]:
"""准备用于绘制角色参与度分布图的数据。
Args:
uuid_df (pl.DataFrame): 按UUID聚合后的伤害数据。
Returns:
Dict[str, Any]: 包含绘制饼图所需数据的字典。
- 'char_dmg_df': 按角色分组的伤害总和。
- 'char_stun_df': 按角色分组的失衡总和。
- 'char_skill_dmg_df': 按角色和技能标签分组的伤害总和。
- 'char_element_df': 按角色和元素类型分组的积蓄总和。
"""
# 各伤害来源占比
char_dmg_df = (
uuid_df.filter(pl.col("dmg_expect_sum") > 0)
.group_by(["name", "is_anomaly"])
.agg(pl.col("dmg_expect_sum").sum())
)
# 角色失衡占比
char_stun_df = (
uuid_df.filter(pl.col("stun_sum") > 0).group_by("name").agg(pl.col("stun_sum").sum())
)
# 角色技能输出占比
filtered_skill_df = uuid_df.filter(pl.col("cid").is_not_null())
char_skill_dmg_df = filtered_skill_df.group_by(["name", "skill_cn_name"]).agg(
[
pl.col("dmg_expect_sum").sum(),
pl.col("buildup_sum").sum(),
pl.col("stun_sum").sum(),
]
)
# 角色属性积蓄占比
filtered_buildup_df = uuid_df.filter(pl.col("buildup_sum") > 0)
char_element_df = filtered_buildup_df.group_by(["name", "element_type"]).agg(
pl.col("buildup_sum").sum()
)
return {
"char_dmg_df": char_dmg_df,
"char_stun_df": char_stun_df,
"char_skill_dmg_df": char_skill_dmg_df,
"char_element_df": char_element_df,
}
def _find_consecutive_true_ranges(df: pl.DataFrame, column: str) -> list[tuple[int, int]]:
"""查找DataFrame列中连续为True的范围。
Args:
df (pl.DataFrame): 输入的DataFrame,需要包含 'tick' 列。
column (str): 要查找的布尔列名。
Returns:
list[tuple[int, int]]: 一个包含 (开始tick, 结束tick) 元组的列表。
"""
ranges = []
start = None
# 获取tick列和指定列的值
ticks = df["tick"].to_list()
values = df[column].to_list()
for i, (tick, value) in enumerate(zip(ticks, values, strict=False)):
if value:
if start is None:
start = tick
else:
if start is not None:
# 结束tick应该是上一个为True的tick
prev_tick = ticks[i - 1] if i > 0 else start
ranges.append((start, prev_tick))
start = None
# 处理最后一个区间(如果存在)
if start is not None:
ranges.append((start, ticks[-1]))
return ranges
def prepare_timeline_data(dmg_result_df: pl.DataFrame) -> pl.DataFrame | None:
"""准备用于绘制异常状态时间线的数据。
Args:
dmg_result_df (pl.DataFrame): 原始伤害数据。
Returns:
Optional[pl.DataFrame]: 用于绘制Gantt图的DataFrame,如果缺少列或无数据则返回None。
"""
required_columns = [
"冻结",
"霜寒",
"畏缩",
"感电",
"灼烧",
"侵蚀",
"烈霜霜寒",
"tick",
]
missing_cols = [col for col in required_columns if col not in dmg_result_df.columns]
if missing_cols:
print(f"输入数据缺少必要的列: {missing_cols}")
return None
columns_to_check = ["冻结", "霜寒", "畏缩", "感电", "灼烧", "侵蚀", "烈霜霜寒"]
gantt_data = []
for col in columns_to_check:
if col in dmg_result_df.columns:
ranges = _find_consecutive_true_ranges(dmg_result_df, col)
for start, end in ranges:
gantt_data.append({"Task": col, "Start": start, "Finish": end})
if not gantt_data:
return None
gantt_df = pl.DataFrame(gantt_data)
gantt_df = gantt_df.with_columns(
(pl.col("Finish") - pl.col("Start") + 1).alias("Duration")
) # 持续时间包含首尾
return gantt_df
def calculate_and_save_anomaly_attribution(
rid: int, char_dmg_df: pl.DataFrame, char_element_df: pl.DataFrame
) -> None:
"""计算并保存异常伤害归因。
Args:
rid (int): 运行ID。
char_dmg_df (pd.DataFrame): 角色直接伤害数据。
char_element_df (pd.DataFrame): 角色元素积蓄数据。
"""
output_path = f"{results_dir}/{rid}/damage_attribution.json"
# 检查文件是否已存在
if os.path.exists(output_path):
return
# 计算每种元素类型的异常总伤害
anomaly_name_list = list(ANOMALY_MAPPING.values()) + ["极性紊乱", "异放"]
anomaly_damage_totals = {element: 0 for element in anomaly_name_list}
for anomaly_name in anomaly_name_list:
if anomaly_name in char_dmg_df["name"].to_list():
filtered_df = char_dmg_df.filter(pl.col("name") == anomaly_name)
for row in filtered_df.iter_rows(named=True):
anomaly_damage_totals[anomaly_name] += row["dmg_expect_sum"]
# 初始化一个包含所有角色的字典
all_characters = set(char_dmg_df.filter(~pl.col("is_anomaly"))["name"].to_list()).union(
set(char_element_df["name"])
)
# 初始化角色伤害数据
attribution_data: dict[str, dict[str, float]] = {
name: {"direct_damage": 0, "anomaly_damage": 0} for name in all_characters
}
# 处理只打出直伤的角色
for row in char_dmg_df.filter(~pl.col("is_anomaly")).iter_rows(named=True):
name = row["name"]
direct_damage = row["dmg_expect_sum"]
attribution_data[name]["direct_damage"] = direct_damage
# 分配异常伤害到角色
for row in char_element_df.iter_rows(named=True):
name = row["name"]
element_type = row["element_type"]
buildup_sum = row["buildup_sum"]
anomaly_name = ANOMALY_MAPPING[element_type]
total_anomaly_damage = anomaly_damage_totals[anomaly_name]
# 计算角色的异常伤害归因
if total_anomaly_damage > 0:
element_total = char_element_df.filter(pl.col("element_type") == element_type)[
"buildup_sum"
].sum()
anomaly_damage_attribution = (buildup_sum / element_total) * total_anomaly_damage
else:
anomaly_damage_attribution = 0
# 更新角色的异常伤害
attribution_data[name]["anomaly_damage"] += anomaly_damage_attribution
# 处理极性紊乱和异放
for anomaly_name in ["极性紊乱", "异放"]:
total_anomaly_damage = anomaly_damage_totals.get(anomaly_name, 0)
if total_anomaly_damage > 0:
if anomaly_name == "极性紊乱":
for key in attribution_data:
if key == "柳":
attribution_data[key]["anomaly_damage"] += total_anomaly_damage
if anomaly_name == "异放":
for key in attribution_data:
if key == "薇薇安":
attribution_data[key]["anomaly_damage"] += total_anomaly_damage
with open(output_path, "w", encoding="utf-8") as f:
json.dump(attribution_data, f, ensure_ascii=False, indent=4)
def prepare_dmg_data_and_cache(
rid: int | str,
) -> dict[str, pl.DataFrame | dict[str, pl.DataFrame]] | None:
"""准备并缓存伤害分析所需的数据。
Args:
rid (int): 运行ID。
Returns:
Optional[dict[str, pl.DataFrame]]: 包含预处理后的数据的字典,
如果没有数据则返回None。
"""
dmg_result_df = _load_dmg_data(rid)
if dmg_result_df is None:
return None
uuid_df = sort_df_by_UUID(dmg_result_df)
char_chart_data = prepare_char_chart_data(uuid_df)
# st.write(char_chart_data)
calculate_and_save_anomaly_attribution(
int(rid), char_chart_data["char_dmg_df"], char_chart_data["char_element_df"]
)
return {
"dmg_result_df": dmg_result_df,
"char_dmg_df": char_chart_data["char_dmg_df"],
"uuid_df": uuid_df,
"char_chart_data": char_chart_data,
}
================================================
FILE: zsim/utils/process_parallel_data.py
================================================
"""
这个模块应该在WebUI被启用后依然存在,可以转移到api_src中。
"""
import asyncio
import json
import os
from typing import Any
import aiofiles
import plotly.graph_objects as go
from zsim.define import results_dir
from .constants import stats_trans_mapping
from .process_buff_result import prepare_buff_data_and_cache
from .process_dmg_result import prepare_dmg_data_and_cache
reversed_stats_trans_mapping = {v: k for k, v in stats_trans_mapping.items()}
def judge_parallel_result(rid: int | str) -> bool:
"""判断对应的rid是否为并行模式。
Args:
rid (int): 运行ID。
Returns:
bool: 如果是并行模式,则返回True;否则返回False。
"""
result_dir = os.path.join(results_dir, str(rid))
if not os.path.isdir(result_dir):
return False
parallel_config_path = os.path.join(result_dir, ".parallel_config.json")
if not os.path.exists(parallel_config_path):
return False
try:
with open(parallel_config_path, "r", encoding="utf-8") as f:
parallel_config: dict = json.load(f)
if not parallel_config.get("enabled", False):
return False
except (json.JSONDecodeError, IOError):
# 如果文件读取或解析失败,也视为非并行模式
return False
# 检查是否存在至少一个包含 sub.parallel_config.json 的子目录
for item in os.listdir(result_dir):
sub_dir_path = os.path.join(result_dir, item)
if os.path.isdir(sub_dir_path):
sub_config_path = os.path.join(sub_dir_path, "sub.parallel_config.json")
if os.path.exists(sub_config_path):
return True
return False
async def _process_sub_damage(sub_rid: str) -> None:
"""异步处理单个子目录的数据。
Args:
sub_rid (str): 子运行ID。
"""
# prepare_dmg_data_and_cache 不是异步函数,使用 to_thread
await asyncio.to_thread(prepare_dmg_data_and_cache, sub_rid)
async def _process_sub_buff(sub_rid: str) -> None:
"""异步处理单个子目录的数据。
Args:
sub_rid (str): 子运行ID。
"""
await prepare_buff_data_and_cache(sub_rid)
async def prepare_parallel_data_and_cache(rid: int | str) -> None:
"""对并行模式的每一份报告进行数据预处理,并将结果缓存到本地(异步执行)。
Args:
rid (int | str): 运行ID。
"""
result_dir = os.path.join(results_dir, str(rid))
parallel_config_path = os.path.join(result_dir, ".parallel_config.json")
try:
with open(parallel_config_path, "r", encoding="utf-8") as f:
parallel_config: dict = json.load(f)
except (json.JSONDecodeError, IOError) as e:
print(f"读取或解析并行配置文件 {parallel_config_path} 失败: {e}")
return
if parallel_config.get("adjust_sc", {}).get("enabled", False):
merged_sc_file_path = os.path.join(result_dir, "merged_sc_data.json")
if os.path.exists(merged_sc_file_path):
return
tasks = []
for item in os.listdir(result_dir):
sub_dir_path = os.path.join(result_dir, item)
if os.path.isdir(sub_dir_path):
sub_config_path = os.path.join(sub_dir_path, "sub.parallel_config.json")
if os.path.exists(sub_config_path):
sub_rid: str = os.path.join(str(rid), item) # 子进程rid
# 创建异步任务
tasks.append(_process_sub_damage(sub_rid))
# 并发执行所有任务
if tasks:
await asyncio.gather(*tasks)
# 统计并行模式的个子进程伤害归并结果
async def merge_parallel_dmg_data(
rid: int | str,
) -> tuple[str, dict[str, Any]] | None:
"""对并行模式的每一份报告进行数据预处理,并将结果缓存到本地。
Args:
rid (int): 运行ID。
"""
result_dir = os.path.join(results_dir, str(rid))
parallel_config_path = os.path.join(result_dir, ".parallel_config.json")
async with aiofiles.open(parallel_config_path, "r", encoding="utf-8") as f:
parallel_config: dict = json.loads(await f.read())
if parallel_config.get("adjust_sc", {}).get("enabled", False):
# 属性收益曲线功能
func = "attr_curve"
merged_sc_file_path = os.path.join(result_dir, "merged_sc_data.json")
sc_merged_data = {}
if os.path.exists(merged_sc_file_path):
async with aiofiles.open(merged_sc_file_path, "r", encoding="utf-8") as f:
sc_merged_data = json.loads(await f.read())
else:
try:
print("首次处理读取属性收益曲线数据,请稍等...")
sc_merged_data = await _merge_attr_curve_data(rid)
print("属性收益曲线数据合并完成!")
# 将合并后的数据保存到 JSON 文件
try:
async with aiofiles.open(merged_sc_file_path, "w", encoding="utf-8") as f:
await f.write(json.dumps(sc_merged_data, indent=4, ensure_ascii=False))
print(f"合并的属性收益曲线数据已保存至 {merged_sc_file_path}")
except IOError as e:
print(f"保存合并的属性收益曲线数据失败: {e}")
except Exception as e:
print(f"合并属性收益曲线数据时出错: {e}")
return func, sc_merged_data
elif parallel_config.get("adjust_weapon", {}).get("enabled", False):
# 武器切换功能
func = "weapon"
merged_weapon_file_path = os.path.join(result_dir, "merged_weapon_data.json")
weapon_merged_data = {}
if os.path.exists(merged_weapon_file_path):
async with aiofiles.open(merged_weapon_file_path, "r", encoding="utf-8") as f:
weapon_merged_data = json.loads(await f.read())
else:
try:
print("首次处理读取武器切换数据,请稍等...")
weapon_merged_data = await _merge_weapon_data(rid)
print("武器切换数据合并完成!")
# 将合并后的数据保存到 JSON 文件
try:
async with aiofiles.open(merged_weapon_file_path, "w", encoding="utf-8") as f:
await f.write(json.dumps(weapon_merged_data, indent=4, ensure_ascii=False))
print(f"合并的武器切换数据已保存至 {merged_weapon_file_path}")
except IOError as e:
print(f"保存合并的武器切换数据失败: {e}")
except Exception as e:
print(f"合并武器切换数据时出错: {e}")
return func, weapon_merged_data
else:
return None
def __draw_attr_curve(
sc_merged_data: dict[str, dict[str, dict[int | float, dict[str, float | None]]]],
) -> None:
"""绘制属性收益曲线折线图"""
if sc_merged_data:
for char_name, char_data in sc_merged_data.items():
fig = go.Figure()
has_data = False # 标记是否有数据添加到图表中
x_values = [] # 初始化x_values
for sc_name, sc_values_results in char_data.items():
# sc_values_results 的结构现在是 {sc_value: {"result": float, "rate": float | None}}
# 数据在 merge_parallel_sc_data 中已经按 sc_value 排序
if not sc_values_results:
print(f"角色 '{char_name}' 的词条 '{sc_name}' 没有数据,跳过绘制。")
continue
# 提取 x 值 (词条值) 和 y 值 (收益率)
x_values_raw = list(sc_values_results.keys())
# 提取预计算的收益率,跳过第一个点(收益率通常为None)
y_values_rate = [data.get("rate") for data in sc_values_results.values()]
# 尝试将 x 值转换为浮点数
try:
x_values = [float(x) for x in x_values_raw]
except ValueError:
print(f"角色 '{char_name}' 的词条 '{sc_name}' 包含非数值的 x 值,跳过绘制。")
continue
# 确保有足够的数据点来绘制收益率(至少需要两个原始点才能计算一个收益率点)
if len(x_values) < 2:
print(
f"角色 '{char_name}' 的词条 '{sc_name}' 数据点不足 (<2),无法绘制收益率曲线。"
)
continue
# 过滤掉第一个点的 x 值和 y 值(因为第一个点没有收益率)
# 同时处理 y_values_rate 中可能存在的 None 值
plot_x_values = []
plot_y_values = []
for i in range(1, len(x_values)):
if y_values_rate[i] is not None:
plot_x_values.append(x_values[i])
plot_y_values.append(y_values_rate[i])
if not plot_x_values:
print(
f"角色 '{char_name}' 的词条 '{sc_name}' 没有有效的收益率数据点,跳过绘制。"
)
continue
fig.add_trace(
go.Scatter(
x=plot_x_values, # 使用过滤后的 x 值
y=plot_y_values, # 使用过滤后的 y 值 (收益率)
mode="lines+markers",
name=reversed_stats_trans_mapping.get(sc_name, sc_name),
connectgaps=False, # 不连接 None 值造成的断点
)
)
has_data = True
if has_data:
# 计算整数刻度 (基于原始的所有 x_values)
try:
# 确保只使用数值类型的 x 值
numeric_x_values = [x for x in x_values if isinstance(x, (int, float))]
if not numeric_x_values:
raise ValueError("No numeric x values found")
min_x = min(numeric_x_values)
max_x = max(numeric_x_values)
# 生成从最小整数到最大整数的所有整数刻度
integer_ticks = list(
range(
int(min_x) if min_x == int(min_x) else int(min_x) + 1,
int(max_x) + 1,
)
)
# 如果最小值本身是整数,也包含它
if isinstance(min_x, int) or (isinstance(min_x, float) and min_x.is_integer()):
if int(min_x) not in integer_ticks:
integer_ticks.insert(0, int(min_x))
integer_ticks.sort() # 确保刻度排序
except ValueError: # 如果 x_values 为空或不包含数字
integer_ticks = []
# fmt: off
fig.update_layout(
title=f"{char_name} - 属性收益曲线",
xaxis_title="词条数",
yaxis_title="收益率", # 更新 Y 轴标题
hovermode="x unified",
yaxis=dict(tickformat=".2%"), # 将 Y 轴格式化为百分比
xaxis=dict(
tickmode="array" if integer_ticks else "auto", # 如果有计算出的整数刻度则使用array模式
tickvals=integer_ticks if integer_ticks else None, # 设置刻度值为整数
tickformat="d", # 强制显示为整数
),
)
# 使用show()方法显示图表而不是Streamlit
fig.show()
else:
print(f"角色 '{char_name}' 没有足够的数据来绘制组合图表。")
# fmt: on
else:
print("没有可用于绘制属性收益曲线的数据。")
def __draw_weapon_data(
weapon_merged_data: dict[str, dict[str, dict[str, dict[str, Any]]]],
) -> None:
"""绘制武器对比柱状图"""
if weapon_merged_data:
for char_name, char_data in weapon_merged_data.items():
fig = go.Figure()
has_data = False # 标记是否有数据添加到图表中
# 收集所有武器和精炼等级的数据
weapons_data = {}
for weapon_name, weapon_levels in char_data.items():
if not weapon_levels:
print(f"角色 '{char_name}' 的武器 '{weapon_name}' 没有数据,跳过绘制。")
continue
# 为每个精炼等级收集伤害数据
for level, level_data in weapon_levels.items():
damage = level_data.get("damage", 0.0)
if weapon_name not in weapons_data:
weapons_data[weapon_name] = {}
weapons_data[weapon_name][level] = damage
# 如果没有收集到数据,跳过此角色
if not weapons_data:
print(f"角色 '{char_name}' 没有可用的武器数据,跳过绘制。")
continue
# 收集所有独特的精炼等级
all_levels = sorted(
list(
set(
level
for levels_data in weapons_data.values()
for level in levels_data.keys()
)
)
)
all_weapon_names = list(weapons_data.keys())
# 为每个精炼等级创建柱状图系列
for level in all_levels:
level_damages = []
for weapon_name in all_weapon_names:
# 获取该武器在该精炼等级的伤害,如果不存在则为0
damage = weapons_data.get(weapon_name, {}).get(level, 0.0)
level_damages.append(damage)
if any(d > 0 for d in level_damages): # 只添加有数据的精炼等级系列
fig.add_trace(
go.Bar(
x=all_weapon_names, # 武器名称作为 X 轴
y=level_damages,
name=f"精炼 {level}", # 精炼等级作为系列名称
text=[f"{damage:.2f}" for damage in level_damages],
textposition="auto",
)
)
has_data = True
if has_data:
# 更新图表布局
fig.update_layout(
title=f"{char_name} - 武器伤害对比",
xaxis_title="武器名称", # 更新 X 轴标题
yaxis_title="总伤害",
barmode="group", # 分组模式,按 X 轴(武器名称)分组
hovermode="x unified",
)
# 使用show()方法显示图表而不是Streamlit
fig.show()
else:
print(f"角色 '{char_name}' 没有足够的数据来绘制武器对比图表。")
else:
print("没有可用于绘制武器对比图表的数据。")
async def _read_json_file(file_path: str) -> dict[str, Any]:
"""异步读取JSON文件。
Args:
file_path (str): JSON文件路径。
Returns:
dict[str, Any]: 读取到的JSON内容,如果失败则返回空字典。
"""
try:
async with aiofiles.open(file_path, mode="r", encoding="utf-8") as f:
content = await f.read()
return json.loads(content)
except (FileNotFoundError, json.JSONDecodeError, IOError) as e:
# TODO: 使用更健壮的日志记录
print(f"Error reading JSON file {file_path}: {e}")
return {}
async def _collect_sub_parallel_data(
rid: int | str,
) -> list[dict[str, Any]]:
"""异步收集所有子进程的并行配置和伤害数据。
Args:
rid (int | str): 运行ID。
Returns:
list[dict[str, Any]]: 包含每个子进程配置和伤害数据的列表。
每个字典包含 'sub_config', 'sc_data', 'sub_dir_path'。
"""
result_dir: str = os.path.join(results_dir, str(rid))
tasks = []
sub_dir_paths_map: dict[int, str] = {} # 存储 task index 到 sub_dir_path 的映射
collected_data: list[dict[str, Any]] = []
# 收集需要读取的文件路径
task_index = 0
for item in os.listdir(result_dir):
sub_dir_path = os.path.join(result_dir, item)
if os.path.isdir(sub_dir_path):
sub_config_path = os.path.join(sub_dir_path, "sub.parallel_config.json")
dmg_attribution_path = os.path.join(sub_dir_path, "damage_attribution.json")
if os.path.exists(sub_config_path) and os.path.exists(dmg_attribution_path):
# 添加读取配置文件的任务
tasks.append(_read_json_file(sub_config_path))
sub_dir_paths_map[task_index] = sub_dir_path # 记录config对应的目录
task_index += 1
# 添加读取伤害数据的任务
tasks.append(_read_json_file(dmg_attribution_path))
sub_dir_paths_map[task_index] = sub_dir_path # 记录dmg对应的目录
task_index += 1
# 并发执行所有文件读取任务
if not tasks:
print(f"在 {result_dir} 中未找到有效的子进程结果目录。")
return []
results = await asyncio.gather(*tasks)
# 处理读取结果
i = 0
while i < len(results):
sub_config: dict[str, Any] = results[i]
sc_data: dict[str, Any] = results[i + 1]
current_sub_dir = sub_dir_paths_map.get(i, "未知子目录") # 获取对应的子目录路径
i += 2
if not sub_config:
print(
f"警告:跳过子目录 {current_sub_dir},因为 sub.parallel_config.json 读取失败或为空。"
)
continue
if not sc_data:
print(
f"警告:跳过子目录 {current_sub_dir},因为 damage_attribution.json 读取失败或为空。"
)
continue
collected_data.append(
{
"sub_config": sub_config,
"sc_data": sc_data,
"sub_dir_path": current_sub_dir,
}
)
return collected_data
async def _merge_attr_curve_data(
rid: int | str,
) -> dict[str, dict[str, dict[int | float, dict[str, float | None]]]]:
"""读取所有子进程的属性收益曲线数据,合并并计算收益率。
Args:
rid (int | str): 运行ID。
Returns:
dict[str, dict[str, dict[int | float, dict[str, float | None]]]]: {
角色名(adjust_char): {
词条名(sc_name): {
词条值(sc_value): {
"result": 原始结果(sc_result: float),
"rate": 收益率(rate_of_return: float | None)
}
}
}
}
"""
all_sc_data: dict[str, dict[str, dict[int | float | None, float | None]]] = {}
collected_data = await _collect_sub_parallel_data(rid)
for item in collected_data:
sub_config = item["sub_config"]
sc_data = item["sc_data"]
current_sub_dir = item["sub_dir_path"]
adjust_char: str | None = sub_config.get("adjust_char")
sc_name: str | None = sub_config.get("sc_name")
# sc_value 可能是 int 或 float
sc_value_raw: Any = sub_config.get("sc_value")
sc_value: int | float | None = None
if isinstance(sc_value_raw, (int, float)):
sc_value = sc_value_raw
if adjust_char is None or sc_name is None or sc_value is None:
print(
f"警告:跳过子目录 {current_sub_dir},缺少必要的配置信息 (adjust_char, sc_name, sc_value)。"
)
continue
# damage_attribution.json 处理
char_dmg_data: dict[str, Any] | None = sc_data.get(adjust_char)
if char_dmg_data is None:
print(
f"警告:跳过子目录 {current_sub_dir},在 damage_attribution.json 中未找到角色 '{adjust_char}' 的数据。"
)
continue
# 伤害数据包含 direct_damage 和 anomaly_damage
direct_damage: float = char_dmg_data.get("direct_damage", 0.0)
anomaly_damage: float = char_dmg_data.get("anomaly_damage", 0.0)
sc_result: float = direct_damage + anomaly_damage
# 填充结果字典
if adjust_char not in all_sc_data:
all_sc_data[adjust_char] = {}
if sc_name not in all_sc_data[adjust_char]:
all_sc_data[adjust_char][sc_name] = {}
# 检查 sc_value 是否已存在,如果存在则打印警告(理论上并行配置不应重复)
if sc_value in all_sc_data[adjust_char][sc_name]:
print(
f"警告:在角色 '{adjust_char}' 的词条 '{sc_name}' 中,词条值 '{sc_value}' 重复出现。来自子目录: {current_sub_dir}"
)
# 存储原始结果
all_sc_data[adjust_char][sc_name][sc_value] = { # type: ignore
"result": sc_result,
"rate": None,
}
# 对每个词条的值按 sc_value 排序并计算收益率
for char_name, char_data in all_sc_data.items():
for sc_name_key, sc_values_data in char_data.items():
# 按 sc_value 排序
try:
# 尝试将键转换为浮点数进行排序
# 过滤掉 sc_value 为 None 的项再排序
filtered_items = [(k, v) for k, v in sc_values_data.items() if k is not None]
sorted_items = sorted(filtered_items, key=lambda item: float(item[0]))
except ValueError:
# 如果转换失败,按原始键(字符串)排序
sorted_items = [(k, v) for k, v in sc_values_data.items() if k is not None]
sorted_items = sorted(sorted_items, key=lambda item: str(item[0]))
# 更新排序后的字典,并计算收益率
sorted_sc_data: dict[int | float, dict[str, float | None]] = {}
previous_result: float | None = None
for i, (sc_val, data) in enumerate(sorted_items):
current_result = data["result"] # type: ignore
rate = None
if i > 0 and previous_result is not None and previous_result != 0:
rate = (current_result / previous_result) - 1
sorted_sc_data[sc_val] = {"result": current_result, "rate": rate}
previous_result = current_result
# 用包含收益率的排序后字典替换原来的字典
all_sc_data[char_name][sc_name_key] = sorted_sc_data # type: ignore
return all_sc_data # type: ignore
async def _merge_weapon_data(
rid: int | str,
) -> dict[str, dict[str, dict[str, dict[str, Any]]]]:
"""读取所有子进程的武器切换数据,合并并计算平均伤害。
Args:
rid (int | str): 运行ID。
Returns:
dict[str, dict[str, dict[str, dict[str, Any]]]]: {
角色名(adjust_char): {
武器名(weapon_name): {
精炼等级{weapon_level}: {
"damage": 总伤害加权,
}
}
}
}
"""
all_weapon_data: dict[str, dict[str, dict[str, dict[str, Any]]]] = {}
collected_data = await _collect_sub_parallel_data(rid)
for item in collected_data:
sub_config = item["sub_config"]
sc_data = item["sc_data"]
current_sub_dir = item["sub_dir_path"]
adjust_char: str | None = sub_config.get("adjust_char")
weapon_name: str | None = sub_config.get("weapon_name")
weapon_level: str | None = sub_config.get("weapon_level")
if adjust_char is None or weapon_name is None or weapon_level is None:
print(
f"警告:跳过子目录 {current_sub_dir},缺少必要的配置信息 (adjust_char, weapon_name, weapon_level)。"
)
continue
char_dmg_data: dict[str, Any] | None = sc_data.get(adjust_char)
if char_dmg_data is None:
print(
f"警告:跳过子目录 {current_sub_dir},在 damage_attribution.json 中未找到角色 '{adjust_char}' 的数据。"
)
continue
# 伤害数据包含 direct_damage 和 anomaly_damage
direct_damage: float = char_dmg_data.get("direct_damage", 0.0)
anomaly_damage: float = char_dmg_data.get("anomaly_damage", 0.0)
total_damage: float = direct_damage + anomaly_damage
# 填充结果字典
if adjust_char not in all_weapon_data:
all_weapon_data[adjust_char] = {}
if weapon_name not in all_weapon_data[adjust_char]:
all_weapon_data[adjust_char][weapon_name] = {}
# 检查 weapon_level 是否已存在,如果存在则打印警告(理论上并行配置不应重复)
if weapon_level in all_weapon_data[adjust_char][weapon_name]:
print(
f"警告:在角色 '{adjust_char}' 的武器 '{weapon_name}' 中,精炼等级 '{weapon_level}' 重复出现。来自子目录: {current_sub_dir}"
)
# 存储总伤害
all_weapon_data[adjust_char][weapon_name][weapon_level] = {
"damage": total_damage,
}
return all_weapon_data
================================================
FILE: zsim/webui.py
================================================
import streamlit as st
from zsim.lib_webui.version_checker import check_github_updates
# 页面导航
PAGES = {
"功能选择": [
st.Page("page_character_config.py", title="角色配置"),
st.Page("page_simulator.py", title="模拟器"),
st.Page("page_data_analysis.py", title="数据分析"),
st.Page("page_apl_editor.py", title="APL编辑器"),
],
"文档": [
st.Page("lib_webui/doc_pages/page_char_support.py", title="角色支持列表"),
st.Page("lib_webui/doc_pages/page_apl_doc.py", title="APL设计书"),
st.Page("lib_webui/doc_pages/page_contribution.py", title="贡献指南"),
],
}
def main():
st.set_page_config(layout="wide")
st.markdown(
"""
""",
unsafe_allow_html=True,
)
# 检查GitHub更新
check_github_updates()
pg = st.navigation(PAGES, expanded=True)
pg.run()
if __name__ == "__main__":
main()
================================================
FILE: zsim_api.spec
================================================
# -*- mode: python ; coding: utf-8 -*-
"""
ZSim API PyInstaller configuration file
Used to package zsim/api.py into a standalone executable
Supports both current platform builds and cross-compilation:
- Current platform: uv run pyinstaller zsim_api.spec
- Cross-platform: TARGET_PLATFORM=windows/linux/macos uv run pyinstaller zsim_api.spec
"""
import os
import platform
from pathlib import Path
import toml
# Get target platform from environment variable or detect current platform
TARGET_PLATFORM = os.environ.get('TARGET_PLATFORM', platform.system().lower())
if TARGET_PLATFORM == 'darwin':
TARGET_PLATFORM = 'macos'
print(f"Building for target platform: {TARGET_PLATFORM}")
# Get project root directory
project_root = Path(os.getcwd())
# Basic configuration
block_cipher = None
# Data file configuration
datas = []
binaries = []
# Add data directory
data_dir = project_root / "zsim" / "data"
if data_dir.exists():
datas.append((str(data_dir), "zsim/data"))
print(f"Added data directory: {data_dir} -> zsim/data")
# Add configuration file
config_file = project_root / "zsim" / "config_example.json"
if config_file.exists():
datas.append((str(config_file), "zsim/config_example.json"))
print(f"Added configuration file: {config_file} -> zsim/config_example.json")
# Add other necessary configuration files
config_json = project_root / "zsim" / "config.json"
if config_json.exists():
datas.append((str(config_json), "zsim/config.json"))
print(f"Added configuration file: {config_json} -> zsim/config.json")
# Add buff configuration file
buff_config_json = project_root / "zsim" / "sim_progress" / "Buff" / "buff_config.json"
if buff_config_json.exists():
datas.append((str(buff_config_json), "zsim/sim_progress/Buff/buff_config.json"))
print(f"Added configuration file: {buff_config_json} -> zsim/sim_progress/Buff/buff_config.json")
# Hidden imports (to prevent PyInstaller from failing to detect automatically)
hiddenimports = [
"pandas",
"tqdm",
"numpy",
"dash",
"setuptools",
"toml",
"aiofiles",
"pydantic",
"psutil",
"streamlit_ace",
"polars",
"pywebview",
"fastapi",
"uvicorn",
"aiosqlite",
"sqlalchemy",
"alembic",
"greenlet",
"httpx",
"zsim",
"plotly",
]
# Excluded modules
excludes = [
"tkinter",
"unittest",
"ipykernel",
"pytest",
"matplotlib",
"jupyter",
"streamlit",
"viztracer",
]
# Analysis configuration
a = Analysis(
['zsim/api.py'],
pathex=[str(project_root)],
binaries=binaries,
datas=datas,
hiddenimports=hiddenimports,
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=excludes,
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
# Packaging configuration
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
# Platform-specific executable configuration
exe_kwargs = {
'pyz': pyz,
'a.scripts': a.scripts,
'exclude_binaries': True,
'name': 'zsim_api',
'debug': False,
'bootloader_ignore_signals': False,
'strip': False,
'upx': True,
'upx_exclude': [],
'runtime_tmpdir': None,
'console': True,
'disable_windowed_traceback': False,
'argv_emulation': False,
'target_arch': None,
'codesign_identity': None,
'entitlements_file': None,
}
# Platform-specific adjustments
if TARGET_PLATFORM == 'windows':
exe_kwargs.update({
'win_no_prefer_redirects': False,
'win_private_assemblies': False,
})
elif TARGET_PLATFORM == 'macos':
exe_kwargs.update({
'argv_emulation': False,
})
elif TARGET_PLATFORM == 'linux':
pass # Linux uses default settings
exe = EXE(**exe_kwargs)
# Create collection with directory structure
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='zsim_api',
)
# Dynamically create version file during packaging
import shutil
import os
import tempfile
# Read version number
def get_version():
try:
with open("pyproject.toml", "r", encoding="utf-8") as f:
pyproject_config = toml.load(f)
return pyproject_config.get("project", {}).get("version", "0.0.0")
except FileNotFoundError:
return "1.0.0"
# Create temporary define.py file and inject version number
version_str = get_version()
with open("zsim/define.py", "r", encoding="utf-8") as f:
define_content = f.read()
# Replace version line
define_content = define_content.replace(
'__version__ = "1.0.0" # Default value, will be replaced during packaging',
f'__version__ = "{version_str}" # Version injected during packaging'
)
# Write to temporary file
temp_define_file = tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False)
temp_define_file.write(define_content)
temp_define_file.close()
# Add modified define.py file
datas.append((temp_define_file.name, "zsim/define.py"))
# Manually copy data files to root directory
dist_path = os.path.join("dist", "zsim_api")
import glob
zsim_dir = project_root / "zsim"
if zsim_dir.exists():
# Ensure target directory exists
target_zsim_dir = os.path.join(dist_path, "zsim")
os.makedirs(target_zsim_dir, exist_ok=True)
# Copy all .json, .toml, .md, .csv files
for ext in ["*.json", "*.toml", "*.md", "*.csv"]:
for file_path in zsim_dir.rglob(ext):
# Calculate relative path
rel_path = file_path.relative_to(zsim_dir)
target_path = os.path.join(target_zsim_dir, rel_path)
# Ensure target directory exists
os.makedirs(os.path.dirname(target_path), exist_ok=True)
# Copy file
shutil.copy2(str(file_path), target_path)
print(f"Copied configuration file: {file_path} -> {target_path}")
print(f"✅ Successfully configured build for {TARGET_PLATFORM}")
print(f"📦 Executable will be created in: dist/zsim_api/")