320 lines
No EOL
11 KiB
C#
320 lines
No EOL
11 KiB
C#
using EllieBot.Modules.Games.Fish;
|
|
using EllieBot.Modules.Xp.Services;
|
|
using Format = Discord.Format;
|
|
|
|
namespace EllieBot.Modules.Games;
|
|
|
|
public partial class Games
|
|
{
|
|
public class FishingCommands(
|
|
FishService fs,
|
|
FishItemService fis,
|
|
FishConfigService fcs,
|
|
IBotCache cache,
|
|
UserService us,
|
|
CaptchaService captchaService) : EllieModule
|
|
{
|
|
private static readonly EllieRandom _rng = new();
|
|
|
|
private TypedKey<bool> FishingWhitelistKey(ulong userId)
|
|
=> new($"fishingwhitelist:{userId}");
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public async Task Fish()
|
|
{
|
|
var cRes = await cache.GetAsync(FishingWhitelistKey(ctx.User.Id));
|
|
if (cRes.TryPickT1(out _, out _))
|
|
{
|
|
string? password = null;
|
|
if (fcs.Data.RequireCaptcha)
|
|
password = await captchaService.GetUserCaptcha(ctx.User.Id);
|
|
|
|
if (password is not null)
|
|
{
|
|
var img = captchaService.GetPasswordImage(password);
|
|
using var stream = await img.ToStreamAsync();
|
|
|
|
var toSend = Response()
|
|
.File(stream, "timely.png")
|
|
.Embed(CreateEmbed()
|
|
.WithFooter("captcha: type the text from the image")
|
|
.WithImageUrl("attachment://timely.png"));
|
|
|
|
#if GLOBAL_ELLIE
|
|
if (_rng.Next(0, 8) == 0)
|
|
toSend = toSend
|
|
.Text("*[Sub on Patreon](https://patreon.com/elliebot) to remove captcha.*");
|
|
#endif
|
|
var captcha = await toSend.SendAsync();
|
|
|
|
try
|
|
{
|
|
var userInput = await GetUserInputAsync(ctx.User.Id, ctx.Channel.Id);
|
|
if (userInput?.ToLowerInvariant() != password?.ToLowerInvariant())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// whitelist the user for 30 minutes
|
|
await cache.AddAsync(FishingWhitelistKey(ctx.User.Id), true, TimeSpan.FromMinutes(30));
|
|
// reset the password
|
|
await captchaService.ClearUserCaptcha(ctx.User.Id);
|
|
}
|
|
finally
|
|
{
|
|
_ = captcha.DeleteAsync();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
var multis = await fis.GetUserMultipliersAsync(ctx.User.Id);
|
|
var fishResult = await fs.FishAsync(ctx.User.Id, ctx.Channel.Id, multis);
|
|
if (fishResult.TryPickT1(out _, out var fishTask))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var currentWeather = fs.GetCurrentWeather();
|
|
var currentTod = fs.GetTime();
|
|
var spot = fs.GetSpot(ctx.Channel.Id);
|
|
|
|
var msg = await Response()
|
|
.Embed(CreateEmbed()
|
|
.WithPendingColor()
|
|
.WithAuthor(ctx.User)
|
|
.WithDescription($"""
|
|
{GetText(strs.fish_waiting)}
|
|
{FishItemCommands.GetMultiplierInfo(multis)}
|
|
""")
|
|
.AddField(GetText(strs.fish_spot), GetSpotEmoji(spot) + " " + spot.ToString(), true)
|
|
.AddField(GetText(strs.fish_weather),
|
|
GetWeatherEmoji(currentWeather) + " " + currentWeather,
|
|
true)
|
|
.AddField(GetText(strs.fish_tod), GetTodEmoji(currentTod) + " " + currentTod, true))
|
|
.SendAsync();
|
|
|
|
var res = await fishTask;
|
|
if (res is null)
|
|
{
|
|
await Response().Error(strs.fish_nothing).SendAsync();
|
|
return;
|
|
}
|
|
|
|
var desc = GetText(strs.fish_caught(res.Fish.Emoji + " " + Format.Bold(res.Fish.Name)));
|
|
|
|
if (res.IsSkillUp)
|
|
{
|
|
desc += "\n" + GetText(strs.fish_skill_up(res.Skill, res.MaxSkill));
|
|
}
|
|
|
|
await Response()
|
|
.Embed(CreateEmbed()
|
|
.WithOkColor()
|
|
.WithAuthor(ctx.User)
|
|
.WithDescription(desc)
|
|
.AddField(GetText(strs.fish_quality), fs.GetStarText(res.Stars, res.Fish.Stars), true)
|
|
.AddField(GetText(strs.desc), res.Fish.Fluff, true)
|
|
.WithThumbnailUrl(res.Fish.Image))
|
|
.SendAsync();
|
|
|
|
await msg.DeleteAsync();
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public async Task FishSpot()
|
|
{
|
|
var ws = fs.GetWeatherForPeriods(7);
|
|
var spot = fs.GetSpot(ctx.Channel.Id);
|
|
var time = fs.GetTime();
|
|
|
|
await Response()
|
|
.Embed(CreateEmbed()
|
|
.WithOkColor()
|
|
.WithDescription(GetText(strs.fish_weather_duration(fs.GetWeatherPeriodDuration())))
|
|
.AddField(GetText(strs.fish_spot), GetSpotEmoji(spot) + " " + spot, true)
|
|
.AddField(GetText(strs.fish_tod), GetTodEmoji(time) + " " + time, true)
|
|
.AddField(GetText(strs.fish_weather_forecast),
|
|
ws.Select(x => GetWeatherEmoji(x)).Join(""),
|
|
true))
|
|
.SendAsync();
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public async Task FishList(int page = 1)
|
|
{
|
|
if (--page < 0)
|
|
return;
|
|
|
|
var fishes = await fs.GetAllFish();
|
|
|
|
var catches = await fs.GetUserCatches(ctx.User.Id);
|
|
var (skill, maxSkill) = await fs.GetSkill(ctx.User.Id);
|
|
|
|
var catchDict = catches.ToDictionary(x => x.FishId, x => x);
|
|
|
|
var items = await fis.GetEquippedItemsAsync(ctx.User.Id);
|
|
var desc = $"""
|
|
🧠 {skill} / {maxSkill}
|
|
""";
|
|
|
|
foreach (var itemType in Enum.GetValues<FishItemType>())
|
|
{
|
|
var i = items.Where(x => x.Item.ItemType == itemType).ToArray();
|
|
|
|
desc += " · " + FishItemCommands.GetEmoji(itemType) + " " +
|
|
(i.Any() ? string.Join(", ", i.Select(x => x.Item.Name)) : "None");
|
|
}
|
|
|
|
await Response()
|
|
.Paginated()
|
|
.Items(fishes)
|
|
.PageSize(9)
|
|
.CurrentPage(page)
|
|
.Page((pageFish, i) =>
|
|
{
|
|
var eb = CreateEmbed()
|
|
.WithDescription(desc)
|
|
.WithAuthor(ctx.User)
|
|
.WithTitle(GetText(strs.fish_list_title))
|
|
.WithOkColor();
|
|
|
|
foreach (var f in pageFish)
|
|
{
|
|
if (catchDict.TryGetValue(f.Id, out var c))
|
|
{
|
|
eb.AddField(f.Name,
|
|
GetFishEmoji(f, c.Count)
|
|
+ " "
|
|
+ GetSpotEmoji(f.Spot)
|
|
+ GetTodEmoji(f.Time)
|
|
+ GetWeatherEmoji(f.Weather)
|
|
+ "\n"
|
|
+ fs.GetStarText(c.MaxStars, f.Stars)
|
|
+ "\n"
|
|
+ Format.Italics(f.Fluff),
|
|
true);
|
|
}
|
|
else
|
|
{
|
|
eb.AddField("?", GetFishEmoji(null, 0) + "\n" + fs.GetStarText(0, f.Stars), true);
|
|
}
|
|
}
|
|
|
|
return eb;
|
|
})
|
|
.SendAsync();
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public async Task FishLb(int page = 1)
|
|
{
|
|
if (--page < 0)
|
|
return;
|
|
|
|
await Response()
|
|
.Paginated()
|
|
.PageItems(async p => await fs.GetFishLbAsync(p))
|
|
.PageSize(9)
|
|
.Page(async (items, page) =>
|
|
{
|
|
var users = await us.GetUsersAsync(items.Select(x => x.UserId).ToArray());
|
|
|
|
var eb = CreateEmbed()
|
|
.WithTitle(GetText(strs.fish_lb_title))
|
|
.WithOkColor();
|
|
|
|
for (var i = 0; i < items.Count; i++)
|
|
{
|
|
var data = items[i];
|
|
var user = users.TryGetValue(data.UserId, out var ud)
|
|
? ud.ToString()
|
|
: data.UserId.ToString();
|
|
|
|
var text =
|
|
$"""
|
|
{GetText(strs.fish_unique(Format.Bold(data.Unique.ToString())))}
|
|
*{GetText(strs.fish_catches(data.Catches))}*
|
|
""";
|
|
|
|
eb.AddField("#" + (page * 9 + i + 1) + " | " + user,
|
|
text,
|
|
false);
|
|
}
|
|
|
|
return eb;
|
|
})
|
|
.SendAsync();
|
|
}
|
|
|
|
|
|
private string GetFishEmoji(FishData? fish, int count)
|
|
{
|
|
if (fish is null)
|
|
return "";
|
|
|
|
return fish.Emoji + " x" + count;
|
|
}
|
|
|
|
private string GetSpotEmoji(FishingSpot? spot)
|
|
{
|
|
if (spot is not FishingSpot fs)
|
|
return string.Empty;
|
|
|
|
var conf = fcs.Data;
|
|
|
|
return conf.SpotEmojis[(int)fs];
|
|
}
|
|
|
|
private string GetTodEmoji(FishingTime? fishTod)
|
|
{
|
|
return fishTod switch
|
|
{
|
|
FishingTime.Night => "🌑",
|
|
FishingTime.Dawn => "🌅",
|
|
FishingTime.Dusk => "🌆",
|
|
FishingTime.Day => "☀️",
|
|
_ => ""
|
|
};
|
|
}
|
|
|
|
private string GetWeatherEmoji(FishingWeather? w)
|
|
=> w switch
|
|
{
|
|
FishingWeather.Rain => "🌧️",
|
|
FishingWeather.Snow => "❄️",
|
|
FishingWeather.Storm => "⛈️",
|
|
FishingWeather.Clear => "☀️",
|
|
_ => ""
|
|
};
|
|
}
|
|
}
|
|
|
|
public enum FishingSpot
|
|
{
|
|
Ocean,
|
|
River,
|
|
Lake,
|
|
Swamp,
|
|
Reef
|
|
}
|
|
|
|
public enum FishingTime
|
|
{
|
|
Night,
|
|
Dawn,
|
|
Day,
|
|
Dusk
|
|
}
|
|
|
|
public enum FishingWeather
|
|
{
|
|
Clear,
|
|
Rain,
|
|
Storm,
|
|
Snow
|
|
} |