private void FireBullet(RigidBody body, Transform transform, Vector2 localPos, float localAngle) { ShipBlueprint blueprint = this.blueprint.Res; if (blueprint.BulletType == null) { return; } Bullet bullet = blueprint.BulletType.Res.CreateBullet(); Vector2 recoilImpulse; Vector2 worldPos = transform.GetWorldPoint(localPos); bullet.Fire(this.owner, body.LinearVelocity, worldPos, transform.Angle + localAngle, out recoilImpulse); body.ApplyWorldImpulse(recoilImpulse); Scene.Current.AddObject(bullet.GameObj); SoundInstance inst = null; if (Player.AlivePlayers.Count() > 1) { inst = DualityApp.Sound.PlaySound3D(this.owner.WeaponSound, new Vector3(worldPos)); } else { inst = DualityApp.Sound.PlaySound(this.owner.WeaponSound); } inst.Volume = MathF.Rnd.NextFloat(0.6f, 1.0f); inst.Pitch = MathF.Rnd.NextFloat(0.9f, 1.11f); }
public void Die() { // Ignore, if already dead if (this.isDead) { return; } this.isDead = true; // Notify everyone who is interested that we're dead this.SendMessage(new ShipDeathMessage()); // Spawn death effects ShipBlueprint blueprint = this.blueprint.Res; if (blueprint.DeathEffects != null) { Transform transform = this.GameObj.Transform; RigidBody body = this.GameObj.GetComponent <RigidBody>(); for (int i = 0; i < blueprint.DeathEffects.Length; i++) { Prefab deathEffectPrefab = blueprint.DeathEffects[i].Res; GameObject effectObj = deathEffectPrefab.Instantiate(transform.Pos); ParticleEffect effect = effectObj.GetComponent <ParticleEffect>(); if (effect != null && this.owner != null) { foreach (ParticleEmitter emitter in effect.Emitters) { if (emitter == null) { continue; } emitter.MaxColor = this.owner.Color.ToHsva().WithValue(emitter.MaxColor.V); emitter.MinColor = this.owner.Color.ToHsva().WithValue(emitter.MinColor.V); emitter.BaseVel += new Vector3(body.LinearVelocity); } } Scene.Current.AddObject(effectObj); } } // Safely dispose the ships GameObject and set hitpoints to zero this.hitpoints = 0.0f; if (this.owner != null) { this.GameObj.Active = false; } else { this.GameObj.DisposeLater(); } }
void ICmpUpdatable.OnUpdate() { Transform transform = this.GameObj.Transform; RigidBody body = this.GameObj.GetComponent <RigidBody>(); ShipBlueprint blueprint = this.blueprint.Res; // Heal when damaged if (this.hitpoints < 1.0f) { this.hitpoints = MathF.Clamp(this.hitpoints + blueprint.HealRate * Time.SPFMult * Time.TimeMult / blueprint.MaxHitpoints, 0.0f, 1.0f); } // Apply force according to the desired thrust Vector2 actualVelocity = body.LinearVelocity; Vector2 targetVelocity = this.targetThrust * blueprint.MaxSpeed; Vector2 velocityDiff = (targetVelocity - actualVelocity); float sameDirectionFactor = Vector2.Dot( velocityDiff / MathF.Max(0.001f, velocityDiff.Length), this.targetThrust / MathF.Max(0.001f, this.targetThrust.Length)); Vector2 thrusterActivity = this.targetThrust.Length * MathF.Max(sameDirectionFactor, 0.0f) * velocityDiff / MathF.Max(velocityDiff.Length, 1.0f); if (thrusterActivity.Length > 0.00001f) // Don't wake physics without actually doing work { body.ApplyWorldForce(thrusterActivity * blueprint.ThrusterPower); } // Turn to the desired fire angle if (this.targetAngleRatio > 0.0f) { float shortestTurnDirection = MathF.TurnDir(transform.Angle, this.targetAngle); float shortestTurnLength = MathF.CircularDist(transform.Angle, this.targetAngle); float turnDirection; float turnLength; if (MathF.Abs(body.AngularVelocity) > blueprint.MaxTurnSpeed * 0.25f) { turnDirection = MathF.Sign(body.AngularVelocity); turnLength = (turnDirection == shortestTurnDirection) ? shortestTurnLength : (MathF.RadAngle360 - shortestTurnLength); } else { turnDirection = shortestTurnDirection; turnLength = shortestTurnLength; } float turnSpeedRatio = MathF.Min(turnLength * 0.25f, MathF.RadAngle30) / MathF.RadAngle30; float turnVelocity = turnSpeedRatio * turnDirection * blueprint.MaxTurnSpeed * this.targetAngleRatio; float angularVelocityChange = (turnVelocity - body.AngularVelocity) * blueprint.TurnPower; if (MathF.Abs(angularVelocityChange) > 0.0000001f) // Don't wake physics without actually doing work { body.AngularVelocity += angularVelocityChange * Time.TimeMult; } } // Weapon cooldown this.weaponTimer = MathF.Max(0.0f, this.weaponTimer - Time.MsPFMult * Time.TimeMult); // Play the owners special flight sound, when available if (this.owner != null && this.owner.FlightLoop != null) { SoundListener listener = Scene.Current.FindComponent <SoundListener>(); Vector3 listenerPos = listener.GameObj.Transform.Pos; // Determine the target panning manually, because we don't want a true 3D sound here (doppler, falloff, ...) float targetPanning; if (listenerPos.Xy == transform.Pos.Xy || Player.AlivePlayers.Count() <= 1) { targetPanning = 0.0f; } else { targetPanning = -Vector2.Dot(Vector2.UnitX, (listenerPos - transform.Pos).Xy.Normalized); } // Determine the target volume float targetVolume = MathF.Clamp(this.targetThrust.Length, 0.0f, 1.0f); // Clean up disposed flight loop if (this.flightLoop != null && this.flightLoop.Disposed) { this.flightLoop = null; } // Start the flight loop when requested if (targetVolume > 0.0f && this.flightLoop == null) { if ((int)Time.MainTimer.TotalMilliseconds % 2976 <= (int)Time.MsPFMult) { this.flightLoop = DualityApp.Sound.PlaySound(this.owner.FlightLoop); this.flightLoop.Looped = true; } } // Configure existing flight loop if (this.flightLoop != null) { this.flightLoop.Volume += (targetVolume - this.flightLoop.Volume) * 0.05f * Time.TimeMult; this.flightLoop.Panning += (targetPanning - this.flightLoop.Panning) * 0.05f * Time.TimeMult; } } // Display the damage effect when damaged if (this.hitpoints < 0.85f && blueprint.DamageEffect != null) { // Create a new damage effect instance, if not present yet if (this.damageEffect == null) { GameObject damageObj = blueprint.DamageEffect.Res.Instantiate(transform.Pos); damageObj.Parent = this.GameObj; this.damageEffect = damageObj.GetComponent <ParticleEffect>(); if (this.damageEffect == null) { throw new NullReferenceException(); } } // Configure the damage effect foreach (ParticleEmitter emitter in this.damageEffect.Emitters) { if (emitter == null) { continue; } emitter.BurstDelay = new Range(50.0f + this.hitpoints * 50.0f, 100.0f + this.hitpoints * 300.0f); if (this.owner != null) { ColorHsva targetColor = this.owner.Color.ToHsva(); emitter.MinColor = new ColorHsva(targetColor.H, targetColor.S, emitter.MinColor.V, emitter.MinColor.A); emitter.MaxColor = new ColorHsva(targetColor.H, targetColor.S, emitter.MaxColor.V, emitter.MaxColor.A); } } } // Get rid of existing damage effects, if no longer needed else if (this.damageEffect != null) { // Stop emitting and dispose when empty foreach (ParticleEmitter emitter in this.damageEffect.Emitters) { if (emitter == null) { continue; } emitter.BurstDelay = float.MaxValue; } this.damageEffect.DisposeWhenEmpty = true; this.damageEffect = null; } }