11using System ;
2- using System . Linq ;
2+ using System . Collections ;
33using Il2CppInterop . Runtime . Attributes ;
44using MiraAPI . GameOptions ;
5- using MiraAPI . Modifiers ;
65using MiraAPI . Networking ;
76using NewMod . Options . Roles . WraithCallerOptions ;
87using NewMod . Utilities ;
@@ -17,124 +16,171 @@ public class WraithCallerNpc(IntPtr ptr) : MonoBehaviour(ptr)
1716 {
1817 public PlayerControl Owner { get ; set ; }
1918 public PlayerControl Target { get ; set ; }
20- public PlayerControl npc ;
19+ public PlayerControl Visual { get ; set ; }
20+ public Rigidbody2D body ;
21+ public PlayerAnimations animations ;
22+
2123 public LightSource ownerLight ;
2224 public bool isActive ;
2325
2426 [ HideFromIl2Cpp ]
27+
2528 // Inspired by: https://github.com/NuclearPowered/Reactor/blob/e27a79249ea706318f3c06f3dc56a5c42d65b1cf/Reactor.Debugger/Window/Tabs/GameTab.cs#L70
26- public void Initialize ( PlayerControl wraith , PlayerControl target , PlayerControl spawned )
29+ public void Initialize ( PlayerControl owner , PlayerControl target , Vector2 start )
2730 {
28- Owner = wraith ;
31+ Owner = owner ;
2932 Target = target ;
30- npc = spawned ;
3133
32- isActive = true ;
34+ Visual = Instantiate ( AmongUsClient . Instance . PlayerPrefab ) ;
35+ Visual . transform . position = new Vector3 ( start . x , start . y , Owner . transform . position . z ) ;
3336
34- KillAnimation . SetMovement ( npc , true ) ;
37+ Visual . notRealPlayer = true ;
38+ Visual . enabled = false ;
39+ Visual . NetTransform . enabled = false ;
40+ Visual . Collider . enabled = false ;
41+ Visual . MyPhysics . enabled = false ;
3542
36- npc . Collider . enabled = false ;
37- npc . MyPhysics . Speed = OptionGroupSingleton < WraithCallerOptions > . Instance . NPCSpeed ;
43+ PlayerControl . AllPlayerControls . Remove ( Visual ) ;
3844
39- if ( ! npc . TryGetComponent < ModifierComponent > ( out _ ) )
40- npc . gameObject . AddComponent < ModifierComponent > ( ) ;
45+ body = Visual . MyPhysics . body ;
46+ animations = Visual . MyPhysics . Animations ;
4147
42- if ( AmongUsClient . Instance . AmHost )
43- {
44- NewMod . Instance . Log . LogMessage ( $ "Host is setting cosmetics for NPC (ID: { npc . PlayerId } ") ;
45- npc . NetTransform . RpcSnapTo ( Owner . transform . position ) ;
48+ body . isKinematic = false ;
4649
47- var color = ( byte ) ( npc . PlayerId % Palette . PlayerColors . Length ) ;
48- npc . RpcSetName ( "Wraith NPC" ) ;
49- npc . RpcSetColor ( color ) ;
50+ Visual . cosmetics . enabled = true ;
51+ Visual . cosmetics . Visible = true ;
5052
51- var noShadow = npc . gameObject . AddComponent < NoShadowBehaviour > ( ) ;
52- if ( noShadow != null )
53- {
54- noShadow . rend = npc . cosmetics . currentBodySprite . BodySprite ;
55- noShadow . hitOverride = npc . Collider ;
56- }
57- }
53+ Visual . cosmetics . SetName ( "Wraith NPC" ) ;
54+ Visual . cosmetics . ToggleName ( true ) ;
55+ Visual . cosmetics . ToggleHat ( false ) ;
56+ Visual . cosmetics . TogglePet ( false ) ;
57+ Visual . cosmetics . ToggleVisor ( false ) ;
58+
59+ var color = Owner . PlayerId % Palette . PlayerColors . Length ;
60+ var bodySprite = Visual . cosmetics . currentBodySprite ;
61+
62+ bodySprite . Visible = true ;
63+ PlayerMaterial . SetColors ( color , bodySprite . BodySprite ) ;
5864
59- Coroutines . Start ( WalkToTarget ( ) ) ;
65+ var noShadow = Visual . gameObject . AddComponent < NoShadowBehaviour > ( ) ;
66+ noShadow . rend = bodySprite . BodySprite ;
67+ noShadow . hitOverride = Visual . Collider ;
6068
61- if ( OptionGroupSingleton < WraithCallerOptions > . Instance . ShouldSwitchCamToNPC )
69+ isActive = true ;
70+ Coroutines . Start ( CoMove ( ) ) ;
71+
72+ if ( Owner . AmOwner && OptionGroupSingleton < WraithCallerOptions > . Instance . ShouldSwitchCamToNPC )
6273 {
63- Camera . main . GetComponent < FollowerCamera > ( ) . SetTarget ( npc ) ;
74+ Camera . main . GetComponent < FollowerCamera > ( ) . SetTarget ( Visual ) ;
6475 ownerLight = Owner . lightSource ;
65- ownerLight . transform . SetParent ( npc . transform , false ) ;
66- ownerLight . transform . localPosition = npc . Collider . offset ;
76+ ownerLight . transform . SetParent ( Visual . transform , false ) ;
77+ ownerLight . transform . localPosition = Visual . Collider . offset ;
6778 }
6879
69- npc . cosmetics . enabled = true ;
70- npc . enabled = false ;
71-
7280 if ( Target . AmOwner )
7381 SoundManager . Instance . PlaySound ( NewModAsset . HeartbeatSound . LoadAsset ( ) , false , 1f ) ;
7482 }
7583
7684 [ HideFromIl2Cpp ]
77- private System . Collections . IEnumerator WalkToTarget ( )
85+ public IEnumerator CoMove ( )
7886 {
79- //yield return null;
80-
81- if ( ! AmongUsClient . Instance . AmHost ) yield break ;
87+ var speed = OptionGroupSingleton < WraithCallerOptions > . Instance . NPCSpeed ;
8288
8389 while ( isActive && ! MeetingHud . Instance )
8490 {
85- if ( ! Target || ! Target . Data || Target . Data . IsDead )
91+ if ( Target . Data . IsDead || Target . Data . Disconnected )
8692 break ;
8793
88- Vector2 npcPos = npc . GetTruePosition ( ) ;
89- Vector2 targetPos = Target . GetTruePosition ( ) ;
90- Vector2 dir = ( targetPos - npcPos ) . normalized ;
94+ var npcPos = ( Vector2 ) Visual . transform . position ;
95+ var targetPos = Target . GetTruePosition ( ) ;
96+ var delta = targetPos - npcPos ;
97+
98+ var slowDown = Mathf . Clamp ( delta . magnitude * 2f , 0.05f , 1f ) ;
99+ var velocity = delta . normalized * speed * slowDown ;
91100
92- npc . MyPhysics . SetNormalizedVelocity ( dir ) ;
101+ body . velocity = velocity ;
93102
94- if ( Vector2 . Distance ( npcPos , targetPos ) <= 0.1f )
103+ UpdateWalkAnimation ( velocity ) ;
104+
105+ if ( AmongUsClient . Instance . AmHost && delta . magnitude <= 0.15f )
95106 {
96- npc . MyPhysics . SetNormalizedVelocity ( Vector2 . zero ) ;
107+ body . velocity = Vector2 . zero ;
108+ UpdateWalkAnimation ( Vector2 . zero ) ;
97109
98110 Owner . RpcCustomMurder ( Target , true , teleportMurderer : false ) ;
99111
100112 if ( Target . AmOwner )
113+ {
101114 CoroutinesHelper . CoNotify ( "<color=#FF4D4D><b>Oops!</b> The <i>Wraith NPC</i> got you..." ) ;
115+ }
102116
103117 WraithCallerUtilities . AddKillNPC ( Owner . PlayerId ) ;
104118 break ;
105119 }
106- yield return new WaitForFixedUpdate ( ) ;
120+
121+ yield return null ;
107122 }
108123
109- npc . MyPhysics . SetNormalizedVelocity ( Vector2 . zero ) ;
124+ body . velocity = Vector2 . zero ;
125+ UpdateWalkAnimation ( Vector2 . zero ) ;
110126 Dispose ( ) ;
111127 }
128+ [ HideFromIl2Cpp ]
129+ public void UpdateWalkAnimation ( Vector2 velocity )
130+ {
131+ bool moving = velocity . sqrMagnitude >= 0.01f ;
112132
133+ if ( velocity . x < - 0.01f )
134+ Visual . cosmetics . SetFlipXWithoutPet ( true ) ;
135+ else if ( velocity . x > 0.01f )
136+ Visual . cosmetics . SetFlipXWithoutPet ( false ) ;
137+
138+ if ( moving )
139+ {
140+ if ( ! animations . IsPlayingRunAnimation ( ) )
141+ {
142+ animations . PlayRunAnimation ( ) ;
143+ }
144+ if ( Visual . cosmetics . HasSkinLoaded ( ) && ! Visual . cosmetics . IsSkinPlayingRunAnim ( ) )
145+ {
146+ Visual . cosmetics . AnimateSkinRun ( ) ;
147+ }
148+ }
149+ else
150+ {
151+ if ( animations . IsPlayingRunAnimation ( ) || ! animations . IsPlayingSomeAnimation ( ) )
152+ {
153+ animations . PlayIdleAnimation ( ) ;
154+
155+ if ( Visual . cosmetics . HasSkinLoaded ( ) )
156+ {
157+ Visual . cosmetics . AnimateSkinIdle ( ) ;
158+ }
159+ }
160+ }
161+ var pos = Visual . transform . position ;
162+ pos . z = pos . y / 1000f ;
163+ Visual . transform . position = pos ;
164+ }
113165 [ HideFromIl2Cpp ]
114166 public void Dispose ( )
115167 {
116168 if ( ! isActive ) return ;
169+
117170 isActive = false ;
118171
119- if ( OptionGroupSingleton < WraithCallerOptions > . Instance . ShouldSwitchCamToNPC )
172+ if ( Owner . AmOwner && OptionGroupSingleton < WraithCallerOptions > . Instance . ShouldSwitchCamToNPC )
120173 {
121174 Camera . main . GetComponent < FollowerCamera > ( ) . SetTarget ( Owner ) ;
122175 ownerLight . transform . SetParent ( Owner . transform , false ) ;
123176 ownerLight . transform . localPosition = Owner . Collider . offset ;
124177 }
125178
126- if ( AmongUsClient . Instance . AmHost )
127- {
128- var info = GameData . Instance . AllPlayers . ToArray ( ) . FirstOrDefault ( d => d . PlayerId == npc . PlayerId ) ;
129- GameData . Instance . RemovePlayer ( info . PlayerId ) ;
130- PlayerControl . AllPlayerControls . Remove ( npc ) ;
131-
132- npc . Despawn ( ) ;
133- Destroy ( npc . gameObject ) ;
134- npc = null ;
135- }
179+ if ( body )
180+ body . velocity = Vector2 . zero ;
136181
182+ Destroy ( Visual . gameObject ) ;
137183 Destroy ( gameObject ) ;
138184 }
139185 }
140- }
186+ }
0 commit comments