mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-19 10:32:19 +08:00
Merge branch 'dev' of https://github.com/MaaAssistantArknights/MaaAssistantArknights into dev
This commit is contained in:
@@ -1,354 +1,404 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
|
||||
# ======================= constants begin =========================
|
||||
class Direction:
|
||||
UP = "上"
|
||||
DOWN = "下"
|
||||
RIGHT = "右"
|
||||
LEFT = "左"
|
||||
|
||||
|
||||
class ActionType:
|
||||
Deploy = "部署"
|
||||
Skill = "技能"
|
||||
Retreat = "撤退"
|
||||
SpeedUp = "二倍速"
|
||||
BulletTime = "子弹时间"
|
||||
SkillUsage = "技能用法"
|
||||
|
||||
|
||||
class SkillUsageType:
|
||||
NotAutoUse = 0 # 0 - 不自动使用 默认
|
||||
UseWhenOk = 1 # 1 - 好了就用,有多少次用多少次(例如干员 棘刺 3 技能、桃金娘 1 技能等)
|
||||
UseWhenOkOnce = 2 # 2 - 好了就用,仅使用一次(例如干员 山 2 技能)
|
||||
AutoUse = 3 # 3 - 自动判断使用时机(画饼.jpg)
|
||||
|
||||
|
||||
# ========================= constants end =============================
|
||||
|
||||
class Document:
|
||||
def __init__(self, title: str, details: str, title_color: str = "dark", details_color: str = "dark"):
|
||||
self._details_color = details_color
|
||||
self._details = details
|
||||
self._title_color = title_color
|
||||
self._title = title
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
"title": self._title,
|
||||
"title_color": self._title_color,
|
||||
"details": self._details,
|
||||
"details_color": self._details_color
|
||||
}
|
||||
|
||||
|
||||
class Requirements:
|
||||
def __init__(self, elite: int, level: int, skill_level: int, module: int, potentiality: int):
|
||||
self._potentiality = potentiality
|
||||
self._module = module
|
||||
self._skill_level = skill_level
|
||||
self._level = level
|
||||
self._elite = elite
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"elite": self._elite,
|
||||
"level": self._level,
|
||||
"skill_level": self._skill_level,
|
||||
"module": self._module,
|
||||
"potentiality": self._potentiality
|
||||
}
|
||||
|
||||
|
||||
class Action:
|
||||
def __init__(self, action_type: str, name: str = None, location: tuple = None, direction: str = "无", kills: int = 0,
|
||||
skill_usage: int = None, pre_delay: int = 0, rear_delay: int = 0, timeout: int = 999999999,
|
||||
doc: str = None, doc_color: str = None):
|
||||
self._doc_color = doc_color
|
||||
self._doc = doc
|
||||
self._timeout = timeout
|
||||
self._rear_delay = rear_delay
|
||||
self._pre_delay = pre_delay
|
||||
self._skill_usage = skill_usage
|
||||
self._kills = kills
|
||||
self._direction = direction
|
||||
self._name = name
|
||||
self._location = location
|
||||
self._action_type = action_type
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
res = {
|
||||
"type": self._action_type,
|
||||
}
|
||||
if self._kills != 0:
|
||||
res["kills"] = self._kills
|
||||
if self._pre_delay != 0:
|
||||
res["pre_delay"] = self._pre_delay
|
||||
if self._rear_delay != 0:
|
||||
res["rear_delay"] = self._rear_delay
|
||||
if self._action_type == ActionType.Deploy or self._action_type == ActionType.Skill \
|
||||
or self._action_type == ActionType.Retreat:
|
||||
res["name"] = self._name
|
||||
|
||||
if self._action_type == ActionType.Deploy:
|
||||
res["location"] = self._location
|
||||
res["direction"] = self._direction
|
||||
return res
|
||||
|
||||
|
||||
class OperatorOrGroup:
|
||||
def __init__(self, name: str, battle: Battle):
|
||||
self._battle = battle
|
||||
self._name = name
|
||||
self._pre_delay = 0
|
||||
self._rear_delay = 0
|
||||
self._wait_kills = 0
|
||||
|
||||
def pre_delay(self, times: int) -> OperatorOrGroup:
|
||||
self._pre_delay = times
|
||||
return self
|
||||
|
||||
def rear_delay(self, times: int) -> OperatorOrGroup:
|
||||
self._rear_delay = times
|
||||
return self
|
||||
|
||||
def wait_kills(self, kills: int) -> OperatorOrGroup:
|
||||
self._wait_kills = kills
|
||||
return self
|
||||
|
||||
def deploy(self, location: tuple, direction: str):
|
||||
self._battle.add_action(self._add_conditions(Action(ActionType.Deploy, self._name, location, direction)))
|
||||
|
||||
def use_skill(self):
|
||||
self._battle.add_action(self._add_conditions(Action(ActionType.Skill, self._name)))
|
||||
|
||||
def retreat(self):
|
||||
self._battle.add_action(self._add_conditions(Action(ActionType.Retreat, self._name)))
|
||||
|
||||
def _add_conditions(self, action: Action) -> Action:
|
||||
if self._pre_delay != 0:
|
||||
action._pre_delay = self._pre_delay
|
||||
if self._rear_delay != 0:
|
||||
action._rear_delay = self._rear_delay
|
||||
if self._wait_kills != 0:
|
||||
action._kills = self._wait_kills
|
||||
self._clear_conditions()
|
||||
return action
|
||||
|
||||
def _clear_conditions(self):
|
||||
self._pre_delay = 0
|
||||
self._rear_delay = 0
|
||||
self._wait_kills = 0
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
# 此处不应该被执行到
|
||||
raise Exception("请使用子类方法")
|
||||
|
||||
|
||||
class Group(OperatorOrGroup):
|
||||
def __init__(self, name: str, battle: Battle, operators: tuple):
|
||||
"""
|
||||
请使用 Battle.create_group()
|
||||
:param name:
|
||||
:param battle:
|
||||
:param operators:
|
||||
"""
|
||||
super().__init__(name, battle)
|
||||
self._operators = operators
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
"name": self._name,
|
||||
"opers": [operator.to_dict() for operator in self._operators]
|
||||
}
|
||||
|
||||
|
||||
class Operator(OperatorOrGroup):
|
||||
def __init__(self, name: str, battle: Battle, skill: int, skill_usage: int = SkillUsageType.NotAutoUse,
|
||||
requirements: Requirements = None):
|
||||
"""
|
||||
仅当要创建群组时才应该被调用
|
||||
正常使用请用 Battle.create_operator()
|
||||
:param name:
|
||||
:param battle:
|
||||
:param skill:
|
||||
:param skill_usage:
|
||||
:param requirements:
|
||||
"""
|
||||
super().__init__(name, battle)
|
||||
self._requirements = requirements
|
||||
self._skill_usage = skill_usage
|
||||
self._skill = skill
|
||||
|
||||
def skill_usage_change(self, change_to: int):
|
||||
"""
|
||||
改变技能使用的类型
|
||||
由于需要兼容群组类型,有时此方法可能无法通过代码提示调用,手动调用即可
|
||||
:param change_to: 需要调整到的 SkillUsageType
|
||||
:return:
|
||||
"""
|
||||
self._battle.add_action(self._add_conditions(Action(ActionType.SkillUsage, skill_usage=change_to)))
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
res = {
|
||||
"name": self._name,
|
||||
"skill": self._skill,
|
||||
"skill_usage": self._skill_usage,
|
||||
}
|
||||
if self._skill_usage != SkillUsageType.NotAutoUse:
|
||||
res["skill_usage"] = self._skill_usage
|
||||
if self._requirements is not None:
|
||||
res["requirements"] = self._requirements.to_dict()
|
||||
return res
|
||||
|
||||
|
||||
class Battle:
|
||||
def __init__(self, stage_name: str, version="v4.0", doc: Document = None):
|
||||
self._doc = doc
|
||||
self._stage_name = stage_name
|
||||
self._version = version
|
||||
self._operators = []
|
||||
self._groups = []
|
||||
self._actions = []
|
||||
self._wait_times = 0
|
||||
self._wait_kills = 0
|
||||
self._speedup = False
|
||||
self._last_save_file_name = "battle.json"
|
||||
|
||||
def create_operator(self, name: str, skill: int, skill_usage: int = SkillUsageType.NotAutoUse,
|
||||
requirements: Requirements = None) -> Operator:
|
||||
"""
|
||||
创建并添加一个干员
|
||||
:param name: 干员名称
|
||||
:param skill: 要使用的技能
|
||||
:param skill_usage: 技能使用类型 参见 SkillUsageType
|
||||
:param requirements: 可选
|
||||
:return: 新生成的干员
|
||||
"""
|
||||
new_operator = Operator(name, self, skill, skill_usage, requirements)
|
||||
self._add_operator(new_operator)
|
||||
return new_operator
|
||||
|
||||
def create_group(self, name: str, *operators: Operator) -> Group:
|
||||
"""
|
||||
创建并添加一个群组
|
||||
:param name: 群组名
|
||||
:param operators: 所包含的干员,构造的时候 battle 一项置 None 即可
|
||||
:return: 新创建的群组
|
||||
"""
|
||||
new_group = Group(name, self, operators)
|
||||
self._add_group(new_group)
|
||||
return new_group
|
||||
|
||||
def _add_operator(self, operator: Operator):
|
||||
self._operators.append(operator)
|
||||
|
||||
def _add_group(self, group: Group):
|
||||
self._groups.append(group)
|
||||
|
||||
def add_action(self, action: Action):
|
||||
self._actions.append(action)
|
||||
|
||||
def speed_up(self):
|
||||
"""
|
||||
调整为二倍速
|
||||
:return:
|
||||
"""
|
||||
if not self._speedup:
|
||||
self._speed_change()
|
||||
self._speedup = True
|
||||
|
||||
def speed_down(self):
|
||||
"""
|
||||
调整为一倍速
|
||||
:return:
|
||||
"""
|
||||
if self._speedup:
|
||||
self._speed_change()
|
||||
self._speedup = False
|
||||
|
||||
def _speed_change(self):
|
||||
self.add_action(Action(ActionType.SpeedUp))
|
||||
|
||||
def save_as_json(self, file_name: str = None):
|
||||
"""
|
||||
保存为 json 文件,生成在同目录下
|
||||
:param file_name: 要保存的文件名
|
||||
:return:
|
||||
"""
|
||||
if file_name is None:
|
||||
file_name = self._last_save_file_name
|
||||
else:
|
||||
self._last_save_file_name = file_name
|
||||
with open(file_name, 'w', encoding='utf-8') as f:
|
||||
json.dump(self.to_dict(), f, ensure_ascii=False)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
res = {
|
||||
"stage_name": self._stage_name,
|
||||
"minimum_required": self._version,
|
||||
"opers": [operator.to_dict() for operator in self._operators],
|
||||
"groups": [group.to_dict() for group in self._groups],
|
||||
"actions": [action.to_dict() for action in self._actions],
|
||||
}
|
||||
if self._doc is not None:
|
||||
res["doc"] = self._doc.to_dict()
|
||||
|
||||
return res
|
||||
|
||||
def test(self, enable_debug_output: bool = True):
|
||||
"""
|
||||
自动调用 MeoAssistantArknights 并测试生成自动战斗的文件
|
||||
测试的是上次生成的 json 需先调用 save_as_json()
|
||||
请自行配置 MeoAssistantArknights 所在地址等
|
||||
同时确保能导入 asst.py
|
||||
|
||||
:param enable_debug_output: 是否开启 debug 输出
|
||||
:return:
|
||||
"""
|
||||
# MeoAssistantArknights所在的路径
|
||||
mma_path = "D:\\Program Files\\MeoAssistantArknights"
|
||||
# adb的路径
|
||||
adb_path = "D:\\Program Files\\MeoAssistantArknights\\platform-tools\\adb.exe"
|
||||
# adb连接的地址
|
||||
adb_address = "127.0.0.1:8873"
|
||||
try:
|
||||
from asst import Asst, Message
|
||||
except ImportError:
|
||||
import sys
|
||||
import pathlib
|
||||
sys.path.append(str(pathlib.Path.cwd().parent.parent/"src"/"Python"))
|
||||
try:
|
||||
from asst import Asst, Message
|
||||
except ImportError:
|
||||
print("asst导入失败,请自行解决,或者下载"
|
||||
" https://github.com/MaaAssistantArknights/MaaAssistantArknights/blob/master/src/Python/asst.py "
|
||||
"到同目录下")
|
||||
return
|
||||
|
||||
@Asst.CallBackType
|
||||
def callback(msg, details, arg):
|
||||
print(Message(msg), json.loads(details.decode('utf-8')), arg)
|
||||
|
||||
if not Asst.load(mma_path):
|
||||
print("加载MeoAssistantArknights失败,请确定路径是否正确")
|
||||
|
||||
asst = Asst(callback) if enable_debug_output else Asst()
|
||||
if asst.connect(adb_path, adb_address):
|
||||
print('连接成功')
|
||||
else:
|
||||
print('连接失败')
|
||||
print('请检查adb地址或者端口是否有误')
|
||||
exit()
|
||||
asst.append_task('Copilot', {
|
||||
'stage_name': self._stage_name,
|
||||
'filename': self._last_save_file_name,
|
||||
'formation': False,
|
||||
})
|
||||
asst.start()
|
||||
while True:
|
||||
if "stop" == str(input('>')):
|
||||
exit()
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
|
||||
# ======================= constants begin =========================
|
||||
class Direction:
|
||||
UP = "上"
|
||||
DOWN = "下"
|
||||
RIGHT = "右"
|
||||
LEFT = "左"
|
||||
|
||||
|
||||
class ActionType:
|
||||
Deploy = "部署"
|
||||
Skill = "技能"
|
||||
Retreat = "撤退"
|
||||
SpeedUp = "二倍速"
|
||||
BulletTime = "子弹时间"
|
||||
SkillUsage = "技能用法"
|
||||
|
||||
|
||||
class SkillUsageType:
|
||||
NotAutoUse = 0 # 0 - 不自动使用 默认
|
||||
UseWhenOk = 1 # 1 - 好了就用,有多少次用多少次(例如干员 棘刺 3 技能、桃金娘 1 技能等)
|
||||
UseWhenOkOnce = 2 # 2 - 好了就用,仅使用一次(例如干员 山 2 技能)
|
||||
# AutoUse = 3 # 3 - 自动判断使用时机(画饼.jpg)
|
||||
|
||||
|
||||
# ========================= constants end =============================
|
||||
|
||||
class Document:
|
||||
def __init__(self, title: str, details: str, title_color: str = "dark", details_color: str = "dark"):
|
||||
self._details_color = details_color
|
||||
self._details = details
|
||||
self._title_color = title_color
|
||||
self._title = title
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
"title": self._title,
|
||||
"title_color": self._title_color,
|
||||
"details": self._details,
|
||||
"details_color": self._details_color
|
||||
}
|
||||
|
||||
|
||||
class Requirements:
|
||||
def __init__(self, elite: int, level: int, skill_level: int, module: int, potentiality: int):
|
||||
self._potentiality = potentiality
|
||||
self._module = module
|
||||
self._skill_level = skill_level
|
||||
self._level = level
|
||||
self._elite = elite
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"elite": self._elite,
|
||||
"level": self._level,
|
||||
"skill_level": self._skill_level,
|
||||
"module": self._module,
|
||||
"potentiality": self._potentiality
|
||||
}
|
||||
|
||||
|
||||
class Action:
|
||||
def __init__(self, action_type: str, name: str = None, location: tuple = None, direction: str = "无", kills: int = 0,
|
||||
skill_usage: int = None, pre_delay: int = 0, rear_delay: int = 0, cost_changes: int = 0,
|
||||
timeout: int = 999999999, doc: str = None, doc_color: str = None):
|
||||
self._doc_color = doc_color
|
||||
self._doc = doc
|
||||
# self._timeout = timeout
|
||||
self._cost_changes = cost_changes
|
||||
self._rear_delay = rear_delay
|
||||
self._pre_delay = pre_delay
|
||||
self._skill_usage = skill_usage
|
||||
self._kills = kills
|
||||
self._direction = direction
|
||||
self._name = name
|
||||
self._location = location
|
||||
self._action_type = action_type
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
res = {
|
||||
"type": self._action_type,
|
||||
}
|
||||
if self._kills != 0:
|
||||
res["kills"] = self._kills
|
||||
if self._pre_delay != 0:
|
||||
res["pre_delay"] = self._pre_delay
|
||||
if self._rear_delay != 0:
|
||||
res["rear_delay"] = self._rear_delay
|
||||
if self._cost_changes != 0:
|
||||
res["cost_changes"] = self._cost_changes
|
||||
# 保留字段,暂未实现。
|
||||
# if self._timeout != 0:
|
||||
# res["timeout"] = self._timeout
|
||||
if self._action_type == ActionType.Deploy or self._action_type == ActionType.Skill \
|
||||
or self._action_type == ActionType.Retreat:
|
||||
res["name"] = self._name
|
||||
|
||||
if self._action_type == ActionType.Deploy:
|
||||
res["location"] = self._location
|
||||
res["direction"] = self._direction
|
||||
return res
|
||||
|
||||
|
||||
class OperatorOrGroup:
|
||||
def __init__(self, name: str, battle: Battle):
|
||||
self._battle = battle
|
||||
self._name = name
|
||||
self._pre_delay = 0
|
||||
self._rear_delay = 0
|
||||
self._wait_kills = 0
|
||||
self._cost_changes = 0
|
||||
|
||||
def pre_delay(self, times: int) -> OperatorOrGroup:
|
||||
self._pre_delay = times
|
||||
return self
|
||||
|
||||
def rear_delay(self, times: int) -> OperatorOrGroup:
|
||||
self._rear_delay = times
|
||||
return self
|
||||
|
||||
def cost_changes(self, costs: int) -> OperatorOrGroup:
|
||||
self._cost_changes = costs
|
||||
return self
|
||||
|
||||
def wait_kills(self, kills: int) -> OperatorOrGroup:
|
||||
self._wait_kills = kills
|
||||
return self
|
||||
|
||||
def deploy(self, location: tuple, direction: str):
|
||||
self._battle.add_action(self._add_conditions(Action(ActionType.Deploy, self._name, location, direction)))
|
||||
|
||||
def use_skill(self):
|
||||
self._battle.add_action(self._add_conditions(Action(ActionType.Skill, self._name)))
|
||||
|
||||
def retreat(self):
|
||||
self._battle.add_action(self._add_conditions(Action(ActionType.Retreat, self._name)))
|
||||
|
||||
def _add_conditions(self, action: Action) -> Action:
|
||||
if self._pre_delay != 0:
|
||||
action._pre_delay = self._pre_delay
|
||||
if self._rear_delay != 0:
|
||||
action._rear_delay = self._rear_delay
|
||||
if self._wait_kills != 0:
|
||||
action._kills = self._wait_kills
|
||||
if self._cost_changes != 0:
|
||||
action._cost_changes = self._cost_changes
|
||||
self._clear_conditions()
|
||||
return action
|
||||
|
||||
def _clear_conditions(self):
|
||||
self._pre_delay = 0
|
||||
self._rear_delay = 0
|
||||
self._wait_kills = 0
|
||||
self._cost_changes = 0
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
# 此处不应该被执行到
|
||||
raise Exception("请使用子类方法")
|
||||
|
||||
|
||||
class Group(OperatorOrGroup):
|
||||
def __init__(self, name: str, battle: Battle, operators: tuple):
|
||||
"""
|
||||
请使用 Battle.create_group()
|
||||
:param name:
|
||||
:param battle:
|
||||
:param operators:
|
||||
"""
|
||||
super().__init__(name, battle)
|
||||
self._operators = operators
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
"name": self._name,
|
||||
"opers": [operator.to_dict() for operator in self._operators]
|
||||
}
|
||||
|
||||
|
||||
class Operator(OperatorOrGroup):
|
||||
def __init__(self, name: str, battle: Battle, skill: int, skill_usage: int = SkillUsageType.NotAutoUse,
|
||||
requirements: Requirements = None):
|
||||
"""
|
||||
仅当要创建群组时才应该被调用
|
||||
正常使用请用 Battle.create_operator()
|
||||
:param name:
|
||||
:param battle:
|
||||
:param skill:
|
||||
:param skill_usage:
|
||||
:param requirements:
|
||||
"""
|
||||
super().__init__(name, battle)
|
||||
self._requirements = requirements
|
||||
self._skill_usage = skill_usage
|
||||
self._skill = skill
|
||||
self._character_name_list = None
|
||||
|
||||
def check_operator_name(self, auto_complete: bool = True) -> bool:
|
||||
if self._character_name_list is None:
|
||||
import os
|
||||
if os.path.exists('./characters/character_name_list.json'):
|
||||
with open('./characters/character_name_list.json', 'r', encoding='utf-8') as f:
|
||||
self._character_name_list = json.load(f)
|
||||
else:
|
||||
raise Exception("无法进行名称检查,请检查名称文件是否存在")
|
||||
if self._name in self._character_name_list:
|
||||
return True
|
||||
if auto_complete:
|
||||
possible_character_name = None
|
||||
for character_name in self._character_name_list:
|
||||
if self._name in character_name:
|
||||
if possible_character_name is None:
|
||||
possible_character_name = character_name
|
||||
else:
|
||||
raise Exception("匹配到多个名字,无法自动补全,请手动补全")
|
||||
if possible_character_name is not None:
|
||||
self._name = possible_character_name
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def skill_usage_change(self, change_to: int):
|
||||
"""
|
||||
改变技能使用的类型
|
||||
由于需要兼容群组类型,有时此方法可能无法通过代码提示调用,手动调用即可
|
||||
:param change_to: 需要调整到的 SkillUsageType
|
||||
:return:
|
||||
"""
|
||||
self._battle.add_action(self._add_conditions(Action(ActionType.SkillUsage, skill_usage=change_to)))
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
res = {
|
||||
"name": self._name,
|
||||
"skill": self._skill,
|
||||
"skill_usage": self._skill_usage,
|
||||
}
|
||||
if self._skill_usage != SkillUsageType.NotAutoUse:
|
||||
res["skill_usage"] = self._skill_usage
|
||||
if self._requirements is not None:
|
||||
res["requirements"] = self._requirements.to_dict()
|
||||
return res
|
||||
|
||||
|
||||
class Battle:
|
||||
def __init__(self, stage_name: str, version="v4.0", doc: Document = None):
|
||||
self._doc = doc
|
||||
self._stage_name = stage_name
|
||||
self._version = version
|
||||
self._operators = []
|
||||
self._groups = []
|
||||
self._actions = []
|
||||
self._wait_times = 0
|
||||
self._wait_kills = 0
|
||||
self._speedup = False
|
||||
self._last_save_file_name = "battle.json"
|
||||
|
||||
def create_operator(self, name: str, skill: int, skill_usage: int = SkillUsageType.NotAutoUse,
|
||||
requirements: Requirements = None, check_operator_name: bool = True) -> Operator:
|
||||
"""
|
||||
创建并添加一个干员
|
||||
:param name: 干员名称
|
||||
:param skill: 要使用的技能
|
||||
:param skill_usage: 技能使用类型 参见 SkillUsageType
|
||||
:param requirements: 可选
|
||||
:param check_operator_name: 是否要检查输入的名字是否正确
|
||||
:return: 新生成的干员
|
||||
"""
|
||||
new_operator = Operator(name, self, skill, skill_usage, requirements)
|
||||
if check_operator_name:
|
||||
if not new_operator.check_operator_name():
|
||||
raise Exception("干员名称有误,请更改为正确的名称或关闭名称检查")
|
||||
self._add_operator(new_operator)
|
||||
return new_operator
|
||||
|
||||
def create_group(self, name: str, check_operator_name: bool = True, *operators: Operator) -> Group:
|
||||
"""
|
||||
创建并添加一个群组
|
||||
:param name: 群组名
|
||||
:param operators: 所包含的干员,构造的时候 battle 一项置 None 即可
|
||||
:param check_operator_name: 是否要检查输入的名字是否正确
|
||||
:return: 新创建的群组
|
||||
"""
|
||||
if check_operator_name:
|
||||
for operator in operators:
|
||||
if not operator.check_operator_name():
|
||||
raise Exception("干员名称有误,请更改为正确的名称或关闭名称检查")
|
||||
new_group = Group(name, self, operators)
|
||||
self._add_group(new_group)
|
||||
return new_group
|
||||
|
||||
def _add_operator(self, operator: Operator):
|
||||
self._operators.append(operator)
|
||||
|
||||
def _add_group(self, group: Group):
|
||||
self._groups.append(group)
|
||||
|
||||
def add_action(self, action: Action):
|
||||
self._actions.append(action)
|
||||
|
||||
def speed_up(self):
|
||||
"""
|
||||
调整为二倍速
|
||||
:return:
|
||||
"""
|
||||
if not self._speedup:
|
||||
self._speed_change()
|
||||
self._speedup = True
|
||||
|
||||
def speed_down(self):
|
||||
"""
|
||||
调整为一倍速
|
||||
:return:
|
||||
"""
|
||||
if self._speedup:
|
||||
self._speed_change()
|
||||
self._speedup = False
|
||||
|
||||
def _speed_change(self):
|
||||
self.add_action(Action(ActionType.SpeedUp))
|
||||
|
||||
def save_as_json(self, file_name: str = None):
|
||||
"""
|
||||
保存为 json 文件,生成在同目录下
|
||||
:param file_name: 要保存的文件名
|
||||
:return:
|
||||
"""
|
||||
if file_name is None:
|
||||
file_name = self._last_save_file_name
|
||||
else:
|
||||
self._last_save_file_name = file_name
|
||||
with open(file_name, 'w', encoding='utf-8') as f:
|
||||
json.dump(self.to_dict(), f, ensure_ascii=False)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
res = {
|
||||
"stage_name": self._stage_name,
|
||||
"minimum_required": self._version,
|
||||
"opers": [operator.to_dict() for operator in self._operators],
|
||||
"groups": [group.to_dict() for group in self._groups],
|
||||
"actions": [action.to_dict() for action in self._actions],
|
||||
}
|
||||
if self._doc is not None:
|
||||
res["doc"] = self._doc.to_dict()
|
||||
|
||||
return res
|
||||
|
||||
def test(self, enable_debug_output: bool = True):
|
||||
"""
|
||||
自动调用 MeoAssistantArknights 并测试生成自动战斗的文件
|
||||
测试的是上次生成的 json 需先调用 save_as_json()
|
||||
请自行配置 MeoAssistantArknights 所在地址等
|
||||
同时确保能导入 asst.py
|
||||
|
||||
:param enable_debug_output: 是否开启 debug 输出
|
||||
:return:
|
||||
"""
|
||||
# MeoAssistantArknights所在的路径
|
||||
mma_path = "D:\\Program Files\\MeoAssistantArknights"
|
||||
# adb的路径
|
||||
adb_path = "D:\\Program Files\\MeoAssistantArknights\\platform-tools\\adb.exe"
|
||||
# adb连接的地址
|
||||
adb_address = "127.0.0.1:8873"
|
||||
try:
|
||||
from asst import Asst, Message
|
||||
except ImportError:
|
||||
import sys
|
||||
import pathlib
|
||||
sys.path.append(str(pathlib.Path.cwd().parent.parent / "src" / "Python"))
|
||||
try:
|
||||
from asst import Asst, Message
|
||||
except ImportError:
|
||||
print("asst导入失败,请自行解决,或者下载"
|
||||
" https://github.com/MaaAssistantArknights/MaaAssistantArknights/blob/master/src/Python/asst.py "
|
||||
"到同目录下")
|
||||
return
|
||||
|
||||
@Asst.CallBackType
|
||||
def callback(msg, details, arg):
|
||||
print(Message(msg), json.loads(details.decode('utf-8')), arg)
|
||||
|
||||
if not Asst.load(mma_path):
|
||||
print("加载MeoAssistantArknights失败,请确定路径是否正确")
|
||||
|
||||
asst = Asst(callback) if enable_debug_output else Asst()
|
||||
if asst.connect(adb_path, adb_address):
|
||||
print('连接成功')
|
||||
else:
|
||||
print('连接失败')
|
||||
print('请检查adb地址或者端口是否有误')
|
||||
exit()
|
||||
asst.append_task('Copilot', {
|
||||
'stage_name': self._stage_name,
|
||||
'filename': self._last_save_file_name,
|
||||
'formation': False,
|
||||
})
|
||||
asst.start()
|
||||
while True:
|
||||
if "stop" == str(input('>')):
|
||||
exit()
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
["Lancet-2", "Castle-3", "THRM-EX", "正义骑士号", "夜刀", "黑角", "巡林者", "杜林", "12F", "芬", "香草", "预备干员-近战", "翎羽", "玫兰莎", "泡普卡", "卡缇", "米格鲁", "斑点", "克洛丝", "安德切尔", "预备干员-狙击", "炎熔", "芙蓉", "安赛尔", "预备干员-后勤", "史都华德", "预备干员-术师", "梓兰", "夜烟", "远山", "格雷伊", "卡达", "深靛", "布丁", "杰西卡", "流星", "红云", "梅", "白雪", "松果", "安比尔", "酸糖", "讯使", "清道夫", "红豆", "桃金娘", "豆苗", "杜宾", "缠丸", "断罪者", "霜叶", "艾丝黛尔", "慕斯", "刻刀", "宴", "芳汀", "砾", "孑", "暗索", "末药", "嘉维尔", "苏苏洛", "调香师", "清流", "褐果", "角峰", "蛇屠箱", "泡泡", "古米", "坚雷", "深海色", "地灵", "波登可", "罗比菈塔", "伊桑", "阿消", "白面鸮", "微风", "凛冬", "德克萨斯", "贾维", "苇草", "野鬃", "极境", "夜半", "诗怀雅", "鞭刃", "芙兰卡", "炎客", "Sharp", "因陀罗", "燧石", "拉普兰德", "断崖", "柏喙", "战车", "幽灵鲨", "布洛卡", "星极", "铸铁", "赤冬", "羽毛笔", "龙舌兰", "蓝毒", "白金", "灰喉", "Stormeye", "四月", "寒芒克洛丝", "陨星", "慑砂", "送葬人", "奥斯塔", "阿米娅", "苦艾", "特米米", "天火", "Pith", "惊蛰", "蜜蜡", "莱恩哈特", "薄绿", "爱丽丝", "炎狱炎熔", "蚀清", "耶拉", "洛洛", "梅尔", "稀音", "赫默", "华法琳", "亚叶", "Touch", "锡兰", "絮雨", "图耶", "桑葚", "蜜莓", "临光", "吽", "红", "槐琥", "卡夫卡", "乌有", "雷蛇", "可颂", "拜松", "火神", "石棉", "暮落", "暮落", "闪击", "暴雨", "灰毫", "极光", "普罗旺斯", "守林人", "安哲拉", "熔泉", "埃拉托 ", "崖心", "雪雉", "初雪", "巫恋", "真理", "格劳克斯", "掠风", "空", "海蒂", "月禾", "九色鹿", "夏栎", "狮蝎", "绮良", "食铁兽", "见行者", "罗宾", "霜华", "贝娜", "风丸", "能天使", "空弦", "灰烬", "黑", "远牙", "W", "菲亚梅塔", "早露", "迷迭香", "假日威龙陈", "推进之王", "风笛", "嵯峨", "琴柳", "焰尾", "伊芙利特", "莫斯提马", "艾雅法拉", "刻俄柏", "夕", "异客", "卡涅利安", "澄闪", "灵知", "安洁莉娜", "铃兰", "麦哲伦", "浊心斯卡蒂", "令", "傀影", "老鲤", "温蒂", "阿", "歌蕾蒂娅", "水月", "归溟幽灵鲨", "闪灵", "夜莺", "凯尔希", "流明", "星熊", "塞雷娅", "瑕光", "年", "泥岩", "森蚺", "号角", "山", "银灰", "棘刺", "陈", "艾丽妮", "煌", "史尔特尔", "赫拉格", "帕拉斯", "耀骑士临光", "医疗探机", "触手", "Mon3tr", "幻影", "机械水獭", "龙腾.F", "龙腾.L", "龙腾.A", "诅咒娃娃", "镜中虚影", "此面向敌", "工程蓄水炮", "移动摄影器", "沙之碑", "迷迭香的战术装备", "\"夹子\"", "磐蟹护卫队", "\"小自在\"", "迎宾踏垫", "斯卡蒂的海嗣", "全自动造型仪", "“耀阳”", "“清平”", "“逍遥”", "“弦惊”", "眠兽", "纸偶", "可靠电池", "障碍物", "震撼装置", "闸门", "侦测器", "干扰装置", "弩炮", "指挥终端", "便携式补给站", "源石冰晶", "源石祭坛", "干扰地雷", "源石流发生装置", "L-44\"留声机\"", "巨蕈", "罗德岛临时雇员", "轰隆隆先生", "梅什科线圈", "道路障碍物", "能量聚合体", "霜星的源石冰晶", "爱国者的源石祭坛", "盾卫", "禁锢装置", "改良型二踢脚", "碎石", "脉冲防御模组", "催泪瓦斯控制阀", "无人机工厂", "雪雉的安全起重机", "土石结构", "高能源石炸弹", "加固装置", "大帝", "沙尘暴", "可移动战术机库", "应急救治设施", "子代", "特制水上平台", "风筝", "涨潮控制", "破碎支柱", "战场废墟", "爆破装置", "防水蚀镀膜装置", "城市霓虹", "骑士之徽", "暴风雪", "无主的财富", "失修舞台雾机", "便携气罐", "“报幕助手”", "封印的地面", "丹田", "“冰淇淋机”", "伦蒂尼姆城防副炮", "断罪者", "圣徒之手", "溟痕", "暴行", "空爆", "月见夜", "猎蜂", "杰克", "夜魔", "格拉尼", "斯卡蒂"]
|
||||
12
tools/CopilotDesinger/characters/parse_character_table.py
Normal file
12
tools/CopilotDesinger/characters/parse_character_table.py
Normal file
@@ -0,0 +1,12 @@
|
||||
# https://github.com/yuanyan3060/Arknights-Bot-Resource/blob/main/gamedata/excel/character_table.json
|
||||
# 去除character_table中不需要的字段
|
||||
import json
|
||||
|
||||
with open('./character_table.json', 'r', encoding='utf-8') as f:
|
||||
character_table_dict = json.load(f)
|
||||
character_name_list = []
|
||||
for character_name in character_table_dict.keys():
|
||||
character_name_list.append(character_table_dict[character_name]['name'])
|
||||
# print(new_character_table_dict)
|
||||
with open('./character_name_list.json', 'w', encoding='utf-8') as f:
|
||||
json.dump(character_name_list, f, ensure_ascii=False)
|
||||
249
tools/CopilotDesinger/index.js
Normal file
249
tools/CopilotDesinger/index.js
Normal file
@@ -0,0 +1,249 @@
|
||||
// 全局JSON数据
|
||||
const data = {};
|
||||
|
||||
// 模板
|
||||
const input_text = () => {
|
||||
return $('<input type="text" class="form-control">');
|
||||
};
|
||||
|
||||
const skill_usage = () => {
|
||||
return $('<select id="skill_usage" class="form-control" style="width:auto">' +
|
||||
'<option value=0>不自动使用</option>' +
|
||||
'<option value=1>好了就用,有多少次用多少次</option>' +
|
||||
'<option value=2>好了就用,仅使用一次</option>' +
|
||||
'<option value=3>自动判断使用时机</option>' +
|
||||
'</select>');
|
||||
};
|
||||
|
||||
const delete_icon = () => {
|
||||
return $('<button type="button" class="btn btn-default"><span class="ui-icon ui-icon-closethick"></span></button>')
|
||||
};
|
||||
|
||||
const action_type = () => {
|
||||
return $('<select id="type" class="form-control" style="width:auto">' +
|
||||
'<option value="二倍速">二倍速</option>' +
|
||||
'<option value="部署">部署</option>' +
|
||||
'<option value="技能">技能</option>' +
|
||||
'<option value="撤退">撤退</option>' +
|
||||
'<option value="子弹时间">子弹时间</option>' +
|
||||
'<option value="技能用法">技能用法</option>' +
|
||||
'</select>');
|
||||
};
|
||||
|
||||
const direction = () => {
|
||||
return $('<select id="direction" class="form-control" style="width:auto">' +
|
||||
'<option value=""></option>' +
|
||||
'<option value="上">上</option>' +
|
||||
'<option value="下">下</option>' +
|
||||
'<option value="左">左</option>' +
|
||||
'<option value="右">右</option>' +
|
||||
'</select>');
|
||||
}
|
||||
|
||||
const oper = (oper_data, delete_func) => {
|
||||
const tr = $('<tr>');
|
||||
// 干员名字
|
||||
const name_input = input_text()
|
||||
.val(oper_data.name ?? "")
|
||||
.change(function () { oper_data.name = $(this).val(); });
|
||||
tr.append($('<td>').append(name_input));
|
||||
// 技能
|
||||
const skill_input = input_text()
|
||||
.attr('type', 'number')
|
||||
.val(String(oper_data.skill ?? 0))
|
||||
.change(function () { oper_data.skill = Number($(this).val()); });
|
||||
tr.append($('<td>').append(skill_input));
|
||||
// 技能用法
|
||||
const skill_usage_input = skill_usage()
|
||||
.val(String(oper_data.skill_usage ?? 0))
|
||||
.change(function () { oper_data.skill_usage = Number($(this).val()); });
|
||||
tr.append($('<td>').append(skill_usage_input));
|
||||
// 删除
|
||||
tr.append($('<td>').append(delete_icon().click(delete_func)));
|
||||
return tr;
|
||||
}
|
||||
|
||||
const action = (action_data) => {
|
||||
const tr = $('<tr>');
|
||||
// 类别
|
||||
const type_input = action_type()
|
||||
.val(action_data.type ?? "")
|
||||
.change(function () { action_data.type = $(this).val(); });
|
||||
tr.append($('<td>').append(type_input));
|
||||
// 击杀数
|
||||
const kills_input = input_text()
|
||||
.attr('type', 'number')
|
||||
.val(String(action_data.kills ?? ""))
|
||||
.change(function () { action_data.kills = $(this).val !== "" ? Number($(this).val()) : undefined; });
|
||||
tr.append($('<td>').append(kills_input));
|
||||
// 费用变化
|
||||
const cost_changes = input_text()
|
||||
.attr('type', 'number')
|
||||
.val(String(action_data.cost_changes ?? ""))
|
||||
.change(function () { action_data.cost_changes = $(this).val !== "" ? Number($(this).val()) : undefined; });
|
||||
tr.append($('<td>').append(cost_changes));
|
||||
// 干员
|
||||
const name_input = input_text()
|
||||
.val(action_data.name ?? "")
|
||||
.change(function () { action_data.name = $(this).val(); });
|
||||
tr.append($('<td>').append(name_input));
|
||||
// x坐标
|
||||
const location_x_input = input_text()
|
||||
.attr('type', 'number')
|
||||
.val(String((action_data.location ?? [undefined, undefined])[0] ?? ""))
|
||||
.change(function () {
|
||||
action_data.location = action_data.location ?? [undefined, undefined];
|
||||
action_data.location[0] = $(this).val() !== "" ? Number($(this).val()) : undefined;
|
||||
});
|
||||
tr.append($('<td>').append(location_x_input));
|
||||
// y坐标
|
||||
const location_y_input = input_text()
|
||||
.attr('type', 'number')
|
||||
.val(String((action_data.location ?? [undefined, undefined])[1] ?? ""))
|
||||
.change(function () {
|
||||
action_data.location = action_data.location ?? [undefined, undefined];
|
||||
action_data.location[1] = $(this).val() !== "" ? Number($(this).val()) : undefined;
|
||||
});
|
||||
tr.append($('<td>').append(location_y_input));
|
||||
// 方向
|
||||
const direction_input = direction()
|
||||
.val(action_data.direction ?? "")
|
||||
.change(function () { action_data.direction = $(this).val(); });
|
||||
tr.append($('<td>').append(direction_input));
|
||||
// 前延迟
|
||||
const pre_delay_input = input_text()
|
||||
.attr('type', 'number')
|
||||
.val(String(action_data.pre_delay ?? ""))
|
||||
.change(function () { action_data.pre_delay = $(this).val() !== "" ? Number($(this).val()) : undefined; });
|
||||
tr.append($('<td>').append(pre_delay_input));
|
||||
// 后延迟
|
||||
const rear_delay_input = input_text()
|
||||
.attr('type', 'number')
|
||||
.val(String(action_data.rear_delay ?? ""))
|
||||
.change(function () { action_data.rear_delay = $(this).val() !== "" ? Number($(this).val()) : undefined; });
|
||||
tr.append($('<td>').append(rear_delay_input));
|
||||
// 文本
|
||||
const doc_input = input_text()
|
||||
.val(action_data.doc ?? "")
|
||||
.change(function () { action_data.doc = $(this).val(); });
|
||||
tr.append($('<td>').append(doc_input));
|
||||
// 颜色
|
||||
const doc_color_input = input_text()
|
||||
.val(action_data.doc_color ?? "")
|
||||
.change(function () { action_data.doc_color = $(this).val(); });
|
||||
tr.append($('<td>').append(doc_color_input));
|
||||
// 删除
|
||||
tr.append($('<td>').append(delete_icon().click(() => {
|
||||
data.actions = data.actions.filter(a => a !== action_data);
|
||||
loadData();
|
||||
})));
|
||||
return tr;
|
||||
};
|
||||
|
||||
const group = (group_data, delete_func) => {
|
||||
// 群组名
|
||||
const name_div_row = $('<div class="row">');
|
||||
const name_div_col = $('<div class="col-11">');
|
||||
const name_input = input_text()
|
||||
.val(group_data.name ?? "")
|
||||
.change(function () { group_data.name = $(this).val(); });
|
||||
name_div_col.append(name_input);
|
||||
const delete_col = $('<div class="col-1">');
|
||||
delete_col.append(delete_icon().click(delete_func));
|
||||
name_div_row.append(name_div_col).append(delete_col);
|
||||
|
||||
// 群组干员列表
|
||||
const table = $('<table class="table table-bordered table-condensed table-striped">' +
|
||||
'<thead>' +
|
||||
'<tr>' +
|
||||
'<th>干员名字</th>' +
|
||||
'<th>技能</th>' +
|
||||
'<th>技能用法</th>' +
|
||||
'<th>删除</th>' +
|
||||
'</tr>' +
|
||||
'</thead>' +
|
||||
'<tbody></tbody>' +
|
||||
'</table>');
|
||||
(group_data.opers ?? []).forEach(oper_data => table.find('tbody').append(oper(oper_data, () => {
|
||||
group_data.opers = group_data.opers.filter(o => o !== oper_data);
|
||||
loadData();
|
||||
})));
|
||||
|
||||
return $('<div>')
|
||||
.append(name_div_row)
|
||||
.append(table);
|
||||
};
|
||||
|
||||
// 加载数据
|
||||
const loadData = () => {
|
||||
$('#stage_name').text(data.stage_name ?? "");
|
||||
$('#details').text(data.details ?? "");
|
||||
$('#title').text(data.title ?? "");
|
||||
// TODO: requirements
|
||||
|
||||
$('#opers tbody').html('');
|
||||
(data.opers ?? []).forEach(oper_data => $('#opers tbody').append(oper(oper_data, () => {
|
||||
data.opers = data.opers.filter(o => o !== oper_data);
|
||||
loadData();
|
||||
})));
|
||||
|
||||
$('#groups').html('');
|
||||
(data.groups ?? []).forEach(group_data => $('#groups').append(group(group_data, () => {
|
||||
data.groups = data.groups.filter(g => g !== group_data);
|
||||
loadData();
|
||||
})));
|
||||
|
||||
$('#actions tbody').html('');
|
||||
(data.actions ?? []).forEach(action_data => $('#actions tbody').append(action(action_data)));
|
||||
};
|
||||
|
||||
$(document).ready(() => {
|
||||
// 绑定事件
|
||||
$('#stage_name').change(() => data.stage_name = $('#stage_name').val());
|
||||
$('#details').change(() => data.details = $('#details').val());
|
||||
$('#title').change(() => data.title = $('#title').val());
|
||||
|
||||
$('#opers_new').click(() => {
|
||||
data.opers = data.opers ?? [];
|
||||
data.opers.push({});
|
||||
loadData();
|
||||
});
|
||||
|
||||
$('#actions_new').click(() => {
|
||||
data.actions = data.actions ?? [];
|
||||
data.actions.push({});
|
||||
loadData();
|
||||
});
|
||||
|
||||
$('#groups_new').click(() => {
|
||||
data.groups = data.groups ?? [];
|
||||
data.groups.push({});
|
||||
loadData();
|
||||
})
|
||||
|
||||
$('#download_json').click(() => {
|
||||
const result = JSON.stringify(data, null, 4);
|
||||
const file = new Blob([result], { type: 'application/json' });
|
||||
const fileName = data.stage_name + '_' + data.opers.map(o => o.name).join('+') + '.json';
|
||||
const url = window.URL.createObjectURL(file);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.setAttribute('download', fileName);
|
||||
link.click();
|
||||
});
|
||||
|
||||
$('#upload_json').click(() => $('#json_file').click());
|
||||
|
||||
$('#json_file').change(function (e) {
|
||||
const files = e.target.files;
|
||||
const f = files[0];
|
||||
const reader = new FileReader();
|
||||
reader.onload = (() => (e) => {
|
||||
Object.assign(data, JSON.parse(e.target.result));
|
||||
loadData();
|
||||
})(f);
|
||||
reader.readAsText(f);
|
||||
});
|
||||
|
||||
loadData();
|
||||
});
|
||||
@@ -9,602 +9,77 @@
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
|
||||
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
|
||||
<script src="https://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
|
||||
<style>
|
||||
.actiontitle {
|
||||
font-size: 2rem;
|
||||
line-height: .75em;
|
||||
}
|
||||
|
||||
.tips {
|
||||
font-weight: lighter;
|
||||
font-size: 0.5em;
|
||||
color: gray;
|
||||
}
|
||||
</style>
|
||||
<script src="index.js" type="text/javascript"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="row" style="margin: 50 50;">
|
||||
<div class="col-12">
|
||||
<div class="row">
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<h2 for="stagename">
|
||||
关卡中文名
|
||||
</h2>
|
||||
<input type="text" class="form-control" id="stagename" />
|
||||
<span class="tips">小贴士:填完关卡名后开一局,会在目录下生成 map.png 地图坐标图片</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="form-group">
|
||||
<h2 for="description">
|
||||
文本
|
||||
</h2>
|
||||
<textarea type="text" class="form-control" id="description"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="form-group">
|
||||
<h2 for="title">
|
||||
标题
|
||||
</h2>
|
||||
<textarea type="text" class="form-control" id="title"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<button type="button" id="toJson" class="btn btn-primary" onclick="toJSON()">下载JSON</button>
|
||||
<input type="file" id="selectedFile" style="display: none;" name="files[]" size=1 />
|
||||
<button class="btn btn-primary" onclick="document.getElementById('selectedFile').click();">载入JSON</button>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<button id="download_json" class="btn btn-primary">下载JSON</button>
|
||||
<input type="file" id="json_file" style="display: none;" name="files[]" size=1 />
|
||||
<button id="upload_json" class="btn btn-primary">载入JSON</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h2>选择干员</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<table id="operatorTable" class="table table-bordered table-condensed table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>修改</th>
|
||||
<th>干员名字</th>
|
||||
<th>技能</th>
|
||||
<th>技能用法</th>
|
||||
<th>删除</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label for="operatorname">
|
||||
干员名字
|
||||
</label>
|
||||
<input type="text" class="form-control" id="operatorname" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="skill">
|
||||
技能
|
||||
</label>
|
||||
<input type="number" class="form-control" id="skill" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="skill_usage">
|
||||
技能用法
|
||||
</label>
|
||||
<select id="skill_usage" class="form-control">
|
||||
<option value=0>不自动使用</option>
|
||||
<option value=1>好了就用,有多少次用多少次</option>
|
||||
<option value=2>好了就用,仅使用一次</option>
|
||||
<!-- <option value=3>自动判断使用时机</option> -->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<button type="button" id="updateOperatorButton" class="btn btn-primary" onclick="update('operator');">
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<p class="actiontitle">战斗流程 <span class="tips">小贴士:可以拖拽更改顺序</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-11">
|
||||
<table id="actionTable" class="table table-bordered table-condensed table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>修改</th>
|
||||
<th>类别</th>
|
||||
<th>Kills</th>
|
||||
<th>费用变化</th>
|
||||
<th>干员名字</th>
|
||||
<th>x坐标</th>
|
||||
<th>y坐标</th>
|
||||
<th>方向</th>
|
||||
<th>前延迟</th>
|
||||
<th>后延迟</th>
|
||||
<th>文本</th>
|
||||
<th>颜色</th>
|
||||
<th>删除</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-11">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="form-group col-sm-2">
|
||||
<label for="type">
|
||||
类别
|
||||
</label>
|
||||
<select id="type" class="form-control">
|
||||
<option value="二倍速">二倍速</option>
|
||||
<option value="部署">部署</option>
|
||||
<option value="技能">技能</option>
|
||||
<option value="撤退">撤退</option>
|
||||
<option value="子弹时间">子弹时间</option>
|
||||
<option value="技能用法">技能用法</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-sm-2">
|
||||
<label for="kill">
|
||||
击杀数
|
||||
</label>
|
||||
<input type="number" class="form-control" id="kill" />
|
||||
</div>
|
||||
<div class="form-group col-sm-2">
|
||||
<label for="cost_changes">
|
||||
费用变化
|
||||
</label>
|
||||
<input type="number" class="form-control" id="cost_changes" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group col-sm-3">
|
||||
<label for="operator">
|
||||
干员名字
|
||||
</label>
|
||||
<input type="text" id="operator" class="form-control">
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-sm-2">
|
||||
<label for="xcoordinate">
|
||||
x坐标
|
||||
</label>
|
||||
<input type="number" class="form-control" id="xcoordinate" />
|
||||
</div>
|
||||
<div class="form-group col-sm-2">
|
||||
<label for="ycoordinate">
|
||||
y坐标
|
||||
</label>
|
||||
<input type="number" class="form-control" id="ycoordinate" />
|
||||
</div>
|
||||
|
||||
<div class="form-group col-sm-2">
|
||||
<label for="direction">
|
||||
方向
|
||||
</label>
|
||||
<select id="direction" class="form-control">
|
||||
<option value=""></option>
|
||||
<option value="上">上</option>
|
||||
<option value="下">下</option>
|
||||
<option value="左">左</option>
|
||||
<option value="右">右</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group col-sm-2">
|
||||
<label for="pre_delay">
|
||||
前延迟(毫秒)
|
||||
</label>
|
||||
<input type="number" class="form-control" id="pre_delay" />
|
||||
</div>
|
||||
<div class="form-group col-sm-2">
|
||||
<label for="rear_delay">
|
||||
后延迟(毫秒)
|
||||
</label>
|
||||
<input type="number" class="form-control" id="rear_delay" />
|
||||
</div>
|
||||
<div class="form-group col-sm-4">
|
||||
<label for="doc">
|
||||
文本
|
||||
</label>
|
||||
<input type="text" class="form-control" id="doc" />
|
||||
</div>
|
||||
<div class="form-group col-sm-2">
|
||||
<label for="doc_color">
|
||||
文本颜色
|
||||
</label>
|
||||
<input type="text" class="form-control" id="doc_color" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<button type="button" id="updateActionButton" class="btn btn-primary" onclick="update('action');">
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!--JSON-->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h1>关卡中文名</h1>
|
||||
<span class="tips">小贴士:填完关卡名后开一局,会在目录下生成 map.png 地图坐标图片</span>
|
||||
<input type="text" id="stage_name" class="form-control" placeholder="关卡中文名">
|
||||
</div><!--关卡中文名-->
|
||||
<div class="col-12">
|
||||
<h2>文本</h2>
|
||||
<textarea type="text" class="form-control" id="details" placeholder="文本"></textarea>
|
||||
</div><!--文本-->
|
||||
<div class="col-12">
|
||||
<h2>标题</h2>
|
||||
<input type="text" id="title" class="form-control" placeholder="标题">
|
||||
</div><!--标题-->
|
||||
<div class="col-12">
|
||||
<h2>群组</h2>
|
||||
<div id="groups"></div>
|
||||
<button id="groups_new" class="btn btn-primary">添加群组</button>
|
||||
</div><!--群组-->
|
||||
<div class="col-12">
|
||||
<h2>干员</h2>
|
||||
<table id="opers" class="table table-bordered table-condensed table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>干员名字</th>
|
||||
<th>技能</th>
|
||||
<th>技能用法</th>
|
||||
<th>删除</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
<button id="opers_new" class="btn btn-primary">新增</button>
|
||||
</div><!--干员-->
|
||||
<div class="col-12">
|
||||
<h2>战斗流程</h2>
|
||||
<table id="actions" class="table table-bordered table-condensed table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>类别</th>
|
||||
<th>击杀数</th>
|
||||
<th>费用变化</th>
|
||||
<th>干员名字</th>
|
||||
<th>x坐标</th>
|
||||
<th>y坐标</th>
|
||||
<th>方向</th>
|
||||
<th>前延迟</th>
|
||||
<th>后延迟</th>
|
||||
<th>文本</th>
|
||||
<th>文本颜色</th>
|
||||
<th>删除</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
<button id="actions_new" class="btn btn-primary">新增</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var operatorList = [];
|
||||
var editRow = null;
|
||||
var editRowName = null;
|
||||
function load() {
|
||||
return charName.map(x => ({ "label": x, value: x }));
|
||||
}
|
||||
|
||||
$(function () {
|
||||
var availableTags = load();
|
||||
$("#operatorname").autocomplete({
|
||||
source: availableTags
|
||||
});
|
||||
});
|
||||
|
||||
function display(ctl, type) {
|
||||
editRow = $(ctl).parents("tr");
|
||||
var cols = editRow.children("td");
|
||||
if (type === 'operator') {
|
||||
$("#operatorname").val($(cols[1]).text());
|
||||
$("#skill").val($(cols[2]).text());
|
||||
$("#skill_usage").val($(cols[3]).text());
|
||||
$("#updateOperatorButton").text("Update");
|
||||
editRowName = $(ctl).context.name.substring(4)
|
||||
document.getElementById("operatorname").scrollIntoView();
|
||||
}
|
||||
else if (type === 'action') {
|
||||
$("#type").val($(cols[1]).text())
|
||||
$("#kill").val($(cols[2]).text())
|
||||
$("#cost_changes").val($(cols[3]).text())
|
||||
$("#operator").val($(cols[4]).text())
|
||||
$("#xcoordinate").val($(cols[5]).text())
|
||||
$("#ycoordinate").val($(cols[6]).text())
|
||||
$("#direction").val($(cols[7]).text())
|
||||
$("#pre_delay").val($(cols[8]).text())
|
||||
$("#rear_delay").val($(cols[9]).text())
|
||||
$("#doc").val($(cols[10]).text())
|
||||
$("#doc_color").val($(cols[11]).text())
|
||||
$("#updateActionButton").text("Update");
|
||||
document.getElementById("type").scrollIntoView();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function update(type) {
|
||||
if (type === 'operator') {
|
||||
if ($("#updateOperatorButton").text() == "Update") {
|
||||
updateInTable(type);
|
||||
}
|
||||
else {
|
||||
addToTable(type);
|
||||
}
|
||||
$("#operatorname").focus();
|
||||
}
|
||||
else if (type === 'action') {
|
||||
if ($("#updateActionButton").text() == "Update") {
|
||||
updateInTable(type);
|
||||
}
|
||||
else {
|
||||
addToTable(type);
|
||||
}
|
||||
$("#type").focus();
|
||||
}
|
||||
formClear(type);
|
||||
}
|
||||
function addToTable(type, data = null) {
|
||||
if (type === 'operator') {
|
||||
if ($("#operatorTable tbody").length == 0) {
|
||||
$("#operatorTable").append("<tbody></tbody>");
|
||||
}
|
||||
var result = buildTableRow(type, data);
|
||||
if (result && result.html) $("#operatorTable tbody").append(result.html);
|
||||
if (result && result.operatorName) updateOperatorList("add", result.operatorName);
|
||||
}
|
||||
else if (type === 'action') {
|
||||
if ($("#actionTable tbody").length == 0) $("#actionTable").append("<tbody></tbody>");
|
||||
let indexVal = parseInt($("#insertindex").val())
|
||||
if (indexVal >= 0 && indexVal <= $("#actionTable tbody tr").length) {
|
||||
var newRow = $(buildTableRow(type, data));
|
||||
newRow.insertBefore($('#actionTable tbody tr:nth(' + indexVal + ')'));
|
||||
}
|
||||
else $("#actionTable tbody").append(buildTableRow(type, data))
|
||||
}
|
||||
}
|
||||
|
||||
// Update operator in <table>
|
||||
function updateInTable(type) {
|
||||
var result = buildTableRow(type);
|
||||
if (result.operatorName) {
|
||||
updateOperatorList("update", editRowName, result.operatorName)
|
||||
$(editRow).after(result.html);
|
||||
}
|
||||
else {
|
||||
$(editRow).after(result);
|
||||
}
|
||||
$(editRow).remove();
|
||||
formClear(type);
|
||||
if (type == "operator") $("#updateOperatorButton").text("Add");
|
||||
else if (type == "action") $("#updateActionButton").text("Add");
|
||||
}
|
||||
|
||||
function buildTableRow(type, data = null) {
|
||||
let datastring = '';
|
||||
if (type === 'operator') {
|
||||
if (($("#operatorname").val() && $("#skill").val()) || data) {
|
||||
let obj = { name: "", skill: "", skill_usage: "" };
|
||||
if (data == null) {
|
||||
obj.name = $("#operatorname").val();
|
||||
obj.skill = $("#skill").val();
|
||||
obj.skill_usage = $("#skill_usage").val();
|
||||
}
|
||||
else {
|
||||
obj.name = data.name;
|
||||
obj.skill = data.skill;
|
||||
obj.skill_usage = (data.skill_usage ? data.skill_usage : 0);
|
||||
}
|
||||
datastring = "<td>" + obj.name + "</td>" +
|
||||
"<td>" + obj.skill + "</td>" +
|
||||
"<td>" + (obj.skill_usage ? obj.skill_usage : 0) + "</td>";
|
||||
var ret =
|
||||
"<tr draggable='true' ondragstart='start()' ondragover='dragover()'>" +
|
||||
"<td>" +
|
||||
"<button type='button' name =\"edit" + (data && data.name ? data.name : $("#operatorname").val()) + "\"" +
|
||||
"onclick='display(this,\"operator\");' " +
|
||||
"class='btn btn-default'>" +
|
||||
"<span class='ui-icon ui-icon-pencil' />" +
|
||||
"</button>" +
|
||||
"</td>" + datastring +
|
||||
"<td>" +
|
||||
"<button type='button' name =\"" + (data && data.name ? data.name : $("#operatorname").val()) + "\"" +
|
||||
"onclick='itemDelete(this);' " +
|
||||
"class='btn btn-default'>" +
|
||||
"<span class='ui-icon ui-icon-closethick' />" +
|
||||
"</button>" +
|
||||
"</td>" +
|
||||
"</tr>"
|
||||
return { html: ret, operatorName: $("#operatorname").val() };
|
||||
}
|
||||
else return;
|
||||
}
|
||||
else if (type === 'action') {
|
||||
if (data) {
|
||||
datastring = "<td>" + (data.type ? data.type : "") + "</td>" +
|
||||
"<td>" + (data.kills ? data.kills : "") + "</td>" +
|
||||
"<td>" + (data.cost_changes ? data.cost_changes : "") + "</td>" +
|
||||
"<td>" + (data.name ? data.name : "") + "</td>" +
|
||||
"<td>" + (data.location ? data.location[0] : "") + "</td>" +
|
||||
"<td>" + (data.location ? data.location[1] : "") + "</td>" +
|
||||
"<td>" + (data.direction ? data.direction : "") + "</td>" +
|
||||
"<td>" + (data.pre_delay ? data.pre_delay : "") + "</td>" +
|
||||
"<td>" + (data.rear_delay ? data.rear_delay : "") + "</td>" +
|
||||
"<td>" + (data.doc ? data.doc : "") + "</td>" +
|
||||
"<td>" + (data.doc_color ? data.doc_color : "") + "</td>";
|
||||
}
|
||||
else {
|
||||
datastring = "<td>" + $("#type").val() + "</td>" +
|
||||
"<td>" + $("#kill").val() + "</td>" +
|
||||
"<td>" + $("#cost_changes").val() + "</td>" +
|
||||
"<td>" + $("#operator").val() + "</td>" +
|
||||
"<td>" + $("#xcoordinate").val() + "</td>" +
|
||||
"<td>" + $("#ycoordinate").val() + "</td>" +
|
||||
"<td>" + $("#direction").val() + "</td>" +
|
||||
"<td>" + $("#pre_delay").val() + "</td>" +
|
||||
"<td>" + $("#rear_delay").val() + "</td>" +
|
||||
"<td>" + $("#doc").val() + "</td>" +
|
||||
"<td>" + $("#doc_color").val() + "</td>";
|
||||
}
|
||||
var ret =
|
||||
"<tr draggable='true' ondragstart='start()' ondragover='dragover()'>" +
|
||||
"<td>" +
|
||||
"<button type='button' " +
|
||||
"onclick='display(this,\"action\");' " +
|
||||
"class='btn btn-default'>" +
|
||||
"<span class='ui-icon ui-icon-pencil' />" +
|
||||
"</button>" +
|
||||
"</td>" + datastring +
|
||||
"<td>" +
|
||||
"<button type='button' " +
|
||||
"onclick='itemDelete(this);' " +
|
||||
"class='btn btn-default'>" +
|
||||
"<span class='ui-icon ui-icon-closethick' />" +
|
||||
"</button>" +
|
||||
"</td>" +
|
||||
"</tr>"
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
function itemDelete(ctl) {
|
||||
updateOperatorList("delete", $(ctl).context.name)
|
||||
$(ctl).parents("tr").remove();
|
||||
}
|
||||
function updateOperatorList(action, oldname, newName = "") {
|
||||
var selectobject = document.getElementById("operator");
|
||||
if (action == 'clean') {
|
||||
operatorList = [];
|
||||
operatorList.push({ label: "", value: "" })
|
||||
}
|
||||
else if (action == "add" && operatorList.findIndex(o => o.value == oldname) == -1) {
|
||||
operatorList.push({ value: oldname, label: oldname });
|
||||
}
|
||||
else {
|
||||
var index = operatorList.findIndex(x => x.value == oldname);
|
||||
if (index > -1) {
|
||||
operatorList.slice(index);
|
||||
if (action == "update") {
|
||||
operatorList[index].label = operatorList[index].value = newName;
|
||||
// 把现有table干员名字替换
|
||||
var actionTable = $("#actionTable tbody");
|
||||
actionTable.find('tr').each(function (i, el) {
|
||||
var actionData = prepareActionData($(this))
|
||||
if (actionData.name == oldname) {
|
||||
actionData.name = newName;
|
||||
$(this).replaceWith(buildTableRow("action", actionData))
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
operatorList.slice(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
$("#operator").autocomplete({
|
||||
source: operatorList
|
||||
});
|
||||
}
|
||||
function formClear(type) {
|
||||
if (type == "operator") {
|
||||
$("#operatorname").val("");
|
||||
$("#skill").val("");
|
||||
}
|
||||
else if (type == "action") {
|
||||
$("#type").val("");
|
||||
$("#kill").val("");
|
||||
$("#cost_changes").val("");
|
||||
$("#operator").val("");
|
||||
$("#xcoordinate").val("");
|
||||
$("#ycoordinate").val("");
|
||||
$("#direction").val("");
|
||||
$("#pre_delay").val("");
|
||||
$("#rear_delay").val("");
|
||||
$("#doc").val("");
|
||||
$("#doc_color").val("");
|
||||
}
|
||||
}
|
||||
function prepareActionData(tr) {
|
||||
var $tds = tr.find('td'),
|
||||
type = $tds.eq(1).text(),
|
||||
kills = $tds.eq(2).text(),
|
||||
cost_changes = $tds.eq(3).text(),
|
||||
name = $tds.eq(4).text(),
|
||||
xcoordinate = $tds.eq(5).text(),
|
||||
ycoordinate = $tds.eq(6).text(),
|
||||
direction = $tds.eq(7).text(),
|
||||
pre_delay = $tds.eq(8).text(),
|
||||
rear_delay = $tds.eq(9).text(),
|
||||
doc = $tds.eq(10).text(),
|
||||
doc_color = $tds.eq(11).text();
|
||||
kills = parseInt(kills)
|
||||
cost_changes = parseInt(cost_changes)
|
||||
pre_delay = parseInt(pre_delay)
|
||||
rear_delay = parseInt(rear_delay)
|
||||
xcoordinate = parseInt(xcoordinate)
|
||||
ycoordinate = parseInt(ycoordinate)
|
||||
let location = [xcoordinate, ycoordinate]
|
||||
var actionData = { type, kills, cost_changes, name, location, direction, pre_delay, rear_delay, doc, doc_color };
|
||||
if (isNaN(actionData.kills)) delete actionData.kills;
|
||||
if (isNaN(actionData.pre_delay)) delete actionData.pre_delay;
|
||||
if (isNaN(actionData.rear_delay)) delete actionData.rear_delay;
|
||||
if (actionData.direction === "") delete actionData.direction;
|
||||
if (isNaN(actionData.location[0]) || isNaN(actionData.location[1])) delete actionData.location;
|
||||
return actionData;
|
||||
}
|
||||
function toJSON() {
|
||||
var operatorTable = $("#operatorTable tbody");
|
||||
var actionTable = $("#actionTable tbody");
|
||||
var opers = [];
|
||||
var actions = [];
|
||||
operatorTable.find('tr').each(function (i, el) {
|
||||
var $tds = $(this).find('td'),
|
||||
name = $tds.eq(1).text(),
|
||||
skill = $tds.eq(2).text(),
|
||||
skill_usage = $tds.eq(3).text();
|
||||
if (name && skill && skill_usage) {
|
||||
skill_usage = parseInt(skill_usage)
|
||||
skill = parseInt(skill)
|
||||
opers.push({ name, skill, skill_usage });
|
||||
}
|
||||
});
|
||||
actionTable.find('tr').each(function (i, el) {
|
||||
var actionData = prepareActionData($(this));
|
||||
actions.push(actionData);
|
||||
});
|
||||
var stage_name = document.getElementById("stagename").value;
|
||||
var title = document.getElementById("title").value;
|
||||
var details = document.getElementById("description").value;
|
||||
var doc = { title, details };
|
||||
var obj = { "minimum_required": "v4.0", doc, stage_name, opers, actions };
|
||||
var jsonString = JSON.stringify(obj);
|
||||
var jsonPretty = JSON.stringify(JSON.parse(jsonString), null, 4);
|
||||
var file = new Blob([jsonPretty], { type: 'text/plain' });
|
||||
var fileName = obj.stage_name + '_' + opers.map(o => o.name).join('+') + '.json';
|
||||
const url = window.URL.createObjectURL(file);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.setAttribute('download', fileName);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
}
|
||||
function handleFileSelect(evt) {
|
||||
let files = evt.target.files;
|
||||
let f = files[0];
|
||||
let reader = new FileReader();
|
||||
reader.onload = (function (theFile) {
|
||||
return function (e) {
|
||||
let data = JSON.parse(e.target.result);
|
||||
if (!data) alert("读取文件错误")
|
||||
else {
|
||||
document.getElementById("stagename").value = data.stage_name ? data.stage_name : "";
|
||||
document.getElementById("title").value = data.doc.title ? data.doc.title : "";
|
||||
document.getElementById("description").value = data.doc.details ? data.doc.details : "";
|
||||
$("#operatorTable tbody tr").remove();
|
||||
updateOperatorList("clean", "")
|
||||
if (data.opers) data.opers.forEach(x => { addToTable("operator", x); updateOperatorList("add", x.name); });
|
||||
$("#insertindex").val(-1)
|
||||
$("#actionTable tbody tr").remove();
|
||||
if (data.actions) data.actions.forEach(x => { addToTable("action", x) });
|
||||
}
|
||||
};
|
||||
})(f);
|
||||
reader.readAsText(f);
|
||||
}
|
||||
var row;
|
||||
|
||||
function start() {
|
||||
row = event.target;
|
||||
}
|
||||
function dragover() {
|
||||
var e = event;
|
||||
e.preventDefault();
|
||||
|
||||
let children = Array.from(e.target.parentNode.parentNode.children);
|
||||
|
||||
if (children.indexOf(e.target.parentNode) > children.indexOf(row))
|
||||
e.target.parentNode.after(row);
|
||||
else
|
||||
e.target.parentNode.before(row);
|
||||
}
|
||||
document.getElementById('selectedFile').addEventListener('change', handleFileSelect, false);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
Reference in New Issue
Block a user