mirror of
https://github.com/oshima-studios/OshimaGameModule.git
synced 2026-06-04 19:42:13 +00:00
添加新功能
This commit is contained in:
parent
a769cdf25c
commit
67187200ca
@ -0,0 +1,89 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Milimoe.FunGame.Core.Entity;
|
||||||
|
using Milimoe.FunGame.Core.Interface.Entity;
|
||||||
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
|
using Oshima.FunGame.OshimaModules.BusinessSimulation.Interface;
|
||||||
|
|
||||||
|
namespace Oshima.FunGame.OshimaModules.BusinessSimulation.Entity
|
||||||
|
{
|
||||||
|
public class HumanResource : BaseEntity, IDescription, IBusinessSimulationEntity
|
||||||
|
{
|
||||||
|
public virtual QualityType QualityType { get; set; } = QualityType.White;
|
||||||
|
public virtual HumanResourceType HumanResourceType { get; set; } = HumanResourceType.General;
|
||||||
|
public virtual string Description { get; set; } = "";
|
||||||
|
public virtual string GeneralDescription { get; set; } = "";
|
||||||
|
public virtual string BackgroundStory { get; set; } = "";
|
||||||
|
public virtual string Category { get; set; } = "";
|
||||||
|
|
||||||
|
public bool Enable { get; set; } = true;
|
||||||
|
public Dictionary<string, string> SkillInfo { get; set; } = [];
|
||||||
|
|
||||||
|
public string ToString(bool isShowGeneralDescription, bool isShowInStore = false)
|
||||||
|
{
|
||||||
|
StringBuilder builder = new();
|
||||||
|
|
||||||
|
builder.AppendLine($"【{Name}】");
|
||||||
|
|
||||||
|
string itemquality = ItemSet.GetQualityTypeName(QualityType);
|
||||||
|
string itemtype = GetHumanResourceTypeName(HumanResourceType);
|
||||||
|
if (itemtype != "") itemtype = $" {itemtype}";
|
||||||
|
|
||||||
|
builder.AppendLine($"{itemquality + itemtype}");
|
||||||
|
if (!string.IsNullOrWhiteSpace(Category)) builder.AppendLine(Category);
|
||||||
|
|
||||||
|
if (isShowGeneralDescription && GeneralDescription != "")
|
||||||
|
{
|
||||||
|
builder.AppendLine("描述:" + GeneralDescription);
|
||||||
|
}
|
||||||
|
else if (Description != "")
|
||||||
|
{
|
||||||
|
builder.AppendLine("描述:" + Description);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SkillInfo.Count > 0)
|
||||||
|
{
|
||||||
|
builder.AppendLine("=== 技能 ===");
|
||||||
|
foreach (var skill in SkillInfo)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"{skill.Key}{(!string.IsNullOrWhiteSpace(skill.Value) ? $":{skill.Value}" : "")}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BackgroundStory != "")
|
||||||
|
{
|
||||||
|
builder.AppendLine($"\"{BackgroundStory}\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(IBaseEntity? other) => other is HumanResource hr && GetIdName() == hr.GetIdName();
|
||||||
|
|
||||||
|
public static string GetHumanResourceTypeName(HumanResourceType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
HumanResourceType.General => "通用人力资源",
|
||||||
|
HumanResourceType.Employee => "员工",
|
||||||
|
HumanResourceType.Manager => "经理",
|
||||||
|
HumanResourceType.Team => "团队",
|
||||||
|
HumanResourceType.CooperationPartner => "合作伙伴",
|
||||||
|
_ => "未知人力资源"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void UpdateSkillInfo()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum HumanResourceType
|
||||||
|
{
|
||||||
|
General,
|
||||||
|
Employee,
|
||||||
|
Manager,
|
||||||
|
Team,
|
||||||
|
CooperationPartner
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
namespace Oshima.FunGame.OshimaModules.BusinessSimulation.Entity
|
||||||
|
{
|
||||||
|
public class DeviceMovableProperty : MovableProperty
|
||||||
|
{
|
||||||
|
public override MovablePropertyType MovablePropertyType => MovablePropertyType.Device;
|
||||||
|
|
||||||
|
public virtual string DeviceType { get; set; } = "";
|
||||||
|
public virtual double ProductionSpeedBonusPercent { get; set; } = 0;
|
||||||
|
public virtual double EnergyConsumption { get; set; } = 0;
|
||||||
|
public virtual int DeviceLevel { get; set; } = 1;
|
||||||
|
|
||||||
|
public List<string> CompatibleItem { get; set; } = [];
|
||||||
|
|
||||||
|
public DeviceMovableProperty()
|
||||||
|
{
|
||||||
|
UpdateSkillInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateSkillInfo()
|
||||||
|
{
|
||||||
|
base.UpdateSkillInfo();
|
||||||
|
if (ProductionSpeedBonusPercent > 0) SkillInfo["生产速度"] = $"{ProductionSpeedBonusPercent:0.##}%";
|
||||||
|
if (EnergyConsumption > 0) SkillInfo["能耗"] = $"{EnergyConsumption:0.##} 每日";
|
||||||
|
if (DeviceLevel > 0) SkillInfo["设备等级"] = $"{DeviceLevel}";
|
||||||
|
if (CompatibleItem.Count > 0) SkillInfo["适用货物"] = $"\r\n{string.Join("\r\n", CompatibleItem)}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,136 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Milimoe.FunGame.Core.Entity;
|
||||||
|
using Milimoe.FunGame.Core.Interface.Entity;
|
||||||
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
|
using Oshima.FunGame.OshimaModules.BusinessSimulation.Interface;
|
||||||
|
|
||||||
|
namespace Oshima.FunGame.OshimaModules.BusinessSimulation.Entity
|
||||||
|
{
|
||||||
|
public class MovableProperty : BaseEntity, IDescription, IBusinessSimulationEntity
|
||||||
|
{
|
||||||
|
public virtual QualityType QualityType { get; set; } = QualityType.White;
|
||||||
|
public virtual MovablePropertyType MovablePropertyType { get; set; } = MovablePropertyType.General;
|
||||||
|
public virtual string Description { get; set; } = "";
|
||||||
|
public virtual string GeneralDescription { get; set; } = "";
|
||||||
|
public virtual string BackgroundStory { get; set; } = "";
|
||||||
|
public virtual double Price { get; set; } = 0;
|
||||||
|
public virtual string Category { get; set; } = "";
|
||||||
|
public virtual double MaintenanceCost { get; set; } = 0;
|
||||||
|
public virtual string MaintenanceUnit { get; set; } = "每日";
|
||||||
|
|
||||||
|
public bool Enable { get; set; } = true;
|
||||||
|
public bool IsPurchasable { get; set; } = true;
|
||||||
|
public double OriginalPrice { get; set; } = 0;
|
||||||
|
public bool IsSellable { get; set; } = true;
|
||||||
|
public DateTime NextSellableTime { get; set; } = DateTime.MinValue;
|
||||||
|
public Dictionary<string, string> SkillInfo { get; set; } = [];
|
||||||
|
|
||||||
|
public string ToString(bool isShowGeneralDescription, bool isShowInStore = false)
|
||||||
|
{
|
||||||
|
StringBuilder builder = new();
|
||||||
|
|
||||||
|
builder.AppendLine($"【{Name}】");
|
||||||
|
|
||||||
|
string itemquality = ItemSet.GetQualityTypeName(QualityType);
|
||||||
|
string itemtype = GetMovablePropertyTypeName(MovablePropertyType);
|
||||||
|
if (itemtype != "") itemtype = $" {itemtype}";
|
||||||
|
|
||||||
|
builder.AppendLine($"{itemquality + itemtype}");
|
||||||
|
if (!string.IsNullOrWhiteSpace(Category)) builder.AppendLine(Category);
|
||||||
|
|
||||||
|
if (isShowInStore && Price > 0)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"售价:{Price:0.##} {GameplayEquilibriumConstant.InGameCurrency}");
|
||||||
|
}
|
||||||
|
else if (Price > 0)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"回收价:{Price:0.##} {GameplayEquilibriumConstant.InGameCurrency}");
|
||||||
|
}
|
||||||
|
if (OriginalPrice > 0) builder.AppendLine($"在商店或市场中出售时,售价不得超过原价:{OriginalPrice:0.##} {GameplayEquilibriumConstant.InGameCurrency}");
|
||||||
|
|
||||||
|
if (isShowInStore)
|
||||||
|
{
|
||||||
|
if (IsSellable)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"购买此资产后可立即出售");
|
||||||
|
}
|
||||||
|
else if (NextSellableTime != DateTime.MinValue)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"购买此资产后,将在 {NextSellableTime.ToString(General.GeneralDateTimeFormatChinese)} 后可出售");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (IsSellable)
|
||||||
|
{
|
||||||
|
builder.AppendLine("可出售");
|
||||||
|
}
|
||||||
|
else if (!IsSellable && NextSellableTime != DateTime.MinValue)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"此资产将在 {NextSellableTime.ToString(General.GeneralDateTimeFormatChinese)} 后可出售");
|
||||||
|
}
|
||||||
|
else if (!IsSellable)
|
||||||
|
{
|
||||||
|
builder.AppendLine("不可出售");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MaintenanceCost > 0)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"维护费:{(MaintenanceUnit != "" ? MaintenanceUnit : "每日")} {MaintenanceCost:0.##} {GameplayEquilibriumConstant.InGameCurrency}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isShowGeneralDescription && GeneralDescription != "")
|
||||||
|
{
|
||||||
|
builder.AppendLine("资产描述:" + GeneralDescription);
|
||||||
|
}
|
||||||
|
else if (Description != "")
|
||||||
|
{
|
||||||
|
builder.AppendLine("资产描述:" + Description);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SkillInfo.Count > 0)
|
||||||
|
{
|
||||||
|
builder.AppendLine("=== 资产技能 ===");
|
||||||
|
foreach (var skill in SkillInfo)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"{skill.Key}{(!string.IsNullOrWhiteSpace(skill.Value) ? $":{skill.Value}" : "")}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BackgroundStory != "")
|
||||||
|
{
|
||||||
|
builder.AppendLine($"\"{BackgroundStory}\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(IBaseEntity? other) => other is MovableProperty mp && GetIdName() == mp.GetIdName();
|
||||||
|
|
||||||
|
public static string GetMovablePropertyTypeName(MovablePropertyType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
MovablePropertyType.General => "通用资产",
|
||||||
|
MovablePropertyType.OfficeAsset => "办公资产",
|
||||||
|
MovablePropertyType.Device => "设备资产",
|
||||||
|
MovablePropertyType.Vehicle => "载具资产",
|
||||||
|
_ => "未知资产"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void UpdateSkillInfo()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum MovablePropertyType
|
||||||
|
{
|
||||||
|
General,
|
||||||
|
OfficeAsset,
|
||||||
|
Device,
|
||||||
|
Vehicle
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
namespace Oshima.FunGame.OshimaModules.BusinessSimulation.Entity
|
||||||
|
{
|
||||||
|
public class OfficeAssetMovableProperty : MovableProperty
|
||||||
|
{
|
||||||
|
public override MovablePropertyType MovablePropertyType => MovablePropertyType.OfficeAsset;
|
||||||
|
|
||||||
|
public virtual string OfficeAssetType { get; set; } = "";
|
||||||
|
public virtual int ComfortBonus { get; set; } = 0;
|
||||||
|
public virtual double EfficiencyBonusPercent { get; set; } = 0;
|
||||||
|
|
||||||
|
public OfficeAssetMovableProperty()
|
||||||
|
{
|
||||||
|
UpdateSkillInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateSkillInfo()
|
||||||
|
{
|
||||||
|
if (ComfortBonus > 0) SkillInfo["舒适度"] = $"{ComfortBonus}";
|
||||||
|
if (EfficiencyBonusPercent > 0) SkillInfo["办公效率"] = $"{EfficiencyBonusPercent * 100:0.##}%";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,101 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Milimoe.FunGame.Core.Entity;
|
||||||
|
|
||||||
|
namespace Oshima.FunGame.OshimaModules.BusinessSimulation.Entity
|
||||||
|
{
|
||||||
|
public class VehicleMovableProperty : MovableProperty
|
||||||
|
{
|
||||||
|
public override string Category => "载具";
|
||||||
|
public override MovablePropertyType MovablePropertyType => MovablePropertyType.Vehicle;
|
||||||
|
|
||||||
|
public virtual string VehicleType { get; set; } = "";
|
||||||
|
public virtual int PassengerCapacity { get; set; } = 0;
|
||||||
|
public virtual int CargoCapacity { get; set; } = 0;
|
||||||
|
public virtual double TransportTime { get; set; } = 5;
|
||||||
|
public virtual double TransportCost { get; set; } = 0;
|
||||||
|
public virtual double LoadingTime { get; set; } = 0;
|
||||||
|
public virtual List<RealEstate> ParkingAvailable { get; set; } = [];
|
||||||
|
|
||||||
|
public bool Enabled
|
||||||
|
{
|
||||||
|
get => !IsParked && field;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
public bool IsParked { get; set; } = false;
|
||||||
|
public PackingRequirement ParkingIn { get; set; } = new(Guid.Empty, "");
|
||||||
|
public List<TransportSchedule> TransportScheduleList { get; } = [];
|
||||||
|
public List<Character> VehicleOperations { get; } = [];
|
||||||
|
|
||||||
|
public VehicleMovableProperty()
|
||||||
|
{
|
||||||
|
UpdateSkillInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateSkillInfo()
|
||||||
|
{
|
||||||
|
base.UpdateSkillInfo();
|
||||||
|
if (PassengerCapacity > 0) SkillInfo["乘客定员"] = $"{PassengerCapacity}";
|
||||||
|
if (CargoCapacity > 0) SkillInfo["载货容量"] = $"{CargoCapacity}";
|
||||||
|
if (TransportTime > 0) SkillInfo["运输时间"] = $"{TransportTime:0.##} 分钟 / 次";
|
||||||
|
if (TransportCost > 0) SkillInfo["运输成本"] = $"{TransportCost:0.##} {GameplayEquilibriumConstant.InGameCurrency} / 次";
|
||||||
|
if (LoadingTime > 0) SkillInfo["装载时间"] = $"{LoadingTime:0.##} 分钟 / 百件货物";
|
||||||
|
if (VehicleOperations.Count > 0) SkillInfo["车务担当"] = $"\r\n{string.Join("\r\n", VehicleOperations.Select(c => c.ToStringWithLevelWithOutUser()))}";
|
||||||
|
if (TransportScheduleList.Count > 0)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
SkillInfo["时间表"] = $"";
|
||||||
|
foreach (TransportSchedule ts in TransportScheduleList)
|
||||||
|
{
|
||||||
|
SkillInfo[$"第 {++count} 站"] = $"\r\n{ts}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SkillInfo["停放于"] = $"\r\n{ParkingIn}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum LoadingStrategy
|
||||||
|
{
|
||||||
|
Full = 1,
|
||||||
|
Half = 2,
|
||||||
|
AllowForTime = 3,
|
||||||
|
SpecifiedCapacity = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TransportSchedule(Guid esId, string esName)
|
||||||
|
{
|
||||||
|
public Guid TargetRealEstate { get; set; } = esId;
|
||||||
|
public string TargetRealEstateName { get; set; } = esName;
|
||||||
|
|
||||||
|
public LoadingStrategy LoadingStrategy { get; set; } = LoadingStrategy.Full;
|
||||||
|
public int WaitingTime { get; set; } = 0;
|
||||||
|
public double SpecifiedCapacity { get; set; } = 0;
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new();
|
||||||
|
sb.AppendLine($"目标地点:{TargetRealEstateName}");
|
||||||
|
sb.AppendLine($"装载策略:{GetLoadingStrategyName(LoadingStrategy)}");
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetLoadingStrategyName(LoadingStrategy ls)
|
||||||
|
{
|
||||||
|
return ls switch
|
||||||
|
{
|
||||||
|
LoadingStrategy.Full => "满载",
|
||||||
|
LoadingStrategy.Half => "半载",
|
||||||
|
LoadingStrategy.AllowForTime => $"等待 {WaitingTime} 分钟",
|
||||||
|
LoadingStrategy.SpecifiedCapacity => $"装载容量达到 {SpecifiedCapacity} 件",
|
||||||
|
_ => "未知装载策略"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PackingRequirement(Guid esId, string esName)
|
||||||
|
{
|
||||||
|
public Guid TargetRealEstate { get; set; } = esId;
|
||||||
|
public string TargetRealEstateName { get; set; } = esName;
|
||||||
|
|
||||||
|
public override string ToString() => TargetRealEstateName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
namespace Oshima.FunGame.OshimaModules.BusinessSimulation.Entity
|
||||||
|
{
|
||||||
|
public class CommercialRealEstate : RealEstate
|
||||||
|
{
|
||||||
|
public override RealEstateType RealEstateType => RealEstateType.Commercial;
|
||||||
|
|
||||||
|
public virtual string CommercialType { get; set; } = "";
|
||||||
|
public virtual double Attractiveness { get; set; } = 0;
|
||||||
|
|
||||||
|
public CommercialRealEstate()
|
||||||
|
{
|
||||||
|
UpdateSkillInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateSkillInfo()
|
||||||
|
{
|
||||||
|
if (Attractiveness > 0) SkillInfo["吸引力"] = $"{Attractiveness * 100:0.##}%";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Shop : CommercialRealEstate
|
||||||
|
{
|
||||||
|
public override string CommercialType => "商铺";
|
||||||
|
|
||||||
|
public virtual int ShelfCount { get; set; } = 0;
|
||||||
|
public virtual int InventoryCapacity { get; set; } = 0;
|
||||||
|
public virtual int TruckCount { get; set; } = 0;
|
||||||
|
|
||||||
|
public Shop()
|
||||||
|
{
|
||||||
|
UpdateSkillInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateSkillInfo()
|
||||||
|
{
|
||||||
|
base.UpdateSkillInfo();
|
||||||
|
if (ShelfCount > 0) SkillInfo["货架数量"] = $"{ShelfCount}";
|
||||||
|
if (InventoryCapacity > 0) SkillInfo["库存容量"] = $"{InventoryCapacity}";
|
||||||
|
if (TruckCount > 0) SkillInfo["附赠货车"] = $"{TruckCount}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ParkingLot : CommercialRealEstate
|
||||||
|
{
|
||||||
|
public override string CommercialType => "停车场";
|
||||||
|
|
||||||
|
public virtual int BonusTruckCount { get; set; } = 0;
|
||||||
|
public virtual int MaxTruckCapacity { get; set; } = 0;
|
||||||
|
public virtual int PublicSpots { get; set; } = 0;
|
||||||
|
public virtual double HourlyParkingFee { get; set; } = 0;
|
||||||
|
|
||||||
|
public int CurrentTruckCount { get; set; } = 0;
|
||||||
|
public int CurrentPublicVehicles { get; set; } = 0;
|
||||||
|
|
||||||
|
public ParkingLot()
|
||||||
|
{
|
||||||
|
UpdateSkillInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateSkillInfo()
|
||||||
|
{
|
||||||
|
base.UpdateSkillInfo();
|
||||||
|
if (BonusTruckCount > 0) SkillInfo["附赠货车"] = $"{BonusTruckCount}";
|
||||||
|
if (MaxTruckCapacity > 0) SkillInfo["货车停放"] = $"{CurrentTruckCount} / {MaxTruckCapacity}";
|
||||||
|
if (PublicSpots > 0) SkillInfo["公共车位"] = $"{CurrentPublicVehicles} / {PublicSpots}";
|
||||||
|
if (HourlyParkingFee > 0) SkillInfo["公共停车费收入"] = $"{HourlyParkingFee:0.##} {GameplayEquilibriumConstant.InGameCurrency} / 辆每小时";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Oshima.FunGame.OshimaModules.BusinessSimulation.Entity
|
||||||
|
{
|
||||||
|
public class IndustrialRealEstate : RealEstate
|
||||||
|
{
|
||||||
|
public override RealEstateType RealEstateType => RealEstateType.Industrial;
|
||||||
|
|
||||||
|
public virtual string IndustryType { get; set; } = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Factory : IndustrialRealEstate
|
||||||
|
{
|
||||||
|
public override string IndustryType => "工厂";
|
||||||
|
|
||||||
|
public virtual int TruckCount { get; set; } = 0;
|
||||||
|
public virtual int MaxProductionLines { get; } = 1;
|
||||||
|
public virtual double CostPerLine { get; set; } = 0;
|
||||||
|
|
||||||
|
public Dictionary<int, ProductionLine> ProductionLines { get; set; } = [];
|
||||||
|
public int ActiveLines => ProductionLines.Values.Count(pl => pl.Active);
|
||||||
|
public override double MaintenanceCost => ActiveLines * CostPerLine;
|
||||||
|
public override string MaintenanceUnit => "每条有效流水线每日";
|
||||||
|
|
||||||
|
public Factory(int maxProductionLines = 1)
|
||||||
|
{
|
||||||
|
Category = "工厂";
|
||||||
|
MaxProductionLines = maxProductionLines;
|
||||||
|
// 注意:工厂一经创建,流水线数量便无法更改
|
||||||
|
for (int i = 0; i < MaxProductionLines; i++)
|
||||||
|
{
|
||||||
|
ProductionLines[i] = new();
|
||||||
|
}
|
||||||
|
UpdateSkillInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateSkillInfo()
|
||||||
|
{
|
||||||
|
if (TruckCount > 0) SkillInfo["附赠货车"] = $"{TruckCount}";
|
||||||
|
if (MaxProductionLines > 0)
|
||||||
|
{
|
||||||
|
SkillInfo["流水线数量"] = $"{ActiveLines} / {MaxProductionLines}";
|
||||||
|
if (ProductionLines.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var line in ProductionLines)
|
||||||
|
{
|
||||||
|
SkillInfo[$"流水线 {line.Key}"] = $"\r\n{line.Value}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ProductionLine
|
||||||
|
{
|
||||||
|
public bool Active { get; set; } = false;
|
||||||
|
public string ItemName { get; set; } = "";
|
||||||
|
public double CountPerMinute => 60.0 / UnitProductionTime;
|
||||||
|
public int UnitProductionTime { get; set; } = 0;
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new();
|
||||||
|
sb.AppendLine($"是否启用:{(Active ? "是" : "否")}");
|
||||||
|
sb.AppendLine($"生产货物:{ItemName}");
|
||||||
|
sb.AppendLine($"生产时间:{UnitProductionTime} 秒 / 件");
|
||||||
|
sb.AppendLine($"理论产量:{CountPerMinute:0.##} 件 / 分钟");
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
138
OshimaModules/BusinessSimulation/Entity/RealEstate/RealEstate.cs
Normal file
138
OshimaModules/BusinessSimulation/Entity/RealEstate/RealEstate.cs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Milimoe.FunGame.Core.Entity;
|
||||||
|
using Milimoe.FunGame.Core.Interface.Entity;
|
||||||
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
|
using Oshima.FunGame.OshimaModules.BusinessSimulation.Interface;
|
||||||
|
|
||||||
|
namespace Oshima.FunGame.OshimaModules.BusinessSimulation.Entity
|
||||||
|
{
|
||||||
|
public class RealEstate : BaseEntity, IDescription, IBusinessSimulationEntity
|
||||||
|
{
|
||||||
|
public virtual QualityType QualityType { get; set; } = QualityType.White;
|
||||||
|
public virtual RealEstateType RealEstateType { get; set; } = RealEstateType.General;
|
||||||
|
public virtual string Description { get; set; } = "";
|
||||||
|
public virtual string GeneralDescription { get; set; } = "";
|
||||||
|
public virtual string BackgroundStory { get; set; } = "";
|
||||||
|
public virtual double Price { get; set; } = 0;
|
||||||
|
public virtual string Category { get; set; } = "";
|
||||||
|
public virtual double MaintenanceCost { get; set; } = 0;
|
||||||
|
public virtual string MaintenanceUnit { get; set; } = "每日";
|
||||||
|
|
||||||
|
public bool Enable { get; set; } = true;
|
||||||
|
public bool IsPurchasable { get; set; } = true;
|
||||||
|
public double OriginalPrice { get; set; } = 0;
|
||||||
|
public bool IsSellable { get; set; } = true;
|
||||||
|
public DateTime NextSellableTime { get; set; } = DateTime.MinValue;
|
||||||
|
public Dictionary<string, string> SkillInfo { get; set; } = [];
|
||||||
|
|
||||||
|
public string ToString(bool isShowGeneralDescription, bool isShowInStore = false)
|
||||||
|
{
|
||||||
|
StringBuilder builder = new();
|
||||||
|
|
||||||
|
builder.AppendLine($"【{Name}】");
|
||||||
|
|
||||||
|
string itemquality = ItemSet.GetQualityTypeName(QualityType);
|
||||||
|
string itemtype = GetRealEstateTypeName(RealEstateType);
|
||||||
|
if (itemtype != "") itemtype = $" {itemtype}";
|
||||||
|
|
||||||
|
builder.AppendLine($"{itemquality + itemtype}");
|
||||||
|
if (!string.IsNullOrWhiteSpace(Category)) builder.AppendLine(Category);
|
||||||
|
|
||||||
|
if (isShowInStore && Price > 0)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"售价:{Price:0.##} {GameplayEquilibriumConstant.InGameCurrency}");
|
||||||
|
}
|
||||||
|
else if (Price > 0)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"回收价:{Price:0.##} {GameplayEquilibriumConstant.InGameCurrency}");
|
||||||
|
}
|
||||||
|
if (OriginalPrice > 0) builder.AppendLine($"在商店或市场中出售时,售价不得超过原价:{OriginalPrice:0.##} {GameplayEquilibriumConstant.InGameCurrency}");
|
||||||
|
|
||||||
|
if (isShowInStore)
|
||||||
|
{
|
||||||
|
if (IsSellable)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"购买此资产后可立即出售");
|
||||||
|
}
|
||||||
|
else if (NextSellableTime != DateTime.MinValue)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"购买此资产后,将在 {NextSellableTime.ToString(General.GeneralDateTimeFormatChinese)} 后可出售");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (IsSellable)
|
||||||
|
{
|
||||||
|
builder.AppendLine("可出售");
|
||||||
|
}
|
||||||
|
else if (!IsSellable && NextSellableTime != DateTime.MinValue)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"此资产将在 {NextSellableTime.ToString(General.GeneralDateTimeFormatChinese)} 后可出售");
|
||||||
|
}
|
||||||
|
else if (!IsSellable)
|
||||||
|
{
|
||||||
|
builder.AppendLine("不可出售");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MaintenanceCost > 0)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"维护费:{(MaintenanceUnit != "" ? MaintenanceUnit : "每日")} {MaintenanceCost:0.##} {GameplayEquilibriumConstant.InGameCurrency}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isShowGeneralDescription && GeneralDescription != "")
|
||||||
|
{
|
||||||
|
builder.AppendLine("资产描述:" + GeneralDescription);
|
||||||
|
}
|
||||||
|
else if (Description != "")
|
||||||
|
{
|
||||||
|
builder.AppendLine("资产描述:" + Description);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SkillInfo.Count > 0)
|
||||||
|
{
|
||||||
|
builder.AppendLine("=== 资产技能 ===");
|
||||||
|
foreach (var skill in SkillInfo)
|
||||||
|
{
|
||||||
|
builder.AppendLine($"{skill.Key}{(!string.IsNullOrWhiteSpace(skill.Value) ? $":{skill.Value}" : "")}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BackgroundStory != "")
|
||||||
|
{
|
||||||
|
builder.AppendLine($"\"{BackgroundStory}\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(IBaseEntity? other) => other is RealEstate re && GetIdName() == re.GetIdName();
|
||||||
|
|
||||||
|
public static string GetRealEstateTypeName(RealEstateType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
RealEstateType.General => "通用资产",
|
||||||
|
RealEstateType.Residential => "住宅资产",
|
||||||
|
RealEstateType.Commercial => "商业资产",
|
||||||
|
RealEstateType.Industrial => "工业资产",
|
||||||
|
RealEstateType.Warehouse => "仓储资产",
|
||||||
|
_ => "未知资产"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void UpdateSkillInfo()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum RealEstateType
|
||||||
|
{
|
||||||
|
General,
|
||||||
|
Residential,
|
||||||
|
Commercial,
|
||||||
|
Industrial,
|
||||||
|
Warehouse
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
using Milimoe.FunGame.Core.Entity;
|
||||||
|
|
||||||
|
namespace Oshima.FunGame.OshimaModules.BusinessSimulation.Entity
|
||||||
|
{
|
||||||
|
public class ResidentialRealEstate : RealEstate
|
||||||
|
{
|
||||||
|
public override RealEstateType RealEstateType => RealEstateType.Residential;
|
||||||
|
|
||||||
|
public virtual int MaxResidents { get; set; } = 0;
|
||||||
|
public virtual int ExperiencePerMinute { get; set; } = 0;
|
||||||
|
public virtual double RegenerationBonus { get; set; } = 0;
|
||||||
|
public virtual int ExtraInventorySlots { get; set; } = 0;
|
||||||
|
public virtual int FreeReviveCount { get; set; } = 0;
|
||||||
|
public virtual double CostPerResident { get; set; } = 0;
|
||||||
|
|
||||||
|
public int CurrentUsedFreeReviveCount { get; set; } = 0;
|
||||||
|
public override double MaintenanceCost => Characters.Count * CostPerResident;
|
||||||
|
public override string MaintenanceUnit => "每人每日";
|
||||||
|
public HashSet<Character> Characters { get; } = [];
|
||||||
|
|
||||||
|
public ResidentialRealEstate()
|
||||||
|
{
|
||||||
|
UpdateSkillInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateSkillInfo()
|
||||||
|
{
|
||||||
|
if (MaxResidents > 0) SkillInfo["可入住角色数量"] = $"{Characters.Count} / {MaxResidents}";
|
||||||
|
if (ExperiencePerMinute > 0) SkillInfo["入住角色经验加成"] = $"{ExperiencePerMinute} / 人每分钟";
|
||||||
|
if (RegenerationBonus > 0) SkillInfo["生命/魔法回复速度提升"] = $"{RegenerationBonus * 100:0.##}%";
|
||||||
|
if (ExtraInventorySlots > 0) SkillInfo["额外玩家库存容量"] = $"{ExtraInventorySlots}";
|
||||||
|
if (FreeReviveCount > 0) SkillInfo["免费复活次数"] = $"{CurrentUsedFreeReviveCount} / {FreeReviveCount}";
|
||||||
|
if (Characters.Count > 0) SkillInfo["已入住角色"] = $"\r\n{string.Join("\r\n", Characters.Select(c => c.ToStringWithLevelWithOutUser()))}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
namespace Oshima.FunGame.OshimaModules.BusinessSimulation.Entity
|
||||||
|
{
|
||||||
|
public class WarehouseRealEstate : RealEstate
|
||||||
|
{
|
||||||
|
public override RealEstateType RealEstateType => RealEstateType.Warehouse;
|
||||||
|
|
||||||
|
public virtual int InventoryCapacity { get; set; } = 0;
|
||||||
|
|
||||||
|
public WarehouseRealEstate()
|
||||||
|
{
|
||||||
|
UpdateSkillInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateSkillInfo()
|
||||||
|
{
|
||||||
|
if (InventoryCapacity > 0) SkillInfo["库存容量"] = $"{InventoryCapacity}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
namespace Oshima.FunGame.OshimaModules.BusinessSimulation.Interface
|
||||||
|
{
|
||||||
|
public interface IBusinessSimulationEntity
|
||||||
|
{
|
||||||
|
public string Category { get; }
|
||||||
|
public bool Enable { get; }
|
||||||
|
public Dictionary<string, string> SkillInfo { get; }
|
||||||
|
|
||||||
|
public void UpdateSkillInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
namespace Oshima.FunGame.OshimaModules.BusinessSimulation.Interface
|
||||||
|
{
|
||||||
|
public interface IDescription
|
||||||
|
{
|
||||||
|
public string Description { get; }
|
||||||
|
public string GeneralDescription { get; }
|
||||||
|
public string BackgroundStory { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -84,6 +84,15 @@ namespace Oshima.FunGame.WebAPI.Model
|
|||||||
|
|
||||||
[JsonPropertyName("stage")]
|
[JsonPropertyName("stage")]
|
||||||
public string? Stage { get; set; }
|
public string? Stage { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("team1")]
|
||||||
|
public string? Team1 { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("team2")]
|
||||||
|
public string? Team2 { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("betting_enabled")]
|
||||||
|
public bool? BettingEnabled { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BettingEvent
|
public class BettingEvent
|
||||||
@ -165,6 +174,9 @@ namespace Oshima.FunGame.WebAPI.Model
|
|||||||
|
|
||||||
[JsonPropertyName("updated_at")]
|
[JsonPropertyName("updated_at")]
|
||||||
public DateTime UpdatedAt { get; set; }
|
public DateTime UpdatedAt { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("betting_enabled")]
|
||||||
|
public bool BettingEnabled { get; set; } = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BettingBetRecord
|
public class BettingBetRecord
|
||||||
|
|||||||
@ -48,7 +48,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
|||||||
int id = Convert.ToInt32(row["id"]);
|
int id = Convert.ToInt32(row["id"]);
|
||||||
string name = row["name"].ToString() ?? "";
|
string name = row["name"].ToString() ?? "";
|
||||||
int status = Convert.ToInt32(row["status"]);
|
int status = Convert.ToInt32(row["status"]);
|
||||||
string statusStr = status switch { 0 => "未开始", 1 => "进行中", 2 => "已结束", _ => "未知" };
|
string statusStr = status switch { 0 => "未开始", 1 => "进行中", 2 => "已结束", 3 => "已取消", _ => "未知" };
|
||||||
DateTime startTime = Convert.ToDateTime(row["start_time"]);
|
DateTime startTime = Convert.ToDateTime(row["start_time"]);
|
||||||
DateTime endTime = Convert.ToDateTime(row["end_time"]);
|
DateTime endTime = Convert.ToDateTime(row["end_time"]);
|
||||||
sb.AppendLine($"🏆 [{id}] {name.CreateCmdInput($"赛事详情 {id}")} ({statusStr}:{startTime:yyyy/MM/dd} ~ {endTime:yyyy/MM/dd})");
|
sb.AppendLine($"🏆 [{id}] {name.CreateCmdInput($"赛事详情 {id}")} ({statusStr}:{startTime:yyyy/MM/dd} ~ {endTime:yyyy/MM/dd})");
|
||||||
@ -73,7 +73,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
|||||||
int status = Convert.ToInt32(evt["status"]);
|
int status = Convert.ToInt32(evt["status"]);
|
||||||
DateTime start = Convert.ToDateTime(evt["start_time"]);
|
DateTime start = Convert.ToDateTime(evt["start_time"]);
|
||||||
DateTime end = Convert.ToDateTime(evt["end_time"]);
|
DateTime end = Convert.ToDateTime(evt["end_time"]);
|
||||||
string statusStr = status switch { 0 => "未开始", 1 => "进行中", 2 => "已结束", _ => "未知" };
|
string statusStr = status switch { 0 => "未开始", 1 => "进行中", 2 => "已结束", 3 => "已取消", _ => "未知" };
|
||||||
|
|
||||||
StringBuilder header = new();
|
StringBuilder header = new();
|
||||||
header.AppendLine($"赛事:{name}");
|
header.AppendLine($"赛事:{name}");
|
||||||
@ -115,7 +115,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
|||||||
DateTime deadline = Convert.ToDateTime(row["bet_deadline"]);
|
DateTime deadline = Convert.ToDateTime(row["bet_deadline"]);
|
||||||
string stage = row["stage"].ToString() ?? "";
|
string stage = row["stage"].ToString() ?? "";
|
||||||
string result = row["result"] != DBNull.Value ? row["result"].ToString() ?? "" : "";
|
string result = row["result"] != DBNull.Value ? row["result"].ToString() ?? "" : "";
|
||||||
string mStatusStr = mstatus switch { 0 => "未开始", 1 => "进行中", 2 => "已结束", _ => "未知" };
|
string mStatusStr = mstatus switch { 0 => "未开始", 1 => "进行中", 2 => "已结束", 3 => "已取消", _ => "未知" };
|
||||||
string matchLabel = $"{t1} vs {t2}";
|
string matchLabel = $"{t1} vs {t2}";
|
||||||
string clickableMatch = matchLabel.CreateCmdInput($"比赛详情 {mid}");
|
string clickableMatch = matchLabel.CreateCmdInput($"比赛详情 {mid}");
|
||||||
matches.AppendLine($" [{mid}] {(stage != "" ? $"{stage} " : "")} {clickableMatch} (状态:{mStatusStr}{(result.Trim() != "" ? $", 结果:{result}" : "")}, 截止:{deadline:MM-dd HH:mm})");
|
matches.AppendLine($" [{mid}] {(stage != "" ? $"{stage} " : "")} {clickableMatch} (状态:{mStatusStr}{(result.Trim() != "" ? $", 结果:{result}" : "")}, 截止:{deadline:MM-dd HH:mm})");
|
||||||
@ -179,7 +179,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
|||||||
long eventId = Convert.ToInt64(row["event_id"]);
|
long eventId = Convert.ToInt64(row["event_id"]);
|
||||||
string result = row["result"] != DBNull.Value ? row["result"].ToString() ?? "" : "";
|
string result = row["result"] != DBNull.Value ? row["result"].ToString() ?? "" : "";
|
||||||
|
|
||||||
string statusStr = status switch { 0 => "未开始", 1 => "进行中", 2 => $"已结束 | {result}", _ => "未知" };
|
string statusStr = status switch { 0 => "未开始", 1 => "进行中", 2 => $"已结束 | {result}", 3 => "已取消", _ => "未知" };
|
||||||
string matchLabel = $"{t1} vs {t2}".CreateCmdInput($"比赛详情 {id}");
|
string matchLabel = $"{t1} vs {t2}".CreateCmdInput($"比赛详情 {id}");
|
||||||
|
|
||||||
sb.Append($"[{id}] {matchLabel}");
|
sb.Append($"[{id}] {matchLabel}");
|
||||||
@ -215,6 +215,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
|||||||
long winner = row["winner"] != DBNull.Value ? Convert.ToInt64(row["winner"]) : 0;
|
long winner = row["winner"] != DBNull.Value ? Convert.ToInt64(row["winner"]) : 0;
|
||||||
decimal team1Odds = Convert.ToDecimal(row["team1_win_odds"]);
|
decimal team1Odds = Convert.ToDecimal(row["team1_win_odds"]);
|
||||||
decimal team2Odds = Convert.ToDecimal(row["team2_win_odds"]);
|
decimal team2Odds = Convert.ToDecimal(row["team2_win_odds"]);
|
||||||
|
bool bettingEnabled = Convert.ToBoolean(row["betting_enabled"]);
|
||||||
|
|
||||||
string eventName = "";
|
string eventName = "";
|
||||||
sql.Parameters["@eid"] = eventId;
|
sql.Parameters["@eid"] = eventId;
|
||||||
@ -240,7 +241,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string statusStr = status switch { 0 => "未开始", 1 => "进行中", 2 => "已结束", _ => "未知" };
|
string statusStr = status switch { 0 => "未开始", 1 => "进行中", 2 => "已结束", 3 => "已取消", _ => "未知" };
|
||||||
StringBuilder sb = new();
|
StringBuilder sb = new();
|
||||||
sb.AppendLine($"比赛 #{matchId}");
|
sb.AppendLine($"比赛 #{matchId}");
|
||||||
if (eventName.Trim() != "")
|
if (eventName.Trim() != "")
|
||||||
@ -260,9 +261,10 @@ namespace Oshima.FunGame.WebAPI.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
DateTime now = DateTime.Now;
|
DateTime now = DateTime.Now;
|
||||||
bool canBet = (status == 0 || status == 1) && now < deadline;
|
bool canBet = (status == 0 || status == 1) && now < deadline && bettingEnabled;
|
||||||
if (canBet) sb.AppendLine($"可用选项:");
|
if (canBet) sb.AppendLine($"可用选项:");
|
||||||
else sb.AppendLine($"该比赛已截止预测。");
|
else if (!bettingEnabled) sb.AppendLine($"🔒 该比赛不开放预测。");
|
||||||
|
else sb.AppendLine($"🔒 预测已锁定。");
|
||||||
|
|
||||||
string GetStatString(int opt)
|
string GetStatString(int opt)
|
||||||
{
|
{
|
||||||
@ -334,9 +336,15 @@ namespace Oshima.FunGame.WebAPI.Services
|
|||||||
DateTime deadline = Convert.ToDateTime(row["bet_deadline"]);
|
DateTime deadline = Convert.ToDateTime(row["bet_deadline"]);
|
||||||
decimal team1Odds = Convert.ToDecimal(row["team1_win_odds"]);
|
decimal team1Odds = Convert.ToDecimal(row["team1_win_odds"]);
|
||||||
decimal team2Odds = Convert.ToDecimal(row["team2_win_odds"]);
|
decimal team2Odds = Convert.ToDecimal(row["team2_win_odds"]);
|
||||||
|
bool bettingEnabled = Convert.ToBoolean(row["betting_enabled"]);
|
||||||
|
if (!bettingEnabled)
|
||||||
|
{
|
||||||
|
error = "该比赛不开放预测。";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (status == 2 || DateTime.Now > deadline)
|
if (status == 2 || DateTime.Now > deadline)
|
||||||
{
|
{
|
||||||
error = "当前比赛已结束或非可预测阶段。";
|
error = "该比赛处于不可预测阶段。";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -429,7 +437,9 @@ namespace Oshima.FunGame.WebAPI.Services
|
|||||||
bool isMvp = available.Contains("mvp", StringComparison.CurrentCultureIgnoreCase);
|
bool isMvp = available.Contains("mvp", StringComparison.CurrentCultureIgnoreCase);
|
||||||
int status = Convert.ToInt32(row["status"]);
|
int status = Convert.ToInt32(row["status"]);
|
||||||
if (status == 2)
|
if (status == 2)
|
||||||
return "比赛已结算。";
|
return "比赛已结算,无法再次结算。";
|
||||||
|
if (status == 3)
|
||||||
|
return "比赛已取消,无法结算。";
|
||||||
|
|
||||||
int winTeam = 3;
|
int winTeam = 3;
|
||||||
if (!isMvp)
|
if (!isMvp)
|
||||||
@ -679,11 +689,11 @@ namespace Oshima.FunGame.WebAPI.Services
|
|||||||
|
|
||||||
// 根据时间,设置比赛状态为未开始
|
// 根据时间,设置比赛状态为未开始
|
||||||
sql.Parameters["@now"] = now;
|
sql.Parameters["@now"] = now;
|
||||||
sql.Execute("UPDATE csbetting_matches SET status = 0 WHERE start_time >= @now");
|
sql.Execute("UPDATE csbetting_matches SET status = 0 WHERE start_time >= @now AND status != 3");
|
||||||
|
|
||||||
// 更新比赛状态:0→1 (进行中),1→2 (已结束) - 注意不要覆盖已结算的比赛(winner 为 null 时视为未结束)
|
// 更新比赛状态:0→1 (进行中),1→2 (已结束) - 注意不要覆盖已结算的比赛(winner 为 null 时视为未结束)
|
||||||
sql.Parameters["@now"] = now;
|
sql.Parameters["@now"] = now;
|
||||||
sql.Execute("UPDATE csbetting_matches SET status = 1 WHERE status = 0 AND start_time <= @now AND bet_deadline < @now AND winner IS NULL");
|
sql.Execute("UPDATE csbetting_matches SET status = 1 WHERE status = 0 AND start_time <= @now AND bet_deadline < @now AND winner IS NULL AND status != 3");
|
||||||
// 对于已经过了开始时间但还没有 winner 且状态为 1 的,可保留为进行中;实际上只要 winner 为 null,状态应为 1(进行中)
|
// 对于已经过了开始时间但还没有 winner 且状态为 1 的,可保留为进行中;实际上只要 winner 为 null,状态应为 1(进行中)
|
||||||
// 如果有结果但 winner 不为 null,管理员应该已经手动结算,状态会设为 2,这里不做额外修改。
|
// 如果有结果但 winner 不为 null,管理员应该已经手动结算,状态会设为 2,这里不做额外修改。
|
||||||
// 安全起见,只更新未开始的,以及当比赛时间已过且无 winner 时自动变成进行中。
|
// 安全起见,只更新未开始的,以及当比赛时间已过且无 winner 时自动变成进行中。
|
||||||
@ -843,12 +853,12 @@ namespace Oshima.FunGame.WebAPI.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 管理员提前结束预测(将未开始比赛标记为进行中)
|
/// 管理员提前结束预测
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool CloseBetting(int matchId, out string message) => UpdateMatch(new()
|
public static bool CloseBetting(int matchId, out string message) => UpdateMatch(new()
|
||||||
{
|
{
|
||||||
MatchId = matchId,
|
MatchId = matchId,
|
||||||
StartTime = DateTime.Now
|
BetDeadline = DateTime.Now
|
||||||
}, out message);
|
}, out message);
|
||||||
|
|
||||||
public static bool UpdateMatch(UpdateMatchRequest request, out string error)
|
public static bool UpdateMatch(UpdateMatchRequest request, out string error)
|
||||||
@ -946,19 +956,34 @@ namespace Oshima.FunGame.WebAPI.Services
|
|||||||
}
|
}
|
||||||
if (request.Description != null)
|
if (request.Description != null)
|
||||||
{
|
{
|
||||||
sql.Parameters["@desc"] = (object?)request.Description ?? "";
|
sql.Parameters["@desc"] = request.Description ?? "";
|
||||||
setClause.Append("description = @desc, ");
|
setClause.Append("description = @desc, ");
|
||||||
}
|
}
|
||||||
if (request.Result != null)
|
if (request.Result != null)
|
||||||
{
|
{
|
||||||
sql.Parameters["@result"] = (object?)request.Result ?? "";
|
sql.Parameters["@result"] = request.Result ?? "";
|
||||||
setClause.Append("result = @result, ");
|
setClause.Append("result = @result, ");
|
||||||
}
|
}
|
||||||
if (request.Stage != null)
|
if (request.Stage != null)
|
||||||
{
|
{
|
||||||
sql.Parameters["@stage"] = (object?)request.Stage ?? "";
|
sql.Parameters["@stage"] = request.Stage ?? "";
|
||||||
setClause.Append("stage = @stage, ");
|
setClause.Append("stage = @stage, ");
|
||||||
}
|
}
|
||||||
|
if (request.Team1 != null)
|
||||||
|
{
|
||||||
|
sql.Parameters["@t1"] = request.Team1 ?? "";
|
||||||
|
setClause.Append("team1_name = @t1, ");
|
||||||
|
}
|
||||||
|
if (request.Team2 != null)
|
||||||
|
{
|
||||||
|
sql.Parameters["@t2"] = request.Team2 ?? "";
|
||||||
|
setClause.Append("team2_name = @t2, ");
|
||||||
|
}
|
||||||
|
if (request.BettingEnabled.HasValue)
|
||||||
|
{
|
||||||
|
sql.Parameters["@betting_enabled"] = request.BettingEnabled.Value ? 1 : 0;
|
||||||
|
setClause.Append("betting_enabled = @betting_enabled, ");
|
||||||
|
}
|
||||||
|
|
||||||
if (setClause.Length == 0)
|
if (setClause.Length == 0)
|
||||||
{
|
{
|
||||||
@ -980,6 +1005,95 @@ namespace Oshima.FunGame.WebAPI.Services
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 取消比赛(管理员操作),退还所有未结算投注的本金,标记比赛状态为已取消
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="matchId">比赛ID</param>
|
||||||
|
/// <param name="error">错误信息</param>
|
||||||
|
/// <returns>是否成功</returns>
|
||||||
|
public static bool CancelMatch(int matchId, out string error)
|
||||||
|
{
|
||||||
|
error = "";
|
||||||
|
using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper();
|
||||||
|
if (sql == null)
|
||||||
|
{
|
||||||
|
error = "数据库连接失败。";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sql.NewTransaction();
|
||||||
|
|
||||||
|
// 1. 获取比赛信息
|
||||||
|
sql.Parameters["@mid"] = matchId;
|
||||||
|
sql.ExecuteDataSet("SELECT id, status FROM csbetting_matches WHERE id = @mid");
|
||||||
|
if (!sql.Success || sql.DataSet.Tables[0].Rows.Count == 0)
|
||||||
|
{
|
||||||
|
error = "比赛不存在。";
|
||||||
|
sql.Rollback();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DataRow row = sql.DataSet.Tables[0].Rows[0];
|
||||||
|
int status = Convert.ToInt32(row["status"]);
|
||||||
|
if (status == 2)
|
||||||
|
{
|
||||||
|
error = "比赛已结束,无法取消。";
|
||||||
|
sql.Rollback();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (status == 3)
|
||||||
|
{
|
||||||
|
error = "比赛已经是取消状态。";
|
||||||
|
sql.Rollback();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取该比赛所有未结算的投注记录
|
||||||
|
sql.Parameters["@mid"] = matchId;
|
||||||
|
sql.ExecuteDataSet("SELECT id, amount FROM csbetting_bet_records WHERE match_id = @mid AND is_settled = 0");
|
||||||
|
if (sql.Success && sql.DataSet.Tables[0].Rows.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (DataRow bet in sql.DataSet.Tables[0].Rows)
|
||||||
|
{
|
||||||
|
long betId = Convert.ToInt64(bet["id"]);
|
||||||
|
long amount = Convert.ToInt64(bet["amount"]);
|
||||||
|
|
||||||
|
// 退还本金(payout = amount),标记为已结算,注明取消,但不自动领取
|
||||||
|
sql.Parameters["@bid"] = betId;
|
||||||
|
sql.Parameters["@payout"] = amount;
|
||||||
|
sql.Execute("UPDATE csbetting_bet_records SET is_settled = 1, payout = @payout, result_note = '比赛取消,退还本金' WHERE id = @bid");
|
||||||
|
if (!sql.Success)
|
||||||
|
{
|
||||||
|
sql.Rollback();
|
||||||
|
error = "退还投注本金失败。";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 更新比赛状态为 3(已取消),并禁止继续投注
|
||||||
|
sql.Parameters["@mid"] = matchId;
|
||||||
|
sql.Execute("UPDATE csbetting_matches SET status = 3, betting_enabled = 0 WHERE id = @mid");
|
||||||
|
if (!sql.Success)
|
||||||
|
{
|
||||||
|
sql.Rollback();
|
||||||
|
error = "更新比赛状态失败。";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sql.Commit();
|
||||||
|
error = $"比赛 {matchId} 已取消,所有未结算投注的本金已退还,请用户通过【预测领奖】领取。";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
sql.Rollback();
|
||||||
|
error = $"取消比赛异常:{ex.Message}";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static (decimal oddsA, decimal oddsB) CalculateOdds(decimal team1WinProbability, decimal margin = 0.08m)
|
public static (decimal oddsA, decimal oddsB) CalculateOdds(decimal team1WinProbability, decimal margin = 0.08m)
|
||||||
{
|
{
|
||||||
if (team1WinProbability <= 0 || team1WinProbability >= 1)
|
if (team1WinProbability <= 0 || team1WinProbability >= 1)
|
||||||
|
|||||||
@ -79,3 +79,8 @@ ALTER TABLE `csbetting_matches`
|
|||||||
-- 为投注记录表增加投注时的赔率字段,确保结算公平
|
-- 为投注记录表增加投注时的赔率字段,确保结算公平
|
||||||
ALTER TABLE `csbetting_bet_records`
|
ALTER TABLE `csbetting_bet_records`
|
||||||
ADD COLUMN `odds_at_bet` DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT '投注时的赔率' AFTER `amount`;
|
ADD COLUMN `odds_at_bet` DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT '投注时的赔率' AFTER `amount`;
|
||||||
|
|
||||||
|
-- 增加比赛是否允许预测字段,以及状态值 3 表示已取消
|
||||||
|
ALTER TABLE `csbetting_matches`
|
||||||
|
MODIFY COLUMN `status` tinyint NOT NULL DEFAULT 0 COMMENT '比赛状态:0=未开始,1=进行中,2=已结束,3=已取消',
|
||||||
|
ADD COLUMN `betting_enabled` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否允许预测:1=允许,0=禁止' AFTER `description`;
|
||||||
|
|||||||
@ -239,7 +239,8 @@ namespace Oshima.FunGame.WebAPI.Controllers
|
|||||||
Team2WinOdds = Convert.ToDecimal(row["team2_win_odds"]),
|
Team2WinOdds = Convert.ToDecimal(row["team2_win_odds"]),
|
||||||
Description = row["description"]?.ToString(),
|
Description = row["description"]?.ToString(),
|
||||||
CreatedAt = Convert.ToDateTime(row["created_at"]),
|
CreatedAt = Convert.ToDateTime(row["created_at"]),
|
||||||
UpdatedAt = Convert.ToDateTime(row["updated_at"])
|
UpdatedAt = Convert.ToDateTime(row["updated_at"]),
|
||||||
|
BettingEnabled = Convert.ToInt32(row["betting_enabled"]) == 1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -338,5 +338,32 @@ namespace Oshima.FunGame.WebAPI.Controllers
|
|||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("cancel-match")]
|
||||||
|
public BotReply CancelMatch([FromQuery] long uid, [FromQuery] int matchId)
|
||||||
|
{
|
||||||
|
MarkdownMessage md = new() { Content = "服务器繁忙,请稍后再试。" };
|
||||||
|
BotReply reply = new() { Markdown = md };
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!FunGameConstant.UserIdAndUsername.TryGetValue(uid, out User? user) || (!user.IsAdmin && !user.IsOperator))
|
||||||
|
{
|
||||||
|
md.Content = "你没有权限执行此操作。";
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CSBettingService.CancelMatch(matchId, out string msg))
|
||||||
|
md.Content = msg;
|
||||||
|
else
|
||||||
|
md.Content = msg;
|
||||||
|
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.LogError(e, "CancelMatch 异常");
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
using System.Security.Cryptography;
|
using Milimoe.FunGame.Core.Entity;
|
||||||
using Milimoe.FunGame.Core.Entity;
|
|
||||||
using Milimoe.FunGame.Core.Library.Constant;
|
using Milimoe.FunGame.Core.Library.Constant;
|
||||||
using Oshima.FunGame.OshimaModules.Models;
|
using Oshima.FunGame.OshimaModules.Models;
|
||||||
using Oshima.FunGame.OshimaServers.Model;
|
using Oshima.FunGame.OshimaServers.Model;
|
||||||
@ -295,11 +294,7 @@ namespace Oshima.FunGame.WebAPI.Services
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 指令:创建比赛 <赛事ID> <队伍1> <队伍2> <阶段> <开始时间> <预测截止时间> [选项列表(逗号分隔)]
|
// 指令:创建比赛 <赛事ID> <队伍1> <队伍2> <阶段> <开始时间> [选项列表(key=value格式,空格分隔)]
|
||||||
// 示例(创建默认形式的比赛):创建比赛 1 NAVI FaZe Quarter-final 2026-03-05 14:00 2026-03-05 13:55
|
|
||||||
// 示例(提供比赛助力选项):创建比赛 1 G2 Vitality Semi-final 2026-04-02 18:00 2026-04-02 17:55 team1_win,team2_win
|
|
||||||
// 示例(提供自定义奖励率):创建比赛 1 G2 Vitality Semi-final 2026-04-02 18:00 2026-04-02 17:55 team1_win=2.5 team2_win=2.5 team1_win,team2_win
|
|
||||||
// 示例(提供队伍1的胜率自动计算奖励率):创建比赛 1 Vitality FaZe Final 2026-05-12 20:00 2026-05-12 19:55 prob=0.6
|
|
||||||
if (e.Detail.StartsWith("创建比赛"))
|
if (e.Detail.StartsWith("创建比赛"))
|
||||||
{
|
{
|
||||||
e.UseNotice = false;
|
e.UseNotice = false;
|
||||||
@ -311,13 +306,15 @@ namespace Oshima.FunGame.WebAPI.Services
|
|||||||
|
|
||||||
string detail = e.Detail.Replace("创建比赛", "").Trim();
|
string detail = e.Detail.Replace("创建比赛", "").Trim();
|
||||||
string[] parts = detail.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
string[] parts = detail.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||||
if (parts.Length < 7) // 最少需要 eventId, team1, team2, stage, start date, start time, deadline date, deadline time
|
if (parts.Length < 6) // 最少需要 eventId, team1, team2, stage, start date, start time
|
||||||
{
|
{
|
||||||
await SendAsync(e, "创建比赛",
|
await SendAsync(e, "创建比赛",
|
||||||
"格式:创建比赛 <赛事ID> <队伍1> <队伍2> <阶段> <开始时间> <预测截止时间> [选项列表(逗号分隔)]\r\n" +
|
"格式:创建比赛 <赛事ID> <队伍1> <队伍2> <阶段> <开始时间> [选项列表(key=value格式,空格分隔)]\r\n" +
|
||||||
"时间格式:yyyy-MM-dd HH:mm(开始/截止各占两段)\r\n" +
|
"开始时间格式:yyyy-MM-dd HH:mm(两段)\r\n" +
|
||||||
"示例:创建比赛 1 NAVI FaZe Quarter-final 2026-03-05 14:00 2026-03-05 13:55\r\n" +
|
"可选参数:ddl=截止时间(需要用双引号包围),opts=选项列表(逗号分隔,默认team1_win,team2_win),team1_win=奖励率,team2_win=奖励率,prob=胜率\r\n" +
|
||||||
"选项默认 team1_win,team2_win ,比分和MVP选项只允许独立添加:score,mvp");
|
"示例:创建比赛 1 NAVI FaZe Quarter-final 2026-03-05 14:00\r\n" +
|
||||||
|
"示例:创建比赛 1 NAVI FaZe Quarter-final 2026-03-05 14:00 ddl=\"2026-03-05 13:55\" opts=team1_win,team2_win\r\n" +
|
||||||
|
"示例:创建比赛 1 G2 Vitality Semi-final 2026-04-02 18:00 ddl=\"2026-04-02 17:55\" opts=team1_win,team2_win team1_win=2.5 prob=0.6");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,54 +331,90 @@ namespace Oshima.FunGame.WebAPI.Services
|
|||||||
// 开始时间(parts[4] + parts[5])
|
// 开始时间(parts[4] + parts[5])
|
||||||
string startDate = parts[4];
|
string startDate = parts[4];
|
||||||
string startTime = parts[5];
|
string startTime = parts[5];
|
||||||
// 截止时间(parts[6] + parts[7] 如果存在)
|
if (!DateTime.TryParseExact(startDate + " " + startTime, "yyyy-MM-dd HH:mm", null, System.Globalization.DateTimeStyles.None, out DateTime startDt))
|
||||||
if (parts.Length < 8)
|
|
||||||
{
|
{
|
||||||
await SendAsync(e, "创建比赛", "预测截止时间需要完整日期和时间,示例:2026-03-05 13:55");
|
await SendAsync(e, "创建比赛", "开始时间格式错误,请使用 yyyy-MM-dd HH:mm(日期和时间分为两段)。");
|
||||||
return true;
|
|
||||||
}
|
|
||||||
string deadlineDate = parts[6];
|
|
||||||
string deadlineTime = parts[7];
|
|
||||||
|
|
||||||
if (!DateTime.TryParseExact(startDate + " " + startTime, "yyyy-MM-dd HH:mm", null, System.Globalization.DateTimeStyles.None, out DateTime startDt) ||
|
|
||||||
!DateTime.TryParseExact(deadlineDate + " " + deadlineTime, "yyyy-MM-dd HH:mm", null, System.Globalization.DateTimeStyles.None, out DateTime deadlineDt))
|
|
||||||
{
|
|
||||||
await SendAsync(e, "创建比赛", "时间格式错误,请使用 yyyy-MM-dd HH:mm(开始时间和截止时间各两段)。");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析剩余参数:选项和奖励率
|
// 解析剩余参数(从第6段开始,key=value格式,value支持双引号包围)
|
||||||
List<string> optionParts = [];
|
int spaceCount = 0, idx = 0;
|
||||||
|
for (; idx < detail.Length && spaceCount < 6; idx++)
|
||||||
|
{
|
||||||
|
if (detail[idx] == ' ') spaceCount++;
|
||||||
|
}
|
||||||
|
string paramString = detail[idx..].Trim();
|
||||||
|
System.Text.RegularExpressions.MatchCollection matches = GetParamValue().Matches(paramString);
|
||||||
|
|
||||||
|
// 解析剩余可选参数:key=value 格式,支持双引号包围含空格的value
|
||||||
|
Dictionary<string, string> paramDict = new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (System.Text.RegularExpressions.Match m in matches)
|
||||||
|
{
|
||||||
|
string key = m.Groups[1].Value;
|
||||||
|
string value = m.Groups[2].Success ? m.Groups[2].Value : m.Groups[3].Value;
|
||||||
|
if (!paramDict.TryAdd(key, value))
|
||||||
|
{
|
||||||
|
await SendAsync(e, "创建比赛", $"参数 '{key}' 重复。");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析截止时间 (ddl=),默认与开始时间相同
|
||||||
|
DateTime deadlineDt = startDt;
|
||||||
|
if (paramDict.TryGetValue("ddl", out string? ddlStr))
|
||||||
|
{
|
||||||
|
if (!DateTime.TryParseExact(ddlStr, "yyyy-MM-dd HH:mm", null, System.Globalization.DateTimeStyles.None, out deadlineDt))
|
||||||
|
{
|
||||||
|
await SendAsync(e, "创建比赛", "截止时间格式错误,请使用 yyyy-MM-dd HH:mm。");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析选项列表 (opts=)
|
||||||
|
string options = "team1_win,team2_win";
|
||||||
|
if (paramDict.TryGetValue("opts", out string? optsStr))
|
||||||
|
{
|
||||||
|
options = optsStr; // 预期逗号分隔的列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析奖励率与胜率
|
||||||
decimal? team1Odds = null, team2Odds = null, team1WinProbability = null;
|
decimal? team1Odds = null, team2Odds = null, team1WinProbability = null;
|
||||||
for (int i = 8; i < parts.Length; i++)
|
if (paramDict.TryGetValue("team1_win", out string? t1OddsStr))
|
||||||
{
|
{
|
||||||
string segment = parts[i];
|
if (decimal.TryParse(t1OddsStr, out decimal o1))
|
||||||
if (segment.StartsWith("team1_win="))
|
|
||||||
{
|
{
|
||||||
if (decimal.TryParse(segment[11..], out decimal odds1))
|
team1Odds = o1;
|
||||||
team1Odds = odds1;
|
|
||||||
}
|
|
||||||
else if (segment.StartsWith("team2_win="))
|
|
||||||
{
|
|
||||||
if (decimal.TryParse(segment[11..], out decimal odds2))
|
|
||||||
team2Odds = odds2;
|
|
||||||
}
|
|
||||||
else if (segment.StartsWith("prob="))
|
|
||||||
{
|
|
||||||
if (decimal.TryParse(segment[5..], out decimal prob))
|
|
||||||
team1WinProbability = prob;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await SendAsync(e, "创建比赛", "胜率值无效,应为0~1之间的小数。");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
optionParts.Add(segment);
|
await SendAsync(e, "创建比赛", "team1_win 奖励率无效。");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (paramDict.TryGetValue("team2_win", out string? t2OddsStr))
|
||||||
|
{
|
||||||
|
if (decimal.TryParse(t2OddsStr, out decimal o2))
|
||||||
|
{
|
||||||
|
team2Odds = o2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await SendAsync(e, "创建比赛", "team2_win 奖励率无效。");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (paramDict.TryGetValue("prob", out string? probStr))
|
||||||
|
{
|
||||||
|
if (decimal.TryParse(probStr, out decimal prob) && prob > 0 && prob < 1)
|
||||||
|
{
|
||||||
|
team1WinProbability = prob;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await SendAsync(e, "创建比赛", "胜率值无效,应为0~1之间的小数。");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
string options = optionParts.Count > 0 ? string.Join(",", optionParts) : "team1_win,team2_win";
|
|
||||||
|
|
||||||
BotReply reply = BettingController.CreateMatch(new CreateMatchRequest
|
BotReply reply = BettingController.CreateMatch(new CreateMatchRequest
|
||||||
{
|
{
|
||||||
@ -497,6 +530,24 @@ namespace Oshima.FunGame.WebAPI.Services
|
|||||||
case "result":
|
case "result":
|
||||||
request.Result = val;
|
request.Result = val;
|
||||||
break;
|
break;
|
||||||
|
case "stage":
|
||||||
|
request.Stage = val;
|
||||||
|
break;
|
||||||
|
case "t1":
|
||||||
|
request.Team1 = val;
|
||||||
|
break;
|
||||||
|
case "t2":
|
||||||
|
request.Team2 = val;
|
||||||
|
break;
|
||||||
|
case "be":
|
||||||
|
if (val == "0" || val == "1")
|
||||||
|
request.BettingEnabled = val == "1";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await SendAsync(e, "修改比赛", "enabled 值必须为 0 或 1。");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
await SendAsync(e, "修改比赛", $"未知参数:{key}");
|
await SendAsync(e, "修改比赛", $"未知参数:{key}");
|
||||||
return true;
|
return true;
|
||||||
@ -508,11 +559,34 @@ namespace Oshima.FunGame.WebAPI.Services
|
|||||||
reply.Keyboard = new KeyboardMessage()
|
reply.Keyboard = new KeyboardMessage()
|
||||||
.AppendButtons(2,
|
.AppendButtons(2,
|
||||||
Button.CreateCmdButton("📋 赛事列表", "赛事列表"),
|
Button.CreateCmdButton("📋 赛事列表", "赛事列表"),
|
||||||
|
Button.CreateCmdButton("📅 比赛列表", "比赛列表"),
|
||||||
Button.CreateCmdButton("🔍 比赛详情", $"比赛详情 {matchId}"));
|
Button.CreateCmdButton("🔍 比赛详情", $"比赛详情 {matchId}"));
|
||||||
await SendAsync(e, "修改比赛", reply);
|
await SendAsync(e, "修改比赛", reply);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 取消比赛指令
|
||||||
|
if (e.Detail.StartsWith("取消比赛"))
|
||||||
|
{
|
||||||
|
e.UseNotice = false;
|
||||||
|
string detail = e.Detail.Replace("取消比赛", "").Trim();
|
||||||
|
if (!int.TryParse(detail, out int matchId))
|
||||||
|
{
|
||||||
|
await SendAsync(e, "取消比赛", "格式:取消比赛 <比赛ID>");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BotReply reply = BettingController.CancelMatch(uid, matchId);
|
||||||
|
reply.Keyboard = new KeyboardMessage()
|
||||||
|
.AppendButtons(2,
|
||||||
|
Button.CreateCmdButton("📋 赛事列表", "赛事列表"),
|
||||||
|
Button.CreateCmdButton("📅 比赛列表", "比赛列表"),
|
||||||
|
Button.CreateCmdButton("🔍 比赛详情", $"比赛详情 {matchId}"),
|
||||||
|
Button.CreateCmdButton("💰 预测领奖", "预测领奖"));
|
||||||
|
await SendAsync(e, "取消比赛", reply);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user