mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-12 02:08:27 -04:00
Gambling moved to a separate project. Project builds
This commit is contained in:
@@ -1,16 +0,0 @@
|
||||
#nullable disable
|
||||
namespace NadekoBot.Modules.Gambling.Common.Waifu;
|
||||
|
||||
public enum AffinityTitle
|
||||
{
|
||||
Pure,
|
||||
Faithful,
|
||||
Playful,
|
||||
Cheater,
|
||||
Tainted,
|
||||
Corrupted,
|
||||
Lewd,
|
||||
Sloot,
|
||||
Depraved,
|
||||
Harlot
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
#nullable disable
|
||||
namespace NadekoBot.Modules.Gambling.Common.Waifu;
|
||||
|
||||
public enum ClaimTitle
|
||||
{
|
||||
Lonely,
|
||||
Devoted,
|
||||
Rookie,
|
||||
Schemer,
|
||||
Dilettante,
|
||||
Intermediate,
|
||||
Seducer,
|
||||
Expert,
|
||||
Veteran,
|
||||
Incubis,
|
||||
Harem_King,
|
||||
Harem_God
|
||||
}
|
@@ -1,10 +0,0 @@
|
||||
#nullable disable
|
||||
namespace NadekoBot.Modules.Gambling.Common.Waifu;
|
||||
|
||||
public enum DivorceResult
|
||||
{
|
||||
Success,
|
||||
SucessWithPenalty,
|
||||
NotYourWife,
|
||||
Cooldown
|
||||
}
|
@@ -1,6 +0,0 @@
|
||||
namespace NadekoBot.Modules.Gambling.Common.Waifu;
|
||||
|
||||
public class Extensions
|
||||
{
|
||||
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
#nullable disable
|
||||
namespace NadekoBot.Modules.Gambling.Common.Waifu;
|
||||
|
||||
public enum WaifuClaimResult
|
||||
{
|
||||
Success,
|
||||
NotEnoughFunds,
|
||||
InsufficientAmount
|
||||
}
|
@@ -1,374 +0,0 @@
|
||||
#nullable disable
|
||||
using Nadeko.Common;
|
||||
using NadekoBot.Modules.Gambling.Common;
|
||||
using NadekoBot.Modules.Gambling.Common.Waifu;
|
||||
using NadekoBot.Modules.Gambling.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling;
|
||||
|
||||
public partial class Gambling
|
||||
{
|
||||
[Group]
|
||||
public partial class WaifuClaimCommands : GamblingSubmodule<WaifuService>
|
||||
{
|
||||
public WaifuClaimCommands(GamblingConfigService gamblingConfService)
|
||||
: base(gamblingConfService)
|
||||
{
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
public async Task WaifuReset()
|
||||
{
|
||||
var price = _service.GetResetPrice(ctx.User);
|
||||
var embed = _eb.Create()
|
||||
.WithTitle(GetText(strs.waifu_reset_confirm))
|
||||
.WithDescription(GetText(strs.waifu_reset_price(Format.Bold(N(price)))));
|
||||
|
||||
if (!await PromptUserConfirmAsync(embed))
|
||||
return;
|
||||
|
||||
if (await _service.TryReset(ctx.User))
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.waifu_reset);
|
||||
return;
|
||||
}
|
||||
|
||||
await ReplyErrorLocalizedAsync(strs.waifu_reset_fail);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task WaifuClaim(long amount, [Leftover] IUser target)
|
||||
{
|
||||
if (amount < Config.Waifu.MinPrice)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.waifu_isnt_cheap(Config.Waifu.MinPrice + CurrencySign));
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.Id == ctx.User.Id)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.waifu_not_yourself);
|
||||
return;
|
||||
}
|
||||
|
||||
var (w, isAffinity, result) = await _service.ClaimWaifuAsync(ctx.User, target, amount);
|
||||
|
||||
if (result == WaifuClaimResult.InsufficientAmount)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(
|
||||
strs.waifu_not_enough(N((long)Math.Ceiling(w.Price * (isAffinity ? 0.88f : 1.1f)))));
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == WaifuClaimResult.NotEnoughFunds)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = GetText(strs.waifu_claimed(Format.Bold(target.ToString()), N(amount)));
|
||||
if (w.Affinity?.UserId == ctx.User.Id)
|
||||
msg += "\n" + GetText(strs.waifu_fulfilled(target, N(w.Price)));
|
||||
else
|
||||
msg = " " + msg;
|
||||
await SendConfirmAsync(ctx.User.Mention + msg);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[Priority(0)]
|
||||
public async Task WaifuTransfer(ulong waifuId, IUser newOwner)
|
||||
{
|
||||
if (!await _service.WaifuTransfer(ctx.User, waifuId, newOwner))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.waifu_transfer_fail);
|
||||
return;
|
||||
}
|
||||
|
||||
await ReplyConfirmLocalizedAsync(strs.waifu_transfer_success(Format.Bold(waifuId.ToString()),
|
||||
Format.Bold(ctx.User.ToString()),
|
||||
Format.Bold(newOwner.ToString())));
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[Priority(1)]
|
||||
public async Task WaifuTransfer(IUser waifu, IUser newOwner)
|
||||
{
|
||||
if (!await _service.WaifuTransfer(ctx.User, waifu.Id, newOwner))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.waifu_transfer_fail);
|
||||
return;
|
||||
}
|
||||
|
||||
await ReplyConfirmLocalizedAsync(strs.waifu_transfer_success(Format.Bold(waifu.ToString()),
|
||||
Format.Bold(ctx.User.ToString()),
|
||||
Format.Bold(newOwner.ToString())));
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[Priority(-1)]
|
||||
public Task Divorce([Leftover] string target)
|
||||
{
|
||||
var waifuUserId = _service.GetWaifuUserId(ctx.User.Id, target);
|
||||
if (waifuUserId == default)
|
||||
return ReplyErrorLocalizedAsync(strs.waifu_not_yours);
|
||||
|
||||
return Divorce(waifuUserId);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[Priority(0)]
|
||||
public Task Divorce([Leftover] IGuildUser target)
|
||||
=> Divorce(target.Id);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[Priority(1)]
|
||||
public async Task Divorce([Leftover] ulong targetId)
|
||||
{
|
||||
if (targetId == ctx.User.Id)
|
||||
return;
|
||||
|
||||
var (w, result, amount, remaining) = await _service.DivorceWaifuAsync(ctx.User, targetId);
|
||||
|
||||
if (result == DivorceResult.SucessWithPenalty)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.waifu_divorced_like(Format.Bold(w.Waifu.ToString()),
|
||||
N(amount)));
|
||||
}
|
||||
else if (result == DivorceResult.Success)
|
||||
await ReplyConfirmLocalizedAsync(strs.waifu_divorced_notlike(N(amount)));
|
||||
else if (result == DivorceResult.NotYourWife)
|
||||
await ReplyErrorLocalizedAsync(strs.waifu_not_yours);
|
||||
else
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.waifu_recent_divorce(
|
||||
Format.Bold(((int)remaining?.TotalHours).ToString()),
|
||||
Format.Bold(remaining?.Minutes.ToString())));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Affinity([Leftover] IGuildUser user = null)
|
||||
{
|
||||
if (user?.Id == ctx.User.Id)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.waifu_egomaniac);
|
||||
return;
|
||||
}
|
||||
|
||||
var (oldAff, sucess, remaining) = await _service.ChangeAffinityAsync(ctx.User, user);
|
||||
if (!sucess)
|
||||
{
|
||||
if (remaining is not null)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.waifu_affinity_cooldown(
|
||||
Format.Bold(((int)remaining?.TotalHours).ToString()),
|
||||
Format.Bold(remaining?.Minutes.ToString())));
|
||||
}
|
||||
else
|
||||
await ReplyErrorLocalizedAsync(strs.waifu_affinity_already);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (user is null)
|
||||
await ReplyConfirmLocalizedAsync(strs.waifu_affinity_reset);
|
||||
else if (oldAff is null)
|
||||
await ReplyConfirmLocalizedAsync(strs.waifu_affinity_set(Format.Bold(user.ToString())));
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.waifu_affinity_changed(Format.Bold(oldAff.ToString()),
|
||||
Format.Bold(user.ToString())));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task WaifuLb(int page = 1)
|
||||
{
|
||||
page--;
|
||||
|
||||
if (page < 0)
|
||||
return;
|
||||
|
||||
if (page > 100)
|
||||
page = 100;
|
||||
|
||||
var waifus = _service.GetTopWaifusAtPage(page).ToList();
|
||||
|
||||
if (waifus.Count == 0)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.waifus_none);
|
||||
return;
|
||||
}
|
||||
|
||||
var embed = _eb.Create().WithTitle(GetText(strs.waifus_top_waifus)).WithOkColor();
|
||||
|
||||
var i = 0;
|
||||
foreach (var w in waifus)
|
||||
{
|
||||
var j = i++;
|
||||
embed.AddField("#" + ((page * 9) + j + 1) + " - " + N(w.Price), GetLbString(w));
|
||||
}
|
||||
|
||||
await ctx.Channel.EmbedAsync(embed);
|
||||
}
|
||||
|
||||
private string GetLbString(WaifuLbResult w)
|
||||
{
|
||||
var claimer = "no one";
|
||||
var status = string.Empty;
|
||||
|
||||
var waifuUsername = w.Username.TrimTo(20);
|
||||
var claimerUsername = w.Claimer?.TrimTo(20);
|
||||
|
||||
if (w.Claimer is not null)
|
||||
claimer = $"{claimerUsername}#{w.ClaimerDiscrim}";
|
||||
if (w.Affinity is null)
|
||||
status = $"... but {waifuUsername}'s heart is empty";
|
||||
else if (w.Affinity + w.AffinityDiscrim == w.Claimer + w.ClaimerDiscrim)
|
||||
status = $"... and {waifuUsername} likes {claimerUsername} too <3";
|
||||
else
|
||||
status = $"... but {waifuUsername}'s heart belongs to {w.Affinity.TrimTo(20)}#{w.AffinityDiscrim}";
|
||||
return $"**{waifuUsername}#{w.Discrim}** - claimed by **{claimer}**\n\t{status}";
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[Priority(1)]
|
||||
public Task WaifuInfo([Leftover] IUser target = null)
|
||||
{
|
||||
if (target is null)
|
||||
target = ctx.User;
|
||||
|
||||
return InternalWaifuInfo(target.Id, target.ToString());
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[Priority(0)]
|
||||
public Task WaifuInfo(ulong targetId)
|
||||
=> InternalWaifuInfo(targetId);
|
||||
|
||||
private async Task InternalWaifuInfo(ulong targetId, string name = null)
|
||||
{
|
||||
var wi = await _service.GetFullWaifuInfoAsync(targetId);
|
||||
var affInfo = _service.GetAffinityTitle(wi.AffinityCount);
|
||||
|
||||
var waifuItems = _service.GetWaifuItems().ToDictionary(x => x.ItemEmoji, x => x);
|
||||
|
||||
var nobody = GetText(strs.nobody);
|
||||
var itemList = await _service.GetItems(wi.WaifuId);
|
||||
var itemsStr = !itemList.Any()
|
||||
? "-"
|
||||
: string.Join("\n",
|
||||
itemList.Where(x => waifuItems.TryGetValue(x.ItemEmoji, out _))
|
||||
.OrderBy(x => waifuItems[x.ItemEmoji].Price)
|
||||
.GroupBy(x => x.ItemEmoji)
|
||||
.Select(x => $"{x.Key} x{x.Count(),-3}")
|
||||
.Chunk(2)
|
||||
.Select(x => string.Join(" ", x)));
|
||||
|
||||
var claimsNames = (await _service.GetClaimNames(wi.WaifuId));
|
||||
var claimsStr = claimsNames
|
||||
.Shuffle()
|
||||
.Take(30)
|
||||
.Join('\n');
|
||||
|
||||
var fansList = await _service.GetFansNames(wi.WaifuId);
|
||||
var fansStr = fansList
|
||||
.Select((x) => claimsNames.Contains(x) ? $"{x} 💞" : x)
|
||||
.Join('\n');
|
||||
|
||||
|
||||
if (string.IsNullOrWhiteSpace(fansStr))
|
||||
fansStr = "-";
|
||||
|
||||
var embed = _eb.Create()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.waifu)
|
||||
+ " "
|
||||
+ (wi.FullName ?? name ?? targetId.ToString())
|
||||
+ " - \"the "
|
||||
+ _service.GetClaimTitle(wi.ClaimCount)
|
||||
+ "\"")
|
||||
.AddField(GetText(strs.price), N(wi.Price), true)
|
||||
.AddField(GetText(strs.claimed_by), wi.ClaimerName ?? nobody, true)
|
||||
.AddField(GetText(strs.likes), wi.AffinityName ?? nobody, true)
|
||||
.AddField(GetText(strs.changes_of_heart), $"{wi.AffinityCount} - \"the {affInfo}\"", true)
|
||||
.AddField(GetText(strs.divorces), wi.DivorceCount.ToString(), true)
|
||||
.AddField("\u200B", "\u200B", true)
|
||||
.AddField(GetText(strs.fans(fansList.Count)), fansStr, true)
|
||||
.AddField($"Waifus ({wi.ClaimCount})",
|
||||
wi.ClaimCount == 0 ? nobody : claimsStr,
|
||||
true)
|
||||
.AddField(GetText(strs.gifts), itemsStr, true);
|
||||
|
||||
await ctx.Channel.EmbedAsync(embed);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[Priority(1)]
|
||||
public async Task WaifuGift(int page = 1)
|
||||
{
|
||||
if (--page < 0 || page > (Config.Waifu.Items.Count - 1) / 9)
|
||||
return;
|
||||
|
||||
var waifuItems = _service.GetWaifuItems();
|
||||
await ctx.SendPaginatedConfirmAsync(page,
|
||||
cur =>
|
||||
{
|
||||
var embed = _eb.Create().WithTitle(GetText(strs.waifu_gift_shop)).WithOkColor();
|
||||
|
||||
waifuItems.OrderBy(x => x.Negative)
|
||||
.ThenBy(x => x.Price)
|
||||
.Skip(9 * cur)
|
||||
.Take(9)
|
||||
.ToList()
|
||||
.ForEach(x => embed.AddField(
|
||||
$"{(!x.Negative ? string.Empty : "\\💔")} {x.ItemEmoji} {x.Name}",
|
||||
Format.Bold(N(x.Price)),
|
||||
true));
|
||||
|
||||
return embed;
|
||||
},
|
||||
waifuItems.Count,
|
||||
9);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[Priority(0)]
|
||||
public async Task WaifuGift(string itemName, [Leftover] IUser waifu)
|
||||
{
|
||||
if (waifu.Id == ctx.User.Id)
|
||||
return;
|
||||
|
||||
var allItems = _service.GetWaifuItems();
|
||||
var item = allItems.FirstOrDefault(x => x.Name.ToLowerInvariant() == itemName.ToLowerInvariant());
|
||||
if (item is null)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.waifu_gift_not_exist);
|
||||
return;
|
||||
}
|
||||
|
||||
var sucess = await _service.GiftWaifuAsync(ctx.User, waifu, item);
|
||||
|
||||
if (sucess)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.waifu_gift(Format.Bold(item + " " + item.ItemEmoji),
|
||||
Format.Bold(waifu.ToString())));
|
||||
}
|
||||
else
|
||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,583 +0,0 @@
|
||||
#nullable disable
|
||||
using LinqToDB;
|
||||
using LinqToDB.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using NadekoBot.Db;
|
||||
using NadekoBot.Db.Models;
|
||||
using NadekoBot.Modules.Gambling.Common;
|
||||
using NadekoBot.Modules.Gambling.Common.Waifu;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Services;
|
||||
|
||||
public class WaifuService : INService, IReadyExecutor
|
||||
{
|
||||
private readonly DbService _db;
|
||||
private readonly ICurrencyService _cs;
|
||||
private readonly IBotCache _cache;
|
||||
private readonly GamblingConfigService _gss;
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
public WaifuService(
|
||||
DbService db,
|
||||
ICurrencyService cs,
|
||||
IBotCache cache,
|
||||
GamblingConfigService gss,
|
||||
IBotCredentials creds,
|
||||
DiscordSocketClient client)
|
||||
{
|
||||
_db = db;
|
||||
_cs = cs;
|
||||
_cache = cache;
|
||||
_gss = gss;
|
||||
_creds = creds;
|
||||
_client = client;
|
||||
}
|
||||
|
||||
public async Task<bool> WaifuTransfer(IUser owner, ulong waifuId, IUser newOwner)
|
||||
{
|
||||
if (owner.Id == newOwner.Id || waifuId == newOwner.Id)
|
||||
return false;
|
||||
|
||||
var settings = _gss.Data;
|
||||
|
||||
await using var uow = _db.GetDbContext();
|
||||
var waifu = uow.WaifuInfo.ByWaifuUserId(waifuId);
|
||||
var ownerUser = uow.GetOrCreateUser(owner);
|
||||
|
||||
// owner has to be the owner of the waifu
|
||||
if (waifu is null || waifu.ClaimerId != ownerUser.Id)
|
||||
return false;
|
||||
|
||||
// if waifu likes the person, gotta pay the penalty
|
||||
if (waifu.AffinityId == ownerUser.Id)
|
||||
{
|
||||
if (!await _cs.RemoveAsync(owner.Id, (long)(waifu.Price * 0.6), new("waifu", "affinity-penalty")))
|
||||
// unable to pay 60% penalty
|
||||
return false;
|
||||
|
||||
waifu.Price = (long)(waifu.Price * 0.7); // half of 60% = 30% price reduction
|
||||
if (waifu.Price < settings.Waifu.MinPrice)
|
||||
waifu.Price = settings.Waifu.MinPrice;
|
||||
}
|
||||
else // if not, pay 10% fee
|
||||
{
|
||||
if (!await _cs.RemoveAsync(owner.Id, waifu.Price / 10, new("waifu", "transfer")))
|
||||
return false;
|
||||
|
||||
waifu.Price = (long)(waifu.Price * 0.95); // half of 10% = 5% price reduction
|
||||
if (waifu.Price < settings.Waifu.MinPrice)
|
||||
waifu.Price = settings.Waifu.MinPrice;
|
||||
}
|
||||
|
||||
//new claimerId is the id of the new owner
|
||||
var newOwnerUser = uow.GetOrCreateUser(newOwner);
|
||||
waifu.ClaimerId = newOwnerUser.Id;
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public long GetResetPrice(IUser user)
|
||||
{
|
||||
var settings = _gss.Data;
|
||||
using var uow = _db.GetDbContext();
|
||||
var waifu = uow.WaifuInfo.ByWaifuUserId(user.Id);
|
||||
|
||||
if (waifu is null)
|
||||
return settings.Waifu.MinPrice;
|
||||
|
||||
var divorces = uow.WaifuUpdates.Count(x
|
||||
=> x.Old != null && x.Old.UserId == user.Id && x.UpdateType == WaifuUpdateType.Claimed && x.New == null);
|
||||
var affs = uow.WaifuUpdates.AsQueryable()
|
||||
.Where(w => w.User.UserId == user.Id
|
||||
&& w.UpdateType == WaifuUpdateType.AffinityChanged
|
||||
&& w.New != null)
|
||||
.ToList()
|
||||
.GroupBy(x => x.New)
|
||||
.Count();
|
||||
|
||||
return (long)Math.Ceiling(waifu.Price * 1.25f)
|
||||
+ ((divorces + affs + 2) * settings.Waifu.Multipliers.WaifuReset);
|
||||
}
|
||||
|
||||
public async Task<bool> TryReset(IUser user)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
var price = GetResetPrice(user);
|
||||
if (!await _cs.RemoveAsync(user.Id, price, new("waifu", "reset")))
|
||||
return false;
|
||||
|
||||
var affs = uow.WaifuUpdates.AsQueryable()
|
||||
.Where(w => w.User.UserId == user.Id
|
||||
&& w.UpdateType == WaifuUpdateType.AffinityChanged
|
||||
&& w.New != null);
|
||||
|
||||
var divorces = uow.WaifuUpdates.AsQueryable()
|
||||
.Where(x => x.Old != null
|
||||
&& x.Old.UserId == user.Id
|
||||
&& x.UpdateType == WaifuUpdateType.Claimed
|
||||
&& x.New == null);
|
||||
|
||||
//reset changes of heart to 0
|
||||
uow.WaifuUpdates.RemoveRange(affs);
|
||||
//reset divorces to 0
|
||||
uow.WaifuUpdates.RemoveRange(divorces);
|
||||
var waifu = uow.WaifuInfo.ByWaifuUserId(user.Id);
|
||||
//reset price, remove items
|
||||
//remove owner, remove affinity
|
||||
waifu.Price = 50;
|
||||
waifu.Items.Clear();
|
||||
waifu.ClaimerId = null;
|
||||
waifu.AffinityId = null;
|
||||
|
||||
//wives stay though
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<(WaifuInfo, bool, WaifuClaimResult)> ClaimWaifuAsync(IUser user, IUser target, long amount)
|
||||
{
|
||||
var settings = _gss.Data;
|
||||
WaifuClaimResult result;
|
||||
WaifuInfo w;
|
||||
bool isAffinity;
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
w = uow.WaifuInfo.ByWaifuUserId(target.Id);
|
||||
isAffinity = w?.Affinity?.UserId == user.Id;
|
||||
if (w is null)
|
||||
{
|
||||
var claimer = uow.GetOrCreateUser(user);
|
||||
var waifu = uow.GetOrCreateUser(target);
|
||||
if (!await _cs.RemoveAsync(user.Id, amount, new("waifu", "claim")))
|
||||
result = WaifuClaimResult.NotEnoughFunds;
|
||||
else
|
||||
{
|
||||
uow.WaifuInfo.Add(w = new()
|
||||
{
|
||||
Waifu = waifu,
|
||||
Claimer = claimer,
|
||||
Affinity = null,
|
||||
Price = amount
|
||||
});
|
||||
uow.WaifuUpdates.Add(new()
|
||||
{
|
||||
User = waifu,
|
||||
Old = null,
|
||||
New = claimer,
|
||||
UpdateType = WaifuUpdateType.Claimed
|
||||
});
|
||||
result = WaifuClaimResult.Success;
|
||||
}
|
||||
}
|
||||
else if (isAffinity && amount > w.Price * settings.Waifu.Multipliers.CrushClaim)
|
||||
{
|
||||
if (!await _cs.RemoveAsync(user.Id, amount, new("waifu", "claim")))
|
||||
result = WaifuClaimResult.NotEnoughFunds;
|
||||
else
|
||||
{
|
||||
var oldClaimer = w.Claimer;
|
||||
w.Claimer = uow.GetOrCreateUser(user);
|
||||
w.Price = amount + (amount / 4);
|
||||
result = WaifuClaimResult.Success;
|
||||
|
||||
uow.WaifuUpdates.Add(new()
|
||||
{
|
||||
User = w.Waifu,
|
||||
Old = oldClaimer,
|
||||
New = w.Claimer,
|
||||
UpdateType = WaifuUpdateType.Claimed
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (amount >= w.Price * settings.Waifu.Multipliers.NormalClaim) // if no affinity
|
||||
{
|
||||
if (!await _cs.RemoveAsync(user.Id, amount, new("waifu", "claim")))
|
||||
result = WaifuClaimResult.NotEnoughFunds;
|
||||
else
|
||||
{
|
||||
var oldClaimer = w.Claimer;
|
||||
w.Claimer = uow.GetOrCreateUser(user);
|
||||
w.Price = amount;
|
||||
result = WaifuClaimResult.Success;
|
||||
|
||||
uow.WaifuUpdates.Add(new()
|
||||
{
|
||||
User = w.Waifu,
|
||||
Old = oldClaimer,
|
||||
New = w.Claimer,
|
||||
UpdateType = WaifuUpdateType.Claimed
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
result = WaifuClaimResult.InsufficientAmount;
|
||||
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return (w, isAffinity, result);
|
||||
}
|
||||
|
||||
public async Task<(DiscordUser, bool, TimeSpan?)> ChangeAffinityAsync(IUser user, IGuildUser target)
|
||||
{
|
||||
DiscordUser oldAff = null;
|
||||
var success = false;
|
||||
TimeSpan? remaining = null;
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var w = uow.WaifuInfo.ByWaifuUserId(user.Id);
|
||||
var newAff = target is null ? null : uow.GetOrCreateUser(target);
|
||||
if (w?.Affinity?.UserId == target?.Id)
|
||||
{
|
||||
return (null, false, null);
|
||||
}
|
||||
|
||||
remaining = await _cache.GetRatelimitAsync(GetAffinityKey(user.Id),
|
||||
30.Minutes());
|
||||
|
||||
if (remaining is not null)
|
||||
{
|
||||
}
|
||||
else if (w is null)
|
||||
{
|
||||
var thisUser = uow.GetOrCreateUser(user);
|
||||
uow.WaifuInfo.Add(new()
|
||||
{
|
||||
Affinity = newAff,
|
||||
Waifu = thisUser,
|
||||
Price = 1,
|
||||
Claimer = null
|
||||
});
|
||||
success = true;
|
||||
|
||||
uow.WaifuUpdates.Add(new()
|
||||
{
|
||||
User = thisUser,
|
||||
Old = null,
|
||||
New = newAff,
|
||||
UpdateType = WaifuUpdateType.AffinityChanged
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (w.Affinity is not null)
|
||||
oldAff = w.Affinity;
|
||||
w.Affinity = newAff;
|
||||
success = true;
|
||||
|
||||
uow.WaifuUpdates.Add(new()
|
||||
{
|
||||
User = w.Waifu,
|
||||
Old = oldAff,
|
||||
New = newAff,
|
||||
UpdateType = WaifuUpdateType.AffinityChanged
|
||||
});
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return (oldAff, success, remaining);
|
||||
}
|
||||
|
||||
public IEnumerable<WaifuLbResult> GetTopWaifusAtPage(int page)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
return uow.WaifuInfo.GetTop(9, page * 9);
|
||||
}
|
||||
|
||||
public ulong GetWaifuUserId(ulong ownerId, string name)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
return uow.WaifuInfo.GetWaifuUserId(ownerId, name);
|
||||
}
|
||||
|
||||
private static TypedKey<long> GetDivorceKey(ulong userId)
|
||||
=> new($"waifu:divorce_cd:{userId}");
|
||||
|
||||
private static TypedKey<long> GetAffinityKey(ulong userId)
|
||||
=> new($"waifu:affinity:{userId}");
|
||||
|
||||
public async Task<(WaifuInfo, DivorceResult, long, TimeSpan?)> DivorceWaifuAsync(IUser user, ulong targetId)
|
||||
{
|
||||
DivorceResult result;
|
||||
TimeSpan? remaining = null;
|
||||
long amount = 0;
|
||||
WaifuInfo w;
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
w = uow.WaifuInfo.ByWaifuUserId(targetId);
|
||||
if (w?.Claimer is null || w.Claimer.UserId != user.Id)
|
||||
result = DivorceResult.NotYourWife;
|
||||
else
|
||||
{
|
||||
remaining = await _cache.GetRatelimitAsync(GetDivorceKey(user.Id), 6.Hours());
|
||||
if (remaining is TimeSpan rem)
|
||||
{
|
||||
result = DivorceResult.Cooldown;
|
||||
return (w, result, amount, rem);
|
||||
}
|
||||
|
||||
amount = w.Price / 2;
|
||||
|
||||
if (w.Affinity?.UserId == user.Id)
|
||||
{
|
||||
await _cs.AddAsync(w.Waifu.UserId, amount, new("waifu", "compensation"));
|
||||
w.Price = (long)Math.Floor(w.Price * _gss.Data.Waifu.Multipliers.DivorceNewValue);
|
||||
result = DivorceResult.SucessWithPenalty;
|
||||
}
|
||||
else
|
||||
{
|
||||
await _cs.AddAsync(user.Id, amount, new("waifu", "refund"));
|
||||
|
||||
result = DivorceResult.Success;
|
||||
}
|
||||
|
||||
var oldClaimer = w.Claimer;
|
||||
w.Claimer = null;
|
||||
|
||||
uow.WaifuUpdates.Add(new()
|
||||
{
|
||||
User = w.Waifu,
|
||||
Old = oldClaimer,
|
||||
New = null,
|
||||
UpdateType = WaifuUpdateType.Claimed
|
||||
});
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return (w, result, amount, remaining);
|
||||
}
|
||||
|
||||
public async Task<bool> GiftWaifuAsync(IUser from, IUser giftedWaifu, WaifuItemModel itemObj)
|
||||
{
|
||||
if (!await _cs.RemoveAsync(from, itemObj.Price, new("waifu", "item")))
|
||||
return false;
|
||||
|
||||
await using var uow = _db.GetDbContext();
|
||||
var w = uow.WaifuInfo.ByWaifuUserId(giftedWaifu.Id, set => set.Include(x => x.Items).Include(x => x.Claimer));
|
||||
if (w is null)
|
||||
{
|
||||
uow.WaifuInfo.Add(w = new()
|
||||
{
|
||||
Affinity = null,
|
||||
Claimer = null,
|
||||
Price = 1,
|
||||
Waifu = uow.GetOrCreateUser(giftedWaifu)
|
||||
});
|
||||
}
|
||||
|
||||
if (!itemObj.Negative)
|
||||
{
|
||||
w.Items.Add(new()
|
||||
{
|
||||
Name = itemObj.Name.ToLowerInvariant(),
|
||||
ItemEmoji = itemObj.ItemEmoji
|
||||
});
|
||||
|
||||
if (w.Claimer?.UserId == from.Id)
|
||||
w.Price += (long)(itemObj.Price * _gss.Data.Waifu.Multipliers.GiftEffect);
|
||||
else
|
||||
w.Price += itemObj.Price / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
w.Price -= (long)(itemObj.Price * _gss.Data.Waifu.Multipliers.NegativeGiftEffect);
|
||||
if (w.Price < 1)
|
||||
w.Price = 1;
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<WaifuInfoStats> GetFullWaifuInfoAsync(ulong targetId)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
var wi = await uow.GetWaifuInfoAsync(targetId);
|
||||
if (wi is null)
|
||||
{
|
||||
wi = new()
|
||||
{
|
||||
AffinityCount = 0,
|
||||
AffinityName = null,
|
||||
ClaimCount = 0,
|
||||
ClaimerName = null,
|
||||
DivorceCount = 0,
|
||||
FullName = null,
|
||||
Price = 1
|
||||
};
|
||||
}
|
||||
|
||||
return wi;
|
||||
}
|
||||
|
||||
public string GetClaimTitle(int count)
|
||||
{
|
||||
ClaimTitle title;
|
||||
if (count == 0)
|
||||
title = ClaimTitle.Lonely;
|
||||
else if (count == 1)
|
||||
title = ClaimTitle.Devoted;
|
||||
else if (count < 3)
|
||||
title = ClaimTitle.Rookie;
|
||||
else if (count < 6)
|
||||
title = ClaimTitle.Schemer;
|
||||
else if (count < 10)
|
||||
title = ClaimTitle.Dilettante;
|
||||
else if (count < 17)
|
||||
title = ClaimTitle.Intermediate;
|
||||
else if (count < 25)
|
||||
title = ClaimTitle.Seducer;
|
||||
else if (count < 35)
|
||||
title = ClaimTitle.Expert;
|
||||
else if (count < 50)
|
||||
title = ClaimTitle.Veteran;
|
||||
else if (count < 75)
|
||||
title = ClaimTitle.Incubis;
|
||||
else if (count < 100)
|
||||
title = ClaimTitle.Harem_King;
|
||||
else
|
||||
title = ClaimTitle.Harem_God;
|
||||
|
||||
return title.ToString().Replace('_', ' ');
|
||||
}
|
||||
|
||||
public string GetAffinityTitle(int count)
|
||||
{
|
||||
AffinityTitle title;
|
||||
if (count < 1)
|
||||
title = AffinityTitle.Pure;
|
||||
else if (count < 2)
|
||||
title = AffinityTitle.Faithful;
|
||||
else if (count < 4)
|
||||
title = AffinityTitle.Playful;
|
||||
else if (count < 8)
|
||||
title = AffinityTitle.Cheater;
|
||||
else if (count < 11)
|
||||
title = AffinityTitle.Tainted;
|
||||
else if (count < 15)
|
||||
title = AffinityTitle.Corrupted;
|
||||
else if (count < 20)
|
||||
title = AffinityTitle.Lewd;
|
||||
else if (count < 25)
|
||||
title = AffinityTitle.Sloot;
|
||||
else if (count < 35)
|
||||
title = AffinityTitle.Depraved;
|
||||
else
|
||||
title = AffinityTitle.Harlot;
|
||||
|
||||
return title.ToString().Replace('_', ' ');
|
||||
}
|
||||
|
||||
public IReadOnlyList<WaifuItemModel> GetWaifuItems()
|
||||
{
|
||||
var conf = _gss.Data;
|
||||
return conf.Waifu.Items.Select(x
|
||||
=> new WaifuItemModel(x.ItemEmoji,
|
||||
(long)(x.Price * conf.Waifu.Multipliers.AllGiftPrices),
|
||||
x.Name,
|
||||
x.Negative))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static readonly TypedKey<long> _waifuDecayKey = $"waifu:last_decay";
|
||||
public async Task OnReadyAsync()
|
||||
{
|
||||
// only decay waifu values from shard 0
|
||||
if (_client.ShardId != 0)
|
||||
return;
|
||||
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
var multi = _gss.Data.Waifu.Decay.Percent / 100f;
|
||||
var minPrice = _gss.Data.Waifu.Decay.MinPrice;
|
||||
var decayInterval = _gss.Data.Waifu.Decay.HourInterval;
|
||||
|
||||
if (multi is < 0f or > 1f || decayInterval < 0)
|
||||
continue;
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var nowB = now.ToBinary();
|
||||
|
||||
var result = await _cache.GetAsync(_waifuDecayKey);
|
||||
|
||||
if (result.TryGetValue(out var val))
|
||||
{
|
||||
var lastDecay = DateTime.FromBinary(val);
|
||||
var toWait = decayInterval.Hours() - (DateTime.UtcNow - lastDecay);
|
||||
|
||||
if (toWait > 0.Hours())
|
||||
continue;
|
||||
}
|
||||
|
||||
await _cache.AddAsync(_waifuDecayKey, nowB);
|
||||
|
||||
await using var uow = _db.GetDbContext();
|
||||
|
||||
await uow.GetTable<WaifuInfo>()
|
||||
.Where(x => x.Price > minPrice && x.ClaimerId == null)
|
||||
.UpdateAsync(old => new()
|
||||
{
|
||||
Price = (long)(old.Price * multi)
|
||||
});
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Unexpected error occured in waifu decay loop: {ErrorMessage}", ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Task.Delay(1.Hours());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<string>> GetClaimNames(int waifuId)
|
||||
{
|
||||
await using var ctx = _db.GetDbContext();
|
||||
return await ctx.GetTable<DiscordUser>()
|
||||
.Where(x => ctx.GetTable<WaifuInfo>()
|
||||
.Where(wi => wi.ClaimerId == waifuId)
|
||||
.Select(wi => wi.WaifuId)
|
||||
.Contains(x.Id))
|
||||
.Select(x => $"{x.Username}#{x.Discriminator}")
|
||||
.ToListAsyncEF();
|
||||
}
|
||||
public async Task<IReadOnlyCollection<string>> GetFansNames(int waifuId)
|
||||
{
|
||||
await using var ctx = _db.GetDbContext();
|
||||
return await ctx.GetTable<DiscordUser>()
|
||||
.Where(x => ctx.GetTable<WaifuInfo>()
|
||||
.Where(wi => wi.AffinityId == waifuId)
|
||||
.Select(wi => wi.WaifuId)
|
||||
.Contains(x.Id))
|
||||
.Select(x => $"{x.Username}#{x.Discriminator}")
|
||||
.ToListAsyncEF();
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<WaifuItem>> GetItems(int waifuId)
|
||||
{
|
||||
await using var ctx = _db.GetDbContext();
|
||||
return await ctx.GetTable<WaifuItem>()
|
||||
.Where(x => x.WaifuInfoId == ctx.GetTable<WaifuInfo>()
|
||||
.Where(x => x.WaifuId == waifuId)
|
||||
.Select(x => x.Id)
|
||||
.FirstOrDefault())
|
||||
.ToListAsyncEF();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user