--- /dev/null
+name: AutoBuild .NET
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+ build:
+ runs-on: ubuntu-20.04
+
+ steps:
+ - uses: actions/cache@v2
+ with:
+ path: |
+ ~/.nuget/packages
+ ~/.cache/bepinex
+ key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
+ restore-keys: |
+ ${{ runner.os }}-nuget-
+
+ - uses: actions/checkout@v2
+ with:
+ submodules: true
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: 6.x
+
+ - name: Build
+ run: dotnet build TheOtherRoles/TheOtherRoles.csproj --configuration Release
+
+ - name: Upload TheOtherRoles
+ uses: actions/upload-artifact@v2
+ with:
+ name: TheOtherRoles.dll
+ path: TheOtherRoles/bin/Release/netstandard2.1/TheOtherRoles.dll
\ No newline at end of file
# Releases
| Among Us - Version| Mod Version | Link |
|----------|-------------|-----------------|
+| 2022.6.21| v4.1.6| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v4.1.6/TheOtherRoles.zip)
| 2022.6.21| v4.1.5| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v4.1.5/TheOtherRoles.zip)
| 2022.3.29| v4.1.4| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v4.1.4/TheOtherRoles.zip)
| 2022.3.29| v4.1.3| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v4.1.3/TheOtherRoles.zip)
<details>
<summary>Click to show the Changelog</summary>
+**Version 4.1.6**
+- Added Sound Effects: Most buttons / role funcionalities now have a sound!
+- Added a mod option: Enable Sound Effects
+- Added an option: Hide After Death Modifiers: Modifiers which will have an effect after your death will be hidden while the player is alive so they cant be claimed / called
+- Added lobby timer sync, so that each player can see when the lobby will close on Vanilla Servers
+- Added a credits screen to the main menu
+- Re-Added the player's last position to the map in meetings
+- Changed the custom region ui
+- Fixed a bug where the Spy's Name appearing in white to the impostors in the intro cutscene
+- Fixed a bug where the zoom function for ghosts would zoom in too far
+- Fixed a bug where custom color names did not work (TOR is now compatible with colorblind mode)
+- Fixed a bug where the ninjas color name did not become invisible in colorblind mode
+- Fixed a bug where Morphling & Camouflager did not work correctly for skins with adaptive color
+- Fixed a bug where the first kill shield was given to the wrong lover
+- Fixed a bug where Pursuer appeared in the GuesserUI even when Lawyer was disabled
+- Fixed a bug where a guessed Guesser could still guess if they had the guesser ui open
+- Fixed a bug where the role assignment did not work properly (fingers crossed)
+
**Version 4.1.5**
- Updated to Among Us version 2022.6.21
- Added Custom servers (Modded EU & Modded NA). Thanks to [miniduikboot](https://github.com/miniduikboot) & GD
## Sheriff
### **Team: Crewmates**
-The Sheriff has the ability to kill Impostors.\
+The Sheriff has the ability to kill Impostors or Neutral roles if enabled.\
If they try to kill a Crewmate, they die instead.\
\
**NOTE:**
- If the Sheriff shoots the person the Medic shielded, the Sheriff and the shielded person **both remain unharmed**.
-- If the Sheriff shoots a Mini Impostor, the Sheriff dies if the Mini is still growing up. If it's 18, the Mini Impostor dies.
+- If the Sheriff shoots a Mini Impostor while growing up, nothing happens. If it's fully grown, the Mini Impostor dies.
### Game Options
| Name | Description |
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TheOtherRoles", "TheOtherRoles\TheOtherRoles.csproj", "{11FBC798-BAF5-4EE5-9511-BE6DB0592F99}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TheOtherUpdater", "TheOtherUpdater\TheOtherUpdater.vcxproj", "{73A5662B-9C59-4C97-95C5-F5B2A8BBDDCA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
{11FBC798-BAF5-4EE5-9511-BE6DB0592F99}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11FBC798-BAF5-4EE5-9511-BE6DB0592F99}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11FBC798-BAF5-4EE5-9511-BE6DB0592F99}.Release|Any CPU.Build.0 = Release|Any CPU
+ {73A5662B-9C59-4C97-95C5-F5B2A8BBDDCA}.Debug|Any CPU.ActiveCfg = Debug|Win32
+ {73A5662B-9C59-4C97-95C5-F5B2A8BBDDCA}.Debug|Any CPU.Build.0 = Debug|Win32
+ {73A5662B-9C59-4C97-95C5-F5B2A8BBDDCA}.Release|Any CPU.ActiveCfg = Release|Win32
+ {73A5662B-9C59-4C97-95C5-F5B2A8BBDDCA}.Release|Any CPU.Build.0 = Release|Win32
EndGlobalSection
EndGlobal
public static CustomButton zoomOutButton;
public static Dictionary<byte, List<CustomButton>> deputyHandcuffedButtons = null;
-
- public static bool zoomOutStatus = false;
public static PoolablePlayer morphTargetDisplay;
public static TMPro.TMP_Text securityGuardButtonScrewsText;
timeMasterShieldButton.Timer = timeMasterShieldButton.MaxTimer;
timeMasterShieldButton.isEffectActive = false;
timeMasterShieldButton.actionButton.cooldownTimerText.color = Palette.EnabledColor;
+ SoundEffectsManager.stop("timemasterShield");
}
private static void addReplacementHandcuffedButton(CustomButton button, Vector3? positionOffset = null, Func<bool> couldUse = null)
MessageWriter usedRepairWriter = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.EngineerUsedRepair, Hazel.SendOption.Reliable, -1);
AmongUsClient.Instance.FinishRpcImmediately(usedRepairWriter);
RPCProcedure.engineerUsedRepair();
-
+ SoundEffectsManager.play("engineerRepair");
foreach (PlayerTask task in CachedPlayer.LocalPlayer.PlayerControl.myTasks.GetFastEnumerator()) {
if (task.TaskType == TaskTypes.FixLights) {
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.EngineerFixLights, Hazel.SendOption.Reliable, -1);
AmongUsClient.Instance.FinishRpcImmediately(writer);
RPCProcedure.cleanBody(playerInfo.PlayerId);
janitorCleanButton.Timer = janitorCleanButton.MaxTimer;
+ SoundEffectsManager.play("cleanerClean");
break;
}
RPCProcedure.deputyUsedHandcuffs(targetId);
Deputy.currentTarget = null;
deputyHandcuffButton.Timer = deputyHandcuffButton.MaxTimer;
+
+ SoundEffectsManager.play("deputyHandcuff");
},
() => { return (Deputy.deputy != null && Deputy.deputy == CachedPlayer.LocalPlayer.PlayerControl || Sheriff.sheriff != null && Sheriff.sheriff == CachedPlayer.LocalPlayer.PlayerControl && Sheriff.sheriff == Sheriff.formerDeputy && Deputy.keepsHandcuffsOnPromotion) && !CachedPlayer.LocalPlayer.Data.IsDead; },
() => {
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.TimeMasterShield, Hazel.SendOption.Reliable, -1);
AmongUsClient.Instance.FinishRpcImmediately(writer);
RPCProcedure.timeMasterShield();
+ SoundEffectsManager.play("timemasterShield");
},
() => { return TimeMaster.timeMaster != null && TimeMaster.timeMaster == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; },
() => { return CachedPlayer.LocalPlayer.PlayerControl.CanMove; },
KeyCode.F,
true,
TimeMaster.shieldDuration,
- () => { timeMasterShieldButton.Timer = timeMasterShieldButton.MaxTimer; }
+ () => {
+ timeMasterShieldButton.Timer = timeMasterShieldButton.MaxTimer;
+ SoundEffectsManager.stop("timemasterShield");
+
+ }
);
// Medic Shield
else
RPCProcedure.medicSetShielded(Medic.currentTarget.PlayerId);
Medic.meetingAfterShielding = false;
+
+ SoundEffectsManager.play("medicShield");
},
() => { return Medic.medic != null && Medic.medic == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; },
() => { return !Medic.usedShield && Medic.currentTarget && CachedPlayer.LocalPlayer.PlayerControl.CanMove; },
writer.Write(Shifter.currentTarget.PlayerId);
AmongUsClient.Instance.FinishRpcImmediately(writer);
RPCProcedure.setFutureShifted(Shifter.currentTarget.PlayerId);
+ SoundEffectsManager.play("shifterShift");
},
() => { return Shifter.shifter != null && Shifter.shifter == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; },
() => { return Shifter.currentTarget && Shifter.futureShift == null && CachedPlayer.LocalPlayer.PlayerControl.CanMove; },
RPCProcedure.morphlingMorph(Morphling.sampledTarget.PlayerId);
Morphling.sampledTarget = null;
morphlingButton.EffectDuration = Morphling.duration;
-
+ SoundEffectsManager.play("morphlingMorph");
} else if (Morphling.currentTarget != null) {
Morphling.sampledTarget = Morphling.currentTarget;
morphlingButton.Sprite = Morphling.getMorphSprite();
morphlingButton.EffectDuration = 1f;
+ SoundEffectsManager.play("morphlingSample");
// Add poolable player to the button so that the target outfit is shown
morphlingButton.actionButton.cooldownTimerText.transform.localPosition = new Vector3(0, 0, -1f); // Before the poolable player
if (Morphling.sampledTarget == null) {
morphlingButton.Timer = morphlingButton.MaxTimer;
morphlingButton.Sprite = Morphling.getSampleSprite();
+ SoundEffectsManager.play("morphlingMorph");
// Reset the poolable player
morphTargetDisplay.gameObject.SetActive(false);
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.CamouflagerCamouflage, Hazel.SendOption.Reliable, -1);
AmongUsClient.Instance.FinishRpcImmediately(writer);
RPCProcedure.camouflagerCamouflage();
+ SoundEffectsManager.play("morphlingMorph");
},
() => { return Camouflager.camouflager != null && Camouflager.camouflager == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; },
() => { return CachedPlayer.LocalPlayer.PlayerControl.CanMove; },
KeyCode.F,
true,
Camouflager.duration,
- () => { camouflagerButton.Timer = camouflagerButton.MaxTimer; }
+ () => {
+ camouflagerButton.Timer = camouflagerButton.MaxTimer;
+ SoundEffectsManager.play("morphlingMorph");
+ }
);
// Hacker button
hackerButton = new CustomButton(
() => {
Hacker.hackerTimer = Hacker.duration;
+ SoundEffectsManager.play("hackerHack");
},
() => { return Hacker.hacker != null && Hacker.hacker == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; },
() => { return true; },
writer.Write(Tracker.currentTarget.PlayerId);
AmongUsClient.Instance.FinishRpcImmediately(writer);
RPCProcedure.trackerUsedTracker(Tracker.currentTarget.PlayerId);
+ SoundEffectsManager.play("trackerTrackPlayer");
},
() => { return Tracker.tracker != null && Tracker.tracker == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; },
() => { return CachedPlayer.LocalPlayer.PlayerControl.CanMove && Tracker.currentTarget != null && !Tracker.usedTracker; },
);
trackerTrackCorpsesButton = new CustomButton(
- () => { Tracker.corpsesTrackingTimer = Tracker.corpsesTrackingDuration; },
+ () => { Tracker.corpsesTrackingTimer = Tracker.corpsesTrackingDuration;
+ SoundEffectsManager.play("trackerTrackCorpses"); },
() => { return Tracker.tracker != null && Tracker.tracker == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead && Tracker.canTrackCorpses; },
() => { return CachedPlayer.LocalPlayer.PlayerControl.CanMove; },
() => {
RPCProcedure.vampireSetBitten(byte.MaxValue, byte.MaxValue);
}
})));
+ SoundEffectsManager.play("vampireBite");
vampireKillButton.HasEffect = true; // Trigger effect on this click
}
MessageWriter writer = AmongUsClient.Instance.StartRpc(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.PlaceGarlic, Hazel.SendOption.Reliable);
writer.WriteBytesAndSize(buff);
writer.EndMessage();
- RPCProcedure.placeGarlic(buff);
+ RPCProcedure.placeGarlic(buff);
+ SoundEffectsManager.play("garlic");
},
() => { return !Vampire.localPlacedGarlic && !CachedPlayer.LocalPlayer.Data.IsDead && Vampire.garlicsActive; },
() => { return CachedPlayer.LocalPlayer.PlayerControl.CanMove && !Vampire.localPlacedGarlic; },
writer.WriteBytesAndSize(buff);
writer.EndMessage();
RPCProcedure.placePortal(buff);
+ SoundEffectsManager.play("tricksterPlaceBox");
},
() => { return Portalmaker.portalmaker != null && Portalmaker.portalmaker == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead && Portal.secondPortal == null; },
() => { return CachedPlayer.LocalPlayer.PlayerControl.CanMove && Portal.secondPortal == null; },
}
RPCProcedure.usePortal(CachedPlayer.LocalPlayer.PlayerId);
usePortalButton.Timer = usePortalButton.MaxTimer;
+ SoundEffectsManager.play("portalUse");
FastDestroyableSingleton<HudManager>.Instance.StartCoroutine(Effects.Lerp(Portal.teleportDuration, new Action<float>((p) => { // Delayed action
CachedPlayer.LocalPlayer.PlayerControl.moveable = false;
CachedPlayer.LocalPlayer.NetTransform.Halt();
writer.Write(Jackal.currentTarget.PlayerId);
AmongUsClient.Instance.FinishRpcImmediately(writer);
RPCProcedure.jackalCreatesSidekick(Jackal.currentTarget.PlayerId);
+ SoundEffectsManager.play("jackalSidekick");
},
() => { return Jackal.canCreateSidekick && Jackal.jackal != null && Jackal.jackal == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; },
() => { return Jackal.canCreateSidekick && Jackal.currentTarget != null && CachedPlayer.LocalPlayer.PlayerControl.CanMove; },
lighterButton = new CustomButton(
() => {
Lighter.lighterTimer = Lighter.duration;
+ SoundEffectsManager.play("lighterLight");
},
() => { return Lighter.lighter != null && Lighter.lighter == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; },
() => { return CachedPlayer.LocalPlayer.PlayerControl.CanMove; },
KeyCode.F,
true,
Lighter.duration,
- () => { lighterButton.Timer = lighterButton.MaxTimer; }
+ () => {
+ lighterButton.Timer = lighterButton.MaxTimer;
+ SoundEffectsManager.play("lighterLight");
+ }
);
// Eraser erase button
writer.Write(Eraser.currentTarget.PlayerId);
AmongUsClient.Instance.FinishRpcImmediately(writer);
RPCProcedure.setFutureErased(Eraser.currentTarget.PlayerId);
+ SoundEffectsManager.play("eraserErase");
},
() => { return Eraser.eraser != null && Eraser.eraser == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; },
() => { return CachedPlayer.LocalPlayer.PlayerControl.CanMove && Eraser.currentTarget != null; },
MessageWriter writer = AmongUsClient.Instance.StartRpc(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.PlaceJackInTheBox, Hazel.SendOption.Reliable);
writer.WriteBytesAndSize(buff);
writer.EndMessage();
- RPCProcedure.placeJackInTheBox(buff);
+ RPCProcedure.placeJackInTheBox(buff);
+ SoundEffectsManager.play("tricksterPlaceBox");
},
() => { return Trickster.trickster != null && Trickster.trickster == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead && !JackInTheBox.hasJackInTheBoxLimitReached(); },
() => { return CachedPlayer.LocalPlayer.PlayerControl.CanMove && !JackInTheBox.hasJackInTheBoxLimitReached(); },
() => {
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.LightsOut, Hazel.SendOption.Reliable, -1);
AmongUsClient.Instance.FinishRpcImmediately(writer);
- RPCProcedure.lightsOut();
+ RPCProcedure.lightsOut();
+ SoundEffectsManager.play("lighterLight");
},
() => { return Trickster.trickster != null && Trickster.trickster == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead && JackInTheBox.hasJackInTheBoxLimitReached() && JackInTheBox.boxesConvertedToVents; },
() => { return CachedPlayer.LocalPlayer.PlayerControl.CanMove && JackInTheBox.hasJackInTheBoxLimitReached() && JackInTheBox.boxesConvertedToVents; },
KeyCode.F,
true,
Trickster.lightsOutDuration,
- () => { lightsOutButton.Timer = lightsOutButton.MaxTimer; }
+ () => {
+ lightsOutButton.Timer = lightsOutButton.MaxTimer;
+ SoundEffectsManager.play("lighterLight");
+ }
);
// Cleaner Clean
RPCProcedure.cleanBody(playerInfo.PlayerId);
Cleaner.cleaner.killTimer = cleanerCleanButton.Timer = cleanerCleanButton.MaxTimer;
+ SoundEffectsManager.play("cleanerClean");
break;
}
}
Warlock.curseVictim = Warlock.currentTarget;
warlockCurseButton.Sprite = Warlock.getCurseKillButtonSprite();
warlockCurseButton.Timer = 1f;
+ SoundEffectsManager.play("warlockCurse");
} else if (Warlock.curseVictim != null && Warlock.curseVictimTarget != null) {
MurderAttemptResult murder = Helpers.checkMuderAttemptAndKill(Warlock.warlock, Warlock.curseVictimTarget, showAnimation: false);
if (murder == MurderAttemptResult.SuppressKill) return;
writer.EndMessage();
RPCProcedure.placeCamera(buff);
}
+ SoundEffectsManager.play("securityGuardPlaceCam"); // Same sound used for both types (cam or vent)!
securityGuardButton.Timer = securityGuardButton.MaxTimer;
},
() => { return SecurityGuard.securityGuard != null && SecurityGuard.securityGuard == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead && SecurityGuard.remainingScrews >= Mathf.Min(SecurityGuard.ventPrice, SecurityGuard.camPrice); },
arsonistButton.HasEffect = false;
} else if (Arsonist.currentTarget != null) {
Arsonist.douseTarget = Arsonist.currentTarget;
- arsonistButton.HasEffect = true;
+ arsonistButton.HasEffect = true;
+ SoundEffectsManager.play("arsonistDouse");
}
},
() => { return Arsonist.arsonist != null && Arsonist.arsonist == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; },
Vulture.cooldown = vultureEatButton.Timer = vultureEatButton.MaxTimer;
Vulture.eatenBodies++;
+ SoundEffectsManager.play("vultureEat");
break;
}
}
if (Medium.target != null) {
Medium.soulTarget = Medium.target;
mediumButton.HasEffect = true;
+ SoundEffectsManager.play("mediumAsk");
}
},
() => { return Medium.medium != null && Medium.medium == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; },
Medium.souls.Remove(target);
}
+ SoundEffectsManager.stop("mediumAsk");
}
);
Pursuer.blanks++;
pursuerButton.Timer = pursuerButton.MaxTimer;
+ SoundEffectsManager.play("pursuerBlank");
}
},
() => {
if (Witch.currentTarget != null) {
Witch.spellCastingTarget = Witch.currentTarget;
+ SoundEffectsManager.play("witchSpell");
}
},
() => { return Witch.witch != null && Witch.witch == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; },
if (Ninja.currentTarget != null) {
Ninja.ninjaMarked = Ninja.currentTarget;
ninjaButton.Timer = 5f;
+ SoundEffectsManager.play("warlockCurse");
}
},
() => { return Ninja.ninja != null && Ninja.ninja == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; },
public static CustomOption pursuerCooldown;
public static CustomOption pursuerBlanksNumber;
+ public static CustomOption modifiersAreHidden;
+
public static CustomOption modifierBait;
public static CustomOption modifierBaitQuantity;
public static CustomOption modifierBaitReportDelayMin;
mediumOneTimeUse = CustomOption.Create(363, Types.Crewmate, "Each Soul Can Only Be Questioned Once", false, mediumSpawnRate);
// Modifier
+ modifiersAreHidden = CustomOption.Create(1009, Types.Modifier, cs(Color.yellow, "Hide After Death Modifiers"), true, null, true);
+
modifierBloody = CustomOption.Create(1000, Types.Modifier, cs(Color.yellow, "Bloody"), rates, null, true);
modifierBloodyQuantity = CustomOption.Create(1001, Types.Modifier, cs(Color.yellow, "Bloody Quantity"), ratesModifier, modifierBloody);
modifierBloodyDuration = CustomOption.Create(1002, Types.Modifier, "Trail Duration", 10f, 3f, 60f, 1f, modifierBloody);
return null;
}
+ public static AudioClip loadAudioClipFromResources(string path, string clipName = "UNNAMED_TOR_AUDIO_CLIP") {
+ // must be "raw (headerless) 2-channel signed 32 bit pcm (le)" (can e.g. use Audacity® to export)
+ try {
+ Assembly assembly = Assembly.GetExecutingAssembly();
+ Stream stream = assembly.GetManifestResourceStream(path);
+ var byteAudio = new byte[stream.Length];
+ _ = stream.Read(byteAudio, 0, (int)stream.Length);
+ float[] samples = new float[byteAudio.Length / 4]; // 4 bytes per sample
+ int offset;
+ for (int i = 0; i < samples.Length; i++) {
+ offset = i * 4;
+ samples[i] = (float)BitConverter.ToInt32(byteAudio, offset) / Int32.MaxValue;
+ }
+ int channels = 2;
+ int sampleRate = 48000;
+ AudioClip audioClip = AudioClip.Create(clipName, samples.Length, channels, sampleRate, false);
+ audioClip.SetData(samples, 0);
+ return audioClip;
+ } catch {
+ System.Console.WriteLine("Error loading AudioClip from resources: " + path);
+ }
+ return null;
+
+ /* Usage example:
+ AudioClip exampleClip = Helpers.loadAudioClipFromResources("TheOtherRoles.Resources.exampleClip.raw");
+ if (Constants.ShouldPlaySfx()) SoundManager.Instance.PlaySound(exampleClip, false, 0.8f);
+ */
+ }
public static PlayerControl playerById(byte id)
{
foreach (PlayerControl player in CachedPlayer.AllPlayers)
else clip = nextSkin.IdleAnim;
float progress = playerPhysics.Animator.m_animator.GetCurrentAnimatorStateInfo(0).normalizedTime;
playerPhysics.myPlayer.cosmetics.skin.skin = nextSkin;
- if (playerPhysics.myPlayer.cosmetics.skin.layer.material == FastDestroyableSingleton<HatManager>.Instance.PlayerMaterial)
- target.SetPlayerMaterialColors(playerPhysics.myPlayer.cosmetics.skin.layer);
+ playerPhysics.myPlayer.cosmetics.skin.UpdateMaterial();
+
spriteAnim.Play(clip, 1f);
spriteAnim.m_animator.Play("a", 0, progress % 1);
spriteAnim.m_animator.Update(0f);
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(killer.NetId, (byte)CustomRPC.ShieldedMurderAttempt, Hazel.SendOption.Reliable, -1);
AmongUsClient.Instance.FinishRpcImmediately(writer);
RPCProcedure.shieldedMurderAttempt();
+ SoundEffectsManager.play("fail");
return MurderAttemptResult.SuppressKill;
}
writer.Write((byte)TheOtherRolesPlugin.Version.Major);
writer.Write((byte)TheOtherRolesPlugin.Version.Minor);
writer.Write((byte)TheOtherRolesPlugin.Version.Build);
+ writer.Write(AmongUsClient.Instance.AmHost ? Patches.GameStartManagerPatch.timer : -1f);
writer.WritePacked(AmongUsClient.Instance.ClientId);
writer.Write((byte)(TheOtherRolesPlugin.Version.Revision < 0 ? 0xFF : TheOtherRolesPlugin.Version.Revision));
writer.Write(Assembly.GetExecutingAssembly().ManifestModule.ModuleVersionId.ToByteArray());
}
+ public static bool zoomOutStatus = false;
public static void toggleZoom(bool reset=false) {
- float zoomFactor = 4f;
- if (HudManagerStartPatch.zoomOutStatus)
- zoomFactor = 1 / zoomFactor;
- else if (reset) return; // Dont zoom out if meant to reset.
- HudManagerStartPatch.zoomOutStatus = !HudManagerStartPatch.zoomOutStatus;
- Camera.main.orthographicSize *= zoomFactor;
+ TheOtherRolesPlugin.Logger.LogMessage(Camera.main.orthographicSize);
+ float orthographicSize = reset || zoomOutStatus ? 3f : 12f;
+
+ zoomOutStatus = !zoomOutStatus && !reset;
+ Camera.main.orthographicSize = orthographicSize;
foreach (var cam in Camera.allCameras) {
- if (cam != null && cam.gameObject.name == "UI Camera") cam.orthographicSize *= zoomFactor; // The UI is scaled too, else we cant click the buttons. Downside: map is super small.
+ if (cam != null && cam.gameObject.name == "UI Camera") cam.orthographicSize = orthographicSize; // The UI is scaled too, else we cant click the buttons. Downside: map is super small.
}
- HudManagerStartPatch.zoomOutButton.Sprite = HudManagerStartPatch.zoomOutStatus ? Helpers.loadSpriteFromResources("TheOtherRoles.Resources.PlusButton.png", 150f / zoomFactor * 2) : Helpers.loadSpriteFromResources("TheOtherRoles.Resources.MinusButton.png", 150f);
- HudManagerStartPatch.zoomOutButton.PositionOffset = HudManagerStartPatch.zoomOutStatus ? new Vector3(0f, 3f, 0) : new Vector3(0.4f, 2.8f, 0);
+ HudManagerStartPatch.zoomOutButton.Sprite = zoomOutStatus ? Helpers.loadSpriteFromResources("TheOtherRoles.Resources.PlusButton.png", 75f) : Helpers.loadSpriteFromResources("TheOtherRoles.Resources.MinusButton.png", 150f);
+ HudManagerStartPatch.zoomOutButton.PositionOffset = zoomOutStatus ? new Vector3(0f, 3f, 0) : new Vector3(0.4f, 2.8f, 0);
ResolutionManager.ResolutionChanged.Invoke((float)Screen.width / Screen.height); // This will move button positions to the correct position.
}
public class TheOtherRolesPlugin : BasePlugin
{
public const string Id = "me.eisbison.theotherroles";
- public const string VersionString = "4.1.5";
+ public const string VersionString = "4.1.6";
public static Version Version = Version.Parse(VersionString);
internal static BepInEx.Logging.ManualLogSource Logger;
public static ConfigEntry<bool> GhostsSeeVotes{ get; set; }
public static ConfigEntry<bool> ShowRoleSummary { get; set; }
public static ConfigEntry<bool> ShowLighterDarker { get; set; }
+ public static ConfigEntry<bool> EnableSoundEffects { get; set; }
public static ConfigEntry<bool> EnableHorseMode { get; set; }
public static ConfigEntry<string> Ip { get; set; }
public static ConfigEntry<ushort> Port { get; set; }
public static void UpdateRegions() {
ServerManager serverManager = FastDestroyableSingleton<ServerManager>.Instance;
var regions = new IRegionInfo[] {
- new DnsRegionInfo(Ip.Value, "Custom", StringNames.NoTranslation, Ip.Value, Port.Value, false).CastFast<IRegionInfo>(),
new DnsRegionInfo("mods.hopto.org", "Modded NA (MNA)", StringNames.NoTranslation, "mods.hopto.org", 443, false).CastFast<IRegionInfo>(),
- new DnsRegionInfo("au-eu.duikbo.at", "Modded EU (MEU)", StringNames.NoTranslation, "au-eu.duikbo.at", 22023, false).CastFast<IRegionInfo>()
+ new DnsRegionInfo("au-eu.duikbo.at", "Modded EU (MEU)", StringNames.NoTranslation, "au-eu.duikbo.at", 22023, false).CastFast<IRegionInfo>(),
+ new DnsRegionInfo(Ip.Value, "Custom", StringNames.NoTranslation, Ip.Value, Port.Value, false).CastFast<IRegionInfo>()
};
IRegionInfo ? currentRegion = serverManager.CurrentRegion;
GhostsSeeVotes = Config.Bind("Custom", "Ghosts See Votes", true);
ShowRoleSummary = Config.Bind("Custom", "Show Role Summary", true);
ShowLighterDarker = Config.Bind("Custom", "Show Lighter / Darker", true);
+ EnableSoundEffects = Config.Bind("Custom", "Enable Sound Effects", true);
EnableHorseMode = Config.Bind("Custom", "Enable Horse Mode", false);
ShowPopUpVersion = Config.Bind("Custom", "Show PopUp", "0");
public static bool showRoleSummary = true;
public static bool allowParallelMedBayScans = false;
public static bool showLighterDarker = true;
+ public static bool enableSoundEffects = true;
public static bool enableHorseMode = false;
public static bool shieldFirstKill = false;
ghostsSeeVotes = TheOtherRolesPlugin.GhostsSeeVotes.Value;
showRoleSummary = TheOtherRolesPlugin.ShowRoleSummary.Value;
showLighterDarker = TheOtherRolesPlugin.ShowLighterDarker.Value;
+ enableSoundEffects = TheOtherRolesPlugin.EnableSoundEffects.Value;
enableHorseMode = TheOtherRolesPlugin.EnableHorseMode.Value;
Patches.ShouldAlwaysHorseAround.isHorseMode = TheOtherRolesPlugin.EnableHorseMode.Value;
}
color = new Color32(0xDB, 0xFD, 0x2F, byte.MaxValue),
shadow = new Color32(0x74, 0xE5, 0x10, byte.MaxValue),
isLighterColor = true });
- colors.Add(new CustomColor { longname = "Signal Orange",
+ colors.Add(new CustomColor { longname = "Signal\nOrange",
color = new Color32(0xF7, 0x44, 0x17, byte.MaxValue),
shadow = new Color32(0x9B, 0x2E, 0x0F, byte.MaxValue),
isLighterColor = true });
typeof(Il2CppReferenceArray<Il2CppSystem.Object>)
})]
private class ColorStringPatch {
+ [HarmonyPriority(Priority.Last)]
public static bool Prefix(ref string __result, [HarmonyArgument(0)] StringNames name) {
if ((int)name >= 50000) {
string text = CustomColors.ColorStrings[(int)name];
new SelectionBehaviour("Ghosts Can Additionally See Modifier", () => MapOptions.ghostsSeeModifier = TheOtherRolesPlugin.GhostsSeeModifier.Value = !TheOtherRolesPlugin.GhostsSeeModifier.Value, TheOtherRolesPlugin.GhostsSeeModifier.Value),
new SelectionBehaviour("Show Role Summary", () => MapOptions.showRoleSummary = TheOtherRolesPlugin.ShowRoleSummary.Value = !TheOtherRolesPlugin.ShowRoleSummary.Value, TheOtherRolesPlugin.ShowRoleSummary.Value),
new SelectionBehaviour("Show Lighter / Darker", () => MapOptions.showLighterDarker = TheOtherRolesPlugin.ShowLighterDarker.Value = !TheOtherRolesPlugin.ShowLighterDarker.Value, TheOtherRolesPlugin.ShowLighterDarker.Value),
+ new SelectionBehaviour("Enable Sound Effects", () => MapOptions.enableSoundEffects = TheOtherRolesPlugin.EnableSoundEffects.Value = !TheOtherRolesPlugin.EnableSoundEffects.Value, TheOtherRolesPlugin.EnableSoundEffects.Value),
};
private static GameObject popUp;
Design by <color=#FCCE03FF>Bavari</color>";
public static string contributorsCredentials =
-$@"<size=60%> <color=#FCCE03FF>Special thanks to K3ndo & Smeggy</color>
-GitHub Contributors: Gendelo, Alex2911, amsyarasyiq, MaximeGillot, Psynomit, probablyadnf, JustASysAdmin</size>";
+$@"<size=60%> <color=#FCCE03FF>Special thanks to K3ndo & Smeggy</color></size>";
[HarmonyPatch(typeof(VersionShower), nameof(VersionShower.Start))]
private static class VersionShowerPatch
namespace TheOtherRoles.Patches {
public class GameStartManagerPatch {
public static Dictionary<int, PlayerVersion> playerVersions = new Dictionary<int, PlayerVersion>();
- private static float timer = 600f;
+ public static float timer = 600f;
private static float kickingTimer = 0f;
private static bool versionSent = false;
private static string lobbyCodeText = "";
private static string currentText = "";
public static void Prefix(GameStartManager __instance) {
- if (!AmongUsClient.Instance.AmHost || !GameData.Instance ) return; // Not host or no instance
+ if (!GameData.Instance ) return; // No instance
update = GameData.Instance.PlayerCount != __instance.LastPlayerCount;
}
}
// Lobby timer
- if (!AmongUsClient.Instance.AmHost || !GameData.Instance) return; // Not host or no instance
+ if (!GameData.Instance) return; // No instance
if (update) currentText = __instance.PlayerCounter.text;
BountyHunter.cooldownText.transform.localPosition = bottomLeft + new Vector3(0f, -1f, -1f);
BountyHunter.cooldownText.gameObject.SetActive(true);
}
- }
+ }
+
+ // Force Reload of SoundEffectHolder
+ SoundEffectsManager.Load();
// First kill
if (AmongUsClient.Instance.AmHost && MapOptions.shieldFirstKill && MapOptions.firstKillName != "") {
}
+ [HarmonyPatch(typeof(IntroCutscene), nameof(IntroCutscene.CreatePlayer))]
+ class CreatePlayerPatch {
+ public static void Postfix(IntroCutscene __instance, bool impostorPositioning, ref PoolablePlayer __result) {
+ if (impostorPositioning) __result.SetNameColor(Palette.ImpostorRed);
+ }
+ }
+
+
[HarmonyPatch(typeof(IntroCutscene), nameof(IntroCutscene.ShowRole))]
class SetUpRoleTextPatch {
static public void SetRoleTexts(IntroCutscene __instance) {
+++ /dev/null
-using HarmonyLib;
-using UnityEngine;
-
-
-namespace TheOtherRoles.Patches {
-
- [HarmonyPatch(typeof(LightSource), nameof(LightSource.Start))]
-
- class LightSourceStartPatch {
- static void Postfix(LightSource __instance) {
- __instance.transform.position += Vector3.down * 0.095f; // Fixes Polus Rock / Garbage / Boxes reducing vision to 0
- return;
- }
- }
-}
\ No newline at end of file
-using HarmonyLib;
+using System;
+using System.Collections.Generic;
+using HarmonyLib;
using UnityEngine;
using UnityEngine.UI;
using static UnityEngine.UI.Button;
private static bool horseButtonState = MapOptions.enableHorseMode;
private static Sprite horseModeOffSprite = null;
private static Sprite horseModeOnSprite = null;
+ private static GameObject bottomTemplate;
+ private static AnnouncementPopUp popUp;
private static void Prefix(MainMenuManager __instance) {
CustomHatLoader.LaunchHatFetcher();
// Horse mode stuff
var horseModeSelectionBehavior = new ClientOptionsPatch.SelectionBehaviour("Enable Horse Mode", () => MapOptions.enableHorseMode = TheOtherRolesPlugin.EnableHorseMode.Value = !TheOtherRolesPlugin.EnableHorseMode.Value, TheOtherRolesPlugin.EnableHorseMode.Value);
- var bottomTemplate = GameObject.Find("InventoryButton");
+ bottomTemplate = GameObject.Find("InventoryButton");
if (bottomTemplate == null) return;
var horseButton = Object.Instantiate(bottomTemplate, bottomTemplate.transform.parent);
var passiveHorseButton = horseButton.GetComponent<PassiveButton>();
if (horseButtonState) {
if (horseModeOnSprite == null) horseModeOnSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.HorseModeButtonOn.png", 75f);
spriteHorseButton.sprite = horseModeOnSprite;
- }
- else {
+ } else {
if (horseModeOffSprite == null) horseModeOffSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.HorseModeButtonOff.png", 75f);
spriteHorseButton.sprite = horseModeOffSprite;
}
CredentialsPatch.LogoPatch.updateSprite();
// Avoid wrong Player Particles floating around in the background
- var particles = GameObject.FindObjectOfType<PlayerParticles>();
+ var particles = GameObject.FindObjectOfType<PlayerParticles>();
if (particles != null) {
particles.pool.ReclaimAll();
particles.Start();
}
});
+
+ // TOR credits button
+ if (bottomTemplate == null) return;
+ var creditsButton = Object.Instantiate(bottomTemplate, bottomTemplate.transform.parent);
+ var passiveCreditsButton = creditsButton.GetComponent<PassiveButton>();
+ var spriteCreditsButton = creditsButton.GetComponent<SpriteRenderer>();
+
+ spriteCreditsButton.sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.CreditsButton.png", 75f);
+
+ passiveCreditsButton.OnClick = new ButtonClickedEvent();
+
+ passiveCreditsButton.OnClick.AddListener((System.Action)delegate {
+ // do stuff
+ if (popUp != null) Object.Destroy(popUp);
+ popUp = Object.Instantiate(Object.FindObjectOfType<AnnouncementPopUp>(true));
+ popUp.gameObject.SetActive(true);
+ popUp.Init();
+ //SelectableHyperLinkHelper.DestroyGOs(popUp.selectableHyperLinks, "test");
+ string creditsString = @$"<align=""center"">Github Contributors:
+Gendelo
+Alex2911 amsyarasyiq MaximeGillot
+Psynomit probablyadnf JustASysAdmin
+
+";
+ creditsString += $@"<size=60%> Other Credits & Resources:
+OxygenFilter - For all the version v2.3.0 to v2.6.1, we were using the OxygenFilter for automatic deobfuscation
+Reactor - The framework used for all version before v2.0.0
+BepInEx - Used to hook game functions
+Essentials - Custom game options by DorCoMaNdO:
+
+Before v1.6: We used the default Essentials release
+v1.6-v1.8: We slightly changed the default Essentials. The changes can be found on this branch of our fork.
+v2.0.0 and later: As we're not using Reactor anymore, we are using our own implementation, inspired by the one from DorCoMaNdO
+Jackal and Sidekick - Original idea for the Jackal and Sidekick comes from Dhalucard
+Among-Us-Love-Couple-Mod - Idea for the Lovers role comes from Woodi-dev
+Jester - Idea for the Jester role comes from Maartii
+ExtraRolesAmongUs - Idea for the Engineer and Medic role comes from NotHunter101. Also some code snippets come of the implementation were used.
+Among-Us-Sheriff-Mod - Idea for the Sheriff role comes from Woodi-dev
+TooManyRolesMods - Idea for the Detective and Time Master roles comes from Hardel-DW. Also some code snippets of the implementation were used.
+TownOfUs - Idea for the Swapper, Shifter, Arsonist and a similar Mayor role come from Slushiegoose
+Ottomated - Idea for the Morphling, Snitch and Camouflager role come from Ottomated
+Crowded-Mod - Our implementation for 10+ player lobbies is inspired by the one from the Crowded Mod Team
+Goose-Goose-Duck - Idea for the Vulture role come from Slushygoose</size>";
+ creditsString += "</align>";
+ popUp.AnnounceTextMeshPro.text = creditsString;
+ __instance.StartCoroutine(Effects.Lerp(0.01f, new Action<float>((p) => {
+ if (p == 1) {
+ var titleText = GameObject.Find("Title_Text").GetComponent<TMPro.TextMeshPro>();
+ if (titleText != null) titleText.text = "Credits and Contributors";
+ }
+ })));
+ });
+
+ }
+
+ public static void Postfix(MainMenuManager __instance) {
+ __instance.StartCoroutine(Effects.Lerp(0.01f, new Action<float>((p) => {
+ if (p == 1) {
+ bottomTemplate = GameObject.Find("InventoryButton");
+ foreach (Transform tf in bottomTemplate.transform.parent.GetComponentsInChildren<Transform>()) {
+ tf.localPosition = new Vector2(tf.localPosition.x * 0.8f, tf.localPosition.y);
+ }
+ }
+ })));
+
}
}
-using HarmonyLib;
+using HarmonyLib;
using TheOtherRoles.Players;
using TheOtherRoles.Utilities;
using UnityEngine;
namespace TheOtherRoles.Patches {
-
+
[HarmonyPatch(typeof(MapBehaviour))]
class MapBehaviourPatch {
if (!MapUtilities.CachedShipStatus) {
return false;
}
- Vector3 vector = AntiTeleport.position != null? AntiTeleport.position : CachedPlayer.LocalPlayer.transform.position;
+ Vector3 vector = AntiTeleport.position != null ? AntiTeleport.position : CachedPlayer.LocalPlayer.transform.position;
vector /= MapUtilities.CachedShipStatus.MapScale;
vector.x *= Mathf.Sign(MapUtilities.CachedShipStatus.transform.localScale.x);
vector.z = -1f;
__instance.HerePoint.transform.localPosition = vector;
CachedPlayer.LocalPlayer.PlayerControl.SetPlayerMaterialColors(__instance.HerePoint);
- return false;
- }
- [HarmonyPrefix]
- [HarmonyPatch(typeof(MapBehaviour), nameof(MapBehaviour.ShowNormalMap))]
- static bool Prefix3(MapBehaviour __instance) {
- if (!MeetingHud.Instance || __instance.IsOpen) return true; // Only run in meetings and when the map is closed
-
- CachedPlayer.LocalPlayer.PlayerControl.SetPlayerMaterialColors(__instance.HerePoint);
- __instance.GenericShow();
- __instance.taskOverlay.Show();
- __instance.ColorControl.SetColor(new Color(0.05f, 0.2f, 1f, 1f));
- FastDestroyableSingleton<HudManager>.Instance.SetHudActive(false);
return false;
}
}
-}
\ No newline at end of file
+}
}
}
- private static GameObject guesserUI;
+ public static GameObject guesserUI;
+ public static PassiveButton guesserUIExitButton;
static void guesserOnClick(int buttonTarget, MeetingHud __instance) {
if (guesserUI != null || !(__instance.state == MeetingHud.VoteStates.Voted || __instance.state == MeetingHud.VoteStates.NotVoted)) return;
__instance.playerStates.ToList().ForEach(x => x.gameObject.SetActive(false));
exitButton.gameObject.GetComponent<SpriteRenderer>().sprite = smallButtonTemplate.GetComponent<SpriteRenderer>().sprite;
exitButtonParent.transform.localPosition = new Vector3(2.725f, 2.1f, -5);
exitButtonParent.transform.localScale = new Vector3(0.217f, 0.9f, 1);
- exitButton.GetComponent<PassiveButton>().OnClick.RemoveAllListeners();
- exitButton.GetComponent<PassiveButton>().OnClick.AddListener((System.Action)(() => {
- __instance.playerStates.ToList().ForEach(x => x.gameObject.SetActive(true));
+ guesserUIExitButton = exitButton.GetComponent<PassiveButton>();
+ guesserUIExitButton.OnClick.RemoveAllListeners();
+ guesserUIExitButton.OnClick.AddListener((System.Action)(() => {
+ __instance.playerStates.ToList().ForEach(x => {
+ x.gameObject.SetActive(true);
+ if (CachedPlayer.LocalPlayer.Data.IsDead && x.transform.FindChild("ShootButton") != null) UnityEngine.Object.Destroy(x.transform.FindChild("ShootButton").gameObject);
+ });
UnityEngine.Object.Destroy(container.gameObject);
}));
else if (new List<RoleId>() { RoleId.Janitor, RoleId.Godfather, RoleId.Mafioso }.Contains(roleInfo.roleId) && CustomOptionHolder.mafiaSpawnRate.getSelection() == 0) continue;
else if (roleInfo.roleId == RoleId.Sidekick && (!CustomOptionHolder.jackalCanCreateSidekick.getBool() || CustomOptionHolder.jackalSpawnRate.getSelection() == 0)) continue;
if (roleInfo.roleId == RoleId.Deputy && (CustomOptionHolder.deputySpawnRate.getSelection() == 0 || CustomOptionHolder.sheriffSpawnRate.getSelection() == 0)) continue;
+ if (roleInfo.roleId == RoleId.Pursuer && CustomOptionHolder.lawyerSpawnRate.getSelection() == 0) continue;
if (roleInfo.roleId == RoleId.Spy && roleData.impostors.Count <= 1) continue;
if (Guesser.guesserCantGuessSnitch && Snitch.snitch != null) {
MessageWriter murderAttemptWriter = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.ShieldedMurderAttempt, Hazel.SendOption.Reliable, -1);
AmongUsClient.Instance.FinishRpcImmediately(murderAttemptWriter);
RPCProcedure.shieldedMurderAttempt();
+ SoundEffectsManager.play("fail");
return;
}
// Reset zoomed out ghosts
Helpers.toggleZoom(reset: true);
+
+ // Stop all playing sounds
+ SoundEffectsManager.stopAll();
}
}
if (target.hasFakeTasks())
target.clearAllTasks();
+ // First kill (set before lover suicide)
+ if (MapOptions.firstKillName == "") MapOptions.firstKillName = target.Data.PlayerName;
+
// Lover suicide trigger on murder
if ((Lovers.lover1 != null && target == Lovers.lover1) || (Lovers.lover2 != null && target == Lovers.lover2)) {
PlayerControl otherLover = target == Lovers.lover1 ? Lovers.lover2 : Lovers.lover1;
}
Helpers.showFlash(color, 1.5f);
}
-
- // First kill
- if (MapOptions.firstKillName == "") MapOptions.firstKillName = target.Data.PlayerName;
}
}
public static void Postfix(RegionMenu __instance) {
if (!__instance.TryCast<RegionMenu>()) return;
+ bool isCustomRegion = FastDestroyableSingleton<ServerManager>.Instance.CurrentRegion.Name == "Custom";
+ if (!isCustomRegion)
+ {
+ if (ipField != null && ipField.gameObject != null) {
+ ipField.gameObject.SetActive(false);
+
+ }
+ if (portField != null && portField.gameObject != null) {
+ portField.gameObject.SetActive(false);
+ }
+ } else
+ {
+ if (ipField != null && ipField.gameObject != null)
+ {
+ ipField.gameObject.SetActive(true);
+
+ }
+ if (portField != null && portField.gameObject != null)
+ {
+ portField.gameObject.SetActive(true);
+ }
+ }
var template = FastDestroyableSingleton<JoinGameButton>.Instance;
var joinGameButtons = GameObject.FindObjectsOfType<JoinGameButton>();
foreach (var t in joinGameButtons) { // The correct button has a background, the other 2 dont
ipField.OnFocusLost = new Button.ButtonClickedEvent();
ipField.OnChange.AddListener((UnityAction)onEnterOrIpChange);
ipField.OnFocusLost.AddListener((UnityAction)onFocusLost);
+ ipField.gameObject.SetActive(isCustomRegion);
void onEnterOrIpChange() {
TheOtherRolesPlugin.Ip.Value = ipField.text;
portField.OnFocusLost = new Button.ButtonClickedEvent();
portField.OnChange.AddListener((UnityAction)onEnterOrPortFieldChange);
portField.OnFocusLost.AddListener((UnityAction)onFocusLost);
+ portField.gameObject.SetActive(isCustomRegion);
void onEnterOrPortFieldChange() {
ushort port = 0;
private static int crewValues;
private static int impValues;
private static bool isEvilGuesser;
+ private static List<Tuple<byte, byte>> playerRoleMap = new List<Tuple<byte, byte>>();
public static void Postfix() {
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.ResetVaribles, Hazel.SendOption.Reliable, -1);
AmongUsClient.Instance.FinishRpcImmediately(writer);
assignChanceRoles(data); // Assign roles that may or may not be in the game last
assignRoleTargets(data);
assignModifiers();
+ setRolesAgain();
}
public static RoleAssignmentData getRoleAssignmentData() {
byte playerId = playerList[index].PlayerId;
if (removePlayer) playerList.RemoveAt(index);
+ playerRoleMap.Add(new Tuple<byte, byte>(playerId, roleId));
+
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.SetRole, Hazel.SendOption.Reliable, -1);
writer.Write(roleId);
writer.Write(playerId);
return selection;
}
+ private static void setRolesAgain()
+ {
+
+ while (playerRoleMap.Any())
+ {
+ byte amount = (byte)Math.Min(playerRoleMap.Count, 20);
+ var writer = AmongUsClient.Instance!.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.WorkaroundSetRoles, SendOption.Reliable, -1);
+ writer.Write(amount);
+ for (int i = 0; i < amount; i++)
+ {
+ var option = playerRoleMap[0];
+ playerRoleMap.RemoveAt(0);
+ writer.WritePacked((uint)option.Item1);
+ writer.WritePacked((uint)option.Item2);
+ }
+ AmongUsClient.Instance.FinishRpcImmediately(writer);
+ }
+ }
+
public class RoleAssignmentData {
public List<PlayerControl> crewmates {get;set;}
writer.Write(isEnter ? byte.MaxValue : (byte)0);
writer.EndMessage();
RPCProcedure.useUncheckedVent(__instance.Id, CachedPlayer.LocalPlayer.PlayerId, isEnter ? byte.MaxValue : (byte)0);
+ SoundEffectsManager.play("tricksterUseBoxVent");
return false;
}
ResetVaribles = 60,
ShareOptions,
ForceEnd,
+ WorkaroundSetRoles,
SetRole,
SetModifier,
VersionHandshake,
clearAndReloadRoles();
clearGameHistory();
setCustomButtonCooldowns();
+ Helpers.toggleZoom(reset : true);
}
public static void HandleShareOptions(byte numberOfOptions, MessageReader reader) {
}
}
+ public static void workaroundSetRoles(byte numberOfRoles, MessageReader reader)
+ {
+ for (int i = 0; i < numberOfRoles; i++)
+ {
+ byte playerId = (byte) reader.ReadPackedUInt32();
+ byte roleId = (byte) reader.ReadPackedUInt32();
+ try {
+ setRole(roleId, playerId);
+ } catch (Exception e) {
+ TheOtherRolesPlugin.Logger.LogError("Error while deserializing roles: " + e.Message);
+ }
+ }
+
+ }
+
public static void setRole(byte roleId, byte playerId) {
foreach (PlayerControl player in CachedPlayer.AllPlayers)
if (player.PlayerId == playerId) {
public static void timeMasterRewindTime() {
TimeMaster.shieldActive = false; // Shield is no longer active when rewinding
+ SoundEffectsManager.stop("timemasterShield"); // Shield sound stopped when rewinding
if(TimeMaster.timeMaster != null && TimeMaster.timeMaster == CachedPlayer.LocalPlayer.PlayerControl) {
resetTimeMasterButton();
}
if (wasSpy || wasImpostor) Sidekick.wasTeamRed = true;
Sidekick.wasSpy = wasSpy;
Sidekick.wasImpostor = wasImpostor;
+ if (player == CachedPlayer.LocalPlayer.PlayerControl) SoundEffectsManager.play("jackalSidekick");
}
Jackal.canCreateSidekick = false;
}
if (flag == byte.MaxValue)
{
target.cosmetics.currentBodySprite.BodySprite.color = Color.white;
+ target.cosmetics.colorBlindText.gameObject.SetActive(SaveManager.colorblindMode);
if (Camouflager.camouflageTimer <= 0) target.setDefaultLook();
Ninja.isInvisble = false;
return;
Color color = Color.clear;
if (CachedPlayer.LocalPlayer.Data.Role.IsImpostor || CachedPlayer.LocalPlayer.Data.IsDead) color.a = 0.1f;
target.cosmetics.currentBodySprite.BodySprite.color = color;
+ target.cosmetics.colorBlindText.gameObject.SetActive(false);
Ninja.invisibleTimer = Ninja.invisibleDuration;
Ninja.isInvisble = true;
}
}
PlayerControl guesser = Helpers.playerById(killerId);
if (FastDestroyableSingleton<HudManager>.Instance != null && guesser != null)
- if (CachedPlayer.LocalPlayer.PlayerControl == dyingTarget)
+ if (CachedPlayer.LocalPlayer.PlayerControl == dyingTarget) {
FastDestroyableSingleton<HudManager>.Instance.KillOverlay.ShowKillAnimation(guesser.Data, dyingTarget.Data);
- else if (dyingLoverPartner != null && CachedPlayer.LocalPlayer.PlayerControl == dyingLoverPartner)
+ if (MeetingHudPatch.guesserUI != null) MeetingHudPatch.guesserUIExitButton.OnClick.Invoke();
+ } else if (dyingLoverPartner != null && CachedPlayer.LocalPlayer.PlayerControl == dyingLoverPartner) {
FastDestroyableSingleton<HudManager>.Instance.KillOverlay.ShowKillAnimation(dyingLoverPartner.Data, dyingLoverPartner.Data);
+ if (MeetingHudPatch.guesserUI != null) MeetingHudPatch.guesserUIExitButton.OnClick.Invoke();
+ }
PlayerControl guessedTarget = Helpers.playerById(guessedTargetId);
if (Guesser.showInfoInGhostChat && CachedPlayer.LocalPlayer.Data.IsDead && guessedTarget != null) {
case (byte)CustomRPC.ForceEnd:
RPCProcedure.forceEnd();
break;
+ case (byte)CustomRPC.WorkaroundSetRoles:
+ RPCProcedure.workaroundSetRoles(reader.ReadByte(), reader);
+ break;
case (byte)CustomRPC.SetRole:
byte roleId = reader.ReadByte();
byte playerId = reader.ReadByte();
byte major = reader.ReadByte();
byte minor = reader.ReadByte();
byte patch = reader.ReadByte();
+ float timer = reader.ReadSingle();
+ if (!AmongUsClient.Instance.AmHost && timer >= 0f) GameStartManagerPatch.timer = timer;
int versionOwnerId = reader.ReadPackedInt32();
byte revision = 0xFF;
Guid guid;
--- /dev/null
+#### Custom Button Sound credits, sources and licenses:
+
+| Name | Comment | Url | License |
+|---|---|---|---|
+|engineerRepair| | [Repair_metal.wav](https://freesound.org/people/zbig77/sounds/244985/ ) | CC0 1.0|
+|janitorClean + cleanerClean | combined, shortened + fade out | [Vacuum cleaner.wav](https://freesound.org/people/Accorman/sounds/573797/ ) + [Windowcleaner Spray](https://freesound.org/people/Rudmer_Rotteveel/sounds/536409/ )| CC0 1.0 both|
+|sheriffKill | (already has a sound) |-|-|
+|deputyHandcuff | | [Ratchet close long.wav](https://freesound.org/people/jameswrowles/sounds/437230/ ) | CC0 1.0|
+|timeMasterShield | | [Mechanical alarm clock is ticking (SLAVA).wav](https://freesound.org/people/ST303/sounds/171043/ ) | CC0 1.0|
+|medicShield | | [energy](https://freesound.org/people/Leszek_Szary/sounds/146727/ )| CC0 1.0 |
+|shifterShift | recorded by gendelo3 | - | GPLv3 |
+|morphlingSample |Combined and cut |[Scanner 03.wav](https://freesound.org/people/Glitchedtones/sounds/375942/ ) + [ scanner epson V600](https://freesound.org/people/gecop/sounds/521298/ )| CC0 1.0 both|
+|morphlingMorph | | [Whoosh](https://freesound.org/people/qubodup/sounds/60013/) | CC0 1.0 |
+|camouflagerCamo | same as morphlingMorph | - | - |
+|hackerHack | recorded by gendelo3 | - | GPLv3 |
+|hackerVitals | (already has a sound) |-|-|
+|hackerAdmin | (already has a sound) |-|-|
+|trackerTrackPlayer | | [CASHIER-REGISTER-KEYBOARD-BEEP-CALC.wav](https://freesound.org/people/newagesoup/sounds/348240/ ) | CC0 1.0 |
+|trackerTrackCorpses |sniffing sound|[Male_Breath_Sniff_Stereo_NT4.wav](https://freesound.org/people/Nox_Sound/sounds/554905/ ) | CC0 1.0 |
+|vampireKill |combined |[Gore Rend](https://freesound.org/people/magnuswaker/sounds/563491/ ) + [R15-53-Blood Sucking.wav](https://freesound.org/people/craigsmith/sounds/487070/ ) | CC0 1.0 both|
+|garlic | | [Garlic cloves](https://freesound.org/people/Aemyn/sounds/512453/ )| CC0 1.0 |
+|jackalKill | (already has a sound) |-|-|
+|jackalSidekick | | [Snare - Nice.wav]( https://freesound.org/people/molodiaz/sounds/593550/ )| CC0 1.0 |
+|lighterLight | |[Torch Click 2](https://freesound.org/people/BenjaminNelan/sounds/410360/ ) | CC0 1.0 |
+|eraserErase | | [Pencil Eraser Erasing](https://freesound.org/people/deleted_user_7146007/sounds/383861/ ) | CC0 1.0 |
+|tricksterPlaceBox | |[Carton_impact_5.wav](https://freesound.org/people/andresix/sounds/245617/ ) | CC0 1.0 |
+|tricksterLight | same as lighterLight |-|-|
+|tricksterUseBoxVent | |[rubber duck -CsG-.wav](https://freesound.org/people/csaszi/sounds/350917/) | CC0 1.0 |
+|warlockCurse | | [Buffer Spell](https://freesound.org/people/deleted_user_3277771/sounds/176741/ ) | CC0 1.0 |
+|warlockKill | (already has a sound) |-|-|
+|securityGuardPlaceCam | combined | [drilling a screw into wood.wav](https://freesound.org/people/chewiesmissus/sounds/244242/ ) + [hammer v3.wav](https://freesound.org/people/blukotek/sounds/337814/ ) | CC0 1.0 both|
+|securityGuardSealVent | same as securityGuardPlaceCam |-|-|
+|securityGuardCam | (already has a sound) |-|-|
+|arsonistDouse | | [Water Pouring Out of Bucket](https://freesound.org/people/Breviceps/sounds/508178/ )| CC0 1.0 |
+|vultureEat | combined | [Vulture](https://freesound.org/people/AntumDeluge/sounds/188041/ ) + [Eating Crisps](https://freesound.org/people/Sethroph/sounds/334209/ )| CC0 1.0 both| |
+|mediumAsk| |[ghost_sounds.wav](https://freesound.org/people/fishwithfeathers/sounds/177768/ )| CC0 1.0|
+|pursuerBlank | | [gun load.wav](https://freesound.org/people/Shelby_Wideman/sounds/178141/ )| CC0 1.0 |
+|witchSpell | | [witch's laugh](https://freesound.org/people/Yogoorth/sounds/589144/ ) | CC0 1.0 |
+|portalmakerPlacePortal | same as tricksterPlaceBox| - | - |
+|portalUse | | [Static electronic noise - Xbox 360](https://freesound.org/people/scenes/sounds/431654/ ) + [Teleport Slurp](https://freesound.org/people/GameAudio/sounds/220163/ ) | CC0 1.0 both |
+|fail | | [twitch: SmeggyTV: Mein Auge](https://www.twitch.tv/smeggytv/clip/KindQuaintEndiveDancingBanana-Zoc-bXUnqNCoqQD5 ) | License granted for use in TOR |
\ No newline at end of file
// Modifier
if (showModifier) {
+ // after dead modifier
+ if (!CustomOptionHolder.modifiersAreHidden.getBool() || PlayerControl.LocalPlayer.Data.IsDead)
+ {
+ if (Bait.bait.Any(x => x.PlayerId == p.PlayerId)) infos.Add(bait);
+ if (Bloody.bloody.Any(x => x.PlayerId == p.PlayerId)) infos.Add(bloody);
+ if (Vip.vip.Any(x => x.PlayerId == p.PlayerId)) infos.Add(vip);
+ }
if (p == Lovers.lover1 || p == Lovers.lover2) infos.Add(lover);
if (p == Tiebreaker.tiebreaker) infos.Add(tiebreaker);
- if (Bait.bait.Any(x => x.PlayerId == p.PlayerId)) infos.Add(bait);
- if (Bloody.bloody.Any(x => x.PlayerId == p.PlayerId)) infos.Add(bloody);
if (AntiTeleport.antiTeleport.Any(x => x.PlayerId == p.PlayerId)) infos.Add(antiTeleport);
if (Sunglasses.sunglasses.Any(x => x.PlayerId == p.PlayerId)) infos.Add(sunglasses);
if (p == Mini.mini) infos.Add(mini);
- if (Vip.vip.Any(x => x.PlayerId == p.PlayerId)) infos.Add(vip);
if (Invert.invert.Any(x => x.PlayerId == p.PlayerId)) infos.Add(invert);
}
--- /dev/null
+using System;
+using System.IO;
+using System.Reflection;
+using System.Collections.Generic;
+using System.Text;
+using UnityEngine;
+using System.Linq;
+
+namespace TheOtherRoles
+{
+ // Class to preload all audio/sound effects that are contained in the embedded resources.
+ // The effects are made available through the soundEffects Dict / the get and the play methods.
+ public static class SoundEffectsManager
+
+ {
+ private static Dictionary<string, AudioClip> soundEffects;
+
+ public static void Load()
+ {
+ soundEffects = new Dictionary<string, AudioClip>();
+ Assembly assembly = Assembly.GetExecutingAssembly();
+ string[] resourceNames = assembly.GetManifestResourceNames();
+ foreach (string resourceName in resourceNames)
+ {
+ if (resourceName.Contains("TheOtherRoles.Resources.SoundEffects.") && resourceName.Contains(".raw"))
+ {
+ soundEffects.Add(resourceName, Helpers.loadAudioClipFromResources(resourceName));
+ }
+ }
+ }
+
+ public static AudioClip get(string path)
+ {
+ // Convenience: As as SoundEffects are stored in the same folder, allow using just the name as well
+ if (!path.Contains(".")) path = "TheOtherRoles.Resources.SoundEffects." + path + ".raw";
+ AudioClip returnValue;
+ return soundEffects.TryGetValue(path, out returnValue) ? returnValue : null;
+ }
+
+
+ public static void play(string path, float volume=0.8f)
+ {
+ if (!MapOptions.enableSoundEffects) return;
+ AudioClip clipToPlay = get(path);
+ // if (false) clipToPlay = get("exampleClip"); for april fools?
+ stop(path);
+ if (Constants.ShouldPlaySfx()) SoundManager.Instance.PlaySound(clipToPlay, false, volume);
+ }
+
+ public static void stop(string path) {
+ if (Constants.ShouldPlaySfx()) SoundManager.Instance.StopSound(get(path));
+ }
+
+ public static void stopAll() {
+ foreach (var path in soundEffects.Keys) stop(path);
+ }
+ }
+}
byte localPlayerId = CachedPlayer.LocalPlayer.PlayerId;
handcuffedKnows.Add(localPlayerId, handcuffDuration);
handcuffedPlayers.RemoveAll(x => x == localPlayerId);
+ SoundEffectsManager.play("deputyHandcuff");
}
HudManagerStartPatch.setAllButtonsHandcuffedStatus(active);
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
- <Version>4.1.4</Version>
+ <Version>4.1.6</Version>
<Description>TheOtherRoles</Description>
<Authors>Eisbison</Authors>
<LangVersion>latest</LangVersion>