- Domain/OwnMixer:注资开张、NPC 客户存入、按匿名集洗污点出款(和普通混币器同一套抽象,匿名集主要是本金和收入)、手续费收入取回全额带污点、本金带自己的污点;客流按"费率 + 出款剩下的污点"和 Tumbler 比,乘黑市声望;风声累积按概率查封(全部没收、记 FENCING、停业)、便衣存入(记 FENCING)、卷款跑路(全额污点、记 FRAUD、黑市声望下降、永久关闭) - 灰网页"自营混币器"标签(买下场外换币台就有;TerminalWindow 拆成 partial,页面在 TerminalWindow.OwnMix.cs):状态、费率、口碑、风声、匿名集、各币种本金/收入/客户在途;费率、开张/暂停、注资(账单转账)、取回、卷款跑路(合约签名) - 夜间结算、日志、报纸新闻;浏览器显示"自营混币器"地址名 - Tuning.OwnMix*(research/mixsim 标定),单元测试,手册与 8 种语言 - 实测:注资 20 PSC 开张,三晚各一笔存入、一次便衣 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
137 lines
7.8 KiB
C#
137 lines
7.8 KiB
C#
using System;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
using Decentralized.Domain;
|
|
|
|
namespace Decentralized
|
|
{
|
|
/// <summary>灰网页的"自营混币器"(买下场外换币台就有):状态、口碑、风声、池子,费率、开张或暂停、注资、取回、卷款跑路。规则见 <see cref="OwnMixer"/>。</summary>
|
|
public static partial class TerminalWindow
|
|
{
|
|
private static string _ownAsset = Assets.CRT;
|
|
private static bool _ownRugConfirm;
|
|
private static readonly float[] OwnWBase = { 70f, 110f, 110f, 110f };
|
|
private static float[] OwnW => Sc(OwnWBase);
|
|
private static readonly TextAnchor[] OwnAligns = { TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight };
|
|
|
|
private static void OwnMixPage(CustomUIBuilder b)
|
|
{
|
|
var d = Session.Data;
|
|
var m = d.OwnMix;
|
|
int day = StoreStation.GetDayCounter();
|
|
double enforce = StoryMods.Current.Sanction(day);
|
|
Columns(b, l =>
|
|
{
|
|
l.AddHeader(L.T("ownmix.h"));
|
|
string status = m.Closed ? L.T("ownmix.status.closed") : m.Seized(day) ? L.T("ownmix.status.seized", m.SeizedUntil + 1) : m.Open ? L.T("ownmix.status.open") : L.T("ownmix.status.paused");
|
|
l.AddLabel(status, m.Closed || m.Seized(day) ? "ownmix_status_note" : "ownmix_status_dim");
|
|
var w = new[] { 220f, 200f };
|
|
TableRow(l, w, L.T("ownmix.k.fee"), (m.Fee * 100).ToString("0", CultureInfo.InvariantCulture) + "%");
|
|
TableRow(l, w, L.T("ownmix.k.rating"), (m.Rating * 100).ToString("0", CultureInfo.InvariantCulture) + "%");
|
|
TableRow(l, w, L.T("ownmix.k.heat"), L.T("ownmix.heat." + m.HeatLevel(enforce)));
|
|
TableRow(l, w, L.T("ownmix.k.anon"), Fmt.Credits(m.Anonymity(Session.Price)));
|
|
TableRow(l, w, L.T("ownmix.k.fees"), Fmt.Credits(m.FeesTotal));
|
|
l.AddSeparator();
|
|
TableHeader(l, OwnW.Sum() + 8f, OwnW, OwnAligns, L.T("col.asset"), L.T("ownmix.col.capital"), L.T("ownmix.col.earned"), L.T("ownmix.col.pending"));
|
|
foreach (var a in OwnMixer.AssetsList)
|
|
{
|
|
var p = m.Pool(a);
|
|
double pend = m.Total(a) - m.Free(a);
|
|
TableRow(l, OwnW.Sum() + 8f, OwnW, OwnAligns, new[] { a, Fmt.Num(p.Capital), Fmt.Num(p.Earned), Fmt.Num(pend) });
|
|
}
|
|
}, r =>
|
|
{
|
|
if (m.Closed || m.Seized(day)) { r.AddLabel(m.Closed ? L.T("ownmix.status.closed") : L.T("ownmix.status.seized", m.SeizedUntil + 1), "ownmix_closed_note"); return; }
|
|
var w = d.Hot;
|
|
r.AddHeader(L.T("ownmix.k.fee"));
|
|
r.BeginRow(4f);
|
|
foreach (var f in Tuning.OwnMixFees)
|
|
{
|
|
double fee = f;
|
|
PledgerSkin.Pick(r, (fee * 100).ToString("0", CultureInfo.InvariantCulture) + "%", Math.Abs(fee - m.Fee) < 1e-9, () => { m.Fee = fee; Session.Save(); Open(); }, _picked);
|
|
}
|
|
r.End();
|
|
r.AddButton(m.Open ? L.T("ownmix.pause") : L.T("ownmix.open"), () =>
|
|
{
|
|
if (m.Open) m.Open = false; else m.Start(Session.Price);
|
|
Log.Info($"自营混币器:{(m.Open ? "开张" : "暂停")},费率 {m.Fee:P0}");
|
|
Session.Save(); Open();
|
|
});
|
|
r.AddSeparator();
|
|
r.BeginRow(4f);
|
|
foreach (var a in OwnMixer.AssetsList)
|
|
{
|
|
var asset = a;
|
|
PledgerSkin.Pick(r, asset, asset == _ownAsset, () => { _ownAsset = asset; Open(); }, _picked);
|
|
}
|
|
r.End();
|
|
r.AddLabel(L.T("mixer.balance", _ownAsset, Fmt.Num(w.Avail(_ownAsset))));
|
|
r.AddLabel(L.T("ownmix.free", Fmt.Num(m.Free(_ownAsset)), _ownAsset));
|
|
AmountRow(r, true, _ownAsset);
|
|
r.BeginRow(6f);
|
|
r.AddButton(L.T("ownmix.inject"), () =>
|
|
{
|
|
if (!ReadAmount(out double v)) return;
|
|
string asset = _ownAsset;
|
|
BillFor(d.Address("ownmix"), L.T("pay.ownmix", Fmt.Num(v), asset), asset, v, pw =>
|
|
{
|
|
if (!m.Inject(pw, asset, v, Session.Price)) { _message = L.T("msg.balance"); Open(); return; }
|
|
_message = L.T("ownmix.injected", Fmt.Num(v), asset);
|
|
Log.Info($"自营混币器注资 {v} {asset},匿名集 {m.Anonymity(Session.Price):0}");
|
|
Session.Save(); Open();
|
|
});
|
|
});
|
|
r.AddButton(L.T("ownmix.withdraw"), () =>
|
|
{
|
|
if (!ReadAmount(out double v)) return;
|
|
string asset = _ownAsset;
|
|
if (v > m.Free(asset) + 1e-9) { _message = L.T("ownmix.err"); Open(); return; }
|
|
_message = L.T("hw.wait");
|
|
Open();
|
|
WalletWindow.OpenForBill(new WalletWindow.Bill
|
|
{
|
|
Contract = true, Party = d.Address("ownmix"), Asset = asset, Amount = 0, Spend = 0, FeeAsset = BestFeeAsset(w),
|
|
Summary = L.T("ownmix.withdraw.sum", Fmt.Num(v), asset),
|
|
Execute = pw =>
|
|
{
|
|
if (!m.Withdraw(pw, asset, v, Session.Price, out double over)) { _message = L.T("ownmix.err"); Open(); return; }
|
|
_message = L.T("ownmix.withdrawn", Fmt.Num(v), asset) + TaintCrime.Commit(over);
|
|
Log.Info($"自营混币器取回 {v} {asset},钱包污点 {pw.Taint:0},超出 {over:0}");
|
|
Session.Save(); Open();
|
|
},
|
|
});
|
|
});
|
|
r.End();
|
|
r.AddSeparator();
|
|
double total = OwnMixer.AssetsList.Sum(a => m.Total(a) * Session.Price(a));
|
|
if (total <= 1e-9) return;
|
|
r.AddButton(_ownRugConfirm ? L.T("ownmix.rug.confirm", Fmt.Credits(total)) : L.T("ownmix.rug", Fmt.Credits(total)), () =>
|
|
{
|
|
if (!_ownRugConfirm) { _ownRugConfirm = true; Open(); return; }
|
|
_ownRugConfirm = false;
|
|
_message = L.T("hw.wait");
|
|
Open();
|
|
WalletWindow.OpenForBill(new WalletWindow.Bill
|
|
{
|
|
Contract = true, Party = d.Address("ownmix"), Asset = Assets.CRT, Amount = 0, Spend = 0, FeeAsset = BestFeeAsset(w),
|
|
Summary = L.T("ownmix.rug.sum"),
|
|
Execute = pw =>
|
|
{
|
|
if (m.Closed) return;
|
|
double value = OwnMixer.AssetsList.Sum(a => m.Total(a) * Session.Price(a));
|
|
m.Rug(pw, Session.Price, out double over);
|
|
PlayerStore.Instance.secData.CommitCrime("FRAUD", (int)Tuning.OwnMixRugCrime);
|
|
StoreReputation.ModReputation(StoreReputation.FACTION_BLACK_MARKET, -Tuning.OwnMixRugRep);
|
|
News.Show("pd_news_ownmix_rug_" + StoreStation.GetDayCounter(), L.T("news.ownmix.rug.title"), L.T("news.ownmix.rug.desc"), 4);
|
|
_message = L.T("ownmix.rugged", Fmt.Credits(value)) + TaintCrime.Commit(over);
|
|
Log.Info($"自营混币器卷款跑路:{value:0},钱包 {pw.Address}");
|
|
Session.Save(); Open();
|
|
},
|
|
});
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}
|