mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-19 15:51:58 +00:00
308 lines
15 KiB
C#
308 lines
15 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using UnityEngine;
|
|
using UnityEngine.Networking;
|
|
|
|
#if IL2CPP
|
|
using System.Text.Json;
|
|
#else
|
|
using Newtonsoft.Json;
|
|
#endif
|
|
|
|
// ReSharper disable InconsistentNaming
|
|
|
|
namespace EHR.Modules;
|
|
|
|
public static class Achievements
|
|
{
|
|
public enum Type
|
|
{
|
|
IdeasLiveOn, // As Undead, win with at least 3 Necromancers
|
|
HowCouldIBelieveThem, // Vote for Jester that was ejected
|
|
WhatKindOfGeniusCameUpWithThisRoleIdea, // Get Dad role
|
|
StabbingTheBack, // Kill a Crewmate as Backstabber
|
|
Honk, // Drag someone as Goose
|
|
Bruh, // Get vanilla Crewmate role
|
|
HowDoICraftThisAgain, // Craft something as Adventurer
|
|
ImCrewISwear, // Kill the last Impostor with all Neutral Killers dead
|
|
Covid20, // Infect 2 people as Virus
|
|
TheresThisGameMyDadTaughtMeItsCalledSwitch, // Swap as or be Swapped by Shifter at least 3 times in one round
|
|
FirstDayOnTheJob, // As an Amnesiac, remember that you were a Sidekick or Deathknight
|
|
UnderNewManagement, // As an Amnesiac, remember that you were a Cultist or Virus
|
|
OutOfTime, // Suicide or lose due to a time limit
|
|
TooCold, // Get frozen by Freezer, Beartrap, etc.
|
|
APerfectTimeToRewindIt, // Rewind time as Time Master
|
|
SerialKiller, // Kill everyone in FFA mode
|
|
PVPMaster, // Win in Solo PVP mode
|
|
HarderThanDrivingThroughTrafficLightsRight, // Win in Stop and Go mode
|
|
TwoFast4You, // Win in Speedrun mode
|
|
TooHotForMe, // Win in Hot Potato mode
|
|
SeekAndHide, // Win in Hide and Seek mode
|
|
Two012, // Win in Natural Disasters mode
|
|
YourFlagIsMine, // Win in Capture The Flag mode
|
|
BestReactionTime, // Win in Room Rush mode
|
|
KimJongUnExperience, // Vote for someone as Dictator
|
|
ImUnstoppable, // Get Medic's shield as Snitch
|
|
ThereCanOnlyBeOne, // Kill someone who had the same role as you
|
|
CantTouchThis, // As Detour, redirect at least 3 interactions
|
|
ItsMorbinTime, // Shapeshift into someone
|
|
WhatHaveIDone, // Kill your Lover, Romantic, Lawyer or Follower
|
|
BetrayalLevel100, // Kill Madmate as Impostor or Impostor as Madmate
|
|
TheRealTraitor, // Kill renegade as traitor (the neutral one)
|
|
DiePleaseDie, // Try to kill the same player with Medic's shield 2 times, both unsuccessfully
|
|
MasterDetective, // As Crewmate, vote only for Impostors and Neutral Killers
|
|
Superhero, // Kill all Impostors (including Renegade and Parasite) and Neutral Killers alone as Sheriff
|
|
TheKillingMachine2Point0, // Kill >= 7 players in the same game as Impostor/Neutral Killer
|
|
ALightInTheShadows, // Walk near the Beacon during the lights sabotage so they increase your vision
|
|
IForgotThisRoleExists, // Try to kill someone who is protected by Guardian Angel
|
|
TheLastSurvivor, // Win as a Crewmate, when you are the only alive player left
|
|
YouDidTheExactOppositeOfWhatYouWereSupposedToDo, // Die first as Maverick or Opportunist
|
|
WheresTheBlueShell, // Finish second in Room Rush
|
|
InnocentKiller, // Kill someone without having a kill button
|
|
YouWontTellAnyone, // Silence Snitch with all tasks done as Silencer
|
|
ExpertControl, // As Spurt, avoid reaching 0% and 100% speed for a round (If it was only to avoid 0 then they could just afk. Having to avoid both forces them to keep moving)
|
|
YoureMyFriendNow, // Recruit anyone as Gangster/Jackal/Infection/Virus/Cultist
|
|
ThatWasClose, // As a killer, kill Snitch with all their tasks done (or when Snitch is revealed)
|
|
CrewHero, // As Snitch, complete all your tasks, eject all Impostors and Neutral Killers, be alive until the end and win with crewmates
|
|
Transformer, // Complete at least 3 transformations in the same game (transformation is changing roles. For example Amnesiac => Sheriff => Sidekick)
|
|
InnocentImpostor, // Win as Impostor, not making any kill
|
|
ImpostorGang, // Win as Impostor with all your partners alive
|
|
FuriousAvenger, // Find yourself in a situation when Butcher kills Avenger and everyone dies
|
|
BrainStorm, // Get rid of all killers as a Crewmate and win with crewmates in less than 3 meetings (there must be at least 2 killers at the start of the game)
|
|
NothingCanStopLove, // Win as Lover/Loving Impostor with your partner alone (not with other teams)
|
|
SimonSays, // Complete the Simon's instruction
|
|
Unsuspected, // Win a game as a killer, not being voted by anyone in all meetings
|
|
YoureTooLate, // As Pestilence, have a killer try to kill you, but unsuccessfully for them
|
|
Armageddon, // Survive the Tremor's doom
|
|
GetDownMrPresident, // Protect the President as the Bodyguard
|
|
EconomicCompetition, // Kill Merchant as Bargainer, or kill Bargainer as Merchant
|
|
Gotcha, // Kill Bait and then get ejected
|
|
Speedrun, // Have a game end in under 1 minute
|
|
Carried, // Win as a madmate while the other impostors are dead
|
|
SorryToBurstYourBubble, // Explode 5 people with 1 encased player as the Bubble
|
|
GetDecrypted, // Successfully guess 3 roles as Decryptor
|
|
FlagMaster, // Carry the flag the longest in CTF
|
|
Tag, // Tag the most players in CTF
|
|
Vectory, // Vent 50 times in one game
|
|
Bloodbath, // Kill 8 people in the same game as Killing Machine
|
|
AntiSaboteur, // Fix all types of sabotages (Reactor, o2, Lights, Comms) as Technician (not necessarily in the same game)
|
|
TheBestInSchool, // Be the first who answer the Mathematician's question correctly
|
|
Awww, // Drag Penguin as Goose (this achievement is gain after the game end)
|
|
Ohhh, // Drag Goose as Penguin (this achievement is gain after the game end)
|
|
Hypnosis, // Try to report a dead body during the Hypnotist's ability
|
|
Mimicry, // Change your appearance at least 3 times as a Doppelgänger
|
|
P2W, // Buy all items as Bargainer and win in the same game
|
|
LuckOrObservation, // Guess 1 player correctly
|
|
BeginnerGuesser, // Guess 2 players correctly in the same game
|
|
GuessMaster, // Guess 4 players correctly in the same game
|
|
BestGuesserAward, // Guess 6 players correctly in the same game
|
|
BadLuckOrBadObservation, // Guess incorrectly and die
|
|
Duel, // Find yourself in a situation when you are 1vs1 against another killer
|
|
Delicious, // Get Donut Delivery's donut or a Chef's dish
|
|
Inquisition, // Kill Vampire/Witch/Warlock/Hex Master as Crusader
|
|
Censorship, // Silence 5 people in the same game as Silencer
|
|
LiarLiar, // As Lawyer, fail to protect your client that is crewmate, and then win with impostors somehow
|
|
IWishIReported, // Revive an Impostor/Converted Crewmate/Neutral Killer as Altruist
|
|
WellMeetAgainSomeSunnyDay, // Mark someone as Hookshot
|
|
AndForWhatDidICodeTheseCommandsForIfYouDontUseThemAtAll, // Don't use any command for an entire game
|
|
Prankster, // Shapeshift as Disco
|
|
WhatTheHell, // Type 666 in chat as Demon. Will others notice this? >:)
|
|
WhyJustWhy, // As Dictator, vote out the Jester
|
|
CommonEnemyNo1, // Win as any NK
|
|
CoordinatedAttack, // As Jester, Executioner or Innocent, win with any of the other 2 roles
|
|
ItsJustAPrankBro, // As Bomber, Kill half the lobby in 1 bomb
|
|
DrivingTestFailed, // Propel someone as the Car
|
|
MasterOfTheStones, // Get all infinity stones as Thanos
|
|
FastestRunner, // Win in Deathrace gamemode
|
|
CloseCall, // Survive russian roulette with 5 bullets as Roulette Grandeur
|
|
IKnowYourNames, // Get 3 peoples name guessed as Note Killer
|
|
ThisAintSquidGames, // Win in Mingle game mode
|
|
ItsGamblingTime, // As Sheriff, shoot a killer 10 seconds into the game
|
|
FriendlyFire, // Accidentally blow up your impostor partner as Sapper/Bomber/Fireworker
|
|
MyBad, // As Tree, kill everyone in ur radius by falling
|
|
MindReader, // As Perceiver, get all killers in your ability radius
|
|
OhNo, // Kill the Bait as your first kill
|
|
Why, // As Pawn, choose vanilla Crewmate after doing your tasks
|
|
GetMuted, // As Banshee, make everyone not have a chat button in a meeting
|
|
BadEncounter, // As Veteran, get killed while alerted (pestilence,pelican, bypass abilities, etc)
|
|
DestinysChoice, // Die to the Wyrd's fate countdown
|
|
YouUnderestimatedMe, // Get shielded by the Medic then kill them
|
|
Abstain, // Don't vote any player for the entire game
|
|
Collapse, // With Fragile, get ambushed by the Ambusher
|
|
YouCopiedMyWholeFlow, // As Pelican, have your body scavenged
|
|
Bloodthirsty, // As Juggernaut or Arrogance, reach your minimum kill cooldown
|
|
Lumberjack, // As Weapon Master, kill the Tree with your Axe
|
|
Massacre, // As Chronomancer, kill 4 or more people at once in a Slaughter
|
|
PayUp, // Kill a player as Clerk
|
|
AlarmClock, // Lose Sleepy from Glow getting near you
|
|
Eavesdropper, // Hear 3 or more messages with Listener in a single meeting
|
|
YouCopycat, // Have a Rift Maker go through your portal as Portal Maker
|
|
Easypeasy, // Correctly guess God as Decryptor
|
|
YourZoneIsMine, // Win in King Of The Zones game mode
|
|
Checkmate, // Change your role as Pawn, win as your new role
|
|
HeyRabek // Make H2SO4 as the Chemist
|
|
}
|
|
|
|
private static readonly string SaveFilePath = $"{Main.DataPath}/EHR_DATA/Achievements.json";
|
|
|
|
private const string ApiBaseUrl = "https://app.gurge44.eu/achievements";
|
|
private const string ApiSaveEndpoint = $"{ApiBaseUrl}/save";
|
|
private const string ApiLoadEndpoint = $"{ApiBaseUrl}/load";
|
|
|
|
public static readonly HashSet<Type> WaitingAchievements = [];
|
|
public static HashSet<Type> CompletedAchievements = [];
|
|
|
|
extension(Type type)
|
|
{
|
|
public void Complete()
|
|
{
|
|
if (!CompletedAchievements.Add(type)) return;
|
|
|
|
if (GameStates.IsEnded) WaitingAchievements.Add(type);
|
|
else ShowAchievementCompletion(type);
|
|
|
|
SaveAllData();
|
|
}
|
|
|
|
public void CompleteAfterGameEnd()
|
|
{
|
|
if (!CompletedAchievements.Add(type)) return;
|
|
WaitingAchievements.Add(type);
|
|
|
|
SaveAllData();
|
|
}
|
|
|
|
public void CompleteAfterDelay(float delay)
|
|
{
|
|
if (!CompletedAchievements.Add(type)) return;
|
|
Main.Instance.StartCoroutine(CompleteAchievementAfterDelayAsync());
|
|
SaveAllData();
|
|
return;
|
|
|
|
IEnumerator CompleteAchievementAfterDelayAsync()
|
|
{
|
|
var timer = 0f;
|
|
|
|
while (!GameStates.IsEnded && timer < delay)
|
|
{
|
|
yield return null;
|
|
timer += Time.deltaTime;
|
|
}
|
|
|
|
if (GameStates.IsEnded)
|
|
{
|
|
WaitingAchievements.Add(type);
|
|
yield break;
|
|
}
|
|
|
|
ShowAchievementCompletion(type);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void ShowWaitingAchievements()
|
|
{
|
|
WaitingAchievements.Do(ShowAchievementCompletion);
|
|
WaitingAchievements.Clear();
|
|
}
|
|
|
|
private static void ShowAchievementCompletion(Type type)
|
|
{
|
|
string title = Translator.GetString("AchievementCompletedTitle");
|
|
string description = Translator.GetString($"Achievement.{type}.Description");
|
|
var message = $"<b>{Translator.GetString($"Achievement.{type}")}</b>\n{description}";
|
|
|
|
ChatBubbleShower.ShowChatBubbleInRound(message, title);
|
|
}
|
|
|
|
private static void SaveAllData()
|
|
{
|
|
#if IL2CPP
|
|
string json = JsonSerializer.Serialize(CompletedAchievements);
|
|
#else
|
|
string json = JsonConvert.SerializeObject(CompletedAchievements);
|
|
#endif
|
|
File.WriteAllText(SaveFilePath, json);
|
|
|
|
if (!Options.StoreCompletedAchievementsOnEHRDatabase.GetBool()) return;
|
|
Main.Instance.StartCoroutine(SendAchievementsToApiAsync());
|
|
return;
|
|
|
|
IEnumerator SendAchievementsToApiAsync()
|
|
{
|
|
while (!PlayerControl.LocalPlayer) yield return null;
|
|
|
|
string userId = PlayerControl.LocalPlayer.GetClient().GetHashedPuid();
|
|
|
|
var data = new
|
|
{
|
|
userId,
|
|
achievements = CompletedAchievements
|
|
};
|
|
|
|
#if IL2CPP
|
|
string payload = JsonSerializer.Serialize(data);
|
|
#else
|
|
string payload = JsonConvert.SerializeObject(data);
|
|
#endif
|
|
|
|
var request = new UnityWebRequest(ApiSaveEndpoint, UnityWebRequest.kHttpVerbPOST)
|
|
{
|
|
uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(payload)),
|
|
downloadHandler = new DownloadHandlerBuffer()
|
|
};
|
|
|
|
request.SetRequestHeader("Content-Type", "application/json");
|
|
request.SetRequestHeader("User-Agent", $"{Main.ModName} v{Main.PluginVersion}");
|
|
yield return request.SendWebRequest();
|
|
|
|
Logger.Msg(request.result != UnityWebRequest.Result.Success ? $"Error saving achievements: {request.error}" : "Achievements saved successfully", "Achievements.SaveAllData");
|
|
}
|
|
}
|
|
|
|
public static void LoadAllData()
|
|
{
|
|
if (File.Exists(SaveFilePath))
|
|
{
|
|
string json = File.ReadAllText(SaveFilePath);
|
|
#if IL2CPP
|
|
CompletedAchievements = JsonSerializer.Deserialize<HashSet<Type>>(json);
|
|
#else
|
|
CompletedAchievements = JsonConvert.DeserializeObject<HashSet<Type>>(json);
|
|
#endif
|
|
}
|
|
else if (Options.StoreCompletedAchievementsOnEHRDatabase.GetBool())
|
|
{
|
|
Main.Instance.StartCoroutine(FetchAchievementsFromApiAsync());
|
|
return;
|
|
|
|
IEnumerator FetchAchievementsFromApiAsync()
|
|
{
|
|
while (!PlayerControl.LocalPlayer) yield return null;
|
|
yield return new WaitForSecondsRealtime(3f);
|
|
|
|
string userId = PlayerControl.LocalPlayer.GetClient().GetHashedPuid();
|
|
var url = $"{ApiLoadEndpoint}?userId={userId}";
|
|
|
|
UnityWebRequest request = UnityWebRequest.Get(url);
|
|
request.SetRequestHeader("User-Agent", $"{Main.ModName} v{Main.PluginVersion}");
|
|
yield return request.SendWebRequest();
|
|
|
|
if (request.result != UnityWebRequest.Result.Success)
|
|
Logger.Error($"Error loading achievements: {request.error}", "Achievements.LoadAllData");
|
|
else
|
|
{
|
|
string json = request.downloadHandler.text;
|
|
#if IL2CPP
|
|
CompletedAchievements = JsonSerializer.Deserialize<HashSet<Type>>(json);
|
|
#else
|
|
CompletedAchievements = JsonConvert.DeserializeObject<HashSet<Type>>(json);
|
|
#endif
|
|
File.WriteAllText(SaveFilePath, json);
|
|
Logger.Info("Achievements loaded successfully.", "Achievements.LoadAllData");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |