void IGameActor.TakeDamage(float damage, IGameActor attacker) { damagedThisFrame = true; // TODO Loot. CurrentHealth -= damage; if (CurrentHealth <= 0.0f && CurrentAiState == EAiState.WalkingAndFighting) { CurrentAiState = EAiState.Dying; if (isBoss) { CrashdownGameRoot.TotalBossesKilled++; } else { CrashdownGameRoot.TotalEnemiesKilled++; } if (deathEffect != null) { CosmeticEffect.Spawn(deathEffect, deathEffect.defaultLifetime, transform.position, Quaternion.identity); } } else if (CurrentAiState == EAiState.WalkingAndFighting) { if (hurtEffect != null) { CosmeticEffect.Spawn(hurtEffect, hurtEffect.defaultLifetime, transform.position, Quaternion.identity); } } if (CurrentAggroTarget == null) { CurrentAggroTarget = attacker; } RemainingEnrageDuration = enrageDuration; float staggerSign = Mathf.Sign(UnityEngine.Random.Range(-1.0f, 1.0f)); CurrentSidewaysStaggerAmount = enrageSidewaysStaggerSpeed * staggerSign; }
private void Initialize(WeaponDefinition weapon, IGameActor owner, uint projectileId) { MyWeaponData = weapon; MyOwner = owner; RemainingLifetime = weapon.lifetime; RemainingHits = weapon.maximumEnemiesHit; MyId = projectileId; if (weapon.projectileLockedEffect != null) { spawnedEffect = CosmeticEffect.Spawn(weapon.projectileLockedEffect, weapon.lifetime, transform.position, transform.rotation, this.transform); dummyRenderer.SetActive(false); } else { dummyRenderer.SetActive(!weapon.hiddenAttack); } // Don't spawn the actorsHitByProjectile array until it actually hits something. if (weapon.reflectsOtherAttacks) { reflectionCollider.gameObject.SetActive(true); reflectionCollider.radius = weapon.radius; reflectingColliders[reflectionCollider] = MyOwner; } }
private void UpdatePlayers() { Vector3 cameraAveragedTargetPosition = Vector3.zero; int numberOfCameraTargets = 0; // Flatten out inputs relative to the camera. Vector3 inputRight = Camera.main.transform.right; inputRight.y = 0.0f; inputRight = inputRight.normalized; Vector3 inputUp = Camera.main.transform.up; inputUp.y = 0.0f; inputUp = inputUp.normalized; bool allPlayersDead = true; foreach (CrashdownPlayerController player in CrashdownPlayerController.activePlayerInstances) { if (!player.IsDead()) { allPlayersDead = false; bool debugPlayerIsWalkingAround = true; if (debugPlayerIsWalkingAround) { Vector3 playerMovementThisFrame = Vector3.zero; bool isDodging = player.RemainingDodgeTime > 0.0f; if (player.CrashdownTarget.HasValue) { // Don't take any input or do any lateral movement. } else if (!isDodging) { Vector2 input = player.InputMovementThisFrame; // Don't make diagonal walking any faster. if (input.sqrMagnitude > 1.0f) { input = input.normalized; } Vector3 worldspaceInput = inputRight * input.x + inputUp * input.y; if (worldspaceInput != Vector3.zero) { player.CurrentFacing = worldspaceInput.normalized; } playerMovementThisFrame = worldspaceInput * player.GetMaxSpeed() * Time.deltaTime; } else { playerMovementThisFrame = player.CurrentFacing * player.GetDodgeSpeed() * Time.deltaTime; } float previousDodgeTime = player.RemainingDodgeTime; player.RemainingDodgeTime -= Time.deltaTime; if (previousDodgeTime >= -player.playerDodgeRefreshDuration && player.RemainingDodgeTime < -player.playerDodgeRefreshDuration) { player.RemainingNumberOfDodges = player.MaximumNumberOfDodges; if (player.RemainingNumberOfDodges > 0) { CosmeticEffect.Spawn(player.playerDodgeRefreshEffect, player.playerDodgeRefreshEffect.defaultLifetime, player.transform.position, Quaternion.identity, player.transform); } } // Move on the X and Z axes separately so the player can slide along walls. for (int i = 0; i < 2; i++) { Vector3 newPosition; switch (i) { case 0: newPosition = player.transform.position + new Vector3(playerMovementThisFrame.x, 0.0f, 0.0f); break; default: newPosition = player.transform.position + new Vector3(0.0f, 0.0f, playerMovementThisFrame.z); break; } if (playerMovementThisFrame.sqrMagnitude > 0.0f) { bool targetPositionIsOccupied = false; int numberOfThingsInFrontOfMe = Physics.OverlapSphereNonAlloc(newPosition, player.height / 2.0f, cachedColliderHitArray, actorsLayer.value); if (numberOfThingsInFrontOfMe > 0) { for (int q = 0; q < numberOfThingsInFrontOfMe; q++) { Collider possibleBlocker = cachedColliderHitArray[q]; if (actorColliders.TryGetValue(possibleBlocker, out IGameActor blockerActor)) { if (blockerActor is CrashdownEnemyActor && (blockerActor as CrashdownEnemyActor).aiType == CrashdownEnemyActor.EAiType.InanimateObject) { targetPositionIsOccupied = true; if (debugPhysics) { Debug.Log("Player " + player.gameObject.name + " tried to walk into " + (blockerActor as CrashdownEnemyActor).gameObject.name, (blockerActor as CrashdownEnemyActor).gameObject); } break; } } } } bool targetPositionIsOverFloor = Physics.Raycast(newPosition, Vector3.down, out RaycastHit floorHit, player.height * 2.0f, terrainLayer.value); if (!targetPositionIsOccupied) { if (targetPositionIsOverFloor) { newPosition = floorHit.point + Vector3.up * (player.height / 2.0f); player.transform.position = newPosition; if (debugPhysics) { Debug.Log("Player " + player.gameObject.name + " is walking on " + floorHit.collider.gameObject.name + " and moved to " + newPosition, floorHit.collider.gameObject); } } else { // Player tried to walk off an edge, so they should stop and not move there. if (debugPhysics) { Debug.Log("Player " + player.gameObject.name + " tried to walk off an edge.", player.gameObject); } } } } } if (_currentControlScheme == _mouseAndKeyboardScheme) { Vector2 mousePosition = _aimAction.ReadValue <Vector2>(); Plane plane = new Plane(Vector3.up, player.transform.position); float distance; Ray ray = Camera.main.ScreenPointToRay(mousePosition); if (plane.Raycast(ray, out distance)) { var point = ray.GetPoint(distance); var vector = point - player.transform.position; player.CurrentAiming = vector.normalized; } else { player.CurrentAiming = player.CurrentFacing; } } else { player.CurrentAiming = player.CurrentFacing; } player.UpdateFacingAndRenderer(); // Player Attacks if (player.RemainingWeaponCooldown <= 0.0f && player.InputAttackDownThisFrame && player.TryGetCurrentWeapon(out WeaponDefinition weapon)) { // TODO Cooldowns and so on. ActorUsesWeapon(player, weapon, projectilePrefab); player.RemainingWeaponCooldown = weapon.cooldown; } else { player.RemainingWeaponCooldown -= Time.deltaTime; } // Player Dodges if (player.InputDodgeDownThisFrame) { bool canDodge = true; if (player.IsDodging() || player.RemainingNumberOfDodges <= 0) { canDodge = false; } if (canDodge) { player.RemainingDodgeTime = player.GetDodgeDuration(); player.StartedDodgingThisFrame = true; player.RemainingNumberOfDodges--; CosmeticEffect.Spawn(player.playerDodgeEffect, player.playerDodgeEffect.defaultLifetime, player.transform.position, player.transform.rotation); } } // Player Crashdown if (player.CrashdownTarget.HasValue) { const float kDefaultCrashdownDuration = 1.5f; if (player.CurrentCrashdownTime <= kDefaultCrashdownDuration) { // Crashdown Update Tick player.CurrentCrashdownTime += Time.deltaTime; const float kCrashdownPhaseOneDuration = 1.0f; const float kPlayerRiseDuringCrashdown = 20.0f; if (player.CurrentCrashdownTime < kCrashdownPhaseOneDuration) { player.transform.position += Vector3.up * Time.deltaTime * kPlayerRiseDuringCrashdown / kCrashdownPhaseOneDuration; } else { float distanceToMoveThisFrame = (CrashdownLevelParent.kExpectedDistanceBetweenFloors + kPlayerRiseDuringCrashdown) / (kDefaultCrashdownDuration - kCrashdownPhaseOneDuration) * Time.deltaTime; player.transform.position = Vector3.MoveTowards(player.transform.position, player.CrashdownTarget.Value, distanceToMoveThisFrame);; } } else { // Crashdown Exit player.transform.position = player.CrashdownTarget.Value + Vector3.up * player.height / 2.0f; ActorUsesWeapon(player, player.crashdownSmashWeapon, projectilePrefab); // spawn here. CosmeticEffect.Spawn(crashdownCosmeticEffect, 2, player.transform.position, Quaternion.identity); player.CrashdownTarget = null; foreach (GameObject o in DisposeOnLevelChange) { if (o != null) { o.SetActive(false); } } DisposeOnLevelChange.Clear(); float levelCutoff = player.transform.position.y + CrashdownLevelParent.kExpectedDistanceBetweenFloors / 2.0f; while (CrashdownLevelParent.activeCrashdownLevels.Count > 0 && CrashdownLevelParent.activeCrashdownLevels.Values[0].transform.position.y > levelCutoff) { CrashdownLevelParent.activeCrashdownLevels.Values[0].Dispose(); } } } else if (player.HasCrashdownAttack) { if (player.InputCrashdownDownThisFrame) { if (Physics.Raycast(player.transform.position + Vector3.down * player.height * 2.0f, Vector3.down, out RaycastHit raycastHit, CrashdownLevelParent.kExpectedDistanceBetweenFloors * 1.5f, terrainLayer.value)) { // Crashdown Start player.StartedCrashdownThisFrame = true; Vector3 targetPoint = raycastHit.point; player.CrashdownTarget = targetPoint; player.CurrentCrashdownTime = 0.0f; player.HasCrashdownAttack = false; crashdownPromptRoot.SetActive(false); player.CurrentFacing = Vector3.back; AudioManager.instance.PlaySound(crashdownStartToFinishSound, player.transform.position); } else { AudioManager.instance.PlaySound(sound_UiFailToCrashdown, player.transform.position); } } _currentCrashdownPromptFlash = Mathf.Repeat(_currentCrashdownPromptFlash + Time.deltaTime, 1.0f); crashdownText.color = crashdownTextColorGradient.Evaluate(_currentCrashdownPromptFlash); // Force the player to use the ability by slowly draining their health while they have it. player.CurrentHealth -= player.crashdownHealthDrainPerSecond * Time.deltaTime; player.CurrentHealthRegenDelay = 1.0f; } // Player Interactions & Secret Areas int numberOfInteractions = Physics.OverlapSphereNonAlloc(player.transform.position, player.height / 2.0f, cachedColliderHitArray, interactionsLayer.value); if (numberOfInteractions > 0) { // Only handle the first interaction, overlapping could get messy. bool hasFoundAnInteraction = false; for (int interactionIndex = 0; interactionIndex < numberOfInteractions; interactionIndex++) { Collider thisInteractionCollider = cachedColliderHitArray[interactionIndex]; if (!hasFoundAnInteraction && PlayerInteraction.activeInteractions.TryGetValue(thisInteractionCollider, out PlayerInteraction thisInteraction)) { thisInteraction.OnPlayerStaysThisFrame(); hasFoundAnInteraction = true; if (player.InputInteractDownThisFrame) { switch (thisInteraction.interactionType) { case PlayerInteraction.EInteractionType.HealthPowerUp: player.MaxHealth *= player.playerHealthBoostMultiplier; player.CurrentHealth = player.MaxHealth; float playerHealthRatio = player.MaxHealth / player.playerStartingHealth; AudioManager.instance.PlaySound(getPowerupSound, player.transform.position); break; case PlayerInteraction.EInteractionType.WeaponPickup: player.SetCurrentWeapon(thisInteraction.weaponDefinition); FinalWeaponUsed = thisInteraction.weaponDefinition?.hudAndHighScoreName; currentWeaponSprites[0].sprite = thisInteraction.weaponDefinition.pickupAndHudSprite; currentWeaponSprites[0].color = Color.white; AudioManager.instance.PlaySound(getPowerupSound, player.transform.position); break; case PlayerInteraction.EInteractionType.DodgePowerUp: player.MaximumNumberOfDodges++; player.RemainingNumberOfDodges = player.MaximumNumberOfDodges; AudioManager.instance.PlaySound(getPowerupSound, player.transform.position); break; case PlayerInteraction.EInteractionType.CrashdownKey: player.HasCrashdownAttack = true; player.CurrentHealth = player.MaxHealth; // Fully heal the player so the key can't instakill them. AudioManager.instance.PlaySound(getPowerupSound, player.transform.position); crashdownPromptRoot.gameObject.SetActive(true); break; case PlayerInteraction.EInteractionType.WinTheGame: AudioManager.instance.PlaySound(getPowerupSound, player.transform.position); AudioManager.instance.PlaySound(gameGlitchSound, player.transform.position); nextSceneIndexToLoad = thisInteraction.targetSceneIndex; glitchRenderer.enabled = true; glitchRenderer.material = glitchRendererStages[0]; break; case PlayerInteraction.EInteractionType.Nothing: // This object is not interactable, but it can show a tutorial text message when the player is near it. break; case PlayerInteraction.EInteractionType.ToggleSomething: if (thisInteraction.interactionCoolDown <= 0f) { thisInteraction.interactionCoolDown = buttonInteractCoolDown; AudioManager.instance.PlaySound(buttonPressSound, player.transform.position); foreach (GameObject thing in thisInteraction.objectsToToggle) { bool toggle = thing.activeInHierarchy; thing.SetActive(!toggle); } } break; default: Debug.LogError("TODO: " + thisInteraction.interactionType.ToString()); break; } if (thisInteraction.removeAfterActivation) { GameObject.Destroy(thisInteraction.gameObject); } else { thisInteraction.interactedWithThisFrame = true; } } } else if (SecretAreaTrigger.activeSecretAreas.TryGetValue(thisInteractionCollider, out SecretAreaTrigger secretArea)) { // But do check for all secret areas, don't want to miss one because you landed on a weapon but skipped it. // Trigger Secret Areas On Enter if (!SecretAreasFound.Contains(secretArea)) { SecretAreasFound.Add(secretArea); } } } } // Player Health Regen if (player.CurrentHealthRegenDelay <= 0.0f) { if (player.CurrentHealth < player.MaxHealth) { float regenThisFrame = player.MaxHealth / player.playerFullRegenWait * Time.deltaTime; if (debugCombat) { Debug.Log("Player is regenerating " + regenThisFrame); } player.CurrentHealth = Mathf.Min(player.MaxHealth, player.CurrentHealth + regenThisFrame); } } else { player.CurrentHealthRegenDelay -= Time.deltaTime; if (debugCombat) { Debug.Log("Player has " + player.CurrentHealthRegenDelay + " seconds until they begin to regenerate."); } } } cameraAveragedTargetPosition += player.transform.position; numberOfCameraTargets++; } //player.InputAttackDownThisFrame = false; //player.InputDodgeDownThisFrame = false; //player.InputCrashdownDownThisFrame = false; //player.InputInteractDownThisFrame = false; float playerHealthAmount = player.CurrentHealth / player.MaxHealth; playerHealthBar.SetMaxHealth((int)player.MaxHealth); playerHealthBar.SetHealth((int)player.CurrentHealth); if (player.HasCrashdownAttack) { playerHealthBar.SetColor(crashdownText.color = crashdownTextColorGradient.Evaluate(_currentCrashdownPromptFlash)); } }