-
Notifications
You must be signed in to change notification settings - Fork 180
/
Copy pathMeetingHudPatch.cs
282 lines (262 loc) · 12 KB
/
MeetingHudPatch.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HarmonyLib;
using UnityEngine;
using TownOfHost.Modules;
using TownOfHost.Roles;
using TownOfHost.Roles.Core;
using TownOfHost.Roles.Neutral;
using TownOfHost.Roles.Core.Interfaces;
using static TownOfHost.Translator;
namespace TownOfHost;
[HarmonyPatch]
public static class MeetingHudPatch
{
[HarmonyPatch(typeof(MeetingHud), nameof(MeetingHud.CheckForEndVoting))]
class CheckForEndVotingPatch
{
public static bool Prefix()
{
if (!AmongUsClient.Instance.AmHost) return true;
MeetingVoteManager.Instance?.CheckAndEndMeeting();
return false;
}
}
[HarmonyPatch(typeof(MeetingHud), nameof(MeetingHud.CastVote))]
public static class CastVotePatch
{
public static bool Prefix(MeetingHud __instance, [HarmonyArgument(0)] byte srcPlayerId /* 投票した人 */ , [HarmonyArgument(1)] byte suspectPlayerId /* 投票された人 */ )
{
var voter = Utils.GetPlayerById(srcPlayerId);
var voted = Utils.GetPlayerById(suspectPlayerId);
if (voter.GetRoleClass()?.CheckVoteAsVoter(voted) == false)
{
__instance.RpcClearVote(voter.GetClientId());
Logger.Info($"{voter.GetNameWithRole()} は投票しない", nameof(CastVotePatch));
return false;
}
MeetingVoteManager.Instance?.SetVote(srcPlayerId, suspectPlayerId);
return true;
}
}
[HarmonyPatch(typeof(MeetingHud), nameof(MeetingHud.Start))]
class StartPatch
{
public static void Prefix()
{
Logger.Info("------------会議開始------------", "Phase");
ChatUpdatePatch.DoBlockChat = true;
GameStates.AlreadyDied |= !Utils.IsAllAlive;
Main.AllPlayerControls.Do(x => ReportDeadBodyPatch.WaitReport[x.PlayerId].Clear());
MeetingStates.MeetingCalled = true;
}
public static void Postfix(MeetingHud __instance)
{
MeetingVoteManager.Start();
SoundManager.Instance.ChangeAmbienceVolume(0f);
if (!GameStates.IsModHost) return;
var myRole = PlayerControl.LocalPlayer.GetRoleClass();
foreach (var pva in __instance.playerStates)
{
var pc = Utils.GetPlayerById(pva.TargetPlayerId);
if (pc == null) continue;
var roleTextMeeting = UnityEngine.Object.Instantiate(pva.NameText);
roleTextMeeting.transform.SetParent(pva.NameText.transform);
roleTextMeeting.transform.localPosition = new Vector3(0f, -0.18f, 0f);
roleTextMeeting.fontSize = 1.5f;
(roleTextMeeting.enabled, roleTextMeeting.text)
= Utils.GetRoleNameAndProgressTextData(PlayerControl.LocalPlayer, pc);
roleTextMeeting.gameObject.name = "RoleTextMeeting";
roleTextMeeting.enableWordWrapping = false;
// 役職とサフィックスを同時に表示する必要が出たら要改修
var suffixBuilder = new StringBuilder(32);
if (myRole != null)
{
suffixBuilder.Append(myRole.GetSuffix(PlayerControl.LocalPlayer, pc, isForMeeting: true));
}
suffixBuilder.Append(CustomRoleManager.GetSuffixOthers(PlayerControl.LocalPlayer, pc, isForMeeting: true));
if (suffixBuilder.Length > 0)
{
roleTextMeeting.text = suffixBuilder.ToString();
roleTextMeeting.enabled = true;
}
}
CustomRoleManager.AllActiveRoles.Values.Do(role => role.OnStartMeeting());
if (Options.SyncButtonMode.GetBool())
{
Utils.SendMessage(string.Format(GetString("Message.SyncButtonLeft"), Options.SyncedButtonCount.GetFloat() - Options.UsedButtonCount));
Logger.Info("緊急会議ボタンはあと" + (Options.SyncedButtonCount.GetFloat() - Options.UsedButtonCount) + "回使用可能です。", "SyncButtonMode");
}
if (AntiBlackout.OverrideExiledPlayer)
{
Utils.SendMessage(GetString("Warning.OverrideExiledPlayer"));
}
if (MeetingStates.FirstMeeting) TemplateManager.SendTemplate("OnFirstMeeting", noErr: true);
TemplateManager.SendTemplate("OnMeeting", noErr: true);
if (AmongUsClient.Instance.AmHost)
{
_ = new LateTask(() =>
{
foreach (var seen in Main.AllPlayerControls)
{
var seenName = seen.GetRealName(isMeeting: true);
var coloredName = Utils.ColorString(seen.GetRoleColor(), seenName);
foreach (var seer in Main.AllPlayerControls)
{
seen.RpcSetNamePrivate(
seer == seen ? coloredName : seenName,
true,
seer);
}
}
ChatUpdatePatch.DoBlockChat = false;
}, 3f, "SetName To Chat");
}
foreach (var pva in __instance.playerStates)
{
if (pva == null) continue;
var seer = PlayerControl.LocalPlayer;
var seerRole = seer.GetRoleClass();
var target = Utils.GetPlayerById(pva.TargetPlayerId);
if (target == null) continue;
var sb = new StringBuilder();
//会議画面での名前変更
//自分自身の名前の色を変更
//NameColorManager準拠の処理
pva.NameText.text = pva.NameText.text.ApplyNameColorData(seer, target, true);
//とりあえずSnitchは会議中にもインポスターを確認することができる仕様にしていますが、変更する可能性があります。
if (seer.KnowDeathReason(target))
sb.Append($"({Utils.ColorString(Utils.GetRoleColor(CustomRoles.Doctor), Utils.GetVitalText(target.PlayerId))})");
sb.Append(seerRole?.GetMark(seer, target, true));
sb.Append(CustomRoleManager.GetMarkOthers(seer, target, true));
foreach (var subRole in target.GetCustomSubRoles())
{
switch (subRole)
{
case CustomRoles.Lovers:
if (seer.Is(CustomRoles.Lovers) || seer.Data.IsDead)
sb.Append(Utils.ColorString(Utils.GetRoleColor(CustomRoles.Lovers), "♡"));
break;
}
}
//会議画面ではインポスター自身の名前にSnitchマークはつけません。
pva.NameText.text += sb.ToString();
}
}
}
[HarmonyPatch(typeof(MeetingHud), nameof(MeetingHud.Update))]
class UpdatePatch
{
public static void Postfix(MeetingHud __instance)
{
if (!AmongUsClient.Instance.AmHost) return;
if (Input.GetMouseButtonUp(1) && Input.GetKey(KeyCode.LeftControl))
{
__instance.playerStates.DoIf(x => x.HighlightedFX.enabled, x =>
{
var player = Utils.GetPlayerById(x.TargetPlayerId);
player.RpcExileV2();
var state = PlayerState.GetByPlayerId(player.PlayerId);
state.DeathReason = CustomDeathReason.Execution;
state.SetDead();
Utils.SendMessage(string.Format(GetString("Message.Executed"), player.Data.PlayerName));
Logger.Info($"{player.GetNameWithRole()}を処刑しました", "Execution");
__instance.CheckForEndVoting();
});
}
}
}
[HarmonyPatch(typeof(MeetingHud), nameof(MeetingHud.OnDestroy))]
class OnDestroyPatch
{
public static void Postfix()
{
MeetingStates.FirstMeeting = false;
Logger.Info("------------会議終了------------", "Phase");
if (AmongUsClient.Instance.AmHost)
{
AntiBlackout.SetIsDead();
}
// MeetingVoteManagerを通さずに会議が終了した場合の後処理
MeetingVoteManager.Instance?.Destroy();
}
}
public static void TryAddAfterMeetingDeathPlayers(CustomDeathReason deathReason, params byte[] playerIds)
{
var AddedIdList = new List<byte>();
foreach (var playerId in playerIds)
if (Main.AfterMeetingDeathPlayers.TryAdd(playerId, deathReason))
AddedIdList.Add(playerId);
CheckForDeathOnExile(deathReason, AddedIdList.ToArray());
}
public static void CheckForDeathOnExile(CustomDeathReason deathReason, params byte[] playerIds)
{
foreach (var playerId in playerIds)
{
//Loversの後追い
if (CustomRoles.Lovers.IsPresent() && !Main.isLoversDead && Main.LoversPlayers.Find(lp => lp.PlayerId == playerId) != null)
FixedUpdatePatch.LoversSuicide(playerId, true);
//道連れチェック
RevengeOnExile(playerId, deathReason);
}
}
private static void RevengeOnExile(byte playerId, CustomDeathReason deathReason)
{
var player = Utils.GetPlayerById(playerId);
if (player == null) return;
var target = PickRevengeTarget(player, deathReason);
if (target == null) return;
TryAddAfterMeetingDeathPlayers(CustomDeathReason.Revenge, target.PlayerId);
target.SetRealKiller(player);
Logger.Info($"{player.GetNameWithRole()}の道連れ先:{target.GetNameWithRole()}", "RevengeOnExile");
}
private static PlayerControl PickRevengeTarget(PlayerControl exiledplayer, CustomDeathReason deathReason)//道連れ先選定
{
List<PlayerControl> TargetList = new();
if (exiledplayer.GetRoleClass() is INekomata nekomata)
{
// 道連れしない状態ならnull
if (!nekomata.DoRevenge(deathReason))
{
return null;
}
TargetList = Main.AllAlivePlayerControls.Where(candidate => candidate != exiledplayer && !Main.AfterMeetingDeathPlayers.ContainsKey(candidate.PlayerId) && nekomata.IsCandidate(candidate)).ToList();
}
else
{
var isMadmate =
exiledplayer.Is(CustomRoleTypes.Madmate) ||
// マッド属性化時に削除
(exiledplayer.GetRoleClass() is SchrodingerCat schrodingerCat && schrodingerCat.AmMadmate);
foreach (var candidate in Main.AllAlivePlayerControls)
{
if (candidate == exiledplayer || Main.AfterMeetingDeathPlayers.ContainsKey(candidate.PlayerId)) continue;
switch (exiledplayer.GetCustomRole())
{
// ここにINekomata未適用の道連れ役職を追加
default:
if (isMadmate && deathReason == CustomDeathReason.Vote && Options.MadmateRevengeCrewmate.GetBool() //黒猫オプション
&& !candidate.Is(CustomRoleTypes.Impostor))
TargetList.Add(candidate);
break;
}
}
}
if (TargetList == null || TargetList.Count == 0) return null;
var rand = IRandom.Instance;
var target = TargetList[rand.Next(TargetList.Count)];
return target;
}
}
[HarmonyPatch(typeof(PlayerVoteArea), nameof(PlayerVoteArea.SetHighlighted))]
class SetHighlightedPatch
{
public static bool Prefix(PlayerVoteArea __instance, bool value)
{
if (!AmongUsClient.Instance.AmHost) return true;
if (!__instance.HighlightedFX) return false;
__instance.HighlightedFX.enabled = value;
return false;
}
}