Skip to content

Commit

Permalink
Add Free Play Gamemode
Browse files Browse the repository at this point in the history
  • Loading branch information
dabao40 committed Oct 3, 2024
1 parent 60aaa92 commit 8665d95
Show file tree
Hide file tree
Showing 26 changed files with 622 additions and 145 deletions.
Binary file modified Strings.xlsx
Binary file not shown.
246 changes: 173 additions & 73 deletions TheOtherRoles/Buttons.cs

Large diffs are not rendered by default.

240 changes: 240 additions & 0 deletions TheOtherRoles/CustomGameModes/FreePlayGM.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AmongUs.GameOptions;
using HarmonyLib;
using MonoMod.Cil;
using TheOtherRoles.MetaContext;
using TheOtherRoles.Objects;
using TheOtherRoles.Patches;
using TheOtherRoles.Players;
using UnityEngine;

namespace TheOtherRoles.CustomGameModes
{
public static class FreePlayGM
{
public static bool isFreePlayGM = false;
public static Sprite operateButtonSprite;
public static Sprite reviveSprite;

public static Sprite getOperateButtonSprite()
{
if (operateButtonSprite) return operateButtonSprite;
operateButtonSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.OperateButton.png", 115f);
return operateButtonSprite;
}

public static Sprite getReviveButtonSprite()
{
if (reviveSprite) return reviveSprite;
reviveSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.ReviveButton.png", 115f);
return reviveSprite;
}

public static void OpenRoleWindow()
{
var window = MetaScreen.GenerateWindow(new Vector2(7.5f, 4.5f), HudManager.Instance.transform, new Vector3(0, 0, -400f), true, false);

var gui = TORGUIContextEngine.Instance;
var roleMaskedTittleAttr = gui.GetAttribute(AttributeAsset.MetaRoleButton);
var roleTittleAttr = new TextAttributes(roleMaskedTittleAttr) { Font = gui.GetFont(FontAsset.Gothic) };

void SetWidget(int tab)
{
List<GUIContext> guis = new()
{ gui.LocalizedButton(GUIAlignment.Center, roleTittleAttr, "freePlayRoles", () => SetWidget(0), color: tab == 0 ? Color.yellow : null),
gui.LocalizedButton(GUIAlignment.Center, roleTittleAttr, "freePlayModifiers", () => SetWidget(1), color: tab == 1 ? Color.yellow : null)};
var holder = gui.HorizontalHolder(GUIAlignment.Center,
guis
);

GUIContext inner = GUIEmptyWidget.Default;

if (tab == 0)
{
inner = gui.Arrange(GUIAlignment.Center, RoleInfo.allRoleInfos.Where(x => x != RoleInfo.bomberB && x != RoleInfo.bomberA && x != RoleInfo.jackal
&& x != RoleInfo.sidekick && x != RoleInfo.mimicA && x != RoleInfo.mimicK && x != RoleInfo.arsonist && x != RoleInfo.bountyHunter && !x.isModifier).Select(r => gui.RawButton(GUIAlignment.Center, roleMaskedTittleAttr, Helpers.cs(r.color, r.name), () =>
{
var formerRole = RoleInfo.getRoleInfoForPlayer(PlayerControl.LocalPlayer, false).FirstOrDefault();
if (formerRole == r) return; // Do nothing if the same role was given
RPCProcedure.erasePlayerRoles(PlayerControl.LocalPlayer.PlayerId);
if (r.isImpostor() && !formerRole.isImpostor()) PlayerControl.LocalPlayer.FastSetRole(RoleTypes.Impostor);
else if (!r.isImpostor() && formerRole.isImpostor()) PlayerControl.LocalPlayer.FastSetRole(RoleTypes.Crewmate);

if (r == RoleInfo.chainshifter) Shifter.isNeutral = true;
else if (r == RoleInfo.niceshifter) Shifter.isNeutral = false;
RPCProcedure.setRole((byte)r.roleId, PlayerControl.LocalPlayer.PlayerId);

if (r.roleId == RoleId.Fox) {
if (Shrine.allShrine?.FirstOrDefault() == null){
Shrine.activateShrines(GameOptionsManager.Instance.currentNormalGameOptions.MapId);System.Console.WriteLine("1");
}
List<Byte> taskIdList = new();
Shrine.allShrine.ForEach(shrine => taskIdList.Add((byte)shrine.console.ConsoleId));
taskIdList.Shuffle();
var cpt = new CustomNormalPlayerTask("foxTaskStay", Il2CppType.Of<FoxTask>(), Fox.numTasks, taskIdList.ToArray(), Shrine.allShrine.Find(x => x.console.ConsoleId == taskIdList.ToArray()[0]).console.Room, true);
cpt.addTaskToPlayer(CachedPlayer.LocalPlayer.PlayerId);
} else if (r.roleId == RoleId.JekyllAndHyde) {
CachedPlayer.LocalPlayer.PlayerControl.generateAndAssignTasks(JekyllAndHyde.numCommonTasks, JekyllAndHyde.numShortTasks, JekyllAndHyde.numLongTasks);
} else if (formerRole.roleId == RoleId.Fox || formerRole.roleId == RoleId.JekyllAndHyde || formerRole.roleId == RoleId.TaskMaster) {
var options = GameOptionsManager.Instance.currentNormalGameOptions;
PlayerControl.LocalPlayer.generateAndAssignTasks(options.NumCommonTasks, options.NumShortTasks, options.NumLongTasks);
}
RPCProcedure.resetAchievement();
window.CloseScreen();
})), 4);
}
else if (tab == 1)
{
inner = gui.VerticalHolder(GUIAlignment.Center,
new List<GUIContext>() { gui.LocalizedText(GUIAlignment.Center, roleMaskedTittleAttr, "freePlayModifiersEquipped"),
gui.Arrange(GUIAlignment.Center, RoleInfo.allRoleInfos.Where(r => r.isModifier && RoleInfo.getRoleInfoForPlayer(PlayerControl.LocalPlayer).Contains(r)).Select(r => gui.RawButton(GUIAlignment.Center, roleMaskedTittleAttr, Helpers.cs(r.color, r.name), () =>
{
removeModifier(r.roleId);
SetWidget(1);
})), 4),
gui.LocalizedText(GUIAlignment.Center, roleMaskedTittleAttr, "freePlayModifiersUnequipped"),
gui.Arrange(GUIAlignment.Center, RoleInfo.allRoleInfos.Where(r => r.isModifier && r != RoleInfo.cupidLover && r != RoleInfo.lover && r != RoleInfo.mini && !RoleInfo.getRoleInfoForPlayer(PlayerControl.LocalPlayer).Contains(r)).Select(r => gui.RawButton(GUIAlignment.Center, roleMaskedTittleAttr, Helpers.cs(r.color, r.name), () =>
{
RPCProcedure.setModifier((byte)r.roleId, PlayerControl.LocalPlayer.PlayerId, 0);
SetWidget(1);
})), 4)}
);
}
window.SetContext(gui.VerticalHolder(GUIAlignment.Center, new List<GUIContext>() { holder, gui.ScrollView(GUIAlignment.Center, new(7.4f, 3.5f), null, inner, out _) }), out _);
}

SetWidget(0);
}

private static void FastSetRole(this PlayerControl targetPlayer, RoleTypes roleType)
{
NetworkedPlayerInfo data = targetPlayer.Data;
RoleBehaviour roleBehaviour = UnityEngine.Object.Instantiate(RoleManager.Instance.AllRoles.First(r => r.Role == roleType), data.gameObject.transform);
roleBehaviour.Initialize(targetPlayer);
targetPlayer.Data.Role = roleBehaviour;
targetPlayer.Data.RoleType = roleType;
if (roleType != RoleTypes.ImpostorGhost && roleType != RoleTypes.CrewmateGhost)
targetPlayer.Data.RoleWhenAlive = new Il2CppSystem.Nullable<RoleTypes>(roleType);
roleBehaviour.AdjustTasks(targetPlayer);
}

private static bool isImpostor(this RoleInfo roleInfo) => roleInfo.color == Palette.ImpostorRed && roleInfo.roleId != RoleId.Spy;

public static void removeModifier(RoleId modifierId)
{
var player = PlayerControl.LocalPlayer;
var playerId = player.PlayerId;
switch (modifierId)
{
case RoleId.AntiTeleport:
AntiTeleport.antiTeleport.RemoveAll(x => x.PlayerId == playerId);
break;
case RoleId.Chameleon:
Chameleon.chameleon.RemoveAll(x => x.PlayerId == playerId);
break;
case RoleId.Mini:
Mini.mini = null;
break;
case RoleId.Invert:
Invert.invert.RemoveAll(x => x.PlayerId == playerId);
break;
case RoleId.Bloody:
Bloody.bloody.RemoveAll(x => x.PlayerId == playerId);
break;
case RoleId.Sunglasses:
Sunglasses.sunglasses.RemoveAll(x => x.PlayerId == playerId);
break;
case RoleId.Tiebreaker:
Tiebreaker.tiebreaker = null;
break;
case RoleId.Vip:
Vip.vip.RemoveAll(x => x.PlayerId == playerId);
break;
}
}

public static PlayerControl SpawnDummy()
{
var playerControl = UnityEngine.Object.Instantiate(AmongUsClient.Instance.PlayerPrefab);
var i = playerControl.PlayerId = (byte)GameData.Instance.GetAvailableId();

playerControl.isDummy = true;

var playerInfo = GameData.Instance.AddDummy(playerControl);

playerControl.transform.position = PlayerControl.LocalPlayer.transform.position;
playerControl.GetComponent<DummyBehaviour>().enabled = true;
playerControl.isDummy = true;
playerControl.SetName(AccountManager.Instance.GetRandomName());
playerControl.SetColor(i);
playerControl.SetHat(CosmeticsLayer.EMPTY_HAT_ID, i);
playerControl.SetVisor(CosmeticsLayer.EMPTY_VISOR_ID, i);
playerControl.SetSkin(CosmeticsLayer.EMPTY_SKIN_ID, i);
playerControl.SetPet(CosmeticsLayer.EMPTY_PET_ID, i);

AmongUsClient.Instance.Spawn(playerControl, -2, InnerNet.SpawnFlags.None);
playerInfo.RpcSetTasks(new byte[0]);

return playerControl;
}

public static void clearAndReload()
{
isFreePlayGM = TORMapOptions.gameMode == CustomGamemodes.FreePlay;
}

[HarmonyPatch(typeof(GameStartManager), nameof(GameStartManager.BeginGame))]
public class GameStartManagerBeginGame
{
public static bool Prefix(GameStartManager __instance)
{
if (TORMapOptions.gameMode == CustomGamemodes.FreePlay)
{
if (AmongUsClient.Instance.AmHost && PlayerControl.AllPlayerControls.Count == 1)
{
var numDummies = CustomOptionHolder.freePlayGameModeNumDummies != null ? (int)CustomOptionHolder.freePlayGameModeNumDummies.getFloat() : 0;
for (int i = 0; i < numDummies; i++) SpawnDummy();
}
}
return true;
}
}

[HarmonyPatch(typeof(GameManager), nameof(GameManager.CheckTaskCompletion))]
static class CheckTaskCompletionPatch
{
static bool Prefix(GameManager __instance, ref bool __result)
{
if (!isFreePlayGM) return true;
__result = false;
return false;
}
}

[HarmonyPatch(typeof(GameManager), nameof(GameManager.CheckEndGameViaTasks))]
static class CheckEndGameViaTasksPatch
{
static bool Prefix(GameManager __instance, ref bool __result)
{
if (!isFreePlayGM) return true;
__result = false;
return false;
}
}

[HarmonyPatch(typeof(LogicGameFlowNormal), nameof(LogicGameFlowNormal.IsGameOverDueToDeath))]
public static class BlockGameOverPatch
{
public static bool Prefix(LogicGameFlowNormal __instance, ref bool __result)
{
if (!isFreePlayGM) return true;
__result = false;
return false;
}
}
}
}
6 changes: 3 additions & 3 deletions TheOtherRoles/CustomGameModes/GameModePatches.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
Expand Down Expand Up @@ -46,7 +46,7 @@ public static void Postfix(LobbyInfoPane __instance)
gameModeButton.transform.GetChild(2).GetComponent<SpriteRenderer>().color = new Color(0f, 0f, 0f);
pButton.OnClick.AddListener((Action)(() =>
{
TORMapOptions.gameMode = (CustomGamemodes)((int)(TORMapOptions.gameMode + 1) % Enum.GetNames(typeof(CustomGamemodes)).Length);
TORMapOptions.gameMode = (CustomGamemodes)((int)(TORMapOptions.gameMode + 1) % (Enum.GetNames(typeof(CustomGamemodes)).Length - 1));
__instance.StartCoroutine(Effects.Lerp(0.1f, new Action<float>(p => { pButton.buttonText.text = Helpers.cs(Color.yellow, GameModeText.GetComponent<TextMeshPro>().text); })));
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.ShareGamemode, Hazel.SendOption.Reliable, -1);
writer.Write((byte)TORMapOptions.gameMode);
Expand All @@ -67,4 +67,4 @@ public static void Postfix(LobbyInfoPane __instance)
}
}
}
}
}
4 changes: 3 additions & 1 deletion TheOtherRoles/CustomOptionHolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class CustomOptionHolder {

public static CustomOption enableEventMode;
public static CustomOption anyPlayerCanStopStart;
public static CustomOption freePlayGameModeNumDummies;

public static CustomOption mafiaSpawnRate;
public static CustomOption janitorCooldown;
Expand Down Expand Up @@ -1175,7 +1176,8 @@ public static void Load() {
huntedShieldNumber = CustomOption.Create(3026, Types.HideNSeekRoles, cs(Color.gray, "huntedShieldNumber"), 3f, 1f, 15f, 1f, format: "unitScrews");

// Other options
maxNumberOfMeetings = CustomOption.Create(3, Types.General, "maxNumberOfMeetings", 10, 0, 15, 1, null, true, "unitShots", heading: "headingGameplay");
maxNumberOfMeetings = CustomOption.Create(3, Types.General, "maxNumberOfMeetings", 10, 0, 15, 1, null, true, "unitShots", heading: "headingGameplay");
freePlayGameModeNumDummies = CustomOption.Create(10424, Types.General, cs(Color.green, "freePlayGameModeNumDummies"), 1f, 1f, 23f, 1f, format: "unitPlayers");
anyPlayerCanStopStart = CustomOption.Create(2, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "anyPlayerCanStopStart"), false, null, false);
blockSkippingInEmergencyMeetings = CustomOption.Create(4, Types.General, "blockSkippingInEmergencyMeetings", false);
noVoteIsSelfVote = CustomOption.Create(5, Types.General, "noVoteIsSelfVote", false, blockSkippingInEmergencyMeetings);
Expand Down
3 changes: 2 additions & 1 deletion TheOtherRoles/Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public enum MurderAttemptResult {
public enum CustomGamemodes {
Classic,
Guesser,
HideNSeek
HideNSeek,
FreePlay
}

public static class Direction
Expand Down
2 changes: 1 addition & 1 deletion TheOtherRoles/Main.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
global using Il2CppInterop.Runtime;
global using Il2CppInterop.Runtime;
global using Il2CppInterop.Runtime.Attributes;
global using Il2CppInterop.Runtime.InteropTypes;
global using Il2CppInterop.Runtime.InteropTypes.Arrays;
Expand Down
40 changes: 38 additions & 2 deletions TheOtherRoles/MetaContext/MetaContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
Expand Down Expand Up @@ -476,12 +476,33 @@ public TextAttributes GetAttribute(AttributeAsset attribute)
AttributeAsset.OblongHeader => new TextAttributes(TextAlignment.Left, GetFont(FontAsset.Oblong), FontStyle.Normal, new(5.2f, false), new(0.45f, 3f), new(255, 255, 255), true),
AttributeAsset.StandardMediumMasked => new TextAttributes(TextAlignment.Center, GetFont(FontAsset.Gothic), FontStyle.Bold, new(1.6f, 0.8f, 1.6f), new(1.45f, 0.3f), new(255, 255, 255), false),
AttributeAsset.StandardLargeWideMasked => new TextAttributes(TextAlignment.Center, GetFont(FontAsset.Gothic), FontStyle.Bold, new(1.7f, 1f, 1.7f), new(2.9f, 0.45f), new(255, 255, 255), false),
AttributeAsset.OverlayContent => new TextAttributes(Instance.GetAttribute(AttributeParams.StandardBaredLeft)) { FontSize = new(1.5f, 1.1f, 1.5f), Size = new(5f, 6f) },
AttributeAsset.OverlayContent => new TextAttributes(Instance.GetAttribute(AttributeParams.StandardBaredLeft)) { FontSize = new(1.5f, 1.1f, 1.5f), Size = new(5f, 6f) },
AttributeAsset.MetaRoleButton => new TextAttributes(TextAlignment.Center, GetFont(FontAsset.GothicMasked), FontStyle.Bold, new(1.8f, 1f, 2f), new(1.4f, 0.26f), new(255, 255, 255), false),
_ => null!
};
}

return allAttrAsset[attribute];
}

public GUIContext Arrange(GUIAlignment alignment, IEnumerable<GUIContext> inner, int perLine)
{
List<GUIContext> widgets = new();
List<GUIContext> horizontalWidgets = new();
foreach (var widget in inner)
{
if (widget == null) continue;

horizontalWidgets.Add(widget);
if (horizontalWidgets.Count == perLine)
{
widgets.Add(HorizontalHolder(alignment, horizontalWidgets.ToArray()));
horizontalWidgets.Clear();
}
}
if (horizontalWidgets.Count > 0) widgets.Add(HorizontalHolder(alignment, horizontalWidgets));

return VerticalHolder(alignment, widgets);
}

public TextAttributes GenerateAttribute(AttributeParams attribute, Color color, FontSize fontSize, Size size)
Expand Down Expand Up @@ -559,6 +580,21 @@ public GUIContext Button(GUIAlignment alignment, TextAttributes attribute, TextC
public TextComponent LocalizedTextComponent(string translationKey) => new TranslateTextComponent(translationKey);
public TextComponent ColorTextComponent(Color color, TextComponent component) => new ColorTextComponent(color, component);

}

public class GUIEmptyWidget : AbstractGUIContext
{
static public GUIEmptyWidget Default = new();

public GUIEmptyWidget(GUIAlignment alignment = GUIAlignment.Left) : base(alignment)
{
}

public override GameObject Instantiate(Size size, out Size actualSize)
{
actualSize = new(0f, 0f);
return null;
}
}

public class TORGUIImage : AbstractGUIContext
Expand Down
13 changes: 11 additions & 2 deletions TheOtherRoles/MetaContext/MetaGUI.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
Expand Down Expand Up @@ -282,7 +282,16 @@ public interface GUI
/// <param name="alignment">コンテキストの配置位置</param>
/// <param name="inner">並べるコンテキスト</param>
/// <returns>生成されたコンテキスト定義</returns>
GUIContext HorizontalHolder(GUIAlignment alignment, params GUIContext[] inner) => HorizontalHolder(alignment, inner, null);
GUIContext HorizontalHolder(GUIAlignment alignment, params GUIContext[] inner) => HorizontalHolder(alignment, inner, null);

/// <summary>
/// ウィジットを指定の個数ずつ縦方向に伸ばしながら配置します。
/// </summary>
/// <param name="alignment"></param>
/// <param name="inner"></param>
/// <param name="perLine"></param>
/// <returns></returns>
GUIContext Arrange(GUIAlignment alignment, IEnumerable<GUIContext> inner, int perLine);

/// <summary>
/// 余白を表すコンテキストです。見た目を整えるために使用します。
Expand Down
Loading

0 comments on commit 8665d95

Please sign in to comment.