/// <summary> /// Mage player's secondary attack /// </summary> protected override void SecondaryAttack() { // Ensure player has enough mana to perform this attack if (AttackCooldown > 0 || (Stats as PlayerStatsController).mana.CurrentValue < secondaryAttackManaDepletion) { return; } StartCoroutine(VfxHelper.CreateVFX(secondaryVFX, transform.position + new Vector3(0, 0.01f, 0), Quaternion.identity, PlayerManager.colours.GetColour(Stats.characterColour))); // Check all enemies within attack radius of the player List <Transform> enemies = GetSurroundingEnemies(secondaryAttackRadius); // Attack any enemies within the AOE range foreach (var enemy in enemies) { StartCoroutine(PerformExplosiveDamage(enemy.GetComponent <EntityStatsController>(), secondaryAttackMaxDamage, secondaryAttackStunTime, secondaryAttackExplosionForce, transform.position, secondaryAttackRadius, secondaryAttackDamageDelay)); } // Trigger secondary attack animation Anim.SetTrigger("SecondaryAttack"); // Play the attack audio StartCoroutine(AudioHelper.PlayAudioOverlap(WeaponAudio, secondaryAttackWeaponSFX)); // Reset attack timeout and deplete mana AttackCooldown = secondaryAttackTimeout; (Stats as PlayerStatsController).mana.Subtract(secondaryAttackManaDepletion); // Apply movement speed modifier StartCoroutine(Motor.ApplyTimedMovementModifier(secondaryAttackMovementModifier, secondaryAttackTimeout)); }
/// <summary> /// Lich's spell cast attack /// </summary> public override void SecondaryAttack() { // Calculate direction to attack in relative to staff position Vector3 direction = secondaryAttackVFXRoot.up; direction.y = 0f; Vector3 position = secondaryAttackVFXRoot.position; position.y = 1f; // Spawn VFX from staff and in the direction its pointing Vector3 vfxPos = position + direction * 0.5f + new Vector3(0, 2f); StartCoroutine(VfxHelper.CreateVFX(secondaryAttackVFX, vfxPos, Quaternion.LookRotation(direction))); // Calculate the hit raycast from closer to the enemy, in the direction of the staff Vector3 raycastPos = position - direction * 2.5f; int numHits = Physics.RaycastNonAlloc(raycastPos, direction, _hitBuffer, secondaryAttackRadius); float damageValue = Random.Range(secondaryAttackMinDamage, secondaryAttackMaxDamage) + Stats.damage.GetValue(); // Damage any players hit by the raycast for (int i = 0; i < numHits; i++) { if (_hitBuffer[i].transform.tag.Equals("Player")) { _hitBuffer[i].transform.GetComponent <EntityStatsController>().TakeDamage(Stats, damageValue, Time.deltaTime); } } }
public static void Postfix(CombatHUDStatusPanel __instance, List <CombatHUDStatusIndicator> ___Buffs, List <CombatHUDStatusIndicator> ___Debuffs) { Mod.Log.Trace?.Write("CHUDSP:RDC - entered."); if (__instance != null && __instance.DisplayedCombatant != null) { AbstractActor target = __instance.DisplayedCombatant as AbstractActor; // We can receive a building here, so if (target != null) { if (target.Combat.HostilityMatrix.IsLocalPlayerEnemy(target.team)) { SensorScanType scanType = SensorLockHelper.CalculateSharedLock(target, ModState.LastPlayerActorActivated); // Hide the buffs and debuffs if the current scanType is less than allInfo if (scanType < SensorScanType.AllInformation) { //// Hide the buffs and debuffs ___Buffs.ForEach(si => si.gameObject.SetActive(false)); ___Debuffs.ForEach(si => si.gameObject.SetActive(false)); } } // Calculate stealth pips Traverse stealthDisplayT = Traverse.Create(__instance).Field("stealthDisplay"); CombatHUDStealthBarPips stealthDisplay = stealthDisplayT.GetValue <CombatHUDStealthBarPips>(); VfxHelper.CalculateMimeticPips(stealthDisplay, target); } } }
public static void Postfix(MechComponent __instance, bool __state) { Mod.Log.Trace?.Write("MC:CCE:post entered."); if (__state) { Mod.Log.Debug?.Write($" Stealth effect was cancelled, parent visibility needs refreshed."); EWState parentState = new EWState(__instance.parent); PilotableActorRepresentation par = __instance.parent.GameRep as PilotableActorRepresentation; if (parentState.HasStealth()) { VfxHelper.EnableStealthVfx(__instance.parent); } else { VfxHelper.DisableSensorStealthEffect(__instance.parent); } if (parentState.HasMimetic()) { VfxHelper.EnableMimeticEffect(__instance.parent); } else { VfxHelper.DisableMimeticEffect(__instance.parent); } // Force a refresh in case the signature increased due to stealth loss // TODO: Make this player hostile only List <ICombatant> allLivingCombatants = __instance.parent.Combat.GetAllLivingCombatants(); __instance.parent.VisibilityCache.UpdateCacheReciprocal(allLivingCombatants); } }
/// <summary> /// Melee's secondary attack /// </summary> protected override void SecondaryAttack() { // Ensure player has enough mana to perform this attack if (AttackCooldown > 0 || (Stats as PlayerStatsController).mana.CurrentValue < secondaryAttackManaDepletion) { return; } StartCoroutine(VfxHelper.CreateVFX(secondaryVFX, transform.position + new Vector3(0, 0.01f, 0), transform.rotation, PlayerManager.colours.GetColour(Stats.characterColour), secondaryAttackDamageDelay)); // Check all enemies within attack radius of the player List <Transform> enemies = GetSurroundingEnemies(secondaryAttackRadius); // Attack any enemies within the attack sweep and range foreach (var enemy in enemies.Where(enemy => CanDamageTarget(enemy, secondaryAttackRadius, secondaryAttackSweepAngle))) { // Calculate and perform damage float damageValue = Random.Range(secondaryAttackMinDamage, secondaryAttackMaxDamage + Stats.damage.GetValue()); StartCoroutine(PerformDamage(enemy.GetComponent <EntityStatsController>(), damageValue, secondaryAttackDamageDelay)); } // Trigger secondary attack animation Anim.SetTrigger("SecondaryAttack"); // Play the attack audio StartCoroutine(AudioHelper.PlayAudioOverlap(WeaponAudio, secondaryAttackWeaponSFX)); // Reset attack timeout and deplete mana AttackCooldown = secondaryAttackTimeout; (Stats as PlayerStatsController).mana.Subtract(secondaryAttackManaDepletion); // Apply movement speed modifier StartCoroutine(Motor.ApplyTimedMovementModifier(secondaryAttackMovementModifier, secondaryAttackTimeout)); }
/// <summary> /// Mage player's primary attack /// </summary> protected override void PrimaryAttack() { // Ensure player has enough mana to perform this attack if ((Stats as PlayerStatsController).mana.CurrentValue < 1f) { // Stop attack if not enough mana ReleaseChargedAttack(); return; } (Stats as PlayerStatsController).mana.Subtract(primaryAttackManaDepletion * Time.deltaTime); Vector3 vfxPos = transform.position + transform.forward * 1.6f + new Vector3(0, 2f); StartCoroutine(VfxHelper.CreateVFX(primaryVFX, vfxPos, transform.rotation)); // Check all enemies within attack radius of the player List <Transform> enemies = GetSurroundingEnemies(primaryAttackRadius); float baseDamage = Stats.damage.GetValue(); float damageValue = Random.Range(primaryAttackMinDamage + baseDamage, primaryAttackMaxDamage + baseDamage); // Attack any enemies within the attack sweep and range foreach (var enemy in enemies.Where(enemy => CanDamageTarget(enemy, primaryAttackRadius, primaryAttackSweepAngle))) { enemy.GetComponent <EntityStatsController>().TakeDamage(Stats, damageValue, Time.deltaTime); } }
/// <summary> /// Mage player's primary attack /// </summary> protected override void PrimaryAttack() { // Ensure player has enough mana to perform this attack if ((Stats as PlayerStatsController).mana.CurrentValue < 1f) { // Stop attack if not enough mana _isPrimaryActive = false; Anim.SetBool("PrimaryAttack", false); AudioHelper.StopAudio(WeaponAudio); (Stats as PlayerStatsController).mana.StartRegen(); Motor.ResetMovementModifier(); return; } (Stats as PlayerStatsController).mana.Subtract(primaryAttackManaDepletion * Time.deltaTime); ResetTakeDamageAnim(); Vector3 vfxPos = transform.position + transform.forward * 1.5f + new Vector3(0, 2f); StartCoroutine(VfxHelper.CreateVFX(primaryVFX, vfxPos, transform.rotation)); // Check all enemies within attack radius of the player List <Transform> enemies = GetSurroundingEnemies(primaryAttackRadius); // Attack any enemies within the attack sweep and range foreach (var enemy in enemies.Where(enemy => CanDamageTarget(enemy, primaryAttackRadius, primaryAttackSweepAngle))) { float damageValue = Random.Range(primaryAttackMinDamage, primaryAttackMaxDamage + Stats.damage.GetValue()); enemy.GetComponent <EntityStatsController>().TakeDamage(Stats, damageValue, Time.deltaTime); } }
public static void Prefix(TurnDirector __instance) { Mod.Log.Trace?.Write("TD:OEB:pre entered."); // Initialize the probabilities ModState.InitializeCheckResults(); ModState.InitMapConfig(); ModState.TurnDirectorStarted = true; // Do a pre-encounter populate if (__instance != null && __instance.Combat != null && __instance.Combat.AllActors != null) { // If we are coming from a save, don't recalculate everything - just roll with what we already have if (!IsFromSave) { AbstractActor randomPlayerActor = null; foreach (AbstractActor actor in __instance.Combat.AllActors) { if (actor != null) { // Make a pre-encounter detectCheck for them ActorHelper.UpdateSensorCheck(actor, false); bool isPlayer = actor.TeamId == __instance.Combat.LocalPlayerTeamGuid; if (isPlayer && randomPlayerActor == null) { randomPlayerActor = actor; } } else { Mod.Log.Debug?.Write($" Actor:{CombatantUtils.Label(actor)} was NULL!"); } } if (randomPlayerActor != null) { Mod.Log.Debug?.Write($"Assigning actor: {CombatantUtils.Label(randomPlayerActor)} as lastActive."); ModState.LastPlayerActorActivated = randomPlayerActor; } } } // Initialize the VFX materials // TODO: Do a pooled instantiate here? VfxHelper.Initialize(__instance.Combat); // Attach to the message bus so we get updates on selected actor SelectedActorHelper.Combat = __instance.Combat; __instance.Combat.MessageCenter.Subscribe(MessageCenterMessageType.ActorSelectedMessage, new ReceiveMessageCenterMessage(SelectedActorHelper.OnActorSelectedMessage), true); __instance.Combat.MessageCenter.Subscribe(MessageCenterMessageType.OnAuraAdded, new ReceiveMessageCenterMessage(SelectedActorHelper.OnAuraAddedMessage), true); __instance.Combat.MessageCenter.Subscribe(MessageCenterMessageType.OnAuraRemoved, new ReceiveMessageCenterMessage(SelectedActorHelper.OnAuraRemovedMessage), true); }
public static void Postfix(CombatHUDStatusPanel __instance, AbstractActor target, float previewStealth, CombatHUDStealthBarPips ___stealthDisplay) { if (___stealthDisplay == null) { return; } Mod.Log.Trace?.Write("CHUDSP:SSI:float - entered."); VfxHelper.CalculateMimeticPips(___stealthDisplay, target); }
public static void Postfix(CombatHUDActorInfo __instance, MessageCenterMessage message, AbstractActor ___displayedActor) { Mod.Log.Trace?.Write("CHUDAI:OSC entered"); StealthChangedMessage stealthChangedMessage = message as StealthChangedMessage; if (___displayedActor != null && stealthChangedMessage.affectedObjectGuid == ___displayedActor.GUID && __instance.StealthDisplay != null) { VfxHelper.CalculateMimeticPips(__instance.StealthDisplay, ___displayedActor); } }
public static void Prefix(AbstractActor __instance) { Mod.Log.Trace?.Write("AA:OnAEnd - entered."); if (__instance != null) { // Disable night vision if (ModState.IsNightVisionMode) { VfxHelper.DisableNightVisionEffect(); } } }
public static void Postfix(CombatHUDActorInfo __instance, AbstractActor ___displayedActor) { Mod.Log.Trace?.Write("CHUDAI:RAI entered"); if (___displayedActor == null || ModState.LastPlayerActorActivated == null) { return; } if (__instance.StealthDisplay != null) { VfxHelper.CalculateMimeticPips(__instance.StealthDisplay, ___displayedActor); } }
private void Start() { // Assign enemy a colour if (characterColour == CharacterColour.None) { AssignEnemyColour(characterColour); } // Create a VFX where the enemy will spawn - just slightly above the stage (0.1f) - and change the VFX colour to match the enemy colour StartCoroutine(VfxHelper.CreateVFX(spawnVFX, transform.position + new Vector3(0, 0.01f, 0), Quaternion.identity, PlayerManager.colours.GetColour(characterColour), 0.5f)); // "Spawn" the enemy (they float up through the stage) StartCoroutine(Spawn(gameObject, spawnSpeed, spawnDelay, spawnCooldown)); }
private void Start() { Color playerColour = PlayerManager.colours.GetColour(characterColour); // colour the player's weapon AssignWeaponColour(gameObject, playerColour); if (shouldSpawn) { // Create a VFX where the player will spawn - just slightly above the stage (0.1f) - and change the VFX colour to match the player colour StartCoroutine(VfxHelper.CreateVFX(spawnVFX, transform.position + new Vector3(0, 0.01f, 0), Quaternion.identity, playerColour, 0.5f)); // "Spawn" the player (they float up through the stage) StartCoroutine(Spawn(gameObject, spawnSpeed, spawnDelay, spawnCooldown)); } }
private void Start() { playerInput = GetComponent <PlayerInput>(); Color playerColour = PlayerManager.colours.GetColour(characterColour); // colour the player's weapon AssignWeaponColour(gameObject, playerColour); // Scale player damage based on number of levels cleared damage.AddModifier(LevelManager.Instance.GetNumLevelsCleared()); if (shouldSpawn) { // Create a VFX where the player will spawn - just slightly above the stage (0.1f) - and change the VFX colour to match the player colour StartCoroutine(VfxHelper.CreateVFX(spawnVFX, transform.position + new Vector3(0, 0.01f, 0), Quaternion.identity, playerColour, 0.5f)); // "Spawn" the player (they float up through the stage) StartCoroutine(Spawn(gameObject, spawnSpeed, spawnDelay, spawnCooldown)); } }
public static void Prefix(AbstractActor __instance, int stackItemID) { if (stackItemID == -1 || __instance == null || __instance.HasBegunActivation) { // For some bloody reason DoneWithActor() invokes OnActivationBegin, EVEN THOUGH IT DOES NOTHING. GAH! return; } // Draw stealth if applicable EWState actorState = new EWState(__instance); if (actorState.HasStealth()) { Mod.Log.Debug?.Write($"-- Sending message to update stealth"); StealthChangedMessage message = new StealthChangedMessage(__instance.GUID); __instance.Combat.MessageCenter.PublishMessage(message); } // If friendly, reset the map visibility if (__instance.TeamId != __instance.Combat.LocalPlayerTeamGuid && __instance.Combat.HostilityMatrix.IsLocalPlayerFriendly(__instance.TeamId)) { Mod.Log.Info?.Write($"{CombatantUtils.Label(__instance)} IS FRIENDLY, REBUILDING FOG OF WAR"); if (actorState.HasNightVision() && ModState.GetMapConfig().isDark) { Mod.Log.Info?.Write($"Enabling night vision mode."); VfxHelper.EnableNightVisionEffect(__instance); } else { // TODO: This is likely never triggered due to the patch below... remove? if (ModState.IsNightVisionMode) { VfxHelper.DisableNightVisionEffect(); } } VfxHelper.RedrawFogOfWar(__instance); } }
protected override void Update() { base.Update(); // Handle attack charge ups if (_isPrimaryCharging) { _primaryChargeTime += Time.deltaTime; if (_primaryChargeTime >= primaryAttackChargeTime) { if (!_sparkleShown) { StartCoroutine(VfxHelper.CreateVFX(sparkleVFX, transform.position + transform.forward * 1.6f + new Vector3(0, 2f, 0), transform.rotation)); StartCoroutine(AudioHelper.PlayAudioOverlap(WeaponAudio, sparkleSFX)); _sparkleShown = true; } // Clamp to max charge time _primaryChargeTime = primaryAttackChargeTime; } } }
/// <summary> /// Sets the colour of the orb to the set colour when enabled /// </summary> private void OnEnable() { if (LaunchedByPlayer) { return; } // Select an orb colour randomly List <CharacterColour> orbColours = (LauncherStats as RhakStatsController).OrbsRequired; _colour = orbColours[Random.Range(0, orbColours.Count)]; Color orbColour = PlayerManager.colours.GetColour(_colour); // Set the colour of the particle systems to the orb colour foreach (ParticleSystem p in colourParticleSystems) { VfxHelper.SetParticleSystemColour(p, orbColour); } // Set interactable colour _interactable.colour = _colour; }
/// <summary> /// Enemy boss's tertiary attack /// </summary> public override void TertiaryAttack() { if (AttackCooldown > 0f) { return; } Anim.SetTrigger("TertiaryAttack"); // audio StartCoroutine(AudioHelper.PlayAudioOverlap(WeaponAudio, tertiaryAttackSFX)); StartCoroutine(VfxHelper.CreateVFX(tertiaryAttackVFX, gameObject.transform.position + new Vector3(0, 1f, 0), gameObject.transform.rotation, PlayerManager.colours.GetColour(Stats.characterColour), tertiaryAttackDelay * 0.5f)); // Attack any enemies within the attack sweep and range foreach (GameObject player in Players.Where(player => CanDamageTarget(player.transform.position, attackRadius, attackAngle))) { // Calculate and perform damage StartCoroutine(PerformDamage(player.GetComponent <EntityStatsController>(), Stats.ComputeDamageModifer(), tertiaryAttackDelay * 0.5f)); } AttackCooldown = tertiaryAttackCooldown; }
private void UpdateRespawnPercentage(bool playersOnBeacon) { if (playersOnBeacon) { respawnPercentage += respawnRate; if (respawnPercentage >= 100f) { playerStatsController.Respawn(); StartCoroutine(VfxHelper.CreateVFX(respawnEffectPrefab, transform.position + new Vector3(0, 0.01f, 0), Quaternion.identity, PlayerManager.colours.GetColour(playerStatsController.characterColour))); Destroy(gameObject); } } else { respawnPercentage = Mathf.Max(0, respawnPercentage - 2f * respawnRate); if (respawnPercentage <= 0) { text.text = reviveText; return; } } text.text = respawnPercentage.ToString("N0") + "%"; }
public static void Postfix(PilotableActorRepresentation __instance, VisibilityLevel newLevel) { Mod.Log.Trace?.Write("PAR:OPVC entered."); Traverse parentT = Traverse.Create(__instance).Property("parentActor"); AbstractActor parentActor = parentT.GetValue <AbstractActor>(); if (parentActor == null) { Mod.Log.Trace?.Write($"ParentActor is null, skipping!"); return; } EWState parentState = new EWState(parentActor); if (newLevel == VisibilityLevel.LOSFull) { if (parentState.HasStealth()) { VfxHelper.EnableStealthVfx(parentActor); } else { VfxHelper.DisableSensorStealthEffect(parentActor); } if (parentState.HasMimetic()) { VfxHelper.EnableMimeticEffect(parentActor); } else { VfxHelper.DisableMimeticEffect(parentActor); } __instance.BlipObjectUnknown.SetActive(false); __instance.BlipObjectUnknown.SetActive(false); } else if (newLevel >= VisibilityLevel.Blip0Minimum) { Mod.Log.Debug?.Write($"Actor: {CombatantUtils.Label(parentActor)} has changed player visibility to: {newLevel}"); if (parentActor.team.IsFriendly(parentActor.Combat.LocalPlayerTeam)) { Mod.Log.Debug?.Write($" Target actor is friendly, forcing blip off"); __instance.BlipObjectUnknown.SetActive(false); } else { // Because Blip1 corresponds to ArmorAndWeapon, go ahead and show the model as the chassis is 'known' if (newLevel >= VisibilityLevel.Blip1Type) { Mod.Log.Debug?.Write($" Actor is a foe, disabling the identified blip and showing the object"); __instance.VisibleObject.SetActive(true); __instance.BlipObjectIdentified.SetActive(false); __instance.BlipObjectUnknown.transform.localScale = new Vector3(1f, 0.8f, 1f); __instance.BlipObjectUnknown.SetActive(true); } } } }