first commit
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NitroxClient.GameLogic.InitialSync.Abstract;
|
||||
using NitroxClient.MonoBehaviours;
|
||||
using NitroxModel.DataStructures.GameLogic;
|
||||
using NitroxModel.Packets;
|
||||
using Story;
|
||||
|
||||
namespace NitroxClient.GameLogic.InitialSync;
|
||||
|
||||
public sealed class StoryGoalInitialSyncProcessor : InitialSyncProcessor
|
||||
{
|
||||
private readonly TimeManager timeManager;
|
||||
|
||||
public StoryGoalInitialSyncProcessor(TimeManager timeManager)
|
||||
{
|
||||
this.timeManager = timeManager;
|
||||
|
||||
AddStep(SetTimeData);
|
||||
AddStep(SetupStoryGoalManager);
|
||||
AddStep(SetupTrackers);
|
||||
AddStep(SetupAuroraAndSunbeam);
|
||||
AddStep(SetScheduledGoals);
|
||||
}
|
||||
|
||||
private static void SetupStoryGoalManager(InitialPlayerSync packet)
|
||||
{
|
||||
List<string> completedGoals = packet.StoryGoalData.CompletedGoals;
|
||||
List<string> radioQueue = packet.StoryGoalData.RadioQueue;
|
||||
Dictionary<string, float> personalGoals = packet.StoryGoalData.PersonalCompletedGoalsWithTimestamp;
|
||||
StoryGoalManager storyGoalManager = StoryGoalManager.main;
|
||||
|
||||
storyGoalManager.completedGoals.AddRange(completedGoals);
|
||||
|
||||
storyGoalManager.pendingRadioMessages.AddRange(radioQueue);
|
||||
storyGoalManager.PulsePendingMessages();
|
||||
|
||||
// Restore states of GoalManager and the (tutorial) arrow system
|
||||
foreach (KeyValuePair<string, float> entry in personalGoals)
|
||||
{
|
||||
Goal entryGoal = GoalManager.main.goals.Find(goal => goal.customGoalName.Equals(entry.Key));
|
||||
if (entryGoal != null)
|
||||
{
|
||||
entryGoal.SetTimeCompleted(entry.Value);
|
||||
}
|
||||
}
|
||||
GoalManager.main.completedGoalNames.AddRange(personalGoals.Keys);
|
||||
PlayerWorldArrows.main.completedCustomGoals.AddRange(personalGoals.Keys);
|
||||
|
||||
// Deactivate the current arrow if it was completed
|
||||
if (personalGoals.Any(goal => goal.Key.Equals(WorldArrowManager.main.currentGoalText)))
|
||||
{
|
||||
WorldArrowManager.main.DeactivateArrow();
|
||||
}
|
||||
|
||||
Log.Info($"""
|
||||
Received initial sync packet with:
|
||||
- Completed story goals : {completedGoals.Count}
|
||||
- Personal goals : {personalGoals.Count}
|
||||
- Radio queue : {radioQueue.Count}
|
||||
""");
|
||||
}
|
||||
|
||||
private static void SetupTrackers(InitialPlayerSync packet)
|
||||
{
|
||||
List<string> completedGoals = packet.StoryGoalData.CompletedGoals;
|
||||
StoryGoalManager storyGoalManager = StoryGoalManager.main;
|
||||
OnGoalUnlockTracker onGoalUnlockTracker = storyGoalManager.onGoalUnlockTracker;
|
||||
CompoundGoalTracker compoundGoalTracker = storyGoalManager.compoundGoalTracker;
|
||||
|
||||
// Initializing CompoundGoalTracker and OnGoalUnlockTracker again (with OnSceneObjectsLoaded) requires us to
|
||||
// we first clear what was done in the first iteration of OnSceneObjectsLoaded
|
||||
onGoalUnlockTracker.goalUnlocks.Clear();
|
||||
compoundGoalTracker.goals.Clear();
|
||||
// we force initialized to false so OnSceneObjectsLoaded actually does something
|
||||
storyGoalManager.initialized = false;
|
||||
storyGoalManager.OnSceneObjectsLoaded();
|
||||
|
||||
// Clean LocationGoalTracker, BiomeGoalTracker and ItemGoalTracker already completed goals
|
||||
storyGoalManager.locationGoalTracker.goals.RemoveAll(goal => completedGoals.Contains(goal.key));
|
||||
storyGoalManager.biomeGoalTracker.goals.RemoveAll(goal => completedGoals.Contains(goal.key));
|
||||
|
||||
List<TechType> techTypesToRemove = new();
|
||||
foreach (KeyValuePair<TechType, List<ItemGoal>> entry in storyGoalManager.itemGoalTracker.goals)
|
||||
{
|
||||
// Goals are all triggered at the same time but we don't know if some entries share certain goals
|
||||
if (entry.Value.All(goal => completedGoals.Contains(goal.key)))
|
||||
{
|
||||
techTypesToRemove.Add(entry.Key);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
techTypesToRemove.ForEach(techType => storyGoalManager.itemGoalTracker.goals.Remove(techType));
|
||||
|
||||
// OnGoalUnlock might trigger the creation of a signal which is later on set to invisible when getting close to it
|
||||
// the invisibility is managed by PingInstance_Set_Patches and is restored during PlayerPreferencesInitialSyncProcessor
|
||||
// So we still need to recreate the signals at every game launch
|
||||
|
||||
// To avoid having the SignalPing play its sound we just make its notification null while triggering it
|
||||
// (the sound is something like "coordinates added to the gps" or something)
|
||||
SignalPing prefabSignalPing = onGoalUnlockTracker.signalPrefab.GetComponent<SignalPing>();
|
||||
PDANotification pdaNotification = prefabSignalPing.vo;
|
||||
prefabSignalPing.vo = null;
|
||||
|
||||
foreach (OnGoalUnlock onGoalUnlock in onGoalUnlockTracker.unlockData.onGoalUnlocks)
|
||||
{
|
||||
if (completedGoals.Contains(onGoalUnlock.goal))
|
||||
{
|
||||
// Code adapted from OnGoalUnlock.Trigger
|
||||
foreach (UnlockSignalData unlockSignalData in onGoalUnlock.signals)
|
||||
{
|
||||
unlockSignalData.Trigger(onGoalUnlockTracker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// recover the notification sound
|
||||
prefabSignalPing.vo = pdaNotification;
|
||||
}
|
||||
|
||||
// Must happen after CompletedGoals
|
||||
private static void SetupAuroraAndSunbeam(InitialPlayerSync packet)
|
||||
{
|
||||
TimeData timeData = packet.TimeData;
|
||||
|
||||
AuroraWarnings auroraWarnings = Player.mainObject.GetComponentInChildren<AuroraWarnings>(true);
|
||||
auroraWarnings.timeSerialized = DayNightCycle.main.timePassedAsFloat;
|
||||
auroraWarnings.OnProtoDeserialize(null);
|
||||
|
||||
CrashedShipExploder.main.version = 2;
|
||||
CrashedShipExploder.main.initialized = true;
|
||||
StoryManager.UpdateAuroraData(timeData.AuroraEventData);
|
||||
CrashedShipExploder.main.timeSerialized = DayNightCycle.main.timePassedAsFloat;
|
||||
CrashedShipExploder.main.OnProtoDeserialize(null);
|
||||
|
||||
// Sunbeam countdown is deducted from the scheduled goal PrecursorGunAimCheck
|
||||
NitroxScheduledGoal sunbeamCountdownGoal = packet.StoryGoalData.ScheduledGoals.Find(goal => string.Equals(goal.GoalKey, "PrecursorGunAimCheck", StringComparison.OrdinalIgnoreCase));
|
||||
if (sunbeamCountdownGoal != null)
|
||||
{
|
||||
StoryGoalCustomEventHandler.main.countdownActive = true;
|
||||
StoryGoalCustomEventHandler.main.countdownStartingTime = sunbeamCountdownGoal.TimeExecute - 2370;
|
||||
// See StoryGoalCustomEventHandler.endTime for calculation (endTime - 30 seconds)
|
||||
}
|
||||
}
|
||||
|
||||
// Must happen after CompletedGoals
|
||||
private static void SetScheduledGoals(InitialPlayerSync packet)
|
||||
{
|
||||
List<NitroxScheduledGoal> scheduledGoals = packet.StoryGoalData.ScheduledGoals;
|
||||
|
||||
// We don't want any scheduled goal we add now to be executed before initial sync has finished, else they might not get broadcasted
|
||||
StoryGoalScheduler.main.paused = true;
|
||||
Multiplayer.OnLoadingComplete += () => StoryGoalScheduler.main.paused = false;
|
||||
|
||||
foreach (NitroxScheduledGoal scheduledGoal in scheduledGoals)
|
||||
{
|
||||
// Clear duplicated goals that might have appeared during loading and before sync
|
||||
StoryGoalScheduler.main.schedule.RemoveAll(goal => goal.goalKey == scheduledGoal.GoalKey);
|
||||
|
||||
ScheduledGoal goal = new()
|
||||
{
|
||||
goalKey = scheduledGoal.GoalKey,
|
||||
goalType = (Story.GoalType)scheduledGoal.GoalType,
|
||||
timeExecute = scheduledGoal.TimeExecute,
|
||||
};
|
||||
if (!StoryGoalManager.main.completedGoals.Contains(goal.goalKey))
|
||||
{
|
||||
StoryGoalScheduler.main.schedule.Add(goal);
|
||||
}
|
||||
}
|
||||
|
||||
RefreshStoryWithLatestData();
|
||||
}
|
||||
|
||||
// Must happen after CompletedGoals
|
||||
private static void RefreshStoryWithLatestData()
|
||||
{
|
||||
// If those aren't set up yet, they'll initialize correctly in time
|
||||
// Else, we need to force them to acquire the right data
|
||||
if (StoryGoalCustomEventHandler.main)
|
||||
{
|
||||
StoryGoalCustomEventHandler.main.Awake();
|
||||
}
|
||||
if (PrecursorGunStoryEvents.main)
|
||||
{
|
||||
PrecursorGunStoryEvents.main.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetTimeData(InitialPlayerSync packet)
|
||||
{
|
||||
timeManager.ProcessUpdate(packet.TimeData.TimePacket);
|
||||
timeManager.InitRealTimeElapsed(packet.TimeData.TimePacket.RealTimeElapsed, packet.TimeData.TimePacket.UpdateTime, packet.IsFirstPlayer);
|
||||
timeManager.AuroraRealExplosionTime = packet.TimeData.AuroraEventData.AuroraRealExplosionTime;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user