1+ using QSB . HUD . Messages ;
2+ using QSB . Messaging ;
3+ using QSB . Player ;
4+ using QSB . ServerSettings ;
5+ using QSB . Utility ;
6+ using QSB . WorldSync ;
7+ using System . Linq ;
8+ using UnityEngine ;
9+ using UnityEngine . SceneManagement ;
10+
11+ namespace QSB . HUD ;
12+
13+ internal class MultiplayerHUDManager : MonoBehaviour , IAddComponentOnStart
14+ {
15+ public static MultiplayerHUDManager Instance ;
16+
17+ private Transform _playerList ;
18+ private Material _markerMaterial ;
19+
20+ public static Sprite UnknownSprite ;
21+ public static Sprite DeadSprite ;
22+ public static Sprite SpaceSprite ;
23+ public static Sprite ShipSprite ;
24+ public static Sprite TimberHearth ;
25+ public static Sprite Attlerock ;
26+ public static Sprite CaveTwin ;
27+ public static Sprite TowerTwin ;
28+ public static Sprite BrittleHollow ;
29+ public static Sprite HollowsLantern ;
30+ public static Sprite GiantsDeep ;
31+ public static Sprite DarkBramble ;
32+ public static Sprite Interloper ;
33+ public static Sprite WhiteHole ;
34+
35+ public static ListStack < HUDIcon > HUDIconStack = new ( ) ;
36+
37+ private void Start ( )
38+ {
39+ Instance = this ;
40+
41+ GlobalMessenger . AddListener ( OWEvents . WakeUp , OnWakeUp ) ;
42+
43+ QSBPlayerManager . OnAddPlayer += OnAddPlayer ;
44+ QSBPlayerManager . OnRemovePlayer += OnRemovePlayer ;
45+
46+ UnknownSprite = QSBCore . HUDAssetBundle . LoadAsset < Sprite > ( "Assets/MULTIPLAYER_UI/playerbox_unknown.png" ) ;
47+ DeadSprite = QSBCore . HUDAssetBundle . LoadAsset < Sprite > ( "Assets/MULTIPLAYER_UI/playerbox_dead.png" ) ;
48+ ShipSprite = QSBCore . HUDAssetBundle . LoadAsset < Sprite > ( "Assets/MULTIPLAYER_UI/playerbox_ship.png" ) ;
49+ CaveTwin = QSBCore . HUDAssetBundle . LoadAsset < Sprite > ( "Assets/MULTIPLAYER_UI/playerbox_cavetwin.png" ) ;
50+ TowerTwin = QSBCore . HUDAssetBundle . LoadAsset < Sprite > ( "Assets/MULTIPLAYER_UI/playerbox_towertwin.png" ) ;
51+ TimberHearth = QSBCore . HUDAssetBundle . LoadAsset < Sprite > ( "Assets/MULTIPLAYER_UI/playerbox_timberhearth.png" ) ;
52+ Attlerock = QSBCore . HUDAssetBundle . LoadAsset < Sprite > ( "Assets/MULTIPLAYER_UI/playerbox_attlerock.png" ) ;
53+ BrittleHollow = QSBCore . HUDAssetBundle . LoadAsset < Sprite > ( "Assets/MULTIPLAYER_UI/playerbox_brittlehollow.png" ) ;
54+ HollowsLantern = QSBCore . HUDAssetBundle . LoadAsset < Sprite > ( "Assets/MULTIPLAYER_UI/playerbox_hollowslantern.png" ) ;
55+ GiantsDeep = QSBCore . HUDAssetBundle . LoadAsset < Sprite > ( "Assets/MULTIPLAYER_UI/playerbox_giantsdeep.png" ) ;
56+ DarkBramble = QSBCore . HUDAssetBundle . LoadAsset < Sprite > ( "Assets/MULTIPLAYER_UI/playerbox_darkbramble.png" ) ;
57+ Interloper = QSBCore . HUDAssetBundle . LoadAsset < Sprite > ( "Assets/MULTIPLAYER_UI/playerbox_interloper.png" ) ;
58+ WhiteHole = QSBCore . HUDAssetBundle . LoadAsset < Sprite > ( "Assets/MULTIPLAYER_UI/playerbox_whitehole.png" ) ;
59+ SpaceSprite = QSBCore . HUDAssetBundle . LoadAsset < Sprite > ( "Assets/MULTIPLAYER_UI/playerbox_space.png" ) ;
60+ }
61+
62+ private void Update ( )
63+ {
64+ if ( ! QSBWorldSync . AllObjectsReady || _playerList == null )
65+ {
66+ return ;
67+ }
68+
69+ _playerList . gameObject . SetActive ( ServerSettingsManager . ShowExtraHUD ) ;
70+ }
71+
72+ private void OnWakeUp ( )
73+ {
74+ var hudController = Locator . GetPlayerCamera ( ) . transform . Find ( "Helmet" ) . Find ( "HUDController" ) . GetComponent < HUDCanvas > ( ) ;
75+ var hudCamera = hudController . _hudCamera ;
76+ var hudCanvas = hudCamera . transform . parent . Find ( "UICanvas" ) ;
77+
78+ var multiplayerGroup = Instantiate ( QSBCore . HUDAssetBundle . LoadAsset < GameObject > ( "assets/Prefabs/multiplayergroup.prefab" ) ) ;
79+
80+ Delay . RunNextFrame ( ( ) =>
81+ {
82+ // no idea why this has to be next frame, but it does
83+ multiplayerGroup . transform . parent = hudCanvas ;
84+ multiplayerGroup . transform . localPosition = Vector3 . zero ;
85+ var rect = multiplayerGroup . GetComponent < RectTransform > ( ) ;
86+ rect . anchorMin = new Vector2 ( 1 , 0.5f ) ;
87+ rect . anchorMax = new Vector2 ( 1 , 0.5f ) ;
88+ rect . sizeDelta = new Vector2 ( 100 , 100 ) ;
89+ rect . anchoredPosition3D = new Vector3 ( - 267 , 0 , 0 ) ;
90+ rect . localRotation = Quaternion . Euler ( 0 , 55 , 0 ) ;
91+ rect . localScale = Vector3 . one ;
92+ } ) ;
93+
94+ _playerList = multiplayerGroup . transform . Find ( "PlayerList" ) ;
95+
96+ foreach ( var player in QSBPlayerManager . PlayerList )
97+ {
98+ AddBox ( player ) ;
99+
100+ foreach ( var item in QSBWorldSync . GetUnityObjects < Minimap > ( ) )
101+ {
102+ AddMinimapMarker ( player , item ) ;
103+ }
104+ }
105+
106+ CreateTrigger ( "TowerTwin_Body/Sector_TowerTwin" , HUDIcon . TOWER_TWIN ) ;
107+ CreateTrigger ( "CaveTwin_Body/Sector_CaveTwin" , HUDIcon . CAVE_TWIN ) ;
108+ CreateTrigger ( "TimberHearth_Body/Sector_TH" , HUDIcon . TIMBER_HEARTH ) ;
109+ CreateTrigger ( "Moon_Body/Sector_THM" , HUDIcon . ATTLEROCK ) ;
110+ CreateTrigger ( "BrittleHollow_Body/Sector_BH" , HUDIcon . BRITTLE_HOLLOW ) ;
111+ CreateTrigger ( "VolcanicMoon_Body/Sector_VM" , HUDIcon . HOLLOWS_LANTERN ) ;
112+ CreateTrigger ( "GiantsDeep_Body/Sector_GD" , HUDIcon . GIANTS_DEEP ) ;
113+ CreateTrigger ( "DarkBramble_Body/Sector_DB" , HUDIcon . DARK_BRAMBLE ) ;
114+ CreateTrigger ( "Comet_Body/Sector_CO" , HUDIcon . INTERLOPER ) ;
115+ CreateTrigger ( "WhiteHole_Body/Sector_WhiteHole" , HUDIcon . WHITE_HOLE ) ;
116+
117+ HUDIconStack . Clear ( ) ;
118+ HUDIconStack . Push ( HUDIcon . SPACE ) ;
119+ HUDIconStack . Push ( HUDIcon . TIMBER_HEARTH ) ;
120+
121+ new PlanetMessage ( HUDIcon . TIMBER_HEARTH ) . Send ( ) ;
122+
123+ var playerMinimap = QSBWorldSync . GetUnityObjects < Minimap > ( ) . First ( x => x . name == "Minimap_Root" ) ;
124+ _markerMaterial = Instantiate ( playerMinimap . _probeMarkerTransform . GetComponent < MeshRenderer > ( ) . material ) ;
125+ _markerMaterial . color = Color . gray ;
126+ }
127+
128+ public void UpdateMinimapMarkers ( Minimap minimap )
129+ {
130+ var localRuleset = Locator . GetPlayerRulesetDetector ( ) . GetPlanetoidRuleset ( ) ;
131+
132+ foreach ( var player in QSBPlayerManager . PlayerList )
133+ {
134+ if ( player . IsDead || player . IsLocalPlayer || ! player . IsReady )
135+ {
136+ continue ;
137+ }
138+
139+ if ( player . RulesetDetector == null )
140+ {
141+ if ( player . Body != null )
142+ {
143+ DebugLog . ToConsole ( $ "Error - { player . PlayerId } 's RulesetDetector is null.", OWML . Common . MessageType . Error ) ;
144+ }
145+
146+ continue ;
147+ }
148+
149+ if ( player . RulesetDetector . GetPlanetoidRuleset ( ) == null
150+ || player . RulesetDetector . GetPlanetoidRuleset ( ) != localRuleset )
151+ {
152+ continue ;
153+ }
154+
155+ if ( player . MinimapPlayerMarker == null )
156+ {
157+ continue ;
158+ }
159+
160+ if ( ServerSettingsManager . ShowExtraHUD )
161+ {
162+ player . MinimapPlayerMarker . localPosition = GetLocalMapPosition ( player , minimap ) ;
163+ player . MinimapPlayerMarker . LookAt ( minimap . _globeMeshTransform , minimap . _globeMeshTransform . up ) ;
164+ }
165+ else
166+ {
167+ player . MinimapPlayerMarker . localPosition = Vector3 . zero ;
168+ player . MinimapPlayerMarker . localRotation = Quaternion . identity ;
169+ }
170+ }
171+ }
172+
173+ private void AddMinimapMarker ( PlayerInfo player , Minimap minimap )
174+ {
175+ player . MinimapPlayerMarker = Instantiate ( minimap . _probeMarkerTransform ) ;
176+ player . MinimapPlayerMarker . parent = minimap . _probeMarkerTransform . parent ;
177+ player . MinimapPlayerMarker . localScale = new Vector3 ( 0.05f , 0.05f , 0.05f ) ;
178+ player . MinimapPlayerMarker . localPosition = Vector3 . zero ;
179+ player . MinimapPlayerMarker . localRotation = Quaternion . identity ;
180+ player . MinimapPlayerMarker . GetComponent < MeshRenderer > ( ) . material = _markerMaterial ;
181+ }
182+
183+ private void AddBox ( PlayerInfo player )
184+ {
185+ var box = Instantiate ( QSBCore . HUDAssetBundle . LoadAsset < GameObject > ( "assets/Prefabs/playerbox.prefab" ) ) ;
186+ box . transform . parent = _playerList ;
187+ box . transform . localScale = new Vector3 ( 1 , 1 , 1 ) ;
188+ box . transform . localPosition = Vector3 . zero ;
189+ box . transform . localRotation = Quaternion . identity ;
190+
191+ var boxScript = box . GetComponent < PlayerBox > ( ) ;
192+ boxScript . AssignPlayer ( player ) ;
193+ }
194+
195+ private Vector3 GetLocalMapPosition ( PlayerInfo player , Minimap minimap )
196+ {
197+ return Vector3 . Scale (
198+ player . RulesetDetector . GetPlanetoidRuleset ( ) . transform . InverseTransformPoint ( player . Body . transform . position ) . normalized * 0.51f ,
199+ minimap . _globeMeshTransform . localScale ) ;
200+ }
201+
202+ private void OnAddPlayer ( PlayerInfo player )
203+ {
204+ if ( ! QSBWorldSync . AllObjectsReady )
205+ {
206+ return ;
207+ }
208+
209+ AddBox ( player ) ;
210+
211+ foreach ( var item in QSBWorldSync . GetUnityObjects < Minimap > ( ) )
212+ {
213+ AddMinimapMarker ( player , item ) ;
214+ }
215+ }
216+
217+ private void OnRemovePlayer ( PlayerInfo player )
218+ {
219+ Destroy ( player . HUDBox . gameObject ) ;
220+ }
221+
222+ private PlanetTrigger CreateTrigger ( string parentPath , HUDIcon icon )
223+ => CreateTrigger ( Find ( parentPath ) , icon ) ;
224+
225+ private PlanetTrigger CreateTrigger ( GameObject parent , HUDIcon icon )
226+ {
227+ if ( parent == null )
228+ {
229+ return null ;
230+ }
231+
232+ var triggerGO = parent . FindChild ( "HUD_PLANET_TRIGGER" ) ;
233+ if ( triggerGO != null )
234+ {
235+ var trigger = triggerGO . GetAddComponent < PlanetTrigger > ( ) ;
236+ trigger . Icon = icon ;
237+ return trigger ;
238+ }
239+ else
240+ {
241+ triggerGO = new GameObject ( "HUD_PLANET_TRIGGER" ) ;
242+ triggerGO . transform . SetParent ( parent . transform , false ) ;
243+ triggerGO . SetActive ( false ) ;
244+ var trigger = triggerGO . AddComponent < PlanetTrigger > ( ) ;
245+ trigger . Icon = icon ;
246+ triggerGO . SetActive ( true ) ;
247+ return trigger ;
248+ }
249+ }
250+
251+ public static GameObject Find ( string path )
252+ {
253+ var go = GameObject . Find ( path ) ;
254+
255+ if ( go == null )
256+ {
257+ // find inactive use root + transform.find
258+ var names = path . Split ( '/' ) ;
259+ var rootName = names [ 0 ] ;
260+ var root = SceneManager . GetActiveScene ( ) . GetRootGameObjects ( ) . FirstOrDefault ( x => x . name == rootName ) ;
261+ if ( root == null )
262+ {
263+ return null ;
264+ }
265+
266+ var childPath = string . Join ( "/" , names . Skip ( 1 ) ) ;
267+ go = root . FindChild ( childPath ) ;
268+ }
269+
270+ return go ;
271+ }
272+ }
0 commit comments