终端窗口可调大小(默认 1200×900)与布局放宽;mod 设置:界面、剧情、难度
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fa8f416545
commit
861acac240
26 files changed
+302
-45
No files matched your search
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
### 第九阶段:剧情与收尾
|
### 第九阶段:剧情与收尾
|
||||||
|
|
||||||
|
- 交易终端窗口加大:默认 1200×900,mod 设置里可选 828×680(旧大小)、1200×900、1400×1000。窗口大时左右栏、表格列、货架列数、K 线图(3 倍像素)、首页卡片、订单两栏、钱包表按多出的宽度放宽,文字放大到约 1.24 倍(上限 1.3,行高和商品卡同步)。
|
||||||
|
- mod 设置(分类:界面、剧情、难度):终端窗口大小;Tumbler 剧情开关;风险倍率(骗局、跑路、钱包投毒、地址劫持、钓鱼概率)和挖矿产出倍率,档位 ×0.5 / ×1 / ×1.5 / ×2,领域层 `Scales` 读取。分类名要等本地化表可用后再登记。
|
||||||
- 多语言:新增 de fr es pl ru ja 六种语言表(连同 zh en 共 8 种,对应游戏主菜单的语言)。英文表是源文,其他语言由 Gemini 分块译出(术语表 `docs/i18n-glossary.md` 在运营仓库,游戏已有的词用官方译名),未经人工校对,文件头有说明。键集合、占位符、`§ ¶ \\n` 标记由单元测试逐语言核对,非中日语言里不得出现汉字或假名。
|
- 多语言:新增 de fr es pl ru ja 六种语言表(连同 zh en 共 8 种,对应游戏主菜单的语言)。英文表是源文,其他语言由 Gemini 分块译出(术语表 `docs/i18n-glossary.md` 在运营仓库,游戏已有的词用官方译名),未经人工校对,文件头有说明。键集合、占位符、`§ ¶ \\n` 标记由单元测试逐语言核对,非中日语言里不得出现汉字或假名。
|
||||||
- 多语言布局:表格单元、按钮文字放不下时自动缩小字号(最小 68%)再省略,商品名限定两行内缩放;de fr es pl ru 的钱包表、手册章节列表、硬件钱包粘贴键按语言放宽;ru de 等少数过长标签改短(永续“加保证金”键、收益池列头)。
|
- 多语言布局:表格单元、按钮文字放不下时自动缩小字号(最小 68%)再省略,商品名限定两行内缩放;de fr es pl ru 的钱包表、手册章节列表、硬件钱包粘贴键按语言放宽;ru de 等少数过长标签改短(永续“加保证金”键、收益池列头)。
|
||||||
- 中文“匿名币”改称“隐私币”(英文 privacy coin),手册、黑市标签同步。
|
- 中文“匿名币”改称“隐私币”(英文 privacy coin),手册、黑市标签同步。
|
||||||
|
|||||||
@@ -13,6 +13,21 @@ All coins, exchanges and events are fictional and have no real-world value.
|
|||||||
| `tests/Decentralized.Domain.Tests` | Console test runner: `dotnet run --project tests/Decentralized.Domain.Tests` |
|
| `tests/Decentralized.Domain.Tests` | Console test runner: `dotnet run --project tests/Decentralized.Domain.Tests` |
|
||||||
| `art/sprites.py`, `art/big.py`, `art/big_cases.py` | Generate the item sprites, the large inspection art and its overlays, and the terminal icons into `src/Decentralized/ModFiles/Textures/Items`. |
|
| `art/sprites.py`, `art/big.py`, `art/big_cases.py` | Generate the item sprites, the large inspection art and its overlays, and the terminal icons into `src/Decentralized/ModFiles/Textures/Items`. |
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
Mod settings are in the game's mod list (select the mod, gear button); they apply to all saves.
|
||||||
|
|
||||||
|
| Section | Setting | Values |
|
||||||
|
|---|---|---|
|
||||||
|
| Interface | Trading terminal size | 828×680, 1200×900 (default), 1400×1000. Layout and text scale with the window; applies when the terminal is next opened. |
|
||||||
|
| Story | Tumbler storyline | on / off. Off stops new visits; progress already made is kept. |
|
||||||
|
| Difficulty | Risk multiplier | ×0.5 / ×1 / ×1.5 / ×2 on the chance of scams, rug pulls, wallet poisoning, address hijacking and phishing. |
|
||||||
|
| Difficulty | Mining reward multiplier | ×0.5 / ×1 / ×1.5 / ×2 on the block reward paid to the player's rigs. |
|
||||||
|
|
||||||
|
## Languages
|
||||||
|
|
||||||
|
The interface follows the game language: en, zh, de, fr, es, pl, ru, ja. English is the source text; zh is hand-written; the other six are AI translations and have not been proofread. Tables are in `src/Decentralized/ModFiles/Localization/<code>.csv`.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT, see `LICENSE`.
|
MIT, see `LICENSE`.
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ namespace Decentralized.Domain
|
|||||||
/// <summary>下单实付(信用点):托管 = 订单价 + 托管费;提前放款 = 订单价打折。</summary>
|
/// <summary>下单实付(信用点):托管 = 订单价 + 托管费;提前放款 = 订单价打折。</summary>
|
||||||
public static int Pay(int price, PayMode m) => m == PayMode.Escrow ? price + EscrowFee(price) : (int)Math.Round(price * (1 - Tuning.BmAdvanceDiscount));
|
public static int Pay(int price, PayMode m) => m == PayMode.Escrow ? price + EscrowFee(price) : (int)Math.Round(price * (1 - Tuning.BmAdvanceDiscount));
|
||||||
|
|
||||||
public double RugChance => Math.Min(Tuning.BmRugMax, Tuning.BmRugBase + Held / 10000.0 * Tuning.BmRugPerTenK);
|
public double RugChance => Scales.R(Math.Min(Tuning.BmRugMax, Tuning.BmRugBase + Held / 10000.0 * Tuning.BmRugPerTenK));
|
||||||
|
|
||||||
/// <summary>每周必有的"矿工路线"商家类型(从中抽 2 家),其余按全部类型抽。</summary>
|
/// <summary>每周必有的"矿工路线"商家类型(从中抽 2 家),其余按全部类型抽。</summary>
|
||||||
private static readonly MerchantType[] Core = { MerchantType.GpuNew, MerchantType.GpuUsed, MerchantType.Rigs, MerchantType.Modules };
|
private static readonly MerchantType[] Core = { MerchantType.GpuNew, MerchantType.GpuUsed, MerchantType.Rigs, MerchantType.Modules };
|
||||||
@@ -232,7 +232,7 @@ namespace Decentralized.Domain
|
|||||||
if (scam && rng.Chance(Tuning.BmForumScamHint)) Forum.Add("scam:" + s.Name);
|
if (scam && rng.Chance(Tuning.BmForumScamHint)) Forum.Add("scam:" + s.Name);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < Sellers.Count; i++) Catalog.AddRange(MerchantGoods(i, Sellers[i], day, rep, rng, value));
|
for (int i = 0; i < Sellers.Count; i++) Catalog.AddRange(MerchantGoods(i, Sellers[i], day, rep, rng, value));
|
||||||
if (RugChance > Tuning.BmRugBase * Tuning.BmForumRugFactor) Forum.Add("rug");
|
if (RugChance > Scales.R(Tuning.BmRugBase * Tuning.BmForumRugFactor)) Forum.Add("rug");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>目录里全部货按订单价算的总值(信用点),用来核对每周周转量。</summary>
|
/// <summary>目录里全部货按订单价算的总值(信用点),用来核对每周周转量。</summary>
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ namespace Decentralized.Domain
|
|||||||
{
|
{
|
||||||
if (playerHash <= 0) return 0;
|
if (playerHash <= 0) return 0;
|
||||||
double total = NpcHash * Availability + playerHash;
|
double total = NpcHash * Availability + playerHash;
|
||||||
return Reward * playerHash / total * (1 - Tuning.PoolFee);
|
return Reward * playerHash / total * (1 - Tuning.PoolFee) * Scales.Mining;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum EraStep { None, Warn, Start }
|
public enum EraStep { None, Warn, Start }
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ namespace Decentralized.Domain
|
|||||||
var results = new List<MixResult>();
|
var results = new List<MixResult>();
|
||||||
foreach (var d in Pending.ToList())
|
foreach (var d in Pending.ToList())
|
||||||
{
|
{
|
||||||
if (rng.Chance(Math.Min(1, Tuning.MixerRugChance * StoryMods.Current.MixerRug)))
|
if (rng.Chance(Scales.R(Tuning.MixerRugChance * StoryMods.Current.MixerRug)))
|
||||||
{
|
{
|
||||||
Pending.Remove(d);
|
Pending.Remove(d);
|
||||||
results.Add(new MixResult { Rugged = true, Asset = d.Asset, Amount = d.Amount });
|
results.Add(new MixResult { Rugged = true, Asset = d.Asset, Amount = d.Amount });
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Decentralized.Domain
|
||||||
|
{
|
||||||
|
/// <summary>mod 设置里的倍率(全局,不随存档)。接入层读设置后写进来;领域层在对应概率和产出处乘上。</summary>
|
||||||
|
public static class Scales
|
||||||
|
{
|
||||||
|
/// <summary>风险倍率:骗局、跑路、钱包投毒、地址劫持、钓鱼的发生概率。</summary>
|
||||||
|
public static double Risk = 1;
|
||||||
|
/// <summary>挖矿产出倍率:玩家矿机拿到的区块奖励。</summary>
|
||||||
|
public static double Mining = 1;
|
||||||
|
|
||||||
|
/// <summary>概率乘风险倍率,封顶 1。</summary>
|
||||||
|
public static double R(double chance) => Math.Min(1, chance * Risk);
|
||||||
|
|
||||||
|
/// <summary>设置里的倍率档位(“×1.5”)。</summary>
|
||||||
|
public static readonly string[] Choices = { "×0.5", "×1", "×1.5", "×2" };
|
||||||
|
public const string Default = "×1";
|
||||||
|
|
||||||
|
public static double Parse(string s)
|
||||||
|
{
|
||||||
|
if (s != null && s.StartsWith("×") && double.TryParse(s.Substring(1), System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var v) && v >= 0) return v;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -75,7 +75,7 @@ namespace Decentralized.Domain
|
|||||||
/// <summary>新空投。钓鱼的名字常是正规名字的仿冒(一个字符不同),要求对一种币无上限授权,奖励看起来更高。</summary>
|
/// <summary>新空投。钓鱼的名字常是正规名字的仿冒(一个字符不同),要求对一种币无上限授权,奖励看起来更高。</summary>
|
||||||
public Airdrop NewAirdrop(int day, Rng rng, string[] assets)
|
public Airdrop NewAirdrop(int day, Rng rng, string[] assets)
|
||||||
{
|
{
|
||||||
bool scam = rng.Chance(Tuning.AirdropScam);
|
bool scam = rng.Chance(Scales.R(Tuning.AirdropScam));
|
||||||
string name = AirdropNames[rng.Range(0, AirdropNames.Length - 1)];
|
string name = AirdropNames[rng.Range(0, AirdropNames.Length - 1)];
|
||||||
if (scam && rng.Chance(0.6)) name = Typo(name, rng);
|
if (scam && rng.Chance(0.6)) name = Typo(name, rng);
|
||||||
string asset = assets[rng.Range(0, assets.Length - 1)];
|
string asset = assets[rng.Range(0, assets.Length - 1)];
|
||||||
@@ -183,7 +183,7 @@ namespace Decentralized.Domain
|
|||||||
}
|
}
|
||||||
foreach (var p in Pools.Where(x => !x.Closed).ToList())
|
foreach (var p in Pools.Where(x => !x.Closed).ToList())
|
||||||
{
|
{
|
||||||
if (p.Scam && p.Total > 0 && day > p.OpenDay && rng.Chance(Tuning.YieldRugNightly))
|
if (p.Scam && p.Total > 0 && day > p.OpenDay && rng.Chance(Scales.R(Tuning.YieldRugNightly)))
|
||||||
{
|
{
|
||||||
foreach (var kv in p.Deposits) evs.Add(new ScamEvent { Kind = "rug", Name = p.Name, Wallet = kv.Key, Asset = Assets.CRC, Amount = kv.Value });
|
foreach (var kv in p.Deposits) evs.Add(new ScamEvent { Kind = "rug", Name = p.Name, Wallet = kv.Key, Asset = Assets.CRC, Amount = kv.Value });
|
||||||
foreach (var q in p.Pending) evs.Add(new ScamEvent { Kind = "rug", Name = p.Name, Wallet = q.Wallet, Asset = Assets.CRC, Amount = q.Amount });
|
foreach (var q in p.Pending) evs.Add(new ScamEvent { Kind = "rug", Name = p.Name, Wallet = q.Wallet, Asset = Assets.CRC, Amount = q.Amount });
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace Decentralized.Domain
|
|||||||
/// <summary>今天来的贩子的类型:骗子越往后越多。</summary>
|
/// <summary>今天来的贩子的类型:骗子越往后越多。</summary>
|
||||||
public static DealerKind Kind(int day, Rng rng)
|
public static DealerKind Kind(int day, Rng rng)
|
||||||
{
|
{
|
||||||
double scam = Math.Min(Tuning.UsedScamMax, Tuning.UsedScamBase + day * Tuning.UsedScamPerDay);
|
double scam = Scales.R(Math.Min(Tuning.UsedScamMax, Tuning.UsedScamBase + day * Tuning.UsedScamPerDay));
|
||||||
double r = rng.NextDouble();
|
double r = rng.NextDouble();
|
||||||
return r < scam ? DealerKind.Scammer : r < scam + Tuning.UsedMiddlemanShare ? DealerKind.Middleman : DealerKind.Honest;
|
return r < scam ? DealerKind.Scammer : r < scam + Tuning.UsedMiddlemanShare ? DealerKind.Middleman : DealerKind.Honest;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ namespace Decentralized
|
|||||||
public static GameItem Make(string id, string model, string fakeOf, double health)
|
public static GameItem Make(string id, string model, string fakeOf, double health)
|
||||||
{
|
{
|
||||||
if (id.StartsWith("gpu:")) return Hardware.Specs[model].Asic ? AsicItems.New(model, health) : Items.NewCard(model, health, fakeOf, Session.Data.Rng);
|
if (id.StartsWith("gpu:")) return Hardware.Specs[model].Asic ? AsicItems.New(model, health) : Items.NewCard(model, health, fakeOf, Session.Data.Rng);
|
||||||
if (id == Items.HwWallet) return Items.NewHwWallet(Session.Data.Rng.Chance(Tuning.WalletPoisonShare));
|
if (id == Items.HwWallet) return Items.NewHwWallet(Session.Data.Rng.Chance(Scales.R(Tuning.WalletPoisonShare)));
|
||||||
return ModHelper.SpawnItem(id);
|
return ModHelper.SpawnItem(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ namespace Decentralized
|
|||||||
|
|
||||||
public const int Days = 30, Step = 6, Body = 5, PriceH = 70, Gap = 3, VolH = 16, Pad = 2, AxisW = 22, DateH = 7;
|
public const int Days = 30, Step = 6, Body = 5, PriceH = 70, Gap = 3, VolH = 16, Pad = 2, AxisW = 22, DateH = 7;
|
||||||
public const int W = Days * Step + Pad * 2 + AxisW, H = PriceH + Gap + VolH + Pad * 2 + DateH;
|
public const int W = Days * Step + Pad * 2 + AxisW, H = PriceH + Gap + VolH + Pad * 2 + DateH;
|
||||||
public const float Scale = 2f;
|
public static float Scale => TerminalWindow.ChartScale;
|
||||||
|
|
||||||
private static readonly Color32 Bg = new Color32(30, 31, 34, 255), Grid = new Color32(48, 50, 54, 255);
|
private static readonly Color32 Bg = new Color32(30, 31, 34, 255), Grid = new Color32(48, 50, 54, 255);
|
||||||
private static readonly Color32 Up = new Color32(108, 190, 112, 255), Down = new Color32(214, 94, 88, 255);
|
private static readonly Color32 Up = new Color32(108, 190, 112, 255), Down = new Color32(214, 94, 88, 255);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ namespace Decentralized
|
|||||||
Items.OpenHandbook = HandbookWindow.Open;
|
Items.OpenHandbook = HandbookWindow.Open;
|
||||||
Items.AddWallet = TerminalWindow.AddWallet;
|
Items.AddWallet = TerminalWindow.AddWallet;
|
||||||
Items.CanAddWallet = TerminalWindow.CanAddWallet;
|
Items.CanAddWallet = TerminalWindow.CanAddWallet;
|
||||||
|
Settings.DefineWhenReady(ModHelper.GetConfig(ModInfo.Id));
|
||||||
Unlock.Register();
|
Unlock.Register();
|
||||||
Night.Subscribe();
|
Night.Subscribe();
|
||||||
DealerStock.Subscribe();
|
DealerStock.Subscribe();
|
||||||
|
|||||||
@@ -809,3 +809,14 @@ cart.partial,{0} Artikel bezahlt; {1} fehlgeschlagen (Bestand oder Guthaben).
|
|||||||
black.info.accepts,Akzeptiert: {0}
|
black.info.accepts,Akzeptiert: {0}
|
||||||
black.info.advance.yes,Vorzeitige Freigabe akzeptiert
|
black.info.advance.yes,Vorzeitige Freigabe akzeptiert
|
||||||
black.info.advance.no,Keine vorzeitige Freigabe
|
black.info.advance.no,Keine vorzeitige Freigabe
|
||||||
|
settings.section.ui,Oberfläche
|
||||||
|
settings.section.story,Handlung
|
||||||
|
settings.section.difficulty,Schwierigkeit
|
||||||
|
settings.terminal_size,Größe des Handelsterminals
|
||||||
|
settings.terminal_size_desc,Fenstergröße in Oberflächeneinheiten (Breite × Höhe). Gilt beim nächsten Öffnen des Terminals.
|
||||||
|
settings.tumbler_story,Tumbler-Handlung
|
||||||
|
settings.tumbler_story_desc,Tumblers Besuche und der Auftrag mit der Schlüsselscheibe. Ausgeschaltet gibt es keine neuen Besuche; erreichter Fortschritt bleibt.
|
||||||
|
settings.risk_scale,Risikofaktor
|
||||||
|
settings.risk_scale_desc,"Multipliziert die Wahrscheinlichkeit von Betrug, Rug Pulls, Wallet-Vergiftung, Adressumleitung und Phishing. Gilt für alle Spielstände."
|
||||||
|
settings.mining_scale,Mining-Ertragsfaktor
|
||||||
|
settings.mining_scale_desc,Multipliziert die Blockbelohnung der Mining-Geräte. Gilt für alle Spielstände.
|
||||||
|
@@ -809,3 +809,14 @@ cart.partial,"Paid for {0} items; {1} failed (stock or balance)."
|
|||||||
black.info.accepts,Takes: {0}
|
black.info.accepts,Takes: {0}
|
||||||
black.info.advance.yes,advance release accepted
|
black.info.advance.yes,advance release accepted
|
||||||
black.info.advance.no,no advance release
|
black.info.advance.no,no advance release
|
||||||
|
settings.section.ui,Interface
|
||||||
|
settings.section.story,Story
|
||||||
|
settings.section.difficulty,Difficulty
|
||||||
|
settings.terminal_size,Trading terminal size
|
||||||
|
settings.terminal_size_desc,Window size in interface units (width × height). Applies the next time the terminal opens.
|
||||||
|
settings.tumbler_story,Tumbler storyline
|
||||||
|
settings.tumbler_story_desc,Tumbler's visits and the keydisk errand. Turning this off stops new visits; progress already made is kept.
|
||||||
|
settings.risk_scale,Risk multiplier
|
||||||
|
settings.risk_scale_desc,"Multiplies the chance of scams, rug pulls, wallet poisoning, address hijacking and phishing. Applies to all saves."
|
||||||
|
settings.mining_scale,Mining reward multiplier
|
||||||
|
settings.mining_scale_desc,Multiplies the block reward paid to mining rigs. Applies to all saves.
|
||||||
|
@@ -809,3 +809,14 @@ cart.partial,Artículos pagados: {0}; fallidos: {1} (stock o saldo).
|
|||||||
black.info.accepts,Acepta: {0}
|
black.info.accepts,Acepta: {0}
|
||||||
black.info.advance.yes,acepta liberación anticipada
|
black.info.advance.yes,acepta liberación anticipada
|
||||||
black.info.advance.no,no acepta liberación anticipada
|
black.info.advance.no,no acepta liberación anticipada
|
||||||
|
settings.section.ui,Interfaz
|
||||||
|
settings.section.story,Historia
|
||||||
|
settings.section.difficulty,Dificultad
|
||||||
|
settings.terminal_size,Tamaño del terminal de comercio
|
||||||
|
settings.terminal_size_desc,Tamaño de la ventana en unidades de interfaz (ancho × alto). Se aplica al abrir el terminal de nuevo.
|
||||||
|
settings.tumbler_story,Trama de Tumbler
|
||||||
|
settings.tumbler_story_desc,"Visitas de Tumbler y el encargo del disco de claves. Desactivada, no habrá visitas nuevas; el progreso logrado se conserva."
|
||||||
|
settings.risk_scale,Multiplicador de riesgo
|
||||||
|
settings.risk_scale_desc,"Multiplica la probabilidad de estafas, rug pulls, envenenamiento de billetera, secuestro de direcciones y phishing. Se aplica a todas las partidas."
|
||||||
|
settings.mining_scale,Multiplicador de minería
|
||||||
|
settings.mining_scale_desc,Multiplica la recompensa de bloque que reciben los equipos de minería. Se aplica a todas las partidas.
|
||||||
|
@@ -809,3 +809,14 @@ cart.partial,Articles payés : {0} ; {1} en échec (stock ou solde).
|
|||||||
black.info.accepts,Accepte : {0}
|
black.info.accepts,Accepte : {0}
|
||||||
black.info.advance.yes,déblocage anticipé accepté
|
black.info.advance.yes,déblocage anticipé accepté
|
||||||
black.info.advance.no,pas de déblocage anticipé
|
black.info.advance.no,pas de déblocage anticipé
|
||||||
|
settings.section.ui,Interface
|
||||||
|
settings.section.story,Histoire
|
||||||
|
settings.section.difficulty,Difficulté
|
||||||
|
settings.terminal_size,Taille du terminal de trading
|
||||||
|
settings.terminal_size_desc,Taille de la fenêtre en unités d'interface (largeur × hauteur). Appliquée à la prochaine ouverture du terminal.
|
||||||
|
settings.tumbler_story,Intrigue Tumbler
|
||||||
|
settings.tumbler_story_desc,"Visites de Tumbler et mission de la clé. Désactivée, plus aucune nouvelle visite ; la progression acquise est conservée."
|
||||||
|
settings.risk_scale,Multiplicateur de risque
|
||||||
|
settings.risk_scale_desc,"Multiplie la probabilité d'arnaques, de rug pulls, d'empoisonnement de portefeuille, de détournement d'adresse et d'hameçonnage. S'applique à toutes les sauvegardes."
|
||||||
|
settings.mining_scale,Multiplicateur de gains de minage
|
||||||
|
settings.mining_scale_desc,Multiplie la récompense de bloc versée aux machines de minage. S'applique à toutes les sauvegardes.
|
||||||
|
@@ -809,3 +809,14 @@ cart.partial,{0} 件を決済、{1} 件が失敗(在庫または残高不足
|
|||||||
black.info.accepts,対応通貨:{0}
|
black.info.accepts,対応通貨:{0}
|
||||||
black.info.advance.yes,事前リリース対応
|
black.info.advance.yes,事前リリース対応
|
||||||
black.info.advance.no,事前リリース非対応
|
black.info.advance.no,事前リリース非対応
|
||||||
|
settings.section.ui,インターフェース
|
||||||
|
settings.section.story,ストーリー
|
||||||
|
settings.section.difficulty,難易度
|
||||||
|
settings.terminal_size,取引ターミナルのサイズ
|
||||||
|
settings.terminal_size_desc,ウィンドウサイズ(幅×高さ、UI 単位)。次にターミナルを開いたときに反映されます。
|
||||||
|
settings.tumbler_story,Tumbler のストーリー
|
||||||
|
settings.tumbler_story_desc,Tumbler の訪問とキーディスクの依頼。オフにすると新たな訪問はなくなり、進行済みの分は保持されます。
|
||||||
|
settings.risk_scale,リスク倍率
|
||||||
|
settings.risk_scale_desc,詐欺、ラグプル、ウォレット汚染、アドレス乗っ取り、フィッシングの発生確率にかかる倍率。すべてのセーブに適用されます。
|
||||||
|
settings.mining_scale,採掘報酬倍率
|
||||||
|
settings.mining_scale_desc,採掘機器が受け取るブロック報酬にかかる倍率。すべてのセーブに適用されます。
|
||||||
|
@@ -809,3 +809,14 @@ cart.partial,Opłacono: {0} szt.; nieudane: {1} szt. (brak zapasów lub salda).
|
|||||||
black.info.accepts,Akceptuje: {0}
|
black.info.accepts,Akceptuje: {0}
|
||||||
black.info.advance.yes,akceptowana wypłata zaliczkowa
|
black.info.advance.yes,akceptowana wypłata zaliczkowa
|
||||||
black.info.advance.no,brak wypłaty zaliczkowej
|
black.info.advance.no,brak wypłaty zaliczkowej
|
||||||
|
settings.section.ui,Interfejs
|
||||||
|
settings.section.story,Fabuła
|
||||||
|
settings.section.difficulty,Poziom trudności
|
||||||
|
settings.terminal_size,Rozmiar terminala handlowego
|
||||||
|
settings.terminal_size_desc,Rozmiar okna w jednostkach interfejsu (szerokość × wysokość). Obowiązuje po następnym otwarciu terminala.
|
||||||
|
settings.tumbler_story,Wątek Tumblera
|
||||||
|
settings.tumbler_story_desc,Wizyty Tumblera i zlecenie z dyskiem kluczy. Po wyłączeniu nie będzie nowych wizyt; dotychczasowy postęp zostaje.
|
||||||
|
settings.risk_scale,Mnożnik ryzyka
|
||||||
|
settings.risk_scale_desc,"Mnoży szanse na oszustwa, rug pulle, zatrucie portfela, podmianę adresu i phishing. Dotyczy wszystkich zapisów."
|
||||||
|
settings.mining_scale,Mnożnik wydobycia
|
||||||
|
settings.mining_scale_desc,Mnoży nagrodę za blok wypłacaną koparkom. Dotyczy wszystkich zapisów.
|
||||||
|
@@ -809,3 +809,14 @@ cart.partial,Оплачено: {0} шт.; не удалось: {1} шт. (нет
|
|||||||
black.info.accepts,Принимает: {0}
|
black.info.accepts,Принимает: {0}
|
||||||
black.info.advance.yes,досрочная выплата принимается
|
black.info.advance.yes,досрочная выплата принимается
|
||||||
black.info.advance.no,без досрочной выплаты
|
black.info.advance.no,без досрочной выплаты
|
||||||
|
settings.section.ui,Интерфейс
|
||||||
|
settings.section.story,Сюжет
|
||||||
|
settings.section.difficulty,Сложность
|
||||||
|
settings.terminal_size,Размер торгового терминала
|
||||||
|
settings.terminal_size_desc,Размер окна в единицах интерфейса (ширина × высота). Применяется при следующем открытии терминала.
|
||||||
|
settings.tumbler_story,Сюжет Tumbler
|
||||||
|
settings.tumbler_story_desc,Визиты Tumbler и поручение с ключ-диском. При отключении новых визитов не будет; достигнутый прогресс сохраняется.
|
||||||
|
settings.risk_scale,Множитель риска
|
||||||
|
settings.risk_scale_desc,"Умножает вероятность мошенничества, рагпулов, отравления кошелька, подмены адреса и фишинга. Действует для всех сохранений."
|
||||||
|
settings.mining_scale,Множитель добычи
|
||||||
|
settings.mining_scale_desc,"Умножает награду за блок, получаемую майнинг-устройствами. Действует для всех сохранений."
|
||||||
|
@@ -808,3 +808,14 @@ cart.partial,"已结算 {0} 件,{1} 件没成(库存或余额不足)。"
|
|||||||
black.info.accepts,收币:{0}
|
black.info.accepts,收币:{0}
|
||||||
black.info.advance.yes,接受提前放款
|
black.info.advance.yes,接受提前放款
|
||||||
black.info.advance.no,不接受提前放款
|
black.info.advance.no,不接受提前放款
|
||||||
|
settings.section.ui,界面
|
||||||
|
settings.section.story,剧情
|
||||||
|
settings.section.difficulty,难度
|
||||||
|
settings.terminal_size,交易终端窗口大小
|
||||||
|
settings.terminal_size_desc,窗口尺寸(宽×高,界面单位)。下次打开终端时生效。
|
||||||
|
settings.tumbler_story,Tumbler 剧情
|
||||||
|
settings.tumbler_story_desc,Tumbler 上门与藏密钥盘的支线。关闭后不再有新的上门,已有进度保留。
|
||||||
|
settings.risk_scale,风险倍率
|
||||||
|
settings.risk_scale_desc,骗局、跑路、钱包投毒、地址劫持和钓鱼的发生概率倍数。对所有存档生效。
|
||||||
|
settings.mining_scale,挖矿产出倍率
|
||||||
|
settings.mining_scale_desc,挖矿设备拿到的区块奖励倍数。对所有存档生效。
|
||||||
|
@@ -105,7 +105,7 @@ namespace Decentralized
|
|||||||
TxRules.Record(w.History, new TxRecord { Addr = fake, Asset = Assets.PSC, Amount = 0, Day = day + 1, In = true });
|
TxRules.Record(w.History, new TxRecord { Addr = fake, Asset = Assets.PSC, Amount = 0, Day = day + 1, In = true });
|
||||||
Log.Info($"地址投毒:{p.Target} 的相似地址 {fake} 向 {w.Address} 转入 0 币");
|
Log.Info($"地址投毒:{p.Target} 的相似地址 {fake} 向 {w.Address} 转入 0 币");
|
||||||
}
|
}
|
||||||
if (d.Rng.Chance(Tuning.TxPhishChance))
|
if (d.Rng.Chance(Scales.R(Tuning.TxPhishChance)))
|
||||||
{
|
{
|
||||||
string fake = ChainAddress.Make(d.Rng.NextUInt());
|
string fake = ChainAddress.Make(d.Rng.NextUInt());
|
||||||
if (Unlock.BlackUnlocked && d.Rng.Chance(0.5)) d.Black.Forum.Add("phish:" + fake);
|
if (Unlock.BlackUnlocked && d.Rng.Chance(0.5)) d.Black.Forum.Add("phish:" + fake);
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ namespace Decentralized
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>应用整套 PLEDGER 皮肤:机身、边框、墨水屏、刻字、文字颜色、触控键、滚动条与数据表行。</summary>
|
/// <summary>应用整套 PLEDGER 皮肤:机身、边框、墨水屏、刻字、文字颜色、触控键、滚动条与数据表行。</summary>
|
||||||
public static void Apply(CustomUIWindow win, RectTransform screen, Image bezel, Image paper, ICollection<GameObject> picked, ICollection<Image> clear, IEnumerable<RowDef> rows = null)
|
public static void Apply(CustomUIWindow win, RectTransform screen, Image bezel, Image paper, ICollection<GameObject> picked, ICollection<Image> clear, IEnumerable<RowDef> rows = null, float fontScale = 1f)
|
||||||
{
|
{
|
||||||
if (win?.root == null) return;
|
if (win?.root == null) return;
|
||||||
Slice(win.root.GetComponent<Image>(), "ui_hw_shell", 10);
|
Slice(win.root.GetComponent<Image>(), "ui_hw_shell", 10);
|
||||||
@@ -210,6 +210,12 @@ namespace Decentralized
|
|||||||
if (content != null) content.childAlignment = TextAnchor.UpperCenter;
|
if (content != null) content.childAlignment = TextAnchor.UpperCenter;
|
||||||
|
|
||||||
if (screen == null) return;
|
if (screen == null) return;
|
||||||
|
if (fontScale > 1.001f)
|
||||||
|
foreach (var t in screen.GetComponentsInChildren<TextMeshProUGUI>(true))
|
||||||
|
{
|
||||||
|
t.fontSize *= fontScale;
|
||||||
|
if (t.enableAutoSizing) { t.fontSizeMax *= fontScale; t.fontSizeMin *= fontScale; }
|
||||||
|
}
|
||||||
|
|
||||||
// 收集所有表格行 root,避免按钮样式覆盖整行
|
// 收集所有表格行 root,避免按钮样式覆盖整行
|
||||||
var rowRoots = new HashSet<GameObject>();
|
var rowRoots = new HashSet<GameObject>();
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
using Decentralized.Domain;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Decentralized
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// mod 设置(游戏自带的模组配置:主菜单“改装件”里选中 mod 后点齿轮)。
|
||||||
|
/// 文件在 persistentDataPath/ModConfigs/probably_decentralized.json,全局生效,不随存档。
|
||||||
|
/// 分类(Section):界面、剧情、难度。
|
||||||
|
/// </summary>
|
||||||
|
public static class Settings
|
||||||
|
{
|
||||||
|
/// <summary>终端窗口尺寸档位(宽×高,界面单位)。第一项是旧版大小。</summary>
|
||||||
|
public static readonly string[] TerminalSizes = { "828×680", "1200×900", "1400×1000" };
|
||||||
|
public const string DefaultTerminalSize = "1200×900";
|
||||||
|
|
||||||
|
private static ConfigEntry<string> _size, _risk, _mining;
|
||||||
|
private static ConfigEntry<bool> _tumbler;
|
||||||
|
|
||||||
|
/// <summary>Tumbler 上门剧情开关:关掉后不再有新的上门,已有进度保留。</summary>
|
||||||
|
public static bool TumblerStory => _tumbler?.Value ?? true;
|
||||||
|
|
||||||
|
public static float TerminalW => Size().w;
|
||||||
|
public static float TerminalH => Size().h;
|
||||||
|
|
||||||
|
private static (float w, float h) Size()
|
||||||
|
{
|
||||||
|
var s = _size?.Value ?? DefaultTerminalSize;
|
||||||
|
var p = s.Split('×');
|
||||||
|
if (p.Length == 2 && float.TryParse(p[0], out var w) && float.TryParse(p[1], out var h)) return (w, h);
|
||||||
|
return (1200f, 900f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 登记设置项。本地化表要等主菜单起来才读得到,而分类名是字符串(不是回调),所以等表可用了再登记。
|
||||||
|
/// </summary>
|
||||||
|
public static void DefineWhenReady(ModConfig config)
|
||||||
|
{
|
||||||
|
var go = new GameObject("pd_settings_defer");
|
||||||
|
Object.DontDestroyOnLoad(go);
|
||||||
|
go.AddComponent<Waiter>().Config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class Waiter : MonoBehaviour
|
||||||
|
{
|
||||||
|
public ModConfig Config;
|
||||||
|
private int _frames;
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
_frames++;
|
||||||
|
if (_frames < 300 || (L.T("settings.section.ui") == "settings.section.ui" && _frames < 1800)) return;
|
||||||
|
Log.Guard("登记 mod 设置", () => Define(Config));
|
||||||
|
Destroy(gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Define(ModConfig config)
|
||||||
|
{
|
||||||
|
if (config == null) { Log.Warn("[Settings] 拿不到模组配置,用默认值"); return; }
|
||||||
|
string ui = L.T("settings.section.ui");
|
||||||
|
_size = config.Define("terminal_size", DefaultTerminalSize, ui, () => L.T("settings.terminal_size"), TerminalSizes,
|
||||||
|
() => L.T("settings.terminal_size_desc"));
|
||||||
|
_size.OnChanged += _ => { if (CustomUIManager.Instance != null && CustomUIManager.Instance.IsOpen(TerminalWindow.Id)) TerminalWindow.Open(); };
|
||||||
|
|
||||||
|
_tumbler = config.Define("tumbler_story", true, L.T("settings.section.story"), () => L.T("settings.tumbler_story"), () => L.T("settings.tumbler_story_desc"));
|
||||||
|
|
||||||
|
string hard = L.T("settings.section.difficulty");
|
||||||
|
_risk = config.Define("risk_scale", Scales.Default, hard, () => L.T("settings.risk_scale"), Scales.Choices, () => L.T("settings.risk_scale_desc"));
|
||||||
|
_mining = config.Define("mining_scale", Scales.Default, hard, () => L.T("settings.mining_scale"), Scales.Choices, () => L.T("settings.mining_scale_desc"));
|
||||||
|
_risk.OnChanged += _ => Apply();
|
||||||
|
_mining.OnChanged += _ => Apply();
|
||||||
|
Apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Apply()
|
||||||
|
{
|
||||||
|
Scales.Risk = Scales.Parse(_risk?.Value);
|
||||||
|
Scales.Mining = Scales.Parse(_mining?.Value);
|
||||||
|
Log.Info($"[Settings] 风险 ×{Scales.Risk},挖矿 ×{Scales.Mining}"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,8 +16,19 @@ namespace Decentralized
|
|||||||
public static class TerminalWindow
|
public static class TerminalWindow
|
||||||
{
|
{
|
||||||
public const string Id = "pd_terminal";
|
public const string Id = "pd_terminal";
|
||||||
private const float WinW = 828f, WinH = 680f, ScreenW = 768f, ScreenH = 578f, Bezel = 6f;
|
private const float Bezel = 6f;
|
||||||
private const float LeftW = 420f, RightW = 310f;
|
// 窗口尺寸取自 mod 设置(828×680 是基准,各页布局按多出来的宽高等比放宽)
|
||||||
|
private static float WinW => Settings.TerminalW;
|
||||||
|
private static float WinH => Settings.TerminalH;
|
||||||
|
private static float ScreenW => WinW - 60f;
|
||||||
|
private static float ScreenH => WinH - 102f;
|
||||||
|
private static float Ext => ScreenW - 768f;
|
||||||
|
private static float LeftW => 420f + Ext * 0.6f;
|
||||||
|
private static float RightW => 310f + Ext * 0.4f;
|
||||||
|
/// <summary>大窗口里文字放大(行高、卡片同步放大):基准窗口 1,1200 宽约 1.24,上限 1.3。</summary>
|
||||||
|
private static float F => Mathf.Clamp(1f + Ext * 0.00065f, 1f, 1.3f);
|
||||||
|
public static float ChartScale => LeftW >= 634f ? 3f : 2f;
|
||||||
|
private static float[] Sc(float[] b) { float k = LeftW / 420f; return b.Select(x => x * k).ToArray(); }
|
||||||
private enum Page { Home, Bank, Poinbase, Web3, Wallets }
|
private enum Page { Home, Bank, Poinbase, Web3, Wallets }
|
||||||
|
|
||||||
private static Page _page = Page.Home;
|
private static Page _page = Page.Home;
|
||||||
@@ -69,7 +80,7 @@ namespace Decentralized
|
|||||||
b.End();
|
b.End();
|
||||||
b.End();
|
b.End();
|
||||||
var win = b.Show();
|
var win = b.Show();
|
||||||
Log.Guard("交易终端外观", () => PledgerSkin.Apply(win, screen, bezel, paper, _picked, _clear, _rows));
|
Log.Guard("交易终端外观", () => PledgerSkin.Apply(win, screen, bezel, paper, _picked, _clear, _rows, F));
|
||||||
Log.Guard("购物车按钮", () => CartFab(win));
|
Log.Guard("购物车按钮", () => CartFab(win));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +95,7 @@ namespace Decentralized
|
|||||||
|
|
||||||
private static void Entry(CustomUIBuilder b, string icon, string name, string line1, string line2, Page target)
|
private static void Entry(CustomUIBuilder b, string icon, string name, string line1, string line2, Page target)
|
||||||
{
|
{
|
||||||
b.BeginPanel(230f, 250f, TextAnchor.UpperCenter, 10f, 6f);
|
b.BeginPanel((ScreenW - 80f) / 3f, 250f + (ScreenH - 578f) * 0.25f, TextAnchor.UpperCenter, 10f, 6f);
|
||||||
var cardImg = b.Last.image;
|
var cardImg = b.Last.image;
|
||||||
cardImg.sprite = MachineryStyle.White;
|
cardImg.sprite = MachineryStyle.White;
|
||||||
cardImg.color = PledgerSkin.RowNormal;
|
cardImg.color = PledgerSkin.RowNormal;
|
||||||
@@ -116,7 +127,7 @@ namespace Decentralized
|
|||||||
|
|
||||||
private static void Cell(CustomUIBuilder b, float w, string text, bool header = false, TextAnchor align = TextAnchor.MiddleLeft)
|
private static void Cell(CustomUIBuilder b, float w, string text, bool header = false, TextAnchor align = TextAnchor.MiddleLeft)
|
||||||
{
|
{
|
||||||
b.BeginPanel(w, 26f, align, 0f, 0f);
|
b.BeginPanel(w, 26f * F, align, 0f, 0f);
|
||||||
_clear.Add(b.Last.image);
|
_clear.Add(b.Last.image);
|
||||||
b.AddLabel(header ? "<b>" + text + "</b>" : text);
|
b.AddLabel(header ? "<b>" + text + "</b>" : text);
|
||||||
PledgerSkin.FitCurrent(b);
|
PledgerSkin.FitCurrent(b);
|
||||||
@@ -125,7 +136,7 @@ namespace Decentralized
|
|||||||
|
|
||||||
private static void TableRow(CustomUIBuilder b, float totalW, float[] widths, TextAnchor[] aligns, string[] cells, bool selected = false, Action onClick = null)
|
private static void TableRow(CustomUIBuilder b, float totalW, float[] widths, TextAnchor[] aligns, string[] cells, bool selected = false, Action onClick = null)
|
||||||
{
|
{
|
||||||
b.BeginPanel(totalW, 28f, TextAnchor.MiddleLeft, 4f, 0f);
|
b.BeginPanel(totalW, 28f * F, TextAnchor.MiddleLeft, 4f, 0f);
|
||||||
_rows.Add(new PledgerSkin.RowDef { Root = b.Last.root, Image = b.Last.image, Selected = selected, OnClick = onClick });
|
_rows.Add(new PledgerSkin.RowDef { Root = b.Last.root, Image = b.Last.image, Selected = selected, OnClick = onClick });
|
||||||
b.BeginRow(0f, TextAnchor.MiddleLeft);
|
b.BeginRow(0f, TextAnchor.MiddleLeft);
|
||||||
for (int i = 0; i < widths.Length; i++)
|
for (int i = 0; i < widths.Length; i++)
|
||||||
@@ -142,7 +153,7 @@ namespace Decentralized
|
|||||||
|
|
||||||
private static void TableHeader(CustomUIBuilder b, float totalW, float[] widths, TextAnchor[] aligns, params string[] cells)
|
private static void TableHeader(CustomUIBuilder b, float totalW, float[] widths, TextAnchor[] aligns, params string[] cells)
|
||||||
{
|
{
|
||||||
b.BeginPanel(totalW, 26f, TextAnchor.MiddleLeft, 4f, 0f);
|
b.BeginPanel(totalW, 26f * F, TextAnchor.MiddleLeft, 4f, 0f);
|
||||||
_clear.Add(b.Last.image);
|
_clear.Add(b.Last.image);
|
||||||
b.BeginRow(0f, TextAnchor.MiddleLeft);
|
b.BeginRow(0f, TextAnchor.MiddleLeft);
|
||||||
for (int i = 0; i < widths.Length; i++)
|
for (int i = 0; i < widths.Length; i++)
|
||||||
@@ -199,10 +210,10 @@ namespace Decentralized
|
|||||||
AmlPreview(r);
|
AmlPreview(r);
|
||||||
r.AddSpace(6f);
|
r.AddSpace(6f);
|
||||||
r.AddButton(L.T("trade.deposit"), Deposit);
|
r.AddButton(L.T("trade.deposit"), Deposit);
|
||||||
r.Last?.SetPreferredSize(RightW - 30f, 28f);
|
r.Last?.SetPreferredSize(RightW - 30f, 28f * F);
|
||||||
r.AddSpace(4f);
|
r.AddSpace(4f);
|
||||||
r.AddButton(L.T("trade.withdraw"), Withdraw);
|
r.AddButton(L.T("trade.withdraw"), Withdraw);
|
||||||
r.Last?.SetPreferredSize(RightW - 30f, 28f);
|
r.Last?.SetPreferredSize(RightW - 30f, 28f * F);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +227,8 @@ namespace Decentralized
|
|||||||
b.AddLabel(L.T("aml.taint", (int)Math.Round(taintRatio * 100)), taintRatio > 0.05 ? "aml_note" : "aml_dim");
|
b.AddLabel(L.T("aml.taint", (int)Math.Round(taintRatio * 100)), taintRatio > 0.05 ? "aml_note" : "aml_dim");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly float[] StableW = { 80f, 100f, 220f };
|
private static readonly float[] StableWBase = new[] { 80f, 100f, 220f };
|
||||||
|
private static float[] StableW => Sc(StableWBase);
|
||||||
|
|
||||||
private static string StableStatus(Stablecoins st, string asset, int day)
|
private static string StableStatus(Stablecoins st, string asset, int day)
|
||||||
{
|
{
|
||||||
@@ -250,7 +262,8 @@ namespace Decentralized
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly float[] HoldW = { 80f, 100f, 110f, 110f };
|
private static readonly float[] HoldWBase = new[] { 80f, 100f, 110f, 110f };
|
||||||
|
private static float[] HoldW => Sc(HoldWBase);
|
||||||
private static readonly TextAnchor[] HoldAligns = { TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight };
|
private static readonly TextAnchor[] HoldAligns = { TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight };
|
||||||
|
|
||||||
private static void Holdings(CustomUIBuilder b, Wallet w, Func<string, bool> show)
|
private static void Holdings(CustomUIBuilder b, Wallet w, Func<string, bool> show)
|
||||||
@@ -323,7 +336,8 @@ namespace Decentralized
|
|||||||
private static string _perpProduct = "PSC";
|
private static string _perpProduct = "PSC";
|
||||||
private static bool _perpLong = true;
|
private static bool _perpLong = true;
|
||||||
private static int _perpLev = 3;
|
private static int _perpLev = 3;
|
||||||
private static readonly float[] PerpW = { 80f, 70f, 110f, 60f, 70f };
|
private static readonly float[] PerpWBase = new[] { 80f, 70f, 110f, 60f, 70f };
|
||||||
|
private static float[] PerpW => Sc(PerpWBase);
|
||||||
private static readonly TextAnchor[] PerpAligns = { TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight };
|
private static readonly TextAnchor[] PerpAligns = { TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight };
|
||||||
|
|
||||||
private static double PerpSpot(string p) => p == "SPARK" ? Session.Data.Stable.Spark : Session.Data.Market.Price;
|
private static double PerpSpot(string p) => p == "SPARK" ? Session.Data.Stable.Spark : Session.Data.Market.Price;
|
||||||
@@ -409,7 +423,8 @@ namespace Decentralized
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly float[] PoolW = { 110f, 50f, 64f, 64f, 50f, 78f };
|
private static readonly float[] PoolWBase = new[] { 110f, 50f, 64f, 64f, 50f, 78f };
|
||||||
|
private static float[] PoolW => Sc(PoolWBase);
|
||||||
private static readonly TextAnchor[] PoolAligns = { TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight };
|
private static readonly TextAnchor[] PoolAligns = { TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight };
|
||||||
|
|
||||||
/// <summary>第三方收益池:日息、储备、时间锁、审计、我的存款,存入 / 取出(合约调用,签名)。是不是骗局看不见。</summary>
|
/// <summary>第三方收益池:日息、储备、时间锁、审计、我的存款,存入 / 取出(合约调用,签名)。是不是骗局看不见。</summary>
|
||||||
@@ -804,7 +819,7 @@ namespace Decentralized
|
|||||||
{
|
{
|
||||||
int tier = t;
|
int tier = t;
|
||||||
r.AddButton(L.T("intel.tip.buy", (int)Math.Round(Tuning.IntelTipAcc[t] * 100), Fmt.Num(Tuning.IntelTipPrice[t] * m.MaxLoss)), () => IntelBuyTip(mk, tier));
|
r.AddButton(L.T("intel.tip.buy", (int)Math.Round(Tuning.IntelTipAcc[t] * 100), Fmt.Num(Tuning.IntelTipPrice[t] * m.MaxLoss)), () => IntelBuyTip(mk, tier));
|
||||||
r.Last?.SetPreferredSize(RightW - 30f, 28f);
|
r.Last?.SetPreferredSize(RightW - 30f, 28f * F);
|
||||||
if (t < 2) r.AddSpace(2f);
|
if (t < 2) r.AddSpace(2f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -985,9 +1000,11 @@ namespace Decentralized
|
|||||||
{
|
{
|
||||||
var d = Session.Data;
|
var d = Session.Data;
|
||||||
b.AddLabel(L.T("wallets.count", d.Wallets.Count, Tuning.WalletMax), "wallets_count_dim");
|
b.AddLabel(L.T("wallets.count", d.Wallets.Count, Tuning.WalletMax), "wallets_count_dim");
|
||||||
var cols = L.Wide ? new[] { 100f, 92f, 62f, 62f, 372f } : new[] { 100f, 110f, 85f, 70f, 375f };
|
bool wide = L.Wide && Ext < 100f;
|
||||||
|
float k = Ext < 100f ? 1f : (ScreenW - 28f) / 740f;
|
||||||
|
var cols = (wide ? new[] { 100f, 92f, 62f, 62f, 372f } : new[] { 100f, 110f, 85f, 70f, 375f }).Select(x => x * k).ToArray();
|
||||||
var aligns = new[] { TextAnchor.MiddleLeft, TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleLeft };
|
var aligns = new[] { TextAnchor.MiddleLeft, TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleLeft };
|
||||||
TableHeader(b, 740f, cols, aligns, L.T("wallets.col.name"), L.T("wallets.col.addr"), L.T("col.value"), L.T("wallets.col.taint"), "");
|
TableHeader(b, 740f * k, cols, aligns, L.T("wallets.col.name"), L.T("wallets.col.addr"), L.T("col.value"), L.T("wallets.col.taint"), "");
|
||||||
for (int i = 0; i < d.Wallets.Count; i++)
|
for (int i = 0; i < d.Wallets.Count; i++)
|
||||||
{
|
{
|
||||||
var w = d.Wallets[i];
|
var w = d.Wallets[i];
|
||||||
@@ -995,7 +1012,7 @@ namespace Decentralized
|
|||||||
bool isCurrent = idx == d.Current;
|
bool isCurrent = idx == d.Current;
|
||||||
// 一行三块:数据格(整行反色的是这一块)、操作键(和顶栏页签一样的黑色触控键,放在纸面上)、连接状态靠右
|
// 一行三块:数据格(整行反色的是这一块)、操作键(和顶栏页签一样的黑色触控键,放在纸面上)、连接状态靠右
|
||||||
b.BeginRow(4f, TextAnchor.MiddleLeft);
|
b.BeginRow(4f, TextAnchor.MiddleLeft);
|
||||||
b.BeginPanel(cols[0] + cols[1] + cols[2] + cols[3] + 8f, 32f, TextAnchor.MiddleLeft, 4f, 0f);
|
b.BeginPanel(cols[0] + cols[1] + cols[2] + cols[3] + 8f, 32f * F, TextAnchor.MiddleLeft, 4f, 0f);
|
||||||
_rows.Add(new PledgerSkin.RowDef
|
_rows.Add(new PledgerSkin.RowDef
|
||||||
{
|
{
|
||||||
Root = b.Last.root,
|
Root = b.Last.root,
|
||||||
@@ -1010,7 +1027,7 @@ namespace Decentralized
|
|||||||
Cell(b, cols[3], Fmt.Credits(w.Taint), false, TextAnchor.MiddleRight);
|
Cell(b, cols[3], Fmt.Credits(w.Taint), false, TextAnchor.MiddleRight);
|
||||||
b.End();
|
b.End();
|
||||||
b.End();
|
b.End();
|
||||||
b.BeginPanel(cols[4] - (L.Wide ? 112f : 150f), 32f, TextAnchor.MiddleLeft, 0f, 0f);
|
b.BeginPanel(cols[4] - (wide ? 112f : 150f) * k, 32f * F, TextAnchor.MiddleLeft, 0f, 0f);
|
||||||
_clear.Add(b.Last.image);
|
_clear.Add(b.Last.image);
|
||||||
b.BeginRow(4f, TextAnchor.MiddleLeft);
|
b.BeginRow(4f, TextAnchor.MiddleLeft);
|
||||||
RowKey(b, L.T("wallets.open"), () => WalletWindow.Open(w));
|
RowKey(b, L.T("wallets.open"), () => WalletWindow.Open(w));
|
||||||
@@ -1019,7 +1036,7 @@ namespace Decentralized
|
|||||||
if (idx > 0) RowKey(b, _destroyPick == w.Address ? L.T("wallets.destroy.sure") : L.T("wallets.destroy"), () => DestroyWallet(w));
|
if (idx > 0) RowKey(b, _destroyPick == w.Address ? L.T("wallets.destroy.sure") : L.T("wallets.destroy"), () => DestroyWallet(w));
|
||||||
b.End();
|
b.End();
|
||||||
b.End();
|
b.End();
|
||||||
b.BeginPanel(L.Wide ? 92f : 84f, 32f, TextAnchor.MiddleRight, 0f, 0f);
|
b.BeginPanel((wide ? 92f : 84f) * k, 32f * F, TextAnchor.MiddleRight, 0f, 0f);
|
||||||
_clear.Add(b.Last.image);
|
_clear.Add(b.Last.image);
|
||||||
b.BeginRow(0f, TextAnchor.MiddleRight);
|
b.BeginRow(0f, TextAnchor.MiddleRight);
|
||||||
if (idx != d.Current) RowKey(b, L.T("wallets.use"), () => { d.Current = idx; Session.Save(); Open(); });
|
if (idx != d.Current) RowKey(b, L.T("wallets.use"), () => { d.Current = idx; Session.Save(); Open(); });
|
||||||
@@ -1038,7 +1055,7 @@ namespace Decentralized
|
|||||||
var go = b.Last.root;
|
var go = b.Last.root;
|
||||||
if (go == null) return;
|
if (go == null) return;
|
||||||
var le = go.GetComponent<LayoutElement>() ?? go.AddComponent<LayoutElement>();
|
var le = go.GetComponent<LayoutElement>() ?? go.AddComponent<LayoutElement>();
|
||||||
le.minHeight = 26f; le.preferredHeight = 26f; le.flexibleHeight = 0f;
|
le.minHeight = 26f * F; le.preferredHeight = 26f * F; le.flexibleHeight = 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string _destroyPick, _renamePick, _renameText = "";
|
private static string _destroyPick, _renamePick, _renameText = "";
|
||||||
@@ -1230,7 +1247,8 @@ namespace Decentralized
|
|||||||
b.AddLabel(f.StartsWith("phish:") ? L.T("black.forum.phish", f.Substring(6)) : f.StartsWith("scam:") ? L.T("black.forum.scam", BlackDelivery.DisplayName(f.Substring(5))) : f.StartsWith("busted:") ? L.T("black.forum.busted", f.Substring(7)) : L.T("black.forum." + f), "forum_dim");
|
b.AddLabel(f.StartsWith("phish:") ? L.T("black.forum.phish", f.Substring(6)) : f.StartsWith("scam:") ? L.T("black.forum.scam", BlackDelivery.DisplayName(f.Substring(5))) : f.StartsWith("busted:") ? L.T("black.forum.busted", f.Substring(7)) : L.T("black.forum." + f), "forum_dim");
|
||||||
}
|
}
|
||||||
|
|
||||||
private const float SellerW = 176f, ShelfW = 560f;
|
private static float SellerW => 176f * F;
|
||||||
|
private static float ShelfW => 560f + Ext * 0.78f;
|
||||||
|
|
||||||
private static void PlatformShelf(CustomUIBuilder b, SaveData d)
|
private static void PlatformShelf(CustomUIBuilder b, SaveData d)
|
||||||
{
|
{
|
||||||
@@ -1242,7 +1260,7 @@ namespace Decentralized
|
|||||||
c.AddLabel(L.T("black.price", offer.Price));
|
c.AddLabel(L.T("black.price", offer.Price));
|
||||||
c.AddFlexibleSpace();
|
c.AddFlexibleSpace();
|
||||||
c.AddButton(L.T("black.buy"), () => AddToCart(-1, offer));
|
c.AddButton(L.T("black.buy"), () => AddToCart(-1, offer));
|
||||||
}, 3);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ——— 付款面板 ———
|
// ——— 付款面板 ———
|
||||||
@@ -1425,7 +1443,7 @@ namespace Decentralized
|
|||||||
c.AddLabel(L.T("black.price", g.Price));
|
c.AddLabel(L.T("black.price", g.Price));
|
||||||
c.AddFlexibleSpace();
|
c.AddFlexibleSpace();
|
||||||
c.AddButton(L.T("black.sell.list"), () => BeginEdit(new EditPrice { Item = it, Id = g.Item, Ref = BlackDelivery.RefValue(it) }, g.Price));
|
c.AddButton(L.T("black.sell.list"), () => BeginEdit(new EditPrice { Item = it, Id = g.Item, Ref = BlackDelivery.RefValue(it) }, g.Price));
|
||||||
}, 3);
|
});
|
||||||
b.End();
|
b.End();
|
||||||
b.End();
|
b.End();
|
||||||
}
|
}
|
||||||
@@ -1725,12 +1743,14 @@ namespace Decentralized
|
|||||||
}, 3);
|
}, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private const int ShelfCols = 4;
|
private static int ShelfCols => Ext < 100f ? 3 : Math.Max(3, (int)Math.Round(ShelfW / (172f * F)));
|
||||||
private const float CardW = 172f, CardH = 268f;
|
private static float CardW => Ext < 100f ? 172f : (ShelfW - 6f * (ShelfCols - 1)) / ShelfCols;
|
||||||
|
private static float CardH => 268f * F;
|
||||||
|
|
||||||
/// <summary>货架:一行 4 张商品卡。</summary>
|
/// <summary>货架:一行 4 张商品卡。</summary>
|
||||||
private static void Shelf(CustomUIBuilder b, System.Collections.Generic.List<BmGood> goods, Action<CustomUIBuilder, BmGood> card, int cols = ShelfCols)
|
private static void Shelf(CustomUIBuilder b, System.Collections.Generic.List<BmGood> goods, Action<CustomUIBuilder, BmGood> card, int cols = -1)
|
||||||
{
|
{
|
||||||
|
if (cols <= 0) cols = ShelfCols;
|
||||||
for (int i = 0; i < goods.Count; i += cols)
|
for (int i = 0; i < goods.Count; i += cols)
|
||||||
{
|
{
|
||||||
b.BeginRow(6f);
|
b.BeginRow(6f);
|
||||||
@@ -1755,7 +1775,7 @@ namespace Decentralized
|
|||||||
var sprite = Icon(g.Item);
|
var sprite = Icon(g.Item);
|
||||||
if (sprite != null) c.AddImage(sprite, 64f, 64f); else c.AddSpace(64f);
|
if (sprite != null) c.AddImage(sprite, 64f, 64f); else c.AddSpace(64f);
|
||||||
c.AddLabel("<b>" + ItemName(g.Item) + "</b>");
|
c.AddLabel("<b>" + ItemName(g.Item) + "</b>");
|
||||||
PledgerSkin.FitBox(c, 52f);
|
PledgerSkin.FitBox(c, 52f * F);
|
||||||
bool asic = g.Kind == GoodKind.Gpu && g.Model != null && Hardware.Specs.TryGetValue(g.Model, out var gs) && gs.Asic;
|
bool asic = g.Kind == GoodKind.Gpu && g.Model != null && Hardware.Specs.TryGetValue(g.Model, out var gs) && gs.Asic;
|
||||||
c.AddLabel(asic ? (g.Health >= 100 ? L.T("black.kind.asicnew") : L.T("black.kind.asic", (int)Math.Round(g.Health))) : g.Kind == GoodKind.Gpu ? (g.Health >= 100 ? L.T("black.kind.gpunew") : L.T("black.kind.gpu", (int)Math.Round(g.Health))) : L.T("black.kind." + g.Kind + (g.Kind == GoodKind.Contraband ? "." + g.Level : "")), "good_kind_dim");
|
c.AddLabel(asic ? (g.Health >= 100 ? L.T("black.kind.asicnew") : L.T("black.kind.asic", (int)Math.Round(g.Health))) : g.Kind == GoodKind.Gpu ? (g.Health >= 100 ? L.T("black.kind.gpunew") : L.T("black.kind.gpu", (int)Math.Round(g.Health))) : L.T("black.kind." + g.Kind + (g.Kind == GoodKind.Contraband ? "." + g.Level : "")), "good_kind_dim");
|
||||||
if (!string.IsNullOrEmpty(line2)) c.AddLabel(line2, "good_line2_dim");
|
if (!string.IsNullOrEmpty(line2)) c.AddLabel(line2, "good_line2_dim");
|
||||||
@@ -1902,14 +1922,14 @@ namespace Decentralized
|
|||||||
{
|
{
|
||||||
var d = Session.Data;
|
var d = Session.Data;
|
||||||
b.BeginRow(8f, TextAnchor.UpperLeft);
|
b.BeginRow(8f, TextAnchor.UpperLeft);
|
||||||
b.BeginPanel(SellerW + 8f + 150f, -1f, TextAnchor.UpperLeft, 4f, 4f);
|
b.BeginPanel((SellerW + 8f + ShelfW) * 0.45f, -1f, TextAnchor.UpperLeft, 4f, 4f);
|
||||||
_clear.Add(b.Last.image);
|
_clear.Add(b.Last.image);
|
||||||
b.AddHeader(L.T("black.orders.in"));
|
b.AddHeader(L.T("black.orders.in"));
|
||||||
if (d.Black.Orders.Count == 0) b.AddLabel(L.T("black.mine.none"), "orders_in_none_dim");
|
if (d.Black.Orders.Count == 0) b.AddLabel(L.T("black.mine.none"), "orders_in_none_dim");
|
||||||
foreach (var o in d.Black.Orders.OrderByDescending(x => x.PlacedDay))
|
foreach (var o in d.Black.Orders.OrderByDescending(x => x.PlacedDay))
|
||||||
b.AddLabel(L.T("black.mine.line", ItemName(o.Item) + (o.Count > 1 ? " ×" + o.Count : ""), BlackDelivery.DisplayName(o.Seller), L.T("black.mode." + o.Mode), L.T("black.state." + o.State + (o.Why != null && o.State != OrderState.Done ? "." + o.Why : "")), o.DueDay));
|
b.AddLabel(L.T("black.mine.line", ItemName(o.Item) + (o.Count > 1 ? " ×" + o.Count : ""), BlackDelivery.DisplayName(o.Seller), L.T("black.mode." + o.Mode), L.T("black.state." + o.State + (o.Why != null && o.State != OrderState.Done ? "." + o.Why : "")), o.DueDay));
|
||||||
b.End();
|
b.End();
|
||||||
b.BeginPanel(ShelfW - 150f, -1f, TextAnchor.UpperLeft, 4f, 4f);
|
b.BeginPanel((SellerW + 8f + ShelfW) * 0.55f, -1f, TextAnchor.UpperLeft, 4f, 4f);
|
||||||
_clear.Add(b.Last.image);
|
_clear.Add(b.Last.image);
|
||||||
b.AddHeader(L.T("black.orders.out"));
|
b.AddHeader(L.T("black.orders.out"));
|
||||||
if (d.Black.Sales.Count == 0) b.AddLabel(L.T("black.mine.none"), "orders_out_none_dim");
|
if (d.Black.Sales.Count == 0) b.AddLabel(L.T("black.mine.none"), "orders_out_none_dim");
|
||||||
@@ -1979,7 +1999,8 @@ namespace Decentralized
|
|||||||
private static Web3Tab _web3Tab = Web3Tab.Trade;
|
private static Web3Tab _web3Tab = Web3Tab.Trade;
|
||||||
private static string _mixAsset = Assets.PSC;
|
private static string _mixAsset = Assets.PSC;
|
||||||
private static int _mixRounds = 2;
|
private static int _mixRounds = 2;
|
||||||
private static readonly float[] MixW = { 70f, 100f, 100f, 80f };
|
private static readonly float[] MixWBase = new[] { 70f, 100f, 100f, 80f };
|
||||||
|
private static float[] MixW => Sc(MixWBase);
|
||||||
private static readonly TextAnchor[] MixAligns = { TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight };
|
private static readonly TextAnchor[] MixAligns = { TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight };
|
||||||
|
|
||||||
/// <summary>混币器:把币存进去,几轮之后提回同一个地址,污点降低,每轮扣手续费。</summary>
|
/// <summary>混币器:把币存进去,几轮之后提回同一个地址,污点降低,每轮扣手续费。</summary>
|
||||||
@@ -2048,7 +2069,8 @@ namespace Decentralized
|
|||||||
Open();
|
Open();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly float[] StakeW = { 110f, 90f, 200f };
|
private static readonly float[] StakeWBase = new[] { 110f, 90f, 200f };
|
||||||
|
private static float[] StakeW => Sc(StakeWBase);
|
||||||
private static readonly TextAnchor[] StakeAligns = { TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight };
|
private static readonly TextAnchor[] StakeAligns = { TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight };
|
||||||
|
|
||||||
/// <summary>质押页:革命军 CRA 质押池(日息、解锁排队)和平衡代币 SPARK 的铸销。都是合约调用,要在钱包上签名。</summary>
|
/// <summary>质押页:革命军 CRA 质押池(日息、解锁排队)和平衡代币 SPARK 的铸销。都是合约调用,要在钱包上签名。</summary>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace Decentralized
|
|||||||
private static void OnGenerate(StoreClientManager manager)
|
private static void OnGenerate(StoreClientManager manager)
|
||||||
{
|
{
|
||||||
var d = Session.Data;
|
var d = Session.Data;
|
||||||
if (d == null || !Unlock.IsUnlocked) return;
|
if (d == null || !Unlock.IsUnlocked || !Settings.TumblerStory) return;
|
||||||
Log.Guard("Tumbler", () =>
|
Log.Guard("Tumbler", () =>
|
||||||
{
|
{
|
||||||
int day = StoreStation.GetDayCounter();
|
int day = StoreStation.GetDayCounter();
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ namespace Decentralized
|
|||||||
{
|
{
|
||||||
string clip = Clipboard.Paste();
|
string clip = Clipboard.Paste();
|
||||||
// 被投毒的钱包:固件里的木马有时把粘贴的地址换成长得很像的骗子地址
|
// 被投毒的钱包:固件里的木马有时把粘贴的地址换成长得很像的骗子地址
|
||||||
if (w.Poisoned && ChainAddress.Valid(clip) && Session.Data.Rng.Chance(Tuning.TxHijackChance)) { clip = ChainAddress.Lookalike(clip, Session.Data.Rng); Log.Info($"剪贴板木马:{Clipboard.Paste()} → {clip}"); }
|
if (w.Poisoned && ChainAddress.Valid(clip) && Session.Data.Rng.Chance(Scales.R(Tuning.TxHijackChance))) { clip = ChainAddress.Lookalike(clip, Session.Data.Rng); Log.Info($"剪贴板木马:{Clipboard.Paste()} → {clip}"); }
|
||||||
dev.To = clip; dev.Note = null; Open(dev);
|
dev.To = clip; dev.Note = null; Open(dev);
|
||||||
});
|
});
|
||||||
b.End();
|
b.End();
|
||||||
@@ -546,7 +546,7 @@ namespace Decentralized
|
|||||||
var mine = d.Own(tx.To);
|
var mine = d.Own(tx.To);
|
||||||
if (mine != null) TxRules.Record(mine.History, new TxRecord { Addr = w.Address, Asset = tx.Asset, Amount = amt, Day = day, In = true });
|
if (mine != null) TxRules.Record(mine.History, new TxRecord { Addr = w.Address, Asset = tx.Asset, Amount = amt, Day = day, In = true });
|
||||||
// 地址投毒:链上公开的转账被人盯上,过一两天会收到一笔来自相似地址的 0 币转入
|
// 地址投毒:链上公开的转账被人盯上,过一两天会收到一笔来自相似地址的 0 币转入
|
||||||
else if (d.Rng.Chance(Tuning.TxPoisonChance)) d.PoisonPlans.Add(new PoisonPlan { Wallet = w.Address, Target = tx.To, Day = day + d.Rng.Range(1, Tuning.TxPoisonDelayMax) });
|
else if (d.Rng.Chance(Scales.R(Tuning.TxPoisonChance))) d.PoisonPlans.Add(new PoisonPlan { Wallet = w.Address, Target = tx.To, Day = day + d.Rng.Range(1, Tuning.TxPoisonDelayMax) });
|
||||||
}
|
}
|
||||||
if (tx.Bill != null && !tx.Bill.Contract)
|
if (tx.Bill != null && !tx.Bill.Contract)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1565,6 +1565,22 @@ internal static class Program
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Test("mod 设置倍率:解析、封顶、挖矿产出按倍率", () =>
|
||||||
|
{
|
||||||
|
Check(Scales.Parse("×1.5") == 1.5 && Scales.Parse("×0.5") == 0.5 && Scales.Parse("x") == 1 && Scales.Parse(null) == 1, "解析档位文字");
|
||||||
|
foreach (var c in Scales.Choices) Check(Scales.Parse(c) > 0, "档位都能解析:" + c);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Scales.Risk = 2; Check(Math.Abs(Scales.R(0.3) - 0.6) < 1e-9 && Scales.R(0.8) == 1, "风险倍率乘概率并封顶 1");
|
||||||
|
Scales.Risk = 1;
|
||||||
|
var n = new Network();
|
||||||
|
Scales.Mining = 1; double a = n.Mine(100);
|
||||||
|
Scales.Mining = 2; double b2 = n.Mine(100);
|
||||||
|
Check(a > 0 && Math.Abs(b2 - 2 * a) < 1e-9, "挖矿产出按倍率");
|
||||||
|
}
|
||||||
|
finally { Scales.Risk = 1; Scales.Mining = 1; }
|
||||||
|
});
|
||||||
|
|
||||||
Test("预测市场:先验概率", () =>
|
Test("预测市场:先验概率", () =>
|
||||||
{
|
{
|
||||||
Check(Math.Abs(PredictMath.PurityAtLeast(8500, 8000, 8999) - 0.5) < 1e-9, "纯度 8500 以上一半");
|
Check(Math.Abs(PredictMath.PurityAtLeast(8500, 8000, 8999) - 0.5) < 1e-9, "纯度 8500 以上一半");
|
||||||
|
|||||||
Reference in new issue
Block a user