Merge remote-tracking branch 'origin/dev' into dev_

# Conflicts:
#	src/MeoAsstGui/ViewModels/VersionUpdateViewModel.cs
This commit is contained in:
lhhxxxxx
2022-07-30 05:33:11 +08:00
24 changed files with 161 additions and 78 deletions

View File

@@ -1,12 +1,5 @@
- New support for interface English, 日本語, 한국어, 繁體中文 and 牛牛喝酒 translation. It was a difficult task, thanks to all collaborators: @ABA2396 @ChingCdesu @LYZhelloworld @Tastror @cloakWatcher @lsequeiraa @MistEO
- 新增支持 肉鸽 开局自定义分队、自定义职业组、自定义干员 @MistEO @ABA2396
- 新增支持 Rust http 接口 @yuanyan3060
- 优化 肉鸽 干员部署及朝向、商店购买逻辑、干员招募逻辑 @MistEO
- 重构并优化 公招 功能 @horror-proton
- 优化 刷理智 `上次作战/当前作战` 功能,合并为一个选项 @MistEO
- 优化 界面布局 @ABA2396
- 优化项目 CI/CD 工程 @LiamSho @zzyyyl
- 修复 美服肉鸽 一处卡住的问题 @AkarinVS
- 修复 刷理智 偶现掉落识别错误 @MistEO @ABA2396
- 修复 基建 会客室送线索时卡住问题 @MistEO
- 修复 Linux 编译错误 @zhangchi0104
- 优化 肉鸽 干员部署策略、招募策略、商店购物策略 @MistEO @ABA2396
- 修复 肉鸽 商店卡住的问题、自定义干员不选的问题 @MistEO
- 修复公招的一些小问题 @horror-proton
- 修复一些界面 bug 和布局问题 @ABA2396 @zzyyyl @lhhxxxxx
- 新增 日文 文档翻译 @LYZhelloworld

View File

@@ -16,8 +16,8 @@
"stage_name": "压轴登场",
"replacement_home": [
[
2,
2
3,
3
]
]
},

View File

@@ -82,7 +82,7 @@
"name": "棘刺",
"level": 6,
"skill": 3,
"alternate_skill": 2
"alternate_skill": 1
},
{
"name": "柏喙",
@@ -285,12 +285,12 @@
"skill": 1
},
{
"name": "芙蓉",
"name": "安赛尔",
"level": 3,
"skill": 1
},
{
"name": "安赛尔",
"name": "芙蓉",
"level": 3,
"skill": 1
}

View File

@@ -6932,7 +6932,9 @@
216
],
"next": [
"Roguelike1StageTraderInvestSystemEnter"
"Roguelike1StageTraderInvestSystemEnter",
"Roguelike1TraderRandomShopping",
"Roguelike1StageTraderLeave"
]
},
"Roguelike1StageTraderInvestSystemEnter": {

View File

@@ -126,6 +126,7 @@ namespace asst
Rect rect;
int elite = 0;
int level = 0;
bool required = false;
};
using BattleAttackRange = std::vector<Point>;

View File

@@ -313,7 +313,13 @@ asst::AutoRecruitTask::calc_task_result_type asst::AutoRecruitTask::recruit_calc
// assuming timer would be set to 09:00:00
for (RecruitCombs& rc : result_vec) {
rc.min_level = (std::max)(rc.min_level, 3);
if (rc.min_level < 3) {
// find another min level backwards (assuming operator list reversely sorted by level)
auto sec = std::find_if(
rc.opers.crbegin(), rc.opers.crend(),
[](const RecruitOperInfo& op) -> bool { return op.level >= 3; });
if (sec != rc.opers.crend()) { rc.min_level = sec->level; }
}
}
std::sort(

View File

@@ -111,6 +111,12 @@ void asst::BattleImageAnalyzer::clear() noexcept
m_cost = 0;
}
void asst::BattleImageAnalyzer::sort_opers_by_cost()
{
// 本来游戏就是按费用排的,这里倒序一下就行了
std::reverse(m_opers.begin(), m_opers.end());
}
bool asst::BattleImageAnalyzer::opers_analyze()
{
LogTraceFunction;

View File

@@ -39,6 +39,7 @@ namespace asst
[[nodiscard]] int get_cost() const noexcept;
void clear() noexcept;
void sort_opers_by_cost(); // 高费在前,费用降序
protected:
bool opers_analyze(); // 识别干员

View File

@@ -208,13 +208,23 @@ void asst::OcrImageAnalyzer::sort_result_by_required()
return;
}
std::unordered_map<std::string, size_t> m_req_cache;
std::unordered_map<std::string, size_t> req_cache;
for (size_t i = 0; i != m_required.size(); ++i) {
m_req_cache.emplace(m_required.at(i), i + 1);
req_cache.emplace(m_required.at(i), i + 1);
}
std::sort(get_result().begin(), get_result().end(),
[&m_req_cache](const auto& lhs, const auto& rhs) -> bool {
return m_req_cache[lhs.text] < m_req_cache[rhs.text];
auto& result = get_result();
// 不在 m_required 中的将被排在最后
std::sort(result.begin(), result.end(),
[&req_cache](const auto& lhs, const auto& rhs) -> bool {
size_t lvalue = req_cache[lhs.text];
size_t rvalue = req_cache[rhs.text];
if (lvalue == 0) {
return false;
}
else if (rvalue == 0) {
return true;
}
return lvalue < rvalue;
});
}

View File

@@ -214,10 +214,30 @@ bool asst::RoguelikeBattleTaskPlugin::auto_battle()
return false;
}
battle_analyzer.sort_opers_by_cost();
const auto& opers = battle_analyzer.get_opers();
if (opers.empty()) {
return true;
}
// 如果已经放了一些人了,就不要再有费就下了
if (m_used_tiles.size() >= std::max(m_homes.size(), 2ULL)) {
size_t available_count = 0;
size_t not_cooling_count = 0;
for (const auto& oper : opers) {
if (oper.available) {
available_count += 1;
}
if (!oper.cooling) {
not_cooling_count += 1;
}
}
// 超过一半的人费用都没好,那就不下人
if (available_count <= not_cooling_count / 2) {
Log.trace("already used", m_used_tiles.size(), ", now_total", opers.size(),
", avaliable", available_count, ", not_cooling", not_cooling_count);
return true;
}
}
static const std::array<BattleRole, 9> RoleOrder = {
BattleRole::Warrior,
@@ -725,7 +745,7 @@ std::pair<asst::Point, int> asst::RoguelikeBattleTaskPlugin::calc_best_direction
{ TileKey::Wall, 500 },
{ TileKey::Road, 1000 },
{ TileKey::Home, 500 },
{ TileKey::EnemyHome, 1200 },
{ TileKey::EnemyHome, 1000 },
{ TileKey::Floor, 1000 },
{ TileKey::Hole, 0 },
{ TileKey::Telin, 700 },

View File

@@ -107,13 +107,13 @@ bool asst::RoguelikeCustomStartTaskPlugin::hijack_core_char()
const std::string& char_name = m_customs[RoguelikeCustomType::CoreChar];
static const std::unordered_map<BattleRole, std::string> RoleOcrNameMap = {
{ BattleRole::Caster, "" },
{ BattleRole::Caster, "" },
{ BattleRole::Medic, "医疗" },
{ BattleRole::Pioneer, "先锋" },
{ BattleRole::Sniper, "狙击" },
{ BattleRole::Special, "特种" },
{ BattleRole::Support, "辅助" },
{ BattleRole::Tank, "坦克" },
{ BattleRole::Tank, "重装" },
{ BattleRole::Warrior, "近卫" }
};
const auto& role = Resrc.battle_data().get_role(char_name);

View File

@@ -15,12 +15,12 @@ bool asst::RoguelikeRecruitImageAnalyzer::analyze()
analyzer.set_replace(
Task.get<OcrTaskInfo>("CharsNameOcrReplace")->replace_map);
analyzer.set_required(Resrc.roguelike_recruit().get_oper_order());
if (!analyzer.analyze()) {
return false;
}
const auto& order = Resrc.roguelike_recruit().get_oper_order();
analyzer.set_required(order);
analyzer.sort_result_by_required();
for (const auto& [_, rect, name] : analyzer.get_result()) {
@@ -32,10 +32,23 @@ bool asst::RoguelikeRecruitImageAnalyzer::analyze()
info.name = name;
info.elite = elite;
info.level = level;
info.required = std::find(order.cbegin(), order.cend(), name) != order.cend();
Log.info(__FUNCTION__, name, elite, level, rect.to_string());
m_result.emplace_back(std::move(info));
}
auto first_un_req = std::find_if(m_result.begin(), m_result.end(),
[&](const auto& info) -> bool {
return info.required == false;
});
std::sort(first_un_req, m_result.end(),
[&](const auto& lhs, const auto& rhs) -> bool {
if (lhs.elite == rhs.elite) {
return lhs.level > rhs.level;
}
return lhs.elite > rhs.elite;
});
return !m_result.empty();
}

View File

@@ -57,6 +57,9 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
if (!recruited) {
for (const auto& info : oper_list) {
if (!info.required) {
continue;
}
// 拿个精一 50 以上的
if (info.elite == 0 ||
(info.elite == 1 && info.level < 50)) {
@@ -71,6 +74,9 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
Log.info("All are lower");
// 随便招个精一的
for (const auto& info : oper_list) {
if (!info.required) {
continue;
}
if (info.elite == 0) {
continue;
}

View File

@@ -46,7 +46,8 @@ bool asst::RoguelikeShoppingTaskPlugin::_run()
}
auto find_it = std::find_if(result.cbegin(), result.cend(),
[&](const TextRect& tr) -> bool {
return tr.text == goods.name;
return tr.text.find(goods.name) != std::string::npos ||
goods.name.find(tr.text) != std::string::npos;
});
if (find_it == result.cend()) {
continue;

View File

@@ -116,6 +116,7 @@
<system:String x:Key="HideUnavailableStage">Hide today not open level</system:String>
<system:String x:Key="MainViewButtonFeature">Variable function button</system:String>
<system:String x:Key="ADBTip">Tip: It will be automatically detected here. If there is an error, you can try to modify it again, otherwise you do not need to fill in it yourself</system:String>
<system:String x:Key="ADBPath">ADB path (rel/abs)</system:String>
<system:String x:Key="ConnectionAddress">Connection address</system:String>
<system:String x:Key="ConnectionPreset">Connection Preset</system:String>

View File

@@ -116,6 +116,7 @@
<system:String x:Key="HideUnavailableStage">当日は開放されないステージを隠します</system:String>
<system:String x:Key="MainViewButtonFeature">メイン画面左側のボタン</system:String>
<system:String x:Key="ADBTip">ヒント:ここで自動的に検出されます。エラーが発生した場合は、もう一度変更を試みることができます。それ以外の場合は、自分で入力する必要はありません。</system:String>
<system:String x:Key="ADBPath">adbパス (相対/絶対)</system:String>
<system:String x:Key="ConnectionAddress">接続先アドレス</system:String>
<system:String x:Key="ConnectionPreset">接続構成</system:String>

View File

@@ -116,6 +116,7 @@
<system:String x:Key="HideUnavailableStage">당일 스테이지만 표시</system:String>
<system:String x:Key="MainViewButtonFeature">메인 화면 왼쪽의 버튼</system:String>
<system:String x:Key="ADBTip">팁: 여기에서 자동으로 감지됩니다. 오류가 있는 경우 다시 수정을 시도할 수 있습니다. 그렇지 않으면 직접 입력할 필요가 없습니다.</system:String>
<system:String x:Key="ADBPath">adb 경로 (상대/절대)</system:String>
<system:String x:Key="ConnectionAddress">연결 주소</system:String>
<system:String x:Key="ConnectionPreset">연결 사전 설정</system:String>

View File

@@ -116,6 +116,7 @@
<system:String x:Key="HideUnavailableStage">🍻🍸🍷🍺🍺🍸🍸</system:String>
<system:String x:Key="MainViewButtonFeature">🍷🍺🍻🍺🍸🍺</system:String>
<system:String x:Key="ADBTip">🍸🍻🍻🍸🍸🍻💃🍻🍸🍷🍸🍷🍷💃🍸🍻💃🍷🍺🍺🍸🍷🕺🍷🍷🍷🕺🍻🍷🍸💃🕺🍸🍷🍺</system:String>
<system:String x:Key="ADBPath">🕺💃🍸🍷🍸💃🍸</system:String>
<system:String x:Key="ConnectionAddress">🍻🕺🍻</system:String>
<system:String x:Key="ConnectionPreset">🍸🍷🍻</system:String>

View File

@@ -116,6 +116,7 @@
<system:String x:Key="HideUnavailableStage">关卡选择隐藏当日不开放关卡</system:String>
<system:String x:Key="MainViewButtonFeature">主界面可选择按钮功能</system:String>
<system:String x:Key="ADBTip">小提示:这里会自动检测的,若出现问题可再尝试修改,否则不需要自行填写</system:String>
<system:String x:Key="ADBPath">adb 路径 (相对/绝对)</system:String>
<system:String x:Key="ConnectionAddress">连接地址</system:String>
<system:String x:Key="ConnectionPreset">连接配置</system:String>

View File

@@ -116,6 +116,7 @@
<system:String x:Key="HideUnavailableStage">關卡選擇隱藏當日不開放關卡</system:String>
<system:String x:Key="MainViewButtonFeature">主介面可選擇按鈕功能</system:String>
<system:String x:Key="ADBTip">小提示:這裡會自動檢測的,若出現問題可再嘗試修改,否則不需要自行填寫</system:String>
<system:String x:Key="ADBPath">adb 路徑 (相對/絕對)</system:String>
<system:String x:Key="ConnectionAddress">連接地址</system:String>
<system:String x:Key="ConnectionPreset">連接配置</system:String>

View File

@@ -21,9 +21,18 @@
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Grid.ColumnSpan="3"
Margin="10"
Block.TextAlignment="Center"
Style="{StaticResource TextBlockDefault}"
Text="{DynamicResource ADBTip}"
TextWrapping="Wrap" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="10"
Block.TextAlignment="Center"
@@ -31,14 +40,14 @@
Text="{DynamicResource ADBPath}"
TextWrapping="Wrap" />
<TextBox
Grid.Row="0"
Grid.Row="1"
Grid.Column="1"
Width="250"
Height="30"
Margin="10"
Text="{Binding AdbPath}" />
<Button
Grid.Row="0"
Grid.Row="1"
Grid.Column="3"
Height="30"
Margin="10"
@@ -46,7 +55,7 @@
Content="{DynamicResource Select}" />
<TextBlock
Grid.Row="1"
Grid.Row="2"
Grid.Column="0"
Margin="10"
Block.TextAlignment="Center"
@@ -54,7 +63,7 @@
Text="{DynamicResource ConnectionAddress}"
TextWrapping="Wrap" />
<TextBox
Grid.Row="1"
Grid.Row="2"
Grid.Column="1"
Width="250"
Height="30"
@@ -62,7 +71,7 @@
Text="{Binding ConnectAddress}" />
<TextBlock
Grid.Row="2"
Grid.Row="3"
Grid.Column="0"
Margin="10"
Block.TextAlignment="Center"
@@ -70,7 +79,7 @@
Text="{DynamicResource ConnectionPreset}"
TextWrapping="Wrap" />
<ComboBox
Grid.Row="2"
Grid.Row="3"
Grid.Column="1"
Width="250"
Height="30"

View File

@@ -180,7 +180,7 @@ namespace MeoAsstGui
{
new CombData { Display = "默认职业组", Value = string.Empty },
new CombData { Display = "先手必胜(先锋、狙击、特种)", Value = "先手必胜" },
new CombData { Display = "稳扎稳打(重装、术、狙击)", Value = "稳扎稳打" },
new CombData { Display = "稳扎稳打(重装、术、狙击)", Value = "稳扎稳打" },
new CombData { Display = "取长补短(近卫、辅助、医疗)", Value = "取长补短" },
new CombData { Display = "随心所欲(三张随机)", Value = "随心所欲" },
};

View File

@@ -20,9 +20,6 @@ using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Documents;
using Markdig;
using Neo.Markdig.Xaml;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Stylet;
@@ -44,6 +41,15 @@ namespace MeoAsstGui
[DllImport("MeoAssistant.dll")]
private static extern IntPtr AsstGetVersion();
private static string AddContributorLink(string text)
{
// "@ " -> "@ "
// "`@`" -> "`@`"
// "@MistEO" -> "[@MistEO](https://github.com/MistEO)"
// "[@MistEO]" -> "[@MistEO]"
return Regex.Replace(text, @"([^\[`]|^)@([^\s]+)", "$1[@$2](https://github.com/$2)");
}
private readonly string _curVersion = Marshal.PtrToStringAnsi(AsstGetVersion());
private string _latestVersion;
@@ -65,13 +71,20 @@ namespace MeoAsstGui
private string _updateInfo = ViewStatusStorage.Get("VersionUpdate.body", string.Empty);
private static readonly MarkdownPipeline s_markdownPipeline = new MarkdownPipelineBuilder().UseXamlSupportedExtensions().Build();
// private static readonly MarkdownPipeline s_markdownPipeline = new MarkdownPipelineBuilder().UseXamlSupportedExtensions().Build();
public string UpdateInfo
{
get
{
return _updateInfo;
try
{
return AddContributorLink(_updateInfo);
}
catch
{
return _updateInfo;
}
}
set
@@ -96,29 +109,29 @@ namespace MeoAsstGui
}
}
public FlowDocument UpdateInfoDocument
{
get
{
try
{
return MarkdownXaml.ToFlowDocument(UpdateInfo, s_markdownPipeline);
}
catch (Exception)
{
// 不知道为什么有一部分用户的电脑上,用 MarkdownXaml 解析直接就会 crash
// 换另一个库再试一遍
try
{
return new MdXaml.Markdown().Transform(UpdateInfo);
}
catch (Exception)
{
return new FlowDocument();
}
}
}
}
// public FlowDocument UpdateInfoDocument
// {
// get
// {
// try
// {
// return MarkdownXaml.ToFlowDocument(UpdateInfo, s_markdownPipeline);
// }
// catch (Exception)
// {
// // 不知道为什么有一部分用户的电脑上,用 MarkdownXaml 解析直接就会 crash
// // 换另一个库再试一遍
// try
// {
// return new MdXaml.Markdown().Transform(UpdateInfo);
// }
// catch (Exception)
// {
// return new FlowDocument();
// }
// }
// }
// }
public bool IsFirstBootAfterUpdate
{

View File

@@ -4,8 +4,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:md="clr-namespace:Neo.Markdig.Xaml;assembly=Neo.Markdig.Xaml"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:mdxam="clr-namespace:MdXaml;assembly=MdXaml"
xmlns:vm="clr-namespace:MeoAsstGui;assembly=MeoAsstGui"
Title="版本已更新"
Width="600"
@@ -37,20 +37,16 @@
TextWrapping="Wrap" />
</StackPanel>
<FlowDocumentScrollViewer
x:Name="flowDocumentScrollViewer"
<mdxam:MarkdownScrollViewer
x:Name="UpdateInfoMarkdownDocument"
Grid.Row="1"
Margin="16,0"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Document="{Binding UpdateInfoDocument}"
MaxZoom="100"
MinZoom="100"
VerticalScrollBarVisibility="Auto">
<FlowDocumentScrollViewer.CommandBindings>
<CommandBinding Command="{x:Static md:MarkdownXaml.Hyperlink}" Executed="{s:Action OpenHyperlink}" />
</FlowDocumentScrollViewer.CommandBindings>
</FlowDocumentScrollViewer>
ClickAction="DisplayWithRelativePath"
Markdown="{Binding UpdateInfo}"
MarkdownStyleName="GithubLike">
</mdxam:MarkdownScrollViewer>
<StackPanel
Grid.Row="2"