3 using Microsoft.Xna.Framework;
5 using System.Collections.Generic;
6 using System.Collections.Immutable;
25 private readonly Identifier spawnPointTag;
27 private readonly Identifier destructibleItemTag;
29 private readonly
string endCinematicSound;
31 private ImmutableArray<Character> minions;
32 private readonly
int minionCount;
33 private readonly
float minionScatter;
39 private float projectileTimer = 30.0f;
41 private readonly
float startCinematicDistance = 30.0f;
43 private float endCinematicTimer;
45 private readonly List<Item> destructibleItems =
new List<Item>();
53 get {
return destructibleItems.Where(it => it.Condition > 0.0f).Select(it => (
Prefab.SonarLabel, it.WorldPosition)); }
58 get {
return base.State; }
65 OnStateChangedProjSpecific();
66 if (Phase == MissionPhase.AllItemsDestroyed)
68 CoroutineManager.Invoke(() =>
70 if (boss !=
null && !boss.
Removed)
72 Vector2 prevPos = boss.AnimController.Collider.SimPosition;
73 boss.AnimController.ColliderIndex = 1;
74 if (bossSpawnPoint != null)
77 boss.AnimController.Collider.SetTransform(prevPos, 0.0f);
86 private MissionPhase Phase
96 if (state == 0) {
return MissionPhase.Initial; }
97 if (state == 1) {
return MissionPhase.NoItemsDestroyed; }
98 if (state < destructibleItems.Count + 1) {
return MissionPhase.SomeItemsDestroyed; }
99 if (state < destructibleItems.Count + 2) {
return MissionPhase.AllItemsDestroyed; }
100 return MissionPhase.BossKilled;
105 : base(prefab, locations, sub)
108 if (!speciesName.IsEmpty)
111 if (bossPrefab ==
null)
113 DebugConsole.ThrowError($
"Error in end mission \"{prefab.Identifier}\". Could not find a character prefab with the name \"{speciesName}\".",
119 DebugConsole.ThrowError($
"Error in end mission \"{prefab.Identifier}\". Monster file not set.",
124 if (!minionName.IsEmpty)
127 if (minionPrefab ==
null)
129 DebugConsole.ThrowError($
"Error in end mission \"{prefab.Identifier}\". Could not find a character prefab with the name \"{speciesName}\".",
138 if (!projectileId.IsEmpty)
141 if (projectilePrefab ==
null)
143 DebugConsole.ThrowError($
"Error in end mission \"{prefab.Identifier}\". Could not find an item prefab with the name \"{projectileId}\".",
157 if (bossSpawnPoint ==
null)
159 DebugConsole.ThrowError($
"Error in end mission \"{Prefab.Identifier}\". Could not find a spawn point \"{spawnPointTag}\".",
165 boss =
Character.
Create(bossPrefab.Identifier, bossSpawnPoint.WorldPosition, ToolBox.RandomSeed(8), createNetworkEvent:
false);
166 var minionList =
new List<Character>();
168 float angleStep = MathHelper.TwoPi / Math.Max(minionCount, 1);
169 for (
int i = 0; i < minionCount; i++)
171 minionList.Add(
Character.
Create(minionPrefab.Identifier, MathUtils.GetPointOnCircumference(bossSpawnPoint.WorldPosition, minionScatter, angle), ToolBox.RandomSeed(8), createNetworkEvent:
false));
175 minions = minionList.ToImmutableArray();
177 if (destructibleItemTag.IsEmpty)
179 DebugConsole.ThrowError($
"Error in end mission \"{Prefab.Identifier}\". Destructible item tag not set.",
183 destructibleItems.Clear();
184 destructibleItems.AddRange(
Item.
ItemList.FindAll(it => it.HasTag(destructibleItemTag)));
185 if (destructibleItems.None())
187 DebugConsole.ThrowError($
"Error in end mission \"{Prefab.Identifier}\". Could not find any destructible items with the tag \"{spawnPointTag}\".",
195 UpdateProjSpecific();
199 if (startCinematicDistance <= 0.0f ||
208 if (!IsClient && State > 0)
210 State = Math.Max(State, destructibleItems.Count(it => it.Condition <= 0.0f) + 1);
213 if (Phase == MissionPhase.AllItemsDestroyed)
215 if (projectilePrefab !=
null && boss !=
null && !boss.IsDead && !boss.Removed)
217 projectileTimer -= deltaTime;
218 if (projectileTimer <= 0.0f)
221 float distanceFactor = Math.Min(dist / 10000.0f, 1.0f);
222 int projectileAmount = Rand.Range(3, 6);
224 float spread = MathHelper.ToRadians(Rand.Range(20.0f, 180.0f)) * Math.Max(1.0f - distanceFactor, 0.2f);
225 for (
int i = 0; i < projectileAmount; i++)
230 var projectile = it.GetComponent<Projectile>();
231 float angle = MathUtils.VectorToAngle(Submarine.MainSub.WorldPosition - boss.WorldPosition);
232 if (projectileAmount > 1)
234 angle += (index / (float)(projectileAmount - 1) - 0.5f) * spread;
236 it.body.SetTransform(it.SimPosition, angle);
237 it.UpdateTransform();
239 projectile.Use(launchImpulseModifier: MathHelper.Lerp(0, 5, distanceFactor));
244 float shortIntervalProbability = MathHelper.Lerp(0.9f, 0.05f, distanceFactor);
245 if (Rand.Range(0.0f, 1.0f) < shortIntervalProbability)
247 projectileTimer = Rand.Range(3.0f, 5.0f);
251 projectileTimer = Rand.Range(15f, 30f);
257 State = Math.Max(destructibleItems.Count + 2, State);
260 else if (Phase == MissionPhase.BossKilled)
262 const float EndCinematicDuration = 20.0f;
264 endCinematicTimer += deltaTime;
270 Math.Max((endCinematicTimer - 5.0f) * 0.05f, 0.0f)
271 + (PerlinNoise.GetPerlin(endCinematicTimer * 0.1f, endCinematicTimer * 0.05f) - 0.5f) * 0.5f * (endCinematicTimer / EndCinematicDuration);
272 if (Rand.Range(0.0f, 100.0f) < endCinematicTimer)
276 Level.Loaded.Renderer.ChromaticAberrationStrength = endCinematicTimer * 5;
277 Level.Loaded.Renderer.CollapseEffectOrigin = boss.WorldPosition;
278 Level.Loaded.Renderer.CollapseEffectStrength = endCinematicTimer / EndCinematicDuration;
280 if (endCinematicTimer > 5 && !IsClient)
282 foreach (Character c
in Character.CharacterList)
284 if (c.AIController is EnemyAIController enemyAI && enemyAI.PetBehavior ==
null)
286 c.SetAllDamage(200.0f, 0.0f, 0.0f);
291 if (endCinematicTimer > EndCinematicDuration && !IsClient)
294 GameMain.GameSession.Campaign?.LoadNewLevel();
300 partial
void UpdateProjSpecific();
302 partial
void OnStateChangedProjSpecific();
306 return Phase == MissionPhase.BossKilled;
static Character Create(CharacterInfo characterInfo, Vector2 position, string seed, ushort id=Entity.NullEntityID, bool isRemotePlayer=false, bool hasAi=true, RagdollParams ragdoll=null, bool spawnInitialItems=true)
Create a new character
static CharacterPrefab FindBySpeciesName(Identifier speciesName)
string? GetAttributeString(string key, string? def)
float GetAttributeFloat(string key, float def)
int GetAttributeInt(string key, int def)
Identifier GetAttributeIdentifier(string key, string def)
override IEnumerable<(LocalizedString Label, Vector2 Position)> SonarLabels
override void StartMissionSpecific(Level level)
override bool DetermineCompleted()
override void UpdateMissionSpecific(float deltaTime)
readonly float wakeUpCinematicDelay
readonly float bossWakeUpDelay
EndMission(MissionPrefab prefab, Location[] locations, Submarine sub)
readonly float cameraWaitDuration
static EntitySpawner Spawner
void AddItemToSpawnQueue(ItemPrefab itemPrefab, Vector2 worldPosition, float? condition=null, int? quality=null, Action< Item > onSpawned=null)
static readonly List< Item > ItemList
Defines a point in the event that GoTo actions can jump to.
static MapEntityPrefab FindByIdentifier(Identifier identifier)
readonly ContentXElement ConfigElement
ContentPackage? ContentPackage
override Vector2? WorldPosition
static void CreateSwarm(IEnumerable< AICharacter > swarm)
static List< WayPoint > WayPointList